From 2379bda36f87ae8140a105d8618bc182ad5f4e61 Mon Sep 17 00:00:00 2001
From: Conor McCarthy <cmccarthy@codeweavers.com>
Date: Mon, 8 Apr 2024 14:37:32 +1000
Subject: [PATCH 1/5] tests/hlsl: Add a tessellation test.

---
 Makefile.am                         |  1 +
 tests/hlsl/tessellation.shader_test | 98 +++++++++++++++++++++++++++++
 tests/shader_runner.c               | 72 +++++++++++++++++++++
 tests/shader_runner.h               |  4 ++
 tests/shader_runner_d3d11.c         | 65 +++++++++++++++++--
 tests/shader_runner_d3d12.c         | 41 ++++++++++--
 tests/shader_runner_gl.c            | 37 +++++++++--
 tests/shader_runner_vulkan.c        | 27 ++++++--
 8 files changed, 326 insertions(+), 19 deletions(-)
 create mode 100644 tests/hlsl/tessellation.shader_test

diff --git a/Makefile.am b/Makefile.am
index f823cbc85b..2ad556c775 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -202,6 +202,7 @@ vkd3d_shader_tests = \
 	tests/hlsl/swizzle-matrix.shader_test \
 	tests/hlsl/swizzles.shader_test \
 	tests/hlsl/ternary.shader_test \
+	tests/hlsl/tessellation.shader_test \
 	tests/hlsl/texture-load-offset.shader_test \
 	tests/hlsl/texture-load-typed.shader_test \
 	tests/hlsl/texture-load.shader_test \
diff --git a/tests/hlsl/tessellation.shader_test b/tests/hlsl/tessellation.shader_test
new file mode 100644
index 0000000000..d8b2634736
--- /dev/null
+++ b/tests/hlsl/tessellation.shader_test
@@ -0,0 +1,98 @@
+[require]
+shader model >= 5.0
+
+[vertex shader]
+struct data
+{
+    float4 position : SV_Position;
+    float r : RED;
+    float g : GREEN;
+    float b : BLUE;
+};
+
+void main(uint id : SV_VertexID, out data output)
+{
+    float2 coords = float2((id << 1) & 2, id & 2);
+    output.position = float4(coords * float2(2, -2) + float2(-1, 1), 0, 1);
+    output.r = 0.0;
+    output.g = 1.0;
+    output.b = 0.0;
+}
+
+[hull shader todo]
+struct data
+{
+    float4 position : SV_Position;
+    float r : RED;
+    float g : GREEN;
+    float b : BLUE;
+};
+
+struct patch_constant_data
+{
+    float edges[3] : SV_TessFactor;
+    float inside : SV_InsideTessFactor;
+};
+
+void patch_constant(InputPatch<data, 3> input, out patch_constant_data output)
+{
+    output.edges[0] = output.edges[1] = output.edges[2] = 1.0f;
+    output.inside = 1.0f;
+}
+
+    [domain("tri")]
+    [outputcontrolpoints(3)]
+    [partitioning("integer")]
+    [outputtopology("triangle_cw")]
+    [patchconstantfunc("patch_constant")]
+data main(InputPatch<data, 3> input, uint i : SV_OutputControlPointID)
+{
+    return input[i];
+}
+
+[domain shader todo]
+struct data
+{
+    float4 position : SV_Position;
+    float r : RED;
+    float g : GREEN;
+    float b : BLUE;
+};
+
+struct patch_constant_data
+{
+    float edges[3] : SV_TessFactor;
+    float inside : SV_InsideTessFactor;
+};
+
+    [domain("tri")]
+void main(patch_constant_data input,
+        float3 tess_coord : SV_DomainLocation,
+        const OutputPatch<data, 3> patch,
+        out data output)
+{
+    output.position = tess_coord.x * patch[0].position
+            + tess_coord.y * patch[1].position
+            + tess_coord.z * patch[2].position;
+    output.r = tess_coord.x * patch[0].r + tess_coord.y * patch[1].r + tess_coord.z * patch[2].r;
+    output.g = tess_coord.x * patch[0].g + tess_coord.y * patch[1].g + tess_coord.z * patch[2].g;
+    output.b = tess_coord.x * patch[0].b + tess_coord.y * patch[1].b + tess_coord.z * patch[2].b;
+}
+
+[pixel shader]
+struct data
+{
+    float4 position : SV_Position;
+    float r : RED;
+    float g : GREEN;
+    float b : BLUE;
+};
+
+float4 main(data input) : sv_target
+{
+    return float4(input.r, input.g, input.b, 1.0);
+}
+
+[test]
+todo draw 3 control point patch list 3
+probe all rgba (0.0, 1.0, 0.0, 1.0)
diff --git a/tests/shader_runner.c b/tests/shader_runner.c
index f800ded323..359eafb952 100644
--- a/tests/shader_runner.c
+++ b/tests/shader_runner.c
@@ -107,6 +107,10 @@ enum parse_state
     STATE_SHADER_VERTEX_TODO,
     STATE_SHADER_EFFECT,
     STATE_SHADER_EFFECT_TODO,
+    STATE_SHADER_HULL,
+    STATE_SHADER_HULL_TODO,
+    STATE_SHADER_DOMAIN,
+    STATE_SHADER_DOMAIN_TODO,
     STATE_TEST,
 };
 
@@ -818,6 +822,9 @@ static void parse_test_directive(struct shader_runner *runner, const char *line)
             "    return pos;\n"
             "}";
 
