diff --git a/src/bvh/collision_object.cpp b/src/bvh/collision_object.cpp index bd9721b..0ea587a 100644 --- a/src/bvh/collision_object.cpp +++ b/src/bvh/collision_object.cpp @@ -97,6 +97,8 @@ namespace bvh vt::debug( "objgroup make_collective {:x}\n", m_impl->objgroup.getProxy() ); }); + + m_impl->local_patches.resize( m_impl->overdecomposition ); } collision_object::collision_object( collision_object && ) noexcept = default; @@ -112,6 +114,8 @@ namespace bvh m_impl->num_splits = m_impl->splits.extent( 0 ); + std::swap( m_impl->last_step_local_patches, m_impl->local_patches ); + m_impl->local_patches.clear(); m_impl->local_patches.resize( od_factor ); @@ -161,16 +165,26 @@ namespace bvh // Update the data; od_factor should be identical across nodes std::size_t offset = rank * od_factor; m_impl->chainset.nextStep( "broadphase_patch_step", [this, rank, offset]( vt_index _local ) { - auto msg = ::vt::makeMessage< broadphase_patch_msg >(); - msg->patch = m_impl->local_patches.at( _local.x() ); - msg->origin_node = rank; - msg->local_idx = _local; - ::bvh::vt::debug( "{}: sending broadphase patch {} for body {} size {}\n", - ::vt::theContext()->getNode(), - vt_index{ _local.x() + offset }, m_impl->collision_idx, - msg->patch.size() ); - return m_impl->broadphase_patch_collection_proxy[vt_index{ _local.x() + offset }] - .sendMsg< broadphase_patch_msg, &details::set_broadphase_patches >( msg.get() ); + const auto &local_patch = m_impl->local_patches.at( _local.x() ); + const auto &last_step_local_patch = m_impl->last_step_local_patches.at( _local.x() ); + + // A patch may become empty and we need to update it + // But if it was empty last time step and it's empty this time step, don't update + // We could also do a more complete diff against the patch + if ( !local_patch.empty() || !last_step_local_patch.empty() + || ( last_step_local_patch.global_id() == static_cast< broadphase_patch_type::index_type >( -1 ) ) ) + { + auto msg = ::vt::makeMessage< broadphase_patch_msg >(); + msg->patch = local_patch; + msg->origin_node = rank; + msg->local_idx = _local; + ::bvh::vt::debug( "{}: sending broadphase patch {} for body {} size {}\n", ::vt::theContext()->getNode(), + vt_index{ _local.x() + offset }, m_impl->collision_idx, msg->patch.size() ); + return m_impl->broadphase_patch_collection_proxy[vt_index{ _local.x() + offset }] + .sendMsg< broadphase_patch_msg, &details::set_broadphase_patches >( msg.get() ); + } else { + return pending_send{ nullptr }; + } } ); // Right now use top down algorithm diff --git a/src/bvh/collision_object/impl.hpp b/src/bvh/collision_object/impl.hpp index e1ffc23..026031d 100644 --- a/src/bvh/collision_object/impl.hpp +++ b/src/bvh/collision_object/impl.hpp @@ -125,6 +125,7 @@ namespace bvh std::size_t collision_idx; std::vector< broadphase_patch_type > local_patches; + std::vector< broadphase_patch_type > last_step_local_patches; std::vector< ::vt::MsgPtr< collision_object_impl::narrowphase_patch_msg > > narrowphase_patch_messages; std::vector< std::size_t > local_data_indices; diff --git a/src/bvh/collision_object/top_down.cpp b/src/bvh/collision_object/top_down.cpp index 15e0014..719de59 100644 --- a/src/bvh/collision_object/top_down.cpp +++ b/src/bvh/collision_object/top_down.cpp @@ -55,6 +55,11 @@ namespace bvh tree_reduction() = default; + tree_reduction( collision_object_proxy_type _collision_object ) + : m_collision_object_proxy( _collision_object ), + m_snapshots{} + {} + tree_reduction( entity_snapshot _initial, collision_object_proxy_type _collision_object ) : m_collision_object_proxy( _collision_object ), m_snapshots{ _initial } @@ -63,6 +68,7 @@ namespace bvh tree_reduction &operator+=( const tree_reduction &_other ) { // The collision object proxies should be the same so no need to reduce those + debug_assert( m_collision_object_proxy.getProxy() == _other.m_collision_object_proxy.getProxy(), "collision objects must match" ); m_snapshots += _other.m_snapshots; return *this; } @@ -96,23 +102,28 @@ namespace bvh void tree_build_reduce( const tree_reduction &_reduc ) { - // Build the tree - auto msg = ::vt::makeMessage< broadphase_tree_msg >(); - msg->tree = build_tree_top_down< tree_type >( _reduc.snapshots() ); + // Build the tree + auto msg = ::vt::makeMessage< broadphase_tree_msg >(); + msg->tree = build_tree_top_down< tree_type >( _reduc.snapshots() ); - // Broadcast to every element of the collision object objgroup - _reduc.collision_object_proxy().broadcastMsg< broadphase_tree_msg, &collision_object_holder::delegate< broadphase_tree_msg, &set_broadphase_trees > >( msg ); + // Broadcast to every element of the collision object objgroup + _reduc.collision_object_proxy().broadcastMsg< broadphase_tree_msg, &collision_object_holder::delegate< broadphase_tree_msg, &set_broadphase_trees > >( msg ); } void tree_build_broadcast( broadphase_patch_collection_type *_patch, tree_build_broadcast_msg *_msg ) { - auto snap = make_snapshot( _patch->patch, static_cast< std::size_t >( _patch->getIndex().x() ) ); - using ::vt::collective::reduce::makeStamp; using ::vt::collective::reduce::StrongUserID; auto stamp = makeStamp(static_cast(::vt::thePhase()->getCurrentPhase())); - _patch->getCollectionProxy().reduce< tree_build_reduce, ::vt::collective::PlusOp >( 0, tree_reduction{ snap, _msg->coll_obj }, stamp ); + // Don't build a snapshot of an empty patch + if ( !_patch->patch.empty() ) + { + auto snap = make_snapshot( _patch->patch, static_cast< std::size_t >( _patch->getIndex().x() ) ); + _patch->getCollectionProxy().reduce< tree_build_reduce, ::vt::collective::PlusOp >( 0, tree_reduction{ snap, _msg->coll_obj }, stamp ); + } else { + _patch->getCollectionProxy().reduce< tree_build_reduce, ::vt::collective::PlusOp >( 0, tree_reduction{ _msg->coll_obj }, stamp ); + } } } diff --git a/src/bvh/collision_object/types.hpp b/src/bvh/collision_object/types.hpp index 83fc411..4690082 100644 --- a/src/bvh/collision_object/types.hpp +++ b/src/bvh/collision_object/types.hpp @@ -60,8 +60,8 @@ namespace bvh vt_msg_serialize_required(); broadphase_patch_type patch; - ::vt::NodeType origin_node; - vt_index local_idx; + ::vt::NodeType origin_node = {}; + vt_index local_idx = {}; template< typename Serializer > void serialize( Serializer &_s ) diff --git a/tests/collision_object_test.cpp b/tests/collision_object_test.cpp index 9ca13e9..3d7efe1 100644 --- a/tests/collision_object_test.cpp +++ b/tests/collision_object_test.cpp @@ -64,20 +64,29 @@ struct test_sing_trees { auto nranks = ::vt::theContext()->getNumNodes(); REQUIRE( _tree.count() == od_factor * nranks ); + } - for ( auto &&l : _tree.leafs() ) - { - REQUIRE( l.kdop().centroid() == bvh::m::vec3d::zeros() ); - } + std::size_t od_factor; +}; + +struct test_empty_trees +{ + void operator()( const bvh::snapshot_tree &_tree ) + { + auto nranks = ::vt::theContext()->getNumNodes(); + REQUIRE( _tree.count() == 0 ); } std::size_t od_factor; }; +std::size_t test_od_factor = 0; + void verify_num_elements( std::size_t _count ) { bvh::vt::debug("{}: count: {}\n", ::vt::theContext()->getNode(), _count ); - REQUIRE( _count == 12 * ::vt::theContext()->getNumNodes() ); + // Cube test_od_factor because each dimension is multiplied... + REQUIRE( _count == 12 * ::vt::theContext()->getNumNodes() * test_od_factor * test_od_factor * test_od_factor ); }; void @@ -90,12 +99,13 @@ verify_empty_elements( std::size_t _count ) TEST_CASE( "collision_object init", "[vt]") { std::size_t od_factor = GENERATE( 1, 2, 4, 32, 64 ); + test_od_factor = od_factor; bvh::collision_world world( od_factor ); auto &obj = world.create_collision_object(); auto rank = ::vt::theContext()->getNode(); - auto elements = build_element_grid( 2, 3, 2, rank * 12 ); + auto elements = build_element_grid( 2 * od_factor, 3 * od_factor, 2 * od_factor, rank * 12 * od_factor ); const std::array bound_vers{ bvh::m::vec3d{ 0.0, 0.0, 0.0 }, bvh::m::vec3d{ 0.0, 0.0, 1.0 }, bvh::m::vec3d{ 0.0, 1.0, 0.0 }, @@ -105,16 +115,17 @@ TEST_CASE( "collision_object init", "[vt]") bvh::m::vec3d{ 1.0, 1.0, 0.0 }, bvh::m::vec3d{ 1.0, 1.0, 1.0 } }; const auto bounds = Element::kdop_type::from_vertices( bound_vers.begin(), bound_vers.end() ); - const std::array update_bounds_vers{ bvh::m::vec3d{ 0.0, 0.0, 0.0 } }; + const std::array update_bounds_vers{ bvh::m::vec3d{ 10.0, 10.0, 10.0 }, + bvh::m::vec3d{ 10.0, 10.0, 11.0 }, + bvh::m::vec3d{ 10.0, 11.0, 10.0 }, + bvh::m::vec3d{ 10.0, 11.0, 11.0 }, + bvh::m::vec3d{ 11.0, 10.0, 10.0 }, + bvh::m::vec3d{ 11.0, 10.0, 11.0 }, + bvh::m::vec3d{ 11.0, 11.0, 10.0 }, + bvh::m::vec3d{ 11.0, 11.0, 11.0 } }; const auto update_bounds = Element::kdop_type::from_vertices( update_bounds_vers.begin(), update_bounds_vers.end() ); bvh::vt::debug( "{}: bounds: {}\n", ::vt::theContext()->getNode(), bounds ); - auto update_elements = bvh::view< Element * >{ "sing_vec", 12 }; - Kokkos::parallel_for( - 12, KOKKOS_LAMBDA( int _i ) { - update_elements( _i ).setVertices( bvh::m::vec3d::zeros(), bvh::m::vec3d::zeros(), bvh::m::vec3d::zeros(), - bvh::m::vec3d::zeros(), bvh::m::vec3d::zeros(), bvh::m::vec3d::zeros(), - bvh::m::vec3d::zeros(), bvh::m::vec3d::zeros() ); - } ); + auto update_elements = build_element_grid( 2 * od_factor, 3 * od_factor, 2 * od_factor, rank * 12 * od_factor, 10.0 ); auto split_method = GENERATE( bvh::split_algorithm::geom_axis, bvh::split_algorithm::ml_geom_axis, bvh::split_algorithm::clustering ); @@ -199,7 +210,7 @@ TEST_CASE( "collision_object init", "[vt]") obj.set_entity_data( empty_elements, split_method ); obj.init_broadphase(); - obj.for_each_tree( test_trees{ od_factor } ); + obj.for_each_tree( test_empty_trees{ od_factor } ); } ); vt::runInEpochCollective( "set_data.init.check", [&]() { @@ -218,7 +229,7 @@ TEST_CASE( "collision_object init", "[vt]") obj.set_entity_data( empty_elements, split_method ); obj.init_broadphase(); - obj.for_each_tree( test_sing_trees{ od_factor } ); + obj.for_each_tree( test_empty_trees{ od_factor } ); } ); vt::runInEpochCollective( "set_data.update.check", [&]() {