mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2024-12-28 02:26:07 +00:00
DebugPrintf in shaders (#1252)
* Add shader debug print opcode that uses NonSemantic DebugPrintf extension * small correction for flags in Inst * Fix IR Debug Print. Add StringLiteral op * add missing microinstruction changes for debugprint * cleanup. delete vaarg stuff. Smuggle format string in Info and flags * more cleanup * more * (dont merge??) update sirit submodule * fix num args 4 -> 5 * add notes about DebugPrint IR op * use NumArgsOf again * copyright * update sirit submodule * fix clangformat * add new Value variant for string literal. Use arg0 for fmt string * remove string pool changes * Update src/shader_recompiler/ir/value.cpp Co-authored-by: TheTurtle <47210458+raphaelthegreat@users.noreply.github.com> --------- Co-authored-by: TheTurtle <47210458+raphaelthegreat@users.noreply.github.com>
This commit is contained in:
parent
310814ac71
commit
3c0255b953
2
externals/sirit
vendored
2
externals/sirit
vendored
|
@ -1 +1 @@
|
|||
Subproject commit 37090c74cc6e680f2bc334cac8fd182f7634a1f6
|
||||
Subproject commit 6cecb95d679c82c413d1f989e0b7ad9af130600d
|
|
@ -70,6 +70,8 @@ ArgType Arg(EmitContext& ctx, const IR::Value& arg) {
|
|||
return arg.ScalarReg();
|
||||
} else if constexpr (std::is_same_v<ArgType, IR::VectorReg>) {
|
||||
return arg.VectorReg();
|
||||
} else if constexpr (std::is_same_v<ArgType, const char*>) {
|
||||
return arg.StringLiteral();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -48,6 +48,7 @@ void EmitPrologue(EmitContext& ctx);
|
|||
void EmitEpilogue(EmitContext& ctx);
|
||||
void EmitDiscard(EmitContext& ctx);
|
||||
void EmitDiscardCond(EmitContext& ctx, Id condition);
|
||||
void EmitDebugPrint(EmitContext& ctx, IR::Inst* inst, Id arg0, Id arg1, Id arg2, Id arg3, Id arg4);
|
||||
void EmitBarrier(EmitContext& ctx);
|
||||
void EmitWorkgroupMemoryBarrier(EmitContext& ctx);
|
||||
void EmitDeviceMemoryBarrier(EmitContext& ctx);
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#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 {
|
||||
|
||||
|
@ -57,4 +58,11 @@ void EmitEndPrimitive(EmitContext& ctx, const IR::Value& stream) {
|
|||
throw NotImplementedException("Geometry streams");
|
||||
}
|
||||
|
||||
void EmitDebugPrint(EmitContext& ctx, IR::Inst* inst, Id fmt, Id arg0, Id arg1, Id arg2, Id arg3) {
|
||||
IR::DebugPrintFlags flags = inst->Flags<IR::DebugPrintFlags>();
|
||||
std::array<Id, IR::DEBUGPRINT_NUM_FORMAT_ARGS> fmt_args = {arg0, arg1, arg2, arg3};
|
||||
auto fmt_args_span = std::span<Id>(fmt_args.begin(), fmt_args.begin() + flags.num_args);
|
||||
ctx.OpDebugPrintf(fmt, fmt_args_span);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::SPIRV
|
||||
|
|
|
@ -91,6 +91,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());
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <span>
|
||||
#include <vector>
|
||||
#include <boost/container/small_vector.hpp>
|
||||
#include <boost/container/static_vector.hpp>
|
||||
#include "common/assert.h"
|
||||
|
|
21
src/shader_recompiler/ir/debug_print.h
Normal file
21
src/shader_recompiler/ir/debug_print.h
Normal file
|
@ -0,0 +1,21 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// 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;
|
||||
// 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
|
|
@ -1,10 +1,15 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <array>
|
||||
#include <bit>
|
||||
#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,6 +1558,38 @@ void IREmitter::ImageWrite(const Value& handle, const Value& coords, const Value
|
|||
Inst(Opcode::ImageWrite, Flags{info}, handle, coords, color);
|
||||
}
|
||||
|
||||
// Debug print maps to SPIRV's NonSemantic DebugPrintf instruction
|
||||
// Renderdoc will hook in its own implementation of the SPIRV instruction
|
||||
// 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(const char* fmt, boost::container::small_vector<Value, 5> format_args) {
|
||||
std::array<Value, DEBUGPRINT_NUM_FORMAT_ARGS> 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 < DEBUGPRINT_NUM_FORMAT_ARGS; i++) {
|
||||
args[i] = Inst(Opcode::Void);
|
||||
}
|
||||
|
||||
IR::Value fmt_val{fmt};
|
||||
|
||||
DebugPrintFlags flags;
|
||||
flags.num_args.Assign(format_args.size());
|
||||
Inst(Opcode::DebugPrint, Flags{flags}, fmt_val, args[0], args[1], args[2], args[3]);
|
||||
}
|
||||
|
||||
void IREmitter::EmitVertex() {
|
||||
Inst(Opcode::EmitVertex);
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include <cstring>
|
||||
#include <type_traits>
|
||||
|
||||
#include "shader_recompiler/info.h"
|
||||
#include "shader_recompiler/ir/attribute.h"
|
||||
#include "shader_recompiler/ir/basic_block.h"
|
||||
#include "shader_recompiler/ir/condition.h"
|
||||
|
@ -43,6 +44,7 @@ public:
|
|||
void Epilogue();
|
||||
void Discard();
|
||||
void Discard(const U1& cond);
|
||||
void DebugPrint(const char* fmt, boost::container::small_vector<Value, 5> args);
|
||||
|
||||
void Barrier();
|
||||
void WorkgroupMemoryBarrier();
|
||||
|
|
|
@ -89,6 +89,7 @@ bool Inst::MayHaveSideEffects() const noexcept {
|
|||
case Opcode::ImageAtomicOr32:
|
||||
case Opcode::ImageAtomicXor32:
|
||||
case Opcode::ImageAtomicExchange32:
|
||||
case Opcode::DebugPrint:
|
||||
case Opcode::EmitVertex:
|
||||
case Opcode::EmitPrimitive:
|
||||
return true;
|
||||
|
|
|
@ -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, ...) \
|
||||
|
@ -81,7 +82,7 @@ constexpr u8 NUM_ARGS[]{
|
|||
}
|
||||
|
||||
/// Get the number of arguments an opcode accepts
|
||||
[[nodiscard]] inline size_t NumArgsOf(Opcode op) noexcept {
|
||||
[[nodiscard]] constexpr inline size_t NumArgsOf(Opcode op) noexcept {
|
||||
return static_cast<size_t>(Detail::NUM_ARGS[static_cast<size_t>(op)]);
|
||||
}
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ OPCODE(Prologue, Void,
|
|||
OPCODE(Epilogue, Void, )
|
||||
OPCODE(Discard, Void, )
|
||||
OPCODE(DiscardCond, Void, U1, )
|
||||
OPCODE(DebugPrint, Void, StringLiteral, Opaque, Opaque, Opaque, Opaque, )
|
||||
|
||||
// Constant memory operations
|
||||
OPCODE(ReadConst, U32, U32x2, U32, )
|
||||
|
|
|
@ -11,8 +11,7 @@ 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",
|
||||
};
|
||||
"F16x4", "F32x2", "F32x3", "F32x4", "F64x2", "F64x3", "F64x4", "StringLiteral"};
|
||||
const size_t bits{static_cast<size_t>(type)};
|
||||
if (bits == 0) {
|
||||
return "Void";
|
||||
|
|
|
@ -36,6 +36,7 @@ enum class Type {
|
|||
F64x2 = 1 << 22,
|
||||
F64x3 = 1 << 23,
|
||||
F64x4 = 1 << 24,
|
||||
StringLiteral = 1 << 25,
|
||||
};
|
||||
DECLARE_ENUM_FLAG_OPERATORS(Type)
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <string>
|
||||
#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_view(string_literal) == other.string_literal;
|
||||
case Type::U32x2:
|
||||
case Type::U32x3:
|
||||
case Type::U32x4:
|
||||
|
|
|
@ -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<u32>(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;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue