16#include <fmt/format.h>
20# include <openvdb/Grid.h>
21# include <openvdb/math/Transform.h>
22# include <openvdb/tools/Merge.h>
32template<
typename Gr
idT>
33static constexpr bool is_supported_grid_type =
is_same_any_v<GridT,
40template<
typename Fn>
static void to_typed_grid(
const openvdb::GridBase &grid_base, Fn &&fn)
43 BKE_volume_grid_type_to_static_type(grid_type, [&](
auto type_tag) {
44 using GridT =
typename decltype(type_tag)::type;
45 if constexpr (is_supported_grid_type<GridT>) {
46 fn(
static_cast<const GridT &
>(grid_base));
54template<
typename Fn>
static void to_typed_grid(openvdb::GridBase &grid_base, Fn &&fn)
57 BKE_volume_grid_type_to_static_type(grid_type, [&](
auto type_tag) {
58 using GridT =
typename decltype(type_tag)::type;
59 if constexpr (is_supported_grid_type<GridT>) {
60 fn(
static_cast<GridT &
>(grid_base));
68static std::optional<VolumeGridType> cpp_type_to_grid_type(
const CPPType &cpp_type)
77using LeafNodeMask = openvdb::util::NodeMask<3u>;
78using GetVoxelsFn = FunctionRef<void(MutableSpan<openvdb::Coord> r_voxels)>;
79using ProcessLeafFn = FunctionRef<void(
const LeafNodeMask &leaf_node_mask,
80 const openvdb::CoordBBox &leaf_bbox,
81 GetVoxelsFn get_voxels_fn)>;
82using ProcessTilesFn = FunctionRef<void(Span<openvdb::CoordBBox>
tiles)>;
83using ProcessVoxelsFn = FunctionRef<void(Span<openvdb::Coord> voxels)>;
89template<
typename LeafNodeT>
90static void parallel_grid_topology_tasks_leaf_node(
const LeafNodeT &node,
91 const ProcessLeafFn process_leaf_fn,
94 using NodeMaskT =
typename LeafNodeT::NodeMaskType;
96 const int on_count = node.onVoxelCount();
100 const int on_count_threshold = 64;
101 if (on_count <= on_count_threshold) {
104 for (
auto value_iter = node.cbeginValueOn(); value_iter.test(); ++value_iter) {
105 const openvdb::Coord coord = value_iter.getCoord();
113 const NodeMaskT &value_mask = node.getValueMask();
114 const openvdb::CoordBBox bbox = node.getNodeBoundingBox();
115 process_leaf_fn(value_mask, bbox, [&](MutableSpan<openvdb::Coord> r_voxels) {
116 for (
auto value_iter = node.cbeginValueOn(); value_iter.test(); ++value_iter) {
117 r_voxels[value_iter.pos()] = value_iter.getCoord();
125template<
typename InternalNodeT>
126static void parallel_grid_topology_tasks_internal_node(
const InternalNodeT &node,
127 const ProcessLeafFn process_leaf_fn,
128 const ProcessVoxelsFn process_voxels_fn,
129 const ProcessTilesFn process_tiles_fn)
131 using ChildNodeT =
typename InternalNodeT::ChildNodeType;
132 using LeafNodeT =
typename InternalNodeT::LeafNodeType;
133 using NodeMaskT =
typename InternalNodeT::NodeMaskType;
134 using UnionT =
typename InternalNodeT::UnionType;
137 const NodeMaskT &child_mask = node.getChildMask();
138 const UnionT *table = node.getTable();
140 for (
auto child_mask_iter = child_mask.beginOn(); child_mask_iter.test(); ++child_mask_iter) {
141 child_indices.
append(child_mask_iter.pos());
148 Vector<openvdb::Coord, 1024> gathered_voxels;
149 for (const int child_index : child_indices.as_span().slice(range)) {
150 const ChildNodeT &child = *table[child_index].getChild();
151 if constexpr (std::is_same_v<ChildNodeT, LeafNodeT>) {
152 parallel_grid_topology_tasks_leaf_node(child, process_leaf_fn, gathered_voxels);
154 if (gathered_voxels.size() >= 512) {
155 process_voxels_fn(gathered_voxels);
156 gathered_voxels.clear();
161 parallel_grid_topology_tasks_internal_node(
162 child, process_leaf_fn, process_voxels_fn, process_tiles_fn);
166 if (!gathered_voxels.is_empty()) {
167 process_voxels_fn(gathered_voxels);
168 gathered_voxels.clear();
175 const NodeMaskT &value_mask = node.getValueMask();
177 for (
auto value_mask_iter = value_mask.beginOn(); value_mask_iter.test(); ++value_mask_iter) {
178 const openvdb::Index32 index = value_mask_iter.pos();
179 const openvdb::Coord tile_origin = node.offsetToGlobalCoord(index);
180 const openvdb::CoordBBox tile_bbox = openvdb::CoordBBox::createCube(tile_origin,
182 tile_bboxes.
append(tile_bbox);
185 process_tiles_fn(tile_bboxes);
190static void parallel_grid_topology_tasks(
const openvdb::MaskTree &mask_tree,
191 const ProcessLeafFn process_leaf_fn,
192 const ProcessVoxelsFn process_voxels_fn,
193 const ProcessTilesFn process_tiles_fn)
196 for (
auto root_child_iter = mask_tree.cbeginRootChildren(); root_child_iter.test();
199 const auto &internal_node = *root_child_iter;
200 parallel_grid_topology_tasks_internal_node(
201 internal_node, process_leaf_fn, process_voxels_fn, process_tiles_fn);
219BLI_NOINLINE static void process_leaf_node(
const mf::MultiFunction &fn,
223 const openvdb::math::Transform &
transform,
224 const LeafNodeMask &leaf_node_mask,
225 const openvdb::CoordBBox &leaf_bbox,
226 const GetVoxelsFn get_voxels_fn)
232 return leaf_node_mask.isOn(
i);
235 AlignedBuffer<8192, 8> allocation_buffer;
237 scope.
allocator().provide_buffer(allocation_buffer);
238 mf::ParamsBuilder
params{fn, &index_mask};
243 const openvdb::Coord any_voxel_in_leaf = leaf_bbox.min();
245 std::optional<MutableSpan<openvdb::Coord>> voxel_coords_opt;
246 auto ensure_voxel_coords = [&]() {
247 if (!voxel_coords_opt.has_value()) {
248 voxel_coords_opt = scope.
allocator().allocate_array<openvdb::Coord>(
250 get_voxels_fn(voxel_coords_opt.value());
252 return *voxel_coords_opt;
255 for (
const int input_i : input_values.
index_range()) {
256 const bke::SocketValueVariant &value_variant = *input_values[input_i];
257 const mf::ParamType param_type = fn.param_type(
params.next_param_index());
258 const CPPType ¶m_cpp_type = param_type.data_type().single_type();
260 if (
const openvdb::GridBase *grid_base = input_grids[input_i]) {
262 to_typed_grid(*grid_base, [&](
const auto &grid) {
263 using GridT =
typename std::decay_t<
decltype(grid)>;
264 using ValueT =
typename GridT::ValueType;
266 const auto &
tree = grid.tree();
268 if (
const auto *leaf_node =
tree.probeLeaf(any_voxel_in_leaf)) {
271 if constexpr (std::is_same_v<ValueT, bool>) {
276 const openvdb::Coord &coord = voxels[
i];
277 values[
i] =
tree.getValue(coord);
279 params.add_readonly_single_input(values);
282 const Span<ValueT> values(leaf_node->buffer().data(), LeafNodeMask::SIZE);
283 const LeafNodeMask &input_leaf_mask = leaf_node->valueMask();
284 const LeafNodeMask missing_mask = leaf_node_mask & !input_leaf_mask;
285 if (missing_mask.isOff()) {
287 params.add_readonly_single_input(
288 GSpan(param_cpp_type, values.
data(), values.
size()));
293 const auto &background =
tree.background();
294 for (
auto missing_it = missing_mask.beginOn(); missing_it.test(); ++missing_it) {
295 const int index = missing_it.pos();
296 copied_values[index] = background;
298 params.add_readonly_single_input(
299 GSpan(param_cpp_type, copied_values.
data(), copied_values.
size()));
307 const auto single_value =
tree.getValue(any_voxel_in_leaf);
308 params.add_readonly_single_input(GPointer(param_cpp_type, &single_value));
312 else if (value_variant.is_context_dependent_field()) {
315 const fn::GField field = value_variant.get<fn::GField>();
316 const CPPType &type = field.cpp_type();
318 bke::VoxelFieldContext field_context{
transform, voxels};
319 fn::FieldEvaluator evaluator{field_context, &index_mask};
322 evaluator.add_with_destination(field, values);
323 evaluator.evaluate();
324 params.add_readonly_single_input(values);
328 params.add_readonly_single_input(value_variant.get_single_ptr());
332 for (
const int output_i : output_grids.
index_range()) {
333 const mf::ParamType param_type = fn.param_type(
params.next_param_index());
334 const CPPType ¶m_cpp_type = param_type.data_type().single_type();
336 openvdb::GridBase &grid_base = *output_grids[output_i];
337 to_typed_grid(grid_base, [&](
auto &grid) {
338 using GridT =
typename std::decay_t<
decltype(grid)>;
339 using ValueT =
typename GridT::ValueType;
341 auto &
tree = grid.tree();
342 auto *leaf_node =
tree.probeLeaf(any_voxel_in_leaf);
347 if constexpr (std::is_same_v<ValueT, bool>) {
350 params.add_uninitialized_single_output(values);
354 ValueT *values = leaf_node->buffer().
data();
355 params.add_uninitialized_single_output(
356 GMutableSpan(param_cpp_type, values, LeafNodeMask::SIZE));
363 fn.call_auto(index_mask,
params, context);
365 for (
const int output_i : output_grids.
index_range()) {
366 const int param_index = input_values.
size() + output_i;
367 const mf::ParamType param_type = fn.param_type(param_index);
368 const CPPType ¶m_cpp_type = param_type.data_type().single_type();
369 if (!param_cpp_type.
is<
bool>()) {
372 openvdb::BoolGrid &grid =
static_cast<openvdb::BoolGrid &
>(*output_grids[output_i]);
374 auto accessor = grid.getUnsafeAccessor();
377 const openvdb::Coord &coord = voxels[
i];
378 accessor.setValue(coord, values[
i]);
394BLI_NOINLINE static void process_voxels(
const mf::MultiFunction &fn,
398 const openvdb::math::Transform &
transform,
403 AlignedBuffer<8192, 8> allocation_buffer;
405 scope.
allocator().provide_buffer(allocation_buffer);
406 mf::ParamsBuilder
params{fn, &index_mask};
409 for (
const int input_i : input_values.
index_range()) {
410 const bke::SocketValueVariant &value_variant = *input_values[input_i];
411 const mf::ParamType param_type = fn.param_type(
params.next_param_index());
412 const CPPType ¶m_cpp_type = param_type.data_type().single_type();
414 if (
const openvdb::GridBase *grid_base = input_grids[input_i]) {
416 to_typed_grid(*grid_base, [&](
const auto &grid) {
417 using ValueType =
typename std::decay_t<
decltype(grid)>::ValueType;
418 const auto &
tree = grid.tree();
422 auto accessor = grid.getConstUnsafeAccessor();
426 const openvdb::Coord &coord = voxels[
i];
427 values[
i] =
tree.getValue(coord, accessor);
430 params.add_readonly_single_input(GSpan(param_cpp_type, values.
data(), voxels_num));
433 else if (value_variant.is_context_dependent_field()) {
435 const fn::GField field = value_variant.get<fn::GField>();
436 const CPPType &type = field.cpp_type();
437 bke::VoxelFieldContext field_context{
transform, voxels};
438 fn::FieldEvaluator evaluator{field_context, voxels_num};
440 evaluator.add_with_destination(field, values);
441 evaluator.evaluate();
442 params.add_readonly_single_input(values);
446 params.add_readonly_single_input(value_variant.get_single_ptr());
452 for ([[maybe_unused]]
const int output_i : output_grids.
index_range()) {
453 const int param_index = input_values.
size() + output_i;
454 const mf::ParamType param_type = fn.param_type(param_index);
455 const CPPType &type = param_type.data_type().single_type();
456 void *buffer = scope.
allocator().allocate_array(type, voxels_num);
461 fn.call_auto(index_mask,
params, context);
464 for (
const int output_i : output_grids.
index_range()) {
465 openvdb::GridBase &grid_base = *output_grids[output_i];
466 to_typed_grid(grid_base, [&](
auto &grid) {
467 using GridT = std::decay_t<
decltype(grid)>;
468 using ValueType =
typename GridT::ValueType;
469 const int param_index = input_values.
size() + output_i;
470 const ValueType *computed_values =
static_cast<const ValueType *
>(
473 auto accessor = grid.getUnsafeAccessor();
475 const openvdb::Coord &coord = voxels[
i];
476 const ValueType &value = computed_values[
i];
477 accessor.setValue(coord, value);
495BLI_NOINLINE static void process_tiles(
const mf::MultiFunction &fn,
499 const openvdb::math::Transform &
transform,
505 AlignedBuffer<8192, 8> allocation_buffer;
507 scope.
allocator().provide_buffer(allocation_buffer);
508 mf::ParamsBuilder
params{fn, &index_mask};
511 for (
const int input_i : input_values.
index_range()) {
512 const bke::SocketValueVariant &value_variant = *input_values[input_i];
513 const mf::ParamType param_type = fn.param_type(
params.next_param_index());
514 const CPPType ¶m_cpp_type = param_type.data_type().single_type();
516 if (
const openvdb::GridBase *grid_base = input_grids[input_i]) {
518 to_typed_grid(*grid_base, [&](
const auto &grid) {
519 using GridT = std::decay_t<
decltype(grid)>;
520 using ValueType =
typename GridT::ValueType;
521 const auto &
tree = grid.tree();
522 auto accessor = grid.getConstUnsafeAccessor();
529 const openvdb::Coord any_coord_in_tile =
tile.min();
530 values[
i] =
tree.getValue(any_coord_in_tile, accessor);
533 params.add_readonly_single_input(GSpan(param_cpp_type, values.
data(), tiles_num));
536 else if (value_variant.is_context_dependent_field()) {
538 const fn::GField field = value_variant.get<fn::GField>();
539 const CPPType &type = field.cpp_type();
541 fn::FieldEvaluator evaluator{field_context, tiles_num};
543 evaluator.add_with_destination(field, values);
544 evaluator.evaluate();
545 params.add_readonly_single_input(values);
549 params.add_readonly_single_input(value_variant.get_single_ptr());
555 for ([[maybe_unused]]
const int output_i : output_grids.
index_range()) {
556 const int param_index = input_values.
size() + output_i;
557 const mf::ParamType param_type = fn.param_type(param_index);
558 const CPPType &type = param_type.data_type().single_type();
559 void *buffer = scope.
allocator().allocate_array(type, tiles_num);
564 fn.call_auto(index_mask,
params, context);
567 for (
const int output_i : output_grids.
index_range()) {
568 const int param_index = input_values.
size() + output_i;
569 openvdb::GridBase &grid_base = *output_grids[output_i];
570 to_typed_grid(grid_base, [&](
auto &grid) {
571 using GridT =
typename std::decay_t<
decltype(grid)>;
572 using TreeT =
typename GridT::TreeType;
573 using ValueType =
typename GridT::ValueType;
574 auto &
tree = grid.tree();
576 const ValueType *computed_values =
static_cast<const ValueType *
>(
579 const auto set_tile_value =
580 [&](
auto &node,
const openvdb::Coord &coord_in_tile,
auto value) {
581 const openvdb::Index n = node.coordToOffset(coord_in_tile);
587 using UnionType =
typename std::decay_t<
decltype(node)>::UnionType;
588 auto *table =
const_cast<UnionType *
>(node.getTable());
589 table[n].setValue(value);
594 const openvdb::Coord coord_in_tile =
tile.min();
595 const auto &computed_value = computed_values[
i];
596 using InternalNode1 =
typename TreeT::RootNodeType::ChildNodeType;
597 using InternalNode2 =
typename InternalNode1::ChildNodeType;
599 if (
auto *node =
tree.template probeNode<InternalNode2>(coord_in_tile)) {
600 set_tile_value(*node, coord_in_tile, computed_value);
602 else if (
auto *node =
tree.template probeNode<InternalNode1>(coord_in_tile)) {
603 set_tile_value(*node, coord_in_tile, computed_value);
614 const mf::MultiFunction &fn,
617 std::string &r_error_message)
619 const int inputs_num = input_values.
size();
623 for (
const int input_i :
IndexRange(inputs_num)) {
624 bke::SocketValueVariant &value_variant = *input_values[input_i];
625 if (value_variant.is_volume_grid()) {
626 const bke::GVolumeGrid g_volume_grid = value_variant.get<bke::GVolumeGrid>();
627 input_grids[input_i] = &g_volume_grid->grid(input_volume_tokens[input_i]);
629 else if (value_variant.is_context_dependent_field()) {
633 value_variant.convert_to_single();
637 const openvdb::math::Transform *
transform =
nullptr;
638 for (
const openvdb::GridBase *grid : input_grids) {
642 const openvdb::math::Transform &other_transform = grid->transform();
648 r_error_message =
TIP_(
"Input grids have incompatible transforms");
653 r_error_message =
TIP_(
"No input grid found that can determine the topology");
657 openvdb::MaskTree mask_tree;
658 for (
const openvdb::GridBase *grid : input_grids) {
662 to_typed_grid(*grid, [&](
const auto &grid) { mask_tree.topologyUnion(grid.tree()); });
667 const int param_index = input_values.
size() +
i;
668 const mf::ParamType param_type = fn.param_type(param_index);
669 const CPPType &cpp_type = param_type.data_type().single_type();
670 const std::optional<VolumeGridType> grid_type = cpp_type_to_grid_type(cpp_type);
672 r_error_message =
TIP_(
"Grid type not supported");
676 openvdb::GridBase::Ptr grid;
677 BKE_volume_grid_type_to_static_type(*grid_type, [&](
auto type_tag) {
678 using GridT =
typename decltype(type_tag)::type;
679 using TreeT =
typename GridT::TreeType;
680 using ValueType =
typename TreeT::ValueType;
681 const ValueType background{};
682 auto tree = std::make_shared<TreeT>(mask_tree, background, openvdb::TopologyCopy());
683 grid = openvdb::createGrid(std::move(
tree));
687 output_grids[
i] = std::move(grid);
690 parallel_grid_topology_tasks(
692 [&](
const LeafNodeMask &leaf_node_mask,
693 const openvdb::CoordBBox &leaf_bbox,
694 const GetVoxelsFn get_voxels_fn) {
695 process_leaf_node(fn,
705 process_voxels(fn, input_values, input_grids, output_grids, *
transform, voxels);
708 process_tiles(fn, input_values, input_grids, output_grids, *
transform,
tiles);
712 if (bke::SocketValueVariant *output_value = output_values[
i]) {
713 output_value->set(bke::GVolumeGrid(std::move(output_grids[
i])));
723 const mf::MultiFunction & ,
726 std::string &r_error_message)
728 r_error_message =
TIP_(
"Compiled without OpenVDB");
CustomData interface, see also DNA_customdata_types.h.
#define BLI_assert_unreachable()
BMesh const char void * data
SIMD_FORCE_INLINE btVector3 transform(const btVector3 &point) const
static IndexMask from_predicate(const IndexMask &universe, GrainSize grain_size, IndexMaskMemory &memory, Fn &&predicate)
int64_t min_array_size() const
void foreach_index(Fn &&fn) const
constexpr int64_t size() const
constexpr T * data() const
constexpr IndexRange index_range() const
LinearAllocator & allocator()
constexpr int64_t size() const
constexpr IndexRange index_range() const
void append(const T &value)
IndexRange index_range() const
ccl_gpu_kernel_postfix ccl_global KernelWorkTile * tiles
const ccl_global KernelWorkTile * tile
VolumeGridType get_type(const VolumeGridData &grid)
eCustomDataType cpp_type_to_custom_data_type(const CPPType &type)
std::optional< VolumeGridType > custom_data_type_to_volume_grid_type(eCustomDataType type)
int context(const bContext *C, const char *member, bContextDataResult *result)
bool execute_multi_function_on_value_variant__volume_grid(const mf::MultiFunction &, const Span< bke::SocketValueVariant * >, const Span< bke::SocketValueVariant * >, std::string &r_error_message)
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
constexpr bool is_same_any_v