Объект PixelBuffer и glReadPixel для блокировки Android (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 size
GLES30.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 call
ByteBuffer byteBuffer = (ByteBuffer) GLES30.glMapBufferRange(GLES30.GL_PIXEL_PACK_BUFFER, 0, mPboSize, GLES30.GL_MAP_READ_BIT); //downdload from the GPU to CPU
Bitmap 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 не должен занимать какое-то время, но он занимает около 25 мс (в Google Pixel 2) и создает растровое изображение, занимая еще 40 мс. Это достигается только как 13 FPS, что хуже, чем glReadPixels без PBO.
Есть ли что-то, что мне не хватает или не так в моем коде?
Ответы
Ответ 1
EDITED, так как вы указали, что моя первоначальная гипотеза была неправильной (исходный PboIndex == PboNextIndex). Надеясь быть полезным, вот код С++, который я только что написал на родной стороне, вызванный через JNI с Android, используя GLES 3. Кажется, что он работает, а не блокирует 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);
...
предыдущая необоснованная гипотеза:
В вашем вопросе не отображается код, который устанавливает начальные значения mPboIndex и mPboNewIndex, но если они установлены на одинаковые начальные значения, например 0, то они будут иметь соответствующие значения в каждом цикле, что приведет к отображению тот же самый PBO, который только что был прочитан. В этом гипотетическом/реальном сценарии, даже если используются 2 PBOs, они не чередуются между glReadPixels и glMapBufferRange, которые затем блокируются до тех пор, пока GPU не завершит передачу данных. Я предлагаю это изменение, чтобы гарантировать, что ОПО чередуются:
mPboNewIndex = mPboIndex;
mPboIndex = (mPboNewIndex + 1) % 2;