Resize window in D3D12

by Range   Last Updated September 11, 2019 12:13 PM

I am trying to implement the d3d12 window resizing function. First, I delete all the buffers referenced by SwapChain, and then run SwapChain :: ResizeBuffers. At this point I get the error:

D3D12 ERROR: ID3D12Resource1::<final-release>: CORRUPTION: An ID3D12Resource object (0x000002323B7029A0:'Unnamed Object') is referenced by GPU operations in-flight on Command Queue (0x000002323B6BFD00:'Command queue'). It is not safe to final-release objects that may have GPU operations pending. This can result in application instability. [ EXECUTION ERROR #921: OBJECT_DELETED_WHILE_STILL_IN_USE]
Exception thrown at 0x00007FF89004A839 (KernelBase.dll) in dx12_buffers_learn.exe: 0x0000087D (parameters: 0x0000000000000000, 0x000000712E99AE80, 0x0000023238E800D0).
Unhandled exception at 0x00007FF89004A839 (KernelBase.dll) in dx12_buffers_learn.exe: 0xC000041D: 

This is the code of the procedure for changing sizes of window:

bool resize(int w, int h)
{
    if (device && swapChain && commandAllocator[frameIndex])
    {
        WaitForPreviousFrame();
        if (FAILED(commandAllocator[frameIndex]->Reset())) return false;
        if (FAILED(commandList->Reset(commandAllocator[frameIndex].Get(), nullptr))) return false;
        for (int i(0); i < frameBufferCount; i++)
            renderTargets[i].Reset();
        if (FAILED(swapChain->ResizeBuffers(frameBufferCount, w, h, backBufferFormat, NULL))) return false;
        frameIndex = 0;
        if (!createBackBuffer()) return false;
        createViewport();
        createScissor();
        if (FAILED(commandList->Close())) return false;
        vector<ID3D12CommandList*> cmdsLists = { commandList.Get() };
        commandQueue->ExecuteCommandLists(cmdsLists.size(), cmdsLists.data());
    }
    return true;
}

I put together a minimal self sufficient example for modeling the problem that described above:

#include <windows.h>
#include <d3d12.h>
#include <dxgi1_4.h>
#include "d3dx12.h"
#include <wrl.h>
#include <vector>
#pragma comment(lib, "dxgi.lib")
#pragma comment(lib, "d3d12.lib")
using namespace std;
using Microsoft::WRL::ComPtr;

void updateSize();
bool InitializeWindow(HINSTANCE hInstance);
bool resize(int w, int h);
void mainloop();
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
bool InitD3D();
void UpdatePipeline();
void Render();
void WaitForPreviousFrame();
bool createBackBuffer();
void createViewport();
void createScissor();

HWND hwnd = nullptr;
int widthClient = 900;
int heightClient = 650;
int startWidth;
int startHeight;
int widthWindow;
int heightWindow;
int frameBufferCount = 3;
ComPtr<IDXGIFactory4> dxgiFactory;
DXGI_FORMAT backBufferFormat = DXGI_FORMAT_R8G8B8A8_UNORM;
ComPtr < ID3D12Device> device;
ComPtr<IDXGISwapChain3> swapChain;
ComPtr < ID3D12CommandQueue> commandQueue;
ComPtr < ID3D12DescriptorHeap> rtvDescriptorHeap;
vector< ComPtr<ID3D12Resource>> renderTargets;
vector< ComPtr<ID3D12CommandAllocator>> commandAllocator;
ComPtr < ID3D12GraphicsCommandList> commandList;
vector< ComPtr<ID3D12Fence>> fence;
HANDLE fenceEvent = nullptr;
vector<unsigned long long> fenceValue;
int frameIndex = 0;
int rtvDescriptorSize = -1;
D3D12_VIEWPORT viewport;
D3D12_RECT scissorRect;
bool vsync = false;
float color[] = { 0.53f, 0.53f, 0.53f, 1.f };

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
    if (!InitializeWindow(hInstance)) return -1;
    if (!InitD3D()) return -1;
    mainloop();
    WaitForPreviousFrame();
    return 0;
}

void updateSize()
{
    RECT windowRect;
    GetClientRect(hwnd, &windowRect);
    widthClient = windowRect.right;
    heightClient = windowRect.bottom;
    startWidth = windowRect.left;
    startHeight = windowRect.top;
    GetWindowRect(hwnd, &windowRect);
    widthWindow = windowRect.right - windowRect.left;
    heightWindow = windowRect.bottom - windowRect.top;
}

