D3D12: Use waitable swap chain

This commit is contained in:
Alvin Wong 2024-07-31 03:27:08 +08:00
parent 3e0c10d393
commit 83df6ce27c
6 changed files with 73 additions and 0 deletions

View File

@ -32,6 +32,7 @@
#include "core/config/project_settings.h"
#include "core/io/marshalls.h"
#include "main/main.h"
#include "servers/rendering/rendering_device.h"
#include "thirdparty/zlib/zlib.h"
@ -2294,11 +2295,32 @@ Error RenderingDeviceDriverD3D12::command_queue_execute_and_present(CommandQueue
bool any_present_failed = false;
for (uint32_t i = 0; i < p_swap_chains.size(); i++) {
SwapChain *swap_chain = (SwapChain *)(p_swap_chains[i].id);
bool skip_present = false;
if (swap_chain->needs_wait) {
DWORD wait = WaitForSingleObject(swap_chain->frame_latency_waitable_obj, 0);
if (wait != WAIT_OBJECT_0) {
skip_present = true;
if (wait == WAIT_TIMEOUT) {
// Force a redraw so that we can present the latest frame
// for this swap chain on the next redraw.
Main::force_redraw();
} else if (wait == WAIT_FAILED) {
DWORD error = GetLastError();
ERR_PRINT(vformat("Wait for frame latency waitable failed with error: 0x%08X", (unsigned)error));
} else {
ERR_PRINT(vformat("Wait for frame latency waitable failed, WaitForSingleObject returned 0x%08X", (unsigned)wait));
}
}
}
if (skip_present) {
continue;
}
res = swap_chain->d3d_swap_chain->Present(swap_chain->sync_interval, swap_chain->present_flags);
if (!SUCCEEDED(res)) {
print_verbose(vformat("D3D12: Presenting swapchain failed with error 0x%08ux.", (uint64_t)res));
any_present_failed = true;
}
swap_chain->needs_wait = true;
}
return any_present_failed ? FAILED : OK;
@ -2412,6 +2434,10 @@ void RenderingDeviceDriverD3D12::command_buffer_execute_secondary(CommandBufferI
void RenderingDeviceDriverD3D12::_swap_chain_release(SwapChain *p_swap_chain) {
_swap_chain_release_buffers(p_swap_chain);
CloseHandle(p_swap_chain->frame_latency_waitable_obj);
p_swap_chain->frame_latency_waitable_obj = nullptr;
p_swap_chain->needs_wait = false;
p_swap_chain->d3d_swap_chain.Reset();
}
@ -2493,6 +2519,8 @@ Error RenderingDeviceDriverD3D12::swap_chain_resize(CommandQueueID p_cmd_queue,
break;
}
creation_flags |= DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT;
print_verbose("Using swap chain flags: " + itos(creation_flags) + ", sync interval: " + itos(sync_interval) + ", present flags: " + itos(present_flags));
if (swap_chain->d3d_swap_chain != nullptr && creation_flags != swap_chain->creation_flags) {
@ -2535,6 +2563,9 @@ Error RenderingDeviceDriverD3D12::swap_chain_resize(CommandQueueID p_cmd_queue,
res = context_driver->dxgi_factory_get()->MakeWindowAssociation(surface->hwnd, DXGI_MWA_NO_ALT_ENTER | DXGI_MWA_NO_WINDOW_CHANGES);
ERR_FAIL_COND_V(!SUCCEEDED(res), ERR_CANT_CREATE);
swap_chain->frame_latency_waitable_obj = swap_chain->d3d_swap_chain->GetFrameLatencyWaitableObject();
swap_chain->needs_wait = true;
}
res = swap_chain->d3d_swap_chain->GetDesc1(&swap_chain_desc);
@ -2555,6 +2586,8 @@ Error RenderingDeviceDriverD3D12::swap_chain_resize(CommandQueueID p_cmd_queue,
swap_chain->render_targets_info.reserve(swap_chain_desc.BufferCount);
swap_chain->framebuffers.reserve(swap_chain_desc.BufferCount);
swap_chain->d3d_swap_chain->SetMaximumFrameLatency(swap_chain_desc.BufferCount - 1);
for (uint32_t i = 0; i < swap_chain_desc.BufferCount; i++) {
// Retrieve the resource corresponding to the swap chain's buffer.
ID3D12Resource *render_target = nullptr;
@ -2623,6 +2656,24 @@ void RenderingDeviceDriverD3D12::swap_chain_free(SwapChainID p_swap_chain) {
memdelete(swap_chain);
}
void RenderingDeviceDriverD3D12::wait_for_present(SwapChainID p_swap_chain) {
SwapChain *swap_chain = (SwapChain *)(p_swap_chain.id);
if (swap_chain->needs_wait) {
DWORD wait = WaitForSingleObject(swap_chain->frame_latency_waitable_obj, 1000);
if (wait != WAIT_OBJECT_0) {
if (wait == WAIT_FAILED) {
DWORD error = GetLastError();
ERR_PRINT(vformat("Wait for frame latency waitable failed with error: 0x%08X", (unsigned)error));
} else {
ERR_PRINT(vformat("Wait for frame latency waitable failed, WaitForSingleObject returned 0x%08X", (unsigned)wait));
}
} else {
swap_chain->needs_wait = false;
}
}
}
/*********************/
/**** FRAMEBUFFER ****/
/*********************/

View File

@ -512,6 +512,8 @@ private:
TightLocalVector<TextureInfo> render_targets_info;
TightLocalVector<FramebufferID> framebuffers;
RDD::DataFormat data_format = DATA_FORMAT_MAX;
HANDLE frame_latency_waitable_obj = nullptr;
bool needs_wait = false;
};
void _swap_chain_release(SwapChain *p_swap_chain);
@ -524,6 +526,7 @@ public:
virtual RenderPassID swap_chain_get_render_pass(SwapChainID p_swap_chain) override;
virtual DataFormat swap_chain_get_format(SwapChainID p_swap_chain) override;
virtual void swap_chain_free(SwapChainID p_swap_chain) override;
virtual void wait_for_present(SwapChainID p_swap_chain) override;
/*********************/
/**** FRAMEBUFFER ****/

View File

@ -3003,6 +3003,12 @@ String DisplayServerWindows::keyboard_get_layout_name(int p_index) const {
void DisplayServerWindows::process_events() {
ERR_FAIL_COND(!Thread::is_main_thread());
#ifdef RD_ENABLED
if (rendering_device) {
rendering_device->screen_wait_for_present(get_focused_window() == INVALID_WINDOW_ID ? MAIN_WINDOW_ID : get_focused_window());
}
#endif
if (!drop_events) {
joypad->process_joypads();
}

View File

@ -3544,6 +3544,15 @@ Error RenderingDevice::screen_free(DisplayServer::WindowID p_screen) {
return OK;
}
void RenderingDevice::screen_wait_for_present(DisplayServer::WindowID p_screen) {
_THREAD_SAFE_METHOD_
HashMap<DisplayServer::WindowID, RDD::SwapChainID>::ConstIterator it = screen_swap_chains.find(p_screen);
ERR_FAIL_COND_MSG(it == screen_swap_chains.end(), "A swap chain was not created for the screen.");
driver->wait_for_present(it->value);
}
/*******************/
/**** DRAW LIST ****/
/*******************/

View File

@ -1054,6 +1054,7 @@ public:
int screen_get_height(DisplayServer::WindowID p_screen = DisplayServer::MAIN_WINDOW_ID) const;
FramebufferFormatID screen_get_framebuffer_format(DisplayServer::WindowID p_screen = DisplayServer::MAIN_WINDOW_ID) const;
Error screen_free(DisplayServer::WindowID p_screen = DisplayServer::MAIN_WINDOW_ID);
void screen_wait_for_present(DisplayServer::WindowID p_screen);
/*************************/
/**** DRAW LISTS (II) ****/

View File

@ -459,6 +459,9 @@ public:
// Wait until all rendering associated to the swap chain is finished before deleting it.
virtual void swap_chain_free(SwapChainID p_swap_chain) = 0;
// Wait for the swap chain to be presented on the screen.
virtual void wait_for_present(SwapChainID p_swap_chain){};
/*********************/
/**** FRAMEBUFFER ****/
/*********************/