summaryrefslogtreecommitdiff
path: root/src/citra_qt/debugger/graphics_framebuffer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/citra_qt/debugger/graphics_framebuffer.cpp')
-rw-r--r--src/citra_qt/debugger/graphics_framebuffer.cpp283
1 files changed, 283 insertions, 0 deletions
diff --git a/src/citra_qt/debugger/graphics_framebuffer.cpp b/src/citra_qt/debugger/graphics_framebuffer.cpp
new file mode 100644
index 000000000..dd41c3880
--- /dev/null
+++ b/src/citra_qt/debugger/graphics_framebuffer.cpp
@@ -0,0 +1,283 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <QBoxLayout>
6#include <QComboBox>
7#include <QDebug>
8#include <QLabel>
9#include <QMetaType>
10#include <QPushButton>
11#include <QSpinBox>
12
13#include "video_core/pica.h"
14
15#include "graphics_framebuffer.hxx"
16
17#include "util/spinbox.hxx"
18
19BreakPointObserverDock::BreakPointObserverDock(std::shared_ptr<Pica::DebugContext> debug_context,
20 const QString& title, QWidget* parent)
21 : QDockWidget(title, parent), BreakPointObserver(debug_context)
22{
23 qRegisterMetaType<Pica::DebugContext::Event>("Pica::DebugContext::Event");
24
25 connect(this, SIGNAL(Resumed()), this, SLOT(OnResumed()));
26
27 // NOTE: This signal is emitted from a non-GUI thread, but connect() takes
28 // care of delaying its handling to the GUI thread.
29 connect(this, SIGNAL(BreakPointHit(Pica::DebugContext::Event,void*)),
30 this, SLOT(OnBreakPointHit(Pica::DebugContext::Event,void*)),
31 Qt::BlockingQueuedConnection);
32}
33
34void BreakPointObserverDock::OnPicaBreakPointHit(Pica::DebugContext::Event event, void* data)
35{
36 emit BreakPointHit(event, data);
37}
38
39void BreakPointObserverDock::OnPicaResume()
40{
41 emit Resumed();
42}
43
44
45GraphicsFramebufferWidget::GraphicsFramebufferWidget(std::shared_ptr<Pica::DebugContext> debug_context,
46 QWidget* parent)
47 : BreakPointObserverDock(debug_context, tr("Pica Framebuffer"), parent),
48 framebuffer_source(Source::PicaTarget)
49{
50 setObjectName("PicaFramebuffer");
51
52 framebuffer_source_list = new QComboBox;
53 framebuffer_source_list->addItem(tr("Active Render Target"));
54 framebuffer_source_list->addItem(tr("Custom"));
55 framebuffer_source_list->setCurrentIndex(static_cast<int>(framebuffer_source));
56
57 framebuffer_address_control = new CSpinBox;
58 framebuffer_address_control->SetBase(16);
59 framebuffer_address_control->SetRange(0, 0xFFFFFFFF);
60 framebuffer_address_control->SetPrefix("0x");
61
62 framebuffer_width_control = new QSpinBox;
63 framebuffer_width_control->setMinimum(1);
64 framebuffer_width_control->setMaximum(std::numeric_limits<int>::max()); // TODO: Find actual maximum
65
66 framebuffer_height_control = new QSpinBox;
67 framebuffer_height_control->setMinimum(1);
68 framebuffer_height_control->setMaximum(std::numeric_limits<int>::max()); // TODO: Find actual maximum
69
70 framebuffer_format_control = new QComboBox;
71 framebuffer_format_control->addItem(tr("RGBA8"));
72 framebuffer_format_control->addItem(tr("RGB8"));
73 framebuffer_format_control->addItem(tr("RGBA5551"));
74 framebuffer_format_control->addItem(tr("RGB565"));
75 framebuffer_format_control->addItem(tr("RGBA4"));
76
77 // TODO: This QLabel should shrink the image to the available space rather than just expanding...
78 framebuffer_picture_label = new QLabel;
79
80 auto enlarge_button = new QPushButton(tr("Enlarge"));
81
82 // Connections
83 connect(this, SIGNAL(Update()), this, SLOT(OnUpdate()));
84 connect(framebuffer_source_list, SIGNAL(currentIndexChanged(int)), this, SLOT(OnFramebufferSourceChanged(int)));
85 connect(framebuffer_address_control, SIGNAL(ValueChanged(qint64)), this, SLOT(OnFramebufferAddressChanged(qint64)));
86 connect(framebuffer_width_control, SIGNAL(valueChanged(int)), this, SLOT(OnFramebufferWidthChanged(int)));
87 connect(framebuffer_height_control, SIGNAL(valueChanged(int)), this, SLOT(OnFramebufferHeightChanged(int)));
88 connect(framebuffer_format_control, SIGNAL(currentIndexChanged(int)), this, SLOT(OnFramebufferFormatChanged(int)));
89
90 auto main_widget = new QWidget;
91 auto main_layout = new QVBoxLayout;
92 {
93 auto sub_layout = new QHBoxLayout;
94 sub_layout->addWidget(new QLabel(tr("Source:")));
95 sub_layout->addWidget(framebuffer_source_list);
96 main_layout->addLayout(sub_layout);
97 }
98 {
99 auto sub_layout = new QHBoxLayout;
100 sub_layout->addWidget(new QLabel(tr("Virtual Address:")));
101 sub_layout->addWidget(framebuffer_address_control);
102 main_layout->addLayout(sub_layout);
103 }
104 {
105 auto sub_layout = new QHBoxLayout;
106 sub_layout->addWidget(new QLabel(tr("Width:")));
107 sub_layout->addWidget(framebuffer_width_control);
108 main_layout->addLayout(sub_layout);
109 }
110 {
111 auto sub_layout = new QHBoxLayout;
112 sub_layout->addWidget(new QLabel(tr("Height:")));
113 sub_layout->addWidget(framebuffer_height_control);
114 main_layout->addLayout(sub_layout);
115 }
116 {
117 auto sub_layout = new QHBoxLayout;
118 sub_layout->addWidget(new QLabel(tr("Format:")));
119 sub_layout->addWidget(framebuffer_format_control);
120 main_layout->addLayout(sub_layout);
121 }
122 main_layout->addWidget(framebuffer_picture_label);
123 main_layout->addWidget(enlarge_button);
124 main_widget->setLayout(main_layout);
125 setWidget(main_widget);
126
127 // Load current data - TODO: Make sure this works when emulation is not running
128 if (debug_context && debug_context->at_breakpoint)
129 emit Update();
130 widget()->setEnabled(false); // TODO: Only enable if currently at breakpoint
131}
132
133void GraphicsFramebufferWidget::OnBreakPointHit(Pica::DebugContext::Event event, void* data)
134{
135 emit Update();
136 widget()->setEnabled(true);
137}
138
139void GraphicsFramebufferWidget::OnResumed()
140{
141 widget()->setEnabled(false);
142}
143
144void GraphicsFramebufferWidget::OnFramebufferSourceChanged(int new_value)
145{
146 framebuffer_source = static_cast<Source>(new_value);
147 emit Update();
148}
149
150void GraphicsFramebufferWidget::OnFramebufferAddressChanged(qint64 new_value)
151{
152 if (framebuffer_address != new_value) {
153 framebuffer_address = static_cast<unsigned>(new_value);
154
155 framebuffer_source_list->setCurrentIndex(static_cast<int>(Source::Custom));
156 emit Update();
157 }
158}
159
160void GraphicsFramebufferWidget::OnFramebufferWidthChanged(int new_value)
161{
162 if (framebuffer_width != new_value) {
163 framebuffer_width = new_value;
164
165 framebuffer_source_list->setCurrentIndex(static_cast<int>(Source::Custom));
166 emit Update();
167 }
168}
169
170void GraphicsFramebufferWidget::OnFramebufferHeightChanged(int new_value)
171{
172 if (framebuffer_height != new_value) {
173 framebuffer_height = new_value;
174
175 framebuffer_source_list->setCurrentIndex(static_cast<int>(Source::Custom));
176 emit Update();
177 }
178}
179
180void GraphicsFramebufferWidget::OnFramebufferFormatChanged(int new_value)
181{
182 if (framebuffer_format != static_cast<Format>(new_value)) {
183 framebuffer_format = static_cast<Format>(new_value);
184
185 framebuffer_source_list->setCurrentIndex(static_cast<int>(Source::Custom));
186 emit Update();
187 }
188}
189
190void GraphicsFramebufferWidget::OnUpdate()
191{
192 QPixmap pixmap;
193
194 switch (framebuffer_source) {
195 case Source::PicaTarget:
196 {
197 // TODO: Store a reference to the registers in the debug context instead of accessing them directly...
198
199 auto framebuffer = Pica::registers.framebuffer;
200 using Framebuffer = decltype(framebuffer);
201
202 framebuffer_address = framebuffer.GetColorBufferPhysicalAddress();
203 framebuffer_width = framebuffer.GetWidth();
204 framebuffer_height = framebuffer.GetHeight();
205 framebuffer_format = static_cast<Format>(framebuffer.color_format);
206
207 break;
208 }
209
210 case Source::Custom:
211 {
212 // Keep user-specified values
213 break;
214 }
215
216 default:
217 qDebug() << "Unknown framebuffer source " << static_cast<int>(framebuffer_source);
218 break;
219 }
220
221 // TODO: Implement a good way to visualize alpha components!
222 // TODO: Unify this decoding code with the texture decoder
223 switch (framebuffer_format) {
224 case Format::RGBA8:
225 {
226 QImage decoded_image(framebuffer_width, framebuffer_height, QImage::Format_ARGB32);
227 u32* color_buffer = (u32*)Memory::GetPointer(Pica::PAddrToVAddr(framebuffer_address));
228 for (unsigned y = 0; y < framebuffer_height; ++y) {
229 for (unsigned x = 0; x < framebuffer_width; ++x) {
230 u32 value = *(color_buffer + x + y * framebuffer_width);
231
232 decoded_image.setPixel(x, y, qRgba((value >> 16) & 0xFF, (value >> 8) & 0xFF, value & 0xFF, 255/*value >> 24*/));
233 }
234 }
235 pixmap = QPixmap::fromImage(decoded_image);
236 break;
237 }
238
239 case Format::RGB8:
240 {
241 QImage decoded_image(framebuffer_width, framebuffer_height, QImage::Format_ARGB32);
242 u8* color_buffer = Memory::GetPointer(Pica::PAddrToVAddr(framebuffer_address));
243 for (unsigned y = 0; y < framebuffer_height; ++y) {
244 for (unsigned x = 0; x < framebuffer_width; ++x) {
245 u8* pixel_pointer = color_buffer + x * 3 + y * 3 * framebuffer_width;
246
247 decoded_image.setPixel(x, y, qRgba(pixel_pointer[0], pixel_pointer[1], pixel_pointer[2], 255/*value >> 24*/));
248 }
249 }
250 pixmap = QPixmap::fromImage(decoded_image);
251 break;
252 }
253
254 case Format::RGBA5551:
255 {
256 QImage decoded_image(framebuffer_width, framebuffer_height, QImage::Format_ARGB32);
257 u32* color_buffer = (u32*)Memory::GetPointer(Pica::PAddrToVAddr(framebuffer_address));
258 for (unsigned y = 0; y < framebuffer_height; ++y) {
259 for (unsigned x = 0; x < framebuffer_width; ++x) {
260 u16 value = *(u16*)(((u8*)color_buffer) + x * 2 + y * framebuffer_width * 2);
261 u8 r = (value >> 11) & 0x1F;
262 u8 g = (value >> 6) & 0x1F;
263 u8 b = (value >> 1) & 0x1F;
264 u8 a = value & 1;
265
266 decoded_image.setPixel(x, y, qRgba(r, g, b, 255/*a*/));
267 }
268 }
269 pixmap = QPixmap::fromImage(decoded_image);
270 break;
271 }
272
273 default:
274 qDebug() << "Unknown fb color format " << static_cast<int>(framebuffer_format);
275 break;
276 }
277
278 framebuffer_address_control->SetValue(framebuffer_address);
279 framebuffer_width_control->setValue(framebuffer_width);
280 framebuffer_height_control->setValue(framebuffer_height);
281 framebuffer_format_control->setCurrentIndex(static_cast<int>(framebuffer_format));
282 framebuffer_picture_label->setPixmap(pixmap);
283}