一. 简单介绍
GLSurfaceView继承自SurfaceView,实现了SurfaceHolder.Callback2接口, 其实是对SurfaceView再做了一次封装【加入了EGL的管理】,方便我们在安卓中使用OpenGL
下面是AndroidDeveloper中介绍GLSurfaceView的特性翻译【仅供参考】
1> 提供并且管理一个独立的Surface。
2> 提供并且管理一个EGL display,它能让opengl把内容渲染到上述的Surface上。
3> 支持用户自定义渲染器(Render),通过setRenderer设置一个自定义的Renderer。
4> 让渲染器在独立的GLThread线程里运作,和UI线程分离。
5> 支持按需渲染(on-demand)和连续渲染(continuous)两种模式。
6> 另外还针对OpenGL调用进行追踪和错误检查。
另外:
GPU加速:GLSurfaceView的效率是SurfaceView的30倍以上,SurfaceView使用画布进行绘制,GLSurfaceView利用GPU加速提高了绘制效率。
View的绘制onDraw(Canvas canvas)使用Skia渲染引擎渲染,而GLSurfaceView的渲染器Renderer的onDrawFrame(GL10 gl)使用opengl绘制引擎进行渲染。
OpenGL ES :一个嵌入式的(2D/3D)图形处理库
EGL环境:是OpenGL es和本地窗口系统【比如说:在Android上就是安卓的窗口, IOS上就是苹果的窗口,等等】的接口,不同平台上EGL配置是不一样的,而OpenGL的调用方式是一致的,就是说:OpenGL跨平台就是依赖于EGL接口。
我们还应该了解的:
Android里面系统已经提供了GLSurfaceView,已经有了EGL环境,我们为什么还要自己搭建这个环境呢【搭建属于我们自己的EGL】?
答:当我们需要把同一个场景渲染到不同的Surface上时,此时系统GLSurfaceView就不能满足需求了,所以我们需要自己创建EGL环境来实现渲染操作。注意: OpenGL整体是一个状态机,通过改变状态就能改变后续的渲染方式,而EGLContext(Egl上下文)就保存有所有状态,因此可以通过共享EGLContext 来实现同一场景渲染到不同的Surface上。
二.Android中使用GLSurfaceView大体分三步(例子放到文末github的项目中)
(1). 继承GLSurfaceView
(2). 实现接口 GLSurfaceView.Renderer 重写以下三个方法
void onSurfaceCreated(GL10 gl, EGLConfig config);
void onSurfaceChanged(GL10 gl, int width, int height);
void onDrawFrame(GL10 gl);
(3). 编写glsl脚本(shader)
三.GLSurfaceView工作过程简单分析
public class AnGLSurfaceView extends GLSurfaceView {
public AnGLSurfaceView(Context context) {
this(context, null);
}
public AnGLSurfaceView(Context context, AttributeSet attrs) {
super(context, attrs);
// AnRender implements GLSurfaceView.Renderer
AnRender render = new AnRender();
setRenderer(render); //分析入口
}
}
public void setRenderer(Renderer renderer) {
........
mRenderer = renderer; //这就是我们传进来的renderer
// private final WeakReference<GLSurfaceView> mThisWeakRef = new WeakReference<GLSurfaceView>(this); 持有GLSurfaceView弱引用
mGLThread = new GLThread(mThisWeakRef); //开启一个GL线程
mGLThread.start();
}
public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback2 {
...
private Renderer mRenderer //我们传进来的Renderer
...
//TODO: 它的一个静态内部类 GLThread 线程持有GLSurfaceView弱引用
static class GLThread extends Thread {
...
@Override
public void run() {
...
guardedRun();
...
}
...
private void guardedRun() throws InterruptedException {
//EglHelper是创建OpenGL 的 Egl环境的,这个类很有用。
//注意:下面的while循环时, 这个Egl环境必须准备好。
mEglHelper = new EglHelper(mGLSurfaceViewWeakRef);
...
while(true){ //绘制它是一个不间断的动作,所以用了死循环。
if (createEglContext) {//变量createEglContext=true表示EGL环境已经准备好了。
...
GLSurfaceView view = mGLSurfaceViewWeakRef.get();
...
//这里就回调到我们自定义Renderer的放中去了。
view.mRenderer.onSurfaceCreated(gl, mEglHelper.mEglConfig); 【mRenderer这个是我们自己创建传进来的Render】
...
createEglContext = false; //【注意这里把createEglContext 赋值为false了】
}
if (sizeChanged) {//变量sizeChanged = true 表示尺寸发生了变化。
...
GLSurfaceView view = mGLSurfaceViewWeakRef.get();
...
view.mRenderer.onSurfaceChanged(gl, w, h);
}
if (LOG_RENDERER_DRAW_FRAME) {
Log.w("GLThread", "onDrawFrame tid=" + getId());
}else{
...
GLSurfaceView view = mGLSurfaceViewWeakRef.get();
view.mRenderer.onDrawFrame(gl); //一直执行
...
}
//显示当前渲染面。
int swapError = mEglHelper.swap();
switch (swapError) { //来自eglSwapBuffers的EGL错误代码。
case EGL10.EGL_SUCCESS:
break;
case EGL11.EGL_CONTEXT_LOST:
break;
default:
break;
}
}
...
}
...
}
...
简单总结:
GLThread:OpenGL ES的运行线程,包含创建EGL环境,调用GLRender的 onSurfaceCreated, onSurfaceChanged和onDrawFrame方法以及生命周期的管理。
EglHelper:负责创建EGL环境。
GLSurfaceView:负责提供Surface和状态改变。
四. 做一个我们自己的EglHelper
我们模仿Android中GLSurfaceView里面的EglHelper来做一个自己的MyEglHelper方便未来我们使用GLSurfaceView, OpenGL这一块的开发。
大体分为九个步骤,我直接贴出代码。
import android.view.Surface;
import javax.microedition.khronos.egl.EGL;
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;
public class MyEglHelper {
private EGL10 mEgl;
private EGLDisplay mEglDisplay;
private EGLContext mEglContext;
private EGLSurface mEglSurface;
private 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("My eglGetDisplay failed...");
}
//3.初始化默认显示设备
int[] version = new int[2]; //主版本号,次版本号
if (!mEgl.eglInitialize(mEglDisplay, version)) {
throw new RuntimeException("My eglInitialize failed...");
}
//4.设置显示设备的属性
int[] attribes = 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, //深度 3D相关的
EGL10.EGL_STENCIL_SIZE, 8, //模板
EGL10.EGL_RENDERABLE_TYPE, 4, //这个是安卓规定的 用opengl2.0这个版本
EGL10.EGL_NONE //到这个NONE 他就知道结尾了
};
int[] num_config = new int[1];
if (!mEgl.eglChooseConfig(mEglDisplay, attribes, null, 1, num_config)) {
throw new IllegalArgumentException("My eglChooseConfig failed...");
}
int numConfigs = num_config[0];
if (numConfigs <= 0) {
throw new IllegalArgumentException("My No configs match configSpec...");
}
//5.从系统中获取对应属性的配置
EGLConfig[] configs = new EGLConfig[numConfigs];
if (!mEgl.eglChooseConfig(mEglDisplay, attribes, configs, numConfigs, num_config)) {
throw new IllegalArgumentException("My eglChooseConfig#2 failed...");
}
//6. 创建EglContext
if (eglContext != null) {
mEglContext = mEgl.eglCreateContext(mEglDisplay, configs[0], eglContext, null);
} else {
mEglContext = mEgl.eglCreateContext(mEglDisplay, configs[0], EGL10.EGL_NO_CONTEXT, null);
}
//7.创建渲染的Surface
mEglSurface = mEgl.eglCreateWindowSurface(mEglDisplay, configs[0], surface, null);
//8.绑定EglContext 和 Surface到显示设备中
if(!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)){
throw new IllegalArgumentException("My eglMakeCurrent failed...");
}
//9.刷新数据,显示渲染场景
}
/**
* 刷新数据,显示渲染场景
* @return
*/
public boolean swapBuffers(){
if (mEgl != null){
return mEgl.eglSwapBuffers(mEglDisplay, mEglSurface);
}else {
throw new IllegalArgumentException("My eglSwapBuffers failed...");
}
}
/**
* 释放资源
*/
public void destroyEgl(){
if(mEgl != null){
mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
mEgl.eglDestroyContext(mEglDisplay, mEglContext);
mEglContext = null;
mEgl.eglDestroySurface(mEglDisplay,mEglSurface);
mEglSurface = null;
mEgl.eglTerminate(mEglDisplay);
mEglDisplay = null;
mEgl = null;
}
}
public EGLContext getmEglContext() {
return mEglContext;
}
}
五. 自定义一个我们自己的GLSurfaceView
public abstract class BaseSurfaceView extends SurfaceView implements SurfaceHolder.Callback {
private Surface surface;
private EGLContext eglContext;
private GLThread glThread;
private GLRender glRender;
public final static int RENDERMODE_WHEN_DIRTY = 0;
public final static int RENDERMODE_CONTINUOUSLY = 1;
private int mRenderMode = RENDERMODE_CONTINUOUSLY;
public BaseSurfaceView(Context context) {
this(context, null);
}
public BaseSurfaceView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public BaseSurfaceView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
getHolder().addCallback(this);
}
public void setRender(GLRender GLRender) {
this.glRender = GLRender;
}
public void setRenderMode(int mRenderMode) {
if (glRender == null) {
throw new RuntimeException("must set render before");
}
this.mRenderMode = mRenderMode;
}
public void setSurfaceAndEglContext(Surface surface, EGLContext eglContext) {
this.surface = surface;
this.eglContext = eglContext;
}
public EGLContext getEglContext() {
if (glThread != null) {
return glThread.getEglContext();
}
return null;
}
public void requestRender() {
if (glThread != null) {
glThread.requestRender();
}
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
if (surface == null) {
surface = holder.getSurface();
}
glThread = new GLThread(new WeakReference<BaseSurfaceView>(this));
glThread.isCreate = true;
glThread.start();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
glThread.width = width;
glThread.height = height;
glThread.isChange = true;
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
glThread.onDestory();
glThread = null;
surface = null;
eglContext = null;
}
public interface GLRender {
void onSurfaceCreated();
void onSurfaceChanged(int width, int height);
void onDrawFrame();
}
static class GLThread extends Thread {
private WeakReference<BaseSurfaceView> wleglSurfaceViewWeakReference;
private BaseEglHelper baseEglHelper = 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 GLThread(WeakReference<BaseSurfaceView> wleglSurfaceViewWeakReference) {
this.wleglSurfaceViewWeakReference = wleglSurfaceViewWeakReference;
}
@Override
public void run() {
super.run();
isExit = false;
isStart = false;
object = new Object();
baseEglHelper = new BaseEglHelper();
baseEglHelper.initEgl(wleglSurfaceViewWeakReference.get().surface, wleglSurfaceViewWeakReference.get().eglContext);
while (true) {
if (isExit) {
//释放资源
release();
break;
}
if (isStart) {
if (wleglSurfaceViewWeakReference.get().mRenderMode == RENDERMODE_WHEN_DIRTY) {
synchronized (object) {
try {
object.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} else if (wleglSurfaceViewWeakReference.get().mRenderMode == RENDERMODE_CONTINUOUSLY) {
try {
Thread.sleep(1000 / 60);
} 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 && wleglSurfaceViewWeakReference.get().glRender != null) {
isCreate = false;
wleglSurfaceViewWeakReference.get().glRender.onSurfaceCreated();
}
}
private void onChange(int width, int height) {
if (isChange && wleglSurfaceViewWeakReference.get().glRender != null) {
isChange = false;
wleglSurfaceViewWeakReference.get().glRender.onSurfaceChanged(width, height);
}
}
private void onDraw() {
if (wleglSurfaceViewWeakReference.get().glRender != null && baseEglHelper != null) {
wleglSurfaceViewWeakReference.get().glRender.onDrawFrame();
if (!isStart) {
wleglSurfaceViewWeakReference.get().glRender.onDrawFrame();
}
baseEglHelper.swapBuffers();
}
}
private void requestRender() {
if (object != null) {
synchronized (object) {
object.notifyAll();
}
}
}
public void onDestory() {
isExit = true;
requestRender();
}
public void release() {
if (baseEglHelper != null) {
baseEglHelper.destoryEgl();
baseEglHelper = null;
object = null;
wleglSurfaceViewWeakReference = null;
}
}
public EGLContext getEglContext() {
if (baseEglHelper != null) {
return baseEglHelper.getmEglContext();
}
return null;
}
}
}
后续学习代码都放到github中,会不断更新!https://github.com/YuLingRui/AntonyCFAV