diff options
| author | 2019-04-09 21:20:02 -0400 | |
|---|---|---|
| committer | 2019-04-09 21:20:02 -0400 | |
| commit | f46c3164e7c5cbc3272f762ea4e4f38f817396ac (patch) | |
| tree | e1ab9aee631a92d951e45fb5848777ac159cb699 /src | |
| parent | Merge pull request #2354 from lioncash/header (diff) | |
| parent | yuzu/debugger: Remove graphics surface viewer (diff) | |
| download | yuzu-f46c3164e7c5cbc3272f762ea4e4f38f817396ac.tar.gz yuzu-f46c3164e7c5cbc3272f762ea4e4f38f817396ac.tar.xz yuzu-f46c3164e7c5cbc3272f762ea4e4f38f817396ac.zip | |
Merge pull request #2353 from lioncash/surface
yuzu/debugger: Remove graphics surface viewer
Diffstat (limited to 'src')
| -rw-r--r-- | src/yuzu/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/yuzu/debugger/graphics/graphics_surface.cpp | 516 | ||||
| -rw-r--r-- | src/yuzu/debugger/graphics/graphics_surface.h | 96 | ||||
| -rw-r--r-- | src/yuzu/main.cpp | 6 | ||||
| -rw-r--r-- | src/yuzu/main.h | 2 |
5 files changed, 0 insertions, 622 deletions
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index 732a1bf89..2eb86d6e5 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt | |||
| @@ -56,8 +56,6 @@ add_executable(yuzu | |||
| 56 | debugger/graphics/graphics_breakpoints.cpp | 56 | debugger/graphics/graphics_breakpoints.cpp |
| 57 | debugger/graphics/graphics_breakpoints.h | 57 | debugger/graphics/graphics_breakpoints.h |
| 58 | debugger/graphics/graphics_breakpoints_p.h | 58 | debugger/graphics/graphics_breakpoints_p.h |
| 59 | debugger/graphics/graphics_surface.cpp | ||
| 60 | debugger/graphics/graphics_surface.h | ||
| 61 | debugger/console.cpp | 59 | debugger/console.cpp |
| 62 | debugger/console.h | 60 | debugger/console.h |
| 63 | debugger/profiler.cpp | 61 | debugger/profiler.cpp |
diff --git a/src/yuzu/debugger/graphics/graphics_surface.cpp b/src/yuzu/debugger/graphics/graphics_surface.cpp deleted file mode 100644 index f2d14becf..000000000 --- a/src/yuzu/debugger/graphics/graphics_surface.cpp +++ /dev/null | |||
| @@ -1,516 +0,0 @@ | |||
| 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 <QFileDialog> | ||
| 9 | #include <QLabel> | ||
| 10 | #include <QMessageBox> | ||
| 11 | #include <QMouseEvent> | ||
| 12 | #include <QPushButton> | ||
| 13 | #include <QScrollArea> | ||
| 14 | #include <QSpinBox> | ||
| 15 | #include "common/vector_math.h" | ||
| 16 | #include "core/core.h" | ||
| 17 | #include "core/memory.h" | ||
| 18 | #include "video_core/engines/maxwell_3d.h" | ||
| 19 | #include "video_core/gpu.h" | ||
| 20 | #include "video_core/textures/decoders.h" | ||
| 21 | #include "video_core/textures/texture.h" | ||
| 22 | #include "yuzu/debugger/graphics/graphics_surface.h" | ||
| 23 | #include "yuzu/util/spinbox.h" | ||
| 24 | |||
| 25 | static Tegra::Texture::TextureFormat ConvertToTextureFormat( | ||
| 26 | Tegra::RenderTargetFormat render_target_format) { | ||
| 27 | switch (render_target_format) { | ||
| 28 | case Tegra::RenderTargetFormat::RGBA8_UNORM: | ||
| 29 | return Tegra::Texture::TextureFormat::A8R8G8B8; | ||
| 30 | case Tegra::RenderTargetFormat::RGB10_A2_UNORM: | ||
| 31 | return Tegra::Texture::TextureFormat::A2B10G10R10; | ||
| 32 | default: | ||
| 33 | UNIMPLEMENTED_MSG("Unimplemented RT format"); | ||
| 34 | return Tegra::Texture::TextureFormat::A8R8G8B8; | ||
| 35 | } | ||
| 36 | } | ||
| 37 | |||
| 38 | SurfacePicture::SurfacePicture(QWidget* parent, GraphicsSurfaceWidget* surface_widget_) | ||
| 39 | : QLabel(parent), surface_widget(surface_widget_) {} | ||
| 40 | |||
| 41 | SurfacePicture::~SurfacePicture() = default; | ||
| 42 | |||
| 43 | void SurfacePicture::mousePressEvent(QMouseEvent* event) { | ||
| 44 | // Only do something while the left mouse button is held down | ||
| 45 | if (!(event->buttons() & Qt::LeftButton)) | ||
| 46 | return; | ||
| 47 | |||
| 48 | if (pixmap() == nullptr) | ||
| 49 | return; | ||
| 50 | |||
| 51 | if (surface_widget) | ||
| 52 | surface_widget->Pick(event->x() * pixmap()->width() / width(), | ||
| 53 | event->y() * pixmap()->height() / height()); | ||
| 54 | } | ||
| 55 | |||
| 56 | void SurfacePicture::mouseMoveEvent(QMouseEvent* event) { | ||
| 57 | // We also want to handle the event if the user moves the mouse while holding down the LMB | ||
| 58 | mousePressEvent(event); | ||
| 59 | } | ||
| 60 | |||
| 61 | GraphicsSurfaceWidget::GraphicsSurfaceWidget(std::shared_ptr<Tegra::DebugContext> debug_context, | ||
| 62 | QWidget* parent) | ||
| 63 | : BreakPointObserverDock(debug_context, tr("Maxwell Surface Viewer"), parent), | ||
| 64 | surface_source(Source::RenderTarget0) { | ||
| 65 | setObjectName("MaxwellSurface"); | ||
| 66 | |||
| 67 | surface_source_list = new QComboBox; | ||
| 68 | surface_source_list->addItem(tr("Render Target 0")); | ||
| 69 | surface_source_list->addItem(tr("Render Target 1")); | ||
| 70 | surface_source_list->addItem(tr("Render Target 2")); | ||
| 71 | surface_source_list->addItem(tr("Render Target 3")); | ||
| 72 | surface_source_list->addItem(tr("Render Target 4")); | ||
| 73 | surface_source_list->addItem(tr("Render Target 5")); | ||
| 74 | surface_source_list->addItem(tr("Render Target 6")); | ||
| 75 | surface_source_list->addItem(tr("Render Target 7")); | ||
| 76 | surface_source_list->addItem(tr("Z Buffer")); | ||
| 77 | surface_source_list->addItem(tr("Custom")); | ||
| 78 | surface_source_list->setCurrentIndex(static_cast<int>(surface_source)); | ||
| 79 | |||
| 80 | surface_address_control = new CSpinBox; | ||
| 81 | surface_address_control->SetBase(16); | ||
| 82 | surface_address_control->SetRange(0, 0x7FFFFFFFFFFFFFFF); | ||
| 83 | surface_address_control->SetPrefix("0x"); | ||
| 84 | |||
| 85 | unsigned max_dimension = 16384; // TODO: Find actual maximum | ||
| 86 | |||
| 87 | surface_width_control = new QSpinBox; | ||
| 88 | surface_width_control->setRange(0, max_dimension); | ||
| 89 | |||
| 90 | surface_height_control = new QSpinBox; | ||
| 91 | surface_height_control->setRange(0, max_dimension); | ||
| 92 | |||
| 93 | surface_picker_x_control = new QSpinBox; | ||
| 94 | surface_picker_x_control->setRange(0, max_dimension - 1); | ||
| 95 | |||
| 96 | surface_picker_y_control = new QSpinBox; | ||
| 97 | surface_picker_y_control->setRange(0, max_dimension - 1); | ||
| 98 | |||
| 99 | // clang-format off | ||
| 100 | // Color formats sorted by Maxwell texture format index | ||
| 101 | const QStringList surface_formats{ | ||
| 102 | tr("None"), | ||
| 103 | QStringLiteral("R32_G32_B32_A32"), | ||
| 104 | QStringLiteral("R32_G32_B32"), | ||
| 105 | QStringLiteral("R16_G16_B16_A16"), | ||
| 106 | QStringLiteral("R32_G32"), | ||
| 107 | QStringLiteral("R32_B24G8"), | ||
| 108 | QStringLiteral("ETC2_RGB"), | ||
| 109 | QStringLiteral("X8B8G8R8"), | ||
| 110 | QStringLiteral("A8R8G8B8"), | ||
| 111 | QStringLiteral("A2B10G10R10"), | ||
| 112 | QStringLiteral("ETC2_RGB_PTA"), | ||
| 113 | QStringLiteral("ETC2_RGBA"), | ||
| 114 | QStringLiteral("R16_G16"), | ||
| 115 | QStringLiteral("G8R24"), | ||
| 116 | QStringLiteral("G24R8"), | ||
| 117 | QStringLiteral("R32"), | ||
| 118 | QStringLiteral("BC6H_SF16"), | ||
| 119 | QStringLiteral("BC6H_UF16"), | ||
| 120 | QStringLiteral("A4B4G4R4"), | ||
| 121 | QStringLiteral("A5B5G5R1"), | ||
| 122 | QStringLiteral("A1B5G5R5"), | ||
| 123 | QStringLiteral("B5G6R5"), | ||
| 124 | QStringLiteral("B6G5R5"), | ||
| 125 | QStringLiteral("BC7U"), | ||
| 126 | QStringLiteral("G8R8"), | ||
| 127 | QStringLiteral("EAC"), | ||
| 128 | QStringLiteral("EACX2"), | ||
| 129 | QStringLiteral("R16"), | ||
| 130 | QStringLiteral("Y8_VIDEO"), | ||
| 131 | QStringLiteral("R8"), | ||
| 132 | QStringLiteral("G4R4"), | ||
| 133 | QStringLiteral("R1"), | ||
| 134 | QStringLiteral("E5B9G9R9_SHAREDEXP"), | ||
| 135 | QStringLiteral("BF10GF11RF11"), | ||
| 136 | QStringLiteral("G8B8G8R8"), | ||
| 137 | QStringLiteral("B8G8R8G8"), | ||
| 138 | QStringLiteral("DXT1"), | ||
| 139 | QStringLiteral("DXT23"), | ||
| 140 | QStringLiteral("DXT45"), | ||
| 141 | QStringLiteral("DXN1"), | ||
| 142 | QStringLiteral("DXN2"), | ||
| 143 | QStringLiteral("Z24S8"), | ||
| 144 | QStringLiteral("X8Z24"), | ||
| 145 | QStringLiteral("S8Z24"), | ||
| 146 | QStringLiteral("X4V4Z24__COV4R4V"), | ||
| 147 | QStringLiteral("X4V4Z24__COV8R8V"), | ||
| 148 | QStringLiteral("V8Z24__COV4R12V"), | ||
| 149 | QStringLiteral("ZF32"), | ||
| 150 | QStringLiteral("ZF32_X24S8"), | ||
| 151 | QStringLiteral("X8Z24_X20V4S8__COV4R4V"), | ||
| 152 | QStringLiteral("X8Z24_X20V4S8__COV8R8V"), | ||
| 153 | QStringLiteral("ZF32_X20V4X8__COV4R4V"), | ||
| 154 | QStringLiteral("ZF32_X20V4X8__COV8R8V"), | ||
| 155 | QStringLiteral("ZF32_X20V4S8__COV4R4V"), | ||
| 156 | QStringLiteral("ZF32_X20V4S8__COV8R8V"), | ||
| 157 | QStringLiteral("X8Z24_X16V8S8__COV4R12V"), | ||
| 158 | QStringLiteral("ZF32_X16V8X8__COV4R12V"), | ||
| 159 | QStringLiteral("ZF32_X16V8S8__COV4R12V"), | ||
| 160 | QStringLiteral("Z16"), | ||
| 161 | QStringLiteral("V8Z24__COV8R24V"), | ||
| 162 | QStringLiteral("X8Z24_X16V8S8__COV8R24V"), | ||
| 163 | QStringLiteral("ZF32_X16V8X8__COV8R24V"), | ||
| 164 | QStringLiteral("ZF32_X16V8S8__COV8R24V"), | ||
| 165 | QStringLiteral("ASTC_2D_4X4"), | ||
| 166 | QStringLiteral("ASTC_2D_5X5"), | ||
| 167 | QStringLiteral("ASTC_2D_6X6"), | ||
| 168 | QStringLiteral("ASTC_2D_8X8"), | ||
| 169 | QStringLiteral("ASTC_2D_10X10"), | ||
| 170 | QStringLiteral("ASTC_2D_12X12"), | ||
| 171 | QStringLiteral("ASTC_2D_5X4"), | ||
| 172 | QStringLiteral("ASTC_2D_6X5"), | ||
| 173 | QStringLiteral("ASTC_2D_8X6"), | ||
| 174 | QStringLiteral("ASTC_2D_10X8"), | ||
| 175 | QStringLiteral("ASTC_2D_12X10"), | ||
| 176 | QStringLiteral("ASTC_2D_8X5"), | ||
| 177 | QStringLiteral("ASTC_2D_10X5"), | ||
| 178 | QStringLiteral("ASTC_2D_10X6"), | ||
| 179 | }; | ||
| 180 | // clang-format on | ||
| 181 | |||
| 182 | surface_format_control = new QComboBox; | ||
| 183 | surface_format_control->addItems(surface_formats); | ||
| 184 | |||
| 185 | surface_info_label = new QLabel(); | ||
| 186 | surface_info_label->setWordWrap(true); | ||
| 187 | |||
| 188 | surface_picture_label = new SurfacePicture(0, this); | ||
| 189 | surface_picture_label->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); | ||
| 190 | surface_picture_label->setAlignment(Qt::AlignLeft | Qt::AlignTop); | ||
| 191 | surface_picture_label->setScaledContents(false); | ||
| 192 | |||
| 193 | auto scroll_area = new QScrollArea(); | ||
| 194 | scroll_area->setBackgroundRole(QPalette::Dark); | ||
| 195 | scroll_area->setWidgetResizable(false); | ||
| 196 | scroll_area->setWidget(surface_picture_label); | ||
| 197 | |||
| 198 | save_surface = new QPushButton(QIcon::fromTheme("document-save"), tr("Save")); | ||
| 199 | |||
| 200 | // Connections | ||
| 201 | connect(this, &GraphicsSurfaceWidget::Update, this, &GraphicsSurfaceWidget::OnUpdate); | ||
| 202 | connect(surface_source_list, qOverload<int>(&QComboBox::currentIndexChanged), this, | ||
| 203 | &GraphicsSurfaceWidget::OnSurfaceSourceChanged); | ||
| 204 | connect(surface_address_control, &CSpinBox::ValueChanged, this, | ||
| 205 | &GraphicsSurfaceWidget::OnSurfaceAddressChanged); | ||
| 206 | connect(surface_width_control, qOverload<int>(&QSpinBox::valueChanged), this, | ||
| 207 | &GraphicsSurfaceWidget::OnSurfaceWidthChanged); | ||
| 208 | connect(surface_height_control, qOverload<int>(&QSpinBox::valueChanged), this, | ||
| 209 | &GraphicsSurfaceWidget::OnSurfaceHeightChanged); | ||
| 210 | connect(surface_format_control, qOverload<int>(&QComboBox::currentIndexChanged), this, | ||
| 211 | &GraphicsSurfaceWidget::OnSurfaceFormatChanged); | ||
| 212 | connect(surface_picker_x_control, qOverload<int>(&QSpinBox::valueChanged), this, | ||
| 213 | &GraphicsSurfaceWidget::OnSurfacePickerXChanged); | ||
| 214 | connect(surface_picker_y_control, qOverload<int>(&QSpinBox::valueChanged), this, | ||
| 215 | &GraphicsSurfaceWidget::OnSurfacePickerYChanged); | ||
| 216 | connect(save_surface, &QPushButton::clicked, this, &GraphicsSurfaceWidget::SaveSurface); | ||
| 217 | |||
| 218 | auto main_widget = new QWidget; | ||
| 219 | auto main_layout = new QVBoxLayout; | ||
| 220 | { | ||
| 221 | auto sub_layout = new QHBoxLayout; | ||
| 222 | sub_layout->addWidget(new QLabel(tr("Source:"))); | ||
| 223 | sub_layout->addWidget(surface_source_list); | ||
| 224 | main_layout->addLayout(sub_layout); | ||
| 225 | } | ||
| 226 | { | ||
| 227 | auto sub_layout = new QHBoxLayout; | ||
| 228 | sub_layout->addWidget(new QLabel(tr("GPU Address:"))); | ||
| 229 | sub_layout->addWidget(surface_address_control); | ||
| 230 | main_layout->addLayout(sub_layout); | ||
| 231 | } | ||
| 232 | { | ||
| 233 | auto sub_layout = new QHBoxLayout; | ||
| 234 | sub_layout->addWidget(new QLabel(tr("Width:"))); | ||
| 235 | sub_layout->addWidget(surface_width_control); | ||
| 236 | main_layout->addLayout(sub_layout); | ||
| 237 | } | ||
| 238 | { | ||
| 239 | auto sub_layout = new QHBoxLayout; | ||
| 240 | sub_layout->addWidget(new QLabel(tr("Height:"))); | ||
| 241 | sub_layout->addWidget(surface_height_control); | ||
| 242 | main_layout->addLayout(sub_layout); | ||
| 243 | } | ||
| 244 | { | ||
| 245 | auto sub_layout = new QHBoxLayout; | ||
| 246 | sub_layout->addWidget(new QLabel(tr("Format:"))); | ||
| 247 | sub_layout->addWidget(surface_format_control); | ||
| 248 | main_layout->addLayout(sub_layout); | ||
| 249 | } | ||
| 250 | main_layout->addWidget(scroll_area); | ||
| 251 | |||
| 252 | auto info_layout = new QHBoxLayout; | ||
| 253 | { | ||
| 254 | auto xy_layout = new QVBoxLayout; | ||
| 255 | { | ||
| 256 | { | ||
| 257 | auto sub_layout = new QHBoxLayout; | ||
| 258 | sub_layout->addWidget(new QLabel(tr("X:"))); | ||
| 259 | sub_layout->addWidget(surface_picker_x_control); | ||
| 260 | xy_layout->addLayout(sub_layout); | ||
| 261 | } | ||
| 262 | { | ||
| 263 | auto sub_layout = new QHBoxLayout; | ||
| 264 | sub_layout->addWidget(new QLabel(tr("Y:"))); | ||
| 265 | sub_layout->addWidget(surface_picker_y_control); | ||
| 266 | xy_layout->addLayout(sub_layout); | ||
| 267 | } | ||
| 268 | } | ||
| 269 | info_layout->addLayout(xy_layout); | ||
| 270 | surface_info_label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); | ||
| 271 | info_layout->addWidget(surface_info_label); | ||
| 272 | } | ||
| 273 | main_layout->addLayout(info_layout); | ||
| 274 | |||
| 275 | main_layout->addWidget(save_surface); | ||
| 276 | main_widget->setLayout(main_layout); | ||
| 277 | setWidget(main_widget); | ||
| 278 | |||
| 279 | // Load current data - TODO: Make sure this works when emulation is not running | ||
| 280 | if (debug_context && debug_context->at_breakpoint) { | ||
| 281 | emit Update(); | ||
| 282 | widget()->setEnabled(debug_context->at_breakpoint); | ||
| 283 | } else { | ||
| 284 | widget()->setEnabled(false); | ||
| 285 | } | ||
| 286 | } | ||
| 287 | |||
| 288 | void GraphicsSurfaceWidget::OnBreakPointHit(Tegra::DebugContext::Event event, void* data) { | ||
| 289 | emit Update(); | ||
| 290 | widget()->setEnabled(true); | ||
| 291 | } | ||
| 292 | |||
| 293 | void GraphicsSurfaceWidget::OnResumed() { | ||
| 294 | widget()->setEnabled(false); | ||
| 295 | } | ||
| 296 | |||
| 297 | void GraphicsSurfaceWidget::OnSurfaceSourceChanged(int new_value) { | ||
| 298 | surface_source = static_cast<Source>(new_value); | ||
| 299 | emit Update(); | ||
| 300 | } | ||
| 301 | |||
| 302 | void GraphicsSurfaceWidget::OnSurfaceAddressChanged(qint64 new_value) { | ||
| 303 | if (surface_address != new_value) { | ||
| 304 | surface_address = static_cast<GPUVAddr>(new_value); | ||
| 305 | |||
| 306 | surface_source_list->setCurrentIndex(static_cast<int>(Source::Custom)); | ||
| 307 | emit Update(); | ||
| 308 | } | ||
| 309 | } | ||
| 310 | |||
| 311 | void GraphicsSurfaceWidget::OnSurfaceWidthChanged(int new_value) { | ||
| 312 | if (surface_width != static_cast<unsigned>(new_value)) { | ||
| 313 | surface_width = static_cast<unsigned>(new_value); | ||
| 314 | |||
| 315 | surface_source_list->setCurrentIndex(static_cast<int>(Source::Custom)); | ||
| 316 | emit Update(); | ||
| 317 | } | ||
| 318 | } | ||
| 319 | |||
| 320 | void GraphicsSurfaceWidget::OnSurfaceHeightChanged(int new_value) { | ||
| 321 | if (surface_height != static_cast<unsigned>(new_value)) { | ||
| 322 | surface_height = static_cast<unsigned>(new_value); | ||
| 323 | |||
| 324 | surface_source_list->setCurrentIndex(static_cast<int>(Source::Custom)); | ||
| 325 | emit Update(); | ||
| 326 | } | ||
| 327 | } | ||
| 328 | |||
| 329 | void GraphicsSurfaceWidget::OnSurfaceFormatChanged(int new_value) { | ||
| 330 | if (surface_format != static_cast<Tegra::Texture::TextureFormat>(new_value)) { | ||
| 331 | surface_format = static_cast<Tegra::Texture::TextureFormat>(new_value); | ||
| 332 | |||
| 333 | surface_source_list->setCurrentIndex(static_cast<int>(Source::Custom)); | ||
| 334 | emit Update(); | ||
| 335 | } | ||
| 336 | } | ||
| 337 | |||
| 338 | void GraphicsSurfaceWidget::OnSurfacePickerXChanged(int new_value) { | ||
| 339 | if (surface_picker_x != new_value) { | ||
| 340 | surface_picker_x = new_value; | ||
| 341 | Pick(surface_picker_x, surface_picker_y); | ||
| 342 | } | ||
| 343 | } | ||
| 344 | |||
| 345 | void GraphicsSurfaceWidget::OnSurfacePickerYChanged(int new_value) { | ||
| 346 | if (surface_picker_y != new_value) { | ||
| 347 | surface_picker_y = new_value; | ||
| 348 | Pick(surface_picker_x, surface_picker_y); | ||
| 349 | } | ||
| 350 | } | ||
| 351 | |||
| 352 | void GraphicsSurfaceWidget::Pick(int x, int y) { | ||
| 353 | surface_picker_x_control->setValue(x); | ||
| 354 | surface_picker_y_control->setValue(y); | ||
| 355 | |||
| 356 | if (x < 0 || x >= static_cast<int>(surface_width) || y < 0 || | ||
| 357 | y >= static_cast<int>(surface_height)) { | ||
| 358 | surface_info_label->setText(tr("Pixel out of bounds")); | ||
| 359 | surface_info_label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); | ||
| 360 | return; | ||
| 361 | } | ||
| 362 | |||
| 363 | surface_info_label->setText(QString("Raw: <Unimplemented>\n(%1)").arg("<Unimplemented>")); | ||
| 364 | surface_info_label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); | ||
| 365 | } | ||
| 366 | |||
| 367 | void GraphicsSurfaceWidget::OnUpdate() { | ||
| 368 | auto& gpu = Core::System::GetInstance().GPU(); | ||
| 369 | |||
| 370 | QPixmap pixmap; | ||
| 371 | |||
| 372 | switch (surface_source) { | ||
| 373 | case Source::RenderTarget0: | ||
| 374 | case Source::RenderTarget1: | ||
| 375 | case Source::RenderTarget2: | ||
| 376 | case Source::RenderTarget3: | ||
| 377 | case Source::RenderTarget4: | ||
| 378 | case Source::RenderTarget5: | ||
| 379 | case Source::RenderTarget6: | ||
| 380 | case Source::RenderTarget7: { | ||
| 381 | // TODO: Store a reference to the registers in the debug context instead of accessing them | ||
| 382 | // directly... | ||
| 383 | |||
| 384 | const auto& registers = gpu.Maxwell3D().regs; | ||
| 385 | const auto& rt = registers.rt[static_cast<std::size_t>(surface_source) - | ||
| 386 | static_cast<std::size_t>(Source::RenderTarget0)]; | ||
| 387 | |||
| 388 | surface_address = rt.Address(); | ||
| 389 | surface_width = rt.width; | ||
| 390 | surface_height = rt.height; | ||
| 391 | if (rt.format != Tegra::RenderTargetFormat::NONE) { | ||
| 392 | surface_format = ConvertToTextureFormat(rt.format); | ||
| 393 | } | ||
| 394 | |||
| 395 | break; | ||
| 396 | } | ||
| 397 | |||
| 398 | case Source::Custom: { | ||
| 399 | // Keep user-specified values | ||
| 400 | break; | ||
| 401 | } | ||
| 402 | |||
| 403 | default: | ||
| 404 | qDebug() << "Unknown surface source " << static_cast<int>(surface_source); | ||
| 405 | break; | ||
| 406 | } | ||
| 407 | |||
| 408 | surface_address_control->SetValue(surface_address); | ||
| 409 | surface_width_control->setValue(surface_width); | ||
| 410 | surface_height_control->setValue(surface_height); | ||
| 411 | surface_format_control->setCurrentIndex(static_cast<int>(surface_format)); | ||
| 412 | |||
| 413 | if (surface_address == 0) { | ||
| 414 | surface_picture_label->hide(); | ||
| 415 | surface_info_label->setText(tr("(invalid surface address)")); | ||
| 416 | surface_info_label->setAlignment(Qt::AlignCenter); | ||
| 417 | surface_picker_x_control->setEnabled(false); | ||
| 418 | surface_picker_y_control->setEnabled(false); | ||
| 419 | save_surface->setEnabled(false); | ||
| 420 | return; | ||
| 421 | } | ||
| 422 | |||
| 423 | // TODO: Implement a good way to visualize alpha components! | ||
| 424 | |||
| 425 | QImage decoded_image(surface_width, surface_height, QImage::Format_ARGB32); | ||
| 426 | |||
| 427 | // TODO(bunnei): Will not work with BCn formats that swizzle 4x4 tiles. | ||
| 428 | // Needs to be fixed if we plan to use this feature more, otherwise we may remove it. | ||
| 429 | auto unswizzled_data = Tegra::Texture::UnswizzleTexture( | ||
| 430 | gpu.MemoryManager().GetPointer(surface_address), 1, 1, | ||
| 431 | Tegra::Texture::BytesPerPixel(surface_format), surface_width, surface_height, 1U); | ||
| 432 | |||
| 433 | auto texture_data = Tegra::Texture::DecodeTexture(unswizzled_data, surface_format, | ||
| 434 | surface_width, surface_height); | ||
| 435 | |||
| 436 | surface_picture_label->show(); | ||
| 437 | |||
| 438 | for (unsigned int y = 0; y < surface_height; ++y) { | ||
| 439 | for (unsigned int x = 0; x < surface_width; ++x) { | ||
| 440 | Common::Vec4<u8> color; | ||
| 441 | color[0] = texture_data[x + y * surface_width + 0]; | ||
| 442 | color[1] = texture_data[x + y * surface_width + 1]; | ||
| 443 | color[2] = texture_data[x + y * surface_width + 2]; | ||
| 444 | color[3] = texture_data[x + y * surface_width + 3]; | ||
| 445 | decoded_image.setPixel(x, y, qRgba(color.r(), color.g(), color.b(), color.a())); | ||
| 446 | } | ||
| 447 | } | ||
| 448 | |||
| 449 | pixmap = QPixmap::fromImage(decoded_image); | ||
| 450 | surface_picture_label->setPixmap(pixmap); | ||
| 451 | surface_picture_label->resize(pixmap.size()); | ||
| 452 | |||
| 453 | // Update the info with pixel data | ||
| 454 | surface_picker_x_control->setEnabled(true); | ||
| 455 | surface_picker_y_control->setEnabled(true); | ||
| 456 | Pick(surface_picker_x, surface_picker_y); | ||
| 457 | |||
| 458 | // Enable saving the converted pixmap to file | ||
| 459 | save_surface->setEnabled(true); | ||
| 460 | } | ||
| 461 | |||
| 462 | void GraphicsSurfaceWidget::SaveSurface() { | ||
| 463 | const QString png_filter = tr("Portable Network Graphic (*.png)"); | ||
| 464 | const QString bin_filter = tr("Binary data (*.bin)"); | ||
| 465 | |||
| 466 | QString selected_filter; | ||
| 467 | const QString filename = QFileDialog::getSaveFileName( | ||
| 468 | this, tr("Save Surface"), | ||
| 469 | QStringLiteral("texture-0x%1.png").arg(QString::number(surface_address, 16)), | ||
| 470 | QStringLiteral("%1;;%2").arg(png_filter, bin_filter), &selected_filter); | ||
| 471 | |||
| 472 | if (filename.isEmpty()) { | ||
| 473 | // If the user canceled the dialog, don't save anything. | ||
| 474 | return; | ||
| 475 | } | ||
| 476 | |||
| 477 | if (selected_filter == png_filter) { | ||
| 478 | const QPixmap* const pixmap = surface_picture_label->pixmap(); | ||
| 479 | ASSERT_MSG(pixmap != nullptr, "No pixmap set"); | ||
| 480 | |||
| 481 | QFile file{filename}; | ||
| 482 | if (!file.open(QIODevice::WriteOnly)) { | ||
| 483 | QMessageBox::warning(this, tr("Error"), tr("Failed to open file '%1'").arg(filename)); | ||
| 484 | return; | ||
| 485 | } | ||
| 486 | |||
| 487 | if (!pixmap->save(&file, "PNG")) { | ||
| 488 | QMessageBox::warning(this, tr("Error"), | ||
| 489 | tr("Failed to save surface data to file '%1'").arg(filename)); | ||
| 490 | } | ||
| 491 | } else if (selected_filter == bin_filter) { | ||
| 492 | auto& gpu = Core::System::GetInstance().GPU(); | ||
| 493 | const std::optional<VAddr> address = gpu.MemoryManager().GpuToCpuAddress(surface_address); | ||
| 494 | |||
| 495 | const u8* const buffer = Memory::GetPointer(*address); | ||
| 496 | ASSERT_MSG(buffer != nullptr, "Memory not accessible"); | ||
| 497 | |||
| 498 | QFile file{filename}; | ||
| 499 | if (!file.open(QIODevice::WriteOnly)) { | ||
| 500 | QMessageBox::warning(this, tr("Error"), tr("Failed to open file '%1'").arg(filename)); | ||
| 501 | return; | ||
| 502 | } | ||
| 503 | |||
| 504 | const int size = | ||
| 505 | surface_width * surface_height * Tegra::Texture::BytesPerPixel(surface_format); | ||
| 506 | const QByteArray data(reinterpret_cast<const char*>(buffer), size); | ||
| 507 | if (file.write(data) != data.size()) { | ||
| 508 | QMessageBox::warning( | ||
| 509 | this, tr("Error"), | ||
| 510 | tr("Failed to completely write surface data to file. The saved data will " | ||
| 511 | "likely be corrupt.")); | ||
| 512 | } | ||
| 513 | } else { | ||
| 514 | UNREACHABLE_MSG("Unhandled filter selected"); | ||
| 515 | } | ||
| 516 | } | ||
diff --git a/src/yuzu/debugger/graphics/graphics_surface.h b/src/yuzu/debugger/graphics/graphics_surface.h deleted file mode 100644 index 89445b18f..000000000 --- a/src/yuzu/debugger/graphics/graphics_surface.h +++ /dev/null | |||
| @@ -1,96 +0,0 @@ | |||
| 1 | // Copyright 2014 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 <QLabel> | ||
| 8 | #include <QPushButton> | ||
| 9 | #include "video_core/memory_manager.h" | ||
| 10 | #include "video_core/textures/texture.h" | ||
| 11 | #include "yuzu/debugger/graphics/graphics_breakpoint_observer.h" | ||
| 12 | |||
| 13 | class QComboBox; | ||
| 14 | class QSpinBox; | ||
| 15 | class CSpinBox; | ||
| 16 | |||
| 17 | class GraphicsSurfaceWidget; | ||
| 18 | |||
| 19 | class SurfacePicture : public QLabel { | ||
| 20 | Q_OBJECT | ||
| 21 | |||
| 22 | public: | ||
| 23 | explicit SurfacePicture(QWidget* parent = nullptr, | ||
| 24 | GraphicsSurfaceWidget* surface_widget = nullptr); | ||
| 25 | ~SurfacePicture() override; | ||
| 26 | |||
| 27 | protected slots: | ||
| 28 | void mouseMoveEvent(QMouseEvent* event) override; | ||
| 29 | void mousePressEvent(QMouseEvent* event) override; | ||
| 30 | |||
| 31 | private: | ||
| 32 | GraphicsSurfaceWidget* surface_widget; | ||
| 33 | }; | ||
| 34 | |||
| 35 | class GraphicsSurfaceWidget : public BreakPointObserverDock { | ||
| 36 | Q_OBJECT | ||
| 37 | |||
| 38 | using Event = Tegra::DebugContext::Event; | ||
| 39 | |||
| 40 | enum class Source { | ||
| 41 | RenderTarget0 = 0, | ||
| 42 | RenderTarget1 = 1, | ||
| 43 | RenderTarget2 = 2, | ||
| 44 | RenderTarget3 = 3, | ||
| 45 | RenderTarget4 = 4, | ||
| 46 | RenderTarget5 = 5, | ||
| 47 | RenderTarget6 = 6, | ||
| 48 | RenderTarget7 = 7, | ||
| 49 | ZBuffer = 8, | ||
| 50 | Custom = 9, | ||
| 51 | }; | ||
| 52 | |||
| 53 | public: | ||
| 54 | explicit GraphicsSurfaceWidget(std::shared_ptr<Tegra::DebugContext> debug_context, | ||
| 55 | QWidget* parent = nullptr); | ||
| 56 | void Pick(int x, int y); | ||
| 57 | |||
| 58 | public slots: | ||
| 59 | void OnSurfaceSourceChanged(int new_value); | ||
| 60 | void OnSurfaceAddressChanged(qint64 new_value); | ||
| 61 | void OnSurfaceWidthChanged(int new_value); | ||
| 62 | void OnSurfaceHeightChanged(int new_value); | ||
| 63 | void OnSurfaceFormatChanged(int new_value); | ||
| 64 | void OnSurfacePickerXChanged(int new_value); | ||
| 65 | void OnSurfacePickerYChanged(int new_value); | ||
| 66 | void OnUpdate(); | ||
| 67 | |||
| 68 | signals: | ||
| 69 | void Update(); | ||
| 70 | |||
| 71 | private: | ||
| 72 | void OnBreakPointHit(Tegra::DebugContext::Event event, void* data) override; | ||
| 73 | void OnResumed() override; | ||
| 74 | |||
| 75 | void SaveSurface(); | ||
| 76 | |||
| 77 | QComboBox* surface_source_list; | ||
| 78 | CSpinBox* surface_address_control; | ||
| 79 | QSpinBox* surface_width_control; | ||
| 80 | QSpinBox* surface_height_control; | ||
| 81 | QComboBox* surface_format_control; | ||
| 82 | |||
| 83 | SurfacePicture* surface_picture_label; | ||
| 84 | QSpinBox* surface_picker_x_control; | ||
| 85 | QSpinBox* surface_picker_y_control; | ||
| 86 | QLabel* surface_info_label; | ||
| 87 | QPushButton* save_surface; | ||
| 88 | |||
| 89 | Source surface_source; | ||
| 90 | GPUVAddr surface_address; | ||
| 91 | unsigned surface_width; | ||
| 92 | unsigned surface_height; | ||
| 93 | Tegra::Texture::TextureFormat surface_format; | ||
| 94 | int surface_picker_x = 0; | ||
| 95 | int surface_picker_y = 0; | ||
| 96 | }; | ||
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index d5a328d92..ca231d710 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp | |||
| @@ -90,7 +90,6 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual | |||
| 90 | #include "yuzu/configuration/configure_dialog.h" | 90 | #include "yuzu/configuration/configure_dialog.h" |
| 91 | #include "yuzu/debugger/console.h" | 91 | #include "yuzu/debugger/console.h" |
| 92 | #include "yuzu/debugger/graphics/graphics_breakpoints.h" | 92 | #include "yuzu/debugger/graphics/graphics_breakpoints.h" |
| 93 | #include "yuzu/debugger/graphics/graphics_surface.h" | ||
| 94 | #include "yuzu/debugger/profiler.h" | 93 | #include "yuzu/debugger/profiler.h" |
| 95 | #include "yuzu/debugger/wait_tree.h" | 94 | #include "yuzu/debugger/wait_tree.h" |
| 96 | #include "yuzu/discord.h" | 95 | #include "yuzu/discord.h" |
| @@ -483,11 +482,6 @@ void GMainWindow::InitializeDebugWidgets() { | |||
| 483 | graphicsBreakpointsWidget->hide(); | 482 | graphicsBreakpointsWidget->hide(); |
| 484 | debug_menu->addAction(graphicsBreakpointsWidget->toggleViewAction()); | 483 | debug_menu->addAction(graphicsBreakpointsWidget->toggleViewAction()); |
| 485 | 484 | ||
| 486 | graphicsSurfaceWidget = new GraphicsSurfaceWidget(debug_context, this); | ||
| 487 | addDockWidget(Qt::RightDockWidgetArea, graphicsSurfaceWidget); | ||
| 488 | graphicsSurfaceWidget->hide(); | ||
| 489 | debug_menu->addAction(graphicsSurfaceWidget->toggleViewAction()); | ||
| 490 | |||
| 491 | waitTreeWidget = new WaitTreeWidget(this); | 485 | waitTreeWidget = new WaitTreeWidget(this); |
| 492 | addDockWidget(Qt::LeftDockWidgetArea, waitTreeWidget); | 486 | addDockWidget(Qt::LeftDockWidgetArea, waitTreeWidget); |
| 493 | waitTreeWidget->hide(); | 487 | waitTreeWidget->hide(); |
diff --git a/src/yuzu/main.h b/src/yuzu/main.h index c727e942c..85e3810f2 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h | |||
| @@ -23,7 +23,6 @@ class EmuThread; | |||
| 23 | class GameList; | 23 | class GameList; |
| 24 | class GImageInfo; | 24 | class GImageInfo; |
| 25 | class GraphicsBreakPointsWidget; | 25 | class GraphicsBreakPointsWidget; |
| 26 | class GraphicsSurfaceWidget; | ||
| 27 | class GRenderWindow; | 26 | class GRenderWindow; |
| 28 | class LoadingScreen; | 27 | class LoadingScreen; |
| 29 | class MicroProfileDialog; | 28 | class MicroProfileDialog; |
| @@ -240,7 +239,6 @@ private: | |||
| 240 | ProfilerWidget* profilerWidget; | 239 | ProfilerWidget* profilerWidget; |
| 241 | MicroProfileDialog* microProfileDialog; | 240 | MicroProfileDialog* microProfileDialog; |
| 242 | GraphicsBreakPointsWidget* graphicsBreakpointsWidget; | 241 | GraphicsBreakPointsWidget* graphicsBreakpointsWidget; |
| 243 | GraphicsSurfaceWidget* graphicsSurfaceWidget; | ||
| 244 | WaitTreeWidget* waitTreeWidget; | 242 | WaitTreeWidget* waitTreeWidget; |
| 245 | 243 | ||
| 246 | QAction* actions_recent_files[max_recent_files_item]; | 244 | QAction* actions_recent_files[max_recent_files_item]; |