Skip to content

[RFC] D3D12: Use waitable swap chain to reduce input latency #94960

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions drivers/d3d12/rendering_device_driver_d3d12.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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();
}

Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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);
Expand All @@ -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;
Expand Down Expand Up @@ -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 ****/
/*********************/
Expand Down
3 changes: 3 additions & 0 deletions drivers/d3d12/rendering_device_driver_d3d12.h
Original file line number Diff line number Diff line change
Expand Up @@ -512,6 +512,8 @@ class RenderingDeviceDriverD3D12 : public RenderingDeviceDriver {
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);
Expand All @@ -524,6 +526,7 @@ class RenderingDeviceDriverD3D12 : public RenderingDeviceDriver {
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 ****/
Expand Down
6 changes: 6 additions & 0 deletions platform/windows/display_server_windows.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
Expand Down
9 changes: 9 additions & 0 deletions servers/rendering/rendering_device.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 ****/
/*******************/
Expand Down
1 change: 1 addition & 0 deletions servers/rendering/rendering_device.h
Original file line number Diff line number Diff line change
Expand Up @@ -1054,6 +1054,7 @@ class RenderingDevice : public RenderingDeviceCommons {
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) ****/
Expand Down
3 changes: 3 additions & 0 deletions servers/rendering/rendering_device_driver.h
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,9 @@ class RenderingDeviceDriver : public RenderingDeviceCommons {
// 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 ****/
/*********************/
Expand Down
Loading