From bcc95bfbbc2c7a386fbd4958192412b268a086ec Mon Sep 17 00:00:00 2001 From: Frodo Baggins Date: Sun, 24 Nov 2024 11:46:36 -0800 Subject: [PATCH] test: make local/tcs use attr arrays --- .../spirv/emit_spirv_context_get_set.cpp | 51 +++++++++++-------- .../backend/spirv/spirv_emit_context.cpp | 35 +++++++++---- .../backend/spirv/spirv_emit_context.h | 2 + src/shader_recompiler/info.h | 4 +- src/shader_recompiler/runtime_info.h | 10 ++++ .../renderer_vulkan/vk_graphics_pipeline.cpp | 28 ++++++++-- .../renderer_vulkan/vk_graphics_pipeline.h | 3 ++ .../renderer_vulkan/vk_pipeline_cache.cpp | 16 ++++-- 8 files changed, 109 insertions(+), 40 deletions(-) diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp index 497a62e97..a31a35b4a 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp @@ -47,24 +47,27 @@ Id VsOutputAttrPointer(EmitContext& ctx, VsOutput output) { } } -Id OutputAttrPointer(EmitContext& ctx, IR::Attribute attr, Id array_index, u32 element) { +// TODO refactor +Id SpecialOutputAttrPointer(EmitContext& ctx, IR::Attribute attr, u32 element) { + const u32 attr_index{u32(attr) - u32(IR::Attribute::Param0)}; + if (ctx.stage == Stage::Local && ctx.runtime_info.ls_info.links_with_tcs) { + // TODO refactor all this + const auto component_ptr = ctx.TypePointer(spv::StorageClass::Output, ctx.F32[1]); + return ctx.OpAccessChain(component_ptr, ctx.output_attr_array, ctx.ConstU32(attr_index), + ctx.ConstU32(element)); + } + UNREACHABLE(); +} + +Id OutputAttrPointer(EmitContext& ctx, IR::Attribute attr, u32 element) { if (IR::IsParam(attr)) { const u32 index{u32(attr) - u32(IR::Attribute::Param0)}; const auto& info{ctx.output_params.at(index)}; ASSERT(info.num_components > 0); - Id base = info.id; - boost::container::small_vector indices; - if (ctx.l_stage == LogicalStage::TessellationControl) { - indices.push_back(array_index); - } - if (info.num_components > 1) { - indices.push_back(ctx.ConstU32(element)); - } - - if (indices.empty()) { + if (info.num_components == 1) { return info.id; } else { - return ctx.OpAccessChain(info.pointer_type, info.id, indices); + return ctx.OpAccessChain(info.pointer_type, info.id, ctx.ConstU32(element)); } } if (IR::IsMrt(attr)) { @@ -93,10 +96,6 @@ Id OutputAttrPointer(EmitContext& ctx, IR::Attribute attr, Id array_index, u32 e } } -Id OutputAttrPointer(EmitContext& ctx, IR::Attribute attr, u32 element) { - return OutputAttrPointer(ctx, attr, {}, element); -} - std::pair OutputAttrComponentType(EmitContext& ctx, IR::Attribute attr) { if (IR::IsParam(attr)) { const u32 index{u32(attr) - u32(IR::Attribute::Param0)}; @@ -233,6 +232,15 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, u32 comp, Id index) { UNREACHABLE(); } + if (ctx.info.l_stage == LogicalStage::TessellationControl) { + ASSERT(IR::IsParam(attr)); + const u32 attr_index{u32(attr) - u32(IR::Attribute::Param0)}; + const auto attr_comp_ptr = ctx.TypePointer(spv::StorageClass::Input, ctx.F32[1]); + return ctx.OpLoad(ctx.F32[1], + ctx.OpAccessChain(attr_comp_ptr, ctx.input_attr_array, index, + ctx.ConstU32(attr_index), ctx.ConstU32(comp))); + } + if (IR::IsParam(attr)) { const u32 index{u32(attr) - u32(IR::Attribute::Param0)}; const auto& param{ctx.input_params.at(index)}; @@ -333,12 +341,13 @@ void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, Id value, u32 elemen return; } - Id pointer; - if (ctx.l_stage == LogicalStage::TessellationControl) { - pointer = OutputAttrPointer(ctx, attr, ctx.OpLoad(ctx.U32[1], ctx.invocation_id), element); - } else { - pointer = OutputAttrPointer(ctx, attr, element); + // TODO refactor + if (ctx.stage == Stage::Local && ctx.runtime_info.ls_info.links_with_tcs) { + ctx.OpStore(SpecialOutputAttrPointer(ctx, attr, element), value); + return; } + + const Id pointer{OutputAttrPointer(ctx, attr, element)}; const auto component_type{OutputAttrComponentType(ctx, attr)}; if (component_type.second) { ctx.OpStore(pointer, ctx.OpBitcast(component_type.first, value)); diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index 06e42cae8..3d7f611a4 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp @@ -6,6 +6,7 @@ #include "shader_recompiler/backend/spirv/spirv_emit_context.h" #include "shader_recompiler/frontend/fetch_shader.h" #include "shader_recompiler/ir/passes/srt.h" +#include "shader_recompiler/runtime_info.h" #include "video_core/amdgpu/types.h" #include @@ -393,6 +394,7 @@ void EmitContext::DefineInputs() { DefineVariable(U32[1], spv::BuiltIn::PatchVertices, spv::StorageClass::Input); primitive_id = DefineVariable(U32[1], spv::BuiltIn::PrimitiveId, spv::StorageClass::Input); +#if 0 for (u32 i = 0; i < IR::NumParams; i++) { const IR::Attribute param{IR::Attribute::Param0 + i}; if (!info.loads.GetAny(param)) { @@ -405,6 +407,14 @@ void EmitContext::DefineInputs() { Name(id, fmt::format("in_attr{}", i)); input_params[i] = {id, input_f32, F32[1], 4}; } +#else + const u32 num_attrs = runtime_info.hs_info.ls_stride >> 4; + const Id per_vertex_type{TypeArray(F32[4], ConstU32(num_attrs))}; + // The input vertex count isn't statically known, so make length 32 (what glslang does) + const Id patch_array_type{TypeArray(per_vertex_type, ConstU32(32u))}; + input_attr_array = DefineInput(patch_array_type, 0); + Name(input_attr_array, "in_attrs"); +#endif break; } case LogicalStage::TessellationEval: { @@ -458,16 +468,23 @@ void EmitContext::DefineOutputs() { cull_distances = DefineVariable(type, spv::BuiltIn::CullDistance, spv::StorageClass::Output); } - for (u32 i = 0; i < IR::NumParams; i++) { - const IR::Attribute param{IR::Attribute::Param0 + i}; - if (!info.stores.GetAny(param)) { - continue; + if (stage == Shader::Stage::Local && runtime_info.ls_info.links_with_tcs) { + const u32 num_attrs = runtime_info.ls_info.ls_stride >> 4; + const Id type{TypeArray(F32[4], ConstU32(num_attrs))}; + output_attr_array = DefineOutput(type, 0); + Name(output_attr_array, "out_attrs"); + } else { + for (u32 i = 0; i < IR::NumParams; i++) { + const IR::Attribute param{IR::Attribute::Param0 + i}; + if (!info.stores.GetAny(param)) { + continue; + } + const u32 num_components = info.stores.NumComponents(param); + const Id id{DefineOutput(F32[num_components], i)}; + Name(id, fmt::format("out_attr{}", i)); + output_params[i] = + GetAttributeInfo(AmdGpu::NumberFormat::Float, id, num_components, true); } - const u32 num_components = info.stores.NumComponents(param); - const Id id{DefineOutput(F32[num_components], i)}; - Name(id, fmt::format("out_attr{}", i)); - output_params[i] = - GetAttributeInfo(AmdGpu::NumberFormat::Float, id, num_components, true); } break; } diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.h b/src/shader_recompiler/backend/spirv/spirv_emit_context.h index 497aa1d0f..583d96b99 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.h +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.h @@ -264,6 +264,8 @@ public: bool is_loaded{}; s32 buffer_handle{-1}; }; + Id input_attr_array; + Id output_attr_array; std::array input_params{}; std::array output_params{}; std::array frag_outputs{}; diff --git a/src/shader_recompiler/info.h b/src/shader_recompiler/info.h index 814af10c3..dc332fa72 100644 --- a/src/shader_recompiler/info.h +++ b/src/shader_recompiler/info.h @@ -255,11 +255,11 @@ struct Info { } // TODO probably not needed - bool FoundTessConstantsSharp() { + bool FoundTessConstantsSharp() const { return tess_consts_dword_offset >= 0; } - void ReadTessConstantBuffer(TessellationDataConstantBuffer& tess_constants) { + void ReadTessConstantBuffer(TessellationDataConstantBuffer& tess_constants) const { ASSERT(FoundTessConstantsSharp()); auto buf = ReadUdReg(static_cast(tess_consts_ptr_base), static_cast(tess_consts_dword_offset)); diff --git a/src/shader_recompiler/runtime_info.h b/src/shader_recompiler/runtime_info.h index 290528dae..228ddc60a 100644 --- a/src/shader_recompiler/runtime_info.h +++ b/src/shader_recompiler/runtime_info.h @@ -38,6 +38,13 @@ enum class LogicalStage : u32 { return static_cast(index); } +struct LocalRuntimeInfo { + u32 ls_stride; + bool links_with_tcs; + + auto operator<=>(const LocalRuntimeInfo&) const noexcept = default; +}; + struct ExportRuntimeInfo { u32 vertex_data_size; @@ -208,6 +215,7 @@ struct RuntimeInfo { AmdGpu::FpDenormMode fp_denorm_mode32; AmdGpu::FpRoundMode fp_round_mode32; union { + LocalRuntimeInfo ls_info; ExportRuntimeInfo es_info; VertexRuntimeInfo vs_info; HullRuntimeInfo hs_info; @@ -235,6 +243,8 @@ struct RuntimeInfo { return gs_info == other.gs_info; case Stage::Hull: return hs_info == other.hs_info; + case Stage::Local: + return ls_info == other.ls_info; default: return true; } diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index 144fec934..43b1afb67 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -346,15 +346,33 @@ GraphicsPipeline::GraphicsPipeline(const Instance& instance_, Scheduler& schedul GraphicsPipeline::~GraphicsPipeline() = default; +boost::container::small_vector +GraphicsPipeline::GetCompilationOrder() const { + bool uses_tessellation = stages[static_cast(Shader::LogicalStage::TessellationControl)]; + + boost::container::small_vector ordered_stages; + for (auto s = 0; s < MaxShaderStages; s++) { + if (uses_tessellation && + static_cast(s) == Shader::LogicalStage::Vertex) { + continue; + } + if (stages[s]) { + ordered_stages.push_back(stages[s]); + } + } + if (uses_tessellation) { + ordered_stages.push_back(stages[static_cast(Shader::LogicalStage::Vertex)]); + } + // TODO handle geometry + tess + return ordered_stages; +} + void GraphicsPipeline::BuildDescSetLayout() { boost::container::small_vector bindings; u32 binding{}; - for (const auto* stage : stages) { - if (!stage) { - continue; - } - + for (const auto* stage : GetCompilationOrder()) { + ASSERT(stage); if (stage->has_readconst) { bindings.push_back({ .binding = binding++, diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h index 444c8517e..e8d3b5cd0 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h @@ -101,6 +101,9 @@ public: private: void BuildDescSetLayout(); + // Temp, find better way + boost::container::small_vector GetCompilationOrder() + const; private: GraphicsPipelineKey key; diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 1ca460b42..ea20db7dc 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -9,6 +9,7 @@ #include "common/path_util.h" #include "core/debug_state.h" #include "shader_recompiler/backend/spirv/emit_spirv.h" +#include "shader_recompiler/frontend/tessellation.h" #include "shader_recompiler/info.h" #include "shader_recompiler/recompiler.h" #include "shader_recompiler/runtime_info.h" @@ -93,6 +94,15 @@ Shader::RuntimeInfo PipelineCache::BuildRuntimeInfo(Stage stage, LogicalStage l_ switch (stage) { case Stage::Local: { BuildCommon(regs.ls_program); + if (regs.stage_enable.IsStageEnabled(static_cast(Stage::Hull))) { + info.ls_info.links_with_tcs = true; + Shader::TessellationDataConstantBuffer tess_constants; + const auto* pgm = regs.ProgramForStage(static_cast(Stage::Hull)); + const auto params = Liverpool::GetParams(*pgm); + const auto& hull_info = program_cache.at(params.hash)->info; + hull_info.ReadTessConstantBuffer(tess_constants); + info.ls_info.ls_stride = tess_constants.m_lsStride; + } break; } case Stage::Hull: { @@ -396,15 +406,15 @@ bool PipelineCache::RefreshGraphicsKey() { if (!instance.IsTessellationSupported()) { break; } - if (!TryBindStage(Stage::Local, LogicalStage::Vertex)) { - return false; - } if (!TryBindStage(Stage::Hull, LogicalStage::TessellationControl)) { return false; } if (!TryBindStage(Stage::Vertex, LogicalStage::TessellationEval)) { return false; } + if (!TryBindStage(Stage::Local, LogicalStage::Vertex)) { + return false; + } break; } default: {