glGetError和性能

Mue*_*ito 9 c++ opengl performance opengl-es

背景

在工作中,我们开发了两种产品,它们都具有OpenGL 3.x +和GLES 2.0/3.0 +后端.团队是独立的,但确实有一些重叠,我们最近讨论的表现glGetError.

在这两种产品中,设计都是这样的,没有GL调用应该生成记录的错误代码glGetError.为了检测这样的错误,在调试中我们有一个宏,它glGetError在每个GL调用之后添加一个,并且如果检测到任何错误它会断言,因为这意味着存在错误.在我的产品上,默认情况下启用此功能,另一方面,必须明确启用它.

这些已存在于我工作多年的产品的代码库中,我们发现它们会导致性能下降,通常在许多平台上都会达到25%左右.我们已经确定这是一个合理的价格,以便及早发现错误.另一个团队在某些情况下声称,添加这些检查可能会使其产品在60FPS下的执行速度降至<1FPS,从而使产品无法使用,这就是默认情况下不启用它们的原因.这两款产品均可在许多OpenGL/GLES平台(PC,OSX,Linux,iOS和Android)上运行.

问题

我理解glGetError降低性能背后的原因; 您(可能)需要CPU/GPU同步才能使上一个操作的状态正确.根据我的理解,这应该将预期的帧时间从" MAX(CPU time, GPU time)"(假设没有其他同步点,没有排队的帧)更改为" CPU time + GPU time + synchronization overheap"(假设每个 glGetError调用产生一个同步点).这是不正确的推理,还是有使用性能降低的其他原因glGetError

我一直认为glGetError调试中的每次调用是合理的事情(至少在没有错误的GL调用之后).是不是这种情况还是不被视为"最佳做法"?是否存在一些可能导致极端性能问题的情况,例如其他团队描述的问题(例如,使用特定的GL调用和/或平台)?

Ret*_*adi 10

glGetError()真的不必等待GPU的任何事情.它报告的所有错误都来自检查API调用的参数,以及驱动程序管理的内部状态.所以CPU/GPU同步不会在这里发挥作用.

唯一可能出现延迟的错误是GL_OUT_OF_MEMORY,但规范相对公开("可能会生成"),所以它也不是同步的原因.

我可以想到为什么glGetError()在每次API 调用之后调用可能会显着降低性能的两个原因:

  • 你做两倍的OpenGL调用.调用本身以及检查和返回错误状态都有开销.虽然打电话glGetError()一次可能不是很贵,但如果你打电话数百万次,它就会增加.
  • 一些驱动程序在驱动程序中使用多线程.在这种情况下,glGetError()将导致驱动程序中的线程之间的同步,如果它经常发生,则可能会产生非常大的性能影响.

至于你应该做什么,你真的必须找出有效的方法.一些想法/建议:

  • 我绝对不会glGetError()在发布版本中调用.它在调试期间非常有用,但一旦您的测试/ QA完成,就会产生不必要的开销.
  • 错误很棘手.因此,如果您只想知道是否有任何错误,则无需glGetError()在每次通话后拨打电话.例如,您可以在每帧结束时调用一次.当然,如果您收到错误,并且想知道哪个呼叫导致它,则需要更频繁的呼叫.所以你可以拥有多种构建类型.

    • 无需glGetError()调用即可发布版本.
    • glGetError()在每帧结束时使用调用进行测试/ QA构建.
    • glGetError()每次OpenGL调用后调用构建调用.


And*_*man 5

查询错误状态可能需要某种 CPU/GPU 同步,但我认为它被夸大了。这完全不像读回仍在进行中或待处理的渲染操作的结果。错误状态是在执行命令之前验证和设置的东西,它通常会提醒您无效的 API 使用或状态设置,但不会有太多其他内容。

现代 OpenGL 实现具有用于跟踪调试信息的更复杂的扩展/核心功能,简称为“调试输出”。您已经标记了此 OpenGL 和 OpenGL ES,因此它可能不适合您软件的所有部署,但是当使用此功能可用的 OpenGL 或 ES 实现时,它确实应该是您的首选解决方案。您当然会收到错误信息,但还会收到有关弃用和性能之类的警告(这实际上取决于驱动程序的冗长程度,我已经看到一些驱动程序提供了非常出色的警告,而其他驱动程序则根本不使用该功能)。

您可以同步运行调试输出,这可能会引入您在问题中讨论的性能损失,或者异步运行,这往往对性能更友好,但在尝试实时追踪问题原因时不太有用。没有一刀切的解决方案,这就是为什么调试输出比glGetError (...).