mirror of https://git.suyu.dev/suyu/suyu
BufferCache: Rework mapping caching.
This commit is contained in:
parent
86d8563314
commit
5f4b746a1e
|
@ -25,6 +25,8 @@ class RasterizerInterface;
|
||||||
|
|
||||||
namespace VideoCommon {
|
namespace VideoCommon {
|
||||||
|
|
||||||
|
using MapInterval = std::shared_ptr<MapIntervalBase>;
|
||||||
|
|
||||||
template <typename TBuffer, typename TBufferType, typename StreamBuffer>
|
template <typename TBuffer, typename TBufferType, typename StreamBuffer>
|
||||||
class BufferCache {
|
class BufferCache {
|
||||||
public:
|
public:
|
||||||
|
@ -90,9 +92,11 @@ public:
|
||||||
|
|
||||||
std::vector<MapInterval> objects = GetMapsInRange(addr, size);
|
std::vector<MapInterval> objects = GetMapsInRange(addr, size);
|
||||||
for (auto& object : objects) {
|
for (auto& object : objects) {
|
||||||
|
if (object->IsRegistered()) {
|
||||||
Unregister(object);
|
Unregister(object);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
virtual const TBufferType* GetEmptyBuffer(std::size_t size) = 0;
|
virtual const TBufferType* GetEmptyBuffer(std::size_t size) = 0;
|
||||||
|
|
||||||
|
@ -120,51 +124,54 @@ protected:
|
||||||
std::size_t dst_offset, std::size_t size) = 0;
|
std::size_t dst_offset, std::size_t size) = 0;
|
||||||
|
|
||||||
/// Register an object into the cache
|
/// Register an object into the cache
|
||||||
void Register(const MapInterval& new_interval, const GPUVAddr gpu_addr) {
|
void Register(const MapInterval& new_map) {
|
||||||
const CacheAddr cache_ptr = new_interval.start;
|
const CacheAddr cache_ptr = new_map->GetStart();
|
||||||
const std::size_t size = new_interval.end - new_interval.start;
|
|
||||||
const std::optional<VAddr> cpu_addr =
|
const std::optional<VAddr> cpu_addr =
|
||||||
system.GPU().MemoryManager().GpuToCpuAddress(gpu_addr);
|
system.GPU().MemoryManager().GpuToCpuAddress(new_map->GetGpuAddress());
|
||||||
if (!cache_ptr || !cpu_addr) {
|
if (!cache_ptr || !cpu_addr) {
|
||||||
LOG_CRITICAL(HW_GPU, "Failed to register buffer with unmapped gpu_address 0x{:016x}",
|
LOG_CRITICAL(HW_GPU, "Failed to register buffer with unmapped gpu_address 0x{:016x}",
|
||||||
gpu_addr);
|
new_map->GetGpuAddress());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const IntervalType interval{new_interval.start, new_interval.end};
|
const std::size_t size = new_map->GetEnd() - new_map->GetStart();
|
||||||
mapped_addresses.insert(interval);
|
new_map->SetCpuAddress(*cpu_addr);
|
||||||
map_storage[new_interval] = MapInfo{gpu_addr, *cpu_addr};
|
new_map->MarkAsRegistered(true);
|
||||||
|
const IntervalType interval{new_map->GetStart(), new_map->GetEnd()};
|
||||||
|
mapped_addresses.insert({interval, new_map});
|
||||||
rasterizer.UpdatePagesCachedCount(*cpu_addr, size, 1);
|
rasterizer.UpdatePagesCachedCount(*cpu_addr, size, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Unregisters an object from the cache
|
/// Unregisters an object from the cache
|
||||||
void Unregister(const MapInterval& interval) {
|
void Unregister(MapInterval& map) {
|
||||||
const MapInfo info = map_storage[interval];
|
const std::size_t size = map->GetEnd() - map->GetStart();
|
||||||
const std::size_t size = interval.end - interval.start;
|
rasterizer.UpdatePagesCachedCount(map->GetCpuAddress(), size, -1);
|
||||||
rasterizer.UpdatePagesCachedCount(info.cpu_addr, size, -1);
|
map->MarkAsRegistered(false);
|
||||||
const IntervalType delete_interval{interval.start, interval.end};
|
const IntervalType delete_interval{map->GetStart(), map->GetEnd()};
|
||||||
mapped_addresses.erase(delete_interval);
|
mapped_addresses.erase(delete_interval);
|
||||||
map_storage.erase(interval);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
MapInterval CreateMap(const CacheAddr start, const CacheAddr end, const GPUVAddr gpu_addr) {
|
||||||
|
return std::make_shared<MapIntervalBase>(start, end, gpu_addr);
|
||||||
|
}
|
||||||
|
|
||||||
void MapAddress(const TBuffer& block, const GPUVAddr gpu_addr, const CacheAddr cache_addr,
|
void MapAddress(const TBuffer& block, const GPUVAddr gpu_addr, const CacheAddr cache_addr,
|
||||||
const std::size_t size) {
|
const std::size_t size) {
|
||||||
|
|
||||||
std::vector<MapInterval> overlaps = GetMapsInRange(cache_addr, size);
|
std::vector<MapInterval> overlaps = GetMapsInRange(cache_addr, size);
|
||||||
if (overlaps.empty()) {
|
if (overlaps.empty()) {
|
||||||
const CacheAddr cache_addr_end = cache_addr + size;
|
const CacheAddr cache_addr_end = cache_addr + size;
|
||||||
MapInterval new_interval{cache_addr, cache_addr_end};
|
MapInterval new_map = CreateMap(cache_addr, cache_addr_end, gpu_addr);
|
||||||
u8* host_ptr = FromCacheAddr(cache_addr);
|
u8* host_ptr = FromCacheAddr(cache_addr);
|
||||||
UploadBlockData(block, block->GetOffset(cache_addr), size, host_ptr);
|
UploadBlockData(block, block->GetOffset(cache_addr), size, host_ptr);
|
||||||
Register(new_interval, gpu_addr);
|
Register(new_map);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const CacheAddr cache_addr_end = cache_addr + size;
|
const CacheAddr cache_addr_end = cache_addr + size;
|
||||||
if (overlaps.size() == 1) {
|
if (overlaps.size() == 1) {
|
||||||
const MapInterval& current_map = overlaps[0];
|
const MapInterval& current_map = overlaps[0];
|
||||||
if (current_map.IsInside(cache_addr, cache_addr_end)) {
|
if (current_map->IsInside(cache_addr, cache_addr_end)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -172,25 +179,25 @@ private:
|
||||||
CacheAddr new_end = cache_addr_end;
|
CacheAddr new_end = cache_addr_end;
|
||||||
// Calculate new buffer parameters
|
// Calculate new buffer parameters
|
||||||
for (auto& overlap : overlaps) {
|
for (auto& overlap : overlaps) {
|
||||||
new_start = std::min(overlap.start, new_start);
|
new_start = std::min(overlap->GetStart(), new_start);
|
||||||
new_end = std::max(overlap.end, new_end);
|
new_end = std::max(overlap->GetEnd(), new_end);
|
||||||
}
|
}
|
||||||
GPUVAddr new_gpu_addr = gpu_addr + new_start - cache_addr;
|
GPUVAddr new_gpu_addr = gpu_addr + new_start - cache_addr;
|
||||||
for (auto& overlap : overlaps) {
|
for (auto& overlap : overlaps) {
|
||||||
Unregister(overlap);
|
Unregister(overlap);
|
||||||
}
|
}
|
||||||
UpdateBlock(block, new_start, new_end, overlaps);
|
UpdateBlock(block, new_start, new_end, overlaps);
|
||||||
MapInterval new_interval{new_start, new_end};
|
MapInterval new_map = CreateMap(new_start, new_end, new_gpu_addr);
|
||||||
Register(new_interval, new_gpu_addr);
|
Register(new_map);
|
||||||
}
|
}
|
||||||
|
|
||||||
void UpdateBlock(const TBuffer& block, CacheAddr start, CacheAddr end,
|
void UpdateBlock(const TBuffer& block, CacheAddr start, CacheAddr end,
|
||||||
std::vector<MapInterval>& overlaps) {
|
std::vector<MapInterval>& overlaps) {
|
||||||
const IntervalType base_interval{start, end};
|
const IntervalType base_interval{start, end};
|
||||||
IntervalCache interval_set{};
|
IntervalSet interval_set{};
|
||||||
interval_set.add(base_interval);
|
interval_set.add(base_interval);
|
||||||
for (auto& overlap : overlaps) {
|
for (auto& overlap : overlaps) {
|
||||||
const IntervalType subtract{overlap.start, overlap.end};
|
const IntervalType subtract{overlap->GetStart(), overlap->GetEnd()};
|
||||||
interval_set.subtract(subtract);
|
interval_set.subtract(subtract);
|
||||||
}
|
}
|
||||||
for (auto& interval : interval_set) {
|
for (auto& interval : interval_set) {
|
||||||
|
@ -210,7 +217,7 @@ private:
|
||||||
std::vector<MapInterval> objects{};
|
std::vector<MapInterval> objects{};
|
||||||
const IntervalType interval{addr, addr + size};
|
const IntervalType interval{addr, addr + size};
|
||||||
for (auto& pair : boost::make_iterator_range(mapped_addresses.equal_range(interval))) {
|
for (auto& pair : boost::make_iterator_range(mapped_addresses.equal_range(interval))) {
|
||||||
objects.emplace_back(pair.lower(), pair.upper());
|
objects.push_back(pair.second);
|
||||||
}
|
}
|
||||||
|
|
||||||
return objects;
|
return objects;
|
||||||
|
@ -322,10 +329,10 @@ private:
|
||||||
u64 buffer_offset = 0;
|
u64 buffer_offset = 0;
|
||||||
u64 buffer_offset_base = 0;
|
u64 buffer_offset_base = 0;
|
||||||
|
|
||||||
using IntervalCache = boost::icl::interval_set<CacheAddr>;
|
using IntervalSet = boost::icl::interval_set<CacheAddr>;
|
||||||
|
using IntervalCache = boost::icl::interval_map<CacheAddr, MapInterval>;
|
||||||
using IntervalType = typename IntervalCache::interval_type;
|
using IntervalType = typename IntervalCache::interval_type;
|
||||||
IntervalCache mapped_addresses{};
|
IntervalCache mapped_addresses{};
|
||||||
std::unordered_map<MapInterval, MapInfo> map_storage;
|
|
||||||
|
|
||||||
static constexpr u64 block_page_bits{24};
|
static constexpr u64 block_page_bits{24};
|
||||||
static constexpr u64 block_page_size{1 << block_page_bits};
|
static constexpr u64 block_page_size{1 << block_page_bits};
|
||||||
|
|
|
@ -4,45 +4,65 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <boost/functional/hash.hpp>
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "video_core/gpu.h"
|
#include "video_core/gpu.h"
|
||||||
|
|
||||||
namespace VideoCommon {
|
namespace VideoCommon {
|
||||||
|
|
||||||
struct MapInterval {
|
class MapIntervalBase {
|
||||||
MapInterval(const CacheAddr start, const CacheAddr end) : start{start}, end{end} {}
|
public:
|
||||||
CacheAddr start;
|
MapIntervalBase(const CacheAddr start, const CacheAddr end, const GPUVAddr gpu_addr)
|
||||||
CacheAddr end;
|
: start{start}, end{end}, gpu_addr{gpu_addr} {}
|
||||||
|
|
||||||
|
void SetCpuAddress(VAddr new_cpu_addr) {
|
||||||
|
cpu_addr = new_cpu_addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
VAddr GetCpuAddress() const {
|
||||||
|
return cpu_addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
GPUVAddr GetGpuAddress() const {
|
||||||
|
return gpu_addr;
|
||||||
|
}
|
||||||
|
|
||||||
bool IsInside(const CacheAddr other_start, const CacheAddr other_end) const {
|
bool IsInside(const CacheAddr other_start, const CacheAddr other_end) const {
|
||||||
return (start <= other_start && other_end <= end);
|
return (start <= other_start && other_end <= end);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator==(const MapInterval& rhs) const {
|
bool operator==(const MapIntervalBase& rhs) const {
|
||||||
return std::tie(start, end) == std::tie(rhs.start, rhs.end);
|
return std::tie(start, end) == std::tie(rhs.start, rhs.end);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator!=(const MapInterval& rhs) const {
|
bool operator!=(const MapIntervalBase& rhs) const {
|
||||||
return !operator==(rhs);
|
return !operator==(rhs);
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
struct MapInfo {
|
void MarkAsRegistered(const bool registered) {
|
||||||
|
is_registered = registered;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsRegistered() const {
|
||||||
|
return is_registered;
|
||||||
|
}
|
||||||
|
|
||||||
|
CacheAddr GetStart() const {
|
||||||
|
return start;
|
||||||
|
}
|
||||||
|
|
||||||
|
CacheAddr GetEnd() const {
|
||||||
|
return end;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
CacheAddr start;
|
||||||
|
CacheAddr end;
|
||||||
GPUVAddr gpu_addr;
|
GPUVAddr gpu_addr;
|
||||||
VAddr cpu_addr;
|
VAddr cpu_addr{};
|
||||||
|
bool is_write{};
|
||||||
|
bool is_modified{};
|
||||||
|
bool is_registered{};
|
||||||
|
u64 ticks{};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace VideoCommon
|
} // namespace VideoCommon
|
||||||
|
|
||||||
namespace std {
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct hash<VideoCommon::MapInterval> {
|
|
||||||
std::size_t operator()(const VideoCommon::MapInterval& k) const noexcept {
|
|
||||||
std::size_t a = std::hash<CacheAddr>()(k.start);
|
|
||||||
boost::hash_combine(a, std::hash<CacheAddr>()(k.end));
|
|
||||||
return a;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace std
|
|
||||||
|
|
Loading…
Reference in New Issue