diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index 00e6667c23..2a722f37d8 100644 --- a/src/android/app/src/main/jni/native.cpp +++ b/src/android/app/src/main/jni/native.cpp @@ -26,6 +26,7 @@ #include "core/hle/service/nfc/nfc.h" #include "core/loader/loader.h" #include "core/savestate.h" +#include "core/telemetry_session.h" #include "jni/android_common/android_common.h" #include "jni/applets/mii_selector.h" #include "jni/applets/swkbd.h" @@ -156,7 +157,7 @@ static Core::System::ResultStatus RunCitra(const std::string& filepath) { system.RegisterSoftwareKeyboard(std::make_shared()); // Register microphone permission check - Core::System::GetInstance().RegisterMicPermissionCheck(&CheckMicPermission); + system.RegisterMicPermissionCheck(&CheckMicPermission); InputManager::Init(); @@ -166,7 +167,7 @@ static Core::System::ResultStatus RunCitra(const std::string& filepath) { return load_result; } - auto& telemetry_session = Core::System::GetInstance().TelemetrySession(); + auto& telemetry_session = system.TelemetrySession(); telemetry_session.AddField(Common::Telemetry::FieldType::App, "Frontend", "Android"); stop_run = false; @@ -187,8 +188,7 @@ static Core::System::ResultStatus RunCitra(const std::string& filepath) { audio_stretching_event = system.CoreTiming().RegisterEvent("AudioStretchingEvent", [&](u64, s64 cycles_late) { if (Settings::values.enable_audio_stretching) { - Core::DSP().EnableStretching( - Core::System::GetInstance().GetAndResetPerfStats().emulation_speed < 0.95); + system.DSP().EnableStretching(system.GetAndResetPerfStats().emulation_speed < 0.95); } system.CoreTiming().ScheduleEvent(audio_stretching_ticks - cycles_late, @@ -219,7 +219,7 @@ static Core::System::ResultStatus RunCitra(const std::string& filepath) { SCOPE_EXIT({ Settings::values.volume = volume; }); Settings::values.volume = 0; - std::unique_lock pause_lock(paused_mutex); + std::unique_lock pause_lock{paused_mutex}; running_cv.wait(pause_lock, [] { return !pause_emulation || stop_run; }); window->PollEvents(); } @@ -621,7 +621,7 @@ jobjectArray Java_org_citra_citra_1emu_NativeLibrary_GetSavestateInfo( return nullptr; } - const auto savestates = Core::ListSaveStates(title_id); + const auto savestates = Core::ListSaveStates(title_id, system.Movie().GetCurrentMovieID()); const jobjectArray array = env->NewObjectArray(static_cast(savestates.size()), savestate_info_class, nullptr); for (std::size_t i = 0; i < savestates.size(); ++i) { diff --git a/src/audio_core/hle/hle.cpp b/src/audio_core/hle/hle.cpp index 38632d252a..f24176ca80 100644 --- a/src/audio_core/hle/hle.cpp +++ b/src/audio_core/hle/hle.cpp @@ -8,6 +8,7 @@ #include #include #include "audio_core/audio_types.h" +#include "common/archives.h" #ifdef HAVE_MF #include "audio_core/hle/wmf_decoder.h" #elif HAVE_AUDIOTOOLBOX diff --git a/src/citra/citra.cpp b/src/citra/citra.cpp index c23324212e..588f46403c 100644 --- a/src/citra/citra.cpp +++ b/src/citra/citra.cpp @@ -19,7 +19,6 @@ #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" @@ -28,18 +27,15 @@ #include "core/core.h" #include "core/dumping/backend.h" #include "core/dumping/ffmpeg_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/telemetry_session.h" #include "input_common/main.h" #include "network/network.h" #include "video_core/renderer_base.h" -#include "video_core/video_core.h" #undef _UNICODE #include @@ -331,7 +327,7 @@ int main(int argc, char** argv) { } auto& system = Core::System::GetInstance(); - auto& movie = Core::Movie::GetInstance(); + auto& movie = system.Movie(); if (!movie_record.empty()) { movie.PrepareForRecording(); diff --git a/src/citra_qt/compatdb.cpp b/src/citra_qt/compatdb.cpp index 92069dccaa..ca8f5870a1 100644 --- a/src/citra_qt/compatdb.cpp +++ b/src/citra_qt/compatdb.cpp @@ -9,6 +9,7 @@ #include "citra_qt/compatdb.h" #include "common/telemetry.h" #include "core/core.h" +#include "core/telemetry_session.h" #include "ui_compatdb.h" CompatDB::CompatDB(Core::TelemetrySession& telemetry_session_, QWidget* parent) diff --git a/src/citra_qt/configuration/configure.ui b/src/citra_qt/configuration/configure.ui index 1774ccbc16..f6f5a517a8 100644 --- a/src/citra_qt/configuration/configure.ui +++ b/src/citra_qt/configuration/configure.ui @@ -21,68 +21,8 @@ - 0 + -1 - - - General - - - - - System - - - - - Input - - - - - Hotkeys - - - - - Graphics - - - - - Enhancements - - - - - Audio - - - - - Camera - - - - - Debug - - - - - Storage - - - - - Web - - - - - UI - - diff --git a/src/citra_qt/configuration/configure_audio.cpp b/src/citra_qt/configuration/configure_audio.cpp index c32bebc19d..f921c3a628 100644 --- a/src/citra_qt/configuration/configure_audio.cpp +++ b/src/citra_qt/configuration/configure_audio.cpp @@ -10,14 +10,13 @@ #include "citra_qt/configuration/configuration_shared.h" #include "citra_qt/configuration/configure_audio.h" #include "common/settings.h" -#include "core/core.h" #include "ui_configure_audio.h" #if defined(__APPLE__) #include "common/apple_authorization.h" #endif -ConfigureAudio::ConfigureAudio(QWidget* parent) +ConfigureAudio::ConfigureAudio(bool is_powered_on, QWidget* parent) : QWidget(parent), ui(std::make_unique()) { ui->setupUi(this); @@ -27,8 +26,7 @@ ConfigureAudio::ConfigureAudio(QWidget* parent) AudioCore::GetSinkName(static_cast(type)).data())); } - const bool is_running = Core::System::GetInstance().IsPoweredOn(); - ui->emulation_combo_box->setEnabled(!is_running); + ui->emulation_combo_box->setEnabled(!is_powered_on); connect(ui->volume_slider, &QSlider::valueChanged, this, &ConfigureAudio::SetVolumeIndicatorText); diff --git a/src/citra_qt/configuration/configure_audio.h b/src/citra_qt/configuration/configure_audio.h index a25153c508..001d574edb 100644 --- a/src/citra_qt/configuration/configure_audio.h +++ b/src/citra_qt/configuration/configure_audio.h @@ -19,7 +19,7 @@ class ConfigureAudio : public QWidget { Q_OBJECT public: - explicit ConfigureAudio(QWidget* parent = nullptr); + explicit ConfigureAudio(bool is_powered_on, QWidget* parent = nullptr); ~ConfigureAudio() override; void ApplyConfiguration(); diff --git a/src/citra_qt/configuration/configure_camera.cpp b/src/citra_qt/configuration/configure_camera.cpp index de270726ec..08368fe5cb 100644 --- a/src/citra_qt/configuration/configure_camera.cpp +++ b/src/citra_qt/configuration/configure_camera.cpp @@ -47,8 +47,7 @@ ConfigureCamera::~ConfigureCamera() { } void ConfigureCamera::ConnectEvents() { - connect(ui->image_source, - static_cast(&QComboBox::currentIndexChanged), this, + connect(ui->image_source, qOverload(&QComboBox::currentIndexChanged), this, [this](int index) { StopPreviewing(); UpdateImageSourceUI(); @@ -58,36 +57,33 @@ void ConfigureCamera::ConnectEvents() { } #endif }); - connect(ui->camera_selection, - static_cast(&QComboBox::currentIndexChanged), this, [this] { - StopPreviewing(); - if (GetCameraSelection() != current_selected) { - RecordConfig(); - } - if (ui->camera_selection->currentIndex() == 1) { - ui->camera_mode->setCurrentIndex(1); // Double - if (camera_name[0] == camera_name[2] && camera_config[0] == camera_config[2]) { - ui->camera_mode->setCurrentIndex(0); // Single - } - } - UpdateCameraMode(); - SetConfiguration(); - }); - connect(ui->camera_mode, static_cast(&QComboBox::currentIndexChanged), - this, [this] { - StopPreviewing(); - ui->camera_position_label->setVisible(ui->camera_mode->currentIndex() == 1); - ui->camera_position->setVisible(ui->camera_mode->currentIndex() == 1); - current_selected = GetCameraSelection(); - }); - connect(ui->camera_position, - static_cast(&QComboBox::currentIndexChanged), this, [this] { - StopPreviewing(); - if (GetCameraSelection() != current_selected) { - RecordConfig(); - } - SetConfiguration(); - }); + connect(ui->camera_selection, qOverload(&QComboBox::currentIndexChanged), this, [this] { + StopPreviewing(); + if (GetCameraSelection() != current_selected) { + RecordConfig(); + } + if (ui->camera_selection->currentIndex() == 1) { + ui->camera_mode->setCurrentIndex(1); // Double + if (camera_name[0] == camera_name[2] && camera_config[0] == camera_config[2]) { + ui->camera_mode->setCurrentIndex(0); // Single + } + } + UpdateCameraMode(); + SetConfiguration(); + }); + connect(ui->camera_mode, qOverload(&QComboBox::currentIndexChanged), this, [this] { + StopPreviewing(); + ui->camera_position_label->setVisible(ui->camera_mode->currentIndex() == 1); + ui->camera_position->setVisible(ui->camera_mode->currentIndex() == 1); + current_selected = GetCameraSelection(); + }); + connect(ui->camera_position, qOverload(&QComboBox::currentIndexChanged), this, [this] { + StopPreviewing(); + if (GetCameraSelection() != current_selected) { + RecordConfig(); + } + SetConfiguration(); + }); connect(ui->toolButton, &QToolButton::clicked, this, &ConfigureCamera::OnToolButtonClicked); connect(ui->preview_button, &QPushButton::clicked, this, [this] { StartPreviewing(); }); connect(ui->prompt_before_load, &QCheckBox::stateChanged, this, [this](int state) { diff --git a/src/citra_qt/configuration/configure_cheats.cpp b/src/citra_qt/configuration/configure_cheats.cpp index 4569f7ed84..c589a62e60 100644 --- a/src/citra_qt/configuration/configure_cheats.cpp +++ b/src/citra_qt/configuration/configure_cheats.cpp @@ -9,11 +9,9 @@ #include "core/cheats/cheat_base.h" #include "core/cheats/cheats.h" #include "core/cheats/gateway_cheat.h" -#include "core/core.h" -#include "core/hle/kernel/process.h" #include "ui_configure_cheats.h" -ConfigureCheats::ConfigureCheats(u64 title_id_, QWidget* parent) +ConfigureCheats::ConfigureCheats(Core::System& system, u64 title_id_, QWidget* parent) : QWidget(parent), ui(std::make_unique()), title_id{title_id_} { // Setup gui control settings ui->setupUi(this); @@ -36,7 +34,7 @@ ConfigureCheats::ConfigureCheats(u64 title_id_, QWidget* parent) [this] { SaveCheat(ui->tableCheats->currentRow()); }); connect(ui->buttonDelete, &QPushButton::clicked, this, &ConfigureCheats::OnDeleteCheat); - cheat_engine = std::make_unique(title_id, Core::System::GetInstance()); + cheat_engine = std::make_unique(title_id, system); LoadCheats(); } diff --git a/src/citra_qt/configuration/configure_cheats.h b/src/citra_qt/configuration/configure_cheats.h index 5242c82d81..28dcb72e58 100644 --- a/src/citra_qt/configuration/configure_cheats.h +++ b/src/citra_qt/configuration/configure_cheats.h @@ -5,6 +5,7 @@ #pragma once #include +#include #include "common/common_types.h" namespace Cheats { @@ -12,6 +13,10 @@ class CheatBase; class CheatEngine; } // namespace Cheats +namespace Core { +class System; +} + namespace Ui { class ConfigureCheats; } // namespace Ui @@ -20,7 +25,7 @@ class ConfigureCheats : public QWidget { Q_OBJECT public: - explicit ConfigureCheats(u64 title_id_, QWidget* parent = nullptr); + explicit ConfigureCheats(Core::System& system, u64 title_id, QWidget* parent = nullptr); ~ConfigureCheats(); bool ApplyConfiguration(); diff --git a/src/citra_qt/configuration/configure_debug.cpp b/src/citra_qt/configuration/configure_debug.cpp index d0b2441339..3e166834ba 100644 --- a/src/citra_qt/configuration/configure_debug.cpp +++ b/src/citra_qt/configuration/configure_debug.cpp @@ -11,7 +11,6 @@ #include "common/file_util.h" #include "common/logging/backend.h" #include "common/settings.h" -#include "core/core.h" #include "ui_configure_debug.h" // The QSlider doesn't have an easy way to set a custom step amount, @@ -25,8 +24,8 @@ static constexpr int SettingsToSlider(int value) { return (value - 5) / 5; } -ConfigureDebug::ConfigureDebug(QWidget* parent) - : QWidget(parent), ui(std::make_unique()) { +ConfigureDebug::ConfigureDebug(bool is_powered_on_, QWidget* parent) + : QWidget(parent), ui(std::make_unique()), is_powered_on{is_powered_on_} { ui->setupUi(this); SetConfiguration(); @@ -35,7 +34,6 @@ ConfigureDebug::ConfigureDebug(QWidget* parent) QDesktopServices::openUrl(QUrl::fromLocalFile(path)); }); - 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); @@ -59,7 +57,7 @@ void ConfigureDebug::SetConfiguration() { ui->toggle_gdbstub->setChecked(Settings::values.use_gdbstub.GetValue()); ui->gdbport_spinbox->setEnabled(Settings::values.use_gdbstub.GetValue()); ui->gdbport_spinbox->setValue(Settings::values.gdbstub_port.GetValue()); - ui->toggle_console->setEnabled(!Core::System::GetInstance().IsPoweredOn()); + ui->toggle_console->setEnabled(!is_powered_on); ui->toggle_console->setChecked(UISettings::values.show_console.GetValue()); ui->log_filter_edit->setText(QString::fromStdString(Settings::values.log_filter.GetValue())); ui->toggle_cpu_jit->setChecked(Settings::values.use_cpu_jit.GetValue()); diff --git a/src/citra_qt/configuration/configure_debug.h b/src/citra_qt/configuration/configure_debug.h index 02101016f3..f511e260e1 100644 --- a/src/citra_qt/configuration/configure_debug.h +++ b/src/citra_qt/configuration/configure_debug.h @@ -15,7 +15,7 @@ class ConfigureDebug : public QWidget { Q_OBJECT public: - explicit ConfigureDebug(QWidget* parent = nullptr); + explicit ConfigureDebug(bool is_powered_on, QWidget* parent = nullptr); ~ConfigureDebug() override; void ApplyConfiguration(); @@ -25,4 +25,5 @@ public: private: std::unique_ptr ui; + bool is_powered_on; }; diff --git a/src/citra_qt/configuration/configure_dialog.cpp b/src/citra_qt/configuration/configure_dialog.cpp index 254a636411..ee46ff5fbe 100644 --- a/src/citra_qt/configuration/configure_dialog.cpp +++ b/src/citra_qt/configuration/configure_dialog.cpp @@ -4,8 +4,19 @@ #include #include -#include "citra_qt/configuration/config.h" +#include "citra_qt/configuration/configure_audio.h" +#include "citra_qt/configuration/configure_camera.h" +#include "citra_qt/configuration/configure_debug.h" #include "citra_qt/configuration/configure_dialog.h" +#include "citra_qt/configuration/configure_enhancements.h" +#include "citra_qt/configuration/configure_general.h" +#include "citra_qt/configuration/configure_graphics.h" +#include "citra_qt/configuration/configure_hotkeys.h" +#include "citra_qt/configuration/configure_input.h" +#include "citra_qt/configuration/configure_storage.h" +#include "citra_qt/configuration/configure_system.h" +#include "citra_qt/configuration/configure_ui.h" +#include "citra_qt/configuration/configure_web.h" #include "citra_qt/hotkeys.h" #include "common/settings.h" #include "core/core.h" @@ -14,16 +25,41 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry_, Core::System& system_, bool enable_web_config) : QDialog(parent), ui{std::make_unique()}, registry{registry_}, - system{system_} { + system{system_}, is_powered_on{system.IsPoweredOn()}, + general_tab{std::make_unique(this)}, + system_tab{std::make_unique(system, this)}, + input_tab{std::make_unique(this)}, + hotkeys_tab{std::make_unique(this)}, + graphics_tab{std::make_unique(is_powered_on, this)}, + enhancements_tab{std::make_unique(this)}, + audio_tab{std::make_unique(is_powered_on, this)}, + camera_tab{std::make_unique(this)}, + debug_tab{std::make_unique(is_powered_on, this)}, + storage_tab{std::make_unique(is_powered_on, this)}, + web_tab{std::make_unique(this)}, ui_tab{std::make_unique(this)} { Settings::SetConfiguringGlobal(true); ui->setupUi(this); - ui->hotkeysTab->Populate(registry); - ui->webTab->SetWebServiceConfigEnabled(enable_web_config); + + ui->tabWidget->addTab(general_tab.get(), tr("General")); + ui->tabWidget->addTab(system_tab.get(), tr("System")); + ui->tabWidget->addTab(input_tab.get(), tr("Input")); + ui->tabWidget->addTab(hotkeys_tab.get(), tr("Hotkeys")); + ui->tabWidget->addTab(graphics_tab.get(), tr("Graphics")); + ui->tabWidget->addTab(enhancements_tab.get(), tr("Enhancements")); + ui->tabWidget->addTab(audio_tab.get(), tr("Audio")); + ui->tabWidget->addTab(camera_tab.get(), tr("Camera")); + ui->tabWidget->addTab(debug_tab.get(), tr("Debug")); + ui->tabWidget->addTab(storage_tab.get(), tr("Storage")); + ui->tabWidget->addTab(web_tab.get(), tr("Web")); + ui->tabWidget->addTab(ui_tab.get(), tr("UI")); + + hotkeys_tab->Populate(registry); + web_tab->SetWebServiceConfigEnabled(enable_web_config); PopulateSelectionList(); - connect(ui->uiTab, &ConfigureUi::LanguageChanged, this, &ConfigureDialog::OnLanguageChanged); + connect(ui_tab.get(), &ConfigureUi::LanguageChanged, this, &ConfigureDialog::OnLanguageChanged); connect(ui->selectorList, &QListWidget::itemSelectionChanged, this, &ConfigureDialog::UpdateVisibleTabs); @@ -31,46 +67,46 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry_, Cor ui->selectorList->setCurrentRow(0); // Set up used key list synchronisation - connect(ui->inputTab, &ConfigureInput::InputKeysChanged, ui->hotkeysTab, + connect(input_tab.get(), &ConfigureInput::InputKeysChanged, hotkeys_tab.get(), &ConfigureHotkeys::OnInputKeysChanged); - connect(ui->hotkeysTab, &ConfigureHotkeys::HotkeysChanged, ui->inputTab, + connect(hotkeys_tab.get(), &ConfigureHotkeys::HotkeysChanged, input_tab.get(), &ConfigureInput::OnHotkeysChanged); // Synchronise lists upon initialisation - ui->inputTab->EmitInputKeysChanged(); - ui->hotkeysTab->EmitHotkeysChanged(); + input_tab->EmitInputKeysChanged(); + hotkeys_tab->EmitHotkeysChanged(); } ConfigureDialog::~ConfigureDialog() = default; void ConfigureDialog::SetConfiguration() { - ui->generalTab->SetConfiguration(); - ui->systemTab->SetConfiguration(); - ui->inputTab->LoadConfiguration(); - ui->graphicsTab->SetConfiguration(); - ui->enhancementsTab->SetConfiguration(); - ui->audioTab->SetConfiguration(); - ui->cameraTab->SetConfiguration(); - ui->debugTab->SetConfiguration(); - ui->webTab->SetConfiguration(); - ui->uiTab->SetConfiguration(); - ui->storageTab->SetConfiguration(); + general_tab->SetConfiguration(); + system_tab->SetConfiguration(); + input_tab->LoadConfiguration(); + graphics_tab->SetConfiguration(); + enhancements_tab->SetConfiguration(); + audio_tab->SetConfiguration(); + camera_tab->SetConfiguration(); + debug_tab->SetConfiguration(); + web_tab->SetConfiguration(); + ui_tab->SetConfiguration(); + storage_tab->SetConfiguration(); } void ConfigureDialog::ApplyConfiguration() { - ui->generalTab->ApplyConfiguration(); - ui->systemTab->ApplyConfiguration(); - ui->inputTab->ApplyConfiguration(); - ui->inputTab->ApplyProfile(); - ui->hotkeysTab->ApplyConfiguration(registry); - ui->graphicsTab->ApplyConfiguration(); - ui->enhancementsTab->ApplyConfiguration(); - ui->audioTab->ApplyConfiguration(); - ui->cameraTab->ApplyConfiguration(); - ui->debugTab->ApplyConfiguration(); - ui->webTab->ApplyConfiguration(); - ui->uiTab->ApplyConfiguration(); - ui->storageTab->ApplyConfiguration(); + general_tab->ApplyConfiguration(); + system_tab->ApplyConfiguration(); + input_tab->ApplyConfiguration(); + input_tab->ApplyProfile(); + hotkeys_tab->ApplyConfiguration(registry); + graphics_tab->ApplyConfiguration(); + enhancements_tab->ApplyConfiguration(); + audio_tab->ApplyConfiguration(); + camera_tab->ApplyConfiguration(); + debug_tab->ApplyConfiguration(); + web_tab->ApplyConfiguration(); + ui_tab->ApplyConfiguration(); + storage_tab->ApplyConfiguration(); system.ApplySettings(); Settings::LogSettings(); } @@ -81,11 +117,11 @@ void ConfigureDialog::PopulateSelectionList() { ui->selectorList->clear(); const std::array>, 5> items{ - {{tr("General"), {ui->generalTab, ui->webTab, ui->debugTab, ui->uiTab}}, - {tr("System"), {ui->systemTab, ui->cameraTab, ui->storageTab}}, - {tr("Graphics"), {ui->enhancementsTab, ui->graphicsTab}}, - {tr("Audio"), {ui->audioTab}}, - {tr("Controls"), {ui->inputTab, ui->hotkeysTab}}}}; + {{tr("General"), {general_tab.get(), web_tab.get(), debug_tab.get(), ui_tab.get()}}, + {tr("System"), {system_tab.get(), camera_tab.get(), storage_tab.get()}}, + {tr("Graphics"), {enhancements_tab.get(), graphics_tab.get()}}, + {tr("Audio"), {audio_tab.get()}}, + {tr("Controls"), {input_tab.get(), hotkeys_tab.get()}}}}; for (const auto& entry : items) { auto* const item = new QListWidgetItem(entry.first); @@ -112,18 +148,18 @@ void ConfigureDialog::RetranslateUI() { ui->selectorList->setCurrentRow(old_row); ui->tabWidget->setCurrentIndex(old_index); - ui->generalTab->RetranslateUI(); - ui->systemTab->RetranslateUI(); - ui->inputTab->RetranslateUI(); - ui->hotkeysTab->RetranslateUI(); - ui->graphicsTab->RetranslateUI(); - ui->enhancementsTab->RetranslateUI(); - ui->audioTab->RetranslateUI(); - ui->cameraTab->RetranslateUI(); - ui->debugTab->RetranslateUI(); - ui->webTab->RetranslateUI(); - ui->uiTab->RetranslateUI(); - ui->storageTab->RetranslateUI(); + general_tab->RetranslateUI(); + system_tab->RetranslateUI(); + input_tab->RetranslateUI(); + hotkeys_tab->RetranslateUI(); + graphics_tab->RetranslateUI(); + enhancements_tab->RetranslateUI(); + audio_tab->RetranslateUI(); + camera_tab->RetranslateUI(); + debug_tab->RetranslateUI(); + web_tab->RetranslateUI(); + ui_tab->RetranslateUI(); + storage_tab->RetranslateUI(); } void ConfigureDialog::UpdateVisibleTabs() { @@ -131,18 +167,18 @@ void ConfigureDialog::UpdateVisibleTabs() { if (items.isEmpty()) return; - const std::map widgets = {{ui->generalTab, tr("General")}, - {ui->systemTab, tr("System")}, - {ui->inputTab, tr("Input")}, - {ui->hotkeysTab, tr("Hotkeys")}, - {ui->enhancementsTab, tr("Enhancements")}, - {ui->graphicsTab, tr("Advanced")}, - {ui->audioTab, tr("Audio")}, - {ui->cameraTab, tr("Camera")}, - {ui->debugTab, tr("Debug")}, - {ui->storageTab, tr("Storage")}, - {ui->webTab, tr("Web")}, - {ui->uiTab, tr("UI")}}; + const std::map widgets = {{general_tab.get(), tr("General")}, + {system_tab.get(), tr("System")}, + {input_tab.get(), tr("Input")}, + {hotkeys_tab.get(), tr("Hotkeys")}, + {enhancements_tab.get(), tr("Enhancements")}, + {graphics_tab.get(), tr("Advanced")}, + {audio_tab.get(), tr("Audio")}, + {camera_tab.get(), tr("Camera")}, + {debug_tab.get(), tr("Debug")}, + {storage_tab.get(), tr("Storage")}, + {web_tab.get(), tr("Web")}, + {ui_tab.get(), tr("UI")}}; ui->tabWidget->clear(); diff --git a/src/citra_qt/configuration/configure_dialog.h b/src/citra_qt/configuration/configure_dialog.h index 23b32a1974..7a4ae96d2e 100644 --- a/src/citra_qt/configuration/configure_dialog.h +++ b/src/citra_qt/configuration/configure_dialog.h @@ -17,6 +17,19 @@ namespace Core { class System; } +class ConfigureGeneral; +class ConfigureSystem; +class ConfigureInput; +class ConfigureHotkeys; +class ConfigureGraphics; +class ConfigureEnhancements; +class ConfigureAudio; +class ConfigureCamera; +class ConfigureDebug; +class ConfigureStorage; +class ConfigureWeb; +class ConfigureUi; + class ConfigureDialog : public QDialog { Q_OBJECT @@ -42,4 +55,18 @@ private: std::unique_ptr ui; HotkeyRegistry& registry; Core::System& system; + bool is_powered_on; + + std::unique_ptr general_tab; + std::unique_ptr system_tab; + std::unique_ptr input_tab; + std::unique_ptr hotkeys_tab; + std::unique_ptr graphics_tab; + std::unique_ptr enhancements_tab; + std::unique_ptr audio_tab; + std::unique_ptr camera_tab; + std::unique_ptr debug_tab; + std::unique_ptr storage_tab; + std::unique_ptr web_tab; + std::unique_ptr ui_tab; }; diff --git a/src/citra_qt/configuration/configure_enhancements.cpp b/src/citra_qt/configuration/configure_enhancements.cpp index 90dfbf44d6..55b8a79eb9 100644 --- a/src/citra_qt/configuration/configure_enhancements.cpp +++ b/src/citra_qt/configuration/configure_enhancements.cpp @@ -22,8 +22,7 @@ ConfigureEnhancements::ConfigureEnhancements(QWidget* parent) const bool res_scale_enabled = graphics_api != Settings::GraphicsAPI::Software; ui->resolution_factor_combobox->setEnabled(res_scale_enabled); - connect(ui->render_3d_combobox, - static_cast(&QComboBox::currentIndexChanged), this, + connect(ui->render_3d_combobox, qOverload(&QComboBox::currentIndexChanged), this, [this](int currentIndex) { updateShaders(static_cast(currentIndex)); }); diff --git a/src/citra_qt/configuration/configure_graphics.cpp b/src/citra_qt/configuration/configure_graphics.cpp index c4fcd81c2b..300a2736d8 100644 --- a/src/citra_qt/configuration/configure_graphics.cpp +++ b/src/citra_qt/configuration/configure_graphics.cpp @@ -6,14 +6,13 @@ #include "citra_qt/configuration/configuration_shared.h" #include "citra_qt/configuration/configure_graphics.h" #include "common/settings.h" -#include "core/core.h" #include "ui_configure_graphics.h" -ConfigureGraphics::ConfigureGraphics(QWidget* parent) +ConfigureGraphics::ConfigureGraphics(bool is_powered_on, QWidget* parent) : QWidget(parent), ui(std::make_unique()) { ui->setupUi(this); - ui->toggle_vsync_new->setEnabled(!Core::System::GetInstance().IsPoweredOn()); + ui->toggle_vsync_new->setEnabled(!is_powered_on); // Set the index to -1 to ensure the below lambda is called with setCurrentIndex ui->graphics_api_combo->setCurrentIndex(-1); diff --git a/src/citra_qt/configuration/configure_graphics.h b/src/citra_qt/configuration/configure_graphics.h index 389bfba853..44b12cfa96 100644 --- a/src/citra_qt/configuration/configure_graphics.h +++ b/src/citra_qt/configuration/configure_graphics.h @@ -19,7 +19,7 @@ class ConfigureGraphics : public QWidget { Q_OBJECT public: - explicit ConfigureGraphics(QWidget* parent = nullptr); + explicit ConfigureGraphics(bool is_powered_on, QWidget* parent = nullptr); ~ConfigureGraphics() override; void ApplyConfiguration(); diff --git a/src/citra_qt/configuration/configure_graphics.ui b/src/citra_qt/configuration/configure_graphics.ui index 74fe21786f..4235759cb1 100644 --- a/src/citra_qt/configuration/configure_graphics.ui +++ b/src/citra_qt/configuration/configure_graphics.ui @@ -19,6 +19,9 @@ Form + + Graphics + diff --git a/src/citra_qt/configuration/configure_hotkeys.cpp b/src/citra_qt/configuration/configure_hotkeys.cpp index e4015aa25d..b07370528d 100644 --- a/src/citra_qt/configuration/configure_hotkeys.cpp +++ b/src/citra_qt/configuration/configure_hotkeys.cpp @@ -9,7 +9,6 @@ #include "citra_qt/configuration/configure_hotkeys.h" #include "citra_qt/hotkeys.h" #include "citra_qt/util/sequence_dialog/sequence_dialog.h" -#include "common/settings.h" #include "ui_configure_hotkeys.h" constexpr int name_column = 0; @@ -189,9 +188,9 @@ void ConfigureHotkeys::PopupContextMenu(const QPoint& menu_location) { QAction* clear = context_menu.addAction(tr("Clear")); const auto hotkey_index = index.sibling(index.row(), hotkey_column); - connect(restore_default, &QAction::triggered, + connect(restore_default, &QAction::triggered, this, [this, hotkey_index] { RestoreHotkey(hotkey_index); }); - connect(clear, &QAction::triggered, + connect(clear, &QAction::triggered, this, [this, hotkey_index] { model->setData(hotkey_index, QString{}); }); context_menu.exec(ui->hotkey_list->viewport()->mapToGlobal(menu_location)); diff --git a/src/citra_qt/configuration/configure_per_game.cpp b/src/citra_qt/configuration/configure_per_game.cpp index c422a74f9f..a9896e0add 100644 --- a/src/citra_qt/configuration/configure_per_game.cpp +++ b/src/citra_qt/configuration/configure_per_game.cpp @@ -31,13 +31,14 @@ ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const QString : fmt::format("{:016X}", title_id); game_config = std::make_unique(config_file_name, Config::ConfigType::PerGameConfig); - audio_tab = std::make_unique(this); + const bool is_powered_on = system.IsPoweredOn(); + audio_tab = std::make_unique(is_powered_on, this); general_tab = std::make_unique(this); enhancements_tab = std::make_unique(this); - graphics_tab = std::make_unique(this); - system_tab = std::make_unique(this); - debug_tab = std::make_unique(this); - cheat_tab = std::make_unique(title_id, this); + graphics_tab = std::make_unique(is_powered_on, this); + system_tab = std::make_unique(system, this); + debug_tab = std::make_unique(is_powered_on, this); + cheat_tab = std::make_unique(system, title_id, this); ui->setupUi(this); diff --git a/src/citra_qt/configuration/configure_storage.cpp b/src/citra_qt/configuration/configure_storage.cpp index 694395ead3..d46cb06146 100644 --- a/src/citra_qt/configuration/configure_storage.cpp +++ b/src/citra_qt/configuration/configure_storage.cpp @@ -6,12 +6,12 @@ #include #include #include "citra_qt/configuration/configure_storage.h" +#include "common/file_util.h" #include "common/settings.h" -#include "core/core.h" #include "ui_configure_storage.h" -ConfigureStorage::ConfigureStorage(QWidget* parent) - : QWidget(parent), ui(std::make_unique()) { +ConfigureStorage::ConfigureStorage(bool is_powered_on_, QWidget* parent) + : QWidget(parent), ui(std::make_unique()), is_powered_on{is_powered_on_} { ui->setupUi(this); SetConfiguration(); @@ -74,7 +74,7 @@ void ConfigureStorage::SetConfiguration() { ui->toggle_virtual_sd->setChecked(Settings::values.use_virtual_sd.GetValue()); ui->toggle_custom_storage->setChecked(Settings::values.use_custom_storage.GetValue()); - ui->storage_group->setEnabled(!Core::System::GetInstance().IsPoweredOn()); + ui->storage_group->setEnabled(!is_powered_on); } void ConfigureStorage::ApplyConfiguration() { diff --git a/src/citra_qt/configuration/configure_storage.h b/src/citra_qt/configuration/configure_storage.h index e79d3c008e..48850fbc22 100644 --- a/src/citra_qt/configuration/configure_storage.h +++ b/src/citra_qt/configuration/configure_storage.h @@ -15,7 +15,7 @@ class ConfigureStorage : public QWidget { Q_OBJECT public: - explicit ConfigureStorage(QWidget* parent = nullptr); + explicit ConfigureStorage(bool is_powered_on, QWidget* parent = nullptr); ~ConfigureStorage() override; void ApplyConfiguration(); @@ -23,4 +23,5 @@ public: void SetConfiguration(); std::unique_ptr ui; + bool is_powered_on; }; diff --git a/src/citra_qt/configuration/configure_system.cpp b/src/citra_qt/configuration/configure_system.cpp index bf2b46ce91..0a5c14a846 100644 --- a/src/citra_qt/configuration/configure_system.cpp +++ b/src/citra_qt/configuration/configure_system.cpp @@ -223,14 +223,12 @@ static const std::array country_names = { QT_TRANSLATE_NOOP("ConfigureSystem", "Bermuda"), // 180-186 }; -ConfigureSystem::ConfigureSystem(QWidget* parent) - : QWidget(parent), ui(std::make_unique()) { +ConfigureSystem::ConfigureSystem(Core::System& system_, QWidget* parent) + : QWidget(parent), ui(std::make_unique()), system{system_} { ui->setupUi(this); - connect(ui->combo_birthmonth, - static_cast(&QComboBox::currentIndexChanged), this, + connect(ui->combo_birthmonth, qOverload(&QComboBox::currentIndexChanged), this, &ConfigureSystem::UpdateBirthdayComboBox); - connect(ui->combo_init_clock, - static_cast(&QComboBox::currentIndexChanged), this, + connect(ui->combo_init_clock, qOverload(&QComboBox::currentIndexChanged), this, &ConfigureSystem::UpdateInitTime); connect(ui->button_regenerate_console_id, &QPushButton::clicked, this, &ConfigureSystem::RefreshConsoleID); @@ -280,7 +278,7 @@ ConfigureSystem::ConfigureSystem(QWidget* parent) ConfigureSystem::~ConfigureSystem() = default; void ConfigureSystem::SetConfiguration() { - enabled = !Core::System::GetInstance().IsPoweredOn(); + enabled = !system.IsPoweredOn(); ui->combo_init_clock->setCurrentIndex(static_cast(Settings::values.init_clock.GetValue())); QDateTime date_time; @@ -296,7 +294,7 @@ void ConfigureSystem::SetConfiguration() { ui->edit_init_time_offset_time->setTime(time); if (!enabled) { - cfg = Service::CFG::GetModule(Core::System::GetInstance()); + cfg = Service::CFG::GetModule(system); ASSERT_MSG(cfg, "CFG Module missing!"); ReadSystemSettings(); ui->group_system_settings->setEnabled(false); diff --git a/src/citra_qt/configuration/configure_system.h b/src/citra_qt/configuration/configure_system.h index 6e24084659..86b6b70168 100644 --- a/src/citra_qt/configuration/configure_system.h +++ b/src/citra_qt/configuration/configure_system.h @@ -16,6 +16,10 @@ namespace ConfigurationShared { enum class CheckState; } +namespace Core { +class System; +} + namespace Service { namespace CFG { class Module; @@ -26,7 +30,7 @@ class ConfigureSystem : public QWidget { Q_OBJECT public: - explicit ConfigureSystem(QWidget* parent = nullptr); + explicit ConfigureSystem(Core::System& system, QWidget* parent = nullptr); ~ConfigureSystem() override; void ApplyConfiguration(); @@ -45,8 +49,10 @@ private: void DownloadFromNUS(); - ConfigurationShared::CheckState is_new_3ds; +private: std::unique_ptr ui; + Core::System& system; + ConfigurationShared::CheckState is_new_3ds; bool enabled = false; std::shared_ptr cfg; diff --git a/src/citra_qt/debugger/graphics/graphics_cmdlists.cpp b/src/citra_qt/debugger/graphics/graphics_cmdlists.cpp index c34a5d153c..6711644e62 100644 --- a/src/citra_qt/debugger/graphics/graphics_cmdlists.cpp +++ b/src/citra_qt/debugger/graphics/graphics_cmdlists.cpp @@ -16,7 +16,6 @@ #include "citra_qt/debugger/graphics/graphics_cmdlists.h" #include "citra_qt/util/util.h" #include "common/vector_math.h" -#include "core/core.h" #include "core/memory.h" #include "video_core/debug_utils/debug_utils.h" #include "video_core/pica_state.h" @@ -167,8 +166,7 @@ void GPUCommandListWidget::SetCommandInfo(const QModelIndex& index) { const auto format = texture.format; const auto info = Pica::Texture::TextureInfo::FromPicaRegister(config, format); - const u8* src = - Core::System::GetInstance().Memory().GetPhysicalPointer(config.GetPhysicalAddress()); + const u8* src = memory.GetPhysicalPointer(config.GetPhysicalAddress()); new_info_widget = new TextureInfoWidget(src, info); } if (command_info_widget) { @@ -182,8 +180,8 @@ void GPUCommandListWidget::SetCommandInfo(const QModelIndex& index) { } #undef COMMAND_IN_RANGE -GPUCommandListWidget::GPUCommandListWidget(QWidget* parent) - : QDockWidget(tr("Pica Command List"), parent) { +GPUCommandListWidget::GPUCommandListWidget(Memory::MemorySystem& memory_, QWidget* parent) + : QDockWidget(tr("Pica Command List"), parent), memory{memory_} { setObjectName(QStringLiteral("Pica Command List")); GPUCommandListModel* model = new GPUCommandListModel(this); diff --git a/src/citra_qt/debugger/graphics/graphics_cmdlists.h b/src/citra_qt/debugger/graphics/graphics_cmdlists.h index 8f40b94c59..5ea4d125b3 100644 --- a/src/citra_qt/debugger/graphics/graphics_cmdlists.h +++ b/src/citra_qt/debugger/graphics/graphics_cmdlists.h @@ -7,11 +7,14 @@ #include #include #include "video_core/debug_utils/debug_utils.h" -#include "video_core/gpu_debugger.h" class QPushButton; class QTreeView; +namespace Memory { +class MemorySystem; +} + class GPUCommandListModel : public QAbstractListModel { Q_OBJECT @@ -39,7 +42,7 @@ class GPUCommandListWidget : public QDockWidget { Q_OBJECT public: - explicit GPUCommandListWidget(QWidget* parent = nullptr); + explicit GPUCommandListWidget(Memory::MemorySystem& memory, QWidget* parent = nullptr); public slots: void OnToggleTracing(); @@ -54,7 +57,7 @@ signals: private: std::unique_ptr pica_trace; - + Memory::MemorySystem& memory; QTreeView* list_widget; QWidget* command_info_widget; QPushButton* toggle_tracing; diff --git a/src/citra_qt/debugger/graphics/graphics_surface.cpp b/src/citra_qt/debugger/graphics/graphics_surface.cpp index 9089c5724d..13d6556cdf 100644 --- a/src/citra_qt/debugger/graphics/graphics_surface.cpp +++ b/src/citra_qt/debugger/graphics/graphics_surface.cpp @@ -15,8 +15,6 @@ #include "citra_qt/debugger/graphics/graphics_surface.h" #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" @@ -51,9 +49,10 @@ void SurfacePicture::mouseMoveEvent(QMouseEvent* event) { mousePressEvent(event); } -GraphicsSurfaceWidget::GraphicsSurfaceWidget(std::shared_ptr debug_context, +GraphicsSurfaceWidget::GraphicsSurfaceWidget(Memory::MemorySystem& memory_, + std::shared_ptr debug_context, QWidget* parent) - : BreakPointObserverDock(debug_context, tr("Pica Surface Viewer"), parent), + : BreakPointObserverDock(debug_context, tr("Pica Surface Viewer"), parent), memory{memory_}, surface_source(Source::ColorBuffer) { setObjectName(QStringLiteral("PicaSurface")); @@ -290,57 +289,57 @@ void GraphicsSurfaceWidget::Pick(int x, int y) { return; } - u8* buffer = Core::System::GetInstance().Memory().GetPhysicalPointer(surface_address); - if (buffer == nullptr) { + const u8* buffer = memory.GetPhysicalPointer(surface_address); + if (!buffer) { 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 nibbles_per_pixel = GraphicsSurfaceWidget::NibblesPerPixel(surface_format); + const u32 stride = nibbles_per_pixel * surface_width / 2; + const bool nibble_mode = (nibbles_per_pixel == 1); + const u32 bytes_per_pixel = [&] { + if (nibble_mode) { + // As nibbles are contained in a byte we still need to access one byte per nibble + return 1u; + } else { + return nibbles_per_pixel / 2; + } + }(); const u32 coarse_y = y & ~7; - u32 offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * stride; + const 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) { + const auto get_text = [offset](Format format, const u8* pixel) { switch (format) { case Format::RGBA8: { - auto value = Common::Color::DecodeRGBA8(pixel) / 255.0f; + const auto value = Common::Color::DecodeRGBA8(pixel) / 255.0f; return QStringLiteral("Red: %1, Green: %2, Blue: %3, Alpha: %4") .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 = Common::Color::DecodeRGB8(pixel) / 255.0f; + const auto value = Common::Color::DecodeRGB8(pixel) / 255.0f; return QStringLiteral("Red: %1, Green: %2, Blue: %3") .arg(QString::number(value.r(), 'f', 2), QString::number(value.g(), 'f', 2), QString::number(value.b(), 'f', 2)); } case Format::RGB5A1: { - auto value = Common::Color::DecodeRGB5A1(pixel) / 255.0f; + const auto value = Common::Color::DecodeRGB5A1(pixel) / 255.0f; return QStringLiteral("Red: %1, Green: %2, Blue: %3, Alpha: %4") .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 = Common::Color::DecodeRGB565(pixel) / 255.0f; + const auto value = Common::Color::DecodeRGB565(pixel) / 255.0f; return QStringLiteral("Red: %1, Green: %2, Blue: %3") .arg(QString::number(value.r(), 'f', 2), QString::number(value.g(), 'f', 2), QString::number(value.b(), 'f', 2)); } case Format::RGBA4: { - auto value = Common::Color::DecodeRGBA4(pixel) / 255.0f; + const auto value = Common::Color::DecodeRGBA4(pixel) / 255.0f; return QStringLiteral("Red: %1, Green: %2, Blue: %3, Alpha: %4") .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)); @@ -348,7 +347,7 @@ void GraphicsSurfaceWidget::Pick(int x, int y) { case Format::IA8: return QStringLiteral("Index: %1, Alpha: %2").arg(pixel[0], pixel[1]); case Format::RG8: { - auto value = Common::Color::DecodeRG8(pixel) / 255.0f; + const auto value = Common::Color::DecodeRG8(pixel) / 255.0f; return QStringLiteral("Red: %1, Green: %2") .arg(QString::number(value.r(), 'f', 2), QString::number(value.g(), 'f', 2)); } @@ -359,11 +358,11 @@ void GraphicsSurfaceWidget::Pick(int x, int y) { case Format::IA4: return QStringLiteral("Index: %1, Alpha: %2").arg(*pixel & 0xF, (*pixel & 0xF0) >> 4); case Format::I4: { - u8 i = (*pixel >> ((offset % 2) ? 4 : 0)) & 0xF; + const u8 i = (*pixel >> ((offset % 2) ? 4 : 0)) & 0xF; return QStringLiteral("Index: %1").arg(i); } case Format::A4: { - u8 a = (*pixel >> ((offset % 2) ? 4 : 0)) & 0xF; + const u8 a = (*pixel >> ((offset % 2) ? 4 : 0)) & 0xF; return QStringLiteral("Alpha: %1").arg(QString::number(a / 15.0f, 'f', 2)); } case Format::ETC1: @@ -371,17 +370,17 @@ void GraphicsSurfaceWidget::Pick(int x, int y) { // TODO: Display block information or channel values? return QStringLiteral("Compressed data"); case Format::D16: { - auto value = Common::Color::DecodeD16(pixel); + const auto value = Common::Color::DecodeD16(pixel); return QStringLiteral("Depth: %1").arg(QString::number(value / (float)0xFFFF, 'f', 4)); } case Format::D24: { - auto value = Common::Color::DecodeD24(pixel); + const auto value = Common::Color::DecodeD24(pixel); return QStringLiteral("Depth: %1") .arg(QString::number(value / (float)0xFFFFFF, 'f', 4)); } case Format::D24X8: case Format::X24S8: { - auto values = Common::Color::DecodeD24S8(pixel); + const auto values = Common::Color::DecodeD24S8(pixel); return QStringLiteral("Depth: %1, Stencil: %2") .arg(QString::number(values[0] / (float)0xFFFFFF, 'f', 4), values[1]); } @@ -398,13 +397,13 @@ void GraphicsSurfaceWidget::Pick(int x, int y) { if (nibble_mode) { nibble_index += (offset % 2) ? 0 : 1; } - u8 byte = pixel[nibble_index / 2]; - u8 nibble = (byte >> ((nibble_index % 2) ? 0 : 4)) & 0xF; + const u8 byte = pixel[nibble_index / 2]; + const u8 nibble = (byte >> ((nibble_index % 2) ? 0 : 4)) & 0xF; nibbles.append(QString::number(nibble, 16).toUpper()); } surface_info_label->setText( - QStringLiteral("Raw: 0x%3\n(%4)").arg(nibbles, GetText(surface_format, pixel))); + QStringLiteral("Raw: 0x%3\n(%4)").arg(nibbles, get_text(surface_format, pixel))); surface_info_label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); } @@ -546,9 +545,9 @@ void GraphicsSurfaceWidget::OnUpdate() { // TODO: Implement a good way to visualize alpha components! QImage decoded_image(surface_width, surface_height, QImage::Format_ARGB32); - u8* buffer = Core::System::GetInstance().Memory().GetPhysicalPointer(surface_address); + const u8* buffer = memory.GetPhysicalPointer(surface_address); - if (buffer == nullptr) { + if (!buffer) { surface_picture_label->hide(); surface_info_label->setText(tr("(invalid surface address)")); surface_info_label->setAlignment(Qt::AlignCenter); @@ -682,9 +681,8 @@ void GraphicsSurfaceWidget::SaveSurface() { 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); - ASSERT_MSG(buffer != nullptr, "Memory not accessible"); + const u8* const buffer = memory.GetPhysicalPointer(surface_address); + ASSERT_MSG(buffer, "Memory not accessible"); QFile file{filename}; if (!file.open(QIODevice::WriteOnly)) { diff --git a/src/citra_qt/debugger/graphics/graphics_surface.h b/src/citra_qt/debugger/graphics/graphics_surface.h index 7f4c9d3cac..7fd6b81113 100644 --- a/src/citra_qt/debugger/graphics/graphics_surface.h +++ b/src/citra_qt/debugger/graphics/graphics_surface.h @@ -14,6 +14,10 @@ class CSpinBox; class GraphicsSurfaceWidget; +namespace Memory { +class MemorySystem; +} + class SurfacePicture : public QLabel { Q_OBJECT @@ -72,7 +76,8 @@ class GraphicsSurfaceWidget : public BreakPointObserverDock { static unsigned int NibblesPerPixel(Format format); public: - explicit GraphicsSurfaceWidget(std::shared_ptr debug_context, + explicit GraphicsSurfaceWidget(Memory::MemorySystem& memory, + std::shared_ptr debug_context, QWidget* parent = nullptr); void Pick(int x, int y); @@ -95,6 +100,7 @@ private: void SaveSurface(); + Memory::MemorySystem& memory; QComboBox* surface_source_list; CSpinBox* surface_address_control; QSpinBox* surface_width_control; diff --git a/src/citra_qt/debugger/ipc/recorder.cpp b/src/citra_qt/debugger/ipc/recorder.cpp index b9ba704462..27382bbec8 100644 --- a/src/citra_qt/debugger/ipc/recorder.cpp +++ b/src/citra_qt/debugger/ipc/recorder.cpp @@ -16,13 +16,13 @@ #include "core/hle/service/sm/sm.h" #include "ui_recorder.h" -IPCRecorderWidget::IPCRecorderWidget(QWidget* parent) - : QDockWidget(parent), ui(std::make_unique()) { +IPCRecorderWidget::IPCRecorderWidget(Core::System& system_, QWidget* parent) + : QDockWidget(parent), ui(std::make_unique()), system{system_} { ui->setupUi(this); qRegisterMetaType(); - connect(ui->enabled, &QCheckBox::stateChanged, + connect(ui->enabled, &QCheckBox::stateChanged, this, [this](int new_state) { SetEnabled(new_state == Qt::Checked); }); connect(ui->clearButton, &QPushButton::clicked, this, &IPCRecorderWidget::Clear); connect(ui->filter, &QLineEdit::textChanged, this, &IPCRecorderWidget::ApplyFilterToAll); @@ -90,7 +90,7 @@ void IPCRecorderWidget::OnEntryUpdated(IPCDebugger::RequestRecord record) { (record.status == IPCDebugger::RequestStatus::Handled && record.translated_reply_cmdbuf[1] != RESULT_SUCCESS.raw)) { // Unimplemented / Error - auto* item = ui->main->invisibleRootItem()->child(row_id); + auto item = ui->main->invisibleRootItem()->child(row_id); for (int column = 0; column < item->columnCount(); ++column) { item->setBackground(column, QBrush(QColor::fromRgb(255, 0, 0))); } @@ -100,11 +100,11 @@ void IPCRecorderWidget::OnEntryUpdated(IPCDebugger::RequestRecord record) { } void IPCRecorderWidget::SetEnabled(bool enabled) { - if (!Core::System::GetInstance().IsPoweredOn()) { + if (!system.IsPoweredOn()) { return; } - auto& ipc_recorder = Core::System::GetInstance().Kernel().GetIPCRecorder(); + auto& ipc_recorder = system.Kernel().GetIPCRecorder(); ipc_recorder.SetEnabled(enabled); if (enabled) { @@ -123,10 +123,10 @@ void IPCRecorderWidget::Clear() { } QString IPCRecorderWidget::GetServiceName(const IPCDebugger::RequestRecord& record) const { - if (Core::System::GetInstance().IsPoweredOn() && record.client_port.id != -1) { - const auto service_name = - Core::System::GetInstance().ServiceManager().GetServiceNameByPortId( - static_cast(record.client_port.id)); + if (system.IsPoweredOn() && record.client_port.id != -1) { + const Service::SM::ServiceManager& sm = system.ServiceManager(); + const u32 port_id = static_cast(record.client_port.id); + const auto service_name = sm.GetServiceNameByPortId(port_id); if (!service_name.empty()) { return QString::fromStdString(service_name); diff --git a/src/citra_qt/debugger/ipc/recorder.h b/src/citra_qt/debugger/ipc/recorder.h index aa02041e86..de0e512be3 100644 --- a/src/citra_qt/debugger/ipc/recorder.h +++ b/src/citra_qt/debugger/ipc/recorder.h @@ -14,11 +14,15 @@ namespace Ui { class IPCRecorder; } +namespace Core { +class System; +} + class IPCRecorderWidget : public QDockWidget { Q_OBJECT public: - explicit IPCRecorderWidget(QWidget* parent = nullptr); + explicit IPCRecorderWidget(Core::System& system, QWidget* parent = nullptr); ~IPCRecorderWidget(); void OnEmulationStarting(); @@ -37,9 +41,10 @@ private: QString GetFunctionName(const IPCDebugger::RequestRecord& record) const; void OpenRecordDialog(QTreeWidgetItem* item, int column); +private: std::unique_ptr ui; IPCDebugger::CallbackHandle handle; - + Core::System& system; // The offset between record id and row id, assuming record ids are assigned // continuously and only the 'Clear' action can be performed, this is enough. // The initial value is 1, which means record 1 = row 0. diff --git a/src/citra_qt/debugger/registers.cpp b/src/citra_qt/debugger/registers.cpp index 6e07869d06..df065ca5c4 100644 --- a/src/citra_qt/debugger/registers.cpp +++ b/src/citra_qt/debugger/registers.cpp @@ -9,8 +9,8 @@ #include "core/core.h" #include "ui_registers.h" -RegistersWidget::RegistersWidget(QWidget* parent) - : QDockWidget(parent), cpu_regs_ui(std::make_unique()) { +RegistersWidget::RegistersWidget(const Core::System& system_, QWidget* parent) + : QDockWidget(parent), cpu_regs_ui(std::make_unique()), system{system_} { cpu_regs_ui->setupUi(this); tree = cpu_regs_ui->treeWidget; @@ -62,17 +62,21 @@ RegistersWidget::RegistersWidget(QWidget* parent) RegistersWidget::~RegistersWidget() = default; void RegistersWidget::OnDebugModeEntered() { - if (!Core::System::GetInstance().IsPoweredOn()) + if (!system.IsPoweredOn()) { return; + } - // Todo: Handle all cores - for (int i = 0; i < core_registers->childCount(); ++i) + // TODO: Handle all cores + const ARM_Interface& core = system.GetCore(0); + for (int i = 0; i < core_registers->childCount(); ++i) { core_registers->child(i)->setText( - 1, QStringLiteral("0x%1").arg(Core::GetCore(0).GetReg(i), 8, 16, QLatin1Char('0'))); + 1, QStringLiteral("0x%1").arg(core.GetReg(i), 8, 16, QLatin1Char('0'))); + } - for (int i = 0; i < vfp_registers->childCount(); ++i) + for (int i = 0; i < vfp_registers->childCount(); ++i) { vfp_registers->child(i)->setText( - 1, QStringLiteral("0x%1").arg(Core::GetCore(0).GetVFPReg(i), 8, 16, QLatin1Char('0'))); + 1, QStringLiteral("0x%1").arg(core.GetVFPReg(i), 8, 16, QLatin1Char('0'))); + } UpdateCPSRValues(); UpdateVFPSystemRegisterValues(); @@ -86,24 +90,29 @@ void RegistersWidget::OnEmulationStarting(EmuThread* emu_thread) { void RegistersWidget::OnEmulationStopping() { // Reset widget text - for (int i = 0; i < core_registers->childCount(); ++i) + for (int i = 0; i < core_registers->childCount(); ++i) { core_registers->child(i)->setText(1, QString{}); + } - for (int i = 0; i < vfp_registers->childCount(); ++i) + for (int i = 0; i < vfp_registers->childCount(); ++i) { vfp_registers->child(i)->setText(1, QString{}); + } - for (int i = 0; i < cpsr->childCount(); ++i) + for (int i = 0; i < cpsr->childCount(); ++i) { cpsr->child(i)->setText(1, QString{}); + } cpsr->setText(1, QString{}); // FPSCR - for (int i = 0; i < vfp_system_registers->child(0)->childCount(); ++i) + for (int i = 0; i < vfp_system_registers->child(0)->childCount(); ++i) { vfp_system_registers->child(0)->child(i)->setText(1, QString{}); + } // FPEXC - for (int i = 0; i < vfp_system_registers->child(1)->childCount(); ++i) + for (int i = 0; i < vfp_system_registers->child(1)->childCount(); ++i) { vfp_system_registers->child(1)->child(i)->setText(1, QString{}); + } vfp_system_registers->child(0)->setText(1, QString{}); vfp_system_registers->child(1)->setText(1, QString{}); @@ -130,8 +139,8 @@ void RegistersWidget::CreateCPSRChildren() { } void RegistersWidget::UpdateCPSRValues() { - // Todo: Handle all cores - const u32 cpsr_val = Core::GetCore(0).GetCPSR(); + // TODO: Handle all cores + const u32 cpsr_val = system.GetCore(0).GetCPSR(); cpsr->setText(1, QStringLiteral("0x%1").arg(cpsr_val, 8, 16, QLatin1Char('0'))); cpsr->child(0)->setText( @@ -193,9 +202,10 @@ void RegistersWidget::CreateVFPSystemRegisterChildren() { } void RegistersWidget::UpdateVFPSystemRegisterValues() { - // Todo: handle all cores - const u32 fpscr_val = Core::GetCore(0).GetVFPSystemReg(VFP_FPSCR); - const u32 fpexc_val = Core::GetCore(0).GetVFPSystemReg(VFP_FPEXC); + // TODO: handle all cores + const ARM_Interface& core = system.GetCore(0); + const u32 fpscr_val = core.GetVFPSystemReg(VFP_FPSCR); + const u32 fpexc_val = core.GetVFPSystemReg(VFP_FPEXC); QTreeWidgetItem* const fpscr = vfp_system_registers->child(0); fpscr->setText(1, QStringLiteral("0x%1").arg(fpscr_val, 8, 16, QLatin1Char('0'))); diff --git a/src/citra_qt/debugger/registers.h b/src/citra_qt/debugger/registers.h index 5d3c1df224..19ed374eaa 100644 --- a/src/citra_qt/debugger/registers.h +++ b/src/citra_qt/debugger/registers.h @@ -15,11 +15,15 @@ namespace Ui { class ARMRegisters; } +namespace Core { +class System; +} + class RegistersWidget : public QDockWidget { Q_OBJECT public: - explicit RegistersWidget(QWidget* parent = nullptr); + explicit RegistersWidget(const Core::System& system, QWidget* parent = nullptr); ~RegistersWidget(); public slots: @@ -37,7 +41,7 @@ private: void UpdateVFPSystemRegisterValues(); std::unique_ptr cpu_regs_ui; - + const Core::System& system; QTreeWidget* tree; QTreeWidgetItem* core_registers; diff --git a/src/citra_qt/debugger/wait_tree.cpp b/src/citra_qt/debugger/wait_tree.cpp index 2e70e1cd33..a277dc81a7 100644 --- a/src/citra_qt/debugger/wait_tree.cpp +++ b/src/citra_qt/debugger/wait_tree.cpp @@ -5,11 +5,10 @@ #include #include "citra_qt/debugger/wait_tree.h" #include "citra_qt/uisettings.h" -#include "citra_qt/util/util.h" #include "common/assert.h" -#include "common/settings.h" #include "core/hle/kernel/event.h" #include "core/hle/kernel/mutex.h" +#include "core/hle/kernel/process.h" #include "core/hle/kernel/semaphore.h" #include "core/hle/kernel/thread.h" #include "core/hle/kernel/timer.h" @@ -76,13 +75,13 @@ std::size_t WaitTreeItem::Row() const { return row; } -std::vector> WaitTreeItem::MakeThreadItemList() { - const u32 num_cores = Core::GetNumCores(); +std::vector> WaitTreeItem::MakeThreadItemList( + Core::System& system) { + const u32 num_cores = system.GetNumCores(); std::vector> item_list; item_list.reserve(num_cores); for (u32 i = 0; i < num_cores; ++i) { - const auto& threads = - Core::System::GetInstance().Kernel().GetThreadManager(i).GetThreadList(); + const auto threads = system.Kernel().GetThreadManager(i).GetThreadList(); item_list.reserve(item_list.size() + threads.size()); for (std::size_t j = 0; j < threads.size(); ++j) { item_list.push_back(std::make_unique(*threads[j])); @@ -436,11 +435,12 @@ void WaitTreeModel::ClearItems() { thread_items.clear(); } -void WaitTreeModel::InitItems() { - thread_items = WaitTreeItem::MakeThreadItemList(); +void WaitTreeModel::InitItems(Core::System& system) { + thread_items = WaitTreeItem::MakeThreadItemList(system); } -WaitTreeWidget::WaitTreeWidget(QWidget* parent) : QDockWidget(tr("Wait Tree"), parent) { +WaitTreeWidget::WaitTreeWidget(Core::System& system_, QWidget* parent) + : QDockWidget(tr("Wait Tree"), parent), system{system_} { setObjectName(QStringLiteral("WaitTreeWidget")); view = new QTreeView(this); view->setHeaderHidden(true); @@ -449,9 +449,10 @@ WaitTreeWidget::WaitTreeWidget(QWidget* parent) : QDockWidget(tr("Wait Tree"), p } void WaitTreeWidget::OnDebugModeEntered() { - if (!Core::System::GetInstance().IsPoweredOn()) + if (!system.IsPoweredOn()) { return; - model->InitItems(); + } + model->InitItems(system); view->setModel(model); setEnabled(true); } diff --git a/src/citra_qt/debugger/wait_tree.h b/src/citra_qt/debugger/wait_tree.h index eca8534c5b..bb9385649f 100644 --- a/src/citra_qt/debugger/wait_tree.h +++ b/src/citra_qt/debugger/wait_tree.h @@ -23,6 +23,10 @@ class Thread; class Timer; } // namespace Kernel +namespace Core { +class System; +} + class WaitTreeThread; class WaitTreeItem : public QObject { @@ -39,7 +43,7 @@ public: WaitTreeItem* Parent() const; std::span> Children() const; std::size_t Row() const; - static std::vector> MakeThreadItemList(); + static std::vector> MakeThreadItemList(Core::System& system); private: std::size_t row; @@ -166,7 +170,7 @@ public: int columnCount(const QModelIndex& parent) const override; void ClearItems(); - void InitItems(); + void InitItems(Core::System& system); private: std::vector> thread_items; @@ -176,7 +180,7 @@ class WaitTreeWidget : public QDockWidget { Q_OBJECT public: - explicit WaitTreeWidget(QWidget* parent = nullptr); + explicit WaitTreeWidget(Core::System& system, QWidget* parent = nullptr); public slots: void OnDebugModeEntered(); @@ -188,4 +192,5 @@ public slots: private: QTreeView* view; WaitTreeModel* model; + Core::System& system; }; diff --git a/src/citra_qt/discord_impl.cpp b/src/citra_qt/discord_impl.cpp index 25f97d07ce..076c15fa80 100644 --- a/src/citra_qt/discord_impl.cpp +++ b/src/citra_qt/discord_impl.cpp @@ -9,10 +9,11 @@ #include "citra_qt/uisettings.h" #include "common/common_types.h" #include "core/core.h" +#include "core/loader/loader.h" namespace DiscordRPC { -DiscordImpl::DiscordImpl() { +DiscordImpl::DiscordImpl(const Core::System& system_) : system{system_} { DiscordEventHandlers handlers{}; // The number is the client ID for Citra, it's used for images and the @@ -34,12 +35,15 @@ void DiscordImpl::Update() { std::chrono::system_clock::now().time_since_epoch()) .count(); std::string title; - if (Core::System::GetInstance().IsPoweredOn()) - Core::System::GetInstance().GetAppLoader().ReadTitle(title); + const bool is_powered_on = system.IsPoweredOn(); + if (is_powered_on) { + system.GetAppLoader().ReadTitle(title); + } + DiscordRichPresence presence{}; presence.largeImageKey = "citra"; presence.largeImageText = "Citra is an emulator for the Nintendo 3DS"; - if (Core::System::GetInstance().IsPoweredOn()) { + if (is_powered_on) { presence.state = title.c_str(); presence.details = "Currently in game"; } else { diff --git a/src/citra_qt/discord_impl.h b/src/citra_qt/discord_impl.h index 067cf2e3d7..25bee06967 100644 --- a/src/citra_qt/discord_impl.h +++ b/src/citra_qt/discord_impl.h @@ -6,15 +6,22 @@ #include "citra_qt/discord.h" +namespace Core { +class System; +} + namespace DiscordRPC { class DiscordImpl : public DiscordInterface { public: - DiscordImpl(); + DiscordImpl(const Core::System& system); ~DiscordImpl() override; void Pause() override; void Update() override; + +private: + const Core::System& system; }; } // namespace DiscordRPC diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp index 7eb2e4b7a1..b447af3234 100644 --- a/src/citra_qt/main.cpp +++ b/src/citra_qt/main.cpp @@ -14,6 +14,7 @@ #include #include #include +#include "core/telemetry_session.h" #ifdef __APPLE__ #include // for chdir #endif @@ -73,7 +74,6 @@ #include "common/microprofile.h" #include "common/scm_rev.h" #include "common/scope_exit.h" -#include "common/string_util.h" #if CITRA_ARCH(x86_64) #include "common/x64/cpu_detect.h" #endif @@ -173,7 +173,7 @@ static QString PrettyProductName() { } GMainWindow::GMainWindow(Core::System& system_) - : ui{std::make_unique()}, system{system_}, movie{Core::Movie::GetInstance()}, + : ui{std::make_unique()}, system{system_}, movie{system.Movie()}, config{std::make_unique()}, emu_thread{nullptr} { Common::Log::Initialize(); Common::Log::Start(); @@ -353,8 +353,8 @@ void GMainWindow::InitializeWidgets() { }); InputCommon::Init(); - multiplayer_state = new MultiplayerState(this, game_list->GetModel(), ui->action_Leave_Room, - ui->action_Show_Room); + multiplayer_state = new MultiplayerState(system, this, game_list->GetModel(), + ui->action_Leave_Room, ui->action_Show_Room); multiplayer_state->setVisible(false); #if ENABLE_QT_UPDATER @@ -434,7 +434,7 @@ void GMainWindow::InitializeDebugWidgets() { debug_menu->addAction(microProfileDialog->toggleViewAction()); #endif - registersWidget = new RegistersWidget(this); + registersWidget = new RegistersWidget(system, this); addDockWidget(Qt::RightDockWidgetArea, registersWidget); registersWidget->hide(); debug_menu->addAction(registersWidget->toggleViewAction()); @@ -448,7 +448,7 @@ void GMainWindow::InitializeDebugWidgets() { graphicsWidget->hide(); debug_menu->addAction(graphicsWidget->toggleViewAction()); - graphicsCommandsWidget = new GPUCommandListWidget(this); + graphicsCommandsWidget = new GPUCommandListWidget(system.Memory(), this); addDockWidget(Qt::RightDockWidgetArea, graphicsCommandsWidget); graphicsCommandsWidget->hide(); debug_menu->addAction(graphicsCommandsWidget->toggleViewAction()); @@ -472,7 +472,7 @@ void GMainWindow::InitializeDebugWidgets() { connect(this, &GMainWindow::EmulationStopping, graphicsTracingWidget, &GraphicsTracingWidget::OnEmulationStopping); - waitTreeWidget = new WaitTreeWidget(this); + waitTreeWidget = new WaitTreeWidget(system, this); addDockWidget(Qt::LeftDockWidgetArea, waitTreeWidget); waitTreeWidget->hide(); debug_menu->addAction(waitTreeWidget->toggleViewAction()); @@ -490,7 +490,7 @@ void GMainWindow::InitializeDebugWidgets() { connect(this, &GMainWindow::EmulationStopping, waitTreeWidget, [this] { lleServiceModulesWidget->setDisabled(false); }); - ipcRecorderWidget = new IPCRecorderWidget(this); + ipcRecorderWidget = new IPCRecorderWidget(system, this); addDockWidget(Qt::RightDockWidgetArea, ipcRecorderWidget); ipcRecorderWidget->hide(); debug_menu->addAction(ipcRecorderWidget->toggleViewAction()); @@ -1413,7 +1413,7 @@ void GMainWindow::UpdateSaveStates() { if (system.GetAppLoader().ReadProgramId(title_id) != Loader::ResultStatus::Success) { return; } - auto savestates = Core::ListSaveStates(title_id); + auto savestates = Core::ListSaveStates(title_id, movie.GetCurrentMovieID()); for (u32 i = 0; i < Core::SaveStateSlotCount; ++i) { actions_load_state[i]->setEnabled(false); actions_load_state[i]->setText(tr("Slot %1").arg(i + 1)); @@ -2046,7 +2046,6 @@ void GMainWindow::OnLoadAmiibo() { return; } - Core::System& system{Core::System::GetInstance()}; Service::SM::ServiceManager& sm = system.ServiceManager(); auto nfc = sm.GetService("nfc:u"); if (nfc == nullptr) { @@ -2118,14 +2117,15 @@ void GMainWindow::OnToggleFilterBar() { } void GMainWindow::OnCreateGraphicsSurfaceViewer() { - auto graphicsSurfaceViewerWidget = new GraphicsSurfaceWidget(Pica::g_debug_context, this); + auto graphicsSurfaceViewerWidget = + new GraphicsSurfaceWidget(system.Memory(), Pica::g_debug_context, this); addDockWidget(Qt::RightDockWidgetArea, graphicsSurfaceViewerWidget); // TODO: Maybe graphicsSurfaceViewerWidget->setFloating(true); graphicsSurfaceViewerWidget->show(); } void GMainWindow::OnRecordMovie() { - MovieRecordDialog dialog(this); + MovieRecordDialog dialog(this, system); if (dialog.exec() != QDialog::Accepted) { return; } @@ -2142,7 +2142,7 @@ void GMainWindow::OnRecordMovie() { } void GMainWindow::OnPlayMovie() { - MoviePlayDialog dialog(this, game_list); + MoviePlayDialog dialog(this, game_list, system); if (dialog.exec() != QDialog::Accepted) { return; } @@ -2847,7 +2847,7 @@ void GMainWindow::RetranslateStatusBar() { void GMainWindow::SetDiscordEnabled([[maybe_unused]] bool state) { #ifdef USE_DISCORD_PRESENCE if (state) { - discord_rpc = std::make_unique(); + discord_rpc = std::make_unique(system); } else { discord_rpc = std::make_unique(); } diff --git a/src/citra_qt/movie/movie_play_dialog.cpp b/src/citra_qt/movie/movie_play_dialog.cpp index 0a389985af..dfdc799f17 100644 --- a/src/citra_qt/movie/movie_play_dialog.cpp +++ b/src/citra_qt/movie/movie_play_dialog.cpp @@ -15,8 +15,9 @@ #include "core/movie.h" #include "ui_movie_play_dialog.h" -MoviePlayDialog::MoviePlayDialog(QWidget* parent, GameList* game_list_) - : QDialog(parent), ui(std::make_unique()), game_list(game_list_) { +MoviePlayDialog::MoviePlayDialog(QWidget* parent, GameList* game_list_, const Core::System& system_) + : QDialog(parent), + ui(std::make_unique()), game_list{game_list_}, system{system_} { ui->setupUi(this); ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); @@ -26,10 +27,10 @@ MoviePlayDialog::MoviePlayDialog(QWidget* parent, GameList* game_list_) connect(ui->buttonBox, &QDialogButtonBox::accepted, this, &MoviePlayDialog::accept); connect(ui->buttonBox, &QDialogButtonBox::rejected, this, &MoviePlayDialog::reject); - if (Core::System::GetInstance().IsPoweredOn()) { + if (system.IsPoweredOn()) { QString note_text; note_text = tr("Current running game will be stopped."); - if (Core::Movie::GetInstance().GetPlayMode() == Core::Movie::PlayMode::Recording) { + if (system.Movie().GetPlayMode() == Core::Movie::PlayMode::Recording) { note_text.append(tr("
Current recording will be discarded.")); } ui->note2Label->setText(note_text); @@ -43,7 +44,7 @@ QString MoviePlayDialog::GetMoviePath() const { } QString MoviePlayDialog::GetGamePath() const { - const auto metadata = Core::Movie::GetInstance().GetMovieMetadata(GetMoviePath().toStdString()); + const auto metadata = system.Movie().GetMovieMetadata(GetMoviePath().toStdString()); return game_list->FindGameByProgramID(metadata.program_id, GameListItemPath::FullPathRole); } @@ -67,9 +68,10 @@ void MoviePlayDialog::UpdateUIDisplay() { ui->lengthLineEdit->clear(); ui->note1Label->setVisible(true); + const auto& movie = system.Movie(); const auto path = GetMoviePath().toStdString(); - const auto validation_result = Core::Movie::GetInstance().ValidateMovie(path); + const auto validation_result = movie.ValidateMovie(path); if (validation_result == Core::Movie::ValidationResult::Invalid) { ui->note1Label->setText(tr("Invalid movie file.")); ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); @@ -94,7 +96,7 @@ void MoviePlayDialog::UpdateUIDisplay() { UNREACHABLE(); } - const auto metadata = Core::Movie::GetInstance().GetMovieMetadata(path); + const auto metadata = movie.GetMovieMetadata(path); // Format game title const auto title = diff --git a/src/citra_qt/movie/movie_play_dialog.h b/src/citra_qt/movie/movie_play_dialog.h index dc4f344a5a..12d6ff991c 100644 --- a/src/citra_qt/movie/movie_play_dialog.h +++ b/src/citra_qt/movie/movie_play_dialog.h @@ -11,11 +11,15 @@ namespace Ui { class MoviePlayDialog; } +namespace Core { +class System; +} + class MoviePlayDialog : public QDialog { Q_OBJECT public: - explicit MoviePlayDialog(QWidget* parent, GameList* game_list); + explicit MoviePlayDialog(QWidget* parent, GameList* game_list, const Core::System& system); ~MoviePlayDialog() override; QString GetMoviePath() const; @@ -27,4 +31,5 @@ private: std::unique_ptr ui; GameList* game_list; + const Core::System& system; }; diff --git a/src/citra_qt/movie/movie_record_dialog.cpp b/src/citra_qt/movie/movie_record_dialog.cpp index 9b7967d9aa..b689145354 100644 --- a/src/citra_qt/movie/movie_record_dialog.cpp +++ b/src/citra_qt/movie/movie_record_dialog.cpp @@ -10,8 +10,8 @@ #include "core/movie.h" #include "ui_movie_record_dialog.h" -MovieRecordDialog::MovieRecordDialog(QWidget* parent) - : QDialog(parent), ui(std::make_unique()) { +MovieRecordDialog::MovieRecordDialog(QWidget* parent, const Core::System& system_) + : QDialog(parent), ui(std::make_unique()), system{system_} { ui->setupUi(this); ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); @@ -23,9 +23,9 @@ MovieRecordDialog::MovieRecordDialog(QWidget* parent) connect(ui->buttonBox, &QDialogButtonBox::rejected, this, &MovieRecordDialog::reject); QString note_text; - if (Core::System::GetInstance().IsPoweredOn()) { + if (system.IsPoweredOn()) { note_text = tr("Current running game will be restarted."); - if (Core::Movie::GetInstance().GetPlayMode() == Core::Movie::PlayMode::Recording) { + if (system.Movie().GetPlayMode() == Core::Movie::PlayMode::Recording) { note_text.append(tr("
Current recording will be discarded.")); } } else { diff --git a/src/citra_qt/movie/movie_record_dialog.h b/src/citra_qt/movie/movie_record_dialog.h index c91f1f4141..4eb03af9a4 100644 --- a/src/citra_qt/movie/movie_record_dialog.h +++ b/src/citra_qt/movie/movie_record_dialog.h @@ -9,11 +9,15 @@ namespace Ui { class MovieRecordDialog; } +namespace Core { +class System; +} + class MovieRecordDialog : public QDialog { Q_OBJECT public: - explicit MovieRecordDialog(QWidget* parent); + explicit MovieRecordDialog(QWidget* parent, const Core::System& system); ~MovieRecordDialog() override; QString GetPath() const; @@ -24,4 +28,5 @@ private: void UpdateUIDisplay(); std::unique_ptr ui; + const Core::System& system; }; diff --git a/src/citra_qt/multiplayer/direct_connect.cpp b/src/citra_qt/multiplayer/direct_connect.cpp index 3b2b6549e4..936fd0435c 100644 --- a/src/citra_qt/multiplayer/direct_connect.cpp +++ b/src/citra_qt/multiplayer/direct_connect.cpp @@ -9,10 +9,8 @@ #include #include #include "citra_qt/main.h" -#include "citra_qt/multiplayer/client_room.h" #include "citra_qt/multiplayer/direct_connect.h" #include "citra_qt/multiplayer/message.h" -#include "citra_qt/multiplayer/state.h" #include "citra_qt/multiplayer/validation.h" #include "citra_qt/uisettings.h" #include "core/hle/service/cfg/cfg.h" @@ -22,9 +20,9 @@ enum class ConnectionType : u8 { TraversalServer, IP }; -DirectConnectWindow::DirectConnectWindow(QWidget* parent) +DirectConnectWindow::DirectConnectWindow(Core::System& system_, QWidget* parent) : QDialog(parent, Qt::WindowTitleHint | Qt::WindowCloseButtonHint | Qt::WindowSystemMenuHint), - ui(std::make_unique()) { + ui(std::make_unique()), system{system_} { ui->setupUi(this); @@ -91,7 +89,7 @@ void DirectConnectWindow::Connect() { if (auto room_member = Network::GetRoomMember().lock()) { auto port = UISettings::values.port.toUInt(); room_member->Join(ui->nickname->text().toStdString(), - Service::CFG::GetConsoleIdHash(Core::System::GetInstance()), + Service::CFG::GetConsoleIdHash(system), ui->ip->text().toStdString().c_str(), port, 0, Network::NoPreferredMac, ui->password->text().toStdString().c_str()); } diff --git a/src/citra_qt/multiplayer/direct_connect.h b/src/citra_qt/multiplayer/direct_connect.h index 757ec24a00..b03fa06c74 100644 --- a/src/citra_qt/multiplayer/direct_connect.h +++ b/src/citra_qt/multiplayer/direct_connect.h @@ -13,11 +13,15 @@ namespace Ui { class DirectConnect; } +namespace Core { +class System; +} + class DirectConnectWindow : public QDialog { Q_OBJECT public: - explicit DirectConnectWindow(QWidget* parent = nullptr); + explicit DirectConnectWindow(Core::System& system, QWidget* parent = nullptr); ~DirectConnectWindow(); void RetranslateUi(); @@ -39,5 +43,6 @@ private: QFutureWatcher* watcher; std::unique_ptr ui; + Core::System& system; Validation validation; }; diff --git a/src/citra_qt/multiplayer/host_room.cpp b/src/citra_qt/multiplayer/host_room.cpp index 498ce943b7..8a1ca02e25 100644 --- a/src/citra_qt/multiplayer/host_room.cpp +++ b/src/citra_qt/multiplayer/host_room.cpp @@ -12,7 +12,6 @@ #include #include #include "citra_qt/game_list_p.h" -#include "citra_qt/main.h" #include "citra_qt/multiplayer/host_room.h" #include "citra_qt/multiplayer/message.h" #include "citra_qt/multiplayer/state.h" @@ -27,10 +26,10 @@ #include "web_service/verify_user_jwt.h" #endif -HostRoomWindow::HostRoomWindow(QWidget* parent, QStandardItemModel* list, +HostRoomWindow::HostRoomWindow(Core::System& system_, QWidget* parent, QStandardItemModel* list, std::shared_ptr session) : QDialog(parent, Qt::WindowTitleHint | Qt::WindowCloseButtonHint | Qt::WindowSystemMenuHint), - ui(std::make_unique()), announce_multiplayer_session(session) { + ui(std::make_unique()), system{system_}, announce_multiplayer_session(session) { ui->setupUi(this); // set up validation for all of the fields @@ -196,9 +195,8 @@ void HostRoomWindow::Host() { } } #endif - member->Join(ui->username->text().toStdString(), - Service::CFG::GetConsoleIdHash(Core::System::GetInstance()), "127.0.0.1", port, - 0, Network::NoPreferredMac, password, token); + member->Join(ui->username->text().toStdString(), Service::CFG::GetConsoleIdHash(system), + "127.0.0.1", port, 0, Network::NoPreferredMac, password, token); // Store settings UISettings::values.room_nickname = ui->username->text(); diff --git a/src/citra_qt/multiplayer/host_room.h b/src/citra_qt/multiplayer/host_room.h index b6f84f078b..e481bd000d 100644 --- a/src/citra_qt/multiplayer/host_room.h +++ b/src/citra_qt/multiplayer/host_room.h @@ -9,14 +9,16 @@ #include #include #include -#include "citra_qt/multiplayer/chat_room.h" #include "citra_qt/multiplayer/validation.h" -#include "network/network.h" namespace Ui { class HostRoom; } +namespace Core { +class System; +} + namespace Network { class AnnounceMultiplayerSession; } @@ -34,7 +36,7 @@ class HostRoomWindow : public QDialog { Q_OBJECT public: - explicit HostRoomWindow(QWidget* parent, QStandardItemModel* list, + explicit HostRoomWindow(Core::System& system, QWidget* parent, QStandardItemModel* list, std::shared_ptr session); ~HostRoomWindow(); @@ -50,6 +52,7 @@ private: std::unique_ptr CreateVerifyBackend(bool use_validation) const; std::unique_ptr ui; + Core::System& system; std::weak_ptr announce_multiplayer_session; QStandardItemModel* game_list; ComboBoxProxyModel* proxy; diff --git a/src/citra_qt/multiplayer/lobby.cpp b/src/citra_qt/multiplayer/lobby.cpp index 72f11b9dbd..862ed19a53 100644 --- a/src/citra_qt/multiplayer/lobby.cpp +++ b/src/citra_qt/multiplayer/lobby.cpp @@ -7,11 +7,9 @@ #include #include "citra_qt/game_list_p.h" #include "citra_qt/main.h" -#include "citra_qt/multiplayer/client_room.h" #include "citra_qt/multiplayer/lobby.h" #include "citra_qt/multiplayer/lobby_p.h" #include "citra_qt/multiplayer/message.h" -#include "citra_qt/multiplayer/state.h" #include "citra_qt/multiplayer/validation.h" #include "citra_qt/uisettings.h" #include "common/logging/log.h" @@ -23,10 +21,10 @@ #include "web_service/web_backend.h" #endif -Lobby::Lobby(QWidget* parent, QStandardItemModel* list, +Lobby::Lobby(Core::System& system_, QWidget* parent, QStandardItemModel* list, std::shared_ptr session) : QDialog(parent, Qt::WindowTitleHint | Qt::WindowCloseButtonHint | Qt::WindowSystemMenuHint), - ui(std::make_unique()), announce_multiplayer_session(session) { + ui(std::make_unique()), system{system_}, announce_multiplayer_session(session) { ui->setupUi(this); // setup the watcher for background connections @@ -152,8 +150,8 @@ void Lobby::OnJoinRoom(const QModelIndex& source) { const std::string verify_UID = proxy->data(connection_index, LobbyItemHost::HostVerifyUIDRole).toString().toStdString(); - // attempt to connect in a different thread - QFuture f = QtConcurrent::run([nickname, ip, port, password, verify_UID] { + // Attempt to connect in a different thread + QFuture f = QtConcurrent::run([this, nickname, ip, port, password, verify_UID] { std::string token; #ifdef ENABLE_WEB_SERVICE if (!NetSettings::values.citra_username.empty() && @@ -170,8 +168,8 @@ void Lobby::OnJoinRoom(const QModelIndex& source) { } #endif if (auto room_member = Network::GetRoomMember().lock()) { - room_member->Join(nickname, Service::CFG::GetConsoleIdHash(Core::System::GetInstance()), - ip.c_str(), port, 0, Network::NoPreferredMac, password, token); + room_member->Join(nickname, Service::CFG::GetConsoleIdHash(system), ip.c_str(), port, 0, + Network::NoPreferredMac, password, token); } }); watcher->setFuture(f); diff --git a/src/citra_qt/multiplayer/lobby.h b/src/citra_qt/multiplayer/lobby.h index 129b25497f..f245c7ac69 100644 --- a/src/citra_qt/multiplayer/lobby.h +++ b/src/citra_qt/multiplayer/lobby.h @@ -12,12 +12,16 @@ #include "citra_qt/multiplayer/validation.h" #include "common/announce_multiplayer_room.h" #include "network/announce_multiplayer_session.h" -#include "network/network.h" +#include "network/room_member.h" namespace Ui { class Lobby; } +namespace Core { +class System; +} + class LobbyModel; class LobbyFilterProxyModel; @@ -29,7 +33,7 @@ class Lobby : public QDialog { Q_OBJECT public: - explicit Lobby(QWidget* parent, QStandardItemModel* list, + explicit Lobby(Core::System& system, QWidget* parent, QStandardItemModel* list, std::shared_ptr session); ~Lobby() override; @@ -84,7 +88,9 @@ private: */ QString PasswordPrompt(); +private: std::unique_ptr ui; + Core::System& system; QStandardItemModel* model{}; QStandardItemModel* game_list{}; diff --git a/src/citra_qt/multiplayer/state.cpp b/src/citra_qt/multiplayer/state.cpp index 2835cae75f..72a36c6af9 100644 --- a/src/citra_qt/multiplayer/state.cpp +++ b/src/citra_qt/multiplayer/state.cpp @@ -17,9 +17,10 @@ #include "citra_qt/util/clickable_label.h" #include "common/logging/log.h" -MultiplayerState::MultiplayerState(QWidget* parent, QStandardItemModel* game_list_model, - QAction* leave_room, QAction* show_room) - : QWidget(parent), game_list_model(game_list_model), leave_room(leave_room), +MultiplayerState::MultiplayerState(Core::System& system_, QWidget* parent, + QStandardItemModel* game_list_model, QAction* leave_room, + QAction* show_room) + : QWidget(parent), system{system_}, game_list_model(game_list_model), leave_room(leave_room), show_room(show_room) { if (auto member = Network::GetRoomMember().lock()) { // register the network structs to use in slots and signals @@ -203,14 +204,14 @@ static void BringWidgetToFront(QWidget* widget) { void MultiplayerState::OnViewLobby() { if (lobby == nullptr) { - lobby = new Lobby(this, game_list_model, announce_multiplayer_session); + lobby = new Lobby(system, this, game_list_model, announce_multiplayer_session); } BringWidgetToFront(lobby); } void MultiplayerState::OnCreateRoom() { if (host_room == nullptr) { - host_room = new HostRoomWindow(this, game_list_model, announce_multiplayer_session); + host_room = new HostRoomWindow(system, this, game_list_model, announce_multiplayer_session); } BringWidgetToFront(host_room); } @@ -274,7 +275,7 @@ void MultiplayerState::OnOpenNetworkRoom() { void MultiplayerState::OnDirectConnectToRoom() { if (direct_connect == nullptr) { - direct_connect = new DirectConnectWindow(this); + direct_connect = new DirectConnectWindow(system, this); } BringWidgetToFront(direct_connect); } diff --git a/src/citra_qt/multiplayer/state.h b/src/citra_qt/multiplayer/state.h index 3607d97af7..ccdec49330 100644 --- a/src/citra_qt/multiplayer/state.h +++ b/src/citra_qt/multiplayer/state.h @@ -15,12 +15,16 @@ class ClientRoomWindow; class DirectConnectWindow; class ClickableLabel; +namespace Core { +class System; +} + class MultiplayerState : public QWidget { Q_OBJECT; public: - explicit MultiplayerState(QWidget* parent, QStandardItemModel* game_list, QAction* leave_room, - QAction* show_room); + explicit MultiplayerState(Core::System& system, QWidget* parent, QStandardItemModel* game_list, + QAction* leave_room, QAction* show_room); ~MultiplayerState(); /** @@ -71,6 +75,7 @@ signals: void AnnounceFailed(const Common::WebResult&); private: + Core::System& system; Lobby* lobby = nullptr; HostRoomWindow* host_room = nullptr; ClientRoomWindow* client_room = nullptr; diff --git a/src/citra_qt/updater/updater.cpp b/src/citra_qt/updater/updater.cpp index 498ba986ca..d88e2cb0f4 100644 --- a/src/citra_qt/updater/updater.cpp +++ b/src/citra_qt/updater/updater.cpp @@ -130,11 +130,9 @@ bool UpdaterPrivate::StartUpdateCheck() { main_process->setProgram(tool_info.absoluteFilePath()); main_process->setArguments({QStringLiteral("--checkupdates"), QStringLiteral("-v")}); - connect(main_process, - static_cast(&QProcess::finished), this, + connect(main_process, qOverload(&QProcess::finished), this, &UpdaterPrivate::UpdaterReady, Qt::QueuedConnection); - connect(main_process, - static_cast(&QProcess::errorOccurred), this, + connect(main_process, qOverload(&QProcess::errorOccurred), this, &UpdaterPrivate::UpdaterError, Qt::QueuedConnection); main_process->start(QIODevice::ReadOnly); @@ -156,7 +154,7 @@ void UpdaterPrivate::StopUpdateCheck(int delay, bool async) { QTimer* timer = new QTimer(this); timer->setSingleShot(true); - connect(timer, &QTimer::timeout, [this, timer]() { + connect(timer, &QTimer::timeout, this, [this, timer]() { StopUpdateCheck(0, false); timer->deleteLater(); }); diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index ddf715f2f4..d5500a4322 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -176,6 +176,9 @@ endif() if (CITRA_USE_PRECOMPILED_HEADERS) target_precompile_headers(citra_common PRIVATE precompiled_headers.h) endif() -if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux" AND CMAKE_CXX_COMPILER_ID STREQUAL GNU) - target_link_libraries(citra_common PRIVATE backtrace dl) + +find_library(BACKTRACE_LIBRARY backtrace) +if (BACKTRACE_LIBRARY AND ${CMAKE_SYSTEM_NAME} STREQUAL "Linux" AND CMAKE_CXX_COMPILER_ID STREQUAL GNU) + target_link_libraries(citra_common PRIVATE ${BACKTRACE_LIBRARY} dl) + target_compile_definitions(citra_common PRIVATE CITRA_LINUX_GCC_BACKTRACE) endif() diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp index 5c80cdff0f..f39c79dea0 100644 --- a/src/common/logging/backend.cpp +++ b/src/common/logging/backend.cpp @@ -13,12 +13,11 @@ #define _SH_DENYWR 0 #endif -#if defined(__linux__) && defined(__GNUG__) && !defined(__clang__) +#ifdef CITRA_LINUX_GCC_BACKTRACE #define BOOST_STACKTRACE_USE_BACKTRACE #include #undef BOOST_STACKTRACE_USE_BACKTRACE #include -#define CITRA_LINUX_GCC_BACKTRACE #endif #include "common/bounded_threadsafe_queue.h" diff --git a/src/common/slot_vector.h b/src/common/slot_vector.h index 9a383f2a0f..1969afe64f 100644 --- a/src/common/slot_vector.h +++ b/src/common/slot_vector.h @@ -62,12 +62,29 @@ public: return SlotId{index}; } + template + [[nodiscard]] SlotId swap_and_insert(SlotId existing_id, Args&&... args) noexcept { + const u32 index = FreeValueIndex(); + T& existing_value = values[existing_id.index].object; + + new (&values[index].object) T(std::move(existing_value)); + existing_value.~T(); + new (&values[existing_id.index].object) T(std::forward(args)...); + SetStorageBit(index); + + return SlotId{index}; + } + void erase(SlotId id) noexcept { values[id.index].object.~T(); free_list.push_back(id.index); ResetStorageBit(id.index); } + size_t size() const noexcept { + return values_capacity - free_list.size(); + } + private: struct NonTrivialDummy { NonTrivialDummy() noexcept {} @@ -93,7 +110,7 @@ private: return ((stored_bitset[index / 64] >> (index % 64)) & 1) != 0; } - void ValidateIndex(SlotId id) const noexcept { + void ValidateIndex([[maybe_unused]] SlotId id) const noexcept { DEBUG_ASSERT(id); DEBUG_ASSERT(id.index / 64 < stored_bitset.size()); DEBUG_ASSERT(((stored_bitset[id.index / 64] >> (id.index % 64)) & 1) != 0); diff --git a/src/core/core.cpp b/src/core/core.cpp index 098c3625bb..97f71d7d65 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -2,8 +2,6 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include -#include #include #include #include @@ -16,6 +14,8 @@ #include "core/arm/arm_interface.h" #include "core/arm/exclusive_monitor.h" #include "core/hle/service/cam/cam.h" +#include "core/hle/service/hid/hid.h" +#include "core/hle/service/ir/ir_user.h" #if CITRA_ARCH(x86_64) || CITRA_ARCH(arm64) #include "core/arm/dynarmic/arm_dynarmic.h" #endif @@ -35,9 +35,7 @@ #include "core/hle/service/cam/cam.h" #include "core/hle/service/fs/archive.h" #include "core/hle/service/gsp/gsp.h" -#include "core/hle/service/hid/hid.h" #include "core/hle/service/ir/ir_rst.h" -#include "core/hle/service/ir/ir_user.h" #include "core/hle/service/mic/mic_u.h" #include "core/hle/service/plgldr/plgldr.h" #include "core/hle/service/service.h" @@ -48,6 +46,7 @@ #include "core/loader/loader.h" #include "core/movie.h" #include "core/rpc/server.h" +#include "core/telemetry_session.h" #include "network/network.h" #include "video_core/custom_textures/custom_tex_manager.h" #include "video_core/renderer_base.h" @@ -72,6 +71,8 @@ Core::Timing& Global() { return System::GetInstance().CoreTiming(); } +System::System() : movie{*this} {} + System::~System() = default; System::ResultStatus System::RunLoop(bool tight_loop) { @@ -85,7 +86,7 @@ System::ResultStatus System::RunLoop(bool tight_loop) { if (thread && running_core) { running_core->SaveContext(thread->context); } - GDBStub::HandlePacket(); + GDBStub::HandlePacket(*this); // If the loop is halted and we want to step, use a tiny (1) number of instructions to // execute. Otherwise, get out of the loop function. @@ -320,12 +321,12 @@ System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::st cheat_engine = std::make_unique(title_id, *this); perf_stats = std::make_unique(title_id); + if (Settings::values.dump_textures) { + custom_tex_manager->PrepareDumping(title_id); + } if (Settings::values.custom_textures) { custom_tex_manager->FindCustomTextures(); } - if (Settings::values.dump_textures) { - custom_tex_manager->WriteConfig(); - } status = ResultStatus::Success; m_emu_window = &emu_window; @@ -367,12 +368,13 @@ System::ResultStatus System::Init(Frontend::EmuWindow& emu_window, const Kernel::New3dsHwCapabilities& n3ds_hw_caps, u32 num_cores) { LOG_DEBUG(HW_Memory, "initialized OK"); - memory = std::make_unique(); + memory = std::make_unique(*this); timing = std::make_unique(num_cores, Settings::values.cpu_clock_percentage.GetValue()); kernel = std::make_unique( - *memory, *timing, [this] { PrepareReschedule(); }, memory_mode, num_cores, n3ds_hw_caps); + *memory, *timing, [this] { PrepareReschedule(); }, memory_mode, num_cores, n3ds_hw_caps, + movie.GetOverrideInitTime()); exclusive_monitor = MakeExclusiveMonitor(*memory, num_cores); cpu_cores.reserve(num_cores); @@ -508,6 +510,14 @@ const VideoCore::CustomTexManager& System::CustomTexManager() const { return *custom_tex_manager; } +Core::Movie& System::Movie() { + return movie; +} + +const Core::Movie& System::Movie() const { + return movie; +} + void System::RegisterMiiSelector(std::shared_ptr mii_selector) { registered_mii_selector = std::move(mii_selector); } @@ -617,16 +627,14 @@ void System::ApplySettings() { if (VideoCore::g_renderer) { auto& settings = VideoCore::g_renderer->Settings(); settings.bg_color_update_requested = true; - settings.sampler_update_requested = true; settings.shader_update_requested = true; - settings.texture_filter_update_requested = true; } if (IsPoweredOn()) { CoreTiming().UpdateClockSpeed(Settings::values.cpu_clock_percentage.GetValue()); - Core::DSP().SetSink(Settings::values.output_type.GetValue(), - Settings::values.output_device.GetValue()); - Core::DSP().EnableStretching(Settings::values.enable_audio_stretching.GetValue()); + dsp_core->SetSink(Settings::values.output_type.GetValue(), + Settings::values.output_device.GetValue()); + dsp_core->EnableStretching(Settings::values.enable_audio_stretching.GetValue()); auto hid = Service::HID::GetModule(*this); if (hid) { @@ -704,7 +712,7 @@ void System::serialize(Archive& ar, const unsigned int file_version) { ar&* kernel.get(); VideoCore::serialize(ar, file_version); if (file_version >= 1) { - ar& Movie::GetInstance(); + ar& movie; } // This needs to be set from somewhere - might as well be here! diff --git a/src/core/core.h b/src/core/core.h index af72da85c0..4c401b736d 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -10,18 +10,17 @@ #include #include #include "common/common_types.h" -#include "core/frontend/applets/mii_selector.h" -#include "core/frontend/applets/swkbd.h" -#include "core/loader/loader.h" -#include "core/memory.h" +#include "core/arm/arm_interface.h" +#include "core/movie.h" #include "core/perf_stats.h" -#include "core/telemetry_session.h" class ARM_Interface; namespace Frontend { class EmuWindow; class ImageInterface; +class MiiSelector; +class SoftwareKeyboard; } // namespace Frontend namespace Memory { @@ -47,7 +46,9 @@ class ArchiveManager; namespace Kernel { class KernelSystem; -} +struct New3dsHwCapabilities; +enum class MemoryMode : u8; +} // namespace Kernel namespace Cheats { class CheatEngine; @@ -62,8 +63,13 @@ class CustomTexManager; class RendererBase; } // namespace VideoCore +namespace Loader { +class AppLoader; +} + namespace Core { +class TelemetrySession; class ExclusiveMonitor; class Timing; @@ -95,6 +101,7 @@ public: ErrorUnknown ///< Any other error }; + explicit System(); ~System(); /** @@ -187,6 +194,10 @@ public: return *cpu_cores[core_id]; }; + [[nodiscard]] const ARM_Interface& GetCore(u32 core_id) const { + return *cpu_cores[core_id]; + }; + [[nodiscard]] u32 GetNumCores() const { return static_cast(cpu_cores.size()); } @@ -258,6 +269,12 @@ public: /// Gets a const reference to the custom texture cache system [[nodiscard]] const VideoCore::CustomTexManager& CustomTexManager() const; + /// Gets a reference to the movie recorder + [[nodiscard]] Core::Movie& Movie(); + + /// Gets a const reference to the movie recorder + [[nodiscard]] const Core::Movie& Movie() const; + /// Video Dumper interface void RegisterVideoDumper(std::shared_ptr video_dumper); @@ -373,6 +390,9 @@ private: std::shared_ptr registered_mii_selector; std::shared_ptr registered_swkbd; + /// Movie recorder + Core::Movie movie; + /// Cheats manager std::unique_ptr cheat_engine; @@ -435,10 +455,6 @@ private: return System::GetInstance().GetNumCores(); } -[[nodiscard]] inline AudioCore::DspInterface& DSP() { - return System::GetInstance().DSP(); -} - } // namespace Core BOOST_CLASS_VERSION(Core::System, 1) diff --git a/src/core/file_sys/ncch_container.h b/src/core/file_sys/ncch_container.h index a683a4ee53..ea1b66b482 100644 --- a/src/core/file_sys/ncch_container.h +++ b/src/core/file_sys/ncch_container.h @@ -12,8 +12,8 @@ #include "common/common_types.h" #include "common/file_util.h" #include "common/swap.h" -#include "core/core.h" #include "core/file_sys/romfs_reader.h" +#include "core/loader/loader.h" enum NCSDContentIndex { Main = 0, Manual = 1, DLP = 2, New3DSUpdate = 6, Update = 7 }; diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp index 592078aa28..e0257f0a61 100644 --- a/src/core/gdbstub/gdbstub.cpp +++ b/src/core/gdbstub/gdbstub.cpp @@ -36,7 +36,6 @@ #include "core/gdbstub/gdbstub.h" #include "core/gdbstub/hio.h" #include "core/hle/kernel/process.h" -#include "core/loader/loader.h" #include "core/memory.h" namespace GDBStub { @@ -1035,7 +1034,7 @@ static void RemoveBreakpoint() { SendReply("OK"); } -void HandlePacket() { +void HandlePacket(Core::System& system) { if (!IsConnected()) { if (defer_start) { ToggleServer(true); @@ -1076,7 +1075,7 @@ void HandlePacket() { Continue(); return; case 'F': - HandleHioReply(command_buffer, command_length); + HandleHioReply(system, command_buffer, command_length); break; case 'g': ReadRegisters(); diff --git a/src/core/gdbstub/gdbstub.h b/src/core/gdbstub/gdbstub.h index d361eb1ea2..fc0c7df238 100644 --- a/src/core/gdbstub/gdbstub.h +++ b/src/core/gdbstub/gdbstub.h @@ -10,6 +10,10 @@ #include "common/common_types.h" #include "core/hle/kernel/thread.h" +namespace Core { +class System; +} + namespace GDBStub { /// Breakpoint Method @@ -70,7 +74,7 @@ void Break(bool is_memory_break = false); bool IsMemoryBreak(); /// Read and handle packet from gdb client. -void HandlePacket(); +void HandlePacket(Core::System& system); /** * Get the nearest breakpoint of the specified type at the given address. diff --git a/src/core/gdbstub/hio.cpp b/src/core/gdbstub/hio.cpp index c9c1cb161e..00eab9061a 100644 --- a/src/core/gdbstub/hio.cpp +++ b/src/core/gdbstub/hio.cpp @@ -54,7 +54,7 @@ static void SendErrorReply(int error_code, int retval = -1) { SendReply(packet.data()); } -void SetHioRequest(const VAddr addr) { +void SetHioRequest(Core::System& system, const VAddr addr) { if (!IsServerEnabled()) { LOG_WARNING(Debug_GDBStub, "HIO requested but GDB stub is not running"); return; @@ -69,15 +69,15 @@ void SetHioRequest(const VAddr addr) { LOG_INFO(Debug_GDBStub, "overwriting existing HIO request that was not sent yet"); } - auto& memory = Core::System::GetInstance().Memory(); - const auto process = Core::System::GetInstance().Kernel().GetCurrentProcess(); + auto& memory = system.Memory(); + const auto process = system.Kernel().GetCurrentProcess(); if (!memory.IsValidVirtualAddress(*process, addr)) { LOG_WARNING(Debug_GDBStub, "Invalid address for HIO request"); return; } - memory.ReadBlock(*process, addr, ¤t_hio_request, sizeof(PackedGdbHioRequest)); + memory.ReadBlock(addr, ¤t_hio_request, sizeof(PackedGdbHioRequest)); if (current_hio_request.magic != std::array{'G', 'D', 'B', '\0'}) { std::string_view bad_magic{ @@ -105,10 +105,11 @@ void SetHioRequest(const VAddr addr) { Break(); SetCpuHaltFlag(true); SetCpuStepFlag(false); - Core::GetRunningCore().ClearInstructionCache(); + system.GetRunningCore().ClearInstructionCache(); } -void HandleHioReply(const u8* const command_buffer, const u32 command_length) { +void HandleHioReply(Core::System& system, const u8* const command_buffer, + const u32 command_length) { if (!IsWaitingForHioReply()) { LOG_WARNING(Debug_GDBStub, "Got HIO reply but never sent a request"); return; @@ -176,8 +177,8 @@ void HandleHioReply(const u8* const command_buffer, const u32 command_length) { current_hio_request.retval, current_hio_request.gdb_errno, current_hio_request.ctrl_c); - const auto process = Core::System::GetInstance().Kernel().GetCurrentProcess(); - auto& memory = Core::System::GetInstance().Memory(); + const auto process = system.Kernel().GetCurrentProcess(); + auto& memory = system.Memory(); // should have been checked when we first initialized the request, // but just double check again before we write to memory @@ -187,8 +188,7 @@ void HandleHioReply(const u8* const command_buffer, const u32 command_length) { return; } - memory.WriteBlock(*process, current_hio_request_addr, ¤t_hio_request, - sizeof(PackedGdbHioRequest)); + memory.WriteBlock(current_hio_request_addr, ¤t_hio_request, sizeof(PackedGdbHioRequest)); current_hio_request = {}; current_hio_request_addr = 0; @@ -197,7 +197,7 @@ void HandleHioReply(const u8* const command_buffer, const u32 command_length) { // Restore state from before the request came in SetCpuStepFlag(was_stepping); SetCpuHaltFlag(was_halted); - Core::GetRunningCore().ClearInstructionCache(); + system.GetRunningCore().ClearInstructionCache(); } bool HandlePendingHioRequestPacket() { diff --git a/src/core/gdbstub/hio.h b/src/core/gdbstub/hio.h index d498a27378..827521841b 100644 --- a/src/core/gdbstub/hio.h +++ b/src/core/gdbstub/hio.h @@ -6,6 +6,10 @@ #include "common/common_types.h" +namespace Core { +class System; +} + namespace GDBStub { /** @@ -47,7 +51,7 @@ static_assert(sizeof(PackedGdbHioRequest) == 152, * * @param address The memory address of the \ref PackedGdbHioRequest. */ -void SetHioRequest(const VAddr address); +void SetHioRequest(Core::System& system, const VAddr address); /** * If there is a pending HIO request, send it to the client. @@ -59,6 +63,6 @@ bool HandlePendingHioRequestPacket(); /** * Process an HIO reply from the client. */ -void HandleHioReply(const u8* const command_buffer, const u32 command_length); +void HandleHioReply(Core::System& system, const u8* const command_buffer, const u32 command_length); } // namespace GDBStub diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index ad5e6ee210..62b993f339 100644 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp @@ -4,6 +4,7 @@ #include #include +#include "common/archives.h" #include "common/assert.h" #include "common/common_types.h" #include "core/core.h" diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 61612d617c..d5cbf0fd66 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -25,13 +25,13 @@ namespace Kernel { KernelSystem::KernelSystem(Memory::MemorySystem& memory, Core::Timing& timing, std::function prepare_reschedule_callback, MemoryMode memory_mode, u32 num_cores, - const New3dsHwCapabilities& n3ds_hw_caps) + const New3dsHwCapabilities& n3ds_hw_caps, u64 override_init_time) : memory(memory), timing(timing), prepare_reschedule_callback(std::move(prepare_reschedule_callback)), memory_mode(memory_mode), n3ds_hw_caps(n3ds_hw_caps) { std::generate(memory_regions.begin(), memory_regions.end(), [] { return std::make_shared(); }); - MemoryInit(memory_mode, n3ds_hw_caps.memory_mode); + MemoryInit(memory_mode, n3ds_hw_caps.memory_mode, override_init_time); resource_limits = std::make_unique(*this); for (u32 core_id = 0; core_id < num_cores; ++core_id) { diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 69d267de0f..c381e97cb3 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -134,7 +134,8 @@ class KernelSystem { public: explicit KernelSystem(Memory::MemorySystem& memory, Core::Timing& timing, std::function prepare_reschedule_callback, MemoryMode memory_mode, - u32 num_cores, const New3dsHwCapabilities& n3ds_hw_caps); + u32 num_cores, const New3dsHwCapabilities& n3ds_hw_caps, + u64 override_init_time = 0); ~KernelSystem(); using PortPair = std::pair, std::shared_ptr>; @@ -330,7 +331,7 @@ public: Core::Timing& timing; private: - void MemoryInit(MemoryMode memory_mode, New3dsMemoryMode n3ds_mode); + void MemoryInit(MemoryMode memory_mode, New3dsMemoryMode n3ds_mode, u64 override_init_time); std::function prepare_reschedule_callback; diff --git a/src/core/hle/kernel/memory.cpp b/src/core/hle/kernel/memory.cpp index b77a147201..12fea67a11 100644 --- a/src/core/hle/kernel/memory.cpp +++ b/src/core/hle/kernel/memory.cpp @@ -37,7 +37,8 @@ static const u32 memory_region_sizes[8][3] = { {0x0B200000, 0x02E00000, 0x02000000}, // 7 }; -void KernelSystem::MemoryInit(MemoryMode memory_mode, New3dsMemoryMode n3ds_mode) { +void KernelSystem::MemoryInit(MemoryMode memory_mode, New3dsMemoryMode n3ds_mode, + u64 override_init_time) { const bool is_new_3ds = Settings::values.is_new_3ds.GetValue(); u32 mem_type_index = static_cast(memory_mode); u32 reported_mem_type = static_cast(memory_mode); @@ -73,7 +74,7 @@ void KernelSystem::MemoryInit(MemoryMode memory_mode, New3dsMemoryMode n3ds_mode config_mem.sys_mem_alloc = memory_regions[1]->size; config_mem.base_mem_alloc = memory_regions[2]->size; - shared_page_handler = std::make_shared(timing); + shared_page_handler = std::make_shared(timing, override_init_time); } std::shared_ptr KernelSystem::GetMemoryRegion(MemoryRegion region) { diff --git a/src/core/hle/kernel/shared_page.cpp b/src/core/hle/kernel/shared_page.cpp index 249c912d96..3583974862 100644 --- a/src/core/hle/kernel/shared_page.cpp +++ b/src/core/hle/kernel/shared_page.cpp @@ -19,7 +19,8 @@ namespace boost::serialization { template void load_construct_data(Archive& ar, SharedPage::Handler* t, const unsigned int) { - ::new (t) SharedPage::Handler(Core::System::GetInstance().CoreTiming()); + ::new (t) SharedPage::Handler(Core::System::GetInstance().CoreTiming(), + Core::System::GetInstance().Movie().GetOverrideInitTime()); } template void load_construct_data(iarchive& ar, SharedPage::Handler* t, const unsigned int); @@ -28,8 +29,7 @@ template void load_construct_data(iarchive& ar, SharedPage::Handler* t namespace SharedPage { -static std::chrono::seconds GetInitTime() { - const u64 override_init_time = Core::Movie::GetInstance().GetOverrideInitTime(); +static std::chrono::seconds GetInitTime(u64 override_init_time) { if (override_init_time != 0) { // Override the clock init time with the one in the movie return std::chrono::seconds(override_init_time); @@ -62,7 +62,7 @@ static std::chrono::seconds GetInitTime() { } } -Handler::Handler(Core::Timing& timing) : timing(timing) { +Handler::Handler(Core::Timing& timing, u64 override_init_time) : timing(timing) { std::memset(&shared_page, 0, sizeof(shared_page)); shared_page.running_hw = 0x1; // product @@ -76,7 +76,7 @@ Handler::Handler(Core::Timing& timing) : timing(timing) { shared_page.battery_state.is_adapter_connected.Assign(1); shared_page.battery_state.is_charging.Assign(1); - init_time = GetInitTime(); + init_time = GetInitTime(override_init_time); using namespace std::placeholders; update_time_event = timing.RegisterEvent("SharedPage::UpdateTimeCallback", diff --git a/src/core/hle/kernel/shared_page.h b/src/core/hle/kernel/shared_page.h index ef7b86e5ce..eebda76178 100644 --- a/src/core/hle/kernel/shared_page.h +++ b/src/core/hle/kernel/shared_page.h @@ -86,7 +86,7 @@ static_assert(sizeof(SharedPageDef) == Memory::SHARED_PAGE_SIZE, class Handler : public BackingMem { public: - Handler(Core::Timing& timing); + Handler(Core::Timing& timing, u64 override_init_time); void SetMacAddress(const MacAddress&); diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 385e64aac7..c66e5cb033 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -5,6 +5,7 @@ #include #include #include +#include "common/archives.h" #include "common/logging/log.h" #include "common/microprofile.h" #include "common/scm_rev.h" @@ -38,7 +39,6 @@ #include "core/hle/lock.h" #include "core/hle/result.h" #include "core/hle/service/plgldr/plgldr.h" -#include "core/hle/service/service.h" namespace Kernel { @@ -1128,7 +1128,7 @@ void SVC::OutputDebugString(VAddr address, s32 len) { } if (len == 0) { - GDBStub::SetHioRequest(address); + GDBStub::SetHioRequest(system, address); return; } @@ -1953,12 +1953,12 @@ ResultCode SVC::GetProcessList(s32* process_count, VAddr out_process_array, } ResultCode SVC::InvalidateInstructionCacheRange(u32 addr, u32 size) { - Core::GetRunningCore().InvalidateCacheRange(addr, size); + system.GetRunningCore().InvalidateCacheRange(addr, size); return RESULT_SUCCESS; } ResultCode SVC::InvalidateEntireInstructionCache() { - Core::GetRunningCore().ClearInstructionCache(); + system.GetRunningCore().ClearInstructionCache(); return RESULT_SUCCESS; } diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h index ea0e14b2e0..d520eeccbd 100644 --- a/src/core/hle/kernel/vm_manager.h +++ b/src/core/hle/kernel/vm_manager.h @@ -13,6 +13,7 @@ #include #include "common/common_types.h" #include "common/memory_ref.h" +#include "core/hle/kernel/memory.h" #include "core/hle/result.h" #include "core/memory.h" #include "core/mmio.h" diff --git a/src/core/hle/service/act/act.cpp b/src/core/hle/service/act/act.cpp index cd9de87fe6..96c62404d7 100644 --- a/src/core/hle/service/act/act.cpp +++ b/src/core/hle/service/act/act.cpp @@ -4,6 +4,7 @@ #include "core/core.h" #include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/shared_memory.h" #include "core/hle/service/act/act.h" #include "core/hle/service/act/act_a.h" #include "core/hle/service/act/act_u.h" diff --git a/src/core/hle/service/apt/applet_manager.cpp b/src/core/hle/service/apt/applet_manager.cpp index 68f3026eeb..36f4012074 100644 --- a/src/core/hle/service/apt/applet_manager.cpp +++ b/src/core/hle/service/apt/applet_manager.cpp @@ -2,6 +2,8 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/archives.h" +#include "common/file_util.h" #include "common/settings.h" #include "core/core.h" #include "core/frontend/input.h" diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp index 998ba00d9e..88d9e29538 100644 --- a/src/core/hle/service/apt/apt.cpp +++ b/src/core/hle/service/apt/apt.cpp @@ -33,6 +33,8 @@ #include "core/hle/service/service.h" #include "core/hw/aes/ccm.h" #include "core/hw/aes/key.h" +#include "core/loader/loader.h" +#include "core/telemetry_session.h" SERVICE_CONSTRUCT_IMPL(Service::APT::Module) diff --git a/src/core/hle/service/frd/frd.cpp b/src/core/hle/service/frd/frd.cpp index d291148a5a..242e7254a1 100644 --- a/src/core/hle/service/frd/frd.cpp +++ b/src/core/hle/service/frd/frd.cpp @@ -6,9 +6,9 @@ #include #include #include +#include "common/archives.h" #include "common/assert.h" #include "common/logging/log.h" -#include "common/string_util.h" #include "core/core.h" #include "core/hle/ipc_helpers.h" #include "core/hle/result.h" diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 2b81f47fb5..cc4a33b467 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -150,7 +150,7 @@ void Module::UpdatePadCallback(std::uintptr_t user_data, s64 cycles_late) { circle_pad_old_y.erase(circle_pad_old_y.begin()); circle_pad_old_y.push_back(circle_pad_new_y); - Core::Movie::GetInstance().HandlePadAndCircleStatus(state, circle_pad_x, circle_pad_y); + system.Movie().HandlePadAndCircleStatus(state, circle_pad_x, circle_pad_y); const DirectionState direction = GetStickDirectionState(circle_pad_x, circle_pad_y); state.circle_up.Assign(direction.up); @@ -200,7 +200,7 @@ void Module::UpdatePadCallback(std::uintptr_t user_data, s64 cycles_late) { touch_entry.y = static_cast(y * Core::kScreenBottomHeight); touch_entry.valid.Assign(pressed ? 1 : 0); - Core::Movie::GetInstance().HandleTouchStatus(touch_entry); + system.Movie().HandleTouchStatus(touch_entry); // TODO(bunnei): We're not doing anything with offset 0xA8 + 0x18 of HID SharedMemory, which // supposedly is "Touch-screen entry, which contains the raw coordinate data prior to being @@ -246,7 +246,7 @@ void Module::UpdateAccelerometerCallback(std::uintptr_t user_data, s64 cycles_la accelerometer_entry.y = static_cast(accel.y); accelerometer_entry.z = static_cast(accel.z); - Core::Movie::GetInstance().HandleAccelerometerStatus(accelerometer_entry); + system.Movie().HandleAccelerometerStatus(accelerometer_entry); // Make up "raw" entry // TODO(wwylele): @@ -287,7 +287,7 @@ void Module::UpdateGyroscopeCallback(std::uintptr_t user_data, s64 cycles_late) gyroscope_entry.y = static_cast(gyro.y); gyroscope_entry.z = static_cast(gyro.z); - Core::Movie::GetInstance().HandleGyroscopeStatus(gyroscope_entry); + system.Movie().HandleGyroscopeStatus(gyroscope_entry); // Make up "raw" entry mem->gyroscope.raw_entry.x = gyroscope_entry.x; diff --git a/src/core/hle/service/http/http_c.h b/src/core/hle/service/http/http_c.h index d78e624e12..0e08aa7dff 100644 --- a/src/core/hle/service/http/http_c.h +++ b/src/core/hle/service/http/http_c.h @@ -31,6 +31,10 @@ namespace Core { class System; } +namespace IPC { +class RequestParser; +} + namespace Service::HTTP { enum class RequestMethod : u8 { @@ -362,6 +366,37 @@ private: void SetProxyDefault(Kernel::HLERequestContext& ctx); + /** + * HTTP_C::ReceiveData service function + * Inputs: + * 1 : Context handle + * 2 : Buffer size + * 3 : (OutSize<<4) | 12 + * 4 : Output data pointer + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ + void ReceiveData(Kernel::HLERequestContext& ctx); + + /** + * HTTP_C::ReceiveDataTimeout service function + * Inputs: + * 1 : Context handle + * 2 : Buffer size + * 3-4 : u64 nanoseconds delay + * 5 : (OutSize<<4) | 12 + * 6 : Output data pointer + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ + void ReceiveDataTimeout(Kernel::HLERequestContext& ctx); + + /** + * ReceiveDataImpl: + * Implements ReceiveData and ReceiveDataTimeout service functions + */ + void ReceiveDataImpl(Kernel::HLERequestContext& ctx, bool timeout); + /** * HTTP_C::AddRequestHeader service function * Inputs: diff --git a/src/core/hle/service/ir/extra_hid.cpp b/src/core/hle/service/ir/extra_hid.cpp index cddcd2edb1..08af23c42e 100644 --- a/src/core/hle/service/ir/extra_hid.cpp +++ b/src/core/hle/service/ir/extra_hid.cpp @@ -5,8 +5,6 @@ #include #include "common/alignment.h" #include "common/settings.h" -#include "common/string_util.h" -#include "core/core.h" #include "core/core_timing.h" #include "core/hle/service/ir/extra_hid.h" #include "core/movie.h" @@ -65,7 +63,8 @@ enum class ResponseID : u8 { ReadCalibrationData = 0x11, }; -ExtraHID::ExtraHID(SendFunc send_func, Core::Timing& timing) : IRDevice(send_func), timing(timing) { +ExtraHID::ExtraHID(SendFunc send_func, Core::Timing& timing_, Core::Movie& movie_) + : IRDevice(send_func), timing{timing_}, movie{movie_} { LoadInputDevices(); // The data below was retrieved from a New 3DS @@ -249,7 +248,7 @@ void ExtraHID::SendHIDStatus() { response.buttons.r_not_held.Assign(1); response.unknown = 0; - Core::Movie::GetInstance().HandleExtraHidResponse(response); + movie.HandleExtraHidResponse(response); std::vector response_buffer(sizeof(response)); memcpy(response_buffer.data(), &response, sizeof(response)); diff --git a/src/core/hle/service/ir/extra_hid.h b/src/core/hle/service/ir/extra_hid.h index 02974517d0..5dc36b4cd8 100644 --- a/src/core/hle/service/ir/extra_hid.h +++ b/src/core/hle/service/ir/extra_hid.h @@ -16,6 +16,7 @@ namespace Core { struct TimingEventType; class Timing; +class Movie; } // namespace Core namespace Service::IR { @@ -43,7 +44,7 @@ static_assert(sizeof(ExtraHIDResponse) == 6, "HID status response has wrong size */ class ExtraHID final : public IRDevice { public: - explicit ExtraHID(SendFunc send_func, Core::Timing& timing); + explicit ExtraHID(SendFunc send_func, Core::Timing& timing, Core::Movie& movie); ~ExtraHID(); void OnConnect() override; @@ -60,6 +61,7 @@ private: void LoadInputDevices(); Core::Timing& timing; + Core::Movie& movie; u8 hid_period; Core::TimingEventType* hid_polling_callback_id; std::array calibration_data; diff --git a/src/core/hle/service/ir/ir_rst.cpp b/src/core/hle/service/ir/ir_rst.cpp index ad219302ad..28aba87c71 100644 --- a/src/core/hle/service/ir/ir_rst.cpp +++ b/src/core/hle/service/ir/ir_rst.cpp @@ -83,7 +83,7 @@ void IR_RST::UpdateCallback(std::uintptr_t user_data, s64 cycles_late) { s16 c_stick_x = static_cast(c_stick_x_f * MAX_CSTICK_RADIUS); s16 c_stick_y = static_cast(c_stick_y_f * MAX_CSTICK_RADIUS); - Core::Movie::GetInstance().HandleIrRst(state, c_stick_x, c_stick_y); + system.Movie().HandleIrRst(state, c_stick_x, c_stick_y); if (!raw_c_stick) { const HID::DirectionState direction = HID::GetStickDirectionState(c_stick_x, c_stick_y); diff --git a/src/core/hle/service/ir/ir_user.cpp b/src/core/hle/service/ir/ir_user.cpp index e66ca47844..856978a1d5 100644 --- a/src/core/hle/service/ir/ir_user.cpp +++ b/src/core/hle/service/ir/ir_user.cpp @@ -9,7 +9,7 @@ #include #include #include -#include "common/string_util.h" +#include "common/archives.h" #include "common/swap.h" #include "core/core.h" #include "core/hle/ipc_helpers.h" @@ -467,7 +467,7 @@ IR_USER::IR_USER(Core::System& system) : ServiceFramework("ir:USER", 1) { receive_event = system.Kernel().CreateEvent(ResetType::OneShot, "IR:ReceiveEvent"); extra_hid = std::make_unique([this](std::span data) { PutToReceive(data); }, - system.CoreTiming()); + system.CoreTiming(), system.Movie()); } IR_USER::~IR_USER() { diff --git a/src/core/hle/service/nfc/nfc_device.cpp b/src/core/hle/service/nfc/nfc_device.cpp index 5fb98cfcc3..18ac135cc2 100644 --- a/src/core/hle/service/nfc/nfc_device.cpp +++ b/src/core/hle/service/nfc/nfc_device.cpp @@ -7,13 +7,14 @@ #include #include +#include "common/file_util.h" #include "common/logging/log.h" -#include "common/string_util.h" #include "core/core.h" #include "core/hle/kernel/shared_page.h" #include "core/hle/service/nfc/amiibo_crypto.h" #include "core/hle/service/nfc/nfc_device.h" #include "core/hw/aes/key.h" +#include "core/loader/loader.h" SERVICE_CONSTRUCT_IMPL(Service::NFC::NfcDevice) diff --git a/src/core/hle/service/nim/nim_u.cpp b/src/core/hle/service/nim/nim_u.cpp index 153e866430..1462c0b029 100644 --- a/src/core/hle/service/nim/nim_u.cpp +++ b/src/core/hle/service/nim/nim_u.cpp @@ -6,6 +6,7 @@ #include "common/string_util.h" #include "core/core.h" #include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/event.h" #include "core/hle/service/nim/nim_u.h" SERVICE_CONSTRUCT_IMPL(Service::NIM::NIM_U) diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index aa7f0f5bd3..668b6cbb5c 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -53,6 +53,7 @@ #include "core/hle/service/sm/srv.h" #include "core/hle/service/soc/soc_u.h" #include "core/hle/service/ssl/ssl_c.h" +#include "core/loader/loader.h" namespace Service { diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h index 731db3808e..f21c11fa62 100644 --- a/src/core/loader/loader.h +++ b/src/core/loader/loader.h @@ -14,7 +14,6 @@ #include "common/file_util.h" #include "core/file_sys/romfs_reader.h" #include "core/hle/kernel/kernel.h" -#include "core/hle/kernel/object.h" namespace Kernel { struct AddressMapping; diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp index e53d9cf6a2..22bb2989ec 100644 --- a/src/core/loader/ncch.cpp +++ b/src/core/loader/ncch.cpp @@ -27,6 +27,7 @@ #include "core/loader/smdh.h" #include "core/memory.h" #include "core/system_titles.h" +#include "core/telemetry_session.h" #include "network/network.h" namespace Loader { diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 3bef55322b..dd71cb7f71 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -17,9 +17,7 @@ #include "core/arm/arm_interface.h" #include "core/core.h" #include "core/global.h" -#include "core/hle/kernel/memory.h" #include "core/hle/kernel/process.h" -#include "core/hle/lock.h" #include "core/hle/service/plgldr/plgldr.h" #include "core/hw/hw.h" #include "core/memory.h" @@ -89,12 +87,13 @@ private: class MemorySystem::Impl { public: - // Visual Studio would try to allocate these on compile time if they are std::array, which would - // exceed the memory limit. + // Visual Studio would try to allocate these on compile time + // if they are std::array which would exceed the memory limit. std::unique_ptr fcram = std::make_unique(Memory::FCRAM_N3DS_SIZE); std::unique_ptr vram = std::make_unique(Memory::VRAM_SIZE); std::unique_ptr n3ds_extra_ram = std::make_unique(Memory::N3DS_EXTRA_RAM_SIZE); + Core::System& system; std::shared_ptr current_page_table = nullptr; RasterizerCacheMarker cache_marker; std::vector> page_table_list; @@ -106,7 +105,7 @@ public: std::shared_ptr n3ds_extra_ram_mem; std::shared_ptr dsp_mem; - Impl(); + Impl(Core::System& system_); const u8* GetPtr(Region r) const { switch (r) { @@ -153,6 +152,10 @@ public: } } + u32 GetPC() const noexcept { + return system.GetRunningCore().GetPC(); + } + /** * This function should only be called for virtual addreses with attribute `PageType::Special`. */ @@ -187,7 +190,7 @@ public: HW_Memory, "unmapped ReadBlock @ 0x{:08X} (start address = 0x{:08X}, size = {}) at PC " "0x{:08X}", - current_vaddr, src_addr, size, Core::GetRunningCore().GetPC()); + current_vaddr, src_addr, size, GetPC()); std::memset(dest_buffer, 0, copy_amount); break; } @@ -242,7 +245,7 @@ public: HW_Memory, "unmapped WriteBlock @ 0x{:08X} (start address = 0x{:08X}, size = {}) at PC " "0x{:08X}", - current_vaddr, dest_addr, size, Core::GetRunningCore().GetPC()); + current_vaddr, dest_addr, size, GetPC()); break; } case PageType::Memory: { @@ -345,13 +348,13 @@ private: friend class boost::serialization::access; }; -MemorySystem::Impl::Impl() - : fcram_mem(std::make_shared>(*this)), +MemorySystem::Impl::Impl(Core::System& system_) + : system{system_}, fcram_mem(std::make_shared>(*this)), vram_mem(std::make_shared>(*this)), n3ds_extra_ram_mem(std::make_shared>(*this)), dsp_mem(std::make_shared>(*this)) {} -MemorySystem::MemorySystem() : impl(std::make_unique()) {} +MemorySystem::MemorySystem(Core::System& system) : impl(std::make_unique(system)) {} MemorySystem::~MemorySystem() = default; template @@ -467,7 +470,7 @@ T MemorySystem::Read(const VAddr vaddr) { switch (type) { case PageType::Unmapped: LOG_ERROR(HW_Memory, "unmapped Read{} @ 0x{:08X} at PC 0x{:08X}", sizeof(T) * 8, vaddr, - Core::GetRunningCore().GetPC()); + impl->GetPC()); return 0; case PageType::Memory: ASSERT_MSG(false, "Mapped memory page without a pointer @ {:08X}", vaddr); @@ -518,7 +521,7 @@ void MemorySystem::Write(const VAddr vaddr, const T data) { switch (type) { case PageType::Unmapped: LOG_ERROR(HW_Memory, "unmapped Write{} 0x{:08X} @ 0x{:08X} at PC 0x{:08X}", - sizeof(data) * 8, (u32)data, vaddr, Core::GetRunningCore().GetPC()); + sizeof(data) * 8, (u32)data, vaddr, impl->GetPC()); return; case PageType::Memory: ASSERT_MSG(false, "Mapped memory page without a pointer @ {:08X}", vaddr); @@ -550,7 +553,7 @@ bool MemorySystem::WriteExclusive(const VAddr vaddr, const T data, const T expec switch (type) { case PageType::Unmapped: LOG_ERROR(HW_Memory, "unmapped Write{} 0x{:08X} @ 0x{:08X} at PC 0x{:08X}", - sizeof(data) * 8, (u32)data, vaddr, Core::GetRunningCore().GetPC()); + sizeof(data) * 8, static_cast(data), vaddr, impl->GetPC()); return true; case PageType::Memory: ASSERT_MSG(false, "Mapped memory page without a pointer @ {:08X}", vaddr); @@ -606,8 +609,7 @@ u8* MemorySystem::GetPointer(const VAddr vaddr) { return GetPointerForRasterizerCache(vaddr); } - LOG_ERROR(HW_Memory, "unknown GetPointer @ 0x{:08x} at PC 0x{:08X}", vaddr, - Core::GetRunningCore().GetPC()); + LOG_ERROR(HW_Memory, "unknown GetPointer @ 0x{:08x} at PC 0x{:08X}", vaddr, impl->GetPC()); return nullptr; } @@ -663,7 +665,7 @@ MemoryRef MemorySystem::GetPhysicalRef(PAddr address) const { if (area == memory_areas.end()) { LOG_ERROR(HW_Memory, "Unknown GetPhysicalPointer @ {:#08X} at PC {:#08X}", address, - Core::GetRunningCore().GetPC()); + impl->GetPC()); return nullptr; } @@ -693,8 +695,7 @@ MemoryRef MemorySystem::GetPhysicalRef(PAddr address) const { return {target_mem, offset_into_region}; } -/// For a rasterizer-accessible PAddr, gets a list of all possible VAddr -static std::vector PhysicalToVirtualAddressForRasterizer(PAddr addr) { +std::vector MemorySystem::PhysicalToVirtualAddressForRasterizer(PAddr addr) { if (addr >= VRAM_PADDR && addr < VRAM_PADDR_END) { return {addr - VRAM_PADDR + VRAM_VADDR}; } @@ -714,7 +715,7 @@ static std::vector PhysicalToVirtualAddressForRasterizer(PAddr addr) { // parts of the texture. LOG_ERROR(HW_Memory, "Trying to use invalid physical address for rasterizer: {:08X} at PC 0x{:08X}", addr, - Core::GetRunningCore().GetPC()); + impl->GetPC()); return {}; } @@ -729,7 +730,7 @@ void MemorySystem::RasterizerMarkRegionCached(PAddr start, u32 size, bool cached for (unsigned i = 0; i < num_pages; ++i, paddr += CITRA_PAGE_SIZE) { for (VAddr vaddr : PhysicalToVirtualAddressForRasterizer(paddr)) { impl->cache_marker.Mark(vaddr, cached); - for (auto page_table : impl->page_table_list) { + for (auto& page_table : impl->page_table_list) { PageType& page_type = page_table->attributes[vaddr >> CITRA_PAGE_BITS]; if (cached) { @@ -868,7 +869,7 @@ void MemorySystem::ReadBlock(const Kernel::Process& process, const VAddr src_add } void MemorySystem::ReadBlock(VAddr src_addr, void* dest_buffer, std::size_t size) { - const auto& process = *Core::System::GetInstance().Kernel().GetCurrentProcess(); + const auto& process = *impl->system.Kernel().GetCurrentProcess(); return impl->ReadBlockImpl(process, src_addr, dest_buffer, size); } @@ -911,7 +912,7 @@ void MemorySystem::WriteBlock(const Kernel::Process& process, const VAddr dest_a void MemorySystem::WriteBlock(const VAddr dest_addr, const void* src_buffer, const std::size_t size) { - auto& process = *Core::System::GetInstance().Kernel().GetCurrentProcess(); + auto& process = *impl->system.Kernel().GetCurrentProcess(); return impl->WriteBlockImpl(process, dest_addr, src_buffer, size); } @@ -934,7 +935,7 @@ void MemorySystem::ZeroBlock(const Kernel::Process& process, const VAddr dest_ad LOG_ERROR(HW_Memory, "unmapped ZeroBlock @ 0x{:08X} (start address = 0x{:08X}, size = {}) at PC " "0x{:08X}", - current_vaddr, dest_addr, size, Core::GetRunningCore().GetPC()); + current_vaddr, dest_addr, size, impl->GetPC()); break; } case PageType::Memory: { @@ -989,7 +990,7 @@ void MemorySystem::CopyBlock(const Kernel::Process& dest_process, LOG_ERROR(HW_Memory, "unmapped CopyBlock @ 0x{:08X} (start address = 0x{:08X}, size = {}) at PC " "0x{:08X}", - current_vaddr, src_addr, size, Core::GetRunningCore().GetPC()); + current_vaddr, src_addr, size, impl->GetPC()); ZeroBlock(dest_process, dest_addr, copy_amount); break; } diff --git a/src/core/memory.h b/src/core/memory.h index 56ae6f2b8d..69a499434a 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -18,6 +18,10 @@ namespace Kernel { class Process; } +namespace Core { +class System; +} + namespace AudioCore { class DspInterface; } @@ -287,7 +291,7 @@ void RasterizerFlushVirtualRegion(VAddr start, u32 size, FlushMode mode); class MemorySystem { public: - MemorySystem(); + explicit MemorySystem(Core::System& system); ~MemorySystem(); /** @@ -575,6 +579,9 @@ public: */ void RasterizerMarkRegionCached(PAddr start, u32 size, bool cached); + /// For a rasterizer-accessible PAddr, gets a list of all possible VAddr + std::vector PhysicalToVirtualAddressForRasterizer(PAddr addr); + /// Gets a pointer to the memory region beginning at the specified physical address. u8* GetPhysicalPointer(PAddr address) const; @@ -627,6 +634,7 @@ private: void MapPages(PageTable& page_table, u32 base, u32 size, MemoryRef memory, PageType type); +private: class Impl; std::unique_ptr impl; diff --git a/src/core/movie.cpp b/src/core/movie.cpp index 0453dbec74..376f8521d2 100644 --- a/src/core/movie.cpp +++ b/src/core/movie.cpp @@ -10,13 +10,11 @@ #include #include #include -#include +#include "common/archives.h" #include "common/bit_field.h" -#include "common/common_types.h" #include "common/file_util.h" #include "common/logging/log.h" #include "common/scm_rev.h" -#include "common/string_util.h" #include "common/swap.h" #include "common/timer.h" #include "core/core.h" @@ -24,12 +22,11 @@ #include "core/hle/service/ir/extra_hid.h" #include "core/hle/service/ir/ir_rst.h" #include "core/hw/gpu.h" +#include "core/loader/loader.h" #include "core/movie.h" namespace Core { -/*static*/ Movie Movie::s_instance; - enum class ControllerStateType : u8 { PadAndCircle, Touch, @@ -146,6 +143,10 @@ static u64 GetInputCount(std::span input) { return input_count; } +Movie::Movie(const Core::System& system_) : system{system_} {} + +Movie::~Movie() = default; + template void Movie::serialize(Archive& ar, const unsigned int file_version) { // Only serialize what's needed to make savestates useful for TAS: @@ -565,7 +566,7 @@ void Movie::StartRecording(const std::string& movie_file, const std::string& aut // Get program ID program_id = 0; - Core::System::GetInstance().GetAppLoader().ReadProgramId(program_id); + system.GetAppLoader().ReadProgramId(program_id); LOG_INFO(Movie, "Enabling Movie recording, ID: {:016X}", id); } diff --git a/src/core/movie.h b/src/core/movie.h index 70b999b036..3e4ff908f1 100644 --- a/src/core/movie.h +++ b/src/core/movie.h @@ -23,25 +23,29 @@ union PadState; } // namespace Service namespace Core { + +class System; struct CTMHeader; struct ControllerState; class Movie { public: - enum class PlayMode { None, Recording, Playing, MovieFinished }; - enum class ValidationResult { + enum class PlayMode : u32 { + None, + Recording, + Playing, + MovieFinished, + }; + + enum class ValidationResult : u32 { OK, RevisionDismatch, InputCountDismatch, Invalid, }; - /** - * Gets the instance of the Movie singleton class. - * @returns Reference to the instance of the Movie singleton class. - */ - static Movie& GetInstance() { - return s_instance; - } + + explicit Movie(const Core::System& system); + ~Movie(); void SetPlaybackCompletionCallback(std::function completion_callback); void StartPlayback(const std::string& movie_file); @@ -133,8 +137,6 @@ public: void SaveMovie(); private: - static Movie s_instance; - void CheckInputEnd(); template @@ -159,6 +161,8 @@ private: ValidationResult ValidateHeader(const CTMHeader& header) const; ValidationResult ValidateInput(std::span input, u64 expected_count) const; +private: + const Core::System& system; PlayMode play_mode; std::string record_movie_file; diff --git a/src/core/savestate.cpp b/src/core/savestate.cpp index ccaa8d4f19..ecf8b9d4dd 100644 --- a/src/core/savestate.cpp +++ b/src/core/savestate.cpp @@ -3,11 +3,14 @@ // Refer to the license.txt file included. #include +#include #include #include #include "common/archives.h" +#include "common/file_util.h" #include "common/logging/log.h" #include "common/scm_rev.h" +#include "common/swap.h" #include "common/zstd_compression.h" #include "core/core.h" #include "core/movie.h" @@ -30,8 +33,7 @@ static_assert(sizeof(CSTHeader) == 256, "CSTHeader should be 256 bytes"); constexpr std::array header_magic_bytes{{'C', 'S', 'T', 0x1B}}; -static std::string GetSaveStatePath(u64 program_id, u32 slot) { - const u64 movie_id = Movie::GetInstance().GetCurrentMovieID(); +static std::string GetSaveStatePath(u64 program_id, u64 movie_id, u32 slot) { if (movie_id) { return fmt::format("{}{:016X}.movie{:016X}.{:02d}.cst", FileUtil::GetUserPath(FileUtil::UserPath::StatesDir), program_id, @@ -43,8 +45,8 @@ static std::string GetSaveStatePath(u64 program_id, u32 slot) { } static bool ValidateSaveState(const CSTHeader& header, SaveStateInfo& info, u64 program_id, - u32 slot) { - const auto path = GetSaveStatePath(program_id, slot); + u64 movie_id, u32 slot) { + const auto path = GetSaveStatePath(program_id, movie_id, slot); if (header.filetype != header_magic_bytes) { LOG_WARNING(Core, "Invalid save state file {}", path); return false; @@ -66,11 +68,11 @@ static bool ValidateSaveState(const CSTHeader& header, SaveStateInfo& info, u64 return true; } -std::vector ListSaveStates(u64 program_id) { +std::vector ListSaveStates(u64 program_id, u64 movie_id) { std::vector result; result.reserve(SaveStateSlotCount); for (u32 slot = 1; slot <= SaveStateSlotCount; ++slot) { - const auto path = GetSaveStatePath(program_id, slot); + const auto path = GetSaveStatePath(program_id, movie_id, slot); if (!FileUtil::Exists(path)) { continue; } @@ -92,7 +94,7 @@ std::vector ListSaveStates(u64 program_id) { LOG_ERROR(Core, "Could not read from file {}", path); continue; } - if (!ValidateSaveState(header, info, program_id, slot)) { + if (!ValidateSaveState(header, info, program_id, movie_id, slot)) { continue; } @@ -111,7 +113,8 @@ void System::SaveState(u32 slot) const { const auto data = std::span{reinterpret_cast(str.data()), str.size()}; auto buffer = Common::Compression::CompressDataZSTDDefault(data); - const auto path = GetSaveStatePath(title_id, slot); + const u64 movie_id = movie.GetCurrentMovieID(); + const auto path = GetSaveStatePath(title_id, movie_id, slot); if (!FileUtil::CreateFullPath(path)) { throw std::runtime_error("Could not create path " + path); } @@ -143,7 +146,8 @@ void System::LoadState(u32 slot) { throw std::runtime_error("Unable to load while connected to multiplayer"); } - const auto path = GetSaveStatePath(title_id, slot); + const u64 movie_id = movie.GetCurrentMovieID(); + const auto path = GetSaveStatePath(title_id, movie_id, slot); std::vector decompressed; { @@ -159,7 +163,7 @@ void System::LoadState(u32 slot) { // validate header SaveStateInfo info; - if (!ValidateSaveState(header, info, title_id, slot)) { + if (!ValidateSaveState(header, info, title_id, movie_id, slot)) { throw std::runtime_error("Invalid savestate"); } diff --git a/src/core/savestate.h b/src/core/savestate.h index 2a1d1db524..84daf47789 100644 --- a/src/core/savestate.h +++ b/src/core/savestate.h @@ -20,6 +20,6 @@ struct SaveStateInfo { constexpr u32 SaveStateSlotCount = 10; // Maximum count of savestate slots -std::vector ListSaveStates(u64 program_id); +std::vector ListSaveStates(u64 program_id, u64 movie_id); } // namespace Core diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp index 88f493196d..968e36eacb 100644 --- a/src/core/telemetry_session.cpp +++ b/src/core/telemetry_session.cpp @@ -11,6 +11,7 @@ #include "common/scm_rev.h" #include "common/settings.h" #include "core/core.h" +#include "core/loader/loader.h" #include "core/telemetry_session.h" #include "network/network_settings.h" diff --git a/src/tests/audio_core/decoder_tests.cpp b/src/tests/audio_core/decoder_tests.cpp index 41817cb7aa..7dd384c439 100644 --- a/src/tests/audio_core/decoder_tests.cpp +++ b/src/tests/audio_core/decoder_tests.cpp @@ -20,7 +20,8 @@ #include "audio_fixures.h" TEST_CASE("DSP HLE Audio Decoder", "[audio_core]") { - Memory::MemorySystem memory; + Core::System system; + Memory::MemorySystem memory{system}; SECTION("decoder should produce correct samples") { auto decoder = #ifdef HAVE_MF diff --git a/src/tests/audio_core/hle/hle.cpp b/src/tests/audio_core/hle/hle.cpp index ebc4cb3614..5bb1c2afd7 100644 --- a/src/tests/audio_core/hle/hle.cpp +++ b/src/tests/audio_core/hle/hle.cpp @@ -7,14 +7,17 @@ #include "audio_core/hle/hle.h" #include "audio_core/lle/lle.h" #include "common/common_paths.h" +#include "common/file_util.h" +#include "core/core.h" #include "core/core_timing.h" #include "core/memory.h" TEST_CASE("DSP LLE vs HLE", "[audio_core][hle]") { - Memory::MemorySystem hle_memory; + Core::System system; + Memory::MemorySystem hle_memory{system}; Core::Timing hle_core_timing(1, 100); - Memory::MemorySystem lle_memory; + Memory::MemorySystem lle_memory{system}; Core::Timing lle_core_timing(1, 100); AudioCore::DspHle hle(hle_memory, hle_core_timing); diff --git a/src/tests/audio_core/lle/lle.cpp b/src/tests/audio_core/lle/lle.cpp index a9526b74ad..8b7d21e90d 100644 --- a/src/tests/audio_core/lle/lle.cpp +++ b/src/tests/audio_core/lle/lle.cpp @@ -6,11 +6,14 @@ #include "audio_core/hle/decoder.h" #include "audio_core/lle/lle.h" #include "common/common_paths.h" +#include "common/file_util.h" +#include "core/core.h" #include "core/core_timing.h" #include "core/memory.h" TEST_CASE("DSP LLE Sanity", "[audio_core][lle]") { - Memory::MemorySystem memory; + Core::System system; + Memory::MemorySystem memory{system}; Core::Timing core_timing(1, 100); AudioCore::DspLle lle(memory, core_timing, true); diff --git a/src/tests/core/arm/arm_test_common.cpp b/src/tests/core/arm/arm_test_common.cpp index 2bbf248601..0a69c01da5 100644 --- a/src/tests/core/arm/arm_test_common.cpp +++ b/src/tests/core/arm/arm_test_common.cpp @@ -16,7 +16,8 @@ TestEnvironment::TestEnvironment(bool mutable_memory_) : mutable_memory(mutable_memory_), test_memory(std::make_shared(this)) { timing = std::make_unique(1, 100); - memory = std::make_unique(); + system = std::make_unique(); + memory = std::make_unique(*system); kernel = std::make_unique( *memory, *timing, [] {}, Kernel::MemoryMode::Prod, 1, Kernel::New3dsHwCapabilities{false, false, Kernel::New3dsMemoryMode::Legacy}); diff --git a/src/tests/core/arm/arm_test_common.h b/src/tests/core/arm/arm_test_common.h index 604880d21f..e99347b7d3 100644 --- a/src/tests/core/arm/arm_test_common.h +++ b/src/tests/core/arm/arm_test_common.h @@ -85,6 +85,7 @@ private: std::vector write_records; std::unique_ptr timing; + std::unique_ptr system; std::unique_ptr memory; std::unique_ptr kernel; }; diff --git a/src/tests/core/hle/kernel/hle_ipc.cpp b/src/tests/core/hle/kernel/hle_ipc.cpp index 0ab4e2ac25..e301fe306f 100644 --- a/src/tests/core/hle/kernel/hle_ipc.cpp +++ b/src/tests/core/hle/kernel/hle_ipc.cpp @@ -3,12 +3,10 @@ // Refer to the license.txt file included. #include -#include "common/archives.h" #include "core/core.h" #include "core/core_timing.h" #include "core/hle/ipc.h" #include "core/hle/kernel/client_port.h" -#include "core/hle/kernel/client_session.h" #include "core/hle/kernel/event.h" #include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/hle_ipc.h" @@ -23,7 +21,8 @@ static std::shared_ptr MakeObject(Kernel::KernelSystem& kernel) { TEST_CASE("HLERequestContext::PopulateFromIncomingCommandBuffer", "[core][kernel]") { Core::Timing timing(1, 100); - Memory::MemorySystem memory; + Core::System system; + Memory::MemorySystem memory{system}; Kernel::KernelSystem kernel( memory, timing, [] {}, Kernel::MemoryMode::Prod, 1, Kernel::New3dsHwCapabilities{false, false, Kernel::New3dsMemoryMode::Legacy}); @@ -247,7 +246,8 @@ TEST_CASE("HLERequestContext::PopulateFromIncomingCommandBuffer", "[core][kernel TEST_CASE("HLERequestContext::WriteToOutgoingCommandBuffer", "[core][kernel]") { Core::Timing timing(1, 100); - Memory::MemorySystem memory; + Core::System system; + Memory::MemorySystem memory{system}; Kernel::KernelSystem kernel( memory, timing, [] {}, Kernel::MemoryMode::Prod, 1, Kernel::New3dsHwCapabilities{false, false, Kernel::New3dsMemoryMode::Legacy}); diff --git a/src/tests/core/memory/memory.cpp b/src/tests/core/memory/memory.cpp index f0384ef294..597ac5132d 100644 --- a/src/tests/core/memory/memory.cpp +++ b/src/tests/core/memory/memory.cpp @@ -3,13 +3,15 @@ // Refer to the license.txt file included. #include +#include "core/core.h" #include "core/core_timing.h" #include "core/hle/kernel/process.h" #include "core/memory.h" TEST_CASE("memory.IsValidVirtualAddress", "[core][memory]") { Core::Timing timing(1, 100); - Memory::MemorySystem memory; + Core::System system; + Memory::MemorySystem memory{system}; Kernel::KernelSystem kernel( memory, timing, [] {}, Kernel::MemoryMode::Prod, 1, Kernel::New3dsHwCapabilities{false, false, Kernel::New3dsMemoryMode::Legacy}); diff --git a/src/tests/core/memory/vm_manager.cpp b/src/tests/core/memory/vm_manager.cpp index 5d1787b05f..38c5c938f9 100644 --- a/src/tests/core/memory/vm_manager.cpp +++ b/src/tests/core/memory/vm_manager.cpp @@ -4,9 +4,9 @@ #include #include +#include "core/core.h" #include "core/core_timing.h" #include "core/hle/kernel/errors.h" -#include "core/hle/kernel/memory.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/vm_manager.h" #include "core/memory.h" @@ -15,7 +15,8 @@ TEST_CASE("Memory Basics", "[kernel][memory]") { auto mem = std::make_shared(Memory::CITRA_PAGE_SIZE); MemoryRef block{mem}; Core::Timing timing(1, 100); - Memory::MemorySystem memory; + Core::System system; + Memory::MemorySystem memory{system}; Kernel::KernelSystem kernel( memory, timing, [] {}, Kernel::MemoryMode::Prod, 1, Kernel::New3dsHwCapabilities{false, false, Kernel::New3dsMemoryMode::Legacy}); diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index b2cc2884fa..824a6b3108 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -34,7 +34,6 @@ add_library(video_core STATIC regs_texturing.h renderer_base.cpp renderer_base.h - rasterizer_cache/framebuffer_base.cpp rasterizer_cache/framebuffer_base.h rasterizer_cache/pixel_format.cpp rasterizer_cache/pixel_format.h @@ -76,6 +75,8 @@ add_library(video_core STATIC renderer_opengl/gl_state.h renderer_opengl/gl_stream_buffer.cpp renderer_opengl/gl_stream_buffer.h + renderer_opengl/gl_texture_mailbox.cpp + renderer_opengl/gl_texture_mailbox.h renderer_opengl/gl_texture_runtime.cpp renderer_opengl/gl_texture_runtime.h renderer_opengl/gl_vars.cpp diff --git a/src/video_core/custom_textures/custom_tex_manager.cpp b/src/video_core/custom_textures/custom_tex_manager.cpp index 8bb2adb42d..b8bb214389 100644 --- a/src/video_core/custom_textures/custom_tex_manager.cpp +++ b/src/video_core/custom_textures/custom_tex_manager.cpp @@ -11,6 +11,8 @@ #include "common/texture.h" #include "core/core.h" #include "core/frontend/image_interface.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/process.h" #include "video_core/custom_textures/custom_tex_manager.h" #include "video_core/rasterizer_cache/surface_params.h" #include "video_core/rasterizer_cache/utils.h" @@ -88,19 +90,12 @@ void CustomTexManager::FindCustomTextures() { CreateWorkers(); } - const u64 program_id = system.Kernel().GetCurrentProcess()->codeset->program_id; - const std::string load_path = - fmt::format("{}textures/{:016X}/", GetUserPath(FileUtil::UserPath::LoadDir), program_id); - - if (!FileUtil::Exists(load_path)) { - FileUtil::CreateFullPath(load_path); + const u64 title_id = system.Kernel().GetCurrentProcess()->codeset->program_id; + const auto textures = GetTextures(title_id); + if (!ReadConfig(title_id)) { + use_new_hash = false; + skip_mipmap = true; } - ReadConfig(load_path); - - FileUtil::FSTEntry texture_dir; - std::vector textures; - FileUtil::ScanDirectoryTree(load_path, texture_dir, 64); - FileUtil::GetAllFilesFromNestedEntries(texture_dir, textures); custom_textures.reserve(textures.size()); for (const FileUtil::FSTEntry& file : textures) { @@ -135,8 +130,8 @@ bool CustomTexManager::ParseFilename(const FileUtil::FSTEntry& file, CustomTextu if (file_format == CustomFileFormat::None) { return false; } - if (file_format == CustomFileFormat::DDS && refuse_dds) { - LOG_ERROR(Render, "Legacy pack is attempting to use DDS textures, skipping!"); + if (file_format == CustomFileFormat::DDS && skip_mipmap) { + LOG_ERROR(Render, "Mipmap skip is incompatible with DDS textures, skipping!"); return false; } texture->file_format = file_format; @@ -174,10 +169,14 @@ bool CustomTexManager::ParseFilename(const FileUtil::FSTEntry& file, CustomTextu return true; } -void CustomTexManager::WriteConfig() { - const u64 program_id = system.Kernel().GetCurrentProcess()->codeset->program_id; +void CustomTexManager::PrepareDumping(u64 title_id) { + // If a pack exists in the load folder that uses the old hash + // dump textures using the old hash. + ReadConfig(title_id, true); + + // Write template config file const std::string dump_path = - fmt::format("{}textures/{:016X}/", GetUserPath(FileUtil::UserPath::DumpDir), program_id); + fmt::format("{}textures/{:016X}/", GetUserPath(FileUtil::UserPath::DumpDir), title_id); const std::string pack_config = dump_path + "pack.json"; if (FileUtil::Exists(pack_config)) { return; @@ -305,18 +304,23 @@ bool CustomTexManager::Decode(Material* material, std::function&& upload return false; } -void CustomTexManager::ReadConfig(const std::string& load_path) { +bool CustomTexManager::ReadConfig(u64 title_id, bool options_only) { + const std::string load_path = + fmt::format("{}textures/{:016X}/", GetUserPath(FileUtil::UserPath::LoadDir), title_id); + if (!FileUtil::Exists(load_path)) { + FileUtil::CreateFullPath(load_path); + } + const std::string config_path = load_path + "pack.json"; FileUtil::IOFile config_file{config_path, "r"}; if (!config_file.IsOpen()) { LOG_INFO(Render, "Unable to find pack config file, using legacy defaults"); - refuse_dds = true; - return; + return false; } std::string config(config_file.GetSize(), '\0'); const std::size_t read_size = config_file.ReadBytes(config.data(), config.size()); if (!read_size) { - return; + return false; } nlohmann::json json = nlohmann::json::parse(config, nullptr, false, true); @@ -325,7 +329,10 @@ void CustomTexManager::ReadConfig(const std::string& load_path) { skip_mipmap = options["skip_mipmap"].get(); flip_png_files = options["flip_png_files"].get(); use_new_hash = options["use_new_hash"].get(); - refuse_dds = skip_mipmap || !use_new_hash; + + if (options_only) { + return true; + } const auto& textures = json["textures"]; for (const auto& material : textures.items()) { @@ -353,6 +360,21 @@ void CustomTexManager::ReadConfig(const std::string& load_path) { LOG_ERROR(Render, "Material with key {} is invalid", material.key()); } } + return true; +} + +std::vector CustomTexManager::GetTextures(u64 title_id) { + const std::string load_path = + fmt::format("{}textures/{:016X}/", GetUserPath(FileUtil::UserPath::LoadDir), title_id); + if (!FileUtil::Exists(load_path)) { + FileUtil::CreateFullPath(load_path); + } + + FileUtil::FSTEntry texture_dir; + std::vector textures; + FileUtil::ScanDirectoryTree(load_path, texture_dir, 64); + FileUtil::GetAllFilesFromNestedEntries(texture_dir, textures); + return textures; } void CustomTexManager::CreateWorkers() { diff --git a/src/video_core/custom_textures/custom_tex_manager.h b/src/video_core/custom_textures/custom_tex_manager.h index 1e7a0d4d8c..ba8a6b5dde 100644 --- a/src/video_core/custom_textures/custom_tex_manager.h +++ b/src/video_core/custom_textures/custom_tex_manager.h @@ -40,8 +40,11 @@ public: /// Searches the load directory assigned to program_id for any custom textures and loads them void FindCustomTextures(); + /// Reads the pack configuration file + bool ReadConfig(u64 title_id, bool options_only = false); + /// Saves the pack configuration file template to the dump directory if it doesn't exist. - void WriteConfig(); + void PrepareDumping(u64 title_id); /// Preloads all registered custom textures void PreloadTextures(const std::atomic_bool& stop_run, @@ -70,8 +73,8 @@ private: /// Parses the custom texture filename (hash, material type, etc). bool ParseFilename(const FileUtil::FSTEntry& file, CustomTexture* texture); - /// Reads the pack configuration file - void ReadConfig(const std::string& load_path); + /// Returns a vector of all custom texture files. + std::vector GetTextures(u64 title_id); /// Creates the thread workers. void CreateWorkers(); @@ -87,10 +90,9 @@ private: std::unique_ptr workers; bool textures_loaded{false}; bool async_custom_loading{true}; - bool skip_mipmap{true}; + bool skip_mipmap{false}; bool flip_png_files{true}; - bool use_new_hash{false}; - bool refuse_dds{false}; + bool use_new_hash{true}; }; } // namespace VideoCore diff --git a/src/video_core/rasterizer_cache/framebuffer_base.cpp b/src/video_core/rasterizer_cache/framebuffer_base.cpp deleted file mode 100644 index 8f7a5f7c28..0000000000 --- a/src/video_core/rasterizer_cache/framebuffer_base.cpp +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2023 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include "video_core/rasterizer_cache/framebuffer_base.h" -#include "video_core/rasterizer_cache/surface_base.h" -#include "video_core/regs.h" - -namespace VideoCore { - -FramebufferBase::FramebufferBase() = default; - -FramebufferBase::FramebufferBase(const Pica::Regs& regs, const SurfaceBase* color, u32 color_level, - const SurfaceBase* depth_stencil, u32 depth_level, - Common::Rectangle surfaces_rect) { - res_scale = color ? color->res_scale : (depth_stencil ? depth_stencil->res_scale : 1u); - - // Determine the draw rectangle (render area + scissor) - const Common::Rectangle viewport_rect = regs.rasterizer.GetViewportRect(); - draw_rect.left = - std::clamp(static_cast(surfaces_rect.left) + viewport_rect.left * res_scale, - surfaces_rect.left, surfaces_rect.right); - draw_rect.top = - std::clamp(static_cast(surfaces_rect.bottom) + viewport_rect.top * res_scale, - surfaces_rect.bottom, surfaces_rect.top); - draw_rect.right = - std::clamp(static_cast(surfaces_rect.left) + viewport_rect.right * res_scale, - surfaces_rect.left, surfaces_rect.right); - draw_rect.bottom = - std::clamp(static_cast(surfaces_rect.bottom) + viewport_rect.bottom * res_scale, - surfaces_rect.bottom, surfaces_rect.top); - - // Update viewport - viewport.x = static_cast(surfaces_rect.left) + viewport_rect.left * res_scale; - viewport.y = static_cast(surfaces_rect.bottom) + viewport_rect.bottom * res_scale; - viewport.width = static_cast(viewport_rect.GetWidth() * res_scale); - viewport.height = static_cast(viewport_rect.GetHeight() * res_scale); - - // Scissor checks are window-, not viewport-relative, which means that if the cached texture - // sub-rect changes, the scissor bounds also need to be updated. - scissor_rect.left = - static_cast(surfaces_rect.left + regs.rasterizer.scissor_test.x1 * res_scale); - scissor_rect.bottom = - static_cast(surfaces_rect.bottom + regs.rasterizer.scissor_test.y1 * res_scale); - - // x2, y2 have +1 added to cover the entire pixel area, otherwise you might get cracks when - // scaling or doing multisampling. - scissor_rect.right = - static_cast(surfaces_rect.left + (regs.rasterizer.scissor_test.x2 + 1) * res_scale); - scissor_rect.top = - static_cast(surfaces_rect.bottom + (regs.rasterizer.scissor_test.y2 + 1) * res_scale); - - // Rendering to mipmaps is something quite rare so log it when it occurs. - if (color_level != 0) { - LOG_WARNING(HW_GPU, "Game is rendering to color mipmap {}", color_level); - } - if (depth_level != 0) { - LOG_WARNING(HW_GPU, "Game is rendering to depth mipmap {}", depth_level); - } - - // Query surface invalidation intervals - const Common::Rectangle draw_rect_unscaled{draw_rect / res_scale}; - if (color) { - color_params = *color; - intervals[0] = color->GetSubRectInterval(draw_rect_unscaled, color_level); - } - if (depth_stencil) { - depth_params = *depth_stencil; - intervals[1] = depth_stencil->GetSubRectInterval(draw_rect_unscaled, depth_level); - } -} - -} // namespace VideoCore diff --git a/src/video_core/rasterizer_cache/framebuffer_base.h b/src/video_core/rasterizer_cache/framebuffer_base.h index 932bf73a47..afd25cc21c 100644 --- a/src/video_core/rasterizer_cache/framebuffer_base.h +++ b/src/video_core/rasterizer_cache/framebuffer_base.h @@ -4,12 +4,11 @@ #pragma once +#include "common/hash.h" #include "common/math_util.h" +#include "video_core/rasterizer_cache/slot_id.h" #include "video_core/rasterizer_cache/surface_params.h" - -namespace Pica { -struct Regs; -} +#include "video_core/regs_rasterizer.h" namespace VideoCore { @@ -22,31 +21,109 @@ struct ViewportInfo { s32 height; }; +struct FramebufferParams { + SurfaceId color_id; + SurfaceId depth_id; + u32 color_level; + u32 depth_level; + bool shadow_rendering; + INSERT_PADDING_BYTES(3); + + bool operator==(const FramebufferParams& params) const noexcept { + return std::memcmp(this, ¶ms, sizeof(FramebufferParams)) == 0; + } + + u64 Hash() const noexcept { + return Common::ComputeHash64(this, sizeof(FramebufferParams)); + } + + u32 Index(VideoCore::SurfaceType type) const noexcept { + switch (type) { + case VideoCore::SurfaceType::Color: + return 0; + case VideoCore::SurfaceType::Depth: + case VideoCore::SurfaceType::DepthStencil: + return 1; + default: + LOG_CRITICAL(HW_GPU, "Unknown surface type in framebuffer"); + return 0; + } + } +}; +static_assert(std::has_unique_object_representations_v, + "FramebufferParams is not suitable for hashing"); + +template +class RasterizerCache; + /** - * A framebuffer is a lightweight abstraction over a pair of surfaces and provides - * metadata about them. + * @brief FramebufferHelper is a RAII wrapper over backend specific framebuffer handle that + * provides the viewport/scissor/draw rectanges and performs automatic rasterizer cache invalidation + * when out of scope. */ -class FramebufferBase { +template +class FramebufferHelper { public: - FramebufferBase(); - FramebufferBase(const Pica::Regs& regs, const SurfaceBase* color, u32 color_level, - const SurfaceBase* depth_stencil, u32 depth_level, - Common::Rectangle surfaces_rect); + explicit FramebufferHelper(RasterizerCache* res_cache_, typename T::Framebuffer* fb_, + const Pica::RasterizerRegs& regs, + Common::Rectangle surfaces_rect) + : res_cache{res_cache_}, fb{fb_} { + const u32 res_scale = fb->Scale(); - SurfaceParams ColorParams() const noexcept { - return color_params; + // Determine the draw rectangle (render area + scissor) + const Common::Rectangle viewport_rect = regs.GetViewportRect(); + draw_rect.left = + std::clamp(static_cast(surfaces_rect.left) + viewport_rect.left * res_scale, + surfaces_rect.left, surfaces_rect.right); + draw_rect.top = + std::clamp(static_cast(surfaces_rect.bottom) + viewport_rect.top * res_scale, + surfaces_rect.bottom, surfaces_rect.top); + draw_rect.right = + std::clamp(static_cast(surfaces_rect.left) + viewport_rect.right * res_scale, + surfaces_rect.left, surfaces_rect.right); + draw_rect.bottom = std::clamp(static_cast(surfaces_rect.bottom) + + viewport_rect.bottom * res_scale, + surfaces_rect.bottom, surfaces_rect.top); + + // Update viewport + viewport.x = static_cast(surfaces_rect.left) + viewport_rect.left * res_scale; + viewport.y = static_cast(surfaces_rect.bottom) + viewport_rect.bottom * res_scale; + viewport.width = static_cast(viewport_rect.GetWidth() * res_scale); + viewport.height = static_cast(viewport_rect.GetHeight() * res_scale); + + // Scissor checks are window-, not viewport-relative, which means that if the cached texture + // sub-rect changes, the scissor bounds also need to be updated. + scissor_rect.left = static_cast(surfaces_rect.left + regs.scissor_test.x1 * res_scale); + scissor_rect.bottom = + static_cast(surfaces_rect.bottom + regs.scissor_test.y1 * res_scale); + + // x2, y2 have +1 added to cover the entire pixel area, otherwise you might get cracks when + // scaling or doing multisampling. + scissor_rect.right = + static_cast(surfaces_rect.left + (regs.scissor_test.x2 + 1) * res_scale); + scissor_rect.top = + static_cast(surfaces_rect.bottom + (regs.scissor_test.y2 + 1) * res_scale); } - SurfaceParams DepthParams() const noexcept { - return depth_params; + ~FramebufferHelper() { + const Common::Rectangle draw_rect_unscaled{draw_rect / fb->Scale()}; + const auto invalidate = [&](SurfaceId surface_id, u32 level) { + const auto& surface = res_cache->GetSurface(surface_id); + const SurfaceInterval interval = surface.GetSubRectInterval(draw_rect_unscaled, level); + const PAddr addr = boost::icl::first(interval); + const u32 size = boost::icl::length(interval); + res_cache->InvalidateRegion(addr, size, surface_id); + }; + if (fb->color_id) { + invalidate(fb->color_id, fb->color_level); + } + if (fb->depth_id) { + invalidate(fb->depth_id, fb->depth_level); + } } - SurfaceInterval Interval(SurfaceType type) const noexcept { - return intervals[Index(type)]; - } - - u32 ResolutionScale() const noexcept { - return res_scale; + typename T::Framebuffer* Framebuffer() const noexcept { + return fb; } Common::Rectangle DrawRect() const noexcept { @@ -61,28 +138,21 @@ public: return viewport; } -protected: - u32 Index(VideoCore::SurfaceType type) const noexcept { - switch (type) { - case VideoCore::SurfaceType::Color: - return 0; - case VideoCore::SurfaceType::Depth: - case VideoCore::SurfaceType::DepthStencil: - return 1; - default: - LOG_CRITICAL(HW_GPU, "Unknown surface type in framebuffer"); - return 0; - } - } - -protected: - SurfaceParams color_params{}; - SurfaceParams depth_params{}; - std::array intervals{}; - Common::Rectangle scissor_rect{}; - Common::Rectangle draw_rect{}; +private: + RasterizerCache* res_cache; + typename T::Framebuffer* fb; + Common::Rectangle scissor_rect; + Common::Rectangle draw_rect; ViewportInfo viewport; - u32 res_scale{1}; }; } // namespace VideoCore + +namespace std { +template <> +struct hash { + std::size_t operator()(const VideoCore::FramebufferParams& params) const noexcept { + return params.Hash(); + } +}; +} // namespace std diff --git a/src/video_core/rasterizer_cache/rasterizer_cache.h b/src/video_core/rasterizer_cache/rasterizer_cache.h index 512f216c01..81e44504e0 100644 --- a/src/video_core/rasterizer_cache/rasterizer_cache.h +++ b/src/video_core/rasterizer_cache/rasterizer_cache.h @@ -37,7 +37,7 @@ RasterizerCache::RasterizerCache(Memory::MemorySystem& memory_, Pica::Regs& regs_, RendererBase& renderer_) : memory{memory_}, custom_tex_manager{custom_tex_manager_}, runtime{runtime_}, regs{regs_}, renderer{renderer_}, resolution_scale_factor{renderer.GetResolutionScaleFactor()}, - use_filter{Settings::values.texture_filter.GetValue() != Settings::TextureFilter::None}, + filter{Settings::values.texture_filter.GetValue()}, dump_textures{Settings::values.dump_textures.GetValue()}, use_custom_textures{Settings::values.custom_textures.GetValue()} { using TextureConfig = Pica::TexturingRegs::TextureConfig; @@ -76,17 +76,21 @@ RasterizerCache::~RasterizerCache() { template void RasterizerCache::TickFrame() { custom_tex_manager.TickFrame(); + RunGarbageCollector(); + + const auto new_filter = Settings::values.texture_filter.GetValue(); + if (filter != new_filter) [[unlikely]] { + filter = new_filter; + UnregisterAll(); + } const u32 scale_factor = renderer.GetResolutionScaleFactor(); const bool resolution_scale_changed = resolution_scale_factor != scale_factor; const bool use_custom_texture_changed = Settings::values.custom_textures.GetValue() != use_custom_textures; - const bool texture_filter_changed = - renderer.Settings().texture_filter_update_requested.exchange(false); - if (resolution_scale_changed || texture_filter_changed || use_custom_texture_changed) { + if (resolution_scale_changed || use_custom_texture_changed) { resolution_scale_factor = scale_factor; - use_filter = Settings::values.texture_filter.GetValue() != Settings::TextureFilter::None; use_custom_textures = Settings::values.custom_textures.GetValue(); if (use_custom_textures) { custom_tex_manager.FindCustomTextures(); @@ -95,6 +99,34 @@ void RasterizerCache::TickFrame() { } } +template +void RasterizerCache::RunGarbageCollector() { + frame_tick++; + for (auto it = sentenced.begin(); it != sentenced.end();) { + const auto [surface_id, tick] = *it; + if (frame_tick - tick <= runtime.RemoveThreshold()) { + it++; + continue; + } + RemoveFramebuffers(surface_id); + slot_surfaces.erase(surface_id); + it = sentenced.erase(it); + } +} + +template +void RasterizerCache::RemoveFramebuffers(SurfaceId surface_id) { + for (auto it = framebuffers.begin(); it != framebuffers.end();) { + const auto& params = it->first; + if (params.color_id == surface_id || params.depth_id == surface_id) { + slot_framebuffers.erase(it->second); + it = framebuffers.erase(it); + } else { + it++; + } + } +} + template bool RasterizerCache::AccelerateTextureCopy(const GPU::Regs::DisplayTransferConfig& config) { const DebugScope scope{runtime, Common::Vec4f{0.f, 0.f, 1.f, 1.f}, @@ -322,29 +354,46 @@ template void RasterizerCache::CopySurface(Surface& src_surface, Surface& dst_surface, SurfaceInterval copy_interval) { MICROPROFILE_SCOPE(RasterizerCache_CopySurface); - const PAddr copy_addr = copy_interval.lower(); const SurfaceParams subrect_params = dst_surface.FromInterval(copy_interval); - const auto dst_rect = dst_surface.GetScaledSubRect(subrect_params); ASSERT(subrect_params.GetInterval() == copy_interval); if (src_surface.type == SurfaceType::Fill) { const TextureClear clear = { .texture_level = dst_surface.LevelOf(copy_addr), - .texture_rect = dst_rect, + .texture_rect = dst_surface.GetScaledSubRect(subrect_params), .value = src_surface.MakeClearValue(copy_addr, dst_surface.pixel_format), }; runtime.ClearTexture(dst_surface, clear); return; } - const TextureBlit blit = { - .src_level = src_surface.LevelOf(copy_addr), - .dst_level = dst_surface.LevelOf(copy_addr), - .src_rect = src_surface.GetScaledSubRect(subrect_params), - .dst_rect = dst_rect, - }; - runtime.BlitTextures(src_surface, dst_surface, blit); + const u32 src_scale = src_surface.res_scale; + const u32 dst_scale = dst_surface.res_scale; + if (src_scale > dst_scale) { + dst_surface.ScaleUp(src_scale); + } + + const auto src_rect = src_surface.GetScaledSubRect(subrect_params); + const auto dst_rect = dst_surface.GetScaledSubRect(subrect_params); + if (src_scale == dst_scale) { + const TextureCopy copy = { + .src_level = src_surface.LevelOf(copy_addr), + .dst_level = dst_surface.LevelOf(copy_addr), + .src_offset = {src_rect.left, src_rect.bottom}, + .dst_offset = {dst_rect.left, dst_rect.bottom}, + .extent = {src_rect.GetWidth(), src_rect.GetHeight()}, + }; + runtime.CopyTextures(src_surface, dst_surface, copy); + } else { + const TextureBlit blit = { + .src_level = src_surface.LevelOf(copy_addr), + .dst_level = dst_surface.LevelOf(copy_addr), + .src_rect = src_rect, + .dst_rect = dst_rect, + }; + runtime.BlitTextures(src_surface, dst_surface, blit); + } } template @@ -361,33 +410,7 @@ SurfaceId RasterizerCache::GetSurface(const SurfaceParams& params, ScaleMatch SurfaceId surface_id = FindMatch(params, match_res_scale); if (!surface_id) { - u16 target_res_scale = params.res_scale; - if (match_res_scale != ScaleMatch::Exact) { - // This surface may have a subrect of another surface with a higher res_scale, find - // it to adjust our params - SurfaceParams find_params = params; - SurfaceId expandable_id = FindMatch(find_params, match_res_scale); - if (expandable_id) { - Surface& expandable = slot_surfaces[expandable_id]; - if (expandable.res_scale > target_res_scale) { - target_res_scale = expandable.res_scale; - } - } - // Keep res_scale when reinterpreting d24s8 -> rgba8 - if (params.pixel_format == PixelFormat::RGBA8) { - find_params.pixel_format = PixelFormat::D24S8; - expandable_id = FindMatch(find_params, match_res_scale); - if (expandable_id) { - Surface& expandable = slot_surfaces[expandable_id]; - if (expandable.res_scale > target_res_scale) { - target_res_scale = expandable.res_scale; - } - } - } - } - SurfaceParams new_params = params; - new_params.res_scale = target_res_scale; - surface_id = CreateSurface(new_params); + surface_id = CreateSurface(params); RegisterSurface(surface_id); } @@ -429,31 +452,6 @@ typename RasterizerCache::SurfaceRect_Tuple RasterizerCache::GetSurfaceSub aligned_params.UpdateParams(); } - // Check for a surface we can expand before creating a new one - if (!surface_id) { - surface_id = FindMatch(aligned_params, match_res_scale); - if (surface_id) { - Surface& surface = slot_surfaces[surface_id]; - aligned_params.width = aligned_params.stride; - aligned_params.UpdateParams(); - - SurfaceParams new_params = surface; - new_params.addr = std::min(aligned_params.addr, surface.addr); - new_params.end = std::max(aligned_params.end, surface.end); - new_params.size = new_params.end - new_params.addr; - new_params.height = - new_params.size / aligned_params.BytesInPixels(aligned_params.stride); - new_params.UpdateParams(); - ASSERT(new_params.size % aligned_params.BytesInPixels(aligned_params.stride) == 0); - - SurfaceId new_surface_id = CreateSurface(new_params); - DuplicateSurface(surface_id, new_surface_id); - UnregisterSurface(surface_id); - RegisterSurface(new_surface_id); - surface_id = new_surface_id; - } - } - // No subrect found - create and return a new surface if (!surface_id) { SurfaceParams new_params = aligned_params; @@ -499,32 +497,33 @@ SurfaceId RasterizerCache::GetTextureSurface(const Pica::Texture::TextureInfo params.levels = max_level + 1; params.is_tiled = true; params.pixel_format = PixelFormatFromTextureFormat(info.format); - params.res_scale = use_filter ? resolution_scale_factor : 1; + params.res_scale = filter != Settings::TextureFilter::None ? resolution_scale_factor : 1; params.UpdateParams(); const u32 min_width = info.width >> max_level; const u32 min_height = info.height >> max_level; if (min_width % 8 != 0 || min_height % 8 != 0) { - if (min_width % 4 == 0 && min_height % 4 == 0) { - const auto [src_surface_id, rect] = GetSurfaceSubRect(params, ScaleMatch::Ignore, true); - Surface& src_surface = slot_surfaces[src_surface_id]; - - params.res_scale = src_surface.res_scale; - SurfaceId tmp_surface_id = CreateSurface(params); - Surface& tmp_surface = slot_surfaces[tmp_surface_id]; - - const TextureBlit blit = { - .src_level = src_surface.LevelOf(params.addr), - .dst_level = 0, - .src_rect = rect, - .dst_rect = tmp_surface.GetScaledRect(), - }; - runtime.BlitTextures(src_surface, tmp_surface, blit); - return tmp_surface_id; + if (min_width % 4 != 0 || min_height % 4 != 0) { + LOG_CRITICAL(HW_GPU, "Texture size ({}x{}) is not multiple of 4", min_width, + min_height); + return NULL_SURFACE_ID; } + const auto [src_surface_id, rect] = GetSurfaceSubRect(params, ScaleMatch::Ignore, true); + Surface& src_surface = slot_surfaces[src_surface_id]; - LOG_CRITICAL(HW_GPU, "Texture size ({}x{}) is not multiple of 4", min_width, min_height); - return NULL_SURFACE_ID; + params.res_scale = src_surface.res_scale; + SurfaceId tmp_surface_id = CreateSurface(params); + Surface& tmp_surface = slot_surfaces[tmp_surface_id]; + sentenced.emplace_back(tmp_surface_id, frame_tick); + + const TextureBlit blit = { + .src_level = src_surface.LevelOf(params.addr), + .dst_level = 0, + .src_rect = rect, + .dst_rect = tmp_surface.GetScaledRect(), + }; + runtime.BlitTextures(src_surface, tmp_surface, blit); + return tmp_surface_id; } if (info.width != (min_width << max_level) || info.height != (min_height << max_level)) { LOG_CRITICAL(HW_GPU, "Texture size ({}x{}) does not support required mipmap level ({})", @@ -552,7 +551,7 @@ typename T::Surface& RasterizerCache::GetTextureCube(const TextureCubeConfig& .height = config.width, .stride = config.width, .levels = config.levels, - .res_scale = use_filter ? resolution_scale_factor : 1, + .res_scale = filter != Settings::TextureFilter::None ? resolution_scale_factor : 1, .texture_type = TextureType::CubeMap, .pixel_format = PixelFormatFromTextureFormat(config.format), .type = SurfaceType::Texture, @@ -609,8 +608,8 @@ typename T::Surface& RasterizerCache::GetTextureCube(const TextureCubeConfig& } template -typename T::Framebuffer RasterizerCache::GetFramebufferSurfaces(bool using_color_fb, - bool using_depth_fb) { +FramebufferHelper RasterizerCache::GetFramebufferSurfaces(bool using_color_fb, + bool using_depth_fb) { const auto& config = regs.framebuffer.framebuffer; const s32 framebuffer_width = config.GetWidth(); @@ -692,35 +691,20 @@ typename T::Framebuffer RasterizerCache::GetFramebufferSurfaces(bool using_co boost::icl::length(depth_vp_interval)); } - render_targets = RenderTargets{ + fb_params = FramebufferParams{ .color_id = color_id, .depth_id = depth_id, + .color_level = color_level, + .depth_level = depth_level, + .shadow_rendering = regs.framebuffer.IsShadowRendering(), }; - return Framebuffer{runtime, color_surface, color_level, depth_surface, - depth_level, regs, fb_rect}; -} + auto [it, new_framebuffer] = framebuffers.try_emplace(fb_params); + if (new_framebuffer) { + it->second = slot_framebuffers.insert(runtime, fb_params, color_surface, depth_surface); + } -template -void RasterizerCache::InvalidateFramebuffer(const Framebuffer& framebuffer) { - const auto invalidate = [&](SurfaceId surface_id) { - if (!surface_id) { - return; - } - Surface& surface = slot_surfaces[surface_id]; - const SurfaceInterval interval = framebuffer.Interval(surface.type); - const PAddr addr = boost::icl::first(interval); - const u32 size = boost::icl::length(interval); - InvalidateRegion(addr, size, surface_id); - }; - const bool has_color = framebuffer.HasAttachment(SurfaceType::Color); - const bool has_depth = framebuffer.HasAttachment(SurfaceType::DepthStencil); - if (has_color) { - invalidate(render_targets.color_id); - } - if (has_depth) { - invalidate(render_targets.depth_id); - } + return FramebufferHelper{this, &slot_framebuffers[it->second], regs.rasterizer, fb_rect}; } template @@ -875,9 +859,6 @@ SurfaceId RasterizerCache::FindMatch(const SurfaceParams& params, ScaleMatch surface.CanReinterpret(params); return std::make_pair(matched, copy_interval); }); - IsMatch_Helper(std::integral_constant{}, [&] { - return std::make_pair(surface.CanExpand(params), surface.GetInterval()); - }); IsMatch_Helper(std::integral_constant{}, [&] { return std::make_pair(surface.CanTexCopy(params), surface.GetInterval()); }); @@ -1013,7 +994,7 @@ void RasterizerCache::UploadSurface(Surface& surface, SurfaceInterval interva runtime.NeedsConversion(surface.pixel_format)); if (dump_textures && False(surface.flags & SurfaceFlagBits::Custom)) { - const u64 hash = Common::ComputeHash64(upload_data.data(), upload_data.size()); + const u64 hash = ComputeHash(load_info, upload_data); const u32 level = surface.LevelOf(load_info.addr); custom_tex_manager.DumpTexture(load_info, level, upload_data, hash); } @@ -1027,6 +1008,20 @@ void RasterizerCache::UploadSurface(Surface& surface, SurfaceInterval interva surface.Upload(upload, staging); } +template +u64 RasterizerCache::ComputeHash(const SurfaceParams& load_info, std::span upload_data) { + if (!custom_tex_manager.UseNewHash()) { + const u32 width = load_info.width; + const u32 height = load_info.height; + const u32 bpp = GetFormatBytesPerPixel(load_info.pixel_format); + auto decoded = std::vector(width * height * bpp); + DecodeTexture(load_info, load_info.addr, load_info.end, upload_data, decoded, false); + return Common::ComputeHash64(decoded.data(), decoded.size()); + } else { + return Common::ComputeHash64(upload_data.data(), upload_data.size()); + } +} + template bool RasterizerCache::UploadCustomSurface(SurfaceId surface_id, SurfaceInterval interval) { MICROPROFILE_SCOPE(RasterizerCache_UploadSurface); @@ -1041,18 +1036,7 @@ bool RasterizerCache::UploadCustomSurface(SurfaceId surface_id, SurfaceInterv } const auto upload_data = source_ptr.GetWriteBytes(load_info.end - load_info.addr); - const u64 hash = [&] { - if (!custom_tex_manager.UseNewHash()) { - const u32 width = load_info.width; - const u32 height = load_info.height; - const u32 bpp = surface.GetInternalBytesPerPixel(); - auto decoded = std::vector(width * height * bpp); - DecodeTexture(load_info, load_info.addr, load_info.end, upload_data, decoded, false); - return Common::ComputeHash64(decoded.data(), decoded.size()); - } else { - return Common::ComputeHash64(upload_data.data(), upload_data.size()); - } - }(); + const u64 hash = ComputeHash(load_info, upload_data); const u32 level = surface.LevelOf(load_info.addr); Material* material = custom_tex_manager.GetMaterial(hash); @@ -1068,14 +1052,13 @@ bool RasterizerCache::UploadCustomSurface(SurfaceId surface_id, SurfaceInterv const auto upload = [this, level, surface_id, material]() -> bool { Surface& surface = slot_surfaces[surface_id]; - if (False(surface.flags & SurfaceFlagBits::Custom)) { - LOG_ERROR(HW_GPU, "Surface is not suitable for custom upload, aborting!"); - return false; - } - if (!surface.IsCustom() && !surface.Swap(material)) { - LOG_ERROR(HW_GPU, "Custom compressed format {} unsupported by host GPU", - material->format); - return false; + ASSERT_MSG(True(surface.flags & SurfaceFlagBits::Custom), + "Surface is not suitable for custom upload, aborting!"); + if (!surface.IsCustom()) { + const SurfaceBase old_surface{surface}; + const SurfaceId old_id = + slot_surfaces.swap_and_insert(surface_id, runtime, old_surface, material); + sentenced.emplace_back(old_id, frame_tick); } surface.UploadCustom(material, level); if (custom_tex_manager.SkipMipmaps()) { @@ -1159,6 +1142,10 @@ bool RasterizerCache::ValidateByReinterpretation(Surface& surface, SurfacePar if (boost::icl::is_empty(copy_interval & interval)) { return false; } + const u32 res_scale = src_surface.res_scale; + if (res_scale > surface.res_scale) { + surface.ScaleUp(res_scale); + } const PAddr addr = boost::icl::lower(interval); const SurfaceParams copy_params = surface.FromInterval(copy_interval); const TextureBlit reinterpret = { @@ -1229,25 +1216,24 @@ void RasterizerCache::FlushRegion(PAddr addr, u32 size, SurfaceId flush_surfa SurfaceRegions flushed_intervals; for (const auto& [region, surface_id] : RangeFromInterval(dirty_regions, flush_interval)) { - // Small sizes imply that this most likely comes from the cpu, flush the entire region - // the point is to avoid thousands of small writes every frame if the cpu decides to - // access that region, anything higher than 8 you're guaranteed it comes from a service - auto interval = size <= 8 ? region : region & flush_interval; if (flush_surface_id && surface_id != flush_surface_id) { continue; } + // Small sizes imply that this most likely comes from the cpu, flush the entire region + // the point is to avoid thousands of small writes every frame if the cpu decides to + // access that region, anything higher than 8 you're guaranteed it comes from a service + const auto interval = size <= 8 ? region : region & flush_interval; + Surface& surface = slot_surfaces[surface_id]; + ASSERT_MSG(surface.IsRegionValid(interval), "Region owner has invalid regions"); + const DebugScope scope{runtime, Common::Vec4f{0.f, 0.f, 0.f, 1.f}, "RasterizerCache::FlushRegion (from {:#x} to {:#x})", interval.lower(), interval.upper()}; - // Sanity check, this surface is the last one that marked this region dirty - Surface& surface = slot_surfaces[surface_id]; - ASSERT(surface.IsRegionValid(interval)); - - if (surface.type == SurfaceType::Fill) { + SCOPE_EXIT({ flushed_intervals += interval; }); + if (surface.IsFill()) { DownloadFillSurface(surface, interval); - flushed_intervals += interval; continue; } @@ -1261,8 +1247,6 @@ void RasterizerCache::FlushRegion(PAddr addr, u32 size, SurfaceId flush_surfa } DownloadSurface(surface, download_interval); } - - flushed_intervals += interval; } // Reset dirty regions @@ -1294,7 +1278,6 @@ void RasterizerCache::InvalidateRegion(PAddr addr, u32 size, SurfaceId region if (surface_id == region_owner_id) { return; } - // If the CPU is invalidating this region we want to remove it // to (likely) mark the memory pages as uncached if (!region_owner_id && size <= 8) { @@ -1302,14 +1285,12 @@ void RasterizerCache::InvalidateRegion(PAddr addr, u32 size, SurfaceId region remove_surfaces.push_back(surface_id); return; } - - surface.MarkInvalid(surface.GetInterval() & invalid_interval); - - // If the surface has no salvageable data it should be removed - // from the cache to avoid clogging the data structure. - if (surface.IsFullyInvalid()) { - remove_surfaces.push_back(surface_id); + const auto interval = surface.GetInterval() & invalid_interval; + surface.MarkInvalid(interval); + if (!surface.IsFullyInvalid()) { + return; } + remove_surfaces.push_back(surface_id); }); if (region_owner_id) { @@ -1318,15 +1299,30 @@ void RasterizerCache::InvalidateRegion(PAddr addr, u32 size, SurfaceId region dirty_regions.erase(invalid_interval); } - for (const SurfaceId remove_surface_id : remove_surfaces) { - UnregisterSurface(remove_surface_id); + for (const SurfaceId surface_id : remove_surfaces) { + UnregisterSurface(surface_id); + if (!slot_surfaces[surface_id].IsFill()) { + sentenced.emplace_back(surface_id, frame_tick); + } else { + slot_surfaces.erase(surface_id); + } } remove_surfaces.clear(); } template SurfaceId RasterizerCache::CreateSurface(const SurfaceParams& params) { - SurfaceId surface_id = slot_surfaces.insert(runtime, params); + const SurfaceId surface_id = [&] { + const auto it = std::find_if(sentenced.begin(), sentenced.end(), [&](const auto& pair) { + return slot_surfaces[pair.first] == params; + }); + if (it != sentenced.end()) { + const SurfaceId surface_id = it->first; + sentenced.erase(it); + return surface_id; + } + return slot_surfaces.insert(runtime, params); + }(); Surface& surface = slot_surfaces[surface_id]; surface.MarkInvalid(surface.GetInterval()); return surface_id; @@ -1368,8 +1364,6 @@ void RasterizerCache::UnregisterSurface(SurfaceId surface_id) { surfaces.erase(vector_it); }); - SCOPE_EXIT({ slot_surfaces.erase(surface_id); }); - if (False(surface.flags & SurfaceFlagBits::Tracked)) { return; } @@ -1383,7 +1377,7 @@ void RasterizerCache::UnregisterSurface(SurfaceId surface_id) { } if (std::none_of(cube.face_ids.begin(), cube.face_ids.end(), [](SurfaceId id) { return id; })) { - slot_surfaces.erase(cube.surface_id); + sentenced.emplace_back(cube.surface_id, frame_tick); return true; } return false; @@ -1400,7 +1394,6 @@ void RasterizerCache::UnregisterAll() { } texture_cube_cache.clear(); remove_surfaces.clear(); - runtime.Reset(); } template diff --git a/src/video_core/rasterizer_cache/rasterizer_cache_base.h b/src/video_core/rasterizer_cache/rasterizer_cache_base.h index 2b76f40767..daed5f2532 100644 --- a/src/video_core/rasterizer_cache/rasterizer_cache_base.h +++ b/src/video_core/rasterizer_cache/rasterizer_cache_base.h @@ -5,11 +5,14 @@ #pragma once #include +#include #include +#include #include #include #include #include +#include "video_core/rasterizer_cache/framebuffer_base.h" #include "video_core/rasterizer_cache/sampler_params.h" #include "video_core/rasterizer_cache/surface_params.h" #include "video_core/rasterizer_cache/texture_cube.h" @@ -26,6 +29,10 @@ namespace Pica::Texture { struct TextureInfo; } +namespace Settings { +enum class TextureFilter : u32; +} + namespace VideoCore { enum class ScaleMatch { @@ -38,9 +45,8 @@ enum class MatchFlags { Exact = 1 << 0, ///< Surface perfectly matches params SubRect = 1 << 1, ///< Surface encompasses params Copy = 1 << 2, ///< Surface that can be used as a copy source - Expand = 1 << 3, ///< Surface that can expand params - TexCopy = 1 << 4, ///< Surface that will match a display transfer "texture copy" parameters - Reinterpret = 1 << 5, ///< Surface might have different pixel format. + TexCopy = 1 << 3, ///< Surface that will match a display transfer "texture copy" parameters + Reinterpret = 1 << 4, ///< Surface might have different pixel format. }; DECLARE_ENUM_FLAG_OPERATORS(MatchFlags); @@ -66,11 +72,6 @@ class RasterizerCache { using SurfaceRect_Tuple = std::pair>; using PageMap = boost::icl::interval_map; - struct RenderTargets { - SurfaceId color_id; - SurfaceId depth_id; - }; - public: explicit RasterizerCache(Memory::MemorySystem& memory, CustomTexManager& custom_tex_manager, Runtime& runtime, Pica::Regs& regs, RendererBase& renderer); @@ -115,10 +116,7 @@ public: Surface& GetTextureCube(const TextureCubeConfig& config); /// Get the color and depth surfaces based on the framebuffer configuration - Framebuffer GetFramebufferSurfaces(bool using_color_fb, bool using_depth_fb); - - /// Marks the draw rectangle defined in framebuffer as invalid - void InvalidateFramebuffer(const Framebuffer& framebuffer); + FramebufferHelper GetFramebufferSurfaces(bool using_color_fb, bool using_depth_fb); /// Get a surface that matches a "texture copy" display transfer config SurfaceRect_Tuple GetTexCopySurface(const SurfaceParams& params); @@ -161,9 +159,18 @@ private: SurfaceId FindMatch(const SurfaceParams& params, ScaleMatch match_scale_type, std::optional validate_interval = std::nullopt); + /// Unregisters sentenced surfaces that have surpassed the destruction threshold. + void RunGarbageCollector(); + + /// Removes any framebuffers that reference the provided surface_id. + void RemoveFramebuffers(SurfaceId surface_id); + /// Transfers ownership of a memory region from src_surface to dest_surface void DuplicateSurface(SurfaceId src_id, SurfaceId dst_id); + /// Computes the hash of the provided texture data. + u64 ComputeHash(const SurfaceParams& load_info, std::span upload_data); + /// Update surface's texture for given region when necessary void ValidateSurface(SurfaceId surface, PAddr addr, u32 size); @@ -209,15 +216,19 @@ private: RendererBase& renderer; std::unordered_map texture_cube_cache; tsl::robin_pg_map, Common::IdentityHash> page_table; + std::unordered_map framebuffers; std::unordered_map samplers; + std::list> sentenced; Common::SlotVector slot_surfaces; Common::SlotVector slot_samplers; + Common::SlotVector slot_framebuffers; SurfaceMap dirty_regions; PageMap cached_pages; std::vector remove_surfaces; u32 resolution_scale_factor; - RenderTargets render_targets; - bool use_filter; + u64 frame_tick{}; + FramebufferParams fb_params; + Settings::TextureFilter filter; bool dump_textures; bool use_custom_textures; }; diff --git a/src/video_core/rasterizer_cache/slot_id.h b/src/video_core/rasterizer_cache/slot_id.h index b76805be94..84f3396e1c 100644 --- a/src/video_core/rasterizer_cache/slot_id.h +++ b/src/video_core/rasterizer_cache/slot_id.h @@ -10,6 +10,7 @@ namespace VideoCore { using SurfaceId = Common::SlotId; using SamplerId = Common::SlotId; +using FramebufferId = Common::SlotId; /// Fake surface ID for null surfaces constexpr SurfaceId NULL_SURFACE_ID{0}; diff --git a/src/video_core/rasterizer_cache/surface_base.cpp b/src/video_core/rasterizer_cache/surface_base.cpp index 1ad8bfa37a..8d310dfe4a 100644 --- a/src/video_core/rasterizer_cache/surface_base.cpp +++ b/src/video_core/rasterizer_cache/surface_base.cpp @@ -105,7 +105,7 @@ SurfaceInterval SurfaceBase::GetCopyableInterval(const SurfaceParams& params) co return result; } -Extent SurfaceBase::RealExtent(bool scaled) { +Extent SurfaceBase::RealExtent(bool scaled) const { const bool is_custom = IsCustom(); u32 real_width = width; u32 real_height = height; diff --git a/src/video_core/rasterizer_cache/surface_base.h b/src/video_core/rasterizer_cache/surface_base.h index 9be06e0dfe..c3bd7b4320 100644 --- a/src/video_core/rasterizer_cache/surface_base.h +++ b/src/video_core/rasterizer_cache/surface_base.h @@ -41,11 +41,15 @@ public: ClearValue MakeClearValue(PAddr copy_addr, PixelFormat dst_format); /// Returns the internal surface extent. - Extent RealExtent(bool scaled = true); + Extent RealExtent(bool scaled = true) const; /// Returns true if the surface contains a custom material with a normal map. bool HasNormalMap() const noexcept; + bool IsFill() const noexcept { + return type == SurfaceType::Fill; + } + bool Overlaps(PAddr overlap_addr, size_t overlap_size) const noexcept { const PAddr overlap_end = overlap_addr + static_cast(overlap_size); return addr < overlap_end && overlap_addr < end; diff --git a/src/video_core/rasterizer_cache/surface_params.cpp b/src/video_core/rasterizer_cache/surface_params.cpp index 87b6a7274d..2f14961935 100644 --- a/src/video_core/rasterizer_cache/surface_params.cpp +++ b/src/video_core/rasterizer_cache/surface_params.cpp @@ -34,15 +34,6 @@ bool SurfaceParams::CanReinterpret(const SurfaceParams& other_surface) { GetSubRect(other_surface).right <= stride; } -bool SurfaceParams::CanExpand(const SurfaceParams& expanded_surface) const { - return pixel_format != PixelFormat::Invalid && pixel_format == expanded_surface.pixel_format && - addr <= expanded_surface.end && expanded_surface.addr <= end && - is_tiled == expanded_surface.is_tiled && stride == expanded_surface.stride && - (std::max(expanded_surface.addr, addr) - std::min(expanded_surface.addr, addr)) % - BytesInPixels(stride * (is_tiled ? 8 : 1)) == - 0; -} - bool SurfaceParams::CanTexCopy(const SurfaceParams& texcopy_params) const { const SurfaceInterval copy_interval = texcopy_params.GetInterval(); if (pixel_format == PixelFormat::Invalid || addr > texcopy_params.addr || diff --git a/src/video_core/rasterizer_cache/surface_params.h b/src/video_core/rasterizer_cache/surface_params.h index 74f880d2a9..89aff60431 100644 --- a/src/video_core/rasterizer_cache/surface_params.h +++ b/src/video_core/rasterizer_cache/surface_params.h @@ -26,9 +26,6 @@ public: /// Returns true if other_surface can be used for reinterpretion. bool CanReinterpret(const SurfaceParams& other_surface); - /// Returns true if params can be expanded to match expanded_surface - bool CanExpand(const SurfaceParams& expanded_surface) const; - /// Returns true if params can be used for texcopy bool CanTexCopy(const SurfaceParams& texcopy_params) const; @@ -56,6 +53,10 @@ public: /// Returns a string identifier of the params object std::string DebugName(bool scaled, bool custom = false) const noexcept; + bool operator==(const SurfaceParams& other) const noexcept { + return std::memcmp(this, &other, sizeof(SurfaceParams)) == 0; + } + [[nodiscard]] SurfaceInterval GetInterval() const noexcept { return SurfaceInterval{addr, end}; } @@ -72,12 +73,12 @@ public: return height * res_scale; } - [[nodiscard]] Common::Rectangle GetRect() const noexcept { - return {0, height, width, 0}; + [[nodiscard]] Common::Rectangle GetRect(u32 level = 0) const noexcept { + return {0, height >> level, width >> level, 0}; } - [[nodiscard]] Common::Rectangle GetScaledRect() const noexcept { - return {0, GetScaledHeight(), GetScaledWidth(), 0}; + [[nodiscard]] Common::Rectangle GetScaledRect(u32 level = 0) const noexcept { + return {0, GetScaledHeight() >> level, GetScaledWidth() >> level, 0}; } [[nodiscard]] u32 PixelsInBytes(u32 size) const noexcept { diff --git a/src/video_core/rasterizer_cache/utils.h b/src/video_core/rasterizer_cache/utils.h index aeb7d74513..3971bc4be0 100644 --- a/src/video_core/rasterizer_cache/utils.h +++ b/src/video_core/rasterizer_cache/utils.h @@ -67,6 +67,7 @@ struct StagingData { }; class SurfaceParams; +struct FramebufferParams; u32 MipLevels(u32 width, u32 height, u32 max_level); diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h index 3109567ac1..3a86d1eb4c 100644 --- a/src/video_core/rasterizer_interface.h +++ b/src/video_core/rasterizer_interface.h @@ -9,10 +9,6 @@ #include "common/common_types.h" #include "core/hw/gpu.h" -namespace OpenGL { -struct ScreenInfo; -} - namespace Pica::Shader { struct OutputVertex; } // namespace Pica::Shader @@ -76,14 +72,6 @@ public: return false; } - /// Attempt to use a faster method to display the framebuffer to screen - virtual bool AccelerateDisplay([[maybe_unused]] const GPU::Regs::FramebufferConfig& config, - [[maybe_unused]] PAddr framebuffer_addr, - [[maybe_unused]] u32 pixel_stride, - [[maybe_unused]] OpenGL::ScreenInfo& screen_info) { - return false; - } - /// Attempt to draw using hardware shaders virtual bool AccelerateDrawBatch([[maybe_unused]] bool is_indexed) { return false; diff --git a/src/video_core/renderer_base.h b/src/video_core/renderer_base.h index 038d5705f4..6e1f327e4e 100644 --- a/src/video_core/renderer_base.h +++ b/src/video_core/renderer_base.h @@ -31,9 +31,7 @@ struct RendererSettings { std::function screenshot_complete_callback; Layout::FramebufferLayout screenshot_framebuffer_layout; // Renderer - std::atomic_bool texture_filter_update_requested{false}; std::atomic_bool bg_color_update_requested{false}; - std::atomic_bool sampler_update_requested{false}; std::atomic_bool shader_update_requested{false}; }; @@ -44,7 +42,7 @@ public: virtual ~RendererBase(); /// Returns the rasterizer owned by the renderer - virtual VideoCore::RasterizerInterface* Rasterizer() const = 0; + virtual VideoCore::RasterizerInterface* Rasterizer() = 0; /// Finalize rendering the guest frame and draw into the presentation texture virtual void SwapBuffers() = 0; @@ -74,9 +72,6 @@ public: /// Ends the current frame void EndFrame(); - // Getter/setter functions: - // ------------------------ - f32 GetCurrentFPS() const { return current_fps; } diff --git a/src/video_core/renderer_opengl/frame_dumper_opengl.cpp b/src/video_core/renderer_opengl/frame_dumper_opengl.cpp index 1d4ac8c32a..dd8720b101 100644 --- a/src/video_core/renderer_opengl/frame_dumper_opengl.cpp +++ b/src/video_core/renderer_opengl/frame_dumper_opengl.cpp @@ -7,7 +7,7 @@ #include #include "core/frontend/emu_window.h" #include "video_core/renderer_opengl/frame_dumper_opengl.h" -#include "video_core/renderer_opengl/renderer_opengl.h" +#include "video_core/renderer_opengl/gl_texture_mailbox.h" namespace OpenGL { diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 0377673855..22e568aca5 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -12,9 +12,9 @@ #include "video_core/regs_rasterizer.h" #include "video_core/renderer_opengl/gl_rasterizer.h" #include "video_core/renderer_opengl/gl_shader_gen.h" -#include "video_core/renderer_opengl/gl_vars.h" #include "video_core/renderer_opengl/pica_to_gl.h" #include "video_core/renderer_opengl/renderer_opengl.h" +#include "video_core/texture/texture_decode.h" #include "video_core/video_core.h" namespace OpenGL { @@ -76,8 +76,9 @@ GLenum MakeAttributeType(Pica::PipelineRegs::VertexAttributeFormat format) { RasterizerOpenGL::RasterizerOpenGL(Memory::MemorySystem& memory, VideoCore::CustomTexManager& custom_tex_manager, VideoCore::RendererBase& renderer, Driver& driver_) - : VideoCore::RasterizerAccelerated{memory}, driver{driver_}, runtime{driver, renderer}, - res_cache{memory, custom_tex_manager, runtime, regs, renderer}, + : VideoCore::RasterizerAccelerated{memory}, driver{driver_}, + shader_manager{renderer.GetRenderWindow(), driver, !driver.IsOpenGLES()}, + runtime{driver, renderer}, res_cache{memory, custom_tex_manager, runtime, regs, renderer}, texture_buffer_size{TextureBufferSize()}, vertex_buffer{driver, GL_ARRAY_BUFFER, VERTEX_BUFFER_SIZE}, uniform_buffer{driver, GL_UNIFORM_BUFFER, UNIFORM_BUFFER_SIZE}, @@ -89,14 +90,6 @@ RasterizerOpenGL::RasterizerOpenGL(Memory::MemorySystem& memory, // Clipping plane 0 is always enabled for PICA fixed clip plane z <= 0 state.clip_distance[0] = true; - // Create a 1x1 clear texture to use in the NULL case, - // instead of OpenGL's default of solid black - glGenTextures(1, &default_texture); - glBindTexture(GL_TEXTURE_2D, default_texture); - // For some reason alpha 0 wraps around to 1.0, so use 1/255 instead - u8 framebuffer_data[4] = {0, 0, 0, 1}; - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, framebuffer_data); - // Generate VAO sw_vao.Create(); hw_vao.Create(); @@ -142,9 +135,6 @@ RasterizerOpenGL::RasterizerOpenGL(Memory::MemorySystem& memory, (GLvoid*)offsetof(HardwareVertex, view)); glEnableVertexAttribArray(ATTRIBUTE_VIEW); - // Create render framebuffer - framebuffer.Create(); - // Allocate and bind texture buffer lut textures texture_buffer_lut_lf.Create(); texture_buffer_lut_rg.Create(); @@ -165,9 +155,6 @@ RasterizerOpenGL::RasterizerOpenGL(Memory::MemorySystem& memory, state.Apply(); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_buffer.GetHandle()); - shader_program_manager = - std::make_unique(renderer.GetRenderWindow(), driver, !GLES); - glEnable(GL_BLEND); SyncEntireState(); @@ -181,7 +168,7 @@ void RasterizerOpenGL::TickFrame() { void RasterizerOpenGL::LoadDiskResources(const std::atomic_bool& stop_loading, const VideoCore::DiskResourceLoadCallback& callback) { - shader_program_manager->LoadDiskCache(stop_loading, callback); + shader_manager.LoadDiskCache(stop_loading, callback); } void RasterizerOpenGL::SyncFixedState() { @@ -277,7 +264,7 @@ void RasterizerOpenGL::SetupVertexArray(u8* array_ptr, GLintptr buffer_offset, bool RasterizerOpenGL::SetupVertexShader() { MICROPROFILE_SCOPE(OpenGL_VS); - return shader_program_manager->UseProgrammableVertexShader(regs, Pica::g_state.vs); + return shader_manager.UseProgrammableVertexShader(regs, Pica::g_state.vs); } bool RasterizerOpenGL::SetupGeometryShader() { @@ -288,7 +275,7 @@ bool RasterizerOpenGL::SetupGeometryShader() { return false; } - shader_program_manager->UseFixedGeometryShader(regs); + shader_manager.UseFixedGeometryShader(regs); return true; } @@ -302,11 +289,13 @@ bool RasterizerOpenGL::AccelerateDrawBatch(bool is_indexed) { } } - if (!SetupVertexShader()) + if (!SetupVertexShader()) { return false; + } - if (!SetupGeometryShader()) + if (!SetupGeometryShader()) { return false; + } return Draw(true, is_indexed); } @@ -329,7 +318,7 @@ bool RasterizerOpenGL::AccelerateDrawBatchInternal(bool is_indexed) { SetupVertexArray(buffer_ptr, buffer_offset, vs_input_index_min, vs_input_index_max); vertex_buffer.Unmap(vs_input_size); - shader_program_manager->ApplyTo(state); + shader_manager.ApplyTo(state); state.Apply(); if (is_indexed) { @@ -341,9 +330,9 @@ bool RasterizerOpenGL::AccelerateDrawBatchInternal(bool is_indexed) { return false; } - const u8* index_data = VideoCore::g_memory->GetPhysicalPointer( - regs.pipeline.vertex_attributes.GetPhysicalBaseAddress() + - regs.pipeline.index_array.offset); + const u8* index_data = + memory.GetPhysicalPointer(regs.pipeline.vertex_attributes.GetPhysicalBaseAddress() + + regs.pipeline.index_array.offset); std::tie(buffer_ptr, buffer_offset, std::ignore) = index_buffer.Map(index_buffer_size, 4); std::memcpy(buffer_ptr, index_data, index_buffer_size); index_buffer.Unmap(index_buffer_size); @@ -386,21 +375,20 @@ bool RasterizerOpenGL::Draw(bool accelerate, bool is_indexed) { (write_depth_fb || regs.framebuffer.output_merger.depth_test_enable != 0 || (has_stencil && state.stencil.test_enabled)); - const Framebuffer framebuffer = - res_cache.GetFramebufferSurfaces(using_color_fb, using_depth_fb); - const bool has_color = framebuffer.HasAttachment(SurfaceType::Color); - if (!has_color && shadow_rendering) { + const auto fb_helper = res_cache.GetFramebufferSurfaces(using_color_fb, using_depth_fb); + const Framebuffer* framebuffer = fb_helper.Framebuffer(); + if (!framebuffer->color_id && framebuffer->shadow_rendering) { return true; } // Bind the framebuffer surfaces if (shadow_rendering) { - state.image_shadow_buffer = framebuffer.Attachment(SurfaceType::Color); + state.image_shadow_buffer = framebuffer->Attachment(SurfaceType::Color); } - state.draw.draw_framebuffer = framebuffer.Handle(); + state.draw.draw_framebuffer = framebuffer->Handle(); // Sync the viewport - const auto viewport = framebuffer.Viewport(); + const auto viewport = fb_helper.Viewport(); state.viewport.x = static_cast(viewport.x); state.viewport.y = static_cast(viewport.y); state.viewport.width = static_cast(viewport.width); @@ -408,21 +396,15 @@ bool RasterizerOpenGL::Draw(bool accelerate, bool is_indexed) { // Viewport can have negative offsets or larger dimensions than our framebuffer sub-rect. // Enable scissor test to prevent drawing outside of the framebuffer region - const auto draw_rect = framebuffer.DrawRect(); + const auto draw_rect = fb_helper.DrawRect(); state.scissor.enabled = true; state.scissor.x = draw_rect.left; state.scissor.y = draw_rect.bottom; state.scissor.width = draw_rect.GetWidth(); state.scissor.height = draw_rect.GetHeight(); - const int res_scale = static_cast(framebuffer.ResolutionScale()); - if (uniform_block_data.data.framebuffer_scale != res_scale) { - uniform_block_data.data.framebuffer_scale = res_scale; - uniform_block_data.dirty = true; - } - // Update scissor uniforms - const auto [scissor_x1, scissor_y2, scissor_x2, scissor_y1] = framebuffer.Scissor(); + const auto [scissor_x1, scissor_y2, scissor_x2, scissor_y1] = fb_helper.Scissor(); if (uniform_block_data.data.scissor_x1 != scissor_x1 || uniform_block_data.data.scissor_x2 != scissor_x2 || uniform_block_data.data.scissor_y1 != scissor_y1 || @@ -441,7 +423,7 @@ bool RasterizerOpenGL::Draw(bool accelerate, bool is_indexed) { // Sync and bind the shader if (shader_dirty) { - shader_program_manager->UseFragmentShader(regs, use_custom_normal); + shader_manager.UseFragmentShader(regs, use_custom_normal); shader_dirty = false; } @@ -459,9 +441,9 @@ bool RasterizerOpenGL::Draw(bool accelerate, bool is_indexed) { } else { state.draw.vertex_array = sw_vao.handle; state.draw.vertex_buffer = vertex_buffer.GetHandle(); - shader_program_manager->UseTrivialVertexShader(); - shader_program_manager->UseTrivialGeometryShader(); - shader_program_manager->ApplyTo(state); + shader_manager.UseTrivialVertexShader(); + shader_manager.UseTrivialGeometryShader(); + shader_manager.ApplyTo(state); state.Apply(); std::size_t max_vertices = 3 * (VERTEX_BUFFER_SIZE / (3 * sizeof(HardwareVertex))); @@ -486,13 +468,12 @@ bool RasterizerOpenGL::Draw(bool accelerate, bool is_indexed) { GL_TEXTURE_UPDATE_BARRIER_BIT | GL_FRAMEBUFFER_BARRIER_BIT); } - res_cache.InvalidateFramebuffer(framebuffer); use_custom_normal = false; return succeeded; } -void RasterizerOpenGL::SyncTextureUnits(const Framebuffer& framebuffer) { +void RasterizerOpenGL::SyncTextureUnits(const Framebuffer* framebuffer) { using TextureType = Pica::TexturingRegs::TextureConfig::TextureType; const auto pica_textures = regs.texturing.GetTextures(); @@ -603,27 +584,15 @@ void RasterizerOpenGL::BindMaterial(u32 texture_index, Surface& surface) { } } -bool RasterizerOpenGL::IsFeedbackLoop(u32 texture_index, const Framebuffer& framebuffer, +bool RasterizerOpenGL::IsFeedbackLoop(u32 texture_index, const Framebuffer* framebuffer, Surface& surface) { - const GLuint color_attachment = framebuffer.Attachment(SurfaceType::Color); + const GLuint color_attachment = framebuffer->Attachment(SurfaceType::Color); const bool is_feedback_loop = color_attachment == surface.Handle(); if (!is_feedback_loop) { return false; } - // Make a temporary copy of the framebuffer to sample from - Surface temp_surface{runtime, framebuffer.ColorParams()}; - const VideoCore::TextureCopy copy = { - .src_level = 0, - .dst_level = 0, - .src_layer = 0, - .dst_layer = 0, - .src_offset = {0, 0}, - .dst_offset = {0, 0}, - .extent = {temp_surface.GetScaledWidth(), temp_surface.GetScaledHeight()}, - }; - runtime.CopyTextures(surface, temp_surface, copy); - state.texture_units[texture_index].texture_2d = temp_surface.Handle(); + state.texture_units[texture_index].texture_2d = surface.CopyHandle(); return true; } @@ -840,7 +809,7 @@ void RasterizerOpenGL::SyncBlendColor() { void RasterizerOpenGL::SyncLogicOp() { state.logic_op = PicaToGL::LogicOp(regs.framebuffer.output_merger.logic_op); - if (GLES) { + if (driver.IsOpenGLES()) { if (!regs.framebuffer.output_merger.alphablend_enable) { if (regs.framebuffer.output_merger.logic_op == Pica::FramebufferRegs::LogicOp::NoOp) { // Color output is disabled by logic operation. We use color write mask to skip @@ -852,7 +821,7 @@ void RasterizerOpenGL::SyncLogicOp() { } void RasterizerOpenGL::SyncColorWriteMask() { - if (GLES) { + if (driver.IsOpenGLES()) { if (!regs.framebuffer.output_merger.alphablend_enable) { if (regs.framebuffer.output_merger.logic_op == Pica::FramebufferRegs::LogicOp::NoOp) { // Color output is disabled by logic operation. We use color write mask to skip @@ -862,17 +831,19 @@ void RasterizerOpenGL::SyncColorWriteMask() { } } - auto IsColorWriteEnabled = [&](u32 value) { + auto is_color_write_enabled = [&](u32 value) { return (regs.framebuffer.framebuffer.allow_color_write != 0 && value != 0) ? GL_TRUE : GL_FALSE; }; - state.color_mask.red_enabled = IsColorWriteEnabled(regs.framebuffer.output_merger.red_enable); + state.color_mask.red_enabled = + is_color_write_enabled(regs.framebuffer.output_merger.red_enable); state.color_mask.green_enabled = - IsColorWriteEnabled(regs.framebuffer.output_merger.green_enable); - state.color_mask.blue_enabled = IsColorWriteEnabled(regs.framebuffer.output_merger.blue_enable); + is_color_write_enabled(regs.framebuffer.output_merger.green_enable); + state.color_mask.blue_enabled = + is_color_write_enabled(regs.framebuffer.output_merger.blue_enable); state.color_mask.alpha_enabled = - IsColorWriteEnabled(regs.framebuffer.output_merger.alpha_enable); + is_color_write_enabled(regs.framebuffer.output_merger.alpha_enable); } void RasterizerOpenGL::SyncStencilWriteMask() { @@ -923,12 +894,10 @@ void RasterizerOpenGL::SyncAndUploadLUTsLF() { return; } - u8* buffer; - GLintptr offset; - bool invalidate; std::size_t bytes_used = 0; glBindBuffer(GL_TEXTURE_BUFFER, texture_lf_buffer.GetHandle()); - std::tie(buffer, offset, invalidate) = texture_lf_buffer.Map(max_size, sizeof(Common::Vec4f)); + const auto [buffer, offset, invalidate] = + texture_lf_buffer.Map(max_size, sizeof(Common::Vec4f)); // Sync the lighting luts if (uniform_block_data.lighting_lut_dirty_any || invalidate) { @@ -993,50 +962,48 @@ void RasterizerOpenGL::SyncAndUploadLUTs() { return; } - u8* buffer; - GLintptr offset; - bool invalidate; std::size_t bytes_used = 0; glBindBuffer(GL_TEXTURE_BUFFER, texture_buffer.GetHandle()); - std::tie(buffer, offset, invalidate) = texture_buffer.Map(max_size, sizeof(Common::Vec4f)); + const auto [buffer, offset, invalidate] = texture_buffer.Map(max_size, sizeof(Common::Vec4f)); // helper function for SyncProcTexNoiseLUT/ColorMap/AlphaMap - auto SyncProcTexValueLUT = [this, buffer, offset, invalidate, &bytes_used]( - const std::array& lut, - std::array& lut_data, GLint& lut_offset) { - std::array new_data; - std::transform(lut.begin(), lut.end(), new_data.begin(), [](const auto& entry) { - return Common::Vec2f{entry.ToFloat(), entry.DiffToFloat()}; - }); + const auto sync_proc_tex_value_lut = + [this, buffer = buffer, offset = offset, invalidate = invalidate, + &bytes_used](const std::array& lut, + std::array& lut_data, GLint& lut_offset) { + std::array new_data; + std::transform(lut.begin(), lut.end(), new_data.begin(), [](const auto& entry) { + return Common::Vec2f{entry.ToFloat(), entry.DiffToFloat()}; + }); - if (new_data != lut_data || invalidate) { - lut_data = new_data; - std::memcpy(buffer + bytes_used, new_data.data(), - new_data.size() * sizeof(Common::Vec2f)); - lut_offset = static_cast((offset + bytes_used) / sizeof(Common::Vec2f)); - uniform_block_data.dirty = true; - bytes_used += new_data.size() * sizeof(Common::Vec2f); - } - }; + if (new_data != lut_data || invalidate) { + lut_data = new_data; + std::memcpy(buffer + bytes_used, new_data.data(), + new_data.size() * sizeof(Common::Vec2f)); + lut_offset = static_cast((offset + bytes_used) / sizeof(Common::Vec2f)); + uniform_block_data.dirty = true; + bytes_used += new_data.size() * sizeof(Common::Vec2f); + } + }; // Sync the proctex noise lut if (uniform_block_data.proctex_noise_lut_dirty || invalidate) { - SyncProcTexValueLUT(Pica::g_state.proctex.noise_table, proctex_noise_lut_data, - uniform_block_data.data.proctex_noise_lut_offset); + sync_proc_tex_value_lut(Pica::g_state.proctex.noise_table, proctex_noise_lut_data, + uniform_block_data.data.proctex_noise_lut_offset); uniform_block_data.proctex_noise_lut_dirty = false; } // Sync the proctex color map if (uniform_block_data.proctex_color_map_dirty || invalidate) { - SyncProcTexValueLUT(Pica::g_state.proctex.color_map_table, proctex_color_map_data, - uniform_block_data.data.proctex_color_map_offset); + sync_proc_tex_value_lut(Pica::g_state.proctex.color_map_table, proctex_color_map_data, + uniform_block_data.data.proctex_color_map_offset); uniform_block_data.proctex_color_map_dirty = false; } // Sync the proctex alpha map if (uniform_block_data.proctex_alpha_map_dirty || invalidate) { - SyncProcTexValueLUT(Pica::g_state.proctex.alpha_map_table, proctex_alpha_map_data, - uniform_block_data.data.proctex_alpha_map_offset); + sync_proc_tex_value_lut(Pica::g_state.proctex.alpha_map_table, proctex_alpha_map_data, + uniform_block_data.data.proctex_alpha_map_offset); uniform_block_data.proctex_alpha_map_dirty = false; } @@ -1090,28 +1057,25 @@ void RasterizerOpenGL::SyncAndUploadLUTs() { } void RasterizerOpenGL::UploadUniforms(bool accelerate_draw) { - // glBindBufferRange below also changes the generic buffer binding point, so we sync the state - // first + // glBindBufferRange also changes the generic buffer binding point, so we sync the state first. state.draw.uniform_buffer = uniform_buffer.GetHandle(); state.Apply(); - bool sync_vs = accelerate_draw; - bool sync_fs = uniform_block_data.dirty; - - if (!sync_vs && !sync_fs) + const bool sync_vs = accelerate_draw; + const bool sync_fs = uniform_block_data.dirty; + if (!sync_vs && !sync_fs) { return; + } std::size_t uniform_size = uniform_size_aligned_vs + uniform_size_aligned_fs; std::size_t used_bytes = 0; - u8* uniforms; - GLintptr offset; - bool invalidate; - std::tie(uniforms, offset, invalidate) = + + const auto [uniforms, offset, invalidate] = uniform_buffer.Map(uniform_size, uniform_buffer_alignment); if (sync_vs) { Pica::Shader::VSUniformData vs_uniforms; - vs_uniforms.uniforms.SetFromRegs(Pica::g_state.regs.vs, Pica::g_state.vs); + vs_uniforms.uniforms.SetFromRegs(regs.vs, Pica::g_state.vs); std::memcpy(uniforms + used_bytes, &vs_uniforms, sizeof(vs_uniforms)); glBindBufferRange(GL_UNIFORM_BUFFER, static_cast(Pica::Shader::UniformBindings::VS), uniform_buffer.GetHandle(), offset + used_bytes, diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index 51f0648686..22f1b5bd2b 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -6,7 +6,6 @@ #include "core/hw/gpu.h" #include "video_core/rasterizer_accelerated.h" -#include "video_core/rasterizer_cache/rasterizer_cache.h" #include "video_core/rasterizer_interface.h" #include "video_core/regs_texturing.h" #include "video_core/renderer_opengl/gl_shader_manager.h" @@ -24,6 +23,8 @@ class CustomTexManager; namespace OpenGL { +struct ScreenInfo; + class Driver; class ShaderProgramManager; @@ -48,7 +49,7 @@ public: bool AccelerateTextureCopy(const GPU::Regs::DisplayTransferConfig& config) override; bool AccelerateFill(const GPU::Regs::MemoryFillConfig& config) override; bool AccelerateDisplay(const GPU::Regs::FramebufferConfig& config, PAddr framebuffer_addr, - u32 pixel_stride, ScreenInfo& screen_info) override; + u32 pixel_stride, ScreenInfo& screen_info); bool AccelerateDrawBatch(bool is_indexed) override; private: @@ -93,7 +94,7 @@ private: void SyncAndUploadLUTsLF(); /// Syncs all enabled PICA texture units - void SyncTextureUnits(const Framebuffer& framebuffer); + void SyncTextureUnits(const Framebuffer* framebuffer); /// Binds the PICA shadow cube required for shadow mapping void BindShadowCube(const Pica::TexturingRegs::FullTextureConfig& texture); @@ -102,7 +103,7 @@ private: void BindTextureCube(const Pica::TexturingRegs::FullTextureConfig& texture); /// Makes a temporary copy of the framebuffer if a feedback loop is detected - bool IsFeedbackLoop(u32 texture_index, const Framebuffer& framebuffer, Surface& surface); + bool IsFeedbackLoop(u32 texture_index, const Framebuffer* framebuffer, Surface& surface); /// Unbinds all special texture unit 0 texture configurations void UnbindSpecial(); @@ -132,10 +133,9 @@ private: private: Driver& driver; OpenGLState state; - GLuint default_texture; + ShaderProgramManager shader_manager; TextureRuntime runtime; RasterizerCache res_cache; - std::unique_ptr shader_program_manager; OGLVertexArray sw_vao; // VAO for software shader draw OGLVertexArray hw_vao; // VAO for hardware shader / accelerate draw @@ -147,7 +147,6 @@ private: OGLStreamBuffer index_buffer; OGLStreamBuffer texture_buffer; OGLStreamBuffer texture_lf_buffer; - OGLFramebuffer framebuffer; GLint uniform_buffer_alignment; std::size_t uniform_size_aligned_vs; std::size_t uniform_size_aligned_fs; diff --git a/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp index 8099ab0167..88b3c34bf9 100644 --- a/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp @@ -5,7 +5,6 @@ #include #include -#include "common/assert.h" #include "common/common_paths.h" #include "common/common_types.h" #include "common/file_util.h" @@ -14,7 +13,7 @@ #include "common/settings.h" #include "common/zstd_compression.h" #include "core/core.h" -#include "core/hle/kernel/process.h" +#include "core/loader/loader.h" #include "video_core/renderer_opengl/gl_shader_disk_cache.h" namespace OpenGL { diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp index 577fd34945..128137d52c 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.cpp +++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp @@ -7,6 +7,7 @@ #include "common/bit_set.h" #include "common/logging/log.h" #include "core/core.h" +#include "core/telemetry_session.h" #include "video_core/pica_state.h" #include "video_core/renderer_opengl/gl_shader_decompiler.h" #include "video_core/renderer_opengl/gl_shader_gen.h" diff --git a/src/video_core/renderer_opengl/gl_shader_manager.cpp b/src/video_core/renderer_opengl/gl_shader_manager.cpp index 0b0f38b242..f656705dda 100644 --- a/src/video_core/renderer_opengl/gl_shader_manager.cpp +++ b/src/video_core/renderer_opengl/gl_shader_manager.cpp @@ -8,7 +8,6 @@ #include #include #include -#include "common/scope_exit.h" #include "core/frontend/emu_window.h" #include "video_core/renderer_opengl/gl_driver.h" #include "video_core/renderer_opengl/gl_resource_manager.h" diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp index 89944f80e0..dc95e2c24b 100644 --- a/src/video_core/renderer_opengl/gl_state.cpp +++ b/src/video_core/renderer_opengl/gl_state.cpp @@ -2,9 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include -#include "common/common_funcs.h" -#include "common/logging/log.h" +#include "common/common_types.h" #include "video_core/renderer_opengl/gl_state.h" #include "video_core/renderer_opengl/gl_vars.h" diff --git a/src/video_core/renderer_opengl/gl_texture_mailbox.cpp b/src/video_core/renderer_opengl/gl_texture_mailbox.cpp new file mode 100644 index 0000000000..11a8b6eb9a --- /dev/null +++ b/src/video_core/renderer_opengl/gl_texture_mailbox.cpp @@ -0,0 +1,194 @@ +// Copyright 2023 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "video_core/renderer_opengl/gl_state.h" +#include "video_core/renderer_opengl/gl_texture_mailbox.h" + +namespace OpenGL { + +OGLTextureMailbox::OGLTextureMailbox(bool has_debug_tool_) : has_debug_tool{has_debug_tool_} { + for (auto& frame : swap_chain) { + free_queue.push(&frame); + } +} + +OGLTextureMailbox::~OGLTextureMailbox() { + // Lock the mutex and clear out the present and free_queues and notify any people who are + // blocked to prevent deadlock on shutdown + std::scoped_lock lock(swap_chain_lock); + free_queue = {}; + present_queue.clear(); + present_cv.notify_all(); + free_cv.notify_all(); +} + +void OGLTextureMailbox::ReloadPresentFrame(Frontend::Frame* frame, u32 height, u32 width) { + frame->present.Release(); + frame->present.Create(); + GLint previous_draw_fbo{}; + glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &previous_draw_fbo); + glBindFramebuffer(GL_FRAMEBUFFER, frame->present.handle); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, + frame->color.handle); + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + LOG_CRITICAL(Render_OpenGL, "Failed to recreate present FBO!"); + } + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, previous_draw_fbo); + frame->color_reloaded = false; +} + +void OGLTextureMailbox::ReloadRenderFrame(Frontend::Frame* frame, u32 width, u32 height) { + OpenGLState prev_state = OpenGLState::GetCurState(); + OpenGLState state = OpenGLState::GetCurState(); + + // Recreate the color texture attachment + frame->color.Release(); + frame->color.Create(); + state.renderbuffer = frame->color.handle; + state.Apply(); + glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, width, height); + + // Recreate the FBO for the render target + frame->render.Release(); + frame->render.Create(); + state.draw.read_framebuffer = frame->render.handle; + state.draw.draw_framebuffer = frame->render.handle; + state.Apply(); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, + frame->color.handle); + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + LOG_CRITICAL(Render_OpenGL, "Failed to recreate render FBO!"); + } + prev_state.Apply(); + frame->width = width; + frame->height = height; + frame->color_reloaded = true; +} + +Frontend::Frame* OGLTextureMailbox::GetRenderFrame() { + std::unique_lock lock{swap_chain_lock}; + + // If theres no free frames, we will reuse the oldest render frame + if (free_queue.empty()) { + auto frame = present_queue.back(); + present_queue.pop_back(); + return frame; + } + + Frontend::Frame* frame = free_queue.front(); + free_queue.pop(); + return frame; +} + +void OGLTextureMailbox::ReleaseRenderFrame(Frontend::Frame* frame) { + std::unique_lock lock{swap_chain_lock}; + present_queue.push_front(frame); + present_cv.notify_one(); + + DebugNotifyNextFrame(); +} + +void OGLTextureMailbox::LoadPresentFrame() { + // Free the previous frame and add it back to the free queue + if (previous_frame) { + free_queue.push(previous_frame); + free_cv.notify_one(); + } + + // The newest entries are pushed to the front of the queue + Frontend::Frame* frame = present_queue.front(); + present_queue.pop_front(); + // Remove all old entries from the present queue and move them back to the free_queue + for (auto f : present_queue) { + free_queue.push(f); + } + present_queue.clear(); + previous_frame = frame; +} + +Frontend::Frame* OGLTextureMailbox::TryGetPresentFrame(int timeout_ms) { + DebugWaitForNextFrame(); + + std::unique_lock lock{swap_chain_lock}; + // Wait for new entries in the present_queue + present_cv.wait_for(lock, std::chrono::milliseconds(timeout_ms), + [&] { return !present_queue.empty(); }); + if (present_queue.empty()) { + // Timed out waiting for a frame to draw so return the previous frame + return previous_frame; + } + + LoadPresentFrame(); + return previous_frame; +} + +void OGLTextureMailbox::DebugNotifyNextFrame() { + if (!has_debug_tool) { + return; + } + frame_for_debug++; + std::scoped_lock lock{debug_synch_mutex}; + debug_synch_condition.notify_one(); +} + +void OGLTextureMailbox::DebugWaitForNextFrame() { + if (!has_debug_tool) { + return; + } + const int last_frame = frame_for_debug; + std::unique_lock lock{debug_synch_mutex}; + debug_synch_condition.wait(lock, [this, last_frame] { return frame_for_debug > last_frame; }); +} + +Frontend::Frame* OGLVideoDumpingMailbox::GetRenderFrame() { + std::unique_lock lock{swap_chain_lock}; + + // If theres no free frames, we will wait until one shows up + if (free_queue.empty()) { + free_cv.wait(lock, [&] { return (!free_queue.empty() || quit); }); + if (quit) { + throw OGLTextureMailboxException("VideoDumpingMailbox quitting"); + } + + if (free_queue.empty()) { + LOG_CRITICAL(Render_OpenGL, "Could not get free frame"); + return nullptr; + } + } + + Frontend::Frame* frame = free_queue.front(); + free_queue.pop(); + return frame; +} + +void OGLVideoDumpingMailbox::LoadPresentFrame() { + // Free the previous frame and add it back to the free queue + if (previous_frame) { + free_queue.push(previous_frame); + free_cv.notify_one(); + } + + Frontend::Frame* frame = present_queue.back(); + present_queue.pop_back(); + previous_frame = frame; + + // Do not remove entries from the present_queue, as video dumping would require + // that we preserve all frames +} + +Frontend::Frame* OGLVideoDumpingMailbox::TryGetPresentFrame(int timeout_ms) { + std::unique_lock lock{swap_chain_lock}; + // Wait for new entries in the present_queue + present_cv.wait_for(lock, std::chrono::milliseconds(timeout_ms), + [&] { return !present_queue.empty(); }); + if (present_queue.empty()) { + // Timed out waiting for a frame + return nullptr; + } + + LoadPresentFrame(); + return previous_frame; +} + +} // namespace OpenGL diff --git a/src/video_core/renderer_opengl/gl_texture_mailbox.h b/src/video_core/renderer_opengl/gl_texture_mailbox.h new file mode 100644 index 0000000000..67646a3edf --- /dev/null +++ b/src/video_core/renderer_opengl/gl_texture_mailbox.h @@ -0,0 +1,92 @@ +// Copyright 2023 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include +#include + +#include "core/frontend/emu_window.h" +#include "video_core/renderer_opengl/gl_resource_manager.h" + +namespace Frontend { +struct Frame { + u32 width{}; ///< Width of the frame (to detect resize) + u32 height{}; ///< Height of the frame + bool color_reloaded = false; ///< Texture attachment was recreated (ie: resized) + OpenGL::OGLRenderbuffer color{}; ///< Buffer shared between the render/present FBO + OpenGL::OGLFramebuffer render{}; ///< FBO created on the render thread + OpenGL::OGLFramebuffer present{}; ///< FBO created on the present thread + GLsync render_fence{}; ///< Fence created on the render thread + GLsync present_fence{}; ///< Fence created on the presentation thread +}; +} // namespace Frontend + +namespace OpenGL { + +// If the size of this is too small, it ends up creating a soft cap on FPS as the renderer will have +// to wait on available presentation frames. There doesn't seem to be much of a downside to a larger +// number but 9 swap textures at 60FPS presentation allows for 800% speed so thats probably fine +#ifdef ANDROID +// Reduce the size of swap_chain, since the UI only allows upto 200% speed. +constexpr std::size_t SWAP_CHAIN_SIZE = 6; +#else +constexpr std::size_t SWAP_CHAIN_SIZE = 9; +#endif + +class OGLTextureMailbox : public Frontend::TextureMailbox { +public: + explicit OGLTextureMailbox(bool has_debug_tool = false); + ~OGLTextureMailbox() override; + + void ReloadPresentFrame(Frontend::Frame* frame, u32 height, u32 width) override; + void ReloadRenderFrame(Frontend::Frame* frame, u32 width, u32 height) override; + void ReleaseRenderFrame(Frontend::Frame* frame) override; + + Frontend::Frame* GetRenderFrame() override; + Frontend::Frame* TryGetPresentFrame(int timeout_ms) override; + + /// This is virtual as it is to be overriden in OGLVideoDumpingMailbox below. + virtual void LoadPresentFrame(); + +private: + /// Signal that a new frame is available (called from GPU thread) + void DebugNotifyNextFrame(); + + /// Wait for a new frame to be available (called from presentation thread) + void DebugWaitForNextFrame(); + +public: + std::mutex swap_chain_lock; + std::condition_variable free_cv; + std::condition_variable present_cv; + std::array swap_chain{}; + std::queue free_queue{}; + std::deque present_queue{}; + Frontend::Frame* previous_frame = nullptr; + std::mutex debug_synch_mutex; + std::condition_variable debug_synch_condition; + std::atomic_int frame_for_debug{}; + const bool has_debug_tool; ///< When true, using a GPU debugger, so keep frames in lock-step +}; + +class OGLTextureMailboxException : public std::runtime_error { +public: + using std::runtime_error::runtime_error; +}; + +/// This mailbox is different in that it will never discard rendered frames +class OGLVideoDumpingMailbox : public OGLTextureMailbox { +public: + void LoadPresentFrame() override; + Frontend::Frame* GetRenderFrame() override; + Frontend::Frame* TryGetPresentFrame(int timeout_ms) override; + +public: + bool quit = false; +}; + +} // namespace OpenGL diff --git a/src/video_core/renderer_opengl/gl_texture_runtime.cpp b/src/video_core/renderer_opengl/gl_texture_runtime.cpp index 2786b9d671..e7ca8179fb 100644 --- a/src/video_core/renderer_opengl/gl_texture_runtime.cpp +++ b/src/video_core/renderer_opengl/gl_texture_runtime.cpp @@ -5,10 +5,10 @@ #include "common/scope_exit.h" #include "common/settings.h" #include "video_core/custom_textures/material.h" -#include "video_core/regs.h" #include "video_core/renderer_base.h" #include "video_core/renderer_opengl/gl_driver.h" #include "video_core/renderer_opengl/gl_state.h" +#include "video_core/renderer_opengl/gl_texture_mailbox.h" #include "video_core/renderer_opengl/gl_texture_runtime.h" #include "video_core/renderer_opengl/pica_to_gl.h" @@ -22,6 +22,8 @@ using VideoCore::SurfaceFlagBits; using VideoCore::SurfaceType; using VideoCore::TextureType; +constexpr GLenum TEMP_UNIT = GL_TEXTURE15; + constexpr FormatTuple DEFAULT_TUPLE = {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE}; static constexpr std::array DEPTH_TUPLES = {{ @@ -58,13 +60,6 @@ static constexpr std::array CUSTOM_TUPLES = {{ {GL_COMPRESSED_RGBA_ASTC_8x6, GL_COMPRESSED_RGBA_ASTC_8x6, GL_UNSIGNED_BYTE}, }}; -struct FramebufferInfo { - GLuint color; - GLuint depth; - u32 color_level; - u32 depth_level; -}; - [[nodiscard]] GLbitfield MakeBufferMask(SurfaceType type) { switch (type) { case SurfaceType::Color: @@ -128,9 +123,8 @@ TextureRuntime::TextureRuntime(const Driver& driver_, VideoCore::RendererBase& r TextureRuntime::~TextureRuntime() = default; -void TextureRuntime::Reset() { - alloc_cache.clear(); - framebuffer_cache.clear(); +u32 TextureRuntime::RemoveThreshold() { + return SWAP_CHAIN_SIZE; } bool TextureRuntime::NeedsConversion(VideoCore::PixelFormat pixel_format) const { @@ -151,6 +145,10 @@ VideoCore::StagingData TextureRuntime::FindStaging(u32 size, bool upload) { } const FormatTuple& TextureRuntime::GetFormatTuple(PixelFormat pixel_format) const { + if (pixel_format == PixelFormat::Invalid) { + return DEFAULT_TUPLE; + } + const auto type = GetFormatType(pixel_format); const std::size_t format_index = static_cast(pixel_format); @@ -171,74 +169,6 @@ const FormatTuple& TextureRuntime::GetFormatTuple(VideoCore::CustomPixelFormat p return CUSTOM_TUPLES[format_index]; } -void TextureRuntime::Recycle(const HostTextureTag tag, Allocation&& alloc) { - alloc_cache.emplace(tag, std::move(alloc)); -} - -Allocation TextureRuntime::Allocate(const VideoCore::SurfaceParams& params, - const VideoCore::Material* material) { - const GLenum target = params.texture_type == VideoCore::TextureType::CubeMap - ? GL_TEXTURE_CUBE_MAP - : GL_TEXTURE_2D; - const bool is_custom = material != nullptr; - const bool has_normal = material && material->Map(MapType::Normal); - const auto& tuple = - is_custom ? GetFormatTuple(params.custom_format) : GetFormatTuple(params.pixel_format); - const HostTextureTag key = { - .width = params.width, - .height = params.height, - .levels = params.levels, - .res_scale = params.res_scale, - .tuple = tuple, - .type = params.texture_type, - .is_custom = is_custom, - .has_normal = has_normal, - }; - - if (auto it = alloc_cache.find(key); it != alloc_cache.end()) { - auto alloc{std::move(it->second)}; - alloc_cache.erase(it); - return alloc; - } - - const GLuint old_tex = OpenGLState::GetCurState().texture_units[0].texture_2d; - glActiveTexture(GL_TEXTURE0); - - std::array textures{}; - std::array handles{}; - - textures[0] = MakeHandle(target, params.width, params.height, params.levels, tuple, - params.DebugName(false)); - handles.fill(textures[0].handle); - - if (params.res_scale != 1) { - const u32 scaled_width = is_custom ? params.width : params.GetScaledWidth(); - const u32 scaled_height = is_custom ? params.height : params.GetScaledHeight(); - const auto& scaled_tuple = is_custom ? GetFormatTuple(PixelFormat::RGBA8) : tuple; - textures[1] = MakeHandle(target, scaled_width, scaled_height, params.levels, scaled_tuple, - params.DebugName(true, is_custom)); - handles[1] = textures[1].handle; - } - if (has_normal) { - textures[2] = MakeHandle(target, params.width, params.height, params.levels, tuple, - params.DebugName(true, is_custom)); - handles[2] = textures[2].handle; - } - - glBindTexture(GL_TEXTURE_2D, old_tex); - - return Allocation{ - .textures = std::move(textures), - .handles = std::move(handles), - .tuple = tuple, - .width = params.width, - .height = params.height, - .levels = params.levels, - .res_scale = params.res_scale, - .is_custom = is_custom, - }; -} - bool TextureRuntime::Reinterpret(Surface& source, Surface& dest, const VideoCore::TextureBlit& blit) { const PixelFormat src_format = source.pixel_format; @@ -353,40 +283,90 @@ void TextureRuntime::GenerateMipmaps(Surface& surface) { } Surface::Surface(TextureRuntime& runtime_, const VideoCore::SurfaceParams& params) - : SurfaceBase{params}, driver{&runtime_.GetDriver()}, runtime{&runtime_} { + : SurfaceBase{params}, driver{&runtime_.GetDriver()}, runtime{&runtime_}, + tuple{runtime->GetFormatTuple(pixel_format)} { if (pixel_format == PixelFormat::Invalid) { return; } - alloc = runtime->Allocate(params); + glActiveTexture(TEMP_UNIT); + const GLenum target = + texture_type == VideoCore::TextureType::CubeMap ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D; + + textures[0] = MakeHandle(target, width, height, levels, tuple, DebugName(false)); + if (res_scale != 1) { + textures[1] = MakeHandle(target, GetScaledWidth(), GetScaledHeight(), levels, tuple, + DebugName(true, false)); + } } -Surface::~Surface() { - if (pixel_format == PixelFormat::Invalid || !alloc) { +Surface::Surface(TextureRuntime& runtime, const VideoCore::SurfaceBase& surface, + const VideoCore::Material* mat) + : SurfaceBase{surface}, tuple{runtime.GetFormatTuple(mat->format)} { + if (mat && !driver->IsCustomFormatSupported(mat->format)) { return; } - runtime->Recycle(MakeTag(), std::move(alloc)); + + glActiveTexture(TEMP_UNIT); + const GLenum target = + texture_type == VideoCore::TextureType::CubeMap ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D; + + custom_format = mat->format; + material = mat; + + textures[0] = MakeHandle(target, mat->width, mat->height, levels, tuple, DebugName(false)); + if (res_scale != 1) { + textures[1] = MakeHandle(target, mat->width, mat->height, levels, DEFAULT_TUPLE, + DebugName(true, true)); + } + const bool has_normal = mat->Map(MapType::Normal); + if (has_normal) { + textures[2] = + MakeHandle(target, mat->width, mat->height, levels, tuple, DebugName(true, true)); + } +} + +Surface::~Surface() = default; + +GLuint Surface::Handle(u32 index) const noexcept { + if (!textures[index].handle) { + return textures[0].handle; + } + return textures[index].handle; +} + +GLuint Surface::CopyHandle() noexcept { + if (!copy_texture.handle) { + copy_texture = MakeHandle(GL_TEXTURE_2D, GetScaledWidth(), GetScaledHeight(), levels, tuple, + DebugName(true)); + } + + for (u32 level = 0; level < levels; level++) { + const u32 width = GetScaledWidth() >> level; + const u32 height = GetScaledHeight() >> level; + glCopyImageSubData(Handle(1), GL_TEXTURE_2D, level, 0, 0, 0, copy_texture.handle, + GL_TEXTURE_2D, level, 0, 0, 0, width, height, 1); + } + + return copy_texture.handle; } void Surface::Upload(const VideoCore::BufferTextureCopy& upload, const VideoCore::StagingData& staging) { ASSERT(stride * GetFormatBytesPerPixel(pixel_format) % 4 == 0); - const GLuint old_tex = OpenGLState::GetCurState().texture_units[0].texture_2d; const u32 unscaled_width = upload.texture_rect.GetWidth(); const u32 unscaled_height = upload.texture_rect.GetHeight(); glPixelStorei(GL_UNPACK_ROW_LENGTH, unscaled_width); - glActiveTexture(GL_TEXTURE0); + glActiveTexture(TEMP_UNIT); glBindTexture(GL_TEXTURE_2D, Handle(0)); - const auto& tuple = alloc.tuple; glTexSubImage2D(GL_TEXTURE_2D, upload.texture_level, upload.texture_rect.left, upload.texture_rect.bottom, unscaled_width, unscaled_height, tuple.format, tuple.type, staging.mapped.data()); glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); - glBindTexture(GL_TEXTURE_2D, old_tex); const VideoCore::TextureBlit blit = { .src_level = upload.texture_level, @@ -400,14 +380,12 @@ void Surface::Upload(const VideoCore::BufferTextureCopy& upload, } void Surface::UploadCustom(const VideoCore::Material* material, u32 level) { - const GLuint old_tex = OpenGLState::GetCurState().texture_units[0].texture_2d; - const auto& tuple = alloc.tuple; const u32 width = material->width; const u32 height = material->height; const auto color = material->textures[0]; const Common::Rectangle filter_rect{0U, height, width, 0U}; - glActiveTexture(GL_TEXTURE0); + glActiveTexture(TEMP_UNIT); glPixelStorei(GL_UNPACK_ROW_LENGTH, width); const auto upload = [&](u32 index, VideoCore::CustomTexture* texture) { @@ -440,7 +418,6 @@ void Surface::UploadCustom(const VideoCore::Material* material, u32 level) { } glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); - glBindTexture(GL_TEXTURE_2D, old_tex); } void Surface::Download(const VideoCore::BufferTextureCopy& download, @@ -491,6 +468,7 @@ bool Surface::DownloadWithoutFbo(const VideoCore::BufferTextureCopy& download, const auto& tuple = runtime->GetFormatTuple(pixel_format); const u32 unscaled_width = download.texture_rect.GetWidth(); + glActiveTexture(TEMP_UNIT); glPixelStorei(GL_PACK_ROW_LENGTH, unscaled_width); SCOPE_EXIT({ glPixelStorei(GL_PACK_ROW_LENGTH, 0); }); @@ -541,27 +519,24 @@ void Surface::Attach(GLenum target, u32 level, u32 layer, bool scaled) { } } -bool Surface::Swap(const VideoCore::Material* mat) { - const VideoCore::CustomPixelFormat format{mat->format}; - if (!driver->IsCustomFormatSupported(format)) { - return false; +void Surface::ScaleUp(u32 new_scale) { + if (res_scale == new_scale || new_scale == 1) { + return; } - runtime->Recycle(MakeTag(), std::move(alloc)); - SurfaceParams params = *this; - params.width = mat->width; - params.height = mat->height; - params.custom_format = mat->format; - alloc = runtime->Allocate(params, mat); + res_scale = new_scale; + textures[1] = MakeHandle(GL_TEXTURE_2D, GetScaledWidth(), GetScaledHeight(), levels, tuple, + DebugName(true)); - LOG_DEBUG(Render_OpenGL, "Swapped {}x{} {} surface at address {:#x} to {}x{} {}", - GetScaledWidth(), GetScaledHeight(), VideoCore::PixelFormatAsString(pixel_format), - addr, width, height, VideoCore::CustomPixelFormatAsString(format)); - - custom_format = format; - material = mat; - - return true; + for (u32 level = 0; level < levels; level++) { + const VideoCore::TextureBlit blit = { + .src_level = level, + .dst_level = level, + .src_rect = GetRect(level), + .dst_rect = GetScaledRect(level), + }; + BlitScale(blit, true); + } } u32 Surface::GetInternalBytesPerPixel() const { @@ -591,27 +566,11 @@ void Surface::BlitScale(const VideoCore::TextureBlit& blit, bool up_scale) { blit.dst_rect.right, blit.dst_rect.top, buffer_mask, filter); } -HostTextureTag Surface::MakeTag() const noexcept { - return HostTextureTag{ - .width = alloc.width, - .height = alloc.height, - .levels = alloc.levels, - .res_scale = alloc.res_scale, - .tuple = alloc.tuple, - .type = texture_type, - .is_custom = alloc.is_custom, - .has_normal = HasNormalMap(), - }; -} +Framebuffer::Framebuffer(TextureRuntime& runtime, const VideoCore::FramebufferParams& params, + const Surface* color, const Surface* depth) + : VideoCore::FramebufferParams{params}, res_scale{color ? color->res_scale + : (depth ? depth->res_scale : 1u)} { -Framebuffer::Framebuffer(TextureRuntime& runtime, const Surface* color, u32 color_level, - const Surface* depth_stencil, u32 depth_level, const Pica::Regs& regs, - Common::Rectangle surfaces_rect) - : VideoCore::FramebufferBase{regs, color, color_level, - depth_stencil, depth_level, surfaces_rect} { - - const bool shadow_rendering = regs.framebuffer.IsShadowRendering(); - const bool has_stencil = regs.framebuffer.HasStencil(); if (shadow_rendering && !color) { return; } @@ -619,33 +578,15 @@ Framebuffer::Framebuffer(TextureRuntime& runtime, const Surface* color, u32 colo if (color) { attachments[0] = color->Handle(); } - if (depth_stencil) { - attachments[1] = depth_stencil->Handle(); + if (depth) { + attachments[1] = depth->Handle(); } - const FramebufferInfo info = { - .color = attachments[0], - .depth = attachments[1], - .color_level = color_level, - .depth_level = depth_level, - }; - - const u64 hash = Common::ComputeHash64(&info, sizeof(FramebufferInfo)); - auto [it, new_framebuffer] = runtime.framebuffer_cache.try_emplace(hash); - - if (!new_framebuffer) { - handle = it->second.handle; - return; - } - - const GLuint old_fbo = OpenGLState::GetCurState().draw.draw_framebuffer; - - OGLFramebuffer& framebuffer = it->second; framebuffer.Create(); - handle = it->second.handle; - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer.handle); - SCOPE_EXIT({ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, old_fbo); }); + OpenGLState state = OpenGLState::GetCurState(); + state.draw.draw_framebuffer = framebuffer.handle; + state.Apply(); if (shadow_rendering) { glFramebufferParameteri(GL_DRAW_FRAMEBUFFER, GL_FRAMEBUFFER_DEFAULT_WIDTH, @@ -658,13 +599,13 @@ Framebuffer::Framebuffer(TextureRuntime& runtime, const Surface* color, u32 colo } else { glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color ? color->Handle() : 0, color_level); - if (depth_stencil) { - if (has_stencil) { + if (depth) { + if (depth->pixel_format == PixelFormat::D24S8) { glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, - GL_TEXTURE_2D, depth_stencil->Handle(), depth_level); + GL_TEXTURE_2D, depth->Handle(), depth_level); } else { glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, - depth_stencil->Handle(), depth_level); + depth->Handle(), depth_level); glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0); } diff --git a/src/video_core/renderer_opengl/gl_texture_runtime.h b/src/video_core/renderer_opengl/gl_texture_runtime.h index 654e321ebb..02914624b5 100644 --- a/src/video_core/renderer_opengl/gl_texture_runtime.h +++ b/src/video_core/renderer_opengl/gl_texture_runtime.h @@ -27,46 +27,6 @@ struct FormatTuple { } }; -struct HostTextureTag { - u32 width; - u32 height; - u32 levels; - u32 res_scale; - FormatTuple tuple; - VideoCore::TextureType type; - bool is_custom; - bool has_normal; - - bool operator==(const HostTextureTag& other) const noexcept { - return std::tie(tuple, type, width, height, levels, res_scale, is_custom, has_normal) == - std::tie(other.tuple, other.type, other.width, other.height, other.levels, - other.res_scale, other.is_custom, other.has_normal); - } - - struct Hash { - const u64 operator()(const HostTextureTag& tag) const { - return Common::ComputeHash64(&tag, sizeof(HostTextureTag)); - } - }; -}; -static_assert(std::has_unique_object_representations_v, - "HostTextureTag is not suitable for hashing!"); - -struct Allocation { - std::array textures; - std::array handles; - FormatTuple tuple; - u32 width; - u32 height; - u32 levels; - u32 res_scale; - bool is_custom; - - operator bool() const noexcept { - return textures[0].handle; - } -}; - class Surface; class Driver; @@ -82,8 +42,8 @@ public: explicit TextureRuntime(const Driver& driver, VideoCore::RendererBase& renderer); ~TextureRuntime(); - /// Clears all cached runtime resources - void Reset(); + /// Returns the removal threshold ticks for the garbage collector + u32 RemoveThreshold(); /// Returns true if the provided pixel format cannot be used natively by the runtime. bool NeedsConversion(VideoCore::PixelFormat pixel_format) const; @@ -111,13 +71,6 @@ public: void GenerateMipmaps(Surface& surface); private: - /// Takes back ownership of the allocation for recycling - void Recycle(const HostTextureTag tag, Allocation&& alloc); - - /// Allocates a texture with the specified dimentions and format - Allocation Allocate(const VideoCore::SurfaceParams& params, - const VideoCore::Material* material = nullptr); - /// Returns the OpenGL driver class const Driver& GetDriver() const { return driver; @@ -127,8 +80,6 @@ private: const Driver& driver; BlitHelper blit_helper; std::vector staging_buffer; - std::unordered_multimap alloc_cache; - std::unordered_map> framebuffer_cache; std::array draw_fbos; std::array read_fbos; }; @@ -136,6 +87,8 @@ private: class Surface : public VideoCore::SurfaceBase { public: explicit Surface(TextureRuntime& runtime, const VideoCore::SurfaceParams& params); + explicit Surface(TextureRuntime& runtime, const VideoCore::SurfaceBase& surface, + const VideoCore::Material* material); ~Surface(); Surface(const Surface&) = delete; @@ -144,13 +97,15 @@ public: Surface(Surface&& o) noexcept = default; Surface& operator=(Surface&& o) noexcept = default; - [[nodiscard]] GLuint Handle(u32 index = 1) const noexcept { - return alloc.handles[index]; + [[nodiscard]] const FormatTuple& Tuple() const noexcept { + return tuple; } - [[nodiscard]] const FormatTuple& Tuple() const noexcept { - return alloc.tuple; - } + /// Returns the texture handle at index, otherwise the first one if not valid. + GLuint Handle(u32 index = 1) const noexcept; + + /// Returns a copy of the upscaled texture handle, used for feedback loops. + GLuint CopyHandle() noexcept; /// Uploads pixel data in staging to a rectangle region of the surface texture void Upload(const VideoCore::BufferTextureCopy& upload, const VideoCore::StagingData& staging); @@ -165,8 +120,8 @@ public: /// Attaches a handle of surface to the specified framebuffer target void Attach(GLenum target, u32 level, u32 layer, bool scaled = true); - /// Swaps the internal allocation to match the provided material - bool Swap(const VideoCore::Material* material); + /// Scales up the surface to match the new resolution scale. + void ScaleUp(u32 new_scale); /// Returns the bpp of the internal surface format u32 GetInternalBytesPerPixel() const; @@ -179,24 +134,32 @@ private: bool DownloadWithoutFbo(const VideoCore::BufferTextureCopy& download, const VideoCore::StagingData& staging); - /// Returns the texture tag of the current allocation - HostTextureTag MakeTag() const noexcept; - private: const Driver* driver; TextureRuntime* runtime; - Allocation alloc{}; + std::array textures; + OGLTexture copy_texture; + FormatTuple tuple; }; -class Framebuffer : public VideoCore::FramebufferBase { +class Framebuffer : public VideoCore::FramebufferParams { public: - explicit Framebuffer(TextureRuntime& runtime, const Surface* color, u32 color_level, - const Surface* depth_stencil, u32 depth_level, const Pica::Regs& regs, - Common::Rectangle surfaces_rect); + explicit Framebuffer(TextureRuntime& runtime, const VideoCore::FramebufferParams& params, + const Surface* color, const Surface* depth_stencil); ~Framebuffer(); + Framebuffer(const Framebuffer&) = delete; + Framebuffer& operator=(const Framebuffer&) = delete; + + Framebuffer(Framebuffer&& o) noexcept = default; + Framebuffer& operator=(Framebuffer&& o) noexcept = default; + + [[nodiscard]] u32 Scale() const noexcept { + return res_scale; + } + [[nodiscard]] GLuint Handle() const noexcept { - return handle; + return framebuffer.handle; } [[nodiscard]] GLuint Attachment(VideoCore::SurfaceType type) const noexcept { @@ -208,8 +171,9 @@ public: } private: + u32 res_scale{1}; std::array attachments{}; - GLuint handle{}; + OGLFramebuffer framebuffer; }; class Sampler { diff --git a/src/video_core/renderer_opengl/pica_to_gl.h b/src/video_core/renderer_opengl/pica_to_gl.h index e4c14f186b..ef7578c497 100644 --- a/src/video_core/renderer_opengl/pica_to_gl.h +++ b/src/video_core/renderer_opengl/pica_to_gl.h @@ -9,6 +9,7 @@ #include "common/assert.h" #include "common/logging/log.h" #include "core/core.h" +#include "core/telemetry_session.h" #include "video_core/regs_framebuffer.h" #include "video_core/regs_lighting.h" #include "video_core/regs_texturing.h" diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index 9d48e23caa..be778c764c 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -2,20 +2,18 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include #include "common/logging/log.h" #include "common/microprofile.h" #include "common/settings.h" #include "core/core.h" -#include "core/dumping/backend.h" #include "core/frontend/emu_window.h" #include "core/frontend/framebuffer_layout.h" #include "core/hw/hw.h" #include "core/hw/lcd.h" #include "core/memory.h" -#include "video_core/rasterizer_interface.h" #include "video_core/renderer_opengl/gl_shader_util.h" #include "video_core/renderer_opengl/gl_state.h" +#include "video_core/renderer_opengl/gl_texture_mailbox.h" #include "video_core/renderer_opengl/gl_vars.h" #include "video_core/renderer_opengl/post_processing_opengl.h" #include "video_core/renderer_opengl/renderer_opengl.h" @@ -31,232 +29,6 @@ namespace OpenGL { MICROPROFILE_DEFINE(OpenGL_RenderFrame, "OpenGL", "Render Frame", MP_RGB(128, 128, 64)); MICROPROFILE_DEFINE(OpenGL_WaitPresent, "OpenGL", "Wait For Present", MP_RGB(128, 128, 128)); -// If the size of this is too small, it ends up creating a soft cap on FPS as the renderer will have -// to wait on available presentation frames. There doesn't seem to be much of a downside to a larger -// number but 9 swap textures at 60FPS presentation allows for 800% speed so thats probably fine -#ifdef ANDROID -// Reduce the size of swap_chain, since the UI only allows upto 200% speed. -constexpr std::size_t SWAP_CHAIN_SIZE = 6; -#else -constexpr std::size_t SWAP_CHAIN_SIZE = 9; -#endif - -class OGLTextureMailboxException : public std::runtime_error { -public: - using std::runtime_error::runtime_error; -}; - -class OGLTextureMailbox : public Frontend::TextureMailbox { -public: - std::mutex swap_chain_lock; - std::condition_variable free_cv; - std::condition_variable present_cv; - std::array swap_chain{}; - std::queue free_queue{}; - std::deque present_queue{}; - Frontend::Frame* previous_frame = nullptr; - - OGLTextureMailbox(bool has_debug_tool_ = false) : has_debug_tool{has_debug_tool_} { - for (auto& frame : swap_chain) { - free_queue.push(&frame); - } - } - - ~OGLTextureMailbox() override { - // lock the mutex and clear out the present and free_queues and notify any people who are - // blocked to prevent deadlock on shutdown - std::scoped_lock lock(swap_chain_lock); - std::queue().swap(free_queue); - present_queue.clear(); - present_cv.notify_all(); - free_cv.notify_all(); - } - - void ReloadPresentFrame(Frontend::Frame* frame, u32 height, u32 width) override { - frame->present.Release(); - frame->present.Create(); - GLint previous_draw_fbo{}; - glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &previous_draw_fbo); - glBindFramebuffer(GL_FRAMEBUFFER, frame->present.handle); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, - frame->color.handle); - if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { - LOG_CRITICAL(Render_OpenGL, "Failed to recreate present FBO!"); - } - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, previous_draw_fbo); - frame->color_reloaded = false; - } - - void ReloadRenderFrame(Frontend::Frame* frame, u32 width, u32 height) override { - OpenGLState prev_state = OpenGLState::GetCurState(); - OpenGLState state = OpenGLState::GetCurState(); - - // Recreate the color texture attachment - frame->color.Release(); - frame->color.Create(); - state.renderbuffer = frame->color.handle; - state.Apply(); - glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, width, height); - - // Recreate the FBO for the render target - frame->render.Release(); - frame->render.Create(); - state.draw.read_framebuffer = frame->render.handle; - state.draw.draw_framebuffer = frame->render.handle; - state.Apply(); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, - frame->color.handle); - if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { - LOG_CRITICAL(Render_OpenGL, "Failed to recreate render FBO!"); - } - prev_state.Apply(); - frame->width = width; - frame->height = height; - frame->color_reloaded = true; - } - - Frontend::Frame* GetRenderFrame() override { - std::unique_lock lock(swap_chain_lock); - - // If theres no free frames, we will reuse the oldest render frame - if (free_queue.empty()) { - auto frame = present_queue.back(); - present_queue.pop_back(); - return frame; - } - - Frontend::Frame* frame = free_queue.front(); - free_queue.pop(); - return frame; - } - - void ReleaseRenderFrame(Frontend::Frame* frame) override { - std::unique_lock lock(swap_chain_lock); - present_queue.push_front(frame); - present_cv.notify_one(); - - DebugNotifyNextFrame(); - } - - // This is virtual as it is to be overriden in OGLVideoDumpingMailbox below. - virtual void LoadPresentFrame() { - // free the previous frame and add it back to the free queue - if (previous_frame) { - free_queue.push(previous_frame); - free_cv.notify_one(); - } - - // the newest entries are pushed to the front of the queue - Frontend::Frame* frame = present_queue.front(); - present_queue.pop_front(); - // remove all old entries from the present queue and move them back to the free_queue - for (auto f : present_queue) { - free_queue.push(f); - } - present_queue.clear(); - previous_frame = frame; - } - - Frontend::Frame* TryGetPresentFrame(int timeout_ms) override { - DebugWaitForNextFrame(); - - std::unique_lock lock(swap_chain_lock); - // wait for new entries in the present_queue - present_cv.wait_for(lock, std::chrono::milliseconds(timeout_ms), - [&] { return !present_queue.empty(); }); - if (present_queue.empty()) { - // timed out waiting for a frame to draw so return the previous frame - return previous_frame; - } - - LoadPresentFrame(); - return previous_frame; - } - -private: - std::mutex debug_synch_mutex; - std::condition_variable debug_synch_condition; - std::atomic_int frame_for_debug{}; - const bool has_debug_tool; // When true, using a GPU debugger, so keep frames in lock-step - - /// Signal that a new frame is available (called from GPU thread) - void DebugNotifyNextFrame() { - if (!has_debug_tool) { - return; - } - frame_for_debug++; - std::lock_guard lock{debug_synch_mutex}; - debug_synch_condition.notify_one(); - } - - /// Wait for a new frame to be available (called from presentation thread) - void DebugWaitForNextFrame() { - if (!has_debug_tool) { - return; - } - const int last_frame = frame_for_debug; - std::unique_lock lock{debug_synch_mutex}; - debug_synch_condition.wait(lock, - [this, last_frame] { return frame_for_debug > last_frame; }); - } -}; - -/// This mailbox is different in that it will never discard rendered frames -class OGLVideoDumpingMailbox : public OGLTextureMailbox { -public: - bool quit = false; - - Frontend::Frame* GetRenderFrame() override { - std::unique_lock lock(swap_chain_lock); - - // If theres no free frames, we will wait until one shows up - if (free_queue.empty()) { - free_cv.wait(lock, [&] { return (!free_queue.empty() || quit); }); - if (quit) { - throw OGLTextureMailboxException("VideoDumpingMailbox quitting"); - } - - if (free_queue.empty()) { - LOG_CRITICAL(Render_OpenGL, "Could not get free frame"); - return nullptr; - } - } - - Frontend::Frame* frame = free_queue.front(); - free_queue.pop(); - return frame; - } - - void LoadPresentFrame() override { - // free the previous frame and add it back to the free queue - if (previous_frame) { - free_queue.push(previous_frame); - free_cv.notify_one(); - } - - Frontend::Frame* frame = present_queue.back(); - present_queue.pop_back(); - previous_frame = frame; - - // Do not remove entries from the present_queue, as video dumping would require - // that we preserve all frames - } - - Frontend::Frame* TryGetPresentFrame(int timeout_ms) override { - std::unique_lock lock(swap_chain_lock); - // wait for new entries in the present_queue - present_cv.wait_for(lock, std::chrono::milliseconds(timeout_ms), - [&] { return !present_queue.empty(); }); - if (present_queue.empty()) { - // timed out waiting for a frame - return nullptr; - } - - LoadPresentFrame(); - return previous_frame; - } -}; - /** * Vertex structure that the drawn screen rectangles are composed of. */ @@ -306,7 +78,8 @@ static std::array MakeOrthographicMatrix(const float width, cons RendererOpenGL::RendererOpenGL(Core::System& system, Frontend::EmuWindow& window, Frontend::EmuWindow* secondary_window) : VideoCore::RendererBase{system, window, secondary_window}, driver{system.TelemetrySession()}, - frame_dumper{system, window} { + rasterizer{system.Memory(), system.CustomTexManager(), *this, driver}, frame_dumper{system, + window} { const bool has_debug_tool = driver.HasDebugTool(); window.mailbox = std::make_unique(has_debug_tool); if (secondary_window) { @@ -314,8 +87,6 @@ RendererOpenGL::RendererOpenGL(Core::System& system, Frontend::EmuWindow& window } frame_dumper.mailbox = std::make_unique(); InitOpenGLObjects(); - rasterizer = std::make_unique(system.Memory(), system.CustomTexManager(), - *this, driver); } RendererOpenGL::~RendererOpenGL() = default; @@ -349,8 +120,7 @@ void RendererOpenGL::SwapBuffers() { EndFrame(); prev_state.Apply(); - - rasterizer->TickFrame(); + rasterizer.TickFrame(); } void RendererOpenGL::RenderScreenshot() { @@ -501,8 +271,8 @@ void RendererOpenGL::LoadFBToScreenInfo(const GPU::Regs::FramebufferConfig& fram // only allows rows to have a memory alignement of 4. ASSERT(pixel_stride % 4 == 0); - if (!Rasterizer()->AccelerateDisplay(framebuffer, framebuffer_addr, - static_cast(pixel_stride), screen_info)) { + if (!rasterizer.AccelerateDisplay(framebuffer, framebuffer_addr, static_cast(pixel_stride), + screen_info)) { // Reset the screen info's display texture to its own permanent texture screen_info.display_texture = screen_info.texture.resource.handle; screen_info.display_texcoords = Common::Rectangle(0.f, 0.f, 1.f, 1.f); @@ -559,8 +329,15 @@ void RendererOpenGL::InitOpenGLObjects() { glClearColor(Settings::values.bg_red.GetValue(), Settings::values.bg_green.GetValue(), Settings::values.bg_blue.GetValue(), 0.0f); - filter_sampler.Create(); - ReloadSampler(); + for (size_t i = 0; i < samplers.size(); i++) { + samplers[i].Create(); + glSamplerParameteri(samplers[i].handle, GL_TEXTURE_MIN_FILTER, + i == 0 ? GL_NEAREST : GL_LINEAR); + glSamplerParameteri(samplers[i].handle, GL_TEXTURE_MAG_FILTER, + i == 0 ? GL_NEAREST : GL_LINEAR); + glSamplerParameteri(samplers[i].handle, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glSamplerParameteri(samplers[i].handle, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } ReloadShader(); @@ -608,15 +385,6 @@ void RendererOpenGL::InitOpenGLObjects() { state.Apply(); } -void RendererOpenGL::ReloadSampler() { - glSamplerParameteri(filter_sampler.handle, GL_TEXTURE_MIN_FILTER, - Settings::values.filter_mode ? GL_LINEAR : GL_NEAREST); - glSamplerParameteri(filter_sampler.handle, GL_TEXTURE_MAG_FILTER, - Settings::values.filter_mode ? GL_LINEAR : GL_NEAREST); - glSamplerParameteri(filter_sampler.handle, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glSamplerParameteri(filter_sampler.handle, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); -} - void RendererOpenGL::ReloadShader() { // Link shaders and get variable locations std::string shader_data; @@ -793,13 +561,14 @@ void RendererOpenGL::DrawSingleScreen(const ScreenInfo& screen_info, float x, fl } const u32 scale_factor = GetResolutionScaleFactor(); + const GLuint sampler = samplers[Settings::values.filter_mode.GetValue()].handle; glUniform4f(uniform_i_resolution, static_cast(screen_info.texture.width * scale_factor), static_cast(screen_info.texture.height * scale_factor), 1.0f / static_cast(screen_info.texture.width * scale_factor), 1.0f / static_cast(screen_info.texture.height * scale_factor)); glUniform4f(uniform_o_resolution, h, w, 1.0f / h, 1.0f / w); state.texture_units[0].texture_2d = screen_info.display_texture; - state.texture_units[0].sampler = filter_sampler.handle; + state.texture_units[0].sampler = sampler; state.Apply(); glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices.data()); @@ -862,6 +631,7 @@ void RendererOpenGL::DrawSingleScreenStereo(const ScreenInfo& screen_info_l, } const u32 scale_factor = GetResolutionScaleFactor(); + const GLuint sampler = samplers[Settings::values.filter_mode.GetValue()].handle; glUniform4f(uniform_i_resolution, static_cast(screen_info_l.texture.width * scale_factor), static_cast(screen_info_l.texture.height * scale_factor), @@ -870,8 +640,8 @@ void RendererOpenGL::DrawSingleScreenStereo(const ScreenInfo& screen_info_l, glUniform4f(uniform_o_resolution, h, w, 1.0f / h, 1.0f / w); state.texture_units[0].texture_2d = screen_info_l.display_texture; state.texture_units[1].texture_2d = screen_info_r.display_texture; - state.texture_units[0].sampler = filter_sampler.handle; - state.texture_units[1].sampler = filter_sampler.handle; + state.texture_units[0].sampler = sampler; + state.texture_units[1].sampler = sampler; state.Apply(); glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices.data()); @@ -894,11 +664,6 @@ void RendererOpenGL::DrawScreens(const Layout::FramebufferLayout& layout, bool f Settings::values.bg_blue.GetValue(), 0.0f); } - if (settings.sampler_update_requested.exchange(false)) { - // Set the new filtering mode for the sampler - ReloadSampler(); - } - if (settings.shader_update_requested.exchange(false)) { // Update fragment shader before drawing shader.Release(); @@ -1119,7 +884,7 @@ void RendererOpenGL::TryPresent(int timeout_ms, bool is_secondary) { void RendererOpenGL::PrepareVideoDumping() { auto* mailbox = static_cast(frame_dumper.mailbox.get()); { - std::unique_lock lock(mailbox->swap_chain_lock); + std::scoped_lock lock{mailbox->swap_chain_lock}; mailbox->quit = false; } frame_dumper.StartDumping(); @@ -1129,14 +894,14 @@ void RendererOpenGL::CleanupVideoDumping() { frame_dumper.StopDumping(); auto* mailbox = static_cast(frame_dumper.mailbox.get()); { - std::unique_lock lock(mailbox->swap_chain_lock); + std::scoped_lock lock{mailbox->swap_chain_lock}; mailbox->quit = true; } mailbox->free_cv.notify_one(); } void RendererOpenGL::Sync() { - rasterizer->SyncEntireState(); + rasterizer.SyncEntireState(); } } // namespace OpenGL diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h index 088a817f25..5e005e8dce 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.h +++ b/src/video_core/renderer_opengl/renderer_opengl.h @@ -21,20 +21,6 @@ namespace Core { class System; } -namespace Frontend { - -struct Frame { - u32 width{}; /// Width of the frame (to detect resize) - u32 height{}; /// Height of the frame - bool color_reloaded = false; /// Texture attachment was recreated (ie: resized) - OpenGL::OGLRenderbuffer color{}; /// Buffer shared between the render/present FBO - OpenGL::OGLFramebuffer render{}; /// FBO created on the render thread - OpenGL::OGLFramebuffer present{}; /// FBO created on the present thread - GLsync render_fence{}; /// Fence created on the render thread - GLsync present_fence{}; /// Fence created on the presentation thread -}; -} // namespace Frontend - namespace OpenGL { /// Structure used for storing information about the textures for each 3DS screen @@ -60,8 +46,8 @@ public: Frontend::EmuWindow* secondary_window); ~RendererOpenGL() override; - [[nodiscard]] VideoCore::RasterizerInterface* Rasterizer() const override { - return rasterizer.get(); + [[nodiscard]] VideoCore::RasterizerInterface* Rasterizer() override { + return &rasterizer; } void SwapBuffers() override; @@ -72,7 +58,6 @@ public: private: void InitOpenGLObjects(); - void ReloadSampler(); void ReloadShader(); void PrepareRendertarget(); void RenderScreenshot(); @@ -101,17 +86,17 @@ private: private: Driver driver; + RasterizerOpenGL rasterizer; OpenGLState state; - std::unique_ptr rasterizer; // OpenGL object IDs OGLVertexArray vertex_array; OGLBuffer vertex_buffer; OGLProgram shader; OGLFramebuffer screenshot_framebuffer; - OGLSampler filter_sampler; + std::array samplers; - /// Display information for top and bottom screens respectively + // Display information for top and bottom screens respectively std::array screen_infos; // Shader uniform location indices diff --git a/src/video_core/renderer_software/renderer_software.cpp b/src/video_core/renderer_software/renderer_software.cpp index 7194be8e1d..0a8f889506 100644 --- a/src/video_core/renderer_software/renderer_software.cpp +++ b/src/video_core/renderer_software/renderer_software.cpp @@ -13,7 +13,7 @@ namespace SwRenderer { RendererSoftware::RendererSoftware(Core::System& system, Frontend::EmuWindow& window) : VideoCore::RendererBase{system, window, nullptr}, memory{system.Memory()}, - rasterizer{std::make_unique(system.Memory())} {} + rasterizer{system.Memory()} {} RendererSoftware::~RendererSoftware() = default; diff --git a/src/video_core/renderer_software/renderer_software.h b/src/video_core/renderer_software/renderer_software.h index 6ed86ffa3e..2df5550c47 100644 --- a/src/video_core/renderer_software/renderer_software.h +++ b/src/video_core/renderer_software/renderer_software.h @@ -24,8 +24,8 @@ public: explicit RendererSoftware(Core::System& system, Frontend::EmuWindow& window); ~RendererSoftware() override; - [[nodiscard]] VideoCore::RasterizerInterface* Rasterizer() const override { - return rasterizer.get(); + [[nodiscard]] VideoCore::RasterizerInterface* Rasterizer() override { + return &rasterizer; } [[nodiscard]] const ScreenInfo& Screen(VideoCore::ScreenId id) const noexcept { @@ -42,7 +42,7 @@ private: private: Memory::MemorySystem& memory; - std::unique_ptr rasterizer; + RasterizerSoftware rasterizer; std::array screen_infos{}; }; diff --git a/src/video_core/renderer_vulkan/vk_shader_gen.cpp b/src/video_core/renderer_vulkan/vk_shader_gen.cpp index 648aa5d88b..555b17b5b1 100644 --- a/src/video_core/renderer_vulkan/vk_shader_gen.cpp +++ b/src/video_core/renderer_vulkan/vk_shader_gen.cpp @@ -7,6 +7,7 @@ #include "common/bit_set.h" #include "common/logging/log.h" #include "core/core.h" +#include "core/telemetry_session.h" #include "video_core/pica_state.h" #include "video_core/regs_framebuffer.h" #include "video_core/renderer_opengl/gl_shader_decompiler.h" diff --git a/src/video_core/renderer_vulkan/vk_shader_gen_spv.cpp b/src/video_core/renderer_vulkan/vk_shader_gen_spv.cpp index 4e4d50d227..0b9647e3fa 100644 --- a/src/video_core/renderer_vulkan/vk_shader_gen_spv.cpp +++ b/src/video_core/renderer_vulkan/vk_shader_gen_spv.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include "core/core.h" +#include "core/telemetry_session.h" #include "video_core/renderer_vulkan/vk_shader_gen_spv.h" using Pica::FramebufferRegs;