SDL и Qt. Изменение размера приложения приводит к зависанию экрана при рендеринге из другого потока - PullRequest
0 голосов
/ 27 апреля 2020

Qt: 5.14.1 SDL: 2.0.12 ОС: Windows 10

Я работаю над видеопроигрывателем и использую Qt для пользовательского интерфейса и SDL для рендеринга кадров. Я создал окно SDL путем передачи дескриптора winId () моего виджета рендеринга (внутри макета).

Это прекрасно работает, когда я запускаю многопоточный Play (). Однако это вызывает некоторые проблемы с воспроизведением при изменении размера или перемещении окна приложения. Ничего серьезного, но так как код воспроизведения не является многопоточным, мое заполнение очередей кадров приводит к ускорению видео до тех пор, пока оно не перехватывает звук.

Я решил это, поместив код воспроизведения в созданный поток Win32. с функцией CreateThread. Теперь, когда я перемещаю окно, видео продолжает воспроизводиться, как и предполагалось, но при изменении размера приложения виджет рендеринга перестанет обновлять виджет и будет отображаться только последний отображенный кадр перед событием изменения размера. Я могу подтвердить, что видео все еще работает и правильные кадры все еще отображаются. Размер отображаемого изображения может быть даже изменен, но он никогда не обновляется.

Подобное происходит, когда я тестировал потоки Qt с SDL. Рассмотрим этот код

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, контекст рендеринга будет потерян, и то же самое произойдет в моем видеопроигрывателе.

Я также обнаружил, что если я удалю функцию SDL_RenderPresent из Зафиксируйте объект и поместите его в главное окно Qt, рендеринг продолжится после события resize. Однако это оказалось совершенно ненадежным и иногда полностью замораживает мое приложение.

Я также не могу понять, почему мое полностью отдельное окно SDL и средство визуализации по-прежнему зависает при изменении размера. Я предполагаю, что где-то есть cla sh с SDL-рендерером / окном и материалами для рисования Qt, но я здесь в замешательстве.

Кроме того, это всего лишь материал для изменения размера. Все остальное работает.

Спасибо.

1 Ответ

1 голос
/ 27 апреля 2020

Ответ: 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;
}
...