QT - SDL. Resizing the app causes SDL to lose its rendering context.
-
wrote on 27 Apr 2020, 09:15 last edited by Simula
Hi.
Qt: 5.14.1
SDL: 2.0.12
OS: Windows 10I'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.
-
wrote on 27 Apr 2020, 13:23 last edited by
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; }
-
wrote on 27 Apr 2020, 20:51 last edited by
When I was working on ffmpeg player in Qt, I only used SDL for a little while so I'm not very familiar with it.
But in the source code of ffplay you can see when window is resized, they only destroy SDL_Texture.case SDL_WINDOWEVENT: switch (event.window.event) { case SDL_WINDOWEVENT_SIZE_CHANGED: screen_width = cur_stream->width = event.window.data1; screen_height = cur_stream->height = event.window.data2; if (cur_stream->vis_texture) { SDL_DestroyTexture(cur_stream->vis_texture); cur_stream->vis_texture = NULL; } case SDL_WINDOWEVENT_EXPOSED: cur_stream->force_refresh = 1; } break;
-
wrote on 28 Apr 2020, 06:30 last edited by Simula
I tried that. Renderer still needs to be destroyed. The example in my post does't have textures but the rendering context is still lost on resize.
This only happens when rendering from threads though.
1/4