From ed552e45193dea1d99bb2402495002dc73958bcf Mon Sep 17 00:00:00 2001 From: Giovanni Mascellani Date: Fri, 4 Oct 2024 10:01:46 +0200 Subject: [PATCH] tests/hlsl: Add tests for the EvaluateAttribute*() intrinsics. Based on earlier tests by Conor McCarthy. --- Makefile.am | 1 + tests/hlsl/eval-attrib.shader_test | 259 +++++++++++++++++++++++++++++ tests/shader_runner.c | 2 +- tests/shader_runner.h | 2 +- tests/shader_runner_d3d12.c | 26 +-- tests/shader_runner_gl.c | 13 +- tests/shader_runner_metal.m | 9 +- tests/shader_runner_vulkan.c | 25 +-- 8 files changed, 282 insertions(+), 55 deletions(-) create mode 100644 tests/hlsl/eval-attrib.shader_test diff --git a/Makefile.am b/Makefile.am index 7dcd2468..ca47abb1 100644 --- a/Makefile.am +++ b/Makefile.am @@ -118,6 +118,7 @@ vkd3d_shader_tests = \ tests/hlsl/effect-variables-fx_4.shader_test \ tests/hlsl/effect-variables-fx_5.shader_test \ tests/hlsl/entry-point-semantics.shader_test \ + tests/hlsl/eval-attrib.shader_test \ tests/hlsl/exp.shader_test \ tests/hlsl/expr-indexing.shader_test \ tests/hlsl/f16tof32.shader_test \ diff --git a/tests/hlsl/eval-attrib.shader_test b/tests/hlsl/eval-attrib.shader_test new file mode 100644 index 00000000..ded4727a --- /dev/null +++ b/tests/hlsl/eval-attrib.shader_test @@ -0,0 +1,259 @@ +[require] +shader model >= 5.0 + +[rtv 0] +format r32g32b32a32-float +size (2dms, 4, 640, 480) + +[vertex shader] +% The pixels in the leftmost column lose their leftmost sample. +static const float epsilon = 0.25f * 2.0f / 640.0f; +static const float2 vertices[3] = +{ + {-1.0f + epsilon, 1.0f}, + { 3.0f, 1.0f}, + {-1.0f + epsilon, -3.0f}, +}; +void main(uint id : SV_VertexID, out float4 position : SV_Position, out float2 attr : ATTR) +{ + position = float4(vertices[id], 0.0f, 1.0f); + attr = float2(position.x * 320.0f + 320.0f, -position.y * 240.0f + 240.0f); +} + +% The multiplication by (sample_idx + 1) when computing the error is to +% introduce a dependency on the sample index which hopefully the compiler is not +% able to optimize; this way the shader is hopefully forced to execute for each +% sample. These tests depend on the implementation using the default sample +% positions. + +% First let's check that we're getting the position interpolation right: +% center interpolation. + +[pixel shader] +float4 main(float4 pos : SV_Position, uint sample_idx : SV_SampleIndex) : SV_Target +{ + float2 ref = floor(pos.xy) + float2(0.5f, 0.5f); + float2 diff = pos.xy - ref; + float2 err = abs(diff) * (sample_idx + 1); + return floor(1000.0f * float4(err, 0.0f, 0.0f)); +} + +[test] +clear rtv 0 1.0 1.0 1.0 1.0 +todo(msl) draw triangle list 3 +todo probe ( 0, 0) rgba(0.25, 0.25, 0.25, 0.25) +todo probe (639, 0) rgba(0.0, 0.0, 0.0, 0.0) +todo probe ( 0, 479) rgba(0.25, 0.25, 0.25, 0.25) +todo probe (639, 479) rgba(0.0, 0.0, 0.0, 0.0) + +% Centroid interpolation, which means the pixel center if all samples are +% covered and the first covered sample if a least a sample is not covered (in +% general it has nothing to do with the actual centroid of the covered samples). +% The WARP driver seems to have problems with some corner pixels, which we +% avoid. + +[pixel shader] +static const float2 positions[4] = +{ + {-0.125f, -0.375f}, + { 0.375f, -0.125f}, + {-0.375f, 0.125f}, + { 0.125f, 0.375f}, +}; +float4 main(centroid float4 pos : SV_Position, uint sample_idx : SV_SampleIndex) : SV_Target +{ + bool first_col = floor(pos.x) == 0.0f; + float2 ref = floor(pos.xy) + float2(0.5f, 0.5f); + float2 diff = pos.xy - ref; + float2 expected = first_col ? positions[0] : 0.0f; + float2 err = abs(diff - expected) * (sample_idx + 1); + return floor(1000.0f * float4(err, 0.0f, 0.0f)); +} + +[test] +clear rtv 0 1.0 1.0 1.0 1.0 +todo(glsl | msl) draw triangle list 3 +todo probe ( 0, 0) rgba(0.25, 0.25, 0.25, 0.25) +todo probe (638, 0) rgba(0.0, 0.0, 0.0, 0.0) +todo probe ( 0, 478) rgba(0.25, 0.25, 0.25, 0.25) +todo probe (639, 479) rgba(0.0, 0.0, 0.0, 0.0) + +% Sample interpolation. + +[pixel shader todo] +static const float2 positions[4] = +{ + {-0.125f, -0.375f}, + { 0.375f, -0.125f}, + {-0.375f, 0.125f}, + { 0.125f, 0.375f}, +}; +float4 main(sample float4 pos : SV_Position, uint sample_idx : SV_SampleIndex) : SV_Target +{ + float2 ref = floor(pos.xy) + float2(0.5f, 0.5f); + float2 diff = pos.xy - ref; + float2 err = abs(diff - positions[sample_idx]) * (sample_idx + 1); + return floor(1000.0f * float4(err, 0.0f, 0.0f)); +} + +[test] +clear rtv 0 1.0 1.0 1.0 1.0 +todo(sm<6) draw triangle list 3 +probe ( 0, 0) rgba(0.25, 0.25, 0.25, 0.25) +probe (639, 0) rgba(0.0, 0.0, 0.0, 0.0) +probe ( 0, 479) rgba(0.25, 0.25, 0.25, 0.25) +probe (639, 479) rgba(0.0, 0.0, 0.0, 0.0) + +% Same tests with a non-SV semantic: pixel center interpolation. + +[pixel shader] +float4 main(float4 pos : SV_Position, float2 attr : ATTR, uint sample_idx : SV_SampleIndex) : SV_Target +{ + float2 ref = floor(pos.xy) + float2(0.5f, 0.5f); + float2 diff = attr.xy - ref; + float2 err = abs(diff) * (sample_idx + 1); + return floor(1000.0f * float4(err, 0.0f, 0.0f)); +} + +[test] +clear rtv 0 1.0 1.0 1.0 1.0 +todo(msl) draw triangle list 3 +probe ( 0, 0) rgba(0.25, 0.25, 0.25, 0.25) +probe (639, 0) rgba(0.0, 0.0, 0.0, 0.0) +probe ( 0, 479) rgba(0.25, 0.25, 0.25, 0.25) +probe (639, 479) rgba(0.0, 0.0, 0.0, 0.0) + +% Centroid interpolation, which means the pixel center if all samples are +% covered and the first covered sample if a least a sample is not covered (in +% general it has nothing to do with the actual centroid of the covered samples). +% The WARP driver seems to have problems with some corner pixels, which we +% avoid. +% +% Since Vulkan has more relaxed specification for centroid interpolation than +% D3D12, not all drivers implement the behavior we need. llvmpipe is one of +% those, so is marked todo. + +[pixel shader] +static const float2 positions[4] = +{ + {-0.125f, -0.375f}, + { 0.375f, -0.125f}, + {-0.375f, 0.125f}, + { 0.125f, 0.375f}, +}; +float4 main(float4 pos : SV_Position, centroid float2 attr : ATTR, + uint sample_idx : SV_SampleIndex) : SV_Target +{ + bool first_col = floor(pos.x) == 0.0f; + float2 ref = floor(pos.xy) + float2(0.5f, 0.5f); + float2 diff = attr.xy - ref; + float2 expected = first_col ? positions[0] : 0.0f; + float2 err = abs(diff - expected) * (sample_idx + 1); + return floor(1000.0f * float4(err, 0.0f, 0.0f)); +} + +[test] +clear rtv 0 1.0 1.0 1.0 1.0 +todo(glsl | msl) draw triangle list 3 +todo(llvmpipe) probe (0, 0) rgba(0.25, 0.25, 0.25, 0.25) +probe (638, 0) rgba(0.0, 0.0, 0.0, 0.0) +probe (0, 478) rgba(0.25, 0.25, 0.25, 0.25) +todo(llvmpipe) probe (639, 479) rgba(0.0, 0.0, 0.0, 0.0) + +% Sample interpolation. + +[pixel shader todo] +static const float2 positions[4] = +{ + {-0.125f, -0.375f}, + { 0.375f, -0.125f}, + {-0.375f, 0.125f}, + { 0.125f, 0.375f}, +}; +float4 main(float4 pos : SV_Position, sample float2 attr : ATTR, + uint sample_idx : SV_SampleIndex) : SV_Target +{ + float2 ref = floor(pos.xy) + float2(0.5f, 0.5f); + float2 diff = attr.xy - ref; + float2 err = abs(diff - positions[sample_idx]) * (sample_idx + 1); + return floor(1000.0f * float4(err, 0.0f, 0.0f)); +} + +[test] +clear rtv 0 1.0 1.0 1.0 1.0 +todo(sm<6) draw triangle list 3 +probe ( 0, 0) rgba(0.25, 0.25, 0.25, 0.25) +probe (639, 0) rgba(0.0, 0.0, 0.0, 0.0) +probe ( 0, 479) rgba(0.25, 0.25, 0.25, 0.25) +probe (639, 479) rgba(0.0, 0.0, 0.0, 0.0) + +% Using EvaluateAttributeCentroid(). + +[pixel shader todo] +static const float2 positions[4] = +{ + {-0.125f, -0.375f}, + { 0.375f, -0.125f}, + {-0.375f, 0.125f}, + { 0.125f, 0.375f}, +}; +float4 main(float4 pos : SV_Position, float2 attr : ATTR, + uint sample_idx : SV_SampleIndex) : SV_Target +{ + bool first_col = floor(pos.x) == 0.0f; + float2 ref = floor(pos.xy) + float2(0.5f, 0.5f); + float2 eval = EvaluateAttributeCentroid(attr); + float2 diff = eval.xy - ref; + float2 expected = first_col ? positions[0] : 0.0f; + float2 err = abs(diff - expected) * (sample_idx + 1); + return floor(1000.0f * float4(err, 0.0f, 0.0f)); +} + +[test] +clear rtv 0 1.0 1.0 1.0 1.0 +todo draw triangle list 3 +probe ( 0, 0) rgba(0.25, 0.25, 0.25, 0.25) +probe (638, 0) rgba(0.0, 0.0, 0.0, 0.0) +probe ( 0, 478) rgba(0.25, 0.25, 0.25, 0.25) +probe (639, 479) rgba(0.0, 0.0, 0.0, 0.0) + +% Using EvaluateAttributeAtSample(). + +[pixel shader todo] +static const float2 positions[4] = +{ + {-0.125f, -0.375f}, + { 0.375f, -0.125f}, + {-0.375f, 0.125f}, + { 0.125f, 0.375f}, +}; +static const uint mixer[4] = {1, 3, 2, 0}; +float4 main(float4 pos : SV_Position, float2 attr : ATTR, + uint sample_idx : SV_SampleIndex) : SV_Target +{ + /* Use a different sample index than the one we're evaluating, just in case + * the compiler tries to cheat and do a plain sample interpolation instead + * of using the specific sample we requested. */ + float2 eval; + /* Older WARP versions do not like calling EvaluateAttributeAtSample() on + * anything that is not an immediate constant. */ + switch (mixer[sample_idx]) + { + case 0: eval = EvaluateAttributeAtSample(attr, 0); break; + case 1: eval = EvaluateAttributeAtSample(attr, 1); break; + case 2: eval = EvaluateAttributeAtSample(attr, 2); break; + case 3: eval = EvaluateAttributeAtSample(attr, 3); break; + } + float2 ref = floor(pos.xy) + float2(0.5f, 0.5f); + float2 diff = eval.xy - ref; + float2 err = abs(diff - positions[mixer[sample_idx]]) * (sample_idx + 1); + return floor(1000.0f * float4(err, 0.0f, 0.0f)); +} + +[test] +clear rtv 0 1.0 1.0 1.0 1.0 +todo draw triangle list 3 +probe ( 0, 0) rgba(0.25, 0.25, 0.25, 0.25) +probe (639, 0) rgba(0.0, 0.0, 0.0, 0.0) +probe ( 0, 479) rgba(0.25, 0.25, 0.25, 0.25) +probe (639, 479) rgba(0.0, 0.0, 0.0, 0.0) diff --git a/tests/shader_runner.c b/tests/shader_runner.c index 8b182f61..1e73cb55 100644 --- a/tests/shader_runner.c +++ b/tests/shader_runner.c @@ -143,7 +143,7 @@ static bool match_tag(struct shader_runner *runner, const char *tag) static bool check_qualifier_args_conjunction(struct shader_runner *runner, const char *line, const char **const rest, uint32_t *model_mask) { - static const char *const valid_tags[] = {"d3d12", "glsl", "msl", "mvk", "vulkan"}; + static const char *const valid_tags[] = {"d3d12", "glsl", "llvmpipe", "msl", "mvk", "vulkan"}; bool holds = true; *model_mask = ~0u; diff --git a/tests/shader_runner.h b/tests/shader_runner.h index 885fcf9b..f64bc8c0 100644 --- a/tests/shader_runner.h +++ b/tests/shader_runner.h @@ -155,7 +155,7 @@ enum shader_cap struct shader_runner_caps { const char *runner; - const char *const *tags; + const char *tags[2]; size_t tag_count; enum shader_model minimum_shader_model; enum shader_model maximum_shader_model; diff --git a/tests/shader_runner_d3d12.c b/tests/shader_runner_d3d12.c index 89c16627..15188801 100644 --- a/tests/shader_runner_d3d12.c +++ b/tests/shader_runner_d3d12.c @@ -991,17 +991,6 @@ static void d3d12_runner_init_caps(struct d3d12_shader_runner *runner, D3D12_FEATURE_DATA_D3D12_OPTIONS options; HRESULT hr; - static const char *const tags[] = - { - "d3d12", - }; - - static const char *const mvk_tags[] = - { - "d3d12", - "mvk", - }; - static const enum DXGI_FORMAT formats[] = { DXGI_FORMAT_R32_FLOAT, @@ -1046,16 +1035,13 @@ static void d3d12_runner_init_caps(struct d3d12_shader_runner *runner, runner->caps.shader_caps[SHADER_CAP_ROV] = options.ROVsSupported; runner->caps.shader_caps[SHADER_CAP_WAVE_OPS] = options1.WaveOps; + runner->caps.tag_count = 0; + runner->caps.tags[runner->caps.tag_count++] = "d3d12"; + if (is_mvk_device(device)) - { - runner->caps.tags = mvk_tags; - runner->caps.tag_count = ARRAY_SIZE(mvk_tags); - } - else - { - runner->caps.tags = tags; - runner->caps.tag_count = ARRAY_SIZE(tags); - } + runner->caps.tags[runner->caps.tag_count++] = "mvk"; + else if (is_llvmpipe_device(device)) + runner->caps.tags[runner->caps.tag_count++] = "llvmpipe"; for (unsigned int i = 0; i < ARRAY_SIZE(formats); ++i) { diff --git a/tests/shader_runner_gl.c b/tests/shader_runner_gl.c index ba2a5e7d..f51a9655 100644 --- a/tests/shader_runner_gl.c +++ b/tests/shader_runner_gl.c @@ -237,11 +237,6 @@ static bool gl_runner_init(struct gl_runner *runner, enum shading_language langu EGLBoolean ret; GLuint vao; - static const char *const tags[] = - { - "glsl", - }; - static const EGLint attributes[] = { EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT, @@ -347,12 +342,16 @@ static bool gl_runner_init(struct gl_runner *runner, enum shading_language langu continue; } runner->caps.runner = language == SPIR_V ? "OpenGL/SPIR-V" : "OpenGL/GLSL"; - runner->caps.tags = tags; - runner->caps.tag_count = runner->language == GLSL; runner->caps.minimum_shader_model = SHADER_MODEL_4_0; runner->caps.maximum_shader_model = SHADER_MODEL_5_1; runner->caps.shader_caps[SHADER_CAP_GEOMETRY_SHADER] = true; + runner->caps.tag_count = 0; + if (runner->language == GLSL) + runner->caps.tags[runner->caps.tag_count++] = "glsl"; + if (strncmp((const char *)glGetString(GL_RENDERER), "llvmpipe ", 9) == 0) + runner->caps.tags[runner->caps.tag_count++] = "llvmpipe"; + glGetIntegerv(GL_NUM_EXTENSIONS, &extension_count); if (check_gl_extension("GL_ARB_internalformat_query2", extension_count)) { diff --git a/tests/shader_runner_metal.m b/tests/shader_runner_metal.m index 70c9a6ed..233a8424 100644 --- a/tests/shader_runner_metal.m +++ b/tests/shader_runner_metal.m @@ -675,11 +675,6 @@ static bool metal_runner_init(struct metal_runner *runner) NSArray> *devices; id device; - static const char *const tags[] = - { - "msl", - }; - if (!check_msl_support()) { skip("MSL support is not enabled. If this is unintentional, " @@ -717,8 +712,8 @@ static bool metal_runner_init(struct metal_runner *runner) } runner->caps.runner = "Metal"; - runner->caps.tags = tags; - runner->caps.tag_count = ARRAY_SIZE(tags); + runner->caps.tags[0] = "msl"; + runner->caps.tag_count = 1; runner->caps.minimum_shader_model = SHADER_MODEL_4_0; runner->caps.maximum_shader_model = SHADER_MODEL_5_0; diff --git a/tests/shader_runner_vulkan.c b/tests/shader_runner_vulkan.c index 0b1be984..28391ba1 100644 --- a/tests/shader_runner_vulkan.c +++ b/tests/shader_runner_vulkan.c @@ -1685,16 +1685,6 @@ static bool init_vulkan_runner(struct vulkan_shader_runner *runner) DXGI_FORMAT_R8_UINT, DXGI_FORMAT_R8_SINT, }; - static const char *const tags[] = - { - "vulkan", - }; - - static const char *const mvk_tags[] = - { - "vulkan", - "mvk", - }; if (!vulkan_test_context_init_instance(context, instance_extensions, ARRAY_SIZE(instance_extensions))) return false; @@ -1728,16 +1718,13 @@ static bool init_vulkan_runner(struct vulkan_shader_runner *runner) get_physical_device_info(runner, &device_info); ret_features = &device_info.features2.features; + runner->caps.tag_count = 0; + runner->caps.tags[runner->caps.tag_count++] = "vulkan"; + if (device_info.driver_properties.driverID == VK_DRIVER_ID_MOLTENVK) - { - runner->caps.tags = mvk_tags; - runner->caps.tag_count = ARRAY_SIZE(mvk_tags); - } - else - { - runner->caps.tags = tags; - runner->caps.tag_count = ARRAY_SIZE(tags); - } + runner->caps.tags[runner->caps.tag_count++] = "mvk"; + else if (device_info.driver_properties.driverID == VK_DRIVER_ID_MESA_LLVMPIPE) + runner->caps.tags[runner->caps.tag_count++] = "llvmpipe"; runner->caps.shader_caps[SHADER_CAP_CLIP_PLANES] = true; runner->caps.shader_caps[SHADER_CAP_FOG] = true;