tests/hlsl: Add tests for the EvaluateAttribute*() intrinsics.

Based on earlier tests by Conor McCarthy.
This commit is contained in:
Giovanni Mascellani 2024-10-04 10:01:46 +02:00 committed by Henri Verbeet
parent a68fd1b0de
commit ed552e4519
Notes: Henri Verbeet 2024-12-18 17:38:37 +01:00
Approved-by: Giovanni Mascellani (@giomasce)
Approved-by: Henri Verbeet (@hverbeet)
Merge-Request: https://gitlab.winehq.org/wine/vkd3d/-/merge_requests/806
8 changed files with 282 additions and 55 deletions

View File

@ -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 \

View File

@ -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)

View File

@ -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;

View File

@ -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;

View File

@ -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)
{

View File

@ -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))
{

View File

@ -675,11 +675,6 @@ static bool metal_runner_init(struct metal_runner *runner)
NSArray<id<MTLDevice>> *devices;
id<MTLDevice> 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;

View File

@ -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;