Compare commits

...

2 commits

Author SHA1 Message Date
Ribbit
0f15714b45 [vk] Fast UBO: fix tracking, resize heuristics, add debug guard
Some checks failed
eden-license / license-header (pull_request) Failing after 24s
2025-10-08 02:26:28 +02:00
acd7d792a3
[hle] Stubbed QueryLastPlayTime (#389)
Ported from Torzu, made by Jarrod Norwell.

Co-authored-by: Jarrod Norwell <official.antique@gmail.com>

Co-authored-by: Gamer64 <76565986+Gamer64ytb@users.noreply.github.com>
Reviewed-on: #389
Reviewed-by: crueter <crueter@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Co-authored-by: Gamer64 <gamer64@eden-emu.dev>
Co-committed-by: Gamer64 <gamer64@eden-emu.dev>
2025-10-08 02:08:13 +02:00
6 changed files with 68 additions and 18 deletions

View file

@ -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);

View file

@ -467,6 +467,29 @@ void DeviceMemoryManager<Traits>::ReadBlockUnsafe(DAddr address, void* dest_poin
});
}
#ifdef YUZU_DEBUG
template <typename Traits>
bool DeviceMemoryManager<Traits>::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<u8*>(dest_pointer) + copy_amount;
});
return success;
}
#endif
template <typename Traits>
void DeviceMemoryManager<Traits>::WriteBlockUnsafe(DAddr address, const void* src_pointer,
size_t size) {

View file

@ -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<s32> out_entries, u8 unknown,
OutArray<LastPlayTime, BufferAttr_HipcMapAlias> out_last_play_times,
InArray<s32, BufferAttr_HipcMapAlias> application_ids) {
*out_entries = 1;
*out_last_play_times = {};
R_SUCCEED();
}
} // namespace Service::NS

View file

@ -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<IQueryService> {
public:
explicit IQueryService(Core::System& system_);
@ -31,6 +36,9 @@ public:
private:
Result QueryPlayStatisticsByApplicationIdAndUserAccountId(
Out<PlayStatistics> out_play_statistics, bool unknown, u64 application_id, Uid account_id);
Result QueryLastPlayTime(Out<s32> out_entries, u8 unknown,
OutArray<LastPlayTime, BufferAttr_HipcMapAlias> out_last_play_times,
InArray<s32, BufferAttr_HipcMapAlias> application_ids);
};
} // namespace Service::NS

View file

@ -806,7 +806,7 @@ void BufferCache<P>::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<P>::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<u8> 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<P>::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<P>::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<P>::BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32
} else {
runtime.BindUniformBuffer(buffer, offset, size);
}
channel_state->fast_bound_uniform_buffers[stage] &= ~(1u << binding_index);
}
template <class P>
@ -1789,12 +1797,7 @@ std::span<u8> BufferCache<P>::ImmediateBuffer(size_t wanted_capacity) {
template <class P>
bool BufferCache<P>::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 <class P>

View file

@ -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<std::array<u32, NUM_GRAPHICS_UNIFORM_BUFFERS>, NUM_STAGES>;
using ComputeUniformBufferSizes = std::array<u32, NUM_COMPUTE_UNIFORM_BUFFERS>;
@ -137,8 +138,8 @@ public:
u32 written_compute_texture_buffers = 0;
u32 image_compute_texture_buffers = 0;
std::array<u32, 16> uniform_cache_hits{};
std::array<u32, 16> uniform_cache_shots{};
std::array<u32, NUM_GRAPHICS_UNIFORM_BUFFERS> uniform_cache_hits{};
std::array<u32, NUM_GRAPHICS_UNIFORM_BUFFERS> uniform_cache_shots{};
u32 uniform_buffer_skip_cache_size = DEFAULT_SKIP_CACHE_SIZE;