OpenGL、OpenGL ES、EGL三者的区别和联系

2022-11-02

1. OpenGL、OpenGL ES、EGL三者的区别和联系?

OpenGL(Open Graphics Library): 开放图形库,主要用于2D、3D矢量图形的绘制。封装了一系列API用于图像绘制,OpenGL具有非常好的跨平台性,可在Windows、Linux、MacOS上使用,它依赖于硬件的支持,能够通过GPU高效绘制各种图形和动画。

OpenGL ES (OpenGL for Embedded Systems): OpenGL的简化版本,是以手持和移动设备为目标的高级3D图形图像API。OpenGL ES去除了一些OpenGL中移动端用不到的冗余功能,是目前主流的智能手机图形API,目前支持的平台包括:iOS、Android、BlackBerry、bada、Linux、Windows等。

EGL: OpenGL ES API没有提供如何创建渲染上下文或者上下文如何链接到原生窗口。EGL是渲染API和原生窗口系统之间的接口,比如OpenGL ES和各个平台。iOS系统是唯一支持OpenGL ES但不支持EGL的平台,因为苹果提供了一套自己的EGL API实现,称为EAGL。这有些类似与我们日常开发中对第三方库的二次封装,OpenGL/OpenGL ES对于苹果来说就是他们的第三方库。


2. 什么是OpenGL上下文?

OpenGL是一个状态机,它拥有非常多的状态变量,并且每个状态变量都有默认值。

OpenGL在渲染的时候需要一个Context来记录OpenGL渲染需要的所有信息和状态,可以把它理解成一个大的结构体,里面记录了当前绘制使用的颜色、是否有光照计算以及开启的光源等非常多我们使用OpenGL函数调用设置的状态和状态属性。

在程序中,我们设置的各种状态和默认状态会一直生效,直到我们再次修改它们。

OpenGL的绘制命令都是作用在当前的Context上,这个Current Context是一个线程私有(thread-local)的变量,也就是说如果我们在线程中绘制,那么需要为该线程指定一个Current Context的,当多个线程参与绘制任务时,需要原线程解绑再重新绑定新的线程。多个线程不能同时指定同一个Context为Current Context,否则会导致崩溃。


3. 什么是固定管线和可编程管线?

根据渲染管线的类型,OpenGLES可以被分为固定管线和可编程管线两类。

固定渲染管线的OpenGLES不需要也不允许你自己去定义顶点渲染和像素渲染的具体逻辑,它内部已经固化了一套完整的渲染流程,只需要开发者在CPU代码端输入渲染所需要的参数并指定特定的开关,就能完成不同的渲染。

可编程渲染管线的OpenGLES版本必须由开发者自行实现渲染流程,否则无法绘制出最终的画面。开发者可以根据自己的具体需要来编写顶点渲染(Vertex Shader)和像素渲染(Fragment Shader)中的具体逻辑,可最大程度的简化渲染管线的逻辑以提高渲染效率,也可自己实现特定的算法和逻辑来渲染出固定管线无法渲染的效果。

4. OpenGL多线程绘制方式?

实现效果: OpenGL开启两个线程,一个线程用于绘制,另一个线程用于加载纹理。
实现方法: OpenGL是单线程的,其他线程不能访问另外线程的纹理资源,但是,如果两个线程共享上下文,就可以访问彼此的纹理资源。
绘制线程: 创建两个上下文

// 只有主要代码

...

context = eglCreateContext(display, config, NULL, ctxAttribs);

context1 = eglCreateContext(display, config, context, ctxAttribs);

...

eglMakeCurrent(display, eglSurface, eglSurface, context);

加载纹理线程: 绑定绘制线程中两个上下文中的一个

eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, context1);


5. EGL初始化流程

