blob: 8e2f63b1b59885d307441a1ec88661637ad45c8d [file] [edit]
/*
* 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_set_render_targets(void)
{
ID3D12DescriptorHeap *dsv_heap, *rtv_heap;
ID3D12GraphicsCommandList *command_list;
D3D12_CPU_DESCRIPTOR_HANDLE dsv, rtv;
struct test_context context;
ID3D12Device *device;
HRESULT hr;
if (!init_test_context(&context, NULL))
return;
device = context.device;
command_list = context.list;
rtv_heap = create_cpu_descriptor_heap(device, D3D12_DESCRIPTOR_HEAP_TYPE_RTV, 4);
dsv_heap = create_cpu_descriptor_heap(device, D3D12_DESCRIPTOR_HEAP_TYPE_DSV, 4);
dsv = ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(dsv_heap);
rtv = ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(rtv_heap);
ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &rtv, false, NULL);
ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &rtv, true, NULL);
ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &rtv, true, &dsv);
ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 0, &rtv, true, &dsv);
ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 0, &rtv, false, &dsv);
ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 0, NULL, true, &dsv);
ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 0, NULL, false, &dsv);
hr = ID3D12GraphicsCommandList_Close(command_list);
ok(hr == S_OK, "Failed to close command list, hr %#x.\n", hr);
ID3D12DescriptorHeap_Release(rtv_heap);
ID3D12DescriptorHeap_Release(dsv_heap);
destroy_test_context(&context);
}
void test_draw_instanced(void)
{
static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};
ID3D12GraphicsCommandList *command_list;
struct test_context context;
ID3D12CommandQueue *queue;
if (!init_test_context(&context, NULL))
return;
command_list = context.list;
queue = context.queue;
ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);
if (!use_warp_device)
{
/* This draw call is ignored. */
ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);
}
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_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);
check_sub_resource_uint(context.render_target, 0, queue, command_list, 0xff00ff00, 0);
destroy_test_context(&context);
}
void test_draw_indexed_instanced(void)
{
static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};
static const uint16_t indices[] = {0, 1, 2};
ID3D12GraphicsCommandList *command_list;
struct test_context context;
D3D12_INDEX_BUFFER_VIEW ibv;
ID3D12CommandQueue *queue;
ID3D12Resource *ib;
if (!init_test_context(&context, NULL))
return;
command_list = context.list;
queue = context.queue;
ib = create_upload_buffer(context.device, sizeof(indices), indices);
ibv.BufferLocation = ID3D12Resource_GetGPUVirtualAddress(ib);
ibv.SizeInBytes = sizeof(indices);
ibv.Format = DXGI_FORMAT_R16_UINT;
ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);
if (!use_warp_device)
{
/* This draw call is ignored. */
ID3D12GraphicsCommandList_DrawIndexedInstanced(command_list, 3, 1, 0, 0, 0);
}
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_IASetIndexBuffer(command_list, NULL);
ID3D12GraphicsCommandList_IASetIndexBuffer(command_list, &ibv);
ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
ID3D12GraphicsCommandList_DrawIndexedInstanced(command_list, 3, 1, 0, 0, 0);
transition_resource_state(command_list, context.render_target,
D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
check_sub_resource_uint(context.render_target, 0, queue, command_list, 0xff00ff00, 0);
ID3D12Resource_Release(ib);
destroy_test_context(&context);
}
void test_draw_no_descriptor_bindings(void)
{
static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};
D3D12_ROOT_SIGNATURE_DESC root_signature_desc;
D3D12_DESCRIPTOR_RANGE descriptor_range[2];
ID3D12GraphicsCommandList *command_list;
D3D12_ROOT_PARAMETER root_parameters[2];
struct test_context_desc desc;
struct test_context context;
ID3D12CommandQueue *queue;
HRESULT hr;
memset(&desc, 0, sizeof(desc));
desc.no_root_signature = true;
if (!init_test_context(&context, &desc))
return;
command_list = context.list;
queue = context.queue;
descriptor_range[0].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
descriptor_range[0].NumDescriptors = 2;
descriptor_range[0].BaseShaderRegister = 0;
descriptor_range[0].RegisterSpace = 0;
descriptor_range[0].OffsetInDescriptorsFromTableStart = 1;
root_parameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
root_parameters[0].DescriptorTable.NumDescriptorRanges = 1;
root_parameters[0].DescriptorTable.pDescriptorRanges = &descriptor_range[0];
root_parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
descriptor_range[1].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV;
descriptor_range[1].NumDescriptors = 1;
descriptor_range[1].BaseShaderRegister = 0;
descriptor_range[1].RegisterSpace = 0;
descriptor_range[1].OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
root_parameters[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
root_parameters[1].DescriptorTable.NumDescriptorRanges = 1;
root_parameters[1].DescriptorTable.pDescriptorRanges = &descriptor_range[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(context.device, &root_signature_desc, &context.root_signature);
ok(hr == S_OK, "Failed to create root signature, hr %#x.\n", hr);
context.pipeline_state = create_pipeline_state(context.device,
context.root_signature, context.render_target_desc.Format, NULL, NULL, NULL);
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_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);
check_sub_resource_uint(context.render_target, 0, queue, command_list, 0xff00ff00, 0);
destroy_test_context(&context);
}
void test_multiple_render_targets(void)
{
static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};
struct vec4 expected_vec4 = {0.0f, 0.0f, 0.0f, 1.0f};
D3D12_GRAPHICS_PIPELINE_STATE_DESC pso_desc;
ID3D12GraphicsCommandList *command_list;
D3D12_CPU_DESCRIPTOR_HANDLE rtvs[3];
ID3D12Resource *render_targets[2];
struct test_context_desc desc;
struct test_context context;
ID3D12CommandQueue *queue;
unsigned int i;
HRESULT hr;
#include "shaders/command/headers/multiple_render_targets.h"
memset(&desc, 0, sizeof(desc));
desc.rt_format = DXGI_FORMAT_R32G32B32A32_FLOAT;
desc.rt_descriptor_count = ARRAY_SIZE(rtvs);
desc.no_pipeline = true;
if (!init_test_context(&context, &desc))
return;
command_list = context.list;
queue = context.queue;
init_pipeline_state_desc(&pso_desc, context.root_signature, 0, NULL, &multiple_render_targets_dxbc, NULL);
pso_desc.NumRenderTargets = ARRAY_SIZE(rtvs);
for (i = 0; i < ARRAY_SIZE(rtvs); ++i)
pso_desc.RTVFormats[i] = desc.rt_format;
hr = ID3D12Device_CreateGraphicsPipelineState(context.device, &pso_desc,
&IID_ID3D12PipelineState, (void **)&context.pipeline_state);
ok(hr == S_OK, "Failed to create state, hr %#x.\n", hr);
rtvs[0] = get_cpu_rtv_handle(&context, context.rtv_heap, 2);
rtvs[1] = get_cpu_rtv_handle(&context, context.rtv_heap, 0);
rtvs[2] = get_cpu_rtv_handle(&context, context.rtv_heap, 1);
create_render_target(&context, &desc, &render_targets[0], &rtvs[0]);
create_render_target(&context, &desc, &render_targets[1], &rtvs[2]);
for (i = 0; i < ARRAY_SIZE(rtvs); ++i)
ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, rtvs[i], white, 0, NULL);
ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, ARRAY_SIZE(rtvs), rtvs, 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_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);
transition_resource_state(command_list, render_targets[0],
D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
transition_resource_state(command_list, render_targets[1],
D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
expected_vec4.x = 2.0f;
check_sub_resource_vec4(context.render_target, 0, queue, command_list, &expected_vec4, 0);
reset_command_list(command_list, context.allocator);
expected_vec4.x = 1.0f;
check_sub_resource_vec4(render_targets[0], 0, queue, command_list, &expected_vec4, 0);
reset_command_list(command_list, context.allocator);
expected_vec4.x = 3.0f;
check_sub_resource_vec4(render_targets[1], 0, queue, command_list, &expected_vec4, 0);
reset_command_list(command_list, context.allocator);
transition_resource_state(command_list, context.render_target,
D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);
transition_resource_state(command_list, render_targets[0],
D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);
transition_resource_state(command_list, render_targets[1],
D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);
ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, ARRAY_SIZE(rtvs), &context.rtv, true, 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_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);
transition_resource_state(command_list, render_targets[0],
D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
transition_resource_state(command_list, render_targets[1],
D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
expected_vec4.x = 1.0f;
check_sub_resource_vec4(context.render_target, 0, queue, command_list, &expected_vec4, 0);
reset_command_list(command_list, context.allocator);
expected_vec4.x = 3.0f;
check_sub_resource_vec4(render_targets[0], 0, queue, command_list, &expected_vec4, 0);
reset_command_list(command_list, context.allocator);
expected_vec4.x = 2.0f;
check_sub_resource_vec4(render_targets[1], 0, queue, command_list, &expected_vec4, 0);
reset_command_list(command_list, context.allocator);
for (i = 0; i < ARRAY_SIZE(render_targets); ++i)
ID3D12Resource_Release(render_targets[i]);
destroy_test_context(&context);
}
void test_fractional_viewports(void)
{
static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};
ID3D12GraphicsCommandList *command_list;
D3D12_INPUT_LAYOUT_DESC input_layout;
struct test_context_desc desc;
D3D12_VERTEX_BUFFER_VIEW vbv;
struct test_context context;
struct resource_readback rb;
ID3D12CommandQueue *queue;
D3D12_VIEWPORT viewport;
unsigned int i, x, y;
ID3D12Resource *vb;
#include "shaders/command/headers/fractional_viewports_vs.h"
#include "shaders/command/headers/fractional_viewports_ps.h"
static const D3D12_INPUT_ELEMENT_DESC layout_desc[] =
{
{"POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
{"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 8, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
};
static const struct
{
struct vec2 position;
struct vec2 texcoord;
}
quad[] =
{
{{-1.0f, -1.0f}, {0.0f, 0.0f}},
{{-1.0f, 1.0f}, {0.0f, 1.0f}},
{{ 1.0f, -1.0f}, {1.0f, 0.0f}},
{{ 1.0f, 1.0f}, {1.0f, 1.0f}},
};
static const float viewport_offsets[] =
{
0.0f, 1.0f / 2.0f, 1.0f / 4.0f, 1.0f / 8.0f, 1.0f / 16.0f, 1.0f / 32.0f,
1.0f / 64.0f, 1.0f / 128.0f, 1.0f / 256.0f, 63.0f / 128.0f,
};
memset(&desc, 0, sizeof(desc));
desc.rt_format = DXGI_FORMAT_R32G32B32A32_FLOAT;
desc.no_root_signature = true;
if (!init_test_context(&context, &desc))
return;
command_list = context.list;
queue = context.queue;
context.root_signature = create_empty_root_signature(context.device,
D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);
input_layout.pInputElementDescs = layout_desc;
input_layout.NumElements = ARRAY_SIZE(layout_desc);
context.pipeline_state = create_pipeline_state(context.device,
context.root_signature, desc.rt_format,
&fractional_viewports_vs_dxbc, &fractional_viewports_ps_dxbc, &input_layout);
vb = create_upload_buffer(context.device, sizeof(quad), quad);
vbv.BufferLocation = ID3D12Resource_GetGPUVirtualAddress(vb);
vbv.StrideInBytes = sizeof(*quad);
vbv.SizeInBytes = sizeof(quad);
for (i = 0; i < ARRAY_SIZE(viewport_offsets); ++i)
{
set_viewport(&viewport, viewport_offsets[i], viewport_offsets[i],
context.render_target_desc.Width, context.render_target_desc.Height, 0.0f, 1.0f);
if (i)
transition_resource_state(command_list, context.render_target,
D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);
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_TRIANGLESTRIP);
ID3D12GraphicsCommandList_IASetVertexBuffers(command_list, 0, 1, &vbv);
ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &viewport);
ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
ID3D12GraphicsCommandList_DrawInstanced(command_list, 4, 1, 0, 0);
transition_resource_state(command_list, context.render_target,
D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
get_texture_readback_with_command_list(context.render_target, 0, &rb, queue, command_list);
for (y = 0; y < rb.height; ++y)
{
for (x = 0; x < rb.width; ++x)
{
const struct vec4 *v = get_readback_vec4(&rb, x, y);
struct vec4 expected = {x + 0.5f, y + 0.5f,
(x + 0.5f - viewport_offsets[i]) / context.render_target_desc.Width,
1.0f - (y + 0.5f - viewport_offsets[i]) / context.render_target_desc.Height};
ok(compare_float(v->x, expected.x, 0) && compare_float(v->y, expected.y, 0),
"Got fragcoord {%.8e, %.8e}, expected {%.8e, %.8e} at (%u, %u), offset %.8e.\n",
v->x, v->y, expected.x, expected.y, x, y, viewport_offsets[i]);
ok(compare_float(v->z, expected.z, 2) && compare_float(v->w, expected.w, 2),
"Got texcoord {%.8e, %.8e}, expected {%.8e, %.8e} at (%u, %u), offset %.8e.\n",
v->z, v->w, expected.z, expected.w, x, y, viewport_offsets[i]);
}
}
release_resource_readback(&rb);
reset_command_list(command_list, context.allocator);
}
ID3D12Resource_Release(vb);
destroy_test_context(&context);
}
void test_negative_viewports(void)
{
static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};
D3D12_FEATURE_DATA_D3D12_OPTIONS13 options13;
ID3D12GraphicsCommandList *command_list;
D3D12_INPUT_LAYOUT_DESC input_layout;
struct test_context_desc desc;
D3D12_VERTEX_BUFFER_VIEW vbv;
struct test_context context;
struct resource_readback rb;
ID3D12CommandQueue *queue;
D3D12_VIEWPORT viewport;
ID3D12Device *device;
ID3D12Resource *vb;
unsigned int x, y;
HRESULT hr;
#include "shaders/command/headers/negative_viewports_vs.h"
#include "shaders/command/headers/negative_viewports_ps.h"
static const D3D12_INPUT_ELEMENT_DESC layout_desc[] =
{
{"POSITION", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
{"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 16, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
};
static const struct
{
struct vec4 position;
struct vec2 texcoord;
}
quad[] =
{
{{-1.0f, 1.0f, 0.0f, 1.0f}, {0.0f, 1.0f}},
{{-1.0f, -1.0f, 1.0f, 1.0f}, {0.0f, 0.0f}},
{{ 1.0f, 1.0f, 0.0f, 1.0f}, {1.0f, 1.0f}},
{{ 1.0f, -1.0f, 1.0f, 1.0f}, {1.0f, 0.0f}},
};
memset(&desc, 0, sizeof(desc));
desc.rt_format = DXGI_FORMAT_R32G32B32A32_FLOAT;
desc.no_root_signature = true;
if (!init_test_context(&context, &desc))
return;
command_list = context.list;
queue = context.queue;
device = context.device;
memset(&options13, 0, sizeof(options13));
hr = ID3D12Device_CheckFeatureSupport(device, D3D12_FEATURE_D3D12_OPTIONS13, &options13, sizeof(options13));
ok(SUCCEEDED(hr), "OPTIONS13 is not supported by runtime.\n");
if (!options13.InvertedViewportHeightFlipsYSupported)
{
skip("Device does not support negative viewport height.\n");
destroy_test_context(&context);
return;
}
context.root_signature = create_empty_root_signature(context.device,
D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);
input_layout.pInputElementDescs = layout_desc;
input_layout.NumElements = ARRAY_SIZE(layout_desc);
context.pipeline_state = create_pipeline_state(context.device,
context.root_signature, desc.rt_format,
&negative_viewports_vs_dxbc, &negative_viewports_ps_dxbc, &input_layout);
vb = create_upload_buffer(context.device, sizeof(quad), quad);
vbv.BufferLocation = ID3D12Resource_GetGPUVirtualAddress(vb);
vbv.StrideInBytes = sizeof(quad[0]);
vbv.SizeInBytes = sizeof(quad);
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_TRIANGLESTRIP);
ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
for (y = 0; y < context.render_target_desc.Height; y += context.render_target_desc.Height / 2)
{
for (x = 0; x < context.render_target_desc.Width; x += context.render_target_desc.Width / 2)
{
set_viewport(&viewport, x, context.render_target_desc.Height - y,
context.render_target_desc.Width / 2, -(float)(context.render_target_desc.Height / 2), 1.0f, 0.0f);
ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &viewport);
ID3D12GraphicsCommandList_IASetVertexBuffers(command_list, 0, 1, &vbv);
ID3D12GraphicsCommandList_DrawInstanced(command_list, 4, 1, 0, 0);
}
}
transition_resource_state(command_list, context.render_target,
D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
get_texture_readback_with_command_list(context.render_target, 0, &rb, queue, command_list);
for (y = 0; y < rb.height; ++y)
{
for (x = 0; x < rb.width; ++x)
{
const struct vec4 *actual = get_readback_vec4(&rb, x, y);
struct vec2 expected_fragcoord = {x + 0.5f, y + 0.5f};
float expected_texcoord_y = (((y % (rb.height / 2)) * 2.0f + 1.0f) / rb.height);
float expected_depth = expected_texcoord_y;
ok(compare_float(actual->x, expected_fragcoord.x, 0) && compare_float(actual->y, expected_fragcoord.y, 0),
"Got fragcoord {%.8e, %.8e}, expected {%.8e, %.8e} at (%u, %u).\n",
actual->x, actual->y, expected_fragcoord.x, expected_fragcoord.y, x, y);
ok(compare_float(actual->z, expected_depth, 2),
"Got depth {%.2f}, expected {%.2f} at (%u, %u).\n",
actual->z, expected_depth, x, y);
ok(compare_float(actual->w, expected_texcoord_y, 2),
"Got texcoord {%.2f}, expected {%.2f} at (%u, %u).\n",
actual->w, expected_texcoord_y, x, y);
}
}
release_resource_readback(&rb);
reset_command_list(command_list, context.allocator);
ID3D12Resource_Release(vb);
destroy_test_context(&context);
}
void test_scissor(void)
{
ID3D12GraphicsCommandList *command_list;
struct test_context_desc desc;
struct test_context context;
struct resource_readback rb;
ID3D12CommandQueue *queue;
unsigned int color;
RECT scissor_rect;
#include "shaders/command/headers/scissor.h"
static const float red[] = {1.0f, 0.0f, 0.0f, 1.0f};
memset(&desc, 0, sizeof(desc));
desc.rt_width = 640;
desc.rt_height = 480;
desc.ps = &scissor_dxbc;
if (!init_test_context(&context, &desc))
return;
command_list = context.list;
queue = context.queue;
set_rect(&scissor_rect, 160, 120, 480, 360);
ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, red, 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, &scissor_rect);
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);
get_texture_readback_with_command_list(context.render_target, 0, &rb, queue, command_list);
color = get_readback_uint(&rb, 320, 60, 0);
ok(compare_color(color, 0xff0000ff, 1), "Got unexpected color 0x%08x.\n", color);
color = get_readback_uint(&rb, 80, 240, 0);
ok(compare_color(color, 0xff0000ff, 1), "Got unexpected color 0x%08x.\n", color);
color = get_readback_uint(&rb, 320, 240, 0);
ok(compare_color(color, 0xff00ff00, 1), "Got unexpected color 0x%08x.\n", color);
color = get_readback_uint(&rb, 560, 240, 0);
ok(compare_color(color, 0xff0000ff, 1), "Got unexpected color 0x%08x.\n", color);
color = get_readback_uint(&rb, 320, 420, 0);
ok(compare_color(color, 0xff0000ff, 1), "Got unexpected color 0x%08x.\n", color);
release_resource_readback(&rb);
destroy_test_context(&context);
}
void test_draw_depth_no_ps(void)
{
D3D12_GRAPHICS_PIPELINE_STATE_DESC pso_desc;
ID3D12GraphicsCommandList *command_list;
D3D12_INPUT_LAYOUT_DESC input_layout;
struct depth_stencil_resource ds;
struct test_context_desc desc;
D3D12_VERTEX_BUFFER_VIEW vbv;
struct test_context context;
ID3D12CommandQueue *queue;
ID3D12Resource *vb;
HRESULT hr;
static const D3D12_INPUT_ELEMENT_DESC layout_desc[] =
{
{"POSITION", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
};
static const struct
{
struct vec4 position;
}
quad[] =
{
{{-1.0f, -1.0f, 0.5f, 1.0f}},
{{-1.0f, 1.0f, 0.5f, 1.0f}},
{{ 1.0f, -1.0f, 0.5f, 1.0f}},
{{ 1.0f, 1.0f, 0.5f, 1.0f}},
};
#include "shaders/command/headers/draw_depth_no_ps.h"
memset(&desc, 0, sizeof(desc));
desc.no_render_target = true;
if (!init_test_context(&context, &desc))
return;
command_list = context.list;
queue = context.queue;
vb = create_upload_buffer(context.device, sizeof(quad), quad);
vbv.BufferLocation = ID3D12Resource_GetGPUVirtualAddress(vb);
vbv.StrideInBytes = sizeof(*quad);
vbv.SizeInBytes = sizeof(quad);
init_depth_stencil(&ds, context.device, 640, 480, 1, 1, DXGI_FORMAT_D32_FLOAT, 0, NULL);
set_viewport(&context.viewport, 0.0f, 0.0f, 640.0f, 480.0f, 0.0f, 1.0f);
set_rect(&context.scissor_rect, 0, 0, 640, 480);
context.root_signature = create_empty_root_signature(context.device,
D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);
input_layout.pInputElementDescs = layout_desc;
input_layout.NumElements = ARRAY_SIZE(layout_desc);
init_pipeline_state_desc(&pso_desc, context.root_signature, 0, &draw_depth_no_ps_dxbc, NULL, &input_layout);
memset(&pso_desc.PS, 0, sizeof(pso_desc.PS));
pso_desc.NumRenderTargets = 0;
pso_desc.DSVFormat = DXGI_FORMAT_D32_FLOAT;
pso_desc.DepthStencilState.DepthEnable = true;
pso_desc.DepthStencilState.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL;
pso_desc.DepthStencilState.DepthFunc = D3D12_COMPARISON_FUNC_ALWAYS;
hr = ID3D12Device_CreateGraphicsPipelineState(context.device, &pso_desc,
&IID_ID3D12PipelineState, (void **)&context.pipeline_state);
ok(hr == S_OK, "Failed to create graphics pipeline state, hr %#x.\n", hr);
ID3D12GraphicsCommandList_ClearDepthStencilView(command_list, ds.dsv_handle,
D3D12_CLEAR_FLAG_DEPTH, 1.0f, 0, 0, NULL);
ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 0, NULL, false, &ds.dsv_handle);
ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
ID3D12GraphicsCommandList_IASetVertexBuffers(command_list, 0, 1, &vbv);
ID3D12GraphicsCommandList_DrawInstanced(command_list, 4, 1, 0, 0);
transition_resource_state(command_list, ds.texture,
D3D12_RESOURCE_STATE_DEPTH_WRITE, D3D12_RESOURCE_STATE_COPY_SOURCE);
check_sub_resource_float(ds.texture, 0, queue, command_list, 0.5f, 1);
destroy_depth_stencil(&ds);
ID3D12Resource_Release(vb);
destroy_test_context(&context);
}
void test_draw_depth_only(void)
{
D3D12_GRAPHICS_PIPELINE_STATE_DESC pso_desc;
ID3D12GraphicsCommandList *command_list;
struct depth_stencil_resource ds;
struct test_context_desc desc;
struct resource_readback rb;
struct test_context context;
ID3D12CommandQueue *queue;
unsigned int i, j;
HRESULT hr;
#include "shaders/command/headers/draw_depth_only.h"
static const struct
{
float clear_depth;
float depth;
float expected_depth;
}
tests[] =
{
{0.0f, 0.0f, 0.0f},
{0.0f, 0.7f, 0.0f},
{0.0f, 0.8f, 0.0f},
{0.0f, 0.5f, 0.0f},
{1.0f, 0.0f, 0.0f},
{1.0f, 0.7f, 0.7f},
{1.0f, 0.8f, 0.8f},
{1.0f, 0.5f, 0.5f},
};
memset(&desc, 0, sizeof(desc));
desc.no_render_target = true;
if (!init_test_context(&context, &desc))
return;
command_list = context.list;
queue = context.queue;
init_depth_stencil(&ds, context.device, 640, 480, 1, 1, DXGI_FORMAT_D32_FLOAT, 0, NULL);
set_viewport(&context.viewport, 0.0f, 0.0f, 640.0f, 480.0f, 0.0f, 1.0f);
set_rect(&context.scissor_rect, 0, 0, 640, 480);
context.root_signature = create_32bit_constants_root_signature(context.device,
0, 1, D3D12_SHADER_VISIBILITY_PIXEL);
init_pipeline_state_desc(&pso_desc, context.root_signature, 0, NULL, &draw_depth_only_dxbc, NULL);
pso_desc.NumRenderTargets = 0;
pso_desc.DSVFormat = DXGI_FORMAT_D32_FLOAT;
pso_desc.DepthStencilState.DepthEnable = true;
pso_desc.DepthStencilState.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL;
pso_desc.DepthStencilState.DepthFunc = D3D12_COMPARISON_FUNC_LESS;
hr = ID3D12Device_CreateGraphicsPipelineState(context.device, &pso_desc,
&IID_ID3D12PipelineState, (void **)&context.pipeline_state);
ok(SUCCEEDED(hr), "Failed to create graphics pipeline state, hr %#x.\n", hr);
for (i = 0; i < ARRAY_SIZE(tests); ++i)
{
vkd3d_test_set_context("Test %u", i);
ID3D12GraphicsCommandList_ClearDepthStencilView(command_list, ds.dsv_handle,
D3D12_CLEAR_FLAG_DEPTH, tests[i].clear_depth, 0, 0, NULL);
ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 0, NULL, false, &ds.dsv_handle);
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_SetGraphicsRoot32BitConstants(command_list, 0, 1, &tests[i].depth, 0);
ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);
transition_resource_state(command_list, ds.texture,
D3D12_RESOURCE_STATE_DEPTH_WRITE, D3D12_RESOURCE_STATE_COPY_SOURCE);
check_sub_resource_float(ds.texture, 0, queue, command_list, tests[i].expected_depth, 1);
reset_command_list(command_list, context.allocator);
transition_resource_state(command_list, ds.texture,
D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_DEPTH_WRITE);
}
vkd3d_test_set_context(NULL);
ID3D12GraphicsCommandList_ClearDepthStencilView(command_list, ds.dsv_handle,
D3D12_CLEAR_FLAG_DEPTH, 1.0f, 0, 0, NULL);
ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 0, NULL, false, &ds.dsv_handle);
ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
for (i = 0; i < 4; ++i)
{
for (j = 0; j < 4; ++j)
{
float depth = 1.0f / 16.0f * (j + 4 * i);
ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstants(command_list, 0, 1, &depth, 0);
set_viewport(&context.viewport, 160.0f * j, 120.0f * i, 160.0f, 120.0f, 0.0f, 1.0f);
ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);
}
}
transition_resource_state(command_list, ds.texture,
D3D12_RESOURCE_STATE_DEPTH_WRITE, D3D12_RESOURCE_STATE_COPY_SOURCE);
get_texture_readback_with_command_list(ds.texture, 0, &rb, queue, command_list);
for (i = 0; i < 4; ++i)
{
for (j = 0; j < 4; ++j)
{
float obtained_depth, expected_depth;
obtained_depth = get_readback_float(&rb, 80 + j * 160, 60 + i * 120);
expected_depth = 1.0f / 16.0f * (j + 4 * i);
ok(compare_float(obtained_depth, expected_depth, 1),
"Got unexpected depth %.8e at (%u, %u), expected %.8e.\n",
obtained_depth, j, i, expected_depth);
}
}
release_resource_readback(&rb);
destroy_depth_stencil(&ds);
destroy_test_context(&context);
}
void test_draw_uav_only(void)
{
ID3D12DescriptorHeap *cpu_descriptor_heap, *descriptor_heap;
D3D12_ROOT_SIGNATURE_DESC root_signature_desc;
D3D12_DESCRIPTOR_RANGE descriptor_range;
ID3D12GraphicsCommandList *command_list;
D3D12_CPU_DESCRIPTOR_HANDLE cpu_handle;
D3D12_GPU_DESCRIPTOR_HANDLE gpu_handle;
D3D12_ROOT_PARAMETER root_parameter;
D3D12_RESOURCE_BARRIER barrier;
struct test_context_desc desc;
struct test_context context;
ID3D12CommandQueue *queue;
ID3D12Resource *resource;
unsigned int i;
HRESULT hr;
#include "shaders/command/headers/draw_uav_only.h"
static const UINT zero[4] = {0};
memset(&desc, 0, sizeof(desc));
desc.no_render_target = true;
if (!init_test_context(&context, &desc))
return;
command_list = context.list;
queue = context.queue;
descriptor_range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV;
descriptor_range.NumDescriptors = 1;
descriptor_range.BaseShaderRegister = 0;
descriptor_range.RegisterSpace = 0;
descriptor_range.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
root_parameter.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
root_parameter.DescriptorTable.NumDescriptorRanges = 1;
root_parameter.DescriptorTable.pDescriptorRanges = &descriptor_range;
root_parameter.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
root_signature_desc.NumParameters = 1;
root_signature_desc.pParameters = &root_parameter;
root_signature_desc.NumStaticSamplers = 0;
root_signature_desc.pStaticSamplers = NULL;
root_signature_desc.Flags = D3D12_ROOT_SIGNATURE_FLAG_NONE;
hr = create_root_signature(context.device, &root_signature_desc, &context.root_signature);
ok(SUCCEEDED(hr), "Failed to create root signature, hr %#x.\n", hr);
context.pipeline_state = create_pipeline_state(context.device, context.root_signature, 0, NULL, &draw_uav_only_dxbc, NULL);
resource = create_default_texture(context.device, 1, 1, DXGI_FORMAT_R32_SINT,
D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
descriptor_heap = create_gpu_descriptor_heap(context.device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 1);
cpu_descriptor_heap = create_cpu_descriptor_heap(context.device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 1);
cpu_handle = ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(descriptor_heap);
gpu_handle = ID3D12DescriptorHeap_GetGPUDescriptorHandleForHeapStart(descriptor_heap);
ID3D12Device_CreateUnorderedAccessView(context.device, resource, NULL, NULL, cpu_handle);
cpu_handle = ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(cpu_descriptor_heap);
ID3D12Device_CreateUnorderedAccessView(context.device, resource, NULL, NULL, cpu_handle);
ID3D12GraphicsCommandList_ClearUnorderedAccessViewUint(command_list,
gpu_handle, cpu_handle, resource, zero, 0, NULL);
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_UAV;
barrier.UAV.pResource = resource;
barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
ID3D12GraphicsCommandList_ResourceBarrier(command_list, 1, &barrier);
set_rect(&context.scissor_rect, 0, 0, 1000, 1000);
set_viewport(&context.viewport, 0.0f, 0.0f, 1.0f, 100.0f, 0.0f, 0.0f);
ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
ID3D12GraphicsCommandList_SetDescriptorHeaps(command_list, 1, &descriptor_heap);
ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(command_list, 0, gpu_handle);
ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
for (i = 0; i < 5; ++i)
ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);
transition_resource_state(command_list, resource,
D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);
check_sub_resource_uint(resource, 0, queue, command_list, 500, 0);
ID3D12DescriptorHeap_Release(cpu_descriptor_heap);
ID3D12DescriptorHeap_Release(descriptor_heap);
ID3D12Resource_Release(resource);
destroy_test_context(&context);
}
void test_texture_resource_barriers(void)
{
ID3D12CommandAllocator *command_allocator;
ID3D12GraphicsCommandList *command_list;
D3D12_RESOURCE_BARRIER barriers[8];
ID3D12CommandQueue *queue;
ID3D12Resource *resource;
ID3D12Device *device;
ULONG refcount;
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_CreateCommandAllocator(device, D3D12_COMMAND_LIST_TYPE_DIRECT,
&IID_ID3D12CommandAllocator, (void **)&command_allocator);
ok(SUCCEEDED(hr), "Failed to create command allocator, hr %#x.\n", hr);
hr = ID3D12Device_CreateCommandList(device, 0, D3D12_COMMAND_LIST_TYPE_DIRECT,
command_allocator, NULL, &IID_ID3D12GraphicsCommandList, (void **)&command_list);
ok(SUCCEEDED(hr), "Failed to create command list, hr %#x.\n", hr);
resource = create_default_texture(device, 32, 32, DXGI_FORMAT_R8G8B8A8_UNORM,
D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COMMON);
barriers[0].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
barriers[0].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
barriers[0].Transition.pResource = resource;
barriers[0].Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
barriers[0].Transition.StateBefore = D3D12_RESOURCE_STATE_COMMON;
barriers[0].Transition.StateAfter = D3D12_RESOURCE_STATE_UNORDERED_ACCESS;
ID3D12GraphicsCommandList_ResourceBarrier(command_list, 1, &barriers[0]);
barriers[1].Type = D3D12_RESOURCE_BARRIER_TYPE_UAV;
barriers[1].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
barriers[1].UAV.pResource = resource;
ID3D12GraphicsCommandList_ResourceBarrier(command_list, 1, &barriers[1]);
barriers[2].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
barriers[2].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
barriers[2].Transition.pResource = resource;
barriers[2].Transition.Subresource = 0;
barriers[2].Transition.StateBefore = D3D12_RESOURCE_STATE_UNORDERED_ACCESS;
barriers[2].Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_SOURCE;
ID3D12GraphicsCommandList_ResourceBarrier(command_list, 1, &barriers[2]);
barriers[3].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
barriers[3].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
barriers[3].Transition.pResource = resource;
barriers[3].Transition.Subresource = 0;
barriers[3].Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_SOURCE;
barriers[3].Transition.StateAfter = D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE
| D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
ID3D12GraphicsCommandList_ResourceBarrier(command_list, 1, &barriers[3]);
barriers[4].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
barriers[4].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
barriers[4].Transition.pResource = resource;
barriers[4].Transition.Subresource = 0;
barriers[4].Transition.StateBefore = D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE
| D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
barriers[4].Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_SOURCE;
ID3D12GraphicsCommandList_ResourceBarrier(command_list, 1, &barriers[4]);
barriers[5].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
barriers[5].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
barriers[5].Transition.pResource = resource;
barriers[5].Transition.Subresource = 0;
barriers[5].Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_SOURCE;
barriers[5].Transition.StateAfter = D3D12_RESOURCE_STATE_UNORDERED_ACCESS;
ID3D12GraphicsCommandList_ResourceBarrier(command_list, 1, &barriers[5]);
barriers[6].Type = D3D12_RESOURCE_BARRIER_TYPE_UAV;
barriers[6].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
barriers[6].UAV.pResource = resource;
ID3D12GraphicsCommandList_ResourceBarrier(command_list, 1, &barriers[6]);
ID3D12GraphicsCommandList_ResourceBarrier(command_list, 1, &barriers[6]);
barriers[7].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
barriers[7].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
barriers[7].Transition.pResource = resource;
barriers[7].Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
barriers[7].Transition.StateBefore = D3D12_RESOURCE_STATE_UNORDERED_ACCESS;
barriers[7].Transition.StateAfter = D3D12_RESOURCE_STATE_COMMON;
ID3D12GraphicsCommandList_ResourceBarrier(command_list, 1, &barriers[7]);
ID3D12GraphicsCommandList_ResourceBarrier(command_list, 8, barriers);
hr = ID3D12GraphicsCommandList_Close(command_list);
ok(SUCCEEDED(hr), "Failed to close command list, hr %#x.\n", hr);
exec_command_list(queue, command_list);
wait_queue_idle(device, queue);
ID3D12GraphicsCommandList_Release(command_list);
ID3D12CommandAllocator_Release(command_allocator);
ID3D12Resource_Release(resource);
ID3D12CommandQueue_Release(queue);
refcount = ID3D12Device_Release(device);
ok(!refcount, "ID3D12Device has %u references left.\n", (unsigned int)refcount);
}
void test_bundle_state_inheritance(void)
{
static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};
ID3D12GraphicsCommandList *command_list, *bundle;
ID3D12CommandAllocator *bundle_allocator;
struct test_context context;
struct resource_readback rb;
ID3D12CommandQueue *queue;
ID3D12Device *device;
unsigned int x, y;
HRESULT hr;
if (use_warp_device)
{
skip("Bundle state inheritance test crashes on WARP.\n");
return;
}
if (!init_test_context(&context, NULL))
return;
device = context.device;
command_list = context.list;
queue = context.queue;
hr = ID3D12Device_CreateCommandAllocator(device, D3D12_COMMAND_LIST_TYPE_BUNDLE,
&IID_ID3D12CommandAllocator, (void **)&bundle_allocator);
ok(SUCCEEDED(hr), "Failed to create command allocator, hr %#x.\n", hr);
hr = ID3D12Device_CreateCommandList(device, 0, D3D12_COMMAND_LIST_TYPE_BUNDLE,
bundle_allocator, NULL, &IID_ID3D12GraphicsCommandList, (void **)&bundle);
ok(SUCCEEDED(hr), "Failed to create command list, hr %#x.\n", hr);
/* A bundle does not inherit the current pipeline state. */
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_DrawInstanced(bundle, 3, 1, 0, 0);
hr = ID3D12GraphicsCommandList_Close(bundle);
ok(SUCCEEDED(hr), "Failed to close bundle, hr %#x.\n", hr);
ID3D12GraphicsCommandList_ExecuteBundle(command_list, bundle);
transition_resource_state(command_list, context.render_target,
D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
get_texture_readback_with_command_list(context.render_target, 0, &rb, queue, command_list);
for (y = 0; y < rb.height; ++y)
{
for (x = 0; x < rb.width; ++x)
{
unsigned int v = get_readback_uint(&rb, x, y, 0);
/* This works on AMD. */
ok(v == 0xffffffff || v == 0xff00ff00, "Got unexpected value 0x%08x at (%u, %u).\n", v, x, y);
}
}
release_resource_readback(&rb);
reset_command_list(command_list, context.allocator);
reset_command_list(bundle, bundle_allocator);
/* A bundle does not inherit the current primitive topology. */
transition_resource_state(command_list, context.render_target,
D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);
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_SetPipelineState(bundle, context.pipeline_state);
ID3D12GraphicsCommandList_DrawInstanced(bundle, 3, 1, 0, 0);
hr = ID3D12GraphicsCommandList_Close(bundle);
ok(SUCCEEDED(hr), "Failed to close bundle, hr %#x.\n", hr);
ID3D12GraphicsCommandList_ExecuteBundle(command_list, bundle);
transition_resource_state(command_list, context.render_target,
D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
get_texture_readback_with_command_list(context.render_target, 0, &rb, queue, command_list);
for (y = 0; y < rb.height; ++y)
{
for (x = 0; x < rb.width; ++x)
{
unsigned int v = get_readback_uint(&rb, x, y, 0);
/* This works on AMD, even though the debug layer says that the primitive topology is undefined. */
ok(v == 0xffffffff || v == 0xff00ff00, "Got unexpected value 0x%08x at (%u, %u).\n", v, x, y);
}
}
release_resource_readback(&rb);
reset_command_list(command_list, context.allocator);
reset_command_list(bundle, bundle_allocator);
/* A bundle inherit all other states. */
transition_resource_state(command_list, context.render_target,
D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);
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_RSSetViewports(command_list, 1, &context.viewport);
ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
ID3D12GraphicsCommandList_SetPipelineState(bundle, context.pipeline_state);
ID3D12GraphicsCommandList_IASetPrimitiveTopology(bundle, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
ID3D12GraphicsCommandList_DrawInstanced(bundle, 3, 1, 0, 0);
hr = ID3D12GraphicsCommandList_Close(bundle);
ok(SUCCEEDED(hr), "Failed to close bundle, hr %#x.\n", hr);
ID3D12GraphicsCommandList_ExecuteBundle(command_list, bundle);
transition_resource_state(command_list, context.render_target,
D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
check_sub_resource_uint(context.render_target, 0, queue, command_list, 0xff00ff00, 0);
reset_command_list(command_list, context.allocator);
reset_command_list(bundle, bundle_allocator);
/* All state that is set in a bundle affects a command list. */
transition_resource_state(command_list, context.render_target,
D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);
ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);
ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
ID3D12GraphicsCommandList_SetGraphicsRootSignature(bundle, context.root_signature);
ID3D12GraphicsCommandList_SetPipelineState(bundle, context.pipeline_state);
ID3D12GraphicsCommandList_IASetPrimitiveTopology(bundle, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
hr = ID3D12GraphicsCommandList_Close(bundle);
ok(SUCCEEDED(hr), "Failed to close bundle, hr %#x.\n", hr);
ID3D12GraphicsCommandList_ExecuteBundle(command_list, bundle);
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);
check_sub_resource_uint(context.render_target, 0, queue, command_list, 0xff00ff00, 0);
ID3D12GraphicsCommandList_Release(bundle);
ID3D12CommandAllocator_Release(bundle_allocator);
destroy_test_context(&context);
}
void test_null_vbv(void)
{
ID3D12GraphicsCommandList *command_list;
D3D12_INPUT_LAYOUT_DESC input_layout;
D3D12_VERTEX_BUFFER_VIEW vbv[2];
struct test_context_desc desc;
struct test_context context;
ID3D12CommandQueue *queue;
ID3D12Resource *vb;
static const D3D12_INPUT_ELEMENT_DESC layout_desc[] =
{
{"SV_POSITION", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D12_APPEND_ALIGNED_ELEMENT,
D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
{"COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 1, D3D12_APPEND_ALIGNED_ELEMENT,
D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
};
#include "shaders/command/headers/null_vbv_vs.h"
#include "shaders/command/headers/null_vbv_ps.h"
static const struct vec4 positions[] =
{
{-1.0f, -1.0f, 0.0f, 1.0f},
{-1.0f, 1.0f, 0.0f, 1.0f},
{ 1.0f, -1.0f, 0.0f, 1.0f},
{ 1.0f, 1.0f, 0.0f, 1.0f},
};
static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};
memset(&desc, 0, sizeof(desc));
desc.no_root_signature = true;
if (!init_test_context(&context, &desc))
return;
command_list = context.list;
queue = context.queue;
context.root_signature = create_empty_root_signature(context.device,
D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);
input_layout.pInputElementDescs = layout_desc;
input_layout.NumElements = ARRAY_SIZE(layout_desc);
context.pipeline_state = create_pipeline_state(context.device,
context.root_signature, context.render_target_desc.Format,
&null_vbv_vs_dxbc, &null_vbv_ps_dxbc, &input_layout);
vb = create_upload_buffer(context.device, sizeof(positions), positions);
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_TRIANGLESTRIP);
ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
vbv[0].BufferLocation = ID3D12Resource_GetGPUVirtualAddress(vb);
vbv[0].StrideInBytes = sizeof(*positions);
vbv[0].SizeInBytes = sizeof(positions);
vbv[1] = vbv[0];
ID3D12GraphicsCommandList_IASetVertexBuffers(command_list, 0, ARRAY_SIZE(vbv), vbv);
vbv[1].BufferLocation = 0;
vbv[1].StrideInBytes = 0;
vbv[1].SizeInBytes = 0;
ID3D12GraphicsCommandList_IASetVertexBuffers(command_list, 0, ARRAY_SIZE(vbv), vbv);
/* Call should be ignored. */
ID3D12GraphicsCommandList_IASetVertexBuffers(command_list, 0, 1, NULL);
ID3D12GraphicsCommandList_IASetVertexBuffers(command_list, 1, 1, NULL);
ID3D12GraphicsCommandList_DrawInstanced(command_list, 4, 4, 0, 0);
transition_resource_state(command_list, context.render_target,
D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
check_sub_resource_uint(context.render_target, 0, queue, command_list, 0x00000000, 0);
ID3D12Resource_Release(vb);
destroy_test_context(&context);
}
void test_vbv_stride_edge_cases(void)
{
D3D12_GRAPHICS_PIPELINE_STATE_DESC pso_desc;
D3D12_STREAM_OUTPUT_BUFFER_VIEW so_view;
D3D12_INPUT_LAYOUT_DESC input_layout;
D3D12_SO_DECLARATION_ENTRY so_entry;
struct test_context_desc desc;
D3D12_VERTEX_BUFFER_VIEW vbv;
struct resource_readback rb;
struct test_context context;
ID3D12Resource *vb, *xfb;
ID3D12PipelineState *pso;
unsigned int i;
static const D3D12_INPUT_ELEMENT_DESC layout_desc[] =
{
{"POSITION", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 16, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
};
#include "shaders/command/headers/vbv_stride_edge_cases.h"
const UINT so_stride = 16;
float vb_data[1024];
/* Various edge case behavior when stride < offset.
* This is actually broken on native AMD drivers where bounds checking
* happens based on vertex index being less than VBV size / stride. */
struct test_case
{
UINT stride;
UINT size;
float reference[8];
};
/* Negative value marks case which should be 0.0f due to robustness.
* The positive value denotes the value we should read if robustness does not work as expected. */
static const struct test_case tests[] = {
/* Stride 0 should always work as expected on AMD. */
{ 0, 4, { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f } },
{ 0, 8, { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f } },
{ 0, 12, { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f } },
{ 0, 16, { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f } }, /* Fully OOB */
{ 0, 32, { 4.0f, 5.0f, 6.0f, 7.0f, 4.0f, 5.0f, 6.0f, 7.0f } }, /* Fine */
{ 4, 16, { -4.0f, -5.0f, -6.0f, -7.0f, -5.0f, -6.0f, -7.0f, -8.0f } }, /* Fully OOB, but native D3D12 AMD driver thinks there are valid elements here. */
{ 4, 36, { 4.0f, 5.0f, 6.0f, 7.0f, 5.0f, 6.0f, 7.0f, 8.0f } }, /* Fine. There should be room for 2 vertices here. */
{ 8, 16, { -4.0f, -5.0f, -6.0f, -7.0f, -6.0f, -7.0f, -8.0f, -9.0f } }, /* Fully OOB, but native D3D12 AMD driver thinks there are valid elements here. */
{ 8, 40, { 4.0f, 5.0f, 6.0f, 7.0f, 6.0f, 7.0f, 8.0f, 9.0f } }, /* Fine. There should be room for 2. */
{ 12, 16, { -4.0f, -5.0f, -6.0f, -7.0f, 0.0f, 0.0f, 0.0f, 0.0f } }, /* Fully OOB, but native D3D12 AMD driver thinks there is one valid element. */
{ 12, 44, { 4.0f, 5.0f, 6.0f, 7.0f, 7.0f, 8.0f, 9.0f, 10.0f } }, /* Fine. There should be room for 2. */
};
memset(&desc, 0, sizeof(desc));
desc.no_root_signature = true;
if (!init_test_context(&context, &desc))
return;
context.root_signature = create_empty_root_signature(context.device,
D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |
D3D12_ROOT_SIGNATURE_FLAG_ALLOW_STREAM_OUTPUT);
input_layout.pInputElementDescs = layout_desc;
input_layout.NumElements = ARRAY_SIZE(layout_desc);
init_pipeline_state_desc(&pso_desc, context.root_signature, DXGI_FORMAT_UNKNOWN, &vbv_stride_edge_cases_dxbc, NULL, &input_layout);
pso_desc.PS.BytecodeLength = 0;
pso_desc.PS.pShaderBytecode = NULL;
pso_desc.StreamOutput.NumEntries = 1;
pso_desc.StreamOutput.RasterizedStream = 0;
pso_desc.StreamOutput.pBufferStrides = &so_stride;
pso_desc.StreamOutput.NumStrides = 1;
pso_desc.StreamOutput.pSODeclaration = &so_entry;
pso_desc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_POINT;
so_entry.ComponentCount = 4;
so_entry.OutputSlot = 0;
so_entry.SemanticIndex = 0;
so_entry.SemanticName = "SV_Position";
so_entry.StartComponent = 0;
so_entry.Stream = 0;
xfb = create_default_buffer(context.device, 4096, D3D12_RESOURCE_FLAG_NONE, D3D12_RESOURCE_STATE_STREAM_OUT);
for (i = 0; i < ARRAY_SIZE(vb_data); i++)
vb_data[i] = (float)i;
vb = create_upload_buffer(context.device, sizeof(vb_data), vb_data);
ID3D12Device_CreateGraphicsPipelineState(context.device, &pso_desc, &IID_ID3D12PipelineState, (void **)&pso);
ID3D12GraphicsCommandList_SetGraphicsRootSignature(context.list, context.root_signature);
ID3D12GraphicsCommandList_SetPipelineState(context.list, pso);
so_view.BufferFilledSizeLocation = ID3D12Resource_GetGPUVirtualAddress(xfb);
so_view.BufferLocation = ID3D12Resource_GetGPUVirtualAddress(xfb) + 16;
so_view.SizeInBytes = 4096 - 16;
ID3D12GraphicsCommandList_SOSetTargets(context.list, 0, 1, &so_view);
ID3D12GraphicsCommandList_IASetPrimitiveTopology(context.list, D3D_PRIMITIVE_TOPOLOGY_POINTLIST);
vbv.BufferLocation = ID3D12Resource_GetGPUVirtualAddress(vb);
for (i = 0; i < ARRAY_SIZE(tests); i++)
{
const D3D12_VIEWPORT vp = { 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f };
const D3D12_RECT rect = { 0, 0, 1, 1 };
vbv.SizeInBytes = tests[i].size;
vbv.StrideInBytes = tests[i].stride;
ID3D12GraphicsCommandList_RSSetViewports(context.list, 1, &vp);
ID3D12GraphicsCommandList_RSSetScissorRects(context.list, 1, &rect);
ID3D12GraphicsCommandList_IASetVertexBuffers(context.list, 0, 1, &vbv);
if (tests[i].stride && tests[i].stride < 16)
vkd3d_mute_validation_message("06209", "Intentionally testing murky D3D12 behavior");
ID3D12GraphicsCommandList_DrawInstanced(context.list, 2, 1, 0, 0);
if (tests[i].stride && tests[i].stride < 16)
vkd3d_unmute_validation_message("06209");
}
transition_resource_state(context.list, xfb, D3D12_RESOURCE_STATE_STREAM_OUT, D3D12_RESOURCE_STATE_COPY_SOURCE);
get_buffer_readback_with_command_list(xfb, DXGI_FORMAT_R32G32B32A32_FLOAT, &rb, context.queue, context.list);
for (i = 0; i < ARRAY_SIZE(tests); i++)
{
const struct vec4 *v0, *v1;
v0 = get_readback_vec4(&rb, 1 + 2 * i, 0);
v1 = get_readback_vec4(&rb, 2 + 2 * i, 0);
#define check(dat, ref_index) do { \
float ref = tests[i].reference[ref_index]; \
bool robust_is_zero = ref < 0.0f; \
ref = fabsf(ref); \
if (robust_is_zero && dat == ref) \
skip("Test %u, index %u expected 0 output, but robustness failed. Got expected output as if robustness did not happen.\n", i, ref_index); \
else \
ok(dat == ref || (robust_is_zero && dat == 0.0f), "Test %u, index %u, %f != %f\n", i, ref_index, dat, ref); \
} while(0)
check(v0->x, 0); check(v0->y, 1); check(v0->z, 2); check(v0->w, 3);
check(v1->x, 4); check(v1->y, 5); check(v1->z, 6); check(v1->w, 7);
#undef check
}
release_resource_readback(&rb);
ID3D12PipelineState_Release(pso);
ID3D12Resource_Release(xfb);
ID3D12Resource_Release(vb);
destroy_test_context(&context);
}
void test_execute_indirect_multi_dispatch_root_descriptors(void)
{
const unsigned int max_indirect_count = 5;
D3D12_INDIRECT_ARGUMENT_DESC arguments[2];
D3D12_ROOT_PARAMETER root_params[1];
ID3D12CommandSignature *signature;
D3D12_COMMAND_SIGNATURE_DESC desc;
D3D12_ROOT_SIGNATURE_DESC rs_desc;
struct resource_readback rb;
struct test_context context;
ID3D12Resource *indirect;
unsigned int test_index;
ID3D12Resource *output;
unsigned int iteration;
unsigned int i;
HRESULT hr;
struct test_data
{
uint32_t dispatch_counts[4];
struct
{
D3D12_GPU_VIRTUAL_ADDRESS va;
uint32_t wg_x, wg_y, wg_z;
} dispatches[16];
} indirect_data = {
{ 2, 7, 4, 13 },
{
{ 0, 2, 3, 4 },
{ 0, 5, 6, 7 },
{ 0, 8, 9, 10 },
{ 0, 11, 3, 12 },
{ 0, 6, 13, 14 },
{ 0, 13, 14, 15 },
{ 0, 16, 17, 18 },
{ 0, 19, 18, 7 },
{ 0, 2, 3, 4 },
{ 0, 5, 6, 7 },
{ 0, 8, 9, 10 },
{ 0, 11, 3, 12 },
{ 0, 6, 13, 14 },
{ 0, 13, 14, 15 },
{ 0, 16, 17, 18 },
{ 0, 19, 18, 7 },
},
};
uint32_t expected_counts[ARRAY_SIZE(indirect_data.dispatches)] = { 0 };
#include "shaders/command/headers/execute_indirect_multi_dispatch_root_descriptors.h"
if (!init_compute_test_context(&context))
return;
desc.ByteStride = sizeof(indirect_data.dispatches[0]);
desc.NodeMask = 0;
desc.NumArgumentDescs = ARRAY_SIZE(arguments);
desc.pArgumentDescs = arguments;
memset(&rs_desc, 0, sizeof(rs_desc));
memset(root_params, 0, sizeof(root_params));
rs_desc.NumParameters = ARRAY_SIZE(root_params);
rs_desc.pParameters = root_params;
root_params[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
root_params[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_UAV;
create_root_signature(context.device, &rs_desc, &context.root_signature);
context.pipeline_state = create_compute_pipeline_state(context.device, context.root_signature,
execute_indirect_multi_dispatch_root_descriptors_dxbc);
memset(arguments, 0, sizeof(arguments));
arguments[0].Type = D3D12_INDIRECT_ARGUMENT_TYPE_UNORDERED_ACCESS_VIEW;
arguments[0].UnorderedAccessView.RootParameterIndex = 0;
arguments[1].Type = D3D12_INDIRECT_ARGUMENT_TYPE_DISPATCH;
hr = ID3D12Device_CreateCommandSignature(context.device, &desc, context.root_signature, &IID_ID3D12CommandSignature, (void **)&signature);
if (FAILED(hr))
signature = NULL;
todo ok(SUCCEEDED(hr), "Failed to create command signature, hr #%x.\n", hr);
output = create_default_buffer(context.device, ARRAY_SIZE(indirect_data.dispatches) * 4, D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
for (i = 0; i < ARRAY_SIZE(indirect_data.dispatches); i++)
indirect_data.dispatches[i].va = ID3D12Resource_GetGPUVirtualAddress(output) + 4 * i;
indirect = create_upload_buffer(context.device, sizeof(indirect_data), &indirect_data);
ID3D12GraphicsCommandList_SetComputeRootSignature(context.list, context.root_signature);
ID3D12GraphicsCommandList_SetPipelineState(context.list, context.pipeline_state);
for (test_index = 0; test_index < 8 && signature; test_index++)
{
iteration = test_index & 3;
if (test_index < 4)
{
/* Multi-dispatch, direct count. */
ID3D12GraphicsCommandList_ExecuteIndirect(context.list, signature, indirect_data.dispatch_counts[iteration],
indirect, offsetof(struct test_data, dispatches[test_index]), NULL, 0);
}
else
{
/* Indirect count style. */
ID3D12GraphicsCommandList_ExecuteIndirect(context.list, signature, max_indirect_count,
indirect, offsetof(struct test_data, dispatches[test_index]),
indirect, offsetof(struct test_data, dispatch_counts[iteration]));
}
if (test_index == 6)
{
/* Test case with late indirect update. Just make sure we can exercise both
* code paths. */
transition_resource_state(context.list, indirect, D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT,
D3D12_RESOURCE_STATE_COPY_SOURCE);
transition_resource_state(context.list, indirect, D3D12_RESOURCE_STATE_COPY_SOURCE,
D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT);
}
}
transition_resource_state(context.list, output, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);
get_buffer_readback_with_command_list(output, DXGI_FORMAT_UNKNOWN, &rb, context.queue, context.list);
for (test_index = 0; test_index < 8; test_index++)
{
unsigned int num_dispatches;
iteration = test_index & 3;
num_dispatches = test_index < 4 ?
indirect_data.dispatch_counts[iteration] :
min(indirect_data.dispatch_counts[iteration], max_indirect_count);
for (i = test_index; i < num_dispatches + test_index; i++)
{
expected_counts[i] +=
indirect_data.dispatches[i].wg_x *
indirect_data.dispatches[i].wg_y *
indirect_data.dispatches[i].wg_z;
}
}
for (i = 0; i < ARRAY_SIZE(indirect_data.dispatches); i++)
{
uint32_t value;
value = get_readback_uint(&rb, i, 0, 0);
todo_if(!signature) ok(value == expected_counts[i], "Value %u: Expected %u, got %u.\n", i, expected_counts[i], value);
}
release_resource_readback(&rb);
ID3D12Resource_Release(indirect);
ID3D12Resource_Release(output);
if (signature)
ID3D12CommandSignature_Release(signature);
destroy_test_context(&context);
}
void test_execute_indirect_multi_dispatch_root_constants(void)
{
const unsigned int max_indirect_count = 5;
D3D12_INDIRECT_ARGUMENT_DESC arguments[2];
D3D12_ROOT_PARAMETER root_params[2];
ID3D12CommandSignature *signature;
D3D12_COMMAND_SIGNATURE_DESC desc;
D3D12_ROOT_SIGNATURE_DESC rs_desc;
struct resource_readback rb;
struct test_context context;
ID3D12Resource *indirect;
unsigned int test_index;
ID3D12Resource *output;
unsigned int iteration;
HRESULT hr;
static const uint32_t indirect_data[] = {
1, 8, 3, 6, /* indirect multi dispatch counts */
13, 10, 20, 30, /* root constant u32 + dispatch group counts */
14, 11, 21, 31,
15, 0, 22, 32,
16, 13, 23, 0,
17, 10, 20, 30,
18, 11, 21, 31,
19, 12, 22, 32,
20, 13, 23, 33,
21, 10, 20, 30,
22, 11, 21, 31,
23, 12, 22, 32,
24, 13, 23, 33,
25, 10, 20, 30,
26, 11, 21, 31,
27, 12, 22, 32,
28, 13, 23, 33,
};
#include "shaders/command/headers/execute_indirect_multi_dispatch_root_constants.h"
if (!init_compute_test_context(&context))
return;
desc.ByteStride = sizeof(uint32_t) + sizeof(D3D12_DISPATCH_ARGUMENTS);
desc.NodeMask = 0;
desc.NumArgumentDescs = ARRAY_SIZE(arguments);
desc.pArgumentDescs = arguments;
memset(&rs_desc, 0, sizeof(rs_desc));
memset(root_params, 0, sizeof(root_params));
rs_desc.NumParameters = ARRAY_SIZE(root_params);
rs_desc.pParameters = root_params;
root_params[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
root_params[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_UAV;
root_params[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
root_params[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS;
root_params[1].Constants.Num32BitValues = 4;
create_root_signature(context.device, &rs_desc, &context.root_signature);
context.pipeline_state = create_compute_pipeline_state(context.device, context.root_signature,
execute_indirect_multi_dispatch_root_constants_dxbc);
memset(arguments, 0, sizeof(arguments));
arguments[0].Type = D3D12_INDIRECT_ARGUMENT_TYPE_CONSTANT;
arguments[0].Constant.RootParameterIndex = 1;
arguments[0].Constant.Num32BitValuesToSet = 1;
arguments[0].Constant.DestOffsetIn32BitValues = 2;
arguments[1].Type = D3D12_INDIRECT_ARGUMENT_TYPE_DISPATCH;
hr = ID3D12Device_CreateCommandSignature(context.device, &desc, context.root_signature, &IID_ID3D12CommandSignature, (void **)&signature);
if (FAILED(hr))
signature = NULL;
todo ok(SUCCEEDED(hr), "Failed to create command signature, hr #%x.\n", hr);
output = create_default_buffer(context.device, 8 * 4, D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
indirect = create_upload_buffer(context.device, sizeof(indirect_data), indirect_data);
ID3D12GraphicsCommandList_SetComputeRootSignature(context.list, context.root_signature);
ID3D12GraphicsCommandList_SetPipelineState(context.list, context.pipeline_state);
for (test_index = 0; test_index < 8 && signature; test_index++)
{
iteration = test_index & 3;
ID3D12GraphicsCommandList_SetComputeRootUnorderedAccessView(context.list, 0,
ID3D12Resource_GetGPUVirtualAddress(output) + 4 * test_index);
ID3D12GraphicsCommandList_SetComputeRoot32BitConstant(context.list, 1, 1, 0);
ID3D12GraphicsCommandList_SetComputeRoot32BitConstant(context.list, 1, 2, 1);
ID3D12GraphicsCommandList_SetComputeRoot32BitConstant(context.list, 1, 3, 2);
ID3D12GraphicsCommandList_SetComputeRoot32BitConstant(context.list, 1, 4, 3);
if (test_index < 4)
{
/* Multi-dispatch, direct count. */
ID3D12GraphicsCommandList_ExecuteIndirect(context.list, signature, indirect_data[iteration], indirect, 16 + 16 * test_index, NULL, 0);
}
else
{
/* Indirect count style. */
ID3D12GraphicsCommandList_ExecuteIndirect(context.list, signature, max_indirect_count,
indirect, 16 + 16 * test_index,
indirect, 4 * iteration);
}
/* Also test behavior for cleared root constant state. cbv.z should be cleared to 0 here. */
ID3D12GraphicsCommandList_Dispatch(context.list, 1, 1, 1);
if (test_index == 6)
{
/* Test case with late indirect update. Just make sure we can exercise both
* code paths. */
transition_resource_state(context.list, indirect, D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT,
D3D12_RESOURCE_STATE_COPY_SOURCE);
transition_resource_state(context.list, indirect, D3D12_RESOURCE_STATE_COPY_SOURCE,
D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT);
}
}
transition_resource_state(context.list, output, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);
get_buffer_readback_with_command_list(output, DXGI_FORMAT_UNKNOWN, &rb, context.queue, context.list);
for (test_index = 0; test_index < 8; test_index++)
{
unsigned int num_dispatches, expected, expected_cbv, value, i;
iteration = test_index & 3;
num_dispatches = test_index < 4 ? indirect_data[iteration] : min(indirect_data[iteration], max_indirect_count);
expected = 0;
for (i = test_index; i < num_dispatches + test_index; i++)
{
expected_cbv = 1 | 2 | 4; /* x, y and w elements are untouched. */
expected_cbv |= indirect_data[4 + 4 * i + 0];
expected += expected_cbv *
indirect_data[4 + 4 * i + 1] *
indirect_data[4 + 4 * i + 2] *
indirect_data[4 + 4 * i + 3];
}
expected += 7; /* For the single 1, 1, 1 dispatch. */
value = get_readback_uint(&rb, test_index, 0, 0);
todo_if(!signature) ok(value == expected, "Iteration %u: Expected %u, got %u.\n", test_index, expected, value);
}
release_resource_readback(&rb);
ID3D12Resource_Release(indirect);
ID3D12Resource_Release(output);
if (signature)
ID3D12CommandSignature_Release(signature);
destroy_test_context(&context);
}
void test_execute_indirect_multi_dispatch(void)
{
const unsigned int max_indirect_count = 5;
D3D12_INDIRECT_ARGUMENT_DESC arguments[1];
ID3D12CommandSignature *signature;
D3D12_COMMAND_SIGNATURE_DESC desc;
D3D12_ROOT_SIGNATURE_DESC rs_desc;
D3D12_ROOT_PARAMETER root_param;
struct resource_readback rb;
struct test_context context;
ID3D12Resource *indirect;
unsigned int test_index;
ID3D12Resource *output;
unsigned int iteration;
HRESULT hr;
/* Simple multi dispatch indirect without state changes. */
static const uint32_t indirect_data[] = {
1, 8, 3, 6, /* indirect multi dispatch counts */
10, 20, 30, /* dispatch group counts */
11, 21, 31,
0, 22, 32,
13, 23, 0,
10, 20, 30,
11, 21, 31,
12, 22, 32,
13, 23, 33,
10, 20, 30,
11, 21, 31,
12, 22, 32,
13, 23, 33,
10, 20, 30,
11, 21, 31,
12, 22, 32,
13, 23, 33,
};
#include "shaders/command/headers/execute_indirect_multi_dispatch.h"
if (!init_compute_test_context(&context))
return;
memset(&rs_desc, 0, sizeof(rs_desc));
memset(&root_param, 0, sizeof(root_param));
rs_desc.NumParameters = 1;
rs_desc.pParameters = &root_param;
root_param.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
root_param.ParameterType = D3D12_ROOT_PARAMETER_TYPE_UAV;
create_root_signature(context.device, &rs_desc, &context.root_signature);
context.pipeline_state = create_compute_pipeline_state(context.device, context.root_signature,
execute_indirect_multi_dispatch_dxbc);
desc.ByteStride = sizeof(D3D12_DISPATCH_ARGUMENTS);
desc.NodeMask = 0;
desc.NumArgumentDescs = ARRAY_SIZE(arguments);
desc.pArgumentDescs = arguments;
memset(arguments, 0, sizeof(arguments));
arguments[0].Type = D3D12_INDIRECT_ARGUMENT_TYPE_DISPATCH;
hr = ID3D12Device_CreateCommandSignature(context.device, &desc, NULL, &IID_ID3D12CommandSignature, (void **)&signature);
ok(SUCCEEDED(hr), "Failed to create command signature, hr #%x.\n", hr);
output = create_default_buffer(context.device, 8 * 4, D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
indirect = create_upload_buffer(context.device, sizeof(indirect_data), indirect_data);
ID3D12GraphicsCommandList_SetComputeRootSignature(context.list, context.root_signature);
ID3D12GraphicsCommandList_SetPipelineState(context.list, context.pipeline_state);
for (test_index = 0; test_index < 8; test_index++)
{
iteration = test_index & 3;
ID3D12GraphicsCommandList_SetComputeRootUnorderedAccessView(context.list, 0,
ID3D12Resource_GetGPUVirtualAddress(output) + 4 * test_index);
if (test_index < 4)
{
/* Multi-dispatch, direct count. */
ID3D12GraphicsCommandList_ExecuteIndirect(context.list, signature, indirect_data[iteration], indirect, 16 + 12 * test_index, NULL, 0);
}
else
{
/* Indirect count style. */
ID3D12GraphicsCommandList_ExecuteIndirect(context.list, signature, max_indirect_count,
indirect, 16 + 12 * test_index,
indirect, 4 * iteration);
}
if (test_index == 6)
{
/* Test case with late indirect update. Just make sure we can exercise both
* code paths. */
transition_resource_state(context.list, indirect, D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT,
D3D12_RESOURCE_STATE_COPY_SOURCE);
transition_resource_state(context.list, indirect, D3D12_RESOURCE_STATE_COPY_SOURCE,
D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT);
}
}
transition_resource_state(context.list, output, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);
get_buffer_readback_with_command_list(output, DXGI_FORMAT_UNKNOWN, &rb, context.queue, context.list);
for (test_index = 0; test_index < 8; test_index++)
{
unsigned int num_dispatches, expected, value, i;
iteration = test_index & 3;
num_dispatches = test_index < 4 ? indirect_data[iteration] : min(indirect_data[iteration], max_indirect_count);
expected = 0;
for (i = test_index; i < num_dispatches + test_index; i++)
{
expected += indirect_data[4 + 3 * i + 0] *
indirect_data[4 + 3 * i + 1] *
indirect_data[4 + 3 * i + 2];
}
value = get_readback_uint(&rb, test_index, 0, 0);
ok(value == expected, "Iteration %u: Expected %u, got %u.\n", test_index, expected, value);
}
release_resource_readback(&rb);
ID3D12Resource_Release(indirect);
ID3D12Resource_Release(output);
ID3D12CommandSignature_Release(signature);
destroy_test_context(&context);
}
static void report_predication_timestamps(ID3D12CommandQueue *queue, ID3D12Resource *resource,
unsigned int num_timestamps, const char *tag)
{
uint64_t min_delta = UINT64_MAX;
uint64_t max_delta = 0;
const uint64_t *ts;
unsigned int i;
uint64_t delta;
UINT64 ts_freq;
ID3D12Resource_Map(resource, 0, NULL, (void **)&ts);
ID3D12CommandQueue_GetTimestampFrequency(queue, &ts_freq);
for (i = 1; i < num_timestamps; i++)
{
delta = ts[i] - ts[i - 1];
max_delta = max(max_delta, delta);
min_delta = min(min_delta, delta);
}
printf("Maximum %s time: %.3f us.\n", tag,
1e6 * (double)max_delta / (double)ts_freq);
printf("Minimum %s time: %.3f us.\n", tag,
1e6 * (double)min_delta / (double)ts_freq);
delta = ts[num_timestamps - 1] - ts[0];
printf("Average %s time: %.3f us.\n", tag,
1e6 * (double)delta / (double)(ts_freq * (num_timestamps - 1)));
ID3D12Resource_Unmap(resource, 0, NULL);
}
void test_execute_indirect_state_predication(void)
{
D3D12_INDIRECT_ARGUMENT_DESC argument_descs[2];
D3D12_GRAPHICS_PIPELINE_STATE_DESC pso_desc;
D3D12_COMMAND_SIGNATURE_DESC sig_desc;
ID3D12CommandSignature *sig_compute;
D3D12_ROOT_SIGNATURE_DESC rs_desc;
D3D12_ROOT_PARAMETER rs_params[3];
ID3D12Resource *indirect_graphics;
D3D12_QUERY_HEAP_DESC query_desc;
ID3D12Resource *indirect_compute;
ID3D12Resource *indirect_counts;
ID3D12Resource *query_readback;
ID3D12Resource *indirect_copy;
struct test_context_desc desc;
ID3D12PipelineState *pso_comp;
ID3D12PipelineState *pso_gfx;
unsigned int timestamp_index;
ID3D12CommandSignature *sig;
struct test_context context;
struct resource_readback rb;
ID3D12QueryHeap *query_heap;
ID3D12Resource *output;
D3D12_VIEWPORT vp;
unsigned int i, j;
D3D12_RECT sci;
HRESULT hr;
/* Tests predication feature alongside ExecuteIndirect templates,
* but also considers our internal implementation details w.r.t.
* adding extra predication for performance. */
struct draw_arguments
{
uint32_t value;
D3D12_DRAW_ARGUMENTS draw;
};
struct dispatch_arguments
{
uint32_t value;
D3D12_DISPATCH_ARGUMENTS dispatch;
};
#include "shaders/command/headers/execute_indirect_state_predication_vs.h"
#include "shaders/command/headers/execute_indirect_state_predication_ps.h"
#include "shaders/command/headers/execute_indirect_state_predication_cs.h"
memset(&desc, 0, sizeof(desc));
desc.no_pipeline = true;
desc.no_render_target = true;
desc.no_root_signature = true;
if (!init_test_context(&context, &desc))
return;
memset(&rs_desc, 0, sizeof(rs_desc));
memset(rs_params, 0, sizeof(rs_params));
rs_desc.NumParameters = ARRAY_SIZE(rs_params);
rs_desc.pParameters = rs_params;
rs_params[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
rs_params[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS;
rs_params[0].Constants.Num32BitValues = 1;
rs_params[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
rs_params[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
rs_params[1].Descriptor.ShaderRegister = 1;
rs_params[2].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
rs_params[2].ParameterType = D3D12_ROOT_PARAMETER_TYPE_UAV;
create_root_signature(context.device, &rs_desc, &context.root_signature);
memset(argument_descs, 0, sizeof(argument_descs));
argument_descs[0].Type = D3D12_INDIRECT_ARGUMENT_TYPE_CONSTANT;
argument_descs[0].Constant.RootParameterIndex = 0;
argument_descs[0].Constant.Num32BitValuesToSet = 1;
argument_descs[1].Type = D3D12_INDIRECT_ARGUMENT_TYPE_DRAW;
memset(&sig_desc, 0, sizeof(sig_desc));
sig_desc.pArgumentDescs = argument_descs;
sig_desc.NumArgumentDescs = ARRAY_SIZE(argument_descs);
sig_desc.ByteStride = sizeof(struct draw_arguments);
if (FAILED(hr = ID3D12Device_CreateCommandSignature(context.device, &sig_desc, context.root_signature,
&IID_ID3D12CommandSignature, (void **)&sig)))
{
skip("Implementation does not support DGC, skipping test.\n");
destroy_test_context(&context);
return;
}
argument_descs[1].Type = D3D12_INDIRECT_ARGUMENT_TYPE_DISPATCH;
sig_desc.ByteStride = sizeof(struct dispatch_arguments);
if (FAILED(hr = ID3D12Device_CreateCommandSignature(context.device, &sig_desc, context.root_signature,
&IID_ID3D12CommandSignature, (void **)&sig_compute)))
{
skip("Implementation does not support DGCC, skipping test.\n");
ID3D12CommandSignature_Release(sig);
destroy_test_context(&context);
return;
}
pso_comp = create_compute_pipeline_state(context.device, context.root_signature,
execute_indirect_state_predication_cs_dxbc);
init_pipeline_state_desc(&pso_desc, context.root_signature, DXGI_FORMAT_UNKNOWN,
&execute_indirect_state_predication_vs_dxbc, &execute_indirect_state_predication_ps_dxbc, NULL);
pso_desc.DepthStencilState.DepthEnable = FALSE;
pso_desc.DepthStencilState.StencilEnable = FALSE;
pso_desc.RasterizerState.CullMode = D3D12_CULL_MODE_NONE;
ID3D12Device_CreateGraphicsPipelineState(context.device, &pso_desc, &IID_ID3D12PipelineState, (void **)&pso_gfx);
{
uint32_t counts[256] = { 0 };
for (i = 0; i < ARRAY_SIZE(counts) / 2; i++)
counts[i] = i;
indirect_counts = create_upload_buffer(context.device, sizeof(counts), counts);
indirect_copy = create_default_buffer(context.device, sizeof(counts), D3D12_RESOURCE_FLAG_NONE, D3D12_RESOURCE_STATE_COMMON);
}
{
/* Test various ways to observe empty draws. Go beyond 32 so we can test behavior for larger dispatches as well. */
struct draw_arguments draws[128] = {{0}};
for (i = 0; i < ARRAY_SIZE(draws); i++)
{
draws[i].value = i * 4;
if (i & 1)
draws[i].draw.VertexCountPerInstance = 3;
else
draws[i].draw.InstanceCount = 40;
}
/* Let only the last one be a real draw. */
draws[ARRAY_SIZE(draws) - 1].draw.VertexCountPerInstance = 3;
draws[ARRAY_SIZE(draws) - 1].draw.InstanceCount = 40;
indirect_graphics = create_upload_buffer(context.device, sizeof(draws), draws);
}
{
/* Test various ways to observe empty dispatches. */
struct dispatch_arguments dispatches[128] = {{0}};
for (i = 0; i < ARRAY_SIZE(dispatches); i++)
{
dispatches[i].value = i * 4;
switch (i & 3)
{
case 0:
dispatches[i].dispatch.ThreadGroupCountX = 1;
dispatches[i].dispatch.ThreadGroupCountY = 1;
break;
case 1:
dispatches[i].dispatch.ThreadGroupCountX = 1;
dispatches[i].dispatch.ThreadGroupCountZ = 1;
break;
default:
dispatches[i].dispatch.ThreadGroupCountY = 1;
dispatches[i].dispatch.ThreadGroupCountZ = 1;
break;
}
}
dispatches[ARRAY_SIZE(dispatches) - 1].dispatch.ThreadGroupCountX = 10;
dispatches[ARRAY_SIZE(dispatches) - 1].dispatch.ThreadGroupCountY = 10;
dispatches[ARRAY_SIZE(dispatches) - 1].dispatch.ThreadGroupCountZ = 10;
indirect_compute = create_upload_buffer(context.device, sizeof(dispatches), dispatches);
}
memset(&query_desc, 0, sizeof(query_desc));
query_desc.Count = 64 * 1024;
query_desc.Type = D3D12_QUERY_HEAP_TYPE_TIMESTAMP;
ID3D12Device_CreateQueryHeap(context.device, &query_desc, &IID_ID3D12QueryHeap, (void **)&query_heap);
query_readback = create_readback_buffer(context.device, 64 * 1024 * sizeof(uint64_t));
/* Graphics */
{
timestamp_index = 0;
ID3D12GraphicsCommandList_EndQuery(context.list, query_heap, D3D12_QUERY_TYPE_TIMESTAMP, timestamp_index++);
/* Make sure the first timestamp is ordered before any reordered preprocess work. */
ID3D12GraphicsCommandList_Close(context.list);
exec_command_list(context.queue, context.list);
ID3D12GraphicsCommandList_Reset(context.list, context.allocator, NULL);
output = create_default_buffer(context.device, 64 * 1024, D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COMMON);
set_viewport(&vp, 0, 0, 1, 1, 0, 1);
set_rect(&sci, 0, 0, 1, 1);
ID3D12GraphicsCommandList_RSSetViewports(context.list, 1, &vp);
ID3D12GraphicsCommandList_RSSetScissorRects(context.list, 1, &sci);
ID3D12GraphicsCommandList_SetPipelineState(context.list, pso_gfx);
ID3D12GraphicsCommandList_SetGraphicsRootSignature(context.list, context.root_signature);
ID3D12GraphicsCommandList_IASetPrimitiveTopology(context.list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
ID3D12GraphicsCommandList_SetGraphicsRootConstantBufferView(context.list, 1, ID3D12Resource_GetGPUVirtualAddress(indirect_counts) + 256);
for (i = 0; i < 6; i++)
{
if (i == 0)
ID3D12GraphicsCommandList_SetPredication(context.list, NULL, 0, D3D12_PREDICATION_OP_EQUAL_ZERO);
else if (i == 1)
ID3D12GraphicsCommandList_SetPredication(context.list, indirect_counts, 0, D3D12_PREDICATION_OP_EQUAL_ZERO);
else if (i == 2)
ID3D12GraphicsCommandList_SetPredication(context.list, indirect_counts, 0, D3D12_PREDICATION_OP_NOT_EQUAL_ZERO);
else if (i == 5)
ID3D12GraphicsCommandList_SetPredication(context.list, indirect_copy, 0, D3D12_PREDICATION_OP_EQUAL_ZERO);
/* inherit predication in i == 3 */
/* Try to trigger sync issues. */
ID3D12GraphicsCommandList_SetGraphicsRootUnorderedAccessView(context.list, 2, ID3D12Resource_GetGPUVirtualAddress(output) + (i * 512 + 1) * sizeof(uint32_t));
for (j = 0; j < 16; j++)
{
ID3D12GraphicsCommandList_ExecuteIndirect(context.list, sig, 128, indirect_graphics, 0, NULL, 0); /* last draw will do something, verify we actually check all draws when we cull */
ID3D12GraphicsCommandList_EndQuery(context.list, query_heap, D3D12_QUERY_TYPE_TIMESTAMP, timestamp_index++);
}
ID3D12GraphicsCommandList_SetGraphicsRootUnorderedAccessView(context.list, 2, ID3D12Resource_GetGPUVirtualAddress(output) + (i * 512 + 0) * sizeof(uint32_t));
/* Hammer hard to study profiler. */
for (j = 0; j < 1024; j++)
{
ID3D12GraphicsCommandList_ExecuteIndirect(context.list, sig, 127, indirect_graphics, 0, NULL, 0); /* should do nothing, and should be culled out */
ID3D12GraphicsCommandList_EndQuery(context.list, query_heap, D3D12_QUERY_TYPE_TIMESTAMP, timestamp_index++);
}
ID3D12GraphicsCommandList_SetGraphicsRootUnorderedAccessView(context.list, 2, ID3D12Resource_GetGPUVirtualAddress(output) + (i * 512 + 2) * sizeof(uint32_t));
ID3D12GraphicsCommandList_ExecuteIndirect(context.list, sig, 1, indirect_graphics, 127 * sizeof(struct draw_arguments), NULL, 0); /* same, but only 1 draw */
ID3D12GraphicsCommandList_SetGraphicsRootUnorderedAccessView(context.list, 2, ID3D12Resource_GetGPUVirtualAddress(output) + (i * 512 + 3) * sizeof(uint32_t));
if (i > 2)
ID3D12GraphicsCommandList_ExecuteIndirect(context.list, sig, 1, indirect_graphics, 127 * sizeof(struct draw_arguments), indirect_copy, 0); /* same, but indirect count */
else
ID3D12GraphicsCommandList_ExecuteIndirect(context.list, sig, 1, indirect_graphics, 127 * sizeof(struct draw_arguments), indirect_counts, 4); /* same, but indirect count */
ID3D12GraphicsCommandList_EndQuery(context.list, query_heap, D3D12_QUERY_TYPE_TIMESTAMP, timestamp_index++);
if (i == 2)
{
/* Check if predication persists across an INDIRECT_ARGUMENT barrier */
ID3D12GraphicsCommandList_SetPredication(context.list, NULL, 0, D3D12_PREDICATION_OP_EQUAL_ZERO);
ID3D12GraphicsCommandList_CopyBufferRegion(context.list, indirect_copy, 0, indirect_counts, 0, 8);
ID3D12GraphicsCommandList_SetPredication(context.list, indirect_counts, 0, D3D12_PREDICATION_OP_NOT_EQUAL_ZERO);
transition_resource_state(context.list, indirect_copy, D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT); /* --- split --- */
}
else if (i == 3)
{
ID3D12GraphicsCommandList_SetPredication(context.list, NULL, 0, D3D12_PREDICATION_OP_EQUAL_ZERO);
transition_resource_state(context.list, indirect_copy, D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT, D3D12_RESOURCE_STATE_COPY_DEST);
ID3D12GraphicsCommandList_CopyBufferRegion(context.list, indirect_copy, 0, indirect_counts, 512, 8); /* copy a 0 count here. Copy to same location to make sure the indirect count caches get flushed. */
transition_resource_state(context.list, indirect_copy, D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT); /* --- split --- */
}
else if (i == 4)
{
transition_resource_state(context.list, indirect_copy, D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT, D3D12_RESOURCE_STATE_COPY_DEST);
ID3D12GraphicsCommandList_CopyBufferRegion(context.list, indirect_copy, 0, indirect_counts, 32, 8); /* copy a non-zero count here. Copy to same location to make sure the indirect count caches get flushed. */
transition_resource_state(context.list, indirect_copy, D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT); /* --- split --- */
}
else if (i == 5)
{
/* Stress test to make sure we stop splitting at some point. */
transition_resource_state(context.list, indirect_copy, D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT, D3D12_RESOURCE_STATE_COPY_DEST);
transition_resource_state(context.list, indirect_copy, D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT); /* --- split --- */
transition_resource_state(context.list, indirect_copy, D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT, D3D12_RESOURCE_STATE_COPY_DEST);
transition_resource_state(context.list, indirect_copy, D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT); /* --- split --- */
transition_resource_state(context.list, indirect_copy, D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT, D3D12_RESOURCE_STATE_COPY_DEST);
transition_resource_state(context.list, indirect_copy, D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT); /* --- split --- */
transition_resource_state(context.list, indirect_copy, D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT, D3D12_RESOURCE_STATE_COPY_DEST);
transition_resource_state(context.list, indirect_copy, D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT); /* --- split --- */
}
}
assert(timestamp_index <= 64 * 1024);
ID3D12GraphicsCommandList_ResolveQueryData(context.list, query_heap, D3D12_QUERY_TYPE_TIMESTAMP, 0, timestamp_index,
query_readback, 0);
ID3D12GraphicsCommandList_SetPredication(context.list, NULL, 0, D3D12_PREDICATION_OP_EQUAL_ZERO);
transition_resource_state(context.list, output, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);
get_buffer_readback_with_command_list(output, DXGI_FORMAT_UNKNOWN, &rb, context.queue, context.list);
reset_command_list(context.list, context.allocator);
for (i = 0; i < 128; i++)
{
uint32_t expected_y;
uint32_t expected_w;
struct uvec4 value;
uint32_t expected;
for (j = 0; j < 6; j++)
{
value = *get_readback_uvec4(&rb, i + 128 * j, 0);
expected = i == 127 ? 40 : 0; /* Number of instances (i.e. pixels) drawn */
expected *= 64; /* multiplier from root CBV */
if (j == 2 || j == 3)
expected = 0; /* predicated away */
/* In iteration 5, we copied a zero indirect count. */
expected_w = j == 4 ? 0 : expected;
expected_y = expected * 16;
/* of the gang of 4 draws, the first one should not do anything */
ok(value.x == 0 && value.y == expected_y && value.z == expected && value.w == expected_w,
"Iteration %u, draw output %u: expected {%u, %u, %u, %u}, got {%u, %u, %u, %u}.\n", j, i, 0,
expected_y, expected, expected,
value.x, value.y, value.z, value.w);
}
}
release_resource_readback(&rb);
ID3D12Resource_Release(output);
report_predication_timestamps(context.queue, query_readback, timestamp_index, "DGC");
}
/* Compute */
{
timestamp_index = 0;
ID3D12GraphicsCommandList_EndQuery(context.list, query_heap, D3D12_QUERY_TYPE_TIMESTAMP, timestamp_index++);
/* Make sure the first timestamp is ordered before any reordered preprocess work. */
ID3D12GraphicsCommandList_Close(context.list);
exec_command_list(context.queue, context.list);
ID3D12GraphicsCommandList_Reset(context.list, context.allocator, NULL);
output = create_default_buffer(context.device, 64 * 1024, D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COMMON);
ID3D12GraphicsCommandList_SetComputeRootSignature(context.list, context.root_signature);
ID3D12GraphicsCommandList_SetComputeRootConstantBufferView(context.list, 1, ID3D12Resource_GetGPUVirtualAddress(indirect_counts) + 256);
ID3D12GraphicsCommandList_SetPipelineState(context.list, pso_comp);
for (i = 0; i < 6; i++)
{
if (i == 0)
ID3D12GraphicsCommandList_SetPredication(context.list, NULL, 0, D3D12_PREDICATION_OP_EQUAL_ZERO);
else if (i == 1)
ID3D12GraphicsCommandList_SetPredication(context.list, indirect_counts, 0, D3D12_PREDICATION_OP_EQUAL_ZERO);
else if (i == 2)
ID3D12GraphicsCommandList_SetPredication(context.list, indirect_counts, 0, D3D12_PREDICATION_OP_NOT_EQUAL_ZERO);
else if (i == 5)
ID3D12GraphicsCommandList_SetPredication(context.list, indirect_copy, 0, D3D12_PREDICATION_OP_EQUAL_ZERO);
/* inherit predication in i == 3 */
/* Try to trigger any sync issues. */
ID3D12GraphicsCommandList_SetComputeRootUnorderedAccessView(context.list, 2, ID3D12Resource_GetGPUVirtualAddress(output) + (i * 512 + 1) * sizeof(uint32_t));
for (j = 0; j < 16; j++)
{
ID3D12GraphicsCommandList_ExecuteIndirect(context.list, sig_compute, 128, indirect_compute, 0, NULL, 0); /* last draw will do something, verify we actually check all draws when we cull */
ID3D12GraphicsCommandList_EndQuery(context.list, query_heap, D3D12_QUERY_TYPE_TIMESTAMP, timestamp_index++);
}
ID3D12GraphicsCommandList_SetComputeRootUnorderedAccessView(context.list, 2, ID3D12Resource_GetGPUVirtualAddress(output) + (i * 512 + 0) * sizeof(uint32_t));
/* Hammer this really hard, so we can stare at profiler. */
for (j = 0; j < 1024; j++)
{
ID3D12GraphicsCommandList_ExecuteIndirect(context.list, sig_compute, 127, indirect_compute, 0, NULL, 0); /* should do nothing, and should be culled out */
ID3D12GraphicsCommandList_EndQuery(context.list, query_heap, D3D12_QUERY_TYPE_TIMESTAMP, timestamp_index++);
}
ID3D12GraphicsCommandList_SetComputeRootUnorderedAccessView(context.list, 2, ID3D12Resource_GetGPUVirtualAddress(output) + (i * 512 + 2) * sizeof(uint32_t));
ID3D12GraphicsCommandList_ExecuteIndirect(context.list, sig_compute, 1, indirect_compute, 127 * sizeof(struct dispatch_arguments), NULL, 0); /* same, but only 1 draw */
ID3D12GraphicsCommandList_EndQuery(context.list, query_heap, D3D12_QUERY_TYPE_TIMESTAMP, timestamp_index++);
ID3D12GraphicsCommandList_SetComputeRootUnorderedAccessView(context.list, 2, ID3D12Resource_GetGPUVirtualAddress(output) + (i * 512 + 3) * sizeof(uint32_t));
if (i > 2)
ID3D12GraphicsCommandList_ExecuteIndirect(context.list, sig_compute, 1, indirect_compute, 127 * sizeof(struct dispatch_arguments), indirect_copy, 0); /* same, but indirect count */
else
ID3D12GraphicsCommandList_ExecuteIndirect(context.list, sig_compute, 1, indirect_compute, 127 * sizeof(struct dispatch_arguments), indirect_counts, 4); /* same, but indirect count */
ID3D12GraphicsCommandList_EndQuery(context.list, query_heap, D3D12_QUERY_TYPE_TIMESTAMP, timestamp_index++);
if (i == 2)
{
/* Check if predication persists across an INDIRECT_ARGUMENT barrier */
ID3D12GraphicsCommandList_SetPredication(context.list, NULL, 0, D3D12_PREDICATION_OP_EQUAL_ZERO);
ID3D12GraphicsCommandList_CopyBufferRegion(context.list, indirect_copy, 0, indirect_counts, 0, 8);
ID3D12GraphicsCommandList_SetPredication(context.list, indirect_counts, 0, D3D12_PREDICATION_OP_NOT_EQUAL_ZERO);
transition_resource_state(context.list, indirect_copy, D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT); /* --- split --- */
}
else if (i == 3)
{
ID3D12GraphicsCommandList_SetPredication(context.list, NULL, 0, D3D12_PREDICATION_OP_EQUAL_ZERO);
transition_resource_state(context.list, indirect_copy, D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT, D3D12_RESOURCE_STATE_COPY_DEST);
ID3D12GraphicsCommandList_CopyBufferRegion(context.list, indirect_copy, 0, indirect_counts, 512, 8); /* copy a 0 count here. Copy to same location to make sure the indirect count caches get flushed. */
transition_resource_state(context.list, indirect_copy, D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT); /* --- split --- */
}
else if (i == 4)
{
transition_resource_state(context.list, indirect_copy, D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT, D3D12_RESOURCE_STATE_COPY_DEST);
ID3D12GraphicsCommandList_CopyBufferRegion(context.list, indirect_copy, 0, indirect_counts, 32, 8); /* copy a non-zero count here. Copy to same location to make sure the indirect count caches get flushed. */
transition_resource_state(context.list, indirect_copy, D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT); /* --- split --- */
}
else if (i == 5)
{
/* Stress test to make sure we stop splitting at some point. */
transition_resource_state(context.list, indirect_copy, D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT, D3D12_RESOURCE_STATE_COPY_DEST);
transition_resource_state(context.list, indirect_copy, D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT); /* --- split --- */
transition_resource_state(context.list, indirect_copy, D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT, D3D12_RESOURCE_STATE_COPY_DEST);
transition_resource_state(context.list, indirect_copy, D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT); /* --- split --- */
transition_resource_state(context.list, indirect_copy, D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT, D3D12_RESOURCE_STATE_COPY_DEST);
transition_resource_state(context.list, indirect_copy, D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT); /* --- split --- */
transition_resource_state(context.list, indirect_copy, D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT, D3D12_RESOURCE_STATE_COPY_DEST);
transition_resource_state(context.list, indirect_copy, D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT); /* --- split --- */
}
}
assert(timestamp_index <= 64 * 1024);
ID3D12GraphicsCommandList_ResolveQueryData(context.list, query_heap, D3D12_QUERY_TYPE_TIMESTAMP, 0, timestamp_index,
query_readback, 0);
ID3D12GraphicsCommandList_SetPredication(context.list, NULL, 0, D3D12_PREDICATION_OP_EQUAL_ZERO);
transition_resource_state(context.list, output, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);
get_buffer_readback_with_command_list(output, DXGI_FORMAT_UNKNOWN, &rb, context.queue, context.list);
reset_command_list(context.list, context.allocator);
for (i = 0; i < 128; i++)
{
uint32_t expected_y;
uint32_t expected_w;
struct uvec4 value;
uint32_t expected;
for (j = 0; j < 6; j++)
{
value = *get_readback_uvec4(&rb, i + 128 * j, 0);
expected = i == 127 ? 1000 : 0; /* Number of workgroups drawn */
expected *= 64; /* multiplier from root CBV */
if (j == 2 || j == 3)
expected = 0; /* predicated away */
/* In iteration 5, we copied a zero indirect count. */
expected_w = j == 4 ? 0 : expected;
expected_y = expected * 16;
/* of the gang of 4 dispatches, the first one should not do anything */
ok(value.x == 0 && value.y == expected_y && value.z == expected && value.w == expected_w,
"Iteration %u, draw output %u: expected {%u, %u, %u, %u}, got {%u, %u, %u, %u}.\n", j, i, 0,
expected_y, expected, expected,
value.x, value.y, value.z, value.w);
}
}
release_resource_readback(&rb);
ID3D12Resource_Release(output);
report_predication_timestamps(context.queue, query_readback, timestamp_index, "DGCC");
}
ID3D12Resource_Release(query_readback);
ID3D12QueryHeap_Release(query_heap);
ID3D12CommandSignature_Release(sig);
ID3D12CommandSignature_Release(sig_compute);
ID3D12Resource_Release(indirect_counts);
ID3D12Resource_Release(indirect_graphics);
ID3D12Resource_Release(indirect_compute);
ID3D12Resource_Release(indirect_copy);
ID3D12PipelineState_Release(pso_comp);
ID3D12PipelineState_Release(pso_gfx);
destroy_test_context(&context);
}
void test_execute_indirect_state(void)
{
static const struct vec4 values = { 1000.0f, 2000.0f, 3000.0f, 4000.0f };
D3D12_INDIRECT_ARGUMENT_DESC indirect_argument_descs[2];
D3D12_COMMAND_SIGNATURE_DESC command_signature_desc;
D3D12_ROOT_SIGNATURE_DESC root_signature_desc;
D3D12_GRAPHICS_PIPELINE_STATE_DESC pso_desc;
ID3D12CommandSignature *command_signature;
D3D12_SO_DECLARATION_ENTRY so_entries[1];
ID3D12GraphicsCommandList *command_list;
D3D12_ROOT_PARAMETER root_parameters[4];
ID3D12RootSignature *root_signatures[2];
ID3D12Resource *argument_buffer_late;
D3D12_STREAM_OUTPUT_BUFFER_VIEW sov;
ID3D12Resource *streamout_buffer;
D3D12_VERTEX_BUFFER_VIEW vbvs[2];
ID3D12Resource *argument_buffer;
struct test_context_desc desc;
ID3D12PipelineState *psos[2];
struct test_context context;
struct resource_readback rb;
D3D12_INDEX_BUFFER_VIEW ibv;
ID3D12CommandQueue *queue;
const UINT so_stride = 16;
ID3D12Resource *vbo[3];
ID3D12Resource *ibo[2];
unsigned int i, j, k;
ID3D12Resource *cbv;
ID3D12Resource *srv;
ID3D12Resource *uav;
HRESULT hr;
static const D3D12_INPUT_ELEMENT_DESC layout_desc[] =
{
{"COLOR", 0, DXGI_FORMAT_R32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
{"COLOR", 1, DXGI_FORMAT_R32_FLOAT, 1, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
};
struct test
{
const D3D12_INDIRECT_ARGUMENT_DESC *indirect_arguments;
uint32_t indirect_argument_count;
const void *argument_buffer_data;
size_t argument_buffer_size;
uint32_t api_max_count;
const struct vec4 *expected_output;
uint32_t expected_output_count;
uint32_t stride;
uint32_t pso_index;
bool needs_root_sig;
};
/* Modify root parameters. */
struct root_constant_data
{
float constants[2];
D3D12_DRAW_INDEXED_ARGUMENTS indexed;
};
static const D3D12_INDIRECT_ARGUMENT_DESC root_constant_sig[2] =
{
{ .Type = D3D12_INDIRECT_ARGUMENT_TYPE_CONSTANT, .Constant = {
.RootParameterIndex = 0, .DestOffsetIn32BitValues = 1, .Num32BitValuesToSet = 2 }},
{ .Type = D3D12_INDIRECT_ARGUMENT_TYPE_DRAW_INDEXED }
};
static const struct root_constant_data root_constant_data[] =
{
{
.constants = { 100.0f, 500.0f },
.indexed = { .IndexCountPerInstance = 2, .InstanceCount = 1 }
},
{
.constants = { 200.0f, 800.0f },
.indexed = { .IndexCountPerInstance = 1, .InstanceCount = 2,
.StartIndexLocation = 1, .StartInstanceLocation = 100, }
},
};
static const struct vec4 root_constant_expected[] =
{
{ 1000.0f, 64.0f + 100.0f, 500.0f, 4000.0f },
{ 1001.0f, 65.0f + 100.0f, 500.0f, 4000.0f },
{ 1001.0f, 65.0f + 200.0f, 800.0f, 4000.0f },
{ 1001.0f, 65.0f + 200.0f, 800.0f, 4001.0f },
};
/* Modify root parameters, but very large root signature to test boundary conditions. */
static const D3D12_INDIRECT_ARGUMENT_DESC root_constant_spill_sig[2] =
{
{ .Type = D3D12_INDIRECT_ARGUMENT_TYPE_CONSTANT, .Constant = {
.RootParameterIndex = 0, .DestOffsetIn32BitValues = 44 + 1, .Num32BitValuesToSet = 2 }},
{ .Type = D3D12_INDIRECT_ARGUMENT_TYPE_DRAW_INDEXED }
};
static const struct root_constant_data root_constant_spill_data[] =
{
{
.constants = { 100.0f, 500.0f },
.indexed = { .IndexCountPerInstance = 2, .InstanceCount = 1 }
},
{
.constants = { 200.0f, 800.0f },
.indexed = { .IndexCountPerInstance = 1, .InstanceCount = 2,
.StartIndexLocation = 1, .StartInstanceLocation = 100, }
},
};
static const struct vec4 root_constant_spill_expected[] =
{
{ 1000.0f, 64.0f + 100.0f, 500.0f, 4000.0f },
{ 1001.0f, 65.0f + 100.0f, 500.0f, 4000.0f },
{ 1001.0f, 65.0f + 200.0f, 800.0f, 4000.0f },
{ 1001.0f, 65.0f + 200.0f, 800.0f, 4001.0f },
};
/* Modify VBOs. */
struct indirect_vbo_data
{
D3D12_VERTEX_BUFFER_VIEW view[2];
D3D12_DRAW_INDEXED_ARGUMENTS indexed;
};
static const D3D12_INDIRECT_ARGUMENT_DESC indirect_vbo_sig[3] =
{
{ .Type = D3D12_INDIRECT_ARGUMENT_TYPE_VERTEX_BUFFER_VIEW, .VertexBuffer = { .Slot = 0 }},
{ .Type = D3D12_INDIRECT_ARGUMENT_TYPE_VERTEX_BUFFER_VIEW, .VertexBuffer = { .Slot = 1 }},
{ .Type = D3D12_INDIRECT_ARGUMENT_TYPE_DRAW_INDEXED },
};
/* Fill buffer locations later. */
struct indirect_vbo_data indirect_vbo_data[] =
{
{
.view = { { 0, 64, 8 }, { 0, 64, 16 } },
.indexed = { .IndexCountPerInstance = 2, .InstanceCount = 2 }
},
{
/* Test indirectly binding NULL descriptor and 0 stride. */
.view = { { 0, 0, 0 }, { 0, 64, 0 } },
.indexed = { .IndexCountPerInstance = 2, .InstanceCount = 1 }
}
};
static const struct vec4 indirect_vbo_expected[] =
{
{ 1064.0f, 2128.0f, 3000.0f, 4000.0f },
{ 1066.0f, 2132.0f, 3000.0f, 4000.0f },
{ 1064.0f, 2128.0f, 3000.0f, 4001.0f },
{ 1066.0f, 2132.0f, 3000.0f, 4001.0f },
{ 1000.0f, 2016.0f, 3000.0f, 4000.0f }, /* This is buggy on WARP and AMD. We seem to get null descriptor instead. */
{ 1000.0f, 2016.0f, 3000.0f, 4000.0f }, /* This is buggy on WARP and AMD. */
};
/* Modify just one VBO. */
struct indirect_vbo_one_data
{
D3D12_VERTEX_BUFFER_VIEW view;
D3D12_DRAW_INDEXED_ARGUMENTS indexed;
};
static const D3D12_INDIRECT_ARGUMENT_DESC indirect_vbo_one_sig[2] =
{
{ .Type = D3D12_INDIRECT_ARGUMENT_TYPE_VERTEX_BUFFER_VIEW, .VertexBuffer = { .Slot = 0 }},
{ .Type = D3D12_INDIRECT_ARGUMENT_TYPE_DRAW_INDEXED },
};
/* Fill buffer locations later. */
struct indirect_vbo_one_data indirect_vbo_one_data[] =
{
{
.view = { 0, 64, 8 },
.indexed = { .IndexCountPerInstance = 2, .InstanceCount = 1 }
},
{
.indexed = { .IndexCountPerInstance = 1, .InstanceCount = 1 }
}
};
static const struct vec4 indirect_vbo_one_expected[] =
{
{ 1128.0f, 2064.0f, 3000.0f, 4000.0f },
{ 1130.0f, 2065.0f, 3000.0f, 4000.0f },
{ 1000.0f, 2064.0f, 3000.0f, 4000.0f },
};
/* Indirect IBO */
struct indirect_ibo_data
{
D3D12_INDEX_BUFFER_VIEW view;
D3D12_DRAW_INDEXED_ARGUMENTS indexed;
};
static const D3D12_INDIRECT_ARGUMENT_DESC indirect_ibo_sig[2] =
{
{ .Type = D3D12_INDIRECT_ARGUMENT_TYPE_INDEX_BUFFER_VIEW },
{ .Type = D3D12_INDIRECT_ARGUMENT_TYPE_DRAW_INDEXED },
};
struct indirect_ibo_data indirect_ibo_data[] =
{
{
.view = { 0, 0, DXGI_FORMAT_R32_UINT },
.indexed = { .IndexCountPerInstance = 2, .InstanceCount = 1 }
},
{
.view = { 0, 64, DXGI_FORMAT_R16_UINT },
.indexed = { .IndexCountPerInstance = 4, .InstanceCount = 1 }
},
};
static const struct vec4 indirect_ibo_expected[] =
{
{ 1000.0f, 2064.0f, 3000.0f, 4000.0f },
{ 1000.0f, 2064.0f, 3000.0f, 4000.0f },
{ 1016.0f, 2080.0f, 3000.0f, 4000.0f },
{ 1000.0f, 2064.0f, 3000.0f, 4000.0f },
{ 1017.0f, 2081.0f, 3000.0f, 4000.0f },
{ 1000.0f, 2064.0f, 3000.0f, 4000.0f },
};
/* Indirect root arguments */
struct indirect_root_descriptor_data
{
D3D12_GPU_VIRTUAL_ADDRESS cbv;
D3D12_GPU_VIRTUAL_ADDRESS srv;
D3D12_GPU_VIRTUAL_ADDRESS uav;
D3D12_DRAW_ARGUMENTS array;
};
static const D3D12_INDIRECT_ARGUMENT_DESC indirect_root_descriptor_sig[4] =
{
{ .Type = D3D12_INDIRECT_ARGUMENT_TYPE_CONSTANT_BUFFER_VIEW, .ConstantBufferView = { .RootParameterIndex = 1 } },
{ .Type = D3D12_INDIRECT_ARGUMENT_TYPE_SHADER_RESOURCE_VIEW, .ShaderResourceView = { .RootParameterIndex = 2 } },
{ .Type = D3D12_INDIRECT_ARGUMENT_TYPE_UNORDERED_ACCESS_VIEW, .UnorderedAccessView = { .RootParameterIndex = 3 } },
{ .Type = D3D12_INDIRECT_ARGUMENT_TYPE_DRAW },
};
struct indirect_root_descriptor_data indirect_root_descriptor_data[] =
{
{ .array = { .VertexCountPerInstance = 1, .InstanceCount = 1 } },
{ .array = { .VertexCountPerInstance = 1, .InstanceCount = 1 } },
};
static const struct vec4 indirect_root_descriptor_expected[] =
{
{ 1000.0f, 2064.0f, 3000.0f + 64.0f, 4000.0f + 2.0f },
{ 1000.0f, 2064.0f, 3000.0f + 128.0f, 4000.0f + 3.0f },
};
/* Test packing rules.
* 64-bit aligned values are tightly packed with 32-bit alignment when they are in indirect command buffers. */
struct indirect_alignment_data
{
float value;
uint32_t cbv_va[2];
D3D12_DRAW_ARGUMENTS arrays;
};
static const D3D12_INDIRECT_ARGUMENT_DESC indirect_alignment_sig[3] =
{
{ .Type = D3D12_INDIRECT_ARGUMENT_TYPE_CONSTANT, .Constant = {
.RootParameterIndex = 0, .DestOffsetIn32BitValues = 1, .Num32BitValuesToSet = 1 }},
{ .Type = D3D12_INDIRECT_ARGUMENT_TYPE_CONSTANT_BUFFER_VIEW, .ConstantBufferView = { .RootParameterIndex = 1 }},
{ .Type = D3D12_INDIRECT_ARGUMENT_TYPE_DRAW },
};
struct indirect_alignment_data indirect_alignment_data[] =
{
{
.value = 5.0f,
.arrays = { .VertexCountPerInstance = 1, .InstanceCount = 1 }
},
{
.value = 6.0f,
.arrays = { .VertexCountPerInstance = 1, .InstanceCount = 1 }
},
};
static const struct vec4 indirect_alignment_expected[] =
{
{ 1000.0f, 69.0f, 3064.0f, 4000.0f },
{ 1000.0f, 70.0f, 3128.0f, 4000.0f },
};
#define DECL_TEST(t, pso_index, needs_root_sig) { t##_sig, ARRAY_SIZE(t##_sig), t##_data, sizeof(t##_data), ARRAY_SIZE(t##_data), \
t##_expected, ARRAY_SIZE(t##_expected), sizeof(*(t##_data)), pso_index, needs_root_sig }
const struct test tests[] =
{
DECL_TEST(root_constant, 0, true),
DECL_TEST(indirect_vbo, 0, false),
DECL_TEST(indirect_vbo_one, 0, false),
DECL_TEST(indirect_ibo, 0, false),
DECL_TEST(indirect_root_descriptor, 0, true),
DECL_TEST(indirect_alignment, 0, true),
DECL_TEST(root_constant_spill, 1, true),
DECL_TEST(indirect_root_descriptor, 1, true),
};
#undef DECL_TEST
uint32_t ibo_data[ARRAY_SIZE(ibo)][64];
float vbo_data[ARRAY_SIZE(vbo)][64];
float generic_data[4096];
#include "shaders/command/headers/execute_indirect_state_vs_code_small_cbv.h"
#include "shaders/command/headers/execute_indirect_state_vs_code_large_cbv.h"
memset(&desc, 0, sizeof(desc));
desc.no_root_signature = true;
desc.no_pipeline = true;
if (!init_test_context(&context, &desc))
return;
command_list = context.list;
queue = context.queue;
for (j = 0; j < ARRAY_SIZE(ibo); j++)
for (i = 0; i < ARRAY_SIZE(ibo_data[j]); i++)
ibo_data[j][i] = j * 16 + i;
for (j = 0; j < ARRAY_SIZE(vbo); j++)
for (i = 0; i < ARRAY_SIZE(vbo_data[j]); i++)
vbo_data[j][i] = (float)(j * ARRAY_SIZE(vbo_data[j]) + i);
for (i = 0; i < ARRAY_SIZE(generic_data); i++)
generic_data[i] = (float)i;
for (i = 0; i < ARRAY_SIZE(ibo); i++)
ibo[i] = create_upload_buffer(context.device, sizeof(ibo_data[i]), ibo_data[i]);
for (i = 0; i < ARRAY_SIZE(vbo); i++)
vbo[i] = create_upload_buffer(context.device, sizeof(vbo_data[i]), vbo_data[i]);
cbv = create_upload_buffer(context.device, sizeof(generic_data), generic_data);
srv = create_upload_buffer(context.device, sizeof(generic_data), generic_data);
uav = create_default_buffer(context.device, sizeof(generic_data),
D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS,
D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
indirect_vbo_data[0].view[0].BufferLocation = ID3D12Resource_GetGPUVirtualAddress(vbo[1]);
indirect_vbo_data[0].view[1].BufferLocation = ID3D12Resource_GetGPUVirtualAddress(vbo[2]);
indirect_vbo_data[1].view[0].BufferLocation = 0;
indirect_vbo_data[1].view[1].BufferLocation = ID3D12Resource_GetGPUVirtualAddress(vbo[0]) + 64;
indirect_vbo_one_data[0].view.BufferLocation = ID3D12Resource_GetGPUVirtualAddress(vbo[2]);
indirect_vbo_one_data[1].view.BufferLocation = 0;
indirect_ibo_data[1].view.BufferLocation = ID3D12Resource_GetGPUVirtualAddress(ibo[1]);
indirect_root_descriptor_data[0].cbv = ID3D12Resource_GetGPUVirtualAddress(cbv) + 256;
indirect_root_descriptor_data[0].srv = ID3D12Resource_GetGPUVirtualAddress(srv) + 8;
indirect_root_descriptor_data[0].uav = ID3D12Resource_GetGPUVirtualAddress(uav) + 4;
indirect_root_descriptor_data[1].cbv = ID3D12Resource_GetGPUVirtualAddress(cbv) + 512;
indirect_root_descriptor_data[1].srv = ID3D12Resource_GetGPUVirtualAddress(srv) + 12;
indirect_root_descriptor_data[1].uav = ID3D12Resource_GetGPUVirtualAddress(uav) + 8;
memcpy(indirect_alignment_data[0].cbv_va, &indirect_root_descriptor_data[0].cbv, sizeof(D3D12_GPU_VIRTUAL_ADDRESS));
memcpy(indirect_alignment_data[1].cbv_va, &indirect_root_descriptor_data[1].cbv, sizeof(D3D12_GPU_VIRTUAL_ADDRESS));
memset(&root_signature_desc, 0, sizeof(root_signature_desc));
root_signature_desc.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |
D3D12_ROOT_SIGNATURE_FLAG_ALLOW_STREAM_OUTPUT;
memset(root_parameters, 0, sizeof(root_parameters));
root_signature_desc.pParameters = root_parameters;
root_signature_desc.NumParameters = ARRAY_SIZE(root_parameters);
root_parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
root_parameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS;
root_parameters[0].Constants.RegisterSpace = 1;
root_parameters[0].Constants.Num32BitValues = 4;
root_parameters[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
root_parameters[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
root_parameters[2].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
root_parameters[2].ParameterType = D3D12_ROOT_PARAMETER_TYPE_SRV;
root_parameters[3].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
root_parameters[3].ParameterType = D3D12_ROOT_PARAMETER_TYPE_UAV;
hr = create_root_signature(context.device, &root_signature_desc, &root_signatures[0]);
ok(SUCCEEDED(hr), "Failed to create root signature, hr #%x.\n", hr);
root_parameters[0].Constants.Num32BitValues = 48;
hr = create_root_signature(context.device, &root_signature_desc, &root_signatures[1]);
ok(SUCCEEDED(hr), "Failed to create root signature, hr #%x.\n", hr);
memset(so_entries, 0, sizeof(so_entries));
so_entries[0].ComponentCount = 4;
so_entries[0].SemanticName = "SV_Position";
memset(&pso_desc, 0, sizeof(pso_desc));
pso_desc.VS = execute_indirect_state_vs_code_small_cbv_dxbc;
pso_desc.StreamOutput.NumStrides = 1;
pso_desc.StreamOutput.pBufferStrides = &so_stride;
pso_desc.StreamOutput.pSODeclaration = so_entries;
pso_desc.StreamOutput.NumEntries = ARRAY_SIZE(so_entries);
pso_desc.StreamOutput.RasterizedStream = D3D12_SO_NO_RASTERIZED_STREAM;
pso_desc.pRootSignature = root_signatures[0];
pso_desc.SampleDesc.Count = 1;
pso_desc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_POINT;
pso_desc.RasterizerState.FillMode = D3D12_FILL_MODE_SOLID;
pso_desc.RasterizerState.CullMode = D3D12_CULL_MODE_NONE;
pso_desc.InputLayout.NumElements = ARRAY_SIZE(layout_desc);
pso_desc.InputLayout.pInputElementDescs = layout_desc;
hr = ID3D12Device_CreateGraphicsPipelineState(context.device, &pso_desc, &IID_ID3D12PipelineState, (void**)&psos[0]);
ok(SUCCEEDED(hr), "Failed to create PSO, hr #%x.\n", hr);
pso_desc.VS = execute_indirect_state_vs_code_large_cbv_dxbc;
pso_desc.pRootSignature = root_signatures[1];
hr = ID3D12Device_CreateGraphicsPipelineState(context.device, &pso_desc, &IID_ID3D12PipelineState, (void**)&psos[1]);
ok(SUCCEEDED(hr), "Failed to create PSO, hr #%x.\n", hr);
/* Verify sanity checks.
* As per validation layers, there must be exactly one command in the signature.
* It must come last. Verify that we check for this. */
memset(&command_signature_desc, 0, sizeof(command_signature_desc));
command_signature_desc.NumArgumentDescs = 1;
command_signature_desc.pArgumentDescs = indirect_argument_descs;
command_signature_desc.ByteStride = sizeof(D3D12_VERTEX_BUFFER_VIEW);
indirect_argument_descs[0].Type = D3D12_INDIRECT_ARGUMENT_TYPE_VERTEX_BUFFER_VIEW;
hr = ID3D12Device_CreateCommandSignature(context.device, &command_signature_desc, NULL,
&IID_ID3D12CommandSignature, (void**)&command_signature);
ok(hr == E_INVALIDARG, "Unexpected hr #%x.\n", hr);
command_signature_desc.NumArgumentDescs = 2;
command_signature_desc.pArgumentDescs = indirect_argument_descs;
command_signature_desc.ByteStride = sizeof(D3D12_DRAW_INDEXED_ARGUMENTS) + sizeof(D3D12_VERTEX_BUFFER_VIEW);
indirect_argument_descs[0].Type = D3D12_INDIRECT_ARGUMENT_TYPE_DRAW_INDEXED;
indirect_argument_descs[1].Type = D3D12_INDIRECT_ARGUMENT_TYPE_VERTEX_BUFFER_VIEW;
hr = ID3D12Device_CreateCommandSignature(context.device, &command_signature_desc, NULL,
&IID_ID3D12CommandSignature, (void**)&command_signature);
ok(hr == E_INVALIDARG, "Unexpected hr #%x.\n", hr);
command_signature_desc.ByteStride = sizeof(D3D12_DRAW_INDEXED_ARGUMENTS) + sizeof(D3D12_DRAW_INDEXED_ARGUMENTS);
indirect_argument_descs[0].Type = D3D12_INDIRECT_ARGUMENT_TYPE_DRAW_INDEXED;
indirect_argument_descs[1].Type = D3D12_INDIRECT_ARGUMENT_TYPE_DRAW_INDEXED;
hr = ID3D12Device_CreateCommandSignature(context.device, &command_signature_desc, NULL,
&IID_ID3D12CommandSignature, (void**)&command_signature);
ok(hr == E_INVALIDARG, "Unexpected hr #%x.\n", hr);
for (i = 0; i < ARRAY_SIZE(tests); i++)
{
struct vec4 expect_reset_state[2];
const struct vec4 *expect, *v;
uint32_t expected_output_size;
uint32_t clear_vbo_mask;
bool root_cbv;
uint32_t size;
vkd3d_test_set_context("Test %u", i);
command_signature_desc.ByteStride = tests[i].stride;
command_signature_desc.pArgumentDescs = tests[i].indirect_arguments;
command_signature_desc.NumArgumentDescs = tests[i].indirect_argument_count;
command_signature_desc.NodeMask = 0;
hr = ID3D12Device_CreateCommandSignature(context.device, &command_signature_desc,
tests[i].needs_root_sig ? root_signatures[tests[i].pso_index] : NULL,
&IID_ID3D12CommandSignature, (void**)&command_signature);
/* Updating root CBV requires push BDA path, which we don't enable on NV by default yet. */
root_cbv = false;
for (j = 0; j < tests[i].indirect_argument_count; j++)
{
if (tests[i].indirect_arguments[j].Type == D3D12_INDIRECT_ARGUMENT_TYPE_CONSTANT_BUFFER_VIEW)
{
root_cbv = true;
break;
}
}
if (FAILED(hr))
{
if (root_cbv && is_nvidia_device(context.device))
skip("Creating indirect root CBV update failed. If the GPU is NVIDIA, try VKD3D_CONFIG=force_raw_va_cbv.\n");
else
skip("Failed creating command signature, skipping test.\n");
continue;
}
argument_buffer = create_upload_buffer(context.device, 256 * 1024, NULL);
argument_buffer_late = create_default_buffer(context.device, 256 * 1024,
D3D12_RESOURCE_FLAG_NONE, D3D12_RESOURCE_STATE_COPY_DEST);
#define UNALIGNED_ARGUMENT_BUFFER_OFFSET (64 * 1024 + 4)
#define UNALIGNED_COUNT_BUFFER_OFFSET (128 * 1024 + 4)
#define OVERSIZED_COUNT_BUFFER_OFFSET (128 * 1024 + 8)
#define ALIGNED_COUNT_BUFFER_OFFSET (128 * 1024 + 4 * 1024)
{
const uint32_t large_value = 1024;
uint8_t *ptr;
ID3D12Resource_Map(argument_buffer, 0, NULL, (void**)&ptr);
memcpy(ptr, tests[i].argument_buffer_data, tests[i].argument_buffer_size);
memcpy(ptr + UNALIGNED_ARGUMENT_BUFFER_OFFSET, tests[i].argument_buffer_data, tests[i].argument_buffer_size);
memcpy(ptr + UNALIGNED_COUNT_BUFFER_OFFSET, &tests[i].api_max_count, sizeof(tests[i].api_max_count));
memcpy(ptr + OVERSIZED_COUNT_BUFFER_OFFSET, &large_value, sizeof(large_value));
memcpy(ptr + ALIGNED_COUNT_BUFFER_OFFSET, &tests[i].api_max_count, sizeof(tests[i].api_max_count));
ID3D12Resource_Unmap(argument_buffer, 0, NULL);
}
streamout_buffer = create_default_buffer(context.device, 64 * 1024,
D3D12_RESOURCE_FLAG_NONE, D3D12_RESOURCE_STATE_STREAM_OUT);
ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, root_signatures[tests[i].pso_index]);
ID3D12GraphicsCommandList_SetPipelineState(command_list, psos[tests[i].pso_index]);
sov.SizeInBytes = 64 * 1024 - sizeof(struct vec4);
sov.BufferLocation = ID3D12Resource_GetGPUVirtualAddress(streamout_buffer) + sizeof(struct vec4);
sov.BufferFilledSizeLocation = ID3D12Resource_GetGPUVirtualAddress(streamout_buffer);
ID3D12GraphicsCommandList_SOSetTargets(command_list, 0, 1, &sov);
/* Set up default rendering state. */
ibv.BufferLocation = ID3D12Resource_GetGPUVirtualAddress(ibo[0]);
ibv.SizeInBytes = sizeof(ibo_data[0]);
ibv.Format = DXGI_FORMAT_R32_UINT;
vbvs[0].BufferLocation = ID3D12Resource_GetGPUVirtualAddress(vbo[0]);
vbvs[0].SizeInBytes = sizeof(vbo_data[0]);
vbvs[0].StrideInBytes = 4;
vbvs[1].BufferLocation = ID3D12Resource_GetGPUVirtualAddress(vbo[1]);
vbvs[1].SizeInBytes = sizeof(vbo_data[1]);
vbvs[1].StrideInBytes = 4;
ID3D12GraphicsCommandList_IASetIndexBuffer(command_list, &ibv);
ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_POINTLIST);
ID3D12GraphicsCommandList_IASetVertexBuffers(command_list, 0, 2, vbvs);
for (j = 0; j < (tests[i].pso_index ? 12 : 1); j++)
ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstants(command_list, 0, 4, &values, 4 * j);
ID3D12GraphicsCommandList_SetGraphicsRootConstantBufferView(command_list, 1,
ID3D12Resource_GetGPUVirtualAddress(cbv));
ID3D12GraphicsCommandList_SetGraphicsRootShaderResourceView(command_list, 2,
ID3D12Resource_GetGPUVirtualAddress(srv));
ID3D12GraphicsCommandList_SetGraphicsRootUnorderedAccessView(command_list, 3,
ID3D12Resource_GetGPUVirtualAddress(uav));
ID3D12GraphicsCommandList_ExecuteIndirect(command_list, command_signature, tests[i].api_max_count,
argument_buffer, 0, NULL, 0);
/* Test equivalent call with indirect count. */
ID3D12GraphicsCommandList_ExecuteIndirect(command_list, command_signature, 1024,
argument_buffer, UNALIGNED_ARGUMENT_BUFFER_OFFSET,
argument_buffer, UNALIGNED_COUNT_BUFFER_OFFSET);
/* Test equivalent call with indirect count, but indirect count which needs to be clamped. */
ID3D12GraphicsCommandList_ExecuteIndirect(command_list, command_signature, tests[i].api_max_count,
argument_buffer, UNALIGNED_ARGUMENT_BUFFER_OFFSET,
argument_buffer, OVERSIZED_COUNT_BUFFER_OFFSET);
/* Test equivalent, but now with late transition to INDIRECT. */
ID3D12GraphicsCommandList_CopyResource(command_list, argument_buffer_late, argument_buffer);
transition_resource_state(command_list, argument_buffer_late, D3D12_RESOURCE_STATE_COPY_DEST,
D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT);
ID3D12GraphicsCommandList_ExecuteIndirect(command_list, command_signature, 1024,
argument_buffer_late, 0, argument_buffer_late, ALIGNED_COUNT_BUFFER_OFFSET);
/* Root descriptors which are part of the state block are cleared to NULL. Recover them here
* since attempting to draw next test will crash GPU. */
ID3D12GraphicsCommandList_SetGraphicsRootConstantBufferView(command_list, 1,
ID3D12Resource_GetGPUVirtualAddress(cbv));
ID3D12GraphicsCommandList_SetGraphicsRootShaderResourceView(command_list, 2,
ID3D12Resource_GetGPUVirtualAddress(srv));
ID3D12GraphicsCommandList_SetGraphicsRootUnorderedAccessView(command_list, 3,
ID3D12Resource_GetGPUVirtualAddress(uav));
/* Other state is cleared to 0. */
ID3D12GraphicsCommandList_DrawInstanced(command_list, 2, 1, 0, 0);
transition_resource_state(command_list, streamout_buffer, D3D12_RESOURCE_STATE_STREAM_OUT, D3D12_RESOURCE_STATE_COPY_SOURCE);
get_buffer_readback_with_command_list(streamout_buffer, DXGI_FORMAT_R32G32B32A32_FLOAT, &rb, queue, command_list);
reset_command_list(command_list, context.allocator);
expected_output_size = (tests[i].expected_output_count * 4 + 2) * sizeof(struct vec4);
size = get_readback_uint(&rb, 0, 0, 0);
ok(size == expected_output_size, "Expected size %u, got %u.\n", expected_output_size, size);
for (j = 0; j < tests[i].expected_output_count; j++)
{
expect = &tests[i].expected_output[j];
v = get_readback_vec4(&rb, j + 1, 0);
ok(compare_vec4(v, expect, 0), "Element (direct count) %u failed: (%f, %f, %f, %f) != (%f, %f, %f, %f)\n",
j, v->x, v->y, v->z, v->w, expect->x, expect->y, expect->z, expect->w);
v = get_readback_vec4(&rb, j + tests[i].expected_output_count + 1, 0);
ok(compare_vec4(v, expect, 0), "Element (clamped count) %u failed: (%f, %f, %f, %f) != (%f, %f, %f, %f)\n",
j, v->x, v->y, v->z, v->w, expect->x, expect->y, expect->z, expect->w);
v = get_readback_vec4(&rb, j + 2 * tests[i].expected_output_count + 1, 0);
ok(compare_vec4(v, expect, 0), "Element (indirect count) %u failed: (%f, %f, %f, %f) != (%f, %f, %f, %f)\n",
j, v->x, v->y, v->z, v->w, expect->x, expect->y, expect->z, expect->w);
v = get_readback_vec4(&rb, j + 3 * tests[i].expected_output_count + 1, 0);
ok(compare_vec4(v, expect, 0), "Element (late latch) %u failed: (%f, %f, %f, %f) != (%f, %f, %f, %f)\n",
j, v->x, v->y, v->z, v->w, expect->x, expect->y, expect->z, expect->w);
}
clear_vbo_mask = 0;
expect_reset_state[0] = values;
/* Root constant state is cleared to zero if it's part of the signature. */
for (j = 0; j < tests[i].indirect_argument_count; j++)
{
if (tests[i].indirect_arguments[j].Type == D3D12_INDIRECT_ARGUMENT_TYPE_CONSTANT)
{
for (k = 0; k < tests[i].indirect_arguments[j].Constant.Num32BitValuesToSet; k++)
(&expect_reset_state[0].x)[(tests[i].indirect_arguments[j].Constant.DestOffsetIn32BitValues + k) % 4] = 0.0f;
}
else if (tests[i].indirect_arguments[j].Type == D3D12_INDIRECT_ARGUMENT_TYPE_VERTEX_BUFFER_VIEW)
clear_vbo_mask |= 1u << tests[i].indirect_arguments[j].VertexBuffer.Slot;
}
expect_reset_state[1] = expect_reset_state[0];
/* VBO/IBO state is cleared to zero if it's part of the signature.
* A NULL IBO should be seen as a IBO which only reads 0 index. */
if (!(clear_vbo_mask & (1u << 0)))
expect_reset_state[1].x += 1.0f;
if (!(clear_vbo_mask & (1u << 1)))
{
expect_reset_state[0].y += 64.0f;
expect_reset_state[1].y += 65.0f;
}
for (j = 0; j < 2; j++)
{
v = get_readback_vec4(&rb, j + 1 + 4 * tests[i].expected_output_count, 0);
expect = &expect_reset_state[j];
ok(compare_vec4(v, expect, 0), "Post-reset element %u failed: (%f, %f, %f, %f) != (%f, %f, %f, %f)\n",
j, v->x, v->y, v->z, v->w, expect->x, expect->y, expect->z, expect->w);
}
ID3D12CommandSignature_Release(command_signature);
ID3D12Resource_Release(argument_buffer);
ID3D12Resource_Release(argument_buffer_late);
ID3D12Resource_Release(streamout_buffer);
release_resource_readback(&rb);
}
vkd3d_test_set_context(NULL);
for (i = 0; i < ARRAY_SIZE(psos); i++)
ID3D12PipelineState_Release(psos[i]);
for (i = 0; i < ARRAY_SIZE(root_signatures); i++)
ID3D12RootSignature_Release(root_signatures[i]);
for (i = 0; i < ARRAY_SIZE(vbo); i++)
ID3D12Resource_Release(vbo[i]);
for (i = 0; i < ARRAY_SIZE(ibo); i++)
ID3D12Resource_Release(ibo[i]);
ID3D12Resource_Release(cbv);
ID3D12Resource_Release(srv);
ID3D12Resource_Release(uav);
destroy_test_context(&context);
}
void test_execute_indirect_state_tier_11(void)
{
enum { DRAW = 0, DRAW_INDEXED, DISPATCH, MESH, COUNT };
ID3D12CommandSignature *command_signature[COUNT] = { NULL };
D3D12_FEATURE_DATA_D3D12_OPTIONS21 options21;
D3D12_GRAPHICS_PIPELINE_STATE_DESC pso_desc;
ID3D12PipelineState *psos[COUNT] = { NULL };
D3D12_FEATURE_DATA_D3D12_OPTIONS7 options7;
D3D12_INDIRECT_ARGUMENT_DESC arguments[2];
struct test_context_desc context_desc;
D3D12_COMMAND_SIGNATURE_DESC cs_desc;
ID3D12Resource *indirect_args[COUNT];
D3D12_ROOT_PARAMETER root_param[2];
D3D12_ROOT_SIGNATURE_DESC rs_desc;
struct resource_readback rb;
struct test_context context;
ID3D12Resource *output;
ID3D12Resource *ibo;
unsigned int i, j;
HRESULT hr;
#include "shaders/command/headers/execute_indirect_tier11_draw.h"
#include "shaders/command/headers/execute_indirect_tier11_dispatch.h"
#include "shaders/command/headers/execute_indirect_tier11_mesh.h"
static const D3D12_DRAW_ARGUMENTS draw_arguments[] = {
{ 14 * 3, 2 },
{ 19 * 3, 1 },
{ 15 * 3, 0 },
{ 251 * 3, 2 },
};
static const D3D12_DRAW_INDEXED_ARGUMENTS indexed_arguments[] = {
{ 100 * 3, 2 },
{ 4 * 3, 1 },
{ 4 * 3, 0 },
{ 251 * 3, 2 },
};
static const D3D12_DISPATCH_ARGUMENTS dispatch_arguments[] = {
{ 8, 5, 2 },
{ 1, 3, 10 },
{ 8, 5, 0 },
{ 4, 6, 20 },
};
uint32_t expected_counts[COUNT][ARRAY_SIZE(dispatch_arguments)];
static const union d3d12_shader_bytecode_subobject ms_subobject = { { D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_MS,
{ execute_indirect_tier11_mesh_code_dxil, sizeof(execute_indirect_tier11_mesh_code_dxil) } } };
static const union d3d12_root_signature_subobject root_signature_subobject =
{ {
D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_ROOT_SIGNATURE,
NULL, /* fill in dynamically */
} };
struct
{
union d3d12_root_signature_subobject root_signature;
union d3d12_shader_bytecode_subobject ms;
} ms_only_pipeline_desc = {
root_signature_subobject, ms_subobject,
};
static const struct
{
const void *args;
size_t size;
} indirect_buffers[] = {
{ draw_arguments, sizeof(draw_arguments) },
{ indexed_arguments, sizeof(indexed_arguments) },
{ dispatch_arguments, sizeof(dispatch_arguments) },
{ dispatch_arguments, sizeof(dispatch_arguments) },
};
for (i = 0; i < ARRAY_SIZE(draw_arguments); i++)
expected_counts[DRAW][i] = draw_arguments[i].VertexCountPerInstance * draw_arguments[i].InstanceCount;
for (i = 0; i < ARRAY_SIZE(indexed_arguments); i++)
expected_counts[DRAW_INDEXED][i] = indexed_arguments[i].IndexCountPerInstance * indexed_arguments[i].InstanceCount;
for (i = 0; i < ARRAY_SIZE(dispatch_arguments); i++)
{
expected_counts[DISPATCH][i] = dispatch_arguments[i].ThreadGroupCountX * dispatch_arguments[i].ThreadGroupCountY * dispatch_arguments[i].ThreadGroupCountZ;
expected_counts[MESH][i] = expected_counts[DISPATCH][i];
}
memset(&context_desc, 0, sizeof(context_desc));
context_desc.no_pipeline = true;
context_desc.no_root_signature = true;
context_desc.no_render_target = true;
if (!init_test_context(&context, &context_desc))
return;
if (FAILED(ID3D12Device_CheckFeatureSupport(context.device, D3D12_FEATURE_D3D12_OPTIONS21, &options21, sizeof(options21))) ||
options21.ExecuteIndirectTier < D3D12_EXECUTE_INDIRECT_TIER_1_1)
{
skip("ExecuteIndirect tier 1.1 not supported.\n");
destroy_test_context(&context);
return;
}
if (FAILED(ID3D12Device_CheckFeatureSupport(context.device, D3D12_FEATURE_D3D12_OPTIONS7, &options7, sizeof(options7))) ||
options7.MeshShaderTier < D3D12_MESH_SHADER_TIER_1)
{
options7.MeshShaderTier = D3D12_MESH_SHADER_TIER_NOT_SUPPORTED;
skip("Mesh shader not supported, skipping mesh indirect test.\n");
}
output = create_default_buffer(context.device, 4096, D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COMMON);
{
uint32_t ibo_data[1024];
for (i = 0; i < ARRAY_SIZE(ibo_data); i++)
ibo_data[i] = i;
ibo = create_upload_buffer(context.device, sizeof(ibo_data), ibo_data);
}
for (i = 0; i < COUNT; i++)
indirect_args[i] = create_upload_buffer(context.device, indirect_buffers[i].size, indirect_buffers[i].args);
memset(&cs_desc, 0, sizeof(cs_desc));
cs_desc.NumArgumentDescs = ARRAY_SIZE(arguments);
cs_desc.pArgumentDescs = arguments;
arguments[0].Type = D3D12_INDIRECT_ARGUMENT_TYPE_INCREMENTING_CONSTANT;
arguments[0].Constant.DestOffsetIn32BitValues = 0;
arguments[0].Constant.Num32BitValuesToSet = 1;
arguments[0].Constant.RootParameterIndex = 0;
memset(&rs_desc, 0, sizeof(rs_desc));
rs_desc.NumParameters = ARRAY_SIZE(root_param);
rs_desc.pParameters = root_param;
memset(root_param, 0, sizeof(root_param));
root_param[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS;
root_param[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
root_param[0].Constants.Num32BitValues = 1;
root_param[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_UAV;
root_param[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
create_root_signature(context.device, &rs_desc, &context.root_signature);
arguments[1].Type = D3D12_INDIRECT_ARGUMENT_TYPE_DRAW;
cs_desc.ByteStride = sizeof(D3D12_DRAW_ARGUMENTS);
hr = ID3D12Device_CreateCommandSignature(context.device, &cs_desc, context.root_signature,
&IID_ID3D12CommandSignature, (void **)&command_signature[DRAW]);
ok(SUCCEEDED(hr), "Failed to create command signature, hr %x.\n", hr);
init_pipeline_state_desc(&pso_desc, context.root_signature, DXGI_FORMAT_UNKNOWN, &execute_indirect_tier11_draw_dxbc, NULL, NULL);
pso_desc.PS.BytecodeLength = 0;
pso_desc.PS.pShaderBytecode = NULL;
hr = ID3D12Device_CreateGraphicsPipelineState(context.device, &pso_desc, &IID_ID3D12PipelineState, (void **)&psos[DRAW]);
ok(SUCCEEDED(hr), "Failed to create PSO, hr #%x.\n", hr);
arguments[1].Type = D3D12_INDIRECT_ARGUMENT_TYPE_DRAW_INDEXED;
cs_desc.ByteStride = sizeof(D3D12_DRAW_INDEXED_ARGUMENTS);
hr = ID3D12Device_CreateCommandSignature(context.device, &cs_desc, context.root_signature,
&IID_ID3D12CommandSignature, (void **)&command_signature[DRAW_INDEXED]);
ok(SUCCEEDED(hr), "Failed to create command signature, hr %x.\n", hr);
psos[DRAW_INDEXED] = psos[DRAW];
ID3D12PipelineState_AddRef(psos[DRAW_INDEXED]);
arguments[1].Type = D3D12_INDIRECT_ARGUMENT_TYPE_DISPATCH;
cs_desc.ByteStride = sizeof(D3D12_DISPATCH_ARGUMENTS);
hr = ID3D12Device_CreateCommandSignature(context.device, &cs_desc, context.root_signature,
&IID_ID3D12CommandSignature, (void **)&command_signature[DISPATCH]);
ok(SUCCEEDED(hr), "Failed to create command signature, hr %x.\n", hr);
psos[DISPATCH] = create_compute_pipeline_state(context.device, context.root_signature, execute_indirect_tier11_dispatch_dxbc);
if (options7.MeshShaderTier >= D3D12_MESH_SHADER_TIER_1)
{
ID3D12Device2 *device2;
ms_only_pipeline_desc.root_signature.root_signature = context.root_signature;
ID3D12Device_QueryInterface(context.device, &IID_ID3D12Device2, (void **)&device2);
arguments[1].Type = D3D12_INDIRECT_ARGUMENT_TYPE_DISPATCH_MESH;
cs_desc.ByteStride = sizeof(D3D12_DISPATCH_MESH_ARGUMENTS);
hr = ID3D12Device_CreateCommandSignature(context.device, &cs_desc, context.root_signature,
&IID_ID3D12CommandSignature, (void **)&command_signature[MESH]);
ok(SUCCEEDED(hr), "Failed to create command signature, hr %x.\n", hr);
hr = create_pipeline_state_from_stream(device2, &ms_only_pipeline_desc, &psos[MESH]);
ok(SUCCEEDED(hr), "Failed to create mesh PSO, hr #%x.\n", hr);
ID3D12Device2_Release(device2);
}
ID3D12GraphicsCommandList_SetGraphicsRootSignature(context.list, context.root_signature);
ID3D12GraphicsCommandList_SetComputeRootSignature(context.list, context.root_signature);
ID3D12GraphicsCommandList_IASetPrimitiveTopology(context.list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
for (i = 0; i < COUNT; i++)
{
D3D12_INDEX_BUFFER_VIEW ibv;
ID3D12GraphicsCommandList_SetGraphicsRootUnorderedAccessView(context.list, 1, ID3D12Resource_GetGPUVirtualAddress(output) + i * 1024);
ID3D12GraphicsCommandList_SetComputeRootUnorderedAccessView(context.list, 1, ID3D12Resource_GetGPUVirtualAddress(output) + i * 1024);
ibv.BufferLocation = ID3D12Resource_GetGPUVirtualAddress(ibo);
ibv.Format = DXGI_FORMAT_R32_UINT;
ibv.SizeInBytes = 1024 * sizeof(uint32_t);
ID3D12GraphicsCommandList_IASetIndexBuffer(context.list, &ibv);
if (psos[i])
{
ID3D12GraphicsCommandList_SetPipelineState(context.list, psos[i]);
ID3D12GraphicsCommandList_ExecuteIndirect(context.list, command_signature[i], ARRAY_SIZE(draw_arguments), indirect_args[i], 0, NULL, 0);
}
}
transition_resource_state(context.list, output, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);
get_buffer_readback_with_command_list(output, DXGI_FORMAT_R32_UINT, &rb, context.queue, context.list);
for (i = 0; i < COUNT; i++)
{
if (!psos[i])
continue;
for (j = 0; j < ARRAY_SIZE(draw_arguments); j++)
{
uint32_t expected = expected_counts[i][j];
uint32_t v;
v = get_readback_uint(&rb, 256 * i + j, 0, 0);
ok(expected == v, "PSO %u, ID %u: Expected %u, got %u.\n", i, j, expected, v);
}
}
for (i = 0; i < COUNT; i++)
{
ID3D12Resource_Release(indirect_args[i]);
if (command_signature[i])
ID3D12CommandSignature_Release(command_signature[i]);
if (psos[i])
ID3D12PipelineState_Release(psos[i]);
}
ID3D12Resource_Release(output);
ID3D12Resource_Release(ibo);
release_resource_readback(&rb);
destroy_test_context(&context);
}
void test_execute_indirect_state_vbo_offsets(void)
{
D3D12_INDIRECT_ARGUMENT_DESC indirect_args[2];
D3D12_GRAPHICS_PIPELINE_STATE_DESC pso_desc;
D3D12_COMMAND_SIGNATURE_DESC cs_desc;
D3D12_INPUT_LAYOUT_DESC input_layout;
ID3D12CommandSignature *command_sig;
D3D12_ROOT_SIGNATURE_DESC rs_desc;
ID3D12Resource *indirect_buffer;
ID3D12Resource *instance_buffer;
D3D12_ROOT_PARAMETER rs_param;
struct test_context_desc desc;
D3D12_VERTEX_BUFFER_VIEW vbv;
ID3D12Resource *index_buffer;
struct resource_readback rb;
struct test_context context;
D3D12_INDEX_BUFFER_VIEW ibv;
D3D12_VIEWPORT vp;
D3D12_RECT sci;
HRESULT hr;
static const D3D12_INPUT_ELEMENT_DESC layout_desc[] =
{
{"I", 0, DXGI_FORMAT_R32_UINT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_INSTANCE_DATA, 0},
};
static const struct indirect_args
{
float values[4];
D3D12_DRAW_INDEXED_ARGUMENTS draw_indexed;
} draw = {
{ 100, 200, 300, 400 }, { 3, 1, 0, 0, 4 },
};
static const uint32_t instance_data[] = { 0, 1, 2, 3, 4, 5 };
static const uint32_t index_buffer_data[] = { 0, 1, 2 };
#include "shaders/command/headers/indirect_state_vbo_offsets_vs.h"
#include "shaders/command/headers/indirect_state_vbo_offsets_ps.h"
input_layout.NumElements = ARRAY_SIZE(layout_desc);
input_layout.pInputElementDescs = layout_desc;
memset(&desc, 0, sizeof(desc));
desc.no_pipeline = true;
desc.no_root_signature = true;
desc.rt_format = DXGI_FORMAT_R32G32B32A32_FLOAT;
desc.rt_width = 1;
desc.rt_height = 1;
if (!init_test_context(&context, &desc))
return;
indirect_buffer = create_upload_buffer(context.device, sizeof(draw), &draw);
instance_buffer = create_upload_buffer(context.device, sizeof(instance_data), instance_data);
index_buffer = create_upload_buffer(context.device, sizeof(index_buffer_data), index_buffer_data);
memset(&rs_desc, 0, sizeof(rs_desc));
memset(&rs_param, 0, sizeof(rs_param));
rs_desc.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT;
rs_desc.NumParameters = 1;
rs_desc.pParameters = &rs_param;
rs_param.ParameterType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS;
rs_param.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
rs_param.Constants.Num32BitValues = 4;
create_root_signature(context.device, &rs_desc, &context.root_signature);
init_pipeline_state_desc(&pso_desc, context.root_signature, DXGI_FORMAT_R32G32B32A32_FLOAT,
&indirect_state_vbo_offsets_vs_dxbc, &indirect_state_vbo_offsets_ps_dxbc, &input_layout);
pso_desc.RasterizerState.CullMode = D3D12_CULL_MODE_NONE;
pso_desc.DepthStencilState.DepthEnable = FALSE;
pso_desc.DepthStencilState.StencilEnable = FALSE;
ID3D12Device_CreateGraphicsPipelineState(context.device, &pso_desc, &IID_ID3D12PipelineState, (void **)&context.pipeline_state);
memset(&cs_desc, 0, sizeof(cs_desc));
memset(indirect_args, 0, sizeof(indirect_args));
cs_desc.NumArgumentDescs = ARRAY_SIZE(indirect_args);
cs_desc.pArgumentDescs = indirect_args;
cs_desc.ByteStride = sizeof(struct indirect_args);
indirect_args[0].Type = D3D12_INDIRECT_ARGUMENT_TYPE_CONSTANT;
indirect_args[0].Constant.Num32BitValuesToSet = 4;
indirect_args[1].Type = D3D12_INDIRECT_ARGUMENT_TYPE_DRAW_INDEXED;
hr = ID3D12Device_CreateCommandSignature(context.device, &cs_desc, context.root_signature, &IID_ID3D12CommandSignature, (void **)&command_sig);
ok(SUCCEEDED(hr) || hr == E_NOTIMPL, "Failed to create command signature.\n");
if (FAILED(hr))
command_sig = NULL;
if (hr == E_NOTIMPL)
skip("Failed to create command signature, DGC is likely not supported.\n");
ID3D12GraphicsCommandList_OMSetRenderTargets(context.list, 1, &context.rtv, TRUE, NULL);
ID3D12GraphicsCommandList_SetGraphicsRootSignature(context.list, context.root_signature);
ID3D12GraphicsCommandList_SetPipelineState(context.list, context.pipeline_state);
set_viewport(&vp, 0, 0, 1, 1, 0, 1);
set_rect(&sci, 0, 0, 1, 1);
ID3D12GraphicsCommandList_RSSetViewports(context.list, 1, &vp);
ID3D12GraphicsCommandList_RSSetScissorRects(context.list, 1, &sci);
ID3D12GraphicsCommandList_IASetPrimitiveTopology(context.list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
ibv.BufferLocation = ID3D12Resource_GetGPUVirtualAddress(index_buffer);
ibv.Format = DXGI_FORMAT_R32_UINT;
ibv.SizeInBytes = sizeof(index_buffer_data);
vbv.BufferLocation = ID3D12Resource_GetGPUVirtualAddress(instance_buffer);
vbv.SizeInBytes = sizeof(instance_data);
vbv.StrideInBytes = sizeof(uint32_t);
ID3D12GraphicsCommandList_IASetIndexBuffer(context.list, &ibv);
ID3D12GraphicsCommandList_IASetVertexBuffers(context.list, 0, 1, &vbv);
if (command_sig)
ID3D12GraphicsCommandList_ExecuteIndirect(context.list, command_sig, 1, indirect_buffer, 0, NULL, 0);
transition_resource_state(context.list, context.render_target, D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
get_texture_readback_with_command_list(context.render_target, 0, &rb, context.queue, context.list);
if (command_sig)
{
float r, g, b, a;
r = get_readback_float(&rb, 0, 0);
g = get_readback_float(&rb, 1, 0);
b = get_readback_float(&rb, 2, 0);
a = get_readback_float(&rb, 3, 0);
ok(r == draw.values[0] + draw.draw_indexed.StartInstanceLocation, "r: Expected %f, got %f\n", draw.values[0] + draw.draw_indexed.StartInstanceLocation, r);
ok(g == draw.values[1], "r: Expected %f, got %f\n", draw.values[1], g);
ok(b == draw.values[2], "r: Expected %f, got %f\n", draw.values[2], b);
ok(a == draw.values[3], "r: Expected %f, got %f\n", draw.values[3], a);
}
ID3D12Resource_Release(indirect_buffer);
ID3D12Resource_Release(instance_buffer);
ID3D12Resource_Release(index_buffer);
if (command_sig)
ID3D12CommandSignature_Release(command_sig);
release_resource_readback(&rb);
destroy_test_context(&context);
}
void test_execute_indirect(void)
{
ID3D12Resource *argument_buffer, *count_buffer, *uav;
D3D12_ROOT_SIGNATURE_DESC root_signature_desc;
ID3D12CommandSignature *command_signature;
ID3D12GraphicsCommandList *command_list;
D3D12_INPUT_LAYOUT_DESC input_layout;
D3D12_ROOT_PARAMETER root_parameter;
ID3D12PipelineState *pipeline_state;
ID3D12RootSignature *root_signature;
struct test_context_desc desc;
D3D12_VERTEX_BUFFER_VIEW vbv;
D3D12_INDEX_BUFFER_VIEW ibv;
struct resource_readback rb;
struct test_context context;
ID3D12CommandQueue *queue;
ID3D12Resource *vb, *ib;
unsigned int i;
D3D12_BOX box;
HRESULT hr;
static const struct
{
struct vec4 position;
uint32_t color;
}
vertices[] =
{
{{-1.0f, -1.0f, 0.0f, 1.0f}, 0xffffff00},
{{-1.0f, 1.0f, 0.0f, 1.0f}, 0xffffff00},
{{ 1.0f, -1.0f, 0.0f, 1.0f}, 0xffffff00},
{{ 1.0f, 1.0f, 0.0f, 1.0f}, 0xffffff00},
{{-1.0f, -1.0f, 0.0f, 1.0f}, 0xff00ff00},
{{-1.0f, 0.5f, 0.0f, 1.0f}, 0xff00ff00},
{{ 0.5f, -1.0f, 0.0f, 1.0f}, 0xff00ff00},
{{ 0.5f, 0.5f, 0.0f, 1.0f}, 0xff00ff00},
{{-1.0f, -1.0f, 0.0f, 1.0f}, 0xff00ff00},
{{-1.0f, 1.0f, 0.0f, 1.0f}, 0xff00ff00},
{{ 1.0f, -1.0f, 0.0f, 1.0f}, 0xff00ff00},
{{ 1.0f, 1.0f, 0.0f, 1.0f}, 0xff00ff00},
};
static const uint32_t indices[] = {0, 1, 2, 3, 2, 1};
static const D3D12_INPUT_ELEMENT_DESC layout_desc[] =
{
{"SV_POSITION", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
{"COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 16, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
};
#include "shaders/command/headers/execute_indirect_vs.h"
#include "shaders/command/headers/execute_indirect_ps.h"
#include "shaders/command/headers/execute_indirect_cs.h"
static const struct argument_data
{
D3D12_DRAW_ARGUMENTS draws[4];
D3D12_DISPATCH_ARGUMENTS dispatch;
D3D12_DRAW_INDEXED_ARGUMENTS indexed_draws[2];
}
argument_data =
{
{{6, 1, 4, 0}, {6, 1, 8, 0}, {6, 1, 0, 0}},
{2, 3, 4},
{{6, 1, 0, 0, 0}, {6, 1, 0, 4, 0}},
};
static const uint32_t count_data[] = {2, 1};
static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};
memset(&desc, 0, sizeof(desc));
desc.root_signature_flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT;
desc.no_pipeline = true;
if (!init_test_context(&context, &desc))
return;
command_list = context.list;
queue = context.queue;
input_layout.pInputElementDescs = layout_desc;
input_layout.NumElements = ARRAY_SIZE(layout_desc);
context.pipeline_state = create_pipeline_state(context.device,
context.root_signature, context.render_target_desc.Format,
&execute_indirect_vs_dxbc, &execute_indirect_ps_dxbc, &input_layout);
vb = create_upload_buffer(context.device, sizeof(vertices), vertices);
vbv.BufferLocation = ID3D12Resource_GetGPUVirtualAddress(vb);
vbv.StrideInBytes = sizeof(*vertices);
vbv.SizeInBytes = sizeof(vertices);
ib = create_upload_buffer(context.device, sizeof(indices), indices);
ibv.BufferLocation = ID3D12Resource_GetGPUVirtualAddress(ib);
ibv.SizeInBytes = sizeof(indices);
ibv.Format = DXGI_FORMAT_R32_UINT;
argument_buffer = create_upload_buffer(context.device, sizeof(argument_data), &argument_data);
count_buffer = create_upload_buffer(context.device, sizeof(count_data), count_data);
command_signature = create_command_signature(context.device, D3D12_INDIRECT_ARGUMENT_TYPE_DRAW);
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_TRIANGLESTRIP);
ID3D12GraphicsCommandList_IASetVertexBuffers(command_list, 0, 1, &vbv);
ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
ID3D12GraphicsCommandList_ExecuteIndirect(command_list, command_signature, 2, argument_buffer, 0, NULL, 0);
transition_resource_state(command_list, context.render_target,
D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
check_sub_resource_uint(context.render_target, 0, queue, command_list, 0xff00ff00, 0);
reset_command_list(command_list, context.allocator);
transition_resource_state(command_list, context.render_target,
D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);
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_TRIANGLESTRIP);
ID3D12GraphicsCommandList_IASetVertexBuffers(command_list, 0, 1, &vbv);
ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
ID3D12GraphicsCommandList_ExecuteIndirect(command_list, command_signature, 4, argument_buffer, 0,
count_buffer, 0);
transition_resource_state(command_list, context.render_target,
D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
check_sub_resource_uint(context.render_target, 0, queue, command_list, 0xff00ff00, 0);
reset_command_list(command_list, context.allocator);
ID3D12CommandSignature_Release(command_signature);
command_signature = create_command_signature(context.device, D3D12_INDIRECT_ARGUMENT_TYPE_DISPATCH);
uav = create_default_buffer(context.device, 2 * 3 * 4 * sizeof(UINT),
D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
root_parameter.ParameterType = D3D12_ROOT_PARAMETER_TYPE_UAV;
root_parameter.Descriptor.ShaderRegister = 0;
root_parameter.Descriptor.RegisterSpace = 0;
root_parameter.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
root_signature_desc.NumParameters = 1;
root_signature_desc.pParameters = &root_parameter;
root_signature_desc.NumStaticSamplers = 0;
root_signature_desc.pStaticSamplers = NULL;
root_signature_desc.Flags = D3D12_ROOT_SIGNATURE_FLAG_NONE;
hr = create_root_signature(context.device, &root_signature_desc, &root_signature);
ok(hr == S_OK, "Failed to create root signature, hr %#x.\n", hr);
ID3D12GraphicsCommandList_SetComputeRootSignature(command_list, root_signature);
pipeline_state = create_compute_pipeline_state(context.device, root_signature, execute_indirect_cs_dxbc);
ID3D12GraphicsCommandList_SetPipelineState(command_list, pipeline_state);
ID3D12GraphicsCommandList_SetComputeRootUnorderedAccessView(command_list,
0, ID3D12Resource_GetGPUVirtualAddress(uav));
ID3D12GraphicsCommandList_ExecuteIndirect(command_list, command_signature, 1, argument_buffer,
offsetof(struct argument_data, dispatch), NULL, 0);
transition_sub_resource_state(command_list, uav, 0,
D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);
get_buffer_readback_with_command_list(uav, DXGI_FORMAT_R32_UINT, &rb, queue, command_list);
for (i = 0; i < rb.width; ++i)
{
unsigned int ret = get_readback_uint(&rb, i, 0, 0);
ok(ret == i, "Got unexpected result %#x at index %u.\n", ret, i);
}
release_resource_readback(&rb);
reset_command_list(command_list, context.allocator);
transition_resource_state(command_list, context.render_target,
D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);
ID3D12CommandSignature_Release(command_signature);
command_signature = create_command_signature(context.device, D3D12_INDIRECT_ARGUMENT_TYPE_DRAW_INDEXED);
ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);
ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
ID3D12GraphicsCommandList_IASetIndexBuffer(command_list, &ibv);
ID3D12GraphicsCommandList_IASetVertexBuffers(command_list, 0, 1, &vbv);
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_ExecuteIndirect(command_list, command_signature,
ARRAY_SIZE(argument_data.indexed_draws), argument_buffer,
offsetof(struct argument_data, indexed_draws), NULL, 0);
transition_resource_state(command_list, context.render_target,
D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
get_texture_readback_with_command_list(context.render_target, 0, &rb, queue, command_list);
set_box(&box, 0, 0, 0, 32, 8, 1);
check_readback_data_uint(&rb, &box, 0xffffff00, 0);
set_box(&box, 24, 8, 0, 32, 32, 1);
check_readback_data_uint(&rb, &box, 0xffffff00, 0);
set_box(&box, 0, 8, 0, 24, 32, 1);
check_readback_data_uint(&rb, &box, 0xff00ff00, 0);
release_resource_readback(&rb);
reset_command_list(command_list, context.allocator);
transition_resource_state(command_list, context.render_target,
D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);
ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);
ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
ID3D12GraphicsCommandList_IASetIndexBuffer(command_list, &ibv);
ID3D12GraphicsCommandList_IASetVertexBuffers(command_list, 0, 1, &vbv);
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_ExecuteIndirect(command_list, command_signature,
ARRAY_SIZE(argument_data.indexed_draws), argument_buffer,
offsetof(struct argument_data, indexed_draws), count_buffer, sizeof(uint32_t));
transition_resource_state(command_list, context.render_target,
D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
check_sub_resource_uint(context.render_target, 0, queue, command_list, 0xffffff00, 0);
ID3D12PipelineState_Release(pipeline_state);
ID3D12RootSignature_Release(root_signature);
ID3D12Resource_Release(ib);
ID3D12Resource_Release(uav);
ID3D12Resource_Release(vb);
ID3D12CommandSignature_Release(command_signature);
ID3D12Resource_Release(argument_buffer);
ID3D12Resource_Release(count_buffer);
destroy_test_context(&context);
}
void test_dispatch_zero_thread_groups(void)
{
D3D12_ROOT_SIGNATURE_DESC root_signature_desc;
ID3D12CommandSignature *command_signature;
ID3D12GraphicsCommandList *command_list;
D3D12_ROOT_PARAMETER root_parameters[2];
ID3D12Resource *argument_buffer, *uav;
struct resource_readback rb;
struct test_context context;
ID3D12CommandQueue *queue;
unsigned int ret, i;
HRESULT hr;
#include "shaders/command/headers/dispatch_zero_thread_groups.h"
static const D3D12_DISPATCH_ARGUMENTS argument_data[] =
{
{1, 1, 1},
{0, 3, 4},
{0, 0, 4},
{0, 0, 0},
{4, 0, 0},
{4, 0, 3},
{4, 2, 0},
{0, 2, 0},
{0, 0, 0},
};
if (!init_compute_test_context(&context))
return;
command_list = context.list;
queue = context.queue;
argument_buffer = create_upload_buffer(context.device, sizeof(argument_data), &argument_data);
command_signature = create_command_signature(context.device, D3D12_INDIRECT_ARGUMENT_TYPE_DISPATCH);
uav = create_default_buffer(context.device, 2 * 256, /* minTexelBufferOffsetAlignment */
D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
root_parameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_UAV;
root_parameters[0].Descriptor.ShaderRegister = 0;
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 = 2;
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(context.device, &root_signature_desc, &context.root_signature);
ok(SUCCEEDED(hr), "Failed to create root signature, hr %#x.\n", hr);
context.pipeline_state = create_compute_pipeline_state(context.device, context.root_signature, dispatch_zero_thread_groups_dxbc);
ID3D12GraphicsCommandList_SetComputeRootSignature(command_list, context.root_signature);
ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
ID3D12GraphicsCommandList_SetComputeRootUnorderedAccessView(command_list,
0, ID3D12Resource_GetGPUVirtualAddress(uav));
for (i = 0; i < ARRAY_SIZE(argument_data); ++i)
{
ID3D12GraphicsCommandList_SetComputeRoot32BitConstant(command_list,
1, 10 + i, 0);
ID3D12GraphicsCommandList_ExecuteIndirect(command_list, command_signature,
1, argument_buffer, i * sizeof(*argument_data), NULL, 0);
}
ID3D12GraphicsCommandList_SetComputeRootUnorderedAccessView(command_list,
0, ID3D12Resource_GetGPUVirtualAddress(uav) + 256);
for (i = 0; i < ARRAY_SIZE(argument_data); ++i)
{
const D3D12_DISPATCH_ARGUMENTS *arg = &argument_data[i];
ID3D12GraphicsCommandList_SetComputeRoot32BitConstant(command_list,
1, 50 + i, 0);
ID3D12GraphicsCommandList_Dispatch(command_list,
arg->ThreadGroupCountX, arg->ThreadGroupCountY, arg->ThreadGroupCountZ);
}
transition_sub_resource_state(command_list, uav, 0,
D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);
get_buffer_readback_with_command_list(uav, DXGI_FORMAT_R32_UINT, &rb, queue, command_list);
ret = get_readback_uint(&rb, 0, 0, 0);
ok(ret == 10, "Got unexpected result %#x.\n", ret);
ret = get_readback_uint(&rb, 64, 0, 0);
ok(ret == 50, "Got unexpected result %#x.\n", ret);
release_resource_readback(&rb);
ID3D12Resource_Release(uav);
ID3D12CommandSignature_Release(command_signature);
ID3D12Resource_Release(argument_buffer);
destroy_test_context(&context);
}
void test_unaligned_vertex_stride(void)
{
ID3D12PipelineState *instance_pipeline_state;
ID3D12GraphicsCommandList *command_list;
D3D12_INPUT_LAYOUT_DESC input_layout;
D3D12_VERTEX_BUFFER_VIEW vbv[2];
struct test_context_desc desc;
struct test_context context;
ID3D12CommandQueue *queue;
ID3D12Resource *vb[2];
unsigned int i;
static const D3D12_INPUT_ELEMENT_DESC layout_desc[] =
{
{"sv_position", 0, DXGI_FORMAT_R16G16B16A16_SNORM, 0, D3D12_APPEND_ALIGNED_ELEMENT,
D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
{"color", 0, DXGI_FORMAT_R16G16B16A16_SNORM, 1, D3D12_APPEND_ALIGNED_ELEMENT,
D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
};
static const D3D12_INPUT_ELEMENT_DESC instance_layout_desc[] =
{
{"sv_position", 0, DXGI_FORMAT_R16G16B16A16_SNORM, 0, D3D12_APPEND_ALIGNED_ELEMENT,
D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
{"color", 0, DXGI_FORMAT_R16G16B16A16_SNORM, 1, D3D12_APPEND_ALIGNED_ELEMENT,
D3D12_INPUT_CLASSIFICATION_PER_INSTANCE_DATA, 0},
};
#include "shaders/command/headers/unaligned_vertex_stride_vs.h"
#include "shaders/command/headers/unaligned_vertex_stride_ps.h"
struct i16vec4
{
int16_t x, y, z, w;
};
struct unaligned_i16vec4
{
uint8_t blob[2 * 4 + 1];
};
#define I16_MIN -0x7fff
#define I16_MAX 0x7fff
static const struct i16vec4 positions[] =
{
{I16_MIN, I16_MIN, 0.0f, I16_MAX},
{I16_MIN, I16_MAX, 0.0f, I16_MAX},
{I16_MAX, I16_MIN, 0.0f, I16_MAX},
{I16_MAX, I16_MAX, 0.0f, I16_MAX},
};
static const struct i16vec4 colors[] =
{
{I16_MAX, 0, 0, 0},
{0, I16_MAX, 0, 0},
{0, 0, I16_MAX, 0},
{0, 0, 0, I16_MAX},
{0, 0, 0, I16_MAX},
{0, 0, I16_MAX, 0},
{0, I16_MAX, 0, 0},
{I16_MAX, 0, 0, 0},
};
static const float white[] = { 1.0f, 1.0f, 1.0f, 1.0f };
struct unaligned_i16vec4 unaligned_colors[ARRAY_SIZE(colors)];
for (i = 0; i < ARRAY_SIZE(colors); i++)
memcpy(&unaligned_colors[i], &colors[i], sizeof(*colors));
memset(&desc, 0, sizeof(desc));
desc.no_root_signature = true;
if (!init_test_context(&context, &desc))
return;
command_list = context.list;
queue = context.queue;
context.root_signature = create_empty_root_signature(context.device,
D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);
input_layout.pInputElementDescs = layout_desc;
input_layout.NumElements = ARRAY_SIZE(layout_desc);
context.pipeline_state = create_pipeline_state(context.device,
context.root_signature, context.render_target_desc.Format,
&unaligned_vertex_stride_vs_dxbc, &unaligned_vertex_stride_ps_dxbc, &input_layout);
input_layout.pInputElementDescs = instance_layout_desc;
input_layout.NumElements = ARRAY_SIZE(instance_layout_desc);
instance_pipeline_state = create_pipeline_state(context.device,
context.root_signature, context.render_target_desc.Format,
&unaligned_vertex_stride_vs_dxbc, &unaligned_vertex_stride_ps_dxbc, &input_layout);
memset(vbv, 0, sizeof(vbv));
vb[0] = create_upload_buffer(context.device, sizeof(positions), positions);
vbv[0].BufferLocation = ID3D12Resource_GetGPUVirtualAddress(vb[0]);
vbv[0].StrideInBytes = sizeof(*positions);
vbv[0].SizeInBytes = sizeof(positions);
vb[1] = create_upload_buffer(context.device, sizeof(unaligned_colors), unaligned_colors);
vbv[1].BufferLocation = ID3D12Resource_GetGPUVirtualAddress(vb[1]) + 2 * sizeof(*unaligned_colors);
vbv[1].StrideInBytes = sizeof(*unaligned_colors);
vbv[1].SizeInBytes = 4 * sizeof(*unaligned_colors);
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_TRIANGLESTRIP);
ID3D12GraphicsCommandList_IASetVertexBuffers(command_list, 0, ARRAY_SIZE(vbv), vbv);
ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
ID3D12GraphicsCommandList_DrawInstanced(command_list, 4, 4, 0, 0);
vbv[1].BufferLocation = ID3D12Resource_GetGPUVirtualAddress(vb[1]);
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, instance_pipeline_state);
ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
ID3D12GraphicsCommandList_IASetVertexBuffers(command_list, 0, ARRAY_SIZE(vbv), vbv);
ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
ID3D12GraphicsCommandList_DrawInstanced(command_list, 4, 4, 0, 0);
transition_resource_state(command_list, context.render_target,
D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
/* There is no one correct result. If we don't crash the GPU, we pass the test. */
check_sub_resource_uint(context.render_target, 0, queue, command_list, 0xff00ff00, 0xff);
ID3D12Resource_Release(vb[1]);
ID3D12Resource_Release(vb[0]);
ID3D12PipelineState_Release(instance_pipeline_state);
destroy_test_context(&context);
}
void test_zero_vertex_stride(void)
{
ID3D12PipelineState *instance_pipeline_state;
ID3D12GraphicsCommandList *command_list;
D3D12_INPUT_LAYOUT_DESC input_layout;
D3D12_VERTEX_BUFFER_VIEW vbv[2];
struct test_context_desc desc;
struct test_context context;
ID3D12CommandQueue *queue;
ID3D12Resource *vb[2];
static const D3D12_INPUT_ELEMENT_DESC layout_desc[] =
{
{"sv_position", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D12_APPEND_ALIGNED_ELEMENT,
D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
{"color", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 1, D3D12_APPEND_ALIGNED_ELEMENT,
D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
};
static const D3D12_INPUT_ELEMENT_DESC instance_layout_desc[] =
{
{"sv_position", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D12_APPEND_ALIGNED_ELEMENT,
D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
{"color", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 1, D3D12_APPEND_ALIGNED_ELEMENT,
D3D12_INPUT_CLASSIFICATION_PER_INSTANCE_DATA, 0},
};
#include "shaders/command/headers/unaligned_vertex_stride_vs.h"
#include "shaders/command/headers/unaligned_vertex_stride_ps.h"
static const struct vec4 positions[] =
{
{-1.0f, -1.0f, 0.0f, 1.0f},
{-1.0f, 1.0f, 0.0f, 1.0f},
{ 1.0f, -1.0f, 0.0f, 1.0f},
{ 1.0f, 1.0f, 0.0f, 1.0f},
};
static const struct vec4 colors[] =
{
{0.0f, 1.0f, 0.0f, 1.0f},
{1.0f, 0.0f, 0.0f, 1.0f},
{0.5f, 0.5f, 0.5f, 1.0f},
{1.0f, 0.0f, 1.0f, 1.0f},
{1.0f, 0.0f, 1.0f, 1.0f},
{1.0f, 0.0f, 1.0f, 1.0f},
{1.0f, 0.0f, 1.0f, 1.0f},
{1.0f, 0.0f, 1.0f, 1.0f},
{1.0f, 0.0f, 1.0f, 1.0f},
};
static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};
memset(&desc, 0, sizeof(desc));
desc.no_root_signature = true;
if (!init_test_context(&context, &desc))
return;
command_list = context.list;
queue = context.queue;
context.root_signature = create_empty_root_signature(context.device,
D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);
input_layout.pInputElementDescs = layout_desc;
input_layout.NumElements = ARRAY_SIZE(layout_desc);
context.pipeline_state = create_pipeline_state(context.device,
context.root_signature, context.render_target_desc.Format,
&unaligned_vertex_stride_vs_dxbc, &unaligned_vertex_stride_ps_dxbc, &input_layout);
input_layout.pInputElementDescs = instance_layout_desc;
input_layout.NumElements = ARRAY_SIZE(instance_layout_desc);
instance_pipeline_state = create_pipeline_state(context.device,
context.root_signature, context.render_target_desc.Format,
&unaligned_vertex_stride_vs_dxbc, &unaligned_vertex_stride_ps_dxbc, &input_layout);
memset(vbv, 0, sizeof(vbv));
vb[0] = create_upload_buffer(context.device, sizeof(positions), positions);
vbv[0].BufferLocation = ID3D12Resource_GetGPUVirtualAddress(vb[0]);
vbv[0].StrideInBytes = sizeof(*positions);
vbv[0].SizeInBytes = sizeof(positions);
vb[1] = create_upload_buffer(context.device, sizeof(colors), colors);
vbv[1].BufferLocation = ID3D12Resource_GetGPUVirtualAddress(vb[1]) + 2 * sizeof(*colors);
vbv[1].StrideInBytes = 0;
vbv[1].SizeInBytes = sizeof(colors);
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_TRIANGLESTRIP);
ID3D12GraphicsCommandList_IASetVertexBuffers(command_list, 0, ARRAY_SIZE(vbv), vbv);
ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
ID3D12GraphicsCommandList_DrawInstanced(command_list, 4, 4, 0, 0);
transition_resource_state(command_list, context.render_target,
D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
check_sub_resource_uint(context.render_target, 0, queue, command_list, 0xff808080, 2);
reset_command_list(command_list, context.allocator);
transition_resource_state(command_list, context.render_target,
D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);
vbv[1].BufferLocation = ID3D12Resource_GetGPUVirtualAddress(vb[1]);
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, instance_pipeline_state);
ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
ID3D12GraphicsCommandList_IASetVertexBuffers(command_list, 0, ARRAY_SIZE(vbv), vbv);
ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
ID3D12GraphicsCommandList_DrawInstanced(command_list, 4, 4, 0, 0);
transition_resource_state(command_list, context.render_target,
D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
check_sub_resource_uint(context.render_target, 0, queue, command_list, 0xff00ff00, 0);
ID3D12Resource_Release(vb[1]);
ID3D12Resource_Release(vb[0]);
ID3D12PipelineState_Release(instance_pipeline_state);
destroy_test_context(&context);
}
static void draw_thread_main(void *thread_data)
{
static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};
struct test_context *context = thread_data;
ID3D12GraphicsCommandList *command_list;
ID3D12CommandAllocator *allocator;
D3D12_CPU_DESCRIPTOR_HANDLE rtv;
ID3D12Resource *render_target;
ID3D12DescriptorHeap *heap;
ID3D12CommandQueue *queue;
ID3D12Device *device;
unsigned int i;
HRESULT hr;
queue = context->queue;
device = context->device;
heap = create_cpu_descriptor_heap(device, D3D12_DESCRIPTOR_HEAP_TYPE_RTV, 1);
rtv = get_cpu_descriptor_handle(context, heap, 0);
create_render_target(context, NULL, &render_target, &rtv);
hr = ID3D12Device_CreateCommandAllocator(device, D3D12_COMMAND_LIST_TYPE_DIRECT,
&IID_ID3D12CommandAllocator, (void **)&allocator);
ok(SUCCEEDED(hr), "Failed to create command allocator, hr %#x.\n", hr);
hr = ID3D12Device_CreateCommandList(device, 0, D3D12_COMMAND_LIST_TYPE_DIRECT,
allocator, NULL, &IID_ID3D12GraphicsCommandList, (void **)&command_list);
ok(SUCCEEDED(hr), "Failed to create command list, hr %#x.\n", hr);
for (i = 0; i < 100; ++i)
{
ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, rtv, white, 0, NULL);
ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &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_DrawInstanced(command_list, 3, 1, 0, 0);
transition_resource_state(command_list, render_target,
D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
check_sub_resource_uint(render_target, 0, queue, command_list, 0xff00ff00, 0);
reset_command_list(command_list, allocator);
transition_resource_state(command_list, render_target,
D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);
}
ID3D12DescriptorHeap_Release(heap);
ID3D12Resource_Release(render_target);
ID3D12CommandAllocator_Release(allocator);
ID3D12GraphicsCommandList_Release(command_list);
}
void test_multithread_command_queue_exec(void)
{
static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};
ID3D12GraphicsCommandList *command_list;
struct test_context context;
ID3D12CommandQueue *queue;
HANDLE threads[10];
unsigned int i;
if (!init_test_context(&context, NULL))
return;
command_list = context.list;
queue = context.queue;
for (i = 0; i < ARRAY_SIZE(threads); ++i)
{
threads[i] = create_thread(draw_thread_main, &context);
ok(threads[i], "Failed to create thread %u.\n", i);
}
for (i = 0; i < 100; ++i)
{
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_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);
check_sub_resource_uint(context.render_target, 0, queue, command_list, 0xff00ff00, 0);
reset_command_list(command_list, context.allocator);
transition_resource_state(command_list, context.render_target,
D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);
}
for (i = 0; i < ARRAY_SIZE(threads); ++i)
ok(join_thread(threads[i]), "Failed to join thread %u.\n", i);
destroy_test_context(&context);
}
void test_command_list_initial_pipeline_state(void)
{
static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};
ID3D12GraphicsCommandList *command_list;
ID3D12PipelineState *pipeline_state;
ID3D12CommandAllocator *allocator;
struct test_context context;
ID3D12CommandQueue *queue;
HRESULT hr;
#include "shaders/command/headers/command_list_initial_pipeline_state.h"
if (!init_test_context(&context, NULL))
return;
queue = context.queue;
pipeline_state = create_pipeline_state(context.device,
context.root_signature, context.render_target_desc.Format,
NULL, &command_list_initial_pipeline_state_dxbc, NULL);
hr = ID3D12Device_CreateCommandAllocator(context.device, D3D12_COMMAND_LIST_TYPE_DIRECT,
&IID_ID3D12CommandAllocator, (void **)&allocator);
ok(hr == S_OK, "Failed to create command allocator, hr %#x.\n", hr);
hr = ID3D12Device_CreateCommandList(context.device, 0, D3D12_COMMAND_LIST_TYPE_DIRECT,
allocator, pipeline_state, &IID_ID3D12GraphicsCommandList, (void **)&command_list);
ok(hr == S_OK, "Failed to create command list, hr %#x.\n", hr);
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_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
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);
check_sub_resource_uint(context.render_target, 0, queue, command_list, 0xff804000, 1);
hr = ID3D12CommandAllocator_Reset(allocator);
ok(hr == S_OK, "Failed to reset command allocator, hr %#x.\n", hr);
hr = ID3D12GraphicsCommandList_Reset(command_list, allocator, context.pipeline_state);
ok(hr == S_OK, "Failed to reset command list, hr %#x.\n", hr);
transition_resource_state(command_list, context.render_target,
D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);
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_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
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);
check_sub_resource_uint(context.render_target, 0, queue, command_list, 0xff00ff00, 0);
ID3D12CommandAllocator_Release(allocator);
ID3D12GraphicsCommandList_Release(command_list);
ID3D12PipelineState_Release(pipeline_state);
destroy_test_context(&context);
}
static void prepare_instanced_draw(struct test_context *context)
{
ID3D12GraphicsCommandList *command_list = context->list;
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);
}
void test_conditional_rendering(void)
{
D3D12_ROOT_SIGNATURE_DESC root_signature_desc;
ID3D12Resource *conditions, *upload_buffer;
ID3D12CommandSignature *command_signature;
ID3D12GraphicsCommandList *command_list;
D3D12_ROOT_PARAMETER root_parameters[2];
ID3D12Resource *texture, *texture_copy;
D3D12_RENDER_TARGET_VIEW_DESC rtv_desc;
D3D12_HEAP_PROPERTIES heap_properties;
ID3D12PipelineState *pipeline_state;
ID3D12RootSignature *root_signature;
D3D12_RESOURCE_DESC resource_desc;
struct test_context context;
ID3D12Resource *buffer, *cb;
struct resource_readback rb;
ID3D12CommandQueue *queue;
unsigned int i;
uint32_t value;
HRESULT hr;
static const uint64_t predicate_args[] = {0, 1, (uint64_t)1 << 32};
static const uint32_t r8g8b8a8_data[] = {0x28384858, 0x39495969};
static const D3D12_DRAW_ARGUMENTS draw_args = {3, 1, 0, 0};
static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};
static const float green[] = {0.0f, 1.0f, 0.0f, 1.0f};
static const float ms_color[] = {0.345f, 0.282f, 0.219f, 0.156f};
static const uint32_t init_value = 0xdeadbeef;
static const D3D12_SUBRESOURCE_DATA copy_data[] =
{
{&r8g8b8a8_data[0], sizeof(r8g8b8a8_data[0]), sizeof(r8g8b8a8_data[0])},
{&r8g8b8a8_data[1], sizeof(r8g8b8a8_data[1]), sizeof(r8g8b8a8_data[1])}
};
#include "shaders/command/headers/conditional_rendering.h"
static const struct
{
uint32_t offset;
uint32_t value;
uint32_t uav_offset;
}
input = {0, 4, 0};
if (!init_test_context(&context, NULL))
return;
command_list = context.list;
queue = context.queue;
if (is_intel_windows_device(context.device))
{
skip("Predicated rendering is broken on Intel.\n");
destroy_test_context(&context);
return;
}
conditions = create_default_buffer(context.device, sizeof(predicate_args),
D3D12_RESOURCE_FLAG_NONE, D3D12_RESOURCE_STATE_COPY_DEST);
upload_buffer_data(conditions, 0, sizeof(predicate_args), &predicate_args, queue, command_list);
reset_command_list(command_list, context.allocator);
transition_resource_state(command_list, conditions,
D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_PREDICATION);
ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);
/* Skip draw on zero. */
prepare_instanced_draw(&context);
ID3D12GraphicsCommandList_SetPredication(command_list, conditions, 0, D3D12_PREDICATION_OP_EQUAL_ZERO);
ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);
ID3D12GraphicsCommandList_SetPredication(command_list, NULL, 0, 0);
transition_resource_state(command_list, context.render_target,
D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
check_sub_resource_uint(context.render_target, 0, queue, command_list, 0xffffffff, 0);
reset_command_list(command_list, context.allocator);
transition_resource_state(command_list, context.render_target,
D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);
/* Skip draw on non-zero. */
prepare_instanced_draw(&context);
ID3D12GraphicsCommandList_SetPredication(command_list, conditions,
sizeof(uint64_t), D3D12_PREDICATION_OP_NOT_EQUAL_ZERO);
ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);
/* Don't reset predication to test automatic reset on next SetPredication() call. */
transition_resource_state(command_list, context.render_target,
D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
ID3D12GraphicsCommandList_SetPredication(command_list, conditions,
sizeof(uint64_t), D3D12_PREDICATION_OP_EQUAL_ZERO);
check_sub_resource_uint(context.render_target, 0, queue, command_list, 0xffffffff, 0);
reset_command_list(command_list, context.allocator);
transition_resource_state(command_list, context.render_target,
D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);
/* Skip clear on zero. */
ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);
ID3D12GraphicsCommandList_SetPredication(command_list, conditions, 0, D3D12_PREDICATION_OP_EQUAL_ZERO);
ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, green, 0, NULL);
ID3D12GraphicsCommandList_SetPredication(command_list, NULL, 0, 0);
transition_resource_state(command_list, context.render_target,
D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
get_texture_readback_with_command_list(context.render_target, 0, &rb, queue, command_list);
todo check_readback_data_uint(&rb, NULL, 0xffffffff, 0);
release_resource_readback(&rb);
reset_command_list(command_list, context.allocator);
transition_resource_state(command_list, context.render_target,
D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);
/* Draw on zero. */
ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);
prepare_instanced_draw(&context);
ID3D12GraphicsCommandList_SetPredication(command_list, conditions, 0, D3D12_PREDICATION_OP_NOT_EQUAL_ZERO);
ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);
ID3D12GraphicsCommandList_SetPredication(command_list, NULL, 0, 0);
transition_resource_state(command_list, context.render_target,
D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
check_sub_resource_uint(context.render_target, 0, queue, command_list, 0xff00ff00, 0);
reset_command_list(command_list, context.allocator);
transition_resource_state(command_list, context.render_target,
D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);
/* Draw on non-zero. */
ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);
prepare_instanced_draw(&context);
ID3D12GraphicsCommandList_SetPredication(command_list, conditions,
sizeof(uint64_t), D3D12_PREDICATION_OP_EQUAL_ZERO);
ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);
ID3D12GraphicsCommandList_SetPredication(command_list, NULL, 0, 0);
transition_resource_state(command_list, context.render_target,
D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
check_sub_resource_uint(context.render_target, 0, queue, command_list, 0xff00ff00, 0);
reset_command_list(command_list, context.allocator);
transition_resource_state(command_list, context.render_target,
D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);
/* 64-bit conditional 0x100000000 */
ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);
prepare_instanced_draw(&context);
ID3D12GraphicsCommandList_SetPredication(command_list, conditions,
2 * sizeof(uint64_t), D3D12_PREDICATION_OP_NOT_EQUAL_ZERO);
ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);
ID3D12GraphicsCommandList_SetPredication(command_list, NULL, 0, 0);
transition_resource_state(command_list, context.render_target,
D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
get_texture_readback_with_command_list(context.render_target, 0, &rb, queue, command_list);
check_readback_data_uint(&rb, NULL, 0xffffffff, 0);
release_resource_readback(&rb);
reset_command_list(command_list, context.allocator);
transition_resource_state(command_list, context.render_target,
D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);
/* Direct3D latches the value of the predicate upon beginning predicated rendering. */
buffer = create_default_buffer(context.device, sizeof(predicate_args),
D3D12_RESOURCE_FLAG_NONE, D3D12_RESOURCE_STATE_COPY_DEST);
transition_resource_state(command_list, conditions,
D3D12_RESOURCE_STATE_PREDICATION, D3D12_RESOURCE_STATE_COPY_SOURCE);
ID3D12GraphicsCommandList_CopyResource(command_list, buffer, conditions);
transition_resource_state(command_list,
buffer, D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_PREDICATION);
ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);
prepare_instanced_draw(&context);
ID3D12GraphicsCommandList_SetPredication(command_list, buffer, 0, D3D12_PREDICATION_OP_NOT_EQUAL_ZERO);
transition_resource_state(command_list, buffer,
D3D12_RESOURCE_STATE_PREDICATION, D3D12_RESOURCE_STATE_COPY_DEST);
ID3D12GraphicsCommandList_CopyBufferRegion(command_list, buffer, 0, conditions, sizeof(uint64_t), sizeof(uint64_t));
transition_resource_state(command_list,
buffer, D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_PREDICATION);
transition_resource_state(command_list,
conditions, D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_PREDICATION);
ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);
ID3D12GraphicsCommandList_SetPredication(command_list, NULL, 0, 0);
transition_resource_state(command_list, context.render_target,
D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
check_sub_resource_uint(context.render_target, 0, queue, command_list, 0xff00ff00, 0);
ID3D12Resource_Release(buffer);
reset_command_list(command_list, context.allocator);
transition_resource_state(command_list, context.render_target,
D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);
/* SetPredication() and upload buffer. */
upload_buffer = create_upload_buffer(context.device, sizeof(predicate_args), predicate_args);
ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);
prepare_instanced_draw(&context);
/* Skip. */
ID3D12GraphicsCommandList_SetPredication(command_list, upload_buffer,
0, D3D12_PREDICATION_OP_EQUAL_ZERO);
ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);
ID3D12GraphicsCommandList_SetPredication(command_list, upload_buffer,
0, D3D12_PREDICATION_OP_NOT_EQUAL_ZERO);
transition_resource_state(command_list, context.render_target,
D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
check_sub_resource_uint(context.render_target, 0, queue, command_list, 0xffffffff, 0);
reset_command_list(command_list, context.allocator);
transition_resource_state(command_list, context.render_target,
D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);
/* ExecuteIndirect(). */
buffer = create_upload_buffer(context.device, sizeof(draw_args), &draw_args);
command_signature = create_command_signature(context.device, D3D12_INDIRECT_ARGUMENT_TYPE_DRAW);
ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);
prepare_instanced_draw(&context);
/* Skip. */
ID3D12GraphicsCommandList_SetPredication(command_list, conditions, 0, D3D12_PREDICATION_OP_EQUAL_ZERO);
ID3D12GraphicsCommandList_ExecuteIndirect(command_list, command_signature, 1, buffer, 0, NULL, 0);
ID3D12GraphicsCommandList_SetPredication(command_list, NULL, 0, 0);
transition_resource_state(command_list, context.render_target,
D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
check_sub_resource_uint(context.render_target, 0, queue, command_list, 0xffffffff, 0);
reset_command_list(command_list, context.allocator);
transition_resource_state(command_list, context.render_target,
D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);
prepare_instanced_draw(&context);
/* Draw. */
ID3D12GraphicsCommandList_SetPredication(command_list, conditions, 0, D3D12_PREDICATION_OP_NOT_EQUAL_ZERO);
ID3D12GraphicsCommandList_ExecuteIndirect(command_list, command_signature, 1, buffer, 0, NULL, 0);
ID3D12GraphicsCommandList_SetPredication(command_list, NULL, 0, 0);
transition_resource_state(command_list, context.render_target,
D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
check_sub_resource_uint(context.render_target, 0, queue, command_list, 0xff00ff00, 0);
ID3D12Resource_Release(buffer);
ID3D12CommandSignature_Release(command_signature);
reset_command_list(command_list, context.allocator);
/* CopyResource(). */
texture = create_default_texture(context.device,
1, 1, DXGI_FORMAT_R8G8B8A8_UNORM, 0, D3D12_RESOURCE_STATE_COPY_DEST);
upload_texture_data(texture, &copy_data[0], 1, queue, command_list);
reset_command_list(command_list, context.allocator);
transition_resource_state(command_list, texture,
D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_COPY_SOURCE);
texture_copy = create_default_texture(context.device,
1, 1, DXGI_FORMAT_R8G8B8A8_UNORM, 0, D3D12_RESOURCE_STATE_COPY_DEST);
upload_texture_data(texture_copy, &copy_data[1], 1, queue, command_list);
reset_command_list(command_list, context.allocator);
/* Skip. */
ID3D12GraphicsCommandList_SetPredication(command_list, conditions, 0, D3D12_PREDICATION_OP_EQUAL_ZERO);
ID3D12GraphicsCommandList_CopyResource(command_list, texture_copy, texture);
ID3D12GraphicsCommandList_SetPredication(command_list, NULL, 0, 0);
transition_resource_state(command_list, texture_copy,
D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_COPY_SOURCE);
get_texture_readback_with_command_list(texture_copy, 0, &rb, queue, command_list);
todo check_readback_data_uint(&rb, NULL, r8g8b8a8_data[1], 0);
release_resource_readback(&rb);
reset_command_list(command_list, context.allocator);
transition_resource_state(command_list, texture_copy,
D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_COPY_DEST);
/* Copy. */
ID3D12GraphicsCommandList_SetPredication(command_list, conditions, 0, D3D12_PREDICATION_OP_NOT_EQUAL_ZERO);
ID3D12GraphicsCommandList_CopyResource(command_list, texture_copy, texture);
ID3D12GraphicsCommandList_SetPredication(command_list, NULL, 0, 0);
transition_resource_state(command_list, texture_copy,
D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_COPY_SOURCE);
check_sub_resource_uint(texture_copy, 0, queue, command_list, r8g8b8a8_data[0], 0);
/* Multisample texture. */
ID3D12Resource_Release(texture);
memset(&heap_properties, 0, sizeof(heap_properties));
heap_properties.Type = D3D12_HEAP_TYPE_DEFAULT;
memset(&resource_desc, 0, sizeof(resource_desc));
resource_desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
resource_desc.Width = 1;
resource_desc.Height = 1;
resource_desc.DepthOrArraySize = 1;
resource_desc.MipLevels = 1;
resource_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
resource_desc.SampleDesc.Count = 4;
resource_desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;
hr = ID3D12Device_CreateCommittedResource(context.device, &heap_properties, D3D12_HEAP_FLAG_NONE,
&resource_desc, D3D12_RESOURCE_STATE_RENDER_TARGET, NULL, &IID_ID3D12Resource, (void **)&texture);
ok(hr == S_OK, "Failed to create texture, hr %#x.\n", hr);
memset(&rtv_desc, 0, sizeof(rtv_desc));
rtv_desc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DMS;
rtv_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
ID3D12Device_CreateRenderTargetView(context.device, texture, &rtv_desc,
get_cpu_rtv_handle(&context, context.rtv_heap, 0));
reset_command_list(command_list, context.allocator);
ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, ms_color, 0, NULL);
/* ResolveSubresource(). */
transition_resource_state(command_list, texture_copy,
D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_COPY_DEST);
upload_texture_data(texture_copy, &copy_data[1], 1, queue, command_list);
reset_command_list(command_list, context.allocator);
transition_resource_state(command_list, texture_copy,
D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_RESOLVE_DEST);
transition_resource_state(command_list, texture,
D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_RESOLVE_SOURCE);
/* Skip. */
ID3D12GraphicsCommandList_SetPredication(command_list, conditions, 0, D3D12_PREDICATION_OP_EQUAL_ZERO);
ID3D12GraphicsCommandList_ResolveSubresource(command_list,
texture_copy, 0, texture, 0, DXGI_FORMAT_R8G8B8A8_UNORM);
ID3D12GraphicsCommandList_SetPredication(command_list, NULL, 0, 0);
transition_resource_state(command_list, texture_copy,
D3D12_RESOURCE_STATE_RESOLVE_DEST, D3D12_RESOURCE_STATE_COPY_SOURCE);
get_texture_readback_with_command_list(texture_copy, 0, &rb, queue, command_list);
todo check_readback_data_uint(&rb, NULL, r8g8b8a8_data[1], 0);
release_resource_readback(&rb);
reset_command_list(command_list, context.allocator);
transition_resource_state(command_list, texture_copy,
D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RESOLVE_DEST);
/* Resolve. */
ID3D12GraphicsCommandList_SetPredication(command_list, conditions, 0, D3D12_PREDICATION_OP_NOT_EQUAL_ZERO);
ID3D12GraphicsCommandList_ResolveSubresource(command_list,
texture_copy, 0, texture, 0, DXGI_FORMAT_R8G8B8A8_UNORM);
ID3D12GraphicsCommandList_SetPredication(command_list, NULL, 0, 0);
transition_resource_state(command_list, texture_copy,
D3D12_RESOURCE_STATE_RESOLVE_DEST, D3D12_RESOURCE_STATE_COPY_SOURCE);
check_sub_resource_uint(texture_copy, 0, queue, command_list, r8g8b8a8_data[0], 2);
reset_command_list(command_list, context.allocator);
/* Dispatch(). */
cb = create_upload_buffer(context.device, sizeof(input), &input);
buffer = create_default_buffer(context.device, 512,
D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_DEST);
upload_buffer_data(buffer, 0, sizeof(init_value), &init_value, queue, command_list);
reset_command_list(command_list, context.allocator);
transition_sub_resource_state(command_list, buffer, 0,
D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
root_parameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
root_parameters[0].Descriptor.ShaderRegister = 0;
root_parameters[0].Descriptor.RegisterSpace = 0;
root_parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
root_parameters[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_UAV;
root_parameters[1].Descriptor.ShaderRegister = 0;
root_parameters[1].Descriptor.RegisterSpace = 0;
root_parameters[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
memset(&root_signature_desc, 0, sizeof(root_signature_desc));
root_signature_desc.NumParameters = 2;
root_signature_desc.pParameters = root_parameters;
hr = create_root_signature(context.device, &root_signature_desc, &root_signature);
ok(hr == S_OK, "Failed to create root signature, hr %#x.\n", hr);
pipeline_state = create_compute_pipeline_state(context.device, root_signature, conditional_rendering_dxbc);
for (i = 0; i < 2; ++i)
{
ID3D12GraphicsCommandList_SetPipelineState(command_list, pipeline_state);
ID3D12GraphicsCommandList_SetComputeRootSignature(command_list, root_signature);
ID3D12GraphicsCommandList_SetComputeRootConstantBufferView(command_list,
0, ID3D12Resource_GetGPUVirtualAddress(cb));
ID3D12GraphicsCommandList_SetComputeRootUnorderedAccessView(command_list,
1, ID3D12Resource_GetGPUVirtualAddress(buffer));
ID3D12GraphicsCommandList_SetPredication(command_list, conditions, i * sizeof(uint64_t),
D3D12_PREDICATION_OP_EQUAL_ZERO);
ID3D12GraphicsCommandList_Dispatch(command_list, 1, 1, 1);
ID3D12GraphicsCommandList_SetPredication(command_list, NULL, 0, 0);
transition_sub_resource_state(command_list, buffer, 0,
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 == (!i ? init_value : input.value), "Got %#x, expected %#x.\n", value, input.value);
release_resource_readback(&rb);
reset_command_list(command_list, context.allocator);
transition_sub_resource_state(command_list, buffer, 0,
D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
}
ID3D12Resource_Release(texture);
ID3D12Resource_Release(texture_copy);
ID3D12Resource_Release(conditions);
ID3D12Resource_Release(cb);
ID3D12Resource_Release(buffer);
ID3D12Resource_Release(upload_buffer);
ID3D12RootSignature_Release(root_signature);
ID3D12PipelineState_Release(pipeline_state);
destroy_test_context(&context);
}
void test_write_buffer_immediate(void)
{
D3D12_WRITEBUFFERIMMEDIATE_PARAMETER parameters[3];
ID3D12GraphicsCommandList2 *command_list2;
D3D12_WRITEBUFFERIMMEDIATE_MODE modes[3];
ID3D12GraphicsCommandList *command_list;
struct resource_readback rb;
struct test_context context;
ID3D12CommandQueue *queue;
ID3D12Resource *buffer;
ID3D12Device *device;
unsigned int value;
HRESULT hr;
static const unsigned int data_values[] = {0xdeadbeef, 0xf00baa, 0xdeadbeef, 0xf00baa};
if (!init_test_context(&context, NULL))
return;
device = context.device;
command_list = context.list;
queue = context.queue;
if (FAILED(hr = ID3D12GraphicsCommandList_QueryInterface(command_list,
&IID_ID3D12GraphicsCommandList2, (void **)&command_list2)))
{
skip("ID3D12GraphicsCommandList2 not implemented.\n");
destroy_test_context(&context);
return;
}
buffer = create_default_buffer(device, sizeof(data_values),
D3D12_RESOURCE_FLAG_NONE, D3D12_RESOURCE_STATE_COPY_DEST);
upload_buffer_data(buffer, 0, sizeof(data_values), data_values, queue, command_list);
reset_command_list(command_list, context.allocator);
parameters[0].Dest = ID3D12Resource_GetGPUVirtualAddress(buffer);
parameters[0].Value = 0x1020304;
parameters[1].Dest = parameters[0].Dest + sizeof(data_values[0]);
parameters[1].Value = 0xc0d0e0f;
parameters[2].Dest = parameters[0].Dest + sizeof(data_values[0]) * 3;
parameters[2].Value = 0x5060708;
ID3D12GraphicsCommandList2_WriteBufferImmediate(command_list2, ARRAY_SIZE(parameters), parameters, NULL);
hr = ID3D12GraphicsCommandList_Close(command_list);
ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
exec_command_list(queue, command_list);
wait_queue_idle(device, queue);
reset_command_list(command_list, context.allocator);
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 == parameters[0].Value, "Got unexpected value %#x, expected %#x.\n", value, parameters[0].Value);
value = get_readback_uint(&rb, 1, 0, 0);
ok(value == parameters[1].Value, "Got unexpected value %#x, expected %#x.\n", value, parameters[1].Value);
value = get_readback_uint(&rb, 2, 0, 0);
ok(value == data_values[2], "Got unexpected value %#x, expected %#x.\n", value, data_values[2]);
value = get_readback_uint(&rb, 3, 0, 0);
ok(value == parameters[2].Value, "Got unexpected value %#x, expected %#x.\n", value, parameters[2].Value);
release_resource_readback(&rb);
reset_command_list(command_list, context.allocator);
parameters[0].Value = 0x2030405;
parameters[1].Value = 0xb0c0d0e;
parameters[2].Value = 0x708090a;
modes[0] = D3D12_WRITEBUFFERIMMEDIATE_MODE_DEFAULT;
modes[1] = D3D12_WRITEBUFFERIMMEDIATE_MODE_MARKER_IN;
modes[2] = D3D12_WRITEBUFFERIMMEDIATE_MODE_MARKER_OUT;
ID3D12GraphicsCommandList2_WriteBufferImmediate(command_list2, ARRAY_SIZE(parameters), parameters, modes);
hr = ID3D12GraphicsCommandList_Close(command_list);
ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
exec_command_list(queue, command_list);
wait_queue_idle(device, queue);
reset_command_list(command_list, context.allocator);
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 == parameters[0].Value, "Got unexpected value %#x, expected %#x.\n", value, parameters[0].Value);
value = get_readback_uint(&rb, 1, 0, 0);
ok(value == parameters[1].Value, "Got unexpected value %#x, expected %#x.\n", value, parameters[1].Value);
value = get_readback_uint(&rb, 3, 0, 0);
ok(value == parameters[2].Value, "Got unexpected value %#x, expected %#x.\n", value, parameters[2].Value);
release_resource_readback(&rb);
reset_command_list(command_list, context.allocator);
modes[0] = 0x7fffffff;
ID3D12GraphicsCommandList2_WriteBufferImmediate(command_list2, ARRAY_SIZE(parameters), parameters, modes);
hr = ID3D12GraphicsCommandList_Close(command_list);
ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);
ID3D12Resource_Release(buffer);
ID3D12GraphicsCommandList2_Release(command_list2);
destroy_test_context(&context);
}
void test_aliasing_barrier(void)
{
/* This test mostly serves to verify that validation is clean,
* and that we don't crash on weird inputs. There is no particular output we expect to see. */
ID3D12GraphicsCommandList *command_list;
D3D12_FEATURE_DATA_D3D12_OPTIONS opts;
D3D12_RESOURCE_BARRIER barriers[256];
ID3D12Resource *placed_textures[3];
ID3D12Resource *committed_texture;
ID3D12Resource *placed_buffers[3];
D3D12_RESOURCE_DESC texture_desc;
ID3D12Resource *committed_buffer;
struct test_context_desc desc;
struct test_context context;
D3D12_HEAP_DESC heap_desc;
bool supports_heap_tier_2;
ID3D12Heap *buffer_heap;
ID3D12Heap *texture_heap;
ID3D12Heap *common_heap;
ID3D12Device *device;
unsigned int i;
HRESULT hr;
memset(&desc, 0, sizeof(desc));
desc.no_render_target = true;
if (!init_test_context(&context, &desc))
return;
device = context.device;
command_list = context.list;
committed_texture = create_default_texture(context.device, 1, 1, DXGI_FORMAT_R32_SINT,
D3D12_RESOURCE_FLAG_NONE, D3D12_RESOURCE_STATE_COMMON);
committed_buffer = create_default_buffer(device, 1,
D3D12_RESOURCE_FLAG_NONE, D3D12_RESOURCE_STATE_COMMON);
ID3D12Device_CheckFeatureSupport(device, D3D12_FEATURE_D3D12_OPTIONS, &opts, sizeof(opts));
supports_heap_tier_2 = opts.ResourceHeapTier >= D3D12_RESOURCE_HEAP_TIER_2;
memset(&heap_desc, 0, sizeof(heap_desc));
heap_desc.Alignment = D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT;
heap_desc.Properties.Type = D3D12_HEAP_TYPE_DEFAULT;
heap_desc.SizeInBytes = 1024 * 1024;
heap_desc.Flags = D3D12_HEAP_FLAG_ALLOW_ONLY_BUFFERS;
hr = ID3D12Device_CreateHeap(device, &heap_desc, &IID_ID3D12Heap, (void**)&buffer_heap);
ok(hr == S_OK, "Failed to create buffer heap hr #%u.\n", hr);
heap_desc.Flags = D3D12_HEAP_FLAG_ALLOW_ONLY_NON_RT_DS_TEXTURES;
hr = ID3D12Device_CreateHeap(device, &heap_desc, &IID_ID3D12Heap, (void **)&texture_heap);
ok(hr == S_OK, "Failed to create buffer heap hr #%u.\n", hr);
if (supports_heap_tier_2)
{
heap_desc.Flags = D3D12_HEAP_FLAG_ALLOW_ALL_BUFFERS_AND_TEXTURES;
hr = ID3D12Device_CreateHeap(device, &heap_desc, &IID_ID3D12Heap, (void **)&common_heap);
ok(hr == S_OK, "Failed to create buffer heap hr #%u.\n", hr);
}
else
common_heap = NULL;
texture_desc.Format = DXGI_FORMAT_R32_SINT;
texture_desc.Alignment = D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT;
texture_desc.Width = 1;
texture_desc.Height = 1;
texture_desc.DepthOrArraySize = 1;
texture_desc.MipLevels = 1;
texture_desc.SampleDesc.Count = 1;
texture_desc.SampleDesc.Quality = 0;
texture_desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
texture_desc.Flags = D3D12_RESOURCE_FLAG_NONE;
texture_desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
for (i = 0; i < 2; i++)
{
placed_buffers[i] = create_placed_buffer(device, buffer_heap, 0, 1, D3D12_RESOURCE_FLAG_NONE, D3D12_RESOURCE_STATE_COMMON);
hr = ID3D12Device_CreatePlacedResource(device, texture_heap, 0, &texture_desc, D3D12_RESOURCE_STATE_COMMON, NULL, &IID_ID3D12Resource, (void **)&placed_textures[i]);
ok(hr == S_OK, "Failed to create placed resource. hr = #%u.\n", hr);
}
placed_buffers[2] = create_placed_buffer(device, supports_heap_tier_2 ? common_heap : buffer_heap, 0, 1, D3D12_RESOURCE_FLAG_NONE, D3D12_RESOURCE_STATE_COMMON);
hr = ID3D12Device_CreatePlacedResource(device, supports_heap_tier_2 ? common_heap : texture_heap, 0, &texture_desc, D3D12_RESOURCE_STATE_COMMON, NULL, &IID_ID3D12Resource, (void **)&placed_textures[2]);
ok(hr == S_OK, "Failed to create placed resource. hr = #%u.\n", hr);
for (i = 0; i < ARRAY_SIZE(barriers); i++)
{
barriers[i].Type = D3D12_RESOURCE_BARRIER_TYPE_ALIASING;
barriers[i].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
}
/* Full barrier */
barriers[0].Aliasing.pResourceBefore = NULL;
barriers[0].Aliasing.pResourceAfter = NULL;
ID3D12GraphicsCommandList_ResourceBarrier(command_list, 1, barriers);
/* NULL to buffer */
barriers[0].Aliasing.pResourceBefore = NULL;
barriers[0].Aliasing.pResourceAfter = placed_buffers[0];
ID3D12GraphicsCommandList_ResourceBarrier(command_list, 1, barriers);
/* NULL to image */
barriers[0].Aliasing.pResourceBefore = NULL;
barriers[0].Aliasing.pResourceAfter = placed_textures[0];
ID3D12GraphicsCommandList_ResourceBarrier(command_list, 1, barriers);
/* buffer to NULL */
barriers[0].Aliasing.pResourceBefore = placed_buffers[0];
barriers[0].Aliasing.pResourceAfter = NULL;
ID3D12GraphicsCommandList_ResourceBarrier(command_list, 1, barriers);
/* NULL to image */
barriers[0].Aliasing.pResourceBefore = placed_textures[0];
barriers[0].Aliasing.pResourceAfter = NULL;
ID3D12GraphicsCommandList_ResourceBarrier(command_list, 1, barriers);
/* buffer to buffer */
barriers[0].Aliasing.pResourceBefore = placed_buffers[0];
barriers[0].Aliasing.pResourceAfter = placed_buffers[1];
ID3D12GraphicsCommandList_ResourceBarrier(command_list, 1, barriers);
/* image to image */
barriers[0].Aliasing.pResourceBefore = placed_textures[0];
barriers[0].Aliasing.pResourceAfter = placed_textures[1];
ID3D12GraphicsCommandList_ResourceBarrier(command_list, 1, barriers);
/* buffer to image */
if (supports_heap_tier_2)
{
barriers[0].Aliasing.pResourceBefore = placed_buffers[2];
barriers[0].Aliasing.pResourceAfter = placed_textures[2];
ID3D12GraphicsCommandList_ResourceBarrier(command_list, 1, barriers);
}
/* Test spamming lots of redundant image barriers. */
for (i = 0; i < ARRAY_SIZE(barriers); i++)
{
barriers[i].Aliasing.pResourceBefore = NULL;
barriers[i].Aliasing.pResourceAfter = placed_textures[i % 3];
}
ID3D12GraphicsCommandList_ResourceBarrier(command_list, ARRAY_SIZE(barriers), barriers);
ID3D12Resource_Release(committed_texture);
ID3D12Resource_Release(committed_buffer);
for (i = 0; i < 3; i++)
{
ID3D12Resource_Release(placed_textures[i]);
ID3D12Resource_Release(placed_buffers[i]);
}
ID3D12Heap_Release(buffer_heap);
ID3D12Heap_Release(texture_heap);
if (common_heap)
ID3D12Heap_Release(common_heap);
destroy_test_context(&context);
}
static void test_discard_resource_uav_type(bool compute_queue)
{
static const float white[] = { 1.0f, 1.0f, 1.0f, 1.0f };
D3D12_UNORDERED_ACCESS_VIEW_DESC uav;
struct test_context context;
ID3D12DescriptorHeap *gpu;
ID3D12DescriptorHeap *cpu;
ID3D12Resource *resource;
if (compute_queue)
{
/* Creates a COMPUTE list instead of DIRECT. */
if (!init_compute_test_context(&context))
return;
}
else
{
if (!init_test_context(&context, NULL))
return;
}
/* In compute lists, we can discard UAV enabled resources,
* and the resource must be in UAV state. */
resource = create_default_texture2d(context.device, 4, 4, 1, 1, DXGI_FORMAT_R32_FLOAT,
D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS,
D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
gpu = create_gpu_descriptor_heap(context.device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 1);
cpu = create_cpu_descriptor_heap(context.device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 1);
memset(&uav, 0, sizeof(uav));
uav.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2D;
uav.Format = DXGI_FORMAT_R32_FLOAT;
ID3D12Device_CreateUnorderedAccessView(context.device, resource, NULL, &uav,
ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(gpu));
ID3D12Device_CreateUnorderedAccessView(context.device, resource, NULL, &uav,
ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(cpu));
ID3D12GraphicsCommandList_DiscardResource(context.list, resource, NULL);
ID3D12GraphicsCommandList_ClearUnorderedAccessViewFloat(context.list,
ID3D12DescriptorHeap_GetGPUDescriptorHandleForHeapStart(gpu),
ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(cpu),
resource, white, 0, NULL);
transition_resource_state(context.list, resource,
D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);
check_sub_resource_float(resource, 0, context.queue, context.list, 1.0f, 0);
ID3D12DescriptorHeap_Release(gpu);
ID3D12DescriptorHeap_Release(cpu);
ID3D12Resource_Release(resource);
destroy_test_context(&context);
}
void test_discard_resource_uav(void)
{
vkd3d_test_set_context("Test graphics");
test_discard_resource_uav_type(false);
vkd3d_test_set_context("Test compute");
test_discard_resource_uav_type(true);
}
void test_discard_resource(void)
{
ID3D12GraphicsCommandList *command_list;
D3D12_HEAP_PROPERTIES heap_properties;
D3D12_CPU_DESCRIPTOR_HANDLE rtv, dsv;
D3D12_RESOURCE_DESC resource_desc;
ID3D12DescriptorHeap *rtv_heap;
ID3D12DescriptorHeap *dsv_heap;
D3D12_DISCARD_REGION ds_region;
D3D12_DISCARD_REGION region;
struct test_context context;
ID3D12Resource *tmp_depth;
ID3D12Resource *depth_rt;
ID3D12Device *device;
ID3D12Resource *rt;
HRESULT hr;
const float clear_color[] = { 1.0f, 0.0f, 0.0f, 0.0f };
if (!init_test_context(&context, NULL))
return;
device = context.device;
command_list = context.list;
memset(&heap_properties, 0, sizeof(heap_properties));
heap_properties.Type = D3D12_HEAP_TYPE_DEFAULT;
resource_desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
resource_desc.Alignment = 0;
resource_desc.Width = 16;
resource_desc.Height = 16;
resource_desc.DepthOrArraySize = 2;
resource_desc.MipLevels = 1;
resource_desc.SampleDesc.Count = 1;
resource_desc.SampleDesc.Quality = 0;
resource_desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
resource_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
resource_desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;
hr = ID3D12Device_CreateCommittedResource(device, &heap_properties, D3D12_HEAP_FLAG_NONE,
&resource_desc, D3D12_RESOURCE_STATE_RENDER_TARGET, NULL, &IID_ID3D12Resource, (void **)&rt);
ok(SUCCEEDED(hr), "Failed to create texture, hr %#x.\n", hr);
resource_desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;
resource_desc.Format = DXGI_FORMAT_D32_FLOAT_S8X24_UINT;
hr = ID3D12Device_CreateCommittedResource(device, &heap_properties, D3D12_HEAP_FLAG_NONE,
&resource_desc, D3D12_RESOURCE_STATE_DEPTH_WRITE, NULL, &IID_ID3D12Resource, (void **)&depth_rt);
ok(SUCCEEDED(hr), "Failed to create texture, hr %#x.\n", hr);
resource_desc.Flags = D3D12_RESOURCE_FLAG_NONE;
resource_desc.Format = DXGI_FORMAT_R32_FLOAT;
resource_desc.DepthOrArraySize = 1;
hr = ID3D12Device_CreateCommittedResource(device, &heap_properties, D3D12_HEAP_FLAG_NONE,
&resource_desc, D3D12_RESOURCE_STATE_COPY_DEST, NULL, &IID_ID3D12Resource, (void **)&tmp_depth);
ok(SUCCEEDED(hr), "Failed to create texture, hr %#x.\n", hr);
region.NumRects = 0;
region.pRects = NULL;
region.FirstSubresource = 0;
region.NumSubresources = 2;
ds_region = region;
ds_region.NumSubresources = 4;
ID3D12GraphicsCommandList_DiscardResource(context.list, rt, &region);
ID3D12GraphicsCommandList_DiscardResource(context.list, depth_rt, &ds_region);
rtv_heap = create_cpu_descriptor_heap(device, D3D12_DESCRIPTOR_HEAP_TYPE_RTV, 1);
dsv_heap = create_cpu_descriptor_heap(device, D3D12_DESCRIPTOR_HEAP_TYPE_DSV, 1);
rtv = ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(rtv_heap);
dsv = ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(dsv_heap);
ID3D12Device_CreateRenderTargetView(device, rt, NULL, rtv);
ID3D12Device_CreateDepthStencilView(device, depth_rt, NULL, dsv);
ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &rtv, false, &dsv);
ID3D12GraphicsCommandList_DiscardResource(context.list, rt, &region);
ID3D12GraphicsCommandList_DiscardResource(context.list, depth_rt, &ds_region);
/* Just make sure we don't have validation errors */
hr = ID3D12GraphicsCommandList_Close(context.list);
ok(hr == S_OK, "Failed to close command list, hr %#x.\n", hr);
hr = ID3D12GraphicsCommandList_Reset(context.list, context.allocator, NULL);
ok(hr == S_OK, "Failed to reset command list, hr %#x.\n", hr);
ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &rtv, false, &dsv);
ID3D12GraphicsCommandList_DiscardResource(context.list, rt, &region);
ID3D12GraphicsCommandList_DiscardResource(context.list, depth_rt, &ds_region);
ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, rtv, clear_color, 0, NULL);
ID3D12GraphicsCommandList_ClearDepthStencilView(command_list, dsv, D3D12_CLEAR_FLAG_DEPTH | D3D12_CLEAR_FLAG_STENCIL, 1.0f, 128, 0, NULL);
region.FirstSubresource = 1;
region.NumSubresources = 1;
ID3D12GraphicsCommandList_DiscardResource(context.list, rt, &region);
/* Discard stencil aspect and mip 1 of depth aspect. */
ds_region.FirstSubresource = 1;
ds_region.NumSubresources = 3;
ID3D12GraphicsCommandList_DiscardResource(context.list, depth_rt, &ds_region);
/* Ensure that the clear gets executed properly for subresource 0 */
transition_resource_state(context.list, rt, D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
transition_resource_state(context.list, depth_rt, D3D12_RESOURCE_STATE_DEPTH_WRITE, D3D12_RESOURCE_STATE_COPY_SOURCE);
check_sub_resource_uint(rt, 0, context.queue, context.list, 0x000000ffu, 0);
/* Ensure that the clear gets executed properly for subresource 0 */
hr = ID3D12GraphicsCommandList_Reset(context.list, context.allocator, NULL);
{
D3D12_TEXTURE_COPY_LOCATION dst_location, src_location;
D3D12_BOX src_box;
dst_location.SubresourceIndex = 0;
dst_location.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
dst_location.pResource = tmp_depth;
src_location.SubresourceIndex = 0;
src_location.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
src_location.pResource = depth_rt;
src_box.left = 0;
src_box.right = 16;
src_box.top = 0;
src_box.bottom = 16;
src_box.front = 0;
src_box.back = 1;
ID3D12GraphicsCommandList_CopyTextureRegion(context.list, &dst_location, 0, 0, 0, &src_location, &src_box);
transition_resource_state(context.list, tmp_depth, D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_COPY_SOURCE);
}
check_sub_resource_float(tmp_depth, 0, context.queue, context.list, 1.0f, 0);
ID3D12Resource_Release(rt);
ID3D12Resource_Release(depth_rt);
ID3D12Resource_Release(tmp_depth);
ID3D12DescriptorHeap_Release(rtv_heap);
ID3D12DescriptorHeap_Release(dsv_heap);
destroy_test_context(&context);
}
void test_root_parameter_preservation(void)
{
D3D12_ROOT_SIGNATURE_DESC root_signature_desc;
D3D12_GRAPHICS_PIPELINE_STATE_DESC pso_desc;
ID3D12RootSignature *root_signature;
D3D12_ROOT_PARAMETER root_parameter;
ID3D12PipelineState *graphics_pso;
ID3D12PipelineState *compute_pso;
struct test_context_desc desc;
struct test_context context;
struct resource_readback rb;
ID3D12Resource *buffer;
uint32_t value;
HRESULT hr;
#include "shaders/command/headers/root_parameter_preservation_ps.h"
#include "shaders/command/headers/root_parameter_preservation_cs.h"
memset(&desc, 0, sizeof(desc));
desc.rt_width = 1;
desc.rt_height = 1;
desc.rt_format = DXGI_FORMAT_R8G8B8A8_UNORM;
desc.rt_descriptor_count = 1;
desc.sample_desc.Count = 1;
desc.rt_array_size = 1;
desc.no_pipeline = true;
desc.no_root_signature = true;
if (!init_test_context(&context, &desc))
return;
memset(&root_signature_desc, 0, sizeof(root_signature_desc));
memset(&root_parameter, 0, sizeof(root_parameter));
root_signature_desc.NumParameters = 1;
root_signature_desc.pParameters = &root_parameter;
root_parameter.ParameterType = D3D12_ROOT_PARAMETER_TYPE_UAV;
root_parameter.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
root_parameter.Descriptor.ShaderRegister = 1;
hr = create_root_signature(context.device, &root_signature_desc, &root_signature);
ok(SUCCEEDED(hr), "Failed to create root signature, hr = #%x.\n", hr);
init_pipeline_state_desc(&pso_desc, root_signature, desc.rt_format, NULL, &root_parameter_preservation_ps_dxbc, NULL);
hr = ID3D12Device_CreateGraphicsPipelineState(context.device, &pso_desc, &IID_ID3D12PipelineState, (void **)&graphics_pso);
ok(SUCCEEDED(hr), "Failed to create PSO, hr = #%x.\n", hr);
compute_pso = create_compute_pipeline_state(context.device, root_signature, root_parameter_preservation_cs_dxbc);
buffer = create_default_buffer(context.device, 4096, D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
ID3D12GraphicsCommandList_SetComputeRootSignature(context.list, root_signature);
ID3D12GraphicsCommandList_SetGraphicsRootSignature(context.list, root_signature);
ID3D12GraphicsCommandList_SetComputeRootUnorderedAccessView(context.list, 0, ID3D12Resource_GetGPUVirtualAddress(buffer));
ID3D12GraphicsCommandList_SetGraphicsRootUnorderedAccessView(context.list, 0, ID3D12Resource_GetGPUVirtualAddress(buffer) + 4);
ID3D12GraphicsCommandList_SetPipelineState(context.list, compute_pso);
ID3D12GraphicsCommandList_Dispatch(context.list, 4, 1, 1);
uav_barrier(context.list, buffer);
ID3D12GraphicsCommandList_SetPipelineState(context.list, graphics_pso);
ID3D12GraphicsCommandList_IASetPrimitiveTopology(context.list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
ID3D12GraphicsCommandList_OMSetRenderTargets(context.list, 1, &context.rtv, TRUE, NULL);
ID3D12GraphicsCommandList_RSSetViewports(context.list, 1, &context.viewport);
ID3D12GraphicsCommandList_RSSetScissorRects(context.list, 1, &context.scissor_rect);
ID3D12GraphicsCommandList_DrawInstanced(context.list, 3, 1, 0, 0);
uav_barrier(context.list, buffer);
/* We never touched root signature or root parameters, but verify that we correctly update push constants here. */
ID3D12GraphicsCommandList_SetPipelineState(context.list, compute_pso);
ID3D12GraphicsCommandList_Dispatch(context.list, 4, 1, 1);
transition_resource_state(context.list, buffer, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);
get_buffer_readback_with_command_list(buffer, DXGI_FORMAT_UNKNOWN, &rb, context.queue, context.list);
value = get_readback_uint(&rb, 0, 0, 0);
ok(value == 8, "Value %u != 8.\n", value);
value = get_readback_uint(&rb, 1, 0, 0);
ok(value == 100, "Value %u != 100.\n", value);
release_resource_readback(&rb);
ID3D12Resource_Release(buffer);
ID3D12RootSignature_Release(root_signature);
ID3D12PipelineState_Release(graphics_pso);
ID3D12PipelineState_Release(compute_pso);
destroy_test_context(&context);
}
static void test_cbv_hoisting(bool use_dxil)
{
D3D12_VERSIONED_ROOT_SIGNATURE_DESC root_signature_desc;
D3D12_ROOT_PARAMETER1 root_parameters[2];
D3D12_DESCRIPTOR_RANGE1 table_ranges[4];
unsigned int i, base_shader_register;
ID3D12RootSignature *root_signature;
uint32_t cbuffer_data[64 * 4];
struct test_context context;
struct resource_readback rb;
ID3D12DescriptorHeap *desc;
ID3D12PipelineState *pso;
ID3D12Resource *wbuffer;
ID3D12Resource *rbuffer;
uint32_t value;
HRESULT hr;
#include "shaders/command/headers/cbv_hoisting.h"
if (!init_compute_test_context(&context))
return;
if (use_dxil && !context_supports_dxil(&context))
{
destroy_test_context(&context);
return;
}
memset(&root_signature_desc, 0, sizeof(root_signature_desc));
memset(root_parameters, 0, sizeof(root_parameters));
memset(table_ranges, 0, sizeof(table_ranges));
root_signature_desc.Version = D3D_ROOT_SIGNATURE_VERSION_1_1;
root_signature_desc.Desc_1_1.NumParameters = ARRAY_SIZE(root_parameters);
root_signature_desc.Desc_1_1.pParameters = root_parameters;
root_parameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
root_parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
root_parameters[0].DescriptorTable.NumDescriptorRanges = ARRAY_SIZE(table_ranges);
root_parameters[0].DescriptorTable.pDescriptorRanges = table_ranges;
root_parameters[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_UAV;
root_parameters[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
base_shader_register = 0;
for (i = 0; i < ARRAY_SIZE(table_ranges); i++)
{
table_ranges[i].NumDescriptors = i >= 2 ? 2 : 1;
table_ranges[i].OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
table_ranges[i].Flags = D3D12_DESCRIPTOR_RANGE_FLAG_DESCRIPTORS_STATIC_KEEPING_BUFFER_BOUNDS_CHECKS;
table_ranges[i].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_CBV;
table_ranges[i].BaseShaderRegister = base_shader_register;
base_shader_register += table_ranges[i].NumDescriptors;
}
hr = create_versioned_root_signature(context.device, &root_signature_desc, &root_signature);
ok(SUCCEEDED(hr), "Failed to create root signature, hr = #%x.\n", hr);
pso = create_compute_pipeline_state(context.device, root_signature, use_dxil ? cbv_hoisting_dxil : cbv_hoisting_dxbc);
memset(cbuffer_data, 0, sizeof(cbuffer_data));
for (i = 0; i < ARRAY_SIZE(table_ranges); i++)
cbuffer_data[i * 64] = i;
rbuffer = create_upload_buffer(context.device, sizeof(cbuffer_data), cbuffer_data);
wbuffer = create_default_buffer(context.device, ARRAY_SIZE(table_ranges) * sizeof(uint32_t), D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
desc = create_gpu_descriptor_heap(context.device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, ARRAY_SIZE(table_ranges));
for (i = 0; i < ARRAY_SIZE(table_ranges); i++)
{
D3D12_CONSTANT_BUFFER_VIEW_DESC cbv;
D3D12_CPU_DESCRIPTOR_HANDLE handle;
handle = ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(desc);
handle.ptr += i * ID3D12Device_GetDescriptorHandleIncrementSize(context.device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
cbv.BufferLocation = ID3D12Resource_GetGPUVirtualAddress(rbuffer) + 256 * i;
cbv.SizeInBytes = 256;
ID3D12Device_CreateConstantBufferView(context.device, &cbv, handle);
}
ID3D12GraphicsCommandList_SetDescriptorHeaps(context.list, 1, &desc);
ID3D12GraphicsCommandList_SetPipelineState(context.list, pso);
ID3D12GraphicsCommandList_SetComputeRootSignature(context.list, root_signature);
ID3D12GraphicsCommandList_SetComputeRootDescriptorTable(context.list, 0, ID3D12DescriptorHeap_GetGPUDescriptorHandleForHeapStart(desc));
ID3D12GraphicsCommandList_SetComputeRootUnorderedAccessView(context.list, 1, ID3D12Resource_GetGPUVirtualAddress(wbuffer));
ID3D12GraphicsCommandList_Dispatch(context.list, 1, 1, 1);
transition_resource_state(context.list, wbuffer, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);
get_buffer_readback_with_command_list(wbuffer, DXGI_FORMAT_UNKNOWN, &rb, context.queue, context.list);
for (i = 0; i < ARRAY_SIZE(table_ranges); i++)
{
value = get_readback_uint(&rb, i, 0, 0);
ok(value == i, "Value %u != %u.\n", value, i);
}
release_resource_readback(&rb);
ID3D12Resource_Release(wbuffer);
ID3D12Resource_Release(rbuffer);
ID3D12RootSignature_Release(root_signature);
ID3D12PipelineState_Release(pso);
ID3D12DescriptorHeap_Release(desc);
destroy_test_context(&context);
}
void test_cbv_hoisting_sm51(void)
{
test_cbv_hoisting(false);
}
void test_cbv_hoisting_dxil(void)
{
test_cbv_hoisting(true);
}
static void test_conservative_rasterization(bool use_dxil)
{
ID3D12PipelineState *pipeline_conservative_underestimate;
ID3D12PipelineState *pipeline_conservative_overestimate;
ID3D12PipelineState *pipeline_conservative_off;
D3D12_ROOT_SIGNATURE_DESC root_signature_desc;
D3D12_GRAPHICS_PIPELINE_STATE_DESC pso_desc;
ID3D12PipelineState *pipeline_stencil_test;
D3D12_FEATURE_DATA_D3D12_OPTIONS options;
ID3D12GraphicsCommandList *command_list;
D3D12_INPUT_LAYOUT_DESC input_layout;
ID3D12RootSignature *root_signature;
struct depth_stencil_resource ds;
D3D12_QUERY_HEAP_DESC heap_desc;
struct test_context_desc desc;
ID3D12Resource *vb, *readback;
D3D12_VERTEX_BUFFER_VIEW vbv;
struct test_context context;
ID3D12QueryHeap *query_heap;
struct resource_readback rb;
ID3D12CommandQueue *queue;
uint64_t value;
unsigned int i;
HRESULT hr;
#include "shaders/command/headers/conservative_rasterization_vs.h"
#include "shaders/command/headers/conservative_rasterization_ps.h"
#include "shaders/command/headers/conservative_rasterization_ps_underestimate.h"
const D3D12_SHADER_BYTECODE ps_underestimate =
use_dxil ? conservative_rasterization_ps_underestimate_dxil : conservative_rasterization_ps_underestimate_dxbc;
static const D3D12_INPUT_ELEMENT_DESC layout_desc[] =
{
{"position", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
};
static const struct vec4 vertices[] =
{
{ 0.5f, -0.5f, 1.0f, 1.0f},
{-0.5f, -0.5f, 1.0f, 1.0f},
{ 0.0f, 0.5f, 1.0f, 1.0f},
};
static const struct
{
unsigned int stencil_ref;
D3D12_CONSERVATIVE_RASTERIZATION_TIER min_tier;
}
tests[] =
{
{ 0x1, D3D12_CONSERVATIVE_RASTERIZATION_TIER_1 },
{ 0x3, D3D12_CONSERVATIVE_RASTERIZATION_TIER_1 },
{ 0x7, D3D12_CONSERVATIVE_RASTERIZATION_TIER_3 },
};
memset(&desc, 0, sizeof(desc));
desc.no_root_signature = true;
if (!init_test_context(&context, &desc))
return;
hr = ID3D12Device_CheckFeatureSupport(context.device, D3D12_FEATURE_D3D12_OPTIONS, &options, sizeof(options));
ok(hr == S_OK, "Failed to check feature support, hr %#x.\n", hr);
if (!options.ConservativeRasterizationTier)
{
skip("Conservative rasterization not supported by device.\n");
destroy_test_context(&context);
return;
}
heap_desc.Type = D3D12_QUERY_HEAP_TYPE_OCCLUSION;
heap_desc.Count = ARRAY_SIZE(tests);
heap_desc.NodeMask = 0;
hr = ID3D12Device_CreateQueryHeap(context.device, &heap_desc, &IID_ID3D12QueryHeap, (void **)&query_heap);
ok(SUCCEEDED(hr), "Failed to create query heap, hr %#x.\n", hr);
readback = create_readback_buffer(context.device, ARRAY_SIZE(tests) * sizeof(uint64_t));
root_signature_desc.NumParameters = 0;
root_signature_desc.pParameters = NULL;
root_signature_desc.NumStaticSamplers = 0;
root_signature_desc.pStaticSamplers = NULL;
root_signature_desc.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT;
hr = create_root_signature(context.device, &root_signature_desc, &root_signature);
ok(hr == S_OK, "Failed to create root signature, hr %#x.\n", hr);
command_list = context.list;
queue = context.queue;
init_depth_stencil(&ds, context.device, 32, 32, 1, 1, DXGI_FORMAT_D32_FLOAT_S8X24_UINT, 0, NULL);
vb = create_upload_buffer(context.device, sizeof(vertices), vertices);
vbv.BufferLocation = ID3D12Resource_GetGPUVirtualAddress(vb);
vbv.StrideInBytes = sizeof(*vertices);
vbv.SizeInBytes = sizeof(vertices);
input_layout.pInputElementDescs = layout_desc;
input_layout.NumElements = ARRAY_SIZE(layout_desc);
init_pipeline_state_desc(&pso_desc, root_signature, DXGI_FORMAT_UNKNOWN,
use_dxil ? &conservative_rasterization_vs_dxil : &conservative_rasterization_vs_dxbc,
use_dxil ? &conservative_rasterization_ps_dxil : &conservative_rasterization_ps_dxbc, &input_layout);
pso_desc.DSVFormat = DXGI_FORMAT_D32_FLOAT_S8X24_UINT;
pso_desc.DepthStencilState.DepthEnable = true;
pso_desc.DepthStencilState.DepthFunc = D3D12_COMPARISON_FUNC_ALWAYS;
pso_desc.DepthStencilState.StencilEnable = true;
pso_desc.DepthStencilState.StencilWriteMask = 0x01;
pso_desc.DepthStencilState.FrontFace.StencilFailOp = D3D12_STENCIL_OP_REPLACE;
pso_desc.DepthStencilState.FrontFace.StencilDepthFailOp = D3D12_STENCIL_OP_REPLACE;
pso_desc.DepthStencilState.FrontFace.StencilPassOp = D3D12_STENCIL_OP_REPLACE;
pso_desc.DepthStencilState.FrontFace.StencilFunc = D3D12_COMPARISON_FUNC_ALWAYS;
pso_desc.DepthStencilState.BackFace = pso_desc.DepthStencilState.FrontFace;
pso_desc.RasterizerState.ConservativeRaster = D3D12_CONSERVATIVE_RASTERIZATION_MODE_ON;
hr = ID3D12Device_CreateGraphicsPipelineState(context.device, &pso_desc, &IID_ID3D12PipelineState, (void **)&pipeline_conservative_overestimate);
ok(hr == S_OK, "Failed to create pipeline, hr %#x.\n", hr);
pso_desc.DepthStencilState.StencilWriteMask = 0x02;
pso_desc.RasterizerState.ConservativeRaster = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF;
hr = ID3D12Device_CreateGraphicsPipelineState(context.device, &pso_desc, &IID_ID3D12PipelineState, (void **)&pipeline_conservative_off);
ok(hr == S_OK, "Failed to create pipeline, hr %#x.\n", hr);
if (options.ConservativeRasterizationTier >= D3D12_CONSERVATIVE_RASTERIZATION_TIER_3)
{
pso_desc.PS = ps_underestimate;
pso_desc.DepthStencilState.StencilWriteMask = 0x04;
pso_desc.RasterizerState.ConservativeRaster = D3D12_CONSERVATIVE_RASTERIZATION_MODE_ON;
hr = ID3D12Device_CreateGraphicsPipelineState(context.device, &pso_desc, &IID_ID3D12PipelineState, (void **)&pipeline_conservative_underestimate);
ok(hr == S_OK, "Failed to create pipeline, hr %#x.\n", hr);
}
else
pipeline_conservative_underestimate = NULL;
init_pipeline_state_desc(&pso_desc, root_signature, DXGI_FORMAT_UNKNOWN, NULL, NULL, NULL);
pso_desc.DSVFormat = DXGI_FORMAT_D32_FLOAT_S8X24_UINT;
pso_desc.DepthStencilState.DepthEnable = true;
pso_desc.DepthStencilState.DepthFunc = D3D12_COMPARISON_FUNC_ALWAYS;
pso_desc.DepthStencilState.StencilEnable = true;
pso_desc.DepthStencilState.StencilReadMask = 0xFF;
pso_desc.DepthStencilState.StencilWriteMask = 0x00;
pso_desc.DepthStencilState.FrontFace.StencilFailOp = D3D12_STENCIL_OP_KEEP;
pso_desc.DepthStencilState.FrontFace.StencilDepthFailOp = D3D12_STENCIL_OP_KEEP;
pso_desc.DepthStencilState.FrontFace.StencilPassOp = D3D12_STENCIL_OP_KEEP;
pso_desc.DepthStencilState.FrontFace.StencilFunc = D3D12_COMPARISON_FUNC_EQUAL;
pso_desc.DepthStencilState.BackFace = pso_desc.DepthStencilState.FrontFace;
pso_desc.RasterizerState.ConservativeRaster = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF;
hr = ID3D12Device_CreateGraphicsPipelineState(context.device, &pso_desc, &IID_ID3D12PipelineState, (void **)&pipeline_stencil_test);
ok(hr == S_OK, "Failed to create pipeline, hr %#x.\n", hr);
ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 0, NULL, false, &ds.dsv_handle);
ID3D12GraphicsCommandList_ClearDepthStencilView(command_list, ds.dsv_handle,
D3D12_CLEAR_FLAG_DEPTH | D3D12_CLEAR_FLAG_STENCIL, 0.0f, 0, 0, NULL);
ID3D12GraphicsCommandList_OMSetStencilRef(command_list, 0xFF);
ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, root_signature);
ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
ID3D12GraphicsCommandList_IASetVertexBuffers(command_list, 0, 1, &vbv);
ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
ID3D12GraphicsCommandList_SetPipelineState(command_list, pipeline_conservative_overestimate);
ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);
ID3D12GraphicsCommandList_SetPipelineState(command_list, pipeline_conservative_off);
ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);
if (pipeline_conservative_underestimate)
{
ID3D12GraphicsCommandList_SetPipelineState(command_list, pipeline_conservative_underestimate);
ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);
}
ID3D12GraphicsCommandList_SetPipelineState(command_list, pipeline_stencil_test);
for (i = 0; i < ARRAY_SIZE(tests); i++)
{
ID3D12GraphicsCommandList_OMSetStencilRef(command_list, tests[i].stencil_ref);
ID3D12GraphicsCommandList_BeginQuery(command_list, query_heap, D3D12_QUERY_TYPE_OCCLUSION, i);
ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);
ID3D12GraphicsCommandList_EndQuery(command_list, query_heap, D3D12_QUERY_TYPE_OCCLUSION, i);
}
ID3D12GraphicsCommandList_ResolveQueryData(command_list, query_heap,
D3D12_QUERY_TYPE_OCCLUSION, 0, ARRAY_SIZE(tests), readback, 0);
get_buffer_readback_with_command_list(readback, DXGI_FORMAT_UNKNOWN, &rb, queue, command_list);
for (i = 0; i < ARRAY_SIZE(tests); i++)
{
vkd3d_test_set_context("Test %u", i);
if (options.ConservativeRasterizationTier >= tests[i].min_tier)
{
value = get_readback_uint64(&rb, i, 0);
ok(value, "Unexpected value %"PRIu64".\n", value);
}
else
skip("Conservative rasterization tier %u not supported.\n", tests[i].min_tier);
}
ID3D12PipelineState_Release(pipeline_conservative_off);
ID3D12PipelineState_Release(pipeline_conservative_overestimate);
ID3D12PipelineState_Release(pipeline_stencil_test);
if (pipeline_conservative_underestimate)
ID3D12PipelineState_Release(pipeline_conservative_underestimate);
release_resource_readback(&rb);
ID3D12RootSignature_Release(root_signature);
ID3D12QueryHeap_Release(query_heap);
ID3D12Resource_Release(readback);
ID3D12Resource_Release(vb);
destroy_depth_stencil(&ds);
destroy_test_context(&context);
}
void test_conservative_rasterization_dxbc(void)
{
test_conservative_rasterization(false);
}
void test_conservative_rasterization_dxil(void)
{
test_conservative_rasterization(true);
}
void test_uninit_root_parameters(void)
{
D3D12_DESCRIPTOR_RANGE table_range[1];
D3D12_ROOT_SIGNATURE_DESC rs_desc;
D3D12_ROOT_PARAMETER rs_params[3];
ID3D12DescriptorHeap *desc_heap;
struct test_context context;
ID3D12RootSignature *alt_rs;
struct resource_readback rb;
ID3D12Resource *buf[4];
ID3D12Resource *output;
unsigned int i, j;
#include "shaders/command/headers/uninit_root_parameters.h"
if (!init_compute_test_context(&context))
return;
memset(&rs_desc, 0, sizeof(rs_desc));
memset(rs_params, 0, sizeof(rs_params));
memset(table_range, 0, sizeof(table_range));
rs_desc.NumParameters = ARRAY_SIZE(rs_params);
rs_desc.pParameters = rs_params;
rs_params[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
rs_params[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
rs_params[0].DescriptorTable.NumDescriptorRanges = ARRAY_SIZE(table_range);
rs_params[0].DescriptorTable.pDescriptorRanges = table_range;
table_range[0].NumDescriptors = 4;
table_range[0].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
rs_params[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
rs_params[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_UAV;
rs_params[2].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
rs_params[2].ParameterType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS;
rs_params[2].Constants.Num32BitValues = ARRAY_SIZE(buf);
create_root_signature(context.device, &rs_desc, &context.root_signature);
rs_params[2].Constants.Num32BitValues += 2;
create_root_signature(context.device, &rs_desc, &alt_rs);
context.pipeline_state = create_compute_pipeline_state(context.device, context.root_signature, uninit_root_parameters_dxbc);
desc_heap = create_gpu_descriptor_heap(context.device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, ARRAY_SIZE(buf));
for (i = 0; i < ARRAY_SIZE(buf); i++)
{
D3D12_SHADER_RESOURCE_VIEW_DESC srv_desc;
D3D12_CPU_DESCRIPTOR_HANDLE cpu_h;
float data[4];
for (j = 0; j < 4; j++)
data[j] = (float)(4 * i + 1 + j);
buf[i] = create_upload_buffer(context.device, sizeof(data), data);
srv_desc.Format = DXGI_FORMAT_R32G32B32A32_FLOAT;
srv_desc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
srv_desc.ViewDimension = D3D12_SRV_DIMENSION_BUFFER;
srv_desc.Buffer.FirstElement = 0;
srv_desc.Buffer.Flags = 0;
srv_desc.Buffer.NumElements = 1;
srv_desc.Buffer.StructureByteStride = 0;
cpu_h = ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(desc_heap);
cpu_h.ptr += ID3D12Device_GetDescriptorHandleIncrementSize(context.device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV) * i;
ID3D12Device_CreateShaderResourceView(context.device, buf[i], &srv_desc, cpu_h);
}
output = create_default_buffer(context.device, 4096, D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
/* First, check to see what happens when we let stale 32-bit constants and descriptor heap tables pass through a SetRootSignature call. */
{
D3D12_GPU_DESCRIPTOR_HANDLE gpu_h = ID3D12DescriptorHeap_GetGPUDescriptorHandleForHeapStart(desc_heap);
const struct vec4 expected = { 15.0f, 26.0f, 37.0f, 48.0f };
const float data[4] = { 10, 20, 30, 40 };
const struct vec4 *value;
gpu_h.ptr += ID3D12Device_GetDescriptorHandleIncrementSize(context.device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
ID3D12GraphicsCommandList_SetDescriptorHeaps(context.list, 1, &desc_heap);
ID3D12GraphicsCommandList_SetComputeRootSignature(context.list, alt_rs);
ID3D12GraphicsCommandList_SetComputeRootDescriptorTable(context.list, 0, gpu_h);
ID3D12GraphicsCommandList_SetComputeRootUnorderedAccessView(context.list, 1, ID3D12Resource_GetGPUVirtualAddress(output));
ID3D12GraphicsCommandList_SetComputeRoot32BitConstants(context.list, 2, 4, data, 0);
ID3D12GraphicsCommandList_SetComputeRootSignature(context.list, context.root_signature);
ID3D12GraphicsCommandList_SetPipelineState(context.list, context.pipeline_state);
ID3D12GraphicsCommandList_Dispatch(context.list, 1, 1, 1);
transition_resource_state(context.list, output, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);
get_buffer_readback_with_command_list(output, DXGI_FORMAT_UNKNOWN, &rb, context.queue, context.list);
reset_command_list(context.list, context.allocator);
/* Setting a new root signature does not clear out any state. This does not even trigger a warning, but it can hang AMD GPUs ... */
value = get_readback_vec4(&rb, 0, 0);
ok(compare_vec4(value, &expected, 0), "Expected (%f, %f, %f, %f), got (%f, %f, %f, %f).\n",
expected.x, expected.y, expected.z, expected.w,
value->x, value->y, value->z, value->w);
release_resource_readback(&rb);
}
/* Try not setting root constants. Expect zeroed state. */
{
D3D12_GPU_DESCRIPTOR_HANDLE gpu_h = ID3D12DescriptorHeap_GetGPUDescriptorHandleForHeapStart(desc_heap);
const struct vec4 expected = { 5.0f, 6.0f, 7.0f, 8.0f };
const struct vec4 *value;
gpu_h.ptr += ID3D12Device_GetDescriptorHandleIncrementSize(context.device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
ID3D12GraphicsCommandList_SetDescriptorHeaps(context.list, 1, &desc_heap);
ID3D12GraphicsCommandList_SetComputeRootSignature(context.list, context.root_signature);
ID3D12GraphicsCommandList_SetComputeRootDescriptorTable(context.list, 0, gpu_h);
ID3D12GraphicsCommandList_SetComputeRootUnorderedAccessView(context.list, 1, ID3D12Resource_GetGPUVirtualAddress(output) + 16);
ID3D12GraphicsCommandList_SetPipelineState(context.list, context.pipeline_state);
ID3D12GraphicsCommandList_Dispatch(context.list, 1, 1, 1);
transition_resource_state(context.list, output, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);
get_buffer_readback_with_command_list(output, DXGI_FORMAT_UNKNOWN, &rb, context.queue, context.list);
reset_command_list(context.list, context.allocator);
/* Setting a new root signature does not clear out any state. This does not even trigger a warning, but it can hang AMD GPUs ... */
value = get_readback_vec4(&rb, 1, 0);
ok(compare_vec4(value, &expected, 0), "Expected (%f, %f, %f, %f), got (%f, %f, %f, %f).\n",
expected.x, expected.y, expected.z, expected.w,
value->x, value->y, value->z, value->w);
release_resource_readback(&rb);
}
/* See what happens on ClearState(). It should be equivalent to a Reset(), i.e. cleared state. */
{
D3D12_GPU_DESCRIPTOR_HANDLE gpu_h = ID3D12DescriptorHeap_GetGPUDescriptorHandleForHeapStart(desc_heap);
const struct vec4 expected = { 5.0f, 6.0f, 7.0f, 8.0f };
const float data[4] = { 10, 20, 30, 40 };
const struct vec4 *value;
gpu_h.ptr += ID3D12Device_GetDescriptorHandleIncrementSize(context.device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
ID3D12GraphicsCommandList_SetDescriptorHeaps(context.list, 1, &desc_heap);
ID3D12GraphicsCommandList_SetComputeRootSignature(context.list, alt_rs);
ID3D12GraphicsCommandList_SetComputeRootDescriptorTable(context.list, 0, gpu_h);
ID3D12GraphicsCommandList_SetComputeRootUnorderedAccessView(context.list, 1, ID3D12Resource_GetGPUVirtualAddress(output));
ID3D12GraphicsCommandList_SetComputeRoot32BitConstants(context.list, 2, 4, data, 0);
/* Root parameter state is cleared to zero in ClearState. Have to set table + root descriptor or weird things happen. */
ID3D12GraphicsCommandList_ClearState(context.list, NULL);
ID3D12GraphicsCommandList_SetDescriptorHeaps(context.list, 1, &desc_heap);
ID3D12GraphicsCommandList_SetComputeRootSignature(context.list, context.root_signature);
ID3D12GraphicsCommandList_SetComputeRootDescriptorTable(context.list, 0, gpu_h);
ID3D12GraphicsCommandList_SetComputeRootUnorderedAccessView(context.list, 1, ID3D12Resource_GetGPUVirtualAddress(output) + 32);
ID3D12GraphicsCommandList_SetPipelineState(context.list, context.pipeline_state);
ID3D12GraphicsCommandList_Dispatch(context.list, 1, 1, 1);
transition_resource_state(context.list, output, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);
get_buffer_readback_with_command_list(output, DXGI_FORMAT_UNKNOWN, &rb, context.queue, context.list);
reset_command_list(context.list, context.allocator);
value = get_readback_vec4(&rb, 2, 0);
ok(compare_vec4(value, &expected, 0), "Expected (%f, %f, %f, %f), got (%f, %f, %f, %f).\n",
expected.x, expected.y, expected.z, expected.w,
value->x, value->y, value->z, value->w);
release_resource_readback(&rb);
}
ID3D12RootSignature_Release(alt_rs);
ID3D12Resource_Release(output);
for (i = 0; i < ARRAY_SIZE(buf); i++)
ID3D12Resource_Release(buf[i]);
ID3D12DescriptorHeap_Release(desc_heap);
destroy_test_context(&context);
}