Direct3D 11 IDXGISwapChain::Present() 在 Windows 10 窗口模式下每次调用时都会阻塞

发布于 2025-01-09 13:36:55 字数 4669 浏览 1 评论 0原文

我正在尝试 Windows 10、Direct3D 11 和 IDXGISwapChain::Present() 函数。 当我在全屏模式下调用 Present 函数时,它会对命令进行排队,并且直到大约第四次调用时才会阻塞,这是预期的行为。

但是,当我在窗口模式下调用相同的函数时,它在第一次调用和每个后续调用时都会阻塞。

我还尝试将 IDXGIDevice1::SetMaximumFrameLatency 函数设置为不同的值,但在窗口模式下该函数似乎被忽略。在全屏模式下该功能工作正常。

我想知道是否有一种方法可以解决在保持窗口模式时每次调用时阻塞的 Present 函数。

这是我用来对上述问题进行单元测试的 C++ 代码:

#include <windows.h>
#include <windowsx.h>
#include <d3d11_1.h>
#include <wrl.h>
#include <dxgi1_2.h>

#include <iostream>
#include <chrono>
#include <stdio.h>
#include <list>

#pragma comment (lib, "d3d11.lib")

#define SCREEN_WIDTH  2560
#define SCREEN_HEIGHT 1440

using namespace std;
using chrono::milliseconds;
using chrono::high_resolution_clock;
using chrono::duration_cast;
using chrono::duration;
using Microsoft::WRL::ComPtr;

ComPtr<IDXGISwapChain> swapchain;
ComPtr<ID3D11Device> dev;
ComPtr<ID3D11DeviceContext> devcon;
ID3D11RenderTargetView* backbuffer;

list<milliseconds> times;

void initConsole()
{
    AllocConsole();

    FILE* pstdin, * pstdout, * pstderr;

    freopen_s(&pstdin, "CONIN$", "r", stdin);
    freopen_s(&pstdout, "CONOUT$", "w", stdout);
    freopen_s(&pstderr, "CONOUT$", "w", stderr);
}

LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_DESTROY:
    {
        PostQuitMessage(0);
        return 0;
    } break;
    }

    return DefWindowProc(hWnd, message, wParam, lParam);
}

void InitD3D(HWND hWnd)
{
    DXGI_SWAP_CHAIN_DESC scd = {};
    scd.BufferCount = 2;
    scd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    scd.BufferDesc.Width = SCREEN_WIDTH;
    scd.BufferDesc.Height = SCREEN_HEIGHT;
    scd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
    scd.OutputWindow = hWnd;
    scd.SampleDesc.Count = 1;
    scd.Windowed = true;
    scd.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
    D3D11CreateDeviceAndSwapChain(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, 0, nullptr, 0, D3D11_SDK_VERSION, &scd, &swapchain, &dev, nullptr, &devcon);

    //swapchain->SetFullscreenState(true, nullptr);

    ComPtr<ID3D11Texture2D> pBackBuffer;
    swapchain->GetBuffer(0, IID_PPV_ARGS(&pBackBuffer));

    dev->CreateRenderTargetView(pBackBuffer.Get(), nullptr, &backbuffer);

    devcon->OMSetRenderTargets(1, &backbuffer, nullptr);

    D3D11_VIEWPORT viewport = {};
    viewport.Width = SCREEN_WIDTH;
    viewport.Height = SCREEN_HEIGHT;
    devcon->RSSetViewports(1, &viewport);
}

void RenderFrame(void)
{
    float color[] = { 0.0f, 0.2f, 0.4f, 1.0f };
    devcon->ClearRenderTargetView(backbuffer, color);

    auto t1 = high_resolution_clock::now();

    swapchain->Present(1, 0);

    auto t2 = high_resolution_clock::now();
    auto time = duration_cast<milliseconds>(t2 - t1);
    times.push_back(time);
}

void CleanD3D(void)
{
    swapchain->SetFullscreenState(false, nullptr);
    backbuffer->Release();
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    initConsole();

    WNDCLASSEX wc = {};
    wc.cbSize = sizeof(WNDCLASSEX);
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = WindowProc;
    wc.hInstance = hInstance;
    wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
    wc.lpszClassName = L"WindowClass";

    RegisterClassEx(&wc);

    RECT wr = { 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT };
    AdjustWindowRect(&wr, WS_OVERLAPPEDWINDOW, FALSE);

    auto hWnd = CreateWindowEx(NULL, L"WindowClass", L"Our First Direct3D Program", WS_OVERLAPPEDWINDOW, 0, 0, wr.right - wr.left, wr.bottom - wr.top, nullptr, nullptr, hInstance, nullptr);

    ShowWindow(hWnd, nCmdShow);

    InitD3D(hWnd);

    MSG msg;

    for (int i = 0; i < 20; ++i)
    {
        if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);

            if (msg.message == WM_QUIT) {
                break;
            }
        }

        RenderFrame();
    }

    CleanD3D();

    for (auto time : times)
    {
        cout << time << " ";
    }
    cin.ignore();

    return 0;
}

如果运行上面的代码,它会输出:

4ms 8ms 15ms 16ms 14ms 16ms 14ms 16ms 15ms 16ms 16ms 16ms 16ms 16ms 16ms 16ms 16ms 15ms 16ms 16ms

但如果取消注释 SetFullscreenState 行,则会输出:

1ms 0ms 0ms 13ms 16ms 49ms 33ms 50ms 16ms 16ms 16ms 16ms 16ms 16ms 16ms 16ms 16ms 15ms 16ms 16ms

