Geometry Nodes: do cache invalidation after writing attributes

This is a better and more general fix for T92511 and T92508 than
the ones that I committed before.

Previously, we tagged caches dirty when first accessing attributes.
This led to incorrect caches when under some circumstances. Now
cache invalidation is part of `OutputAttribute.save()`.

A nice side benefit of this change is that it may make things more
efficient in some cases, because we don't invalidate caches when
they don't have to be invalidated.

Differential Revision: https://developer.blender.org/D13009
This commit is contained in:
Jacques Lucke 2021-10-29 09:28:31 +02:00
parent b546202a9a
commit cf771807b7
Notes: blender-bot 2023-02-14 04:10:15 +01:00
Referenced by issue #92509, Do cache invalidation OutputAttribute.save() method
8 changed files with 98 additions and 43 deletions

View File

@ -183,6 +183,8 @@ struct WriteAttributeLookup {
GVMutableArrayPtr varray;
/* Domain the attributes lives on in the geometry. */
AttributeDomain domain;
/* Call this after changing the attribute to invalidate caches that depend on this attribute. */
std::function<void()> tag_modified_fn;
/* Convenience function to check if the attribute has been found. */
operator bool() const

View File

@ -570,6 +570,8 @@ struct CurveEval {
blender::Array<int> evaluated_point_offsets() const;
blender::Array<float> accumulated_spline_lengths() const;
void mark_cache_invalid();
void assert_valid_point_attributes() const;
};

View File

@ -360,7 +360,7 @@ GVArrayPtr BuiltinCustomDataLayerProvider::try_get_for_read(
return as_read_attribute_(data, domain_size);
}
GVMutableArrayPtr BuiltinCustomDataLayerProvider::try_get_for_write(
WriteAttributeLookup BuiltinCustomDataLayerProvider::try_get_for_write(
GeometryComponent &component) const
{
if (writable_ != Writable) {
@ -397,10 +397,14 @@ GVMutableArrayPtr BuiltinCustomDataLayerProvider::try_get_for_write(
data = new_data;
}
std::function<void()> tag_modified_fn;
if (update_on_write_ != nullptr) {
update_on_write_(component);
tag_modified_fn = [component = &component, update = update_on_write_]() {
update(*component);
};
}
return as_write_attribute_(data, domain_size);
return {as_write_attribute_(data, domain_size), domain_, std::move(tag_modified_fn)};
}
bool BuiltinCustomDataLayerProvider::try_delete(GeometryComponent &component) const
@ -925,7 +929,7 @@ blender::bke::WriteAttributeLookup GeometryComponent::attribute_try_get_for_writ
const BuiltinAttributeProvider *builtin_provider =
providers->builtin_attribute_providers().lookup_default_as(attribute_id.name(), nullptr);
if (builtin_provider != nullptr) {
return {builtin_provider->try_get_for_write(*this), builtin_provider->domain()};
return builtin_provider->try_get_for_write(*this);
}
}
for (const DynamicAttributesProvider *dynamic_provider :
@ -1249,6 +1253,20 @@ static void save_output_attribute(OutputAttribute &output_attribute)
varray.get(i, buffer);
write_attribute.varray->set_by_relocate(i, buffer);
}
if (write_attribute.tag_modified_fn) {
write_attribute.tag_modified_fn();
}
}
static std::function<void(OutputAttribute &)> get_simple_output_attribute_save_method(
const blender::bke::WriteAttributeLookup &attribute)
{
if (!attribute.tag_modified_fn) {
return {};
}
return [tag_modified_fn = attribute.tag_modified_fn](OutputAttribute &UNUSED(attribute)) {
tag_modified_fn();
};
}
static OutputAttribute create_output_attribute(GeometryComponent &component,
@ -1293,14 +1311,21 @@ static OutputAttribute create_output_attribute(GeometryComponent &component,
/* Builtin attribute is on different domain. */
return {};
}
GVMutableArrayPtr varray = std::move(attribute.varray);
if (varray->type() == *cpp_type) {
/* Builtin attribute matches exactly. */
return OutputAttribute(std::move(varray), domain, {}, ignore_old_values);
return OutputAttribute(std::move(varray),
domain,
get_simple_output_attribute_save_method(attribute),
ignore_old_values);
}
/* Builtin attribute is on the same domain but has a different data type. */
varray = conversions.try_convert(std::move(varray), *cpp_type);
return OutputAttribute(std::move(varray), domain, {}, ignore_old_values);
return OutputAttribute(std::move(varray),
domain,
get_simple_output_attribute_save_method(attribute),
ignore_old_values);
}
const int domain_size = component.attribute_domain_size(domain);
@ -1324,7 +1349,11 @@ static OutputAttribute create_output_attribute(GeometryComponent &component,
}
if (attribute.domain == domain && attribute.varray->type() == *cpp_type) {
/* Existing generic attribute matches exactly. */
return OutputAttribute(std::move(attribute.varray), domain, {}, ignore_old_values);
return OutputAttribute(std::move(attribute.varray),
domain,
get_simple_output_attribute_save_method(attribute),
ignore_old_values);
}
/* Allocate a new array that lives next to the existing attribute. It will overwrite the existing

View File

@ -87,7 +87,7 @@ class BuiltinAttributeProvider {
}
virtual GVArrayPtr try_get_for_read(const GeometryComponent &component) const = 0;
virtual GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const = 0;
virtual WriteAttributeLookup try_get_for_write(GeometryComponent &component) const = 0;
virtual bool try_delete(GeometryComponent &component) const = 0;
virtual bool try_create(GeometryComponent &UNUSED(component),
const AttributeInit &UNUSED(initializer)) const = 0;
@ -267,7 +267,7 @@ class BuiltinCustomDataLayerProvider final : public BuiltinAttributeProvider {
}
GVArrayPtr try_get_for_read(const GeometryComponent &component) const final;
GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const final;
WriteAttributeLookup try_get_for_write(GeometryComponent &component) const final;
bool try_delete(GeometryComponent &component) const final;
bool try_create(GeometryComponent &component, const AttributeInit &initializer) const final;
bool exists(const GeometryComponent &component) const final;

View File

@ -160,6 +160,13 @@ blender::Array<float> CurveEval::accumulated_spline_lengths() const
return spline_lengths;
}
void CurveEval::mark_cache_invalid()
{
for (SplinePtr &spline : splines_) {
spline->mark_cache_invalid();
}
}
static BezierSpline::HandleType handle_type_from_dna_bezt(const eBezTriple_Handle dna_handle_type)
{
switch (dna_handle_type) {

View File

@ -433,7 +433,7 @@ class BuiltinSplineAttributeProvider final : public BuiltinAttributeProvider {
return as_read_attribute_(*curve);
}
GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const final
WriteAttributeLookup try_get_for_write(GeometryComponent &component) const final
{
if (writable_ != Writable) {
return {};
@ -442,7 +442,7 @@ class BuiltinSplineAttributeProvider final : public BuiltinAttributeProvider {
if (curve == nullptr) {
return {};
}
return as_write_attribute_(*curve);
return {as_write_attribute_(*curve), domain_};
}
bool try_delete(GeometryComponent &UNUSED(component)) const final
@ -1122,7 +1122,7 @@ template<typename T> class BuiltinPointAttributeProvider : public BuiltinAttribu
return point_data_gvarray(spans, offsets);
}
GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const override
WriteAttributeLookup try_get_for_write(GeometryComponent &component) const override
{
CurveEval *curve = get_curve_from_component_for_write(component);
if (curve == nullptr) {
@ -1133,25 +1133,30 @@ template<typename T> class BuiltinPointAttributeProvider : public BuiltinAttribu
return {};
}
std::function<void()> tag_modified_fn;
if (update_on_write_ != nullptr) {
tag_modified_fn = [curve, update = update_on_write_]() {
for (SplinePtr &spline : curve->splines()) {
update(*spline);
}
};
}
MutableSpan<SplinePtr> splines = curve->splines();
if (splines.size() == 1) {
if (update_on_write_) {
update_on_write_(*splines[0]);
}
return std::make_unique<fn::GVMutableArray_For_GMutableSpan>(
get_mutable_span_(*splines.first()));
return {std::make_unique<fn::GVMutableArray_For_GMutableSpan>(
get_mutable_span_(*splines.first())),
domain_,
std::move(tag_modified_fn)};
}
Array<int> offsets = curve->control_point_offsets();
Array<MutableSpan<T>> spans(splines.size());
for (const int i : splines.index_range()) {
spans[i] = get_mutable_span_(*splines[i]);
if (update_on_write_) {
update_on_write_(*splines[i]);
}
}
return point_data_gvarray(spans, offsets);
return {point_data_gvarray(spans, offsets), domain_, tag_modified_fn};
}
bool try_delete(GeometryComponent &component) const final
@ -1223,7 +1228,7 @@ class PositionAttributeProvider final : public BuiltinPointAttributeProvider<flo
{
}
GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const final
WriteAttributeLookup try_get_for_write(GeometryComponent &component) const final
{
CurveEval *curve = get_curve_from_component_for_write(component);
if (curve == nullptr) {
@ -1236,16 +1241,19 @@ class PositionAttributeProvider final : public BuiltinPointAttributeProvider<flo
return BuiltinPointAttributeProvider<float3>::try_get_for_write(component);
}
/* Changing the positions requires recalculation of cached evaluated data in many cases.
* This could set more specific flags in the future to avoid unnecessary recomputation. */
for (SplinePtr &spline : curve->splines()) {
spline->mark_cache_invalid();
}
auto tag_modified_fn = [curve]() {
/* Changing the positions requires recalculation of cached evaluated data in many cases.
* This could set more specific flags in the future to avoid unnecessary recomputation. */
curve->mark_cache_invalid();
};
Array<int> offsets = curve->control_point_offsets();
return std::make_unique<
fn::GVMutableArray_For_EmbeddedVMutableArray<float3, VMutableArray_For_SplinePosition>>(
offsets.last(), curve->splines(), std::move(offsets));
return {std::make_unique<
fn::GVMutableArray_For_EmbeddedVMutableArray<float3,
VMutableArray_For_SplinePosition>>(
offsets.last(), curve->splines(), std::move(offsets)),
domain_,
tag_modified_fn};
}
};
@ -1281,7 +1289,7 @@ class BezierHandleAttributeProvider : public BuiltinAttributeProvider {
offsets.last(), curve->splines(), std::move(offsets), is_right_);
}
GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const override
WriteAttributeLookup try_get_for_write(GeometryComponent &component) const override
{
CurveEval *curve = get_curve_from_component_for_write(component);
if (curve == nullptr) {
@ -1292,10 +1300,15 @@ class BezierHandleAttributeProvider : public BuiltinAttributeProvider {
return {};
}
auto tag_modified_fn = [curve]() { curve->mark_cache_invalid(); };
Array<int> offsets = curve->control_point_offsets();
return std::make_unique<
fn::GVMutableArray_For_EmbeddedVMutableArray<float3, VMutableArray_For_BezierHandles>>(
offsets.last(), curve->splines(), std::move(offsets), is_right_);
return {
std::make_unique<
fn::GVMutableArray_For_EmbeddedVMutableArray<float3, VMutableArray_For_BezierHandles>>(
offsets.last(), curve->splines(), std::move(offsets), is_right_),
domain_,
tag_modified_fn};
}
bool try_delete(GeometryComponent &UNUSED(component)) const final

View File

@ -398,15 +398,16 @@ class InstancePositionAttributeProvider final : public BuiltinAttributeProvider
transforms);
}
GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const final
WriteAttributeLookup try_get_for_write(GeometryComponent &component) const final
{
InstancesComponent &instances_component = static_cast<InstancesComponent &>(component);
MutableSpan<float4x4> transforms = instances_component.instance_transforms();
return std::make_unique<fn::GVMutableArray_For_DerivedSpan<float4x4,
float3,
get_transform_position,
set_transform_position>>(
transforms);
return {
std::make_unique<fn::GVMutableArray_For_DerivedSpan<float4x4,
float3,
get_transform_position,
set_transform_position>>(transforms),
domain_};
}
bool try_delete(GeometryComponent &UNUSED(component)) const final
@ -443,13 +444,14 @@ class InstanceIDAttributeProvider final : public BuiltinAttributeProvider {
return std::make_unique<fn::GVArray_For_Span<int>>(instances.instance_ids());
}
GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const final
WriteAttributeLookup try_get_for_write(GeometryComponent &component) const final
{
InstancesComponent &instances = static_cast<InstancesComponent &>(component);
if (instances.instance_ids().is_empty()) {
return {};
}
return std::make_unique<fn::GVMutableArray_For_MutableSpan<int>>(instances.instance_ids());
return {std::make_unique<fn::GVMutableArray_For_MutableSpan<int>>(instances.instance_ids()),
domain_};
}
bool try_delete(GeometryComponent &component) const final

View File

@ -1210,7 +1210,7 @@ class NormalAttributeProvider final : public BuiltinAttributeProvider {
return std::make_unique<fn::GVArray_For_ArrayContainer<Array<float3>>>(std::move(normals));
}
GVMutableArrayPtr try_get_for_write(GeometryComponent &UNUSED(component)) const final
WriteAttributeLookup try_get_for_write(GeometryComponent &UNUSED(component)) const final
{
return {};
}