Просмотр исходного кода

Add GUI widget for controlling pica breakpoints.

Tony Wasserka 11 лет назад
Родитель
Сommit
c63a495de6

+ 2 - 0
src/citra_qt/CMakeLists.txt

@@ -8,6 +8,7 @@ set(SRCS
             debugger/callstack.cpp
             debugger/callstack.cpp
             debugger/disassembler.cpp
             debugger/disassembler.cpp
             debugger/graphics.cpp
             debugger/graphics.cpp
+            debugger/graphics_breakpoints.cpp
             debugger/graphics_cmdlists.cpp
             debugger/graphics_cmdlists.cpp
             debugger/ramview.cpp
             debugger/ramview.cpp
             debugger/registers.cpp
             debugger/registers.cpp
@@ -24,6 +25,7 @@ set(HEADERS
             debugger/callstack.hxx
             debugger/callstack.hxx
             debugger/disassembler.hxx
             debugger/disassembler.hxx
             debugger/graphics.hxx
             debugger/graphics.hxx
+            debugger/graphics_breakpoints.hxx
             debugger/graphics_cmdlists.hxx
             debugger/graphics_cmdlists.hxx
             debugger/ramview.hxx
             debugger/ramview.hxx
             debugger/registers.hxx
             debugger/registers.hxx

+ 253 - 0
src/citra_qt/debugger/graphics_breakpoints.cpp

@@ -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 - 0
src/citra_qt/debugger/graphics_breakpoints.hxx

@@ -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;
+};

+ 6 - 0
src/citra_qt/main.cpp

@@ -20,6 +20,7 @@
 #include "debugger/callstack.hxx"
 #include "debugger/callstack.hxx"
 #include "debugger/ramview.hxx"
 #include "debugger/ramview.hxx"
 #include "debugger/graphics.hxx"
 #include "debugger/graphics.hxx"
+#include "debugger/graphics_breakpoints.hxx"
 #include "debugger/graphics_cmdlists.hxx"
 #include "debugger/graphics_cmdlists.hxx"
 
 
 #include "core/settings.h"
 #include "core/settings.h"
@@ -69,12 +70,17 @@ GMainWindow::GMainWindow()
     addDockWidget(Qt::RightDockWidgetArea, graphicsCommandsWidget);
     addDockWidget(Qt::RightDockWidgetArea, graphicsCommandsWidget);
     graphicsCommandsWidget->hide();
     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"));
     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());
     debug_menu->addAction(callstackWidget->toggleViewAction());
     debug_menu->addAction(callstackWidget->toggleViewAction());
     debug_menu->addAction(graphicsWidget->toggleViewAction());
     debug_menu->addAction(graphicsWidget->toggleViewAction());
     debug_menu->addAction(graphicsCommandsWidget->toggleViewAction());
     debug_menu->addAction(graphicsCommandsWidget->toggleViewAction());
+    debug_menu->addAction(graphicsBreakpointsWidget->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