GPv3: Port frame_clean_duplicate
operator #116655
|
@ -5892,6 +5892,7 @@ class VIEW3D_MT_edit_greasepencil_cleanup(Menu):
|
|||
layout = self.layout
|
||||
|
||||
layout.operator("grease_pencil.clean_loose")
|
||||
layout.operator("grease_pencil.frame_clean_duplicate")
|
||||
|
||||
|
||||
class VIEW3D_MT_edit_greasepencil(Menu):
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
* \ingroup edgreasepencil
|
||||
*/
|
||||
|
||||
#include "BKE_curves.hh"
|
||||
#include "BLI_map.hh"
|
||||
#include "BLI_math_vector_types.hh"
|
||||
#include "BLI_utildefines.h"
|
||||
|
@ -410,6 +411,160 @@ 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)
|
||||
{
|
||||
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();
|
||||
amsaid1989 marked this conversation as resolved
Outdated
|
||||
|
||||
if (attrs_span_a.data() == attrs_span_b.data()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
amsaid1989 marked this conversation as resolved
Outdated
Iliya Katushenock
commented
Should be false. Should be false.
Abdelrahman Said
commented
Done Done
|
||||
return false;
|
||||
}
|
||||
PratikPB2123 marked this conversation as resolved
Outdated
Pratik Borhade
commented
Not sure why this check is needed. Not sure why this check is needed.
Iliya Katushenock
commented
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..
Abdelrahman Said
commented
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
Pratik Borhade
commented
I see, make sense. I see, make sense.
|
||||
|
||||
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() ||
|
||||
curves_a.points_num() != curves_b.points_num() || curves_a.offsets() != curves_b.offsets())
|
||||
{
|
||||
return false;
|
||||
amsaid1989 marked this conversation as resolved
Outdated
Pratik Borhade
commented
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:
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;
}
```
Abdelrahman Said
commented
Fixed it. Thank you very much for catching it Fixed it. Thank you very much for catching it
|
||||
}
|
||||
|
||||
const AttributeAccessor attributes_a = curves_a.attributes();
|
||||
const AttributeAccessor attributes_b = curves_b.attributes();
|
||||
amsaid1989 marked this conversation as resolved
Outdated
Pratik Borhade
commented
not sure if this is needed. You could just call not sure if this is needed. You could just call `std::equal()` in current lamda too, no?
I might be misunderstanding something
Abdelrahman Said
commented
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 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)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
amsaid1989 marked this conversation as resolved
Outdated
Iliya Katushenock
commented
```Cpp
if (attributes_varrays_are_equal(attrs_a, attrs_b)) {
return true;
}
```
Abdelrahman Said
commented
Done Done
|
||||
if (attributes_varrays_span_data_equal(attrs_a, attrs_b)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool attributes_are_equal = true;
|
||||
|
||||
amsaid1989 marked this conversation as resolved
Outdated
Iliya Katushenock
commented
`attrs_equal` -> `attributes_are_equal`
Abdelrahman Said
commented
Renamed the variable Renamed the variable
|
||||
attribute_math::convert_to_static_type(attrs_a.varray.type(), [&](auto dummy) {
|
||||
using T = decltype(dummy);
|
||||
amsaid1989 marked this conversation as resolved
Outdated
Iliya Katushenock
commented
Right now this is incorrect. 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.
Abdelrahman Said
commented
@mod_moder I have addressed your comments about the varrays check. Renamed
Then, I added
These 2 functions are then used like this:
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?
Abdelrahman Said
commented
Regarding this, I would like to confirm that I understand you correctly. You want to change the lambda in
And change the
> 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());
}
```
Iliya Katushenock
commented
Order of
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.
Abdelrahman Said
commented
Great.. Thank you very much. Updated the PR. Great.. Thank you very much. Updated the PR.
|
||||
|
||||
amsaid1989 marked this conversation as resolved
Outdated
Pratik Borhade
commented
`get_editable_drawing_at` accepts layer reference. So pass `*layer`
|
||||
const VArray attributes_a = attrs_a.varray.typed<T>();
|
||||
amsaid1989 marked this conversation as resolved
Outdated
Pratik Borhade
commented
same as above same as above
Abdelrahman Said
commented
Updated all uses of Updated all uses of `get_editable_drawing_at` to pass a `Layer &` instead of `Layer *`
|
||||
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;
|
||||
}
|
||||
|
||||
static int frame_clean_duplicate_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
using namespace blender::bke::greasepencil;
|
||||
amsaid1989 marked this conversation as resolved
Outdated
Pratik Borhade
commented
wouldn't wouldn't `get_editable_drawing_at` work here?
Abdelrahman Said
commented
You're right.. You're right.. `get_editable_drawing_at` works. Updated it
|
||||
Object *object = CTX_data_active_object(C);
|
||||
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
|
||||
amsaid1989 marked this conversation as resolved
Outdated
Pratik Borhade
commented
same as above. Also names can be improved. eg: same as above.
Also names can be improved. eg: `curves` and `curves_next`
Abdelrahman Said
commented
Switched to Switched to `get_editable_drawing_at` and renamed the variables
|
||||
const bool selected = RNA_boolean_get(op->ptr, "selected");
|
||||
|
||||
bool changed = false;
|
||||
|
||||
for (Layer *layer : grease_pencil.layers_for_write()) {
|
||||
if (!layer->is_editable()) {
|
||||
amsaid1989 marked this conversation as resolved
Outdated
Iliya Katushenock
commented
`Vector<FramesMapKey> frames_to_delete;`
Abdelrahman Said
commented
Done Done
|
||||
continue;
|
||||
}
|
||||
|
||||
const Span<FramesMapKey> &keys = layer->sorted_keys();
|
||||
Vector<FramesMapKey> frames_to_delete;
|
||||
mod_moder marked this conversation as resolved
Outdated
Iliya Katushenock
commented
.... ? `for (size_t i = 0; i < keys.size(); ++i) {` and `if (i + 1 >= keys.size()) {`
->
`const int i : keys.index_range().drop_front(1)`
.... ?
Abdelrahman Said
commented
I needed I needed `const int i : keys.index_range().drop_back(1)` instead of `const int i : keys.index_range().drop_front(1)`
|
||||
|
||||
for (const int i : keys.index_range().drop_back(1)) {
|
||||
const FramesMapKey current = keys[i];
|
||||
PratikPB2123 marked this conversation as resolved
Outdated
Pratik Borhade
commented
`grease_pencil.remove_frames()`
Abdelrahman Said
commented
When I try to use this
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 FramesMapKey next = keys[i + 1];
|
||||
|
||||
amsaid1989 marked this conversation as resolved
Outdated
Iliya Katushenock
commented
Can be const? Can be const?
Abdelrahman Said
commented
Changed to Changed to `const`
|
||||
GreasePencilFrame frame = layer->frames().lookup(current);
|
||||
|
||||
if (selected && !frame.is_selected()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Drawing *drawing = grease_pencil.get_editable_drawing_at(*layer, current);
|
||||
Drawing *drawing_next = grease_pencil.get_editable_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)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
frames_to_delete.append(next);
|
||||
}
|
||||
|
||||
for (const FramesMapKey frame : frames_to_delete) {
|
||||
layer->remove_frame(frame);
|
||||
}
|
||||
|
||||
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;
|
||||
|
@ -432,6 +587,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;
|
||||
|
@ -632,4 +808,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);
|
||||
}
|
||||
|
|
You have check size few lines above, right?
Removed this extra size check