citra_qt: Improve graphics API intergration

* Add renderer debug option which toggles debug output in OpenGL/validation layers in Vulkan

* Fix many warnings and replace deprecated Qt functionailty with newer alternatives
This commit is contained in:
GPUCode
2022-10-02 18:54:00 +03:00
parent 88d0b7de13
commit dbfa06c6b1
20 changed files with 161 additions and 132 deletions

View File

@ -7,17 +7,8 @@
#include <regex>
#include <string>
#include <thread>
// This needs to be included before getopt.h because the latter #defines symbols used by it
#include "common/microprofile.h"
#ifdef _WIN32
// windows.h needs to be included before shellapi.h
#include <windows.h>
#include <shellapi.h>
#endif
#include "citra/config.h"
#include "citra/emu_window/emu_window_sdl2.h"
#include "citra/lodepng_image_interface.h"
@ -25,20 +16,15 @@
#include "common/detached_tasks.h"
#include "common/file_util.h"
#include "common/logging/backend.h"
#include "common/logging/filter.h"
#include "common/logging/log.h"
#include "common/scm_rev.h"
#include "common/scope_exit.h"
#include "common/string_util.h"
#include "core/core.h"
#include "core/dumping/backend.h"
#include "core/file_sys/cia_container.h"
#include "core/frontend/applets/default_applets.h"
#include "core/frontend/framebuffer_layout.h"
#include "core/gdbstub/gdbstub.h"
#include "core/hle/service/am/am.h"
#include "core/hle/service/cfg/cfg.h"
#include "core/loader/loader.h"
#include "core/movie.h"
#include "core/settings.h"
#include "network/network.h"
@ -51,6 +37,10 @@
#endif
#ifdef _WIN32
// windows.h needs to be included before shellapi.h
#include <windows.h>
#include <shellapi.h>
extern "C" {
// tells Nvidia drivers to use the dedicated GPU by default on laptops with switchable graphics
__declspec(dllexport) unsigned long NvOptimusEnablement = 0x00000001;
@ -103,35 +93,35 @@ static void OnNetworkError(const Network::RoomMember::Error& error) {
break;
case Network::RoomMember::Error::CouldNotConnect:
LOG_ERROR(Network, "Error: Could not connect");
exit(1);
std::exit(1);
break;
case Network::RoomMember::Error::NameCollision:
LOG_ERROR(
Network,
"You tried to use the same nickname as another user that is connected to the Room");
exit(1);
std::exit(1);
break;
case Network::RoomMember::Error::MacCollision:
LOG_ERROR(Network, "You tried to use the same MAC-Address as another user that is "
"connected to the Room");
exit(1);
std::exit(1);
break;
case Network::RoomMember::Error::ConsoleIdCollision:
LOG_ERROR(Network, "Your Console ID conflicted with someone else in the Room");
exit(1);
std::exit(1);
break;
case Network::RoomMember::Error::WrongPassword:
LOG_ERROR(Network, "Room replied with: Wrong password");
exit(1);
std::exit(1);
break;
case Network::RoomMember::Error::WrongVersion:
LOG_ERROR(Network,
"You are using a different version than the room you are trying to connect to");
exit(1);
std::exit(1);
break;
case Network::RoomMember::Error::RoomIsFull:
LOG_ERROR(Network, "The room is full");
exit(1);
std::exit(1);
break;
case Network::RoomMember::Error::HostKicked:
LOG_ERROR(Network, "You have been kicked by the host");
@ -139,6 +129,8 @@ static void OnNetworkError(const Network::RoomMember::Error& error) {
case Network::RoomMember::Error::HostBanned:
LOG_ERROR(Network, "You have been banned by the host");
break;
default:
LOG_ERROR(Network, "Unknown network error {}", error);
}
}

View File

@ -9,7 +9,6 @@
#include <QMessageBox>
#include <QOffscreenSurface>
#include <QOpenGLContext>
#include <QOpenGLFunctions_4_3_Core>
#include <QOpenGLExtraFunctions>
#include <fmt/format.h>
#include "citra_qt/bootmanager.h"
@ -34,11 +33,13 @@ EmuThread::EmuThread(Frontend::GraphicsContext& core_context) : core_context(cor
EmuThread::~EmuThread() = default;
static GMainWindow* GetMainWindow() {
for (QWidget* w : qApp->topLevelWidgets()) {
const auto widgets = qApp->topLevelWidgets();
for (QWidget* w : widgets) {
if (GMainWindow* main = qobject_cast<GMainWindow*>(w)) {
return main;
}
}
return nullptr;
}
@ -48,7 +49,8 @@ void EmuThread::run() {
emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0);
Core::System::GetInstance().Renderer().Rasterizer()->LoadDiskResources(
Core::System& system = Core::System::GetInstance();
system.Renderer().Rasterizer()->LoadDiskResources(
stop_run, [this](VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total) {
emit LoadProgress(stage, value, total);
});
@ -58,18 +60,17 @@ void EmuThread::run() {
core_context.MakeCurrent();
if (Core::System::GetInstance().frame_limiter.IsFrameAdvancing()) {
if (system.frame_limiter.IsFrameAdvancing()) {
// Usually the loading screen is hidden after the first frame is drawn. In this case
// we hide it immediately as we need to wait for user input to start the emulation.
emit HideLoadingScreen();
Core::System::GetInstance().frame_limiter.WaitOnce();
system.frame_limiter.WaitOnce();
}
// Holds whether the cpu was running during the last iteration,
// so that the DebugModeLeft signal can be emitted before the
// next execution step.
bool was_active = false;
Core::System& system = Core::System::GetInstance();
while (!stop_run) {
if (running) {
if (!was_active)
@ -119,8 +120,20 @@ public:
/// Create the original context that should be shared from
explicit OpenGLSharedContext(QSurface* surface) : surface(surface) {
QSurfaceFormat format;
if (Settings::values.graphics_api == Settings::GraphicsAPI::OpenGLES) {
QCoreApplication::setAttribute(Qt::AA_UseOpenGLES);
format.setVersion(3, 2);
} else {
QCoreApplication::setAttribute(Qt::AA_UseDesktopOpenGL);
format.setVersion(4, 4);
format.setProfile(QSurfaceFormat::CoreProfile);
}
if (Settings::values.renderer_debug) {
format.setOption(QSurfaceFormat::FormatOption::DebugContext);
}
// TODO: expose a setting for buffer value (ie default/single/double/triple)
format.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior);
format.setSwapInterval(0);
@ -630,14 +643,6 @@ bool GRenderWindow::InitializeOpenGL() {
child->SetContext(
std::make_unique<OpenGLSharedContext>(context->GetShareContext(), child->windowHandle()));
// Check for OpenGL 4.3 support
if (!QOpenGLContext::globalShareContext()->versionFunctions<QOpenGLFunctions_4_3_Core>()) {
QMessageBox::critical(this, tr("OpenGL 4.3 Unsupported"),
tr("Your GPU may not support OpenGL 4.3, or you do not "
"have the latest graphics driver."));
return false;
}
return true;
}

View File

@ -91,7 +91,7 @@ bool CheatDialog::SaveCheat(int row) {
}
// Check if the cheat lines are valid
auto code_lines = ui->textCode->toPlainText().split(QLatin1Char{'\n'}, QString::SkipEmptyParts);
auto code_lines = ui->textCode->toPlainText().split(QLatin1Char{'\n'}, Qt::SkipEmptyParts);
for (int i = 0; i < code_lines.size(); ++i) {
Cheats::GatewayCheat::CheatLine cheat_line(code_lines[i].toStdString());
if (cheat_line.valid)

View File

@ -482,6 +482,10 @@ void Config::ReadPathValues() {
void Config::ReadRendererValues() {
qt_config->beginGroup(QStringLiteral("Renderer"));
Settings::values.graphics_api = static_cast<Settings::GraphicsAPI>(
ReadSetting(QStringLiteral("graphics_api"), static_cast<u32>(Settings::GraphicsAPI::OpenGL))
.toUInt());
Settings::values.renderer_debug = ReadSetting(QStringLiteral("renderer_debug"), false).toBool();
Settings::values.use_hw_renderer =
ReadSetting(QStringLiteral("use_hw_renderer"), true).toBool();
Settings::values.use_hw_shader = ReadSetting(QStringLiteral("use_hw_shader"), true).toBool();
@ -521,7 +525,7 @@ void Config::ReadRendererValues() {
void Config::ReadShortcutValues() {
qt_config->beginGroup(QStringLiteral("Shortcuts"));
for (auto [name, group, shortcut] : default_hotkeys) {
for (const auto& [name, group, shortcut] : default_hotkeys) {
auto [keyseq, context] = shortcut;
qt_config->beginGroup(group);
qt_config->beginGroup(name);
@ -556,7 +560,7 @@ void Config::ReadSystemValues() {
// https://developers.google.com/media/vp9/live-encoding
const QString DEFAULT_VIDEO_ENCODER_OPTIONS =
QStringLiteral("quality:realtime,speed:6,tile-columns:4,frame-parallel:1,threads:8,row-mt:1");
const QString DEFAULT_AUDIO_ENCODER_OPTIONS = QString{};
const QString DEFAULT_AUDIO_ENCODER_OPTIONS = QStringLiteral("");
void Config::ReadVideoDumpingValues() {
qt_config->beginGroup(QStringLiteral("VideoDumping"));
@ -994,6 +998,9 @@ void Config::SavePathValues() {
void Config::SaveRendererValues() {
qt_config->beginGroup(QStringLiteral("Renderer"));
WriteSetting(QStringLiteral("graphics_api"), static_cast<u32>(Settings::values.graphics_api),
static_cast<u32>(Settings::GraphicsAPI::OpenGL));
WriteSetting(QStringLiteral("renderer_debug"), Settings::values.renderer_debug, false);
WriteSetting(QStringLiteral("use_hw_renderer"), Settings::values.use_hw_renderer, true);
WriteSetting(QStringLiteral("use_hw_shader"), Settings::values.use_hw_shader, true);
#ifdef __APPLE__
@ -1015,9 +1022,9 @@ void Config::SaveRendererValues() {
200);
// Cast to double because Qt's written float values are not human-readable
WriteSetting(QStringLiteral("bg_red"), (double)Settings::values.bg_red, 0.0);
WriteSetting(QStringLiteral("bg_green"), (double)Settings::values.bg_green, 0.0);
WriteSetting(QStringLiteral("bg_blue"), (double)Settings::values.bg_blue, 0.0);
WriteSetting(QStringLiteral("bg_red"), static_cast<double>(Settings::values.bg_red), 0.0);
WriteSetting(QStringLiteral("bg_green"), static_cast<double>(Settings::values.bg_green), 0.0);
WriteSetting(QStringLiteral("bg_blue"), static_cast<double>(Settings::values.bg_blue), 0.0);
WriteSetting(QStringLiteral("texture_filter_name"),
QString::fromStdString(Settings::values.texture_filter_name),

View File

@ -8,8 +8,6 @@
#include "citra_qt/debugger/console.h"
#include "citra_qt/uisettings.h"
#include "common/file_util.h"
#include "common/logging/backend.h"
#include "common/logging/filter.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/settings.h"
@ -24,7 +22,10 @@ ConfigureDebug::ConfigureDebug(QWidget* parent)
QString path = QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::LogDir));
QDesktopServices::openUrl(QUrl::fromLocalFile(path));
});
ui->toggle_cpu_jit->setEnabled(!Core::System::GetInstance().IsPoweredOn());
const bool is_powered_on = Core::System::GetInstance().IsPoweredOn();
ui->toggle_cpu_jit->setEnabled(!is_powered_on);
ui->toggle_renderer_debug->setEnabled(!is_powered_on);
}
ConfigureDebug::~ConfigureDebug() = default;
@ -37,6 +38,7 @@ void ConfigureDebug::SetConfiguration() {
ui->toggle_console->setChecked(UISettings::values.show_console);
ui->log_filter_edit->setText(QString::fromStdString(Settings::values.log_filter));
ui->toggle_cpu_jit->setChecked(Settings::values.use_cpu_jit);
ui->toggle_renderer_debug->setChecked(Settings::values.renderer_debug);
}
void ConfigureDebug::ApplyConfiguration() {
@ -49,6 +51,7 @@ void ConfigureDebug::ApplyConfiguration() {
filter.ParseFilterString(Settings::values.log_filter);
Log::SetGlobalFilter(filter);
Settings::values.use_cpu_jit = ui->toggle_cpu_jit->isChecked();
Settings::values.renderer_debug = ui->toggle_renderer_debug->isChecked();
}
void ConfigureDebug::RetranslateUI() {

View File

@ -114,11 +114,24 @@
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QCheckBox" name="toggle_cpu_jit">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Enables the use of the ARM JIT compiler for emulating the 3DS CPUs. Don't disable unless for debugging purposes&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Enable CPU JIT</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="toggle_renderer_debug">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Enables debug reporting in the currently selected graphics API. Causes measurable performance loss, don't enable unless for debugging purposes&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Enable debug renderer</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>

View File

@ -16,7 +16,6 @@
#include "citra_qt/util/spinbox.h"
#include "common/color.h"
#include "core/core.h"
#include "core/hw/gpu.h"
#include "core/memory.h"
#include "video_core/pica_state.h"
#include "video_core/regs_framebuffer.h"
@ -34,12 +33,15 @@ void SurfacePicture::mousePressEvent(QMouseEvent* event) {
if (!(event->buttons() & Qt::LeftButton))
return;
if (pixmap() == nullptr)
const QPixmap pixmap = this->pixmap(Qt::ReturnByValue);
if (pixmap.isNull()) {
return;
}
if (surface_widget)
surface_widget->Pick(event->x() * pixmap()->width() / width(),
event->y() * pixmap()->height() / height());
if (surface_widget) {
surface_widget->Pick(event->x() * pixmap.width() / width(),
event->y() * pixmap.height() / height());
}
}
void SurfacePicture::mouseMoveEvent(QMouseEvent* event) {
@ -314,48 +316,48 @@ void GraphicsSurfaceWidget::Pick(int x, int y) {
case Format::RGBA8: {
auto value = Color::DecodeRGBA8(pixel) / 255.0f;
return QStringLiteral("Red: %1, Green: %2, Blue: %3, Alpha: %4")
.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));
.arg(QString::number(value.r(), 'f', 2),
QString::number(value.g(), 'f', 2),
QString::number(value.b(), 'f', 2),
QString::number(value.a(), 'f', 2));
}
case Format::RGB8: {
auto value = Color::DecodeRGB8(pixel) / 255.0f;
return QStringLiteral("Red: %1, Green: %2, Blue: %3")
.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.r(), 'f', 2),
QString::number(value.g(), 'f', 2),
QString::number(value.b(), 'f', 2));
}
case Format::RGB5A1: {
auto value = Color::DecodeRGB5A1(pixel) / 255.0f;
return QStringLiteral("Red: %1, Green: %2, Blue: %3, Alpha: %4")
.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));
.arg(QString::number(value.r(), 'f', 2),
QString::number(value.g(), 'f', 2),
QString::number(value.b(), 'f', 2),
QString::number(value.a(), 'f', 2));
}
case Format::RGB565: {
auto value = Color::DecodeRGB565(pixel) / 255.0f;
return QStringLiteral("Red: %1, Green: %2, Blue: %3")
.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.r(), 'f', 2),
QString::number(value.g(), 'f', 2),
QString::number(value.b(), 'f', 2));
}
case Format::RGBA4: {
auto value = Color::DecodeRGBA4(pixel) / 255.0f;
return QStringLiteral("Red: %1, Green: %2, Blue: %3, Alpha: %4")
.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));
.arg(QString::number(value.r(), 'f', 2),
QString::number(value.g(), 'f', 2),
QString::number(value.b(), 'f', 2),
QString::number(value.a(), 'f', 2));
}
case Format::IA8:
return QStringLiteral("Index: %1, Alpha: %2").arg(pixel[0]).arg(pixel[1]);
return QStringLiteral("Index: %1, Alpha: %2").arg(pixel[0], pixel[1]);
case Format::RG8: {
auto value = Color::DecodeRG8(pixel) / 255.0f;
return QStringLiteral("Red: %1, Green: %2")
.arg(QString::number(value.r(), 'f', 2))
.arg(QString::number(value.g(), 'f', 2));
.arg(QString::number(value.r(), 'f', 2),
QString::number(value.g(), 'f', 2));
}
case Format::I8:
return QStringLiteral("Index: %1").arg(*pixel);
@ -363,8 +365,7 @@ void GraphicsSurfaceWidget::Pick(int x, int y) {
return QStringLiteral("Alpha: %1").arg(QString::number(*pixel / 255.0f, 'f', 2));
case Format::IA4:
return QStringLiteral("Index: %1, Alpha: %2")
.arg(*pixel & 0xF)
.arg((*pixel & 0xF0) >> 4);
.arg(*pixel & 0xF, (*pixel & 0xF0) >> 4);
case Format::I4: {
u8 i = (*pixel >> ((offset % 2) ? 4 : 0)) & 0xF;
return QStringLiteral("Index: %1").arg(i);
@ -390,8 +391,7 @@ void GraphicsSurfaceWidget::Pick(int x, int y) {
case Format::X24S8: {
auto values = Color::DecodeD24S8(pixel);
return QStringLiteral("Depth: %1, Stencil: %2")
.arg(QString::number(values[0] / (float)0xFFFFFF, 'f', 4))
.arg(values[1]);
.arg(QString::number(values[0] / (float)0xFFFFFF, 'f', 4), values[1]);
}
case Format::Unknown:
return QStringLiteral("Unknown format");
@ -401,8 +401,8 @@ void GraphicsSurfaceWidget::Pick(int x, int y) {
};
QString nibbles;
for (unsigned i = 0; i < nibbles_per_pixel; i++) {
unsigned nibble_index = i;
for (u32 i = 0; i < nibbles_per_pixel; i++) {
u32 nibble_index = i;
if (nibble_mode) {
nibble_index += (offset % 2) ? 0 : 1;
}
@ -412,7 +412,7 @@ void GraphicsSurfaceWidget::Pick(int x, int y) {
}
surface_info_label->setText(
QStringLiteral("Raw: 0x%3\n(%4)").arg(nibbles).arg(GetText(surface_format, pixel)));
QStringLiteral("Raw: 0x%3\n(%4)").arg(nibbles, GetText(surface_format, pixel)));
surface_info_label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
}
@ -676,8 +676,8 @@ void GraphicsSurfaceWidget::SaveSurface() {
}
if (selected_filter == png_filter) {
const QPixmap* const pixmap = surface_picture_label->pixmap();
ASSERT_MSG(pixmap != nullptr, "No pixmap set");
const QPixmap pixmap = surface_picture_label->pixmap(Qt::ReturnByValue);
ASSERT_MSG(!pixmap.isNull(), "No pixmap set");
QFile file{filename};
if (!file.open(QIODevice::WriteOnly)) {
@ -685,7 +685,7 @@ void GraphicsSurfaceWidget::SaveSurface() {
return;
}
if (!pixmap->save(&file, "PNG")) {
if (!pixmap.save(&file, "PNG")) {
QMessageBox::warning(this, tr("Error"),
tr("Failed to save surface data to file '%1'").arg(filename));
}

View File

@ -111,13 +111,13 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con
}
};
const Instruction instr = par->info.code[index.row()];
const Instruction& instr = par->info.code[index.row()];
const OpCode opcode = instr.opcode;
const OpCode::Info opcode_info = opcode.GetInfo();
const u32 operand_desc_id = opcode_info.type == OpCode::Type::MultiplyAdd
? instr.mad.operand_desc_id.Value()
: instr.common.operand_desc_id.Value();
const SwizzlePattern swizzle = par->info.swizzle_info[operand_desc_id].pattern;
const SwizzlePattern& swizzle = par->info.swizzle_info[operand_desc_id].pattern;
// longest known instruction name: "setemit "
int kOpcodeColumnWidth = 8;
@ -407,8 +407,7 @@ GraphicsVertexShaderWidget::GraphicsVertexShaderWidget(
static_cast<void (QSignalMapper::*)()>(&QSignalMapper::map));
input_data_mapper->setMapping(input_data[i], i);
}
connect(input_data_mapper, static_cast<void (QSignalMapper::*)(int)>(&QSignalMapper::mapped),
this, &GraphicsVertexShaderWidget::OnInputAttributeChanged);
connect(input_data_mapper, &QSignalMapper::mappedInt, this, &GraphicsVertexShaderWidget::OnInputAttributeChanged);
auto main_widget = new QWidget;
auto main_layout = new QVBoxLayout;
@ -514,8 +513,10 @@ void GraphicsVertexShaderWidget::Reload(bool replace_vertex_data, void* vertex_d
info.code.push_back({instr});
int num_attributes = shader_config.max_input_attribute_index + 1;
for (auto pattern : shader_setup.swizzle_data)
info.swizzle_info.push_back({pattern});
for (auto pattern : shader_setup.swizzle_data) {
const nihstro::SwizzleInfo swizzle_info = { .pattern = nihstro::SwizzlePattern{pattern} };
info.swizzle_info.push_back(swizzle_info);
}
u32 entry_point = Pica::g_state.regs.vs.main_offset;
info.labels.insert({entry_point, "main"});

View File

@ -56,9 +56,7 @@
#include "common/detached_tasks.h"
#include "common/file_util.h"
#include "common/logging/backend.h"
#include "common/logging/filter.h"
#include "common/logging/log.h"
#include "common/logging/text_formatter.h"
#include "common/microprofile.h"
#include "common/scm_rev.h"
#include "common/scope_exit.h"
@ -70,17 +68,14 @@
#include "core/file_sys/archive_extsavedata.h"
#include "core/file_sys/archive_source_sd_savedata.h"
#include "core/frontend/applets/default_applets.h"
#include "core/gdbstub/gdbstub.h"
#include "core/hle/service/fs/archive.h"
#include "core/hle/service/nfc/nfc.h"
#include "core/loader/loader.h"
#include "core/movie.h"
#include "core/savestate.h"
#include "core/settings.h"
#include "game_list_p.h"
#include "network/network_settings.h"
#include "ui_main.h"
#include "video_core/renderer_base.h"
#include "video_core/video_core.h"
#ifdef __APPLE__
@ -151,9 +146,7 @@ static void InitializeLogging() {
#endif
}
GMainWindow::GMainWindow()
: config(std::make_unique<Config>()), emu_thread(nullptr),
ui(std::make_unique<Ui::MainWindow>()) {
GMainWindow::GMainWindow() : ui{std::make_unique<Ui::MainWindow>()}, config{std::make_unique<Config>()}, emu_thread{nullptr} {
InitializeLogging();
Debugger::ToggleConsole();
Settings::LogSettings();
@ -263,7 +256,7 @@ void GMainWindow::InitializeWidgets() {
loading_screen = new LoadingScreen(this);
loading_screen->hide();
ui->horizontalLayout->addWidget(loading_screen);
connect(loading_screen, &LoadingScreen::Hidden, [&] {
connect(loading_screen, &LoadingScreen::Hidden, this, [&] {
loading_screen->Clear();
if (emulation_running) {
render_window->show();
@ -432,13 +425,13 @@ void GMainWindow::InitializeSaveStateMenuActions() {
ui->menu_Save_State->addAction(actions_save_state[i]);
}
connect(ui->action_Load_from_Newest_Slot, &QAction::triggered, [this] {
connect(ui->action_Load_from_Newest_Slot, &QAction::triggered, this, [this] {
UpdateSaveStates();
if (newest_slot != 0) {
actions_load_state[newest_slot - 1]->trigger();
}
});
connect(ui->action_Save_to_Oldest_Slot, &QAction::triggered, [this] {
connect(ui->action_Save_to_Oldest_Slot, &QAction::triggered, this, [this] {
UpdateSaveStates();
actions_save_state[oldest_slot - 1]->trigger();
});
@ -674,7 +667,7 @@ void GMainWindow::ConnectWidgetEvents() {
connect(game_list_placeholder, &GameListPlaceholder::AddDirectory, this,
&GMainWindow::OnGameListAddDirectory);
connect(game_list, &GameList::ShowList, this, &GMainWindow::OnGameListShowList);
connect(game_list, &GameList::PopulatingCompleted,
connect(game_list, &GameList::PopulatingCompleted, this,
[this] { multiplayer_state->UpdateGameList(game_list->GetModel()); });
connect(this, &GMainWindow::EmulationStarting, render_window,
@ -765,7 +758,7 @@ void GMainWindow::ConnectMenuEvents() {
connect(ui->action_Close_Movie, &QAction::triggered, this, &GMainWindow::OnCloseMovie);
connect(ui->action_Save_Movie, &QAction::triggered, this, &GMainWindow::OnSaveMovie);
connect(ui->action_Movie_Read_Only_Mode, &QAction::toggled, this,
[this](bool checked) { Core::Movie::GetInstance().SetReadOnly(checked); });
[](bool checked) { Core::Movie::GetInstance().SetReadOnly(checked); });
connect(ui->action_Enable_Frame_Advancing, &QAction::triggered, this, [this] {
if (emulation_running) {
Core::System::GetInstance().frame_limiter.SetFrameAdvancing(
@ -1396,7 +1389,7 @@ void GMainWindow::OnGameListDumpRomFS(QString game_path, u64 program_id) {
program_id | 0x0004000e00000000);
using FutureWatcher = QFutureWatcher<std::pair<Loader::ResultStatus, Loader::ResultStatus>>;
auto* future_watcher = new FutureWatcher(this);
connect(future_watcher, &FutureWatcher::finished,
connect(future_watcher, &FutureWatcher::finished, this,
[this, dialog, base_path, update_path, future_watcher] {
dialog->hide();
const auto& [base, update] = future_watcher->result();
@ -1498,7 +1491,7 @@ void GMainWindow::InstallCIA(QStringList filepaths) {
const auto cia_progress = [&](std::size_t written, std::size_t total) {
emit UpdateProgress(written, total);
};
for (const auto current_path : filepaths) {
for (const auto& current_path : filepaths) {
status = Service::AM::InstallCIA(current_path.toStdString(), cia_progress);
emit CIAInstallReport(status, current_path);
}
@ -1536,6 +1529,10 @@ void GMainWindow::OnCIAInstallReport(Service::AM::InstallStatus status, QString
"before being used with Citra. A real 3DS is required.")
.arg(filename));
break;
case Service::AM::InstallStatus::ErrorFileNotFound:
QMessageBox::critical(this, tr("Unable to find File"),
tr("Could not find %1").arg(filename));
break;
}
}
@ -1713,6 +1710,8 @@ void GMainWindow::ToggleScreenLayout() {
case Settings::LayoutOption::SideScreen:
new_layout = Settings::LayoutOption::Default;
break;
default:
LOG_ERROR(Frontend, "Unknown layout option {}", Settings::values.layout_option);
}
Settings::values.layout_option = new_layout;
@ -1959,7 +1958,7 @@ void GMainWindow::OnCaptureScreenshot() {
const QString filename = game_title.remove(QRegularExpression(QStringLiteral("[\\/:?\"<>|]")));
const QString timestamp =
QDateTime::currentDateTime().toString(QStringLiteral("dd.MM.yy_hh.mm.ss.z"));
path.append(QStringLiteral("/%1_%2.png").arg(filename).arg(timestamp));
path.append(QStringLiteral("/%1_%2.png").arg(filename, timestamp));
render_window->CaptureScreenshot(UISettings::values.screenshot_resolution_factor, path);
OnStartGame();
}

View File

@ -21,7 +21,6 @@ namespace Settings {
Values values = {};
void Apply() {
GDBStub::SetServerPort(values.gdbstub_port);
GDBStub::ToggleServer(values.use_gdbstub);
@ -108,16 +107,15 @@ void LogSettings() {
LogSetting("Audio_OutputDevice", values.audio_device_id);
LogSetting("Audio_InputDeviceType", values.mic_input_type);
LogSetting("Audio_InputDevice", values.mic_input_device);
using namespace Service::CAM;
LogSetting("Camera_OuterRightName", values.camera_name[OuterRightCamera]);
LogSetting("Camera_OuterRightConfig", values.camera_config[OuterRightCamera]);
LogSetting("Camera_OuterRightFlip", values.camera_flip[OuterRightCamera]);
LogSetting("Camera_InnerName", values.camera_name[InnerCamera]);
LogSetting("Camera_InnerConfig", values.camera_config[InnerCamera]);
LogSetting("Camera_InnerFlip", values.camera_flip[InnerCamera]);
LogSetting("Camera_OuterLeftName", values.camera_name[OuterLeftCamera]);
LogSetting("Camera_OuterLeftConfig", values.camera_config[OuterLeftCamera]);
LogSetting("Camera_OuterLeftFlip", values.camera_flip[OuterLeftCamera]);
LogSetting("Camera_OuterRightName", values.camera_name[Service::CAM::OuterRightCamera]);
LogSetting("Camera_OuterRightConfig", values.camera_config[Service::CAM::OuterRightCamera]);
LogSetting("Camera_OuterRightFlip", values.camera_flip[Service::CAM::OuterRightCamera]);
LogSetting("Camera_InnerName", values.camera_name[Service::CAM::InnerCamera]);
LogSetting("Camera_InnerConfig", values.camera_config[Service::CAM::InnerCamera]);
LogSetting("Camera_InnerFlip", values.camera_flip[Service::CAM::InnerCamera]);
LogSetting("Camera_OuterLeftName", values.camera_name[Service::CAM::OuterLeftCamera]);
LogSetting("Camera_OuterLeftConfig", values.camera_config[Service::CAM::OuterLeftCamera]);
LogSetting("Camera_OuterLeftFlip", values.camera_flip[Service::CAM::OuterLeftCamera]);
LogSetting("DataStorage_UseVirtualSd", values.use_virtual_sd);
LogSetting("DataStorage_SdmcDir", FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir));
LogSetting("DataStorage_NandDir", FileUtil::GetUserPath(FileUtil::UserPath::NANDDir));

View File

@ -168,13 +168,15 @@ struct Values {
u64 init_time;
// Renderer
GraphicsAPI graphics_api = GraphicsAPI::Vulkan;
GraphicsAPI graphics_api;
bool renderer_debug;
bool use_hw_renderer;
bool use_hw_shader;
bool separable_shader;
bool use_disk_shader_cache;
bool shaders_accurate_mul;
bool use_shader_jit;
bool use_vsync_new;
u16 resolution_factor;
bool use_frame_limit_alternate;
u16 frame_limit;
@ -208,12 +210,11 @@ struct Values {
bool filter_mode;
std::string pp_shader_name;
// Custom textures
bool dump_textures;
bool custom_textures;
bool preload_textures;
bool use_vsync_new;
// Audio
bool enable_dsp_lle;
bool enable_dsp_lle_multithread;

View File

@ -3,12 +3,9 @@
// Refer to the license.txt file included.
#include <cryptopp/osrng.h>
#include "common/assert.h"
#include "common/common_types.h"
#include "common/file_util.h"
#include "common/logging/log.h"
#include "common/scm_rev.h"
#include "core/core.h"
#include "core/settings.h"
#include "core/telemetry_session.h"

View File

@ -28,7 +28,7 @@ public:
* @note All methods in this class are called from the GSP thread
*/
virtual void GXCommandProcessed(int total_command_count) {
const Service::GSP::Command& cmd =
[[maybe_unused]] const Service::GSP::Command& cmd =
observed->ReadGXCommandHistory(total_command_count - 1);
LOG_TRACE(Debug_GPU, "Received command: id={:x}", (int)cmd.id.Value());
}

View File

@ -129,6 +129,8 @@ struct LightingRegs {
"ReflectBlue, instead got %i",
config);
}
return false;
}
struct LightSrc {

View File

@ -69,7 +69,7 @@ static void APIENTRY DebugHandler(GLenum source, GLenum type, GLuint id, GLenum
id, message);
}
Driver::Driver(bool gles) : is_gles{gles} {
Driver::Driver(bool gles, bool enable_debug) : is_gles{gles} {
#ifndef ANDROID
if (!gladLoadGL()) {
return;
@ -79,8 +79,10 @@ Driver::Driver(bool gles) : is_gles{gles} {
* Qualcomm has some spammy info messages that are marked as errors but not important
* https://developer.qualcomm.com/comment/11845
*/
if (enable_debug) {
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
glDebugMessageCallback(DebugHandler, nullptr);
}
#endif
ReportDriverInfo();

View File

@ -33,31 +33,37 @@ enum class DriverBug {
*/
class Driver {
public:
Driver(bool gles);
Driver(bool gles, bool enable_debug);
/// Returns true of the driver has a particular bug stated in the DriverBug enum
bool HasBug(DriverBug bug) const;
/// Returns the vendor of the currently selected physical device
Vendor GetVendor() const {
return vendor;
}
/// Returns true if the current context is ES compatible
bool IsOpenGLES() const {
return is_gles;
}
/// Returns true if the implementation supports ARB_buffer_storage
bool HasArbBufferStorage() const {
return arb_buffer_storage;
}
/// Returns true if the implementation supports EXT_buffer_storage
bool HasExtBufferStorage() const {
return ext_buffer_storage;
}
/// Returns true if the implementation supports EXT_clip_cull_distance
bool HasExtClipCullDistance() const {
return ext_clip_cull_distance;
}
/// Returns true if the implementation supports ARB_direct_state_access
bool HasArbDirectStateAccess() const {
return arb_direct_state_access;
}

View File

@ -353,7 +353,8 @@ static std::array<GLfloat, 3 * 2> MakeOrthographicMatrix(const float width, cons
}
RendererOpenGL::RendererOpenGL(Frontend::EmuWindow& window)
: RendererBase{window}, driver{Settings::values.graphics_api == Settings::GraphicsAPI::OpenGLES},
: RendererBase{window}, driver{Settings::values.graphics_api == Settings::GraphicsAPI::OpenGLES,
Settings::values.renderer_debug},
frame_dumper{Core::System::GetInstance().VideoDumper(), window} {
window.mailbox = std::make_unique<OGLTextureMailbox>();

View File

@ -183,8 +183,9 @@ static std::array<float, 3 * 2> MakeOrthographicMatrix(float width, float height
}
RendererVulkan::RendererVulkan(Frontend::EmuWindow& window)
: RendererBase{window}, instance{window}, scheduler{instance, *this}, renderpass_cache{instance, scheduler},
runtime{instance, scheduler, renderpass_cache}, swapchain{instance, renderpass_cache},
: RendererBase{window}, instance{window, Settings::values.renderer_debug}, scheduler{instance, *this},
renderpass_cache{instance, scheduler}, runtime{instance, scheduler, renderpass_cache},
swapchain{instance, renderpass_cache},
vertex_buffer{instance, scheduler, VERTEX_BUFFER_SIZE, vk::BufferUsageFlagBits::eVertexBuffer, {}} {
auto& telemetry_session = Core::System::GetInstance().TelemetrySession();

View File

@ -38,7 +38,7 @@ vk::Format ToVkFormat(VideoCore::PixelFormat format) {
}
}
Instance::Instance(Frontend::EmuWindow& window) {
Instance::Instance(Frontend::EmuWindow& window, bool enable_validation) {
auto window_info = window.GetWindowInfo();
// Fetch instance independant function pointers
@ -64,9 +64,10 @@ Instance::Instance(Frontend::EmuWindow& window) {
};
const std::array layers = {"VK_LAYER_KHRONOS_validation"};
const u32 layer_count = enable_validation ? 1u : 0u;
const vk::InstanceCreateInfo instance_info = {
.pApplicationInfo = &application_info,
.enabledLayerCount = static_cast<u32>(layers.size()),
.enabledLayerCount = layer_count,
.ppEnabledLayerNames = layers.data(),
.enabledExtensionCount = static_cast<u32>(extensions.size()),
.ppEnabledExtensionNames = extensions.data()

View File

@ -27,7 +27,7 @@ struct FormatTraits {
/// The global Vulkan instance
class Instance {
public:
Instance(Frontend::EmuWindow& window);
Instance(Frontend::EmuWindow& window, bool enable_validation);
~Instance();
/// Returns the FormatTraits struct for the provided pixel format