Przeglądaj źródła

GPU: Refcount Vulkan allocations to fix transfer corruption on defrag (#15127)

Evan Hemsley 4 dni temu
rodzic
commit
96c4104523
1 zmienionych plików z 149 dodań i 14 usunięć
  1. 149 14
      src/gpu/vulkan/SDL_gpu_vulkan.c

+ 149 - 14
src/gpu/vulkan/SDL_gpu_vulkan.c

@@ -500,6 +500,7 @@ struct VulkanMemoryAllocation
     VkDeviceSize usedSpace;
     VkDeviceSize usedSpace;
     Uint8 *mapPointer;
     Uint8 *mapPointer;
     SDL_Mutex *memoryLock;
     SDL_Mutex *memoryLock;
+    SDL_AtomicInt referenceCount; // Used to avoid defrag races
 };
 };
 
 
 typedef struct VulkanMemoryAllocator
 typedef struct VulkanMemoryAllocator
@@ -1040,10 +1041,18 @@ typedef struct VulkanCommandBuffer
     Sint32 usedBufferCount;
     Sint32 usedBufferCount;
     Sint32 usedBufferCapacity;
     Sint32 usedBufferCapacity;
 
 
+    VulkanBuffer **buffersUsedInPendingTransfers;
+    Sint32 buffersUsedInPendingTransfersCount;
+    Sint32 buffersUsedInPendingTransfersCapacity;
+
     VulkanTexture **usedTextures;
     VulkanTexture **usedTextures;
     Sint32 usedTextureCount;
     Sint32 usedTextureCount;
     Sint32 usedTextureCapacity;
     Sint32 usedTextureCapacity;
 
 
+    VulkanTexture **texturesUsedInPendingTransfers;
+    Sint32 texturesUsedInPendingTransfersCount;
+    Sint32 texturesUsedInPendingTransfersCapacity;
+
     VulkanSampler **usedSamplers;
     VulkanSampler **usedSamplers;
     Sint32 usedSamplerCount;
     Sint32 usedSamplerCount;
     Sint32 usedSamplerCapacity;
     Sint32 usedSamplerCapacity;
@@ -1205,6 +1214,10 @@ struct VulkanRenderer
     SDL_Mutex *descriptorSetLayoutFetchLock;
     SDL_Mutex *descriptorSetLayoutFetchLock;
     SDL_Mutex *windowLock;
     SDL_Mutex *windowLock;
 
 
+    // We don't want transfer commands to block each other,
+    // but we want all transfers to block during defrag.
+    SDL_RWLock *defragLock;
+
     Uint8 defragInProgress;
     Uint8 defragInProgress;
 
 
     VulkanMemoryAllocation **allocationsToDefrag;
     VulkanMemoryAllocation **allocationsToDefrag;
