Implement a simple constant buffer cache

In some games such as SMO thousands of constant buffers are bound per frame which was causing an unreasonable number of lookups in both vmm and the buffer manager. Work around this by introducing a simple hashmap based cache, eviction is currently unsupported but not really necessary yet due to the small size of the buffers in the cache.
This commit is contained in:
Billy Laws 2022-04-03 16:16:16 +01:00 committed by PixelyIon
parent cb2614f80e
commit e5e20f39c9

View File

@ -3,6 +3,7 @@
#pragma once #pragma once
#include <boost/functional/hash.hpp>
#include <boost/container/static_vector.hpp> #include <boost/container/static_vector.hpp>
#include <range/v3/algorithm.hpp> #include <range/v3/algorithm.hpp>
#include <gpu/texture/format.h> #include <gpu/texture/format.h>
@ -614,15 +615,58 @@ namespace skyline::gpu::interconnect {
constantBufferSelector.view = {}; constantBufferSelector.view = {};
} }
/**
* @brief Simple hashmap cache for constant buffers to avoid the constant overhead of TranslateRange and GetView that would otherwise be present
* @note TODO: This doesn't currently evict views but that can be fixed later when we encounter a performance issue
*/
class ConstantBufferCache {
private:
struct Key {
u32 size;
u64 iova;
auto operator<=>(const Key &) const = default;
};
struct KeyHash {
size_t operator()(const Key &entry) const noexcept {
size_t seed = 0;
boost::hash_combine(seed, entry.size);
boost::hash_combine(seed, entry.iova);
return seed;
}
};
std::unordered_map<Key, BufferView, KeyHash> cache;
public:
std::optional<BufferView> Lookup(u32 size, u64 iova) {
if (auto it{cache.find({size, iova})}; it != cache.end())
return it->second;
return std::nullopt;
}
void Insert(u32 size, u64 iova, BufferView &view) {
cache[Key{size, iova}] = view;
}
} constantBufferCache;
std::optional<ConstantBuffer> GetConstantBufferSelector() { std::optional<ConstantBuffer> GetConstantBufferSelector() {
if (constantBufferSelector.size == 0) if (constantBufferSelector.size == 0)
return std::nullopt; return std::nullopt;
else if (constantBufferSelector.view) else if (constantBufferSelector.view)
return constantBufferSelector; return constantBufferSelector;
auto view{constantBufferCache.Lookup(constantBufferSelector.size, constantBufferSelector.iova)};
if (!view) {
auto mappings{channelCtx.asCtx->gmmu.TranslateRange(constantBufferSelector.iova, constantBufferSelector.size)}; auto mappings{channelCtx.asCtx->gmmu.TranslateRange(constantBufferSelector.iova, constantBufferSelector.size)};
view = gpu.buffer.FindOrCreate(mappings.front(), executor.cycle);
constantBufferCache.Insert(constantBufferSelector.size, constantBufferSelector.iova, *view);
}
constantBufferSelector.view = gpu.buffer.FindOrCreate(mappings.front(), executor.cycle); constantBufferSelector.view = *view;
return constantBufferSelector; return constantBufferSelector;
} }