From: Zebediah Figura Subject: [PATCH vkd3d v2 1/7] tests: Separate the compile_shader function from the shader_test parser. Message-Id: <20220114233308.49491-1-zfigura@codeweavers.com> Date: Fri, 14 Jan 2022 17:33:02 -0600 In essence, the goal is to separate the d3d12 backend. Just separate the compiler first, though, to make the diff easier to read. NOTE TO SELF: Generate this diff with -C Signed-off-by: Zebediah Figura --- Makefile.am | 41 +- include/private/vkd3d_test.h | 19 +- tests/shader_runner.c | 862 +++++++++++++++++++++++++++++++++++ tests/shader_runner.h | 29 ++ tests/shader_runner_d3d12.c | 847 +--------------------------------- 5 files changed, 947 insertions(+), 851 deletions(-) create mode 100644 tests/shader_runner.c create mode 100644 tests/shader_runner.h diff --git a/Makefile.am b/Makefile.am index 20fee06d0..84aba0ee7 100644 --- a/Makefile.am +++ b/Makefile.am @@ -50,9 +50,6 @@ vkd3d_cross_tests = \ tests/d3d12_invalid_usage \ tests/hlsl_d3d12 -vkd3d_shader_runners = \ - tests/shader_runner_d3d12 - vkd3d_shader_tests = \ tests/abs.shader_test \ tests/cast-to-float.shader_test \ @@ -113,7 +110,8 @@ vkd3d_shader_tests = \ vkd3d_test_headers = \ tests/d3d12_crosstest.h \ - tests/d3d12_test_utils.h + tests/d3d12_test_utils.h \ + tests/shader_runner.h vkd3d_demos = \ demos/vkd3d-gears \ @@ -273,15 +271,18 @@ AM_DEFAULT_SOURCE_EXT = .c TEST_EXTENSIONS = .shader_test if BUILD_TESTS -check_PROGRAMS = $(vkd3d_tests) $(vkd3d_cross_tests) $(vkd3d_shader_runners) +check_PROGRAMS = $(vkd3d_tests) $(vkd3d_cross_tests) tests/shader_runner TESTS = $(vkd3d_tests) $(vkd3d_cross_tests) $(vkd3d_shader_tests) tests_d3d12_LDADD = $(LDADD) @PTHREAD_LIBS@ @VULKAN_LIBS@ tests_d3d12_invalid_usage_LDADD = $(LDADD) @VULKAN_LIBS@ tests_hlsl_d3d12_LDADD = $(LDADD) @VULKAN_LIBS@ -tests_shader_runner_d3d12_LDADD = $(LDADD) @VULKAN_LIBS@ +tests_shader_runner_LDADD = $(LDADD) @VULKAN_LIBS@ +tests_shader_runner_SOURCES = \ + tests/shader_runner.c \ + tests/shader_runner_d3d12.c tests_vkd3d_api_LDADD = libvkd3d.la @VULKAN_LIBS@ tests_vkd3d_shader_api_LDADD = libvkd3d-shader.la -SHADER_TEST_LOG_COMPILER = tests/shader_runner_d3d12 +SHADER_TEST_LOG_COMPILER = tests/shader_runner XFAIL_TESTS = \ tests/cast-to-float.shader_test \ tests/cast-to-half.shader_test \ @@ -388,14 +389,17 @@ CROSS_CPPFLAGS = -I$(srcdir)/include -I$(srcdir)/include/private -I$(builddir)/i CROSS_CFLAGS = -g -O2 -Wall -municode ${CROSS_CPPFLAGS} EXTRA_DIST += $(cross_implibs:=.cross32.def) $(cross_implibs:=.cross64.def) +shader_runner_cross_sources = \ + $(srcdir)/tests/shader_runner.c \ + $(srcdir)/tests/shader_runner_d3d12.c + if HAVE_CROSSTARGET32 CROSS32_CC = @CROSSCC32@ CROSS32_DLLTOOL = @CROSSTARGET32@-dlltool CROSS32_IMPLIBS = $(cross_implibs:=.cross32.a) CROSS32_EXEFILES = $(vkd3d_cross_tests:=.cross32.exe) \ - $(vkd3d_demos:demos/vkd3d-%=demos/%.cross32.exe) \ - $(vkd3d_shader_runners:=.cross32.exe) -CROSS32_FILES = $(CROSS32_IMPLIBS) $(CROSS32_EXEFILES) + $(vkd3d_demos:demos/vkd3d-%=demos/%.cross32.exe) +CROSS32_FILES = $(CROSS32_IMPLIBS) $(CROSS32_EXEFILES) tests/shader_runner.cross32.exe CLEANFILES += $(CROSS32_FILES) crosstest32: $(CROSS32_FILES) @@ -411,6 +415,12 @@ $(CROSS32_EXEFILES): %.cross32.exe: %.c $(CROSS32_IMPLIBS) $(widl_headers) $(AM_V_CCLD)depbase=`echo $@ | $(SED) 's![^/]*$$!$(DEPDIR)/&!;s!\.exe$$!!'`; \ $(CROSS32_CC) $(CROSS_CFLAGS) -MT $@ -MD -MP -MF $$depbase.Tpo -o $@ $< $(CROSS32_IMPLIBS) -ldxgi -lgdi32 -ld3dcompiler_47 && \ $(am__mv) $$depbase.Tpo $$depbase.Po + +tests/shader_runner.cross32.exe: $(shader_runner_cross_sources) $(CROSS32_IMPLIBS) $(widl_headers) + $(AM_V_CCLD)depbase=`echo $@ | sed 's![^/]*$$!$(DEPDIR)/&!;s!\.exe$$!!'`; \ + $(CROSS32_CC) $(CROSS_CFLAGS) -MT $@ -MD -MP -MF $$depbase.Tpo -o $@ $(shader_runner_cross_sources) $(CROSS32_IMPLIBS) -ldxgi -lgdi32 -ld3dcompiler_47 && \ + $(am__mv) $$depbase.Tpo $$depbase.Po + else crosstest32: endif @@ -420,9 +430,8 @@ CROSS64_CC = @CROSSCC64@ CROSS64_DLLTOOL = @CROSSTARGET64@-dlltool CROSS64_IMPLIBS = $(cross_implibs:=.cross64.a) CROSS64_EXEFILES = $(vkd3d_cross_tests:=.cross64.exe) \ - $(vkd3d_demos:demos/vkd3d-%=demos/%.cross64.exe) \ - $(vkd3d_shader_runners:=.cross64.exe) -CROSS64_FILES = $(CROSS64_IMPLIBS) $(CROSS64_EXEFILES) + $(vkd3d_demos:demos/vkd3d-%=demos/%.cross64.exe) +CROSS64_FILES = $(CROSS64_IMPLIBS) $(CROSS64_EXEFILES) tests/shader_runner.cross64.exe CLEANFILES += $(CROSS64_FILES) crosstest64: $(CROSS64_FILES) @@ -438,6 +447,12 @@ $(CROSS64_EXEFILES): %.cross64.exe: %.c $(CROSS64_IMPLIBS) $(widl_headers) $(AM_V_CCLD)depbase=`echo $@ | sed 's![^/]*$$!$(DEPDIR)/&!;s!\.exe$$!!'`; \ $(CROSS64_CC) $(CROSS_CFLAGS) -MT $@ -MD -MP -MF $$depbase.Tpo -o $@ $< $(CROSS64_IMPLIBS) -ldxgi -lgdi32 -ld3dcompiler_47 && \ $(am__mv) $$depbase.Tpo $$depbase.Po + +tests/shader_runner.cross64.exe: $(shader_runner_cross_sources) $(CROSS64_IMPLIBS) $(widl_headers) + $(AM_V_CCLD)depbase=`echo $@ | sed 's![^/]*$$!$(DEPDIR)/&!;s!\.exe$$!!'`; \ + $(CROSS64_CC) $(CROSS_CFLAGS) -MT $@ -MD -MP -MF $$depbase.Tpo -o $@ $(shader_runner_cross_sources) $(CROSS64_IMPLIBS) -ldxgi -lgdi32 -ld3dcompiler_47 && \ + $(am__mv) $$depbase.Tpo $$depbase.Po + else crosstest64: endif diff --git a/include/private/vkd3d_test.h b/include/private/vkd3d_test.h index 6b8763dde..0cc7fe4f3 100644 --- a/include/private/vkd3d_test.h +++ b/include/private/vkd3d_test.h @@ -28,16 +28,15 @@ #include #include -static void vkd3d_test_main(int argc, char **argv); -static const char *vkd3d_test_name; -static const char *vkd3d_test_platform = "other"; +extern const char *vkd3d_test_name; +extern const char *vkd3d_test_platform; static void vkd3d_test_start_todo(bool is_todo); static int vkd3d_test_loop_todo(void); static void vkd3d_test_end_todo(void); #define START_TEST(name) \ - static const char *vkd3d_test_name = #name; \ + const char *vkd3d_test_name = #name; \ static void vkd3d_test_main(int argc, char **argv) /* @@ -100,7 +99,7 @@ static void vkd3d_test_end_todo(void); #define todo todo_if(true) -static struct +struct vkd3d_test_state { LONG success_count; LONG failure_count; @@ -120,7 +119,8 @@ static struct const char *test_name_filter; char context[1024]; -} vkd3d_test_state; +}; +extern struct vkd3d_test_state vkd3d_test_state; static bool vkd3d_test_platform_is_windows(void) @@ -253,6 +253,12 @@ vkd3d_test_debug(const char *fmt, ...) printf("%s\n", buffer); } +#ifndef VKD3D_TEST_NO_DEFS +const char *vkd3d_test_platform = "other"; +struct vkd3d_test_state vkd3d_test_state; + +static void vkd3d_test_main(int argc, char **argv); + int main(int argc, char **argv) { const char *test_filter = getenv("VKD3D_TEST_FILTER"); @@ -339,6 +345,7 @@ int wmain(int argc, WCHAR **wargv) return ret; } #endif /* _WIN32 */ +#endif /* VKD3D_TEST_NO_DEFS */ typedef void (*vkd3d_test_pfn)(void); diff --git a/tests/shader_runner.c b/tests/shader_runner.c new file mode 100644 index 000000000..df4344881 --- /dev/null +++ b/tests/shader_runner.c @@ -0,0 +1,862 @@ +/* + * Copyright 2020-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 + */ + +/* + * This application contains code derived from piglit, the license for which + * follows: + * + * Copyright © 2010 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "d3d12_crosstest.h" +#include "shader_runner.h" +#include + +static void VKD3D_NORETURN VKD3D_PRINTF_FUNC(1, 2) fatal_error(const char *format, ...) +{ + va_list args; + + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); + exit(1); +} + +static bool vkd3d_array_reserve(void **elements, size_t *capacity, size_t element_count, size_t element_size) +{ + size_t new_capacity, max_capacity; + void *new_elements; + + if (element_count <= *capacity) + return true; + + max_capacity = ~(size_t)0 / element_size; + if (max_capacity < element_count) + return false; + + new_capacity = max(*capacity, 4); + while (new_capacity < element_count && new_capacity <= max_capacity / 2) + new_capacity *= 2; + + if (new_capacity < element_count) + new_capacity = element_count; + + if (!(new_elements = realloc(*elements, new_capacity * element_size))) + return false; + + *elements = new_elements; + *capacity = new_capacity; + + return true; +} + +enum texture_data_type +{ + TEXTURE_DATA_FLOAT, + TEXTURE_DATA_SINT, + TEXTURE_DATA_UINT, +}; + +struct sampler +{ + unsigned int slot; + + D3D12_FILTER filter; + D3D12_TEXTURE_ADDRESS_MODE u_address, v_address, w_address; +}; + +struct texture +{ + unsigned int slot; + + DXGI_FORMAT format; + enum texture_data_type data_type; + unsigned int texel_size; + unsigned int width, height; + uint8_t *data; + size_t data_size, data_capacity; + + D3D12_DESCRIPTOR_RANGE descriptor_range; + ID3D12DescriptorHeap *heap; + ID3D12Resource *resource; + unsigned int root_index; +}; + +struct shader_context +{ + const struct shader_runner_ops *ops; + + struct test_context c; + + ID3D10Blob *ps_code; + + uint32_t *uniforms; + size_t uniform_count; + + struct texture *textures; + size_t texture_count; + + struct sampler *samplers; + size_t sampler_count; +}; + +static void free_texture(struct texture *texture) +{ + ID3D12DescriptorHeap_Release(texture->heap); + ID3D12Resource_Release(texture->resource); + free(texture->data); + memset(texture, 0, sizeof(*texture)); +} + +enum parse_state +{ + STATE_NONE, + STATE_PREPROC, + STATE_PREPROC_INVALID, + STATE_SAMPLER, + STATE_SHADER_INVALID_PIXEL, + STATE_SHADER_PIXEL, + STATE_TEXTURE, + STATE_TEST, +}; + +static bool match_string(const char *line, const char *token, const char **const rest) +{ + size_t len = strlen(token); + + if (strncmp(line, token, len) || !isspace(line[len])) + return false; + if (rest) + { + *rest = line + len; + while (isspace(**rest)) + ++*rest; + } + return true; +} + +static void parse_texture_format(struct texture *texture, const char *line) +{ + static const struct + { + const char *string; + enum texture_data_type data_type; + unsigned int texel_size; + DXGI_FORMAT format; + } + formats[] = + { + {"r32g32b32a32 float", TEXTURE_DATA_FLOAT, 16, DXGI_FORMAT_R32G32B32A32_FLOAT}, + {"r32g32 uint", TEXTURE_DATA_UINT, 8, DXGI_FORMAT_R32G32_UINT}, + {"r32 float", TEXTURE_DATA_FLOAT, 4, DXGI_FORMAT_R32_FLOAT}, + {"r32 sint", TEXTURE_DATA_SINT, 4, DXGI_FORMAT_R32_SINT}, + }; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(formats); ++i) + { + if (match_string(line, formats[i].string, &line)) + { + texture->format = formats[i].format; + texture->data_type = formats[i].data_type; + texture->texel_size = formats[i].texel_size; + return; + } + } + + fatal_error("Unknown format '%s'.\n", line); +} + +static D3D12_TEXTURE_ADDRESS_MODE parse_sampler_address_mode(const char *line, const char **rest) +{ + if (match_string(line, "border", rest)) + return D3D12_TEXTURE_ADDRESS_MODE_BORDER; + if (match_string(line, "clamp", rest)) + return D3D12_TEXTURE_ADDRESS_MODE_CLAMP; + if (match_string(line, "mirror_once", rest)) + return D3D12_TEXTURE_ADDRESS_MODE_MIRROR_ONCE; + if (match_string(line, "mirror", rest)) + return D3D12_TEXTURE_ADDRESS_MODE_MIRROR; + if (match_string(line, "wrap", rest)) + return D3D12_TEXTURE_ADDRESS_MODE_WRAP; + + fatal_error("Unknown sampler address mode '%s'.\n", line); +} + +static void parse_sampler_directive(struct sampler *sampler, const char *line) +{ + if (match_string(line, "address", &line)) + { + sampler->u_address = parse_sampler_address_mode(line, &line); + sampler->v_address = parse_sampler_address_mode(line, &line); + sampler->w_address = parse_sampler_address_mode(line, &line); + } + else if (match_string(line, "filter", &line)) + { + static const struct + { + const char *string; + D3D12_FILTER filter; + } + filters[] = + { + {"point point point", D3D12_FILTER_MIN_MAG_MIP_POINT}, + {"point point linear", D3D12_FILTER_MIN_MAG_POINT_MIP_LINEAR}, + {"point linear point", D3D12_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT}, + {"point linear linear", D3D12_FILTER_MIN_POINT_MAG_MIP_LINEAR}, + {"linear point point", D3D12_FILTER_MIN_LINEAR_MAG_MIP_POINT}, + {"linear point linear", D3D12_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR}, + {"linear linear point", D3D12_FILTER_MIN_MAG_LINEAR_MIP_POINT}, + {"linear linear linear", D3D12_FILTER_MIN_MAG_MIP_LINEAR}, + }; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(filters); ++i) + { + if (match_string(line, filters[i].string, &line)) + { + sampler->filter = filters[i].filter; + return; + } + } + + fatal_error("Unknown sampler filter '%s'.\n", line); + } + else + { + fatal_error("Unknown sampler directive '%s'.\n", line); + } +} + +static void parse_texture_directive(struct texture *texture, const char *line) +{ + int ret; + + if (match_string(line, "format", &line)) + { + parse_texture_format(texture, line); + } + else if (match_string(line, "size", &line)) + { + ret = sscanf(line, "( %u , %u )", &texture->width, &texture->height); + if (ret < 2) + fatal_error("Malformed texture size '%s'.\n", line); + } + else + { + union + { + float f; + int32_t i; + uint32_t u; + } u; + char *rest; + + u.u = 0; + + for (;;) + { + switch (texture->data_type) + { + case TEXTURE_DATA_FLOAT: + u.f = strtof(line, &rest); + break; + + case TEXTURE_DATA_SINT: + u.i = strtol(line, &rest, 10); + break; + + case TEXTURE_DATA_UINT: + u.u = strtoul(line, &rest, 10); + break; + } + + if (rest == line) + break; + + vkd3d_array_reserve((void **)&texture->data, &texture->data_capacity, texture->data_size + sizeof(u), 1); + memcpy(texture->data + texture->data_size, &u, sizeof(u)); + texture->data_size += sizeof(u); + line = rest; + } + } +} + +static void parse_test_directive(struct shader_context *context, const char *line) +{ + if (match_string(line, "draw quad", &line)) + { + D3D12_SHADER_BYTECODE ps + = {ID3D10Blob_GetBufferPointer(context->ps_code), ID3D10Blob_GetBufferSize(context->ps_code)}; + ID3D12GraphicsCommandList *command_list = context->c.list; + D3D12_ROOT_SIGNATURE_DESC root_signature_desc = {0}; + D3D12_ROOT_PARAMETER root_params[3], *root_param; + D3D12_STATIC_SAMPLER_DESC static_samplers[1]; + static const float clear_color[4]; + unsigned int uniform_index; + ID3D12PipelineState *pso; + HRESULT hr; + size_t i; + + root_signature_desc.NumParameters = 0; + root_signature_desc.pParameters = root_params; + root_signature_desc.NumStaticSamplers = 0; + root_signature_desc.pStaticSamplers = static_samplers; + + if (context->uniform_count) + { + uniform_index = root_signature_desc.NumParameters++; + root_param = &root_params[uniform_index]; + root_param->ParameterType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS; + root_param->Constants.ShaderRegister = 0; + root_param->Constants.RegisterSpace = 0; + root_param->Constants.Num32BitValues = context->uniform_count; + root_param->ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; + } + + for (i = 0; i < context->texture_count; ++i) + { + struct texture *texture = &context->textures[i]; + D3D12_DESCRIPTOR_RANGE *range = &texture->descriptor_range; + D3D12_SUBRESOURCE_DATA resource_data; + + texture->root_index = root_signature_desc.NumParameters++; + root_param = &root_params[texture->root_index]; + root_param->ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; + root_param->DescriptorTable.NumDescriptorRanges = 1; + root_param->DescriptorTable.pDescriptorRanges = range; + root_param->ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; + + range->RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV; + range->NumDescriptors = 1; + range->BaseShaderRegister = texture->slot; + range->RegisterSpace = 0; + range->OffsetInDescriptorsFromTableStart = 0; + + if (!texture->resource) + { + texture->heap = create_gpu_descriptor_heap(context->c.device, + D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 1); + texture->resource = create_default_texture(context->c.device, texture->width, texture->height, + texture->format, 0, D3D12_RESOURCE_STATE_COPY_DEST); + resource_data.pData = texture->data; + resource_data.SlicePitch = resource_data.RowPitch = texture->width * texture->texel_size; + upload_texture_data(texture->resource, &resource_data, 1, context->c.queue, command_list); + reset_command_list(command_list, context->c.allocator); + transition_resource_state(command_list, texture->resource, D3D12_RESOURCE_STATE_COPY_DEST, + D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE | D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); + ID3D12Device_CreateShaderResourceView(context->c.device, texture->resource, + NULL, get_cpu_descriptor_handle(&context->c, texture->heap, 0)); + } + } + + assert(root_signature_desc.NumParameters <= ARRAY_SIZE(root_params)); + + for (i = 0; i < context->sampler_count; ++i) + { + D3D12_STATIC_SAMPLER_DESC *sampler_desc = &static_samplers[root_signature_desc.NumStaticSamplers++]; + const struct sampler *sampler = &context->samplers[i]; + + memset(sampler_desc, 0, sizeof(*sampler_desc)); + sampler_desc->Filter = sampler->filter; + sampler_desc->AddressU = sampler->u_address; + sampler_desc->AddressV = sampler->v_address; + sampler_desc->AddressW = sampler->w_address; + sampler_desc->ShaderRegister = sampler->slot; + sampler_desc->RegisterSpace = 0; + sampler_desc->ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; + } + + if (context->c.root_signature) + ID3D12RootSignature_Release(context->c.root_signature); + hr = create_root_signature(context->c.device, &root_signature_desc, &context->c.root_signature); + ok(hr == S_OK, "Failed to create root signature, hr %#x.\n", hr); + + pso = create_pipeline_state(context->c.device, context->c.root_signature, context->c.render_target_desc.Format, + NULL, &ps, NULL); + if (!pso) + return; + vkd3d_array_reserve((void **)&context->c.pso, &context->c.pso_capacity, context->c.pso_count + 1, sizeof(*context->c.pso)); + context->c.pso[context->c.pso_count++] = pso; + + ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context->c.root_signature); + if (context->uniform_count) + ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstants(command_list, uniform_index, + context->uniform_count, context->uniforms, 0); + for (i = 0; i < context->texture_count; ++i) + ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(command_list, context->textures[i].root_index, + get_gpu_descriptor_handle(&context->c, context->textures[i].heap, 0)); + + ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context->c.rtv, false, NULL); + ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context->c.scissor_rect); + ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context->c.viewport); + ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context->c.rtv, clear_color, 0, NULL); + ID3D12GraphicsCommandList_SetPipelineState(command_list, pso); + ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0); + transition_resource_state(command_list, context->c.render_target, + D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE); + } + else if (match_string(line, "probe all rgba", &line)) + { + unsigned int ulps; + struct vec4 v; + int ret; + + ret = sscanf(line, "( %f , %f , %f , %f ) %u", &v.x, &v.y, &v.z, &v.w, &ulps); + if (ret < 4) + fatal_error("Malformed probe arguments '%s'.\n", line); + if (ret < 5) + ulps = 0; + check_sub_resource_vec4(context->c.render_target, 0, context->c.queue, context->c.list, &v, ulps); + reset_command_list(context->c.list, context->c.allocator); + } + else if (match_string(line, "probe rect rgba", &line)) + { + unsigned int x, y, w, h, ulps; + struct resource_readback rb; + 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); + if (ret < 8) + fatal_error("Malformed probe arguments '%s'.\n", line); + if (ret < 9) + ulps = 0; + + get_texture_readback_with_command_list(context->c.render_target, 0, &rb, context->c.queue, context->c.list); + rect.left = x; + rect.right = x + w; + rect.top = y; + rect.bottom = y + h; + check_readback_data_vec4(&rb, &rect, &v, ulps); + release_resource_readback(&rb); + reset_command_list(context->c.list, context->c.allocator); + } + else if (match_string(line, "probe rgba", &line)) + { + struct resource_readback rb; + unsigned int x, y, ulps; + struct vec4 v; + RECT rect; + int ret; + + ret = sscanf(line, "( %u , %u ) ( %f , %f , %f , %f ) %u", &x, &y, &v.x, &v.y, &v.z, &v.w, &ulps); + if (ret < 6) + fatal_error("Malformed probe arguments '%s'.\n", line); + if (ret < 7) + ulps = 0; + + get_texture_readback_with_command_list(context->c.render_target, 0, &rb, context->c.queue, context->c.list); + rect.left = x; + rect.right = x + 1; + rect.top = y; + rect.bottom = y + 1; + check_readback_data_vec4(&rb, &rect, &v, ulps); + release_resource_readback(&rb); + reset_command_list(context->c.list, context->c.allocator); + } + else if (match_string(line, "uniform", &line)) + { + unsigned int offset; + + if (!sscanf(line, "%u", &offset)) + fatal_error("Unknown uniform type '%s'.\n", line); + line = strchr(line, ' ') + 1; + + if (match_string(line, "float4", &line)) + { + struct vec4 v; + + if (sscanf(line, "%f %f %f %f", &v.x, &v.y, &v.z, &v.w) < 4) + fatal_error("Malformed float4 constant '%s'.\n", line); + if (offset + 4 > context->uniform_count) + { + context->uniform_count = offset + 4; + context->uniforms = realloc(context->uniforms, context->uniform_count * sizeof(*context->uniforms)); + } + memcpy(context->uniforms + offset, &v, sizeof(v)); + } + else if (match_string(line, "float", &line)) + { + float f; + + if (sscanf(line, "%f", &f) < 1) + fatal_error("Malformed float constant '%s'.\n", line); + if (offset + 1 > context->uniform_count) + { + context->uniform_count = offset + 1; + context->uniforms = realloc(context->uniforms, context->uniform_count * sizeof(*context->uniforms)); + } + memcpy(context->uniforms + offset, &f, sizeof(f)); + } + else if (match_string(line, "int", &line)) + { + int i; + + if (sscanf(line, "%i", &i) < 1) + fatal_error("Malformed int constant '%s'.\n", line); + if (offset + 1 > context->uniform_count) + { + context->uniform_count = offset + 1; + context->uniforms = realloc(context->uniforms, context->uniform_count * sizeof(*context->uniforms)); + } + memcpy(context->uniforms + offset, &i, sizeof(i)); + } + else if (match_string(line, "uint", &line)) + { + unsigned int u; + + if (sscanf(line, "%u", &u) < 1) + fatal_error("Malformed uint constant '%s'.\n", line); + if (offset + 1 > context->uniform_count) + { + context->uniform_count = offset + 1; + context->uniforms = realloc(context->uniforms, context->uniform_count * sizeof(*context->uniforms)); + } + memcpy(context->uniforms + offset, &u, sizeof(u)); + } + } + else + { + fatal_error("Unknown test directive '%s'.\n", line); + } +} + +static struct sampler *get_sampler(struct shader_context *context, unsigned int slot) +{ + struct sampler *sampler; + size_t i; + + for (i = 0; i < context->sampler_count; ++i) + { + sampler = &context->samplers[i]; + + if (sampler->slot == slot) + return sampler; + } + + return NULL; +} + +static struct texture *get_texture(struct shader_context *context, unsigned int slot) +{ + struct texture *texture; + size_t i; + + for (i = 0; i < context->texture_count; ++i) + { + texture = &context->textures[i]; + if (texture->slot == slot) + return texture; + } + + return NULL; +} + +void run_shader_tests(int argc, char **argv, const struct shader_runner_ops *ops) +{ + static const struct test_context_desc desc = + { + .rt_width = 640, + .rt_height = 480, + .no_root_signature = true, + .rt_format = DXGI_FORMAT_R32G32B32A32_FLOAT, + }; + size_t shader_source_size = 0, shader_source_len = 0; + struct sampler *current_sampler = NULL; + struct texture *current_texture = NULL; + enum parse_state state = STATE_NONE; + unsigned int i, line_number = 0; + struct shader_context context; + const char *filename = NULL; + char *shader_source = NULL; + char line[256]; + FILE *f; + + parse_args(argc, argv); + enable_d3d12_debug_layer(argc, argv); + init_adapter_info(); + + for (i = 1; i < argc; ++i) + { + if (argv[i][0] != '-') + { + filename = argv[i]; + break; + } + } + + if (!filename) + { + fprintf(stderr, "Usage: %s [file]\n", argv[0]); + return; + } + + if (!(f = fopen(filename, "r"))) + { + fatal_error("Unable to open '%s' for reading: %s\n", argv[1], strerror(errno)); + return; + } + + memset(&context, 0, sizeof(context)); + context.ops = ops; + init_test_context(&context.c, &desc); + + for (;;) + { + char *ret = fgets(line, sizeof(line), f); + + ++line_number; + + if (!ret || line[0] == '[') + { + switch (state) + { + case STATE_NONE: + case STATE_SAMPLER: + case STATE_TEST: + case STATE_TEXTURE: + break; + + case STATE_SHADER_PIXEL: + if (!(context.ps_code = context.ops->compile_shader(shader_source))) + return; + shader_source_len = 0; + break; + + case STATE_SHADER_INVALID_PIXEL: + { + ID3D10Blob *blob = NULL, *errors = NULL; + HRESULT hr; + + hr = D3DCompile(shader_source, strlen(shader_source), NULL, + NULL, NULL, "main", "ps_4_0", 0, 0, &blob, &errors); + ok(hr == E_FAIL, "Got unexpected hr %#x.\n", hr); + ok(!blob, "Expected no compiled shader blob.\n"); + ok(!!errors, "Expected non-NULL error blob.\n"); + if (!errors) + return; + + if (vkd3d_test_state.debug_level) + trace("%s\n", (char *)ID3D10Blob_GetBufferPointer(errors)); + ID3D10Blob_Release(errors); + + shader_source_len = 0; + break; + } + + case STATE_PREPROC_INVALID: + { + ID3D10Blob *blob = NULL, *errors = NULL; + HRESULT hr; + + hr = D3DPreprocess(shader_source, strlen(shader_source), NULL, NULL, NULL, &blob, &errors); + ok(hr == E_FAIL, "Got unexpected hr %#x.\n", hr); + ok(!blob, "Expected no compiled shader blob.\n"); + ok(!!errors, "Expected non-NULL error blob.\n"); + + if (errors) + { + if (vkd3d_test_state.debug_level) + trace("%s\n", (char *)ID3D10Blob_GetBufferPointer(errors)); + ID3D10Blob_Release(errors); + } + + shader_source_len = 0; + break; + } + + case STATE_PREPROC: + { + ID3D10Blob *blob = NULL, *errors = NULL; + SIZE_T size; + HRESULT hr; + char *text; + + hr = D3DPreprocess(shader_source, strlen(shader_source), NULL, NULL, NULL, &blob, &errors); + ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); + if (hr == S_OK) + { + if (errors) + { + if (vkd3d_test_state.debug_level) + trace("%s\n", (char *)ID3D10Blob_GetBufferPointer(errors)); + ID3D10Blob_Release(errors); + } + + text = ID3D10Blob_GetBufferPointer(blob); + size = ID3D10Blob_GetBufferSize(blob); + ok(vkd3d_memmem(text, size, "pass", strlen("pass")), + "'pass' not found in preprocessed shader.\n"); + ok(!vkd3d_memmem(text, size, "fail", strlen("fail")), + "'fail' found in preprocessed shader.\n"); + ID3D10Blob_Release(blob); + } + + shader_source_len = 0; + break; + } + } + } + + if (!ret) + break; + + if (line[0] == '[') + { + unsigned int index; + + if (!strcmp(line, "[pixel shader]\n")) + { + state = STATE_SHADER_PIXEL; + + if (context.ps_code) + ID3D10Blob_Release(context.ps_code); + context.ps_code = NULL; + } + else if (!strcmp(line, "[pixel shader fail]\n")) + { + state = STATE_SHADER_INVALID_PIXEL; + } + else if (sscanf(line, "[sampler %u]\n", &index)) + { + state = STATE_SAMPLER; + + if ((current_sampler = get_sampler(&context, index))) + { + memset(current_sampler, 0, sizeof(*current_sampler)); + } + else + { + context.samplers = realloc(context.samplers, + ++context.sampler_count * sizeof(*context.samplers)); + current_sampler = &context.samplers[context.sampler_count - 1]; + memset(current_sampler, 0, sizeof(*current_sampler)); + } + current_sampler->slot = index; + current_sampler->filter = D3D12_FILTER_MIN_MAG_MIP_POINT; + current_sampler->u_address = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; + current_sampler->v_address = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; + current_sampler->w_address = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; + } + else if (sscanf(line, "[texture %u]\n", &index)) + { + state = STATE_TEXTURE; + + if ((current_texture = get_texture(&context, index))) + { + free_texture(current_texture); + } + else + { + context.textures = realloc(context.textures, + ++context.texture_count * sizeof(*context.textures)); + current_texture = &context.textures[context.texture_count - 1]; + memset(current_texture, 0, sizeof(*current_texture)); + } + current_texture->slot = index; + current_texture->format = DXGI_FORMAT_R32G32B32A32_FLOAT; + current_texture->data_type = TEXTURE_DATA_FLOAT; + current_texture->texel_size = 16; + } + else if (!strcmp(line, "[test]\n")) + { + state = STATE_TEST; + } + else if (!strcmp(line, "[preproc]\n")) + { + state = STATE_PREPROC; + } + else if (!strcmp(line, "[preproc fail]\n")) + { + state = STATE_PREPROC_INVALID; + } + + vkd3d_test_set_context("Section %.*s, line %u", strlen(line) - 1, line, line_number); + } + else if (line[0] != '%' && line[0] != '\n') + { + switch (state) + { + case STATE_NONE: + fatal_error("Malformed line '%s'.\n", line); + break; + + case STATE_PREPROC: + case STATE_PREPROC_INVALID: + case STATE_SHADER_INVALID_PIXEL: + case STATE_SHADER_PIXEL: + { + size_t len = strlen(line); + + vkd3d_array_reserve((void **)&shader_source, &shader_source_size, shader_source_len + len + 1, 1); + memcpy(shader_source + shader_source_len, line, len + 1); + shader_source_len += len; + break; + } + + case STATE_SAMPLER: + parse_sampler_directive(current_sampler, line); + break; + + case STATE_TEXTURE: + parse_texture_directive(current_texture, line); + break; + + case STATE_TEST: + parse_test_directive(&context, line); + break; + } + } + } + + if (context.ps_code) + ID3D10Blob_Release(context.ps_code); + for (i = 0; i < context.texture_count; ++i) + free_texture(&context.textures[i]); + destroy_test_context(&context.c); + + fclose(f); +} + +START_TEST(shader_runner) +{ + run_shader_tests_d3d12(argc, argv); +} diff --git a/tests/shader_runner.h b/tests/shader_runner.h new file mode 100644 index 000000000..5178c6544 --- /dev/null +++ b/tests/shader_runner.h @@ -0,0 +1,29 @@ +/* + * 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 + */ + +#include "vkd3d_windows.h" +#include "vkd3d_d3dcommon.h" + +struct shader_runner_ops +{ + ID3D10Blob *(*compile_shader)(const char *source); +}; + +void run_shader_tests(int argc, char **argv, const struct shader_runner_ops *ops); + +void run_shader_tests_d3d12(int argc, char **argv); diff --git a/tests/shader_runner_d3d12.c b/tests/shader_runner_d3d12.c index 47f419b96..6246c8701 100644 --- a/tests/shader_runner_d3d12.c +++ b/tests/shader_runner_d3d12.c @@ -1,5 +1,5 @@ /* - * Copyright 2020 Zebediah Figura for CodeWeavers + * Copyright 2020-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 @@ -16,127 +16,21 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ -/* - * This application contains code derived from piglit, the license for which - * follows: - * - * Copyright © 2010 Intel Corporation - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ - -#include "d3d12_crosstest.h" -#include - -static void VKD3D_NORETURN VKD3D_PRINTF_FUNC(1, 2) fatal_error(const char *format, ...) -{ - va_list args; - - va_start(args, format); - vfprintf(stderr, format, args); - va_end(args); - exit(1); -} - -static bool vkd3d_array_reserve(void **elements, size_t *capacity, size_t element_count, size_t element_size) -{ - size_t new_capacity, max_capacity; - void *new_elements; - - if (element_count <= *capacity) - return true; - - max_capacity = ~(size_t)0 / element_size; - if (max_capacity < element_count) - return false; - - new_capacity = max(*capacity, 4); - while (new_capacity < element_count && new_capacity <= max_capacity / 2) - new_capacity *= 2; - - if (new_capacity < element_count) - new_capacity = element_count; - - if (!(new_elements = realloc(*elements, new_capacity * element_size))) - return false; - - *elements = new_elements; - *capacity = new_capacity; - - return true; -} - -enum texture_data_type -{ - TEXTURE_DATA_FLOAT, - TEXTURE_DATA_SINT, - TEXTURE_DATA_UINT, -}; +#define COBJMACROS +#define CONST_VTABLE +#define VKD3D_TEST_NO_DEFS +#include "vkd3d_windows.h" +#include "vkd3d_d3dcommon.h" +#include "vkd3d_d3dcompiler.h" +#include "vkd3d_test.h" +#include "shader_runner.h" -struct sampler -{ - unsigned int slot; - - D3D12_FILTER filter; - D3D12_TEXTURE_ADDRESS_MODE u_address, v_address, w_address; -}; - -struct texture -{ - unsigned int slot; - - DXGI_FORMAT format; - enum texture_data_type data_type; - unsigned int texel_size; - unsigned int width, height; - uint8_t *data; - size_t data_size, data_capacity; - - D3D12_DESCRIPTOR_RANGE descriptor_range; - ID3D12DescriptorHeap *heap; - ID3D12Resource *resource; - unsigned int root_index; -}; - -struct shader_context -{ - struct test_context c; - - ID3D10Blob *ps_code; - - uint32_t *uniforms; - size_t uniform_count; - - struct texture *textures; - size_t texture_count; - - struct sampler *samplers; - size_t sampler_count; -}; - -static ID3D10Blob *compile_shader(const char *source, const char *target) +static ID3D10Blob *d3d12_runner_compile_shader(const char *source) { ID3D10Blob *blob = NULL, *errors = NULL; HRESULT hr; - hr = D3DCompile(source, strlen(source), NULL, NULL, NULL, "main", target, 0, 0, &blob, &errors); + hr = D3DCompile(source, strlen(source), NULL, NULL, NULL, "main", "ps_4_0", 0, 0, &blob, &errors); ok(hr == S_OK, "Failed to compile shader, hr %#x.\n", hr); if (errors) { @@ -147,723 +41,12 @@ static ID3D10Blob *compile_shader(const char *source, const char *target) return blob; } -static void free_texture(struct texture *texture) +static const struct shader_runner_ops d3d12_runner_ops = { - ID3D12DescriptorHeap_Release(texture->heap); - ID3D12Resource_Release(texture->resource); - free(texture->data); - memset(texture, 0, sizeof(*texture)); -} - -enum parse_state -{ - STATE_NONE, - STATE_PREPROC, - STATE_PREPROC_INVALID, - STATE_SAMPLER, - STATE_SHADER_INVALID_PIXEL, - STATE_SHADER_PIXEL, - STATE_TEXTURE, - STATE_TEST, + .compile_shader = d3d12_runner_compile_shader, }; -static bool match_string(const char *line, const char *token, const char **const rest) -{ - size_t len = strlen(token); - - if (strncmp(line, token, len) || !isspace(line[len])) - return false; - if (rest) - { - *rest = line + len; - while (isspace(**rest)) - ++*rest; - } - return true; -} - -static void parse_texture_format(struct texture *texture, const char *line) -{ - static const struct - { - const char *string; - enum texture_data_type data_type; - unsigned int texel_size; - DXGI_FORMAT format; - } - formats[] = - { - {"r32g32b32a32 float", TEXTURE_DATA_FLOAT, 16, DXGI_FORMAT_R32G32B32A32_FLOAT}, - {"r32g32 uint", TEXTURE_DATA_UINT, 8, DXGI_FORMAT_R32G32_UINT}, - {"r32 float", TEXTURE_DATA_FLOAT, 4, DXGI_FORMAT_R32_FLOAT}, - {"r32 sint", TEXTURE_DATA_SINT, 4, DXGI_FORMAT_R32_SINT}, - }; - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(formats); ++i) - { - if (match_string(line, formats[i].string, &line)) - { - texture->format = formats[i].format; - texture->data_type = formats[i].data_type; - texture->texel_size = formats[i].texel_size; - return; - } - } - - fatal_error("Unknown format '%s'.\n", line); -} - -static D3D12_TEXTURE_ADDRESS_MODE parse_sampler_address_mode(const char *line, const char **rest) -{ - if (match_string(line, "border", rest)) - return D3D12_TEXTURE_ADDRESS_MODE_BORDER; - if (match_string(line, "clamp", rest)) - return D3D12_TEXTURE_ADDRESS_MODE_CLAMP; - if (match_string(line, "mirror_once", rest)) - return D3D12_TEXTURE_ADDRESS_MODE_MIRROR_ONCE; - if (match_string(line, "mirror", rest)) - return D3D12_TEXTURE_ADDRESS_MODE_MIRROR; - if (match_string(line, "wrap", rest)) - return D3D12_TEXTURE_ADDRESS_MODE_WRAP; - - fatal_error("Unknown sampler address mode '%s'.\n", line); -} - -static void parse_sampler_directive(struct sampler *sampler, const char *line) -{ - if (match_string(line, "address", &line)) - { - sampler->u_address = parse_sampler_address_mode(line, &line); - sampler->v_address = parse_sampler_address_mode(line, &line); - sampler->w_address = parse_sampler_address_mode(line, &line); - } - else if (match_string(line, "filter", &line)) - { - static const struct - { - const char *string; - D3D12_FILTER filter; - } - filters[] = - { - {"point point point", D3D12_FILTER_MIN_MAG_MIP_POINT}, - {"point point linear", D3D12_FILTER_MIN_MAG_POINT_MIP_LINEAR}, - {"point linear point", D3D12_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT}, - {"point linear linear", D3D12_FILTER_MIN_POINT_MAG_MIP_LINEAR}, - {"linear point point", D3D12_FILTER_MIN_LINEAR_MAG_MIP_POINT}, - {"linear point linear", D3D12_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR}, - {"linear linear point", D3D12_FILTER_MIN_MAG_LINEAR_MIP_POINT}, - {"linear linear linear", D3D12_FILTER_MIN_MAG_MIP_LINEAR}, - }; - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(filters); ++i) - { - if (match_string(line, filters[i].string, &line)) - { - sampler->filter = filters[i].filter; - return; - } - } - - fatal_error("Unknown sampler filter '%s'.\n", line); - } - else - { - fatal_error("Unknown sampler directive '%s'.\n", line); - } -} - -static void parse_texture_directive(struct texture *texture, const char *line) -{ - int ret; - - if (match_string(line, "format", &line)) - { - parse_texture_format(texture, line); - } - else if (match_string(line, "size", &line)) - { - ret = sscanf(line, "( %u , %u )", &texture->width, &texture->height); - if (ret < 2) - fatal_error("Malformed texture size '%s'.\n", line); - } - else - { - union - { - float f; - int32_t i; - uint32_t u; - } u; - char *rest; - - u.u = 0; - - for (;;) - { - switch (texture->data_type) - { - case TEXTURE_DATA_FLOAT: - u.f = strtof(line, &rest); - break; - - case TEXTURE_DATA_SINT: - u.i = strtol(line, &rest, 10); - break; - - case TEXTURE_DATA_UINT: - u.u = strtoul(line, &rest, 10); - break; - } - - if (rest == line) - break; - - vkd3d_array_reserve((void **)&texture->data, &texture->data_capacity, texture->data_size + sizeof(u), 1); - memcpy(texture->data + texture->data_size, &u, sizeof(u)); - texture->data_size += sizeof(u); - line = rest; - } - } -} - -static void parse_test_directive(struct shader_context *context, const char *line) -{ - if (match_string(line, "draw quad", &line)) - { - D3D12_SHADER_BYTECODE ps - = {ID3D10Blob_GetBufferPointer(context->ps_code), ID3D10Blob_GetBufferSize(context->ps_code)}; - ID3D12GraphicsCommandList *command_list = context->c.list; - D3D12_ROOT_SIGNATURE_DESC root_signature_desc = {0}; - D3D12_ROOT_PARAMETER root_params[3], *root_param; - D3D12_STATIC_SAMPLER_DESC static_samplers[1]; - static const float clear_color[4]; - unsigned int uniform_index; - ID3D12PipelineState *pso; - HRESULT hr; - size_t i; - - root_signature_desc.NumParameters = 0; - root_signature_desc.pParameters = root_params; - root_signature_desc.NumStaticSamplers = 0; - root_signature_desc.pStaticSamplers = static_samplers; - - if (context->uniform_count) - { - uniform_index = root_signature_desc.NumParameters++; - root_param = &root_params[uniform_index]; - root_param->ParameterType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS; - root_param->Constants.ShaderRegister = 0; - root_param->Constants.RegisterSpace = 0; - root_param->Constants.Num32BitValues = context->uniform_count; - root_param->ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; - } - - for (i = 0; i < context->texture_count; ++i) - { - struct texture *texture = &context->textures[i]; - D3D12_DESCRIPTOR_RANGE *range = &texture->descriptor_range; - D3D12_SUBRESOURCE_DATA resource_data; - - texture->root_index = root_signature_desc.NumParameters++; - root_param = &root_params[texture->root_index]; - root_param->ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; - root_param->DescriptorTable.NumDescriptorRanges = 1; - root_param->DescriptorTable.pDescriptorRanges = range; - root_param->ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; - - range->RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV; - range->NumDescriptors = 1; - range->BaseShaderRegister = texture->slot; - range->RegisterSpace = 0; - range->OffsetInDescriptorsFromTableStart = 0; - - if (!texture->resource) - { - texture->heap = create_gpu_descriptor_heap(context->c.device, - D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 1); - texture->resource = create_default_texture(context->c.device, texture->width, texture->height, - texture->format, 0, D3D12_RESOURCE_STATE_COPY_DEST); - resource_data.pData = texture->data; - resource_data.SlicePitch = resource_data.RowPitch = texture->width * texture->texel_size; - upload_texture_data(texture->resource, &resource_data, 1, context->c.queue, command_list); - reset_command_list(command_list, context->c.allocator); - transition_resource_state(command_list, texture->resource, D3D12_RESOURCE_STATE_COPY_DEST, - D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE | D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); - ID3D12Device_CreateShaderResourceView(context->c.device, texture->resource, - NULL, get_cpu_descriptor_handle(&context->c, texture->heap, 0)); - } - } - - assert(root_signature_desc.NumParameters <= ARRAY_SIZE(root_params)); - - for (i = 0; i < context->sampler_count; ++i) - { - D3D12_STATIC_SAMPLER_DESC *sampler_desc = &static_samplers[root_signature_desc.NumStaticSamplers++]; - const struct sampler *sampler = &context->samplers[i]; - - memset(sampler_desc, 0, sizeof(*sampler_desc)); - sampler_desc->Filter = sampler->filter; - sampler_desc->AddressU = sampler->u_address; - sampler_desc->AddressV = sampler->v_address; - sampler_desc->AddressW = sampler->w_address; - sampler_desc->ShaderRegister = sampler->slot; - sampler_desc->RegisterSpace = 0; - sampler_desc->ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; - } - - if (context->c.root_signature) - ID3D12RootSignature_Release(context->c.root_signature); - hr = create_root_signature(context->c.device, &root_signature_desc, &context->c.root_signature); - ok(hr == S_OK, "Failed to create root signature, hr %#x.\n", hr); - - pso = create_pipeline_state(context->c.device, context->c.root_signature, context->c.render_target_desc.Format, - NULL, &ps, NULL); - if (!pso) - return; - vkd3d_array_reserve((void **)&context->c.pso, &context->c.pso_capacity, context->c.pso_count + 1, sizeof(*context->c.pso)); - context->c.pso[context->c.pso_count++] = pso; - - ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context->c.root_signature); - if (context->uniform_count) - ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstants(command_list, uniform_index, - context->uniform_count, context->uniforms, 0); - for (i = 0; i < context->texture_count; ++i) - ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(command_list, context->textures[i].root_index, - get_gpu_descriptor_handle(&context->c, context->textures[i].heap, 0)); - - ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context->c.rtv, false, NULL); - ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context->c.scissor_rect); - ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context->c.viewport); - ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); - ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context->c.rtv, clear_color, 0, NULL); - ID3D12GraphicsCommandList_SetPipelineState(command_list, pso); - ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0); - transition_resource_state(command_list, context->c.render_target, - D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE); - } - else if (match_string(line, "probe all rgba", &line)) - { - unsigned int ulps; - struct vec4 v; - int ret; - - ret = sscanf(line, "( %f , %f , %f , %f ) %u", &v.x, &v.y, &v.z, &v.w, &ulps); - if (ret < 4) - fatal_error("Malformed probe arguments '%s'.\n", line); - if (ret < 5) - ulps = 0; - check_sub_resource_vec4(context->c.render_target, 0, context->c.queue, context->c.list, &v, ulps); - reset_command_list(context->c.list, context->c.allocator); - } - else if (match_string(line, "probe rect rgba", &line)) - { - unsigned int x, y, w, h, ulps; - struct resource_readback rb; - 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); - if (ret < 8) - fatal_error("Malformed probe arguments '%s'.\n", line); - if (ret < 9) - ulps = 0; - - get_texture_readback_with_command_list(context->c.render_target, 0, &rb, context->c.queue, context->c.list); - rect.left = x; - rect.right = x + w; - rect.top = y; - rect.bottom = y + h; - check_readback_data_vec4(&rb, &rect, &v, ulps); - release_resource_readback(&rb); - reset_command_list(context->c.list, context->c.allocator); - } - else if (match_string(line, "probe rgba", &line)) - { - struct resource_readback rb; - unsigned int x, y, ulps; - struct vec4 v; - RECT rect; - int ret; - - ret = sscanf(line, "( %u , %u ) ( %f , %f , %f , %f ) %u", &x, &y, &v.x, &v.y, &v.z, &v.w, &ulps); - if (ret < 6) - fatal_error("Malformed probe arguments '%s'.\n", line); - if (ret < 7) - ulps = 0; - - get_texture_readback_with_command_list(context->c.render_target, 0, &rb, context->c.queue, context->c.list); - rect.left = x; - rect.right = x + 1; - rect.top = y; - rect.bottom = y + 1; - check_readback_data_vec4(&rb, &rect, &v, ulps); - release_resource_readback(&rb); - reset_command_list(context->c.list, context->c.allocator); - } - else if (match_string(line, "uniform", &line)) - { - unsigned int offset; - - if (!sscanf(line, "%u", &offset)) - fatal_error("Unknown uniform type '%s'.\n", line); - line = strchr(line, ' ') + 1; - - if (match_string(line, "float4", &line)) - { - struct vec4 v; - - if (sscanf(line, "%f %f %f %f", &v.x, &v.y, &v.z, &v.w) < 4) - fatal_error("Malformed float4 constant '%s'.\n", line); - if (offset + 4 > context->uniform_count) - { - context->uniform_count = offset + 4; - context->uniforms = realloc(context->uniforms, context->uniform_count * sizeof(*context->uniforms)); - } - memcpy(context->uniforms + offset, &v, sizeof(v)); - } - else if (match_string(line, "float", &line)) - { - float f; - - if (sscanf(line, "%f", &f) < 1) - fatal_error("Malformed float constant '%s'.\n", line); - if (offset + 1 > context->uniform_count) - { - context->uniform_count = offset + 1; - context->uniforms = realloc(context->uniforms, context->uniform_count * sizeof(*context->uniforms)); - } - memcpy(context->uniforms + offset, &f, sizeof(f)); - } - else if (match_string(line, "int", &line)) - { - int i; - - if (sscanf(line, "%i", &i) < 1) - fatal_error("Malformed int constant '%s'.\n", line); - if (offset + 1 > context->uniform_count) - { - context->uniform_count = offset + 1; - context->uniforms = realloc(context->uniforms, context->uniform_count * sizeof(*context->uniforms)); - } - memcpy(context->uniforms + offset, &i, sizeof(i)); - } - else if (match_string(line, "uint", &line)) - { - unsigned int u; - - if (sscanf(line, "%u", &u) < 1) - fatal_error("Malformed uint constant '%s'.\n", line); - if (offset + 1 > context->uniform_count) - { - context->uniform_count = offset + 1; - context->uniforms = realloc(context->uniforms, context->uniform_count * sizeof(*context->uniforms)); - } - memcpy(context->uniforms + offset, &u, sizeof(u)); - } - } - else - { - fatal_error("Unknown test directive '%s'.\n", line); - } -} - -static struct sampler *get_sampler(struct shader_context *context, unsigned int slot) -{ - struct sampler *sampler; - size_t i; - - for (i = 0; i < context->sampler_count; ++i) - { - sampler = &context->samplers[i]; - - if (sampler->slot == slot) - return sampler; - } - - return NULL; -} - -static struct texture *get_texture(struct shader_context *context, unsigned int slot) -{ - struct texture *texture; - size_t i; - - for (i = 0; i < context->texture_count; ++i) - { - texture = &context->textures[i]; - if (texture->slot == slot) - return texture; - } - - return NULL; -} - -START_TEST(shader_runner_d3d12) +void run_shader_tests_d3d12(int argc, char **argv) { - static const struct test_context_desc desc = - { - .rt_width = 640, - .rt_height = 480, - .no_root_signature = true, - .rt_format = DXGI_FORMAT_R32G32B32A32_FLOAT, - }; - size_t shader_source_size = 0, shader_source_len = 0; - struct sampler *current_sampler = NULL; - struct texture *current_texture = NULL; - enum parse_state state = STATE_NONE; - unsigned int i, line_number = 0; - struct shader_context context; - const char *filename = NULL; - char *shader_source = NULL; - char line[256]; - FILE *f; - - parse_args(argc, argv); - enable_d3d12_debug_layer(argc, argv); - init_adapter_info(); - - for (i = 1; i < argc; ++i) - { - if (argv[i][0] != '-') - { - filename = argv[i]; - break; - } - } - - if (!filename) - { - fprintf(stderr, "Usage: %s [file]\n", argv[0]); - return; - } - - if (!(f = fopen(filename, "r"))) - { - fatal_error("Unable to open '%s' for reading: %s\n", argv[1], strerror(errno)); - return; - } - - memset(&context, 0, sizeof(context)); - init_test_context(&context.c, &desc); - - for (;;) - { - char *ret = fgets(line, sizeof(line), f); - - ++line_number; - - if (!ret || line[0] == '[') - { - switch (state) - { - case STATE_NONE: - case STATE_SAMPLER: - case STATE_TEST: - case STATE_TEXTURE: - break; - - case STATE_SHADER_PIXEL: - if (!(context.ps_code = compile_shader(shader_source, "ps_4_0"))) - return; - shader_source_len = 0; - break; - - case STATE_SHADER_INVALID_PIXEL: - { - ID3D10Blob *blob = NULL, *errors = NULL; - HRESULT hr; - - hr = D3DCompile(shader_source, strlen(shader_source), NULL, - NULL, NULL, "main", "ps_4_0", 0, 0, &blob, &errors); - ok(hr == E_FAIL, "Got unexpected hr %#x.\n", hr); - ok(!blob, "Expected no compiled shader blob.\n"); - ok(!!errors, "Expected non-NULL error blob.\n"); - if (!errors) - return; - - if (vkd3d_test_state.debug_level) - trace("%s\n", (char *)ID3D10Blob_GetBufferPointer(errors)); - ID3D10Blob_Release(errors); - - shader_source_len = 0; - break; - } - - case STATE_PREPROC_INVALID: - { - ID3D10Blob *blob = NULL, *errors = NULL; - HRESULT hr; - - hr = D3DPreprocess(shader_source, strlen(shader_source), NULL, NULL, NULL, &blob, &errors); - ok(hr == E_FAIL, "Got unexpected hr %#x.\n", hr); - ok(!blob, "Expected no compiled shader blob.\n"); - ok(!!errors, "Expected non-NULL error blob.\n"); - - if (errors) - { - if (vkd3d_test_state.debug_level) - trace("%s\n", (char *)ID3D10Blob_GetBufferPointer(errors)); - ID3D10Blob_Release(errors); - } - - shader_source_len = 0; - break; - } - - case STATE_PREPROC: - { - ID3D10Blob *blob = NULL, *errors = NULL; - SIZE_T size; - HRESULT hr; - char *text; - - hr = D3DPreprocess(shader_source, strlen(shader_source), NULL, NULL, NULL, &blob, &errors); - ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); - if (hr == S_OK) - { - if (errors) - { - if (vkd3d_test_state.debug_level) - trace("%s\n", (char *)ID3D10Blob_GetBufferPointer(errors)); - ID3D10Blob_Release(errors); - } - - text = ID3D10Blob_GetBufferPointer(blob); - size = ID3D10Blob_GetBufferSize(blob); - ok(vkd3d_memmem(text, size, "pass", strlen("pass")), - "'pass' not found in preprocessed shader.\n"); - ok(!vkd3d_memmem(text, size, "fail", strlen("fail")), - "'fail' found in preprocessed shader.\n"); - ID3D10Blob_Release(blob); - } - - shader_source_len = 0; - break; - } - } - } - - if (!ret) - break; - - if (line[0] == '[') - { - unsigned int index; - - if (!strcmp(line, "[pixel shader]\n")) - { - state = STATE_SHADER_PIXEL; - - if (context.ps_code) - ID3D10Blob_Release(context.ps_code); - context.ps_code = NULL; - } - else if (!strcmp(line, "[pixel shader fail]\n")) - { - state = STATE_SHADER_INVALID_PIXEL; - } - else if (sscanf(line, "[sampler %u]\n", &index)) - { - state = STATE_SAMPLER; - - if ((current_sampler = get_sampler(&context, index))) - { - memset(current_sampler, 0, sizeof(*current_sampler)); - } - else - { - context.samplers = realloc(context.samplers, - ++context.sampler_count * sizeof(*context.samplers)); - current_sampler = &context.samplers[context.sampler_count - 1]; - memset(current_sampler, 0, sizeof(*current_sampler)); - } - current_sampler->slot = index; - current_sampler->filter = D3D12_FILTER_MIN_MAG_MIP_POINT; - current_sampler->u_address = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; - current_sampler->v_address = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; - current_sampler->w_address = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; - } - else if (sscanf(line, "[texture %u]\n", &index)) - { - state = STATE_TEXTURE; - - if ((current_texture = get_texture(&context, index))) - { - free_texture(current_texture); - } - else - { - context.textures = realloc(context.textures, - ++context.texture_count * sizeof(*context.textures)); - current_texture = &context.textures[context.texture_count - 1]; - memset(current_texture, 0, sizeof(*current_texture)); - } - current_texture->slot = index; - current_texture->format = DXGI_FORMAT_R32G32B32A32_FLOAT; - current_texture->data_type = TEXTURE_DATA_FLOAT; - current_texture->texel_size = 16; - } - else if (!strcmp(line, "[test]\n")) - { - state = STATE_TEST; - } - else if (!strcmp(line, "[preproc]\n")) - { - state = STATE_PREPROC; - } - else if (!strcmp(line, "[preproc fail]\n")) - { - state = STATE_PREPROC_INVALID; - } - - vkd3d_test_set_context("Section %.*s, line %u", strlen(line) - 1, line, line_number); - } - else if (line[0] != '%' && line[0] != '\n') - { - switch (state) - { - case STATE_NONE: - fatal_error("Malformed line '%s'.\n", line); - break; - - case STATE_PREPROC: - case STATE_PREPROC_INVALID: - case STATE_SHADER_INVALID_PIXEL: - case STATE_SHADER_PIXEL: - { - size_t len = strlen(line); - - vkd3d_array_reserve((void **)&shader_source, &shader_source_size, shader_source_len + len + 1, 1); - memcpy(shader_source + shader_source_len, line, len + 1); - shader_source_len += len; - break; - } - - case STATE_SAMPLER: - parse_sampler_directive(current_sampler, line); - break; - - case STATE_TEXTURE: - parse_texture_directive(current_texture, line); - break; - - case STATE_TEST: - parse_test_directive(&context, line); - break; - } - } - } - - if (context.ps_code) - ID3D10Blob_Release(context.ps_code); - for (i = 0; i < context.texture_count; ++i) - free_texture(&context.textures[i]); - destroy_test_context(&context.c); - - fclose(f); + run_shader_tests(argc, argv, &d3d12_runner_ops); } -- 2.34.1