diff --git a/source/blender/blenkernel/BKE_paint.hh b/source/blender/blenkernel/BKE_paint.hh index 22cdcdab169..133f5e63f90 100644 --- a/source/blender/blenkernel/BKE_paint.hh +++ b/source/blender/blenkernel/BKE_paint.hh @@ -224,7 +224,7 @@ bool BKE_paint_always_hide_test(const Object *ob); * Returns non-zero if any of the corners of the grid * face whose inner corner is at (x, y) are hidden, zero otherwise. */ -bool paint_is_grid_face_hidden(const unsigned int *grid_hidden, int gridsize, int x, int y); +bool paint_is_grid_face_hidden(blender::BoundedBitSpan grid_hidden, int gridsize, int x, int y); /** * Return true if all vertices in the face are visible, false otherwise. */ diff --git a/source/blender/blenkernel/BKE_pbvh_api.hh b/source/blender/blenkernel/BKE_pbvh_api.hh index fb91a5a80fd..2691ba889f3 100644 --- a/source/blender/blenkernel/BKE_pbvh_api.hh +++ b/source/blender/blenkernel/BKE_pbvh_api.hh @@ -12,7 +12,7 @@ #include #include -#include "BLI_bitmap.h" +#include "BLI_bit_group_vector.hh" #include "BLI_compiler_compat.h" #include "BLI_function_ref.hh" #include "BLI_index_mask.hh" @@ -320,7 +320,7 @@ void BKE_pbvh_sync_visibility_from_verts(PBVH *pbvh, Mesh *me); /** * Returns the number of visible quads in the nodes' grids. */ -int BKE_pbvh_count_grid_quads(blender::Span grid_hidden, +int BKE_pbvh_count_grid_quads(const blender::BitGroupVector<> &grid_visibility, const int *grid_indices, int totgrid, int gridsize, @@ -464,7 +464,8 @@ struct PBVHVertexIter { CCGKey key; CCGElem *const *grids; CCGElem *grid; - BLI_bitmap *const *grid_hidden, *gh; + const blender::BitGroupVector<> *grid_hidden; + std::optional gh; const int *grid_indices; int totgrid; int gridsize; @@ -508,7 +509,12 @@ void pbvh_vertex_iter_init(PBVH *pbvh, PBVHNode *node, PBVHVertexIter *vi, int m vi.index = vi.vertex.i = vi.grid_indices[vi.g] * vi.key.grid_area - 1; \ vi.grid = CCG_elem_offset(&vi.key, vi.grids[vi.grid_indices[vi.g]], -1); \ if (mode == PBVH_ITER_UNIQUE) { \ - vi.gh = vi.grid_hidden[vi.grid_indices[vi.g]]; \ + if (vi.grid_hidden) { \ + vi.gh.emplace((*vi.grid_hidden)[vi.grid_indices[vi.g]]); \ + } \ + else { \ + vi.gh.reset(); \ + } \ } \ } \ else { \ @@ -527,7 +533,7 @@ void pbvh_vertex_iter_init(PBVH *pbvh, PBVHNode *node, PBVHVertexIter *vi, int m vi.vertex.i++; \ vi.visible = true; \ if (vi.gh) { \ - if (BLI_BITMAP_TEST(vi.gh, vi.gy * vi.gridsize + vi.gx)) { \ + if ((*vi.gh)[vi.gy * vi.gridsize + vi.gx]) { \ continue; \ } \ } \ diff --git a/source/blender/blenkernel/BKE_subdiv_ccg.hh b/source/blender/blenkernel/BKE_subdiv_ccg.hh index 447a8aa22ae..e569b614932 100644 --- a/source/blender/blenkernel/BKE_subdiv_ccg.hh +++ b/source/blender/blenkernel/BKE_subdiv_ccg.hh @@ -9,7 +9,7 @@ #pragma once #include "BLI_array.hh" -#include "BLI_bitmap.h" +#include "BLI_bit_group_vector.hh" #include "BLI_offset_indices.hh" #include "BLI_sys_types.h" @@ -171,7 +171,9 @@ struct SubdivCCG { blender::Array adjacent_verts; blender::Array grid_flag_mats; - blender::Array grid_hidden; + + /** Store the visibility of the items in each grid. If empty, everything is visible. */ + blender::BitGroupVector<> grid_hidden; /* TODO(sergey): Consider adding some accessors to a "decoded" geometry, * to make integration with draw manager and such easy. @@ -315,5 +317,5 @@ SubdivCCGAdjacencyType BKE_subdiv_ccg_coarse_mesh_adjacency_info_get( const int *BKE_subdiv_ccg_start_face_grid_index_ensure(SubdivCCG &subdiv_ccg); const int *BKE_subdiv_ccg_start_face_grid_index_get(const SubdivCCG &subdiv_ccg); -void BKE_subdiv_ccg_grid_hidden_ensure(SubdivCCG &subdiv_ccg, int grid_index); -void BKE_subdiv_ccg_grid_hidden_free(SubdivCCG &subdiv_ccg, int grid_index); +blender::BitGroupVector<> &BKE_subdiv_ccg_grid_hidden_ensure(SubdivCCG &subdiv_ccg); +void BKE_subdiv_ccg_grid_hidden_free(SubdivCCG &subdiv_ccg); diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index de9291a1961..512d4832e6e 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -1282,13 +1282,14 @@ void BKE_paint_blend_read_data(BlendDataReader *reader, const Scene *scene, Pain BKE_paint_runtime_init(scene->toolsettings, p); } -bool paint_is_grid_face_hidden(const uint *grid_hidden, int gridsize, int x, int y) +bool paint_is_grid_face_hidden(const blender::BoundedBitSpan grid_hidden, + int gridsize, + int x, + int y) { /* Skip face if any of its corners are hidden. */ - return (BLI_BITMAP_TEST(grid_hidden, y * gridsize + x) || - BLI_BITMAP_TEST(grid_hidden, y * gridsize + x + 1) || - BLI_BITMAP_TEST(grid_hidden, (y + 1) * gridsize + x + 1) || - BLI_BITMAP_TEST(grid_hidden, (y + 1) * gridsize + x)); + return grid_hidden[y * gridsize + x] || grid_hidden[y * gridsize + x + 1] || + grid_hidden[(y + 1) * gridsize + x + 1] || grid_hidden[(y + 1) * gridsize + x]; } bool paint_is_bmesh_face_hidden(const BMFace *f) @@ -2148,32 +2149,24 @@ void BKE_sculpt_sync_face_visibility_to_grids(Mesh *mesh, SubdivCCG *subdiv_ccg) const VArray hide_poly = *attributes.lookup_or_default( ".hide_poly", ATTR_DOMAIN_FACE, false); if (hide_poly.is_single() && !hide_poly.get_internal_single()) { - /* Nothing is hidden, so we can just remove all visibility bitmaps. */ - for (const int i : subdiv_ccg->grid_hidden.index_range()) { - BKE_subdiv_ccg_grid_hidden_free(*subdiv_ccg, i); - } + BKE_subdiv_ccg_grid_hidden_free(*subdiv_ccg); return; } + const OffsetIndices faces = mesh->faces(); + const VArraySpan hide_poly_span(hide_poly); CCGKey key; BKE_subdiv_ccg_key_top_level(key, *subdiv_ccg); - for (int i = 0; i < mesh->totloop; i++) { - const int face_index = BKE_subdiv_ccg_grid_to_face_index(*subdiv_ccg, i); - const bool is_hidden = hide_poly_span[face_index]; - - /* Avoid creating and modifying the grid_hidden bitmap if the base mesh face is visible and - * there is not bitmap for the grid. This is because missing grid_hidden implies grid is fully - * visible. */ - if (is_hidden) { - BKE_subdiv_ccg_grid_hidden_ensure(*subdiv_ccg, i); + BitGroupVector<> &grid_hidden = BKE_subdiv_ccg_grid_hidden_ensure(*subdiv_ccg); + threading::parallel_for(faces.index_range(), 1024, [&](const IndexRange range) { + for (const int i : range) { + const bool face_hidden = hide_poly_span[i]; + for (const int corner : faces[i]) { + grid_hidden[corner].set_all(face_hidden); + } } - - BLI_bitmap *gh = subdiv_ccg->grid_hidden[i]; - if (gh) { - BLI_bitmap_set_all(gh, is_hidden, key.grid_area); - } - } + }); } static PBVH *build_pbvh_for_dynamic_topology(Object *ob) diff --git a/source/blender/blenkernel/intern/pbvh.cc b/source/blender/blenkernel/intern/pbvh.cc index 857159e3026..0d8c2e222de 100644 --- a/source/blender/blenkernel/intern/pbvh.cc +++ b/source/blender/blenkernel/intern/pbvh.cc @@ -45,6 +45,7 @@ #include "pbvh_intern.hh" +using blender::BitGroupVector; using blender::float3; using blender::MutableSpan; using blender::Span; @@ -390,14 +391,16 @@ static void update_vb(PBVH *pbvh, PBVHNode *node, const Span prim_bbc, int node->orig_vb = node->vb; } -int BKE_pbvh_count_grid_quads(const Span grid_hidden, +int BKE_pbvh_count_grid_quads(const BitGroupVector<> &grid_hidden, const int *grid_indices, int totgrid, int gridsize, int display_gridsize) { const int gridarea = (gridsize - 1) * (gridsize - 1); - int totquad = 0; + if (grid_hidden.is_empty()) { + return gridarea * totgrid; + } /* grid hidden layer is present, so have to check each grid for * visibility */ @@ -407,22 +410,17 @@ int BKE_pbvh_count_grid_quads(const Span grid_hidden, int skip = depth2 < depth1 ? 1 << (depth1 - depth2 - 1) : 1; + int totquad = 0; for (int i = 0; i < totgrid; i++) { - const BLI_bitmap *gh = grid_hidden[grid_indices[i]]; - - if (gh) { - /* grid hidden are present, have to check each element */ - for (int y = 0; y < gridsize - skip; y += skip) { - for (int x = 0; x < gridsize - skip; x += skip) { - if (!paint_is_grid_face_hidden(gh, gridsize, x, y)) { - totquad++; - } + const blender::BoundedBitSpan gh = grid_hidden[grid_indices[i]]; + /* grid hidden are present, have to check each element */ + for (int y = 0; y < gridsize - skip; y += skip) { + for (int x = 0; x < gridsize - skip; x += skip) { + if (!paint_is_grid_face_hidden(gh, gridsize, x, y)) { + totquad++; } } } - else { - totquad += gridarea; - } } return totquad; @@ -1466,7 +1464,6 @@ static blender::draw::pbvh::PBVH_GPU_Args pbvh_draw_args_init(const Mesh &mesh, args.grid_indices = node.prim_indices; args.subdiv_ccg = pbvh.subdiv_ccg; args.grids = pbvh.subdiv_ccg->grids; - args.grid_hidden = pbvh.subdiv_ccg->grid_hidden; args.grid_flag_mats = pbvh.subdiv_ccg->grid_flag_mats; args.vert_normals = pbvh.vert_normals; break; @@ -1662,20 +1659,24 @@ static void pbvh_faces_node_visibility_update(const Mesh &mesh, const Span nodes) { using namespace blender; + const BitGroupVector<> &grid_hidden = pbvh->subdiv_ccg->grid_hidden; + if (grid_hidden.is_empty()) { + for (PBVHNode *node : nodes) { + BKE_pbvh_node_fully_hidden_set(node, false); + node->flag &= ~PBVH_UpdateVisibility; + } + return; + } + CCGKey key = *BKE_pbvh_get_grid_key(pbvh); - const Span grid_hidden = pbvh->subdiv_ccg->grid_hidden; threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) { for (PBVHNode *node : nodes.slice(range)) { for (const int grid_index : BKE_pbvh_node_get_grid_indices(*node)) { - const BLI_bitmap *gh = grid_hidden[grid_index]; - if (!gh) { - BKE_pbvh_node_fully_hidden_set(node, false); - return; - } + const blender::BoundedBitSpan gh = grid_hidden[grid_index]; for (int y = 0; y < key.grid_size; y++) { for (int x = 0; x < key.grid_size; x++) { - if (!BLI_BITMAP_TEST(gh, y * key.grid_size + x)) { + if (!gh[y * key.grid_size + x]) { BKE_pbvh_node_fully_hidden_set(node, false); return; } @@ -2335,7 +2336,7 @@ static bool pbvh_grids_node_raycast(PBVH *pbvh, bool hit = false; float nearest_vertex_co[3] = {0.0}; const CCGKey *gridkey = &pbvh->gridkey; - const Span grid_hidden = pbvh->subdiv_ccg->grid_hidden; + const BitGroupVector<> &grid_hidden = pbvh->subdiv_ccg->grid_hidden; const Span grids = pbvh->subdiv_ccg->grids; for (int i = 0; i < totgrid; i++) { @@ -2345,13 +2346,11 @@ static bool pbvh_grids_node_raycast(PBVH *pbvh, continue; } - const BLI_bitmap *gh = grid_hidden[grid_index]; - for (int y = 0; y < gridsize - 1; y++) { for (int x = 0; x < gridsize - 1; x++) { /* check if grid face is hidden */ - if (gh) { - if (paint_is_grid_face_hidden(gh, gridsize, x, y)) { + if (!grid_hidden.is_empty()) { + if (paint_is_grid_face_hidden(grid_hidden[grid_index], gridsize, x, y)) { continue; } } @@ -2665,7 +2664,7 @@ static bool pbvh_grids_node_nearest_to_ray(PBVH *pbvh, const int totgrid = node->prim_indices.size(); const int gridsize = pbvh->gridkey.grid_size; bool hit = false; - const Span grid_hidden = pbvh->subdiv_ccg->grid_hidden; + const BitGroupVector<> &grid_hidden = pbvh->subdiv_ccg->grid_hidden; const Span grids = pbvh->subdiv_ccg->grids; for (int i = 0; i < totgrid; i++) { @@ -2674,13 +2673,11 @@ static bool pbvh_grids_node_nearest_to_ray(PBVH *pbvh, continue; } - const BLI_bitmap *gh = grid_hidden[node->prim_indices[i]]; - for (int y = 0; y < gridsize - 1; y++) { for (int x = 0; x < gridsize - 1; x++) { /* check if grid face is hidden */ - if (gh) { - if (paint_is_grid_face_hidden(gh, gridsize, x, y)) { + if (!grid_hidden.is_empty()) { + if (paint_is_grid_face_hidden(grid_hidden[node->prim_indices[i]], gridsize, x, y)) { continue; } } @@ -3078,9 +3075,10 @@ void pbvh_vertex_iter_init(PBVH *pbvh, PBVHNode *node, PBVHVertexIter *vi, int m vi->bm_vdata, CD_PROP_FLOAT, ".sculpt_mask"); } - vi->gh = nullptr; + vi->gh.reset(); if (vi->grids && mode == PBVH_ITER_UNIQUE) { - vi->grid_hidden = pbvh->subdiv_ccg->grid_hidden.data(); + vi->grid_hidden = pbvh->subdiv_ccg->grid_hidden.is_empty() ? nullptr : + &pbvh->subdiv_ccg->grid_hidden; } vi->mask = 0.0f; @@ -3294,20 +3292,23 @@ void BKE_pbvh_sync_visibility_from_verts(PBVH *pbvh, Mesh *mesh) } case PBVH_GRIDS: { const OffsetIndices faces = mesh->faces(); - const Span grid_hidden = pbvh->subdiv_ccg->grid_hidden; + const BitGroupVector<> &grid_hidden = pbvh->subdiv_ccg->grid_hidden; CCGKey key = pbvh->gridkey; IndexMaskMemory memory; - const IndexMask hidden_faces = IndexMask::from_predicate( - faces.index_range(), GrainSize(1024), memory, [&](const int i) { - const IndexRange face = faces[i]; - return std::any_of(face.begin(), face.end(), [&](const int corner) { - if (!grid_hidden[corner]) { - return false; - } - return BLI_BITMAP_TEST_BOOL(grid_hidden[corner], key.grid_area - 1); - }); - }); + const IndexMask hidden_faces = + grid_hidden.is_empty() ? + IndexMask::from_predicate(faces.index_range(), + GrainSize(1024), + memory, + [&](const int i) { + const IndexRange face = faces[i]; + return std::any_of( + face.begin(), face.end(), [&](const int corner) { + return grid_hidden[corner][key.grid_area - 1]; + }); + }) : + IndexMask(); MutableAttributeAccessor attributes = mesh->attributes_for_write(); if (hidden_faces.is_empty()) { diff --git a/source/blender/blenkernel/intern/subdiv_ccg.cc b/source/blender/blenkernel/intern/subdiv_ccg.cc index bb34b62dae2..88ae5ff6ac9 100644 --- a/source/blender/blenkernel/intern/subdiv_ccg.cc +++ b/source/blender/blenkernel/intern/subdiv_ccg.cc @@ -144,10 +144,6 @@ static void subdiv_ccg_alloc_elements(SubdivCCG &subdiv_ccg, Subdiv &subdiv) subdiv_ccg.grids[grid_index] = (CCGElem *)&subdiv_ccg.grids_storage[grid_offset]; } subdiv_ccg.grid_flag_mats.reinitialize(num_grids); - subdiv_ccg.grid_hidden.reinitialize(num_grids); - for (int grid_index = 0; grid_index < num_grids; grid_index++) { - subdiv_ccg.grid_hidden[grid_index] = BLI_BITMAP_NEW(grid_area, "ccg grid hidden"); - } /* TODO(sergey): Allocate memory for loose elements. */ subdiv_ccg.faces.reinitialize(num_faces); subdiv_ccg.grid_to_face_map.reinitialize(num_grids); @@ -563,10 +559,6 @@ Mesh *BKE_subdiv_to_ccg_mesh(Subdiv &subdiv, void BKE_subdiv_ccg_destroy(SubdivCCG *subdiv_ccg) { - for (const int grid_index : subdiv_ccg->grid_hidden.index_range()) { - MEM_SAFE_FREE(subdiv_ccg->grid_hidden[grid_index]); - } - if (subdiv_ccg->subdiv != nullptr) { BKE_subdiv_free(subdiv_ccg->subdiv); } @@ -1684,20 +1676,18 @@ SubdivCCGAdjacencyType BKE_subdiv_ccg_coarse_mesh_adjacency_info_get( return SUBDIV_CCG_ADJACENT_NONE; } -void BKE_subdiv_ccg_grid_hidden_ensure(SubdivCCG &subdiv_ccg, int grid_index) +blender::BitGroupVector<> &BKE_subdiv_ccg_grid_hidden_ensure(SubdivCCG &subdiv_ccg) { - if (subdiv_ccg.grid_hidden[grid_index] != nullptr) { - return; + if (subdiv_ccg.grid_hidden.is_empty()) { + const int grid_area = subdiv_ccg.grid_size * subdiv_ccg.grid_size; + subdiv_ccg.grid_hidden = blender::BitGroupVector<>(subdiv_ccg.grids.size(), grid_area, false); } - - CCGKey key; - BKE_subdiv_ccg_key_top_level(key, subdiv_ccg); - subdiv_ccg.grid_hidden[grid_index] = BLI_BITMAP_NEW(key.grid_area, __func__); + return subdiv_ccg.grid_hidden; } -void BKE_subdiv_ccg_grid_hidden_free(SubdivCCG &subdiv_ccg, int grid_index) +void BKE_subdiv_ccg_grid_hidden_free(SubdivCCG &subdiv_ccg) { - MEM_SAFE_FREE(subdiv_ccg.grid_hidden[grid_index]); + subdiv_ccg.grid_hidden = {}; } static void subdiv_ccg_coord_to_ptex_coord(const SubdivCCG &subdiv_ccg, diff --git a/source/blender/blenlib/BLI_bit_group_vector.hh b/source/blender/blenlib/BLI_bit_group_vector.hh index 4e4fed13542..c6ca966394c 100644 --- a/source/blender/blenlib/BLI_bit_group_vector.hh +++ b/source/blender/blenlib/BLI_bit_group_vector.hh @@ -76,6 +76,11 @@ class BitGroupVector { return aligned_group_size_ == 0 ? 0 : data_.size() / aligned_group_size_; } + bool is_empty() const + { + return this->size() == 0; + } + /** Number of bits per group. */ int64_t group_size() const { diff --git a/source/blender/blenlib/BLI_bit_span.hh b/source/blender/blenlib/BLI_bit_span.hh index 0b9f518b28f..c464db9ffed 100644 --- a/source/blender/blenlib/BLI_bit_span.hh +++ b/source/blender/blenlib/BLI_bit_span.hh @@ -363,6 +363,11 @@ class MutableBoundedBitSpan : public MutableBitSpan { return {data_, bit_range_.take_front(n)}; } + BoundedBitSpan as_span() const + { + return BoundedBitSpan(data_, bit_range_); + } + void copy_from(const BitSpan other); void copy_from(const BoundedBitSpan other); }; diff --git a/source/blender/blenlib/BLI_bit_span_ops.hh b/source/blender/blenlib/BLI_bit_span_ops.hh index 466b9d64c24..c39b1bafd07 100644 --- a/source/blender/blenlib/BLI_bit_span_ops.hh +++ b/source/blender/blenlib/BLI_bit_span_ops.hh @@ -255,6 +255,16 @@ template inline bool any_bit_set(const BitSpanT &arg) return has_common_set_bits(arg); } +template inline bool has_common_unset_bits(const BitSpanT &...args) +{ + return any_set_expr([](const auto... x) { return ~(x | ...); }, args...); +} + +template inline bool any_bit_unset(const BitSpanT &arg) +{ + return has_common_unset_bits(arg); +} + template inline void foreach_1_index(const BitSpanT &data, Fn &&fn) { foreach_1_index_expr([](const BitInt x) { return x; }, fn, data); diff --git a/source/blender/blenlib/BLI_bit_vector.hh b/source/blender/blenlib/BLI_bit_vector.hh index 4a2cf198ca1..59df72924a6 100644 --- a/source/blender/blenlib/BLI_bit_vector.hh +++ b/source/blender/blenlib/BLI_bit_vector.hh @@ -95,10 +95,10 @@ class BitVector { BitVector(NoExceptConstructor, Allocator allocator = {}) noexcept : BitVector(allocator) {} - BitVector(const BitVector &other) : BitVector(NoExceptConstructor(), other.allocator_) + BitVector(const BoundedBitSpan span) : BitVector(NoExceptConstructor()) { - const int64_t ints_to_copy = other.used_ints_amount(); - if (other.size_in_bits_ <= BitsInInlineBuffer) { + const int64_t ints_to_copy = required_ints_for_bits(span.size()); + if (span.size() <= BitsInInlineBuffer) { /* The data is copied into the owned inline buffer. */ data_ = inline_buffer_; capacity_in_bits_ = BitsInInlineBuffer; @@ -109,8 +109,13 @@ class BitVector { allocator_.allocate(ints_to_copy * sizeof(BitInt), AllocationAlignment, __func__)); capacity_in_bits_ = ints_to_copy * BitsPerInt; } - size_in_bits_ = other.size_in_bits_; - uninitialized_copy_n(other.data_, ints_to_copy, data_); + size_in_bits_ = span.size(); + uninitialized_copy_n(span.data(), ints_to_copy, data_); + } + + BitVector(const BitVector &other) : BitVector(BoundedBitSpan(other)) + { + allocator_ = other.allocator_; } BitVector(BitVector &&other) noexcept : BitVector(NoExceptConstructor(), other.allocator_) diff --git a/source/blender/draw/DRW_pbvh.hh b/source/blender/draw/DRW_pbvh.hh index b30c24e5df0..14060a7f676 100644 --- a/source/blender/draw/DRW_pbvh.hh +++ b/source/blender/draw/DRW_pbvh.hh @@ -10,7 +10,6 @@ /* Needed for BKE_ccg.h. */ #include "BLI_assert.h" -#include "BLI_bitmap.h" #include "BLI_math_vector_types.hh" #include "BLI_offset_indices.hh" #include "BLI_set.hh" @@ -80,7 +79,6 @@ struct PBVH_GPU_Args { Span grid_indices; CCGKey ccg_key; Span grids; - Span grid_hidden; Span prim_indices; diff --git a/source/blender/draw/intern/draw_pbvh.cc b/source/blender/draw/intern/draw_pbvh.cc index c935af1873c..79503acf4ad 100644 --- a/source/blender/draw/intern/draw_pbvh.cc +++ b/source/blender/draw/intern/draw_pbvh.cc @@ -351,7 +351,7 @@ struct PBVHBatches { break; } case PBVH_GRIDS: { - count = BKE_pbvh_count_grid_quads(args.grid_hidden, + count = BKE_pbvh_count_grid_quads(args.subdiv_ccg->grid_hidden, args.grid_indices.data(), args.grid_indices.size(), args.ccg_key.grid_size, @@ -1111,6 +1111,7 @@ struct PBVHBatches { void create_index_grids(const PBVH_GPU_Args &args, bool do_coarse) { + const BitGroupVector<> &grid_hidden = args.subdiv_ccg->grid_hidden; const int *mat_index = static_cast( CustomData_get_layer_named(args.face_data, CD_PROP_INT32, "material_index")); @@ -1134,14 +1135,15 @@ struct PBVHBatches { for (const int grid_index : args.grid_indices) { bool smooth = !args.grid_flag_mats[grid_index].sharp; - const BLI_bitmap *gh = args.grid_hidden[grid_index]; - - for (int y = 0; y < gridsize - 1; y += skip) { - for (int x = 0; x < gridsize - 1; x += skip) { - if (gh && paint_is_grid_face_hidden(gh, gridsize, x, y)) { - /* Skip hidden faces by just setting smooth to true. */ - smooth = true; - goto outer_loop_break; + if (!grid_hidden.is_empty()) { + const BoundedBitSpan gh = grid_hidden[grid_index]; + for (int y = 0; y < gridsize - 1; y += skip) { + for (int x = 0; x < gridsize - 1; x += skip) { + if (paint_is_grid_face_hidden(gh, gridsize, x, y)) { + /* Skip hidden faces by just setting smooth to true. */ + smooth = true; + goto outer_loop_break; + } } } } @@ -1159,7 +1161,7 @@ struct PBVHBatches { const CCGKey *key = &args.ccg_key; uint visible_quad_len = BKE_pbvh_count_grid_quads( - args.grid_hidden, args.grid_indices.data(), totgrid, key->grid_size, display_gridsize); + grid_hidden, args.grid_indices.data(), totgrid, key->grid_size, display_gridsize); GPU_indexbuf_init(&elb, GPU_PRIM_TRIS, 2 * visible_quad_len, INT_MAX); GPU_indexbuf_init(&elb_lines, @@ -1174,12 +1176,13 @@ struct PBVHBatches { uint v0, v1, v2, v3; bool grid_visible = false; - const BLI_bitmap *gh = args.grid_hidden[args.grid_indices[i]]; + const BoundedBitSpan gh = grid_hidden.is_empty() ? BoundedBitSpan() : + grid_hidden[args.grid_indices[i]]; for (int j = 0; j < gridsize - skip; j += skip) { for (int k = 0; k < gridsize - skip; k += skip) { /* Skip hidden grid face */ - if (gh && paint_is_grid_face_hidden(gh, gridsize, k, j)) { + if (!gh.is_empty() && paint_is_grid_face_hidden(gh, gridsize, k, j)) { continue; } /* Indices in a Clockwise QUAD disposition. */ @@ -1212,13 +1215,14 @@ struct PBVHBatches { for (int i = 0; i < totgrid; i++, offset += grid_vert_len) { bool grid_visible = false; - const BLI_bitmap *gh = args.grid_hidden[args.grid_indices[i]]; + const BoundedBitSpan gh = grid_hidden.is_empty() ? BoundedBitSpan() : + grid_hidden[args.grid_indices[i]]; uint v0, v1, v2, v3; for (int j = 0; j < gridsize - skip; j += skip) { for (int k = 0; k < gridsize - skip; k += skip) { /* Skip hidden grid face */ - if (gh && paint_is_grid_face_hidden(gh, gridsize, k, j)) { + if (!gh.is_empty() && paint_is_grid_face_hidden(gh, gridsize, k, j)) { continue; } diff --git a/source/blender/editors/sculpt_paint/paint_hide.cc b/source/blender/editors/sculpt_paint/paint_hide.cc index 618a12caa47..11ee4e2f796 100644 --- a/source/blender/editors/sculpt_paint/paint_hide.cc +++ b/source/blender/editors/sculpt_paint/paint_hide.cc @@ -10,7 +10,7 @@ #include "MEM_guardedalloc.h" #include "BLI_array_utils.hh" -#include "BLI_bitmap.h" +#include "BLI_bit_span_ops.hh" #include "BLI_enumerable_thread_specific.hh" #include "BLI_math_geom.h" #include "BLI_math_matrix.h" @@ -156,12 +156,12 @@ static void vert_hide_update(Object &object, } static void partialvis_update_mesh(Object &object, - PBVH &pbvh, const VisAction action, const VisArea area, const float planes[4][4], const Span nodes) { + PBVH &pbvh = *object.sculpt->pbvh; Mesh &mesh = *static_cast(object.data); bke::MutableAttributeAccessor attributes = mesh.attributes_for_write(); if (action == VisAction::Show && !attributes.contains(".hide_vert")) { @@ -195,7 +195,7 @@ static void partialvis_update_mesh(Object &object, break; } break; - case VisArea::Masked: + case VisArea::Masked: { const VArraySpan mask = *attributes.lookup(".sculpt_mask", ATTR_DOMAIN_POINT); if (action == VisAction::Show && mask.is_empty()) { vert_show_all(object, nodes); @@ -210,96 +210,154 @@ static void partialvis_update_mesh(Object &object, }); } break; + } } BKE_mesh_flush_hidden_from_verts(&mesh); } -/* Hide or show elements in multires grids with a special GridFlags - * customdata layer. */ -static void partialvis_update_grids(Depsgraph *depsgraph, - Object *ob, - PBVH *pbvh, +static void grids_show_all(Depsgraph &depsgraph, Object &object, const Span nodes) +{ + Mesh &mesh = *static_cast(object.data); + PBVH &pbvh = *object.sculpt->pbvh; + SubdivCCG &subdiv_ccg = *object.sculpt->subdiv_ccg; + const BitGroupVector<> &grid_hidden = subdiv_ccg.grid_hidden; + if (!grid_hidden.is_empty()) { + threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) { + for (PBVHNode *node : nodes.slice(range)) { + const Span grids = BKE_pbvh_node_get_grid_indices(*node); + if (std::any_of(grids.begin(), grids.end(), [&](const int i) { + return bits::any_bit_set(grid_hidden[i]); + })) + { + SCULPT_undo_push_node(&object, node, SCULPT_UNDO_HIDDEN); + BKE_pbvh_node_mark_rebuild_draw(node); + } + } + }); + } + for (PBVHNode *node : nodes) { + BKE_pbvh_node_fully_hidden_set(node, false); + } + BKE_subdiv_ccg_grid_hidden_free(subdiv_ccg); + BKE_pbvh_sync_visibility_from_verts(&pbvh, &mesh); + multires_mark_as_modified(&depsgraph, &object, MULTIRES_HIDDEN_MODIFIED); +} + +static void grid_hide_update(Depsgraph &depsgraph, + Object &object, + const Span nodes, + const FunctionRef calc_hide) +{ + Mesh &mesh = *static_cast(object.data); + PBVH &pbvh = *object.sculpt->pbvh; + SubdivCCG &subdiv_ccg = *object.sculpt->subdiv_ccg; + BitGroupVector<> &grid_hidden = BKE_subdiv_ccg_grid_hidden_ensure(subdiv_ccg); + + bool any_changed = false; + threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) { + for (PBVHNode *node : nodes.slice(range)) { + bool changed = false; + bool any_visible = false; + for (const int grid : BKE_pbvh_node_get_grid_indices(*node)) { + MutableBoundedBitSpan gh = grid_hidden[grid]; + const BitVector<1024> old_hide(gh); + calc_hide(grid, gh); + changed |= !bits::spans_equal(old_hide, gh); + any_visible |= bits::any_bit_unset(gh); + } + if (!changed) { + continue; + } + any_changed = true; + + SCULPT_undo_push_node(&object, node, SCULPT_UNDO_HIDDEN); + + /* Don't tag a visibility update, we handle updating the fully hidden status here. */ + BKE_pbvh_node_mark_rebuild_draw(node); + BKE_pbvh_node_fully_hidden_set(node, !any_visible); + } + }); + + if (any_changed) { + multires_mark_as_modified(&depsgraph, &object, MULTIRES_HIDDEN_MODIFIED); + BKE_pbvh_sync_visibility_from_verts(&pbvh, &mesh); + } +} + +static void partialvis_update_grids(Depsgraph &depsgraph, + Object &object, const VisAction action, const VisArea area, const float planes[4][4], const Span nodes) { - SubdivCCG &subdiv_ccg = *ob->sculpt->subdiv_ccg; - const Span grids = subdiv_ccg.grids; - const CCGKey key = *BKE_pbvh_get_grid_key(pbvh); - MutableSpan grid_hidden = subdiv_ccg.grid_hidden; - - for (PBVHNode *node : nodes) { - bool any_changed = false; - bool any_visible = false; - - SCULPT_undo_push_node(ob, node, SCULPT_UNDO_HIDDEN); - - for (const int grid_index : BKE_pbvh_node_get_grid_indices(*node)) { - int any_hidden = 0; - BLI_bitmap *gh = grid_hidden[grid_index]; - if (!gh) { - switch (action) { - case VisAction::Hide: - /* Create grid flags data. */ - gh = grid_hidden[grid_index] = BLI_BITMAP_NEW(key.grid_area, - "partialvis_update_grids"); - break; - case VisAction::Show: - /* Entire grid is visible, nothing to show. */ - continue; - } - } - else if (action == VisAction::Show && area == VisArea::All) { - /* Special case if we're showing all, just free the grid. */ - MEM_freeN(gh); - grid_hidden[grid_index] = nullptr; - any_changed = true; - any_visible = true; - continue; - } - - for (int y = 0; y < key.grid_size; y++) { - for (int x = 0; x < key.grid_size; x++) { - CCGElem *elem = CCG_grid_elem(&key, grids[grid_index], x, y); - const float *co = CCG_elem_co(&key, elem); - float mask = key.has_mask ? *CCG_elem_mask(&key, elem) : 0.0f; - - /* Skip grid element if not in the effected area. */ - if (is_effected(area, planes, co, mask)) { - /* Set or clear the hide flag. */ - BLI_BITMAP_SET(gh, y * key.grid_size + x, action == VisAction::Hide); - - any_changed = true; - } - - /* Keep track of whether any elements are still hidden. */ - if (BLI_BITMAP_TEST(gh, y * key.grid_size + x)) { - any_hidden = true; - } - else { - any_visible = true; - } - } - } - - /* If everything in the grid is now visible, free the grid flags. */ - if (!any_hidden) { - MEM_freeN(gh); - grid_hidden[grid_index] = nullptr; - } - } - - /* Mark updates if anything was hidden/shown. */ - if (any_changed) { - BKE_pbvh_node_mark_rebuild_draw(node); - BKE_pbvh_node_fully_hidden_set(node, !any_visible); - multires_mark_as_modified(depsgraph, ob, MULTIRES_HIDDEN_MODIFIED); - } + PBVH &pbvh = *object.sculpt->pbvh; + SubdivCCG &subdiv_ccg = *object.sculpt->subdiv_ccg; + if (action == VisAction::Show && area == VisArea::All) { + grids_show_all(depsgraph, object, nodes); + return; } - BKE_pbvh_sync_visibility_from_verts(pbvh, static_cast(ob->data)); + const bool value = action_to_hide(action); + switch (area) { + case VisArea::Inside: + case VisArea::Outside: { + const CCGKey key = *BKE_pbvh_get_grid_key(&pbvh); + const Span grids = subdiv_ccg.grids; + grid_hide_update( + depsgraph, object, nodes, [&](const int grid_index, MutableBoundedBitSpan hide) { + CCGElem *grid = grids[grid_index]; + for (const int y : IndexRange(key.grid_size)) { + for (const int x : IndexRange(key.grid_size)) { + CCGElem *elem = CCG_grid_elem(&key, grid, x, y); + if (isect_point_planes_v3(planes, 4, CCG_elem_co(&key, elem))) { + hide[y * key.grid_size + x].set(value); + } + } + } + }); + break; + } + case VisArea::All: + switch (action) { + case VisAction::Hide: + grid_hide_update( + depsgraph, object, nodes, [&](const int /*verts*/, MutableBoundedBitSpan hide) { + hide.fill(true); + }); + break; + case VisAction::Show: + grids_show_all(depsgraph, object, nodes); + break; + } + break; + case VisArea::Masked: { + const CCGKey key = *BKE_pbvh_get_grid_key(&pbvh); + const Span grids = subdiv_ccg.grids; + if (!key.has_mask) { + grid_hide_update( + depsgraph, object, nodes, [&](const int /*verts*/, MutableBoundedBitSpan hide) { + hide.fill(value); + }); + } + else { + grid_hide_update( + depsgraph, object, nodes, [&](const int grid_index, MutableBoundedBitSpan hide) { + CCGElem *grid = grids[grid_index]; + for (const int y : IndexRange(key.grid_size)) { + for (const int x : IndexRange(key.grid_size)) { + CCGElem *elem = CCG_grid_elem(&key, grid, x, y); + if (*CCG_elem_mask(&key, elem) > 0.5f) { + hide[y * key.grid_size + x].set(value); + } + } + } + }); + } + break; + } + } } static void partialvis_update_bmesh_verts(BMesh *bm, @@ -464,10 +522,10 @@ static int hide_show_exec(bContext *C, wmOperator *op) switch (pbvh_type) { case PBVH_FACES: - partialvis_update_mesh(*ob, *pbvh, action, area, clip_planes, nodes); + partialvis_update_mesh(*ob, action, area, clip_planes, nodes); break; case PBVH_GRIDS: - partialvis_update_grids(depsgraph, ob, pbvh, action, area, clip_planes, nodes); + partialvis_update_grids(*depsgraph, *ob, action, area, clip_planes, nodes); break; case PBVH_BMESH: partialvis_update_bmesh(ob, pbvh, action, area, clip_planes, nodes); diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index 949714d5630..e1d76e99145 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -417,9 +417,8 @@ bool SCULPT_vertex_visible_get(const SculptSession *ss, PBVHVertRef vertex) const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); const int grid_index = vertex.i / key->grid_area; const int vertex_index = vertex.i - grid_index * key->grid_area; - const blender::Span grid_hidden = ss->subdiv_ccg->grid_hidden; - if (grid_hidden[grid_index]) { - return !BLI_BITMAP_TEST(grid_hidden[grid_index], vertex_index); + if (!ss->subdiv_ccg->grid_hidden.is_empty()) { + return !ss->subdiv_ccg->grid_hidden[grid_index][vertex_index]; } } } diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.hh b/source/blender/editors/sculpt_paint/sculpt_intern.hh index 399887683c8..6d098681260 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.hh +++ b/source/blender/editors/sculpt_paint/sculpt_intern.hh @@ -200,7 +200,7 @@ struct SculptUndoNode { int maxgrid; /* same for grid */ int gridsize; /* same for grid */ blender::Array grids; /* to restore into right location */ - BLI_bitmap **grid_hidden; + blender::BitGroupVector<> grid_hidden; /* bmesh */ BMLogEntry *bm_entry; diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.cc b/source/blender/editors/sculpt_paint/sculpt_undo.cc index a10db0ea139..262c4cca465 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_undo.cc @@ -510,10 +510,19 @@ static bool sculpt_undo_restore_hidden(bContext *C, SculptUndoNode *unode, bool hide_vert.finish(); } else if (!unode->grids.is_empty() && subdiv_ccg != nullptr) { + if (unode->grid_hidden.is_empty()) { + BKE_subdiv_ccg_grid_hidden_free(*subdiv_ccg); + return true; + } + + blender::BitGroupVector<> &grid_hidden = BKE_subdiv_ccg_grid_hidden_ensure(*subdiv_ccg); const Span grids = unode->grids; - blender::MutableSpan grid_hidden = subdiv_ccg->grid_hidden; for (const int i : grids.index_range()) { - SWAP(BLI_bitmap *, unode->grid_hidden[i], grid_hidden[grids[i]]); + const int grid_index = grids[i]; + /* Swap the two bit spans. */ + blender::BitVector<512> tmp(grid_hidden[grid_index]); + grid_hidden[grid_index].copy_from(unode->grid_hidden[i].as_span()); + unode->grid_hidden[i].copy_from(tmp); } } @@ -1087,13 +1096,6 @@ static void sculpt_undo_free_list(ListBase *lb) while (unode != nullptr) { SculptUndoNode *unode_next = unode->next; - if (unode->grid_hidden) { - for (const int i : unode->grids.index_range()) { - MEM_SAFE_FREE(unode->grid_hidden[i]); - } - MEM_freeN(unode->grid_hidden); - } - if (unode->bm_entry) { BM_log_entry_drop(unode->bm_entry); } @@ -1162,25 +1164,20 @@ SculptUndoNode *SCULPT_undo_get_first_node() static size_t sculpt_undo_alloc_and_store_hidden(SculptSession *ss, SculptUndoNode *unode) { PBVHNode *node = static_cast(unode->node); - const blender::Span grid_hidden = ss->subdiv_ccg->grid_hidden; - - const Span grid_indices = BKE_pbvh_node_get_grid_indices(*node); - - size_t alloc_size = sizeof(*unode->grid_hidden) * grid_indices.size(); - unode->grid_hidden = static_cast(MEM_callocN(alloc_size, "unode->grid_hidden")); - - for (const int i : grid_indices.index_range()) { - const int grid_index = grid_indices[i]; - if (grid_hidden[grid_index]) { - unode->grid_hidden[i] = static_cast(MEM_dupallocN(grid_hidden[grid_index])); - alloc_size += MEM_allocN_len(unode->grid_hidden[i]); - } - else { - unode->grid_hidden[i] = nullptr; - } + if (!ss->subdiv_ccg) { + return 0; + } + const blender::BitGroupVector<> grid_hidden = ss->subdiv_ccg->grid_hidden; + if (grid_hidden.is_empty()) { + return 0; } - return alloc_size; + const Span grid_indices = BKE_pbvh_node_get_grid_indices(*node); + for (const int i : grid_indices.index_range()) { + unode->grid_hidden[i].copy_from(grid_hidden[grid_indices[i]]); + } + + return unode->grid_hidden.all_bits().full_ints_num() / blender::bits::BitsPerInt; } /* Allocate node and initialize its default fields specific for the given undo type.