+        if (!runner->hs_source != !runner->ds_source)
+            fatal_error("Have a domain or hull shader but not both.\n");
+
         if (!shader_runner_get_resource(runner, RESOURCE_TYPE_RENDER_TARGET, 0))
         {
             memset(&params, 0, sizeof(params));
@@ -868,6 +875,9 @@ static void parse_test_directive(struct shader_runner *runner, const char *line)
         struct resource_params params;
         unsigned int vertex_count;
 
+        if (!runner->hs_source != !runner->ds_source)
+            fatal_error("Have a domain or hull shader but not both.\n");
+
         if (!shader_runner_get_resource(runner, RESOURCE_TYPE_RENDER_TARGET, 0))
         {
             memset(&params, 0, sizeof(params));
@@ -888,6 +898,14 @@ static void parse_test_directive(struct shader_runner *runner, const char *line)
             topology = D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
         else if (match_string(line, "triangle strip", &line))
             topology = D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP;
+        else if (match_string(line, "1 control point patch list", &line))
+            topology = D3D_PRIMITIVE_TOPOLOGY_1_CONTROL_POINT_PATCHLIST;
+        else if (match_string(line, "2 control point patch list", &line))
+            topology = D3D_PRIMITIVE_TOPOLOGY_2_CONTROL_POINT_PATCHLIST;
+        else if (match_string(line, "3 control point patch list", &line))
+            topology = D3D_PRIMITIVE_TOPOLOGY_3_CONTROL_POINT_PATCHLIST;
+        else if (match_string(line, "4 control point patch list", &line))
+            topology = D3D_PRIMITIVE_TOPOLOGY_4_CONTROL_POINT_PATCHLIST;
         else
             fatal_error("Unknown primitive topology '%s'.\n", line);
 
@@ -1205,6 +1223,8 @@ const char *shader_type_string(enum shader_type type)
         [SHADER_TYPE_CS] = "cs",
         [SHADER_TYPE_PS] = "ps",
         [SHADER_TYPE_VS] = "vs",
+        [SHADER_TYPE_HS] = "hs",
+        [SHADER_TYPE_DS] = "ds",
         [SHADER_TYPE_FX] = "fx",
     };
     assert(type < ARRAY_SIZE(shader_types));
@@ -1266,6 +1286,8 @@ HRESULT dxc_compiler_compile_shader(void *dxc_compiler, enum shader_type type, u
         [SHADER_TYPE_CS] = L"cs_6_0",
         [SHADER_TYPE_PS] = L"ps_6_0",
         [SHADER_TYPE_VS] = L"vs_6_0",
+        [SHADER_TYPE_HS] = L"hs_6_0",
+        [SHADER_TYPE_DS] = L"ds_6_0",
     };
     const WCHAR *args[] =
     {
@@ -1393,6 +1415,10 @@ static enum parse_state get_parse_state_todo(enum parse_state state)
         return STATE_SHADER_PIXEL_TODO;
     else if (state == STATE_SHADER_VERTEX)
         return STATE_SHADER_VERTEX_TODO;
+    else if (state == STATE_SHADER_HULL)
+        return STATE_SHADER_HULL_TODO;
+    else if (state == STATE_SHADER_DOMAIN)
+        return STATE_SHADER_DOMAIN_TODO;
     else
         return STATE_SHADER_EFFECT_TODO;
 }
@@ -1613,6 +1639,36 @@ void run_shader_tests(struct shader_runner *runner, const struct shader_runner_c
                     shader_source_size = 0;
                     break;
 
+                case STATE_SHADER_HULL:
+                case STATE_SHADER_HULL_TODO:
+                    if (!skip_tests)
+                    {
+                        todo_if (state == STATE_SHADER_HULL_TODO)
+                        compile_shader(runner, dxc_compiler, shader_source, shader_source_len, SHADER_TYPE_HS,
+                                expect_hr);
+                    }
+                    free(runner->hs_source);
+                    runner->hs_source = shader_source;
+                    shader_source = NULL;
+                    shader_source_len = 0;
+                    shader_source_size = 0;
+                    break;
+
+                case STATE_SHADER_DOMAIN:
+                case STATE_SHADER_DOMAIN_TODO:
+                    if (!skip_tests)
+                    {
+                        todo_if (state == STATE_SHADER_DOMAIN_TODO)
+                        compile_shader(runner, dxc_compiler, shader_source, shader_source_len, SHADER_TYPE_DS,
+                                expect_hr);
+                    }
+                    free(runner->ds_source);
+                    runner->ds_source = shader_source;
+                    shader_source = NULL;
+                    shader_source_len = 0;
+                    shader_source_size = 0;
+                    break;
+
                 case STATE_PREPROC_INVALID:
                 {
                     ID3D10Blob *blob = NULL, *errors = NULL;
@@ -1795,6 +1851,18 @@ void run_shader_tests(struct shader_runner *runner, const struct shader_runner_c
                 expect_hr = S_OK;
                 state = read_shader_directive(runner, state, line_buffer, line, &expect_hr);
             }
+            else if (match_directive_substring(line, "[hull shader", &line))
+            {
+                state = STATE_SHADER_HULL;
+                expect_hr = S_OK;
+                state = read_shader_directive(runner, state, line_buffer, line, &expect_hr);
+            }
+            else if (match_directive_substring(line, "[domain shader", &line))
+            {
+                state = STATE_SHADER_DOMAIN;
+                expect_hr = S_OK;
+                state = read_shader_directive(runner, state, line_buffer, line, &expect_hr);
+            }
             else if (!strcmp(line, "[input layout]\n"))
             {
                 state = STATE_INPUT_LAYOUT;
@@ -1826,6 +1894,10 @@ void run_shader_tests(struct shader_runner *runner, const struct shader_runner_c
                 case STATE_SHADER_VERTEX_TODO:
                 case STATE_SHADER_EFFECT:
                 case STATE_SHADER_EFFECT_TODO:
+                case STATE_SHADER_HULL:
+                case STATE_SHADER_HULL_TODO:
+                case STATE_SHADER_DOMAIN:
+                case STATE_SHADER_DOMAIN_TODO:
                 {
                     size_t len = strlen(line);
 
diff --git a/tests/shader_runner.h b/tests/shader_runner.h
index 2f7e8fb251..dd92048f5f 100644
--- a/tests/shader_runner.h
+++ b/tests/shader_runner.h
@@ -45,6 +45,8 @@ enum shader_type
     SHADER_TYPE_CS,
     SHADER_TYPE_PS,
     SHADER_TYPE_VS,
+    SHADER_TYPE_HS,
+    SHADER_TYPE_DS,
     SHADER_TYPE_FX,
 };
 
@@ -157,6 +159,8 @@ struct shader_runner
     char *ps_source;
     char *cs_source;
     char *fx_source;
+    char *hs_source;
+    char *ds_source;
     enum shader_model minimum_shader_model;
     enum shader_model maximum_shader_model;
     bool require_float64;
diff --git a/tests/shader_runner_d3d11.c b/tests/shader_runner_d3d11.c
index b26d6a36b8..bc1c5c5f53 100644
--- a/tests/shader_runner_d3d11.c
+++ b/tests/shader_runner_d3d11.c
@@ -589,24 +589,46 @@ static bool d3d11_runner_draw(struct shader_runner *r,
 {
     ID3D11UnorderedAccessView *uavs[D3D11_PS_CS_UAV_REGISTER_COUNT] = {0};
     ID3D11RenderTargetView *rtvs[D3D11_PS_CS_UAV_REGISTER_COUNT] = {0};
+    ID3D10Blob *vs_code, *ps_code, *hs_code = NULL, *ds_code = NULL;
     struct d3d11_shader_runner *runner = d3d11_shader_runner(r);
     ID3D11DeviceContext *context = runner->immediate_context;
     unsigned int min_uav_slot = ARRAY_SIZE(uavs);
     ID3D11Device *device = runner->device;
-    ID3D10Blob *vs_code, *ps_code;
     unsigned int rtv_count = 0;
     ID3D11Buffer *cb = NULL;
     ID3D11VertexShader *vs;
+    ID3D11DomainShader *ds;
     ID3D11PixelShader *ps;
+    ID3D11HullShader *hs;
+    bool succeeded;
     unsigned int i;
     HRESULT hr;
 
-    if (!(vs_code = compile_shader(runner, runner->r.vs_source, "vs")))
-        return false;
+    vs_code = compile_shader(runner, runner->r.vs_source, "vs");
+    ps_code = compile_shader(runner, runner->r.ps_source, "ps");
+    succeeded = vs_code && ps_code;
 
-    if (!(ps_code = compile_shader(runner, runner->r.ps_source, "ps")))
+    if (runner->r.hs_source)
+    {
+        hs_code = compile_shader(runner, runner->r.hs_source, "hs");
+        succeeded = succeeded && hs_code;
+    }
+    if (runner->r.ds_source)
     {
-        ID3D10Blob_Release(vs_code);
+        ds_code = compile_shader(runner, runner->r.ds_source, "ds");
+        succeeded = succeeded && ds_code;
+    }
+
+    if (!succeeded)
+    {
+        if (ps_code)
+            ID3D10Blob_Release(ps_code);
+        if (vs_code)
+            ID3D10Blob_Release(vs_code);
+        if (hs_code)
+            ID3D10Blob_Release(hs_code);
+        if (ds_code)
+            ID3D10Blob_Release(ds_code);
         return false;
     }
 
@@ -618,12 +640,35 @@ static bool d3d11_runner_draw(struct shader_runner *r,
             ID3D10Blob_GetBufferSize(ps_code), NULL, &ps);
     ok(hr == S_OK, "Failed to create pixel shader, hr %#lx.\n", hr);
 
+    if (hs_code)
+    {
+        hr = ID3D11Device_CreateHullShader(device, ID3D10Blob_GetBufferPointer(hs_code),
+                ID3D10Blob_GetBufferSize(hs_code), NULL, &hs);
+        ok(hr == S_OK, "Failed to create hull shader, hr %#lx.\n", hr);
+    }
+    if (ds_code)
+    {
+        hr = ID3D11Device_CreateDomainShader(device, ID3D10Blob_GetBufferPointer(ds_code),
+                ID3D10Blob_GetBufferSize(ds_code), NULL, &ds);
+        ok(hr == S_OK, "Failed to create domain shader, hr %#lx.\n", hr);
+    }
+
+    ID3D10Blob_Release(ps_code);
+    if (hs_code)
+        ID3D10Blob_Release(hs_code);
+    if (ds_code)
+        ID3D10Blob_Release(ds_code);
+
     if (runner->r.uniform_count)
     {
         cb = create_buffer(device, D3D11_BIND_CONSTANT_BUFFER,
                 runner->r.uniform_count * sizeof(*runner->r.uniforms), 0, runner->r.uniforms);
         ID3D11DeviceContext_VSSetConstantBuffers(context, 0, 1, &cb);
         ID3D11DeviceContext_PSSetConstantBuffers(context, 0, 1, &cb);
+        if (hs_code)
+            ID3D11DeviceContext_HSSetConstantBuffers(context, 0, 1, &cb);
+        if (ds_code)
+            ID3D11DeviceContext_DSSetConstantBuffers(context, 0, 1, &cb);
     }
 
     for (i = 0; i < runner->r.resource_count; ++i)
@@ -694,15 +739,25 @@ static bool d3d11_runner_draw(struct shader_runner *r,
         ID3D11InputLayout_Release(input_layout);
     }
 
+    ID3D10Blob_Release(vs_code);
+
     ID3D11DeviceContext_IASetPrimitiveTopology(context, primitive_topology);
     ID3D11DeviceContext_VSSetShader(context, vs, NULL, 0);
     ID3D11DeviceContext_PSSetShader(context, ps, NULL, 0);
+    if (hs_code)
+        ID3D11DeviceContext_HSSetShader(context, hs, NULL, 0);
+    if (ds_code)
+        ID3D11DeviceContext_DSSetShader(context, ds, NULL, 0);
     ID3D11DeviceContext_RSSetState(context, runner->rasterizer_state);
 
     ID3D11DeviceContext_Draw(context, vertex_count, 0);
 
     ID3D11PixelShader_Release(ps);
     ID3D11VertexShader_Release(vs);
+    if (hs_code)
+        ID3D11HullShader_Release(hs);
+    if (ds_code)
+        ID3D11DomainShader_Release(ds);
     if (cb)
         ID3D11Buffer_Release(cb);
 
diff --git a/tests/shader_runner_d3d12.c b/tests/shader_runner_d3d12.c
index 56844ae364..48792fa7a2 100644
--- a/tests/shader_runner_d3d12.c
+++ b/tests/shader_runner_d3d12.c
@@ -434,29 +434,47 @@ static bool d3d12_runner_draw(struct shader_runner *r,
     struct test_context *test_context = &runner->test_context;
 
     D3D12_CPU_DESCRIPTOR_HANDLE rtvs[D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT] = {0};
+    ID3D10Blob *vs_code, *ps_code, *hs_code = NULL, *ds_code = NULL;
     ID3D12GraphicsCommandList *command_list = test_context->list;
     D3D12_GRAPHICS_PIPELINE_STATE_DESC pso_desc = {0};
     ID3D12CommandQueue *queue = test_context->queue;
     D3D12_INPUT_ELEMENT_DESC *input_element_descs;
     ID3D12Device *device = test_context->device;
-    ID3D10Blob *vs_code, *ps_code;
     unsigned int uniform_index;
     unsigned int rtv_count = 0;
     ID3D12PipelineState *pso;
+    bool succeeded;
     HRESULT hr;
     size_t i;
 
     ps_code = compile_shader(runner, runner->r.ps_source, SHADER_TYPE_PS);
     vs_code = compile_shader(runner, runner->r.vs_source, SHADER_TYPE_VS);
+    succeeded = ps_code && vs_code;
+
+    if (runner->r.hs_source)
+    {
+        hs_code = compile_shader(runner, runner->r.hs_source, SHADER_TYPE_HS);
+        succeeded = succeeded && hs_code;
+    }
+    if (runner->r.ds_source)
+    {
+        ds_code = compile_shader(runner, runner->r.ds_source, SHADER_TYPE_DS);
+        succeeded = succeeded && ds_code;
+    }
+
     todo_if(runner->r.is_todo && runner->r.minimum_shader_model < SHADER_MODEL_6_0)
-    ok(ps_code && vs_code, "Failed to compile shaders.\n");
+    ok(succeeded, "Failed to compile shaders.\n");
 
-    if (!ps_code || !vs_code)
+    if (!succeeded)
     {
         if (ps_code)
             ID3D10Blob_Release(ps_code);
         if (vs_code)
             ID3D10Blob_Release(vs_code);
+        if (hs_code)
+            ID3D10Blob_Release(hs_code);
+        if (ds_code)
+            ID3D10Blob_Release(ds_code);
         return false;
     }
 
@@ -481,9 +499,20 @@ static bool d3d12_runner_draw(struct shader_runner *r,
     pso_desc.VS.BytecodeLength = ID3D10Blob_GetBufferSize(vs_code);
     pso_desc.PS.pShaderBytecode = ID3D10Blob_GetBufferPointer(ps_code);
     pso_desc.PS.BytecodeLength = ID3D10Blob_GetBufferSize(ps_code);
+    if (hs_code)
+    {
+        pso_desc.HS.pShaderBytecode = ID3D10Blob_GetBufferPointer(hs_code);
+        pso_desc.HS.BytecodeLength = ID3D10Blob_GetBufferSize(hs_code);
+        pso_desc.DS.pShaderBytecode = ID3D10Blob_GetBufferPointer(ds_code);
+        pso_desc.DS.BytecodeLength = ID3D10Blob_GetBufferSize(ds_code);
+        pso_desc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_PATCH;
+    }
+    else
+    {
+        pso_desc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
+    }
     pso_desc.RasterizerState.FillMode = D3D12_FILL_MODE_SOLID;
     pso_desc.RasterizerState.CullMode = D3D12_CULL_MODE_NONE;
-    pso_desc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
     pso_desc.SampleDesc.Count = 1;
     pso_desc.SampleMask = ~(UINT)0;
     pso_desc.pRootSignature = test_context->root_signature;
@@ -510,6 +539,10 @@ static bool d3d12_runner_draw(struct shader_runner *r,
     todo_if(runner->r.is_todo) ok(hr == S_OK, "Failed to create state, hr %#x.\n", hr);
     ID3D10Blob_Release(vs_code);
     ID3D10Blob_Release(ps_code);
+    if (hs_code)
+        ID3D10Blob_Release(hs_code);
+    if (ds_code)
+        ID3D10Blob_Release(ds_code);
     free(input_element_descs);
 
     if (FAILED(hr))
diff --git a/tests/shader_runner_gl.c b/tests/shader_runner_gl.c
index 2045950e39..221f356483 100644
--- a/tests/shader_runner_gl.c
+++ b/tests/shader_runner_gl.c
@@ -804,20 +804,40 @@ static GLenum get_compare_op_gl(D3D12_COMPARISON_FUNC op)
 
 static GLuint compile_graphics_shader_program(struct gl_runner *runner, ID3D10Blob **vs_blob)
 {
+    ID3D10Blob *fs_blob, *hs_blob = NULL, *ds_blob = NULL;
     struct vkd3d_shader_code vs_code, fs_code;
     GLuint program_id, vs_id, fs_id;
     const GLchar *source;
-    ID3D10Blob *fs_blob;
     GLint status, size;
+    bool succeeded;
 
     reset_combined_samplers(runner);
 
-    if (!(*vs_blob = compile_hlsl(&runner->r, runner->r.vs_source, "vs")))
-        return false;
+    *vs_blob = compile_hlsl(&runner->r, runner->r.vs_source, "vs");
+    fs_blob = compile_hlsl(&runner->r, runner->r.ps_source, "ps");
+    succeeded = *vs_blob && fs_blob;
 
-    if (!(fs_blob = compile_hlsl(&runner->r, runner->r.ps_source, "ps")))
+    if (runner->r.hs_source)
     {
-        ID3D10Blob_Release(*vs_blob);
+        hs_blob = compile_hlsl(&runner->r, runner->r.hs_source, "hs");
+        succeeded = succeeded && hs_blob;
+    }
+    if (runner->r.ds_source)
+    {
+        ds_blob = compile_hlsl(&runner->r, runner->r.ds_source, "ds");
+        succeeded = succeeded && ds_blob;
+    }
+
+    if (!succeeded)
+    {
+        if (*vs_blob)
+            ID3D10Blob_Release(*vs_blob);
+        if (fs_blob)
+            ID3D10Blob_Release(fs_blob);
+        if (hs_blob)
+            ID3D10Blob_Release(hs_blob);
+        if (ds_blob)
+            ID3D10Blob_Release(ds_blob);
         return false;
     }
 
@@ -837,6 +857,13 @@ static GLuint compile_graphics_shader_program(struct gl_runner *runner, ID3D10Bl
     }
     ID3D10Blob_Release(fs_blob);
 
+    /* TODO: compile and use the hs and ds blobs too, but currently this
+     * point is not reached because compile_hlsl() fails on these. */
+    if (hs_blob)
+        ID3D10Blob_Release(hs_blob);
+    if (ds_blob)
+        ID3D10Blob_Release(ds_blob);
+
     vs_id = glCreateShader(GL_VERTEX_SHADER);
     if (runner->language == SPIR_V)
     {
diff --git a/tests/shader_runner_vulkan.c b/tests/shader_runner_vulkan.c
index a3bbf170c2..c903c8941a 100644
--- a/tests/shader_runner_vulkan.c
+++ b/tests/shader_runner_vulkan.c
@@ -683,9 +683,10 @@ static VkPipeline create_graphics_pipeline(struct vulkan_shader_runner *runner,
     static const VkRect2D rt_rect = {.extent.width = RENDER_TARGET_WIDTH, .extent.height = RENDER_TARGET_HEIGHT};
     VkGraphicsPipelineCreateInfo pipeline_desc = {.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO};
     VkPipelineColorBlendAttachmentState attachment_desc[MAX_RESOURCES] = {0};
+    VkPipelineTessellationStateCreateInfo tessellation_info;
     VkVertexInputAttributeDescription input_attributes[32];
     VkVertexInputBindingDescription input_bindings[32];
-    VkPipelineShaderStageCreateInfo stage_desc[2];
+    VkPipelineShaderStageCreateInfo stage_desc[4];
     struct vkd3d_shader_code vs_dxbc;
     VkDevice device = runner->device;
     VkPipeline pipeline;
@@ -696,11 +697,17 @@ static VkPipeline create_graphics_pipeline(struct vulkan_shader_runner *runner,
     memset(stage_desc, 0, sizeof(stage_desc));
     ret = create_shader_stage(runner, &stage_desc[0], "vs", VK_SHADER_STAGE_VERTEX_BIT, runner->r.vs_source, &vs_dxbc)
             && create_shader_stage(runner, &stage_desc[1], "ps", VK_SHADER_STAGE_FRAGMENT_BIT, runner->r.ps_source, NULL);
+
+    if (runner->r.hs_source)
+    {
+        ret &= create_shader_stage(runner, &stage_desc[1], "hs", VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT, runner->r.hs_source, NULL);
+        ret &= create_shader_stage(runner, &stage_desc[2], "ds", VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, runner->r.ds_source, NULL);
+    }
     todo_if (runner->r.is_todo) ok(ret, "Failed to compile shaders.\n");
     if (!ret)
     {
-        VK_CALL(vkDestroyShaderModule(device, stage_desc[0].module, NULL));
-        VK_CALL(vkDestroyShaderModule(device, stage_desc[1].module, NULL));
+        for (i = 0; i < ARRAY_SIZE(stage_desc); ++i)
+            VK_CALL(vkDestroyShaderModule(device, stage_desc[i].module, NULL));
         return VK_NULL_HANDLE;
     }
 
@@ -791,11 +798,21 @@ static VkPipeline create_graphics_pipeline(struct vulkan_shader_runner *runner,
     pipeline_desc.renderPass = render_pass;
     pipeline_desc.subpass = 0;
 
+    if (runner->r.hs_source)
+    {
+        tessellation_info.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO;
+        tessellation_info.pNext = NULL;
+        tessellation_info.flags = 0;
+        tessellation_info.patchControlPoints
+                = max(primitive_topology - D3D_PRIMITIVE_TOPOLOGY_1_CONTROL_POINT_PATCHLIST + 1, 1);
+        pipeline_desc.pTessellationState = &tessellation_info;
+    }
+
     vr = VK_CALL(vkCreateGraphicsPipelines(runner->device, VK_NULL_HANDLE, 1, &pipeline_desc, NULL, &pipeline));
     ok(vr == VK_SUCCESS, "Failed to create graphics pipeline, vr %d.\n", vr);
 
-    VK_CALL(vkDestroyShaderModule(device, stage_desc[0].module, NULL));
-    VK_CALL(vkDestroyShaderModule(device, stage_desc[1].module, NULL));
+    for (i = 0; i < ARRAY_SIZE(stage_desc); ++i)
+        VK_CALL(vkDestroyShaderModule(device, stage_desc[i].module, NULL));
     vkd3d_shader_free_scan_signature_info(&runner->vs_signatures);
     vkd3d_shader_free_shader_code(&vs_dxbc);
 
-- 
GitLab


From 02cbc511bb339100f96c12db516dbc1e18008a6f Mon Sep 17 00:00:00 2001
From: Conor McCarthy <cmccarthy@codeweavers.com>
Date: Wed, 6 Dec 2023 16:39:51 +1000
Subject: [PATCH 2/5] vkd3d-shader/dxil: Load domain shader properties.

---
 libs/vkd3d-shader/dxil.c                 | 136 ++++++++++++++++++++---
 libs/vkd3d-shader/vkd3d_shader_private.h |   4 +
 2 files changed, 127 insertions(+), 13 deletions(-)

diff --git a/libs/vkd3d-shader/dxil.c b/libs/vkd3d-shader/dxil.c
index da8ba662db..76b500c325 100644
--- a/libs/vkd3d-shader/dxil.c
+++ b/libs/vkd3d-shader/dxil.c
@@ -7685,9 +7685,40 @@ static const enum vkd3d_shader_sysval_semantic sysval_semantic_table[] =
     [SEMANTIC_KIND_TARGET]               = VKD3D_SHADER_SV_TARGET,
 };
 
-static enum vkd3d_shader_sysval_semantic sysval_semantic_from_dxil_semantic_kind(enum dxil_semantic_kind kind)
+static enum vkd3d_shader_sysval_semantic sysval_semantic_from_dxil_semantic_kind(enum dxil_semantic_kind kind,
+        enum vkd3d_tessellator_domain domain)
 {
-    if (kind < ARRAY_SIZE(sysval_semantic_table))
+    if (kind == SEMANTIC_KIND_TESSFACTOR)
+    {
+        switch (domain)
+        {
+            case VKD3D_TESSELLATOR_DOMAIN_LINE:
+                return VKD3D_SHADER_SV_TESS_FACTOR_LINEDET;
+            case VKD3D_TESSELLATOR_DOMAIN_TRIANGLE:
+                return VKD3D_SHADER_SV_TESS_FACTOR_TRIEDGE;
+            case VKD3D_TESSELLATOR_DOMAIN_QUAD:
+                return VKD3D_SHADER_SV_TESS_FACTOR_QUADEDGE;
+            default:
+                /* Error is handled during parsing. */
+                return VKD3D_SHADER_SV_TESS_FACTOR_TRIEDGE;
+        }
+    }
+    else if (kind == SEMANTIC_KIND_INSIDETESSFACTOR)
+    {
+        switch (domain)
+        {
+            case VKD3D_TESSELLATOR_DOMAIN_LINE:
+                return VKD3D_SHADER_SV_TESS_FACTOR_LINEDEN;
+            case VKD3D_TESSELLATOR_DOMAIN_TRIANGLE:
+                return VKD3D_SHADER_SV_TESS_FACTOR_TRIINT;
+            case VKD3D_TESSELLATOR_DOMAIN_QUAD:
+                return VKD3D_SHADER_SV_TESS_FACTOR_QUADINT;
+            default:
+                /* Error is handled during parsing. */
+                return VKD3D_SHADER_SV_TESS_FACTOR_TRIEDGE;
+        }
+    }
+    else if (kind < ARRAY_SIZE(sysval_semantic_table))
     {
         return sysval_semantic_table[kind];
     }
@@ -8443,7 +8474,7 @@ static void signature_element_read_additional_element_values(struct signature_el
 }
 
 static enum vkd3d_result sm6_parser_read_signature(struct sm6_parser *sm6, const struct sm6_metadata_value *m,
-        struct shader_signature *s)
+        struct shader_signature *s, enum vkd3d_tessellator_domain tessellator_domain)
 {
     unsigned int i, j, column_count, operand_count, index;
     const struct sm6_metadata_node *node, *element_node;
@@ -8536,7 +8567,7 @@ static enum vkd3d_result sm6_parser_read_signature(struct sm6_parser *sm6, const
         e->min_precision = minimum_precision_from_dxil_component_type(values[2]);
 
         j = values[3];
-        e->sysval_semantic = sysval_semantic_from_dxil_semantic_kind(j);
+        e->sysval_semantic = sysval_semantic_from_dxil_semantic_kind(j, tessellator_domain);
         if (j != SEMANTIC_KIND_ARBITRARY && j != SEMANTIC_KIND_TARGET && e->sysval_semantic == VKD3D_SHADER_SV_NONE)
         {
             WARN("Unhandled semantic kind %u.\n", j);
@@ -8619,7 +8650,8 @@ static enum vkd3d_result sm6_parser_read_signature(struct sm6_parser *sm6, const
     return VKD3D_OK;
 }
 
-static enum vkd3d_result sm6_parser_signatures_init(struct sm6_parser *sm6, const struct sm6_metadata_value *m)
+static enum vkd3d_result sm6_parser_signatures_init(struct sm6_parser *sm6, const struct sm6_metadata_value *m,
+        enum vkd3d_tessellator_domain tessellator_domain)
 {
     enum vkd3d_result ret;
 
@@ -8632,12 +8664,12 @@ static enum vkd3d_result sm6_parser_signatures_init(struct sm6_parser *sm6, cons
     }
 
     if (m->u.node->operand_count && (ret = sm6_parser_read_signature(sm6, m->u.node->operands[0],
-            &sm6->p.program.input_signature)) < 0)
+            &sm6->p.program.input_signature, tessellator_domain)) < 0)
     {
         return ret;
     }
     if (m->u.node->operand_count > 1 && (ret = sm6_parser_read_signature(sm6, m->u.node->operands[1],
-            &sm6->p.program.output_signature)) < 0)
+            &sm6->p.program.output_signature, tessellator_domain)) < 0)
     {
         return ret;
     }
@@ -8730,10 +8762,85 @@ static enum vkd3d_result sm6_parser_emit_thread_group(struct sm6_parser *sm6, co
     return VKD3D_OK;
 }
 
+static void sm6_parser_emit_dcl_tessellator_domain(struct sm6_parser *sm6,
+        enum vkd3d_tessellator_domain tessellator_domain)
+{
+    struct vkd3d_shader_instruction *ins;
+
+    if (tessellator_domain == VKD3D_TESSELLATOR_DOMAIN_INVALID || tessellator_domain >= VKD3D_TESSELLATOR_DOMAIN_COUNT)
+    {
+        WARN("Unhandled domain %u.\n", tessellator_domain);
+        vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_PROPERTIES,
+                "Domain shader tessellator domain %u is unhandled.", tessellator_domain);
+    }
+
+    ins = sm6_parser_add_instruction(sm6, VKD3DSIH_DCL_TESSELLATOR_DOMAIN);
+    ins->declaration.tessellator_domain = tessellator_domain;
+}
+
+static void sm6_parser_validate_control_point_count(struct sm6_parser *sm6, unsigned int count)
+{
+    if (!count || count > 32)
+    {
+        WARN("Invalid control point count %u.\n", count);
+        vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_PROPERTIES,
+                "Domain shader input control point count %u is invalid.", count);
+    }
+}
+
+static enum vkd3d_tessellator_domain sm6_parser_ds_properties_init(struct sm6_parser *sm6,
+        const struct sm6_metadata_value *m)
+{
+    const struct sm6_metadata_node *node;
+    unsigned int operands[2] = {0};
+    unsigned int i;
+
+    if (!m || !sm6_metadata_value_is_node(m))
+    {
+        WARN("Missing or invalid DS properties.\n");
+        vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_PROPERTIES,
+                "Domain shader properties node is missing or invalid.");
+        return 0;
+    }
+
+    node = m->u.node;
+    if (node->operand_count < ARRAY_SIZE(operands))
+    {
+        WARN("Invalid operand count %u.\n", node->operand_count);
+        vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND_COUNT,
+                "Domain shader properties operand count %u is invalid.", node->operand_count);
+        return 0;
+    }
+    if (node->operand_count > ARRAY_SIZE(operands))
+    {
+        WARN("Ignoring %zu extra operands.\n", node->operand_count - ARRAY_SIZE(operands));
+        vkd3d_shader_parser_warning(&sm6->p, VKD3D_SHADER_WARNING_DXIL_IGNORING_OPERANDS,
+                "Ignoring %zu extra operands for domain shader properties.",
+                node->operand_count - ARRAY_SIZE(operands));
+    }
+
+    for (i = 0; i < node->operand_count; ++i)
+    {
+        if (!sm6_metadata_get_uint_value(sm6, node->operands[i], &operands[i]))
+        {
+            WARN("DS property at index %u is not a uint value.\n", i);
+            vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_PROPERTIES,
+                    "Domain shader properties operand at index %u is not an integer.", i);
+        }
+    }
+
+    sm6_parser_emit_dcl_tessellator_domain(sm6, operands[0]);
+    sm6_parser_validate_control_point_count(sm6, operands[1]);
+    sm6->p.program.input_control_point_count = operands[1];
+
+    return operands[0];
+}
+
 static enum vkd3d_result sm6_parser_entry_point_init(struct sm6_parser *sm6)
 {
     const struct sm6_metadata_value *m = sm6_parser_find_named_metadata(sm6, "dx.entryPoints");
     const struct sm6_metadata_node *node, *entry_node = m ? m->u.node : NULL;
+    enum vkd3d_tessellator_domain tessellator_domain = 0;
     unsigned int i, operand_count, tag;
     const struct sm6_value *value;
     enum vkd3d_result ret;
@@ -8772,12 +8879,6 @@ static enum vkd3d_result sm6_parser_entry_point_init(struct sm6_parser *sm6)
                 "Entry point function name %s does not match the name in metadata.", sm6->entry_point);
     }
 
-    if (entry_node->operand_count >= 3 && (m = entry_node->operands[2])
-            && (ret = sm6_parser_signatures_init(sm6, m)) < 0)
-    {
-        return ret;
-    }
-
     if (entry_node->operand_count >= 5 && (m = entry_node->operands[4]))
     {
         if (!sm6_metadata_value_is_node(m))
@@ -8812,6 +8913,9 @@ static enum vkd3d_result sm6_parser_entry_point_init(struct sm6_parser *sm6)
                 case SHADER_PROPERTIES_FLAGS:
                     sm6_parser_emit_global_flags(sm6, node->operands[i + 1]);
                     break;
+                case SHADER_PROPERTIES_DOMAIN:
+                    tessellator_domain = sm6_parser_ds_properties_init(sm6, node->operands[i + 1]);
+                    break;
                case SHADER_PROPERTIES_COMPUTE:
                     if ((ret = sm6_parser_emit_thread_group(sm6, node->operands[i + 1])) < 0)
                         return ret;
@@ -8825,6 +8929,12 @@ static enum vkd3d_result sm6_parser_entry_point_init(struct sm6_parser *sm6)
         }
     }
 
+    if (entry_node->operand_count >= 3 && (m = entry_node->operands[2])
+            && (ret = sm6_parser_signatures_init(sm6, m, tessellator_domain)) < 0)
+    {
+        return ret;
+    }
+
     return VKD3D_OK;
 }
 
diff --git a/libs/vkd3d-shader/vkd3d_shader_private.h b/libs/vkd3d-shader/vkd3d_shader_private.h
index f401fd7a3f..ac8aebe0f4 100644
--- a/libs/vkd3d-shader/vkd3d_shader_private.h
+++ b/libs/vkd3d-shader/vkd3d_shader_private.h
@@ -763,9 +763,13 @@ enum vkd3d_shader_atomic_rmw_flags
 
 enum vkd3d_tessellator_domain
 {
+    VKD3D_TESSELLATOR_DOMAIN_INVALID   = 0,
+
     VKD3D_TESSELLATOR_DOMAIN_LINE      = 1,
     VKD3D_TESSELLATOR_DOMAIN_TRIANGLE  = 2,
     VKD3D_TESSELLATOR_DOMAIN_QUAD      = 3,
+
+    VKD3D_TESSELLATOR_DOMAIN_COUNT     = 4,
 };
 
 #define VKD3DSI_NONE                    0x0
-- 
GitLab


From 54016b3ced8aea7397382346886167e94a687a9f Mon Sep 17 00:00:00 2001
From: Conor McCarthy <cmccarthy@codeweavers.com>
Date: Wed, 6 Dec 2023 16:49:17 +1000
Subject: [PATCH 3/5] vkd3d-shader/dxil: Load hull shader properties.

---
 libs/vkd3d-shader/dxil.c                 | 183 ++++++++++++++++++++++-
 libs/vkd3d-shader/spirv.c                |   5 -
 libs/vkd3d-shader/vkd3d_shader_private.h |   5 +
 3 files changed, 182 insertions(+), 11 deletions(-)

diff --git a/libs/vkd3d-shader/dxil.c b/libs/vkd3d-shader/dxil.c
index 76b500c325..40ba267156 100644
--- a/libs/vkd3d-shader/dxil.c
+++ b/libs/vkd3d-shader/dxil.c
@@ -791,6 +791,7 @@ struct sm6_parser
     size_t global_symbol_count;
 
     const char *entry_point;
+    const char *patch_constant_function;
 
     struct vkd3d_shader_dst_param *output_params;
     struct vkd3d_shader_dst_param *input_params;
@@ -2790,7 +2791,7 @@ static inline uint64_t decode_rotated_signed_value(uint64_t value)
     return value << 63;
 }
 
-static inline float bitcast_uint64_to_float(uint64_t value)
+static float bitcast_uint_to_float(unsigned int value)
 {
     union
     {
@@ -2814,6 +2815,23 @@ static inline double bitcast_uint64_to_double(uint64_t value)
     return u.double_value;
 }
 
+static float register_get_float_value(const struct vkd3d_shader_register *reg)
+{
+    if (!register_is_constant(reg) || !data_type_is_floating_point(reg->data_type))
+        return 0.0;
+
+    if (reg->dimension == VSIR_DIMENSION_VEC4)
+        WARN("Returning vec4.x.\n");
+
+    if (reg->type == VKD3DSPR_IMMCONST64)
+    {
+        WARN("Truncating double to float.\n");
+        return bitcast_uint64_to_double(reg->u.immconst_u64[0]);
+    }
+
+    return bitcast_uint_to_float(reg->u.immconst_u32[0]);
+}
+
 static enum vkd3d_result value_allocate_constant_array(struct sm6_value *dst, const struct sm6_type *type,
         const uint64_t *operands, struct sm6_parser *sm6)
 {
@@ -3098,7 +3116,7 @@ static enum vkd3d_result sm6_parser_constants_init(struct sm6_parser *sm6, const
                 if (type->u.width == 16)
                     dst->u.reg.u.immconst_u32[0] = record->operands[0];
                 else if (type->u.width == 32)
-                    dst->u.reg.u.immconst_f32[0] = bitcast_uint64_to_float(record->operands[0]);
+                    dst->u.reg.u.immconst_f32[0] = bitcast_uint_to_float(record->operands[0]);
                 else if (type->u.width == 64)
                     dst->u.reg.u.immconst_f64[0] = bitcast_uint64_to_double(record->operands[0]);
                 else
@@ -6728,6 +6746,25 @@ static bool sm6_metadata_get_uint64_value(const struct sm6_parser *sm6,
     return true;
 }
 
+static bool sm6_metadata_get_float_value(const struct sm6_parser *sm6,
+        const struct sm6_metadata_value *m, float *f)
+{
+    const struct sm6_value *value;
+
+    if (!m || m->type != VKD3D_METADATA_VALUE)
+        return false;
+
+    value = m->u.value;
+    if (!sm6_value_is_constant(value))
+        return false;
+    if (!sm6_type_is_floating_point(value->type))
+        return false;
+
+    *f = register_get_float_value(&value->u.reg);
+
+    return true;
+}
+
 static void sm6_parser_metadata_attachment_block_init(struct sm6_parser *sm6, const struct dxil_block *target_block,
         const struct dxil_block *block)
 {
@@ -8762,6 +8799,14 @@ static enum vkd3d_result sm6_parser_emit_thread_group(struct sm6_parser *sm6, co
     return VKD3D_OK;
 }
 
+static void sm6_parser_emit_dcl_count(struct sm6_parser *sm6, enum vkd3d_shader_opcode handler_idx, unsigned int count)
+{
+    struct vkd3d_shader_instruction *ins;
+
+    ins = sm6_parser_add_instruction(sm6, handler_idx);
+    ins->declaration.count = count;
+}
+
 static void sm6_parser_emit_dcl_tessellator_domain(struct sm6_parser *sm6,
         enum vkd3d_tessellator_domain tessellator_domain)
 {
@@ -8778,14 +8823,72 @@ static void sm6_parser_emit_dcl_tessellator_domain(struct sm6_parser *sm6,
     ins->declaration.tessellator_domain = tessellator_domain;
 }
 
-static void sm6_parser_validate_control_point_count(struct sm6_parser *sm6, unsigned int count)
+static void sm6_parser_validate_control_point_count(struct sm6_parser *sm6, unsigned int count,
+        const char *type)
 {
     if (!count || count > 32)
     {
-        WARN("Invalid control point count %u.\n", count);
+        WARN("%s control point count %u invalid.\n", type, count);
+        vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_PROPERTIES,
+                "%s control point count %u is invalid.", type, count);
+    }
+}
+
+static void sm6_parser_emit_dcl_tessellator_partitioning(struct sm6_parser *sm6,
+        enum vkd3d_shader_tessellator_partitioning tessellator_partitioning)
+{
+    struct vkd3d_shader_instruction *ins;
+
+    if (!tessellator_partitioning || tessellator_partitioning > VKD3D_SHADER_TESSELLATOR_PARTITIONING_FRACTIONAL_EVEN)
+    {
+        WARN("Unhandled partitioning %u.\n", tessellator_partitioning);
+        vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_PROPERTIES,
+                "Hull shader tessellator partitioning %u is unhandled.", tessellator_partitioning);
+    }
+
+    ins = sm6_parser_add_instruction(sm6, VKD3DSIH_DCL_TESSELLATOR_PARTITIONING);
+    ins->declaration.tessellator_partitioning = tessellator_partitioning;
+}
+
+static void sm6_parser_emit_dcl_tessellator_output_primitive(struct sm6_parser *sm6,
+        enum vkd3d_shader_tessellator_output_primitive primitive)
+{
+    struct vkd3d_shader_instruction *ins;
+
+    if (!primitive || primitive > VKD3D_SHADER_TESSELLATOR_OUTPUT_TRIANGLE_CCW)
+    {
+        WARN("Unhandled output primitive %u.\n", primitive);
         vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_PROPERTIES,
-                "Domain shader input control point count %u is invalid.", count);
+                "Hull shader tessellator output primitive %u is unhandled.", primitive);
     }
+
+    ins = sm6_parser_add_instruction(sm6, VKD3DSIH_DCL_TESSELLATOR_OUTPUT_PRIMITIVE);
+    ins->declaration.tessellator_output_primitive = primitive;
+}
+
+static void sm6_parser_emit_dcl_max_tessellation_factor(struct sm6_parser *sm6, struct sm6_metadata_value *m)
+{
+    struct vkd3d_shader_instruction *ins;
+    float max_tessellation_factor;
+
+    if (!sm6_metadata_get_float_value(sm6, m, &max_tessellation_factor))
+    {
+        WARN("Max tess factor property is not a float value.\n");
+        vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_PROPERTIES,
+                "Hull shader max tessellation factor property operand is not a float.");
+        return;
+    }
+
+    /* Exclude non-finite values. */
+    if (!(max_tessellation_factor >= 1.0f && max_tessellation_factor <= 64.0f))
+    {
+        WARN("Invalid max tess factor %f.\n", max_tessellation_factor);
+        vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_PROPERTIES,
+                "Hull shader max tessellation factor %f is invalid.", max_tessellation_factor);
+    }
+
+    ins = sm6_parser_add_instruction(sm6, VKD3DSIH_DCL_HS_MAX_TESSFACTOR);
+    ins->declaration.max_tessellation_factor = max_tessellation_factor;
 }
 
 static enum vkd3d_tessellator_domain sm6_parser_ds_properties_init(struct sm6_parser *sm6,
@@ -8830,12 +8933,77 @@ static enum vkd3d_tessellator_domain sm6_parser_ds_properties_init(struct sm6_pa
     }
 
     sm6_parser_emit_dcl_tessellator_domain(sm6, operands[0]);
-    sm6_parser_validate_control_point_count(sm6, operands[1]);
+    sm6_parser_validate_control_point_count(sm6, operands[1], "Domain shader input");
     sm6->p.program.input_control_point_count = operands[1];
 
     return operands[0];
 }
 
+static enum vkd3d_tessellator_domain sm6_parser_hs_properties_init(struct sm6_parser *sm6,
+        const struct sm6_metadata_value *m)
+{
+    const struct sm6_metadata_node *node;
+    unsigned int operands[6] = {0};
+    unsigned int i;
+
+    if (!m || !sm6_metadata_value_is_node(m))
+    {
+        WARN("Missing or invalid HS properties.\n");
+        vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_PROPERTIES,
+                "Hull shader properties node is missing or invalid.");
+        return 0;
+    }
+
+    node = m->u.node;
+    if (node->operand_count < 7)
+    {
+        WARN("Invalid operand count %u.\n", node->operand_count);
+        vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND_COUNT,
+                "Hull shader properties operand count %u is invalid.", node->operand_count);
+        return 0;
+    }
+    if (node->operand_count > 7)
+    {
+        WARN("Ignoring %u extra operands.\n", node->operand_count - 7);
+        vkd3d_shader_parser_warning(&sm6->p, VKD3D_SHADER_WARNING_DXIL_IGNORING_OPERANDS,
+                "Ignoring %u extra operands for hull shader properties.", node->operand_count - 7);
+    }
+
+    m = node->operands[0];
+    if (!sm6_metadata_value_is_value(m) || !sm6_value_is_function_dcl(m->u.value))
+    {
+        WARN("Patch constant function node is not a function value.\n");
+        vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_PROPERTIES,
+                "Hull shader patch constant function node is not a function value.");
+    }
+    else
+    {
+        sm6->patch_constant_function = m->u.value->u.function.name;
+    }
+
+    for (i = 1; i < min(node->operand_count, ARRAY_SIZE(operands)); ++i)
+    {
+        if (!sm6_metadata_get_uint_value(sm6, node->operands[i], &operands[i]))
+        {
+            WARN("HS property at index %u is not a uint value.\n", i);
+            vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_PROPERTIES,
+                    "Hull shader properties operand at index %u is not an integer.", i);
+        }
+    }
+
+    sm6_parser_validate_control_point_count(sm6, operands[1], "Hull shader input");
+    sm6->p.program.input_control_point_count = operands[1];
+    sm6_parser_validate_control_point_count(sm6, operands[2], "Hull shader output");
+    sm6_parser_emit_dcl_count(sm6, VKD3DSIH_DCL_OUTPUT_CONTROL_POINT_COUNT, operands[2]);
+    sm6->p.program.output_control_point_count = operands[2];
+    sm6_parser_emit_dcl_tessellator_domain(sm6, operands[3]);
+    sm6_parser_emit_dcl_tessellator_partitioning(sm6, operands[4]);
+    sm6_parser_emit_dcl_tessellator_output_primitive(sm6, operands[5]);
+    sm6_parser_emit_dcl_max_tessellation_factor(sm6, node->operands[6]);
+
+    return operands[3];
+}
+
 static enum vkd3d_result sm6_parser_entry_point_init(struct sm6_parser *sm6)
 {
     const struct sm6_metadata_value *m = sm6_parser_find_named_metadata(sm6, "dx.entryPoints");
@@ -8916,6 +9084,9 @@ static enum vkd3d_result sm6_parser_entry_point_init(struct sm6_parser *sm6)
                 case SHADER_PROPERTIES_DOMAIN:
                     tessellator_domain = sm6_parser_ds_properties_init(sm6, node->operands[i + 1]);
                     break;
+                case SHADER_PROPERTIES_HULL:
+                    tessellator_domain = sm6_parser_hs_properties_init(sm6, node->operands[i + 1]);
+                    break;
                case SHADER_PROPERTIES_COMPUTE:
                     if ((ret = sm6_parser_emit_thread_group(sm6, node->operands[i + 1])) < 0)
                         return ret;
diff --git a/libs/vkd3d-shader/spirv.c b/libs/vkd3d-shader/spirv.c
index 61390b09c0..217b4d73b7 100644
--- a/libs/vkd3d-shader/spirv.c
+++ b/libs/vkd3d-shader/spirv.c
@@ -223,11 +223,6 @@ enum vkd3d_shader_input_sysval_semantic vkd3d_siv_from_sysval_indexed(enum vkd3d
     }
 }
 
