EGL是OpenGL ES和本地窗口系统的接口,不同平台上EGL配置是不一样的,而OpenGL的调用方式是一致的,也就是说OpenGL的跨平台特性依赖于EGL接口。
当我们需要把同一个场景渲染到不同的Surface上时,此时系统GLSurfaceView就不能满足需求了,所以我们需要自己创建EGL环境来实现渲染操作。
OpenGL整体是一个状态机,通过改变状态就能改变后续的渲染方式,而EGLContext(EgL上下文)就保存有所有状态,因此可以通过共享EGLContext来实现同一场景渲染到不同的Surface上。
1、得到Egl实例:
2、得到默认的显示设备(就是窗口)
3、初始化默认显示设备
4、设置显示设备的属性
5、从系统中获取对应属性的配置
6、创建EglContext
7、创建渲染的Surface
8、绑定EglContext和Surface到显示设备中
9、刷新数据,显示渲染场景
1、继承SurfaceView,并实现其CallBack回调
2、自定义GLThread线程类,主要用于OpenGL的绘制操作
3、添加设置Surface和EglContext的方法(例如需要设置MediaCodec的Surface)
4、提供和系统GLSurfaceView相同的调用方法
package com.zhangyu.myopengl.egl;
import android.content.Context;
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;
public abstract class EGLSurfaceView extends SurfaceView implements SurfaceHolder.Callback {
private static final String TAG = "MyGlSurfaceView";
//surface可以从外面传递进去
private Surface surface;
//egl上下文
private EGLContext eglContext;
//
private EGLThread eglThread;
//
private EGLRender eglRender;
//渲染模式,手动刷新,自动刷新,要给一个默认值,否则都没有走,默认是自动刷新60帧
private RenderMode renderMode = RenderMode.RENDERMODE_CONTINUOUSLY;
/**
* 设置render mode
* 0 手动刷新
* 1 自动刷新
*/
public enum RenderMode {
RENDERMODE_WHEN_DIRTY,
RENDERMODE_CONTINUOUSLY
}
public void setRender(EGLRender eglRender) {
this.eglRender = eglRender;
}
public void setRenderMode(RenderMode renderMode) {
this.renderMode = renderMode;
}
public EGLSurfaceView(Context context) {
this(context, null);
}
public EGLSurfaceView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public EGLSurfaceView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
getHolder().addCallback(this);
}
public void setSurfaceAndEglContext(Surface surface, EGLContext eglContext) {
this.surface = surface;
this.eglContext = eglContext;
}
public EGLContext getEglContext() {
if (eglThread != null) {
return eglThread.getEglContext();
}
return null;
}
public void requestRender() {
if (eglThread != null) {
eglThread.requestRender();
}
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
if (surface == null) {
surface = holder.getSurface();
}
eglThread = new EGLThread(new WeakReference<EGLSurfaceView>(this));
eglThread.isCreate = true;
eglThread.start();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
eglThread.width = width;
eglThread.height = height;
eglThread.isChange = true;
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
eglThread.onDestory();
eglThread = null;
surface = null;
eglContext = null;
}
public interface EGLRender {
void onSurfaceCreated();
void onSurfaceChanged(int width, int height);
void onDrawFrame();
}
static class EGLThread extends Thread {
private WeakReference<EGLSurfaceView> myGlSurfaceViewWeakReference;
private EGLHelper eglHelper = null;
private boolean isExit = false;
private boolean isCreate = false;
private boolean isChange = false;
private boolean isStart = false;
//用来控制手动刷新
private Object object;
private int width;
private int height;
public EGLThread(WeakReference<EGLSurfaceView> myGlSurfaceViewWeakReference) {
this.myGlSurfaceViewWeakReference = myGlSurfaceViewWeakReference;
}
@Override
public void run() {
super.run();
isExit = false;
isStart = false;
object = new Object();
eglHelper = new EGLHelper();
eglHelper.initEgl(myGlSurfaceViewWeakReference.get().surface, myGlSurfaceViewWeakReference.get().eglContext);
while (true) {
if (isExit) {
// 释放资源
release();
break;
}
/**
* 刷新模式
*/
if (isStart) {
if (myGlSurfaceViewWeakReference.get().renderMode == RenderMode.RENDERMODE_WHEN_DIRTY) {
synchronized (object) {
try {
object.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} else if (myGlSurfaceViewWeakReference.get().renderMode == RenderMode.RENDERMODE_CONTINUOUSLY) {
//自动刷新,每秒60帧
try {
Thread.sleep(1000 / 60);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
onCreate();
onChange(width, height);
onDraw();
isStart = true;
}
}
/**
* 创建,执行一次
*/
private void onCreate() {
if (isCreate && myGlSurfaceViewWeakReference.get().eglRender != null) {
isCreate = false;
myGlSurfaceViewWeakReference.get().eglRender.onSurfaceCreated();
}
}
/**
* 改变,执行一次
*
* @param width
* @param height
*/
private void onChange(int width, int height) {
if (isChange && myGlSurfaceViewWeakReference.get().eglRender != null) {
isChange = false;
myGlSurfaceViewWeakReference.get().eglRender.onSurfaceChanged(width, height);
}
}
/**
* 绘制,每次循环里都要执行
*/
private void onDraw() {
if (myGlSurfaceViewWeakReference.get().eglRender != null && eglHelper != null) {
myGlSurfaceViewWeakReference.get().eglRender.onDrawFrame();
//第一次刷新的时候,需要刷新两次
if (!isStart) {
myGlSurfaceViewWeakReference.get().eglRender.onDrawFrame();
}
eglHelper.swapBuffers();
}
}
/**
* 手动刷新
* 解除掉线程里的阻塞等待
*/
private void requestRender() {
if (object != null) {
synchronized (object) {
object.notifyAll();
}
}
}
public void onDestory() {
isExit = true;
requestRender();
}
public void release() {
if (eglHelper != null) {
eglHelper.destoryEgl();
eglHelper = null;
object = null;
myGlSurfaceViewWeakReference = null;
}
}
public EGLContext getEglContext() {
if (eglHelper != null) {
return eglHelper.getmEglContext();
}
return null;
}
}
}
package com.zhangyu.myopengl.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;
/**
* 1、得到Egl实例:
* 2、得到默认的显示设备(就是窗口)
* 3、初始化默认显示设备
* 4、设置显示设备的属性
* 5、从系统中获取对应属性的配置
* 6、创建EglContext
* 7、创建渲染的Surface
* 8、绑定EglContext和Surface到显示设备中
* 9、刷新数据,显示渲染场景
*/
public class EGLHelper {
private EGL10 mEgl;
private EGLDisplay mEglDisplay;
private EGLContext mEglContext;
private EGLSurface mEglSurface;
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("eglGetDisplay failed");
}
//3、初始化默认显示设备
int[] version = new int[2];
if (!mEgl.eglInitialize(mEglDisplay, version)) {
throw new RuntimeException("eglInitialize failed");
}
//4、设置显示设备的属性
int[] attrbutes = 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, attrbutes, null, 1, num_config)) {
throw new IllegalArgumentException("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, attrbutes, configs, numConfigs,
num_config)) {
throw new IllegalArgumentException("eglChooseConfig#2 failed");
}
//6、创建EglContext
int[] attrib_list = {
EGL14.EGL_CONTEXT_CLIENT_VERSION, 2,
EGL10.EGL_NONE
};
if (eglContext != null) {
mEglContext = mEgl.eglCreateContext(mEglDisplay, configs[0], eglContext, attrib_list);
} else {
mEglContext = mEgl.eglCreateContext(mEglDisplay, configs[0], EGL10.EGL_NO_CONTEXT, attrib_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 fail");
}
}
/**
* 交换缓冲区
* 手动刷新
*
* @return
*/
public boolean swapBuffers() {
if (mEgl != null) {
return mEgl.eglSwapBuffers(mEglDisplay, mEglSurface);
} else {
throw new RuntimeException("egl is null");
}
}
public EGLContext getmEglContext() {
return mEglContext;
}
public void destoryEgl() {
if (mEgl != null) {
//解绑
mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE,
EGL10.EGL_NO_SURFACE,
EGL10.EGL_NO_CONTEXT);
//Surface置空
mEgl.eglDestroySurface(mEglDisplay, mEglSurface);
mEglSurface = null;
//Context置空
mEgl.eglDestroyContext(mEglDisplay, mEglContext);
mEglContext = null;
//停用显示设备
mEgl.eglTerminate(mEglDisplay);
mEglDisplay = null;
mEgl = null;
}
}
}
package com.zhangyu.myopengl.egl;
import android.content.Context;
import android.opengl.GLES20;
import android.util.Log;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
public class ShaderUtil {
private static final String TAG = "ShaderUtil";
/**
* 读取shader
*
* @param context
* @param rawId
* @return
*/
public static String readRawTxt(Context context, int rawId) {
InputStream inputStream = context.getResources().openRawResource(rawId);
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
StringBuffer sb = new StringBuffer();
String line;
try {
while ((line = reader.readLine()) != null) {
sb.append(line).append("\n");
}
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
return sb.toString();
}
/**
* 加载shader
*
* @param shaderType
* @param source
* @return
*/
private static int loadShader(int shaderType, String source) {
//创建shader
int shader = GLES20.glCreateShader(shaderType);
if (shader != 0) {
//关联source
GLES20.glShaderSource(shader, source);
//编译
GLES20.glCompileShader(shader);
//获取编译状态
int[] compile = new int[1];
GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compile, 0);
if (compile[0] != GLES20.GL_TRUE) {
//失败
Log.e(TAG, "loadShader: shader compile error");
GLES20.glDeleteShader(shader);
shader = 0;
}
}
return shader;
}
/**
* 生成程序
*
* @param vertexSource
* @param fragmentSource
* @return
*/
public static int createProgram(String vertexSource, String fragmentSource) {
int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);
if (vertexShader == 0) {
return 0;
}
int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);
if (fragmentShader == 0) {
return 0;
}
//创建
int program = GLES20.glCreateProgram();
if (program != 0) {
//附加着色器
GLES20.glAttachShader(program, vertexShader);
GLES20.glAttachShader(program, fragmentShader);
//连接
GLES20.glLinkProgram(program);
//检查连接是否成功
int[] linkStatus = new int[1];
GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
if (linkStatus[0] != GLES20.GL_TRUE) {
//出错
Log.e(TAG, "createProgram: link program error");
GLES20.glDeleteProgram(program);
program = 0;
}
}
return program;
}
}
package com.zhangyu.myopengl.egl;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLES20;
import android.opengl.GLUtils;
import android.util.Log;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
public class EGLUtils {
private static final String TAG = "ShaderUtil";
/**
* 读取shader
*
* @param context
* @param rawId
* @return
*/
public static String readRawTxt(Context context, int rawId) {
InputStream inputStream = context.getResources().openRawResource(rawId);
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
StringBuffer sb = new StringBuffer();
String line;
try {
while ((line = reader.readLine()) != null) {
sb.append(line).append("\n");
}
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
return sb.toString();
}
/**
* 加载shader
*
* @param shaderType
* @param source
* @return
*/
private static int loadShader(int shaderType, String source) {
//创建shader
int shader = GLES20.glCreateShader(shaderType);
if (shader != 0) {
//关联source
GLES20.glShaderSource(shader, source);
//编译
GLES20.glCompileShader(shader);
//获取编译状态
int[] compile = new int[1];
GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compile, 0);
if (compile[0] != GLES20.GL_TRUE) {
//失败
Log.e(TAG, "loadShader: shader compile error");
GLES20.glDeleteShader(shader);
shader = 0;
}
}
return shader;
}
/**
* 生成程序
*
* @param vertexSource
* @param fragmentSource
* @return
*/
public static int createProgram(String vertexSource, String fragmentSource) {
int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);
if (vertexShader == 0) {
return 0;
}
int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);
if (fragmentShader == 0) {
return 0;
}
//创建
int program = GLES20.glCreateProgram();
if (program != 0) {
//附加着色器
GLES20.glAttachShader(program, vertexShader);
GLES20.glAttachShader(program, fragmentShader);
//连接
GLES20.glLinkProgram(program);
//检查连接是否成功
int[] linkStatus = new int[1];
GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
if (linkStatus[0] != GLES20.GL_TRUE) {
//出错
Log.e(TAG, "createProgram: link program error");
GLES20.glDeleteProgram(program);
program = 0;
}
}
return program;
}
/**
* 创建一个图片纹理
*
* @param imgSrc
* @return
*/
public static int createImageTextureId(Context context, int imgSrc) {
//创建纹理
int[] textureIds = new int[1];
GLES20.glGenTextures(1, textureIds, 0);
int textureId = textureIds[0];
//绑定纹理
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId);
//环绕过滤
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_REPEAT);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_REPEAT);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
//生成图片
Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), imgSrc);
//图片绑定到纹理上
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
//解绑纹理
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
return textureId;
}
}