关于opengl-es:OpenglEs之EGL环境搭建

28次阅读

共计 8372 个字符,预计需要花费 21 分钟才能阅读完成。

前言

后面咱们公布了一系列的入门教程,例如 C ++ 系列的指针扫盲、多线程的应用等,JNI 入门系列,ffmpeg 入门系列等,有感兴趣的童鞋们能够关注往回自行查阅。

明天咱们的主题仍然是音视频开发的领域,做过音视频开发的都晓得 Opengl 也是音视频开发中的一项重要技能,特地是波及到视频录制、特效解决、画质渲染细分性能。因而后续笔者打算再出一系列的 Opengl ES 的学习笔记,
心愿能与大家独特温故知新。

因为后面介绍了一些 NDK 和 C ++ 的教程,所以为了坚固,后续的一些 demo 多以 NDK 的模式出现给大家,应用 Opengl ES3 的版本。

明天咱们的主题是 Opengl ES 的第一篇 –>EGL

EGL 是什么

家喻户晓,Opengl 是跨平台的,那么面对各种平台的差异性,Opengl 是如何抹平而做到跨平台的呢?这兴许就是 EGL 的功绩吧,简略地说 EGL 就是 Opengl 和平台各平台之间的一个适配器,是一系列的接口,具体实现是由具体的设施厂商实现的。

EGL 是渲染 API(如 OpenGL ES)和原生窗口零碎之间的接口. 通常来说,OpenGL 是一个操作 GPU 的 API,它通过驱动向 GPU 发送相干指令,管制图形渲染管线状态机的运行状态,然而当波及到与本地窗口零碎进行交互时,就须要这么一个中间层,且它最好是与平台无关的,
因而 EGL 被设计进去,作为 OpenGL 和原生窗口零碎之间的桥梁。

EGL API 是独立于 OpenGL 各版本规范的独立的一套 API,其次要作用是为 OpenGL 指令 创立上下文 Context、绘制指标 Surface、配置 FrameBuffer 属性、Swap 提交绘制后果等。EGL 提供如下机制:

  • 与设施的原生窗口零碎通信
  • 查问绘图外表的可用类型和配置
  • 创立绘图外表
  • 在 OpenGL ES 和其余图形渲染 API 之间同步渲染
  • 治理纹理贴图等渲染资源

上面这张图可简要看出 EGL 的接口能力:

EGL 创立流程

想要在安卓上应用 Opengl ES 咱们能够间接应用 GLSurfaceView 来进行 Opengl 的渲染,因为 GLSurfaceView 外部给咱们封装好了 EGL 环境和渲染线程。如果咱们想要更高的拓展性,咱们也应用 SurfaceView,而后参考 SurfaceView 中的 EGL 环境搭建、线程模型来
自行搭建 Opengl ES 的渲染环境。

本着学习摸索的目标,咱们尝试在 NDK 搭建 EGL 环境。

上面这张图展现的是安卓零碎上 EGL 的次要应用 API:

须要阐明的一点是 EGL 是单线程模型的,也就说说 EGL 环境的创立、渲染操作、EGL 环境的销毁都必须在同一个线程内实现,否则是有效的。当然咱们能够通过共享 EGL 上下文来做多多线程渲染,但这些都是后话了 …

任何 OpenGL ES 应用程序都必须在开始渲染之前应用 EGL 执行如下工作:

  1. 查问并初始化设施商可用的显示器。
  2. 创立渲染外表。
    EGL 中创立的外表能够分类为屏幕上的外表或者屏幕外的外表。屏幕上的外表连贯到原生窗口零碎,而屏幕外的外表是不显示然而能够用作渲染外表的像素缓冲区。这些外表能够用来渲染纹理,并能够在多个 Khronos API 之间共享。
  3. 创立渲染上下文。
    EGL 是创立 OpenGL ES 渲染上下文所必须的。这个上下文必须连贯到适合的外表能力开始渲染。

