Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/144.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ Windows上带OpenGL的可靠窗口vsync?_C++_Windows_Opengl_Rendering - Fatal编程技术网

C++ Windows上带OpenGL的可靠窗口vsync?

C++ Windows上带OpenGL的可靠窗口vsync?,c++,windows,opengl,rendering,C++,Windows,Opengl,Rendering,总结 在窗口模式下,带OpenGL的vsync似乎在Windows上被破坏了。我尝试过不同的API(SDL、glfw、SFML),所有这些都有相同的结果:虽然帧速率是有限的(根据我尝试过的多个60 Hz设置上的CPU测量,始终在16-17毫秒左右),而且CPU实际上大部分时间都在睡眠,但帧经常被跳过。根据机器和CPU在渲染以外的其他方面的使用情况,这可能与有效地将帧速率减半一样糟糕。此问题似乎与驱动程序无关 如何使用OpenGL在窗口模式下在Windows上运行vsync,或使用这些属性获得类似

总结

在窗口模式下,带OpenGL的vsync似乎在Windows上被破坏了。我尝试过不同的API(SDL、glfw、SFML),所有这些都有相同的结果:虽然帧速率是有限的(根据我尝试过的多个60 Hz设置上的CPU测量,始终在16-17毫秒左右),而且CPU实际上大部分时间都在睡眠,但帧经常被跳过。根据机器和CPU在渲染以外的其他方面的使用情况,这可能与有效地将帧速率减半一样糟糕。此问题似乎与驱动程序无关

如何使用OpenGL在窗口模式下在Windows上运行vsync,或使用这些属性获得类似效果(如果我忘记了一些值得注意的内容,或者某些内容不合理,请发表评论):

  • CPU可以在大部分时间睡眠
  • 不撕裂
  • 没有跳过的帧(假设系统没有过载)
  • CPU可以知道实际显示帧的时间

细节/一些研究

当我在谷歌上搜索
opengl-vsync-stutter
opengl-vsync-frame-drop
或类似查询时,我发现很多人都有这个问题(或非常类似的问题),但实际问题似乎没有一致的解决方案(在gamedev stackexchange上也有许多回答不充分的问题;还有许多低投入论坛的帖子)

总结一下我的研究:新版本的Windows中使用的合成窗口管理器(DWM)似乎强制使用三重缓冲,这会干扰vsync。人们建议禁用DWM、不使用vsync或全屏显示,所有这些都不是解决原始问题的方法(脚注1)。我也没有找到详细的解释,说明为什么三重缓冲会导致vsync出现此问题,或者为什么在技术上无法解决此问题

但是:我还测试了Linux上不会出现这种情况,即使在非常脆弱的PC上也是如此。因此,基于OpenGL的硬件加速必须在技术上(至少在一般情况下)能够在不跳过帧的情况下启用功能性vsync

此外,在Windows上使用D3D而不是OpenGL(启用了vsync)时,这也不是问题。因此,在Windows上运行vsync在技术上必须是可行的(我尝试过新的、旧的和非常旧的驱动程序以及不同的(旧的和新的)硬件,虽然我所有可用的硬件设置都是Intel+NVidia,所以我不知道AMD/ATI会发生什么)

最后,一定要有适用于Windows的软件,无论是游戏、多媒体应用程序、创意制作、3D建模/渲染程序还是其他任何软件,它们使用OpenGL并在窗口模式下正常工作,同时仍能准确地渲染,无需等待CPU,也无需掉帧


我注意到,当使用传统的渲染循环时

while (true)
{
    poll_all_events_in_event_queue();
    process_things();
    render();
}
CPU在该循环中所做的工作量会影响口吃的行为。然而,这绝不是CPU过载的问题,因为这个问题也发生在一个可以编写的最简单的程序之一(见下文)和一个功能非常强大的系统上,而该系统不做任何其他事情(程序只是在每一帧上用不同的颜色清除窗口,然后显示它)

我还注意到,它似乎从来没有比每隔一帧跳过一次更糟糕(即,在我的测试中,在60赫兹的系统中,可见帧率总是在30到60之间)。当运行程序在奇数帧和偶数帧上更改2种颜色之间的背景颜色时,您可能会发现Nyquist采样定理有点违规,这使我相信某些东西没有正确同步(即Windows或其OpenGL实现中的软件错误)。同样,就CPU而言,帧速率是坚如磐石。而且,
timeBeginPeriod
在我的测试中没有明显的效果


