#c #performance #opengl #screenshot #freeglut
#c #Производительность #opengl #скриншот #freeglut
Вопрос:
Мне нужно делать снимки в каждом кадре, и мне нужна очень высокая производительность (я использую freeGlut). Я понял, что это можно сделать так внутри glutIdleFunc(thisCallbackFunction)
GLubyte *data = (GLubyte *)malloc(3 * m_screenWidth * m_screenHeight);
glReadPixels(0, 0, m_screenWidth, m_screenHeight, GL_RGB, GL_UNSIGNED_BYTE, data);
// and I can access pixel values like this: data[3*(x*512 y) color] or whatever
Это действительно работает, но у меня с этим огромная проблема, это действительно медленно. Когда мое окно 512×512, оно работает не быстрее 90 кадров в секунду, когда отображается только куб, без этих двух строк оно работает со скоростью 6500 кадров в секунду! Если мы сравним его с графическим движком irrlicht, там я могу это сделать
// irrlicht code
video::IImage *screenShot = driver->createScreenShot();
const uint8_t *data = (uint8_t*)screenShot->lock();
// I can access pixel values from data in a similar manner here
и окно 512×512 работает со скоростью 400 кадров в секунду даже при загруженной огромной сетке (карта Quake 3)! Примите во внимание, что я использую OpenGL в качестве драйвера внутри irrlicht. На мой неопытный взгляд кажется glReadPixels
, что он копирует данные каждого пикселя из одного места в другое, а (uint8_t*)screenShot->lock()
просто копирует указатель на уже существующий массив. Могу ли я сделать что-то подобное последнему, используя freeGlut? Я ожидаю, что это будет быстрее, чем irrlicht.
Обратите внимание, что irrlicht также использует OpenGL (ну, он также предлагает DirectX и другие опции, но в приведенном выше примере я использовал OpenGL, и, кстати, он был самым быстрым по сравнению с другими вариантами)
Комментарии:
1. Найдите объекты буфера пикселей, которые позволяют вам делать это асинхронно.
2. Если вы поместите этот код в функцию ожидания, он может вызываться более одного раза за кадр. Кроме того, у вас происходит утечка памяти, если вы никогда не освобождаете выделяемые вами данные.
3. @RetoKoradi ну, нет, я не выполняю эти строки буквально каждый раз
idle
, когда вызывается.data
выделяется только один раз в конструкторе, и я использую его снова и снова. Теперь я изучаю PBO….
Ответ №1:
Для управления конвейером рендеринга используются методы OpenGL. По своей природе, пока видеокарта показывает изображение зрителю, выполняются вычисления следующего кадра. Когда вы вызываете glReadPixels
; графическая карта ожидает завершения текущего кадра, считывает пиксели, а затем начинает вычислять следующий кадр. Поэтому конвейер останавливается и становится последовательным.
Если вы можете удерживать два буфера и указывать видеокарте считывать данные в эти буферы, меняя местами каждый кадр; тогда вы можете считывать данные из своего буфера с опозданием на 1 кадр, но без остановки конвейера. Это называется двойной буферизацией. Вы также можете выполнить тройную буферизацию с поздним чтением 2 кадров и так далее.
Существует относительно старая веб-страница, описывающая явление и реализацию здесь: http://www.songho.ca/opengl/gl_pbo.html
Также в Интернете есть много руководств по фреймбуферам и рендерингу в текстуру. Один из них здесь: http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-14-render-to-texture /
Комментарии:
1. Итак, я понял, как визуализировать текстуру, но я не знаю, как переместить эту текстуру в основной буфер, чтобы она отображалась на экране, и, самое главное, как получить доступ к значениям пикселей из этой текстуры с помощью индексов (как я показал в вопросе). Что мне делать?
2. Вы можете использовать
glBlitFramebuffer
, привязав задний буфер asDRAW_BUFFER
и привязав свой фреймбуфер текстуры какREAD_BUFFER
априори. Это для вывода изображения на экран. Для чтения значений пикселей вы должны использовать объект буфера пикселей (он же PBO).3. Мне удалось переместить изображение из объекта буфера кадров (где я визуализировал свою сцену) в основной буфер, чтобы оно отображалось на экране, но я все еще не могу получить доступ к значениям пикселей. Я не совсем понимаю, где должен появиться PBO. Этот учебник ( songho.ca/opengl/gl_pbo.html ) мне кажется, не так-то просто следовать, исходный код имеет длину 600 строк и выглядит действительно запутанным. Что мне делать? Задайте другой вопрос конкретно об этом? Я даже не знаю, что именно спросить. Любые советы будут оценены.
4. К сожалению, у меня нет рабочего примера кода. Основная идея songho.ca/opengl/gl_pbo.html вы привязываете один из 2 PBO
GL_PIXEL_PACK_BUFFER
, и вашglReadPixels
становится мгновенным на стороне клиента (ваш код), но вы можете читать из буфера только в том случае, если предыдущие операции с буфером выполнялись на стороне сервера (видеокарта). Поэтому вы можете прочитать предыдущий кадр почти мгновенно.5. кстати, я использую интегрированный графический процессор (Intel HD graphics 4000), насколько я знаю, CPU и GPU используют одну и ту же память, так почему мне нужно загружать? Почему невозможно получить указатель?