出门莫恨无人随,书中车马多如簇。这篇文章主要讲述PixelBuffer对象和Android上的glReadPixel(ARCore)阻塞相关的知识,希望能为你提供帮助。
我知道默认的glReadPixels()会等到GL线程上执行所有绘图命令。但是当你绑定一个PixelBuffer对象然后调用glReadPixels()时,它应该是异步的,不会等待任何事情。但是当我绑定PBO并执行glReadPixels()时,它会阻塞一段时间。
以下是我初始化PBO的方法:
mPboIds = IntBuffer.allocate(2);
GLES30.glGenBuffers(2, mPboIds);
GLES30.glBindBuffer(GLES30.GL_PIXEL_PACK_BUFFER, mPboIds.get(0));
GLES30.glBufferData(GLES30.GL_PIXEL_PACK_BUFFER, mPboSize, null, GLES30.GL_STATIC_READ);
//allocates only memory space given data sizeGLES30.glBindBuffer(GLES30.GL_PIXEL_PACK_BUFFER, mPboIds.get(1));
GLES30.glBufferData(GLES30.GL_PIXEL_PACK_BUFFER, mPboSize, null, GLES30.GL_STATIC_READ);
GLES30.glBindBuffer(GLES30.GL_PIXEL_PACK_BUFFER, 0);
然后我用两个缓冲区来打乒乓:
GLES30.glBindBuffer(GLES30.GL_PIXEL_PACK_BUFFER, mPboIds.get(mPboIndex));
//1st PBO
JNIWrapper.glReadPixels(0, 0, mRowStride / mPixelStride, (int)height, GLES30.GL_RGBA, GLES30.GL_UNSIGNED_BYTE);
//read pixel from the screen and write to 1st buffer(native C++ code)//don't load anything in the first frame
if (mInitRecord) {
GLES30.glBindBuffer(GLES30.GL_PIXEL_PACK_BUFFER, 0);
//reverse the index
mPboIndex = (mPboIndex + 1) % 2;
mPboNewIndex = (mPboNewIndex + 1) % 2;
mInitRecord = false;
return;
}GLES30.glBindBuffer(GLES30.GL_PIXEL_PACK_BUFFER, mPboIds.get(mPboNewIndex));
//2nd PBO
//glMapBufferRange returns pointer to the buffer object
//this is the same thing as calling glReadPixel() without a bound PBO
//The key point is that we can pipeline this callByteBuffer byteBuffer = (ByteBuffer) GLES30.glMapBufferRange(GLES30.GL_PIXEL_PACK_BUFFER, 0, mPboSize, GLES30.GL_MAP_READ_BIT);
//downdload from the GPU to CPUBitmap bitmap = Bitmap.createBitmap((int)mScreenWidth,(int)mScreenHeight, Bitmap.Config.ARGB_8888);
bitmap.copyPixelsFromBuffer(byteBuffer);
GLES30.glUnmapBuffer(GLES30.GL_PIXEL_PACK_BUFFER);
GLES30.glBindBuffer(GLES30.GL_PIXEL_PACK_BUFFER, 0);
//reverse the index
mPboIndex = (mPboIndex + 1) % 2;
mPboNewIndex = (mPboNewIndex + 1) % 2;
每帧都会在我的绘图方法中调用它。根据我的理解,glReadPixels不应该花费任何时间,但它需要大约25ms(在Google Pixel 2上)并且创建位图需要另外40ms。这只能达到13 FPS,比没有PBO的glReadPixels差。
我的代码中是否有任何遗漏或错误的内容?
答案编辑,因为你指出我的原始假设是不正确的(初始PboIndex == PboNextIndex)。希望有所帮助,这里是我刚刚在本机上编写的C ++代码,使用GLES 3通过JNI从android调用。它似乎工作而不是阻止glReadPixels(...)。请注意,只有一个glPboIndex变量:
glBindBuffer(GL_PIXEL_PACK_BUFFER, glPboIds[glPboIndex]);
glReadPixels(0, 0, frameWidth_, frameHeight_, GL_RGBA, GL_UNSIGNED_BYTE, 0);
glPboReady[glPboIndex] = true;
glPboIndex = (glPboIndex + 1) % 2;
if (glPboReady[glPboIndex]) {
glBindBuffer(GL_PIXEL_PACK_BUFFER, glPboIds[glPboIndex]);
GLubyte* rgbaBytes = (GLubyte*)glMapBufferRange(
GL_PIXEL_PACK_BUFFER, 0, frameByteCount_, GL_MAP_READ_BIT);
if (rgbaBytes) {
size_t minYuvByteCount = frameWidth_ * frameHeight_ * 3 / 2;
// 12 bits/pixel
if (videoFrameBufferSize_ <
minYuvByteCount) {
return;
// !!! not logging error inside render loop
}
convertToVideoYuv420NV21FromRgbaInverted(
videoFrameBufferAddress_, rgbaBytes,
frameWidth_, frameHeight_);
}
glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
glPboReady[glPboIndex] = false;
}
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
...
以前毫无根据的假设:
【PixelBuffer对象和Android上的glReadPixel(ARCore)阻塞】您的问题没有显示设置mPboIndex和mPboNewIndex的初始值的代码,但是如果将它们设置为相同的初始值(例如0),那么它们将在每个循环中具有匹配的值,这将导致映射相同的PBO刚读过。在该假设/真实场景中,即使正在使用2个PBO,它们也不会在glReadPixels和glMapBufferRange之间交替,然后它们将阻塞,直到GPU完成数据传输。我建议这种改变以确保公益组织交替:
mPboNewIndex = mPboIndex;
mPboIndex = (mPboNewIndex + 1) % 2;
推荐阅读
- 使用OpenGL ES 2.0在Android上创建Audio wave
- 如何在Android上的GvrView中显示2D图像()
- 如何确定Android OpenGL ES上的最大纹理内存
- 如何在android中实现Drag Distortion Image过滤器()
- 没有引擎的Android 2D游戏开发
- 在OpenGL ES(Android)中使用带纹理的索引缓冲区有什么意义吗()
- 如何使用GPU Video-android获取Watermark并在视频上进行过滤()
- Android Native(CMake链接错误:GL函数的未定义引用 - 即使包含和链接了EGL和GLESv3)
- 在不同的Android设备上有哪些有效的PixelFormats()