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;
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 */
VolumeGridType
#define BLI_assert(a)
Definition BLI_assert.h:50
#define BLI_STRUCT_EQUALITY_OPERATORS_3(Type, m1, m2, m3)
ThreadMutex mutex
in reality light always falls off quadratically Particle Retrieve the data of the particle that spawned the object for example to give variation to multiple instances of an object Point Retrieve information about points in a point cloud Retrieve the edges of an object as it appears to Cycles topology will always appear triangulated Convert a blackbody temperature to an RGB value Normal Map
volatile int lock
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
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