Fix #130942: Vulkan: Hash collision in graphics pipeline pool

Blender stores all pipelines in a pool. Using a hash it checks if a
the pipeline was already created and the previous could be reused. Due
to performance issues when working with graphics pipelines some equal
operations only used a hash check. For scissors and viewports this isn't
enough and could lead to issues.

This PR fixes this to still perform an exact check if the hash are
equal. Note that the performance drops a bit. And should be countered
with other performance improvements in the future.

Pull Request: https://projects.blender.org/blender/blender/pulls/132005
This commit is contained in:
Jeroen Bakker
2024-12-17 12:43:20 +01:00
parent a669decca8
commit 7e788ec4e4
2 changed files with 53 additions and 35 deletions

View File

@@ -437,6 +437,7 @@ if(WITH_VULKAN_BACKEND)
${VULKAN_LIBRARIES}
${SHADERC_LIBRARIES}
extern_vulkan_memory_allocator
PRIVATE bf::extern::xxhash
)
add_definitions(-DWITH_VULKAN_BACKEND)

View File

@@ -10,6 +10,8 @@
#include <mutex>
#include "xxhash.h"
#include "BLI_map.hh"
#include "BLI_utility_mixins.hh"
@@ -61,19 +63,12 @@ struct VKGraphicsInfo {
uint64_t hash() const
{
uint64_t hash = 0;
hash = hash * 33 ^ uint64_t(vk_topology);
for (const VkVertexInputAttributeDescription &attribute : attributes) {
hash = hash * 33 ^ uint64_t(attribute.location);
hash = hash * 33 ^ uint64_t(attribute.binding);
hash = hash * 33 ^ uint64_t(attribute.format);
hash = hash * 33 ^ uint64_t(attribute.offset);
}
for (const VkVertexInputBindingDescription &binding : bindings) {
hash = hash * 33 ^ uint64_t(binding.binding);
hash = hash * 33 ^ uint64_t(binding.inputRate);
hash = hash * 33 ^ uint64_t(binding.stride);
}
uint64_t hash = uint64_t(vk_topology);
hash = hash * 33 ^
XXH3_64bits(attributes.data(),
attributes.size() * sizeof(VkVertexInputAttributeDescription));
hash = hash * 33 ^ XXH3_64bits(bindings.data(),
bindings.size() * sizeof(VkVertexInputBindingDescription));
return hash;
}
};
@@ -101,8 +96,26 @@ struct VKGraphicsInfo {
bool operator==(const FragmentShader &other) const
{
/* TODO: Do not use hash. */
return vk_fragment_module == other.vk_fragment_module && hash() == other.hash();
if (vk_fragment_module != other.vk_fragment_module ||
viewports.size() != other.viewports.size() || scissors.size() != other.scissors.size() ||
hash() != other.hash())
{
return false;
}
if (memcmp(viewports.data(),
other.viewports.data(),
viewports.size() * sizeof(VkViewport)) != 0)
{
return false;
}
if (memcmp(scissors.data(), other.scissors.data(), scissors.size() * sizeof(VkRect2D)) != 0)
{
return false;
}
return true;
}
uint64_t hash() const
@@ -121,22 +134,10 @@ struct VKGraphicsInfo {
private:
uint64_t calc_hash() const
{
uint64_t hash = 0;
hash = hash * 33 ^ uint64_t(vk_fragment_module);
for (const VkViewport &vk_viewport : viewports) {
hash = hash * 33 ^ uint64_t(vk_viewport.x);
hash = hash * 33 ^ uint64_t(vk_viewport.y);
hash = hash * 33 ^ uint64_t(vk_viewport.width);
hash = hash * 33 ^ uint64_t(vk_viewport.height);
hash = hash * 33 ^ uint64_t(vk_viewport.minDepth);
hash = hash * 33 ^ uint64_t(vk_viewport.maxDepth);
}
for (const VkRect2D &scissor : scissors) {
hash = hash * 33 ^ uint64_t(scissor.offset.x);
hash = hash * 33 ^ uint64_t(scissor.offset.y);
hash = hash * 33 ^ uint64_t(scissor.extent.width);
hash = hash * 33 ^ uint64_t(scissor.extent.height);
}
uint64_t hash = uint64_t(vk_fragment_module);
hash = hash * 33 ^ XXH3_64bits(viewports.data(), viewports.size() * sizeof(VkViewport));
hash = hash * 33 ^ XXH3_64bits(scissors.data(), scissors.size() * sizeof(VkRect2D));
return hash;
}
};
@@ -152,7 +153,25 @@ struct VKGraphicsInfo {
bool operator==(const FragmentOut &other) const
{
#if 1
return hash() == other.hash();
#else
if (depth_attachment_format != other.depth_attachment_format ||
stencil_attachment_format != other.stencil_attachment_format ||
vk_render_pass != other.vk_render_pass ||
color_attachment_formats.size() != other.color_attachment_formats.size())
{
return false;
}
if (memcmp(color_attachment_formats.data(),
other.color_attachment_formats.data(),
color_attachment_formats.size() * sizeof(VkFormat)) == 0)
{
return false;
}
return true;
#endif
}
uint64_t hash() const
@@ -160,10 +179,8 @@ struct VKGraphicsInfo {
uint64_t hash = uint64_t(vk_render_pass);
hash = hash * 33 ^ uint64_t(depth_attachment_format);
hash = hash * 33 ^ uint64_t(stencil_attachment_format);
for (VkFormat color_attachment_format : color_attachment_formats) {
hash = hash * 33 ^ uint64_t(color_attachment_format);
}
hash = hash * 33 ^ XXH3_64bits(color_attachment_formats.data(),
color_attachment_formats.size() * sizeof(VkFormat));
return hash;
}
};