From 8f1f849a068f04601fedd4e39d4dbf90a2551393 Mon Sep 17 00:00:00 2001 From: Triang3l Date: Wed, 5 May 2021 21:40:00 +0300 Subject: [PATCH] [D3D12] unordered_map instead of multimap in TextureCache --- src/xenia/base/hash.h | 9 +++ src/xenia/gpu/d3d12/texture_cache.cc | 24 +++----- src/xenia/gpu/d3d12/texture_cache.h | 91 +++++++++++----------------- 3 files changed, 55 insertions(+), 69 deletions(-) diff --git a/src/xenia/base/hash.h b/src/xenia/base/hash.h index 88c98b64c..d469bad99 100644 --- a/src/xenia/base/hash.h +++ b/src/xenia/base/hash.h @@ -12,6 +12,8 @@ #include +#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(key); } }; +template +struct XXHasher { + size_t operator()(const Key& key) const { + return static_cast(XXH3_64bits(&key, sizeof(key))); + } +}; + } // namespace hash } // namespace xe diff --git a/src/xenia/gpu/d3d12/texture_cache.cc b/src/xenia/gpu/d3d12/texture_cache.cc index 25e88ff7d..08c0d8603 100644 --- a/src/xenia/gpu/d3d12/texture_cache.cc +++ b/src/xenia/gpu/d3d12/texture_cache.cc @@ -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", diff --git a/src/xenia/gpu/d3d12/texture_cache.h b/src/xenia/gpu/d3d12/texture_cache.h index 76bd141c2..2b45e9605 100644 --- a/src/xenia/gpu/d3d12/texture_cache.h +++ b/src/xenia/gpu/d3d12/texture_cache.h @@ -18,6 +18,7 @@ #include #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; + 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 textures_; + std::unordered_map textures_; uint64_t textures_total_size_ = 0; Texture* texture_used_first_ = nullptr; Texture* texture_used_last_ = nullptr;