From 01fa412c86f97d47e2bbe9db5ff66cf6f4546c10 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Sat, 7 Feb 2026 18:30:35 -0400 Subject: [PATCH] [common, gpu] Added a page bitset range to convert Subtract within linear operations --- src/common/page_bitset_range_set.h | 143 ++++++++++++++++++ src/common/range_sets.inc | 3 + .../buffer_cache/buffer_cache_base.h | 3 +- src/video_core/gpu_thread.h | 3 + 4 files changed, 151 insertions(+), 1 deletion(-) create mode 100644 src/common/page_bitset_range_set.h diff --git a/src/common/page_bitset_range_set.h b/src/common/page_bitset_range_set.h new file mode 100644 index 0000000000..71e68cd061 --- /dev/null +++ b/src/common/page_bitset_range_set.h @@ -0,0 +1,143 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include +#include +#include +#include + +#include "common/common_types.h" +#include "common/div_ceil.h" + +namespace Common { + +template +class PageBitsetRangeSet { +public: + PageBitsetRangeSet() : m_words(kWordCount) { + static_assert((MaxAddress % kPageSize) == 0, "MaxAddress must be page-aligned."); + } + + void Add(AddressType base_address, size_t size) { + Modify(base_address, size, true); + } + + void Subtract(AddressType base_address, size_t size) { + Modify(base_address, size, false); + } + + void Clear() { + std::fill(m_words.begin(), m_words.end(), 0); + } + + bool Empty() const { + return std::none_of(m_words.begin(), m_words.end(), [](u64 word) { return word != 0; }); + } + + template + void ForEachInRange(AddressType base_address, size_t size, Func&& func) const { + if (size == 0 || m_words.empty()) { + return; + } + const AddressType end_address = base_address + static_cast(size); + const u64 start_page = static_cast(base_address) >> PageBits; + const u64 end_page = Common::DivCeil(static_cast(end_address), kPageSize); + const u64 clamped_start = (std::min)(start_page, kPageCount); + const u64 clamped_end = (std::min)(end_page, kPageCount); + if (clamped_start >= clamped_end) { + return; + } + + bool in_run = false; + u64 run_start_page = 0; + for (u64 page = clamped_start; page < clamped_end; ++page) { + if (Test(page)) { + if (!in_run) { + in_run = true; + run_start_page = page; + } + continue; + } + if (in_run) { + EmitRun(run_start_page, page, base_address, end_address, func); + in_run = false; + } + } + if (in_run) { + EmitRun(run_start_page, clamped_end, base_address, end_address, func); + } + } + +private: + static constexpr u64 kPageSize = u64{1} << PageBits; + static constexpr u64 kPageCount = MaxAddress / kPageSize; + static constexpr u64 kWordCount = (kPageCount + 63) / 64; + + void Modify(AddressType base_address, size_t size, bool set_bits) { + if (size == 0 || m_words.empty()) { + return; + } + const AddressType end_address = base_address + static_cast(size); + const u64 start_page = static_cast(base_address) >> PageBits; + const u64 end_page = Common::DivCeil(static_cast(end_address), kPageSize); + const u64 clamped_start = (std::min)(start_page, kPageCount); + const u64 clamped_end = (std::min)(end_page, kPageCount); + if (clamped_start >= clamped_end) { + return; + } + const u64 start_word = clamped_start / 64; + const u64 end_word = (clamped_end - 1) / 64; + const u64 start_mask = ~0ULL << (clamped_start % 64); + const u64 end_mask = (clamped_end % 64) == 0 ? ~0ULL : ((1ULL << (clamped_end % 64)) - 1); + + if (start_word == end_word) { + const u64 mask = start_mask & end_mask; + if (set_bits) { + m_words[start_word] |= mask; + } else { + m_words[start_word] &= ~mask; + } + return; + } + + if (set_bits) { + m_words[start_word] |= start_mask; + m_words[end_word] |= end_mask; + std::fill(m_words.begin() + static_cast(start_word + 1), + m_words.begin() + static_cast(end_word), ~0ULL); + } else { + m_words[start_word] &= ~start_mask; + m_words[end_word] &= ~end_mask; + std::fill(m_words.begin() + static_cast(start_word + 1), + m_words.begin() + static_cast(end_word), 0ULL); + } + } + + bool Test(u64 page) const { + const u64 word = page / 64; + const u64 bit = page % 64; + return (m_words[word] & (1ULL << bit)) != 0; + } + + template + static void EmitRun(u64 run_start_page, u64 run_end_page, AddressType base_address, + AddressType end_address, Func&& func) { + AddressType run_start = static_cast(run_start_page * kPageSize); + AddressType run_end = static_cast(run_end_page * kPageSize); + if (run_start < base_address) { + run_start = base_address; + } + if (run_end > end_address) { + run_end = end_address; + } + if (run_start < run_end) { + func(run_start, run_end); + } + } + + std::vector m_words; +}; + +} // namespace Common diff --git a/src/common/range_sets.inc b/src/common/range_sets.inc index 31382f5c51..47671b94a4 100644 --- a/src/common/range_sets.inc +++ b/src/common/range_sets.inc @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/video_core/buffer_cache/buffer_cache_base.h b/src/video_core/buffer_cache/buffer_cache_base.h index 3c0bc81758..c26402c43c 100644 --- a/src/video_core/buffer_cache/buffer_cache_base.h +++ b/src/video_core/buffer_cache/buffer_cache_base.h @@ -21,6 +21,7 @@ #include "common/div_ceil.h" #include "common/literals.h" #include "common/lru_cache.h" +#include "common/page_bitset_range_set.h" #include "common/range_sets.h" #include "common/scope_exit.h" #include "common/settings.h" @@ -474,7 +475,7 @@ private: MemoryTracker memory_tracker; Common::RangeSet uncommitted_gpu_modified_ranges; - Common::RangeSet gpu_modified_ranges; + Common::PageBitsetRangeSet gpu_modified_ranges; std::deque> committed_gpu_modified_ranges; // Async Buffers diff --git a/src/video_core/gpu_thread.h b/src/video_core/gpu_thread.h index 90c455fdd9..ac1283a338 100644 --- a/src/video_core/gpu_thread.h +++ b/src/video_core/gpu_thread.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later