diff --git a/src/gpu/metal/SDL_gpu_metal.m b/src/gpu/metal/SDL_gpu_metal.m index fd6c399d9a05f..0c4e98fe2d0a5 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,7 +741,10 @@ static void METAL_DestroyDevice(SDL_GPUDevice *device) // Release fence infrastructure for (Uint32 i = 0; i < renderer->availableFenceCount; i += 1) { - 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); @@ -2085,7 +2090,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 +2135,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 +3570,14 @@ static void METAL_INTERNAL_PerformPendingDestroys( } // Fences +static bool METAL_INTERNAL_FenceComplete(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 +3591,23 @@ static bool METAL_WaitForFences( if (waitAll) { for (Uint32 i = 0; i < numFences; i += 1) { - while (!SDL_GetAtomicInt(&((MetalFence *)fences[i])->complete)) { - // Spin! + MetalFence *fence = (MetalFence *)fences[i]; + SDL_LockMutex(fence->mutex); + while (!fence->complete) { + SDL_WaitCondition(fence->condition, fence->mutex); } + SDL_UnlockMutex(fence->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_FenceComplete((MetalFence *)fences[i])) { waiting = 0; break; } } + SDL_DelayNS(1000); } } @@ -3603,7 +3622,7 @@ static bool METAL_QueryFence( SDL_GPUFence *fence) { MetalFence *metalFence = (MetalFence *)fence; - return SDL_GetAtomicInt(&metalFence->complete) == 1; + return METAL_INTERNAL_FenceComplete(metalFence); } // Window and Swapchain Management @@ -4055,7 +4074,11 @@ static bool METAL_Submit( // Notify the fence when the command buffer has completed [metalCommandBuffer->handle addCompletedHandler:^(id buffer) { - SDL_AtomicIncRef(&metalCommandBuffer->fence->complete); + MetalFence *metalFence = (MetalFence *)metalCommandBuffer->fence; + SDL_LockMutex(metalFence->mutex); + metalFence->complete = true; + SDL_SignalCondition(metalFence->condition); + SDL_UnlockMutex(metalFence->mutex); }]; // Submit the command buffer @@ -4075,7 +4098,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_FenceComplete(renderer->submittedCommandBuffers[i]->fence)) { METAL_INTERNAL_CleanCommandBuffer( renderer, renderer->submittedCommandBuffers[i], @@ -4128,9 +4151,7 @@ static bool METAL_Wait( * 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(driverData, true, (SDL_GPUFence **)&renderer->submittedCommandBuffers[i]->fence, 1); } SDL_LockMutex(renderer->submitLock);