我现在正在建模一些OpenGL库,以便玩弄图形编程等。因此,我使用类来包装特定的OpenGL函数调用,例如纹理创建、着色器创建等等。到目前为止一切都很好。
我的问题:
所有的OpenGL调用必须由拥有创建的OpenGL上下文的线程完成(至少在Windows下,其他线程将不起作用并创建一个OpenGL错误)。因此,为了获得一个OpenGL上下文,我首先创建一个窗口类的实例(只是另一个对Win API调用的包装器),最后为该窗口创建一个OpenGL上下文。这听起来对我来说非常合乎逻辑。(如果我的设计中已经有缺陷让你尖叫,请告诉我...)
如果我想创建一个纹理或任何需要OpenGL调用创建的对象,我基本上会这样做(一个OpenGL对象的构造函数示例):
opengl_object()
{
//do necessary stuff for object initialisation
//pass object to the OpenGL thread for final contruction
//wait until object is constructed by the OpenGL thread
}
所以,简单来说,我使用以下方式创建一个像任何其他对象一样的对象:
opengl_object obj;
在构造函数中,它将自己放入OpenGL对象队列中,以便由OpenGL上下文线程创建。然后,OpenGL上下文线程调用一个虚拟函数,该函数在所有OpenGL对象中都有实现,并包含必要的OpenGL调用以最终创建对象。
我曾经认为,这种处理问题的方式很好。然而,现在我觉得我非常错了。
问题是,尽管上述方法到目前为止完全正常,但是当类层次结构变得更深时,我遇到了麻烦。例如(这并不完美,但它展示了我的问题):
假设我有一个名为sprite的类,表示一个精灵。它有自己的OpenGL线程创建函数,在其中顶点和纹理坐标被加载到显卡内存中等等。到目前为止没有问题。 再假设,我想要有两种渲染精灵的方式。一种是实例化的,另一种是通过其他方式。因此,我最终会得到两个类,即sprite_instanced和sprite_not_instanced。它们都派生自sprite类,因为它们都是只以不同的方式呈现的精灵。但是,sprite_instanced和sprite_not_instanced需要在其创建函数中进一步进行OpenGL调用。
到目前为止,我的解决方案(我感到非常糟糕!)
我对C++中的对象生成方式以及它对虚拟函数的影响有一些了解。因此,我决定仅使用sprite类的虚拟创建函数将顶点数据等加载到图形内存中。然后,sprite_instanced的虚拟创建方法将准备好以实例化的方式呈现该精灵。 因此,如果我想要编写
sprite_instanced s;
首先,精灵构造函数被调用并进行了一些初始化,构造线程将对象传递给OpenGL线程。此时,传递的对象仅是普通精灵,因此将调用 sprite::create 并且 OpenGL 线程将创建普通精灵。之后,构造线程将再次调用 sprite_instanced 的构造函数,再做一些初始化并将对象传递给OpenGL线程。但这一次,它是 sprite_instanced,因此将调用 sprite_instanced::create。
所以,如果以上假设是正确的,那么在我的情况下,一切都按照预期发生。我花费了最后一个小时阅读有关从构造函数调用虚函数以及如何构建虚表等方面的内容。我运行了一些测试来检查我的假设,但那可能是特定于编译器的,所以我不会100%依赖它们。此外,这种方法感觉很糟糕,像一个可怕的黑客。
另一个解决方案
另一个可能性是在OpenGL线程类中实现工厂方法来处理这个问题。因此,我可以在这些对象的构造函数中完成所有OpenGL调用。但是,在这种情况下,我需要很多函数(或一种基于模板的方法),当OpenGL线程有更多要做的事情时,它感觉像潜在的渲染时间损失......
我的问题
按照我上面描述的方式处理它是否可以?还是我应该放弃那些东西并做些其他事情?