上面是 EGL 环境创立的次要流程:

说完焦躁的基础理论,那就放码过去吧!!!

应用 Android Studio 创立一个 Native 工程,而后配置好 CMakeLists.txt 引入相干库:


cmake_minimum_required(VERSION 3.18.1)

project("learn")

#找到蕴含所有的 cpp 文件
file(GLOB allCpp *.cpp **/**.cpp **/**/**.cpp  **/**/**/**.cpp  **/**/**/**/**.cpp)

add_library( # Sets the name of the library.
        # 库名称
        learn

        SHARED

        ${allCpp})

target_link_libraries(
        learn
        # 引入 egl
        egl
        # 引入 gles 3
        GLESv3
        # 安卓相干库
        android
        # 安卓 log
        log)

上面咱们创立一个与 Native 映射的 EGLHelper 类:

package com.fly.opengles.learn.egl;

import android.view.Surface;

public class EGLHelper {
    protected long nativePtr;

    public void surfaceCreated(Surface surface) {nativePtrInit();
        n_surfaceCreated(nativePtr,surface);
    }

    public void surfaceChanged(int width, int height) {nativePtrInit();
        n_surfaceChanged(nativePtr,width,height);
    }

    public void surfaceDestroyed() {if(nativePtr != 0){n_surfaceDestroyed(nativePtr);
            nativePtr = 0;
        }
    }

    private void nativePtrInit(){if(nativePtr == 0){nativePtr = n_nativePtrInit();
        }
    }

    private native long n_nativePtrInit();
    private native void n_surfaceCreated(long nativePtr,Surface surface);
    private native void n_surfaceChanged(long nativePtr,int width, int height);
    private native void n_surfaceDestroyed(long nativePtr);
}

而后自定义一个 MySurfaceView 继承于 SurfaceView,在它的 Callback 回调办法中对 EGL 进行操作:

public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback {

    private EGLHelper eglHelper;

    public MySurfaceView(Context context) {this(context,null);
    }

    public MySurfaceView(Context context, AttributeSet attrs) {super(context, attrs);
        eglHelper = new EGLHelper();
        getHolder().addCallback(this);
    }

    @Override
    public void surfaceCreated(@NonNull SurfaceHolder surfaceHolder) {eglHelper.surfaceCreated(surfaceHolder.getSurface());
    }

    @Override
    public void surfaceChanged(@NonNull SurfaceHolder surfaceHolder, int i, int w, int h) {eglHelper.surfaceChanged(w,h);
    }

    @Override
    public void surfaceDestroyed(@NonNull SurfaceHolder surfaceHolder) {eglHelper.surfaceDestroyed();
    }
}

测试成果时,咱们在布局中应用咱们自定义好 MySurfaceView 即可,自此 java 层代码编写结束,在 NDK 层咱们将 EGL 环境创立结束后即可通过 MySurfaceView 看到渲染后果。

为了不便调试和 debug,咱们定义 Log.h 日志工具:

#ifndef NDK_OPENGLES_LEARN_LOG_H
#define NDK_OPENGLES_LEARN_LOG_H

#include "android/log.h"

#define LOGD(FORMAT, ...) __android_log_print(ANDROID_LOG_DEBUG, "fly_learn_opengl", FORMAT, ##__VA_ARGS__);
#define LOGE(FORMAT, ...) __android_log_print(ANDROID_LOG_ERROR, "fly_learn_opengl", FORMAT, ##__VA_ARGS__);

#endif //NDK_OPENGLES_LEARN_LOG_H

将 EGL 的相干操作封装在类 C ++ 的类 EglHelper 中:

EglHelper.h

#ifndef NDK_OPENGLES_LEARN_EGLHELPER_H
#define NDK_OPENGLES_LEARN_EGLHELPER_H

#include "EGL/egl.h"