bool InitializeWindow(HINSTANCE hInstance)
{
    WNDCLASSEX wc;
    ZeroMemory(&wc, sizeof(wc));
    wc.cbSize = sizeof(WNDCLASSEX);
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = WndProc;
    wc.hInstance = hInstance;
    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)GetStockObject(NULL_BRUSH);
    wc.lpszClassName = L"dx12_className";
    wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
    if (!RegisterClassEx(&wc)) return false;
    if(!(hwnd = CreateWindowEx(NULL, L"dx12_className", L"DX12 Render Triangles", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, widthClient, heightClient, NULL, NULL, hInstance, NULL))) return false;
    updateSize();
    ShowWindow(hwnd, SW_SHOW);
    UpdateWindow(hwnd);
    return true;
}

void mainloop()
{
    MSG msg = {};
    while (true)
        if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
        {
            if (msg.message == WM_QUIT)
                break;
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        else
            Render();
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)
    {
    case WM_DESTROY: // x button on top right corner of window was pressed
        PostQuitMessage(0);
        return 0;
    case WM_SIZE:
        updateSize();
        if (!resize(widthWindow, heightWindow))
        {
            DestroyWindow(hwnd);
            return 0;
        }
        break;
    }
    return DefWindowProc(hwnd, msg, wParam, lParam);
}

bool InitD3D()
{
    // init
    renderTargets.resize(frameBufferCount);
    commandAllocator.resize(frameBufferCount);
    fence.resize(frameBufferCount);
    fenceValue = { 0,0,0 };
    // debug
    ComPtr<ID3D12Debug> debugController;
    if (FAILED(D3D12GetDebugInterface(IID_PPV_ARGS(&debugController)))) return false;
    debugController->EnableDebugLayer();
    // dxgi factory
    if (FAILED(CreateDXGIFactory1(IID_PPV_ARGS(dxgiFactory.GetAddressOf())))) return false;
    // device
    if (FAILED(D3D12CreateDevice(nullptr, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(device.GetAddressOf())))) return false;
    // queue
    D3D12_COMMAND_QUEUE_DESC cqDesc = {};
    cqDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
    cqDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; 
    if (FAILED(device->CreateCommandQueue(&cqDesc, IID_PPV_ARGS(commandQueue.GetAddressOf())))) return false;
    commandQueue->SetName(L"Command queue");
    // swap chain
    DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {};
    swapChainDesc.Format = backBufferFormat;
    swapChainDesc.Stereo = FALSE;
    swapChainDesc.SampleDesc.Count = 1;
    swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
    swapChainDesc.BufferCount = frameBufferCount;
    swapChainDesc.Scaling = DXGI_SCALING_NONE;
    swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
    swapChainDesc.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED;
    ComPtr<IDXGISwapChain1> tempSwapChain;
    if (FAILED(dxgiFactory->CreateSwapChainForHwnd(commandQueue.Get(), hwnd, &swapChainDesc, nullptr, nullptr, tempSwapChain.GetAddressOf()))) return false;
    if(!(swapChain = static_cast<IDXGISwapChain3*>(tempSwapChain.Get()))) return false;
    frameIndex = swapChain->GetCurrentBackBufferIndex();
    DXGI_RGBA swapChainBkColor = { color[0], color[1], color[2], color[3] };
    if (FAILED(swapChain->SetBackgroundColor(&swapChainBkColor))) return false;
    // back buffers
    if (!createBackBuffer()) return false;
    // viewport
    createViewport();
    // scissor
    createScissor();
    // allocators
    for (int i = 0; i < frameBufferCount; i++)
        if (FAILED(device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(commandAllocator[i].GetAddressOf())))) return false;
    // list
    if (FAILED(device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, commandAllocator[frameIndex].Get(), NULL, IID_PPV_ARGS(commandList.GetAddressOf())))) return false;
    // fence
    for (int i = 0; i < frameBufferCount; i++)
        if (FAILED(device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(fence[i].GetAddressOf())))) return false;
    if(!(fenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr))) return false;
    // closed list
    commandList->Close();
    vector<ID3D12CommandList*> cmdsLists = { commandList.Get() };
    commandQueue->ExecuteCommandLists(cmdsLists.size(), cmdsLists.data());
    return true;
}

