美文网首页androidandroid开发音视频开发集锦
Android EGL + C/C++ 编写OpenGLES程序

Android EGL + C/C++ 编写OpenGLES程序

作者: cain_huang | 来源:发表于2018-04-07 21:30 被阅读164次

    本人在编写基于FFmpeg的播放器时,需要将解码后的视频帧数据upload到GPU进行渲染输出,方便给视频添加滤镜之类的。输出部分有两种方案,一种是使用GLSurfaceView,就是将Native解码得到的数据回到到Java层进行渲染。第二种是使用EGL + ANativeWindow 直接在Native层利用GPU进行渲染。第一种方案需要在Native层与Java层之间不断进行数据交换,这方的方式其实并不太好,如果遇到多线程处理,由于Java层与Native层之间各自线程空间的不同,个人不太推荐这样的方式。下面来讲解一下如何在Native层使用EGL进行渲染。如果不了解什么是EGL的话,建议先看看本人的文章EGL简介以及窗口初始化了解一下。另外,大家有兴趣的话,可以看下GLSurfaceView的源码,GLSurfaceView也是通过开辟一个Looper线程GLThread来封装EGL来实现的。我们接下来实现一个Native层封装EGL + ANativeWindow的项目,项目的Github地址如下:
    EGLNativeRender

    EGL的封装

    由于EGL的API使用起来比较复杂,为了方便使用,我们需要将EGL做一层封装。关于EGL的API使用方式,可以参考Grafika项目中的EglCore.java、EglSurfaceBase.java、WindowSurface.java和OffscreenSurface.java,我的CainCamera 项目中也用到了Grafika的EGL封装类,使用方便。由于Grafika的EglCore是用Java层封装的,我们现在需要在Native层实现类似的EglCore、EglSurfaceBase、WindowSurface和OffscreenSurface类。废话不多数,直接上源码:
    EglCore.h:

    #ifndef CAINCAMERA_EGLCORE_H
    #define CAINCAMERA_EGLCORE_H
    
    #include <android/native_window.h>
    #include <EGL/egl.h>
    #include <EGL/eglext.h>
    #include <EGL/eglplatform.h>
    
    /**
     * Constructor flag: surface must be recordable.  This discourages EGL from using a
     * pixel format that cannot be converted efficiently to something usable by the video
     * encoder.
     */
    #define FLAG_RECORDABLE 0x01
    
    /**
     * Constructor flag: ask for GLES3, fall back to GLES2 if not available.  Without this
     * flag, GLES2 is used.
     */
    #define FLAG_TRY_GLES3 002
    
    // Android-specific extension
    #define EGL_RECORDABLE_ANDROID 0x3142
    
    typedef EGLBoolean (EGLAPIENTRYP EGL_PRESENTATION_TIME_ANDROIDPROC)(EGLDisplay display, EGLSurface surface, khronos_stime_nanoseconds_t time);
    
    class EglCore {
    
    private:
        EGLDisplay mEGLDisplay = EGL_NO_DISPLAY;
        EGLConfig  mEGLConfig = NULL;
        EGLContext mEGLContext = EGL_NO_CONTEXT;
    
        // 设置时间戳方法
        EGL_PRESENTATION_TIME_ANDROIDPROC eglPresentationTimeANDROID = NULL;
    
        int mGlVersion = -1;
        // 查找合适的EGLConfig
        EGLConfig getConfig(int flags, int version);
    
    public:
        EglCore();
        ~EglCore();
        EglCore(EGLContext sharedContext, int flags);
        bool init(EGLContext sharedContext, int flags);
        // 释放资源
        void release();
        // 获取EglContext
        EGLContext getEGLContext();
        // 销毁Surface
        void releaseSurface(EGLSurface eglSurface);
        //  创建EGLSurface
        EGLSurface createWindowSurface(ANativeWindow *surface);
        // 创建离屏EGLSurface
        EGLSurface createOffscreenSurface(int width, int height);
        // 切换到当前上下文
        void makeCurrent(EGLSurface eglSurface);
        // 切换到某个上下文
        void makeCurrent(EGLSurface drawSurface, EGLSurface readSurface);
        // 没有上下文
        void makeNothingCurrent();
        // 交换显示
        bool swapBuffers(EGLSurface eglSurface);
        // 设置pts
        void setPresentationTime(EGLSurface eglSurface, long nsecs);
        // 判断是否属于当前上下文
        bool isCurrent(EGLSurface eglSurface);
        // 执行查询
        int querySurface(EGLSurface eglSurface, int what);
        // 查询字符串
        const char *queryString(int what);
        // 获取当前的GLES 版本号
        int getGlVersion();
        // 检查是否出错
        void checkEglError(const char *msg);
    };
    
    
    #endif //CAINCAMERA_EGLCORE_H
    

    EglCore.cpp:

    #include "../common/native_log.h"
    #include "EglCore.h"
    #include <assert.h>
    
    EglCore::EglCore() {
        init(NULL, 0);
    }
    
    
    EglCore::~EglCore() {
        release();
    }
    
    /**
     * 构造方法
     * @param sharedContext
     * @param flags
     */
    EglCore::EglCore(EGLContext sharedContext, int flags) {
        init(sharedContext, flags);
    }
    
    /**
     * 初始化
     * @param sharedContext
     * @param flags
     * @return
     */
    bool EglCore::init(EGLContext sharedContext, int flags) {
        assert(mEGLDisplay == EGL_NO_DISPLAY);
        if (mEGLDisplay != EGL_NO_DISPLAY) {
            ALOGE("EGL already set up");
            return false;
        }
        if (sharedContext == NULL) {
            sharedContext = EGL_NO_CONTEXT;
        }
    
        mEGLDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
        assert(mEGLDisplay != EGL_NO_DISPLAY);
        if (mEGLDisplay == EGL_NO_DISPLAY) {
            ALOGE("unable to get EGL14 display.\n");
            return false;
        }
    
        if (!eglInitialize(mEGLDisplay, 0, 0)) {
            mEGLDisplay = EGL_NO_DISPLAY;
            ALOGE("unable to initialize EGL14");
            return false;
        }
    
        // 尝试使用GLES3
        if ((flags & FLAG_TRY_GLES3) != 0) {
            EGLConfig config = getConfig(flags, 3);
            if (config != NULL) {
                int attrib3_list[] = {
                        EGL_CONTEXT_CLIENT_VERSION, 3,
                        EGL_NONE
                };
                EGLContext context = eglCreateContext(mEGLDisplay, config,
                                                      sharedContext, attrib3_list);
                checkEglError("eglCreateContext");
                if (eglGetError() == EGL_SUCCESS) {
                    mEGLConfig = config;
                    mEGLContext = context;
                    mGlVersion = 3;
                }
            }
        }
        // 如果GLES3没有获取到,则尝试使用GLES2
        if (mEGLContext == EGL_NO_CONTEXT) {
            EGLConfig config = getConfig(flags, 2);
            assert(config != NULL);
            int attrib2_list[] = {
                    EGL_CONTEXT_CLIENT_VERSION, 2,
                    EGL_NONE
            };
            EGLContext context = eglCreateContext(mEGLDisplay, config,
                                                  sharedContext, attrib2_list);
            checkEglError("eglCreateContext");
            if (eglGetError() == EGL_SUCCESS) {
                mEGLConfig = config;
                mEGLContext = context;
                mGlVersion = 2;
            }
        }
    
        // 获取eglPresentationTimeANDROID方法的地址
        eglPresentationTimeANDROID = (EGL_PRESENTATION_TIME_ANDROIDPROC)
                eglGetProcAddress("eglPresentationTimeANDROID");
        if (!eglPresentationTimeANDROID) {
            ALOGE("eglPresentationTimeANDROID is not available!");
        }
    
        int values[1] = {0};
        eglQueryContext(mEGLDisplay, mEGLContext, EGL_CONTEXT_CLIENT_VERSION, values);
        ALOGD("EGLContext created, client version %d", values[0]);
    
        return true;
    }
    
    
    /**
     * 获取合适的EGLConfig
     * @param flags
     * @param version
     * @return
     */
    EGLConfig EglCore::getConfig(int flags, int version) {
        int renderableType = EGL_OPENGL_ES2_BIT;
        if (version >= 3) {
            renderableType |= EGL_OPENGL_ES3_BIT_KHR;
        }
        int attribList[] = {
                EGL_RED_SIZE, 8,
                EGL_GREEN_SIZE, 8,
                EGL_BLUE_SIZE, 8,
                EGL_ALPHA_SIZE, 8,
                //EGL_DEPTH_SIZE, 16,
                //EGL_STENCIL_SIZE, 8,
                EGL_RENDERABLE_TYPE, renderableType,
                EGL_NONE, 0,      // placeholder for recordable [@-3]
                EGL_NONE
        };
        int length = sizeof(attribList) / sizeof(attribList[0]);
        if ((flags & FLAG_RECORDABLE) != 0) {
            attribList[length - 3] = EGL_RECORDABLE_ANDROID;
            attribList[length - 2] = 1;
        }
        EGLConfig configs = NULL;
        int numConfigs;
        if (!eglChooseConfig(mEGLDisplay, attribList, &configs, 1, &numConfigs)) {
            ALOGW("unable to find RGB8888 / %d  EGLConfig", version);
            return NULL;
        }
        return configs;
    }
    
    /**
     * 释放资源
     */
    void EglCore::release() {
        if (mEGLDisplay != EGL_NO_DISPLAY) {
            eglMakeCurrent(mEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
            eglDestroyContext(mEGLDisplay, mEGLContext);
            eglReleaseThread();
            eglTerminate(mEGLDisplay);
        }
    
        mEGLDisplay = EGL_NO_DISPLAY;
        mEGLContext = EGL_NO_CONTEXT;
        mEGLConfig = NULL;
    }
    
    /**
     * 获取EGLContext
     * @return
     */
    EGLContext EglCore::getEGLContext() {
        return mEGLContext;
    }
    
    /**
     * 销毁EGLSurface
     * @param eglSurface
     */
    void EglCore::releaseSurface(EGLSurface eglSurface) {
        eglDestroySurface(mEGLDisplay, eglSurface);
    }
    
    /**
     * 创建EGLSurface
     * @param surface
     * @return
     */
    EGLSurface EglCore::createWindowSurface(ANativeWindow *surface) {
        assert(surface != NULL);
        if (surface == NULL) {
            ALOGE("ANativeWindow is NULL!");
            return NULL;
        }
        int surfaceAttribs[] = {
                EGL_NONE
        };
        ALOGD("eglCreateWindowSurface start");
        EGLSurface eglSurface = eglCreateWindowSurface(mEGLDisplay, mEGLConfig, surface, surfaceAttribs);
        checkEglError("eglCreateWindowSurface");
        assert(eglSurface != NULL);
        if (eglSurface == NULL) {
            ALOGE("EGLSurface is NULL!");
            return NULL;
        }
        return eglSurface;
    }
    
    /**
     * 创建离屏渲染的EGLSurface
     * @param width
     * @param height
     * @return
     */
    EGLSurface EglCore::createOffscreenSurface(int width, int height) {
        int surfaceAttribs[] = {
                EGL_WIDTH, width,
                EGL_HEIGHT, height,
                EGL_NONE
        };
        EGLSurface eglSurface = eglCreatePbufferSurface(mEGLDisplay, mEGLConfig, surfaceAttribs);
        assert(eglSurface != NULL);
        if (eglSurface == NULL) {
            ALOGE("Surface was null");
            return NULL;
        }
        return eglSurface;
    }
    
    /**
     * 切换到当前的上下文
     * @param eglSurface
     */
    void EglCore::makeCurrent(EGLSurface eglSurface) {
        if (mEGLDisplay == EGL_NO_DISPLAY) {
            ALOGD("Note: makeCurrent w/o display.\n");
        }
        if (!eglMakeCurrent(mEGLDisplay, eglSurface, eglSurface, mEGLContext)) {
            // TODO 抛出异常
        }
    }
    
    /**
     * 切换到某个上下文
     * @param drawSurface
     * @param readSurface
     */
    void EglCore::makeCurrent(EGLSurface drawSurface, EGLSurface readSurface) {
        if (mEGLDisplay == EGL_NO_DISPLAY) {
            ALOGD("Note: makeCurrent w/o display.\n");
        }
        if (!eglMakeCurrent(mEGLDisplay, drawSurface, readSurface, mEGLContext)) {
            // TODO 抛出异常
        }
    }
    
    /**
     *
     */
    void EglCore::makeNothingCurrent() {
        if (!eglMakeCurrent(mEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) {
            // TODO 抛出异常
        }
    }
    
    /**
     * 交换显示
     * @param eglSurface
     * @return
     */
    bool EglCore::swapBuffers(EGLSurface eglSurface) {
        return eglSwapBuffers(mEGLDisplay, eglSurface);
    }
    
    /**
     * 设置显示时间戳pts
     * @param eglSurface
     * @param nsecs
     */
    void EglCore::setPresentationTime(EGLSurface eglSurface, long nsecs) {
        eglPresentationTimeANDROID(mEGLDisplay, eglSurface, nsecs);
    }
    
    /**
     * 是否处于当前上下文
     * @param eglSurface
     * @return
     */
    bool EglCore::isCurrent(EGLSurface eglSurface) {
        return mEGLContext == eglGetCurrentContext() &&
                eglSurface == eglGetCurrentSurface(EGL_DRAW);
    }
    
    /**
     * 查询surface
     * @param eglSurface
     * @param what
     * @return
     */
    int EglCore::querySurface(EGLSurface eglSurface, int what) {
        int value;
        eglQuerySurface(mEGLContext, eglSurface, what, &value);
        return value;
    }
    
    /**
     * 查询字符串
     * @param what
     * @return
     */
    const char* EglCore::queryString(int what) {
        return eglQueryString(mEGLDisplay, what);
    }
    
    /**
     * 获取GLES版本号
     * @return
     */
    int EglCore::getGlVersion() {
        return mGlVersion;
    }
    
    /**
     * 检查是否出错
     * @param msg
     */
    void EglCore::checkEglError(const char *msg) {
        int error;
        if ((error = eglGetError()) != EGL_SUCCESS) {
            // TODO 抛出异常
            ALOGE("%s: EGL error: %x", msg, error);
        }
    }
    

    到这里,我们就封装好了EglCore核心,那么接下来我们需要对EGLSurface进行封装处理:
    EglSurfaceBase.h:

    #ifndef CAINCAMERA_EGLSURFACEBASE_H
    #define CAINCAMERA_EGLSURFACEBASE_H
    
    #include "EglCore.h"
    #include "../common/native_log.h"
    
    class EglSurfaceBase {
    
    public:
        EglSurfaceBase(EglCore *eglCore);
        // 创建窗口Surface
        void createWindowSurface(ANativeWindow *nativeWindow);
        // 创建离屏Surface
        void createOffscreenSurface(int width, int height);
        // 获取宽度
        int getWidth();
        // 获取高度
        int getHeight();
        // 释放EGLSurface
        void releaseEglSurface();
        // 切换到当前上下文
        void makeCurrent();
        // 交换缓冲区,显示图像
        bool swapBuffers();
        // 设置显示时间戳
        void setPresentationTime(long nsecs);
        // 获取当前帧缓冲
        char *getCurrentFrame();
    
    protected:
        EglCore *mEglCore;
        EGLSurface mEglSurface;
        int mWidth;
        int mHeight;
    
    };
    
    
    #endif //CAINCAMERA_EGLSURFACEBASE_H
    

    EglSurfaceBase.cpp:

    #include <assert.h>
    #include <GLES2/gl2.h>
    #include "EglSurfaceBase.h"
    
    
    EglSurfaceBase::EglSurfaceBase(EglCore *eglCore) : mEglCore(eglCore) {
        mEglSurface = EGL_NO_SURFACE;
    }
    
    /**
     * 创建显示的Surface
     * @param nativeWindow
     */
    void EglSurfaceBase::createWindowSurface(ANativeWindow *nativeWindow) {
        assert(mEglSurface == EGL_NO_SURFACE);
        if (mEglSurface != EGL_NO_SURFACE) {
            ALOGE("surface already created\n");
            return;
        }
        mEglSurface = mEglCore->createWindowSurface(nativeWindow);
    }
    
    /**
     * 创建离屏surface
     * @param width
     * @param height
     */
    void EglSurfaceBase::createOffscreenSurface(int width, int height) {
        assert(mEglSurface == EGL_NO_SURFACE);
        if (mEglSurface != EGL_NO_SURFACE) {
            ALOGE("surface already created\n");
            return;
        }
        mEglSurface = mEglCore->createOffscreenSurface(width, height);
        mWidth = width;
        mHeight = height;
    }
    
    /**
     * 获取宽度
     * @return
     */
    int EglSurfaceBase::getWidth() {
        if (mWidth < 0) {
            return mEglCore->querySurface(mEglSurface, EGL_WIDTH);
        } else {
            return mWidth;
        }
    }
    
    /**
     * 获取高度
     * @return
     */
    int EglSurfaceBase::getHeight() {
        if (mHeight < 0) {
            return mEglCore->querySurface(mEglSurface, EGL_HEIGHT);
        } else {
            return mHeight;
        }
    }
    
    /**
     * 释放EGLSurface
     */
    void EglSurfaceBase::releaseEglSurface() {
        mEglCore->releaseSurface(mEglSurface);
        mEglSurface = EGL_NO_SURFACE;
        mWidth = mHeight = -1;
    }
    
    /**
     * 切换到当前EGLContext
     */
    void EglSurfaceBase::makeCurrent() {
        mEglCore->makeCurrent(mEglSurface);
    }
    
    /**
     * 交换到前台显示
     * @return
     */
    bool EglSurfaceBase::swapBuffers() {
        bool result = mEglCore->swapBuffers(mEglSurface);
        if (!result) {
            ALOGD("WARNING: swapBuffers() failed");
        }
        return result;
    }
    
    /**
     * 设置当前时间戳
     * @param nsecs
     */
    void EglSurfaceBase::setPresentationTime(long nsecs) {
        mEglCore->setPresentationTime(mEglSurface, nsecs);
    }
    
    /**
     * 获取当前像素
     * @return
     */
    char* EglSurfaceBase::getCurrentFrame() {
        char *pixels = NULL;
        glReadPixels(0, 0, getWidth(), getHeight(), GL_RGBA, GL_UNSIGNED_BYTE, pixels);
        return pixels;
    }
    

    好了,我们封装好了EGLSurface的基类,接下来我们需要实现一个用于显示的WindowSurface 和 用于离屏渲染的OffscreenSurface,实现如下:
    WindowSurface.h:

    #ifndef CAINCAMERA_WINDOWSURFACE_H
    #define CAINCAMERA_WINDOWSURFACE_H
    
    
    #include "EglSurfaceBase.h"
    
    class WindowSurface : public EglSurfaceBase {
    
    public:
        WindowSurface(EglCore *eglCore, ANativeWindow *window, bool releaseSurface);
        WindowSurface(EglCore *eglCore, ANativeWindow *window);
        // 释放资源
        void release();
        // 重新创建
        void recreate(EglCore *eglCore);
    
    private:
        ANativeWindow  *mSurface;
        bool mReleaseSurface;
    };
    #endif //CAINCAMERA_WINDOWSURFACE_H
    

    WindowSurface.cpp:

    #include <assert.h>
    #include "WindowSurface.h"
    
    WindowSurface::WindowSurface(EglCore *eglCore, ANativeWindow *window, bool releaseSurface)
            : EglSurfaceBase(eglCore) {
        mSurface = window;
        createWindowSurface(mSurface);
        mReleaseSurface = releaseSurface;
    }
    
    WindowSurface::WindowSurface(EglCore *eglCore, ANativeWindow *window)
            : EglSurfaceBase(eglCore) {
        createWindowSurface(window);
        mSurface = window;
    }
    
    void WindowSurface::release() {
        releaseEglSurface();
        if (mSurface != NULL) {
            ANativeWindow_release(mSurface);
            mSurface = NULL;
        }
    
    }
    
    void WindowSurface::recreate(EglCore *eglCore) {
        assert(mSurface != NULL);
        if (mSurface == NULL) {
            ALOGE("not yet implemented ANativeWindow");
            return;
        }
        mEglCore = eglCore;
        createWindowSurface(mSurface);
    }
    

    OffscreenSurface.h:

    #ifndef CAINCAMERA_OFFSCREENSURFACE_H
    #define CAINCAMERA_OFFSCREENSURFACE_H
    
    
    #include "EglSurfaceBase.h"
    
    class OffscreenSurface : public EglSurfaceBase {
    public:
        OffscreenSurface(EglCore *eglCore, int width, int height);
        void release();
    };
    
    #endif //CAINCAMERA_OFFSCREENSURFACE_H
    

    OffscreenSurface.cpp:

    #include "OffscreenSurface.h"
    
    OffscreenSurface::OffscreenSurface(EglCore *eglCore, int width, int height)
            : EglSurfaceBase(eglCore) {
        createOffscreenSurface(width, height);
    }
    
    void OffscreenSurface::release() {
        releaseEglSurface();
    }
    

    到这里,我们就在Native实现了跟Grafika 的Java层一样的EGL封装。在封装好EGL 之后,我们需要一个GLRender,跟Java 层 GLSurfaceView 差不多。废话不多说,直接上源码:
    GLRender.h:

    #ifndef EGLNATIVESAMPLE_GLRENDER_H
    #define EGLNATIVESAMPLE_GLRENDER_H
    
    
    #include <android/native_window.h>
    #include "../caingles/EglCore.h"
    #include "../caingles/WindowSurface.h"
    #include "Triangle.h"
    
    class GLRender {
    public:
        GLRender();
    
        virtual ~GLRender();
    
        void surfaceCreated(ANativeWindow *window);
    
        void surfaceChanged(int width, int height);
    
        void surfaceDestroyed(void);
    
    private:
        EglCore *mEglCore;
        WindowSurface *mWindowSurface;
        Triangle *mTriangle;
    };
    #endif //EGLNATIVESAMPLE_GLRENDER_H
    

    GLRender.cpp:

    #include "GLRender.h"
    #include "../caingles/GlUtils.h"
    
    #include <GLES2/gl2.h>
    #include <GLES2/gl2ext.h>
    #include <GLES2/gl2platform.h>
    #include <assert.h>
    
    GLRender::GLRender() {
        mEglCore = NULL;
        mWindowSurface = NULL;
    }
    
    GLRender::~GLRender() {
        if (mEglCore) {
            mEglCore->release();
            delete mEglCore;
            mEglCore = NULL;
        }
    }
    
    void GLRender::surfaceCreated(ANativeWindow *window) {
    
        if (mEglCore == NULL) {
            mEglCore = new EglCore(NULL, FLAG_RECORDABLE);
        }
        mWindowSurface = new WindowSurface(mEglCore, window, false);
        assert(mWindowSurface != NULL && mEglCore != NULL);
        mWindowSurface->makeCurrent();
        mTriangle = new Triangle();
        mTriangle->init();
    }
    
    void GLRender::surfaceChanged(int width, int height) {
        mWindowSurface->makeCurrent();
        mTriangle->onDraw(width, height);
        mWindowSurface->swapBuffers();
    }
    
    void GLRender::surfaceDestroyed() {
        if (mTriangle) {
            mTriangle->destroy();
            delete mTriangle;
            mTriangle = NULL;
        }
        if (mWindowSurface) {
            mWindowSurface->release();
            delete mWindowSurface;
            mWindowSurface = NULL;
        }
        if (mEglCore) {
            mEglCore->release();
            delete mEglCore;
            mEglCore = NULL;
        }
    }
    

    其中Triangle是使用OpenGLES绘制三角形的类,实现如下:
    Triangle.h:

    #ifndef EGLNATIVERENDER_TRIANGLE_H
    #define EGLNATIVERENDER_TRIANGLE_H
    
    #include "../caingles/GlUtils.h"
    #include "../caingles/GlShaders.h"
    #include "../common/native_log.h"
    
    class Triangle {
    public:
        Triangle();
    
        virtual ~Triangle();
    
        int init(void);
    
        void onDraw(int width, int height);
    
        void destroy();
    
    private:
        int programHandle;
    };
    
    #endif //EGLNATIVERENDER_TRIANGLE_H
    

    Triangle.cpp:

    #include "Triangle.h"
    
    const GLint COORDS_PER_VERTEX = 3;
    const GLint vertexStride = COORDS_PER_VERTEX * 4;
    
    Triangle::Triangle() {}
    
    Triangle::~Triangle() {
    
    }
    
    int Triangle::init() {
        char vertexShader[] =
                "#version 300 es\n"
                        "layout(location = 0) in vec4 a_position;\n"
                        "layout(location = 1) in vec4 a_color;\n"
                        "out vec4 v_color;"
                        "void main()\n"
                        "{\n"
                        "   gl_Position = a_position;\n"
                        "   v_color = a_color;\n"
                        "}\n";
    
        char fragmentShader[] =
                "#version 300 es\n"
                        "precision mediump float;\n"
                        "in vec4 v_color;\n"
                        "out vec4 fragColor;\n"
                        "void main()\n"
                        "{\n"
    //                    "   fragColor = vec4 ( 1.0, 0.0, 0.0, 1.0 );\n"
                        "   fragColor = v_color;\n"
                        "}\n";
        programHandle = createProgram(vertexShader, fragmentShader);
        if (programHandle <= 0) {
            return -1;
        }
        glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
        return 0;
    }
    
    
    void Triangle::onDraw(int width, int height) {
        GLfloat vertices[] = {
                0.0f,  0.5f, 0.0f,
                -0.5f, -0.5f, 0.0f,
                0.5f, -0.5f, 0.0f
        };
    
        GLfloat color[] = {
                1.0f, 0.0f, 0.0f, 1.0f
        };
    
        GLint vertexCount = sizeof(vertices) / (sizeof(vertices[0]) * COORDS_PER_VERTEX);
    
        glViewport(0, 0, width, height);
    
        glClear(GL_COLOR_BUFFER_BIT);
    
        glUseProgram(programHandle);
    
        GLint positionHandle = glGetAttribLocation(programHandle, "a_position");
        glVertexAttribPointer(positionHandle, COORDS_PER_VERTEX, GL_FLOAT, GL_FALSE, vertexStride, vertices);
        glEnableVertexAttribArray(positionHandle);
        glVertexAttrib4fv(1, color);
    
        glDrawArrays(GL_TRIANGLES, 0, vertexCount);
    
        glDisableVertexAttribArray(positionHandle);
    }
    
    void Triangle::destroy() {
        if (programHandle > 0) {
            glDeleteProgram(programHandle);
        }
        programHandle = -1;
    }
    
    Looper线程的实现

    由于在使用OpenGLES时需要单独一个线程空间,可参考GLSurfaceview的源码,GLSurfaceView就是通过开辟一个Looper 线程GLThread来调用EGL的API实现的。因此,我们也要创建一个Looper 线程,但Native层并没有提供跟Java层那么好用的Looper,只提供了ALooper,但个人觉得ALooper 使用并不是很友好。Looper的底层是基于epoll来实现的。我们这里不用epoll,而是采用信号量来搭建Looper,可以参考Google官方的android-ndk项目中的native-codec,native-codec项目就是用了信号量来创建Looper,并调用AMediaCodec渲染的。我们也有样学样,直接封装一个比较实用的Looper线程。废话不多数,直接上代码:
    Looper.h:

    #ifndef CAINPLAYER_LOOPER_H
    #define CAINPLAYER_LOOPER_H
    
    #include <pthread.h>
    #include <sys/types.h>
    #include <semaphore.h>
    
    struct LooperMessage {
        int what;
        int arg1;
        int arg2;
        void *obj;
        LooperMessage *next;
        bool quit;
    };
    
    class Looper {
    
    public:
        Looper();
        Looper&operator=(const Looper& ) = delete;
        Looper(Looper&) = delete;
        virtual ~Looper();
    
        // 发送消息
        void postMessage(int what, bool flush = false);
        void postMessage(int what, void *obj, bool flush = false);
        void postMessage(int what, int arg1, int arg2, bool flush = false);
        void postMessage(int what, int arg1, int arg2, void *obj, bool flush = false);
    
        // 退出Looper循环
        void quit();
    
        // 处理消息
        virtual void handleMessage(LooperMessage *msg);
    
    private:
        // 添加消息
        void addMessage(LooperMessage *msg, bool flush);
    
        // 消息线程句柄
        static void *trampoline(void *p);
    
        // 循环体
        void loop(void);
    
        LooperMessage *head;
        pthread_t worker;
        sem_t headwriteprotect;
        sem_t headdataavailable;
        bool running;
    
    };
    
    #endif //CAINPLAYER_LOOPER_H
    

    Looper.cpp:

    #include "Looper.h"
    
    #include <jni.h>
    #include <pthread.h>
    #include <stdio.h>
    #include <string.h>
    #include <unistd.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <semaphore.h>
    #include "native_log.h"
    
    struct LooperMessage;
    typedef struct LooperMessage LooperMessage;
    
    
    
    /**
     * 线程执行句柄
     * @param p
     * @return
     */
    void* Looper::trampoline(void* p) {
        ((Looper*)p)->loop();
        return NULL;
    }
    
    
    Looper::Looper() {
        head = NULL;
    
        sem_init(&headdataavailable, 0, 0);
        sem_init(&headwriteprotect, 0, 1);
        pthread_attr_t attr;
        pthread_attr_init(&attr);
    
        pthread_create(&worker, &attr, trampoline, this);
        running = true;
    }
    
    Looper::~Looper() {
        if (running) {
            ALOGD("Looper deleted while still running. Some messages will not be processed");
            quit();
        }
    }
    
    /**
     * 入队消息
     * @param what
     * @param flush
     */
    void Looper::postMessage(int what, bool flush) {
        postMessage(what, 0, 0, NULL, flush);
    }
    
    /**
     * 入队消息
     * @param what
     * @param obj
     * @param flush
     */
    void Looper::postMessage(int what, void *obj, bool flush) {
        postMessage(what, 0, 0, obj, flush);
    }
    
    /**
     * 入队消息
     * @param what
     * @param arg1
     * @param arg2
     * @param flush
     */
    void Looper::postMessage(int what, int arg1, int arg2, bool flush) {
        postMessage(what, arg1, arg2, NULL, flush);
    }
    
    /**
     *
     * @param what
     * @param arg1
     * @param arg2
     * @param obj
     * @param flush
     */
    void Looper::postMessage(int what, int arg1, int arg2, void *obj, bool flush) {
        LooperMessage *msg = new LooperMessage();
        msg->what = what;
        msg->obj = obj;
        msg->arg1 = arg1;
        msg->arg2 = arg2;
        msg->next = NULL;
        msg->quit = false;
        addMessage(msg, flush);
    }
    
    /**
     * 添加消息
     * @param msg
     * @param flush
     */
    void Looper::addMessage(LooperMessage *msg, bool flush) {
        sem_wait(&headwriteprotect);
        LooperMessage *h = head;
    
        if (flush) {
            while(h) {
                LooperMessage *next = h->next;
                delete h;
                h = next;
            }
            h = NULL;
        }
        if (h) {
            while (h->next) {
                h = h->next;
            }
            h->next = msg;
        } else {
            head = msg;
        }
        ALOGD("post msg %d", msg->what);
        sem_post(&headwriteprotect);
        sem_post(&headdataavailable);
    }
    
    /**
     * 循环体
     */
    void Looper::loop() {
        while(true) {
            // wait for available message
            sem_wait(&headdataavailable);
    
            // get next available message
            sem_wait(&headwriteprotect);
            LooperMessage *msg = head;
            if (msg == NULL) {
                ALOGD("no msg");
                sem_post(&headwriteprotect);
                continue;
            }
            head = msg->next;
            sem_post(&headwriteprotect);
    
            if (msg->quit) {
                ALOGD("quitting");
                delete msg;
                return;
            }
            ALOGD("processing msg %d", msg->what);
            handleMessage(msg);
            delete msg;
        }
    }
    
    /**
     * 退出Looper循环
     */
    void Looper::quit() {
        ALOGD("quit");
        LooperMessage *msg = new LooperMessage();
        msg->what = 0;
        msg->obj = NULL;
        msg->next = NULL;
        msg->quit = true;
        addMessage(msg, false);
        void *retval;
        pthread_join(worker, &retval);
        sem_destroy(&headdataavailable);
        sem_destroy(&headwriteprotect);
        running = false;
    }
    
    /**
     * 处理消息
     * @param what
     * @param data
     */
    void Looper::handleMessage(LooperMessage *msg) {
        ALOGD("dropping msg %d %p", msg->what, msg->obj);
    }
    

    此时,我们封装了一个Looper 基类,里面集成了跟Handler一样的消息队列,这里就不再需要创建MessageQueue了。
    接下来,我们在使用的时候,可以继承的Looper,实现MyLooper对象:

    MyLooper.h

    #ifndef EGLNATIVESAMPLE_MYLOOPER_H
    #define EGLNATIVESAMPLE_MYLOOPER_H
    
    #include "Looper.h"
    #include "../cainrender/GLRender.h"
    
    enum {
        kMsgSurfaceCreated,
        kMsgSurfaceChanged,
        kMsgSurfaceDestroyed
    };
    
    class MyLooper : public Looper {
    
    public:
        MyLooper();
    
        virtual ~MyLooper();
    
        virtual void handleMessage(LooperMessage *msg);
    
    private:
        GLRender *mRender;
    
    };
    
    #endif //EGLNATIVESAMPLE_MYLOOPER_H
    

    MyLooper.cpp:

    #include "MyLooper.h"
    
    MyLooper::MyLooper() {
        mRender = new GLRender();
    }
    
    void MyLooper::handleMessage(LooperMessage *msg) {
        switch (msg && msg->what) {
            case kMsgSurfaceCreated:
                if (mRender) {
                    mRender->surfaceCreated((ANativeWindow *) msg->obj);
                }
                break;
    
            case kMsgSurfaceChanged:
                if (mRender) {
                    mRender->surfaceChanged(msg->arg1, msg->arg2);
                }
                break;
    
            case kMsgSurfaceDestroyed:
                if (mRender) {
                    mRender->surfaceDestroyed();
                }
                break;
        }
    }
    
    MyLooper::~MyLooper() {
        delete mRender;
    }
    

    至此,我们在Native层实现了自己的EGL以及GLRender的封装。那么接下来我们应该怎么使用呢?请看下面的步骤:
    1、在MainActivity中添加一个SurfaceView,并实现SurfaceHolder.Callback 方法。加载nativeEgl库,并创建几个native方法,如下所示:

    public class MainActivity extends AppCompatActivity implements SurfaceHolder.Callback {
    
        // Used to load the 'native-lib' library on application startup.
        static {
            System.loadLibrary("nativeEgl");
        }
        private native void nativeInit();
        private native void nativeRelease();
        private native void onSurfaceCreated(Surface surface);
        private native void onSurfaceChanged(int width, int height);
        private native void onSurfaceDestroyed();
    
        private SurfaceView mSurfaceView;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            nativeInit();
            mSurfaceView = (SurfaceView) findViewById(R.id.surface_view);
            mSurfaceView.getHolder().addCallback(this);
        }
    
    
        @Override
        protected void onDestroy() {
            nativeRelease();
            super.onDestroy();
        }
    
        @Override
        public void surfaceCreated(SurfaceHolder holder) {
            onSurfaceCreated(holder.getSurface());
        }
    
        @Override
        public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
            onSurfaceChanged(width, height);
        }
    
        @Override
        public void surfaceDestroyed(SurfaceHolder holder) {
            onSurfaceDestroyed();
        }
    
    }
    

    native层实现上面的方法如下:
    NativeEglController.cpp:

    #include <jni.h>
    #include "common/Looper.h"
    #include "common/MyLooper.h"
    #include <android/native_window.h>
    #include <android/native_window_jni.h>
    
    MyLooper *mLooper = NULL;
    ANativeWindow *mWindow = NULL;
    
    extern "C"
    JNIEXPORT void JNICALL
    Java_com_cgfay_eglnativerender_MainActivity_nativeInit(JNIEnv *env, jobject instance) {
    
        // TODO
        mLooper = new MyLooper();
    }
    
    extern "C"
    JNIEXPORT void JNICALL
    Java_com_cgfay_eglnativerender_MainActivity_nativeRelease(JNIEnv *env, jobject instance) {
        if (mLooper != NULL) {
            mLooper->quit();
            delete mLooper;
            mLooper = NULL;
        }
        if (mWindow) {
            ANativeWindow_release(mWindow);
            mWindow = NULL;
        }
    }
    
    extern "C"
    JNIEXPORT void JNICALL
    Java_com_cgfay_eglnativerender_MainActivity_onSurfaceCreated(JNIEnv *env, jobject instance,
                                                                 jobject surface) {
        if (mWindow) {
            ANativeWindow_release(mWindow);
            mWindow = NULL;
        }
        mWindow = ANativeWindow_fromSurface(env, surface);
        if (mLooper) {
            mLooper->postMessage(kMsgSurfaceCreated, mWindow);
        }
    }
    
    extern "C"
    JNIEXPORT void JNICALL
    Java_com_cgfay_eglnativerender_MainActivity_onSurfaceChanged(JNIEnv *env, jobject instance,
                                                                 jint width, jint height) {
        if (mLooper) {
            mLooper->postMessage(kMsgSurfaceChanged, width, height);
        }
    
    }
    
    extern "C"
    JNIEXPORT void JNICALL
    Java_com_cgfay_eglnativerender_MainActivity_onSurfaceDestroyed(JNIEnv *env, jobject instance) {
    
        if (mLooper) {
            mLooper->postMessage(kMsgSurfaceDestroyed);
        }
    
    }
    

    编译运行程序,就可以看到Demo成功绘制了一个红色的三角形,如下图所示:


    运行结果

    使用方式跟我们Java层使用GLSurfaceView没什么两样,至此,我们就封装好EGL + C/C++ 编写OpenGLES的基本框架。Github地址:EGLNativeRender

    相关文章

      网友评论

      • glumes:Android 中用的是 EGL14,在 Native 中有 14 11 这样的版本区分嘛?
        glumes:@cain_huang 这样啊,感谢回复:anger:
        cain_huang:@蓄意碎碎 似乎没有。如果需要兼容API17以前的设备,那就需要区分,否则后面都是EGL14以及兼容14的。Native层的EGL是要求动态检测API是否可用的。有些API在11版本是没有的

      本文标题:Android EGL + C/C++ 编写OpenGLES程序

      本文链接:https://www.haomeiwen.com/subject/fsxuhftx.html