在窗口情况下,它会从第一次调用开始阻塞。在全屏情况下,它从第四次调用开始阻塞。

编辑:添加示例代码。

I am experimenting with Windows 10, Direct3D 11 and the IDXGISwapChain::Present() function.
When I call the Present function in fullscreen mode, it queues commands and doesn't block until about the 4th call, which is the expected behaviour.

However, when I call the same function in windowed mode, it is blocking at the 1st call and every subsequent call.

I also tried setting the IDXGIDevice1::SetMaximumFrameLatency function to different values, but in windowed mode this function seems to be ignored. In fullscreen mode the function works ok.

I wonder if there is a way around the Present function blocking at every call while staying in windowed mode.

Here is the C++ code i'm using to unit test the issue above:

#include <windows.h>
#include <windowsx.h>
#include <d3d11_1.h>
#include <wrl.h>
#include <dxgi1_2.h>

#include <iostream>
#include <chrono>
#include <stdio.h>
#include <list>

#pragma comment (lib, "d3d11.lib")

#define SCREEN_WIDTH  2560
#define SCREEN_HEIGHT 1440

using namespace std;
using chrono::milliseconds;
using chrono::high_resolution_clock;
using chrono::duration_cast;
using chrono::duration;
using Microsoft::WRL::ComPtr;

ComPtr<IDXGISwapChain> swapchain;
ComPtr<ID3D11Device> dev;
ComPtr<ID3D11DeviceContext> devcon;
ID3D11RenderTargetView* backbuffer;

list<milliseconds> times;

void initConsole()
{
    AllocConsole();

    FILE* pstdin, * pstdout, * pstderr;

    freopen_s(&pstdin, "CONIN
quot;, "r", stdin);
    freopen_s(&pstdout, "CONOUT
quot;, "w", stdout);
    freopen_s(&pstderr, "CONOUT
quot;, "w", stderr);
}

LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_DESTROY:
    {
        PostQuitMessage(0);
        return 0;
    } break;
    }

    return DefWindowProc(hWnd, message, wParam, lParam);
}

void InitD3D(HWND hWnd)
{
    DXGI_SWAP_CHAIN_DESC scd = {};
    scd.BufferCount = 2;
    scd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    scd.BufferDesc.Width = SCREEN_WIDTH;
    scd.BufferDesc.Height = SCREEN_HEIGHT;
    scd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
    scd.OutputWindow = hWnd;
    scd.SampleDesc.Count = 1;
    scd.Windowed = true;
    scd.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
    D3D11CreateDeviceAndSwapChain(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, 0, nullptr, 0, D3D11_SDK_VERSION, &scd, &swapchain, &dev, nullptr, &devcon);

    //swapchain->SetFullscreenState(true, nullptr);

    ComPtr<ID3D11Texture2D> pBackBuffer;
    swapchain->GetBuffer(0, IID_PPV_ARGS(&pBackBuffer));

    dev->CreateRenderTargetView(pBackBuffer.Get(), nullptr, &backbuffer);

    devcon->OMSetRenderTargets(1, &backbuffer, nullptr);

    D3D11_VIEWPORT viewport = {};
    viewport.Width = SCREEN_WIDTH;
    viewport.Height = SCREEN_HEIGHT;
    devcon->RSSetViewports(1, &viewport);
}

void RenderFrame(void)
{
    float color[] = { 0.0f, 0.2f, 0.4f, 1.0f };
    devcon->ClearRenderTargetView(backbuffer, color);

    auto t1 = high_resolution_clock::now();

    swapchain->Present(1, 0);

    auto t2 = high_resolution_clock::now();
    auto time = duration_cast<milliseconds>(t2 - t1);
    times.push_back(time);
}

void CleanD3D(void)
{
    swapchain->SetFullscreenState(false, nullptr);
    backbuffer->Release();
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    initConsole();

    WNDCLASSEX wc = {};
    wc.cbSize = sizeof(WNDCLASSEX);
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = WindowProc;
    wc.hInstance = hInstance;
    wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
    wc.lpszClassName = L"WindowClass";

    RegisterClassEx(&wc);

    RECT wr = { 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT };
    AdjustWindowRect(&wr, WS_OVERLAPPEDWINDOW, FALSE);

    auto hWnd = CreateWindowEx(NULL, L"WindowClass", L"Our First Direct3D Program", WS_OVERLAPPEDWINDOW, 0, 0, wr.right - wr.left, wr.bottom - wr.top, nullptr, nullptr, hInstance, nullptr);

    ShowWindow(hWnd, nCmdShow);

    InitD3D(hWnd);

    MSG msg;

    for (int i = 0; i < 20; ++i)
    {
        if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);

            if (msg.message == WM_QUIT) {
                break;
            }
        }

        RenderFrame();
    }

    CleanD3D();

    for (auto time : times)
    {
        cout << time << " ";
    }
    cin.ignore();

    return 0;
}

If you run the code above, it outputs:

4ms 8ms 15ms 16ms 14ms 16ms 14ms 16ms 15ms 16ms 16ms 16ms 16ms 16ms 16ms 16ms 16ms 15ms 16ms 16ms

But if you uncomment the SetFullscreenState line, then it outputs:

1ms 0ms 0ms 13ms 16ms 49ms 33ms 50ms 16ms 16ms 16ms 16ms 16ms 16ms 16ms 16ms 16ms 15ms 16ms 16ms

In the windowed case, it blocks from the first call onwards. In the fullscreen case, it blocks from the fourth call onwards.

EDIT: Added sample code.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文