BufferCache: Rework mapping caching.

This commit is contained in:
Fernando Sahmkow 2019-07-19 21:07:28 -04:00 committed by FernandoS27
parent 86d8563314
commit 5f4b746a1e
2 changed files with 78 additions and 51 deletions

View File

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

View File

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