diff options
-rw-r--r-- | include/amber/amber_vulkan.h | 28 | ||||
-rw-r--r-- | include/amber/recipe.h | 13 | ||||
-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 | ||||
-rw-r--r-- | src/recipe.cc | 8 | ||||
-rw-r--r-- | src/script.cc | 131 | ||||
-rw-r--r-- | src/script.h | 6 | ||||
-rw-r--r-- | src/vulkan/device.cc | 89 | ||||
-rw-r--r-- | src/vulkan/device.h | 25 | ||||
-rw-r--r-- | src/vulkan/engine_vulkan.cc | 13 |
14 files changed, 1237 insertions, 67 deletions
diff --git a/include/amber/amber_vulkan.h b/include/amber/amber_vulkan.h index 084a2c9..1f38ab1 100644 --- a/include/amber/amber_vulkan.h +++ b/include/amber/amber_vulkan.h @@ -15,14 +15,38 @@ #ifndef AMBER_AMBER_VULKAN_H_ #define AMBER_AMBER_VULKAN_H_ +#include <limits> +#include <string> +#include <vector> + +#include "amber/amber.h" + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" #include "vulkan/vulkan.h" +#pragma clang diagnostic pop namespace amber { /// Configuration for the Vulkan Engine. struct VulkanEngineConfig : public EngineConfig { - /// The VkDevice to use for the tests. - VkDevice device; + /// The VkPhysicalDevice to use. + VkPhysicalDevice physical_device = VK_NULL_HANDLE; + + /// Physical device features available for |physical_device|. + VkPhysicalDeviceFeatures available_features = {}; + + /// Physical device extensions available for |physical_device|. + std::vector<std::string> available_extensions; + + /// The given queue family index to use. + uint32_t queue_family_index = std::numeric_limits<uint32_t>::max(); + + /// The VkDevice to use. + VkDevice device = VK_NULL_HANDLE; + + /// The VkQueue to use. + VkQueue queue = VK_NULL_HANDLE; }; } // namespace amber diff --git a/include/amber/recipe.h b/include/amber/recipe.h index 3dd932c..d33c479 100644 --- a/include/amber/recipe.h +++ b/include/amber/recipe.h @@ -16,6 +16,7 @@ #define AMBER_RECIPE_H_ #include <memory> +#include <string> #include <utility> #include <vector> @@ -31,6 +32,12 @@ class RecipeImpl { /// Retrieves information on all the shaders in the given recipe. virtual std::vector<ShaderInfo> GetShaderInfo() const = 0; + /// Returns required features in the given recipe. + virtual std::vector<std::string> GetRequiredFeatures() const = 0; + + /// Returns required extensions in the given recipe. + virtual std::vector<std::string> GetRequiredExtensions() const = 0; + protected: RecipeImpl(); }; @@ -47,6 +54,12 @@ class Recipe { RecipeImpl* GetImpl() const { return impl_.get(); } void SetImpl(std::unique_ptr<RecipeImpl> impl) { impl_ = std::move(impl); } + /// Returns required features in the given recipe. + std::vector<std::string> GetRequiredFeatures() const; + + /// Returns required extensions in the given recipe. + std::vector<std::string> GetRequiredExtensions() const; + private: std::unique_ptr<RecipeImpl> impl_; }; 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_ diff --git a/src/recipe.cc b/src/recipe.cc index 4cd6f03..bae7d54 100644 --- a/src/recipe.cc +++ b/src/recipe.cc @@ -29,4 +29,12 @@ std::vector<ShaderInfo> Recipe::GetShaderInfo() const { return impl_->GetShaderInfo(); } +std::vector<std::string> Recipe::GetRequiredFeatures() const { + return impl_ ? impl_->GetRequiredFeatures() : std::vector<std::string>(); +} + +std::vector<std::string> Recipe::GetRequiredExtensions() const { + return impl_ ? impl_->GetRequiredExtensions() : std::vector<std::string>(); +} + } // namespace amber diff --git a/src/script.cc b/src/script.cc index 978df32..012e6aa 100644 --- a/src/script.cc +++ b/src/script.cc @@ -15,6 +15,123 @@ #include "src/script.h" namespace amber { +namespace { + +std::string FeatureToName(Feature feature) { + if (feature == Feature::kRobustBufferAccess) + return "robustBufferAccess"; + if (feature == Feature::kFullDrawIndexUint32) + return "fullDrawIndexUint32"; + if (feature == Feature::kImageCubeArray) + return "imageCubeArray"; + if (feature == Feature::kIndependentBlend) + return "independentBlend"; + if (feature == Feature::kGeometryShader) + return "geometryShader"; + if (feature == Feature::kTessellationShader) + return "tessellationShader"; + if (feature == Feature::kSampleRateShading) + return "sampleRateShading"; + if (feature == Feature::kDualSrcBlend) + return "dualSrcBlend"; + if (feature == Feature::kLogicOp) + return "logicOp"; + if (feature == Feature::kMultiDrawIndirect) + return "multiDrawIndirect"; + if (feature == Feature::kDrawIndirectFirstInstance) + return "drawIndirectFirstInstance"; + if (feature == Feature::kDepthClamp) + return "depthClamp"; + if (feature == Feature::kDepthBiasClamp) + return "depthBiasClamp"; + if (feature == Feature::kFillModeNonSolid) + return "fillModeNonSolid"; + if (feature == Feature::kDepthBounds) + return "depthBounds"; + if (feature == Feature::kWideLines) + return "wideLines"; + if (feature == Feature::kLargePoints) + return "largePoints"; + if (feature == Feature::kAlphaToOne) + return "alphaToOne"; + if (feature == Feature::kMultiViewport) + return "multiViewport"; + if (feature == Feature::kSamplerAnisotropy) + return "samplerAnisotropy"; + if (feature == Feature::kTextureCompressionETC2) + return "textureCompressionETC2"; + if (feature == Feature::kTextureCompressionASTC_LDR) + return "textureCompressionASTC_LDR"; + if (feature == Feature::kTextureCompressionBC) + return "textureCompressionBC"; + if (feature == Feature::kOcclusionQueryPrecise) + return "occlusionQueryPrecise"; + if (feature == Feature::kPipelineStatisticsQuery) + return "pipelineStatisticsQuery"; + if (feature == Feature::kVertexPipelineStoresAndAtomics) + return "vertexPipelineStoresAndAtomics"; + if (feature == Feature::kFragmentStoresAndAtomics) + return "fragmentStoresAndAtomics"; + if (feature == Feature::kShaderTessellationAndGeometryPointSize) + return "shaderTessellationAndGeometryPointSize"; + if (feature == Feature::kShaderImageGatherExtended) + return "shaderImageGatherExtended"; + if (feature == Feature::kShaderStorageImageExtendedFormats) + return "shaderStorageImageExtendedFormats"; + if (feature == Feature::kShaderStorageImageMultisample) + return "shaderStorageImageMultisample"; + if (feature == Feature::kShaderStorageImageReadWithoutFormat) + return "shaderStorageImageReadWithoutFormat"; + if (feature == Feature::kShaderStorageImageWriteWithoutFormat) + return "shaderStorageImageWriteWithoutFormat"; + if (feature == Feature::kShaderUniformBufferArrayDynamicIndexing) + return "shaderUniformBufferArrayDynamicIndexing"; + if (feature == Feature::kShaderSampledImageArrayDynamicIndexing) + return "shaderSampledImageArrayDynamicIndexing"; + if (feature == Feature::kShaderStorageBufferArrayDynamicIndexing) + return "shaderStorageBufferArrayDynamicIndexing"; + if (feature == Feature::kShaderStorageImageArrayDynamicIndexing) + return "shaderStorageImageArrayDynamicIndexing"; + if (feature == Feature::kShaderClipDistance) + return "shaderClipDistance"; + if (feature == Feature::kShaderCullDistance) + return "shaderCullDistance"; + if (feature == Feature::kShaderFloat64) + return "shaderFloat64"; + if (feature == Feature::kShaderInt64) + return "shaderInt64"; + if (feature == Feature::kShaderInt16) + return "shaderInt16"; + if (feature == Feature::kShaderResourceResidency) + return "shaderResourceResidency"; + if (feature == Feature::kShaderResourceMinLod) + return "shaderResourceMinLod"; + if (feature == Feature::kSparseBinding) + return "sparseBinding"; + if (feature == Feature::kSparseResidencyBuffer) + return "sparseResidencyBuffer"; + if (feature == Feature::kSparseResidencyImage2D) + return "sparseResidencyImage2D"; + if (feature == Feature::kSparseResidencyImage3D) + return "sparseResidencyImage3D"; + if (feature == Feature::kSparseResidency2Samples) + return "sparseResidency2Samples"; + if (feature == Feature::kSparseResidency4Samples) + return "sparseResidency4Samples"; + if (feature == Feature::kSparseResidency8Samples) + return "sparseResidency8Samples"; + if (feature == Feature::kSparseResidency16Samples) + return "sparseResidency16Samples"; + if (feature == Feature::kSparseResidencyAliased) + return "sparseResidencyAliased"; + if (feature == Feature::kVariableMultisampleRate) + return "variableMultisampleRate"; + if (feature == Feature::kInheritedQueries) + return "inheritedQueries"; + return ""; +} + +} // namespace Script::Script() = default; @@ -38,4 +155,18 @@ std::vector<ShaderInfo> Script::GetShaderInfo() const { return ret; } +std::vector<std::string> Script::GetRequiredFeatures() const { + std::vector<std::string> required_features_in_string; + for (auto feature : engine_info_.required_features) { + auto name = FeatureToName(feature); + if (!name.empty()) + required_features_in_string.push_back(name); + } + return required_features_in_string; +} + +std::vector<std::string> Script::GetRequiredExtensions() const { + return engine_info_.required_extensions; +} + } // namespace amber diff --git a/src/script.h b/src/script.h index 6933775..e46d7e7 100644 --- a/src/script.h +++ b/src/script.h @@ -42,6 +42,12 @@ class Script : public RecipeImpl { /// Retrieves information on the shaders in the given script. std::vector<ShaderInfo> GetShaderInfo() const override; + /// Returns required features in the given recipe. + std::vector<std::string> GetRequiredFeatures() const override; + + /// Returns required extensions in the given recipe. + std::vector<std::string> GetRequiredExtensions() const override; + /// Adds |pipeline| to the list of known pipelines. The |pipeline| must have /// a unique name over all pipelines in the script. Result AddPipeline(std::unique_ptr<Pipeline> pipeline) { diff --git a/src/vulkan/device.cc b/src/vulkan/device.cc index 631ed4c..e211e4e 100644 --- a/src/vulkan/device.cc +++ b/src/vulkan/device.cc @@ -205,13 +205,11 @@ VkPhysicalDeviceFeatures RequestedFeatures( } bool AreAllRequiredFeaturesSupported( - const VkPhysicalDevice& physical_device, + const VkPhysicalDeviceFeatures& available_features, const std::vector<Feature>& required_features) { if (required_features.empty()) return true; - VkPhysicalDeviceFeatures available_features = {}; - vkGetPhysicalDeviceFeatures(physical_device, &available_features); for (const auto& feature : required_features) { switch (feature) { case Feature::kRobustBufferAccess: @@ -450,35 +448,44 @@ bool AreAllRequiredFeaturesSupported( return true; } -bool AreAllExtensionsSupported( - const VkPhysicalDevice& physical_device, - const std::vector<std::string>& required_extensions) { - if (required_extensions.empty()) - return true; - +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 false; + return available_extensions; } if (available_extension_count == 0) - return false; + 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 false; + return available_extensions; } + for (const auto& property : available_extension_properties) + available_extensions.push_back(property.extensionName); + + return available_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& property : available_extension_properties) { - required_extension_set.erase(property.extensionName); + for (const auto& extension : available_extensions) { + required_extension_set.erase(extension); } return required_extension_set.empty(); @@ -487,7 +494,19 @@ bool AreAllExtensionsSupported( } // namespace Device::Device() = default; -Device::Device(VkDevice device) : device_(device), destroy_device_(false) {} +Device::Device(VkPhysicalDevice physical_device, + const VkPhysicalDeviceFeatures& available_features, + const std::vector<std::string>& available_extensions, + uint32_t queue_family_index, + VkDevice device, + VkQueue queue) + : physical_device_(physical_device), + available_physical_device_features_(available_features), + available_physical_device_extensions_(available_extensions), + queue_family_index_(queue_family_index), + device_(device), + queue_(queue), + destroy_device_(false) {} Device::~Device() = default; void Device::Shutdown() { @@ -499,7 +518,7 @@ void Device::Shutdown() { Result Device::Initialize(const std::vector<Feature>& required_features, const std::vector<std::string>& required_extensions) { - if (device_ == VK_NULL_HANDLE) { + if (destroy_device_) { Result r = CreateInstance(); if (!r.IsSuccess()) return r; @@ -511,13 +530,29 @@ Result Device::Initialize(const std::vector<Feature>& required_features, r = CreateDevice(required_features, required_extensions); if (!r.IsSuccess()) return r; - } - if (queue_ == VK_NULL_HANDLE) { vkGetDeviceQueue(device_, queue_family_index_, 0, &queue_); if (queue_ == VK_NULL_HANDLE) return Result("Vulkan::Calling vkGetDeviceQueue Fail"); + } else { + if (!AreAllRequiredFeaturesSupported(available_physical_device_features_, + required_features)) { + return Result( + "Vulkan: Device::Initialize given physical device does not support " + "required features"); + } + + if (!AreAllExtensionsSupported(available_physical_device_extensions_, + required_extensions)) { + return Result( + "Vulkan: Device::Initialize given physical device does not support " + "required extensions"); + } } + + vkGetPhysicalDeviceMemoryProperties(physical_device_, + &physical_memory_properties_); + return {}; } @@ -533,15 +568,6 @@ bool Device::ChooseQueueFamilyIndex(const VkPhysicalDevice& physical_device) { for (uint32_t i = 0; i < count; ++i) { if (properties[i].queueFlags & (VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT)) { - queue_family_flags_ = properties[i].queueFlags; - queue_family_index_ = i; - return true; - } - } - - for (uint32_t i = 0; i < count; ++i) { - if (properties[i].queueFlags & VK_QUEUE_COMPUTE_BIT) { - queue_family_flags_ = properties[i].queueFlags; queue_family_index_ = i; return true; } @@ -580,19 +606,20 @@ Result Device::ChoosePhysicalDevice( return Result("Vulkan::Calling vkEnumeratePhysicalDevices Fail"); for (uint32_t i = 0; i < count; ++i) { - if (!AreAllRequiredFeaturesSupported(physical_devices[i], + VkPhysicalDeviceFeatures available_features = {}; + vkGetPhysicalDeviceFeatures(physical_devices[i], &available_features); + if (!AreAllRequiredFeaturesSupported(available_features, required_features)) { continue; } - if (!AreAllExtensionsSupported(physical_devices[i], required_extensions)) { + if (!AreAllExtensionsSupported(GetAvailableExtensions(physical_devices[i]), + required_extensions)) { continue; } if (ChooseQueueFamilyIndex(physical_devices[i])) { physical_device_ = physical_devices[i]; - vkGetPhysicalDeviceMemoryProperties(physical_device_, - &physical_memory_properties_); return {}; } } diff --git a/src/vulkan/device.h b/src/vulkan/device.h index 4298dd8..df161da 100644 --- a/src/vulkan/device.h +++ b/src/vulkan/device.h @@ -30,13 +30,20 @@ namespace vulkan { class Device { public: Device(); - explicit Device(VkDevice device); + Device(VkPhysicalDevice physical_device, + const VkPhysicalDeviceFeatures& available_features, + const std::vector<std::string>& required_extensions, + uint32_t queue_family_index, + VkDevice device, + VkQueue queue); ~Device(); Result Initialize(const std::vector<Feature>& required_features, const std::vector<std::string>& required_extensions); void Shutdown(); + VkInstance GetInstance() const { return instance_; } + VkPhysicalDevice GetPhysicalDevice() { return physical_device_; } VkDevice GetDevice() const { return device_; } VkPhysicalDevice GetPhysicalDevice() const { return physical_device_; } uint32_t GetQueueFamilyIndex() const { return queue_family_index_; } @@ -49,16 +56,19 @@ class Device { Result CreateInstance(); // Get a physical device by checking if the physical device has a proper - // queue family, required features, and required extensions. + // queue family, required features, and required extensions. Note that + // this method calls ChooseQueueFamilyIndex() to check if any queue + // provided by the physical device supports graphics or compute pipeline + // and sets |queue_family_index_| for the proper queue family. Result ChoosePhysicalDevice( const std::vector<Feature>& required_features, const std::vector<std::string>& required_extensions); // Return true if |physical_device| has a queue family that supports both // graphics and compute or only a compute pipeline. If the proper queue - // family exists, |queue_family_index_| and |queue_family_flags_| will have - // the queue family index and flags, respectively. Return false if the proper - // queue family does not exist. + // family exists, |queue_family_index_| will have the queue family index + // and flags, respectively. Return false if the proper queue family does + // not exist. bool ChooseQueueFamilyIndex(const VkPhysicalDevice& physical_device); // Create a logical device with enabled features |required_features| @@ -69,9 +79,10 @@ class Device { VkInstance instance_ = VK_NULL_HANDLE; VkPhysicalDevice physical_device_ = VK_NULL_HANDLE; VkPhysicalDeviceMemoryProperties physical_memory_properties_ = {}; - VkDevice device_ = VK_NULL_HANDLE; - VkQueueFlags queue_family_flags_ = 0; + VkPhysicalDeviceFeatures available_physical_device_features_ = {}; + std::vector<std::string> available_physical_device_extensions_; uint32_t queue_family_index_ = 0; + VkDevice device_ = VK_NULL_HANDLE; VkQueue queue_ = VK_NULL_HANDLE; diff --git a/src/vulkan/engine_vulkan.cc b/src/vulkan/engine_vulkan.cc index 6375417..0dfe51c 100644 --- a/src/vulkan/engine_vulkan.cc +++ b/src/vulkan/engine_vulkan.cc @@ -125,10 +125,21 @@ Result EngineVulkan::InitializeWithConfig( return Result("Vulkan::Set device_ already exists"); VulkanEngineConfig* vk_config = static_cast<VulkanEngineConfig*>(config); + if (vk_config->physical_device == VK_NULL_HANDLE) { + return Result( + "Vulkan::InitializeWithConfig physical device handle is null."); + } + if (vk_config->device == VK_NULL_HANDLE) return Result("Vulkan::InitializeWithConfig device handle is null."); - device_ = MakeUnique<Device>(vk_config->device); + if (vk_config->queue == VK_NULL_HANDLE) + return Result("Vulkan::InitializeWithConfig queue handle is null."); + + device_ = MakeUnique<Device>( + vk_config->physical_device, vk_config->available_features, + vk_config->available_extensions, vk_config->queue_family_index, + vk_config->device, vk_config->queue); return InitDeviceAndCreateCommand(features, extensions); } |