For compatibility with shader models before 4.0.
Signed-off-by: Zebediah Figura zfigura@codeweavers.com --- v2: Draw a large triangle anyway. Also, make it large enough to avoid depending on pixel-exact rendering near the viewport edges.
tests/shader_runner.c | 69 ++++++++++++++++++++++++++++++------------- 1 file changed, 48 insertions(+), 21 deletions(-)
diff --git a/tests/shader_runner.c b/tests/shader_runner.c index d3d131164..ffa4cf61b 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,13 +344,40 @@ 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; + + /* For simplicity, draw a large triangle instead. */ + static const struct vec2 quad[] = + { + {-2.0f, -2.0f}, + {-2.0f, 4.0f}, + { 4.0f, -2.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);
@@ -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 --- v2: No changes.
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 ffa4cf61b..05c0fbe1e 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);
Signed-off-by: Henri Verbeet hverbeet@codeweavers.com
Signed-off-by: Zebediah Figura zfigura@codeweavers.com --- v2: New patch.
tests/conditional.shader_test | 2 +- tests/shader_runner.c | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/tests/conditional.shader_test b/tests/conditional.shader_test index 70718f117..1a1b8fee5 100644 --- a/tests/conditional.shader_test +++ b/tests/conditional.shader_test @@ -10,4 +10,4 @@ float4 main(float4 pos : SV_POSITION) : SV_TARGET [test] 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 rect rgba (200, 0, 640, 480) (0.1, 0.2, 0.3, 0.4) diff --git a/tests/shader_runner.c b/tests/shader_runner.c index 05c0fbe1e..6f4c70031 100644 --- a/tests/shader_runner.c +++ b/tests/shader_runner.c @@ -419,22 +419,22 @@ static void parse_test_directive(struct shader_runner *runner, const char *line) } else if (match_string(line, "probe rect rgba", &line)) { - unsigned int x, y, w, h, ulps; + unsigned int left, top, right, bottom, ulps; struct vec4 v; RECT rect; int ret;
- ret = sscanf(line, "( %u , %u , %u , %u ) ( %f , %f , %f , %f ) %u", - &x, &y, &w, &h, &v.x, &v.y, &v.z, &v.w, &ulps); + ret = sscanf(line, "( %d , %d , %d , %d ) ( %f , %f , %f , %f ) %u", + &left, &top, &right, &bottom, &v.x, &v.y, &v.z, &v.w, &ulps); if (ret < 8) fatal_error("Malformed probe arguments '%s'.\n", line); if (ret < 9) ulps = 0;
- rect.left = x; - rect.right = x + w; - rect.top = y; - rect.bottom = y + h; + rect.left = left; + rect.top = top; + rect.right = right; + rect.bottom = bottom; runner->ops->probe_vec4(runner, &rect, &v, ulps); } else if (match_string(line, "probe rgba", &line))
Signed-off-by: Henri Verbeet hverbeet@codeweavers.com
Signed-off-by: Zebediah Figura zfigura@codeweavers.com --- v2: Instead of using uniforms, change to using texcoord.
In order to avoid depending on pixel-exact rendering (and in order to work around different pixel centers for d3d9), don't validate pixels near a border (for conditional and hlsl-for), and quantize the input (for trigonometry). Thanks to Henri Verbeet for the latter idea.
tests/conditional.shader_test | 16 +++++++---- tests/hlsl-for.shader_test | 18 ++++++++----- tests/hlsl-struct-semantics.shader_test | 35 ++++++++++++++++++++----- tests/trigonometry.shader_test | 15 ++++++++--- 4 files changed, 63 insertions(+), 21 deletions(-)
diff --git a/tests/conditional.shader_test b/tests/conditional.shader_test index 1a1b8fee5..e665ac1d2 100644 --- a/tests/conditional.shader_test +++ b/tests/conditional.shader_test @@ -1,7 +1,13 @@ -[pixel shader] -float4 main(float4 pos : SV_POSITION) : SV_TARGET +[vertex shader] +void main(out float tex : texcoord, inout float4 pos : sv_position) { - if(pos.x > 200.0) + tex = pos.x; +} + +[pixel shader] +float4 main(float tex : texcoord) : SV_TARGET +{ + if (tex > 0.0) return float4(0.1, 0.2, 0.3, 0.4); else return float4(0.9, 0.8, 0.7, 0.6); @@ -9,5 +15,5 @@ float4 main(float4 pos : SV_POSITION) : SV_TARGET
[test] draw quad -probe rect rgba (0, 0, 200, 480) (0.9, 0.8, 0.7, 0.6) -probe rect rgba (200, 0, 640, 480) (0.1, 0.2, 0.3, 0.4) +probe rect rgba ( 0, 0, 319, 480) (0.9, 0.8, 0.7, 0.6) +probe rect rgba (321, 0, 640, 480) (0.1, 0.2, 0.3, 0.4) diff --git a/tests/hlsl-for.shader_test b/tests/hlsl-for.shader_test index b133ce996..4e91ce412 100644 --- a/tests/hlsl-for.shader_test +++ b/tests/hlsl-for.shader_test @@ -1,14 +1,20 @@ +[vertex shader] +void main(out float tex : texcoord, inout float4 pos : sv_position) +{ + tex = pos.x; +} + [pixel shader] -float4 main(float4 pos : SV_POSITION) : SV_TARGET +float4 main(float tex : texcoord) : sv_target { int i; float x = 0.0; for (i = 0; i < 10; i++) { x += i; - if (pos.x == 1.5 && i == 5) + if (tex > 0.5 && i == 5) break; - if (pos.x == 2.5 && i >= 7) + if (tex > -0.5 && i >= 7) continue; x -= 1; } @@ -17,6 +23,6 @@ float4 main(float4 pos : SV_POSITION) : SV_TARGET
[test] 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 rect rgba ( 0, 0, 159, 480) (10.0, 35.0, 0.0, 0.0) +probe rect rgba (161, 0, 479, 480) (10.0, 38.0, 0.0, 0.0) +probe rect rgba (481, 0, 640, 480) ( 5.0, 10.0, 0.0, 0.0) diff --git a/tests/hlsl-struct-semantics.shader_test b/tests/hlsl-struct-semantics.shader_test index b623a998c..43b07bb86 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 -2.0 -2.0 +0.0 1.0 0.0 1.0 -2.0 2.0 +0.0 1.0 0.0 1.0 2.0 -2.0 +0.0 1.0 0.0 1.0 2.0 2.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..cf53d7a12 100644 --- a/tests/trigonometry.shader_test +++ b/tests/trigonometry.shader_test @@ -1,7 +1,14 @@ -[pixel shader] -float4 main(float4 pos : SV_POSITION) : SV_TARGET +[vertex shader] +void main(out float tex : texcoord, inout float4 pos : sv_position) { - return float4(sin(pos.x - 0.5), cos(pos.x - 0.5), 0, 0); + tex = (pos.x + 1) * 320; +} + +[pixel shader] +float4 main(float tex : texcoord) : sv_target +{ + tex = floor(tex + 0.25); + return float4(sin(tex), cos(tex), 0, 0); }
[test] @@ -17,7 +24,7 @@ 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 (11, 0) (-0.99999021, 0.00442570, 0.0, 0.0) 2048 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
Signed-off-by: Henri Verbeet hverbeet@codeweavers.com
Signed-off-by: Zebediah Figura zfigura@codeweavers.com --- v2: No changes.
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 f08bb926f..737dc260d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -436,6 +436,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 6f4c70031..0aca7830e 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); +}
Signed-off-by: Henri Verbeet hverbeet@codeweavers.com