Volumes: refactor volume grid storage

This refactors how volume grids are stored with the following new goals in mind:
* Get a **stand-alone volume grid** data structure that can be used by geometry nodes.
  Previously, the `VolumeGrid` data structure was tightly coupled with the `Volume` data block.
* Support **implicit sharing of grids and trees**. Previously, it was possible to share data
  when multiple `Volume` data blocks loaded grids from the same `.vdb` files but this was
  not flexible enough.
* Get a safe API for **lazy-loading and unloading** of grids without requiring explicit calls
  to some "load" function all the time.
* Get a safe API for **caching grids from files** that is not coupled to the `Volume` data block.
* Get a **tiered API** for different levels of `openvdb` involvement:
  * No `OpenVDB`: Since `WITH_OPENVDB` is optional, it's helpful to have parts of the API that
    still work in this case. This makes it possible to write high level code for volumes that does
    not require `#ifdef WITH_OPENVDB` checks everywhere. This is in `BKE_volume_grid_fwd.hh`.
  * Shallow `OpenVDB`: Code using this API requires `WITH_OPENVDB` checks. However, care
  is taken to not include the expensive parts of `OpenVDB` and to use forward declarations as
  much as possible. This is in `BKE_volume_grid.hh` and uses `openvdb_fwd.hh`.
  * "Full" `OpenVDB`: This API requires more heavy `OpenVDB` includes. Fortunately, it turned
  out to be not necessary for the common API. So this is only used for task specific APIs.

At the core of the new API is the `VolumeGridData` type. It's a wrapper around an
`openvdb::Grid` and adds some features on top like implicit sharing, lazy-loading and unloading.
Then there are `GVolumeGrid` and `VolumeGrid` which are containers for a volume grid.
Semantically, each `VolumeGrid` has its own independent grid, but this is cheap due to implicit
sharing. At highest level we currently have the `Volume` data-block which contains a list of
`VolumeGrid`.

```mermaid
flowchart LR
  Volume --> VolumeGrid --> VolumeGridData --> openvdb::Grid
```

The loading of `.vdb` files is abstracted away behind the volume file cache API. This API makes
it easy to load and reuse entire files and individual grids from disk. It also supports caching
simplify levels for grids on disk.

An important new concept are the "tree access tokens". Whenever some code wants to work
with an openvdb tree, it has to retrieve an access token from the corresponding `VolumeGridData`.
This access token has to be kept alive for as long as the code works with the grid data. The same
token is valid for read and write access. The purpose of these access tokens is to make it possible
to detect when some code is currently working with the openvdb tree. This allows freeing it if it's
possible to reload it later on (e.g. from disk). It's possible to free a tree that is referenced by
multiple owners, but only no one is actively working with. In some sense, this is similar to the
existing `ImageUser` concept.

The most important new files to read are `BKE_volume_grid.hh` and `BKE_volume_grid_file_cache.hh`.
Most other changes are updates to existing code to use the new API.

Pull Request: https://projects.blender.org/blender/blender/pulls/116315
This commit is contained in:
Jacques Lucke
2023-12-20 15:32:52 +01:00
parent 5f527fdc89
commit a72e7a220d
45 changed files with 2079 additions and 925 deletions

View File

