# Volume Grid API [Proposal]
```cpp
class VolumeGridData : public ImplicitSharingMixin {
private:
/**
* OpenVDB grid which contains a tree, transform and meta-data. The `std::shared_ptr`
* is only used because it makes it easier to use some OpenVDB APIs. It's not used
* to actually support shared ownership, because that's what implicit sharing is used
* for.
*
* `grid_mutex_` has to be locked before accessing this to make lazy-loading thread-safe.
*/
mutable std::shared_ptr<openvdb::GridBase> grid_;
/**
* Sharing info for the tree referenced by the grid above. This allows sharing the
* same tree (which is the heavy data) between multiple volume grids with different
* transforms and meta-data.
*/
const ImplicitSharingInfo *tree_sharing_info_;
/**
* Used to support lazy-loading of grids.
*/
mutable std::mutex grid_mutex_;
/**
* Can be used to load the grid if it's not loaded already.
*/
std::function<std::shared_ptr<openvdb::GridBase>()> lazy_load_grid_;
public:
/**
* Takes unique ownership of the passed in grid.
*/
explicit VolumeGridData(std::shared_ptr<openvdb::GridBase> grid);
/**
* Creates a grid whose underlying data may be lazy-loaded on demand.
*/
explicit VolumeGridData(
std::function<std::shared_ptr<openvdb::GridBase>()> lazy_load_grid);
/**
* Creates a copy of the #VolumeGridData. The copy can still implicitly share the
* underlying `openvdb::Tree`.
*/
GVolumeGrid copy() const;
/**
* Get the grid for read-only access.
*/
const openvdb::GridBase &grid() const;
/**
* Get the grid for read- and write-access.
*/
openvdb::GridBase &grid_for_write();
/**
* Same as above but can be used when the returned grid is used by an API
* function that only supports grids wrapped into a `std::shared_ptr`.
* The #VolumeGridData is still expected to be the only owner of the grid.
*/
std::shared_ptr<const openvdb::GridBase> grid_ptr() const;
std::shared_ptr<openvdb::GridBase> grid_ptr_for_write();
};
class GVolumeGrid {
private:
ImplicitSharingPtr<VolumeGridData> data_;
public:
GVolumeGrid() = default;
explicit GVolumeGrid(const VolumeGridData *data);
/**
* Utility constructor that avoids manually building the #VolumeGridData.
*/
explicit GVolumeGrid(std::shared_ptr<openvdb::GridBase> grid);
/**
* Get read-only access to the volume grid.
*/
const VolumeGridData *get() const;
/**
* Get read and write access to the volume grid.
*/
VolumeGridData *get_for_write();
/**
* Gives simple read-only access to the underlying grid data.
*/
const VolumeGridData *operator->() const;
/**
* False when this grid is empty, true otherwise.
*/
operator bool() const;
};
template<typename T>
class VolumeGrid : public GVolumeGrid {
public:
const openvdb::Grid<T> &grid() const;
openvdb::Grid<T> &grid_for_write();
std::shared_ptr<const openvdb::Grid<T>> grid_ptr() const;
std::shared_ptr<openvdb::Grid<T>> grid_ptr_for_write();
};
/* This uses a namespace insead of a class because this is global. */
namespace volume_file_cache {
struct CacheKey {
std::string filepath;
std::string grid_name;
int simplify_level = 0;
};
/**
* Returns a grid that references the grid on disk. The actual grid data may only
* be loaded lazily on demand.
*/
GVolumeGrid get(const CacheKey &key);
/**
* Remove all grids from the cache that are currently only used by the cache.
*/
void unloaded_unused();
} // namespace volume_file_cache
```
Notes:
* There are two levels of implicit sharing here.
* The same `openvdb::Tree` may be shared between multiple `VolumeGridData` (this uses "External ImplicitSharingInfo" from the [Implicit Sharing API docs](https://hackmd.io/@II9-Bkl4TJifCqGL2jgbUw/ryFvCQyIp)):
* Owner: `VolumeGridData`
* Data: `openvdb::Tree`
* The same `VolumeGridData` may be shared between multiple `GVolumeGrid`:
* Owner: `GVolumeGrid` and `VolumeGrid<T>`
* Data: `VolumeGridData`
* One thing this does not support yet is unloading grids that still have owners outside of the file cache.
* This does not seem necessary now, but could still be added later by adding another level of indirection for accessing grid data.
* `VolumeGridData::new_grid_user() -> VolumeGridUser` + `VolumeGridUser::grid() -> const openvdb::GridBase &`
* `VolumeGridData` could now keep track of current users of the grid data and potentially unload it if there are no users left.