diff options
author | Jaebaek Seo <duke.acacia@gmail.com> | 2019-01-08 15:26:25 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-01-08 15:26:25 -0500 |
commit | fa990893ef842a0447522b15ec5f8b8e094ed603 (patch) | |
tree | 33891a9d7568cd52b724611dfcc4fa3a8bb40cfe /samples | |
parent | 90b061ab8a69a9082726c2c9c6cd0195f071ca30 (diff) | |
download | amber-fa990893ef842a0447522b15ec5f8b8e094ed603.tar.gz |
Vulkan: reuse device using config (#205)
If `VkPhysicalDevice`, `VkDevice`, `VkQueue`, queue family index,
required features and extensions are given, Amber reuses them.
Fixes #124
Diffstat (limited to 'samples')
-rw-r--r-- | samples/CMakeLists.txt | 5 | ||||
-rw-r--r-- | samples/amber.cc | 119 | ||||
-rw-r--r-- | samples/config_helper.cc | 57 | ||||
-rw-r--r-- | samples/config_helper.h | 68 | ||||
-rw-r--r-- | samples/config_helper_vulkan.cc | 670 | ||||
-rw-r--r-- | samples/config_helper_vulkan.h | 72 |
6 files changed, 965 insertions, 26 deletions
diff --git a/samples/CMakeLists.txt b/samples/CMakeLists.txt index afc8c2f..73a617a 100644 --- a/samples/CMakeLists.txt +++ b/samples/CMakeLists.txt @@ -16,9 +16,14 @@ include_directories("${PROJECT_SOURCE_DIR}/include") set(AMBER_SOURCES amber.cc + config_helper.cc ${CMAKE_BINARY_DIR}/src/build-versions.h.fake ) +if (${Vulkan_FOUND}) + set(AMBER_SOURCES ${AMBER_SOURCES} config_helper_vulkan.cc) +endif() + add_executable(amber ${AMBER_SOURCES}) target_include_directories(amber PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/..") set_target_properties(amber PROPERTIES OUTPUT_NAME "amber") diff --git a/samples/amber.cc b/samples/amber.cc index 7c1339b..7d150fc 100644 --- a/samples/amber.cc +++ b/samples/amber.cc @@ -15,30 +15,36 @@ #include <cassert> #include <cstdlib> #include <iostream> +#include <set> +#include <utility> #include <vector> #include "amber/amber.h" #include "amber/recipe.h" +#include "samples/config_helper.h" #include "src/build-versions.h" +#include "src/make_unique.h" namespace { struct Options { - std::string input_filename; + std::vector<std::string> input_filenames; std::string image_filename; std::string buffer_filename; int64_t buffer_binding_index = 0; bool parse_only = false; + bool show_summary = false; bool show_help = false; bool show_version_info = false; amber::EngineType engine = amber::EngineType::kVulkan; }; -const char kUsage[] = R"(Usage: amber [options] SCRIPT +const char kUsage[] = R"(Usage: amber [options] SCRIPT [SCRIPTS...] options: - -p -- Parse input files only; Don't execute + -p -- Parse input files only; Don't execute. + -s -- Print summary of pass/failure. -i <filename> -- Write rendering to <filename> as a PPM image. -b <filename> -- Write contents of a UBO or SSBO to <filename>. -B <buffer> -- Index of buffer to write. Defaults buffer 0. @@ -104,11 +110,13 @@ bool ParseArgs(const std::vector<std::string>& args, Options* opts) { opts->show_version_info = true; } else if (arg == "-p") { opts->parse_only = true; + } else if (arg == "-s") { + opts->show_summary = true; } else if (arg.size() > 0 && arg[0] == '-') { std::cerr << "Unrecognized option " << arg << std::endl; return false; - } else { - opts->input_filename = args[i]; + } else if (!arg.empty()) { + opts->input_filenames.push_back(arg); } } @@ -178,21 +186,39 @@ int main(int argc, const char** argv) { return 0; } - if (options.input_filename.empty()) { + if (options.input_filenames.empty()) { std::cerr << "Input file must be provided." << std::endl; return 2; } - auto data = ReadFile(options.input_filename); - if (data.empty()) - return 1; + amber::Result result; + std::vector<std::string> failures; + struct RecipeData { + std::string file; + std::unique_ptr<amber::Recipe> recipe; + }; + std::vector<RecipeData> recipe_data; + for (const auto& file : options.input_filenames) { + auto data = ReadFile(file); + if (data.empty()) { + std::cerr << file << " is empty." << std::endl; + failures.push_back(file); + continue; + } - amber::Amber am; - amber::Recipe recipe; - amber::Result result = am.Parse(data, &recipe); - if (!result.IsSuccess()) { - std::cerr << result.Error() << std::endl; - return 1; + amber::Amber am; + std::unique_ptr<amber::Recipe> recipe = amber::MakeUnique<amber::Recipe>(); + + result = am.Parse(data, recipe.get()); + if (!result.IsSuccess()) { + std::cerr << file << ": " << result.Error() << std::endl; + failures.push_back(file); + continue; + } + + recipe_data.emplace_back(); + recipe_data.back().file = file; + recipe_data.back().recipe = std::move(recipe); } if (options.parse_only) @@ -200,21 +226,62 @@ int main(int argc, const char** argv) { amber::Options amber_options; amber_options.engine = options.engine; - result = am.Execute(&recipe, amber_options); - if (!result.IsSuccess()) { - std::cerr << result.Error() << std::endl; - return 1; + + std::set<std::string> required_features; + std::set<std::string> required_extensions; + for (const auto& recipe_data_elem : recipe_data) { + const auto features = recipe_data_elem.recipe->GetRequiredFeatures(); + required_features.insert(features.begin(), features.end()); + + const auto extensions = recipe_data_elem.recipe->GetRequiredExtensions(); + required_extensions.insert(extensions.begin(), extensions.end()); } - if (!options.buffer_filename.empty()) { - // TODO(dsinclair): Write buffer file - assert(false); + sample::ConfigHelper config_helper; + amber_options.config = config_helper.CreateConfig( + amber_options.engine, + std::vector<std::string>(required_features.begin(), + required_features.end()), + std::vector<std::string>(required_extensions.begin(), + required_extensions.end())); + + for (const auto& recipe_data_elem : recipe_data) { + const auto* recipe = recipe_data_elem.recipe.get(); + const auto& file = recipe_data_elem.file; + + amber::Amber am; + result = am.Execute(recipe, amber_options); + if (!result.IsSuccess()) { + std::cerr << file << ": " << result.Error() << std::endl; + failures.push_back(file); + continue; + } + + if (!options.buffer_filename.empty()) { + // TODO(dsinclair): Write buffer file + assert(false); + } + + if (!options.image_filename.empty()) { + // TODO(dsinclair): Write image file + assert(false); + } } - if (!options.image_filename.empty()) { - // TODO(dsinclair): Write image file - assert(false); + if (options.show_summary) { + if (!failures.empty()) { + std::cout << "\nSummary of Failures:" << std::endl; + + for (const auto& failure : failures) + std::cout << " " << failure << std::endl; + } + + std::cout << "\nSummary: " + << (options.input_filenames.size() - failures.size()) << " pass, " + << failures.size() << " fail" << std::endl; + + config_helper.Shutdown(); } - return 0; + return !failures.empty(); } diff --git a/samples/config_helper.cc b/samples/config_helper.cc new file mode 100644 index 0000000..677e945 --- /dev/null +++ b/samples/config_helper.cc @@ -0,0 +1,57 @@ +// Copyright 2019 The Amber Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "samples/config_helper.h" + +#include <algorithm> +#include <cassert> +#include <set> +#include <string> +#include <vector> + +#include "src/make_unique.h" + +#if AMBER_ENGINE_VULKAN +#include "samples/config_helper_vulkan.h" +#endif // AMBER_ENGINE_VULKAN + +namespace sample { + +ConfigHelperImpl::~ConfigHelperImpl() = default; + +ConfigHelper::ConfigHelper() = default; + +ConfigHelper::~ConfigHelper() = default; + +std::unique_ptr<amber::EngineConfig> ConfigHelper::CreateConfig( + amber::EngineType engine, + const std::vector<std::string>& required_features, + const std::vector<std::string>& required_extensions) { + if (engine == amber::EngineType::kDawn) + return nullptr; + +#if AMBER_ENGINE_VULKAN + impl_ = amber::MakeUnique<ConfigHelperVulkan>(); +#endif // AMBER_ENGINE_VULKAN + return impl_ ? impl_->CreateConfig(required_features, required_extensions) + : nullptr; +} + +void ConfigHelper::Shutdown() { + if (!impl_) + return; + impl_->Shutdown(); +} + +} // namespace sample diff --git a/samples/config_helper.h b/samples/config_helper.h new file mode 100644 index 0000000..444d443 --- /dev/null +++ b/samples/config_helper.h @@ -0,0 +1,68 @@ +// Copyright 2019 The Amber Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SAMPLES_CONFIG_HELPER_H_ +#define SAMPLES_CONFIG_HELPER_H_ + +#include <memory> +#include <string> +#include <vector> + +#include "amber/amber.h" + +namespace sample { + +// Proof of concept implementation showing how to provide and use +// EngineConfig within sample amber program. +class ConfigHelperImpl { + public: + virtual ~ConfigHelperImpl(); + + // Create instance and device and return them as amber::EngineConfig. + // |required_features| and |required_extensions| contain lists of + // required features and required extensions, respectively. + virtual std::unique_ptr<amber::EngineConfig> CreateConfig( + const std::vector<std::string>& required_features, + const std::vector<std::string>& required_extensions) = 0; + + // Destroy instance and device. + virtual void Shutdown() = 0; +}; + +// Wrapper of ConfigHelperImpl. +class ConfigHelper { + public: + ConfigHelper(); + ~ConfigHelper(); + + // Create instance and device and return them as amber::EngineConfig. + // |required_features| and |required_extensions| contain lists of + // required features and required extensions, respectively. |engine| + // indicates whether the caller required VulkanEngineConfig or + // DawnEngineConfig. + std::unique_ptr<amber::EngineConfig> CreateConfig( + amber::EngineType engine, + const std::vector<std::string>& required_features, + const std::vector<std::string>& required_extensions); + + // Destroy instance and device. + void Shutdown(); + + private: + std::unique_ptr<ConfigHelperImpl> impl_; +}; + +} // namespace sample + +#endif // SAMPLES_CONFIG_HELPER_H_ diff --git a/samples/config_helper_vulkan.cc b/samples/config_helper_vulkan.cc new file mode 100644 index 0000000..2fb0379 --- /dev/null +++ b/samples/config_helper_vulkan.cc @@ -0,0 +1,670 @@ +// Copyright 2019 The Amber Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "samples/config_helper_vulkan.h" + +#include <algorithm> +#include <cassert> +#include <set> + +#include "src/make_unique.h" + +namespace sample { +namespace { + +// Convert required features given as a string array to +// VkPhysicalDeviceFeatures. +VkPhysicalDeviceFeatures NamesToVulkanFeatures( + const std::vector<std::string>& required_feature_names) { + VkPhysicalDeviceFeatures required_vulkan_features = {}; + for (const auto& name : required_feature_names) { + if (name == "robustBufferAccess") { + required_vulkan_features.robustBufferAccess = VK_TRUE; + continue; + } + if (name == "fullDrawIndexUint32") { + required_vulkan_features.fullDrawIndexUint32 = VK_TRUE; + continue; + } + if (name == "imageCubeArray") { + required_vulkan_features.imageCubeArray = VK_TRUE; + continue; + } + if (name == "independentBlend") { + required_vulkan_features.independentBlend = VK_TRUE; + continue; + } + if (name == "geometryShader") { + required_vulkan_features.geometryShader = VK_TRUE; + continue; + } + if (name == "tessellationShader") { + required_vulkan_features.tessellationShader = VK_TRUE; + continue; + } + if (name == "sampleRateShading") { + required_vulkan_features.sampleRateShading = VK_TRUE; + continue; + } + if (name == "dualSrcBlend") { + required_vulkan_features.dualSrcBlend = VK_TRUE; + continue; + } + if (name == "logicOp") { + required_vulkan_features.logicOp = VK_TRUE; + continue; + } + if (name == "multiDrawIndirect") { + required_vulkan_features.multiDrawIndirect = VK_TRUE; + continue; + } + if (name == "drawIndirectFirstInstance") { + required_vulkan_features.drawIndirectFirstInstance = VK_TRUE; + continue; + } + if (name == "depthClamp") { + required_vulkan_features.depthClamp = VK_TRUE; + continue; + } + if (name == "depthBiasClamp") { + required_vulkan_features.depthBiasClamp = VK_TRUE; + continue; + } + if (name == "fillModeNonSolid") { + required_vulkan_features.fillModeNonSolid = VK_TRUE; + continue; + } + if (name == "depthBounds") { + required_vulkan_features.depthBounds = VK_TRUE; + continue; + } + if (name == "wideLines") { + required_vulkan_features.wideLines = VK_TRUE; + continue; + } + if (name == "largePoints") { + required_vulkan_features.largePoints = VK_TRUE; + continue; + } + if (name == "alphaToOne") { + required_vulkan_features.alphaToOne = VK_TRUE; + continue; + } + if (name == "multiViewport") { + required_vulkan_features.multiViewport = VK_TRUE; + continue; + } + if (name == "samplerAnisotropy") { + required_vulkan_features.samplerAnisotropy = VK_TRUE; + continue; + } + if (name == "textureCompressionETC2") { + required_vulkan_features.textureCompressionETC2 = VK_TRUE; + continue; + } + if (name == "textureCompressionASTC_LDR") { + required_vulkan_features.textureCompressionASTC_LDR = VK_TRUE; + continue; + } + if (name == "textureCompressionBC") { + required_vulkan_features.textureCompressionBC = VK_TRUE; + continue; + } + if (name == "occlusionQueryPrecise") { + required_vulkan_features.occlusionQueryPrecise = VK_TRUE; + continue; + } + if (name == "pipelineStatisticsQuery") { + required_vulkan_features.pipelineStatisticsQuery = VK_TRUE; + continue; + } + if (name == "vertexPipelineStoresAndAtomics") { + required_vulkan_features.vertexPipelineStoresAndAtomics = VK_TRUE; + continue; + } + if (name == "fragmentStoresAndAtomics") { + required_vulkan_features.fragmentStoresAndAtomics = VK_TRUE; + continue; + } + if (name == "shaderTessellationAndGeometryPointSize") { + required_vulkan_features.shaderTessellationAndGeometryPointSize = VK_TRUE; + continue; + } + if (name == "shaderImageGatherExtended") { + required_vulkan_features.shaderImageGatherExtended = VK_TRUE; + continue; + } + if (name == "shaderStorageImageExtendedFormats") { + required_vulkan_features.shaderStorageImageExtendedFormats = VK_TRUE; + continue; + } + if (name == "shaderStorageImageMultisample") { + required_vulkan_features.shaderStorageImageMultisample = VK_TRUE; + continue; + } + if (name == "shaderStorageImageReadWithoutFormat") { + required_vulkan_features.shaderStorageImageReadWithoutFormat = VK_TRUE; + continue; + } + if (name == "shaderStorageImageWriteWithoutFormat") { + required_vulkan_features.shaderStorageImageWriteWithoutFormat = VK_TRUE; + continue; + } + if (name == "shaderUniformBufferArrayDynamicIndexing") { + required_vulkan_features.shaderUniformBufferArrayDynamicIndexing = + VK_TRUE; + continue; + } + if (name == "shaderSampledImageArrayDynamicIndexing") { + required_vulkan_features.shaderSampledImageArrayDynamicIndexing = VK_TRUE; + continue; + } + if (name == "shaderStorageBufferArrayDynamicIndexing") { + required_vulkan_features.shaderStorageBufferArrayDynamicIndexing = + VK_TRUE; + continue; + } + if (name == "shaderStorageImageArrayDynamicIndexing") { + required_vulkan_features.shaderStorageImageArrayDynamicIndexing = VK_TRUE; + continue; + } + if (name == "shaderClipDistance") { + required_vulkan_features.shaderClipDistance = VK_TRUE; + continue; + } + if (name == "shaderCullDistance") { + required_vulkan_features.shaderCullDistance = VK_TRUE; + continue; + } + if (name == "shaderFloat64") { + required_vulkan_features.shaderFloat64 = VK_TRUE; + continue; + } + if (name == "shaderInt64") { + required_vulkan_features.shaderInt64 = VK_TRUE; + continue; + } + if (name == "shaderInt16") { + required_vulkan_features.shaderInt16 = VK_TRUE; + continue; + } + if (name == "shaderResourceResidency") { + required_vulkan_features.shaderResourceResidency = VK_TRUE; + continue; + } + if (name == "shaderResourceMinLod") { + required_vulkan_features.shaderResourceMinLod = VK_TRUE; + continue; + } + if (name == "sparseBinding") { + required_vulkan_features.sparseBinding = VK_TRUE; + continue; + } + if (name == "sparseResidencyBuffer") { + required_vulkan_features.sparseResidencyBuffer = VK_TRUE; + continue; + } + if (name == "sparseResidencyImage2D") { + required_vulkan_features.sparseResidencyImage2D = VK_TRUE; + continue; + } + if (name == "sparseResidencyImage3D") { + required_vulkan_features.sparseResidencyImage3D = VK_TRUE; + continue; + } + if (name == "sparseResidency2Samples") { + required_vulkan_features.sparseResidency2Samples = VK_TRUE; + continue; + } + if (name == "sparseResidency4Samples") { + required_vulkan_features.sparseResidency4Samples = VK_TRUE; + continue; + } + if (name == "sparseResidency8Samples") { + required_vulkan_features.sparseResidency8Samples = VK_TRUE; + continue; + } + if (name == "sparseResidency16Samples") { + required_vulkan_features.sparseResidency16Samples = VK_TRUE; + continue; + } + if (name == "sparseResidencyAliased") { + required_vulkan_features.sparseResidencyAliased = VK_TRUE; + continue; + } + if (name == "variableMultisampleRate") { + required_vulkan_features.variableMultisampleRate = VK_TRUE; + continue; + } + if (name == "inheritedQueries") { + required_vulkan_features.inheritedQueries = VK_TRUE; + continue; + } + + assert(false && "Sample: Unknown Vulkan feature"); + } + return required_vulkan_features; +} + +// Check if |physical_device| supports all required features given +// in |required_features|. +bool AreAllRequiredFeaturesSupported( + const VkPhysicalDeviceFeatures& available_features, + const VkPhysicalDeviceFeatures& required_features) { + if (available_features.robustBufferAccess == VK_FALSE && + required_features.robustBufferAccess == VK_TRUE) { + return false; + } + if (available_features.fullDrawIndexUint32 == VK_FALSE && + required_features.fullDrawIndexUint32 == VK_TRUE) { + return false; + } + if (available_features.imageCubeArray == VK_FALSE && + required_features.imageCubeArray == VK_TRUE) { + return false; + } + if (available_features.independentBlend == VK_FALSE && + required_features.independentBlend == VK_TRUE) { + return false; + } + if (available_features.geometryShader == VK_FALSE && + required_features.geometryShader == VK_TRUE) { + return false; + } + if (available_features.tessellationShader == VK_FALSE && + required_features.tessellationShader == VK_TRUE) { + return false; + } + if (available_features.sampleRateShading == VK_FALSE && + required_features.sampleRateShading == VK_TRUE) { + return false; + } + if (available_features.dualSrcBlend == VK_FALSE && + required_features.dualSrcBlend == VK_TRUE) { + return false; + } + if (available_features.logicOp == VK_FALSE && + required_features.logicOp == VK_TRUE) { + return false; + } + if (available_features.multiDrawIndirect == VK_FALSE && + required_features.multiDrawIndirect == VK_TRUE) { + return false; + } + if (available_features.drawIndirectFirstInstance == VK_FALSE && + required_features.drawIndirectFirstInstance == VK_TRUE) { + return false; + } + if (available_features.depthClamp == VK_FALSE && + required_features.depthClamp == VK_TRUE) { + return false; + } + if (available_features.depthBiasClamp == VK_FALSE && + required_features.depthBiasClamp == VK_TRUE) { + return false; + } + if (available_features.fillModeNonSolid == VK_FALSE && + required_features.fillModeNonSolid == VK_TRUE) { + return false; + } + if (available_features.depthBounds == VK_FALSE && + required_features.depthBounds == VK_TRUE) { + return false; + } + if (available_features.wideLines == VK_FALSE && + required_features.wideLines == VK_TRUE) { + return false; + } + if (available_features.largePoints == VK_FALSE && + required_features.largePoints == VK_TRUE) { + return false; + } + if (available_features.alphaToOne == VK_FALSE && + required_features.alphaToOne == VK_TRUE) { + return false; + } + if (available_features.multiViewport == VK_FALSE && + required_features.multiViewport == VK_TRUE) { + return false; + } + if (available_features.samplerAnisotropy == VK_FALSE && + required_features.samplerAnisotropy == VK_TRUE) { + return false; + } + if (available_features.textureCompressionETC2 == VK_FALSE && + required_features.textureCompressionETC2 == VK_TRUE) { + return false; + } + if (available_features.textureCompressionASTC_LDR == VK_FALSE && + required_features.textureCompressionASTC_LDR == VK_TRUE) { + return false; + } + if (available_features.textureCompressionBC == VK_FALSE && + required_features.textureCompressionBC == VK_TRUE) { + return false; + } + if (available_features.occlusionQueryPrecise == VK_FALSE && + required_features.occlusionQueryPrecise == VK_TRUE) { + return false; + } + if (available_features.pipelineStatisticsQuery == VK_FALSE && + required_features.pipelineStatisticsQuery == VK_TRUE) { + return false; + } + if (available_features.vertexPipelineStoresAndAtomics == VK_FALSE && + required_features.vertexPipelineStoresAndAtomics == VK_TRUE) { + return false; + } + if (available_features.fragmentStoresAndAtomics == VK_FALSE && + required_features.fragmentStoresAndAtomics == VK_TRUE) { + return false; + } + if (available_features.shaderTessellationAndGeometryPointSize == VK_FALSE && + required_features.shaderTessellationAndGeometryPointSize == VK_TRUE) { + return false; + } + if (available_features.shaderImageGatherExtended == VK_FALSE && + required_features.shaderImageGatherExtended == VK_TRUE) { + return false; + } + if (available_features.shaderStorageImageExtendedFormats == VK_FALSE && + required_features.shaderStorageImageExtendedFormats == VK_TRUE) { + return false; + } + if (available_features.shaderStorageImageMultisample == VK_FALSE && + required_features.shaderStorageImageMultisample == VK_TRUE) { + return false; + } + if (available_features.shaderStorageImageReadWithoutFormat == VK_FALSE && + required_features.shaderStorageImageReadWithoutFormat == VK_TRUE) { + return false; + } + if (available_features.shaderStorageImageWriteWithoutFormat == VK_FALSE && + required_features.shaderStorageImageWriteWithoutFormat == VK_TRUE) { + return false; + } + if (available_features.shaderUniformBufferArrayDynamicIndexing == VK_FALSE && + required_features.shaderUniformBufferArrayDynamicIndexing == VK_TRUE) { + return false; + } + if (available_features.shaderSampledImageArrayDynamicIndexing == VK_FALSE && + required_features.shaderSampledImageArrayDynamicIndexing == VK_TRUE) { + return false; + } + if (available_features.shaderStorageBufferArrayDynamicIndexing == VK_FALSE && + required_features.shaderStorageBufferArrayDynamicIndexing == VK_TRUE) { + return false; + } + if (available_features.shaderStorageImageArrayDynamicIndexing == VK_FALSE && + required_features.shaderStorageImageArrayDynamicIndexing == VK_TRUE) { + return false; + } + if (available_features.shaderClipDistance == VK_FALSE && + required_features.shaderClipDistance == VK_TRUE) { + return false; + } + if (available_features.shaderCullDistance == VK_FALSE && + required_features.shaderCullDistance == VK_TRUE) { + return false; + } + if (available_features.shaderFloat64 == VK_FALSE && + required_features.shaderFloat64 == VK_TRUE) { + return false; + } + if (available_features.shaderInt64 == VK_FALSE && + required_features.shaderInt64 == VK_TRUE) { + return false; + } + if (available_features.shaderInt16 == VK_FALSE && + required_features.shaderInt16 == VK_TRUE) { + return false; + } + if (available_features.shaderResourceResidency == VK_FALSE && + required_features.shaderResourceResidency == VK_TRUE) { + return false; + } + if (available_features.shaderResourceMinLod == VK_FALSE && + required_features.shaderResourceMinLod == VK_TRUE) { + return false; + } + if (available_features.sparseBinding == VK_FALSE && + required_features.sparseBinding == VK_TRUE) { + return false; + } + if (available_features.sparseResidencyBuffer == VK_FALSE && + required_features.sparseResidencyBuffer == VK_TRUE) { + return false; + } + if (available_features.sparseResidencyImage2D == VK_FALSE && + required_features.sparseResidencyImage2D == VK_TRUE) { + return false; + } + if (available_features.sparseResidencyImage3D == VK_FALSE && + required_features.sparseResidencyImage3D == VK_TRUE) { + return false; + } + if (available_features.sparseResidency2Samples == VK_FALSE && + required_features.sparseResidency2Samples == VK_TRUE) { + return false; + } + if (available_features.sparseResidency4Samples == VK_FALSE && + required_features.sparseResidency4Samples == VK_TRUE) { + return false; + } + if (available_features.sparseResidency8Samples == VK_FALSE && + required_features.sparseResidency8Samples == VK_TRUE) { + return false; + } + if (available_features.sparseResidency16Samples == VK_FALSE && + required_features.sparseResidency16Samples == VK_TRUE) { + return false; + } + if (available_features.sparseResidencyAliased == VK_FALSE && + required_features.sparseResidencyAliased == VK_TRUE) { + return false; + } + if (available_features.variableMultisampleRate == VK_FALSE && + required_features.variableMultisampleRate == VK_TRUE) { + return false; + } + if (available_features.inheritedQueries == VK_FALSE && + required_features.inheritedQueries == VK_TRUE) { + return false; + } + return true; +} + +// Get all available extensions of |physical_device|. +std::vector<std::string> GetAvailableExtensions( + const VkPhysicalDevice& physical_device) { + std::vector<std::string> available_extensions; + uint32_t available_extension_count = 0; + std::vector<VkExtensionProperties> available_extension_properties; + + if (vkEnumerateDeviceExtensionProperties(physical_device, nullptr, + &available_extension_count, + nullptr) != VK_SUCCESS) { + return available_extensions; + } + + if (available_extension_count == 0) + return available_extensions; + + available_extension_properties.resize(available_extension_count); + if (vkEnumerateDeviceExtensionProperties( + physical_device, nullptr, &available_extension_count, + available_extension_properties.data()) != VK_SUCCESS) { + return available_extensions; + } + + for (const auto& property : available_extension_properties) + available_extensions.push_back(property.extensionName); + + return available_extensions; +} + +// Check if |physical_device| supports all required extensions given +// in |required_extensions|. +bool AreAllExtensionsSupported( + const std::vector<std::string>& available_extensions, + const std::vector<std::string>& required_extensions) { + if (required_extensions.empty()) + return true; + + std::set<std::string> required_extension_set(required_extensions.begin(), + required_extensions.end()); + for (const auto& extension : available_extensions) { + required_extension_set.erase(extension); + } + + return required_extension_set.empty(); +} + +// Check if |physical_device| supports both compute and graphics +// pipelines. +uint32_t ChooseQueueFamilyIndex(const VkPhysicalDevice& physical_device) { + uint32_t count; + std::vector<VkQueueFamilyProperties> properties; + + vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &count, nullptr); + properties.resize(count); + vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &count, + properties.data()); + + for (uint32_t i = 0; i < count; ++i) { + if (properties[i].queueFlags & + (VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT)) { + return i; + } + } + + return std::numeric_limits<uint32_t>::max(); +} + +} // namespace + +ConfigHelperVulkan::ConfigHelperVulkan() = default; + +ConfigHelperVulkan::~ConfigHelperVulkan() = default; + +void ConfigHelperVulkan::CreateVulkanInstance() { + VkApplicationInfo app_info = {}; + app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; + app_info.apiVersion = VK_MAKE_VERSION(1, 0, 0); + + VkInstanceCreateInfo instance_info = {}; + instance_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + instance_info.pApplicationInfo = &app_info; + + assert(vkCreateInstance(&instance_info, nullptr, &vulkan_instance_) == + VK_SUCCESS); +} + +void ConfigHelperVulkan::ChooseVulkanPhysicalDevice( + const VkPhysicalDeviceFeatures& required_features, + const std::vector<std::string>& required_extensions) { + uint32_t count; + std::vector<VkPhysicalDevice> physical_devices; + + assert(vkEnumeratePhysicalDevices(vulkan_instance_, &count, nullptr) == + VK_SUCCESS); + physical_devices.resize(count); + assert(vkEnumeratePhysicalDevices(vulkan_instance_, &count, + physical_devices.data()) == VK_SUCCESS); + + for (uint32_t i = 0; i < count; ++i) { + vkGetPhysicalDeviceFeatures(physical_devices[i], &available_features_); + if (!AreAllRequiredFeaturesSupported(available_features_, + required_features)) { + continue; + } + + available_extensions_ = GetAvailableExtensions(physical_devices[i]); + if (!AreAllExtensionsSupported(available_extensions_, + required_extensions)) { + continue; + } + + vulkan_queue_family_index_ = ChooseQueueFamilyIndex(physical_devices[i]); + if (vulkan_queue_family_index_ != std::numeric_limits<uint32_t>::max()) { + vulkan_physical_device_ = physical_devices[i]; + return; + } + } + + assert(false && "Vulkan::No physical device supports Vulkan"); +} + +void ConfigHelperVulkan::CreateVulkanDevice( + const VkPhysicalDeviceFeatures& required_features, + const std::vector<std::string>& required_extensions) { + VkDeviceQueueCreateInfo queue_info; + const float priorities[] = {1.0f}; + + queue_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + queue_info.queueFamilyIndex = vulkan_queue_family_index_; + queue_info.queueCount = 1; + queue_info.pQueuePriorities = priorities; + + VkDeviceCreateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + info.pQueueCreateInfos = &queue_info; + info.queueCreateInfoCount = 1; + info.pEnabledFeatures = &required_features; + + std::vector<const char*> required_extensions_in_char; + std::transform( + required_extensions.begin(), required_extensions.end(), + std::back_inserter(required_extensions_in_char), + [](const std::string& ext) -> const char* { return ext.c_str(); }); + info.enabledExtensionCount = + static_cast<uint32_t>(required_extensions_in_char.size()); + info.ppEnabledExtensionNames = required_extensions_in_char.data(); + + assert(vkCreateDevice(vulkan_physical_device_, &info, nullptr, + &vulkan_device_) == VK_SUCCESS); +} + +std::unique_ptr<amber::EngineConfig> ConfigHelperVulkan::CreateConfig( + const std::vector<std::string>& required_features, + const std::vector<std::string>& required_extensions) { + std::unique_ptr<amber::VulkanEngineConfig> config = + amber::MakeUnique<amber::VulkanEngineConfig>(); + + auto required_vulkan_features = NamesToVulkanFeatures(required_features); + + CreateVulkanInstance(); + ChooseVulkanPhysicalDevice(required_vulkan_features, required_extensions); + CreateVulkanDevice(required_vulkan_features, required_extensions); + vkGetDeviceQueue(vulkan_device_, vulkan_queue_family_index_, 0, + &vulkan_queue_); + + config->physical_device = vulkan_physical_device_; + config->available_features = available_features_; + config->available_extensions = available_extensions_; + config->queue_family_index = vulkan_queue_family_index_; + config->queue = vulkan_queue_; + config->device = vulkan_device_; + return config; +} + +void ConfigHelperVulkan::Shutdown() { + if (vulkan_device_ != VK_NULL_HANDLE) + vkDestroyDevice(vulkan_device_, nullptr); + + if (vulkan_instance_ != VK_NULL_HANDLE) + vkDestroyInstance(vulkan_instance_, nullptr); +} + +} // namespace sample diff --git a/samples/config_helper_vulkan.h b/samples/config_helper_vulkan.h new file mode 100644 index 0000000..61c4c40 --- /dev/null +++ b/samples/config_helper_vulkan.h @@ -0,0 +1,72 @@ +// Copyright 2019 The Amber Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SAMPLES_CONFIG_HELPER_VULKAN_H_ +#define SAMPLES_CONFIG_HELPER_VULKAN_H_ + +#include <limits> +#include <memory> +#include <string> +#include <vector> + +#include "amber/amber.h" +#include "amber/amber_vulkan.h" +#include "samples/config_helper.h" + +namespace sample { + +// Child class of ConfigHelperImpl for Vulkan. +class ConfigHelperVulkan : public ConfigHelperImpl { + public: + ConfigHelperVulkan(); + ~ConfigHelperVulkan() override; + + // Create Vulkan instance and device and return them as + // amber::VulkanEngineConfig. Required Vulkan device features and + // extensions are given in |required_features| and + // |required_extensions|, respectively. + std::unique_ptr<amber::EngineConfig> CreateConfig( + const std::vector<std::string>& required_features, + const std::vector<std::string>& required_extensions) override; + + // Destroy Vulkan instance and device. + void Shutdown() override; + + private: + // Create Vulkan instance. + void CreateVulkanInstance(); + + // Choose Vulkan physical device that supports both + // |required_features| and |required_extensions|. + void ChooseVulkanPhysicalDevice( + const VkPhysicalDeviceFeatures& required_features, + const std::vector<std::string>& required_extensions); + + // Create Vulkan logical device that enables both + // |required_features| and |required_extensions|. + void CreateVulkanDevice(const VkPhysicalDeviceFeatures& required_features, + const std::vector<std::string>& required_extensions); + + VkInstance vulkan_instance_ = VK_NULL_HANDLE; + VkPhysicalDevice vulkan_physical_device_ = VK_NULL_HANDLE; + VkPhysicalDeviceFeatures available_features_ = {}; + std::vector<std::string> available_extensions_; + uint32_t vulkan_queue_family_index_ = std::numeric_limits<uint32_t>::max(); + VkQueue vulkan_queue_ = VK_NULL_HANDLE; + VkDevice vulkan_device_ = VK_NULL_HANDLE; +}; + +} // namespace sample + +#endif // SAMPLES_CONFIG_HELPER_VULKAN_H_ |