调试OpenGL ES应用程序

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_38498942/article/details/100108742

大家好,今天小白给大家简单介绍下几种调试OpenGL ES应用程序的方法,欢迎一起交流学习.

一, 使用glGetError

OpenGL ES API是一个严格的C API,这意味着它检测到的任何错误都会通过异常进行报告。 OpenGL ES应用程序检测错误的基本方法是使用glGetError调用。 可以在绑定了渲染上下文的任何线程中发出此调用。


如果进行了无效调用,则可以返回一系列不同的错误代码。 通常的规则是,如果OpenGL ES检测到错误,则违规命令不会导致OpenGL ES状态的任何修改,也不会栅格化任何样本。 错误代码GL_OUT_OF_MEMORY是一个值得注意的例外。 当驱动程序端的内存分配请求失败时,会报告此错误。 如果应用程序检测到此错误,则应假定OpenGL ES状态未定义。 它可以采取的最安全的步骤是终止该过程。

OpenGL ES可以报告的所有错误代码是:

1, GL_INVALID_ENUM  - 传递给函数的GLenum参数之一无效。 导致此问题的一个常见原因是,当该扩展实际上不可用时,开发人员会传递定义为OpenGL ES扩展的一部分的枚举值。 另一个原因是
价值是不正确的。

2, GL_INVALID_FRAMEBUFFER_OPERATION  - 调用需要绘制和/或读取帧缓冲区的API函数,但事实并非如此。 请注意,并非所有API函数都需要这样做。 如果是OpenGL ES,也可以报告此错误代码实现不支持渲染到特定的帧缓冲区配置,在这种情况下,应用程序应该为相关的帧缓冲区使用不同的内部格式集。

3, GL_INVALID_OPERATION  - 在给定当前OpenGL ES状态配置的情况下,尝试执行的操作无效。 此错误代码含糊不清,必须缩小违规命令以查找错误原因。 对于大型应用,尤其如此可能不是一项微不足道的任务。

4, GL_INVALID_VALUE  - 传递给函数的数字参数无效。 这通常是出于类似于为GL_INVALID_ENUM描述的原因而发生的。

5, GL_OUT_OF_MEMORY  - 尝试执行命令时驱动程序内存不足。 应用程序应尽快终止,因为工作环境不能再被认为是稳定的。

上面给出了错误的一般描述,但更具体的解释取决于导致错误的命令。

glGetError不告诉应用程序哪个命令报告了错误,并且驱动程序隐式调用的OpenGL ES命令可以报告错误; 例如,当驾驶员先前已决定推迟诸如平局呼叫之类的呼叫时,并且现在在处理另一命令的过程中在内部发出呼叫。

在内部,当驱动程序在API调用中检测到错误时,它会引发与其中一个代码对应的标志。如果检测到更多错误,它们不会影响记录的错误状态。调用glGetError时,将返回缓存的错误代码并降低标志,以便后续的glGetError调用返回GL_NO_ERROR。


由于Adreno驱动程序在命令缓冲区中缓存API调用,因此glGetError调用可能被认为是昂贵的,因为它们会刷新管道并等待所有缓冲命令的完成。除非性能对应用程序不重要,否则不建议在每次API调用之后插入glGetError调用,至少不在发布版本中。一个很好的折衷方案是在战略位置插入保护glGetError调用,仅用于调试版本。运用
这种方法,性能在生产构建中不受影响,并且在错误报告到达的情况下,团队准备可用于定位导致问题的渲染管道部分的调试版本不会花费很长时间。

二, 使用GL_KHR_debug

使用glGetError有一些重大限制。

1, 错误代码不会传达有关错误类型的准确信息。

2, 要解决问题,程序员必须首先确定哪个OpenGL ES API调用引起了错误,然后必须查看更大的图片,例如OpenGL ES状态,以了解为什么错误代码是产生。 这需要开发时间相反,它可以花在实现新功能上。

