diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.cc b/source/blender/editors/sculpt_paint/sculpt_undo.cc index 33212f85e6e..cb54db7db82 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_undo.cc @@ -321,8 +321,12 @@ template Array decompress(const Span src) struct PositionUndoStorage : NonMovable { Vector> nodes_to_compress; + bool multires_undo; Array> compressed_indices; + + /* As undo and redo happen, the data in these arrays is swapped (an undo step becomes a redo + * step, and vice versa). */ Array> compressed_positions; Array unique_verts_nums; @@ -335,15 +339,18 @@ struct PositionUndoStorage : NonMovable { explicit PositionUndoStorage(StepData &step_data) : nodes_to_compress(std::move(step_data.nodes)), owner_step_data(&step_data) { - unique_verts_nums.reinitialize(nodes_to_compress.size()); - for (const int i : nodes_to_compress.index_range()) { - unique_verts_nums[i] = nodes_to_compress[i]->unique_verts_num; + this->multires_undo = step_data.grids.grids_num != 0; + if (!multires_undo) { + this->unique_verts_nums.reinitialize(this->nodes_to_compress.size()); + for (const int i : this->nodes_to_compress.index_range()) { + this->unique_verts_nums[i] = this->nodes_to_compress[i]->unique_verts_num; + } } - compression_task_pool = BLI_task_pool_create_background(this, TASK_PRIORITY_LOW); - compression_started = true; + this->compression_task_pool = BLI_task_pool_create_background(this, TASK_PRIORITY_LOW); + this->compression_started = true; - BLI_task_pool_push(compression_task_pool, compress_fn, this, false, nullptr); + BLI_task_pool_push(this->compression_task_pool, compress_fn, this, false, nullptr); } ~PositionUndoStorage() @@ -375,10 +382,10 @@ struct PositionUndoStorage : NonMovable { threading::isolate_task([&]() { threading::parallel_for(IndexRange(nodes_num), 1, [&](const IndexRange range) { for (const int i : range) { - Array verts = zstd::compress(nodes[i]->vert_indices); - Array positions = zstd::compress(nodes[i]->position); - new (&compressed_indices[i]) Array(std::move(verts)); - new (&compressed_data[i]) Array(std::move(positions)); + const Span indices = data->multires_undo ? nodes[i]->grids : nodes[i]->vert_indices; + const Span positions = nodes[i]->position; + new (&compressed_indices[i]) Array(zstd::compress(indices)); + new (&compressed_data[i]) Array(zstd::compress(positions)); nodes[i].reset(); } }); @@ -562,7 +569,6 @@ static void restore_position_mesh(Object &object, modified_verts.fill_indices(verts, true); - undo_data.compressed_indices[i] = zstd::compress(indices); undo_data.compressed_positions[i] = zstd::compress(node_positions); } }); @@ -570,21 +576,30 @@ static void restore_position_mesh(Object &object, static void restore_position_grids(const MutableSpan positions, const CCGKey &key, - Node &unode, + PositionUndoStorage &undo_data, const MutableSpan modified_grids) { - const Span grids = unode.grids; - const MutableSpan undo_position = unode.position; + const int nodes_num = undo_data.compressed_indices.size(); - for (const int i : grids.index_range()) { - MutableSpan data = positions.slice(bke::ccg::grid_range(key, grids[i])); - MutableSpan undo_data = undo_position.slice(bke::ccg::grid_range(key, i)); - for (const int offset : data.index_range()) { - std::swap(data[offset], undo_data[offset]); + threading::parallel_for(IndexRange(nodes_num), 1, [&](const IndexRange range) { + for (const int i : range) { + Array grids = zstd::decompress(undo_data.compressed_indices[i]); + Array node_positions = zstd::decompress(undo_data.compressed_positions[i]); + + for (const int i : grids.index_range()) { + MutableSpan data = positions.slice(bke::ccg::grid_range(key, grids[i])); + MutableSpan undo_data = node_positions.as_mutable_span().slice( + bke::ccg::grid_range(key, i)); + for (const int offset : data.index_range()) { + std::swap(data[offset], undo_data[offset]); + } + } + + modified_grids.fill_indices(grids.as_span(), true); + + undo_data.compressed_positions[i] = zstd::compress(node_positions); } - } - - modified_grids.fill_indices(grids, true); + }); } static void restore_vert_visibility_mesh(Object &object, @@ -1033,9 +1048,9 @@ static void restore_list(bContext *C, Depsgraph *depsgraph, StepData &step_data) const CCGKey key = BKE_subdiv_ccg_key_top_level(subdiv_ccg); Array modified_grids(subdiv_ccg.grids_num, false); - for (std::unique_ptr &unode : step_data.nodes) { - restore_position_grids(subdiv_ccg.positions, key, *unode, modified_grids); - } + restore_position_grids( + subdiv_ccg.positions, key, *step_data.position_step_storage, modified_grids); + const IndexMask changed_nodes = IndexMask::from_predicate( node_mask, GrainSize(1), memory, [&](const int i) { return indices_contain_true(modified_grids, nodes[i].grids()); @@ -1964,7 +1979,7 @@ void push_end_ex(Object &ob, const bool use_nested_undo) * just one positions array that has a different semantic meaning depending on whether there are * deform modifiers. */ - if (step_data->type == Type::Position && !use_multires_undo(*step_data, *ob.sculpt)) { + if (step_data->type == Type::Position) { step_data->position_step_storage = std::make_unique(*step_data); } else {