diff --git a/source/blender/blenlib/BLI_array_utils.hh b/source/blender/blenlib/BLI_array_utils.hh index fb96f0a39d9..af138b822b7 100644 --- a/source/blender/blenlib/BLI_array_utils.hh +++ b/source/blender/blenlib/BLI_array_utils.hh @@ -222,6 +222,7 @@ void invert_booleans(MutableSpan span); void invert_booleans(MutableSpan span, const IndexMask &mask); int64_t count_booleans(const VArray &varray); +int64_t count_booleans(const VArray &varray, const IndexMask &mask); enum class BooleanMix { None, diff --git a/source/blender/blenlib/intern/array_utils.cc b/source/blender/blenlib/intern/array_utils.cc index 1de31e2d4fd..f607472a819 100644 --- a/source/blender/blenlib/intern/array_utils.cc +++ b/source/blender/blenlib/intern/array_utils.cc @@ -141,40 +141,59 @@ BooleanMix booleans_mix_calc(const VArray &varray, const IndexRange range_ [&](BooleanMix a, BooleanMix b) { return (a == b) ? a : BooleanMix::Mixed; }); } -int64_t count_booleans(const VArray &varray) +int64_t count_booleans(const VArray &varray, const IndexMask &mask) { - if (varray.is_empty()) { + if (varray.is_empty() || mask.is_empty()) { return 0; } - const CommonVArrayInfo info = varray.common_info(); - if (info.type == CommonVArrayInfo::Type::Single) { - return *static_cast(info.data) ? varray.size() : 0; - } - if (info.type == CommonVArrayInfo::Type::Span) { - const Span span(static_cast(info.data), varray.size()); + /* Check if mask is full. */ + if (varray.size() == mask.size()) { + const CommonVArrayInfo info = varray.common_info(); + if (info.type == CommonVArrayInfo::Type::Single) { + return *static_cast(info.data) ? varray.size() : 0; + } + if (info.type == CommonVArrayInfo::Type::Span) { + const Span span(static_cast(info.data), varray.size()); + return threading::parallel_reduce( + varray.index_range(), + 4096, + 0, + [&](const IndexRange range, const int64_t init) { + const Span slice = span.slice(range); + return init + std::count(slice.begin(), slice.end(), true); + }, + std::plus()); + } return threading::parallel_reduce( varray.index_range(), - 4096, + 2048, 0, [&](const IndexRange range, const int64_t init) { - const Span slice = span.slice(range); - return init + std::count(slice.begin(), slice.end(), true); + int64_t value = init; + /* Alternatively, this could use #materialize to retrieve many values at once. */ + for (const int64_t i : range) { + value += int64_t(varray[i]); + } + return value; }, std::plus()); } - return threading::parallel_reduce( - varray.index_range(), - 2048, - 0, - [&](const IndexRange range, const int64_t init) { - int64_t value = init; - /* Alternatively, this could use #materialize to retrieve many values at once. */ - for (const int64_t i : range) { - value += int64_t(varray[i]); - } - return value; - }, - std::plus()); + const CommonVArrayInfo info = varray.common_info(); + if (info.type == CommonVArrayInfo::Type::Single) { + return *static_cast(info.data) ? mask.size() : 0; + } + int64_t value = 0; + mask.foreach_segment([&](const IndexMaskSegment segment) { + for (const int64_t i : segment) { + value += int64_t(varray[i]); + } + }); + return value; +} + +int64_t count_booleans(const VArray &varray) +{ + return count_booleans(varray, IndexMask(varray.size())); } } // namespace blender::array_utils diff --git a/source/blender/draw/intern/draw_cache_impl_grease_pencil.cc b/source/blender/draw/intern/draw_cache_impl_grease_pencil.cc index def2b447974..0034c944f67 100644 --- a/source/blender/draw/intern/draw_cache_impl_grease_pencil.cc +++ b/source/blender/draw/intern/draw_cache_impl_grease_pencil.cc @@ -198,7 +198,9 @@ BLI_INLINE int32_t pack_rotation_aspect_hardness(float rot, float asp, float har return packed; } -static void grease_pencil_edit_batch_ensure(const GreasePencil &grease_pencil, const Scene &scene) +static void grease_pencil_edit_batch_ensure(Object &object, + const GreasePencil &grease_pencil, + const Scene &scene) { using namespace blender::bke::greasepencil; BLI_assert(grease_pencil.runtime != nullptr); @@ -249,6 +251,10 @@ static void grease_pencil_edit_batch_ensure(const GreasePencil &grease_pencil, c const bke::CurvesGeometry &curves = info.drawing.strokes(); const bke::AttributeAccessor attributes = curves.attributes(); const OffsetIndices points_by_curve = curves.points_by_curve(); + const VArray cyclic = curves.cyclic(); + IndexMaskMemory memory; + const IndexMask editable_strokes = ed::greasepencil::retrieve_editable_strokes( + object, info.drawing, memory); /* Assumes that if the ".selection" attribute does not exist, all points are selected. */ const VArray selection_float = *attributes.lookup_or_default( @@ -267,11 +273,15 @@ static void grease_pencil_edit_batch_ensure(const GreasePencil &grease_pencil, c drawing_start_offset += curves.points_num(); /* Add one id for the restart after every curve. */ - total_line_ids_num += curves.curves_num(); + total_line_ids_num += editable_strokes.size(); + Array size_per_editable_stroke(editable_strokes.size()); + offset_indices::gather_group_sizes( + points_by_curve, editable_strokes, size_per_editable_stroke); /* Add one id for every non-cyclic segment. */ - total_line_ids_num += curves.points_num(); + total_line_ids_num += std::accumulate( + size_per_editable_stroke.begin(), size_per_editable_stroke.end(), 0); /* Add one id for the last segment of every cyclic curve. */ - total_line_ids_num += array_utils::count_booleans(curves.cyclic()); + total_line_ids_num += array_utils::count_booleans(curves.cyclic(), editable_strokes); /* Do not show points for locked layers. */ if (layer->is_locked()) { @@ -281,12 +291,12 @@ static void grease_pencil_edit_batch_ensure(const GreasePencil &grease_pencil, c const VArray selection = *attributes.lookup_or_default( ".selection", ATTR_DOMAIN_POINT, true); - for (const int curve_i : curves.curves_range()) { + editable_strokes.foreach_index([&](const int curve_i) { const IndexRange points = points_by_curve[curve_i]; if (ed::curves::has_anything_selected(selection, points)) { visible_points_num += points.size(); } - } + }); } GPUIndexBufBuilder elb; @@ -308,23 +318,24 @@ static void grease_pencil_edit_batch_ensure(const GreasePencil &grease_pencil, c const bke::CurvesGeometry &curves = info.drawing.strokes(); const OffsetIndices points_by_curve = curves.points_by_curve(); const VArray cyclic = curves.cyclic(); + IndexMaskMemory memory; + const IndexMask editable_strokes = ed::greasepencil::retrieve_editable_strokes( + object, info.drawing, memory); /* Fill line indices. */ - threading::parallel_for(curves.curves_range(), 512, [&](IndexRange range) { - for (const int curve_i : range) { - const IndexRange points = points_by_curve[curve_i]; - const bool is_cyclic = cyclic[curve_i]; + editable_strokes.foreach_index([&](const int curve_i) { + const IndexRange points = points_by_curve[curve_i]; + const bool is_cyclic = cyclic[curve_i]; - for (const int point : points) { - GPU_indexbuf_add_generic_vert(&elb, point + drawing_start_offset); - } - - if (is_cyclic) { - GPU_indexbuf_add_generic_vert(&elb, points.first() + drawing_start_offset); - } - - GPU_indexbuf_add_primitive_restart(&elb); + for (const int point_i : points) { + GPU_indexbuf_add_generic_vert(&elb, point_i + drawing_start_offset); } + + if (is_cyclic) { + GPU_indexbuf_add_generic_vert(&elb, points.first() + drawing_start_offset); + } + + GPU_indexbuf_add_primitive_restart(&elb); }); /* Assumes that if the ".selection" attribute does not exist, all points are selected. */ @@ -333,16 +344,14 @@ static void grease_pencil_edit_batch_ensure(const GreasePencil &grease_pencil, c /* Fill point indices. */ if (!layer->is_locked()) { - threading::parallel_for(curves.curves_range(), 512, [&](IndexRange range) { - for (const int curve_i : range) { - const IndexRange points = points_by_curve[curve_i]; - if (!ed::curves::has_anything_selected(selection, points)) { - continue; - } + editable_strokes.foreach_index([&](const int curve_i) { + const IndexRange points = points_by_curve[curve_i]; + if (!ed::curves::has_anything_selected(selection, points)) { + return; + } - for (const int point : points) { - GPU_indexbuf_add_generic_vert(&epb, point + drawing_start_offset); - } + for (const int point : points) { + GPU_indexbuf_add_generic_vert(&epb, point + drawing_start_offset); } }); } @@ -365,7 +374,9 @@ static void grease_pencil_edit_batch_ensure(const GreasePencil &grease_pencil, c cache->is_dirty = false; } -static void grease_pencil_geom_batch_ensure(const GreasePencil &grease_pencil, const Scene &scene) +static void grease_pencil_geom_batch_ensure(Object &object, + const GreasePencil &grease_pencil, + const Scene &scene) { using namespace blender::bke::greasepencil; BLI_assert(grease_pencil.runtime != nullptr); @@ -386,7 +397,6 @@ static void grease_pencil_geom_batch_ensure(const GreasePencil &grease_pencil, c /* First, count how many vertices and triangles are needed for the whole object. Also record the * offsets into the curves for the vertices and triangles. */ - // int total_points_num = 0; /* UNUSED. */ int total_verts_num = 0; int total_triangles_num = 0; int v_offset = 0; @@ -396,16 +406,34 @@ static void grease_pencil_geom_batch_ensure(const GreasePencil &grease_pencil, c const bke::CurvesGeometry &curves = info.drawing.strokes(); const OffsetIndices points_by_curve = curves.points_by_curve(); const VArray cyclic = curves.cyclic(); + IndexMaskMemory memory; + const IndexMask visible_strokes = ed::greasepencil::retrieve_visible_strokes( + object, info.drawing, memory); - int verts_start_offsets_size = curves.curves_num(); - int tris_start_offsets_size = curves.curves_num(); + const int num_curves = visible_strokes.size(); + const int verts_start_offsets_size = num_curves; + const int tris_start_offsets_size = num_curves; Array verts_start_offsets(verts_start_offsets_size); Array tris_start_offsets(tris_start_offsets_size); - /* Calculate the vertex and triangle offsets for all the curves. */ + /* Calculate the triangle offsets for all the visible curves. */ int t_offset = 0; - int num_cyclic = 0; + int pos = 0; for (const int curve_i : curves.curves_range()) { + IndexRange points = points_by_curve[curve_i]; + if (visible_strokes.contains(curve_i)) { + tris_start_offsets[pos] = t_offset; + pos++; + } + if (points.size() >= 3) { + t_offset += points.size() - 2; + } + } + + /* Calculate the vertex offsets for all the visible curves. */ + int num_cyclic = 0; + int num_points = 0; + visible_strokes.foreach_index([&](const int curve_i, const int pos) { IndexRange points = points_by_curve[curve_i]; const bool is_cyclic = cyclic[curve_i]; @@ -413,20 +441,14 @@ static void grease_pencil_geom_batch_ensure(const GreasePencil &grease_pencil, c num_cyclic++; } - tris_start_offsets[curve_i] = t_offset; - if (points.size() >= 3) { - t_offset += points.size() - 2; - } - - verts_start_offsets[curve_i] = v_offset; + verts_start_offsets[pos] = v_offset; v_offset += 1 + points.size() + (is_cyclic ? 1 : 0) + 1; - } - - // total_points_num += curves.points_num(); /* UNUSED. */ + num_points += points.size(); + }); /* One vertex is stored before and after as padding. Cyclic strokes have one extra vertex. */ - total_verts_num += curves.points_num() + num_cyclic + curves.curves_num() * 2; - total_triangles_num += (curves.points_num() + num_cyclic) * 2; + total_verts_num += num_points + num_cyclic + num_curves * 2; + total_triangles_num += (num_points + num_cyclic) * 2; total_triangles_num += info.drawing.triangles().size(); verts_start_offsets_per_visible_drawing.append(std::move(verts_start_offsets)); @@ -477,6 +499,9 @@ static void grease_pencil_geom_batch_ensure(const GreasePencil &grease_pencil, c const Span triangles = info.drawing.triangles(); const Span verts_start_offsets = verts_start_offsets_per_visible_drawing[drawing_i]; const Span tris_start_offsets = tris_start_offsets_per_visible_drawing[drawing_i]; + IndexMaskMemory memory; + const IndexMask visible_strokes = ed::greasepencil::retrieve_visible_strokes( + object, info.drawing, memory); auto populate_point = [&](IndexRange verts_range, int curve_i, @@ -510,59 +535,57 @@ static void grease_pencil_geom_batch_ensure(const GreasePencil &grease_pencil, c GPU_indexbuf_add_tri_verts(&ibo, v_mat + 2, v_mat + 1, v_mat + 3); }; - threading::parallel_for(curves.curves_range(), 512, [&](IndexRange range) { - for (const int curve_i : range) { - IndexRange points = points_by_curve[curve_i]; - const bool is_cyclic = cyclic[curve_i]; - const int verts_start_offset = verts_start_offsets[curve_i]; - const int tris_start_offset = tris_start_offsets[curve_i]; - const int num_verts = 1 + points.size() + (is_cyclic ? 1 : 0) + 1; - IndexRange verts_range = IndexRange(verts_start_offset, num_verts); - MutableSpan verts_slice = verts.slice(verts_range); - MutableSpan cols_slice = cols.slice(verts_range); + visible_strokes.foreach_index([&](const int curve_i, const int pos) { + IndexRange points = points_by_curve[curve_i]; + const bool is_cyclic = cyclic[curve_i]; + const int verts_start_offset = verts_start_offsets[pos]; + const int tris_start_offset = tris_start_offsets[pos]; + const int num_verts = 1 + points.size() + (is_cyclic ? 1 : 0) + 1; + IndexRange verts_range = IndexRange(verts_start_offset, num_verts); + MutableSpan verts_slice = verts.slice(verts_range); + MutableSpan cols_slice = cols.slice(verts_range); - /* First vertex is not drawn. */ - verts_slice.first().mat = -1; + /* First vertex is not drawn. */ + verts_slice.first().mat = -1; - /* If the stroke has more than 2 points, add the triangle indices to the index buffer. */ - if (points.size() >= 3) { - const Span tris_slice = triangles.slice(tris_start_offset, points.size() - 2); - for (const uint3 tri : tris_slice) { - GPU_indexbuf_add_tri_verts(&ibo, - (verts_range[1] + tri.x) << GP_VERTEX_ID_SHIFT, - (verts_range[1] + tri.y) << GP_VERTEX_ID_SHIFT, - (verts_range[1] + tri.z) << GP_VERTEX_ID_SHIFT); - } + /* If the stroke has more than 2 points, add the triangle indices to the index buffer. */ + if (points.size() >= 3) { + const Span tris_slice = triangles.slice(tris_start_offset, points.size() - 2); + for (const uint3 tri : tris_slice) { + GPU_indexbuf_add_tri_verts(&ibo, + (verts_range[1] + tri.x) << GP_VERTEX_ID_SHIFT, + (verts_range[1] + tri.y) << GP_VERTEX_ID_SHIFT, + (verts_range[1] + tri.z) << GP_VERTEX_ID_SHIFT); } - - /* Write all the point attributes to the vertex buffers. Create a quad for each point. */ - for (const int i : IndexRange(points.size())) { - const int idx = i + 1; - populate_point(verts_range, - curve_i, - start_caps[curve_i], - end_caps[curve_i], - points[i], - idx, - verts_slice[idx], - cols_slice[idx]); - } - - if (is_cyclic) { - const int idx = points.size() + 1; - populate_point(verts_range, - curve_i, - start_caps[curve_i], - end_caps[curve_i], - points[0], - idx, - verts_slice[idx], - cols_slice[idx]); - } - - /* Last vertex is not drawn. */ - verts_slice.last().mat = -1; } + + /* Write all the point attributes to the vertex buffers. Create a quad for each point. */ + for (const int i : IndexRange(points.size())) { + const int idx = i + 1; + populate_point(verts_range, + curve_i, + start_caps[curve_i], + end_caps[curve_i], + points[i], + idx, + verts_slice[idx], + cols_slice[idx]); + } + + if (is_cyclic) { + const int idx = points.size() + 1; + populate_point(verts_range, + curve_i, + start_caps[curve_i], + end_caps[curve_i], + points[0], + idx, + verts_slice[idx], + cols_slice[idx]); + } + + /* Last vertex is not drawn. */ + verts_slice.last().mat = -1; }); } @@ -628,7 +651,7 @@ GPUBatch *DRW_cache_grease_pencil_get(const Scene *scene, Object *ob) using namespace blender::draw; GreasePencil &grease_pencil = *static_cast(ob->data); GreasePencilBatchCache *cache = grease_pencil_batch_cache_get(grease_pencil); - grease_pencil_geom_batch_ensure(grease_pencil, *scene); + grease_pencil_geom_batch_ensure(*ob, grease_pencil, *scene); return cache->geom_batch; } @@ -638,7 +661,7 @@ GPUBatch *DRW_cache_grease_pencil_edit_points_get(const Scene *scene, Object *ob using namespace blender::draw; GreasePencil &grease_pencil = *static_cast(ob->data); GreasePencilBatchCache *cache = grease_pencil_batch_cache_get(grease_pencil); - grease_pencil_edit_batch_ensure(grease_pencil, *scene); + grease_pencil_edit_batch_ensure(*ob, grease_pencil, *scene); return cache->edit_points; } @@ -648,7 +671,7 @@ GPUBatch *DRW_cache_grease_pencil_edit_lines_get(const Scene *scene, Object *ob) using namespace blender::draw; GreasePencil &grease_pencil = *static_cast(ob->data); GreasePencilBatchCache *cache = grease_pencil_batch_cache_get(grease_pencil); - grease_pencil_edit_batch_ensure(grease_pencil, *scene); + grease_pencil_edit_batch_ensure(*ob, grease_pencil, *scene); return cache->edit_lines; } @@ -658,7 +681,7 @@ GPUVertBuf *DRW_cache_grease_pencil_position_buffer_get(const Scene *scene, Obje using namespace blender::draw; GreasePencil &grease_pencil = *static_cast(ob->data); GreasePencilBatchCache *cache = grease_pencil_batch_cache_get(grease_pencil); - grease_pencil_geom_batch_ensure(grease_pencil, *scene); + grease_pencil_geom_batch_ensure(*ob, grease_pencil, *scene); return cache->vbo; } @@ -668,7 +691,7 @@ GPUVertBuf *DRW_cache_grease_pencil_color_buffer_get(const Scene *scene, Object using namespace blender::draw; GreasePencil &grease_pencil = *static_cast(ob->data); GreasePencilBatchCache *cache = grease_pencil_batch_cache_get(grease_pencil); - grease_pencil_geom_batch_ensure(grease_pencil, *scene); + grease_pencil_geom_batch_ensure(*ob, grease_pencil, *scene); return cache->vbo_col; } diff --git a/source/blender/editors/grease_pencil/intern/grease_pencil_utils.cc b/source/blender/editors/grease_pencil/intern/grease_pencil_utils.cc index 2bf2e69222a..8185b12f4ba 100644 --- a/source/blender/editors/grease_pencil/intern/grease_pencil_utils.cc +++ b/source/blender/editors/grease_pencil/intern/grease_pencil_utils.cc @@ -312,12 +312,27 @@ static VectorSet get_editable_material_indices(Object &object) (material->gp_style->flag & GP_MATERIAL_LOCKED) == 0 && (material->gp_style->flag & GP_MATERIAL_HIDE) == 0) { - locked_material_indices.add(mat_i); + locked_material_indices.add_new(mat_i); } } return locked_material_indices; } +static VectorSet get_hidden_material_indices(Object &object) +{ + BLI_assert(object.type == OB_GREASE_PENCIL); + VectorSet hidden_material_indices; + for (const int mat_i : IndexRange(object.totcol)) { + Material *material = BKE_object_material_get(&object, mat_i + 1); + if (material != nullptr && material->gp_style != nullptr && + (material->gp_style->flag & GP_MATERIAL_HIDE) != 0) + { + hidden_material_indices.add_new(mat_i); + } + } + return hidden_material_indices; +} + IndexMask retrieve_editable_strokes(Object &object, const bke::greasepencil::Drawing &drawing, IndexMaskMemory &memory) @@ -327,13 +342,23 @@ IndexMask retrieve_editable_strokes(Object &object, /* Get all the editable material indices */ VectorSet editable_material_indices = get_editable_material_indices(object); + if (locked_material_indices.is_empty()) { + return drawing.strokes().curves_range(); + } + const bke::CurvesGeometry &curves = drawing.strokes(); const IndexRange curves_range = drawing.strokes().curves_range(); const bke::AttributeAccessor attributes = curves.attributes(); + const VArray materials = *attributes.lookup("material_index", ATTR_DOMAIN_CURVE); + if (!materials) { + /* if the attribute does not exist then the default is the first material. */ + if (locked_material_indices.contains(0)) { + return curves_range; + } + return IndexMask(); + } /* Get all the strokes that have their material unlocked. */ - const VArray materials = *attributes.lookup_or_default( - "material_index", ATTR_DOMAIN_CURVE, -1); return IndexMask::from_predicate( curves_range, GrainSize(4096), memory, [&](const int64_t curve_i) { const int material_index = materials[curve_i]; @@ -348,13 +373,23 @@ IndexMask retrieve_editable_points(Object &object, /* Get all the editable material indices */ VectorSet editable_material_indices = get_editable_material_indices(object); + if (locked_material_indices.is_empty()) { + return drawing.strokes().points_range(); + } + const bke::CurvesGeometry &curves = drawing.strokes(); const IndexRange points_range = drawing.strokes().points_range(); const bke::AttributeAccessor attributes = curves.attributes(); /* Propagate the material index to the points. */ - const VArray materials = *attributes.lookup_or_default( - "material_index", ATTR_DOMAIN_POINT, -1); + const VArray materials = *attributes.lookup("material_index", ATTR_DOMAIN_POINT); + if (!materials) { + /* if the attribute does not exist then the default is the first material. */ + if (locked_material_indices.contains(0)) { + return points_range; + } + return IndexMask(); + } /* Get all the points that are part of a stroke with an unlocked material. */ return IndexMask::from_predicate( points_range, GrainSize(4096), memory, [&](const int64_t point_i) { @@ -377,6 +412,33 @@ IndexMask retrieve_editable_elements(Object &object, return {}; } +IndexMask retrieve_visible_strokes(Object &object, + const bke::greasepencil::Drawing &drawing, + IndexMaskMemory &memory) +{ + using namespace blender; + + /* Get all the hidden material indices. */ + VectorSet hidden_material_indices = get_hidden_material_indices(object); + + if (hidden_material_indices.is_empty()) { + return drawing.strokes().curves_range(); + } + + const bke::CurvesGeometry &curves = drawing.strokes(); + const IndexRange curves_range = drawing.strokes().curves_range(); + const bke::AttributeAccessor attributes = curves.attributes(); + + /* Get all the strokes that have their material visible. */ + const VArray materials = *attributes.lookup_or_default( + "material_index", ATTR_DOMAIN_CURVE, -1); + return IndexMask::from_predicate( + curves_range, GrainSize(4096), memory, [&](const int64_t curve_i) { + const int material_index = materials[curve_i]; + return !hidden_material_indices.contains(material_index); + }); +} + IndexMask retrieve_editable_and_selected_strokes(Object &object, const bke::greasepencil::Drawing &drawing, IndexMaskMemory &memory) diff --git a/source/blender/editors/include/ED_grease_pencil.hh b/source/blender/editors/include/ED_grease_pencil.hh index b3197869f13..f2a498a2475 100644 --- a/source/blender/editors/include/ED_grease_pencil.hh +++ b/source/blender/editors/include/ED_grease_pencil.hh @@ -181,6 +181,10 @@ IndexMask retrieve_editable_elements(Object &object, eAttrDomain selection_domain, IndexMaskMemory &memory); +IndexMask retrieve_visible_strokes(Object &grease_pencil_object, + const bke::greasepencil::Drawing &drawing, + IndexMaskMemory &memory); + IndexMask retrieve_editable_and_selected_strokes(Object &grease_pencil_object, const bke::greasepencil::Drawing &drawing, IndexMaskMemory &memory);