3, 没有办法让驱动程序对应用程序进行回调,以便程序员可以插入断点来找出出错的地方和位置。

Adreno驱动程序支持名为GL_KHR_debug的特殊扩展,旨在满足这些需求并包含许多功能以增强OpenGL的调试体验
ES开发人员。


以下部分重点介绍与调试OpenGL ES应用程序最相关的功能,并提供API概述。 有关详细信息,请参阅:

https://www.opengl.org/registry/specs/KHR/debug.txt.

当支持GL_KHR_debug扩展时,应用程序可以通过调用glDebugMessageCallbackKHR来注册驱动程序回调。 此函数允许注册回调函数指针和可选的用户指定参数。 OpenGL ES使用该回调, 以可读的以NULL结尾的字符串形式向应用程序提供反馈。 反馈可以包含有关生成错误代码的原因的详细信息,以及以下类型的信息:

1, 有关使用已弃用功能的警告,或在规范中标记为未定义的内容的警告
2, 依赖于实现的性能警告
3, 有关以特定于供应商的方式使用扩展或着色器的警告
4, 用户注入的消息
5, 调试组堆栈通知(如下所述)
6, 每条反馈消息都附有一组枚举,提供有关以下内容的信息:
7, 消息的来源(驱动程序,着色器编译器,窗口系统等)
8, 消息的ID

9, 消息类型(错误报告,性能警告,可移植性提示等)
10, 消息的严重级别(高,中,低,通知)

仅当通过调用glEnable启用GL_DEBUG_OUTPUT_STATE_KHR时才会发生回调。要再次禁用该功能,请为相同的状态枚举调用glDisable。

应用程序可以通过调用glDebugMessageInsertKHR将自己的消息注入调试流。这对于可以使用该机制向开发人员提供提示或检测到任何错误情况的通知的中间件特别有用。

所有由驱动程序生成或使用glDebugMessageInsertKHR插入命令流的消息都将写入活动调试组,该调试组是调试组堆栈的顶部。可以通过调用gl-PushDebugGroupKHR将新的调试组(由用户指定的消息标识)压入堆栈。可以通过调用glPopDebugGroupKHR从堆栈中弹出现有的调试组。每当调试组被推入或弹出时
调试组堆栈,已与该组关联的消息将插入到流中。一个示例用例是使用调试组来标记每个的开始和结束
渲染通道。

应用程序可以通过调用glDebugMessageControlKHR来过滤掉不需要的消息。消息的任何属性都可以用作过滤密钥。这被称为音量控制。音量控制设置适用于活动的调试组,如果将新的调试组推入堆栈,则将继承该设置。

如果应用程序未注册回调函数但启用了GL_DEBUG_OUTPUT_STATE_KHR,则消息将存储在消息日志中。日志可以容纳
GL_MAX_DEBUG_LOGGED_MESSAGES消息。一旦存储器填满,任何随后生成的消息将被丢弃,直到应用程序通过获取一个或多个消息释放一些空间为止。可以通过调用获取消息glGetDebugMessageLogKHR,它返回消息字符串和每条消息的相关属性。GL_KHR_debug扩展提供的另一个有用功能是能够以两种不同的模式提供回调信息:

1, 异步模式(默认情况下是活动的) -  OpenGL ES实现可以从多个线程同时调用调试回调例程,包括生成消息的上下文当前未绑定到的线程(示例包括但不限于,其他线程) 绑定上下文,或驱动程序内部使用的线程)。 它也可以在生成的OpenGL ES命令之后异步发出回调消息已经返回。 当异步模式处于活动状态时,应用程序有责任确保线程安全。

2, 同步模式 - 每个渲染上下文不允许驱动程序一次发出多个回调。 回调将在允许生成调试消息的OpenGL ES命令返回之前进行。 同步模式会导致所有调用都隐式刷新,因此性能会大大降低。 但是,鉴于回调发生在OpenGL ES API调用时,此模式极大地简化了开发人员的调试过程。

