From f286e420afd3ccb0dedc7ec77eb09d4bd2e5e102 Mon Sep 17 00:00:00 2001 From: Evan Hemsley <2342303+thatcosmonaut@users.noreply.github.com> 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 56074c96ad..b7983859d8 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;