(脚注1)但应注意,由于DWM,窗口模式下不会发生撕裂(这是使用vsync的两个主要原因之一,另一个原因是使CPU在不丢失帧的情况下尽可能长时间睡眠)。因此,我可以接受在应用程序层实现vsync的解决方案

但是,我认为唯一可行的方法是,有一种方法可以显式(准确地)等待页面翻转发生(有可能超时或取消),或者查询页面翻转时设置的非粘性标志(以一种不会强制刷新整个异步渲染管道的方式,例如,
glGetError
does),而我也没有找到一种方法


下面是一些代码,可以运行一个演示此问题的快速示例(使用SFML,我发现这是最不费力的)

你应该看到同质闪烁。如果你在多个画面上看到相同的颜色(黑色或紫色),那就糟糕了

(屏幕上会闪烁显示屏的刷新率,因此可能会出现癫痫警告):

//g++TEST\u TEST\u TEST.cpp-lsfml系统-lsfml窗口-lsfml图形-lGL
#包括
#包括
#包括
#包括
#包括
int main()
{
//创建窗口
渲染窗口(sf::视频模式(800600),“OpenGL”);
window.setVerticalSyncEnabled(真);
//激活窗口
window.setActive(true);
int frame_计数器=0;
sf::矩形形状矩形;
rect.setSize(sf::Vector2f(10,10));
sf:时钟;
while(true)
{
//处理事件
sf::事件;
while(window.pollEvent(事件))
{
如果(event.type==sf::event::Closed)
{
返回0;
}
}
++帧计数器;
if(帧计数器和1)
{
glClearColor(0,0,0,1);
}
其他的
{
glClearColor(6
// g++ TEST_TEST_TEST.cpp -lsfml-system -lsfml-window -lsfml-graphics -lGL

#include <SFML/System.hpp>
#include <SFML/Window.hpp>
#include <SFML/Graphics.hpp>
#include <SFML/OpenGL.hpp>
#include <iostream>

int main()
{
    // create the window
    sf::RenderWindow window(sf::VideoMode(800, 600), "OpenGL");
    window.setVerticalSyncEnabled(true);

    // activate the window
    window.setActive(true);

    int frame_counter = 0;

    sf::RectangleShape rect;
    rect.setSize(sf::Vector2f(10, 10));

    sf::Clock clock;

    while (true)
    {
        // handle events
        sf::Event event;
        while (window.pollEvent(event))
        {
            if (event.type == sf::Event::Closed)
            {
                return 0;
            }
        }

        ++frame_counter;

        if (frame_counter & 1)
        {
            glClearColor(0, 0, 0, 1);
        }
        else
        {
            glClearColor(60.0/255.0, 50.0/255.0, 75.0/255.0, 1);
        }

        // clear the buffers
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        // Enable this to display a column of rectangles on each frame
        // All colors (and positions) should pop up the same amount
        // This shows that apparently, 1 frame is skipped at most
#if 0
        int fc_mod = frame_counter % 8;
        int color_mod = fc_mod % 4;

        for (int i = 0; i < 30; ++i)
        {
            rect.setPosition(fc_mod * 20 + 10, i * 20 + 10);
            rect.setFillColor(
                sf::Color(
                (color_mod == 0 || color_mod == 3) ? 255 : 0,
                    (color_mod == 0 || color_mod == 2) ? 255 : 0,
                    (color_mod == 1) ? 155 : 0,
                    255
                )
            );
            window.draw(rect);
        }
#endif

        int elapsed_ms = clock.restart().asMilliseconds();
        // NOTE: These numbers are only valid for 60 Hz displays
        if (elapsed_ms > 17 || elapsed_ms < 15)
        {
            // Ideally you should NEVER see this message, but it does tend to stutter a bit for a second or so upon program startup - doesn't matter as long as it stops eventually
            std::cout << elapsed_ms << std::endl;
        }

        // end the current frame (internally swaps the front and back buffers)
        window.display();
    }

    return 0;
}