From b5c86787aba713b97799ebca820a2b66a4f0795e Mon Sep 17 00:00:00 2001 From: DieguinDG Date: Fri, 26 Dec 2025 13:28:47 -0300 Subject: [PATCH] jdlo modifications --- src/core/CMakeLists.txt | 6 +- src/core/core.cpp | 61 ++++++- src/core/file_sys/savedata_factory.cpp | 96 +++++++++- .../hle/service/filesystem/filesystem.cpp | 148 ++++++++++++++++ src/core/internal_network/legacy_online.cpp | 164 ++++++++++++++++++ src/core/internal_network/legacy_online.h | 30 ++++ src/video_core/host1x/vic.cpp | 29 +++- 7 files changed, 519 insertions(+), 15 deletions(-) create mode 100644 src/core/internal_network/legacy_online.cpp create mode 100644 src/core/internal_network/legacy_online.h diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index a961eff8bf..d3fba40177 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -17,8 +17,6 @@ add_library(core STATIC constants.h core.cpp core.h - game_settings.cpp - game_settings.h core_timing.cpp core_timing.h cpu_manager.cpp @@ -45,7 +43,11 @@ add_library(core STATIC device_memory.cpp device_memory.h device_memory_manager.h + device_memory.h + device_memory_manager.h device_memory_manager.inc + internal_network/legacy_online.cpp + internal_network/legacy_online.h file_sys/bis_factory.cpp file_sys/bis_factory.h file_sys/card_image.cpp diff --git a/src/core/core.cpp b/src/core/core.cpp index bf97184f8f..527d38410e 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -49,6 +49,7 @@ #include "core/hle/service/services.h" #include "core/hle/service/set/system_settings_server.h" #include "core/hle/service/sm/sm.h" +#include "core/internal_network/legacy_online.h" #include "core/internal_network/network.h" #include "core/loader/loader.h" #include "core/memory.h" @@ -137,6 +138,13 @@ struct System::Impl { kernel.SetMulticore(is_multicore); cpu_manager.SetMulticore(is_multicore); cpu_manager.SetAsyncGpu(is_async_gpu); + cpu_manager.SetMulticore(is_multicore); + cpu_manager.SetAsyncGpu(is_async_gpu); + + if (!legacy_online) { + legacy_online = std::make_unique(); + legacy_online->Start(); + } } void ReinitializeIfNecessary(System& system) { @@ -293,6 +301,48 @@ struct System::Impl { return SystemResultStatus::Success; } + + void LoadOverrides(u64 programId) const { + std::string vendor = gpu_core->Renderer().GetDeviceVendor(); + LOG_INFO(Core, "GPU Vendor: {}", vendor); + + // Reset all per-game flags + Settings::values.use_squashed_iterated_blend = false; + + // Insert PC overrides here + + #ifdef ANDROID + // Example on how to set a setting based on the program ID and vendor + if (programId == 0x010028600EBDA000 && vendor == "Mali") { // Mario 3d World + // Settings::values.example = true; + } + + // Example array of program IDs + const std::array example_array = { + //0xprogramId + 0x0004000000033400, // Game 1 + 0x0004000000033500 // Game 2 + // And so on + }; + + for (auto id : example_array) { + if (programId == id) { + // Settings::values.example = true; + break; + } + } + + #endif + + // Ninja Gaiden Ragebound + constexpr u64 ngr = 0x0100781020710000ULL; + + if (programId == ngr) { + LOG_INFO(Core, "Enabling game specifc override: use_squashed_iterated_blend"); + Settings::values.use_squashed_iterated_blend = true; + } + } + SystemResultStatus Load(System& system, Frontend::EmuWindow& emu_window, const std::string& filepath, Service::AM::FrontendAppletParameters& params) { @@ -378,8 +428,7 @@ struct System::Impl { LOG_ERROR(Core, "Failed to find program id for ROM"); } - - GameSettings::LoadOverrides(program_id, gpu_core->Renderer()); + LoadOverrides(program_id); if (auto room_member = Network::GetRoomMember().lock()) { Network::GameInfo game_info; game_info.name = name; @@ -428,6 +477,11 @@ struct System::Impl { stop_event = {}; Network::RestartSocketOperations(); + if (legacy_online) { + legacy_online->Stop(); + legacy_online.reset(); + } + if (auto room_member = Network::GetRoomMember().lock()) { Network::GameInfo game_info{}; room_member->SendGameInfo(game_info); @@ -516,6 +570,9 @@ struct System::Impl { /// Network instance Network::NetworkInstance network_instance; + + /// Legacy Online Service + std::unique_ptr legacy_online; /// Debugger std::unique_ptr debugger; diff --git a/src/core/file_sys/savedata_factory.cpp b/src/core/file_sys/savedata_factory.cpp index 114a18891f..9e062293b0 100644 --- a/src/core/file_sys/savedata_factory.cpp +++ b/src/core/file_sys/savedata_factory.cpp @@ -11,6 +11,8 @@ #include "core/core.h" #include "core/file_sys/savedata_factory.h" #include "core/file_sys/vfs/vfs.h" +#include "core/file_sys/sdmc_factory.h" +#include "core/hle/service/filesystem/filesystem.h" namespace FileSys { @@ -74,20 +76,97 @@ VirtualDir SaveDataFactory::Create(SaveDataSpaceId space, const SaveDataAttribut VirtualDir SaveDataFactory::Open(SaveDataSpaceId space, const SaveDataAttribute& meta) const { - const auto save_directory = GetFullPath(program_id, dir, space, meta.type, meta.program_id, + u64 target_program_id = meta.program_id; + // CRITICAL FIX: If the game requests Cache/Temp with ProgramID 0 (generic), + // we MUST redirect it to the actual running TitleID, otherwise it looks in '.../0000000000000000'. + if ((meta.type == SaveDataType::Cache || meta.type == SaveDataType::Temporary) && target_program_id == 0) { + target_program_id = system.GetApplicationProcessProgramID(); + LOG_INFO(Service_FS, "Redirecting generic Cache request (ID 0) to active TitleID: {:016X}", target_program_id); + } + + const auto save_directory = GetFullPath(program_id, dir, space, meta.type, target_program_id, meta.user_id, meta.system_save_data_id); auto out = dir->GetDirectoryRelative(save_directory); + // Fallback for Ryujinx-style cache paths: sdcard/Nintendo/save/{TitleID} (No /cache/ subdir) + // Also include Temporary, as games like Just Dance use Temporary storage for cache-like data (MapBaseCache), + // and Ryujinx stores this in the same simple 'save/{ID}' structure. + // Fallback logic removed. We now enforce standard paths. + // User instructions will direct them to the correct folder. + + if (out == nullptr) { + LOG_WARNING(Service_FS, "Cache/Save path NOT FOUND: '{}'. Auto-create={}", save_directory, auto_create); + } else { + LOG_INFO(Service_FS, "Cache/Save path FOUND: '{}'", save_directory); + } + if (out == nullptr && (ShouldSaveDataBeAutomaticallyCreated(space, meta) && auto_create)) { + LOG_INFO(Service_FS, "Auto-creating save directory..."); return Create(space, meta); } + if (out != nullptr) { + // Some emulators (Ryujinx) or even different firmware versions may rely on the commit + // directories /0 or /1 being present for cache or save data. + // We prioritizing /1 as it usually implies a newer commit if both exist, + // but /0 is what's commonly used by Ryujinx for cache. + + // Ryujinx behavior: If 0 exists and 1 does not, copy 0 to 1. + auto dir_0 = out->GetSubdirectory("0"); + auto dir_1 = out->GetSubdirectory("1"); + + if (dir_0) LOG_INFO(Service_FS, "Found subdirectory '0' in save path."); + if (dir_1) LOG_INFO(Service_FS, "Found subdirectory '1' in save path."); + + if (dir_0 != nullptr && dir_1 == nullptr) { + LOG_INFO(Service_FS, "Ryujinx structure detected: '0' exists, '1' missing. Copying 0->1 for compatibility."); + dir_1 = out->CreateSubdirectory("1"); + if (dir_1 != nullptr) { + // Copy contents from 0 to 1 + VfsRawCopyD(dir_0, dir_1); + } + } + + // Check for 'Addressables' and 'Addressables2' and delete 'json.cache' if present. + // This is a specific workaround for games (e.g. Just Dance) that freeze if they find old cache metadata. + // We force them to regenerate it. + const auto CleanCache = [](VirtualDir root) { + if (root == nullptr) return; + const char* subdirs[] = {"Addressables", "Addressables2"}; + for (const char* subdir_name : subdirs) { + auto subdir = root->GetSubdirectory(subdir_name); + if (subdir != nullptr) { + if (subdir->DeleteFile("json.cache")) { + LOG_INFO(Service_FS, "Deleted stale 'json.cache' in '{}'", subdir_name); + } + } + } + }; + + if (dir_1 != nullptr) { + LOG_INFO(Service_FS, "Returning subdirectory '1' as Save Root."); + CleanCache(dir_1); + return dir_1; + } + if (dir_0 != nullptr) { + LOG_INFO(Service_FS, "Returning subdirectory '0' as Save Root."); + CleanCache(dir_0); + return dir_0; + } + + LOG_INFO(Service_FS, "No '0' or '1' subdirectories found. Returning parent folder as Save Root."); + CleanCache(out); + } + return out; } VirtualDir SaveDataFactory::GetSaveDataSpaceDirectory(SaveDataSpaceId space) const { - return dir->GetDirectoryRelative(GetSaveDataSpaceIdPath(space)); + const auto path = GetSaveDataSpaceIdPath(space); + // Ensure the directory exists, otherwise FindAllSaves fails. + return GetOrCreateDirectoryRelative(dir, path); + // return dir->GetDirectoryRelative(GetSaveDataSpaceIdPath(space)); } std::string SaveDataFactory::GetSaveDataSpaceIdPath(SaveDataSpaceId space) { @@ -96,12 +175,12 @@ std::string SaveDataFactory::GetSaveDataSpaceIdPath(SaveDataSpaceId space) { return "/system/"; case SaveDataSpaceId::User: case SaveDataSpaceId::SdUser: + case SaveDataSpaceId::Temporary: // Map into User so we can find the save/ folder return "/user/"; - case SaveDataSpaceId::Temporary: - return "/temp/"; default: - ASSERT_MSG(false, "Unrecognized SaveDataSpaceId: {:02X}", static_cast(space)); - return "/unrecognized/"; ///< To prevent corruption when ignoring asserts. + // ASSERT_MSG(false, "Unrecognized SaveDataSpaceId: {:02X}", static_cast(space)); + LOG_WARNING(Service_FS, "Unrecognized SaveDataSpaceId: {:02X}, defaulting to /user/", static_cast(space)); + return "/user/"; } } @@ -137,8 +216,9 @@ std::string SaveDataFactory::GetFullPath(ProgramId program_id, VirtualDir dir, return fmt::format("{}save/{:016X}/{:016X}{:016X}/{:016X}", out, 0, user_id[1], user_id[0], title_id); case SaveDataType::Temporary: - return fmt::format("{}{:016X}/{:016X}{:016X}/{:016X}", out, 0, user_id[1], user_id[0], - title_id); + // Unified Cache/Temporary Path: Always use save/cache/{TitleID} + // This simplifies user instructions and avoids UUID/Permission issues. + return fmt::format("{}save/cache/{:016X}", out, title_id); case SaveDataType::Cache: return fmt::format("{}save/cache/{:016X}", out, title_id); default: diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp index 9d7de4242e..3023119020 100644 --- a/src/core/hle/service/filesystem/filesystem.cpp +++ b/src/core/hle/service/filesystem/filesystem.cpp @@ -686,6 +686,154 @@ void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool ove using EdenPath = Common::FS::EdenPath; const auto sdmc_dir_path = Common::FS::GetEdenPath(EdenPath::SDMCDir); const auto sdmc_load_dir_path = sdmc_dir_path / "atmosphere/contents"; + + // If the NAND user save location doesn't exist but the SDMC contains + // Nintendo/save (common portable save structure), create a host-side + // symlink so the emulator will see those saves under the expected NAND path. + // This helps users who placed saves under `/user/sdmc/Nintendo/save/...`. + try { + const auto nand_fs_path = Common::FS::GetEdenPath(EdenPath::NANDDir); + const auto sdmc_fs_path = sdmc_dir_path; + const auto nand_user_save_path = nand_fs_path / "user" / "save"; + const auto sdmc_nintendo_save_path = sdmc_fs_path / "Nintendo" / "save"; + + std::error_code ec; + if (!std::filesystem::exists(nand_user_save_path) && + std::filesystem::exists(sdmc_nintendo_save_path)) { + std::filesystem::create_directory_symlink(sdmc_nintendo_save_path, nand_user_save_path, ec); + if (ec) { + LOG_WARNING(Service_FS, "Could not create symlink {} -> {}: {}", + Common::FS::PathToUTF8String(nand_user_save_path), + Common::FS::PathToUTF8String(sdmc_nintendo_save_path), ec.message()); + + // Fallback: copy the SDMC saves into the NAND save folder so the emulator can see them + try { + if (!std::filesystem::exists(nand_user_save_path)) { + std::filesystem::create_directories(nand_user_save_path); + } + std::filesystem::copy(sdmc_nintendo_save_path, nand_user_save_path, + std::filesystem::copy_options::recursive | + std::filesystem::copy_options::skip_existing, + ec); + if (ec) { + LOG_WARNING(Service_FS, "Failed to copy SDMC saves to {}: {}", + Common::FS::PathToUTF8String(nand_user_save_path), ec.message()); + } else { + LOG_INFO(Service_FS, "Copied SDMC saves to NAND save path: {} -> {}", + Common::FS::PathToUTF8String(sdmc_nintendo_save_path), + Common::FS::PathToUTF8String(nand_user_save_path)); + } + } catch (const std::exception& ex) { + LOG_WARNING(Service_FS, "Exception while copying SDMC saves: {}", ex.what()); + } + } else { + LOG_INFO(Service_FS, "Linked NAND save path to SDMC saves: {} -> {}", + Common::FS::PathToUTF8String(nand_user_save_path), + Common::FS::PathToUTF8String(sdmc_nintendo_save_path)); + } + } + + // Also support official-style SD cache directory name used by some Switch setups. + // If users placed saves or cache under `/user/sdmc/Nintendo/SD_Cache.0000`, + // expose it to the NAND user save path as well so the emulator can find them. + const auto sdmc_nintendo_cache_path = sdmc_fs_path / "Nintendo" / "SD_Cache.0000"; + if (!std::filesystem::exists(nand_user_save_path) && + std::filesystem::exists(sdmc_nintendo_cache_path)) { + std::error_code ec2; + std::filesystem::create_directory_symlink(sdmc_nintendo_cache_path, nand_user_save_path, + ec2); + if (ec2) { + LOG_WARNING(Service_FS, + "Could not create symlink {} -> {}: {}", + Common::FS::PathToUTF8String(nand_user_save_path), + Common::FS::PathToUTF8String(sdmc_nintendo_cache_path), ec2.message()); + + // Fallback: copy the SDMC cache into the NAND save folder so the emulator can see them + try { + if (!std::filesystem::exists(nand_user_save_path)) { + std::filesystem::create_directories(nand_user_save_path); + } + std::filesystem::copy(sdmc_nintendo_cache_path, nand_user_save_path, + std::filesystem::copy_options::recursive | + std::filesystem::copy_options::skip_existing, + ec2); + if (ec2) { + LOG_WARNING(Service_FS, "Failed to copy SDMC cache to {}: {}", + Common::FS::PathToUTF8String(nand_user_save_path), ec2.message()); + } else { + LOG_INFO(Service_FS, "Copied SDMC cache to NAND save path: {} -> {}", + Common::FS::PathToUTF8String(sdmc_nintendo_cache_path), + Common::FS::PathToUTF8String(nand_user_save_path)); + } + } catch (const std::exception& ex) { + LOG_WARNING(Service_FS, "Exception while copying SDMC cache: {}", ex.what()); + } + } else { + LOG_INFO(Service_FS, "Linked NAND save path to SDMC cache: {} -> {}", + Common::FS::PathToUTF8String(nand_user_save_path), + Common::FS::PathToUTF8String(sdmc_nintendo_cache_path)); + } + } + + // If the NAND save folder already exists, ensure individual entries from + // SDMC (both `Nintendo/save` and `Nintendo/SD_Cache.0000`) are visible + // inside it: create per-entry symlinks (with copy fallback) for missing + // title/account directories so saves placed in SDMC are reachable. + auto try_merge_sdmc_entries = [&](const std::filesystem::path& sdmc_src) { + try { + if (!std::filesystem::exists(sdmc_src)) + return; + + std::error_code ec; + if (!std::filesystem::exists(nand_user_save_path)) + std::filesystem::create_directories(nand_user_save_path, ec); + + for (auto& ent : std::filesystem::directory_iterator(sdmc_src)) { + const auto name = ent.path().filename(); + const auto target = nand_user_save_path / name; + if (std::filesystem::exists(target)) + continue; + + std::error_code ec2; + std::filesystem::create_directory_symlink(ent.path(), target, ec2); + if (ec2) { + LOG_WARNING(Service_FS, "Could not create symlink {} -> {}: {}", + Common::FS::PathToUTF8String(target), + Common::FS::PathToUTF8String(ent.path()), ec2.message()); + try { + std::filesystem::copy(ent.path(), target, + std::filesystem::copy_options::recursive | + std::filesystem::copy_options::skip_existing, + ec2); + if (ec2) { + LOG_WARNING(Service_FS, "Failed to copy {} -> {}: {}", + Common::FS::PathToUTF8String(ent.path()), + Common::FS::PathToUTF8String(target), ec2.message()); + } else { + LOG_INFO(Service_FS, "Copied SDMC entry to NAND: {} -> {}", + Common::FS::PathToUTF8String(ent.path()), + Common::FS::PathToUTF8String(target)); + } + } catch (const std::exception& ex) { + LOG_WARNING(Service_FS, "Exception while copying SDMC entry: {}", + ex.what()); + } + } else { + LOG_INFO(Service_FS, "Linked NAND entry to SDMC: {} -> {}", + Common::FS::PathToUTF8String(target), + Common::FS::PathToUTF8String(ent.path())); + } + } + } catch (const std::exception& ex) { + LOG_WARNING(Service_FS, "Exception while merging SDMC entries: {}", ex.what()); + } + }; + + try_merge_sdmc_entries(sdmc_nintendo_save_path); + try_merge_sdmc_entries(sdmc_nintendo_cache_path); + } catch (const std::exception& e) { + LOG_WARNING(Service_FS, "Exception while linking SDMC saves: {}", e.what()); + } const auto rw_mode = FileSys::OpenMode::ReadWrite; auto nand_directory = diff --git a/src/core/internal_network/legacy_online.cpp b/src/core/internal_network/legacy_online.cpp new file mode 100644 index 0000000000..28bea3e676 --- /dev/null +++ b/src/core/internal_network/legacy_online.cpp @@ -0,0 +1,164 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "core/internal_network/legacy_online.h" + +#include +#include + +#ifdef _WIN32 +#include +#else +#include +#include +#include +#include +#endif + +#include "common/logging/log.h" + +namespace Network { + +LegacyOnlineService::LegacyOnlineService() = default; + +LegacyOnlineService::~LegacyOnlineService() { + Stop(); +} + +void LegacyOnlineService::Start() { + if (is_running) { + return; + } + + is_running = true; + worker_thread = std::thread(&LegacyOnlineService::ServerLoop, this); +} + +void LegacyOnlineService::Stop() { + if (!is_running) { + return; + } + + is_running = false; + + // Close socket to wake up the thread if it's blocked on recvfrom + if (socket_fd != ~0ULL) { +#ifdef _WIN32 + closesocket(static_cast(socket_fd)); +#else + close(static_cast(socket_fd)); +#endif + socket_fd = ~0ULL; + } + + if (worker_thread.joinable()) { + worker_thread.join(); + } +} + +void LegacyOnlineService::ServerLoop() { + LOG_INFO(Network, "Starting Legacy Online UDP Server on port {}", PORT); + +#ifdef _WIN32 + WSADATA wsa_data; + if (WSAStartup(MAKEWORD(2, 2), &wsa_data) != 0) { + LOG_ERROR(Network, "WSAStartup failed"); + is_running = false; + return; + } +#endif + + + + auto s = socket(AF_INET, SOCK_DGRAM, 0); +#ifdef _WIN32 + if (s == INVALID_SOCKET) { +#else + if (s == -1) { +#endif + LOG_ERROR(Network, "Failed to create socket"); + is_running = false; + return; + } + socket_fd = static_cast(s); + + int opt = 1; +#ifdef _WIN32 + setsockopt(static_cast(socket_fd), SOL_SOCKET, SO_REUSEADDR, (const char*)&opt, sizeof(opt)); +#else + setsockopt(static_cast(socket_fd), SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); +#endif + + sockaddr_in server_addr{}; + server_addr.sin_family = AF_INET; + server_addr.sin_addr.s_addr = INADDR_ANY; + server_addr.sin_port = htons(PORT); + + int res = -1; +#ifdef _WIN32 + res = bind(static_cast(socket_fd), (sockaddr*)&server_addr, sizeof(server_addr)); +#else + res = bind(static_cast(socket_fd), (sockaddr*)&server_addr, sizeof(server_addr)); +#endif + + if (res < 0) { + LOG_ERROR(Network, "Failed to bind to port {}", PORT); +#ifdef _WIN32 + closesocket(static_cast(socket_fd)); +#else + close(static_cast(socket_fd)); +#endif + socket_fd = ~0ULL; + is_running = false; + return; + } + + LOG_INFO(Network, "Legacy Online Server waiting for messages..."); + + // Set a timeout for recvfrom so check is_running periodically if not closed via socket + // Alternatively, closing the socket (as done in Stop) will cause recvfrom to return error + + char buffer[2048]; + while (is_running) { + sockaddr_in client_addr{}; +#ifdef _WIN32 + int client_len = sizeof(client_addr); +#else + socklen_t client_len = sizeof(client_addr); +#endif + + int len = -1; +#ifdef _WIN32 + len = recvfrom(static_cast(socket_fd), buffer, sizeof(buffer), 0, (sockaddr*)&client_addr, &client_len); +#else + len = recvfrom(static_cast(socket_fd), buffer, sizeof(buffer), 0, (sockaddr*)&client_addr, &client_len); +#endif + + if (!is_running) break; + + if (len > 0) { + // Send ACK + const char* ack_msg = "ACK"; +#ifdef _WIN32 + sendto(static_cast(socket_fd), ack_msg, static_cast(strlen(ack_msg)), 0, (sockaddr*)&client_addr, client_len); +#else + sendto(static_cast(socket_fd), ack_msg, strlen(ack_msg), 0, (sockaddr*)&client_addr, client_len); +#endif + } else { + // Error or closed + // If we closed the socket in Stop(), this will likely trigger. + break; + } + } + +#ifdef _WIN32 + if (socket_fd != ~0ULL) closesocket(static_cast(socket_fd)); + WSACleanup(); +#else + if (socket_fd != ~0ULL) close(static_cast(socket_fd)); +#endif + socket_fd = ~0ULL; + LOG_INFO(Network, "Legacy Online Server stopped"); +} + +} // namespace Network diff --git a/src/core/internal_network/legacy_online.h b/src/core/internal_network/legacy_online.h new file mode 100644 index 0000000000..fdeba8821a --- /dev/null +++ b/src/core/internal_network/legacy_online.h @@ -0,0 +1,30 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include +#include +#include +#include + +namespace Network { + +class LegacyOnlineService { +public: + LegacyOnlineService(); + ~LegacyOnlineService(); + + void Start(); + void Stop(); + +private: + void ServerLoop(); + + std::atomic_bool is_running{false}; + std::thread worker_thread; + uintptr_t socket_fd{~0ULL}; // ~0ULL is approx -1 equivalent for unsigned + static constexpr int PORT = 6000; +}; + +} // namespace Network diff --git a/src/video_core/host1x/vic.cpp b/src/video_core/host1x/vic.cpp index 21cf5f4e92..246c176114 100644 --- a/src/video_core/host1x/vic.cpp +++ b/src/video_core/host1x/vic.cpp @@ -107,9 +107,19 @@ void Vic::Execute() { auto output_height{config.output_surface_config.out_surface_height + 1}; output_surface.resize_destructive(output_width * output_height); + // Initialize the surface with the appropriate black pixel + Pixel black_pixel{}; + if (config.output_surface_config.out_pixel_format == VideoPixelFormat::Y8__V8U8_N420) { + // Y=0, U=512, V=512 (10-bit), A=0 + black_pixel = {0, 512, 512, 0}; + } else { + // R=0, G=0, B=0, A=0 + black_pixel = {0, 0, 0, 0}; + } + std::fill(output_surface.begin(), output_surface.end(), black_pixel); + if (Settings::values.nvdec_emulation.GetValue() == Settings::NvdecEmulation::Off) [[unlikely]] { - // Fill the frame with black, as otherwise they can have random data and be very glitchy. - std::fill(output_surface.begin(), output_surface.end(), Pixel{}); + } else { for (size_t i = 0; i < config.slot_structs.size(); i++) { auto& slot_config{config.slot_structs[i]}; @@ -122,7 +132,18 @@ void Vic::Execute() { nvdec_id = frame_queue.VicFindNvdecFdFromOffset(luma_offset); } - if (auto frame = frame_queue.GetFrame(nvdec_id, luma_offset); frame) { + auto frame = frame_queue.GetFrame(nvdec_id, luma_offset); + if (!frame) { + // We might've failed to find the frame, or the nvdec id is stale/wrong. + // Try to find the nvdec id again. + const s32 new_id = frame_queue.VicFindNvdecFdFromOffset(luma_offset); + if (new_id != -1) { + nvdec_id = new_id; + frame = frame_queue.GetFrame(nvdec_id, luma_offset); + } + } + + if (frame) { if (frame.get()) { switch (frame->GetPixelFormat()) { case AV_PIX_FMT_YUV420P: @@ -170,6 +191,7 @@ void Vic::ReadProgressiveY8__V8U8_N420(const SlotStruct& slot, std::spanGetWidth(), s32(out_luma_width))}; const auto in_luma_height{(std::min)(frame->GetHeight(), s32(out_luma_height))}; @@ -219,6 +241,7 @@ void Vic::ReadInterlacedY8__V8U8_N420(const SlotStruct& slot, std::spanGetWidth(), s32(out_luma_width))}; [[maybe_unused]] const auto in_luma_height{