可以通过调用glEnable for显式启用同步模式, 即GL_DEBUG_OUTPUT_SYNCHRONOUS_KHR模式。 要使应用程序返回异步调试模式,可以为同一个枚举调用glDisable。

GL_KHR_debug扩展还允许开发人员使用函数glObjectLabelKHR和glObjectPtrLabelKHR使用以NULL结尾的字符串标记OpenGL ES对象。 稍后可以使用glGetObjectLabelKHR或glGetObjectPtrLabelKHR检索标签。 这有助于在调试过程中轻松识别OpenGL ES对象,而无需遍历应用程序的内部数据模型。

三, shader调试

可能出现这样的情况:渲染故障不是由不正确的OpenGL ES API使用引起的,而是由构成绘制调用所使用的程序的其中一个着色器中隐藏的错误引起的。 以下是有关如何处理此类情况的一些提示。

1, 首先确保为顶点着色器定义的所有输入属性都传递正确的值。 例如,如果照明没有按预期工作,首先要验证用于计算的正常数据是否有效,例如,通过传递检查未经修改的片段着色器的正常数据,通过目视检查验证每个顶点是否分配了正确的矢量值。

注意: 修改着色器时要小心。 例如,如果开发人员注释掉现有实现并将其替换为代码以将法向量传递给片段着色器,则可能导致许多现有输入属性和制服变为非活动状态。 根据应用程序和着色器的编写方式,这可能会使错误更难以追踪。 除了删除整个主体之外,安排可能有助于结果值的所有其他变量乘以一个非常小的值,例如,对于每个组件使用8位的内部格式,为1.0 / 256.0,这样它们就不会隐藏 正在检查的结果值。

2, 对着色器使用的所有制服执行相同操作。 特别注意统一块成员。 确保着色器定义应用程序假定的相同统一块布局。

3, 如果怀疑某些计算可能执行不正确,请使用转换反馈将数据从渲染管道传输回进程。 变换反馈允许检查数据是否通过整个渲染管道正确传递,片段着色器阶段除外。 在开始使用几何体和/或曲面细分控制/评估着色器时,这一点变得尤为重要。

4, 如果平台支持几何着色器,则使用它们来发射辅助几何体,例如法向量。

5, 如果某些纹理未正确采样,则可能认为它们不完整。 检查此问题的最简单方法是选择GL_NEAREST缩小过滤,并将GL_TEXTURE_BASE_LEVEL和GL_TEXTURE_MAX_LEVEL设置为已上载的mipmap的索引。

6, 如果纹理仍然无法访问,请确保没有采样器对象覆盖已绑定该纹理的纹理单元的纹理参数。 验证采样器制服是否正确
设置为使用相应的纹理单位。

7, 使用Adreno Profiler工具编辑着色器并实时调查结果。

四, 总结

本篇主要是简单介绍了OpenGL ES应用程序的几种调试方式, 欢迎一起交流学习.

文章创建于:
展开阅读全文

OpenGL ES运行出错

02-21

