diff --git a/src/graphics/guest_gpu/graphicsRun.cpp b/src/graphics/guest_gpu/graphicsRun.cpp index 639336aa0..2336fe50d 100644 --- a/src/graphics/guest_gpu/graphicsRun.cpp +++ b/src/graphics/guest_gpu/graphicsRun.cpp @@ -1003,7 +1003,7 @@ void CommandProcessor::DispatchDirect(uint32_t thread_group_x, uint32_t thread_g } m_renderer.GetRenderExecutor().DispatchDirect(m_submit_id, CurrentBuffer(), thread_group_x, - thread_group_y, thread_group_z, mode); + thread_group_y, thread_group_z, mode, IsAsyncComputeQueue()); } } @@ -1015,7 +1015,8 @@ void CommandProcessor::DispatchIndirect(uint64_t args_addr, uint32_t mode) { return; } m_sh_ctx.SetCsWaveSize(Pm4::ComputeWaveSize(mode)); - m_renderer.GetRenderExecutor().DispatchIndirect(m_submit_id, CurrentBuffer(), args_addr, mode); + m_renderer.GetRenderExecutor().DispatchIndirect(m_submit_id, CurrentBuffer(), args_addr, mode, + IsAsyncComputeQueue()); } void CommandProcessor::DrawIndexAuto(DrawAutoArgs args) { diff --git a/src/graphics/host_gpu/renderer/render.h b/src/graphics/host_gpu/renderer/render.h index 4b6055309..39ae92dca 100644 --- a/src/graphics/host_gpu/renderer/render.h +++ b/src/graphics/host_gpu/renderer/render.h @@ -156,9 +156,9 @@ public: KYTY_CLASS_NO_COPY(RenderExecutor); void DispatchDirect(uint64_t submit_id, CommandBuffer& buffer, uint32_t thread_group_x, - uint32_t thread_group_y, uint32_t thread_group_z, uint32_t mode); + uint32_t thread_group_y, uint32_t thread_group_z, uint32_t mode, bool async_compute); void DispatchIndirect(uint64_t submit_id, CommandBuffer& buffer, uint64_t args_addr, - uint32_t mode); + uint32_t mode, bool async_compute); void PrepareBindings(const ShaderStageRuntime& runtime, PreparedBindings& prepared); void FindBuffers(std::span stages); diff --git a/src/graphics/host_gpu/renderer/renderCompute.cpp b/src/graphics/host_gpu/renderer/renderCompute.cpp index bddb6d5c2..7222d7f84 100644 --- a/src/graphics/host_gpu/renderer/renderCompute.cpp +++ b/src/graphics/host_gpu/renderer/renderCompute.cpp @@ -226,7 +226,7 @@ static void BindSharedMemory(RenderContext& context, ShaderComputeInputInfo& inp void RenderExecutor::DispatchDirect(uint64_t submit_id, CommandBuffer& buffer, uint32_t thread_group_x, uint32_t thread_group_y, - uint32_t thread_group_z, uint32_t mode) { + uint32_t thread_group_z, uint32_t mode, bool async_compute) { EXIT_IF(buffer.IsInvalid()); m_context.GetCommandScheduler().PopPendingOperations(); auto& ctx = buffer.GetRegisters(); @@ -281,6 +281,7 @@ void RenderExecutor::DispatchDirect(uint64_t submit_id, CommandBuffer& buffer, const auto& sh_regs = ctx.GetShaderRegisters(); ShaderComputeInputInfo input_info {}; + input_info.async_compute = async_compute; const bool use_thread_dimensions = (mode & DISPATCH_INITIATOR_USE_THREAD_DIMENSIONS) != 0; input_info.dispatch_thread_dimensions = use_thread_dimensions; input_info.workgroup_counts[0] = thread_group_x; @@ -437,7 +438,7 @@ void RenderExecutor::DispatchDirect(uint64_t submit_id, CommandBuffer& buffer, } void RenderExecutor::DispatchIndirect(uint64_t submit_id, CommandBuffer& buffer, - uint64_t args_addr, uint32_t mode) { + uint64_t args_addr, uint32_t mode, bool async_compute) { EXIT_IF(buffer.IsInvalid() || args_addr == 0 || (args_addr & 3u) != 0 || (mode & Pm4::COMPUTE_DISPATCH_INITIATOR_USE_THREAD_DIMENSIONS) != 0); m_context.GetCommandScheduler().PopPendingOperations(); @@ -450,6 +451,7 @@ void RenderExecutor::DispatchIndirect(uint64_t submit_id, CommandBuffer& buffer, return; } ShaderComputeInputInfo input_info {}; + input_info.async_compute = async_compute; const auto compute_program = m_context.GetPipelineCache().GetComputeProgram( cs_regs, buffer.GetRegisters().GetShaderRegisters(), input_info); buffer.EndRendering(); diff --git a/src/graphics/shader/recompiler/ShaderRecompiler.cpp b/src/graphics/shader/recompiler/ShaderRecompiler.cpp index 080d06240..ce3b40799 100644 --- a/src/graphics/shader/recompiler/ShaderRecompiler.cpp +++ b/src/graphics/shader/recompiler/ShaderRecompiler.cpp @@ -534,7 +534,8 @@ void PrepareCallTarget(ShaderSource& source, const CompileOptions& options) { if (inserted) { Block translated; Frontend::Translator translator(query, &translated, 1u, - (options.input_info.compute->float_mode & 0x10u) == 0u); + (options.input_info.compute->float_mode & 0x10u) == 0u, + !options.input_info.compute->async_compute); translator.TranslateInstruction(source.decoded.instructions[index]); for (const auto& inst: translated) { uint32_t destination = UINT32_MAX; diff --git a/src/graphics/shader/recompiler/frontend/decode/ScalarAluOps.cpp b/src/graphics/shader/recompiler/frontend/decode/ScalarAluOps.cpp index 12fc9411d..f48366663 100644 --- a/src/graphics/shader/recompiler/frontend/decode/ScalarAluOps.cpp +++ b/src/graphics/shader/recompiler/frontend/decode/ScalarAluOps.cpp @@ -89,7 +89,7 @@ constexpr OpcodeMap SOPK_OPCODE_LIST[] = { {0x08u, Opcode::S_CMP_LE_I32}, {0x09u, Opcode::S_CMP_EQ_U32}, {0x0au, Opcode::S_CMP_LG_U32}, {0x0bu, Opcode::S_CMP_GT_U32}, {0x0cu, Opcode::S_CMP_GE_U32}, {0x0du, Opcode::S_CMP_LT_U32}, {0x0eu, Opcode::S_CMP_LE_U32}, {0x0fu, Opcode::S_ADD_I32}, {0x10u, Opcode::S_MULK_I32}, - {0x13u, Opcode::S_SETREG_B32}, {0x17u, Opcode::S_WAITCNT_VSCNT}, {0x18u, Opcode::S_WAITCNT}, + {0x12u, Opcode::S_GETREG_B32}, {0x13u, Opcode::S_SETREG_B32}, {0x17u, Opcode::S_WAITCNT_VSCNT}, {0x18u, Opcode::S_WAITCNT}, {0x19u, Opcode::S_WAITCNT}, {0x1au, Opcode::S_WAITCNT}, {0x1bu, Opcode::S_SUBVECTOR_LOOP_BEGIN}, {0x1cu, Opcode::S_SUBVECTOR_LOOP_END}, }; @@ -207,7 +207,7 @@ void DecodeSopk(uint32_t pc, std::span code, uint32_t word_index const uint32_t word = code[word_index]; const uint32_t opcode = (word >> 23u) & 0x1fu; const uint32_t sdst = (word >> 16u) & 0x7fu; - const auto imm = opcode >= 0x09u && opcode <= 0x0eu + const auto imm = (opcode >= 0x09u && opcode <= 0x0eu) || opcode == 0x12u ? static_cast(word & 0xffffu) : static_cast(static_cast(word & 0xffffu)); @@ -227,6 +227,7 @@ void DecodeSopk(uint32_t pc, std::span code, uint32_t word_index } switch (inst.opcode) { + case Opcode::S_GETREG_B32: case Opcode::S_MOVK_I32: DecodeScalarDestination(sdst, pc, inst.dst); return; case Opcode::S_SUBVECTOR_LOOP_BEGIN: case Opcode::S_SUBVECTOR_LOOP_END: diff --git a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h index 5520a35ee..0df6237d4 100644 --- a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h +++ b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h @@ -679,6 +679,7 @@ enum class Opcode { S_CBRANCH_CDBGSYS, S_CBRANCH_CDBGSYS_OR_USER, S_SENDMSG, + S_GETREG_B32, S_SETREG_B32, S_SLEEP, S_TRAP, diff --git a/src/graphics/shader/recompiler/frontend/translate/Scalar.cpp b/src/graphics/shader/recompiler/frontend/translate/Scalar.cpp index d9cfaafc4..b08797d1f 100644 --- a/src/graphics/shader/recompiler/frontend/translate/Scalar.cpp +++ b/src/graphics/shader/recompiler/frontend/translate/Scalar.cpp @@ -18,6 +18,13 @@ void Translator::EmitScalar(const Decoder::Instruction& inst) { case O::S_CSELECT_B32: S_CSELECT_B32(inst); return; case O::S_CSELECT_B64: ScalarSelect64(inst, inst.src1); return; case O::S_CMOV_B64: ScalarSelect64(inst, inst.dst); return; + case O::S_GETREG_B32: + if (!graphics_compute || (inst.src0.value != 0x1818u && inst.src0.value != 0x0a18u)) + EXIT("unsupported S_GETREG_B32 at pc 0x%08x: selector=0x%04x graphics_compute=%u", + inst.pc, inst.src0.value, static_cast(graphics_compute)); + // HW_ID2 identifies graphics compute as QUEUE=6, ME=0. + WriteOperand(inst.dst, IR::Value(inst.src0.value == 0x1818u ? 6u : 0u)); + return; case O::S_SETREG_B32: EmitControlNop(); return; case O::S_WAITCNT_VSCNT: S_WAITCNT_VSCNT(inst); return; case O::S_WAITCNT: return; diff --git a/src/graphics/shader/recompiler/frontend/translate/Translate.cpp b/src/graphics/shader/recompiler/frontend/translate/Translate.cpp index e77a04e66..73d426f92 100644 --- a/src/graphics/shader/recompiler/frontend/translate/Translate.cpp +++ b/src/graphics/shader/recompiler/frontend/translate/Translate.cpp @@ -1356,7 +1356,8 @@ IR::Program TranslateProgram(const Decoder::Program& decoded, const CFG::Graph& (options.input_info.compute->float_mode & 0x10u) == 0; for (const auto& cfg_block: cfg.blocks) { auto* block = blocks_by_id.at(cfg_block.id); - Translator translator(result, block, vector_limit, flush_f32_inputs); + Translator translator(result, block, vector_limit, flush_f32_inputs, + options.stage == ShaderType::Compute && !options.input_info.compute->async_compute); for (uint32_t index = cfg_block.inst_begin; index < cfg_block.inst_end; index++) { const auto& instruction = decoded.instructions[index]; if (IsCodeTableLoad(cfg, instruction.pc)) { diff --git a/src/graphics/shader/recompiler/frontend/translate/Translator.h b/src/graphics/shader/recompiler/frontend/translate/Translator.h index 5d5b4f432..e9b9486d2 100644 --- a/src/graphics/shader/recompiler/frontend/translate/Translator.h +++ b/src/graphics/shader/recompiler/frontend/translate/Translator.h @@ -10,9 +10,9 @@ namespace Libs::Graphics::ShaderRecompiler::Frontend { class Translator { public: Translator(IR::Program& program, IR::Block* block, uint32_t vector_limit, - bool flush_f32_inputs) + bool flush_f32_inputs, bool graphics_compute) : program(program), ir(block), current_vector_limit(vector_limit), - flush_f32_inputs(flush_f32_inputs) {} + flush_f32_inputs(flush_f32_inputs), graphics_compute(graphics_compute) {} void TranslateInstruction(const Decoder::Instruction& inst); void TranslateEmbeddedFetch(const Decoder::Instruction& inst, uint32_t attribute, @@ -264,6 +264,7 @@ private: uint32_t current_pc = 0; uint32_t current_vector_limit = 1; bool flush_f32_inputs; + bool graphics_compute; }; } // namespace Libs::Graphics::ShaderRecompiler::Frontend diff --git a/src/graphics/shader/shader.cpp b/src/graphics/shader/shader.cpp index fee4ba91c..deff9fc60 100644 --- a/src/graphics/shader/shader.cpp +++ b/src/graphics/shader/shader.cpp @@ -574,10 +574,12 @@ static void ShaderGetStaticInputInfoPS( static void ShaderGetStaticInputInfoCS(const HW::ComputeShaderInfo& regs, const HW::ShaderRegisters& /*sh*/, const ShaderMappedData& data, ShaderComputeInputInfo& info) { + const bool async_compute = info.async_compute; const bool dispatch_thread_dimensions = info.dispatch_thread_dimensions; const auto host_subgroup_size = info.host_subgroup_size; const auto workgroup_counts = std::to_array(info.workgroup_counts); info = {}; + info.async_compute = async_compute; info.dispatch_thread_dimensions = dispatch_thread_dimensions; info.host_subgroup_size = host_subgroup_size; std::ranges::copy(workgroup_counts, info.workgroup_counts); @@ -693,7 +695,8 @@ void BuildStageStaticKey(const ShaderComputeInputInfo& info, std::vector(info.dispatch_thread_dimensions)); + key.push_back(static_cast(info.dispatch_thread_dimensions) | + (static_cast(info.async_compute) << 1u)); for (int i = 0; i < 3; i++) { key.push_back(info.threads_num[i]); key.push_back(static_cast(info.group_id[i])); diff --git a/src/graphics/shader/shader.h b/src/graphics/shader/shader.h index d1c0b24ef..39a4aa40b 100644 --- a/src/graphics/shader/shader.h +++ b/src/graphics/shader/shader.h @@ -146,6 +146,7 @@ struct ShaderVertexInputInfo { }; struct ShaderComputeInputInfo: ShaderWorkgroupInputInfo { + bool async_compute = false; uint8_t float_mode = 0xc0; uint32_t dispatch_threads_num[3] = {0, 0, 0}; uint32_t workgroup_counts[3] = {0, 0, 0}; diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index 74ab26f20..b2f7f435b 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -5055,7 +5055,7 @@ public: for (const auto mode : {0x41u, 0x61u}) { for (const auto &groups : empty_dispatches) { context.GetRenderExecutor().DispatchDirect( - 0, scheduler.Current(), groups[0], groups[1], groups[2], mode); + 0, scheduler.Current(), groups[0], groups[1], groups[2], mode, false); } } Require(name, "HTile fixture", @@ -10006,14 +10006,22 @@ public: for (u32 i = 0; i < 4; ++i) shaders.SetCsUserSgpr(i, out.fields[i], HW::UserSgprType::Unknown); uint64_t original_id = 0; - struct Case { uint64_t address; u32 value; bool update; }; + struct Case { uint64_t address; u32 value; bool update; u32 field = 0; }; for (const auto test : {Case{first, 41, false}, Case{first, 41, false}, Case{second, 41, false}, Case{second, 73, true}, - Case{first, 41, false}}) { + Case{first, 41, false}, Case{first, 6, false, 0x1818}, + Case{first, 0, false, 0x0a18}}) { Require(name, "refresh function pointer", context.InvalidateMemory(table + 0x20, 8), "function table escaped mapped memory"); const uint64_t target = test.address | 3u; LibKernel::Memory::WriteBacking(table + 0x20, &target, sizeof(target)); + if (test.field != 0) { + Require(name, "refresh hardware-field leaf", context.InvalidateMemory(first, sizeof(leaf)), + "hardware-field leaf escaped mapped memory"); + const std::array field_leaf{0xb96a0000u | test.field, + EncodeSMovB32(20, 106), EncodeSop1(0x21, 125, 14)}; + LibKernel::Memory::WriteBacking(first, field_leaf.data(), sizeof(field_leaf)); + } if (test.update) { auto [buffer, offset] = cache.ObtainBuffer(test.address + 4, 4, true); buffer->Fill(offset, 4, test.value); @@ -10703,7 +10711,7 @@ public: for (uint32_t i = 0; i < user_data.size(); i++) { shaders.SetCsUserSgpr(i, user_data[i], HW::UserSgprType::Unknown); } - executor.DispatchDirect(0, scheduler.Current(), (count + 63) / 64, 1, 1, 0x41u); + executor.DispatchDirect(0, scheduler.Current(), (count + 63) / 64, 1, 1, 0x41u, false); }; const auto metadata_words = static_cast(metadata_size / 4); fill_metadata(metadata_words); diff --git a/tests/shaderCfgTests.cpp b/tests/shaderCfgTests.cpp index cb1a87437..283b17e4a 100644 --- a/tests/shaderCfgTests.cpp +++ b/tests/shaderCfgTests.cpp @@ -15,6 +15,7 @@ #include "graphics/shader/recompiler/frontend/cfg/ShaderCFG.h" #include "graphics/shader/recompiler/frontend/decode/ShaderDecoder.h" #include "graphics/shader/recompiler/frontend/translate/Translate.h" +#include "graphics/shader/recompiler/frontend/translate/Translator.h" #include "graphics/shader/recompiler/ir/IREmitter.h" #include "graphics/shader/recompiler/ir/ShaderIR.h" #include "graphics/shader/recompiler/ir/passes/ConstantPropagation.h" @@ -10308,6 +10309,62 @@ void TestNewShaderRecompilerBranchConditionForms() { } } +void TestGraphicsComputeGetreg() { + using namespace ShaderRecompiler; + for (const auto [word, expected] : {std::pair{0xb96a1818u, 6u}, std::pair{0xb96a0a18u, 0u}}) { + Decoder::Instruction decoded; + Decoder::DecodeInstruction(std::span(&word, 1), 0, decoded); + Check(decoded.opcode == Decoder::Opcode::S_GETREG_B32 && + decoded.dst.kind == Decoder::OperandKind::VccLo && + decoded.src0.value == (word & 0xffffu), "GETREG field or VCC destination decoded incorrectly"); + for (const auto wave : {32u, 64u}) { + IR::Program program; + program.wave_size = wave; + IR::Block block; + Frontend::Translator translator(program, &block, 1, false, true); + translator.TranslateInstruction(decoded); + bool wrote_vcc = false; + for (const auto& inst : block) { + Check(inst.GetOpcode() != IR::ValueOpcode::SetScc, "GETREG changed SCC"); + if (inst.GetOpcode() == IR::ValueOpcode::SetVccLo) { + Check(inst.Arg(0).IsImmediate() && inst.Arg(0).U32() == expected, + "graphics compute hardware field has the wrong value"); + wrote_vcc = true; + } + } + Check(wrote_vcc, "GETREG did not write VCC_LO"); + } + } + const uint32_t shader[] = {0xb96a1818u, EncodeSopp(0x01)}; + HW::ComputeShaderInfo regs{}; + regs.cs_regs.data_addr = reinterpret_cast(shader); + ShaderMappedData mapped{}; + mapped.code_size_bytes = sizeof(shader); + ShaderMapUserData(regs.cs_regs.data_addr, mapped); + HW::ShaderRegisters sh{}; + ShaderComputeInputInfo graphics{}, async{}; + async.async_compute = true; + (void)PrepareProgram(regs, sh, graphics); + (void)PrepareProgram(regs, sh, async); + Check(!graphics.async_compute && async.async_compute && + MakeStageStaticKey(graphics) != MakeStageStaticKey(async) && + MakeStageStaticKey(graphics).size() == MakeStageStaticKey(async).size(), + "compute input reset or cache identity lost queue provenance"); +#if KYTY_PLATFORM != KYTY_PLATFORM_WINDOWS + auto options = MakeCompileOptions(ShaderType::Compute); + options.input_info.compute = &async; + ExpectFatal([&] { (void)TranslateProgram(shader, options); }, "async GETREG used graphics constants"); + auto vertex = MakeCompileOptions(ShaderType::Vertex); + ExpectFatal([&] { (void)TranslateProgram(shader, vertex); }, "vertex GETREG used compute constants"); + options.input_info.compute = &graphics; + const uint32_t unsupported[] = {0xb96af818u, EncodeSopp(0x01)}; + Decoder::Program decoded; + Decoder::DecodeProgram(unsupported, decoded); + Check(decoded.instructions[0].src0.value == 0xf818u, "GETREG selector was sign extended"); + ExpectFatal([&] { (void)TranslateProgram(unsupported, options); }, "unsupported GETREG field was accepted"); +#endif +} + void TestMemoryFedScalarLeafCall() { using namespace ShaderRecompiler; constexpr uint64_t base = 0xfe0040000ull; @@ -15586,6 +15643,7 @@ int main() { TestNewShaderRecompilerPixelImageSampleLodSelection(); TestNewShaderRecompilerBranchConditionForms(); TestNewShaderRecompilerSetpcBranch(); + TestGraphicsComputeGetreg(); TestMemoryFedScalarLeafCall(); TestFusedShaderHandoffPreservesRegisters(); TestMeshExportStorage();