上文主要介绍了BufferQueue的设计思想和内部实现,本文将介绍对于BufferQueue的常用封装和使用例子。
3.BufferQueue常用封装类
在实际应用中,除了直接使用BuferQueue外,更多的是使用Surface/SurfaceTexture,其对BufferQueue做了包装,方便业务更方便的使用BufferQueue。Surface作为BufferQueue的生产者,SurfaceTexture作为BufferQueue的消费者。
3.1 Surface
Surface的构造函数如下:
代码语言:javascript复制Surface::Surface(
const sp<IGraphicBufferProducer>& bufferProducer,
bool controlledByApp)
: mGraphicBufferProducer(bufferProducer),
mGenerationNumber(0)
构造函数需要传入一个生产者的引用,和BufferQueue的交互均由这个生产者的引用来完成。dequeueBuffer的流程如下:
代码语言:javascript复制int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) {
// 1. 调用mGraphicBufferProducer的dequeueBuffer方法,尝试获取一个Slot索引
int buf = -1;
sp<Fence> fence;
status_t result = mGraphicBufferProducer->dequeueBuffer(&buf, &fence, swapIntervalZero,
reqWidth, reqHeight, reqFormat, reqUsage);
if (result < 0) {
ALOGV("dequeueBuffer: IGraphicBufferProducer::dequeueBuffer(%d, %d, %d, %d, %d)"
"failed: %d", swapIntervalZero, reqWidth, reqHeight, reqFormat,
reqUsage, result);
return result;
}
//2. 调用mGraphicBufferProducer的requestBuffer方法,尝试获取Slot
sp<GraphicBuffer>& gbuf(mSlots[buf].buffer);
if ((result & IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) || gbuf == 0) {
result = mGraphicBufferProducer->requestBuffer(buf, &gbuf);
if (result != NO_ERROR) {
ALOGE("dequeueBuffer: IGraphicBufferProducer::requestBuffer failed: %d", result);
mGraphicBufferProducer->cancelBuffer(buf, fence);
return result;
}
}
// 3. 返回GraphicBuffer
*buffer = gbuf.get();
}
queueBuffer也是如下,流程如下:
代码语言:javascript复制int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) {
IGraphicBufferProducer::QueueBufferOutput output;
IGraphicBufferProducer::QueueBufferInput input(timestamp, isAutoTimestamp,
mDataSpace, crop, mScalingMode, mTransform ^ mStickyTransform,
mSwapIntervalZero, fence, mStickyTransform);
// 1. 直接调用mGraphicBufferProducer的queueBuffer方法即可
status_t err = mGraphicBufferProducer->queueBuffer(i, input, &output);
if (err != OK) {
ALOGE("queueBuffer: error queuing buffer to SurfaceTexture, %d", err);
}
}
Surface还提供了lock函数,用来支持双缓冲,内部也是调用dequeueBuffer方法获取最新的Buffer:
代码语言:javascript复制status_t Surface::lock(
ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds)
{
ANativeWindowBuffer* out;
int fenceFd = -1;
//1. 获取实际Buffer
status_t err = dequeueBuffer(&out, &fenceFd);
//2. 处理双缓冲
if (canCopyBack) {
// copy the area that is invalid and not repainted this round
const Region copyback(mDirtyRegion.subtract(newDirtyRegion));
if (!copyback.isEmpty())
copyBlt(backBuffer, frontBuffer, copyback);
}
}
Surface也提供了unlockAndPost方法,将数据给到BufferQueue:
代码语言:javascript复制status_t Surface::unlockAndPost()
{
if (mLockedBuffer == 0) {
ALOGE("Surface::unlockAndPost failed, no locked buffer");
return INVALID_OPERATION;
}
int fd = -1;
status_t err = mLockedBuffer->unlockAsync(&fd);
ALOGE_IF(err, "failed unlocking buffer (%p)", mLockedBuffer->handle);
//1. 将生产好的数据给到BufferQueue
err = queueBuffer(mLockedBuffer.get(), fd);
ALOGE_IF(err, "queueBuffer (handle=%p) failed (%s)",
mLockedBuffer->handle, strerror(-err));
mPostedBuffer = mLockedBuffer;
mLockedBuffer = 0;
return err;
}
3.2 SurfaceTexture
SurfaceTexture作为BufferQueue的消费者,其初始化代码如下:
代码语言:javascript复制static void SurfaceTexture_init(JNIEnv* env, jobject thiz, jboolean isDetached,
jint texName, jboolean singleBufferMode, jobject weakThiz)
{
sp<IGraphicBufferProducer> producer;
sp<IGraphicBufferConsumer> consumer;
//1. 创建一个BufferQueue
BufferQueue::createBufferQueue(&producer, &consumer);
if (singleBufferMode) {
consumer->disableAsyncBuffer();
consumer->setDefaultMaxBufferCount(1);
}
//2. 创建一个消费者实例surfaceTexture
sp<GLConsumer> surfaceTexture;
if (isDetached) {
surfaceTexture = new GLConsumer(consumer, GL_TEXTURE_EXTERNAL_OES,
true, true);
} else {
surfaceTexture = new GLConsumer(consumer, texName,
GL_TEXTURE_EXTERNAL_OES, true, true);
}
//3. 将消费者实例和该BufferQueue对应的生产者保存到java层,这样Surface构造时,就可以获取到该BufferQueue对应的生产者了
SurfaceTexture_setSurfaceTexture(env, thiz, surfaceTexture);
SurfaceTexture_setProducer(env, thiz, producer);
}
消费的方法是updateTexImage,流程如下:
代码语言:javascript复制static void SurfaceTexture_updateTexImage(JNIEnv* env, jobject thiz)
{
// 1. 先获取到初始化时构造的消费者
sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
// 2. 调用消费者的updateTexImage方法
status_t err = surfaceTexture->updateTexImage方法();
if (err == INVALID_OPERATION) {
jniThrowException(env, IllegalStateException, "Unable to update texture contents (see "
"logcat for details)");
} else if (err < 0) {
jniThrowRuntimeException(env, "Error during updateTexImage (see logcat for details)");
}
}
GLConsumer的updateTextImage实现如下:
代码语言:javascript复制status_t GLConsumer::updateTexImage() {
BufferItem item;
//1. 调用自身的acquireBufferLocked方法
err = acquireBufferLocked(&item, 0);:updateTexImage() {
// Release the previous buffer.
err = updateAndReleaseLocked(item);
if (err != NO_ERROR) {
glBindTexture(mTexTarget, mTexName);
return err;
}
}
acquireBufferLocked方法,最终走到了ConsumerBase的acquireBufferLocked方法。
代码语言:javascript复制status_t ConsumerBase::acquireBufferLocked(BufferItem *item,
nsecs_t presentWhen, uint64_t maxFrameNumber) {
//1. 最终还是走到了消费者的acquireBuffer方法,消费者对应上面的BufferQueueConsumer
status_t err = mConsumer->acquireBuffer(item, presentWhen, maxFrameNumber);
if (err != NO_ERROR) {
return err;
}
return OK;
}
同理,消费者消费数据的方法是releaseTexImage,最终也会走到BufferQueueConsumer的releaseBufferLocked方法,这里不再描述了。
4.BufferQueue的实例
上述介绍了BufferQueue的内部实现,以及常用的封装类。接下来将介绍一个具体的实例。
Android中,SurfaceView作为系统提供的组件,因为可以在子线程中绘制内容而提高性能,SurfaceView拥有自身的Surface,不需要和Activity的Surface共享,在SurfaceFlinger中,Activity的Surface和SurfaceView的Surface是平级且互相独立的,可以独立的进行合成。那我们来看一下SurfaceView是怎么使用BufferQueue的。
4.1 数据的生产过程
SurfaceView的Surface创建过程,这里不关注,有兴趣的可以参考 android SurfaceView绘制实现原理解析 这篇文章,我们主要关注其中与BufferQueue相关的绘制和显示步骤。
使用SuerfaceView绘制伪码如下:
代码语言:javascript复制 Canvas canvas = null;
try {
canvas = holder.lockCanvas(null);
//实际的draw
}catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}finally {
if(canvas != null) {
holder.unlockCanvasAndPost(canvas);
}
需要调用lockCanvas和unlockCanvasAndPost方法,这两个方法的作用是什么呢?
先看下lockCanvas,调用流程是:
- SurfaceHolder.lockCanvas
- SurfaceHolder.internalLockCanvas
- Surface.lockCanvas
- Surface.nativeLockCanvas
nativeLockCanvas实现如下:
代码语言:javascript复制static jlong nativeLockCanvas(JNIEnv* env, jclass clazz,
jlong nativeObject, jobject canvasObj, jobject dirtyRectObj) {
sp<Surface> surface(reinterpret_cast<Surface *>(nativeObject));
ANativeWindow_Buffer outBuffer;
//1. 通过Surface::lock方法,获取一个合适的Buffer
status_t err = surface->lock(&outBuffer, dirtyRectPtr);
//2. 构造一个Bitmap,地址指向步骤1获取的Buffer的地址,这样在这个Bitmap上绘制的内容,直接绘制到了GraphicBuffer,如果GraphicBuffer的内存是SurfaceFlinger通过共享内存申请的,那么SurfaceFlinger就能直接看到绘制的图形数据
SkImageInfo info = SkImageInfo::Make(outBuffer.width, outBuffer.height,
convertPixelFormat(outBuffer.format),
kPremul_SkAlphaType);
SkBitmap bitmap;
ssize_t bpr = outBuffer.stride * bytesPerPixel(outBuffer.format);
bitmap.setInfo(info, bpr);
if (outBuffer.width > 0 && outBuffer.height > 0) {
bitmap.setPixels(outBuffer.bits);
} else {
// be safe with an empty bitmap.
bitmap.setPixels(NULL);
}
// 3. 将创建的Bitmap设置给Canvas,作为画布
Canvas* nativeCanvas = GraphicsJNI::getNativeCanvas(env, canvasObj);
nativeCanvas->setBitmap(bitmap);
}
从这里可以看到,nativeLockCanvas的步骤主要如下:
- 通过调用Surface::lock方法(内部也是调用dequeueBuffer和requestBuffer方法),获取到一个GraphicBuffer
- 将步骤1获取的GraphicBuffer构造成一个Bitmap,设置给Canvas
- 应用通过这个Canvas就可以绘制图形了
在绘制图形完成后,调用unlockCanvasAndPost方法,调用流程是:
- SurfaceHolder.unlockCanvasAndPost
- Surface.unlockCanvasAndPost
- Surface.nativeUnlockCanvasAndPost
nativeUnlockCanvasAndPost 的实现如下:
代码语言:javascript复制static void nativeUnlockCanvasAndPost(JNIEnv* env, jclass clazz,
jlong nativeObject, jobject canvasObj) {
sp<Surface> surface(reinterpret_cast<Surface *>(nativeObject));
if (!isSurfaceValid(surface)) {
return;
}
// detach the canvas from the surface
Canvas* nativeCanvas = GraphicsJNI::getNativeCanvas(env, canvasObj);
nativeCanvas->setBitmap(SkBitmap());
// 直接调用Surface的unlockAndPost方法,有上文可知unlockAndPost内部最终也会调用到qeueBuffer方法
status_t err = surface->unlockAndPost方法,有上文可知unlockAndPost内部最终也会调用到qeueBuffer方法();
if (err < 0) {
doThrowIAE(env);
}
}
从注释可以看到,这个方法,最终会调用到Surface的unlockAndPost方法方法,而该方法内部最终也会调用到BufferQueueProducer的queueBuffer方法。即完成了数据的生产和入队。
4.2 数据的消费过程
SurfaceView绘制的数据,传递过BufferQueue后,最终由SurfaceFlinger进行合成消费。SurfaceFlinger的消费由SurfaceFlingerConsumer实现,流程如下:
代码语言:javascript复制status_t SurfaceFlingerConsumer::updateTexImage(BufferRejecter* rejecter,
const DispSync& dispSync, uint64_t maxFrameNumber)
{
BufferItem item;
// 1. 调用acquireBufferLocked获取一个Slot
err = acquireBufferLocked(&item, computeExpectedPresent(dispSync),
maxFrameNumber);
if (err != NO_ERROR) {
return err;
}
//2. 消费完毕,释放Slot
err = updateAndReleaseLocked(item);
if (err != NO_ERROR) {
return err;
}
}
acquireBufferLocked的实现如下:
代码语言:javascript复制status_t SurfaceFlingerConsumer::acquireBufferLocked(BufferItem* item,
nsecs_t presentWhen, uint64_t maxFrameNumber) {
//1. 调用 GLConsumer::acquireBufferLocked,最终会调用到BufferQueueConsumer的acquireBuffer方法
status_t result = GLConsumer::acquireBufferLocked(item, presentWhen,
maxFrameNumber);
if (result == NO_ERROR) {
mTransformToDisplayInverse = item->mTransformToDisplayInverse;
mSurfaceDamage = item->mSurfaceDamage;
}
return result;
}
而updateAndReleaseLocked方法的流程如下:
代码语言:javascript复制status_t GLConsumer::updateAndReleaseLocked(const BufferItem& item)
{
// Do whatever sync ops we need to do before releasing the old slot.
err = syncForReleaseLocked(mEglDisplay);
if (err != NO_ERROR) {
//1. releaseBufferLocked释放Slot,最终会调用到BufferQueueConsumer的releaseBuffer方法
releaseBufferLocked(buf, mSlots[buf].mGraphicBuffer,
mEglDisplay, EGL_NO_SYNC_KHR);
return err;
}
}
5. 总结
本文对BufferQueue的内部实现做了介绍,结合入队/出对说明了BufferQueue内部Slot的状态扭转过程,并介绍了常用的BufferQueue封装类,最后介绍了一个基于BufferQueue的例子。
6. 参考资料
https://cloud.tencent.com/developer/article/1033903