yuzu/debugger: Remove graphics surface viewer
This doesn't actually work anymore, and given how long it's been left in that state, it's unlikely anyone actually seriously used it. Generally it's preferable to use RenderDoc or Nsight to view surfaces.
This commit is contained in:
parent
89b8801a97
commit
218ae888f3
|
@ -54,8 +54,6 @@ add_executable(yuzu
|
||||||
debugger/graphics/graphics_breakpoints.cpp
|
debugger/graphics/graphics_breakpoints.cpp
|
||||||
debugger/graphics/graphics_breakpoints.h
|
debugger/graphics/graphics_breakpoints.h
|
||||||
debugger/graphics/graphics_breakpoints_p.h
|
debugger/graphics/graphics_breakpoints_p.h
|
||||||
debugger/graphics/graphics_surface.cpp
|
|
||||||
debugger/graphics/graphics_surface.h
|
|
||||||
debugger/console.cpp
|
debugger/console.cpp
|
||||||
debugger/console.h
|
debugger/console.h
|
||||||
debugger/profiler.cpp
|
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/configuration/configure_dialog.h"
|
||||||
#include "yuzu/debugger/console.h"
|
#include "yuzu/debugger/console.h"
|
||||||
#include "yuzu/debugger/graphics/graphics_breakpoints.h"
|
#include "yuzu/debugger/graphics/graphics_breakpoints.h"
|
||||||
#include "yuzu/debugger/graphics/graphics_surface.h"
|
|
||||||
#include "yuzu/debugger/profiler.h"
|
#include "yuzu/debugger/profiler.h"
|
||||||
#include "yuzu/debugger/wait_tree.h"
|
#include "yuzu/debugger/wait_tree.h"
|
||||||
#include "yuzu/discord.h"
|
#include "yuzu/discord.h"
|
||||||
|
@ -478,11 +477,6 @@ void GMainWindow::InitializeDebugWidgets() {
|
||||||
graphicsBreakpointsWidget->hide();
|
graphicsBreakpointsWidget->hide();
|
||||||
debug_menu->addAction(graphicsBreakpointsWidget->toggleViewAction());
|
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);
|
waitTreeWidget = new WaitTreeWidget(this);
|
||||||
addDockWidget(Qt::LeftDockWidgetArea, waitTreeWidget);
|
addDockWidget(Qt::LeftDockWidgetArea, waitTreeWidget);
|
||||||
waitTreeWidget->hide();
|
waitTreeWidget->hide();
|
||||||
|
|
|
@ -23,7 +23,6 @@ class EmuThread;
|
||||||
class GameList;
|
class GameList;
|
||||||
class GImageInfo;
|
class GImageInfo;
|
||||||
class GraphicsBreakPointsWidget;
|
class GraphicsBreakPointsWidget;
|
||||||
class GraphicsSurfaceWidget;
|
|
||||||
class GRenderWindow;
|
class GRenderWindow;
|
||||||
class LoadingScreen;
|
class LoadingScreen;
|
||||||
class MicroProfileDialog;
|
class MicroProfileDialog;
|
||||||
|
@ -238,7 +237,6 @@ private:
|
||||||
ProfilerWidget* profilerWidget;
|
ProfilerWidget* profilerWidget;
|
||||||
MicroProfileDialog* microProfileDialog;
|
MicroProfileDialog* microProfileDialog;
|
||||||
GraphicsBreakPointsWidget* graphicsBreakpointsWidget;
|
GraphicsBreakPointsWidget* graphicsBreakpointsWidget;
|
||||||
GraphicsSurfaceWidget* graphicsSurfaceWidget;
|
|
||||||
WaitTreeWidget* waitTreeWidget;
|
WaitTreeWidget* waitTreeWidget;
|
||||||
|
|
||||||
QAction* actions_recent_files[max_recent_files_item];
|
QAction* actions_recent_files[max_recent_files_item];
|
||||||
|
|
Loading…
Reference in New Issue