mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-28 13:56:59 +01:00
shader: map debug SPIR-V blocks to native RDNA2 PCs
This commit is contained in:
@@ -394,6 +394,7 @@ struct PipelineCache::ProgramCache {
|
|||||||
options.back_code = params.back_code;
|
options.back_code = params.back_code;
|
||||||
options.dump_ir = Config::GetShaderLogDirection() != Config::LogDirection::Silent;
|
options.dump_ir = Config::GetShaderLogDirection() != Config::LogDirection::Silent;
|
||||||
options.early_dump = options.dump_ir;
|
options.early_dump = options.dump_ir;
|
||||||
|
options.emit_debug_info = Config::GraphicsDebugDumpEnabled();
|
||||||
options.dump_label = label;
|
options.dump_label = label;
|
||||||
options.input_info = stage_input;
|
options.input_info = stage_input;
|
||||||
|
|
||||||
|
|||||||
@@ -887,7 +887,7 @@ CompileResult CompileProgram(TranslateResult translated, const CompileOptions& o
|
|||||||
LOGF("%s phase begin: stage=%s hash=0x%016" PRIx64 " SPIR-V EmitProgram\n",
|
LOGF("%s phase begin: stage=%s hash=0x%016" PRIx64 " SPIR-V EmitProgram\n",
|
||||||
GetDumpLabel(options), StageName(ir.stage), ir.shader_hash);
|
GetDumpLabel(options), StageName(ir.stage), ir.shader_hash);
|
||||||
auto spirv = Spirv::EmitProgram(ir, options.input_info, push_data_start_dword,
|
auto spirv = Spirv::EmitProgram(ir, options.input_info, push_data_start_dword,
|
||||||
options.maintenance8_enabled);
|
options.maintenance8_enabled, options.emit_debug_info);
|
||||||
LOGF("%s phase end: stage=%s hash=0x%016" PRIx64 " SPIR-V EmitProgram words=%" PRIu64
|
LOGF("%s phase end: stage=%s hash=0x%016" PRIx64 " SPIR-V EmitProgram words=%" PRIu64
|
||||||
" elapsed_ms=%" PRIu64 "\n",
|
" elapsed_ms=%" PRIu64 "\n",
|
||||||
GetDumpLabel(options), StageName(ir.stage), ir.shader_hash,
|
GetDumpLabel(options), StageName(ir.stage), ir.shader_hash,
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ struct CompileOptions {
|
|||||||
uint64_t shader_hash = 0;
|
uint64_t shader_hash = 0;
|
||||||
bool dump_ir = true;
|
bool dump_ir = true;
|
||||||
bool early_dump = false;
|
bool early_dump = false;
|
||||||
|
bool emit_debug_info = false;
|
||||||
bool maintenance8_enabled = false;
|
bool maintenance8_enabled = false;
|
||||||
const char* dump_label = nullptr;
|
const char* dump_label = nullptr;
|
||||||
std::span<const uint32_t> user_data;
|
std::span<const uint32_t> user_data;
|
||||||
|
|||||||
@@ -160,6 +160,14 @@ void Builder::AddEntryPoint(spv::ExecutionModel execution_model, uint32_t entry_
|
|||||||
AppendInstruction(m_entry_points, spv::OpEntryPoint, operands);
|
AppendInstruction(m_entry_points, spv::OpEntryPoint, operands);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t Builder::AddString(const char* text) {
|
||||||
|
const auto id = AllocateId();
|
||||||
|
std::vector<uint32_t> operands = {id};
|
||||||
|
AppendString(operands, text);
|
||||||
|
AppendInstruction(m_debug, spv::OpString, operands);
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
void Builder::AddName(uint32_t target, const char* name) {
|
void Builder::AddName(uint32_t target, const char* name) {
|
||||||
std::vector<uint32_t> operands = {target};
|
std::vector<uint32_t> operands = {target};
|
||||||
AppendString(operands, name);
|
AppendString(operands, name);
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ public:
|
|||||||
AppendInstruction(m_execution_modes, spv::OpExecutionMode, entry_point, mode, operands...);
|
AppendInstruction(m_execution_modes, spv::OpExecutionMode, entry_point, mode, operands...);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t AddString(const char* text);
|
||||||
void AddName(uint32_t target, const char* name);
|
void AddName(uint32_t target, const char* name);
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
void AddAnnotation(spv::Op opcode, const Args&... operands) {
|
void AddAnnotation(spv::Op opcode, const Args&... operands) {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
namespace Libs::Graphics::ShaderRecompiler::Spirv {
|
namespace Libs::Graphics::ShaderRecompiler::Spirv {
|
||||||
|
|
||||||
@@ -203,7 +204,8 @@ void ValidateNativeProgram(const IR::Program& program, bool lds_storage) {
|
|||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
std::vector<uint32_t> EmitProgram(IR::Program& program, ShaderStageInputInfo input_info,
|
std::vector<uint32_t> EmitProgram(IR::Program& program, ShaderStageInputInfo input_info,
|
||||||
uint32_t push_data_start_dword, bool maintenance8_enabled) {
|
uint32_t push_data_start_dword, bool maintenance8_enabled,
|
||||||
|
bool emit_debug_info) {
|
||||||
using namespace Emitter;
|
using namespace Emitter;
|
||||||
|
|
||||||
if (program.stage != ShaderType::Compute && program.stage != ShaderType::Vertex &&
|
if (program.stage != ShaderType::Compute && program.stage != ShaderType::Vertex &&
|
||||||
@@ -219,6 +221,12 @@ std::vector<uint32_t> EmitProgram(IR::Program& program, ShaderStageInputInfo inp
|
|||||||
IR::ValidateProgram(program, true);
|
IR::ValidateProgram(program, true);
|
||||||
program.bindings = {.push_data_start_dword = push_data_start_dword};
|
program.bindings = {.push_data_start_dword = push_data_start_dword};
|
||||||
EmitterState state(program, input_info, maintenance8_enabled);
|
EmitterState state(program, input_info, maintenance8_enabled);
|
||||||
|
if (emit_debug_info) {
|
||||||
|
char name[64];
|
||||||
|
std::snprintf(name, sizeof(name), "rdna2-%016" PRIx64 "-block-byte-pc-plus-one",
|
||||||
|
program.shader_hash);
|
||||||
|
state.debug_source = state.builder.AddString(name);
|
||||||
|
}
|
||||||
const auto* workgroup = ShaderWorkgroupInput(program.stage, input_info);
|
const auto* workgroup = ShaderWorkgroupInput(program.stage, input_info);
|
||||||
state.lane_count =
|
state.lane_count =
|
||||||
workgroup != nullptr && program.wave_size == 64u && workgroup->host_subgroup_size == 32u
|
workgroup != nullptr && program.wave_size == 64u && workgroup->host_subgroup_size == 32u
|
||||||
|
|||||||
@@ -12,7 +12,8 @@ namespace Libs::Graphics::ShaderRecompiler::Spirv {
|
|||||||
// Resets and writes backend IDs and descriptor indices; do not emit the same program concurrently.
|
// Resets and writes backend IDs and descriptor indices; do not emit the same program concurrently.
|
||||||
std::vector<uint32_t> EmitProgram(IR::Program& program, ShaderStageInputInfo input_info,
|
std::vector<uint32_t> EmitProgram(IR::Program& program, ShaderStageInputInfo input_info,
|
||||||
uint32_t push_data_start_dword = 0,
|
uint32_t push_data_start_dword = 0,
|
||||||
bool maintenance8_enabled = false);
|
bool maintenance8_enabled = false,
|
||||||
|
bool emit_debug_info = false);
|
||||||
|
|
||||||
} // namespace Libs::Graphics::ShaderRecompiler::Spirv
|
} // namespace Libs::Graphics::ShaderRecompiler::Spirv
|
||||||
|
|
||||||
|
|||||||
@@ -89,6 +89,7 @@ struct EmitterState {
|
|||||||
IR::Program& program;
|
IR::Program& program;
|
||||||
ShaderStageInputInfo input_info;
|
ShaderStageInputInfo input_info;
|
||||||
bool maintenance8_enabled;
|
bool maintenance8_enabled;
|
||||||
|
uint32_t debug_source = 0;
|
||||||
uint32_t void_type = 0;
|
uint32_t void_type = 0;
|
||||||
uint32_t bool_type = 0;
|
uint32_t bool_type = 0;
|
||||||
uint32_t u32_type = 0;
|
uint32_t u32_type = 0;
|
||||||
@@ -182,6 +183,11 @@ uint32_t TypeU32ElementPointer(EmitterState& state, spv::StorageClass storage_cl
|
|||||||
inline void EmitLabel(EmitterState& state, uint32_t label) {
|
inline void EmitLabel(EmitterState& state, uint32_t label) {
|
||||||
state.current_label = label;
|
state.current_label = label;
|
||||||
state.builder.AddFunction(spv::OpLabel, label);
|
state.builder.AddFunction(spv::OpLabel, label);
|
||||||
|
if (state.debug_source != 0 && state.current_block != nullptr) {
|
||||||
|
// Source lines encode the containing native block's byte offset plus one.
|
||||||
|
state.builder.AddFunction(spv::OpLine, state.debug_source,
|
||||||
|
state.current_block->start_pc + 1u, 0u);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t TypeId(EmitterState& state, IR::Type type);
|
uint32_t TypeId(EmitterState& state, IR::Type type);
|
||||||
|
|||||||
@@ -348,6 +348,7 @@ void EmitDispatcherFunction(ValueEmitContext& ctx, const DispatcherFunctionState
|
|||||||
const auto initial_parent = state.current_label;
|
const auto initial_parent = state.current_label;
|
||||||
state.builder.AddFunction(spv::OpBranch, dispatcher.header_label);
|
state.builder.AddFunction(spv::OpBranch, dispatcher.header_label);
|
||||||
|
|
||||||
|
state.current_block = nullptr;
|
||||||
EmitLabel(state, dispatcher.header_label);
|
EmitLabel(state, dispatcher.header_label);
|
||||||
const auto pc = state.builder.AllocateId();
|
const auto pc = state.builder.AllocateId();
|
||||||
const auto next_pc = state.builder.AllocateId();
|
const auto next_pc = state.builder.AllocateId();
|
||||||
@@ -383,6 +384,7 @@ void EmitDispatcherFunction(ValueEmitContext& ctx, const DispatcherFunctionState
|
|||||||
next_pc_words.push_back(state.current_label);
|
next_pc_words.push_back(state.current_label);
|
||||||
state.builder.AddFunction(spv::OpBranch, dispatcher.after_switch_label);
|
state.builder.AddFunction(spv::OpBranch, dispatcher.after_switch_label);
|
||||||
}
|
}
|
||||||
|
state.current_block = nullptr;
|
||||||
EmitLabel(state, dispatcher.after_switch_label);
|
EmitLabel(state, dispatcher.after_switch_label);
|
||||||
state.builder.AddFunction(next_pc_words);
|
state.builder.AddFunction(next_pc_words);
|
||||||
state.builder.AddFunction(spv::OpBranch, dispatcher.continue_label);
|
state.builder.AddFunction(spv::OpBranch, dispatcher.continue_label);
|
||||||
@@ -747,6 +749,7 @@ void EmitProgram(EmitterState& state) {
|
|||||||
EmitStructuredFunction(ctx);
|
EmitStructuredFunction(ctx);
|
||||||
}
|
}
|
||||||
state.builder.AddFunction(spv::OpFunctionEnd);
|
state.builder.AddFunction(spv::OpFunctionEnd);
|
||||||
|
state.current_block = nullptr;
|
||||||
if (state.program.stage == ShaderType::Mesh) {
|
if (state.program.stage == ShaderType::Mesh) {
|
||||||
EmitMeshEntryPoint(state);
|
EmitMeshEntryPoint(state);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15711,6 +15711,46 @@ void TestRepeatedExportsHaveOneInterface() {
|
|||||||
"repeated exports duplicated Position or its clipping-error plane");
|
"repeated exports duplicated Position or its clipping-error plane");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CheckSpirvBlockSourceMapping(TestCompileResult &result) {
|
||||||
|
using namespace ShaderRecompiler;
|
||||||
|
const auto options = MakeCompileOptions(ShaderType::Compute);
|
||||||
|
Check(SpirvInstructionOpcodeCount(result.spirv, spv::OpString) == 0 &&
|
||||||
|
SpirvInstructionOpcodeCount(result.spirv, spv::OpLine) == 0,
|
||||||
|
"default emission unexpectedly contains source mapping");
|
||||||
|
const auto mapped = Spirv::EmitProgram(result.program, options.input_info, 0,
|
||||||
|
false, true);
|
||||||
|
CheckSpirvBinaryValidates(mapped);
|
||||||
|
uint32_t source = 0;
|
||||||
|
uint32_t label = 0;
|
||||||
|
size_t lines = 0;
|
||||||
|
uint32_t previous = spv::OpNop;
|
||||||
|
for (size_t at = 5; at < mapped.size();) {
|
||||||
|
const auto op = mapped[at] & spv::OpCodeMask;
|
||||||
|
if (op == spv::OpLabel) label = mapped[at + 1];
|
||||||
|
if (op == spv::OpString) {
|
||||||
|
source = mapped[at + 1];
|
||||||
|
Check(std::string(reinterpret_cast<const char *>(&mapped[at + 2]))
|
||||||
|
.find("-block-byte-pc-plus-one") != std::string::npos,
|
||||||
|
"debug source filename does not identify block offset encoding");
|
||||||
|
} else if (op == spv::OpLine) {
|
||||||
|
Check(previous == spv::OpLabel && mapped[at + 1] == source &&
|
||||||
|
mapped[at + 3] == 0 &&
|
||||||
|
std::ranges::any_of(result.program.blocks, [&](const auto *block) {
|
||||||
|
return block->Definition() == label &&
|
||||||
|
mapped[at + 2] == block->start_pc + 1u;
|
||||||
|
}),
|
||||||
|
"debug source line does not identify a native block after its label");
|
||||||
|
++lines;
|
||||||
|
}
|
||||||
|
previous = op;
|
||||||
|
at += mapped[at] >> spv::WordCountShift;
|
||||||
|
}
|
||||||
|
Check(source != 0 && lines >= result.program.blocks.size(),
|
||||||
|
"debug emission omitted native block source mapping");
|
||||||
|
Check(Spirv::EmitProgram(result.program, options.input_info) == result.spirv,
|
||||||
|
"debug re-emission changed the default binary");
|
||||||
|
}
|
||||||
|
|
||||||
void TestNewShaderRecompilerSpirvSizeBaselines() {
|
void TestNewShaderRecompilerSpirvSizeBaselines() {
|
||||||
const auto compile = [](const char *name, std::span<const uint32_t> shader,
|
const auto compile = [](const char *name, std::span<const uint32_t> shader,
|
||||||
const SpirvMetrics &budget,
|
const SpirvMetrics &budget,
|
||||||
@@ -15755,7 +15795,7 @@ void TestNewShaderRecompilerSpirvSizeBaselines() {
|
|||||||
EncodeSopp(0x02, 0xfffcu), // continue/backedge
|
EncodeSopp(0x02, 0xfffcu), // continue/backedge
|
||||||
EncodeSopp(0x01),
|
EncodeSopp(0x01),
|
||||||
};
|
};
|
||||||
const auto structured_result = compile("structured-phi", structured_phi,
|
auto structured_result = compile("structured-phi", structured_phi,
|
||||||
{.words = 174,
|
{.words = 174,
|
||||||
.instructions = 49,
|
.instructions = 49,
|
||||||
.phis = 1,
|
.phis = 1,
|
||||||
@@ -15772,6 +15812,7 @@ void TestNewShaderRecompilerSpirvSizeBaselines() {
|
|||||||
structured_metrics.loads == 0u && structured_metrics.stores == 0u,
|
structured_metrics.loads == 0u && structured_metrics.stores == 0u,
|
||||||
"structured Phi retained a spill variable or edge memory operation");
|
"structured Phi retained a spill variable or edge memory operation");
|
||||||
CheckSpirvPhiParents(structured_result.spirv);
|
CheckSpirvPhiParents(structured_result.spirv);
|
||||||
|
CheckSpirvBlockSourceMapping(structured_result);
|
||||||
const auto structured_repeat =
|
const auto structured_repeat =
|
||||||
compile("structured-phi-repeat", structured_phi,
|
compile("structured-phi-repeat", structured_phi,
|
||||||
{.words = 174,
|
{.words = 174,
|
||||||
@@ -15942,7 +15983,7 @@ void TestNewShaderRecompilerSpirvSizeBaselines() {
|
|||||||
EncodeSopp(0x02, 0xfffeu), // B -> C
|
EncodeSopp(0x02, 0xfffeu), // B -> C
|
||||||
EncodeSopp(0x01),
|
EncodeSopp(0x01),
|
||||||
};
|
};
|
||||||
const auto dispatcher_result = compile("dispatcher", dispatcher,
|
auto dispatcher_result = compile("dispatcher", dispatcher,
|
||||||
{.words = 255,
|
{.words = 255,
|
||||||
.instructions = 70,
|
.instructions = 70,
|
||||||
.variables = 3,
|
.variables = 3,
|
||||||
@@ -15963,6 +16004,7 @@ void TestNewShaderRecompilerSpirvSizeBaselines() {
|
|||||||
SpirvInstructionOpcodeCount(dispatcher_result.spirv, 251u) == 1u,
|
SpirvInstructionOpcodeCount(dispatcher_result.spirv, 251u) == 1u,
|
||||||
"dispatcher size fixture lost its two control Phis or switch");
|
"dispatcher size fixture lost its two control Phis or switch");
|
||||||
CheckSpirvPhiParents(dispatcher_result.spirv);
|
CheckSpirvPhiParents(dispatcher_result.spirv);
|
||||||
|
CheckSpirvBlockSourceMapping(dispatcher_result);
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "ShaderRayTracingTests.inc"
|
#include "ShaderRayTracingTests.inc"
|
||||||
|
|||||||
Reference in New Issue
Block a user