[PATCH vkd3d 5/5] tests: Add some C HLSL preprocessor tests.

Zebediah Figura zfigura at codeweavers.com
Tue Nov 24 00:29:59 CST 2020


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 at 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);
+}
-- 
2.29.2




More information about the wine-devel mailing list