无需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的使用。
参考
- Android off-screen rendering using EGL pixelbuffers and OpenGL ES 2.0