android图形系统组件 深入剖析android新特性 笔记

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u012654756/article/details/88981285

9.2 图形系统组件

图形系统的实现涉及比较多的模块,代码在:

frameworks/base/core/jni/

frameworks/native/libs/ui/

frameworks/native/libs/gui/

frameworks/native/service/.surfaceflinger/.

hardware/libhardware/

9.2.1 Acitivt与Surface

Android系统的四大组件中,只有Activity包含界面

接下来以Activity为起点,来了解它与整个图形系统的关系

在Android系统中,每个Activity包含一个描述窗口的Window对象,

Window对象的根元素是一个名称为DocorView的试图。

DocorView包含了一个类型为ViewRootImpl的元素,如这个元素名称所示,它是所有视图的根元素,

为了能在显示屏显示,ViewRootImpl会包含一个Surface对象,而Surface正是图形系统的重要组件。

这几个类的关系:

Activity

| getWindow()

Window

| getDecorView()

DecorView

| getViewRootImpl()

ViewRootImpl

| mSurface

Surface

上面提到的结构都是java层的类,

而在surface的实现中会通过JNI调用native层的代码,关系:

ViewRootImpl.java => Surface.java => android_view_Surface.cpp => Surface.cpp

Surface很多逻辑都是在C++层实现的,

所以Surface.java里面包含了一系列native方法,

下面这个方法最重要,负责Surface分配缓冲:

private static native void nativeAllocateBuffers(long nativeObject);

这个native方法对应的JNI方法位于android_view_Surface.cpp,代码:

static void nativeAllocateBuffers(JNIEnv* /* env */, jclass /*clazz */, jlong nativeObject)

{

sp<Surface> surface(reinterpret_cast<Surface *>(nativeObject));

if(!isSurfaceValid(surface)){ return; }

surface->allocateBuffers();

}

很显然,这里真正的实现在C++层的Surface::allocateBuffers方法中,代码:

/Surface.cpp

void Surface::allocateBuffers()

{

mGraphicBufferProducer->allocateBuffers(reqWidth, reqHeight, mReqForemat, mReqUsage);

}

这里通过缓冲区的生产者进行buffer的分配,

目前感觉不好理解正常,这一节会详细讲解最重要的部件

9.2.2 Gralloc

Gralloc,graphics memory allocator是最底层的组件,如其名字,负责图形内存的分配,

AOSP中,系统为Gralloc模块拟定了接口,在

hardware/libhardware/include/hardware/gralloc.h

Gralloc模块是与硬件紧密相关的,因此,它的实现不是AOSP的一部分,而是硬件设备厂商完成的,

厂商实现一个动态链接库so,在运行时被加载和使用,系统仅仅定义了头文件,

gralloc.h包含了gralloc_module_t和alloca_device_t两个结构体,定义:

typedef struct grallolc_module_t {

struct hw_module_t common;

int (*registerBuffer)(struct gralloc_module_t const* module, buffer_hdnle_t handle);

int (lock)(struct gralloc_module_t const* module, buffer_handle_t handle, int usage, int l/t/w/h, void** vaddr);

int (*perform);

int (*lockAsync);

.....

}gralloc_module_t;

typedef struct alloc_device_t {

struct hw_device_t common;

int (*alloc)(struct alloc_device_t *dev, int w/h/format/usage, buffer_hanlde_t* handle, int* stride);

int (*free);

int (*dump);

}alloc_device_t;

API说明:

alloc_deivce_t alloc,分配一个图形缓冲,通过buffer_handle_t返回

alloc_device_t free

alloc_device_t dump

gralloc_module_t registerBuffer,从alloc_device_t::alloc返回的buffer_handle_t必须经过这个调用才可以使用,

gralloc_module_t lock,特定使用场景前调用,标识调用者只会修改指定区域内的内容

gralloc_module_t perform,保留接口,以后扩展使用

gralloc_module_t lock_ycbcr,类似lock,但它使用缓冲布局的描述填充ycbcr,

