From acd7d792a34a809cf4e7f9270b3cee605fb6c35e Mon Sep 17 00:00:00 2001 From: Gamer64 Date: Wed, 8 Oct 2025 02:08:13 +0200 Subject: [PATCH 1/2] [hle] Stubbed QueryLastPlayTime (#389) Ported from Torzu, made by Jarrod Norwell. Co-authored-by: Jarrod Norwell Co-authored-by: Gamer64 <76565986+Gamer64ytb@users.noreply.github.com> Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/389 Reviewed-by: crueter Reviewed-by: MaranBr Co-authored-by: Gamer64 Co-committed-by: Gamer64 --- src/core/hle/service/ns/query_service.cpp | 14 +++++++++++++- src/core/hle/service/ns/query_service.h | 8 ++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/core/hle/service/ns/query_service.cpp b/src/core/hle/service/ns/query_service.cpp index 1384005415..a4632cb6c8 100644 --- a/src/core/hle/service/ns/query_service.cpp +++ b/src/core/hle/service/ns/query_service.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -29,7 +32,7 @@ IQueryService::IQueryService(Core::System& system_) : ServiceFramework{system_, {14, nullptr, "QueryRecentlyPlayedApplication"}, {15, nullptr, "GetRecentlyPlayedApplicationUpdateEvent"}, {16, nullptr, "QueryApplicationPlayStatisticsByUserAccountIdForSystemV0"}, - {17, nullptr, "QueryLastPlayTime"}, + {17, D<&IQueryService::QueryLastPlayTime>, "QueryLastPlayTime"}, {18, nullptr, "QueryApplicationPlayStatisticsForSystem"}, {19, nullptr, "QueryApplicationPlayStatisticsByUserAccountIdForSystem"}, }; @@ -53,4 +56,13 @@ Result IQueryService::QueryPlayStatisticsByApplicationIdAndUserAccountId( R_SUCCEED(); } +Result IQueryService::QueryLastPlayTime( + Out out_entries, u8 unknown, + OutArray out_last_play_times, + InArray application_ids) { + *out_entries = 1; + *out_last_play_times = {}; + R_SUCCEED(); +} + } // namespace Service::NS diff --git a/src/core/hle/service/ns/query_service.h b/src/core/hle/service/ns/query_service.h index c4c82b752e..ba1cddd4ca 100644 --- a/src/core/hle/service/ns/query_service.h +++ b/src/core/hle/service/ns/query_service.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -23,6 +26,8 @@ struct PlayStatistics { }; static_assert(sizeof(PlayStatistics) == 0x28, "PlayStatistics is an invalid size"); +struct LastPlayTime {}; + class IQueryService final : public ServiceFramework { public: explicit IQueryService(Core::System& system_); @@ -31,6 +36,9 @@ public: private: Result QueryPlayStatisticsByApplicationIdAndUserAccountId( Out out_play_statistics, bool unknown, u64 application_id, Uid account_id); + Result QueryLastPlayTime(Out out_entries, u8 unknown, + OutArray out_last_play_times, + InArray application_ids); }; } // namespace Service::NS From 0f15714b457a62ac0c7e42cdc8ebabbcf3f8dc98 Mon Sep 17 00:00:00 2001 From: Ribbit Date: Tue, 7 Oct 2025 17:20:08 -0700 Subject: [PATCH 2/2] [vk] Fast UBO: fix tracking, resize heuristics, add debug guard --- src/core/device_memory_manager.h | 3 ++ src/core/device_memory_manager.inc | 23 +++++++++++++ src/video_core/buffer_cache/buffer_cache.h | 33 ++++++++++--------- .../buffer_cache/buffer_cache_base.h | 5 +-- 4 files changed, 47 insertions(+), 17 deletions(-) diff --git a/src/core/device_memory_manager.h b/src/core/device_memory_manager.h index 6dcf7bb228..2b5ad8847d 100644 --- a/src/core/device_memory_manager.h +++ b/src/core/device_memory_manager.h @@ -109,6 +109,9 @@ public: void ReadBlock(DAddr address, void* dest_pointer, size_t size); void ReadBlockUnsafe(DAddr address, void* dest_pointer, size_t size); +#ifdef YUZU_DEBUG + bool ReadBlockFastChecked(DAddr address, void* dest_pointer, size_t size); +#endif void WriteBlock(DAddr address, const void* src_pointer, size_t size); void WriteBlockUnsafe(DAddr address, const void* src_pointer, size_t size); diff --git a/src/core/device_memory_manager.inc b/src/core/device_memory_manager.inc index 52dff5df9a..b00999772e 100644 --- a/src/core/device_memory_manager.inc +++ b/src/core/device_memory_manager.inc @@ -467,6 +467,29 @@ void DeviceMemoryManager::ReadBlockUnsafe(DAddr address, void* dest_poin }); } +#ifdef YUZU_DEBUG +template +bool DeviceMemoryManager::ReadBlockFastChecked(DAddr address, void* dest_pointer, + size_t size) { + bool success = true; + WalkBlock( + address, size, + [&](size_t copy_amount, DAddr current_vaddr) { + LOG_CRITICAL(Render, "DeviceMemory OOB/unmapped: addr=0x{:x} size={}", current_vaddr, + size); + std::memset(dest_pointer, 0, copy_amount); + success = false; + }, + [&](size_t copy_amount, const u8* const src_ptr) { + std::memcpy(dest_pointer, src_ptr, copy_amount); + }, + [&](const std::size_t copy_amount) { + dest_pointer = static_cast(dest_pointer) + copy_amount; + }); + return success; +} +#endif + template void DeviceMemoryManager::WriteBlockUnsafe(DAddr address, const void* src_pointer, size_t size) { diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index 5223afe937..13b7e98491 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -806,7 +806,7 @@ void BufferCache

::BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32 channel_state->uniform_buffer_binding_sizes[stage][binding_index] != size; if (should_fast_bind) { // We only have to bind when the currently bound buffer is not the fast version - channel_state->fast_bound_uniform_buffers[stage] |= 1U << binding_index; + channel_state->fast_bound_uniform_buffers[stage] |= 1u << binding_index; channel_state->uniform_buffer_binding_sizes[stage][binding_index] = size; runtime.BindFastUniformBuffer(stage, binding_index, size); } @@ -815,13 +815,22 @@ void BufferCache

::BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32 return; } } - if constexpr (IS_OPENGL) { - channel_state->fast_bound_uniform_buffers[stage] |= 1U << binding_index; - channel_state->uniform_buffer_binding_sizes[stage][binding_index] = size; - } + channel_state->fast_bound_uniform_buffers[stage] |= 1u << binding_index; + channel_state->uniform_buffer_binding_sizes[stage][binding_index] = size; // Stream buffer path to avoid stalling on non-Nvidia drivers or Vulkan const std::span span = runtime.BindMappedUniformBuffer(stage, binding_index, size); +#ifdef YUZU_DEBUG + ASSERT(binding_index < NUM_GRAPHICS_UNIFORM_BUFFERS); + ASSERT(span.size() >= size && "UBO stream span too small"); + if (!device_memory.ReadBlockFastChecked(device_addr, span.data(), size)) { + LOG_CRITICAL(Render, "DeviceMemory OOB/unmapped: addr=0x{:x} size={}", device_addr, size); + channel_state->fast_bound_uniform_buffers[stage] &= ~(1u << binding_index); + ASSERT(false); + return; + } +#else device_memory.ReadBlockUnsafe(device_addr, span.data(), size); +#endif return; } // Classic cached path @@ -830,7 +839,8 @@ void BufferCache

::BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32 } // Skip binding if it's not needed and if the bound buffer is not the fast version // This exists to avoid instances where the fast buffer is bound and a GPU write happens - needs_bind |= HasFastUniformBufferBound(stage, binding_index); + const bool was_fast_bound = HasFastUniformBufferBound(stage, binding_index); + needs_bind |= was_fast_bound; if constexpr (HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS) { needs_bind |= channel_state->uniform_buffer_binding_sizes[stage][binding_index] != size; } @@ -839,9 +849,6 @@ void BufferCache

::BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32 } const u32 offset = buffer.Offset(device_addr); if constexpr (IS_OPENGL) { - // Fast buffer will be unbound - channel_state->fast_bound_uniform_buffers[stage] &= ~(1U << binding_index); - // Mark the index as dirty if offset doesn't match const bool is_copy_bind = offset != 0 && !runtime.SupportsNonZeroUniformOffset(); channel_state->dirty_uniform_buffers[stage] |= (is_copy_bind ? 1U : 0U) << index; @@ -855,6 +862,7 @@ void BufferCache

::BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32 } else { runtime.BindUniformBuffer(buffer, offset, size); } + channel_state->fast_bound_uniform_buffers[stage] &= ~(1u << binding_index); } template @@ -1789,12 +1797,7 @@ std::span BufferCache

::ImmediateBuffer(size_t wanted_capacity) { template bool BufferCache

::HasFastUniformBufferBound(size_t stage, u32 binding_index) const noexcept { - if constexpr (IS_OPENGL) { - return ((channel_state->fast_bound_uniform_buffers[stage] >> binding_index) & 1) != 0; - } else { - // Only OpenGL has fast uniform buffers - return false; - } + return ((channel_state->fast_bound_uniform_buffers[stage] >> binding_index) & 1u) != 0; } template diff --git a/src/video_core/buffer_cache/buffer_cache_base.h b/src/video_core/buffer_cache/buffer_cache_base.h index 486d19fb79..09631ffd83 100644 --- a/src/video_core/buffer_cache/buffer_cache_base.h +++ b/src/video_core/buffer_cache/buffer_cache_base.h @@ -53,6 +53,7 @@ constexpr u32 NUM_COMPUTE_UNIFORM_BUFFERS = 8; constexpr u32 NUM_STORAGE_BUFFERS = 16; constexpr u32 NUM_TEXTURE_BUFFERS = 32; constexpr u32 NUM_STAGES = 5; +static_assert(NUM_GRAPHICS_UNIFORM_BUFFERS <= 32, "fast bitmask must fit u32"); using UniformBufferSizes = std::array, NUM_STAGES>; using ComputeUniformBufferSizes = std::array; @@ -137,8 +138,8 @@ public: u32 written_compute_texture_buffers = 0; u32 image_compute_texture_buffers = 0; - std::array uniform_cache_hits{}; - std::array uniform_cache_shots{}; + std::array uniform_cache_hits{}; + std::array uniform_cache_shots{}; u32 uniform_buffer_skip_cache_size = DEFAULT_SKIP_CACHE_SIZE;