EGL资源的数据共享应用和底层驱动实现
已有 4201 次阅读 2015-8-12 19:22 |个人分类:
OpenGL
|系统分类:
科研笔记
为了某个原因成文于2013年,本文最有意义的是在最后揭示了如何实现免拷贝的数据共享(non-copy,zero-copy),我还没有在之前的书籍教材中有看到过介绍的。通过CPU和GPU直接的数据直接共享,GPU之上各API之间的数据直接共享,可以大幅度的提高性能。这是个普遍的思路,在各种应用背景下都可以实现并使用。
摘 要 随着移动设备的广泛应用,如何以更低的功耗提供更佳的用户体验成为一个非常重要的课题。EGL通过定义EGLSurface和EGL Image等资源,使得不同renderingAPI间以及和原生平台间的数据可以实现直接共享,从而降低功耗提高性能。本文整合分散在各种规范中的概念介绍了如何在应用层使用EGL资源进行数据共享,并解释了在驱动程序中是如何支持的思路,以深入理解,不仅知其然,而且知其所以然。
关键词 EGL, OpenGL ES, 低功耗, 数据共享
EGL是由Khronos组织维护的、连接OpenGL ES[
[1]
]和OpenVG[
[2]
]等rendering API与原生平台系统(native platformwindow system)的一套API接口,由核心规范[
[3]
]和扩展规范[
[4]
]组成。随着移动设备的广泛应用,如何以更低的功耗提供更佳的用户体验成为一个非常重要的课题。EGL通过定义EGL Surface和EGL Image等资源,使得不同rendering API间以及和原生平台间的数据可以实现直接共享,而无需拷贝。这种无拷贝机制不仅减少了内存读写从而降低功耗,而且提高了性能从而改善用户的体验。
由于不同的EGL资源在概念上相近,却在核心规范和多个扩展规范中被分别定义,容易混淆,因此,很有必要总结成文厘清概念,并解释其驱动程序的底层实现思路,以加深理解,从而更好的在移动设备上的应用程序中用好EGL资源。
2.EGL资源在应用层的数据共享
EGL Surface和EGLImage等EGL资源在应用程序中的创建和使用是类似的,具体函数名不同,但是形式上是类似的。而且两者最终都是通过成为绘制目标(RenderingTarget)或者纹理资源(Texture)来实现的,很容易造成函数的误用。
2.1 EGL Surface的创建和使用
EGL Surface的创建和使用在EGL核心规范中定义。EGL Surface分成 WindowSurface、PixmapSurface和PbufferSurface三种类型,分别由函数eglCreateWindowSurface、eglCreatePixmapSurface和eglCreatePbufferSurface/eglCreatePbufferFromClientBuffer创建,如图1所示,箭头上的注释是函数名。其中,WindowSurface来自窗口系统,是最终在屏幕可见的Surface;PixmapSurface和PbufferSurface则是不可见的,两者区别在于PbufferSurface属于EGL创建,而PixmapSurface则是来自原生平台系统。随着函数eglCreatePbufferFromClientBuffer的出现,使得PbufferSurface也可以来自原生平台系统。由于PixmapSurface的概念相对简单,而且和窗口系统的不可见资源具有对应关系,因此,一般来说,我们会倾向使用PixmapSurface而不是PbufferSurface。
在图1中,指向椭圆形EGL Surface的箭头表示Surface的被创建,而源自EGL Surface的箭头则表示Surface的被使用。箭头起始端的矩形中的变量是该箭头函数的主要参数,其中,EGLNativeWindowType win和EGLNativePixmapType pixmap一般都在原生平台中被定义,所以可以被原生平台中支持的函数所直接访问。例如X11系统中由Xlib库创建的Window和Pixmap。
无论哪种EGL Surface,都可以通过eglMakeCurrent函数成为Render Target,但是,由于eglMakeCurrent会导致graphics context的切换从而影响性能,所以,在可能的情况下,不可见的RenderTarget一般不使用EGL Surface,而使用FBO技术(FrameBuffer Object[
[5]
])。PBufferSurface还可以通过eglBindTexImage函数成为纹理资源(Texture),以作为graphicspipeline的数据来源;一般来说,原生平台提供商还会增加一个扩展规范,使得PixmapSurface也可以通过eglBindTexImage函数成为Texture。
2.2
EGL Image的创建和使用
EGL Image最初在扩展规范EGL_KHR_image中定义,为了进一步扩展的需要,在2008年11月19号被分成为两个扩展规范,如图2的椭圆形EGLImage向上部分所示,其中,EGL_KHR_image_base定义了EGLImage的相关概念和相应的eglCreateImageKHR函数,该函数的两个关键参数是target和buffer,另一个扩展规范是EGL_KHR_image_pixmap,使得原生平台的pixmap可以被创建为EGL Image。在此基础上,Android平台提出了EGL_Android_image_native_buffer使得android平台定义的ANativeWindowBuffer可以被创建为EGL Image;还有EGL_KHR_gl_texture_2D_image,EGL_KHR_gl_texture_3D_image,EGL_KHR_gl_renderbuffer_image和EGL_KHR_gl_texture_cubemap_image等扩展规范使得OpenGLES中的资源可以被创建为EGL Image。一般的,每个原生平台提供者都会提供其私有的扩展规范来创建相应的EGLImage。
EGL Image的使用在OpenGLES的扩展规范中被定义,如图2的椭圆形EGL Surface部分向下所示,首先,EGL Image被重新定义(typedef)为GLeglImageOES,然后,扩展规范GL_OES_EGL_image定义了两个函数,使得EGL Image可以被当做2D Texture或者Renderbuffer使用,可分别作为graphicspipeline的数据来源和绘制目标。另一个扩展规范GL_OES_EGL_image_external定义了一个新的Texture类型GL_TEXTURE_EXTERNAL_OES,使得诸如YUV等以前不被OpenGL ES支持的格式的EGL Image,可以被直接作为一个Texture从而被OpenGL ES所支持,当然,其背后离不开驱动程序内部增加shader指令进行格式转换,假以时日,可能会出现直接支持YUV等格式的texture采样硬件。
3.EGL资源在驱动底层的实现
EGL Surface和EGLImage数据结构的c语言定义都是void *,它们可以从多种来源被创建,也有多个使用场合,那么,在驱动底层是如何光凭void *就能知道当前情况呢,一个参考实现是使用基础struct和magic number,其中,magic number在基础struct中被定义,所有相关资源数据结构的最开始部分就是基础struct。这样,在创建时返回对应资源数据结构(struct)的指针作为void *的handle,在使用时则将此handle强制转回基础struct指针,然后根据struct中magic number数据成员的值来得知原始资源是什么从而将其用好。
之前提到,有些资源还可以被原生平台提供的函数读写,那么,是怎么实现CPU和GPU对同一资源的访问呢?抛开表象看本质,我们知道所有资源最终都是由物理存储器中的内存页面组成,CPU通过其内存管理单元(MMU)进行地址映射从而访问到真实存储,有了这些信息,内核态的驱动程序就可以通过配置GPU的类MMU单元,从而使得GPU也可以访问这些资源,这也就意味着graphicspipeline可以读写这些资源。在此,需要特别关注并解决好CPU和GPU对同一资源的并发访问和cache刷新的技术问题。
知其然,也知其所以然。通过以上对EGL Surface和EGLImage的介绍,包括如何创建和使用,也包括在驱动底层的支持思路,可以深入理解这些EGL资源的无需拷贝的数据共享机制是如何降低功耗和提高性能。特别地,将所有相关重要概念都在文中的两幅图中表现出来,做到一目了然,这对用好EGL资源具有重要意义。限于篇幅所限,无法对EGL Surface和EGLImage进行更多细节上的介绍,需要用户在使用细节上参考相应的EGL和OpenGLES的核心规范和扩展规范。
[1]
OpenGL ES - The Standard for Embedded Accelerated 3D Graphics,
http://www.khronos.org/opengles/
[2]
OpenVG - The Standard for Vector Graphics Acceleration,
http://www.khronos.org/openvg/
[3]
Jon Leech, Khronos Native Platform Graphics Interface (EGL Version 1.4 – April6, 2011)
http://www.khronos.org/registry/egl/specs/eglspec.1.4.20110406.pdf
[4]
Khronos EGL API Registry,
http://www.khronos.org/registry/egl/
[5]
Aaftab Munshi, GL_OES_framebuffer_object,
http://www.khronos.org/registry/gles/extensions/OES/OES_framebuffer_object.txt
扩展参考
http://blog.chinaunix.net/uid-20235103-id-2974856.html
转载本文请联系原作者获取授权,同时请注明本文来自郭叶军科学网博客。
链接地址:
http://blog.sciencenet.cn/blog-1420268-912560.html
EGL资源的数据共享应用和底层驱动实现已有 4201 次阅读2015-8-12 19:22|个人分类:OpenGL|系统分类:科研笔记为了某个原因成文于2013年,本文最有意义的是在最后揭示了如何实现免拷贝的数据共享(non-copy,zero-copy),我还没有在之前的书籍教材中有看到过介绍的。通过CPU和GPU直接的数据直接共享,GPU之上各API之间的数据直接共享,可以大幅度的提高性能。这是个普遍的思路,在各种应用背景下都可以实现并使用。摘要随着移动设备的广泛应...
EGL
Image代表一种由
EGL
客户API(如OpenGL,OpenVG)创建的
共享
资源
类型。它的本意是
共享
2D图像
数据
,但是并没有明确限定
共享
数据
的格式以及
共享
的目的,所以理论上来讲,
应用
程序以及相关的客户API可以基于任意的目的创建任意类型的
共享
数据
。
关于
EGL
Image的一种使用情景就是通过它来创建一个2D纹理。相关函数原型声明如下:
EGL
API
EGL
Ima
1. 概览
EGL
是介于各种粉刷(rendering)API如(OpenGL,OpenVG等)和
底层
窗口系统平台之间的接口。
EGL
提供各种机制创建图形context和粉刷surface供客户API使用。同时,
EGL
也在绘图的过程中为本地绘图平台和客户api提供同步机制。
EGL
提供远程和间接粉刷机制,而GLX API则有这个机制。
2.
EGL
操作
2.1本地窗口系统和粉刷API
EGL
可以
Linux上的OpenGL无头(
EGL
):示例程序
该存储库包含一个示例python程序,该程序以无头模式(无显示)运行GPU计算(OpenGL) 。
该程序使用
EGL
(以及可选的GBM)初始化OpenGL上下文。 它将连接到/dev/dri/<something>内核DRM接口文件。
然后,为了测试它是否有效,它调用了非常基本的OpenGL绘制函数来绘制一个蓝色矩形,并编写一个ppm图像文件。
操作系统库
无论如何,您都需要带有libGL和lib
EGL
的合适的GPU
驱动
程序。 如果您使用最新的Nvidia
驱动
程序,这可能就足够了。 否则(例如,英特尔集成GPU),程序将尝试加载li
-【参考-khronos-
egl
(最新版本1.5)】https://www.khronos.org/
egl
-【参考-khronos-
egl
-api(目前共34个API,不包括Khronos扩展)】https://www.khronos.org/registry/
EGL
/sdk/docs/man/
-【参考-khronos-
egl
-intro】https://www.khronos.org/registry/
EGL
/sdk/docs/man/html/
egl
Intro.xhtml
EGL
.
EGL
(Embedded System Graphics Library)是用于嵌入式系统的图形库,用于管理OpenGL ES或OpenVG与本地窗口系统之间的通信。在配置
EGL
之前,需要确保您已经安装了OpenGL ES和OpenVG的
驱动
程序。
下面是一些基本的配置步骤:
1. 创建
EGL
上下文
在
应用
程序初始化期间,需要创建
EGL
上下文来管理OpenGL ES或OpenVG渲染。可以使用以下代码创建一个
EGL
上下文:
EGL
Display display =
egl
GetDisplay(
EGL
_DEFAULT_DISPLAY);
EGL
int major, minor;
egl
Initialize(display, &major, &minor);
EGL
int configAttribs[] = {
EGL
_SURFACE_TYPE,
EGL
_WINDOW_BIT,
EGL
_RED_SIZE, 8,
EGL
_GREEN_SIZE, 8,
EGL
_BLUE_SIZE, 8,
EGL
_ALPHA_SIZE, 8,
EGL
_NONE
EGL
int numConfigs;
EGL
Config config;
egl
ChooseConfig(display, configAttribs, &config, 1, &numConfigs);
EGL
Surface surface =
egl
CreateWindowSurface(display, config, window, NULL);
EGL
int contextAttribs[] = {
EGL
_CONTEXT_CLIENT_VERSION, 2,
EGL
_NONE
EGL
Context context =
egl
CreateContext(display, config,
EGL
_NO_CONTEXT, contextAttribs);
egl
MakeCurrent(display, surface, surface, context);
请注意,这只是创建
EGL
上下文的基本代码示例。您需要根据您的
应用
程序需要进行修改。
2. 绑定OpenGL ES或OpenVG
在创建
EGL
上下文后,需要将OpenGL ES或OpenVG与
EGL
绑定。可以使用以下代码:
egl
BindAPI(
EGL
_OPENGL_ES_API);
或者,如果您正在使用OpenVG:
egl
BindAPI(
EGL
_OPENVG_API);
3. 渲染
一旦您的
EGL
上下文已经创建和绑定,您就可以开始渲染OpenGL ES或OpenVG图形了。您可以使用OpenGL ES或OpenVG API来绘制图形。
4. 清理
最后,在
应用
程序退出时,需要清理
EGL
上下文。您可以使用以下代码:
egl
MakeCurrent(display,
EGL
_NO_SURFACE,
EGL
_NO_SURFACE,
EGL
_NO_CONTEXT);
egl
DestroyContext(display, context);
egl
DestroySurface(display, surface);
egl
Terminate(display);
这些是基本的
EGL
配置步骤。您可能需要根据您的
应用
程序需要进行修改。