@@ -11,6 +11,7 @@ set(INC_SYS
set(SRC
openvdb_capi.h
openvdb_fwd.hh
)
set(LIB

View File

@@ -0,0 +1,111 @@
/* SPDX-FileCopyrightText: 2023 Blender Foundation
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/* Note: version header included here to enable correct forward declaration of some types. No other
* OpenVDB headers should be included here, especially openvdb.h, to avoid affecting other
* compilation units. */
#include <openvdb/Types.h>
#include <openvdb/version.h>
/* -------------------------------------------------------------------- */
/** \name OpenVDB Forward Declaration
* \{ */
/* Forward declaration for basic OpenVDB types. */
namespace openvdb {
OPENVDB_USE_VERSION_NAMESPACE
namespace OPENVDB_VERSION_NAME {
class GridBase;
class MetaMap;
template<typename TreeType> class Grid;
namespace math {
class Transform;
}
namespace tree {
class TreeBase;
template<typename T, Index Log2Dim> class LeafNode;
template<typename ChildNodeType, Index Log2Dim> class InternalNode;
template<typename ChildNodeType> class RootNode;
template<typename RootNodeType> class Tree;
/* Forward-declared version of Tree4, can't use the actual Tree4 alias because it can't be
* forward-declared. */
template<typename T, Index N1 = 5, Index N2 = 4, Index N3 = 3> struct Tree4Fwd {
using Type = openvdb::tree::Tree<openvdb::tree::RootNode<
openvdb::tree::InternalNode<openvdb::tree::InternalNode<openvdb::tree::LeafNode<T, N3>, N2>,
N1>>>;
};
} // namespace tree
namespace tools {
template<typename T, Index Log2Dim> struct PointIndexLeafNode;
using PointIndexTree = tree::Tree<tree::RootNode<
tree::InternalNode<tree::InternalNode<PointIndexLeafNode<PointIndex32, 3>, 4>, 5>>>;
using PointIndexGrid = Grid<PointIndexTree>;
} // namespace tools
namespace points {
template<typename T, Index Log2Dim> class PointDataLeafNode;
using PointDataTree = tree::Tree<tree::RootNode<
tree::InternalNode<tree::InternalNode<PointDataLeafNode<PointDataIndex32, 3>, 4>, 5>>>;
using PointDataGrid = Grid<PointDataTree>;
struct NullCodec;
template<typename ValueType, typename Codec> class TypedAttributeArray;
} // namespace points
/// Common tree types
using BoolTree = tree::Tree4Fwd<bool, 5, 4, 3>::Type;
using DoubleTree = tree::Tree4Fwd<double, 5, 4, 3>::Type;
using FloatTree = tree::Tree4Fwd<float, 5, 4, 3>::Type;
using Int8Tree = tree::Tree4Fwd<int8_t, 5, 4, 3>::Type;
using Int32Tree = tree::Tree4Fwd<int32_t, 5, 4, 3>::Type;
using Int64Tree = tree::Tree4Fwd<int64_t, 5, 4, 3>::Type;
using MaskTree = tree::Tree4Fwd<ValueMask, 5, 4, 3>::Type;
using UInt32Tree = tree::Tree4Fwd<uint32_t, 5, 4, 3>::Type;
using Vec2DTree = tree::Tree4Fwd<Vec2d, 5, 4, 3>::Type;
using Vec2ITree = tree::Tree4Fwd<Vec2i, 5, 4, 3>::Type;
using Vec2STree = tree::Tree4Fwd<Vec2s, 5, 4, 3>::Type;
using Vec3DTree = tree::Tree4Fwd<Vec3d, 5, 4, 3>::Type;
using Vec3ITree = tree::Tree4Fwd<Vec3i, 5, 4, 3>::Type;
using Vec3STree = tree::Tree4Fwd<Vec3f, 5, 4, 3>::Type;
using Vec4STree = tree::Tree4Fwd<Vec4f, 5, 4, 3>::Type;
using ScalarTree = FloatTree;
using TopologyTree = MaskTree;
using Vec3dTree = Vec3DTree;
using Vec3fTree = Vec3STree;
using Vec4fTree = Vec4STree;
using VectorTree = Vec3fTree;
/// Common grid types
using BoolGrid = Grid<BoolTree>;
using DoubleGrid = Grid<DoubleTree>;
using FloatGrid = Grid<FloatTree>;
using Int8Grid = Grid<Int8Tree>;
using Int32Grid = Grid<Int32Tree>;
using Int64Grid = Grid<Int64Tree>;
using UInt32Grid = Grid<UInt32Tree>;
using MaskGrid = Grid<MaskTree>;
using Vec3DGrid = Grid<Vec3DTree>;
using Vec2IGrid = Grid<Vec2ITree>;
using Vec3IGrid = Grid<Vec3ITree>;
using Vec2SGrid = Grid<Vec2STree>;
using Vec3SGrid = Grid<Vec3STree>;
using Vec4SGrid = Grid<Vec4STree>;
using ScalarGrid = FloatGrid;
using TopologyGrid = MaskGrid;
using Vec3dGrid = Vec3DGrid;
using Vec2fGrid = Vec2SGrid;
using Vec3fGrid = Vec3SGrid;
using Vec4fGrid = Vec4SGrid;
using VectorGrid = Vec3fGrid;
} // namespace OPENVDB_VERSION_NAME
} // namespace openvdb
/** \} */