SDL with Qt. The rendering display freezes on last displayed frame when resizing app

Hi.

Qt: 5.14.1
SDL: 2.0.12
OS: Windows 10

I’m working on a video player and I’m using Qt for UI and SDL for rendering frames.
I created the SDL window by passing my rendering widget’s (inside a layout) winId() handle.

This works perfectly when I start a non-threaded Play().
However, this causes some issues with playback when resizing or moving the app window. Nothing serious, but since the play code is non threaded my frame queues fill-up which then causes the video to speed up until it catches to the audio.

I solved that by putting my play code inside Win32 thread created with CreateThread function.
Now when I move window the video continues to play as intended, but when resizing the app, rendering widget will stop refreshing the widget and only the last displayed frame before resize event will be shown.
I can confirm that video is still running and correct frames are still being displayed. The displayed image can even be resized, but its never refreshed.

The similar thing happens when I was testing Qt threads with SDL. Consider this code

class TestThread: public QThread
{
public:
    TestThread(QObject *parent = NULL) : QThread(parent)
    {
    }

    void run() override
    {
		 for (;;)
		 {
		  SDL_Delay(1000/60);
			// Basic square bouncing animation
			SDL_Rect spos;
			spos.h = 100;
			spos.w = 100;
			spos.y = 100;
			spos.x = position;

			SDL_SetRenderDrawColor(RendererRef, 0, 0, 0, 255);
			SDL_RenderFillRect(RendererRef, 0);
			SDL_SetRenderDrawColor(RendererRef, 0xFF, 0x0, 0x0, 0xFF);
			SDL_RenderFillRect(RendererRef, &spos);
			SDL_RenderPresent(RendererRef);
	
			if (position >= 500)
				dir = 0;
			else if (position <= 0)
				dir = 1;

			if (dir)
				position += 5;
			else
				position -= 5;

				}
    }
};

// a call from Init SDL and Start Thread button
... 
// create new SDL borderless resizible window.
WindowRef = SDL_CreateWindow("test",10,10,1280,800,SDL_WINDOW_RESIZABLE | SDL_WINDOW_BORDERLESS);

// create and start thread
 test_thread = new TestThread();
 test_thread->start();

...

This will create a separate window from the Qt app window and will start rendering a bouncy square. However if any resize event occurs in the Qt app, the rendering context will be lost and the the same thing that happens in my video player will happen here.

I also found out that If I remove SDL_RenderPresent function from the Thread object and put it in a Main Qt Window, the rendering will continue after resize event. However this has proved as completely unreliable and will sometimes completely freeze my app.

I also can’t figure out why my completely separate SDL window and renderer still freezes on resize.
I presume there is a clash somewhere with SDL renderer/window and Qt’s drawing stuff, but I’m at a loss here.

Also, it’s only the resize stuff. Everything else works.

Thanks.

Answer:
SDL_Renderer needs to be destroyed and recreated on window resize as well as any SDL_Texture created with previous renderer.

The same thing will happen even without qt.

However, I think this is just a workaround and not a real solution.

A simple code to recreate the issue.

    int position = 0;
	int dir = 0;


SDL_Window *window = NULL;
SDL_Renderer *sdlRenderer_ = NULL;


DWORD WINAPI MyThreadFunction( LPVOID lpParam )
{
		 for (;;)
		 {
		  SDL_Delay(1000/60);
			// Basic square bouncing animation
			SDL_Rect spos;
			spos.h = 100;
			spos.w = 100;
			spos.y = 100;
			spos.x = position;

			SDL_SetRenderDrawColor(sdlRenderer_, 0, 0, 0, 255);
			SDL_RenderFillRect(sdlRenderer_, 0);
			SDL_SetRenderDrawColor(sdlRenderer_, 0xFF, 0x0, 0x0, 0xFF);
			SDL_RenderFillRect(sdlRenderer_, &spos);
			SDL_RenderPresent(sdlRenderer_);
	
			if (position >= 500)
				dir = 0;
			else if (position <= 0)
				dir = 1;

			if (dir)
				position += 5;
			else
				position -= 5;

				}
}


int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance,  _In_ LPWSTR    lpCmdLine,_In_ int       nCmdShow)
{
  SDL_Init(SDL_INIT_VIDEO);

  window = SDL_CreateWindow("test",SDL_WINDOWPOS_UNDEFINED,SDL_WINDOWPOS_UNDEFINED,600,600,SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE);
  
  if (!window)
      printf("Unable to create window");

  sdlRenderer_ = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED |SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_TARGETTEXTURE);
  if (!sdlRenderer_)
      printf("Unable to create renderer");




    HANDLE playHandle = CreateThread(0, 0, MyThreadFunction, 0, 0, 0);
    if (playHandle == NULL)
    {
        return 0;
    }


    SDL_Event e;

    while(1)
    {
     SDL_PollEvent(&e);

     if (e.type == SDL_WINDOWEVENT )
        {
        switch( e.window.event )
              {
               case SDL_WINDOWEVENT_SIZE_CHANGED:
                int mWidth = e.window.data1;
                int mHeight = e.window.data2;
                SDL_DestroyRenderer(sdlRenderer_); // stops rendering on resize if commented out
                sdlRenderer_ = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED |SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_TARGETTEXTURE);

                break;         
              }
        }
     
    }
    


    return 0;
}

My understanding is that SDL2’s rendering functions must be called from the same thread that created the renderer, you cannot (safely) call them from a different thread. It is difficult to find this documented explicitly, but in SDL_render.h it says “These functions must be called from the main thread”. A related discussion can be found here.

Drawing functions involving the renderer must be called from the thread that created the renderer. Problem solved by calling user event to the main thread.