From 39664f3b775f9c2b531cb2c554b313b4f505bb8e Mon Sep 17 00:00:00 2001 From: William Horvath Date: Sat, 29 Aug 2026 16:13:37 +0900 Subject: [PATCH] metal: Read the fence in SubmitAndAcquireFence while submitLock is held Once the lock is released, another thread can clean up the completed command buffer, return it to the pool and resubmit it with a different fence before we read metalCommandBuffer->fence, handing the caller a fence it doesn't own. (cherry picked from commit 10309e32409032d07170a85727b948b5b1f6bb55) --- src/gpu/metal/SDL_gpu_metal.m | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/src/gpu/metal/SDL_gpu_metal.m b/src/gpu/metal/SDL_gpu_metal.m index 3a02f8fd3a..f6d6d30f96 100644 --- a/src/gpu/metal/SDL_gpu_metal.m +++ b/src/gpu/metal/SDL_gpu_metal.m @@ -4063,8 +4063,9 @@ static bool METAL_SetAllowedFramesInFlight( // Submission -static bool METAL_Submit( - SDL_GPUCommandBuffer *commandBuffer) +static bool METAL_INTERNAL_Submit( + SDL_GPUCommandBuffer *commandBuffer, + SDL_GPUFence **fence) { @autoreleasepool { MetalCommandBuffer *metalCommandBuffer = (MetalCommandBuffer *)commandBuffer; @@ -4077,6 +4078,12 @@ static bool METAL_Submit( return false; } + // Return the fence while submitLock is held, another thread could + // recycle this command buffer as soon as the lock is released. + if (fence) { + *fence = (SDL_GPUFence *)metalCommandBuffer->fence; + } + // Enqueue present requests, if applicable for (Uint32 i = 0; i < metalCommandBuffer->windowDataCount; i += 1) { MetalWindowData *windowData = metalCommandBuffer->windowDatas[i]; @@ -4123,15 +4130,22 @@ static bool METAL_Submit( } } +static bool METAL_Submit( + SDL_GPUCommandBuffer *commandBuffer) +{ + return METAL_INTERNAL_Submit(commandBuffer, NULL); +} + static SDL_GPUFence *METAL_SubmitAndAcquireFence( SDL_GPUCommandBuffer *commandBuffer) { MetalCommandBuffer *metalCommandBuffer = (MetalCommandBuffer *)commandBuffer; + SDL_GPUFence *fence = NULL; metalCommandBuffer->autoReleaseFence = false; - if (!METAL_Submit(commandBuffer)) { + if (!METAL_INTERNAL_Submit(commandBuffer, &fence)) { return NULL; } - return (SDL_GPUFence *)metalCommandBuffer->fence; + return fence; } static bool METAL_Cancel(