shader: expose graphics compute queue fields to GETREG

This commit is contained in:
nmzik
2026-10-09 16:22:28 +02:00
parent 1818aae562
commit bb5edbd490
13 changed files with 102 additions and 17 deletions
+3 -2
View File
@@ -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) {
+2 -2
View File
@@ -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<PreparedBindings* const> stages);
@@ -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();
@@ -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;
@@ -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<const uint32_t> 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<int32_t>(word & 0xffffu)
: static_cast<int32_t>(static_cast<int16_t>(word & 0xffffu));
@@ -227,6 +227,7 @@ void DecodeSopk(uint32_t pc, std::span<const uint32_t> 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:
@@ -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,
@@ -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<unsigned>(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;
@@ -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)) {
@@ -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
+4 -1
View File
@@ -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<uint32_
key.push_back(info.thread_ids_num);
key.push_back(info.lds_size_dwords);
key.push_back(info.scratch_size_dwords);
key.push_back(static_cast<uint32_t>(info.dispatch_thread_dimensions));
key.push_back(static_cast<uint32_t>(info.dispatch_thread_dimensions) |
(static_cast<uint32_t>(info.async_compute) << 1u));
for (int i = 0; i < 3; i++) {
key.push_back(info.threads_num[i]);
key.push_back(static_cast<uint32_t>(info.group_id[i]));
+1
View File
@@ -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};
+12 -4
View File
@@ -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<uint32_t>(metadata_size / 4);
fill_metadata(metadata_words);
+58
View File
@@ -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<uint64_t>(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();