Add shader debug print opcode that uses NonSemantic DebugPrintf extension

This commit is contained in:
Frodo Baggins 2024-09-18 23:05:54 -07:00
parent a5968b630d
commit ff293920e6
10 changed files with 141 additions and 1 deletions

2
externals/sirit vendored

@ -1 +1 @@
Subproject commit 37090c74cc6e680f2bc334cac8fd182f7634a1f6
Subproject commit b51af1a2ca01090cbcbd4855dc29adf40d3bd612

View file

@ -49,6 +49,8 @@ void EmitPrologue(EmitContext& ctx);
void EmitEpilogue(EmitContext& ctx);
void EmitDiscard(EmitContext& ctx);
void EmitDiscardCond(EmitContext& ctx, Id condition);
void EmitVaArg(EmitContext& ctx, IR::Inst* inst, Id arg, Id next);
void EmitDebugPrint(EmitContext& ctx, IR::Inst* inst);
void EmitBarrier(EmitContext& ctx);
void EmitWorkgroupMemoryBarrier(EmitContext& ctx);
void EmitDeviceMemoryBarrier(EmitContext& ctx);

View file

@ -1,8 +1,12 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <boost/container/small_vector.hpp>
#include "common/assert.h"
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
#include "shader_recompiler/ir/debug_print.h"
namespace Shader::Backend::SPIRV {
@ -49,4 +53,37 @@ void EmitEndPrimitive(EmitContext& ctx, const IR::Value& stream) {
throw NotImplementedException("Geometry streams");
}
void EmitVaArg(EmitContext& ctx, IR::Inst* inst, Id arg, Id next) {
IR::Value next_val = inst->Arg(1);
u32 va_arglist_idx;
if (next_val.IsEmpty()) {
va_arglist_idx = ctx.va_arg_lists.size();
ctx.va_arg_lists.emplace_back();
} else {
va_arglist_idx = next_val.Inst()->Flags<VariadicArgInfo>().va_arg_idx;
}
ctx.va_arg_lists[va_arglist_idx].push_back(arg);
VariadicArgInfo va_info;
va_info.va_arg_idx.Assign(va_arglist_idx);
}
void EmitDebugPrint(EmitContext& ctx, IR::Inst* inst) {
const std::string& fmt =
ctx.info.debug_print_strings.at(inst->Flags<DebugPrintInfo>().string_idx);
Id fmt_id = ctx.String(fmt);
IR::Value va_arg_list_val = inst->Arg(0);
boost::container::small_vector<Id, 4> fmt_arg_operands;
if (!va_arg_list_val.IsEmpty()) {
u32 va_arglist_idx = va_arg_list_val.Inst()->Flags<VariadicArgInfo>().va_arg_idx;
const auto& va_arglist = ctx.va_arg_lists[va_arglist_idx];
// reverse the order
std::copy(va_arglist.rbegin(), va_arglist.rend(), fmt_arg_operands.end());
}
ASSERT(fmt_arg_operands.size() == inst->Flags<DebugPrintInfo>().num_args);
ctx.OpDebugPrintf(fmt_id, fmt_arg_operands);
}
} // namespace Shader::Backend::SPIRV

View file

@ -244,6 +244,8 @@ public:
std::array<SpirvAttribute, IR::NumParams> output_params{};
std::array<SpirvAttribute, IR::NumRenderTargets> frag_outputs{};
boost::container::small_vector<boost::container::small_vector<Id, 4>, 4> va_arg_lists;
private:
void DefineArithmeticTypes();
void DefineInterfaces();

View file

