1

I know Directx for Dx9 at least, has a texture object where you are able to get only a small portion of the texture to CPU accessible memory. It was a function called "LockRect" I believe. OpenGL has glGetTexImage() but it grabs the entire image and if the format isn't the same as the texture's then it is going to have to convert the entire texture into the new pixel format on top of transferring the entire texture. This function is also not in OpenGL ES. Framebuffers is another option but where I could potentially bind a framebuffer where a color attachment in connected to a texture. Then there is glReadPixels which reads from the framebuffer, so it should be reading from the texture. glReadPixels has limited pixel format options so a conversion is going to have to happen, but I can read the pixels I need (which is only 1 pixel). I haven't used this method but it seems like it is possible. If anyone can confirm the framebuffer method, that it is a working alternative. Then this method would also work for OpenGL ES 2+.

Are there any other methods? How efficient is the framebuffer method (if it works), does it end up having to convert the entire texture to the desired format before it reads the pixels or is it entirely implementation defined?

Edit: @Nicol_Bolas Please stop removing OpenGL from tags and adding OpenGL-ES, OpenGL-ES isn't applicable, OpenGL is. This is for OpenGL specifically but I would like it to be Open ES 2+ compatible if possible, though it doesn't have to be. If a OpenGL only solution is available then it is a consideration I will make if it is worth the trade off. Thank you.

4

1 回答 1

2

请注意,我在 ES 方面没有太多经验,所以在这种情况下可能有更好的方法来做这件事。不过,一般要点适用于普通的 OpenGL 或 ES。


首先,最重要的性能考虑应该是在阅读时。如果您在渲染时从视频卡请求数据,您的程序(CPU 端)将不得不暂停,直到视频卡返回数据,由于您无法发出进一步的渲染命令,这将减慢渲染速度。作为一般规则,您应该始终上传、渲染、下载 - 不要混合这些过程中的任何一个,它会极大地影响速度,而这在很大程度上取决于驱动程序/硬件/操作系统。

我建议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( )在其上使用。

最后,您应该记住,下载数据仍然会停止 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 次以上也没有发现它有明显的影响,但如果你正在处理过时的硬件,它可能是值得三思。

于 2016-02-27T02:57:01.663 回答