Android 图形架构 之六——深入分析draw()是如何工作的
Posted 薛瑄
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 图形架构 之六——深入分析draw()是如何工作的相关的知识,希望对你有一定的参考价值。
前言
前面我们分析了 app进程中的 addView 的执行过程,其中涉及到了ViewRootImpl,WMS,Session,WindowState,Surface 等等,最后分析到performDraw() 函数,接下来,我们就继续来分析,带着两个问题,它是如何把数据写入生产者队列的,已经SurfaceFlinger 何时开始处理的?
Android 图形架构 之一 ——概述
Android 图形架构 之二—— SurfaceFlinger 启动和连接
Android 图形架构 之三—— 创建Layer、Surface、SurfaceControl
Android 图形架构 之四——图形缓冲区的申请和消费流程及核心类
Android 图形架构 之五——深入分析addView所发生的的一切
Android 图形架构 之六——深入分析draw()是如何工作的
Android 图形架构 之七——Choreographer 源码分析
android图形架构 之八——硬件VSync、VSync-app、Vsync-sf
流程分析
本文要分析的主流程,如下图所示:
接下来就从performDraw 开始分析:
文件路径:frameworks/base/core/java/android/view/ViewRootImpl.java
代码一:
private void performDraw()
......
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "draw");
try
draw(fullRedrawNeeded);
finally
mIsDrawing = false;
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
......
private void draw(boolean fullRedrawNeeded)
.....
if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty))
return;
......
代码二:
/**
* @return true if drawing was successful, false if an error occurred
*/
private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
boolean scalingRequired, Rect dirty)
// Draw with software renderer.
.....
//申请的图形缓冲区,并锁定,用于在上面写数据,代码三分析
canvas = mSurface.lockCanvas(dirty);
.....
//调用系统的api(这里指Canvas 类的 ),向缓冲区写入数据,这里也会调用我们熟悉的onDraw 函数
mView.draw(canvas);
.....
//解锁缓冲区,入队,并通知SurfaceFlinger 去消费,下面会分析
surface.unlockCanvasAndPost(canvas);
.....
return true;
lockCanvas
来看看mSurface.lockCanvas(dirty);
位于 frameworks/base/core/java/android/view/Surface.java:
代码三:
public Canvas lockCanvas(Rect inOutDirty)
throws Surface.OutOfResourcesException, IllegalArgumentException
synchronized (mLock)
checkNotReleasedLocked();
//调用Native代码
//mCanvas 里面有SkBitmap,SkBitmap里面的数据 指向了缓冲区队列中 某一个BufferSlot 对应的 GraphicBuffer
mLockedObject = nativeLockCanvas(mNativeObject, mCanvas, inOutDirty);
return mCanvas;
位于 frameworks/base/core/jni/android_view_Surface.cpp:
代码四:
static jlong nativeLockCanvas(JNIEnv* env, jclass clazz,
jlong nativeObject, jobject canvasObj, jobject dirtyRectObj)
//获取java层的Surface保存的long型句柄
sp<Surface> surface(reinterpret_cast<Surface *>(nativeObject));
......
Rect dirtyRect(Rect::EMPTY_RECT);
Rect* dirtyRectPtr = NULL;
//获取java层dirty Rect的位置大小信息
if (dirtyRectObj)
dirtyRect.left = env->GetIntField(dirtyRectObj, gRectClassInfo.left);
dirtyRect.top = env->GetIntField(dirtyRectObj, gRectClassInfo.top);
dirtyRect.right = env->GetIntField(dirtyRectObj, gRectClassInfo.right);
dirtyRect.bottom = env->GetIntField(dirtyRectObj, gRectClassInfo.bottom);
dirtyRectPtr = &dirtyRect;
ANativeWindow_Buffer outBuffer;
//调用Surface的lock方法,获取一个缓冲区,并赋值给outBuffer
//代码五分析
status_t err = surface->lock(&outBuffer, dirtyRectPtr);
......
//创建一个SkImageInfo ,用于下面创建SkBitmap
SkImageInfo info = SkImageInfo::Make(outBuffer.width, outBuffer.height,
convertPixelFormat(outBuffer.format),
outBuffer.format == PIXEL_FORMAT_RGBX_8888
? kOpaque_SkAlphaType : kPremul_SkAlphaType);
// 使用outBuffer为SkBitmap变量赋值
SkBitmap bitmap;
//获取图形缓冲区每一行像素大小
ssize_t bpr = outBuffer.stride * bytesPerPixel(outBuffer.format);
bitmap.setInfo(info, bpr);
if (outBuffer.width > 0 && outBuffer.height > 0)
//把outBuffer的数据,设置到bitmap中
bitmap.setPixels(outBuffer.bits);
else
// be safe with an empty bitmap.
bitmap.setPixels(NULL);
Canvas* nativeCanvas = GraphicsJNI::getNativeCanvas(env, canvasObj);
//把刚才创建的SkBitmap ,设置到Canvas 中,后续 就可以在这个bitmap上绘图了
nativeCanvas->setBitmap(bitmap);
if (dirtyRectPtr)
//剪裁出dirty区域大小
nativeCanvas->clipRect(dirtyRect.left, dirtyRect.top,
dirtyRect.right, dirtyRect.bottom, SkClipOp::kIntersect);
if (dirtyRectObj)
//将剪裁位置大小信息赋给java层Canvas对象
env->SetIntField(dirtyRectObj, gRectClassInfo.left, dirtyRect.left);
env->SetIntField(dirtyRectObj, gRectClassInfo.top, dirtyRect.top);
env->SetIntField(dirtyRectObj, gRectClassInfo.right, dirtyRect.right);
env->SetIntField(dirtyRectObj, gRectClassInfo.bottom, dirtyRect.bottom);
// Create another reference to the surface and return it. This reference
// should be passed to nativeUnlockCanvasAndPost in place of mNativeObject,
// because the latter could be replaced while the surface is locked.
sp<Surface> lockedSurface(surface);
lockedSurface->incStrong(&sRefBaseOwner);
return (jlong) lockedSurface.get();
接下来分析Surface的lock函数,它的功能是申请图形缓冲区,并锁定这块缓冲区,后面就可调用draw函数,来绘制了
函数位于frameworks/native/libs/gui/Surface.cpp:
代码五:
status_t Surface::lock(
ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds)
if (mLockedBuffer != nullptr)
ALOGE("Surface::lock failed, already locked");
return INVALID_OPERATION;
if (!mConnectedToCpu)
// 调用connect函数, 连接cpu
int err = Surface::connect(NATIVE_WINDOW_API_CPU);
if (err)
return err;
// we're intending to do software rendering from this point
setUsage(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN);
ANativeWindowBuffer* out;
int fenceFd = -1;
//调用dequeueBuffer函数,申请图形缓冲区,代码六会分析
status_t err = dequeueBuffer(&out, &fenceFd);
ALOGE_IF(err, "dequeueBuffer failed (%s)", strerror(-err));
if (err == NO_ERROR)
//获取图形缓冲区区域大小,赋给后备缓冲区变量backBuffer
sp<GraphicBuffer> backBuffer(GraphicBuffer::getSelf(out));
//该缓冲区的宽高,赋值给bounds
const Rect bounds(backBuffer->width, backBuffer->height);
Region newDirtyRegion;
//是否在脏区域边界,newDirtyRegion表示新的脏区域
if (inOutDirtyBounds)
//设置新的脏区域
newDirtyRegion.set(static_cast<Rect const&>(*inOutDirtyBounds));
//脏区域和 缓冲区区域 作 与运算,求交集
newDirtyRegion.andSelf(bounds);
else
//不是只刷新脏区域,则待刷新区域,是整个缓冲区的宽高
newDirtyRegion.set(bounds);
// figure out if we can copy the frontbuffer back
//上一次绘制的信息保存在mPostedBuffer中,而这个mPostedBuffer则要在unLockAndPost函数中设置
const sp<GraphicBuffer>& frontBuffer(mPostedBuffer);
//比较 本次 和 上次的缓冲区,宽、高、格式是否一致
const bool canCopyBack = (frontBuffer != nullptr &&
backBuffer->width == frontBuffer->width &&
backBuffer->height == frontBuffer->height &&
backBuffer->format == frontBuffer->format);
if (canCopyBack)
// copy the area that is invalid and not repainted this round
// 获取到需要更新的脏区域
const Region copyback(mDirtyRegion.subtract(newDirtyRegion));
if (!copyback.isEmpty())
//把frontBuffer 数据拷贝到backBuffer,后续只用更新该脏区域的数据,提高了效率
copyBlt(backBuffer, frontBuffer, copyback, &fenceFd);
else
// if we can't copy-back anything, modify the user's dirty
// region to make sure they redraw the whole buffer
//如果两次缓冲区的大小,格式不一致,则设置脏区域为当前缓冲区的大小 ,后续更新整个缓冲区
newDirtyRegion.set(bounds);
mDirtyRegion.clear();
Mutex::Autolock lock(mMutex);
for (size_t i=0 ; i<NUM_BUFFER_SLOTS ; i++)
mSlots[i].dirtyRegion.clear();
// scope for the lock
Mutex::Autolock lock(mMutex);
//得到此次后备缓冲器的bufferSlot在mSlots里面的index
int backBufferSlot(getSlotFromBufferLocked(backBuffer.get()));
if (backBufferSlot >= 0)
//将新的dirty赋给这个bufferslot
Region& dirtyRegion(mSlots[backBufferSlot].dirtyRegion);
mDirtyRegion.subtract(dirtyRegion);
dirtyRegion = newDirtyRegion;
//两个脏区域,或运算,相叠加
mDirtyRegion.orSelf(newDirtyRegion);
if (inOutDirtyBounds)
*inOutDirtyBounds = newDirtyRegion.getBounds();
void* vaddr;
//lock和unlock分别用来锁定和解锁一个指定的图形缓冲区,在访问一块图形缓冲区的时候,
//例如,向一块图形缓冲写入内容的时候,需要将该图形缓冲区锁定,用来避免访问冲突,
//锁定之后,就可以获得由参数参数l、t、w和h所圈定的一块缓冲区的起始地址,保存在输出参数vaddr中
status_t res = backBuffer->lockAsync(
GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
newDirtyRegion.bounds(), &vaddr, fenceFd);
ALOGW_IF(res, "failed locking buffer (handle = %p)",
backBuffer->handle);
if (res != 0)
err = INVALID_OPERATION;
else
//mLockedBuffer 表示锁定的buffer,指向当前绘制的buffer backBuffer
mLockedBuffer = backBuffer;
//outBuffer 是要返回的数据,使用backBuffer 为它赋值
outBuffer->width = backBuffer->width;
outBuffer->height = backBuffer->height;
outBuffer->stride = backBuffer->stride;
outBuffer->format = backBuffer->format;
//指向一块缓冲区的起始地址,这块缓冲区被锁定了,接下来就可以在上面写入数据
outBuffer->bits = vaddr;
return err;
注意在Surface 中也有一个结构体BufferSlot,代码如下,它和BufferQueueCore中的BufferSlot 类型有点差别。前者是在app进程中,后者是在SurfaceFlinger进程中
struct BufferSlot
sp<GraphicBuffer> buffer;
Region dirtyRegion;
;
接下来 调用了Surface::dequeueBuffer
代码六:
int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd)
......
FrameEventHistoryDelta frameTimestamps;
//在生产者-消费者队列,申请图形缓冲区
status_t result = mGraphicBufferProducer->dequeueBuffer(&buf, &fence, reqWidth, reqHeight,
reqFormat, reqUsage, &mBufferAge,
enableFrameTimestamps ? &frameTimestamps
: nullptr);
.....
if (buf < 0 || buf >= NUM_BUFFER_SLOTS)
ALOGE("dequeueBuffer: IGraphicBufferProducer returned invalid slot number %d", buf);
android_errorWriteLog(0x534e4554, "36991414"); // SafetyNet logging
return FAILED_TRANSACTION;
Mutex::Autolock lock(mMutex);
// Write this while holding the mutex
mLastDequeueStartTime = startTime;
//这里的mSlots,是Surface中的BufferSlot数组,其中buffer 类型是GraphicBuffer
sp<GraphicBuffer>& gbuf(mSlots[buf].buffer);
if (CC_UNLIKELY(atrace_is_tag_enabled(ATRACE_TAG_GRAPHICS)))
static FenceMonitor hwcReleaseThread("HWC release");
hwcReleaseThread.queueFence(fence);
//如果所有状态buffer的标志位是RELEASE_ALL_BUFFERS,表示个数大于64,就要释放所有超过64的buffer
if (result & IGraphicBufferProducer::RELEASE_ALL_BUFFERS)
freeAllBuffers();
if (enableFrameTimestamps)
mFrameEventHistory->applyDelta(frameTimestamps);
//新分配的BufferSlot的状态是BUFFER_NEEDS_REALLOCATION标志,代表需要重新分配了GraphicBuffer
if ((result & IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) || gbuf == nullptr)
if (mReportRemovedBuffers && (gbuf != nullptr))
mRemovedBuffers.push_back(gbuf);
//由于申请内存的操作是在surfaceflinger进程中,当前在app进程,所以这是一个进程间通信
//buf 存储的是BufferSlot在mSlot 的索引,gbuf 存储是GraphicBuffer
//把buf 对应的BufferSlot 对应的GraphicBuffer,指针赋值给gbuf
result = mGraphicBufferProducer->requestBuffer(buf, &gbuf);
.....
......
//获取这个buffer对象的指针内容
*buffer = gbuf.get();
if (mSharedBufferMode && mAutoRefresh)
mSharedBufferSlot = buf;
mSharedBufferHasBeenQueued = false;
else if (mSharedBufferSlot == buf)
mSharedBufferSlot = BufferItem::INVALID_BUFFER_SLOT;
mSharedBufferHasBeenQueued = false;
return OK;
调用BufferQueueProducer的dequeueBuffer函数申请一块图形缓冲区,这个在文章 Android 图形架构 之四——图形缓冲区的申请和消费流程及核心类 分析过了
这里分析一下BufferQueueProducer的requestBuffer,它是通过IPC Binder调用远程接口,(Binder 通信的过程就不分析了)直接看实现,位于frameworks/native/libs/gui/BufferQueueProducer.cpp:
在SurfaceFlinger 创建Layer 的时候,调用createBufferQueue,进而创建了BufferQueueProducer,这些操作是在SurfaceFlinger 进程中的 ,可阅读Android 图形架构 之三—— 创建Layer、Surface、SurfaceControl 和 Android 图形架构 之四——图形缓冲区的申请和消费流程及核心类 来了解这些
该函数 是根据指定index取出mSlots中的slot中的buffer。
代码七:
status_t BufferQueueProducer::requestBuffer(int slot, sp<GraphicBuffer>* buf)
ATRACE_CALL();
BQ_LOGV("requestBuffer: slot %d", slot);
Mutex::Autolock lock(mCore->mMutex);
if (mCore->mIsAbandoned)
BQ_LOGE("requestBuffer: BufferQueue has been abandoned");
return NO_INIT;
//check index and state
if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS)
BQ_LOGE("requestBuffer: slot index %d out of range [0, %d)",
slot, BufferQueueDefs::NUM_BUFFER_SLOTS);
return BAD_VALUE;
else if (mSlots[slot].mBufferState != BufferSlot::DEQUEUED)
BQ_LOGE("requestBuffer: slot %d is not owned by the producer "
"(state = %d)", slot, mSlots[slot].mBufferState);
return BAD_VALUE;
//注意,这里已经到了SurfaceFlinger进程中,此时的BufferSlot的Graphicbuffer 数据存储在变量 mGraphicBuffer
//将slot对应的BufferSlot mRequestBufferCalled 标志位置为true,并返回BufferSlot的Graphicbuffer
mSlots[slot].mRequestBufferCalled = true;
*buf = mSlots[slot].mGraphicBuffer;
return NO_ERROR;
draw
接着回到代码二,mView.draw(canvas);
,调用Canvas 的api 在这块缓冲区上,进行数据的绘制。此处我们就省略了,下面看看SurfaceFlinger 如何使用这块缓冲区
unlockCanvasAndPost
接下来 在代码二中,调用了 surface.unlockCanvasAndPost(canvas);
,主要作用就是把缓冲区入队,通知SurfaceFlinger进行渲染
代码路径:frameworks/base/core/java/android/view/Surface.java
代码八:
public void unlockCanvasAndPost(Canvas canvas)
synchronized (mLock)
checkNotReleasedLocked();
if (mHwuiContext != null)
mHwuiContext.unlockAndPost(canvas);
else
unlockSwCanvasAndPost(canvas);
private void unlockSwCanvasAndPost(Canvas canvas)
.以上是关于Android 图形架构 之六——深入分析draw()是如何工作的的主要内容,如果未能解决你的问题,请参考以下文章
Android 图形架构 之七——Choreographer 源码分析
Android 图形架构 之七——Choreographer 源码分析
Android 图形架构 之二—— SurfaceFlinger 启动和连接
Android 图形架构 之二—— SurfaceFlinger 启动和连接