diff --git a/include/SDL3/SDL_gpu.h b/include/SDL3/SDL_gpu.h index def7cc7728..c627b268e0 100644 --- a/include/SDL3/SDL_gpu.h +++ b/include/SDL3/SDL_gpu.h @@ -2254,6 +2254,22 @@ extern SDL_DECLSPEC SDL_GPUDevice * SDLCALL SDL_CreateGPUDevice( * - `SDL_PROP_GPU_DEVICE_CREATE_D3D12_SEMANTIC_NAME_STRING`: the prefix to * use for all vertex semantics, default is "TEXCOORD". * + * With the Vulkan renderer: + * + * - `SDL_PROP_GPU_DEVICE_CREATE_VULKAN_SHADERCLIPDISTANCE_BOOL`: Enable device feature + * shaderClipDistance. If disabled, clip distances are not supported in shader code: + * gl_ClipDistance[] built-ins of GLSL, SV_ClipDistance0/1 semantics of HLSL and + * [[clip_distance]] attribute of Metal. Defaults to true. + * - `SDL_PROP_GPU_DEVICE_CREATE_VULKAN_DEPTHCLAMP_BOOL`: Enable device feature + * depthClamp. If disabled, there is no depth clamp support and enable_depth_clip in + * SDL_GPURasterizerState must always be set to true. Defaults to true. + * - `SDL_PROP_GPU_DEVICE_CREATE_VULKAN_DRAWINDIRECTFIRST_BOOL`: Enable device feature + * drawIndirectFirstInstance. If disabled, the argument first_instance of + * SDL_GPUIndirectDrawCommand must be set to zero. Defaults to true. + * - `SDL_PROP_GPU_DEVICE_CREATE_VULKAN_SAMPLERANISOTROPY_BOOL`: Enable device feature + * samplerAnisotropy. If disabled, enable_anisotropy of SDL_GPUSamplerCreateInfo must + * be set to false. Defaults to true. + * * \param props the properties to use. * \returns a GPU context on success or NULL on failure; call SDL_GetError() * for more information. @@ -2268,17 +2284,21 @@ extern SDL_DECLSPEC SDL_GPUDevice * SDLCALL SDL_CreateGPUDevice( extern SDL_DECLSPEC SDL_GPUDevice * SDLCALL SDL_CreateGPUDeviceWithProperties( SDL_PropertiesID props); -#define SDL_PROP_GPU_DEVICE_CREATE_DEBUGMODE_BOOLEAN "SDL.gpu.device.create.debugmode" -#define SDL_PROP_GPU_DEVICE_CREATE_PREFERLOWPOWER_BOOLEAN "SDL.gpu.device.create.preferlowpower" -#define SDL_PROP_GPU_DEVICE_CREATE_VERBOSE_BOOLEAN "SDL.gpu.device.create.verbose" -#define SDL_PROP_GPU_DEVICE_CREATE_NAME_STRING "SDL.gpu.device.create.name" -#define SDL_PROP_GPU_DEVICE_CREATE_SHADERS_PRIVATE_BOOLEAN "SDL.gpu.device.create.shaders.private" -#define SDL_PROP_GPU_DEVICE_CREATE_SHADERS_SPIRV_BOOLEAN "SDL.gpu.device.create.shaders.spirv" -#define SDL_PROP_GPU_DEVICE_CREATE_SHADERS_DXBC_BOOLEAN "SDL.gpu.device.create.shaders.dxbc" -#define SDL_PROP_GPU_DEVICE_CREATE_SHADERS_DXIL_BOOLEAN "SDL.gpu.device.create.shaders.dxil" -#define SDL_PROP_GPU_DEVICE_CREATE_SHADERS_MSL_BOOLEAN "SDL.gpu.device.create.shaders.msl" -#define SDL_PROP_GPU_DEVICE_CREATE_SHADERS_METALLIB_BOOLEAN "SDL.gpu.device.create.shaders.metallib" -#define SDL_PROP_GPU_DEVICE_CREATE_D3D12_SEMANTIC_NAME_STRING "SDL.gpu.device.create.d3d12.semantic" +#define SDL_PROP_GPU_DEVICE_CREATE_DEBUGMODE_BOOLEAN "SDL.gpu.device.create.debugmode" +#define SDL_PROP_GPU_DEVICE_CREATE_PREFERLOWPOWER_BOOLEAN "SDL.gpu.device.create.preferlowpower" +#define SDL_PROP_GPU_DEVICE_CREATE_VERBOSE_BOOLEAN "SDL.gpu.device.create.verbose" +#define SDL_PROP_GPU_DEVICE_CREATE_NAME_STRING "SDL.gpu.device.create.name" +#define SDL_PROP_GPU_DEVICE_CREATE_SHADERS_PRIVATE_BOOLEAN "SDL.gpu.device.create.shaders.private" +#define SDL_PROP_GPU_DEVICE_CREATE_SHADERS_SPIRV_BOOLEAN "SDL.gpu.device.create.shaders.spirv" +#define SDL_PROP_GPU_DEVICE_CREATE_SHADERS_DXBC_BOOLEAN "SDL.gpu.device.create.shaders.dxbc" +#define SDL_PROP_GPU_DEVICE_CREATE_SHADERS_DXIL_BOOLEAN "SDL.gpu.device.create.shaders.dxil" +#define SDL_PROP_GPU_DEVICE_CREATE_SHADERS_MSL_BOOLEAN "SDL.gpu.device.create.shaders.msl" +#define SDL_PROP_GPU_DEVICE_CREATE_SHADERS_METALLIB_BOOLEAN "SDL.gpu.device.create.shaders.metallib" +#define SDL_PROP_GPU_DEVICE_CREATE_D3D12_SEMANTIC_NAME_STRING "SDL.gpu.device.create.d3d12.semantic" +#define SDL_PROP_GPU_DEVICE_CREATE_VULKAN_SHADERCLIPDISTANCE_BOOLEAN "SDL.gpu.device.create.vulkan.shaderclipdistance" +#define SDL_PROP_GPU_DEVICE_CREATE_VULKAN_DEPTHCLAMP_BOOLEAN "SDL.gpu.device.create.vulkan.depthclamp" +#define SDL_PROP_GPU_DEVICE_CREATE_VULKAN_DRAWINDIRECTFIRST_BOOLEAN "SDL.gpu.device.create.vulkan.drawindirectfirstinstance" +#define SDL_PROP_GPU_DEVICE_CREATE_VULKAN_SAMPLERANISOTROPY_BOOLEAN "SDL.gpu.device.create.vulkan.sampleranisotropy" /** * Destroys a GPU context previously returned by SDL_CreateGPUDevice. diff --git a/src/gpu/SDL_gpu.c b/src/gpu/SDL_gpu.c index 54a66e6b0d..ea86aabcaa 100644 --- a/src/gpu/SDL_gpu.c +++ b/src/gpu/SDL_gpu.c @@ -450,7 +450,7 @@ static const SDL_GPUBootstrap * SDL_GPUSelectBackend(SDL_PropertiesID props) SDL_SetError("Required shader format for backend %s not provided!", gpudriver); return NULL; } - if (backends[i]->PrepareDriver(_this)) { + if (backends[i]->PrepareDriver(_this, props)) { return backends[i]; } } @@ -465,7 +465,7 @@ static const SDL_GPUBootstrap * SDL_GPUSelectBackend(SDL_PropertiesID props) // Don't select a backend which doesn't support the app's shaders. continue; } - if (backends[i]->PrepareDriver(_this)) { + if (backends[i]->PrepareDriver(_this, props)) { return backends[i]; } } diff --git a/src/gpu/SDL_sysgpu.h b/src/gpu/SDL_sysgpu.h index ab69d64210..d4f532fde6 100644 --- a/src/gpu/SDL_sysgpu.h +++ b/src/gpu/SDL_sysgpu.h @@ -1142,7 +1142,7 @@ typedef struct SDL_GPUBootstrap { const char *name; const SDL_GPUShaderFormat shader_formats; - bool (*PrepareDriver)(SDL_VideoDevice *_this); + bool (*PrepareDriver)(SDL_VideoDevice *_this, SDL_PropertiesID props); SDL_GPUDevice *(*CreateDevice)(bool debug_mode, bool prefer_low_power, SDL_PropertiesID props); } SDL_GPUBootstrap; diff --git a/src/gpu/d3d12/SDL_gpu_d3d12.c b/src/gpu/d3d12/SDL_gpu_d3d12.c index 8f5d85bb82..2b65775e54 100644 --- a/src/gpu/d3d12/SDL_gpu_d3d12.c +++ b/src/gpu/d3d12/SDL_gpu_d3d12.c @@ -8317,7 +8317,7 @@ static void D3D12_INTERNAL_InitBlitResources( } } -static bool D3D12_PrepareDriver(SDL_VideoDevice *_this) +static bool D3D12_PrepareDriver(SDL_VideoDevice *_this, SDL_PropertiesID props) { #if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES) return true; diff --git a/src/gpu/metal/SDL_gpu_metal.m b/src/gpu/metal/SDL_gpu_metal.m index c8b894f3ba..9450b6a62c 100644 --- a/src/gpu/metal/SDL_gpu_metal.m +++ b/src/gpu/metal/SDL_gpu_metal.m @@ -4307,7 +4307,7 @@ static bool METAL_SupportsTextureFormat( // Device Creation -static bool METAL_PrepareDriver(SDL_VideoDevice *this) +static bool METAL_PrepareDriver(SDL_VideoDevice *this, SDL_PropertiesID props) { if (@available(macOS 10.14, iOS 13.0, tvOS 13.0, *)) { return (this->Metal_CreateView != NULL); diff --git a/src/gpu/vulkan/SDL_gpu_vulkan.c b/src/gpu/vulkan/SDL_gpu_vulkan.c index 449ee33328..2f7bd82b3f 100644 --- a/src/gpu/vulkan/SDL_gpu_vulkan.c +++ b/src/gpu/vulkan/SDL_gpu_vulkan.c @@ -1088,6 +1088,7 @@ struct VulkanRenderer VkPhysicalDevice physicalDevice; VkPhysicalDeviceProperties2KHR physicalDeviceProperties; VkPhysicalDeviceDriverPropertiesKHR physicalDeviceDriverProperties; + VkPhysicalDeviceFeatures desiredDeviceFeatures; VkDevice logicalDevice; Uint8 integratedMemoryNotification; Uint8 outOfDeviceLocalMemoryWarning; @@ -11220,12 +11221,14 @@ static Uint8 VULKAN_INTERNAL_IsDeviceSuitable( renderer->vkGetPhysicalDeviceFeatures( physicalDevice, &deviceFeatures); - if (!deviceFeatures.independentBlend || - !deviceFeatures.imageCubeArray || - !deviceFeatures.depthClamp || - !deviceFeatures.shaderClipDistance || - !deviceFeatures.drawIndirectFirstInstance || - !deviceFeatures.sampleRateShading) { + + if ((!deviceFeatures.independentBlend && renderer->desiredDeviceFeatures.independentBlend) || + (!deviceFeatures.imageCubeArray && renderer->desiredDeviceFeatures.imageCubeArray) || + (!deviceFeatures.depthClamp && renderer->desiredDeviceFeatures.depthClamp) || + (!deviceFeatures.shaderClipDistance && renderer->desiredDeviceFeatures.shaderClipDistance) || + (!deviceFeatures.drawIndirectFirstInstance && renderer->desiredDeviceFeatures.drawIndirectFirstInstance) || + (!deviceFeatures.sampleRateShading && renderer->desiredDeviceFeatures.sampleRateShading) || + (!deviceFeatures.samplerAnisotropy && renderer->desiredDeviceFeatures.samplerAnisotropy)) { return 0; } @@ -11441,7 +11444,6 @@ static Uint8 VULKAN_INTERNAL_CreateLogicalDevice( { VkResult vulkanResult; VkDeviceCreateInfo deviceCreateInfo; - VkPhysicalDeviceFeatures desiredDeviceFeatures; VkPhysicalDeviceFeatures haveDeviceFeatures; VkPhysicalDevicePortabilitySubsetFeaturesKHR portabilityFeatures; const char **deviceExtensions; @@ -11465,22 +11467,13 @@ static Uint8 VULKAN_INTERNAL_CreateLogicalDevice( // specifying used device features - SDL_zero(desiredDeviceFeatures); - desiredDeviceFeatures.independentBlend = VK_TRUE; - desiredDeviceFeatures.samplerAnisotropy = VK_TRUE; - desiredDeviceFeatures.imageCubeArray = VK_TRUE; - desiredDeviceFeatures.depthClamp = VK_TRUE; - desiredDeviceFeatures.shaderClipDistance = VK_TRUE; - desiredDeviceFeatures.drawIndirectFirstInstance = VK_TRUE; - desiredDeviceFeatures.sampleRateShading = VK_TRUE; - if (haveDeviceFeatures.fillModeNonSolid) { - desiredDeviceFeatures.fillModeNonSolid = VK_TRUE; + renderer->desiredDeviceFeatures.fillModeNonSolid = VK_TRUE; renderer->supportsFillModeNonSolid = true; } if (haveDeviceFeatures.multiDrawIndirect) { - desiredDeviceFeatures.multiDrawIndirect = VK_TRUE; + renderer->desiredDeviceFeatures.multiDrawIndirect = VK_TRUE; renderer->supportsMultiDrawIndirect = true; } @@ -11521,7 +11514,7 @@ static Uint8 VULKAN_INTERNAL_CreateLogicalDevice( deviceCreateInfo.enabledExtensionCount); CreateDeviceExtensionArray(&renderer->supports, deviceExtensions); deviceCreateInfo.ppEnabledExtensionNames = deviceExtensions; - deviceCreateInfo.pEnabledFeatures = &desiredDeviceFeatures; + deviceCreateInfo.pEnabledFeatures = &renderer->desiredDeviceFeatures; vulkanResult = renderer->vkCreateDevice( renderer->physicalDevice, @@ -11606,7 +11599,7 @@ static bool VULKAN_INTERNAL_PrepareVulkan( return true; } -static bool VULKAN_PrepareDriver(SDL_VideoDevice *_this) +static bool VULKAN_PrepareDriver(SDL_VideoDevice *_this, SDL_PropertiesID props) { // Set up dummy VulkanRenderer VulkanRenderer *renderer; @@ -11622,6 +11615,17 @@ static bool VULKAN_PrepareDriver(SDL_VideoDevice *_this) renderer = (VulkanRenderer *)SDL_calloc(1, sizeof(*renderer)); if (renderer) { + // Opt out device features (higher compatibility in exchange for reduced functionality) + renderer->desiredDeviceFeatures.samplerAnisotropy = SDL_GetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_VULKAN_SAMPLERANISOTROPY_BOOLEAN, true) ? VK_TRUE : VK_FALSE; + renderer->desiredDeviceFeatures.depthClamp = SDL_GetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_VULKAN_DEPTHCLAMP_BOOLEAN, true) ? VK_TRUE : VK_FALSE; + renderer->desiredDeviceFeatures.shaderClipDistance = SDL_GetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_VULKAN_SHADERCLIPDISTANCE_BOOLEAN, true) ? VK_TRUE : VK_FALSE; + renderer->desiredDeviceFeatures.drawIndirectFirstInstance = SDL_GetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_VULKAN_DRAWINDIRECTFIRST_BOOLEAN, true) ? VK_TRUE : VK_FALSE; + + // These features have near universal support so they are always enabled + renderer->desiredDeviceFeatures.independentBlend = VK_TRUE; + renderer->desiredDeviceFeatures.sampleRateShading = VK_TRUE; + renderer->desiredDeviceFeatures.imageCubeArray = VK_TRUE; + result = VULKAN_INTERNAL_PrepareVulkan(renderer); if (result) { renderer->vkDestroyInstance(renderer->instance, NULL); @@ -11660,6 +11664,17 @@ static SDL_GPUDevice *VULKAN_CreateDevice(bool debugMode, bool preferLowPower, S renderer->preferLowPower = preferLowPower; renderer->allowedFramesInFlight = 2; + // Opt out device features (higher compatibility in exchange for reduced functionality) + renderer->desiredDeviceFeatures.samplerAnisotropy = SDL_GetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_VULKAN_SAMPLERANISOTROPY_BOOLEAN, true) ? VK_TRUE : VK_FALSE; + renderer->desiredDeviceFeatures.depthClamp = SDL_GetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_VULKAN_DEPTHCLAMP_BOOLEAN, true) ? VK_TRUE : VK_FALSE; + renderer->desiredDeviceFeatures.shaderClipDistance = SDL_GetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_VULKAN_SHADERCLIPDISTANCE_BOOLEAN, true) ? VK_TRUE : VK_FALSE; + renderer->desiredDeviceFeatures.drawIndirectFirstInstance = SDL_GetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_VULKAN_DRAWINDIRECTFIRST_BOOLEAN, true) ? VK_TRUE : VK_FALSE; + + // These features have near universal support so they are always enabled + renderer->desiredDeviceFeatures.independentBlend = VK_TRUE; + renderer->desiredDeviceFeatures.sampleRateShading = VK_TRUE; + renderer->desiredDeviceFeatures.imageCubeArray = VK_TRUE; + if (!VULKAN_INTERNAL_PrepareVulkan(renderer)) { SDL_free(renderer); SDL_Vulkan_UnloadLibrary();