[common, gpu] Added a page bitset range to convert Subtract within linear operations

This commit is contained in:
CamilleLaVey 2026-02-07 18:30:35 -04:00 committed by crueter
parent 1eaf8bb28f
commit 1b8f4c2062
4 changed files with 151 additions and 1 deletions

View File

@ -0,0 +1,143 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <vector>
#include "common/common_types.h"
#include "common/div_ceil.h"
namespace Common {
template <typename AddressType, u32 PageBits, u64 MaxAddress>
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 <typename Func>
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<AddressType>(size);
const u64 start_page = static_cast<u64>(base_address) >> PageBits;
const u64 end_page = Common::DivCeil(static_cast<u64>(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<AddressType>(size);
const u64 start_page = static_cast<u64>(base_address) >> PageBits;
const u64 end_page = Common::DivCeil(static_cast<u64>(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<std::ptrdiff_t>(start_word + 1),
m_words.begin() + static_cast<std::ptrdiff_t>(end_word), ~0ULL);
} else {
m_words[start_word] &= ~start_mask;
m_words[end_word] &= ~end_mask;
std::fill(m_words.begin() + static_cast<std::ptrdiff_t>(start_word + 1),
m_words.begin() + static_cast<std::ptrdiff_t>(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 <typename Func>
static void EmitRun(u64 run_start_page, u64 run_end_page, AddressType base_address,
AddressType end_address, Func&& func) {
AddressType run_start = static_cast<AddressType>(run_start_page * kPageSize);
AddressType run_end = static_cast<AddressType>(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<u64> m_words;
};
} // namespace Common

View File

@ -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-FileCopyrightText: 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later

View File

@ -21,6 +21,7 @@
#include "common/div_ceil.h" #include "common/div_ceil.h"
#include "common/literals.h" #include "common/literals.h"
#include "common/lru_cache.h" #include "common/lru_cache.h"
#include "common/page_bitset_range_set.h"
#include "common/range_sets.h" #include "common/range_sets.h"
#include "common/scope_exit.h" #include "common/scope_exit.h"
#include "common/settings.h" #include "common/settings.h"
@ -474,7 +475,7 @@ private:
MemoryTracker memory_tracker; MemoryTracker memory_tracker;
Common::RangeSet<DAddr> uncommitted_gpu_modified_ranges; Common::RangeSet<DAddr> uncommitted_gpu_modified_ranges;
Common::RangeSet<DAddr> gpu_modified_ranges; Common::PageBitsetRangeSet<DAddr, CACHING_PAGEBITS, (1ULL << 34)> gpu_modified_ranges;
std::deque<Common::RangeSet<DAddr>> committed_gpu_modified_ranges; std::deque<Common::RangeSet<DAddr>> committed_gpu_modified_ranges;
// Async Buffers // Async Buffers

View File

@ -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-FileCopyrightText: Copyright 2019 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later