From f286e420afd3ccb0dedc7ec77eb09d4bd2e5e102 Mon Sep 17 00:00:00 2001
From: Evan Hemsley <[EMAIL REDACTED]>
Date: Thu, 14 May 2026 17:01:13 -0700
Subject: [PATCH] GPU: Refactor Vulkan barriers to fix defrag segfault (#15593)
---
src/gpu/vulkan/SDL_gpu_vulkan.c | 159 +++++++++++++++++++++-----------
1 file changed, 106 insertions(+), 53 deletions(-)
diff --git a/src/gpu/vulkan/SDL_gpu_vulkan.c b/src/gpu/vulkan/SDL_gpu_vulkan.c
index 56074c96ad7d2..b7983859d8786 100644
--- a/src/gpu/vulkan/SDL_gpu_vulkan.c
+++ b/src/gpu/vulkan/SDL_gpu_vulkan.c
@@ -641,6 +641,11 @@ struct VulkanTexture
VkImageAspectFlags aspectFlags;
Uint32 depth; // used for cleanup only
+ // used to avoid indirection on barriers
+ Uint32 levelCount;
+ Uint32 layerCount;
+ SDL_GPUTextureType type;
+
// FIXME: It'd be nice if we didn't have to have this on the texture...
SDL_GPUTextureUsageFlags usage; // used for defrag transitions only.
@@ -2723,12 +2728,16 @@ static void VULKAN_INTERNAL_BufferMemoryBarrier(
buffer->transitioned = true;
}
-static void VULKAN_INTERNAL_TextureSubresourceMemoryBarrier(
+static void VULKAN_INTERNAL_TextureMemoryBarrier(
VulkanRenderer *renderer,
VulkanCommandBuffer *commandBuffer,
VulkanTextureUsageMode sourceUsageMode,
VulkanTextureUsageMode destinationUsageMode,
- VulkanTextureSubresource *textureSubresource)
+ Uint32 baseLevel,
+ Uint32 levelCount,
+ Uint32 baseLayer,
+ Uint32 layerCount,
+ VulkanTexture *texture)
{
VkPipelineStageFlags srcStages = 0;
VkPipelineStageFlags dstStages = 0;
@@ -2742,21 +2751,12 @@ static void VULKAN_INTERNAL_TextureSubresourceMemoryBarrier(
memoryBarrier.newLayout = VK_IMAGE_LAYOUT_UNDEFINED;
memoryBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
memoryBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
- memoryBarrier.image = textureSubresource->parent->image;
- memoryBarrier.subresourceRange.aspectMask = textureSubresource->parent->aspectFlags;
- memoryBarrier.subresourceRange.baseMipLevel = textureSubresource->level;
- memoryBarrier.subresourceRange.levelCount = 1;
- memoryBarrier.subresourceRange.baseArrayLayer = textureSubresource->layer;
- memoryBarrier.subresourceRange.layerCount = 1;
-
- // VK_KHR_maintenance9 adds the ability to independently transition arbitrary subsets of slices in a 3D texture
- // but otherwise it is not necessarily supported by the driver.
- // As a workaround we have to transition the whole texture instead of just the subresource.
- // If VK_KHR_maintenance9 becomes widely supported, this can be removed.
- // See https://docs.vulkan.org/features/latest/features/proposals/VK_KHR_maintenance9.html#_barriers_with_2d_array_compatible_3d_images
- if (textureSubresource->parent->container->header.info.type == SDL_GPU_TEXTURETYPE_3D) {
- memoryBarrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS;
- }
+ memoryBarrier.image = texture->image;
+ memoryBarrier.subresourceRange.aspectMask = texture->aspectFlags;
+ memoryBarrier.subresourceRange.baseMipLevel = baseLevel;
+ memoryBarrier.subresourceRange.levelCount = levelCount;
+ memoryBarrier.subresourceRange.baseArrayLayer = baseLayer;
+ memoryBarrier.subresourceRange.layerCount = layerCount;
if (sourceUsageMode == VULKAN_TEXTURE_USAGE_MODE_UNINITIALIZED) {
srcStages = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
@@ -2853,6 +2853,56 @@ static void VULKAN_INTERNAL_TextureSubresourceMemoryBarrier(
&memoryBarrier);
}
+// Transitions the entire texture with a single barrier call.
+static void VULKAN_INTERNAL_FullTextureMemoryBarrier(
+ VulkanRenderer *renderer,
+ VulkanCommandBuffer *commandBuffer,
+ VulkanTextureUsageMode sourceUsageMode,
+ VulkanTextureUsageMode destinationUsageMode,
+ VulkanTexture *texture)
+{
+ VULKAN_INTERNAL_TextureMemoryBarrier(
+ renderer,
+ commandBuffer,
+ sourceUsageMode,
+ destinationUsageMode,
+ 0,
+ texture->levelCount,
+ 0,
+ texture->layerCount,
+ texture);
+}
+
+static void VULKAN_INTERNAL_TextureSubresourceMemoryBarrier(
+ VulkanRenderer *renderer,
+ VulkanCommandBuffer *commandBuffer,
+ VulkanTextureUsageMode sourceUsageMode,
+ VulkanTextureUsageMode destinationUsageMode,
+ VulkanTextureSubresource *textureSubresource)
+{
+ Uint32 layerCount = 1;
+
+ // VK_KHR_maintenance9 adds the ability to independently transition arbitrary subsets of slices in a 3D texture
+ // but otherwise it is not necessarily supported by the driver.
+ // As a workaround we have to transition the whole texture instead of just the subresource.
+ // If VK_KHR_maintenance9 becomes widely supported, this can be removed.
+ // See https://docs.vulkan.org/features/latest/features/proposals/VK_KHR_maintenance9.html#_barriers_with_2d_array_compatible_3d_images
+ if (textureSubresource->parent->type == SDL_GPU_TEXTURETYPE_3D) {
+ layerCount = VK_REMAINING_ARRAY_LAYERS;
+ }
+
+ VULKAN_INTERNAL_TextureMemoryBarrier(
+ renderer,
+ commandBuffer,
+ sourceUsageMode,
+ destinationUsageMode,
+ textureSubresource->level,
+ 1,
+ textureSubresource->layer,
+ layerCount,
+ textureSubresource->parent);
+}
+
static VulkanBufferUsageMode VULKAN_INTERNAL_DefaultBufferUsageMode(
VulkanBuffer *buffer)
{
@@ -2950,13 +3000,12 @@ static void VULKAN_INTERNAL_TextureTransitionFromDefaultUsage(
VulkanTextureUsageMode destinationUsageMode,
VulkanTexture *texture)
{
- for (Uint32 i = 0; i < texture->subresourceCount; i += 1) {
- VULKAN_INTERNAL_TextureSubresourceTransitionFromDefaultUsage(
- renderer,
- commandBuffer,
- destinationUsageMode,
- &texture->subresources[i]);
- }
+ VULKAN_INTERNAL_FullTextureMemoryBarrier(
+ renderer,
+ commandBuffer,
+ VULKAN_INTERNAL_DefaultTextureUsageMode(texture),
+ destinationUsageMode,
+ texture);
}
static void VULKAN_INTERNAL_TextureSubresourceTransitionToDefaultUsage(
@@ -2979,14 +3028,12 @@ static void VULKAN_INTERNAL_TextureTransitionToDefaultUsage(
VulkanTextureUsageMode sourceUsageMode,
VulkanTexture *texture)
{
- // FIXME: could optimize this barrier
- for (Uint32 i = 0; i < texture->subresourceCount; i += 1) {
- VULKAN_INTERNAL_TextureSubresourceTransitionToDefaultUsage(
- renderer,
- commandBuffer,
- sourceUsageMode,
- &texture->subresources[i]);
- }
+ VULKAN_INTERNAL_FullTextureMemoryBarrier(
+ renderer,
+ commandBuffer,
+ sourceUsageMode,
+ VULKAN_INTERNAL_DefaultTextureUsageMode(texture),
+ texture);
}
// Resource Disposal
@@ -5738,6 +5785,9 @@ static VulkanTexture *VULKAN_INTERNAL_CreateTexture(
texture->swizzle = SwizzleForSDLFormat(createinfo->format);
texture->depth = depth;
texture->usage = createinfo->usage;
+ texture->levelCount = createinfo->num_levels;
+ texture->layerCount = (createinfo->type == SDL_GPU_TEXTURETYPE_3D) ? 1 : createinfo->layer_count_or_depth;
+ texture->type = createinfo->type;
SDL_SetAtomicInt(&texture->referenceCount, 0);
if (IsDepthFormat(createinfo->format)) {
@@ -6959,7 +7009,9 @@ static SDL_GPUTexture *VULKAN_CreateTexture(
barrierCommandBuffer,
VULKAN_TEXTURE_USAGE_MODE_UNINITIALIZED,
texture);
+
VULKAN_INTERNAL_TrackTexture(barrierCommandBuffer, texture);
+
if (!VULKAN_Submit((SDL_GPUCommandBuffer *)barrierCommandBuffer)) {
VULKAN_ReleaseTexture((SDL_GPURenderer *)renderer, (SDL_GPUTexture *)container);
return NULL;
@@ -8402,7 +8454,6 @@ static void VULKAN_BindComputeStorageTextures(
VULKAN_TEXTURE_USAGE_MODE_COMPUTE_STORAGE_READ,
textureContainer->activeTexture);
-
VULKAN_INTERNAL_TrackTexture(
vulkanCommandBuffer,
textureContainer->activeTexture);
@@ -11119,24 +11170,26 @@ static bool VULKAN_INTERNAL_DefragmentMemory(
}
SDL_GPUTextureCreateInfo info = currentRegion->vulkanTexture->container->header.info;
+
+ VULKAN_INTERNAL_TextureTransitionFromDefaultUsage(
+ renderer,
+ commandBuffer,
+ VULKAN_TEXTURE_USAGE_MODE_COPY_SOURCE,
+ currentRegion->vulkanTexture);
+
+ VULKAN_INTERNAL_FullTextureMemoryBarrier(
+ renderer,
+ commandBuffer,
+ VULKAN_TEXTURE_USAGE_MODE_UNINITIALIZED,
+ VULKAN_TEXTURE_USAGE_MODE_COPY_DESTINATION,
+ newTexture
+ );
+
+ // Can only copy one mip level at a time
for (Uint32 subresourceIndex = 0; subresourceIndex < currentRegion->vulkanTexture->subresourceCount; subresourceIndex += 1) {
- // copy subresource if necessary
VulkanTextureSubresource *srcSubresource = ¤tRegion->vulkanTexture->subresources[subresourceIndex];
VulkanTextureSubresource *dstSubresource = &newTexture->subresources[subresourceIndex];
- VULKAN_INTERNAL_TextureSubresourceTransitionFromDefaultUsage(
- renderer,
- commandBuffer,
- VULKAN_TEXTURE_USAGE_MODE_COPY_SOURCE,
- srcSubresource);
-
- VULKAN_INTERNAL_TextureSubresourceMemoryBarrier(
- renderer,
- commandBuffer,
- VULKAN_TEXTURE_USAGE_MODE_UNINITIALIZED,
- VULKAN_TEXTURE_USAGE_MODE_COPY_DESTINATION,
- dstSubresource);
-
VkImageCopy imageCopy;
imageCopy.srcOffset.x = 0;
imageCopy.srcOffset.y = 0;
@@ -11165,16 +11218,16 @@ static bool VULKAN_INTERNAL_DefragmentMemory(
1,
&imageCopy);
- VULKAN_INTERNAL_TextureSubresourceTransitionToDefaultUsage(
- renderer,
- commandBuffer,
- VULKAN_TEXTURE_USAGE_MODE_COPY_DESTINATION,
- dstSubresource);
-
VULKAN_INTERNAL_TrackTexture(commandBuffer, srcSubresource->parent);
VULKAN_INTERNAL_TrackTexture(commandBuffer, dstSubresource->parent);
}
+ VULKAN_INTERNAL_TextureTransitionToDefaultUsage(
+ renderer,
+ commandBuffer,
+ VULKAN_TEXTURE_USAGE_MODE_COPY_DESTINATION,
+ newTexture);
+
// re-point original container to new texture
newTexture->container = currentRegion->vulkanTexture->container;
newTexture->containerIndex = currentRegion->vulkanTexture->containerIndex;