GPU: Refactor Vulkan barriers to fix defrag segfault (#15593)

This commit is contained in:
Evan Hemsley
2026-05-14 17:01:13 -07:00
committed by GitHub
parent c9bb41bd9c
commit f286e420af

View File

@@ -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 = &currentRegion->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;