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