嵌入式 DirectFB

转自:原文地址

DirectFB常用接口

1. 概述

   ================   
   本文主要研究DirectFB及其向上向下的接口,顺带讲一下QT搭配DirectFB使用的一些笔记。
   其他,如DirectFB的内部结构、Qt的内部结构、及其他GUI系统如android不在本文讨论范围内。
   本文会设计一些DirectFB的2D加速函数介绍,3D加速不再本文讨论范围内。

   ================   

   桌面GUI系统涉及很多图像运算,比如画点、画线、填充、透明度处理、平滑度处理、层的叠加、字体处理、贴图等等。如果这些运算都由CPU来处理,那这将是对CPU的一个很大的负载。在QT的文档中曾说到,假如一个图形运算让加速设备来做需要1到2个CLK,同样的图形运算让CPU来算则至少需要20个CLK,而且,对于嵌入式SoC来说,CPU的频率和SDRAM/DRAM的带宽始终是图形运算的瓶颈。所以这些芯片的图形加速能力有无、高低,最终决定其目标GUI的质量。

   近两年嵌入式设备主芯片越来越多的嵌入了2D/3D加速功能,由此软件可以搭建越来越复杂、炫酷、先进的桌面系统,如Qt/Android等。这些加速硬件,通常也因其是否拥有私有内存、是否支持3D、加速接口是否可编程等特性而分为三六九等。硬件的加速功能如果要在目标GUI系统中跑起来,还得需要一些软件中间层为不同的硬件加速提供同一的接口,并且为上层GUI提供服务。DirectFB就是这样一个软件中间层,它主要为上层提供2D加速服务。OpenGL/ES 主要为上层提供3D加速服务。

   DirectFB是一个专门针对Linux图形库设计的图形加速中间层。它的上层直接面向图形库比如QT,提供如显示、画图、输入设备控制等服务,他的下层直接面向GFX加速硬件,要求硬件驱动实现全部或部分预定的画图函数。


嵌入式 DirectFB_第1张图片