-static bool data_type_is_floating_point(enum vkd3d_data_type data_type)
-{
-    return data_type == VKD3D_DATA_HALF || data_type == VKD3D_DATA_FLOAT || data_type == VKD3D_DATA_DOUBLE;
-}
-
 #define VKD3D_SPIRV_VERSION 0x00010000
 #define VKD3D_SPIRV_GENERATOR_ID 18
 #define VKD3D_SPIRV_GENERATOR_VERSION 11
diff --git a/libs/vkd3d-shader/vkd3d_shader_private.h b/libs/vkd3d-shader/vkd3d_shader_private.h
index ac8aebe0f4..ee063cab00 100644
--- a/libs/vkd3d-shader/vkd3d_shader_private.h
+++ b/libs/vkd3d-shader/vkd3d_shader_private.h
@@ -641,6 +641,11 @@ static inline bool data_type_is_bool(enum vkd3d_data_type data_type)
     return data_type == VKD3D_DATA_BOOL;
 }
 
+static inline bool data_type_is_floating_point(enum vkd3d_data_type data_type)
+{
+    return data_type == VKD3D_DATA_HALF || data_type == VKD3D_DATA_FLOAT || data_type == VKD3D_DATA_DOUBLE;
+}
+
 static inline bool data_type_is_64_bit(enum vkd3d_data_type data_type)
 {
     return data_type == VKD3D_DATA_DOUBLE || data_type == VKD3D_DATA_UINT64;
-- 
GitLab


From e1abf1e48ad2dbd46116d1befe665bac23c01fd7 Mon Sep 17 00:00:00 2001
From: Conor McCarthy <cmccarthy@codeweavers.com>
Date: Thu, 15 Jun 2023 12:26:14 +1000
Subject: [PATCH 4/5] vkd3d-shader/dxil: Implement DX intrinsic PrimitiveID.

---
 libs/vkd3d-shader/dxil.c | 24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)

