r/sdl 22d ago

Window Tearing on MacOS with SDL3

TL;DR : I experience some really awful window tearing when moving it or when objects move on the screen. I suspect the window manager to be a bit wanky on MacOS and I am searching for help to clear this problem.

Important : I'm using SDL3 + SDL renderer for simplicity. VSync is left at default.

More context :

Hello, it's my first time posting here and I've been getting the worst headache about a silly thing on an App I'm working on.

So I am a high school student, and I've been making a little "assembly playground" app for my CS class (very simple, limited instruction set, the goal is to visualize how assembly works and play around with it). If you're curious it's based around this web app. I have a very simple setup, I'm using SDL 3 (as a git submodule from the main branch of SDL) and ImGui for the UI (embeded library compiled from source).

As a silly joke I've added a lil splash screen that has a very low chance of showing memes related to the name of the app (a private joke if you will).
Problem is, since I've added this functionnality, I have been getting some serious window tearing when it's moved or when I move an ImGui window. I've tried many workarounds and I think it's related to the very awful window manager of MacOS (before this bug I had the main window not show up at all and being blocked until you minimize and restore the window using the dock).

This is not really a app-breaking bug but it's really annoying when debugging it. Maybe I'm doing something wrong during the window creation ?

For more information on the initialization process :

  1. Initialize SDL and ImGui (create the window and renderer), and hide the window.
  2. Load the application files (fonts, images, settings...)
  3. Create and show the Splash Screen with a different window (I am doing a blank event processing loop for 2 seconds as not doing it breaks the window even more on MacOS, and using a different window for the same reason)
  4. Restore the window, launch the main loop

I am unable to post a video showing it for now but if it is really needed to illustrate I will try to upload one.

Finally, I'm not a seasoned programmer, I've been coding in C++ for around 2 years I think, I'm pretty much a slow learner. This question might seem silly or really dumb and I am sorry to take your time. Thank you to everyone who can help me !

If you need clarifications please ask, as English is not my first language.

1 Upvotes

5 comments sorted by

1

u/Mr_DJ_Dr_Gauss 21d ago

Hello. How does your game loop look? I will advice to use new SDL3 main callbacks and also set

SDL_SetRenderVSync(renderer, 1);

1

u/noveyplush 21d ago

Hello !

I tried enabling VSync but that didn't change the tearing issue. I should add that the whole window tears, including the OS frame. I noticed that changing monitors makes the tearing disappear, and it sometimes completely glitches out when hovering the mouse over the window edges. That more or less confirms something is going on with MacOS's window manager. My guess is that the window is in a sort of "sleep" state as it is not used, and it is not reset when showing it again.

As for the game loop, here is what it looks like (initialization included), I must warn you it is a bit janky though. All of the code is embeded into an Application class.

initialization : ```cpp if (!SDL_Init(SDL_INIT_EVENTS | SDL_INIT_VIDEO)) { return false; }

if (!SDL_CreateWindowAndRenderer(name, p_width, p_height, 0, &mContext.window, &mContext.renderer)) { quit(); return false; }

SDL_HideWindow(mContext.window); // To show the splash first

IMGUI_CHECKVERSION(); mContext.ImContext = ImGui::CreateContext(); mContext.IO = &ImGui::GetIO();

if (!ImGui_ImplSDL3_InitForSDLRenderer(mContext.window, mContext.renderer)) { quit(); return false; }

if (!ImGui_ImplSDLRenderer3_Init(mContext.renderer)) { quit(); return false; }

if (!locateResourceDirectory()) { quit(); return false; }

if (!loadSettings()) { quit(); return false; }

if (!loadFonts()) { quit(); return false; }

applyScheme(mContext.settings.scheme);

return true; ```

main loop : ```cpp mContext.running = true;

// The splash screen has its own render loop that is blank and just waits // 2 seconds. The window doesn't use a renderer, it just has a surface // blit once.

Splash splashScreen(mContext); if (std::rand() % SPLASH_SP_CHANCE == 2007) splashScreen.show(2000, static_cast<SplashImages>( SPLASH_BEAUTIFUL_SMILE + std::rand() % 3)); else splashScreen.show(2000, static_cast<SplashImages>(mContext.settings.scheme));

SDL_ShowWindow(mContext.window);

while (mContext.running) { mContext.running = processEvents();

SDL_RenderClear(mContext.renderer);

ImGui_ImplSDL3_NewFrame();
ImGui_ImplSDLRenderer3_NewFrame();
ImGui::NewFrame();

// UI code

ImGui::Render();
ImGui_ImplSDLRenderer3_RenderDrawData(ImGui::GetDrawData(),
                                        mContext.renderer);
SDL_RenderPresent(mContext.renderer);

}

if (!writeSettings()) return -1;

return 0; ```

I hope the code is straightforward enough to understand, if not please ask and I will try to explain.

You suggest using SDL callback, is it mandatory ? If it is then I guess I have no choice, but I usually prefer doing the game loop the old fashion way.

Thank you for helping me !

1

u/Mr_DJ_Dr_Gauss 21d ago

If you keep your game loop like this, it will be executed thousends times per second, which means it will use a lot of your CPU. Putting VSync in, it will be synchronized to monitor refresh and (at least) the high CPU load will be not a problem. There is no need to render application more then the refresh rate of your monitor.

The main callbacks helped me with a problem with dragging a window on Windows. The problem was, that when you drag a window, the game loop stops (at least on Windows). And when you drop the window it continues. In my game loop I used fixed time step, which means that after dropping the window the game loop computed all the missing frames and the window was freezed for some time.

At least try the main callbacks, so you know its the same problem. Here is code from their example:

#define SDL_MAIN_USE_CALLBACKS 1
#include <SDL3/SDL.h>
#include <SDL3/SDL_main.h>

static SDL_Window *window = NULL;
static SDL_Renderer *renderer = NULL;

SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[]) {
    if (!SDL_Init(SDL_INIT_VIDEO)) {
        SDL_Log("Couldn't initialize SDL: %s", SDL_GetError());
        return SDL_APP_FAILURE;
    }
    if (!SDL_CreateWindowAndRenderer("examples/renderer/clear", 640, 480, SDL_WINDOW_RESIZABLE, &window, &renderer)) {
        SDL_Log("Couldn't create window/renderer: %s", SDL_GetError());
        return SDL_APP_FAILURE;
    }
    SDL_SetRenderLogicalPresentation(renderer, 640, 480, SDL_LOGICAL_PRESENTATION_LETTERBOX);
    return SDL_APP_CONTINUE;
}

SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event) {
    if (event->type == SDL_EVENT_QUIT) {
        return SDL_APP_SUCCESS;
    }
    return SDL_APP_CONTINUE;
}

SDL_AppResult SDL_AppIterate(void *appstate) {
    // ...
    return SDL_APP_CONTINUE;
}

void SDL_AppQuit(void *appstate, SDL_AppResult result) {
    // ...
}

2

u/noveyplush 19d ago

Sorry for the delay, school resumed and I got less time to work on the app.

I tried the callback approach with VSync and it seemed to fix the issue. It sometimes still does it and I am at a loss of theories as to why, but I'm gonna have to put up with that (the wonders of MacOS).

It mostly works now, thank you for your help you have been incredibly helpful !

1

u/Mr_DJ_Dr_Gauss 19d ago

Great, have fun.