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/7] 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/7] 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); From 0f86335fd14c57cdbe9f040ed909b8bfacdb5e84 Mon Sep 17 00:00:00 2001 From: Alexey Tselousov <52216194+alexgu754@users.noreply.github.com> Date: Tue, 16 Dec 2025 10:17:30 +0300 Subject: [PATCH 3/7] Apply suggestions from code review semantic fixes Co-authored-by: Sam Lantinga --- src/gpu/metal/SDL_gpu_metal.m | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/gpu/metal/SDL_gpu_metal.m b/src/gpu/metal/SDL_gpu_metal.m index cacacb5027e75..306b8997c0e31 100644 --- a/src/gpu/metal/SDL_gpu_metal.m +++ b/src/gpu/metal/SDL_gpu_metal.m @@ -3569,7 +3569,8 @@ static void METAL_INTERNAL_PerformPendingDestroys( } // Fences -static bool METAL_INTERNAL_FenceOpen(MetalFence *fence) { +static bool METAL_INTERNAL_FenceOpen(MetalFence *fence) +{ bool complete; SDL_LockMutex(fence->mutex); complete = fence->complete; @@ -3589,7 +3590,8 @@ static bool METAL_WaitForFences( if (waitAll) { for (Uint32 i = 0; i < numFences; i += 1) { - SDL_LockMutex(((MetalFence *)fences[i])->mutex); + MetalFence *fence = (MetalFence *)fences[i]; + SDL_LockMutex(fence->mutex); while(!((MetalFence *)fences[i])->complete) { SDL_WaitCondition(((MetalFence *)fences[i])->condition, ((MetalFence *)fences[i])->mutex); } @@ -3599,7 +3601,7 @@ static bool METAL_WaitForFences( waiting = 1; while (waiting) { for (Uint32 i = 0; i < numFences; i += 1) { - if(METAL_INTERNAL_FenceOpen((MetalFence *)fences[i])) { + if (METAL_INTERNAL_FenceOpen((MetalFence *)fences[i])) { waiting = 0; break; } @@ -3618,7 +3620,8 @@ static bool METAL_QueryFence( SDL_GPURenderer *driverData, SDL_GPUFence *fence) { - return METAL_INTERNAL_FenceOpen(((MetalFence *)fence)); + MetalFence *metalFence = (MetalFence *)fence; + return METAL_INTERNAL_FenceOpen(metalFence); } // Window and Swapchain Management From 02b175fe1f5b08298c3ed686107207184aecbf87 Mon Sep 17 00:00:00 2001 From: Alexey Tselousov <52216194+alexgu754@users.noreply.github.com> Date: Tue, 16 Dec 2025 10:20:38 +0300 Subject: [PATCH 4/7] Update SDL_gpu_metal.m more semantic changes --- src/gpu/metal/SDL_gpu_metal.m | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/gpu/metal/SDL_gpu_metal.m b/src/gpu/metal/SDL_gpu_metal.m index 306b8997c0e31..6533176b95cb9 100644 --- a/src/gpu/metal/SDL_gpu_metal.m +++ b/src/gpu/metal/SDL_gpu_metal.m @@ -3569,7 +3569,7 @@ static void METAL_INTERNAL_PerformPendingDestroys( } // Fences -static bool METAL_INTERNAL_FenceOpen(MetalFence *fence) +static bool METAL_INTERNAL_FenceComplete(MetalFence *fence) { bool complete; SDL_LockMutex(fence->mutex); @@ -3601,7 +3601,7 @@ static bool METAL_WaitForFences( waiting = 1; while (waiting) { for (Uint32 i = 0; i < numFences; i += 1) { - if (METAL_INTERNAL_FenceOpen((MetalFence *)fences[i])) { + if (METAL_INTERNAL_FenceComplete((MetalFence *)fences[i])) { waiting = 0; break; } @@ -3621,7 +3621,7 @@ static bool METAL_QueryFence( SDL_GPUFence *fence) { MetalFence *metalFence = (MetalFence *)fence; - return METAL_INTERNAL_FenceOpen(metalFence); + return METAL_INTERNAL_FenceComplete(metalFence); } // Window and Swapchain Management @@ -4073,10 +4073,11 @@ static bool METAL_Submit( // Notify the fence when the command buffer has completed [metalCommandBuffer->handle addCompletedHandler:^(id buffer) { - SDL_LockMutex(metalCommandBuffer->fence->mutex); - metalCommandBuffer->fence->complete = true; - SDL_SignalCondition(metalCommandBuffer->fence->condition); - SDL_UnlockMutex(metalCommandBuffer->fence->mutex); + MetalFence *metalFence = (MetalFence *)metalCommandBuffer->fence; + SDL_LockMutex(metalFence->mutex); + metalFence->complete = true; + SDL_SignalCondition(metalFence->condition); + SDL_UnlockMutex(metalFence->mutex); }]; // Submit the command buffer @@ -4096,7 +4097,7 @@ static bool METAL_Submit( // Check if we can perform any cleanups for (Sint32 i = renderer->submittedCommandBufferCount - 1; i >= 0; i -= 1) { - if (METAL_INTERNAL_FenceOpen(renderer->submittedCommandBuffers[i]->fence)) { + if (METAL_INTERNAL_FenceComplete(renderer->submittedCommandBuffers[i]->fence)) { METAL_INTERNAL_CleanCommandBuffer( renderer, renderer->submittedCommandBuffers[i], From 9bb8cb290ac6eb1fab4c20b0e3d0f426318e2b71 Mon Sep 17 00:00:00 2001 From: Alexey Tselousov <52216194+alexgu754@users.noreply.github.com> Date: Tue, 16 Dec 2025 12:27:59 +0300 Subject: [PATCH 5/7] Update SDL_gpu_metal.m syntactic changes --- src/gpu/metal/SDL_gpu_metal.m | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/gpu/metal/SDL_gpu_metal.m b/src/gpu/metal/SDL_gpu_metal.m index 6533176b95cb9..686d3f75a3ecb 100644 --- a/src/gpu/metal/SDL_gpu_metal.m +++ b/src/gpu/metal/SDL_gpu_metal.m @@ -3592,16 +3592,17 @@ static bool METAL_WaitForFences( for (Uint32 i = 0; i < numFences; i += 1) { MetalFence *fence = (MetalFence *)fences[i]; SDL_LockMutex(fence->mutex); - while(!((MetalFence *)fences[i])->complete) { - SDL_WaitCondition(((MetalFence *)fences[i])->condition, ((MetalFence *)fences[i])->mutex); + while(!fence->complete) { + SDL_WaitCondition(fence->condition, fence->mutex); } - SDL_UnlockMutex(((MetalFence *)fences[i])->mutex); + SDL_UnlockMutex(fence->mutex); } } else { waiting = 1; while (waiting) { for (Uint32 i = 0; i < numFences; i += 1) { - if (METAL_INTERNAL_FenceComplete((MetalFence *)fences[i])) { + MetalFence *fence = (MetalFence *)fences[i]; + if (METAL_INTERNAL_FenceComplete(fence) { waiting = 0; break; } From 8cb5e25a6ec29c14432e2d0c239c92ff098186db Mon Sep 17 00:00:00 2001 From: Alexey Tselousov <52216194+alexgu754@users.noreply.github.com> Date: Tue, 16 Dec 2025 12:30:48 +0300 Subject: [PATCH 6/7] Update SDL_gpu_metal.m --- src/gpu/metal/SDL_gpu_metal.m | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/gpu/metal/SDL_gpu_metal.m b/src/gpu/metal/SDL_gpu_metal.m index 686d3f75a3ecb..5e3d84cfc96f4 100644 --- a/src/gpu/metal/SDL_gpu_metal.m +++ b/src/gpu/metal/SDL_gpu_metal.m @@ -741,9 +741,10 @@ 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]); + MetalFence *fence = (MetalFence *)renderer->availableFences[i]; + SDL_DestroyMutex(fence->mutex); + SDL_DestroyCondition(fence->condition); + SDL_free(fence); } SDL_free(renderer->availableFences); From db099f545f6f5f30a5282d256da29050526d5a22 Mon Sep 17 00:00:00 2001 From: Alexey Tselousov <52216194+alexgu754@users.noreply.github.com> Date: Tue, 16 Dec 2025 13:20:48 +0300 Subject: [PATCH 7/7] Update SDL_gpu_metal.m fix missing bracket --- src/gpu/metal/SDL_gpu_metal.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gpu/metal/SDL_gpu_metal.m b/src/gpu/metal/SDL_gpu_metal.m index 5e3d84cfc96f4..15395abaa567a 100644 --- a/src/gpu/metal/SDL_gpu_metal.m +++ b/src/gpu/metal/SDL_gpu_metal.m @@ -3603,7 +3603,7 @@ static bool METAL_WaitForFences( while (waiting) { for (Uint32 i = 0; i < numFences; i += 1) { MetalFence *fence = (MetalFence *)fences[i]; - if (METAL_INTERNAL_FenceComplete(fence) { + if (METAL_INTERNAL_FenceComplete(fence)) { waiting = 0; break; }