如何设计窗口渲染循环?

时间:2019-02-04 03:17:23

标签: c++ window glfw software-design

我正在尝试设计一个窗口对象,该对象的工作是处理GLFW窗口的所有功能(引入,回调,输入处理...)

最重要的事情之一是渲染循环。我能想到的最幼稚的设计是让renderloop方法采用不带参数的函数指针,然后在loop方法内调用它,如下所示:

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 ++中,哪种渲染循环设计是不错的选择,它试图优先考虑使用的多功能性和执行速度?

1 个答案:

答案 0 :(得分:1)

我的简短答案应该是:

使用std::function代替原始函数指针。这样可以为您提供更大的灵活性:

  • 功能指针
  • 方法指针(当然还有对象)
  • functors
  • lambda(带有或不带有捕获,实际上重复了上面的一个或另一个)。

仍然需要为呼叫定义签名,但是您可以提供自己可能需要的回叫上下文。

这就是它的外观:

#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的原始示例没什么不同。)


三思而后行,我发现值得一提的是小部件集及其提供的解决方案(有同样的问题要解决)。

两个通用解决方案是

  • 信号/信号处理程序(signal slot concept
  • virtual用于事件处理程序的方法,可以重写。

信号基本上就是带有函数指针(或std::function或类似对象)的容器。在某些情况下会发出信号(即调用存储的函数指针)。因此,其他对象可以在那种情况下得到通知(通过在信号中注册其信号处理程序)。因此,除了没有临时提供函数指针而是将其存储在成员变量中之外,信号实际上与上述相似。

另一种方法是在某些情况下调用virtual方法。要添加自定义行为,则分别为。必须派生基类,并且必须重写搜索中的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小部件以覆盖事件处理程序方法。 (还有事件过滤器的概念,但是恕我直言,这并不完全相同。)

gtkmm(至少在我过去使用的版本2中)提供了我可以记住的virtual方法信号的所有内容。因此,总是可以选择仅通过注册信号处理程序来派生gtkmm窗口小部件或更改/扩展gtkmm窗口小部件的行为。这可能会带来很少的额外性能成本,但是对于应用程序编程来说非常方便。