Самый эффективный способ получить данные текстуры пикселей?

Я знаю, что Directx для Dx9, по крайней мере, имеет объект текстуры, где вы можете получить только небольшую часть текстуры в доступную для процессора память. Я считаю, что это была функция под названием "LockRect". OpenGL имеет glGetTexImage() но он захватывает все изображение, и если формат не совпадает с форматом текстуры, то ему придется преобразовать всю текстуру в новый формат пикселей поверх передачи всей текстуры. Эта функция также отсутствует в OpenGL ES. Framebuffers - это еще один вариант, но где я мог бы потенциально связать кадровый буфер, где привязка цвета связана с текстурой. Тогда есть glReadPixels который читает из кадрового буфера, поэтому он должен читать из текстуры. glReadPixels имеет ограниченные параметры формата пикселей, поэтому преобразование должно произойти, но я могу прочитать нужные мне пиксели (это всего 1 пиксель). Я не использовал этот метод, но кажется, что это возможно. Если кто-то может подтвердить метод framebuffer, то это рабочая альтернатива. Тогда этот метод также будет работать для OpenGL ES 2+.

Есть ли другие методы? Насколько эффективен метод фреймбуфера (если он работает), нужно ли в конечном итоге преобразовывать всю текстуру в нужный формат, прежде чем он читает пиксели, или это полностью определяется реализацией?

Редактировать: @Nicol_Bolas Пожалуйста, прекратите удаление OpenGL из тегов и добавление OpenGL-ES, OpenGL-ES не применимо, OpenGL есть. Это специально для OpenGL, но я бы хотел, чтобы он был совместим с Open ES 2+, если это возможно, хотя это не обязательно. Если доступно только решение OpenGL, то я рассмотрю его, если оно того стоит. Спасибо.

1 ответ

Решение

Обратите внимание, что у меня нет особого опыта работы с ES, в частности, поэтому могут быть более эффективные способы сделать это именно в этом контексте. Общая суть применима либо в обычном OpenGL, либо в ES.


Прежде всего, наиболее важным фактором производительности должно быть, когда вы делаете чтение. Если вы запрашиваете данные с видеокарты во время рендеринга, ваша программа (сторона ЦП) должна будет остановиться до тех пор, пока видеокарта не вернет данные, что замедлит рендеринг из-за вашей неспособности выдавать дополнительные команды рендеринга. Как правило, вы всегда должны загружать, отображать, загружать - не смешивайте ни один из этих процессов, это очень сильно повлияет на скорость, и насколько это будет зависеть от драйверов / оборудования / ОС.

Я предлагаю использовать glReadPixels( ) в конце вашего цикла рендеринга. Я подозреваю, что ограничения на форматы для этой функции связаны с ограничениями на форматы кадрового буфера; кроме того, вы действительно должны использовать 8-битные без знака или с плавающей запятой, оба из которых поддерживаются. Если у вас есть какой-то случай, когда вы не можете использовать какой-либо из этих поддерживаемых форматов, вы должны объяснить, что это такое, так как может быть способ решить это конкретно.

Если вам нужно содержимое кадрового буфера в определенной точке рендеринга (а не в конце), создайте второй текстурный + кадровый буфер (опять же с тем же форматом), чтобы он был эффективным "буферным буфером", а затем скопируйте из целевого кадрового буфера в него текстура. Это происходит на видеокарте, поэтому это не накладывает задержки на чтение шины. Вот что я написал, что делает эту операцию:

glActiveTexture( GL_TEXTURE0 + unit );
glBindTexture( GL_TEXTURE_2D, backbufferTextureHandle );
glBindFramebuffer( GL_READ_FRAMEBUFFER, framebufferHandle );
glCopyTexSubImage2D(
        GL_TEXTURE_2D,
        0, // level
        0, 0, // offset
        0, 0, // x, y
        screenX, screenY );
glBindFramebuffer( GL_DRAW_FRAMEBUFFER, framebufferHandle );

Затем, когда вы хотите получить данные, свяжите буфер с GL_READ_FRAMEBUFFER и используйте glReadPixels( ) в теме.

Наконец, вы должны иметь в виду, что загрузка данных все равно остановит процессор. Если вы загружаете до отображения кадрового буфера, вы откладываете отображение изображения до тех пор, пока не сможете снова выполнить команды, что может привести к видимой задержке. Поэтому я по-прежнему предлагаю использовать кадровый буфер не по умолчанию, даже если вы заботитесь только о конечном состоянии буфера, и заканчиваете цикл рендеринга с эффектом:

(1.) Перейдите к кадровому буферу по умолчанию:

glBindFramebuffer( GL_DRAW_FRAMEBUFFER, 0 ); // Default framebuffer
glBindFramebuffer( GL_READ_FRAMEBUFFER, framebufferHandle );
glBlitFramebuffer(
        0, 0, screenX, screenY,
        0, 0, screenX, screenY,
        GL_COLOR_BUFFER_BIT,
        GL_NEAREST );

(2.) Вызовите, какой бы ни была ваша команда буферов обмена в данной ситуации.

(3.) Ваш вызов загрузки из кадрового буфера (будь то glReadPixels( ) или что-то другое).

Что касается влияния на скорость операций blit/texcopy, то оно весьма неплохо для большинства современных аппаратных средств, и я не обнаружил, что оно оказало заметное влияние даже на 10+ кадров в кадре, но если вы имеете дело с устаревшим оборудованием, это может быть стоит подумать

Другие вопросы по тегам