2016-04-09 17:23:15 +01:00
|
|
|
// 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>
|
Port various minor changes from yuzu PRs (#4725)
* common/thread: Remove unused functions
Many of these functions are carried over from Dolphin (where they aren't
used anymore). Given these have no use (and we really shouldn't be
screwing around with OS-specific thread scheduler handling from the
emulator, these can be removed.
The function for setting the thread name is left, however, since it can
have debugging utility usages.
* input_common/sdl: Use a type alias to shorten declaration of GetPollers
Just makes the definitions a little bit more tidy.
* input_common/sdl: Correct return values within implementations of GetPollers()
In both cases, we weren't actually returning anything, which is
undefined behavior.
* yuzu/debugger/graphics_surface: Fill in missing surface format listings
Fills in the missing surface types that were marked as unknown. The
order corresponds with the TextureFormat enum within
video_core/texture.h.
We also don't need to all of these strings as translatable (only the
first string, as it's an English word).
* yuzu/debugger/graphics_surface: Clean up connection overload deduction
We can utilize qOverload with the signal connections to make the
function deducing a little less ugly.
* yuzu/debugger/graphics_surface: Tidy up SaveSurface
- Use QStringLiteral where applicable.
- Use const where applicable
- Remove unnecessary precondition check (we already assert the pixbuf
being non null)
* yuzu/debugger/graphics_surface: Display error messages for file I/O errors
* core: Add missing override specifiers where applicable
Applies the override specifier where applicable. In the case of
destructors that are defaulted in their definition, they can
simply be removed.
This also removes the unnecessary inclusions being done in audin_u and
audrec_u, given their close proximity.
* kernel/thread: Make parameter of GetWaitObjectIndex() const qualified
The pointed to member is never actually modified, so it can be made
const.
* kernel/thread: Avoid sign conversion within GetCommandBufferAddress()
Previously this was performing a u64 + int sign conversion. When dealing
with addresses, we should generally be keeping the arithmetic in the
same signedness type.
This also gets rid of the static lifetime of the constant, as there's no
need to make a trivial type like this potentially live for the entire
duration of the program.
* kernel/codeset: Make CodeSet's memory data member a regular std::vector
The use of a shared_ptr is an implementation detail of the VMManager
itself when mapping memory. Because of that, we shouldn't require all
users of the CodeSet to have to allocate the shared_ptr ahead of time.
It's intended that CodeSet simply pass in the required direct data, and
that the memory manager takes care of it from that point on.
This means we just do the shared pointer allocation in a single place,
when loading modules, as opposed to in each loader.
* kernel/wait_object: Make ShouldWait() take thread members by pointer-to-const
Given this is intended as a querying function, it doesn't make sense to
allow the implementer to modify the state of the given thread.
2019-05-01 13:28:49 +01:00
|
|
|
#include <QMessageBox>
|
2016-04-09 17:23:15 +01:00
|
|
|
#include <QMouseEvent>
|
|
|
|
#include <QPushButton>
|
|
|
|
#include <QScrollArea>
|
|
|
|
#include <QSpinBox>
|
2016-12-21 22:19:12 +00:00
|
|
|
#include "citra_qt/debugger/graphics/graphics_surface.h"
|
2016-04-09 17:23:15 +01:00
|
|
|
#include "citra_qt/util/spinbox.h"
|
|
|
|
#include "common/color.h"
|
2018-11-21 17:01:19 +00:00
|
|
|
#include "core/core.h"
|
2016-04-09 17:23:15 +01:00
|
|
|
#include "core/hw/gpu.h"
|
2016-09-18 01:38:01 +01:00
|
|
|
#include "core/memory.h"
|
2016-04-09 17:23:15 +01:00
|
|
|
#include "video_core/pica_state.h"
|
2017-01-28 23:12:09 +00:00
|
|
|
#include "video_core/regs_framebuffer.h"
|
|
|
|
#include "video_core/regs_texturing.h"
|
2017-01-05 22:11:23 +00:00
|
|
|
#include "video_core/texture/texture_decode.h"
|
2016-04-09 17:23:15 +01:00
|
|
|
#include "video_core/utils.h"
|
|
|
|
|
2016-09-18 01:38:01 +01:00
|
|
|
SurfacePicture::SurfacePicture(QWidget* parent, GraphicsSurfaceWidget* surface_widget_)
|
2016-09-19 02:01:46 +01:00
|
|
|
: QLabel(parent), surface_widget(surface_widget_) {}
|
2018-08-24 16:14:09 +01:00
|
|
|
|
|
|
|
SurfacePicture::~SurfacePicture() = default;
|
2016-04-09 17:23:15 +01:00
|
|
|
|
2016-09-18 01:38:01 +01:00
|
|
|
void SurfacePicture::mousePressEvent(QMouseEvent* event) {
|
2016-04-09 17:23:15 +01:00
|
|
|
// 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());
|
|
|
|
}
|
|
|
|
|
2016-09-18 01:38:01 +01:00
|
|
|
void SurfacePicture::mouseMoveEvent(QMouseEvent* event) {
|
2016-04-09 17:23:15 +01:00
|
|
|
// 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<Pica::DebugContext> debug_context,
|
2016-09-18 01:38:01 +01:00
|
|
|
QWidget* parent)
|
2016-04-09 17:23:15 +01:00
|
|
|
: BreakPointObserverDock(debug_context, tr("Pica Surface Viewer"), parent),
|
2016-09-18 01:38:01 +01:00
|
|
|
surface_source(Source::ColorBuffer) {
|
2016-04-09 17:23:15 +01:00
|
|
|
setObjectName("PicaSurface");
|
|
|
|
|
|
|
|
surface_source_list = new QComboBox;
|
|
|
|
surface_source_list->addItem(tr("Color Buffer"));
|
|
|
|
surface_source_list->addItem(tr("Depth Buffer"));
|
|
|
|
surface_source_list->addItem(tr("Stencil Buffer"));
|
|
|
|
surface_source_list->addItem(tr("Texture 0"));
|
|
|
|
surface_source_list->addItem(tr("Texture 1"));
|
|
|
|
surface_source_list->addItem(tr("Texture 2"));
|
|
|
|
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, 0xFFFFFFFF);
|
|
|
|
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);
|
|
|
|
|
|
|
|
// Color formats sorted by Pica texture format index
|
Port various minor changes from yuzu PRs (#4725)
* common/thread: Remove unused functions
Many of these functions are carried over from Dolphin (where they aren't
used anymore). Given these have no use (and we really shouldn't be
screwing around with OS-specific thread scheduler handling from the
emulator, these can be removed.
The function for setting the thread name is left, however, since it can
have debugging utility usages.
* input_common/sdl: Use a type alias to shorten declaration of GetPollers
Just makes the definitions a little bit more tidy.
* input_common/sdl: Correct return values within implementations of GetPollers()
In both cases, we weren't actually returning anything, which is
undefined behavior.
* yuzu/debugger/graphics_surface: Fill in missing surface format listings
Fills in the missing surface types that were marked as unknown. The
order corresponds with the TextureFormat enum within
video_core/texture.h.
We also don't need to all of these strings as translatable (only the
first string, as it's an English word).
* yuzu/debugger/graphics_surface: Clean up connection overload deduction
We can utilize qOverload with the signal connections to make the
function deducing a little less ugly.
* yuzu/debugger/graphics_surface: Tidy up SaveSurface
- Use QStringLiteral where applicable.
- Use const where applicable
- Remove unnecessary precondition check (we already assert the pixbuf
being non null)
* yuzu/debugger/graphics_surface: Display error messages for file I/O errors
* core: Add missing override specifiers where applicable
Applies the override specifier where applicable. In the case of
destructors that are defaulted in their definition, they can
simply be removed.
This also removes the unnecessary inclusions being done in audin_u and
audrec_u, given their close proximity.
* kernel/thread: Make parameter of GetWaitObjectIndex() const qualified
The pointed to member is never actually modified, so it can be made
const.
* kernel/thread: Avoid sign conversion within GetCommandBufferAddress()
Previously this was performing a u64 + int sign conversion. When dealing
with addresses, we should generally be keeping the arithmetic in the
same signedness type.
This also gets rid of the static lifetime of the constant, as there's no
need to make a trivial type like this potentially live for the entire
duration of the program.
* kernel/codeset: Make CodeSet's memory data member a regular std::vector
The use of a shared_ptr is an implementation detail of the VMManager
itself when mapping memory. Because of that, we shouldn't require all
users of the CodeSet to have to allocate the shared_ptr ahead of time.
It's intended that CodeSet simply pass in the required direct data, and
that the memory manager takes care of it from that point on.
This means we just do the shared pointer allocation in a single place,
when loading modules, as opposed to in each loader.
* kernel/wait_object: Make ShouldWait() take thread members by pointer-to-const
Given this is intended as a querying function, it doesn't make sense to
allow the implementer to modify the state of the given thread.
2019-05-01 13:28:49 +01:00
|
|
|
const QStringList surface_formats{
|
|
|
|
QStringLiteral("RGBA8"),
|
|
|
|
QStringLiteral("RGB8"),
|
|
|
|
QStringLiteral("RGB5A1"),
|
|
|
|
QStringLiteral("RGB565"),
|
|
|
|
QStringLiteral("RGBA4"),
|
|
|
|
QStringLiteral("IA8"),
|
|
|
|
QStringLiteral("RG8"),
|
|
|
|
QStringLiteral("I8"),
|
|
|
|
QStringLiteral("A8"),
|
|
|
|
QStringLiteral("IA4"),
|
|
|
|
QStringLiteral("I4"),
|
|
|
|
QStringLiteral("A4"),
|
|
|
|
QStringLiteral("ETC1"),
|
|
|
|
QStringLiteral("ETC1A4"),
|
|
|
|
QStringLiteral("D16"),
|
|
|
|
QStringLiteral("D24"),
|
|
|
|
QStringLiteral("D24X8"),
|
|
|
|
QStringLiteral("X24S8"),
|
|
|
|
tr("Unknown"),
|
|
|
|
};
|
|
|
|
|
|
|
|
surface_format_control = new QComboBox;
|
|
|
|
surface_format_control->addItems(surface_formats);
|
2016-04-09 17:23:15 +01:00
|
|
|
|
|
|
|
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
|
2017-12-17 21:24:19 +00:00
|
|
|
connect(this, &GraphicsSurfaceWidget::Update, this, &GraphicsSurfaceWidget::OnUpdate);
|
Port various minor changes from yuzu PRs (#4725)
* common/thread: Remove unused functions
Many of these functions are carried over from Dolphin (where they aren't
used anymore). Given these have no use (and we really shouldn't be
screwing around with OS-specific thread scheduler handling from the
emulator, these can be removed.
The function for setting the thread name is left, however, since it can
have debugging utility usages.
* input_common/sdl: Use a type alias to shorten declaration of GetPollers
Just makes the definitions a little bit more tidy.
* input_common/sdl: Correct return values within implementations of GetPollers()
In both cases, we weren't actually returning anything, which is
undefined behavior.
* yuzu/debugger/graphics_surface: Fill in missing surface format listings
Fills in the missing surface types that were marked as unknown. The
order corresponds with the TextureFormat enum within
video_core/texture.h.
We also don't need to all of these strings as translatable (only the
first string, as it's an English word).
* yuzu/debugger/graphics_surface: Clean up connection overload deduction
We can utilize qOverload with the signal connections to make the
function deducing a little less ugly.
* yuzu/debugger/graphics_surface: Tidy up SaveSurface
- Use QStringLiteral where applicable.
- Use const where applicable
- Remove unnecessary precondition check (we already assert the pixbuf
being non null)
* yuzu/debugger/graphics_surface: Display error messages for file I/O errors
* core: Add missing override specifiers where applicable
Applies the override specifier where applicable. In the case of
destructors that are defaulted in their definition, they can
simply be removed.
This also removes the unnecessary inclusions being done in audin_u and
audrec_u, given their close proximity.
* kernel/thread: Make parameter of GetWaitObjectIndex() const qualified
The pointed to member is never actually modified, so it can be made
const.
* kernel/thread: Avoid sign conversion within GetCommandBufferAddress()
Previously this was performing a u64 + int sign conversion. When dealing
with addresses, we should generally be keeping the arithmetic in the
same signedness type.
This also gets rid of the static lifetime of the constant, as there's no
need to make a trivial type like this potentially live for the entire
duration of the program.
* kernel/codeset: Make CodeSet's memory data member a regular std::vector
The use of a shared_ptr is an implementation detail of the VMManager
itself when mapping memory. Because of that, we shouldn't require all
users of the CodeSet to have to allocate the shared_ptr ahead of time.
It's intended that CodeSet simply pass in the required direct data, and
that the memory manager takes care of it from that point on.
This means we just do the shared pointer allocation in a single place,
when loading modules, as opposed to in each loader.
* kernel/wait_object: Make ShouldWait() take thread members by pointer-to-const
Given this is intended as a querying function, it doesn't make sense to
allow the implementer to modify the state of the given thread.
2019-05-01 13:28:49 +01:00
|
|
|
connect(surface_source_list, qOverload<int>(&QComboBox::currentIndexChanged), this,
|
2017-12-17 21:24:19 +00:00
|
|
|
&GraphicsSurfaceWidget::OnSurfaceSourceChanged);
|
|
|
|
connect(surface_address_control, &CSpinBox::ValueChanged, this,
|
|
|
|
&GraphicsSurfaceWidget::OnSurfaceAddressChanged);
|
Port various minor changes from yuzu PRs (#4725)
* common/thread: Remove unused functions
Many of these functions are carried over from Dolphin (where they aren't
used anymore). Given these have no use (and we really shouldn't be
screwing around with OS-specific thread scheduler handling from the
emulator, these can be removed.
The function for setting the thread name is left, however, since it can
have debugging utility usages.
* input_common/sdl: Use a type alias to shorten declaration of GetPollers
Just makes the definitions a little bit more tidy.
* input_common/sdl: Correct return values within implementations of GetPollers()
In both cases, we weren't actually returning anything, which is
undefined behavior.
* yuzu/debugger/graphics_surface: Fill in missing surface format listings
Fills in the missing surface types that were marked as unknown. The
order corresponds with the TextureFormat enum within
video_core/texture.h.
We also don't need to all of these strings as translatable (only the
first string, as it's an English word).
* yuzu/debugger/graphics_surface: Clean up connection overload deduction
We can utilize qOverload with the signal connections to make the
function deducing a little less ugly.
* yuzu/debugger/graphics_surface: Tidy up SaveSurface
- Use QStringLiteral where applicable.
- Use const where applicable
- Remove unnecessary precondition check (we already assert the pixbuf
being non null)
* yuzu/debugger/graphics_surface: Display error messages for file I/O errors
* core: Add missing override specifiers where applicable
Applies the override specifier where applicable. In the case of
destructors that are defaulted in their definition, they can
simply be removed.
This also removes the unnecessary inclusions being done in audin_u and
audrec_u, given their close proximity.
* kernel/thread: Make parameter of GetWaitObjectIndex() const qualified
The pointed to member is never actually modified, so it can be made
const.
* kernel/thread: Avoid sign conversion within GetCommandBufferAddress()
Previously this was performing a u64 + int sign conversion. When dealing
with addresses, we should generally be keeping the arithmetic in the
same signedness type.
This also gets rid of the static lifetime of the constant, as there's no
need to make a trivial type like this potentially live for the entire
duration of the program.
* kernel/codeset: Make CodeSet's memory data member a regular std::vector
The use of a shared_ptr is an implementation detail of the VMManager
itself when mapping memory. Because of that, we shouldn't require all
users of the CodeSet to have to allocate the shared_ptr ahead of time.
It's intended that CodeSet simply pass in the required direct data, and
that the memory manager takes care of it from that point on.
This means we just do the shared pointer allocation in a single place,
when loading modules, as opposed to in each loader.
* kernel/wait_object: Make ShouldWait() take thread members by pointer-to-const
Given this is intended as a querying function, it doesn't make sense to
allow the implementer to modify the state of the given thread.
2019-05-01 13:28:49 +01:00
|
|
|
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,
|
2017-12-17 21:24:19 +00:00
|
|
|
&GraphicsSurfaceWidget::OnSurfaceFormatChanged);
|
Port various minor changes from yuzu PRs (#4725)
* common/thread: Remove unused functions
Many of these functions are carried over from Dolphin (where they aren't
used anymore). Given these have no use (and we really shouldn't be
screwing around with OS-specific thread scheduler handling from the
emulator, these can be removed.
The function for setting the thread name is left, however, since it can
have debugging utility usages.
* input_common/sdl: Use a type alias to shorten declaration of GetPollers
Just makes the definitions a little bit more tidy.
* input_common/sdl: Correct return values within implementations of GetPollers()
In both cases, we weren't actually returning anything, which is
undefined behavior.
* yuzu/debugger/graphics_surface: Fill in missing surface format listings
Fills in the missing surface types that were marked as unknown. The
order corresponds with the TextureFormat enum within
video_core/texture.h.
We also don't need to all of these strings as translatable (only the
first string, as it's an English word).
* yuzu/debugger/graphics_surface: Clean up connection overload deduction
We can utilize qOverload with the signal connections to make the
function deducing a little less ugly.
* yuzu/debugger/graphics_surface: Tidy up SaveSurface
- Use QStringLiteral where applicable.
- Use const where applicable
- Remove unnecessary precondition check (we already assert the pixbuf
being non null)
* yuzu/debugger/graphics_surface: Display error messages for file I/O errors
* core: Add missing override specifiers where applicable
Applies the override specifier where applicable. In the case of
destructors that are defaulted in their definition, they can
simply be removed.
This also removes the unnecessary inclusions being done in audin_u and
audrec_u, given their close proximity.
* kernel/thread: Make parameter of GetWaitObjectIndex() const qualified
The pointed to member is never actually modified, so it can be made
const.
* kernel/thread: Avoid sign conversion within GetCommandBufferAddress()
Previously this was performing a u64 + int sign conversion. When dealing
with addresses, we should generally be keeping the arithmetic in the
same signedness type.
This also gets rid of the static lifetime of the constant, as there's no
need to make a trivial type like this potentially live for the entire
duration of the program.
* kernel/codeset: Make CodeSet's memory data member a regular std::vector
The use of a shared_ptr is an implementation detail of the VMManager
itself when mapping memory. Because of that, we shouldn't require all
users of the CodeSet to have to allocate the shared_ptr ahead of time.
It's intended that CodeSet simply pass in the required direct data, and
that the memory manager takes care of it from that point on.
This means we just do the shared pointer allocation in a single place,
when loading modules, as opposed to in each loader.
* kernel/wait_object: Make ShouldWait() take thread members by pointer-to-const
Given this is intended as a querying function, it doesn't make sense to
allow the implementer to modify the state of the given thread.
2019-05-01 13:28:49 +01:00
|
|
|
connect(surface_picker_x_control, qOverload<int>(&QSpinBox::valueChanged), this,
|
|
|
|
&GraphicsSurfaceWidget::OnSurfacePickerXChanged);
|
|
|
|
connect(surface_picker_y_control, qOverload<int>(&QSpinBox::valueChanged), this,
|
|
|
|
&GraphicsSurfaceWidget::OnSurfacePickerYChanged);
|
2017-12-17 21:24:19 +00:00
|
|
|
connect(save_surface, &QPushButton::clicked, this, &GraphicsSurfaceWidget::SaveSurface);
|
2016-04-09 17:23:15 +01:00
|
|
|
|
|
|
|
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("Physical 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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-18 01:38:01 +01:00
|
|
|
void GraphicsSurfaceWidget::OnBreakPointHit(Pica::DebugContext::Event event, void* data) {
|
2016-04-09 17:23:15 +01:00
|
|
|
emit Update();
|
|
|
|
widget()->setEnabled(true);
|
|
|
|
}
|
|
|
|
|
2016-09-18 01:38:01 +01:00
|
|
|
void GraphicsSurfaceWidget::OnResumed() {
|
2016-04-09 17:23:15 +01:00
|
|
|
widget()->setEnabled(false);
|
|
|
|
}
|
|
|
|
|
2016-09-18 01:38:01 +01:00
|
|
|
void GraphicsSurfaceWidget::OnSurfaceSourceChanged(int new_value) {
|
2016-04-09 17:23:15 +01:00
|
|
|
surface_source = static_cast<Source>(new_value);
|
|
|
|
emit Update();
|
|
|
|
}
|
|
|
|
|
2016-09-18 01:38:01 +01:00
|
|
|
void GraphicsSurfaceWidget::OnSurfaceAddressChanged(qint64 new_value) {
|
2016-04-09 17:23:15 +01:00
|
|
|
if (surface_address != new_value) {
|
|
|
|
surface_address = static_cast<unsigned>(new_value);
|
|
|
|
|
|
|
|
surface_source_list->setCurrentIndex(static_cast<int>(Source::Custom));
|
|
|
|
emit Update();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-18 01:38:01 +01:00
|
|
|
void GraphicsSurfaceWidget::OnSurfaceWidthChanged(int new_value) {
|
2016-04-09 17:23:15 +01:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-18 01:38:01 +01:00
|
|
|
void GraphicsSurfaceWidget::OnSurfaceHeightChanged(int new_value) {
|
2016-04-09 17:23:15 +01:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-18 01:38:01 +01:00
|
|
|
void GraphicsSurfaceWidget::OnSurfaceFormatChanged(int new_value) {
|
2016-04-09 17:23:15 +01:00
|
|
|
if (surface_format != static_cast<Format>(new_value)) {
|
|
|
|
surface_format = static_cast<Format>(new_value);
|
|
|
|
|
|
|
|
surface_source_list->setCurrentIndex(static_cast<int>(Source::Custom));
|
|
|
|
emit Update();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-18 01:38:01 +01:00
|
|
|
void GraphicsSurfaceWidget::OnSurfacePickerXChanged(int new_value) {
|
2016-04-09 17:23:15 +01:00
|
|
|
if (surface_picker_x != new_value) {
|
|
|
|
surface_picker_x = new_value;
|
|
|
|
Pick(surface_picker_x, surface_picker_y);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-18 01:38:01 +01:00
|
|
|
void GraphicsSurfaceWidget::OnSurfacePickerYChanged(int new_value) {
|
2016-04-09 17:23:15 +01:00
|
|
|
if (surface_picker_y != new_value) {
|
|
|
|
surface_picker_y = new_value;
|
|
|
|
Pick(surface_picker_x, surface_picker_y);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-18 01:38:01 +01:00
|
|
|
void GraphicsSurfaceWidget::Pick(int x, int y) {
|
2016-04-09 17:23:15 +01:00
|
|
|
surface_picker_x_control->setValue(x);
|
|
|
|
surface_picker_y_control->setValue(y);
|
|
|
|
|
2017-09-27 00:26:09 +01:00
|
|
|
if (x < 0 || x >= static_cast<int>(surface_width) || y < 0 ||
|
|
|
|
y >= static_cast<int>(surface_height)) {
|
2016-04-09 17:23:15 +01:00
|
|
|
surface_info_label->setText(tr("Pixel out of bounds"));
|
|
|
|
surface_info_label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-11-21 17:01:19 +00:00
|
|
|
u8* buffer = Core::System::GetInstance().Memory().GetPhysicalPointer(surface_address);
|
2016-04-09 17:23:15 +01:00
|
|
|
if (buffer == nullptr) {
|
|
|
|
surface_info_label->setText(tr("(unable to access pixel data)"));
|
|
|
|
surface_info_label->setAlignment(Qt::AlignCenter);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned nibbles_per_pixel = GraphicsSurfaceWidget::NibblesPerPixel(surface_format);
|
|
|
|
unsigned stride = nibbles_per_pixel * surface_width / 2;
|
|
|
|
|
|
|
|
unsigned bytes_per_pixel;
|
|
|
|
bool nibble_mode = (nibbles_per_pixel == 1);
|
|
|
|
if (nibble_mode) {
|
|
|
|
// As nibbles are contained in a byte we still need to access one byte per nibble
|
|
|
|
bytes_per_pixel = 1;
|
|
|
|
} else {
|
|
|
|
bytes_per_pixel = nibbles_per_pixel / 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
const u32 coarse_y = y & ~7;
|
|
|
|
u32 offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * stride;
|
|
|
|
const u8* pixel = buffer + (nibble_mode ? (offset / 2) : offset);
|
|
|
|
|
|
|
|
auto GetText = [offset](Format format, const u8* pixel) {
|
|
|
|
switch (format) {
|
2016-09-18 01:38:01 +01:00
|
|
|
case Format::RGBA8: {
|
2016-04-09 17:23:15 +01:00
|
|
|
auto value = Color::DecodeRGBA8(pixel) / 255.0f;
|
|
|
|
return QString("Red: %1, Green: %2, Blue: %3, Alpha: %4")
|
2016-09-18 01:38:01 +01:00
|
|
|
.arg(QString::number(value.r(), 'f', 2))
|
|
|
|
.arg(QString::number(value.g(), 'f', 2))
|
|
|
|
.arg(QString::number(value.b(), 'f', 2))
|
|
|
|
.arg(QString::number(value.a(), 'f', 2));
|
2016-04-09 17:23:15 +01:00
|
|
|
}
|
2016-09-18 01:38:01 +01:00
|
|
|
case Format::RGB8: {
|
2016-04-09 17:23:15 +01:00
|
|
|
auto value = Color::DecodeRGB8(pixel) / 255.0f;
|
|
|
|
return QString("Red: %1, Green: %2, Blue: %3")
|
2016-09-18 01:38:01 +01:00
|
|
|
.arg(QString::number(value.r(), 'f', 2))
|
|
|
|
.arg(QString::number(value.g(), 'f', 2))
|
|
|
|
.arg(QString::number(value.b(), 'f', 2));
|
2016-04-09 17:23:15 +01:00
|
|
|
}
|
2016-09-18 01:38:01 +01:00
|
|
|
case Format::RGB5A1: {
|
2016-04-09 17:23:15 +01:00
|
|
|
auto value = Color::DecodeRGB5A1(pixel) / 255.0f;
|
|
|
|
return QString("Red: %1, Green: %2, Blue: %3, Alpha: %4")
|
2016-09-18 01:38:01 +01:00
|
|
|
.arg(QString::number(value.r(), 'f', 2))
|
|
|
|
.arg(QString::number(value.g(), 'f', 2))
|
|
|
|
.arg(QString::number(value.b(), 'f', 2))
|
|
|
|
.arg(QString::number(value.a(), 'f', 2));
|
2016-04-09 17:23:15 +01:00
|
|
|
}
|
2016-09-18 01:38:01 +01:00
|
|
|
case Format::RGB565: {
|
2016-04-09 17:23:15 +01:00
|
|
|
auto value = Color::DecodeRGB565(pixel) / 255.0f;
|
|
|
|
return QString("Red: %1, Green: %2, Blue: %3")
|
2016-09-18 01:38:01 +01:00
|
|
|
.arg(QString::number(value.r(), 'f', 2))
|
|
|
|
.arg(QString::number(value.g(), 'f', 2))
|
|
|
|
.arg(QString::number(value.b(), 'f', 2));
|
2016-04-09 17:23:15 +01:00
|
|
|
}
|
2016-09-18 01:38:01 +01:00
|
|
|
case Format::RGBA4: {
|
2016-04-09 17:23:15 +01:00
|
|
|
auto value = Color::DecodeRGBA4(pixel) / 255.0f;
|
|
|
|
return QString("Red: %1, Green: %2, Blue: %3, Alpha: %4")
|
2016-09-18 01:38:01 +01:00
|
|
|
.arg(QString::number(value.r(), 'f', 2))
|
|
|
|
.arg(QString::number(value.g(), 'f', 2))
|
|
|
|
.arg(QString::number(value.b(), 'f', 2))
|
|
|
|
.arg(QString::number(value.a(), 'f', 2));
|
2016-04-09 17:23:15 +01:00
|
|
|
}
|
|
|
|
case Format::IA8:
|
2016-09-18 01:38:01 +01:00
|
|
|
return QString("Index: %1, Alpha: %2").arg(pixel[0]).arg(pixel[1]);
|
2016-04-09 17:23:15 +01:00
|
|
|
case Format::RG8: {
|
|
|
|
auto value = Color::DecodeRG8(pixel) / 255.0f;
|
|
|
|
return QString("Red: %1, Green: %2")
|
2016-09-18 01:38:01 +01:00
|
|
|
.arg(QString::number(value.r(), 'f', 2))
|
|
|
|
.arg(QString::number(value.g(), 'f', 2));
|
2016-04-09 17:23:15 +01:00
|
|
|
}
|
|
|
|
case Format::I8:
|
|
|
|
return QString("Index: %1").arg(*pixel);
|
|
|
|
case Format::A8:
|
|
|
|
return QString("Alpha: %1").arg(QString::number(*pixel / 255.0f, 'f', 2));
|
|
|
|
case Format::IA4:
|
2016-09-18 01:38:01 +01:00
|
|
|
return QString("Index: %1, Alpha: %2").arg(*pixel & 0xF).arg((*pixel & 0xF0) >> 4);
|
|
|
|
case Format::I4: {
|
2016-04-09 17:23:15 +01:00
|
|
|
u8 i = (*pixel >> ((offset % 2) ? 4 : 0)) & 0xF;
|
|
|
|
return QString("Index: %1").arg(i);
|
|
|
|
}
|
2016-09-18 01:38:01 +01:00
|
|
|
case Format::A4: {
|
2016-04-09 17:23:15 +01:00
|
|
|
u8 a = (*pixel >> ((offset % 2) ? 4 : 0)) & 0xF;
|
|
|
|
return QString("Alpha: %1").arg(QString::number(a / 15.0f, 'f', 2));
|
|
|
|
}
|
|
|
|
case Format::ETC1:
|
|
|
|
case Format::ETC1A4:
|
|
|
|
// TODO: Display block information or channel values?
|
|
|
|
return QString("Compressed data");
|
2016-09-18 01:38:01 +01:00
|
|
|
case Format::D16: {
|
2016-04-09 17:23:15 +01:00
|
|
|
auto value = Color::DecodeD16(pixel);
|
|
|
|
return QString("Depth: %1").arg(QString::number(value / (float)0xFFFF, 'f', 4));
|
|
|
|
}
|
2016-09-18 01:38:01 +01:00
|
|
|
case Format::D24: {
|
2016-04-09 17:23:15 +01:00
|
|
|
auto value = Color::DecodeD24(pixel);
|
|
|
|
return QString("Depth: %1").arg(QString::number(value / (float)0xFFFFFF, 'f', 4));
|
|
|
|
}
|
|
|
|
case Format::D24X8:
|
2016-09-18 01:38:01 +01:00
|
|
|
case Format::X24S8: {
|
2016-04-09 17:23:15 +01:00
|
|
|
auto values = Color::DecodeD24S8(pixel);
|
2016-09-18 01:38:01 +01:00
|
|
|
return QString("Depth: %1, Stencil: %2")
|
|
|
|
.arg(QString::number(values[0] / (float)0xFFFFFF, 'f', 4))
|
|
|
|
.arg(values[1]);
|
2016-04-09 17:23:15 +01:00
|
|
|
}
|
|
|
|
case Format::Unknown:
|
|
|
|
return QString("Unknown format");
|
|
|
|
default:
|
|
|
|
return QString("Unhandled format");
|
|
|
|
}
|
|
|
|
return QString("");
|
|
|
|
};
|
|
|
|
|
|
|
|
QString nibbles = "";
|
|
|
|
for (unsigned i = 0; i < nibbles_per_pixel; i++) {
|
|
|
|
unsigned nibble_index = i;
|
|
|
|
if (nibble_mode) {
|
|
|
|
nibble_index += (offset % 2) ? 0 : 1;
|
|
|
|
}
|
|
|
|
u8 byte = pixel[nibble_index / 2];
|
|
|
|
u8 nibble = (byte >> ((nibble_index % 2) ? 0 : 4)) & 0xF;
|
|
|
|
nibbles.append(QString::number(nibble, 16).toUpper());
|
|
|
|
}
|
|
|
|
|
2016-09-18 01:38:01 +01:00
|
|
|
surface_info_label->setText(
|
|
|
|
QString("Raw: 0x%3\n(%4)").arg(nibbles).arg(GetText(surface_format, pixel)));
|
2016-04-09 17:23:15 +01:00
|
|
|
surface_info_label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
|
|
|
|
}
|
|
|
|
|
2016-09-18 01:38:01 +01:00
|
|
|
void GraphicsSurfaceWidget::OnUpdate() {
|
2016-04-09 17:23:15 +01:00
|
|
|
QPixmap pixmap;
|
|
|
|
|
|
|
|
switch (surface_source) {
|
2016-09-18 01:38:01 +01:00
|
|
|
case Source::ColorBuffer: {
|
|
|
|
// TODO: Store a reference to the registers in the debug context instead of accessing them
|
|
|
|
// directly...
|
2016-04-09 17:23:15 +01:00
|
|
|
|
2017-01-28 05:47:34 +00:00
|
|
|
const auto& framebuffer = Pica::g_state.regs.framebuffer.framebuffer;
|
2016-04-09 17:23:15 +01:00
|
|
|
|
|
|
|
surface_address = framebuffer.GetColorBufferPhysicalAddress();
|
|
|
|
surface_width = framebuffer.GetWidth();
|
|
|
|
surface_height = framebuffer.GetHeight();
|
|
|
|
|
|
|
|
switch (framebuffer.color_format) {
|
2017-01-28 05:47:34 +00:00
|
|
|
case Pica::FramebufferRegs::ColorFormat::RGBA8:
|
2016-04-09 17:23:15 +01:00
|
|
|
surface_format = Format::RGBA8;
|
|
|
|
break;
|
|
|
|
|
2017-01-28 05:47:34 +00:00
|
|
|
case Pica::FramebufferRegs::ColorFormat::RGB8:
|
2016-04-09 17:23:15 +01:00
|
|
|
surface_format = Format::RGB8;
|
|
|
|
break;
|
|
|
|
|
2017-01-28 05:47:34 +00:00
|
|
|
case Pica::FramebufferRegs::ColorFormat::RGB5A1:
|
2016-04-09 17:23:15 +01:00
|
|
|
surface_format = Format::RGB5A1;
|
|
|
|
break;
|
|
|
|
|
2017-01-28 05:47:34 +00:00
|
|
|
case Pica::FramebufferRegs::ColorFormat::RGB565:
|
2016-04-09 17:23:15 +01:00
|
|
|
surface_format = Format::RGB565;
|
|
|
|
break;
|
|
|
|
|
2017-01-28 05:47:34 +00:00
|
|
|
case Pica::FramebufferRegs::ColorFormat::RGBA4:
|
2016-04-09 17:23:15 +01:00
|
|
|
surface_format = Format::RGBA4;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
surface_format = Format::Unknown;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2016-09-18 01:38:01 +01:00
|
|
|
case Source::DepthBuffer: {
|
2017-01-28 05:47:34 +00:00
|
|
|
const auto& framebuffer = Pica::g_state.regs.framebuffer.framebuffer;
|
2016-04-09 17:23:15 +01:00
|
|
|
|
|
|
|
surface_address = framebuffer.GetDepthBufferPhysicalAddress();
|
|
|
|
surface_width = framebuffer.GetWidth();
|
|
|
|
surface_height = framebuffer.GetHeight();
|
|
|
|
|
|
|
|
switch (framebuffer.depth_format) {
|
2017-01-28 05:47:34 +00:00
|
|
|
case Pica::FramebufferRegs::DepthFormat::D16:
|
2016-04-09 17:23:15 +01:00
|
|
|
surface_format = Format::D16;
|
|
|
|
break;
|
|
|
|
|
2017-01-28 05:47:34 +00:00
|
|
|
case Pica::FramebufferRegs::DepthFormat::D24:
|
2016-04-09 17:23:15 +01:00
|
|
|
surface_format = Format::D24;
|
|
|
|
break;
|
|
|
|
|
2017-01-28 05:47:34 +00:00
|
|
|
case Pica::FramebufferRegs::DepthFormat::D24S8:
|
2016-04-09 17:23:15 +01:00
|
|
|
surface_format = Format::D24X8;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
surface_format = Format::Unknown;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2016-09-18 01:38:01 +01:00
|
|
|
case Source::StencilBuffer: {
|
2017-01-28 05:47:34 +00:00
|
|
|
const auto& framebuffer = Pica::g_state.regs.framebuffer.framebuffer;
|
2016-04-09 17:23:15 +01:00
|
|
|
|
|
|
|
surface_address = framebuffer.GetDepthBufferPhysicalAddress();
|
|
|
|
surface_width = framebuffer.GetWidth();
|
|
|
|
surface_height = framebuffer.GetHeight();
|
|
|
|
|
|
|
|
switch (framebuffer.depth_format) {
|
2017-01-28 05:47:34 +00:00
|
|
|
case Pica::FramebufferRegs::DepthFormat::D24S8:
|
2016-04-09 17:23:15 +01:00
|
|
|
surface_format = Format::X24S8;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
surface_format = Format::Unknown;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case Source::Texture0:
|
|
|
|
case Source::Texture1:
|
2016-09-18 01:38:01 +01:00
|
|
|
case Source::Texture2: {
|
2016-04-09 17:23:15 +01:00
|
|
|
unsigned texture_index;
|
2016-09-18 01:38:01 +01:00
|
|
|
if (surface_source == Source::Texture0)
|
|
|
|
texture_index = 0;
|
|
|
|
else if (surface_source == Source::Texture1)
|
|
|
|
texture_index = 1;
|
|
|
|
else if (surface_source == Source::Texture2)
|
|
|
|
texture_index = 2;
|
2016-04-09 17:23:15 +01:00
|
|
|
else {
|
|
|
|
qDebug() << "Unknown texture source " << static_cast<int>(surface_source);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2017-01-28 04:51:59 +00:00
|
|
|
const auto texture = Pica::g_state.regs.texturing.GetTextures()[texture_index];
|
2017-01-05 22:11:23 +00:00
|
|
|
auto info = Pica::Texture::TextureInfo::FromPicaRegister(texture.config, texture.format);
|
2016-04-09 17:23:15 +01:00
|
|
|
|
|
|
|
surface_address = info.physical_address;
|
|
|
|
surface_width = info.width;
|
|
|
|
surface_height = info.height;
|
|
|
|
surface_format = static_cast<Format>(info.format);
|
|
|
|
|
|
|
|
if (surface_format > Format::MaxTextureFormat) {
|
|
|
|
qDebug() << "Unknown texture format " << static_cast<int>(info.format);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2016-09-18 01:38:01 +01:00
|
|
|
case Source::Custom: {
|
2016-04-09 17:23:15 +01:00
|
|
|
// 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));
|
|
|
|
|
|
|
|
// TODO: Implement a good way to visualize alpha components!
|
|
|
|
|
|
|
|
QImage decoded_image(surface_width, surface_height, QImage::Format_ARGB32);
|
2018-11-21 17:01:19 +00:00
|
|
|
u8* buffer = Core::System::GetInstance().Memory().GetPhysicalPointer(surface_address);
|
2016-04-09 17:23:15 +01:00
|
|
|
|
|
|
|
if (buffer == nullptr) {
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (surface_format == Format::Unknown) {
|
|
|
|
surface_picture_label->hide();
|
|
|
|
surface_info_label->setText(tr("(unknown surface format)"));
|
|
|
|
surface_info_label->setAlignment(Qt::AlignCenter);
|
|
|
|
surface_picker_x_control->setEnabled(false);
|
|
|
|
surface_picker_y_control->setEnabled(false);
|
|
|
|
save_surface->setEnabled(false);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
surface_picture_label->show();
|
|
|
|
|
|
|
|
if (surface_format <= Format::MaxTextureFormat) {
|
|
|
|
// Generate a virtual texture
|
2017-01-05 22:11:23 +00:00
|
|
|
Pica::Texture::TextureInfo info;
|
2016-04-09 17:23:15 +01:00
|
|
|
info.physical_address = surface_address;
|
|
|
|
info.width = surface_width;
|
|
|
|
info.height = surface_height;
|
2017-01-28 04:51:59 +00:00
|
|
|
info.format = static_cast<Pica::TexturingRegs::TextureFormat>(surface_format);
|
2017-01-06 03:19:06 +00:00
|
|
|
info.SetDefaultStride();
|
2016-04-09 17:23:15 +01:00
|
|
|
|
|
|
|
for (unsigned int y = 0; y < surface_height; ++y) {
|
|
|
|
for (unsigned int x = 0; x < surface_width; ++x) {
|
2019-02-27 03:38:34 +00:00
|
|
|
Common::Vec4<u8> color = Pica::Texture::LookupTexture(buffer, x, y, info, true);
|
2016-04-09 17:23:15 +01:00
|
|
|
decoded_image.setPixel(x, y, qRgba(color.r(), color.g(), color.b(), color.a()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
2017-01-06 03:19:06 +00:00
|
|
|
// We handle depth formats here because DebugUtils only supports TextureFormats
|
|
|
|
|
|
|
|
// TODO(yuriks): Convert to newer tile-based addressing
|
|
|
|
unsigned nibbles_per_pixel = GraphicsSurfaceWidget::NibblesPerPixel(surface_format);
|
|
|
|
unsigned stride = nibbles_per_pixel * surface_width / 2;
|
2016-04-09 17:23:15 +01:00
|
|
|
|
2016-09-18 01:38:01 +01:00
|
|
|
ASSERT_MSG(nibbles_per_pixel >= 2,
|
|
|
|
"Depth decoder only supports formats with at least one byte per pixel");
|
2016-04-09 17:23:15 +01:00
|
|
|
unsigned bytes_per_pixel = nibbles_per_pixel / 2;
|
|
|
|
|
|
|
|
for (unsigned int y = 0; y < surface_height; ++y) {
|
|
|
|
for (unsigned int x = 0; x < surface_width; ++x) {
|
|
|
|
const u32 coarse_y = y & ~7;
|
|
|
|
u32 offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * stride;
|
|
|
|
const u8* pixel = buffer + offset;
|
2019-02-27 03:38:34 +00:00
|
|
|
Common::Vec4<u8> color = {0, 0, 0, 0};
|
2016-04-09 17:23:15 +01:00
|
|
|
|
2016-09-18 01:38:01 +01:00
|
|
|
switch (surface_format) {
|
|
|
|
case Format::D16: {
|
2016-04-09 17:23:15 +01:00
|
|
|
u32 data = Color::DecodeD16(pixel);
|
|
|
|
color.r() = data & 0xFF;
|
|
|
|
color.g() = (data >> 8) & 0xFF;
|
|
|
|
break;
|
|
|
|
}
|
2016-09-18 01:38:01 +01:00
|
|
|
case Format::D24: {
|
2016-04-09 17:23:15 +01:00
|
|
|
u32 data = Color::DecodeD24(pixel);
|
|
|
|
color.r() = data & 0xFF;
|
|
|
|
color.g() = (data >> 8) & 0xFF;
|
|
|
|
color.b() = (data >> 16) & 0xFF;
|
|
|
|
break;
|
|
|
|
}
|
2016-09-18 01:38:01 +01:00
|
|
|
case Format::D24X8: {
|
2019-02-27 03:38:34 +00:00
|
|
|
Common::Vec2<u32> data = Color::DecodeD24S8(pixel);
|
2016-04-09 17:23:15 +01:00
|
|
|
color.r() = data.x & 0xFF;
|
|
|
|
color.g() = (data.x >> 8) & 0xFF;
|
|
|
|
color.b() = (data.x >> 16) & 0xFF;
|
|
|
|
break;
|
|
|
|
}
|
2016-09-18 01:38:01 +01:00
|
|
|
case Format::X24S8: {
|
2019-02-27 03:38:34 +00:00
|
|
|
Common::Vec2<u32> data = Color::DecodeD24S8(pixel);
|
2016-04-09 17:23:15 +01:00
|
|
|
color.r() = color.g() = color.b() = data.y;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
qDebug() << "Unknown surface format " << static_cast<int>(surface_format);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
decoded_image.setPixel(x, y, qRgba(color.r(), color.g(), color.b(), 255));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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() {
|
Port various minor changes from yuzu PRs (#4725)
* common/thread: Remove unused functions
Many of these functions are carried over from Dolphin (where they aren't
used anymore). Given these have no use (and we really shouldn't be
screwing around with OS-specific thread scheduler handling from the
emulator, these can be removed.
The function for setting the thread name is left, however, since it can
have debugging utility usages.
* input_common/sdl: Use a type alias to shorten declaration of GetPollers
Just makes the definitions a little bit more tidy.
* input_common/sdl: Correct return values within implementations of GetPollers()
In both cases, we weren't actually returning anything, which is
undefined behavior.
* yuzu/debugger/graphics_surface: Fill in missing surface format listings
Fills in the missing surface types that were marked as unknown. The
order corresponds with the TextureFormat enum within
video_core/texture.h.
We also don't need to all of these strings as translatable (only the
first string, as it's an English word).
* yuzu/debugger/graphics_surface: Clean up connection overload deduction
We can utilize qOverload with the signal connections to make the
function deducing a little less ugly.
* yuzu/debugger/graphics_surface: Tidy up SaveSurface
- Use QStringLiteral where applicable.
- Use const where applicable
- Remove unnecessary precondition check (we already assert the pixbuf
being non null)
* yuzu/debugger/graphics_surface: Display error messages for file I/O errors
* core: Add missing override specifiers where applicable
Applies the override specifier where applicable. In the case of
destructors that are defaulted in their definition, they can
simply be removed.
This also removes the unnecessary inclusions being done in audin_u and
audrec_u, given their close proximity.
* kernel/thread: Make parameter of GetWaitObjectIndex() const qualified
The pointed to member is never actually modified, so it can be made
const.
* kernel/thread: Avoid sign conversion within GetCommandBufferAddress()
Previously this was performing a u64 + int sign conversion. When dealing
with addresses, we should generally be keeping the arithmetic in the
same signedness type.
This also gets rid of the static lifetime of the constant, as there's no
need to make a trivial type like this potentially live for the entire
duration of the program.
* kernel/codeset: Make CodeSet's memory data member a regular std::vector
The use of a shared_ptr is an implementation detail of the VMManager
itself when mapping memory. Because of that, we shouldn't require all
users of the CodeSet to have to allocate the shared_ptr ahead of time.
It's intended that CodeSet simply pass in the required direct data, and
that the memory manager takes care of it from that point on.
This means we just do the shared pointer allocation in a single place,
when loading modules, as opposed to in each loader.
* kernel/wait_object: Make ShouldWait() take thread members by pointer-to-const
Given this is intended as a querying function, it doesn't make sense to
allow the implementer to modify the state of the given thread.
2019-05-01 13:28:49 +01:00
|
|
|
const QString png_filter = tr("Portable Network Graphic (*.png)");
|
|
|
|
const QString bin_filter = tr("Binary data (*.bin)");
|
2016-04-09 17:23:15 +01:00
|
|
|
|
Port various minor changes from yuzu PRs (#4725)
* common/thread: Remove unused functions
Many of these functions are carried over from Dolphin (where they aren't
used anymore). Given these have no use (and we really shouldn't be
screwing around with OS-specific thread scheduler handling from the
emulator, these can be removed.
The function for setting the thread name is left, however, since it can
have debugging utility usages.
* input_common/sdl: Use a type alias to shorten declaration of GetPollers
Just makes the definitions a little bit more tidy.
* input_common/sdl: Correct return values within implementations of GetPollers()
In both cases, we weren't actually returning anything, which is
undefined behavior.
* yuzu/debugger/graphics_surface: Fill in missing surface format listings
Fills in the missing surface types that were marked as unknown. The
order corresponds with the TextureFormat enum within
video_core/texture.h.
We also don't need to all of these strings as translatable (only the
first string, as it's an English word).
* yuzu/debugger/graphics_surface: Clean up connection overload deduction
We can utilize qOverload with the signal connections to make the
function deducing a little less ugly.
* yuzu/debugger/graphics_surface: Tidy up SaveSurface
- Use QStringLiteral where applicable.
- Use const where applicable
- Remove unnecessary precondition check (we already assert the pixbuf
being non null)
* yuzu/debugger/graphics_surface: Display error messages for file I/O errors
* core: Add missing override specifiers where applicable
Applies the override specifier where applicable. In the case of
destructors that are defaulted in their definition, they can
simply be removed.
This also removes the unnecessary inclusions being done in audin_u and
audrec_u, given their close proximity.
* kernel/thread: Make parameter of GetWaitObjectIndex() const qualified
The pointed to member is never actually modified, so it can be made
const.
* kernel/thread: Avoid sign conversion within GetCommandBufferAddress()
Previously this was performing a u64 + int sign conversion. When dealing
with addresses, we should generally be keeping the arithmetic in the
same signedness type.
This also gets rid of the static lifetime of the constant, as there's no
need to make a trivial type like this potentially live for the entire
duration of the program.
* kernel/codeset: Make CodeSet's memory data member a regular std::vector
The use of a shared_ptr is an implementation detail of the VMManager
itself when mapping memory. Because of that, we shouldn't require all
users of the CodeSet to have to allocate the shared_ptr ahead of time.
It's intended that CodeSet simply pass in the required direct data, and
that the memory manager takes care of it from that point on.
This means we just do the shared pointer allocation in a single place,
when loading modules, as opposed to in each loader.
* kernel/wait_object: Make ShouldWait() take thread members by pointer-to-const
Given this is intended as a querying function, it doesn't make sense to
allow the implementer to modify the state of the given thread.
2019-05-01 13:28:49 +01:00
|
|
|
QString selected_filter;
|
|
|
|
const QString filename = QFileDialog::getSaveFileName(
|
2016-09-18 01:38:01 +01:00
|
|
|
this, tr("Save Surface"),
|
Port various minor changes from yuzu PRs (#4725)
* common/thread: Remove unused functions
Many of these functions are carried over from Dolphin (where they aren't
used anymore). Given these have no use (and we really shouldn't be
screwing around with OS-specific thread scheduler handling from the
emulator, these can be removed.
The function for setting the thread name is left, however, since it can
have debugging utility usages.
* input_common/sdl: Use a type alias to shorten declaration of GetPollers
Just makes the definitions a little bit more tidy.
* input_common/sdl: Correct return values within implementations of GetPollers()
In both cases, we weren't actually returning anything, which is
undefined behavior.
* yuzu/debugger/graphics_surface: Fill in missing surface format listings
Fills in the missing surface types that were marked as unknown. The
order corresponds with the TextureFormat enum within
video_core/texture.h.
We also don't need to all of these strings as translatable (only the
first string, as it's an English word).
* yuzu/debugger/graphics_surface: Clean up connection overload deduction
We can utilize qOverload with the signal connections to make the
function deducing a little less ugly.
* yuzu/debugger/graphics_surface: Tidy up SaveSurface
- Use QStringLiteral where applicable.
- Use const where applicable
- Remove unnecessary precondition check (we already assert the pixbuf
being non null)
* yuzu/debugger/graphics_surface: Display error messages for file I/O errors
* core: Add missing override specifiers where applicable
Applies the override specifier where applicable. In the case of
destructors that are defaulted in their definition, they can
simply be removed.
This also removes the unnecessary inclusions being done in audin_u and
audrec_u, given their close proximity.
* kernel/thread: Make parameter of GetWaitObjectIndex() const qualified
The pointed to member is never actually modified, so it can be made
const.
* kernel/thread: Avoid sign conversion within GetCommandBufferAddress()
Previously this was performing a u64 + int sign conversion. When dealing
with addresses, we should generally be keeping the arithmetic in the
same signedness type.
This also gets rid of the static lifetime of the constant, as there's no
need to make a trivial type like this potentially live for the entire
duration of the program.
* kernel/codeset: Make CodeSet's memory data member a regular std::vector
The use of a shared_ptr is an implementation detail of the VMManager
itself when mapping memory. Because of that, we shouldn't require all
users of the CodeSet to have to allocate the shared_ptr ahead of time.
It's intended that CodeSet simply pass in the required direct data, and
that the memory manager takes care of it from that point on.
This means we just do the shared pointer allocation in a single place,
when loading modules, as opposed to in each loader.
* kernel/wait_object: Make ShouldWait() take thread members by pointer-to-const
Given this is intended as a querying function, it doesn't make sense to
allow the implementer to modify the state of the given thread.
2019-05-01 13:28:49 +01:00
|
|
|
QStringLiteral("texture-0x%1.png").arg(QString::number(surface_address, 16)),
|
|
|
|
QStringLiteral("%1;;%2").arg(png_filter, bin_filter), &selected_filter);
|
2016-04-09 17:23:15 +01:00
|
|
|
|
|
|
|
if (filename.isEmpty()) {
|
|
|
|
// If the user canceled the dialog, don't save anything.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
Port various minor changes from yuzu PRs (#4725)
* common/thread: Remove unused functions
Many of these functions are carried over from Dolphin (where they aren't
used anymore). Given these have no use (and we really shouldn't be
screwing around with OS-specific thread scheduler handling from the
emulator, these can be removed.
The function for setting the thread name is left, however, since it can
have debugging utility usages.
* input_common/sdl: Use a type alias to shorten declaration of GetPollers
Just makes the definitions a little bit more tidy.
* input_common/sdl: Correct return values within implementations of GetPollers()
In both cases, we weren't actually returning anything, which is
undefined behavior.
* yuzu/debugger/graphics_surface: Fill in missing surface format listings
Fills in the missing surface types that were marked as unknown. The
order corresponds with the TextureFormat enum within
video_core/texture.h.
We also don't need to all of these strings as translatable (only the
first string, as it's an English word).
* yuzu/debugger/graphics_surface: Clean up connection overload deduction
We can utilize qOverload with the signal connections to make the
function deducing a little less ugly.
* yuzu/debugger/graphics_surface: Tidy up SaveSurface
- Use QStringLiteral where applicable.
- Use const where applicable
- Remove unnecessary precondition check (we already assert the pixbuf
being non null)
* yuzu/debugger/graphics_surface: Display error messages for file I/O errors
* core: Add missing override specifiers where applicable
Applies the override specifier where applicable. In the case of
destructors that are defaulted in their definition, they can
simply be removed.
This also removes the unnecessary inclusions being done in audin_u and
audrec_u, given their close proximity.
* kernel/thread: Make parameter of GetWaitObjectIndex() const qualified
The pointed to member is never actually modified, so it can be made
const.
* kernel/thread: Avoid sign conversion within GetCommandBufferAddress()
Previously this was performing a u64 + int sign conversion. When dealing
with addresses, we should generally be keeping the arithmetic in the
same signedness type.
This also gets rid of the static lifetime of the constant, as there's no
need to make a trivial type like this potentially live for the entire
duration of the program.
* kernel/codeset: Make CodeSet's memory data member a regular std::vector
The use of a shared_ptr is an implementation detail of the VMManager
itself when mapping memory. Because of that, we shouldn't require all
users of the CodeSet to have to allocate the shared_ptr ahead of time.
It's intended that CodeSet simply pass in the required direct data, and
that the memory manager takes care of it from that point on.
This means we just do the shared pointer allocation in a single place,
when loading modules, as opposed to in each loader.
* kernel/wait_object: Make ShouldWait() take thread members by pointer-to-const
Given this is intended as a querying function, it doesn't make sense to
allow the implementer to modify the state of the given thread.
2019-05-01 13:28:49 +01:00
|
|
|
if (selected_filter == png_filter) {
|
|
|
|
const QPixmap* const pixmap = surface_picture_label->pixmap();
|
2016-04-09 17:23:15 +01:00
|
|
|
ASSERT_MSG(pixmap != nullptr, "No pixmap set");
|
|
|
|
|
Port various minor changes from yuzu PRs (#4725)
* common/thread: Remove unused functions
Many of these functions are carried over from Dolphin (where they aren't
used anymore). Given these have no use (and we really shouldn't be
screwing around with OS-specific thread scheduler handling from the
emulator, these can be removed.
The function for setting the thread name is left, however, since it can
have debugging utility usages.
* input_common/sdl: Use a type alias to shorten declaration of GetPollers
Just makes the definitions a little bit more tidy.
* input_common/sdl: Correct return values within implementations of GetPollers()
In both cases, we weren't actually returning anything, which is
undefined behavior.
* yuzu/debugger/graphics_surface: Fill in missing surface format listings
Fills in the missing surface types that were marked as unknown. The
order corresponds with the TextureFormat enum within
video_core/texture.h.
We also don't need to all of these strings as translatable (only the
first string, as it's an English word).
* yuzu/debugger/graphics_surface: Clean up connection overload deduction
We can utilize qOverload with the signal connections to make the
function deducing a little less ugly.
* yuzu/debugger/graphics_surface: Tidy up SaveSurface
- Use QStringLiteral where applicable.
- Use const where applicable
- Remove unnecessary precondition check (we already assert the pixbuf
being non null)
* yuzu/debugger/graphics_surface: Display error messages for file I/O errors
* core: Add missing override specifiers where applicable
Applies the override specifier where applicable. In the case of
destructors that are defaulted in their definition, they can
simply be removed.
This also removes the unnecessary inclusions being done in audin_u and
audrec_u, given their close proximity.
* kernel/thread: Make parameter of GetWaitObjectIndex() const qualified
The pointed to member is never actually modified, so it can be made
const.
* kernel/thread: Avoid sign conversion within GetCommandBufferAddress()
Previously this was performing a u64 + int sign conversion. When dealing
with addresses, we should generally be keeping the arithmetic in the
same signedness type.
This also gets rid of the static lifetime of the constant, as there's no
need to make a trivial type like this potentially live for the entire
duration of the program.
* kernel/codeset: Make CodeSet's memory data member a regular std::vector
The use of a shared_ptr is an implementation detail of the VMManager
itself when mapping memory. Because of that, we shouldn't require all
users of the CodeSet to have to allocate the shared_ptr ahead of time.
It's intended that CodeSet simply pass in the required direct data, and
that the memory manager takes care of it from that point on.
This means we just do the shared pointer allocation in a single place,
when loading modules, as opposed to in each loader.
* kernel/wait_object: Make ShouldWait() take thread members by pointer-to-const
Given this is intended as a querying function, it doesn't make sense to
allow the implementer to modify the state of the given thread.
2019-05-01 13:28:49 +01:00
|
|
|
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) {
|
|
|
|
const u8* const buffer =
|
|
|
|
Core::System::GetInstance().Memory().GetPhysicalPointer(surface_address);
|
2016-04-09 17:23:15 +01:00
|
|
|
ASSERT_MSG(buffer != nullptr, "Memory not accessible");
|
|
|
|
|
Port various minor changes from yuzu PRs (#4725)
* common/thread: Remove unused functions
Many of these functions are carried over from Dolphin (where they aren't
used anymore). Given these have no use (and we really shouldn't be
screwing around with OS-specific thread scheduler handling from the
emulator, these can be removed.
The function for setting the thread name is left, however, since it can
have debugging utility usages.
* input_common/sdl: Use a type alias to shorten declaration of GetPollers
Just makes the definitions a little bit more tidy.
* input_common/sdl: Correct return values within implementations of GetPollers()
In both cases, we weren't actually returning anything, which is
undefined behavior.
* yuzu/debugger/graphics_surface: Fill in missing surface format listings
Fills in the missing surface types that were marked as unknown. The
order corresponds with the TextureFormat enum within
video_core/texture.h.
We also don't need to all of these strings as translatable (only the
first string, as it's an English word).
* yuzu/debugger/graphics_surface: Clean up connection overload deduction
We can utilize qOverload with the signal connections to make the
function deducing a little less ugly.
* yuzu/debugger/graphics_surface: Tidy up SaveSurface
- Use QStringLiteral where applicable.
- Use const where applicable
- Remove unnecessary precondition check (we already assert the pixbuf
being non null)
* yuzu/debugger/graphics_surface: Display error messages for file I/O errors
* core: Add missing override specifiers where applicable
Applies the override specifier where applicable. In the case of
destructors that are defaulted in their definition, they can
simply be removed.
This also removes the unnecessary inclusions being done in audin_u and
audrec_u, given their close proximity.
* kernel/thread: Make parameter of GetWaitObjectIndex() const qualified
The pointed to member is never actually modified, so it can be made
const.
* kernel/thread: Avoid sign conversion within GetCommandBufferAddress()
Previously this was performing a u64 + int sign conversion. When dealing
with addresses, we should generally be keeping the arithmetic in the
same signedness type.
This also gets rid of the static lifetime of the constant, as there's no
need to make a trivial type like this potentially live for the entire
duration of the program.
* kernel/codeset: Make CodeSet's memory data member a regular std::vector
The use of a shared_ptr is an implementation detail of the VMManager
itself when mapping memory. Because of that, we shouldn't require all
users of the CodeSet to have to allocate the shared_ptr ahead of time.
It's intended that CodeSet simply pass in the required direct data, and
that the memory manager takes care of it from that point on.
This means we just do the shared pointer allocation in a single place,
when loading modules, as opposed to in each loader.
* kernel/wait_object: Make ShouldWait() take thread members by pointer-to-const
Given this is intended as a querying function, it doesn't make sense to
allow the implementer to modify the state of the given thread.
2019-05-01 13:28:49 +01:00
|
|
|
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 * NibblesPerPixel(surface_format) / 2;
|
|
|
|
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."));
|
|
|
|
}
|
2016-04-09 17:23:15 +01:00
|
|
|
} else {
|
|
|
|
UNREACHABLE_MSG("Unhandled filter selected");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned int GraphicsSurfaceWidget::NibblesPerPixel(GraphicsSurfaceWidget::Format format) {
|
|
|
|
if (format <= Format::MaxTextureFormat) {
|
2017-01-28 04:51:59 +00:00
|
|
|
return Pica::TexturingRegs::NibblesPerPixel(
|
|
|
|
static_cast<Pica::TexturingRegs::TextureFormat>(format));
|
2016-04-09 17:23:15 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
switch (format) {
|
2016-09-18 01:38:01 +01:00
|
|
|
case Format::D24X8:
|
|
|
|
case Format::X24S8:
|
|
|
|
return 4 * 2;
|
|
|
|
case Format::D24:
|
|
|
|
return 3 * 2;
|
|
|
|
case Format::D16:
|
|
|
|
return 2 * 2;
|
|
|
|
default:
|
2016-09-19 02:01:46 +01:00
|
|
|
UNREACHABLE_MSG("GraphicsSurfaceWidget::BytesPerPixel: this should not be reached as this "
|
|
|
|
"function should be given a format which is in "
|
2018-03-27 16:28:42 +01:00
|
|
|
"GraphicsSurfaceWidget::Format. Instead got {}",
|
2016-09-18 01:38:01 +01:00
|
|
|
static_cast<int>(format));
|
|
|
|
return 0;
|
2016-04-09 17:23:15 +01:00
|
|
|
}
|
|
|
|
}
|