Android OpenGLES开发:EGL环境搭建

努力,不是为了要感动谁,也不是要做给哪个人看,而是要让自己随时有能力跳出自己厌恶的圈子,并拥有选择的权利,用自己喜欢的方式过一生!

EGL是什么?

谈到openGL开发我们就不得不说EGL,那EGL是什么呢?请看下图

官方听不懂解释

EGL(Embedded Graphic Library)是渲染API(如OpenGL ES)和本地窗口系统(native platform window system)之间的中间层接口,它主要由系统制造商实现。

EGL是一套OpenGLES、OpenVG等渲染接口和它所运行的平台的底层原生窗口系统之间的桥接接口,也就是帮助OpenGLES或其他Khronos图形API渲染的内容投射到目标窗口的接口

EGL是一个与平台无关的接口,EGL可以在多种操作系统(如Android和Linux)和本机窗口系统上实现。

通俗类比解释

  • OpengGL :他才是真正的画家,他负责创作画作。OpenGL是一个操作GPU的API,你可以认为GPU就是画笔、颜料等,如何妙笔生花就是OpenGL的功劳(哈哈哈,其实要画什么还是人发的指令,这里就不纠结了)。画家只负责饮酒作乐,哦不作画。至于我的画如何向别人展示,如何放到画室亦或是大街上供别人参观那我不管,我请一个公司EGL去负责,下面有请EGL登场。
  • EGL:画家(OpenGL)作了好多的画,我得想着怎么才能卖个好价钱:)。目前有Android画室、Linux画室以及windows画室在洽谈。我需要对接画家和画室,把画装裱到不同的画框、保证运输、并将画展示到画室。当然我们公司只提供了一套标准的流程,如何操作还需要Android、Linux画室工作人员来操作,他们需要遵循我们定的流程标准,将画正确无误的摆放到自己的画室供别人浏览参观。

提供了哪些功能?

  1. 与设备的原生窗口系统通信
  2. 查询绘图表面的可用类型和配置
  3. 创建绘图表面
  4. 在OpenGL ES 和其他图形渲染API之间同步渲染
  5. 管理纹理贴图等渲染资源

EGL怎么用

聪明的同学发现没有,我们使用GLSurfaceView编写OpenGL代码时好像并没有发现EGL的身影。那是因为GLSurfaceView早已帮我们搭建好了EGL环境,我们只需要编写OpenGL代码即可。如果我们想要更高的扩展性,以及真正了解他的运行机制我们需要自己搭建EGL。

下图请看EGL的主要使用API:

  • Display(EGLDisplay) 是对实际显示设备的抽象
  • Surface(EGLSurface)是对用来存储图像的内存区域
  • FrameBuffer 的抽象,包括 Color Buffer, Stencil Buffer ,Depth Buffer
  • Context (EGLContext) 存储 OpenGL ES绘图的一些状态信息

需要说明的是EGL是单线程模型,也就是说EGL环境创建、渲染操作、EGL环境的销毁都必须在同一个线程内完成,否则是无效的。那么我们要么在主线程创建,要么就是创建一个有消息循环的线程,聪明的你应该能想到那就得用HandlerThread或者自定义带Looper的Thread(GLSurfaceView采用该种方式)

使用EGL的基本步骤

  1. 获取OpenGL ES与原生窗口系统的连接:调用eglGetDisplay方法得到EGLDisplay
java 复制代码
    public static native EGLDisplay eglGetDisplay(
        int display_id
    );
  1. 初始化EGL链接:调用 eglInitialize 方法初始化
java 复制代码
    public static native boolean eglInitialize(
        EGLDisplay dpy,    // 要进行初始化的EGL连接,即上一步的返回值
        int[] major,       // 主版本号
        int majorOffset,   // 主版本号偏移
        int[] minor,       // 次版本号
        int minorOffset    // 次版本号偏移
    )
  1. 确定渲染表面的配置信息 :调用eglChooseConfig方法得到EGLConfig
    指定一组需求,然后再让EGL推荐(eglChooseChofig)最佳配置
  2. 创建渲染上下文:通过 EGLDisplay 和 EGLConfig ,调用 eglCreateContext 方法创建渲染上下文,得到 EGLContext
java 复制代码
    public static native EGLContext eglCreateContext(
        EGLDisplay dpy,
        EGLConfig config,
        EGLContext share_context,
        int[] attrib_list,
        int offset
    )
  1. 创建渲染表面:通过 EGLDisplay 和 EGLConfig ,调用 eglCreateWindowSurface 方法创建渲染表面,得到 EGLSurface
java 复制代码
    public static EGLSurface eglCreateWindowSurface(
        EGLDisplay dpy,      // EGLDisplay连接
        EGLConfig config,    // EGL frame buffer配置,定义了可用于Surface的frame buffer资源
        Object win,          // Android的窗口,可以是SurfaceView、Surface、SurfaceHolder、SurfaceTexture
        int[] attrib_list,   // 配置
        int offset           // 配置偏移
    )
  1. 绑定上下文:将EGL上下文绑定到当前线程,实现渲染环境的设置。通过eglMakeCurrent 方法将EGLSurface、EGLContext、EGLDisplay 三者绑定,接下来就可以使用 OpenGL 进行绘制了
