如何设计窗口渲染循环
How to design a window render loop?
我正在尝试设计一个窗口对象,其工作是处理GLFW窗口的所有功能(初始化、回调、输入处理…)
最重要的事情之一是渲染循环。我能想到的最天真的设计是让renderloop方法获取一个没有参数的函数指针,然后在循环方法内部调用它,如下所示:
class Window
{
public:
Window();
~Window();
void WindowLoop(void (*f) (void));
protected:
GLFWwindow* window;
};
void Window::WindowLoop(void (*f) (void))
{
while(!glfwWindowShouldClose(window))
{
f();
glfwPollEvents();
}
}
然而,这带来了很多限制。首先,这意味着函数不能接受任何参数。这可能是个问题,也可能不是个问题。
我已经做了一些研究,显然你可以传递函数指针,这些指针采用任意数量的参数,但这似乎既困难又不可取。
另一个选项是通用函子,然后可以将参数定义为类/结构的一部分,从而避免处理它。
可能还有其他我不知道的潜在设计。
对于渲染循环,在C++中哪一个设计是好的,试图优先考虑使用的多功能性和执行速度?
我的简短回答是:
使用std::function
而不是原始函数指针。这给了你更多的灵活性,因为它可以容纳:
- 函数指针
- 方法指针(当然有对象)
- 函子
- lambdas(有或没有捕获,它实际上重复上面的一个或另一个)
您仍然需要为调用定义签名,但您可以提供回调上下文,这可能正是您所需要的。
所以,这就是它的样子:
#include <functional>
class Window
{
public:
Window();
~Window();
void WindowLoop(std::function<void()> f);
protected:
GLFWwindow* window;
};
void Window::WindowLoop(std::function<void()> f)
{
while(!glfwWindowShouldClose(window))
{
f();
glfwPollEvents();
}
}
(它看起来与OP的原始样本没有太大区别。)
三思而后行,我发现值得一提的是小部件集及其提供的解决方案(因为有相同的问题需要解决)。
两种通用解决方案是
- 信号/信号处理程序(信号槽概念)
- 可重写的事件处理程序的
virtual
方法
信号基本上只不过是一个带有函数指针(或std::function
或类似的东西)的容器。在某些情况下会发出信号(即调用存储的函数指针)。因此,在这种情况下,其他对象可以得到通知(通过在信号中注册它们的信号处理程序)。因此,除了函数指针不是临时提供的而是存储在成员变量中之外,信号实际上与上述类似。
另一种选择是在某些情况下调用virtual
方法。要添加自定义行为。基类必须派生,并且必须重写quest中的virtual
方法。
在OP的情况下,这可能看起来像这样:
class Window
{
public:
Window();
~Window();
void WindowLoop();
protected:
virtual void step();
protected:
GLFWwindow* window;
};
void Window::WindowLoop()
{
while(!glfwWindowShouldClose(window))
{
step();
glfwPollEvents();
}
}
void Window::step() { /* empty placeholder */ }
要在应用程序中使用它,Window
的派生类是强制性的:
class GameWindow: public Window {
protected:
virtual void step() override;
};
void GameWindow::step()
{
// Do the game step stuff (e.g. rendering)
// where this (of type GameWindow) can provide the necessary context.
}
关于Qt,有各种情况的信号和virtual
方法,例如小部件中的事件处理程序。大多数情况下可以选择/或–我不记得这两种东西都有。例如,可能存在为QPushButton::clicked()
注册的信号处理程序,但要将事件处理程序自定义为mousePressEvent()
,必须重载Qt小部件以覆盖事件处理程序方法。(也有事件过滤器的概念,但IMHO这并不完全相同。)
gtkmm(至少在我过去使用的版本2中)提供了我能记住的virtual
方法和信号的所有内容。因此,总是可以选择派生gtkmm小部件,或者仅通过注册信号处理程序来更改/扩展gtkmm部件的行为。这可能会带来很少的额外性能成本,但对于应用程序编程来说非常方便。
- 如何循环打印顶点结构
- 如何在C++中从两个单独的for循环中添加两个数组
- C++我的数学有什么问题,为什么我的代码不能正确循环
- 正在尝试了解输入验证循环
- std::map<struct,struct>::find 找不到匹配项,但是如果我循环通过 begin() 到 end(),我在那里看到匹配项
- 在包含窗口标头时难以解决循环依赖关系问题
- 使用 while 循环时不显示窗口
- 使用 QLinkedList 循环浏览不同的窗口屏幕
- 如何设计窗口渲染循环
- 如何在C++循环中创建形状并将其附加到窗口
- GLFW-无需使用循环而保持窗口
- 当窗口聚焦时,OpenGL程序不会循环
- Qt-如何在不使用插槽的情况下检测自定义事件循环中的关闭窗口
- GDI 窗口中的游戏循环/一段时间后不刷新并崩溃
- 将游戏服务器线程循环限制为 30FPS,而无需游戏引擎/渲染窗口
- 是否可以捕获翻译/调度消息循环中的窗口消息/事件
- C++ WTL8.1 如何在具有消息循环的新线程中创建窗口
- 在窗口循环中,在窗口控件上获取鼠标移动比子类化更容易
- 在Qt窗口中运行游戏循环
- 当消息循环在另一个线程上时,等待窗口关闭