diff options
Diffstat (limited to '')
21 files changed, 380 insertions, 101 deletions
diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt index c2d1ad240..47aaeca24 100644 --- a/src/citra_qt/CMakeLists.txt +++ b/src/citra_qt/CMakeLists.txt @@ -12,6 +12,7 @@ set(SRCS debugger/graphics_breakpoints.cpp debugger/graphics_cmdlists.cpp debugger/graphics_framebuffer.cpp + debugger/graphics_tracing.cpp debugger/graphics_vertex_shader.cpp debugger/profiler.cpp debugger/ramview.cpp @@ -35,6 +36,7 @@ set(HEADERS debugger/graphics_breakpoints_p.h debugger/graphics_cmdlists.h debugger/graphics_framebuffer.h + debugger/graphics_tracing.h debugger/graphics_vertex_shader.h debugger/profiler.h debugger/ramview.h @@ -73,7 +75,9 @@ target_link_libraries(citra-qt core common video_core qhexedit) target_link_libraries(citra-qt ${OPENGL_gl_LIBRARY} ${CITRA_QT_LIBS}) target_link_libraries(citra-qt ${PLATFORM_LIBRARIES}) -#install(TARGETS citra-qt RUNTIME DESTINATION ${bindir}) +if(${CMAKE_SYSTEM_NAME} MATCHES "Linux|FreeBSD|OpenBSD|NetBSD") + install(TARGETS citra-qt RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin") +endif() if (Qt5_FOUND AND MSVC) set(Qt5_DLL_DIR "${Qt5_DIR}/../../../bin") diff --git a/src/citra_qt/bootmanager.cpp b/src/citra_qt/bootmanager.cpp index 3db09c65b..fa7bce466 100644 --- a/src/citra_qt/bootmanager.cpp +++ b/src/citra_qt/bootmanager.cpp @@ -11,6 +11,10 @@ #include "bootmanager.h" #include "main.h" +#include "common/string_util.h" +#include "common/scm_rev.h" +#include "common/key_map.h" + #include "core/core.h" #include "core/settings.h" #include "core/system.h" @@ -61,7 +65,7 @@ void EmuThread::run() { was_active = false; } else { std::unique_lock<std::mutex> lock(running_mutex); - running_cv.wait(lock, [this]{ return IsRunning() || stop_run; }); + running_cv.wait(lock, [this]{ return IsRunning() || exec_step || stop_run; }); } } diff --git a/src/citra_qt/bootmanager.h b/src/citra_qt/bootmanager.h index 475124319..1a1e0e6a5 100644 --- a/src/citra_qt/bootmanager.h +++ b/src/citra_qt/bootmanager.h @@ -35,7 +35,10 @@ public: * Steps the emulation thread by a single CPU instruction (if the CPU is not already running) * @note This function is thread-safe */ - void ExecStep() { exec_step = true; } + void ExecStep() { + exec_step = true; + running_cv.notify_all(); + } /** * Sets whether the emulation thread is running or not diff --git a/src/citra_qt/config.cpp b/src/citra_qt/config.cpp index 2a9af1f38..5c056446e 100644 --- a/src/citra_qt/config.cpp +++ b/src/citra_qt/config.cpp @@ -2,11 +2,11 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <QSettings> #include <QString> #include <QStringList> #include "core/settings.h" -#include "core/core.h" #include "common/file_util.h" #include "config.h" diff --git a/src/citra_qt/config.h b/src/citra_qt/config.h index 4485cae73..dd0b2ef0b 100644 --- a/src/citra_qt/config.h +++ b/src/citra_qt/config.h @@ -4,9 +4,9 @@ #pragma once -#include <QSettings> +#include <string> -#include "common/common_types.h" +class QSettings; class Config { QSettings* qt_config; diff --git a/src/citra_qt/debugger/disassembler.cpp b/src/citra_qt/debugger/disassembler.cpp index e99ec1b30..b41c40a0e 100644 --- a/src/citra_qt/debugger/disassembler.cpp +++ b/src/citra_qt/debugger/disassembler.cpp @@ -2,6 +2,8 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <QShortcut> + #include "disassembler.h" #include "../bootmanager.h" diff --git a/src/citra_qt/debugger/graphics_breakpoint_observer.h b/src/citra_qt/debugger/graphics_breakpoint_observer.h index f0d3361f8..02a0f4f4f 100644 --- a/src/citra_qt/debugger/graphics_breakpoint_observer.h +++ b/src/citra_qt/debugger/graphics_breakpoint_observer.h @@ -13,7 +13,7 @@ * 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 { +class BreakPointObserverDock : public QDockWidget, protected Pica::DebugContext::BreakPointObserver { Q_OBJECT public: diff --git a/src/citra_qt/debugger/graphics_breakpoints.cpp b/src/citra_qt/debugger/graphics_breakpoints.cpp index 1da64f616..5202c168c 100644 --- a/src/citra_qt/debugger/graphics_breakpoints.cpp +++ b/src/citra_qt/debugger/graphics_breakpoints.cpp @@ -4,7 +4,7 @@ #include <QMetaType> #include <QPushButton> -#include <QTreeWidget> +#include <QTreeView> #include <QVBoxLayout> #include <QLabel> @@ -23,7 +23,7 @@ BreakPointModel::BreakPointModel(std::shared_ptr<Pica::DebugContext> debug_conte int BreakPointModel::columnCount(const QModelIndex& parent) const { - return 2; + return 1; } int BreakPointModel::rowCount(const QModelIndex& parent) const @@ -38,29 +38,29 @@ QVariant BreakPointModel::data(const QModelIndex& index, int role) const switch (role) { case Qt::DisplayRole: { - switch (index.column()) { - case 0: - { + if (index.column() == 0) { static const std::map<Pica::DebugContext::Event, QString> map = { - { Pica::DebugContext::Event::CommandLoaded, tr("Pica command loaded") }, - { Pica::DebugContext::Event::CommandProcessed, tr("Pica command processed") }, + { Pica::DebugContext::Event::PicaCommandLoaded, tr("Pica command loaded") }, + { Pica::DebugContext::Event::PicaCommandProcessed, tr("Pica command processed") }, { Pica::DebugContext::Event::IncomingPrimitiveBatch, tr("Incoming primitive batch") }, { Pica::DebugContext::Event::FinishedPrimitiveBatch, tr("Finished primitive batch") }, - { Pica::DebugContext::Event::VertexLoaded, tr("Vertex loaded") } + { Pica::DebugContext::Event::VertexLoaded, tr("Vertex loaded") }, + { Pica::DebugContext::Event::IncomingDisplayTransfer, tr("Incoming display transfer") }, + { Pica::DebugContext::Event::GSPCommandProcessed, tr("GSP command processed") }, + { Pica::DebugContext::Event::BufferSwapped, tr("Buffers swapped") } }; DEBUG_ASSERT(map.size() == static_cast<size_t>(Pica::DebugContext::Event::NumEvents)); - return (map.find(event) != map.end()) ? map.at(event) : QString(); } - case 1: - return data(index, Role_IsEnabled).toBool() ? tr("Enabled") : tr("Disabled"); - - default: - break; - } + break; + } + case Qt::CheckStateRole: + { + if (index.column() == 0) + return data(index, Role_IsEnabled).toBool() ? Qt::Checked : Qt::Unchecked; break; } @@ -84,37 +84,34 @@ QVariant BreakPointModel::data(const QModelIndex& index, int role) const return QVariant(); } -QVariant BreakPointModel::headerData(int section, Qt::Orientation orientation, int role) const +Qt::ItemFlags BreakPointModel::flags(const QModelIndex &index) const { - switch(role) { - case Qt::DisplayRole: - { - if (section == 0) { - return tr("Event"); - } else if (section == 1) { - return tr("Status"); - } - - break; - } - } + if (!index.isValid()) + return 0; - return QVariant(); + Qt::ItemFlags flags = Qt::ItemIsEnabled; + if (index.column() == 0) + flags |= Qt::ItemIsUserCheckable; + return flags; } + 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: + case Qt::CheckStateRole: { + if (index.column() != 0) + return false; + auto context = context_weak.lock(); if (!context) return false; - context->breakpoints[event].enabled = value.toBool(); - QModelIndex changed_index = createIndex(index.row(), 1); + context->breakpoints[event].enabled = value == Qt::Checked; + QModelIndex changed_index = createIndex(index.row(), 0); emit dataChanged(changed_index, changed_index); return true; } @@ -133,7 +130,7 @@ void BreakPointModel::OnBreakPointHit(Pica::DebugContext::Event event) active_breakpoint = context->active_breakpoint; at_breakpoint = context->at_breakpoint; emit dataChanged(createIndex(static_cast<int>(event), 0), - createIndex(static_cast<int>(event), 1)); + createIndex(static_cast<int>(event), 0)); } void BreakPointModel::OnResumed() @@ -144,7 +141,7 @@ void BreakPointModel::OnResumed() at_breakpoint = context->at_breakpoint; emit dataChanged(createIndex(static_cast<int>(active_breakpoint), 0), - createIndex(static_cast<int>(active_breakpoint), 1)); + createIndex(static_cast<int>(active_breakpoint), 0)); active_breakpoint = context->active_breakpoint; } @@ -162,13 +159,15 @@ GraphicsBreakPointsWidget::GraphicsBreakPointsWidget(std::shared_ptr<Pica::Debug breakpoint_model = new BreakPointModel(debug_context, this); breakpoint_list = new QTreeView; + breakpoint_list->setRootIsDecorated(false); + breakpoint_list->setHeaderHidden(true); 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(breakpoint_list, SIGNAL(doubleClicked(const QModelIndex&)), + this, SLOT(OnItemDoubleClicked(const QModelIndex&))); + connect(resume_button, SIGNAL(clicked()), this, SLOT(OnResumeRequested())); connect(this, SIGNAL(BreakPointHit(Pica::DebugContext::Event,void*)), @@ -184,11 +183,6 @@ GraphicsBreakPointsWidget::GraphicsBreakPointsWidget(std::shared_ptr<Pica::Debug 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; { @@ -198,7 +192,6 @@ GraphicsBreakPointsWidget::GraphicsBreakPointsWidget(std::shared_ptr<Pica::Debug main_layout->addLayout(sub_layout); } main_layout->addWidget(breakpoint_list); - main_layout->addWidget(toggle_breakpoint_button); main_widget->setLayout(main_layout); setWidget(main_widget); @@ -234,32 +227,15 @@ void GraphicsBreakPointsWidget::OnResumeRequested() context->Resume(); } -void GraphicsBreakPointsWidget::OnBreakpointSelectionChanged(const QModelIndex& index) +void GraphicsBreakPointsWidget::OnItemDoubleClicked(const QModelIndex& index) { - if (!index.isValid()) { - toggle_breakpoint_button->setEnabled(false); + if (!index.isValid()) 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")); - } + QModelIndex check_index = breakpoint_list->model()->index(index.row(), 0); + QVariant enabled = breakpoint_list->model()->data(check_index, Qt::CheckStateRole); + QVariant new_state = Qt::Unchecked; + if (enabled == Qt::Unchecked) + new_state = Qt::Checked; + breakpoint_list->model()->setData(check_index, new_state, Qt::CheckStateRole); } diff --git a/src/citra_qt/debugger/graphics_breakpoints.h b/src/citra_qt/debugger/graphics_breakpoints.h index 5b9ba324e..d900729da 100644 --- a/src/citra_qt/debugger/graphics_breakpoints.h +++ b/src/citra_qt/debugger/graphics_breakpoints.h @@ -31,10 +31,9 @@ public: public slots: void OnBreakPointHit(Pica::DebugContext::Event event, void* data); + void OnItemDoubleClicked(const QModelIndex&); void OnResumeRequested(); void OnResumed(); - void OnBreakpointSelectionChanged(const QModelIndex&); - void OnToggleBreakpointEnabled(); signals: void Resumed(); @@ -42,11 +41,8 @@ signals: 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; diff --git a/src/citra_qt/debugger/graphics_breakpoints_p.h b/src/citra_qt/debugger/graphics_breakpoints_p.h index 34e72e859..00d8d5101 100644 --- a/src/citra_qt/debugger/graphics_breakpoints_p.h +++ b/src/citra_qt/debugger/graphics_breakpoints_p.h @@ -23,7 +23,7 @@ public: 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; + Qt::ItemFlags flags(const QModelIndex &index) const; bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override; diff --git a/src/citra_qt/debugger/graphics_cmdlists.cpp b/src/citra_qt/debugger/graphics_cmdlists.cpp index cabf5fe07..de10bce1f 100644 --- a/src/citra_qt/debugger/graphics_cmdlists.cpp +++ b/src/citra_qt/debugger/graphics_cmdlists.cpp @@ -2,6 +2,8 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <QApplication> +#include <QClipboard> #include <QLabel> #include <QListView> #include <QMainWindow> @@ -74,7 +76,7 @@ TextureInfoDockWidget::TextureInfoDockWidget(const Pica::DebugUtils::TextureInfo format_choice->addItem(tr("I8")); format_choice->addItem(tr("A8")); format_choice->addItem(tr("IA4")); - format_choice->addItem(tr("UNK10")); + format_choice->addItem(tr("I4")); format_choice->addItem(tr("A4")); format_choice->addItem(tr("ETC1")); format_choice->addItem(tr("ETC1A4")); @@ -168,7 +170,7 @@ GPUCommandListModel::GPUCommandListModel(QObject* parent) : QAbstractListModel(p } int GPUCommandListModel::rowCount(const QModelIndex& parent) const { - return pica_trace.writes.size(); + return static_cast<int>(pica_trace.writes.size()); } int GPUCommandListModel::columnCount(const QModelIndex& parent) const { @@ -304,16 +306,24 @@ GPUCommandListWidget::GPUCommandListWidget(QWidget* parent) : QDockWidget(tr("Pi this, SLOT(OnCommandDoubleClicked(const QModelIndex&))); toggle_tracing = new QPushButton(tr("Start Tracing")); + QPushButton* copy_all = new QPushButton(tr("Copy All")); connect(toggle_tracing, SIGNAL(clicked()), this, SLOT(OnToggleTracing())); connect(this, SIGNAL(TracingFinished(const Pica::DebugUtils::PicaTrace&)), model, SLOT(OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace&))); + connect(copy_all, SIGNAL(clicked()), this, SLOT(CopyAllToClipboard())); + command_info_widget = new QWidget; QVBoxLayout* main_layout = new QVBoxLayout; main_layout->addWidget(list_widget); - main_layout->addWidget(toggle_tracing); + { + QHBoxLayout* sub_layout = new QHBoxLayout; + sub_layout->addWidget(toggle_tracing); + sub_layout->addWidget(copy_all); + main_layout->addLayout(sub_layout); + } main_layout->addWidget(command_info_widget); main_widget->setLayout(main_layout); @@ -330,3 +340,21 @@ void GPUCommandListWidget::OnToggleTracing() { toggle_tracing->setText(tr("Start Tracing")); } } + +void GPUCommandListWidget::CopyAllToClipboard() { + QClipboard* clipboard = QApplication::clipboard(); + QString text; + + QAbstractItemModel* model = static_cast<QAbstractListModel*>(list_widget->model()); + + for (int row = 0; row < model->rowCount({}); ++row) { + for (int col = 0; col < model->columnCount({}); ++col) { + QModelIndex index = model->index(row, col); + text += model->data(index).value<QString>(); + text += '\t'; + } + text += '\n'; + } + + clipboard->setText(text); +} diff --git a/src/citra_qt/debugger/graphics_cmdlists.h b/src/citra_qt/debugger/graphics_cmdlists.h index a465d044c..4859b6ec8 100644 --- a/src/citra_qt/debugger/graphics_cmdlists.h +++ b/src/citra_qt/debugger/graphics_cmdlists.h @@ -49,6 +49,8 @@ public slots: void SetCommandInfo(const QModelIndex&); + void CopyAllToClipboard(); + signals: void TracingFinished(const Pica::DebugUtils::PicaTrace&); diff --git a/src/citra_qt/debugger/graphics_framebuffer.cpp b/src/citra_qt/debugger/graphics_framebuffer.cpp index 6bbe7572c..39eefbf75 100644 --- a/src/citra_qt/debugger/graphics_framebuffer.cpp +++ b/src/citra_qt/debugger/graphics_framebuffer.cpp @@ -55,7 +55,9 @@ GraphicsFramebufferWidget::GraphicsFramebufferWidget(std::shared_ptr<Pica::Debug framebuffer_format_control->addItem(tr("RGBA4")); framebuffer_format_control->addItem(tr("D16")); framebuffer_format_control->addItem(tr("D24")); - framebuffer_format_control->addItem(tr("D24S8")); + framebuffer_format_control->addItem(tr("D24X8")); + framebuffer_format_control->addItem(tr("X24S8")); + framebuffer_format_control->addItem(tr("(unknown)")); // TODO: This QLabel should shrink the image to the available space rather than just expanding... framebuffer_picture_label = new QLabel; @@ -184,8 +186,32 @@ void GraphicsFramebufferWidget::OnUpdate() framebuffer_address = framebuffer.GetColorBufferPhysicalAddress(); framebuffer_width = framebuffer.GetWidth(); framebuffer_height = framebuffer.GetHeight(); - // TODO: It's unknown how this format is actually specified - framebuffer_format = Format::RGBA8; + + switch (framebuffer.color_format) { + case Pica::Regs::ColorFormat::RGBA8: + framebuffer_format = Format::RGBA8; + break; + + case Pica::Regs::ColorFormat::RGB8: + framebuffer_format = Format::RGB8; + break; + + case Pica::Regs::ColorFormat::RGB5A1: + framebuffer_format = Format::RGB5A1; + break; + + case Pica::Regs::ColorFormat::RGB565: + framebuffer_format = Format::RGB565; + break; + + case Pica::Regs::ColorFormat::RGBA4: + framebuffer_format = Format::RGBA4; + break; + + default: + framebuffer_format = Format::Unknown; + break; + } break; } @@ -197,7 +223,24 @@ void GraphicsFramebufferWidget::OnUpdate() framebuffer_address = framebuffer.GetDepthBufferPhysicalAddress(); framebuffer_width = framebuffer.GetWidth(); framebuffer_height = framebuffer.GetHeight(); - framebuffer_format = Format::D16; + + switch (framebuffer.depth_format) { + case Pica::Regs::DepthFormat::D16: + framebuffer_format = Format::D16; + break; + + case Pica::Regs::DepthFormat::D24: + framebuffer_format = Format::D24; + break; + + case Pica::Regs::DepthFormat::D24S8: + framebuffer_format = Format::D24X8; + break; + + default: + framebuffer_format = Format::Unknown; + break; + } break; } @@ -258,7 +301,7 @@ void GraphicsFramebufferWidget::OnUpdate() color.b() = (data >> 16) & 0xFF; break; } - case Format::D24S8: + case Format::D24X8: { Math::Vec2<u32> data = Color::DecodeD24S8(pixel); color.r() = data.x & 0xFF; @@ -266,6 +309,12 @@ void GraphicsFramebufferWidget::OnUpdate() color.b() = (data.x >> 16) & 0xFF; break; } + case Format::X24S8: + { + Math::Vec2<u32> data = Color::DecodeD24S8(pixel); + color.r() = color.g() = color.b() = data.y; + break; + } default: qDebug() << "Unknown fb color format " << static_cast<int>(framebuffer_format); break; @@ -286,7 +335,8 @@ void GraphicsFramebufferWidget::OnUpdate() u32 GraphicsFramebufferWidget::BytesPerPixel(GraphicsFramebufferWidget::Format format) { switch (format) { case Format::RGBA8: - case Format::D24S8: + case Format::D24X8: + case Format::X24S8: return 4; case Format::RGB8: case Format::D24: diff --git a/src/citra_qt/debugger/graphics_framebuffer.h b/src/citra_qt/debugger/graphics_framebuffer.h index 4cb396ffe..e9eae679f 100644 --- a/src/citra_qt/debugger/graphics_framebuffer.h +++ b/src/citra_qt/debugger/graphics_framebuffer.h @@ -35,7 +35,9 @@ class GraphicsFramebufferWidget : public BreakPointObserverDock { RGBA4 = 4, D16 = 5, D24 = 6, - D24S8 = 7 + D24X8 = 7, + X24S8 = 8, + Unknown = 9 }; static u32 BytesPerPixel(Format format); diff --git a/src/citra_qt/debugger/graphics_tracing.cpp b/src/citra_qt/debugger/graphics_tracing.cpp new file mode 100644 index 000000000..3f20f149d --- /dev/null +++ b/src/citra_qt/debugger/graphics_tracing.cpp @@ -0,0 +1,170 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <memory> + +#include <QBoxLayout> +#include <QComboBox> +#include <QFileDialog> +#include <QLabel> +#include <QMessageBox> +#include <QPushButton> +#include <QSpinBox> + +#include <boost/range/algorithm/copy.hpp> + +#include "core/hw/gpu.h" +#include "core/hw/lcd.h" + +#include "video_core/pica.h" + +#include "nihstro/float24.h" + +#include "graphics_tracing.h" + +GraphicsTracingWidget::GraphicsTracingWidget(std::shared_ptr<Pica::DebugContext> debug_context, + QWidget* parent) + : BreakPointObserverDock(debug_context, tr("CiTrace Recorder"), parent) { + + setObjectName("CiTracing"); + + QPushButton* start_recording = new QPushButton(tr("Start Recording")); + QPushButton* stop_recording = new QPushButton(QIcon::fromTheme("document-save"), tr("Stop and Save")); + QPushButton* abort_recording = new QPushButton(tr("Abort Recording")); + + connect(this, SIGNAL(SetStartTracingButtonEnabled(bool)), start_recording, SLOT(setVisible(bool))); + connect(this, SIGNAL(SetStopTracingButtonEnabled(bool)), stop_recording, SLOT(setVisible(bool))); + connect(this, SIGNAL(SetAbortTracingButtonEnabled(bool)), abort_recording, SLOT(setVisible(bool))); + connect(start_recording, SIGNAL(clicked()), this, SLOT(StartRecording())); + connect(stop_recording, SIGNAL(clicked()), this, SLOT(StopRecording())); + connect(abort_recording, SIGNAL(clicked()), this, SLOT(AbortRecording())); + + stop_recording->setVisible(false); + abort_recording->setVisible(false); + + auto main_widget = new QWidget; + auto main_layout = new QVBoxLayout; + { + auto sub_layout = new QHBoxLayout; + sub_layout->addWidget(start_recording); + sub_layout->addWidget(stop_recording); + sub_layout->addWidget(abort_recording); + main_layout->addLayout(sub_layout); + } + main_widget->setLayout(main_layout); + setWidget(main_widget); +} + +void GraphicsTracingWidget::StartRecording() { + auto context = context_weak.lock(); + if (!context) + return; + + auto shader_binary = Pica::g_state.vs.program_code; + auto swizzle_data = Pica::g_state.vs.swizzle_data; + + // Encode floating point numbers to 24-bit values + // TODO: Drop this explicit conversion once we store float24 values bit-correctly internally. + std::array<uint32_t, 4 * 16> default_attributes; + for (unsigned i = 0; i < 16; ++i) { + for (unsigned comp = 0; comp < 3; ++comp) { + default_attributes[4 * i + comp] = nihstro::to_float24(Pica::g_state.vs.default_attributes[i][comp].ToFloat32()); + } + } + + std::array<uint32_t, 4 * 96> vs_float_uniforms; + for (unsigned i = 0; i < 96; ++i) + for (unsigned comp = 0; comp < 3; ++comp) + vs_float_uniforms[4 * i + comp] = nihstro::to_float24(Pica::g_state.vs.uniforms.f[i][comp].ToFloat32()); + + CiTrace::Recorder::InitialState state; + std::copy_n((u32*)&GPU::g_regs, sizeof(GPU::g_regs) / sizeof(u32), std::back_inserter(state.gpu_registers)); + std::copy_n((u32*)&LCD::g_regs, sizeof(LCD::g_regs) / sizeof(u32), std::back_inserter(state.lcd_registers)); + std::copy_n((u32*)&Pica::g_state.regs, sizeof(Pica::g_state.regs) / sizeof(u32), std::back_inserter(state.pica_registers)); + boost::copy(default_attributes, std::back_inserter(state.default_attributes)); + boost::copy(shader_binary, std::back_inserter(state.vs_program_binary)); + boost::copy(swizzle_data, std::back_inserter(state.vs_swizzle_data)); + boost::copy(vs_float_uniforms, std::back_inserter(state.vs_float_uniforms)); + //boost::copy(TODO: Not implemented, std::back_inserter(state.gs_program_binary)); + //boost::copy(TODO: Not implemented, std::back_inserter(state.gs_swizzle_data)); + //boost::copy(TODO: Not implemented, std::back_inserter(state.gs_float_uniforms)); + + auto recorder = new CiTrace::Recorder(state); + context->recorder = std::shared_ptr<CiTrace::Recorder>(recorder); + + emit SetStartTracingButtonEnabled(false); + emit SetStopTracingButtonEnabled(true); + emit SetAbortTracingButtonEnabled(true); +} + +void GraphicsTracingWidget::StopRecording() { + auto context = context_weak.lock(); + if (!context) + return; + + QString filename = QFileDialog::getSaveFileName(this, tr("Save CiTrace"), "citrace.ctf", + tr("CiTrace File (*.ctf)")); + + if (filename.isEmpty()) { + // If the user canceled the dialog, keep recording + return; + } + + context->recorder->Finish(filename.toStdString()); + context->recorder = nullptr; + + emit SetStopTracingButtonEnabled(false); + emit SetAbortTracingButtonEnabled(false); + emit SetStartTracingButtonEnabled(true); +} + +void GraphicsTracingWidget::AbortRecording() { + auto context = context_weak.lock(); + if (!context) + return; + + context->recorder = nullptr; + + emit SetStopTracingButtonEnabled(false); + emit SetAbortTracingButtonEnabled(false); + emit SetStartTracingButtonEnabled(true); +} + +void GraphicsTracingWidget::OnBreakPointHit(Pica::DebugContext::Event event, void* data) { + widget()->setEnabled(true); +} + +void GraphicsTracingWidget::OnResumed() { + widget()->setEnabled(false); +} + +void GraphicsTracingWidget::OnEmulationStarting(EmuThread* emu_thread) { + // Disable tracing starting/stopping until a GPU breakpoint is reached + widget()->setEnabled(false); +} + +void GraphicsTracingWidget::OnEmulationStopping() { + // TODO: Is it safe to access the context here? + + auto context = context_weak.lock(); + if (!context) + return; + + + if (context->recorder) { + auto reply = QMessageBox::question(this, tr("CiTracing still active"), + tr("A CiTrace is still being recorded. Do you want to save it? If not, all recorded data will be discarded."), + QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); + + if (reply == QMessageBox::Yes) { + StopRecording(); + } else { + AbortRecording(); + } + } + + // If the widget was disabled before, enable it now to allow starting + // tracing before starting the next emulation session + widget()->setEnabled(true); +} diff --git a/src/citra_qt/debugger/graphics_tracing.h b/src/citra_qt/debugger/graphics_tracing.h new file mode 100644 index 000000000..2a0e4819b --- /dev/null +++ b/src/citra_qt/debugger/graphics_tracing.h @@ -0,0 +1,32 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "graphics_breakpoint_observer.h" + +class EmuThread; + +class GraphicsTracingWidget : public BreakPointObserverDock { + Q_OBJECT + +public: + GraphicsTracingWidget(std::shared_ptr<Pica::DebugContext> debug_context, QWidget* parent = nullptr); + +private slots: + void StartRecording(); + void StopRecording(); + void AbortRecording(); + + void OnBreakPointHit(Pica::DebugContext::Event event, void* data) override; + void OnResumed() override; + + void OnEmulationStarting(EmuThread* emu_thread); + void OnEmulationStopping(); + +signals: + void SetStartTracingButtonEnabled(bool enable); + void SetStopTracingButtonEnabled(bool enable); + void SetAbortTracingButtonEnabled(bool enable); +}; diff --git a/src/citra_qt/debugger/graphics_vertex_shader.cpp b/src/citra_qt/debugger/graphics_vertex_shader.cpp index 14d3f8f39..f42a2f4ce 100644 --- a/src/citra_qt/debugger/graphics_vertex_shader.cpp +++ b/src/citra_qt/debugger/graphics_vertex_shader.cpp @@ -34,7 +34,7 @@ int GraphicsVertexShaderModel::columnCount(const QModelIndex& parent) const { } int GraphicsVertexShaderModel::rowCount(const QModelIndex& parent) const { - return info.code.size(); + return static_cast<int>(info.code.size()); } QVariant GraphicsVertexShaderModel::headerData(int section, Qt::Orientation orientation, int role) const { @@ -259,7 +259,7 @@ void GraphicsVertexShaderModel::OnUpdate() for (auto pattern : Pica::g_state.vs.swizzle_data) info.swizzle_info.push_back({pattern}); - info.labels.insert({ Pica::g_state.regs.vs_main_offset, "main" }); + info.labels.insert({ Pica::g_state.regs.vs.main_offset, "main" }); endResetModel(); } diff --git a/src/citra_qt/debugger/profiler.cpp b/src/citra_qt/debugger/profiler.cpp index 2ac1748b7..89b28c2f4 100644 --- a/src/citra_qt/debugger/profiler.cpp +++ b/src/citra_qt/debugger/profiler.cpp @@ -74,7 +74,7 @@ int ProfilerModel::rowCount(const QModelIndex& parent) const if (parent.isValid()) { return 0; } else { - return results.time_per_category.size() + 2; + return static_cast<int>(results.time_per_category.size() + 2); } } diff --git a/src/citra_qt/hotkeys.cpp b/src/citra_qt/hotkeys.cpp index 322c25c9e..5ed6cf0b1 100644 --- a/src/citra_qt/hotkeys.cpp +++ b/src/citra_qt/hotkeys.cpp @@ -2,10 +2,13 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <map> + #include <QKeySequence> #include <QSettings> +#include <QShortcut> + #include "hotkeys.h" -#include <map> struct Hotkey { diff --git a/src/citra_qt/hotkeys.h b/src/citra_qt/hotkeys.h index 75c7cc625..2317f8188 100644 --- a/src/citra_qt/hotkeys.h +++ b/src/citra_qt/hotkeys.h @@ -2,12 +2,12 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include <QShortcut> -#include <QDialog> #include "ui_hotkeys.h" +class QDialog; class QKeySequence; class QSettings; +class QShortcut; /** * Register a hotkey. diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp index 8041816a0..2746de779 100644 --- a/src/citra_qt/main.cpp +++ b/src/citra_qt/main.cpp @@ -10,18 +10,16 @@ #include "qhexedit.h" #include "main.h" +#include "common/string_util.h" #include "common/logging/text_formatter.h" #include "common/logging/log.h" #include "common/logging/backend.h" #include "common/logging/filter.h" #include "common/make_unique.h" #include "common/platform.h" +#include "common/scm_rev.h" #include "common/scope_exit.h" -#if EMU_PLATFORM == PLATFORM_LINUX -#include <unistd.h> -#endif - #include "bootmanager.h" #include "hotkeys.h" @@ -34,6 +32,7 @@ #include "debugger/graphics_breakpoints.h" #include "debugger/graphics_cmdlists.h" #include "debugger/graphics_framebuffer.h" +#include "debugger/graphics_tracing.h" #include "debugger/graphics_vertex_shader.h" #include "debugger/profiler.h" @@ -96,6 +95,10 @@ GMainWindow::GMainWindow() : emu_thread(nullptr) addDockWidget(Qt::RightDockWidgetArea, graphicsVertexShaderWidget); graphicsVertexShaderWidget->hide(); + auto graphicsTracingWidget = new GraphicsTracingWidget(Pica::g_debug_context, this); + addDockWidget(Qt::RightDockWidgetArea, graphicsTracingWidget); + graphicsTracingWidget->hide(); + QMenu* debug_menu = ui.menu_View->addMenu(tr("Debugging")); debug_menu->addAction(profilerWidget->toggleViewAction()); debug_menu->addAction(disasmWidget->toggleViewAction()); @@ -106,6 +109,7 @@ GMainWindow::GMainWindow() : emu_thread(nullptr) debug_menu->addAction(graphicsBreakpointsWidget->toggleViewAction()); debug_menu->addAction(graphicsFramebufferWidget->toggleViewAction()); debug_menu->addAction(graphicsVertexShaderWidget->toggleViewAction()); + debug_menu->addAction(graphicsTracingWidget->toggleViewAction()); // Set default UI state // geometry: 55% of the window contents are in the upper screen half, 45% in the lower half @@ -150,6 +154,9 @@ GMainWindow::GMainWindow() : emu_thread(nullptr) connect(this, SIGNAL(EmulationStopping()), registersWidget, SLOT(OnEmulationStopping())); connect(this, SIGNAL(EmulationStarting(EmuThread*)), render_window, SLOT(OnEmulationStarting(EmuThread*))); connect(this, SIGNAL(EmulationStopping()), render_window, SLOT(OnEmulationStopping())); + connect(this, SIGNAL(EmulationStarting(EmuThread*)), graphicsTracingWidget, SLOT(OnEmulationStarting(EmuThread*))); + connect(this, SIGNAL(EmulationStopping()), graphicsTracingWidget, SLOT(OnEmulationStopping())); + // Setup hotkeys RegisterHotkey("Main Window", "Load File", QKeySequence::Open); |