gralloc_module_t lockAsync,类似lock,但是异步的,缓冲的同步栅栏对象被传递到锁里面,而不是让调用者等待。

gralloc_module_t unlockAsync,返回缓冲区同步栅栏对象

注意,由于图形缓冲区通常是很大的数据,如果在进程间传递,性能会很差。

所以这里的操作都是通过buffer_handle_t来对图形缓冲进程标识,这是一个轻量的文件描述符结构的句柄,这个句柄可以轻松的在进程间传递。

9.2.3,bufferQueue

BufferQueues是android图形组件的粘合剂,

它是将缓冲池与队列相结合的数据结构,使用BinderIPC在生产者和消费者进程之间传递缓冲区。

在实现中:

缓冲区通过GraphicsBuffer描述,

BufferQueues的生产者使用IGraphicBufferProducer描述,

BufferQueues的生产者使用IgraphicsBufferConsumer描述,

图像生产者的一些例子是由相机HAL或者OGLES游戏制作的相机预览,但最常见的生产者是activity,

图像消费者的一些示例是SurfaceFlinger,或者另一个显示OGLES流的AP,如显示相机取景器的相机AP。

一旦Acitivty生产者提交了buffer,SF消费者就将其合成并且显示在显示器上:

queue acquire

PRODUCER BUFFER QUEUE CONSUMER

dequeue release

BufferQueue可以在三种模块运行:

同步模式,

默认BufferQueue以同步模式运行,从生产者产生的每个缓冲区都通过消费者消费掉,

此模式下不会丢弃任何缓冲区。

如果生产者太快,请创建缓冲区比他们消耗的速度快,它将阻塞并且等待可用的缓冲区。

非阻塞模式,

BufferQueue也可以在非阻塞模式运行。这时他会产生错误而不是等待缓冲区,

这种模式下不会丢弃缓冲区,

这对于避免可能不了解图形框架的复杂依赖性的AP中欧冠潜在死锁很有用,

丢弃模式,

BufferQUeue的状态

生产者和消费者使用BufferQueue的方法:

1,生产者通过dequeueBuffer()请求一个空闲的buffer,并且指定相关的参数,如w,h,format像素格式

2,生产者填充buffer内容后会通过queueBuffer()将其放入队列中,

3,之后,消费者会通过acquireBuffer()使用Buffer的内容

4,消费者用完,releaseBuffer()归还给队列。

FREE --> IGraphicBufferProducer::dequeueBuffer() -->

DEQUEUED --> IGraphicBufferProducer::queueBuffer() -->

QUEUED --> IGraphicBufferConsumer::acquireBuffer() -->

ACQUIED --> IGraphicBufferConsumer::releaseBuffer()-->

屏幕上内容的更新,就是通过对BufferQUeue的缓冲区不断的进出队列实现的,

GraphicBufferAllocator,

GraphicBufferAllocator负责缓冲的份额皮,这个类的结构简单,主要提供了allocate方法分配缓冲,还有free,

GraphicBufferAllocator最终要依赖于Gralloc模块完成缓冲的分配和释放,因此其接口也通过buffer_handle_t标识缓冲区。

9.2.4 Surface

前面提到过Surface组件,Surface是Android从API Level 1就公开的API。

Surface是BufferQueue的生产者一端,而SurfaceFlinger通常是消费者一端

当渲染Surface时,结果将通过BufferQueue传递给消费者。

Surface的BufferQueue通常配置为三重缓冲(见下节Project Butter),

但缓冲是按需分配的。

所以如果生产者缓慢的生成buffer--也许是在60fps的显示器上以30fps的速度动画,则队列可能只有两个分配的buffer,

这样有助于最小化内存消耗。

通过adb shell dumpsys SurfaceFlinger输出图形系统的详细信息

以前的Android版本,所有渲染都是软件完成的,虽然现在也可以这样做。底层实现由Skia图形库提供。

