2

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

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

我还尝试将 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

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

编辑:添加了示例代码。

4

0 回答 0