GPv3: Port frame_clean_duplicate operator #116655

Open
Abdelrahman Said wants to merge 33 commits from amsaid1989/blender:gpv3-delete-duplicate-frames into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
2 changed files with 180 additions and 0 deletions

View File

@ -6056,6 +6056,7 @@ class VIEW3D_MT_edit_greasepencil_cleanup(Menu):
layout = self.layout
layout.operator("grease_pencil.clean_loose")
layout.operator("grease_pencil.frame_clean_duplicate")
if ob.mode != 'PAINT_GREASE_PENCIL':
layout.operator("grease_pencil.stroke_merge_by_distance", text="Merge by Distance")

View File

@ -6,6 +6,7 @@
* \ingroup edgreasepencil
*/
#include "BKE_curves.hh"
#include "BLI_map.hh"
#include "BLI_math_vector_types.hh"
#include "BLI_utildefines.h"
@ -413,6 +414,162 @@ static int insert_blank_frame_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
static bool attributes_varrays_not_equal(const bke::GAttributeReader &attrs_a,
const bke::GAttributeReader &attrs_b)
{
return (attrs_a.varray.size() != attrs_b.varray.size() ||
attrs_a.varray.type() != attrs_b.varray.type());
}
static bool attributes_varrays_span_data_equal(const bke::GAttributeReader &attrs_a,
const bke::GAttributeReader &attrs_b)
{
amsaid1989 marked this conversation as resolved Outdated

You have check size few lines above, right?

You have check size few lines above, right?

Removed this extra size check

Removed this extra size check
if (attrs_a.varray.is_span() && attrs_b.varray.is_span()) {
const GSpan attrs_span_a = attrs_a.varray.get_internal_span();
const GSpan attrs_span_b = attrs_b.varray.get_internal_span();
if (attrs_span_a.data() == attrs_span_b.data()) {
return true;
amsaid1989 marked this conversation as resolved Outdated

Should be false.

Should be false.

Done

Done
}
}
PratikPB2123 marked this conversation as resolved Outdated

Not sure why this check is needed.

Not sure why this check is needed.

In case lists of attributes are different, i think this is reasonable to treat such curves as different..

In case lists of attributes are different, i think this is reasonable to treat such curves as different..

I will leave this check as is since @mod_moder agrees it's needed

I will leave this check as is since @mod_moder agrees it's needed

I see, make sense.

I see, make sense.
return false;
}
template<typename T>
static bool attributes_elements_are_equal(const VArray<T> &attributes_a,
const VArray<T> &attributes_b)
{
const std::optional<T> value_a = attributes_a.get_if_single();
const std::optional<T> value_b = attributes_b.get_if_single();
if (value_a.has_value() && value_b.has_value()) {
return value_a.value() == value_b.value();
}
const VArraySpan attrs_span_a = attributes_a;
const VArraySpan attrs_span_b = attributes_b;
return std::equal(
attrs_span_a.begin(), attrs_span_a.end(), attrs_span_b.begin(), attrs_span_b.end());
}
static bool curves_geometry_is_equal(const bke::CurvesGeometry &curves_a,
const bke::CurvesGeometry &curves_b)
{
using namespace blender::bke;
if (curves_a.points_num() == 0 && curves_b.points_num() == 0) {
return true;
}
if (curves_a.curves_num() != curves_b.curves_num() ||
amsaid1989 marked this conversation as resolved Outdated

here offset() memory is garbage if curves has empty drawing. This would lead to crash. Just add one if condition before this to return true when both the curves has 0 points:

  if ((curves_a.points_num() == 0) && (curves_b.points_num() == 0)) {
    return true;
  }
here offset() memory is garbage if curves has empty drawing. This would lead to crash. Just add one if condition before this to return true when both the curves has 0 points: ``` if ((curves_a.points_num() == 0) && (curves_b.points_num() == 0)) { return true; } ```

Fixed it. Thank you very much for catching it

Fixed it. Thank you very much for catching it
curves_a.points_num() != curves_b.points_num() || curves_a.offsets() != curves_b.offsets())
{
return false;
}
amsaid1989 marked this conversation as resolved Outdated

not sure if this is needed. You could just call std::equal() in current lamda too, no?
I might be misunderstanding something

not sure if this is needed. You could just call `std::equal()` in current lamda too, no? I might be misunderstanding something

This was implemented based on guidance I got in blender.chat from @mod_moder when I started working on this. This is what was recommended to me to compare all the attributes.

This was implemented based on guidance I got in [blender.chat](blender.chat) from @mod_moder when I started working on this. This is what was recommended to me to compare all the attributes.
const AttributeAccessor attributes_a = curves_a.attributes();
const AttributeAccessor attributes_b = curves_b.attributes();
const Set<AttributeIDRef> ids_a = attributes_a.all_ids();
const Set<AttributeIDRef> ids_b = attributes_b.all_ids();
if (ids_a != ids_b) {
return false;
}
for (const AttributeIDRef &id : ids_a) {
GAttributeReader attrs_a = attributes_a.lookup(id);
GAttributeReader attrs_b = attributes_b.lookup(id);
if (attributes_varrays_not_equal(attrs_a, attrs_b)) {
amsaid1989 marked this conversation as resolved Outdated
if (attributes_varrays_are_equal(attrs_a, attrs_b)) {
  return true;
}
```Cpp if (attributes_varrays_are_equal(attrs_a, attrs_b)) { return true; } ```

Done

Done
return false;
}
if (attributes_varrays_span_data_equal(attrs_a, attrs_b)) {
return true;
}
amsaid1989 marked this conversation as resolved Outdated

attrs_equal -> attributes_are_equal

`attrs_equal` -> `attributes_are_equal`

Renamed the variable

Renamed the variable
bool attributes_are_equal = true;
amsaid1989 marked this conversation as resolved Outdated

Right now this is incorrect.
if (attributes_varrays_are_equal(attrs_a, attrs_b)) { is used for early skip if this is possible (arrays is actually is the same data segment in memory).
But right now you also have to check if this is obviously impossible (different types or sizes).
If you'll do this cast for attributes with different sizes/types, this will be UB and assertion in debug.
You need to explicitly check if attributes_varrays_not_equal before of this.
And additional cleanup comment: upcast to static type for attributes should be on the caller, not in the template method.

Right now this is incorrect. `if (attributes_varrays_are_equal(attrs_a, attrs_b)) {` is used for early skip if this is possible (arrays is actually is the same data segment in memory). But right now you also have to check if this is obviously impossible (different types or sizes). If you'll do this cast for attributes with different sizes/types, this will be UB and assertion in debug. You need to explicitly check if `attributes_varrays_not_equal` before of this. And additional cleanup comment: upcast to static type for attributes should be on the caller, not in the template method.

@mod_moder I have addressed your comments about the varrays check.

Renamed attributes_varrays_are_equal to attributes_varrays_span_data_equal and updated it to this:

static bool attributes_varrays_span_data_equal(const bke::GAttributeReader &attrs_a,
                                         const bke::GAttributeReader &attrs_b)
{
  if (attrs_a.varray.is_span() && attrs_b.varray.is_span()) {
    const GSpan attrs_span_a = attrs_a.varray.get_internal_span();
    const GSpan attrs_span_b = attrs_b.varray.get_internal_span();

    if (attrs_span_a.data() == attrs_span_b.data()) {
      return true;
    }
  }

  return false;
}

Then, I added attributes_varrays_not_equal which is implemented as follows:

static bool attributes_varrays_not_equal(const bke::GAttributeReader &attrs_a,
                                         const bke::GAttributeReader &attrs_b)
{
  return (attrs_a.varray.size() != attrs_b.varray.size() ||
          attrs_a.varray.type() != attrs_b.varray.type());
}

These 2 functions are then used like this:

if (attributes_varrays_span_data_equal(attrs_a, attrs_b)) {
	return true;
}

if (attributes_varrays_not_equal(attrs_a, attrs_b)) {
	return false;
}

Does this make more sense?

@mod_moder I have addressed your comments about the varrays check. Renamed `attributes_varrays_are_equal` to `attributes_varrays_span_data_equal` and updated it to this: ```cpp static bool attributes_varrays_span_data_equal(const bke::GAttributeReader &attrs_a, const bke::GAttributeReader &attrs_b) { if (attrs_a.varray.is_span() && attrs_b.varray.is_span()) { const GSpan attrs_span_a = attrs_a.varray.get_internal_span(); const GSpan attrs_span_b = attrs_b.varray.get_internal_span(); if (attrs_span_a.data() == attrs_span_b.data()) { return true; } } return false; } ``` Then, I added `attributes_varrays_not_equal` which is implemented as follows: ```cpp static bool attributes_varrays_not_equal(const bke::GAttributeReader &attrs_a, const bke::GAttributeReader &attrs_b) { return (attrs_a.varray.size() != attrs_b.varray.size() || attrs_a.varray.type() != attrs_b.varray.type()); } ``` These 2 functions are then used like this: ```cpp if (attributes_varrays_span_data_equal(attrs_a, attrs_b)) { return true; } if (attributes_varrays_not_equal(attrs_a, attrs_b)) { return false; } ``` Does this make more sense?

And additional cleanup comment: upcast to static type for attributes should be on the caller, not in the template method.

Regarding this, I would like to confirm that I understand you correctly.

You want to change the lambda in convert_to_static_type to this:

attribute_math::convert_to_static_type(attrs_a.varray.type(), [&](auto dummy) {
  using T = decltype(dummy);

  const VArray attributes_a = attrs_a.varray.typed<T>();
  const VArray attributes_b = attrs_b.varray.typed<T>();

  attributes_are_equal = attributes_elements_are_equal(attributes_a, attributes_b);
});

And change the attributes_elements_are_equal to the following:

template<typename T>
static bool attributes_elements_are_equal(const VArray<T> &attributes_a,
                                          const VArray<T> &attributes_b)
{
  const std::optional<T> value_a = attributes_a.get_if_single();
  const std::optional<T> value_b = attributes_b.get_if_single();
  if (value_a.has_value() && value_b.has_value()) {
    return value_a.value() == value_b.value();
  }

  const VArraySpan attrs_span_a = attributes_a;
  const VArraySpan attrs_span_b = attributes_b;

  return std::equal(
      attrs_span_a.begin(), attrs_span_a.end(), attrs_span_b.begin(), attrs_span_b.end());
}
> And additional cleanup comment: upcast to static type for attributes should be on the caller, not in the template method. Regarding this, I would like to confirm that I understand you correctly. You want to change the lambda in `convert_to_static_type` to this: ```cpp attribute_math::convert_to_static_type(attrs_a.varray.type(), [&](auto dummy) { using T = decltype(dummy); const VArray attributes_a = attrs_a.varray.typed<T>(); const VArray attributes_b = attrs_b.varray.typed<T>(); attributes_are_equal = attributes_elements_are_equal(attributes_a, attributes_b); }); ``` And change the `attributes_elements_are_equal` to the following: ```cpp template<typename T> static bool attributes_elements_are_equal(const VArray<T> &attributes_a, const VArray<T> &attributes_b) { const std::optional<T> value_a = attributes_a.get_if_single(); const std::optional<T> value_b = attributes_b.get_if_single(); if (value_a.has_value() && value_b.has_value()) { return value_a.value() == value_b.value(); } const VArraySpan attrs_span_a = attributes_a; const VArraySpan attrs_span_b = attributes_b; return std::equal( attrs_span_a.begin(), attrs_span_a.end(), attrs_span_b.begin(), attrs_span_b.end()); } ```

Order of attributes_varrays_span_data_equal and attributes_varrays_not_equal can be changed (first - early skip of wrong types and sizes, and next early confirm that data is the same). This looks good now.

Regarding this, I would like to confirm that I understand you correctly.

Yes, this is correct.

Order of `attributes_varrays_span_data_equal` and `attributes_varrays_not_equal` can be changed (first - early skip of wrong types and sizes, and next early confirm that data is the same). This looks good now. > Regarding this, I would like to confirm that I understand you correctly. Yes, this is correct.

Great.. Thank you very much. Updated the PR.

Great.. Thank you very much. Updated the PR.
amsaid1989 marked this conversation as resolved Outdated

get_editable_drawing_at accepts layer reference. So pass *layer

`get_editable_drawing_at` accepts layer reference. So pass `*layer`
attribute_math::convert_to_static_type(attrs_a.varray.type(), [&](auto dummy) {
amsaid1989 marked this conversation as resolved Outdated

same as above

same as above

Updated all uses of get_editable_drawing_at to pass a Layer & instead of Layer *

Updated all uses of `get_editable_drawing_at` to pass a `Layer &` instead of `Layer *`
using T = decltype(dummy);
const VArray attributes_a = attrs_a.varray.typed<T>();
const VArray attributes_b = attrs_b.varray.typed<T>();
attributes_are_equal = attributes_elements_are_equal(attributes_a, attributes_b);
});
if (!attributes_are_equal) {
return false;
}
}
return true;
}
amsaid1989 marked this conversation as resolved Outdated

wouldn't get_editable_drawing_at work here?

wouldn't `get_editable_drawing_at` work here?

You're right.. get_editable_drawing_at works. Updated it

You're right.. `get_editable_drawing_at` works. Updated it
static int frame_clean_duplicate_exec(bContext *C, wmOperator *op)
{
amsaid1989 marked this conversation as resolved Outdated

same as above.

Also names can be improved. eg: curves and curves_next

same as above. Also names can be improved. eg: `curves` and `curves_next`

Switched to get_editable_drawing_at and renamed the variables

Switched to `get_editable_drawing_at` and renamed the variables
using namespace blender::bke::greasepencil;
Object *object = CTX_data_active_object(C);
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
const bool selected = RNA_boolean_get(op->ptr, "selected");
bool changed = false;
amsaid1989 marked this conversation as resolved Outdated

Vector<FramesMapKey> frames_to_delete;

`Vector<FramesMapKey> frames_to_delete;`

Done

Done
for (Layer *layer : grease_pencil.layers_for_write()) {
if (!layer->is_editable()) {
continue;
amsaid1989 marked this conversation as resolved
Review

No need to get the span by reference, use const Span<FramesMapKey> keys.
Also: FramesMapKey was renamed to FramesMapKeyT

No need to get the span by reference, use `const Span<FramesMapKey> keys`. Also: `FramesMapKey` was renamed to `FramesMapKeyT`
Review

Done

Done
}
mod_moder marked this conversation as resolved Outdated

for (size_t i = 0; i < keys.size(); ++i) { and if (i + 1 >= keys.size()) {
->
const int i : keys.index_range().drop_front(1)

.... ?

`for (size_t i = 0; i < keys.size(); ++i) {` and `if (i + 1 >= keys.size()) {` -> `const int i : keys.index_range().drop_front(1)` .... ?

I needed const int i : keys.index_range().drop_back(1) instead of const int i : keys.index_range().drop_front(1)

I needed `const int i : keys.index_range().drop_back(1)` instead of `const int i : keys.index_range().drop_front(1)`
Vector<int> start_frame_numbers;
for (const FramesMapKeyT key : layer->sorted_keys()) {
PratikPB2123 marked this conversation as resolved Outdated

grease_pencil.remove_frames()

`grease_pencil.remove_frames()`

When I try to use this grease_pencil.remove_frames(*layer, frames_to_delete); I get a crash. I ran Blender in a debugger to see if I can find what is causing the crash, but I haven't figured out the reason. I am sure I am missing something with this being my first time working with Blender's code. This is the output I get in the terminal after running the test file I am using. I can't see what might be causing the problem so I thought I would share it here in case someone can figure it out.

Traceback (most recent call last):
  File "/home/abdelrahman/.config/blender/4.1/scripts/addons/bone_layer_manager/constraint_operators.py", line 472, in poll
    idx = cbone.constraint_active_index
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'NoneType' object has no attribute 'constraint_active_index'
Traceback (most recent call last):
  File "/home/abdelrahman/.config/blender/4.1/scripts/addons/bone_layer_manager/constraint_operators.py", line 370, in poll
    return len(bone.constraints) > 0 and len(context.selected_pose_bones) > 1
               ^^^^^^^^^^^^^^^^
AttributeError: 'NoneType' object has no attribute 'constraints'
Traceback (most recent call last):
  File "/home/abdelrahman/.config/blender/4.1/scripts/addons/bone_layer_manager/constraint_operators.py", line 404, in poll
    return len(bone.constraints) > 0 and len(context.selected_pose_bones) > 1
               ^^^^^^^^^^^^^^^^
AttributeError: 'NoneType' object has no attribute 'constraints'
Traceback (most recent call last):
  File "/home/abdelrahman/.config/blender/4.1/scripts/addons/bone_layer_manager/constraint_operators.py", line 443, in poll
    return len(bone.constraints) > 0 and len(context.selected_pose_bones) > 1
               ^^^^^^^^^^^^^^^^
AttributeError: 'NoneType' object has no attribute 'constraints'
Traceback (most recent call last):
  File "/home/abdelrahman/.config/blender/4.1/scripts/addons/bone_layer_manager/constraint_operators.py", line 260, in poll
    return len(bone.constraints) > 0 and len(context.selected_pose_bones) > 0
               ^^^^^^^^^^^^^^^^
AttributeError: 'NoneType' object has no attribute 'constraints'
=================================================================
==1796374==ERROR: AddressSanitizer: heap-use-after-free on address 0x60c000027c40 at pc 0x0000047e8a4d bp 0x7fa4ffff03d0 sp 0x7fa4ffff03c0
READ of size 8 at 0x60c000027c40 thread T18
    #0 0x47e8a4c in MEM_lockfree_freeN /home/abdelrahman/Sources/programming/blender/blender/intern/guardedalloc/intern/mallocn_lockfree_impl.c:98
    #1 0x374bfe9 in free_layer_data /home/abdelrahman/Sources/programming/blender/blender/source/blender/blenkernel/intern/customdata.cc:2150
    #2 0x377b79e in CustomDataLayerImplicitSharing::delete_self_with_data() /home/abdelrahman/Sources/programming/blender/blender/source/blender/blenkernel/intern/customdata.cc:2312
    #3 0xa5ff14 in blender::ImplicitSharingInfo::remove_user_and_delete_if_last() const /home/abdelrahman/Sources/programming/blender/blender/source/blender/blenlib/BLI_implicit_sharing.hh:145
    #4 0x3750959 in customData_free_layer__internal /home/abdelrahman/Sources/programming/blender/blender/source/blender/blenkernel/intern/customdata.cc:2478
    #5 0x3750cc3 in CustomData_free(CustomData*, int) /home/abdelrahman/Sources/programming/blender/blender/source/blender/blenkernel/intern/customdata.cc:2500
    #6 0x35f7df0 in blender::bke::CurvesGeometry::~CurvesGeometry() /home/abdelrahman/Sources/programming/blender/blender/source/blender/blenkernel/intern/curves_geometry.cc:174
    #7 0xac1657 in blender::bke::greasepencil::Drawing::~Drawing() /home/abdelrahman/Sources/programming/blender/blender/source/blender/blenkernel/intern/grease_pencil.cc:300
    #8 0xb02cb8 in void MEM_delete<blender::bke::greasepencil::Drawing>(blender::bke::greasepencil::Drawing const*) /home/abdelrahman/Sources/programming/blender/blender/intern/guardedalloc/MEM_guardedalloc.h:292
    #9 0xae30f9 in free_drawing_array /home/abdelrahman/Sources/programming/blender/blender/source/blender/blenkernel/intern/grease_pencil.cc:2359
    #10 0xabf9f7 in grease_pencil_free_data /home/abdelrahman/Sources/programming/blender/blender/source/blender/blenkernel/intern/grease_pencil.cc:132
    #11 0xdbf343 in BKE_libblock_free_datablock /home/abdelrahman/Sources/programming/blender/blender/source/blender/blenkernel/intern/lib_id_delete.cc:77
    #12 0x46a3088 in blender::deg::deg_free_copy_on_write_datablock(ID*) /home/abdelrahman/Sources/programming/blender/blender/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc:1017
    #13 0x46a26fd in blender::deg::deg_update_copy_on_write_datablock(blender::deg::Depsgraph const*, blender::deg::IDNode const*) /home/abdelrahman/Sources/programming/blender/blender/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc:896
    #14 0x46a35e3 in blender::deg::deg_evaluate_copy_on_write(Depsgraph*, blender::deg::IDNode const*) /home/abdelrahman/Sources/programming/blender/blender/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc:1032
    #15 0x4729aa1 in operator() /home/abdelrahman/Sources/programming/blender/blender/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc:182
    #16 0x4781e16 in __invoke_impl<void, blender::deg::DepsgraphNodeBuilder::add_id_node(ID*)::<lambda(Depsgraph*)>&, Depsgraph*> /usr/lib/gcc/x86_64-pc-linux-gnu/12.3.0/include/c++/bits/invoke.h:61
    #17 0x4777ccc in __invoke_r<void, blender::deg::DepsgraphNodeBuilder::add_id_node(ID*)::<lambda(Depsgraph*)>&, Depsgraph*> /usr/lib/gcc/x86_64-pc-linux-gnu/12.3.0/include/c++/bits/invoke.h:111
    #18 0x477177f in _M_invoke /usr/lib/gcc/x86_64-pc-linux-gnu/12.3.0/include/c++/bits/std_function.h:290
    #19 0x469a772 in std::function<void (Depsgraph*)>::operator()(Depsgraph*) const /usr/lib/gcc/x86_64-pc-linux-gnu/12.3.0/include/c++/bits/std_function.h:591
    #20 0x4694a81 in evaluate_node /home/abdelrahman/Sources/programming/blender/blender/source/blender/depsgraph/intern/eval/deg_eval.cc:102
    #21 0x4694ec2 in deg_task_run_func /home/abdelrahman/Sources/programming/blender/blender/source/blender/depsgraph/intern/eval/deg_eval.cc:119
    #22 0x45170de in Task::operator()() const /home/abdelrahman/Sources/programming/blender/blender/source/blender/blenlib/intern/task_pool.cc:166
    #23 0x451c02e in tbb::internal::function_task<Task>::execute() /home/abdelrahman/Sources/programming/blender/lib/linux_x86_64/tbb/include/tbb/task.h:1059
    #24 0x7fa55a59f5b4 in tbb::internal::custom_scheduler<tbb::internal::IntelSchedulerTraits>::process_bypass_loop(tbb::internal::context_guard_helper<false>&, tbb::task*, long) (/home/abdelrahman/Sources/programming/blender/build_linux_debug/bin/lib/libtbb.so.2+0x2a5b4) (BuildId: efcc170f66e9a8108477d04e9164946672875ebb)
    #25 0x7fa55a59f8f2 in tbb::internal::custom_scheduler<tbb::internal::IntelSchedulerTraits>::local_wait_for_all(tbb::task&, tbb::task*) (/home/abdelrahman/Sources/programming/blender/build_linux_debug/bin/lib/libtbb.so.2+0x2a8f2) (BuildId: efcc170f66e9a8108477d04e9164946672875ebb)
    #26 0x7fa55a587776 in tbb::internal::arena::process(tbb::internal::generic_scheduler&) (/home/abdelrahman/Sources/programming/blender/build_linux_debug/bin/lib/libtbb.so.2+0x12776) (BuildId: efcc170f66e9a8108477d04e9164946672875ebb)
    #27 0x7fa55a595f9f in tbb::internal::market::process(rml::job&) (/home/abdelrahman/Sources/programming/blender/build_linux_debug/bin/lib/libtbb.so.2+0x20f9f) (BuildId: efcc170f66e9a8108477d04e9164946672875ebb)
    #28 0x7fa55a599d0d in tbb::internal::rml::private_worker::run() (/home/abdelrahman/Sources/programming/blender/build_linux_debug/bin/lib/libtbb.so.2+0x24d0d) (BuildId: efcc170f66e9a8108477d04e9164946672875ebb)
    #29 0x7fa55a599f88 in tbb::internal::rml::private_worker::thread_routine(void*) (/home/abdelrahman/Sources/programming/blender/build_linux_debug/bin/lib/libtbb.so.2+0x24f88) (BuildId: efcc170f66e9a8108477d04e9164946672875ebb)
    #30 0x7fa54f8aa9ea  (/usr/lib/libc.so.6+0x8c9ea) (BuildId: 8bfe03f6bf9b6a6e2591babd0bbc266837d8f658)
    #31 0x7fa54f92e7cb  (/usr/lib/libc.so.6+0x1107cb) (BuildId: 8bfe03f6bf9b6a6e2591babd0bbc266837d8f658)

0x60c000027c40 is located 0 bytes inside of 128-byte region [0x60c000027c40,0x60c000027cc0)
freed by thread T18 here:
    #0 0x7fa559edfdb2 in __interceptor_free /usr/src/debug/gcc/gcc/libsanitizer/asan/asan_malloc_linux.cpp:52
    #1 0x47e8ce9 in MEM_lockfree_freeN /home/abdelrahman/Sources/programming/blender/blender/intern/guardedalloc/intern/mallocn_lockfree_impl.c:110
    #2 0x374bfe9 in free_layer_data /home/abdelrahman/Sources/programming/blender/blender/source/blender/blenkernel/intern/customdata.cc:2150
    #3 0x377b79e in CustomDataLayerImplicitSharing::delete_self_with_data() /home/abdelrahman/Sources/programming/blender/blender/source/blender/blenkernel/intern/customdata.cc:2312
    #4 0xa5ff14 in blender::ImplicitSharingInfo::remove_user_and_delete_if_last() const /home/abdelrahman/Sources/programming/blender/blender/source/blender/blenlib/BLI_implicit_sharing.hh:145
    #5 0x3750959 in customData_free_layer__internal /home/abdelrahman/Sources/programming/blender/blender/source/blender/blenkernel/intern/customdata.cc:2478
    #6 0x3750cc3 in CustomData_free(CustomData*, int) /home/abdelrahman/Sources/programming/blender/blender/source/blender/blenkernel/intern/customdata.cc:2500
    #7 0x35f7df0 in blender::bke::CurvesGeometry::~CurvesGeometry() /home/abdelrahman/Sources/programming/blender/blender/source/blender/blenkernel/intern/curves_geometry.cc:174
    #8 0xac1657 in blender::bke::greasepencil::Drawing::~Drawing() /home/abdelrahman/Sources/programming/blender/blender/source/blender/blenkernel/intern/grease_pencil.cc:300
    #9 0xb02cb8 in void MEM_delete<blender::bke::greasepencil::Drawing>(blender::bke::greasepencil::Drawing const*) /home/abdelrahman/Sources/programming/blender/blender/intern/guardedalloc/MEM_guardedalloc.h:292
    #10 0xae30f9 in free_drawing_array /home/abdelrahman/Sources/programming/blender/blender/source/blender/blenkernel/intern/grease_pencil.cc:2359
    #11 0xabf9f7 in grease_pencil_free_data /home/abdelrahman/Sources/programming/blender/blender/source/blender/blenkernel/intern/grease_pencil.cc:132
    #12 0xdbf343 in BKE_libblock_free_datablock /home/abdelrahman/Sources/programming/blender/blender/source/blender/blenkernel/intern/lib_id_delete.cc:77
    #13 0x46a3088 in blender::deg::deg_free_copy_on_write_datablock(ID*) /home/abdelrahman/Sources/programming/blender/blender/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc:1017
    #14 0x46a26fd in blender::deg::deg_update_copy_on_write_datablock(blender::deg::Depsgraph const*, blender::deg::IDNode const*) /home/abdelrahman/Sources/programming/blender/blender/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc:896
    #15 0x46a35e3 in blender::deg::deg_evaluate_copy_on_write(Depsgraph*, blender::deg::IDNode const*) /home/abdelrahman/Sources/programming/blender/blender/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc:1032
    #16 0x4729aa1 in operator() /home/abdelrahman/Sources/programming/blender/blender/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc:182
    #17 0x4781e16 in __invoke_impl<void, blender::deg::DepsgraphNodeBuilder::add_id_node(ID*)::<lambda(Depsgraph*)>&, Depsgraph*> /usr/lib/gcc/x86_64-pc-linux-gnu/12.3.0/include/c++/bits/invoke.h:61
    #18 0x4777ccc in __invoke_r<void, blender::deg::DepsgraphNodeBuilder::add_id_node(ID*)::<lambda(Depsgraph*)>&, Depsgraph*> /usr/lib/gcc/x86_64-pc-linux-gnu/12.3.0/include/c++/bits/invoke.h:111
    #19 0x477177f in _M_invoke /usr/lib/gcc/x86_64-pc-linux-gnu/12.3.0/include/c++/bits/std_function.h:290
    #20 0x469a772 in std::function<void (Depsgraph*)>::operator()(Depsgraph*) const /usr/lib/gcc/x86_64-pc-linux-gnu/12.3.0/include/c++/bits/std_function.h:591
    #21 0x4694a81 in evaluate_node /home/abdelrahman/Sources/programming/blender/blender/source/blender/depsgraph/intern/eval/deg_eval.cc:102
    #22 0x4694ec2 in deg_task_run_func /home/abdelrahman/Sources/programming/blender/blender/source/blender/depsgraph/intern/eval/deg_eval.cc:119
    #23 0x45170de in Task::operator()() const /home/abdelrahman/Sources/programming/blender/blender/source/blender/blenlib/intern/task_pool.cc:166
    #24 0x451c02e in tbb::internal::function_task<Task>::execute() /home/abdelrahman/Sources/programming/blender/lib/linux_x86_64/tbb/include/tbb/task.h:1059
    #25 0x7fa55a59f5b4 in tbb::internal::custom_scheduler<tbb::internal::IntelSchedulerTraits>::process_bypass_loop(tbb::internal::context_guard_helper<false>&, tbb::task*, long) (/home/abdelrahman/Sources/programming/blender/build_linux_debug/bin/lib/libtbb.so.2+0x2a5b4) (BuildId: efcc170f66e9a8108477d04e9164946672875ebb)

previously allocated by thread T0 here:
    #0 0x7fa559ee1359 in __interceptor_malloc /usr/src/debug/gcc/gcc/libsanitizer/asan/asan_malloc_linux.cpp:69
    #1 0x47e96c4 in MEM_lockfree_mallocN /home/abdelrahman/Sources/programming/blender/blender/intern/guardedalloc/intern/mallocn_lockfree_impl.c:258
    #2 0x4a9dc3c in read_struct /home/abdelrahman/Sources/programming/blender/blender/source/blender/blenloader/intern/readfile.cc:1811
    #3 0x4aa4936 in read_data_into_datamap /home/abdelrahman/Sources/programming/blender/blender/source/blender/blenloader/intern/readfile.cc:2550
    #4 0x4aab6cc in read_libblock /home/abdelrahman/Sources/programming/blender/blender/source/blender/blenloader/intern/readfile.cc:3025
    #5 0x4ab5192 in blo_read_file_internal(FileData*, char const*) /home/abdelrahman/Sources/programming/blender/blender/source/blender/blenloader/intern/readfile.cc:3668
    #6 0x4a88cdb in BLO_read_from_file /home/abdelrahman/Sources/programming/blender/blender/source/blender/blenloader/intern/readblenentry.cc:417
    #7 0x8331f8 in BKE_blendfile_read(char const*, BlendFileReadParams const*, BlendFileReadReport*) /home/abdelrahman/Sources/programming/blender/blender/source/blender/blenkernel/intern/blendfile.cc:1030
    #8 0x48776bd in WM_file_read(bContext*, char const*, ReportList*) /home/abdelrahman/Sources/programming/blender/blender/source/blender/windowmanager/intern/wm_files.cc:1024
    #9 0x48846f9 in wm_file_read_opwrap /home/abdelrahman/Sources/programming/blender/blender/source/blender/windowmanager/intern/wm_files.cc:2744
    #10 0x48857bb in wm_open_mainfile__open /home/abdelrahman/Sources/programming/blender/blender/source/blender/windowmanager/intern/wm_files.cc:2870
    #11 0x4884a91 in operator_state_dispatch /home/abdelrahman/Sources/programming/blender/blender/source/blender/windowmanager/intern/wm_files.cc:2780
    #12 0x4885995 in wm_open_mainfile_dispatch /home/abdelrahman/Sources/programming/blender/blender/source/blender/windowmanager/intern/wm_files.cc:2891
    #13 0x4884d51 in wm_open_mainfile__discard_changes /home/abdelrahman/Sources/programming/blender/blender/source/blender/windowmanager/intern/wm_files.cc:2819
    #14 0x4884a91 in operator_state_dispatch /home/abdelrahman/Sources/programming/blender/blender/source/blender/windowmanager/intern/wm_files.cc:2780
    #15 0x4885995 in wm_open_mainfile_dispatch /home/abdelrahman/Sources/programming/blender/blender/source/blender/windowmanager/intern/wm_files.cc:2891
    #16 0x48859be in wm_open_mainfile_invoke /home/abdelrahman/Sources/programming/blender/blender/source/blender/windowmanager/intern/wm_files.cc:2896
    #17 0x482b5a6 in wm_operator_invoke /home/abdelrahman/Sources/programming/blender/blender/source/blender/windowmanager/intern/wm_event_system.cc:1550
    #18 0x482d47e in wm_operator_call_internal /home/abdelrahman/Sources/programming/blender/blender/source/blender/windowmanager/intern/wm_event_system.cc:1785
    #19 0x482d587 in WM_operator_name_call_ptr(bContext*, wmOperatorType*, wmOperatorCallContext, PointerRNA*, wmEvent const*) /home/abdelrahman/Sources/programming/blender/blender/source/blender/windowmanager/intern/wm_event_system.cc:1799
    #20 0x482eb28 in WM_operator_name_call_ptr_with_depends_on_cursor(bContext*, wmOperatorType*, wmOperatorCallContext, PointerRNA*, wmEvent const*, char const*) /home/abdelrahman/Sources/programming/blender/blender/source/blender/windowmanager/intern/wm_event_system.cc:1988
    #21 0x807c3d6 in ui_apply_but_funcs_after /home/abdelrahman/Sources/programming/blender/blender/source/blender/editors/interface/interface_handlers.cc:1039
    #22 0x81138b0 in ui_popup_handler /home/abdelrahman/Sources/programming/blender/blender/source/blender/editors/interface/interface_handlers.cc:11747
    #23 0x4824085 in wm_handler_ui_call /home/abdelrahman/Sources/programming/blender/blender/source/blender/windowmanager/intern/wm_event_system.cc:831
    #24 0x48447a9 in wm_handlers_do_intern /home/abdelrahman/Sources/programming/blender/blender/source/blender/windowmanager/intern/wm_event_system.cc:3333
    #25 0x4845bb1 in wm_handlers_do /home/abdelrahman/Sources/programming/blender/blender/source/blender/windowmanager/intern/wm_event_system.cc:3450
    #26 0x48501ce in wm_event_do_handlers(bContext*) /home/abdelrahman/Sources/programming/blender/blender/source/blender/windowmanager/intern/wm_event_system.cc:4077
    #27 0x47fbb3a in WM_main(bContext*) /home/abdelrahman/Sources/programming/blender/blender/source/blender/windowmanager/intern/wm.cc:613
    #28 0x80e26b in main /home/abdelrahman/Sources/programming/blender/blender/source/creator/creator.cc:575
    #29 0x7fa54f845ccf  (/usr/lib/libc.so.6+0x27ccf) (BuildId: 8bfe03f6bf9b6a6e2591babd0bbc266837d8f658)

Thread T18 created by T0 here:
    #0 0x7fa559e4a497 in __interceptor_pthread_create /usr/src/debug/gcc/gcc/libsanitizer/asan/asan_interceptors.cpp:208
    #1 0x7fa55a599be5 in tbb::internal::rml::private_server::wake_some(int) (/home/abdelrahman/Sources/programming/blender/build_linux_debug/bin/lib/libtbb.so.2+0x24be5) (BuildId: efcc170f66e9a8108477d04e9164946672875ebb)

SUMMARY: AddressSanitizer: heap-use-after-free /home/abdelrahman/Sources/programming/blender/blender/intern/guardedalloc/intern/mallocn_lockfree_impl.c:98 in MEM_lockfree_freeN
Shadow bytes around the buggy address:
  0x60c000027980: 00 00 00 00 00 00 00 00 fa fa fa fa fa fa fa fa
  0x60c000027a00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x60c000027a80: fa fa fa fa fa fa fa fa 00 00 00 00 00 00 00 00
  0x60c000027b00: 00 00 00 00 00 00 00 00 fa fa fa fa fa fa fa fa
  0x60c000027b80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x60c000027c00: fa fa fa fa fa fa fa fa[fd]fd fd fd fd fd fd fd
  0x60c000027c80: fd fd fd fd fd fd fd fd fa fa fa fa fa fa fa fa
  0x60c000027d00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x60c000027d80: fa fa fa fa fa fa fa fa fd fd fd fd fd fd fd fd
  0x60c000027e00: fd fd fd fd fd fd fd fd fa fa fa fa fa fa fa fa
  0x60c000027e80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==1796374==ABORTING
When I try to use this `grease_pencil.remove_frames(*layer, frames_to_delete);` I get a crash. I ran Blender in a debugger to see if I can find what is causing the crash, but I haven't figured out the reason. I am sure I am missing something with this being my first time working with Blender's code. This is the output I get in the terminal after running the test file I am using. I can't see what might be causing the problem so I thought I would share it here in case someone can figure it out. ``` Traceback (most recent call last): File "/home/abdelrahman/.config/blender/4.1/scripts/addons/bone_layer_manager/constraint_operators.py", line 472, in poll idx = cbone.constraint_active_index ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ AttributeError: 'NoneType' object has no attribute 'constraint_active_index' Traceback (most recent call last): File "/home/abdelrahman/.config/blender/4.1/scripts/addons/bone_layer_manager/constraint_operators.py", line 370, in poll return len(bone.constraints) > 0 and len(context.selected_pose_bones) > 1 ^^^^^^^^^^^^^^^^ AttributeError: 'NoneType' object has no attribute 'constraints' Traceback (most recent call last): File "/home/abdelrahman/.config/blender/4.1/scripts/addons/bone_layer_manager/constraint_operators.py", line 404, in poll return len(bone.constraints) > 0 and len(context.selected_pose_bones) > 1 ^^^^^^^^^^^^^^^^ AttributeError: 'NoneType' object has no attribute 'constraints' Traceback (most recent call last): File "/home/abdelrahman/.config/blender/4.1/scripts/addons/bone_layer_manager/constraint_operators.py", line 443, in poll return len(bone.constraints) > 0 and len(context.selected_pose_bones) > 1 ^^^^^^^^^^^^^^^^ AttributeError: 'NoneType' object has no attribute 'constraints' Traceback (most recent call last): File "/home/abdelrahman/.config/blender/4.1/scripts/addons/bone_layer_manager/constraint_operators.py", line 260, in poll return len(bone.constraints) > 0 and len(context.selected_pose_bones) > 0 ^^^^^^^^^^^^^^^^ AttributeError: 'NoneType' object has no attribute 'constraints' ================================================================= ==1796374==ERROR: AddressSanitizer: heap-use-after-free on address 0x60c000027c40 at pc 0x0000047e8a4d bp 0x7fa4ffff03d0 sp 0x7fa4ffff03c0 READ of size 8 at 0x60c000027c40 thread T18 #0 0x47e8a4c in MEM_lockfree_freeN /home/abdelrahman/Sources/programming/blender/blender/intern/guardedalloc/intern/mallocn_lockfree_impl.c:98 #1 0x374bfe9 in free_layer_data /home/abdelrahman/Sources/programming/blender/blender/source/blender/blenkernel/intern/customdata.cc:2150 #2 0x377b79e in CustomDataLayerImplicitSharing::delete_self_with_data() /home/abdelrahman/Sources/programming/blender/blender/source/blender/blenkernel/intern/customdata.cc:2312 #3 0xa5ff14 in blender::ImplicitSharingInfo::remove_user_and_delete_if_last() const /home/abdelrahman/Sources/programming/blender/blender/source/blender/blenlib/BLI_implicit_sharing.hh:145 #4 0x3750959 in customData_free_layer__internal /home/abdelrahman/Sources/programming/blender/blender/source/blender/blenkernel/intern/customdata.cc:2478 #5 0x3750cc3 in CustomData_free(CustomData*, int) /home/abdelrahman/Sources/programming/blender/blender/source/blender/blenkernel/intern/customdata.cc:2500 #6 0x35f7df0 in blender::bke::CurvesGeometry::~CurvesGeometry() /home/abdelrahman/Sources/programming/blender/blender/source/blender/blenkernel/intern/curves_geometry.cc:174 #7 0xac1657 in blender::bke::greasepencil::Drawing::~Drawing() /home/abdelrahman/Sources/programming/blender/blender/source/blender/blenkernel/intern/grease_pencil.cc:300 #8 0xb02cb8 in void MEM_delete<blender::bke::greasepencil::Drawing>(blender::bke::greasepencil::Drawing const*) /home/abdelrahman/Sources/programming/blender/blender/intern/guardedalloc/MEM_guardedalloc.h:292 #9 0xae30f9 in free_drawing_array /home/abdelrahman/Sources/programming/blender/blender/source/blender/blenkernel/intern/grease_pencil.cc:2359 #10 0xabf9f7 in grease_pencil_free_data /home/abdelrahman/Sources/programming/blender/blender/source/blender/blenkernel/intern/grease_pencil.cc:132 #11 0xdbf343 in BKE_libblock_free_datablock /home/abdelrahman/Sources/programming/blender/blender/source/blender/blenkernel/intern/lib_id_delete.cc:77 #12 0x46a3088 in blender::deg::deg_free_copy_on_write_datablock(ID*) /home/abdelrahman/Sources/programming/blender/blender/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc:1017 #13 0x46a26fd in blender::deg::deg_update_copy_on_write_datablock(blender::deg::Depsgraph const*, blender::deg::IDNode const*) /home/abdelrahman/Sources/programming/blender/blender/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc:896 #14 0x46a35e3 in blender::deg::deg_evaluate_copy_on_write(Depsgraph*, blender::deg::IDNode const*) /home/abdelrahman/Sources/programming/blender/blender/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc:1032 #15 0x4729aa1 in operator() /home/abdelrahman/Sources/programming/blender/blender/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc:182 #16 0x4781e16 in __invoke_impl<void, blender::deg::DepsgraphNodeBuilder::add_id_node(ID*)::<lambda(Depsgraph*)>&, Depsgraph*> /usr/lib/gcc/x86_64-pc-linux-gnu/12.3.0/include/c++/bits/invoke.h:61 #17 0x4777ccc in __invoke_r<void, blender::deg::DepsgraphNodeBuilder::add_id_node(ID*)::<lambda(Depsgraph*)>&, Depsgraph*> /usr/lib/gcc/x86_64-pc-linux-gnu/12.3.0/include/c++/bits/invoke.h:111 #18 0x477177f in _M_invoke /usr/lib/gcc/x86_64-pc-linux-gnu/12.3.0/include/c++/bits/std_function.h:290 #19 0x469a772 in std::function<void (Depsgraph*)>::operator()(Depsgraph*) const /usr/lib/gcc/x86_64-pc-linux-gnu/12.3.0/include/c++/bits/std_function.h:591 #20 0x4694a81 in evaluate_node /home/abdelrahman/Sources/programming/blender/blender/source/blender/depsgraph/intern/eval/deg_eval.cc:102 #21 0x4694ec2 in deg_task_run_func /home/abdelrahman/Sources/programming/blender/blender/source/blender/depsgraph/intern/eval/deg_eval.cc:119 #22 0x45170de in Task::operator()() const /home/abdelrahman/Sources/programming/blender/blender/source/blender/blenlib/intern/task_pool.cc:166 #23 0x451c02e in tbb::internal::function_task<Task>::execute() /home/abdelrahman/Sources/programming/blender/lib/linux_x86_64/tbb/include/tbb/task.h:1059 #24 0x7fa55a59f5b4 in tbb::internal::custom_scheduler<tbb::internal::IntelSchedulerTraits>::process_bypass_loop(tbb::internal::context_guard_helper<false>&, tbb::task*, long) (/home/abdelrahman/Sources/programming/blender/build_linux_debug/bin/lib/libtbb.so.2+0x2a5b4) (BuildId: efcc170f66e9a8108477d04e9164946672875ebb) #25 0x7fa55a59f8f2 in tbb::internal::custom_scheduler<tbb::internal::IntelSchedulerTraits>::local_wait_for_all(tbb::task&, tbb::task*) (/home/abdelrahman/Sources/programming/blender/build_linux_debug/bin/lib/libtbb.so.2+0x2a8f2) (BuildId: efcc170f66e9a8108477d04e9164946672875ebb) #26 0x7fa55a587776 in tbb::internal::arena::process(tbb::internal::generic_scheduler&) (/home/abdelrahman/Sources/programming/blender/build_linux_debug/bin/lib/libtbb.so.2+0x12776) (BuildId: efcc170f66e9a8108477d04e9164946672875ebb) #27 0x7fa55a595f9f in tbb::internal::market::process(rml::job&) (/home/abdelrahman/Sources/programming/blender/build_linux_debug/bin/lib/libtbb.so.2+0x20f9f) (BuildId: efcc170f66e9a8108477d04e9164946672875ebb) #28 0x7fa55a599d0d in tbb::internal::rml::private_worker::run() (/home/abdelrahman/Sources/programming/blender/build_linux_debug/bin/lib/libtbb.so.2+0x24d0d) (BuildId: efcc170f66e9a8108477d04e9164946672875ebb) #29 0x7fa55a599f88 in tbb::internal::rml::private_worker::thread_routine(void*) (/home/abdelrahman/Sources/programming/blender/build_linux_debug/bin/lib/libtbb.so.2+0x24f88) (BuildId: efcc170f66e9a8108477d04e9164946672875ebb) #30 0x7fa54f8aa9ea (/usr/lib/libc.so.6+0x8c9ea) (BuildId: 8bfe03f6bf9b6a6e2591babd0bbc266837d8f658) #31 0x7fa54f92e7cb (/usr/lib/libc.so.6+0x1107cb) (BuildId: 8bfe03f6bf9b6a6e2591babd0bbc266837d8f658) 0x60c000027c40 is located 0 bytes inside of 128-byte region [0x60c000027c40,0x60c000027cc0) freed by thread T18 here: #0 0x7fa559edfdb2 in __interceptor_free /usr/src/debug/gcc/gcc/libsanitizer/asan/asan_malloc_linux.cpp:52 #1 0x47e8ce9 in MEM_lockfree_freeN /home/abdelrahman/Sources/programming/blender/blender/intern/guardedalloc/intern/mallocn_lockfree_impl.c:110 #2 0x374bfe9 in free_layer_data /home/abdelrahman/Sources/programming/blender/blender/source/blender/blenkernel/intern/customdata.cc:2150 #3 0x377b79e in CustomDataLayerImplicitSharing::delete_self_with_data() /home/abdelrahman/Sources/programming/blender/blender/source/blender/blenkernel/intern/customdata.cc:2312 #4 0xa5ff14 in blender::ImplicitSharingInfo::remove_user_and_delete_if_last() const /home/abdelrahman/Sources/programming/blender/blender/source/blender/blenlib/BLI_implicit_sharing.hh:145 #5 0x3750959 in customData_free_layer__internal /home/abdelrahman/Sources/programming/blender/blender/source/blender/blenkernel/intern/customdata.cc:2478 #6 0x3750cc3 in CustomData_free(CustomData*, int) /home/abdelrahman/Sources/programming/blender/blender/source/blender/blenkernel/intern/customdata.cc:2500 #7 0x35f7df0 in blender::bke::CurvesGeometry::~CurvesGeometry() /home/abdelrahman/Sources/programming/blender/blender/source/blender/blenkernel/intern/curves_geometry.cc:174 #8 0xac1657 in blender::bke::greasepencil::Drawing::~Drawing() /home/abdelrahman/Sources/programming/blender/blender/source/blender/blenkernel/intern/grease_pencil.cc:300 #9 0xb02cb8 in void MEM_delete<blender::bke::greasepencil::Drawing>(blender::bke::greasepencil::Drawing const*) /home/abdelrahman/Sources/programming/blender/blender/intern/guardedalloc/MEM_guardedalloc.h:292 #10 0xae30f9 in free_drawing_array /home/abdelrahman/Sources/programming/blender/blender/source/blender/blenkernel/intern/grease_pencil.cc:2359 #11 0xabf9f7 in grease_pencil_free_data /home/abdelrahman/Sources/programming/blender/blender/source/blender/blenkernel/intern/grease_pencil.cc:132 #12 0xdbf343 in BKE_libblock_free_datablock /home/abdelrahman/Sources/programming/blender/blender/source/blender/blenkernel/intern/lib_id_delete.cc:77 #13 0x46a3088 in blender::deg::deg_free_copy_on_write_datablock(ID*) /home/abdelrahman/Sources/programming/blender/blender/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc:1017 #14 0x46a26fd in blender::deg::deg_update_copy_on_write_datablock(blender::deg::Depsgraph const*, blender::deg::IDNode const*) /home/abdelrahman/Sources/programming/blender/blender/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc:896 #15 0x46a35e3 in blender::deg::deg_evaluate_copy_on_write(Depsgraph*, blender::deg::IDNode const*) /home/abdelrahman/Sources/programming/blender/blender/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc:1032 #16 0x4729aa1 in operator() /home/abdelrahman/Sources/programming/blender/blender/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc:182 #17 0x4781e16 in __invoke_impl<void, blender::deg::DepsgraphNodeBuilder::add_id_node(ID*)::<lambda(Depsgraph*)>&, Depsgraph*> /usr/lib/gcc/x86_64-pc-linux-gnu/12.3.0/include/c++/bits/invoke.h:61 #18 0x4777ccc in __invoke_r<void, blender::deg::DepsgraphNodeBuilder::add_id_node(ID*)::<lambda(Depsgraph*)>&, Depsgraph*> /usr/lib/gcc/x86_64-pc-linux-gnu/12.3.0/include/c++/bits/invoke.h:111 #19 0x477177f in _M_invoke /usr/lib/gcc/x86_64-pc-linux-gnu/12.3.0/include/c++/bits/std_function.h:290 #20 0x469a772 in std::function<void (Depsgraph*)>::operator()(Depsgraph*) const /usr/lib/gcc/x86_64-pc-linux-gnu/12.3.0/include/c++/bits/std_function.h:591 #21 0x4694a81 in evaluate_node /home/abdelrahman/Sources/programming/blender/blender/source/blender/depsgraph/intern/eval/deg_eval.cc:102 #22 0x4694ec2 in deg_task_run_func /home/abdelrahman/Sources/programming/blender/blender/source/blender/depsgraph/intern/eval/deg_eval.cc:119 #23 0x45170de in Task::operator()() const /home/abdelrahman/Sources/programming/blender/blender/source/blender/blenlib/intern/task_pool.cc:166 #24 0x451c02e in tbb::internal::function_task<Task>::execute() /home/abdelrahman/Sources/programming/blender/lib/linux_x86_64/tbb/include/tbb/task.h:1059 #25 0x7fa55a59f5b4 in tbb::internal::custom_scheduler<tbb::internal::IntelSchedulerTraits>::process_bypass_loop(tbb::internal::context_guard_helper<false>&, tbb::task*, long) (/home/abdelrahman/Sources/programming/blender/build_linux_debug/bin/lib/libtbb.so.2+0x2a5b4) (BuildId: efcc170f66e9a8108477d04e9164946672875ebb) previously allocated by thread T0 here: #0 0x7fa559ee1359 in __interceptor_malloc /usr/src/debug/gcc/gcc/libsanitizer/asan/asan_malloc_linux.cpp:69 #1 0x47e96c4 in MEM_lockfree_mallocN /home/abdelrahman/Sources/programming/blender/blender/intern/guardedalloc/intern/mallocn_lockfree_impl.c:258 #2 0x4a9dc3c in read_struct /home/abdelrahman/Sources/programming/blender/blender/source/blender/blenloader/intern/readfile.cc:1811 #3 0x4aa4936 in read_data_into_datamap /home/abdelrahman/Sources/programming/blender/blender/source/blender/blenloader/intern/readfile.cc:2550 #4 0x4aab6cc in read_libblock /home/abdelrahman/Sources/programming/blender/blender/source/blender/blenloader/intern/readfile.cc:3025 #5 0x4ab5192 in blo_read_file_internal(FileData*, char const*) /home/abdelrahman/Sources/programming/blender/blender/source/blender/blenloader/intern/readfile.cc:3668 #6 0x4a88cdb in BLO_read_from_file /home/abdelrahman/Sources/programming/blender/blender/source/blender/blenloader/intern/readblenentry.cc:417 #7 0x8331f8 in BKE_blendfile_read(char const*, BlendFileReadParams const*, BlendFileReadReport*) /home/abdelrahman/Sources/programming/blender/blender/source/blender/blenkernel/intern/blendfile.cc:1030 #8 0x48776bd in WM_file_read(bContext*, char const*, ReportList*) /home/abdelrahman/Sources/programming/blender/blender/source/blender/windowmanager/intern/wm_files.cc:1024 #9 0x48846f9 in wm_file_read_opwrap /home/abdelrahman/Sources/programming/blender/blender/source/blender/windowmanager/intern/wm_files.cc:2744 #10 0x48857bb in wm_open_mainfile__open /home/abdelrahman/Sources/programming/blender/blender/source/blender/windowmanager/intern/wm_files.cc:2870 #11 0x4884a91 in operator_state_dispatch /home/abdelrahman/Sources/programming/blender/blender/source/blender/windowmanager/intern/wm_files.cc:2780 #12 0x4885995 in wm_open_mainfile_dispatch /home/abdelrahman/Sources/programming/blender/blender/source/blender/windowmanager/intern/wm_files.cc:2891 #13 0x4884d51 in wm_open_mainfile__discard_changes /home/abdelrahman/Sources/programming/blender/blender/source/blender/windowmanager/intern/wm_files.cc:2819 #14 0x4884a91 in operator_state_dispatch /home/abdelrahman/Sources/programming/blender/blender/source/blender/windowmanager/intern/wm_files.cc:2780 #15 0x4885995 in wm_open_mainfile_dispatch /home/abdelrahman/Sources/programming/blender/blender/source/blender/windowmanager/intern/wm_files.cc:2891 #16 0x48859be in wm_open_mainfile_invoke /home/abdelrahman/Sources/programming/blender/blender/source/blender/windowmanager/intern/wm_files.cc:2896 #17 0x482b5a6 in wm_operator_invoke /home/abdelrahman/Sources/programming/blender/blender/source/blender/windowmanager/intern/wm_event_system.cc:1550 #18 0x482d47e in wm_operator_call_internal /home/abdelrahman/Sources/programming/blender/blender/source/blender/windowmanager/intern/wm_event_system.cc:1785 #19 0x482d587 in WM_operator_name_call_ptr(bContext*, wmOperatorType*, wmOperatorCallContext, PointerRNA*, wmEvent const*) /home/abdelrahman/Sources/programming/blender/blender/source/blender/windowmanager/intern/wm_event_system.cc:1799 #20 0x482eb28 in WM_operator_name_call_ptr_with_depends_on_cursor(bContext*, wmOperatorType*, wmOperatorCallContext, PointerRNA*, wmEvent const*, char const*) /home/abdelrahman/Sources/programming/blender/blender/source/blender/windowmanager/intern/wm_event_system.cc:1988 #21 0x807c3d6 in ui_apply_but_funcs_after /home/abdelrahman/Sources/programming/blender/blender/source/blender/editors/interface/interface_handlers.cc:1039 #22 0x81138b0 in ui_popup_handler /home/abdelrahman/Sources/programming/blender/blender/source/blender/editors/interface/interface_handlers.cc:11747 #23 0x4824085 in wm_handler_ui_call /home/abdelrahman/Sources/programming/blender/blender/source/blender/windowmanager/intern/wm_event_system.cc:831 #24 0x48447a9 in wm_handlers_do_intern /home/abdelrahman/Sources/programming/blender/blender/source/blender/windowmanager/intern/wm_event_system.cc:3333 #25 0x4845bb1 in wm_handlers_do /home/abdelrahman/Sources/programming/blender/blender/source/blender/windowmanager/intern/wm_event_system.cc:3450 #26 0x48501ce in wm_event_do_handlers(bContext*) /home/abdelrahman/Sources/programming/blender/blender/source/blender/windowmanager/intern/wm_event_system.cc:4077 #27 0x47fbb3a in WM_main(bContext*) /home/abdelrahman/Sources/programming/blender/blender/source/blender/windowmanager/intern/wm.cc:613 #28 0x80e26b in main /home/abdelrahman/Sources/programming/blender/blender/source/creator/creator.cc:575 #29 0x7fa54f845ccf (/usr/lib/libc.so.6+0x27ccf) (BuildId: 8bfe03f6bf9b6a6e2591babd0bbc266837d8f658) Thread T18 created by T0 here: #0 0x7fa559e4a497 in __interceptor_pthread_create /usr/src/debug/gcc/gcc/libsanitizer/asan/asan_interceptors.cpp:208 #1 0x7fa55a599be5 in tbb::internal::rml::private_server::wake_some(int) (/home/abdelrahman/Sources/programming/blender/build_linux_debug/bin/lib/libtbb.so.2+0x24be5) (BuildId: efcc170f66e9a8108477d04e9164946672875ebb) SUMMARY: AddressSanitizer: heap-use-after-free /home/abdelrahman/Sources/programming/blender/blender/intern/guardedalloc/intern/mallocn_lockfree_impl.c:98 in MEM_lockfree_freeN Shadow bytes around the buggy address: 0x60c000027980: 00 00 00 00 00 00 00 00 fa fa fa fa fa fa fa fa 0x60c000027a00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x60c000027a80: fa fa fa fa fa fa fa fa 00 00 00 00 00 00 00 00 0x60c000027b00: 00 00 00 00 00 00 00 00 fa fa fa fa fa fa fa fa 0x60c000027b80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 =>0x60c000027c00: fa fa fa fa fa fa fa fa[fd]fd fd fd fd fd fd fd 0x60c000027c80: fd fd fd fd fd fd fd fd fa fa fa fa fa fa fa fa 0x60c000027d00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x60c000027d80: fa fa fa fa fa fa fa fa fd fd fd fd fd fd fd fd 0x60c000027e00: fd fd fd fd fd fd fd fd fa fa fa fa fa fa fa fa 0x60c000027e80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 Shadow byte legend (one shadow byte represents 8 application bytes): Addressable: 00 Partially addressable: 01 02 03 04 05 06 07 Heap left redzone: fa Freed heap region: fd Stack left redzone: f1 Stack mid redzone: f2 Stack right redzone: f3 Stack after return: f5 Stack use after scope: f8 Global redzone: f9 Global init order: f6 Poisoned by user: f7 Container overflow: fc Array cookie: ac Intra object redzone: bb ASan internal: fe Left alloca redzone: ca Right alloca redzone: cb ==1796374==ABORTING ```
const GreasePencilFrame *frame = layer->frames().lookup_ptr(key);
amsaid1989 marked this conversation as resolved Outdated

This works only if we assume that there are no fixed length keyframes. In order to avoid future breakage I suggest the following:

  • First, gather all the start frame numbers for the frames to consider:
Vector<int> start_frame_numbers;
for (const FramesMapKeyT key : layer->sorted_keys()) {
   const GreasePencilFrame *frame = layer->frames().lookup_ptr(key);
   if (selected && !frame->is_selected()) {
      continue;
   }
   if (frame->is_end()) {
     continue;
   }
   start_frame_numbers.append(int(key));
}
  • Then, loop over the start frame numbers and compare:
Vector<int> frame_numbers_to_delete;
for (const int i : start_frame_numbers.index_range().drop_back(1)) {
   const int current = start_frame_numbers[i];
   const int next = start_frame_numbers[i+1];
   
   Drawing *drawing = grease_pencil.get_editable_drawing_at(*layer, current);
   ...
}
This works only if we assume that there are no fixed length keyframes. In order to avoid future breakage I suggest the following: * First, gather all the start frame numbers for the frames to consider: ```cpp Vector<int> start_frame_numbers; for (const FramesMapKeyT key : layer->sorted_keys()) { const GreasePencilFrame *frame = layer->frames().lookup_ptr(key); if (selected && !frame->is_selected()) { continue; } if (frame->is_end()) { continue; } start_frame_numbers.append(int(key)); } ``` * Then, loop over the start frame numbers and compare: ```cpp Vector<int> frame_numbers_to_delete; for (const int i : start_frame_numbers.index_range().drop_back(1)) { const int current = start_frame_numbers[i]; const int next = start_frame_numbers[i+1]; Drawing *drawing = grease_pencil.get_editable_drawing_at(*layer, current); ... } ```

All done.

All done.
if (selected && !frame->is_selected()) {
amsaid1989 marked this conversation as resolved Outdated

Can be const?

Can be const?

Changed to const

Changed to `const`
continue;
}
if (frame->is_end()) {
continue;
}
start_frame_numbers.append(int(key));
}
amsaid1989 marked this conversation as resolved Outdated

We can use get_drawing_at here. The layer is already guaranteed to be editable.

We can use `get_drawing_at` here. The layer is already guaranteed to be editable.

Changed it

Changed it
Vector<int> frame_numbers_to_delete;
for (const int i : start_frame_numbers.index_range().drop_back(1)) {
const int current = start_frame_numbers[i];
const int next = start_frame_numbers[i + 1];
Drawing *drawing = grease_pencil.get_drawing_at(*layer, current);
Drawing *drawing_next = grease_pencil.get_drawing_at(*layer, next);
if (!drawing || !drawing_next) {
continue;
}
bke::CurvesGeometry &curves = drawing->strokes_for_write();
bke::CurvesGeometry &curves_next = drawing_next->strokes_for_write();
if (!curves_geometry_is_equal(curves, curves_next)) {
amsaid1989 marked this conversation as resolved Outdated

This loop should be replaced by grease_pencil.remove_frames(layer, frames_to_delete.as_span());.

This loop should be replaced by `grease_pencil.remove_frames(layer, frames_to_delete.as_span());`.

Yeah, I suggested that earlier: #116655 (comment)
But Abdelrahman experienced crash with remove_frames() (I honestly didn't try to repro crash locally 😅)

Yeah, I suggested that earlier: https://projects.blender.org/blender/blender/pulls/116655#issuecomment-1108915 But Abdelrahman experienced crash with `remove_frames()` (I honestly didn't try to repro crash locally 😅)

If this still results in a crash, then we should fix the crash. Calling remove_frame on the layer class shouldn't be done (I'm working on making these functions protected).

If this still results in a crash, then we should fix the crash. Calling `remove_frame` on the layer class shouldn't be done (I'm working on making these functions protected).

Tried remove_frames again and, this time, it worked. I think whatever the cause of the crash, it was fixed by some other update and now, since I merge the main branch, it's working.

Tried `remove_frames` again and, this time, it worked. I think whatever the cause of the crash, it was fixed by some other update and now, since I merge the `main` branch, it's working.
continue;
}
frame_numbers_to_delete.append(next);
}
grease_pencil.remove_frames(*layer, frame_numbers_to_delete.as_span());
changed = true;
}
if (changed) {
DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, nullptr);
}
return OPERATOR_FINISHED;
}
static void GREASE_PENCIL_OT_insert_blank_frame(wmOperatorType *ot)
{
PropertyRNA *prop;
@ -435,6 +592,27 @@ static void GREASE_PENCIL_OT_insert_blank_frame(wmOperatorType *ot)
RNA_def_int(ot->srna, "duration", 0, 0, MAXFRAME, "Duration", "", 0, 100);
}
static void GREASE_PENCIL_OT_frame_clean_duplicate(wmOperatorType *ot)
{
PropertyRNA *prop;
/* identifiers */
ot->name = "Delete Duplicate Frames";
ot->idname = "GREASE_PENCIL_OT_frame_clean_duplicate";
ot->description = "Remove any keyframe that is a duplicate of the previous one";
/* callbacks */
ot->exec = frame_clean_duplicate_exec;
ot->poll = active_grease_pencil_poll;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
prop = RNA_def_boolean(
ot->srna, "selected", false, "Selected", "Only delete selected keyframes");
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
}
bool grease_pencil_copy_keyframes(bAnimContext *ac, KeyframeClipboard &clipboard)
{
using namespace bke::greasepencil;
@ -635,4 +813,5 @@ void ED_operatortypes_grease_pencil_frames()
{
using namespace blender::ed::greasepencil;
WM_operatortype_append(GREASE_PENCIL_OT_insert_blank_frame);
WM_operatortype_append(GREASE_PENCIL_OT_frame_clean_duplicate);
}