如果要绘制一个矩形,则进程库调用,并在buffer中适当设置内容,

为确保两个客户端不会同时更新buffer,或者在显示时写入buffer,必须先锁定buffer才可以访问。

lockCanvas()用来lock buffer并返回Canvas用于绘图,unlockCanvasAndPost()用来unlock buffer并发送到合成器。

后来,具有通用3D引擎的设备出现了,Android重新定位在OpenGLES上,

而重要的是保持旧的API适用于APP和APP框架代码,所以系统通过硬件加速Canvas API。

Canvas提供给View的onDraw()方法可能会被硬件加速,

但是当APP直接使用lockCanvas()锁定Surface时获得的Canvas一定不会使用硬件加速,

为了访问Canvas而锁定Surface时,“CPU渲染器”连接到BufferQueue的生产者端,直到Surface被销毁时才断开连接。

多数其他生产者(如GLES)可以断开连接并且重新连接到Surface,但是基于Canvas的“GPU渲染器”是不行的。

这意味着,如果已经将其锁定在画布上,则无法使用GLES绘制曲面或者从视频解码器发送帧

生产者首次从BufferQueue请求缓冲区时,将被分配并init为0,。

init是必要的,来避免无意间在进程之间共享数据。

而在重新使用buffer时,以前的内容仍然在。

若反复调用lockCanvas()和unlockCanvasAndPost(),而不绘制内容,则会在先前渲染的帧之间循环。

Surface 锁定/解锁代码保留对先前渲染的缓冲区的引用。

如果在锁定Surface时指定了脏区域,那么它将从前一个缓冲区复制非脏像素。

buffer可能由SF或者HWC处理,但由于我们只需要读,所以不用等待独占访问。

软件绘制的实现

ViewRootImpl中的drawSoftware负责软件绘制的逻辑,

//ViewRootImpl.java

private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff, boolean scalingRequired, Rect dirty){

final Canvas canvas;

try {

final int left = dirty.left;

final int top = dirty.top;

final int right .....

canvas = mSurface.lockCanvas(ditry); ①

...

}

try {

...

if(!canvas.isOpaque() || yoff != 0 || xoff != 0) {

canvas.drawColor(0, PorterDuff.Mode.CLEAR);

}

...

try {

canvas.translate(-xoff, -yoff);

if(mTranslator != null) mTranslator.translateCanvas(canvas);

}

....

mView.draw(canvas); ②

drawAccessibilityFocusedDrawableIfNeeded(canvas);

} finally {

...

}

} finally {

try {

surface.unlockCanvasAndPost(canvas); ③

} catch(IllegalArgumentException e) {

}

...

}

}

这段代码的主要逻辑:

1,lock界面中要绘制的部分

2,将View的内容画到Canvas上

3,post绘制的结果

Surface.unlockCanvasAndPost方法中提交结果的方式就是向BufferQueue传递缓冲,逻辑:

ViewRootImpl.drawSoftware JAVA层

Surface.unlockCanvasAndPost

Surface.unlockSwCanvasAndPost

Surface.nativeUnlockCanvasAndPost JNI层

Surface::unlockAndPost C++层

Surface::queueBuffer

IGraphicBufferProducer::queueBuffer

这样ViewRootImpl中相关区域的内容便进行了更新

http://10.30.252.36/source/xref/zx-android-7.1.2_r36_ZX2100/frameworks/base/tests/HwAccelerationTest/src/com/android/test/hwui/

本节内容总结:

Acitivity

Surface SurfaceFlinger

IGraphicBufferProducer <-> BufferQUeue <-> IGraphicBufferConsumer

GraphicBufferAllocator

Gralloc

http://elinux.org/images/2/2b/Android_graphics_path-chis_simmonds.pdf

http://www.cnblogs.com/samchen2009/p/3367496.html

http://lindt.blog.51cto.com/9699125/1864591

猜你喜欢

转载自blog.csdn.net/u012654756/article/details/88981285