【发布时间】:2011-09-28 22:59:23
【问题描述】:
现在,我正在对某种小 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 的类,显然它代表一个 Sprite。它有自己的OpenGL线程创建函数,其中顶点和纹理坐标被加载到显卡内存等。到目前为止,这没有问题。 让我们进一步说,我想要两种渲染精灵的方式。一个实例化,一个通过另一种方式。所以,我最终会得到 2 个类,sprite_instanced 和 sprite_not_instanced。两者都是从 sprite 类派生的,因为它们都是 sprite,只是呈现方式不同。然而, sprite_instanced 和 sprite_not_instanced 需要在其创建函数中进一步调用 OpenGL。
到目前为止我的解决方案(我对此感到非常糟糕!)
我对 c++ 中的对象生成如何工作以及它如何影响虚函数有一定的了解。所以我决定只使用类精灵的虚拟创建功能将顶点数据等加载到图形内存中。然后 sprite_instanced 的虚拟创建方法将准备渲染该 sprite 实例。 所以,如果我想写
sprite_instanced s;
首先,调用 sprite 构造函数,经过一些初始化,构造线程将对象传递给 OpenGL 线程。此时,传递的对象只是一个普通的精灵,所以 sprite::create 将被调用,OpenGL 线程将创建一个普通的精灵。之后,构造线程将调用 sprite_instanced 的构造函数,再次进行一些初始化并将对象传递给 OpenGL 线程。然而这一次,它是一个 sprite_instanced,因此 sprite_instanced::create 将被调用。
所以,如果我对上述假设是正确的,那么至少在我的情况下,一切都会完全按照它应该发生的那样发生。我花了最后一个小时阅读关于从构造函数调用虚函数以及如何构建 v-table 等。我已经运行了一些测试来检查我的假设,但这可能是编译器特定的,所以我不依赖它们 100% .此外,它只是感觉很糟糕,就像一个可怕的黑客。
另一种解决方案
另一种可能性是在 OpenGL 线程类中实现工厂方法来处理这个问题。所以我可以在这些对象的构造函数中进行所有的 OpenGL 调用。但是,在这种情况下,我需要很多函数(或一种基于模板的方法),并且当 OpenGL 线程要做的事情超出其需要时,感觉可能会损失潜在的渲染时间......
我的问题
可以按照我上面描述的方式处理吗?还是我应该把这些东西扔掉去做别的事情?
【问题讨论】:
-
这对于一个所谓的“小 OpenGL 库”来说是一个巨大的复杂性。它甚至是不必要的多线程。您概述的数据组织不会特别有效。您希望“精灵”的概念比“纹理”和“网格”等概念更高层次。 更多更高级别。
-
是的,不知怎的,它变得相当大了......我想我只是对太多不同的方式感到好奇。但是,我看不出我所概述的数据组织方式不会特别有效。你是什么意思?表现?可维护性?还有什么吗?也许这只是我在这里简化的一部分,但我想清除它......
-
我发现(VS 编译器)如果构造函数调用虚函数,则始终调用基本实现。但是如果我从构造函数调用的虚函数中调用虚函数,就可以了。
-
多线程实际渲染过程本身不会给您带来任何好处。只有一个 GPU,所以驱动程序最后仍然必须自己序列化调用。 D3D 11 提供了一种批处理调用(命令列表)的方法。我不确定 GL 4 是否也可以。当我读到它时,我扬起了眉毛,除非在某些非常特殊的情况下,否则它会对性能产生很大影响。无论如何,我认为您的解决方案可能有点过度设计。
标签: c++ inheritance opengl