【问题标题】:How to design a window render loop?如何设计一个窗口渲染循环?
【发布时间】:2019-06-27 19:37:05
【问题描述】:

我正在尝试设计一个窗口对象,其工作是处理 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++ 中,哪个是渲染循环的好设计,试图优先考虑使用的多功能性和执行速度?

【问题讨论】:

    标签: c++ window glfw software-design


    【解决方案1】:

    我的简短回答是:

    使用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 的原始样本没有太大区别。)


    三思而后行,我发现值得一提的是小部件集以及它们提供的解决方案(因为要解决相同的问题)。

    两个通用解决方案是

    • 信号/信号处理程序 (signal slot concept)
    • virtual 可被覆盖的事件处理程序方法。

    信号基本上只是一个带有函数指针的容器(或std::function 或类似的东西)。在某些情况下会发出信号(即调用存储的函数指针)。因此,其他对象可以在这种情况下得到通知(通过在信号中注册它们的信号处理程序)。所以,一个信号实际上和上面类似,只是函数指针不是临时提供的,而是存储在成员变量中。

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

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

    【讨论】:

    猜你喜欢
    • 2016-12-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多