| /* |
| * Copyright 2025 Google Inc. |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "include/gpu/vk/VulkanPreferredFeatures.h" |
| |
| #include "include/private/base/SkAssert.h" |
| #include "include/private/base/SkDebug.h" |
| #include "src/gpu/vk/VulkanUtilsPriv.h" |
| |
| #include <cstdint> |
| #include <cstring> |
| #include <vector> |
| |
| namespace skgpu { |
| |
| /* |
| * # Overview |
| * |
| * Skia requires the application to create the Vulkan instance and device. This operation involves |
| * enabling extensions and device features that the application would like to take advantage of. |
| * However, the application is not necessarily aware of all the extensions and features that _Skia_ |
| * would like to use (and it's too onerous to require them to keep up). If the application does not |
| * enable some extensions, Skia's performance may degrade. |
| * |
| * VulkanPreferredFeatures is used to let Skia modify the list of extensions and device features |
| * before the application creates the device. The application thus never needs to worry about what |
| * Skia might or might not use. |
| * |
| * # How It Works |
| * |
| * There are three main operations: |
| * |
| * - Add instance extensions. Before creating the Vulkan instance, Skia could add more extensions to |
| * enable. |
| * - Add device features to query. This lets Skia discover what features are available. |
| * - Enable device features. |
| * |
| * Device features and extensions are where the complexity is. In particular, there is: |
| * |
| * - The list of _available_ extensions. When adding features to query, this information is |
| * assembled in DeviceExtensions for faster look up. |
| * - The list of _enabled_ extensions. When enabling features, this information is also assembled in |
| * DeviceExtensions. |
| * - The list of device features. This is a chain of VkPhysicalDeviceFooFeatures structs, starting |
| * with VkPhysicalDeviceFeatures2. FeaturesToAdd is used to track whether any of these feature |
| * structs need to be chained or not. The choice of feature structs depends on the API version |
| * used by the application, following the extension promotions to core. |
| * |
| * See addFeaturesToQuery() for details of how feature structs are chosen and chained to be queried |
| * in addition to the application's own set of feature structs. |
| * |
| * See addFeaturesToEnable() for details of how the application-provided chain of feature structs is |
| * manipulated to include the previously-queried features. Based on Vulkan rules, some structs are |
| * mutually exclusive, which is handled by this function as well. |
| * |
| * # How to Add Support for New Extensions |
| * |
| * When adding a new extension, the following changes must be made: |
| * |
| * - Add a flag corresponding to the extension to DeviceExtensions |
| * - Detect the extension and set it in mark_device_extensions(). |
| * - Add the extensions's feature struct to VulkanPreferredFeatures. This provides storage for the |
| * struct so it lives long enough (and not go out of scope too early). |
| * - Add a flag corresponding to the feature struct to FeaturesToAdd. |
| * - In addFeaturesToQuery(), initalize the sType of the feature struct based on the flag in |
| * DeviceExtensions. If extension is promoted to core in the API version used by the |
| * application, the VkPhysicalDeviceVulkan??Features struct should be used for the same feature |
| * instead, if available. |
| * - In get_features_to_add(), if the extension is promoted to a core version, set the |
| * FeaturesToAdd flag to false as the VkPhysicalDeviceVulkan??Features struct would be used to |
| * query the same feature. |
| * - In get_features_to_add() additionally, set the FeaturesToAdd flag to false if the application |
| * has already included the corresponding feature struct in its own chain. |
| * - Back in addFeaturesToQuery(), chain the feature struct if the FeaturesToAdd flag is set. |
| * - In addFeaturesToEnable(): |
| * - Set the FeaturesToAdd flag to true iff the sType of the feature struct is set. |
| * - If the extension is not enabled by the app but the FeaturesToAdd flag is set, add the |
| * extension to appExtensions. |
| * - Disable optional features in the struct if not going to be used by Skia. Note that this |
| * modifies the struct owned by VulkanPreferredFeatures; if that's the one that's chained, the |
| * application didn't care about these features. If the application has chained its own struct |
| * of this type, it's unaffected by this operation. |
| * - Detect the struct in the big switch. If promoted to core, make sure to drop the struct. If |
| * not, chain it if the extension is enabled. |
| * - In the end, chain the feature struct if still needed to be added. |
| * - Make sure to update the existing tests in tests/VkPreferredFeaturesTest.cpp to make sure the |
| * extension and its feature is enabled appropropriately. |
| * |
| * # How to Add Support for New Vulkan Versions |
| * |
| * When adding a support for a new Vulkan version, the following changes must be made: |
| * |
| * - Add the VkPhysicalDeviceVulkan??Features struct to VulkanPreferredFeatures, and a flag for it |
| * in FeaturesToAdd. |
| * - In get_features_to_add(), check for the Vulkan version and decided if that struct should be |
| * used, or the individual feature structs from extensions that were promoted to core in that |
| * version. |
| * - Check in the same function if the application has already included |
| * VkPhysicalDeviceVulkan??Features. |
| * - In addFeaturesToQuery(), similarly check for the Vulkan version and set the sType of either the |
| * VkPhysicalDeviceVulkan??Features struct or the individual feature structs from promoted |
| * extensions. |
| * - Chain the VkPhysicalDeviceVulkan??Features struct if the FeaturesToAdd flag is set. |
| * - In addFeaturesToEnable(): |
| * - Set the FeaturesToAdd flag to true iff the sType of the feature struct is set. |
| * - Disable optional features in the struct if not going to be used by Skia. |
| * - Detect the struct in the big switch. If pointer does not point to VulkanPreferredFeatures's |
| * own struct, copy the application-enabled features to VulkanPreferredFeatures's struct. Note |
| * that for simplicity, Skia always chains its own instance of VkPhysicalDeviceVulkan??Features. |
| * - Based on valid usage that forbids including VkPhysicalDeviceVulkan??Features and feature |
| * structs from promoted extensions, detect the latter structs in the big switch, copy their |
| * features to VkPhysicalDeviceVulkan??Features and drop them from the chain. |
| * - In the end, chain the VkPhysicalDeviceVulkan??Features feature struct. |
| * - Make sure to update tests/VkPreferredFeaturesTest.cpp with a new test that ensures the |
| * promotion rules were followed. |
| */ |
| |
| namespace { |
| struct DeviceExtensions { |
| // Extensions that Skia may benefit from enabling if available. |
| bool fRasterizationOrderAttachmentAccessARM = false; |
| bool fRasterizationOrderAttachmentAccessEXT = false; |
| bool fBlendOperationAdvancedEXT = false; |
| bool fExtendedDynamicStateEXT = false; |
| bool fExtendedDynamicState2EXT = false; |
| bool fVertexInputDynamicStateEXT = false; |
| bool fGraphicsPipelineLibraryEXT = false; |
| bool fSamplerYcbcrConversionKHR = false; |
| bool fRGBA10x6FormatsEXT = false; |
| bool fSynchronization2KHR = false; |
| bool fDynamicRenderingKHR = false; |
| bool fDynamicRenderingLocalReadKHR = false; |
| bool fMultisampledRenderToSingleSampledEXT = false; |
| bool fHostImageCopyEXT = false; |
| bool fPipelineCreationCacheControlEXT = false; |
| bool fDriverPropertiesKHR = false; |
| bool fCreateRenderpass2KHR = false; |
| bool fLoadStoreOpNoneEXT = false; |
| bool fLoadStoreOpNoneKHR = false; |
| bool fConservativeRasterizationEXT = false; |
| bool fPipelineLibraryKHR = false; |
| bool fCopyCommands2KHR = false; |
| bool fFormatFeatureFlags2KHR = false; |
| bool fDepthStencilResolveKHR = false; |
| |
| // Featureless extensions that the application may have enabled and that Skia may need to enable |
| // via a VkPhysicalDeviceVulkanNNFeatures struct. See "Table 1. Extension Feature Aliases" in |
| // the Features chapter of the spec: |
| // |
| // Extension: Features on promotion to core: |
| // VK_KHR_shader_draw_parameters shaderDrawParameters |
| // VK_KHR_draw_indirect_count drawIndirectCount |
| // VK_KHR_sampler_mirror_clamp_to_edge samplerMirrorClampToEdge |
| // VK_EXT_descriptor_indexing descriptorIndexing |
| // VK_EXT_sampler_filter_minmax samplerFilterMinmax |
| // VK_EXT_shader_viewport_index_layer shaderOutputViewportIndex, shaderOutputLayer |
| // VK_KHR_push_descriptor pushDescriptor |
| bool fShaderDrawParametersKHR = false; |
| bool fDrawIndirectCountKHR = false; |
| bool fSamplerMirrorClampToEdgeKHR = false; |
| bool fDescriptorIndexingEXT = false; |
| bool fSamplerFilterMinmaxEXT = false; |
| bool fShaderViewportIndexLayerEXT = false; |
| bool fPushDescriptorKHR = false; |
| }; |
| |
| void mark_device_extensions(DeviceExtensions& exts, const char* name) { |
| if (strcmp(name, VK_ARM_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_EXTENSION_NAME) == 0) { |
| exts.fRasterizationOrderAttachmentAccessARM = true; |
| } else if (strcmp(name, VK_EXT_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_EXTENSION_NAME) == 0) { |
| exts.fRasterizationOrderAttachmentAccessEXT = true; |
| } else if (strcmp(name, VK_EXT_BLEND_OPERATION_ADVANCED_EXTENSION_NAME) == 0) { |
| exts.fBlendOperationAdvancedEXT = true; |
| } else if (strcmp(name, VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME) == 0) { |
| exts.fExtendedDynamicStateEXT = true; |
| } else if (strcmp(name, VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME) == 0) { |
| exts.fExtendedDynamicState2EXT = true; |
| } else if (strcmp(name, VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME) == 0) { |
| exts.fVertexInputDynamicStateEXT = true; |
| } else if (strcmp(name, VK_EXT_GRAPHICS_PIPELINE_LIBRARY_EXTENSION_NAME) == 0) { |
| exts.fGraphicsPipelineLibraryEXT = true; |
| } else if (strcmp(name, VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME) == 0) { |
| exts.fSamplerYcbcrConversionKHR = true; |
| } else if (strcmp(name, VK_EXT_RGBA10X6_FORMATS_EXTENSION_NAME) == 0) { |
| exts.fRGBA10x6FormatsEXT = true; |
| } else if (strcmp(name, VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME) == 0) { |
| exts.fSynchronization2KHR = true; |
| } else if (strcmp(name, VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME) == 0) { |
| exts.fDynamicRenderingKHR = true; |
| } else if (strcmp(name, VK_KHR_DYNAMIC_RENDERING_LOCAL_READ_EXTENSION_NAME) == 0) { |
| exts.fDynamicRenderingLocalReadKHR = true; |
| } else if (strcmp(name, VK_EXT_MULTISAMPLED_RENDER_TO_SINGLE_SAMPLED_EXTENSION_NAME) == 0) { |
| exts.fMultisampledRenderToSingleSampledEXT = true; |
| } else if (strcmp(name, VK_EXT_HOST_IMAGE_COPY_EXTENSION_NAME) == 0) { |
| exts.fHostImageCopyEXT = true; |
| } else if (strcmp(name, VK_EXT_PIPELINE_CREATION_CACHE_CONTROL_EXTENSION_NAME) == 0) { |
| exts.fPipelineCreationCacheControlEXT = true; |
| } else if (strcmp(name, VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME) == 0) { |
| exts.fDriverPropertiesKHR = true; |
| } else if (strcmp(name, VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME) == 0) { |
| exts.fCreateRenderpass2KHR = true; |
| } else if (strcmp(name, VK_EXT_LOAD_STORE_OP_NONE_EXTENSION_NAME) == 0) { |
| exts.fLoadStoreOpNoneEXT = true; |
| } else if (strcmp(name, VK_KHR_LOAD_STORE_OP_NONE_EXTENSION_NAME) == 0) { |
| exts.fLoadStoreOpNoneKHR = true; |
| } else if (strcmp(name, VK_EXT_CONSERVATIVE_RASTERIZATION_EXTENSION_NAME) == 0) { |
| exts.fConservativeRasterizationEXT = true; |
| } else if (strcmp(name, VK_KHR_PIPELINE_LIBRARY_EXTENSION_NAME) == 0) { |
| exts.fPipelineLibraryKHR = true; |
| } else if (strcmp(name, VK_KHR_COPY_COMMANDS_2_EXTENSION_NAME) == 0) { |
| exts.fCopyCommands2KHR = true; |
| } else if (strcmp(name, VK_KHR_FORMAT_FEATURE_FLAGS_2_EXTENSION_NAME) == 0) { |
| exts.fFormatFeatureFlags2KHR = true; |
| } else if (strcmp(name, VK_KHR_DEPTH_STENCIL_RESOLVE_EXTENSION_NAME) == 0) { |
| exts.fDepthStencilResolveKHR = true; |
| } else if (strcmp(name, VK_KHR_SHADER_DRAW_PARAMETERS_EXTENSION_NAME) == 0) { |
| exts.fShaderDrawParametersKHR = true; |
| } else if (strcmp(name, VK_KHR_DRAW_INDIRECT_COUNT_EXTENSION_NAME) == 0) { |
| exts.fDrawIndirectCountKHR = true; |
| } else if (strcmp(name, VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_EXTENSION_NAME) == 0) { |
| exts.fSamplerMirrorClampToEdgeKHR = true; |
| } else if (strcmp(name, VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME) == 0) { |
| exts.fDescriptorIndexingEXT = true; |
| } else if (strcmp(name, VK_EXT_SAMPLER_FILTER_MINMAX_EXTENSION_NAME) == 0) { |
| exts.fSamplerFilterMinmaxEXT = true; |
| } else if (strcmp(name, VK_EXT_SHADER_VIEWPORT_INDEX_LAYER_EXTENSION_NAME) == 0) { |
| exts.fShaderViewportIndexLayerEXT = true; |
| } else if (strcmp(name, VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME) == 0) { |
| exts.fPushDescriptorKHR = true; |
| } |
| } |
| |
| DeviceExtensions get_supported_device_extensions(const VkExtensionProperties* deviceExtensions, |
| size_t deviceExtensionCount) { |
| DeviceExtensions exts; |
| |
| for (size_t i = 0; i < deviceExtensionCount; ++i) { |
| mark_device_extensions(exts, deviceExtensions[i].extensionName); |
| } |
| |
| return exts; |
| } |
| |
| struct FeaturesToAdd { |
| bool fVulkan11 = true; |
| bool fVulkan12 = true; |
| bool fVulkan13 = true; |
| bool fVulkan14 = true; |
| bool fRasterizationOrderAttachmentAccess = true; |
| bool fBlendOperationAdvanced = true; |
| bool fExtendedDynamicState = true; |
| bool fExtendedDynamicState2 = true; |
| bool fVertexInputDynamicState = true; |
| bool fGraphicsPipelineLibrary = true; |
| bool fSamplerYcbcrConversion = true; |
| bool fRGBA10x6Formats = true; |
| bool fSynchronization2 = true; |
| bool fDynamicRendering = true; |
| bool fDynamicRenderingLocalRead = true; |
| bool fMultisampledRenderToSingleSampled = true; |
| bool fHostImageCopy = true; |
| bool fPipelineCreationCacheControl = true; |
| }; |
| |
| FeaturesToAdd get_features_to_add(uint32_t apiVersion, |
| const DeviceExtensions& exts, |
| const VkPhysicalDeviceFeatures2& appFeatures) { |
| FeaturesToAdd toAdd; |
| |
| // First, exclude feature structs that are not available / are unnecessary based on apiVersion |
| // and available extensions. |
| if (apiVersion >= VK_API_VERSION_1_4) { |
| // VK_KHR_dynamic_rendering_local_read's feature is included in |
| // VkPhysicalDeviceVulkan14Features |
| toAdd.fDynamicRenderingLocalRead = false; |
| // VK_EXT_host_image_copy's feature is included in VkPhysicalDeviceVulkan14Features |
| toAdd.fHostImageCopy = false; |
| } else { |
| toAdd.fVulkan14 = false; |
| // Check for support via individual extensions' feature structs |
| toAdd.fDynamicRenderingLocalRead = exts.fDynamicRenderingLocalReadKHR; |
| toAdd.fHostImageCopy = exts.fHostImageCopyEXT; |
| } |
| if (apiVersion >= VK_API_VERSION_1_3) { |
| // VK_KHR_synchronization2's feature is included in VkPhysicalDeviceVulkan13Features |
| toAdd.fSynchronization2 = false; |
| // VK_KHR_dynamic_rendering's feature is included in VkPhysicalDeviceVulkan13Features |
| toAdd.fDynamicRendering = false; |
| // VK_EXT_pipeline_creation_cache_control's feature is included in |
| // VkPhysicalDeviceVulkan13Features |
| toAdd.fPipelineCreationCacheControl = false; |
| // VK_EXT_extended_dynamic_state is core in Vulkan 1.3, but no feature for it is present in |
| // VkPhysicalDeviceVulkan13Features; it's assumed supported / enabled. |
| toAdd.fExtendedDynamicState = false; |
| // Note: VK_EXT_extended_dynamic_state2 was partially promoted to Vulkan 1.3. Only dynamic |
| // state from the main feature is used by Skia, so it's ok to rely on Vulkan 1.3 to provide |
| // them, but if the optional features of this extension are needed, the extension struct |
| // should still be used to query and enable those features. |
| toAdd.fExtendedDynamicState2 = false; |
| } else { |
| toAdd.fVulkan13 = false; |
| // Check for support via individual extensions' feature structs |
| toAdd.fSynchronization2 = exts.fSynchronization2KHR; |
| toAdd.fDynamicRendering = exts.fDynamicRenderingKHR; |
| toAdd.fPipelineCreationCacheControl = exts.fPipelineCreationCacheControlEXT; |
| toAdd.fExtendedDynamicState = exts.fExtendedDynamicStateEXT; |
| toAdd.fExtendedDynamicState2 = exts.fExtendedDynamicState2EXT; |
| } |
| if (apiVersion >= VK_API_VERSION_1_2) { |
| // VK_KHR_sampler_ycbcr_conversion's feature is included in |
| // VkPhysicalDeviceVulkan11Features. Note that this struct was introduced in Vulkan 1.2. |
| toAdd.fSamplerYcbcrConversion = false; |
| // No feature from VkPhysicalDeviceVulkan12Features is used by Skia currently. |
| } else { |
| toAdd.fVulkan12 = false; |
| toAdd.fVulkan11 = false; |
| // Check for support via individual extensions' feature structs |
| toAdd.fSamplerYcbcrConversion = exts.fSamplerYcbcrConversionKHR; |
| } |
| |
| // Check for support via individual extensions' feature structs (non of which have been promoted |
| // to core). |
| toAdd.fRasterizationOrderAttachmentAccess = exts.fRasterizationOrderAttachmentAccessARM || |
| exts.fRasterizationOrderAttachmentAccessEXT; |
| toAdd.fBlendOperationAdvanced = exts.fBlendOperationAdvancedEXT; |
| toAdd.fVertexInputDynamicState = exts.fVertexInputDynamicStateEXT; |
| toAdd.fGraphicsPipelineLibrary = exts.fGraphicsPipelineLibraryEXT; |
| toAdd.fRGBA10x6Formats = exts.fRGBA10x6FormatsEXT; |
| toAdd.fMultisampledRenderToSingleSampled = exts.fMultisampledRenderToSingleSampledEXT; |
| |
| // Then, go over appFeatures::pNext and exclude features that are already being queried by the |
| // app. |
| const VkBaseInStructure* pNext = static_cast<const VkBaseInStructure*>(appFeatures.pNext); |
| while (pNext) { |
| switch (pNext->sType) { |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES: |
| toAdd.fVulkan11 = false; |
| break; |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES: |
| toAdd.fVulkan12 = false; |
| break; |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES: |
| toAdd.fVulkan13 = false; |
| break; |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_4_FEATURES: |
| toAdd.fVulkan14 = false; |
| break; |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_FEATURES_EXT: |
| toAdd.fRasterizationOrderAttachmentAccess = false; |
| break; |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BLEND_OPERATION_ADVANCED_FEATURES_EXT: |
| toAdd.fBlendOperationAdvanced = false; |
| break; |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_FEATURES_EXT: |
| toAdd.fExtendedDynamicState = false; |
| break; |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_2_FEATURES_EXT: |
| toAdd.fExtendedDynamicState2 = false; |
| break; |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_INPUT_DYNAMIC_STATE_FEATURES_EXT: |
| toAdd.fVertexInputDynamicState = false; |
| break; |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GRAPHICS_PIPELINE_LIBRARY_FEATURES_EXT: |
| toAdd.fGraphicsPipelineLibrary = false; |
| break; |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES: |
| toAdd.fSamplerYcbcrConversion = false; |
| break; |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RGBA10X6_FORMATS_FEATURES_EXT: |
| toAdd.fRGBA10x6Formats = false; |
| break; |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SYNCHRONIZATION_2_FEATURES: |
| toAdd.fSynchronization2 = false; |
| break; |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES: |
| toAdd.fDynamicRendering = false; |
| break; |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_LOCAL_READ_FEATURES: |
| toAdd.fDynamicRenderingLocalRead = false; |
| break; |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTISAMPLED_RENDER_TO_SINGLE_SAMPLED_FEATURES_EXT: |
| toAdd.fMultisampledRenderToSingleSampled = false; |
| break; |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_IMAGE_COPY_FEATURES: |
| toAdd.fHostImageCopy = false; |
| break; |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_CREATION_CACHE_CONTROL_FEATURES: |
| toAdd.fPipelineCreationCacheControl = false; |
| break; |
| default: |
| break; |
| } |
| |
| pNext = pNext->pNext; |
| } |
| |
| return toAdd; |
| } |
| |
| template <typename VulkanStruct> void chain(void**& chainEnd, VulkanStruct* toAdd) { |
| *chainEnd = toAdd; |
| chainEnd = (void**)&toAdd->pNext; |
| } |
| } // anonymous namespace |
| |
| VulkanPreferredFeatures::~VulkanPreferredFeatures() { |
| if (fHasAddedToInstanceExtensions && !fHasAddedFeaturesToQuery) { |
| SkDebugf("WARNING: VulkanPreferredFeatures::addFeaturesToQuery() was not called\n"); |
| } |
| if (fHasAddedToInstanceExtensions && !fHasAddedFeaturesToEnable) { |
| SkDebugf("WARNING: VulkanPreferredFeatures::addFeaturesToEnable() was not called\n"); |
| } |
| } |
| |
| void VulkanPreferredFeatures::init(uint32_t appAPIVersion) { |
| fAPIVersion = appAPIVersion; |
| |
| // Minimum required version is Vulkan 1.1 |
| SkASSERT(fAPIVersion >= VK_API_VERSION_1_1); |
| // This class does not understand Vulkan versions higher than 1.4 |
| SkASSERT(fAPIVersion <= VK_API_VERSION_1_4); |
| } |
| |
| void VulkanPreferredFeatures::addToInstanceExtensions( |
| const VkExtensionProperties* instanceExtensions, |
| size_t instanceExtensionCount, |
| std::vector<const char*>& appExtensions) { |
| // Nothing to do, currently Skia does not take advantage of any additional instance extensions. |
| fHasAddedToInstanceExtensions = true; |
| } |
| |
| void VulkanPreferredFeatures::addFeaturesToQuery(const VkExtensionProperties* deviceExtensions, |
| size_t deviceExtensionCount, |
| VkPhysicalDeviceFeatures2& appFeatures) { |
| // First, inspect the list of device extensions and note which are available. |
| DeviceExtensions exts = |
| get_supported_device_extensions(deviceExtensions, deviceExtensionCount); |
| |
| // For now, pretend these extensions are not supported. This de-risks using this API by users |
| // who didn't previously enable them. Those already in use by Skia will be re-enabled shortly |
| // after. Those not yet in use by Skia will be enabled in the future when they start to get used. |
| #if defined(SK_DISABLE_GRAPHICS_PIPELINE_LIBRARY) |
| exts.fPipelineLibraryKHR = false; |
| exts.fGraphicsPipelineLibraryEXT = false; |
| #endif |
| exts.fSynchronization2KHR = false; |
| exts.fDynamicRenderingKHR = false; |
| exts.fDynamicRenderingLocalReadKHR = false; |
| exts.fHostImageCopyEXT = false; |
| exts.fCopyCommands2KHR = false; |
| exts.fShaderDrawParametersKHR = false; |
| exts.fDrawIndirectCountKHR = false; |
| exts.fSamplerMirrorClampToEdgeKHR = false; |
| exts.fDescriptorIndexingEXT = false; |
| exts.fSamplerFilterMinmaxEXT = false; |
| exts.fShaderViewportIndexLayerEXT = false; |
| exts.fPushDescriptorKHR = false; |
| |
| // Set the sType and extensions to enable. Later in addFeaturesToEnable, the availability of |
| // device extensions is inferred from this. This is done irrespective of FeaturesToAdd |
| // calculated below, because if an app includes a feature struct that has a desirable feature |
| // disabled we can enable it later in addFeaturesToEnable. This is only possible if we know the |
| // feature is supported (inferred from the sType being set here) and the feature is required by |
| // the spec to be supported when the extension is available. |
| if (fAPIVersion >= VK_API_VERSION_1_4) { |
| fVulkan14.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_4_FEATURES; |
| } else { |
| if (exts.fDynamicRenderingLocalReadKHR) { |
| fDynamicRenderingLocalRead.sType = |
| VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_LOCAL_READ_FEATURES; |
| } |
| if (exts.fHostImageCopyEXT) { |
| fHostImageCopy.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_IMAGE_COPY_FEATURES; |
| } |
| if (exts.fLoadStoreOpNoneKHR) { |
| fLoadStoreOpNoneExtension = VK_KHR_LOAD_STORE_OP_NONE_EXTENSION_NAME; |
| } else if (exts.fLoadStoreOpNoneEXT) { |
| fLoadStoreOpNoneExtension = VK_EXT_LOAD_STORE_OP_NONE_EXTENSION_NAME; |
| } |
| } |
| if (fAPIVersion >= VK_API_VERSION_1_3) { |
| fVulkan13.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES; |
| } else { |
| if (exts.fSynchronization2KHR) { |
| fSynchronization2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SYNCHRONIZATION_2_FEATURES; |
| } |
| if (exts.fDynamicRenderingKHR) { |
| fDynamicRendering.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES; |
| } |
| if (exts.fPipelineCreationCacheControlEXT) { |
| fPipelineCreationCacheControl.sType = |
| VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_CREATION_CACHE_CONTROL_FEATURES; |
| } |
| if (exts.fExtendedDynamicStateEXT) { |
| fExtendedDynamicState.sType = |
| VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_FEATURES_EXT; |
| } |
| if (exts.fExtendedDynamicState2EXT) { |
| fExtendedDynamicState2.sType = |
| VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_2_FEATURES_EXT; |
| } |
| if (exts.fCopyCommands2KHR) { |
| fCopyCommands2Extension = VK_KHR_COPY_COMMANDS_2_EXTENSION_NAME; |
| } |
| if (exts.fFormatFeatureFlags2KHR) { |
| fFormatFeatureFlags2Extension = VK_KHR_FORMAT_FEATURE_FLAGS_2_EXTENSION_NAME; |
| } |
| } |
| if (fAPIVersion >= VK_API_VERSION_1_2) { |
| fVulkan11.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES; |
| fVulkan12.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES; |
| } else { |
| if (exts.fSamplerYcbcrConversionKHR) { |
| fSamplerYcbcrConversion.sType = |
| VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES; |
| } |
| if (exts.fDriverPropertiesKHR) { |
| fDriverPropertiesExtension = VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME; |
| } |
| if (exts.fCreateRenderpass2KHR) { |
| fCreateRenderpass2Extension = VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME; |
| } |
| if (exts.fDepthStencilResolveKHR) { |
| fDepthStencilResolveExtension = VK_KHR_DEPTH_STENCIL_RESOLVE_EXTENSION_NAME; |
| } |
| } |
| SkASSERT(fAPIVersion >= VK_API_VERSION_1_1); |
| |
| if (exts.fRasterizationOrderAttachmentAccessARM) { |
| fRasterizationOrderAttachmentAccess.sType = |
| VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_FEATURES_ARM; |
| fRasterizationOrderAttachmentAccessExtension = |
| VK_ARM_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_EXTENSION_NAME; |
| } |
| if (exts.fRasterizationOrderAttachmentAccessEXT) { |
| fRasterizationOrderAttachmentAccess.sType = |
| VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_FEATURES_EXT; |
| fRasterizationOrderAttachmentAccessExtension = |
| VK_EXT_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_EXTENSION_NAME; |
| } |
| if (exts.fBlendOperationAdvancedEXT) { |
| fBlendOperationAdvanced.sType = |
| VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BLEND_OPERATION_ADVANCED_FEATURES_EXT; |
| } |
| if (exts.fVertexInputDynamicStateEXT) { |
| fVertexInputDynamicState.sType = |
| VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_INPUT_DYNAMIC_STATE_FEATURES_EXT; |
| } |
| if (exts.fGraphicsPipelineLibraryEXT) { |
| fGraphicsPipelineLibrary.sType = |
| VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GRAPHICS_PIPELINE_LIBRARY_FEATURES_EXT; |
| // Depends on VK_KHR_pipeline_library |
| fPipelineLibraryExtension = VK_KHR_PIPELINE_LIBRARY_EXTENSION_NAME; |
| } |
| if (exts.fRGBA10x6FormatsEXT) { |
| fRGBA10x6Formats.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RGBA10X6_FORMATS_FEATURES_EXT; |
| } |
| if (exts.fMultisampledRenderToSingleSampledEXT) { |
| fMultisampledRenderToSingleSampled.sType = |
| VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTISAMPLED_RENDER_TO_SINGLE_SAMPLED_FEATURES_EXT; |
| } |
| if (exts.fConservativeRasterizationEXT) { |
| fConservativeRasterizationExtension = VK_EXT_CONSERVATIVE_RASTERIZATION_EXTENSION_NAME; |
| } |
| |
| // Inspect the list of features the app has already included and decide on which features to |
| // add. |
| const FeaturesToAdd toAdd = get_features_to_add(fAPIVersion, exts, appFeatures); |
| |
| // Chain any new features we'd like to query. |
| if (toAdd.fVulkan11) { |
| SkASSERT(fVulkan11.sType != 0); |
| AddToPNextChain(&appFeatures, &fVulkan11); |
| } |
| if (toAdd.fVulkan12) { |
| SkASSERT(fVulkan12.sType != 0); |
| AddToPNextChain(&appFeatures, &fVulkan12); |
| } |
| if (toAdd.fVulkan13) { |
| SkASSERT(fVulkan13.sType != 0); |
| AddToPNextChain(&appFeatures, &fVulkan13); |
| } |
| if (toAdd.fVulkan14) { |
| SkASSERT(fVulkan14.sType != 0); |
| AddToPNextChain(&appFeatures, &fVulkan14); |
| } |
| if (toAdd.fRasterizationOrderAttachmentAccess) { |
| SkASSERT(fRasterizationOrderAttachmentAccess.sType != 0); |
| AddToPNextChain(&appFeatures, &fRasterizationOrderAttachmentAccess); |
| } |
| if (toAdd.fBlendOperationAdvanced) { |
| SkASSERT(fBlendOperationAdvanced.sType != 0); |
| AddToPNextChain(&appFeatures, &fBlendOperationAdvanced); |
| } |
| if (toAdd.fExtendedDynamicState) { |
| SkASSERT(fExtendedDynamicState.sType != 0); |
| AddToPNextChain(&appFeatures, &fExtendedDynamicState); |
| } |
| if (toAdd.fExtendedDynamicState2) { |
| SkASSERT(fExtendedDynamicState2.sType != 0); |
| AddToPNextChain(&appFeatures, &fExtendedDynamicState2); |
| } |
| if (toAdd.fVertexInputDynamicState) { |
| SkASSERT(fVertexInputDynamicState.sType != 0); |
| AddToPNextChain(&appFeatures, &fVertexInputDynamicState); |
| } |
| if (toAdd.fGraphicsPipelineLibrary) { |
| SkASSERT(fGraphicsPipelineLibrary.sType != 0); |
| AddToPNextChain(&appFeatures, &fGraphicsPipelineLibrary); |
| } |
| if (toAdd.fSamplerYcbcrConversion) { |
| SkASSERT(fSamplerYcbcrConversion.sType != 0); |
| AddToPNextChain(&appFeatures, &fSamplerYcbcrConversion); |
| } |
| if (toAdd.fRGBA10x6Formats) { |
| SkASSERT(fRGBA10x6Formats.sType != 0); |
| AddToPNextChain(&appFeatures, &fRGBA10x6Formats); |
| } |
| if (toAdd.fSynchronization2) { |
| SkASSERT(fSynchronization2.sType != 0); |
| AddToPNextChain(&appFeatures, &fSynchronization2); |
| } |
| if (toAdd.fDynamicRendering) { |
| SkASSERT(fDynamicRendering.sType != 0); |
| AddToPNextChain(&appFeatures, &fDynamicRendering); |
| } |
| if (toAdd.fDynamicRenderingLocalRead) { |
| SkASSERT(fDynamicRenderingLocalRead.sType != 0); |
| AddToPNextChain(&appFeatures, &fDynamicRenderingLocalRead); |
| } |
| if (toAdd.fMultisampledRenderToSingleSampled) { |
| SkASSERT(fMultisampledRenderToSingleSampled.sType != 0); |
| AddToPNextChain(&appFeatures, &fMultisampledRenderToSingleSampled); |
| } |
| if (toAdd.fHostImageCopy) { |
| SkASSERT(fHostImageCopy.sType != 0); |
| AddToPNextChain(&appFeatures, &fHostImageCopy); |
| } |
| if (toAdd.fPipelineCreationCacheControl) { |
| SkASSERT(fPipelineCreationCacheControl.sType != 0); |
| AddToPNextChain(&appFeatures, &fPipelineCreationCacheControl); |
| } |
| |
| fHasAddedFeaturesToQuery = true; |
| } |
| |
| void VulkanPreferredFeatures::addFeaturesToEnable(std::vector<const char*>& appExtensions, |
| VkPhysicalDeviceFeatures2& appFeatures) { |
| if (!fHasAddedFeaturesToQuery) { |
| SkDebugf( |
| "WARNING: VulkanPreferredFeatures::addFeaturesToQuery() was not called before " |
| "addFeaturesToEnable, performance may degrade\n"); |
| } |
| SkASSERT(fHasAddedFeaturesToQuery); |
| |
| FeaturesToAdd toAdd; |
| toAdd.fVulkan11 = fVulkan11.sType != 0; |
| toAdd.fVulkan12 = fVulkan12.sType != 0; |
| toAdd.fVulkan13 = fVulkan13.sType != 0; |
| toAdd.fVulkan14 = fVulkan14.sType != 0; |
| toAdd.fRasterizationOrderAttachmentAccess = fRasterizationOrderAttachmentAccess.sType != 0; |
| toAdd.fBlendOperationAdvanced = fBlendOperationAdvanced.sType != 0; |
| toAdd.fExtendedDynamicState = fExtendedDynamicState.sType != 0; |
| toAdd.fExtendedDynamicState2 = fExtendedDynamicState2.sType != 0; |
| toAdd.fVertexInputDynamicState = fVertexInputDynamicState.sType != 0; |
| toAdd.fGraphicsPipelineLibrary = fGraphicsPipelineLibrary.sType != 0; |
| toAdd.fSamplerYcbcrConversion = fSamplerYcbcrConversion.sType != 0; |
| toAdd.fRGBA10x6Formats = fRGBA10x6Formats.sType != 0; |
| toAdd.fSynchronization2 = fSynchronization2.sType != 0; |
| toAdd.fDynamicRendering = fDynamicRendering.sType != 0; |
| toAdd.fDynamicRenderingLocalRead = fDynamicRenderingLocalRead.sType != 0; |
| toAdd.fMultisampledRenderToSingleSampled = fMultisampledRenderToSingleSampled.sType != 0; |
| toAdd.fHostImageCopy = fHostImageCopy.sType != 0; |
| toAdd.fPipelineCreationCacheControl = fPipelineCreationCacheControl.sType != 0; |
| |
| // Check which extensions are already enabled by the application. |
| DeviceExtensions exts; |
| for (const char* name : appExtensions) { |
| mark_device_extensions(exts, name); |
| } |
| |
| // Add extensions that are not enabled by the app. |
| if (!exts.fRasterizationOrderAttachmentAccessARM && |
| !exts.fRasterizationOrderAttachmentAccessEXT && toAdd.fRasterizationOrderAttachmentAccess) { |
| appExtensions.push_back(fRasterizationOrderAttachmentAccessExtension); |
| exts.fRasterizationOrderAttachmentAccessEXT = true; |
| } |
| if (!exts.fBlendOperationAdvancedEXT && toAdd.fBlendOperationAdvanced) { |
| appExtensions.push_back(VK_EXT_BLEND_OPERATION_ADVANCED_EXTENSION_NAME); |
| exts.fBlendOperationAdvancedEXT = true; |
| } |
| if (!exts.fExtendedDynamicStateEXT && toAdd.fExtendedDynamicState) { |
| appExtensions.push_back(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME); |
| exts.fExtendedDynamicStateEXT = true; |
| } |
| if (!exts.fExtendedDynamicState2EXT && toAdd.fExtendedDynamicState2) { |
| appExtensions.push_back(VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME); |
| exts.fExtendedDynamicState2EXT = true; |
| } |
| if (!exts.fVertexInputDynamicStateEXT && toAdd.fVertexInputDynamicState) { |
| appExtensions.push_back(VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME); |
| exts.fVertexInputDynamicStateEXT = true; |
| } |
| if (!exts.fGraphicsPipelineLibraryEXT && toAdd.fGraphicsPipelineLibrary) { |
| appExtensions.push_back(VK_EXT_GRAPHICS_PIPELINE_LIBRARY_EXTENSION_NAME); |
| exts.fGraphicsPipelineLibraryEXT = true; |
| } |
| if (!exts.fSamplerYcbcrConversionKHR && toAdd.fSamplerYcbcrConversion) { |
| appExtensions.push_back(VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME); |
| exts.fSamplerYcbcrConversionKHR = true; |
| } |
| if (!exts.fRGBA10x6FormatsEXT && toAdd.fRGBA10x6Formats) { |
| appExtensions.push_back(VK_EXT_RGBA10X6_FORMATS_EXTENSION_NAME); |
| exts.fRGBA10x6FormatsEXT = true; |
| } |
| if (!exts.fSynchronization2KHR && toAdd.fSynchronization2) { |
| appExtensions.push_back(VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME); |
| exts.fSynchronization2KHR = true; |
| } |
| if (!exts.fDynamicRenderingKHR && toAdd.fDynamicRendering) { |
| appExtensions.push_back(VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME); |
| exts.fDynamicRenderingKHR = true; |
| } |
| if (!exts.fDynamicRenderingLocalReadKHR && toAdd.fDynamicRenderingLocalRead) { |
| appExtensions.push_back(VK_KHR_DYNAMIC_RENDERING_LOCAL_READ_EXTENSION_NAME); |
| exts.fDynamicRenderingLocalReadKHR = true; |
| } |
| if (!exts.fMultisampledRenderToSingleSampledEXT && toAdd.fMultisampledRenderToSingleSampled) { |
| appExtensions.push_back(VK_EXT_MULTISAMPLED_RENDER_TO_SINGLE_SAMPLED_EXTENSION_NAME); |
| exts.fMultisampledRenderToSingleSampledEXT = true; |
| } |
| if (!exts.fHostImageCopyEXT && toAdd.fHostImageCopy) { |
| appExtensions.push_back(VK_EXT_HOST_IMAGE_COPY_EXTENSION_NAME); |
| exts.fHostImageCopyEXT = true; |
| } |
| if (!exts.fPipelineCreationCacheControlEXT && toAdd.fPipelineCreationCacheControl) { |
| appExtensions.push_back(VK_EXT_PIPELINE_CREATION_CACHE_CONTROL_EXTENSION_NAME); |
| exts.fPipelineCreationCacheControlEXT = true; |
| } |
| if (!exts.fDriverPropertiesKHR && fDriverPropertiesExtension != nullptr) { |
| appExtensions.push_back(fDriverPropertiesExtension); |
| exts.fDriverPropertiesKHR = true; |
| } |
| if (!exts.fCreateRenderpass2KHR && fCreateRenderpass2Extension != nullptr) { |
| appExtensions.push_back(fCreateRenderpass2Extension); |
| exts.fCreateRenderpass2KHR = true; |
| } |
| if (!exts.fLoadStoreOpNoneKHR && !exts.fLoadStoreOpNoneEXT && |
| fLoadStoreOpNoneExtension != nullptr) { |
| appExtensions.push_back(fLoadStoreOpNoneExtension); |
| exts.fCreateRenderpass2KHR = true; |
| } |
| if (!exts.fConservativeRasterizationEXT && fConservativeRasterizationExtension != nullptr) { |
| appExtensions.push_back(fConservativeRasterizationExtension); |
| exts.fConservativeRasterizationEXT = true; |
| } |
| if (!exts.fPipelineLibraryKHR && fPipelineLibraryExtension != nullptr) { |
| appExtensions.push_back(fPipelineLibraryExtension); |
| exts.fPipelineLibraryKHR = true; |
| } |
| if (!exts.fCopyCommands2KHR && fCopyCommands2Extension != nullptr) { |
| appExtensions.push_back(fCopyCommands2Extension); |
| exts.fCopyCommands2KHR = true; |
| } |
| if (!exts.fFormatFeatureFlags2KHR && fFormatFeatureFlags2Extension != nullptr) { |
| appExtensions.push_back(fFormatFeatureFlags2Extension); |
| exts.fFormatFeatureFlags2KHR = true; |
| } |
| if (!exts.fDepthStencilResolveKHR && fDepthStencilResolveExtension != nullptr) { |
| appExtensions.push_back(fDepthStencilResolveExtension); |
| exts.fDepthStencilResolveKHR = true; |
| } |
| |
| // Go over the app features. There are a couple of possibilities: |
| // |
| // - A struct is included by the app: In this case, if there is a feature Skia wants that |
| // is required to be supported when the extension is present, that feature is enabled. |
| // If an optional feature is supported but disabled by the app, Skia has no visibility into it |
| // right now, but no such feature is used by Skia currently. |
| // - A struct is included byFeaturesToQuery: In this case, if there are features that Skia |
| // doesn't need, they are disabled since the app wasn't relying on those features. |
| // - If a Vulkan1X feature struct needs to be chained, then the individual feature structs that |
| // it subsumes are removed from the chain and their features are enabled in the Vulkan1X |
| // struct instead. |
| // |
| // Note that chaining the Vulkan1X feature struct is preferred where possible, because a 1.X |
| // driver is not obliged to also expose the extensions that are promoted to core in that |
| // version. As such, features are aggregated in Vulkan1X structs when possible and the |
| // individual extension-specific structs are dropped. If Skia requires that Vulkan1X structs be |
| // used when possible, the feature aggregation code below becomes unnecessary. |
| // |
| // In the following, if an extension of interest is enabled, its spec-required main feature is |
| // enabled. Normally this is not needed, as the feature query would return VK_TRUE for that |
| // feature. However, this is done to protect against the app having turned the feature off after |
| // query. |
| |
| // Create a new chain of structs. AddToPNextChain is not used because the structs being chained |
| // may already be present in appFeatures' pNext chain. |
| void* newChainStart = nullptr; |
| void** newChainEnd = &newChainStart; |
| |
| if (toAdd.fVulkan11) { |
| // Disable all features not used by Skia |
| fVulkan11.storageBuffer16BitAccess = VK_FALSE; |
| fVulkan11.uniformAndStorageBuffer16BitAccess = VK_FALSE; |
| fVulkan11.storagePushConstant16 = VK_FALSE; |
| fVulkan11.storageInputOutput16 = VK_FALSE; |
| fVulkan11.multiview = VK_FALSE; |
| fVulkan11.multiviewGeometryShader = VK_FALSE; |
| fVulkan11.multiviewTessellationShader = VK_FALSE; |
| fVulkan11.variablePointersStorageBuffer = VK_FALSE; |
| fVulkan11.variablePointers = VK_FALSE; |
| fVulkan11.protectedMemory = VK_FALSE; |
| fVulkan11.shaderDrawParameters = VK_FALSE; |
| |
| // samplerYcbcrConversion is required since Version 1.4. |
| if (fAPIVersion >= VK_API_VERSION_1_4) { |
| fVulkan11.samplerYcbcrConversion = VK_TRUE; |
| } |
| |
| // The following are features from promoted extensions, where the original extension did not |
| // have a feature. |
| if (exts.fShaderDrawParametersKHR) { |
| fVulkan11.shaderDrawParameters = VK_TRUE; |
| } |
| } |
| if (toAdd.fVulkan12) { |
| // Disable all features not used by Skia |
| fVulkan12.samplerMirrorClampToEdge = VK_FALSE; |
| fVulkan12.drawIndirectCount = VK_FALSE; |
| fVulkan12.storageBuffer8BitAccess = VK_FALSE; |
| fVulkan12.uniformAndStorageBuffer8BitAccess = VK_FALSE; |
| fVulkan12.storagePushConstant8 = VK_FALSE; |
| fVulkan12.shaderBufferInt64Atomics = VK_FALSE; |
| fVulkan12.shaderSharedInt64Atomics = VK_FALSE; |
| fVulkan12.shaderFloat16 = VK_FALSE; |
| fVulkan12.shaderInt8 = VK_FALSE; |
| fVulkan12.descriptorIndexing = VK_FALSE; |
| fVulkan12.shaderInputAttachmentArrayDynamicIndexing = VK_FALSE; |
| fVulkan12.shaderUniformTexelBufferArrayDynamicIndexing = VK_FALSE; |
| fVulkan12.shaderStorageTexelBufferArrayDynamicIndexing = VK_FALSE; |
| fVulkan12.shaderUniformBufferArrayNonUniformIndexing = VK_FALSE; |
| fVulkan12.shaderSampledImageArrayNonUniformIndexing = VK_FALSE; |
| fVulkan12.shaderStorageBufferArrayNonUniformIndexing = VK_FALSE; |
| fVulkan12.shaderStorageImageArrayNonUniformIndexing = VK_FALSE; |
| fVulkan12.shaderInputAttachmentArrayNonUniformIndexing = VK_FALSE; |
| fVulkan12.shaderUniformTexelBufferArrayNonUniformIndexing = VK_FALSE; |
| fVulkan12.shaderStorageTexelBufferArrayNonUniformIndexing = VK_FALSE; |
| fVulkan12.descriptorBindingUniformBufferUpdateAfterBind = VK_FALSE; |
| fVulkan12.descriptorBindingSampledImageUpdateAfterBind = VK_FALSE; |
| fVulkan12.descriptorBindingStorageImageUpdateAfterBind = VK_FALSE; |
| fVulkan12.descriptorBindingStorageBufferUpdateAfterBind = VK_FALSE; |
| fVulkan12.descriptorBindingUniformTexelBufferUpdateAfterBind = VK_FALSE; |
| fVulkan12.descriptorBindingStorageTexelBufferUpdateAfterBind = VK_FALSE; |
| fVulkan12.descriptorBindingUpdateUnusedWhilePending = VK_FALSE; |
| fVulkan12.descriptorBindingPartiallyBound = VK_FALSE; |
| fVulkan12.descriptorBindingVariableDescriptorCount = VK_FALSE; |
| fVulkan12.runtimeDescriptorArray = VK_FALSE; |
| fVulkan12.samplerFilterMinmax = VK_FALSE; |
| fVulkan12.scalarBlockLayout = VK_FALSE; |
| fVulkan12.imagelessFramebuffer = VK_FALSE; |
| fVulkan12.uniformBufferStandardLayout = VK_FALSE; |
| fVulkan12.shaderSubgroupExtendedTypes = VK_FALSE; |
| fVulkan12.separateDepthStencilLayouts = VK_FALSE; |
| fVulkan12.hostQueryReset = VK_FALSE; |
| fVulkan12.timelineSemaphore = VK_FALSE; |
| fVulkan12.bufferDeviceAddress = VK_FALSE; |
| fVulkan12.bufferDeviceAddressCaptureReplay = VK_FALSE; |
| fVulkan12.bufferDeviceAddressMultiDevice = VK_FALSE; |
| fVulkan12.vulkanMemoryModel = VK_FALSE; |
| fVulkan12.vulkanMemoryModelDeviceScope = VK_FALSE; |
| fVulkan12.vulkanMemoryModelAvailabilityVisibilityChains = VK_FALSE; |
| fVulkan12.shaderOutputViewportIndex = VK_FALSE; |
| fVulkan12.shaderOutputLayer = VK_FALSE; |
| fVulkan12.subgroupBroadcastDynamicId = VK_FALSE; |
| |
| // No required feature of Vulkan 1.2 is currently needed. The imagelessFramebuffer feature |
| // is a candidate, but likely better to not bother and go directly with dynamic rendering. |
| |
| // The following are features from promoted extensions, where the original extension did not |
| // have a feature. |
| if (exts.fDrawIndirectCountKHR) { |
| fVulkan12.drawIndirectCount = VK_TRUE; |
| } |
| if (exts.fSamplerMirrorClampToEdgeKHR) { |
| fVulkan12.samplerMirrorClampToEdge = VK_TRUE; |
| } |
| if (exts.fDescriptorIndexingEXT) { |
| fVulkan12.descriptorIndexing = VK_TRUE; |
| } |
| if (exts.fSamplerFilterMinmaxEXT) { |
| fVulkan12.samplerFilterMinmax = VK_TRUE; |
| } |
| if (exts.fShaderViewportIndexLayerEXT) { |
| fVulkan12.shaderOutputViewportIndex = VK_TRUE; |
| fVulkan12.shaderOutputLayer = VK_TRUE; |
| } |
| } |
| if (toAdd.fVulkan13) { |
| // Disable all features not used by Skia |
| fVulkan13.robustImageAccess = VK_FALSE; |
| fVulkan13.inlineUniformBlock = VK_FALSE; |
| fVulkan13.descriptorBindingInlineUniformBlockUpdateAfterBind = VK_FALSE; |
| fVulkan13.privateData = VK_FALSE; |
| fVulkan13.shaderDemoteToHelperInvocation = VK_FALSE; |
| fVulkan13.shaderTerminateInvocation = VK_FALSE; |
| fVulkan13.subgroupSizeControl = VK_FALSE; |
| fVulkan13.computeFullSubgroups = VK_FALSE; |
| fVulkan13.textureCompressionASTC_HDR = VK_FALSE; |
| fVulkan13.shaderZeroInitializeWorkgroupMemory = VK_FALSE; |
| fVulkan13.shaderIntegerDotProduct = VK_FALSE; |
| fVulkan13.maintenance4 = VK_FALSE; |
| |
| // synchronization2, dynamicRendering and pipelineCreationCacheControl are required features |
| // of Vulkan 1.3. |
| fVulkan13.synchronization2 = VK_TRUE; |
| fVulkan13.dynamicRendering = VK_TRUE; |
| fVulkan13.pipelineCreationCacheControl = VK_TRUE; |
| } |
| if (toAdd.fVulkan14) { |
| // Disable all features not used by Skia |
| fVulkan14.globalPriorityQuery = VK_FALSE; |
| fVulkan14.shaderSubgroupRotate = VK_FALSE; |
| fVulkan14.shaderSubgroupRotateClustered = VK_FALSE; |
| fVulkan14.shaderFloatControls2 = VK_FALSE; |
| fVulkan14.shaderExpectAssume = VK_FALSE; |
| fVulkan14.rectangularLines = VK_FALSE; |
| fVulkan14.bresenhamLines = VK_FALSE; |
| fVulkan14.smoothLines = VK_FALSE; |
| fVulkan14.stippledRectangularLines = VK_FALSE; |
| fVulkan14.stippledBresenhamLines = VK_FALSE; |
| fVulkan14.stippledSmoothLines = VK_FALSE; |
| fVulkan14.vertexAttributeInstanceRateDivisor = VK_FALSE; |
| fVulkan14.vertexAttributeInstanceRateZeroDivisor = VK_FALSE; |
| fVulkan14.indexTypeUint8 = VK_FALSE; |
| fVulkan14.maintenance5 = VK_FALSE; |
| fVulkan14.maintenance6 = VK_FALSE; |
| fVulkan14.pipelineProtectedAccess = VK_FALSE; |
| fVulkan14.pipelineRobustness = VK_FALSE; |
| fVulkan14.pushDescriptor = VK_FALSE; |
| |
| // dynamicRenderingLocalRead is a required feature of Vulkan 1.4. |
| fVulkan14.dynamicRenderingLocalRead = VK_TRUE; |
| |
| // The following are features from promoted extensions, where the original extension did not |
| // have a feature. |
| if (exts.fPushDescriptorKHR) { |
| fVulkan14.pushDescriptor = VK_TRUE; |
| } |
| } |
| |
| bool hasVulkan11Features = toAdd.fVulkan11; |
| bool hasVulkan12Features = toAdd.fVulkan12; |
| bool hasVulkan13Features = toAdd.fVulkan13; |
| bool hasVulkan14Features = toAdd.fVulkan14; |
| |
| // If chained by Skia, disable depth/stencil coherence even if supported, in case it comes with |
| // a perf cost. |
| fRasterizationOrderAttachmentAccess.rasterizationOrderDepthAttachmentAccess = VK_FALSE; |
| fRasterizationOrderAttachmentAccess.rasterizationOrderStencilAttachmentAccess = VK_FALSE; |
| |
| // If chained by Skia, disable dynamic state that is unused by Skia. |
| fExtendedDynamicState2.extendedDynamicState2LogicOp = VK_FALSE; |
| fExtendedDynamicState2.extendedDynamicState2PatchControlPoints = VK_FALSE; |
| |
| // Helper to enable a feature in the feature aggregate struct if it's enabled in the |
| // extension-specific struct. It reduces code verbosity but more importantly ensures the feature |
| // name is identical and reduces chance of copy-paste mistakes. |
| #define DUP_INTO(aggregate, feature) aggregate.feature |= features->feature |
| |
| VkBaseOutStructure* pNext = static_cast<VkBaseOutStructure*>(appFeatures.pNext); |
| while (pNext) { |
| switch (pNext->sType) { |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES: { |
| auto* features = reinterpret_cast<VkPhysicalDeviceVulkan11Features*>(pNext); |
| // If this struct is not chained by Skia, copy enabled features. |
| if (features != &fVulkan11) { |
| DUP_INTO(fVulkan11, storageBuffer16BitAccess); |
| DUP_INTO(fVulkan11, uniformAndStorageBuffer16BitAccess); |
| DUP_INTO(fVulkan11, storagePushConstant16); |
| DUP_INTO(fVulkan11, storageInputOutput16); |
| DUP_INTO(fVulkan11, multiview); |
| DUP_INTO(fVulkan11, multiviewGeometryShader); |
| DUP_INTO(fVulkan11, multiviewTessellationShader); |
| DUP_INTO(fVulkan11, variablePointersStorageBuffer); |
| DUP_INTO(fVulkan11, variablePointers); |
| DUP_INTO(fVulkan11, protectedMemory); |
| DUP_INTO(fVulkan11, samplerYcbcrConversion); |
| DUP_INTO(fVulkan11, shaderDrawParameters); |
| } |
| break; |
| } |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES: { |
| auto* features = reinterpret_cast<VkPhysicalDeviceVulkan12Features*>(pNext); |
| // If this struct is not chained by Skia, copy enabled features. |
| if (features != &fVulkan12) { |
| DUP_INTO(fVulkan12, samplerMirrorClampToEdge); |
| DUP_INTO(fVulkan12, drawIndirectCount); |
| DUP_INTO(fVulkan12, storageBuffer8BitAccess); |
| DUP_INTO(fVulkan12, uniformAndStorageBuffer8BitAccess); |
| DUP_INTO(fVulkan12, storagePushConstant8); |
| DUP_INTO(fVulkan12, shaderBufferInt64Atomics); |
| DUP_INTO(fVulkan12, shaderSharedInt64Atomics); |
| DUP_INTO(fVulkan12, shaderFloat16); |
| DUP_INTO(fVulkan12, shaderInt8); |
| DUP_INTO(fVulkan12, descriptorIndexing); |
| DUP_INTO(fVulkan12, shaderInputAttachmentArrayDynamicIndexing); |
| DUP_INTO(fVulkan12, shaderUniformTexelBufferArrayDynamicIndexing); |
| DUP_INTO(fVulkan12, shaderStorageTexelBufferArrayDynamicIndexing); |
| DUP_INTO(fVulkan12, shaderUniformBufferArrayNonUniformIndexing); |
| DUP_INTO(fVulkan12, shaderSampledImageArrayNonUniformIndexing); |
| DUP_INTO(fVulkan12, shaderStorageBufferArrayNonUniformIndexing); |
| DUP_INTO(fVulkan12, shaderStorageImageArrayNonUniformIndexing); |
| DUP_INTO(fVulkan12, shaderInputAttachmentArrayNonUniformIndexing); |
| DUP_INTO(fVulkan12, shaderUniformTexelBufferArrayNonUniformIndexing); |
| DUP_INTO(fVulkan12, shaderStorageTexelBufferArrayNonUniformIndexing); |
| DUP_INTO(fVulkan12, descriptorBindingUniformBufferUpdateAfterBind); |
| DUP_INTO(fVulkan12, descriptorBindingSampledImageUpdateAfterBind); |
| DUP_INTO(fVulkan12, descriptorBindingStorageImageUpdateAfterBind); |
| DUP_INTO(fVulkan12, descriptorBindingStorageBufferUpdateAfterBind); |
| DUP_INTO(fVulkan12, descriptorBindingUniformTexelBufferUpdateAfterBind); |
| DUP_INTO(fVulkan12, descriptorBindingStorageTexelBufferUpdateAfterBind); |
| DUP_INTO(fVulkan12, descriptorBindingUpdateUnusedWhilePending); |
| DUP_INTO(fVulkan12, descriptorBindingPartiallyBound); |
| DUP_INTO(fVulkan12, descriptorBindingVariableDescriptorCount); |
| DUP_INTO(fVulkan12, runtimeDescriptorArray); |
| DUP_INTO(fVulkan12, samplerFilterMinmax); |
| DUP_INTO(fVulkan12, scalarBlockLayout); |
| DUP_INTO(fVulkan12, imagelessFramebuffer); |
| DUP_INTO(fVulkan12, uniformBufferStandardLayout); |
| DUP_INTO(fVulkan12, shaderSubgroupExtendedTypes); |
| DUP_INTO(fVulkan12, separateDepthStencilLayouts); |
| DUP_INTO(fVulkan12, hostQueryReset); |
| DUP_INTO(fVulkan12, timelineSemaphore); |
| DUP_INTO(fVulkan12, bufferDeviceAddress); |
| DUP_INTO(fVulkan12, bufferDeviceAddressCaptureReplay); |
| DUP_INTO(fVulkan12, bufferDeviceAddressMultiDevice); |
| DUP_INTO(fVulkan12, vulkanMemoryModel); |
| DUP_INTO(fVulkan12, vulkanMemoryModelDeviceScope); |
| DUP_INTO(fVulkan12, vulkanMemoryModelAvailabilityVisibilityChains); |
| DUP_INTO(fVulkan12, shaderOutputViewportIndex); |
| DUP_INTO(fVulkan12, shaderOutputLayer); |
| DUP_INTO(fVulkan12, subgroupBroadcastDynamicId); |
| } |
| break; |
| } |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES: { |
| auto* features = reinterpret_cast<VkPhysicalDeviceVulkan13Features*>(pNext); |
| // If this struct is not chained by Skia, copy enabled features. |
| if (features != &fVulkan13) { |
| DUP_INTO(fVulkan13, robustImageAccess); |
| DUP_INTO(fVulkan13, inlineUniformBlock); |
| DUP_INTO(fVulkan13, descriptorBindingInlineUniformBlockUpdateAfterBind); |
| DUP_INTO(fVulkan13, pipelineCreationCacheControl); |
| DUP_INTO(fVulkan13, privateData); |
| DUP_INTO(fVulkan13, shaderDemoteToHelperInvocation); |
| DUP_INTO(fVulkan13, shaderTerminateInvocation); |
| DUP_INTO(fVulkan13, subgroupSizeControl); |
| DUP_INTO(fVulkan13, computeFullSubgroups); |
| DUP_INTO(fVulkan13, synchronization2); |
| DUP_INTO(fVulkan13, textureCompressionASTC_HDR); |
| DUP_INTO(fVulkan13, shaderZeroInitializeWorkgroupMemory); |
| DUP_INTO(fVulkan13, dynamicRendering); |
| DUP_INTO(fVulkan13, shaderIntegerDotProduct); |
| DUP_INTO(fVulkan13, maintenance4); |
| } |
| break; |
| } |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_4_FEATURES: { |
| auto* features = reinterpret_cast<VkPhysicalDeviceVulkan14Features*>(pNext); |
| // If this struct is not chained by Skia, copy enabled features. |
| if (features != &fVulkan14) { |
| DUP_INTO(fVulkan14, globalPriorityQuery); |
| DUP_INTO(fVulkan14, shaderSubgroupRotate); |
| DUP_INTO(fVulkan14, shaderSubgroupRotateClustered); |
| DUP_INTO(fVulkan14, shaderFloatControls2); |
| DUP_INTO(fVulkan14, shaderExpectAssume); |
| DUP_INTO(fVulkan14, rectangularLines); |
| DUP_INTO(fVulkan14, bresenhamLines); |
| DUP_INTO(fVulkan14, smoothLines); |
| DUP_INTO(fVulkan14, stippledRectangularLines); |
| DUP_INTO(fVulkan14, stippledBresenhamLines); |
| DUP_INTO(fVulkan14, stippledSmoothLines); |
| DUP_INTO(fVulkan14, vertexAttributeInstanceRateDivisor); |
| DUP_INTO(fVulkan14, vertexAttributeInstanceRateZeroDivisor); |
| DUP_INTO(fVulkan14, indexTypeUint8); |
| DUP_INTO(fVulkan14, dynamicRenderingLocalRead); |
| DUP_INTO(fVulkan14, maintenance5); |
| DUP_INTO(fVulkan14, maintenance6); |
| DUP_INTO(fVulkan14, pipelineProtectedAccess); |
| DUP_INTO(fVulkan14, pipelineRobustness); |
| DUP_INTO(fVulkan14, hostImageCopy); |
| DUP_INTO(fVulkan14, pushDescriptor); |
| } |
| break; |
| } |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_FEATURES_EXT: { |
| chain(newChainEnd, pNext); |
| toAdd.fRasterizationOrderAttachmentAccess = false; |
| break; |
| } |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BLEND_OPERATION_ADVANCED_FEATURES_EXT: { |
| chain(newChainEnd, pNext); |
| toAdd.fBlendOperationAdvanced = false; |
| break; |
| } |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_FEATURES_EXT: { |
| // Drop this struct if Vulkan 1.3 is enabled |
| if (!hasVulkan13Features) { |
| chain(newChainEnd, pNext); |
| |
| // Enable the main feature |
| if (exts.fExtendedDynamicStateEXT) { |
| auto* features = |
| reinterpret_cast<VkPhysicalDeviceExtendedDynamicStateFeaturesEXT*>( |
| pNext); |
| features->extendedDynamicState = VK_TRUE; |
| } |
| } |
| toAdd.fExtendedDynamicState = false; |
| break; |
| } |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_2_FEATURES_EXT: { |
| // Drop this struct if Vulkan 1.3 is enabled and the extension's optional features |
| // are not (they are not promoted to 1.3). |
| auto* features = |
| reinterpret_cast<VkPhysicalDeviceExtendedDynamicState2FeaturesEXT*>(pNext); |
| if (!hasVulkan13Features && !features->extendedDynamicState2LogicOp && |
| !features->extendedDynamicState2PatchControlPoints) { |
| chain(newChainEnd, pNext); |
| |
| // Enable the main feature |
| if (exts.fExtendedDynamicState2EXT) { |
| features->extendedDynamicState2 = VK_TRUE; |
| } |
| } |
| toAdd.fExtendedDynamicState2 = false; |
| break; |
| } |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_INPUT_DYNAMIC_STATE_FEATURES_EXT: { |
| chain(newChainEnd, pNext); |
| // Enable the main feature |
| if (exts.fVertexInputDynamicStateEXT) { |
| auto* features = |
| reinterpret_cast<VkPhysicalDeviceVertexInputDynamicStateFeaturesEXT*>( |
| pNext); |
| features->vertexInputDynamicState = VK_TRUE; |
| } |
| toAdd.fVertexInputDynamicState = false; |
| break; |
| } |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GRAPHICS_PIPELINE_LIBRARY_FEATURES_EXT: { |
| chain(newChainEnd, pNext); |
| // Enable the main feature |
| if (exts.fGraphicsPipelineLibraryEXT) { |
| auto* features = |
| reinterpret_cast<VkPhysicalDeviceGraphicsPipelineLibraryFeaturesEXT*>( |
| pNext); |
| features->graphicsPipelineLibrary = VK_TRUE; |
| } |
| toAdd.fGraphicsPipelineLibrary = false; |
| break; |
| } |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES: { |
| // Drop this struct if using the Vulkan 1.1 feature struct |
| if (!hasVulkan11Features) { |
| chain(newChainEnd, pNext); |
| |
| // Enable the main feature |
| if (exts.fSamplerYcbcrConversionKHR) { |
| auto* features = |
| reinterpret_cast<VkPhysicalDeviceSamplerYcbcrConversionFeatures*>( |
| pNext); |
| features->samplerYcbcrConversion = VK_TRUE; |
| } |
| } |
| toAdd.fSamplerYcbcrConversion = false; |
| break; |
| } |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RGBA10X6_FORMATS_FEATURES_EXT: { |
| chain(newChainEnd, pNext); |
| toAdd.fRGBA10x6Formats = false; |
| break; |
| } |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SYNCHRONIZATION_2_FEATURES: { |
| // Drop this struct if Vulkan 1.3 is enabled |
| if (!hasVulkan13Features) { |
| chain(newChainEnd, pNext); |
| // Enable the main feature |
| if (exts.fSynchronization2KHR) { |
| auto* features = |
| reinterpret_cast<VkPhysicalDeviceSynchronization2Features*>(pNext); |
| features->synchronization2 = VK_TRUE; |
| } |
| } |
| toAdd.fSynchronization2 = false; |
| break; |
| } |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES: { |
| // Drop this struct if Vulkan 1.3 is enabled |
| if (!hasVulkan13Features) { |
| chain(newChainEnd, pNext); |
| // Enable the main feature |
| if (exts.fDynamicRenderingKHR) { |
| auto* features = |
| reinterpret_cast<VkPhysicalDeviceDynamicRenderingFeatures*>(pNext); |
| features->dynamicRendering = VK_TRUE; |
| } |
| } |
| toAdd.fDynamicRendering = false; |
| break; |
| } |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_LOCAL_READ_FEATURES: { |
| // Drop this struct if Vulkan 1.4 is enabled |
| if (!hasVulkan14Features) { |
| chain(newChainEnd, pNext); |
| // Enable the main feature |
| if (exts.fDynamicRenderingLocalReadKHR) { |
| auto* features = reinterpret_cast< |
| VkPhysicalDeviceDynamicRenderingLocalReadFeatures*>(pNext); |
| features->dynamicRenderingLocalRead = VK_TRUE; |
| } |
| toAdd.fDynamicRenderingLocalRead = false; |
| } |
| break; |
| } |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTISAMPLED_RENDER_TO_SINGLE_SAMPLED_FEATURES_EXT: { |
| chain(newChainEnd, pNext); |
| // Enable the main feature |
| if (exts.fMultisampledRenderToSingleSampledEXT) { |
| auto* features = reinterpret_cast< |
| VkPhysicalDeviceMultisampledRenderToSingleSampledFeaturesEXT*>(pNext); |
| features->multisampledRenderToSingleSampled = VK_TRUE; |
| } |
| toAdd.fMultisampledRenderToSingleSampled = false; |
| break; |
| } |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_IMAGE_COPY_FEATURES: { |
| // Drop this struct if Vulkan 1.4 is enabled |
| if (!hasVulkan14Features) { |
| chain(newChainEnd, pNext); |
| // Enable the main feature |
| if (exts.fHostImageCopyEXT) { |
| auto* features = |
| reinterpret_cast<VkPhysicalDeviceHostImageCopyFeatures*>(pNext); |
| features->hostImageCopy = VK_TRUE; |
| } |
| } |
| toAdd.fHostImageCopy = false; |
| break; |
| } |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_CREATION_CACHE_CONTROL_FEATURES: { |
| // Drop this struct if Vulkan 1.3 is enabled |
| if (!hasVulkan13Features) { |
| chain(newChainEnd, pNext); |
| // Enable the main feature |
| if (exts.fPipelineCreationCacheControlEXT) { |
| auto* features = reinterpret_cast< |
| VkPhysicalDevicePipelineCreationCacheControlFeatures*>(pNext); |
| features->pipelineCreationCacheControl = VK_TRUE; |
| } |
| } |
| toAdd.fPipelineCreationCacheControl = false; |
| break; |
| } |
| // Gather features promoted to Vulkan 1.1 and drop their structs per the following valid |
| // usage statement: |
| // |
| // If the pNext chain includes a VkPhysicalDeviceVulkan11Features structure, then it |
| // must not include a VkPhysicalDevice16BitStorageFeatures, |
| // VkPhysicalDeviceMultiviewFeatures, VkPhysicalDeviceVariablePointersFeatures, |
| // VkPhysicalDeviceProtectedMemoryFeatures, |
| // VkPhysicalDeviceSamplerYcbcrConversionFeatures, or |
| // VkPhysicalDeviceShaderDrawParametersFeatures structure |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES: { |
| if (hasVulkan11Features) { |
| auto* features = reinterpret_cast<VkPhysicalDevice16BitStorageFeatures*>(pNext); |
| DUP_INTO(fVulkan11, storageBuffer16BitAccess); |
| DUP_INTO(fVulkan11, uniformAndStorageBuffer16BitAccess); |
| DUP_INTO(fVulkan11, storagePushConstant16); |
| DUP_INTO(fVulkan11, storageInputOutput16); |
| } else { |
| chain(newChainEnd, pNext); |
| } |
| break; |
| } |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES: { |
| if (hasVulkan11Features) { |
| auto* features = reinterpret_cast<VkPhysicalDeviceMultiviewFeatures*>(pNext); |
| DUP_INTO(fVulkan11, multiview); |
| DUP_INTO(fVulkan11, multiviewGeometryShader); |
| DUP_INTO(fVulkan11, multiviewTessellationShader); |
| } else { |
| chain(newChainEnd, pNext); |
| } |
| break; |
| } |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTERS_FEATURES: { |
| if (hasVulkan11Features) { |
| auto* features = |
| reinterpret_cast<VkPhysicalDeviceVariablePointersFeatures*>(pNext); |
| DUP_INTO(fVulkan11, variablePointersStorageBuffer); |
| DUP_INTO(fVulkan11, variablePointers); |
| } else { |
| chain(newChainEnd, pNext); |
| } |
| break; |
| } |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_FEATURES: { |
| if (hasVulkan11Features) { |
| auto* features = |
| reinterpret_cast<VkPhysicalDeviceProtectedMemoryFeatures*>(pNext); |
| DUP_INTO(fVulkan11, protectedMemory); |
| } else { |
| chain(newChainEnd, pNext); |
| } |
| break; |
| } |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DRAW_PARAMETERS_FEATURES: { |
| if (hasVulkan11Features) { |
| auto* features = |
| reinterpret_cast<VkPhysicalDeviceShaderDrawParametersFeatures*>(pNext); |
| DUP_INTO(fVulkan11, shaderDrawParameters); |
| } else { |
| chain(newChainEnd, pNext); |
| } |
| break; |
| } |
| |
| // Gather features promoted to Vulkan 1.2 and drop their structs per the following valid |
| // usage statement: |
| // |
| // If the pNext chain includes a VkPhysicalDeviceVulkan12Features structure, then it |
| // must not include a VkPhysicalDevice8BitStorageFeatures, |
| // VkPhysicalDeviceShaderAtomicInt64Features, VkPhysicalDeviceShaderFloat16Int8Features, |
| // VkPhysicalDeviceDescriptorIndexingFeatures, |
| // VkPhysicalDeviceScalarBlockLayoutFeatures, |
| // VkPhysicalDeviceImagelessFramebufferFeatures, |
| // VkPhysicalDeviceUniformBufferStandardLayoutFeatures, |
| // VkPhysicalDeviceShaderSubgroupExtendedTypesFeatures, |
| // VkPhysicalDeviceSeparateDepthStencilLayoutsFeatures, |
| // VkPhysicalDeviceHostQueryResetFeatures, VkPhysicalDeviceTimelineSemaphoreFeatures, |
| // VkPhysicalDeviceBufferDeviceAddressFeatures, or |
| // VkPhysicalDeviceVulkanMemoryModelFeatures structure |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES: { |
| if (hasVulkan12Features) { |
| auto* features = reinterpret_cast<VkPhysicalDevice8BitStorageFeatures*>(pNext); |
| DUP_INTO(fVulkan12, storageBuffer8BitAccess); |
| DUP_INTO(fVulkan12, uniformAndStorageBuffer8BitAccess); |
| DUP_INTO(fVulkan12, storagePushConstant8); |
| } else { |
| chain(newChainEnd, pNext); |
| } |
| break; |
| } |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_ATOMIC_INT64_FEATURES: { |
| if (hasVulkan12Features) { |
| auto* features = |
| reinterpret_cast<VkPhysicalDeviceShaderAtomicInt64Features*>(pNext); |
| DUP_INTO(fVulkan12, shaderBufferInt64Atomics); |
| DUP_INTO(fVulkan12, shaderSharedInt64Atomics); |
| } else { |
| chain(newChainEnd, pNext); |
| } |
| break; |
| } |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES: { |
| if (hasVulkan12Features) { |
| auto* features = |
| reinterpret_cast<VkPhysicalDeviceShaderFloat16Int8Features*>(pNext); |
| DUP_INTO(fVulkan12, shaderFloat16); |
| DUP_INTO(fVulkan12, shaderInt8); |
| } else { |
| chain(newChainEnd, pNext); |
| } |
| break; |
| } |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES: { |
| if (hasVulkan12Features) { |
| auto* features = |
| reinterpret_cast<VkPhysicalDeviceDescriptorIndexingFeatures*>(pNext); |
| DUP_INTO(fVulkan12, shaderInputAttachmentArrayDynamicIndexing); |
| DUP_INTO(fVulkan12, shaderUniformTexelBufferArrayDynamicIndexing); |
| DUP_INTO(fVulkan12, shaderStorageTexelBufferArrayDynamicIndexing); |
| DUP_INTO(fVulkan12, shaderUniformBufferArrayNonUniformIndexing); |
| DUP_INTO(fVulkan12, shaderSampledImageArrayNonUniformIndexing); |
| DUP_INTO(fVulkan12, shaderStorageBufferArrayNonUniformIndexing); |
| DUP_INTO(fVulkan12, shaderStorageImageArrayNonUniformIndexing); |
| DUP_INTO(fVulkan12, shaderInputAttachmentArrayNonUniformIndexing); |
| DUP_INTO(fVulkan12, shaderUniformTexelBufferArrayNonUniformIndexing); |
| DUP_INTO(fVulkan12, shaderStorageTexelBufferArrayNonUniformIndexing); |
| DUP_INTO(fVulkan12, descriptorBindingUniformBufferUpdateAfterBind); |
| DUP_INTO(fVulkan12, descriptorBindingSampledImageUpdateAfterBind); |
| DUP_INTO(fVulkan12, descriptorBindingStorageImageUpdateAfterBind); |
| DUP_INTO(fVulkan12, descriptorBindingStorageBufferUpdateAfterBind); |
| DUP_INTO(fVulkan12, descriptorBindingUniformTexelBufferUpdateAfterBind); |
| DUP_INTO(fVulkan12, descriptorBindingStorageTexelBufferUpdateAfterBind); |
| DUP_INTO(fVulkan12, descriptorBindingUpdateUnusedWhilePending); |
| DUP_INTO(fVulkan12, descriptorBindingPartiallyBound); |
| DUP_INTO(fVulkan12, descriptorBindingVariableDescriptorCount); |
| DUP_INTO(fVulkan12, runtimeDescriptorArray); |
| } else { |
| chain(newChainEnd, pNext); |
| } |
| break; |
| } |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SCALAR_BLOCK_LAYOUT_FEATURES: { |
| if (hasVulkan12Features) { |
| auto* features = |
| reinterpret_cast<VkPhysicalDeviceScalarBlockLayoutFeatures*>(pNext); |
| DUP_INTO(fVulkan12, scalarBlockLayout); |
| } else { |
| chain(newChainEnd, pNext); |
| } |
| break; |
| } |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGELESS_FRAMEBUFFER_FEATURES: { |
| if (hasVulkan12Features) { |
| auto* features = |
| reinterpret_cast<VkPhysicalDeviceImagelessFramebufferFeatures*>(pNext); |
| DUP_INTO(fVulkan12, imagelessFramebuffer); |
| } else { |
| chain(newChainEnd, pNext); |
| } |
| break; |
| break; |
| } |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_UNIFORM_BUFFER_STANDARD_LAYOUT_FEATURES: { |
| if (hasVulkan12Features) { |
| auto* features = |
| reinterpret_cast<VkPhysicalDeviceUniformBufferStandardLayoutFeatures*>( |
| pNext); |
| DUP_INTO(fVulkan12, uniformBufferStandardLayout); |
| } else { |
| chain(newChainEnd, pNext); |
| } |
| break; |
| } |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SUBGROUP_EXTENDED_TYPES_FEATURES: { |
| if (hasVulkan12Features) { |
| auto* features = |
| reinterpret_cast<VkPhysicalDeviceShaderSubgroupExtendedTypesFeatures*>( |
| pNext); |
| DUP_INTO(fVulkan12, shaderSubgroupExtendedTypes); |
| } else { |
| chain(newChainEnd, pNext); |
| } |
| break; |
| } |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SEPARATE_DEPTH_STENCIL_LAYOUTS_FEATURES: { |
| if (hasVulkan12Features) { |
| auto* features = |
| reinterpret_cast<VkPhysicalDeviceSeparateDepthStencilLayoutsFeatures*>( |
| pNext); |
| DUP_INTO(fVulkan12, separateDepthStencilLayouts); |
| } else { |
| chain(newChainEnd, pNext); |
| } |
| break; |
| } |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_QUERY_RESET_FEATURES: { |
| if (hasVulkan12Features) { |
| auto* features = |
| reinterpret_cast<VkPhysicalDeviceHostQueryResetFeatures*>(pNext); |
| DUP_INTO(fVulkan12, hostQueryReset); |
| } else { |
| chain(newChainEnd, pNext); |
| } |
| break; |
| } |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES: { |
| if (hasVulkan12Features) { |
| auto* features = |
| reinterpret_cast<VkPhysicalDeviceTimelineSemaphoreFeatures*>(pNext); |
| DUP_INTO(fVulkan12, timelineSemaphore); |
| } else { |
| chain(newChainEnd, pNext); |
| } |
| break; |
| } |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES: { |
| if (hasVulkan12Features) { |
| auto* features = |
| reinterpret_cast<VkPhysicalDeviceBufferDeviceAddressFeatures*>(pNext); |
| DUP_INTO(fVulkan12, bufferDeviceAddress); |
| DUP_INTO(fVulkan12, bufferDeviceAddressCaptureReplay); |
| DUP_INTO(fVulkan12, bufferDeviceAddressMultiDevice); |
| } else { |
| chain(newChainEnd, pNext); |
| } |
| break; |
| } |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_MEMORY_MODEL_FEATURES: { |
| if (hasVulkan12Features) { |
| auto* features = |
| reinterpret_cast<VkPhysicalDeviceVulkanMemoryModelFeatures*>(pNext); |
| DUP_INTO(fVulkan12, vulkanMemoryModel); |
| DUP_INTO(fVulkan12, vulkanMemoryModelDeviceScope); |
| DUP_INTO(fVulkan12, vulkanMemoryModelAvailabilityVisibilityChains); |
| } else { |
| chain(newChainEnd, pNext); |
| } |
| break; |
| } |
| |
| // Gather features promoted to Vulkan 1.3 and drop their structs per the following valid |
| // usage statement: |
| // |
| // If the pNext chain includes a VkPhysicalDeviceVulkan13Features structure, then it |
| // must not include a VkPhysicalDeviceDynamicRenderingFeatures, |
| // VkPhysicalDeviceImageRobustnessFeatures, VkPhysicalDeviceInlineUniformBlockFeatures, |
| // VkPhysicalDeviceMaintenance4Features, |
| // VkPhysicalDevicePipelineCreationCacheControlFeatures, |
| // VkPhysicalDevicePrivateDataFeatures, |
| // VkPhysicalDeviceShaderDemoteToHelperInvocationFeatures, |
| // VkPhysicalDeviceShaderIntegerDotProductFeatures, |
| // VkPhysicalDeviceShaderTerminateInvocationFeatures, |
| // VkPhysicalDeviceSubgroupSizeControlFeatures, |
| // VkPhysicalDeviceSynchronization2Features, |
| // VkPhysicalDeviceTextureCompressionASTCHDRFeatures, or |
| // VkPhysicalDeviceZeroInitializeWorkgroupMemoryFeatures structure |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_ROBUSTNESS_FEATURES: { |
| if (hasVulkan13Features) { |
| auto* features = |
| reinterpret_cast<VkPhysicalDeviceImageRobustnessFeatures*>(pNext); |
| DUP_INTO(fVulkan13, robustImageAccess); |
| } else { |
| chain(newChainEnd, pNext); |
| } |
| break; |
| } |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INLINE_UNIFORM_BLOCK_FEATURES: { |
| if (hasVulkan13Features) { |
| auto* features = |
| reinterpret_cast<VkPhysicalDeviceInlineUniformBlockFeatures*>(pNext); |
| DUP_INTO(fVulkan13, inlineUniformBlock); |
| DUP_INTO(fVulkan13, descriptorBindingInlineUniformBlockUpdateAfterBind); |
| } else { |
| chain(newChainEnd, pNext); |
| } |
| break; |
| } |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_4_FEATURES: { |
| if (hasVulkan13Features) { |
| auto* features = reinterpret_cast<VkPhysicalDeviceMaintenance4Features*>(pNext); |
| DUP_INTO(fVulkan13, maintenance4); |
| } else { |
| chain(newChainEnd, pNext); |
| } |
| break; |
| } |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIVATE_DATA_FEATURES: { |
| if (hasVulkan13Features) { |
| auto* features = reinterpret_cast<VkPhysicalDevicePrivateDataFeatures*>(pNext); |
| DUP_INTO(fVulkan13, privateData); |
| } else { |
| chain(newChainEnd, pNext); |
| } |
| break; |
| } |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DEMOTE_TO_HELPER_INVOCATION_FEATURES: { |
| if (hasVulkan13Features) { |
| auto* features = reinterpret_cast< |
| VkPhysicalDeviceShaderDemoteToHelperInvocationFeatures*>(pNext); |
| DUP_INTO(fVulkan13, shaderDemoteToHelperInvocation); |
| } else { |
| chain(newChainEnd, pNext); |
| } |
| break; |
| } |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_INTEGER_DOT_PRODUCT_FEATURES: { |
| if (hasVulkan13Features) { |
| auto* features = |
| reinterpret_cast<VkPhysicalDeviceShaderIntegerDotProductFeatures*>( |
| pNext); |
| DUP_INTO(fVulkan13, shaderIntegerDotProduct); |
| } else { |
| chain(newChainEnd, pNext); |
| } |
| break; |
| } |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_TERMINATE_INVOCATION_FEATURES: { |
| if (hasVulkan13Features) { |
| auto* features = |
| reinterpret_cast<VkPhysicalDeviceShaderTerminateInvocationFeatures*>( |
| pNext); |
| DUP_INTO(fVulkan13, shaderTerminateInvocation); |
| } else { |
| chain(newChainEnd, pNext); |
| } |
| break; |
| } |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_FEATURES: { |
| if (hasVulkan13Features) { |
| auto* features = |
| reinterpret_cast<VkPhysicalDeviceSubgroupSizeControlFeatures*>(pNext); |
| DUP_INTO(fVulkan13, subgroupSizeControl); |
| DUP_INTO(fVulkan13, computeFullSubgroups); |
| } else { |
| chain(newChainEnd, pNext); |
| } |
| break; |
| } |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TEXTURE_COMPRESSION_ASTC_HDR_FEATURES: { |
| if (hasVulkan13Features) { |
| auto* features = |
| reinterpret_cast<VkPhysicalDeviceTextureCompressionASTCHDRFeatures*>( |
| pNext); |
| DUP_INTO(fVulkan13, textureCompressionASTC_HDR); |
| } else { |
| chain(newChainEnd, pNext); |
| } |
| break; |
| } |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ZERO_INITIALIZE_WORKGROUP_MEMORY_FEATURES: { |
| if (hasVulkan13Features) { |
| auto* features = reinterpret_cast< |
| VkPhysicalDeviceZeroInitializeWorkgroupMemoryFeatures*>(pNext); |
| DUP_INTO(fVulkan13, shaderZeroInitializeWorkgroupMemory); |
| } else { |
| chain(newChainEnd, pNext); |
| } |
| break; |
| } |
| |
| // Gather features promoted to Vulkan 1.4 and drop their structs per the following valid |
| // usage statement: |
| // |
| // If the pNext chain includes a VkPhysicalDeviceVulkan14Features structure, then it |
| // must not include a VkPhysicalDeviceGlobalPriorityQueryFeatures, |
| // VkPhysicalDeviceShaderSubgroupRotateFeatures, |
| // VkPhysicalDeviceShaderFloatControls2Features, |
| // VkPhysicalDeviceShaderExpectAssumeFeatures, |
| // VkPhysicalDeviceLineRasterizationFeatures, |
| // VkPhysicalDeviceVertexAttributeDivisorFeatures, |
| // VkPhysicalDeviceIndexTypeUint8Features, |
| // VkPhysicalDeviceDynamicRenderingLocalReadFeatures, |
| // VkPhysicalDeviceMaintenance5Features, VkPhysicalDeviceMaintenance6Features, |
| // VkPhysicalDevicePipelineProtectedAccessFeatures, |
| // VkPhysicalDevicePipelineRobustnessFeatures, or VkPhysicalDeviceHostImageCopyFeatures |
| // structure |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GLOBAL_PRIORITY_QUERY_FEATURES: { |
| if (hasVulkan14Features) { |
| auto* features = |
| reinterpret_cast<VkPhysicalDeviceGlobalPriorityQueryFeatures*>(pNext); |
| DUP_INTO(fVulkan14, globalPriorityQuery); |
| } else { |
| chain(newChainEnd, pNext); |
| } |
| break; |
| } |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SUBGROUP_ROTATE_FEATURES: { |
| if (hasVulkan14Features) { |
| auto* features = |
| reinterpret_cast<VkPhysicalDeviceShaderSubgroupRotateFeatures*>(pNext); |
| DUP_INTO(fVulkan14, shaderSubgroupRotate); |
| DUP_INTO(fVulkan14, shaderSubgroupRotateClustered); |
| } else { |
| chain(newChainEnd, pNext); |
| } |
| break; |
| } |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT_CONTROLS_2_FEATURES: { |
| if (hasVulkan14Features) { |
| auto* features = |
| reinterpret_cast<VkPhysicalDeviceShaderFloatControls2Features*>(pNext); |
| DUP_INTO(fVulkan14, shaderFloatControls2); |
| } else { |
| chain(newChainEnd, pNext); |
| } |
| break; |
| } |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_EXPECT_ASSUME_FEATURES: { |
| if (hasVulkan14Features) { |
| auto* features = |
| reinterpret_cast<VkPhysicalDeviceShaderExpectAssumeFeatures*>(pNext); |
| DUP_INTO(fVulkan14, shaderExpectAssume); |
| } else { |
| chain(newChainEnd, pNext); |
| } |
| break; |
| } |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_LINE_RASTERIZATION_FEATURES: { |
| if (hasVulkan14Features) { |
| auto* features = |
| reinterpret_cast<VkPhysicalDeviceLineRasterizationFeatures*>(pNext); |
| DUP_INTO(fVulkan14, rectangularLines); |
| DUP_INTO(fVulkan14, bresenhamLines); |
| DUP_INTO(fVulkan14, smoothLines); |
| DUP_INTO(fVulkan14, stippledRectangularLines); |
| DUP_INTO(fVulkan14, stippledBresenhamLines); |
| DUP_INTO(fVulkan14, stippledSmoothLines); |
| } else { |
| chain(newChainEnd, pNext); |
| } |
| break; |
| } |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_FEATURES: { |
| if (hasVulkan14Features) { |
| auto* features = |
| reinterpret_cast<VkPhysicalDeviceVertexAttributeDivisorFeatures*>( |
| pNext); |
| DUP_INTO(fVulkan14, vertexAttributeInstanceRateDivisor); |
| DUP_INTO(fVulkan14, vertexAttributeInstanceRateZeroDivisor); |
| } else { |
| chain(newChainEnd, pNext); |
| } |
| break; |
| } |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INDEX_TYPE_UINT8_FEATURES: { |
| if (hasVulkan14Features) { |
| auto* features = |
| reinterpret_cast<VkPhysicalDeviceIndexTypeUint8Features*>(pNext); |
| DUP_INTO(fVulkan14, indexTypeUint8); |
| } else { |
| chain(newChainEnd, pNext); |
| } |
| break; |
| } |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_5_FEATURES: { |
| if (hasVulkan14Features) { |
| auto* features = reinterpret_cast<VkPhysicalDeviceMaintenance5Features*>(pNext); |
| DUP_INTO(fVulkan14, maintenance5); |
| } else { |
| chain(newChainEnd, pNext); |
| } |
| break; |
| } |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_6_FEATURES: { |
| if (hasVulkan14Features) { |
| auto* features = reinterpret_cast<VkPhysicalDeviceMaintenance6Features*>(pNext); |
| DUP_INTO(fVulkan14, maintenance6); |
| } else { |
| chain(newChainEnd, pNext); |
| } |
| break; |
| } |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_PROTECTED_ACCESS_FEATURES: { |
| if (hasVulkan14Features) { |
| auto* features = |
| reinterpret_cast<VkPhysicalDevicePipelineProtectedAccessFeatures*>( |
| pNext); |
| DUP_INTO(fVulkan14, pipelineProtectedAccess); |
| } else { |
| chain(newChainEnd, pNext); |
| } |
| break; |
| } |
| case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_ROBUSTNESS_FEATURES: { |
| if (hasVulkan14Features) { |
| auto* features = |
| reinterpret_cast<VkPhysicalDevicePipelineRobustnessFeatures*>(pNext); |
| DUP_INTO(fVulkan14, pipelineRobustness); |
| } else { |
| chain(newChainEnd, pNext); |
| } |
| break; |
| } |
| |
| default: |
| // Retain everything else that's chained. |
| chain(newChainEnd, pNext); |
| break; |
| } |
| |
| pNext = pNext->pNext; |
| } |
| #undef DUP_INTO |
| |
| // Chain any struct that was not found in appFeatures. |
| if (hasVulkan11Features) { |
| chain(newChainEnd, &fVulkan11); |
| } |
| if (hasVulkan12Features) { |
| chain(newChainEnd, &fVulkan12); |
| } |
| if (hasVulkan13Features) { |
| chain(newChainEnd, &fVulkan13); |
| } |
| if (hasVulkan14Features) { |
| chain(newChainEnd, &fVulkan14); |
| } |
| if (toAdd.fRasterizationOrderAttachmentAccess) { |
| chain(newChainEnd, &fRasterizationOrderAttachmentAccess); |
| } |
| if (toAdd.fBlendOperationAdvanced) { |
| chain(newChainEnd, &fBlendOperationAdvanced); |
| } |
| if (toAdd.fExtendedDynamicState) { |
| chain(newChainEnd, &fExtendedDynamicState); |
| } |
| if (toAdd.fExtendedDynamicState2) { |
| chain(newChainEnd, &fExtendedDynamicState2); |
| } |
| if (toAdd.fVertexInputDynamicState) { |
| chain(newChainEnd, &fVertexInputDynamicState); |
| } |
| if (toAdd.fGraphicsPipelineLibrary) { |
| chain(newChainEnd, &fGraphicsPipelineLibrary); |
| } |
| if (toAdd.fSamplerYcbcrConversion) { |
| chain(newChainEnd, &fSamplerYcbcrConversion); |
| } |
| if (toAdd.fRGBA10x6Formats) { |
| chain(newChainEnd, &fRGBA10x6Formats); |
| } |
| if (toAdd.fSynchronization2) { |
| chain(newChainEnd, &fSynchronization2); |
| } |
| if (toAdd.fDynamicRendering) { |
| chain(newChainEnd, &fDynamicRendering); |
| } |
| if (toAdd.fDynamicRenderingLocalRead) { |
| chain(newChainEnd, &fDynamicRenderingLocalRead); |
| } |
| if (toAdd.fMultisampledRenderToSingleSampled) { |
| chain(newChainEnd, &fMultisampledRenderToSingleSampled); |
| } |
| if (toAdd.fHostImageCopy) { |
| chain(newChainEnd, &fHostImageCopy); |
| } |
| if (toAdd.fPipelineCreationCacheControl) { |
| chain(newChainEnd, &fPipelineCreationCacheControl); |
| } |
| |
| // Replace appFeatures' pNext with the new chain. |
| *newChainEnd = nullptr; |
| appFeatures.pNext = newChainStart; |
| |
| fHasAddedFeaturesToEnable = true; |
| } |
| |
| } // namespace skgpu |