检索纹理像素数据的最有效方法?
Most Efficient Way to Retrieve Texture Pixel Data?
我至少知道 Dx9 的 Directx,有一个纹理对象,您只能在其中获取纹理的一小部分到 CPU 可访问内存。我相信这是一个名为 "LockRect" 的函数。 OpenGL 有 glGetTexImage()
但它会抓取整个图像,如果格式与纹理的格式不同,那么它必须在传输整个纹理的基础上将整个纹理转换为新的像素格式。 OpenGL ES 中也没有此功能。帧缓冲区是另一种选择,但我可能会绑定一个帧缓冲区,其中颜色附件连接到纹理。然后是 glReadPixels
从帧缓冲区读取,所以它应该是从纹理读取。 glReadPixels
像素格式选项有限,因此必须进行转换,但我可以读取所需的像素(只有 1 个像素)。我没有使用过这种方法,但它似乎是可能的。如果有人可以确认 framebuffer 方法,那么它是一个可行的替代方法。那么这个方法也适用于OpenGL ES 2+。
还有其他方法吗?帧缓冲方法的效率如何(如果有效),它最终是否必须在读取像素之前将整个纹理转换为所需的格式,或者它是否完全由实现定义?
编辑:@Nicol_Bolas 请停止从标签中删除 OpenGL 并添加 OpenGL-ES,OpenGL-ES 不适用,OpenGL 是。这是专门针对 OpenGL 的,但我希望它尽可能与 Open ES 2+ 兼容,尽管它不一定是。如果只有 OpenGL 的解决方案可用,那么我会考虑是否值得权衡取舍。谢谢。
请注意,我在 ES 方面没有太多经验,因此在这种情况下可能有更好的方法来专门执行此操作。不过,一般要点适用于普通 OpenGL 或 ES。
首先,最重要的性能考虑应该是在您进行读取时。如果您在渲染时从显卡请求数据,您的程序(CPU 结束)将不得不停止,直到显卡 returns 数据,这将由于您无法发出而减慢渲染速度进一步渲染命令。作为一般规则,您应该始终上传、渲染、下载 - 不要混用这些过程中的任何一个,它会极大地影响速度,并且影响的程度可能非常 driver/hardware/OS 依赖。
我建议在渲染周期结束时使用 glReadPixels( )
。我怀疑该功能的格式限制与帧缓冲区格式的限制有关;此外,你真的应该使用 8 位无符号或浮点数 both of which are supported。如果您有一些边缘案例不允许任何这些支持的格式,您应该解释那是什么,因为可能有一种方法可以专门处理它。
如果您需要渲染中特定点(而不是结束)的帧缓冲区内容,请创建第二个纹理 + 帧缓冲区(再次使用相同格式)以使其有效 "backbuffer" 然后从目标帧缓冲区复制到该纹理。这发生在视频卡上,因此它不会直接读取总线延迟。这是我写的执行此操作的内容:
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( )
。
最后,您应该记住,下载数据仍然会停止 CPU 结束。如果您在显示帧缓冲区之前下载,您将推迟显示图像,直到您可以再次执行命令,这可能会导致明显的延迟。因此,我建议即使您只关心最终缓冲区状态,仍然使用非默认帧缓冲区,并结束渲染周期以达到以下效果:
(1.) Blit 到默认帧缓冲区:
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 次以上,但如果你正在处理使用过时的硬件,可能值得再考虑一下。
我至少知道 Dx9 的 Directx,有一个纹理对象,您只能在其中获取纹理的一小部分到 CPU 可访问内存。我相信这是一个名为 "LockRect" 的函数。 OpenGL 有 glGetTexImage()
但它会抓取整个图像,如果格式与纹理的格式不同,那么它必须在传输整个纹理的基础上将整个纹理转换为新的像素格式。 OpenGL ES 中也没有此功能。帧缓冲区是另一种选择,但我可能会绑定一个帧缓冲区,其中颜色附件连接到纹理。然后是 glReadPixels
从帧缓冲区读取,所以它应该是从纹理读取。 glReadPixels
像素格式选项有限,因此必须进行转换,但我可以读取所需的像素(只有 1 个像素)。我没有使用过这种方法,但它似乎是可能的。如果有人可以确认 framebuffer 方法,那么它是一个可行的替代方法。那么这个方法也适用于OpenGL ES 2+。
还有其他方法吗?帧缓冲方法的效率如何(如果有效),它最终是否必须在读取像素之前将整个纹理转换为所需的格式,或者它是否完全由实现定义?
编辑:@Nicol_Bolas 请停止从标签中删除 OpenGL 并添加 OpenGL-ES,OpenGL-ES 不适用,OpenGL 是。这是专门针对 OpenGL 的,但我希望它尽可能与 Open ES 2+ 兼容,尽管它不一定是。如果只有 OpenGL 的解决方案可用,那么我会考虑是否值得权衡取舍。谢谢。
请注意,我在 ES 方面没有太多经验,因此在这种情况下可能有更好的方法来专门执行此操作。不过,一般要点适用于普通 OpenGL 或 ES。
首先,最重要的性能考虑应该是在您进行读取时。如果您在渲染时从显卡请求数据,您的程序(CPU 结束)将不得不停止,直到显卡 returns 数据,这将由于您无法发出而减慢渲染速度进一步渲染命令。作为一般规则,您应该始终上传、渲染、下载 - 不要混用这些过程中的任何一个,它会极大地影响速度,并且影响的程度可能非常 driver/hardware/OS 依赖。
我建议在渲染周期结束时使用 glReadPixels( )
。我怀疑该功能的格式限制与帧缓冲区格式的限制有关;此外,你真的应该使用 8 位无符号或浮点数 both of which are supported。如果您有一些边缘案例不允许任何这些支持的格式,您应该解释那是什么,因为可能有一种方法可以专门处理它。
如果您需要渲染中特定点(而不是结束)的帧缓冲区内容,请创建第二个纹理 + 帧缓冲区(再次使用相同格式)以使其有效 "backbuffer" 然后从目标帧缓冲区复制到该纹理。这发生在视频卡上,因此它不会直接读取总线延迟。这是我写的执行此操作的内容:
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( )
。
最后,您应该记住,下载数据仍然会停止 CPU 结束。如果您在显示帧缓冲区之前下载,您将推迟显示图像,直到您可以再次执行命令,这可能会导致明显的延迟。因此,我建议即使您只关心最终缓冲区状态,仍然使用非默认帧缓冲区,并结束渲染周期以达到以下效果:
(1.) Blit 到默认帧缓冲区:
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 次以上,但如果你正在处理使用过时的硬件,可能值得再考虑一下。