最通俗的安卓OpenGL教学02——EGL环境搭建

EGL 是OpenGL ES和本地窗口系统的接口,不同平台上EGL配置是不一样的,而OpenGL的调用方式是一致的,就是说:OpenGL跨平台就是依赖于EGL接口。

当需要把同一个场景渲染到不同的Surface上时,此时系统GLSurfaceView 往往就不能满足需求,所以需要自己创建EGL环境来实现渲染操作。

安卓中EGL环境的创建有以下步骤:

  1. 获取Egl实例
  2. 获取默认的显示设备(就是窗口)
  3. 初始化默认显示设备
  4. 设置显示设备的属性
  5. 从系统中获取对应属性的配置
  6. 创建EglContext
  7. 创建渲染的Surface
  8. 绑定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) {

            }
        });
    }
}

效果如图:

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值