Qt: 5.14.1SDL: 2.0.12OS: Windows 10。
我正在做一个视频播放器,我使用Qt做UI,SDL做渲染帧。我通过传递我的渲染部件(在布局内)的winId()句柄来创建SDL窗口。
当我启动一个非线程的Play()时,这可以完美地工作。然而,当调整大小或移动应用程序窗口时,这会导致一些播放问题。没有什么严重的问题,但是由于播放代码是非线程的,我的帧队列被填满,然后导致视频加速,直到它赶上音频。
我把我的播放代码放在用CreateThread函数创建的Win32线程中解决了这个问题。现在,当我移动窗口时,视频继续按原计划播放,但当调整应用程序大小时,渲染小组件将停止刷新小组件,只有在调整大小事件之前显示的最后一帧才会显示。我可以确认,视频仍在运行,正确的帧仍在显示。显示的图像甚至可以调整大小,但它从来没有被刷新。
类似的事情发生在我用SDL测试Qt线程的时候。请看这段代码
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();
...
这将创建一个独立于Qt应用程序窗口的窗口,并将开始渲染一个弹跳的方块。然而,如果Qt应用程序中发生任何调整大小的事件,渲染上下文将丢失,并且在我的视频播放器中发生的事情也会在这里发生。
我还发现,如果我从Thread对象中删除SDL_RenderPresent函数,并将其放在主Qt窗口中,在resize事件发生后,渲染将继续进行。然而事实证明这完全不可靠,有时会完全冻结我的应用程序。
我也不明白为什么我的完全独立的SDL窗口和渲染器还是会在resize时冻结.我猜测是SDL渲染器window和Qt的绘图东西有冲突,但我在这里很迷茫。
另外,这只是调整大小的问题。其他的都能用。
谢谢。
答:SDL_Renderer需要在窗口调整大小时被销毁并重新创建,同时也需要销毁之前用渲染器创建的任何SDL_Texture。
即使没有qt也会发生同样的事情。
然而,我认为这只是一个变通的方法,而不是真正的解决方案。
一个简单的代码来重新创建这个问题。
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;
}
EDIT:
真正的解决方案 渲染器不需要重新创建。将Qt代码和SDL主线程分开,建立一个独立的线程。在该线程中创建所有SDL的东西,因为SDL_Renderer需要在一个处理SDL事件的线程中创建.使用SDL_PushEvent向该线程发出信号以渲染到屏幕。
这样纹理就不需要重新创建了。