OpenGL:将纹理复制到主存
OpenGL: Copy texture to main memory
对于 Unity 中的一个项目,我正在编写一个本机 (C++) 插件,我的目标是将纹理数据放入主系统内存中。我知道 Unity 提供了类似的功能来实现这一点 (Texture2D.GetRawTextureData()
)。调用这个函数的不幸副作用是它每次调用都会分配一大块内存,我 运行 这个调用每帧都会经常触发垃圾收集(阅读:每一帧)
所以我在这里的尝试是编写一个使用预分配缓冲区的类似函数,将缓冲区的地址传递给 C++ 插件并将纹理数据复制到其中。但是,返回的纹理数据全是黑色(因此所有值都设置为 0)。我做错了什么,或者我在检索这些数据时忘记考虑什么?
Unity(C#)中调用插件的代码如下:
private Texture2D tempTexture = null;
private byte[] textureBuffer = null;
private IntPtr textureHandle;
public void StartStream(StreamerOptions options)
{
...
tempTexture = new Texture2D(options.width, options.height, TextureFormat.RGBA32, false);
textureBuffer = new byte[options.width * options.height * 4];
textureHandle = tempTexture.GetNativeTexturePtr();
...
}
private void OnRenderImage(RenderTexture source, RenderTexture destination)
{
if (IsStreaming)
{
Graphics.Blit(source, destination, material, 0);
tempTexture.ReadPixels(new Rect(0, 0, attachedCamera.targetTexture.width, attachedCamera.targetTexture.height), 0, 0, false);
tempTexture.Apply();
}
}
private void OnPostRender()
{
if (IsStreaming)
{
FetchTexture(textureHandle, textureBuffer);
pipe.Write(textureBuffer);
// pipe.Write(tempTexture.GetRawTextureData());
}
}
在函数OnPostRender
中我调用插件来获取纹理数据。获取OpenGL的C++代码如下:
void RenderAPI_OpenGLCoreES::FetchTextureData(void *textureHandle, uint8_t *textureData)
{
GLuint glTextureName = static_cast<GLuint>(reinterpret_cast<intptr_t>(textureHandle));
glBindTexture(GL_TEXTURE_2D, glTextureName);
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_BYTE, textureData);
}
使用 Unity Answers 上的解决方案回答我自己的问题:
https://answers.unity.com/questions/1305191/glreadpixels-from-a-rendertexture.html
我使用示例 unity native-plugin 项目作为基础,并开始剥离我不需要的功能。
其中最重要的部分是初始化 GLEW sub-system。
glewExperimental = GL_TRUE;
glewInit();
glGetError(); // Clean up error generated by glewInit
之后,我可以使用链接答案中的代码开始读取纹理数据:
void RenderAPI_OpenGLCoreES::ReadPixels(void *data, int textureWidth, int textureHeight)
{
int currentFBORead;
int currentFBOWrite;
glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, ¤tFBORead);
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, ¤tFBOWrite);
glBindFramebuffer(GL_READ_FRAMEBUFFER, currentFBOWrite);
glReadPixels(0, 0, textureWidth, textureHeight, GL_RGBA, GL_UNSIGNED_BYTE, data);
glBindFramebuffer(GL_READ_FRAMEBUFFER, currentFBORead);
}
对于 Unity 中的一个项目,我正在编写一个本机 (C++) 插件,我的目标是将纹理数据放入主系统内存中。我知道 Unity 提供了类似的功能来实现这一点 (Texture2D.GetRawTextureData()
)。调用这个函数的不幸副作用是它每次调用都会分配一大块内存,我 运行 这个调用每帧都会经常触发垃圾收集(阅读:每一帧)
所以我在这里的尝试是编写一个使用预分配缓冲区的类似函数,将缓冲区的地址传递给 C++ 插件并将纹理数据复制到其中。但是,返回的纹理数据全是黑色(因此所有值都设置为 0)。我做错了什么,或者我在检索这些数据时忘记考虑什么?
Unity(C#)中调用插件的代码如下:
private Texture2D tempTexture = null;
private byte[] textureBuffer = null;
private IntPtr textureHandle;
public void StartStream(StreamerOptions options)
{
...
tempTexture = new Texture2D(options.width, options.height, TextureFormat.RGBA32, false);
textureBuffer = new byte[options.width * options.height * 4];
textureHandle = tempTexture.GetNativeTexturePtr();
...
}
private void OnRenderImage(RenderTexture source, RenderTexture destination)
{
if (IsStreaming)
{
Graphics.Blit(source, destination, material, 0);
tempTexture.ReadPixels(new Rect(0, 0, attachedCamera.targetTexture.width, attachedCamera.targetTexture.height), 0, 0, false);
tempTexture.Apply();
}
}
private void OnPostRender()
{
if (IsStreaming)
{
FetchTexture(textureHandle, textureBuffer);
pipe.Write(textureBuffer);
// pipe.Write(tempTexture.GetRawTextureData());
}
}
在函数OnPostRender
中我调用插件来获取纹理数据。获取OpenGL的C++代码如下:
void RenderAPI_OpenGLCoreES::FetchTextureData(void *textureHandle, uint8_t *textureData)
{
GLuint glTextureName = static_cast<GLuint>(reinterpret_cast<intptr_t>(textureHandle));
glBindTexture(GL_TEXTURE_2D, glTextureName);
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_BYTE, textureData);
}
使用 Unity Answers 上的解决方案回答我自己的问题: https://answers.unity.com/questions/1305191/glreadpixels-from-a-rendertexture.html
我使用示例 unity native-plugin 项目作为基础,并开始剥离我不需要的功能。
其中最重要的部分是初始化 GLEW sub-system。
glewExperimental = GL_TRUE;
glewInit();
glGetError(); // Clean up error generated by glewInit
之后,我可以使用链接答案中的代码开始读取纹理数据:
void RenderAPI_OpenGLCoreES::ReadPixels(void *data, int textureWidth, int textureHeight)
{
int currentFBORead;
int currentFBOWrite;
glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, ¤tFBORead);
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, ¤tFBOWrite);
glBindFramebuffer(GL_READ_FRAMEBUFFER, currentFBOWrite);
glReadPixels(0, 0, textureWidth, textureHeight, GL_RGBA, GL_UNSIGNED_BYTE, data);
glBindFramebuffer(GL_READ_FRAMEBUFFER, currentFBORead);
}