要在Android平台实现OpenGL渲染,需要完成一系列的EGL操作,主要为下面几步:


    获取显示设备(EGLDisplay): 获取将要用于显示的设备,有些系统具有多个显示器,会存在多个Display。在Android上通过调用EGL10的eglGetDisplay(Object native_display)方法获得EGLDisplay对象,通常传入的参数为EGL10.EGL_DEFAULT_DISPLAY。

    初始化EGL: 调用EGL10的eglInitialize(EGLDisplay display, int[] major_minor)方法完成初始化操作。display参数即为上一步获取的对象,major_minor传入的是一个int数组,通常传入的是一个大小为2的数组,表示EGL的版本信息。

    选择Config配置: 调用EGL10的eglChooseConfig(EGLDisplay display, int[] attrib_list, EGLConfig[] configs, int config_size, int[] num_config)方法,参数1、2其意明显,参数3用于存放输出的configs,参数4指定最多输出多少个config,参数5由EGL系统写入,表明满足attributes的config一共有多少个。

    创建EGL环境(EGLContext): eglCreateContext(EGLDisplay display, EGLConfig config, EGLContext share_context, int[] attrib_list);参数1即为上面获取的Display,参数2为上一步chooseConfig传入的configs,share_context,是否有context共享,共享的contxt之间亦共享所有数据,通常设置为EGL_NO_CONTEXT代表不共享。attrib_list为int数组 {EGL_CONTEXT_CLIENT_VERSION, 2,EGL10.EGL_NONE };中间的2代表的是OpenGL ES的版本。

    创建EGLSurface: eglCreateWindowSurface(EGLDisplay display, EGLConfig config, Object native_window, int[] attrib_list);参数1、2均为上述步骤得到的结果,参数3为上层创建的用于绘制内容的surface对象,参数4常设置为null。

    设置OpenGL的渲染环境: eglMakeCurrent(EGLDisplay display, EGLSurface draw, EGLSurface read, EGLContext context);该方法的参数意义很明确,该方法在异步线程中被调用,该线程也会被成为GL线程,一旦设定后,所有OpenGL渲染相关的操作都必须放在该线程中执行。

通过上述6步操作,就完成了EGL的初始化设置,便可以进行OpenGL的渲染操作。


6. EGLDisplay、EGLSurface和EGLContext三者的区别与联系?

EGL 是 OpenGL ES 渲染 API 和本地窗口系统(native platform window system)之间的一个中间接口层,它主要由系统制造商实现。为了让OpenGL ES能够绘制在当前设备上,需要EGL作为OpenGL ES与设备的桥梁。以Android系统来举例:




EGLDisplay、EGLSurface和EGLContext分别对应于上图中的Display、Surface和Context,它们分贝对应于不同的底层物理设备和存储方式。

7. Surface、SurfaceView和SurfaceHolder三者的联系

Surface: 每个Surface都是双缓冲,它有一个backBuffer和一个frontBuffer,Surface中创建了Canvas对象,用来管理Surface绘图操作,Canvas对应Bitmap,存储Surface中的内容。

SurfaceView: SurfaceView是视图类View的子类,且实现了Parcelable接口,其中内嵌了一个专门用于绘制的Surface,SurfaceView可以控制这个Surface的格式和尺寸,以及Surface的绘制位置。可以理解为Surface就是管理数据的地方,SurfaceView就是展示数据的地方,而我们就是通过SurfaceView看到的Surface的部分或者全部内容,再换句话说就是Surface是用通过SurfaceView才能展示其中的内容,二者的关系图如下:

SurfaceView和View最本质的区别在于:SurfaceView支持在单独线程中重新绘制画面,而View必须在UI的主线程中更新画面。

SurfaceHolder: SurfaceHolder是一个接口,其作用类似于一个Surace的监听器,提供访问和控制Surface的方法。SurfaceView中调用getHolder方法,可以获得当前SurfaceView中的Surface对应的SurfaceHolder。

从设计模式的角度来看,Surface、SurfaceView和SurfaceHolder实质上就是广为人知的MVC,即Model-View-Controller。Model就是模型的意思,或者说是数据模型,或者更简单地说就是数据,也就是这里的Surface;View即视图,代表用户交互界面,也就是这里的SurfaceView;SurfaceHolder很明显可以理解为MVC中的Controller(控制器)。这样再看三者之间的关系就清楚了很多。


8. SurfaceView、GLSurfaceView、SurfaceTexture、TextureView的区别与联系

SurfaceView是一个有自己Surface的View。它的渲染可以放在单独线程而不是主线程中。其缺点是不能做变形和动画。

GLSurfaceView作为SurfaceView的补充,可以看作是SurfaceView的一种典型使用模式。在SurfaceView的基础上,它加入了EGL的管理,并自带了渲染线程。另外它定义了用户需要实现的Render接口,提供了用Strategy pattern更改具体Render行为的灵活性。

SurfaceTexture可以用作非直接输出的内容流,这样就提供二次处理的机会。与SurfaceView直接输出相比,这样会有若干帧的延迟。同时,由于它本身管理BufferQueue,因此内存消耗也会稍微大一些。

TextureView是一个可以把内容流作为外部纹理输出在上面的View。它本身需要是一个硬件加速层。事实上TextureView本身也包含了SurfaceTexture。它与SurfaceView+SurfaceTexture组合相比可以完成类似的功能(即把内容流上的图像转成纹理,然后输出)。区别在于TextureView是在View hierachy中做绘制,因此一般它是在主线程上做的(在Android 5.0引入渲染线程后,它是在渲染线程中做的)。而SurfaceView+SurfaceTexture在单独的Surface上做绘制,可以是用户提供的线程,而不是系统的主线程或是渲染线程。



阅读625
分享