From 5e02be75a38de316f0cf47182edf38ad6e9526fd Mon Sep 17 00:00:00 2001 From: Steveice10 <1269164+Steveice10@users.noreply.github.com> Date: Tue, 13 Feb 2024 21:43:09 -0800 Subject: [PATCH 01/12] renderer_vulkan: Use getToolPropertiesEXT instead of getToolProperties (#7434) getToolProperties is not available until Vulkan 1.3; we need to use the EXT version. --- src/video_core/renderer_vulkan/vk_instance.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/video_core/renderer_vulkan/vk_instance.cpp b/src/video_core/renderer_vulkan/vk_instance.cpp index 2303df203..b9b5aa6ac 100644 --- a/src/video_core/renderer_vulkan/vk_instance.cpp +++ b/src/video_core/renderer_vulkan/vk_instance.cpp @@ -670,7 +670,7 @@ void Instance::CollectToolingInfo() { if (!tooling_info) { return; } - const auto tools = physical_device.getToolProperties(); + const auto tools = physical_device.getToolPropertiesEXT(); for (const vk::PhysicalDeviceToolProperties& tool : tools) { const std::string_view name = tool.name; LOG_INFO(Render_Vulkan, "Attached debugging tool: {}", name); From aa6809e2a8f8264f6c5ed359a0fc458c66368856 Mon Sep 17 00:00:00 2001 From: Steveice10 <1269164+Steveice10@users.noreply.github.com> Date: Thu, 15 Feb 2024 19:38:32 -0800 Subject: [PATCH 02/12] renderer_vulkan: Use no more than target supported version. (#7439) --- src/video_core/renderer_vulkan/vk_instance.cpp | 9 ++++++++- src/video_core/renderer_vulkan/vk_platform.cpp | 18 ++++++++++-------- src/video_core/renderer_vulkan/vk_platform.h | 2 ++ 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_instance.cpp b/src/video_core/renderer_vulkan/vk_instance.cpp index b9b5aa6ac..c0e14e4a2 100644 --- a/src/video_core/renderer_vulkan/vk_instance.cpp +++ b/src/video_core/renderer_vulkan/vk_instance.cpp @@ -4,6 +4,7 @@ #include #include +#include #include "common/assert.h" #include "common/settings.h" @@ -153,6 +154,12 @@ Instance::Instance(Core::TelemetrySession& telemetry, Frontend::EmuWindow& windo physical_device = physical_devices[physical_device_index]; available_extensions = GetSupportedExtensions(physical_device); properties = physical_device.getProperties(); + if (properties.apiVersion < TargetVulkanApiVersion) { + throw std::runtime_error(fmt::format( + "Vulkan {}.{} is required, but only {}.{} is supported by device!", + VK_VERSION_MAJOR(TargetVulkanApiVersion), VK_VERSION_MINOR(TargetVulkanApiVersion), + VK_VERSION_MAJOR(properties.apiVersion), VK_VERSION_MINOR(properties.apiVersion))); + } CollectTelemetryParameters(telemetry); CreateDevice(); @@ -629,7 +636,7 @@ void Instance::CreateAllocator() { .device = *device, .pVulkanFunctions = &functions, .instance = *instance, - .vulkanApiVersion = properties.apiVersion, + .vulkanApiVersion = TargetVulkanApiVersion, }; const VkResult result = vmaCreateAllocator(&allocator_info, &allocator); diff --git a/src/video_core/renderer_vulkan/vk_platform.cpp b/src/video_core/renderer_vulkan/vk_platform.cpp index 2b415354f..94ce93ec9 100644 --- a/src/video_core/renderer_vulkan/vk_platform.cpp +++ b/src/video_core/renderer_vulkan/vk_platform.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include "common/assert.h" #include "common/logging/log.h" @@ -290,13 +291,14 @@ vk::UniqueInstance CreateInstance(const Common::DynamicLibrary& library, } VULKAN_HPP_DEFAULT_DISPATCHER.init(vkGetInstanceProcAddr); - if (!VULKAN_HPP_DEFAULT_DISPATCHER.vkEnumerateInstanceVersion) { - throw std::runtime_error("Vulkan 1.0 is not supported, 1.1 is required!"); - } - - const u32 available_version = vk::enumerateInstanceVersion(); - if (available_version < VK_API_VERSION_1_1) { - throw std::runtime_error("Vulkan 1.0 is not supported, 1.1 is required!"); + const u32 available_version = VULKAN_HPP_DEFAULT_DISPATCHER.vkEnumerateInstanceVersion + ? vk::enumerateInstanceVersion() + : VK_API_VERSION_1_0; + if (available_version < TargetVulkanApiVersion) { + throw std::runtime_error(fmt::format( + "Vulkan {}.{} is required, but only {}.{} is supported by instance!", + VK_VERSION_MAJOR(TargetVulkanApiVersion), VK_VERSION_MINOR(TargetVulkanApiVersion), + VK_VERSION_MAJOR(available_version), VK_VERSION_MINOR(available_version))); } const auto extensions = GetInstanceExtensions(window_type, enable_validation); @@ -306,7 +308,7 @@ vk::UniqueInstance CreateInstance(const Common::DynamicLibrary& library, .applicationVersion = VK_MAKE_VERSION(1, 0, 0), .pEngineName = "Citra Vulkan", .engineVersion = VK_MAKE_VERSION(1, 0, 0), - .apiVersion = VK_API_VERSION_1_3, + .apiVersion = TargetVulkanApiVersion, }; boost::container::static_vector layers; diff --git a/src/video_core/renderer_vulkan/vk_platform.h b/src/video_core/renderer_vulkan/vk_platform.h index 20e9ebb0d..68f6457e0 100644 --- a/src/video_core/renderer_vulkan/vk_platform.h +++ b/src/video_core/renderer_vulkan/vk_platform.h @@ -19,6 +19,8 @@ enum class WindowSystemType : u8; namespace Vulkan { +constexpr u32 TargetVulkanApiVersion = VK_API_VERSION_1_1; + using DebugCallback = std::variant; From 7638f87f7421e705979119ef93d309893673df0a Mon Sep 17 00:00:00 2001 From: Tobias Date: Fri, 16 Feb 2024 13:34:10 +0100 Subject: [PATCH 03/12] Port several small multiplayer PRs from yuzu (#7419) * yuzu: Use displayed port on direct connect * Color player counts in the multiplayer public lobby list - Full lobbies have their player count displayed in red. - Lobbies with one slot left have their player count displayed in orange. - Empty lobbies have their player count grayed out. * Add hotkeys for multiplayer actions Default shortcuts were chosen as to be intuitive (use the first letter of the action, or the second word's first letter) and work on all types of keyboards. The hotkeys can be used while playing a game too, as they are application-wide. * Persist filters in multiplayer public lobby list After connecting to a room, the chosen filter text, "Games I Own", "Hide Empty Rooms" and "Hide Full Rooms" values are persisted to configuration so they are preserved across restarts. This makes it easier to rejoin a room if you regularly play the same game, or after a crash. * citra_qt/lobby: Fix multiplayer player count color in dark theme Co-Authored-By: Kevnkkm <56404895+kevnkkm@users.noreply.github.com> * Address review comments --------- Co-authored-by: Narr the Reg Co-authored-by: Hugo Locurcio Co-authored-by: Kevnkkm <56404895+kevnkkm@users.noreply.github.com> --- src/citra_qt/configuration/config.cpp | 25 +++++++++++++++- src/citra_qt/configuration/config.h | 2 +- src/citra_qt/main.cpp | 7 +++++ src/citra_qt/multiplayer/direct_connect.cpp | 5 ++-- src/citra_qt/multiplayer/lobby.cpp | 12 +++++++- src/citra_qt/multiplayer/lobby_p.h | 33 ++++++++++++++++++--- src/citra_qt/uisettings.h | 5 ++++ 7 files changed, 79 insertions(+), 10 deletions(-) diff --git a/src/citra_qt/configuration/config.cpp b/src/citra_qt/configuration/config.cpp index 389a3becf..54c8c4f4b 100644 --- a/src/citra_qt/configuration/config.cpp +++ b/src/citra_qt/configuration/config.cpp @@ -54,7 +54,7 @@ const std::array, Settings::NativeAnalog::NumAnalogs> Config: // This must be in alphabetical order according to action name as it must have the same order as // UISetting::values.shortcuts, which is alphabetically ordered. // clang-format off -const std::array Config::default_hotkeys {{ +const std::array Config::default_hotkeys {{ {QStringLiteral("Advance Frame"), QStringLiteral("Main Window"), {QStringLiteral(""), Qt::ApplicationShortcut}}, {QStringLiteral("Audio Mute/Unmute"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+M"), Qt::WindowShortcut}}, {QStringLiteral("Audio Volume Down"), QStringLiteral("Main Window"), {QStringLiteral(""), Qt::WindowShortcut}}, @@ -71,6 +71,11 @@ const std::array Config::default_hotkeys {{ {QStringLiteral("Load Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F2"), Qt::WidgetWithChildrenShortcut}}, {QStringLiteral("Load File"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+O"), Qt::WidgetWithChildrenShortcut}}, {QStringLiteral("Load from Newest Slot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+V"), Qt::WindowShortcut}}, + {QStringLiteral("Multiplayer Browse Public Game Lobby"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+B"), Qt::ApplicationShortcut}}, + {QStringLiteral("Multiplayer Create Room"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+N"), Qt::ApplicationShortcut}}, + {QStringLiteral("Multiplayer Direct Connect to Room"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Shift"), Qt::ApplicationShortcut}}, + {QStringLiteral("Multiplayer Leave Room"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+L"), Qt::ApplicationShortcut}}, + {QStringLiteral("Multiplayer Show Current Room"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+R"), Qt::ApplicationShortcut}}, {QStringLiteral("Remove Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F3"), Qt::ApplicationShortcut}}, {QStringLiteral("Restart Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F6"), Qt::WindowShortcut}}, {QStringLiteral("Rotate Screens Upright"), QStringLiteral("Main Window"), {QStringLiteral("F8"), Qt::WindowShortcut}}, @@ -557,6 +562,15 @@ void Config::ReadMultiplayerValues() { UISettings::values.game_id = ReadSetting(QStringLiteral("game_id"), 0).toULongLong(); UISettings::values.room_description = ReadSetting(QStringLiteral("room_description"), QString{}).toString(); + UISettings::values.multiplayer_filter_text = + ReadSetting(QStringLiteral("multiplayer_filter_text"), QString{}).toString(); + UISettings::values.multiplayer_filter_games_owned = + ReadSetting(QStringLiteral("multiplayer_filter_games_owned"), false).toBool(); + UISettings::values.multiplayer_filter_hide_empty = + ReadSetting(QStringLiteral("multiplayer_filter_hide_empty"), false).toBool(); + UISettings::values.multiplayer_filter_hide_full = + ReadSetting(QStringLiteral("multiplayer_filter_hide_full"), false).toBool(); + // Read ban list back int size = qt_config->beginReadArray(QStringLiteral("username_ban_list")); UISettings::values.ban_list.first.resize(size); @@ -1074,6 +1088,15 @@ void Config::SaveMultiplayerValues() { WriteSetting(QStringLiteral("game_id"), UISettings::values.game_id, 0); WriteSetting(QStringLiteral("room_description"), UISettings::values.room_description, QString{}); + WriteSetting(QStringLiteral("multiplayer_filter_text"), + UISettings::values.multiplayer_filter_text, QString{}); + WriteSetting(QStringLiteral("multiplayer_filter_games_owned"), + UISettings::values.multiplayer_filter_games_owned, false); + WriteSetting(QStringLiteral("multiplayer_filter_hide_empty"), + UISettings::values.multiplayer_filter_hide_empty, false); + WriteSetting(QStringLiteral("multiplayer_filter_hide_full"), + UISettings::values.multiplayer_filter_hide_full, false); + // Write ban list qt_config->beginWriteArray(QStringLiteral("username_ban_list")); for (std::size_t i = 0; i < UISettings::values.ban_list.first.size(); ++i) { diff --git a/src/citra_qt/configuration/config.h b/src/citra_qt/configuration/config.h index 27be93711..521c6baf9 100644 --- a/src/citra_qt/configuration/config.h +++ b/src/citra_qt/configuration/config.h @@ -26,7 +26,7 @@ public: static const std::array default_buttons; static const std::array, Settings::NativeAnalog::NumAnalogs> default_analogs; - static const std::array default_hotkeys; + static const std::array default_hotkeys; private: void Initialize(const std::string& config_name); diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp index 807da30e3..4f179a35c 100644 --- a/src/citra_qt/main.cpp +++ b/src/citra_qt/main.cpp @@ -647,6 +647,13 @@ void GMainWindow::InitializeHotkeys() { link_action_shortcut(ui->action_Advance_Frame, QStringLiteral("Advance Frame")); link_action_shortcut(ui->action_Load_from_Newest_Slot, QStringLiteral("Load from Newest Slot")); link_action_shortcut(ui->action_Save_to_Oldest_Slot, QStringLiteral("Save to Oldest Slot")); + link_action_shortcut(ui->action_View_Lobby, + QStringLiteral("Multiplayer Browse Public Game Lobby")); + link_action_shortcut(ui->action_Start_Room, QStringLiteral("Multiplayer Create Room")); + link_action_shortcut(ui->action_Connect_To_Room, + QStringLiteral("Multiplayer Direct Connect to Room")); + link_action_shortcut(ui->action_Show_Room, QStringLiteral("Multiplayer Show Current Room")); + link_action_shortcut(ui->action_Leave_Room, QStringLiteral("Multiplayer Leave Room")); const auto add_secondary_window_hotkey = [this](QKeySequence hotkey, const char* slot) { // This action will fire specifically when secondary_window is in focus diff --git a/src/citra_qt/multiplayer/direct_connect.cpp b/src/citra_qt/multiplayer/direct_connect.cpp index 936fd0435..bdc866a75 100644 --- a/src/citra_qt/multiplayer/direct_connect.cpp +++ b/src/citra_qt/multiplayer/direct_connect.cpp @@ -80,9 +80,8 @@ void DirectConnectWindow::Connect() { // Store settings UISettings::values.nickname = ui->nickname->text(); UISettings::values.ip = ui->ip->text(); - UISettings::values.port = (ui->port->isModified() && !ui->port->text().isEmpty()) - ? ui->port->text() - : UISettings::values.port; + UISettings::values.port = + !ui->port->text().isEmpty() ? ui->port->text() : UISettings::values.port; // attempt to connect in a different thread QFuture f = QtConcurrent::run([&] { diff --git a/src/citra_qt/multiplayer/lobby.cpp b/src/citra_qt/multiplayer/lobby.cpp index 7cd2ae7e1..00671b842 100644 --- a/src/citra_qt/multiplayer/lobby.cpp +++ b/src/citra_qt/multiplayer/lobby.cpp @@ -63,10 +63,10 @@ Lobby::Lobby(Core::System& system_, QWidget* parent, QStandardItemModel* list, // UI Buttons connect(ui->refresh_list, &QPushButton::clicked, this, &Lobby::RefreshLobby); + connect(ui->search, &QLineEdit::textChanged, proxy, &LobbyFilterProxyModel::SetFilterSearch); connect(ui->games_owned, &QCheckBox::toggled, proxy, &LobbyFilterProxyModel::SetFilterOwned); connect(ui->hide_empty, &QCheckBox::toggled, proxy, &LobbyFilterProxyModel::SetFilterEmpty); connect(ui->hide_full, &QCheckBox::toggled, proxy, &LobbyFilterProxyModel::SetFilterFull); - connect(ui->search, &QLineEdit::textChanged, proxy, &LobbyFilterProxyModel::SetFilterSearch); connect(ui->room_list, &QTreeView::doubleClicked, this, &Lobby::OnJoinRoom); connect(ui->room_list, &QTreeView::clicked, this, &Lobby::OnExpandRoom); @@ -74,6 +74,12 @@ Lobby::Lobby(Core::System& system_, QWidget* parent, QStandardItemModel* list, connect(&room_list_watcher, &QFutureWatcher::finished, this, &Lobby::OnRefreshLobby); + // Load persistent filters after events are connected to make sure they apply + ui->search->setText(UISettings::values.multiplayer_filter_text); + ui->games_owned->setChecked(UISettings::values.multiplayer_filter_games_owned); + ui->hide_empty->setChecked(UISettings::values.multiplayer_filter_hide_empty); + ui->hide_full->setChecked(UISettings::values.multiplayer_filter_hide_full); + // manually start a refresh when the window is opening // TODO(jroweboy): if this refresh is slow for people with bad internet, then don't do it as // part of the constructor, but offload the refresh until after the window shown. perhaps emit a @@ -180,6 +186,10 @@ void Lobby::OnJoinRoom(const QModelIndex& source) { UISettings::values.nickname = ui->nickname->text(); UISettings::values.ip = proxy->data(connection_index, LobbyItemHost::HostIPRole).toString(); UISettings::values.port = proxy->data(connection_index, LobbyItemHost::HostPortRole).toString(); + UISettings::values.multiplayer_filter_text = ui->search->text(); + UISettings::values.multiplayer_filter_games_owned = ui->games_owned->isChecked(); + UISettings::values.multiplayer_filter_hide_empty = ui->hide_empty->isChecked(); + UISettings::values.multiplayer_filter_hide_full = ui->hide_full->isChecked(); } void Lobby::ResetModel() { diff --git a/src/citra_qt/multiplayer/lobby_p.h b/src/citra_qt/multiplayer/lobby_p.h index 16db78eae..f1890fdef 100644 --- a/src/citra_qt/multiplayer/lobby_p.h +++ b/src/citra_qt/multiplayer/lobby_p.h @@ -188,12 +188,37 @@ public: } QVariant data(int role) const override { - if (role != Qt::DisplayRole) { + switch (role) { + case Qt::DisplayRole: { + auto members = data(MemberListRole).toList(); + return QStringLiteral("%1 / %2").arg(QString::number(members.size()), + data(MaxPlayerRole).toString()); + } + case Qt::ForegroundRole: { + auto members = data(MemberListRole).toList(); + auto max_players = data(MaxPlayerRole).toInt(); + const QColor room_full_color(255, 48, 32); + const QColor room_almost_full_color(255, 140, 32); + const QColor room_has_players_color(32, 160, 32); + const QColor room_empty_color(128, 128, 128); + + if (members.size() >= max_players) { + return QBrush(room_full_color); + } else if (members.size() == (max_players - 1)) { + return QBrush(room_almost_full_color); + } else if (members.size() == 0) { + return QBrush(room_empty_color); + } else if (members.size() > 0 && members.size() < (max_players - 1)) { + return QBrush(room_has_players_color); + } + + // FIXME: How to return a value that tells Qt not to modify the + // text color from the default (as if Qt::ForegroundRole wasn't overridden)? + return QBrush(nullptr); + } + default: return LobbyItem::data(role); } - auto members = data(MemberListRole).toList(); - return QStringLiteral("%1 / %2").arg(QString::number(members.size()), - data(MaxPlayerRole).toString()); } bool operator<(const QStandardItem& other) const override { diff --git a/src/citra_qt/uisettings.h b/src/citra_qt/uisettings.h index 8e671f49f..6dedff0f0 100644 --- a/src/citra_qt/uisettings.h +++ b/src/citra_qt/uisettings.h @@ -138,6 +138,11 @@ struct Values { QString room_description; std::pair, std::vector> ban_list; + QString multiplayer_filter_text; + bool multiplayer_filter_games_owned; + bool multiplayer_filter_hide_empty; + bool multiplayer_filter_hide_full; + // logging Settings::Setting show_console{false, "showConsole"}; }; From bb003c2bd46043c71b7287940a576029de2b7bd1 Mon Sep 17 00:00:00 2001 From: SachinVin <26602104+SachinVin@users.noreply.github.com> Date: Sat, 17 Feb 2024 06:42:54 +0530 Subject: [PATCH 04/12] audio_core\hle\source.cpp: Improve accuracy of SourceStatus (#7432) --- src/audio_core/hle/source.cpp | 30 ++++++++++++++++++++---------- src/audio_core/hle/source.h | 6 ++---- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/src/audio_core/hle/source.cpp b/src/audio_core/hle/source.cpp index 54a28bee3..d3fbfd0cb 100644 --- a/src/audio_core/hle/source.cpp +++ b/src/audio_core/hle/source.cpp @@ -298,9 +298,9 @@ void Source::ParseConfig(SourceConfiguration::Configuration& config, b.buffer_id, state.mono_or_stereo, state.format, - true, - {}, // 0 in u32_dsp - false, + true, // from_queue + 0, // play_position + false, // has_played }); } LOG_TRACE(Audio_DSP, "enqueuing queued {} addr={:#010x} len={} id={}", i, @@ -321,7 +321,11 @@ void Source::ParseConfig(SourceConfiguration::Configuration& config, void Source::GenerateFrame() { current_frame.fill({}); - if (state.current_buffer.empty() && !DequeueBuffer()) { + if (state.current_buffer.empty()) { + // TODO(SachinV): Should dequeue happen at the end of the frame generation? + if (DequeueBuffer()) { + return; + } state.enabled = false; state.buffer_update = true; state.last_buffer_id = state.current_buffer_id; @@ -330,8 +334,6 @@ void Source::GenerateFrame() { } std::size_t frame_position = 0; - - state.current_sample_number = state.next_sample_number; while (frame_position < current_frame.size()) { if (state.current_buffer.empty() && !DequeueBuffer()) { break; @@ -358,7 +360,7 @@ void Source::GenerateFrame() { } // TODO(jroweboy): Keep track of frame_position independently so that it doesn't lose precision // over time - state.next_sample_number += static_cast(frame_position * state.rate_multiplier); + state.current_sample_number += static_cast(frame_position * state.rate_multiplier); state.filters.ProcessFrame(current_frame); } @@ -409,7 +411,6 @@ bool Source::DequeueBuffer() { // the first playthrough starts at play_position, loops start at the beginning of the buffer state.current_sample_number = (!buf.has_played) ? buf.play_position : 0; - state.next_sample_number = state.current_sample_number; state.current_buffer_physical_address = buf.physical_address; state.current_buffer_id = buf.buffer_id; state.last_buffer_id = 0; @@ -420,8 +421,17 @@ bool Source::DequeueBuffer() { state.input_queue.push(buf); } - LOG_TRACE(Audio_DSP, "source_id={} buffer_id={} from_queue={} current_buffer.size()={}", - source_id, buf.buffer_id, buf.from_queue, state.current_buffer.size()); + // Because our interpolation consumes samples instead of using an index, + // let's just consume the samples up to the current sample number. + state.current_buffer.erase( + state.current_buffer.begin(), + std::next(state.current_buffer.begin(), state.current_sample_number)); + + LOG_TRACE(Audio_DSP, + "source_id={} buffer_id={} from_queue={} current_buffer.size()={}, " + "buf.has_played={}, buf.play_position={}", + source_id, buf.buffer_id, buf.from_queue, state.current_buffer.size(), buf.has_played, + buf.play_position); return true; } diff --git a/src/audio_core/hle/source.h b/src/audio_core/hle/source.h index a3af06d27..d7114952b 100644 --- a/src/audio_core/hle/source.h +++ b/src/audio_core/hle/source.h @@ -87,8 +87,8 @@ private: Format format; bool from_queue; - u32_dsp play_position; // = 0; - bool has_played; // = false; + u32 play_position; // = 0; + bool has_played; // = false; private: template @@ -136,7 +136,6 @@ private: // Current buffer u32 current_sample_number = 0; - u32 next_sample_number = 0; PAddr current_buffer_physical_address = 0; AudioInterp::StereoBuffer16 current_buffer = {}; @@ -171,7 +170,6 @@ private: ar& mono_or_stereo; ar& format; ar& current_sample_number; - ar& next_sample_number; ar& current_buffer_physical_address; ar& current_buffer; ar& buffer_update; From 749a721aa28ed0aa3aba85ff014324ba824cdc8c Mon Sep 17 00:00:00 2001 From: Castor215 <132155746+Castor215@users.noreply.github.com> Date: Sat, 17 Feb 2024 14:39:38 +0000 Subject: [PATCH 05/12] externals: disable system cpp-httplib if it is a shared object (#7446) Co-authored-by: Castor216 --- externals/CMakeLists.txt | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index 5f4c23ad3..fc45473b6 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -294,11 +294,20 @@ endif() add_library(httplib INTERFACE) if(USE_SYSTEM_CPP_HTTPLIB) find_package(CppHttp 0.14.1) - if(CppHttp_FOUND) - target_link_libraries(httplib INTERFACE httplib::httplib) - else() - message(STATUS "Cpp-httplib not found or not suitable version! Falling back to bundled...") + # Detect if system cpphttplib is a shared library + # this breaks building as Citra relies on functions that are moved + # into the shared object. + get_target_property(HTTP_LIBS httplib::httplib INTERFACE_LINK_LIBRARIES) + if(HTTP_LIBS) + message(WARNING "Shared cpp-http (${HTTP_LIBS}) not supported. Falling back to bundled...") target_include_directories(httplib SYSTEM INTERFACE ./httplib) + else() + if(CppHttp_FOUND) + target_link_libraries(httplib INTERFACE httplib::httplib) + else() + message(STATUS "Cpp-httplib not found or not suitable version! Falling back to bundled...") + target_include_directories(httplib SYSTEM INTERFACE ./httplib) + endif() endif() else() target_include_directories(httplib SYSTEM INTERFACE ./httplib) From da5aa70fc940c3f0feec3bf83acab233c84966d7 Mon Sep 17 00:00:00 2001 From: Charles Lombardo Date: Sat, 17 Feb 2024 23:10:10 -0500 Subject: [PATCH 06/12] android: Port yuzu system info logging (#7431) --- .../org/citra/citra_emu/CitraApplication.kt | 14 +++ .../java/org/citra/citra_emu/NativeLibrary.kt | 4 +- .../utils/DirectoryInitialization.kt | 6 +- .../java/org/citra/citra_emu/utils/Log.kt | 27 +---- .../org/citra/citra_emu/utils/MemoryUtil.kt | 108 ++++++++++++++++++ src/android/app/src/main/jni/CMakeLists.txt | 1 + src/android/app/src/main/jni/native_log.cpp | 30 +++++ .../app/src/main/res/values/strings.xml | 11 ++ 8 files changed, 174 insertions(+), 27 deletions(-) create mode 100644 src/android/app/src/main/java/org/citra/citra_emu/utils/MemoryUtil.kt create mode 100644 src/android/app/src/main/jni/native_log.cpp diff --git a/src/android/app/src/main/java/org/citra/citra_emu/CitraApplication.kt b/src/android/app/src/main/java/org/citra/citra_emu/CitraApplication.kt index c414d4246..75a88baf1 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/CitraApplication.kt +++ b/src/android/app/src/main/java/org/citra/citra_emu/CitraApplication.kt @@ -9,10 +9,13 @@ import android.app.Application import android.app.NotificationChannel import android.app.NotificationManager import android.content.Context +import android.os.Build import org.citra.citra_emu.utils.DirectoryInitialization import org.citra.citra_emu.utils.DocumentsTree import org.citra.citra_emu.utils.GpuDriverHelper import org.citra.citra_emu.utils.PermissionsHandler +import org.citra.citra_emu.utils.Log +import org.citra.citra_emu.utils.MemoryUtil class CitraApplication : Application() { private fun createNotificationChannel() { @@ -53,9 +56,20 @@ class CitraApplication : Application() { } NativeLibrary.logDeviceInfo() + logDeviceInfo() createNotificationChannel() } + fun logDeviceInfo() { + Log.info("Device Manufacturer - ${Build.MANUFACTURER}") + Log.info("Device Model - ${Build.MODEL}") + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.R) { + Log.info("SoC Manufacturer - ${Build.SOC_MANUFACTURER}") + Log.info("SoC Model - ${Build.SOC_MODEL}") + } + Log.info("Total System Memory - ${MemoryUtil.getDeviceRAM()}") + } + companion object { private var application: CitraApplication? = null diff --git a/src/android/app/src/main/java/org/citra/citra_emu/NativeLibrary.kt b/src/android/app/src/main/java/org/citra/citra_emu/NativeLibrary.kt index 75150fd16..bfbe658f8 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/NativeLibrary.kt +++ b/src/android/app/src/main/java/org/citra/citra_emu/NativeLibrary.kt @@ -413,12 +413,12 @@ object NativeLibrary { } fun setEmulationActivity(emulationActivity: EmulationActivity?) { - Log.verbose("[NativeLibrary] Registering EmulationActivity.") + Log.debug("[NativeLibrary] Registering EmulationActivity.") sEmulationActivity = WeakReference(emulationActivity) } fun clearEmulationActivity() { - Log.verbose("[NativeLibrary] Unregistering EmulationActivity.") + Log.debug("[NativeLibrary] Unregistering EmulationActivity.") sEmulationActivity.clear() } diff --git a/src/android/app/src/main/java/org/citra/citra_emu/utils/DirectoryInitialization.kt b/src/android/app/src/main/java/org/citra/citra_emu/utils/DirectoryInitialization.kt index 10e509f23..c48f9d22e 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/utils/DirectoryInitialization.kt +++ b/src/android/app/src/main/java/org/citra/citra_emu/utils/DirectoryInitialization.kt @@ -94,14 +94,14 @@ object DirectoryInitialization { val dataPath = PermissionsHandler.citraDirectory if (dataPath.toString().isNotEmpty()) { userPath = dataPath.toString() - Log.debug("[DirectoryInitialization] User Dir: $userPath") + android.util.Log.d("[Citra Frontend]", "[DirectoryInitialization] User Dir: $userPath") return true } return false } private fun copyAsset(asset: String, output: File, overwrite: Boolean, context: Context) { - Log.verbose("[DirectoryInitialization] Copying File $asset to $output") + Log.debug("[DirectoryInitialization] Copying File $asset to $output") try { if (!output.exists() || overwrite) { val inputStream = context.assets.open(asset) @@ -121,7 +121,7 @@ object DirectoryInitialization { overwrite: Boolean, context: Context ) { - Log.verbose("[DirectoryInitialization] Copying Folder $assetFolder to $outputFolder") + Log.debug("[DirectoryInitialization] Copying Folder $assetFolder to $outputFolder") try { var createdFolder = false for (file in context.assets.list(assetFolder)!!) { diff --git a/src/android/app/src/main/java/org/citra/citra_emu/utils/Log.kt b/src/android/app/src/main/java/org/citra/citra_emu/utils/Log.kt index 26c41bc98..f691d51b0 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/utils/Log.kt +++ b/src/android/app/src/main/java/org/citra/citra_emu/utils/Log.kt @@ -4,34 +4,17 @@ package org.citra.citra_emu.utils -import android.util.Log -import org.citra.citra_emu.BuildConfig - -/** - * Contains methods that call through to [android.util.Log], but - * with the same TAG automatically provided. Also no-ops VERBOSE and DEBUG log - * levels in release builds. - */ object Log { // Tracks whether we should share the old log or the current log var gameLaunched = false - private const val TAG = "Citra Frontend" - fun verbose(message: String?) { - if (BuildConfig.DEBUG) { - Log.v(TAG, message!!) - } - } + external fun debug(message: String) - fun debug(message: String?) { - if (BuildConfig.DEBUG) { - Log.d(TAG, message!!) - } - } + external fun warning(message: String) - fun info(message: String?) = Log.i(TAG, message!!) + external fun info(message: String) - fun warning(message: String?) = Log.w(TAG, message!!) + external fun error(message: String) - fun error(message: String?) = Log.e(TAG, message!!) + external fun critical(message: String) } diff --git a/src/android/app/src/main/java/org/citra/citra_emu/utils/MemoryUtil.kt b/src/android/app/src/main/java/org/citra/citra_emu/utils/MemoryUtil.kt new file mode 100644 index 000000000..4bf1d88c7 --- /dev/null +++ b/src/android/app/src/main/java/org/citra/citra_emu/utils/MemoryUtil.kt @@ -0,0 +1,108 @@ +// SPDX-FileCopyrightText: 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.citra.citra_emu.utils + +import android.app.ActivityManager +import android.content.Context +import android.os.Build +import org.citra.citra_emu.CitraApplication +import org.citra.citra_emu.R +import java.util.Locale +import kotlin.math.ceil + +object MemoryUtil { + private val context get() = CitraApplication.appContext + + private val Float.hundredths: String + get() = String.format(Locale.ROOT, "%.2f", this) + + const val Kb: Float = 1024F + const val Mb = Kb * 1024 + const val Gb = Mb * 1024 + const val Tb = Gb * 1024 + const val Pb = Tb * 1024 + const val Eb = Pb * 1024 + + fun bytesToSizeUnit(size: Float, roundUp: Boolean = false): String = + when { + size < Kb -> { + context.getString( + R.string.memory_formatted, + size.hundredths, + context.getString(R.string.memory_byte_shorthand) + ) + } + size < Mb -> { + context.getString( + R.string.memory_formatted, + if (roundUp) ceil(size / Kb) else (size / Kb).hundredths, + context.getString(R.string.memory_kilobyte) + ) + } + size < Gb -> { + context.getString( + R.string.memory_formatted, + if (roundUp) ceil(size / Mb) else (size / Mb).hundredths, + context.getString(R.string.memory_megabyte) + ) + } + size < Tb -> { + context.getString( + R.string.memory_formatted, + if (roundUp) ceil(size / Gb) else (size / Gb).hundredths, + context.getString(R.string.memory_gigabyte) + ) + } + size < Pb -> { + context.getString( + R.string.memory_formatted, + if (roundUp) ceil(size / Tb) else (size / Tb).hundredths, + context.getString(R.string.memory_terabyte) + ) + } + size < Eb -> { + context.getString( + R.string.memory_formatted, + if (roundUp) ceil(size / Pb) else (size / Pb).hundredths, + context.getString(R.string.memory_petabyte) + ) + } + else -> { + context.getString( + R.string.memory_formatted, + if (roundUp) ceil(size / Eb) else (size / Eb).hundredths, + context.getString(R.string.memory_exabyte) + ) + } + } + + val totalMemory: Float + get() { + val memInfo = ActivityManager.MemoryInfo() + with(context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager) { + getMemoryInfo(memInfo) + } + + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { + memInfo.advertisedMem.toFloat() + } else { + memInfo.totalMem.toFloat() + } + } + + fun isLessThan(minimum: Int, size: Float): Boolean = + when (size) { + Kb -> totalMemory < Mb && totalMemory < minimum + Mb -> totalMemory < Gb && (totalMemory / Mb) < minimum + Gb -> totalMemory < Tb && (totalMemory / Gb) < minimum + Tb -> totalMemory < Pb && (totalMemory / Tb) < minimum + Pb -> totalMemory < Eb && (totalMemory / Pb) < minimum + Eb -> totalMemory / Eb < minimum + else -> totalMemory < Kb && totalMemory < minimum + } + + // Devices are unlikely to have 0.5GB increments of memory so we'll just round up to account for + // the potential error created by memInfo.totalMem + fun getDeviceRAM(): String = bytesToSizeUnit(totalMemory, true) +} diff --git a/src/android/app/src/main/jni/CMakeLists.txt b/src/android/app/src/main/jni/CMakeLists.txt index 84a34441a..233d568ed 100644 --- a/src/android/app/src/main/jni/CMakeLists.txt +++ b/src/android/app/src/main/jni/CMakeLists.txt @@ -28,6 +28,7 @@ add_library(citra-android SHARED ndk_motion.cpp ndk_motion.h system_save_game.cpp + native_log.cpp ) target_link_libraries(citra-android PRIVATE audio_core citra_common citra_core input_common network) diff --git a/src/android/app/src/main/jni/native_log.cpp b/src/android/app/src/main/jni/native_log.cpp new file mode 100644 index 000000000..86a57b99f --- /dev/null +++ b/src/android/app/src/main/jni/native_log.cpp @@ -0,0 +1,30 @@ +// SPDX-FileCopyrightText: 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include "android_common/android_common.h" + +extern "C" { + +void Java_org_citra_citra_1emu_utils_Log_debug(JNIEnv* env, jobject obj, jstring jmessage) { + LOG_DEBUG(Frontend, "{}", GetJString(env, jmessage)); +} + +void Java_org_citra_citra_1emu_utils_Log_warning(JNIEnv* env, jobject obj, jstring jmessage) { + LOG_WARNING(Frontend, "{}", GetJString(env, jmessage)); +} + +void Java_org_citra_citra_1emu_utils_Log_info(JNIEnv* env, jobject obj, jstring jmessage) { + LOG_INFO(Frontend, "{}", GetJString(env, jmessage)); +} + +void Java_org_citra_citra_1emu_utils_Log_error(JNIEnv* env, jobject obj, jstring jmessage) { + LOG_ERROR(Frontend, "{}", GetJString(env, jmessage)); +} + +void Java_org_citra_citra_1emu_utils_Log_critical(JNIEnv* env, jobject obj, jstring jmessage) { + LOG_CRITICAL(Frontend, "{}", GetJString(env, jmessage)); +} + +} // extern "C" diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index 303632162..781e78da1 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -442,6 +442,17 @@ \"%s\" must be decrypted before being used with Citra.\n A real 3DS is required An unknown error occurred while installing \"%s\".\n Please see the log for more details + + %1$s %2$s + Byte + B + KB + MB + GB + TB + PB + EB + Change Theme Mode Follow System From cbe898703631b404dfc430a60050fd8d0956a62f Mon Sep 17 00:00:00 2001 From: Steveice10 <1269164+Steveice10@users.noreply.github.com> Date: Sun, 18 Feb 2024 08:23:15 -0800 Subject: [PATCH 07/12] ci: Update action versions. (#7449) --- .github/workflows/build.yml | 42 ++++++++++++++++----------------- .github/workflows/format.yml | 2 +- .github/workflows/publish.yml | 16 ++++++------- .github/workflows/transifex.yml | 2 +- 4 files changed, 31 insertions(+), 31 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ad0665c5b..b848e93d5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -12,13 +12,13 @@ jobs: if: ${{ !github.head_ref }} runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: recursive - name: Pack run: ./.ci/source.sh - name: Upload - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: source path: artifacts/ @@ -37,11 +37,11 @@ jobs: OS: linux TARGET: ${{ matrix.target }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: recursive - name: Set up cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ${{ env.CCACHE_DIR }} key: ${{ runner.os }}-${{ matrix.target }}-${{ github.sha }} @@ -53,7 +53,7 @@ jobs: run: ./.ci/pack.sh if: ${{ matrix.target == 'appimage' }} - name: Upload - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: ${{ matrix.target == 'appimage' }} with: name: ${{ env.OS }}-${{ env.TARGET }} @@ -70,11 +70,11 @@ jobs: OS: macos TARGET: ${{ matrix.target }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: recursive - name: Set up cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ${{ env.CCACHE_DIR }} key: ${{ runner.os }}-${{ matrix.target }}-${{ github.sha }} @@ -87,7 +87,7 @@ jobs: - name: Prepare outputs for caching run: mv build/bundle $OS-$TARGET - name: Cache outputs for universal build - uses: actions/cache/save@v3 + uses: actions/cache/save@v4 with: path: ${{ env.OS }}-${{ env.TARGET }} key: ${{ runner.os }}-${{ matrix.target }}-${{ github.sha }}-${{ github.run_id }}-${{ github.run_attempt }} @@ -98,15 +98,15 @@ jobs: OS: macos TARGET: universal steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Download x86_64 build from cache - uses: actions/cache/restore@v3 + uses: actions/cache/restore@v4 with: path: ${{ env.OS }}-x86_64 key: ${{ runner.os }}-x86_64-${{ github.sha }}-${{ github.run_id }}-${{ github.run_attempt }} fail-on-cache-miss: true - name: Download ARM64 build from cache - uses: actions/cache/restore@v3 + uses: actions/cache/restore@v4 with: path: ${{ env.OS }}-arm64 key: ${{ runner.os }}-arm64-${{ github.sha }}-${{ github.run_id }}-${{ github.run_attempt }} @@ -118,7 +118,7 @@ jobs: - name: Pack run: ./.ci/pack.sh - name: Upload - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: ${{ env.OS }}-${{ env.TARGET }} path: artifacts/ @@ -137,11 +137,11 @@ jobs: OS: windows TARGET: ${{ matrix.target }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: recursive - name: Set up cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ${{ env.CCACHE_DIR }} key: ${{ runner.os }}-${{ matrix.target }}-${{ github.sha }} @@ -179,7 +179,7 @@ jobs: - name: Pack run: ./.ci/pack.sh - name: Upload - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: ${{ env.OS }}-${{ env.TARGET }} path: artifacts/ @@ -192,11 +192,11 @@ jobs: OS: android TARGET: universal steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: recursive - name: Set up cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | ~/.gradle/caches @@ -228,7 +228,7 @@ jobs: env: UNPACKED: 1 - name: Upload - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: ${{ env.OS }}-${{ env.TARGET }} path: src/android/app/artifacts/ @@ -242,11 +242,11 @@ jobs: OS: ios TARGET: arm64 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: recursive - name: Set up cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ${{ env.CCACHE_DIR }} key: ${{ runner.os }}-ios-${{ github.sha }} @@ -261,7 +261,7 @@ jobs: needs: [windows, linux, macos-universal, android, source] if: ${{ startsWith(github.ref, 'refs/tags/') }} steps: - - uses: actions/download-artifact@v3 + - uses: actions/download-artifact@v4 - name: Create release uses: actions/create-release@v1 env: diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml index 574a24e62..0d9a30caf 100644 --- a/.github/workflows/format.yml +++ b/.github/workflows/format.yml @@ -13,7 +13,7 @@ jobs: image: citraemu/build-environments:linux-fresh options: -u 1001 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 0 - name: Build diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 4c46597f4..8c63dc6bf 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -20,11 +20,11 @@ jobs: if: ${{ github.event.inputs.nightly != 'false' && github.repository == 'citra-emu/citra' }} steps: # this checkout is required to make sure the GitHub Actions scripts are available - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 name: Pre-checkout with: submodules: false - - uses: actions/github-script@v6 + - uses: actions/github-script@v7 id: check-changes name: 'Check for new changes' env: @@ -38,7 +38,7 @@ jobs: return checkBaseChanges(github, context); - run: npm install execa@5 if: ${{ steps.check-changes.outputs.result == 'true' }} - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 name: Checkout if: ${{ steps.check-changes.outputs.result == 'true' }} with: @@ -46,7 +46,7 @@ jobs: fetch-depth: 0 submodules: true token: ${{ secrets.ALT_GITHUB_TOKEN }} - - uses: actions/github-script@v6 + - uses: actions/github-script@v7 name: 'Update and tag new commits' if: ${{ steps.check-changes.outputs.result == 'true' }} env: @@ -62,11 +62,11 @@ jobs: if: ${{ github.event.inputs.canary != 'false' && github.repository == 'citra-emu/citra' }} steps: # this checkout is required to make sure the GitHub Actions scripts are available - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 name: Pre-checkout with: submodules: false - - uses: actions/github-script@v6 + - uses: actions/github-script@v7 id: check-changes name: 'Check for new changes' env: @@ -79,7 +79,7 @@ jobs: return checkCanaryChanges(github, context); - run: npm install execa@5 if: ${{ steps.check-changes.outputs.result == 'true' }} - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 name: Checkout if: ${{ steps.check-changes.outputs.result == 'true' }} with: @@ -87,7 +87,7 @@ jobs: fetch-depth: 0 submodules: true token: ${{ secrets.ALT_GITHUB_TOKEN }} - - uses: actions/github-script@v6 + - uses: actions/github-script@v7 name: 'Check and merge canary changes' if: ${{ steps.check-changes.outputs.result == 'true' }} env: diff --git a/.github/workflows/transifex.yml b/.github/workflows/transifex.yml index 64d6e5866..b7a695f2b 100644 --- a/.github/workflows/transifex.yml +++ b/.github/workflows/transifex.yml @@ -10,7 +10,7 @@ jobs: container: citraemu/build-environments:linux-fresh if: ${{ github.repository == 'citra-emu/citra' }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: recursive fetch-depth: 0 From 3a4ebb1413cb4767bb03cd11263eb188ea2d97f0 Mon Sep 17 00:00:00 2001 From: Steveice10 <1269164+Steveice10@users.noreply.github.com> Date: Sun, 18 Feb 2024 15:21:53 -0800 Subject: [PATCH 08/12] file_util: Make sure portable user path is absolute. (#7448) --- src/citra_qt/main.cpp | 6 ++-- src/common/common_paths.h | 1 - src/common/file_util.cpp | 63 ++++++++++++++++++++++----------------- src/common/file_util.h | 5 +--- 4 files changed, 41 insertions(+), 34 deletions(-) diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp index 4f179a35c..5d8b4204b 100644 --- a/src/citra_qt/main.cpp +++ b/src/citra_qt/main.cpp @@ -3197,8 +3197,10 @@ int main(int argc, char* argv[]) { QApplication::setHighDpiScaleFactorRoundingPolicy(rounding_policy); #ifdef __APPLE__ - std::string bin_path = FileUtil::GetBundleDirectory() + DIR_SEP + ".."; - chdir(bin_path.c_str()); + auto bundle_dir = FileUtil::GetBundleDirectory(); + if (bundle_dir) { + FileUtil::SetCurrentDir(bundle_dir.value() + ".."); + } #endif #ifdef ENABLE_OPENGL diff --git a/src/common/common_paths.h b/src/common/common_paths.h index da9c04f2f..f12eedb0c 100644 --- a/src/common/common_paths.h +++ b/src/common/common_paths.h @@ -13,7 +13,6 @@ #endif // The user data dir -#define ROOT_DIR "." #define USERDATA_DIR "user" #ifdef USER_DIR #define EMU_DATA_DIR USER_DIR diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp index 1acbb2724..cda752e5f 100644 --- a/src/common/file_util.cpp +++ b/src/common/file_util.cpp @@ -634,6 +634,10 @@ std::optional GetCurrentDir() { std::string strDir = dir; #endif free(dir); + + if (!strDir.ends_with(DIR_SEP)) { + strDir += DIR_SEP; + } return strDir; } // namespace FileUtil @@ -646,17 +650,36 @@ bool SetCurrentDir(const std::string& directory) { } #if defined(__APPLE__) -std::string GetBundleDirectory() { - CFURLRef BundleRef; - char AppBundlePath[MAXPATHLEN]; +std::optional GetBundleDirectory() { // Get the main bundle for the app - BundleRef = CFBundleCopyBundleURL(CFBundleGetMainBundle()); - CFStringRef BundlePath = CFURLCopyFileSystemPath(BundleRef, kCFURLPOSIXPathStyle); - CFStringGetFileSystemRepresentation(BundlePath, AppBundlePath, sizeof(AppBundlePath)); - CFRelease(BundleRef); - CFRelease(BundlePath); + CFBundleRef bundle_ref = CFBundleGetMainBundle(); + if (!bundle_ref) { + return {}; + } - return AppBundlePath; + CFURLRef bundle_url_ref = CFBundleCopyBundleURL(bundle_ref); + if (!bundle_url_ref) { + return {}; + } + SCOPE_EXIT({ CFRelease(bundle_url_ref); }); + + CFStringRef bundle_path_ref = CFURLCopyFileSystemPath(bundle_url_ref, kCFURLPOSIXPathStyle); + if (!bundle_path_ref) { + return {}; + } + SCOPE_EXIT({ CFRelease(bundle_path_ref); }); + + char app_bundle_path[MAXPATHLEN]; + if (!CFStringGetFileSystemRepresentation(bundle_path_ref, app_bundle_path, + sizeof(app_bundle_path))) { + return {}; + } + + std::string path_str(app_bundle_path); + if (!path_str.ends_with(DIR_SEP)) { + path_str += DIR_SEP; + } + return path_str; } #endif @@ -732,22 +755,6 @@ static const std::string& GetHomeDirectory() { } #endif -std::string GetSysDirectory() { - std::string sysDir; - -#if defined(__APPLE__) - sysDir = GetBundleDirectory(); - sysDir += DIR_SEP; - sysDir += SYSDATA_DIR; -#else - sysDir = SYSDATA_DIR; -#endif - sysDir += DIR_SEP; - - LOG_DEBUG(Common_Filesystem, "Setting to {}:", sysDir); - return sysDir; -} - namespace { std::unordered_map g_paths; std::unordered_map g_default_paths; @@ -777,8 +784,10 @@ void SetUserPath(const std::string& path) { g_paths.emplace(UserPath::ConfigDir, user_path + CONFIG_DIR DIR_SEP); g_paths.emplace(UserPath::CacheDir, user_path + CACHE_DIR DIR_SEP); #else - if (FileUtil::Exists(ROOT_DIR DIR_SEP USERDATA_DIR)) { - user_path = ROOT_DIR DIR_SEP USERDATA_DIR DIR_SEP; + auto current_dir = FileUtil::GetCurrentDir(); + if (current_dir.has_value() && + FileUtil::Exists(current_dir.value() + USERDATA_DIR DIR_SEP)) { + user_path = current_dir.value() + USERDATA_DIR DIR_SEP; g_paths.emplace(UserPath::ConfigDir, user_path + CONFIG_DIR DIR_SEP); g_paths.emplace(UserPath::CacheDir, user_path + CACHE_DIR DIR_SEP); } else { diff --git a/src/common/file_util.h b/src/common/file_util.h index 2a4cc70ef..6595fead9 100644 --- a/src/common/file_util.h +++ b/src/common/file_util.h @@ -193,11 +193,8 @@ void SetCurrentRomPath(const std::string& path); // Update the Global Path with the new value void UpdateUserPath(UserPath path, const std::string& filename); -// Returns the path to where the sys file are -[[nodiscard]] std::string GetSysDirectory(); - #ifdef __APPLE__ -[[nodiscard]] std::string GetBundleDirectory(); +[[nodiscard]] std::optional GetBundleDirectory(); #endif #ifdef _WIN32 From e524542a400c8d1daec013f5ff124fbe8305b148 Mon Sep 17 00:00:00 2001 From: Wunk Date: Wed, 21 Feb 2024 16:35:57 -0800 Subject: [PATCH 09/12] vk_texture_runtime: Use boost-`static_vector` (#7455) * vk_texture_runtime: Use boost-`static_vector` for image init-barriers Uses `static_vector` rather than `std::array`+`u32` when passing input parameters into the initialization barriers. * vk_texture_runtime: Use boost-`static_vector` for framebuffer attachments * vk_texture_runtime: Use boost-`static_vector` for surface uploads --- .../renderer_vulkan/vk_texture_runtime.cpp | 66 +++++++++---------- 1 file changed, 31 insertions(+), 35 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_texture_runtime.cpp b/src/video_core/renderer_vulkan/vk_texture_runtime.cpp index 4ba7e6052..2db1a9c39 100644 --- a/src/video_core/renderer_vulkan/vk_texture_runtime.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_runtime.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include +#include #include "common/literals.h" #include "common/microprofile.h" @@ -119,9 +120,9 @@ u32 UnpackDepthStencil(const VideoCore::StagingData& data, vk::Format dest) { } boost::container::small_vector MakeInitBarriers( - vk::ImageAspectFlags aspect, std::span images, std::size_t num_images) { + vk::ImageAspectFlags aspect, std::span images) { boost::container::small_vector barriers; - for (std::size_t i = 0; i < num_images; i++) { + for (const vk::Image& image : images) { barriers.push_back(vk::ImageMemoryBarrier{ .srcAccessMask = vk::AccessFlagBits::eNone, .dstAccessMask = vk::AccessFlagBits::eNone, @@ -129,7 +130,7 @@ boost::container::small_vector MakeInitBarriers( .newLayout = vk::ImageLayout::eGeneral, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = images[i], + .image = image, .subresourceRange{ .aspectMask = aspect, .baseMipLevel = 0, @@ -219,11 +220,10 @@ Handle MakeHandle(const Instance* instance, u32 width, u32 height, u32 levels, T } vk::UniqueFramebuffer MakeFramebuffer(vk::Device device, vk::RenderPass render_pass, u32 width, - u32 height, std::span attachments, - u32 num_attachments) { + u32 height, std::span attachments) { const vk::FramebufferCreateInfo framebuffer_info = { .renderPass = render_pass, - .attachmentCount = num_attachments, + .attachmentCount = static_cast(attachments.size()), .pAttachments = attachments.data(), .width = width, .height = height, @@ -715,8 +715,7 @@ Surface::Surface(TextureRuntime& runtime_, const VideoCore::SurfaceParams& param ASSERT_MSG(format != vk::Format::eUndefined && levels >= 1, "Image allocation parameters are invalid"); - u32 num_images = 0; - std::array raw_images; + boost::container::static_vector raw_images; vk::ImageCreateFlags flags{}; if (texture_type == VideoCore::TextureType::CubeMap) { @@ -729,18 +728,18 @@ Surface::Surface(TextureRuntime& runtime_, const VideoCore::SurfaceParams& param const bool need_format_list = is_mutable && instance->IsImageFormatListSupported(); handles[0] = MakeHandle(instance, width, height, levels, texture_type, format, traits.usage, flags, traits.aspect, need_format_list, DebugName(false)); - raw_images[num_images++] = handles[0].image; + raw_images.emplace_back(handles[0].image); if (res_scale != 1) { handles[1] = MakeHandle(instance, GetScaledWidth(), GetScaledHeight(), levels, texture_type, format, traits.usage, flags, traits.aspect, need_format_list, DebugName(true)); - raw_images[num_images++] = handles[1].image; + raw_images.emplace_back(handles[1].image); } runtime->renderpass_cache.EndRendering(); - scheduler->Record([raw_images, num_images, aspect = traits.aspect](vk::CommandBuffer cmdbuf) { - const auto barriers = MakeInitBarriers(aspect, raw_images, num_images); + scheduler->Record([raw_images, aspect = traits.aspect](vk::CommandBuffer cmdbuf) { + const auto barriers = MakeInitBarriers(aspect, raw_images); cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eTopOfPipe, vk::PipelineStageFlagBits::eTopOfPipe, vk::DependencyFlagBits::eByRegion, {}, {}, barriers); @@ -758,8 +757,7 @@ Surface::Surface(TextureRuntime& runtime_, const VideoCore::SurfaceBase& surface const bool has_normal = mat && mat->Map(MapType::Normal); const vk::Format format = traits.native; - u32 num_images = 0; - std::array raw_images; + boost::container::static_vector raw_images; vk::ImageCreateFlags flags{}; if (texture_type == VideoCore::TextureType::CubeMap) { @@ -769,23 +767,23 @@ Surface::Surface(TextureRuntime& runtime_, const VideoCore::SurfaceBase& surface const std::string debug_name = DebugName(false, true); handles[0] = MakeHandle(instance, mat->width, mat->height, levels, texture_type, format, traits.usage, flags, traits.aspect, false, debug_name); - raw_images[num_images++] = handles[0].image; + raw_images.emplace_back(handles[0].image); if (res_scale != 1) { handles[1] = MakeHandle(instance, mat->width, mat->height, levels, texture_type, vk::Format::eR8G8B8A8Unorm, traits.usage, flags, traits.aspect, false, debug_name); - raw_images[num_images++] = handles[1].image; + raw_images.emplace_back(handles[1].image); } if (has_normal) { handles[2] = MakeHandle(instance, mat->width, mat->height, levels, texture_type, format, traits.usage, flags, traits.aspect, false, debug_name); - raw_images[num_images++] = handles[2].image; + raw_images.emplace_back(handles[2].image); } runtime->renderpass_cache.EndRendering(); - scheduler->Record([raw_images, num_images, aspect = traits.aspect](vk::CommandBuffer cmdbuf) { - const auto barriers = MakeInitBarriers(aspect, raw_images, num_images); + scheduler->Record([raw_images, aspect = traits.aspect](vk::CommandBuffer cmdbuf) { + const auto barriers = MakeInitBarriers(aspect, raw_images); cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eTopOfPipe, vk::PipelineStageFlagBits::eTopOfPipe, vk::DependencyFlagBits::eByRegion, {}, {}, barriers); @@ -825,11 +823,10 @@ void Surface::Upload(const VideoCore::BufferTextureCopy& upload, scheduler->Record([buffer = runtime->upload_buffer.Handle(), format = traits.native, params, staging, upload](vk::CommandBuffer cmdbuf) { - u32 num_copies = 1; - std::array buffer_image_copies; + boost::container::static_vector buffer_image_copies; const auto rect = upload.texture_rect; - buffer_image_copies[0] = vk::BufferImageCopy{ + buffer_image_copies.emplace_back(vk::BufferImageCopy{ .bufferOffset = upload.buffer_offset, .bufferRowLength = rect.GetWidth(), .bufferImageHeight = rect.GetHeight(), @@ -841,15 +838,16 @@ void Surface::Upload(const VideoCore::BufferTextureCopy& upload, }, .imageOffset = {static_cast(rect.left), static_cast(rect.bottom), 0}, .imageExtent = {rect.GetWidth(), rect.GetHeight(), 1}, - }; + }); if (params.aspect & vk::ImageAspectFlagBits::eStencil) { buffer_image_copies[0].imageSubresource.aspectMask = vk::ImageAspectFlagBits::eDepth; - vk::BufferImageCopy& stencil_copy = buffer_image_copies[1]; + + vk::BufferImageCopy& stencil_copy = + buffer_image_copies.emplace_back(buffer_image_copies[0]); stencil_copy = buffer_image_copies[0]; stencil_copy.bufferOffset += UnpackDepthStencil(staging, format); stencil_copy.imageSubresource.aspectMask = vk::ImageAspectFlagBits::eStencil; - num_copies++; } const vk::ImageMemoryBarrier read_barrier = { @@ -877,7 +875,7 @@ void Surface::Upload(const VideoCore::BufferTextureCopy& upload, vk::DependencyFlagBits::eByRegion, {}, {}, read_barrier); cmdbuf.copyBufferToImage(buffer, params.src_image, vk::ImageLayout::eTransferDstOptimal, - num_copies, buffer_image_copies.data()); + buffer_image_copies); cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer, params.pipeline_flags, vk::DependencyFlagBits::eByRegion, {}, {}, write_barrier); @@ -1085,7 +1083,7 @@ void Surface::ScaleUp(u32 new_scale) { runtime->renderpass_cache.EndRendering(); scheduler->Record( [raw_images = std::array{Image()}, aspect = traits.aspect](vk::CommandBuffer cmdbuf) { - const auto barriers = MakeInitBarriers(aspect, raw_images, raw_images.size()); + const auto barriers = MakeInitBarriers(aspect, raw_images); cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eTopOfPipe, vk::PipelineStageFlagBits::eTopOfPipe, vk::DependencyFlagBits::eByRegion, {}, {}, barriers); @@ -1351,7 +1349,7 @@ vk::Framebuffer Surface::Framebuffer() noexcept { runtime->renderpass_cache.GetRenderpass(color_format, depth_format, false); const auto attachments = std::array{ImageView()}; framebuffers[index] = MakeFramebuffer(instance->GetDevice(), render_pass, GetScaledWidth(), - GetScaledHeight(), attachments, 1); + GetScaledHeight(), attachments); return framebuffers[index].get(); } @@ -1481,17 +1479,16 @@ Framebuffer::Framebuffer(TextureRuntime& runtime, const VideoCore::FramebufferPa image_views[index] = shadow_rendering ? surface->StorageView() : surface->FramebufferView(); }; - u32 num_attachments = 0; - std::array attachments; + boost::container::static_vector attachments; if (color) { prepare(0, color); - attachments[num_attachments++] = image_views[0]; + attachments.emplace_back(image_views[0]); } if (depth) { prepare(1, depth); - attachments[num_attachments++] = image_views[1]; + attachments.emplace_back(image_views[1]); } const vk::Device device = runtime.GetInstance().GetDevice(); @@ -1499,11 +1496,10 @@ Framebuffer::Framebuffer(TextureRuntime& runtime, const VideoCore::FramebufferPa render_pass = renderpass_cache.GetRenderpass(PixelFormat::Invalid, PixelFormat::Invalid, false); framebuffer = MakeFramebuffer(device, render_pass, color->GetScaledWidth(), - color->GetScaledHeight(), {}, 0); + color->GetScaledHeight(), {}); } else { render_pass = renderpass_cache.GetRenderpass(formats[0], formats[1], false); - framebuffer = - MakeFramebuffer(device, render_pass, width, height, attachments, num_attachments); + framebuffer = MakeFramebuffer(device, render_pass, width, height, attachments); } } From b5042a525768f422d9881b8800602dc886b1356d Mon Sep 17 00:00:00 2001 From: kylon <3252255+kylon@users.noreply.github.com> Date: Fri, 23 Feb 2024 00:43:33 +0100 Subject: [PATCH 10/12] Core: update kernel config memory to latest 11.17 (#7460) --- src/core/hle/kernel/config_mem.cpp | 10 +++++----- src/core/hle/kernel/process.cpp | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/core/hle/kernel/config_mem.cpp b/src/core/hle/kernel/config_mem.cpp index e8f612160..eab8a9f67 100644 --- a/src/core/hle/kernel/config_mem.cpp +++ b/src/core/hle/kernel/config_mem.cpp @@ -14,18 +14,18 @@ namespace ConfigMem { Handler::Handler() { std::memset(&config_mem, 0, sizeof(config_mem)); - // Values extracted from firmware 11.2.0-35E - config_mem.kernel_version_min = 0x34; + // Values extracted from firmware 11.17.0-50E + config_mem.kernel_version_min = 0x3a; config_mem.kernel_version_maj = 0x2; config_mem.ns_tid = 0x0004013000008002; config_mem.sys_core_ver = 0x2; config_mem.unit_info = 0x1; // Bit 0 set for Retail config_mem.prev_firm = 0x1; - config_mem.ctr_sdk_ver = 0x0000F297; - config_mem.firm_version_min = 0x34; + config_mem.ctr_sdk_ver = 0x0000F450; + config_mem.firm_version_min = 0x3a; config_mem.firm_version_maj = 0x2; config_mem.firm_sys_core_ver = 0x2; - config_mem.firm_ctr_sdk_ver = 0x0000F297; + config_mem.firm_ctr_sdk_ver = 0x0000F450; } ConfigMemDef& Handler::GetConfigMem() { diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index 17f142cfd..3d0a315ef 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp @@ -210,10 +210,10 @@ void Process::Set3dsxKernelCaps() { }; // Similar to Rosalina, we set kernel version to a recent one. - // This is 11.2.0, to be consistent with core/hle/kernel/config_mem.cpp + // This is 11.17.0, to be consistent with core/hle/kernel/config_mem.cpp // TODO: refactor kernel version out so it is configurable and consistent // among all relevant places. - kernel_version = 0x234; + kernel_version = 0x23a; } void Process::Run(s32 main_thread_priority, u32 stack_size) { From d857743075c2883f041c33b310b10b8b9615a5af Mon Sep 17 00:00:00 2001 From: GPUCode <47210458+GPUCode@users.noreply.github.com> Date: Fri, 23 Feb 2024 01:43:44 +0200 Subject: [PATCH 11/12] Downgrade blend factor crash to warning (#7459) * pica_to_vk: Downgrade assert to warning * pica_to_gl: Downgrade unreachable to warning --- src/video_core/renderer_opengl/pica_to_gl.h | 2 -- src/video_core/renderer_vulkan/pica_to_vk.h | 5 ++++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/video_core/renderer_opengl/pica_to_gl.h b/src/video_core/renderer_opengl/pica_to_gl.h index 15a53b771..fa9409465 100644 --- a/src/video_core/renderer_opengl/pica_to_gl.h +++ b/src/video_core/renderer_opengl/pica_to_gl.h @@ -148,8 +148,6 @@ inline GLenum BlendFunc(Pica::FramebufferRegs::BlendFactor factor) { // Range check table for input if (index >= blend_func_table.size()) { LOG_CRITICAL(Render_OpenGL, "Unknown blend factor {}", index); - UNREACHABLE(); - return GL_ONE; } diff --git a/src/video_core/renderer_vulkan/pica_to_vk.h b/src/video_core/renderer_vulkan/pica_to_vk.h index 6067c07ae..b0dfe891c 100644 --- a/src/video_core/renderer_vulkan/pica_to_vk.h +++ b/src/video_core/renderer_vulkan/pica_to_vk.h @@ -96,7 +96,10 @@ inline vk::BlendFactor BlendFunc(Pica::FramebufferRegs::BlendFactor factor) { }}; const auto index = static_cast(factor); - ASSERT_MSG(index < blend_func_table.size(), "Unknown blend factor {}", index); + if (index >= blend_func_table.size()) { + LOG_CRITICAL(Render_Vulkan, "Unknown blend factor {}", index); + return vk::BlendFactor::eOne; + } return blend_func_table[index]; } From 4f9fc88bb3f501781ec4e06580a5aad4a35691b5 Mon Sep 17 00:00:00 2001 From: Steveice10 <1269164+Steveice10@users.noreply.github.com> Date: Fri, 23 Feb 2024 16:18:16 -0800 Subject: [PATCH 12/12] apt: Improve accuracy of applet slot states on system applet launch. (#7456) --- src/core/hle/service/apt/applet_manager.cpp | 31 ++++++++++++++------- src/core/hle/service/apt/applet_manager.h | 1 + 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/src/core/hle/service/apt/applet_manager.cpp b/src/core/hle/service/apt/applet_manager.cpp index fe640b5c5..0d657cd9f 100644 --- a/src/core/hle/service/apt/applet_manager.cpp +++ b/src/core/hle/service/apt/applet_manager.cpp @@ -373,7 +373,10 @@ ResultVal AppletManager::Initialize(AppletId ap if (active_slot == AppletSlot::Error) { active_slot = slot; - // Wake up the application. + // APT automatically calls enable on the first registered applet. + Enable(attributes); + + // Wake up the applet. SendParameter({ .sender_id = AppletId::None, .destination_id = app_id, @@ -398,7 +401,8 @@ Result AppletManager::Enable(AppletAttributes attributes) { auto slot_data = GetAppletSlot(slot); slot_data->registered = true; - if (slot_data->attributes.applet_pos == AppletPos::System && + if (slot_data->applet_id != AppletId::None && + slot_data->attributes.applet_pos == AppletPos::System && slot_data->attributes.is_home_menu) { slot_data->attributes.raw |= attributes.raw; LOG_DEBUG(Service_APT, "Updated home menu attributes to {:08X}.", @@ -786,16 +790,23 @@ Result AppletManager::PrepareToStartSystemApplet(AppletId applet_id) { Result AppletManager::StartSystemApplet(AppletId applet_id, std::shared_ptr object, const std::vector& buffer) { - auto source_applet_id = AppletId::None; + auto source_applet_id = AppletId::Application; if (last_system_launcher_slot != AppletSlot::Error) { - const auto slot_data = GetAppletSlot(last_system_launcher_slot); - source_applet_id = slot_data->applet_id; + const auto launcher_slot_data = GetAppletSlot(last_system_launcher_slot); + source_applet_id = launcher_slot_data->applet_id; - // If a system applet is launching another system applet, reset the slot to avoid conflicts. - // This is needed because system applets won't necessarily call CloseSystemApplet before - // exiting. - if (last_system_launcher_slot == AppletSlot::SystemApplet) { - slot_data->Reset(); + // APT generally clears and terminates the caller of StartSystemApplet. This helps in + // situations such as a system applet launching another system applet, which would + // otherwise deadlock. + // TODO: In real APT, the check for AppletSlot::Application does not exist; there is + // TODO: something wrong with our implementation somewhere that makes this necessary. + // TODO: Otherwise, games that attempt to launch system applets will be cleared and + // TODO: emulation will crash. + if (!launcher_slot_data->registered || + (last_system_launcher_slot != AppletSlot::Application && + !launcher_slot_data->attributes.no_exit_on_system_applet)) { + launcher_slot_data->Reset(); + // TODO: Implement launcher process termination. } } diff --git a/src/core/hle/service/apt/applet_manager.h b/src/core/hle/service/apt/applet_manager.h index 2c4d0879e..dee8afa4e 100644 --- a/src/core/hle/service/apt/applet_manager.h +++ b/src/core/hle/service/apt/applet_manager.h @@ -152,6 +152,7 @@ union AppletAttributes { u32 raw; BitField<0, 3, AppletPos> applet_pos; + BitField<28, 1, u32> no_exit_on_system_applet; BitField<29, 1, u32> is_home_menu; AppletAttributes() : raw(0) {}