Signed-off-by: Zebediah Figura zfigura@codeweavers.com --- Makefile.am | 2 + tests/preproc-if-expr.shader_test | 261 ++++++++++++++++++++++++++++++ 2 files changed, 263 insertions(+) create mode 100644 tests/preproc-if-expr.shader_test
diff --git a/Makefile.am b/Makefile.am index 0ee7eda7..2cb1567a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -66,6 +66,7 @@ vkd3d_shader_tests = \ tests/math.shader_test \ tests/preproc-if.shader_test \ tests/preproc-ifdef.shader_test \ + tests/preproc-if-expr.shader_test \ tests/swizzle-0.shader_test \ tests/swizzle-1.shader_test \ tests/swizzle-2.shader_test \ @@ -210,6 +211,7 @@ XFAIL_TESTS = \ tests/math.shader_test \ tests/preproc-if.shader_test \ tests/preproc-ifdef.shader_test \ + tests/preproc-if-expr.shader_test \ tests/swizzle-0.shader_test \ tests/swizzle-1.shader_test \ tests/swizzle-2.shader_test \ diff --git a/tests/preproc-if-expr.shader_test b/tests/preproc-if-expr.shader_test new file mode 100644 index 00000000..61c5a397 --- /dev/null +++ b/tests/preproc-if-expr.shader_test @@ -0,0 +1,261 @@ +[preproc] +#if 1 == 1 +pass +#endif + +[preproc] +#if 1 == 0 +fail +#endif +pass + +[preproc] +#if 2 +pass +#endif + +[preproc] +#if -1 +pass +#endif + +[preproc] +#if-1 +pass +#endif + +[preproc] +#if 1 + 1 == 2 +pass +#endif + +[preproc] +#if 1 + 1 == 3 +fail +#endif +pass + +[preproc] +#if 8 - 3 == 5 +pass +#endif + +[preproc] +#if 2 * 2 == 4 +pass +#endif + +[preproc] +#if 2 * 2 == 4 +pass +#endif + +[preproc] +#if 8 / 3 == 2 +pass +#endif + +[preproc] +#if 0x12 == 18 +pass +#endif + +[preproc] +#if 012 == 10 +pass +#endif + +[preproc] +#if -1 == 0xfffffff +fail +#elif -1 == 0xffffffff +pass +#endif + +[preproc] +#if -1 == 0xefffffffel +fail +#elif -1 == 0xeffffffffl +pass +#endif + +[preproc] +#if (-1 == 4294967295l) && (-1 == 8589934591l) && (1 == 4294967297l) +pass +#endif + +[preproc] +#if (-1ul == 4294967295ul) && (-1ul == 8589934591ul) && (1ul == 4294967297ul) +pass +#endif + +[preproc] +#if (-1lu == 4294967295lu) && (-1lu == 8589934591lu) && (1lu == 4294967297lu) +pass +#endif + +[preproc] +#if 36893488147419103233 == 1 +pass +#endif + +[preproc] +/* All math is done using unsigned 32-bit integers. */ +#if 8 / -3 == 2 +fail +#elif 8 / -3 == 3 +fail +#elif 8 / -3 == -2 +fail +#elif 8 / -3 == -3 +fail +#elif 8 / -3 == 0 +pass +#endif + +[preproc] +#if -8 / 3 == 2 +fail +#elif -8 / 3 == 3 +fail +#elif -8 / 3 == -2 +fail +#elif -8 / 3 == -3 +fail +#elif -8 / 3 == 1431655762 +pass +#endif + +[preproc] +#if 1 && 0 +fail +#endif +pass + +[preproc] +#if 0 && 1 +fail +#endif +pass + +[preproc] +#if 1 && 1 +pass +#endif + +[preproc] +#if 1 || 0 +pass +#endif + +[preproc] +#if 0 || 1 +pass +#endif + +[preproc] +#if 0 || 0 +fail +#endif +pass + +[preproc] +#if 1 != 1 +fail +#elif 1 != 0 +pass +#endif + +[preproc] +#if 2 < 1 +fail +#elif 2 < 2 +fail +#elif 1 < 2 +pass +#endif + +[preproc] +#if 2 <= 1 +fail +#elif (1 <= 1) && (1 <= 2) +pass +#endif + +[preproc] +#if 1 > 2 +fail +#elif 2 > 2 +fail +#elif 2 > 1 +pass +#endif + +[preproc] +#if 1 >= 2 +fail +#elif (1 >= 1) && (2 >= 1) +pass +#endif + +[preproc] +#if (2 == 2) == 1 +pass +#endif + +[preproc] +#if ((!0) == 1) && ((!1) == 0) && ((!2) == 0) +pass +#endif + +[preproc] +#if (0 ? 2 : 3) == 3 +pass +#endif + +[preproc] +#if (1 ? 2 : 3) == 2 +pass +#endif + +[preproc] +#if (6 & 3) == 2 +pass +#endif + +[preproc] +#if (6 | 3) == 7 +pass +#endif + +[preproc] +#if (6 ^ 3) == 5 +pass +#endif + +[preproc] +#if +1 == 1 +pass +#endif + +[preproc] +#if -(-1) == 1 +pass +#endif + +[preproc] +#if 2 + 3 * 5 == 17 +pass +#endif + +[preproc] +#if (2 + 3) * 5 == 25 +pass +#endif + +[preproc] +#if 0 \ +< \ +1 +pass +#endif
Signed-off-by: Zebediah Figura zfigura@codeweavers.com --- Makefile.am | 2 + tests/preproc-macro.shader_test | 303 ++++++++++++++++++++++++++++++++ 2 files changed, 305 insertions(+) create mode 100644 tests/preproc-macro.shader_test
diff --git a/Makefile.am b/Makefile.am index 2cb1567a..59e054fe 100644 --- a/Makefile.am +++ b/Makefile.am @@ -67,6 +67,7 @@ vkd3d_shader_tests = \ tests/preproc-if.shader_test \ tests/preproc-ifdef.shader_test \ tests/preproc-if-expr.shader_test \ + tests/preproc-macro.shader_test \ tests/swizzle-0.shader_test \ tests/swizzle-1.shader_test \ tests/swizzle-2.shader_test \ @@ -212,6 +213,7 @@ XFAIL_TESTS = \ tests/preproc-if.shader_test \ tests/preproc-ifdef.shader_test \ tests/preproc-if-expr.shader_test \ + tests/preproc-macro.shader_test \ tests/swizzle-0.shader_test \ tests/swizzle-1.shader_test \ tests/swizzle-2.shader_test \ diff --git a/tests/preproc-macro.shader_test b/tests/preproc-macro.shader_test new file mode 100644 index 00000000..0fe73f7c --- /dev/null +++ b/tests/preproc-macro.shader_test @@ -0,0 +1,303 @@ +[preproc] +#define KEY pass +KEY + +[preproc] +#define KEY fail +pass + +[preproc] +#define KEY(a, b) a +KEY(pass, fail) + +[preproc] +/* Make sure argument expansion replaces the right tokens. */ +#define KEY(pass, fail) fail +KEY(fail, pass) + +[preproc] +#define KEY (pass) +KEY(a,b) + +[preproc] +#define FUNC(a) pass +#define LEFT FUNC( +LEFT fail) + +[preproc] +#define KEY(a, b) a +KEY( +pass +, +fail +) + +[preproc] +/* Function-like macros which cannot be expanded (due to argument count mismatch + * or other parse errors) emit only the macro name and nothing else. In the case + * of unterminated macro lists, nothing after the macro name is emitted. */ +#define pass(a, b) a b fail +pass(fail, fail +fail + +[preproc] +#define pass(a, b) fail +pass(fail, fail, fail) + +[preproc] +#define KEY(a, b) fail +KEY(fail, fail, fail) +pass + +[preproc] +#define pass(a, b) fail +pass +(a, b) + +[preproc] +#define FUNC(a) pass +#define KEY FUNC +KEY (fail) + +[preproc] +#define KEY(a, b) a ## b +KEY ( pa , ss ) + +[preproc] +#define KEY(a, b) pa ## ss +KEY(fail, fail) + +[preproc] +#define KEY(a) a ## ss +KEY(pa) + +[preproc] +#define KEY(a) pa ## a +KEY(ss) + +[preproc] +#define KEY(x) p \ + ## \ + x \ + ## \ + ss +KEY( +a +) + +[preproc] +/* Concatenation is only parsed if the macro has arguments. */ +#define KEY fa ## il +KEY pass + +[preproc] +#define KEY(a) a +KEY(pa ## ss) + +[preproc] +fa ## il +pass + +[preproc] +#define KEY1 KEY2 +#define KEY2 pass +KEY1 + +[preproc] +#define KEY2 pass +#define KEY1 KEY2 +KEY1 + +[preproc] +#define KEY fail +#undef KEY +KEY pass + +[preproc] +#define KEY(a,b) fail +#undef KEY +KEY(pass, pass) + +[preproc] +#define KEY1 KEY2 +#define KEY2 fail +#undef KEY2 +KEY1 pass + +[preproc] +#define KEY2 fail +#define KEY1 KEY2 +#undef KEY2 +KEY1 pass + +[preproc] +#define KEY1(a, b) a +#define KEY2 pass +#define KEY3 fail +KEY1(KEY2, KEY3) + +[preproc] +#define a b +#define KEY(a) b +KEY(fail) +pass + +[preproc] +#define a b +#define KEY(a) a +KEY(pass) + +[preproc] +#define OP== +#if 2 OP 3 +fail +#elif 2 OP 2 +pass +#endif + +[preproc] +#define KEY 1 +#if KEY == 1 +pass +#endif + +[preproc] +#define KEY(a, b) (a < b) +#if KEY(2, 1) +fail +#elif KEY(1, 2) +pass +#endif + +[preproc] +#define KEY fail +#define KEY pass +KEY + +[preproc] +/* Identifiers are not expanded in the LHS of #define statements. */ +#define KEY pass +#define KEY fail +pass + +[preproc] +#undef KEY +pass + +[preproc] +#define KEY(a, b) b +KEY("fail,fail",pass) + +[preproc] +#define \ +KEY( \ +a \ +, \ +b \ +) \ +a +KEY(pass, fail) + +[preproc] +#define \ +KEY \ +pass +KEY + +[preproc] +#define KEY(a, b) b +KEY(multiline +argument,pass) + +[preproc] +#define KEY(a, b) b +KEY( +multiline +#define fail pass +argument, fail) + +[preproc] +#define KEY(a, b) a +KEY((pass,pass),(fail,fail)) + +[preproc] +#define KEY(a, b) b +KEY((,,fail,,fail,,),(,,pass,,pass,,)) + +[preproc] +#define KEY(a, b) b +KEY([,,fail,,fail,,],[,,pass,,pass,,]) + +[preproc] +#define KEY(a, b) b +KEY({,,fail,,fail,,},{,,pass,,pass,,}) + +[preproc] +#define KEY(a, b) pass +KEY((),()) + +[preproc] +#define KEY(a, b) pass +KEY((,),(,)) + +[preproc] +/* Unbalanced parentheses result in an unterminated macro. */ +#define pass(a, b) fail +pass((),() + +[preproc] +#define KEY(a,a) a +KEY(pass,fail) + +[preproc] +/* Macro arguments suffer their own macro expansion only after the macro has + * been completely parsed. */ +#define KEY(a) a +KEY(fail +#define fail pass +) + +[preproc] +/* The same applies to #if. */ +#define KEY(a) a +KEY( +#define OBJ +#ifdef OBJ +pass +#endif +) + +[preproc] +#define KEY(a) a +KEY(pass +#ifdef OBJ +fail +#endif +#define OBJ +) + +[preproc] +/* Directives inside of macro arguments are always evaluated. */ +#define FUNC(a) value +FUNC(fail +#define KEY pass +) +KEY + +[preproc] +#define FUNC1(a) a ## ss +#define FUNC2(a, b) a < b + +FUNC1( +#if FUNC2(3, 2) +fail +#elif FUNC2(2, 3) +pa +#else +fail +#endif +) + +[preproc] +#define __LINE__ pass +__LINE__
Signed-off-by: Henri Verbeet hverbeet@codeweavers.com
Signed-off-by: Matteo Bruni mbruni@codeweavers.com --- Okay, this is getting into pretty deep madness. Nice tests btw.
I forgot that sometimes D3DPreprocess() returns success and "some" output even when it reports actual errors. I don't know that we should also check for those errors (for some value of "check") in the test. I'd say no until we have a very good reason.
The other side is the sometimes peculiar output of the preprocessor, in particular WRT macro expansion. Unfortunately we might eventually want to check the exact output because, as it's invariably the case, there are applications that depend on it (https://bugs.winehq.org/show_bug.cgi?id=31701).
Somewhat related, I don't know if it makes sense to add a test option to dump the output of D3DPreprocess(). Just putting it out there, I hacked a trace() in the test runner and that's good enough for me anyway.
On 11/24/20 3:21 PM, Matteo Bruni wrote:
Signed-off-by: Matteo Bruni mbruni@codeweavers.com
Okay, this is getting into pretty deep madness. Nice tests btw.
I forgot that sometimes D3DPreprocess() returns success and "some" output even when it reports actual errors. I don't know that we should also check for those errors (for some value of "check") in the test. I'd say no until we have a very good reason.
I'm inclined to agree.
The other side is the sometimes peculiar output of the preprocessor, in particular WRT macro expansion. Unfortunately we might eventually want to check the exact output because, as it's invariably the case, there are applications that depend on it (https://bugs.winehq.org/show_bug.cgi?id=31701).
That's an interesting point.
I did try to avoid testing exact spacing, because it does constrain the implementation, but if that's a necessary constraint, then it may make the most sense just to check exact output anyway. Somewhat fortunately, my current implementation actually matches the spacing of native relatively closely.
I'm not sure how this interacts with the shader_test format. It's easy enough to have multiple tests in the same file; this could even be done for the other shader tests such as swizzle-*; I only avoided it for the sake of visual clarity, but perhaps that's not worthwhile.
Somewhat related, I don't know if it makes sense to add a test option to dump the output of D3DPreprocess(). Just putting it out there, I hacked a trace() in the test runner and that's good enough for me anyway.
It's probably not unreasonable to do that on VKD3D_TEST_DEBUG=3.
Signed-off-by: Zebediah Figura zfigura@codeweavers.com --- Makefile.am | 2 ++ tests/preproc-invalid.shader_test | 8 ++++++++ tests/shader_runner_d3d12.c | 25 +++++++++++++++++++++++++ 3 files changed, 35 insertions(+) create mode 100644 tests/preproc-invalid.shader_test
diff --git a/Makefile.am b/Makefile.am index 59e054fe..ec827076 100644 --- a/Makefile.am +++ b/Makefile.am @@ -67,6 +67,7 @@ vkd3d_shader_tests = \ tests/preproc-if.shader_test \ tests/preproc-ifdef.shader_test \ tests/preproc-if-expr.shader_test \ + tests/preproc-invalid.shader_test \ tests/preproc-macro.shader_test \ tests/swizzle-0.shader_test \ tests/swizzle-1.shader_test \ @@ -213,6 +214,7 @@ XFAIL_TESTS = \ tests/preproc-if.shader_test \ tests/preproc-ifdef.shader_test \ tests/preproc-if-expr.shader_test \ + tests/preproc-invalid.shader_test \ tests/preproc-macro.shader_test \ tests/swizzle-0.shader_test \ tests/swizzle-1.shader_test \ diff --git a/tests/preproc-invalid.shader_test b/tests/preproc-invalid.shader_test new file mode 100644 index 00000000..8a30aad3 --- /dev/null +++ b/tests/preproc-invalid.shader_test @@ -0,0 +1,8 @@ +[preproc fail] +#error + +[preproc fail] +#include <nonexistent_file> + +[preproc fail] +#include "nonexistent_file" diff --git a/tests/shader_runner_d3d12.c b/tests/shader_runner_d3d12.c index 39495fa2..bf5c3e08 100644 --- a/tests/shader_runner_d3d12.c +++ b/tests/shader_runner_d3d12.c @@ -103,6 +103,7 @@ enum parse_state { STATE_NONE, STATE_PREPROC, + STATE_PREPROC_INVALID, STATE_SHADER_INVALID_PIXEL, STATE_SHADER_PIXEL, STATE_TEST, @@ -347,6 +348,27 @@ START_TEST(shader_runner_d3d12) 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; @@ -389,6 +411,8 @@ START_TEST(shader_runner_d3d12) 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); } @@ -402,6 +426,7 @@ START_TEST(shader_runner_d3d12) break;
case STATE_PREPROC: + case STATE_PREPROC_INVALID: case STATE_SHADER_INVALID_PIXEL: case STATE_SHADER_PIXEL: {
Signed-off-by: Henri Verbeet hverbeet@codeweavers.com
Signed-off-by: Matteo Bruni mbruni@codeweavers.com
Signed-off-by: Zebediah Figura zfigura@codeweavers.com --- Makefile.am | 2 + tests/preproc-misc.shader_test | 80 ++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+) create mode 100644 tests/preproc-misc.shader_test
diff --git a/Makefile.am b/Makefile.am index ec827076..fc0bad34 100644 --- a/Makefile.am +++ b/Makefile.am @@ -69,6 +69,7 @@ vkd3d_shader_tests = \ tests/preproc-if-expr.shader_test \ tests/preproc-invalid.shader_test \ tests/preproc-macro.shader_test \ + tests/preproc-misc.shader_test \ tests/swizzle-0.shader_test \ tests/swizzle-1.shader_test \ tests/swizzle-2.shader_test \ @@ -216,6 +217,7 @@ XFAIL_TESTS = \ tests/preproc-if-expr.shader_test \ tests/preproc-invalid.shader_test \ tests/preproc-macro.shader_test \ + tests/preproc-misc.shader_test \ tests/swizzle-0.shader_test \ tests/swizzle-1.shader_test \ tests/swizzle-2.shader_test \ diff --git a/tests/preproc-misc.shader_test b/tests/preproc-misc.shader_test new file mode 100644 index 00000000..f14c5993 --- /dev/null +++ b/tests/preproc-misc.shader_test @@ -0,0 +1,80 @@ +[preproc] + # define KEY pass +KEY + +[preproc] +#if 0 +#error +#endif +pass + +[preproc] +#define pass fail +"pass" + +[preproc] +#define pass fail +" escape " pass " + +[preproc] +#define fail pass +" escape \" fail + +[preproc] +"multiline +string" +pass + +[preproc] +"multiline +pass" + +[preproc] +"pass +string" + +[preproc] +/* +#error +fail + * / fail*/pass + +[preproc] +pass/* +#error +fail + */ + +[preproc] +pass//fail + +[preproc] +// multiline comment \ +fail +pass + +[preproc] +fa/* */il +pass + +[preproc] +#define KEY // +KEY pass + +[preproc] +#define KEY /* fail */ pass +KEY + +[preproc] +#define KEY(a, b) /* multiline + +comment */ a +KEY(pass, fail) + +[preproc] +pass /* fail + +[preproc] +/* The backslash must be the last character in the line. */ +#define KEY \ +pass
Signed-off-by: Henri Verbeet hverbeet@codeweavers.com
Signed-off-by: Matteo Bruni mbruni@codeweavers.com --- The patch intentionally introduces a trailing whitespace, reported by git.
On 11/24/20 3:21 PM, Matteo Bruni wrote:
Signed-off-by: Matteo Bruni mbruni@codeweavers.com
The patch intentionally introduces a trailing whitespace, reported by git.
Hmm, indeed. I noticed that and thought "I should probably move that to hlsl_d3d12.c", but never actually did. I'll send a separate patch if this one is committed...
That is, tests in C, for features of the HLSL preprocessor which are difficult to test using the shader_runner framework.
Signed-off-by: Zebediah Figura zfigura@codeweavers.com --- Makefile.am | 4 +- include/private/vkd3d_common.h | 14 ++ tests/.gitignore | 1 + tests/d3d12_crosstest.h | 1 + tests/hlsl_d3d12.c | 413 +++++++++++++++++++++++++++++++++ 5 files changed, 432 insertions(+), 1 deletion(-) create mode 100644 tests/hlsl_d3d12.c
diff --git a/Makefile.am b/Makefile.am index fc0bad34..5a6e4dc9 100644 --- a/Makefile.am +++ b/Makefile.am @@ -44,7 +44,8 @@ vkd3d_tests = \
vkd3d_cross_tests = \ tests/d3d12 \ - tests/d3d12_invalid_usage + tests/d3d12_invalid_usage \ + tests/hlsl_d3d12
vkd3d_shader_runners = \ tests/shader_runner_d3d12 @@ -193,6 +194,7 @@ check_PROGRAMS = $(vkd3d_tests) $(vkd3d_cross_tests) $(vkd3d_shader_runners) 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_vkd3d_api_LDADD = libvkd3d.la @VULKAN_LIBS@ tests_vkd3d_shader_api_LDADD = libvkd3d-shader.la diff --git a/include/private/vkd3d_common.h b/include/private/vkd3d_common.h index ed80f48a..23495fc2 100644 --- a/include/private/vkd3d_common.h +++ b/include/private/vkd3d_common.h @@ -120,6 +120,20 @@ static inline unsigned int vkd3d_log2i(unsigned int x) #endif }
+static inline void *vkd3d_memmem( const void *haystack, size_t haystack_len, const void *needle, size_t needle_len) +{ + const char *str = haystack; + + while (haystack_len >= needle_len) + { + if (!memcmp(str, needle, needle_len)) + return (char *)str; + ++str; + --haystack_len; + } + return NULL; +} + static inline int ascii_isupper(int c) { return 'A' <= c && c <= 'Z'; diff --git a/tests/.gitignore b/tests/.gitignore index 94d003dd..475a1502 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -1,5 +1,6 @@ /d3d12 /d3d12_invalid_usage +/hlsl_d3d12 /shader_runner_d3d12 /vkd3d_api /vkd3d_common diff --git a/tests/d3d12_crosstest.h b/tests/d3d12_crosstest.h index f7a31804..43af375b 100644 --- a/tests/d3d12_crosstest.h +++ b/tests/d3d12_crosstest.h @@ -40,6 +40,7 @@ typedef int HRESULT; #endif
#define COBJMACROS +#define CONST_VTABLE #define INITGUID #include "vkd3d_test.h" #include "vkd3d_windows.h" diff --git a/tests/hlsl_d3d12.c b/tests/hlsl_d3d12.c new file mode 100644 index 00000000..03a732ce --- /dev/null +++ b/tests/hlsl_d3d12.c @@ -0,0 +1,413 @@ +/* + * Copyright (C) 2020 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 "config.h" +#include "d3d12_crosstest.h" +#include "vkd3d_common.h" + +#define check_preprocess(a, b, c, d, e) check_preprocess_(__LINE__, a, b, c, d, e) +static void check_preprocess_(int line, const char *source, const D3D_SHADER_MACRO *macros, + ID3DInclude *include, const char *present, const char *absent) +{ + ID3D10Blob *blob, *errors; + const char *code; + SIZE_T size; + HRESULT hr; + + hr = D3DPreprocess(source, strlen(source), NULL, macros, include, &blob, &errors); + todo ok_(line)(hr == S_OK, "Failed to preprocess shader, hr %#x.\n", hr); + if (errors) + { + if (vkd3d_test_state.debug_level) + trace_(line)("%s\n", (char *)ID3D10Blob_GetBufferPointer(errors)); + ID3D10Blob_Release(errors); + } + if (hr != S_OK) + return; + code = ID3D10Blob_GetBufferPointer(blob); + size = ID3D10Blob_GetBufferSize(blob); + if (present) + ok_(line)(vkd3d_memmem(code, size, present, strlen(present)), + ""%s" not found in preprocessed shader.\n", present); + if (absent) + ok_(line)(!vkd3d_memmem(code, size, absent, strlen(absent)), + ""%s" found in preprocessed shader.\n", absent); + ID3D10Blob_Release(blob); +} + +static const char test_include_top[] = + "#include "file1"\n" + "#include < file2 >\n" + "ARGES\n"; + +static const char test_include_file1[] = + "#define BRONTES\n" + "#include "file3"\n" + "#ifndef BRONTES\n" + "#define STEROPES\n" + "#endif"; + +static const char test_include_file2[] = + "#ifdef STEROPES\n" + "#define ARGES pass\n" + "#undef STEROPES\n" + "#include < file2 >\n" + "#endif"; + +static const char test_include_file3[] = + "#undef BRONTES"; + +static unsigned int refcount_file1, refcount_file2, refcount_file3, include_count_file2; + +static HRESULT WINAPI test_include_Open(ID3DInclude *iface, D3D_INCLUDE_TYPE type, + const char *filename, const void *parent_data, const void **code, UINT *size) +{ + ok(!*code, "Data pointer should be zeroed.\n"); + ok(!*size, "Size pointer should be zeroed.\n"); + if (!strcmp(filename, "file1")) + { + ok(type == D3D_INCLUDE_LOCAL, "Got type %#x.\n", type); + ok(!parent_data, "Got parent data %p.\n", parent_data); + *code = test_include_file1; + *size = strlen(test_include_file1); + ++refcount_file1; + } + else if (!strcmp(filename, " file2 ")) + { + ok(type == D3D_INCLUDE_SYSTEM, "Got type %#x.\n", type); + if (!include_count_file2++) + ok(!parent_data, "Got parent data %p.\n", parent_data); + else + ok(parent_data == test_include_file2, "Got parent data %p.\n", parent_data); + *code = test_include_file2; + *size = strlen(test_include_file2); + ++refcount_file2; + } + else if (!strcmp(filename, "file3")) + { + ok(type == D3D_INCLUDE_LOCAL, "Got type %#x.\n", type); + ok(parent_data == test_include_file1, "Got parent data %p.\n", parent_data); + *code = test_include_file3; + *size = strlen(test_include_file3); + ++refcount_file3; + } + else + { + ok(0, "Unexpected filename "%s".\n", filename); + } + return S_FALSE; +} + +static HRESULT WINAPI test_include_Close(ID3DInclude *iface, const void *code) +{ + if (code == test_include_file1) + --refcount_file1; + else if (code == test_include_file2) + --refcount_file2; + else if (code == test_include_file3) + --refcount_file3; + return E_FAIL; +} + +static const struct ID3DIncludeVtbl test_include_vtbl = +{ + test_include_Open, + test_include_Close, +}; + +static HRESULT WINAPI test_include_fail_Open(ID3DInclude *iface, D3D_INCLUDE_TYPE type, + const char *filename, const void *parent_data, const void **code, UINT *size) +{ + return 0xdeadbeef; +} + +static HRESULT WINAPI test_include_fail_Close(ID3DInclude *iface, const void *code) +{ + ok(0, "Unexpected call.\n"); + return E_FAIL; +} + +static const struct ID3DIncludeVtbl test_include_fail_vtbl = +{ + test_include_fail_Open, + test_include_fail_Close, +}; + +static void test_preprocess(void) +{ + ID3DInclude test_include_fail = {&test_include_fail_vtbl}; + ID3DInclude test_include = {&test_include_vtbl}; + D3D_SHADER_MACRO macros[3]; + ID3D10Blob *blob, *errors; + unsigned int i; + HRESULT hr; + + static const struct + { + const char *source; + const char *present; + const char *absent; + } + tests[] = + { + /* Stringification. */ + { + "#define KEY(a) #a\n" + "KEY(apple)", + + ""apple"", + }, + { + "#define KEY(a) # a\n" + "KEY(apple)", + + ""apple"", + }, + { + "#define KEY(if) #if\n" + "KEY(apple)", + + ""apple"", + }, + { + "#define KEY(a) #a\n" + "KEY("apple")", + + ""\"apple\""", + }, + { + "#define KEY(a) #b\n" + "KEY(apple)", + + ""b"", + }, + { + "#define KEY(a) a\n" + "KEY(banana #apple)", + + "#", + ""apple"", + }, + { + "#define KEY #apple\n" + "KEY", + + "apple", + ""apple"", + }, + { + "banana #apple\n", + + "apple", + ""apple"", + }, + { + "banana #apple\n", + + "#", + }, + + /* #pragma is preserved. */ + { + "#pragma pack_matrix(column_major)\n" + "text", + + "#pragma pack_matrix(column_major)\n", + }, + + /* DOS-style newlines. */ + { + "#define KEY(a, b) \\r\n" + " a ## b\r\n" + "KEY(pa,\r\n" + "ss\r\n" + ")\r\n" + "#ifndef KEY\r\n" + "fail\r\n" + "#endif\r\n", + + "pass", + "fail", + }, + { + "#define KEY(a, b) \\r\n" + " a ## b\n" + "KEY(pa,\r\n" + "ss\n" + ")\r\n" + "#ifndef KEY\n" + "fail\r\n" + "#endif\n", + + "pass", + "fail", + }, + + /* Pre-defined macros. */ + { + "__LINE__", + "1", + "__LINE__", + }, + { + "\n" + "__LINE__", + "2", + "__LINE__", + }, + { + "#define KEY __LINE__\n" + "KEY", + "2", + }, + + /* Tokens which must be preserved verbatim for HLSL (i.e. which cannot + * be broken up with spaces). */ + {"<<", "<<"}, + {">>", ">>"}, + {"++", "++"}, + {"--", "--"}, + {"+=", "+="}, + {"-=", "-="}, + {"*=", "*="}, + {"/=", "/="}, + {"%=", "%="}, + {"&=", "&="}, + {"|=", "|="}, + {"^=", "^="}, + {"<<=", "<<="}, + {">>=", ">>="}, + {"0.0", "0.0"}, + {".0", ".0"}, + {"0.", "0."}, + {"1e1", "1e1"}, + {"1E1", "1E1"}, + {"1e+1", "1e+1"}, + {"1e-1", "1e-1"}, + {".0f", ".0f"}, + {".0F", ".0F"}, + {".0h", ".0h"}, + {".0H", ".0H"}, + {"0.f", "0.f"}, + {"1.1e-1f", "1.1e-1f"}, + + /* Parentheses are emitted for object-like macros invoked like + * function-like macros. */ + { + "#define KEY value\n" + "KEY(apple)", + + "(", + }, + + /* Function-like macro with no parentheses and no following tokens (a + * corner case in our implementation). */ + { + "#define pass(a) fail\n" + "pass", + + "pass", + "fail", + }, + + /* A single-line comment not terminated by a newline. */ + { + "pass // fail", + + "pass", + "fail", + }, + }; + + for (i = 0; i < ARRAY_SIZE(tests); ++i) + { + vkd3d_test_set_context("Source "%s"", tests[i].source); + check_preprocess(tests[i].source, NULL, NULL, tests[i].present, tests[i].absent); + } + vkd3d_test_set_context(NULL); + + macros[0].Name = "KEY"; + macros[0].Definition = "value"; + macros[1].Name = NULL; + macros[1].Definition = NULL; + check_preprocess("KEY", macros, NULL, "value", "KEY"); + + check_preprocess("#undef KEY\nKEY", macros, NULL, "KEY", "value"); + + macros[0].Name = NULL; + check_preprocess("KEY", macros, NULL, "KEY", "value"); + + macros[0].Name = "KEY"; + macros[0].Definition = NULL; + check_preprocess("KEY", macros, NULL, NULL, "KEY"); + + macros[0].Name = "0"; + macros[0].Definition = "value"; + check_preprocess("0", macros, NULL, "0", "value"); + + macros[0].Name = "KEY(a)"; + macros[0].Definition = "value"; + check_preprocess("KEY(a)", macros, NULL, "KEY", "value"); + + macros[0].Name = "KEY"; + macros[0].Definition = "value1"; + macros[1].Name = "KEY"; + macros[1].Definition = "value2"; + macros[2].Name = NULL; + macros[2].Definition = NULL; + check_preprocess("KEY", macros, NULL, "value2", NULL); + + macros[0].Name = "KEY"; + macros[0].Definition = "KEY2"; + macros[1].Name = "KEY2"; + macros[1].Definition = "value"; + check_preprocess("KEY", macros, NULL, "value", NULL); + + macros[0].Name = "KEY2"; + macros[0].Definition = "value"; + macros[1].Name = "KEY"; + macros[1].Definition = "KEY2"; + check_preprocess("KEY", macros, NULL, "value", NULL); + + check_preprocess(test_include_top, NULL, &test_include, "pass", "fail"); + ok(!refcount_file1, "Got %d references to file1.\n", refcount_file1); + ok(!refcount_file2, "Got %d references to file1.\n", refcount_file2); + ok(!refcount_file3, "Got %d references to file1.\n", refcount_file3); + todo ok(include_count_file2 == 2, "file2 was included %u times.\n", include_count_file2); + + blob = errors = (ID3D10Blob *)0xdeadbeef; + hr = D3DPreprocess(test_include_top, strlen(test_include_top), NULL, NULL, &test_include_fail, &blob, &errors); + todo ok(hr == E_FAIL, "Got hr %#x.\n", hr); + ok(blob == (ID3D10Blob *)0xdeadbeef, "Expected no compiled shader blob.\n"); + todo 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); + } +} + +START_TEST(hlsl_d3d12) +{ + parse_args(argc, argv); + enable_d3d12_debug_layer(argc, argv); + init_adapter_info(); + + run_test(test_preprocess); +}
Signed-off-by: Henri Verbeet hverbeet@codeweavers.com
Signed-off-by: Matteo Bruni mbruni@codeweavers.com