For compatibility with shader models before 4.0.
Signed-off-by: Zebediah Figura zfigura@codeweavers.com --- tests/shader_runner.c | 71 +++++++++++++++++++++++++++++-------------- 1 file changed, 49 insertions(+), 22 deletions(-)
diff --git a/tests/shader_runner.c b/tests/shader_runner.c index d3d131164..0ad2d56c7 100644 --- a/tests/shader_runner.c +++ b/tests/shader_runner.c @@ -314,6 +314,24 @@ static void parse_input_layout_directive(struct shader_runner *runner, const cha element->index = 0; }
+static void set_resource(struct shader_runner *runner, struct resource *resource) +{ + size_t i; + + for (i = 0; i < runner->resource_count; ++i) + { + if (runner->resources[i]->slot == resource->slot && runner->resources[i]->type == resource->type) + { + runner->ops->destroy_resource(runner, runner->resources[i]); + runner->resources[i] = resource; + return; + } + } + + runner->resources = realloc(runner->resources, (runner->resource_count + 1) * sizeof(*runner->resources)); + runner->resources[runner->resource_count++] = resource; +} + static void set_uniforms(struct shader_runner *runner, size_t offset, size_t count, const void *uniforms) { runner->uniform_count = align(max(runner->uniform_count, offset + count), 4); @@ -326,17 +344,44 @@ static void parse_test_directive(struct shader_runner *runner, const char *line) { if (match_string(line, "draw quad", &line)) { + struct resource_params params; + struct input_element *element; + + static const struct vec2 quad[] = + { + {-1.0f, -1.0f}, + {-1.0f, 1.0f}, + { 1.0f, -1.0f}, + { 1.0f, 1.0f}, + }; + static const char vs_source[] = - "void main(uint id : SV_VertexID, out float4 position : SV_Position)\n" + "void main(inout float4 position : sv_position)\n" "{\n" - " float2 coords = float2((id << 1) & 2, id & 2);\n" - " position = float4(coords * float2(2, -2) + float2(-1, 1), 0, 1);\n" "}";
+ vkd3d_array_reserve((void **)&runner->input_elements, &runner->input_element_capacity, + 1, sizeof(*runner->input_elements)); + element = &runner->input_elements[0]; + element->name = strdup("sv_position"); + element->slot = 0; + element->format = DXGI_FORMAT_R32G32_FLOAT; + element->texel_size = sizeof(*quad); + element->index = 0; + runner->input_element_count = 1; + + memset(¶ms, 0, sizeof(params)); + params.slot = 0; + params.type = RESOURCE_TYPE_VERTEX_BUFFER; + params.data = malloc(sizeof(quad)); + memcpy(params.data, quad, sizeof(quad)); + params.data_size = sizeof(quad); + set_resource(runner, runner->ops->create_resource(runner, ¶ms)); + if (!runner->vs_source) runner->vs_source = strdup(vs_source);
- runner->ops->draw(runner, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST, 3); + runner->ops->draw(runner, D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP, 4); } else if (match_string(line, "draw", &line)) { @@ -482,24 +527,6 @@ static struct sampler *get_sampler(struct shader_runner *runner, unsigned int sl return NULL; }
-static void set_resource(struct shader_runner *runner, struct resource *resource) -{ - size_t i; - - for (i = 0; i < runner->resource_count; ++i) - { - if (runner->resources[i]->slot == resource->slot && runner->resources[i]->type == resource->type) - { - runner->ops->destroy_resource(runner, runner->resources[i]); - runner->resources[i] = resource; - return; - } - } - - runner->resources = realloc(runner->resources, (runner->resource_count + 1) * sizeof(*runner->resources)); - runner->resources[runner->resource_count++] = resource; -} - unsigned int get_vb_stride(const struct shader_runner *runner, unsigned int slot) { unsigned int stride = 0;
Signed-off-by: Zebediah Figura zfigura@codeweavers.com --- tests/shader_runner.c | 7 ++++++- tests/shader_runner.h | 3 +++ 2 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/tests/shader_runner.c b/tests/shader_runner.c index 0ad2d56c7..bcf863f66 100644 --- a/tests/shader_runner.c +++ b/tests/shader_runner.c @@ -595,11 +595,15 @@ void run_shader_tests(struct shader_runner *runner, int argc, char **argv, const { case STATE_INPUT_LAYOUT: case STATE_NONE: - case STATE_REQUIRE: case STATE_SAMPLER: case STATE_TEST: break;
+ case STATE_REQUIRE: + if (runner->ops->check_requirements && !runner->ops->check_requirements(runner)) + goto out; + break; + case STATE_TEXTURE: case STATE_VERTEX_BUFFER: set_resource(runner, runner->ops->create_resource(runner, ¤t_resource)); @@ -832,6 +836,7 @@ void run_shader_tests(struct shader_runner *runner, int argc, char **argv, const } }
+out: for (i = 0; i < runner->input_element_count; ++i) free(runner->input_elements[i].name); free(runner->input_elements); diff --git a/tests/shader_runner.h b/tests/shader_runner.h index a98917c5f..a304ba919 100644 --- a/tests/shader_runner.h +++ b/tests/shader_runner.h @@ -110,6 +110,9 @@ struct shader_runner
struct shader_runner_ops { + /* Returns false if unable to run the given tests. If NULL, all tests are + * run. */ + bool (*check_requirements)(struct shader_runner *runner); struct resource *(*create_resource)(struct shader_runner *runner, const struct resource_params *params); void (*destroy_resource)(struct shader_runner *runner, struct resource *resource); void (*draw)(struct shader_runner *runner, D3D_PRIMITIVE_TOPOLOGY primitive_topology, unsigned int vertex_count);
On Wed, 6 Apr 2022 at 23:05, Zebediah Figura zfigura@codeweavers.com wrote:
case STATE_REQUIRE:
if (runner->ops->check_requirements && !runner->ops->check_requirements(runner))
goto out;
break;
Should we log a message when we skip a test?
On 4/7/22 08:07, Henri Verbeet wrote:
On Wed, 6 Apr 2022 at 23:05, Zebediah Figura zfigura@codeweavers.com wrote:
case STATE_REQUIRE:
if (runner->ops->check_requirements && !runner->ops->check_requirements(runner))
goto out;
break;
Should we log a message when we skip a test?
I don't think it makes sense to log a message here. My impression, although it may not be the consensus, is that skips are only useful when it reveals something about the user's environment that might not be true of other environments. In the case of a d3d9 shader runner, these tests will always be skipped.
On Thu, 7 Apr 2022 at 20:02, Zebediah Figura zfigura@codeweavers.com wrote:
On 4/7/22 08:07, Henri Verbeet wrote:
On Wed, 6 Apr 2022 at 23:05, Zebediah Figura zfigura@codeweavers.com wrote:
case STATE_REQUIRE:
if (runner->ops->check_requirements && !runner->ops->check_requirements(runner))
goto out;
break;
Should we log a message when we skip a test?
I don't think it makes sense to log a message here. My impression, although it may not be the consensus, is that skips are only useful when it reveals something about the user's environment that might not be true of other environments. In the case of a d3d9 shader runner, these tests will always be skipped.
In the case where the requirement is shader model 4 in combination with the d3d9 runner, sure. That wouldn't necessarily be true for a hypothetical Vulkan or OpenGL runner. Even for d3d9/11/12, it shouldn't be too hard to imagine caps (e.g. format capabilities) a particular test may depend on. Either way, I don't feel strongly about this at this point.
On 4/7/22 13:18, Henri Verbeet wrote:
On Thu, 7 Apr 2022 at 20:02, Zebediah Figura zfigura@codeweavers.com wrote:
On 4/7/22 08:07, Henri Verbeet wrote:
On Wed, 6 Apr 2022 at 23:05, Zebediah Figura zfigura@codeweavers.com wrote:
case STATE_REQUIRE:
if (runner->ops->check_requirements && !runner->ops->check_requirements(runner))
goto out;
break;
Should we log a message when we skip a test?
I don't think it makes sense to log a message here. My impression, although it may not be the consensus, is that skips are only useful when it reveals something about the user's environment that might not be true of other environments. In the case of a d3d9 shader runner, these tests will always be skipped.
In the case where the requirement is shader model 4 in combination with the d3d9 runner, sure. That wouldn't necessarily be true for a hypothetical Vulkan or OpenGL runner. Even for d3d9/11/12, it shouldn't be too hard to imagine caps (e.g. format capabilities) a particular test may depend on. Either way, I don't feel strongly about this at this point.
Right. I imagine that we should print a skip message in the check_requirements callback in that case.
Signed-off-by: Zebediah Figura zfigura@codeweavers.com --- tests/conditional.shader_test | 11 +++++--- tests/hlsl-for.shader_test | 17 +++++++----- tests/hlsl-struct-semantics.shader_test | 35 ++++++++++++++++++++----- tests/trigonometry.shader_test | 31 +++++++++------------- 4 files changed, 60 insertions(+), 34 deletions(-)
diff --git a/tests/conditional.shader_test b/tests/conditional.shader_test index 70718f117..377ec0cb8 100644 --- a/tests/conditional.shader_test +++ b/tests/conditional.shader_test @@ -1,13 +1,16 @@ [pixel shader] -float4 main(float4 pos : SV_POSITION) : SV_TARGET +float4 main(uniform float u) : SV_TARGET { - if(pos.x > 200.0) + if (u > 200.0) return float4(0.1, 0.2, 0.3, 0.4); else return float4(0.9, 0.8, 0.7, 0.6); }
[test] +uniform 0 float 123.0 draw quad -probe rect rgba (0, 0, 200, 480) (0.9, 0.8, 0.7, 0.6) -probe rect rgba (200, 0, 440, 480) (0.1, 0.2, 0.3, 0.4) +probe all rgba (0.9, 0.8, 0.7, 0.6) +uniform 0 float 456.0 +draw quad +probe all rgba (0.1, 0.2, 0.3, 0.4) diff --git a/tests/hlsl-for.shader_test b/tests/hlsl-for.shader_test index b133ce996..14a23d6e2 100644 --- a/tests/hlsl-for.shader_test +++ b/tests/hlsl-for.shader_test @@ -1,14 +1,14 @@ [pixel shader] -float4 main(float4 pos : SV_POSITION) : SV_TARGET +float4 main(uniform float u) : SV_TARGET { int i; float x = 0.0; for (i = 0; i < 10; i++) { x += i; - if (pos.x == 1.5 && i == 5) + if (u == 1.0 && i == 5) break; - if (pos.x == 2.5 && i >= 7) + if (u == 2.0 && i >= 7) continue; x -= 1; } @@ -16,7 +16,12 @@ float4 main(float4 pos : SV_POSITION) : SV_TARGET }
[test] +uniform 0 float 0 draw quad -probe rgba (0, 0) (10.0, 35.0, 0.0, 0.0) -probe rgba (1, 0) (5.0, 10.0, 0.0, 0.0) -probe rgba (2, 0) (10.0, 38.0, 0.0, 0.0) +probe all rgba (10.0, 35.0, 0.0, 0.0) +uniform 0 float 1 +draw quad +probe all rgba (5.0, 10.0, 0.0, 0.0) +uniform 0 float 2 +draw quad +probe all rgba (10.0, 38.0, 0.0, 0.0) diff --git a/tests/hlsl-struct-semantics.shader_test b/tests/hlsl-struct-semantics.shader_test index b623a998c..c1dd87fe6 100644 --- a/tests/hlsl-struct-semantics.shader_test +++ b/tests/hlsl-struct-semantics.shader_test @@ -1,9 +1,34 @@ +[input layout] +0 r32g32b32a32 float texcoord +0 r32g32 float sv_position + +[vertex buffer 0] +0.0 1.0 0.0 1.0 -1.0 -1.0 +0.0 1.0 0.0 1.0 -1.0 1.0 +0.0 1.0 0.0 1.0 1.0 -1.0 +0.0 1.0 0.0 1.0 1.0 1.0 + +[vertex shader] + +struct vertex +{ + struct + { + float4 texcoord : texcoord; + float4 pos : sv_position; + } m; +}; + +void main(inout struct vertex v) +{ +} + [pixel shader] struct input { struct { - float4 pos : sv_position; + float4 texcoord : texcoord; } m; };
@@ -18,12 +43,10 @@ struct output struct output main(struct input i) { struct output o; - o.m.color = i.m.pos; + o.m.color = i.m.texcoord; return o; }
[test] -draw quad -probe rgba (0, 1) (0.5, 1.5, 0.0, 1.0) -probe rgba (1, 0) (1.5, 0.5, 0.0, 1.0) -probe rgba (3, 5) (3.5, 5.5, 0.0, 1.0) +draw triangle strip 4 +probe all rgba (0.0, 1.0, 0.0, 1.0) diff --git a/tests/trigonometry.shader_test b/tests/trigonometry.shader_test index 07a280a02..c8c672fe7 100644 --- a/tests/trigonometry.shader_test +++ b/tests/trigonometry.shader_test @@ -1,24 +1,19 @@ [pixel shader] -float4 main(float4 pos : SV_POSITION) : SV_TARGET +float4 main(uniform float f) : SV_TARGET { - return float4(sin(pos.x - 0.5), cos(pos.x - 0.5), 0, 0); + return float4(sin(f), cos(f), sin(f + 1.0), cos(f + 1.0)); }
[test] +uniform 0 float 0.0 draw quad -probe rgba ( 0, 0) ( 0.00000000, 1.00000000, 0.0, 0.0) -probe rgba ( 1, 0) ( 0.84147098, 0.54030231, 0.0, 0.0) 1024 -probe rgba ( 2, 0) ( 0.90929743, -0.41614684, 0.0, 0.0) 1024 -probe rgba ( 3, 0) ( 0.14112001, -0.98999250, 0.0, 0.0) 1024 -probe rgba ( 4, 0) (-0.75680250, -0.65364362, 0.0, 0.0) 1024 -probe rgba ( 5, 0) (-0.95892427, 0.28366219, 0.0, 0.0) 1024 -probe rgba ( 6, 0) (-0.27941550, 0.96017029, 0.0, 0.0) 1024 -probe rgba ( 7, 0) ( 0.65698660, 0.75390225, 0.0, 0.0) 1024 -probe rgba ( 8, 0) ( 0.98935825, -0.14550003, 0.0, 0.0) 1024 -probe rgba ( 9, 0) ( 0.41211849, -0.91113026, 0.0, 0.0) 1024 -probe rgba (10, 0) (-0.54402111, -0.83907153, 0.0, 0.0) 1024 -probe rgba (11, 0) (-0.99999021, 0.00442570, 0.0, 0.0) 1024 -probe rgba (12, 0) (-0.53657292, 0.84385396, 0.0, 0.0) 1024 -probe rgba (13, 0) ( 0.42016704, 0.90744678, 0.0, 0.0) 1024 -probe rgba (14, 0) ( 0.99060736, 0.13673722, 0.0, 0.0) 1024 -probe rgba (15, 0) ( 0.65028784, -0.75968791, 0.0, 0.0) 1024 +probe all rgba ( 0.00000000, 1.00000000, 0.84147098, 0.54030231) 1024 +uniform 0 float 2.0 +draw quad +probe all rgba ( 0.90929743, -0.41614684, 0.14112001, -0.98999250) 1024 +uniform 0 float 4.0 +draw quad +probe all rgba (-0.75680250, -0.65364362, -0.95892427, 0.28366219) 1024 +uniform 0 float 6.0 +draw quad +probe all rgba (-0.27941550, 0.96017029, 0.65698660, 0.75390225) 1024
On Wed, 6 Apr 2022 at 23:05, Zebediah Figura zfigura@codeweavers.com wrote:
Signed-off-by: Zebediah Figura zfigura@codeweavers.com
tests/conditional.shader_test | 11 +++++--- tests/hlsl-for.shader_test | 17 +++++++----- tests/hlsl-struct-semantics.shader_test | 35 ++++++++++++++++++++----- tests/trigonometry.shader_test | 31 +++++++++------------- 4 files changed, 60 insertions(+), 34 deletions(-)
Using uniforms works of course, but an alternative worth considering is using texture coordinates. That would allow the structure of these tests to remain otherwise unmodified.
On 4/7/22 08:07, Henri Verbeet wrote:
On Wed, 6 Apr 2022 at 23:05, Zebediah Figura zfigura@codeweavers.com wrote:
Signed-off-by: Zebediah Figura zfigura@codeweavers.com
tests/conditional.shader_test | 11 +++++--- tests/hlsl-for.shader_test | 17 +++++++----- tests/hlsl-struct-semantics.shader_test | 35 ++++++++++++++++++++----- tests/trigonometry.shader_test | 31 +++++++++------------- 4 files changed, 60 insertions(+), 34 deletions(-)
Using uniforms works of course, but an alternative worth considering is using texture coordinates. That would allow the structure of these tests to remain otherwise unmodified.
I believe I had tried that at some point and found it difficult, but maybe that was before I encoded support for custom input layouts into the shader runner. I'll look at it again...
On Thu, 7 Apr 2022 at 20:03, Zebediah Figura zfigura@codeweavers.com wrote:
On 4/7/22 08:07, Henri Verbeet wrote:
On Wed, 6 Apr 2022 at 23:05, Zebediah Figura zfigura@codeweavers.com wrote:
Signed-off-by: Zebediah Figura zfigura@codeweavers.com
tests/conditional.shader_test | 11 +++++--- tests/hlsl-for.shader_test | 17 +++++++----- tests/hlsl-struct-semantics.shader_test | 35 ++++++++++++++++++++----- tests/trigonometry.shader_test | 31 +++++++++------------- 4 files changed, 60 insertions(+), 34 deletions(-)
Using uniforms works of course, but an alternative worth considering is using texture coordinates. That would allow the structure of these tests to remain otherwise unmodified.
I believe I had tried that at some point and found it difficult, but maybe that was before I encoded support for custom input layouts into the shader runner. I'll look at it again...
In principle it should be a matter of adding texture coordinates to the vertex buffer in patch 1/5, and then emitting them from the vertex shader. We'd also get normalised coordinates in the pixel shader instead of render target coordinates, but that's a relatively minor difference.
On 4/7/22 13:11, Henri Verbeet wrote:
On Thu, 7 Apr 2022 at 20:03, Zebediah Figura zfigura@codeweavers.com wrote:
On 4/7/22 08:07, Henri Verbeet wrote:
On Wed, 6 Apr 2022 at 23:05, Zebediah Figura zfigura@codeweavers.com wrote:
Signed-off-by: Zebediah Figura zfigura@codeweavers.com
tests/conditional.shader_test | 11 +++++--- tests/hlsl-for.shader_test | 17 +++++++----- tests/hlsl-struct-semantics.shader_test | 35 ++++++++++++++++++++----- tests/trigonometry.shader_test | 31 +++++++++------------- 4 files changed, 60 insertions(+), 34 deletions(-)
Using uniforms works of course, but an alternative worth considering is using texture coordinates. That would allow the structure of these tests to remain otherwise unmodified.
I believe I had tried that at some point and found it difficult, but maybe that was before I encoded support for custom input layouts into the shader runner. I'll look at it again...
In principle it should be a matter of adding texture coordinates to the vertex buffer in patch 1/5, and then emitting them from the vertex shader. We'd also get normalised coordinates in the pixel shader instead of render target coordinates, but that's a relatively minor difference.
Ah, now I remember. The problem ends up being a difference in pixel center. E.g. one can do something like this for the trigonometry test:
[vertex shader] void main(out float tex : texcoord, inout float4 pos : sv_position) { tex = (pos.x + 1) * 320; }
[pixel shader] float4 main(float tex : texcoord) : sv_target { return float4(sin(tex), cos(tex), 0, 0); }
and the results will remain about the same (modulo measuring error) for d3d9, but will be different for d3d11/d3d12 due to the half-pixel offset.
I suppose we could manually offset vertex position in the shader runner backend...
On Thu, 7 Apr 2022 at 21:14, Zebediah Figura zfigura@codeweavers.com wrote:
Ah, now I remember. The problem ends up being a difference in pixel center. E.g. one can do something like this for the trigonometry test:
[vertex shader] void main(out float tex : texcoord, inout float4 pos : sv_position) { tex = (pos.x + 1) * 320; }
[pixel shader] float4 main(float tex : texcoord) : sv_target { return float4(sin(tex), cos(tex), 0, 0); }
and the results will remain about the same (modulo measuring error) for d3d9, but will be different for d3d11/d3d12 due to the half-pixel offset.
I suppose we could manually offset vertex position in the shader runner backend...
Actually, we should do that in order to properly draw screen-aligned quads with d3d9. Otherwise these quads are going to only partially cover the pixels at their edges.
Signed-off-by: Zebediah Figura zfigura@codeweavers.com --- tests/hlsl-struct-array.shader_test | 3 +++ tests/texture-load-typed.shader_test | 3 +++ tests/texture-load.shader_test | 3 +++ 3 files changed, 9 insertions(+)
diff --git a/tests/hlsl-struct-array.shader_test b/tests/hlsl-struct-array.shader_test index a5df9cdbf..aff0a677a 100644 --- a/tests/hlsl-struct-array.shader_test +++ b/tests/hlsl-struct-array.shader_test @@ -1,5 +1,8 @@ % In SM4, array elements are always aligned to the next vec4, although structs are not.
+[require] +shader model >= 4.0 + [pixel shader] uniform struct { diff --git a/tests/texture-load-typed.shader_test b/tests/texture-load-typed.shader_test index c0c3568a8..c92273373 100644 --- a/tests/texture-load-typed.shader_test +++ b/tests/texture-load-typed.shader_test @@ -1,3 +1,6 @@ +[require] +shader model >= 4.0 + [pixel shader fail] texture<float> t;
diff --git a/tests/texture-load.shader_test b/tests/texture-load.shader_test index 86b36af49..951ee3ead 100644 --- a/tests/texture-load.shader_test +++ b/tests/texture-load.shader_test @@ -1,3 +1,6 @@ +[require] +shader model >= 4.0 + [texture 0] size (2, 2) 0.1 0.2 0.3 0.4 0.5 0.7 0.6 0.8
Signed-off-by: Henri Verbeet hverbeet@codeweavers.com
Signed-off-by: Zebediah Figura zfigura@codeweavers.com --- Makefile.am | 1 + tests/shader_runner.c | 2 +- tests/shader_runner.h | 1 + tests/shader_runner_d3d9.c | 546 +++++++++++++++++++++++++++++++++++++ 4 files changed, 549 insertions(+), 1 deletion(-) create mode 100644 tests/shader_runner_d3d9.c
diff --git a/Makefile.am b/Makefile.am index f49f96426..1e916ee45 100644 --- a/Makefile.am +++ b/Makefile.am @@ -438,6 +438,7 @@ EXTRA_DIST += $(cross_implibs:=.cross32.def) $(cross_implibs:=.cross64.def)
shader_runner_cross_sources = \ $(srcdir)/tests/shader_runner.c \ + $(srcdir)/tests/shader_runner_d3d9.c \ $(srcdir)/tests/shader_runner_d3d11.c \ $(srcdir)/tests/shader_runner_d3d12.c
diff --git a/tests/shader_runner.c b/tests/shader_runner.c index bcf863f66..6fb7a6adc 100644 --- a/tests/shader_runner.c +++ b/tests/shader_runner.c @@ -58,7 +58,6 @@ typedef int HRESULT; #include "vkd3d_windows.h" #include "vkd3d_d3dcommon.h" #include "vkd3d_d3dcompiler.h" -#include "vkd3d_common.h" #include "vkd3d_test.h" #include "shader_runner.h"
@@ -857,6 +856,7 @@ out: START_TEST(shader_runner) { #ifdef _WIN32 + run_shader_tests_d3d9(argc, argv); run_shader_tests_d3d11(argc, argv); #endif run_shader_tests_d3d12(argc, argv); diff --git a/tests/shader_runner.h b/tests/shader_runner.h index a304ba919..8dc77673b 100644 --- a/tests/shader_runner.h +++ b/tests/shader_runner.h @@ -126,6 +126,7 @@ unsigned int get_vb_stride(const struct shader_runner *runner, unsigned int slot void run_shader_tests(struct shader_runner *runner, int argc, char **argv, const struct shader_runner_ops *ops);
#ifdef _WIN32 +void run_shader_tests_d3d9(int argc, char **argv); void run_shader_tests_d3d11(int argc, char **argv); #endif void run_shader_tests_d3d12(int argc, char **argv); diff --git a/tests/shader_runner_d3d9.c b/tests/shader_runner_d3d9.c new file mode 100644 index 000000000..a4e8938ad --- /dev/null +++ b/tests/shader_runner_d3d9.c @@ -0,0 +1,546 @@ +/* + * Copyright 2021 Zebediah Figura for CodeWeavers + * + * 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 COBJMACROS +#define CONST_VTABLE +#define VKD3D_TEST_NO_DEFS +#include <d3d9.h> +#include "vkd3d_d3dcommon.h" +#include "vkd3d_d3dcompiler.h" +#include "shader_runner.h" +#include "vkd3d_test.h" + +struct d3d9_resource +{ + struct resource r; + + IDirect3DTexture9 *texture; + IDirect3DVertexBuffer9 *vb; +}; + +static struct d3d9_resource *d3d9_resource(struct resource *r) +{ + return CONTAINING_RECORD(r, struct d3d9_resource, r); +} + +struct d3d9_shader_runner +{ + struct shader_runner r; + + IDirect3DDevice9 *device; + IDirect3DSurface9 *rt; + HWND window; +}; + +static struct d3d9_shader_runner *d3d9_shader_runner(struct shader_runner *r) +{ + return CONTAINING_RECORD(r, struct d3d9_shader_runner, r); +} + +static IDirect3D9 *(WINAPI *pDirect3DCreate9)(UINT sdk_version); + +static unsigned int use_adapter_idx; + +static ID3D10Blob *compile_shader(const char *source, const char *profile) +{ + ID3D10Blob *blob = NULL, *errors = NULL; + HRESULT hr; + + hr = D3DCompile(source, strlen(source), NULL, NULL, NULL, "main", profile, 0, 0, &blob, &errors); + ok(hr == S_OK, "Failed to compile shader, hr %#lx.\n", hr); + if (errors) + { + if (vkd3d_test_state.debug_level) + trace("%s\n", (char *)ID3D10Blob_GetBufferPointer(errors)); + ID3D10Blob_Release(errors); + } + return blob; +} + +static void parse_args(int argc, char **argv) +{ + unsigned int i; + + for (i = 1; i < argc; ++i) + { + if (!strcmp(argv[i], "--adapter") && i + 1 < argc) + use_adapter_idx = atoi(argv[++i]); + } +} + +static void init_adapter_info(void) +{ + D3DADAPTER_IDENTIFIER9 identifier; + IDirect3D9 *d3d; + HRESULT hr; + + d3d = pDirect3DCreate9(D3D_SDK_VERSION); + ok(!!d3d, "Failed to create a D3D object.\n"); + + hr = IDirect3D9_GetAdapterIdentifier(d3d, use_adapter_idx, 0, &identifier); + ok(hr == S_OK, "Failed to get adapter identifier, hr %#lx.\n", hr); + + trace("Driver string: %s.\n", identifier.Driver); + trace("Device: %s, %04lx:%04lx.\n", identifier.Description, identifier.VendorId, identifier.DeviceId); + + if (identifier.VendorId == 0x1414 && identifier.DeviceId == 0x008c) + trace("Using WARP device.\n"); + + IDirect3D9_Release(d3d); +} + +static bool init_test_context(struct d3d9_shader_runner *runner) +{ + D3DPRESENT_PARAMETERS present_parameters = + { + .Windowed = TRUE, + .SwapEffect = D3DSWAPEFFECT_DISCARD, + .BackBufferWidth = RENDER_TARGET_WIDTH, + .BackBufferHeight = RENDER_TARGET_HEIGHT, + .BackBufferFormat = D3DFMT_A8R8G8B8, + }; + RECT rect = {0, 0, RENDER_TARGET_WIDTH, RENDER_TARGET_HEIGHT}; + IDirect3D9 *d3d; + D3DCAPS9 caps; + HRESULT hr; + + memset(runner, 0, sizeof(*runner)); + + AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, FALSE); + runner->window = CreateWindowA("static", "d3dcompiler_test", WS_OVERLAPPEDWINDOW, + 0, 0, rect.right - rect.left, rect.bottom - rect.top, NULL, NULL, NULL, NULL); + ok(!!runner->window, "Failed to create a window.\n"); + + d3d = pDirect3DCreate9(D3D_SDK_VERSION); + ok(!!d3d, "Failed to create a D3D object.\n"); + + present_parameters.hDeviceWindow = runner->window; + + hr = IDirect3D9_CreateDevice(d3d, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, runner->window, + D3DCREATE_HARDWARE_VERTEXPROCESSING, &present_parameters, &runner->device); + IDirect3D9_Release(d3d); + if (FAILED(hr)) + { + skip("Failed to create a 3D device, hr %#lx.\n", hr); + DestroyWindow(runner->window); + return false; + } + + hr = IDirect3DDevice9_GetDeviceCaps(runner->device, &caps); + ok(hr == D3D_OK, "Failed to get device caps, hr %#lx.\n", hr); + if (caps.PixelShaderVersion < D3DPS_VERSION(2, 0) || caps.VertexShaderVersion < D3DVS_VERSION(2, 0)) + { + skip("No shader model 2 support.\n"); + IDirect3DDevice9_Release(runner->device); + DestroyWindow(runner->window); + return false; + } + + if (FAILED(hr = IDirect3DDevice9_CreateRenderTarget(runner->device, RENDER_TARGET_WIDTH, RENDER_TARGET_HEIGHT, + D3DFMT_A32B32G32R32F, D3DMULTISAMPLE_NONE, 0, FALSE, &runner->rt, NULL))) + { + skip("Failed to create an A32B32G32R32F surface, hr %#lx.\n", hr); + IDirect3DDevice9_Release(runner->device); + DestroyWindow(runner->window); + return false; + } + ok(hr == D3D_OK, "Got unexpected hr %#lx.\n", hr); + hr = IDirect3DDevice9_SetRenderTarget(runner->device, 0, runner->rt); + ok(hr == D3D_OK, "Failed to set render target, hr %#lx.\n", hr); + + return true; +} + +static void destroy_test_context(struct d3d9_shader_runner *runner) +{ + ULONG ref; + + IDirect3DSurface9_Release(runner->rt); + ref = IDirect3DDevice9_Release(runner->device); + ok(!ref, "Device has %lu references left.\n", ref); + DestroyWindow(runner->window); +} + +static D3DTEXTUREADDRESS sampler_address_to_d3d9(D3D12_TEXTURE_ADDRESS_MODE address) +{ + switch (address) + { + case D3D12_TEXTURE_ADDRESS_MODE_WRAP: + return D3DTADDRESS_WRAP; + + case D3D12_TEXTURE_ADDRESS_MODE_MIRROR: + return D3DTADDRESS_MIRROR; + + case D3D12_TEXTURE_ADDRESS_MODE_CLAMP: + return D3DTADDRESS_CLAMP; + + case D3D12_TEXTURE_ADDRESS_MODE_BORDER: + return D3DTADDRESS_BORDER; + + case D3D12_TEXTURE_ADDRESS_MODE_MIRROR_ONCE: + return D3DTADDRESS_MIRRORONCE; + } + + assert(0); + return 0; +} + +static bool d3d9_runner_check_requirements(struct shader_runner *r) +{ + struct d3d9_shader_runner *runner = d3d9_shader_runner(r); + + if (runner->r.minimum_shader_model >= SHADER_MODEL_4_0) + return false; + + return true; +} + +static struct resource *d3d9_runner_create_resource(struct shader_runner *r, const struct resource_params *params) +{ + struct d3d9_shader_runner *runner = d3d9_shader_runner(r); + IDirect3DDevice9 *device = runner->device; + struct d3d9_resource *resource; + unsigned int src_pitch, y; + D3DLOCKED_RECT map_desc; + D3DFORMAT format; + HRESULT hr; + void *data; + + resource = calloc(1, sizeof(*resource)); + resource->r.slot = params->slot; + resource->r.type = params->type; + resource->r.size = params->data_size; + + switch (params->type) + { + case RESOURCE_TYPE_TEXTURE: + switch (params->format) + { + case DXGI_FORMAT_R32G32B32A32_FLOAT: + format = D3DFMT_A32B32G32R32F; + break; + + case DXGI_FORMAT_R32_FLOAT: + format = D3DFMT_R32F; + break; + + default: + format = D3DFMT_UNKNOWN; + break; + } + + hr = IDirect3DDevice9_CreateTexture(device, params->width, params->height, + 1, D3DUSAGE_DYNAMIC, format, D3DPOOL_DEFAULT, &resource->texture, NULL); + ok(hr == D3D_OK, "Failed to create texture, hr %#lx.\n", hr); + + hr = IDirect3DTexture9_LockRect(resource->texture, 0, &map_desc, NULL, D3DLOCK_DISCARD); + ok(hr == D3D_OK, "Failed to map texture, hr %#lx.\n", hr); + src_pitch = params->width * params->texel_size; + for (y = 0; y < params->height; ++y) + memcpy((char *)map_desc.pBits + y * map_desc.Pitch, params->data + y * src_pitch, src_pitch); + hr = IDirect3DTexture9_UnlockRect(resource->texture, 0); + ok(hr == D3D_OK, "Failed to unmap texture, hr %#lx.\n", hr); + break; + + case RESOURCE_TYPE_VERTEX_BUFFER: + hr = IDirect3DDevice9_CreateVertexBuffer(device, params->data_size, + D3DUSAGE_DYNAMIC, 0, D3DPOOL_DEFAULT, &resource->vb, NULL); + ok(hr == D3D_OK, "Failed to create vertex buffer, hr %#lx.\n", hr); + + hr = IDirect3DVertexBuffer9_Lock(resource->vb, 0, 0, &data, D3DLOCK_DISCARD); + ok(hr == D3D_OK, "Failed to map texture, hr %#lx.\n", hr); + memcpy(data, params->data, params->data_size); + hr = IDirect3DVertexBuffer9_Unlock(resource->vb); + ok(hr == D3D_OK, "Failed to unmap texture, hr %#lx.\n", hr); + break; + } + + return &resource->r; +} + +static void d3d9_runner_destroy_resource(struct shader_runner *r, struct resource *res) +{ + struct d3d9_resource *resource = d3d9_resource(res); + + if (resource->texture) + IDirect3DTexture9_Release(resource->texture); + if (resource->vb) + IDirect3DVertexBuffer9_Release(resource->vb); + free(resource); +} + +static D3DDECLTYPE vertex_decl_type_from_format(DXGI_FORMAT format) +{ + switch (format) + { + case DXGI_FORMAT_R32G32_FLOAT: + return D3DDECLTYPE_FLOAT2; + + case DXGI_FORMAT_R32G32B32A32_FLOAT: + return D3DDECLTYPE_FLOAT4; + + default: + fatal_error("Cannot translate format %#x to a d3d9 vertex buffer format.\n", format); + } +} + +static D3DDECLUSAGE vertex_decl_usage_from_name(const char *name) +{ + if (!strcasecmp(name, "position") || !strcasecmp(name, "sv_position")) + return D3DDECLUSAGE_POSITION; + if (!strcasecmp(name, "texcoord")) + return D3DDECLUSAGE_TEXCOORD; + fatal_error("Cannot translate usage "%s" to a d3d9 usage.\n", name); +} + +static void d3d9_runner_draw(struct shader_runner *r, + D3D_PRIMITIVE_TOPOLOGY primitive_topology, unsigned int vertex_count) +{ + static const D3DVERTEXELEMENT9 decl_element_end = D3DDECL_END(); + struct d3d9_shader_runner *runner = d3d9_shader_runner(r); + IDirect3DVertexDeclaration9 *vertex_declaration; + IDirect3DDevice9 *device = runner->device; + D3DVERTEXELEMENT9 *decl_elements; + ID3D10Blob *vs_code, *ps_code; + IDirect3DVertexShader9 *vs; + IDirect3DPixelShader9 *ps; + unsigned int i, j; + HRESULT hr; + + if (!(vs_code = compile_shader(runner->r.vs_source, "vs_2_0"))) + return; + + if (!(ps_code = compile_shader(runner->r.ps_source, "ps_2_0"))) + { + ID3D10Blob_Release(vs_code); + return; + } + + if (runner->r.uniform_count) + { + hr = IDirect3DDevice9_SetPixelShaderConstantF(device, 0, + (const float *)runner->r.uniforms, runner->r.uniform_count / 4); + ok(hr == D3D_OK, "Failed to set uniforms, hr %#lx.\n", hr); + } + + decl_elements = calloc(runner->r.input_element_count + 1, sizeof(*decl_elements)); + for (i = 0; i < runner->r.input_element_count; ++i) + { + const struct input_element *src_element = &runner->r.input_elements[i]; + D3DVERTEXELEMENT9 *dst_element = &decl_elements[i]; + + dst_element->Stream = src_element->slot; + dst_element->Type = vertex_decl_type_from_format(src_element->format); + dst_element->Method = D3DDECLMETHOD_DEFAULT; + dst_element->Usage = vertex_decl_usage_from_name(src_element->name); + dst_element->UsageIndex = src_element->index; + + /* The offset will be filled below. */ + } + decl_elements[runner->r.input_element_count] = decl_element_end; + + for (i = 0; i < runner->r.resource_count; ++i) + { + struct d3d9_resource *resource = d3d9_resource(runner->r.resources[i]); + unsigned int stride = 0; + + switch (resource->r.type) + { + case RESOURCE_TYPE_TEXTURE: + hr = IDirect3DDevice9_SetTexture(device, resource->r.slot, (IDirect3DBaseTexture9 *)resource->texture); + ok(hr == D3D_OK, "Failed to set texture, hr %#lx.\n", hr); + break; + + case RESOURCE_TYPE_VERTEX_BUFFER: + for (j = 0; j < runner->r.input_element_count; ++j) + { + if (runner->r.input_elements[j].slot == resource->r.slot) + { + decl_elements[j].Offset = stride; + stride += runner->r.input_elements[j].texel_size; + } + } + + hr = IDirect3DDevice9_SetStreamSource(device, resource->r.slot, resource->vb, 0, stride); + ok(hr == D3D_OK, "Failed to set vertex buffer, hr %#lx.\n", hr); + break; + } + } + + for (i = 0; i < runner->r.sampler_count; ++i) + { + const struct sampler *sampler = &runner->r.samplers[i]; + + hr = IDirect3DDevice9_SetSamplerState(device, sampler->slot, + D3DSAMP_ADDRESSU, sampler_address_to_d3d9(sampler->u_address)); + ok(hr == D3D_OK, "Failed to set sampler state, hr %#lx.\n", hr); + hr = IDirect3DDevice9_SetSamplerState(device, sampler->slot, + D3DSAMP_ADDRESSV, sampler_address_to_d3d9(sampler->v_address)); + ok(hr == D3D_OK, "Failed to set sampler state, hr %#lx.\n", hr); + hr = IDirect3DDevice9_SetSamplerState(device, sampler->slot, + D3DSAMP_ADDRESSW, sampler_address_to_d3d9(sampler->w_address)); + ok(hr == D3D_OK, "Failed to set sampler state, hr %#lx.\n", hr); + + hr = IDirect3DDevice9_SetSamplerState(device, sampler->slot, D3DSAMP_MINFILTER, + (sampler->filter & 0x1) ? D3DTEXF_LINEAR : D3DTEXF_POINT); + ok(hr == D3D_OK, "Failed to set sampler state, hr %#lx.\n", hr); + hr = IDirect3DDevice9_SetSamplerState(device, sampler->slot, D3DSAMP_MAGFILTER, + (sampler->filter & 0x4) ? D3DTEXF_LINEAR : D3DTEXF_POINT); + ok(hr == D3D_OK, "Failed to set sampler state, hr %#lx.\n", hr); + hr = IDirect3DDevice9_SetSamplerState(device, sampler->slot, D3DSAMP_MIPFILTER, + (sampler->filter & 0x10) ? D3DTEXF_LINEAR : D3DTEXF_POINT); + ok(hr == D3D_OK, "Failed to set sampler state, hr %#lx.\n", hr); + } + + hr = IDirect3DDevice9_CreateVertexDeclaration(device, decl_elements, &vertex_declaration); + ok(hr == D3D_OK, "Failed to create vertex declaration, hr %#lx.\n", hr); + hr = IDirect3DDevice9_CreateVertexShader(device, ID3D10Blob_GetBufferPointer(vs_code), &vs); + ok(hr == D3D_OK, "Failed to create vertex shader, hr %#lx.\n", hr); + hr = IDirect3DDevice9_CreatePixelShader(device, ID3D10Blob_GetBufferPointer(ps_code), &ps); + ok(hr == D3D_OK, "Failed to create pixel shader, hr %#lx.\n", hr); + + hr = IDirect3DDevice9_SetVertexDeclaration(device, vertex_declaration); + ok(hr == D3D_OK, "Failed to set vertex declaration, hr %#lx.\n", hr); + hr = IDirect3DDevice9_SetVertexShader(device, vs); + ok(hr == D3D_OK, "Failed to set vertex shader, hr %#lx.\n", hr); + hr = IDirect3DDevice9_SetPixelShader(device, ps); + ok(hr == D3D_OK, "Failed to set pixel shader, hr %#lx.\n", hr); + + hr = IDirect3DDevice9_BeginScene(device); + ok(hr == D3D_OK, "Failed to draw, hr %#lx.\n", hr); + + switch (primitive_topology) + { + case D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST: + hr = IDirect3DDevice9_DrawPrimitive(device, D3DPT_TRIANGLELIST, 0, vertex_count / 3); + break; + + case D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP: + hr = IDirect3DDevice9_DrawPrimitive(device, D3DPT_TRIANGLESTRIP, 0, vertex_count - 2); + break; + + default: + fatal_error("Cannot translate topology %#x to a d3d9 topology.\n", primitive_topology); + } + ok(hr == D3D_OK, "Failed to draw, hr %#lx.\n", hr); + + hr = IDirect3DDevice9_EndScene(device); + ok(hr == D3D_OK, "Failed to draw, hr %#lx.\n", hr); + + IDirect3DVertexDeclaration9_Release(vertex_declaration); + IDirect3DVertexShader9_Release(vs); + IDirect3DPixelShader9_Release(ps); +} + +struct resource_readback +{ + IDirect3DSurface9 *surface; + D3DLOCKED_RECT rect; +}; + +static void init_readback(struct d3d9_shader_runner *runner, struct resource_readback *rb) +{ + D3DSURFACE_DESC desc; + HRESULT hr; + + hr = IDirect3DSurface9_GetDesc(runner->rt, &desc); + ok(hr == D3D_OK, "Failed to get surface desc, hr %#lx.\n", hr); + hr = IDirect3DDevice9Ex_CreateOffscreenPlainSurface(runner->device, desc.Width, + desc.Height, desc.Format, D3DPOOL_SYSTEMMEM, &rb->surface, NULL); + ok(hr == D3D_OK, "Failed to create surface, hr %#lx.\n", hr); + + hr = IDirect3DDevice9Ex_GetRenderTargetData(runner->device, runner->rt, rb->surface); + ok(hr == D3D_OK, "Failed to get render target data, hr %#lx.\n", hr); + + hr = IDirect3DSurface9_LockRect(rb->surface, &rb->rect, NULL, D3DLOCK_READONLY); + ok(hr == D3D_OK, "Failed to lock surface, hr %#lx.\n", hr); +} + +static const struct vec4 *get_readback_vec4(const struct resource_readback *rb, unsigned int x, unsigned int y) +{ + return (struct vec4 *)((BYTE *)rb->rect.pBits + y * rb->rect.Pitch + x * sizeof(struct vec4)); +} + +static void check_readback_data_vec4(struct resource_readback *rb, + const RECT *rect, const struct vec4 *expected, unsigned int max_diff) +{ + unsigned int x = 0, y = 0; + struct vec4 got = {0}; + bool all_match = true; + + for (y = rect->top; y < rect->bottom; ++y) + { + for (x = rect->left; x < rect->right; ++x) + { + got = *get_readback_vec4(rb, x, y); + if (!compare_vec4(&got, expected, max_diff)) + { + all_match = false; + break; + } + } + if (!all_match) + break; + } + ok(all_match, "Got {%.8e, %.8e, %.8e, %.8e}, expected {%.8e, %.8e, %.8e, %.8e} at (%u, %u).\n", + got.x, got.y, got.z, got.w, expected->x, expected->y, expected->z, expected->w, x, y); +} + +static void release_readback(struct resource_readback *rb) +{ + IDirect3DSurface9_UnlockRect(rb->surface); + IDirect3DSurface9_Release(rb->surface); +} + +static void d3d9_runner_probe_vec4(struct shader_runner *r, const RECT *rect, const struct vec4 *v, unsigned int ulps) +{ + struct d3d9_shader_runner *runner = d3d9_shader_runner(r); + struct resource_readback rb; + + init_readback(runner, &rb); + check_readback_data_vec4(&rb, rect, v, ulps); + release_readback(&rb); +} + +static const struct shader_runner_ops d3d9_runner_ops = +{ + .check_requirements = d3d9_runner_check_requirements, + .create_resource = d3d9_runner_create_resource, + .destroy_resource = d3d9_runner_destroy_resource, + .draw = d3d9_runner_draw, + .probe_vec4 = d3d9_runner_probe_vec4, +}; + +void run_shader_tests_d3d9(int argc, char **argv) +{ + struct d3d9_shader_runner runner; + HMODULE d3d9_module; + + d3d9_module = LoadLibraryA("d3d9.dll"); + if (d3d9_module) + { + pDirect3DCreate9 = (void *)GetProcAddress(d3d9_module, "Direct3DCreate9"); + + parse_args(argc, argv); + init_adapter_info(); + init_test_context(&runner); + run_shader_tests(&runner.r, argc, argv, &d3d9_runner_ops); + destroy_test_context(&runner); + } + FreeLibrary(d3d9_module); +}
On Wed, 6 Apr 2022 at 23:05, Zebediah Figura zfigura@codeweavers.com wrote:
@@ -326,17 +344,44 @@ static void parse_test_directive(struct shader_runner *runner, const char *line)
[...]
memset(¶ms, 0, sizeof(params));
params.slot = 0;
params.type = RESOURCE_TYPE_VERTEX_BUFFER;
params.data = malloc(sizeof(quad));
memcpy(params.data, quad, sizeof(quad));
params.data_size = sizeof(quad);
set_resource(runner, runner->ops->create_resource(runner, ¶ms));
I don't think this is an issue right now, but note that this means "draw quad" is going to override previously set resources, so a subsequent "draw" might not do what you'd expect.
On 4/7/22 08:07, Henri Verbeet wrote:
On Wed, 6 Apr 2022 at 23:05, Zebediah Figura zfigura@codeweavers.com wrote:
@@ -326,17 +344,44 @@ static void parse_test_directive(struct shader_runner *runner, const char *line)
[...]
memset(¶ms, 0, sizeof(params));
params.slot = 0;
params.type = RESOURCE_TYPE_VERTEX_BUFFER;
params.data = malloc(sizeof(quad));
memcpy(params.data, quad, sizeof(quad));
params.data_size = sizeof(quad);
set_resource(runner, runner->ops->create_resource(runner, ¶ms));
I don't think this is an issue right now, but note that this means "draw quad" is going to override previously set resources, so a subsequent "draw" might not do what you'd expect.
It is, yes. That might be avoidable by reorganizing the way vertex buffers are defined and bound, but it doesn't seem worthwhile at the moment...