Bladeren bron

GPU: Add SetGPUAllowedFramesInFlight (#11599)

Evan Hemsley 4 maanden geleden
bovenliggende
commit
fa5f84fb6e

+ 23 - 0
include/SDL3/SDL_gpu.h

@@ -3496,6 +3496,29 @@ extern SDL_DECLSPEC bool SDLCALL SDL_SetGPUSwapchainParameters(
     SDL_GPUSwapchainComposition swapchain_composition,
     SDL_GPUPresentMode present_mode);
 
+/**
+ * Configures the maximum allowed number of frames in flight.
+ *
+ * The default value when the device is created is 2.
+ * This means that after you have submitted 2 frames for presentation, if the GPU has not finished working on the first frame, SDL_AcquireGPUSwapchainTexture() will block or return false depending on the present mode.
+ *
+ * Higher values increase throughput at the expense of visual latency.
+ * Lower values decrease visual latency at the expense of throughput.
+ *
+ * Note that calling this function will stall and flush the command queue to prevent synchronization issues.
+ *
+ * The minimum value of allowed frames in flight is 1, and the maximum is 3.
+ *
+ * \param device a GPU context.
+ * \param allowed_frames_in_flight the maximum number of frames that can be pending on the GPU before AcquireSwapchainTexture blocks or returns false.
+ * \returns true if successful, false on error; call SDL_GetError() for more information.
+ *
+ * \since This function is available since SDL 3.2.0.
+ */
+extern SDL_DECLSPEC bool SDLCALL SDL_SetGPUAllowedFramesInFlight(
+    SDL_GPUDevice *device,
+    Uint32 allowed_frames_in_flight);
+
 /**
  * Obtains the texture format of the swapchain for the given window.
  *

+ 1 - 0
src/dynapi/SDL_dynapi.sym

@@ -1203,6 +1203,7 @@ SDL3_0.0.0 {
     SDL_ShowFileDialogWithProperties;
     SDL_IsMainThread;
     SDL_RunOnMainThread;
+    SDL_SetGPUAllowedFramesInFlight;
     # extra symbols go here (don't modify this line)
   local: *;
 };

+ 1 - 0
src/dynapi/SDL_dynapi_overrides.h

@@ -1228,3 +1228,4 @@
 #define SDL_ShowFileDialogWithProperties SDL_ShowFileDialogWithProperties_REAL
 #define SDL_IsMainThread SDL_IsMainThread_REAL
 #define SDL_RunOnMainThread SDL_RunOnMainThread_REAL
+#define SDL_SetGPUAllowedFramesInFlight SDL_SetGPUAllowedFramesInFlight_REAL

+ 1 - 0
src/dynapi/SDL_dynapi_procs.h

@@ -1234,3 +1234,4 @@ SDL_DYNAPI_PROC(bool,SDL_LoadFileAsync,(const char *a, SDL_AsyncIOQueue *b, void
 SDL_DYNAPI_PROC(void,SDL_ShowFileDialogWithProperties,(SDL_FileDialogType a, SDL_DialogFileCallback b, void *c, SDL_PropertiesID d),(a,b,c,d),)
 SDL_DYNAPI_PROC(bool,SDL_IsMainThread,(void),(),return)
 SDL_DYNAPI_PROC(bool,SDL_RunOnMainThread,(SDL_MainThreadCallback a,void *b,bool c),(a,b,c),return)
+SDL_DYNAPI_PROC(bool,SDL_SetGPUAllowedFramesInFlight,(SDL_GPUDevice *a,Uint32 b),(a,b),return)

+ 19 - 0
src/gpu/SDL_gpu.c

@@ -2650,6 +2650,25 @@ bool SDL_SetGPUSwapchainParameters(
         present_mode);
 }
 
+bool SDL_SetGPUAllowedFramesInFlight(
+    SDL_GPUDevice *device,
+    Uint32 allowed_frames_in_flight)
+{
+    CHECK_DEVICE_MAGIC(device, false);
+
+    if (device->debug_mode) {
+        if (allowed_frames_in_flight < 1 || allowed_frames_in_flight > 3)
+        {
+            SDL_assert_release(!"allowed_frames_in_flight value must be between 1 and 3!");
+        }
+    }
+
+    allowed_frames_in_flight = SDL_clamp(allowed_frames_in_flight, 1, 3);
+    return device->SetAllowedFramesInFlight(
+        device->driverData,
+        allowed_frames_in_flight);
+}
+
 SDL_GPUTextureFormat SDL_GetGPUSwapchainTextureFormat(
     SDL_GPUDevice *device,
     SDL_Window *window)

+ 5 - 0
src/gpu/SDL_sysgpu.h

@@ -791,6 +791,10 @@ struct SDL_GPUDevice
         SDL_GPUSwapchainComposition swapchainComposition,
         SDL_GPUPresentMode presentMode);
 
+    bool (*SetAllowedFramesInFlight)(
+        SDL_GPURenderer *driverData,
+        Uint32 allowedFramesInFlight);
+
     SDL_GPUTextureFormat (*GetSwapchainTextureFormat)(
         SDL_GPURenderer *driverData,
         SDL_Window *window);
@@ -927,6 +931,7 @@ struct SDL_GPUDevice
     ASSIGN_DRIVER_FUNC(ClaimWindow, name)                   \
     ASSIGN_DRIVER_FUNC(ReleaseWindow, name)                 \
     ASSIGN_DRIVER_FUNC(SetSwapchainParameters, name)        \
+    ASSIGN_DRIVER_FUNC(SetAllowedFramesInFlight, name)      \
     ASSIGN_DRIVER_FUNC(GetSwapchainTextureFormat, name)     \
     ASSIGN_DRIVER_FUNC(AcquireCommandBuffer, name)          \
     ASSIGN_DRIVER_FUNC(AcquireSwapchainTexture, name)       \

+ 22 - 6
src/gpu/d3d12/SDL_gpu_d3d12.c

@@ -750,6 +750,7 @@ struct D3D12Renderer
     // FIXME: these might not be necessary since we're not using custom heaps
     bool UMA;
     bool UMACacheCoherent;
+    Uint32 allowedFramesInFlight;
 
     // Indirect command signatures
     ID3D12CommandSignature *indirectDrawCommandSignature;
@@ -6809,6 +6810,20 @@ static bool D3D12_SetSwapchainParameters(
     return true;
 }
 
+static bool D3D12_SetAllowedFramesInFlight(
+    SDL_GPURenderer *driverData,
+    Uint32 allowedFramesInFlight)
+{
+    D3D12Renderer *renderer = (D3D12Renderer *)driverData;
+
+    if (!D3D12_Wait(driverData)) {
+        return false;
+    }
+
+    renderer->allowedFramesInFlight = allowedFramesInFlight;
+    return true;
+}
+
 static SDL_GPUTextureFormat D3D12_GetSwapchainTextureFormat(
     SDL_GPURenderer *driverData,
     SDL_Window *window)
@@ -7569,7 +7584,7 @@ static bool D3D12_Submit(
 
         windowData->inFlightFences[windowData->frameCounter] = (SDL_GPUFence*)d3d12CommandBuffer->inFlightFence;
         (void)SDL_AtomicIncRef(&d3d12CommandBuffer->inFlightFence->referenceCount);
-        windowData->frameCounter = (windowData->frameCounter + 1) % MAX_FRAMES_IN_FLIGHT;
+        windowData->frameCounter = (windowData->frameCounter + 1) % renderer->allowedFramesInFlight;
     }
 
     // Check for cleanups
@@ -8181,10 +8196,10 @@ static bool D3D12_INTERNAL_TryInitializeD3D12DebugInfoQueue(D3D12Renderer *rende
 }
 
 static void WINAPI D3D12_INTERNAL_OnD3D12DebugInfoMsg(
-    D3D12_MESSAGE_CATEGORY category, 
-    D3D12_MESSAGE_SEVERITY severity, 
-    D3D12_MESSAGE_ID id, 
-    LPCSTR description, 
+    D3D12_MESSAGE_CATEGORY category,
+    D3D12_MESSAGE_SEVERITY severity,
+    D3D12_MESSAGE_ID id,
+    LPCSTR description,
     void *context)
 {
     char *catStr;
@@ -8288,7 +8303,7 @@ static void D3D12_INTERNAL_TryInitializeD3D12DebugInfoLogger(D3D12Renderer *rend
         D3D12_MESSAGE_CALLBACK_FLAG_NONE,
         NULL,
         NULL);
-    
+
     ID3D12InfoQueue1_Release(infoQueue);
 }
 #endif
@@ -8776,6 +8791,7 @@ static SDL_GPUDevice *D3D12_CreateDevice(bool debugMode, bool preferLowPower, SD
     renderer->disposeLock = SDL_CreateMutex();
 
     renderer->debug_mode = debugMode;
+    renderer->allowedFramesInFlight = 2;
 
     renderer->semantic = SDL_GetStringProperty(props, SDL_PROP_GPU_DEVICE_CREATE_D3D12_SEMANTIC_NAME_STRING, "TEXCOORD");
 

+ 19 - 1
src/gpu/metal/SDL_gpu_metal.m

@@ -641,6 +641,7 @@ struct MetalRenderer
     id<MTLCommandQueue> queue;
 
     bool debugMode;
+    Uint32 allowedFramesInFlight;
 
     MetalWindowData **claimedWindows;
     Uint32 claimedWindowCount;
@@ -3817,6 +3818,22 @@ static bool METAL_SetSwapchainParameters(
     }
 }
 
+static bool METAL_SetAllowedFramesInFlight(
+    SDL_GPURenderer *driverData,
+    Uint32 allowedFramesInFlight)
+{
+    @autoreleasepool {
+        MetalRenderer *renderer = (MetalRenderer *)driverData;
+
+        if (!METAL_Wait(driverData)) {
+            return false;
+        }
+
+        renderer->allowedFramesInFlight = allowedFramesInFlight;
+        return true;
+    }
+}
+
 // Submission
 
 static bool METAL_Submit(
@@ -3843,7 +3860,7 @@ static bool METAL_Submit(
 
             (void)SDL_AtomicIncRef(&metalCommandBuffer->fence->referenceCount);
 
-            windowData->frameCounter = (windowData->frameCounter + 1) % MAX_FRAMES_IN_FLIGHT;
+            windowData->frameCounter = (windowData->frameCounter + 1) % renderer->allowedFramesInFlight;
         }
 
         // Notify the fence when the command buffer has completed
@@ -4301,6 +4318,7 @@ static SDL_GPUDevice *METAL_CreateDevice(bool debugMode, bool preferLowPower, SD
 
         // Remember debug mode
         renderer->debugMode = debugMode;
+        renderer->allowedFramesInFlight = 2;
 
         // Set up colorspace array
         SwapchainCompositionToColorSpace[0] = kCGColorSpaceSRGB;

+ 18 - 2
src/gpu/vulkan/SDL_gpu_vulkan.c

@@ -1123,6 +1123,8 @@ struct VulkanRenderer
 
     bool debugMode;
     bool preferLowPower;
+    Uint32 allowedFramesInFlight;
+
     VulkanExtensions supports;
     bool supportsDebugUtils;
     bool supportsColorspace;
@@ -9898,6 +9900,20 @@ static bool VULKAN_SetSwapchainParameters(
     return true;
 }
 
+static bool VULKAN_SetAllowedFramesInFlight(
+    SDL_GPURenderer *driverData,
+    Uint32 allowedFramesInFlight)
+{
+    VulkanRenderer *renderer = (VulkanRenderer *)driverData;
+
+    if (!VULKAN_Wait(driverData)) {
+        return false;
+    }
+
+    renderer->allowedFramesInFlight = allowedFramesInFlight;
+    return true;
+}
+
 // Submission structure
 
 static VulkanFenceHandle *VULKAN_INTERNAL_AcquireFenceFromPool(
@@ -10348,8 +10364,7 @@ static bool VULKAN_Submit(
         }
 
         presentData->windowData->frameCounter =
-            (presentData->windowData->frameCounter + 1) % MAX_FRAMES_IN_FLIGHT;
-
+            (presentData->windowData->frameCounter + 1) % renderer->allowedFramesInFlight;
     }
 
     // Check if we can perform any cleanups
@@ -11438,6 +11453,7 @@ static SDL_GPUDevice *VULKAN_CreateDevice(bool debugMode, bool preferLowPower, S
     SDL_memset(renderer, '\0', sizeof(VulkanRenderer));
     renderer->debugMode = debugMode;
     renderer->preferLowPower = preferLowPower;
+    renderer->allowedFramesInFlight = 2;
 
     if (!VULKAN_INTERNAL_PrepareVulkan(renderer)) {
         SDL_free(renderer);