Move HashCombine to HashUtils and make it work better on 64bits

HashCombine will be used in more than just BindGroupLayout caching so we
extract it to a separate header. Add a better mixing constant for 64bit
systems.
This commit is contained in:
Corentin Wallez 2018-05-07 17:33:48 -04:00 committed by Corentin Wallez
parent 60ffbfc071
commit ae27c7a4bc
3 changed files with 58 additions and 22 deletions

View File

@ -15,39 +15,19 @@
#include "backend/BindGroupLayout.h" #include "backend/BindGroupLayout.h"
#include "backend/Device.h" #include "backend/Device.h"
#include "common/HashUtils.h"
#include <functional> #include <functional>
namespace backend { namespace backend {
namespace { namespace {
// Workaround for Chrome's stdlib having a broken std::hash for enums and bitsets
template <typename T>
typename std::enable_if<std::is_enum<T>::value, size_t>::type Hash(T value) {
using Integral = typename nxt::UnderlyingType<T>::type;
return std::hash<Integral>()(static_cast<Integral>(value));
}
template <size_t N>
size_t Hash(const std::bitset<N>& value) {
static_assert(N <= sizeof(unsigned long long) * 8, "");
return std::hash<unsigned long long>()(value.to_ullong());
}
// TODO(cwallez@chromium.org): see if we can use boost's hash combined or some equivalent
// this currently assumes that size_t is 64 bits
void CombineHashes(size_t* h1, size_t h2) {
*h1 ^= (h2 << 7) + (h2 >> (sizeof(size_t) * 8 - 7)) + 0x304975;
}
size_t HashBindingInfo(const BindGroupLayoutBase::LayoutBindingInfo& info) { size_t HashBindingInfo(const BindGroupLayoutBase::LayoutBindingInfo& info) {
size_t hash = Hash(info.mask); size_t hash = Hash(info.mask);
for (size_t binding = 0; binding < kMaxBindingsPerGroup; ++binding) { for (size_t binding = 0; binding < kMaxBindingsPerGroup; ++binding) {
if (info.mask[binding]) { if (info.mask[binding]) {
CombineHashes(&hash, Hash(info.visibilities[binding])); HashCombine(&hash, info.visibilities[binding], info.types[binding]);
CombineHashes(&hash, Hash(info.types[binding]));
} }
} }

View File

@ -21,6 +21,7 @@ list(APPEND COMMON_SOURCES
${COMMON_DIR}/Compiler.h ${COMMON_DIR}/Compiler.h
${COMMON_DIR}/DynamicLib.cpp ${COMMON_DIR}/DynamicLib.cpp
${COMMON_DIR}/DynamicLib.h ${COMMON_DIR}/DynamicLib.h
${COMMON_DIR}/HashUtils.h
${COMMON_DIR}/Math.cpp ${COMMON_DIR}/Math.cpp
${COMMON_DIR}/Math.h ${COMMON_DIR}/Math.h
${COMMON_DIR}/Platform.h ${COMMON_DIR}/Platform.h

55
src/common/HashUtils.h Normal file
View File

@ -0,0 +1,55 @@
// Copyright 2018 The NXT Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef COMMON_HASHUTILS_H_
#define COMMON_HASHUTILS_H_
#include "common/Platform.h"
#include <functional>
// Wrapper around std::hash to make it a templated function instead of a functor. It is marginally
// nicer, and avoids adding to the std namespace to add hashing of other types.
template <typename T>
size_t Hash(const T& value) {
return std::hash<T>()(value);
}
// When hashing sparse structures we want to iteratively build a hash value with only parts of the
// data. HashCombine "hashes" together an existing hash and hashable values.
//
// Example usage to compute the hash of a mask and values corresponding to the mask:
//
// size_t hash = Hash(mask):
// for (uint32_t i : IterateBitSet(mask)) { HashCombine(&hash, hashables[i]); }
// return hash;
template <typename T>
void HashCombine(size_t* hash, const T& value) {
#if defined(NXT_PLATFORM_64_BIT)
const size_t offset = 0x9e3779b97f4a7c16;
#elif defined(NXT_PLATFORM_32_BIT)
const size_t offset = 0x9e3779b9;
#else
# error "Unsupported platform"
#endif
*hash ^= Hash(value) + offset + (*hash << 6) + (*hash >> 2);
}
template <typename T, typename... Args>
void HashCombine(size_t* hash, const T& value, const Args&... args) {
HashCombine(hash, value);
HashCombine(hash, args...);
}
#endif // COMMON_HASHUTILS_H_