diff --git a/src/graphics/shader/recompiler/ShaderRecompiler.cpp b/src/graphics/shader/recompiler/ShaderRecompiler.cpp index 2eb182c50..e565a0da5 100644 --- a/src/graphics/shader/recompiler/ShaderRecompiler.cpp +++ b/src/graphics/shader/recompiler/ShaderRecompiler.cpp @@ -209,20 +209,20 @@ int BufferTableAttribFromOffset(uint32_t raw_offset, int dword) { } Frontend::EmbeddedFetchPlan DetectEmbeddedVertexFetch( - const Decoder::Program& decoded, const ShaderVertexInputInfo* input_info, + const Decoder::Program& decoded, const ShaderVertexInputInfo& input_info, uint32_t user_data_base, uint32_t user_data_count, uint32_t wave_size) { - const uint32_t vertex_index_reg = input_info->logical_stage == ShaderType::Local ? 2u : 5u; - const uint32_t instance_index_reg = input_info->logical_stage == ShaderType::Local ? 5u : 8u; + const uint32_t vertex_index_reg = input_info.logical_stage == ShaderType::Local ? 2u : 5u; + const uint32_t instance_index_reg = input_info.logical_stage == ShaderType::Local ? 5u : 8u; Frontend::EmbeddedFetchPlan data; - data.loads.reserve(input_info->resources_num); + data.loads.reserve(input_info.resources_num); int32_t vertex_offset_candidate = -1; int32_t instance_offset_candidate = -1; bool vertex_offset_conflict = false; bool instance_offset_conflict = false; const int shift_regs = 8; - const int attrib_reg = input_info->fetch_attrib_reg + shift_regs; - const int buffer_reg = input_info->fetch_buffer_reg + shift_regs; + const int attrib_reg = input_info.fetch_attrib_reg + shift_regs; + const int buffer_reg = input_info.fetch_buffer_reg + shift_regs; std::array sgprs {}; std::array vgpr_is_index {}; @@ -488,7 +488,6 @@ void PrepareCallTargets(ShaderSource& source, const CompileOptions& options) { if (source.decoded.instructions[i].opcode != Decoder::Opcode::S_SWAPPC_B64) continue; source.calls.push_back({.instruction = i}); } - EXIT_IF(options.input_info.compute == nullptr); const auto graph = CFG::BuildGraph(source.decoded); Program query; query.stage = options.stage; @@ -809,9 +808,9 @@ TranslateResult TranslateProgram(const Decoder::Program& decoded, const CompileO const auto& cfg = *selected_cfg; Frontend::EmbeddedFetchPlan embedded_fetch; if ((options.stage == ShaderType::Vertex || options.stage == ShaderType::Local) && - options.input_info.vertex != nullptr && options.input_info.vertex->fetch_embedded) { + options.input_info.vertex->fetch_embedded) { embedded_fetch = DetectEmbeddedVertexFetch( - decoded, options.input_info.vertex, options.user_data_base, + decoded, *options.input_info.vertex, options.user_data_base, static_cast(options.user_data.size()), options.wave_size); if (!embedded_fetch.loads.empty()) { LOGF("%s embedded vertex fetch plan: detected=%" PRIu64 "\n", GetDumpLabel(options), diff --git a/src/graphics/shader/recompiler/backend/spirv/SpirvEmitter.cpp b/src/graphics/shader/recompiler/backend/spirv/SpirvEmitter.cpp index 791333ef9..1a201e46c 100644 --- a/src/graphics/shader/recompiler/backend/spirv/SpirvEmitter.cpp +++ b/src/graphics/shader/recompiler/backend/spirv/SpirvEmitter.cpp @@ -233,8 +233,8 @@ std::vector EmitProgram(IR::Program& program, ShaderStageInputInfo inp ? 2u : 1u; DefineModule(state); - ValidateNativeProgram(program, program.stage == ShaderType::Compute && - input_info.compute != nullptr && input_info.compute->lds_storage); + ValidateNativeProgram(program, + program.stage == ShaderType::Compute && input_info.compute->lds_storage); EmitProgram(state); state.builder.AddEntryPoint(ExecutionModelForStage(state.program.stage), state.main_func, "main", state.interface_variables); diff --git a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterFlow.cpp b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterFlow.cpp index a6c413992..74250b80e 100644 --- a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterFlow.cpp +++ b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterFlow.cpp @@ -53,8 +53,7 @@ uint32_t EmitBuiltinU32(EmitterState& state, IR::StageInputKind kind, uint32_t c const auto bits = state.builder.AllocateId(); state.builder.AddFunction(spv::OpLoad, TypeBool(state), value, variable); // SPI_BARYC_CNTL.FRONT_FACE_ALL_BITS selects uint masks or float +/-1. - const bool all_bits = - state.input_info.pixel != nullptr && state.input_info.pixel->ps_front_face_all_bits; + const bool all_bits = state.input_info.pixel->ps_front_face_all_bits; const auto front = ConstantU32(state, all_bits ? 0xffffffffu : 0x3f800000u); const auto back = ConstantU32(state, all_bits ? 0u : 0xbf800000u); state.builder.AddFunction(spv::OpSelect, TypeU32(state), bits, value, front, back); diff --git a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterInternal.h b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterInternal.h index d66d1299a..db8d43aa8 100644 --- a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterInternal.h +++ b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterInternal.h @@ -80,7 +80,7 @@ struct EmitterState { lds_storage_class = spv::StorageClassWorkgroup; } if (program.info.uses_lds && program.stage == ShaderType::Compute && - input_info.compute != nullptr && input_info.compute->lds_storage) { + input_info.compute->lds_storage) { lds_storage_class = spv::StorageClassStorageBuffer; } } diff --git a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterMemoryHelpers.cpp b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterMemoryHelpers.cpp index d9cc9bfbe..eebb8137b 100644 --- a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterMemoryHelpers.cpp +++ b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterMemoryHelpers.cpp @@ -169,10 +169,10 @@ uint32_t LdsDwordCount(const EmitterState& state) { if (const auto* workgroup = ShaderWorkgroupInput(state.program.stage, state.input_info)) { return workgroup->lds_size_dwords; } - if (state.program.stage == ShaderType::Pixel && state.input_info.pixel != nullptr) { + if (state.program.stage == ShaderType::Pixel) { return std::max(state.input_info.pixel->lds_size_dwords, 1u); } - if (state.program.stage == ShaderType::Vertex && state.input_info.vertex != nullptr) { + if (state.program.stage == ShaderType::Vertex) { return std::max(state.input_info.vertex->lds_size_dwords, 1u); } return FUNCTION_LDS_DWORDS; diff --git a/src/graphics/shader/recompiler/frontend/translate/Translate.cpp b/src/graphics/shader/recompiler/frontend/translate/Translate.cpp index 8bbfb7e9f..15a09a10f 100644 --- a/src/graphics/shader/recompiler/frontend/translate/Translate.cpp +++ b/src/graphics/shader/recompiler/frontend/translate/Translate.cpp @@ -987,30 +987,6 @@ void ValidateTranslateOptions(const TranslateOptions& options) { options.stage != ShaderType::Local) { EXIT("embedded fetch requires a vertex or local shader"); } - switch (options.stage) { - case ShaderType::Vertex: - case ShaderType::Local: - case ShaderType::TessellationControl: - case ShaderType::TessellationEvaluation: - case ShaderType::Mesh: - if (options.input_info.vertex == nullptr) { - EXIT("vertex shader translation has no vertex input metadata"); - } - return; - case ShaderType::Pixel: - if (options.input_info.pixel == nullptr) { - EXIT("pixel shader translation has no pixel input metadata"); - } - return; - case ShaderType::Compute: - if (options.input_info.compute == nullptr) { - EXIT("compute shader translation has no compute input metadata"); - } - return; - default: - EXIT("shader translation has unsupported stage %u", - static_cast(options.stage)); - } } } // namespace @@ -1044,7 +1020,9 @@ IR::Program TranslateProgram(const Decoder::Program& decoded, const CFG::Graph& case ShaderType::Compute: result.scratch_dwords = options.input_info.compute->scratch_size_dwords; break; - default: break; // ValidateTranslateOptions rejects unsupported stages. + default: + EXIT("shader translation has unsupported stage %u", + static_cast(options.stage)); } result.dispatcher_fallback = cfg.irreducible || cfg.unsupported; result.cfg_failure_kind = cfg.failure_kind; diff --git a/src/graphics/shader/recompiler/ir/passes/ResourceTracking.cpp b/src/graphics/shader/recompiler/ir/passes/ResourceTracking.cpp index a87a06fdd..c466f0595 100644 --- a/src/graphics/shader/recompiler/ir/passes/ResourceTracking.cpp +++ b/src/graphics/shader/recompiler/ir/passes/ResourceTracking.cpp @@ -362,8 +362,7 @@ class Tracker { public: Tracker(Program& program, const Decoder::Program& decoded, const CFG::Graph& native_cfg, ShaderStageInputInfo input) - : m_program(program), m_decoded(decoded), m_native_cfg(native_cfg), - m_workgroup(program.stage == ShaderType::Compute ? input.compute : nullptr), + : m_program(program), m_decoded(decoded), m_native_cfg(native_cfg), m_input(input), m_scalar_writes(std::move(program.scalar_writes)), m_info(program.info) { std::ranges::sort(m_scalar_writes, {}, &Program::ScalarWrite::pc); m_info.buffers.clear(); @@ -1853,7 +1852,7 @@ private: if (inst->GetOpcode() == ValueOpcode::GetBuiltin && inst->Arg(0) == Value(static_cast(StageInputKind::LocalInvocationId)) && ImmediateU32(inst->Arg(1), axis) && axis < 3u && - m_workgroup->threads_num[axis] <= (uint64_t {1} << bits)) return axis + 1u; + m_input.compute->threads_num[axis] <= (uint64_t {1} << bits)) return axis + 1u; if (!allow_induction || inst->GetOpcode() != ValueOpcode::Phi || (!induction.IsEmpty() && induction != at)) return {}; induction = at; @@ -1864,20 +1863,20 @@ private: bool LinearLocalIndex(const std::array& index) const { uint64_t stride = 1u; for (uint32_t axis = 0; axis < 3u; ++axis) { - if (m_workgroup->threads_num[axis] != 1u && index[axis + 1u] != stride) return false; - stride *= m_workgroup->threads_num[axis]; + if (m_input.compute->threads_num[axis] != 1u && index[axis + 1u] != stride) return false; + stride *= m_input.compute->threads_num[axis]; } return index[0] == 0u; } void ForwardPrivateLds() { - if (m_workgroup == nullptr) return; + if (m_program.stage != ShaderType::Compute) return; uint64_t bytes = 4u; - for (const auto size: m_workgroup->threads_num) { + for (const auto size: m_input.compute->threads_num) { if (size == 0u || size > 1024u) return; bytes *= size; } - if (bytes > uint64_t {m_workgroup->lds_size_dwords} * 4u) return; + if (bytes > uint64_t {m_input.compute->lds_size_dwords} * 4u) return; struct Access { Inst* inst; uint32_t offset; }; std::vector reads, writes; Value address; @@ -1893,7 +1892,7 @@ private: if ((op != ValueOpcode::LoadSharedU32 && op != ValueOpcode::WriteSharedU32) || memory.data_bits != 32u || memory.secondary_offset != 0u || (memory.offset & 3u) != 0u || - uint64_t {memory.offset} + bytes > uint64_t {m_workgroup->lds_size_dwords} * 4u) + uint64_t {memory.offset} + bytes > uint64_t {m_input.compute->lds_size_dwords} * 4u) return; if (address.IsEmpty()) address = inst.Arg(0).Resolve(); else if (!EquivalentValue(m_program, address, inst.Arg(0))) return; @@ -2039,16 +2038,16 @@ private: Value SharedLoadBound(Value value, Value active) { const auto* read = ActiveValue(value, active).TryInstruction(); if (read == nullptr || read->GetOpcode() != ValueOpcode::LoadSharedU32 || - m_workgroup == nullptr || m_program.stage != ShaderType::Compute || + m_program.stage != ShaderType::Compute || read->Arg(1).Resolve() != Value(true)) return {}; uint64_t group_size = 1u; - for (const auto dimension: m_workgroup->threads_num) { - if (dimension == 0u || dimension > m_workgroup->wave_size) return {}; + for (const auto dimension: m_input.compute->threads_num) { + if (dimension == 0u || dimension > m_input.compute->wave_size) return {}; group_size *= dimension; } // Indexed LDS operations within one native wave execute in order. Multiwave // append lists require a separate workgroup synchronization proof. - if (group_size != m_workgroup->wave_size || group_size != m_program.wave_size || + if (group_size != m_input.compute->wave_size || group_size != m_program.wave_size || (group_size != 32u && group_size != 64u)) return {}; const auto memory = [&](const Inst& inst) -> const MemoryInfo* { const auto index = inst.Flags().index; @@ -2101,7 +2100,7 @@ private: if (op != ValueOpcode::SharedAtomicIAdd32 || ActiveValue(inst.Arg(1), guard) != Value(1u) || !constant_address(inst, address) || - uint64_t {address} + 4u > uint64_t {m_workgroup->lds_size_dwords} * 4u || + uint64_t {address} + 4u > uint64_t {m_input.compute->lds_size_dwords} * 4u || address >= 0x10000u || std::ranges::any_of(appends, [&](const Append& at) { return at.counter == address; })) return {}; appends.push_back({.atomic = &inst, .counter = address}); @@ -4059,7 +4058,7 @@ private: Program& m_program; const Decoder::Program& m_decoded; const CFG::Graph& m_native_cfg; - const ShaderWorkgroupInputInfo* m_workgroup; + ShaderStageInputInfo m_input; std::vector m_scalar_writes; std::vector m_resolved_handles; std::vector m_srt_visiting; @@ -4092,8 +4091,8 @@ UniformFillPlan RecognizePatternFill(Program& program, const Decoder::Program& d // Dispatch thread dimensions, valid in both wave sizes. Pattern {.code_words = 38, .hash = 0xacbda1a1ccdd4198ull, .wave_size_mask = 32 | 64, .bounded = false}, }; - if (program.stage != ShaderType::Compute || input.compute == nullptr || - decoded.instructions.empty() || program.user_data_base != 0 || program.user_data_count < 10 || + if (program.stage != ShaderType::Compute || decoded.instructions.empty() || + program.user_data_base != 0 || program.user_data_count < 10 || program.info.buffers.size() != 1 || !program.info.images.empty()) return {}; const auto& cs = *input.compute; const bool matching_inputs = cs.workgroup_register == 10 && cs.thread_ids_num == 1 && diff --git a/src/graphics/shader/recompiler/ir/passes/ResourceTracking.h b/src/graphics/shader/recompiler/ir/passes/ResourceTracking.h index 9b6a701a7..c9d4bc29a 100644 --- a/src/graphics/shader/recompiler/ir/passes/ResourceTracking.h +++ b/src/graphics/shader/recompiler/ir/passes/ResourceTracking.h @@ -7,7 +7,7 @@ namespace Libs::Graphics::ShaderRecompiler::IR { // Resolves native descriptor sources, plans their scalar reads, and assigns dense resource bindings. void TrackResources(Program& program, const Decoder::Program& decoded, const CFG::Graph& native_cfg, - ShaderStageInputInfo input = {}); + ShaderStageInputInfo input); } // namespace Libs::Graphics::ShaderRecompiler::IR diff --git a/src/graphics/shader/recompiler/ir/passes/ShaderInfoCollection.cpp b/src/graphics/shader/recompiler/ir/passes/ShaderInfoCollection.cpp index b4529d640..4de7f2b84 100644 --- a/src/graphics/shader/recompiler/ir/passes/ShaderInfoCollection.cpp +++ b/src/graphics/shader/recompiler/ir/passes/ShaderInfoCollection.cpp @@ -47,26 +47,17 @@ void ValidateOptions(const Program& program, ShaderStageInputInfo input_info) { case ShaderType::TessellationControl: case ShaderType::TessellationEvaluation: case ShaderType::Mesh: - if (input_info.vertex == nullptr) { - return Fail("vertex shader has no input metadata"); - } if (input_info.vertex->resources_num < 0 || input_info.vertex->resources_num > ShaderVertexInputInfo::RES_MAX) { return Fail("vertex resource count is out of range"); } return; case ShaderType::Pixel: - if (input_info.pixel == nullptr) { - return Fail("pixel shader has no input metadata"); - } if (input_info.pixel->input_num > std::size(input_info.pixel->interpolator_settings)) { return Fail("pixel input count is out of range"); } return; case ShaderType::Compute: - if (input_info.compute == nullptr) { - return Fail("compute shader has no input metadata"); - } if (input_info.compute->thread_ids_num < 0 || input_info.compute->thread_ids_num > 3) { return Fail("compute thread ID count is out of range"); } @@ -191,7 +182,7 @@ void AddBuiltinInput(ShaderInfo& info, StageInputKind kind) { void CollectOutput(const Program& program, ShaderStageInputInfo input_info, ShaderInfo& info, const Inst& inst) { - const bool alpha_remap = program.stage == ShaderType::Pixel && input_info.pixel != nullptr && + const bool alpha_remap = program.stage == ShaderType::Pixel && input_info.pixel->alpha_blend_source != ShaderAlphaBlendSource::None; const auto& export_info = program.export_info[inst.Flags().index]; if (export_info.kind == ExportTargetKind::MrtZ) { diff --git a/tests/ResourceTrackingTests.cpp b/tests/ResourceTrackingTests.cpp index 67f545bc4..a2203f5e9 100644 --- a/tests/ResourceTrackingTests.cpp +++ b/tests/ResourceTrackingTests.cpp @@ -52,6 +52,7 @@ void CheckFatal(F &&function, std::string_view expected, const char *message) { struct Fixture { Program program; Block *block = nullptr; + ShaderComputeInputInfo compute_info{}; explicit Fixture(ShaderType stage = ShaderType::Compute) { program.stage = stage; @@ -131,7 +132,9 @@ struct Fixture { Value(0u), Value(0u), Value(0u)}); } - void PlanAndTrack(Libs::Graphics::ShaderStageInputInfo input = {}) { + void PlanAndTrack() { PlanAndTrack({.compute = &compute_info}); } + + void PlanAndTrack(Libs::Graphics::ShaderStageInputInfo input) { for (size_t index = 0; index < program.blocks.size(); ++index) { const auto condition = program.blocks[index]->condition; if (!condition.IsEmpty()) diff --git a/tests/ScalarProvenanceTests.cpp b/tests/ScalarProvenanceTests.cpp index d1144eeb1..ac8540e81 100644 --- a/tests/ScalarProvenanceTests.cpp +++ b/tests/ScalarProvenanceTests.cpp @@ -14,6 +14,7 @@ namespace { using namespace Libs::Graphics::ShaderRecompiler::IR; +using Libs::Graphics::ShaderComputeInputInfo; using Libs::Graphics::ShaderType; void Check(bool condition, const char *message) { @@ -37,6 +38,7 @@ void CheckFatal(F &&function, std::string_view expected, const char *message) { struct Fixture { Program program; + ShaderComputeInputInfo compute{}; explicit Fixture(uint32_t block_count = 1) { program.stage = ShaderType::Compute; @@ -73,7 +75,7 @@ struct Fixture { return static_cast(program.memory_info.size() - 1u); } - void Plan() { TrackResources(program, {}, {}); } + void Plan() { TrackResources(program, {}, {}, {.compute = &compute}); } }; struct TestMemory { @@ -158,7 +160,7 @@ void TestScalarMemoryDomainMismatchFails() { Fixture raw; const auto raw_memory = raw.AddMemory(ResourceKind::ScalarBuffer); RawRead(raw, Address(raw, Value(0x1000u), Value(0u)), Value(0u), raw_memory); - CheckFatal([&] { TrackResources(raw.program, {}, {}); }, + CheckFatal([&] { raw.Plan(); }, "incompatible scalar memory metadata", "raw scalar load accepted descriptor-buffer metadata"); @@ -169,7 +171,7 @@ void TestScalarMemoryDomainMismatchFails() { {Value(0x1000u), Value(0u), Value(16u), Value(0u)}); buffer.EmitMemory(ValueOpcode::ReadConstBuffer, {resource, Value(0u)}, buffer_memory); - CheckFatal([&] { TrackResources(buffer.program, {}, {}); }, + CheckFatal([&] { buffer.Plan(); }, "incompatible scalar memory metadata", "descriptor scalar load accepted raw-address metadata"); } diff --git a/tests/shaderCfgTests.cpp b/tests/shaderCfgTests.cpp index 5c792da2b..b81bd1e03 100644 --- a/tests/shaderCfgTests.cpp +++ b/tests/shaderCfgTests.cpp @@ -14343,7 +14343,7 @@ void BuildTypedPlan(const uint32_t *code, uint32_t words, ShaderRecompiler::IR::RemoveIdentities(ir); ShaderRecompiler::IR::EliminateDeadCode(ir.blocks); } - ShaderRecompiler::IR::TrackResources(ir, decoded, cfg); + ShaderRecompiler::IR::TrackResources(ir, decoded, cfg, options.input_info); ShaderRecompiler::IR::EliminateDeadCode(ir.blocks); } @@ -16182,10 +16182,6 @@ void TestCompilerStageInputOwnership() { CheckSpirvBinaryValidates(compiled.spirv); #if KYTY_PLATFORM != KYTY_PLATFORM_WINDOWS - options.stage = ShaderType::Vertex; - options.input_info.vertex = nullptr; - ExpectFatal([&] { (void)TranslateProgram(shader, options); }, - "missing stage metadata bypassed translation validation"); const uint32_t auxiliary_position[] = { EncodeExp0(0x0d, 0x1), EncodeExp1(0, 0, 0, 0), EncodeSopp(0x01)}; for (const auto stage : {ShaderType::Pixel, ShaderType::Compute}) {