Grease Pencil: Initialize runtime drawing user counts on read
This adds a function `GreasePencil::count_frame_users_for_drawings()` that computes the drawing user counts from the layer frames. This is used to correctly initialize the runtime drawing user counts when reading the ID. Note that we don't expose any functionality to instance drawings currently. This change is also in preparation for when this will be possible. Also adds a function `GreasePencil::validate_drawing_user_counts()` that compares the actual user counts with the runtime user counts that are stored. Only runs these checks in debug builds. Pull Request: https://projects.blender.org/blender/blender/pulls/141458
This commit is contained in:
@@ -76,6 +76,12 @@ class DrawingRuntime {
|
||||
* and remove a drawing if it has zero users.
|
||||
*/
|
||||
mutable std::atomic<int> user_count = 1;
|
||||
|
||||
/**
|
||||
* Ensures that the drawing is not deleted and can be used temporarily (e.g. by the transform
|
||||
* code).
|
||||
*/
|
||||
mutable bool fake_user = false;
|
||||
};
|
||||
|
||||
class Drawing : public ::GreasePencilDrawing {
|
||||
|
||||
@@ -173,6 +173,21 @@ static void grease_pencil_set_runtime_visibilities(ID &id_dst, GreasePencil &gre
|
||||
}
|
||||
}
|
||||
|
||||
static void grease_pencil_initialize_drawing_user_counts_after_read(GreasePencil &grease_pencil)
|
||||
{
|
||||
using namespace blender;
|
||||
using namespace blender::bke::greasepencil;
|
||||
const Array<int> user_counts = grease_pencil.count_frame_users_for_drawings();
|
||||
BLI_assert(user_counts.size() == grease_pencil.drawings().size());
|
||||
for (const int drawing_i : grease_pencil.drawings().index_range()) {
|
||||
GreasePencilDrawingBase *drawing_base = grease_pencil.drawing(drawing_i);
|
||||
if (drawing_base->type != GP_DRAWING_REFERENCE) {
|
||||
Drawing &drawing = reinterpret_cast<GreasePencilDrawing *>(drawing_base)->wrap();
|
||||
drawing.runtime->user_count.store(user_counts[drawing_i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void grease_pencil_copy_data(Main * /*bmain*/,
|
||||
std::optional<Library *> /*owner_library*/,
|
||||
ID *id_dst,
|
||||
@@ -300,6 +315,8 @@ static void grease_pencil_blend_read_data(BlendDataReader *reader, ID *id)
|
||||
read_drawing_array(*grease_pencil, reader);
|
||||
/* Read layer tree. */
|
||||
read_layer_tree(*grease_pencil, reader);
|
||||
/* Initialize drawing user counts */
|
||||
grease_pencil_initialize_drawing_user_counts_after_read(*grease_pencil);
|
||||
|
||||
CustomData_blend_read(
|
||||
reader, &grease_pencil->layers_data_legacy, grease_pencil->layers().size());
|
||||
@@ -3029,6 +3046,9 @@ bool GreasePencil::insert_duplicate_frame(blender::bke::greasepencil::Layer &lay
|
||||
layer.remove_frame(dst_frame_number);
|
||||
return false;
|
||||
}
|
||||
#ifndef NDEBUG
|
||||
this->validate_drawing_user_counts();
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -3065,6 +3085,11 @@ bool GreasePencil::remove_frames(blender::bke::greasepencil::Layer &layer,
|
||||
this->remove_drawings_with_no_users();
|
||||
return true;
|
||||
}
|
||||
#ifndef NDEBUG
|
||||
else {
|
||||
this->validate_drawing_user_counts();
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -3135,6 +3160,10 @@ void GreasePencil::remove_drawings_with_no_users()
|
||||
using namespace blender;
|
||||
using namespace blender::bke::greasepencil;
|
||||
|
||||
#ifndef NDEBUG
|
||||
this->validate_drawing_user_counts();
|
||||
#endif
|
||||
|
||||
/* Compress the drawings array by finding unused drawings.
|
||||
* In every step two indices are found:
|
||||
* - The next unused drawing from the start
|
||||
@@ -3156,7 +3185,7 @@ void GreasePencil::remove_drawings_with_no_users()
|
||||
return false;
|
||||
}
|
||||
GreasePencilDrawing *drawing = reinterpret_cast<GreasePencilDrawing *>(drawing_base);
|
||||
return drawing->wrap().has_users();
|
||||
return drawing->wrap().has_users() || drawing->runtime->fake_user;
|
||||
};
|
||||
|
||||
/* Index map to remap drawing indices in frame data.
|
||||
@@ -3239,12 +3268,16 @@ void GreasePencil::remove_drawings_with_no_users()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
this->validate_drawing_user_counts();
|
||||
#endif
|
||||
}
|
||||
|
||||
void GreasePencil::update_drawing_users_for_layer(const blender::bke::greasepencil::Layer &layer)
|
||||
{
|
||||
using namespace blender;
|
||||
for (auto [key, value] : layer.frames().items()) {
|
||||
for (const auto &[key, value] : layer.frames().items()) {
|
||||
BLI_assert(this->drawings().index_range().contains(value.drawing_index));
|
||||
GreasePencilDrawingBase *drawing_base = this->drawing(value.drawing_index);
|
||||
if (drawing_base->type != GP_DRAWING) {
|
||||
@@ -3256,6 +3289,10 @@ void GreasePencil::update_drawing_users_for_layer(const blender::bke::greasepenc
|
||||
drawing.add_user();
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
this->validate_drawing_user_counts();
|
||||
#endif
|
||||
}
|
||||
|
||||
void GreasePencil::move_frames(blender::bke::greasepencil::Layer &layer,
|
||||
@@ -4260,6 +4297,36 @@ void GreasePencil::print_layer_tree()
|
||||
this->root_group().print_nodes("Layer Tree:");
|
||||
}
|
||||
|
||||
blender::Array<int> GreasePencil::count_frame_users_for_drawings() const
|
||||
{
|
||||
using namespace blender;
|
||||
using namespace blender::bke::greasepencil;
|
||||
Array<int> user_counts(this->drawings().size(), 0);
|
||||
for (const Layer *layer : this->layers()) {
|
||||
for (const auto &[frame, value] : layer->frames().items()) {
|
||||
BLI_assert(this->drawings().index_range().contains(value.drawing_index));
|
||||
user_counts[value.drawing_index]++;
|
||||
}
|
||||
}
|
||||
return user_counts;
|
||||
}
|
||||
|
||||
void GreasePencil::validate_drawing_user_counts()
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
using namespace blender::bke::greasepencil;
|
||||
blender::Array<int> actual_user_counts = this->count_frame_users_for_drawings();
|
||||
for (const int drawing_i : this->drawings().index_range()) {
|
||||
const GreasePencilDrawingBase *drawing_base = this->drawing(drawing_i);
|
||||
if (drawing_base->type != GP_DRAWING_REFERENCE) {
|
||||
const Drawing &drawing = reinterpret_cast<const GreasePencilDrawing *>(drawing_base)->wrap();
|
||||
/* Ignore `fake_user` flag. */
|
||||
BLI_assert(drawing.user_count() == actual_user_counts[drawing_i]);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
blender::bke::AttributeAccessor GreasePencil::attributes() const
|
||||
{
|
||||
return blender::bke::AttributeAccessor(
|
||||
|
||||
@@ -53,9 +53,9 @@ static bool is_td2d_int(TransData2D *td2d)
|
||||
/** \name Grease Pencil Transform helpers
|
||||
* \{ */
|
||||
|
||||
/* Add a user to ensure drawings are not deleted during transform when a frame is overwritten
|
||||
/* Add a fake user to ensure drawings are not deleted during transform when a frame is overwritten
|
||||
* temporarily. The drawing_index of any existing frame will also remain valid. */
|
||||
static void grease_pencil_transdata_add_drawing_users(const GreasePencil &grease_pencil)
|
||||
static void grease_pencil_transdata_add_fake_drawing_users(const GreasePencil &grease_pencil)
|
||||
{
|
||||
using namespace bke::greasepencil;
|
||||
|
||||
@@ -66,13 +66,13 @@ static void grease_pencil_transdata_add_drawing_users(const GreasePencil &grease
|
||||
}
|
||||
|
||||
const Drawing &drawing = reinterpret_cast<const GreasePencilDrawing *>(drawing_base)->wrap();
|
||||
drawing.add_user();
|
||||
drawing.runtime->fake_user = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Remove users from drawings after frame data has been restored. After this drawing data can be
|
||||
* freed and drawing indices may become invalid. */
|
||||
static void grease_pencil_transdata_remove_drawing_users(const GreasePencil &grease_pencil)
|
||||
/* Remove fake users from drawings after frame data has been restored. After this drawing data can
|
||||
* be freed and drawing indices may become invalid. */
|
||||
static void grease_pencil_transdata_remove_fake_drawing_users(const GreasePencil &grease_pencil)
|
||||
{
|
||||
using namespace bke::greasepencil;
|
||||
|
||||
@@ -83,7 +83,7 @@ static void grease_pencil_transdata_remove_drawing_users(const GreasePencil &gre
|
||||
}
|
||||
|
||||
const Drawing &drawing = reinterpret_cast<const GreasePencilDrawing *>(drawing_base)->wrap();
|
||||
drawing.remove_user();
|
||||
drawing.runtime->fake_user = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,10 +99,10 @@ static bool grease_pencil_layer_initialize_trans_data(const GreasePencil &grease
|
||||
return false;
|
||||
}
|
||||
|
||||
/* "Freeze" drawing indices by adding a user to each drawing. This ensures the draw_index in
|
||||
* frame data remains valid and no data is lost if the drawing is temporarily unused during
|
||||
/* "Freeze" drawing indices by adding a fake user to each drawing. This ensures the drawing_index
|
||||
* in frame data remains valid and no data is lost if the drawing is temporarily unused during
|
||||
* transform. */
|
||||
grease_pencil_transdata_add_drawing_users(grease_pencil);
|
||||
grease_pencil_transdata_add_fake_drawing_users(grease_pencil);
|
||||
|
||||
/* Initialize the transformation data structure, by storing in separate maps frames that will
|
||||
* remain static during the transformation, and frames that are affected by the
|
||||
@@ -247,7 +247,7 @@ static bool grease_pencil_layer_apply_trans_data(GreasePencil &grease_pencil,
|
||||
}
|
||||
|
||||
/* All frame data is updated, safe to remove the fake user and remove unused drawings. */
|
||||
grease_pencil_transdata_remove_drawing_users(grease_pencil);
|
||||
grease_pencil_transdata_remove_fake_drawing_users(grease_pencil);
|
||||
grease_pencil.remove_drawings_with_no_users();
|
||||
|
||||
/* Clear the frames copy. */
|
||||
|
||||
@@ -743,7 +743,14 @@ typedef struct GreasePencil {
|
||||
|
||||
void count_memory(blender::MemoryCounter &memory) const;
|
||||
|
||||
/**
|
||||
* Compute the user counts of the drawings by iterating through the keyframes of all the layers
|
||||
* and counting the number of references to each drawing.
|
||||
*/
|
||||
blender::Array<int> count_frame_users_for_drawings() const;
|
||||
|
||||
/* For debugging purposes. */
|
||||
void print_layer_tree();
|
||||
void validate_drawing_user_counts();
|
||||
#endif
|
||||
} GreasePencil;
|
||||
|
||||
Reference in New Issue
Block a user