blob: 551fc129edd82087a9820635ea19cf7891ed85a9 [file] [log] [blame]
// 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 <vulkan/vulkan.h>
#include <algorithm>
#include <cstring>
#include <iostream>
#include <iterator>
#include <set>
#include <sstream>
#include "samples/log.h"
namespace sample {
namespace {
const char* const kRequiredValidationLayers[] = {
#ifdef __ANDROID__
// Note that the order of enabled layers is important. It is
// based on Android NDK Vulkan document.
"VK_LAYER_GOOGLE_threading", "VK_LAYER_LUNARG_parameter_validation",
"VK_LAYER_LUNARG_object_tracker", "VK_LAYER_LUNARG_core_validation",
"VK_LAYER_GOOGLE_unique_objects",
#else // __ANDROID__
"VK_LAYER_LUNARG_standard_validation",
#endif // __ANDROID__
};
const size_t kNumberOfRequiredValidationLayers =
sizeof(kRequiredValidationLayers) / sizeof(const char*);
const char kVariablePointers[] = "VariablePointerFeatures.variablePointers";
const char kVariablePointersStorageBuffer[] =
"VariablePointerFeatures.variablePointersStorageBuffer";
const char kExtensionForValidationLayer[] = "VK_EXT_debug_report";
VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(VkDebugReportFlagsEXT flag,
VkDebugReportObjectTypeEXT,
uint64_t,
size_t,
int32_t,
const char* layerPrefix,
const char* msg,
void*) {
std::string flag_message;
switch (flag) {
case VK_DEBUG_REPORT_ERROR_BIT_EXT:
flag_message = "[ERROR]";
break;
case VK_DEBUG_REPORT_WARNING_BIT_EXT:
flag_message = "[WARNING]";
break;
default:
flag_message = "[UNKNOWN]";
break;
}
LogError(flag_message + " validation layer (" + layerPrefix + "):\n" + msg);
return VK_FALSE;
}
// Convert required features given as a string array to
// VkPhysicalDeviceFeatures.
amber::Result NamesToVulkanFeatures(
const std::vector<std::string>& required_feature_names,
VkPhysicalDeviceFeatures* features) {
for (const auto& name : required_feature_names) {
if (name == "robustBufferAccess") {
features->robustBufferAccess = VK_TRUE;
} else if (name == "fullDrawIndexUint32") {
features->fullDrawIndexUint32 = VK_TRUE;
} else if (name == "imageCubeArray") {
features->imageCubeArray = VK_TRUE;
} else if (name == "independentBlend") {
features->independentBlend = VK_TRUE;
} else if (name == "geometryShader") {
features->geometryShader = VK_TRUE;
} else if (name == "tessellationShader") {
features->tessellationShader = VK_TRUE;
} else if (name == "sampleRateShading") {
features->sampleRateShading = VK_TRUE;
} else if (name == "dualSrcBlend") {
features->dualSrcBlend = VK_TRUE;
} else if (name == "logicOp") {
features->logicOp = VK_TRUE;
} else if (name == "multiDrawIndirect") {
features->multiDrawIndirect = VK_TRUE;
} else if (name == "drawIndirectFirstInstance") {
features->drawIndirectFirstInstance = VK_TRUE;
} else if (name == "depthClamp") {
features->depthClamp = VK_TRUE;
} else if (name == "depthBiasClamp") {
features->depthBiasClamp = VK_TRUE;
} else if (name == "fillModeNonSolid") {
features->fillModeNonSolid = VK_TRUE;
} else if (name == "depthBounds") {
features->depthBounds = VK_TRUE;
} else if (name == "wideLines") {
features->wideLines = VK_TRUE;
} else if (name == "largePoints") {
features->largePoints = VK_TRUE;
} else if (name == "alphaToOne") {
features->alphaToOne = VK_TRUE;
} else if (name == "multiViewport") {
features->multiViewport = VK_TRUE;
} else if (name == "samplerAnisotropy") {
features->samplerAnisotropy = VK_TRUE;
} else if (name == "textureCompressionETC2") {
features->textureCompressionETC2 = VK_TRUE;
} else if (name == "textureCompressionASTC_LDR") {
features->textureCompressionASTC_LDR = VK_TRUE;
} else if (name == "textureCompressionBC") {
features->textureCompressionBC = VK_TRUE;
} else if (name == "occlusionQueryPrecise") {
features->occlusionQueryPrecise = VK_TRUE;
} else if (name == "pipelineStatisticsQuery") {
features->pipelineStatisticsQuery = VK_TRUE;
} else if (name == "vertexPipelineStoresAndAtomics") {
features->vertexPipelineStoresAndAtomics = VK_TRUE;
} else if (name == "fragmentStoresAndAtomics") {
features->fragmentStoresAndAtomics = VK_TRUE;
} else if (name == "shaderTessellationAndGeometryPointSize") {
features->shaderTessellationAndGeometryPointSize = VK_TRUE;
} else if (name == "shaderImageGatherExtended") {
features->shaderImageGatherExtended = VK_TRUE;
} else if (name == "shaderStorageImageExtendedFormats") {
features->shaderStorageImageExtendedFormats = VK_TRUE;
} else if (name == "shaderStorageImageMultisample") {
features->shaderStorageImageMultisample = VK_TRUE;
} else if (name == "shaderStorageImageReadWithoutFormat") {
features->shaderStorageImageReadWithoutFormat = VK_TRUE;
} else if (name == "shaderStorageImageWriteWithoutFormat") {
features->shaderStorageImageWriteWithoutFormat = VK_TRUE;
} else if (name == "shaderUniformBufferArrayDynamicIndexing") {
features->shaderUniformBufferArrayDynamicIndexing = VK_TRUE;
} else if (name == "shaderSampledImageArrayDynamicIndexing") {
features->shaderSampledImageArrayDynamicIndexing = VK_TRUE;
} else if (name == "shaderStorageBufferArrayDynamicIndexing") {
features->shaderStorageBufferArrayDynamicIndexing = VK_TRUE;
} else if (name == "shaderStorageImageArrayDynamicIndexing") {
features->shaderStorageImageArrayDynamicIndexing = VK_TRUE;
} else if (name == "shaderClipDistance") {
features->shaderClipDistance = VK_TRUE;
} else if (name == "shaderCullDistance") {
features->shaderCullDistance = VK_TRUE;
} else if (name == "shaderFloat64") {
features->shaderFloat64 = VK_TRUE;
} else if (name == "shaderInt64") {
features->shaderInt64 = VK_TRUE;
} else if (name == "shaderInt16") {
features->shaderInt16 = VK_TRUE;
} else if (name == "shaderResourceResidency") {
features->shaderResourceResidency = VK_TRUE;
} else if (name == "shaderResourceMinLod") {
features->shaderResourceMinLod = VK_TRUE;
} else if (name == "sparseBinding") {
features->sparseBinding = VK_TRUE;
} else if (name == "sparseResidencyBuffer") {
features->sparseResidencyBuffer = VK_TRUE;
} else if (name == "sparseResidencyImage2D") {
features->sparseResidencyImage2D = VK_TRUE;
} else if (name == "sparseResidencyImage3D") {
features->sparseResidencyImage3D = VK_TRUE;
} else if (name == "sparseResidency2Samples") {
features->sparseResidency2Samples = VK_TRUE;
} else if (name == "sparseResidency4Samples") {
features->sparseResidency4Samples = VK_TRUE;
} else if (name == "sparseResidency8Samples") {
features->sparseResidency8Samples = VK_TRUE;
} else if (name == "sparseResidency16Samples") {
features->sparseResidency16Samples = VK_TRUE;
} else if (name == "sparseResidencyAliased") {
features->sparseResidencyAliased = VK_TRUE;
} else if (name == "variableMultisampleRate") {
features->variableMultisampleRate = VK_TRUE;
} else if (name == "inheritedQueries") {
features->inheritedQueries = VK_TRUE;
} else {
return amber::Result("Sample: Unknown Vulkan feature: " + name);
}
}
return {};
}
bool AreAllValidationLayersSupported() {
std::vector<VkLayerProperties> available_layer_properties;
uint32_t available_layer_count = 0;
if (vkEnumerateInstanceLayerProperties(&available_layer_count, nullptr) !=
VK_SUCCESS) {
return false;
}
available_layer_properties.resize(available_layer_count);
if (vkEnumerateInstanceLayerProperties(&available_layer_count,
available_layer_properties.data()) !=
VK_SUCCESS) {
return false;
}
std::set<std::string> required_layer_set(
kRequiredValidationLayers,
&kRequiredValidationLayers[kNumberOfRequiredValidationLayers]);
for (const auto& property : available_layer_properties) {
required_layer_set.erase(property.layerName);
}
if (required_layer_set.empty())
return true;
std::string missing_layers;
for (const auto& missing_layer : required_layer_set)
missing_layers = missing_layers + missing_layer + ",\n\t\t";
LogError("Vulkan: missing validation layers:\n\t\t" + missing_layers);
return true;
}
bool AreAllValidationExtensionsSupported() {
for (const auto& layer : kRequiredValidationLayers) {
uint32_t available_extension_count = 0;
std::vector<VkExtensionProperties> extension_properties;
if (vkEnumerateInstanceExtensionProperties(
layer, &available_extension_count, nullptr) != VK_SUCCESS) {
return false;
}
extension_properties.resize(available_extension_count);
if (vkEnumerateInstanceExtensionProperties(
layer, &available_extension_count, extension_properties.data()) !=
VK_SUCCESS) {
return false;
}
for (const auto& ext : extension_properties) {
if (!strcmp(kExtensionForValidationLayer, ext.extensionName))
return true;
}
}
return false;
}
// 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 instance extensions.
std::vector<std::string> GetAvailableInstanceExtensions() {
std::vector<std::string> available_extensions;
uint32_t available_extension_count = 0;
std::vector<VkExtensionProperties> available_extension_properties;
if (vkEnumerateInstanceExtensionProperties(
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 (vkEnumerateInstanceExtensionProperties(
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;
}
// Get all available extensions of |physical_device|.
std::vector<std::string> GetAvailableDeviceExtensions(
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 = 0;
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();
}
std::string deviceTypeToName(VkPhysicalDeviceType type) {
switch (type) {
case VK_PHYSICAL_DEVICE_TYPE_OTHER:
return "other";
case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU:
return "integrated gpu";
case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU:
return "discrete gpu";
case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU:
return "virtual gpu";
case VK_PHYSICAL_DEVICE_TYPE_CPU:
return "cpu";
default:
break;
}
return "unknown";
}
} // namespace
ConfigHelperVulkan::ConfigHelperVulkan()
: available_features_(VkPhysicalDeviceFeatures()),
available_features2_(VkPhysicalDeviceFeatures2KHR()),
variable_pointers_feature_(VkPhysicalDeviceVariablePointerFeaturesKHR()) {
}
ConfigHelperVulkan::~ConfigHelperVulkan() {
if (vulkan_device_)
vkDestroyDevice(vulkan_device_, nullptr);
if (vulkan_callback_) {
auto vkDestroyDebugReportCallbackEXT =
reinterpret_cast<PFN_vkDestroyDebugReportCallbackEXT>(
vkGetInstanceProcAddr(vulkan_instance_,
"vkDestroyDebugReportCallbackEXT"));
if (vkDestroyDebugReportCallbackEXT) {
vkDestroyDebugReportCallbackEXT(vulkan_instance_, vulkan_callback_,
nullptr);
}
}
if (vulkan_instance_)
vkDestroyInstance(vulkan_instance_, nullptr);
}
amber::Result ConfigHelperVulkan::CreateVulkanInstance(
uint32_t engine_major,
uint32_t engine_minor,
std::vector<std::string> required_extensions,
bool disable_validation_layer) {
VkApplicationInfo app_info = VkApplicationInfo();
app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
app_info.apiVersion = VK_MAKE_VERSION(engine_major, engine_minor, 0);
VkInstanceCreateInfo instance_info = VkInstanceCreateInfo();
instance_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
instance_info.pApplicationInfo = &app_info;
if (!disable_validation_layer) {
if (!AreAllValidationLayersSupported())
return amber::Result("Sample: not all validation layers are supported");
if (!AreAllValidationExtensionsSupported()) {
return amber::Result(
"Sample: extensions of validation layers are not supported");
}
instance_info.enabledLayerCount = kNumberOfRequiredValidationLayers;
instance_info.ppEnabledLayerNames = kRequiredValidationLayers;
required_extensions.push_back(kExtensionForValidationLayer);
}
if (!required_extensions.empty()) {
available_instance_extensions_ = GetAvailableInstanceExtensions();
if (!AreAllExtensionsSupported(available_instance_extensions_,
required_extensions)) {
return amber::Result("Missing required instance extensions");
}
}
// Determine if VkPhysicalDeviceProperties2KHR should be used
for (auto& ext : required_extensions) {
if (ext == "VK_KHR_get_physical_device_properties2") {
use_physical_device_features2_ = true;
}
}
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(); });
instance_info.enabledExtensionCount =
static_cast<uint32_t>(required_extensions_in_char.size());
instance_info.ppEnabledExtensionNames = required_extensions_in_char.data();
if (vkCreateInstance(&instance_info, nullptr, &vulkan_instance_) !=
VK_SUCCESS) {
return amber::Result("Unable to create vulkan instance");
}
return {};
}
amber::Result ConfigHelperVulkan::CreateDebugReportCallback() {
VkDebugReportCallbackCreateInfoEXT info =
VkDebugReportCallbackCreateInfoEXT();
info.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT;
info.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT;
info.pfnCallback = debugCallback;
auto vkCreateDebugReportCallbackEXT =
reinterpret_cast<PFN_vkCreateDebugReportCallbackEXT>(
vkGetInstanceProcAddr(vulkan_instance_,
"vkCreateDebugReportCallbackEXT"));
if (!vkCreateDebugReportCallbackEXT)
return amber::Result("Sample: vkCreateDebugReportCallbackEXT is nullptr");
if (vkCreateDebugReportCallbackEXT(vulkan_instance_, &info, nullptr,
&vulkan_callback_) != VK_SUCCESS) {
return amber::Result("Sample: vkCreateDebugReportCallbackEXT fail");
}
return {};
}
amber::Result ConfigHelperVulkan::ChooseVulkanPhysicalDevice(
const std::vector<std::string>& required_features,
const std::vector<std::string>& required_extensions) {
uint32_t count = 0;
std::vector<VkPhysicalDevice> physical_devices;
if (vkEnumeratePhysicalDevices(vulkan_instance_, &count, nullptr) !=
VK_SUCCESS) {
return amber::Result("Unable to enumerate physical devices");
}
physical_devices.resize(count);
if (vkEnumeratePhysicalDevices(vulkan_instance_, &count,
physical_devices.data()) != VK_SUCCESS) {
return amber::Result("Unable to enumerate physical devices");
}
VkPhysicalDeviceFeatures required_vulkan_features =
VkPhysicalDeviceFeatures();
for (uint32_t i = 0; i < count; ++i) {
if (use_physical_device_features2_) {
VkPhysicalDeviceVariablePointerFeaturesKHR var_ptrs =
VkPhysicalDeviceVariablePointerFeaturesKHR();
var_ptrs.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTER_FEATURES_KHR;
var_ptrs.pNext = nullptr;
VkPhysicalDeviceFeatures2KHR features2 = VkPhysicalDeviceFeatures2KHR();
features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR;
features2.pNext = &var_ptrs;
auto vkGetPhysicalDeviceFeatures2KHR =
reinterpret_cast<PFN_vkGetPhysicalDeviceFeatures2KHR>(
vkGetInstanceProcAddr(vulkan_instance_,
"vkGetPhysicalDeviceFeatures2KHR"));
vkGetPhysicalDeviceFeatures2KHR(physical_devices[i], &features2);
available_features_ = features2.features;
std::vector<std::string> required_features1;
for (const auto& feature : required_features) {
// No dot means this is a features1 feature.
if (feature.find_first_of('.') == std::string::npos) {
required_features1.push_back(feature);
continue;
}
if (feature == kVariablePointers &&
var_ptrs.variablePointers != VK_TRUE) {
continue;
}
if (feature == kVariablePointersStorageBuffer &&
var_ptrs.variablePointersStorageBuffer != VK_TRUE) {
continue;
}
}
amber::Result r =
NamesToVulkanFeatures(required_features1, &required_vulkan_features);
if (!r.IsSuccess())
return r;
} else {
amber::Result r =
NamesToVulkanFeatures(required_features, &required_vulkan_features);
if (!r.IsSuccess())
return r;
vkGetPhysicalDeviceFeatures(physical_devices[i], &available_features_);
}
if (!AreAllRequiredFeaturesSupported(available_features_,
required_vulkan_features)) {
continue;
}
available_device_extensions_ =
GetAvailableDeviceExtensions(physical_devices[i]);
if (!AreAllExtensionsSupported(available_device_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 {};
}
}
std::ostringstream out;
out << "Unable to find Vulkan device supporting:" << std::endl;
for (const auto& str : required_features)
out << " " << str << std::endl;
for (const auto& str : required_extensions)
out << " " << str << std::endl;
return amber::Result(out.str());
}
amber::Result ConfigHelperVulkan::CreateVulkanDevice(
const std::vector<std::string>& required_features,
const std::vector<std::string>& required_extensions) {
VkDeviceQueueCreateInfo queue_info = VkDeviceQueueCreateInfo();
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;
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(); });
VkDeviceCreateInfo info = VkDeviceCreateInfo();
info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
info.pQueueCreateInfos = &queue_info;
info.queueCreateInfoCount = 1;
info.enabledExtensionCount =
static_cast<uint32_t>(required_extensions_in_char.size());
info.ppEnabledExtensionNames = required_extensions_in_char.data();
if (use_physical_device_features2_)
return CreateDeviceWithFeatures2(required_features, &info);
return CreateDeviceWithFeatures1(required_features, &info);
}
amber::Result ConfigHelperVulkan::CreateDeviceWithFeatures1(
const std::vector<std::string>& required_features,
VkDeviceCreateInfo* info) {
VkPhysicalDeviceFeatures required_vulkan_features =
VkPhysicalDeviceFeatures();
amber::Result r =
NamesToVulkanFeatures(required_features, &required_vulkan_features);
if (!r.IsSuccess())
return r;
info->pEnabledFeatures = &required_vulkan_features;
return DoCreateDevice(info);
}
amber::Result ConfigHelperVulkan::CreateDeviceWithFeatures2(
const std::vector<std::string>& required_features,
VkDeviceCreateInfo* info) {
variable_pointers_feature_.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTER_FEATURES_KHR;
variable_pointers_feature_.pNext = nullptr;
available_features2_.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR;
available_features2_.pNext = &variable_pointers_feature_;
std::vector<std::string> feature1_names;
for (const auto& feature : required_features) {
// No dot means this is a features1 feature.
if (feature.find_first_of('.') == std::string::npos) {
feature1_names.push_back(feature);
continue;
}
if (feature == kVariablePointers)
variable_pointers_feature_.variablePointers = VK_TRUE;
else if (feature == kVariablePointersStorageBuffer)
variable_pointers_feature_.variablePointersStorageBuffer = VK_TRUE;
}
VkPhysicalDeviceFeatures required_vulkan_features =
VkPhysicalDeviceFeatures();
amber::Result r =
NamesToVulkanFeatures(feature1_names, &required_vulkan_features);
if (!r.IsSuccess())
return r;
available_features2_.features = required_vulkan_features;
info->pNext = &available_features2_;
info->pEnabledFeatures = nullptr;
return DoCreateDevice(info);
}
amber::Result ConfigHelperVulkan::DoCreateDevice(VkDeviceCreateInfo* info) {
if (vkCreateDevice(vulkan_physical_device_, info, nullptr, &vulkan_device_) !=
VK_SUCCESS) {
return amber::Result("Unable to create vulkan device");
}
return {};
}
void ConfigHelperVulkan::DumpPhysicalDeviceInfo() {
VkPhysicalDeviceProperties props;
vkGetPhysicalDeviceProperties(vulkan_physical_device_, &props);
uint32_t api_version = props.apiVersion;
std::cout << std::endl;
std::cout << "Physical device properties:" << std::endl;
std::cout << " apiVersion: " << static_cast<uint32_t>(api_version >> 22)
<< "." << static_cast<uint32_t>((api_version >> 12) & 0x3ff) << "."
<< static_cast<uint32_t>(api_version & 0xfff) << std::endl;
std::cout << " driverVersion: " << props.driverVersion << std::endl;
std::cout << " vendorID: " << props.vendorID << std::endl;
std::cout << " deviceID: " << props.deviceID << std::endl;
std::cout << " deviceType: " << deviceTypeToName(props.deviceType)
<< std::endl;
std::cout << " deviceName: " << props.deviceName << std::endl;
}
amber::Result ConfigHelperVulkan::CreateConfig(
uint32_t engine_major,
uint32_t engine_minor,
const std::vector<std::string>& required_features,
const std::vector<std::string>& required_instance_extensions,
const std::vector<std::string>& required_device_extensions,
bool disable_validation_layer,
bool show_version_info,
std::unique_ptr<amber::EngineConfig>* cfg_holder) {
amber::Result r = CreateVulkanInstance(engine_major, engine_minor,
required_instance_extensions,
disable_validation_layer);
if (!r.IsSuccess())
return r;
if (!disable_validation_layer) {
r = CreateDebugReportCallback();
if (!r.IsSuccess())
return r;
}
r = ChooseVulkanPhysicalDevice(required_features, required_device_extensions);
if (!r.IsSuccess())
return r;
if (show_version_info)
DumpPhysicalDeviceInfo();
r = CreateVulkanDevice(required_features, required_device_extensions);
if (!r.IsSuccess())
return r;
vkGetDeviceQueue(vulkan_device_, vulkan_queue_family_index_, 0,
&vulkan_queue_);
*cfg_holder =
std::unique_ptr<amber::EngineConfig>(new amber::VulkanEngineConfig());
amber::VulkanEngineConfig* config =
static_cast<amber::VulkanEngineConfig*>(cfg_holder->get());
config->physical_device = vulkan_physical_device_;
config->available_features = available_features_;
config->available_features2 = available_features2_;
config->available_instance_extensions = available_instance_extensions_;
config->available_device_extensions = available_device_extensions_;
config->instance = vulkan_instance_;
config->queue_family_index = vulkan_queue_family_index_;
config->queue = vulkan_queue_;
config->device = vulkan_device_;
config->vkGetInstanceProcAddr = vkGetInstanceProcAddr;
return {};
}
} // namespace sample