NDK OpenGL ES 3 编译C/C++可执行文件(无需JNI调用)

无需JNI调用是指将C/C++代码编译成Linux可执行程序,不需打包成APK或APK + JNI调用。此方式适用于已Root的手机。

这里的使用场景是OpenGL ES作为通用计算单元,只处理数据,不用显示到屏幕。也可以考虑OpenCL、RenderScript,不过OpenCL只有部分Android设备才支持。

运行环境为Android 6.0。

1、Android.mk编译脚本

Android.mk文件内容如下:

LOCAL_PATH := $(call my-dir)

include $(CLEAR-VARS)
LOCAL_MODULE := opengles_module
LOCAL_MODULE_FILENAME := libopengles_module
LOCAL_SRC_FILES := *.cpp
LOCAL_CFLAGS += -pie -fPIE
LOCAL_LDFLAGS += -pie -fPIE
LOCAL_LDLIBS += -lGLESv3 -lEGL -landroid

include $(BUILD_EXECUTABLE)

PIE安全机制从4.1引入,Android L之前的系统并不会去检验可执行文件是否基于PIE编译出的,但是Android L已经开启验证,如果调用的可执行文件不是基于PIE方式编译的,则无法运行并提示错误error: only position independent executables (PIE) are supported.。解决办法为在Android.mk中加入LOCAL_LDFLAGS += -pie -fPIE即可。

因为OpenGL ES系统与本地窗口系统连接在Android系统中需要配置EGL,在iOS中为EAGL,-lEGL -landroid完成了链接任务。当然-lGLESv3也是必须的,不然用不了OpenGL ES 3.0。

使用OpenGL ES 3.0是因为它提供了新功能Transform Feedback,允许用户读取Vertex Shader的计算结果,设置不进行光栅化,省掉渲染管线后续流程。而OpenGL ES 2.0只能读取Fragment Shader的处理结果。虽然,对于做通用计算的场合,Fragment Shader是多余的,只想利用Vertex Shader作并行处理单元,而无需屏幕显示界面。但是,还得为OpenGL ES提供Fragment Shader,空操作就行。

2、配置EGL

首先,定义后面用的变量。

static EGLConfig eglConf;
static EGLSurface eglSurface;
static EGLContext eglCtx;
static EGLDisplay eglDisp;

现在创建EGL环境,只有设置EGLContext为当前绘图上下文才能使用OpenGL ES接口,否则它们都调用失败。

void setupEGL(int w, int h) {
    // EGL config attributes
    const EGLint confAttr[] = {
        EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,    // very important!
        EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,          // we will create a pixelbuffer surface
        EGL_RED_SIZE,   8,
        EGL_GREEN_SIZE, 8,
        EGL_BLUE_SIZE,  8,
        EGL_ALPHA_SIZE, 8,     // if you need the alpha channel
        EGL_DEPTH_SIZE, 16,    // if you need the depth buffer
        EGL_NONE
    };
    
    // EGL context attributes
    const EGLint ctxAttr[] = {
        EGL_CONTEXT_CLIENT_VERSION, 3,              // 初始化3.0的上下文
        EGL_NONE
    };
    
    // surface attributes
    // the surface size is set to the input frame size
    const EGLint surfaceAttr[] = {
        EGL_WIDTH, w,
        EGL_HEIGHT, h,
        EGL_NONE
    };
    
    EGLint eglMajVers, eglMinVers;
    EGLint numConfigs;
    
    eglDisp = eglGetDisplay(EGL_DEFAULT_DISPLAY);
    eglInitialize(eglDisp, &eglMajVers, &eglMinVers);
    
    printf("EGL init with version %d.%d", eglMajVers, eglMinVers);
    
    // choose the first config, i.e. best config
    eglChooseConfig(eglDisp, confAttr, &eglConf, 1, &numConfigs);
    
    eglCtx = eglCreateContext(eglDisp, eglConf, EGL_NO_CONTEXT, ctxAttr);
    
    // create a pixelbuffer surface
    eglSurface = eglCreatePbufferSurface(eglDisp, eglConf, surfaceAttr);
    
    eglMakeCurrent(eglDisp, eglSurface, eglSurface, eglCtx);
}

3、检查OpenGL ES版本

有很多种办法获取当前Android设备支持的OpenGL ES版本,直接打印也行:

static void printGLString(const char *name, GLenum s) {
    const char *v = (const char *) glGetString(s);
    printf("GL %s = %s\\n", name, v);
}

void printOpenGLInfo() {
    printGLString("Version", GL_VERSION);
    printGLString("Vendor", GL_VENDOR);
    printGLString("Renderer", GL_RENDERER);
    printGLString("Extensions", GL_EXTENSIONS);
}

如果输出类似Version = OpenGL ES 3.1,那就可以用Transform Feedback。

4、使用Transform Feedback编程

因内容较多,另起一篇文档描述Transform Feedback的使用。

参考

  1. Android off-screen rendering using EGL pixelbuffers and OpenGL ES 2.0

你可能感兴趣的:(NDK OpenGL ES 3 编译C/C++可执行文件(无需JNI调用))