Resize window in D3D12

Started by
4 comments, last by Range 4 years, 7 months ago

Hello! 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: 

Quote

    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:

Spoiler


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:

Spoiler


#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.

P.S.:

I set my RTV names and text error replace:

Quote

    D3D12 ERROR: ID3D12Resource1::<final-release>: CORRUPTION: An ID3D12Resource object (0x000001E405CEB230:'back buffer #1')...
    
    D3D12 ERROR: ID3D12Resource1::<final-release>: CORRUPTION: An ID3D12Resource object (0x0000022F37904B10:'back buffer #3')

RTV number is always random.

Advertisement

You have to wait for your GPU to idle before you can resize.

4 minutes ago, SoldierOfLight said:

You have to wait for your GPU to idle before you can resize.

Sorry, I do not quite understand. I call the


WaitForPreviousFrame 

procedure before resizing - waiting for the frame to finish, however, the error remains. Please tell us in more detail where exactly I am mistaken :(

WaitForPreviousFrame isn't sufficient, and also isn't a good implementation of what it says it does. It says it waits for the previous frame, but in reality, it waits for the work that was last executed against the current back buffer.

Before you can resize the swapchain, you need to wait for the work that was executed against *all* back buffers, not just the current one.

14 hours ago, SoldierOfLight said:

WaitForPreviousFrame isn't sufficient, and also isn't a good implementation of what it says it does. It says it waits for the previous frame, but in reality, it waits for the work that was last executed against the current back buffer.

Before you can resize the swapchain, you need to wait for the work that was executed against *all* back buffers, not just the current one.

Thank you! I constructed the following procedure:


void flushGpu()
{
    for (int i = 0; i < frameBufferCount; i++)
    {
        uint64_t fenceValueForSignal = ++fenceValue[i];
        commandQueue->Signal(fence[i].Get(), fenceValueForSignal);
        if (fence[i]->GetCompletedValue() < fenceValue[i])
        {
            fence[i]->SetEventOnCompletion(fenceValueForSignal, fenceEvent);
            WaitForSingleObject(fenceEvent, INFINITE);
        }
    }
    frameIndex = 0;
}


I call it before resizing, and the error is gone.

This topic is closed to new replies.

Advertisement