diff --git a/Makefile.am b/Makefile.am index f823cbc8..2ad556c7 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 00000000..d8b26347 --- /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 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 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 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 f800ded3..359eafb9 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(¶ms, 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(¶ms, 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 2f7e8fb2..dd92048f 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 b26d6a36..bc1c5c5f 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) { - ID3D10Blob_Release(vs_code); + hs_code = compile_shader(runner, runner->r.hs_source, "hs"); + succeeded = succeeded && hs_code; + } + if (runner->r.ds_source) + { + 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 56844ae3..48792fa7 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); - 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"); + succeeded = ps_code && vs_code; - if (!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(succeeded, "Failed to compile shaders.\n"); + + 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 2045950e..221f3564 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 a3bbf170..c903c894 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);