Reference count window claim calls for a given GPU device

Fixes https://github.com/libsdl-org/SDL/issues/14918
This commit is contained in:
Sam Lantinga
2026-01-29 13:48:31 -08:00
parent a4fa04f2d4
commit 696c12826f
3 changed files with 68 additions and 27 deletions

View File

@@ -111,7 +111,7 @@
#define CREATE_DXGI_FACTORY1_FUNC "CreateDXGIFactory1" #define CREATE_DXGI_FACTORY1_FUNC "CreateDXGIFactory1"
#define DXGI_GET_DEBUG_INTERFACE_FUNC "DXGIGetDebugInterface" #define DXGI_GET_DEBUG_INTERFACE_FUNC "DXGIGetDebugInterface"
#define D3D12_GET_DEBUG_INTERFACE_FUNC "D3D12GetDebugInterface" #define D3D12_GET_DEBUG_INTERFACE_FUNC "D3D12GetDebugInterface"
#define WINDOW_PROPERTY_DATA "SDL_GPUD3D12WindowPropertyData" #define WINDOW_PROPERTY_DATA "SDL.internal.gpu.d3d12.data"
#define D3D_FEATURE_LEVEL_CHOICE D3D_FEATURE_LEVEL_11_0 #define D3D_FEATURE_LEVEL_CHOICE D3D_FEATURE_LEVEL_11_0
#define D3D_FEATURE_LEVEL_CHOICE_STR "11_0" #define D3D_FEATURE_LEVEL_CHOICE_STR "11_0"
#define MAX_ROOT_SIGNATURE_PARAMETERS 64 #define MAX_ROOT_SIGNATURE_PARAMETERS 64
@@ -831,6 +831,8 @@ typedef struct D3D12Sampler
typedef struct D3D12WindowData typedef struct D3D12WindowData
{ {
SDL_Window *window; SDL_Window *window;
D3D12Renderer *renderer;
int refcount;
#if (defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES)) #if (defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES))
D3D12XBOX_FRAME_PIPELINE_TOKEN frameToken; D3D12XBOX_FRAME_PIPELINE_TOKEN frameToken;
#else #else
@@ -7081,6 +7083,8 @@ static bool D3D12_ClaimWindow(
return false; return false;
} }
windowData->window = window; windowData->window = window;
windowData->renderer = renderer;
windowData->refcount = 1;
if (D3D12_INTERNAL_CreateSwapchain(renderer, windowData, SDL_GPU_SWAPCHAINCOMPOSITION_SDR, SDL_GPU_PRESENTMODE_VSYNC)) { if (D3D12_INTERNAL_CreateSwapchain(renderer, windowData, SDL_GPU_SWAPCHAINCOMPOSITION_SDR, SDL_GPU_PRESENTMODE_VSYNC)) {
SDL_SetPointerProperty(SDL_GetWindowProperties(window), WINDOW_PROPERTY_DATA, windowData); SDL_SetPointerProperty(SDL_GetWindowProperties(window), WINDOW_PROPERTY_DATA, windowData);
@@ -7101,8 +7105,11 @@ static bool D3D12_ClaimWindow(
return true; return true;
} else { } else {
SDL_free(windowData); SDL_free(windowData);
SET_STRING_ERROR_AND_RETURN("Could not create swapchain, failed to claim window!", false); return false;
} }
} else if (windowData->renderer == renderer) {
++windowData->refcount;
return true;
} else { } else {
SET_STRING_ERROR_AND_RETURN("Window already claimed", false); SET_STRING_ERROR_AND_RETURN("Window already claimed", false);
} }
@@ -7116,7 +7123,15 @@ static void D3D12_ReleaseWindow(
D3D12WindowData *windowData = D3D12_INTERNAL_FetchWindowData(window); D3D12WindowData *windowData = D3D12_INTERNAL_FetchWindowData(window);
if (windowData == NULL) { if (windowData == NULL) {
SET_STRING_ERROR_AND_RETURN("Window already unclaimed!", ); return;
}
if (windowData->renderer != renderer) {
SDL_SetError("Window not claimed by this device");
return;
}
if (windowData->refcount > 1) {
--windowData->refcount;
return;
} }
D3D12_Wait(driverData); D3D12_Wait(driverData);

View File

@@ -31,7 +31,7 @@
// Defines // Defines
#define METAL_FIRST_VERTEX_BUFFER_SLOT 14 #define METAL_FIRST_VERTEX_BUFFER_SLOT 14
#define WINDOW_PROPERTY_DATA "SDL_GPUMetalWindowPropertyData" #define WINDOW_PROPERTY_DATA "SDL.internal.gpu.metal.data"
#define SDL_GPU_SHADERSTAGE_COMPUTE 2 #define SDL_GPU_SHADERSTAGE_COMPUTE 2
#define TRACK_RESOURCE(resource, type, array, count, capacity) \ #define TRACK_RESOURCE(resource, type, array, count, capacity) \
@@ -423,6 +423,8 @@ static MTLDepthClipMode SDLToMetal_DepthClipMode(
// Structs // Structs
typedef struct MetalRenderer MetalRenderer;
typedef struct MetalTexture typedef struct MetalTexture
{ {
id<MTLTexture> handle; id<MTLTexture> handle;
@@ -452,6 +454,8 @@ typedef struct MetalFence
typedef struct MetalWindowData typedef struct MetalWindowData
{ {
SDL_Window *window; SDL_Window *window;
MetalRenderer *renderer;
int refcount;
SDL_MetalView view; SDL_MetalView view;
CAMetalLayer *layer; CAMetalLayer *layer;
SDL_GPUPresentMode presentMode; SDL_GPUPresentMode presentMode;
@@ -523,8 +527,6 @@ typedef struct MetalUniformBuffer
Uint32 drawOffset; Uint32 drawOffset;
} MetalUniformBuffer; } MetalUniformBuffer;
typedef struct MetalRenderer MetalRenderer;
typedef struct MetalCommandBuffer typedef struct MetalCommandBuffer
{ {
CommandBufferCommonHeader common; CommandBufferCommonHeader common;
@@ -3645,7 +3647,7 @@ static bool METAL_SupportsSwapchainComposition(
} }
// This function assumes that it's called from within an autorelease pool // This function assumes that it's called from within an autorelease pool
static Uint8 METAL_INTERNAL_CreateSwapchain( static bool METAL_INTERNAL_CreateSwapchain(
MetalRenderer *renderer, MetalRenderer *renderer,
MetalWindowData *windowData, MetalWindowData *windowData,
SDL_GPUSwapchainComposition swapchainComposition, SDL_GPUSwapchainComposition swapchainComposition,
@@ -3717,7 +3719,7 @@ static Uint8 METAL_INTERNAL_CreateSwapchain(
windowData->textureContainer.header.info.width = (Uint32)drawableSize.width; windowData->textureContainer.header.info.width = (Uint32)drawableSize.width;
windowData->textureContainer.header.info.height = (Uint32)drawableSize.height; windowData->textureContainer.header.info.height = (Uint32)drawableSize.height;
return 1; return true;
} }
static bool METAL_SupportsPresentMode( static bool METAL_SupportsPresentMode(
@@ -3747,6 +3749,8 @@ static bool METAL_ClaimWindow(
if (windowData == NULL) { if (windowData == NULL) {
windowData = (MetalWindowData *)SDL_calloc(1, sizeof(MetalWindowData)); windowData = (MetalWindowData *)SDL_calloc(1, sizeof(MetalWindowData));
windowData->window = window; windowData->window = window;
windowData->renderer = renderer;
windowData->refcount = 1;
if (METAL_INTERNAL_CreateSwapchain(renderer, windowData, SDL_GPU_SWAPCHAINCOMPOSITION_SDR, SDL_GPU_PRESENTMODE_VSYNC)) { if (METAL_INTERNAL_CreateSwapchain(renderer, windowData, SDL_GPU_SWAPCHAINCOMPOSITION_SDR, SDL_GPU_PRESENTMODE_VSYNC)) {
SDL_SetPointerProperty(SDL_GetWindowProperties(window), WINDOW_PROPERTY_DATA, windowData); SDL_SetPointerProperty(SDL_GetWindowProperties(window), WINDOW_PROPERTY_DATA, windowData);
@@ -3767,10 +3771,13 @@ static bool METAL_ClaimWindow(
return true; return true;
} else { } else {
SDL_free(windowData); SDL_free(windowData);
SET_STRING_ERROR_AND_RETURN("Could not create swapchain, failed to claim window", false); return false;
} }
} else if (windowData->renderer == renderer) {
++windowData->refcount;
return true;
} else { } else {
SET_ERROR_AND_RETURN("%s", "Window already claimed!", false); SET_STRING_ERROR_AND_RETURN("Window already claimed", false);
} }
} }
} }
@@ -3784,7 +3791,15 @@ static void METAL_ReleaseWindow(
MetalWindowData *windowData = METAL_INTERNAL_FetchWindowData(window); MetalWindowData *windowData = METAL_INTERNAL_FetchWindowData(window);
if (windowData == NULL) { if (windowData == NULL) {
SET_STRING_ERROR_AND_RETURN("Window is not claimed by this SDL_GPUDevice", ); return;
}
if (windowData->renderer != renderer) {
SDL_SetError("Window not claimed by this device");
return;
}
if (windowData->refcount > 1) {
--windowData->refcount;
return;
} }
METAL_Wait(driverData); METAL_Wait(driverData);

View File

@@ -70,7 +70,7 @@ typedef struct VulkanExtensions
#define LARGE_ALLOCATION_INCREMENT 67108864 // 64 MiB #define LARGE_ALLOCATION_INCREMENT 67108864 // 64 MiB
#define MAX_UBO_SECTION_SIZE 4096 // 4 KiB #define MAX_UBO_SECTION_SIZE 4096 // 4 KiB
#define DESCRIPTOR_POOL_SIZE 128 #define DESCRIPTOR_POOL_SIZE 128
#define WINDOW_PROPERTY_DATA "SDL_GPUVulkanWindowPropertyData" #define WINDOW_PROPERTY_DATA "SDL.internal.gpu.vulkan.data"
#define IDENTITY_SWIZZLE \ #define IDENTITY_SWIZZLE \
{ \ { \
@@ -432,6 +432,8 @@ static VkSamplerAddressMode SDLToVK_SamplerAddressMode[] = {
// Structures // Structures
typedef struct VulkanRenderer VulkanRenderer;
typedef struct VulkanCommandPool VulkanCommandPool;
typedef struct VulkanMemoryAllocation VulkanMemoryAllocation; typedef struct VulkanMemoryAllocation VulkanMemoryAllocation;
typedef struct VulkanBuffer VulkanBuffer; typedef struct VulkanBuffer VulkanBuffer;
typedef struct VulkanBufferContainer VulkanBufferContainer; typedef struct VulkanBufferContainer VulkanBufferContainer;
@@ -666,6 +668,8 @@ typedef struct VulkanFramebuffer
typedef struct WindowData typedef struct WindowData
{ {
SDL_Window *window; SDL_Window *window;
VulkanRenderer *renderer;
int refcount;
SDL_GPUSwapchainComposition swapchainComposition; SDL_GPUSwapchainComposition swapchainComposition;
SDL_GPUPresentMode presentMode; SDL_GPUPresentMode presentMode;
bool needsSwapchainRecreate; bool needsSwapchainRecreate;
@@ -928,10 +932,6 @@ typedef struct VulkanFencePool
Uint32 availableFenceCapacity; Uint32 availableFenceCapacity;
} VulkanFencePool; } VulkanFencePool;
typedef struct VulkanCommandPool VulkanCommandPool;
typedef struct VulkanRenderer VulkanRenderer;
typedef struct VulkanCommandBuffer typedef struct VulkanCommandBuffer
{ {
CommandBufferCommonHeader common; CommandBufferCommonHeader common;
@@ -9757,8 +9757,13 @@ static bool VULKAN_ClaimWindow(
WindowData *windowData = VULKAN_INTERNAL_FetchWindowData(window); WindowData *windowData = VULKAN_INTERNAL_FetchWindowData(window);
if (windowData == NULL) { if (windowData == NULL) {
windowData = SDL_calloc(1, sizeof(WindowData)); windowData = (WindowData *)SDL_calloc(1, sizeof(WindowData));
if (!windowData) {
return false;
}
windowData->window = window; windowData->window = window;
windowData->renderer = renderer;
windowData->refcount = 1;
windowData->presentMode = SDL_GPU_PRESENTMODE_VSYNC; windowData->presentMode = SDL_GPU_PRESENTMODE_VSYNC;
windowData->swapchainComposition = SDL_GPU_SWAPCHAINCOMPOSITION_SDR; windowData->swapchainComposition = SDL_GPU_SWAPCHAINCOMPOSITION_SDR;
@@ -9773,18 +9778,14 @@ static bool VULKAN_ClaimWindow(
#endif #endif
SDL_VideoDevice *videoDevice = SDL_GetVideoDevice(); SDL_VideoDevice *videoDevice = SDL_GetVideoDevice();
if (!videoDevice) if (!videoDevice) {
{
SDL_SetError("No video device found!");
SDL_free(windowData); SDL_free(windowData);
return false; return SDL_SetError("No video device found");
} }
if (!videoDevice->Vulkan_CreateSurface) if (!videoDevice->Vulkan_CreateSurface) {
{
SDL_SetError("Video device does not have Vulkan_CreateSurface implemented!");
SDL_free(windowData); SDL_free(windowData);
return false; return SDL_SetError("Video device does not implement Vulkan_CreateSurface");
} }
// Each window must have its own surface. // Each window must have its own surface.
@@ -9794,7 +9795,6 @@ static bool VULKAN_ClaimWindow(
renderer->instance, renderer->instance,
NULL, // FIXME: VAllocationCallbacks NULL, // FIXME: VAllocationCallbacks
&windowData->surface)) { &windowData->surface)) {
SDL_SetError("Failed to create Vulkan surface!");
SDL_free(windowData); SDL_free(windowData);
return false; return false;
} }
@@ -9830,8 +9830,11 @@ static bool VULKAN_ClaimWindow(
SDL_free(windowData); SDL_free(windowData);
return false; return false;
} }
} else if (windowData->renderer == renderer) {
++windowData->refcount;
return true;
} else { } else {
SET_STRING_ERROR_AND_RETURN("Window already claimed!", false); SET_STRING_ERROR_AND_RETURN("Window already claimed", false);
} }
} }
@@ -9846,6 +9849,14 @@ static void VULKAN_ReleaseWindow(
if (windowData == NULL) { if (windowData == NULL) {
return; return;
} }
if (windowData->renderer != renderer) {
SDL_SetError("Window not claimed by this device");
return;
}
if (windowData->refcount > 1) {
--windowData->refcount;
return;
}
VULKAN_Wait(driverData); VULKAN_Wait(driverData);