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的。