2. DirectFB给上层图形库提供的接口调用


   2.1 主要接口


    DirectFB由超级接口IDirectFB为上层提供接口调用,其他所有接口都由此接口生成,这些接口内容包括:

    =========================
    . IDirectFBScreen
    . IDirectbFBDisplayLayer
    . IDirectFBSurface

    =========================

    显示屏幕、显示层、显示表面,三者之间的关系可这样表述:

    显示屏幕<-Output(connector)<-IDirectFBScreen<-(mixer)-DisplayLayers <-DisplayLayer's IDirectFBSurface<-gfx Accelarator <--显卡/或CPU运算画图

    首先,用户在显示屏幕上看到一个Screen的输出,这个Screen由多个画面组成(比如同时包含:背景图片,前景视频/字幕/图片)。组成Screen的每个画面,都由一个单独的DisplayLayer来处理。多个DisplayLayers由Mixer来决定叠加的顺序/位置/以及透明度等。单独一个显示层DisplayLayer,可自由控制输出大小(放大/缩小)、背景颜色、翻转等。DisplayLayer所显示的数据(比如解析JPEG产生的RGB数据),则需要在DisplayLayer上生成一个Surface来显示,Surface控制实际的贴图操作,比如:画点/线/框/填充/变换位数、色深、分辨率,等等, Surface可直接和硬件加速打交道(gfxcard)。

    以上描述的DisplayLayers和Surface都是Screen的后端,Screen的前端(就是输出部分),还有Encoder,Output等概念。Encoder着重描述输出信号的编码参数,Output着重描述输出接口. 它们有以下重要成员:
 
     Standards  :配置输出制式(标清/高清/PAL/NTSC/>>扩展<<)
     Signals    :配置输出信号(VGA/YC/CVBS/RGB/YCBCR/HDMI/656/>>扩展<<)
     Connectors :配置输出接口(VGA/SCART/YC/CVBS/SCART2/COMPONENT/HDMI/>>扩展<<)
     Resolution :配置输出分辨率(directfb.h中所列举的常见分辨率/>>扩展<<)
    从Screen到Encoder,再从Encoder到Output的connector,最后到显示屏幕,DirectFB的底层实现者需要做的就是根据目标硬件的特性,实现这条路线中各条通路,DirectFB的使用者需要做的就是通过Screen的成员函数配置、使用这些通路。

    以上表述中,关于每个节点功能的详细表述可能有误,但其所处的位置和大概的介绍应该是对的。笔者曾参与SigmaDesigns系列解码芯片和PNX8735系列的研发工作,发现DFB的这种结构设计非常接近于这类芯片已有的硬件设计。    
    这部分代码主要分布在子目录“src/display”下。


    =========================
    IDirectFBImageProvider
    IDirectFBVideoProvider
    IDirectFBFont

    =========================

    参见源代码:idirectfbimageprovider_dfiff.c / idirectfbimageprovider_gif.c / idirectfbimageprovider_jpeg.c / idirectfbimageprovider_png.c 

    DFB对dfiff/gif/jpeg(libjpeg.a)/png(libpng.a)第三方软件解码库做了简单封装,以在DFB的框架内在恰当的时候去解码或贴图。
    在子目录“interfaces/IDirectFBImageProvider”下有具体图片格式的实现,具体是否生效取决于configure参数。

    举例JPEG通过imageprovider解码和贴图的过程,具体参见以下代码:
  • tests/dfbtest_scale.c
  • lib/direct/interface.c
  • lib/direct/interface_implementation.h
  • interfaces/IDirectFBImageProvider/idirectfbimageprovider_jpeg.c

    1. idirectfbimageprovider_jpeg.c会把自己的'funcs'注册到interface链表中,注意,Register的调用是通过GNU的__attribute__((constructor))实现的,即进入main函数之前已经注册,没有显式调用注册函数。

    2. 测试代码通过CreateImageProvider调用创建provider,此函数会定位到interface.c::DirectGetInterface()中。DirectGetInterface函数遍历interface链表,首先匹配provider类型,再通过具体implementation的Probe函数回调挨个匹配,Probe函数有的是通过文件扩展名匹配,有的需要读取文件内容进行匹配(这里JPEG是通过文件扩展名匹配的)。与某种类型的provider完全匹配后,返回provider,调用其Construct函数. test程序再调用GetSurfaceDescription获取图片的参数,再用此参数通过函数CreateSurface创建目标显示表面,再通过provider的RenderTo函数往目标Surface贴图。
    视频采集与显示IDirectFBVideoProvider, VideoProvider分两种:
  • idirectfbvideoprovider_v4l.c: 直接从v4l设备读取yuv数据,软件贴图显示视频。
  • idirectfbvideoprovider_gif.c: 封装gif第三方库,软件显示gif动画。
    字体点阵的解码贴图IDirectFBFont,FontProvider分两种:
  • idirectfbfont_ft2.c: 软件封装freetype2库,软件解析贴图字体矩阵。
  • idirectfbfont_dgiff.c: 封装dgiff.c库.

    每种类型的provider,其贴图/播放控制函数都位于其结构定义IDirectFBXXXProvider(其中XXX=Image/Video),或者IDirectFBFont中。


    =========================
    IDirectFBEventBuffer
    IDirectFBDataBuffer
    IDirectFBInputDevice

    =========================

    IDirectFBEventBuffer: 事件同步机制,一端接入产生事件的设备,使用PostEvent之类的接口递交事件,另一端使用WaitForEvent之类的接口等待事件,使用GetEvent之类的接口获取事件。
    常用与输入设备

    IDirectFBDataBuffer: 数据DataBuffer缓冲,提供同步等待功能,并提供CreateXXXProvider(XXX=Image/Video)功能,在原代码中被广泛使用.

    IDirectFBInputDevice: 输入设备接口
    

   2.2. QT与DirectFB之间的关系

       QT从4.3版本开始尝试加入DirectFB,直到4.6版本以后才稳定下来。DirectFB至今也到了1.5.0版本(201106),给图形库提供的硬件加速接口也在不断的扩充。
       需要说明,DirectFB并不包含QT的所有画图函数的实现,这一点在QT的文档中有表述。
       QT中调用的DirectFB代码,主要分布在子目录"src/plugins/gfxdrivers/directfb/"下,上面提到的几乎所有接口都有涉及。

    笔者移植QT+DirectFB过程中,使用了QT的Configure选项:
        ./configure -embedded mips -qt-gfx-directfb -little-endian -prefix /tftpboot/install -prefix-install -v
    运行时敲入命令:
        ./browser -qws -display directfb
        

