[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 <cstddef>
#include "xenia/base/xxhash.h"
namespace xe { namespace xe {
namespace hash { namespace hash {
@ -24,6 +26,13 @@ struct IdentityHasher {
size_t operator()(const Key& key) const { return static_cast<size_t>(key); } 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 hash
} // namespace xe } // namespace xe

View File

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

View File

@ -18,6 +18,7 @@
#include <vector> #include <vector>
#include "xenia/base/assert.h" #include "xenia/base/assert.h"
#include "xenia/base/hash.h"
#include "xenia/base/mutex.h" #include "xenia/base/mutex.h"
#include "xenia/gpu/d3d12/d3d12_shader.h" #include "xenia/gpu/d3d12/d3d12_shader.h"
#include "xenia/gpu/d3d12/d3d12_shared_memory.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 // MipAddress but no BaseAddress to save memory because textures are streamed
// this way anyway. // this way anyway.
class TextureCache { class TextureCache {
union TextureKey { struct TextureKey {
struct { // Physical 4 KB page with the base mip level, disregarding A/C/E address
// Physical 4 KB page with the base mip level, disregarding A/C/E address // range prefix.
// range prefix. uint32_t base_page : 17; // 17 total
uint32_t base_page : 17; // 17 total xenos::DataDimension dimension : 2; // 19
xenos::DataDimension dimension : 2; // 19 uint32_t width : 13; // 32
uint32_t width : 13; // 32
uint32_t height : 13; // 45 uint32_t height : 13; // 45
uint32_t tiled : 1; // 46 uint32_t tiled : 1; // 46
uint32_t packed_mips : 1; // 47 uint32_t packed_mips : 1; // 47
// Physical 4 KB page with mip 1 and smaller. // Physical 4 KB page with mip 1 and smaller.
uint32_t mip_page : 17; // 64 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() { MakeInvalid(); }
TextureKey(const TextureKey& key) { TextureKey(const TextureKey& key) {
SetMapKey(key.GetMapKey()); std::memcpy(this, &key, sizeof(*this));
bucket_key = key.bucket_key;
} }
TextureKey& operator=(const TextureKey& key) { TextureKey& operator=(const TextureKey& key) {
SetMapKey(key.GetMapKey()); std::memcpy(this, &key, sizeof(*this));
bucket_key = key.bucket_key;
return *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 { bool IsInvalid() const {
// Zero base and zero width is enough for a binding to be invalid. // Zero size is enough for a binding to be invalid (not possible on the
return map_key[0] == 0; // real GPU since dimensions minus 1 are stored).
return !width;
} }
void MakeInvalid() { void MakeInvalid() {
// Reset all for a stable hash. // Zero everything, including the padding, for a stable hash.
SetMapKey(0); std::memset(this, 0, sizeof(*this));
bucket_key = 0;
} }
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: public:
@ -699,7 +682,7 @@ class TextureCache {
// Load pipelines for resolution-scaled resolve targets. // Load pipelines for resolution-scaled resolve targets.
ID3D12PipelineState* load_pipelines_scaled_[size_t(LoadMode::kCount)] = {}; 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; uint64_t textures_total_size_ = 0;
Texture* texture_used_first_ = nullptr; Texture* texture_used_first_ = nullptr;
Texture* texture_used_last_ = nullptr; Texture* texture_used_last_ = nullptr;