class EglHelper {

public:
    EGLDisplay  mEglDisplay;
    EGLSurface  mEglSurface;
    EGLConfig  mEglConfig;
    EGLContext mEglContext;

public:
    EglHelper();
    ~EglHelper();
    int initEgl(EGLNativeWindowType win);
    int swapBuffers();
    void destroyEgl();};


#endif

EglHelper.cpp 次要实现如下,EGL 的次要创立过程在函数 initEgl 中,具体看正文:

#include "EglHelper.h"
#include "../utils/Log.h"

EglHelper::EglHelper() {

    mEglDisplay = EGL_NO_DISPLAY;
    mEglSurface = EGL_NO_SURFACE;
    mEglContext = EGL_NO_CONTEXT;
    mEglConfig = NULL;
}

EglHelper::~EglHelper() {destroyEgl();
}

int EglHelper::initEgl(EGLNativeWindowType window) {

    //1、获取显示设施
    mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
    if(mEglDisplay == EGL_NO_DISPLAY)
    {LOGE("eglGetDisplay error");
        return -1;
    }
    // 2、EGL 初始化
    EGLint *version = new EGLint[2];
    if(!eglInitialize(mEglDisplay, &version[0], &version[1]))
    {LOGE("eglInitialize error");
        return -1;
    }

    //3、资源配置,例如色彩位数等
    const EGLint attribs[] = {
            EGL_RED_SIZE, 8,
            EGL_GREEN_SIZE, 8,
            EGL_BLUE_SIZE, 8,
            EGL_ALPHA_SIZE, 8,
            EGL_DEPTH_SIZE, 8,
            EGL_STENCIL_SIZE, 8,
            EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
            EGL_NONE
    };

    EGLint num_config;
    if(!eglChooseConfig(mEglDisplay, attribs, NULL, 1, &num_config))
    {LOGE("eglChooseConfig  error 1");
        return -1;
    }

    //4、ChooseConfig
    if(!eglChooseConfig(mEglDisplay, attribs, &mEglConfig, num_config, &num_config))
    {LOGE("eglChooseConfig  error 2");
        return -1;
    }

    // 5、创立上下文
    int attrib_list[] = {
            EGL_CONTEXT_CLIENT_VERSION, 2,
            EGL_NONE
    };

    mEglContext = eglCreateContext(mEglDisplay, mEglConfig, EGL_NO_CONTEXT, attrib_list);

    if(mEglContext == EGL_NO_CONTEXT)
    {LOGE("eglCreateContext  error");
        return -1;
    }

    //6、创立渲染的 Surface
    mEglSurface = eglCreateWindowSurface(mEglDisplay, mEglConfig, window, NULL);
    if(mEglSurface == EGL_NO_SURFACE)
    {LOGE("eglCreateWindowSurface  error");
        return -1;
    }

    // 7、应用
    if(!eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext))
    {LOGE("eglMakeCurrent  error");
        return -1;
    }
    LOGD("egl init success!");
    return 0;
}

int EglHelper::swapBuffers() {if(mEglDisplay != EGL_NO_DISPLAY && mEglSurface != EGL_NO_SURFACE)
    {if(eglSwapBuffers(mEglDisplay, mEglSurface))
        {return 0;}
    }
    return -1;
}

void EglHelper::destroyEgl() {if(mEglDisplay != EGL_NO_DISPLAY)
    {eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
    }
    if(mEglDisplay != EGL_NO_DISPLAY && mEglSurface != EGL_NO_SURFACE)
    {eglDestroySurface(mEglDisplay, mEglSurface);
        mEglSurface = EGL_NO_SURFACE;
    }
    if(mEglDisplay != EGL_NO_DISPLAY && mEglContext != EGL_NO_CONTEXT){eglDestroyContext(mEglDisplay, mEglContext);
        mEglContext = EGL_NO_CONTEXT;
    }
    if(mEglDisplay != EGL_NO_DISPLAY)
    {eglTerminate(mEglDisplay);
        mEglDisplay = EGL_NO_DISPLAY;
    }
}

