文档内容拷贝自:
codebase_host/yunhal/opendoc/zh/yunhal_doc/graphics.md
联系人:@陆生华
本文档详细描述了YunOS Graphics Framework的整体软件框架设计, 侧重于YunOS Graphics Hardware Abstract Layer的介绍,并对芯片厂商如何实现YunOS Graphics Hardware Abstract Layer提供了指导。
文中涉及YunOS Graphics Hardware Abstract Layer的部分主要包括下面几块:
图2.1展示了YunOS Graphics Framework整体软件架构图:
在YunOS graphics framework架构图中,进程之间的通信是基于Wayland协议,而各个模块之间的交互实体是graphic buffer, 它承载了每个模块的加工成果,并按照既定的时序从一个模块流转到另一个模块,从而达到各个模块对graphic buffer的共享。
在YunOS App中,在它需要绘制UI或者流媒体内容之前,它首先需要向libgui 申请一块graphic buffer, 在UI绘制结束或者流媒体内容填充结束之后,需要把这块graphic buffer交还给libgui。
在YunOS graphics framework中,当libgui收到app的buffer请求时,它会调用YunHAL Yalloc提供的接口,为app申请一个graphic buffer。如果是用OpenGL ES来渲染,则app需要在申请buffer之前,通过YunHAL EGL接口,初始化相应的上下文。当收到app提交的buffer后,它会通过Buffer Queue的Wayland IPC ,把这块buffer提交给图形混合器,并形成一次合成请求。图形混合器根据收集到的需要合成layers的特点做相应处理,一种是选择GPU 3D来进行合成,另一种是尽可能利用Hardware Flinger硬件合成(取决于硬件显示特性)。当合成结束后,再把合成结果送到display device中显示。
在YunHAL Graphics中,主要有三个模块:EGL/GLES, Yalloc以及Hardware Flinger,它们分别负责GPU渲染,graphic buffer分配,释放与管理,以及硬件合成。 这三个模块有序的参与到graphic buffer生命周期中的不同阶段,需要厂商根据YunOS定义的接口分别实现。
在YunOS Graphics Stack中,正是这些模块纵向接力,横向配合,才最终生成完整的显示画面。
Yalloc是负责graphic buffer管理的HAL层模块,它主要负责graphic buffer的分配,释放,跨进程获取以及与显示设备之间的通信。
对于Yalloc模块,它跟framework的交互主要是通过yalloc_device_t和fb_device_t。当Framework需要申请buffer时,首先需要调用yalloc_open接口得到yalloc_device_t的指针,然后通过yalloc_device_t调用alloc方法得到gb_target_t, framework拿到gb_target_t后,就可以把它封装成NativeSurfaceBuffer,而NativeSurface操作的buffer就是NativeSurfaceBuffer。
对于fb_device_t,它是图形混合器跟display driver交互的接口,当图形混合器初始化的时候,需要通过framebuffer_open得到fb_device_t,通过fb_device_t,可以获得display device的一些基本信息,比如 resolution, refresh rate, Dots Per Inch, display format等。
图2是yunOS framework与Yalloc交互的接口模型图:
对于图形混合器而言,当需要从Yalloc分配graphics buffer的时候,首先需要调用yalloc_open获得yalloc_device_t, 然后调用alloc接口得到标识Yalloc buffer的gb_target_t,此时如果需要获得这个buffer的虚拟地址,可以通过map方法,得到相应的虚拟地址,这样就可以通过cpu操作读写这块地址空间的内容,当这块buffer的读写操作结束后,yalloc_device_t需要调用unmap取消虚拟地址空间映射;如果不再需要这块buffer,则需要调用free释放相应的内存空间。当W图形混合器需要打开并操作显示设备的时候,需要调用framebuffer_open获得fb_device_t, 这样就可以从fb_device_t中获得显示设备的resolution, refresh rate, Dots Per Inch, display format等信息, 最终可以通过fb_device_t的publish方法将compose结果flip到display device上。
对client来说,如果想要通过Yalloc分配graphics buffer, 首先需要调用yalloc_open获得yalloc_device_t并向weston发起分配buffer的请求,weston通过Yalloc分配好buffer后,通过Wayland协议跨进程传送gb_target_t给client端,然后client端需要调用authorizeBuffer取得授权,当client端需要获取这块buffer虚拟地址的时候,同样需要调用map/unmap操作,若不在需要这块buffer,则需要调用unAuthorizeBuffer取消授权。
EGL是由Khronos Group 提供的一组平台无关的API。它的功能:
OpenGL ES 和 OpenGL 一样,是 khronos 所维护、定义的免授权费、跨平台的 3D Graphics API。
在YunOS中对于EGL模块,它跟framework的交互主要是靠NativeSurface来完成的,当client调用eglCreateWindowSurface时,我们需要传入的EGLNativeWindowType是NativeSurface,当OpenGL driver需要申请framework的buffer进行图形渲染时,它就可以通过NativeSurface的*obtainBuffer*接口得到NativeSurfaceBuffer,当openGL driver渲染完毕后,必须把buffer交还给framework, 此时就要调用NativeSurface的*sumbitBuffer*,framework得知OpenGL driver已经完成渲染后,通过Wayland协议跨进程通信,告诉Weston新的更新已提交,图形混合器收到更新通知后,需要等待vsync事件,当新的vsync事件到来时,它会把这个更新生成eglImage并通过OpenGL操作画到framebuffer上,然后再通过fb_device_t的publish接口送到display driver去显示,这样client端的更新内容就显示到了屏幕上。
对于EGL接口,我们需要定义相对应的**EGLNativeDisplayType**和**EGLNativeWindowType**, 它们是OpenGL driver与YunOS graphics framework的交互窗口。
由于OpenGL是一种跨平台的图形库,对于不同的操作系统,需要定义不同的EGLNativeDisplayType和EGLNativeWindowType作为eglGetDisplay和eglCreateWindowSurface的函数参数。
在YunOS中,我们定义的EGLNativeDisplayType和EGLNativeWindowType分别是void*和NativeSurface, 此外,我们也在eglext.h中定义了EGL_YUNHAL_DISPLAY 作为YunOS的native display, 当client需要调用OpenGL图形库API进行渲染时,它首先要调用eglGetDisplay获得EGLDisplay,接着调用eglChooseConfig获取所需要的配置,然后通过eglCreateWindowSurface获得EGLSurface, 这样就可以通过EGLDisplay和EGLSurface进行一系列的初始化和渲染操作。
Hardware Flinger模块负责图形合成工作,具体的实现可以是overlay或2D硬件合成模块。跟GPU合成相比,Hardware Flinger合成降低了对内存的读写操作和GPU负载, 优化了系统的性能和功耗。图2.4展示了Hardware Flinger的接口模型。
在图形混合器中,当YunOS Backend初始化的时候,需要通过*hwf_open*打开HW Flinger模块, HW Flinger模块创建hwf_device_t并返回hwf_device_t指针给framework,这样Weston就可以通过hwf_device_t的指针和HW Flinger模块交互了。
在初始化过程中,图形混合器需要调用*queryDispConfigs*接口获取Hardware Flinger显示设备的基本信息(宽,高和格式等),并根据这些信息创建所需要的display window显示窗口。为了接收vsync和hotplug等信息,图形混合器还需要调用*registerCallback*向Hardware Flinger注册回调函数,当vsync事件或者hotplug事件发生时,Hardware Flinger就可以通过回调函数发送Weston所需要的vsync时间戳以及热插拔等信息。
当图形混合器收到vsync事件时,它会发起一次合成请求。首先需要检查当前可见的layer的个数以及之间的z-order顺序,然后根据z-order顺序创建相应的hwf_layer_t实例,并调用Hardware Flinger的detect函数,Hardware Flinger根据接收到的hwf_layer_t列表,确认每个layer是否可以通过Hardware Flinger来合成,并设置相应的合成方式。当图形混合器从detect函数返回时,它会检查layer的合成方式,如果是HW_FB, 则需要通过OpenGL来合成,如果是HW_OVERLAY,则跳过OpenGL的合成,直接调用Hardware Flinger的flip函数,通过overlay或者2D Engine的方式来达到合成的目的。
为了更好的关系YunOS系统graphics memory, 我们在libgui中实现了一个通用的buffer queue,使不同场合下的graphic buffer管理统一化。基于该buffer queue实现窗口管理进程与渲染进程的分离。Surface与buffer queue分离,buffer queue可以独立工作,外部使用更加灵活。
图2.5为buffer queue的总体结构示意图。
Buffer queue由producer和consumer两个主要部分组成。这里的Client/Server仅为了方便描述,事实上producer/consumer各自可在任意两个进程,也可以处于同一个进程。IPC基于Wayland协议,不依赖于其它service。Producer端和consumer端基于domain socket建立点对点的连接。它们维护各自的buffer列表,并通过专用Wayland协议进行跨进程的传输,从而形成graphic buffer交换的循环。其中的graphic buffer为跨进程对象,由server端或client端来分配并控制其生命周期。Graphic buffer在client和server端分别进行封装,其封装类分别为NativeWindowBuffer和RemoteWindowBuffer。其中的buffer handle指向同一块memory,从而实现buffer的共享。以上为图中指示的buffer queue core部分,可以脱离其它部件独立使用。
实际使用中,为了让graphics driver可以通过EGL层使用buffer,需要有NativeSurface的实现类Surface包装BufferQueueProducer。这样client端的渲染模块就可以通过硬件渲染来往buffer上绘制。另一端,ConsumerBase用于封装和扩展BufferQueueConsumer,server端可通过继承ConsumerBase来实现consumer端对buffer的处理逻辑。如需要将buffer作为gl纹理可使用GLConsumer,如果想通过CPU直接操作可使用SWConsumer。另外ProducerListener和ConsumerListener是可选的,用于让producer注册监听buffer release时的listener,和consumer注册监听buffer available时的listener。
这里分两种情况,一种是窗口管理与渲染处于不同进程。图2.6中只列出与buffer管理相关的步骤。首先App端向Weston端申请创建surface,然后图形混合器返回用于建立buffer queue的socket name作为id,接着App端将该id传给渲染进程,最后渲染进程用该id与图形混合器建立连接。之后渲染进程可以直接向图形混合器发起buffer的交换申请;第二种情况下,窗口管理与渲染处于同一进程,则不需要第3步的fd传输过程。其余步骤类似。总之,这一部分主要是为了窗口控制与渲染的进程分离,从而为基于通用buffer queue的统一buffer管理架构打下基础。注意这里只是模型的抽象示意图,具体使用时图中的渲染进程可以是media server等。
Buffer queue实现采用了多线程设计。与buffer queue相关的基本线程模型如图2.7所示。其中producer端与consumer端除了主线程外,会有专门的dispatcher线程来处理wayland events。这样,主线程中不需要关心Wayland的通信,可以专注于业务逻辑。
另外,为了简化模型及将来更好的扩展性,buffer的传输使用了专用的Wayland协议。其中主要包括了buffer handle及buffer属性的传输和通知。未来可能会根据需要不断扩充。
通用buffer queue的设计目标之一是可以适用于多种不同场景,而不局限于on-screen渲染中client与compositor间的buffer交换。这就要求buffer queue的核心组件可以独立使用。以下是一个祼用buffer queue的简单例子。为了更清晰的展示基本用法,错误处理等非核心代码已略去。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | namespace yg = yunos::libgui; void producer() { yg::BaseNativeSurfaceBuffer *nwb; int fencefd; auto producer = std::make_shared<yg::BufferQueueProducer>(producerFd); ... producer->dequeueBuffer(&nwb, &fencefd); ... // Do rendering. producer->queueBuffer(nwb, - 1 ); return ; } int consumer( void ) { auto consumer = yg::BufferQueue::makeBufferQueue(&producerFd) ... // pass the producer fd to the producer process. std::shared_ptr<yg::BufferHolder> buffer; consumer->acquireBuffer(buffer); // No explicit releaseBuffer() is needed. ... return ; } |
如前面所述,producer与consumer代码可以在不同进程,也可以在同一进程。 Consumer端首先创建buffer queue,其中会创建BufferQueueConsumer端和用于producer端的fd。然后consumer端通过IPC将该fd传给producer进程。Producer进程基于它构造BufferQueueProducer。这样buffer queue的两端就构建完成,接下来就可以通过基本接口进行buffer交换了。注意除了直接使用fd建立连接,还可以使用socket name建立连接。这种情况下,首先需要创建唯一的字符串作为id,然后将之作为参数传到makeBufferQueue()中创建BufferQueueConsumer,再将该id作为参数创建BufferQueueProducer,即可建立连接。
1 2 3 4 5 6 | { std::string sockstr = getUniqueId(); auto consumer = yg::BufferQueue::makeBufferQueue(sockstr.c_str()); … auto producer = std::make_shared<yg::BufferQueueProducer>(sockstr.c_str()); } |
Surface模块是Buffer Queue的producer端,是和YunOS GUI Framework上层交互的重要接口,无论开发者使用什么Rendering API,所绘制的东西都是输出到一块“Surface”上。 YunHAL上显示出的一切窗口都是基于Surface, 最终所有可见的Surface都会被图形混合器合成到display上。
当用户使用GPU进行绘制时,首先需要实例化一个Surface对象,这个Surface对象可以作为YunOS Native Surface直接传给eglCreateWindowSurface,eglSwapBuffers会管理该Surface中的Buffers,将其交给OpenGL进行渲染。
当用户使用CPU进行绘制时,同样需要实例化一个Surface对象,可以通过Surface的obtainBuffer接口取得BaseNativeSurfaceBuffer,然后再使用mapBuffer接口取得BaseNativeSurfaceBuffer的虚拟地址,使用CPU将内容绘制到BaseNativeSurfaceBuffer中,最后使用BaseNativeSurface的submitBuffer接口将该Buffer提交到Consumer端。
Surface模块提供了lookup接口供使用者查询Surface的属性,如宽,高,颜色等。同时Surface模块也提供dispose接口设置Surface属性, 如宽,高,Buffer个数等。
Surface是对BaseNativeSurface的进一步封装,实现了BaseNativeSurface所声明的接口如表2.5.1所示。
属性和方法 | 说明 |
---|---|
bool checkInit(); | 检查Surface是否初始化成功 |
void destroyBuffers() override; | 销毁该Surface所含Buffers实现 |
virtual int obtainBuffer(BaseNativeSurfaceBuffer **buffer, int *syncFd) override; | 获取BaseNativeSurfaceBuffer接口实现 |
virtual int submitBuffer(BaseNativeSurfaceBuffer* buffer, int syncFd) override; | 提交BaseNativeSurfaceBuffer接口实现 |
virtual int dropBuffer(BaseNativeSurfaceBuffer* buffer, int syncFd) override; | 放弃BaseNativeSurfaceBuffer接口实现 |
Surface的设计目标就是向Graphic, Media, Camera等clients提供Surface, 示例2.5.1是一个如何获取和使用Surface的简单例子。为了更清晰的展示基本用法,错误处理等非核心代码已略去。
如果要利用Surface来进行on-screen窗口的绘制,可以利用SurfaceController/SubSurfaceController等类来创建Surface。示例代码如下:
1 2 3 4 5 6 7 8 | { namespace yg = yunos::libgui; auto conn = new WaylandConnection(nullptr); auto surfaceController = new yg::SurfaceController(conn, nullptr, WINDOW_WIDTH, WINDOW_HEIGHT); assert (surfaceController->checkInit()); auto producer = std::make_shared<yg::BufferQueueProducer>(surfaceController->getProducerSocketName().c_str()); yg::BaseNativeSurface* surface = std::make_shared<yg::Surface>(producer); } |
首先,创建WaylandConnection对象,它会与compositor建立连接及完成相应的初始化。然后基于它创建SurfaceController,它是on-screen窗口的封装。其中会通过与compositor间的连接创建相应的wayland surface proxy等资源,同时它会返回用于创建Buffer Queue Producer的socket name(id)。因此,之后便可以用它创建相应的Buffer Queue Producer和相应的Surface。结合上面的说明,图2.9中展示了一个比较典型的场景中的主要步骤。
如果需要使用GPU对Surface进行渲染,上面的示例1中拿到的Surface可作为YunOS Native Surface,基于该Surface建立EGL Context后就可以使用OpenGL ES对该Surface进行渲染。示例2.5.2简要说明了如何基于该Surface建立EGL Context, 为了更清晰的展示基本用法,略去了一些非核心代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | { ... EGLDisplay mEGLDisplay; EGLSurface mEGLSurface; mEGLDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); ret = eglInitialize(mEGLDisplay, &major, &minor); ret = eglBindAPI(EGL_OPENGL_ES_API); eglGetConfigs(mEGLDisplay, NULL, 0 , &count); configs = (EGLConfig *)calloc(count, sizeof *configs); ret = eglChooseConfig(mEGLDisplay, config_attribs, configs, count, &n); mEGLContext = eglCreateContext(mEGLDisplay, configs[ 0 ], EGL_NO_CONTEXT, context_attribs); mEGLSurface = eglCreateWindowSurface(mEGLDisplay, mEGLConfig, (EGLNativeWindowType)mSurface, NULL); ret = eglMakeCurrent(mEGLDisplay, mEGLSurface,mEGLSurface, mEGLContext); ... } |
如果需要使用CPU对Surface进行渲染,可以参考示例2.5.3. 先基于示例2.5.1中创建的surface,使用obtainBuffer接口取得yg::BaseNativeSurfaceBuffer类型的Buffer Object, 再使用libgui中的mapBuffer接口取得Buffer的虚拟地址data, 在使用CPU对data所指向的Buffer绘制完成后使用unMap函数将map出来的虚拟地址空间释放掉。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | { namespace yg = yunos::libgui; int fencefd; void *data = nullptr; uint32_t usage = ALLOC_USAGE_PREF(SW_READ_OFTEN) | ALLOC_USAGE_PREF(SW_WRITE_OFTEN); Rect rect = { 0 , 0 , WIDTH, HEIGHT}; yg::BaseNativeSurfaceBuffer *nwb = nullptr; ret = surface->obtainBuffer(&nwb, &fencefd); handle_fence(fencefd); ret = yg::mapBuffer(wnb, usage, rect, &data); CPU Rendering ret = yg::unmapBuffer(wnb); ret = nativeWindow->submitBuffer(nwb, - 1 ); } |
Surface模块还封装了Surface属性的查询和设置接口,示例2.5.4列举了如何通过Surface模块封装的接口设置和查询Surface的宽高, 接口中的参数surface即是示例2.5.1中所创建的surface.
1 2 3 4 5 6 7 | { #define WIDTH 500 #define HEIGHT 500 native_window_set_buffers_dimensions(surface, WIDTH, HEIGHT); int width = native_get_buffers_width(surface) int height = native_get_buffers_width(surface) } |
YunOS实现Yalloc HAL的接口定义和相关的参考实现,芯片厂商可以根据自身Graphic Chips memory user/kernel drivers特点来实现Yalloc HAL。
native_target是Yalloc分配buffer所得到的基本数据结构,定义如表3.1.2所示,Yalloc分配得到的fd信息以及attribute信息分别存储在fds和attributes数据字段中。当需要进行跨进程传递时,我们需要传递的信息包括pounds,pid,fds以及attributes字段。gb_target_t是指向native_target的指针常量。
1 | * typedef const native_target_t* gb_target_t; |
属性 | 说明 |
---|---|
int num | 指定data数组大小 |
Int* data | data数组 |
属性 | 说明 |
---|---|
int pounds | native_target size |
int pid | 所在进程的进程号 |
void* handle | Vendor在hal层实现的buffer指针 |
native_array_t fds | 存储handle fd数组 |
native_array_t attributes | 存储handle属性值数组 |
yalloc_device_t是yalloc分配buffer的接口,接口描述如表3.1.4所示。当前进程需要yalloc分配buffer时,要先通过yalloc_open接口打开yalloc模块,然后就可以调用alloc方法指定buffer的格式,宽高,并得到相应的gb_target_t。若不需要跨进程使用gb_target_t, OpenGL driver可以直接通过gb_target_t获得对应的物理地址,这样就可以进行下一步的渲染工作;若需要跨进程使用gb_target_t, 当目标进程收到跨进程传送过来的gb_target_t时,必须先调用yalloc_device_t的authorizeBuffer方法,这样才能授权gb_target_t给当前进程使用。
除了GPU可以操作yalloc_device_t分配得到的buffer, CPU也可以操作yalloc_device_t分配到的buffer,不过在用之前,需要通过map方法得到gb_target_t所对应buffer的虚拟地址,这样才能读写gb_target_t相对应的buffer, 当CPU读写完毕后,还必须调用unmap方法解除虚拟地址映射。
属性和方法 | 说明 |
---|---|
uint32_t tag | 标识 |
uint32_t version | 版本 |
int (*close)(struct yunhal_device_t* device) | 关闭device方法 |
属性和方法 | 说明 |
---|---|
struct yunhal_device_t common | yunhal device |
int (*authorizeBuffer)(struct yalloc_device_t* device, gb_target_t target); | 授权gb_target_t给当前进程,在gb_target_t跨进程中使用 |
int (*unAuthorizeBuffer)(struct yalloc_device_t* device, gb_target_t target); | 解除授权gb_target_t给当前进程,在gb_target_t跨进程中使用 |
int (*map)(struct yalloc_device_t* device, gb_target_t target, int flags, int l, int t, int w, int h, void** vaddr); | 获取gb_target_t的虚拟地址映射 |
int (*unmap)(struct yalloc_device_t* device, gb_target_t target); | 解除gb_target_t的虚拟地址映射 |
int (*mapAsync)(struct yalloc_device_t* device, gb_target_t target, int flags, int l, int t, int w, int h, void** vaddr, int syncFd); | 获取gb_target_t的虚拟地址映射前,必须等syncFd同步结束 |
int (*unmapAsync)(struct yalloc_device_t* device, gb_target_t target, int* syncFd); | 解除gb_target_t的虚拟地址映射,并得到一个syncFd同步信号 |
int (*alloc)(struct yalloc_device_t* device, int w, int h, int format, int usage, gb_target_t* target, int* stride); | 分配gb_target_t接口 |
int (*free)(struct yalloc_device_t* device, gb_target_t target); | 释放gb_target_t接口 |
void (*dump)(struct yalloc_device_t* device, char *buff, int buff_len); | 获取yallloc一些基本信息 |
fb_device_t是yalloc操作显示设备的接口,接口描述如表3.1.5所示,在YunOS中,图形混合器进程负责显示设备的打开与关闭。当图形混合器进程启动并初始化时,它需要调用framebuffer_open打开fb_device_t显示设备,并获取相应的设备信息为后续操作服务,比如client端创建NativeSurface时,一般需要通过图形混合器查询获得显示设备的宽高等信息,然后才决定创建NativeSurface所需要的宽高。当图形混合器完成一帧的composition操作后,需要把framebuffer送给display显示,这时就会调用fb_device_t的publish方法,告诉display driver下一个vsync到来时,请显示这一块framebuffer。
属性和方法 | 说明 |
---|---|
struct yunhal_device_t common | yunhal device |
const uint32_t flags | 标识符 |
const uint32_t width | 宽 |
const uint32_t height | 高 |
const int stride | 行字节数 |
const int format | 格式 |
const float xdpi | 横向dpi |
const float ydpi | 纵向dpi |
const float fps | 每秒帧数 |
const int minSwapInterval | 最小帧数切换间隔 |
const int maxSwapInterval | 最大帧数切换间隔 |
const int numFramebuffers | 所支持的framebuffer个数 |
int (*setSwapInterval)(struct fb_device_t* dev, int interval); | 设置帧数切换间隔,默认为0 |
int (*publish)(struct fb_device_t* dev, gb_target_t target); | 提交framebuffer到display,当下一个vsync事件到来时,将会显示这个FB |
int (*composeFinished)(struct fb_device_t* dev); | Weston composition结束时调用 |
void (*dump)(struct fb_device_t* dev, char *buff, int buff_len); | 获取framebuffer设备信息 |
因此芯片厂商需要根据自身芯片特点,分别实现yalloc_device_t和fb_device_t里面的一系列函数接口。
YunOS定义了EGL_YUNHAL_DISPLAY 作为YunOS的native display,通过eglGetDisplay可以方便的建立Wayland connection,以便为后续graphics buffer producer/consumer做好准备。
Native Surface
NativeSurface是EGL API与YunOS framework交互最重要的接口,数据结构描述如表3.2.4所示,它主要负责NativeSurfaceBuffer的获取,提交等操作,在GPU进行渲染之前,我们需要通过调用NativeSurface的obtainBuffer获得NativeSurfaceBuffer, 渲染结束后,我们需要通过类似eglSwapBuffers调用NativeSurface的sumbitBuffer, 这样Compositor服务端就能拿到这个buffer并生成eglImage, 待收到系统的vsync事件后,就可以开始compose操作。
属性和方法 | 说明 |
---|---|
Yunos_native_sp base | 智能指针 |
int (*lookup)(const struct NativeSurface* surface, int attrib, int* value); | 查找相应的属性值,包括Surface宽,高,格式等 |
int (*dispose)(struct NativeSurface* surface, int operation, …); | 设置操作类型,包括buffer个数,宽,高,buffer格式等 |
int (*obtainBuffer)(struct NativeSurface* surface, struct NativeSurfaceBuffer** buffer, int* syncFd); | 获取NativeSurfaceBuffer |
int (*submitBuffer)(struct NativeSurface* surface, struct NativeSurfaceBuffer* buffer, int syncFd); | 提交NativeSurfaceBuffer |
int (*dropBuffer)(struct NativeSurface* surface, struct NativeSurfaceBuffer* buffer, int syncFd); | 放弃得到的NativeSurfaceBuffer |
Base Native Surface
BaseNativeSurface是对NativeSurface的c++封装,类描述见表3.2.5所示,它封装了NativeSurface所需功能的接口,并提供给YunOS framework上层API使用,可以基于BaseNativeSurface执行对Surface的操作,譬如获取或提交Buffer,查询或者设置Surface属性等。该类实现了NativeSurface中增减引用计数的方法,并且负责初始化NativeSurface的函数指针以及智能指针,最后提供方法返回NativeSurface对象供EGL Driver使用。
属性和方法 | 说明 |
---|---|
virtual int obtainBuffer(BaseNativeSurfaceBuffer **buffer, int *fenceFd) = 0; | 获取BaseNativeSurfaceBuffer接口声明 |
virtual int submitBuffer(BaseNativeSurfaceBuffer *buffer, int fenceFd) = 0; | 提交BaseNativeSurfaceBuffer接口声明 |
virtual int dropBuffer(BaseNativeSurfaceBuffer *buffer, int fenceFd) = 0; | 放弃BaseNativeSurfaceBuffer接口声明 |
virtual void* get_wl_egl_window(); | 获取NativeSurface接口声明 |
virtual void destroyBuffers(); | 销毁该Surface所含Buffers接口声明 |
Native Surface Buffer
NativeSurfaceBuffer是对gb_target_t的封装,数据结构描述见表3.2.2, 对于EGL Driver来说,当它需要申请graphics buffer进行渲染时,通过NativeSurface拿到的是NativeSurfaceBuffer, 这时需要调用base的incRef来增加引用计数,渲染结束之后,它又把NativeSurfaceBuffer交还给NativeSurface,这时需要调用base的decRef来减少引用计数,通过这种方式来保证NativeSurfaceBuffer实例在不同模块之间的共享以及对象生命周期的管理。
属性和方法 | 说明 |
---|---|
void (*incRef)(struct yunos_native_sp* base); | 增加引用计数声明 |
void (*decRef)(struct yunos_native_sp* base); | 减少引用计数声明 |
属性和方法 | 说明 |
---|---|
Yunos_native_sp base | 智能指针 |
int width | 宽 |
int height | 高 |
int stride | 行字节数 |
int format | 格式 |
int flags | 分配buffer标识符 |
Base Native Surface Buffer
BaseNativeSurfaceBuffer是对NativeSurfaceBuffer的进一步c++封装和实现,类描述见表3.2.3, YunOS framework上层APIs基于BaseNativeSurfaceBuffer执行对Buffer的操作。该类实现了NativeSurfaceBuffer中增减引用计数的方法,并且负责初始化NativeSurfaceBuffer的属性,最后提供方法返回NativeSurfaceBuffer对象供EGL Driver使用。
属性和方法 | 说明 |
---|---|
void (*incRef)(struct yunos_native_sp* base); | 增加引用计数实现 |
void (*decRef)(struct yunos_native_sp* base); | 减少引用计数实现 |
YNativeSurfaceBuffer* getNativeBuffer() const; | 返回NativeSurfaceBuffer |
unsigned int refcount; | 引用计数 |
因此芯片厂商即可以根据YunOS的EGLNativeDisplayType,EGLNativeWindowType来适配自己的EGL drivers,对于OpenGLES driver来说,因为是平台无关的,所以无须特别针对YunOS做相应的修改。
hwf_layer_t在Hardware Flinger中用来表示Client端传递过来的每一个layer的信息,包括graphics buffer对象指针(gb_target_t),合成区域,旋转角度以及混合模式等。这一系列的hwf_layer_t列表将会传递给Hardware Flinger HAL做进一步compose。
hwf_layer_t的详细定义如表3.3.1所示。
属性和方法 | 说明 |
---|---|
int32_t composeMode | 合成模式 |
uint32_t clues | Layer信息标记位 |
uint32_t flags | Layer标识符,包括HWF_LAYER_IGNORED以及HWF_LAYER_CURSOR |
gb_target_t target | Layer graphic buffer指针 |
uint32_t transform | 旋转角度,参考HWF_TRANSFORM_* |
int32_t blendMode | 混合模式,参考HWF_BLENDING_* |
hwf_frect_t srcRect | Buffer source region |
hwf_rect_t destRect | Buffer dest region |
hwf_region_t visibleRegion | Layer的可见区域,可能包括多个rectangle |
int acquireSyncFd | Sync object from submitBuffer |
int releaseSyncFd | Sync object from flip() |
uint8_t globalAlpha | Layer全局透明度 |
hwf_display_t用来表示Hardware Flinger的显示设备信息, 目前支持primary display, external display以及virtual display三种display设备。 hwf_display_t数据结构定义如表3.3.2所示。
属性和方法 | 说明 |
---|---|
int retireSyncFd | Sync object. |
gb_target_t outTarget | Display destination buffer 指针,用来支持virtual display. |
int outTargetAcquireSyncFd | Output buffer sync object. |
uint32_t flags | display标识符,包含HWF_GEOMETRY_UPDATED |
size_t numLayers | layer个数 |
hwf_layer_t hwfLayers[0] | hwf_layer_t列表 |
hwf_device_t是图形混合器操作Hardware Flinger的接口。当图形混合器初始化的时候,它需要获取hwf_device_t的指针,然后就可以进行查询显示设备信息,设置设备状态等操作。当图形混合器收到合成请求时,它他需要通过hwf_device_t调用detect检测当前hwf_layer_t列表中layer的合成模式,并最终调用flip函数,把layer送到显示设备显示,hwf_device_t的函数接口定义如表3.3.3所示。
厂商需要根据自身芯片的Display硬件能力,来实现下面接口。
属性和方法 | 说明 |
---|---|
int (*detect)(struct hwf_device_t device, size_t dispCount, hwf_display_t* displays) |
检测layer的合成模式,包括HWF_FB, HWF_OVERLAY, HWF_FB_TARGET以及HWF_CURSOR_OVERLAY |
int (*flip)(struct hwf_device_t device, size_t dispCount, hwf_display_t* displays) |
发送hwf_layer_t列表给display device。当vsync事件发生时,屏幕上就能显示出当前发送的layer内容 |
int (*setEventState)(struct hwf_device_t* device, int disp, int event, int enabled) | 设置事件的状态,目前主要用来开关vsync |
int (*setDisplayState)(struct hwf_device_t* device, int disp, int state); | 设置display device状态,目前主要用来开关display power |
int (*lookup)(struct hwf_device_t* device, int what, int* value); | 查询显示设备的信息 |
void (*registerCallback)(struct hwf_device_t* device, | |
hwf_callback_t const* callback); | 注册callback函数,包括vsync callback以及hot plug callback |
int (*queryDispConfigs)(struct hwf_device_t* device, int disp,uint32_t* configs, size_t* numConfigs); | 查询display device的配置信息: |
int (*queryDispAttribs)(struct hwf_device_t* device, int disp, uint32_t config, const uint32_t* attributes, int32_t* values); | 查询display device的属性信息,包括宽,高,格式以及DPI等 |
void (*dump)(struct hwf_device_t* device, char *buff, int buff_len); | Dump HW flinger的设备信息 |
Gtest是一套灵活、易用、高效的开源单元测试库。它是目前C/C++项目中应用最广泛的测试框架之一。因此,YunOS的图形系统也采用了gtest作为主要的单元测试手段。
可在http://code.google.com/p/googletest/
下载其源码及库文件。gtest用法和cppunit用法差不多,主要通过宏TEST_F定义测试用例,通过EXPECT_系列和ASSERT_系列宏进行检测。
Yalloc的gtest测试程序位于yalloc项目目录的test目录下,默认在编译image时会参于编译。如果需要单独模块编译。可以在xmake目录下执行:
1 | $ xmake yalloc |
Buffer queue的gtest测试程序位于libgui项目目录的test目录下,默认在编译image时会参于编译。如果需要单独模块编译。可以在xmake目录下执行:
1 | $ xmake surface |
如需执行测试用例,先进入设备shell,然后执行以下命令即可自动化执行所有相关单元测试:
1 | $ yalloc-test |
该测试程序为gtest程序,用于yalloc模块基本测试。
1 | $ libgui-test |
该测试程序为gtest程序,因此所有的gtest参数均适用,以下命令可以获取参数说明:
1 | $ libgui-test –help |
举例来说,如果需要只执行BufferQueueTest中的测试,重复5遍,且测试结果带语法着色,可用以下命令:
1 | $ libgui-test --gtest_color=yes --gtest_filter=BufferQueueTest.* --gtest_repeat= 5 |
Glossary | Description |
---|---|
YunHAL | YunOS Hardware Abstract Layer |
Yalloc | YunOS Graphics Buffer Allocator |
Gb_target_t | type define of graphics buffer target |
fb_device_t | type define of framebuffer device |
YNativeSurface | YunOS Native Surface |
IPC | Inter-Process Communication |
Fd | File descriptor |
Vsync | Display Vertical Synchronize Signal |
https://www.khronos.org/registry/gles/
http://code.google.com/p/googletest/