[GPU] XXH3 hash instead of XXH64

This commit is contained in:
Triang3l
2020-12-08 22:31:09 +03:00
parent 9a4643d0f2
commit 36a0bcec8b
24 changed files with 83 additions and 2002 deletions

View File

@@ -17,7 +17,7 @@ namespace hash {
// For use in unordered_sets and unordered_maps (primarily multisets and
// multimaps, with manual collision resolution), where the hash is calculated
// externally (for instance, as XXH64), possibly requiring context data rather
// externally (for instance, as XXH3), possibly requiring context data rather
// than a pure function to calculate the hash
template <typename Key>
struct IdentityHasher {

21
src/xenia/base/xxhash.h Normal file
View File

@@ -0,0 +1,21 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_BASE_XXHASH_H_
#define XENIA_BASE_XXHASH_H_
#define XXH_INLINE_ALL
// Can't use XXH_X86DISPATCH because XXH is calculated on multiple threads,
// while the dispatch writes the result (multiple pointers without any
// synchronization) to XXH_g_dispatch at the first call.
#include "third_party/xxhash/xxhash.h"
#endif // XENIA_BASE_XXHASH_H_

View File

@@ -7,8 +7,6 @@
******************************************************************************
*/
#include "third_party/xxhash/xxhash.h"
#include <algorithm>
#include <cstring>
#include <utility>

View File

@@ -20,7 +20,6 @@
#include <utility>
#include "third_party/fmt/include/fmt/format.h"
#include "third_party/xxhash/xxhash.h"
#include "xenia/base/assert.h"
#include "xenia/base/byte_order.h"
#include "xenia/base/clock.h"
@@ -30,6 +29,7 @@
#include "xenia/base/math.h"
#include "xenia/base/profiling.h"
#include "xenia/base/string.h"
#include "xenia/base/xxhash.h"
#include "xenia/gpu/d3d12/d3d12_command_processor.h"
#include "xenia/gpu/gpu_flags.h"
#include "xenia/ui/d3d12/d3d12_util.h"
@@ -325,9 +325,9 @@ void PipelineCache::InitializeShaderStorage(
pipeline_stored_descriptions[i];
// Validate file integrity, stop and truncate the stream if data is
// corrupted.
if (XXH64(&pipeline_stored_description.description,
sizeof(pipeline_stored_description.description),
0) != pipeline_stored_description.description_hash) {
if (XXH3_64bits(&pipeline_stored_description.description,
sizeof(pipeline_stored_description.description)) !=
pipeline_stored_description.description_hash) {
pipeline_stored_descriptions.resize(i);
break;
}
@@ -471,7 +471,7 @@ void PipelineCache::InitializeShaderStorage(
break;
}
uint64_t ucode_data_hash =
XXH64(ucode_dwords.data(), ucode_byte_count, 0);
XXH3_64bits(ucode_dwords.data(), ucode_byte_count);
if (shader_header.ucode_data_hash != ucode_data_hash) {
// Validation failed.
break;
@@ -828,7 +828,7 @@ D3D12Shader* PipelineCache::LoadShader(xenos::ShaderType shader_type,
uint32_t dword_count) {
// Hash the input memory and lookup the shader.
return LoadShader(shader_type, host_address, dword_count,
XXH64(host_address, dword_count * sizeof(uint32_t), 0));
XXH3_64bits(host_address, dword_count * sizeof(uint32_t)));
}
D3D12Shader* PipelineCache::LoadShader(xenos::ShaderType shader_type,
@@ -1065,7 +1065,7 @@ bool PipelineCache::ConfigurePipeline(
}
// Find an existing pipeline in the cache.
uint64_t hash = XXH64(&description, sizeof(description), 0);
uint64_t hash = XXH3_64bits(&description, sizeof(description));
auto found_range = pipelines_.equal_range(hash);
for (auto it = found_range.first; it != found_range.second; ++it) {
Pipeline* found_pipeline = it->second;
@@ -1185,20 +1185,20 @@ bool PipelineCache::TranslateShader(DxbcShaderTranslator& translator,
uint64_t texture_binding_layout_hash = 0;
if (texture_binding_count) {
texture_binding_layout_hash =
XXH64(texture_bindings, texture_binding_layout_bytes, 0);
XXH3_64bits(texture_bindings, texture_binding_layout_bytes);
}
uint32_t bindless_sampler_count =
bindless_resources_used_ ? sampler_binding_count : 0;
uint64_t bindless_sampler_layout_hash = 0;
if (bindless_sampler_count) {
XXH64_state_t hash_state;
XXH64_reset(&hash_state, 0);
XXH3_state_t hash_state;
XXH3_64bits_reset(&hash_state);
for (uint32_t i = 0; i < bindless_sampler_count; ++i) {
XXH64_update(&hash_state,
&sampler_bindings[i].bindless_descriptor_index,
sizeof(sampler_bindings[i].bindless_descriptor_index));
XXH3_64bits_update(
&hash_state, &sampler_bindings[i].bindless_descriptor_index,
sizeof(sampler_bindings[i].bindless_descriptor_index));
}
bindless_sampler_layout_hash = XXH64_digest(&hash_state);
bindless_sampler_layout_hash = XXH3_64bits_digest(&hash_state);
}
// Obtain the unique IDs of binding layouts if there are any texture
// bindings or bindless samplers, for invalidation in the command processor.

View File

@@ -95,7 +95,7 @@ class PipelineCache {
reg::SQ_PROGRAM_CNTL sq_program_cntl;
static constexpr uint32_t kVersion = 0x20201129;
static constexpr uint32_t kVersion = 0x20201207;
});
// Update PipelineDescription::kVersion if any of the Pipeline* enums are
@@ -208,7 +208,7 @@ class PipelineCache {
PipelineRenderTarget render_targets[4];
static constexpr uint32_t kVersion = 0x20201202;
static constexpr uint32_t kVersion = 0x20201207;
});
XEPACKEDSTRUCT(PipelineStoredDescription, {
@@ -279,7 +279,7 @@ class PipelineCache {
// Texture binding layouts of different shaders, for obtaining layout UIDs.
std::vector<D3D12Shader::TextureBinding> texture_binding_layouts_;
// Map of texture binding layouts used by shaders, for obtaining UIDs. Keys
// are XXH64 hashes of layouts, values need manual collision resolution using
// are XXH3 hashes of layouts, values need manual collision resolution using
// layout_vector_offset:layout_length of texture_binding_layouts_.
std::unordered_multimap<uint64_t, LayoutUID,
xe::hash::IdentityHasher<uint64_t>>
@@ -287,7 +287,7 @@ class PipelineCache {
// Bindless sampler indices of different shaders, for obtaining layout UIDs.
// For bindful, sampler count is used as the UID instead.
std::vector<uint32_t> bindless_sampler_layouts_;
// Keys are XXH64 hashes of used bindless sampler indices.
// Keys are XXH3 hashes of used bindless sampler indices.
std::unordered_multimap<uint64_t, LayoutUID,
xe::hash::IdentityHasher<uint64_t>>
bindless_sampler_layout_map_;

View File

@@ -9,7 +9,6 @@
#include "xenia/gpu/d3d12/texture_cache.h"
#include "third_party/xxhash/xxhash.h"
#include <algorithm>
#include <cfloat>
@@ -21,6 +20,7 @@
#include "xenia/base/logging.h"
#include "xenia/base/math.h"
#include "xenia/base/profiling.h"
#include "xenia/base/xxhash.h"
#include "xenia/gpu/d3d12/d3d12_command_processor.h"
#include "xenia/gpu/gpu_flags.h"
#include "xenia/gpu/texture_info.h"

View File

@@ -12,7 +12,7 @@
#include <cstring>
#include <memory>
#include "third_party/xxhash/xxhash.h"
#include "xenia/base/xxhash.h"
namespace xe {
namespace gpu {
@@ -51,7 +51,7 @@ bool SamplerInfo::Prepare(const xenos::xe_gpu_texture_fetch_t& fetch,
}
uint64_t SamplerInfo::hash() const {
return XXH64(this, sizeof(SamplerInfo), 0);
return XXH3_64bits(this, sizeof(SamplerInfo));
}
} // namespace gpu

View File

@@ -18,8 +18,7 @@
#include "xenia/base/math.h"
#include "xenia/base/memory.h"
#include "xenia/base/profiling.h"
#include "third_party/xxhash/xxhash.h"
#include "xenia/base/xxhash.h"
namespace xe {
namespace gpu {

View File

@@ -16,8 +16,7 @@
#include "xenia/base/logging.h"
#include "xenia/base/math.h"
#include "xenia/base/memory.h"
#include "third_party/xxhash/xxhash.h"
#include "xenia/base/xxhash.h"
namespace xe {
namespace gpu {
@@ -319,7 +318,7 @@ bool TextureInfo::GetPackedTileOffset(int packed_tile, uint32_t* offset_x,
}
uint64_t TextureInfo::hash() const {
return XXH64(this, sizeof(TextureInfo), 0);
return XXH3_64bits(this, sizeof(TextureInfo));
}
void TextureInfo::SetupMemoryInfo(uint32_t base_address, uint32_t mip_address) {

View File

@@ -552,14 +552,14 @@ std::pair<VkBuffer, VkDeviceSize> BufferCache::UploadVertexBuffer(
}
void BufferCache::HashVertexBindings(
XXH64_state_t* hash_state,
XXH3_state_t* hash_state,
const std::vector<Shader::VertexBinding>& vertex_bindings) {
auto& regs = *register_file_;
for (const auto& vertex_binding : vertex_bindings) {
#if 0
XXH64_update(hash_state, &vertex_binding.binding_index, sizeof(vertex_binding.binding_index));
XXH64_update(hash_state, &vertex_binding.fetch_constant, sizeof(vertex_binding.fetch_constant));
XXH64_update(hash_state, &vertex_binding.stride_words, sizeof(vertex_binding.stride_words));
XXH3_64bits_update(hash_state, &vertex_binding.binding_index, sizeof(vertex_binding.binding_index));
XXH3_64bits_update(hash_state, &vertex_binding.fetch_constant, sizeof(vertex_binding.fetch_constant));
XXH3_64bits_update(hash_state, &vertex_binding.stride_words, sizeof(vertex_binding.stride_words));
#endif
int r = XE_GPU_REG_SHADER_CONSTANT_FETCH_00_0 +
(vertex_binding.fetch_constant / 3) * 6;
@@ -567,15 +567,15 @@ void BufferCache::HashVertexBindings(
switch (vertex_binding.fetch_constant % 3) {
case 0: {
auto& fetch = group->vertex_fetch_0;
XXH64_update(hash_state, &fetch, sizeof(fetch));
XXH3_64bits_update(hash_state, &fetch, sizeof(fetch));
} break;
case 1: {
auto& fetch = group->vertex_fetch_1;
XXH64_update(hash_state, &fetch, sizeof(fetch));
XXH3_64bits_update(hash_state, &fetch, sizeof(fetch));
} break;
case 2: {
auto& fetch = group->vertex_fetch_2;
XXH64_update(hash_state, &fetch, sizeof(fetch));
XXH3_64bits_update(hash_state, &fetch, sizeof(fetch));
} break;
}
}
@@ -585,12 +585,12 @@ VkDescriptorSet BufferCache::PrepareVertexSet(
VkCommandBuffer command_buffer, VkFence fence,
const std::vector<Shader::VertexBinding>& vertex_bindings) {
// (quickly) Generate a hash.
XXH64_state_t hash_state;
XXH64_reset(&hash_state, 0);
XXH3_state_t hash_state;
XXH3_64bits_reset(&hash_state);
// (quickly) Generate a hash.
HashVertexBindings(&hash_state, vertex_bindings);
uint64_t hash = XXH64_digest(&hash_state);
uint64_t hash = XXH3_64bits_digest(&hash_state);
for (auto it = vertex_sets_.find(hash); it != vertex_sets_.end(); ++it) {
// TODO(DrChat): We need to compare the bindings and ensure they're equal.
return it->second;

View File

@@ -10,6 +10,7 @@
#ifndef XENIA_GPU_VULKAN_BUFFER_CACHE_H_
#define XENIA_GPU_VULKAN_BUFFER_CACHE_H_
#include "xenia/base/xxhash.h"
#include "xenia/gpu/register_file.h"
#include "xenia/gpu/shader.h"
#include "xenia/gpu/xenos.h"
@@ -20,7 +21,6 @@
#include "xenia/ui/vulkan/vulkan_device.h"
#include "third_party/vulkan/vk_mem_alloc.h"
#include "third_party/xxhash/xxhash.h"
#include <map>
#include <unordered_map>
@@ -127,7 +127,7 @@ class BufferCache {
void FreeConstantDescriptorSet();
void HashVertexBindings(
XXH64_state_t* hash_state,
XXH3_state_t* hash_state,
const std::vector<Shader::VertexBinding>& vertex_bindings);
// Allocates a block of memory in the transient buffer.

View File

@@ -9,11 +9,11 @@
#include "xenia/gpu/vulkan/pipeline_cache.h"
#include "third_party/xxhash/xxhash.h"
#include "xenia/base/logging.h"
#include "xenia/base/math.h"
#include "xenia/base/memory.h"
#include "xenia/base/profiling.h"
#include "xenia/base/xxhash.h"
#include "xenia/gpu/gpu_flags.h"
#include "xenia/gpu/vulkan/vulkan_gpu_flags.h"
@@ -208,7 +208,8 @@ VulkanShader* PipelineCache::LoadShader(xenos::ShaderType shader_type,
const uint32_t* host_address,
uint32_t dword_count) {
// Hash the input memory and lookup the shader.
uint64_t data_hash = XXH64(host_address, dword_count * sizeof(uint32_t), 0);
uint64_t data_hash =
XXH3_64bits(host_address, dword_count * sizeof(uint32_t));
auto it = shader_map_.find(data_hash);
if (it != shader_map_.end()) {
// Shader has been previously loaded.
@@ -259,7 +260,7 @@ PipelineCache::UpdateStatus PipelineCache::ConfigurePipeline(
}
if (!pipeline) {
// Should have a hash key produced by the UpdateState pass.
uint64_t hash_key = XXH64_digest(&hash_state_);
uint64_t hash_key = XXH3_64bits_digest(&hash_state_);
pipeline = GetPipeline(render_state, hash_key);
current_pipeline_ = pipeline;
if (!pipeline) {
@@ -961,7 +962,7 @@ PipelineCache::UpdateStatus PipelineCache::UpdateState(
bool mismatch = false;
// Reset hash so we can build it up.
XXH64_reset(&hash_state_, 0);
XXH3_64bits_reset(&hash_state_);
#define CHECK_UPDATE_STATUS(status, mismatch, error_message) \
{ \
@@ -1028,7 +1029,7 @@ PipelineCache::UpdateStatus PipelineCache::UpdateRenderTargetState() {
regs.rb_color1_info.color_format = cur_regs->rb_color1_info.color_format;
regs.rb_color2_info.color_format = cur_regs->rb_color2_info.color_format;
regs.rb_color3_info.color_format = cur_regs->rb_color3_info.color_format;
XXH64_update(&hash_state_, &regs, sizeof(regs));
XXH3_64bits_update(&hash_state_, &regs, sizeof(regs));
if (!dirty) {
return UpdateStatus::kCompatible;
}
@@ -1061,7 +1062,7 @@ PipelineCache::UpdateStatus PipelineCache::UpdateShaderStages(
regs.vertex_shader = vertex_shader;
regs.pixel_shader = pixel_shader;
regs.primitive_type = primitive_type;
XXH64_update(&hash_state_, &regs, sizeof(regs));
XXH3_64bits_update(&hash_state_, &regs, sizeof(regs));
if (!dirty) {
return UpdateStatus::kCompatible;
}
@@ -1148,7 +1149,7 @@ PipelineCache::UpdateStatus PipelineCache::UpdateVertexInputState(
bool dirty = false;
dirty |= vertex_shader != regs.vertex_shader;
regs.vertex_shader = vertex_shader;
XXH64_update(&hash_state_, &regs, sizeof(regs));
XXH3_64bits_update(&hash_state_, &regs, sizeof(regs));
if (!dirty) {
return UpdateStatus::kCompatible;
}
@@ -1177,7 +1178,7 @@ PipelineCache::UpdateStatus PipelineCache::UpdateInputAssemblyState(
dirty |= SetShadowRegister(&regs.multi_prim_ib_reset_index,
XE_GPU_REG_VGT_MULTI_PRIM_IB_RESET_INDX);
regs.primitive_type = primitive_type;
XXH64_update(&hash_state_, &regs, sizeof(regs));
XXH3_64bits_update(&hash_state_, &regs, sizeof(regs));
if (!dirty) {
return UpdateStatus::kCompatible;
}
@@ -1303,7 +1304,7 @@ PipelineCache::UpdateStatus PipelineCache::UpdateRasterizationState(
dirty = true;
}
XXH64_update(&hash_state_, &regs, sizeof(regs));
XXH3_64bits_update(&hash_state_, &regs, sizeof(regs));
if (!dirty) {
return UpdateStatus::kCompatible;
}
@@ -1385,7 +1386,7 @@ PipelineCache::UpdateStatus PipelineCache::UpdateMultisampleState() {
dirty |= SetShadowRegister(&regs.pa_su_sc_mode_cntl,
XE_GPU_REG_PA_SU_SC_MODE_CNTL);
dirty |= SetShadowRegister(&regs.rb_surface_info, XE_GPU_REG_RB_SURFACE_INFO);
XXH64_update(&hash_state_, &regs, sizeof(regs));
XXH3_64bits_update(&hash_state_, &regs, sizeof(regs));
if (!dirty) {
return UpdateStatus::kCompatible;
}
@@ -1437,7 +1438,7 @@ PipelineCache::UpdateStatus PipelineCache::UpdateDepthStencilState() {
dirty |= SetShadowRegister(&regs.rb_depthcontrol, XE_GPU_REG_RB_DEPTHCONTROL);
dirty |=
SetShadowRegister(&regs.rb_stencilrefmask, XE_GPU_REG_RB_STENCILREFMASK);
XXH64_update(&hash_state_, &regs, sizeof(regs));
XXH3_64bits_update(&hash_state_, &regs, sizeof(regs));
if (!dirty) {
return UpdateStatus::kCompatible;
}
@@ -1526,7 +1527,7 @@ PipelineCache::UpdateStatus PipelineCache::UpdateColorBlendState() {
dirty |=
SetShadowRegister(&regs.rb_blendcontrol[3], XE_GPU_REG_RB_BLENDCONTROL3);
dirty |= SetShadowRegister(&regs.rb_modecontrol, XE_GPU_REG_RB_MODECONTROL);
XXH64_update(&hash_state_, &regs, sizeof(regs));
XXH3_64bits_update(&hash_state_, &regs, sizeof(regs));
if (!dirty) {
return UpdateStatus::kCompatible;
}

View File

@@ -12,8 +12,7 @@
#include <unordered_map>
#include "third_party/xxhash/xxhash.h"
#include "xenia/base/xxhash.h"
#include "xenia/gpu/register_file.h"
#include "xenia/gpu/spirv_shader_translator.h"
#include "xenia/gpu/vulkan/render_cache.h"
@@ -121,7 +120,7 @@ class PipelineCache {
// Hash state used to incrementally produce pipeline hashes during update.
// By the time the full update pass has run the hash will represent the
// current state in a way that can uniquely identify the produced VkPipeline.
XXH64_state_t hash_state_;
XXH3_state_t hash_state_;
// All previously generated pipelines mapped by hash.
std::unordered_map<uint64_t, VkPipeline> cached_pipelines_;

View File

@@ -1377,7 +1377,7 @@ void TextureCache::WritebackTexture(Texture* texture) {
}
void TextureCache::HashTextureBindings(
XXH64_state_t* hash_state, uint32_t& fetch_mask,
XXH3_state_t* hash_state, uint32_t& fetch_mask,
const std::vector<Shader::TextureBinding>& bindings) {
for (auto& binding : bindings) {
uint32_t fetch_bit = 1 << binding.fetch_constant;
@@ -1393,7 +1393,7 @@ void TextureCache::HashTextureBindings(
reinterpret_cast<const xenos::xe_gpu_fetch_group_t*>(&regs.values[r]);
auto& fetch = group->texture_fetch;
XXH64_update(hash_state, &fetch, sizeof(fetch));
XXH3_64bits_update(hash_state, &fetch, sizeof(fetch));
}
}
@@ -1401,14 +1401,14 @@ VkDescriptorSet TextureCache::PrepareTextureSet(
VkCommandBuffer command_buffer, VkFence completion_fence,
const std::vector<Shader::TextureBinding>& vertex_bindings,
const std::vector<Shader::TextureBinding>& pixel_bindings) {
XXH64_state_t hash_state;
XXH64_reset(&hash_state, 0);
XXH3_state_t hash_state;
XXH3_64bits_reset(&hash_state);
// (quickly) Generate a hash.
uint32_t fetch_mask = 0;
HashTextureBindings(&hash_state, fetch_mask, vertex_bindings);
HashTextureBindings(&hash_state, fetch_mask, pixel_bindings);
uint64_t hash = XXH64_digest(&hash_state);
uint64_t hash = XXH3_64bits_digest(&hash_state);
for (auto it = texture_sets_.find(hash); it != texture_sets_.end(); ++it) {
// TODO(DrChat): We need to compare the bindings and ensure they're equal.
return it->second;

View File

@@ -186,7 +186,7 @@ class TextureCache {
bool UploadTexture(VkCommandBuffer command_buffer, VkFence completion_fence,
Texture* dest, const TextureInfo& src);
void HashTextureBindings(XXH64_state_t* hash_state, uint32_t& fetch_mask,
void HashTextureBindings(XXH3_state_t* hash_state, uint32_t& fetch_mask,
const std::vector<Shader::TextureBinding>& bindings);
bool SetupTextureBindings(
VkCommandBuffer command_buffer, VkFence completion_fence,