From 07be29b625878ff07ef359eacc4282862ddffb2a Mon Sep 17 00:00:00 2001
From: Cameron Cawley <[EMAIL REDACTED]>
Date: Sun, 18 Jan 2026 22:21:35 +0000
Subject: [PATCH] Support 16-bit packed texture formats with the Vulkan
renderer
---
src/render/vulkan/SDL_render_vulkan.c | 126 +++++++++++++++-----------
1 file changed, 71 insertions(+), 55 deletions(-)
diff --git a/src/render/vulkan/SDL_render_vulkan.c b/src/render/vulkan/SDL_render_vulkan.c
index ec2dd4a16c5cf..c20eecd8e651c 100644
--- a/src/render/vulkan/SDL_render_vulkan.c
+++ b/src/render/vulkan/SDL_render_vulkan.c
@@ -134,6 +134,7 @@
VULKAN_INSTANCE_FUNCTION(vkEnumeratePhysicalDevices) \
VULKAN_INSTANCE_FUNCTION(vkGetDeviceProcAddr) \
VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceFeatures) \
+ VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceImageFormatProperties) \
VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceProperties) \
VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceMemoryProperties) \
VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceQueueFamilyProperties) \
@@ -144,7 +145,6 @@
VULKAN_INSTANCE_FUNCTION(vkQueueWaitIdle) \
VULKAN_OPTIONAL_INSTANCE_FUNCTION(vkGetPhysicalDeviceFeatures2KHR) \
VULKAN_OPTIONAL_INSTANCE_FUNCTION(vkGetPhysicalDeviceFormatProperties2KHR) \
- VULKAN_OPTIONAL_INSTANCE_FUNCTION(vkGetPhysicalDeviceImageFormatProperties2KHR) \
VULKAN_OPTIONAL_INSTANCE_FUNCTION(vkGetPhysicalDeviceMemoryProperties2KHR) \
VULKAN_OPTIONAL_INSTANCE_FUNCTION(vkGetPhysicalDeviceProperties2KHR) \
VULKAN_OPTIONAL_DEVICE_FUNCTION(vkCreateSamplerYcbcrConversionKHR) \
@@ -392,22 +392,40 @@ typedef struct
static bool VULKAN_UpdateTextureInternal(VULKAN_RenderData *rendererData, VkImage image, VkFormat format, int plane, int x, int y, int w, int h, const void *pixels, int pitch, VkImageLayout *imageLayout);
+// TODO: Sort this list based on what the Vulkan driver prefers?
+static const struct
+{
+ SDL_PixelFormat sdl;
+ VkFormat unorm;
+ VkFormat srgb;
+} vk_format_map[] = {
+ { SDL_PIXELFORMAT_BGRA32, VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_B8G8R8A8_SRGB }, // SDL_PIXELFORMAT_ARGB8888 on little endian systems
+ { SDL_PIXELFORMAT_RGBA32, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_R8G8B8A8_SRGB },
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+ { SDL_PIXELFORMAT_ABGR8888, VK_FORMAT_A8B8G8R8_UNORM_PACK32, VK_FORMAT_A8B8G8R8_SRGB_PACK32 },
+#endif
+ { SDL_PIXELFORMAT_ABGR2101010, VK_FORMAT_A2B10G10R10_UNORM_PACK32, VK_FORMAT_A2B10G10R10_UNORM_PACK32 },
+ { SDL_PIXELFORMAT_RGBA64_FLOAT, VK_FORMAT_R16G16B16A16_SFLOAT, VK_FORMAT_R16G16B16A16_SFLOAT },
+ { SDL_PIXELFORMAT_RGB565, VK_FORMAT_R5G6B5_UNORM_PACK16, VK_FORMAT_R5G6B5_UNORM_PACK16 },
+ { SDL_PIXELFORMAT_BGR565, VK_FORMAT_B5G6R5_UNORM_PACK16, VK_FORMAT_B5G6R5_UNORM_PACK16 },
+ { SDL_PIXELFORMAT_RGBA5551, VK_FORMAT_R5G5B5A1_UNORM_PACK16, VK_FORMAT_R5G5B5A1_UNORM_PACK16 },
+ { SDL_PIXELFORMAT_BGRA5551, VK_FORMAT_B5G5R5A1_UNORM_PACK16, VK_FORMAT_B5G5R5A1_UNORM_PACK16 },
+ { SDL_PIXELFORMAT_ARGB1555, VK_FORMAT_A1R5G5B5_UNORM_PACK16, VK_FORMAT_A1R5G5B5_UNORM_PACK16 },
+ { SDL_PIXELFORMAT_RGBA4444, VK_FORMAT_R4G4B4A4_UNORM_PACK16, VK_FORMAT_R4G4B4A4_UNORM_PACK16 },
+ { SDL_PIXELFORMAT_BGRA4444, VK_FORMAT_B4G4R4A4_UNORM_PACK16, VK_FORMAT_B4G4R4A4_UNORM_PACK16 },
+ { SDL_PIXELFORMAT_ARGB4444, VK_FORMAT_A4R4G4B4_UNORM_PACK16_EXT, VK_FORMAT_A4R4G4B4_UNORM_PACK16_EXT },
+ { SDL_PIXELFORMAT_ABGR4444, VK_FORMAT_A4B4G4R4_UNORM_PACK16_EXT, VK_FORMAT_A4B4G4R4_UNORM_PACK16_EXT }
+};
+
static SDL_PixelFormat VULKAN_VkFormatToSDLPixelFormat(VkFormat vkFormat)
{
- switch (vkFormat) {
- case VK_FORMAT_R8G8B8A8_UNORM:
- return SDL_PIXELFORMAT_RGBA32;
- case VK_FORMAT_B8G8R8A8_UNORM:
- return SDL_PIXELFORMAT_BGRA32;
- case VK_FORMAT_A8B8G8R8_UNORM_PACK32:
- return SDL_PIXELFORMAT_ABGR8888;
- case VK_FORMAT_A2R10G10B10_UNORM_PACK32:
- return SDL_PIXELFORMAT_ABGR2101010;
- case VK_FORMAT_R16G16B16A16_SFLOAT:
- return SDL_PIXELFORMAT_RGBA64_FLOAT;
- default:
- return SDL_PIXELFORMAT_UNKNOWN;
+ for (int i = 0; i < SDL_arraysize(vk_format_map); i++) {
+ if (vk_format_map[i].unorm == vkFormat ||
+ vk_format_map[i].srgb == vkFormat) {
+ return vk_format_map[i].sdl;
+ }
}
+ return SDL_PIXELFORMAT_UNKNOWN;
}
static int VULKAN_VkFormatGetNumPlanes(VkFormat vkFormat)
@@ -432,16 +450,6 @@ static VkDeviceSize VULKAN_GetBytesPerPixel(VkFormat vkFormat, int plane)
return 2;
case VK_FORMAT_R16G16_UNORM:
return 4;
- case VK_FORMAT_R8G8B8A8_SRGB:
- case VK_FORMAT_R8G8B8A8_UNORM:
- case VK_FORMAT_B8G8R8A8_SRGB:
- case VK_FORMAT_B8G8R8A8_UNORM:
- case VK_FORMAT_A8B8G8R8_SRGB_PACK32:
- case VK_FORMAT_A8B8G8R8_UNORM_PACK32:
- case VK_FORMAT_A2R10G10B10_UNORM_PACK32:
- return 4;
- case VK_FORMAT_R16G16B16A16_SFLOAT:
- return 8;
case VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM:
return 1;
case VK_FORMAT_G8_B8R8_2PLANE_420_UNORM:
@@ -449,34 +457,13 @@ static VkDeviceSize VULKAN_GetBytesPerPixel(VkFormat vkFormat, int plane)
case VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16:
return (plane == 0) ? 2 : 4;
default:
- return 4;
+ return SDL_BYTESPERPIXEL(VULKAN_VkFormatToSDLPixelFormat(vkFormat));
}
}
-static VkFormat SDLPixelFormatToVkTextureFormat(Uint32 format, Uint32 output_colorspace)
+static VkFormat SDLPixelFormatToVkTextureFormat(SDL_PixelFormat format, Uint32 output_colorspace)
{
switch (format) {
- case SDL_PIXELFORMAT_RGBA64_FLOAT:
- return VK_FORMAT_R16G16B16A16_SFLOAT;
- case SDL_PIXELFORMAT_ABGR2101010:
- return VK_FORMAT_A2B10G10R10_UNORM_PACK32;
- case SDL_PIXELFORMAT_RGBA32:
- if (output_colorspace == SDL_COLORSPACE_SRGB_LINEAR) {
- return VK_FORMAT_R8G8B8A8_SRGB;
- }
- return VK_FORMAT_R8G8B8A8_UNORM;
- case SDL_PIXELFORMAT_BGRA32:
- if (output_colorspace == SDL_COLORSPACE_SRGB_LINEAR) {
- return VK_FORMAT_B8G8R8A8_SRGB;
- }
- return VK_FORMAT_B8G8R8A8_UNORM;
-#if SDL_BYTEORDER == SDL_BIG_ENDIAN
- case SDL_PIXELFORMAT_ABGR8888:
- if (output_colorspace == SDL_COLORSPACE_SRGB_LINEAR) {
- return VK_FORMAT_A8B8G8R8_SRGB_PACK32;
- }
- return VK_FORMAT_A8B8G8R8_UNORM_PACK32;
-#endif
case SDL_PIXELFORMAT_INDEX8:
return VK_FORMAT_R8_UNORM;
case SDL_PIXELFORMAT_YUY2:
@@ -492,6 +479,15 @@ static VkFormat SDLPixelFormatToVkTextureFormat(Uint32 format, Uint32 output_col
case SDL_PIXELFORMAT_P010:
return VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16;
default:
+ for (int i = 0; i < SDL_arraysize(vk_format_map); i++) {
+ if (vk_format_map[i].sdl == format) {
+ if (output_colorspace == SDL_COLORSPACE_SRGB_LINEAR) {
+ return vk_format_map[i].srgb;
+ } else {
+ return vk_format_map[i].unorm;
+ }
+ }
+ }
return VK_FORMAT_UNDEFINED;
}
}
@@ -4580,14 +4576,6 @@ static bool VULKAN_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SD
VULKAN_InvalidateCachedState(renderer);
renderer->name = VULKAN_RenderDriver.name;
- SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_BGRA32); // SDL_PIXELFORMAT_ARGB8888 on little endian systems
- SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_RGBA32);
-#if SDL_BYTEORDER == SDL_BIG_ENDIAN
- SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_ABGR8888);
-#endif
- SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_ABGR2101010);
- SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_RGBA64_FLOAT);
- SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_INDEX8);
SDL_SetNumberProperty(SDL_GetRendererProperties(renderer), SDL_PROP_RENDERER_MAX_TEXTURE_SIZE_NUMBER, 16384);
/* HACK: make sure the SDL_Renderer references the SDL_Window data now, in
@@ -4604,6 +4592,34 @@ static bool VULKAN_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SD
return false;
}
+ for (int i = 0; i < SDL_arraysize(vk_format_map); i++) {
+ VkImageFormatProperties properties;
+
+ if (vkGetPhysicalDeviceImageFormatProperties(rendererData->physicalDevice,
+ vk_format_map[i].unorm,
+ VK_IMAGE_TYPE_2D,
+ VK_IMAGE_TILING_OPTIMAL,
+ VK_IMAGE_USAGE_SAMPLED_BIT,
+ 0,
+ &properties) != VK_SUCCESS) {
+ continue;
+ }
+
+ if (vkGetPhysicalDeviceImageFormatProperties(rendererData->physicalDevice,
+ vk_format_map[i].srgb,
+ VK_IMAGE_TYPE_2D,
+ VK_IMAGE_TILING_OPTIMAL,
+ VK_IMAGE_USAGE_SAMPLED_BIT,
+ 0,
+ &properties) != VK_SUCCESS) {
+ continue;
+ }
+
+ SDL_AddSupportedTextureFormat(renderer, vk_format_map[i].sdl);
+ }
+
+ SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_INDEX8);
+
#ifdef SDL_HAVE_YUV
if (rendererData->supportsKHRSamplerYCbCrConversion) {
SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_YV12);