|
@@ -430,6 +430,7 @@ static MTLDepthClipMode SDLToMetal_DepthClipMode(
|
|
|
// Structs
|
|
// Structs
|
|
|
|
|
|
|
|
typedef struct MetalRenderer MetalRenderer;
|
|
typedef struct MetalRenderer MetalRenderer;
|
|
|
|
|
+typedef struct MetalCommandBuffer MetalCommandBuffer;
|
|
|
|
|
|
|
|
typedef struct MetalTexture
|
|
typedef struct MetalTexture
|
|
|
{
|
|
{
|
|
@@ -453,7 +454,8 @@ typedef struct MetalTextureContainer
|
|
|
|
|
|
|
|
typedef struct MetalFence
|
|
typedef struct MetalFence
|
|
|
{
|
|
{
|
|
|
- SDL_AtomicInt complete;
|
|
|
|
|
|
|
+ // can be NULL if the command buffer was recycled
|
|
|
|
|
+ MetalCommandBuffer *commandBuffer;
|
|
|
SDL_AtomicInt referenceCount;
|
|
SDL_AtomicInt referenceCount;
|
|
|
} MetalFence;
|
|
} MetalFence;
|
|
|
|
|
|
|
@@ -2093,7 +2095,6 @@ static Uint8 METAL_INTERNAL_CreateFence(
|
|
|
MetalFence *fence;
|
|
MetalFence *fence;
|
|
|
|
|
|
|
|
fence = SDL_calloc(1, sizeof(MetalFence));
|
|
fence = SDL_calloc(1, sizeof(MetalFence));
|
|
|
- SDL_SetAtomicInt(&fence->complete, 0);
|
|
|
|
|
SDL_SetAtomicInt(&fence->referenceCount, 0);
|
|
SDL_SetAtomicInt(&fence->referenceCount, 0);
|
|
|
|
|
|
|
|
// Add it to the available pool
|
|
// Add it to the available pool
|
|
@@ -2136,7 +2137,7 @@ static bool METAL_INTERNAL_AcquireFence(
|
|
|
|
|
|
|
|
// Associate the fence with the command buffer
|
|
// Associate the fence with the command buffer
|
|
|
commandBuffer->fence = fence;
|
|
commandBuffer->fence = fence;
|
|
|
- SDL_SetAtomicInt(&fence->complete, 0); // FIXME: Is this right?
|
|
|
|
|
|
|
+ fence->commandBuffer = commandBuffer;
|
|
|
(void)SDL_AtomicIncRef(&commandBuffer->fence->referenceCount);
|
|
(void)SDL_AtomicIncRef(&commandBuffer->fence->referenceCount);
|
|
|
|
|
|
|
|
return true;
|
|
return true;
|
|
@@ -3517,6 +3518,8 @@ static void METAL_INTERNAL_CleanCommandBuffer(
|
|
|
METAL_ReleaseFence(
|
|
METAL_ReleaseFence(
|
|
|
(SDL_GPURenderer *)renderer,
|
|
(SDL_GPURenderer *)renderer,
|
|
|
(SDL_GPUFence *)commandBuffer->fence);
|
|
(SDL_GPUFence *)commandBuffer->fence);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ commandBuffer->fence->commandBuffer = NULL;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Return command buffer to pool
|
|
// Return command buffer to pool
|
|
@@ -3587,6 +3590,16 @@ static void METAL_INTERNAL_PerformPendingDestroys(
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Fences
|
|
// Fences
|
|
|
|
|
+static bool METAL_INTERNAL_IsFenceBusy(
|
|
|
|
|
+ MetalFence *fence
|
|
|
|
|
+) {
|
|
|
|
|
+ if (!fence->commandBuffer) {
|
|
|
|
|
+ return false; // command buffer was recycled
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ MTLCommandBufferStatus status = fence->commandBuffer->handle.status;
|
|
|
|
|
+ return status == MTLCommandBufferStatusCommitted || status == MTLCommandBufferStatusScheduled;
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
static bool METAL_WaitForFences(
|
|
static bool METAL_WaitForFences(
|
|
|
SDL_GPURenderer *driverData,
|
|
SDL_GPURenderer *driverData,
|
|
@@ -3596,24 +3609,29 @@ static bool METAL_WaitForFences(
|
|
|
{
|
|
{
|
|
|
@autoreleasepool {
|
|
@autoreleasepool {
|
|
|
MetalRenderer *renderer = (MetalRenderer *)driverData;
|
|
MetalRenderer *renderer = (MetalRenderer *)driverData;
|
|
|
- bool waiting;
|
|
|
|
|
|
|
|
|
|
if (waitAll) {
|
|
if (waitAll) {
|
|
|
for (Uint32 i = 0; i < numFences; i += 1) {
|
|
for (Uint32 i = 0; i < numFences; i += 1) {
|
|
|
- while (!SDL_GetAtomicInt(&((MetalFence *)fences[i])->complete)) {
|
|
|
|
|
- // Spin!
|
|
|
|
|
|
|
+ MetalFence *fence = (MetalFence *)fences[i];
|
|
|
|
|
+ if (METAL_INTERNAL_IsFenceBusy(fence)) {
|
|
|
|
|
+ [fence->commandBuffer->handle waitUntilCompleted];
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
} else {
|
|
} else {
|
|
|
- waiting = 1;
|
|
|
|
|
- while (waiting) {
|
|
|
|
|
- for (Uint32 i = 0; i < numFences; i += 1) {
|
|
|
|
|
- if (SDL_GetAtomicInt(&((MetalFence *)fences[i])->complete) > 0) {
|
|
|
|
|
- waiting = 0;
|
|
|
|
|
- break;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
|
|
|
|
|
+ for (Uint32 i = 0; i < numFences; i += 1) {
|
|
|
|
|
+ MetalFence *fence = (MetalFence *)fences[i];
|
|
|
|
|
+ // command buffer has completed and been recycled
|
|
|
|
|
+ if(!fence->commandBuffer)
|
|
|
|
|
+ return true;
|
|
|
|
|
+
|
|
|
|
|
+ // even if it's completed, the handle will call back straight away
|
|
|
|
|
+ [fence->commandBuffer->handle addCompletedHandler:^(id<MTLCommandBuffer> buffer) {
|
|
|
|
|
+ dispatch_semaphore_signal(semaphore);
|
|
|
|
|
+ }];
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
METAL_INTERNAL_PerformPendingDestroys(renderer);
|
|
METAL_INTERNAL_PerformPendingDestroys(renderer);
|
|
@@ -3627,7 +3645,7 @@ static bool METAL_QueryFence(
|
|
|
SDL_GPUFence *fence)
|
|
SDL_GPUFence *fence)
|
|
|
{
|
|
{
|
|
|
MetalFence *metalFence = (MetalFence *)fence;
|
|
MetalFence *metalFence = (MetalFence *)fence;
|
|
|
- return SDL_GetAtomicInt(&metalFence->complete) == 1;
|
|
|
|
|
|
|
+ return METAL_INTERNAL_IsFenceBusy(metalFence);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Window and Swapchain Management
|
|
// Window and Swapchain Management
|
|
@@ -4090,11 +4108,6 @@ static bool METAL_Submit(
|
|
|
windowData->frameCounter = (windowData->frameCounter + 1) % renderer->allowedFramesInFlight;
|
|
windowData->frameCounter = (windowData->frameCounter + 1) % renderer->allowedFramesInFlight;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // Notify the fence when the command buffer has completed
|
|
|
|
|
- [metalCommandBuffer->handle addCompletedHandler:^(id<MTLCommandBuffer> buffer) {
|
|
|
|
|
- SDL_AtomicIncRef(&metalCommandBuffer->fence->complete);
|
|
|
|
|
- }];
|
|
|
|
|
-
|
|
|
|
|
// Submit the command buffer
|
|
// Submit the command buffer
|
|
|
[metalCommandBuffer->handle commit];
|
|
[metalCommandBuffer->handle commit];
|
|
|
metalCommandBuffer->handle = nil;
|
|
metalCommandBuffer->handle = nil;
|
|
@@ -4112,7 +4125,8 @@ static bool METAL_Submit(
|
|
|
|
|
|
|
|
// Check if we can perform any cleanups
|
|
// Check if we can perform any cleanups
|
|
|
for (Sint32 i = renderer->submittedCommandBufferCount - 1; i >= 0; i -= 1) {
|
|
for (Sint32 i = renderer->submittedCommandBufferCount - 1; i >= 0; i -= 1) {
|
|
|
- if (SDL_GetAtomicInt(&renderer->submittedCommandBuffers[i]->fence->complete)) {
|
|
|
|
|
|
|
+
|
|
|
|
|
+ if (!METAL_INTERNAL_IsFenceBusy(renderer->submittedCommandBuffers[i]->fence)) {
|
|
|
METAL_INTERNAL_CleanCommandBuffer(
|
|
METAL_INTERNAL_CleanCommandBuffer(
|
|
|
renderer,
|
|
renderer,
|
|
|
renderer->submittedCommandBuffers[i],
|
|
renderer->submittedCommandBuffers[i],
|
|
@@ -4165,9 +4179,8 @@ static bool METAL_Wait(
|
|
|
* Sort of equivalent to vkDeviceWaitIdle.
|
|
* Sort of equivalent to vkDeviceWaitIdle.
|
|
|
*/
|
|
*/
|
|
|
for (Uint32 i = 0; i < renderer->submittedCommandBufferCount; i += 1) {
|
|
for (Uint32 i = 0; i < renderer->submittedCommandBufferCount; i += 1) {
|
|
|
- while (!SDL_GetAtomicInt(&renderer->submittedCommandBuffers[i]->fence->complete)) {
|
|
|
|
|
- // Spin!
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ SDL_GPUFence *opaqueFence = (SDL_GPUFence *)renderer->submittedCommandBuffers[i]->fence;
|
|
|
|
|
+ METAL_WaitForFences(driverData, true, &opaqueFence, 1);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
SDL_LockMutex(renderer->submitLock);
|
|
SDL_LockMutex(renderer->submitLock);
|