OpenGL ES3.0是兼容2.0的,这样从代码层面就可以,使用ES3.0的库写出兼容ES2.0的代码,通过判断在ES2.0上避免调用3.0的特性就可以了。但是Android studio的工程需要设定API platform的版本,ES3.0的版本apk无法安装在ES2.0的设备上。
而在ES2.0的平台上,无法连接ES3.0的函数。所以,这里就需要动态加载ES3.0的函数,在ES2.0的平台上。 NDK的demo gles3jni给出了个解决方案,代码在这里https://github.com/googlesamples/android-ndk/tree/master/gles3jni 文本主要解释其原理,如何在ES2.0的平台调用ES3.0的函数。
首先,我们如果使用了Android 18以前的版本,那么我们在连接的时候,无法看见头文件<GLES3/gl3.h>的,就算手动拷贝了gl3的头文件也是无法连接到GLESv3的,只能连接到GLESv2。
cmake_minimum_required(VERSION 3.4.1) # set targetPlatform, will be passed in from gradle when this sample is completed # openGL Supportability # platform status # (0 11) ES2/ES3 not supported # [11, 18) ES2 only, for ES3, app do dynamic load # [18, 24) ES2 & ES3 # [24, infinite) ES2 & ES3 & Vulkan set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -fno-rtti -fno-exceptions -Wall") if (${ANDROID_PLATFORM_LEVEL} LESS 11) message(FATAL_ERROR "OpenGL 2 is not supported before API level 11 (currently using ${ANDROID_PLATFORM_LEVEL}).") return() elseif (${ANDROID_PLATFORM_LEVEL} LESS 18) add_definitions("-DDYNAMIC_ES3") set(OPENGL_LIB GLESv2) else () #set(OPENGL_LIB GLESv3) endif (${ANDROID_PLATFORM_LEVEL} LESS 11) add_library(gles3jni SHARED gl3stub_wrapper.c gles3jni.cpp RendererES2.cpp RendererES3.cpp) # Include libraries needed for gles3jni lib target_link_libraries(gles3jni ${OPENGL_LIB} android EGL log m)这里cmakelist.txt通过判断api平台版本动态选择加载openGL的so文件,值得说明的是,在gradle设置里面,如果abiFilters设置有几个选项,cmakelist.txt就会执行几次,并且如果有arm64的话,就一定会编译ES3.0的库。好了,如果我们选择了ES2.0的平台,就没有了gl3头文件,demo里面给出了一个gl3stub.h头文件和一个gl3stub.c.inc实现文件。我来解释一下原理。用一个函数的例子来说明。
extern GL_APICALL void (* GL_APIENTRY glBindVertexArray) (GLuint array);这是ES3.0的一个函数,头文件的声明使用了函数指针形式,而不是标准gl3文件里的函数原型。添加extern关键字,说明实现在别处,也就是在gl3stub.c.inc中。真正的实现是在设备驱动里面的,这里的实现只是一个函数指针。
GL_APICALL void (* GL_APIENTRY glBindVertexArray) (GLuint array);这就是实现,一个函数指针,这样编译连接的时候ES2.0的so文件无法提供这个函数地址,我们自己提供了一个函数地址,这样就连接成功了。但是,这个地址并没有功能,需要在设备上运行的时候查找,这个函数的真正实现来替换这个指针。这就需要EGL提供的一个函数了,通过函数名,找到函数地址。 EGLAPI __eglMustCastToProperFunctionPointerType EGLAPIENTRY eglGetProcAddress(const char *procname); GLboolean gl3stubInit() { #define FIND_PROC(s) s = (void*)eglGetProcAddress(#s) FIND_PROC(glReadBuffer); FIND_PROC(glDrawRangeElements); FIND_PROC(glTexImage3D); FIND_PROC(glTexSubImage3D); FIND_PROC(glCopyTexSubImage3D); FIND_PROC(glCompressedTexImage3D); FIND_PROC(glCompressedTexSubImage3D); FIND_PROC(glGenQueries); FIND_PROC(glDeleteQueries); FIND_PROC(glIsQuery); FIND_PROC(glBeginQuery); FIND_PROC(glEndQuery); FIND_PROC(glGetQueryiv); FIND_PROC(glGetQueryObjectuiv); FIND_PROC(glUnmapBuffer); FIND_PROC(glGetBufferPointerv); FIND_PROC(glDrawBuffers); FIND_PROC(glUniformMatrix2x3fv); FIND_PROC(glUniformMatrix3x2fv); FIND_PROC(glUniformMatrix2x4fv); FIND_PROC(glUniformMatrix4x2fv); FIND_PROC(glUniformMatrix3x4fv); FIND_PROC(glUniformMatrix4x3fv); FIND_PROC(glBlitFramebuffer); FIND_PROC(glRenderbufferStorageMultisample); FIND_PROC(glFramebufferTextureLayer); FIND_PROC(glMapBufferRange); FIND_PROC(glFlushMappedBufferRange); FIND_PROC(glBindVertexArray); FIND_PROC(glDeleteVertexArrays); FIND_PROC(glGenVertexArrays); FIND_PROC(glIsVertexArray); FIND_PROC(glGetIntegeri_v); FIND_PROC(glBeginTransformFeedback); FIND_PROC(glEndTransformFeedback); FIND_PROC(glBindBufferRange); FIND_PROC(glBindBufferBase); FIND_PROC(glTransformFeedbackVaryings); FIND_PROC(glGetTransformFeedbackVarying); FIND_PROC(glVertexAttribIPointer); FIND_PROC(glGetVertexAttribIiv); FIND_PROC(glGetVertexAttribIuiv); FIND_PROC(glVertexAttribI4i); FIND_PROC(glVertexAttribI4ui); FIND_PROC(glVertexAttribI4iv); FIND_PROC(glVertexAttribI4uiv); FIND_PROC(glGetUniformuiv); FIND_PROC(glGetFragDataLocation); FIND_PROC(glUniform1ui); FIND_PROC(glUniform2ui); FIND_PROC(glUniform3ui); FIND_PROC(glUniform4ui); FIND_PROC(glUniform1uiv); FIND_PROC(glUniform2uiv); FIND_PROC(glUniform3uiv); FIND_PROC(glUniform4uiv); FIND_PROC(glClearBufferiv); FIND_PROC(glClearBufferuiv); FIND_PROC(glClearBufferfv); FIND_PROC(glClearBufferfi); FIND_PROC(glGetStringi); FIND_PROC(glCopyBufferSubData); FIND_PROC(glGetUniformIndices); FIND_PROC(glGetActiveUniformsiv); FIND_PROC(glGetUniformBlockIndex); FIND_PROC(glGetActiveUniformBlockiv); FIND_PROC(glGetActiveUniformBlockName); FIND_PROC(glUniformBlockBinding); FIND_PROC(glDrawArraysInstanced); FIND_PROC(glDrawElementsInstanced); FIND_PROC(glFenceSync); FIND_PROC(glIsSync); FIND_PROC(glDeleteSync); FIND_PROC(glClientWaitSync); FIND_PROC(glWaitSync); FIND_PROC(glGetInteger64v); FIND_PROC(glGetSynciv); FIND_PROC(glGetInteger64i_v); FIND_PROC(glGetBufferParameteri64v); FIND_PROC(glGenSamplers); FIND_PROC(glDeleteSamplers); FIND_PROC(glIsSampler); FIND_PROC(glBindSampler); FIND_PROC(glSamplerParameteri); FIND_PROC(glSamplerParameteriv); FIND_PROC(glSamplerParameterf); FIND_PROC(glSamplerParameterfv); FIND_PROC(glGetSamplerParameteriv); FIND_PROC(glGetSamplerParameterfv); FIND_PROC(glVertexAttribDivisor); FIND_PROC(glBindTransformFeedback); FIND_PROC(glDeleteTransformFeedbacks); FIND_PROC(glGenTransformFeedbacks); FIND_PROC(glIsTransformFeedback); FIND_PROC(glPauseTransformFeedback); FIND_PROC(glResumeTransformFeedback); FIND_PROC(glGetProgramBinary); FIND_PROC(glProgramBinary); FIND_PROC(glProgramParameteri); FIND_PROC(glInvalidateFramebuffer); FIND_PROC(glInvalidateSubFramebuffer); FIND_PROC(glTexStorage2D); FIND_PROC(glTexStorage3D); FIND_PROC(glGetInternalformativ); #undef FIND_PROC if (!glReadBuffer || !glDrawRangeElements || !glTexImage3D || !glTexSubImage3D || !glCopyTexSubImage3D || !glCompressedTexImage3D || !glCompressedTexSubImage3D || !glGenQueries || !glDeleteQueries || !glIsQuery || !glBeginQuery || !glEndQuery || !glGetQueryiv || !glGetQueryObjectuiv || !glUnmapBuffer || !glGetBufferPointerv || !glDrawBuffers || !glUniformMatrix2x3fv || !glUniformMatrix3x2fv || !glUniformMatrix2x4fv || !glUniformMatrix4x2fv || !glUniformMatrix3x4fv || !glUniformMatrix4x3fv || !glBlitFramebuffer || !glRenderbufferStorageMultisample || !glFramebufferTextureLayer || !glMapBufferRange || !glFlushMappedBufferRange || !glBindVertexArray || !glDeleteVertexArrays || !glGenVertexArrays || !glIsVertexArray || !glGetIntegeri_v || !glBeginTransformFeedback || !glEndTransformFeedback || !glBindBufferRange || !glBindBufferBase || !glTransformFeedbackVaryings || !glGetTransformFeedbackVarying || !glVertexAttribIPointer || !glGetVertexAttribIiv || !glGetVertexAttribIuiv || !glVertexAttribI4i || !glVertexAttribI4ui || !glVertexAttribI4iv || !glVertexAttribI4uiv || !glGetUniformuiv || !glGetFragDataLocation || !glUniform1ui || !glUniform2ui || !glUniform3ui || !glUniform4ui || !glUniform1uiv || !glUniform2uiv || !glUniform3uiv || !glUniform4uiv || !glClearBufferiv || !glClearBufferuiv || !glClearBufferfv || !glClearBufferfi || !glGetStringi || !glCopyBufferSubData || !glGetUniformIndices || !glGetActiveUniformsiv || !glGetUniformBlockIndex || !glGetActiveUniformBlockiv || !glGetActiveUniformBlockName || !glUniformBlockBinding || !glDrawArraysInstanced || !glDrawElementsInstanced || !glFenceSync || !glIsSync || !glDeleteSync || !glClientWaitSync || !glWaitSync || !glGetInteger64v || !glGetSynciv || !glGetInteger64i_v || !glGetBufferParameteri64v || !glGenSamplers || !glDeleteSamplers || !glIsSampler || !glBindSampler || !glSamplerParameteri || !glSamplerParameteriv || !glSamplerParameterf || !glSamplerParameterfv || !glGetSamplerParameteriv || !glGetSamplerParameterfv || !glVertexAttribDivisor || !glBindTransformFeedback || !glDeleteTransformFeedbacks || !glGenTransformFeedbacks || !glIsTransformFeedback || !glPauseTransformFeedback || !glResumeTransformFeedback || !glGetProgramBinary || !glProgramBinary || !glProgramParameteri || !glInvalidateFramebuffer || !glInvalidateSubFramebuffer || !glTexStorage2D || !glTexStorage3D || !glGetInternalformativ) { return GL_FALSE; } return GL_TRUE; } 在设备上运行这个函数,如果支持ES3.0,就会把连接时候没有实现的函数指针的值,替换成真正驱动实现的函数地址。这样就可以正确调用了。 另外,我有个思路,还没有验证,既然ES3.0兼容ES2.0。那么我用ES3.0的so文件连接,打包,在代码中判断了OpenGL的版本来控制函数的调用。这样打包后修改API的平台到低版本上,设备运行的时候动态判断了ES3.0的函数可用性,自然也是可以兼容2.0的。
