Qt4/Opengl bindTexture в отдельном потоке

Я пытаюсь реализовать эффект, подобный CoverFlow, с помощью QGLWidget, проблема заключается в процессе загрузки текстуры.

У меня есть рабочий (QThread) для загрузки изображений с диска, и основной поток проверяет наличие новых загруженных изображений, если он их находит, то использует bindTexture для их загрузки в QGLContext. Пока привязывается текстура, основной поток блокируется, поэтому у меня падает фпс.

Как правильно это сделать?


person louissmr    schedule 25.09.2012    source источник


Ответы (2)


Привязка в основном потоке (единственное QGLWidget решение):

  1. выбрать максимальный размер текстуры. Вы можете решить это, например, исходя из максимально возможного размера виджета. Допустим, вы знаете, что размер виджета может быть не более (приблизительно) 800x600 пикселей, а самая большая видимая обложка имеет поля шириной 30 пикселей вверх и вниз и соотношение сторон 1:2 -> 600-2*30 = 540 -> максимальный размер обложки 270x540, например хранится в m_maxCoverSize.

  2. масштабировать входящие изображения до этого размера в потоке загрузчика. Большую текстуру привязывать не имеет смысла и чем она больше, тем дольше будет загружаться на видеокарту. Используйте QImage::scaled(m_maxCoverSize, Qt::KeepAspectRatio) для масштабирования загруженного изображения и передачи его в основной поток.

  3. ограничить количество текстур или лучше время, затрачиваемое на их привязку к кадру. т.е. запомните время, когда вы начали привязку текстур (например, QTime bindStartTime;) и после привязки каждой текстуры выполните:

    если (bindStartTime.elapsed() > BIND_TIME_LIMIT) перерыв;

BIND_TIME_LIMIT будет зависеть от частоты кадров, которую вы хотите сохранить. Но, конечно, если связывание каждой текстуры занимает гораздо больше времени, чем BIND_TIME_LIMIT, вы ничего не решили.

Вы все еще можете столкнуться с падением частоты кадров при загрузке изображений, хотя на более медленных машинах / видеокартах. Остальной код должен быть готов к тому, чтобы с ним жить (например, использовать фактическое время для управления анимацией).


Альтернативное решение – связать в отдельном потоке (используя второй невидимый QGLWidget, см. документация):

2. Загрузка текстур в треде.

Выполнение загрузки текстур в потоке может быть очень полезным для приложений, обрабатывающих большое количество изображений, которые необходимо отобразить, например, приложение для фотогалереи. Это поддерживается в Qt через существующий API bindTexture(). Простой способ сделать это — создать два общих QGLWidget. Один из них становится текущим в основном потоке графического интерфейса, а другой — в потоке загрузки текстуры. Виджет в потоке загрузки никогда не отображается, он используется только для обмена текстурами с основным потоком. Для каждой текстуры, связанной с помощью bindTexture(), уведомите основной поток, чтобы он мог начать использовать текстуру.

person artm    schedule 27.09.2012
comment
Проблема в размере текстуры, я использую изображения 480х700 (более-менее), но каждая обложка имеет свой размер (480х741, 480х744, 640х437), чтобы сохранить соотношение сторон обложки. Я уменьшил размеры обложек на рабочем потоке, но в зависимости от графической карты размер обложки должен быть слишком сильно масштабирован.... - person louissmr; 27.09.2012
comment
извините, мой комментарий был неполным. - person louissmr; 27.09.2012
comment
Большое спасибо за ваш ответ, я попытался ограничить количество привязок на кадр, и это немного помогает. Вчера я попробовал альтернативное решение, но для привязки текстуры в фоновом потоке требуется вызов makeCurrent() (получить доступ к общему QGLContext), поэтому основной поток не может получить доступ к контексту, пока текстура привязывается, и я получаю такое же падение фпс. Может я что-то не так делаю... - person louissmr; 27.09.2012
comment
вам нужно два QGLWidget для второго решения - один (невидимый) для привязки текстур, а другой (видимый) для рендеринга. - person artm; 27.09.2012
comment
Я попробую еще раз со вторым QGLWidget и QThread, если я обнаружу проблемы, я поделюсь некоторым кодом. Спасибо еще раз. - person louissmr; 27.09.2012

Я обнаружил, что поведение bindTexture по умолчанию в Qt4 очень медленное:

bindTexture(image,target,format,LinearFilteringBindOption | InvertedYBindOption | MipmapBindOption)

использование только LinearFilteringBindOption в параметрах привязки значительно ускоряет работу, это мой текущий вызов:

bindTexture(image, GL_TEXTURE_2D,GL_RGBA,QGLContext::LinearFilteringBindOption);

дополнительная информация здесь: время загрузки bmp-файла 3800x2850 уменьшено с 2 секунд до 34. миллисекунды

Конечно, если вам нужно мипмэппинг, это не решение. В этом случае я думаю, что путь — это Pixel Buffer Objects.

person louissmr    schedule 27.09.2012
comment
Итак, генерация мипмапов большой текстуры, не равной степени двойки, медленная, почему вы думаете, что пиксельные буферы будут иметь какое-то значение? - person artm; 27.09.2012
comment
PBO могут передавать данные о пикселях на графическую карту, используя DMA, не задействуя циклы процессора, не так ли? Таким образом, использование PBO в отдельном потоке может решить проблему. - person louissmr; 28.09.2012
comment
Все, что графический драйвер может сделать для оптимизации загрузки PBO, он сделает и для оптимизации загрузки текстуры. Проблема с мипмэппингом изображения такого размера заключается не (только) в передаче, а в том, что он будет увеличен до 4096x4096 и уменьшен в 12 раз для формирования мипмапов. Эти операции не будут ускорены переключением на PBO, на самом деле я не вижу, как PBO вообще имеет отношение к mipmapping. - person artm; 28.09.2012
comment
Хуже того, если вы загрузите несколько изображений такого размера без мипмэппинга, ваш рендеринг будет замедлен из-за постоянного масштабирования огромных текстур. С другой стороны, при мипмаппинге вы будете тратить видеопамять впустую. Лучший подход в вашем случае - уменьшить изображения перед созданием из них текстур, и если вы не ожидаете, что вам нужно будет сильно уменьшать их при рендеринге - не создавайте мип-карты. - person artm; 28.09.2012
comment
Итак, если я использую mipmapping, я буду использовать больше VRAM и больше пропускной способности, когда происходит привязка, а если я этого не сделаю, видеокарта будет уменьшать масштаб текстур, если это необходимо (плохо для производительности рендеринга). Решение должно быть таким: уменьшить масштаб моих изображений + мипмэппинг. - person louissmr; 28.09.2012
comment
в зависимости от того, как выглядит ваш поток покрытия, вам может вообще не понадобиться мипмаппинг: если вы всегда визуализируете текстуры примерно одного размера. Если интерфейс таков, что использование может изменить размер окна потока обложки в любой момент, делая обложки намного меньше, это может быть полезно. Но измерить производительность с MIP-картами или без них будет легко, как только остальная часть программы заработает. - person artm; 28.09.2012
comment
Еще раз спасибо, вы мне очень помогли! - person louissmr; 28.09.2012