Doesn't support tessellation yet, but adds some requirements.
-- v6: 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.
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..3c348e05a --- /dev/null +++ b/tests/hlsl/tessellation.shader_test @@ -0,0 +1,99 @@ +[require] +shader model >= 5.0 +tessellation + +[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 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 | 125 +++++++++++++++++++++++++++++++++++---- 1 file changed, 112 insertions(+), 13 deletions(-)
diff --git a/libs/vkd3d-shader/dxil.c b/libs/vkd3d-shader/dxil.c index b5a61d99d..a8d77a398 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: + /* 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]; } @@ -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,74 @@ 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_QUAD) + { + 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 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 +8710,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 +8744,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 +8760,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 | 172 ++++++++++++++++++++++- libs/vkd3d-shader/spirv.c | 5 - libs/vkd3d-shader/vkd3d_shader_private.h | 5 + 3 files changed, 175 insertions(+), 7 deletions(-)
diff --git a/libs/vkd3d-shader/dxil.c b/libs/vkd3d-shader/dxil.c index a8d77a398..702dc6bb7 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) { @@ -8620,6 +8665,63 @@ 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; + + if (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 > 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, + "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, const struct sm6_metadata_value *m) { @@ -8667,6 +8769,69 @@ 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); + } + + 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->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"); @@ -8747,6 +8912,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 702dc6bb7..7d299dbd1 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 | 37 ++++++++++++++++++++++++ libs/vkd3d-shader/vkd3d_shader_private.h | 1 + 2 files changed, 38 insertions(+)
diff --git a/libs/vkd3d-shader/ir.c b/libs/vkd3d-shader/ir.c index 8af537390..f89141a5c 100644 --- a/libs/vkd3d-shader/ir.c +++ b/libs/vkd3d-shader/ir.c @@ -5078,6 +5078,43 @@ 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 > 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, };
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:
This feels a little too unwieldy for our current needs, I'd say. I would content of having a `[shaders]` section that behaves just as if a series of `[vertex shader]`, `[pixel shader]`, etc had been there, all using the same code, except that the entry point is `vs_main`, `ps_main`, etc for the functions that exist. That means that later you can replace the shaders individually, even if at that point you have to rewrite the whole code. That seems to me a good strike between having to write less code in the common case and keeping the file format simple and not too verbose.
Giovanni Mascellani (@giomasce) commented about libs/vkd3d-shader/dxil.c:
+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. */
I wouldn't remove the FIXME() here. If anything I'd replace it with something stronger (`vkd3d_unreachable()`), but if for some bad reason we end up here in spite of the validation we did before I'd like to know.
Giovanni Mascellani (@giomasce) commented about libs/vkd3d-shader/dxil.c:
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_QUAD)
Value zero is invalid too, isn't it? Also, I'd be in favor of introducing `VKD3D_TESSELLATOR_DOMAIN_COUNT`, and similarly for the other fields.
Giovanni Mascellani (@giomasce) commented about libs/vkd3d-shader/dxil.c:
"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];
Any value is acceptable here? I'd guess that you cannot have more than 32 or so control points? Can you have zero?
Giovanni Mascellani (@giomasce) commented about libs/vkd3d-shader/dxil.c:
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->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];
Those three are not being validate, are they?
On Wed Apr 10 13:02:56 2024 +0000, Giovanni Mascellani wrote:
I wouldn't remove the FIXME() here. If anything I'd replace it with something stronger (`vkd3d_unreachable()`), but if for some bad reason we end up here in spite of the validation we did before I'd like to know.
The parser is allowed to continue even if values like these are invalid, so it is reachable. This is a common pattern where nothing prevents continued parsing, so we can potentially find other issues before the parser errors out.