我目前正在尝试创建一个业余游戏引擎,并且经常遇到非常基本的错误(例如,我更改了节点层次结构代码,但突然不显示任何内容)。我将使用的一些方法来查找这些错误,包括:


使用我的IDE的调试器和许多printf调用来查看内存中的内容。使用不同的参数进行测试,将相机放置在不同的位置,以更好地了解代码的作用。

这些方法对我来说似乎都是缓慢而低效的。问题在于,3D渲染涉及许多不直观的操作,除了最终的“产品”之外,几乎没有可视化的内容。当我遇到一个错误(尤其是“什么都没有显示”类型的错误)时,我常常觉得自己盲目地摸索。


所以。

假设您是一名使用Unity,Godot或其他3D引擎的开发人员,而您只是更改了渲染管线中的某些步骤,现在,无论出于何种原因,当实体批处理为活性。 “导线”在屏幕上纵横交错,而不是代表批处理实体的三角形的边缘。

如何跟踪并修复该错误?您有特定的工作流程吗?您是否使用某种GPU分析工具?还是只是盲目摸索?

换句话说,引擎开发人员(而非引擎用户)可以使用哪些方法,工具和最佳做法来分析其3D图形代码的数据和行为(不仅仅是其性能)以有效解决缺陷和错误?

评论

有专门的图形调试器,例如Nvidia Nsight,甚至是具有许多功能的可视化工作室的内置调试器。大多数工具都可以拍摄屏幕截图,选择像素并查看其到达方式的详细历史记录。

#1 楼

我主要在OpenGL中编程。在这种情况下,我要做的第一件事就是设置KHR_debug扩展。它提供了一种订阅GL驱动程序生成的消息的方法-从错误到警告,再到有关驱动程序正在执行的详细信息。特别是开源Linux驱动程序(Mesa)会生成许多有用的消息。

在工具方面,我的首选通常是ApiTrace。它允许您记录所有渲染API调用的跟踪,然后在任何时间点重播并检查状态(这包括缓冲区,渲染目标等的内容)。它还允许您与其他人共享跟踪,这在团队合作中非常有用。然后,还有其他工具,例如RenderDoc,NVIDIA Tegra图形调试器,Xcode的图形调试器等。无论选择哪种方法,都可以大大改善您的工作。

另一件事是测试。一件事是构建一组简单的场景,以行使引擎的单个功能(例如,不同的材质设置,不同的动画技巧等)。可以轻松地分析易于使用的测试用例),并验证修复程序不会意外破坏其他功能。我想这很普遍。例如,Unity3D开发人员可以做到这一点。

您可以通过创建测试工具来对其进行扩展和自动化,该工具将定期构建和运行所有测试,并验证是否没有发生回归。如果您有很多测试场景,这可能是可行的,尽管显然需要对基础架构进行额外的投资。

然后是更多的“标准”单元测试。我应用它们的一个领域是基于节点的材质系统,它可以针对不同的渲染技术(转发,递延等)和API。我通过编写单元测试来实现它,该单元测试从材质和其他设置生成着色器代码,对其进行编译,并使用给定的输入(像素着色器输入)发出简单的绘制调用。将输出写入渲染目标并获取以与预期值进行比较。恕我直言,这是一件好事,特别是当您具有复杂的物料系统时。最后,可以将更多“传统”方法应用于可以与渲染分离的区域。可以对空间数据结构,资产加载等类似事物进行单元/集成测试,以适用于特定情况。

编辑:这是Unity3D开发人员的帖子,描述了它们如何测试图形代码。

评论


$ \ begingroup $
Re:场景测试,我目前正在使用glTF示例场景来测试我的引擎;我可能会在某个时候构建更复杂的场景,以更好地测试出了什么问题。
$ \ endgroup $
– Narateur du chaos
18年6月7日在2:11

$ \ begingroup $
我忘记了要包含的有关Unity3D方法的链接-我已经编辑了答案以包含它。
$ \ endgroup $
– joe_chip
18年6月7日在21:39

#2 楼

我通常更喜欢RenderDoc调试框架。为您提供有关从Vertex到Pixel渲染的特定帧的足够信息。最后,还有其他同样有用的工具,例如Nvidia Nsight

,总是有颜色编码,在视觉上调试着色器时很有用。将着色器输出分解为单独的颜色也有帮助。不确定是否需要您的案子。

希望有帮助。

评论


$ \ begingroup $
然后我将研究RenderDoc。回复:着色器,有没有可用的工具可以为您提供更强大的调试功能(例如,中介变量的值),还是基于颜色的调试是标准的?
$ \ endgroup $
– Narateur du chaos
18年6月7日在2:07

$ \ begingroup $
我刚刚在RenderDoc上花了1小时,我已经可以确认它的用处了。这使得难以理解的神秘难解的错误混合在一起。非常感谢!
$ \ endgroup $
– Narateur du chaos
18年6月7日在5:50



#3 楼

我主要在几乎不存在OpenGL工具的macOS上工作。 (我开始进入Metal的工具更好的地方。)所以我感到您的痛苦。

我要做的第一件事是频繁地,少量地将代码提交给源代码管理。并非总是可能的,但通常是这样。如果那会干扰其他人,请在分支机构上工作。这样,如果出了什么大问题,我可以对HEAD或trunk进行当前代码的快速差异化,并将问题仅隔离在差异中。

我下一步要做的是尝试分析问题所在。在您的示例中,以前使用的几何图形突然绘制了错误的形状,我会考虑可能导致以下情况的情况:


glBufferData()发送虚假指针

通过错误设置属性指针/偏移量,从缓冲区的错误字节开始

有许多常见症状的原因,例如令人恐惧的黑屏。有许多技术与printf()的图形等效。我开始分解有效的方法和无效的方法。我从笔挺开始。我的图纸去了正确的FBO吗?将FBO的透明颜色设置为类似于RGB =(1,0.5,0)或(0,1,1)的颜色,然后查看屏幕是否变为橙色或品红色。您可能会为通常使用的每个FBO选择不同的调试颜色,以便一眼就能看出要在给定渲染器上进行显示的屏幕。如果可行,我会检查诸如裁剪平面之类的东西-是否已裁剪所有几何图形?检查照明-我是否打开灯?我是否将正确的照明信息发送到着色器?纹理正确吗?进行一些带有诸如网格之类的纹理的测试纹理可能会有所帮助,或者,如果您要处理的是立方体贴图,则其表面带有名称的纹理也可能会有所帮助。如果存在着色器问题,则可以执行一些操作,例如简单地在每个点将纹理坐标输出为红色和绿色(如果是3D纹理,则输出为蓝色)以确保它们有效。与法线相同-将它们归一化并在xyz通道中输出rgb。并验证您的假设。能够快速,轻松地回滚非常有用。当然,不要忘记glGetError()。它返回的少量错误令人沮丧,但通常可以将您指出问题所在。

如果您可以找到一个工具来显示与默认OpenGL状态的不同,它也可以指示您将精力花在哪里。在macOS上有OpenGL Profiler。您可以在任何OpenGL函数上设置断点并检查整个状态机。它将突出显示与默认值的差异,或与上一次停止的差异,或两者。