Blender V4.3
outliner_treehash.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
11#include <cstdlib>
12#include <cstring>
13
14#include "BLI_mempool.h"
15#include "BLI_utildefines.h"
16#include "BLI_vector.hh"
17
18#include "DNA_outliner_types.h"
19
21
22#include "MEM_guardedalloc.h"
23
25
26/* -------------------------------------------------------------------- */
30class TseGroup {
31 public:
33 /* Index of last used #TreeStoreElem item, to speed up search for another one. */
34 int lastused = 0;
35 /* Counter used to reduce the amount of 'rests' of `lastused` index, otherwise search for unused
36 * item is exponential and becomes critically slow when there are a lot of items in the group. */
38
39 void add_element(TreeStoreElem &elem);
40 void remove_element(TreeStoreElem &elem);
41};
42
43/* Only allow reset of #TseGroup.lastused counter to 0 once every 1k search. */
44#define TSEGROUP_LASTUSED_RESET_VALUE 10000
45
47{
48 const int64_t idx = elems.append_and_get_index(&elem);
49 lastused = idx;
50}
51
53{
54 const int64_t idx = elems.first_index_of(&elem);
55 elems.remove(idx);
56}
57
60/* -------------------------------------------------------------------- */
65 : id(elem.id), type(elem.type), nr(elem.nr)
66{
67}
68
69TreeStoreElemKey::TreeStoreElemKey(ID *id, short type, short nr) : id(id), type(type), nr(nr) {}
70
72{
73 return get_default_hash(id, type, nr);
74}
75
77{
78 return (a.id == b.id) && (a.type == b.type) && (a.nr == b.nr);
79}
80
83/* -------------------------------------------------------------------- */
87TreeHash::~TreeHash() = default;
88
89std::unique_ptr<TreeHash> TreeHash::create_from_treestore(BLI_mempool &treestore)
90{
91 /* Can't use `make_unique()` here because of private constructor. */
92 std::unique_ptr<TreeHash> tree_hash{new TreeHash()};
93 tree_hash->fill_treehash(treestore);
94
95 return tree_hash;
96}
97
98void TreeHash::fill_treehash(BLI_mempool &treestore)
99{
100 TreeStoreElem *tselem;
101 BLI_mempool_iter iter;
102 BLI_mempool_iternew(&treestore, &iter);
103
104 while ((tselem = static_cast<TreeStoreElem *>(BLI_mempool_iterstep(&iter)))) {
105 add_element(*tselem);
106 }
107}
108
110{
111 for (auto &group : elem_groups_.values()) {
112 group->lastused = 0;
113 group->lastused_reset_count = 0;
114 }
115}
116
118{
119 elem_groups_.clear();
120 fill_treehash(treestore);
121}
122
124{
125 std::unique_ptr<TseGroup> &group = elem_groups_.lookup_or_add_cb(
126 TreeStoreElemKey(elem), []() { return std::make_unique<TseGroup>(); });
127 group->add_element(elem);
128}
129
131{
132 TseGroup *group = lookup_group(elem);
133 BLI_assert(group != nullptr);
134
135 if (group->elems.size() <= 1) {
136 /* One element -> remove group completely. */
137 elem_groups_.remove(TreeStoreElemKey(elem));
138 }
139 else {
140 group->remove_element(elem);
141 }
142}
143
144TseGroup *TreeHash::lookup_group(const TreeStoreElemKey &key) const
145{
146 const auto *group = elem_groups_.lookup_ptr(key);
147 if (group) {
148 return group->get();
149 }
150 return nullptr;
151}
152
153TseGroup *TreeHash::lookup_group(const TreeStoreElem &elem) const
154{
155 return lookup_group(TreeStoreElemKey(elem));
156}
157
158TseGroup *TreeHash::lookup_group(const short type, const short nr, ID *id) const
159{
160 TreeStoreElemKey key(id, type, nr);
161 if (type == TSE_SOME_ID) {
162 key.nr = 0; /* we're picky! :) */
163 }
164 return lookup_group(key);
165}
166
167TreeStoreElem *TreeHash::lookup_unused(const short type, const short nr, ID *id) const
168{
169 TseGroup *group = lookup_group(type, nr, id);
170 if (!group) {
171 return nullptr;
172 }
173
174 /* Find unused element, with optimization to start from previously
175 * found element assuming we do repeated lookups. */
176 const int size = group->elems.size();
177 int offset = group->lastused;
178
179 for (int i = 0; i < size; i++, offset++) {
180 /* Once at the end of the array of items, in most cases it just means that all items are
181 * used, so only check the whole array once every TSEGROUP_LASTUSED_RESET_VALUE times. */
182 if (offset >= size) {
183 if (LIKELY(group->lastused_reset_count <= TSEGROUP_LASTUSED_RESET_VALUE)) {
184 group->lastused_reset_count++;
185 group->lastused = group->elems.size() - 1;
186 break;
187 }
188 group->lastused_reset_count = 0;
189 offset = 0;
190 }
191
192 if (!group->elems[offset]->used) {
193 group->lastused = offset;
194 return group->elems[offset];
195 }
196 }
197 return nullptr;
198}
199
200TreeStoreElem *TreeHash::lookup_any(const short type, const short nr, ID *id) const
201{
202 const TseGroup *group = lookup_group(type, nr, id);
203 return group ? group->elems[0] : nullptr;
204}
205
208} // namespace blender::bke::outliner::treehash
#define BLI_assert(a)
Definition BLI_assert.h:50
void BLI_mempool_iternew(BLI_mempool *pool, BLI_mempool_iter *iter) ATTR_NONNULL()
void * BLI_mempool_iterstep(BLI_mempool_iter *iter) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
#define LIKELY(x)
@ TSE_SOME_ID
Read Guarded memory(de)allocation.
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
int64_t append_and_get_index(const T &value)
void remove(const int64_t index)
int64_t first_index_of(const T &value) const
void rebuild_from_treestore(BLI_mempool &treestore)
TreeStoreElem * lookup_unused(short type, short nr, ID *id) const
TreeStoreElem * lookup_any(short type, short nr, ID *id) const
static std::unique_ptr< TreeHash > create_from_treestore(BLI_mempool &treestore)
blender::Vector< TreeStoreElem * > elems
local_group_size(16, 16) .push_constant(Type b
bool operator==(const TreeStoreElemKey &a, const TreeStoreElemKey &b)
uint64_t get_default_hash(const T &v)
Definition BLI_hash.hh:219
#define TSEGROUP_LASTUSED_RESET_VALUE
__int64 int64_t
Definition stdint.h:89
unsigned __int64 uint64_t
Definition stdint.h:90
Definition DNA_ID.h:413