OpenGL ES(OpenGL for Embedded System)是以手持和嵌入式设备为目标的高级3D图形应用程序编程接口(API)。OpenGL ES是当今智能手机中占据统治地位的图形API,其作用范围已经扩展到桌面,OpenGL ES支持的平台包括IOS、Android、BlackBerry、bada、Linux和Windows,它还是基于浏览器的3D图形Web标准WebGL的基础。当然本篇主要介绍Android平台上对OpenGL ES的一些支持。
本章不详细介绍Android图形系统,有兴趣的可以点击我进行参考。
由ViewRootImpl发起performTraversals开始View的绘制流程:
每个Window对应一个Surface,任何View都要画在Surface的Canvas上。图形的传递是通过Buffer作为载体,Surface是对Buffer的进一步封装,也就是说Surface内部具有多个Buffer供上层使用,如何管理这些Buffer呢?请看下面这个模型:
Surface对应生产者代理对象,Surface(Native)对应生产者本地对象。那么流程就是:上层app通过Surface获取buffer,供上层绘制,绘制过程通过Canvas来完成,底层实现是skia引擎,绘制完成后数据通过Surface被queue进BufferQueue, 然后监听会通知SurfaceFlinger去消费buffer, 接着SurfaceFlinger就acquire数据拿去合成, 合成完成后会将buffer release回BufferQueue。如此循环,形成一个Buffer被循环使用的过程。另外,这个过程有这么几个状态:
所以Surface主要干两件事:获取Canvas来干绘制的活;申请buffer并把Canvas最终生产的图形、纹理数据放进去。
surfaceflinger是Android图形系统中一个比较核心独立的Service,它接收所有Surface作为输入,创建layer(其主要的组件是一个 BufferQueue)与Surface一一对应,根据ZOrder, 透明度,大小,位置等参数,计算出每个layer在最终合成图像中的位置,然后交由HWComposer或OpenGL生成最终的栅格化数据, 放到layer的framebuffer上。
Layer是SurfaceFlinger 进行合成的基本操作单元,其主要的组件是一个 BufferQueue。Layer在应用请求创建Surface的时候在SurfaceFlinger内部创建,因此一个Surface对应一个 Layer。Layer 其实是一个 FrameBuffer,每个 FrameBuffer 中有两个 GraphicBuffer 记作 FrontBuffer 和 BackBuffer。
Hardware Composer HAL (HWC) 在 Android 3.0 中被引入。它的主要目标是通过可用硬件确定组合缓冲区的最有效方式。
SurfaceFlinger 为 HWC 提供完整的 layers 的列表并询问,“你想要如何处理它?”。
这样设计的好处是可以充分发挥硬件性能,同时降低SurfaceFlinger和硬件平台的耦合度(方便移植),另外SurfaceFlinger能将一些合成工作委托给Hardware Composer,从而降低来自OpenGL和GPUd的负载。总结下来它有两种合成方式:
经过上述分析,我们大概了解了整个显示数据的产生、传送、合成的过程以及相关类在这个过程中所起的作用,最后总结一张图形数据流:
显示屏上的内容,是从硬件帧缓冲区读取的,大致读取过程为:从Buffer的起始地址开始,从上往下,从左往右扫描整个Buffer,将内容映射到显示屏上。
下图显示的是双缓冲:一个FrontBuffer用于提供屏幕显示内容,一个BackBuffer用于后台合成下一帧图形。
假设前一帧显示完毕,后一帧准备好了,屏幕将会开始读取下一帧的内容,也就是开始读取上图中的后缓冲区的内容.此时,前后缓冲区进行一次角色互换,之前的后缓冲区变为前缓冲区,进行图形的显示,之前的前缓冲区则变为后缓冲区,进行图形的合成。
总结下渲染Android应用视图的渲染流程:测量流程用来确定视图的大小、布局流程用来确定视图的位置、绘制流程最终将视图绘制在应用窗口上。Android应用程序窗口UI首先是使用Canvas通过Skia图形库API来绘制在一块画布上,实际地是通过Surface绘制在这块画布里面的一个图形缓冲区中,这个图形缓冲区最终会通过layer的形式交给SurfaceFlinger来合成,而合成后栅格化数据的操作交由HWComposer或OpenGL生成,即将这个图形缓冲区渲染到硬件帧缓冲区中,供屏幕显示。如下图:
本节主要介绍如何利用Android图形系统来创建一个Surface作为后续OpenGL渲染的载体。本章内容主要仿照安卓原生系统的bootanim的方式来创建两个图层,供后面章节在这两个图层上通过opengl来绘制一些图形控件。bootanimation在源代码路径为frameworks/base/cmds/bootanimation/。
从Android图像系统简要介绍知道,我们Surface都需要通过SurfaceFlinger这个核心服务起来了才能进行,所以需要等待它的服务。目前有两种等待方式:
//frameworks/base/cmds/bootanimation/BootAnimationUtil.cpp
void waitForSurfaceFlinger() {
// TODO: replace this with better waiting logic in future, b/35253872
int64_t waitStartTime = elapsedRealtime();
sp sm = defaultServiceManager();
const String16 name("SurfaceFlinger");
const int SERVICE_WAIT_SLEEP_MS = 100;
const int LOG_PER_RETRIES = 10;
int retry = 0;
while (sm->checkService(name) == nullptr) {
retry++;
if ((retry % LOG_PER_RETRIES) == 0) {
ALOGW("Waiting for SurfaceFlinger, waited for %" PRId64 " ms", elapsedRealtime() - waitStartTime);
}
usleep(SERVICE_WAIT_SLEEP_MS * 1000);
};
int64_t totalWaited = elapsedRealtime() - waitStartTime;
if (totalWaited > SERVICE_WAIT_SLEEP_MS) {
ALOGI("Waiting for SurfaceFlinger took %" PRId64 " ms", totalWaited);
}
}
void waitForSurfaceFlinger() {
char property[PROPERTY_VALUE_MAX] = {0};
while (!property_get("init.svc.surfaceflinger", property, nullptr)) {
if (strcmp(property, "runing", strlen("runing"))) {
break;
}
usleep(SERVICE_WAIT_SLEEP_MS * 1000);
}
}
void createSurface() {
SurfaceComposerClient::Transaction t;
sp session = new SurfaceComposerClient();
sp token = SurfaceComposerClient::getInternalDisplayToken();
// 保存当前显示屏信息
DisplayInfo dinfo;
// 获取当前显示屏信息
SurfaceComposerClient::getDisplayInfo(token, &dinfo);
// 当前显示屏宽
int surfaceW = dinfo.w;
// 当前显示屏高
int surfaceH = dinfo.h;
// 当前图层旋转180°
int orient = 2;
Rect destRect = Rect(surfaceW, surfaceH);
Rect viewPort = destRect;
// 设置后面创建图层的宽高旋转信息 其中参数orient表示旋转枚举
t.setDisplayProjection(token, orient, viewPort, destRect);
// 创建第一个native图层
sp controlPlayer = session->createSurface(string8("SHEN_Bootvideo_Player"), surfaceW, surfaceH, PIXEL_FORMAT_BGRA_8888);
// 创建第二个native图层
sp controlUI = session->createSurface(string8("SHEN_Bootvideo_UI"), surfaceW, surfaceH, PIXEL_FORMAT_BGRA_8888);
// 设置图层上下位置
t.setLayer(controlPlayer, 0x03FFFFFF).apply();
t.setLayer(controlUI , 0x04000000).apply();
}
在使用Android原生的mediaplayer一些服务的时候,可能涉及到视频层与图形层之间挖洞操作,这个时候就需要得到新创建图层的IGraphicBufferProducer了。如下代码使用了自动锁详情请点击:
// mediaplayer拿到IGraphicBufferProducer之后可以作为setVideoSurfaceTexture接口的参数
sp surfacPlayer;
sp GetSideBand() {
Mutex::AutoLock autoL(g_mtxSideBand);
g_cdtSideBand.wait(g_mtxSideBand);
return surfacPlayer->getIGraphicBufferProducer();
}
void initSurfacePlayer() {
Mutex::AutoLock autoL(g_mtxSideBand);
surfacPlayer = controlPlayer->getSurface();
g_cdtSideBand.signal();
}
在Bootvideo中使用opengl渲染的时候出现如下错误: E BufferQueueProducer: [HiBootVideo_UI#0] attempting to exceed the max dequeued buffer count(2)
OpenGL ES通过eglSwapBuffers通知gpu交换数据帧绘制整个界面的时候缓冲满了,gpu渲染就是通过这个Buffer以消费者的设计方式,在绘制的很慢或者刷新的很快的时候就会出现缓冲数量不够。这个时候可以通过libnativewindow.so的某个接口手动为当前Surface设置缓冲数量,如下代码:
//Android.bp
//头文件引入路径:frameworks/native/libs/nativewindow/include/system/window.h
//动态库引入:libnativewindow.so
#include
void initSurfaceUI() {
sp surfacUI = controlUI->getSurface();
//设置6个缓冲
native_window_set_buffer_count(surfaceUI.get(), 6);
}
参考:ANativeWindow 和 Surface