aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/amber/amber_vulkan.h28
-rw-r--r--include/amber/recipe.h13
-rw-r--r--samples/CMakeLists.txt5
-rw-r--r--samples/amber.cc119
-rw-r--r--samples/config_helper.cc57
-rw-r--r--samples/config_helper.h68
-rw-r--r--samples/config_helper_vulkan.cc670
-rw-r--r--samples/config_helper_vulkan.h72
-rw-r--r--src/recipe.cc8
-rw-r--r--src/script.cc131
-rw-r--r--src/script.h6
-rw-r--r--src/vulkan/device.cc89
-rw-r--r--src/vulkan/device.h25
-rw-r--r--src/vulkan/engine_vulkan.cc13
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);
}