NDK GLES2.0动态加载GLES3.0的原理

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



已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页