使用 full-screen window 而多个其他 windows 打开时帧率非常低

Very low framerate when using full-screen window while multiple other windows are open

抱歉,标题太长了,但我有一个非常具体的问题,无法更简洁地表达。我正在编写一个游戏引擎 (GitHub link: here),我试图让客户端在主 window 之上创建 windows应用程序自动提供。

我已经完全设法让它工作了,但我对主 window 进入 full-screen 模式时的帧率感到困扰(无论是在初始化时还是当用户按下 alt 时+输入)。我没有对性能进行基准测试,但它明显很差(所以可能大约 20-30 FPS)并且性能仅在用户创建另一个 window 时下降(甚至不必显示)。

由于用户创建的所有 windows 都是主要 window 的 children,我必须在进入 full-screen 模式之前隐藏它们。

我的 window class 中有很多代码(超过 1000 行),因此给您一个最小的示例将非常困难。如果您必须查看代码,请访问 GitHub 存储库(在 platform/windows 下您会找到我引用的代码)。我想知道这是否是在同一进程中打开多个 windows 的奇怪产物,或者我是否只是缺少一些代码。

也就是说,这是一些实际的客户端代码:

SandboxApp.h

#pragma once

#include<Infinity.h>

class SandboxApp : public Infinity::Application
{
private:
    Infinity::Window *m_popup_window;
    Infinity::Rasterizer *m_rasterizer;

    Infinity::OrthoCamera m_camera;
    Infinity::Renderer2D m_renderer;
    Infinity::Texture2D *m_texture;

    float m_aspect_ratio;

public:
    SandboxApp();
    ~SandboxApp();

    void OnApplicationEntered(Infinity::ApplicationEnteredEvent *event) override;

    void OnUserCreate(Infinity::UserCreateEvent *event) override;
    void OnUserUpdate(Infinity::UserUpdateEvent *event) override;
    void OnUserRender(Infinity::UserRenderEvent *event) override;
    void OnUserDestroy(Infinity::UserDestroyEvent *event) override;

    void OnWindowResized(Infinity::WindowResizedEvent *event) override;

    void Exit(const char *message);
};

SanboxApp.cpp

#define INFINITY_ENTRY_POINT
#include"SandboxApp.h"

SandboxApp::SandboxApp():
    m_popup_window(nullptr),
    m_rasterizer(nullptr),

    m_renderer(),
    m_texture(),

    m_aspect_ratio(),
    m_camera()
{}

SandboxApp::~SandboxApp()
{}

void SandboxApp::Exit(const char *message)
{
    INFINITY_CLIENT_ERROR(message);
    RequestExit();
}

void SandboxApp::OnApplicationEntered(Infinity::ApplicationEnteredEvent *event)
{
    Infinity::Window::WindowParams &params = event->GetMainWindowParams();
    params.fullscreen = true;
    params.auto_show = false;
}

void SandboxApp::OnUserCreate(Infinity::UserCreateEvent *event)
{
    Infinity::Window *window = GetMainWindow();
    m_popup_window = Infinity::Window::CreateWindow();

    Infinity::Window::WindowParams window_params;
    window_params.width = 300;
    window_params.height = 300;
    window_params.title = "Popup window!";

    if (!m_popup_window->Init(window_params))
    {
        Exit("Error initializing popup window");
        return;
    }

    // Set clear color
    Infinity::Context *context = Infinity::Window::GetContext();
    context->SetClearColor(0.0f, 0.0f, 0.0f, 1.0f);

    m_popup_window->MakeContextCurrent();

    context = Infinity::Window::GetContext();
    context->SetClearColor(0.0f, 0.0f, 1.0f, 1.0f);

    window->MakeContextCurrent();

    // Initialize other resources
    m_rasterizer = Infinity::Rasterizer::CreateRasterizer();

    if (!m_rasterizer->Init(Infinity::Rasterizer::CullMode::NONE, true))
    {
        Exit("Error initializing rasterizer");
        return;
    }

    m_rasterizer->Bind();

    if (!m_renderer.Init())
    {
        Exit("Error initializing Renderer2D");
        return;
    }

    m_texture = Infinity::Texture2D::CreateTexture();

    if (!m_texture->Init("assets/image.png"))
    {
        Exit("Error initializing texture");
        return;
    }

    INFINITY_CLIENT_INFO("Client created");

    window->Show();
    m_popup_window->Show();

    event->Consume();
}

void SandboxApp::OnUserUpdate(Infinity::UserUpdateEvent *event)
{
    Infinity::Window *window = GetMainWindow();

    if (KeyPressed(Infinity::KeyCode::Escape))
    {
        if (window->CursorEnabled())
        {
            window->DisableCursor();
        }
        else
        {
            window->EnableCursor();
        }
    }

    if (window->CursorEnabled())
    {
        event->Consume();
        return;
    }

    float speed = (float)(3.0 * event->GetDT());
    float r_speed = (float)(2.0 * event->GetDT());
    float z_speed = (float)(1.0 * event->GetDT());

    if (KeyDown(Infinity::KeyCode::Left)) { m_camera.MoveLeft(speed); }
    if (KeyDown(Infinity::KeyCode::Right)) { m_camera.MoveRight(speed); }

    if (KeyDown(Infinity::KeyCode::Down)) { m_camera.MoveBackward(speed); }
    if (KeyDown(Infinity::KeyCode::Up)) { m_camera.MoveForward(speed); }

    if (KeyDown(Infinity::KeyCode::W)) { m_camera.zoom += z_speed; }
    if (KeyDown(Infinity::KeyCode::S)) { m_camera.zoom -= z_speed; }
    if (KeyDown(Infinity::KeyCode::A)) { m_camera.roll -= r_speed; }
    if (KeyDown(Infinity::KeyCode::D)) { m_camera.roll += r_speed; }

    m_camera.Update(m_aspect_ratio);

    event->Consume();
}

