Przeglądaj źródła

metal: Make the command buffer hold a reference to its fence

METAL_ReleaseFence cleared the fence's MTLCommandBuffer without holding
submitLock, while METAL_Submit's cleanup loop may still be polling it
through submittedCommandBuffers. It also did so before the DecRef, so
any non-final release broke later waits on a still-owned fence.
William Horvath 2 tygodni temu
rodzic
commit
d8451e52d5
1 zmienionych plików z 8 dodań i 11 usunięć
  1. 8 11
      src/gpu/metal/SDL_gpu_metal.m

+ 8 - 11
src/gpu/metal/SDL_gpu_metal.m

@@ -608,7 +608,6 @@ typedef struct MetalCommandBuffer
 
     // Fences
     MetalFence *fence;
-    bool autoReleaseFence;
 
     // Reference Counting
     MetalBuffer **usedBuffers;
@@ -2162,8 +2161,6 @@ static SDL_GPUCommandBuffer *METAL_AcquireCommandBuffer(
             commandBuffer->computeUniformBuffers[i] = NULL;
         }
 
-        commandBuffer->autoReleaseFence = true;
-
         SDL_UnlockMutex(renderer->acquireCommandBufferLock);
 
         return (SDL_GPUCommandBuffer *)commandBuffer;
@@ -3406,8 +3403,9 @@ static void METAL_ReleaseFence(
     SDL_GPUFence *fence)
 {
     MetalFence *metalFence = (MetalFence *)fence;
-    metalFence->commandBuffer = nil;
     if (SDL_AtomicDecRef(&metalFence->referenceCount)) {
+        // Nothing references the fence anymore, so the command buffer can go too.
+        metalFence->commandBuffer = nil;
         METAL_INTERNAL_ReleaseFenceToPool(
             (MetalRenderer *)driverData,
             (MetalFence *)fence);
@@ -3513,8 +3511,9 @@ static void METAL_INTERNAL_CleanCommandBuffer(
     commandBuffer->needComputeReadOnlyStorageTextureBind = false;
     SDL_zeroa(commandBuffer->needComputeUniformBufferBind);
 
-    // The fence is now available (unless SubmitAndAcquireFence was called)
-    if (commandBuffer->autoReleaseFence) {
+    // Drop the command buffer's reference to the fence. A cancelled
+    // command buffer never acquired one.
+    if (!cancel) {
         METAL_ReleaseFence(
             (SDL_GPURenderer *)renderer,
             (SDL_GPUFence *)commandBuffer->fence);
@@ -4084,9 +4083,10 @@ static bool METAL_INTERNAL_Submit(
             return false;
         }
 
-        // Return the fence while submitLock is held, another thread could
-        // recycle this command buffer as soon as the lock is released.
+        // Give the caller its own reference while submitLock is held, another
+        // thread could recycle this command buffer as soon as the lock is released.
         if (fence) {
+            (void)SDL_AtomicIncRef(&metalCommandBuffer->fence->referenceCount);
             *fence = (SDL_GPUFence *)metalCommandBuffer->fence;
         }
 
@@ -4145,9 +4145,7 @@ static bool METAL_Submit(
 static SDL_GPUFence *METAL_SubmitAndAcquireFence(
     SDL_GPUCommandBuffer *commandBuffer)
 {
-    MetalCommandBuffer *metalCommandBuffer = (MetalCommandBuffer *)commandBuffer;
     SDL_GPUFence *fence = NULL;
-    metalCommandBuffer->autoReleaseFence = false;
     if (!METAL_INTERNAL_Submit(commandBuffer, &fence)) {
         return NULL;
     }
@@ -4160,7 +4158,6 @@ static bool METAL_Cancel(
     MetalCommandBuffer *metalCommandBuffer = (MetalCommandBuffer *)commandBuffer;
     MetalRenderer *renderer = metalCommandBuffer->renderer;
 
-    metalCommandBuffer->autoReleaseFence = false;
     SDL_LockMutex(renderer->submitLock);
     METAL_INTERNAL_CleanCommandBuffer(renderer, metalCommandBuffer, true);
     SDL_UnlockMutex(renderer->submitLock);