d3d11/tests: Add a test for image UAV clears.

Ported from the similar Direct3D 12 test in vkd3d.

Signed-off-by: Henri Verbeet <hverbeet@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Henri Verbeet 2021-06-07 16:55:07 +02:00 committed by Alexandre Julliard
parent 479b659463
commit 736c4803d7
1 changed files with 329 additions and 0 deletions

View File

@ -102,6 +102,22 @@ struct device_desc
UINT flags;
};
struct resource_desc
{
D3D11_RESOURCE_DIMENSION dimension;
unsigned int width;
unsigned int height;
unsigned int depth_or_array_size;
unsigned int level_count;
DXGI_FORMAT format;
DXGI_SAMPLE_DESC sample_desc;
D3D11_USAGE usage;
unsigned int bind_flags;
unsigned int cpu_access_flags;
unsigned int misc_flags;
unsigned int structure_byte_stride;
};
struct swapchain_desc
{
BOOL windowed;
@ -771,6 +787,66 @@ static ID3D11Buffer *create_buffer_(unsigned int line, ID3D11Device *device,
return buffer;
}
static HRESULT create_resource(ID3D11Device *device, const struct resource_desc *desc,
const D3D11_SUBRESOURCE_DATA *data, ID3D11Resource **resource)
{
D3D11_TEXTURE1D_DESC texture1d_desc;
D3D11_TEXTURE2D_DESC texture2d_desc;
D3D11_TEXTURE3D_DESC texture3d_desc;
D3D11_BUFFER_DESC buffer_desc;
switch (desc->dimension)
{
case D3D11_RESOURCE_DIMENSION_BUFFER:
buffer_desc.ByteWidth = desc->width;
buffer_desc.Usage = desc->usage;
buffer_desc.BindFlags = desc->bind_flags;
buffer_desc.CPUAccessFlags = desc->cpu_access_flags;
buffer_desc.MiscFlags = desc->misc_flags;
buffer_desc.StructureByteStride = desc->structure_byte_stride;
return ID3D11Device_CreateBuffer(device, &buffer_desc, data, (ID3D11Buffer **)resource);
case D3D11_RESOURCE_DIMENSION_TEXTURE1D:
texture1d_desc.Width = desc->width;
texture1d_desc.MipLevels = desc->level_count;
texture1d_desc.ArraySize = desc->depth_or_array_size;
texture1d_desc.Format = desc->format;
texture1d_desc.Usage = desc->usage;
texture1d_desc.BindFlags = desc->bind_flags;
texture1d_desc.CPUAccessFlags = desc->cpu_access_flags;
texture1d_desc.MiscFlags = desc->misc_flags;
return ID3D11Device_CreateTexture1D(device, &texture1d_desc, data, (ID3D11Texture1D **)resource);
case D3D11_RESOURCE_DIMENSION_TEXTURE2D:
texture2d_desc.Width = desc->width;
texture2d_desc.Height = desc->height;
texture2d_desc.MipLevels = desc->level_count;
texture2d_desc.ArraySize = desc->depth_or_array_size;
texture2d_desc.Format = desc->format;
texture2d_desc.SampleDesc = desc->sample_desc;
texture2d_desc.Usage = desc->usage;
texture2d_desc.BindFlags = desc->bind_flags;
texture2d_desc.CPUAccessFlags = desc->cpu_access_flags;
texture2d_desc.MiscFlags = desc->misc_flags;
return ID3D11Device_CreateTexture2D(device, &texture2d_desc, data, (ID3D11Texture2D **)resource);
case D3D11_RESOURCE_DIMENSION_TEXTURE3D:
texture3d_desc.Width = desc->width;
texture3d_desc.Height = desc->height;
texture3d_desc.Depth = desc->depth_or_array_size;
texture3d_desc.MipLevels = desc->level_count;
texture3d_desc.Format = desc->format;
texture3d_desc.Usage = desc->usage;
texture3d_desc.BindFlags = desc->bind_flags;
texture3d_desc.CPUAccessFlags = desc->cpu_access_flags;
texture3d_desc.MiscFlags = desc->misc_flags;
return ID3D11Device_CreateTexture3D(device, &texture3d_desc, data, (ID3D11Texture3D **)resource);
default:
return E_INVALIDARG;
}
}
struct resource_readback
{
ID3D11Resource *resource;
@ -936,6 +1012,36 @@ static void get_texture3d_readback(ID3D11Texture3D *texture, unsigned int sub_re
ID3D11Device_Release(device);
}
static void get_resource_readback(ID3D11Resource *resource,
unsigned int sub_resource_idx, struct resource_readback *rb)
{
D3D11_RESOURCE_DIMENSION d;
ID3D11Resource_GetType(resource, &d);
switch (d)
{
case D3D11_RESOURCE_DIMENSION_BUFFER:
get_buffer_readback((ID3D11Buffer *)resource, rb);
return;
case D3D11_RESOURCE_DIMENSION_TEXTURE1D:
get_texture1d_readback((ID3D11Texture1D *)resource, sub_resource_idx, rb);
return;
case D3D11_RESOURCE_DIMENSION_TEXTURE2D:
get_texture_readback((ID3D11Texture2D *)resource, sub_resource_idx, rb);
return;
case D3D11_RESOURCE_DIMENSION_TEXTURE3D:
get_texture3d_readback((ID3D11Texture3D *)resource, sub_resource_idx, rb);
return;
default:
memset(rb, 0, sizeof(*rb));
return;
}
}
static void *get_readback_data(struct resource_readback *rb,
unsigned int x, unsigned int y, unsigned int z, unsigned byte_width)
{
@ -16314,6 +16420,228 @@ static void test_clear_buffer_unordered_access_view(void)
ok(!refcount, "Device has %u references left.\n", refcount);
}
static void test_clear_image_unordered_access_view(void)
{
unsigned int expected_colour, actual_colour;
D3D11_UNORDERED_ACCESS_VIEW_DESC uav_desc;
unsigned int i, j, d, p, x, y, z, layer;
struct d3d11_test_context test_context;
unsigned int image_size, image_depth;
struct resource_desc resource_desc;
ID3D11UnorderedAccessView *uav[2];
ID3D11DeviceContext *context;
struct resource_readback rb;
BOOL is_small_float_format;
ID3D11Resource *resource;
BOOL is_inside, success;
ID3D11Device *device;
UINT clear_value[4];
HRESULT hr;
#define IMAGE_SIZE 16
static const struct
{
DXGI_FORMAT format;
unsigned int image_mips;
unsigned int image_layers;
unsigned int mip_level;
unsigned int first_layer;
unsigned int layer_count;
unsigned int values[4];
unsigned int expected;
BOOL is_float;
unsigned int clamped;
BOOL is_todo;
}
tests[] =
{
/* Test clearing a specific mip level. */
{DXGI_FORMAT_R32_FLOAT, 2, 1, 0, 0, 1, {1, 0, 0, 0}, 0x00000001, FALSE, 0, TRUE},
{DXGI_FORMAT_R32_FLOAT, 2, 1, 1, 0, 1, {1, 0, 0, 0}, 0x00000001, FALSE, 0, TRUE},
{DXGI_FORMAT_R32_FLOAT, 2, 1, 0, 0, 1, {0x3f000000, 0, 0, 0}, 0x3f000000, TRUE, 0, TRUE},
{DXGI_FORMAT_R32_FLOAT, 2, 1, 1, 0, 1, {0x3f000000, 0, 0, 0}, 0x3f000000, TRUE, 0, TRUE},
/* Test clearing specific array layers. */
{DXGI_FORMAT_R32_FLOAT, 1, IMAGE_SIZE, 0, 0, IMAGE_SIZE, {1, 0, 0, 0}, 0x00000001, FALSE, 0, TRUE},
{DXGI_FORMAT_R32_FLOAT, 1, IMAGE_SIZE, 0, 3, 2, {1, 0, 0, 0}, 0x00000001, FALSE, 0, TRUE},
{DXGI_FORMAT_R32_FLOAT, 1, IMAGE_SIZE, 0, 0, IMAGE_SIZE,
{0x3f000000, 0, 0, 0}, 0x3f000000, TRUE, 0, TRUE},
{DXGI_FORMAT_R32_FLOAT, 1, IMAGE_SIZE, 0, 3, 2,
{0x3f000000, 0, 0, 0}, 0x3f000000, TRUE, 0, TRUE},
/* Test uint clears with formats. */
{DXGI_FORMAT_R16G16_UINT, 1, 1, 0, 0, 1, {1, 2, 3, 4}, 0x00020001, FALSE, 0, TRUE},
{DXGI_FORMAT_R16G16_UINT, 1, 1, 0, 0, 1, {0x12345, 0, 0, 0}, 0x00002345, FALSE, 0x0000ffff, TRUE},
{DXGI_FORMAT_R16G16_UNORM, 1, 1, 0, 0, 1, {1, 2, 3, 4}, 0x00020001, FALSE, 0, TRUE},
{DXGI_FORMAT_R16G16_FLOAT, 1, 1, 0, 0, 1, {1, 2, 3, 4}, 0x00020001, FALSE, 0, TRUE},
{DXGI_FORMAT_R8G8B8A8_UINT, 1, 1, 0, 0, 1, {1, 2, 3, 4}, 0x04030201, FALSE, 0, TRUE},
{DXGI_FORMAT_R8G8B8A8_UINT, 1, 1, 0, 0, 1, {0x123, 0, 0, 0}, 0x00000023, FALSE, 0x000000ff, TRUE},
{DXGI_FORMAT_R8G8B8A8_UNORM, 1, 1, 0, 0, 1, {1, 2, 3, 4}, 0x04030201, FALSE, 0, TRUE},
{DXGI_FORMAT_R11G11B10_FLOAT, 1, 1, 0, 0, 1, {1, 2, 3, 4}, 0x00c01001, FALSE, 0, TRUE},
/* Test float clears with formats. */
{DXGI_FORMAT_R16G16_UNORM, 1, 1, 0, 0, 1,
{0x3f000000 /* 0.5f */, 0x3f800000 /* 1.0f */, 0, 0}, 0xffff8000, TRUE, 0, TRUE},
{DXGI_FORMAT_R16G16_FLOAT, 1, 1, 0, 0, 1,
{0x3f000000 /* 0.5f */, 0x3f800000 /* 1.0f */, 0, 0}, 0x3c003800, TRUE, 0, TRUE},
{DXGI_FORMAT_R8G8B8A8_UNORM, 1, 1, 0, 0, 1,
{0x3f000000 /* 0.5f */, 0x3f800000 /* 1.0f */, 0, 0}, 0x0000ff80, TRUE, 0, TRUE},
{DXGI_FORMAT_R8G8B8A8_UNORM, 1, 1, 0, 0, 1,
{0, 0, 0x3f000000 /* 0.5f */, 0x3f800000 /* 1.0f */}, 0xff800000, TRUE, 0, TRUE},
{DXGI_FORMAT_R11G11B10_FLOAT, 1, 1, 0, 0, 1,
{0x3f000000 /* 1.0f */, 0 /* 0.0f */, 0xbf800000 /* -1.0f */, 0x3f000000 /* 1.0f */},
0x00000380, TRUE, 0, TRUE},
};
static const struct
{
D3D11_RESOURCE_DIMENSION resource_dim;
D3D11_UAV_DIMENSION view_dim;
BOOL is_layered;
}
uav_dimensions[] =
{
{D3D11_RESOURCE_DIMENSION_TEXTURE2D, D3D11_UAV_DIMENSION_TEXTURE2D, FALSE},
{D3D11_RESOURCE_DIMENSION_TEXTURE2D, D3D11_UAV_DIMENSION_TEXTURE2DARRAY, TRUE },
/* Expected behaviour with partial layer coverage is unclear. */
{D3D11_RESOURCE_DIMENSION_TEXTURE3D, D3D11_UAV_DIMENSION_TEXTURE3D, FALSE},
};
if (!init_test_context(&test_context, NULL))
return;
device = test_context.device;
context = test_context.immediate_context;
memset(&resource_desc, 0, sizeof(resource_desc));
resource_desc.width = IMAGE_SIZE;
resource_desc.height = IMAGE_SIZE;
resource_desc.sample_desc.Count = 1;
resource_desc.usage = D3D11_USAGE_DEFAULT;
resource_desc.bind_flags = D3D11_BIND_UNORDERED_ACCESS;
for (d = 0; d < ARRAY_SIZE(uav_dimensions); ++d)
{
for (i = 0; i < ARRAY_SIZE(tests); ++i)
{
winetest_push_context("Dim %u, Test %u", d, i);
if (tests[i].image_layers > 1 && !uav_dimensions[d].is_layered)
{
winetest_pop_context();
continue;
}
resource_desc.dimension = uav_dimensions[d].resource_dim;
resource_desc.depth_or_array_size = tests[i].image_layers;
resource_desc.level_count = tests[i].image_mips;
resource_desc.format = tests[i].format;
if (FAILED(hr = create_resource(device, &resource_desc, NULL, &resource)))
{
skip("Failed to create resource, hr %#x.\n", hr);
winetest_pop_context();
continue;
}
uav_desc.Format = tests[i].format;
uav_desc.ViewDimension = uav_dimensions[d].view_dim;
for (j = 0; j < 2; ++j)
{
unsigned int first_layer = j ? 0 : tests[i].first_layer;
unsigned int layer_count = j ? tests[i].image_layers : tests[i].layer_count;
switch (uav_desc.ViewDimension)
{
case D3D11_UAV_DIMENSION_TEXTURE2D:
uav_desc.Texture2D.MipSlice = tests[i].mip_level;
break;
case D3D11_UAV_DIMENSION_TEXTURE2DARRAY:
uav_desc.Texture2DArray.MipSlice = tests[i].mip_level;
uav_desc.Texture2DArray.FirstArraySlice = first_layer;
uav_desc.Texture2DArray.ArraySize = layer_count;
break;
case D3D11_UAV_DIMENSION_TEXTURE3D:
uav_desc.Texture3D.MipSlice = tests[i].mip_level;
uav_desc.Texture3D.FirstWSlice = first_layer;
uav_desc.Texture3D.WSize = layer_count;
break;
default:
ok(0, "Unhandled uav dimension %#x.\n", uav_dimensions[d].view_dim);
break;
}
hr = ID3D11Device_CreateUnorderedAccessView(device, resource, &uav_desc, &uav[j]);
ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
}
for (j = 0; j < 4; ++j)
{
clear_value[j] = tests[i].expected ? 0u : ~0u;
}
ID3D11DeviceContext_ClearUnorderedAccessViewUint(context, uav[1], clear_value);
if (tests[i].is_float)
ID3D11DeviceContext_ClearUnorderedAccessViewFloat(context, uav[0], (const float *)tests[i].values);
else
ID3D11DeviceContext_ClearUnorderedAccessViewUint(context, uav[0], tests[i].values);
image_depth = uav_dimensions[d].resource_dim == D3D11_RESOURCE_DIMENSION_TEXTURE3D
? max(tests[i].image_layers >> tests[i].mip_level, 1u) : 1;
image_size = max(IMAGE_SIZE >> tests[i].mip_level, 1u);
is_small_float_format = tests[i].format == DXGI_FORMAT_R16G16_UNORM
|| tests[i].format == DXGI_FORMAT_R16G16_FLOAT
|| tests[i].format == DXGI_FORMAT_R11G11B10_FLOAT
|| tests[i].format == DXGI_FORMAT_R8G8B8A8_UNORM;
for (layer = 0; layer < tests[i].image_layers / image_depth; ++layer)
{
get_resource_readback(resource, tests[i].mip_level + (layer * tests[i].image_mips), &rb);
success = TRUE;
expected_colour = actual_colour = x = y = z = 0;
for (p = 0; p < image_depth * image_size * image_size; ++p)
{
x = p % image_size;
y = (p / image_size) % image_size;
z = p / (image_size * image_size);
if (uav_dimensions[d].resource_dim == D3D11_RESOURCE_DIMENSION_TEXTURE3D)
is_inside = z >= tests[i].first_layer
&& z < tests[i].first_layer + tests[i].layer_count;
else
is_inside = layer >= tests[i].first_layer
&& layer < tests[i].first_layer + tests[i].layer_count;
expected_colour = is_inside ? tests[i].expected : clear_value[0];
actual_colour = get_readback_u32(&rb, x, y, z);
if (!(success = compare_color(actual_colour, expected_colour, tests[i].is_float ? 1 : 0)
/* Some drivers/GPUs clamp clear values that can't
* be represented by the format. (Windows 7
* testbot, AMD PALM) */
|| broken(is_inside && tests[i].clamped && actual_colour == tests[i].clamped)
/* Some drivers/GPUs mishandle integer clears of
* small float/normalised formats. (AMD PALM) */
|| broken(is_inside && !tests[i].is_float && is_small_float_format && !actual_colour)))
break;
}
todo_wine_if(tests[i].is_todo && expected_colour)
ok(success, "At layer %u, (%u,%u,%u), expected 0x%08x, got 0x%08x.\n",
layer, x, y, z, expected_colour, actual_colour);
release_resource_readback(&rb);
}
ID3D11UnorderedAccessView_Release(uav[1]);
ID3D11UnorderedAccessView_Release(uav[0]);
ID3D11Resource_Release(resource);
winetest_pop_context();
}
}
release_test_context(&test_context);
#undef IMAGE_SIZE
}
static void test_initial_depth_stencil_state(void)
{
static const struct vec4 green = {0.0f, 1.0f, 0.0f, 1.0f};
@ -33043,6 +33371,7 @@ START_TEST(d3d11)
queue_test(test_clear_render_target_view_3d);
queue_test(test_clear_depth_stencil_view);
queue_test(test_clear_buffer_unordered_access_view);
queue_test(test_clear_image_unordered_access_view);
queue_test(test_initial_depth_stencil_state);
queue_test(test_draw_depth_only);
queue_test(test_draw_uav_only);