Wat
Wat

Reputation: 71

Direct3D 11 IDXGISwapChain::Present() blocks at every call in Windows 10 windowed mode

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$", "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;
}

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.

Upvotes: 2

Views: 1991

Answers (0)

Related Questions