diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.cpp b/src/shader_recompiler/backend/spirv/emit_spirv.cpp index 891b26084..5ad402061 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv.cpp @@ -39,6 +39,8 @@ ArgType Arg(EmitContext& ctx, const IR::Value& arg) { return arg.ScalarReg(); } else if constexpr (std::is_same_v) { return arg.VectorReg(); + } else if constexpr (std::is_same_v) { + return arg.StringLiteral(); } } diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp index 642567e0b..947f82079 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp @@ -50,12 +50,9 @@ void EmitEndPrimitive(EmitContext& ctx, const IR::Value& stream) { throw NotImplementedException("Geometry streams"); } -void EmitDebugPrint(EmitContext& ctx, IR::Inst* inst, Id arg0, Id arg1, Id arg2, Id arg3, Id arg4) { - DebugPrintFlags flags = inst->Flags(); - const std::string& format_string = ctx.info.string_pool[flags.string_idx]; - Id fmt = ctx.String(format_string); - - std::array fmt_args = {arg0, arg1, arg2, arg3, arg4}; +void EmitDebugPrint(EmitContext& ctx, IR::Inst* inst, Id fmt, Id arg0, Id arg1, Id arg2, Id arg3) { + IR::DebugPrintFlags flags = inst->Flags(); + std::array fmt_args = {arg0, arg1, arg2, arg3}; auto fmt_args_span = std::span(fmt_args.begin(), fmt_args.begin() + flags.num_args); ctx.OpDebugPrintf(fmt, fmt_args_span); } diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index 6a9eb246f..0763ba65d 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp @@ -73,6 +73,8 @@ Id EmitContext::Def(const IR::Value& value) { return ConstF32(value.F32()); case IR::Type::F64: return Constant(F64[1], value.F64()); + case IR::Type::StringLiteral: + return String(value.StringLiteral()); default: throw NotImplementedException("Immediate type {}", value.Type()); } diff --git a/src/shader_recompiler/ir/debug_print.h b/src/shader_recompiler/ir/debug_print.h index 1750d6771..1ab1575de 100644 --- a/src/shader_recompiler/ir/debug_print.h +++ b/src/shader_recompiler/ir/debug_print.h @@ -2,12 +2,20 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include "common/bit_field.h" +#include "shader_recompiler/ir/opcodes.h" #include "src/common/types.h" #pragma once +namespace Shader::IR { + +constexpr size_t DEBUGPRINT_NUM_FORMAT_ARGS = NumArgsOf(IR::Opcode::DebugPrint) - 1; + union DebugPrintFlags { u32 raw; - BitField<0, 16, u32> string_idx; - BitField<16, 16, u32> num_args; -}; \ No newline at end of file + // For now, only flag is the number of variadic format args actually used + // So bitfield not really needed + BitField<0, 32, u32> num_args; +}; + +} // namespace Shader::IR \ No newline at end of file diff --git a/src/shader_recompiler/ir/ir_emitter.cpp b/src/shader_recompiler/ir/ir_emitter.cpp index c842fd920..162672a60 100644 --- a/src/shader_recompiler/ir/ir_emitter.cpp +++ b/src/shader_recompiler/ir/ir_emitter.cpp @@ -1563,35 +1563,31 @@ void IREmitter::ImageWrite(const Value& handle, const Value& coords, const Value // Renderdoc accepts format specifiers, e.g. %u, listed here: // https://github.com/KhronosGroup/Vulkan-ValidationLayers/blob/main/docs/debug_printf.md // +// fmt must be a string literal (pointer is shallow copied into a Value) // Example usage: // ir.DebugPrint("invocation xyz: (%u, %u, %u)", // {ir.GetVectorReg(IR::VectorReg::V0), // ir.GetVectorReg(IR::VectorReg::V1), // ir.GetVectorReg(IR::VectorReg::V2)}); -void IREmitter::DebugPrint(std::string_view format, - boost::container::small_vector format_args) { - constexpr size_t PRINT_MAX_ARGS = NumArgsOf(IR::Opcode::DebugPrint); - std::array args; +void IREmitter::DebugPrint(const char* fmt, boost::container::small_vector format_args) { + std::array args; - ASSERT_MSG(format_args.size() < PRINT_MAX_ARGS, "DebugPrint only supports up to {} format args", - PRINT_MAX_ARGS); + ASSERT_MSG(format_args.size() < DEBUGPRINT_NUM_FORMAT_ARGS, + "DebugPrint only supports up to {} format args", DEBUGPRINT_NUM_FORMAT_ARGS); for (int i = 0; i < format_args.size(); i++) { args[i] = format_args[i]; } - for (int i = format_args.size(); i < PRINT_MAX_ARGS; i++) { + for (int i = format_args.size(); i < DEBUGPRINT_NUM_FORMAT_ARGS; i++) { args[i] = Inst(Opcode::Void); } - Value val = Inst(Opcode::DebugPrint, args[0], args[1], args[2], args[3], args[4]); + IR::Value fmt_val{fmt}; + DebugPrintFlags flags; - - flags.string_idx.Assign(info.string_pool.size()); - info.string_pool.emplace_back(format); flags.num_args.Assign(format_args.size()); - - val.Inst()->SetFlags(flags); + Inst(Opcode::DebugPrint, Flags{flags}, fmt_val, args[0], args[1], args[2], args[3]); } } // namespace Shader::IR diff --git a/src/shader_recompiler/ir/ir_emitter.h b/src/shader_recompiler/ir/ir_emitter.h index e5732f67f..5077456b9 100644 --- a/src/shader_recompiler/ir/ir_emitter.h +++ b/src/shader_recompiler/ir/ir_emitter.h @@ -45,7 +45,7 @@ public: void Epilogue(); void Discard(); void Discard(const U1& cond); - void DebugPrint(std::string_view format, boost::container::small_vector args); + void DebugPrint(const char* fmt, boost::container::small_vector args); void Barrier(); void WorkgroupMemoryBarrier(); diff --git a/src/shader_recompiler/ir/opcodes.h b/src/shader_recompiler/ir/opcodes.h index ceea8d4a8..2cea70090 100644 --- a/src/shader_recompiler/ir/opcodes.h +++ b/src/shader_recompiler/ir/opcodes.h @@ -51,6 +51,7 @@ constexpr Type F32x4{Type::F32x4}; constexpr Type F64x2{Type::F64x2}; constexpr Type F64x3{Type::F64x3}; constexpr Type F64x4{Type::F64x4}; +constexpr Type StringLiteral{Type::StringLiteral}; constexpr OpcodeMeta META_TABLE[]{ #define OPCODE(name_token, type_token, ...) \ diff --git a/src/shader_recompiler/ir/opcodes.inc b/src/shader_recompiler/ir/opcodes.inc index 34cf30e72..f3ddd7e9b 100644 --- a/src/shader_recompiler/ir/opcodes.inc +++ b/src/shader_recompiler/ir/opcodes.inc @@ -14,7 +14,7 @@ OPCODE(Prologue, Void, OPCODE(Epilogue, Void, ) OPCODE(Discard, Void, ) OPCODE(DiscardCond, Void, U1, ) -OPCODE(DebugPrint, Void, Opaque, Opaque, Opaque, Opaque, Opaque, ) +OPCODE(DebugPrint, Void, StringLiteral, Opaque, Opaque, Opaque, Opaque, ) // Constant memory operations OPCODE(ReadConst, U32, U32x2, U32, ) diff --git a/src/shader_recompiler/ir/type.cpp b/src/shader_recompiler/ir/type.cpp index 9d303b4db..08157f108 100644 --- a/src/shader_recompiler/ir/type.cpp +++ b/src/shader_recompiler/ir/type.cpp @@ -9,10 +9,9 @@ namespace Shader::IR { std::string NameOf(Type type) { static constexpr std::array names{ - "Opaque", "Label", "Reg", "Pred", "Attribute", "U1", "U8", "U16", "U32", - "U64", "F16", "F32", "F64", "U32x2", "U32x3", "U32x4", "F16x2", "F16x3", - "F16x4", "F32x2", "F32x3", "F32x4", "F64x2", "F64x3", "F64x4", - }; + "Opaque", "Label", "Reg", "Pred", "Attribute", "U1", "U8", "U16", "U32", + "U64", "F16", "F32", "F64", "U32x2", "U32x3", "U32x4", "F16x2", "F16x3", + "F16x4", "F32x2", "F32x3", "F32x4", "F64x2", "F64x3", "F64x4", "StringLiteral"}; const size_t bits{static_cast(type)}; if (bits == 0) { return "Void"; diff --git a/src/shader_recompiler/ir/type.h b/src/shader_recompiler/ir/type.h index d7f47e1de..ec855a77e 100644 --- a/src/shader_recompiler/ir/type.h +++ b/src/shader_recompiler/ir/type.h @@ -36,6 +36,7 @@ enum class Type { F64x2 = 1 << 22, F64x3 = 1 << 23, F64x4 = 1 << 24, + StringLiteral = 1 << 25, }; DECLARE_ENUM_FLAG_OPERATORS(Type) diff --git a/src/shader_recompiler/ir/value.cpp b/src/shader_recompiler/ir/value.cpp index 86e5dd141..562d06989 100644 --- a/src/shader_recompiler/ir/value.cpp +++ b/src/shader_recompiler/ir/value.cpp @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include #include "shader_recompiler/ir/value.h" namespace Shader::IR { @@ -27,6 +28,8 @@ Value::Value(u64 value) noexcept : type{Type::U64}, imm_u64{value} {} Value::Value(f64 value) noexcept : type{Type::F64}, imm_f64{value} {} +Value::Value(const char* value) noexcept : type{Type::StringLiteral}, string_literal{value} {} + IR::Type Value::Type() const noexcept { if (IsPhi()) { // The type of a phi node is stored in its flags @@ -69,6 +72,8 @@ bool Value::operator==(const Value& other) const { case Type::U64: case Type::F64: return imm_u64 == other.imm_u64; + case Type::StringLiteral: + return std::string(string_literal) == other.string_literal; case Type::U32x2: case Type::U32x3: case Type::U32x4: diff --git a/src/shader_recompiler/ir/value.h b/src/shader_recompiler/ir/value.h index db939eaa5..060b9d2bb 100644 --- a/src/shader_recompiler/ir/value.h +++ b/src/shader_recompiler/ir/value.h @@ -39,6 +39,7 @@ public: explicit Value(f32 value) noexcept; explicit Value(u64 value) noexcept; explicit Value(f64 value) noexcept; + explicit Value(const char* value) noexcept; [[nodiscard]] bool IsIdentity() const noexcept; [[nodiscard]] bool IsPhi() const noexcept; @@ -60,6 +61,7 @@ public: [[nodiscard]] f32 F32() const; [[nodiscard]] u64 U64() const; [[nodiscard]] f64 F64() const; + [[nodiscard]] const char* StringLiteral() const; [[nodiscard]] bool operator==(const Value& other) const; [[nodiscard]] bool operator!=(const Value& other) const; @@ -78,6 +80,7 @@ private: f32 imm_f32; u64 imm_u64; f64 imm_f64; + const char* string_literal; }; }; static_assert(static_cast(IR::Type::Void) == 0, "memset relies on IR::Type being zero"); @@ -348,6 +351,14 @@ inline f64 Value::F64() const { return imm_f64; } +inline const char* Value::StringLiteral() const { + if (IsIdentity()) { + return inst->Arg(0).StringLiteral(); + } + DEBUG_ASSERT(type == Type::StringLiteral); + return string_literal; +} + [[nodiscard]] inline bool IsPhi(const Inst& inst) { return inst.GetOpcode() == Opcode::Phi; }