Deleting the last Annotations layer

Hello,

I’m new to Blender coding and currently I’m fixing an issue #112683 I’ve raised myself.

Apparently, I’ve managed to fix this, but I thought I’d ask here about some details first before creating the pull request.

GPencil data block bGPdata is created on the first stroke, as well as the default layer ‘Note’. After deleting that single layer, bGPdata isn’t deleted and is accessed through Tool Options header drawing code, producing those warnings in the console. I’ve fixed it by freeing bGPdata when the last annotation layer is deleted.

What I’m wondering about is the following. bGPdata is allocated by BKE_libblock_alloc(), but there is no BKE_libblock_free(), even though it is mentioned somewhere in a comment in blender\blenkernel\intern\lib_id.cc. So instead I used BKE_libblock_management_main_remove() (which btw hasn’t been used anywhere so far!) followed by MEM_freeN().

This is the fix which I put at the end of the gpencil_layer_remove_exec():

/* If we removed the last layer for annotations, free the current GPencil block. */
if (is_annotation && gpd->layers.first == nullptr) {
  BKE_gpencil_free_data(gpd, true);

  Main *bmain = CTX_data_main(C);
  BKE_libblock_management_main_remove(bmain, gpd);

  MEM_freeN(gpd);

  bGPdata **gpd_ptr = ED_annotation_data_get_pointers(C, nullptr);
  *gpd_ptr = nullptr;
}

This seems to work just fine, but I’d appreciete some advice on this.

Regards,
Milan

Datablocks in Blender have a user count and can be linked in multiple places. They are often not immediately freed, rather the user count is decreased and they are deleted later.

I guess in this case the grease pencil data is typically only used in one place and it may make sense to delete it. But it still would need to be checked to make sure there are no other users. BKE_id_free_us would be the appropriate call for that.

1 Like

Thanks for your answer Brecht!

I tried BKE_id_free_us and it seems to work, but now I get an error message:

ERROR (bke.lib_id): D:\Work\Projects\blender-git\blender\source\blender\blenkernel\intern\lib_id.cc:337 id_us_min: ID user decrement error: GDAnnotations (from '[Main]'): 0 <= 0

It decrements the user count twice. Initially, id->us is 1. First decrement happens at the start of BKE_id_free_us:

>	blender.exe!id_us_min(ID * id) Line 328	C++
 	blender.exe!BKE_id_free_us(Main * bmain, void * idv) Line 210	C++
 	blender.exe!gpencil_layer_remove_exec(bContext * C, wmOperator * op) Line 355	C++

And then later for the second time:

>	blender.exe!id_us_min(ID * id) Line 328	C++
 	blender.exe!foreach_libblock_remap_callback_apply(ID * id_owner, ID * id_self, ID * * id_ptr, IDRemap * id_remap_data, const IDRemapper * mappings, const IDRemapperApplyOptions id_remapper_options, const int cb_flag, const bool is_indirect, const bool violates_never_null) Line 159	C++
 	blender.exe!foreach_libblock_remap_callback(LibraryIDLinkCallbackData * cb_data) Line 289	C++
 	blender.exe!BKE_lib_query_foreachid_process(LibraryForeachIDData * data, ID * * id_pp, int cb_flag) Line 95	C++
 	blender.exe!scene_foreach_id(ID * id, LibraryForeachIDData * data) Line 866	C++
 	blender.exe!library_foreach_ID_link(Main * bmain, ID * owner_id, ID * id, int(*)(LibraryIDLinkCallbackData *) callback, void * user_data, int flag, LibraryForeachIDData * inherit_data) Line 367	C++
 	blender.exe!BKE_library_foreach_ID_link(Main * bmain, ID * id, int(*)(LibraryIDLinkCallbackData *) callback, void * user_data, int flag) Line 385	C++
 	blender.exe!libblock_remap_data(Main * bmain, ID * id, eIDRemapType remap_type, IDRemapper * id_remapper, const int remap_flags) Line 539	C++
 	blender.exe!BKE_libblock_remap_multiple_locked(Main * bmain, IDRemapper * mappings, const int remap_flags) Line 652	C++
 	blender.exe!BKE_libblock_remap_locked(Main * bmain, void * old_idv, void * new_idv, int remap_flags) Line 690	C++
 	blender.exe!BKE_libblock_unlink(Main * bmain, void * idv, bool do_flag_never_null, bool do_skip_indirect) Line 723	C++
 	blender.exe!BKE_id_free_us(Main * bmain, void * idv) Line 217	C++
 	blender.exe!gpencil_layer_remove_exec(bContext * C, wmOperator * op) Line 355	C++

I see that it logs the error on non-deprecated ID types only:

/* Do not assert on deprecated ID types, we cannot really ensure that their ID
 * reference-counting is valid. */

Are by any chance “GDAnnotations” similar to deprecated types in that regard?

Set scene->gpd = nullptr before calling BKE_id_free_us.

That fixed it. Thanks Brecht!