@@ -1836,6 +1849,8 @@ static Uint8 VULKAN_INTERNAL_AllocateMemory(
     allocation->freeRegionCount = 0;
     allocation->freeRegionCount = 0;
     allocation->freeRegionCapacity = 1;
     allocation->freeRegionCapacity = 1;
 
 
+    SDL_SetAtomicInt(&allocation->referenceCount, 0);
+
     allocation->allocator = allocator;
     allocation->allocator = allocator;
 
 
     result = renderer->vkAllocateMemory(
     result = renderer->vkAllocateMemory(
@@ -2373,7 +2388,7 @@ static Uint8 VULKAN_INTERNAL_BindMemoryForBuffer(
 
 
 // Resource tracking
 // Resource tracking
 
 
-#define TRACK_RESOURCE(resource, type, array, count, capacity)  \
+#define TRACK_RESOURCE(resource, type, array, count, capacity, refcountvar)  \
     for (Sint32 i = commandBuffer->count - 1; i >= 0; i -= 1) { \
     for (Sint32 i = commandBuffer->count - 1; i >= 0; i -= 1) { \
         if (commandBuffer->array[i] == resource) {              \
         if (commandBuffer->array[i] == resource) {              \
             return;                                             \
             return;                                             \
@@ -2388,7 +2403,8 @@ static Uint8 VULKAN_INTERNAL_BindMemoryForBuffer(
     }                                                           \
     }                                                           \
     commandBuffer->array[commandBuffer->count] = resource;      \
     commandBuffer->array[commandBuffer->count] = resource;      \
     commandBuffer->count += 1;                                  \
     commandBuffer->count += 1;                                  \
-    SDL_AtomicIncRef(&resource->referenceCount);
+    SDL_AtomicIncRef(&refcountvar)
+
 
 
 static void VULKAN_INTERNAL_TrackBuffer(
 static void VULKAN_INTERNAL_TrackBuffer(
     VulkanCommandBuffer *commandBuffer,
     VulkanCommandBuffer *commandBuffer,
@@ -2399,7 +2415,23 @@ static void VULKAN_INTERNAL_TrackBuffer(
         VulkanBuffer *,
         VulkanBuffer *,
         usedBuffers,
         usedBuffers,
         usedBufferCount,
         usedBufferCount,
-        usedBufferCapacity)
+        usedBufferCapacity,
+        buffer->referenceCount);
+}
+
+// Use this function when a GPU buffer is part of a transfer operation.
+// Note that this isn't for transfer buffers, those don't need to refcount their allocations.
+static void VULKAN_INTERNAL_TrackBufferTransfer(
+    VulkanCommandBuffer *commandBuffer,
+    VulkanBuffer *buffer)
+{
+    TRACK_RESOURCE(
+        buffer,
+        VulkanBuffer *,
+        buffersUsedInPendingTransfers,
+        buffersUsedInPendingTransfersCount,
+        buffersUsedInPendingTransfersCapacity,
+        buffer->usedRegion->allocation->referenceCount);
 }
 }
 
 
 static void VULKAN_INTERNAL_TrackTexture(
 static void VULKAN_INTERNAL_TrackTexture(
@@ -2411,7 +2443,22 @@ static void VULKAN_INTERNAL_TrackTexture(
         VulkanTexture *,
         VulkanTexture *,
         usedTextures,
         usedTextures,
         usedTextureCount,
         usedTextureCount,
-        usedTextureCapacity)
+        usedTextureCapacity,
+        texture->referenceCount);
+}
+
+// Use this when a texture is part of a transfer operation.
+static void VULKAN_INTERNAL_TrackTextureTransfer(
+    VulkanCommandBuffer *commandBuffer,
+    VulkanTexture *texture)
+{
+    TRACK_RESOURCE(
+        texture,
+        VulkanTexture *,
+        texturesUsedInPendingTransfers,
+        texturesUsedInPendingTransfersCount,
+        texturesUsedInPendingTransfersCapacity,
+        texture->usedRegion->allocation->referenceCount);
 }
 }
 
 
 static void VULKAN_INTERNAL_TrackSampler(
 static void VULKAN_INTERNAL_TrackSampler(
@@ -2423,7 +2470,8 @@ static void VULKAN_INTERNAL_TrackSampler(
         VulkanSampler *,
         VulkanSampler *,
         usedSamplers,
         usedSamplers,
         usedSamplerCount,
         usedSamplerCount,
-        usedSamplerCapacity)
+        usedSamplerCapacity,
+        sampler->referenceCount);
 }
 }
 
 
 static void VULKAN_INTERNAL_TrackGraphicsPipeline(
 static void VULKAN_INTERNAL_TrackGraphicsPipeline(
@@ -2435,7 +2483,8 @@ static void VULKAN_INTERNAL_TrackGraphicsPipeline(
         VulkanGraphicsPipeline *,
         VulkanGraphicsPipeline *,
         usedGraphicsPipelines,
         usedGraphicsPipelines,
         usedGraphicsPipelineCount,
         usedGraphicsPipelineCount,
-        usedGraphicsPipelineCapacity)
+        usedGraphicsPipelineCapacity,
+        graphicsPipeline->referenceCount);
 }
 }
 
 
 static void VULKAN_INTERNAL_TrackComputePipeline(
 static void VULKAN_INTERNAL_TrackComputePipeline(
@@ -2447,7 +2496,8 @@ static void VULKAN_INTERNAL_TrackComputePipeline(
         VulkanComputePipeline *,
         VulkanComputePipeline *,
         usedComputePipelines,
         usedComputePipelines,
         usedComputePipelineCount,
         usedComputePipelineCount,
-        usedComputePipelineCapacity)
+        usedComputePipelineCapacity,
+        computePipeline->referenceCount);
 }
 }
 
 
 static void VULKAN_INTERNAL_TrackFramebuffer(
 static void VULKAN_INTERNAL_TrackFramebuffer(
@@ -2459,7 +2509,8 @@ static void VULKAN_INTERNAL_TrackFramebuffer(
         VulkanFramebuffer *,
         VulkanFramebuffer *,
         usedFramebuffers,
         usedFramebuffers,
         usedFramebufferCount,
         usedFramebufferCount,
-        usedFramebufferCapacity);
+        usedFramebufferCapacity,
+        framebuffer->referenceCount);
 }
 }
 
 
 static void VULKAN_INTERNAL_TrackUniformBuffer(
 static void VULKAN_INTERNAL_TrackUniformBuffer(
@@ -6844,7 +6895,7 @@ static SDL_GPUTexture *VULKAN_CreateTexture(
             texture);
             texture);
         VULKAN_INTERNAL_TrackTexture(barrierCommandBuffer, texture);
         VULKAN_INTERNAL_TrackTexture(barrierCommandBuffer, texture);
         if (!VULKAN_Submit((SDL_GPUCommandBuffer *)barrierCommandBuffer)) {
         if (!VULKAN_Submit((SDL_GPUCommandBuffer *)barrierCommandBuffer)) {
-            VULKAN_ReleaseTexture((SDL_GPURenderer *)renderer, (SDL_GPUTexture *)container);  
+            VULKAN_ReleaseTexture((SDL_GPURenderer *)renderer, (SDL_GPUTexture *)container);
             return NULL;
             return NULL;
         }
         }
     }
     }
