文档中心 > 芯片厂商开放

文档内容拷贝自:codebase_host/yunhal/opendoc/zh/yunhal_doc/graphics.md
联系人:@陆生华

1. 概述(Introduction)

本文档详细描述了YunOS Graphics Framework的整体软件框架设计, 侧重于YunOS Graphics Hardware Abstract Layer的介绍,并对芯片厂商如何实现YunOS Graphics Hardware Abstract Layer提供了指导。
文中涉及YunOS Graphics Hardware Abstract Layer的部分主要包括下面几块:

  • Graphics buffer对象的设计及管理;
  • surface对象接口的使用
  • EGL/GLES的实现
  • 以及Hardware Flinger框架模型。

2. 架构(Graphics Framework Architecture)

图2.1展示了YunOS Graphics Framework整体软件架构图:
1

图2.1 YunHAL graphics模型

在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中,正是这些模块纵向接力,横向配合,才最终生成完整的显示画面。

2.1 Yalloc(YunOS Graphic Buffer Allocator)

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交互的接口模型图:

2

图2.2 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取消授权。

2.2 EGL & GLES

EGL是由Khronos Group 提供的一组平台无关的API。它的功能:

  • 和本地窗口系统(native windowing system)通讯;
  • 查询可用的配置;
  • 创建OpenGL ES可用的“绘图表面”(drawing surface);
  • 同步不同类别的API之间的渲染,比如在OpenGL ES和OpenVG之间同步,或者在OpenGL和本地窗口的绘图命令之间;
  • 管理“渲染资源”,比如纹理映射(rendering map)。

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的交互窗口。

3

图2.3 EGL接口模型

由于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进行一系列的初始化和渲染操作。

2.3 Hardware Flinger

Hardware Flinger模块负责图形合成工作,具体的实现可以是overlay或2D硬件合成模块。跟GPU合成相比,Hardware Flinger合成降低了对内存的读写操作和GPU负载, 优化了系统的性能和功耗。图2.4展示了Hardware Flinger的接口模型。

4

图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的方式来达到合成的目的。

2.4 Generic Buffer Management

为了更好的关系YunOS系统graphics memory, 我们在libgui中实现了一个通用的buffer queue,使不同场合下的graphic buffer管理统一化。基于该buffer queue实现窗口管理进程与渲染进程的分离。Surface与buffer queue分离,buffer queue可以独立工作,外部使用更加灵活。

2.4.1 Buffer Queue介绍

图2.5为buffer queue的总体结构示意图。

5

图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.4.2 基于通用buffer queue的应用端绘制

这里分两种情况,一种是窗口管理与渲染处于不同进程。图2.6中只列出与buffer管理相关的步骤。首先App端向Weston端申请创建surface,然后图形混合器返回用于建立buffer queue的socket name作为id,接着App端将该id传给渲染进程,最后渲染进程用该id与图形混合器建立连接。之后渲染进程可以直接向图形混合器发起buffer的交换申请;第二种情况下,窗口管理与渲染处于同一进程,则不需要第3步的fd传输过程。其余步骤类似。总之,这一部分主要是为了窗口控制与渲染的进程分离,从而为基于通用buffer queue的统一buffer管理架构打下基础。注意这里只是模型的抽象示意图,具体使用时图中的渲染进程可以是media server等。

6

图2.6 App-Compositor间buffer queue初始化和buffer交换模型

2.4.3 线程模型

Buffer queue实现采用了多线程设计。与buffer queue相关的基本线程模型如图2.7所示。其中producer端与consumer端除了主线程外,会有专门的dispatcher线程来处理wayland events。这样,主线程中不需要关心Wayland的通信,可以专注于业务逻辑。

7

图2.7 Buffer queue相关基本线程模型

另外,为了简化模型及将来更好的扩展性,buffer的传输使用了专用的Wayland协议。其中主要包括了buffer handle及buffer属性的传输和通知。未来可能会根据需要不断扩充。

2.4.4 使用用法

通用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;
}
示例2.1

如前面所述,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());
}
示例2.2

2.5 Surface

Surface模块是Buffer Queue的producer端,是和YunOS GUI Framework上层交互的重要接口,无论开发者使用什么Rendering API,所绘制的东西都是输出到一块“Surface”上。 YunHAL上显示出的一切窗口都是基于Surface, 最终所有可见的Surface都会被图形混合器合成到display上。

8

图2.8 YunHAL Surface模型

2.5.1 Surface对象介绍

当用户使用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接口实现
表2.5.1 Surface接口描述

2.5.2 使用场景

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);
}
示例2.3

