[D3D12] unordered_map instead of multimap in TextureCache
This commit is contained in:
parent
ce19d948f0
commit
8f1f849a06
|
@ -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
|
||||
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue