GPv3: Reimplement how customdata is updated when layers are re-ordered #113962
No reviewers
Labels
No Label
Interest
Alembic
Interest
Animation & Rigging
Interest
Asset Browser
Interest
Asset Browser Project Overview
Interest
Audio
Interest
Automated Testing
Interest
Blender Asset Bundle
Interest
BlendFile
Interest
Collada
Interest
Compatibility
Interest
Compositing
Interest
Core
Interest
Cycles
Interest
Dependency Graph
Interest
Development Management
Interest
EEVEE
Interest
EEVEE & Viewport
Interest
Freestyle
Interest
Geometry Nodes
Interest
Grease Pencil
Interest
ID Management
Interest
Images & Movies
Interest
Import Export
Interest
Line Art
Interest
Masking
Interest
Metal
Interest
Modeling
Interest
Modifiers
Interest
Motion Tracking
Interest
Nodes & Physics
Interest
OpenGL
Interest
Overlay
Interest
Overrides
Interest
Performance
Interest
Physics
Interest
Pipeline, Assets & IO
Interest
Platforms, Builds & Tests
Interest
Python API
Interest
Render & Cycles
Interest
Render Pipeline
Interest
Sculpt, Paint & Texture
Interest
Text Editor
Interest
Translations
Interest
Triaging
Interest
Undo
Interest
USD
Interest
User Interface
Interest
UV Editing
Interest
VFX & Video
Interest
Video Sequencer
Interest
Virtual Reality
Interest
Vulkan
Interest
Wayland
Interest
Workbench
Interest: X11
Legacy
Blender 2.8 Project
Legacy
Milestone 1: Basic, Local Asset Browser
Legacy
OpenGL Error
Meta
Good First Issue
Meta
Papercut
Meta
Retrospective
Meta
Security
Module
Animation & Rigging
Module
Core
Module
Development Management
Module
EEVEE & Viewport
Module
Grease Pencil
Module
Modeling
Module
Nodes & Physics
Module
Pipeline, Assets & IO
Module
Platforms, Builds & Tests
Module
Python API
Module
Render & Cycles
Module
Sculpt, Paint & Texture
Module
Triaging
Module
User Interface
Module
VFX & Video
Platform
FreeBSD
Platform
Linux
Platform
macOS
Platform
Windows
Priority
High
Priority
Low
Priority
Normal
Priority
Unbreak Now!
Status
Archived
Status
Confirmed
Status
Duplicate
Status
Needs Info from Developers
Status
Needs Information from User
Status
Needs Triage
Status
Resolved
Type
Bug
Type
Design
Type
Known Issue
Type
Patch
Type
Report
Type
To Do
No Milestone
No project
No Assignees
2 Participants
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: blender/blender#113962
Loading…
Reference in New Issue
No description provided.
Delete Branch "Douglas-Paul/blender:gpv3-customdata-reordering-alternative-approach"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Instead of calculating the expected insertion index for a customdata entry being moved, this implementation just records the initial layer indices so that it can compare them with the final indices to determine how the customdata entries need to be re-arranged.
This PR supersedes #113874. If this one is merged, that one should be closed.
The first commit fixes a bug where
grow_or_init_customdata()
was calculating on an insertion index when it should not have been. SinceGreasePencil::add_layer()
always adds the new layer at the top,grow_or_init_custom_data()
should always add the new customdata entry at the end.The second commit is the focus of this PR. With the above bug fixed, the only thing that the insertion index was used for was to prepare a mapping from the old layer indices to the new ones (to be passed to
reorder_customdata()
). This implementation instead builds that map based on a "snapshot" of the layer indices taken before the layers are re-ordered. That eliminates the need to calculate an insertion index at all, which allows a lot of logic to be deleted.Another benefit of this approach is that it works just as well if multiple layers are moved at the same time—it should be able to support the movement of entire layer groups.
Bah, I can't figure out how to do comments on multiple lines of code in Gitea, so I'll just ask my questions here:
grow_customdata()
be simplified even further now that it only ever inserts at the end?reorder_layer_data()
, is it too risky to use pointers as map keys? I don't know how likely or unlikely it would be for an an operation that changes the order of the layers to move them around in memory. If that's a concern, all that's really needed is a unique identifier each layer before & after the layer order is changed.Thanks for working on this, makes the code so much cleaner :)
I think there is one bug in your code right now that needs fixing. And I left some smaller cleanup comments.
@ -2113,3 +2067,4 @@
static void grow_or_init_customdata(GreasePencil *grease_pencil)
{
using namespace blender;
const Span<const bke::greasepencil::Layer *> layers = grease_pencil->layers();
I think with what you're going for, this
if
/else
can just be replaced byCustomData_realloc(&grease_pencil->layers_data, layers.size(), layers.size() + 1);
and the static functiongrow_customdata
is no longer needed.Great, thanks. With this change
grow_or_init_customdata()
is now just 2 simple lines, so maybe not worth its weight as a function? Not sure where the Blender team lands on the tradeoffs on that sort of thing. I've left it for now.Yea and
grow_or_init_customdata
isn't used in many places either, so we can remove that one now too :)I'm happy to see that we can remove a lot of code complexity here 👍
@ -2128,3 +2082,3 @@
using namespace blender;
std::string unique_name = unique_layer_name(*this, name);
grow_or_init_customdata(this, parent_group);
grow_or_init_customdata(this);
This now grows the custom data by one, but the layer might not be at the end. It's added to the end of the
parent_group
which doesn't have to be the root. So I assume that there needs to be somereorder_custom_data
call here as well that shifts things in the right place.Ack, yes, very glad you caught this because I had completely forgotten to deal with it.
Most places that add a layer were adding the layer to the root group anyway, so I added some signatures of
add_layer()
that implicitly add the layer to the root group (and append the custom data at the end). And I made these existing signatures ofadd_layer()
(that accept a group) into basically convenience methods: They just add the layer to the root group and then move it into the requested group viamove_node_into()
.Right, I just read through the changes and I like the way you resolved this. Just calling
move_node_into
for the convinience function makes a lot of sense.@ -2184,2 +2128,2 @@
array_utils::fill_index_range(reorder_indices.slice(IndexRange(reorder_from + 1, dist)),
reorder_from);
/* Stash the initial layer order that we can refer back to later */
auto old_layer_index_by_layer = Map<const bke::greasepencil::Layer *, int>();
We like to avoid
auto
when possible. This can just beMap<const bke::greasepencil::Layer *, int> old_layer_index_by_layer;
Good to know, thanks. You might consider adding that to the style guide, especially since the default linter settings in some IDEs (or at least mine) nudge towards using auto wherever possible.
@ -2204,0 +2140,4 @@
/* Compose the mapping from old layer indices to new layer indices */
Array<int> new_by_old_map(layers.size());
int i_new = 0;
for (const bke::greasepencil::Layer *layer : layers) {
Indeed! An embarrassing oversight
It's a bit weird but you can click the plus on a line, then "Start Review", add all your comments and then in the upper right where it says "Review", just press the "Comment" button.
Yes, I left a comment in the code about it.
Because the layer tree is implemented with links and linked-lists, I don't think this is a problem. Reordering should only ever change the link pointers and shouldn't reallocate anything. Using the pointers as keys is ok.
@blender-bot build
@ -2121,2 +2073,2 @@
grow_customdata(grease_pencil->layers_data, insertion_index, layers.size());
}
blender::bke::greasepencil::Layer &new_layer = add_layer(name);
move_node_into(new_layer.as_node(), parent_group);
If someone happened to call this method and pass in the root group as
parent_group
, then this call tomove_node_into()
is just wasted effort. I could add a check to avoid that, but all we'd really be doing is optimizing a case where the calling code was using the wrong method signature (because they should instead use the signature that implicitly adds to the root group).Yea optimizing a case that the function shouldn't be used for in the first place seems like a waste. Better keep it simple.
@ -2205,0 +2147,4 @@
BLI_assert(old_layer_index_by_layer.is_empty());
/* Use the mapping to re-order the custom data */
reorder_customdata(grease_pencil.layers_data, new_by_old_map);
It's possible that the new layer order is exactly the same as the old layer order, in which case we could skip this call to
reorder_customdata()
. It's tempting to detect that case and skip this call as an optimization, but I'm not sure there's any practical benefit to doing that? It would arguably arguably just "complexity creep" to optimize things that don't really need to be optimized.Agreed. We might come back to this if it is an issue for some reason, but I can't see why it would be a bottleneck right now.
Code looks good now :) I'll test this locally once more, but I think we can merge it after that goes well.