如何解决这个设计缺陷?

How to resolve this design flaw?

开发 teris 游戏 我得出的结论是,使用通常的 MVC 模式可能会使我的代码混乱,而且由于 OpenGL 在 GLEW 下的工作方式,使得构建代码变得更加困难。相反,我想出了这个巧妙的修改,也依赖于观察者模式,这会更有意义,至少在短期内(图像不反映完整的 class 树):

解释一下:
- 每个盒子都是一个 class,Engine 包含 Entities 的实例,Game 包含 Figure 的实例。
-Figure继承Entity
-Engine 观察 Game,并且 Game 被观察者与 Figures.

耦合 它应该如何工作:
理想情况下,我应该实例化 Engine,它将设置 GLEW 和 GLFW 以及构成 window 的所有其他内容,然后创建一个 Game 对象,该对象将在新线程中创建一个figure,并通知引擎开始绘图循环,当该 figure 被设置时,通过 Observer 将其与指向 Engine 的对象指针一起传递。
这会很好地工作,我可以通过继承 Entity 添加各种图形,图形不会从屏幕上掉下来,因为每个坐标更新都会经过 Game,而且我可能可以, 如果有必要,给每个图形一个独特的着色器。

它的实际工作原理:
Engine初始化,Game初始化,Figure在另一个线程中调用,抛出

atioglxx.dll: 0xC0000005: Access violation reading location 0x00000728.

当被要求访问任何 OpenGL 功能时。
我怀疑发生这种情况是因为 Figure 不知道所有支持功能都在 Engine 中启动。而且我不能在 Figure 中再做一次。我可以将与视图有关的所有内容移动到 Engine,但那样我就无法设置单独的着色器,而且我必须为 Engine 中的每个图形构建 VAO(顶点数组对象) ].
那么,如何在引入尽可能少的更改的同时消除错误?

简短的回答是,对给定上下文的所有 OpenGL 调用都需要在同一个线程上。更长的答案正在路上...


首先,Entities 需要能够使用 GL 调用绘制自己。您不能将来自一个线程的绘图代码与另一个线程混合,OpenGL 客户端状态将不正确。 (将 OpenGL 视为 'C' 状态机)。

其次,您需要按帧缓冲实体状态。当您在 Game 线程中开始下一帧时,Engine 可能会或可能不会完成提交绘图调用。所以你需要一些同步的方式来提交一个框架的所有 Entity 状态。这有点棘手,因为您想避免同步原语(例如互斥锁),这可能会使您的管道停滞或导致阻塞。

您可以阅读 Doom III source code,它使用双缓冲管道将绘图调用提交到 Engine 后端。诀窍在于 Doom 在主线程上完成大部分工作,然后在后端 (Engine) 线程上执行 Draw Call,并在 [=] 上为下一帧提交绘制状态 (Entities) 12=] 主线程。然后它等待 Engine 线程完成并交换缓冲区。

阅读 Metal 和 Vulkan 多线程。这些很相似。

需要在 Engine 上加载资源并返回句柄供您的实体引用(着色器、纹理、静态几何等。)