VkDevice: Fix sample cache hash collisions that caused spurious test failures. Keying off a single size_t is likely to cause collisions, more so when the hash function is simple shift-xors of 3 32-bit integers. Use a proper cache key comparision and a better hash function while we're at it. Bug: b/137649247 Change-Id: I1e0deefb0976102714d43dcb9dcedf6aa809bf7f Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/34909 Tested-by: Nicolas Capens <nicolascapens@google.com> Reviewed-by: Nicolas Capens <nicolascapens@google.com> Reviewed-by: Chris Forbes <chrisforbes@google.com> Kokoro-Presubmit: kokoro <noreply+kokoro@google.com> (cherry picked from commit f046402b1480245c3c17e91131f11f066f53b289) Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/34928
diff --git a/src/Device/LRUCache.hpp b/src/Device/LRUCache.hpp index 083861e..e4ec2b3 100644 --- a/src/Device/LRUCache.hpp +++ b/src/Device/LRUCache.hpp
@@ -48,7 +48,7 @@ Data *data; }; - template<class Key, class Data> + template<class Key, class Data, class Hasher = std::hash<Key>> class LRUConstCache : public LRUCache<Key, Data> { using LRUBase = LRUCache<Key, Data>; @@ -68,7 +68,7 @@ private: void clearConstCache(); bool constCacheNeedsUpdate = false; - std::unordered_map<Key, Data> constCache; + std::unordered_map<Key, Data, Hasher> constCache; }; // Helper class for clearing the memory of objects at construction. @@ -189,14 +189,14 @@ return data; } - template<class Key, class Data> - void LRUConstCache<Key, Data>::clearConstCache() + template<class Key, class Data, class Hasher> + void LRUConstCache<Key, Data, Hasher>::clearConstCache() { constCache.clear(); } - template<class Key, class Data> - void LRUConstCache<Key, Data>::updateConstCache() + template<class Key, class Data, class Hasher> + void LRUConstCache<Key, Data, Hasher>::updateConstCache() { if(constCacheNeedsUpdate) { @@ -214,8 +214,8 @@ } } - template<class Key, class Data> - const Data& LRUConstCache<Key, Data>::queryConstCache(const Key &key) const + template<class Key, class Data, class Hasher> + const Data& LRUConstCache<Key, Data, Hasher>::queryConstCache(const Key &key) const { auto it = constCache.find(key); static Data null = {};
diff --git a/src/Vulkan/VkDevice.cpp b/src/Vulkan/VkDevice.cpp index bceb001..692db75 100644 --- a/src/Vulkan/VkDevice.cpp +++ b/src/Vulkan/VkDevice.cpp
@@ -38,18 +38,18 @@ std::shared_ptr<rr::Routine> Device::SamplingRoutineCache::query(const vk::Device::SamplingRoutineCache::Key& key) const { - return cache.query(hash(key)); + return cache.query(key); } void Device::SamplingRoutineCache::add(const vk::Device::SamplingRoutineCache::Key& key, const std::shared_ptr<rr::Routine>& routine) { ASSERT(routine); - cache.add(hash(key), routine); + cache.add(key, routine); } rr::Routine* Device::SamplingRoutineCache::queryConst(const vk::Device::SamplingRoutineCache::Key& key) const { - return cache.queryConstCache(hash(key)).get(); + return cache.queryConstCache(key).get(); } void Device::SamplingRoutineCache::updateConstCache() @@ -57,11 +57,6 @@ cache.updateConstCache(); } -std::size_t Device::SamplingRoutineCache::hash(const vk::Device::SamplingRoutineCache::Key &key) -{ - return (key.instruction << 16) ^ (key.sampler << 8) ^ key.imageView; -} - Device::Device(const VkDeviceCreateInfo* pCreateInfo, void* mem, PhysicalDevice *physicalDevice, const VkPhysicalDeviceFeatures *enabledFeatures) : physicalDevice(physicalDevice), queues(reinterpret_cast<Queue*>(mem)),
diff --git a/src/Vulkan/VkDevice.hpp b/src/Vulkan/VkDevice.hpp index 742d662..ae7c21f 100644 --- a/src/Vulkan/VkDevice.hpp +++ b/src/Vulkan/VkDevice.hpp
@@ -65,6 +65,14 @@ uint32_t instruction; uint32_t sampler; uint32_t imageView; + + inline bool operator == (const Key& rhs) const; + inline bool operator < (const Key& rhs) const; + + struct Hash + { + inline std::size_t operator()(const Key& key) const noexcept; + }; }; std::shared_ptr<rr::Routine> query(const Key& key) const; @@ -73,10 +81,8 @@ rr::Routine* queryConst(const Key& key) const; void updateConstCache(); - static std::size_t hash(const Key &key); - private: - sw::LRUConstCache<std::size_t, std::shared_ptr<rr::Routine>> cache; + sw::LRUConstCache<Key, std::shared_ptr<rr::Routine>, Key::Hash> cache; }; SamplingRoutineCache* getSamplingRoutineCache() const; @@ -104,6 +110,24 @@ return DispatchableDevice::Cast(object); } +inline bool vk::Device::SamplingRoutineCache::Key::operator == (const Key& rhs) const +{ + return instruction == rhs.instruction && sampler == rhs.sampler && imageView == rhs.imageView; +} + +inline bool vk::Device::SamplingRoutineCache::Key::operator < (const Key& rhs) const +{ + return instruction < rhs.instruction || sampler < rhs.sampler || imageView < rhs.imageView; +} + +inline std::size_t vk::Device::SamplingRoutineCache::Key::Hash::operator() (const Key& key) const noexcept +{ + auto hash = key.instruction; + hash = (hash * 31) ^ key.sampler; + hash = (hash * 31) ^ key.imageView; + return hash; +} + } // namespace vk #endif // VK_DEVICE_HPP_