@ -179,6 +179,8 @@ struct Info {
std::span<const u32> user_data;
Stage stage;
boost::container::small_vector<std::string, 4> debug_print_strings;
u64 pgm_hash{};
VAddr pgm_base;
bool has_storage_images{};

View file

@ -0,0 +1,16 @@
#include "common/bit_field.h"
#include "src/common/enum.h"
#pragma once
union VariadicArgInfo {
u32 raw;
BitField<0, 12, u32> va_arg_idx;
};
union DebugPrintInfo {
u32 raw;
BitField<0, 16, u32> string_idx;
BitField<16, 16, u32> num_args;
};

View file

@ -2,9 +2,14 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <bit>
#include <iterator>
#include <source_location>
#include <boost/container/small_vector.hpp>
#include "common/assert.h"
#include "shader_recompiler/exception.h"
#include "shader_recompiler/ir/debug_print.h"
#include "shader_recompiler/ir/ir_emitter.h"
#include "shader_recompiler/ir/opcodes.h"
#include "shader_recompiler/ir/value.h"
namespace Shader::IR {
@ -1553,4 +1558,69 @@ void IREmitter::ImageWrite(const Value& handle, const Value& coords, const Value
Inst(Opcode::ImageWrite, Flags{info}, handle, coords, color);
}
Value IREmitter::VaArg(Value arg, Value next) {
return Inst(Opcode::VaArg, arg, next);
}
void IREmitter::DebugPrint(std::string_view format, boost::container::small_vector<Value, 4> args,
bool infer_specifiers) {
auto infer_specifier = [&](IR::Value arg) -> const char* {
switch (arg.Type()) {
case Shader::IR::Type::U1:
case Shader::IR::Type::U8:
case Shader::IR::Type::U16:
case Shader::IR::Type::U32:
return "%u";
case Shader::IR::Type::U64:
return "%lu";
case Shader::IR::Type::F16:
case Shader::IR::Type::F32:
case Shader::IR::Type::F64:
return "%lf";
case Shader::IR::Type::U32x2:
return "%v2u";
case Shader::IR::Type::U32x3:
return "%v3u";
case Shader::IR::Type::U32x4:
return "%v4u";
case Shader::IR::Type::F16x2:
case Shader::IR::Type::F32x2:
case Shader::IR::Type::F64x2:
return "%v2f";
case Shader::IR::Type::F16x3:
case Shader::IR::Type::F32x3:
case Shader::IR::Type::F64x3:
return "%v3f";
case Shader::IR::Type::F16x4:
case Shader::IR::Type::F32x4:
case Shader::IR::Type::F64x4:
return "%v4f";
case Shader::IR::Type::Void:
case Shader::IR::Type::Opaque:
case Shader::IR::Type::ScalarReg:
case Shader::IR::Type::VectorReg:
case Shader::IR::Type::Attribute:
case Shader::IR::Type::SystemValue:
UNREACHABLE();
}
};
if (infer_specifiers) {
UNREACHABLE();
// need to fmt the format string with dynamic sized array of format spec strings
// could use param pack for this function, but less flexible
}
DebugPrintInfo info{};
info.string_idx.Assign(debug_print_strings.size());
info.num_args.Assign(args.size());
Value arg_list = Inst(Opcode::Void);
for (Value arg : args) {
arg_list = VaArg(arg, arg_list);
}
Inst(Opcode::DebugPrint, info, arg_list);
}
} // namespace Shader::IR

View file

@ -4,7 +4,9 @@
#pragma once
#include <cstring>
#include <string_view>
#include <type_traits>
#include <boost/container/small_vector.hpp>
#include "shader_recompiler/ir/attribute.h"
#include "shader_recompiler/ir/basic_block.h"
@ -44,6 +46,11 @@ public:
void Discard();
void Discard(const U1& cond);
Value VaArg(Value arg, Value next);
void DebugPrint(std::string_view format, boost::container::small_vector<Value, 4> args,
bool infer_specifiers = false);
void Barrier();
void WorkgroupMemoryBarrier();
void DeviceMemoryBarrier();
@ -312,6 +319,7 @@ public:
private:
IR::Block::iterator insertion_point;
boost::container::small_vector<std::string, 4> debug_print_strings;
template <typename T = Value, typename... Args>
T Inst(Opcode op, Args... args) {

View file

@ -89,6 +89,7 @@ bool Inst::MayHaveSideEffects() const noexcept {
case Opcode::ImageAtomicOr32:
case Opcode::ImageAtomicXor32:
case Opcode::ImageAtomicExchange32:
case Opcode::DebugPrint:
return true;
default:
return false;

View file

@ -14,6 +14,8 @@ OPCODE(Prologue, Void,
OPCODE(Epilogue, Void, )
OPCODE(Discard, Void, )
OPCODE(DiscardCond, Void, U1, )
OPCODE(DebugPrint, Void, Opaque, )
OPCODE(VaArg, Opaque, Opaque, Opaque )
// Constant memory operations
OPCODE(ReadConst, U32, U32x2, U32, )