From 33bf26c7b1dea0fe65922bedc4b7039cf15b8c25 Mon Sep 17 00:00:00 2001 From: Alexey Tselousov <52216194+alexgu754@users.noreply.github.com> Date: Sat, 29 Nov 2025 22:19:32 +0300 Subject: [PATCH 1/2] Remove busy loops in SDL_GPUFence SDL_GPUFence is implemented as just a busy loop on an atomic int, a SDL_GPUCondition gives the cpu some rest --- src/gpu/metal/SDL_gpu_metal.m | 43 +++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/src/gpu/metal/SDL_gpu_metal.m b/src/gpu/metal/SDL_gpu_metal.m index fd6c399d9a05f..ae849f338995c 100644 --- a/src/gpu/metal/SDL_gpu_metal.m +++ b/src/gpu/metal/SDL_gpu_metal.m @@ -445,7 +445,9 @@ static MTLDepthClipMode SDLToMetal_DepthClipMode( typedef struct MetalFence { - SDL_AtomicInt complete; + bool complete; + SDL_Mutex *mutex; + SDL_Condition *condition; SDL_AtomicInt referenceCount; } MetalFence; @@ -739,6 +741,8 @@ static void METAL_DestroyDevice(SDL_GPUDevice *device) // Release fence infrastructure for (Uint32 i = 0; i < renderer->availableFenceCount; i += 1) { + SDL_DestroyMutex(renderer->availableFences[i]->mutex); + SDL_DestroyCondition(renderer->availableFences[i]->condition); SDL_free(renderer->availableFences[i]); } SDL_free(renderer->availableFences); @@ -2085,7 +2089,9 @@ static Uint8 METAL_INTERNAL_CreateFence( MetalFence *fence; fence = SDL_calloc(1, sizeof(MetalFence)); - SDL_SetAtomicInt(&fence->complete, 0); + fence->condition = SDL_CreateCondition(); + fence->mutex = SDL_CreateMutex(); + fence->complete = false; SDL_SetAtomicInt(&fence->referenceCount, 0); // Add it to the available pool @@ -2128,7 +2134,7 @@ static bool METAL_INTERNAL_AcquireFence( // Associate the fence with the command buffer commandBuffer->fence = fence; - SDL_SetAtomicInt(&fence->complete, 0); // FIXME: Is this right? + fence->complete = false; (void)SDL_AtomicIncRef(&commandBuffer->fence->referenceCount); return true; @@ -3563,6 +3569,13 @@ static void METAL_INTERNAL_PerformPendingDestroys( } // Fences +static bool METAL_INTERNAL_FenceOpen(MetalFence *fence) { + bool complete; + SDL_LockMutex(fence->mutex); + complete = fence->complete; + SDL_UnlockMutex(fence->mutex); + return complete; +} static bool METAL_WaitForFences( SDL_GPURenderer *driverData, @@ -3576,19 +3589,22 @@ static bool METAL_WaitForFences( if (waitAll) { for (Uint32 i = 0; i < numFences; i += 1) { - while (!SDL_GetAtomicInt(&((MetalFence *)fences[i])->complete)) { - // Spin! + SDL_LockMutex(((MetalFence *)fences[i])->mutex); + while(!((MetalFence *)fences[i])->complete) { + SDL_WaitCondition(((MetalFence *)fences[i])->condition, ((MetalFence *)fences[i])->mutex); } + SDL_UnlockMutex(((MetalFence *)fences[i])->mutex); } } else { waiting = 1; while (waiting) { for (Uint32 i = 0; i < numFences; i += 1) { - if (SDL_GetAtomicInt(&((MetalFence *)fences[i])->complete) > 0) { + if(METAL_INTERNAL_FenceOpen((MetalFence *)fences[i])) { waiting = 0; break; } } + SDL_DelayNS(1000); } } @@ -3602,8 +3618,7 @@ static bool METAL_QueryFence( SDL_GPURenderer *driverData, SDL_GPUFence *fence) { - MetalFence *metalFence = (MetalFence *)fence; - return SDL_GetAtomicInt(&metalFence->complete) == 1; + return METAL_INTERNAL_FenceOpen(((MetalFence *)fence)); } // Window and Swapchain Management @@ -4055,7 +4070,9 @@ static bool METAL_Submit( // Notify the fence when the command buffer has completed [metalCommandBuffer->handle addCompletedHandler:^(id buffer) { - SDL_AtomicIncRef(&metalCommandBuffer->fence->complete); + SDL_LockMutex(metalCommandBuffer->fence->mutex); + metalCommandBuffer->fence->complete = true; + SDL_UnlockMutex(metalCommandBuffer->fence->mutex); }]; // Submit the command buffer @@ -4075,7 +4092,7 @@ static bool METAL_Submit( // Check if we can perform any cleanups for (Sint32 i = renderer->submittedCommandBufferCount - 1; i >= 0; i -= 1) { - if (SDL_GetAtomicInt(&renderer->submittedCommandBuffers[i]->fence->complete)) { + if (METAL_INTERNAL_FenceOpen(renderer->submittedCommandBuffers[i]->fence)) { METAL_INTERNAL_CleanCommandBuffer( renderer, renderer->submittedCommandBuffers[i], @@ -4127,11 +4144,7 @@ static bool METAL_Wait( * Wait for all submitted command buffers to complete. * Sort of equivalent to vkDeviceWaitIdle. */ - for (Uint32 i = 0; i < renderer->submittedCommandBufferCount; i += 1) { - while (!SDL_GetAtomicInt(&renderer->submittedCommandBuffers[i]->fence->complete)) { - // Spin! - } - } + METAL_WaitForFences(renderer, true, renderer->submittedCommandBuffers, renderer->submittedCommandBufferCount); SDL_LockMutex(renderer->submitLock); From e12dab7026e97f8e1990f48a9cde827148f02b44 Mon Sep 17 00:00:00 2001 From: Alexey Tselousov <52216194+alexgu754@users.noreply.github.com> Date: Sun, 30 Nov 2025 09:55:56 +0300 Subject: [PATCH 2/2] Update SDL_gpu_metal.m removed bad casts --- src/gpu/metal/SDL_gpu_metal.m | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/gpu/metal/SDL_gpu_metal.m b/src/gpu/metal/SDL_gpu_metal.m index ae849f338995c..cacacb5027e75 100644 --- a/src/gpu/metal/SDL_gpu_metal.m +++ b/src/gpu/metal/SDL_gpu_metal.m @@ -3830,7 +3830,7 @@ static bool METAL_WaitForSwapchain( if (windowData->inFlightFences[windowData->frameCounter] != NULL) { if (!METAL_WaitForFences( - driverData, + (SDL_GPURenderer *)driverData, true, &windowData->inFlightFences[windowData->frameCounter], 1)) { @@ -4072,6 +4072,7 @@ static bool METAL_Submit( [metalCommandBuffer->handle addCompletedHandler:^(id buffer) { SDL_LockMutex(metalCommandBuffer->fence->mutex); metalCommandBuffer->fence->complete = true; + SDL_SignalCondition(metalCommandBuffer->fence->condition); SDL_UnlockMutex(metalCommandBuffer->fence->mutex); }]; @@ -4144,7 +4145,9 @@ static bool METAL_Wait( * Wait for all submitted command buffers to complete. * Sort of equivalent to vkDeviceWaitIdle. */ - METAL_WaitForFences(renderer, true, renderer->submittedCommandBuffers, renderer->submittedCommandBufferCount); + for (Uint32 i = 0; i < renderer->submittedCommandBufferCount; i += 1) { + METAL_WaitForFences((SDL_GPURenderer *)renderer, true, (SDL_GPUFence **)&renderer->submittedCommandBuffers[i]->fence, 1); + } SDL_LockMutex(renderer->submitLock);