---
# System prepended metadata

title: Volume Grid API

---

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