努力,不是为了要感动谁,也不是要做给哪个人看,而是要让自己随时有能力跳出自己厌恶的圈子,并拥有选择的权利,用自己喜欢的方式过一生!
EGL是什么?
谈到openGL开发我们就不得不说EGL,那EGL是什么呢?请看下图
官方听不懂解释
EGL(Embedded Graphic Library)是渲染API(如OpenGL ES)和本地窗口系统(native platform window system)之间的中间层接口,它主要由系统制造商实现。
EGL是一套OpenGLES、OpenVG等渲染接口和它所运行的平台的底层原生窗口系统之间的桥接接口,也就是帮助OpenGLES或其他Khronos图形API渲染的内容投射到目标窗口的接口。
EGL是一个与平台无关的接口,EGL可以在多种操作系统(如Android和Linux)和本机窗口系统上实现。
通俗类比解释
- OpengGL:他才是真正的画家,他负责创作画作。OpenGL是一个操作GPU的API,你可以认为GPU就是画笔、颜料等,如何妙笔生花就是OpenGL的功劳(哈哈哈,其实要画什么还是人发的指令,这里就不纠结了)。画家只负责饮酒作乐,哦不作画。至于我的画如何向别人展示,如何放到画室亦或是大街上供别人参观那我不管,我请一个公司EGL去负责,下面有请EGL登场。
- EGL:画家(OpenGL)作了好多的画,我得想着怎么才能卖个好价钱:)。目前有Android画室、Linux画室以及windows画室在洽谈。我需要对接画家和画室,把画装裱到不同的画框、保证运输、并将画展示到画室。当然我们公司只提供了一套标准的流程,如何操作还需要Android、Linux画室工作人员来操作,他们需要遵循我们定的流程标准,将画正确无误的摆放到自己的画室供别人浏览参观。
提供了哪些功能?
- 与设备的原生窗口系统通信
- 查询绘图表面的可用类型和配置
- 创建绘图表面
- 在OpenGL ES 和其他图形渲染API之间同步渲染
- 管理纹理贴图等渲染资源
EGL怎么用
聪明的同学发现没有,我们使用GLSurfaceView编写OpenGL代码时好像并没有发现EGL的身影。那是因为GLSurfaceView早已帮我们搭建好了EGL环境,我们只需要编写OpenGL代码即可。如果我们想要更高的扩展性,以及真正了解他的运行机制我们需要自己搭建EGL。
下图请看EGL的主要使用API:
- Display(EGLDisplay) 是对实际显示设备的抽象
- Surface(EGLSurface)是对用来存储图像的内存区域
- FrameBuffer 的抽象,包括 Color Buffer, Stencil Buffer ,Depth Buffer
- Context (EGLContext) 存储 OpenGL ES绘图的一些状态信息
需要说明的是EGL是单线程模型,也就是说EGL环境创建、渲染操作、EGL环境的销毁都必须在同一个线程内完成,否则是无效的。那么我们要么在主线程创建,要么就是创建一个有消息循环的线程,聪明的你应该能想到那就得用HandlerThread或者自定义带Looper的Thread(GLSurfaceView采用该种方式,他是内部起一个while循环)
使用EGL的基本步骤
- 获取OpenGL ES与原生窗口系统的连接:调用eglGetDisplay方法得到EGLDisplay
public static native EGLDisplay eglGetDisplay(
int display_id
);
- 初始化EGL链接:调用 eglInitialize 方法初始化
public static native boolean eglInitialize(
EGLDisplay dpy, // 要进行初始化的EGL连接,即上一步的返回值
int[] major, // 主版本号
int majorOffset, // 主版本号偏移
int[] minor, // 次版本号
int minorOffset // 次版本号偏移
)
- 确定渲染表面的配置信息:调用eglChooseConfig方法得到EGLConfig
指定一组需求,然后再让EGL推荐(eglChooseChofig)最佳配置 - 创建渲染上下文:通过 EGLDisplay 和 EGLConfig ,调用 eglCreateContext 方法创建渲染上下文,得到 EGLContext
public static native EGLContext eglCreateContext(
EGLDisplay dpy,
EGLConfig config,
EGLContext share_context,
int[] attrib_list,
int offset
)
- 创建渲染表面:通过 EGLDisplay 和 EGLConfig ,调用 eglCreateWindowSurface 方法创建渲染表面,得到 EGLSurface
public static EGLSurface eglCreateWindowSurface(
EGLDisplay dpy, // EGLDisplay连接
EGLConfig config, // EGL frame buffer配置,定义了可用于Surface的frame buffer资源
Object win, // Android的窗口,可以是SurfaceView、Surface、SurfaceHolder、SurfaceTexture
int[] attrib_list, // 配置
int offset // 配置偏移
)
- 绑定上下文:将EGL上下文绑定到当前线程,实现渲染环境的设置。通过eglMakeCurrent 方法将EGLSurface、EGLContext、EGLDisplay 三者绑定,接下来就可以使用 OpenGL 进行绘制了
public static native boolean eglMakeCurrent(
EGLDisplay dpy,
EGLSurface draw,
EGLSurface read,
EGLContext ctx
);
- 绘制图像:使用OpenGL的API绘制精美的图像
- 交换缓冲区:当用 OpenGL 绘制结束后,使用 eglSwapBuffers 方法交换前后缓冲,将绘制内容显示到屏幕上
- 释放 EGL 环境 :绘制结束,不再需要使用 EGL 时,取消 eglMakeCurrent 的绑定,销毁 EGLDisplay、EGLSurface、EGLContext
EGL代码实现
创建EGLHelper工具类如下:
package com.android.xz.egldemo.gles;
import android.opengl.EGL14;
import android.opengl.EGLConfig;
import android.opengl.EGLContext;
import android.opengl.EGLDisplay;
import android.opengl.EGLSurface;
import android.util.Log;
/**
* EGL环境搭建类
* <p>
* 1.创建显示屏幕类EGLDisplay
* 2.配置FrameBuffer类EGLConfig
* 3.创建FrameBuffer的EGLSurface
* 4.创建上下文EGLContext,并与Surface绑定
*/
public class EGLHelper {
private static final String TAG = EGLHelper.class.getSimpleName();
/**
* 屏幕显示类,表示一个可以现实的屏幕
*/
private EGLDisplay mEGLDisplay = EGL14.EGL_NO_DISPLAY;
/**
* 系统窗口或FrameBuffer
*/
private EGLSurface mEGLSurface = EGL14.EGL_NO_SURFACE;
/**
* FrameBuffer的配置属性
*/
private EGLConfig mEGLConfig;
/**
* 渲染上下文,用于绑定上面3个属性,将其关联起来
*/
private EGLContext mEGLContext = EGL14.EGL_NO_CONTEXT;
/**
* 初始化EGL环境
*
* @param surface
*/
public void initEGL(Object surface) {
// 1、获取显示设备
mEGLDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
if (mEGLDisplay == EGL14.EGL_NO_DISPLAY) {
throw new RuntimeException("unable to get EGL14 display");
}
// 2、初始化EGL
int[] version = new int[2];
if (!EGL14.eglInitialize(mEGLDisplay, version, 0, version, 1)) {
mEGLDisplay = null;
throw new RuntimeException("unable to initialize EGL14");
}
// 3、资源配置,例如颜色配置等
int[] attribList = {
EGL14.EGL_RED_SIZE, 8,
EGL14.EGL_GREEN_SIZE, 8,
EGL14.EGL_BLUE_SIZE, 8,
EGL14.EGL_ALPHA_SIZE, 8,
//EGL14.EGL_DEPTH_SIZE, 16,
//EGL14.EGL_STENCIL_SIZE, 8,
EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,
EGL14.EGL_NONE, 0, // placeholder for recordable [@-3]
EGL14.EGL_NONE
};
EGLConfig[] configs = new EGLConfig[1];
int[] numConfigs = new int[1];
// 4、ChooseConfig
if (!EGL14.eglChooseConfig(mEGLDisplay, attribList, 0, configs, 0, configs.length,
numConfigs, 0)) {
throw new RuntimeException("unable to find RGB8888 / " + version + " EGLConfig");
}
mEGLConfig = configs[0];
// 5、创建上下文
int[] attrib2_list = {
EGL14.EGL_CONTEXT_CLIENT_VERSION, 2,
EGL14.EGL_NONE
};
EGLContext context = EGL14.eglCreateContext(mEGLDisplay, mEGLConfig, EGL14.EGL_NO_CONTEXT, attrib2_list, 0);
if (context == EGL14.EGL_NO_CONTEXT) {
throw new RuntimeException("eglCreateContext error");
}
mEGLContext = context;
// 6、创建渲染Surface
int[] attrib_list = {
EGL14.EGL_NONE
};
EGLSurface eglSurface = EGL14.eglCreateWindowSurface(mEGLDisplay, mEGLConfig, surface, attrib_list, 0);
if (eglSurface == null) {
throw new RuntimeException("surface was null");
}
mEGLSurface = eglSurface;
// 7、将EGL上下文绑定到当前线程,实现渲染环境的设置,之后就可以使用OpenGL进行绘制了
if (!EGL14.eglMakeCurrent(mEGLDisplay, mEGLSurface, mEGLSurface, mEGLContext)) {
throw new RuntimeException("eglMakeCurrent failed");
}
Log.i(TAG, "egl init success!");
}
/**
* 交换缓冲区
*/
public void swapBuffers() {
if (mEGLDisplay != EGL14.EGL_NO_DISPLAY && mEGLSurface != EGL14.EGL_NO_SURFACE) {
if (!EGL14.eglSwapBuffers(mEGLDisplay, mEGLSurface)) {
throw new RuntimeException("swap buffers error");
}
}
}
/**
* 销毁EGL环境
*/
public void destroyEGL() {
if (mEGLDisplay != EGL14.EGL_NO_DISPLAY) {
EGL14.eglMakeCurrent(mEGLDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE,
EGL14.EGL_NO_CONTEXT);
}
if (mEGLDisplay != EGL14.EGL_NO_DISPLAY && mEGLSurface != EGL14.EGL_NO_SURFACE) {
EGL14.eglDestroySurface(mEGLDisplay, mEGLSurface);
mEGLSurface = EGL14.EGL_NO_SURFACE;
}
if (mEGLDisplay != EGL14.EGL_NO_DISPLAY && mEGLContext != EGL14.EGL_NO_CONTEXT) {
EGL14.eglDestroyContext(mEGLDisplay, mEGLContext);
mEGLContext = EGL14.EGL_NO_CONTEXT;
}
if (mEGLDisplay != EGL14.EGL_NO_DISPLAY) {
EGL14.eglReleaseThread();
EGL14.eglTerminate(mEGLDisplay);
mEGLDisplay = EGL14.EGL_NO_DISPLAY;
}
mEGLConfig = null;
}
}
自定义MySurfaceView创建EGL环境
- surfaceCreated中初始化EGL环境
- surfaceChanged中更新画布大小并重新绘制
- surfaceDestroyed中销毁EGL环境
public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback {
private static final String TAG = MySurfaceView.class.getSimpleName();
private EGLHelper mEGLHelper = new EGLHelper();
public MySurfaceView(Context context) {
super(context);
init(context);
}
public MySurfaceView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public MySurfaceView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
private void init(Context context) {
getHolder().addCallback(this);
}
@Override
public void surfaceCreated(@NonNull SurfaceHolder holder) {
Log.i(TAG, "surfaceCreated.");
mEGLHelper.initEGL(holder);
draw();
mEGLHelper.swapBuffers();
}
@Override
public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {
Log.i(TAG, "surfaceChanged.");
GLES20.glViewport(0, 0, width, height);
draw();
mEGLHelper.swapBuffers();
}
@Override
public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
Log.v(TAG, "surfaceDestroyed.");
mEGLHelper.destroyEGL();
}
/**
* 该方法中编写OpenGL绘制相关代码
*/
private void draw() {
// 蓝色清屏
GLES20.glClearColor(0f, 0f, 1f, 1f);
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
}
}
xlm中加入自定义视图
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.android.xz.egldemo.view.MySurfaceView
android:layout_width="200dp"
android:layout_height="200dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
EGL环境创建好后,我们就可以在draw方法中编写OpenGL代码来绘制各种各样的图像了。EGL环境代码基本都是固定的流程,OpenGL绘制完成后,调用EGL的swapBuffers将OpenGL绘制的内容显示到EGLSurface上也就是设备的屏幕上。
最后
本文简述了OpenGL是如何将绘制的图像显示到设备上的,这离不开EGL的功劳,它通过和OpenGL和设备窗口来回周旋,将一幅幅精美的图案呈现到人们的眼前,给它点个赞。
本文代码已经全部粘贴出来,所以就不放项目地址了:)