yuzu-emu
/
yuzu
Archived
1
0
Fork 0

NVDRV: Refactor HeapMapper to use RangeSets

This commit is contained in:
Fernando Sahmkow 2024-02-04 14:44:38 +01:00
parent 01ba6cf610
commit accccc0cbf
1 changed files with 43 additions and 144 deletions

View File

@ -3,110 +3,21 @@
#include <mutex> #include <mutex>
#include <boost/container/small_vector.hpp> #include "common/range_sets.h"
#define BOOST_NO_MT #include "common/range_sets.inc"
#include <boost/pool/detail/mutex.hpp>
#undef BOOST_NO_MT
#include <boost/icl/interval.hpp>
#include <boost/icl/interval_base_set.hpp>
#include <boost/icl/interval_set.hpp>
#include <boost/icl/split_interval_map.hpp>
#include <boost/pool/pool.hpp>
#include <boost/pool/pool_alloc.hpp>
#include <boost/pool/poolfwd.hpp>
#include "core/hle/service/nvdrv/core/heap_mapper.h" #include "core/hle/service/nvdrv/core/heap_mapper.h"
#include "video_core/host1x/host1x.h" #include "video_core/host1x/host1x.h"
namespace boost {
template <typename T>
class fast_pool_allocator<T, default_user_allocator_new_delete, details::pool::null_mutex, 4096, 0>;
}
namespace Service::Nvidia::NvCore { namespace Service::Nvidia::NvCore {
using IntervalCompare = std::less<DAddr>;
using IntervalInstance = boost::icl::interval_type_default<DAddr, std::less>;
using IntervalAllocator = boost::fast_pool_allocator<DAddr>;
using IntervalSet = boost::icl::interval_set<DAddr>;
using IntervalType = typename IntervalSet::interval_type;
template <typename Type>
struct counter_add_functor : public boost::icl::identity_based_inplace_combine<Type> {
// types
typedef counter_add_functor<Type> type;
typedef boost::icl::identity_based_inplace_combine<Type> base_type;
// public member functions
void operator()(Type& current, const Type& added) const {
current += added;
if (current < base_type::identity_element()) {
current = base_type::identity_element();
}
}
// public static functions
static void version(Type&){};
};
using OverlapCombine = counter_add_functor<int>;
using OverlapSection = boost::icl::inter_section<int>;
using OverlapCounter = boost::icl::split_interval_map<DAddr, int>;
struct HeapMapper::HeapMapperInternal { struct HeapMapper::HeapMapperInternal {
HeapMapperInternal(Tegra::Host1x::Host1x& host1x) : device_memory{host1x.MemoryManager()} {} HeapMapperInternal(Tegra::Host1x::Host1x& host1x) : m_device_memory{host1x.MemoryManager()} {}
~HeapMapperInternal() = default; ~HeapMapperInternal() = default;
template <typename Func> Common::RangeSet<VAddr> m_temporary_set;
void ForEachInOverlapCounter(OverlapCounter& current_range, VAddr cpu_addr, u64 size, Common::SplitRangeSet<VAddr> m_mapped_ranges;
Func&& func) { Tegra::MaxwellDeviceMemoryManager& m_device_memory;
const DAddr start_address = cpu_addr; std::mutex m_guard;
const DAddr end_address = start_address + size;
const IntervalType search_interval{start_address, end_address};
auto it = current_range.lower_bound(search_interval);
if (it == current_range.end()) {
return;
}
auto end_it = current_range.upper_bound(search_interval);
for (; it != end_it; it++) {
auto& inter = it->first;
DAddr inter_addr_end = inter.upper();
DAddr inter_addr = inter.lower();
if (inter_addr_end > end_address) {
inter_addr_end = end_address;
}
if (inter_addr < start_address) {
inter_addr = start_address;
}
func(inter_addr, inter_addr_end, it->second);
}
}
void RemoveEachInOverlapCounter(OverlapCounter& current_range,
const IntervalType search_interval, int subtract_value) {
bool any_removals = false;
current_range.add(std::make_pair(search_interval, subtract_value));
do {
any_removals = false;
auto it = current_range.lower_bound(search_interval);
if (it == current_range.end()) {
return;
}
auto end_it = current_range.upper_bound(search_interval);
for (; it != end_it; it++) {
if (it->second <= 0) {
any_removals = true;
current_range.erase(it);
break;
}
}
} while (any_removals);
}
IntervalSet base_set;
OverlapCounter mapping_overlaps;
Tegra::MaxwellDeviceMemoryManager& device_memory;
std::mutex guard;
}; };
HeapMapper::HeapMapper(VAddr start_vaddress, DAddr start_daddress, size_t size, Core::Asid asid, HeapMapper::HeapMapper(VAddr start_vaddress, DAddr start_daddress, size_t size, Core::Asid asid,
@ -116,60 +27,48 @@ HeapMapper::HeapMapper(VAddr start_vaddress, DAddr start_daddress, size_t size,
} }
HeapMapper::~HeapMapper() { HeapMapper::~HeapMapper() {
m_internal->device_memory.Unmap(m_daddress, m_size); // Unmap whatever has been mapped.
m_internal->m_mapped_ranges.ForEach([this](VAddr start_addr, VAddr end_addr, s32 count) {
const size_t sub_size = end_addr - start_addr;
const size_t offset = start_addr - m_vaddress;
m_internal->m_device_memory.Unmap(m_daddress + offset, sub_size);
});
} }
DAddr HeapMapper::Map(VAddr start, size_t size) { DAddr HeapMapper::Map(VAddr start, size_t size) {
std::scoped_lock lk(m_internal->guard); std::scoped_lock lk(m_internal->m_guard);
m_internal->base_set.clear(); // Add the mapping range to a temporary range set.
const IntervalType interval{start, start + size}; m_internal->m_temporary_set.Clear();
m_internal->base_set.insert(interval); m_internal->m_temporary_set.Add(start, size);
m_internal->ForEachInOverlapCounter(m_internal->mapping_overlaps, start, size,
[this](VAddr start_addr, VAddr end_addr, int) { // Remove anything that's already mapped from the temporary range set.
const IntervalType other{start_addr, end_addr}; m_internal->m_mapped_ranges.ForEachInRange(
m_internal->base_set.subtract(other); start, size, [this](VAddr start_addr, VAddr end_addr, s32) {
}); m_internal->m_temporary_set.Subtract(start_addr, end_addr - start_addr);
if (!m_internal->base_set.empty()) { });
auto it = m_internal->base_set.begin();
auto end_it = m_internal->base_set.end(); // Map anything that has not been mapped yet.
for (; it != end_it; it++) { m_internal->m_temporary_set.ForEach([this](VAddr start_addr, VAddr end_addr) {
const VAddr inter_addr_end = it->upper(); const size_t sub_size = end_addr - start_addr;
const VAddr inter_addr = it->lower(); const size_t offset = start_addr - m_vaddress;
const size_t offset = inter_addr - m_vaddress; m_internal->m_device_memory.Map(m_daddress + offset, m_vaddress + offset, sub_size, m_asid);
const size_t sub_size = inter_addr_end - inter_addr; });
m_internal->device_memory.Map(m_daddress + offset, m_vaddress + offset, sub_size,
m_asid); // Add the mapping range to the split map, to register the map and overlaps.
} m_internal->m_mapped_ranges.Add(start, size);
} m_internal->m_temporary_set.Clear();
m_internal->mapping_overlaps += std::make_pair(interval, 1); return m_daddress + static_cast<DAddr>(start - m_vaddress);
m_internal->base_set.clear();
return m_daddress + (start - m_vaddress);
} }
void HeapMapper::Unmap(VAddr start, size_t size) { void HeapMapper::Unmap(VAddr start, size_t size) {
std::scoped_lock lk(m_internal->guard); std::scoped_lock lk(m_internal->m_guard);
m_internal->base_set.clear();
m_internal->ForEachInOverlapCounter(m_internal->mapping_overlaps, start, size, // Just subtract the range and whatever is deleted, unmap it.
[this](VAddr start_addr, VAddr end_addr, int value) { m_internal->m_mapped_ranges.Subtract(start, size, [this](VAddr start_addr, VAddr end_addr) {
if (value <= 1) { const size_t sub_size = end_addr - start_addr;
const IntervalType other{start_addr, end_addr}; const size_t offset = start_addr - m_vaddress;
m_internal->base_set.insert(other); m_internal->m_device_memory.Unmap(m_daddress + offset, sub_size);
} });
});
if (!m_internal->base_set.empty()) {
auto it = m_internal->base_set.begin();
auto end_it = m_internal->base_set.end();
for (; it != end_it; it++) {
const VAddr inter_addr_end = it->upper();
const VAddr inter_addr = it->lower();
const size_t offset = inter_addr - m_vaddress;
const size_t sub_size = inter_addr_end - inter_addr;
m_internal->device_memory.Unmap(m_daddress + offset, sub_size);
}
}
const IntervalType to_remove{start, start + size};
m_internal->RemoveEachInOverlapCounter(m_internal->mapping_overlaps, to_remove, -1);
m_internal->base_set.clear();
} }
} // namespace Service::Nvidia::NvCore } // namespace Service::Nvidia::NvCore