Signed-off-by: Philip Rebohle philip.rebohle@tu-dortmund.de --- v2: Don't set manual-reset flag for the event.
As mentioned in the original series, Monster Hunter World needs this functionality in order to run in Dx12 mode.
dlls/dxgi/swapchain.c | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-)
diff --git a/dlls/dxgi/swapchain.c b/dlls/dxgi/swapchain.c index f19b58da2e..b04e8b4e87 100644 --- a/dlls/dxgi/swapchain.c +++ b/dlls/dxgi/swapchain.c @@ -1094,6 +1094,9 @@ struct d3d12_swapchain IDXGIOutput *target; DXGI_SWAP_CHAIN_DESC1 desc; DXGI_SWAP_CHAIN_FULLSCREEN_DESC fullscreen_desc; + + ID3D12Fence *frame_latency_fence; + HANDLE *frame_latency_event; };
static DXGI_FORMAT dxgi_format_from_vk_format(VkFormat vk_format) @@ -1887,6 +1890,12 @@ static void d3d12_swapchain_destroy(struct d3d12_swapchain *swapchain)
d3d12_swapchain_destroy_buffers(swapchain, TRUE);
+ if (swapchain->frame_latency_event) + CloseHandle(swapchain->frame_latency_event); + + if (swapchain->frame_latency_fence) + ID3D12Fence_Release(swapchain->frame_latency_fence); + if (swapchain->command_queue) ID3D12CommandQueue_Release(swapchain->command_queue);
@@ -2906,7 +2915,7 @@ static HRESULT d3d12_swapchain_init(struct d3d12_swapchain *swapchain, IWineDXGI FIXME("Ignoring scaling %#x.\n", swapchain_desc->Scaling); if (swapchain_desc->AlphaMode && swapchain_desc->AlphaMode != DXGI_ALPHA_MODE_IGNORE) FIXME("Ignoring alpha mode %#x.\n", swapchain_desc->AlphaMode); - if (swapchain_desc->Flags) + if (swapchain_desc->Flags & ~(DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT)) FIXME("Ignoring swapchain flags %#x.\n", swapchain_desc->Flags);
if (fullscreen_desc->RefreshRate.Numerator || fullscreen_desc->RefreshRate.Denominator) @@ -2983,6 +2992,25 @@ static HRESULT d3d12_swapchain_init(struct d3d12_swapchain *swapchain, IWineDXGI return hresult_from_vk_result(vr); }
+ if (swapchain_desc->Flags & DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT) + { + if (FAILED(hr = ID3D12Device_CreateFence(device, DXGI_MAX_SWAP_CHAIN_BUFFERS, + 0, &IID_ID3D12Fence, (void **)&swapchain->frame_latency_fence))) + { + WARN("Failed to create D3D12 fence, hr %d.\n", hr); + d3d12_swapchain_destroy(swapchain); + return hr; + } + + if (!(swapchain->frame_latency_event = CreateEventW(NULL, FALSE, TRUE, NULL))) + { + hr = HRESULT_FROM_WIN32(GetLastError()); + WARN("Failed to create frame latency event, hr %d.\n", hr); + d3d12_swapchain_destroy(swapchain); + return hr; + } + } + IWineDXGIFactory_AddRef(swapchain->factory = factory);
return S_OK;
Signed-off-by: Philip Rebohle philip.rebohle@tu-dortmund.de --- v2: Do not manually reset the event.
dlls/dxgi/swapchain.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+)
diff --git a/dlls/dxgi/swapchain.c b/dlls/dxgi/swapchain.c index b04e8b4e87..875aff0bb6 100644 --- a/dlls/dxgi/swapchain.c +++ b/dlls/dxgi/swapchain.c @@ -1097,6 +1097,9 @@ struct d3d12_swapchain
ID3D12Fence *frame_latency_fence; HANDLE *frame_latency_event; + + uint64_t frame_number; + uint32_t frame_latency; };
static DXGI_FORMAT dxgi_format_from_vk_format(VkFormat vk_format) @@ -2166,6 +2169,25 @@ static HRESULT d3d12_swapchain_present(struct d3d12_swapchain *swapchain, return hresult_from_vk_result(vr); }
+ if (swapchain->desc.Flags & DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT) + { + ++swapchain->frame_number; + + if (FAILED(hr = ID3D12CommandQueue_Signal(swapchain->command_queue, + swapchain->frame_latency_fence, swapchain->frame_number))) + { + ERR("Failed to signal frame latency fence, hr %#x.\n", hr); + return hr; + } + + if (FAILED(hr = ID3D12Fence_SetEventOnCompletion(swapchain->frame_latency_fence, + swapchain->frame_number - swapchain->frame_latency, swapchain->frame_latency_event))) + { + FIXME("Failed to enqueue frame latency event, hr %#x.\n", hr); + return hr; + } + } + swapchain->current_buffer_index = (swapchain->current_buffer_index + 1) % swapchain->desc.BufferCount; vr = d3d12_swapchain_acquire_next_back_buffer(swapchain); if (vr == VK_ERROR_OUT_OF_DATE_KHR) @@ -2994,6 +3016,9 @@ static HRESULT d3d12_swapchain_init(struct d3d12_swapchain *swapchain, IWineDXGI
if (swapchain_desc->Flags & DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT) { + swapchain->frame_number = DXGI_MAX_SWAP_CHAIN_BUFFERS; + swapchain->frame_latency = 1; + if (FAILED(hr = ID3D12Device_CreateFence(device, DXGI_MAX_SWAP_CHAIN_BUFFERS, 0, &IID_ID3D12Fence, (void **)&swapchain->frame_latency_fence))) {
On Thu, 16 Apr 2020 at 01:47, Philip Rebohle philip.rebohle@tu-dortmund.de wrote:
- if (swapchain->desc.Flags & DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT)
- {
++swapchain->frame_number;
if (FAILED(hr = ID3D12CommandQueue_Signal(swapchain->command_queue,
swapchain->frame_latency_fence, swapchain->frame_number)))
{
ERR("Failed to signal frame latency fence, hr %#x.\n", hr);
return hr;
}
if (FAILED(hr = ID3D12Fence_SetEventOnCompletion(swapchain->frame_latency_fence,
swapchain->frame_number - swapchain->frame_latency, swapchain->frame_latency_event)))
{
FIXME("Failed to enqueue frame latency event, hr %#x.\n", hr);
FIXME doesn't seem appropriate here. If we take the position that this should never fail, this should be an ERR like the one for the ID3D12CommandQueue_Signal() call above. FIXME would be for missing/incomplete/broken functionality.
Signed-off-by: Philip Rebohle philip.rebohle@tu-dortmund.de --- dlls/dxgi/swapchain.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/dlls/dxgi/swapchain.c b/dlls/dxgi/swapchain.c index 875aff0bb6..71a580f78c 100644 --- a/dlls/dxgi/swapchain.c +++ b/dlls/dxgi/swapchain.c @@ -2621,9 +2621,11 @@ static HRESULT STDMETHODCALLTYPE d3d12_swapchain_GetMaximumFrameLatency(IDXGISwa
static HANDLE STDMETHODCALLTYPE d3d12_swapchain_GetFrameLatencyWaitableObject(IDXGISwapChain3 *iface) { - FIXME("iface %p stub!\n", iface); + struct d3d12_swapchain *swapchain = d3d12_swapchain_from_IDXGISwapChain3(iface);
- return NULL; + TRACE("iface %p.\n", iface); + + return swapchain->frame_latency_event; }
static HRESULT STDMETHODCALLTYPE d3d12_swapchain_SetMatrixTransform(IDXGISwapChain3 *iface,
Signed-off-by: Philip Rebohle philip.rebohle@tu-dortmund.de --- v2: Do not manually reset the event.
dlls/dxgi/swapchain.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-)
diff --git a/dlls/dxgi/swapchain.c b/dlls/dxgi/swapchain.c index 71a580f78c..7b8f6c7523 100644 --- a/dlls/dxgi/swapchain.c +++ b/dlls/dxgi/swapchain.c @@ -2607,9 +2607,24 @@ static HRESULT STDMETHODCALLTYPE d3d12_swapchain_GetSourceSize(IDXGISwapChain3 *
static HRESULT STDMETHODCALLTYPE d3d12_swapchain_SetMaximumFrameLatency(IDXGISwapChain3 *iface, UINT max_latency) { - FIXME("iface %p, max_latency %u stub!\n", iface, max_latency); + struct d3d12_swapchain *swapchain = d3d12_swapchain_from_IDXGISwapChain3(iface);
- return E_NOTIMPL; + TRACE("iface %p, max_latency %u.\n", iface, max_latency); + + if (!(swapchain->desc.Flags & DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT)) + { + FIXME("DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT not set for swap chain %p.\n", iface); + return DXGI_ERROR_INVALID_CALL; + } + + if (!max_latency || max_latency > DXGI_MAX_SWAP_CHAIN_BUFFERS) + { + FIXME("Invalid maximum frame latency %u.\n", max_latency); + return DXGI_ERROR_INVALID_CALL; + } + + swapchain->frame_latency = max_latency; + return S_OK; }
static HRESULT STDMETHODCALLTYPE d3d12_swapchain_GetMaximumFrameLatency(IDXGISwapChain3 *iface, UINT *max_latency)
On Thu, 16 Apr 2020 at 01:49, Philip Rebohle philip.rebohle@tu-dortmund.de wrote:
- if (!(swapchain->desc.Flags & DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT))
- {
FIXME("DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT not set for swap chain %p.\n", iface);
return DXGI_ERROR_INVALID_CALL;
- }
- if (!max_latency || max_latency > DXGI_MAX_SWAP_CHAIN_BUFFERS)
- {
FIXME("Invalid maximum frame latency %u.\n", max_latency);
return DXGI_ERROR_INVALID_CALL;
- }
Those should use WARN. (And likewise for patch 5/6.)
Signed-off-by: Philip Rebohle philip.rebohle@tu-dortmund.de --- dlls/dxgi/swapchain.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-)
diff --git a/dlls/dxgi/swapchain.c b/dlls/dxgi/swapchain.c index 7b8f6c7523..7d15b4d48d 100644 --- a/dlls/dxgi/swapchain.c +++ b/dlls/dxgi/swapchain.c @@ -2629,9 +2629,18 @@ static HRESULT STDMETHODCALLTYPE d3d12_swapchain_SetMaximumFrameLatency(IDXGISwa
static HRESULT STDMETHODCALLTYPE d3d12_swapchain_GetMaximumFrameLatency(IDXGISwapChain3 *iface, UINT *max_latency) { - FIXME("iface %p, max_latency %p stub!\n", iface, max_latency); + struct d3d12_swapchain *swapchain = d3d12_swapchain_from_IDXGISwapChain3(iface);
- return E_NOTIMPL; + TRACE("iface %p, max_latency %p.\n", iface, max_latency); + + if (!(swapchain->desc.Flags & DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT)) + { + FIXME("DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT not set for swap chain %p.\n", iface); + return DXGI_ERROR_INVALID_CALL; + } + + *max_latency = swapchain->frame_latency; + return S_OK; }
static HANDLE STDMETHODCALLTYPE d3d12_swapchain_GetFrameLatencyWaitableObject(IDXGISwapChain3 *iface)
Signed-off-by: Philip Rebohle philip.rebohle@tu-dortmund.de --- v2: - The event is not actually a manual-reset event. - Always use a flip model swap effect since that is required for waitable swap chains.
dlls/dxgi/tests/dxgi.c | 122 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+)
diff --git a/dlls/dxgi/tests/dxgi.c b/dlls/dxgi/tests/dxgi.c index 3887982634..41c1101608 100644 --- a/dlls/dxgi/tests/dxgi.c +++ b/dlls/dxgi/tests/dxgi.c @@ -6188,6 +6188,127 @@ static void test_factory_check_feature_support(void) ok(!ref_count, "Factory has %u references left.\n", ref_count); }
+static void test_frame_latency_event(IUnknown *device, BOOL is_d3d12) +{ + DXGI_SWAP_CHAIN_DESC1 swapchain_desc; + IDXGISwapChain2 *swapchain2; + IDXGISwapChain1 *swapchain1; + IDXGIFactory2 *factory2; + IDXGIFactory *factory; + UINT frame_latency; + DWORD wait_result; + ULONG ref_count; + unsigned int i; + HANDLE event; + HWND window; + HRESULT hr; + + get_factory(device, is_d3d12, &factory); + + hr = IDXGIFactory_QueryInterface(factory, &IID_IDXGIFactory2, (void**)&factory2); + IDXGIFactory_Release(factory); + + if (FAILED(hr)) + { + win_skip("IDXGIFactory2 not available.\n"); + return; + } + + window = CreateWindowA("static", "dxgi_test", 0, 640, 480, 0, 0, 0, 0, 0, 0); + + swapchain_desc.Width = 640; + swapchain_desc.Height = 480; + swapchain_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + swapchain_desc.Stereo = FALSE; + swapchain_desc.SampleDesc.Count = 1; + swapchain_desc.SampleDesc.Quality = 0; + swapchain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + swapchain_desc.BufferCount = 2; + swapchain_desc.Scaling = DXGI_SCALING_STRETCH; + swapchain_desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; + swapchain_desc.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED; + swapchain_desc.Flags = 0; + + hr = IDXGIFactory2_CreateSwapChainForHwnd(factory2, device, + window, &swapchain_desc, NULL, NULL, &swapchain1); + ok(hr == S_OK, "Failed to create swap chain, hr %#x.\n", hr); + + hr = IDXGISwapChain1_QueryInterface(swapchain1, &IID_IDXGISwapChain2, (void**)&swapchain2); + IDXGISwapChain1_Release(swapchain1); + + if (FAILED(hr)) + { + win_skip("IDXGISwapChain2 not available.\n"); + IDXGIFactory2_Release(factory2); + return; + } + + /* test swap chain without waitable object */ + frame_latency = 0xdeadbeef; + hr = IDXGISwapChain2_GetMaximumFrameLatency(swapchain2, &frame_latency); + ok(hr == DXGI_ERROR_INVALID_CALL, "Got unexpected hr %#x.\n", hr); + ok(frame_latency == 0xdeadbeef, "Got unexpected frame latency %#x.\n", frame_latency); + hr = IDXGISwapChain2_SetMaximumFrameLatency(swapchain2, 1); + ok(hr == DXGI_ERROR_INVALID_CALL, "Got unexpected hr %#x.\n", hr); + event = IDXGISwapChain2_GetFrameLatencyWaitableObject(swapchain2); + ok(!event, "Got unexpected event %p.\n", event); + + ref_count = IDXGISwapChain2_Release(swapchain2); + ok(!ref_count, "Swap chain has %u references left.\n", ref_count); + + /* test swap chain with waitable object */ + swapchain_desc.Flags = DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT; + + hr = IDXGIFactory2_CreateSwapChainForHwnd(factory2, device, + window, &swapchain_desc, NULL, NULL, &swapchain1); + ok(hr == S_OK, "Failed to create swap chain, hr %#x.\n", hr); + hr = IDXGISwapChain1_QueryInterface(swapchain1, &IID_IDXGISwapChain2, (void**)&swapchain2); + ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); + IDXGISwapChain1_Release(swapchain1); + + event = IDXGISwapChain2_GetFrameLatencyWaitableObject(swapchain2); + ok(!!event, "Got unexpected event %p.\n", event); + + /* auto-reset event */ + wait_result = WaitForSingleObject(event, 0); + ok(!wait_result, "Got unexpected wait result %#x.\n", wait_result); + wait_result = WaitForSingleObject(event, 0); + ok(wait_result == WAIT_TIMEOUT, "Got unexpected wait result %#x.\n", wait_result); + + hr = IDXGISwapChain2_GetMaximumFrameLatency(swapchain2, &frame_latency); + ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); + ok(frame_latency == 1, "Got unexpected frame latency %#x.\n", frame_latency); + + hr = IDXGISwapChain2_SetMaximumFrameLatency(swapchain2, 0); + ok(hr == DXGI_ERROR_INVALID_CALL, "Got unexpected hr %#x.\n", hr); + hr = IDXGISwapChain2_GetMaximumFrameLatency(swapchain2, &frame_latency); + ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); + ok(frame_latency == 1, "Got unexpected frame latency %#x.\n", frame_latency); + + hr = IDXGISwapChain2_SetMaximumFrameLatency(swapchain2, 2); + ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); + hr = IDXGISwapChain2_GetMaximumFrameLatency(swapchain2, &frame_latency); + ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); + ok(frame_latency == 2, "Got unexpected frame latency %#x.\n", frame_latency); + + for (i = 0; i < 5; i++) + { + hr = IDXGISwapChain2_Present(swapchain2, 0, 0); + ok(hr == S_OK, "Present failed with hr %#x.\n", hr); + } + + wait_result = WaitForSingleObject(event, 1000); + ok(!wait_result, "Got unexpected wait result %#x.\n", wait_result); + + ref_count = IDXGISwapChain2_Release(swapchain2); + ok(!ref_count, "Swap chain has %u references left.\n", ref_count); + + DestroyWindow(window); + + ref_count = IDXGIFactory2_Release(factory2); + ok(ref_count == !is_d3d12, "Factory has %u references left.\n", ref_count); +} + static void run_on_d3d10(void (*test_func)(IUnknown *device, BOOL is_d3d12)) { IDXGIDevice *device; @@ -6325,6 +6446,7 @@ START_TEST(dxgi) run_on_d3d12(test_swapchain_formats); run_on_d3d12(test_output_ownership); run_on_d3d12(test_cursor_clipping); + run_on_d3d12(test_frame_latency_event);
FreeLibrary(d3d12_module); }
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=69728
Your paranoid android.
=== w1064v1507 (32 bit report) ===
dxgi: dxgi.c:5200: Test failed: Got unexpected message 0x31f, hwnd 000F0088, wparam 0x1, lparam 0.
=== w1064v1809_2scr (32 bit report) ===
dxgi: dxgi.c:4236: Test failed: Failed to create swapchain, hr 0x8007000e.
=== w1064v1809_he (32 bit report) ===
dxgi: dxgi.c:5200: Test failed: Got unexpected message 0x31f, hwnd 000702D6, wparam 0x1, lparam 0.
On Thu, 16 Apr 2020 at 01:50, Philip Rebohle philip.rebohle@tu-dortmund.de wrote:
- window = CreateWindowA("static", "dxgi_test", 0, 640, 480, 0, 0, 0, 0, 0, 0);
Please use create_window() here.
On Thu, 16 Apr 2020 at 01:47, Philip Rebohle philip.rebohle@tu-dortmund.de wrote:
@@ -1094,6 +1094,9 @@ struct d3d12_swapchain IDXGIOutput *target; DXGI_SWAP_CHAIN_DESC1 desc; DXGI_SWAP_CHAIN_FULLSCREEN_DESC fullscreen_desc;
- ID3D12Fence *frame_latency_fence;
- HANDLE *frame_latency_event;
};
That should probably be "HANDLE frame_latency_event;"