Android OpenGL ES 3.0 入门常见问题记录

Android OpenGL ES 3.0 入门常见问题记录,第1张

Android OpenGL ES 3.0 入门常见问题记录 前言

本文将会记录笔者在学习 OpenGL ES 过程中,遇到的问题及解决问题的参考办法。同时也提供了一些 Debug 问题的分析思路和技巧。

以下代码基于 Kotlin 和 C++。

更新记录 日期更新内容2022.01.16新增 Debug 技巧 Debug 技巧 一、检查 Shader 创建问题

创建 Shader 的大致步骤如下:

// 创建一个 shader,并返回它的 handle
// 其中,输入参数 shaderType 为想创建的 shader 类型,
// 一般是 GLES30.GL_VERTEX_SHADER 或 GLES30.GL_FRAGMENT_SHADER
val shaderHandle = GLES30.glCreateShader(shadeType)
// 将预先准备好的源码与该 shader 绑定
GLES30.glShaderSource(shaderHandle, sourceCode)
// 编译 shader
GLES30.glCompileShader(shaderHandle)

一般,问题容易出现在 GLES30.glCompileShader() 阶段,极少情况下,GLES30.glCreateShader() 时会发生异常。

shader 的 handle 一定是大于 0 的,可以用这一点来判断 glCreateShader() 是否发生异常。可以用 GLES30.glGetShaderiv()1 和 GLES30.glGetShaderInfoLog()2 来判断 glCompileShader() 是否发生异常。

基于以上考虑,笔者将创建 shader 的过程封装成一个函数:

fun createShader(shaderType: Int, shaderCode: String): Int {
	val shaderTypeStr = when (shaderType) {
    	GLES30.GL_VERTEX_SHADER -> "Vertex Shader"
        GLES30.GL_FRAGMENT_SHADER -> "Fragment Shader"
        else -> "Unsupported type"
    }
    // !!!注意!!!DEBUG 为笔者自己加的调试开关,在该函数外定义
    if (DEBUG) {
		Log.d(TAG, "create [$shaderTypeStr] source code $shaderCode")
	}
	// 如果创建的不是顶点着色器或片段着色器,打印一条警示 Log
	if (type != GLES30.GL_VERTEX_SHADER && type != GLES30.GL_FRAGMENT_SHADER) {
        Log.w(TAG, "create shader maybe failed: unsupported shader type, your input $type")
    }
    return GLES30.glCreateShader(shaderType).also {
        if (DEBUG) {
			Log.d(TAG, "created [shaderTypeStr] handle: $it")
		}
        GLES30.glShaderSource(it, shaderCode)
        GLES30.glCompileShader(it)
        if (DEBUG) {
            // 检查 shader 编译的状态
            val compileStatus = IntBuffer.allocate(1)
            GLES30.glGetShaderiv(it, GLES30.GL_COMPILE_STATUS, compileStatus)
            Log.d(
                TAG,
                "compile [$shaderTypeStr] status: ${if (compileStatus[0] == GLES30.GL_TRUE) "success" else "failed"}"
            )
            // 如果编译失败,打印出错误信息
            if (compileStatus[0] == GLES30.GL_FALSE) {
            	Log.e(TAG, "compile [$shaderTypeStr] failed, info: ${GLES30.glGetShaderInfoLog(it)}")
            }
        }
    }
}
二、检查 program 创建状态

创建 program 的大致步骤如下:

// 创建并得到一个 program handle
val programHandle = GLES30.glCreateProgram()
// 绑定顶点着色器
GLES30.glAttachShader(programHandle, vertexShaderHandle)
// 绑定片段着色器
GLES30.glAttachShader(programHandle, fragmentShaderHandle)
// 链接该 program
GLES30.gllinkProgram(programHandle)

这里的三种方法调用都有可能发生错误。
创建 program 也可以封装成一个函数,所以可以这么做:

fun createProgram(vertex: Int, fragment: Int): Int = GLES30.glCreateProgram().also {
	// 检验输入参数的合法性
	if (vertex <= 0 || fragment <= 0) {
		Log.w(TAG, "invalid argument: vertex $vertex, fragment $fragment")
	}
	GLES30.glAttachShader(it, vertex)
	if (DEBUG) {
		checkGLError("Attach Vertex Shader")
	}
	GLES30.glAttachShader(it, fragment)
	if (DEBUG) {
		checkGLError("Attach Fragment Shader")
	}
	GLES30.gllinkProgram(it)
	if (DEBUG) {
		val linkStatus = IntBuffer.allocate(1)
		GLES30.glGetProgramiv(it, GLES30.GL_link_STATUS, linkStatus)
		if (linkStatus[0] == GLES30.GL_TRUE) {
			Log.d(TAG, "link program success!")
		} else {
			Log.e(
                TAG,
                "link program failed! info: ${GLES30.glGetProgramInfoLog(it)}"
            )
		}
	}
}

fun checkGLError(what: String) {
	Log.w(TAG, "[$what] error code: ${GLES30.glGetError()}")
}
问题列表 1、确认 shader 代码正常,但编译 shader、创建 program 失败

OpenGL 的接口调用必须要在 EGL 上下文创建完成后进行。例如,如果是使用 GLSurface,那应该是在 GLSurface.Render.onSurfaceCreated() 回调方法之后执行 OpenGL *** 作。如果是自定义 Render,谨记不要在 Render 的构造函数中调用。

结束语

OpenGL 提供的 状态/信息/错误 查询接口,尽量不要在线上环境使用。因为这涉及到 CPU 与 GPU 间通信,太过频繁容易影响到性能。

2、native 发生 Crash

如果是 native 发生了 Crash,原因有很多,例如空指针。建议借助 llvm-symbolizer 工具定位出错行。

笔者这里遇到的比较奇葩,是非 void 函数,未返回值发生的 Crash。举例:

unsigned int createframebuffer() {
	// ......
	// 做了一些列 *** 作
	// 最后没有返回值
}

让我很费解的是,这种错误应该编译期就报出的,不知道为什么到运行期才报错。个人认为这是 NDK 的一个 Bug。

参考资料
  1. glGetShaderiv ↩︎

  2. glGetShaderInfoLog ↩︎

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5709625.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存