| // Copyright 2020 The Dawn 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 "dawn_native/vulkan/VulkanExtensions.h" | |
| #include "common/Assert.h" | |
| #include "common/vulkan_platform.h" | |
| #include <array> | |
| #include <limits> | |
| namespace dawn_native { namespace vulkan { | |
| static constexpr uint32_t VulkanVersion_1_1 = VK_MAKE_VERSION(1, 1, 0); | |
| static constexpr uint32_t VulkanVersion_1_2 = VK_MAKE_VERSION(1, 2, 0); | |
| static constexpr uint32_t NeverPromoted = std::numeric_limits<uint32_t>::max(); | |
| // A static array for InstanceExtInfo that can be indexed with InstanceExts. | |
| // GetInstanceExtInfo checks that "index" matches the index used to access this array so an | |
| // assert will fire if it isn't in the correct order. | |
| static constexpr size_t kInstanceExtCount = static_cast<size_t>(InstanceExt::EnumCount); | |
| static constexpr std::array<InstanceExtInfo, kInstanceExtCount> sInstanceExtInfos{{ | |
| // | |
| {InstanceExt::GetPhysicalDeviceProperties2, "VK_KHR_get_physical_device_properties2", | |
| VulkanVersion_1_1}, | |
| {InstanceExt::ExternalMemoryCapabilities, "VK_KHR_external_memory_capabilities", | |
| VulkanVersion_1_1}, | |
| {InstanceExt::ExternalSemaphoreCapabilities, "VK_KHR_external_semaphore_capabilities", | |
| VulkanVersion_1_1}, | |
| {InstanceExt::Surface, "VK_KHR_surface", NeverPromoted}, | |
| {InstanceExt::FuchsiaImagePipeSurface, "VK_FUCHSIA_imagepipe_surface", NeverPromoted}, | |
| {InstanceExt::MetalSurface, "VK_EXT_metal_surface", NeverPromoted}, | |
| {InstanceExt::WaylandSurface, "VK_KHR_wayland_surface", NeverPromoted}, | |
| {InstanceExt::Win32Surface, "VK_KHR_win32_surface", NeverPromoted}, | |
| {InstanceExt::XcbSurface, "VK_KHR_xcb_surface", NeverPromoted}, | |
| {InstanceExt::XlibSurface, "VK_KHR_xlib_surface", NeverPromoted}, | |
| {InstanceExt::DebugUtils, "VK_EXT_debug_utils", NeverPromoted}, | |
| {InstanceExt::ValidationFeatures, "VK_EXT_validation_features", NeverPromoted}, | |
| // | |
| }}; | |
| const InstanceExtInfo& GetInstanceExtInfo(InstanceExt ext) { | |
| uint32_t index = static_cast<uint32_t>(ext); | |
| ASSERT(index < sInstanceExtInfos.size()); | |
| ASSERT(sInstanceExtInfos[index].index == ext); | |
| return sInstanceExtInfos[index]; | |
| } | |
| std::unordered_map<std::string, InstanceExt> CreateInstanceExtNameMap() { | |
| std::unordered_map<std::string, InstanceExt> result; | |
| for (const InstanceExtInfo& info : sInstanceExtInfos) { | |
| result[info.name] = info.index; | |
| } | |
| return result; | |
| } | |
| InstanceExtSet EnsureDependencies(const InstanceExtSet& advertisedExts) { | |
| // We need to check that all transitive dependencies of extensions are advertised. | |
| // To do that in a single pass and no data structures, the extensions are topologically | |
| // sorted in the definition of InstanceExt. | |
| // To ensure the order is correct, we mark visited extensions in `visitedSet` and each | |
| // dependency check will first assert all its dependents have been visited. | |
| InstanceExtSet visitedSet; | |
| InstanceExtSet trimmedSet; | |
| auto HasDep = [&](InstanceExt ext) -> bool { | |
| ASSERT(visitedSet[ext]); | |
| return trimmedSet[ext]; | |
| }; | |
| for (uint32_t i = 0; i < sInstanceExtInfos.size(); i++) { | |
| InstanceExt ext = static_cast<InstanceExt>(i); | |
| bool hasDependencies = false; | |
| switch (ext) { | |
| case InstanceExt::GetPhysicalDeviceProperties2: | |
| case InstanceExt::Surface: | |
| case InstanceExt::DebugUtils: | |
| case InstanceExt::ValidationFeatures: | |
| hasDependencies = true; | |
| break; | |
| case InstanceExt::ExternalMemoryCapabilities: | |
| case InstanceExt::ExternalSemaphoreCapabilities: | |
| hasDependencies = HasDep(InstanceExt::GetPhysicalDeviceProperties2); | |
| break; | |
| case InstanceExt::FuchsiaImagePipeSurface: | |
| case InstanceExt::MetalSurface: | |
| case InstanceExt::WaylandSurface: | |
| case InstanceExt::Win32Surface: | |
| case InstanceExt::XcbSurface: | |
| case InstanceExt::XlibSurface: | |
| hasDependencies = HasDep(InstanceExt::Surface); | |
| break; | |
| case InstanceExt::EnumCount: | |
| UNREACHABLE(); | |
| } | |
| trimmedSet.set(ext, hasDependencies && advertisedExts[ext]); | |
| visitedSet.set(ext, true); | |
| } | |
| return trimmedSet; | |
| } | |
| void MarkPromotedExtensions(InstanceExtSet* extensions, uint32_t version) { | |
| for (const InstanceExtInfo& info : sInstanceExtInfos) { | |
| if (info.versionPromoted <= version) { | |
| extensions->set(info.index, true); | |
| } | |
| } | |
| } | |
| static constexpr size_t kDeviceExtCount = static_cast<size_t>(DeviceExt::EnumCount); | |
| static constexpr std::array<DeviceExtInfo, kDeviceExtCount> sDeviceExtInfos{{ | |
| // | |
| {DeviceExt::BindMemory2, "VK_KHR_bind_memory2", VulkanVersion_1_1}, | |
| {DeviceExt::Maintenance1, "VK_KHR_maintenance1", VulkanVersion_1_1}, | |
| {DeviceExt::StorageBufferStorageClass, "VK_KHR_storage_buffer_storage_class", | |
| VulkanVersion_1_1}, | |
| {DeviceExt::GetPhysicalDeviceProperties2, "VK_KHR_get_physical_device_properties2", | |
| VulkanVersion_1_1}, | |
| {DeviceExt::GetMemoryRequirements2, "VK_KHR_get_memory_requirements2", VulkanVersion_1_1}, | |
| {DeviceExt::ExternalMemoryCapabilities, "VK_KHR_external_memory_capabilities", | |
| VulkanVersion_1_1}, | |
| {DeviceExt::ExternalSemaphoreCapabilities, "VK_KHR_external_semaphore_capabilities", | |
| VulkanVersion_1_1}, | |
| {DeviceExt::ExternalMemory, "VK_KHR_external_memory", VulkanVersion_1_1}, | |
| {DeviceExt::ExternalSemaphore, "VK_KHR_external_semaphore", VulkanVersion_1_1}, | |
| {DeviceExt::_16BitStorage, "VK_KHR_16bit_storage", VulkanVersion_1_1}, | |
| {DeviceExt::SamplerYCbCrConversion, "VK_KHR_sampler_ycbcr_conversion", VulkanVersion_1_1}, | |
| {DeviceExt::DriverProperties, "VK_KHR_driver_properties", VulkanVersion_1_2}, | |
| {DeviceExt::ImageFormatList, "VK_KHR_image_format_list", VulkanVersion_1_2}, | |
| {DeviceExt::ShaderFloat16Int8, "VK_KHR_shader_float16_int8", VulkanVersion_1_2}, | |
| {DeviceExt::ExternalMemoryFD, "VK_KHR_external_memory_fd", NeverPromoted}, | |
| {DeviceExt::ExternalMemoryDmaBuf, "VK_EXT_external_memory_dma_buf", NeverPromoted}, | |
| {DeviceExt::ExternalMemoryZirconHandle, "VK_FUCHSIA_external_memory", NeverPromoted}, | |
| {DeviceExt::ExternalSemaphoreFD, "VK_KHR_external_semaphore_fd", NeverPromoted}, | |
| {DeviceExt::ExternalSemaphoreZirconHandle, "VK_FUCHSIA_external_semaphore", NeverPromoted}, | |
| {DeviceExt::ImageDrmFormatModifier, "VK_EXT_image_drm_format_modifier", NeverPromoted}, | |
| {DeviceExt::Swapchain, "VK_KHR_swapchain", NeverPromoted}, | |
| {DeviceExt::SubgroupSizeControl, "VK_EXT_subgroup_size_control", NeverPromoted}, | |
| // | |
| }}; | |
| const DeviceExtInfo& GetDeviceExtInfo(DeviceExt ext) { | |
| uint32_t index = static_cast<uint32_t>(ext); | |
| ASSERT(index < sDeviceExtInfos.size()); | |
| ASSERT(sDeviceExtInfos[index].index == ext); | |
| return sDeviceExtInfos[index]; | |
| } | |
| std::unordered_map<std::string, DeviceExt> CreateDeviceExtNameMap() { | |
| std::unordered_map<std::string, DeviceExt> result; | |
| for (const DeviceExtInfo& info : sDeviceExtInfos) { | |
| result[info.name] = info.index; | |
| } | |
| return result; | |
| } | |
| DeviceExtSet EnsureDependencies(const DeviceExtSet& advertisedExts, | |
| const InstanceExtSet& instanceExts, | |
| uint32_t icdVersion) { | |
| // This is very similar to EnsureDependencies for instanceExtSet. See comment there for | |
| // an explanation of what happens. | |
| DeviceExtSet visitedSet; | |
| DeviceExtSet trimmedSet; | |
| auto HasDep = [&](DeviceExt ext) -> bool { | |
| ASSERT(visitedSet[ext]); | |
| return trimmedSet[ext]; | |
| }; | |
| for (uint32_t i = 0; i < sDeviceExtInfos.size(); i++) { | |
| DeviceExt ext = static_cast<DeviceExt>(i); | |
| bool hasDependencies = false; | |
| switch (ext) { | |
| // Happy extensions don't need anybody else! | |
| case DeviceExt::BindMemory2: | |
| case DeviceExt::GetMemoryRequirements2: | |
| case DeviceExt::Maintenance1: | |
| case DeviceExt::ImageFormatList: | |
| case DeviceExt::StorageBufferStorageClass: | |
| hasDependencies = true; | |
| break; | |
| // Physical device extensions technically don't require the instance to support | |
| // them but VulkanFunctions only loads the function pointers if the instance | |
| // advertises the extension. So if we didn't have this check, we'd risk a calling | |
| // a nullptr. | |
| case DeviceExt::GetPhysicalDeviceProperties2: | |
| hasDependencies = instanceExts[InstanceExt::GetPhysicalDeviceProperties2]; | |
| break; | |
| case DeviceExt::ExternalMemoryCapabilities: | |
| hasDependencies = instanceExts[InstanceExt::ExternalMemoryCapabilities] && | |
| HasDep(DeviceExt::GetPhysicalDeviceProperties2); | |
| break; | |
| case DeviceExt::ExternalSemaphoreCapabilities: | |
| hasDependencies = instanceExts[InstanceExt::ExternalSemaphoreCapabilities] && | |
| HasDep(DeviceExt::GetPhysicalDeviceProperties2); | |
| break; | |
| case DeviceExt::ImageDrmFormatModifier: | |
| hasDependencies = HasDep(DeviceExt::BindMemory2) && | |
| HasDep(DeviceExt::GetPhysicalDeviceProperties2) && | |
| HasDep(DeviceExt::ImageFormatList) && | |
| HasDep(DeviceExt::SamplerYCbCrConversion); | |
| break; | |
| case DeviceExt::Swapchain: | |
| hasDependencies = instanceExts[InstanceExt::Surface]; | |
| break; | |
| case DeviceExt::SamplerYCbCrConversion: | |
| hasDependencies = HasDep(DeviceExt::Maintenance1) && | |
| HasDep(DeviceExt::BindMemory2) && | |
| HasDep(DeviceExt::GetMemoryRequirements2) && | |
| HasDep(DeviceExt::GetPhysicalDeviceProperties2); | |
| break; | |
| case DeviceExt::DriverProperties: | |
| case DeviceExt::ShaderFloat16Int8: | |
| hasDependencies = HasDep(DeviceExt::GetPhysicalDeviceProperties2); | |
| break; | |
| case DeviceExt::ExternalMemory: | |
| hasDependencies = HasDep(DeviceExt::ExternalMemoryCapabilities); | |
| break; | |
| case DeviceExt::ExternalSemaphore: | |
| hasDependencies = HasDep(DeviceExt::ExternalSemaphoreCapabilities); | |
| break; | |
| case DeviceExt::ExternalMemoryFD: | |
| case DeviceExt::ExternalMemoryZirconHandle: | |
| hasDependencies = HasDep(DeviceExt::ExternalMemory); | |
| break; | |
| case DeviceExt::ExternalMemoryDmaBuf: | |
| hasDependencies = HasDep(DeviceExt::ExternalMemoryFD); | |
| break; | |
| case DeviceExt::ExternalSemaphoreFD: | |
| case DeviceExt::ExternalSemaphoreZirconHandle: | |
| hasDependencies = HasDep(DeviceExt::ExternalSemaphore); | |
| break; | |
| case DeviceExt::_16BitStorage: | |
| hasDependencies = HasDep(DeviceExt::GetPhysicalDeviceProperties2) && | |
| HasDep(DeviceExt::StorageBufferStorageClass); | |
| break; | |
| case DeviceExt::SubgroupSizeControl: | |
| // Using the extension requires DeviceExt::GetPhysicalDeviceProperties2, but we | |
| // don't need to check for it as it also requires Vulkan 1.1 in which | |
| // VK_KHR_get_physical_device_properties2 was promoted. | |
| hasDependencies = icdVersion >= VulkanVersion_1_1; | |
| break; | |
| case DeviceExt::EnumCount: | |
| UNREACHABLE(); | |
| } | |
| trimmedSet.set(ext, hasDependencies && advertisedExts[ext]); | |
| visitedSet.set(ext, true); | |
| } | |
| return trimmedSet; | |
| } | |
| void MarkPromotedExtensions(DeviceExtSet* extensions, uint32_t version) { | |
| for (const DeviceExtInfo& info : sDeviceExtInfos) { | |
| if (info.versionPromoted <= version) { | |
| extensions->set(info.index, true); | |
| } | |
| } | |
| } | |
| // A static array for VulkanLayerInfo that can be indexed with VulkanLayers. | |
| // GetVulkanLayerInfo checks that "index" matches the index used to access this array so an | |
| // assert will fire if it isn't in the correct order. | |
| static constexpr size_t kVulkanLayerCount = static_cast<size_t>(VulkanLayer::EnumCount); | |
| static constexpr std::array<VulkanLayerInfo, kVulkanLayerCount> sVulkanLayerInfos{{ | |
| // | |
| {VulkanLayer::Validation, "VK_LAYER_KHRONOS_validation"}, | |
| {VulkanLayer::LunargVkTrace, "VK_LAYER_LUNARG_vktrace"}, | |
| {VulkanLayer::RenderDocCapture, "VK_LAYER_RENDERDOC_Capture"}, | |
| {VulkanLayer::FuchsiaImagePipeSwapchain, "VK_LAYER_FUCHSIA_imagepipe_swapchain"}, | |
| // | |
| }}; | |
| const VulkanLayerInfo& GetVulkanLayerInfo(VulkanLayer layer) { | |
| uint32_t index = static_cast<uint32_t>(layer); | |
| ASSERT(index < sVulkanLayerInfos.size()); | |
| ASSERT(sVulkanLayerInfos[index].layer == layer); | |
| return sVulkanLayerInfos[index]; | |
| } | |
| std::unordered_map<std::string, VulkanLayer> CreateVulkanLayerNameMap() { | |
| std::unordered_map<std::string, VulkanLayer> result; | |
| for (const VulkanLayerInfo& info : sVulkanLayerInfos) { | |
| result[info.name] = info.layer; | |
| } | |
| return result; | |
| } | |
| }} // namespace dawn_native::vulkan |