SDL和Qt.NET 当从另一个线程渲染时,调整应用程序的大小会导致显示冻结。

问题描述 投票:0回答:1

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的绘图东西有冲突,但我在这里很迷茫。

另外,这只是调整大小的问题。其他的都能用。

谢谢。

qt resize sdl freeze
1个回答
1
投票

答: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向该线程发出信号以渲染到屏幕。

这样纹理就不需要重新创建了。

© www.soinside.com 2019 - 2024. All rights reserved.