She*_*ing 4 c++ opengl inheritance
现在,我正在建模一些小的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
}
Run Code Online (Sandbox Code Playgroud)
所以,换句话说,我创建一个像任何其他对象一样的对象
opengl_object obj;
Run Code Online (Sandbox Code Playgroud)
然后,在其构造函数中,将自身置于由OpenGL上下文线程创建的OpenGL对象的队列中.然后,OpenGL上下文线程调用一个虚拟函数,该函数在所有OpenGL对象中实现,并包含必要的OpenGL调用以最终创建对象.
我真的认为,这种处理这个问题的方式会很好.但是,现在,我觉得我错了.
情况是,尽管上面的方法到目前为止工作得非常好,但是一旦类层次结构变深,我就会遇到麻烦.例如(这不完美,但它显示了我的问题):
比方说,我有一个名为sprite的类,显然代表了一个Sprite.它有自己的OpenGL线程创建函数,其中顶点和纹理坐标被加载到图形卡内存中等等.到目前为止,这没问题.让我们进一步说,我想有两种渲染精灵的方法.一个实例和另一个方式.所以,我最终会得到2个类,sprite_instanced和sprite_not_instanced.两者都是从sprite类派生的,因为它们都是sprite,它们只是以不同的方式呈现.但是,sprite_instanced和sprite_not_instanced在其create函数中需要进一步的OpenGL调用.
到目前为止我的解决方案(我觉得它非常糟糕!)
我对c ++中的对象生成如何工作以及它如何影响虚函数有一些了解.所以我决定只使用类精灵的虚拟创建函数将顶点数据等加载到图形内存中.然后,sprite_instanced的虚拟create方法将执行准备以呈现该实例的sprite.所以,如果我想写
sprite_instanced s;
Run Code Online (Sandbox Code Playgroud)
首先,调用sprite构造函数,在一些初始化之后,构造线程将对象传递给OpenGL线程.此时,传递的对象只是一个普通的精灵,所以将调用sprite :: create,OpenGL线程将创建一个普通的精灵.之后,构造线程将调用sprite_instanced的构造函数,再次进行一些初始化并将对象传递给OpenGL线程.但是这一次,它是一个sprite_instanced,因此将调用sprite_instanced :: create.
所以,如果我对上述假设是正确的,那么一切都应该完全按照我的情况发生,至少在我的情况下.我花了最后一小时阅读关于从构造函数调用虚函数以及如何构建v-table等等.我已经运行了一些测试来检查我的假设,但这可能是编译器特定的所以我不依赖它们100% .此外,它只是感觉很糟糕,像一个可怕的黑客.
另一种方案
另一种可能性是在OpenGL线程类中实现工厂方法来处理它.所以我可以在这些对象的构造函数中进行所有OpenGL调用.但是,在这种情况下,我需要很多函数(或者一种基于模板的方法),当OpenGL线程需要做的事情多于它需要时,感觉可能会丢失潜在的渲染时间......
我的问题
可以按照我上面描述的方式处理它吗?或者我应该抛弃那些东西并做其他事情?
你已经得到了一些好的建议.所以我会稍微调整一下:
了解OpenGL的一个重要事项是,它是一个状态机,它不需要一些精心设计的"初始化".你只是使用它,就是这样.缓冲对象(纹理,顶点缓冲对象,像素缓冲对象)可能使它看起来不同,大多数教程和实际应用程序确实在应用程序启动时填充缓冲区对象.
但是,在常规程序执行期间创建它们是完全正常的.在我的3D引擎中,我在双缓冲交换期间使用空闲CPU时间来异步上传到Buffer Objects(for(b in buffers){glMapBuffer(b.target, GL_WRITE_ONLY);} start_buffer_filling_thread(); SwapBuffers(); wait_for_buffer_filling_thread(); for(b in buffers){glUnmapBuffer(b.target);}).
同样重要的是要理解,对于像精灵这样的简单事物,不应该为每个精灵赋予自己的VBO.通常在一个VBO中将大组精灵分组.您不必将它们全部绘制在一起,因为您可以偏移到VBO并进行部分绘图调用.但是这种OpenGL的常见模式(共享缓冲区对象的几何对象)完全违背了类的原则.因此,您需要一些缓冲区对象管理器,向消费者分发一些地址空间.
使用OpenGL本身的类层次并不是一个坏主意,但它应该比OpenGL高一些级别.如果你只是将OpenGL 1:1映射到类,你只会获得复杂性和膨胀.如果我直接或按类调用OpenGL函数,我仍然需要完成所有繁琐的工作.因此,纹理类不应该只映射纹理对象的概念,还应该处理与像素缓冲区对象(如果使用)的交互.
如果你真的想在类中包装OpenGL,我强烈建议不要使用虚函数,而是使用静态(在编译单元级别上的意思)内联类,这样它们就会变成语法糖,编译器不会过多膨胀.