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_