Fix: Geometry Nodes: crash when capturing group input geometry in some cases

The crash requires a fairly specific setup that I noticed when working on
#150852. The setup is included here in form of a regression test.

The issue happened because the closure currently assumes that the Group Input
geometry can be part of the output because it is captured due to being a border
link. In another pass when computing the final propagate-relations (data that
can propagate from the group input to group output) it is detected that this
can't actually happen. So there was some inconsistency in the final required
reference lifetimes.

There might be a cleaner fix that doesn't require a post-processing step, but I
haven't found one yet. Might need some more investigation or refactoring later.

Pull Request: https://projects.blender.org/blender/blender/pulls/151390
This commit is contained in:
Jacques Lucke 2025-12-09 21:21:14 +01:00
parent 6eae38e08d
commit 17945ce420
4 changed files with 78 additions and 5 deletions

View file

@ -993,6 +993,32 @@ static aal::RelationsInNode get_tree_relations(
return tree_relations;
}
/**
* After creating detecting the final propagate-relations, we can detect some input geometry that
* looked like it was passed to the output actually is not. So we can update
* #required_data_by_socket to never use the corresponding #ReferenceSetInfo.
*/
static void disable_unused_group_output_propagation(
const Span<ReferenceSetInfo> reference_sets,
const Span<aal::PropagateRelation> &propagate_relations,
BitGroupVector<> &required_data_by_socket)
{
Vector<int> propagate_targets;
for (const auto relation : propagate_relations) {
propagate_targets.append(relation.to_geometry_output);
}
BitVector<> reference_sets_mask(reference_sets.size(), true);
for (const int i : reference_sets.index_range()) {
const ReferenceSetInfo &reference_set = reference_sets[i];
if (reference_set.type == ReferenceSetType::GroupOutputData) {
if (!propagate_targets.contains(reference_set.index)) {
reference_sets_mask[i].reset();
}
}
}
required_data_by_socket.foreach_and(reference_sets_mask);
}
static std::unique_ptr<ReferenceLifetimesInfo> make_reference_lifetimes_info(const bNodeTree &tree)
{
tree.ensure_topology_cache();
@ -1049,6 +1075,16 @@ static std::unique_ptr<ReferenceLifetimesInfo> make_reference_lifetimes_info(con
/* Make sure that all required data is also potentially available. */
required_data_by_socket.all_bits() &= potential_data_by_socket.all_bits();
reference_lifetimes_info->tree_relations = get_tree_relations(tree,
reference_sets,
potential_data_by_socket,
potential_reference_by_socket,
required_data_by_socket);
disable_unused_group_output_propagation(
reference_sets,
reference_lifetimes_info->tree_relations.propagate_relations,
required_data_by_socket);
/* Only useful when debugging the reference lifetimes analysis. */
#if 0
std::cout << "\n\n"
@ -1060,11 +1096,6 @@ static std::unique_ptr<ReferenceLifetimesInfo> make_reference_lifetimes_info(con
<< "\n\n";
#endif
reference_lifetimes_info->tree_relations = get_tree_relations(tree,
reference_sets,
potential_data_by_socket,
potential_reference_by_socket,
required_data_by_socket);
reference_lifetimes_info->required_data_by_socket = std::move(required_data_by_socket);
return reference_lifetimes_info;
}

View file

@ -8,6 +8,7 @@
#pragma once
#include "BLI_bit_span_ops.hh"
#include "BLI_bit_vector.hh"
namespace blender::bits {
@ -138,6 +139,20 @@ class BitGroupVector {
{
return data_;
}
/**
* Updates each group by computing the bitwise-and with the given bits.
*/
void foreach_and(const BoundedBitSpan bits)
{
/* This can still be optimized due to the additional knowledge we have how consecutive groups
* are layed out in memory. It is possible to updated multiple small groups at once. */
BLI_assert(bits.size() == group_size_);
for (const int64_t i : this->index_range()) {
MutableBoundedBitSpan group = (*this)[i];
group &= bits;
}
}
};
} // namespace blender::bits

View file

@ -48,4 +48,28 @@ TEST(bit_group_vector, CopyConstruct)
}
}
TEST(bit_group_vector, foreachAnd)
{
BitGroupVector<> groups(3, 4);
groups.all_bits().set_all();
BitVector<> mask{Span<bool>{false, false, true, false}};
groups.foreach_and(mask);
EXPECT_FALSE(groups[0][0].test());
EXPECT_FALSE(groups[0][1].test());
EXPECT_TRUE(groups[0][2].test());
EXPECT_FALSE(groups[0][3].test());
EXPECT_FALSE(groups[1][0].test());
EXPECT_FALSE(groups[1][1].test());
EXPECT_TRUE(groups[1][2].test());
EXPECT_FALSE(groups[1][3].test());
EXPECT_FALSE(groups[2][0].test());
EXPECT_FALSE(groups[2][1].test());
EXPECT_TRUE(groups[2][2].test());
EXPECT_FALSE(groups[2][3].test());
}
} // namespace blender::bits::tests

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:1002a6af45f3f0fc0e0cffe03edbb9d9fdf47ea48cb02307d50cabf3198f7980
size 97081