aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/citra_qt
diff options
context:
space:
mode:
authorGravatar Tony Wasserka <neobrainx@gmail.com>2015-07-13 21:39:58 +0200
committerGravatar Tony Wasserka <neobrainx@gmail.com>2015-07-13 21:39:58 +0200
commit884b681ccaf3cb4057ca0ed0102e446736bb535f (patch)
tree9359e9b88f0147879c672638d8c02960d2179d3a /src/citra_qt
parentc4e11519afb8c51b591b88f6bfa531b04d15d588 (diff)
parent29abb11e8fbb6a78edf84496979ef946bf4e45a7 (diff)
Merge pull request #702 from neobrain/citrace
Add CiTrace recording support.
Diffstat (limited to 'src/citra_qt')
-rw-r--r--src/citra_qt/CMakeLists.txt2
-rw-r--r--src/citra_qt/debugger/graphics_breakpoint_observer.h2
-rw-r--r--src/citra_qt/debugger/graphics_tracing.cpp170
-rw-r--r--src/citra_qt/debugger/graphics_tracing.h32
-rw-r--r--src/citra_qt/main.cpp9
5 files changed, 214 insertions, 1 deletions
diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt
index d4c0cecc..47aaeca2 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
diff --git a/src/citra_qt/debugger/graphics_breakpoint_observer.h b/src/citra_qt/debugger/graphics_breakpoint_observer.h
index f0d3361f..02a0f4f4 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_tracing.cpp b/src/citra_qt/debugger/graphics_tracing.cpp
new file mode 100644
index 00000000..3f20f149
--- /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 00000000..2a0e4819
--- /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/main.cpp b/src/citra_qt/main.cpp
index d23bafaf..2746de77 100644
--- a/src/citra_qt/main.cpp
+++ b/src/citra_qt/main.cpp
@@ -32,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"
@@ -94,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());
@@ -104,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
@@ -148,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);