当我们需要把同一个场景渲染到不同的Surface
上时,此时系统GLSurfaceView
就不能满足需求了,所以我们需要自己创建EGL环境来实现渲染操作。
注意: OpenGL
整体是一个状态机,通过改变状态就能改变后续的渲染方式,而 EGLContext(EgL上下文)
就保存有所有状态,因此可以通过共享EGLContext
来实现同一场景渲染到不同的Surface
上。
我们通过分析GLSurfaceView
来实现自己的代码
首先需要配置EGL环境(EGLHelper
):Android配置EGL环境
EGL环境配置成功后,定义GLSurfaceView主要为以下步骤:
1、继成SurfaceView,并实现其CallBack回调
2、自定义GLThread线程类,主要用于OpenGL的绘制操作
3、添加设置Surface和EglContext的方法
4、提供和系统GLSurfaceView相同的调用方法
EglSurfaceView.java
package com.zzw.glsurfaceviewdemo;
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 class EglSurfaceView extends SurfaceView implements SurfaceHolder.Callback {
private Renderer mRenderer;
private EGLThread mEGLThread;
private Surface mSurface;
private EGLContext mEglContext;
public final static int RENDERMODE_WHEN_DIRTY = 0;
public final static int RENDERMODE_CONTINUOUSLY = 1;
private int mRenderMode = RENDERMODE_CONTINUOUSLY;
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);
init();
}
private void init() {
SurfaceHolder holder = getHolder();
holder.addCallback(this);
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
if (mSurface == null) {
mSurface = holder.getSurface();
}
mEGLThread = new EGLThread(new WeakReference<>(this));
mEGLThread.isCreate = true;
mEGLThread.start();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
mEGLThread.width = width;
mEGLThread.height = height;
mEGLThread.isChange = true;
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
mEGLThread.onDestroy();
mEGLThread = null;
mSurface = null;
mEglContext = null;
}
public void setRenderer(Renderer mRenderer) {
this.mRenderer = mRenderer;
}
public void setRenderMode(int renderMode) {
if (mRenderer == null) {
throw new RuntimeException("must set render before");
}
this.mRenderMode =renderMode;
}
public void requestRender() {
if (mEGLThread != null) {
mEGLThread.requestRender();
}
}
public void setSurfaceAndEglContext(Surface surface, EGLContext eglContext) {
this.mSurface = surface;
this.mEglContext = eglContext;
}
public EGLContext getEglContext() {
if (mEGLThread != null) {
return mEGLThread.getEglContext();
}
return null;
}
private static class EGLThread extends Thread {
EGLThread(WeakReference eGLSurfaceViewWeakRef) {
this.mEGLSurfaceViewWeakRef = eGLSurfaceViewWeakRef;
}
@Override
public void run() {
super.run();
try {
guardedRun();
} catch (Exception e) {
// fall thru and exit normally
}
}
private void guardedRun() throws InterruptedException {
isExit = false;
isStart = false;
object = new Object();
mEglHelper = new EglHelper();
mEglHelper.initEgl(mEGLSurfaceViewWeakRef.get().mSurface, mEGLSurfaceViewWeakRef.get().mEglContext);
while (true) {
if (isExit) {
//释放资源
release();
break;
}
if (isStart) {
if (mEGLSurfaceViewWeakRef.get().mRenderMode == RENDERMODE_WHEN_DIRTY) {
synchronized (object) {
object.wait();
}
} else if (mEGLSurfaceViewWeakRef.get().mRenderMode == RENDERMODE_CONTINUOUSLY) {
Thread.sleep(1000 / 60);
} else {
throw new IllegalArgumentException("renderMode");
}
}
onCreate();
onChange(width, height);
onDraw();
isStart = true;
}
}
private void onCreate() {
if (!isCreate || mEGLSurfaceViewWeakRef.get().mRenderer == null)
return;
isCreate = false;
mEGLSurfaceViewWeakRef.get().mRenderer.onSurfaceCreated();
}
private void onChange(int width, int height) {
if (!isChange || mEGLSurfaceViewWeakRef.get().mRenderer == null)
return;
isChange = false;
mEGLSurfaceViewWeakRef.get().mRenderer.onSurfaceChanged(width, height);
}
private void onDraw() {
if (mEGLSurfaceViewWeakRef.get().mRenderer == null)
return;
mEGLSurfaceViewWeakRef.get().mRenderer.onDrawFrame();
//第一次的时候手动调用一次 不然不会显示ui
if (!isStart) {
mEGLSurfaceViewWeakRef.get().mRenderer.onDrawFrame();
}
mEglHelper.swapBuffers();
}
void requestRender() {
if (object != null) {
synchronized (object) {
object.notifyAll();
}
}
}
void onDestroy() {
isExit = true;
//释放锁
requestRender();
}
void release() {
if (mEglHelper != null) {
mEglHelper.destoryEgl();
mEglHelper = null;
object = null;
mEGLSurfaceViewWeakRef = null;
}
}
EGLContext getEglContext() {
if (mEglHelper != null) {
return mEglHelper.getEglContext();
}
return null;
}
private WeakReference mEGLSurfaceViewWeakRef;
private EglHelper mEglHelper;
private int width;
private int height;
private boolean isCreate;
private boolean isChange;
private boolean isStart;
private boolean isExit;
private Object object;
}
interface Renderer {
void onSurfaceCreated();
void onSurfaceChanged(int width, int height);
void onDrawFrame();
}
}
使用和正常的GLSurfaceView一样:
package com.zzw.glsurfaceviewdemo;
import android.opengl.GLES20;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
public class MainActivity extends AppCompatActivity implements EglSurfaceView.Renderer {
private EglSurfaceView eglSurfaceView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
eglSurfaceView = findViewById(R.id.egl_surface_view);
eglSurfaceView = new EglSurfaceView(this);
eglSurfaceView.setRenderer(this);
// eglSurfaceView.setRenderMode(EglSurfaceView.RENDERMODE_CONTINUOUSLY);
eglSurfaceView.setRenderMode(EglSurfaceView.RENDERMODE_WHEN_DIRTY);
eglSurfaceView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
eglSurfaceView.requestRender();
}
});
setContentView(eglSurfaceView);
}
@Override
public void onSurfaceCreated() {
Log.e("zzz", "onSurfaceCreated");
}
@Override
public void onSurfaceChanged(int width, int height) {
Log.e("zzz", "onSurfaceChanged");
GLES20.glViewport(0, 0, width, height);
}
@Override
public void onDrawFrame() {
Log.e("zzz", "onDrawFrame");
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
GLES20.glClearColor(1.0f, 0.0f, 1.0f, 1.0f);
}
}