diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/citra_qt/CMakeLists.txt | 4 | ||||
| -rw-r--r-- | src/citra_qt/debugger/graphics_breakpoint_observer.cpp | 32 | ||||
| -rw-r--r-- | src/citra_qt/debugger/graphics_breakpoint_observer.h | 33 | ||||
| -rw-r--r-- | src/citra_qt/debugger/graphics_framebuffer.cpp | 27 | ||||
| -rw-r--r-- | src/citra_qt/debugger/graphics_framebuffer.h | 24 | ||||
| -rw-r--r-- | src/citra_qt/debugger/graphics_vertex_shader.cpp | 298 | ||||
| -rw-r--r-- | src/citra_qt/debugger/graphics_vertex_shader.h | 51 | ||||
| -rw-r--r-- | src/citra_qt/main.cpp | 6 |
8 files changed, 425 insertions, 50 deletions
diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt index bbc521f8a..586bc84b0 100644 --- a/src/citra_qt/CMakeLists.txt +++ b/src/citra_qt/CMakeLists.txt | |||
| @@ -8,9 +8,11 @@ set(SRCS | |||
| 8 | debugger/callstack.cpp | 8 | debugger/callstack.cpp |
| 9 | debugger/disassembler.cpp | 9 | debugger/disassembler.cpp |
| 10 | debugger/graphics.cpp | 10 | debugger/graphics.cpp |
| 11 | debugger/graphics_breakpoint_observer.cpp | ||
| 11 | debugger/graphics_breakpoints.cpp | 12 | debugger/graphics_breakpoints.cpp |
| 12 | debugger/graphics_cmdlists.cpp | 13 | debugger/graphics_cmdlists.cpp |
| 13 | debugger/graphics_framebuffer.cpp | 14 | debugger/graphics_framebuffer.cpp |
| 15 | debugger/graphics_vertex_shader.cpp | ||
| 14 | debugger/ramview.cpp | 16 | debugger/ramview.cpp |
| 15 | debugger/registers.cpp | 17 | debugger/registers.cpp |
| 16 | util/spinbox.cpp | 18 | util/spinbox.cpp |
| @@ -27,10 +29,12 @@ set(HEADERS | |||
| 27 | debugger/callstack.h | 29 | debugger/callstack.h |
| 28 | debugger/disassembler.h | 30 | debugger/disassembler.h |
| 29 | debugger/graphics.h | 31 | debugger/graphics.h |
| 32 | debugger/graphics_breakpoint_observer.h | ||
| 30 | debugger/graphics_breakpoints.h | 33 | debugger/graphics_breakpoints.h |
| 31 | debugger/graphics_breakpoints_p.h | 34 | debugger/graphics_breakpoints_p.h |
| 32 | debugger/graphics_cmdlists.h | 35 | debugger/graphics_cmdlists.h |
| 33 | debugger/graphics_framebuffer.h | 36 | debugger/graphics_framebuffer.h |
| 37 | debugger/graphics_vertex_shader.h | ||
| 34 | debugger/ramview.h | 38 | debugger/ramview.h |
| 35 | debugger/registers.h | 39 | debugger/registers.h |
| 36 | util/spinbox.h | 40 | util/spinbox.h |
diff --git a/src/citra_qt/debugger/graphics_breakpoint_observer.cpp b/src/citra_qt/debugger/graphics_breakpoint_observer.cpp new file mode 100644 index 000000000..10ac1ebad --- /dev/null +++ b/src/citra_qt/debugger/graphics_breakpoint_observer.cpp | |||
| @@ -0,0 +1,32 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <QMetaType> | ||
| 6 | |||
| 7 | #include "graphics_breakpoint_observer.h" | ||
| 8 | |||
| 9 | BreakPointObserverDock::BreakPointObserverDock(std::shared_ptr<Pica::DebugContext> debug_context, | ||
| 10 | const QString& title, QWidget* parent) | ||
| 11 | : QDockWidget(title, parent), BreakPointObserver(debug_context) | ||
| 12 | { | ||
| 13 | qRegisterMetaType<Pica::DebugContext::Event>("Pica::DebugContext::Event"); | ||
| 14 | |||
| 15 | connect(this, SIGNAL(Resumed()), this, SLOT(OnResumed())); | ||
| 16 | |||
| 17 | // NOTE: This signal is emitted from a non-GUI thread, but connect() takes | ||
| 18 | // care of delaying its handling to the GUI thread. | ||
| 19 | connect(this, SIGNAL(BreakPointHit(Pica::DebugContext::Event,void*)), | ||
| 20 | this, SLOT(OnBreakPointHit(Pica::DebugContext::Event,void*)), | ||
| 21 | Qt::BlockingQueuedConnection); | ||
| 22 | } | ||
| 23 | |||
| 24 | void BreakPointObserverDock::OnPicaBreakPointHit(Pica::DebugContext::Event event, void* data) | ||
| 25 | { | ||
| 26 | emit BreakPointHit(event, data); | ||
| 27 | } | ||
| 28 | |||
| 29 | void BreakPointObserverDock::OnPicaResume() | ||
| 30 | { | ||
| 31 | emit Resumed(); | ||
| 32 | } | ||
diff --git a/src/citra_qt/debugger/graphics_breakpoint_observer.h b/src/citra_qt/debugger/graphics_breakpoint_observer.h new file mode 100644 index 000000000..f0d3361f8 --- /dev/null +++ b/src/citra_qt/debugger/graphics_breakpoint_observer.h | |||
| @@ -0,0 +1,33 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <QDockWidget> | ||
| 8 | |||
| 9 | #include "video_core/debug_utils/debug_utils.h" | ||
| 10 | |||
| 11 | /** | ||
| 12 | * Utility class which forwards calls to OnPicaBreakPointHit and OnPicaResume to public slots. | ||
| 13 | * This is because the Pica breakpoint callbacks are called from a non-GUI thread, while | ||
| 14 | * the widget usually wants to perform reactions in the GUI thread. | ||
| 15 | */ | ||
| 16 | class BreakPointObserverDock : public QDockWidget, private Pica::DebugContext::BreakPointObserver { | ||
| 17 | Q_OBJECT | ||
| 18 | |||
| 19 | public: | ||
| 20 | BreakPointObserverDock(std::shared_ptr<Pica::DebugContext> debug_context, const QString& title, | ||
| 21 | QWidget* parent = nullptr); | ||
| 22 | |||
| 23 | void OnPicaBreakPointHit(Pica::DebugContext::Event event, void* data) override; | ||
| 24 | void OnPicaResume() override; | ||
| 25 | |||
| 26 | private slots: | ||
| 27 | virtual void OnBreakPointHit(Pica::DebugContext::Event event, void* data) = 0; | ||
| 28 | virtual void OnResumed() = 0; | ||
| 29 | |||
| 30 | signals: | ||
| 31 | void Resumed(); | ||
| 32 | void BreakPointHit(Pica::DebugContext::Event event, void* data); | ||
| 33 | }; | ||
diff --git a/src/citra_qt/debugger/graphics_framebuffer.cpp b/src/citra_qt/debugger/graphics_framebuffer.cpp index 43c59738f..1ba60021f 100644 --- a/src/citra_qt/debugger/graphics_framebuffer.cpp +++ b/src/citra_qt/debugger/graphics_framebuffer.cpp | |||
| @@ -6,7 +6,6 @@ | |||
| 6 | #include <QComboBox> | 6 | #include <QComboBox> |
| 7 | #include <QDebug> | 7 | #include <QDebug> |
| 8 | #include <QLabel> | 8 | #include <QLabel> |
| 9 | #include <QMetaType> | ||
| 10 | #include <QPushButton> | 9 | #include <QPushButton> |
| 11 | #include <QSpinBox> | 10 | #include <QSpinBox> |
| 12 | 11 | ||
| @@ -17,32 +16,6 @@ | |||
| 17 | 16 | ||
| 18 | #include "util/spinbox.h" | 17 | #include "util/spinbox.h" |
| 19 | 18 | ||
| 20 | BreakPointObserverDock::BreakPointObserverDock(std::shared_ptr<Pica::DebugContext> debug_context, | ||
| 21 | const QString& title, QWidget* parent) | ||
| 22 | : QDockWidget(title, parent), BreakPointObserver(debug_context) | ||
| 23 | { | ||
| 24 | qRegisterMetaType<Pica::DebugContext::Event>("Pica::DebugContext::Event"); | ||
| 25 | |||
| 26 | connect(this, SIGNAL(Resumed()), this, SLOT(OnResumed())); | ||
| 27 | |||
| 28 | // NOTE: This signal is emitted from a non-GUI thread, but connect() takes | ||
| 29 | // care of delaying its handling to the GUI thread. | ||
| 30 | connect(this, SIGNAL(BreakPointHit(Pica::DebugContext::Event,void*)), | ||
| 31 | this, SLOT(OnBreakPointHit(Pica::DebugContext::Event,void*)), | ||
| 32 | Qt::BlockingQueuedConnection); | ||
| 33 | } | ||
| 34 | |||
| 35 | void BreakPointObserverDock::OnPicaBreakPointHit(Pica::DebugContext::Event event, void* data) | ||
| 36 | { | ||
| 37 | emit BreakPointHit(event, data); | ||
| 38 | } | ||
| 39 | |||
| 40 | void BreakPointObserverDock::OnPicaResume() | ||
| 41 | { | ||
| 42 | emit Resumed(); | ||
| 43 | } | ||
| 44 | |||
| 45 | |||
| 46 | GraphicsFramebufferWidget::GraphicsFramebufferWidget(std::shared_ptr<Pica::DebugContext> debug_context, | 19 | GraphicsFramebufferWidget::GraphicsFramebufferWidget(std::shared_ptr<Pica::DebugContext> debug_context, |
| 47 | QWidget* parent) | 20 | QWidget* parent) |
| 48 | : BreakPointObserverDock(debug_context, tr("Pica Framebuffer"), parent), | 21 | : BreakPointObserverDock(debug_context, tr("Pica Framebuffer"), parent), |
diff --git a/src/citra_qt/debugger/graphics_framebuffer.h b/src/citra_qt/debugger/graphics_framebuffer.h index 56215761e..c6e293bc9 100644 --- a/src/citra_qt/debugger/graphics_framebuffer.h +++ b/src/citra_qt/debugger/graphics_framebuffer.h | |||
| @@ -6,7 +6,7 @@ | |||
| 6 | 6 | ||
| 7 | #include <QDockWidget> | 7 | #include <QDockWidget> |
| 8 | 8 | ||
| 9 | #include "video_core/debug_utils/debug_utils.h" | 9 | #include "graphics_breakpoint_observer.h" |
| 10 | 10 | ||
| 11 | class QComboBox; | 11 | class QComboBox; |
| 12 | class QLabel; | 12 | class QLabel; |
| @@ -14,28 +14,6 @@ class QSpinBox; | |||
| 14 | 14 | ||
| 15 | class CSpinBox; | 15 | class CSpinBox; |
| 16 | 16 | ||
| 17 | // Utility class which forwards calls to OnPicaBreakPointHit and OnPicaResume to public slots. | ||
| 18 | // This is because the Pica breakpoint callbacks are called from a non-GUI thread, while | ||
| 19 | // the widget usually wants to perform reactions in the GUI thread. | ||
| 20 | class BreakPointObserverDock : public QDockWidget, Pica::DebugContext::BreakPointObserver { | ||
| 21 | Q_OBJECT | ||
| 22 | |||
| 23 | public: | ||
| 24 | BreakPointObserverDock(std::shared_ptr<Pica::DebugContext> debug_context, const QString& title, | ||
| 25 | QWidget* parent = nullptr); | ||
| 26 | |||
| 27 | void OnPicaBreakPointHit(Pica::DebugContext::Event event, void* data) override; | ||
| 28 | void OnPicaResume() override; | ||
| 29 | |||
| 30 | private slots: | ||
| 31 | virtual void OnBreakPointHit(Pica::DebugContext::Event event, void* data) = 0; | ||
| 32 | virtual void OnResumed() = 0; | ||
| 33 | |||
| 34 | signals: | ||
| 35 | void Resumed(); | ||
| 36 | void BreakPointHit(Pica::DebugContext::Event event, void* data); | ||
| 37 | }; | ||
| 38 | |||
| 39 | class GraphicsFramebufferWidget : public BreakPointObserverDock { | 17 | class GraphicsFramebufferWidget : public BreakPointObserverDock { |
| 40 | Q_OBJECT | 18 | Q_OBJECT |
| 41 | 19 | ||
diff --git a/src/citra_qt/debugger/graphics_vertex_shader.cpp b/src/citra_qt/debugger/graphics_vertex_shader.cpp new file mode 100644 index 000000000..06eaf0bf0 --- /dev/null +++ b/src/citra_qt/debugger/graphics_vertex_shader.cpp | |||
| @@ -0,0 +1,298 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <iomanip> | ||
| 6 | #include <sstream> | ||
| 7 | |||
| 8 | #include <QBoxLayout> | ||
| 9 | #include <QTreeView> | ||
| 10 | |||
| 11 | #include "video_core/vertex_shader.h" | ||
| 12 | |||
| 13 | #include "graphics_vertex_shader.h" | ||
| 14 | |||
| 15 | using nihstro::Instruction; | ||
| 16 | using nihstro::SourceRegister; | ||
| 17 | using nihstro::SwizzlePattern; | ||
| 18 | |||
| 19 | GraphicsVertexShaderModel::GraphicsVertexShaderModel(QObject* parent): QAbstractItemModel(parent) { | ||
| 20 | |||
| 21 | } | ||
| 22 | |||
| 23 | QModelIndex GraphicsVertexShaderModel::index(int row, int column, const QModelIndex& parent) const { | ||
| 24 | return createIndex(row, column); | ||
| 25 | } | ||
| 26 | |||
| 27 | QModelIndex GraphicsVertexShaderModel::parent(const QModelIndex& child) const { | ||
| 28 | return QModelIndex(); | ||
| 29 | } | ||
| 30 | |||
| 31 | int GraphicsVertexShaderModel::columnCount(const QModelIndex& parent) const { | ||
| 32 | return 3; | ||
| 33 | } | ||
| 34 | |||
| 35 | int GraphicsVertexShaderModel::rowCount(const QModelIndex& parent) const { | ||
| 36 | return info.code.size(); | ||
| 37 | } | ||
| 38 | |||
| 39 | QVariant GraphicsVertexShaderModel::headerData(int section, Qt::Orientation orientation, int role) const { | ||
| 40 | switch(role) { | ||
| 41 | case Qt::DisplayRole: | ||
| 42 | { | ||
| 43 | if (section == 0) { | ||
| 44 | return tr("Offset"); | ||
| 45 | } else if (section == 1) { | ||
| 46 | return tr("Raw"); | ||
| 47 | } else if (section == 2) { | ||
| 48 | return tr("Disassembly"); | ||
| 49 | } | ||
| 50 | |||
| 51 | break; | ||
| 52 | } | ||
| 53 | } | ||
| 54 | |||
| 55 | return QVariant(); | ||
| 56 | } | ||
| 57 | |||
| 58 | QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) const { | ||
| 59 | switch (role) { | ||
| 60 | case Qt::DisplayRole: | ||
| 61 | { | ||
| 62 | switch (index.column()) { | ||
| 63 | case 0: | ||
| 64 | if (info.HasLabel(index.row())) | ||
| 65 | return QString::fromStdString(info.GetLabel(index.row())); | ||
| 66 | |||
| 67 | return QString("%1").arg(4*index.row(), 4, 16, QLatin1Char('0')); | ||
| 68 | |||
| 69 | case 1: | ||
| 70 | return QString("%1").arg(info.code[index.row()].hex, 8, 16, QLatin1Char('0')); | ||
| 71 | |||
| 72 | case 2: | ||
| 73 | { | ||
| 74 | std::stringstream output; | ||
| 75 | output.flags(std::ios::hex); | ||
| 76 | |||
| 77 | Instruction instr = info.code[index.row()]; | ||
| 78 | const SwizzlePattern& swizzle = info.swizzle_info[instr.common.operand_desc_id].pattern; | ||
| 79 | |||
| 80 | // longest known instruction name: "setemit " | ||
| 81 | output << std::setw(8) << std::left << instr.opcode.GetInfo().name; | ||
| 82 | |||
| 83 | // e.g. "-c92.xyzw" | ||
| 84 | static auto print_input = [](std::stringstream& output, const SourceRegister& input, | ||
| 85 | bool negate, const std::string& swizzle_mask) { | ||
| 86 | output << std::setw(4) << std::right << (negate ? "-" : "") + input.GetName(); | ||
| 87 | output << "." << swizzle_mask; | ||
| 88 | }; | ||
| 89 | |||
| 90 | // e.g. "-c92[a0.x].xyzw" | ||
| 91 | static auto print_input_indexed = [](std::stringstream& output, const SourceRegister& input, | ||
| 92 | bool negate, const std::string& swizzle_mask, | ||
| 93 | const std::string& address_register_name) { | ||
| 94 | std::string relative_address; | ||
| 95 | if (!address_register_name.empty()) | ||
| 96 | relative_address = "[" + address_register_name + "]"; | ||
| 97 | |||
| 98 | output << std::setw(10) << std::right << (negate ? "-" : "") + input.GetName() + relative_address; | ||
| 99 | output << "." << swizzle_mask; | ||
| 100 | }; | ||
| 101 | |||
| 102 | // Use print_input or print_input_indexed depending on whether relative addressing is used or not. | ||
| 103 | static auto print_input_indexed_compact = [](std::stringstream& output, const SourceRegister& input, | ||
| 104 | bool negate, const std::string& swizzle_mask, | ||
| 105 | const std::string& address_register_name) { | ||
| 106 | if (address_register_name.empty()) | ||
| 107 | print_input(output, input, negate, swizzle_mask); | ||
| 108 | else | ||
| 109 | print_input_indexed(output, input, negate, swizzle_mask, address_register_name); | ||
| 110 | }; | ||
| 111 | |||
| 112 | switch (instr.opcode.GetInfo().type) { | ||
| 113 | case Instruction::OpCodeType::Trivial: | ||
| 114 | // Nothing to do here | ||
| 115 | break; | ||
| 116 | |||
| 117 | case Instruction::OpCodeType::Arithmetic: | ||
| 118 | { | ||
| 119 | // Use custom code for special instructions | ||
| 120 | switch (instr.opcode.EffectiveOpCode()) { | ||
| 121 | case Instruction::OpCode::CMP: | ||
| 122 | { | ||
| 123 | // NOTE: CMP always writes both cc components, so we do not consider the dest mask here. | ||
| 124 | output << std::setw(4) << std::right << "cc."; | ||
| 125 | output << "xy "; | ||
| 126 | |||
| 127 | SourceRegister src1 = instr.common.GetSrc1(false); | ||
| 128 | SourceRegister src2 = instr.common.GetSrc2(false); | ||
| 129 | |||
| 130 | print_input_indexed_compact(output, src1, swizzle.negate_src1, swizzle.SelectorToString(false).substr(0,1), instr.common.AddressRegisterName()); | ||
| 131 | output << " " << instr.common.compare_op.ToString(instr.common.compare_op.x) << " "; | ||
| 132 | print_input(output, src2, swizzle.negate_src2, swizzle.SelectorToString(false).substr(0,1)); | ||
| 133 | |||
| 134 | output << ", "; | ||
| 135 | |||
| 136 | print_input_indexed_compact(output, src1, swizzle.negate_src1, swizzle.SelectorToString(false).substr(1,1), instr.common.AddressRegisterName()); | ||
| 137 | output << " " << instr.common.compare_op.ToString(instr.common.compare_op.y) << " "; | ||
| 138 | print_input(output, src2, swizzle.negate_src2, swizzle.SelectorToString(false).substr(1,1)); | ||
| 139 | |||
| 140 | break; | ||
| 141 | } | ||
| 142 | |||
| 143 | default: | ||
| 144 | { | ||
| 145 | bool src_is_inverted = 0 != (instr.opcode.GetInfo().subtype & Instruction::OpCodeInfo::SrcInversed); | ||
| 146 | |||
| 147 | if (instr.opcode.GetInfo().subtype & Instruction::OpCodeInfo::Dest) { | ||
| 148 | // e.g. "r12.xy__" | ||
| 149 | output << std::setw(4) << std::right << instr.common.dest.GetName() + "."; | ||
| 150 | output << swizzle.DestMaskToString(); | ||
| 151 | } else if (instr.opcode.GetInfo().subtype == Instruction::OpCodeInfo::MOVA) { | ||
| 152 | output << std::setw(4) << std::right << "a0."; | ||
| 153 | output << swizzle.DestMaskToString(); | ||
| 154 | } else { | ||
| 155 | output << " "; | ||
| 156 | } | ||
| 157 | output << " "; | ||
| 158 | |||
| 159 | if (instr.opcode.GetInfo().subtype & Instruction::OpCodeInfo::Src1) { | ||
| 160 | SourceRegister src1 = instr.common.GetSrc1(src_is_inverted); | ||
| 161 | print_input_indexed(output, src1, swizzle.negate_src1, swizzle.SelectorToString(false), instr.common.AddressRegisterName()); | ||
| 162 | } else { | ||
| 163 | output << " "; | ||
| 164 | } | ||
| 165 | |||
| 166 | // TODO: In some cases, the Address Register is used as an index for SRC2 instead of SRC1 | ||
| 167 | if (instr.opcode.GetInfo().subtype & Instruction::OpCodeInfo::Src2) { | ||
| 168 | SourceRegister src2 = instr.common.GetSrc2(src_is_inverted); | ||
| 169 | print_input(output, src2, swizzle.negate_src2, swizzle.SelectorToString(false)); | ||
| 170 | } | ||
| 171 | break; | ||
| 172 | } | ||
| 173 | } | ||
| 174 | |||
| 175 | break; | ||
| 176 | } | ||
| 177 | |||
| 178 | case Instruction::OpCodeType::Conditional: | ||
| 179 | { | ||
| 180 | switch (instr.opcode.EffectiveOpCode()) { | ||
| 181 | case Instruction::OpCode::LOOP: | ||
| 182 | output << "(unknown instruction format)"; | ||
| 183 | break; | ||
| 184 | |||
| 185 | default: | ||
| 186 | output << "if "; | ||
| 187 | |||
| 188 | if (instr.opcode.GetInfo().subtype & Instruction::OpCodeInfo::HasCondition) { | ||
| 189 | const char* ops[] = { | ||
| 190 | " || ", " && ", "", "" | ||
| 191 | }; | ||
| 192 | if (instr.flow_control.op != instr.flow_control.JustY) | ||
| 193 | output << ((!instr.flow_control.refx) ? "!" : " ") << "cc.x"; | ||
| 194 | |||
| 195 | output << ops[instr.flow_control.op]; | ||
| 196 | |||
| 197 | if (instr.flow_control.op != instr.flow_control.JustX) | ||
| 198 | output << ((!instr.flow_control.refy) ? "!" : " ") << "cc.y"; | ||
| 199 | |||
| 200 | output << " "; | ||
| 201 | } else if (instr.opcode.GetInfo().subtype & Instruction::OpCodeInfo::HasUniformIndex) { | ||
| 202 | output << "b" << instr.flow_control.bool_uniform_id << " "; | ||
| 203 | } | ||
| 204 | |||
| 205 | u32 target_addr = instr.flow_control.dest_offset; | ||
| 206 | u32 target_addr_else = instr.flow_control.dest_offset; | ||
| 207 | |||
| 208 | if (instr.opcode.GetInfo().subtype & Instruction::OpCodeInfo::HasAlternative) { | ||
| 209 | output << "else jump to 0x" << std::setw(4) << std::right << std::setfill('0') << 4 * instr.flow_control.dest_offset << " "; | ||
| 210 | } else if (instr.opcode.GetInfo().subtype & Instruction::OpCodeInfo::HasExplicitDest) { | ||
| 211 | output << "jump to 0x" << std::setw(4) << std::right << std::setfill('0') << 4 * instr.flow_control.dest_offset << " "; | ||
| 212 | } else { | ||
| 213 | // TODO: Handle other cases | ||
| 214 | } | ||
| 215 | |||
| 216 | if (instr.opcode.GetInfo().subtype & Instruction::OpCodeInfo::HasFinishPoint) { | ||
| 217 | output << "(return on " << std::setw(4) << std::right << std::setfill('0') | ||
| 218 | << 4 * instr.flow_control.dest_offset + 4 * instr.flow_control.num_instructions << ")"; | ||
| 219 | } | ||
| 220 | |||
| 221 | break; | ||
| 222 | } | ||
| 223 | break; | ||
| 224 | } | ||
| 225 | |||
| 226 | default: | ||
| 227 | output << "(unknown instruction format)"; | ||
| 228 | break; | ||
| 229 | } | ||
| 230 | |||
| 231 | return QString::fromLatin1(output.str().c_str()); | ||
| 232 | } | ||
| 233 | |||
| 234 | default: | ||
| 235 | break; | ||
| 236 | } | ||
| 237 | } | ||
| 238 | |||
| 239 | case Qt::FontRole: | ||
| 240 | return QFont("monospace"); | ||
| 241 | |||
| 242 | default: | ||
| 243 | break; | ||
| 244 | } | ||
| 245 | |||
| 246 | return QVariant(); | ||
| 247 | } | ||
| 248 | |||
| 249 | void GraphicsVertexShaderModel::OnUpdate() | ||
| 250 | { | ||
| 251 | beginResetModel(); | ||
| 252 | |||
| 253 | info.Clear(); | ||
| 254 | |||
| 255 | for (auto instr : Pica::VertexShader::GetShaderBinary()) | ||
| 256 | info.code.push_back({instr}); | ||
| 257 | |||
| 258 | for (auto pattern : Pica::VertexShader::GetSwizzlePatterns()) | ||
| 259 | info.swizzle_info.push_back({pattern}); | ||
| 260 | |||
| 261 | info.labels.insert({Pica::registers.vs_main_offset, "main"}); | ||
| 262 | |||
| 263 | endResetModel(); | ||
| 264 | } | ||
| 265 | |||
| 266 | |||
| 267 | GraphicsVertexShaderWidget::GraphicsVertexShaderWidget(std::shared_ptr< Pica::DebugContext > debug_context, | ||
| 268 | QWidget* parent) | ||
| 269 | : BreakPointObserverDock(debug_context, "Pica Vertex Shader", parent) { | ||
| 270 | setObjectName("PicaVertexShader"); | ||
| 271 | |||
| 272 | auto binary_model = new GraphicsVertexShaderModel(this); | ||
| 273 | auto binary_list = new QTreeView; | ||
| 274 | binary_list->setModel(binary_model); | ||
| 275 | binary_list->setRootIsDecorated(false); | ||
| 276 | binary_list->setAlternatingRowColors(true); | ||
| 277 | |||
| 278 | connect(this, SIGNAL(Update()), binary_model, SLOT(OnUpdate())); | ||
| 279 | |||
| 280 | auto main_widget = new QWidget; | ||
| 281 | auto main_layout = new QVBoxLayout; | ||
| 282 | { | ||
| 283 | auto sub_layout = new QHBoxLayout; | ||
| 284 | sub_layout->addWidget(binary_list); | ||
| 285 | main_layout->addLayout(sub_layout); | ||
| 286 | } | ||
| 287 | main_widget->setLayout(main_layout); | ||
| 288 | setWidget(main_widget); | ||
| 289 | } | ||
| 290 | |||
| 291 | void GraphicsVertexShaderWidget::OnBreakPointHit(Pica::DebugContext::Event event, void* data) { | ||
| 292 | emit Update(); | ||
| 293 | widget()->setEnabled(true); | ||
| 294 | } | ||
| 295 | |||
| 296 | void GraphicsVertexShaderWidget::OnResumed() { | ||
| 297 | widget()->setEnabled(false); | ||
| 298 | } | ||
diff --git a/src/citra_qt/debugger/graphics_vertex_shader.h b/src/citra_qt/debugger/graphics_vertex_shader.h new file mode 100644 index 000000000..38339dc05 --- /dev/null +++ b/src/citra_qt/debugger/graphics_vertex_shader.h | |||
| @@ -0,0 +1,51 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <QAbstractListModel> | ||
| 8 | |||
| 9 | #include "graphics_breakpoint_observer.h" | ||
| 10 | |||
| 11 | #include "nihstro/parser_shbin.h" | ||
| 12 | |||
| 13 | class GraphicsVertexShaderModel : public QAbstractItemModel { | ||
| 14 | Q_OBJECT | ||
| 15 | |||
| 16 | public: | ||
| 17 | GraphicsVertexShaderModel(QObject* parent); | ||
| 18 | |||
| 19 | QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override; | ||
| 20 | QModelIndex parent(const QModelIndex& child) const override; | ||
| 21 | int columnCount(const QModelIndex& parent = QModelIndex()) const override; | ||
| 22 | int rowCount(const QModelIndex& parent = QModelIndex()) const override; | ||
| 23 | QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; | ||
| 24 | QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; | ||
| 25 | |||
| 26 | public slots: | ||
| 27 | void OnUpdate(); | ||
| 28 | |||
| 29 | private: | ||
| 30 | nihstro::ShaderInfo info; | ||
| 31 | }; | ||
| 32 | |||
| 33 | class GraphicsVertexShaderWidget : public BreakPointObserverDock { | ||
| 34 | Q_OBJECT | ||
| 35 | |||
| 36 | using Event = Pica::DebugContext::Event; | ||
| 37 | |||
| 38 | public: | ||
| 39 | GraphicsVertexShaderWidget(std::shared_ptr<Pica::DebugContext> debug_context, | ||
| 40 | QWidget* parent = nullptr); | ||
| 41 | |||
| 42 | private slots: | ||
| 43 | void OnBreakPointHit(Pica::DebugContext::Event event, void* data) override; | ||
| 44 | void OnResumed() override; | ||
| 45 | |||
| 46 | signals: | ||
| 47 | void Update(); | ||
| 48 | |||
| 49 | private: | ||
| 50 | |||
| 51 | }; | ||
diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp index 653ffec75..881c7d337 100644 --- a/src/citra_qt/main.cpp +++ b/src/citra_qt/main.cpp | |||
| @@ -34,6 +34,7 @@ | |||
| 34 | #include "debugger/graphics_breakpoints.h" | 34 | #include "debugger/graphics_breakpoints.h" |
| 35 | #include "debugger/graphics_cmdlists.h" | 35 | #include "debugger/graphics_cmdlists.h" |
| 36 | #include "debugger/graphics_framebuffer.h" | 36 | #include "debugger/graphics_framebuffer.h" |
| 37 | #include "debugger/graphics_vertex_shader.h" | ||
| 37 | 38 | ||
| 38 | #include "core/settings.h" | 39 | #include "core/settings.h" |
| 39 | #include "core/system.h" | 40 | #include "core/system.h" |
| @@ -84,6 +85,10 @@ GMainWindow::GMainWindow() | |||
| 84 | addDockWidget(Qt::RightDockWidgetArea, graphicsFramebufferWidget); | 85 | addDockWidget(Qt::RightDockWidgetArea, graphicsFramebufferWidget); |
| 85 | graphicsFramebufferWidget->hide(); | 86 | graphicsFramebufferWidget->hide(); |
| 86 | 87 | ||
| 88 | auto graphicsVertexShaderWidget = new GraphicsVertexShaderWidget(Pica::g_debug_context, this); | ||
| 89 | addDockWidget(Qt::RightDockWidgetArea, graphicsVertexShaderWidget); | ||
| 90 | graphicsVertexShaderWidget->hide(); | ||
| 91 | |||
| 87 | QMenu* debug_menu = ui.menu_View->addMenu(tr("Debugging")); | 92 | QMenu* debug_menu = ui.menu_View->addMenu(tr("Debugging")); |
| 88 | debug_menu->addAction(disasmWidget->toggleViewAction()); | 93 | debug_menu->addAction(disasmWidget->toggleViewAction()); |
| 89 | debug_menu->addAction(registersWidget->toggleViewAction()); | 94 | debug_menu->addAction(registersWidget->toggleViewAction()); |
| @@ -92,6 +97,7 @@ GMainWindow::GMainWindow() | |||
| 92 | debug_menu->addAction(graphicsCommandsWidget->toggleViewAction()); | 97 | debug_menu->addAction(graphicsCommandsWidget->toggleViewAction()); |
| 93 | debug_menu->addAction(graphicsBreakpointsWidget->toggleViewAction()); | 98 | debug_menu->addAction(graphicsBreakpointsWidget->toggleViewAction()); |
| 94 | debug_menu->addAction(graphicsFramebufferWidget->toggleViewAction()); | 99 | debug_menu->addAction(graphicsFramebufferWidget->toggleViewAction()); |
| 100 | debug_menu->addAction(graphicsVertexShaderWidget->toggleViewAction()); | ||
| 95 | 101 | ||
| 96 | // Set default UI state | 102 | // Set default UI state |
| 97 | // geometry: 55% of the window contents are in the upper screen half, 45% in the lower half | 103 | // geometry: 55% of the window contents are in the upper screen half, 45% in the lower half |