首先,创建WaylandConnection对象,它会与compositor建立连接及完成相应的初始化。然后基于它创建SurfaceController,它是on-screen窗口的封装。其中会通过与compositor间的连接创建相应的wayland surface proxy等资源,同时它会返回用于创建Buffer Queue Producer的socket name(id)。因此,之后便可以用它创建相应的Buffer Queue Producer和相应的Surface。结合上面的说明,图2.9中展示了一个比较典型的场景中的主要步骤。

9

图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);
    ...
}
示例2.4

如果需要使用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);
}
示例2.5

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)
}
示例2.6

3. 实现(Implement Graphics HAL)

3.1 如何实现Yalloc HAL

YunOS实现Yalloc HAL的接口定义和相关的参考实现,芯片厂商可以根据自身Graphic Chips memory user/kernel drivers特点来实现Yalloc HAL。

3.1.1 核心数据结构和接口

  • native_target

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数组
表3.1.1 native\_array\_t数据结构
属性 说明
int pounds native_target size
int pid 所在进程的进程号
void* handle Vendor在hal层实现的buffer指针
native_array_t fds 存储handle fd数组
native_array_t attributes 存储handle属性值数组
表3.1.2 native_target数据结构
  • yalloc_device_t

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方法
表3.1.3 yunhal\_device\_t数据结构
属性和方法 说明
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一些基本信息
表3.1.4 yalloc_device_t数据结构
  • fb_device_t

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设备信息
表3.1.5 fb_device_t数据结构

因此芯片厂商需要根据自身芯片特点,分别实现yalloc_device_t和fb_device_t里面的一系列函数接口。

3.2 如何实现EGL HAL

3.2.1 核心数据结构和接口

  • EGLNativeDisplayType

YunOS定义了EGL_YUNHAL_DISPLAY 作为YunOS的native display,通过eglGetDisplay可以方便的建立Wayland connection,以便为后续graphics buffer producer/consumer做好准备。

  • EGLNativeWindowType

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
表3.2.4 NativeSurface数据结构

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接口声明
表3.2.5 NativeSurface方法接口

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); 减少引用计数声明
表3.2.1 yunos_native_sp数据结构
属性和方法 说明
Yunos_native_sp base 智能指针
int width
int height
int stride 行字节数
int format 格式
int flags 分配buffer标识符
表3.2.2 NativeSurfaceBuffer数据结构

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; 引用计数
表3.2.3 BaseNativeSurfaceBuffer数据结构

因此芯片厂商即可以根据YunOS的EGLNativeDisplayType,EGLNativeWindowType来适配自己的EGL drivers,对于OpenGLES driver来说,因为是平台无关的,所以无须特别针对YunOS做相应的修改。

3.3 如何实现Hardware Flinger

3.3.1 核心数据结构和接口

  • hwf_layer_t

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全局透明度
表3.3.1 hwf_layer_t数据结构
  • hwf_display_t

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列表
表3.3.2 hwf_display_t数据结构
  • hwf_device_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的设备信息
表3.3.3 hwf_device_t数据结构

4. 测试(Vendor Test Suite)

4.1 Gtest 测试框架介绍

Gtest是一套灵活、易用、高效的开源单元测试库。它是目前C/C++项目中应用最广泛的测试框架之一。因此,YunOS的图形系统也采用了gtest作为主要的单元测试手段。
可在http://code.google.com/p/googletest/
下载其源码及库文件。gtest用法和cppunit用法差不多,主要通过宏TEST_F定义测试用例,通过EXPECT_系列和ASSERT_系列宏进行检测。

4.2 Build Test Suite

Yalloc的gtest测试程序位于yalloc项目目录的test目录下,默认在编译image时会参于编译。如果需要单独模块编译。可以在xmake目录下执行:

1
$ xmake yalloc

Buffer queue的gtest测试程序位于libgui项目目录的test目录下,默认在编译image时会参于编译。如果需要单独模块编译。可以在xmake目录下执行:

1
$ xmake surface

4.3 Run Test Suite

如需执行测试用例,先进入设备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

5. 附录(Appendix)

5.1 类抽象

5.1.1 YNativeSurface与YNativeSurfaceBuffer

20

图5.1 YNativeSurface和YNativeSurfaceBuffer

5.1.2 DisplayWindow

21

图5.2 DisplayWindow

5.2 词汇表(Glossary)

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

5.3 参考网站(References)

https://www.khronos.org/egl/

https://www.khronos.org/registry/gles/

http://code.google.com/p/googletest/

FAQ

关于此文档暂时还没有FAQ
返回
顶部