Doesn't support tessellation yet, but adds some requirements.
-- v4: vkd3d-shader/ir: Validate tessellation declarations. vkd3d-shader/dxil: Implement DX intrinsic PrimitiveID. vkd3d-shader/dxil: Load hull shader properties. vkd3d-shader/dxil: Load domain shader properties. tests/hlsl: Add a tessellation test.
From: Conor McCarthy cmccarthy@codeweavers.com
--- Makefile.am | 1 + tests/hlsl/tessellation.shader_test | 99 +++++++++++++++++++++++++++++ tests/shader_runner.c | 73 +++++++++++++++++++++ tests/shader_runner.h | 6 ++ tests/shader_runner_d3d11.c | 69 ++++++++++++++++++-- tests/shader_runner_d3d12.c | 45 +++++++++++-- tests/shader_runner_vulkan.c | 31 +++++++-- 7 files changed, 310 insertions(+), 14 deletions(-) create mode 100644 tests/hlsl/tessellation.shader_test
diff --git a/Makefile.am b/Makefile.am index 68e8642e0..9380af450 100644 --- a/Makefile.am +++ b/Makefile.am @@ -197,6 +197,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 000000000..169d5d9ce --- /dev/null +++ b/tests/hlsl/tessellation.shader_test @@ -0,0 +1,99 @@ +[require] +shader model >= 5.0 +tessellation + +[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; +} + +[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; +} + +[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 5eafbcbe4..c3aa05395 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, };
@@ -320,6 +324,10 @@ static void parse_require_directive(struct shader_runner *runner, const char *li runner->compile_options |= options[i].option; } } + else if (match_string(line, "tessellation", &line)) + { + runner->require_tessellation = true; + } else if (match_string(line, "float64", &line)) { runner->require_float64 = true; @@ -887,6 +895,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);
@@ -1204,6 +1220,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)); @@ -1265,6 +1283,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[] = { @@ -1392,6 +1412,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; } @@ -1435,6 +1459,8 @@ static bool check_requirements(const struct shader_runner *runner, const struct { if (runner->maximum_shader_model < runner->minimum_shader_model) return false; + if (runner->require_tessellation && !caps->tessellation) + return false; if (runner->require_float64 && !caps->float64) return false; if (runner->require_int64 && !caps->int64) @@ -1612,6 +1638,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; @@ -1690,6 +1746,7 @@ void run_shader_tests(struct shader_runner *runner, const struct shader_runner_c state = STATE_REQUIRE; runner->minimum_shader_model = caps->minimum_shader_model; runner->maximum_shader_model = caps->maximum_shader_model; + runner->require_tessellation = false; runner->require_float64 = false; runner->require_int64 = false; runner->require_rov = false; @@ -1794,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; @@ -1825,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 2f7e8fb25..8497c0961 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, };
@@ -129,6 +131,7 @@ struct shader_runner_caps size_t tag_count; enum shader_model minimum_shader_model; enum shader_model maximum_shader_model; + bool tessellation; bool float64; bool int64; bool rov; @@ -157,8 +160,11 @@ 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_tessellation; /* For the GL runner, until HLSL compiler support is added. */ bool require_float64; bool require_int64; bool require_rov; diff --git a/tests/shader_runner_d3d11.c b/tests/shader_runner_d3d11.c index 1285ca41f..5aac59943 100644 --- a/tests/shader_runner_d3d11.c +++ b/tests/shader_runner_d3d11.c @@ -273,6 +273,7 @@ static BOOL init_test_context(struct d3d11_shader_runner *runner) runner->caps.runner = "d3d11.dll"; runner->caps.minimum_shader_model = SHADER_MODEL_4_0; runner->caps.maximum_shader_model = SHADER_MODEL_5_1; + runner->caps.tessellation = true;
hr = ID3D11Device_CheckFeatureSupport(runner->device, D3D11_FEATURE_DOUBLES, &doubles, sizeof(doubles)); @@ -589,27 +590,52 @@ 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; }
+ if (!hs_code != !ds_code) + fatal_error("Have a domain or hull shader but not both.\n"); + hr = ID3D11Device_CreateVertexShader(device, ID3D10Blob_GetBufferPointer(vs_code), ID3D10Blob_GetBufferSize(vs_code), NULL, &vs); ok(hr == S_OK, "Failed to create vertex shader, hr %#lx.\n", hr); @@ -618,12 +644,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 +743,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 9bf7bb248..e82a11c6d 100644 --- a/tests/shader_runner_d3d12.c +++ b/tests/shader_runner_d3d12.c @@ -434,32 +434,53 @@ 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; }
+ if (!hs_code != !ds_code) + fatal_error("Have a domain or hull shader but not both.\n"); + if (test_context->root_signature) ID3D12RootSignature_Release(test_context->root_signature); test_context->root_signature = d3d12_runner_create_root_signature(runner, @@ -481,9 +502,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 +542,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)) @@ -632,6 +668,7 @@ static void d3d12_runner_init_caps(struct d3d12_shader_runner *runner) #endif runner->caps.minimum_shader_model = SHADER_MODEL_4_0; runner->caps.maximum_shader_model = SHADER_MODEL_5_1; + runner->caps.tessellation = true; runner->caps.float64 = options.DoublePrecisionFloatShaderOps; runner->caps.int64 = options1.Int64ShaderOps; runner->caps.rov = options.ROVsSupported; diff --git a/tests/shader_runner_vulkan.c b/tests/shader_runner_vulkan.c index a3bbf170c..353998bb5 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,20 @@ 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 != !runner->r.ds_source) + fatal_error("Have a domain or hull shader but not both.\n"); + + 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 +801,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);
@@ -1495,6 +1515,7 @@ static bool init_vulkan_runner(struct vulkan_shader_runner *runner) }
runner->caps.runner = "Vulkan"; + runner->caps.tessellation = true; get_physical_device_info(runner, &device_info); ret_features = &device_info.features2.features;
From: Conor McCarthy cmccarthy@codeweavers.com
--- libs/vkd3d-shader/dxil.c | 118 ++++++++++++++++++++++++++++++++++----- 1 file changed, 105 insertions(+), 13 deletions(-)
diff --git a/libs/vkd3d-shader/dxil.c b/libs/vkd3d-shader/dxil.c index b5a61d99d..4bcb7af10 100644 --- a/libs/vkd3d-shader/dxil.c +++ b/libs/vkd3d-shader/dxil.c @@ -7527,9 +7527,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: + FIXME("Unhandled domain %u.\n", domain); + 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: + FIXME("Unhandled domain %u.\n", domain); + return VKD3D_SHADER_SV_TESS_FACTOR_TRIEDGE; + } + } + else if (kind < ARRAY_SIZE(sysval_semantic_table)) { return sysval_semantic_table[kind]; } @@ -8285,7 +8316,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; @@ -8378,7 +8409,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); @@ -8461,7 +8492,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;
@@ -8474,12 +8506,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; } @@ -8572,10 +8604,67 @@ 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; + + ins = sm6_parser_add_instruction(sm6, VKD3DSIH_DCL_TESSELLATOR_DOMAIN); + ins->declaration.tessellator_domain = tessellator_domain; +} + +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->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; @@ -8614,12 +8703,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)) @@ -8654,6 +8737,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; @@ -8667,6 +8753,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; }
From: Conor McCarthy cmccarthy@codeweavers.com
--- libs/vkd3d-shader/dxil.c | 147 ++++++++++++++++++++++- libs/vkd3d-shader/spirv.c | 5 - libs/vkd3d-shader/vkd3d_shader_private.h | 5 + 3 files changed, 150 insertions(+), 7 deletions(-)
diff --git a/libs/vkd3d-shader/dxil.c b/libs/vkd3d-shader/dxil.c index 4bcb7af10..28dd19024 100644 --- a/libs/vkd3d-shader/dxil.c +++ b/libs/vkd3d-shader/dxil.c @@ -790,6 +790,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; @@ -2789,7 +2790,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 { @@ -2813,6 +2814,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) { @@ -2981,7 +2999,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 @@ -6570,6 +6588,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) { @@ -8604,6 +8641,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) { @@ -8613,6 +8658,40 @@ static void sm6_parser_emit_dcl_tessellator_domain(struct sm6_parser *sm6, ins->declaration.tessellator_domain = tessellator_domain; }
+static void sm6_parser_emit_dcl_tessellator_partitioning(struct sm6_parser *sm6, + enum vkd3d_shader_tessellator_partitioning tessellator_partitioning) +{ + struct vkd3d_shader_instruction *ins; + + 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; + + 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("HS 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; + } + 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, const struct sm6_metadata_value *m) { @@ -8660,6 +8739,67 @@ static enum vkd3d_tessellator_domain sm6_parser_ds_properties_init(struct sm6_pa 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); + } + if (!sm6_metadata_value_is_value(node->operands[0])) + { + WARN("Patch constant function node is not a value.\n"); + vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_PROPERTIES, + "Hull shader patch constant function node is not a value."); + } + else + { + sm6->patch_constant_function = node->operands[0]->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->p.program.input_control_point_count = operands[1]; + 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"); @@ -8740,6 +8880,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 46130244c..62f17be51 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 b07a7bff7..302ae5312 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;
From: Conor McCarthy cmccarthy@codeweavers.com
--- 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 28dd19024..beb33624d 100644 --- a/libs/vkd3d-shader/dxil.c +++ b/libs/vkd3d-shader/dxil.c @@ -415,6 +415,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, @@ -4428,6 +4429,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) { @@ -4789,6 +4806,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) { @@ -5450,6 +5473,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},
From: Conor McCarthy cmccarthy@codeweavers.com
--- libs/vkd3d-shader/ir.c | 29 ++++++++++++++++++++++++ libs/vkd3d-shader/vkd3d_shader_private.h | 1 + 2 files changed, 30 insertions(+)
diff --git a/libs/vkd3d-shader/ir.c b/libs/vkd3d-shader/ir.c index 8af537390..19e16c627 100644 --- a/libs/vkd3d-shader/ir.c +++ b/libs/vkd3d-shader/ir.c @@ -5077,6 +5077,35 @@ static void vsir_validate_instruction(struct validation_context *ctx) ctx->phase = instruction->handler_idx; ctx->dcl_temps_found = false; return; + case VKD3DSIH_DCL_HS_MAX_TESSFACTOR: + 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 > 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_QUAD) + 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 > 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 > 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 302ae5312..b9ac46b27 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, };
That doesn't need to happen now, but once we start putting longer pipelines in the shader runner tests it might make sense to have a single `[shader]` section and compile the various shaders using different function names.
On Mon Apr 8 15:27:52 2024 +0000, Giovanni Mascellani wrote:
That doesn't need to happen now, but once we start putting longer pipelines in the shader runner tests it might make sense to have a single `[shader]` section and compile the various shaders using different function names.
Yes, for tessellation we should probably put the HS and DS in the same section, at least. That's what we usually do in d3d12 tests.
It may also help to use XFB instead of drawing through a PS, also? No idea about that one.
Giovanni Mascellani (@giomasce) commented about tests/hlsl/tessellation.shader_test:
- [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;
+}
+[vertex shader]
Minor, but can we order the shaders in the "logical" order (VS, HS, DS, PS)?
Giovanni Mascellani (@giomasce) commented about libs/vkd3d-shader/dxil.c:
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:
FIXME("Unhandled domain %u.\n", domain);
return VKD3D_SHADER_SV_TESS_FACTOR_TRIEDGE;
I know there is no agreement here, but my preference still is for validating the domain when parsing and then using `vkd3d_unreachable()` here.
Giovanni Mascellani (@giomasce) commented about libs/vkd3d-shader/dxil.c:
- }
- 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);
- }
- if (!sm6_metadata_value_is_value(node->operands[0]))
- {
WARN("Patch constant function node is not a value.\n");
vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_PROPERTIES,
"Hull shader patch constant function node is not a value.");
- }
- else
- {
sm6->patch_constant_function = node->operands[0]->u.value->u.function.name;
Shouldn't you check that that value is indeed a function and you can access its name?
On Mon Apr 8 18:29:49 2024 +0000, Zebediah Figura wrote:
Yes, for tessellation we should probably put the HS and DS in the same section, at least. That's what we usually do in d3d12 tests. It may also help to use XFB instead of drawing through a PS, also? No idea about that one.
An alternative would be to use the preprocessor and add some kind of `[include]` section.
Giovanni Mascellani (@giomasce) commented about libs/vkd3d-shader/ir.c:
ctx->phase = instruction->handler_idx; ctx->dcl_temps_found = false; return;
case VKD3DSIH_DCL_HS_MAX_TESSFACTOR:
if (instruction->declaration.max_tessellation_factor < 1.0f || instruction->declaration.max_tessellation_factor > 64.0f)
We probably want to exclude NaNs too here.
Giovanni Mascellani (@giomasce) commented about libs/vkd3d-shader/ir.c:
ctx->phase = instruction->handler_idx; ctx->dcl_temps_found = false; return;
case VKD3DSIH_DCL_HS_MAX_TESSFACTOR:
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. */
Please leave empty lines between cases.
Giovanni Mascellani (@giomasce) commented about libs/vkd3d-shader/ir.c:
ctx->phase = instruction->handler_idx; ctx->dcl_temps_found = false; return;
case VKD3DSIH_DCL_HS_MAX_TESSFACTOR:
I think all these checks should go (also) in the frontend, not just live inside the validator.
I don't know what's Henri's view on that, but there is probably a philosophical point here that was discussed a little bit when introducing the validator, possibly not enough: the way I see it frontends should do their own validation independently of the VSIR validator in `ir.c` or whatever other check is done during the processing of a shader, and the VSIR validator should just remain a development tool to have an independent check that the shader remains valid during its processing. This is the reason why the validation doesn't happen by default, it requires the explicit usage of `force_validation` (we still expect vkd3d-shader to error out on invalid input shaders even if `force_validation` is not used); incidentally, it is not the reason why my initial idea was to `assert()` on validation feature, but it's not my intention to rehash that discussion.
If we want to rely on the validator to reject bad input shaders for normal usage (not just for development), then the VSIR validator should be run by default (and possibly have an option to disable it for development), but I'm not sure it's a good idea.
On Mon Apr 8 20:27:08 2024 +0000, Giovanni Mascellani wrote:
An alternative would be to use the preprocessor and add some kind of `[include]` section.
I have an alternate version which compiles all from a single `[tessellation shaders]` section. It requires runner changes to handle entry points, and is less flexible because we can't replace the HS or DS on their own. Duplicating structs was simpler so I went with that option.
On Tue Apr 9 02:37:33 2024 +0000, Conor McCarthy wrote:
I have an alternate version which compiles all from a single `[tessellation shaders]` section. It requires runner changes to handle entry points, and is less flexible because we can't replace the HS or DS on their own. Duplicating structs was simpler so I went with that option.
We'll probably want to add some kind of mechanism to specify a source blob once and the reference them multiple times. You could e.g. imagine something like this: ``` [blob 0] ...
[shader hlsl,vs,blob=0,entry=vs_main]
[shader hlsl,ps,blob=0,entry=ps_main]
[test] ... ```
or even this: ``` [blob 0] ...
[test] shader hlsl,vs,blob=0,entry=vs_main shader hlsl,ps,blob=0,entry=ps_main ```
I think all these checks should go (also) in the frontend, not just live inside the validator.
I don't know what's Henri's view on that, but there is probably a philosophical point here that was discussed a little bit when introducing the validator, possibly not enough: the way I see it frontends should do their own validation independently of the VSIR validator in `ir.c` or whatever other check is done during the processing of a shader, and the VSIR validator should just remain a development tool to have an independent check that the shader remains valid during its processing. This is the reason why the validation doesn't happen by default, it requires the explicit usage of `force_validation` (we still expect vkd3d-shader to error out on invalid input shaders even if `force_validation` is not used); incidentally, it is not the reason why my initial idea was to `assert()` on validation feature, but it's not my intention to rehash that discussion.
Yes, I think in principle frontends should reject invalid input, and produce valid vsir. There is a subtlety to consider though: it can be valid/useful/required to assemble/disassemble some kinds of invalid shaders. That implies a certain level of optionality for some of the validation that the frontends do.