EGL 是OpenGL ES和本地窗口系统的接口,不同平台上EGL配置是不一样的,而OpenGL的调用方式是一致的,就是说:OpenGL跨平台就是依赖于EGL接口。
当需要把同一个场景渲染到不同的Surface上时,此时系统GLSurfaceView 往往就不能满足需求,所以需要自己创建EGL环境来实现渲染操作。
安卓中EGL环境的创建有以下步骤:
- 获取Egl实例
- 获取默认的显示设备(就是窗口)
- 初始化默认显示设备
- 设置显示设备的属性
- 从系统中获取对应属性的配置
- 创建EglContext
- 创建渲染的Surface
- 绑定EglContext和Surface到显示设备中
下面用代码加注释的方式来叙述安卓EGL环境搭建步骤,主要是参考GLSurfaceView的源码。
1. 创建EGLHelper类
package com.york.media.opengl.egl;
import android.opengl.EGL14;
import android.view.Surface;
import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.egl.EGLContext;
import javax.microedition.khronos.egl.EGLDisplay;
import javax.microedition.khronos.egl.EGLSurface;
/**
* author : York
* date : 2020/12/17 22:11
* desc : EGL 环境类 EglHelper
*/
public class YEglHelper {
private EGL10 mEgl;//EGL实例
private EGLDisplay mEglDisplay;//默认的显示设备
private EGLContext mEglContext;//EGL上下文
private EGLSurface mEglSurface;//EGLSurface
/**
* 创建 EGL 环境
* @param surface 外部传入的 Surface
* @param eglContext EGL 上下文
*/
public void initEgl(Surface surface, EGLContext eglContext) {
//1.获取 Egl实例
mEgl = (EGL10) EGLContext.getEGL();
//2.获取一个默认的显示设备
mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
if (mEglDisplay==EGL10.EGL_NO_DISPLAY){
throw new RuntimeException("get EGL_DEFAULT_DISPLAY error");
}
//3.初始化默认显示设备
int version[]=new int[2];
if (!mEgl.eglInitialize(mEglDisplay,version)){
throw new RuntimeException("init EGL_DEFAULT_DISPLAY error");
}
//4.设置显示设备的属性
int[] attrs = new int[]{
EGL10.EGL_RED_SIZE, 8,
EGL10.EGL_GREEN_SIZE, 8,
EGL10.EGL_BLUE_SIZE, 8,
EGL10.EGL_ALPHA_SIZE, 8,
EGL10.EGL_DEPTH_SIZE, 8,
EGL10.EGL_STENCIL_SIZE, 8,
EGL10.EGL_RENDERABLE_TYPE, 4,
EGL10.EGL_NONE};
int[] num_config = new int[1];
if (!mEgl.eglChooseConfig(mEglDisplay, attrs, null, 1, num_config)) {
throw new IllegalArgumentException("set eglChooseConfig failed");
}
int numConfigs = num_config[0];
if (numConfigs <= 0) {
throw new IllegalArgumentException("No configs match configSpec");
}
//5.从系统中获取对应属性的配置
EGLConfig[] configs = new EGLConfig[numConfigs];
if (!mEgl.eglChooseConfig(mEglDisplay, attrs, configs, numConfigs, num_config)) {
throw new IllegalArgumentException("eglChooseConfig#2 failed");
}
//6.创建 EglContext
int[] attr_list = {
EGL14.EGL_CONTEXT_CLIENT_VERSION, 2,
EGL10.EGL_NONE
};
if (eglContext != null) {
mEglContext = mEgl.eglCreateContext(mEglDisplay, configs[0], eglContext, attr_list);
} else {
mEglContext = mEgl.eglCreateContext(mEglDisplay, configs[0], EGL10.EGL_NO_CONTEXT, attr_list);
}
//7.创建渲染的 Surface,最后一个参数传空
mEglSurface = mEgl.eglCreateWindowSurface(mEglDisplay, configs[0], surface, null);
//8、绑定 EglContext和 Surface到显示设备中
if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
throw new RuntimeException("eglMakeCurrent failed");
}
}
/**
* 刷新数据,进行显示
* @return 是否成功
*/
public boolean swapBuffers() {
if (mEgl != null) {
return mEgl.eglSwapBuffers(mEglDisplay, mEglSurface);
} else {
throw new RuntimeException("mEgl is null");
}
}
/**
* 从外部获取 EglContext,OpenGL整体是一个状态机,通过改变状态就能改变后续的渲染方式,而
* EGLContext(EgL上下文)就保存有所有状态,因此可以通过共享 EGLContext
* 来实现同一场景渲染到不同的 Surface上。
* @return 返回 EglContext
*/
public EGLContext getEglContext() {
return mEglContext;
}
/**
* 销毁
*/
public void destroyEgl() {
if (mEgl != null) {
mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE,
EGL10.EGL_NO_SURFACE,
EGL10.EGL_NO_CONTEXT);
mEgl.eglDestroySurface(mEglDisplay, mEglSurface);
mEglSurface = null;
mEgl.eglDestroyContext(mEglDisplay, mEglContext);
mEglContext = null;
mEgl.eglTerminate(mEglDisplay);
mEglDisplay = null;
mEgl = null;
}
}
}
2. 自定义 GLSurfaceView
自定义 GLSurfaceView 继成 SurfaceView,并实现其CallBack回调。
package com.york.media.opengl.egl;
import android.content.Context;
import android.opengl.GLSurfaceView;
import android.util.AttributeSet;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import java.lang.ref.WeakReference;
import javax.microedition.khronos.egl.EGLContext;
/**
* author : York
* date : 2020/12/17 21:57
* desc : 自定义的 GLSurfaceView 继成了 SurfaceView,并实现其CallBack回调
*/
public class YGLSurfaceView extends SurfaceView implements SurfaceHolder.Callback {
private Surface surface;
private EGLContext eglContext;
private YEGLThread yEGLThread;
private YGLRender yGLRender;
public final static int RENDERMODE_WHEN_DIRTY = 0;//手动刷新
public final static int RENDERMODE_CONTINUOUSLY = 1;//自动刷新
private int mRenderMode = RENDERMODE_CONTINUOUSLY;
public YGLSurfaceView(Context context) {
this(context, null);
}
public YGLSurfaceView(Context context, AttributeSet attrs) {
super(context, attrs);
getHolder().addCallback(this);
}
public void setRender(YGLRender yRender) {
this.yGLRender = yRender;
}
public void setRenderMode(int mRenderMode) {
if (yGLRender == null) {
throw new RuntimeException("must set render before set RenderMode");
}
this.mRenderMode = mRenderMode;
}
//添加设置Surface和EglContext的方法
public void setSurfaceAndEglContext(Surface surface, EGLContext eglContext) {
this.surface = surface;
this.eglContext = eglContext;
}
public EGLContext getEglContext() {
if (yEGLThread != null) {
return yEGLThread.getEglContext();
}
return null;
}
public void requestRender() {
if (yEGLThread != null) {
yEGLThread.requestRender();
}
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
if (surface == null) {
surface = holder.getSurface();
}
yEGLThread = new YEGLThread(new WeakReference<YGLSurfaceView>(this));
yEGLThread.isCreate = true;
yEGLThread.start();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
yEGLThread.width = width;
yEGLThread.height = height;
yEGLThread.isChange = true;
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
yEGLThread.onDestroy();
yEGLThread = null;
surface = null;
eglContext = null;
}
public interface YGLRender {
void onSurfaceCreated();
void onSurfaceChanged(int width, int height);
void onDrawFrame();
}
/**
* 主要是在Thread中实现Render的三个回调
*/
static class YEGLThread extends Thread {
private WeakReference<YGLSurfaceView> yGlSurfaceViewWeakReference;
private YEglHelper eglHelper = null;
private Object object = null;//主要是做阻塞用
private boolean isExit = false;
private boolean isCreate = false;
private boolean isChange = false;
private boolean isStart = false;
private int width;
private int height;
public YEGLThread(WeakReference<YGLSurfaceView> yglSurfaceViewWeakReference) {
this.yGlSurfaceViewWeakReference = yglSurfaceViewWeakReference;
}
@Override
public void run() {
super.run();
isExit = false;
isStart = false;
object = new Object();
eglHelper = new YEglHelper();
eglHelper.initEgl(yGlSurfaceViewWeakReference.get().surface, yGlSurfaceViewWeakReference.get().eglContext);
while (true) {
if (isExit) {
//释放资源
release();
break;
}
if (isStart) {
if (yGlSurfaceViewWeakReference.get().mRenderMode == RENDERMODE_WHEN_DIRTY) {
synchronized (object) {
try {
object.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} else if (yGlSurfaceViewWeakReference.get().mRenderMode == RENDERMODE_CONTINUOUSLY) {
try {
Thread.sleep(1000 / 100);
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
throw new RuntimeException("mRenderMode is wrong value");
}
}
onCreate();
onChange(width, height);
onDraw();
isStart = true;
}
}
private void onCreate() {
if (isCreate && yGlSurfaceViewWeakReference.get().yGLRender != null) {
isCreate = false;
yGlSurfaceViewWeakReference.get().yGLRender.onSurfaceCreated();
}
}
private void onChange(int width, int height) {
if (isChange && yGlSurfaceViewWeakReference.get().yGLRender != null) {
isChange = false;
yGlSurfaceViewWeakReference.get().yGLRender.onSurfaceChanged(width, height);
}
}
private void onDraw() {
if (yGlSurfaceViewWeakReference.get().yGLRender != null && eglHelper != null) {
yGlSurfaceViewWeakReference.get().yGLRender.onDrawFrame();
if (!isStart) {
yGlSurfaceViewWeakReference.get().yGLRender.onDrawFrame();
}
eglHelper.swapBuffers();
}
}
private void requestRender() {
if (object != null) {
synchronized (object) {
object.notifyAll();
}
}
}
public void onDestroy() {
isExit = true;
requestRender();
}
public void release() {
if (eglHelper != null) {
eglHelper.destroyEgl();
eglHelper = null;
object = null;
yGlSurfaceViewWeakReference = null;
}
}
public EGLContext getEglContext() {
if (eglHelper != null) {
return eglHelper.getEglContext();
}
return null;
}
}
}
这样EGL环境就搭建好了,现在使用自定义的OpenGL环境,来绘制一屏幕红色,代码如下:
package com.york.media.opengl;
import androidx.appcompat.app.AppCompatActivity;
import android.opengl.GLES20;
import android.os.Bundle;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import com.york.media.opengl.egl.YEglHelper;
public class MainActivity extends AppCompatActivity {
private SurfaceView mYGLSurfaceView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mYGLSurfaceView = findViewById(R.id.mYGLSurfaceView);
mYGLSurfaceView.getHolder().addCallback(new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
}
@Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int format, int width, int height) {
new Thread() {
@Override
public void run() {
super.run();
YEglHelper eglHelper = new YEglHelper();
eglHelper.initEgl(surfaceHolder.getSurface(), null);
while (true) {
GLES20.glViewport(0, 0, width, height);
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
GLES20.glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
eglHelper.swapBuffers();
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
}
@Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
}
});
}
}
效果如图:
![](https://img-blog.csdnimg.cn/51b7c00bfd5440129666b0646ce5c680.jpeg)