@@ -8742,8 +8793,9 @@ static void VULKAN_UploadToTexture(
     VulkanTextureSubresource *vulkanTextureSubresource;
     VulkanTextureSubresource *vulkanTextureSubresource;
     VkBufferImageCopy imageCopy;
     VkBufferImageCopy imageCopy;
 
 
-    // Note that the transfer buffer does not need a barrier, as it is synced by the client
+    SDL_LockRWLockForReading(renderer->defragLock);
 
 
+    // Note that the transfer buffer does not need a barrier, as it is synced by the client
     vulkanTextureSubresource = VULKAN_INTERNAL_PrepareTextureSubresourceForWrite(
     vulkanTextureSubresource = VULKAN_INTERNAL_PrepareTextureSubresourceForWrite(
         renderer,
         renderer,
         vulkanCommandBuffer,
         vulkanCommandBuffer,
@@ -8783,6 +8835,9 @@ static void VULKAN_UploadToTexture(
 
 
     VULKAN_INTERNAL_TrackBuffer(vulkanCommandBuffer, transferBufferContainer->activeBuffer);
     VULKAN_INTERNAL_TrackBuffer(vulkanCommandBuffer, transferBufferContainer->activeBuffer);
     VULKAN_INTERNAL_TrackTexture(vulkanCommandBuffer, vulkanTextureSubresource->parent);
     VULKAN_INTERNAL_TrackTexture(vulkanCommandBuffer, vulkanTextureSubresource->parent);
+    VULKAN_INTERNAL_TrackTextureTransfer(vulkanCommandBuffer, vulkanTextureSubresource->parent);
+
+    SDL_UnlockRWLock(renderer->defragLock);
 }
 }
 
 
 static void VULKAN_UploadToBuffer(
 static void VULKAN_UploadToBuffer(
@@ -8797,8 +8852,9 @@ static void VULKAN_UploadToBuffer(
     VulkanBufferContainer *bufferContainer = (VulkanBufferContainer *)destination->buffer;
     VulkanBufferContainer *bufferContainer = (VulkanBufferContainer *)destination->buffer;
     VkBufferCopy bufferCopy;
     VkBufferCopy bufferCopy;
 
 
-    // Note that the transfer buffer does not need a barrier, as it is synced by the client
+    SDL_LockRWLockForReading(renderer->defragLock);
 
 
+    // Note that the transfer buffer does not need a barrier, as it is synced by the client
     VulkanBuffer *vulkanBuffer = VULKAN_INTERNAL_PrepareBufferForWrite(
     VulkanBuffer *vulkanBuffer = VULKAN_INTERNAL_PrepareBufferForWrite(
         renderer,
         renderer,
         vulkanCommandBuffer,
         vulkanCommandBuffer,
@@ -8806,6 +8862,7 @@ static void VULKAN_UploadToBuffer(
         cycle,
         cycle,
         VULKAN_BUFFER_USAGE_MODE_COPY_DESTINATION);
         VULKAN_BUFFER_USAGE_MODE_COPY_DESTINATION);
 
 
+
     bufferCopy.srcOffset = source->offset;
     bufferCopy.srcOffset = source->offset;
     bufferCopy.dstOffset = destination->offset;
     bufferCopy.dstOffset = destination->offset;
     bufferCopy.size = destination->size;
     bufferCopy.size = destination->size;
@@ -8825,6 +8882,9 @@ static void VULKAN_UploadToBuffer(
 
 
     VULKAN_INTERNAL_TrackBuffer(vulkanCommandBuffer, transferBufferContainer->activeBuffer);
     VULKAN_INTERNAL_TrackBuffer(vulkanCommandBuffer, transferBufferContainer->activeBuffer);
     VULKAN_INTERNAL_TrackBuffer(vulkanCommandBuffer, vulkanBuffer);
     VULKAN_INTERNAL_TrackBuffer(vulkanCommandBuffer, vulkanBuffer);
+    VULKAN_INTERNAL_TrackBufferTransfer(vulkanCommandBuffer, vulkanBuffer);
+
+    SDL_UnlockRWLock(renderer->defragLock);
 }
 }
 
 
 // Readback
 // Readback
@@ -8840,6 +8900,9 @@ static void VULKAN_DownloadFromTexture(
     VulkanTextureSubresource *vulkanTextureSubresource;
     VulkanTextureSubresource *vulkanTextureSubresource;
     VulkanBufferContainer *transferBufferContainer = (VulkanBufferContainer *)destination->transfer_buffer;
     VulkanBufferContainer *transferBufferContainer = (VulkanBufferContainer *)destination->transfer_buffer;
     VkBufferImageCopy imageCopy;
     VkBufferImageCopy imageCopy;
+
+    SDL_LockRWLockForReading(renderer->defragLock);
+
     vulkanTextureSubresource = VULKAN_INTERNAL_FetchTextureSubresource(
     vulkanTextureSubresource = VULKAN_INTERNAL_FetchTextureSubresource(
         textureContainer,
         textureContainer,
         source->layer,
         source->layer,
@@ -8883,6 +8946,9 @@ static void VULKAN_DownloadFromTexture(
 
 
     VULKAN_INTERNAL_TrackBuffer(vulkanCommandBuffer, transferBufferContainer->activeBuffer);
     VULKAN_INTERNAL_TrackBuffer(vulkanCommandBuffer, transferBufferContainer->activeBuffer);
     VULKAN_INTERNAL_TrackTexture(vulkanCommandBuffer, vulkanTextureSubresource->parent);
     VULKAN_INTERNAL_TrackTexture(vulkanCommandBuffer, vulkanTextureSubresource->parent);
+    VULKAN_INTERNAL_TrackTextureTransfer(vulkanCommandBuffer, vulkanTextureSubresource->parent);
+
+    SDL_UnlockRWLock(renderer->defragLock);
 }
 }
 
 
 static void VULKAN_DownloadFromBuffer(
 static void VULKAN_DownloadFromBuffer(
@@ -8896,8 +8962,9 @@ static void VULKAN_DownloadFromBuffer(
     VulkanBufferContainer *transferBufferContainer = (VulkanBufferContainer *)destination->transfer_buffer;
     VulkanBufferContainer *transferBufferContainer = (VulkanBufferContainer *)destination->transfer_buffer;
     VkBufferCopy bufferCopy;
     VkBufferCopy bufferCopy;
 
 
-    // Note that transfer buffer does not need a barrier, as it is synced by the client
+    SDL_LockRWLockForReading(renderer->defragLock);
 
 
+    // Note that transfer buffer does not need a barrier, as it is synced by the client
     VULKAN_INTERNAL_BufferTransitionFromDefaultUsage(
     VULKAN_INTERNAL_BufferTransitionFromDefaultUsage(
         renderer,
         renderer,
         vulkanCommandBuffer,
         vulkanCommandBuffer,
@@ -8923,6 +8990,9 @@ static void VULKAN_DownloadFromBuffer(
 
 
     VULKAN_INTERNAL_TrackBuffer(vulkanCommandBuffer, transferBufferContainer->activeBuffer);
     VULKAN_INTERNAL_TrackBuffer(vulkanCommandBuffer, transferBufferContainer->activeBuffer);
     VULKAN_INTERNAL_TrackBuffer(vulkanCommandBuffer, bufferContainer->activeBuffer);
     VULKAN_INTERNAL_TrackBuffer(vulkanCommandBuffer, bufferContainer->activeBuffer);
+    VULKAN_INTERNAL_TrackBufferTransfer(vulkanCommandBuffer, bufferContainer->activeBuffer);
+
+    SDL_UnlockRWLock(renderer->defragLock);
 }
 }
 
 
 static void VULKAN_CopyTextureToTexture(
 static void VULKAN_CopyTextureToTexture(
@@ -8940,6 +9010,8 @@ static void VULKAN_CopyTextureToTexture(
     VulkanTextureSubresource *dstSubresource;
     VulkanTextureSubresource *dstSubresource;
     VkImageCopy imageCopy;
     VkImageCopy imageCopy;
 
 
+    SDL_LockRWLockForReading(renderer->defragLock);
+
     srcSubresource = VULKAN_INTERNAL_FetchTextureSubresource(
     srcSubresource = VULKAN_INTERNAL_FetchTextureSubresource(
         (VulkanTextureContainer *)source->texture,
         (VulkanTextureContainer *)source->texture,
         source->layer,
         source->layer,
@@ -9001,6 +9073,10 @@ static void VULKAN_CopyTextureToTexture(
 
 
     VULKAN_INTERNAL_TrackTexture(vulkanCommandBuffer, srcSubresource->parent);
     VULKAN_INTERNAL_TrackTexture(vulkanCommandBuffer, srcSubresource->parent);
     VULKAN_INTERNAL_TrackTexture(vulkanCommandBuffer, dstSubresource->parent);
     VULKAN_INTERNAL_TrackTexture(vulkanCommandBuffer, dstSubresource->parent);
+    VULKAN_INTERNAL_TrackTextureTransfer(vulkanCommandBuffer, srcSubresource->parent);
+    VULKAN_INTERNAL_TrackTextureTransfer(vulkanCommandBuffer, dstSubresource->parent);
+
+    SDL_UnlockRWLock(renderer->defragLock);
 }
 }
 
 
 static void VULKAN_CopyBufferToBuffer(
 static void VULKAN_CopyBufferToBuffer(
@@ -9016,6 +9092,8 @@ static void VULKAN_CopyBufferToBuffer(
     VulkanBufferContainer *dstContainer = (VulkanBufferContainer *)destination->buffer;
     VulkanBufferContainer *dstContainer = (VulkanBufferContainer *)destination->buffer;
     VkBufferCopy bufferCopy;
     VkBufferCopy bufferCopy;
 
 
+    SDL_LockRWLockForReading(renderer->defragLock);
+
     VulkanBuffer *dstBuffer = VULKAN_INTERNAL_PrepareBufferForWrite(
     VulkanBuffer *dstBuffer = VULKAN_INTERNAL_PrepareBufferForWrite(
         renderer,
         renderer,
         vulkanCommandBuffer,
         vulkanCommandBuffer,
@@ -9054,6 +9132,10 @@ static void VULKAN_CopyBufferToBuffer(
 
 
     VULKAN_INTERNAL_TrackBuffer(vulkanCommandBuffer, srcContainer->activeBuffer);
     VULKAN_INTERNAL_TrackBuffer(vulkanCommandBuffer, srcContainer->activeBuffer);
     VULKAN_INTERNAL_TrackBuffer(vulkanCommandBuffer, dstBuffer);
     VULKAN_INTERNAL_TrackBuffer(vulkanCommandBuffer, dstBuffer);
+    VULKAN_INTERNAL_TrackBufferTransfer(vulkanCommandBuffer, srcContainer->activeBuffer);
+    VULKAN_INTERNAL_TrackBufferTransfer(vulkanCommandBuffer, dstBuffer);
+
+    SDL_UnlockRWLock(renderer->defragLock);
 }
 }
 
 
 static void VULKAN_GenerateMipmaps(
 static void VULKAN_GenerateMipmaps(
@@ -9067,8 +9149,10 @@ static void VULKAN_GenerateMipmaps(
     VulkanTextureSubresource *dstTextureSubresource;
     VulkanTextureSubresource *dstTextureSubresource;
     VkImageBlit blit;
     VkImageBlit blit;
 
 
+    SDL_LockRWLockForReading(renderer->defragLock);
+
     // Blit each slice sequentially. Barriers, barriers everywhere!
     // Blit each slice sequentially. Barriers, barriers everywhere!
-    for (Uint32 layerOrDepthIndex = 0; layerOrDepthIndex < container->header.info.layer_count_or_depth; layerOrDepthIndex += 1)
+    for (Uint32 layerOrDepthIndex = 0; layerOrDepthIndex < container->header.info.layer_count_or_depth; layerOrDepthIndex += 1) {
         for (Uint32 level = 1; level < container->header.info.num_levels; level += 1) {
         for (Uint32 level = 1; level < container->header.info.num_levels; level += 1) {
             Uint32 layer = container->header.info.type == SDL_GPU_TEXTURETYPE_3D ? 0 : layerOrDepthIndex;
             Uint32 layer = container->header.info.type == SDL_GPU_TEXTURETYPE_3D ? 0 : layerOrDepthIndex;
             Uint32 depth = container->header.info.type == SDL_GPU_TEXTURETYPE_3D ? layerOrDepthIndex : 0;
             Uint32 depth = container->header.info.type == SDL_GPU_TEXTURETYPE_3D ? layerOrDepthIndex : 0;
@@ -9147,7 +9231,13 @@ static void VULKAN_GenerateMipmaps(
 
 
             VULKAN_INTERNAL_TrackTexture(vulkanCommandBuffer, srcTextureSubresource->parent);
             VULKAN_INTERNAL_TrackTexture(vulkanCommandBuffer, srcTextureSubresource->parent);
             VULKAN_INTERNAL_TrackTexture(vulkanCommandBuffer, dstTextureSubresource->parent);
             VULKAN_INTERNAL_TrackTexture(vulkanCommandBuffer, dstTextureSubresource->parent);
+            VULKAN_INTERNAL_TrackTextureTransfer(vulkanCommandBuffer, srcTextureSubresource->parent);
+            VULKAN_INTERNAL_TrackTextureTransfer(vulkanCommandBuffer, dstTextureSubresource->parent);
+
         }
         }
+    }
+
+    SDL_UnlockRWLock(renderer->defragLock);
 }
 }
 
 
 static void VULKAN_EndCopyPass(
 static void VULKAN_EndCopyPass(
@@ -9357,11 +9447,21 @@ static bool VULKAN_INTERNAL_AllocateCommandBuffer(
     commandBuffer->usedBuffers = SDL_malloc(
     commandBuffer->usedBuffers = SDL_malloc(
         commandBuffer->usedBufferCapacity * sizeof(VulkanBuffer *));
         commandBuffer->usedBufferCapacity * sizeof(VulkanBuffer *));
 
 
+    commandBuffer->buffersUsedInPendingTransfersCapacity = 4;
+    commandBuffer->buffersUsedInPendingTransfersCount = 0;
+    commandBuffer->buffersUsedInPendingTransfers = SDL_malloc(
+        commandBuffer->buffersUsedInPendingTransfersCapacity * sizeof(VulkanBuffer *));
+
     commandBuffer->usedTextureCapacity = 4;
     commandBuffer->usedTextureCapacity = 4;
     commandBuffer->usedTextureCount = 0;
     commandBuffer->usedTextureCount = 0;
     commandBuffer->usedTextures = SDL_malloc(
     commandBuffer->usedTextures = SDL_malloc(
         commandBuffer->usedTextureCapacity * sizeof(VulkanTexture *));
         commandBuffer->usedTextureCapacity * sizeof(VulkanTexture *));
 
 
+    commandBuffer->texturesUsedInPendingTransfersCapacity = 4;
+    commandBuffer->texturesUsedInPendingTransfersCount = 0;
+    commandBuffer->texturesUsedInPendingTransfers = SDL_malloc(
+        commandBuffer->texturesUsedInPendingTransfersCapacity * sizeof(VulkanTexture *));
+
     commandBuffer->usedSamplerCapacity = 4;
     commandBuffer->usedSamplerCapacity = 4;
     commandBuffer->usedSamplerCount = 0;
     commandBuffer->usedSamplerCount = 0;
     commandBuffer->usedSamplers = SDL_malloc(
     commandBuffer->usedSamplers = SDL_malloc(
@@ -10430,11 +10530,21 @@ static void VULKAN_INTERNAL_CleanCommandBuffer(
     }
     }
     commandBuffer->usedBufferCount = 0;
     commandBuffer->usedBufferCount = 0;
 
 
+    for (Sint32 i = 0; i < commandBuffer->buffersUsedInPendingTransfersCount; i += 1) {
+        (void)SDL_AtomicDecRef(&commandBuffer->usedBuffers[i]->usedRegion->allocation->referenceCount);
+    }
+    commandBuffer->buffersUsedInPendingTransfersCount = 0;
+
     for (Sint32 i = 0; i < commandBuffer->usedTextureCount; i += 1) {
     for (Sint32 i = 0; i < commandBuffer->usedTextureCount; i += 1) {
         (void)SDL_AtomicDecRef(&commandBuffer->usedTextures[i]->referenceCount);
         (void)SDL_AtomicDecRef(&commandBuffer->usedTextures[i]->referenceCount);
     }
     }
     commandBuffer->usedTextureCount = 0;
     commandBuffer->usedTextureCount = 0;
 
 
+    for (Sint32 i = 0; i < commandBuffer->texturesUsedInPendingTransfersCount; i += 1){
+        (void)SDL_AtomicDecRef(&commandBuffer->usedTextures[i]->usedRegion->allocation->referenceCount);
+    }
+    commandBuffer->texturesUsedInPendingTransfersCount = 0;
+
     for (Sint32 i = 0; i < commandBuffer->usedSamplerCount; i += 1) {
     for (Sint32 i = 0; i < commandBuffer->usedSamplerCount; i += 1) {
         (void)SDL_AtomicDecRef(&commandBuffer->usedSamplers[i]->referenceCount);
         (void)SDL_AtomicDecRef(&commandBuffer->usedSamplers[i]->referenceCount);
     }
     }
@@ -10820,8 +10930,30 @@ static bool VULKAN_INTERNAL_DefragmentMemory(
     commandBuffer->isDefrag = 1;
     commandBuffer->isDefrag = 1;
 
 
     SDL_LockMutex(renderer->allocatorLock);
     SDL_LockMutex(renderer->allocatorLock);
+    SDL_LockRWLockForWriting(renderer->defragLock);
+
+    // Find an allocation that doesn't have any pending transfer operations
+    Sint32 indexToDefrag = -1;
+    for (Sint32 i = renderer->allocationsToDefragCount - 1; i >= 0; i -= 1) {
+        if (SDL_GetAtomicInt(&renderer->allocationsToDefrag[i]->referenceCount) == 0) {
+            indexToDefrag = i;
+            break;
+        }
+    }
 
 
-    VulkanMemoryAllocation *allocation = renderer->allocationsToDefrag[renderer->allocationsToDefragCount - 1];
+    if (indexToDefrag == -1) {
+        // Nothing is available to defrag, but it's not an error
+        SDL_UnlockRWLock(renderer->defragLock);
+        SDL_UnlockMutex(renderer->allocatorLock);
+        return true;
+    }
+
+    VulkanMemoryAllocation *allocation = renderer->allocationsToDefrag[indexToDefrag];
+
+    // Plug the hole
+    if ((Uint32) indexToDefrag != renderer->allocationsToDefragCount - 1) {
+        renderer->allocationsToDefrag[indexToDefrag] = renderer->allocationsToDefrag[renderer->allocationsToDefragCount - 1];
+    }
     renderer->allocationsToDefragCount -= 1;
     renderer->allocationsToDefragCount -= 1;
 
 
     /* For each used region in the allocation
     /* For each used region in the allocation
@@ -10841,6 +10973,7 @@ static bool VULKAN_INTERNAL_DefragmentMemory(
                 currentRegion->vulkanBuffer->container != NULL ? currentRegion->vulkanBuffer->container->debugName : NULL);
                 currentRegion->vulkanBuffer->container != NULL ? currentRegion->vulkanBuffer->container->debugName : NULL);
 
 
             if (newBuffer == NULL) {
             if (newBuffer == NULL) {
+                SDL_UnlockRWLock(renderer->defragLock);
                 SDL_UnlockMutex(renderer->allocatorLock);
                 SDL_UnlockMutex(renderer->allocatorLock);
                 SDL_LogError(SDL_LOG_CATEGORY_GPU, "%s", "Failed to allocate defrag buffer!");
                 SDL_LogError(SDL_LOG_CATEGORY_GPU, "%s", "Failed to allocate defrag buffer!");
                 return false;
                 return false;
@@ -10906,6 +11039,7 @@ static bool VULKAN_INTERNAL_DefragmentMemory(
                 &currentRegion->vulkanTexture->container->header.info);
                 &currentRegion->vulkanTexture->container->header.info);
 
 
             if (newTexture == NULL) {
             if (newTexture == NULL) {
+                SDL_UnlockRWLock(renderer->defragLock);
                 SDL_UnlockMutex(renderer->allocatorLock);
                 SDL_UnlockMutex(renderer->allocatorLock);
                 SDL_LogError(SDL_LOG_CATEGORY_GPU, "%s", "Failed to allocate defrag buffer!");
                 SDL_LogError(SDL_LOG_CATEGORY_GPU, "%s", "Failed to allocate defrag buffer!");
                 return false;
                 return false;
@@ -10980,6 +11114,7 @@ static bool VULKAN_INTERNAL_DefragmentMemory(
         }
         }
     }
     }
 
 
+    SDL_UnlockRWLock(renderer->defragLock);
     SDL_UnlockMutex(renderer->allocatorLock);
     SDL_UnlockMutex(renderer->allocatorLock);
 
 
     return true;
     return true;