最近学OpenGL ES,想在手机上画个球体。用的是极坐标生成顶点,运行会出错。我已经用native order了,还是报错rnrn02-21 23:12:28.710: E/OpenGLES(3314): Application com.gl.demo (SDK target 8) called a GL11 Pointer method with an indirect Buffer.rn02-21 23:12:28.726: W/dalvikvm(3314): threadid=9: thread exiting with uncaught exception (group=0x40018560)rn02-21 23:12:28.734: E/AndroidRuntime(3314): FATAL EXCEPTION: GLThread 10rn02-21 23:12:28.734: E/AndroidRuntime(3314): java.lang.IllegalArgumentException: Must use a native order direct Bufferrn02-21 23:12:28.734: E/AndroidRuntime(3314): at com.google.android.gles_jni.GLImpl.glVertexPointerBounds(Native Method)rn02-21 23:12:28.734: E/AndroidRuntime(3314): at com.google.android.gles_jni.GLImpl.glVertexPointer(GLImpl.java:1121)rn02-21 23:12:28.734: E/AndroidRuntime(3314): at com.gl.demo.Ball.draw(Ball.java:87)rn02-21 23:12:28.734: E/AndroidRuntime(3314): at com.gl.demo.HelloRender.onDrawFrame(HelloRender.java:53)rn02-21 23:12:28.734: E/AndroidRuntime(3314): at android.opengl.GLSurfaceView$GLThread.guardedRun(GLSurfaceView.java:1364)rn02-21 23:12:28.734: E/AndroidRuntime(3314): at android.opengl.GLSurfaceView$GLThread.run(GLSurfaceView.java:1119)rnrn下面是Ball类的代码:rn[code=Java]public class Ball rnrn private FloatBuffer vBuffer;//定点缓存rn rn private ShortBuffer indexBuffer;//定点索引缓存rn rn private int vCount;//顶点数量rn private int iCount;//索引数量rn rn public Ball(int scale)rn ArrayList verties=new ArrayList();rn ArrayList alIndex=new ArrayList();rn int span=18;rn rn for(int v=0;v<=180;v+=span)rn for(int h=0;h<=360;h+=span)rn float r=(float)(scale*Math.sin(Math.toRadians(v)));rn float x=(float)(r*Math.cos(Math.toRadians(h)));rn float y=(float)(scale*Math.cos(Math.toRadians(v)));rn float z=(float)(r*Math.sin(Math.toRadians(h)));rn rn verties.add(x);rn verties.add(y);rn verties.add(z);rn rn rn vCount=verties.size()/3;rn rn float[] v=new float[vCount*3];rn for(int i=0;i 论坛

openGL es 的示例问题

07-28

大家好,小弟初学OPenGL es.在一个相关的技术网站上下了一个例子,但却运行不了。rn代码和出错如下:rn#pragma comment(lib, "libGLES_CM.lib")rnrn#define GLUTES_STATICrnrn#include rnrnrnvoid init()rnrn glClearColor (0.0f, 0.0f, 0.0f, 0.0f);rnrnrnvoid display()rnrn glClear (GL_COLOR_BUFFER_BIT);rnrn glFlush ();rn glutSwapBuffers();rnrnrnvoid keyboard2(int key, int x, int y)rnrn switch(key)rn rn case GLUT_KEY_UP : exit(0); break;rn rnrnrnvoid menu(int entry)rnrn switch(entry)rn rn case 1 : exit(0); break;rn rnrnrnint _tmain(int argc, TCHAR *argv[])rnrn glutInit(&argc, (char**)argv);rn glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA);rnrn glutCreateWindow("06 - Rendering");rnrn init();rnrn glutDisplayFunc(display);rn glutSpecialFunc(keyboard2);rnrn glutCreateMenu(menu);rn glutAddMenuEntry("Quit", 1);rnrn glutAttachMenu(GLUT_LEFT_BUTTON);rnrn glutMainLoop();rnrn return 0;rnrnrnrn------ 已启动生成: 项目: aa, 配置: Debug Pocket PC 2003 (ARMV4) ------rn正在编译...rnaa.cpprn正在链接...rnglutes_static.lib(glutes_main.obj) : error LNK2019: 无法解析的外部符号 main,该符号在函数 WinMain 中被引用rnPocket PC 2003 (ARMV4)\Debug/aa.exe : fatal error LNK1120: 1 个无法解析的外部命令rn生成日志保存在“file://e:\test\aa\aa\Pocket PC 2003 (ARMV4)\Debug\BuildLog.htm”rnaa - 2 个错误,0 个警告rn========== 生成: 0 已成功, 1 已失败, 0 最新, 0 已跳过 ==========rnrnrn出错为main()?rn是不是pocket pc不技术main()这样的非unicode函数?改为_tmain()后,又出现别的错误。rn现在一个例子也没调出来,希望高手帮看看 论坛

没有更多推荐了,返回首页