diff --git a/libs/vkd3d-shader/dxil.c b/libs/vkd3d-shader/dxil.c
index 40ba267156..c03e20a744 100644
--- a/libs/vkd3d-shader/dxil.c
+++ b/libs/vkd3d-shader/dxil.c
@@ -416,6 +416,7 @@ enum dx_intrinsic_opcode
     DX_FLATTENED_THREAD_ID_IN_GROUP =  96,
     DX_MAKE_DOUBLE                  = 101,
     DX_SPLIT_DOUBLE                 = 102,
+    DX_PRIMITIVE_ID                 = 108,
     DX_LEGACY_F32TOF16              = 130,
     DX_LEGACY_F16TOF32              = 131,
     DX_RAW_BUFFER_LOAD              = 139,
@@ -4585,6 +4586,22 @@ static void sm6_parser_dcl_register_builtin(struct sm6_parser *sm6,
     }
 }
 
+static void sm6_parser_emit_dx_input_register_mov(struct sm6_parser *sm6,
+        struct vkd3d_shader_instruction *ins, enum vkd3d_shader_register_type reg_type, enum vkd3d_data_type data_type)
+{
+    struct vkd3d_shader_src_param *src_param;
+
+    vsir_instruction_init(ins, &sm6->p.location, VKD3DSIH_MOV);
+
+    if (!(src_param = instruction_src_params_alloc(ins, 1, sm6)))
+        return;
+    sm6_parser_dcl_register_builtin(sm6, reg_type, data_type, 1);
+    vsir_register_init(&src_param->reg, reg_type, data_type, 0);
+    src_param_init(src_param);
+
+    instruction_dst_param_init_ssa_scalar(ins, sm6);
+}
+
 static const struct sm6_descriptor_info *sm6_parser_get_descriptor(struct sm6_parser *sm6,
         enum vkd3d_shader_descriptor_type type, unsigned int id, const struct sm6_value *address)
 {
@@ -4946,6 +4963,12 @@ static void sm6_parser_emit_dx_make_double(struct sm6_parser *sm6, enum dx_intri
     instruction_dst_param_init_ssa_scalar(ins, sm6);
 }
 
+static void sm6_parser_emit_dx_primitive_id(struct sm6_parser *sm6, enum dx_intrinsic_opcode op,
+        const struct sm6_value **operands, struct function_emission_state *state)
+{
+    sm6_parser_emit_dx_input_register_mov(sm6, state->ins, VKD3DSPR_PRIMID, VKD3D_DATA_UINT);
+}
+
 static void sm6_parser_emit_dx_raw_buffer_load(struct sm6_parser *sm6, enum dx_intrinsic_opcode op,
         const struct sm6_value **operands, struct function_emission_state *state)
 {
@@ -5608,6 +5631,7 @@ static const struct sm6_dx_opcode_info sm6_dx_op_table[] =
     [DX_LOAD_INPUT                    ] = {"o", "ii8i", sm6_parser_emit_dx_load_input},
     [DX_LOG                           ] = {"g", "R",    sm6_parser_emit_dx_unary},
     [DX_MAKE_DOUBLE                   ] = {"d", "ii",   sm6_parser_emit_dx_make_double},
+    [DX_PRIMITIVE_ID                  ] = {"i", "",     sm6_parser_emit_dx_primitive_id},
     [DX_RAW_BUFFER_LOAD               ] = {"o", "Hii8i", sm6_parser_emit_dx_raw_buffer_load},
     [DX_RAW_BUFFER_STORE              ] = {"v", "Hiioooocc", sm6_parser_emit_dx_raw_buffer_store},
     [DX_ROUND_NE                      ] = {"g", "R",    sm6_parser_emit_dx_unary},
-- 
GitLab


From 0515482e82b3885692d0f401c765463a5d90dd6b Mon Sep 17 00:00:00 2001
From: Conor McCarthy <cmccarthy@codeweavers.com>
Date: Fri, 5 Apr 2024 13:54:25 +1000
Subject: [PATCH 5/5] vkd3d-shader/ir: Validate tessellation declarations.

---
 libs/vkd3d-shader/ir.c                   | 40 ++++++++++++++++++++++++
 libs/vkd3d-shader/vkd3d_shader_private.h |  1 +
 2 files changed, 41 insertions(+)

diff --git a/libs/vkd3d-shader/ir.c b/libs/vkd3d-shader/ir.c
index 610d907d98..d972e8e107 100644
--- a/libs/vkd3d-shader/ir.c
+++ b/libs/vkd3d-shader/ir.c
@@ -5433,6 +5433,46 @@ static void vsir_validate_instruction(struct validation_context *ctx)
             ctx->dcl_temps_found = false;
             return;
 
+        case VKD3DSIH_DCL_HS_MAX_TESSFACTOR:
+            /* Exclude non-finite values. */
+            if (!(instruction->declaration.max_tessellation_factor >= 1.0f
+                    && instruction->declaration.max_tessellation_factor <= 64.0f))
+                validator_error(ctx, VKD3D_SHADER_ERROR_VSIR_INVALID_TESSELLATION, "Max tessellation factor %f is invalid.",
+                        instruction->declaration.max_tessellation_factor);
+            return;
+
+        /* The DXIL parser can generate these outside phases, but this is not an issue. */
+        case VKD3DSIH_DCL_INPUT:
+        case VKD3DSIH_DCL_OUTPUT:
+            return;
+
+        case VKD3DSIH_DCL_OUTPUT_CONTROL_POINT_COUNT:
+            if (!instruction->declaration.count || instruction->declaration.count > 32)
+                validator_error(ctx, VKD3D_SHADER_ERROR_VSIR_INVALID_TESSELLATION, "Output control point count %u is invalid.",
+                        instruction->declaration.count);
+            return;
+
+        case VKD3DSIH_DCL_TESSELLATOR_DOMAIN:
+            if (instruction->declaration.tessellator_domain == VKD3D_TESSELLATOR_DOMAIN_INVALID
+                    || instruction->declaration.tessellator_domain >= VKD3D_TESSELLATOR_DOMAIN_COUNT)
+                validator_error(ctx, VKD3D_SHADER_ERROR_VSIR_INVALID_TESSELLATION,
+                        "Tessellator domain %#x is invalid.", instruction->declaration.tessellator_domain);
+            return;
+
+        case VKD3DSIH_DCL_TESSELLATOR_OUTPUT_PRIMITIVE:
+            if (!instruction->declaration.tessellator_output_primitive
+                    || instruction->declaration.tessellator_output_primitive > VKD3D_SHADER_TESSELLATOR_OUTPUT_TRIANGLE_CCW)
+                validator_error(ctx, VKD3D_SHADER_ERROR_VSIR_INVALID_TESSELLATION,
+                        "Tessellator output primitive %#x is invalid.", instruction->declaration.tessellator_output_primitive);
+            return;
+
+        case VKD3DSIH_DCL_TESSELLATOR_PARTITIONING:
+            if (!instruction->declaration.tessellator_partitioning
+                    || instruction->declaration.tessellator_partitioning > VKD3D_SHADER_TESSELLATOR_PARTITIONING_FRACTIONAL_EVEN)
+                validator_error(ctx, VKD3D_SHADER_ERROR_VSIR_INVALID_TESSELLATION,
+                        "Tessellator partitioning %#x is invalid.", instruction->declaration.tessellator_partitioning);
+            return;
+
         default:
             break;
     }
diff --git a/libs/vkd3d-shader/vkd3d_shader_private.h b/libs/vkd3d-shader/vkd3d_shader_private.h
index ee063cab00..468de3cb3c 100644
--- a/libs/vkd3d-shader/vkd3d_shader_private.h
+++ b/libs/vkd3d-shader/vkd3d_shader_private.h
@@ -220,6 +220,7 @@ enum vkd3d_shader_error
     VKD3D_SHADER_ERROR_VSIR_INVALID_INDEX               = 9015,
     VKD3D_SHADER_ERROR_VSIR_INVALID_CONTROL_FLOW        = 9016,
     VKD3D_SHADER_ERROR_VSIR_INVALID_SSA_USAGE           = 9017,
+    VKD3D_SHADER_ERROR_VSIR_INVALID_TESSELLATION        = 9018,
 
     VKD3D_SHADER_WARNING_VSIR_DYNAMIC_DESCRIPTOR_ARRAY  = 9300,
 };
-- 
GitLab