[D3D12] unordered_map instead of multimap in TextureCache

This commit is contained in:
Triang3l 2021-05-05 21:40:00 +03:00
parent ce19d948f0
commit 8f1f849a06
3 changed files with 55 additions and 69 deletions

View File

@ -12,6 +12,8 @@
#include <cstddef>
#include "xenia/base/xxhash.h"
namespace xe {
namespace hash {
@ -24,6 +26,13 @@ struct IdentityHasher {
size_t operator()(const Key& key) const { return static_cast<size_t>(key); }
};
template <typename Key>
struct XXHasher {
size_t operator()(const Key& key) const {
return static_cast<size_t>(XXH3_64bits(&key, sizeof(key)));
}
};
} // namespace hash
} // namespace xe

View File

@ -1215,12 +1215,11 @@ void TextureCache::BeginFrame() {
}
destroyed_any = true;
// Remove the texture from the map.
auto found_range = textures_.equal_range(texture->key.GetMapKey());
for (auto iter = found_range.first; iter != found_range.second; ++iter) {
if (iter->second == texture) {
textures_.erase(iter);
break;
}
auto found_texture_it = textures_.find(texture->key);
assert_true(found_texture_it != textures_.end());
if (found_texture_it != textures_.end()) {
assert_true(found_texture_it->second == texture);
textures_.erase(found_texture_it);
}
// Unlink the texture.
texture_used_first_ = texture->used_next;
@ -2203,17 +2202,12 @@ TextureCache::Texture* TextureCache::FindOrCreateTexture(TextureKey key) {
return nullptr;
}
uint64_t map_key = key.GetMapKey();
// Try to find an existing texture.
// TODO(Triang3l): Reuse a texture with mip_page unchanged, but base_page
// previously 0, now not 0, to save memory - common case in streaming.
auto found_range = textures_.equal_range(map_key);
for (auto iter = found_range.first; iter != found_range.second; ++iter) {
Texture* found_texture = iter->second;
if (found_texture->key.bucket_key == key.bucket_key) {
return found_texture;
}
auto found_texture_it = textures_.find(key);
if (found_texture_it != textures_.end()) {
return found_texture_it->second;
}
// Create the resource. If failed to create one, don't create a texture object
@ -2340,7 +2334,7 @@ TextureCache::Texture* TextureCache::FindOrCreateTexture(TextureKey key) {
}
texture->base_watch_handle = nullptr;
texture->mip_watch_handle = nullptr;
textures_.emplace(map_key, texture);
textures_.emplace(key, texture);
COUNT_profile_set("gpu/texture_cache/textures", textures_.size());
textures_total_size_ += texture->resource_size;
COUNT_profile_set("gpu/texture_cache/total_size_mb",

View File

@ -18,6 +18,7 @@
#include <vector>
#include "xenia/base/assert.h"
#include "xenia/base/hash.h"
#include "xenia/base/mutex.h"
#include "xenia/gpu/d3d12/d3d12_shader.h"
#include "xenia/gpu/d3d12/d3d12_shared_memory.h"
@ -61,71 +62,53 @@ class D3D12CommandProcessor;
// MipAddress but no BaseAddress to save memory because textures are streamed
// this way anyway.
class TextureCache {
union TextureKey {
struct {
// Physical 4 KB page with the base mip level, disregarding A/C/E address
// range prefix.
uint32_t base_page : 17; // 17 total
xenos::DataDimension dimension : 2; // 19
uint32_t width : 13; // 32
struct TextureKey {
// Physical 4 KB page with the base mip level, disregarding A/C/E address
// range prefix.
uint32_t base_page : 17; // 17 total
xenos::DataDimension dimension : 2; // 19
uint32_t width : 13; // 32
uint32_t height : 13; // 45
uint32_t tiled : 1; // 46
uint32_t packed_mips : 1; // 47
// Physical 4 KB page with mip 1 and smaller.
uint32_t mip_page : 17; // 64
uint32_t height : 13; // 45
uint32_t tiled : 1; // 46
uint32_t packed_mips : 1; // 47
// Physical 4 KB page with mip 1 and smaller.
uint32_t mip_page : 17; // 64
// Layers for stacked and 3D, 6 for cube, 1 for other dimensions.
uint32_t depth : 10; // 74
uint32_t mip_max_level : 4; // 78
xenos::TextureFormat format : 6; // 84
xenos::Endian endianness : 2; // 86
// Whether this texture is signed and has a different host representation
// than an unsigned view of the same guest texture.
uint32_t signed_separate : 1; // 87
// Whether this texture is a 2x-scaled resolve target.
uint32_t scaled_resolve : 1; // 88
// Layers for stacked and 3D, 6 for cube, 1 for other dimensions.
uint32_t depth : 10; // 74
uint32_t mip_max_level : 4; // 78
xenos::TextureFormat format : 6; // 84
xenos::Endian endianness : 2; // 86
// Whether this texture is signed and has a different host representation
// than an unsigned view of the same guest texture.
uint32_t signed_separate : 1; // 87
// Whether this texture is a 2x-scaled resolve target.
uint32_t scaled_resolve : 1; // 88
};
struct {
// The key used for unordered_multimap lookup. Single uint32_t instead of
// a uint64_t so XXH hash can be calculated in a stable way due to no
// padding.
uint32_t map_key[2];
// The key used to identify one texture within unordered_multimap buckets.
uint32_t bucket_key;
};
TextureKey() { MakeInvalid(); }
TextureKey(const TextureKey& key) {
SetMapKey(key.GetMapKey());
bucket_key = key.bucket_key;
std::memcpy(this, &key, sizeof(*this));
}
TextureKey& operator=(const TextureKey& key) {
SetMapKey(key.GetMapKey());
bucket_key = key.bucket_key;
std::memcpy(this, &key, sizeof(*this));
return *this;
}
bool operator==(const TextureKey& key) const {
return GetMapKey() == key.GetMapKey() && bucket_key == key.bucket_key;
}
bool operator!=(const TextureKey& key) const {
return GetMapKey() != key.GetMapKey() || bucket_key != key.bucket_key;
}
uint64_t GetMapKey() const {
return uint64_t(map_key[0]) | (uint64_t(map_key[1]) << 32);
}
void SetMapKey(uint64_t key) {
map_key[0] = uint32_t(key);
map_key[1] = uint32_t(key >> 32);
}
bool IsInvalid() const {
// Zero base and zero width is enough for a binding to be invalid.
return map_key[0] == 0;
// Zero size is enough for a binding to be invalid (not possible on the
// real GPU since dimensions minus 1 are stored).
return !width;
}
void MakeInvalid() {
// Reset all for a stable hash.
SetMapKey(0);
bucket_key = 0;
// Zero everything, including the padding, for a stable hash.
std::memset(this, 0, sizeof(*this));
}
using Hasher = xe::hash::XXHasher<TextureKey>;
bool operator==(const TextureKey& key) const {
return !std::memcmp(this, &key, sizeof(*this));
}
bool operator!=(const TextureKey& key) const { return !(*this == key); }
};
public:
@ -699,7 +682,7 @@ class TextureCache {
// Load pipelines for resolution-scaled resolve targets.
ID3D12PipelineState* load_pipelines_scaled_[size_t(LoadMode::kCount)] = {};
std::unordered_multimap<uint64_t, Texture*> textures_;
std::unordered_map<TextureKey, Texture*, TextureKey::Hasher> textures_;
uint64_t textures_total_size_ = 0;
Texture* texture_used_first_ = nullptr;
Texture* texture_used_last_ = nullptr;