forked from eden-emu/eden
		
	Merge pull request #384 from neobrain/vertex_shader_debugger
Vertex shader debugger
This commit is contained in:
		
						commit
						f990728ad4
					
				
					 8 changed files with 425 additions and 50 deletions
				
			
		|  | @ -8,9 +8,11 @@ set(SRCS | ||||||
|             debugger/callstack.cpp |             debugger/callstack.cpp | ||||||
|             debugger/disassembler.cpp |             debugger/disassembler.cpp | ||||||
|             debugger/graphics.cpp |             debugger/graphics.cpp | ||||||
|  |             debugger/graphics_breakpoint_observer.cpp | ||||||
|             debugger/graphics_breakpoints.cpp |             debugger/graphics_breakpoints.cpp | ||||||
|             debugger/graphics_cmdlists.cpp |             debugger/graphics_cmdlists.cpp | ||||||
|             debugger/graphics_framebuffer.cpp |             debugger/graphics_framebuffer.cpp | ||||||
|  |             debugger/graphics_vertex_shader.cpp | ||||||
|             debugger/ramview.cpp |             debugger/ramview.cpp | ||||||
|             debugger/registers.cpp |             debugger/registers.cpp | ||||||
|             util/spinbox.cpp |             util/spinbox.cpp | ||||||
|  | @ -27,10 +29,12 @@ set(HEADERS | ||||||
|             debugger/callstack.h |             debugger/callstack.h | ||||||
|             debugger/disassembler.h |             debugger/disassembler.h | ||||||
|             debugger/graphics.h |             debugger/graphics.h | ||||||
|  |             debugger/graphics_breakpoint_observer.h | ||||||
|             debugger/graphics_breakpoints.h |             debugger/graphics_breakpoints.h | ||||||
|             debugger/graphics_breakpoints_p.h |             debugger/graphics_breakpoints_p.h | ||||||
|             debugger/graphics_cmdlists.h |             debugger/graphics_cmdlists.h | ||||||
|             debugger/graphics_framebuffer.h |             debugger/graphics_framebuffer.h | ||||||
|  |             debugger/graphics_vertex_shader.h | ||||||
|             debugger/ramview.h |             debugger/ramview.h | ||||||
|             debugger/registers.h |             debugger/registers.h | ||||||
|             util/spinbox.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 <QComboBox> | ||||||
| #include <QDebug> | #include <QDebug> | ||||||
| #include <QLabel> | #include <QLabel> | ||||||
| #include <QMetaType> |  | ||||||
| #include <QPushButton> | #include <QPushButton> | ||||||
| #include <QSpinBox> | #include <QSpinBox> | ||||||
| 
 | 
 | ||||||
|  | @ -17,32 +16,6 @@ | ||||||
| 
 | 
 | ||||||
| #include "util/spinbox.h" | #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, | GraphicsFramebufferWidget::GraphicsFramebufferWidget(std::shared_ptr<Pica::DebugContext> debug_context, | ||||||
|                                                      QWidget* parent) |                                                      QWidget* parent) | ||||||
|     : BreakPointObserverDock(debug_context, tr("Pica Framebuffer"), parent), |     : BreakPointObserverDock(debug_context, tr("Pica Framebuffer"), parent), | ||||||
|  |  | ||||||
|  | @ -6,7 +6,7 @@ | ||||||
| 
 | 
 | ||||||
| #include <QDockWidget> | #include <QDockWidget> | ||||||
| 
 | 
 | ||||||
| #include "video_core/debug_utils/debug_utils.h" | #include "graphics_breakpoint_observer.h" | ||||||
| 
 | 
 | ||||||
| class QComboBox; | class QComboBox; | ||||||
| class QLabel; | class QLabel; | ||||||
|  | @ -14,28 +14,6 @@ class QSpinBox; | ||||||
| 
 | 
 | ||||||
| class CSpinBox; | 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 { | class GraphicsFramebufferWidget : public BreakPointObserverDock { | ||||||
|     Q_OBJECT |     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_breakpoints.h" | ||||||
| #include "debugger/graphics_cmdlists.h" | #include "debugger/graphics_cmdlists.h" | ||||||
| #include "debugger/graphics_framebuffer.h" | #include "debugger/graphics_framebuffer.h" | ||||||
|  | #include "debugger/graphics_vertex_shader.h" | ||||||
| 
 | 
 | ||||||
| #include "core/settings.h" | #include "core/settings.h" | ||||||
| #include "core/system.h" | #include "core/system.h" | ||||||
|  | @ -84,6 +85,10 @@ GMainWindow::GMainWindow() | ||||||
|     addDockWidget(Qt::RightDockWidgetArea, graphicsFramebufferWidget); |     addDockWidget(Qt::RightDockWidgetArea, graphicsFramebufferWidget); | ||||||
|     graphicsFramebufferWidget->hide(); |     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")); |     QMenu* debug_menu = ui.menu_View->addMenu(tr("Debugging")); | ||||||
|     debug_menu->addAction(disasmWidget->toggleViewAction()); |     debug_menu->addAction(disasmWidget->toggleViewAction()); | ||||||
|     debug_menu->addAction(registersWidget->toggleViewAction()); |     debug_menu->addAction(registersWidget->toggleViewAction()); | ||||||
|  | @ -92,6 +97,7 @@ GMainWindow::GMainWindow() | ||||||
|     debug_menu->addAction(graphicsCommandsWidget->toggleViewAction()); |     debug_menu->addAction(graphicsCommandsWidget->toggleViewAction()); | ||||||
|     debug_menu->addAction(graphicsBreakpointsWidget->toggleViewAction()); |     debug_menu->addAction(graphicsBreakpointsWidget->toggleViewAction()); | ||||||
|     debug_menu->addAction(graphicsFramebufferWidget->toggleViewAction()); |     debug_menu->addAction(graphicsFramebufferWidget->toggleViewAction()); | ||||||
|  |     debug_menu->addAction(graphicsVertexShaderWidget->toggleViewAction()); | ||||||
| 
 | 
 | ||||||
|     // Set default UI state
 |     // Set default UI state
 | ||||||
|     // geometry: 55% of the window contents are in the upper screen half, 45% in the lower half
 |     // geometry: 55% of the window contents are in the upper screen half, 45% in the lower half
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Tony Wasserka
						Tony Wasserka