3. DirectFB给底层硬件提供的接口。

   3.1 输入设备接口: InputDrivers  

   输入设备的管理位于核心代码src/core/input.c,输入设备的接口定义位于src/core/input_driver.h。 DFB对输入设备的管理、使用,只是简单的对输入设备的设备文件进行封装,并没有真正的去驱动某种输入设备。
   要添加一个输入设备,首先要在DFBROOT目录下的子目录"inputdrivers/"下添加一个源文件,或者是在"inputdrivers/"目录下修改一个已有的源文件。 "inputdrivers"的名称来自于“src/core/input.c”头上的DEFINE_MODULE_DIRECTORY定义,所以不同DFB版本对应的目录名称可能不同。 其次,要在你的源文件中引用头文件'core/input_driver.h'并实现InputDriverFuncs所定义的函数:
  • driver_get_info(): 用于获取driver的vendor/name/version等信息。
  • driver_get_available(): 用于判断设备是否可用,返回值表示当前driver可驱动的设备数量。
  • driver_open_device(): 打开输入设备文件,并开启线程监听设备输入。
  • driver_get_keymap_entry(): 键值修复/更改/映射函数,好像只有keyboard才用,用于处理组合键/大小写/小键盘等键值。
  • driver_close_device: 关闭监听线程,关闭设备文件

    这些函数的具体调用路径如下:
  • 涉及源文件: src/core/input.c,  src/core/input_driver.h, src/core/core.c,例子inputdrivers/keyboard/keyboard.c
  • Core初始化->InputCore初始化->input.c:init_devices():
  • 遍历inputdrivers下所有输入设备驱动: 调用driver_get_info()获取驱动信息,调用driver_get_available()判断当前设备是否可用,调用driver_open_device()打开并监听设备
  • Core关闭/Suspend: 调用driver_close_device()关闭所有已打开设备,然后清空事件缓冲。
  • Core Resume: 再次调用driver_open_device()打开Suspend之前处于开启状态的设备。Suspend/Resume由IDirectFB.Suspend/Resume接入,由用户控制可用于休眠操作。
  • driver_open_device()中会打开一个线程监听设备输入,输入键值通过dfb_input_dispatch()分发,调用堆栈包括driver_get_keymap_entry()用于重新映射键值。键值最终通过fusion_reactor_dispatch()向上层分发事件。

    如果你是新添加的一个.c文件,可能你还需要修改configure文件和Makefile.XXX文件。


    3.2 硬件加速gfxcard.
    
    * 涉及源代码src/core/graphics_driver.h, src/core/gfxcard.h, src/core/gfxcard.c, 举例:gfxdrivers/pxa3xx/

    这一章很复杂,笔者也只能泛泛而谈。
    个人认为,DirectFB最重要的功能就是能够统一不同的硬件显示加速设备的各种加速功能为上层图形库所用。
    
    gfx加速在DFB中分两层概念:
  • graphics driver : 需要实现gfxcard.h::GraphicsDriverFuncs所定义的所有函数。
  • graphics device : 按需实现gfxcard.h::GraphicsDeviceFuncs所定义的函数。

    graphics_driver是core的一个模块,它的函数调用堆栈跟InputDriver很像,依次为:
    GraphicsDriverFuncs的调用堆栈:
    src/core/gfxcard.c: dfb_graphics_core_initialize()->
 

    dfb_gfxcard_find_driver()   
     {Driver.Probe()&Driver.GetDriverInfo()}->
    Driver.InitDevice() -> 
    Device.EngineReset()   

    真正实现加速功能的,是graphics device的函数调用。先看看DirectFB是如何把用户的画图动作,转化为gfxcard的硬件实现的。
    前面讲过Surface控制实际的贴图操作,所以下面的调用堆栈跟踪都从IDirectFBSurface开始。
    上层用户可通过IDirectFBSurface.GetAccelerationMask()的返回值判断所需画图动作是否可以加速,具体调用堆栈如下:

    src/display/idirectfbsurface.c:IDirectFBSurface_Construct() -> IDirectFBSurface_GetAccelerationMask() -> src/core/gfxcard.c:dfb_gfxcard_state_check()
    -> gfxdrivers/pxa3xx/pxa3xx.c & gfxdrivers/pxa3xx/pxa3xx_blt.c: pxa3xxCheckState()

    即: Device的CheckState()函数,是用于判断所需加速功能是否可用的。

    一次具体的画图操作(比如:填充矩形),调用堆栈如下:
    src/display/idirectfbsurface.c:IDirectFBSurface_FillRectangle()    -> src/core/gfxcard.c: dfb_gfxcard_fillrectangles()

          dfb_gfxcard_state_check( state, DFXL_FILLRECTANGLE )      ->          gfxdrivers/pxa3xx/pxa3xx_blt.c: pxa3xxCheckState()
          dfb_gfxcard_state_acquire( state, DFXL_FILLRECTANGLE )    ->        gfxdrivers/pxa3xx/pxa3xx_blt.c: pxa3xxSetState()
      card->funcs.FillRectangle( driver_data, device_data, &rect )     ->         gfxdrivers/pxa3xx/pxa3xx_blt.c: pxa3xxFillRectangle()

    即: Surface在调用每一个加速画图函数之前,都会先调用Device.CheckState()判断所需函数是否可达,再调用Device.SetState()告知具体device即将使用此类加速函数(pxaxxx是在SetState()中确定接下来该使用哪个加速函数来实现当前画图功能),然后调用Device的具体加速函数画图。

    了解了画图加速函数的调用堆栈,再来看看gfxcard支持哪些加速函数,(参考源代码src/core/gfxcard.h):

        FillRectangle     :        填充矩形
        DrawRectangle     :        画矩形
        DrawLine          :        画线
        FillTriangle      :        填充三角形
        Blit              :        位块传送(内存拷贝/移动?)
        Blit2             :        位块传送2(支持源坐标)
        StretchBlit       :        位块传送并拉伸
        TextureTriangles  :        ?

     可以看出,列举的函数都是2D加速函数。


4. DirectFB移植事项
   
   DirectFB是一个软件中间层,它为底层的硬件驱动和上层的GUI提供了统一的接口调用,但它不做任何实际画图操作。要移植DirectFB到目标嵌入式硬件上,为了最大限度利用硬件的加速功能,移植的过程中要考虑以下问题:
  • 首先要实现DirectFB的显示路径。以Screen为切入点,根据目标硬件的特性,向前实现各种Encoder->Output的显示通路,向后实现Screen的各种显示Layer->Surface。
  • 如果目标硬件支持JPEG/PNG/GIF的硬件解码,那么你可能需要实现对应的ImageProvider以代替软件解码。
  • 如果目标硬件支持多种Video格式的硬件解码,那么你可能需要实现对应的VideoProvider以实现硬件解码播放的功能。
  • 如果目标设备具有自己的输入小键盘(GPIO扩展、或者其他特殊输入设备),那么你可能需要实现自己的InputDriver。
  • 如果目标硬件具有GFX加速引擎,那么你可能需要实现对应的gfxdriver以及实现gfxdevice中的各种画图函数。
   DirectFB移植的过程中,因尽量保证idirectb(各种接口结构、函数)不能变,可适当扩展其宏定义。

你可能感兴趣的:(QT,DirectFB)