Blender V4.3
volume_grid_file_cache.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Foundation
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#ifdef WITH_OPENVDB
6
8# include "BKE_volume_openvdb.hh"
9
10# include "BLI_map.hh"
11# include "BLI_memory_cache.hh"
12# include "BLI_memory_counter.hh"
13
14# include <openvdb/openvdb.h>
15
16namespace blender::bke::volume_grid::file_cache {
17
21struct GridCache {
26 openvdb::GridBase::Ptr meta_data_grid;
30 Map<int, GVolumeGrid> grid_by_simplify_level;
31};
32
36struct FileCache {
40 std::string error_message;
44 openvdb::MetaMap meta_data;
48 Vector<GridCache> grids;
49
50 GridCache *grid_cache_by_name(const StringRef name)
51 {
52 for (GridCache &grid_cache : this->grids) {
53 if (grid_cache.meta_data_grid->getName() == name) {
54 return &grid_cache;
55 }
56 }
57 return nullptr;
58 }
59};
60
64struct GlobalCache {
65 std::mutex mutex;
66 Map<std::string, FileCache> file_map;
67};
68
72static GlobalCache &get_global_cache()
73{
74 static GlobalCache global_cache;
75 return global_cache;
76}
77
82static FileCache create_file_cache(const StringRef file_path)
83{
84 FileCache file_cache;
85
86 openvdb::io::File file(file_path);
87 openvdb::GridPtrVec vdb_grids;
88 try {
89 /* Disable delay loading and file copying, this has poor performance
90 * on network drives. */
91 const bool delay_load = false;
92 file.setCopyMaxBytes(0);
93 file.open(delay_load);
94 vdb_grids = *(file.readAllGridMetadata());
95 file_cache.meta_data = *file.getMetadata();
96 }
97 catch (const openvdb::IoError &e) {
98 file_cache.error_message = e.what();
99 }
100 catch (...) {
101 file_cache.error_message = "Unknown error reading VDB file";
102 }
103 if (!file_cache.error_message.empty()) {
104 return file_cache;
105 }
106
107 for (openvdb::GridBase::Ptr &vdb_grid : vdb_grids) {
108 if (!vdb_grid) {
109 continue;
110 }
111 GridCache grid_cache;
112 grid_cache.meta_data_grid = vdb_grid;
113 file_cache.grids.append(std::move(grid_cache));
114 }
115
116 return file_cache;
117}
118
119static FileCache &get_file_cache(const StringRef file_path)
120{
121 GlobalCache &global_cache = get_global_cache();
122 /* Assumes that the cache is locked already. */
123 BLI_assert(!global_cache.mutex.try_lock());
124 return global_cache.file_map.lookup_or_add_cb_as(file_path,
125 [&]() { return create_file_cache(file_path); });
126}
127
131class GridReadKey : public GenericKey {
132 public:
133 std::string file_path;
134 std::string grid_name;
135 int simplify_level;
136
137 uint64_t hash() const override
138 {
139 return get_default_hash(this->file_path, this->grid_name, this->simplify_level);
140 }
141
142 BLI_STRUCT_EQUALITY_OPERATORS_3(GridReadKey, file_path, grid_name, simplify_level)
143
144 bool equal_to(const GenericKey &other) const override
145 {
146 if (const auto *other_typed = dynamic_cast<const GridReadKey *>(&other)) {
147 return *this == *other_typed;
148 }
149 return false;
150 }
151
152 std::unique_ptr<GenericKey> to_storable() const override
153 {
154 return std::make_unique<GridReadKey>(*this);
155 }
156};
157
158class GridReadValue : public memory_cache::CachedValue {
159 private:
160 mutable std::atomic<int64_t> bytes_ = 0;
161
162 public:
163 ImplicitSharingPtr<> tree_sharing_info;
164 openvdb::GridBase::Ptr grid;
165
166 void count_memory(MemoryCounter &memory) const override
167 {
168 /* Avoid computing the amount of memory from scratch every time. */
169 if (bytes_ == 0) {
170 this->bytes_ = grid->baseTree().memUsage();
171 }
172 memory.add(bytes_);
173 }
174};
175
180static openvdb::GridBase::Ptr load_single_grid_from_disk(const StringRef file_path,
181 const StringRef grid_name)
182{
183 /* Disable delay loading and file copying, this has poor performance
184 * on network drivers. */
185 const bool delay_load = false;
186
187 openvdb::io::File file(file_path);
188 file.setCopyMaxBytes(0);
189 file.open(delay_load);
190 return file.readGrid(grid_name);
191}
192
197static LazyLoadedGrid load_single_grid_from_disk_cached(const StringRef file_path,
198 const StringRef grid_name,
199 const int simplify_level)
200{
201 GridReadKey key;
202 key.file_path = file_path;
203 key.grid_name = grid_name;
204 key.simplify_level = simplify_level;
205
206 std::shared_ptr<const GridReadValue> value = memory_cache::get<GridReadValue>(key, [&key]() {
207 openvdb::GridBase::Ptr grid;
208 if (key.simplify_level == 0) {
209 grid = load_single_grid_from_disk(key.file_path, key.grid_name);
210 }
211 else {
212 /* Build the simplified grid from the main grid. */
213 const GVolumeGrid main_grid = get_grid_from_file(key.file_path, key.grid_name, 0);
214 const VolumeGridType grid_type = main_grid->grid_type();
215 const float resolution_factor = 1.0f / (1 << key.simplify_level);
216 VolumeTreeAccessToken tree_token;
217 grid = BKE_volume_grid_create_with_changed_resolution(
218 grid_type, main_grid->grid(tree_token), resolution_factor);
219 }
220 auto value = std::make_unique<GridReadValue>();
221 value->grid = std::move(grid);
222 value->tree_sharing_info = OpenvdbTreeSharingInfo::make(value->grid->baseTreePtr());
223 return value;
224 });
225 if (!value) {
226 return {};
227 }
228
229 /* Copy the grid so that it has a single owner. Note that the tree is still shared. */
230 openvdb::GridBase::Ptr grid = value->grid->copyGrid();
231 grid->setTransform(grid->transform().copy());
232 return {grid, value->tree_sharing_info};
233}
234
239static GVolumeGrid get_cached_grid(const StringRef file_path,
240 GridCache &grid_cache,
241 const int simplify_level)
242{
243 if (GVolumeGrid *grid = grid_cache.grid_by_simplify_level.lookup_ptr(simplify_level)) {
244 return *grid;
245 }
246 /* A callback that actually loads the full grid including the tree when it's accessed. */
247 auto load_grid_fn = [file_path = std::string(file_path),
248 grid_name = std::string(grid_cache.meta_data_grid->getName()),
249 simplify_level]() -> LazyLoadedGrid {
250 return load_single_grid_from_disk_cached(file_path, grid_name, simplify_level);
251 };
252 /* This allows the returned grid to already contain meta-data and transforms, even if the tree is
253 * not loaded yet. */
254 openvdb::GridBase::Ptr meta_data_and_transform_grid;
255 if (simplify_level == 0) {
256 /* Only pass the meta-data grid when there is no simplification for now. For simplified grids,
257 * the transform would have to be updated here already. */
258 meta_data_and_transform_grid = grid_cache.meta_data_grid->copyGrid();
259 }
260 VolumeGridData *grid_data = MEM_new<VolumeGridData>(
261 __func__, load_grid_fn, meta_data_and_transform_grid);
262 GVolumeGrid grid{grid_data};
263 grid_cache.grid_by_simplify_level.add(simplify_level, grid);
264 return grid;
265}
266
267GVolumeGrid get_grid_from_file(const StringRef file_path,
268 const StringRef grid_name,
269 const int simplify_level)
270{
271 GlobalCache &global_cache = get_global_cache();
272 std::lock_guard lock{global_cache.mutex};
273 FileCache &file_cache = get_file_cache(file_path);
274 if (GridCache *grid_cache = file_cache.grid_cache_by_name(grid_name)) {
275 return get_cached_grid(file_path, *grid_cache, simplify_level);
276 }
277 return {};
278}
279
280GridsFromFile get_all_grids_from_file(const StringRef file_path, const int simplify_level)
281{
282 GridsFromFile result;
283 GlobalCache &global_cache = get_global_cache();
284 std::lock_guard lock{global_cache.mutex};
285 FileCache &file_cache = get_file_cache(file_path);
286
287 if (!file_cache.error_message.empty()) {
288 result.error_message = file_cache.error_message;
289 return result;
290 }
291 result.file_meta_data = std::make_shared<openvdb::MetaMap>(file_cache.meta_data);
292 for (GridCache &grid_cache : file_cache.grids) {
293 result.grids.append(get_cached_grid(file_path, grid_cache, simplify_level));
294 }
295 return result;
296}
297
298void unload_unused()
299{
300 GlobalCache &global_cache = get_global_cache();
301 std::lock_guard lock{global_cache.mutex};
302 for (FileCache &file_cache : global_cache.file_map.values()) {
303 for (GridCache &grid_cache : file_cache.grids) {
304 grid_cache.grid_by_simplify_level.remove_if(
305 [&](const auto &item) { return item.value->is_mutable(); });
306 }
307 }
308}
309
310} // namespace blender::bke::volume_grid::file_cache
311
312#endif /* WITH_OPENVDB */
#define BLI_assert(a)
Definition BLI_assert.h:50
#define BLI_STRUCT_EQUALITY_OPERATORS_3(Type, m1, m2, m3)
ThreadMutex mutex
volatile int lock
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
FILE * file
void count_memory(const VolumeGridData &grid, MemoryCounter &memory)
uint64_t get_default_hash(const T &v)
Definition BLI_hash.hh:219
#define hash
Definition noise.c:154
unsigned __int64 uint64_t
Definition stdint.h:90