本人 EGL 环境创立结束,咱们通过 JNI 调用起来看看成果,native-lib.cpp:

#include <jni.h>
#include <string>
#include "eglhelper/EglHelper.h"
#include <cstdint>
#include "android/native_window.h"
#include "android/native_window_jni.h"
#include "GLES3/gl3.h"

jlong eglHelperNativePtrInit(JNIEnv *env, jobject thiz) {EglHelper *eglHelper = new EglHelper();
    return reinterpret_cast<uintptr_t>(eglHelper);
}

void eglSurfaceCreated(JNIEnv *env, jobject thiz,jlong native_ptr, jobject surface) {if(native_ptr != 0){EglHelper *eglHelper = reinterpret_cast<EglHelper *>(native_ptr);
        ANativeWindow *nativeWindow = ANativeWindow_fromSurface(env, surface);
        eglHelper->initEgl(nativeWindow);
    }
}

void eglSurfaceChanged(JNIEnv *env, jobject thiz,jlong native_ptr, jint width,jint height) {if(native_ptr != 0){
        // 设置视口大小
        glViewport(0, 0, width, height);
        // 绿色清屏
//        glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
        // 蓝色清屏
        glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);
        EglHelper *eglHelper = reinterpret_cast<EglHelper *>(native_ptr);
        eglHelper->swapBuffers();}
}

void eglSurfaceDestroyed(JNIEnv *env, jobject thiz,jlong native_ptr) {if(native_ptr != 0){EglHelper *eglHelper = reinterpret_cast<EglHelper *>(native_ptr);
        delete eglHelper;
    }
}

static JNINativeMethod nativeMethod_EGLHelper[] = {
        // Java 中的函数名
        {"n_nativePtrInit",
                // 函数签名信息
         "()J",
                // native 的函数指针
         (jlong *) (eglHelperNativePtrInit)},

        {"n_surfaceCreated",
                // 函数签名信息
         "(JLandroid/view/Surface;)V",
                // native 的函数指针
         (void *) (eglSurfaceCreated)},

        {"n_surfaceChanged",
                // 函数签名信息
         "(JII)V",
                // native 的函数指针
         (void *) (eglSurfaceChanged)},

        {"n_surfaceDestroyed",
                // 函数签名信息
         "(J)V",
                // native 的函数指针
         (void *) (eglSurfaceDestroyed)},
};

static int RegisterNativeMethods(JNIEnv *env, const char *className, JNINativeMethod *methods, int methodNum)
{jclass clazz = env->FindClass(className);
    if (clazz == NULL)
    {return JNI_FALSE;}
    if (env->RegisterNatives(clazz, methods, methodNum) < 0)
    {return JNI_FALSE;}
    return JNI_TRUE;
}

// 类库加载时主动调用
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reversed)
{
    JNIEnv *env = NULL;
    // 初始化 JNIEnv
    if(vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6) != JNI_OK){return JNI_FALSE;}
    // 动静注册
    RegisterNativeMethods(env,"com/fly/opengles/learn/egl/EGLHelper",nativeMethod_EGLHelper,sizeof(nativeMethod_EGLHelper) / sizeof(JNINativeMethod) );
    // 返回 JNI 应用的版本
    return JNI_VERSION_1_6;
}

上述 native-lib.cpp 波及到了之前介绍过的 JNI 函数签名、动静注册等相干知识点,遗记了的童鞋可往回看之前的记录。

如无意外,运行看到的是一个蓝屏画面则阐明 EGL 环境搭建胜利了,后续开启你的 Opengl 炫酷之旅吧!!!

举荐浏览

JNI 根底简介
JNI 之数组与字符串的应用
JNI 之动静注册与动态注册
JNI 之拜访 java 属性和办法
JNI 之缓存与援用
JNI 之异样解决
JNI 之罕用技巧与陷阱

关注我,一起提高,人生不止 coding!!!

正文完
 0