Kode / Kinc

Modern low level game library and hardware abstraction.
http://kinc.tech
zlib License
511 stars 121 forks source link

Fix potential crash when resizing windows when using D3D12 graphics backend #829

Closed FelixK15 closed 1 month ago

FelixK15 commented 9 months ago

Fixed the crash described in https://github.com/Kode/Kinc/issues/822

The issues basically boils down to this: When the user resized the window at exactly the wrong time, the current commandbuffer that the GPU's processing is the commandbuffer that writes to the backbuffer at index 0.

When, at that time, the resize happens, the code sets windows[current_window].currentBuffer = 0 inside kinc_g4_begin(). this results in the commandqueue trying to write to the wrong backbuffer for the next frame (basically it would then try to write to backbuffer 0 again even though it expects to write to backbuffer 1 - if that makes sense).

This requires further testing with an actual workload as I only tested this with the input test.

My testcode for this basically looked like this:

static void update(void *data) {
    static int timer = 0;
    ++timer;

    if ( timer == 3 ) {
        ShowWindow(hwnd, SW_MINIMIZE); //This was implemented inside Backends\System\Windows\Sources\kinc\backend\window.c.h
    }

    if (timer == 6) {
        ShowWindow(hwnd, SW_SHOWNORMAL);  //This was implemented inside Backends\System\Windows\Sources\kinc\backend\window.c.h
        timer = 0;
    }

    kinc_g4_begin(0);
    kinc_g4_clear(KINC_G4_CLEAR_COLOR, 0, 0.0f, 0);
    kinc_g4_end(0);
    kinc_g4_swap_buffers();
}

int kickstart(int argc, char **argv) {
        kinc_init("Shader", 1024, 768, NULL, NULL);
    kinc_set_update_callback(update, NULL);
    kinc_start();

        return 0;
}

Before my change the crash would happen at some point preceeded by logspam that read like this:

D3D12 ERROR: ID3D12CommandQueue::ExecuteCommandLists: A command list, which writes to a swapchain back buffer, may only be executed when that back buffer is the back buffer that will be presented during the next call to Present*. Such a back buffer is also referred to as the "current back buffer". Swap Chain: 0x000002793F1D5D40:'Unnamed Object' - Current Back Buffer Buffer: 0x000002793F22F1B0:'Backbuffer (index 0)' - Attempted Write Buffer: 0x000002793F230070:'Backbuffer (index 1)' [ STATE_SETTING ERROR #907: EXECUTECOMMANDLISTS_WRONGSWAPCHAINBUFFERREFERENCE]
RobDangerous commented 1 month ago

Thanks a ton for the pull request. It was not the actual fix but it got me on the right track. I put this code at the start of update to test:

if (resized) {
    kinc_window_resize(0, 400, 400);
}
else {
    kinc_window_resize(0, 600, 600);
}
resized = !resized;

Originally this caused D3D12 ERROR: ID3D12Resource2::<final-release>: CORRUPTION: An ID3D12Resource object (0x000001AB94F33830:'Backbuffer (index 0)') is referenced by GPU operations in-flight on Command Queue (0x000001AB8E8270F0:'Unnamed ID3D12CommandQueue Object'). 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] and kinc_microsoft_affirm triggers in line 531 in Direct3D12.c.h (kinc_g5_begin).

With your fix for me it caused D3D12 ERROR: ID3D12CommandQueue::ExecuteCommandLists: A command list, which writes to a swapchain back buffer, may only be executed when that back buffer is the back buffer that will be presented during the next call to Present*. Such a back buffer is also referred to as the "current back buffer". Swap Chain: 0x000002E2E5BE5FC0:'Unnamed Object' - Current Back Buffer Buffer: 0x000002E2E5D3F5A0:'Backbuffer (index 0)' - Attempted Write Buffer: 0x000002E2DF5D7B40:'Backbuffer (index 1)' [ STATE_SETTING ERROR #907: EXECUTECOMMANDLISTS_WRONGSWAPCHAINBUFFERREFERENCE] D3D12: Removing Device. D3D12 ERROR: ID3D12Device::RemoveDevice: Device removal has been triggered for the following reason (DXGI_ERROR_ACCESS_DENIED: The application attempted to use a resource it does not access to. This could be, for example, rendering to a texture while only having read access.). [ EXECUTION ERROR #232: DEVICE_REMOVAL_PROCESS_AT_FAULT] and kinc_microsoft_affirm triggers in line 592 in Direct3D12.c.h (kinc_g5_swap_buffers).

I do not fully understand yet if that's my fault somewhere or if D3D itself wants things to go back to the first framebuffer after a resize. Anyway, kinc_g4_begin has a kinc_g5_command_list_execute early on and we delete all framebuffers immediately afterwards on resize, so that's not good. On resize I now wait for it to finish and that seems to fix it.

mikaib commented 1 month ago

Lovely.