void SandboxApp::OnUserRender(Infinity::UserRenderEvent *event)
{
    Infinity::Window *window = GetMainWindow();
    window->MakeContextCurrent();

    Infinity::Context *context = Infinity::Window::GetContext();
    context->Clear();

    m_renderer.StartScene(&m_camera);

    Infinity::Renderer2D::QuadParams quad;
    quad.position = { 0.0f, 0.0f };
    quad.size = { 1.0f, 1.0f };
    quad.color = { 1.0f, 0.0f, 0.0f, 1.0f };

    m_renderer.DrawQuad(quad);

    m_renderer.EndScene();

    m_popup_window->MakeContextCurrent();

    context = Infinity::Window::GetContext();
    context->Clear();

    window->MakeContextCurrent();

    event->Consume();
}

void SandboxApp::OnUserDestroy(Infinity::UserDestroyEvent *event)
{
    m_renderer.Destroy();

    if (m_rasterizer)
    {
        m_rasterizer->Destroy();
        delete m_rasterizer;
    }

    if (m_texture)
    {
        m_texture->Destroy();
        delete m_texture;
    }

    if (m_popup_window)
    {
        m_popup_window->Destroy();
        delete m_popup_window;
    }

    INFINITY_CLIENT_INFO("Client destroyed");

    event->Consume();
}

void SandboxApp::OnWindowResized(Infinity::WindowResizedEvent *event)
{
    if (event->GetWindow() == GetMainWindow())
    {
        m_aspect_ratio = (float)event->GetWidth() / (float)event->GetHeight();

        m_camera.Update(m_aspect_ratio);
        event->Consume();
    }
}

Infinity::Application *Infinity::CreateApplication()
{
    return new SandboxApp;
}

如果您需要任何其他信息,请发表评论。
提前致谢! :)

更新
我尝试将我的可执行文件添加到图形性能选项列表,但它并没有改变 full-screen window.

的低帧率

我做了一些更多的测试,发现我只需要创建 sub-window 就可以使这些低效率发生。即使我不显示、更新或渲染 window,简单地创建它也会降低我的 full-screen 主 window.

的帧速率

尝试做更多研究后,我意识到 MSDN 没有任何关于使用多个 DXGI 交换链的文档。我的直觉是,将一个交换链的 full-screen 状态设置为 true 会以某种方式干扰另一个交换链,导致效率低下(尽管我的 ID3D11Device 调试输出没有在任何地方提到效率低下)

Windows有2种全屏。

真正的全屏可能就是你正在做的,调用 IDXGISwapChain::SetFullscreenState。在此模式下,windows 桌面合成器 dwm.exe 被禁用,让您的应用独占访问监视器。但是,正如您发现的那样,它有并发症。还有更多 顺便说一句:alt+tab 可能会很慢,来自其他应用程序的弹出窗口可能会出现故障,而且正确实施也很棘手,我认为您必须重新创建交换链和所有渲染目标。从好的方面来说,在极少数情况下,它可能会将 FPS 提高一位数。

Borderless windowed 是您应该做的,因为您需要多个 Win32 windows 运行 以及全屏内容。在此模式下,桌面合成器启动并 运行,与其他人一起更新您的 window。进入和退出该模式也更容易,示例如下。

HRESULT WindowImpl::maximizeBorderless( bool max )
{
    // https://devblogs.microsoft.com/oldnewthing/20100412-00/?p=14353
    DWORD dwStyle = GetWindowLong( GWL_STYLE );

    if( max )
    {
        MONITORINFO mi = { sizeof( mi ) };
        if( GetWindowPlacement( &m_wpPrev ) && GetMonitorInfo( MonitorFromWindow( m_hWnd, MONITOR_DEFAULTTOPRIMARY ), &mi ) )
        {
            SetWindowLong( GWL_STYLE, dwStyle & ~WS_OVERLAPPEDWINDOW );
            SetWindowPos( HWND_TOP,
                mi.rcMonitor.left, mi.rcMonitor.top,
                mi.rcMonitor.right - mi.rcMonitor.left,
                mi.rcMonitor.bottom - mi.rcMonitor.top,
                SWP_NOOWNERZORDER | SWP_FRAMECHANGED );
        }
    }
    else
    {
        SetWindowLong( GWL_STYLE, dwStyle | WS_OVERLAPPEDWINDOW );
        SetWindowPlacement( &m_wpPrev );
        SetWindowPos( NULL, 0, 0, 0, 0,
            SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
            SWP_NOOWNERZORDER | SWP_FRAMECHANGED );
    }

    return S_OK;
}