OpenGL ES在Android NDK环境中的使用及多线程开发

一、概览

在过去十年伴随着移动互联网的发展浪潮,音视频行业得以飞速发展,音视频技术方面有几个神器是音视频从业者的必备技能,其中OpenGL ES是移动端视频图像处理的核心技能,诸如抖音,美拍,直播中的美白、磨皮、大眼、瘦脸、贴纸等都是OpenGL ES佳作。

安卓上提供了一套基于Java的OpenGL ES的API,但最终还是通过调用Native的方法实现的。此外,音视频是属于一种通用技术,平台相关性不是很明显,普遍的做法是实现一套跨平台的SDK,这样节约的代价是相当可观。基于音视频技术的一些特点,跨平台语言选用c/c++已成为了默认的标准,但c++层面并没有像GLSurfaceView这样封装完整的环境供开发者使用,需要程序猿们手动来实现这个流程。

本篇文章是除了基本概念之外的另一个入门篇,基于c++的OpenGL ES开发环境搭建,具体的来说,是将GLSurfaceView的核心代码用c++来实现,并开放一些重要的api做多线程开发(代码以Android为例)。

二、EGL环境搭建

1、在CmakeLists.txt的加入这样的依赖

目的是在链接目标文件时将安卓中的libEGL.so和libOpenGLES_2.so这两个动态链接库加入开发者的代码中供使用,可以理解为没有这个#include相关文件会file not find。

target_link_libraries(
        target
        GLESv2
        EGL
)

2、创建渲染表面

通过调用这个eglGetDisplay() 这个Api来创建渲染表面

3、初始化

通过eglInitialize()来初始化渲染表面

4、获取配置

通过eglChooseConfig来配置OpenGL 版本,RGBA每个通道的精度等

5、创建上下文

通过eglCreateContext来获取EGL环境的上下文

至此5步,EGL环境已经搭建完毕,在使用过程中要根据当前线程的环境适时调 eglMakeCurrent切换线程,每一帧渲染之后调用eglSwapBuffers交换前后台内容。

6、一个简单的EGL换将帮助类

这个类的原型是谷歌提供的一个非官方demo,我将其中的Java代码转换成c++实现,实测有效

#include <GLES2/gl2.h>
#include <EGL/egl.h>
#include <android/native_window.h>

class EglCore {

public:

    EglCore();

    EglCore(const EglCore &) = delete;

    EglCore(EglCore &&) = delete;

    bool init();

    void makeCurrent(EGLSurface surface);

    void swapBuffer(EGLSurface surface);

    EGLSurface createEGLSurface(ANativeWindow *window);

    void destroyEGLSurface(EGLSurface surface);

    void destroy();

private:

    EGLDisplay _display{nullptr};

    EGLConfig _config{nullptr};

    EGLContext _context{nullptr};

};
bool EglCore::init() {

    if (_display != EGL_NO_DISPLAY) {
        return false;
    }

    _display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
    if (EGL_NO_DISPLAY == _display) {
        LOGI("egl get no display");
        return false;
    }

    GLint majorVersion, minorVersion;

    if (!eglInitialize(_display, &majorVersion, &minorVersion)) {
        LOGI("egl initialize fail");
        return false;
    }

  
    EGLint config_attrs[] = {
            EGL_BUFFER_SIZE, 32,
            EGL_ALPHA_SIZE, 8,
            EGL_BLUE_SIZE, 8,
            EGL_GREEN_SIZE, 8,
            EGL_RED_SIZE, 8,
            EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
            EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
            EGL_NONE
    };


    int num_configs = 0;
    EGLConfig config;
    if (!eglChooseConfig(_display, config_attrs, &config, 1, &num_configs)) {
        LOGI("egl choose config error");
        return false;
    }

    if (config) {
        EGLint context_attrs[] = {
                EGL_CONTEXT_CLIENT_VERSION, 2,
                EGL_NONE
        };

        EGLContext context = eglCreateContext(_display, config, share_context, context_attrs);
    }
    return EGL_NO_CONTEXT != _context;
}


EGLSurface EglCore::createEGLSurface(ANativeWindow *window) {
    EGLint width, height;
    EGLSurface surface = eglCreateWindowSurface(_display, _config, window, nullptr);
    if (EGL_NO_SURFACE == surface) {
        LOGI("egl create surface error");
        return nullptr;
    }
    return surface;
}

void EglCore::destroyEGLSurface(EGLSurface surface) {
    eglDestroySurface(_display, surface);
}

void EglCore::makeCurrent(EGLSurface surface) {
    if (!eglMakeCurrent(_display, surface, surface, _context)) {
        LOGI("egl make current error");
    }
}

void EglCore::swapBuffer(EGLSurface surface) {
    if (!eglSwapBuffers(_display, surface)) {
        LOGI("egl swap error");
    }
}

void EglCore::destroy() {
    eglMakeCurrent(_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
    eglDestroyContext(_display, _context);
    eglTerminate(_display);
    _display = EGL_NO_DISPLAY;
    _context = EGL_NO_CONTEXT;
}

三、用c++实现GLSurfaceView

本文提供一个精简版的OpenGL开发环境,能满足相机渲染等基本需求,更多的内容请查看GLSurfaceView源码获取帮助


#include "glview.h"

GlView::GlView() {
    LOGI("GLView Constructor");
}

void GlView::createSurface(jobject surface, int w, int h) {
    window = ANativeWindow_fromSurface(env, surface);
    this->width = w;
    this->height = h;
    is_exit = false;
    future = std::async(std::launch::async, &GlView::guardedRun, this);
}

void GlView::guardedRun() {

    if (window != nullptr) {
        eglCore = new EglCore();
        eglCore->init();
        eglSurface = eglCore->createEGLSurface(window);
        eglCore->makeCurrent(eglSurface);
    }
    if (render && window) {
        render->onSurfaceCreate(nullptr, width, height);
        render->onDraw();
    }

    while (!is_exit) {

        cond.wait(lock);
       
            if (render) {
                render->onDraw();
            }

            if (eglCore) {
                eglCore->swapBuffer(eglSurface);
            }
        }
    }
    
    if (render) {
        render->onSurfaceDestroy();
    }
    if (eglCore) {
        eglCore->destroyEGLSurface(eglSurface);
        eglCore->destroy();
        delete eglCore;
    }
    LOGI("GL thread quit");
}

void GlView::destroySurface() {
    is_exit = true;
    cond.notify_all();
    if (future.isValid()) {
        future.wait();
    } 

}

void GlView::setRender(IRender *render_) {
    this->render = render_;
}


void GlView::requestRender() {
    cond.notify_all();
}

GlView::~GlView() {
    LOGI("~ GlView");
}

有了一个类似Java层的GLSurfaceView,那么后续的开发就变得容易,只需要将重心放在滤镜的实现上即可。

四、多线程开发

要在多个线程中使用OpenGL ES开发,需先了解一些状态机的概念,其实很简单。前面在创建EGL环境的时候有个eglContext的概念,这个EGL环境的上下文,可在不同线程共享,切换线程的时候通过这个context调用eglMakeCurrent即可使用当前拥有的program一次包装好的滤镜对象。

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

[](){}

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值