summaryrefslogtreecommitdiff
path: root/src/citra_qt
diff options
context:
space:
mode:
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_framebuffer.cpp62
-rw-r--r--src/citra_qt/debugger/graphics_framebuffer.h4
-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
7 files changed, 273 insertions, 8 deletions
diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt
index d4c0cecc3..47aaeca24 100644
--- a/src/citra_qt/CMakeLists.txt
+++ b/src/citra_qt/CMakeLists.txt
@@ -12,6 +12,7 @@ set(SRCS
12 debugger/graphics_breakpoints.cpp 12 debugger/graphics_breakpoints.cpp
13 debugger/graphics_cmdlists.cpp 13 debugger/graphics_cmdlists.cpp
14 debugger/graphics_framebuffer.cpp 14 debugger/graphics_framebuffer.cpp
15 debugger/graphics_tracing.cpp
15 debugger/graphics_vertex_shader.cpp 16 debugger/graphics_vertex_shader.cpp
16 debugger/profiler.cpp 17 debugger/profiler.cpp
17 debugger/ramview.cpp 18 debugger/ramview.cpp
@@ -35,6 +36,7 @@ set(HEADERS
35 debugger/graphics_breakpoints_p.h 36 debugger/graphics_breakpoints_p.h
36 debugger/graphics_cmdlists.h 37 debugger/graphics_cmdlists.h
37 debugger/graphics_framebuffer.h 38 debugger/graphics_framebuffer.h
39 debugger/graphics_tracing.h
38 debugger/graphics_vertex_shader.h 40 debugger/graphics_vertex_shader.h
39 debugger/profiler.h 41 debugger/profiler.h
40 debugger/ramview.h 42 debugger/ramview.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 @@
13 * This is because the Pica breakpoint callbacks are called from a non-GUI thread, while 13 * This is because the Pica breakpoint callbacks are called from a non-GUI thread, while
14 * the widget usually wants to perform reactions in the GUI thread. 14 * the widget usually wants to perform reactions in the GUI thread.
15 */ 15 */
16class BreakPointObserverDock : public QDockWidget, private Pica::DebugContext::BreakPointObserver { 16class BreakPointObserverDock : public QDockWidget, protected Pica::DebugContext::BreakPointObserver {
17 Q_OBJECT 17 Q_OBJECT
18 18
19public: 19public:
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
55 framebuffer_format_control->addItem(tr("RGBA4")); 55 framebuffer_format_control->addItem(tr("RGBA4"));
56 framebuffer_format_control->addItem(tr("D16")); 56 framebuffer_format_control->addItem(tr("D16"));
57 framebuffer_format_control->addItem(tr("D24")); 57 framebuffer_format_control->addItem(tr("D24"));
58 framebuffer_format_control->addItem(tr("D24S8")); 58 framebuffer_format_control->addItem(tr("D24X8"));
59 framebuffer_format_control->addItem(tr("X24S8"));
60 framebuffer_format_control->addItem(tr("(unknown)"));
59 61
60 // TODO: This QLabel should shrink the image to the available space rather than just expanding... 62 // TODO: This QLabel should shrink the image to the available space rather than just expanding...
61 framebuffer_picture_label = new QLabel; 63 framebuffer_picture_label = new QLabel;
@@ -184,8 +186,32 @@ void GraphicsFramebufferWidget::OnUpdate()
184 framebuffer_address = framebuffer.GetColorBufferPhysicalAddress(); 186 framebuffer_address = framebuffer.GetColorBufferPhysicalAddress();
185 framebuffer_width = framebuffer.GetWidth(); 187 framebuffer_width = framebuffer.GetWidth();
186 framebuffer_height = framebuffer.GetHeight(); 188 framebuffer_height = framebuffer.GetHeight();
187 // TODO: It's unknown how this format is actually specified 189
188 framebuffer_format = Format::RGBA8; 190 switch (framebuffer.color_format) {
191 case Pica::Regs::ColorFormat::RGBA8:
192 framebuffer_format = Format::RGBA8;
193 break;
194
195 case Pica::Regs::ColorFormat::RGB8:
196 framebuffer_format = Format::RGB8;
197 break;
198
199 case Pica::Regs::ColorFormat::RGB5A1:
200 framebuffer_format = Format::RGB5A1;
201 break;
202
203 case Pica::Regs::ColorFormat::RGB565:
204 framebuffer_format = Format::RGB565;
205 break;
206
207 case Pica::Regs::ColorFormat::RGBA4:
208 framebuffer_format = Format::RGBA4;
209 break;
210
211 default:
212 framebuffer_format = Format::Unknown;
213 break;
214 }
189 215
190 break; 216 break;
191 } 217 }
@@ -197,7 +223,24 @@ void GraphicsFramebufferWidget::OnUpdate()
197 framebuffer_address = framebuffer.GetDepthBufferPhysicalAddress(); 223 framebuffer_address = framebuffer.GetDepthBufferPhysicalAddress();
198 framebuffer_width = framebuffer.GetWidth(); 224 framebuffer_width = framebuffer.GetWidth();
199 framebuffer_height = framebuffer.GetHeight(); 225 framebuffer_height = framebuffer.GetHeight();
200 framebuffer_format = Format::D16; 226
227 switch (framebuffer.depth_format) {
228 case Pica::Regs::DepthFormat::D16:
229 framebuffer_format = Format::D16;
230 break;
231
232 case Pica::Regs::DepthFormat::D24:
233 framebuffer_format = Format::D24;
234 break;
235
236 case Pica::Regs::DepthFormat::D24S8:
237 framebuffer_format = Format::D24X8;
238 break;
239
240 default:
241 framebuffer_format = Format::Unknown;
242 break;
243 }
201 244
202 break; 245 break;
203 } 246 }
@@ -258,7 +301,7 @@ void GraphicsFramebufferWidget::OnUpdate()
258 color.b() = (data >> 16) & 0xFF; 301 color.b() = (data >> 16) & 0xFF;
259 break; 302 break;
260 } 303 }
261 case Format::D24S8: 304 case Format::D24X8:
262 { 305 {
263 Math::Vec2<u32> data = Color::DecodeD24S8(pixel); 306 Math::Vec2<u32> data = Color::DecodeD24S8(pixel);
264 color.r() = data.x & 0xFF; 307 color.r() = data.x & 0xFF;
@@ -266,6 +309,12 @@ void GraphicsFramebufferWidget::OnUpdate()
266 color.b() = (data.x >> 16) & 0xFF; 309 color.b() = (data.x >> 16) & 0xFF;
267 break; 310 break;
268 } 311 }
312 case Format::X24S8:
313 {
314 Math::Vec2<u32> data = Color::DecodeD24S8(pixel);
315 color.r() = color.g() = color.b() = data.y;
316 break;
317 }
269 default: 318 default:
270 qDebug() << "Unknown fb color format " << static_cast<int>(framebuffer_format); 319 qDebug() << "Unknown fb color format " << static_cast<int>(framebuffer_format);
271 break; 320 break;
@@ -286,7 +335,8 @@ void GraphicsFramebufferWidget::OnUpdate()
286u32 GraphicsFramebufferWidget::BytesPerPixel(GraphicsFramebufferWidget::Format format) { 335u32 GraphicsFramebufferWidget::BytesPerPixel(GraphicsFramebufferWidget::Format format) {
287 switch (format) { 336 switch (format) {
288 case Format::RGBA8: 337 case Format::RGBA8:
289 case Format::D24S8: 338 case Format::D24X8:
339 case Format::X24S8:
290 return 4; 340 return 4;
291 case Format::RGB8: 341 case Format::RGB8:
292 case Format::D24: 342 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 {
35 RGBA4 = 4, 35 RGBA4 = 4,
36 D16 = 5, 36 D16 = 5,
37 D24 = 6, 37 D24 = 6,
38 D24S8 = 7 38 D24X8 = 7,
39 X24S8 = 8,
40 Unknown = 9
39 }; 41 };
40 42
41 static u32 BytesPerPixel(Format format); 43 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 @@
1// Copyright 2015 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <memory>
6
7#include <QBoxLayout>
8#include <QComboBox>
9#include <QFileDialog>
10#include <QLabel>
11#include <QMessageBox>
12#include <QPushButton>
13#include <QSpinBox>
14
15#include <boost/range/algorithm/copy.hpp>
16
17#include "core/hw/gpu.h"
18#include "core/hw/lcd.h"
19
20#include "video_core/pica.h"
21
22#include "nihstro/float24.h"
23
24#include "graphics_tracing.h"
25
26GraphicsTracingWidget::GraphicsTracingWidget(std::shared_ptr<Pica::DebugContext> debug_context,
27 QWidget* parent)
28 : BreakPointObserverDock(debug_context, tr("CiTrace Recorder"), parent) {
29
30 setObjectName("CiTracing");
31
32 QPushButton* start_recording = new QPushButton(tr("Start Recording"));
33 QPushButton* stop_recording = new QPushButton(QIcon::fromTheme("document-save"), tr("Stop and Save"));
34 QPushButton* abort_recording = new QPushButton(tr("Abort Recording"));
35
36 connect(this, SIGNAL(SetStartTracingButtonEnabled(bool)), start_recording, SLOT(setVisible(bool)));
37 connect(this, SIGNAL(SetStopTracingButtonEnabled(bool)), stop_recording, SLOT(setVisible(bool)));
38 connect(this, SIGNAL(SetAbortTracingButtonEnabled(bool)), abort_recording, SLOT(setVisible(bool)));
39 connect(start_recording, SIGNAL(clicked()), this, SLOT(StartRecording()));
40 connect(stop_recording, SIGNAL(clicked()), this, SLOT(StopRecording()));
41 connect(abort_recording, SIGNAL(clicked()), this, SLOT(AbortRecording()));
42
43 stop_recording->setVisible(false);
44 abort_recording->setVisible(false);
45
46 auto main_widget = new QWidget;
47 auto main_layout = new QVBoxLayout;
48 {
49 auto sub_layout = new QHBoxLayout;
50 sub_layout->addWidget(start_recording);
51 sub_layout->addWidget(stop_recording);
52 sub_layout->addWidget(abort_recording);
53 main_layout->addLayout(sub_layout);
54 }
55 main_widget->setLayout(main_layout);
56 setWidget(main_widget);
57}
58
59void GraphicsTracingWidget::StartRecording() {
60 auto context = context_weak.lock();
61 if (!context)
62 return;
63
64 auto shader_binary = Pica::g_state.vs.program_code;
65 auto swizzle_data = Pica::g_state.vs.swizzle_data;
66
67 // Encode floating point numbers to 24-bit values
68 // TODO: Drop this explicit conversion once we store float24 values bit-correctly internally.
69 std::array<uint32_t, 4 * 16> default_attributes;
70 for (unsigned i = 0; i < 16; ++i) {
71 for (unsigned comp = 0; comp < 3; ++comp) {
72 default_attributes[4 * i + comp] = nihstro::to_float24(Pica::g_state.vs.default_attributes[i][comp].ToFloat32());
73 }
74 }
75
76 std::array<uint32_t, 4 * 96> vs_float_uniforms;
77 for (unsigned i = 0; i < 96; ++i)
78 for (unsigned comp = 0; comp < 3; ++comp)
79 vs_float_uniforms[4 * i + comp] = nihstro::to_float24(Pica::g_state.vs.uniforms.f[i][comp].ToFloat32());
80
81 CiTrace::Recorder::InitialState state;
82 std::copy_n((u32*)&GPU::g_regs, sizeof(GPU::g_regs) / sizeof(u32), std::back_inserter(state.gpu_registers));
83 std::copy_n((u32*)&LCD::g_regs, sizeof(LCD::g_regs) / sizeof(u32), std::back_inserter(state.lcd_registers));
84 std::copy_n((u32*)&Pica::g_state.regs, sizeof(Pica::g_state.regs) / sizeof(u32), std::back_inserter(state.pica_registers));
85 boost::copy(default_attributes, std::back_inserter(state.default_attributes));
86 boost::copy(shader_binary, std::back_inserter(state.vs_program_binary));
87 boost::copy(swizzle_data, std::back_inserter(state.vs_swizzle_data));
88 boost::copy(vs_float_uniforms, std::back_inserter(state.vs_float_uniforms));
89 //boost::copy(TODO: Not implemented, std::back_inserter(state.gs_program_binary));
90 //boost::copy(TODO: Not implemented, std::back_inserter(state.gs_swizzle_data));
91 //boost::copy(TODO: Not implemented, std::back_inserter(state.gs_float_uniforms));
92
93 auto recorder = new CiTrace::Recorder(state);
94 context->recorder = std::shared_ptr<CiTrace::Recorder>(recorder);
95
96 emit SetStartTracingButtonEnabled(false);
97 emit SetStopTracingButtonEnabled(true);
98 emit SetAbortTracingButtonEnabled(true);
99}
100
101void GraphicsTracingWidget::StopRecording() {
102 auto context = context_weak.lock();
103 if (!context)
104 return;
105
106 QString filename = QFileDialog::getSaveFileName(this, tr("Save CiTrace"), "citrace.ctf",
107 tr("CiTrace File (*.ctf)"));
108
109 if (filename.isEmpty()) {
110 // If the user canceled the dialog, keep recording
111 return;
112 }
113
114 context->recorder->Finish(filename.toStdString());
115 context->recorder = nullptr;
116
117 emit SetStopTracingButtonEnabled(false);
118 emit SetAbortTracingButtonEnabled(false);
119 emit SetStartTracingButtonEnabled(true);
120}
121
122void GraphicsTracingWidget::AbortRecording() {
123 auto context = context_weak.lock();
124 if (!context)
125 return;
126
127 context->recorder = nullptr;
128
129 emit SetStopTracingButtonEnabled(false);
130 emit SetAbortTracingButtonEnabled(false);
131 emit SetStartTracingButtonEnabled(true);
132}
133
134void GraphicsTracingWidget::OnBreakPointHit(Pica::DebugContext::Event event, void* data) {
135 widget()->setEnabled(true);
136}
137
138void GraphicsTracingWidget::OnResumed() {
139 widget()->setEnabled(false);
140}
141
142void GraphicsTracingWidget::OnEmulationStarting(EmuThread* emu_thread) {
143 // Disable tracing starting/stopping until a GPU breakpoint is reached
144 widget()->setEnabled(false);
145}
146
147void GraphicsTracingWidget::OnEmulationStopping() {
148 // TODO: Is it safe to access the context here?
149
150 auto context = context_weak.lock();
151 if (!context)
152 return;
153
154
155 if (context->recorder) {
156 auto reply = QMessageBox::question(this, tr("CiTracing still active"),
157 tr("A CiTrace is still being recorded. Do you want to save it? If not, all recorded data will be discarded."),
158 QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
159
160 if (reply == QMessageBox::Yes) {
161 StopRecording();
162 } else {
163 AbortRecording();
164 }
165 }
166
167 // If the widget was disabled before, enable it now to allow starting
168 // tracing before starting the next emulation session
169 widget()->setEnabled(true);
170}
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 @@
1// Copyright 2015 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "graphics_breakpoint_observer.h"
8
9class EmuThread;
10
11class GraphicsTracingWidget : public BreakPointObserverDock {
12 Q_OBJECT
13
14public:
15 GraphicsTracingWidget(std::shared_ptr<Pica::DebugContext> debug_context, QWidget* parent = nullptr);
16
17private slots:
18 void StartRecording();
19 void StopRecording();
20 void AbortRecording();
21
22 void OnBreakPointHit(Pica::DebugContext::Event event, void* data) override;
23 void OnResumed() override;
24
25 void OnEmulationStarting(EmuThread* emu_thread);
26 void OnEmulationStopping();
27
28signals:
29 void SetStartTracingButtonEnabled(bool enable);
30 void SetStopTracingButtonEnabled(bool enable);
31 void SetAbortTracingButtonEnabled(bool enable);
32};
diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp
index d23bafafc..2746de779 100644
--- a/src/citra_qt/main.cpp
+++ b/src/citra_qt/main.cpp
@@ -32,6 +32,7 @@
32#include "debugger/graphics_breakpoints.h" 32#include "debugger/graphics_breakpoints.h"
33#include "debugger/graphics_cmdlists.h" 33#include "debugger/graphics_cmdlists.h"
34#include "debugger/graphics_framebuffer.h" 34#include "debugger/graphics_framebuffer.h"
35#include "debugger/graphics_tracing.h"
35#include "debugger/graphics_vertex_shader.h" 36#include "debugger/graphics_vertex_shader.h"
36#include "debugger/profiler.h" 37#include "debugger/profiler.h"
37 38
@@ -94,6 +95,10 @@ GMainWindow::GMainWindow() : emu_thread(nullptr)
94 addDockWidget(Qt::RightDockWidgetArea, graphicsVertexShaderWidget); 95 addDockWidget(Qt::RightDockWidgetArea, graphicsVertexShaderWidget);
95 graphicsVertexShaderWidget->hide(); 96 graphicsVertexShaderWidget->hide();
96 97
98 auto graphicsTracingWidget = new GraphicsTracingWidget(Pica::g_debug_context, this);
99 addDockWidget(Qt::RightDockWidgetArea, graphicsTracingWidget);
100 graphicsTracingWidget->hide();
101
97 QMenu* debug_menu = ui.menu_View->addMenu(tr("Debugging")); 102 QMenu* debug_menu = ui.menu_View->addMenu(tr("Debugging"));
98 debug_menu->addAction(profilerWidget->toggleViewAction()); 103 debug_menu->addAction(profilerWidget->toggleViewAction());
99 debug_menu->addAction(disasmWidget->toggleViewAction()); 104 debug_menu->addAction(disasmWidget->toggleViewAction());
@@ -104,6 +109,7 @@ GMainWindow::GMainWindow() : emu_thread(nullptr)
104 debug_menu->addAction(graphicsBreakpointsWidget->toggleViewAction()); 109 debug_menu->addAction(graphicsBreakpointsWidget->toggleViewAction());
105 debug_menu->addAction(graphicsFramebufferWidget->toggleViewAction()); 110 debug_menu->addAction(graphicsFramebufferWidget->toggleViewAction());
106 debug_menu->addAction(graphicsVertexShaderWidget->toggleViewAction()); 111 debug_menu->addAction(graphicsVertexShaderWidget->toggleViewAction());
112 debug_menu->addAction(graphicsTracingWidget->toggleViewAction());
107 113
108 // Set default UI state 114 // Set default UI state
109 // geometry: 55% of the window contents are in the upper screen half, 45% in the lower half 115 // 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)
148 connect(this, SIGNAL(EmulationStopping()), registersWidget, SLOT(OnEmulationStopping())); 154 connect(this, SIGNAL(EmulationStopping()), registersWidget, SLOT(OnEmulationStopping()));
149 connect(this, SIGNAL(EmulationStarting(EmuThread*)), render_window, SLOT(OnEmulationStarting(EmuThread*))); 155 connect(this, SIGNAL(EmulationStarting(EmuThread*)), render_window, SLOT(OnEmulationStarting(EmuThread*)));
150 connect(this, SIGNAL(EmulationStopping()), render_window, SLOT(OnEmulationStopping())); 156 connect(this, SIGNAL(EmulationStopping()), render_window, SLOT(OnEmulationStopping()));
157 connect(this, SIGNAL(EmulationStarting(EmuThread*)), graphicsTracingWidget, SLOT(OnEmulationStarting(EmuThread*)));
158 connect(this, SIGNAL(EmulationStopping()), graphicsTracingWidget, SLOT(OnEmulationStopping()));
159
151 160
152 // Setup hotkeys 161 // Setup hotkeys
153 RegisterHotkey("Main Window", "Load File", QKeySequence::Open); 162 RegisterHotkey("Main Window", "Load File", QKeySequence::Open);