From e1377070eca1f8648e9a6a8ece9ec48a775e8603 Mon Sep 17 00:00:00 2001 From: Haixuan Xavier Tao Date: Thu, 16 Jul 2026 17:56:37 +0200 Subject: [PATCH] perf(rbd): dedupe shared TriMesh uploads in from_rapier MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Batched RL environments typically clone one terrain SharedShape across N envs; from_rapier re-serialized the flat BVH + pseudo-normals N times. The GPU Shape descriptor only holds ranges into the shared shape_buffers, so caching it by the parry shape data pointer uploads each unique trimesh once — vertex/index memory O(unique meshes) instead of O(envs). TriMesh only; scenes without shared trimeshes produce byte-identical buffers. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01U2n9RqmxTJb8UG5d1Sjw4W --- src_rbd/pipeline/rbd_state_from_rapier.rs | 25 ++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/src_rbd/pipeline/rbd_state_from_rapier.rs b/src_rbd/pipeline/rbd_state_from_rapier.rs index 92bdd1e..0a34830 100644 --- a/src_rbd/pipeline/rbd_state_from_rapier.rs +++ b/src_rbd/pipeline/rbd_state_from_rapier.rs @@ -146,6 +146,10 @@ impl RbdState { // the equal-topology invariant and to set `BatchIndices::bodies_len`. let mut all_env_body_counts: Vec = Vec::new(); let mut shape_buffers = ShapeBuffers::default(); + // TriMesh dedupe: a `SharedShape` cloned across envs (e.g. shared + // terrain) is serialized into `shape_buffers` once and its `Shape` + // descriptor reused — keyed by the parry shape data pointer. + let mut trimesh_cache: HashMap = HashMap::new(); let mut joint_envs: Vec<( &ImpulseJointSet, HashMap, @@ -273,9 +277,24 @@ impl RbdState { } }; - all_shapes.push( - shape_from_parry(co.shape(), &mut shape_buffers).expect("Unsupported shape"), - ); + let gpu_shape = match co.shape().as_typed_shape() { + crate::parry::shape::TypedShape::TriMesh(tm) => { + let key = tm as *const _ as *const u8 as usize; + match trimesh_cache.get(&key) { + Some(&s) => s, + None => { + let s = shape_from_parry(co.shape(), &mut shape_buffers) + .expect("Unsupported shape"); + trimesh_cache.insert(key, s); + s + } + } + } + _ => { + shape_from_parry(co.shape(), &mut shape_buffers).expect("Unsupported shape") + } + }; + all_shapes.push(gpu_shape); all_collider_local_poses.push(collider_local_pose); all_collision_groups.push(co.collision_groups()); all_collider_materials.push(collider_material_from_rapier(co));