bool resize(int w, int h)
{
    if (device && swapChain && commandAllocator[frameIndex])
    {
        WaitForPreviousFrame();
        if (FAILED(commandAllocator[frameIndex]->Reset())) return false;
        if (FAILED(commandList->Reset(commandAllocator[frameIndex].Get(), nullptr))) return false;
        for (int i(0); i < frameBufferCount; i++)
            renderTargets[i].Reset();
        if (FAILED(swapChain->ResizeBuffers(frameBufferCount, w, h, backBufferFormat, NULL))) return false;
        frameIndex = 0;
        if (!createBackBuffer()) return false;
        createViewport();
        createScissor();
        if (FAILED(commandList->Close())) return false;
        vector<ID3D12CommandList*> cmdsLists = { commandList.Get() };
        commandQueue->ExecuteCommandLists(cmdsLists.size(), cmdsLists.data());
    }
    return true;
}

void UpdatePipeline()
{
    WaitForPreviousFrame();
    commandAllocator[frameIndex]->Reset();
    commandList->Reset(commandAllocator[frameIndex].Get(), nullptr);
    commandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(renderTargets[frameIndex].Get(), D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_RENDER_TARGET));
    CD3DX12_CPU_DESCRIPTOR_HANDLE rtvHandle(rtvDescriptorHeap->GetCPUDescriptorHandleForHeapStart(), frameIndex, rtvDescriptorSize);
    commandList->ClearRenderTargetView(rtvHandle, color, 0, nullptr);
    commandList->RSSetViewports(1, &viewport); 
    commandList->RSSetScissorRects(1, &scissorRect); 
    commandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(renderTargets[frameIndex].Get(), D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PRESENT));
    commandList->Close();
}

void Render()
{
    UpdatePipeline();
    vector<ID3D12CommandList*> ppCommandLists = { commandList.Get() };
    commandQueue->ExecuteCommandLists(ppCommandLists.size(), ppCommandLists.data());
    commandQueue->Signal(fence[frameIndex].Get(), fenceValue[frameIndex]);
    swapChain->Present(vsync ? 1 : 0, 0);
}

void WaitForPreviousFrame()
{
    frameIndex = swapChain->GetCurrentBackBufferIndex();
    if (fence[frameIndex]->GetCompletedValue() < fenceValue[frameIndex])
    {
        fence[frameIndex]->SetEventOnCompletion(fenceValue[frameIndex], fenceEvent);
        WaitForSingleObject(fenceEvent, INFINITE);
    }
    fenceValue[frameIndex]++;
}

bool createBackBuffer()
{
    D3D12_DESCRIPTOR_HEAP_DESC rtvHeapDesc = {};
    rtvHeapDesc.NumDescriptors = frameBufferCount;
    rtvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
    rtvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
    if (FAILED(device->CreateDescriptorHeap(&rtvHeapDesc, IID_PPV_ARGS(rtvDescriptorHeap.GetAddressOf()))))
        return false;
    rtvDescriptorSize = device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
    CD3DX12_CPU_DESCRIPTOR_HANDLE rtvHandle(rtvDescriptorHeap->GetCPUDescriptorHandleForHeapStart());
    for (int i = 0; i < frameBufferCount; i++)
    {
        if (FAILED(swapChain->GetBuffer(i, IID_PPV_ARGS(renderTargets[i].GetAddressOf()))))
            return false;
        device->CreateRenderTargetView(renderTargets[i].Get(), nullptr, rtvHandle);
        rtvHandle.Offset(1, rtvDescriptorSize);
    }
    return true;
}

void createViewport()
{
    viewport.TopLeftX = 0;
    viewport.TopLeftY = 0;
    viewport.Width = widthClient;
    viewport.Height = heightClient;
    viewport.MinDepth = 0.0f;
    viewport.MaxDepth = 1.0f;
}

void createScissor()
{
    scissorRect.left = 0;
    scissorRect.top = 0;
    scissorRect.right = widthClient;
    scissorRect.bottom = heightClient;
}

Tell me how to fix this problem



Related Questions


What are these leaked objects?

Updated December 25, 2018 08:13 AM

What is the point of a borderless fullscreen window?

Updated January 26, 2017 14:05 PM

DirectX11 program bug full screen

Updated January 26, 2019 00:13 AM

Game Engine Improvements

Updated February 07, 2019 22:13 PM