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