diff --git a/include/SDL3/SDL_gpu.h b/include/SDL3/SDL_gpu.h index 1cf787ecbf1a3..069a6f1694388 100644 --- a/include/SDL3/SDL_gpu.h +++ b/include/SDL3/SDL_gpu.h @@ -990,17 +990,52 @@ extern SDL_DECLSPEC void SDLCALL SDL_GpuSetTextureName( const char *text); /** - * Sets an arbitrary string constant to label a section of a command buffer. Useful for debugging. + * Inserts an arbitrary string label into the command buffer callstream. + * Useful for debugging. * * \param commandBuffer a command buffer - * \param text a UTF-8 string constant to mark as the label + * \param text a UTF-8 string constant to insert as the label * * \since This function is available since SDL 3.x.x */ -extern SDL_DECLSPEC void SDLCALL SDL_GpuSetStringMarker( +extern SDL_DECLSPEC void SDLCALL SDL_GpuInsertDebugLabel( SDL_GpuCommandBuffer *commandBuffer, const char *text); +/** + * Begins a debug group with an arbitary name. + * Used for denoting groups of calls when viewing the command buffer callstream + * in a graphics debugging tool. + * + * Each call to SDL_GpuPushDebugGroup must have a corresponding call to SDL_GpuPopDebugGroup. + * + * On some backends (e.g. Metal), pushing a debug group during a render/blit/compute pass + * will create a group that is scoped to the native pass rather than the command buffer. + * For best results, if you push a debug group during a pass, always pop it in the same pass. + * + * \param commandBuffer a command buffer + * \param name a UTF-8 string constant that names the group + * + * \since This function is available since SDL 3.x.x + * + * \sa SDL_GpuPopDebugGroup + */ +extern SDL_DECLSPEC void SDLCALL SDL_GpuPushDebugGroup( + SDL_GpuCommandBuffer *commandBuffer, + const char *name); + +/** + * Ends the most-recently pushed debug group. + * + * \param commandBuffer a command buffer + * + * \since This function is available since SDL 3.x.x + * + * \sa SDL_GpuPushDebugGroup + */ +extern SDL_DECLSPEC void SDLCALL SDL_GpuPopDebugGroup( + SDL_GpuCommandBuffer *commandBuffer); + /* Disposal */ /** diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym index 617a91473ebc0..0c338fe933f2e 100644 --- a/src/dynapi/SDL_dynapi.sym +++ b/src/dynapi/SDL_dynapi.sym @@ -1032,7 +1032,9 @@ SDL3_0.0.0 { SDL_GpuCreateTransferBuffer; SDL_GpuSetBufferName; SDL_GpuSetTextureName; - SDL_GpuSetStringMarker; + SDL_GpuInsertDebugLabel; + SDL_GpuPushDebugGroup; + SDL_GpuPopDebugGroup; SDL_GpuReleaseTexture; SDL_GpuReleaseSampler; SDL_GpuReleaseBuffer; diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h index 7abee4e2d161b..4d2ff7695ca33 100644 --- a/src/dynapi/SDL_dynapi_overrides.h +++ b/src/dynapi/SDL_dynapi_overrides.h @@ -1057,7 +1057,9 @@ #define SDL_GpuCreateTransferBuffer SDL_GpuCreateTransferBuffer_REAL #define SDL_GpuSetBufferName SDL_GpuSetBufferName_REAL #define SDL_GpuSetTextureName SDL_GpuSetTextureName_REAL -#define SDL_GpuSetStringMarker SDL_GpuSetStringMarker_REAL +#define SDL_GpuInsertDebugLabel SDL_GpuInsertDebugLabel_REAL +#define SDL_GpuPushDebugGroup SDL_GpuPushDebugGroup_REAL +#define SDL_GpuPopDebugGroup SDL_GpuPopDebugGroup_REAL #define SDL_GpuReleaseTexture SDL_GpuReleaseTexture_REAL #define SDL_GpuReleaseSampler SDL_GpuReleaseSampler_REAL #define SDL_GpuReleaseBuffer SDL_GpuReleaseBuffer_REAL diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h index d88737bbc3ab6..13642da5c8f68 100644 --- a/src/dynapi/SDL_dynapi_procs.h +++ b/src/dynapi/SDL_dynapi_procs.h @@ -1063,7 +1063,9 @@ SDL_DYNAPI_PROC(SDL_GpuBuffer*,SDL_GpuCreateBuffer,(SDL_GpuDevice *a, SDL_GpuBuf SDL_DYNAPI_PROC(SDL_GpuTransferBuffer*,SDL_GpuCreateTransferBuffer,(SDL_GpuDevice *a, SDL_GpuTransferBufferUsage b, Uint32 c),(a,b,c),return) SDL_DYNAPI_PROC(void,SDL_GpuSetBufferName,(SDL_GpuDevice *a, SDL_GpuBuffer *b, const char *c),(a,b,c),) SDL_DYNAPI_PROC(void,SDL_GpuSetTextureName,(SDL_GpuDevice *a, SDL_GpuTexture *b, const char *c),(a,b,c),) -SDL_DYNAPI_PROC(void,SDL_GpuSetStringMarker,(SDL_GpuCommandBuffer *a, const char *b),(a,b),) +SDL_DYNAPI_PROC(void,SDL_GpuInsertDebugLabel,(SDL_GpuCommandBuffer *a, const char *b),(a,b),) +SDL_DYNAPI_PROC(void,SDL_GpuPushDebugGroup,(SDL_GpuCommandBuffer *a, const char *b),(a,b),) +SDL_DYNAPI_PROC(void,SDL_GpuPopDebugGroup,(SDL_GpuCommandBuffer *a),(a),) SDL_DYNAPI_PROC(void,SDL_GpuReleaseTexture,(SDL_GpuDevice *a, SDL_GpuTexture *b),(a,b),) SDL_DYNAPI_PROC(void,SDL_GpuReleaseSampler,(SDL_GpuDevice *a, SDL_GpuSampler *b),(a,b),) SDL_DYNAPI_PROC(void,SDL_GpuReleaseBuffer,(SDL_GpuDevice *a, SDL_GpuBuffer *b),(a,b),) diff --git a/src/gpu/SDL_gpu.c b/src/gpu/SDL_gpu.c index 110ab1a272e29..2da62086ab041 100644 --- a/src/gpu/SDL_gpu.c +++ b/src/gpu/SDL_gpu.c @@ -484,16 +484,34 @@ void SDL_GpuSetTextureName( text); } -void SDL_GpuSetStringMarker( +void SDL_GpuInsertDebugLabel( SDL_GpuCommandBuffer *commandBuffer, const char *text) { CHECK_COMMAND_BUFFER - COMMAND_BUFFER_DEVICE->SetStringMarker( + COMMAND_BUFFER_DEVICE->InsertDebugLabel( commandBuffer, text); } +void SDL_GpuPushDebugGroup( + SDL_GpuCommandBuffer *commandBuffer, + const char *name) +{ + CHECK_COMMAND_BUFFER + COMMAND_BUFFER_DEVICE->PushDebugGroup( + commandBuffer, + name); +} + +void SDL_GpuPopDebugGroup( + SDL_GpuCommandBuffer *commandBuffer) +{ + CHECK_COMMAND_BUFFER + COMMAND_BUFFER_DEVICE->PopDebugGroup( + commandBuffer); +} + /* Disposal */ void SDL_GpuReleaseTexture( diff --git a/src/gpu/SDL_gpu_driver.h b/src/gpu/SDL_gpu_driver.h index b46920f2b1069..754aa9201a1d1 100644 --- a/src/gpu/SDL_gpu_driver.h +++ b/src/gpu/SDL_gpu_driver.h @@ -246,10 +246,17 @@ struct SDL_GpuDevice SDL_GpuTexture *texture, const char *text); - void (*SetStringMarker)( + void (*InsertDebugLabel)( SDL_GpuCommandBuffer *commandBuffer, const char *text); + void (*PushDebugGroup)( + SDL_GpuCommandBuffer *commandBuffer, + const char *name); + + void (*PopDebugGroup)( + SDL_GpuCommandBuffer *commandBuffer); + /* Disposal */ void (*ReleaseTexture)( @@ -608,7 +615,9 @@ struct SDL_GpuDevice ASSIGN_DRIVER_FUNC(CreateTransferBuffer, name) \ ASSIGN_DRIVER_FUNC(SetBufferName, name) \ ASSIGN_DRIVER_FUNC(SetTextureName, name) \ - ASSIGN_DRIVER_FUNC(SetStringMarker, name) \ + ASSIGN_DRIVER_FUNC(InsertDebugLabel, name) \ + ASSIGN_DRIVER_FUNC(PushDebugGroup, name) \ + ASSIGN_DRIVER_FUNC(PopDebugGroup, name) \ ASSIGN_DRIVER_FUNC(ReleaseTexture, name) \ ASSIGN_DRIVER_FUNC(ReleaseSampler, name) \ ASSIGN_DRIVER_FUNC(ReleaseBuffer, name) \ diff --git a/src/gpu/d3d11/SDL_gpu_d3d11.c b/src/gpu/d3d11/SDL_gpu_d3d11.c index 5d83a7ea6624a..23a3d7dea9c36 100644 --- a/src/gpu/d3d11/SDL_gpu_d3d11.c +++ b/src/gpu/d3d11/SDL_gpu_d3d11.c @@ -677,6 +677,9 @@ typedef struct D3D11CommandBuffer /* Compute Pass */ D3D11ComputePipeline *computePipeline; + /* Debug Annotation */ + ID3DUserDefinedAnnotation *annotation; + /* Resource slot state */ SDL_bool needVertexSamplerBind; @@ -761,7 +764,6 @@ struct D3D11Renderer BOOL supportsTearing; Uint8 supportsFlipDiscard; - ID3DUserDefinedAnnotation *annotation; SDL_iconv_t iconv; /* Blit */ @@ -970,6 +972,9 @@ static void D3D11_DestroyDevice( /* Release command buffer infrastructure */ for (Uint32 i = 0; i < renderer->availableCommandBufferCount; i += 1) { D3D11CommandBuffer *commandBuffer = renderer->availableCommandBuffers[i]; + if (commandBuffer->annotation) { + ID3DUserDefinedAnnotation_Release(commandBuffer->annotation); + } ID3D11DeviceContext_Release(commandBuffer->context); SDL_free(commandBuffer->usedBuffers); SDL_free(commandBuffer->usedTransferBuffers); @@ -1005,10 +1010,7 @@ static void D3D11_DestroyDevice( } SDL_free(renderer->availableFences); - /* Release the annotation/iconv, if applicable */ - if (renderer->annotation != NULL) { - ID3DUserDefinedAnnotation_Release(renderer->annotation); - } + /* Release the iconv, if applicable */ if (renderer->iconv != NULL) { SDL_iconv_close(renderer->iconv); } @@ -1776,20 +1778,14 @@ static void D3D11_SetTextureName( } } -static void D3D11_SetStringMarker( - SDL_GpuCommandBuffer *commandBuffer, - const char *text) +static SDL_bool D3D11_INTERNAL_StrToWStr( + D3D11Renderer *renderer, + const char *str, + wchar_t *wstr, + size_t wstr_size) { - D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer *)commandBuffer; - D3D11Renderer *renderer = (D3D11Renderer *)d3d11CommandBuffer->renderer; - - if (renderer->annotation == NULL) { - return; - } - - wchar_t wstr[256]; - wchar_t *out = wstr; - size_t inlen, outlen, result; + size_t inlen, result; + size_t outlen = wstr_size; if (renderer->iconv == NULL) { renderer->iconv = SDL_iconv_open("WCHAR_T", "UTF-8"); @@ -1797,13 +1793,12 @@ static void D3D11_SetStringMarker( } /* Convert... */ - inlen = SDL_strlen(text) + 1; - outlen = sizeof(wstr); + inlen = SDL_strlen(str) + 1; result = SDL_iconv( renderer->iconv, - &text, + &str, &inlen, - (char **)&out, + (char **)&wstr, &outlen); /* Check... */ @@ -1812,14 +1807,61 @@ static void D3D11_SetStringMarker( case SDL_ICONV_E2BIG: case SDL_ICONV_EILSEQ: case SDL_ICONV_EINVAL: - SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Failed to convert string marker to wchar_t!"); - return; + SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Failed to convert string to wchar_t!"); + return SDL_FALSE; default: break; } - /* Mark, finally. */ - ID3DUserDefinedAnnotation_SetMarker(renderer->annotation, wstr); + return SDL_TRUE; +} + +static void D3D11_InsertDebugLabel( + SDL_GpuCommandBuffer *commandBuffer, + const char *text) +{ + D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer *)commandBuffer; + D3D11Renderer *renderer = (D3D11Renderer *)d3d11CommandBuffer->renderer; + + if (d3d11CommandBuffer->annotation == NULL) { + return; + } + + wchar_t wstr[256]; + if (!D3D11_INTERNAL_StrToWStr(renderer, text, wstr, sizeof(wstr))) { + return; + } + + ID3DUserDefinedAnnotation_SetMarker(d3d11CommandBuffer->annotation, wstr); +} + +static void D3D11_PushDebugGroup( + SDL_GpuCommandBuffer *commandBuffer, + const char *name) +{ + D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer *)commandBuffer; + D3D11Renderer *renderer = (D3D11Renderer *)d3d11CommandBuffer->renderer; + + if (d3d11CommandBuffer->annotation == NULL) { + return; + } + + wchar_t wstr[256]; + if (!D3D11_INTERNAL_StrToWStr(renderer, name, wstr, sizeof(wstr))) { + return; + } + + ID3DUserDefinedAnnotation_BeginEvent(d3d11CommandBuffer->annotation, wstr); +} + +static void D3D11_PopDebugGroup( + SDL_GpuCommandBuffer *commandBuffer) +{ + D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer *)commandBuffer; + if (d3d11CommandBuffer->annotation == NULL) { + return; + } + ID3DUserDefinedAnnotation_EndEvent(d3d11CommandBuffer->annotation); } /* Resource Creation */ @@ -3219,7 +3261,7 @@ static void D3D11_INTERNAL_AllocateCommandBuffers( sizeof(D3D11CommandBuffer *) * renderer->availableCommandBufferCapacity); for (Uint32 i = 0; i < allocateCount; i += 1) { - commandBuffer = SDL_malloc(sizeof(D3D11CommandBuffer)); + commandBuffer = SDL_calloc(1, sizeof(D3D11CommandBuffer)); commandBuffer->renderer = renderer; /* Deferred Device Context */ @@ -3229,6 +3271,12 @@ static void D3D11_INTERNAL_AllocateCommandBuffers( &commandBuffer->context); ERROR_CHECK("Could not create deferred context"); + /* Initialize debug annotation support, if available */ + ID3D11DeviceContext_QueryInterface( + commandBuffer->context, + &D3D_IID_ID3DUserDefinedAnnotation, + (void **)&commandBuffer->annotation); + commandBuffer->initializedVertexUniformBufferCount = 0; commandBuffer->initializedFragmentUniformBufferCount = 0; commandBuffer->initializedComputeUniformBufferCount = 0; @@ -6252,17 +6300,6 @@ static SDL_GpuDevice *D3D11_CreateDevice(SDL_bool debugMode) /* Initialize miscellaneous renderer members */ renderer->debugMode = (flags & D3D11_CREATE_DEVICE_DEBUG); - /* Initialize SetStringMarker support, if available */ - res = ID3D11DeviceContext_QueryInterface( - renderer->immediateContext, - &D3D_IID_ID3DUserDefinedAnnotation, - (void **)&renderer->annotation); - - if (res < 0) { - D3D11_INTERNAL_LogError(renderer->device, "Could not get UserDefinedAnnotation", res); - renderer->annotation = NULL; - } - /* Create command buffer pool */ D3D11_INTERNAL_AllocateCommandBuffers(renderer, 2); diff --git a/src/gpu/metal/SDL_gpu_metal.m b/src/gpu/metal/SDL_gpu_metal.m index d52f99a8014c1..38146f492a6c9 100644 --- a/src/gpu/metal/SDL_gpu_metal.m +++ b/src/gpu/metal/SDL_gpu_metal.m @@ -1096,7 +1096,7 @@ static void METAL_SetTextureName( } } -static void METAL_SetStringMarker( +static void METAL_InsertDebugLabel( SDL_GpuCommandBuffer *commandBuffer, const char *text) { @@ -1110,11 +1110,46 @@ static void METAL_SetStringMarker( } else if (metalCommandBuffer->computeEncoder) { [metalCommandBuffer->computeEncoder insertDebugSignpost:label]; } else { + /* Metal doesn't have insertDebugSignpost for command buffers... */ [metalCommandBuffer->handle pushDebugGroup:label]; [metalCommandBuffer->handle popDebugGroup]; } } +static void METAL_PushDebugGroup( + SDL_GpuCommandBuffer *commandBuffer, + const char *name) +{ + MetalCommandBuffer *metalCommandBuffer = (MetalCommandBuffer *)commandBuffer; + NSString *label = @(name); + + if (metalCommandBuffer->renderEncoder) { + [metalCommandBuffer->renderEncoder pushDebugGroup:label]; + } else if (metalCommandBuffer->blitEncoder) { + [metalCommandBuffer->blitEncoder pushDebugGroup:label]; + } else if (metalCommandBuffer->computeEncoder) { + [metalCommandBuffer->computeEncoder pushDebugGroup:label]; + } else { + [metalCommandBuffer->handle pushDebugGroup:label]; + } +} + +static void METAL_PopDebugGroup( + SDL_GpuCommandBuffer *commandBuffer) +{ + MetalCommandBuffer *metalCommandBuffer = (MetalCommandBuffer *)commandBuffer; + + if (metalCommandBuffer->renderEncoder) { + [metalCommandBuffer->renderEncoder popDebugGroup]; + } else if (metalCommandBuffer->blitEncoder) { + [metalCommandBuffer->blitEncoder popDebugGroup]; + } else if (metalCommandBuffer->computeEncoder) { + [metalCommandBuffer->computeEncoder popDebugGroup]; + } else { + [metalCommandBuffer->handle popDebugGroup]; + } +} + /* Resource Creation */ static SDL_GpuSampler *METAL_CreateSampler( diff --git a/src/gpu/vulkan/SDL_gpu_vulkan.c b/src/gpu/vulkan/SDL_gpu_vulkan.c index 0eaaf81a89214..0649870741540 100644 --- a/src/gpu/vulkan/SDL_gpu_vulkan.c +++ b/src/gpu/vulkan/SDL_gpu_vulkan.c @@ -5344,7 +5344,7 @@ static void VULKAN_SetTextureName( } } -static void VULKAN_SetStringMarker( +static void VULKAN_InsertDebugLabel( SDL_GpuCommandBuffer *commandBuffer, const char *text) { @@ -5363,6 +5363,36 @@ static void VULKAN_SetStringMarker( } } +static void VULKAN_PushDebugGroup( + SDL_GpuCommandBuffer *commandBuffer, + const char *name) +{ + VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer; + VulkanRenderer *renderer = (VulkanRenderer *)vulkanCommandBuffer->renderer; + VkDebugUtilsLabelEXT labelInfo; + + if (renderer->supportsDebugUtils) { + labelInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT; + labelInfo.pNext = NULL; + labelInfo.pLabelName = name; + + renderer->vkCmdBeginDebugUtilsLabelEXT( + vulkanCommandBuffer->commandBuffer, + &labelInfo); + } +} + +static void VULKAN_PopDebugGroup( + SDL_GpuCommandBuffer *commandBuffer) +{ + VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer; + VulkanRenderer *renderer = (VulkanRenderer *)vulkanCommandBuffer->renderer; + + if (renderer->supportsDebugUtils) { + renderer->vkCmdEndDebugUtilsLabelEXT(vulkanCommandBuffer->commandBuffer); + } +} + static VulkanTextureHandle *VULKAN_INTERNAL_CreateTextureHandle( VulkanRenderer *renderer, Uint32 width, diff --git a/src/gpu/vulkan/SDL_gpu_vulkan_vkfuncs.h b/src/gpu/vulkan/SDL_gpu_vulkan_vkfuncs.h index bc6f560a0e4df..2fdaf3fb40c5a 100644 --- a/src/gpu/vulkan/SDL_gpu_vulkan_vkfuncs.h +++ b/src/gpu/vulkan/SDL_gpu_vulkan_vkfuncs.h @@ -55,8 +55,10 @@ VULKAN_INSTANCE_FUNCTION(BaseVK, VkResult, vkGetPhysicalDeviceSurfaceFormatsKHR, VULKAN_INSTANCE_FUNCTION(BaseVK, VkResult, vkGetPhysicalDeviceSurfacePresentModesKHR, (VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, Uint32 *pPresentModeCount, VkPresentModeKHR *pPresentModes)) VULKAN_INSTANCE_FUNCTION(BaseVK, VkResult, vkGetPhysicalDeviceSurfaceSupportKHR, (VkPhysicalDevice physicalDevice, Uint32 queueFamilyIndex, VkSurfaceKHR surface, VkBool32 *pSupported)) -/* Optional debug feature, used by SetStringMarker */ +/* Optional debug features */ VULKAN_INSTANCE_FUNCTION(VK_EXT_debug_utils, void, vkCmdInsertDebugUtilsLabelEXT, (VkCommandBuffer commandBuffer, const VkDebugUtilsLabelEXT *pMarkerInfo)) +VULKAN_INSTANCE_FUNCTION(VK_EXT_debug_utils, void, vkCmdBeginDebugUtilsLabelEXT, (VkCommandBuffer commandBuffer, const VkDebugUtilsLabelEXT *pMarkerInfo)) +VULKAN_INSTANCE_FUNCTION(VK_EXT_debug_utils, void, vkCmdEndDebugUtilsLabelEXT, (VkCommandBuffer commandBuffer)) VULKAN_INSTANCE_FUNCTION(VK_EXT_debug_utils, void, vkSetDebugUtilsObjectNameEXT, (VkDevice device, const VkDebugUtilsObjectNameInfoEXT *pNameInfo)) /*