Merge pull request #384 from neobrain/vertex_shader_debugger
Vertex shader debugger
This commit is contained in:
		| @@ -8,9 +8,11 @@ set(SRCS | ||||
|             debugger/callstack.cpp | ||||
|             debugger/disassembler.cpp | ||||
|             debugger/graphics.cpp | ||||
|             debugger/graphics_breakpoint_observer.cpp | ||||
|             debugger/graphics_breakpoints.cpp | ||||
|             debugger/graphics_cmdlists.cpp | ||||
|             debugger/graphics_framebuffer.cpp | ||||
|             debugger/graphics_vertex_shader.cpp | ||||
|             debugger/ramview.cpp | ||||
|             debugger/registers.cpp | ||||
|             util/spinbox.cpp | ||||
| @@ -27,10 +29,12 @@ set(HEADERS | ||||
|             debugger/callstack.h | ||||
|             debugger/disassembler.h | ||||
|             debugger/graphics.h | ||||
|             debugger/graphics_breakpoint_observer.h | ||||
|             debugger/graphics_breakpoints.h | ||||
|             debugger/graphics_breakpoints_p.h | ||||
|             debugger/graphics_cmdlists.h | ||||
|             debugger/graphics_framebuffer.h | ||||
|             debugger/graphics_vertex_shader.h | ||||
|             debugger/ramview.h | ||||
|             debugger/registers.h | ||||
|             util/spinbox.h | ||||
|   | ||||
							
								
								
									
										32
									
								
								src/citra_qt/debugger/graphics_breakpoint_observer.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								src/citra_qt/debugger/graphics_breakpoint_observer.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | ||||
| // Copyright 2014 Citra Emulator Project | ||||
| // Licensed under GPLv2 or any later version | ||||
| // Refer to the license.txt file included. | ||||
|  | ||||
| #include <QMetaType> | ||||
|  | ||||
| #include "graphics_breakpoint_observer.h" | ||||
|  | ||||
| BreakPointObserverDock::BreakPointObserverDock(std::shared_ptr<Pica::DebugContext> debug_context, | ||||
|                                                const QString& title, QWidget* parent) | ||||
|     : QDockWidget(title, parent), BreakPointObserver(debug_context) | ||||
| { | ||||
|     qRegisterMetaType<Pica::DebugContext::Event>("Pica::DebugContext::Event"); | ||||
|  | ||||
|     connect(this, SIGNAL(Resumed()), this, SLOT(OnResumed())); | ||||
|  | ||||
|     // NOTE: This signal is emitted from a non-GUI thread, but connect() takes | ||||
|     //       care of delaying its handling to the GUI thread. | ||||
|     connect(this, SIGNAL(BreakPointHit(Pica::DebugContext::Event,void*)), | ||||
|             this, SLOT(OnBreakPointHit(Pica::DebugContext::Event,void*)), | ||||
|             Qt::BlockingQueuedConnection); | ||||
| } | ||||
|  | ||||
| void BreakPointObserverDock::OnPicaBreakPointHit(Pica::DebugContext::Event event, void* data) | ||||
| { | ||||
|     emit BreakPointHit(event, data); | ||||
| } | ||||
|  | ||||
| void BreakPointObserverDock::OnPicaResume() | ||||
| { | ||||
|     emit Resumed(); | ||||
| } | ||||
							
								
								
									
										33
									
								
								src/citra_qt/debugger/graphics_breakpoint_observer.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								src/citra_qt/debugger/graphics_breakpoint_observer.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | ||||
| // Copyright 2014 Citra Emulator Project | ||||
| // Licensed under GPLv2 or any later version | ||||
| // Refer to the license.txt file included. | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <QDockWidget> | ||||
|  | ||||
| #include "video_core/debug_utils/debug_utils.h" | ||||
|  | ||||
| /** | ||||
|  * Utility class which forwards calls to OnPicaBreakPointHit and OnPicaResume to public slots. | ||||
|  * This is because the Pica breakpoint callbacks are called from a non-GUI thread, while | ||||
|  * the widget usually wants to perform reactions in the GUI thread. | ||||
|  */ | ||||
| class BreakPointObserverDock : public QDockWidget, private Pica::DebugContext::BreakPointObserver { | ||||
|     Q_OBJECT | ||||
|  | ||||
| public: | ||||
|     BreakPointObserverDock(std::shared_ptr<Pica::DebugContext> debug_context, const QString& title, | ||||
|                            QWidget* parent = nullptr); | ||||
|  | ||||
|     void OnPicaBreakPointHit(Pica::DebugContext::Event event, void* data) override; | ||||
|     void OnPicaResume() override; | ||||
|  | ||||
| private slots: | ||||
|     virtual void OnBreakPointHit(Pica::DebugContext::Event event, void* data) = 0; | ||||
|     virtual void OnResumed() = 0; | ||||
|  | ||||
| signals: | ||||
|     void Resumed(); | ||||
|     void BreakPointHit(Pica::DebugContext::Event event, void* data); | ||||
| }; | ||||
| @@ -6,7 +6,6 @@ | ||||
| #include <QComboBox> | ||||
| #include <QDebug> | ||||
| #include <QLabel> | ||||
| #include <QMetaType> | ||||
| #include <QPushButton> | ||||
| #include <QSpinBox> | ||||
|  | ||||
| @@ -17,32 +16,6 @@ | ||||
|  | ||||
| #include "util/spinbox.h" | ||||
|  | ||||
| BreakPointObserverDock::BreakPointObserverDock(std::shared_ptr<Pica::DebugContext> debug_context, | ||||
|                                                const QString& title, QWidget* parent) | ||||
|     : QDockWidget(title, parent), BreakPointObserver(debug_context) | ||||
| { | ||||
|     qRegisterMetaType<Pica::DebugContext::Event>("Pica::DebugContext::Event"); | ||||
|  | ||||
|     connect(this, SIGNAL(Resumed()), this, SLOT(OnResumed())); | ||||
|  | ||||
|     // NOTE: This signal is emitted from a non-GUI thread, but connect() takes | ||||
|     //       care of delaying its handling to the GUI thread. | ||||
|     connect(this, SIGNAL(BreakPointHit(Pica::DebugContext::Event,void*)), | ||||
|             this, SLOT(OnBreakPointHit(Pica::DebugContext::Event,void*)), | ||||
|             Qt::BlockingQueuedConnection); | ||||
| } | ||||
|  | ||||
| void BreakPointObserverDock::OnPicaBreakPointHit(Pica::DebugContext::Event event, void* data) | ||||
| { | ||||
|     emit BreakPointHit(event, data); | ||||
| } | ||||
|  | ||||
| void BreakPointObserverDock::OnPicaResume() | ||||
| { | ||||
|     emit Resumed(); | ||||
| } | ||||
|  | ||||
|  | ||||
| GraphicsFramebufferWidget::GraphicsFramebufferWidget(std::shared_ptr<Pica::DebugContext> debug_context, | ||||
|                                                      QWidget* parent) | ||||
|     : BreakPointObserverDock(debug_context, tr("Pica Framebuffer"), parent), | ||||
|   | ||||
| @@ -6,7 +6,7 @@ | ||||
|  | ||||
| #include <QDockWidget> | ||||
|  | ||||
| #include "video_core/debug_utils/debug_utils.h" | ||||
| #include "graphics_breakpoint_observer.h" | ||||
|  | ||||
| class QComboBox; | ||||
| class QLabel; | ||||
| @@ -14,28 +14,6 @@ class QSpinBox; | ||||
|  | ||||
| class CSpinBox; | ||||
|  | ||||
| // Utility class which forwards calls to OnPicaBreakPointHit and OnPicaResume to public slots. | ||||
| // This is because the Pica breakpoint callbacks are called from a non-GUI thread, while | ||||
| // the widget usually wants to perform reactions in the GUI thread. | ||||
| class BreakPointObserverDock : public QDockWidget, Pica::DebugContext::BreakPointObserver { | ||||
|     Q_OBJECT | ||||
|  | ||||
| public: | ||||
|     BreakPointObserverDock(std::shared_ptr<Pica::DebugContext> debug_context, const QString& title, | ||||
|                            QWidget* parent = nullptr); | ||||
|  | ||||
|     void OnPicaBreakPointHit(Pica::DebugContext::Event event, void* data) override; | ||||
|     void OnPicaResume() override; | ||||
|  | ||||
| private slots: | ||||
|     virtual void OnBreakPointHit(Pica::DebugContext::Event event, void* data) = 0; | ||||
|     virtual void OnResumed() = 0; | ||||
|  | ||||
| signals: | ||||
|     void Resumed(); | ||||
|     void BreakPointHit(Pica::DebugContext::Event event, void* data); | ||||
| }; | ||||
|  | ||||
| class GraphicsFramebufferWidget : public BreakPointObserverDock { | ||||
|     Q_OBJECT | ||||
|  | ||||
|   | ||||
							
								
								
									
										298
									
								
								src/citra_qt/debugger/graphics_vertex_shader.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										298
									
								
								src/citra_qt/debugger/graphics_vertex_shader.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,298 @@ | ||||
| // Copyright 2014 Citra Emulator Project | ||||
| // Licensed under GPLv2 or any later version | ||||
| // Refer to the license.txt file included. | ||||
|  | ||||
| #include <iomanip> | ||||
| #include <sstream> | ||||
|  | ||||
| #include <QBoxLayout> | ||||
| #include <QTreeView> | ||||
|  | ||||
| #include "video_core/vertex_shader.h" | ||||
|  | ||||
| #include "graphics_vertex_shader.h" | ||||
|  | ||||
| using nihstro::Instruction; | ||||
| using nihstro::SourceRegister; | ||||
| using nihstro::SwizzlePattern; | ||||
|  | ||||
| GraphicsVertexShaderModel::GraphicsVertexShaderModel(QObject* parent): QAbstractItemModel(parent) { | ||||
|  | ||||
| } | ||||
|  | ||||
| QModelIndex GraphicsVertexShaderModel::index(int row, int column, const QModelIndex& parent) const { | ||||
|     return createIndex(row, column); | ||||
| } | ||||
|  | ||||
| QModelIndex GraphicsVertexShaderModel::parent(const QModelIndex& child) const { | ||||
|     return QModelIndex(); | ||||
| } | ||||
|  | ||||
| int GraphicsVertexShaderModel::columnCount(const QModelIndex& parent) const { | ||||
|     return 3; | ||||
| } | ||||
|  | ||||
| int GraphicsVertexShaderModel::rowCount(const QModelIndex& parent) const { | ||||
|     return info.code.size(); | ||||
| } | ||||
|  | ||||
| QVariant GraphicsVertexShaderModel::headerData(int section, Qt::Orientation orientation, int role) const { | ||||
|     switch(role) { | ||||
|     case Qt::DisplayRole: | ||||
|     { | ||||
|         if (section == 0) { | ||||
|             return tr("Offset"); | ||||
|         } else if (section == 1) { | ||||
|             return tr("Raw"); | ||||
|         } else if (section == 2) { | ||||
|             return tr("Disassembly"); | ||||
|         } | ||||
|  | ||||
|         break; | ||||
|     } | ||||
|     } | ||||
|  | ||||
|     return QVariant(); | ||||
| } | ||||
|  | ||||
| QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) const { | ||||
|     switch (role) { | ||||
|     case Qt::DisplayRole: | ||||
|     { | ||||
|         switch (index.column()) { | ||||
|         case 0: | ||||
|             if (info.HasLabel(index.row())) | ||||
|                 return QString::fromStdString(info.GetLabel(index.row())); | ||||
|  | ||||
|             return QString("%1").arg(4*index.row(), 4, 16, QLatin1Char('0')); | ||||
|  | ||||
|         case 1: | ||||
|             return QString("%1").arg(info.code[index.row()].hex, 8, 16, QLatin1Char('0')); | ||||
|  | ||||
|         case 2: | ||||
|         { | ||||
|             std::stringstream output; | ||||
|             output.flags(std::ios::hex); | ||||
|  | ||||
|             Instruction instr = info.code[index.row()]; | ||||
|             const SwizzlePattern& swizzle = info.swizzle_info[instr.common.operand_desc_id].pattern; | ||||
|  | ||||
|             // longest known instruction name: "setemit " | ||||
|             output << std::setw(8) << std::left << instr.opcode.GetInfo().name; | ||||
|  | ||||
|             // e.g. "-c92.xyzw" | ||||
|             static auto print_input = [](std::stringstream& output, const SourceRegister& input, | ||||
|                                          bool negate, const std::string& swizzle_mask) { | ||||
|                 output << std::setw(4) << std::right << (negate ? "-" : "") + input.GetName(); | ||||
|                 output << "." << swizzle_mask; | ||||
|             }; | ||||
|  | ||||
|             // e.g. "-c92[a0.x].xyzw" | ||||
|             static auto print_input_indexed = [](std::stringstream& output, const SourceRegister& input, | ||||
|                                                  bool negate, const std::string& swizzle_mask, | ||||
|                                                  const std::string& address_register_name) { | ||||
|                 std::string relative_address; | ||||
|                 if (!address_register_name.empty()) | ||||
|                     relative_address = "[" + address_register_name + "]"; | ||||
|  | ||||
|                 output << std::setw(10) << std::right << (negate ? "-" : "") + input.GetName() + relative_address; | ||||
|                 output << "." << swizzle_mask; | ||||
|             }; | ||||
|  | ||||
|             // Use print_input or print_input_indexed depending on whether relative addressing is used or not. | ||||
|             static auto print_input_indexed_compact = [](std::stringstream& output, const SourceRegister& input, | ||||
|                                                          bool negate, const std::string& swizzle_mask, | ||||
|                                                          const std::string& address_register_name) { | ||||
|                 if (address_register_name.empty()) | ||||
|                     print_input(output, input, negate, swizzle_mask); | ||||
|                 else | ||||
|                     print_input_indexed(output, input, negate, swizzle_mask, address_register_name); | ||||
|             }; | ||||
|  | ||||
|             switch (instr.opcode.GetInfo().type) { | ||||
|             case Instruction::OpCodeType::Trivial: | ||||
|                 // Nothing to do here | ||||
|                 break; | ||||
|  | ||||
|             case Instruction::OpCodeType::Arithmetic: | ||||
|             { | ||||
|                 // Use custom code for special instructions | ||||
|                 switch (instr.opcode.EffectiveOpCode()) { | ||||
|                 case Instruction::OpCode::CMP: | ||||
|                 { | ||||
|                     // NOTE: CMP always writes both cc components, so we do not consider the dest mask here. | ||||
|                     output << std::setw(4) << std::right << "cc."; | ||||
|                     output << "xy    "; | ||||
|  | ||||
|                     SourceRegister src1 = instr.common.GetSrc1(false); | ||||
|                     SourceRegister src2 = instr.common.GetSrc2(false); | ||||
|  | ||||
|                     print_input_indexed_compact(output, src1, swizzle.negate_src1, swizzle.SelectorToString(false).substr(0,1), instr.common.AddressRegisterName()); | ||||
|                     output << " " << instr.common.compare_op.ToString(instr.common.compare_op.x) << " "; | ||||
|                     print_input(output, src2, swizzle.negate_src2, swizzle.SelectorToString(false).substr(0,1)); | ||||
|  | ||||
|                     output << ", "; | ||||
|  | ||||
|                     print_input_indexed_compact(output, src1, swizzle.negate_src1, swizzle.SelectorToString(false).substr(1,1), instr.common.AddressRegisterName()); | ||||
|                     output << " " << instr.common.compare_op.ToString(instr.common.compare_op.y) << " "; | ||||
|                     print_input(output, src2, swizzle.negate_src2, swizzle.SelectorToString(false).substr(1,1)); | ||||
|  | ||||
|                     break; | ||||
|                 } | ||||
|  | ||||
|                 default: | ||||
|                 { | ||||
|                     bool src_is_inverted = 0 != (instr.opcode.GetInfo().subtype & Instruction::OpCodeInfo::SrcInversed); | ||||
|  | ||||
|                     if (instr.opcode.GetInfo().subtype & Instruction::OpCodeInfo::Dest) { | ||||
|                         // e.g. "r12.xy__" | ||||
|                         output << std::setw(4) << std::right << instr.common.dest.GetName() + "."; | ||||
|                         output << swizzle.DestMaskToString(); | ||||
|                     } else if (instr.opcode.GetInfo().subtype == Instruction::OpCodeInfo::MOVA) { | ||||
|                         output << std::setw(4) << std::right << "a0."; | ||||
|                         output << swizzle.DestMaskToString(); | ||||
|                     } else { | ||||
|                         output << "        "; | ||||
|                     } | ||||
|                     output << "  "; | ||||
|  | ||||
|                     if (instr.opcode.GetInfo().subtype & Instruction::OpCodeInfo::Src1) { | ||||
|                         SourceRegister src1 = instr.common.GetSrc1(src_is_inverted); | ||||
|                         print_input_indexed(output, src1, swizzle.negate_src1, swizzle.SelectorToString(false), instr.common.AddressRegisterName()); | ||||
|                     } else { | ||||
|                         output << "               "; | ||||
|                     } | ||||
|  | ||||
|                     // TODO: In some cases, the Address Register is used as an index for SRC2 instead of SRC1 | ||||
|                     if (instr.opcode.GetInfo().subtype & Instruction::OpCodeInfo::Src2) { | ||||
|                         SourceRegister src2 = instr.common.GetSrc2(src_is_inverted); | ||||
|                         print_input(output, src2, swizzle.negate_src2, swizzle.SelectorToString(false)); | ||||
|                     } | ||||
|                     break; | ||||
|                 } | ||||
|                 } | ||||
|  | ||||
|                 break; | ||||
|             } | ||||
|  | ||||
|             case Instruction::OpCodeType::Conditional: | ||||
|             { | ||||
|                 switch (instr.opcode.EffectiveOpCode()) { | ||||
|                 case Instruction::OpCode::LOOP: | ||||
|                     output << "(unknown instruction format)"; | ||||
|                     break; | ||||
|  | ||||
|                 default: | ||||
|                     output << "if "; | ||||
|  | ||||
|                     if (instr.opcode.GetInfo().subtype & Instruction::OpCodeInfo::HasCondition) { | ||||
|                         const char* ops[] = { | ||||
|                             " || ", " && ", "", "" | ||||
|                         }; | ||||
|                         if (instr.flow_control.op != instr.flow_control.JustY) | ||||
|                             output << ((!instr.flow_control.refx) ? "!" : " ") << "cc.x"; | ||||
|  | ||||
|                         output << ops[instr.flow_control.op]; | ||||
|  | ||||
|                         if (instr.flow_control.op != instr.flow_control.JustX) | ||||
|                             output << ((!instr.flow_control.refy) ? "!" : " ") << "cc.y"; | ||||
|  | ||||
|                         output << " "; | ||||
|                     } else if (instr.opcode.GetInfo().subtype & Instruction::OpCodeInfo::HasUniformIndex) { | ||||
|                         output << "b" << instr.flow_control.bool_uniform_id << " "; | ||||
|                     } | ||||
|  | ||||
|                     u32 target_addr = instr.flow_control.dest_offset; | ||||
|                     u32 target_addr_else = instr.flow_control.dest_offset; | ||||
|  | ||||
|                     if (instr.opcode.GetInfo().subtype & Instruction::OpCodeInfo::HasAlternative) { | ||||
|                         output << "else jump to 0x" << std::setw(4) << std::right << std::setfill('0') << 4 * instr.flow_control.dest_offset << " "; | ||||
|                     } else if (instr.opcode.GetInfo().subtype & Instruction::OpCodeInfo::HasExplicitDest) { | ||||
|                         output << "jump to 0x" << std::setw(4) << std::right << std::setfill('0') << 4 * instr.flow_control.dest_offset << " "; | ||||
|                     } else { | ||||
|                         // TODO: Handle other cases | ||||
|                     } | ||||
|  | ||||
|                     if (instr.opcode.GetInfo().subtype & Instruction::OpCodeInfo::HasFinishPoint) { | ||||
|                         output << "(return on " << std::setw(4) << std::right << std::setfill('0') | ||||
|                                << 4 * instr.flow_control.dest_offset + 4 * instr.flow_control.num_instructions << ")"; | ||||
|                     } | ||||
|  | ||||
|                     break; | ||||
|                 } | ||||
|                 break; | ||||
|             } | ||||
|  | ||||
|             default: | ||||
|                 output << "(unknown instruction format)"; | ||||
|                 break; | ||||
|             } | ||||
|  | ||||
|             return QString::fromLatin1(output.str().c_str()); | ||||
|         } | ||||
|  | ||||
|         default: | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     case Qt::FontRole: | ||||
|         return QFont("monospace"); | ||||
|  | ||||
|     default: | ||||
|         break; | ||||
|     } | ||||
|  | ||||
|     return QVariant(); | ||||
| } | ||||
|  | ||||
| void GraphicsVertexShaderModel::OnUpdate() | ||||
| { | ||||
|     beginResetModel(); | ||||
|  | ||||
|     info.Clear(); | ||||
|  | ||||
|     for (auto instr : Pica::VertexShader::GetShaderBinary()) | ||||
|         info.code.push_back({instr}); | ||||
|  | ||||
|     for (auto pattern : Pica::VertexShader::GetSwizzlePatterns()) | ||||
|         info.swizzle_info.push_back({pattern}); | ||||
|  | ||||
|     info.labels.insert({Pica::registers.vs_main_offset, "main"}); | ||||
|  | ||||
|     endResetModel(); | ||||
| } | ||||
|  | ||||
|  | ||||
| GraphicsVertexShaderWidget::GraphicsVertexShaderWidget(std::shared_ptr< Pica::DebugContext > debug_context, | ||||
|                                                        QWidget* parent) | ||||
|         : BreakPointObserverDock(debug_context, "Pica Vertex Shader", parent) { | ||||
|     setObjectName("PicaVertexShader"); | ||||
|  | ||||
|     auto binary_model = new GraphicsVertexShaderModel(this); | ||||
|     auto binary_list = new QTreeView; | ||||
|     binary_list->setModel(binary_model); | ||||
|     binary_list->setRootIsDecorated(false); | ||||
|     binary_list->setAlternatingRowColors(true); | ||||
|  | ||||
|     connect(this, SIGNAL(Update()), binary_model, SLOT(OnUpdate())); | ||||
|  | ||||
|     auto main_widget = new QWidget; | ||||
|     auto main_layout = new QVBoxLayout; | ||||
|     { | ||||
|         auto sub_layout = new QHBoxLayout; | ||||
|         sub_layout->addWidget(binary_list); | ||||
|         main_layout->addLayout(sub_layout); | ||||
|     } | ||||
|     main_widget->setLayout(main_layout); | ||||
|     setWidget(main_widget); | ||||
| } | ||||
|  | ||||
| void GraphicsVertexShaderWidget::OnBreakPointHit(Pica::DebugContext::Event event, void* data) { | ||||
|     emit Update(); | ||||
|     widget()->setEnabled(true); | ||||
| } | ||||
|  | ||||
| void GraphicsVertexShaderWidget::OnResumed() { | ||||
|     widget()->setEnabled(false); | ||||
| } | ||||
							
								
								
									
										51
									
								
								src/citra_qt/debugger/graphics_vertex_shader.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								src/citra_qt/debugger/graphics_vertex_shader.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,51 @@ | ||||
| // Copyright 2014 Citra Emulator Project | ||||
| // Licensed under GPLv2 or any later version | ||||
| // Refer to the license.txt file included. | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <QAbstractListModel> | ||||
|  | ||||
| #include "graphics_breakpoint_observer.h" | ||||
|  | ||||
| #include "nihstro/parser_shbin.h" | ||||
|  | ||||
| class GraphicsVertexShaderModel : public QAbstractItemModel { | ||||
|     Q_OBJECT | ||||
|  | ||||
| public: | ||||
|     GraphicsVertexShaderModel(QObject* parent); | ||||
|  | ||||
|     QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override; | ||||
|     QModelIndex parent(const QModelIndex& child) const override; | ||||
|     int columnCount(const QModelIndex& parent = QModelIndex()) const override; | ||||
|     int rowCount(const QModelIndex& parent = QModelIndex()) const override; | ||||
|     QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; | ||||
|     QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; | ||||
|  | ||||
| public slots: | ||||
|     void OnUpdate(); | ||||
|  | ||||
| private: | ||||
|     nihstro::ShaderInfo info; | ||||
| }; | ||||
|  | ||||
| class GraphicsVertexShaderWidget : public BreakPointObserverDock { | ||||
|     Q_OBJECT | ||||
|  | ||||
|     using Event = Pica::DebugContext::Event; | ||||
|  | ||||
| public: | ||||
|     GraphicsVertexShaderWidget(std::shared_ptr<Pica::DebugContext> debug_context, | ||||
|                                QWidget* parent = nullptr); | ||||
|  | ||||
| private slots: | ||||
|     void OnBreakPointHit(Pica::DebugContext::Event event, void* data) override; | ||||
|     void OnResumed() override; | ||||
|  | ||||
| signals: | ||||
|     void Update(); | ||||
|  | ||||
| private: | ||||
|  | ||||
| }; | ||||
| @@ -34,6 +34,7 @@ | ||||
| #include "debugger/graphics_breakpoints.h" | ||||
| #include "debugger/graphics_cmdlists.h" | ||||
| #include "debugger/graphics_framebuffer.h" | ||||
| #include "debugger/graphics_vertex_shader.h" | ||||
|  | ||||
| #include "core/settings.h" | ||||
| #include "core/system.h" | ||||
| @@ -84,6 +85,10 @@ GMainWindow::GMainWindow() | ||||
|     addDockWidget(Qt::RightDockWidgetArea, graphicsFramebufferWidget); | ||||
|     graphicsFramebufferWidget->hide(); | ||||
|  | ||||
|     auto graphicsVertexShaderWidget = new GraphicsVertexShaderWidget(Pica::g_debug_context, this); | ||||
|     addDockWidget(Qt::RightDockWidgetArea, graphicsVertexShaderWidget); | ||||
|     graphicsVertexShaderWidget->hide(); | ||||
|  | ||||
|     QMenu* debug_menu = ui.menu_View->addMenu(tr("Debugging")); | ||||
|     debug_menu->addAction(disasmWidget->toggleViewAction()); | ||||
|     debug_menu->addAction(registersWidget->toggleViewAction()); | ||||
| @@ -92,6 +97,7 @@ GMainWindow::GMainWindow() | ||||
|     debug_menu->addAction(graphicsCommandsWidget->toggleViewAction()); | ||||
|     debug_menu->addAction(graphicsBreakpointsWidget->toggleViewAction()); | ||||
|     debug_menu->addAction(graphicsFramebufferWidget->toggleViewAction()); | ||||
|     debug_menu->addAction(graphicsVertexShaderWidget->toggleViewAction()); | ||||
|  | ||||
|     // Set default UI state | ||||
|     // geometry: 55% of the window contents are in the upper screen half, 45% in the lower half | ||||
|   | ||||
		Reference in New Issue
	
	Block a user