mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 13:50:03 +00:00 
			
		
		
		
	Add GUI widget for controlling pica breakpoints.
This commit is contained in:
		
							parent
							
								
									2c71ec7052
								
							
						
					
					
						commit
						c63a495de6
					
				
					 4 changed files with 339 additions and 0 deletions
				
			
		|  | @ -8,6 +8,7 @@ set(SRCS | |||
|             debugger/callstack.cpp | ||||
|             debugger/disassembler.cpp | ||||
|             debugger/graphics.cpp | ||||
|             debugger/graphics_breakpoints.cpp | ||||
|             debugger/graphics_cmdlists.cpp | ||||
|             debugger/ramview.cpp | ||||
|             debugger/registers.cpp | ||||
|  | @ -24,6 +25,7 @@ set(HEADERS | |||
|             debugger/callstack.hxx | ||||
|             debugger/disassembler.hxx | ||||
|             debugger/graphics.hxx | ||||
|             debugger/graphics_breakpoints.hxx | ||||
|             debugger/graphics_cmdlists.hxx | ||||
|             debugger/ramview.hxx | ||||
|             debugger/registers.hxx | ||||
|  |  | |||
							
								
								
									
										253
									
								
								src/citra_qt/debugger/graphics_breakpoints.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										253
									
								
								src/citra_qt/debugger/graphics_breakpoints.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,253 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <QMetaType> | ||||
| #include <QPushButton> | ||||
| #include <QTreeWidget> | ||||
| #include <QVBoxLayout> | ||||
| #include <QLabel> | ||||
| 
 | ||||
| #include "graphics_breakpoints.hxx" | ||||
| 
 | ||||
| BreakPointModel::BreakPointModel(std::shared_ptr<Pica::DebugContext> debug_context, QObject* parent) | ||||
|     : QAbstractListModel(parent), context_weak(debug_context), | ||||
|       at_breakpoint(debug_context->at_breakpoint), | ||||
|       active_breakpoint(debug_context->active_breakpoint) | ||||
| { | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| int BreakPointModel::columnCount(const QModelIndex& parent) const | ||||
| { | ||||
|     return 2; | ||||
| } | ||||
| 
 | ||||
| int BreakPointModel::rowCount(const QModelIndex& parent) const | ||||
| { | ||||
|     return static_cast<int>(Pica::DebugContext::Event::NumEvents); | ||||
| } | ||||
| 
 | ||||
| QVariant BreakPointModel::data(const QModelIndex& index, int role) const | ||||
| { | ||||
|     const auto event = static_cast<Pica::DebugContext::Event>(index.row()); | ||||
| 
 | ||||
|     switch (role) { | ||||
|     case Qt::DisplayRole: | ||||
|     { | ||||
|         if (index.column() == 0) { | ||||
|             std::map<Pica::DebugContext::Event, QString> map; | ||||
|             map.insert({Pica::DebugContext::Event::CommandLoaded, tr("Pica command loaded")}); | ||||
|             map.insert({Pica::DebugContext::Event::CommandProcessed, tr("Pica command processed")}); | ||||
|             map.insert({Pica::DebugContext::Event::IncomingPrimitiveBatch, tr("Incomming primitive batch")}); | ||||
|             map.insert({Pica::DebugContext::Event::FinishedPrimitiveBatch, tr("Finished primitive batch")}); | ||||
| 
 | ||||
|             _dbg_assert_(GPU, map.size() == static_cast<size_t>(Pica::DebugContext::Event::NumEvents)); | ||||
| 
 | ||||
|             return map[event]; | ||||
|         } else if (index.column() == 1) { | ||||
|             return data(index, Role_IsEnabled).toBool() ? tr("Enabled") : tr("Disabled"); | ||||
|         } | ||||
| 
 | ||||
|         break; | ||||
|     } | ||||
| 
 | ||||
|     case Qt::BackgroundRole: | ||||
|     { | ||||
|         if (at_breakpoint && index.row() == static_cast<int>(active_breakpoint)) { | ||||
|             return QBrush(QColor(0xE0, 0xE0, 0x10)); | ||||
|         } | ||||
|         break; | ||||
|     } | ||||
| 
 | ||||
|     case Role_IsEnabled: | ||||
|     { | ||||
|         auto context = context_weak.lock(); | ||||
|         return context && context->breakpoints[event].enabled; | ||||
|     } | ||||
| 
 | ||||
|     default: | ||||
|         break; | ||||
|     } | ||||
|     return QVariant(); | ||||
| } | ||||
| 
 | ||||
| QVariant BreakPointModel::headerData(int section, Qt::Orientation orientation, int role) const | ||||
| { | ||||
|     switch(role) { | ||||
|     case Qt::DisplayRole: | ||||
|     { | ||||
|         if (section == 0) { | ||||
|             return tr("Event"); | ||||
|         } else if (section == 1) { | ||||
|             return tr("Status"); | ||||
|         } | ||||
| 
 | ||||
|         break; | ||||
|     } | ||||
|     } | ||||
| 
 | ||||
|     return QVariant(); | ||||
| } | ||||
| 
 | ||||
| bool BreakPointModel::setData(const QModelIndex& index, const QVariant& value, int role) | ||||
| { | ||||
|     const auto event = static_cast<Pica::DebugContext::Event>(index.row()); | ||||
| 
 | ||||
|     switch (role) { | ||||
|     case Role_IsEnabled: | ||||
|     { | ||||
|         auto context = context_weak.lock(); | ||||
|         if (!context) | ||||
|             return false; | ||||
| 
 | ||||
|         context->breakpoints[event].enabled = value.toBool(); | ||||
|         QModelIndex changed_index = createIndex(index.row(), 1); | ||||
|         emit dataChanged(changed_index, changed_index); | ||||
|         return true; | ||||
|     } | ||||
|     } | ||||
| 
 | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void BreakPointModel::OnBreakPointHit(Pica::DebugContext::Event event) | ||||
| { | ||||
|     auto context = context_weak.lock(); | ||||
|     if (!context) | ||||
|         return; | ||||
| 
 | ||||
|     active_breakpoint = context->active_breakpoint; | ||||
|     at_breakpoint = context->at_breakpoint; | ||||
|     emit dataChanged(createIndex(static_cast<int>(event), 0), | ||||
|                      createIndex(static_cast<int>(event), 1)); | ||||
| } | ||||
| 
 | ||||
| void BreakPointModel::OnResumed() | ||||
| { | ||||
|     auto context = context_weak.lock(); | ||||
|     if (!context) | ||||
|         return; | ||||
| 
 | ||||
|     at_breakpoint = context->at_breakpoint; | ||||
|     emit dataChanged(createIndex(static_cast<int>(active_breakpoint), 0), | ||||
|                      createIndex(static_cast<int>(active_breakpoint), 1)); | ||||
|     active_breakpoint = context->active_breakpoint; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| GraphicsBreakPointsWidget::GraphicsBreakPointsWidget(std::shared_ptr<Pica::DebugContext> debug_context, | ||||
|                                                      QWidget* parent) | ||||
|     : QDockWidget(tr("Pica Breakpoints"), parent), | ||||
|       Pica::DebugContext::BreakPointObserver(debug_context) | ||||
| { | ||||
|     setObjectName("PicaBreakPointsWidget"); | ||||
| 
 | ||||
|     status_text = new QLabel(tr("Emulation running")); | ||||
|     resume_button = new QPushButton(tr("Resume")); | ||||
|     resume_button->setEnabled(false); | ||||
| 
 | ||||
|     breakpoint_model = new BreakPointModel(debug_context, this); | ||||
|     breakpoint_list = new QTreeView; | ||||
|     breakpoint_list->setModel(breakpoint_model); | ||||
| 
 | ||||
|     toggle_breakpoint_button = new QPushButton(tr("Enable")); | ||||
|     toggle_breakpoint_button->setEnabled(false); | ||||
| 
 | ||||
|     qRegisterMetaType<Pica::DebugContext::Event>("Pica::DebugContext::Event"); | ||||
| 
 | ||||
|     connect(resume_button, SIGNAL(clicked()), this, SLOT(OnResumeRequested())); | ||||
| 
 | ||||
|     connect(this, SIGNAL(BreakPointHit(Pica::DebugContext::Event,void*)), | ||||
|             this, SLOT(OnBreakPointHit(Pica::DebugContext::Event,void*)), | ||||
|             Qt::BlockingQueuedConnection); | ||||
|     connect(this, SIGNAL(Resumed()), this, SLOT(OnResumed())); | ||||
| 
 | ||||
|     connect(this, SIGNAL(BreakPointHit(Pica::DebugContext::Event,void*)), | ||||
|             breakpoint_model, SLOT(OnBreakPointHit(Pica::DebugContext::Event)), | ||||
|             Qt::BlockingQueuedConnection); | ||||
|     connect(this, SIGNAL(Resumed()), breakpoint_model, SLOT(OnResumed())); | ||||
| 
 | ||||
|     connect(this, SIGNAL(BreakPointsChanged(const QModelIndex&,const QModelIndex&)), | ||||
|             breakpoint_model, SIGNAL(dataChanged(const QModelIndex&,const QModelIndex&))); | ||||
| 
 | ||||
|     connect(breakpoint_list->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), | ||||
|             this, SLOT(OnBreakpointSelectionChanged(QModelIndex))); | ||||
| 
 | ||||
|     connect(toggle_breakpoint_button, SIGNAL(clicked()), this, SLOT(OnToggleBreakpointEnabled())); | ||||
| 
 | ||||
|     QWidget* main_widget = new QWidget; | ||||
|     auto main_layout = new QVBoxLayout; | ||||
|     { | ||||
|         auto sub_layout = new QHBoxLayout; | ||||
|         sub_layout->addWidget(status_text); | ||||
|         sub_layout->addWidget(resume_button); | ||||
|         main_layout->addLayout(sub_layout); | ||||
|     } | ||||
|     main_layout->addWidget(breakpoint_list); | ||||
|     main_layout->addWidget(toggle_breakpoint_button); | ||||
|     main_widget->setLayout(main_layout); | ||||
| 
 | ||||
|     setWidget(main_widget); | ||||
| } | ||||
| 
 | ||||
| void GraphicsBreakPointsWidget::OnPicaBreakPointHit(Event event, void* data) | ||||
| { | ||||
|     // Process in GUI thread
 | ||||
|     emit BreakPointHit(event, data); | ||||
| } | ||||
| 
 | ||||
| void GraphicsBreakPointsWidget::OnBreakPointHit(Pica::DebugContext::Event event, void* data) | ||||
| { | ||||
|     status_text->setText(tr("Emulation halted at breakpoint")); | ||||
|     resume_button->setEnabled(true); | ||||
| } | ||||
| 
 | ||||
| void GraphicsBreakPointsWidget::OnPicaResume() | ||||
| { | ||||
|     // Process in GUI thread
 | ||||
|     emit Resumed(); | ||||
| } | ||||
| 
 | ||||
| void GraphicsBreakPointsWidget::OnResumed() | ||||
| { | ||||
|     status_text->setText(tr("Emulation running")); | ||||
|     resume_button->setEnabled(false); | ||||
| } | ||||
| 
 | ||||
| void GraphicsBreakPointsWidget::OnResumeRequested() | ||||
| { | ||||
|     if (auto context = context_weak.lock()) | ||||
|         context->Resume(); | ||||
| } | ||||
| 
 | ||||
| void GraphicsBreakPointsWidget::OnBreakpointSelectionChanged(const QModelIndex& index) | ||||
| { | ||||
|     if (!index.isValid()) { | ||||
|         toggle_breakpoint_button->setEnabled(false); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     toggle_breakpoint_button->setEnabled(true); | ||||
|     UpdateToggleBreakpointButton(index); | ||||
| } | ||||
| 
 | ||||
| void GraphicsBreakPointsWidget::OnToggleBreakpointEnabled() | ||||
| { | ||||
|     QModelIndex index = breakpoint_list->selectionModel()->currentIndex(); | ||||
|     bool new_state = !(breakpoint_model->data(index, BreakPointModel::Role_IsEnabled).toBool()); | ||||
| 
 | ||||
|     breakpoint_model->setData(index, new_state, | ||||
|                               BreakPointModel::Role_IsEnabled); | ||||
|     UpdateToggleBreakpointButton(index); | ||||
| } | ||||
| 
 | ||||
| void GraphicsBreakPointsWidget::UpdateToggleBreakpointButton(const QModelIndex& index) | ||||
| { | ||||
|     if (true == breakpoint_model->data(index, BreakPointModel::Role_IsEnabled).toBool()) { | ||||
|         toggle_breakpoint_button->setText(tr("Disable")); | ||||
|     } else { | ||||
|         toggle_breakpoint_button->setText(tr("Enable")); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										78
									
								
								src/citra_qt/debugger/graphics_breakpoints.hxx
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								src/citra_qt/debugger/graphics_breakpoints.hxx
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,78 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <memory> | ||||
| 
 | ||||
| #include <QAbstractListModel> | ||||
| #include <QDockWidget> | ||||
| 
 | ||||
| #include "video_core/debug_utils/debug_utils.h" | ||||
| 
 | ||||
| class QLabel; | ||||
| class QPushButton; | ||||
| class QTreeView; | ||||
| 
 | ||||
| class BreakPointModel : public QAbstractListModel { | ||||
|     Q_OBJECT | ||||
| 
 | ||||
| public: | ||||
|     enum { | ||||
|         Role_IsEnabled = Qt::UserRole, | ||||
|     }; | ||||
| 
 | ||||
|     BreakPointModel(std::shared_ptr<Pica::DebugContext> context, QObject* parent); | ||||
| 
 | ||||
|     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; | ||||
| 
 | ||||
|     bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole); | ||||
| 
 | ||||
| public slots: | ||||
|     void OnBreakPointHit(Pica::DebugContext::Event event); | ||||
|     void OnResumed(); | ||||
| 
 | ||||
| private: | ||||
|     bool at_breakpoint; | ||||
|     Pica::DebugContext::Event active_breakpoint; | ||||
|     std::weak_ptr<Pica::DebugContext> context_weak; | ||||
| }; | ||||
| 
 | ||||
| class GraphicsBreakPointsWidget : public QDockWidget, Pica::DebugContext::BreakPointObserver { | ||||
|     Q_OBJECT | ||||
| 
 | ||||
|     using Event = Pica::DebugContext::Event; | ||||
| 
 | ||||
| public: | ||||
|     GraphicsBreakPointsWidget(std::shared_ptr<Pica::DebugContext> debug_context, | ||||
|                               QWidget* parent = nullptr); | ||||
| 
 | ||||
|     void OnPicaBreakPointHit(Pica::DebugContext::Event event, void* data) override; | ||||
|     void OnPicaResume() override; | ||||
| 
 | ||||
| public slots: | ||||
|     void OnBreakPointHit(Pica::DebugContext::Event event, void* data); | ||||
|     void OnResumeRequested(); | ||||
|     void OnResumed(); | ||||
|     void OnBreakpointSelectionChanged(const QModelIndex&); | ||||
|     void OnToggleBreakpointEnabled(); | ||||
| 
 | ||||
| signals: | ||||
|     void Resumed(); | ||||
|     void BreakPointHit(Pica::DebugContext::Event event, void* data); | ||||
|     void BreakPointsChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight); | ||||
| 
 | ||||
| private: | ||||
|     void UpdateToggleBreakpointButton(const QModelIndex& index); | ||||
| 
 | ||||
|     QLabel* status_text; | ||||
|     QPushButton* resume_button; | ||||
|     QPushButton* toggle_breakpoint_button; | ||||
| 
 | ||||
|     BreakPointModel* breakpoint_model; | ||||
|     QTreeView* breakpoint_list; | ||||
| }; | ||||
|  | @ -20,6 +20,7 @@ | |||
| #include "debugger/callstack.hxx" | ||||
| #include "debugger/ramview.hxx" | ||||
| #include "debugger/graphics.hxx" | ||||
| #include "debugger/graphics_breakpoints.hxx" | ||||
| #include "debugger/graphics_cmdlists.hxx" | ||||
| 
 | ||||
| #include "core/settings.h" | ||||
|  | @ -69,12 +70,17 @@ GMainWindow::GMainWindow() | |||
|     addDockWidget(Qt::RightDockWidgetArea, graphicsCommandsWidget); | ||||
|     graphicsCommandsWidget->hide(); | ||||
| 
 | ||||
|     auto graphicsBreakpointsWidget = new GraphicsBreakPointsWidget(Pica::g_debug_context, this); | ||||
|     addDockWidget(Qt::RightDockWidgetArea, graphicsBreakpointsWidget); | ||||
|     graphicsBreakpointsWidget->hide(); | ||||
| 
 | ||||
|     QMenu* debug_menu = ui.menu_View->addMenu(tr("Debugging")); | ||||
|     debug_menu->addAction(disasmWidget->toggleViewAction()); | ||||
|     debug_menu->addAction(registersWidget->toggleViewAction()); | ||||
|     debug_menu->addAction(callstackWidget->toggleViewAction()); | ||||
|     debug_menu->addAction(graphicsWidget->toggleViewAction()); | ||||
|     debug_menu->addAction(graphicsCommandsWidget->toggleViewAction()); | ||||
|     debug_menu->addAction(graphicsBreakpointsWidget->toggleViewAction()); | ||||
| 
 | ||||
|     // Set default UI state
 | ||||
|     // 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