From ac0f77b7e69683b69eebbbb6741e7826c8efb068 Mon Sep 17 00:00:00 2001
From: TheSniperFan <[EMAIL REDACTED]>
Date: Wed, 12 Nov 2025 00:53:06 +0100
Subject: [PATCH] Allow SDL GPU to opt into additional Vulkan features (#14204)
---
include/SDL3/SDL_gpu.h | 40 +-
src/gpu/vulkan/SDL_gpu_vulkan.c | 653 ++++++++++++++++++++++--
src/gpu/vulkan/SDL_gpu_vulkan_vkfuncs.h | 3 +
3 files changed, 654 insertions(+), 42 deletions(-)
diff --git a/include/SDL3/SDL_gpu.h b/include/SDL3/SDL_gpu.h
index 3d167b4fc4972..c137e898d98fa 100644
--- a/include/SDL3/SDL_gpu.h
+++ b/include/SDL3/SDL_gpu.h
@@ -2315,7 +2315,13 @@ extern SDL_DECLSPEC SDL_GPUDevice * SDLCALL SDL_CreateGPUDevice(
* This can be useful if your application _requires_ SDL_GPU, but if you can
* provide your own fallback renderer (for example, an OpenGL renderer) this
* property can be set to true. Defaults to false.
- *
+ * - `SDL_PROP_GPU_DEVICE_CREATE_VULKAN_OPTIONS_POINTER`: pointer to an
+ * SDL_GPUVulkanOptions structure to be processed during device creation.
+ * This allows configuring a variety of Vulkan-specific options such as
+ * increasing the API version and opting into extensions aside from the
+ * minimal set SDL requires. The structure is NOT deep-copied. Make sure
+ * that the pointer valid memory during window creation.
+ *
* \param props the properties to use.
* \returns a GPU context on success or NULL on failure; call SDL_GetError()
* for more information.
@@ -2347,6 +2353,38 @@ extern SDL_DECLSPEC SDL_GPUDevice * SDLCALL SDL_CreateGPUDeviceWithProperties(
#define SDL_PROP_GPU_DEVICE_CREATE_D3D12_ALLOW_FEWER_RESOURCE_SLOTS_BOOLEAN "SDL.gpu.device.create.d3d12.allowtier1resourcebinding"
#define SDL_PROP_GPU_DEVICE_CREATE_D3D12_SEMANTIC_NAME_STRING "SDL.gpu.device.create.d3d12.semantic"
#define SDL_PROP_GPU_DEVICE_CREATE_VULKAN_REQUIRE_HARDWARE_ACCELERATION_BOOLEAN "SDL.gpu.device.create.vulkan.requirehardwareacceleration"
+#define SDL_PROP_GPU_DEVICE_CREATE_VULKAN_OPTIONS_POINTER "SDL.gpu.device.create.vulkan.options"
+
+
+/**
+ * A structure specifying additional options when using Vulkan.
+ *
+ * When no such structure is provided, SDL will use Vulkan API version 1.0 and
+ * a minimal set of features.
+ * The requested API version influences how the feature_list is processed by
+ * SDL. When requesting API version 1.0, the feature_list is ignored. Only the
+ * vulkan_10_phyisical_device_features and the extension lists are used. When
+ * requesting API version 1.1, the feature_list is scanned for feature
+ * structures introduced in Vulkan 1.1. When requesting Vulkan 1.2 or higher,
+ * the feature_list is additionally scanned for compound feature structs such
+ * as VkPhysicalDeviceVulkan11Features. The device and instance extension
+ * lists, as well as vulkan_10_physical_device_features, are always processed.
+ * The pointers inside this structure are NOT deep-copied. Make sure they are
+ * valid during window creation.
+ *
+ * \since This struct is available since SDL 3.4.0.
+ *
+ */
+typedef struct SDL_GPUVulkanOptions
+{
+ Uint32 vulkan_api_version; /**< The Vulkan API version to request for the instance. Use Vulkan's VK_MAKE_VERSION or VK_MAKE_API_VERSION. */
+ void *feature_list; /**< Pointer to the first element of a chain of Vulkan feature structs. (Requires API version 1.1 or higher.)*/
+ void *vulkan_10_physical_device_features; /**< Pointer to a VkPhysicalDeviceFeatures struct to enable additional Vulkan 1.0 features. */
+ Uint32 device_extension_count; /**< Number of additional device extensions to require. */
+ const char **device_extension_names; /**< Pointer to a list of additional device extensions to require. */
+ Uint32 instance_extension_count; /**< Number of additional instance extensions to require. */
+ const char **instance_extension_names; /**< Pointer to a list of additional instance extensions to require. */
+} SDL_GPUVulkanOptions;
/**
* Destroys a GPU context previously returned by SDL_CreateGPUDevice.
diff --git a/src/gpu/vulkan/SDL_gpu_vulkan.c b/src/gpu/vulkan/SDL_gpu_vulkan.c
index 4550a0b6d199d..20f3972a2eaaf 100644
--- a/src/gpu/vulkan/SDL_gpu_vulkan.c
+++ b/src/gpu/vulkan/SDL_gpu_vulkan.c
@@ -1087,13 +1087,23 @@ struct VulkanRenderer
VkPhysicalDevice physicalDevice;
VkPhysicalDeviceProperties2KHR physicalDeviceProperties;
VkPhysicalDeviceDriverPropertiesKHR physicalDeviceDriverProperties;
- VkPhysicalDeviceFeatures desiredDeviceFeatures;
+ VkPhysicalDeviceFeatures desiredVulkan10DeviceFeatures;
VkDevice logicalDevice;
Uint8 integratedMemoryNotification;
Uint8 outOfDeviceLocalMemoryWarning;
Uint8 outofBARMemoryWarning;
Uint8 fillModeOnlyWarning;
+ bool usesCustomVulkanOptions;
+ Uint32 desiredApiVersion;
+ VkPhysicalDeviceVulkan11Features desiredVulkan11DeviceFeatures;
+ VkPhysicalDeviceVulkan12Features desiredVulkan12DeviceFeatures;
+ VkPhysicalDeviceVulkan13Features desiredVulkan13DeviceFeatures;
+ Uint32 additionalDeviceExtensionCount;
+ const char **additionalDeviceExtensionNames;
+ Uint32 additionalInstanceExtensionCount;
+ const char **additionalInstanceExtensionNames;
+
bool debugMode;
bool preferLowPower;
bool requireHardwareAcceleration;
@@ -11067,7 +11077,8 @@ static Uint8 VULKAN_INTERNAL_CheckInstanceExtensions(
Uint32 requiredExtensionsLength,
bool *supportsDebugUtils,
bool *supportsColorspace,
- bool *supportsPhysicalDeviceProperties2)
+ bool *supportsPhysicalDeviceProperties2,
+ int *firstUnsupportedExtensionIndex)
{
Uint32 extensionCount, i;
VkExtensionProperties *availableExtensions;
@@ -11090,6 +11101,7 @@ static Uint8 VULKAN_INTERNAL_CheckInstanceExtensions(
availableExtensions,
extensionCount)) {
allExtensionsSupported = 0;
+ *firstUnsupportedExtensionIndex = i;
break;
}
}
@@ -11116,6 +11128,30 @@ static Uint8 VULKAN_INTERNAL_CheckInstanceExtensions(
return allExtensionsSupported;
}
+static Uint8 CheckOptInDeviceExtensions(VulkanRenderer *renderer,
+ VkPhysicalDevice physicalDevice,
+ Uint32 numExtensions,
+ VkExtensionProperties *availableExtensions,
+ const char **missingExtensionName) {
+ Uint8 supportsAll = 1;
+ for (Uint32 extensionIdx = 0; extensionIdx < renderer->additionalDeviceExtensionCount; extensionIdx++) {
+ bool found = false;
+ for (Uint32 searchIdx = 0; searchIdx < numExtensions; searchIdx++) {
+ if (SDL_strcmp(renderer->additionalDeviceExtensionNames[extensionIdx], availableExtensions[searchIdx].extensionName) == 0) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ supportsAll = 0;
+ *missingExtensionName = renderer->additionalDeviceExtensionNames[extensionIdx];
+ break;
+ }
+ }
+
+ return supportsAll;
+}
+
static Uint8 VULKAN_INTERNAL_CheckDeviceExtensions(
VulkanRenderer *renderer,
VkPhysicalDevice physicalDevice,
@@ -11143,6 +11179,19 @@ static Uint8 VULKAN_INTERNAL_CheckDeviceExtensions(
extensionCount,
physicalDeviceExtensions);
+ if (renderer->usesCustomVulkanOptions) {
+ const char *missingExtensionName;
+ if (!CheckOptInDeviceExtensions(renderer, physicalDevice, extensionCount, availableExtensions, &missingExtensionName)) {
+ SDL_assert(missingExtensionName);
+ if (renderer->debugMode) {
+ SDL_LogError(SDL_LOG_CATEGORY_GPU,
+ "Required Vulkan device extension '%s' not supported",
+ missingExtensionName);
+ }
+ allExtensionsSupported = 0;
+ }
+ }
+
SDL_free(availableExtensions);
return allExtensionsSupported;
}
@@ -11180,6 +11229,470 @@ static Uint8 VULKAN_INTERNAL_CheckValidationLayers(
return layerFound;
}
+#define CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, feature, result) \
+ if (requested->feature && !supported->feature) { \
+ SDL_LogVerbose( \
+ SDL_LOG_CATEGORY_GPU, \
+ "SDL GPU Vulkan: Application requested unsupported physical device feature '" #feature "'"); \
+ result = false; \
+ }
+
+static bool VULKAN_INTERNAL_ValidateOptInVulkan10Features(VkPhysicalDeviceFeatures *requested, VkPhysicalDeviceFeatures *supported)
+{
+ if (requested && supported) {
+ bool result = true;
+
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, robustBufferAccess, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, fullDrawIndexUint32, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, imageCubeArray, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, independentBlend, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, geometryShader, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, tessellationShader, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, sampleRateShading, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, dualSrcBlend, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, logicOp, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, multiDrawIndirect, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, drawIndirectFirstInstance, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, depthClamp, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, depthBiasClamp, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, fillModeNonSolid, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, depthBounds, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, wideLines, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, largePoints, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, alphaToOne, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, multiViewport, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, samplerAnisotropy, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, textureCompressionETC2, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, textureCompressionASTC_LDR, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, textureCompressionBC, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, occlusionQueryPrecise, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, pipelineStatisticsQuery, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, vertexPipelineStoresAndAtomics, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, fragmentStoresAndAtomics, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, shaderTessellationAndGeometryPointSize, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, shaderImageGatherExtended, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, shaderStorageImageExtendedFormats, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, shaderStorageImageMultisample, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, shaderStorageImageReadWithoutFormat, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, shaderStorageImageWriteWithoutFormat, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, shaderUniformBufferArrayDynamicIndexing, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, shaderSampledImageArrayDynamicIndexing, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, shaderStorageBufferArrayDynamicIndexing, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, shaderStorageImageArrayDynamicIndexing, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, shaderClipDistance, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, shaderCullDistance, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, shaderFloat64, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, shaderInt64, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, shaderInt16, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, shaderResourceResidency, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, shaderResourceMinLod, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, sparseBinding, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, sparseResidencyBuffer, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, sparseResidencyImage2D, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, sparseResidencyImage3D, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, sparseResidency2Samples, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, sparseResidency4Samples, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, sparseResidency8Samples, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, sparseResidency16Samples, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, sparseResidencyAliased, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, variableMultisampleRate, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, inheritedQueries, result)
+
+ return result;
+ } else {
+ return false;
+ }
+}
+
+static bool VULKAN_INTERNAL_ValidateOptInVulkan11Features(VkPhysicalDeviceVulkan11Features *requested, VkPhysicalDeviceVulkan11Features *supported)
+{
+ if (requested && supported) {
+ bool result = true;
+
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, storageBuffer16BitAccess, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, uniformAndStorageBuffer16BitAccess, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, storagePushConstant16, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, storageInputOutput16, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, multiview, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, multiviewGeometryShader, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, multiviewTessellationShader, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, variablePointersStorageBuffer, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, variablePointers, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, protectedMemory, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, samplerYcbcrConversion, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, shaderDrawParameters, result)
+
+ return result;
+ } else {
+ return false;
+ }
+}
+
+static bool VULKAN_INTERNAL_ValidateOptInVulkan12Features(VkPhysicalDeviceVulkan12Features *requested, VkPhysicalDeviceVulkan12Features *supported)
+{
+ if (requested && supported) {
+ bool result = true;
+
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, samplerMirrorClampToEdge, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, drawIndirectCount, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, storageBuffer8BitAccess, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, uniformAndStorageBuffer8BitAccess, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, storagePushConstant8, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, shaderBufferInt64Atomics, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, shaderSharedInt64Atomics, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, shaderFloat16, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, shaderInt8, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, descriptorIndexing, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, shaderInputAttachmentArrayDynamicIndexing, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, shaderUniformTexelBufferArrayDynamicIndexing, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, shaderStorageTexelBufferArrayDynamicIndexing, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, shaderUniformBufferArrayNonUniformIndexing, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, shaderSampledImageArrayNonUniformIndexing, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, shaderStorageBufferArrayNonUniformIndexing, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, shaderStorageImageArrayNonUniformIndexing, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, shaderInputAttachmentArrayNonUniformIndexing, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, shaderUniformTexelBufferArrayNonUniformIndexing, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, shaderStorageTexelBufferArrayNonUniformIndexing, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, descriptorBindingUniformBufferUpdateAfterBind, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, descriptorBindingSampledImageUpdateAfterBind, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, descriptorBindingStorageImageUpdateAfterBind, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, descriptorBindingStorageBufferUpdateAfterBind, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, descriptorBindingUniformTexelBufferUpdateAfterBind, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, descriptorBindingStorageTexelBufferUpdateAfterBind, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, descriptorBindingUpdateUnusedWhilePending, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, descriptorBindingPartiallyBound, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, descriptorBindingVariableDescriptorCount, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, runtimeDescriptorArray, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, samplerFilterMinmax, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, scalarBlockLayout, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, imagelessFramebuffer, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, uniformBufferStandardLayout, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, shaderSubgroupExtendedTypes, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, separateDepthStencilLayouts, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, hostQueryReset, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, timelineSemaphore, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, bufferDeviceAddress, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, bufferDeviceAddressCaptureReplay, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, bufferDeviceAddressMultiDevice, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, vulkanMemoryModel, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, vulkanMemoryModelDeviceScope, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, vulkanMemoryModelAvailabilityVisibilityChains, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, shaderOutputViewportIndex, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, shaderOutputLayer, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, subgroupBroadcastDynamicId, result)
+
+ return result;
+ } else {
+ return false;
+ }
+}
+
+static bool VULKAN_INTERNAL_ValidateOptInVulkan13Features(VkPhysicalDeviceVulkan13Features *requested, VkPhysicalDeviceVulkan13Features *supported)
+{
+ if (requested && supported) {
+ bool result = true;
+
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, robustImageAccess, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, inlineUniformBlock, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, descriptorBindingInlineUniformBlockUpdateAfterBind, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, pipelineCreationCacheControl, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, privateData, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, shaderDemoteToHelperInvocation, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, shaderTerminateInvocation, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, subgroupSizeControl, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, computeFullSubgroups, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, synchronization2, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, textureCompressionASTC_HDR, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, shaderZeroInitializeWorkgroupMemory, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, dynamicRendering, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, shaderIntegerDotProduct, result)
+ CHECK_OPTIONAL_DEVICE_FEATURE(requested, supported, maintenance4, result)
+
+ return result;
+ } else {
+ return false;
+ }
+}
+
+#undef CHECK_OPTIONAL_DEVICE_FEATURE
+
+static bool VULKAN_INTERNAL_ValidateOptInFeatures(VulkanRenderer *renderer, VkPhysicalDevice physicalDevice, VkPhysicalDeviceFeatures *vk10Features)
+{
+ bool supportsAllFeatures = true;
+
+ int minorVersion = VK_API_VERSION_MINOR(renderer->desiredApiVersion);
+
+ if (minorVersion < 1) {
+ supportsAllFeatures &= VULKAN_INTERNAL_ValidateOptInVulkan10Features(&renderer->desiredVulkan10DeviceFeatures, vk10Features);
+ } else if (minorVersion < 2) {
+ // Query device features using the pre-1.2 structures
+ VkPhysicalDevice16BitStorageFeatures storage = { 0 };
+ storage.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES;
+
+ VkPhysicalDeviceMultiviewFeatures multiview = { 0 };
+ multiview.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES;
+
+ VkPhysicalDeviceProtectedMemoryFeatures protectedMem = { 0 };
+ protectedMem.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_FEATURES;
+
+ VkPhysicalDeviceSamplerYcbcrConversionFeatures ycbcr = { 0 };
+ ycbcr.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES;
+
+ VkPhysicalDeviceShaderDrawParametersFeatures drawParams = { 0 };
+ drawParams.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DRAW_PARAMETERS_FEATURES;
+
+ VkPhysicalDeviceVariablePointersFeatures varPointers = { 0 };
+ varPointers.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTERS_FEATURES;
+
+ VkPhysicalDeviceFeatures2 supportedFeatureList = { 0 };
+ supportedFeatureList.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
+ supportedFeatureList.pNext = &storage;
+ storage.pNext = &multiview;
+ multiview.pNext = &protectedMem;
+ protectedMem.pNext = &ycbcr;
+ ycbcr.pNext = &drawParams;
+ drawParams.pNext = &varPointers;
+
+ renderer->vkGetPhysicalDeviceFeatures2(physicalDevice, &supportedFeatureList);
+
+ // Pack the results into the post-1.2 structure for easier checking
+ VkPhysicalDeviceVulkan11Features vk11Features = { 0 };
+ vk11Features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES;
+ vk11Features.storageBuffer16BitAccess = storage.storageBuffer16BitAccess;
+ vk11Features.uniformAndStorageBuffer16BitAccess = storage.uniformAndStorageBuffer16BitAccess;
+ vk11Features.storagePushConstant16 = storage.storagePushConstant16;
+ vk11Features.storageInputOutput16 = storage.storageInputOutput16;
+ vk11Features.multiview = multiview.multiview;
+ vk11Features.multiviewGeometryShader = multiview.multiviewGeometryShader;
+ vk11Features.multiviewTessellationShader = multiview.multiviewTessellationShader;
+ vk11Features.protectedMemory = protectedMem.protectedMemory;
+ vk11Features.samplerYcbcrConversion = ycbcr.samplerYcbcrConversion;
+ vk11Features.shaderDrawParameters = drawParams.shaderDrawParameters;
+ vk11Features.variablePointers = varPointers.variablePointers;
+ vk11Features.variablePointersStorageBuffer = varPointers.variablePointersStorageBuffer;
+
+ // Check support
+ supportsAllFeatures &= VULKAN_INTERNAL_ValidateOptInVulkan10Features(&renderer->desiredVulkan10DeviceFeatures, vk10Features);
+ supportsAllFeatures &= VULKAN_INTERNAL_ValidateOptInVulkan11Features(&renderer->desiredVulkan11DeviceFeatures, &vk11Features);
+ } else {
+ VkPhysicalDeviceVulkan11Features vk11Features = { 0 };
+ vk11Features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES;
+
+ VkPhysicalDeviceVulkan12Features vk12Features = { 0 };
+ vk12Features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES;
+
+ VkPhysicalDeviceVulkan13Features vk13Features = { 0 };
+ vk13Features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES;
+
+ VkPhysicalDeviceFeatures2 supportedFeatureList = { 0 };
+ supportedFeatureList.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
+ supportedFeatureList.pNext = &vk11Features;
+ vk11Features.pNext = &vk12Features;
+ vk12Features.pNext = &vk13Features;
+
+ renderer->vkGetPhysicalDeviceFeatures2(physicalDevice, &supportedFeatureList);
+
+ supportsAllFeatures &= VULKAN_INTERNAL_ValidateOptInVulkan10Features(&renderer->desiredVulkan10DeviceFeatures, vk10Features);
+ supportsAllFeatures &= VULKAN_INTERNAL_ValidateOptInVulkan11Features(&renderer->desiredVulkan11DeviceFeatures, &vk11Features);
+ supportsAllFeatures &= VULKAN_INTERNAL_ValidateOptInVulkan12Features(&renderer->desiredVulkan12DeviceFeatures, &vk12Features);
+ supportsAllFeatures &= VULKAN_INTERNAL_ValidateOptInVulkan13Features(&renderer->desiredVulkan13DeviceFeatures, &vk13Features);
+ }
+
+ return supportsAllFeatures;
+}
+
+static void VULKAN_INTERNAL_AddDeviceFeatures(VkBool32 *firstFeature, VkBool32 *lastFeature, VkBool32 *firstFeatureToAdd)
+{
+ while (firstFeature <= lastFeature) {
+ *firstFeature = (*firstFeature | *firstFeatureToAdd);
+ firstFeature++;
+ firstFeatureToAdd++;
+ }
+}
+
+static bool VULKAN_INTERNAL_TryAddDeviceFeatures_Vulkan_11(VkPhysicalDeviceFeatures *dst10,
+ VkPhysicalDeviceVulkan11Features *dst11,
+ VkBaseOutStructure *src)
+{
+ bool hasAdded = false;
+ switch (src->sType) {
+ case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2:
+ {
+ VkPhysicalDeviceFeatures2 *newFeatures = (VkPhysicalDeviceFeatures2 *)src;
+ VULKAN_INTERNAL_AddDeviceFeatures(&dst10->robustBufferAccess,
+ &dst10->inheritedQueries,
+ &newFeatures->features.robustBufferAccess);
+ hasAdded = true;
+ } break;
+
+ case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES:
+ {
+ VkPhysicalDevice16BitStorageFeatures *newFeatures = (VkPhysicalDevice16BitStorageFeatures *)src;
+ dst11->storageBuffer16BitAccess |= newFeatures->storageBuffer16BitAccess;
+ dst11->uniformAndStorageBuffer16BitAccess |= newFeatures->uniformAndStorageBuffer16BitAccess;
+ dst11->storagePushConstant16 |= newFeatures->storagePushConstant16;
+ dst11->storageInputOutput16 |= newFeatures->storageInputOutput16;
+ hasAdded = true;
+ } break;
+
+ case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES:
+ {
+ VkPhysicalDeviceMultiviewFeatures *newFeatures = (VkPhysicalDeviceMultiviewFeatures *)src;
+ dst11->multiview |= newFeatures->multiview;
+ dst11->multiviewGeometryShader |= newFeatures->multiviewGeometryShader;
+ dst11->multiviewTessellationShader |= newFeatures->multiviewTessellationShader;
+ hasAdded = true;
+ } break;
+
+ case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_FEATURES:
+ {
+ VkPhysicalDeviceProtectedMemoryFeatures *newFeatures = (VkPhysicalDeviceProtectedMemoryFeatures *)src;
+ dst11->protectedMemory |= newFeatures->protectedMemory;
+ hasAdded = true;
+ } break;
+
+ case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES:
+ {
+ VkPhysicalDeviceSamplerYcbcrConversionFeatures *newFeatures = (VkPhysicalDeviceSamplerYcbcrConversionFeatures *)src;
+ dst11->samplerYcbcrConversion |= newFeatures->samplerYcbcrConversion;
+ hasAdded = true;
+ } break;
+
+ case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DRAW_PARAMETERS_FEATURES:
+ {
+ VkPhysicalDeviceShaderDrawParametersFeatures *newFeatures = (VkPhysicalDeviceShaderDrawParametersFeatures *)src;
+ dst11->shaderDrawParameters |= newFeatures->shaderDrawParameters;
+ hasAdded = true;
+ } break;
+
+ case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTERS_FEATURES:
+ {
+ VkPhysicalDeviceVariablePointersFeatures *newFeatures = (VkPhysicalDeviceVariablePointersFeatures *)src;
+ dst11->variablePointers |= newFeatures->variablePointers;
+ dst11->variablePointersStorageBuffer |= newFeatures->variablePointersStorageBuffer;
+ hasAdded = true;
+ } break;
+
+ default:
+ break;
+ }
+
+ return hasAdded;
+}
+
+static bool VULKAN_INTERNAL_TryAddDeviceFeatures_Vulkan_12_Or_Later(VkPhysicalDeviceFeatures *dst10,
+ VkPhysicalDeviceVulkan11Features *dst11,
+ VkPhysicalDeviceVulkan12Features *dst12,
+ VkPhysicalDeviceVulkan13Features *dst13,
+ Uint32 apiVersion,
+ VkBaseOutStructure *src)
+{
+ int minorVersion = VK_API_VERSION_MINOR(apiVersion);
+ SDL_assert(apiVersion >= 2);
+ bool hasAdded = VULKAN_INTERNAL_TryAddDeviceFeatures_Vulkan_11(dst10, dst11, src);
+ if (!hasAdded) {
+ switch (src->sType) {
+ case VK_STRUCTURE_TYPE_PHYSICAL_DEVI
(Patch may be truncated, please check the link at the top of this post.)