GPU: Add swapchain dimension out params (#11003)

This commit is contained in:
Evan Hemsley 2024-09-30 10:23:19 -07:00 committed by GitHub
parent b3388d5753
commit afdf325fb4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 347 additions and 285 deletions

View file

@ -3316,7 +3316,7 @@ extern SDL_DECLSPEC void SDLCALL SDL_ReleaseWindowFromGPUDevice(
* \param window an SDL_Window that has been claimed. * \param window an SDL_Window that has been claimed.
* \param swapchain_composition the desired composition of the swapchain. * \param swapchain_composition the desired composition of the swapchain.
* \param present_mode the desired present mode for the swapchain. * \param present_mode the desired present mode for the swapchain.
* \returns true if successful, false on error. * \returns true if successful, false on error; call SDL_GetError() for more information.
* *
* \since This function is available since SDL 3.0.0. * \since This function is available since SDL 3.0.0.
* *
@ -3350,20 +3350,22 @@ extern SDL_DECLSPEC SDL_GPUTextureFormat SDLCALL SDL_GetGPUSwapchainTextureForma
* When a swapchain texture is acquired on a command buffer, it will * When a swapchain texture is acquired on a command buffer, it will
* automatically be submitted for presentation when the command buffer is * automatically be submitted for presentation when the command buffer is
* submitted. The swapchain texture should only be referenced by the command * submitted. The swapchain texture should only be referenced by the command
* buffer used to acquire it. The swapchain texture handle can be NULL under * buffer used to acquire it. The swapchain texture handle can be filled in with NULL under
* certain conditions. This is not necessarily an error. If this function * certain conditions. This is not necessarily an error. If this function
* returns false then there is an error. The swapchain texture is managed by * returns false then there is an error.
* the implementation and must not be freed by the user. The texture *
* dimensions will be the width and height of the claimed window. You can * The swapchain texture is managed by
* obtain these dimensions by calling SDL_GetWindowSizeInPixels. You MUST NOT * the implementation and must not be freed by the user. You MUST NOT
* call this function from any thread other than the one that created the * call this function from any thread other than the one that created the
* window. * window.
* *
* \param command_buffer a command buffer. * \param command_buffer a command buffer.
* \param window a window that has been claimed. * \param window a window that has been claimed.
* \param swapchainTexture a pointer filled in with a swapchain texture * \param swapchain_texture a pointer filled in with a swapchain texture
* handle. * handle.
* \returns true on success, false on error. * \param swapchain_texture_width a pointer filled in with the swapchain texture width, may be NULL.
* \param swapchain_texture_height a pointer filled in with the swapchain texture height, may be NULL.
* \returns true on success, false on error; call SDL_GetError() for more information.
* *
* \since This function is available since SDL 3.0.0. * \since This function is available since SDL 3.0.0.
* *
@ -3375,7 +3377,9 @@ extern SDL_DECLSPEC SDL_GPUTextureFormat SDLCALL SDL_GetGPUSwapchainTextureForma
extern SDL_DECLSPEC bool SDLCALL SDL_AcquireGPUSwapchainTexture( extern SDL_DECLSPEC bool SDLCALL SDL_AcquireGPUSwapchainTexture(
SDL_GPUCommandBuffer *command_buffer, SDL_GPUCommandBuffer *command_buffer,
SDL_Window *window, SDL_Window *window,
SDL_GPUTexture **swapchainTexture); SDL_GPUTexture **swapchain_texture,
Uint32 *swapchain_texture_width,
Uint32 *swapchain_texture_height);
/** /**
* Submits a command buffer so its commands can be processed on the GPU. * Submits a command buffer so its commands can be processed on the GPU.

View file

@ -50,7 +50,7 @@ SDL_DYNAPI_PROC(int,SDL_swprintf,(SDL_OUT_Z_CAP(b) wchar_t *a, size_t b, SDL_PRI
// New API symbols are added at the end // New API symbols are added at the end
SDL_DYNAPI_PROC(SDL_Surface*,SDL_AcquireCameraFrame,(SDL_Camera *a, Uint64 *b),(a,b),return) SDL_DYNAPI_PROC(SDL_Surface*,SDL_AcquireCameraFrame,(SDL_Camera *a, Uint64 *b),(a,b),return)
SDL_DYNAPI_PROC(SDL_GPUCommandBuffer*,SDL_AcquireGPUCommandBuffer,(SDL_GPUDevice *a),(a),return) SDL_DYNAPI_PROC(SDL_GPUCommandBuffer*,SDL_AcquireGPUCommandBuffer,(SDL_GPUDevice *a),(a),return)
SDL_DYNAPI_PROC(bool,SDL_AcquireGPUSwapchainTexture,(SDL_GPUCommandBuffer *a, SDL_Window *b, SDL_GPUTexture **c),(a,b,c),return) SDL_DYNAPI_PROC(bool,SDL_AcquireGPUSwapchainTexture,(SDL_GPUCommandBuffer *a, SDL_Window *b, SDL_GPUTexture **c, Uint32 *d, Uint32 *e),(a,b,c,d,e),return)
SDL_DYNAPI_PROC(int,SDL_AddAtomicInt,(SDL_AtomicInt *a, int b),(a,b),return) SDL_DYNAPI_PROC(int,SDL_AddAtomicInt,(SDL_AtomicInt *a, int b),(a,b),return)
SDL_DYNAPI_PROC(bool,SDL_AddEventWatch,(SDL_EventFilter a, void *b),(a,b),return) SDL_DYNAPI_PROC(bool,SDL_AddEventWatch,(SDL_EventFilter a, void *b),(a,b),return)
SDL_DYNAPI_PROC(int,SDL_AddGamepadMapping,(const char *a),(a),return) SDL_DYNAPI_PROC(int,SDL_AddGamepadMapping,(const char *a),(a),return)

View file

@ -2613,7 +2613,9 @@ SDL_GPUTextureFormat SDL_GetGPUSwapchainTextureFormat(
bool SDL_AcquireGPUSwapchainTexture( bool SDL_AcquireGPUSwapchainTexture(
SDL_GPUCommandBuffer *command_buffer, SDL_GPUCommandBuffer *command_buffer,
SDL_Window *window, SDL_Window *window,
SDL_GPUTexture **swapchainTexture) SDL_GPUTexture **swapchain_texture,
Uint32 *swapchain_texture_width,
Uint32 *swapchain_texture_height)
{ {
if (command_buffer == NULL) { if (command_buffer == NULL) {
SDL_InvalidParamError("command_buffer"); SDL_InvalidParamError("command_buffer");
@ -2623,8 +2625,8 @@ bool SDL_AcquireGPUSwapchainTexture(
SDL_InvalidParamError("window"); SDL_InvalidParamError("window");
return false; return false;
} }
if (swapchainTexture == NULL) { if (swapchain_texture == NULL) {
SDL_InvalidParamError("swapchainTexture"); SDL_InvalidParamError("swapchain_texture");
return false; return false;
} }
@ -2636,7 +2638,9 @@ bool SDL_AcquireGPUSwapchainTexture(
return COMMAND_BUFFER_DEVICE->AcquireSwapchainTexture( return COMMAND_BUFFER_DEVICE->AcquireSwapchainTexture(
command_buffer, command_buffer,
window, window,
swapchainTexture); swapchain_texture,
swapchain_texture_width,
swapchain_texture_height);
} }
bool SDL_SubmitGPUCommandBuffer( bool SDL_SubmitGPUCommandBuffer(

View file

@ -651,7 +651,9 @@ struct SDL_GPUDevice
bool (*AcquireSwapchainTexture)( bool (*AcquireSwapchainTexture)(
SDL_GPUCommandBuffer *commandBuffer, SDL_GPUCommandBuffer *commandBuffer,
SDL_Window *window, SDL_Window *window,
SDL_GPUTexture **swapchainTexture); SDL_GPUTexture **swapchainTexture,
Uint32 *swapchainTextureWidth,
Uint32 *swapchainTextureHeight);
bool (*Submit)( bool (*Submit)(
SDL_GPUCommandBuffer *commandBuffer); SDL_GPUCommandBuffer *commandBuffer);

View file

@ -480,6 +480,8 @@ typedef struct D3D11WindowData
SDL_GPUSwapchainComposition swapchainComposition; SDL_GPUSwapchainComposition swapchainComposition;
DXGI_FORMAT swapchainFormat; DXGI_FORMAT swapchainFormat;
DXGI_COLOR_SPACE_TYPE swapchainColorSpace; DXGI_COLOR_SPACE_TYPE swapchainColorSpace;
Uint32 width;
Uint32 height;
SDL_GPUFence *inFlightFences[MAX_FRAMES_IN_FLIGHT]; SDL_GPUFence *inFlightFences[MAX_FRAMES_IN_FLIGHT];
Uint32 frameCounter; Uint32 frameCounter;
bool needsSwapchainRecreate; bool needsSwapchainRecreate;
@ -5250,6 +5252,8 @@ static bool D3D11_INTERNAL_CreateSwapchain(
windowData->texture.container = &windowData->textureContainer; windowData->texture.container = &windowData->textureContainer;
windowData->texture.containerIndex = 0; windowData->texture.containerIndex = 0;
windowData->width = w;
windowData->height = h;
return true; return true;
} }
@ -5288,6 +5292,8 @@ static bool D3D11_INTERNAL_ResizeSwapchain(
windowData->textureContainer.header.info.width = w; windowData->textureContainer.header.info.width = w;
windowData->textureContainer.header.info.height = h; windowData->textureContainer.header.info.height = h;
windowData->width = w;
windowData->height = h;
windowData->needsSwapchainRecreate = !result; windowData->needsSwapchainRecreate = !result;
return result; return result;
} }
@ -5469,7 +5475,9 @@ static void D3D11_ReleaseWindow(
static bool D3D11_AcquireSwapchainTexture( static bool D3D11_AcquireSwapchainTexture(
SDL_GPUCommandBuffer *commandBuffer, SDL_GPUCommandBuffer *commandBuffer,
SDL_Window *window, SDL_Window *window,
SDL_GPUTexture **swapchainTexture) SDL_GPUTexture **swapchainTexture,
Uint32 *swapchainTextureWidth,
Uint32 *swapchainTextureHeight)
{ {
D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer *)commandBuffer; D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer *)commandBuffer;
D3D11Renderer *renderer = (D3D11Renderer *)d3d11CommandBuffer->renderer; D3D11Renderer *renderer = (D3D11Renderer *)d3d11CommandBuffer->renderer;
@ -5477,6 +5485,12 @@ static bool D3D11_AcquireSwapchainTexture(
HRESULT res; HRESULT res;
*swapchainTexture = NULL; *swapchainTexture = NULL;
if (swapchainTextureWidth) {
*swapchainTextureWidth = 0;
}
if (swapchainTextureHeight) {
*swapchainTextureHeight = 0;
}
windowData = D3D11_INTERNAL_FetchWindowData(window); windowData = D3D11_INTERNAL_FetchWindowData(window);
if (windowData == NULL) { if (windowData == NULL) {
@ -5489,6 +5503,13 @@ static bool D3D11_AcquireSwapchainTexture(
} }
} }
if (swapchainTextureWidth) {
*swapchainTextureWidth = windowData->width;
}
if (swapchainTextureHeight) {
*swapchainTextureHeight = windowData->height;
}
if (windowData->inFlightFences[windowData->frameCounter] != NULL) { if (windowData->inFlightFences[windowData->frameCounter] != NULL) {
if (windowData->presentMode == SDL_GPU_PRESENTMODE_VSYNC) { if (windowData->presentMode == SDL_GPU_PRESENTMODE_VSYNC) {
// In VSYNC mode, block until the least recent presented frame is done // In VSYNC mode, block until the least recent presented frame is done

View file

@ -554,6 +554,8 @@ typedef struct D3D12WindowData
D3D12TextureContainer textureContainers[MAX_FRAMES_IN_FLIGHT]; D3D12TextureContainer textureContainers[MAX_FRAMES_IN_FLIGHT];
SDL_GPUFence *inFlightFences[MAX_FRAMES_IN_FLIGHT]; SDL_GPUFence *inFlightFences[MAX_FRAMES_IN_FLIGHT];
Uint32 width;
Uint32 height;
bool needsSwapchainRecreate; bool needsSwapchainRecreate;
} D3D12WindowData; } D3D12WindowData;
@ -6331,6 +6333,8 @@ static bool D3D12_INTERNAL_ResizeSwapchain(
} }
} }
windowData->width = w;
windowData->height = h;
windowData->needsSwapchainRecreate = false; windowData->needsSwapchainRecreate = false;
return true; return true;
} }
@ -6382,6 +6386,9 @@ static bool D3D12_INTERNAL_CreateSwapchain(
swapchainFormat = SwapchainCompositionToTextureFormat[swapchainComposition]; swapchainFormat = SwapchainCompositionToTextureFormat[swapchainComposition];
int w, h;
SDL_GetWindowSizeInPixels(windowData->window, &w, &h);
// Initialize the swapchain buffer descriptor // Initialize the swapchain buffer descriptor
swapchainDesc.Width = 0; swapchainDesc.Width = 0;
swapchainDesc.Height = 0; swapchainDesc.Height = 0;
@ -6477,6 +6484,8 @@ static bool D3D12_INTERNAL_CreateSwapchain(
windowData->swapchainComposition = swapchainComposition; windowData->swapchainComposition = swapchainComposition;
windowData->swapchainColorSpace = SwapchainCompositionToColorSpace[swapchainComposition]; windowData->swapchainColorSpace = SwapchainCompositionToColorSpace[swapchainComposition];
windowData->frameCounter = 0; windowData->frameCounter = 0;
windowData->width = w;
windowData->height = h;
// Precache blit pipelines for the swapchain format // Precache blit pipelines for the swapchain format
for (Uint32 i = 0; i < 5; i += 1) { for (Uint32 i = 0; i < 5; i += 1) {
@ -6907,7 +6916,9 @@ static SDL_GPUCommandBuffer *D3D12_AcquireCommandBuffer(
static bool D3D12_AcquireSwapchainTexture( static bool D3D12_AcquireSwapchainTexture(
SDL_GPUCommandBuffer *commandBuffer, SDL_GPUCommandBuffer *commandBuffer,
SDL_Window *window, SDL_Window *window,
SDL_GPUTexture **swapchainTexture) SDL_GPUTexture **swapchainTexture,
Uint32 *swapchainTextureWidth,
Uint32 *swapchainTextureHeight)
{ {
D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer; D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
D3D12Renderer *renderer = d3d12CommandBuffer->renderer; D3D12Renderer *renderer = d3d12CommandBuffer->renderer;
@ -6916,6 +6927,12 @@ static bool D3D12_AcquireSwapchainTexture(
HRESULT res; HRESULT res;
*swapchainTexture = NULL; *swapchainTexture = NULL;
if (swapchainTextureWidth) {
*swapchainTextureWidth = 0;
}
if (swapchainTextureHeight) {
*swapchainTextureHeight = 0;
}
windowData = D3D12_INTERNAL_FetchWindowData(window); windowData = D3D12_INTERNAL_FetchWindowData(window);
if (windowData == NULL) { if (windowData == NULL) {
@ -6928,6 +6945,13 @@ static bool D3D12_AcquireSwapchainTexture(
} }
} }
if (swapchainTextureWidth) {
*swapchainTextureWidth = windowData->width;
}
if (swapchainTextureHeight) {
*swapchainTextureHeight = windowData->height;
}
if (windowData->inFlightFences[windowData->frameCounter] != NULL) { if (windowData->inFlightFences[windowData->frameCounter] != NULL) {
if (windowData->present_mode == SDL_GPU_PRESENTMODE_VSYNC) { if (windowData->present_mode == SDL_GPU_PRESENTMODE_VSYNC) {
// In VSYNC mode, block until the least recent presented frame is done // In VSYNC mode, block until the least recent presented frame is done

View file

@ -3590,7 +3590,9 @@ static void METAL_ReleaseWindow(
static bool METAL_AcquireSwapchainTexture( static bool METAL_AcquireSwapchainTexture(
SDL_GPUCommandBuffer *commandBuffer, SDL_GPUCommandBuffer *commandBuffer,
SDL_Window *window, SDL_Window *window,
SDL_GPUTexture **texture) SDL_GPUTexture **texture,
Uint32 *swapchainTextureWidth,
Uint32 *swapchainTextureHeight)
{ {
@autoreleasepool { @autoreleasepool {
MetalCommandBuffer *metalCommandBuffer = (MetalCommandBuffer *)commandBuffer; MetalCommandBuffer *metalCommandBuffer = (MetalCommandBuffer *)commandBuffer;
@ -3599,6 +3601,12 @@ static bool METAL_AcquireSwapchainTexture(
CGSize drawableSize; CGSize drawableSize;
*texture = NULL; *texture = NULL;
if (swapchainTextureWidth) {
*swapchainTextureWidth = 0;
}
if (swapchainTextureHeight) {
*swapchainTextureHeight = 0;
}
windowData = METAL_INTERNAL_FetchWindowData(window); windowData = METAL_INTERNAL_FetchWindowData(window);
if (windowData == NULL) { if (windowData == NULL) {
@ -3613,6 +3621,12 @@ static bool METAL_AcquireSwapchainTexture(
drawableSize = windowData->layer.drawableSize; drawableSize = windowData->layer.drawableSize;
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;
if (swapchainTextureWidth) {
*swapchainTextureWidth = (Uint32)drawableSize.width;
}
if (swapchainTextureHeight) {
*swapchainTextureHeight = (Uint32)drawableSize.height;
}
// Set up presentation // Set up presentation
if (metalCommandBuffer->windowDataCount == metalCommandBuffer->windowDataCapacity) { if (metalCommandBuffer->windowDataCount == metalCommandBuffer->windowDataCapacity) {

View file

@ -665,8 +665,13 @@ typedef struct VulkanFramebuffer
SDL_AtomicInt referenceCount; SDL_AtomicInt referenceCount;
} VulkanFramebuffer; } VulkanFramebuffer;
typedef struct VulkanSwapchainData typedef struct WindowData
{ {
SDL_Window *window;
SDL_GPUSwapchainComposition swapchainComposition;
SDL_GPUPresentMode presentMode;
bool needsSwapchainRecreate;
// Window surface // Window surface
VkSurfaceKHR surface; VkSurfaceKHR surface;
@ -675,12 +680,13 @@ typedef struct VulkanSwapchainData
VkFormat format; VkFormat format;
VkColorSpaceKHR colorSpace; VkColorSpaceKHR colorSpace;
VkComponentMapping swapchainSwizzle; VkComponentMapping swapchainSwizzle;
VkPresentModeKHR presentMode;
bool usingFallbackFormat; bool usingFallbackFormat;
// Swapchain images // Swapchain images
VulkanTextureContainer *textureContainers; // use containers so that swapchain textures can use the same API as other textures VulkanTextureContainer *textureContainers; // use containers so that swapchain textures can use the same API as other textures
Uint32 imageCount; Uint32 imageCount;
Uint32 width;
Uint32 height;
// Synchronization primitives // Synchronization primitives
VkSemaphore imageAvailableSemaphore[MAX_FRAMES_IN_FLIGHT]; VkSemaphore imageAvailableSemaphore[MAX_FRAMES_IN_FLIGHT];
@ -688,15 +694,6 @@ typedef struct VulkanSwapchainData
SDL_GPUFence *inFlightFences[MAX_FRAMES_IN_FLIGHT]; SDL_GPUFence *inFlightFences[MAX_FRAMES_IN_FLIGHT];
Uint32 frameCounter; Uint32 frameCounter;
} VulkanSwapchainData;
typedef struct WindowData
{
SDL_Window *window;
SDL_GPUSwapchainComposition swapchainComposition;
SDL_GPUPresentMode presentMode;
VulkanSwapchainData *swapchainData;
bool needsSwapchainRecreate;
} WindowData; } WindowData;
typedef struct SwapchainSupportDetails typedef struct SwapchainSupportDetails
@ -1863,7 +1860,7 @@ static Uint8 VULKAN_INTERNAL_BindImageMemory(
SDL_UnlockMutex(usedRegion->allocation->memoryLock); SDL_UnlockMutex(usedRegion->allocation->memoryLock);
CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkBindBufferMemory, 0) CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkBindImageMemory, 0)
return 1; return 1;
} }
@ -3110,57 +3107,61 @@ static void VULKAN_INTERNAL_DestroySwapchain(
WindowData *windowData) WindowData *windowData)
{ {
Uint32 i; Uint32 i;
VulkanSwapchainData *swapchainData;
if (windowData == NULL) { if (windowData == NULL) {
return; return;
} }
swapchainData = windowData->swapchainData; for (i = 0; i < windowData->imageCount; i += 1) {
if (swapchainData == NULL) {
return;
}
for (i = 0; i < swapchainData->imageCount; i += 1) {
VULKAN_INTERNAL_RemoveFramebuffersContainingView( VULKAN_INTERNAL_RemoveFramebuffersContainingView(
renderer, renderer,
swapchainData->textureContainers[i].activeTexture->subresources[0].renderTargetViews[0]); windowData->textureContainers[i].activeTexture->subresources[0].renderTargetViews[0]);
renderer->vkDestroyImageView( renderer->vkDestroyImageView(
renderer->logicalDevice, renderer->logicalDevice,
swapchainData->textureContainers[i].activeTexture->subresources[0].renderTargetViews[0], windowData->textureContainers[i].activeTexture->subresources[0].renderTargetViews[0],
NULL); NULL);
SDL_free(swapchainData->textureContainers[i].activeTexture->subresources[0].renderTargetViews); SDL_free(windowData->textureContainers[i].activeTexture->subresources[0].renderTargetViews);
SDL_free(swapchainData->textureContainers[i].activeTexture->subresources); SDL_free(windowData->textureContainers[i].activeTexture->subresources);
SDL_free(swapchainData->textureContainers[i].activeTexture); SDL_free(windowData->textureContainers[i].activeTexture);
} }
windowData->imageCount = 0;
SDL_free(swapchainData->textureContainers); SDL_free(windowData->textureContainers);
windowData->textureContainers = NULL;
if (windowData->swapchain) {
renderer->vkDestroySwapchainKHR( renderer->vkDestroySwapchainKHR(
renderer->logicalDevice, renderer->logicalDevice,
swapchainData->swapchain, windowData->swapchain,
NULL);
renderer->vkDestroySurfaceKHR(
renderer->instance,
swapchainData->surface,
NULL);
for (i = 0; i < MAX_FRAMES_IN_FLIGHT; i += 1) {
renderer->vkDestroySemaphore(
renderer->logicalDevice,
swapchainData->imageAvailableSemaphore[i],
NULL);
renderer->vkDestroySemaphore(
renderer->logicalDevice,
swapchainData->renderFinishedSemaphore[i],
NULL); NULL);
windowData->swapchain = VK_NULL_HANDLE;
} }
windowData->swapchainData = NULL; if (windowData->surface) {
SDL_free(swapchainData); renderer->vkDestroySurfaceKHR(
renderer->instance,
windowData->surface,
NULL);
windowData->surface = VK_NULL_HANDLE;
}
for (i = 0; i < MAX_FRAMES_IN_FLIGHT; i += 1) {
if (windowData->imageAvailableSemaphore[i]) {
renderer->vkDestroySemaphore(
renderer->logicalDevice,
windowData->imageAvailableSemaphore[i],
NULL);
windowData->imageAvailableSemaphore[i] = VK_NULL_HANDLE;
}
if (windowData->renderFinishedSemaphore[i]) {
renderer->vkDestroySemaphore(
renderer->logicalDevice,
windowData->renderFinishedSemaphore[i],
NULL);
windowData->renderFinishedSemaphore[i] = VK_NULL_HANDLE;
}
}
} }
static void VULKAN_INTERNAL_DestroyGraphicsPipelineResourceLayout( static void VULKAN_INTERNAL_DestroyGraphicsPipelineResourceLayout(
@ -4360,12 +4361,21 @@ static bool VULKAN_INTERNAL_VerifySwapPresentMode(
return false; return false;
} }
static bool VULKAN_INTERNAL_CreateSwapchain( /* It would be nice if VULKAN_INTERNAL_CreateSwapchain could return a bool.
* Unfortunately, some Win32 NVIDIA drivers are stupid
* and will return surface extents of (0, 0)
* in certain edge cases, and the swapchain extents are not allowed to be 0.
* In this case, the client probably still wants to claim the window
* or recreate the swapchain, so we should return 2 to indicate retry.
* -cosmonaut
*/
#define VULKAN_INTERNAL_TRY_AGAIN 2
static Uint32 VULKAN_INTERNAL_CreateSwapchain(
VulkanRenderer *renderer, VulkanRenderer *renderer,
WindowData *windowData) WindowData *windowData)
{ {
VkResult vulkanResult; VkResult vulkanResult;
VulkanSwapchainData *swapchainData;
VkSwapchainCreateInfoKHR swapchainCreateInfo; VkSwapchainCreateInfoKHR swapchainCreateInfo;
VkImage *swapchainImages; VkImage *swapchainImages;
VkSemaphoreCreateInfo semaphoreCreateInfo; VkSemaphoreCreateInfo semaphoreCreateInfo;
@ -4377,18 +4387,15 @@ static bool VULKAN_INTERNAL_CreateSwapchain(
SDL_assert(_this && _this->Vulkan_CreateSurface); SDL_assert(_this && _this->Vulkan_CreateSurface);
swapchainData = SDL_malloc(sizeof(VulkanSwapchainData)); windowData->frameCounter = 0;
swapchainData->frameCounter = 0;
// Each swapchain must have its own surface. // Each swapchain must have its own surface.
if (!_this->Vulkan_CreateSurface( if (!_this->Vulkan_CreateSurface(
_this, _this,
windowData->window, windowData->window,
renderer->instance, renderer->instance,
NULL, // FIXME: VAllocationCallbacks NULL, // FIXME: VAllocationCallbacks
&swapchainData->surface)) { &windowData->surface)) {
SDL_free(swapchainData);
SDL_LogError( SDL_LogError(
SDL_LOG_CATEGORY_GPU, SDL_LOG_CATEGORY_GPU,
"Vulkan_CreateSurface failed: %s", "Vulkan_CreateSurface failed: %s",
@ -4399,11 +4406,11 @@ static bool VULKAN_INTERNAL_CreateSwapchain(
if (!VULKAN_INTERNAL_QuerySwapchainSupport( if (!VULKAN_INTERNAL_QuerySwapchainSupport(
renderer, renderer,
renderer->physicalDevice, renderer->physicalDevice,
swapchainData->surface, windowData->surface,
&swapchainSupportDetails)) { &swapchainSupportDetails)) {
renderer->vkDestroySurfaceKHR( renderer->vkDestroySurfaceKHR(
renderer->instance, renderer->instance,
swapchainData->surface, windowData->surface,
NULL); NULL);
if (swapchainSupportDetails.formatsLength > 0) { if (swapchainSupportDetails.formatsLength > 0) {
SDL_free(swapchainSupportDetails.formats); SDL_free(swapchainSupportDetails.formats);
@ -4411,61 +4418,41 @@ static bool VULKAN_INTERNAL_CreateSwapchain(
if (swapchainSupportDetails.presentModesLength > 0) { if (swapchainSupportDetails.presentModesLength > 0) {
SDL_free(swapchainSupportDetails.presentModes); SDL_free(swapchainSupportDetails.presentModes);
} }
SDL_free(swapchainData);
return false;
}
if (swapchainSupportDetails.capabilities.currentExtent.width == 0 ||
swapchainSupportDetails.capabilities.currentExtent.height == 0) {
// Not an error, just minimize behavior!
renderer->vkDestroySurfaceKHR(
renderer->instance,
swapchainData->surface,
NULL);
if (swapchainSupportDetails.formatsLength > 0) {
SDL_free(swapchainSupportDetails.formats);
}
if (swapchainSupportDetails.presentModesLength > 0) {
SDL_free(swapchainSupportDetails.presentModes);
}
SDL_free(swapchainData);
return false; return false;
} }
// Verify that we can use the requested composition and present mode // Verify that we can use the requested composition and present mode
windowData->format = SwapchainCompositionToFormat[windowData->swapchainComposition];
swapchainData->format = SwapchainCompositionToFormat[windowData->swapchainComposition]; windowData->colorSpace = SwapchainCompositionToColorSpace[windowData->swapchainComposition];
swapchainData->colorSpace = SwapchainCompositionToColorSpace[windowData->swapchainComposition]; windowData->swapchainSwizzle = SwapchainCompositionSwizzle[windowData->swapchainComposition];
swapchainData->swapchainSwizzle = SwapchainCompositionSwizzle[windowData->swapchainComposition]; windowData->usingFallbackFormat = false;
swapchainData->usingFallbackFormat = false;
hasValidSwapchainComposition = VULKAN_INTERNAL_VerifySwapSurfaceFormat( hasValidSwapchainComposition = VULKAN_INTERNAL_VerifySwapSurfaceFormat(
swapchainData->format, windowData->format,
swapchainData->colorSpace, windowData->colorSpace,
swapchainSupportDetails.formats, swapchainSupportDetails.formats,
swapchainSupportDetails.formatsLength); swapchainSupportDetails.formatsLength);
if (!hasValidSwapchainComposition) { if (!hasValidSwapchainComposition) {
// Let's try again with the fallback format... // Let's try again with the fallback format...
swapchainData->format = SwapchainCompositionToFallbackFormat[windowData->swapchainComposition]; windowData->format = SwapchainCompositionToFallbackFormat[windowData->swapchainComposition];
swapchainData->usingFallbackFormat = true; windowData->usingFallbackFormat = true;
hasValidSwapchainComposition = VULKAN_INTERNAL_VerifySwapSurfaceFormat( hasValidSwapchainComposition = VULKAN_INTERNAL_VerifySwapSurfaceFormat(
swapchainData->format, windowData->format,
swapchainData->colorSpace, windowData->colorSpace,
swapchainSupportDetails.formats, swapchainSupportDetails.formats,
swapchainSupportDetails.formatsLength); swapchainSupportDetails.formatsLength);
} }
swapchainData->presentMode = SDLToVK_PresentMode[windowData->presentMode];
hasValidPresentMode = VULKAN_INTERNAL_VerifySwapPresentMode( hasValidPresentMode = VULKAN_INTERNAL_VerifySwapPresentMode(
swapchainData->presentMode, SDLToVK_PresentMode[windowData->presentMode],
swapchainSupportDetails.presentModes, swapchainSupportDetails.presentModes,
swapchainSupportDetails.presentModesLength); swapchainSupportDetails.presentModesLength);
if (!hasValidSwapchainComposition || !hasValidPresentMode) { if (!hasValidSwapchainComposition || !hasValidPresentMode) {
renderer->vkDestroySurfaceKHR( renderer->vkDestroySurfaceKHR(
renderer->instance, renderer->instance,
swapchainData->surface, windowData->surface,
NULL); NULL);
if (swapchainSupportDetails.formatsLength > 0) { if (swapchainSupportDetails.formatsLength > 0) {
@ -4476,8 +4463,6 @@ static bool VULKAN_INTERNAL_CreateSwapchain(
SDL_free(swapchainSupportDetails.presentModes); SDL_free(swapchainSupportDetails.presentModes);
} }
SDL_free(swapchainData);
if (!hasValidSwapchainComposition) { if (!hasValidSwapchainComposition) {
SET_STRING_ERROR_AND_RETURN("Device does not support requested swapchain composition!", false); SET_STRING_ERROR_AND_RETURN("Device does not support requested swapchain composition!", false);
} }
@ -4487,6 +4472,22 @@ static bool VULKAN_INTERNAL_CreateSwapchain(
return false; return false;
} }
// NVIDIA + Win32 can return 0 extent when the window is minimized. Try again!
if (swapchainSupportDetails.capabilities.currentExtent.width == 0 ||
swapchainSupportDetails.capabilities.currentExtent.height == 0) {
renderer->vkDestroySurfaceKHR(
renderer->instance,
windowData->surface,
NULL);
if (swapchainSupportDetails.formatsLength > 0) {
SDL_free(swapchainSupportDetails.formats);
}
if (swapchainSupportDetails.presentModesLength > 0) {
SDL_free(swapchainSupportDetails.presentModes);
}
return VULKAN_INTERNAL_TRY_AGAIN;
}
// Sync now to be sure that our swapchain size is correct // Sync now to be sure that our swapchain size is correct
SDL_SyncWindow(windowData->window); SDL_SyncWindow(windowData->window);
SDL_GetWindowSizeInPixels( SDL_GetWindowSizeInPixels(
@ -4494,34 +4495,36 @@ static bool VULKAN_INTERNAL_CreateSwapchain(
&drawableWidth, &drawableWidth,
&drawableHeight); &drawableHeight);
swapchainData->imageCount = MAX_FRAMES_IN_FLIGHT; windowData->imageCount = MAX_FRAMES_IN_FLIGHT;
windowData->width = drawableWidth;
windowData->height = drawableHeight;
if (swapchainSupportDetails.capabilities.maxImageCount > 0 && if (swapchainSupportDetails.capabilities.maxImageCount > 0 &&
swapchainData->imageCount > swapchainSupportDetails.capabilities.maxImageCount) { windowData->imageCount > swapchainSupportDetails.capabilities.maxImageCount) {
swapchainData->imageCount = swapchainSupportDetails.capabilities.maxImageCount; windowData->imageCount = swapchainSupportDetails.capabilities.maxImageCount;
} }
if (swapchainData->imageCount < swapchainSupportDetails.capabilities.minImageCount) { if (windowData->imageCount < swapchainSupportDetails.capabilities.minImageCount) {
swapchainData->imageCount = swapchainSupportDetails.capabilities.minImageCount; windowData->imageCount = swapchainSupportDetails.capabilities.minImageCount;
} }
if (swapchainData->presentMode == VK_PRESENT_MODE_MAILBOX_KHR) { if (windowData->presentMode == SDL_GPU_PRESENTMODE_MAILBOX) {
/* Required for proper triple-buffering. /* Required for proper triple-buffering.
* Note that this is below the above maxImageCount check! * Note that this is below the above maxImageCount check!
* If the driver advertises MAILBOX but does not support 3 swap * If the driver advertises MAILBOX but does not support 3 swap
* images, it's not real mailbox support, so let it fail hard. * images, it's not real mailbox support, so let it fail hard.
* -flibit * -flibit
*/ */
swapchainData->imageCount = SDL_max(swapchainData->imageCount, 3); windowData->imageCount = SDL_max(windowData->imageCount, 3);
} }
swapchainCreateInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; swapchainCreateInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
swapchainCreateInfo.pNext = NULL; swapchainCreateInfo.pNext = NULL;
swapchainCreateInfo.flags = 0; swapchainCreateInfo.flags = 0;
swapchainCreateInfo.surface = swapchainData->surface; swapchainCreateInfo.surface = windowData->surface;
swapchainCreateInfo.minImageCount = swapchainData->imageCount; swapchainCreateInfo.minImageCount = windowData->imageCount;
swapchainCreateInfo.imageFormat = swapchainData->format; swapchainCreateInfo.imageFormat = windowData->format;
swapchainCreateInfo.imageColorSpace = swapchainData->colorSpace; swapchainCreateInfo.imageColorSpace = windowData->colorSpace;
swapchainCreateInfo.imageExtent.width = drawableWidth; swapchainCreateInfo.imageExtent.width = drawableWidth;
swapchainCreateInfo.imageExtent.height = drawableHeight; swapchainCreateInfo.imageExtent.height = drawableHeight;
swapchainCreateInfo.imageArrayLayers = 1; swapchainCreateInfo.imageArrayLayers = 1;
@ -4533,7 +4536,7 @@ static bool VULKAN_INTERNAL_CreateSwapchain(
swapchainCreateInfo.pQueueFamilyIndices = NULL; swapchainCreateInfo.pQueueFamilyIndices = NULL;
swapchainCreateInfo.preTransform = swapchainSupportDetails.capabilities.currentTransform; swapchainCreateInfo.preTransform = swapchainSupportDetails.capabilities.currentTransform;
swapchainCreateInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; swapchainCreateInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
swapchainCreateInfo.presentMode = swapchainData->presentMode; swapchainCreateInfo.presentMode = SDLToVK_PresentMode[windowData->presentMode];
swapchainCreateInfo.clipped = VK_TRUE; swapchainCreateInfo.clipped = VK_TRUE;
swapchainCreateInfo.oldSwapchain = VK_NULL_HANDLE; swapchainCreateInfo.oldSwapchain = VK_NULL_HANDLE;
@ -4541,7 +4544,7 @@ static bool VULKAN_INTERNAL_CreateSwapchain(
renderer->logicalDevice, renderer->logicalDevice,
&swapchainCreateInfo, &swapchainCreateInfo,
NULL, NULL,
&swapchainData->swapchain); &windowData->swapchain);
if (swapchainSupportDetails.formatsLength > 0) { if (swapchainSupportDetails.formatsLength > 0) {
SDL_free(swapchainSupportDetails.formats); SDL_free(swapchainSupportDetails.formats);
@ -4553,87 +4556,83 @@ static bool VULKAN_INTERNAL_CreateSwapchain(
if (vulkanResult != VK_SUCCESS) { if (vulkanResult != VK_SUCCESS) {
renderer->vkDestroySurfaceKHR( renderer->vkDestroySurfaceKHR(
renderer->instance, renderer->instance,
swapchainData->surface, windowData->surface,
NULL); NULL);
SDL_free(swapchainData);
CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkCreateSwapchainKHR, false) CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkCreateSwapchainKHR, false)
} }
renderer->vkGetSwapchainImagesKHR( vulkanResult = renderer->vkGetSwapchainImagesKHR(
renderer->logicalDevice, renderer->logicalDevice,
swapchainData->swapchain, windowData->swapchain,
&swapchainData->imageCount, &windowData->imageCount,
NULL); NULL);
CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkGetSwapchainImagesKHR, false) CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkGetSwapchainImagesKHR, false)
swapchainData->textureContainers = SDL_malloc( windowData->textureContainers = SDL_malloc(
sizeof(VulkanTextureContainer) * swapchainData->imageCount); sizeof(VulkanTextureContainer) * windowData->imageCount);
if (!swapchainData->textureContainers) { if (!windowData->textureContainers) {
renderer->vkDestroySurfaceKHR( renderer->vkDestroySurfaceKHR(
renderer->instance, renderer->instance,
swapchainData->surface, windowData->surface,
NULL); NULL);
SDL_free(swapchainData);
return false; return false;
} }
swapchainImages = SDL_stack_alloc(VkImage, swapchainData->imageCount); swapchainImages = SDL_stack_alloc(VkImage, windowData->imageCount);
vulkanResult = renderer->vkGetSwapchainImagesKHR( vulkanResult = renderer->vkGetSwapchainImagesKHR(
renderer->logicalDevice, renderer->logicalDevice,
swapchainData->swapchain, windowData->swapchain,
&swapchainData->imageCount, &windowData->imageCount,
swapchainImages); swapchainImages);
CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkGetSwapchainImagesKHR, false) CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkGetSwapchainImagesKHR, false)
for (i = 0; i < swapchainData->imageCount; i += 1) { for (i = 0; i < windowData->imageCount; i += 1) {
// Initialize dummy container // Initialize dummy container
SDL_zero(swapchainData->textureContainers[i]); SDL_zero(windowData->textureContainers[i]);
swapchainData->textureContainers[i].canBeCycled = false; windowData->textureContainers[i].canBeCycled = false;
swapchainData->textureContainers[i].header.info.width = drawableWidth; windowData->textureContainers[i].header.info.width = drawableWidth;
swapchainData->textureContainers[i].header.info.height = drawableHeight; windowData->textureContainers[i].header.info.height = drawableHeight;
swapchainData->textureContainers[i].header.info.layer_count_or_depth = 1; windowData->textureContainers[i].header.info.layer_count_or_depth = 1;
swapchainData->textureContainers[i].header.info.format = SwapchainCompositionToSDLFormat( windowData->textureContainers[i].header.info.format = SwapchainCompositionToSDLFormat(
windowData->swapchainComposition, windowData->swapchainComposition,
swapchainData->usingFallbackFormat); windowData->usingFallbackFormat);
swapchainData->textureContainers[i].header.info.type = SDL_GPU_TEXTURETYPE_2D; windowData->textureContainers[i].header.info.type = SDL_GPU_TEXTURETYPE_2D;
swapchainData->textureContainers[i].header.info.num_levels = 1; windowData->textureContainers[i].header.info.num_levels = 1;
swapchainData->textureContainers[i].header.info.sample_count = SDL_GPU_SAMPLECOUNT_1; windowData->textureContainers[i].header.info.sample_count = SDL_GPU_SAMPLECOUNT_1;
swapchainData->textureContainers[i].header.info.usage = SDL_GPU_TEXTUREUSAGE_COLOR_TARGET; windowData->textureContainers[i].header.info.usage = SDL_GPU_TEXTUREUSAGE_COLOR_TARGET;
swapchainData->textureContainers[i].activeTexture = SDL_malloc(sizeof(VulkanTexture)); windowData->textureContainers[i].activeTexture = SDL_malloc(sizeof(VulkanTexture));
swapchainData->textureContainers[i].activeTexture->image = swapchainImages[i]; windowData->textureContainers[i].activeTexture->image = swapchainImages[i];
// Swapchain memory is managed by the driver // Swapchain memory is managed by the driver
swapchainData->textureContainers[i].activeTexture->usedRegion = NULL; windowData->textureContainers[i].activeTexture->usedRegion = NULL;
swapchainData->textureContainers[i].activeTexture->swizzle = swapchainData->swapchainSwizzle; windowData->textureContainers[i].activeTexture->swizzle = windowData->swapchainSwizzle;
swapchainData->textureContainers[i].activeTexture->aspectFlags = VK_IMAGE_ASPECT_COLOR_BIT; windowData->textureContainers[i].activeTexture->aspectFlags = VK_IMAGE_ASPECT_COLOR_BIT;
swapchainData->textureContainers[i].activeTexture->depth = 1; windowData->textureContainers[i].activeTexture->depth = 1;
swapchainData->textureContainers[i].activeTexture->usage = SDL_GPU_TEXTUREUSAGE_COLOR_TARGET; windowData->textureContainers[i].activeTexture->usage = SDL_GPU_TEXTUREUSAGE_COLOR_TARGET;
swapchainData->textureContainers[i].activeTexture->container = &swapchainData->textureContainers[i]; windowData->textureContainers[i].activeTexture->container = &windowData->textureContainers[i];
SDL_SetAtomicInt(&swapchainData->textureContainers[i].activeTexture->referenceCount, 0); SDL_SetAtomicInt(&windowData->textureContainers[i].activeTexture->referenceCount, 0);
// Create slice // Create slice
swapchainData->textureContainers[i].activeTexture->subresourceCount = 1; windowData->textureContainers[i].activeTexture->subresourceCount = 1;
swapchainData->textureContainers[i].activeTexture->subresources = SDL_malloc(sizeof(VulkanTextureSubresource)); windowData->textureContainers[i].activeTexture->subresources = SDL_malloc(sizeof(VulkanTextureSubresource));
swapchainData->textureContainers[i].activeTexture->subresources[0].parent = swapchainData->textureContainers[i].activeTexture; windowData->textureContainers[i].activeTexture->subresources[0].parent = windowData->textureContainers[i].activeTexture;
swapchainData->textureContainers[i].activeTexture->subresources[0].layer = 0; windowData->textureContainers[i].activeTexture->subresources[0].layer = 0;
swapchainData->textureContainers[i].activeTexture->subresources[0].level = 0; windowData->textureContainers[i].activeTexture->subresources[0].level = 0;
swapchainData->textureContainers[i].activeTexture->subresources[0].transitioned = true; windowData->textureContainers[i].activeTexture->subresources[0].transitioned = true;
swapchainData->textureContainers[i].activeTexture->subresources[0].renderTargetViews = SDL_malloc(sizeof(VkImageView)); windowData->textureContainers[i].activeTexture->subresources[0].renderTargetViews = SDL_malloc(sizeof(VkImageView));
// TODO: ERROR CHECK
if (!VULKAN_INTERNAL_CreateRenderTargetView( if (!VULKAN_INTERNAL_CreateRenderTargetView(
renderer, renderer,
swapchainData->textureContainers[i].activeTexture, windowData->textureContainers[i].activeTexture,
0, 0,
0, 0,
swapchainData->format, windowData->format,
swapchainData->swapchainSwizzle, windowData->swapchainSwizzle,
&swapchainData->textureContainers[i].activeTexture->subresources[0].renderTargetViews[0])) { &windowData->textureContainers[i].activeTexture->subresources[0].renderTargetViews[0])) {
SDL_free(swapchainData);
return false; return false;
} }
} }
@ -4649,10 +4648,9 @@ static bool VULKAN_INTERNAL_CreateSwapchain(
renderer->logicalDevice, renderer->logicalDevice,
&semaphoreCreateInfo, &semaphoreCreateInfo,
NULL, NULL,
&swapchainData->imageAvailableSemaphore[i]); &windowData->imageAvailableSemaphore[i]);
if (vulkanResult != VK_SUCCESS) { if (vulkanResult != VK_SUCCESS) {
SDL_free(swapchainData);
CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkCreateSemaphore, false) CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkCreateSemaphore, false)
} }
@ -4660,19 +4658,16 @@ static bool VULKAN_INTERNAL_CreateSwapchain(
renderer->logicalDevice, renderer->logicalDevice,
&semaphoreCreateInfo, &semaphoreCreateInfo,
NULL, NULL,
&swapchainData->renderFinishedSemaphore[i]); &windowData->renderFinishedSemaphore[i]);
if (vulkanResult != VK_SUCCESS) { if (vulkanResult != VK_SUCCESS) {
SDL_free(swapchainData);
CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkCreateSemaphore, false) CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkCreateSemaphore, false)
} }
swapchainData->inFlightFences[i] = NULL; windowData->inFlightFences[i] = NULL;
} }
windowData->swapchainData = swapchainData;
windowData->needsSwapchainRecreate = false; windowData->needsSwapchainRecreate = false;
return true; return true;
} }
@ -9348,11 +9343,11 @@ static bool VULKAN_SupportsSwapchainComposition(
SwapchainSupportDetails supportDetails; SwapchainSupportDetails supportDetails;
bool result = false; bool result = false;
if (windowData == NULL || windowData->swapchainData == NULL) { if (windowData == NULL) {
SET_STRING_ERROR_AND_RETURN("Must claim window before querying swapchain composition support!", false) SET_STRING_ERROR_AND_RETURN("Must claim window before querying swapchain composition support!", false)
} }
surface = windowData->swapchainData->surface; surface = windowData->surface;
if (VULKAN_INTERNAL_QuerySwapchainSupport( if (VULKAN_INTERNAL_QuerySwapchainSupport(
renderer, renderer,
@ -9393,11 +9388,11 @@ static bool VULKAN_SupportsPresentMode(
SwapchainSupportDetails supportDetails; SwapchainSupportDetails supportDetails;
bool result = false; bool result = false;
if (windowData == NULL || windowData->swapchainData == NULL) { if (windowData == NULL) {
SET_STRING_ERROR_AND_RETURN("Must claim window before querying present mode support!", false) SET_STRING_ERROR_AND_RETURN("Must claim window before querying present mode support!", false)
} }
surface = windowData->swapchainData->surface; surface = windowData->surface;
if (VULKAN_INTERNAL_QuerySwapchainSupport( if (VULKAN_INTERNAL_QuerySwapchainSupport(
renderer, renderer,
@ -9425,12 +9420,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_malloc(sizeof(WindowData)); windowData = SDL_calloc(1, sizeof(WindowData));
windowData->window = window; windowData->window = window;
windowData->presentMode = SDL_GPU_PRESENTMODE_VSYNC; windowData->presentMode = SDL_GPU_PRESENTMODE_VSYNC;
windowData->swapchainComposition = SDL_GPU_SWAPCHAINCOMPOSITION_SDR; windowData->swapchainComposition = SDL_GPU_SWAPCHAINCOMPOSITION_SDR;
if (VULKAN_INTERNAL_CreateSwapchain(renderer, windowData)) { Uint32 createSwapchainResult = VULKAN_INTERNAL_CreateSwapchain(renderer, windowData);
if (createSwapchainResult == 1) {
SDL_SetPointerProperty(SDL_GetWindowProperties(window), WINDOW_PROPERTY_DATA, windowData); SDL_SetPointerProperty(SDL_GetWindowProperties(window), WINDOW_PROPERTY_DATA, windowData);
SDL_LockMutex(renderer->windowLock); SDL_LockMutex(renderer->windowLock);
@ -9447,14 +9443,17 @@ static bool VULKAN_ClaimWindow(
SDL_AddEventWatch(VULKAN_INTERNAL_OnWindowResize, window); SDL_AddEventWatch(VULKAN_INTERNAL_OnWindowResize, window);
return 1; return true;
} else if (createSwapchainResult == VULKAN_INTERNAL_TRY_AGAIN) {
windowData->needsSwapchainRecreate = true;
return true;
} else { } else {
SDL_free(windowData); SDL_free(windowData);
SET_STRING_ERROR_AND_RETURN("Could not create swapchain, failed to claim window!", 0); return false;
} }
} else { } else {
SDL_LogWarn(SDL_LOG_CATEGORY_GPU, "Window already claimed!"); SDL_LogWarn(SDL_LOG_CATEGORY_GPU, "Window already claimed!");
return 0; return false;
} }
} }
@ -9470,21 +9469,20 @@ static void VULKAN_ReleaseWindow(
return; return;
} }
if (windowData->swapchainData != NULL) {
VULKAN_Wait(driverData); VULKAN_Wait(driverData);
for (i = 0; i < MAX_FRAMES_IN_FLIGHT; i += 1) { for (i = 0; i < MAX_FRAMES_IN_FLIGHT; i += 1) {
if (windowData->swapchainData->inFlightFences[i] != NULL) { if (windowData->inFlightFences[i] != NULL) {
VULKAN_ReleaseFence( VULKAN_ReleaseFence(
driverData, driverData,
windowData->swapchainData->inFlightFences[i]); windowData->inFlightFences[i]);
} }
} }
VULKAN_INTERNAL_DestroySwapchain( VULKAN_INTERNAL_DestroySwapchain(
(VulkanRenderer *)driverData, (VulkanRenderer *)driverData,
windowData); windowData);
}
SDL_LockMutex(renderer->windowLock); SDL_LockMutex(renderer->windowLock);
for (i = 0; i < renderer->claimedWindowCount; i += 1) { for (i = 0; i < renderer->claimedWindowCount; i += 1) {
@ -9502,23 +9500,21 @@ static void VULKAN_ReleaseWindow(
SDL_RemoveEventWatch(VULKAN_INTERNAL_OnWindowResize, window); SDL_RemoveEventWatch(VULKAN_INTERNAL_OnWindowResize, window);
} }
static bool VULKAN_INTERNAL_RecreateSwapchain( static Uint32 VULKAN_INTERNAL_RecreateSwapchain(
VulkanRenderer *renderer, VulkanRenderer *renderer,
WindowData *windowData) WindowData *windowData)
{ {
Uint32 i; Uint32 i;
if (windowData->swapchainData != NULL) {
if (!VULKAN_Wait((SDL_GPURenderer *)renderer)) { if (!VULKAN_Wait((SDL_GPURenderer *)renderer)) {
return false; return false;
} }
for (i = 0; i < MAX_FRAMES_IN_FLIGHT; i += 1) { for (i = 0; i < MAX_FRAMES_IN_FLIGHT; i += 1) {
if (windowData->swapchainData->inFlightFences[i] != NULL) { if (windowData->inFlightFences[i] != NULL) {
VULKAN_ReleaseFence( VULKAN_ReleaseFence(
(SDL_GPURenderer *)renderer, (SDL_GPURenderer *)renderer,
windowData->swapchainData->inFlightFences[i]); windowData->inFlightFences[i]);
}
} }
} }
@ -9529,56 +9525,63 @@ static bool VULKAN_INTERNAL_RecreateSwapchain(
static bool VULKAN_AcquireSwapchainTexture( static bool VULKAN_AcquireSwapchainTexture(
SDL_GPUCommandBuffer *commandBuffer, SDL_GPUCommandBuffer *commandBuffer,
SDL_Window *window, SDL_Window *window,
SDL_GPUTexture **swapchainTexture) SDL_GPUTexture **swapchainTexture,
Uint32 *swapchainTextureWidth,
Uint32 *swapchainTextureHeight)
{ {
VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer; VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
VulkanRenderer *renderer = (VulkanRenderer *)vulkanCommandBuffer->renderer; VulkanRenderer *renderer = (VulkanRenderer *)vulkanCommandBuffer->renderer;
Uint32 swapchainImageIndex; Uint32 swapchainImageIndex;
WindowData *windowData; WindowData *windowData;
VulkanSwapchainData *swapchainData;
VkResult acquireResult = VK_SUCCESS; VkResult acquireResult = VK_SUCCESS;
VulkanTextureContainer *swapchainTextureContainer = NULL; VulkanTextureContainer *swapchainTextureContainer = NULL;
VulkanPresentData *presentData; VulkanPresentData *presentData;
*swapchainTexture = NULL; *swapchainTexture = NULL;
if (swapchainTextureWidth) {
*swapchainTextureWidth = 0;
}
if (swapchainTextureHeight) {
*swapchainTextureHeight = 0;
}
windowData = VULKAN_INTERNAL_FetchWindowData(window); windowData = VULKAN_INTERNAL_FetchWindowData(window);
if (windowData == NULL) { if (windowData == NULL) {
SET_STRING_ERROR_AND_RETURN("Cannot acquire a swapchain texture from an unclaimed window!", false) SET_STRING_ERROR_AND_RETURN("Cannot acquire a swapchain texture from an unclaimed window!", false)
} }
swapchainData = windowData->swapchainData; // If window data marked as needing swapchain recreate, try to recreate
if (windowData->needsSwapchainRecreate) {
// Window is claimed but swapchain is invalid! Uint32 recreateSwapchainResult = VULKAN_INTERNAL_RecreateSwapchain(renderer, windowData);
if (swapchainData == NULL) { if (!recreateSwapchainResult) {
if (SDL_GetWindowFlags(window) & SDL_WINDOW_MINIMIZED) { return false;
// Window is minimized, don't bother } else if (recreateSwapchainResult == VULKAN_INTERNAL_TRY_AGAIN) {
// Edge case, texture is filled in with NULL but not an error
return true; return true;
} }
// Let's try to recreate
VULKAN_INTERNAL_RecreateSwapchain(renderer, windowData);
swapchainData = windowData->swapchainData;
if (swapchainData == NULL) {
return false;
}
} }
if (swapchainData->inFlightFences[swapchainData->frameCounter] != NULL) { if (swapchainTextureWidth) {
if (swapchainData->presentMode == VK_PRESENT_MODE_FIFO_KHR) { *swapchainTextureWidth = windowData->width;
}
if (swapchainTextureHeight) {
*swapchainTextureHeight = windowData->height;
}
if (windowData->inFlightFences[windowData->frameCounter] != NULL) {
if (windowData->presentMode == SDL_GPU_PRESENTMODE_VSYNC) {
// In VSYNC mode, block until the least recent presented frame is done // In VSYNC mode, block until the least recent presented frame is done
if (!VULKAN_WaitForFences( if (!VULKAN_WaitForFences(
(SDL_GPURenderer *)renderer, (SDL_GPURenderer *)renderer,
true, true,
&swapchainData->inFlightFences[swapchainData->frameCounter], &windowData->inFlightFences[windowData->frameCounter],
1)) { 1)) {
return false; return false;
} }
} else { } else {
if (!VULKAN_QueryFence( if (!VULKAN_QueryFence(
(SDL_GPURenderer *)renderer, (SDL_GPURenderer *)renderer,
swapchainData->inFlightFences[swapchainData->frameCounter])) { windowData->inFlightFences[windowData->frameCounter])) {
/* /*
* In MAILBOX or IMMEDIATE mode, if the least recent fence is not signaled, * In MAILBOX or IMMEDIATE mode, if the least recent fence is not signaled,
* return true to indicate that there is no error but rendering should be skipped * return true to indicate that there is no error but rendering should be skipped
@ -9589,47 +9592,35 @@ static bool VULKAN_AcquireSwapchainTexture(
VULKAN_ReleaseFence( VULKAN_ReleaseFence(
(SDL_GPURenderer *)renderer, (SDL_GPURenderer *)renderer,
swapchainData->inFlightFences[swapchainData->frameCounter]); windowData->inFlightFences[windowData->frameCounter]);
swapchainData->inFlightFences[swapchainData->frameCounter] = NULL; windowData->inFlightFences[windowData->frameCounter] = NULL;
}
// If window data marked as needing swapchain recreate, try to recreate
if (windowData->needsSwapchainRecreate) {
if (!VULKAN_INTERNAL_RecreateSwapchain(renderer, windowData)) {
return false;
}
swapchainData = windowData->swapchainData;
if (swapchainData == NULL) {
return false;
}
} }
// Finally, try to acquire! // Finally, try to acquire!
acquireResult = renderer->vkAcquireNextImageKHR( acquireResult = renderer->vkAcquireNextImageKHR(
renderer->logicalDevice, renderer->logicalDevice,
swapchainData->swapchain, windowData->swapchain,
SDL_MAX_UINT64, SDL_MAX_UINT64,
swapchainData->imageAvailableSemaphore[swapchainData->frameCounter], windowData->imageAvailableSemaphore[windowData->frameCounter],
VK_NULL_HANDLE, VK_NULL_HANDLE,
&swapchainImageIndex); &swapchainImageIndex);
// Acquisition is invalid, let's try to recreate // Acquisition is invalid, let's try to recreate
if (acquireResult != VK_SUCCESS && acquireResult != VK_SUBOPTIMAL_KHR) { if (acquireResult != VK_SUCCESS && acquireResult != VK_SUBOPTIMAL_KHR) {
VULKAN_INTERNAL_RecreateSwapchain(renderer, windowData); Uint32 recreateSwapchainResult = VULKAN_INTERNAL_RecreateSwapchain(renderer, windowData);
swapchainData = windowData->swapchainData; if (!recreateSwapchainResult) {
if (swapchainData == NULL) {
return false; return false;
} else if (recreateSwapchainResult == VULKAN_INTERNAL_TRY_AGAIN) {
// Edge case, texture is filled in with NULL but not an error
return true;
} }
acquireResult = renderer->vkAcquireNextImageKHR( acquireResult = renderer->vkAcquireNextImageKHR(
renderer->logicalDevice, renderer->logicalDevice,
swapchainData->swapchain, windowData->swapchain,
SDL_MAX_UINT64, SDL_MAX_UINT64,
swapchainData->imageAvailableSemaphore[swapchainData->frameCounter], windowData->imageAvailableSemaphore[windowData->frameCounter],
VK_NULL_HANDLE, VK_NULL_HANDLE,
&swapchainImageIndex); &swapchainImageIndex);
@ -9638,7 +9629,7 @@ static bool VULKAN_AcquireSwapchainTexture(
} }
} }
swapchainTextureContainer = &swapchainData->textureContainers[swapchainImageIndex]; swapchainTextureContainer = &windowData->textureContainers[swapchainImageIndex];
// We need a special execution dependency with pWaitDstStageMask or image transition can start before acquire finishes // We need a special execution dependency with pWaitDstStageMask or image transition can start before acquire finishes
@ -9695,7 +9686,7 @@ static bool VULKAN_AcquireSwapchainTexture(
} }
vulkanCommandBuffer->waitSemaphores[vulkanCommandBuffer->waitSemaphoreCount] = vulkanCommandBuffer->waitSemaphores[vulkanCommandBuffer->waitSemaphoreCount] =
swapchainData->imageAvailableSemaphore[swapchainData->frameCounter]; windowData->imageAvailableSemaphore[windowData->frameCounter];
vulkanCommandBuffer->waitSemaphoreCount += 1; vulkanCommandBuffer->waitSemaphoreCount += 1;
if (vulkanCommandBuffer->signalSemaphoreCount == vulkanCommandBuffer->signalSemaphoreCapacity) { if (vulkanCommandBuffer->signalSemaphoreCount == vulkanCommandBuffer->signalSemaphoreCapacity) {
@ -9706,7 +9697,7 @@ static bool VULKAN_AcquireSwapchainTexture(
} }
vulkanCommandBuffer->signalSemaphores[vulkanCommandBuffer->signalSemaphoreCount] = vulkanCommandBuffer->signalSemaphores[vulkanCommandBuffer->signalSemaphoreCount] =
swapchainData->renderFinishedSemaphore[swapchainData->frameCounter]; windowData->renderFinishedSemaphore[windowData->frameCounter];
vulkanCommandBuffer->signalSemaphoreCount += 1; vulkanCommandBuffer->signalSemaphoreCount += 1;
*swapchainTexture = (SDL_GPUTexture *)swapchainTextureContainer; *swapchainTexture = (SDL_GPUTexture *)swapchainTextureContainer;
@ -9724,13 +9715,9 @@ static SDL_GPUTextureFormat VULKAN_GetSwapchainTextureFormat(
SET_STRING_ERROR_AND_RETURN("Cannot get swapchain format, window has not been claimed!", SDL_GPU_TEXTUREFORMAT_INVALID) SET_STRING_ERROR_AND_RETURN("Cannot get swapchain format, window has not been claimed!", SDL_GPU_TEXTUREFORMAT_INVALID)
} }
if (windowData->swapchainData == NULL) {
SET_STRING_ERROR_AND_RETURN("Cannot get swapchain format, swapchain is currently invalid!", SDL_GPU_TEXTUREFORMAT_INVALID)
}
return SwapchainCompositionToSDLFormat( return SwapchainCompositionToSDLFormat(
windowData->swapchainComposition, windowData->swapchainComposition,
windowData->swapchainData->usingFallbackFormat); windowData->usingFallbackFormat);
} }
static bool VULKAN_SetSwapchainParameters( static bool VULKAN_SetSwapchainParameters(
@ -9757,9 +9744,16 @@ static bool VULKAN_SetSwapchainParameters(
windowData->presentMode = presentMode; windowData->presentMode = presentMode;
windowData->swapchainComposition = swapchainComposition; windowData->swapchainComposition = swapchainComposition;
return VULKAN_INTERNAL_RecreateSwapchain( Uint32 recreateSwapchainResult = VULKAN_INTERNAL_RecreateSwapchain(renderer, windowData);
(VulkanRenderer *)driverData, if (!recreateSwapchainResult) {
windowData); return false;
} else if (recreateSwapchainResult == VULKAN_INTERNAL_TRY_AGAIN) {
// Edge case, swapchain extent is (0, 0) but this is not an error
windowData->needsSwapchainRecreate = true;
return true;
}
return true;
} }
// Submission structure // Submission structure
@ -10109,7 +10103,7 @@ static bool VULKAN_Submit(
for (Uint32 j = 0; j < vulkanCommandBuffer->presentDataCount; j += 1) { for (Uint32 j = 0; j < vulkanCommandBuffer->presentDataCount; j += 1) {
swapchainImageIndex = vulkanCommandBuffer->presentDatas[j].swapchainImageIndex; swapchainImageIndex = vulkanCommandBuffer->presentDatas[j].swapchainImageIndex;
swapchainTextureSubresource = VULKAN_INTERNAL_FetchTextureSubresource( swapchainTextureSubresource = VULKAN_INTERNAL_FetchTextureSubresource(
&vulkanCommandBuffer->presentDatas[j].windowData->swapchainData->textureContainers[swapchainImageIndex], &vulkanCommandBuffer->presentDatas[j].windowData->textureContainers[swapchainImageIndex],
0, 0,
0); 0);
@ -10180,9 +10174,9 @@ static bool VULKAN_Submit(
presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
presentInfo.pNext = NULL; presentInfo.pNext = NULL;
presentInfo.pWaitSemaphores = presentInfo.pWaitSemaphores =
&presentData->windowData->swapchainData->renderFinishedSemaphore[presentData->windowData->swapchainData->frameCounter]; &presentData->windowData->renderFinishedSemaphore[presentData->windowData->frameCounter];
presentInfo.waitSemaphoreCount = 1; presentInfo.waitSemaphoreCount = 1;
presentInfo.pSwapchains = &presentData->windowData->swapchainData->swapchain; presentInfo.pSwapchains = &presentData->windowData->swapchain;
presentInfo.swapchainCount = 1; presentInfo.swapchainCount = 1;
presentInfo.pImageIndices = &presentData->swapchainImageIndex; presentInfo.pImageIndices = &presentData->swapchainImageIndex;
presentInfo.pResults = NULL; presentInfo.pResults = NULL;
@ -10191,16 +10185,18 @@ static bool VULKAN_Submit(
renderer->unifiedQueue, renderer->unifiedQueue,
&presentInfo); &presentInfo);
presentData->windowData->swapchainData->frameCounter = presentData->windowData->frameCounter =
(presentData->windowData->swapchainData->frameCounter + 1) % MAX_FRAMES_IN_FLIGHT; (presentData->windowData->frameCounter + 1) % MAX_FRAMES_IN_FLIGHT;
if (presentResult != VK_SUCCESS) { if (presentResult != VK_SUCCESS) {
result = VULKAN_INTERNAL_RecreateSwapchain( if (presentResult == VK_ERROR_OUT_OF_DATE_KHR) {
renderer, presentData->windowData->needsSwapchainRecreate = true;
presentData->windowData); } else {
CHECK_VULKAN_ERROR_AND_RETURN(presentResult, vkQueuePresentKHR, false)
}
} else { } else {
// If presenting, the swapchain is using the in-flight fence // If presenting, the swapchain is using the in-flight fence
presentData->windowData->swapchainData->inFlightFences[presentData->windowData->swapchainData->frameCounter] = (SDL_GPUFence*)vulkanCommandBuffer->inFlightFence; presentData->windowData->inFlightFences[presentData->windowData->frameCounter] = (SDL_GPUFence*)vulkanCommandBuffer->inFlightFence;
(void)SDL_AtomicIncRef(&vulkanCommandBuffer->inFlightFence->referenceCount); (void)SDL_AtomicIncRef(&vulkanCommandBuffer->inFlightFence->referenceCount);
} }

View file

@ -957,7 +957,8 @@ static bool GPU_RenderPresent(SDL_Renderer *renderer)
GPU_RenderData *data = (GPU_RenderData *)renderer->internal; GPU_RenderData *data = (GPU_RenderData *)renderer->internal;
SDL_GPUTexture *swapchain; SDL_GPUTexture *swapchain;
bool result = SDL_AcquireGPUSwapchainTexture(data->state.command_buffer, renderer->window, &swapchain); Uint32 swapchain_texture_width, swapchain_texture_height;
bool result = SDL_AcquireGPUSwapchainTexture(data->state.command_buffer, renderer->window, &swapchain, &swapchain_texture_width, &swapchain_texture_height);
if (!result) { if (!result) {
SDL_LogError(SDL_LOG_CATEGORY_RENDER, "Failed to acquire swapchain texture: %s", SDL_GetError()); SDL_LogError(SDL_LOG_CATEGORY_RENDER, "Failed to acquire swapchain texture: %s", SDL_GetError());
@ -967,8 +968,6 @@ static bool GPU_RenderPresent(SDL_Renderer *renderer)
goto submit; goto submit;
} }
SDL_GPUTextureFormat swapchain_fmt = SDL_GetGPUSwapchainTextureFormat(data->device, renderer->window);
SDL_GPUBlitInfo blit_info; SDL_GPUBlitInfo blit_info;
SDL_zero(blit_info); SDL_zero(blit_info);
@ -976,18 +975,13 @@ static bool GPU_RenderPresent(SDL_Renderer *renderer)
blit_info.source.w = data->backbuffer.width; blit_info.source.w = data->backbuffer.width;
blit_info.source.h = data->backbuffer.height; blit_info.source.h = data->backbuffer.height;
blit_info.destination.texture = swapchain; blit_info.destination.texture = swapchain;
blit_info.destination.w = renderer->output_pixel_w; blit_info.destination.w = swapchain_texture_width;
blit_info.destination.h = renderer->output_pixel_h; blit_info.destination.h = swapchain_texture_height;
blit_info.load_op = SDL_GPU_LOADOP_DONT_CARE; blit_info.load_op = SDL_GPU_LOADOP_DONT_CARE;
blit_info.filter = SDL_GPU_FILTER_LINEAR; blit_info.filter = SDL_GPU_FILTER_LINEAR;
SDL_BlitGPUTexture(data->state.command_buffer, &blit_info); SDL_BlitGPUTexture(data->state.command_buffer, &blit_info);
if (renderer->output_pixel_w != data->backbuffer.width || renderer->output_pixel_h != data->backbuffer.height || swapchain_fmt != data->backbuffer.format) {
SDL_ReleaseGPUTexture(data->device, data->backbuffer.texture);
CreateBackbuffer(data, renderer->output_pixel_w, renderer->output_pixel_h, swapchain_fmt);
}
// *** FIXME *** // *** FIXME ***
// This is going to block if there is ever a frame in flight. // This is going to block if there is ever a frame in flight.
// We should do something similar to FNA3D // We should do something similar to FNA3D
@ -1006,6 +1000,11 @@ submit:
SDL_SubmitGPUCommandBuffer(data->state.command_buffer); SDL_SubmitGPUCommandBuffer(data->state.command_buffer);
#endif #endif
if (swapchain != NULL && (swapchain_texture_width != data->backbuffer.width || swapchain_texture_height != data->backbuffer.height)) {
SDL_ReleaseGPUTexture(data->device, data->backbuffer.texture);
CreateBackbuffer(data, swapchain_texture_width, swapchain_texture_height, SDL_GetGPUSwapchainTextureFormat(data->device, renderer->window));
}
data->state.command_buffer = SDL_AcquireGPUCommandBuffer(data->device); data->state.command_buffer = SDL_AcquireGPUCommandBuffer(data->device);
return true; return true;

View file

@ -82,7 +82,7 @@ SDL_AppResult SDL_AppIterate(void *appstate)
} }
SDL_GPUTexture *swapchainTexture; SDL_GPUTexture *swapchainTexture;
if (!SDL_AcquireGPUSwapchainTexture(cmdbuf, state->windows[0], &swapchainTexture)) { if (!SDL_AcquireGPUSwapchainTexture(cmdbuf, state->windows[0], &swapchainTexture, NULL, NULL)) {
SDL_Log("SDL_AcquireGPUSwapchainTexture failed: %s", SDL_GetError()); SDL_Log("SDL_AcquireGPUSwapchainTexture failed: %s", SDL_GetError());
return SDL_APP_FAILURE; return SDL_APP_FAILURE;
} }

View file

@ -333,7 +333,7 @@ Render(SDL_Window *window, const int windownum)
SDL_GPURenderPass *pass; SDL_GPURenderPass *pass;
SDL_GPUBufferBinding vertex_binding; SDL_GPUBufferBinding vertex_binding;
SDL_GPUBlitInfo blit_info; SDL_GPUBlitInfo blit_info;
int drawablew, drawableh; Uint32 drawablew, drawableh;
/* Acquire the swapchain texture */ /* Acquire the swapchain texture */
@ -342,7 +342,7 @@ Render(SDL_Window *window, const int windownum)
SDL_Log("Failed to acquire command buffer :%s", SDL_GetError()); SDL_Log("Failed to acquire command buffer :%s", SDL_GetError());
quit(2); quit(2);
} }
if (!SDL_AcquireGPUSwapchainTexture(cmd, state->windows[windownum], &swapchainTexture)) { if (!SDL_AcquireGPUSwapchainTexture(cmd, state->windows[windownum], &swapchainTexture, &drawablew, &drawableh)) {
SDL_Log("Failed to acquire swapchain texture: %s", SDL_GetError()); SDL_Log("Failed to acquire swapchain texture: %s", SDL_GetError());
quit(2); quit(2);
} }
@ -353,8 +353,6 @@ Render(SDL_Window *window, const int windownum)
return; return;
} }
SDL_GetWindowSizeInPixels(window, &drawablew, &drawableh);
/* /*
* Do some rotation with Euler angles. It is not a fixed axis as * Do some rotation with Euler angles. It is not a fixed axis as
* quaterions would be, but the effect is cool. * quaterions would be, but the effect is cool.