用于包装SDL和OpenGL绘图方法的虚函数的替代方法

时间:2011-03-08 03:43:35

标签: c++ design-patterns opengl sdl virtual-functions

我正在尝试用C ++为游戏编写一些样板代码。我想使用SDL和OpenGL绘制。例如,我想在OpenGL和SDL中编写2个版本的函数“DrawPixel”。我不能同时使用SDL和OpenGL进行渲染,但是我想要一个通用的绘图界面,所以在实现游戏时我不需要写两倍。

我可以用虚拟的所有绘图函数编写一个基类,然后为SDL和OpenGL重载它的2个版本。问题在于性能,因为我可能最终每秒呼叫DrawPixel百万次和数百万次,解决虚拟呼叫将是昂贵的。

除了这种OO方法,我还有什么其他选择? (或者你可以告诉我虚拟并不慢)

2 个答案:

答案 0 :(得分:3)

在我回答您的问题之前,您是否考虑过将其设为编译时标志,例如#define - 这可以让您决定GL或SDL,而不会浪费空间或需要虚拟呼叫。如果可以的话,一定要找到这个解决方案。

虚函数只是一个额外的指针查找,调用指针引用的函数而不是常量值。 - 它们有一个很大的缺点,就是它们无法内联,因为编译器不知道函数的位置。这将是任何在运行时决定函数的程序的问题。

我将从一个警告开始:这些可能是微观优化,不值得担心。我或许会考虑做性能基准测试,看看它有多重要。例如,GL和SDL调用已经在一个单独的库中,因此它们中的每一个可能已经需要指针查找等。额外的指针可能是您最不关心的问题。

如果你真的无法在编译时决定,唯一避免虚拟调用的简单方法就是使用C ++模板来完成这项工作。在您的情况下,我将为您需要的每个GL或SDL函数创建两个单独的类,并命名相同,但不要将它们设置为虚拟。然后,整个程序中使用这些函数的每个函数/类都需要声明为模板,使用SDL或GL。最后,在你的顶级main()函数中,有一个if语句,它根据你想要的版本运行一个或另一个版本的程序。

这将导致一个完美有效的解决方案,甚至可以内联所有可以内联的功能;但是,以两份程序副本和占用更多空间为代价。另外 - 因为它会导致更大的程序,这实际上可能会降低性能。

如果你不想要模板,一种可能也有用的奇怪方法是让加载器使用两个不同的动态链接库中的一个。两个库都会导出相同的函数,然后您可以选择加载其中一个(例如,通过将LD_LIBRARY_PATH设置为一个目录)

答案 1 :(得分:1)

虽然我确实认为#defines和模板是一种更加“干净利落”的编译时间方式,但纯粹是轶事说明:

现代编译器能够完全优化虚函数调用,如果它可以证明调用只能与一个类相关而从不与另一个类相关(例如,如果你创建一个类型A的对象并且永远不会通过指针访问它)另一种类型)。您还可以使用范围解析运算符(例如A :: draw())明确告诉编译器此信息。

您还应该知道,大多数GL函数在任何一种情况下都是“虚拟的”(因为您通过函数指针访问它们,这是相同的),并且虚拟函数不是像非内联函数一样存在的问题。现代CPU经过优化,可以很好地处理虚拟函数调用的“虚拟”部分,但仍然没有内联这些函数,这必然会使它们变慢(但是,并不比其他非内联函数慢得多)。 p>

一般来说,“每秒百万次,数百万次”对于编译器无法内联的小函数来说并不是一个好主意。

如果你想在游戏中绘制数百万像素,你最好画一个纹理四边形,这是两个或三个函数调用,并在硬件中运行。逐个绘制像素通常不是人们想要做的。