-- v10: tests/shader-runner: Add sampler comparison tests.
From: Conor McCarthy cmccarthy@codeweavers.com
--- Makefile.am | 1 + tests/hlsl/sample-cmp.shader_test | 158 ++++++++++++++++++++++++++++++ tests/shader_runner.c | 45 ++++++++- tests/shader_runner.h | 2 + tests/shader_runner_d3d11.c | 2 +- tests/shader_runner_d3d12.c | 1 + tests/shader_runner_gl.c | 62 +++++++++--- tests/shader_runner_vulkan.c | 27 +++++ 8 files changed, 279 insertions(+), 19 deletions(-) create mode 100644 tests/hlsl/sample-cmp.shader_test
diff --git a/Makefile.am b/Makefile.am index 9e761ef32..27607d7ab 100644 --- a/Makefile.am +++ b/Makefile.am @@ -160,6 +160,7 @@ vkd3d_shader_tests = \ tests/hlsl/return.shader_test \ tests/hlsl/round.shader_test \ tests/hlsl/sample-bias.shader_test \ + tests/hlsl/sample-cmp.shader_test \ tests/hlsl/sample-grad.shader_test \ tests/hlsl/sample-level.shader_test \ tests/hlsl/sampler-offset.shader_test \ diff --git a/tests/hlsl/sample-cmp.shader_test b/tests/hlsl/sample-cmp.shader_test new file mode 100644 index 000000000..7ae95bcff --- /dev/null +++ b/tests/hlsl/sample-cmp.shader_test @@ -0,0 +1,158 @@ +[require] +shader model >= 4.0 + +[sampler 0] +filter linear linear linear +address clamp clamp clamp +comparison never + +[srv 0] +format r32 float shadow +size (2d, 2, 2) +0.5 0.5 +0.5 0.5 + +[pixel shader] +Texture2D<float> t : register(t0); +SamplerComparisonState s : register(s0); + +float ref; + +float4 main() : sv_target +{ + return t.SampleCmpLevelZero(s, float2(0.5, 0.5), ref); +} + +[test] +uniform 0 float 1.0 +draw quad +probe all r (0.0) + +uniform 0 float 0.0 +draw quad +probe all r (0.0) + +[sampler 0] +filter linear linear linear +address clamp clamp clamp +comparison less + +[test] +uniform 0 float 0.0 +draw quad +probe all r (1.0) + +uniform 0 float 0.5 +draw quad +probe all r (0.0) + +[sampler 0] +filter linear linear linear +address clamp clamp clamp +comparison equal + +[test] +uniform 0 float 0.5 +draw quad +probe all r (1.0) + +uniform 0 float 0.5000001 +draw quad +probe all r (0.0) + +[sampler 0] +filter linear linear linear +address clamp clamp clamp +comparison less equal + +[test] +uniform 0 float 0.5 +draw quad +probe all r (1.0) + +uniform 0 float 0.5000001 +draw quad +probe all r (0.0) + +[sampler 0] +filter linear linear linear +address clamp clamp clamp +comparison greater + +[test] +uniform 0 float 1.0 +draw quad +probe all r (1.0) + +uniform 0 float 0.5 +draw quad +probe all r (0.0) + +[sampler 0] +filter linear linear linear +address clamp clamp clamp +comparison not equal + +[test] +uniform 0 float 0.5000001 +draw quad +probe all r (1.0) + +uniform 0 float 0.5 +draw quad +probe all r (0.0) + +[sampler 0] +filter linear linear linear +address clamp clamp clamp +comparison greater equal + +[test] +uniform 0 float 0.5 +draw quad +probe all r (1.0) + +uniform 0 float 0.4999999 +draw quad +probe all r (0.0) + +[sampler 0] +filter linear linear linear +address clamp clamp clamp +comparison always + +[test] +uniform 0 float 0.0 +draw quad +probe all r (1.0) + +uniform 0 float 1.0 +draw quad +probe all r (1.0) + + +[sampler 0] +filter linear linear linear +address clamp clamp clamp +comparison greater + +[pixel shader] +Texture2D<float> t : register(t0); +SamplerComparisonState s : register(s0); + +float ref; + +float4 main() : sv_target +{ + return t.SampleCmp(s, float2(0.5, 0.5), ref); +} + +[test] +uniform 0 float 1.0 +draw quad +probe all r (1.0) + +uniform 0 float 0.0 +draw quad +probe all r (0.0) + diff --git a/tests/shader_runner.c b/tests/shader_runner.c index d0d806508..6ecdc5fda 100644 --- a/tests/shader_runner.c +++ b/tests/shader_runner.c @@ -324,7 +324,8 @@ static void parse_require_directive(struct shader_runner *runner, const char *li } }
-static DXGI_FORMAT parse_format(const char *line, enum texture_data_type *data_type, unsigned int *texel_size, const char **rest) +static DXGI_FORMAT parse_format(const char *line, enum texture_data_type *data_type, unsigned int *texel_size, + bool *is_shadow, const char **rest) { static const struct { @@ -332,6 +333,7 @@ static DXGI_FORMAT parse_format(const char *line, enum texture_data_type *data_t enum texture_data_type data_type; unsigned int texel_size; DXGI_FORMAT format; + bool is_shadow; } formats[] = { @@ -341,6 +343,7 @@ static DXGI_FORMAT parse_format(const char *line, enum texture_data_type *data_t {"r32g32 float", TEXTURE_DATA_FLOAT, 8, DXGI_FORMAT_R32G32_FLOAT}, {"r32g32 int", TEXTURE_DATA_SINT, 8, DXGI_FORMAT_R32G32_SINT}, {"r32g32 uint", TEXTURE_DATA_UINT, 8, DXGI_FORMAT_R32G32_UINT}, + {"r32 float shadow", TEXTURE_DATA_FLOAT, 4, DXGI_FORMAT_R32_FLOAT, true}, {"r32 float", TEXTURE_DATA_FLOAT, 4, DXGI_FORMAT_R32_FLOAT}, {"r32 sint", TEXTURE_DATA_SINT, 4, DXGI_FORMAT_R32_SINT}, {"r32 uint", TEXTURE_DATA_UINT, 4, DXGI_FORMAT_R32_UINT}, @@ -354,6 +357,8 @@ static DXGI_FORMAT parse_format(const char *line, enum texture_data_type *data_t if (data_type) *data_type = formats[i].data_type; *texel_size = formats[i].texel_size; + if (is_shadow) + *is_shadow = formats[i].is_shadow; return formats[i].format; } } @@ -410,12 +415,46 @@ static void parse_sampler_directive(struct sampler *sampler, const char *line) if (match_string(line, filters[i].string, &line)) { sampler->filter = filters[i].filter; + if (sampler->func) + sampler->filter |= D3D12_FILTER_REDUCTION_TYPE_COMPARISON << D3D12_FILTER_REDUCTION_TYPE_SHIFT; return; } }
fatal_error("Unknown sampler filter '%s'.\n", line); } + else if (match_string(line, "comparison", &line)) + { + static const struct + { + const char *string; + D3D12_COMPARISON_FUNC func; + } + funcs[] = + { + {"less equal", D3D12_COMPARISON_FUNC_LESS_EQUAL}, + {"not equal", D3D12_COMPARISON_FUNC_NOT_EQUAL}, + {"greater equal", D3D12_COMPARISON_FUNC_GREATER_EQUAL}, + {"never", D3D12_COMPARISON_FUNC_NEVER}, + {"less", D3D12_COMPARISON_FUNC_LESS}, + {"equal", D3D12_COMPARISON_FUNC_EQUAL}, + {"greater", D3D12_COMPARISON_FUNC_GREATER}, + {"always", D3D12_COMPARISON_FUNC_ALWAYS}, + }; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(funcs); ++i) + { + if (match_string(line, funcs[i].string, &line)) + { + sampler->filter |= D3D12_FILTER_REDUCTION_TYPE_COMPARISON << D3D12_FILTER_REDUCTION_TYPE_SHIFT; + sampler->func = funcs[i].func; + return; + } + } + + fatal_error("Unknown sampler func '%s'.\n", line); + } else { fatal_error("Unknown sampler directive '%s'.\n", line); @@ -426,7 +465,7 @@ static void parse_resource_directive(struct resource_params *resource, const cha { if (match_string(line, "format", &line)) { - resource->format = parse_format(line, &resource->data_type, &resource->texel_size, &line); + resource->format = parse_format(line, &resource->data_type, &resource->texel_size, &resource->is_shadow, &line); } else if (match_string(line, "stride", &line)) { @@ -513,7 +552,7 @@ static void parse_input_layout_directive(struct shader_runner *runner, const cha fatal_error("Malformed input layout directive '%s'.\n", line); line = rest;
- element->format = parse_format(line, NULL, &element->texel_size, &line); + element->format = parse_format(line, NULL, &element->texel_size, NULL, &line);
if (!(rest = strpbrk(line, " \n"))) rest = line + strlen(line); diff --git a/tests/shader_runner.h b/tests/shader_runner.h index 62358d78c..83399fb03 100644 --- a/tests/shader_runner.h +++ b/tests/shader_runner.h @@ -62,6 +62,7 @@ struct sampler
D3D12_FILTER filter; D3D12_TEXTURE_ADDRESS_MODE u_address, v_address, w_address; + D3D12_COMPARISON_FUNC func; };
enum resource_type @@ -85,6 +86,7 @@ struct resource_params enum resource_dimension dimension;
DXGI_FORMAT format; + bool is_shadow; enum texture_data_type data_type; unsigned int texel_size; unsigned int stride; diff --git a/tests/shader_runner_d3d11.c b/tests/shader_runner_d3d11.c index 528c04e9d..d9310497a 100644 --- a/tests/shader_runner_d3d11.c +++ b/tests/shader_runner_d3d11.c @@ -509,7 +509,7 @@ static ID3D11SamplerState *create_sampler(ID3D11Device *device, const struct sam desc.AddressU = (D3D11_TEXTURE_ADDRESS_MODE)sampler->u_address; desc.AddressV = (D3D11_TEXTURE_ADDRESS_MODE)sampler->v_address; desc.AddressW = (D3D11_TEXTURE_ADDRESS_MODE)sampler->w_address; - desc.ComparisonFunc = D3D11_COMPARISON_NEVER; + desc.ComparisonFunc = sampler->func; desc.MaxLOD = D3D11_FLOAT32_MAX;
hr = ID3D11Device_CreateSamplerState(device, &desc, &d3d11_sampler); diff --git a/tests/shader_runner_d3d12.c b/tests/shader_runner_d3d12.c index ca1375124..ce27ad22a 100644 --- a/tests/shader_runner_d3d12.c +++ b/tests/shader_runner_d3d12.c @@ -325,6 +325,7 @@ static ID3D12RootSignature *d3d12_runner_create_root_signature(struct d3d12_shad sampler_desc->AddressU = sampler->u_address; sampler_desc->AddressV = sampler->v_address; sampler_desc->AddressW = sampler->w_address; + sampler_desc->ComparisonFunc = sampler->func; sampler_desc->MaxLOD = FLT_MAX; sampler_desc->ShaderRegister = sampler->slot; sampler_desc->RegisterSpace = 0; diff --git a/tests/shader_runner_gl.c b/tests/shader_runner_gl.c index 6cec33a43..c3aa3a66b 100644 --- a/tests/shader_runner_gl.c +++ b/tests/shader_runner_gl.c @@ -37,6 +37,7 @@ struct format_info enum DXGI_FORMAT f; unsigned int component_count; bool is_integer; + bool is_shadow; GLenum internal_format; GLenum format; GLenum type; @@ -284,27 +285,28 @@ static void gl_runner_cleanup(struct gl_runner *runner) ok(ret, "Failed to terminate EGL display connection.\n"); }
-static const struct format_info *get_format_info(enum DXGI_FORMAT format) +static const struct format_info *get_format_info(enum DXGI_FORMAT format, bool is_shadow) { size_t i;
static const struct format_info format_info[] = { - {DXGI_FORMAT_UNKNOWN, 1, true, GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT}, - {DXGI_FORMAT_R32G32B32A32_FLOAT, 4, false, GL_RGBA32F, GL_RGBA, GL_FLOAT}, - {DXGI_FORMAT_R32G32B32A32_UINT, 4, true, GL_RGBA32UI, GL_RGBA_INTEGER, GL_UNSIGNED_INT}, - {DXGI_FORMAT_R32G32B32A32_SINT, 4, true, GL_RGBA32I, GL_RGBA_INTEGER, GL_INT}, - {DXGI_FORMAT_R32G32_FLOAT, 2, false, GL_RG32F, GL_RG, GL_FLOAT}, - {DXGI_FORMAT_R32G32_UINT, 2, true, GL_RG32UI, GL_RG_INTEGER, GL_UNSIGNED_INT}, - {DXGI_FORMAT_R32G32_SINT, 2, true, GL_RG32I, GL_RG_INTEGER, GL_INT}, - {DXGI_FORMAT_R32_FLOAT, 1, false, GL_R32F, GL_RED, GL_FLOAT}, - {DXGI_FORMAT_R32_UINT, 1, true, GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT}, - {DXGI_FORMAT_R32_SINT, 1, true, GL_R32I, GL_RED_INTEGER, GL_INT}, + {DXGI_FORMAT_UNKNOWN, 1, true, false, GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT}, + {DXGI_FORMAT_R32G32B32A32_FLOAT, 4, false, false, GL_RGBA32F, GL_RGBA, GL_FLOAT}, + {DXGI_FORMAT_R32G32B32A32_UINT, 4, true, false, GL_RGBA32UI, GL_RGBA_INTEGER, GL_UNSIGNED_INT}, + {DXGI_FORMAT_R32G32B32A32_SINT, 4, true, false, GL_RGBA32I, GL_RGBA_INTEGER, GL_INT}, + {DXGI_FORMAT_R32G32_FLOAT, 2, false, false, GL_RG32F, GL_RG, GL_FLOAT}, + {DXGI_FORMAT_R32G32_UINT, 2, true, false, GL_RG32UI, GL_RG_INTEGER, GL_UNSIGNED_INT}, + {DXGI_FORMAT_R32G32_SINT, 2, true, false, GL_RG32I, GL_RG_INTEGER, GL_INT}, + {DXGI_FORMAT_R32_FLOAT, 1, false, false, GL_R32F, GL_RED, GL_FLOAT}, + {DXGI_FORMAT_R32_FLOAT, 1, false, true, GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT}, + {DXGI_FORMAT_R32_UINT, 1, true, false, GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT}, + {DXGI_FORMAT_R32_SINT, 1, true, false, GL_R32I, GL_RED_INTEGER, GL_INT}, };
for (i = 0; i < ARRAY_SIZE(format_info); ++i) { - if (format_info[i].f == format) + if (format_info[i].f == format && format_info[i].is_shadow == is_shadow) return &format_info[i]; }
@@ -315,7 +317,7 @@ static void init_resource_2d(struct gl_resource *resource, const struct resource { unsigned int offset, w, h, i;
- resource->format = get_format_info(params->format); + resource->format = get_format_info(params->format, params->is_shadow); glGenTextures(1, &resource->id); glBindTexture(GL_TEXTURE_2D, resource->id); glTexStorage2D(GL_TEXTURE_2D, params->level_count, @@ -337,7 +339,7 @@ static void init_resource_2d(struct gl_resource *resource, const struct resource
static void init_resource_buffer(struct gl_resource *resource, const struct resource_params *params) { - resource->format = get_format_info(params->format); + resource->format = get_format_info(params->format, false);
glGenBuffers(1, &resource->id); glBindBuffer(GL_TEXTURE_BUFFER, resource->id); @@ -721,6 +723,31 @@ static GLenum get_texture_filter_min_gl(D3D12_FILTER filter) return filter & 0x10 ? GL_NEAREST_MIPMAP_LINEAR : GL_NEAREST_MIPMAP_NEAREST; }
+static GLenum get_compare_op_gl(D3D12_COMPARISON_FUNC op) +{ + switch (op) + { + case D3D12_COMPARISON_FUNC_NEVER: + return GL_NEVER; + case D3D12_COMPARISON_FUNC_LESS: + return GL_LESS; + case D3D12_COMPARISON_FUNC_EQUAL: + return GL_EQUAL; + case D3D12_COMPARISON_FUNC_LESS_EQUAL: + return GL_LEQUAL; + case D3D12_COMPARISON_FUNC_GREATER: + return GL_GREATER; + case D3D12_COMPARISON_FUNC_NOT_EQUAL: + return GL_NOTEQUAL; + case D3D12_COMPARISON_FUNC_GREATER_EQUAL: + return GL_GEQUAL; + case D3D12_COMPARISON_FUNC_ALWAYS: + return GL_ALWAYS; + default: + fatal_error("Unhandled compare op %#x.\n", op); + } +} + static GLuint compile_graphics_shader_program(struct gl_runner *runner, ID3D10Blob **vs_blob) { struct vkd3d_shader_code vs_code, fs_code; @@ -837,6 +864,11 @@ static bool gl_runner_draw(struct shader_runner *r, glSamplerParameteri(id, GL_TEXTURE_WRAP_R, get_texture_wrap_gl(sampler->w_address)); glSamplerParameteri(id, GL_TEXTURE_MAG_FILTER, get_texture_filter_mag_gl(sampler->filter)); glSamplerParameteri(id, GL_TEXTURE_MIN_FILTER, get_texture_filter_min_gl(sampler->filter)); + if (sampler->func) + { + glSamplerParameteri(id, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); + glSamplerParameteri(id, GL_TEXTURE_COMPARE_FUNC, get_compare_op_gl(sampler->func)); + } sampler_info[i].id = id; }
@@ -938,7 +970,7 @@ static bool gl_runner_draw(struct shader_runner *r, signature_element = vkd3d_shader_find_signature_element(&vs_input_signature, element->name, element->index, 0); attribute_idx = signature_element->register_index; - format = get_format_info(element->format); + format = get_format_info(element->format, false);
glBindBuffer(GL_ARRAY_BUFFER, vbo_info[element->slot].id); if (format->is_integer) diff --git a/tests/shader_runner_vulkan.c b/tests/shader_runner_vulkan.c index 35db729b5..f266030ed 100644 --- a/tests/shader_runner_vulkan.c +++ b/tests/shader_runner_vulkan.c @@ -810,6 +810,31 @@ static VkSamplerAddressMode vk_address_mode_from_d3d12(D3D12_TEXTURE_ADDRESS_MOD } }
+static enum VkCompareOp vk_compare_op_from_d3d12(D3D12_COMPARISON_FUNC op) +{ + switch (op) + { + case D3D12_COMPARISON_FUNC_NEVER: + return VK_COMPARE_OP_NEVER; + case D3D12_COMPARISON_FUNC_LESS: + return VK_COMPARE_OP_LESS; + case D3D12_COMPARISON_FUNC_EQUAL: + return VK_COMPARE_OP_EQUAL; + case D3D12_COMPARISON_FUNC_LESS_EQUAL: + return VK_COMPARE_OP_LESS_OR_EQUAL; + case D3D12_COMPARISON_FUNC_GREATER: + return VK_COMPARE_OP_GREATER; + case D3D12_COMPARISON_FUNC_NOT_EQUAL: + return VK_COMPARE_OP_NOT_EQUAL; + case D3D12_COMPARISON_FUNC_GREATER_EQUAL: + return VK_COMPARE_OP_GREATER_OR_EQUAL; + case D3D12_COMPARISON_FUNC_ALWAYS: + return VK_COMPARE_OP_ALWAYS; + default: + fatal_error("Unhandled compare op %#x.\n", op); + } +} + static VkDescriptorSetLayout create_descriptor_set_layout(struct vulkan_shader_runner *runner) { VkDescriptorSetLayoutCreateInfo set_desc = {.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO}; @@ -874,6 +899,8 @@ static VkDescriptorSetLayout create_descriptor_set_layout(struct vulkan_shader_r sampler_desc.addressModeU = vk_address_mode_from_d3d12(sampler->u_address); sampler_desc.addressModeV = vk_address_mode_from_d3d12(sampler->v_address); sampler_desc.addressModeW = vk_address_mode_from_d3d12(sampler->w_address); + sampler_desc.compareEnable = !!sampler->func; + sampler_desc.compareOp = sampler->func ? vk_compare_op_from_d3d12(sampler->func) : 0; sampler_desc.maxLod = FLT_MAX;
VK_CALL(vkCreateSampler(runner->device, &sampler_desc, NULL, &vulkan_sampler->vk_sampler));
This merge request was approved by Giovanni Mascellani.
Could we use a shader like this (untested) instead?: ```hlsl Texture2D<float> t : register(t0); SamplerComparisonState s : register(s0);
float ref, delta;
float4 main() : sv_target { return float4( t.SampleCmpLevelZero(s, float2(0.5, 0.5), ref - delta), t.SampleCmpLevelZero(s, float2(0.5, 0.5), ref), t.SampleCmpLevelZero(s, float2(0.5, 0.5), ref + delta), 1.0); } ```
This would allow testing three values in a single draw. Testing two values like the tests currently do isn't necessarily sufficient to distinguish the different comparison functions. For example, the current "less equal" results are indistinguishable from the "equal" results.