java 复制代码
public static native boolean eglMakeCurrent(
        EGLDisplay dpy,
        EGLSurface draw,
        EGLSurface read,
        EGLContext ctx
    );
  1. 绘制图像:使用OpenGL的API绘制精美的图像
  2. 交换缓冲区:当用 OpenGL 绘制结束后,使用 eglSwapBuffers 方法交换前后缓冲,将绘制内容显示到屏幕上
  3. 释放 EGL 环境 :绘制结束,不再需要使用 EGL 时,取消 eglMakeCurrent 的绑定,销毁 EGLDisplay、EGLSurface、EGLContext

EGL代码实现

创建EGLHelper工具类如下:

java 复制代码
package com.android.xz.egldemo.gles;

import android.opengl.EGL14;
import android.opengl.EGLConfig;
import android.opengl.EGLContext;
import android.opengl.EGLDisplay;
import android.opengl.EGLSurface;
import android.util.Log;

/**
 * EGL环境搭建类
 * <p>
 * 1.创建显示屏幕类EGLDisplay
 * 2.配置FrameBuffer类EGLConfig
 * 3.创建FrameBuffer的EGLSurface
 * 4.创建上下文EGLContext,并与Surface绑定
 */
public class EGLHelper {

    private static final String TAG = EGLHelper.class.getSimpleName();

    /**
     * 屏幕显示类,表示一个可以现实的屏幕
     */
    private EGLDisplay mEGLDisplay = EGL14.EGL_NO_DISPLAY;
    /**
     * 系统窗口或FrameBuffer
     */
    private EGLSurface mEGLSurface = EGL14.EGL_NO_SURFACE;
    /**
     * FrameBuffer的配置属性
     */
    private EGLConfig mEGLConfig;
    /**
     * 渲染上下文,用于绑定上面3个属性,将其关联起来
     */
    private EGLContext mEGLContext = EGL14.EGL_NO_CONTEXT;

    /**
     * 初始化EGL环境
     *
     * @param surface
     */
    public void initEGL(Object surface) {
        // 1、获取显示设备
        mEGLDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
        if (mEGLDisplay == EGL14.EGL_NO_DISPLAY) {
            throw new RuntimeException("unable to get EGL14 display");
        }

        // 2、初始化EGL
        int[] version = new int[2];
        if (!EGL14.eglInitialize(mEGLDisplay, version, 0, version, 1)) {
            mEGLDisplay = null;
            throw new RuntimeException("unable to initialize EGL14");
        }

        // 3、资源配置,例如颜色配置等
        int[] attribList = {
                EGL14.EGL_RED_SIZE, 8,
                EGL14.EGL_GREEN_SIZE, 8,
                EGL14.EGL_BLUE_SIZE, 8,
                EGL14.EGL_ALPHA_SIZE, 8,
                //EGL14.EGL_DEPTH_SIZE, 16,
                //EGL14.EGL_STENCIL_SIZE, 8,
                EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,
                EGL14.EGL_NONE, 0,      // placeholder for recordable [@-3]
                EGL14.EGL_NONE
        };
        EGLConfig[] configs = new EGLConfig[1];
        int[] numConfigs = new int[1];

        // 4、ChooseConfig
        if (!EGL14.eglChooseConfig(mEGLDisplay, attribList, 0, configs, 0, configs.length,
                numConfigs, 0)) {
            throw new RuntimeException("unable to find RGB8888 / " + version + " EGLConfig");
        }
        mEGLConfig = configs[0];

        // 5、创建上下文
        int[] attrib2_list = {
                EGL14.EGL_CONTEXT_CLIENT_VERSION, 2,
                EGL14.EGL_NONE
        };
        EGLContext context = EGL14.eglCreateContext(mEGLDisplay, mEGLConfig, EGL14.EGL_NO_CONTEXT, attrib2_list, 0);
        if (context == EGL14.EGL_NO_CONTEXT) {
            throw new RuntimeException("eglCreateContext error");
        }
        mEGLContext = context;

        // 6、创建渲染Surface
        int[] attrib_list = {
                EGL14.EGL_NONE
        };
        EGLSurface eglSurface = EGL14.eglCreateWindowSurface(mEGLDisplay, mEGLConfig, surface, attrib_list, 0);
        if (eglSurface == null) {
            throw new RuntimeException("surface was null");
        }
        mEGLSurface = eglSurface;

        // 7、将EGL上下文绑定到当前线程,实现渲染环境的设置,之后就可以使用OpenGL进行绘制了
        if (!EGL14.eglMakeCurrent(mEGLDisplay, mEGLSurface, mEGLSurface, mEGLContext)) {
            throw new RuntimeException("eglMakeCurrent failed");
        }
        Log.i(TAG, "egl init success!");
    }

