zoukankan      html  css  js  c++  java
  • Android服务端本地窗口FramebufferNativeWindow

    Android窗口系统

    我们知道Android系统采用OpenGL来绘制3D图形,OpenGL ES提供了本地窗口(NativeWindow)的概念,无论是在Android平台中还是其他平台中,只要实现OpenGL ES中的本地窗口定义的接口,就可以利用OpenGL ES来绘制图形。由于Android系统所有服务都建立在C/S模式下,因此Android系统在实现OpenGL ES的本地窗口时仍然包括两种本地窗口,服务进程端的本地窗口定义为FramebufferNativeWindow,该本地窗口直接由 SurfaceFlinger管理。在应用程序进程端定义的本地创建为SurfaceTextureClient。在Android系统中,它们之间为一 对多的关系,如下图所示:


    每个应用程序App可以有多个窗口,即多个Surface,每个Surface所需的图形缓冲区由SurfaceFlinger进程中的 BufferQueue对象负责管理,图形缓冲区个数最多可以有32个,每个图形缓冲区用GraphicBuffer来定义,应用程序在图形绘制前,请求 SurfaceFlinger进程中的BufferQueue对象在内存中分配一块图形缓冲区,应用程序完成图形绘制后,由SurfaceFlinger 将多个应用程序需要显示的Surface进行图形混合,混合后的图形窗口使用FramebufferNativeWindow来描述,同时将混合后的图形 数据拷贝到Framebuffer的后台缓冲区中,等待渲染到显示屏上。以下就是Android的窗口系统设计模型:


    FramebufferNativeWindow本地窗口所需的图形缓冲区直接从Framebuffer中分配,而Surface本地窗口所需的图 形缓冲区则是从内存中分配,无论是从Framebuffer中分配还是从内存中分配,图形缓冲区的分配工作都是由Gralloc硬件抽象层完成,在Android图形显示之硬件抽象层Gralloc中详细分析了Gralloc模块,而Android图形缓冲区分配过程源码分析则分析了图形缓冲区的分配过程。SurfaceFlinger收集所有应用程序的显示需求,然后对应用程序所需显示的图形做图像混合操作,然后输出到自己的FramebufferNativeWindow本地窗口上。为了使用OpenGL ES绘制图形窗口,必须实现OpenGL ES定义的本地窗口协议NativeWindow。

    1. EGLSurface eglCreateWindowSurface(EGLDisplay dpy, EGLConfig config,  
    2.                                     NativeWindowType window,  
    3.                                     const EGLint *attrib_list)  

    函数eglCreateWindowSurface是OpenGL ES提供用于创建窗口的函数接口,参数window的类型为NativeWindowType,定义如下:

    frameworks ativeopenglincludeEGLeglplatform.h

    1. typedef EGLNativeWindowType  NativeWindowType;  
    2.   
    3. #if defined(_WIN32) || defined(__VC32__) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__) /* Win32 and WinCE */  
    4. typedef HWND    EGLNativeWindowType;  
    5. #elif defined(__WINSCW__) || defined(__SYMBIAN32__)  /* Symbian */  
    6. typedef void *EGLNativeWindowType;  
    7. #elif defined(__ANDROID__) || defined(ANDROID)  
    8. typedef struct ANativeWindow*           EGLNativeWindowType;  
    9. #elif defined(__unix__)  
    10. typedef Window   EGLNativeWindowType;  
    11. #else  
    12. #error "Platform not recognized"  
    13. #endif  

    NativeWindowType 定义为EGLNativeWindowType类型,而该类型在不同的平台中有不同的定义,这是因为OpenGL ES是一个跨平台的图形绘制库,对于Android系统来说,其定义为ANativeWindow指针类型,而ANativeWindow的定义如下:

    1. struct ANativeWindow  
    2. {  
    3. #ifdef __cplusplus  
    4.     ANativeWindow(): flags(0), minSwapInterval(0), maxSwapInterval(0), xdpi(0), ydpi(0)  
    5.     {  
    6.         common.magic = ANDROID_NATIVE_WINDOW_MAGIC;  
    7.         common.version = sizeof(ANativeWindow);  
    8.         memset(common.reserved, 0, sizeof(common.reserved));  
    9.     }  
    10.     void incStrong(const void* id) const {  
    11.         common.incRef(const_cast<android_native_base_t*>(&common));  
    12.     }  
    13.     void decStrong(const void* id) const {  
    14.         common.decRef(const_cast<android_native_base_t*>(&common));  
    15.     }  
    16. #endif  
    17.   
    18.     struct android_native_base_t common;  
    19.     const uint32_t flags;//用于描述该Surface的一些属性  
    20.     const int   minSwapInterval;//最小交换间隔时间  
    21.     const int   maxSwapInterval;//最大交换间隔时间  
    22.     const float xdpi;//水平方向的密度  
    23.     const float ydpi;//垂直方向的密度  
    24.     intptr_t    oem[4];//为OEM预留  
    25.     //设置交换间隔时间  
    26.     int     (*setSwapInterval)(struct ANativeWindow* window,int interval);  
    27.     //申请一个图形缓冲区buffer  
    28.     int     (*dequeueBuffer)(struct ANativeWindow* window,struct ANativeWindowBuffer** buffer);  
    29.     //锁定图形缓冲区  
    30.     int     (*lockBuffer)(struct ANativeWindow* window,struct ANativeWindowBuffer* buffer);  
    31.     //buffer渲染完成后,它调用这个接口来unlock和post buffer  
    32.     int     (*queueBuffer)(struct ANativeWindow* window,struct ANativeWindowBuffer* buffer);  
    33.     //向本地窗口查询相关信息  
    34.     int     (*query)(const struct ANativeWindow* window,int what, int* value);  
    35.     //执行本地窗口支持的各种操作  
    36.     int     (*perform)(struct ANativeWindow* window,int operation, ... );  
    37.     //取消一个已经dequeued的buffer  
    38.     int     (*cancelBuffer)(struct ANativeWindow* window,struct ANativeWindowBuffer* buffer);  
    39.     //预留  
    40.     void* reserved_proc[2];  
    41. }android_native_window_t;  

    当使用C++编译器是,为ANativeWindow定义了相应的构造函数,在OpenGL ES下的Android本地窗口系统的类关系图如下:


    从上图可以看出,Surface和FramebufferNativeWindow都继承于ANativeWindow,因此也就继承了 OpenGL ES下的本地窗口定义的相关协议:ANativeWindow中定义的相关接口。下面分别对这两种类型的本地窗口进行深入分析。

    FramebufferNativeWindow

    前面已经介绍了FramebufferNativeWindow是SurfaceFlinger服务进程维护的本地窗口,用于描述经过图形混合 后的,即将渲染显示的图形窗口。FramebufferNativeWindow不仅实现了从ANativeWindow中继承下来的接口,自己还定义了 一些额外属性:
    1. class FramebufferNativeWindow   
    2.     : public ANativeObjectBase<  
    3.         ANativeWindow,   
    4.         FramebufferNativeWindow,   
    5.         LightRefBase<FramebufferNativeWindow> >  
    6. {  
    7.     framebuffer_device_t* fbDev; //描述Framebuffer设备  
    8.     alloc_device_t* grDev; //描述gpu设备  
    9.     sp<NativeBuffer> buffers[NUM_FRAME_BUFFERS];//定义2个图形缓冲区  
    10.     sp<NativeBuffer> front; //前台图形缓冲区,即正在渲染的图形缓冲区  
    11.     mutable Mutex mutex;   
    12.     Condition mCondition;  
    13.     int32_t mNumBuffers; //图形缓冲区个数  
    14.     int32_t mNumFreeBuffers; //可以使用的图形缓冲区个数  
    15.     int32_t mBufferHead; //  
    16.     int32_t mCurrentBufferIndex;//当前图形缓冲区的索引  
    17.     bool mUpdateOnDemand;  
    18. };  

    接下来看看FramebufferNativeWindow对象的构造过程:
    1. FramebufferNativeWindow::FramebufferNativeWindow() : BASE(), fbDev(0), grDev(0), mUpdateOnDemand(false)  
    2. {  
    3.     hw_module_t const* module;  
    4.     //加载gralloc模块  
    5.     if (hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module) == 0) {  
    6.         int stride;  
    7.         int err;  
    8.         int i;  
    9.         //打开fb设备  
    10.         err = framebuffer_open(module, &fbDev);  
    11.         ALOGE_IF(err, "couldn't open framebuffer HAL (%s)", strerror(-err));  
    12.         //打开gpu设备  
    13.         err = gralloc_open(module, &grDev);  
    14.         ALOGE_IF(err, "couldn't open gralloc HAL (%s)", strerror(-err));  
    15.         //设备打开失败,返回  
    16.         if (!fbDev || !grDev)  
    17.             return;  
    18.         mUpdateOnDemand = (fbDev->setUpdateRect != 0);  
    19.         // 初始化变量值  
    20.         mNumBuffers = NUM_FRAME_BUFFERS;//2  
    21.         mNumFreeBuffers = NUM_FRAME_BUFFERS;//2  
    22.         mBufferHead = mNumBuffers-1;//1  
    23. #ifdef FRAMEBUFFER_FORCE_FORMAT  
    24.         *((uint32_t *)&fbDev->format) = FRAMEBUFFER_FORCE_FORMAT;  
    25. #endif  
    26.         //创建2个NativeBuffer  
    27.         for (i = 0; i < mNumBuffers; i++)  
    28.         {  
    29.             buffers[i] = new NativeBuffer(fbDev->width, fbDev->height, fbDev->format, GRALLOC_USAGE_HW_FB);  
    30.         }  
    31.         //为NativeBuffer分配缓冲区  
    32.         for (i = 0; i < mNumBuffers; i++)  
    33.         {  
    34.                 err = grDev->alloc(grDev,fbDev->width, fbDev->height, fbDev->format,GRALLOC_USAGE_HW_FB, &buffers[i]->handle, &buffers[i]->stride);  
    35.                 ALOGE_IF(err, "fb buffer %d allocation failed w=%d, h=%d, err=%s",i, fbDev->width, fbDev->height, strerror(-err));  
    36.                 if (err)  
    37.                 {  
    38.                         mNumBuffers = i;  
    39.                         mNumFreeBuffers = i;  
    40.                         mBufferHead = mNumBuffers-1;  
    41.                         break;  
    42.                 }  
    43.         }  
    44.         //使用Framebuffer的设备描述符来初始化本地窗口ANativeWindow的相关属性  
    45.         const_cast<uint32_t&>(ANativeWindow::flags) = fbDev->flags;   
    46.         const_cast<float&>(ANativeWindow::xdpi) = fbDev->xdpi;  
    47.         const_cast<float&>(ANativeWindow::ydpi) = fbDev->ydpi;  
    48.         const_cast<int&>(ANativeWindow::minSwapInterval) = fbDev->minSwapInterval;  
    49.         const_cast<int&>(ANativeWindow::maxSwapInterval) = fbDev->maxSwapInterval;  
    50.     } else {  
    51.         ALOGE("Couldn't get gralloc module");  
    52.     }  
    53.     //为本地窗口ANativeWindow设置回调接口函数  
    54.     ANativeWindow::setSwapInterval = setSwapInterval;  
    55.     ANativeWindow::dequeueBuffer = dequeueBuffer;  
    56.     ANativeWindow::lockBuffer = lockBuffer;  
    57.     ANativeWindow::queueBuffer = queueBuffer;  
    58.     ANativeWindow::query = query;  
    59.     ANativeWindow::perform = perform;  
    60. }  

    函数首先加载Gralloc模块,关于硬件抽象层模块的加载过程,在Android硬件抽象Hardware库加载过程源码分析已 经有详细的介绍了。当成功加载Gralloc模块后,依次打开Gralloc模块中定义的Framebuffer设备及gpu设备,我们知道 Gralloc模块中定义的Framebuffer设备用于将已经准备好了的图形缓冲区渲染到帧缓冲区中,而定义的gpu设备用于分配一块图形缓冲区,并 且将这块图形缓冲区映射到应用程序的地址空间。关于Framebuffer设备和gpu设备的打开过程请参阅Android图形显示之硬件抽象层Gralloc。 打开fb和gpu设备后,将这两种设备描述符分别保存到FramebufferNativeWindow的成员变量fbDev和grDev中。接着创建了 两个NativeBuffer对象,并从Framebuffer帧缓冲区中分配了两块图形缓冲区。Android系统为定义的两种本地窗口分别定义了相应 的图形缓冲区buffer的描述符,对于FramebufferNativeWindow本地窗口来说,为其定义的图形缓冲区描述符为 NativeBuffer,而对于应用程序端的本地窗口Surface,为其定义的图形缓冲区描述符为GraphicBuffer,它们之间的类关系图如 下:


    从上面的类继承图中可以看出,无论是NativeBuffer还是GraphicBuffer,它们都继承于 ANativeWindowBuffer,ANativeWindowBuffer用于描述一块图形缓冲区buffer的属性信息,比如图形的宽,高,图 像格式及该buffer的句柄等等信息。

    1. typedef struct ANativeWindowBuffer  
    2. {  
    3. //针对C++编译器定义构造函数  
    4. #ifdef __cplusplus  
    5.     ANativeWindowBuffer() {  
    6.         common.magic = ANDROID_NATIVE_BUFFER_MAGIC;  
    7.         common.version = sizeof(ANativeWindowBuffer);  
    8.         memset(common.reserved, 0, sizeof(common.reserved));  
    9.     }  
    10.     void incStrong(const void* id) const {  
    11.         common.incRef(const_cast<android_native_base_t*>(&common));  
    12.     }  
    13.     void decStrong(const void* id) const {  
    14.         common.decRef(const_cast<android_native_base_t*>(&common));  
    15.     }  
    16. #endif  
    17.     struct android_native_base_t common;//描述EGL版本信息  
    18.     int width; //图像宽度  
    19.     int height; //图像高度  
    20.     int stride; //  
    21.     int format; //图像格式  
    22.     int usage; //该buffer的用途  
    23.     void* reserved[2]; //保留  
    24.     buffer_handle_t handle; //该buffer的句柄信息  
    25.     void* reserved_proc[8];  
    26. } android_native_buffer_t;  

    接着为创建的2个NativeBuffer分配空间,使用Gralloc模块中的gpu来完成空间的分配过程,同时指定标志位为GRALLOC_USAGE_HW_FB,表示从系统帧缓冲区Framebuffer中分配。

    1. err = grDev->alloc(grDev,fbDev->width, fbDev->height, fbDev->format,GRALLOC_USAGE_HW_FB, &buffers[i]->handle, &buffers[i]->stride);  
    关于图形缓冲区的完整分配过程请阅读Android图形缓冲区分配过程源码分析。 FramebufferNativeWindow完成图形缓冲区的分配后,还需初始化从ANativeWindow中继承而来的本地窗口定义的相关接口, 即FramebufferNativeWindow实现ANativeWindow本地窗口协议。从FramebufferNativeWindow的构 造函数中,我们知道,FramebufferNativeWindow从Framebuffer中分配了2个缓冲区,说明 FramebufferNativeWindow使用了双缓冲技术,使用双缓冲技术的优点是什么呢?假设我们需要绘制这样一个画面,包括两个三角形和三个 圆形,最终结果如下图所示:

    在只有一个buffer的情况下,我们是直接以屏幕为画板来实时做画的,假设图中的每一个三角形或圆形都需要0.5秒为例,那么总计耗时应该是0.5*5=2.5秒,图形绘制过程如下:

    对于用户来说,他将看到一个不断刷新的画面。对于图像刷新很频繁的情况,用户的体验就会更差。出现这种现象的原因就是程序直接以屏幕为绘图板,把还 没有准备就绪的图像直接呈现给了用户。换句话说,如果将整幅图绘制完成以后再刷新到屏幕上,那么对于用户来说,他在任何时候看到的都是完整的图像。采用两 个缓冲区绘制图形的情况如下:

    既然FramebufferNativeWindow创建了两块图形缓冲区,那它是如何维护这两块图形缓冲区的呢?接下来就介绍图形缓冲区的申请过程:

    1. int FramebufferNativeWindow::dequeueBuffer(ANativeWindow* window,   
    2.         ANativeWindowBuffer** buffer)  
    3. {  
    4.     FramebufferNativeWindow* self = getSelf(window);  
    5.     Mutex::Autolock _l(self->mutex);  
    6.     //从FramebufferNativeWindow对象中取出fb设备描述符,在构造FramebufferNativeWindow对象时,已经打开了fb设备  
    7.     framebuffer_device_t* fb = self->fbDev;  
    8.     //计算当前申请的图形缓冲区在buffers数组中的索引,同时将下一个申请的buffer的索引保存到mBufferHead中  
    9.     int index = self->mBufferHead++;  
    10.     //如果申请的下一个buffer的索引大于或等于buffer总数,则将下一个申请的buffer索引设置为0,这样就实现了对buffer数组的循环管理  
    11.     if (self->mBufferHead >= self->mNumBuffers)  
    12.         self->mBufferHead = 0;  
    13.     //如果当前没有空闲的buffer,即mNumFreeBuffers= 0,则线程睡眠等待buffer的释放  
    14.     while (!self->mNumFreeBuffers) {  
    15.         self->mCondition.wait(self->mutex);  
    16.     }  
    17.     //存在了空闲buffer,线程被唤醒继续执行,由于此时要申请一块buffer,因此空闲buffer的个数又需要减1  
    18.     self->mNumFreeBuffers--;  
    19.     //保存当前申请的buffer在缓冲区数组中的索引位置  
    20.     self->mCurrentBufferIndex = index;  
    21.     //得到buffer数组中的NativeBuffer对象指针  
    22.     *buffer = self->buffers[index].get();  
    23.     return 0;  
    24. }  
    dequeueBuffer 函数就是从FramebufferNativeWindow创建的包含2个图形缓冲区的缓冲区队列buffers中取出一块空闲可用的图形buffer, 如果当前缓冲区队列中没有空闲的buffer,则当前申请buffer线程阻塞等待,等待其他线程释放图形缓冲区。mNumFreeBuffers用来描 述可用的空闲图形buffer个数,index记录当前申请buffer在图形缓冲区队列中的索引位置,mBufferHead指向下一次申请的图形 buffer的位置,由于我们是循环利用两个缓冲区的,所以如果这个变量的值超过mNumBuffers,就需要置0。也就是说mBufferHead的 值永远只能是0或者1。
    上图描述了图形绘制的整个过程,SurfaceFlinger首先从FramebufferNativeWindow中申请出列一块图形 buffer,然后将系统中的各个Surface的GraphicBuffer进行图形混合,将混合后的图形保存到申请所得的图形buffer中,接着将 该buffer放回FramebufferNativeWindow的图形缓冲区队列中,最后将该buffer渲染到显示屏幕上。接下来介绍图形 buffer如列过程:
    1. int FramebufferNativeWindow::queueBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer)  
    2. {  
    3.     FramebufferNativeWindow* self = getSelf(window);  
    4.     Mutex::Autolock _l(self->mutex);  
    5.     //从FramebufferNativeWindow对象中取出fb设备描述符,在构造FramebufferNativeWindow对象时,已经打开了fb设备  
    6.     framebuffer_device_t* fb = self->fbDev;  
    7.     //从NativeBuffer对象中取出buffer_handle_t  
    8.     buffer_handle_t handle = static_cast<NativeBuffer*>(buffer)->handle;  
    9.     const int index = self->mCurrentBufferIndex;  
    10.     //调用framebuffer_device_t中注册的post函数将已绘制好的buffer渲染到Framebuffer中。  
    11.     int res = fb->post(fb, handle);  
    12.     //将当前NativeBuffer保存为前台buffer  
    13.     self->front = static_cast<NativeBuffer*>(buffer);  
    14.     //由于当前NativeBuffer已经渲染完成,因此将当前buffer入列,从而可以被申请  
    15.     self->mNumFreeBuffers++;  
    16.     //唤醒图形buffer申请出列线程,表示已有空闲buffer可以被申请  
    17.     self->mCondition.broadcast();  
    18.     return res;  
    19. }  
    这里将调用fb设备的post方法将buffer渲染到屏幕上,然后修改空闲buffer个数,最后唤醒正在申请图形buffer出列,却因无空闲buffer而睡眠的线程。
    1. static int fb_post(struct framebuffer_device_t* dev, buffer_handle_t buffer)  
    2. {     
    3.     //校验buffer_handle_t  
    4.     if (private_handle_t::validate(buffer) < 0)  
    5.         return -EINVAL;  
    6.     //将framebuffer_device_t强制转换为fb_context_t指针  
    7.     fb_context_t* ctx = (fb_context_t*)dev;  
    8.     //将buffer_handle_t强制转换为private_handle_t指针   
    9.     private_handle_t const* hnd = reinterpret_cast<private_handle_t const*>(buffer);  
    10.     //通过fb_context_t设备描述符找到对应的硬件抽象设备hw_device_t,在根据hw_device_t找到对应的硬件抽象模块hw_moudle_t,最后强制转换为private_module_t指针  
    11.     private_module_t* m = reinterpret_cast<private_module_t*>(dev->common.module);  
    12.     //如果当前buffer是从Framebuffer中分配的缓冲区  
    13.     if (hnd->flags & private_handle_t::PRIV_FLAGS_FRAMEBUFFER) {  
    14.         const size_t offset = hnd->base - m->framebuffer->base;  
    15.         m->info.activate = FB_ACTIVATE_VBL;  
    16.         m->info.yoffset = offset / m->finfo.line_length;  
    17.         if (ioctl(m->framebuffer->fd, FBIOPUT_VSCREENINFO, &m->info) == -1) {  
    18.             ALOGE("FBIOPUT_VSCREENINFO failed");  
    19.             m->base.unlock(&m->base, buffer);   
    20.             return -errno;  
    21.         }  
    22.         m->currentBuffer = buffer;  
    23.     } else {  
    24.         // If we can't do the page_flip, just copy the buffer to the front   
    25.         // FIXME: use copybit HAL instead of memcpy  
    26.         void* fb_vaddr;  
    27.         void* buffer_vaddr;  
    28.         m->base.lock(&m->base, m->framebuffer, GRALLOC_USAGE_SW_WRITE_RARELY, 0, 0, m->info.xres, m->info.yres,&fb_vaddr);  
    29.         m->base.lock(&m->base, buffer, GRALLOC_USAGE_SW_READ_RARELY, 0, 0, m->info.xres, m->info.yres,&buffer_vaddr);  
    30.         memcpy(fb_vaddr, buffer_vaddr, m->finfo.line_length * m->info.yres);  
    31.         m->base.unlock(&m->base, buffer);   
    32.         m->base.unlock(&m->base, m->framebuffer);   
    33.     }  
    34.     return 0;  
    35. }  
    最后通过FBIOPUT_VSCREENINFO命令进入Framebuffer驱动,将图形渲染显示。
  • 相关阅读:
    Emacs和ESS的使用技巧。
    响应式
    Day learn,day up
    Docker快速安装kafka | 沈健的技术博客
    闭包函数如何使用循环变量
    leetcode笔记——35.搜索插入位置
    CSS 之动态变换背景颜色
    吴裕雄 PYTHON 神经网络——TENSORFLOW 双隐藏层自编码器设计处理MNIST手写数字数据集并使用TENSORBORD描绘神经网络数据2
    吴裕雄 PYTHON 神经网络——TENSORFLOW 双隐藏层自编码器设计处理MNIST手写数字数据集并使用TENSORBORD描绘神经网络数据
    吴裕雄 PYTHON 神经网络——TENSORFLOW 单隐藏层自编码器设计处理MNIST手写数字数据集并使用TensorBord描绘神经网络数据
  • 原文地址:https://www.cnblogs.com/shakin/p/4521799.html
Copyright © 2011-2022 走看看