-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Description
Hi!
I made small app using SDL3 GPU and noticed that it suffers from graphical stuttering.
Every now and then there is odd delay between draw calls. I made small example that reproduces this issue
I am using Xubuntu 24.04 with NVidia drivers 590
The test application creates Vulkan window and clears screen on every SDL_AppIterate call . Screen is cleared with a color that indicates how many milliseconds there were between draw calls. When running the application it is easy to see that window blinks red quite often (indicating long time between draw calls). In my case it blinks rougly twice per second.
App also logs the time between draw calls, it looks like this:
Elapsed: 17 ms // Normal rendering
Elapsed: 17 ms
Elapsed: 67 ms // Odd delay (stutter)
Elapsed: 0 ms // ?
Elapsed: 0 ms
Elapsed: 0 ms
Elapsed: 16 ms // Back to normal rendering
Elapsed: 17 ms
Elapsed: 17 ms
Here's the code that reproduces the issue:
#define SDL_MAIN_USE_CALLBACKS
#include <SDL3/SDL_main.h>
#include <SDL3/SDL_gpu.h>
#include <SDL3/SDL_timer.h>
#include <SDL3/SDL_log.h>
struct Context
{
SDL_Window *window = nullptr;
SDL_GPUDevice *device = nullptr;
Uint64 lastTicks = 0;
};
SDL_AppResult SDL_AppInit(void **appstate, int /*argc*/, char ** /*argv*/)
{
Context* context = new Context();
*appstate = context;
if (!SDL_Init(SDL_INIT_VIDEO))
return SDL_APP_FAILURE;
// Create Window
SDL_WindowFlags flags = SDL_WINDOW_VULKAN;
// flags |= SDL_WINDOW_FULLSCREEN; // Issue happens in fullscreen also
context->window = SDL_CreateWindow("Stutter test", 1920, 1080, flags);
if (!context->window)
return SDL_APP_FAILURE;
// Create GPU Device
context->device = SDL_CreateGPUDevice(SDL_GPU_SHADERFORMAT_SPIRV, false, NULL);
if (!context->device)
return SDL_APP_FAILURE;
// Claim window
if (!SDL_ClaimWindowForGPUDevice(context->device, context->window))
return SDL_APP_FAILURE;
// Using immediate mode removes the stutter
// SDL_SetGPUSwapchainParameters(context->device, context->window, SDL_GPU_SWAPCHAINCOMPOSITION_SDR, SDL_GPU_PRESENTMODE_IMMEDIATE);
context->lastTicks = SDL_GetTicks();
return SDL_APP_CONTINUE;
}
void SDL_AppQuit(void *appstate, SDL_AppResult /*result*/)
{
Context *context = reinterpret_cast<Context*>(appstate);
if (context)
{
SDL_ReleaseWindowFromGPUDevice(context->device, context->window);
SDL_DestroyGPUDevice(context->device);
SDL_DestroyWindow(context->window);
delete context;
}
SDL_Quit();
}
SDL_AppResult SDL_AppIterate(void *appstate)
{
Context *context = reinterpret_cast<Context*>(appstate);
// Calculate number of ticks between two iterations
Uint64 now = SDL_GetTicks();
Uint64 elapsed = now - context->lastTicks;
context->lastTicks = now;
SDL_Log("Elapsed: %ld ms", elapsed);
// Acquire command buffer
SDL_GPUCommandBuffer* commandBuffer = SDL_AcquireGPUCommandBuffer(context->device);
// Get the swapchain texture
uint32_t width, height;
SDL_GPUTexture* swapchainTexture;
SDL_WaitAndAcquireGPUSwapchainTexture(commandBuffer, context->window, &swapchainTexture, &width, &height);
//SDL_AcquireGPUSwapchainTexture(commandBuffer, context->window, &swapchainTexture, &width, &height);
if (swapchainTexture)
{
// Begin a render pass
SDL_GPUColorTargetInfo colorTargetInfo{};
colorTargetInfo.clear_color = {elapsed/100.0f,0,0, 1}; // Indicate elapsed time with red color
colorTargetInfo.load_op = SDL_GPU_LOADOP_CLEAR;
colorTargetInfo.store_op = SDL_GPU_STOREOP_STORE;
colorTargetInfo.texture = swapchainTexture;
colorTargetInfo.cycle = true;
SDL_GPURenderPass* renderPass = SDL_BeginGPURenderPass(commandBuffer, &colorTargetInfo, 1, NULL);
// No draw commands, just clear the screen
// End the render pass
SDL_EndGPURenderPass(renderPass);
}
// Submit command buffer
SDL_SubmitGPUCommandBuffer(commandBuffer);
return SDL_APP_CONTINUE;
}
SDL_AppResult SDL_AppEvent(void * /*appstate*/, SDL_Event *event)
{
// Exit when window is closed
if (event->type == SDL_EVENT_QUIT)
return SDL_APP_SUCCESS;
// Exit when ESC is pressed
if (event->type == SDL_EVENT_KEY_DOWN && event->key.key == SDLK_ESCAPE)
return SDL_APP_SUCCESS;
return SDL_APP_CONTINUE;
}
Stuttering happens even when SDL_WaitAndAcquireGPUSwapchainTexture is changed to SDL_AcquireGPUSwapchainTexture.
Suttering stops if I set immediate mode after claiming window:
SDL_SetGPUSwapchainParameters(context->device, context->window, SDL_GPU_SWAPCHAINCOMPOSITION_SDR, SDL_GPU_PRESENTMODE_IMMEDIATE);
I would not want to use immediate mode, waiting for VSync would be preferred.
Also, stuttering keeps happening even if I change Vulkan to OpenGL.
Any advice how to address this issue? Is it possible that this happens inside SDL3?
Environment:
SDL3 3.4.0
Xubuntu 24.04
NVidia drivers 590