    /**
     * 交换缓冲区
     */
    public void swapBuffers() {
        if (mEGLDisplay != EGL14.EGL_NO_DISPLAY && mEGLSurface != EGL14.EGL_NO_SURFACE) {
            if (!EGL14.eglSwapBuffers(mEGLDisplay, mEGLSurface)) {
                throw new RuntimeException("swap buffers error");
            }
        }
    }

    /**
     * 销毁EGL环境
     */
    public void destroyEGL() {
        if (mEGLDisplay != EGL14.EGL_NO_DISPLAY) {
            EGL14.eglMakeCurrent(mEGLDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE,
                    EGL14.EGL_NO_CONTEXT);
        }
        if (mEGLDisplay != EGL14.EGL_NO_DISPLAY && mEGLSurface != EGL14.EGL_NO_SURFACE) {
            EGL14.eglDestroySurface(mEGLDisplay, mEGLSurface);
            mEGLSurface = EGL14.EGL_NO_SURFACE;
        }
        if (mEGLDisplay != EGL14.EGL_NO_DISPLAY && mEGLContext != EGL14.EGL_NO_CONTEXT) {
            EGL14.eglDestroyContext(mEGLDisplay, mEGLContext);
            mEGLContext = EGL14.EGL_NO_CONTEXT;
        }
        if (mEGLDisplay != EGL14.EGL_NO_DISPLAY) {
            EGL14.eglReleaseThread();
            EGL14.eglTerminate(mEGLDisplay);
            mEGLDisplay = EGL14.EGL_NO_DISPLAY;
        }
        mEGLConfig = null;
    }
}

自定义MySurfaceView创建EGL环境

  • surfaceCreated中初始化EGL环境
  • surfaceChanged中更新画布大小并重新绘制
  • surfaceDestroyed中销毁EGL环境
java 复制代码
public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback {

    private static final String TAG = MySurfaceView.class.getSimpleName();
    private EGLHelper mEGLHelper = new EGLHelper();

    public MySurfaceView(Context context) {
        super(context);
        init(context);
    }

    public MySurfaceView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    public MySurfaceView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }

    private void init(Context context) {
        getHolder().addCallback(this);
    }

    @Override
    public void surfaceCreated(@NonNull SurfaceHolder holder) {
        Log.i(TAG, "surfaceCreated.");
        mEGLHelper.initEGL(holder);
        draw();
        mEGLHelper.swapBuffers();
    }

    @Override
    public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {
        Log.i(TAG, "surfaceChanged.");
        GLES20.glViewport(0, 0, width, height);
        draw();
        mEGLHelper.swapBuffers();
    }

    @Override
    public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
        Log.v(TAG, "surfaceDestroyed.");
        mEGLHelper.destroyEGL();
    }

    /**
     * 该方法中编写OpenGL绘制相关代码
     */
    private void draw() {
        // 蓝色清屏
        GLES20.glClearColor(0f, 0f, 1f, 1f);
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
    }
}

xlm中加入自定义视图

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <com.android.xz.egldemo.view.MySurfaceView
        android:layout_width="200dp"
        android:layout_height="200dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

EGL环境创建好后,我们就可以在draw 方法中编写OpenGL代码来绘制各种各样的图像了。EGL环境代码基本都是固定的流程,OpenGL绘制完成后,调用EGLswapBuffers将OpenGL绘制的内容显示到EGLSurface上也就是设备的屏幕上。

最后

本文简述了OpenGL 是如何将绘制的图像显示到设备上的,这离不开EGL的功劳,它通过和OpenGL和设备窗口来回周旋,将一幅幅精美的图案呈现到人们的眼前,给它点个赞。

本文代码已经全部粘贴出来,所以就不放项目地址了:)

相关推荐
2501_915918419 分钟前
如何解析iOS崩溃日志:从获取到符号化分析
android·ios·小程序·https·uni-app·iphone·webview
Entropless39 分钟前
OkHttp 深度解析(一) : 从一次完整请求看 OkHttp 整体架构
android·okhttp
v***91301 小时前
Spring+Quartz实现定时任务的配置方法
android·前端·后端
wilsend1 小时前
Android Studio 2024版新建java项目和配置环境下载加速
android
兰琛2 小时前
Android Compose展示PDF文件
android·pdf
走在路上的菜鸟2 小时前
Android学Dart学习笔记第四节 基本类型
android·笔记·学习
百锦再3 小时前
第21章 构建命令行工具
android·java·图像处理·python·计算机视觉·rust·django
skyhh4 小时前
Android Studio 最新版汉化
android·ide·android studio
路人甲ing..4 小时前
Android Studio 快速的制作一个可以在 手机上跑的app
android·java·linux·智能手机·android studio
携欢7 小时前
PortSwigger靶场之Web shell upload via path traversal靶场通关秘籍
android