Browse Source
Merge pull request #2353 from lioncash/surface
Merge pull request #2353 from lioncash/surface
yuzu/debugger: Remove graphics surface viewernce_cpp
committed by
GitHub
5 changed files with 0 additions and 622 deletions
-
2src/yuzu/CMakeLists.txt
-
516src/yuzu/debugger/graphics/graphics_surface.cpp
-
96src/yuzu/debugger/graphics/graphics_surface.h
-
6src/yuzu/main.cpp
-
2src/yuzu/main.h
@ -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; |
|
||||
}; |
|
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue