Merge pull request #2353 from lioncash/surface
yuzu/debugger: Remove graphics surface viewer
This commit is contained in:
		
						commit
						f46c3164e7
					
				| @ -56,8 +56,6 @@ add_executable(yuzu | ||||
|     debugger/graphics/graphics_breakpoints.cpp | ||||
|     debugger/graphics/graphics_breakpoints.h | ||||
|     debugger/graphics/graphics_breakpoints_p.h | ||||
|     debugger/graphics/graphics_surface.cpp | ||||
|     debugger/graphics/graphics_surface.h | ||||
|     debugger/console.cpp | ||||
|     debugger/console.h | ||||
|     debugger/profiler.cpp | ||||
|  | ||||
| @ -1,516 +0,0 @@ | ||||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <QBoxLayout> | ||||
| #include <QComboBox> | ||||
| #include <QDebug> | ||||
| #include <QFileDialog> | ||||
| #include <QLabel> | ||||
| #include <QMessageBox> | ||||
| #include <QMouseEvent> | ||||
| #include <QPushButton> | ||||
| #include <QScrollArea> | ||||
| #include <QSpinBox> | ||||
| #include "common/vector_math.h" | ||||
| #include "core/core.h" | ||||
| #include "core/memory.h" | ||||
| #include "video_core/engines/maxwell_3d.h" | ||||
| #include "video_core/gpu.h" | ||||
| #include "video_core/textures/decoders.h" | ||||
| #include "video_core/textures/texture.h" | ||||
| #include "yuzu/debugger/graphics/graphics_surface.h" | ||||
| #include "yuzu/util/spinbox.h" | ||||
| 
 | ||||
| static Tegra::Texture::TextureFormat ConvertToTextureFormat( | ||||
|     Tegra::RenderTargetFormat render_target_format) { | ||||
|     switch (render_target_format) { | ||||
|     case Tegra::RenderTargetFormat::RGBA8_UNORM: | ||||
|         return Tegra::Texture::TextureFormat::A8R8G8B8; | ||||
|     case Tegra::RenderTargetFormat::RGB10_A2_UNORM: | ||||
|         return Tegra::Texture::TextureFormat::A2B10G10R10; | ||||
|     default: | ||||
|         UNIMPLEMENTED_MSG("Unimplemented RT format"); | ||||
|         return Tegra::Texture::TextureFormat::A8R8G8B8; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| SurfacePicture::SurfacePicture(QWidget* parent, GraphicsSurfaceWidget* surface_widget_) | ||||
|     : QLabel(parent), surface_widget(surface_widget_) {} | ||||
| 
 | ||||
| SurfacePicture::~SurfacePicture() = default; | ||||
| 
 | ||||
| void SurfacePicture::mousePressEvent(QMouseEvent* event) { | ||||
|     // Only do something while the left mouse button is held down
 | ||||
|     if (!(event->buttons() & Qt::LeftButton)) | ||||
|         return; | ||||
| 
 | ||||
|     if (pixmap() == nullptr) | ||||
|         return; | ||||
| 
 | ||||
|     if (surface_widget) | ||||
|         surface_widget->Pick(event->x() * pixmap()->width() / width(), | ||||
|                              event->y() * pixmap()->height() / height()); | ||||
| } | ||||
| 
 | ||||
| void SurfacePicture::mouseMoveEvent(QMouseEvent* event) { | ||||
|     // We also want to handle the event if the user moves the mouse while holding down the LMB
 | ||||
|     mousePressEvent(event); | ||||
| } | ||||
| 
 | ||||
| GraphicsSurfaceWidget::GraphicsSurfaceWidget(std::shared_ptr<Tegra::DebugContext> debug_context, | ||||
|                                              QWidget* parent) | ||||
|     : BreakPointObserverDock(debug_context, tr("Maxwell Surface Viewer"), parent), | ||||
|       surface_source(Source::RenderTarget0) { | ||||
|     setObjectName("MaxwellSurface"); | ||||
| 
 | ||||
|     surface_source_list = new QComboBox; | ||||
|     surface_source_list->addItem(tr("Render Target 0")); | ||||
|     surface_source_list->addItem(tr("Render Target 1")); | ||||
|     surface_source_list->addItem(tr("Render Target 2")); | ||||
|     surface_source_list->addItem(tr("Render Target 3")); | ||||
|     surface_source_list->addItem(tr("Render Target 4")); | ||||
|     surface_source_list->addItem(tr("Render Target 5")); | ||||
|     surface_source_list->addItem(tr("Render Target 6")); | ||||
|     surface_source_list->addItem(tr("Render Target 7")); | ||||
|     surface_source_list->addItem(tr("Z Buffer")); | ||||
|     surface_source_list->addItem(tr("Custom")); | ||||
|     surface_source_list->setCurrentIndex(static_cast<int>(surface_source)); | ||||
| 
 | ||||
|     surface_address_control = new CSpinBox; | ||||
|     surface_address_control->SetBase(16); | ||||
|     surface_address_control->SetRange(0, 0x7FFFFFFFFFFFFFFF); | ||||
|     surface_address_control->SetPrefix("0x"); | ||||
| 
 | ||||
|     unsigned max_dimension = 16384; // TODO: Find actual maximum
 | ||||
| 
 | ||||
|     surface_width_control = new QSpinBox; | ||||
|     surface_width_control->setRange(0, max_dimension); | ||||
| 
 | ||||
|     surface_height_control = new QSpinBox; | ||||
|     surface_height_control->setRange(0, max_dimension); | ||||
| 
 | ||||
|     surface_picker_x_control = new QSpinBox; | ||||
|     surface_picker_x_control->setRange(0, max_dimension - 1); | ||||
| 
 | ||||
|     surface_picker_y_control = new QSpinBox; | ||||
|     surface_picker_y_control->setRange(0, max_dimension - 1); | ||||
| 
 | ||||
|     // clang-format off
 | ||||
|     // Color formats sorted by Maxwell texture format index
 | ||||
|     const QStringList surface_formats{ | ||||
|         tr("None"), | ||||
|         QStringLiteral("R32_G32_B32_A32"), | ||||
|         QStringLiteral("R32_G32_B32"), | ||||
|         QStringLiteral("R16_G16_B16_A16"), | ||||
|         QStringLiteral("R32_G32"), | ||||
|         QStringLiteral("R32_B24G8"), | ||||
|         QStringLiteral("ETC2_RGB"), | ||||
|         QStringLiteral("X8B8G8R8"), | ||||
|         QStringLiteral("A8R8G8B8"), | ||||
|         QStringLiteral("A2B10G10R10"), | ||||
|         QStringLiteral("ETC2_RGB_PTA"), | ||||
|         QStringLiteral("ETC2_RGBA"), | ||||
|         QStringLiteral("R16_G16"), | ||||
|         QStringLiteral("G8R24"), | ||||
|         QStringLiteral("G24R8"), | ||||
|         QStringLiteral("R32"), | ||||
|         QStringLiteral("BC6H_SF16"), | ||||
|         QStringLiteral("BC6H_UF16"), | ||||
|         QStringLiteral("A4B4G4R4"), | ||||
|         QStringLiteral("A5B5G5R1"), | ||||
|         QStringLiteral("A1B5G5R5"), | ||||
|         QStringLiteral("B5G6R5"), | ||||
|         QStringLiteral("B6G5R5"), | ||||
|         QStringLiteral("BC7U"), | ||||
|         QStringLiteral("G8R8"), | ||||
|         QStringLiteral("EAC"), | ||||
|         QStringLiteral("EACX2"), | ||||
|         QStringLiteral("R16"), | ||||
|         QStringLiteral("Y8_VIDEO"), | ||||
|         QStringLiteral("R8"), | ||||
|         QStringLiteral("G4R4"), | ||||
|         QStringLiteral("R1"), | ||||
|         QStringLiteral("E5B9G9R9_SHAREDEXP"), | ||||
|         QStringLiteral("BF10GF11RF11"), | ||||
|         QStringLiteral("G8B8G8R8"), | ||||
|         QStringLiteral("B8G8R8G8"), | ||||
|         QStringLiteral("DXT1"), | ||||
|         QStringLiteral("DXT23"), | ||||
|         QStringLiteral("DXT45"), | ||||
|         QStringLiteral("DXN1"), | ||||
|         QStringLiteral("DXN2"), | ||||
|         QStringLiteral("Z24S8"), | ||||
|         QStringLiteral("X8Z24"), | ||||
|         QStringLiteral("S8Z24"), | ||||
|         QStringLiteral("X4V4Z24__COV4R4V"), | ||||
|         QStringLiteral("X4V4Z24__COV8R8V"), | ||||
|         QStringLiteral("V8Z24__COV4R12V"), | ||||
|         QStringLiteral("ZF32"), | ||||
|         QStringLiteral("ZF32_X24S8"), | ||||
|         QStringLiteral("X8Z24_X20V4S8__COV4R4V"), | ||||
|         QStringLiteral("X8Z24_X20V4S8__COV8R8V"), | ||||
|         QStringLiteral("ZF32_X20V4X8__COV4R4V"), | ||||
|         QStringLiteral("ZF32_X20V4X8__COV8R8V"), | ||||
|         QStringLiteral("ZF32_X20V4S8__COV4R4V"), | ||||
|         QStringLiteral("ZF32_X20V4S8__COV8R8V"), | ||||
|         QStringLiteral("X8Z24_X16V8S8__COV4R12V"), | ||||
|         QStringLiteral("ZF32_X16V8X8__COV4R12V"), | ||||
|         QStringLiteral("ZF32_X16V8S8__COV4R12V"), | ||||
|         QStringLiteral("Z16"), | ||||
|         QStringLiteral("V8Z24__COV8R24V"), | ||||
|         QStringLiteral("X8Z24_X16V8S8__COV8R24V"), | ||||
|         QStringLiteral("ZF32_X16V8X8__COV8R24V"), | ||||
|         QStringLiteral("ZF32_X16V8S8__COV8R24V"), | ||||
|         QStringLiteral("ASTC_2D_4X4"), | ||||
|         QStringLiteral("ASTC_2D_5X5"), | ||||
|         QStringLiteral("ASTC_2D_6X6"), | ||||
|         QStringLiteral("ASTC_2D_8X8"), | ||||
|         QStringLiteral("ASTC_2D_10X10"), | ||||
|         QStringLiteral("ASTC_2D_12X12"), | ||||
|         QStringLiteral("ASTC_2D_5X4"), | ||||
|         QStringLiteral("ASTC_2D_6X5"), | ||||
|         QStringLiteral("ASTC_2D_8X6"), | ||||
|         QStringLiteral("ASTC_2D_10X8"), | ||||
|         QStringLiteral("ASTC_2D_12X10"), | ||||
|         QStringLiteral("ASTC_2D_8X5"), | ||||
|         QStringLiteral("ASTC_2D_10X5"), | ||||
|         QStringLiteral("ASTC_2D_10X6"), | ||||
|     }; | ||||
|     // clang-format on
 | ||||
| 
 | ||||
|     surface_format_control = new QComboBox; | ||||
|     surface_format_control->addItems(surface_formats); | ||||
| 
 | ||||
|     surface_info_label = new QLabel(); | ||||
|     surface_info_label->setWordWrap(true); | ||||
| 
 | ||||
|     surface_picture_label = new SurfacePicture(0, this); | ||||
|     surface_picture_label->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); | ||||
|     surface_picture_label->setAlignment(Qt::AlignLeft | Qt::AlignTop); | ||||
|     surface_picture_label->setScaledContents(false); | ||||
| 
 | ||||
|     auto scroll_area = new QScrollArea(); | ||||
|     scroll_area->setBackgroundRole(QPalette::Dark); | ||||
|     scroll_area->setWidgetResizable(false); | ||||
|     scroll_area->setWidget(surface_picture_label); | ||||
| 
 | ||||
|     save_surface = new QPushButton(QIcon::fromTheme("document-save"), tr("Save")); | ||||
| 
 | ||||
|     // Connections
 | ||||
|     connect(this, &GraphicsSurfaceWidget::Update, this, &GraphicsSurfaceWidget::OnUpdate); | ||||
|     connect(surface_source_list, qOverload<int>(&QComboBox::currentIndexChanged), this, | ||||
|             &GraphicsSurfaceWidget::OnSurfaceSourceChanged); | ||||
|     connect(surface_address_control, &CSpinBox::ValueChanged, this, | ||||
|             &GraphicsSurfaceWidget::OnSurfaceAddressChanged); | ||||
|     connect(surface_width_control, qOverload<int>(&QSpinBox::valueChanged), this, | ||||
|             &GraphicsSurfaceWidget::OnSurfaceWidthChanged); | ||||
|     connect(surface_height_control, qOverload<int>(&QSpinBox::valueChanged), this, | ||||
|             &GraphicsSurfaceWidget::OnSurfaceHeightChanged); | ||||
|     connect(surface_format_control, qOverload<int>(&QComboBox::currentIndexChanged), this, | ||||
|             &GraphicsSurfaceWidget::OnSurfaceFormatChanged); | ||||
|     connect(surface_picker_x_control, qOverload<int>(&QSpinBox::valueChanged), this, | ||||
|             &GraphicsSurfaceWidget::OnSurfacePickerXChanged); | ||||
|     connect(surface_picker_y_control, qOverload<int>(&QSpinBox::valueChanged), this, | ||||
|             &GraphicsSurfaceWidget::OnSurfacePickerYChanged); | ||||
|     connect(save_surface, &QPushButton::clicked, this, &GraphicsSurfaceWidget::SaveSurface); | ||||
| 
 | ||||
|     auto main_widget = new QWidget; | ||||
|     auto main_layout = new QVBoxLayout; | ||||
|     { | ||||
|         auto sub_layout = new QHBoxLayout; | ||||
|         sub_layout->addWidget(new QLabel(tr("Source:"))); | ||||
|         sub_layout->addWidget(surface_source_list); | ||||
|         main_layout->addLayout(sub_layout); | ||||
|     } | ||||
|     { | ||||
|         auto sub_layout = new QHBoxLayout; | ||||
|         sub_layout->addWidget(new QLabel(tr("GPU Address:"))); | ||||
|         sub_layout->addWidget(surface_address_control); | ||||
|         main_layout->addLayout(sub_layout); | ||||
|     } | ||||
|     { | ||||
|         auto sub_layout = new QHBoxLayout; | ||||
|         sub_layout->addWidget(new QLabel(tr("Width:"))); | ||||
|         sub_layout->addWidget(surface_width_control); | ||||
|         main_layout->addLayout(sub_layout); | ||||
|     } | ||||
|     { | ||||
|         auto sub_layout = new QHBoxLayout; | ||||
|         sub_layout->addWidget(new QLabel(tr("Height:"))); | ||||
|         sub_layout->addWidget(surface_height_control); | ||||
|         main_layout->addLayout(sub_layout); | ||||
|     } | ||||
|     { | ||||
|         auto sub_layout = new QHBoxLayout; | ||||
|         sub_layout->addWidget(new QLabel(tr("Format:"))); | ||||
|         sub_layout->addWidget(surface_format_control); | ||||
|         main_layout->addLayout(sub_layout); | ||||
|     } | ||||
|     main_layout->addWidget(scroll_area); | ||||
| 
 | ||||
|     auto info_layout = new QHBoxLayout; | ||||
|     { | ||||
|         auto xy_layout = new QVBoxLayout; | ||||
|         { | ||||
|             { | ||||
|                 auto sub_layout = new QHBoxLayout; | ||||
|                 sub_layout->addWidget(new QLabel(tr("X:"))); | ||||
|                 sub_layout->addWidget(surface_picker_x_control); | ||||
|                 xy_layout->addLayout(sub_layout); | ||||
|             } | ||||
|             { | ||||
|                 auto sub_layout = new QHBoxLayout; | ||||
|                 sub_layout->addWidget(new QLabel(tr("Y:"))); | ||||
|                 sub_layout->addWidget(surface_picker_y_control); | ||||
|                 xy_layout->addLayout(sub_layout); | ||||
|             } | ||||
|         } | ||||
|         info_layout->addLayout(xy_layout); | ||||
|         surface_info_label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); | ||||
|         info_layout->addWidget(surface_info_label); | ||||
|     } | ||||
|     main_layout->addLayout(info_layout); | ||||
| 
 | ||||
|     main_layout->addWidget(save_surface); | ||||
|     main_widget->setLayout(main_layout); | ||||
|     setWidget(main_widget); | ||||
| 
 | ||||
|     // Load current data - TODO: Make sure this works when emulation is not running
 | ||||
|     if (debug_context && debug_context->at_breakpoint) { | ||||
|         emit Update(); | ||||
|         widget()->setEnabled(debug_context->at_breakpoint); | ||||
|     } else { | ||||
|         widget()->setEnabled(false); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void GraphicsSurfaceWidget::OnBreakPointHit(Tegra::DebugContext::Event event, void* data) { | ||||
|     emit Update(); | ||||
|     widget()->setEnabled(true); | ||||
| } | ||||
| 
 | ||||
| void GraphicsSurfaceWidget::OnResumed() { | ||||
|     widget()->setEnabled(false); | ||||
| } | ||||
| 
 | ||||
| void GraphicsSurfaceWidget::OnSurfaceSourceChanged(int new_value) { | ||||
|     surface_source = static_cast<Source>(new_value); | ||||
|     emit Update(); | ||||
| } | ||||
| 
 | ||||
| void GraphicsSurfaceWidget::OnSurfaceAddressChanged(qint64 new_value) { | ||||
|     if (surface_address != new_value) { | ||||
|         surface_address = static_cast<GPUVAddr>(new_value); | ||||
| 
 | ||||
|         surface_source_list->setCurrentIndex(static_cast<int>(Source::Custom)); | ||||
|         emit Update(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void GraphicsSurfaceWidget::OnSurfaceWidthChanged(int new_value) { | ||||
|     if (surface_width != static_cast<unsigned>(new_value)) { | ||||
|         surface_width = static_cast<unsigned>(new_value); | ||||
| 
 | ||||
|         surface_source_list->setCurrentIndex(static_cast<int>(Source::Custom)); | ||||
|         emit Update(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void GraphicsSurfaceWidget::OnSurfaceHeightChanged(int new_value) { | ||||
|     if (surface_height != static_cast<unsigned>(new_value)) { | ||||
|         surface_height = static_cast<unsigned>(new_value); | ||||
| 
 | ||||
|         surface_source_list->setCurrentIndex(static_cast<int>(Source::Custom)); | ||||
|         emit Update(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void GraphicsSurfaceWidget::OnSurfaceFormatChanged(int new_value) { | ||||
|     if (surface_format != static_cast<Tegra::Texture::TextureFormat>(new_value)) { | ||||
|         surface_format = static_cast<Tegra::Texture::TextureFormat>(new_value); | ||||
| 
 | ||||
|         surface_source_list->setCurrentIndex(static_cast<int>(Source::Custom)); | ||||
|         emit Update(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void GraphicsSurfaceWidget::OnSurfacePickerXChanged(int new_value) { | ||||
|     if (surface_picker_x != new_value) { | ||||
|         surface_picker_x = new_value; | ||||
|         Pick(surface_picker_x, surface_picker_y); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void GraphicsSurfaceWidget::OnSurfacePickerYChanged(int new_value) { | ||||
|     if (surface_picker_y != new_value) { | ||||
|         surface_picker_y = new_value; | ||||
|         Pick(surface_picker_x, surface_picker_y); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void GraphicsSurfaceWidget::Pick(int x, int y) { | ||||
|     surface_picker_x_control->setValue(x); | ||||
|     surface_picker_y_control->setValue(y); | ||||
| 
 | ||||
|     if (x < 0 || x >= static_cast<int>(surface_width) || y < 0 || | ||||
|         y >= static_cast<int>(surface_height)) { | ||||
|         surface_info_label->setText(tr("Pixel out of bounds")); | ||||
|         surface_info_label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     surface_info_label->setText(QString("Raw: <Unimplemented>\n(%1)").arg("<Unimplemented>")); | ||||
|     surface_info_label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); | ||||
| } | ||||
| 
 | ||||
| void GraphicsSurfaceWidget::OnUpdate() { | ||||
|     auto& gpu = Core::System::GetInstance().GPU(); | ||||
| 
 | ||||
|     QPixmap pixmap; | ||||
| 
 | ||||
|     switch (surface_source) { | ||||
|     case Source::RenderTarget0: | ||||
|     case Source::RenderTarget1: | ||||
|     case Source::RenderTarget2: | ||||
|     case Source::RenderTarget3: | ||||
|     case Source::RenderTarget4: | ||||
|     case Source::RenderTarget5: | ||||
|     case Source::RenderTarget6: | ||||
|     case Source::RenderTarget7: { | ||||
|         // TODO: Store a reference to the registers in the debug context instead of accessing them
 | ||||
|         // directly...
 | ||||
| 
 | ||||
|         const auto& registers = gpu.Maxwell3D().regs; | ||||
|         const auto& rt = registers.rt[static_cast<std::size_t>(surface_source) - | ||||
|                                       static_cast<std::size_t>(Source::RenderTarget0)]; | ||||
| 
 | ||||
|         surface_address = rt.Address(); | ||||
|         surface_width = rt.width; | ||||
|         surface_height = rt.height; | ||||
|         if (rt.format != Tegra::RenderTargetFormat::NONE) { | ||||
|             surface_format = ConvertToTextureFormat(rt.format); | ||||
|         } | ||||
| 
 | ||||
|         break; | ||||
|     } | ||||
| 
 | ||||
|     case Source::Custom: { | ||||
|         // Keep user-specified values
 | ||||
|         break; | ||||
|     } | ||||
| 
 | ||||
|     default: | ||||
|         qDebug() << "Unknown surface source " << static_cast<int>(surface_source); | ||||
|         break; | ||||
|     } | ||||
| 
 | ||||
|     surface_address_control->SetValue(surface_address); | ||||
|     surface_width_control->setValue(surface_width); | ||||
|     surface_height_control->setValue(surface_height); | ||||
|     surface_format_control->setCurrentIndex(static_cast<int>(surface_format)); | ||||
| 
 | ||||
|     if (surface_address == 0) { | ||||
|         surface_picture_label->hide(); | ||||
|         surface_info_label->setText(tr("(invalid surface address)")); | ||||
|         surface_info_label->setAlignment(Qt::AlignCenter); | ||||
|         surface_picker_x_control->setEnabled(false); | ||||
|         surface_picker_y_control->setEnabled(false); | ||||
|         save_surface->setEnabled(false); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     // TODO: Implement a good way to visualize alpha components!
 | ||||
| 
 | ||||
|     QImage decoded_image(surface_width, surface_height, QImage::Format_ARGB32); | ||||
| 
 | ||||
|     // TODO(bunnei): Will not work with BCn formats that swizzle 4x4 tiles.
 | ||||
|     // Needs to be fixed if we plan to use this feature more, otherwise we may remove it.
 | ||||
|     auto unswizzled_data = Tegra::Texture::UnswizzleTexture( | ||||
|         gpu.MemoryManager().GetPointer(surface_address), 1, 1, | ||||
|         Tegra::Texture::BytesPerPixel(surface_format), surface_width, surface_height, 1U); | ||||
| 
 | ||||
|     auto texture_data = Tegra::Texture::DecodeTexture(unswizzled_data, surface_format, | ||||
|                                                       surface_width, surface_height); | ||||
| 
 | ||||
|     surface_picture_label->show(); | ||||
| 
 | ||||
|     for (unsigned int y = 0; y < surface_height; ++y) { | ||||
|         for (unsigned int x = 0; x < surface_width; ++x) { | ||||
|             Common::Vec4<u8> color; | ||||
|             color[0] = texture_data[x + y * surface_width + 0]; | ||||
|             color[1] = texture_data[x + y * surface_width + 1]; | ||||
|             color[2] = texture_data[x + y * surface_width + 2]; | ||||
|             color[3] = texture_data[x + y * surface_width + 3]; | ||||
|             decoded_image.setPixel(x, y, qRgba(color.r(), color.g(), color.b(), color.a())); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pixmap = QPixmap::fromImage(decoded_image); | ||||
|     surface_picture_label->setPixmap(pixmap); | ||||
|     surface_picture_label->resize(pixmap.size()); | ||||
| 
 | ||||
|     // Update the info with pixel data
 | ||||
|     surface_picker_x_control->setEnabled(true); | ||||
|     surface_picker_y_control->setEnabled(true); | ||||
|     Pick(surface_picker_x, surface_picker_y); | ||||
| 
 | ||||
|     // Enable saving the converted pixmap to file
 | ||||
|     save_surface->setEnabled(true); | ||||
| } | ||||
| 
 | ||||
| void GraphicsSurfaceWidget::SaveSurface() { | ||||
|     const QString png_filter = tr("Portable Network Graphic (*.png)"); | ||||
|     const QString bin_filter = tr("Binary data (*.bin)"); | ||||
| 
 | ||||
|     QString selected_filter; | ||||
|     const QString filename = QFileDialog::getSaveFileName( | ||||
|         this, tr("Save Surface"), | ||||
|         QStringLiteral("texture-0x%1.png").arg(QString::number(surface_address, 16)), | ||||
|         QStringLiteral("%1;;%2").arg(png_filter, bin_filter), &selected_filter); | ||||
| 
 | ||||
|     if (filename.isEmpty()) { | ||||
|         // If the user canceled the dialog, don't save anything.
 | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     if (selected_filter == png_filter) { | ||||
|         const QPixmap* const pixmap = surface_picture_label->pixmap(); | ||||
|         ASSERT_MSG(pixmap != nullptr, "No pixmap set"); | ||||
| 
 | ||||
|         QFile file{filename}; | ||||
|         if (!file.open(QIODevice::WriteOnly)) { | ||||
|             QMessageBox::warning(this, tr("Error"), tr("Failed to open file '%1'").arg(filename)); | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         if (!pixmap->save(&file, "PNG")) { | ||||
|             QMessageBox::warning(this, tr("Error"), | ||||
|                                  tr("Failed to save surface data to file '%1'").arg(filename)); | ||||
|         } | ||||
|     } else if (selected_filter == bin_filter) { | ||||
|         auto& gpu = Core::System::GetInstance().GPU(); | ||||
|         const std::optional<VAddr> address = gpu.MemoryManager().GpuToCpuAddress(surface_address); | ||||
| 
 | ||||
|         const u8* const buffer = Memory::GetPointer(*address); | ||||
|         ASSERT_MSG(buffer != nullptr, "Memory not accessible"); | ||||
| 
 | ||||
|         QFile file{filename}; | ||||
|         if (!file.open(QIODevice::WriteOnly)) { | ||||
|             QMessageBox::warning(this, tr("Error"), tr("Failed to open file '%1'").arg(filename)); | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         const int size = | ||||
|             surface_width * surface_height * Tegra::Texture::BytesPerPixel(surface_format); | ||||
|         const QByteArray data(reinterpret_cast<const char*>(buffer), size); | ||||
|         if (file.write(data) != data.size()) { | ||||
|             QMessageBox::warning( | ||||
|                 this, tr("Error"), | ||||
|                 tr("Failed to completely write surface data to file. The saved data will " | ||||
|                    "likely be corrupt.")); | ||||
|         } | ||||
|     } else { | ||||
|         UNREACHABLE_MSG("Unhandled filter selected"); | ||||
|     } | ||||
| } | ||||
| @ -1,96 +0,0 @@ | ||||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <QLabel> | ||||
| #include <QPushButton> | ||||
| #include "video_core/memory_manager.h" | ||||
| #include "video_core/textures/texture.h" | ||||
| #include "yuzu/debugger/graphics/graphics_breakpoint_observer.h" | ||||
| 
 | ||||
| class QComboBox; | ||||
| class QSpinBox; | ||||
| class CSpinBox; | ||||
| 
 | ||||
| class GraphicsSurfaceWidget; | ||||
| 
 | ||||
| class SurfacePicture : public QLabel { | ||||
|     Q_OBJECT | ||||
| 
 | ||||
| public: | ||||
|     explicit SurfacePicture(QWidget* parent = nullptr, | ||||
|                             GraphicsSurfaceWidget* surface_widget = nullptr); | ||||
|     ~SurfacePicture() override; | ||||
| 
 | ||||
| protected slots: | ||||
|     void mouseMoveEvent(QMouseEvent* event) override; | ||||
|     void mousePressEvent(QMouseEvent* event) override; | ||||
| 
 | ||||
| private: | ||||
|     GraphicsSurfaceWidget* surface_widget; | ||||
| }; | ||||
| 
 | ||||
| class GraphicsSurfaceWidget : public BreakPointObserverDock { | ||||
|     Q_OBJECT | ||||
| 
 | ||||
|     using Event = Tegra::DebugContext::Event; | ||||
| 
 | ||||
|     enum class Source { | ||||
|         RenderTarget0 = 0, | ||||
|         RenderTarget1 = 1, | ||||
|         RenderTarget2 = 2, | ||||
|         RenderTarget3 = 3, | ||||
|         RenderTarget4 = 4, | ||||
|         RenderTarget5 = 5, | ||||
|         RenderTarget6 = 6, | ||||
|         RenderTarget7 = 7, | ||||
|         ZBuffer = 8, | ||||
|         Custom = 9, | ||||
|     }; | ||||
| 
 | ||||
| public: | ||||
|     explicit GraphicsSurfaceWidget(std::shared_ptr<Tegra::DebugContext> debug_context, | ||||
|                                    QWidget* parent = nullptr); | ||||
|     void Pick(int x, int y); | ||||
| 
 | ||||
| public slots: | ||||
|     void OnSurfaceSourceChanged(int new_value); | ||||
|     void OnSurfaceAddressChanged(qint64 new_value); | ||||
|     void OnSurfaceWidthChanged(int new_value); | ||||
|     void OnSurfaceHeightChanged(int new_value); | ||||
|     void OnSurfaceFormatChanged(int new_value); | ||||
|     void OnSurfacePickerXChanged(int new_value); | ||||
|     void OnSurfacePickerYChanged(int new_value); | ||||
|     void OnUpdate(); | ||||
| 
 | ||||
| signals: | ||||
|     void Update(); | ||||
| 
 | ||||
| private: | ||||
|     void OnBreakPointHit(Tegra::DebugContext::Event event, void* data) override; | ||||
|     void OnResumed() override; | ||||
| 
 | ||||
|     void SaveSurface(); | ||||
| 
 | ||||
|     QComboBox* surface_source_list; | ||||
|     CSpinBox* surface_address_control; | ||||
|     QSpinBox* surface_width_control; | ||||
|     QSpinBox* surface_height_control; | ||||
|     QComboBox* surface_format_control; | ||||
| 
 | ||||
|     SurfacePicture* surface_picture_label; | ||||
|     QSpinBox* surface_picker_x_control; | ||||
|     QSpinBox* surface_picker_y_control; | ||||
|     QLabel* surface_info_label; | ||||
|     QPushButton* save_surface; | ||||
| 
 | ||||
|     Source surface_source; | ||||
|     GPUVAddr surface_address; | ||||
|     unsigned surface_width; | ||||
|     unsigned surface_height; | ||||
|     Tegra::Texture::TextureFormat surface_format; | ||||
|     int surface_picker_x = 0; | ||||
|     int surface_picker_y = 0; | ||||
| }; | ||||
| @ -90,7 +90,6 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual | ||||
| #include "yuzu/configuration/configure_dialog.h" | ||||
| #include "yuzu/debugger/console.h" | ||||
| #include "yuzu/debugger/graphics/graphics_breakpoints.h" | ||||
| #include "yuzu/debugger/graphics/graphics_surface.h" | ||||
| #include "yuzu/debugger/profiler.h" | ||||
| #include "yuzu/debugger/wait_tree.h" | ||||
| #include "yuzu/discord.h" | ||||
| @ -483,11 +482,6 @@ void GMainWindow::InitializeDebugWidgets() { | ||||
|     graphicsBreakpointsWidget->hide(); | ||||
|     debug_menu->addAction(graphicsBreakpointsWidget->toggleViewAction()); | ||||
| 
 | ||||
|     graphicsSurfaceWidget = new GraphicsSurfaceWidget(debug_context, this); | ||||
|     addDockWidget(Qt::RightDockWidgetArea, graphicsSurfaceWidget); | ||||
|     graphicsSurfaceWidget->hide(); | ||||
|     debug_menu->addAction(graphicsSurfaceWidget->toggleViewAction()); | ||||
| 
 | ||||
|     waitTreeWidget = new WaitTreeWidget(this); | ||||
|     addDockWidget(Qt::LeftDockWidgetArea, waitTreeWidget); | ||||
|     waitTreeWidget->hide(); | ||||
|  | ||||
| @ -23,7 +23,6 @@ class EmuThread; | ||||
| class GameList; | ||||
| class GImageInfo; | ||||
| class GraphicsBreakPointsWidget; | ||||
| class GraphicsSurfaceWidget; | ||||
| class GRenderWindow; | ||||
| class LoadingScreen; | ||||
| class MicroProfileDialog; | ||||
| @ -240,7 +239,6 @@ private: | ||||
|     ProfilerWidget* profilerWidget; | ||||
|     MicroProfileDialog* microProfileDialog; | ||||
|     GraphicsBreakPointsWidget* graphicsBreakpointsWidget; | ||||
|     GraphicsSurfaceWidget* graphicsSurfaceWidget; | ||||
|     WaitTreeWidget* waitTreeWidget; | ||||
| 
 | ||||
|     QAction* actions_recent_files[max_recent_files_item]; | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 bunnei
						bunnei