为什么OpenGL最初被设计成状态机?

13
在我使用OpenGL的经验中,我经常忘记设置一些状态。因此,我认为状态机可能不是一个好的设计。但现在它必须兼容。我也知道DirectX在早期也是一个状态机。我想知道为什么OpenGL和DirectX最初被设计成状态机?

2
使用状态机,您每次调用时需要传递较少的参数(尽管如果您频繁更改状态,则可能需要进行更多的调用)。如果您看一下事物随着时间的推移而发展的方式,现在是关于命令流,即发送数据块以更改状态,而不是调用许多小的状态更改函数。 - Robinson
2个回答

28

当人们提到“状态机”时,他们从未解释相反的含义,因此不清楚其意思。因此,我将在OpenGL“状态机”与当前D3D“非状态机”方面从一般和特定的角度进行讨论。

OpenGL和Direct3D都使用全局状态。每个渲染命令都需要设置一堆状态。

为了在这两个API中进行渲染,您必须设置一些全局状态。您必须设置要使用的着色器。您必须设置要使用这些统一变量的当前参数。如果您正在使用纹理,则需要设置它们。您需要设置当前视口参数等等。

采用这种“状态机”的原因很简单:这是硬件通常的工作方式。

每个状态位代表GPU中的一些寄存器。这些寄存器是状态。着色器需要加载才能进行渲染。您需要设置视口寄存器。您需要设置要使用哪些纹理寄存器。等等。因此,这些API是状态机,因为GPU是状态机。

你可以想象这是通过渲染命令完成的。但是请看一下需要传递的对象数量。你需要传递一堆着色器、一堆纹理、顶点数据、帧缓冲区、视口设置、混合设置等等。
因此,API要求您事先设置好所有这些内容。
此外,这使得API更快。为什么呢?因为现在API知道您正在使用哪种状态。如果要渲染同一网格的不同部分,并使用不同的纹理,您可以保持帧缓冲区、视口、顶点数据等全部相同。您只需更改使用的纹理即可。
如果您使用某种大型绘制调用并传递了数十个参数,那么API现在必须查看每个参数以查看它是否与上次绘制调用相同。如果不同,它必须更新GPU寄存器。
至于OpenGL和D3D之间的差异,在这种情况下,问题的差异是它们如何处理对象。
D3D是基于对象的,修改对象的函数需要将对象作为参数。此外,大多数D3D对象都是不可变的;一旦创建,就无法更改它们的大多数设置。例如,一旦创建了某个大小、格式等的纹理,就无法使用不同的大小/格式等重新分配它,必须删除对象并创建一个新对象。
OpenGL是基于状态的。这意味着OpenGL函数(大部分)不会将其操作对象作为参数。
这不是“设计”,而是OpenGL严格遵循向后兼容性的结果。在OpenGL 1.0中,最初没有任何对象(除了显示列表)。是的,甚至没有纹理对象。当他们认为这是愚蠢的,并且他们需要对象时,他们决定以向后兼容的方式实现它们。每个人都已经在使用操作全局状态的函数。所以他们只是说通过绑定对象,您可以覆盖全局状态。那些曾经改变全局状态的函数现在改变对象的状态。
以这种方式,他们可以将对象引入API,而不必引入一堆仅适用于对象的新函数。因此,之前运行的代码只需进行微小调整就能与对象一起工作,而不是强制进行繁琐的重写。这也意味着,如果他们需要引入针对纹理的新功能,它们将与对象一起使用且无需修改。因此,它既向后兼容又向前兼容。 大多数OpenGL对象都是这样工作的:如果您想更改它们,您必须绑定它们,然后修改“全局”状态。

3
感谢您耐心的解释。 - Shen Yang
有趣的是,您对“为什么”的解释让我理解了OpenGL的“如何”。 - dawid

13

来自http://www.cs.tufts.edu/research/graphics/resources/OpenGL/OpenGL.htm:

OpenGL操作如状态机是为了避免在每个函数调用上传递许多参数。

最小参数传递

应用程序和OpenGL之间的通信必须最终通过工作站数据总线传递到图形硬件。数据总线通常比工作站CPU和图形硬件慢得多,因此通常可以成为高速图形的瓶颈。为了保持尽可能高的速度,OpenGL在其函数调用中尽可能少地传递参数,这可以通过查看OpenGL文档来看到。所有调用都假定许多内部变量先前已设置为所需值。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接