转自:http://www.myexception.cn/image/1461302.html
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。
EGLSurface eglCreateWindowSurface(EGLDisplay dpy, EGLConfig config, NativeWindowType window, const EGLint *attrib_list)
函数eglCreateWindowSurface是OpenGL ES提供用于创建窗口的函数接口,参数window的类型为NativeWindowType,定义如下:
frameworks\native\opengl\include\EGL\eglplatform.h
typedef EGLNativeWindowType NativeWindowType; #if defined(_WIN32) || defined(__VC32__) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__) /* Win32 and WinCE */ typedef HWND EGLNativeWindowType; #elif defined(__WINSCW__) || defined(__SYMBIAN32__) /* Symbian */ typedef void *EGLNativeWindowType; #elif defined(__ANDROID__) || defined(ANDROID) typedef struct ANativeWindow* EGLNativeWindowType; #elif defined(__unix__) typedef Window EGLNativeWindowType; #else #error "Platform not recognized" #endif
NativeWindowType定义为EGLNativeWindowType类型,而该类型在不同的平台中有不同的定义,这是因为OpenGL ES是一个跨平台的图形绘制库,对于Android系统来说,其定义为ANativeWindow指针类型,而ANativeWindow的定义如下:
struct ANativeWindow { #ifdef __cplusplus ANativeWindow(): flags(0), minSwapInterval(0), maxSwapInterval(0), xdpi(0), ydpi(0) { common.magic = ANDROID_NATIVE_WINDOW_MAGIC; common.version = sizeof(ANativeWindow); memset(common.reserved, 0, sizeof(common.reserved)); } void incStrong(const void* id) const { common.incRef(const_cast<android_native_base_t*>(&common)); } void decStrong(const void* id) const { common.decRef(const_cast<android_native_base_t*>(&common)); } #endif struct android_native_base_t common; const uint32_t flags;//用于描述该Surface的一些属性 const int minSwapInterval;//最小交换间隔时间 const int maxSwapInterval;//最大交换间隔时间 const float xdpi;//水平方向的密度 const float ydpi;//垂直方向的密度 intptr_t oem[4];//为OEM预留 //设置交换间隔时间 int (*setSwapInterval)(struct ANativeWindow* window,int interval); //申请一个图形缓冲区buffer int (*dequeueBuffer)(struct ANativeWindow* window,struct ANativeWindowBuffer** buffer); //锁定图形缓冲区 int (*lockBuffer)(struct ANativeWindow* window,struct ANativeWindowBuffer* buffer); //buffer渲染完成后,它调用这个接口来unlock和post buffer int (*queueBuffer)(struct ANativeWindow* window,struct ANativeWindowBuffer* buffer); //向本地窗口查询相关信息 int (*query)(const struct ANativeWindow* window,int what, int* value); //执行本地窗口支持的各种操作 int (*perform)(struct ANativeWindow* window,int operation, ... ); //取消一个已经dequeued的buffer int (*cancelBuffer)(struct ANativeWindow* window,struct ANativeWindowBuffer* buffer); //预留 void* reserved_proc[2]; }android_native_window_t;
当使用C++编译器是,为ANativeWindow定义了相应的构造函数,在OpenGL ES下的Android本地窗口系统的类关系图如下:
从上图可以看出,Surface和FramebufferNativeWindow都继承于ANativeWindow,因此也就继承了OpenGL ES下的本地窗口定义的相关协议:ANativeWindow中定义的相关接口。下面分别对这两种类型的本地窗口进行深入分析。
FramebufferNativeWindow
class FramebufferNativeWindow : public ANativeObjectBase< ANativeWindow, FramebufferNativeWindow, LightRefBase<FramebufferNativeWindow> > { framebuffer_device_t* fbDev; //描述Framebuffer设备 alloc_device_t* grDev; //描述gpu设备 sp<NativeBuffer> buffers[NUM_FRAME_BUFFERS];//定义2个图形缓冲区 sp<NativeBuffer> front; //前台图形缓冲区,即正在渲染的图形缓冲区 mutable Mutex mutex; Condition mCondition; int32_t mNumBuffers; //图形缓冲区个数 int32_t mNumFreeBuffers; //可以使用的图形缓冲区个数 int32_t mBufferHead; // int32_t mCurrentBufferIndex;//当前图形缓冲区的索引 bool mUpdateOnDemand; };接下来看看FramebufferNativeWindow对象的构造过程:
FramebufferNativeWindow::FramebufferNativeWindow() : BASE(), fbDev(0), grDev(0), mUpdateOnDemand(false) { hw_module_t const* module; //加载gralloc模块 if (hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module) == 0) { int stride; int err; int i; //打开fb设备 err = framebuffer_open(module, &fbDev); ALOGE_IF(err, "couldn't open framebuffer HAL (%s)", strerror(-err)); //打开gpu设备 err = gralloc_open(module, &grDev); ALOGE_IF(err, "couldn't open gralloc HAL (%s)", strerror(-err)); //设备打开失败,返回 if (!fbDev || !grDev) return; mUpdateOnDemand = (fbDev->setUpdateRect != 0); // 初始化变量值 mNumBuffers = NUM_FRAME_BUFFERS;//2 mNumFreeBuffers = NUM_FRAME_BUFFERS;//2 mBufferHead = mNumBuffers-1;//1 #ifdef FRAMEBUFFER_FORCE_FORMAT *((uint32_t *)&fbDev->format) = FRAMEBUFFER_FORCE_FORMAT; #endif //创建2个NativeBuffer for (i = 0; i < mNumBuffers; i++) { buffers[i] = new NativeBuffer(fbDev->width, fbDev->height, fbDev->format, GRALLOC_USAGE_HW_FB); } //为NativeBuffer分配缓冲区 for (i = 0; i < mNumBuffers; i++) { err = grDev->alloc(grDev,fbDev->width, fbDev->height, fbDev->format,GRALLOC_USAGE_HW_FB, &buffers[i]->handle, &buffers[i]->stride); ALOGE_IF(err, "fb buffer %d allocation failed w=%d, h=%d, err=%s",i, fbDev->width, fbDev->height, strerror(-err)); if (err) { mNumBuffers = i; mNumFreeBuffers = i; mBufferHead = mNumBuffers-1; break; } } //使用Framebuffer的设备描述符来初始化本地窗口ANativeWindow的相关属性 const_cast<uint32_t&>(ANativeWindow::flags) = fbDev->flags; const_cast<float&>(ANativeWindow::xdpi) = fbDev->xdpi; const_cast<float&>(ANativeWindow::ydpi) = fbDev->ydpi; const_cast<int&>(ANativeWindow::minSwapInterval) = fbDev->minSwapInterval; const_cast<int&>(ANativeWindow::maxSwapInterval) = fbDev->maxSwapInterval; } else { ALOGE("Couldn't get gralloc module"); } //为本地窗口ANativeWindow设置回调接口函数 ANativeWindow::setSwapInterval = setSwapInterval; ANativeWindow::dequeueBuffer = dequeueBuffer; ANativeWindow::lockBuffer = lockBuffer; ANativeWindow::queueBuffer = queueBuffer; ANativeWindow::query = query; ANativeWindow::perform = perform; }
函数首先加载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的句柄等等信息。
typedef struct ANativeWindowBuffer { //针对C++编译器定义构造函数 #ifdef __cplusplus ANativeWindowBuffer() { common.magic = ANDROID_NATIVE_BUFFER_MAGIC; common.version = sizeof(ANativeWindowBuffer); memset(common.reserved, 0, sizeof(common.reserved)); } void incStrong(const void* id) const { common.incRef(const_cast<android_native_base_t*>(&common)); } void decStrong(const void* id) const { common.decRef(const_cast<android_native_base_t*>(&common)); } #endif struct android_native_base_t common;//描述EGL版本信息 int width; //图像宽度 int height; //图像高度 int stride; // int format; //图像格式 int usage; //该buffer的用途 void* reserved[2]; //保留 buffer_handle_t handle; //该buffer的句柄信息 void* reserved_proc[8]; } android_native_buffer_t;
接着为创建的2个NativeBuffer分配空间,使用Gralloc模块中的gpu来完成空间的分配过程,同时指定标志位为GRALLOC_USAGE_HW_FB,表示从系统帧缓冲区Framebuffer中分配。
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创建了两块图形缓冲区,那它是如何维护这两块图形缓冲区的呢?接下来就介绍图形缓冲区的申请过程:
int FramebufferNativeWindow::dequeueBuffer(ANativeWindow* window, ANativeWindowBuffer** buffer) { FramebufferNativeWindow* self = getSelf(window); Mutex::Autolock _l(self->mutex); //从FramebufferNativeWindow对象中取出fb设备描述符,在构造FramebufferNativeWindow对象时,已经打开了fb设备 framebuffer_device_t* fb = self->fbDev; //计算当前申请的图形缓冲区在buffers数组中的索引,同时将下一个申请的buffer的索引保存到mBufferHead中 int index = self->mBufferHead++; //如果申请的下一个buffer的索引大于或等于buffer总数,则将下一个申请的buffer索引设置为0,这样就实现了对buffer数组的循环管理 if (self->mBufferHead >= self->mNumBuffers) self->mBufferHead = 0; //如果当前没有空闲的buffer,即mNumFreeBuffers= 0,则线程睡眠等待buffer的释放 while (!self->mNumFreeBuffers) { self->mCondition.wait(self->mutex); } //存在了空闲buffer,线程被唤醒继续执行,由于此时要申请一块buffer,因此空闲buffer的个数又需要减1 self->mNumFreeBuffers--; //保存当前申请的buffer在缓冲区数组中的索引位置 self->mCurrentBufferIndex = index; //得到buffer数组中的NativeBuffer对象指针 *buffer = self->buffers[index].get(); return 0; }dequeueBuffer函数就是从FramebufferNativeWindow创建的包含2个图形缓冲区的缓冲区队列buffers中取出一块空闲可用的图形buffer,如果当前缓冲区队列中没有空闲的buffer,则当前申请buffer线程阻塞等待,等待其他线程释放图形缓冲区。mNumFreeBuffers用来描述可用的空闲图形buffer个数,index记录当前申请buffer在图形缓冲区队列中的索引位置,mBufferHead指向下一次申请的图形buffer的位置,由于我们是循环利用两个缓冲区的,所以如果这个变量的值超过mNumBuffers,就需要置0。也就是说mBufferHead的值永远只能是0或者1。
int FramebufferNativeWindow::queueBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer) { FramebufferNativeWindow* self = getSelf(window); Mutex::Autolock _l(self->mutex); //从FramebufferNativeWindow对象中取出fb设备描述符,在构造FramebufferNativeWindow对象时,已经打开了fb设备 framebuffer_device_t* fb = self->fbDev; //从NativeBuffer对象中取出buffer_handle_t buffer_handle_t handle = static_cast<NativeBuffer*>(buffer)->handle; const int index = self->mCurrentBufferIndex; //调用framebuffer_device_t中注册的post函数将已绘制好的buffer渲染到Framebuffer中。 int res = fb->post(fb, handle); //将当前NativeBuffer保存为前台buffer self->front = static_cast<NativeBuffer*>(buffer); //由于当前NativeBuffer已经渲染完成,因此将当前buffer入列,从而可以被申请 self->mNumFreeBuffers++; //唤醒图形buffer申请出列线程,表示已有空闲buffer可以被申请 self->mCondition.broadcast(); return res; }这里将调用fb设备的post方法将buffer渲染到屏幕上,然后修改空闲buffer个数,最后唤醒正在申请图形buffer出列,却因无空闲buffer而睡眠的线程。
static int fb_post(struct framebuffer_device_t* dev, buffer_handle_t buffer) { //校验buffer_handle_t if (private_handle_t::validate(buffer) < 0) return -EINVAL; //将framebuffer_device_t强制转换为fb_context_t指针 fb_context_t* ctx = (fb_context_t*)dev; //将buffer_handle_t强制转换为private_handle_t指针 private_handle_t const* hnd = reinterpret_cast<private_handle_t const*>(buffer); //通过fb_context_t设备描述符找到对应的硬件抽象设备hw_device_t,在根据hw_device_t找到对应的硬件抽象模块hw_moudle_t,最后强制转换为private_module_t指针 private_module_t* m = reinterpret_cast<private_module_t*>(dev->common.module); //如果当前buffer是从Framebuffer中分配的缓冲区 if (hnd->flags & private_handle_t::PRIV_FLAGS_FRAMEBUFFER) { const size_t offset = hnd->base - m->framebuffer->base; m->info.activate = FB_ACTIVATE_VBL; m->info.yoffset = offset / m->finfo.line_length; if (ioctl(m->framebuffer->fd, FBIOPUT_VSCREENINFO, &m->info) == -1) { ALOGE("FBIOPUT_VSCREENINFO failed"); m->base.unlock(&m->base, buffer); return -errno; } m->currentBuffer = buffer; } else { // If we can't do the page_flip, just copy the buffer to the front // FIXME: use copybit HAL instead of memcpy void* fb_vaddr; void* buffer_vaddr; m->base.lock(&m->base, m->framebuffer, GRALLOC_USAGE_SW_WRITE_RARELY, 0, 0, m->info.xres, m->info.yres,&fb_vaddr); m->base.lock(&m->base, buffer, GRALLOC_USAGE_SW_READ_RARELY, 0, 0, m->info.xres, m->info.yres,&buffer_vaddr); memcpy(fb_vaddr, buffer_vaddr, m->finfo.line_length * m->info.yres); m->base.unlock(&m->base, buffer); m->base.unlock(&m->base, m->framebuffer); } return 0; }
相关推荐
3 9 1 Android中的窗口:Activity 3 9 2 广播接收器:Broadcast Receiver 3 9 3 服务 Service 3 9 4 内容提供者 Content Provider 第4章 对话框 信息提示和菜单 4 1 对话框 4 2 信息提示 4 2 1 Toast信息框 4 2 2 ...
Android 24引入了多窗口模式,允许用户同时使用两个应用。此外,它还支持数据节省模式,帮助用户减少移动数据的使用,以及改善电池寿命的Doze模式。源码中,我们可以看到这些功能是如何在系统层面实现的。 2. **...
AnDroidDraw的独特之处在于,它可以让你直接在Android模拟器或真实设备上预览由DroidDraw设计的GUI,确保界面显示的一致性。通过AnDroidDraw,你可以下载DroidDraw中的GUI设计,并在Android设备上实时查看效果,从而...
屏幕截取保存本地是计算机和移动设备中常用的一项功能,它允许用户快速捕获当前屏幕显示的内容,并将其保存为图像文件。在这个过程中,我们通常会使用到一些特定的技术和工具。下面将详细介绍这个过程以及相关的知识...
EGL作为OpenGL ES与本地窗口系统之间的桥梁,负责管理渲染上下文(EGLContext)、渲染面(EGLSurface)以及与设备显示相关的配置。EGLDisplay表示设备的显示句柄,它可以抽象不同操作系统的窗口系统差异。EGLConfig...
这个示例项目,"gen2brain-go-sdl2-android-example-90926bc",旨在帮助开发者了解如何将Go语言与SDL2集成,以便在Android设备上创建高性能的游戏或图形应用。 **Go语言介绍** Go,也被称为Golang,是Google推出的...
- **多窗口支持**:虽然不是完整意义上的多任务窗口,但KitKat允许某些应用在全屏模式下分屏显示。 - **打印框架**:新增了系统级的打印服务,使用户可以方便地通过无线网络或蓝牙打印文档。 - **WebView更新**:...
Graphic 是 Android 应用程序的图形设计机制,用于实现高品质的 GUI 设计。我们可以使用 Graphic 来实现 2D 和 3D 图形设计,并使用它来设计launcher 的界面。 在 launcher 的开发中,我们需要了解这些基础知识,并...
- **多窗口模式**:Android 7首次引入了多窗口支持,允许应用在分屏模式下同时显示两个应用。 - **通知增强**:引入了直接回复功能,用户可以直接在通知栏对消息进行回复,无需打开应用。 - **Doze模式**:优化了...
【eglDemo Android】是一个专为Android平台设计的项目,它主要展示了如何在原生层(native layer)利用OpenGL ES进行图形绘制。OpenGL ES是OpenGL的一个轻量级版本,专为嵌入式系统如手机和平板电脑设计,用于2D和3D...
在Android开发中,高德地图是一个广泛使用的地图API,提供了丰富的功能,如定位、路径规划、地图绘制等。本文将详细讲解如何实现自定义点聚合Marker图片以及Overlay的点击选中功能。 首先,我们需要理解“点聚合”...
在Android系统中,由于权限和安全性的限制,直接使用Java代码进行串口通信较为复杂,所以通常采用JNI来调用本地库进行操作。在示例中,我们可能能看到如何打开串口、设置波特率、数据位、停止位和校验位,以及发送和...
根据给定的信息,我们可以从《Android图片处理.pdf》这一文档中...这些知识点不仅涵盖了基础的图像处理技术,还包括了高级的图形渲染技巧,对于希望深入学习Android图像处理领域的开发者来说是非常有价值的参考资料。
Android 4.2版本引入了多窗口和多显示器的支持,通过`DisplayManager`类来管理系统的所有显示设备。开发者可以通过`getDisplays()`方法获取当前设备的所有显示屏信息,包括分辨率、类型等。 要实现在不同屏幕间切换...
3. **多窗口支持**:虽然不是完整的多任务视图,但Android 5.0引入了"Picture-in-Picture"模式,允许用户在小窗口中观看视频,同时进行其他操作。 4. **64位支持**:Android 5.0开始支持64位处理器,提供了更好的...
SurfaceView是Android系统中用于显示视频流或实时图形的理想组件。在自定义相机中,SurfaceView作为相机预览的显示窗口,它拥有自己的SurfaceHolder,可以提供硬件加速的渲染,确保流畅的帧率。在创建SurfaceView后...
3. **多任务管理**:Android 3.0改进了多任务处理,允许用户平铺显示多个应用程序窗口。通过源代码,开发者可以学习如何优化应用以适应这种模式。 4. **API变更**:新版本通常会引入新的API,如Android 3.0中的...
开发者可以创建浮动窗口,使得应用程序可以在屏幕的任何位置独立显示。 3. **内存管理优化**:KitKat对内存管理进行了优化,减少了内存泄漏和提高内存回收效率,确保系统在低内存设备上的稳定运行。 4. **WebViews...
通过JNI,我们可以在Java应用程序中嵌入本地代码,比如高性能的计算、图形处理或者与硬件设备的直接通信等。NDK和JNI的结合使用,使得Android应用开发更加灵活,可以充分利用C/C++的性能优势。 配置NDK在Android ...