vkd3d-proton/tests/d3d12_sync.c

1190 lines
51 KiB
C

/*
* Copyright 2016-2017 Józef Kucia for CodeWeavers
* Copyright 2020-2021 Philip Rebohle for Valve Corporation
* Copyright 2020-2021 Joshua Ashton for Valve Corporation
* Copyright 2020-2021 Hans-Kristian Arntzen for Valve Corporation
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#define VKD3D_DBG_CHANNEL VKD3D_DBG_CHANNEL_API
#include "d3d12_crosstest.h"
void test_queue_wait(void)
{
D3D12_TEXTURE_COPY_LOCATION dst_location, src_location;
ID3D12GraphicsCommandList *command_list;
ID3D12Resource *readback_buffer, *cb;
ID3D12CommandQueue *queue, *queue2;
D3D12_RESOURCE_DESC resource_desc;
uint64_t row_pitch, buffer_size;
struct test_context_desc desc;
struct resource_readback rb;
ID3D12Fence *fence, *fence2;
struct test_context context;
ID3D12Device *device;
unsigned int ret;
uint64_t value;
float color[4];
HANDLE event;
HRESULT hr;
static const float blue[] = {0.0f, 0.0f, 1.0f, 1.0f};
static const float green[] = {0.0f, 1.0f, 0.0f, 1.0f};
static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};
static const DWORD ps_code[] =
{
#if 0
float4 color;
float4 main(float4 position : SV_POSITION) : SV_Target
{
return color;
}
#endif
0x43425844, 0xd18ead43, 0x8b8264c1, 0x9c0a062d, 0xfc843226, 0x00000001, 0x000000e0, 0x00000003,
0x0000002c, 0x00000060, 0x00000094, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008, 0x00000020,
0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000000f, 0x505f5653, 0x5449534f, 0x004e4f49,
0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003,
0x00000000, 0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x00000044, 0x00000050,
0x00000011, 0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x03000065, 0x001020f2,
0x00000000, 0x06000036, 0x001020f2, 0x00000000, 0x00208e46, 0x00000000, 0x00000000, 0x0100003e,
};
static const D3D12_SHADER_BYTECODE ps = {ps_code, sizeof(ps_code)};
memset(&desc, 0, sizeof(desc));
desc.no_root_signature = true;
if (!init_test_context(&context, &desc))
return;
device = context.device;
command_list = context.list;
queue = context.queue;
queue2 = create_command_queue(device, D3D12_COMMAND_LIST_TYPE_DIRECT, D3D12_COMMAND_QUEUE_PRIORITY_NORMAL);
event = create_event();
ok(event, "Failed to create event.\n");
hr = ID3D12Device_CreateFence(device, 1, D3D12_FENCE_FLAG_NONE, &IID_ID3D12Fence, (void **)&fence);
ok(hr == S_OK, "Failed to create fence, hr %#x.\n", hr);
hr = ID3D12Device_CreateFence(device, 0, D3D12_FENCE_FLAG_NONE, &IID_ID3D12Fence, (void **)&fence2);
ok(hr == S_OK, "Failed to create fence, hr %#x.\n", hr);
context.root_signature = create_cb_root_signature(context.device,
0, D3D12_SHADER_VISIBILITY_PIXEL, D3D12_ROOT_SIGNATURE_FLAG_NONE);
context.pipeline_state = create_pipeline_state(context.device,
context.root_signature, context.render_target_desc.Format, NULL, &ps, NULL);
cb = create_upload_buffer(device, sizeof(color), NULL);
resource_desc = ID3D12Resource_GetDesc(context.render_target);
row_pitch = align(resource_desc.Width * format_size(resource_desc.Format), D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
buffer_size = row_pitch * resource_desc.Height;
readback_buffer = create_readback_buffer(device, buffer_size);
ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);
ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
ID3D12GraphicsCommandList_SetGraphicsRootConstantBufferView(command_list, 0,
ID3D12Resource_GetGPUVirtualAddress(cb));
ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);
transition_resource_state(command_list, context.render_target,
D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
dst_location.pResource = readback_buffer;
dst_location.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
dst_location.PlacedFootprint.Offset = 0;
dst_location.PlacedFootprint.Footprint.Format = resource_desc.Format;
dst_location.PlacedFootprint.Footprint.Width = resource_desc.Width;
dst_location.PlacedFootprint.Footprint.Height = resource_desc.Height;
dst_location.PlacedFootprint.Footprint.Depth = 1;
dst_location.PlacedFootprint.Footprint.RowPitch = row_pitch;
src_location.pResource = context.render_target;
src_location.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
src_location.SubresourceIndex = 0;
ID3D12GraphicsCommandList_CopyTextureRegion(command_list, &dst_location, 0, 0, 0, &src_location, NULL);
transition_resource_state(command_list, context.render_target,
D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);
hr = ID3D12GraphicsCommandList_Close(command_list);
ok(hr == S_OK, "Failed to close command list, hr %#x.\n", hr);
/* Wait() with signaled fence */
update_buffer_data(cb, 0, sizeof(green), &green);
queue_wait(queue, fence, 1);
exec_command_list(queue, command_list);
wait_queue_idle(device, queue);
init_readback(&rb, readback_buffer, buffer_size, resource_desc.Width, resource_desc.Height, 1, row_pitch);
check_readback_data_uint(&rb, NULL, 0xff00ff00, 0);
release_resource_readback(&rb);
/* Wait() before CPU signal */
update_buffer_data(cb, 0, sizeof(blue), &blue);
queue_wait(queue, fence, 2);
exec_command_list(queue, command_list);
queue_signal(queue, fence2, 1);
hr = ID3D12Fence_SetEventOnCompletion(fence2, 1, event);
ok(hr == S_OK, "Failed to set event on completion, hr %#x.\n", hr);
ret = wait_event(event, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
init_readback(&rb, readback_buffer, buffer_size, resource_desc.Width, resource_desc.Height, 1, row_pitch);
check_readback_data_uint(&rb, NULL, 0xff00ff00, 0);
release_resource_readback(&rb);
value = ID3D12Fence_GetCompletedValue(fence2);
ok(value == 0, "Got unexpected value %"PRIu64".\n", value);
hr = ID3D12Fence_Signal(fence, 2);
ok(hr == S_OK, "Failed to signal fence, hr %#x.\n", hr);
ret = wait_event(event, INFINITE);
ok(ret == WAIT_OBJECT_0, "Got unexpected return value %#x.\n", ret);
ret = wait_event(event, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
init_readback(&rb, readback_buffer, buffer_size, resource_desc.Width, resource_desc.Height, 1, row_pitch);
check_readback_data_uint(&rb, NULL, 0xffff0000, 0);
release_resource_readback(&rb);
value = ID3D12Fence_GetCompletedValue(fence2);
ok(value == 1, "Got unexpected value %"PRIu64".\n", value);
/* Wait() before GPU signal */
update_buffer_data(cb, 0, sizeof(green), &green);
queue_wait(queue, fence, 3);
exec_command_list(queue, command_list);
queue_signal(queue, fence2, 2);
hr = ID3D12Fence_SetEventOnCompletion(fence2, 2, event);
ok(hr == S_OK, "Failed to set event on completion, hr %#x.\n", hr);
ret = wait_event(event, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
init_readback(&rb, readback_buffer, buffer_size, resource_desc.Width, resource_desc.Height, 1, row_pitch);
check_readback_data_uint(&rb, NULL, 0xffff0000, 0);
release_resource_readback(&rb);
value = ID3D12Fence_GetCompletedValue(fence2);
ok(value == 1, "Got unexpected value %"PRIu64".\n", value);
queue_signal(queue2, fence, 3);
ret = wait_event(event, INFINITE);
ok(ret == WAIT_OBJECT_0, "Got unexpected return value %#x.\n", ret);
ret = wait_event(event, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
init_readback(&rb, readback_buffer, buffer_size, resource_desc.Width, resource_desc.Height, 1, row_pitch);
check_readback_data_uint(&rb, NULL, 0xff00ff00, 0);
release_resource_readback(&rb);
value = ID3D12Fence_GetCompletedValue(fence2);
ok(value == 2, "Got unexpected value %"PRIu64".\n", value);
/* update constant buffer after Wait() */
queue_wait(queue, fence, 5);
exec_command_list(queue, command_list);
hr = ID3D12Fence_Signal(fence, 4);
ok(hr == S_OK, "Failed to signal fence, hr %#x.\n", hr);
update_buffer_data(cb, 0, sizeof(blue), &blue);
hr = ID3D12Fence_Signal(fence, 5);
ok(hr == S_OK, "Failed to signal fence, hr %#x.\n", hr);
wait_queue_idle(device, queue);
init_readback(&rb, readback_buffer, buffer_size, resource_desc.Width, resource_desc.Height, 1, row_pitch);
check_readback_data_uint(&rb, NULL, 0xffff0000, 0);
release_resource_readback(&rb);
hr = ID3D12Fence_Signal(fence, 6);
ok(hr == S_OK, "Failed to signal fence, hr %#x.\n", hr);
update_buffer_data(cb, 0, sizeof(green), &green);
exec_command_list(queue, command_list);
wait_queue_idle(device, queue);
init_readback(&rb, readback_buffer, buffer_size, resource_desc.Width, resource_desc.Height, 1, row_pitch);
check_readback_data_uint(&rb, NULL, 0xff00ff00, 0);
release_resource_readback(&rb);
/* Signal() and Wait() in the same command queue */
update_buffer_data(cb, 0, sizeof(blue), &blue);
queue_signal(queue, fence, 7);
queue_wait(queue, fence, 7);
exec_command_list(queue, command_list);
wait_queue_idle(device, queue);
init_readback(&rb, readback_buffer, buffer_size, resource_desc.Width, resource_desc.Height, 1, row_pitch);
check_readback_data_uint(&rb, NULL, 0xffff0000, 0);
release_resource_readback(&rb);
value = ID3D12Fence_GetCompletedValue(fence);
ok(value == 7, "Got unexpected value %"PRIu64".\n", value);
destroy_event(event);
ID3D12Fence_Release(fence);
ID3D12Fence_Release(fence2);
ID3D12Resource_Release(cb);
ID3D12CommandQueue_Release(queue2);
ID3D12Resource_Release(readback_buffer);
destroy_test_context(&context);
}
void test_graphics_compute_queue_synchronization(void)
{
ID3D12GraphicsCommandList *graphics_lists[2], *compute_lists[2];
D3D12_ROOT_SIGNATURE_DESC root_signature_desc;
ID3D12PipelineState *compute_pipeline_state;
D3D12_UNORDERED_ACCESS_VIEW_DESC uav_desc;
ID3D12CommandQueue *queue, *compute_queue;
ID3D12DescriptorHeap *cpu_heap, *gpu_heap;
ID3D12GraphicsCommandList *command_list;
D3D12_ROOT_PARAMETER root_parameters[2];
ID3D12CommandAllocator *allocators[3];
struct test_context_desc desc;
struct resource_readback rb;
struct test_context context;
ID3D12Fence *fence, *fence2;
ID3D12Resource *buffer;
ID3D12Device *device;
uint32_t value;
unsigned int i;
HRESULT hr;
unsigned int clear_value[4] = {0};
static const DWORD cs_code[] =
{
#if 0
uint expected_value;
RWByteAddressBuffer u : register(u1);
[numthreads(64, 1, 1)]
void main(void)
{
u.InterlockedCompareStore(0, expected_value, expected_value + 10);
}
#endif
0x43425844, 0x7909aab0, 0xf8576455, 0x58f9dd61, 0x3e7e64f0, 0x00000001, 0x000000e0, 0x00000003,
0x0000002c, 0x0000003c, 0x0000004c, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
0x00000008, 0x00000000, 0x00000008, 0x58454853, 0x0000008c, 0x00050050, 0x00000023, 0x0100086a,
0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x0300009d, 0x0011e000, 0x00000001, 0x02000068,
0x00000001, 0x0400009b, 0x00000040, 0x00000001, 0x00000001, 0x0800001e, 0x00100012, 0x00000000,
0x0020800a, 0x00000000, 0x00000000, 0x00004001, 0x0000000a, 0x0a0000ac, 0x0011e000, 0x00000001,
0x00004001, 0x00000000, 0x0020800a, 0x00000000, 0x00000000, 0x0010000a, 0x00000000, 0x0100003e,
};
static const DWORD ps_code[] =
{
#if 0
uint expected_value;
RWByteAddressBuffer u;
float4 main(void) : SV_Target
{
u.InterlockedCompareStore(0, expected_value, expected_value + 2);
return float4(0, 1, 0, 1);
}
#endif
0x43425844, 0x82fbce04, 0xa014204c, 0xc4d46d91, 0x1081c7a7, 0x00000001, 0x00000120, 0x00000003,
0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003, 0x00000000,
0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x000000a8, 0x00000050, 0x0000002a,
0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x0300009d, 0x0011e000, 0x00000001,
0x03000065, 0x001020f2, 0x00000000, 0x02000068, 0x00000001, 0x0800001e, 0x00100012, 0x00000000,
0x0020800a, 0x00000000, 0x00000000, 0x00004001, 0x00000002, 0x0a0000ac, 0x0011e000, 0x00000001,
0x00004001, 0x00000000, 0x0020800a, 0x00000000, 0x00000000, 0x0010000a, 0x00000000, 0x08000036,
0x001020f2, 0x00000000, 0x00004002, 0x00000000, 0x3f800000, 0x00000000, 0x3f800000, 0x0100003e,
};
static const D3D12_SHADER_BYTECODE ps = {ps_code, sizeof(ps_code)};
memset(&desc, 0, sizeof(desc));
desc.no_root_signature = true;
if (!init_test_context(&context, &desc))
return;
device = context.device;
queue = context.queue;
root_parameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_UAV;
root_parameters[0].Descriptor.ShaderRegister = 1;
root_parameters[0].Descriptor.RegisterSpace = 0;
root_parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
root_parameters[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS;
root_parameters[1].Constants.ShaderRegister = 0;
root_parameters[1].Constants.RegisterSpace = 0;
root_parameters[1].Constants.Num32BitValues = 1;
root_parameters[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
root_signature_desc.NumParameters = ARRAY_SIZE(root_parameters);
root_signature_desc.pParameters = root_parameters;
root_signature_desc.NumStaticSamplers = 0;
root_signature_desc.pStaticSamplers = NULL;
root_signature_desc.Flags = D3D12_ROOT_SIGNATURE_FLAG_NONE;
hr = create_root_signature(device, &root_signature_desc, &context.root_signature);
ok(hr == S_OK, "Failed to create root signature, hr %#x.\n", hr);
compute_pipeline_state = create_compute_pipeline_state(device, context.root_signature,
shader_bytecode(cs_code, sizeof(cs_code)));
context.pipeline_state = create_pipeline_state(context.device,
context.root_signature, context.render_target_desc.Format, NULL, &ps, NULL);
compute_queue = create_command_queue(device, D3D12_COMMAND_LIST_TYPE_COMPUTE, D3D12_COMMAND_QUEUE_PRIORITY_NORMAL);
hr = ID3D12Device_CreateFence(device, 0, D3D12_FENCE_FLAG_NONE, &IID_ID3D12Fence, (void **)&fence);
ok(hr == S_OK, "Failed to create fence, hr %#x.\n", hr);
hr = ID3D12Device_CreateFence(device, 0, D3D12_FENCE_FLAG_NONE, &IID_ID3D12Fence, (void **)&fence2);
ok(hr == S_OK, "Failed to create fence, hr %#x.\n", hr);
buffer = create_default_buffer(device, 1024,
D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
hr = ID3D12Device_CreateCommandAllocator(device, D3D12_COMMAND_LIST_TYPE_DIRECT,
&IID_ID3D12CommandAllocator, (void **)&allocators[0]);
ok(hr == S_OK, "Failed to create command allocator, hr %#x.\n", hr);
hr = ID3D12Device_CreateCommandList(device, 0, D3D12_COMMAND_LIST_TYPE_DIRECT,
allocators[0], NULL, &IID_ID3D12GraphicsCommandList, (void **)&graphics_lists[0]);
ok(hr == S_OK, "Failed to create command list, hr %#x.\n", hr);
graphics_lists[1] = context.list;
ID3D12GraphicsCommandList_AddRef(graphics_lists[1]);
for (i = 0; i < ARRAY_SIZE(compute_lists); ++i)
{
hr = ID3D12Device_CreateCommandAllocator(device, D3D12_COMMAND_LIST_TYPE_COMPUTE,
&IID_ID3D12CommandAllocator, (void **)&allocators[i + 1]);
ok(hr == S_OK, "Failed to create command allocator, hr %#x.\n", hr);
hr = ID3D12Device_CreateCommandList(device, 0, D3D12_COMMAND_LIST_TYPE_COMPUTE,
allocators[i + 1], NULL, &IID_ID3D12GraphicsCommandList, (void **)&compute_lists[i]);
ok(hr == S_OK, "Failed to create command list, hr %#x.\n", hr);
}
cpu_heap = create_cpu_descriptor_heap(device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 1);
gpu_heap = create_gpu_descriptor_heap(device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 1);
memset(&uav_desc, 0, sizeof(uav_desc));
uav_desc.Format = DXGI_FORMAT_R32_UINT;
uav_desc.ViewDimension = D3D12_UAV_DIMENSION_BUFFER;
uav_desc.Buffer.NumElements = 1024 / sizeof(uint32_t);
ID3D12Device_CreateUnorderedAccessView(device, buffer, NULL, &uav_desc,
get_cpu_descriptor_handle(&context, cpu_heap, 0));
ID3D12Device_CreateUnorderedAccessView(device, buffer, NULL, &uav_desc,
get_cpu_descriptor_handle(&context, gpu_heap, 0));
ID3D12GraphicsCommandList_ClearUnorderedAccessViewUint(compute_lists[0],
get_gpu_descriptor_handle(&context, gpu_heap, 0),
get_cpu_descriptor_handle(&context, cpu_heap, 0),
buffer, clear_value, 0, NULL);
uav_barrier(compute_lists[0], buffer);
value = 0;
for (i = 0; i < ARRAY_SIZE(compute_lists); ++i)
{
command_list = compute_lists[i];
ID3D12GraphicsCommandList_SetComputeRootSignature(command_list, context.root_signature);
ID3D12GraphicsCommandList_SetPipelineState(command_list, compute_pipeline_state);
ID3D12GraphicsCommandList_SetComputeRootUnorderedAccessView(command_list,
0, ID3D12Resource_GetGPUVirtualAddress(buffer));
ID3D12GraphicsCommandList_SetComputeRoot32BitConstants(command_list, 1, 1, &value, 0);
ID3D12GraphicsCommandList_Dispatch(command_list, 1, 1, 1);
hr = ID3D12GraphicsCommandList_Close(command_list);
ok(hr == S_OK, "Failed to close command list, hr %#x.\n", hr);
value += 10;
command_list = graphics_lists[i];
ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
ID3D12GraphicsCommandList_SetGraphicsRootUnorderedAccessView(command_list,
0, ID3D12Resource_GetGPUVirtualAddress(buffer));
ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstants(command_list, 1, 1, &value, 0);
ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);
hr = ID3D12GraphicsCommandList_Close(command_list);
ok(hr == S_OK, "Failed to close command list, hr %#x.\n", hr);
value += 2;
}
exec_command_list(compute_queue, compute_lists[0]);
queue_signal(compute_queue, fence, 1);
queue_wait(queue, fence, 1);
exec_command_list(queue, graphics_lists[0]);
queue_signal(queue, fence2, 1);
queue_wait(compute_queue, fence2, 1);
exec_command_list(compute_queue, compute_lists[1]);
queue_signal(compute_queue, fence, 2);
queue_wait(queue, fence, 2);
exec_command_list(queue, graphics_lists[1]);
hr = wait_for_fence(fence2, 1);
ok(hr == S_OK, "Failed to wait for fence, hr %#x.\n", hr);
reset_command_list(graphics_lists[0], allocators[0]);
command_list = graphics_lists[0];
transition_resource_state(command_list, buffer,
D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);
get_buffer_readback_with_command_list(buffer, DXGI_FORMAT_R32_UINT, &rb, queue, command_list);
value = get_readback_uint(&rb, 0, 0, 0);
ok(value == 24, "Got unexpected value %u.\n", value);
release_resource_readback(&rb);
ID3D12DescriptorHeap_Release(cpu_heap);
ID3D12DescriptorHeap_Release(gpu_heap);
for (i = 0; i < ARRAY_SIZE(graphics_lists); ++i)
ID3D12GraphicsCommandList_Release(graphics_lists[i]);
for (i = 0; i < ARRAY_SIZE(compute_lists); ++i)
ID3D12GraphicsCommandList_Release(compute_lists[i]);
for (i = 0; i < ARRAY_SIZE(allocators); ++i)
ID3D12CommandAllocator_Release(allocators[i]);
ID3D12Fence_Release(fence);
ID3D12Fence_Release(fence2);
ID3D12Resource_Release(buffer);
ID3D12CommandQueue_Release(compute_queue);
ID3D12PipelineState_Release(compute_pipeline_state);
destroy_test_context(&context);
}
void test_fence_values(void)
{
uint64_t value, next_value;
ID3D12CommandQueue *queue;
ID3D12Device *device;
ID3D12Fence *fence;
ULONG refcount;
unsigned int i;
HRESULT hr;
if (!(device = create_device()))
{
skip("Failed to create device.\n");
return;
}
queue = create_command_queue(device, D3D12_COMMAND_LIST_TYPE_DIRECT, D3D12_COMMAND_QUEUE_PRIORITY_NORMAL);
next_value = (uint64_t)1 << 60;
hr = ID3D12Device_CreateFence(device, next_value, D3D12_FENCE_FLAG_NONE, &IID_ID3D12Fence, (void **)&fence);
ok(hr == S_OK, "Failed to create fence, hr %#x.\n", hr);
value = ID3D12Fence_GetCompletedValue(fence);
ok(value == next_value, "Got value %#"PRIx64", expected %#"PRIx64".\n", value, next_value);
for (i = 0; i < 100; ++i)
{
++next_value;
queue_signal(queue, fence, next_value);
/* Sprinke in some tests for no event path. */
wait_queue_idle_no_event(device, queue);
value = ID3D12Fence_GetCompletedValue(fence);
ok(value == next_value, "Got value %#"PRIx64", expected %#"PRIx64".\n", value, next_value);
}
for (i = 0; i < 100; ++i)
{
next_value += 10000;
hr = ID3D12Fence_Signal(fence, next_value);
ok(hr == S_OK, "Failed to signal fence, hr %#x.\n", hr);
value = ID3D12Fence_GetCompletedValue(fence);
ok(value == next_value, "Got value %#"PRIx64", expected %#"PRIx64".\n", value, next_value);
}
ID3D12Fence_Release(fence);
hr = ID3D12Device_CreateFence(device, 0, D3D12_FENCE_FLAG_NONE, &IID_ID3D12Fence, (void **)&fence);
ok(hr == S_OK, "Failed to create fence, hr %#x.\n", hr);
next_value = (uint64_t)1 << 60;
hr = ID3D12Fence_Signal(fence, next_value);
ok(hr == S_OK, "Failed to signal fence, hr %#x.\n", hr);
value = ID3D12Fence_GetCompletedValue(fence);
ok(value == next_value, "Got value %#"PRIx64", expected %#"PRIx64".\n", value, next_value);
next_value = 0;
hr = ID3D12Fence_Signal(fence, next_value);
ok(hr == S_OK, "Failed to signal fence, hr %#x.\n", hr);
value = ID3D12Fence_GetCompletedValue(fence);
ok(value == next_value, "Got value %#"PRIx64", expected %#"PRIx64".\n", value, next_value);
ID3D12Fence_Release(fence);
hr = ID3D12Device_CreateFence(device, 0, D3D12_FENCE_FLAG_NONE, &IID_ID3D12Fence, (void **)&fence);
ok(hr == S_OK, "Failed to create fence, hr %#x.\n", hr);
next_value = (uint64_t)1 << 60;
queue_signal(queue, fence, next_value);
wait_queue_idle_no_event(device, queue);
value = ID3D12Fence_GetCompletedValue(fence);
ok(value == next_value, "Got value %#"PRIx64", expected %#"PRIx64".\n", value, next_value);
next_value = 0;
queue_signal(queue, fence, next_value);
wait_queue_idle(device, queue);
value = ID3D12Fence_GetCompletedValue(fence);
ok(value == next_value, "Got value %#"PRIx64", expected %#"PRIx64".\n", value, next_value);
ID3D12Fence_Release(fence);
ID3D12CommandQueue_Release(queue);
refcount = ID3D12Device_Release(device);
ok(!refcount, "ID3D12Device has %u references left.\n", (unsigned int)refcount);
}
void test_cpu_signal_fence(void)
{
HANDLE event1, event2;
ID3D12Device *device;
unsigned int i, ret;
ID3D12Fence *fence;
ULONG refcount;
uint64_t value;
HRESULT hr;
if (!(device = create_device()))
{
skip("Failed to create device.\n");
return;
}
hr = ID3D12Device_CreateFence(device, 0, D3D12_FENCE_FLAG_NONE,
&IID_ID3D12Fence, (void **)&fence);
ok(SUCCEEDED(hr), "Failed to create fence, hr %#x.\n", hr);
hr = ID3D12Fence_Signal(fence, 1);
ok(SUCCEEDED(hr), "Failed to signal fence, hr %#x.\n", hr);
value = ID3D12Fence_GetCompletedValue(fence);
ok(value == 1, "Got unexpected value %"PRIu64".\n", value);
hr = ID3D12Fence_Signal(fence, 10);
ok(SUCCEEDED(hr), "Failed to signal fence, hr %#x.\n", hr);
value = ID3D12Fence_GetCompletedValue(fence);
ok(value == 10, "Got unexpected value %"PRIu64".\n", value);
hr = ID3D12Fence_Signal(fence, 5);
ok(SUCCEEDED(hr), "Failed to signal fence, hr %#x.\n", hr);
value = ID3D12Fence_GetCompletedValue(fence);
ok(value == 5, "Got unexpected value %"PRIu64".\n", value);
hr = ID3D12Fence_Signal(fence, 0);
ok(SUCCEEDED(hr), "Failed to signal fence, hr %#x.\n", hr);
value = ID3D12Fence_GetCompletedValue(fence);
ok(value == 0, "Got unexpected value %"PRIu64".\n", value);
/* Basic tests with single event. */
event1 = create_event();
ok(event1, "Failed to create event.\n");
ret = wait_event(event1, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
hr = ID3D12Fence_SetEventOnCompletion(fence, 5, event1);
ok(SUCCEEDED(hr), "Failed to set event on completion, hr %#x.\n", hr);
ret = wait_event(event1, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
hr = ID3D12Fence_Signal(fence, 5);
ok(SUCCEEDED(hr), "Failed to signal fence, hr %#x.\n", hr);
ret = wait_event(event1, 0);
ok(ret == WAIT_OBJECT_0, "Got unexpected return value %#x.\n", ret);
ret = wait_event(event1, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
hr = ID3D12Fence_SetEventOnCompletion(fence, 6, event1);
ok(SUCCEEDED(hr), "Failed to set event on completion, hr %#x.\n", hr);
ret = wait_event(event1, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
hr = ID3D12Fence_Signal(fence, 7);
ok(SUCCEEDED(hr), "Failed to signal fence, hr %#x.\n", hr);
ret = wait_event(event1, 0);
ok(ret == WAIT_OBJECT_0, "Got unexpected return value %#x.\n", ret);
ret = wait_event(event1, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
hr = ID3D12Fence_Signal(fence, 10);
ok(SUCCEEDED(hr), "Failed to signal fence, hr %#x.\n", hr);
ret = wait_event(event1, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
/* Event is signaled immediately when value <= GetCompletedValue(). */
ret = wait_event(event1, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
for (i = 0; i <= ID3D12Fence_GetCompletedValue(fence); ++i)
{
hr = ID3D12Fence_SetEventOnCompletion(fence, i, event1);
ok(SUCCEEDED(hr), "Failed to set event on completion, hr %#x.\n", hr);
ret = wait_event(event1, 0);
ok(ret == WAIT_OBJECT_0, "Got unexpected return value %#x for %u.\n", ret, i);
ret = wait_event(event1, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x for %u.\n", ret, i);
}
hr = ID3D12Fence_SetEventOnCompletion(fence, i, event1);
ok(SUCCEEDED(hr), "Failed to set event on completion, hr %#x.\n", hr);
ret = wait_event(event1, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
hr = ID3D12Fence_Signal(fence, i);
ok(SUCCEEDED(hr), "Failed to signal fence, hr %#x.\n", hr);
ret = wait_event(event1, 0);
ok(ret == WAIT_OBJECT_0, "Got unexpected return value %#x.\n", ret);
ret = wait_event(event1, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
/* Attach event to multiple values. */
hr = ID3D12Fence_Signal(fence, 0);
ok(SUCCEEDED(hr), "Failed to signal fence, hr %#x.\n", hr);
ret = wait_event(event1, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
hr = ID3D12Fence_SetEventOnCompletion(fence, 3, event1);
ok(SUCCEEDED(hr), "Failed to set event on completion, hr %#x.\n", hr);
hr = ID3D12Fence_SetEventOnCompletion(fence, 5, event1);
ok(SUCCEEDED(hr), "Failed to set event on completion, hr %#x.\n", hr);
hr = ID3D12Fence_SetEventOnCompletion(fence, 9, event1);
ok(SUCCEEDED(hr), "Failed to set event on completion, hr %#x.\n", hr);
hr = ID3D12Fence_SetEventOnCompletion(fence, 12, event1);
ok(SUCCEEDED(hr), "Failed to set event on completion, hr %#x.\n", hr);
hr = ID3D12Fence_SetEventOnCompletion(fence, 12, event1);
ok(SUCCEEDED(hr), "Failed to set event on completion, hr %#x.\n", hr);
ret = wait_event(event1, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
for (i = 1; i < 13; ++i)
{
hr = ID3D12Fence_Signal(fence, i);
ok(SUCCEEDED(hr), "Failed to signal fence, hr %#x.\n", hr);
if (i == 3 || i == 5 || i == 9 || i == 12)
{
ret = wait_event(event1, 0);
ok(ret == WAIT_OBJECT_0, "Got unexpected return value %#x for %u.\n", ret, i);
}
ret = wait_event(event1, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x for %u.\n", ret, i);
}
/* Tests with 2 events. */
hr = ID3D12Fence_Signal(fence, 0);
ok(SUCCEEDED(hr), "Failed to signal fence, hr %#x.\n", hr);
value = ID3D12Fence_GetCompletedValue(fence);
ok(value == 0, "Got unexpected value %"PRIu64".\n", value);
event2 = create_event();
ok(event2, "Failed to create event.\n");
ret = wait_event(event1, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
ret = wait_event(event2, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
hr = ID3D12Fence_SetEventOnCompletion(fence, 100, event1);
ok(SUCCEEDED(hr), "Failed to set event on completion, hr %#x.\n", hr);
hr = ID3D12Fence_SetEventOnCompletion(fence, ~(uint64_t)0, event2);
ok(SUCCEEDED(hr), "Failed to set event on completion, hr %#x.\n", hr);
hr = ID3D12Fence_Signal(fence, 50);
ok(SUCCEEDED(hr), "Failed to signal fence, hr %#x.\n", hr);
ret = wait_event(event1, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
ret = wait_event(event2, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
hr = ID3D12Fence_Signal(fence, 99);
ok(SUCCEEDED(hr), "Failed to signal fence, hr %#x.\n", hr);
ret = wait_event(event1, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
ret = wait_event(event2, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
hr = ID3D12Fence_Signal(fence, 100);
ok(SUCCEEDED(hr), "Failed to signal fence, hr %#x.\n", hr);
ret = wait_event(event1, 0);
ok(ret == WAIT_OBJECT_0, "Got unexpected return value %#x.\n", ret);
ret = wait_event(event1, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
ret = wait_event(event2, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
hr = ID3D12Fence_Signal(fence, 101);
ok(SUCCEEDED(hr), "Failed to signal fence, hr %#x.\n", hr);
ret = wait_event(event1, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
ret = wait_event(event2, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
hr = ID3D12Fence_Signal(fence, 0);
ok(SUCCEEDED(hr), "Failed to signal fence, hr %#x.\n", hr);
ret = wait_event(event1, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
ret = wait_event(event2, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
hr = ID3D12Fence_Signal(fence, 100);
ok(SUCCEEDED(hr), "Failed to signal fence, hr %#x.\n", hr);
ret = wait_event(event1, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
ret = wait_event(event2, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
hr = ID3D12Fence_Signal(fence, ~(uint64_t)0);
ok(SUCCEEDED(hr), "Failed to signal fence, hr %#x.\n", hr);
ret = wait_event(event1, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
ret = wait_event(event2, 0);
ok(ret == WAIT_OBJECT_0, "Got unexpected return value %#x.\n", ret);
ret = wait_event(event2, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
hr = ID3D12Fence_Signal(fence, ~(uint64_t)0);
ok(SUCCEEDED(hr), "Failed to signal fence, hr %#x.\n", hr);
ret = wait_event(event1, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
ret = wait_event(event2, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
hr = ID3D12Fence_Signal(fence, 0);
ok(SUCCEEDED(hr), "Failed to signal fence, hr %#x.\n", hr);
ret = wait_event(event1, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
ret = wait_event(event2, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
/* Attach two events to the same value. */
hr = ID3D12Fence_Signal(fence, 0);
ok(SUCCEEDED(hr), "Failed to signal fence, hr %#x.\n", hr);
ret = wait_event(event1, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
ret = wait_event(event2, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
hr = ID3D12Fence_SetEventOnCompletion(fence, 1, event1);
ok(SUCCEEDED(hr), "Failed to set event on completion, hr %#x.\n", hr);
hr = ID3D12Fence_SetEventOnCompletion(fence, 1, event2);
ok(SUCCEEDED(hr), "Failed to set event on completion, hr %#x.\n", hr);
ret = wait_event(event1, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
ret = wait_event(event2, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
hr = ID3D12Fence_Signal(fence, 3);
ok(SUCCEEDED(hr), "Failed to signal fence, hr %#x.\n", hr);
ret = wait_event(event1, 0);
ok(ret == WAIT_OBJECT_0, "Got unexpected return value %#x.\n", ret);
ret = wait_event(event2, 0);
ok(ret == WAIT_OBJECT_0, "Got unexpected return value %#x.\n", ret);
ret = wait_event(event1, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
ret = wait_event(event2, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
/* Test passing signaled event. */
hr = ID3D12Fence_Signal(fence, 20);
ok(SUCCEEDED(hr), "Failed to signal fence, hr %#x.\n", hr);
value = ID3D12Fence_GetCompletedValue(fence);
ok(value == 20, "Got unexpected value %"PRIu64".\n", value);
ret = wait_event(event1, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
signal_event(event1);
hr = ID3D12Fence_SetEventOnCompletion(fence, 30, event1);
ok(SUCCEEDED(hr), "Failed to set event on completion, hr %#x.\n", hr);
ret = wait_event(event1, 0);
ok(ret == WAIT_OBJECT_0, "Got unexpected return value %#x.\n", ret);
ret = wait_event(event1, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
hr = ID3D12Fence_Signal(fence, 30);
ok(SUCCEEDED(hr), "Failed to signal fence, hr %#x.\n", hr);
ret = wait_event(event1, 0);
ok(ret == WAIT_OBJECT_0, "Got unexpected return value %#x.\n", ret);
ret = wait_event(event1, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
destroy_event(event1);
destroy_event(event2);
ID3D12Fence_Release(fence);
refcount = ID3D12Device_Release(device);
ok(!refcount, "ID3D12Device has %u references left.\n", (unsigned int)refcount);
}
void test_gpu_signal_fence(void)
{
ID3D12CommandQueue *queue;
HANDLE event1, event2;
ID3D12Device *device;
unsigned int i, ret;
ID3D12Fence *fence;
ULONG refcount;
uint64_t value;
HRESULT hr;
if (!(device = create_device()))
{
skip("Failed to create device.\n");
return;
}
queue = create_command_queue(device, D3D12_COMMAND_LIST_TYPE_DIRECT, D3D12_COMMAND_QUEUE_PRIORITY_NORMAL);
hr = ID3D12Device_CreateFence(device, 0, D3D12_FENCE_FLAG_NONE,
&IID_ID3D12Fence, (void **)&fence);
ok(hr == S_OK, "Failed to create fence, hr %#x.\n", hr);
/* XXX: It seems that when a queue is idle a fence is signalled immediately
* in D3D12. Vulkan implementations don't signal a fence immediately so
* libvkd3d doesn't as well. In order to make this test reliable
* wait_queue_idle() is inserted after every ID3D12CommandQueue_Signal(). */
queue_signal(queue, fence, 10);
wait_queue_idle(device, queue);
value = ID3D12Fence_GetCompletedValue(fence);
ok(value == 10, "Got unexpected value %"PRIu64".\n", value);
queue_signal(queue, fence, 0);
wait_queue_idle(device, queue);
value = ID3D12Fence_GetCompletedValue(fence);
ok(value == 0, "Got unexpected value %"PRIu64".\n", value);
/* Basic tests with single event. */
event1 = create_event();
ok(event1, "Failed to create event.\n");
ret = wait_event(event1, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
hr = ID3D12Fence_SetEventOnCompletion(fence, 5, event1);
ok(hr == S_OK, "Failed to set event on completion, hr %#x.\n", hr);
ret = wait_event(event1, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
queue_signal(queue, fence, 5);
wait_queue_idle(device, queue);
ret = wait_event(event1, 0);
ok(ret == WAIT_OBJECT_0, "Got unexpected return value %#x.\n", ret);
ret = wait_event(event1, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
hr = ID3D12Fence_SetEventOnCompletion(fence, 6, event1);
ok(hr == S_OK, "Failed to set event on completion, hr %#x.\n", hr);
ret = wait_event(event1, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
queue_signal(queue, fence, 7);
wait_queue_idle(device, queue);
ret = wait_event(event1, 0);
ok(ret == WAIT_OBJECT_0, "Got unexpected return value %#x.\n", ret);
ret = wait_event(event1, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
queue_signal(queue, fence, 10);
wait_queue_idle(device, queue);
ret = wait_event(event1, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
/* Attach one event to multiple values. */
queue_signal(queue, fence, 0);
wait_queue_idle(device, queue);
ret = wait_event(event1, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
hr = ID3D12Fence_SetEventOnCompletion(fence, 3, event1);
ok(hr == S_OK, "Failed to set event on completion, hr %#x.\n", hr);
hr = ID3D12Fence_SetEventOnCompletion(fence, 5, event1);
ok(hr == S_OK, "Failed to set event on completion, hr %#x.\n", hr);
hr = ID3D12Fence_SetEventOnCompletion(fence, 9, event1);
ok(hr == S_OK, "Failed to set event on completion, hr %#x.\n", hr);
hr = ID3D12Fence_SetEventOnCompletion(fence, 12, event1);
ok(hr == S_OK, "Failed to set event on completion, hr %#x.\n", hr);
hr = ID3D12Fence_SetEventOnCompletion(fence, 12, event1);
ok(hr == S_OK, "Failed to set event on completion, hr %#x.\n", hr);
ret = wait_event(event1, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
for (i = 1; i < 13; ++i)
{
queue_signal(queue, fence, i);
wait_queue_idle(device, queue);
if (i == 3 || i == 5 || i == 9 || i == 12)
{
ret = wait_event(event1, 0);
ok(ret == WAIT_OBJECT_0, "Got unexpected return value %#x for %u.\n", ret, i);
}
ret = wait_event(event1, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x for %u.\n", ret, i);
}
/* Tests with 2 events. */
queue_signal(queue, fence, 0);
wait_queue_idle(device, queue);
value = ID3D12Fence_GetCompletedValue(fence);
ok(value == 0, "Got unexpected value %"PRIu64".\n", value);
event2 = create_event();
ok(event2, "Failed to create event.\n");
ret = wait_event(event1, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
ret = wait_event(event2, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
hr = ID3D12Fence_SetEventOnCompletion(fence, 100, event1);
ok(hr == S_OK, "Failed to set event on completion, hr %#x.\n", hr);
hr = ID3D12Fence_SetEventOnCompletion(fence, ~(uint64_t)0, event2);
ok(hr == S_OK, "Failed to set event on completion, hr %#x.\n", hr);
queue_signal(queue, fence, 50);
wait_queue_idle(device, queue);
ret = wait_event(event1, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
ret = wait_event(event2, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
queue_signal(queue, fence, 99);
wait_queue_idle(device, queue);
ret = wait_event(event1, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
ret = wait_event(event2, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
queue_signal(queue, fence, 100);
wait_queue_idle(device, queue);
ret = wait_event(event1, 0);
ok(ret == WAIT_OBJECT_0, "Got unexpected return value %#x.\n", ret);
ret = wait_event(event1, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
ret = wait_event(event2, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
queue_signal(queue, fence, 101);
wait_queue_idle(device, queue);
ret = wait_event(event1, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
ret = wait_event(event2, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
queue_signal(queue, fence, 0);
wait_queue_idle(device, queue);
ret = wait_event(event1, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
ret = wait_event(event2, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
queue_signal(queue, fence, 100);
wait_queue_idle(device, queue);
ret = wait_event(event1, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
ret = wait_event(event2, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
queue_signal(queue, fence, ~(uint64_t)0);
wait_queue_idle(device, queue);
ret = wait_event(event1, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
ret = wait_event(event2, 0);
ok(ret == WAIT_OBJECT_0, "Got unexpected return value %#x.\n", ret);
ret = wait_event(event2, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
queue_signal(queue, fence, ~(uint64_t)0);
wait_queue_idle(device, queue);
ret = wait_event(event1, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
ret = wait_event(event2, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
queue_signal(queue, fence, 0);
wait_queue_idle(device, queue);
ret = wait_event(event1, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
ret = wait_event(event2, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
/* Attach two events to the same value. */
queue_signal(queue, fence, 0);
wait_queue_idle(device, queue);
ret = wait_event(event1, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
ret = wait_event(event2, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
hr = ID3D12Fence_SetEventOnCompletion(fence, 1, event1);
ok(hr == S_OK, "Failed to set event on completion, hr %#x.\n", hr);
hr = ID3D12Fence_SetEventOnCompletion(fence, 1, event2);
ok(hr == S_OK, "Failed to set event on completion, hr %#x.\n", hr);
ret = wait_event(event1, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
ret = wait_event(event2, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
queue_signal(queue, fence, 3);
wait_queue_idle(device, queue);
ret = wait_event(event1, 0);
ok(ret == WAIT_OBJECT_0, "Got unexpected return value %#x.\n", ret);
ret = wait_event(event2, 0);
ok(ret == WAIT_OBJECT_0, "Got unexpected return value %#x.\n", ret);
ret = wait_event(event1, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
ret = wait_event(event2, 0);
ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
wait_queue_idle(device, queue);
destroy_event(event1);
destroy_event(event2);
ID3D12Fence_Release(fence);
ID3D12CommandQueue_Release(queue);
refcount = ID3D12Device_Release(device);
ok(!refcount, "ID3D12Device has %u references left.\n", (unsigned int)refcount);
}
struct multithread_fence_wait_data
{
HANDLE event;
ID3D12Fence *fence;
uint64_t value;
};
static void fence_event_wait_main(void *untyped_data)
{
struct multithread_fence_wait_data *data = untyped_data;
unsigned int ret;
HANDLE event;
HRESULT hr;
event = create_event();
ok(event, "Failed to create event.\n");
hr = ID3D12Fence_SetEventOnCompletion(data->fence, data->value, event);
ok(SUCCEEDED(hr), "Failed to set event on completion, hr %#x.\n", hr);
signal_event(data->event);
ret = wait_event(event, INFINITE);
ok(ret == WAIT_OBJECT_0, "Got unexpected return value %#x.\n", ret);
destroy_event(event);
}
static void fence_busy_wait_main(void *untyped_data)
{
struct multithread_fence_wait_data *data = untyped_data;
signal_event(data->event);
while (ID3D12Fence_GetCompletedValue(data->fence) < data->value)
;
}
void test_multithread_fence_wait(void)
{
struct multithread_fence_wait_data thread_data;
ID3D12CommandQueue *queue;
ID3D12Device *device;
unsigned int ret;
ULONG refcount;
HANDLE thread;
HRESULT hr;
if (!(device = create_device()))
{
skip("Failed to create device.\n");
return;
}
queue = create_command_queue(device, D3D12_COMMAND_LIST_TYPE_DIRECT, D3D12_COMMAND_QUEUE_PRIORITY_NORMAL);
thread_data.event = create_event();
thread_data.value = 0;
ok(thread_data.event, "Failed to create event.\n");
hr = ID3D12Device_CreateFence(device, thread_data.value, D3D12_FENCE_FLAG_NONE,
&IID_ID3D12Fence, (void **)&thread_data.fence);
ok(hr == S_OK, "Failed to create fence, hr %#x.\n", hr);
/* Signal fence on host. */
++thread_data.value;
thread = create_thread(fence_event_wait_main, &thread_data);
ok(thread, "Failed to create thread.\n");
ret = wait_event(thread_data.event, INFINITE);
ok(ret == WAIT_OBJECT_0, "Failed to wait for thread start, return value %#x.\n", ret);
hr = ID3D12Fence_Signal(thread_data.fence, thread_data.value);
ok(hr == S_OK, "Failed to signal fence, hr %#x.\n", hr);
ok(join_thread(thread), "Failed to join thread.\n");
++thread_data.value;
thread = create_thread(fence_busy_wait_main, &thread_data);
ok(thread, "Failed to create thread.\n");
ret = wait_event(thread_data.event, INFINITE);
ok(ret == WAIT_OBJECT_0, "Failed to wait for thread start, return value %#x.\n", ret);
hr = ID3D12Fence_Signal(thread_data.fence, thread_data.value);
ok(hr == S_OK, "Failed to signal fence, hr %#x.\n", hr);
ok(join_thread(thread), "Failed to join thread.\n");
/* Signal fence on device. */
++thread_data.value;
thread = create_thread(fence_event_wait_main, &thread_data);
ok(thread, "Failed to create thread.\n");
ret = wait_event(thread_data.event, INFINITE);
ok(ret == WAIT_OBJECT_0, "Failed to wait for thread start, return value %#x.\n", ret);
queue_signal(queue, thread_data.fence, thread_data.value);
ok(join_thread(thread), "Failed to join thread.\n");
++thread_data.value;
thread = create_thread(fence_busy_wait_main, &thread_data);
ok(thread, "Failed to create thread.\n");
ret = wait_event(thread_data.event, INFINITE);
ok(ret == WAIT_OBJECT_0, "Failed to wait for thread start, return value %#x.\n", ret);
queue_signal(queue, thread_data.fence, thread_data.value);
ok(join_thread(thread), "Failed to join thread.\n");
destroy_event(thread_data.event);
ID3D12Fence_Release(thread_data.fence);
ID3D12CommandQueue_Release(queue);
refcount = ID3D12Device_Release(device);
ok(!refcount, "ID3D12Device has %u references left.\n", (unsigned int)refcount);
}
void test_create_fence(void)
{
ID3D12Device *device, *tmp_device;
ID3D12Fence *fence;
ULONG refcount;
uint64_t value;
HRESULT hr;
if (!(device = create_device()))
{
skip("Failed to create device.\n");
return;
}
hr = ID3D12Device_CreateFence(device, 0, D3D12_FENCE_FLAG_NONE,
&IID_ID3D12Fence, (void **)&fence);
ok(SUCCEEDED(hr), "Failed to create fence, hr %#x.\n", hr);
refcount = get_refcount(device);
ok(refcount == 2, "Got unexpected refcount %u.\n", (unsigned int)refcount);
hr = ID3D12Fence_GetDevice(fence, &IID_ID3D12Device, (void **)&tmp_device);
ok(SUCCEEDED(hr), "Failed to get device, hr %#x.\n", hr);
refcount = get_refcount(device);
ok(refcount == 3, "Got unexpected refcount %u.\n", (unsigned int)refcount);
refcount = ID3D12Device_Release(tmp_device);
ok(refcount == 2, "Got unexpected refcount %u.\n", (unsigned int)refcount);
check_interface(fence, &IID_ID3D12Object, true);
check_interface(fence, &IID_ID3D12DeviceChild, true);
check_interface(fence, &IID_ID3D12Pageable, true);
check_interface(fence, &IID_ID3D12Fence, true);
value = ID3D12Fence_GetCompletedValue(fence);
ok(value == 0, "Got unexpected value %"PRIu64".\n", value);
refcount = ID3D12Fence_Release(fence);
ok(!refcount, "ID3D12Fence has %u references left.\n", (unsigned int)refcount);
hr = ID3D12Device_CreateFence(device, 99, D3D12_FENCE_FLAG_NONE,
&IID_ID3D12Fence, (void **)&fence);
ok(SUCCEEDED(hr), "Failed to create fence, hr %#x.\n", hr);
value = ID3D12Fence_GetCompletedValue(fence);
ok(value == 99, "Got unexpected value %"PRIu64".\n", value);
refcount = ID3D12Fence_Release(fence);
ok(!refcount, "ID3D12Fence has %u references left.\n", (unsigned int)refcount);
refcount = ID3D12Device_Release(device);
ok(!refcount, "ID3D12Device has %u references left.\n", (unsigned int)refcount);
}