# 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.