// Copyright 2018 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 "src/vulkan/device.h" #include #include #include // Vulkan wrappers: std::setw(), std::left/right #include #include #include #include #include #include #include "src/make_unique.h" namespace amber { namespace vulkan { namespace { const char kVariablePointers[] = "VariablePointerFeatures.variablePointers"; const char kVariablePointersStorageBuffer[] = "VariablePointerFeatures.variablePointersStorageBuffer"; const char kFloat16Int8_Float16[] = "Float16Int8Features.shaderFloat16"; const char kFloat16Int8_Int8[] = "Float16Int8Features.shaderInt8"; const char k8BitStorage_Storage[] = "Storage8BitFeatures.storageBuffer8BitAccess"; const char k8BitStorage_UniformAndStorage[] = "Storage8BitFeatures.uniformAndStorageBuffer8BitAccess"; const char k8BitStorage_PushConstant[] = "Storage8BitFeatures.storagePushConstant8"; const char k16BitStorage_Storage[] = "Storage16BitFeatures.storageBuffer16BitAccess"; const char k16BitStorage_UniformAndStorage[] = "Storage16BitFeatures.uniformAndStorageBuffer16BitAccess"; const char k16BitStorage_PushConstant[] = "Storage16BitFeatures.storagePushConstant16"; const char k16BitStorage_InputOutput[] = "Storage16BitFeatures.storageInputOutput16"; const char kSubgroupSizeControl[] = "SubgroupSizeControl.subgroupSizeControl"; const char kComputeFullSubgroups[] = "SubgroupSizeControl.computeFullSubgroups"; const char kSubgroupSupportedOperations[] = "SubgroupSupportedOperations"; const char kSubgroupSupportedOperationsBasic[] = "SubgroupSupportedOperations.basic"; const char kSubgroupSupportedOperationsVote[] = "SubgroupSupportedOperations.vote"; const char kSubgroupSupportedOperationsArithmetic[] = "SubgroupSupportedOperations.arithmetic"; const char kSubgroupSupportedOperationsBallot[] = "SubgroupSupportedOperations.ballot"; const char kSubgroupSupportedOperationsShuffle[] = "SubgroupSupportedOperations.shuffle"; const char kSubgroupSupportedOperationsShuffleRelative[] = "SubgroupSupportedOperations.shuffleRelative"; const char kSubgroupSupportedOperationsClustered[] = "SubgroupSupportedOperations.clustered"; const char kSubgroupSupportedOperationsQuad[] = "SubgroupSupportedOperations.quad"; const char kSubgroupSupportedStages[] = "SubgroupSupportedStages"; const char kSubgroupSupportedStagesVertex[] = "SubgroupSupportedStages.vertex"; const char kSubgroupSupportedStagesTessellationControl[] = "SubgroupSupportedStages.tessellationControl"; const char kSubgroupSupportedStagesTessellationEvaluation[] = "SubgroupSupportedStages.tessellationEvaluation"; const char kSubgroupSupportedStagesGeometry[] = "SubgroupSupportedStages.geometry"; const char kSubgroupSupportedStagesFragment[] = "SubgroupSupportedStages.fragment"; const char kSubgroupSupportedStagesCompute[] = "SubgroupSupportedStages.compute"; const char kShaderSubgroupExtendedTypes[] = "ShaderSubgroupExtendedTypesFeatures.shaderSubgroupExtendedTypes"; const char kIndexTypeUint8[] = "IndexTypeUint8Features.indexTypeUint8"; struct BaseOutStructure { VkStructureType sType; void* pNext; }; bool AreAllRequiredFeaturesSupported( const VkPhysicalDeviceFeatures& available_features, const std::vector& required_features) { if (required_features.empty()) return true; for (const auto& feature : required_features) { if (feature == "robustBufferAccess") { if (available_features.robustBufferAccess == VK_FALSE) return false; continue; } if (feature == "fullDrawIndexUint32") { if (available_features.fullDrawIndexUint32 == VK_FALSE) return false; continue; } if (feature == "imageCubeArray") { if (available_features.imageCubeArray == VK_FALSE) return false; continue; } if (feature == "independentBlend") { if (available_features.independentBlend == VK_FALSE) return false; continue; } if (feature == "geometryShader") { if (available_features.geometryShader == VK_FALSE) return false; continue; } if (feature == "tessellationShader") { if (available_features.tessellationShader == VK_FALSE) return false; continue; } if (feature == "sampleRateShading") { if (available_features.sampleRateShading == VK_FALSE) return false; continue; } if (feature == "dualSrcBlend") { if (available_features.dualSrcBlend == VK_FALSE) return false; continue; } if (feature == "logicOp") { if (available_features.logicOp == VK_FALSE) return false; continue; } if (feature == "multiDrawIndirect") { if (available_features.multiDrawIndirect == VK_FALSE) return false; continue; } if (feature == "drawIndirectFirstInstance") { if (available_features.drawIndirectFirstInstance == VK_FALSE) return false; continue; } if (feature == "depthClamp") { if (available_features.depthClamp == VK_FALSE) return false; continue; } if (feature == "depthBiasClamp") { if (available_features.depthBiasClamp == VK_FALSE) return false; continue; } if (feature == "fillModeNonSolid") { if (available_features.fillModeNonSolid == VK_FALSE) return false; continue; } if (feature == "depthBounds") { if (available_features.depthBounds == VK_FALSE) return false; continue; } if (feature == "wideLines") { if (available_features.wideLines == VK_FALSE) return false; continue; } if (feature == "largePoints") { if (available_features.largePoints == VK_FALSE) return false; continue; } if (feature == "alphaToOne") { if (available_features.alphaToOne == VK_FALSE) return false; continue; } if (feature == "multiViewport") { if (available_features.multiViewport == VK_FALSE) return false; continue; } if (feature == "samplerAnisotropy") { if (available_features.samplerAnisotropy == VK_FALSE) return false; continue; } if (feature == "textureCompressionETC2") { if (available_features.textureCompressionETC2 == VK_FALSE) return false; continue; } if (feature == "textureCompressionASTC_LDR") { if (available_features.textureCompressionASTC_LDR == VK_FALSE) return false; continue; } if (feature == "textureCompressionBC") { if (available_features.textureCompressionBC == VK_FALSE) return false; continue; } if (feature == "occlusionQueryPrecise") { if (available_features.occlusionQueryPrecise == VK_FALSE) return false; continue; } if (feature == "pipelineStatisticsQuery") { if (available_features.pipelineStatisticsQuery == VK_FALSE) return false; continue; } if (feature == "vertexPipelineStoresAndAtomics") { if (available_features.vertexPipelineStoresAndAtomics == VK_FALSE) return false; continue; } if (feature == "fragmentStoresAndAtomics") { if (available_features.fragmentStoresAndAtomics == VK_FALSE) return false; continue; } if (feature == "shaderTessellationAndGeometryPointSize") { if (available_features.shaderTessellationAndGeometryPointSize == VK_FALSE) return false; continue; } if (feature == "shaderImageGatherExtended") { if (available_features.shaderImageGatherExtended == VK_FALSE) return false; continue; } if (feature == "shaderStorageImageExtendedFormats") { if (available_features.shaderStorageImageExtendedFormats == VK_FALSE) return false; continue; } if (feature == "shaderStorageImageMultisample") { if (available_features.shaderStorageImageMultisample == VK_FALSE) return false; continue; } if (feature == "shaderStorageImageReadWithoutFormat") { if (available_features.shaderStorageImageReadWithoutFormat == VK_FALSE) return false; continue; } if (feature == "shaderStorageImageWriteWithoutFormat") { if (available_features.shaderStorageImageWriteWithoutFormat == VK_FALSE) return false; continue; } if (feature == "shaderUniformBufferArrayDynamicIndexing") { if (available_features.shaderUniformBufferArrayDynamicIndexing == VK_FALSE) return false; continue; } if (feature == "shaderSampledImageArrayDynamicIndexing") { if (available_features.shaderSampledImageArrayDynamicIndexing == VK_FALSE) return false; continue; } if (feature == "shaderStorageBufferArrayDynamicIndexing") { if (available_features.shaderStorageBufferArrayDynamicIndexing == VK_FALSE) return false; continue; } if (feature == "shaderStorageImageArrayDynamicIndexing") { if (available_features.shaderStorageImageArrayDynamicIndexing == VK_FALSE) return false; continue; } if (feature == "shaderClipDistance") { if (available_features.shaderClipDistance == VK_FALSE) return false; continue; } if (feature == "shaderCullDistance") { if (available_features.shaderCullDistance == VK_FALSE) return false; continue; } if (feature == "shaderFloat64") { if (available_features.shaderFloat64 == VK_FALSE) return false; continue; } if (feature == "shaderInt64") { if (available_features.shaderInt64 == VK_FALSE) return false; continue; } if (feature == "shaderInt16") { if (available_features.shaderInt16 == VK_FALSE) return false; continue; } if (feature == "shaderResourceResidency") { if (available_features.shaderResourceResidency == VK_FALSE) return false; continue; } if (feature == "shaderResourceMinLod") { if (available_features.shaderResourceMinLod == VK_FALSE) return false; continue; } if (feature == "sparseBinding") { if (available_features.sparseBinding == VK_FALSE) return false; continue; } if (feature == "sparseResidencyBuffer") { if (available_features.sparseResidencyBuffer == VK_FALSE) return false; continue; } if (feature == "sparseResidencyImage2D") { if (available_features.sparseResidencyImage2D == VK_FALSE) return false; continue; } if (feature == "sparseResidencyImage3D") { if (available_features.sparseResidencyImage3D == VK_FALSE) return false; continue; } if (feature == "sparseResidency2Samples") { if (available_features.sparseResidency2Samples == VK_FALSE) return false; continue; } if (feature == "sparseResidency4Samples") { if (available_features.sparseResidency4Samples == VK_FALSE) return false; continue; } if (feature == "sparseResidency8Samples") { if (available_features.sparseResidency8Samples == VK_FALSE) return false; continue; } if (feature == "sparseResidency16Samples") { if (available_features.sparseResidency16Samples == VK_FALSE) return false; continue; } if (feature == "sparseResidencyAliased") { if (available_features.sparseResidencyAliased == VK_FALSE) return false; continue; } if (feature == "variableMultisampleRate") { if (available_features.variableMultisampleRate == VK_FALSE) return false; continue; } if (feature == "inheritedQueries") { if (available_features.inheritedQueries == VK_FALSE) return false; continue; } } return true; } bool AreAllExtensionsSupported( const std::vector& available_extensions, const std::vector& required_extensions) { if (required_extensions.empty()) return true; std::set 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(); } } // namespace Device::Device(VkInstance instance, VkPhysicalDevice physical_device, uint32_t queue_family_index, VkDevice device, VkQueue queue) : instance_(instance), physical_device_(physical_device), device_(device), queue_(queue), queue_family_index_(queue_family_index) {} Device::~Device() = default; Result Device::LoadVulkanPointers(PFN_vkGetInstanceProcAddr getInstanceProcAddr, Delegate* delegate) { // Note: logging Vulkan calls is done via the delegate rather than a Vulkan // layer because we want such logging even when Amber is built as a native // executable on Android, where Vulkan layers are usable only with APKs. if (delegate && delegate->LogGraphicsCalls()) delegate->Log("Loading Vulkan Pointers"); #include "vk-wrappers-1-0.inc" ptrs_.vkGetPhysicalDeviceProperties(physical_device_, &physical_device_properties_); if (SupportsApiVersion(1, 1, 0)) { #include "vk-wrappers-1-1.inc" } return {}; } bool Device::SupportsApiVersion(uint32_t major, uint32_t minor, uint32_t patch) { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wold-style-cast" return physical_device_properties_.apiVersion >= VK_MAKE_VERSION(major, minor, patch); #pragma clang diagnostic pop } Result Device::Initialize( PFN_vkGetInstanceProcAddr getInstanceProcAddr, Delegate* delegate, const std::vector& required_features, const std::vector& required_device_extensions, const VkPhysicalDeviceFeatures& available_features, const VkPhysicalDeviceFeatures2KHR& available_features2, const std::vector& available_extensions) { Result r = LoadVulkanPointers(getInstanceProcAddr, delegate); if (!r.IsSuccess()) return r; // Check for the core features. We don't know if available_features or // available_features2 is provided, so check both. if (!AreAllRequiredFeaturesSupported(available_features, required_features) && !AreAllRequiredFeaturesSupported(available_features2.features, required_features)) { return Result( "Vulkan: Device::Initialize given physical device does not support " "required features"); } // Search for additional features in case they are found in pNext field of // available_features2. VkPhysicalDeviceVariablePointerFeaturesKHR* var_ptrs = nullptr; VkPhysicalDeviceFloat16Int8FeaturesKHR* float16_ptrs = nullptr; VkPhysicalDevice8BitStorageFeaturesKHR* storage8_ptrs = nullptr; VkPhysicalDevice16BitStorageFeaturesKHR* storage16_ptrs = nullptr; VkPhysicalDeviceVulkan11Features* vulkan11_ptrs = nullptr; VkPhysicalDeviceVulkan12Features* vulkan12_ptrs = nullptr; VkPhysicalDeviceVulkan13Features* vulkan13_ptrs = nullptr; VkPhysicalDeviceSubgroupSizeControlFeaturesEXT* subgroup_size_control_features = nullptr; VkPhysicalDeviceShaderSubgroupExtendedTypesFeatures* shader_subgroup_extended_types_ptrs = nullptr; VkPhysicalDeviceIndexTypeUint8FeaturesEXT* index_type_uint8_ptrs = nullptr; void* ptr = available_features2.pNext; while (ptr != nullptr) { BaseOutStructure* s = static_cast(ptr); switch (s->sType) { case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTER_FEATURES_KHR: var_ptrs = static_cast(ptr); break; case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT16_INT8_FEATURES_KHR: float16_ptrs = static_cast(ptr); break; case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES_KHR: storage8_ptrs = static_cast(ptr); break; case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES_KHR: storage16_ptrs = static_cast(ptr); break; case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_FEATURES_EXT: subgroup_size_control_features = static_cast(ptr); break; // NOLINTNEXTLINE(whitespace/line_length) case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SUBGROUP_EXTENDED_TYPES_FEATURES: shader_subgroup_extended_types_ptrs = static_cast( ptr); break; case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INDEX_TYPE_UINT8_FEATURES_EXT: index_type_uint8_ptrs = static_cast(ptr); break; case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES: vulkan11_ptrs = static_cast(ptr); break; case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES: vulkan12_ptrs = static_cast(ptr); break; case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES: vulkan13_ptrs = static_cast(ptr); break; default: break; } ptr = s->pNext; } // Compare the available additional (non-core) features against the // requirements. // // Vulkan 1.2 added support for defining non-core physical device features // using VkPhysicalDeviceVulkan11Features and VkPhysicalDeviceVulkan12Features // structures. If |vulkan11_ptrs| and/or |vulkan12_ptrs| are null, we must // check for features using the old approach (by checking across various // feature structs); otherwise, we can check features via the new structs. for (const auto& feature : required_features) { // First check the feature structures are provided for the required // features. if ((feature == kVariablePointers || feature == kVariablePointersStorageBuffer) && var_ptrs == nullptr && vulkan11_ptrs == nullptr) { return amber::Result( "Variable pointers requested but feature not returned"); } if ((feature == k16BitStorage_Storage || feature == k16BitStorage_UniformAndStorage || feature == k16BitStorage_PushConstant || feature == k16BitStorage_InputOutput) && storage16_ptrs == nullptr && vulkan11_ptrs == nullptr) { return amber::Result( "Shader 16-bit storage requested but feature not returned"); } if ((feature == kFloat16Int8_Float16 || feature == kFloat16Int8_Int8) && float16_ptrs == nullptr && vulkan12_ptrs == nullptr) { return amber::Result( "Shader float16/int8 requested but feature not returned"); } if ((feature == k8BitStorage_UniformAndStorage || feature == k8BitStorage_Storage || feature == k8BitStorage_PushConstant) && storage8_ptrs == nullptr && vulkan12_ptrs == nullptr) { return amber::Result( "Shader 8-bit storage requested but feature not returned"); } if ((feature == kSubgroupSizeControl || feature == kComputeFullSubgroups) && subgroup_size_control_features == nullptr && vulkan13_ptrs == nullptr) { return amber::Result("Missing subgroup size control features"); } if (feature == kShaderSubgroupExtendedTypes && shader_subgroup_extended_types_ptrs == nullptr && vulkan12_ptrs == nullptr) { return amber::Result( "Subgroup extended types requested but feature not returned"); } if (feature == kIndexTypeUint8 && (index_type_uint8_ptrs == nullptr || index_type_uint8_ptrs->indexTypeUint8 != VK_TRUE)) { return amber::Result( "Index type uint8_t requested but feature not returned"); } // Next check the fields of the feature structures. // If Vulkan 1.1 structure exists the features are set there. if (vulkan11_ptrs) { if (feature == kVariablePointers && vulkan11_ptrs->variablePointers != VK_TRUE) { return amber::Result("Missing variable pointers feature"); } if (feature == kVariablePointersStorageBuffer && vulkan11_ptrs->variablePointersStorageBuffer != VK_TRUE) { return amber::Result( "Missing variable pointers storage buffer feature"); } if (feature == k16BitStorage_Storage && vulkan11_ptrs->storageBuffer16BitAccess != VK_TRUE) { return amber::Result("Missing 16-bit storage access"); } if (feature == k16BitStorage_UniformAndStorage && vulkan11_ptrs->uniformAndStorageBuffer16BitAccess != VK_TRUE) { return amber::Result("Missing 16-bit uniform and storage access"); } if (feature == k16BitStorage_PushConstant && vulkan11_ptrs->storagePushConstant16 != VK_TRUE) { return amber::Result("Missing 16-bit push constant access"); } if (feature == k16BitStorage_InputOutput && vulkan11_ptrs->storageInputOutput16 != VK_TRUE) { return amber::Result("Missing 16-bit input/output access"); } } else { // Vulkan 1.1 structure was not found. Use separate structures per each // feature. if (feature == kVariablePointers && var_ptrs->variablePointers != VK_TRUE) { return amber::Result("Missing variable pointers feature"); } if (feature == kVariablePointersStorageBuffer && var_ptrs->variablePointersStorageBuffer != VK_TRUE) { return amber::Result( "Missing variable pointers storage buffer feature"); } if (feature == k16BitStorage_Storage && storage16_ptrs->storageBuffer16BitAccess != VK_TRUE) { return amber::Result("Missing 16-bit storage access"); } if (feature == k16BitStorage_UniformAndStorage && storage16_ptrs->uniformAndStorageBuffer16BitAccess != VK_TRUE) { return amber::Result("Missing 16-bit uniform and storage access"); } if (feature == k16BitStorage_PushConstant && storage16_ptrs->storagePushConstant16 != VK_TRUE) { return amber::Result("Missing 16-bit push constant access"); } if (feature == k16BitStorage_InputOutput && storage16_ptrs->storageInputOutput16 != VK_TRUE) { return amber::Result("Missing 16-bit input/output access"); } } // If Vulkan 1.2 structure exists the features are set there. if (vulkan12_ptrs) { if (feature == kFloat16Int8_Float16 && vulkan12_ptrs->shaderFloat16 != VK_TRUE) { return amber::Result("Missing float16 feature"); } if (feature == kFloat16Int8_Int8 && vulkan12_ptrs->shaderInt8 != VK_TRUE) { return amber::Result("Missing int8 feature"); } if (feature == k8BitStorage_Storage && vulkan12_ptrs->storageBuffer8BitAccess != VK_TRUE) { return amber::Result("Missing 8-bit storage access"); } if (feature == k8BitStorage_UniformAndStorage && vulkan12_ptrs->uniformAndStorageBuffer8BitAccess != VK_TRUE) { return amber::Result("Missing 8-bit uniform and storage access"); } if (feature == k8BitStorage_PushConstant && vulkan12_ptrs->storagePushConstant8 != VK_TRUE) { return amber::Result("Missing 8-bit push constant access"); } if (feature == kShaderSubgroupExtendedTypes && vulkan12_ptrs->shaderSubgroupExtendedTypes != VK_TRUE) { return amber::Result("Missing subgroup extended types"); } } else { // Vulkan 1.2 structure was not found. Use separate structures per each // feature. if (feature == kFloat16Int8_Float16 && float16_ptrs->shaderFloat16 != VK_TRUE) { return amber::Result("Missing float16 feature"); } if (feature == kFloat16Int8_Int8 && float16_ptrs->shaderInt8 != VK_TRUE) { return amber::Result("Missing int8 feature"); } if (feature == k8BitStorage_Storage && storage8_ptrs->storageBuffer8BitAccess != VK_TRUE) { return amber::Result("Missing 8-bit storage access"); } if (feature == k8BitStorage_UniformAndStorage && storage8_ptrs->uniformAndStorageBuffer8BitAccess != VK_TRUE) { return amber::Result("Missing 8-bit uniform and storage access"); } if (feature == k8BitStorage_PushConstant && storage8_ptrs->storagePushConstant8 != VK_TRUE) { return amber::Result("Missing 8-bit push constant access"); } if (feature == kShaderSubgroupExtendedTypes && shader_subgroup_extended_types_ptrs->shaderSubgroupExtendedTypes != VK_TRUE) { return amber::Result("Missing subgroup extended types"); } } // If Vulkan 1.3 structure exists the features are set there. if (vulkan13_ptrs) { if (feature == kSubgroupSizeControl && vulkan13_ptrs->subgroupSizeControl != VK_TRUE) { return amber::Result("Missing subgroup size control feature"); } if (feature == kComputeFullSubgroups && vulkan13_ptrs->computeFullSubgroups != VK_TRUE) { return amber::Result("Missing compute full subgroups feature"); } } else { if (feature == kSubgroupSizeControl && subgroup_size_control_features->subgroupSizeControl != VK_TRUE) { return amber::Result("Missing subgroup size control feature"); } if (feature == kComputeFullSubgroups && subgroup_size_control_features->computeFullSubgroups != VK_TRUE) { return amber::Result("Missing compute full subgroups feature"); } } } if (!AreAllExtensionsSupported(available_extensions, required_device_extensions)) { return Result( "Vulkan: Device::Initialize given physical device does not support " "required extensions"); } ptrs_.vkGetPhysicalDeviceMemoryProperties(physical_device_, &physical_memory_properties_); subgroup_size_control_properties_ = {}; const bool needs_subgroup_size_control = std::find(required_features.begin(), required_features.end(), kSubgroupSizeControl) != required_features.end(); bool needs_subgroup_supported_operations = false; bool needs_subgroup_supported_stages = false; // Search for subgroup supported operations requirements. for (const auto& feature : required_features) if (feature.find(kSubgroupSupportedOperations) != std::string::npos) needs_subgroup_supported_operations = true; // Search for subgroup supported stages requirements. for (const auto& feature : required_features) if (feature.find(kSubgroupSupportedStages) != std::string::npos) needs_subgroup_supported_stages = true; const bool needs_subgroup_properties = needs_subgroup_supported_operations || needs_subgroup_supported_stages; if (needs_subgroup_size_control || needs_subgroup_properties) { // Always chain all physical device properties structs in case at least one // of them is needed. VkPhysicalDeviceProperties2 properties2 = {}; VkPhysicalDeviceSubgroupProperties subgroup_properties = {}; VkPhysicalDeviceVulkan11Properties vulkan11_properties = {}; VkSubgroupFeatureFlags subgroup_supported_operations; VkShaderStageFlags subgroup_supported_stages; properties2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; properties2.pNext = &subgroup_size_control_properties_; subgroup_size_control_properties_.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_PROPERTIES_EXT; if (SupportsApiVersion(1, 2, 0)) { subgroup_size_control_properties_.pNext = &vulkan11_properties; vulkan11_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_PROPERTIES; } else { subgroup_size_control_properties_.pNext = &subgroup_properties; subgroup_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES; } if (needs_subgroup_size_control && !SupportsApiVersion(1, 1, 0)) { return Result( "Vulkan: Device::Initialize subgroup size control feature also " "requires an API version of 1.1 or higher"); } if (needs_subgroup_properties && !SupportsApiVersion(1, 1, 0)) { return Result( "Vulkan: Device::Initialize subgroup properties also " "requires an API version of 1.1 or higher"); } ptrs_.vkGetPhysicalDeviceProperties2(physical_device_, &properties2); if (needs_subgroup_supported_operations) { // Read supported subgroup operations from the correct struct depending on // the device API if (SupportsApiVersion(1, 2, 0)) { subgroup_supported_operations = vulkan11_properties.subgroupSupportedOperations; } else { subgroup_supported_operations = subgroup_properties.supportedOperations; } for (const auto& feature : required_features) { if (feature == kSubgroupSupportedOperationsBasic && !(subgroup_supported_operations & VK_SUBGROUP_FEATURE_BASIC_BIT)) { return amber::Result("Missing subgroup operation basic feature"); } if (feature == kSubgroupSupportedOperationsVote && !(subgroup_supported_operations & VK_SUBGROUP_FEATURE_VOTE_BIT)) { return amber::Result("Missing subgroup operation vote feature"); } if (feature == kSubgroupSupportedOperationsArithmetic && !(subgroup_supported_operations & VK_SUBGROUP_FEATURE_ARITHMETIC_BIT)) { return amber::Result("Missing subgroup operation arithmetic feature"); } if (feature == kSubgroupSupportedOperationsBallot && !(subgroup_supported_operations & VK_SUBGROUP_FEATURE_BALLOT_BIT)) { return amber::Result("Missing subgroup operation ballot feature"); } if (feature == kSubgroupSupportedOperationsShuffle && !(subgroup_supported_operations & VK_SUBGROUP_FEATURE_SHUFFLE_BIT)) { return amber::Result("Missing subgroup operation shuffle feature"); } if (feature == kSubgroupSupportedOperationsShuffleRelative && !(subgroup_supported_operations & VK_SUBGROUP_FEATURE_SHUFFLE_RELATIVE_BIT)) { return amber::Result( "Missing subgroup operation shuffle relative feature"); } if (feature == kSubgroupSupportedOperationsClustered && !(subgroup_supported_operations & VK_SUBGROUP_FEATURE_CLUSTERED_BIT)) { return amber::Result("Missing subgroup operation clustered feature"); } if (feature == kSubgroupSupportedOperationsQuad && !(subgroup_supported_operations & VK_SUBGROUP_FEATURE_QUAD_BIT)) { return amber::Result("Missing subgroup operation quad feature"); } } } if (needs_subgroup_supported_stages) { // Read supported subgroup stages from the correct struct depending on the // device API if (SupportsApiVersion(1, 2, 0)) { subgroup_supported_stages = vulkan11_properties.subgroupSupportedStages; } else { subgroup_supported_stages = subgroup_properties.supportedStages; } for (const auto& feature : required_features) { if (feature == kSubgroupSupportedStagesVertex && !(subgroup_supported_stages & VK_SHADER_STAGE_VERTEX_BIT)) { return amber::Result( "Subgroup operations not supported for vertex shader stage"); } if (feature == kSubgroupSupportedStagesTessellationControl && !(subgroup_supported_stages & VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT)) { return amber::Result( "Subgroup operations not supported for tessellation control " "shader stage"); } if (feature == kSubgroupSupportedStagesTessellationEvaluation && !(subgroup_supported_stages & VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT)) { return amber::Result( "Subgroup operations not supported for tessellation evaluation " "shader stage"); } if (feature == kSubgroupSupportedStagesGeometry && !(subgroup_supported_stages & VK_SHADER_STAGE_GEOMETRY_BIT)) { return amber::Result( "Subgroup operations not supported for geometry shader stage"); } if (feature == kSubgroupSupportedStagesFragment && !(subgroup_supported_stages & VK_SHADER_STAGE_FRAGMENT_BIT)) { return amber::Result( "Subgroup operations not supported for fragment shader stage"); } if (feature == kSubgroupSupportedStagesCompute && !(subgroup_supported_stages & VK_SHADER_STAGE_COMPUTE_BIT)) { return amber::Result( "Subgroup operations not supported for compute shader stage"); } } } } return {}; } bool Device::IsFormatSupportedByPhysicalDevice(const Format& format, BufferType type) { VkFormat vk_format = GetVkFormat(format); VkFormatProperties properties = VkFormatProperties(); GetPtrs()->vkGetPhysicalDeviceFormatProperties(physical_device_, vk_format, &properties); VkFormatFeatureFlagBits flag = VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT; bool is_buffer_type_image = false; switch (type) { case BufferType::kColor: case BufferType::kResolve: case BufferType::kStorageImage: flag = VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT; is_buffer_type_image = true; break; case BufferType::kDepthStencil: flag = VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT; is_buffer_type_image = true; break; case BufferType::kSampledImage: case BufferType::kCombinedImageSampler: flag = VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT; is_buffer_type_image = true; break; case BufferType::kVertex: flag = VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT; is_buffer_type_image = false; break; default: return false; } return ((is_buffer_type_image ? properties.optimalTilingFeatures : properties.bufferFeatures) & flag) == flag; } bool Device::HasMemoryFlags(uint32_t memory_type_index, const VkMemoryPropertyFlags flags) const { return (physical_memory_properties_.memoryTypes[memory_type_index] .propertyFlags & flags) == flags; } bool Device::IsMemoryHostAccessible(uint32_t memory_type_index) const { return HasMemoryFlags(memory_type_index, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT); } bool Device::IsMemoryHostCoherent(uint32_t memory_type_index) const { return HasMemoryFlags(memory_type_index, VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); } uint32_t Device::GetMaxPushConstants() const { return physical_device_properties_.limits.maxPushConstantsSize; } bool Device::IsDescriptorSetInBounds(uint32_t descriptor_set) const { VkPhysicalDeviceProperties properties = VkPhysicalDeviceProperties(); GetPtrs()->vkGetPhysicalDeviceProperties(physical_device_, &properties); return properties.limits.maxBoundDescriptorSets > descriptor_set; } VkFormat Device::GetVkFormat(const Format& format) const { VkFormat ret = VK_FORMAT_UNDEFINED; switch (format.GetFormatType()) { case FormatType::kUnknown: ret = VK_FORMAT_UNDEFINED; break; case FormatType::kA1R5G5B5_UNORM_PACK16: ret = VK_FORMAT_A1R5G5B5_UNORM_PACK16; break; case FormatType::kA2B10G10R10_SINT_PACK32: ret = VK_FORMAT_A2B10G10R10_SINT_PACK32; break; case FormatType::kA2B10G10R10_SNORM_PACK32: ret = VK_FORMAT_A2B10G10R10_SNORM_PACK32; break; case FormatType::kA2B10G10R10_SSCALED_PACK32: ret = VK_FORMAT_A2B10G10R10_SSCALED_PACK32; break; case FormatType::kA2B10G10R10_UINT_PACK32: ret = VK_FORMAT_A2B10G10R10_UINT_PACK32; break; case FormatType::kA2B10G10R10_UNORM_PACK32: ret = VK_FORMAT_A2B10G10R10_UNORM_PACK32; break; case FormatType::kA2B10G10R10_USCALED_PACK32: ret = VK_FORMAT_A2B10G10R10_USCALED_PACK32; break; case FormatType::kA2R10G10B10_SINT_PACK32: ret = VK_FORMAT_A2R10G10B10_SINT_PACK32; break; case FormatType::kA2R10G10B10_SNORM_PACK32: ret = VK_FORMAT_A2R10G10B10_SNORM_PACK32; break; case FormatType::kA2R10G10B10_SSCALED_PACK32: ret = VK_FORMAT_A2R10G10B10_SSCALED_PACK32; break; case FormatType::kA2R10G10B10_UINT_PACK32: ret = VK_FORMAT_A2R10G10B10_UINT_PACK32; break; case FormatType::kA2R10G10B10_UNORM_PACK32: ret = VK_FORMAT_A2R10G10B10_UNORM_PACK32; break; case FormatType::kA2R10G10B10_USCALED_PACK32: ret = VK_FORMAT_A2R10G10B10_USCALED_PACK32; break; case FormatType::kA8B8G8R8_SINT_PACK32: ret = VK_FORMAT_A8B8G8R8_SINT_PACK32; break; case FormatType::kA8B8G8R8_SNORM_PACK32: ret = VK_FORMAT_A8B8G8R8_SNORM_PACK32; break; case FormatType::kA8B8G8R8_SRGB_PACK32: ret = VK_FORMAT_A8B8G8R8_SRGB_PACK32; break; case FormatType::kA8B8G8R8_SSCALED_PACK32: ret = VK_FORMAT_A8B8G8R8_SSCALED_PACK32; break; case FormatType::kA8B8G8R8_UINT_PACK32: ret = VK_FORMAT_A8B8G8R8_UINT_PACK32; break; case FormatType::kA8B8G8R8_UNORM_PACK32: ret = VK_FORMAT_A8B8G8R8_UNORM_PACK32; break; case FormatType::kA8B8G8R8_USCALED_PACK32: ret = VK_FORMAT_A8B8G8R8_USCALED_PACK32; break; case FormatType::kB10G11R11_UFLOAT_PACK32: ret = VK_FORMAT_B10G11R11_UFLOAT_PACK32; break; case FormatType::kB4G4R4A4_UNORM_PACK16: ret = VK_FORMAT_B4G4R4A4_UNORM_PACK16; break; case FormatType::kB5G5R5A1_UNORM_PACK16: ret = VK_FORMAT_B5G5R5A1_UNORM_PACK16; break; case FormatType::kB5G6R5_UNORM_PACK16: ret = VK_FORMAT_B5G6R5_UNORM_PACK16; break; case FormatType::kB8G8R8A8_SINT: ret = VK_FORMAT_B8G8R8A8_SINT; break; case FormatType::kB8G8R8A8_SNORM: ret = VK_FORMAT_B8G8R8A8_SNORM; break; case FormatType::kB8G8R8A8_SRGB: ret = VK_FORMAT_B8G8R8A8_SRGB; break; case FormatType::kB8G8R8A8_SSCALED: ret = VK_FORMAT_B8G8R8A8_SSCALED; break; case FormatType::kB8G8R8A8_UINT: ret = VK_FORMAT_B8G8R8A8_UINT; break; case FormatType::kB8G8R8A8_UNORM: ret = VK_FORMAT_B8G8R8A8_UNORM; break; case FormatType::kB8G8R8A8_USCALED: ret = VK_FORMAT_B8G8R8A8_USCALED; break; case FormatType::kB8G8R8_SINT: ret = VK_FORMAT_B8G8R8_SINT; break; case FormatType::kB8G8R8_SNORM: ret = VK_FORMAT_B8G8R8_SNORM; break; case FormatType::kB8G8R8_SRGB: ret = VK_FORMAT_B8G8R8_SRGB; break; case FormatType::kB8G8R8_SSCALED: ret = VK_FORMAT_B8G8R8_SSCALED; break; case FormatType::kB8G8R8_UINT: ret = VK_FORMAT_B8G8R8_UINT; break; case FormatType::kB8G8R8_UNORM: ret = VK_FORMAT_B8G8R8_UNORM; break; case FormatType::kB8G8R8_USCALED: ret = VK_FORMAT_B8G8R8_USCALED; break; case FormatType::kD16_UNORM: ret = VK_FORMAT_D16_UNORM; break; case FormatType::kD16_UNORM_S8_UINT: ret = VK_FORMAT_D16_UNORM_S8_UINT; break; case FormatType::kD24_UNORM_S8_UINT: ret = VK_FORMAT_D24_UNORM_S8_UINT; break; case FormatType::kD32_SFLOAT: ret = VK_FORMAT_D32_SFLOAT; break; case FormatType::kD32_SFLOAT_S8_UINT: ret = VK_FORMAT_D32_SFLOAT_S8_UINT; break; case FormatType::kR16G16B16A16_SFLOAT: ret = VK_FORMAT_R16G16B16A16_SFLOAT; break; case FormatType::kR16G16B16A16_SINT: ret = VK_FORMAT_R16G16B16A16_SINT; break; case FormatType::kR16G16B16A16_SNORM: ret = VK_FORMAT_R16G16B16A16_SNORM; break; case FormatType::kR16G16B16A16_SSCALED: ret = VK_FORMAT_R16G16B16A16_SSCALED; break; case FormatType::kR16G16B16A16_UINT: ret = VK_FORMAT_R16G16B16A16_UINT; break; case FormatType::kR16G16B16A16_UNORM: ret = VK_FORMAT_R16G16B16A16_UNORM; break; case FormatType::kR16G16B16A16_USCALED: ret = VK_FORMAT_R16G16B16A16_USCALED; break; case FormatType::kR16G16B16_SFLOAT: ret = VK_FORMAT_R16G16B16_SFLOAT; break; case FormatType::kR16G16B16_SINT: ret = VK_FORMAT_R16G16B16_SINT; break; case FormatType::kR16G16B16_SNORM: ret = VK_FORMAT_R16G16B16_SNORM; break; case FormatType::kR16G16B16_SSCALED: ret = VK_FORMAT_R16G16B16_SSCALED; break; case FormatType::kR16G16B16_UINT: ret = VK_FORMAT_R16G16B16_UINT; break; case FormatType::kR16G16B16_UNORM: ret = VK_FORMAT_R16G16B16_UNORM; break; case FormatType::kR16G16B16_USCALED: ret = VK_FORMAT_R16G16B16_USCALED; break; case FormatType::kR16G16_SFLOAT: ret = VK_FORMAT_R16G16_SFLOAT; break; case FormatType::kR16G16_SINT: ret = VK_FORMAT_R16G16_SINT; break; case FormatType::kR16G16_SNORM: ret = VK_FORMAT_R16G16_SNORM; break; case FormatType::kR16G16_SSCALED: ret = VK_FORMAT_R16G16_SSCALED; break; case FormatType::kR16G16_UINT: ret = VK_FORMAT_R16G16_UINT; break; case FormatType::kR16G16_UNORM: ret = VK_FORMAT_R16G16_UNORM; break; case FormatType::kR16G16_USCALED: ret = VK_FORMAT_R16G16_USCALED; break; case FormatType::kR16_SFLOAT: ret = VK_FORMAT_R16_SFLOAT; break; case FormatType::kR16_SINT: ret = VK_FORMAT_R16_SINT; break; case FormatType::kR16_SNORM: ret = VK_FORMAT_R16_SNORM; break; case FormatType::kR16_SSCALED: ret = VK_FORMAT_R16_SSCALED; break; case FormatType::kR16_UINT: ret = VK_FORMAT_R16_UINT; break; case FormatType::kR16_UNORM: ret = VK_FORMAT_R16_UNORM; break; case FormatType::kR16_USCALED: ret = VK_FORMAT_R16_USCALED; break; case FormatType::kR32G32B32A32_SFLOAT: ret = VK_FORMAT_R32G32B32A32_SFLOAT; break; case FormatType::kR32G32B32A32_SINT: ret = VK_FORMAT_R32G32B32A32_SINT; break; case FormatType::kR32G32B32A32_UINT: ret = VK_FORMAT_R32G32B32A32_UINT; break; case FormatType::kR32G32B32_SFLOAT: ret = VK_FORMAT_R32G32B32_SFLOAT; break; case FormatType::kR32G32B32_SINT: ret = VK_FORMAT_R32G32B32_SINT; break; case FormatType::kR32G32B32_UINT: ret = VK_FORMAT_R32G32B32_UINT; break; case FormatType::kR32G32_SFLOAT: ret = VK_FORMAT_R32G32_SFLOAT; break; case FormatType::kR32G32_SINT: ret = VK_FORMAT_R32G32_SINT; break; case FormatType::kR32G32_UINT: ret = VK_FORMAT_R32G32_UINT; break; case FormatType::kR32_SFLOAT: ret = VK_FORMAT_R32_SFLOAT; break; case FormatType::kR32_SINT: ret = VK_FORMAT_R32_SINT; break; case FormatType::kR32_UINT: ret = VK_FORMAT_R32_UINT; break; case FormatType::kR4G4B4A4_UNORM_PACK16: ret = VK_FORMAT_R4G4B4A4_UNORM_PACK16; break; case FormatType::kR4G4_UNORM_PACK8: ret = VK_FORMAT_R4G4_UNORM_PACK8; break; case FormatType::kR5G5B5A1_UNORM_PACK16: ret = VK_FORMAT_R5G5B5A1_UNORM_PACK16; break; case FormatType::kR5G6B5_UNORM_PACK16: ret = VK_FORMAT_R5G6B5_UNORM_PACK16; break; case FormatType::kR64G64B64A64_SFLOAT: ret = VK_FORMAT_R64G64B64A64_SFLOAT; break; case FormatType::kR64G64B64A64_SINT: ret = VK_FORMAT_R64G64B64A64_SINT; break; case FormatType::kR64G64B64A64_UINT: ret = VK_FORMAT_R64G64B64A64_UINT; break; case FormatType::kR64G64B64_SFLOAT: ret = VK_FORMAT_R64G64B64_SFLOAT; break; case FormatType::kR64G64B64_SINT: ret = VK_FORMAT_R64G64B64_SINT; break; case FormatType::kR64G64B64_UINT: ret = VK_FORMAT_R64G64B64_UINT; break; case FormatType::kR64G64_SFLOAT: ret = VK_FORMAT_R64G64_SFLOAT; break; case FormatType::kR64G64_SINT: ret = VK_FORMAT_R64G64_SINT; break; case FormatType::kR64G64_UINT: ret = VK_FORMAT_R64G64_UINT; break; case FormatType::kR64_SFLOAT: ret = VK_FORMAT_R64_SFLOAT; break; case FormatType::kR64_SINT: ret = VK_FORMAT_R64_SINT; break; case FormatType::kR64_UINT: ret = VK_FORMAT_R64_UINT; break; case FormatType::kR8G8B8A8_SINT: ret = VK_FORMAT_R8G8B8A8_SINT; break; case FormatType::kR8G8B8A8_SNORM: ret = VK_FORMAT_R8G8B8A8_SNORM; break; case FormatType::kR8G8B8A8_SRGB: ret = VK_FORMAT_R8G8B8A8_SRGB; break; case FormatType::kR8G8B8A8_SSCALED: ret = VK_FORMAT_R8G8B8A8_SSCALED; break; case FormatType::kR8G8B8A8_UINT: ret = VK_FORMAT_R8G8B8A8_UINT; break; case FormatType::kR8G8B8A8_UNORM: ret = VK_FORMAT_R8G8B8A8_UNORM; break; case FormatType::kR8G8B8A8_USCALED: ret = VK_FORMAT_R8G8B8A8_USCALED; break; case FormatType::kR8G8B8_SINT: ret = VK_FORMAT_R8G8B8_SINT; break; case FormatType::kR8G8B8_SNORM: ret = VK_FORMAT_R8G8B8_SNORM; break; case FormatType::kR8G8B8_SRGB: ret = VK_FORMAT_R8G8B8_SRGB; break; case FormatType::kR8G8B8_SSCALED: ret = VK_FORMAT_R8G8B8_SSCALED; break; case FormatType::kR8G8B8_UINT: ret = VK_FORMAT_R8G8B8_UINT; break; case FormatType::kR8G8B8_UNORM: ret = VK_FORMAT_R8G8B8_UNORM; break; case FormatType::kR8G8B8_USCALED: ret = VK_FORMAT_R8G8B8_USCALED; break; case FormatType::kR8G8_SINT: ret = VK_FORMAT_R8G8_SINT; break; case FormatType::kR8G8_SNORM: ret = VK_FORMAT_R8G8_SNORM; break; case FormatType::kR8G8_SRGB: ret = VK_FORMAT_R8G8_SRGB; break; case FormatType::kR8G8_SSCALED: ret = VK_FORMAT_R8G8_SSCALED; break; case FormatType::kR8G8_UINT: ret = VK_FORMAT_R8G8_UINT; break; case FormatType::kR8G8_UNORM: ret = VK_FORMAT_R8G8_UNORM; break; case FormatType::kR8G8_USCALED: ret = VK_FORMAT_R8G8_USCALED; break; case FormatType::kR8_SINT: ret = VK_FORMAT_R8_SINT; break; case FormatType::kR8_SNORM: ret = VK_FORMAT_R8_SNORM; break; case FormatType::kR8_SRGB: ret = VK_FORMAT_R8_SRGB; break; case FormatType::kR8_SSCALED: ret = VK_FORMAT_R8_SSCALED; break; case FormatType::kR8_UINT: ret = VK_FORMAT_R8_UINT; break; case FormatType::kR8_UNORM: ret = VK_FORMAT_R8_UNORM; break; case FormatType::kR8_USCALED: ret = VK_FORMAT_R8_USCALED; break; case FormatType::kS8_UINT: ret = VK_FORMAT_S8_UINT; break; case FormatType::kX8_D24_UNORM_PACK32: ret = VK_FORMAT_X8_D24_UNORM_PACK32; break; } return ret; } bool Device::IsRequiredSubgroupSizeSupported( const ShaderType type, const uint32_t required_subgroup_size) const { VkShaderStageFlagBits stage = {}; switch (type) { case kShaderTypeGeometry: stage = VK_SHADER_STAGE_GEOMETRY_BIT; break; case kShaderTypeFragment: stage = VK_SHADER_STAGE_FRAGMENT_BIT; break; case kShaderTypeVertex: stage = VK_SHADER_STAGE_VERTEX_BIT; break; case kShaderTypeTessellationControl: stage = VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT; break; case kShaderTypeTessellationEvaluation: stage = VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT; break; case kShaderTypeCompute: stage = VK_SHADER_STAGE_COMPUTE_BIT; break; default: return false; } if ((stage & subgroup_size_control_properties_.requiredSubgroupSizeStages) == 0) { return false; } if (required_subgroup_size == 0 || required_subgroup_size < subgroup_size_control_properties_.minSubgroupSize || required_subgroup_size > subgroup_size_control_properties_.maxSubgroupSize) { return false; } return true; } uint32_t Device::GetMinSubgroupSize() const { return subgroup_size_control_properties_.minSubgroupSize; } uint32_t Device::GetMaxSubgroupSize() const { return subgroup_size_control_properties_.maxSubgroupSize; } } // namespace vulkan } // namespace amber