From 51f081c43ad2fdb4c9676b77e9e2d65877da6d8c Mon Sep 17 00:00:00 2001 From: Random-Scientist Date: Wed, 22 Apr 2026 04:10:35 -0700 Subject: [PATCH 1/4] extract external API into JNI shims --- common/src/main/rust/rapier/src/api.rs | 889 ++++++++++++++++++ common/src/main/rust/rapier/src/boxes.rs | 20 +- common/src/main/rust/rapier/src/config.rs | 25 +- .../src/main/rust/rapier/src/contraptions.rs | 64 +- common/src/main/rust/rapier/src/joints.rs | 95 +- common/src/main/rust/rapier/src/lib.rs | 232 +---- common/src/main/rust/rapier/src/rope.rs | 92 +- .../main/rust/rapier/src/voxel_collider.rs | 60 +- 8 files changed, 970 insertions(+), 507 deletions(-) create mode 100644 common/src/main/rust/rapier/src/api.rs diff --git a/common/src/main/rust/rapier/src/api.rs b/common/src/main/rust/rapier/src/api.rs new file mode 100644 index 00000000..153d0f5b --- /dev/null +++ b/common/src/main/rust/rapier/src/api.rs @@ -0,0 +1,889 @@ +use jni::{ + JNIEnv, JavaVM, + objects::{JClass, JDoubleArray, JIntArray, JObject}, + sys::{jboolean, jdouble, jint, jlong, jsize}, +}; +use marten::level::SableMethodID; + +use crate::{ + add_chunk, add_linear_velocities, apply_force, apply_force_and_torque, + boxes::{create_box, remove_box}, + change_block, clear_collisions, + config::{config_frequency_and_damping, config_min_island_size, config_solver_iterations}, + contraptions::{ + add_kinematic_contraption_chunk_section, create_kinematic_contraption, + remove_kinematic_contraption, set_kinematic_contraption_transform, + }, + create_sub_level, get_angular_velocity, get_pose, initialize, + joints::{ + add_fixed_constraint, add_free_constraint, add_generic_constraint, add_rotary_constraint, + get_constraint_impulses, is_constraint_valid, remove_constraint, + set_constraint_contacts_enabled, set_constraint_frame, set_constraint_motor, + }, + remove_chunk, remove_sub_level, + rope::{ + add_rope_point_at_start, create_rope, query_rope, remove_rope, remove_rope_point_at_start, + set_rope_attachment, set_rope_first_segment_length, wake_up_rope, + }, + set_center_of_mass, set_local_bounds, set_mass_properties, step, teleport_object, tick, + voxel_collider::{add_voxel_collider_box, clear_voxel_collider_boxes, new_voxel_collider}, + wake_up_object, +}; +macro_rules! extract_jdouble_array { + ($env:expr, $jarr:expr, $len:expr) => {{ + let mut arr = [0.0 as jdouble; $len]; + $env.get_double_array_region($jarr, 0, &mut arr).unwrap(); + arr + }}; +} + +macro_rules! extract_jint_array { + ($env:expr, $jarr:expr, $len:expr) => {{ + let mut arr = [0 as jint; $len]; + $env.get_int_array_region($jarr, 0, &mut arr).unwrap(); + arr + }}; +} + +#[unsafe(no_mangle)] +pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_createBox<'local>( + env: JNIEnv<'local>, + _class: JClass<'local>, + scene_id: jint, + id: jint, + mass: jdouble, + half_extent_x: jdouble, + half_extent_y: jdouble, + half_extent_z: jdouble, + pose: JDoubleArray<'local>, +) { + let pose_arr: [jdouble; 7] = extract_jdouble_array!(env, pose, 7); + + create_box( + scene_id, + id, + mass, + half_extent_x, + half_extent_y, + half_extent_z, + pose_arr, + ); +} + +#[unsafe(no_mangle)] +pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_removeBox<'local>( + _env: JNIEnv<'local>, + _class: JClass<'local>, + scene_id: jint, + id: jint, +) { + remove_box(scene_id, id); +} + +#[unsafe(no_mangle)] +pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_configFrequencyAndDamping< + 'local, +>( + _env: JNIEnv<'local>, + _class: JClass<'local>, + collision_natural_frequency: jdouble, + collision_damping_ratio: jdouble, +) { + config_frequency_and_damping(collision_natural_frequency, collision_damping_ratio); +} + +#[unsafe(no_mangle)] +pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_configSolverIterations< + 'local, +>( + _env: JNIEnv<'local>, + _class: JClass<'local>, + num_solver_iterations: jint, + num_internal_pgs_iterations: jint, + num_internal_stabilization_iterations: jint, +) { + config_solver_iterations( + num_solver_iterations, + num_internal_pgs_iterations, + num_internal_stabilization_iterations, + ); +} + +#[unsafe(no_mangle)] +pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_configMinIslandSize< + 'local, +>( + _env: JNIEnv<'local>, + _class: JClass<'local>, + island_size: jint, +) { + config_min_island_size(island_size); +} + +#[unsafe(no_mangle)] +pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_createKinematicContraption< + 'local, +>( + _env: JNIEnv<'local>, + _class: JClass<'local>, + scene_id: jint, + mount_id: jint, + id: jint, + _pose: JDoubleArray<'local>, +) { + create_kinematic_contraption(scene_id, mount_id, id); +} + +#[unsafe(no_mangle)] +pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_setKinematicContraptionTransform< + 'local, +>( + env: JNIEnv<'local>, + _class: JClass<'local>, + scene_id: jint, + id: jint, + center_of_mass: JDoubleArray<'local>, + pose: JDoubleArray<'local>, + velocities: JDoubleArray<'local>, +) { + let center_of_mass_arr = extract_jdouble_array!(env, center_of_mass, 3); + let pose_arr = extract_jdouble_array!(env, pose, 7); + let velocities_arr = extract_jdouble_array!(env, velocities, 6); + set_kinematic_contraption_transform(scene_id, id, center_of_mass_arr, pose_arr, velocities_arr); +} + +#[unsafe(no_mangle)] +pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_addKinematicContraptionChunkSection< + 'local, +>( + env: JNIEnv<'local>, + _class: JClass<'local>, + scene_id: jint, + id: jint, + x: jint, + y: jint, + z: jint, + data: JIntArray<'local>, +) { + let ints = extract_jint_array!(env, data, 4096); + add_kinematic_contraption_chunk_section(scene_id, id, x, y, z, &ints); +} + +#[unsafe(no_mangle)] +pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_removeKinematicContraption< + 'local, +>( + _env: JNIEnv<'local>, + _class: JClass<'local>, + scene_id: jint, + id: jint, +) { + remove_kinematic_contraption(scene_id, id); +} + +#[unsafe(no_mangle)] +pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_setConstraintMotor< + 'local, +>( + _env: JNIEnv<'local>, + _class: JClass<'local>, + scene_id: jint, + joint_id: jlong, + axis: jint, + target_pos: jdouble, + stiffness: jdouble, + damping: jdouble, + has_max_force: jboolean, + max_force: jdouble, +) { + set_constraint_motor( + scene_id, + joint_id, + axis, + target_pos, + stiffness, + damping, + has_max_force, + max_force, + ); +} + +#[unsafe(no_mangle)] +pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_isConstraintValid< + 'local, +>( + _env: JNIEnv<'local>, + _class: JClass<'local>, + scene_id: jint, + joint_id: jlong, +) -> jboolean { + is_constraint_valid(scene_id, joint_id) +} + +#[unsafe(no_mangle)] +pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_getConstraintImpulses< + 'local, +>( + env: JNIEnv<'local>, + _class: JClass<'local>, + scene_id: jint, + joint_id: jlong, + store: JDoubleArray<'local>, +) { + let arr = get_constraint_impulses(scene_id, joint_id); + env.set_double_array_region(&store, 0, &arr).unwrap(); +} + +#[unsafe(no_mangle)] +pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_setConstraintContactsEnabled< + 'local, +>( + _env: JNIEnv<'local>, + _class: JClass<'local>, + scene_id: jint, + joint_id: jlong, + enabled: jboolean, +) { + set_constraint_contacts_enabled(scene_id, joint_id, enabled); +} + +#[unsafe(no_mangle)] +pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_removeConstraint< + 'local, +>( + _env: JNIEnv<'local>, + _class: JClass<'local>, + scene_id: jint, + joint_id: jlong, +) { + remove_constraint(scene_id, joint_id); +} + +#[unsafe(no_mangle)] +pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_addRotaryConstraint< + 'local, +>( + _env: JNIEnv<'local>, + _class: JClass<'local>, + scene_id: jint, + id_a: jint, + id_b: jint, + local_x_a: jdouble, + local_y_a: jdouble, + local_z_a: jdouble, + local_x_b: jdouble, + local_y_b: jdouble, + local_z_b: jdouble, + axis_x_a: jdouble, + axis_y_a: jdouble, + axis_z_a: jdouble, + axis_x_b: jdouble, + axis_y_b: jdouble, + axis_z_b: jdouble, +) -> jlong { + add_rotary_constraint( + scene_id, id_a, id_b, local_x_a, local_y_a, local_z_a, local_x_b, local_y_b, local_z_b, + axis_x_a, axis_y_a, axis_z_a, axis_x_b, axis_y_b, axis_z_b, + ) +} + +#[unsafe(no_mangle)] +pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_addFixedConstraint< + 'local, +>( + _env: JNIEnv<'local>, + _class: JClass<'local>, + scene_id: jint, + id_a: jint, + id_b: jint, + local_x_a: jdouble, + local_y_a: jdouble, + local_z_a: jdouble, + local_x_b: jdouble, + local_y_b: jdouble, + local_z_b: jdouble, + local_q_x: jdouble, + local_q_y: jdouble, + local_q_z: jdouble, + local_q_w: jdouble, +) -> jlong { + add_fixed_constraint( + scene_id, id_a, id_b, local_x_a, local_y_a, local_z_a, local_x_b, local_y_b, local_z_b, + local_q_x, local_q_y, local_q_z, local_q_w, + ) +} + +#[unsafe(no_mangle)] +pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_addFreeConstraint< + 'local, +>( + _env: JNIEnv<'local>, + _class: JClass<'local>, + scene_id: jint, + id_a: jint, + id_b: jint, + local_x_a: jdouble, + local_y_a: jdouble, + local_z_a: jdouble, + local_x_b: jdouble, + local_y_b: jdouble, + local_z_b: jdouble, + local_q_x: jdouble, + local_q_y: jdouble, + local_q_z: jdouble, + local_q_w: jdouble, +) -> jlong { + add_free_constraint( + scene_id, id_a, id_b, local_x_a, local_y_a, local_z_a, local_x_b, local_y_b, local_z_b, + local_q_x, local_q_y, local_q_z, local_q_w, + ) +} + +#[unsafe(no_mangle)] +pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_addGenericConstraint< + 'local, +>( + _env: JNIEnv<'local>, + _class: JClass<'local>, + scene_id: jint, + id_a: jint, + id_b: jint, + local_x_a: jdouble, + local_y_a: jdouble, + local_z_a: jdouble, + local_q_x_a: jdouble, + local_q_y_a: jdouble, + local_q_z_a: jdouble, + local_q_w_a: jdouble, + local_x_b: jdouble, + local_y_b: jdouble, + local_z_b: jdouble, + local_q_x_b: jdouble, + local_q_y_b: jdouble, + local_q_z_b: jdouble, + local_q_w_b: jdouble, + locked_axes_mask: jint, +) -> jlong { + add_generic_constraint( + scene_id, + id_a, + id_b, + local_x_a, + local_y_a, + local_z_a, + local_q_x_a, + local_q_y_a, + local_q_z_a, + local_q_w_a, + local_x_b, + local_y_b, + local_z_b, + local_q_x_b, + local_q_y_b, + local_q_z_b, + local_q_w_b, + locked_axes_mask, + ) +} + +#[unsafe(no_mangle)] +pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_setConstraintFrame< + 'local, +>( + _env: JNIEnv<'local>, + _class: JClass<'local>, + scene_id: jint, + joint_id: jlong, + side: jint, + local_x: jdouble, + local_y: jdouble, + local_z: jdouble, + local_q_x: jdouble, + local_q_y: jdouble, + local_q_z: jdouble, + local_q_w: jdouble, +) { + set_constraint_frame( + scene_id, joint_id, side, local_x, local_y, local_z, local_q_x, local_q_y, local_q_z, + local_q_w, + ); +} + +#[unsafe(no_mangle)] +pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_initialize<'local>( + env: JNIEnv<'local>, + _class: JClass<'local>, + scene_id: jint, + x: jdouble, + y: jdouble, + z: jdouble, + universal_drag: jdouble, +) { + let vm = unsafe { JavaVM::from_raw(env.get_java_vm().unwrap().get_java_vm_pointer()).unwrap() }; + initialize(Some(vm), scene_id, x, y, z, universal_drag); +} + +#[unsafe(no_mangle)] +pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_tick<'local>( + _env: JNIEnv<'local>, + _class: JClass<'local>, + scene_id: jint, + _time_step: jdouble, +) { + tick(scene_id); +} + +#[unsafe(no_mangle)] +pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_step<'local>( + _env: JNIEnv<'local>, + _class: JClass<'local>, + scene_id: jint, + time_step: jdouble, +) { + step(scene_id, time_step); +} + +#[unsafe(no_mangle)] +pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_getPose<'local>( + env: JNIEnv<'local>, + _class: JClass<'local>, + scene_id: jint, + id: jint, + store: JDoubleArray<'local>, +) { + let arr: [jdouble; 7] = get_pose(scene_id, id); + env.set_double_array_region(&store, 0, &arr).unwrap(); +} + +#[unsafe(no_mangle)] +pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_setCenterOfMass< + 'local, +>( + _env: JNIEnv<'local>, + _class: JClass<'local>, + scene_id: jint, + id: jint, + x: jdouble, + y: jdouble, + z: jdouble, +) { + set_center_of_mass(scene_id, id, x, y, z); +} + +#[unsafe(no_mangle)] +pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_setLocalBounds< + 'local, +>( + _env: JNIEnv<'local>, + _class: JClass<'local>, + scene_id: jint, + id: jint, + min_x: jint, + min_y: jint, + min_z: jint, + max_x: jint, + max_y: jint, + max_z: jint, +) { + set_local_bounds(scene_id, id, min_x, min_y, min_z, max_x, max_y, max_z); +} + +#[unsafe(no_mangle)] +pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_createSubLevel< + 'local, +>( + env: JNIEnv<'local>, + _class: JClass<'local>, + scene_id: jint, + id: jint, + pose: JDoubleArray<'local>, +) { + let pose_arr = extract_jdouble_array!(env, pose, 7); + create_sub_level(scene_id, id, pose_arr); +} + +#[unsafe(no_mangle)] +pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_removeSubLevel< + 'local, +>( + mut _env: JNIEnv<'local>, + _class: JClass<'local>, + scene_id: jint, + id: jint, +) { + remove_sub_level(scene_id, id); +} + +#[unsafe(no_mangle)] +pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_addChunk<'local>( + env: JNIEnv<'local>, + _class: JClass<'local>, + scene_id: jint, + x: jint, + y: jint, + z: jint, + data: JIntArray<'local>, + global: jboolean, + object_id: jint, +) { + let ints = extract_jint_array!(env, data, 4096); + add_chunk(scene_id, x, y, z, &ints, global, object_id); +} + +#[unsafe(no_mangle)] +pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_removeChunk<'local>( + _env: JNIEnv<'local>, + _class: JClass<'local>, + scene_id: jint, + x: jint, + y: jint, + z: jint, + global: jboolean, +) { + remove_chunk(scene_id, x, y, z, global); +} + +#[unsafe(no_mangle)] +pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_changeBlock<'local>( + _env: JNIEnv<'local>, + _class: JClass<'local>, + scene_id: jint, + x: jint, + y: jint, + z: jint, + block: jint, +) { + change_block(scene_id, x, y, z, block); +} + +#[unsafe(no_mangle)] +pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_setMassProperties< + 'local, +>( + env: JNIEnv<'local>, + _class: JClass<'local>, + scene_id: jint, + id: jint, + mass: jdouble, + center_of_mass: JDoubleArray<'local>, + inertia: JDoubleArray<'local>, +) { + let com = extract_jdouble_array!(env, center_of_mass, 3); + let inertia_arr = extract_jdouble_array!(env, inertia, 9); + + set_mass_properties(scene_id, id, mass, com, inertia_arr); +} + +#[unsafe(no_mangle)] +pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_teleportObject< + 'local, +>( + _env: JNIEnv<'local>, + _class: JClass<'local>, + scene_id: jint, + id: jint, + x: jdouble, + y: jdouble, + z: jdouble, + i: jdouble, + j: jdouble, + k: jdouble, + r: jdouble, +) { + teleport_object(scene_id, id, x, y, z, i, j, k, r); +} + +#[unsafe(no_mangle)] +pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_wakeUpObject< + 'local, +>( + _env: JNIEnv<'local>, + _class: JClass<'local>, + scene_id: jint, + id: jint, +) { + wake_up_object(scene_id, id); +} + +#[unsafe(no_mangle)] +pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_addLinearAngularVelocities< + 'local, +>( + _env: JNIEnv<'local>, + _class: JClass<'local>, + scene_id: jint, + id: jint, + linear_x: jdouble, + linear_y: jdouble, + linear_z: jdouble, + angular_x: jdouble, + angular_y: jdouble, + angular_z: jdouble, + wake_up: jboolean, +) { + add_linear_velocities( + scene_id, id, linear_x, linear_y, linear_z, angular_x, angular_y, angular_z, wake_up, + ); +} + +#[unsafe(no_mangle)] +pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_clearCollisions< + 'local, +>( + _env: JNIEnv<'local>, + _class: JClass<'local>, + scene_id: jint, +) -> JDoubleArray<'local> { + let arr = clear_collisions(scene_id); + let double_array = _env.new_double_array(arr.len() as jint).unwrap(); + _env.set_double_array_region(&double_array, 0, &arr) + .unwrap(); + + double_array +} + +#[unsafe(no_mangle)] +pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_applyForce<'local>( + _env: JNIEnv<'local>, + _class: JClass<'local>, + scene_id: jint, + id: jint, + x: jdouble, + y: jdouble, + z: jdouble, + fx: jdouble, + fy: jdouble, + fz: jdouble, + wake_up: jboolean, +) { + apply_force(scene_id, id, x, y, z, fx, fy, fz, wake_up); +} + +#[unsafe(no_mangle)] +pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_applyForceAndTorque< + 'local, +>( + _env: JNIEnv<'local>, + _class: JClass<'local>, + scene_id: jint, + id: jint, + fx: jdouble, + fy: jdouble, + fz: jdouble, + tx: jdouble, + ty: jdouble, + tz: jdouble, + wake_up: jboolean, +) { + apply_force_and_torque(scene_id, id, fx, fy, fz, tx, ty, tz, wake_up); +} + +#[unsafe(no_mangle)] +pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_getLinearVelocity< + 'local, +>( + _env: JNIEnv<'local>, + _class: JClass<'local>, + scene_id: jint, + id: jint, + store: JDoubleArray<'local>, +) { + _env.set_double_array_region(&store, 0, &get_angular_velocity(scene_id, id)) + .unwrap(); +} + +#[unsafe(no_mangle)] +pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_getAngularVelocity< + 'local, +>( + _env: JNIEnv<'local>, + _class: JClass<'local>, + scene_id: jint, + id: jint, + store: JDoubleArray<'local>, +) { + _env.set_double_array_region(&store, 0, &get_angular_velocity(scene_id, id)) + .unwrap(); +} + +#[unsafe(no_mangle)] +pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_createRope<'local>( + env: JNIEnv<'local>, + _class: JClass<'local>, + scene_id: jint, + point_radius: jdouble, + first_joint_length: jdouble, + points: JDoubleArray<'local>, + num_points: jint, +) -> jlong { + let mut coordinates = vec![0.0; (num_points * 3) as usize]; + env.get_double_array_region(points, 0, &mut coordinates) + .unwrap(); + create_rope( + scene_id, + point_radius, + first_joint_length, + coordinates, + num_points, + ) +} + +#[unsafe(no_mangle)] +pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_queryRope<'local>( + env: JNIEnv<'local>, + _class: JClass<'local>, + scene_id: jint, + id: jlong, +) -> JDoubleArray<'local> { + let flattened = query_rope(scene_id, id); + let double_array = env.new_double_array((flattened.len()) as jsize).unwrap(); + env.set_double_array_region(&double_array, 0, &flattened) + .unwrap(); + double_array +} + +#[unsafe(no_mangle)] +pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_removeRope<'local>( + _env: JNIEnv<'local>, + _class: JClass<'local>, + scene_id: jint, + id: jlong, +) { + remove_rope(scene_id, id); +} + +#[unsafe(no_mangle)] +pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_setRopeFirstSegmentLength< + 'local, +>( + _env: JNIEnv<'local>, + _class: JClass<'local>, + scene_id: jint, + id: jlong, + length: jdouble, +) { + set_rope_first_segment_length(scene_id, id, length); +} + +#[unsafe(no_mangle)] +pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_removeRopePointAtStart< + 'local, +>( + _env: JNIEnv<'local>, + _class: JClass<'local>, + scene_id: jint, + id: jlong, +) { + remove_rope_point_at_start(scene_id, id); +} + +#[unsafe(no_mangle)] +pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_addRopePointAtStart< + 'local, +>( + _env: JNIEnv<'local>, + _class: JClass<'local>, + scene_id: jint, + id: jlong, + x: jdouble, + y: jdouble, + z: jdouble, +) { + add_rope_point_at_start(scene_id, id, x, y, z); +} + +#[unsafe(no_mangle)] +pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_wakeUpRope<'local>( + _env: JNIEnv<'local>, + _class: JClass<'local>, + scene_id: jint, + rope_id: jlong, +) { + wake_up_rope(scene_id, rope_id); +} + +#[unsafe(no_mangle)] +pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_setRopeAttachment< + 'local, +>( + _env: JNIEnv<'local>, + _class: JClass<'local>, + scene_id: jint, + rope_id: jlong, + sub_level_id: jint, + x: jdouble, + y: jdouble, + z: jdouble, + end: jboolean, +) { + set_rope_attachment(scene_id, rope_id, sub_level_id, x, y, z, end); +} + +#[unsafe(no_mangle)] +pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_newVoxelCollider< + 'local, +>( + mut env: JNIEnv<'static>, + _class: JClass<'local>, + friction: jdouble, + volume: jdouble, + restitution: jdouble, + is_fluid: jboolean, + contact_events: JObject, + dynamic: jboolean, +) -> jint { + let global_ref = if contact_events.is_null() { + None + } else { + Some(env.new_global_ref(contact_events).unwrap()) + }; + + let global_method = if let Some(global_ref_value) = &global_ref { + let class = env.get_object_class(global_ref_value).unwrap(); + + let id = SableMethodID( + env.get_method_id( + class, + String::from("onCollision"), + String::from("(IIIDDDD)[D"), + ) + .unwrap(), + ); + Some(id) + } else { + None + }; + new_voxel_collider( + friction, + volume, + restitution, + is_fluid, + dynamic, + global_ref, + global_method, + ) +} + +#[unsafe(no_mangle)] +pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_addVoxelColliderBox< + 'local, +>( + env: JNIEnv<'local>, + _class: JClass<'local>, + index: jint, + box_bounds: JDoubleArray<'local>, +) { + let bounds = extract_jdouble_array!(env, box_bounds, 6); + add_voxel_collider_box(index, bounds); +} + +#[unsafe(no_mangle)] +pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_clearVoxelColliderBoxes< + 'local, +>( + _env: JNIEnv<'local>, + _class: JClass<'local>, + index: jint, +) { + clear_voxel_collider_boxes(index); +} diff --git a/common/src/main/rust/rapier/src/boxes.rs b/common/src/main/rust/rapier/src/boxes.rs index cb22eb99..20e2ee90 100644 --- a/common/src/main/rust/rapier/src/boxes.rs +++ b/common/src/main/rust/rapier/src/boxes.rs @@ -1,5 +1,3 @@ -use jni::JNIEnv; -use jni::objects::{JClass, JDoubleArray}; use jni::sys::{jdouble, jint}; use marten::Real; use rapier3d::dynamics::RigidBodyBuilder; @@ -10,21 +8,15 @@ use rapier3d::math::Vector; use crate::get_scene_mut; use crate::scene::LevelColliderID; -#[unsafe(no_mangle)] -pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_createBox<'local>( - env: JNIEnv<'local>, - _class: JClass<'local>, +pub fn create_box( scene_id: jint, id: jint, mass: jdouble, half_extent_x: jdouble, half_extent_y: jdouble, half_extent_z: jdouble, - pose: JDoubleArray<'local>, + pose_arr: [jdouble; 7], ) { - let mut pose_arr: [jdouble; 7] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]; - env.get_double_array_region(pose, 0, &mut pose_arr).unwrap(); - let quat = Quat::from_xyzw( pose_arr[3] as Real, pose_arr[4] as Real, @@ -63,13 +55,7 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_cre scene.rigid_bodies.insert(id as LevelColliderID, handle); } -#[unsafe(no_mangle)] -pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_removeBox<'local>( - _env: JNIEnv<'local>, - _class: JClass<'local>, - scene_id: jint, - id: jint, -) { +pub fn remove_box(scene_id: jint, id: jint) { let scene = get_scene_mut(scene_id); let handle = scene.rigid_bodies[&(id as LevelColliderID)]; scene.rigid_body_set.remove( diff --git a/common/src/main/rust/rapier/src/config.rs b/common/src/main/rust/rapier/src/config.rs index 62db42c6..1a478e33 100644 --- a/common/src/main/rust/rapier/src/config.rs +++ b/common/src/main/rust/rapier/src/config.rs @@ -1,5 +1,3 @@ -use jni::JNIEnv; -use jni::objects::JClass; use jni::sys::{jdouble, jint}; use marten::Real; @@ -11,12 +9,7 @@ pub const JOINT_SPRING_FREQUENCY: Real = 550.0; /// Global damping ratio for joints pub const JOINT_SPRING_DAMPING_RATIO: Real = 4.0; -#[unsafe(no_mangle)] -pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_configFrequencyAndDamping< - 'local, ->( - _env: JNIEnv<'local>, - _class: JClass<'local>, +pub fn config_frequency_and_damping( collision_natural_frequency: jdouble, collision_damping_ratio: jdouble, ) { @@ -34,12 +27,7 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_con } } -#[unsafe(no_mangle)] -pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_configSolverIterations< - 'local, ->( - _env: JNIEnv<'local>, - _class: JClass<'local>, +pub fn config_solver_iterations( num_solver_iterations: jint, num_internal_pgs_iterations: jint, num_internal_stabilization_iterations: jint, @@ -59,14 +47,7 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_con } } -#[unsafe(no_mangle)] -pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_configMinIslandSize< - 'local, ->( - _env: JNIEnv<'local>, - _class: JClass<'local>, - island_size: jint, -) { +pub fn config_min_island_size(island_size: jint) { unsafe { if let Some(state) = &mut PHYSICS_STATE { state.integration_parameters.min_island_size = island_size as usize; diff --git a/common/src/main/rust/rapier/src/contraptions.rs b/common/src/main/rust/rapier/src/contraptions.rs index 16114531..841fd412 100644 --- a/common/src/main/rust/rapier/src/contraptions.rs +++ b/common/src/main/rust/rapier/src/contraptions.rs @@ -1,7 +1,5 @@ use std::collections::HashMap; -use jni::JNIEnv; -use jni::objects::{JClass, JDoubleArray, JIntArray}; use jni::sys::{jdouble, jint}; use marten::Real; use rapier3d::dynamics::RigidBodyBuilder; @@ -17,22 +15,6 @@ use crate::groups::LEVEL_GROUP; use crate::scene::LevelColliderID; use crate::{ActiveLevelColliderInfo, get_scene_mut_ref}; -macro_rules! extract_jdouble_array { - ($env:expr, $jarr:expr, $len:expr) => {{ - let mut arr = [0.0 as jdouble; $len]; - $env.get_double_array_region($jarr, 0, &mut arr).unwrap(); - arr - }}; -} - -macro_rules! extract_jint_array { - ($env:expr, $jarr:expr, $len:expr) => {{ - let mut arr = [0 as jint; $len]; - $env.get_int_array_region($jarr, 0, &mut arr).unwrap(); - arr - }}; -} - // Helper for getting a mutable kinematic sub-level collider info fn get_kinematic_collider_info( scene: &mut crate::scene::PhysicsScene, @@ -44,16 +26,11 @@ fn get_kinematic_collider_info( .expect("No kinematic contraption with given ID!") } -#[unsafe(no_mangle)] -pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_createKinematicContraption< - 'local, ->( - _env: JNIEnv<'local>, - _class: JClass<'local>, +pub fn create_kinematic_contraption( scene_id: jint, mount_id: jint, id: jint, - _pose: JDoubleArray<'local>, + // pose_arr: [jdouble; 7] ) { let scene = get_scene_mut_ref(scene_id); @@ -104,21 +81,14 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_cre } /// Set the transform (position/orientation) of a kinematic sub-level's center of mass relative to its parent -#[unsafe(no_mangle)] -pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_setKinematicContraptionTransform< - 'local, ->( - env: JNIEnv<'local>, - _class: JClass<'local>, +#[inline] +pub fn set_kinematic_contraption_transform( scene_id: jint, id: jint, - center_of_mass: JDoubleArray<'local>, - pose: JDoubleArray<'local>, - velocities: JDoubleArray<'local>, + center_of_mass_arr: [jdouble; 3], + pose_arr: [jdouble; 7], + velocities_arr: [jdouble; 6], ) { - let center_of_mass_arr = extract_jdouble_array!(env, center_of_mass, 3); - let pose_arr = extract_jdouble_array!(env, pose, 7); - let velocities_arr = extract_jdouble_array!(env, velocities, 6); let translation = Vector3::new( pose_arr[0] as Real, pose_arr[1] as Real, @@ -183,20 +153,14 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_set } /// Add a chunk to a kinematic sub-level (4096 blocks, each as packed int) -#[unsafe(no_mangle)] -pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_addKinematicContraptionChunkSection< - 'local, ->( - env: JNIEnv<'local>, - _class: JClass<'local>, +pub fn add_kinematic_contraption_chunk_section( scene_id: jint, id: jint, x: jint, y: jint, z: jint, - data: JIntArray<'local>, + ints: &[i32; 4096], ) { - let ints = extract_jint_array!(env, data, 4096); let mut blocks = Vec::with_capacity(ints.len()); for block in ints { let block_collider_id = (block >> 16) as u16; @@ -217,15 +181,7 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_add } /// Remove a kinematic sub-level from a scene -#[unsafe(no_mangle)] -pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_removeKinematicContraption< - 'local, ->( - _env: JNIEnv<'local>, - _class: JClass<'local>, - scene_id: jint, - id: jint, -) { +pub fn remove_kinematic_contraption(scene_id: jint, id: jint) { let scene = get_scene_mut_ref(scene_id); let info = scene.level_colliders.remove(&(id as LevelColliderID)); let info = info.unwrap(); diff --git a/common/src/main/rust/rapier/src/joints.rs b/common/src/main/rust/rapier/src/joints.rs index 71f6a7d5..5d01ced7 100644 --- a/common/src/main/rust/rapier/src/joints.rs +++ b/common/src/main/rust/rapier/src/joints.rs @@ -1,8 +1,6 @@ use crate::config::{JOINT_SPRING_DAMPING_RATIO, JOINT_SPRING_FREQUENCY}; use crate::scene::LevelColliderID; use crate::{get_scene_mut_ref, get_scene_ref}; -use jni::JNIEnv; -use jni::objects::{JClass, JDoubleArray}; use jni::sys::{jboolean, jdouble, jint, jlong}; use marten::Real; use rapier3d::dynamics::{ @@ -118,12 +116,7 @@ const AXES: [JointAxis; 6] = [ JointAxis::AngZ, ]; -#[unsafe(no_mangle)] -pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_setConstraintMotor< - 'local, ->( - _env: JNIEnv<'local>, - _class: JClass<'local>, +pub fn set_constraint_motor( scene_id: jint, joint_id: jlong, axis: jint, @@ -155,15 +148,7 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_set } } -#[unsafe(no_mangle)] -pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_isConstraintValid< - 'local, ->( - _env: JNIEnv<'local>, - _class: JClass<'local>, - scene_id: jint, - joint_id: jlong, -) -> jboolean { +pub fn is_constraint_valid(scene_id: jint, joint_id: jlong) -> jboolean { let scene = get_scene_ref(scene_id); if scene.joint_set.joints.contains_key(&joint_id) { 1 @@ -172,43 +157,22 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_isC } } -#[unsafe(no_mangle)] -pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_getConstraintImpulses< - 'local, ->( - env: JNIEnv<'local>, - _class: JClass<'local>, - scene_id: jint, - joint_id: jlong, - store: JDoubleArray<'local>, -) { +pub fn get_constraint_impulses(scene_id: jint, joint_id: jlong) -> [jdouble; 6] { let scene = get_scene_ref(scene_id); let joint = scene.joint_set.joints.get(&joint_id).unwrap(); let impulse_joint = scene.impulse_joint_set.get(joint.handle).unwrap(); let impulses = impulse_joint.impulses; - - let arr: [jdouble; 6] = [ + [ impulses[0] as jdouble, impulses[1] as jdouble, impulses[2] as jdouble, impulses[3] as jdouble, impulses[4] as jdouble, impulses[5] as jdouble, - ]; - - env.set_double_array_region(&store, 0, &arr).unwrap(); + ] } -#[unsafe(no_mangle)] -pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_setConstraintContactsEnabled< - 'local, ->( - _env: JNIEnv<'local>, - _class: JClass<'local>, - scene_id: jint, - joint_id: jlong, - enabled: jboolean, -) { +pub fn set_constraint_contacts_enabled(scene_id: jint, joint_id: jlong, enabled: jboolean) { let scene = get_scene_mut_ref(scene_id); let Some(joint) = scene.joint_set.joints.get_mut(&joint_id) else { return; @@ -217,28 +181,15 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_set joint.contacts_enabled = enabled > 0; } -// removes a constraint -#[unsafe(no_mangle)] -pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_removeConstraint< - 'local, ->( - _env: JNIEnv<'local>, - _class: JClass<'local>, - scene_id: jint, - joint_id: jlong, -) { +/// removes a constraint +pub fn remove_constraint(scene_id: jint, joint_id: jlong) { let scene = get_scene_mut_ref(scene_id); if let Some(joint) = scene.joint_set.joints.remove(&joint_id) { scene.impulse_joint_set.remove(joint.handle, true); } } -#[unsafe(no_mangle)] -pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_addRotaryConstraint< - 'local, ->( - _env: JNIEnv<'local>, - _class: JClass<'local>, +pub fn add_rotary_constraint( scene_id: jint, id_a: jint, id_b: jint, @@ -319,12 +270,7 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_add handle_long } -#[unsafe(no_mangle)] -pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_addFixedConstraint< - 'local, ->( - _env: JNIEnv<'local>, - _class: JClass<'local>, +pub fn add_fixed_constraint( scene_id: jint, id_a: jint, id_b: jint, @@ -408,12 +354,7 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_add handle_long } -#[unsafe(no_mangle)] -pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_addFreeConstraint< - 'local, ->( - _env: JNIEnv<'local>, - _class: JClass<'local>, +pub fn add_free_constraint( scene_id: jint, id_a: jint, id_b: jint, @@ -494,12 +435,7 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_add handle_long } -#[unsafe(no_mangle)] -pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_addGenericConstraint< - 'local, ->( - _env: JNIEnv<'local>, - _class: JClass<'local>, +pub fn add_generic_constraint( scene_id: jint, id_a: jint, id_b: jint, @@ -595,12 +531,7 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_add handle_long } -#[unsafe(no_mangle)] -pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_setConstraintFrame< - 'local, ->( - _env: JNIEnv<'local>, - _class: JClass<'local>, +pub fn set_constraint_frame( scene_id: jint, joint_id: jlong, side: jint, diff --git a/common/src/main/rust/rapier/src/lib.rs b/common/src/main/rust/rapier/src/lib.rs index 512b4b94..f9c888b4 100644 --- a/common/src/main/rust/rapier/src/lib.rs +++ b/common/src/main/rust/rapier/src/lib.rs @@ -1,6 +1,7 @@ #![allow(static_mut_refs)] pub mod algo; +mod api; pub mod boxes; mod buoyancy; mod collider; @@ -15,9 +16,8 @@ pub mod rope; mod scene; mod voxel_collider; -use jni::objects::{JClass, JDoubleArray, JIntArray}; use jni::sys::{jboolean, jdouble, jint}; -use jni::{JNIEnv, JavaVM}; +use jni::JavaVM; use rapier3d::glamx::Quat; use rapier3d::math::Vector; use std::collections::HashMap; @@ -313,10 +313,8 @@ pub fn get_rigid_body(scene: &PhysicsScene, id: LevelColliderID) -> &RigidBody { &scene.rigid_body_set[*handle] } -#[unsafe(no_mangle)] -pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_initialize<'local>( - env: JNIEnv<'local>, - _class: JClass<'local>, +pub fn initialize( + current_step_vm: Option, scene_id: jint, x: jdouble, y: jdouble, @@ -403,7 +401,7 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_ini rope_map: RopeMap::default(), level_colliders: HashMap::::new(), rigid_bodies: HashMap::::new(), - current_step_vm: None, + current_step_vm, gravity: Vector::new(x as Real, y as Real, z as Real), universal_drag: universal_drag as Real, manifold_info_map: SableManifoldInfoMap::default(), @@ -411,8 +409,7 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_ini scene.collider_set.insert(collider); scene.ground_handle = Some(scene.rigid_body_set.insert(ground)); - scene.current_step_vm = - Some(JavaVM::from_raw(env.get_java_vm().unwrap().get_java_vm_pointer()).unwrap()); + state.scenes.insert(scene_id, scene); } } @@ -421,12 +418,9 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_ini } /// Computes buoyancy -#[unsafe(no_mangle)] -pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_tick<'local>( - _env: JNIEnv<'local>, - _class: JClass<'local>, +pub fn tick( scene_id: jint, - _time_step: jdouble, + // _time_step: jdouble ) { unsafe { if let Some(state) = &mut PHYSICS_STATE { @@ -443,13 +437,7 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_tic } /// Steps physics -#[unsafe(no_mangle)] -pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_step<'local>( - _env: JNIEnv<'local>, - _class: JClass<'local>, - scene_id: jint, - time_step: jdouble, -) { +pub fn step(scene_id: jint, time_step: jdouble) { unsafe { if let Some(state) = &mut PHYSICS_STATE { rope::tick(scene_id); @@ -481,22 +469,14 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_ste } } -#[unsafe(no_mangle)] -pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_getPose<'local>( - env: JNIEnv<'local>, - _class: JClass<'local>, - scene_id: jint, - id: jint, - store: JDoubleArray<'local>, -) { +pub fn get_pose(scene_id: jint, id: jint) -> [jdouble; 7] { unsafe { let Some(scene) = get_physics_state().scenes.get(&scene_id) else { panic!("No scene with given ID!"); }; let rb: &RigidBody = &scene.rigid_body_set[scene.rigid_bodies[&(id as LevelColliderID)]]; - - let arr: [jdouble; 7] = [ + [ rb.translation().x as jdouble, rb.translation().y as jdouble, rb.translation().z as jdouble, @@ -504,24 +484,11 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_get rb.rotation().y as jdouble, rb.rotation().z as jdouble, rb.rotation().w as jdouble, - ]; - - env.set_double_array_region(&store, 0, &arr).unwrap(); + ] } } -#[unsafe(no_mangle)] -pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_setCenterOfMass< - 'local, ->( - _env: JNIEnv<'local>, - _class: JClass<'local>, - scene_id: jint, - id: jint, - x: jdouble, - y: jdouble, - z: jdouble, -) { +pub fn set_center_of_mass(scene_id: jint, id: jint, x: jdouble, y: jdouble, z: jdouble) { unsafe { if let Some(state) = &mut PHYSICS_STATE { let Some(scene) = state.scenes.get_mut(&scene_id) else { @@ -537,12 +504,7 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_set } } -#[unsafe(no_mangle)] -pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_setLocalBounds< - 'local, ->( - _env: JNIEnv<'local>, - _class: JClass<'local>, +pub fn set_local_bounds( scene_id: jint, id: jint, min_x: jint, @@ -571,19 +533,7 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_set } } -#[unsafe(no_mangle)] -pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_createSubLevel< - 'local, ->( - env: JNIEnv<'local>, - _class: JClass<'local>, - scene_id: jint, - id: jint, - pose: JDoubleArray<'local>, -) { - let mut pose_arr: [jdouble; 7] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]; - env.get_double_array_region(pose, 0, &mut pose_arr).unwrap(); - +pub fn create_sub_level(scene_id: jint, id: jint, pose_arr: [jdouble; 7]) { let quat = Quat::from_xyzw( pose_arr[3] as Real, pose_arr[4] as Real, @@ -644,15 +594,7 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_cre } } -#[unsafe(no_mangle)] -pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_removeSubLevel< - 'local, ->( - mut _env: JNIEnv<'local>, - _class: JClass<'local>, - scene_id: jint, - id: jint, -) { +pub fn remove_sub_level(scene_id: jint, id: jint) { unsafe { if let Some(state) = &mut PHYSICS_STATE { let Some(scene) = state.scenes.get_mut(&scene_id) else { @@ -717,21 +659,15 @@ pub fn insert_block_octree( } } -#[unsafe(no_mangle)] -pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_addChunk<'local>( - env: JNIEnv<'local>, - _class: JClass<'local>, +pub fn add_chunk( scene_id: jint, x: jint, y: jint, z: jint, - data: JIntArray<'local>, + ints: &[jint; 4096], global: jboolean, object_id: jint, ) { - let mut ints: [jint; 4096] = [0; 4096]; - env.get_int_array_region(data, 0, &mut ints).unwrap(); - let mut blocks = Vec::with_capacity(ints.len()); for block in ints { @@ -857,16 +793,7 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_add } } -#[unsafe(no_mangle)] -pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_removeChunk<'local>( - _env: JNIEnv<'local>, - _class: JClass<'local>, - scene_id: jint, - x: jint, - y: jint, - z: jint, - global: jboolean, -) { +pub fn remove_chunk(scene_id: jint, x: jint, y: jint, z: jint, global: jboolean) { unsafe { if let Some(state) = &mut PHYSICS_STATE { let Some(scene) = state.scenes.get_mut(&scene_id) else { @@ -925,16 +852,7 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_rem } } -#[unsafe(no_mangle)] -pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_changeBlock<'local>( - _env: JNIEnv<'local>, - _class: JClass<'local>, - scene_id: jint, - x: jint, - y: jint, - z: jint, - block: jint, -) { +pub fn change_block(scene_id: jint, x: jint, y: jint, z: jint, block: jint) { let block_collider_id = (block >> 16) as u16; let voxel_state_id = (block & 0xFFFF) as u16; @@ -1036,26 +954,13 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_cha } } -#[unsafe(no_mangle)] -pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_setMassProperties< - 'local, ->( - env: JNIEnv<'local>, - _class: JClass<'local>, +pub fn set_mass_properties( scene_id: jint, id: jint, mass: jdouble, - center_of_mass: JDoubleArray<'local>, - inertia: JDoubleArray<'local>, + _com: [jdouble; 3], + inertia_arr: [jdouble; 9], ) { - let mut com: [jdouble; 3] = [0.0, 0.0, 0.0]; - env.get_double_array_region(center_of_mass, 0, &mut com) - .unwrap(); - - let mut inertia_arr: [jdouble; 9] = [0.0; 9]; - env.get_double_array_region(inertia, 0, &mut inertia_arr) - .unwrap(); - let inertia_tensor = Matrix3::new( inertia_arr[0] as Real, inertia_arr[1] as Real, @@ -1079,12 +984,7 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_set } /// Teleports the object to the given position. -#[unsafe(no_mangle)] -pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_teleportObject< - 'local, ->( - _env: JNIEnv<'local>, - _class: JClass<'local>, +pub fn teleport_object( scene_id: jint, id: jint, x: jdouble, @@ -1105,26 +1005,13 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_tel } /// Wakes up an object. -#[unsafe(no_mangle)] -pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_wakeUpObject< - 'local, ->( - _env: JNIEnv<'local>, - _class: JClass<'local>, - scene_id: jint, - id: jint, -) { +pub fn wake_up_object(scene_id: jint, id: jint) { let scene = get_scene_mut_ref(scene_id); let rb = &mut scene.rigid_body_set[scene.rigid_bodies[&(id as LevelColliderID)]]; rb.wake_up(true); } -#[unsafe(no_mangle)] -pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_addLinearAngularVelocities< - 'local, ->( - _env: JNIEnv<'local>, - _class: JClass<'local>, +pub fn add_linear_velocities( scene_id: jint, id: jint, linear_x: jdouble, @@ -1158,14 +1045,7 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_add /// /// A collision is formatted as follows: /// [body_a, body_b, force_amount, local_normal_a, local_normal_b, local_point_a, local_point_b] -#[unsafe(no_mangle)] -pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_clearCollisions< - 'local, ->( - _env: JNIEnv<'local>, - _class: JClass<'local>, - scene_id: jint, -) -> JDoubleArray<'local> { +pub fn clear_collisions(scene_id: jint) -> Vec { let scene = get_scene_mut_ref(scene_id); let max_collisions = 100; @@ -1202,21 +1082,12 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_cle arr.push(collision.local_point_b.y as jdouble); arr.push(collision.local_point_b.z as jdouble); } - - let double_array = _env.new_double_array(arr.len() as jint).unwrap(); - _env.set_double_array_region(&double_array, 0, &arr) - .unwrap(); - scene.reported_collisions.clear(); - - double_array + arr } /// Applies a force to a body -#[unsafe(no_mangle)] -pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_applyForce<'local>( - _env: JNIEnv<'local>, - _class: JClass<'local>, +pub fn apply_force( scene_id: jint, id: jint, x: jdouble, @@ -1258,12 +1129,7 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_app } /// Applies a force and torque -#[unsafe(no_mangle)] -pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_applyForceAndTorque< - 'local, ->( - _env: JNIEnv<'local>, - _class: JClass<'local>, +pub fn apply_force_and_torque( scene_id: jint, id: jint, fx: jdouble, @@ -1303,16 +1169,7 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_app } /// Gets the linear velocity of a body -#[unsafe(no_mangle)] -pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_getLinearVelocity< - 'local, ->( - _env: JNIEnv<'local>, - _class: JClass<'local>, - scene_id: jint, - id: jint, - store: JDoubleArray<'local>, -) { +pub fn get_linear_velocity(scene_id: jint, id: jint) -> [jdouble; 3] { unsafe { let Some(state) = &mut PHYSICS_STATE else { panic!("No physics state!"); @@ -1326,27 +1183,12 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_get let rb = &scene.rigid_body_set[*body]; let vel = rb.linvel(); - - _env.set_double_array_region( - &store, - 0, - &[vel.x as jdouble, vel.y as jdouble, vel.z as jdouble], - ) - .unwrap(); + vel.as_dvec3().to_array() } } /// Gets the angular velocity of a body -#[unsafe(no_mangle)] -pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_getAngularVelocity< - 'local, ->( - _env: JNIEnv<'local>, - _class: JClass<'local>, - scene_id: jint, - id: jint, - store: JDoubleArray<'local>, -) { +pub fn get_angular_velocity(scene_id: jint, id: jint) -> [jdouble; 3] { unsafe { let Some(state) = &mut PHYSICS_STATE else { panic!("No physics state!"); @@ -1360,12 +1202,6 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_get let rb = &scene.rigid_body_set[*body]; let vel = rb.angvel(); - - _env.set_double_array_region( - &store, - 0, - &[vel.x as jdouble, vel.y as jdouble, vel.z as jdouble], - ) - .unwrap(); + vel.as_dvec3().to_array() } } diff --git a/common/src/main/rust/rapier/src/rope.rs b/common/src/main/rust/rapier/src/rope.rs index f2758e28..3ecad63a 100644 --- a/common/src/main/rust/rapier/src/rope.rs +++ b/common/src/main/rust/rapier/src/rope.rs @@ -1,8 +1,6 @@ use std::collections::HashMap; -use jni::JNIEnv; -use jni::objects::{JClass, JDoubleArray}; -use jni::sys::{jboolean, jdouble, jint, jlong, jsize}; +use jni::sys::{jboolean, jdouble, jint, jlong}; use marten::Real; use rapier3d::dynamics::{GenericJointBuilder, JointAxis, RigidBodyBuilder, SpringCoefficients}; use rapier3d::geometry::{ColliderBuilder, SharedShape}; @@ -95,20 +93,13 @@ pub fn tick(scene_id: jint) { } } -#[unsafe(no_mangle)] -pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_createRope<'local>( - env: JNIEnv<'local>, - _class: JClass<'local>, +pub fn create_rope( scene_id: jint, point_radius: jdouble, first_joint_length: jdouble, - points: JDoubleArray<'local>, + coordinates: Vec, num_points: jint, ) -> jlong { - let mut coordinates = vec![0.0; (num_points * 3) as usize]; - env.get_double_array_region(points, 0, &mut coordinates) - .unwrap(); - let scene = get_scene_mut_ref(scene_id); let mut vec = Vec::with_capacity(num_points as usize); @@ -231,43 +222,23 @@ fn create_rope_body(scene_id: i32, coordinate: Vector, point_radius: Real) -> Ri handle } -/// Removes a rope -#[unsafe(no_mangle)] -pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_queryRope<'local>( - env: JNIEnv<'local>, - _class: JClass<'local>, - scene_id: jint, - id: jlong, -) -> JDoubleArray<'local> { +pub fn query_rope(scene_id: jint, id: jlong) -> Vec { let scene = get_scene_mut_ref(scene_id); let strand = scene.rope_map.ropes.get(&(id as usize)).unwrap(); - let flattened: Vec = strand + strand .points .iter() .flat_map(|x| { let pos = scene.rigid_body_set.get(*x).unwrap().position().translation; vec![pos.x as f64, pos.y as f64, pos.z as f64] }) - .collect(); - - let double_array = env - .new_double_array((strand.points.len() * 3) as jsize) - .unwrap(); - env.set_double_array_region(&double_array, 0, &flattened) - .unwrap(); - double_array + .collect() } /// Removes a rope -#[unsafe(no_mangle)] -pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_removeRope<'local>( - _env: JNIEnv<'local>, - _class: JClass<'local>, - scene_id: jint, - id: jlong, -) { +pub fn remove_rope(scene_id: jint, id: jlong) { let scene = get_scene_mut_ref(scene_id); let strand = scene.rope_map.ropes.remove(&(id as usize)).unwrap(); @@ -284,16 +255,7 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_rem } /// Sets the joint -#[unsafe(no_mangle)] -pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_setRopeFirstSegmentLength< - 'local, ->( - _env: JNIEnv<'local>, - _class: JClass<'local>, - scene_id: jint, - id: jlong, - length: jdouble, -) { +pub fn set_rope_first_segment_length(scene_id: jint, id: jlong, length: jdouble) { let scene = get_scene_mut_ref(scene_id); let strand = scene.rope_map.ropes.get_mut(&(id as usize)).unwrap(); @@ -313,15 +275,7 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_set ); } -#[unsafe(no_mangle)] -pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_removeRopePointAtStart< - 'local, ->( - _env: JNIEnv<'local>, - _class: JClass<'local>, - scene_id: jint, - id: jlong, -) { +pub fn remove_rope_point_at_start(scene_id: jint, id: jlong) { let scene = get_scene_mut_ref(scene_id); let strand = scene.rope_map.ropes.get_mut(&(id as usize)).unwrap(); @@ -357,18 +311,7 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_rem } } -#[unsafe(no_mangle)] -pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_addRopePointAtStart< - 'local, ->( - _env: JNIEnv<'local>, - _class: JClass<'local>, - scene_id: jint, - id: jlong, - x: jdouble, - y: jdouble, - z: jdouble, -) { +pub fn add_rope_point_at_start(scene_id: jint, id: jlong, x: jdouble, y: jdouble, z: jdouble) { let scene = get_scene_mut_ref(scene_id); let strand = scene.rope_map.ropes.get_mut(&(id as usize)).unwrap(); @@ -407,13 +350,7 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_add } } -#[unsafe(no_mangle)] -pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_wakeUpRope<'local>( - _env: JNIEnv<'local>, - _class: JClass<'local>, - scene_id: jint, - rope_id: jlong, -) { +pub fn wake_up_rope(scene_id: jint, rope_id: jlong) { let scene = get_scene_mut_ref(scene_id); let strand = scene.rope_map.ropes.get_mut(&(rope_id as usize)).unwrap(); @@ -424,12 +361,7 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_wak } /// Sets the attachment at a given end -#[unsafe(no_mangle)] -pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_setRopeAttachment< - 'local, ->( - _env: JNIEnv<'local>, - _class: JClass<'local>, +pub fn set_rope_attachment( scene_id: jint, rope_id: jlong, sub_level_id: jint, diff --git a/common/src/main/rust/rapier/src/voxel_collider.rs b/common/src/main/rust/rapier/src/voxel_collider.rs index 2badf7fb..740bcaed 100644 --- a/common/src/main/rust/rapier/src/voxel_collider.rs +++ b/common/src/main/rust/rapier/src/voxel_collider.rs @@ -1,7 +1,6 @@ use std::collections::HashMap; -use jni::JNIEnv; -use jni::objects::{JClass, JDoubleArray, JObject}; +use jni::objects::GlobalRef; use jni::sys::{jboolean, jdouble, jint}; use marten::Real; use marten::level::{SableMethodID, VoxelColliderData}; @@ -41,45 +40,18 @@ impl VoxelColliderMap { } } -#[unsafe(no_mangle)] -pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_newVoxelCollider< - 'local, ->( - mut env: JNIEnv<'static>, - _class: JClass<'local>, +pub fn new_voxel_collider( friction: jdouble, volume: jdouble, restitution: jdouble, is_fluid: jboolean, - contact_events: JObject, dynamic: jboolean, + global_ref: Option, + global_method: Option, ) -> jint { let state = unsafe { get_physics_state_mut() }; let next_index = state.voxel_collider_map.voxel_colliders.len(); - - let global_ref = if contact_events.is_null() { - None - } else { - Some(env.new_global_ref(contact_events).unwrap()) - }; - - let global_method = if let Some(global_ref_value) = &global_ref { - let class = env.get_object_class(global_ref_value).unwrap(); - - let id = SableMethodID( - env.get_method_id( - class, - String::from("onCollision"), - String::from("(IIIDDDD)[D"), - ) - .unwrap(), - ); - Some(id) - } else { - None - }; - state .voxel_collider_map .voxel_colliders @@ -97,21 +69,8 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_new next_index as jint } -#[unsafe(no_mangle)] -pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_addVoxelColliderBox< - 'local, ->( - env: JNIEnv<'local>, - _class: JClass<'local>, - index: jint, - box_bounds: JDoubleArray<'local>, -) { +pub fn add_voxel_collider_box(index: jint, bounds: [jdouble; 6]) { let state = unsafe { get_physics_state_mut() }; - - let mut bounds: [jdouble; 6] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]; - env.get_double_array_region(box_bounds, 0, &mut bounds) - .unwrap(); - if let Some(data) = &mut state.voxel_collider_map.voxel_colliders[index as usize] { data.collision_boxes.push(( bounds[0] as f32, @@ -124,14 +83,7 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_add } } -#[unsafe(no_mangle)] -pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_clearVoxelColliderBoxes< - 'local, ->( - _env: JNIEnv<'local>, - _class: JClass<'local>, - index: jint, -) { +pub fn clear_voxel_collider_boxes(index: jint) { let state = unsafe { get_physics_state_mut() }; if let Some(data) = &mut state.voxel_collider_map.voxel_colliders[index as usize] { From d33b0ac4629ec75b4d711989b364d8cc03221c6f Mon Sep 17 00:00:00 2001 From: Random-Scientist Date: Wed, 22 Apr 2026 18:42:29 -0700 Subject: [PATCH 2/4] adds a cute tracing and playback mechanism for sable_rapier --- common/src/main/rust/Cargo.lock | 9 + common/src/main/rust/Cargo.toml | 3 +- common/src/main/rust/fisher/Cargo.toml | 12 ++ common/src/main/rust/fisher/src/imp.rs | 144 +++++++++++++++ .../rust/fisher/src/imp/argument_encodings.rs | 124 +++++++++++++ common/src/main/rust/fisher/src/lib.rs | 21 +++ common/src/main/rust/rapier/Cargo.toml | 7 + common/src/main/rust/rapier/src/api.rs | 174 +++++++++++------- 8 files changed, 430 insertions(+), 64 deletions(-) create mode 100644 common/src/main/rust/fisher/Cargo.toml create mode 100644 common/src/main/rust/fisher/src/imp.rs create mode 100644 common/src/main/rust/fisher/src/imp/argument_encodings.rs create mode 100644 common/src/main/rust/fisher/src/lib.rs diff --git a/common/src/main/rust/Cargo.lock b/common/src/main/rust/Cargo.lock index d75741c1..89245a96 100644 --- a/common/src/main/rust/Cargo.lock +++ b/common/src/main/rust/Cargo.lock @@ -323,6 +323,14 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" +[[package]] +name = "fisher" +version = "0.1.0" +dependencies = [ + "jni", + "marten", +] + [[package]] name = "foldhash" version = "0.1.5" @@ -1012,6 +1020,7 @@ dependencies = [ "criterion", "dashmap", "fern", + "fisher", "humantime", "jni", "log", diff --git a/common/src/main/rust/Cargo.toml b/common/src/main/rust/Cargo.toml index 0600e54a..492cc1da 100644 --- a/common/src/main/rust/Cargo.toml +++ b/common/src/main/rust/Cargo.toml @@ -1,6 +1,6 @@ [workspace] resolver = "3" -members = ["marten", "rapier"] +members = ["fisher", "marten", "rapier"] [workspace.package] edition = "2024" @@ -8,6 +8,7 @@ version = "0.1.0" [workspace.dependencies] marten = { path = "marten" } +fisher = { path = "fisher" } log = "0.4.22" jni = "0.21.1" diff --git a/common/src/main/rust/fisher/Cargo.toml b/common/src/main/rust/fisher/Cargo.toml new file mode 100644 index 00000000..0e4d52a2 --- /dev/null +++ b/common/src/main/rust/fisher/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "fisher" +edition.workspace = true +version.workspace = true + +[dependencies] +jni.workspace = true +marten.workspace = true + +[features] +enabled = [] +default = ["enabled"] diff --git a/common/src/main/rust/fisher/src/imp.rs b/common/src/main/rust/fisher/src/imp.rs new file mode 100644 index 00000000..d30a00ad --- /dev/null +++ b/common/src/main/rust/fisher/src/imp.rs @@ -0,0 +1,144 @@ +use std::{ + array, + fs::File, + io::{self, BufWriter, Write}, + ops::{Deref, DerefMut}, + path::Path, + sync::{Mutex, MutexGuard}, +}; +pub mod argument_encodings; +pub use argument_encodings::BorrowReader; + +#[macro_export] +macro_rules! decl_tracked_api { + ( + $v:vis mod $modname:ident { $($tok:ident),+ $(,)? } + ) => { + /// test + #[expect(non_camel_case_types)] + $v mod $modname { + $( + pub struct $tok; + )+ + #[repr(u8)] + #[derive(::std::fmt::Debug, ::std::marker::Copy, ::std::clone::Clone)] + $v enum CallType { + $( + $tok + ),+ + } + impl CallType { + pub fn as_trackable(self) -> &'static dyn $crate::imp::Trackable { + match self { + $( + Self::$tok => const { $crate::imp::require_trackable( &$tok ) } + ),+ + } + } + } + } + macro_rules! __use_tracking_mod { () => { use self::$modname as __tracking; }; } + }; +} +pub const fn require_trackable(v: &'static T) -> &'static dyn Trackable { + v +} +pub const fn require_argument_encoding<'a, T: ArgumentEncode<'a>>(_: &T) {} + +#[diagnostic::on_unimplemented( + message = "token `{Self}` is declared but has no tracked call. Instrument a call of this token with `tracked_call!`", + label = "this token has no corresponding tracked call", + note = "all tokens must have a tracked call" +)] +pub trait Trackable { + fn name(&self) -> &'static str; + fn playback(&self, reader: &mut BorrowReader<'_>); +} +#[macro_export] +macro_rules! tracked_call { + ( $f:ident( $($arg:expr),* $(,)?) ) => { + { + const TRACKED_ID: u8 = { + __use_tracking_mod!(); + #[expect(non_local_definitions)] + #[allow(unused_variables)] + impl $crate::imp::Trackable for __tracking::$f { + fn name(&self) -> &'static str { + ::std::stringify!($f) + } + fn playback(&self, reader: &mut $crate::imp::BorrowReader) { + $f( + $( + { + let _ = ::std::stringify!($arg); + $crate::imp::ArgumentEncode::read(reader) + } + ),* + ); + } + } + __tracking::CallType::$f as u8 + }; + + let mut w = $crate::imp::get_writer(); + if let ::std::option::Option::Some(w) = &mut *w { + w.frame(TRACKED_ID).unwrap(); + // TODO is there a way to drop w before calling f to shorten the critical section here? + $f($( + { + let arg = $arg; + $crate::imp::ArgumentEncode::write(&arg, &mut **w).expect("write to succeed"); + arg + } + ),* + ) + } else { + drop(w); + $f( $( $arg ),* ) + } + } + }; +} + +pub struct Writer(BufWriter); +impl Deref for Writer { + type Target = BufWriter; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} +impl DerefMut for Writer { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} +impl Writer { + pub fn frame(&mut self, id: u8) -> io::Result<()> { + self.write_all(array::from_ref(&id)) + } + pub fn new(p: impl AsRef) -> Self { + let f = File::options() + .write(true) + .create(true) + .truncate(true) + .open(p) + .expect("trace file to open"); + Self(BufWriter::new(f)) + } +} +pub static FISHER_WRITER: Mutex> = const { Mutex::new(None) }; + +pub fn get_writer<'a>() -> MutexGuard<'a, Option> { + FISHER_WRITER.lock().expect("mutex not to be poisoned") +} + +#[diagnostic::on_unimplemented( + message = "tracked call parameter type `{Self}` doesn't implement `ArgumentEncode`", + label = "this parameter doesn't implement `ArgumentEncode`", + note = "in order to record and playback tracked calls all of their parameters must implement `ArgumentEncode`. Please add an implementation to `rust/fisher`" +)] +pub trait ArgumentEncode<'a>: Sized { + fn read(reader: &mut BorrowReader<'a>) -> Self; + fn write(&self, writer: &mut impl Write) -> io::Result<()>; +} diff --git a/common/src/main/rust/fisher/src/imp/argument_encodings.rs b/common/src/main/rust/fisher/src/imp/argument_encodings.rs new file mode 100644 index 00000000..4fece17d --- /dev/null +++ b/common/src/main/rust/fisher/src/imp/argument_encodings.rs @@ -0,0 +1,124 @@ +use std::{ + array, + io::{self, Write}, +}; + +use jni::{ + JavaVM, + objects::GlobalRef, + sys::{jboolean, jbyte, jchar, jdouble, jfloat, jint, jlong, jshort}, +}; +use marten::level::SableMethodID; + +use super::ArgumentEncode; + +/// Simple cursor that allows readers to temporarily borrow from its internal buffer +pub struct BorrowReader<'a> { + bytes: &'a [u8], + cursor: usize, +} +impl<'a> BorrowReader<'a> { + fn get_bytes(&mut self, ct: usize) -> &'a [u8] { + assert!(self.cursor + ct < self.bytes.len()); + self.cursor += ct; + &self.bytes[self.cursor..self.cursor + ct] + } + fn get_bytes_const(&mut self) -> &[u8; N] { + self.get_bytes(N).first_chunk().unwrap() + } +} + +impl ArgumentEncode<'_> for () { + fn read(_: &mut BorrowReader) -> Self {} + fn write(&self, _: &mut impl Write) -> io::Result<()> { + Ok(()) + } +} + +macro_rules! numeric_argument_encodings { + ( $( $intty:ty ),+ ) => { + $( + impl ArgumentEncode<'_> for $intty { + fn read(reader: &mut BorrowReader<'_>) -> Self { + <$intty>::from_le_bytes(*reader.get_bytes_const()) + } + + fn write(&self, writer: &mut impl Write) -> io::Result<()> { + writer.write_all(&self.to_le_bytes()) + } + } + )+ + }; +} +numeric_argument_encodings!(jboolean, jbyte, jchar, jshort, jint, jlong, jfloat, jdouble); + +impl<'a, const N: usize, T: ArgumentEncode<'a>> ArgumentEncode<'a> for [T; N] { + fn read(reader: &mut BorrowReader<'a>) -> Self { + array::from_fn(|_| T::read(reader)) + } + + fn write(&self, writer: &mut impl Write) -> io::Result<()> { + for i in self.iter() { + i.write(writer)? + } + Ok(()) + } +} + +#[cfg(target_endian = "big")] +compile_error!("fisher does not work on big endian platforms at the moment"); + +impl<'a, const N: usize> ArgumentEncode<'a> for &'a [jint; N] { + fn read(reader: &mut BorrowReader<'a>) -> Self { + let count = N * size_of::(); + let bytes = reader.get_bytes(count); + // Safety: jint aka i32 is valid at every bit pattern of [u8; size_of::()] + unsafe { core::slice::from_raw_parts(bytes.as_ptr().cast::(), N) } + .first_chunk() + .unwrap() + } + + fn write(&self, writer: &mut impl Write) -> io::Result<()> { + writer.write_all(unsafe { + core::slice::from_raw_parts(self.as_ptr().cast::(), std::mem::size_of_val(*self)) + }) + } +} + +impl ArgumentEncode<'_> for Vec { + fn read(reader: &mut BorrowReader<'_>) -> Self { + let len = u32::from_le_bytes(*reader.get_bytes_const()); + let bytes = reader.get_bytes(len as usize * size_of::()); + bytes + .as_chunks() + .0 + .iter() + .copied() + .map(jdouble::from_le_bytes) + .collect() + } + + fn write(&self, writer: &mut impl Write) -> io::Result<()> { + writer.write_all( + &(u32::try_from(self.len()).expect("array len to be <= u32::MAX")).to_le_bytes(), + )?; + writer.write_all(unsafe { + core::slice::from_raw_parts(self.as_ptr().cast::(), std::mem::size_of_val(&**self)) + }) + } +} +macro_rules! ignore_option_impls { + ( $( $opt:ty ),+ ) => { + $( + impl ArgumentEncode<'_> for Option<$opt> { + fn read(_: &mut BorrowReader<'_>) -> Self { + None + } + fn write(&self, _: &mut impl Write) -> io::Result<()> { + Ok(()) + } + } + )+ + }; +} +ignore_option_impls!(JavaVM, GlobalRef, SableMethodID); diff --git a/common/src/main/rust/fisher/src/lib.rs b/common/src/main/rust/fisher/src/lib.rs new file mode 100644 index 00000000..a4bb8064 --- /dev/null +++ b/common/src/main/rust/fisher/src/lib.rs @@ -0,0 +1,21 @@ +#[cfg(feature = "enabled")] +pub mod imp; + +#[cfg(not(feature = "enabled"))] +#[macro_export] +macro_rules! decl_tracked_api { + ( $( $any:tt )* ) => {}; +} +#[cfg(not(feature = "enabled"))] +#[macro_export] +macro_rules! tracked_call { + ( $( $any:tt )* ) => { $( $any )* }; + } +#[cfg(not(feature = "enabled"))] +pub fn setup_trace() {} + +#[cfg(feature = "enabled")] +pub fn setup_trace() { + use crate::imp::Writer; + *imp::FISHER_WRITER.lock().unwrap() = Some(Writer::new("sable_trace.bin")); +} diff --git a/common/src/main/rust/rapier/Cargo.toml b/common/src/main/rust/rapier/Cargo.toml index ccdfa038..fc1855e2 100644 --- a/common/src/main/rust/rapier/Cargo.toml +++ b/common/src/main/rust/rapier/Cargo.toml @@ -5,6 +5,7 @@ edition.workspace = true [dependencies] marten.workspace = true +fisher = { workspace = true } fern.workspace = true jni.workspace = true @@ -32,3 +33,9 @@ harness = false [dev-dependencies] criterion = "0.8.2" + +[features] +#TODO remove +default = ["recording"] + +recording = ["fisher/enabled"] diff --git a/common/src/main/rust/rapier/src/api.rs b/common/src/main/rust/rapier/src/api.rs index 153d0f5b..39dc51e4 100644 --- a/common/src/main/rust/rapier/src/api.rs +++ b/common/src/main/rust/rapier/src/api.rs @@ -1,3 +1,4 @@ +use fisher::{setup_trace, tracked_call}; use jni::{ JNIEnv, JavaVM, objects::{JClass, JDoubleArray, JIntArray, JObject}, @@ -14,7 +15,7 @@ use crate::{ add_kinematic_contraption_chunk_section, create_kinematic_contraption, remove_kinematic_contraption, set_kinematic_contraption_transform, }, - create_sub_level, get_angular_velocity, get_pose, initialize, + create_sub_level, get_angular_velocity, get_linear_velocity, get_pose, initialize, joints::{ add_fixed_constraint, add_free_constraint, add_generic_constraint, add_rotary_constraint, get_constraint_impulses, is_constraint_valid, remove_constraint, @@ -45,6 +46,25 @@ macro_rules! extract_jint_array { }}; } +fisher::decl_tracked_api! { + pub mod recording { + add_chunk, add_linear_velocities, apply_force, apply_force_and_torque, create_box, remove_box, change_block, clear_collisions, + config_frequency_and_damping, config_min_island_size, config_solver_iterations, + add_kinematic_contraption_chunk_section, create_kinematic_contraption, + remove_kinematic_contraption, set_kinematic_contraption_transform, + create_sub_level, get_angular_velocity, get_linear_velocity, get_pose, initialize, + add_fixed_constraint, add_free_constraint, add_generic_constraint, add_rotary_constraint, + get_constraint_impulses, is_constraint_valid, remove_constraint, + set_constraint_contacts_enabled, set_constraint_frame, set_constraint_motor, + remove_chunk, remove_sub_level, + add_rope_point_at_start, create_rope, query_rope, remove_rope, remove_rope_point_at_start, + set_rope_attachment, set_rope_first_segment_length, wake_up_rope, + set_center_of_mass, set_local_bounds, set_mass_properties, step, teleport_object, tick, + add_voxel_collider_box, clear_voxel_collider_boxes, new_voxel_collider, + wake_up_object, + } +} + #[unsafe(no_mangle)] pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_createBox<'local>( env: JNIEnv<'local>, @@ -59,7 +79,7 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_cre ) { let pose_arr: [jdouble; 7] = extract_jdouble_array!(env, pose, 7); - create_box( + tracked_call!(create_box( scene_id, id, mass, @@ -67,7 +87,7 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_cre half_extent_y, half_extent_z, pose_arr, - ); + )); } #[unsafe(no_mangle)] @@ -77,7 +97,7 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_rem scene_id: jint, id: jint, ) { - remove_box(scene_id, id); + tracked_call!(remove_box(scene_id, id)); } #[unsafe(no_mangle)] @@ -89,7 +109,10 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_con collision_natural_frequency: jdouble, collision_damping_ratio: jdouble, ) { - config_frequency_and_damping(collision_natural_frequency, collision_damping_ratio); + tracked_call!(config_frequency_and_damping( + collision_natural_frequency, + collision_damping_ratio + )); } #[unsafe(no_mangle)] @@ -102,11 +125,11 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_con num_internal_pgs_iterations: jint, num_internal_stabilization_iterations: jint, ) { - config_solver_iterations( + tracked_call!(config_solver_iterations( num_solver_iterations, num_internal_pgs_iterations, num_internal_stabilization_iterations, - ); + )); } #[unsafe(no_mangle)] @@ -117,7 +140,7 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_con _class: JClass<'local>, island_size: jint, ) { - config_min_island_size(island_size); + tracked_call!(config_min_island_size(island_size)); } #[unsafe(no_mangle)] @@ -131,7 +154,7 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_cre id: jint, _pose: JDoubleArray<'local>, ) { - create_kinematic_contraption(scene_id, mount_id, id); + tracked_call!(create_kinematic_contraption(scene_id, mount_id, id)); } #[unsafe(no_mangle)] @@ -149,7 +172,13 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_set let center_of_mass_arr = extract_jdouble_array!(env, center_of_mass, 3); let pose_arr = extract_jdouble_array!(env, pose, 7); let velocities_arr = extract_jdouble_array!(env, velocities, 6); - set_kinematic_contraption_transform(scene_id, id, center_of_mass_arr, pose_arr, velocities_arr); + tracked_call!(set_kinematic_contraption_transform( + scene_id, + id, + center_of_mass_arr, + pose_arr, + velocities_arr + )); } #[unsafe(no_mangle)] @@ -166,7 +195,9 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_add data: JIntArray<'local>, ) { let ints = extract_jint_array!(env, data, 4096); - add_kinematic_contraption_chunk_section(scene_id, id, x, y, z, &ints); + tracked_call!(add_kinematic_contraption_chunk_section( + scene_id, id, x, y, z, &ints + )); } #[unsafe(no_mangle)] @@ -178,7 +209,7 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_rem scene_id: jint, id: jint, ) { - remove_kinematic_contraption(scene_id, id); + tracked_call!(remove_kinematic_contraption(scene_id, id)); } #[unsafe(no_mangle)] @@ -196,7 +227,7 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_set has_max_force: jboolean, max_force: jdouble, ) { - set_constraint_motor( + tracked_call!(set_constraint_motor( scene_id, joint_id, axis, @@ -205,7 +236,7 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_set damping, has_max_force, max_force, - ); + )); } #[unsafe(no_mangle)] @@ -217,7 +248,7 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_isC scene_id: jint, joint_id: jlong, ) -> jboolean { - is_constraint_valid(scene_id, joint_id) + tracked_call!(is_constraint_valid(scene_id, joint_id)) } #[unsafe(no_mangle)] @@ -230,7 +261,7 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_get joint_id: jlong, store: JDoubleArray<'local>, ) { - let arr = get_constraint_impulses(scene_id, joint_id); + let arr = tracked_call!(get_constraint_impulses(scene_id, joint_id)); env.set_double_array_region(&store, 0, &arr).unwrap(); } @@ -244,7 +275,7 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_set joint_id: jlong, enabled: jboolean, ) { - set_constraint_contacts_enabled(scene_id, joint_id, enabled); + tracked_call!(set_constraint_contacts_enabled(scene_id, joint_id, enabled)); } #[unsafe(no_mangle)] @@ -256,7 +287,7 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_rem scene_id: jint, joint_id: jlong, ) { - remove_constraint(scene_id, joint_id); + tracked_call!(remove_constraint(scene_id, joint_id)); } #[unsafe(no_mangle)] @@ -281,10 +312,10 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_add axis_y_b: jdouble, axis_z_b: jdouble, ) -> jlong { - add_rotary_constraint( + tracked_call!(add_rotary_constraint( scene_id, id_a, id_b, local_x_a, local_y_a, local_z_a, local_x_b, local_y_b, local_z_b, axis_x_a, axis_y_a, axis_z_a, axis_x_b, axis_y_b, axis_z_b, - ) + )) } #[unsafe(no_mangle)] @@ -307,10 +338,10 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_add local_q_z: jdouble, local_q_w: jdouble, ) -> jlong { - add_fixed_constraint( + tracked_call!(add_fixed_constraint( scene_id, id_a, id_b, local_x_a, local_y_a, local_z_a, local_x_b, local_y_b, local_z_b, local_q_x, local_q_y, local_q_z, local_q_w, - ) + )) } #[unsafe(no_mangle)] @@ -333,10 +364,10 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_add local_q_z: jdouble, local_q_w: jdouble, ) -> jlong { - add_free_constraint( + tracked_call!(add_free_constraint( scene_id, id_a, id_b, local_x_a, local_y_a, local_z_a, local_x_b, local_y_b, local_z_b, local_q_x, local_q_y, local_q_z, local_q_w, - ) + )) } #[unsafe(no_mangle)] @@ -364,7 +395,7 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_add local_q_w_b: jdouble, locked_axes_mask: jint, ) -> jlong { - add_generic_constraint( + tracked_call!(add_generic_constraint( scene_id, id_a, id_b, @@ -383,7 +414,7 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_add local_q_z_b, local_q_w_b, locked_axes_mask, - ) + )) } #[unsafe(no_mangle)] @@ -403,10 +434,10 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_set local_q_z: jdouble, local_q_w: jdouble, ) { - set_constraint_frame( + tracked_call!(set_constraint_frame( scene_id, joint_id, side, local_x, local_y, local_z, local_q_x, local_q_y, local_q_z, local_q_w, - ); + )); } #[unsafe(no_mangle)] @@ -419,8 +450,9 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_ini z: jdouble, universal_drag: jdouble, ) { + setup_trace(); let vm = unsafe { JavaVM::from_raw(env.get_java_vm().unwrap().get_java_vm_pointer()).unwrap() }; - initialize(Some(vm), scene_id, x, y, z, universal_drag); + tracked_call!(initialize(Some(vm), scene_id, x, y, z, universal_drag)); } #[unsafe(no_mangle)] @@ -430,7 +462,7 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_tic scene_id: jint, _time_step: jdouble, ) { - tick(scene_id); + tracked_call!(tick(scene_id)); } #[unsafe(no_mangle)] @@ -440,7 +472,7 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_ste scene_id: jint, time_step: jdouble, ) { - step(scene_id, time_step); + tracked_call!(step(scene_id, time_step)); } #[unsafe(no_mangle)] @@ -451,7 +483,7 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_get id: jint, store: JDoubleArray<'local>, ) { - let arr: [jdouble; 7] = get_pose(scene_id, id); + let arr: [jdouble; 7] = tracked_call!(get_pose(scene_id, id)); env.set_double_array_region(&store, 0, &arr).unwrap(); } @@ -467,7 +499,7 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_set y: jdouble, z: jdouble, ) { - set_center_of_mass(scene_id, id, x, y, z); + tracked_call!(set_center_of_mass(scene_id, id, x, y, z)); } #[unsafe(no_mangle)] @@ -485,7 +517,9 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_set max_y: jint, max_z: jint, ) { - set_local_bounds(scene_id, id, min_x, min_y, min_z, max_x, max_y, max_z); + tracked_call!(set_local_bounds( + scene_id, id, min_x, min_y, min_z, max_x, max_y, max_z + )); } #[unsafe(no_mangle)] @@ -499,7 +533,7 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_cre pose: JDoubleArray<'local>, ) { let pose_arr = extract_jdouble_array!(env, pose, 7); - create_sub_level(scene_id, id, pose_arr); + tracked_call!(create_sub_level(scene_id, id, pose_arr)); } #[unsafe(no_mangle)] @@ -511,7 +545,7 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_rem scene_id: jint, id: jint, ) { - remove_sub_level(scene_id, id); + tracked_call!(remove_sub_level(scene_id, id)); } #[unsafe(no_mangle)] @@ -527,7 +561,7 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_add object_id: jint, ) { let ints = extract_jint_array!(env, data, 4096); - add_chunk(scene_id, x, y, z, &ints, global, object_id); + tracked_call!(add_chunk(scene_id, x, y, z, &ints, global, object_id)); } #[unsafe(no_mangle)] @@ -540,7 +574,7 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_rem z: jint, global: jboolean, ) { - remove_chunk(scene_id, x, y, z, global); + tracked_call!(remove_chunk(scene_id, x, y, z, global)); } #[unsafe(no_mangle)] @@ -553,7 +587,7 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_cha z: jint, block: jint, ) { - change_block(scene_id, x, y, z, block); + tracked_call!(change_block(scene_id, x, y, z, block)); } #[unsafe(no_mangle)] @@ -571,7 +605,7 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_set let com = extract_jdouble_array!(env, center_of_mass, 3); let inertia_arr = extract_jdouble_array!(env, inertia, 9); - set_mass_properties(scene_id, id, mass, com, inertia_arr); + tracked_call!(set_mass_properties(scene_id, id, mass, com, inertia_arr)); } #[unsafe(no_mangle)] @@ -590,7 +624,7 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_tel k: jdouble, r: jdouble, ) { - teleport_object(scene_id, id, x, y, z, i, j, k, r); + tracked_call!(teleport_object(scene_id, id, x, y, z, i, j, k, r)); } #[unsafe(no_mangle)] @@ -602,7 +636,7 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_wak scene_id: jint, id: jint, ) { - wake_up_object(scene_id, id); + tracked_call!(wake_up_object(scene_id, id)); } #[unsafe(no_mangle)] @@ -621,9 +655,9 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_add angular_z: jdouble, wake_up: jboolean, ) { - add_linear_velocities( + tracked_call!(add_linear_velocities( scene_id, id, linear_x, linear_y, linear_z, angular_x, angular_y, angular_z, wake_up, - ); + )); } #[unsafe(no_mangle)] @@ -634,7 +668,7 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_cle _class: JClass<'local>, scene_id: jint, ) -> JDoubleArray<'local> { - let arr = clear_collisions(scene_id); + let arr = tracked_call!(clear_collisions(scene_id)); let double_array = _env.new_double_array(arr.len() as jint).unwrap(); _env.set_double_array_region(&double_array, 0, &arr) .unwrap(); @@ -656,7 +690,7 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_app fz: jdouble, wake_up: jboolean, ) { - apply_force(scene_id, id, x, y, z, fx, fy, fz, wake_up); + tracked_call!(apply_force(scene_id, id, x, y, z, fx, fy, fz, wake_up)); } #[unsafe(no_mangle)] @@ -675,7 +709,9 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_app tz: jdouble, wake_up: jboolean, ) { - apply_force_and_torque(scene_id, id, fx, fy, fz, tx, ty, tz, wake_up); + tracked_call!(apply_force_and_torque( + scene_id, id, fx, fy, fz, tx, ty, tz, wake_up + )); } #[unsafe(no_mangle)] @@ -688,7 +724,7 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_get id: jint, store: JDoubleArray<'local>, ) { - _env.set_double_array_region(&store, 0, &get_angular_velocity(scene_id, id)) + _env.set_double_array_region(&store, 0, &tracked_call!(get_linear_velocity(scene_id, id))) .unwrap(); } @@ -702,8 +738,12 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_get id: jint, store: JDoubleArray<'local>, ) { - _env.set_double_array_region(&store, 0, &get_angular_velocity(scene_id, id)) - .unwrap(); + _env.set_double_array_region( + &store, + 0, + &tracked_call!(get_angular_velocity(scene_id, id)), + ) + .unwrap(); } #[unsafe(no_mangle)] @@ -719,13 +759,13 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_cre let mut coordinates = vec![0.0; (num_points * 3) as usize]; env.get_double_array_region(points, 0, &mut coordinates) .unwrap(); - create_rope( + tracked_call!(create_rope( scene_id, point_radius, first_joint_length, coordinates, num_points, - ) + )) } #[unsafe(no_mangle)] @@ -735,7 +775,7 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_que scene_id: jint, id: jlong, ) -> JDoubleArray<'local> { - let flattened = query_rope(scene_id, id); + let flattened = tracked_call!(query_rope(scene_id, id)); let double_array = env.new_double_array((flattened.len()) as jsize).unwrap(); env.set_double_array_region(&double_array, 0, &flattened) .unwrap(); @@ -749,7 +789,7 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_rem scene_id: jint, id: jlong, ) { - remove_rope(scene_id, id); + tracked_call!(remove_rope(scene_id, id)); } #[unsafe(no_mangle)] @@ -762,7 +802,7 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_set id: jlong, length: jdouble, ) { - set_rope_first_segment_length(scene_id, id, length); + tracked_call!(set_rope_first_segment_length(scene_id, id, length)); } #[unsafe(no_mangle)] @@ -774,7 +814,7 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_rem scene_id: jint, id: jlong, ) { - remove_rope_point_at_start(scene_id, id); + tracked_call!(remove_rope_point_at_start(scene_id, id)); } #[unsafe(no_mangle)] @@ -789,7 +829,7 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_add y: jdouble, z: jdouble, ) { - add_rope_point_at_start(scene_id, id, x, y, z); + tracked_call!(add_rope_point_at_start(scene_id, id, x, y, z)); } #[unsafe(no_mangle)] @@ -799,7 +839,7 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_wak scene_id: jint, rope_id: jlong, ) { - wake_up_rope(scene_id, rope_id); + tracked_call!(wake_up_rope(scene_id, rope_id)); } #[unsafe(no_mangle)] @@ -816,7 +856,15 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_set z: jdouble, end: jboolean, ) { - set_rope_attachment(scene_id, rope_id, sub_level_id, x, y, z, end); + tracked_call!(set_rope_attachment( + scene_id, + rope_id, + sub_level_id, + x, + y, + z, + end + )); } #[unsafe(no_mangle)] @@ -853,7 +901,7 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_new } else { None }; - new_voxel_collider( + tracked_call!(new_voxel_collider( friction, volume, restitution, @@ -861,7 +909,7 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_new dynamic, global_ref, global_method, - ) + )) } #[unsafe(no_mangle)] @@ -874,7 +922,7 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_add box_bounds: JDoubleArray<'local>, ) { let bounds = extract_jdouble_array!(env, box_bounds, 6); - add_voxel_collider_box(index, bounds); + tracked_call!(add_voxel_collider_box(index, bounds)); } #[unsafe(no_mangle)] @@ -885,5 +933,5 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_cle _class: JClass<'local>, index: jint, ) { - clear_voxel_collider_boxes(index); + tracked_call!(clear_voxel_collider_boxes(index)); } From ef01b35f4c04b994b01127594ebd2637dd7ab3c7 Mon Sep 17 00:00:00 2001 From: Random-Scientist Date: Wed, 22 Apr 2026 21:10:17 -0700 Subject: [PATCH 3/4] minimal playback system --- common/src/main/rust/Cargo.lock | 148 +++++++++++++++++- common/src/main/rust/Cargo.toml | 4 +- .../src/main/rust/fisher-playback/Cargo.toml | 10 ++ .../src/main/rust/fisher-playback/src/main.rs | 31 ++++ common/src/main/rust/fisher/src/imp.rs | 18 ++- .../rust/fisher/src/imp/argument_encodings.rs | 66 +++++--- common/src/main/rust/fisher/src/lib.rs | 19 ++- common/src/main/rust/rapier/Cargo.toml | 3 +- common/src/main/rust/rapier/src/api.rs | 41 +++-- .../src/main/rust/rapier/src/contraptions.rs | 4 +- common/src/main/rust/rapier/src/lib.rs | 8 +- 11 files changed, 294 insertions(+), 58 deletions(-) create mode 100644 common/src/main/rust/fisher-playback/Cargo.toml create mode 100644 common/src/main/rust/fisher-playback/src/main.rs diff --git a/common/src/main/rust/Cargo.lock b/common/src/main/rust/Cargo.lock index 89245a96..ae931cd9 100644 --- a/common/src/main/rust/Cargo.lock +++ b/common/src/main/rust/Cargo.lock @@ -32,12 +32,56 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" +[[package]] +name = "anstream" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + [[package]] name = "anstyle" version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" +[[package]] +name = "anstyle-parse" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys 0.61.2", +] + [[package]] name = "approx" version = "0.5.1" @@ -146,11 +190,12 @@ dependencies = [ [[package]] name = "clap" -version = "4.6.0" +version = "4.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b193af5b67834b676abd72466a96c1024e6a6ad978a1f484bd90b85c94041351" +checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" dependencies = [ "clap_builder", + "clap_derive", ] [[package]] @@ -159,8 +204,22 @@ version = "4.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" dependencies = [ + "anstream", "anstyle", "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -169,6 +228,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" +[[package]] +name = "colorchoice" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" + [[package]] name = "colored" version = "1.9.4" @@ -266,6 +331,23 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" +[[package]] +name = "ctor" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83cf0d42651b16c6dfe68685716d18480d18a9c39c62d76e8cf3eb6ed5d8bcbf" +dependencies = [ + "ctor-proc-macro", + "dtor", + "link-section", +] + +[[package]] +name = "ctor-proc-macro" +version = "0.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a949c44fcacbbbb7ada007dc7acb34603dd97cd47de5d054f2b6493ecebb483" + [[package]] name = "dashmap" version = "7.0.0-rc2" @@ -286,6 +368,21 @@ version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "117240f60069e65410b3ae1bb213295bd828f707b5bec6596a1afc8793ce0cbc" +[[package]] +name = "dtor" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edf234dd1594d6dd434a8fb8cada51ddbbc593e40e4a01556a0b31c62da2775b" +dependencies = [ + "dtor-proc-macro", +] + +[[package]] +name = "dtor-proc-macro" +version = "0.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2647271c92754afcb174e758003cfd1cbf1e43e5a7853d7b1813e63e19e39a73" + [[package]] name = "either" version = "1.15.0" @@ -331,6 +428,16 @@ dependencies = [ "marten", ] +[[package]] +name = "fisher-playback" +version = "0.1.0" +dependencies = [ + "clap", + "clap_derive", + "fisher", + "sable_rapier", +] + [[package]] name = "foldhash" version = "0.1.5" @@ -506,6 +613,12 @@ dependencies = [ "stable_deref_trait", ] +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "hermit-abi" version = "0.5.2" @@ -529,6 +642,12 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "is_terminal_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + [[package]] name = "itertools" version = "0.13.0" @@ -616,6 +735,12 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" +[[package]] +name = "link-section" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b685d66585d646efe09fec763d796c291049c8b6bf84e04954bffc8748341f0d" + [[package]] name = "lock_api" version = "0.4.14" @@ -767,6 +892,12 @@ version = "1.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" + [[package]] name = "oorandom" version = "11.1.5" @@ -1018,6 +1149,7 @@ version = "0.1.0" dependencies = [ "colored 2.2.0", "criterion", + "ctor", "dashmap", "fern", "fisher", @@ -1151,6 +1283,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "syn" version = "2.0.117" @@ -1224,6 +1362,12 @@ version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + [[package]] name = "walkdir" version = "2.5.0" diff --git a/common/src/main/rust/Cargo.toml b/common/src/main/rust/Cargo.toml index 492cc1da..a334625e 100644 --- a/common/src/main/rust/Cargo.toml +++ b/common/src/main/rust/Cargo.toml @@ -1,6 +1,6 @@ [workspace] resolver = "3" -members = ["fisher", "marten", "rapier"] +members = ["fisher", "fisher-playback", "marten", "rapier"] [workspace.package] edition = "2024" @@ -9,6 +9,7 @@ version = "0.1.0" [workspace.dependencies] marten = { path = "marten" } fisher = { path = "fisher" } +sable_rapier = { path = "rapier" } log = "0.4.22" jni = "0.21.1" @@ -22,4 +23,5 @@ lto = "thin" codegen-units = 1 [profile.bench] +inherits = "release" debug = true diff --git a/common/src/main/rust/fisher-playback/Cargo.toml b/common/src/main/rust/fisher-playback/Cargo.toml new file mode 100644 index 00000000..439fb8f7 --- /dev/null +++ b/common/src/main/rust/fisher-playback/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "fisher-playback" +edition.workspace = true +version.workspace = true + +[dependencies] +sable_rapier = { workspace = true, features = ["recording"] } +fisher.workspace = true +clap = { version = "4.6", features = ["derive"] } +clap_derive = { version = "4.6.1" } diff --git a/common/src/main/rust/fisher-playback/src/main.rs b/common/src/main/rust/fisher-playback/src/main.rs new file mode 100644 index 00000000..dba2af24 --- /dev/null +++ b/common/src/main/rust/fisher-playback/src/main.rs @@ -0,0 +1,31 @@ +use std::{fs::File, path::PathBuf, time::Instant}; + +use clap::Parser; +use fisher::imp::BorrowReader; +use sable_rapier::api::recording::CallType; + +fn main() { + let args = Arguments::parse(); + + let file = std::fs::read(args.instr_file).unwrap(); + let mut reader = BorrowReader::new(&file); + let start = Instant::now(); + while reader.remaining() != 0 { + let &[byte] = reader.get_bytes_const(); + let ty = CallType::new(byte); + + // dbg!(ty); + let trackable = ty.as_trackable(); + trackable.playback(&mut reader); + } + println!( + "run took {}ms", + Instant::now().saturating_duration_since(start).as_millis() + ); +} + +#[derive(Parser)] +pub struct Arguments { + /// The recorded file to parse + instr_file: PathBuf, +} diff --git a/common/src/main/rust/fisher/src/imp.rs b/common/src/main/rust/fisher/src/imp.rs index d30a00ad..17d14bcc 100644 --- a/common/src/main/rust/fisher/src/imp.rs +++ b/common/src/main/rust/fisher/src/imp.rs @@ -23,9 +23,14 @@ macro_rules! decl_tracked_api { #[repr(u8)] #[derive(::std::fmt::Debug, ::std::marker::Copy, ::std::clone::Clone)] $v enum CallType { - $( - $tok - ),+ + $( $tok, )+ + __Last, + } + impl CallType { + pub fn new(id: u8) -> Self { + ::std::assert!(id < Self::__Last as u8); + unsafe { core::mem::transmute::(id) } + } } impl CallType { pub fn as_trackable(self) -> &'static dyn $crate::imp::Trackable { @@ -33,6 +38,7 @@ macro_rules! decl_tracked_api { $( Self::$tok => const { $crate::imp::require_trackable( &$tok ) } ),+ + _ => ::std::unreachable!(), } } } @@ -43,7 +49,7 @@ macro_rules! decl_tracked_api { pub const fn require_trackable(v: &'static T) -> &'static dyn Trackable { v } -pub const fn require_argument_encoding<'a, T: ArgumentEncode<'a>>(_: &T) {} +pub const fn require_argument_encoding(_: &T) {} #[diagnostic::on_unimplemented( message = "token `{Self}` is declared but has no tracked call. Instrument a call of this token with `tracked_call!`", @@ -138,7 +144,7 @@ pub fn get_writer<'a>() -> MutexGuard<'a, Option> { label = "this parameter doesn't implement `ArgumentEncode`", note = "in order to record and playback tracked calls all of their parameters must implement `ArgumentEncode`. Please add an implementation to `rust/fisher`" )] -pub trait ArgumentEncode<'a>: Sized { - fn read(reader: &mut BorrowReader<'a>) -> Self; +pub trait ArgumentEncode: Sized { + fn read(reader: &mut BorrowReader<'_>) -> Self; fn write(&self, writer: &mut impl Write) -> io::Result<()>; } diff --git a/common/src/main/rust/fisher/src/imp/argument_encodings.rs b/common/src/main/rust/fisher/src/imp/argument_encodings.rs index 4fece17d..8c8ee975 100644 --- a/common/src/main/rust/fisher/src/imp/argument_encodings.rs +++ b/common/src/main/rust/fisher/src/imp/argument_encodings.rs @@ -18,17 +18,24 @@ pub struct BorrowReader<'a> { cursor: usize, } impl<'a> BorrowReader<'a> { - fn get_bytes(&mut self, ct: usize) -> &'a [u8] { - assert!(self.cursor + ct < self.bytes.len()); + pub fn get_bytes(&mut self, ct: usize) -> &'a [u8] { + assert!(self.cursor + ct <= self.bytes.len()); + let out = &self.bytes[self.cursor..self.cursor + ct]; self.cursor += ct; - &self.bytes[self.cursor..self.cursor + ct] + out } - fn get_bytes_const(&mut self) -> &[u8; N] { + pub fn get_bytes_const(&mut self) -> &[u8; N] { self.get_bytes(N).first_chunk().unwrap() } + pub fn new(bytes: &'a [u8]) -> Self { + Self { bytes, cursor: 0 } + } + pub fn remaining(&self) -> usize { + self.bytes.len() - self.cursor + } } -impl ArgumentEncode<'_> for () { +impl ArgumentEncode for () { fn read(_: &mut BorrowReader) -> Self {} fn write(&self, _: &mut impl Write) -> io::Result<()> { Ok(()) @@ -38,7 +45,7 @@ impl ArgumentEncode<'_> for () { macro_rules! numeric_argument_encodings { ( $( $intty:ty ),+ ) => { $( - impl ArgumentEncode<'_> for $intty { + impl ArgumentEncode for $intty { fn read(reader: &mut BorrowReader<'_>) -> Self { <$intty>::from_le_bytes(*reader.get_bytes_const()) } @@ -52,8 +59,8 @@ macro_rules! numeric_argument_encodings { } numeric_argument_encodings!(jboolean, jbyte, jchar, jshort, jint, jlong, jfloat, jdouble); -impl<'a, const N: usize, T: ArgumentEncode<'a>> ArgumentEncode<'a> for [T; N] { - fn read(reader: &mut BorrowReader<'a>) -> Self { +impl ArgumentEncode for [T; N] { + fn read(reader: &mut BorrowReader) -> Self { array::from_fn(|_| T::read(reader)) } @@ -64,37 +71,48 @@ impl<'a, const N: usize, T: ArgumentEncode<'a>> ArgumentEncode<'a> for [T; N] { Ok(()) } } +impl ArgumentEncode for Box { + fn read(reader: &mut BorrowReader) -> Self { + Box::new(T::read(reader)) + } -#[cfg(target_endian = "big")] -compile_error!("fisher does not work on big endian platforms at the moment"); + fn write(&self, writer: &mut impl Write) -> io::Result<()> { + (**self).write(writer) + } +} -impl<'a, const N: usize> ArgumentEncode<'a> for &'a [jint; N] { - fn read(reader: &mut BorrowReader<'a>) -> Self { - let count = N * size_of::(); - let bytes = reader.get_bytes(count); - // Safety: jint aka i32 is valid at every bit pattern of [u8; size_of::()] - unsafe { core::slice::from_raw_parts(bytes.as_ptr().cast::(), N) } - .first_chunk() - .unwrap() +impl ArgumentEncode for Vec { + fn read(reader: &mut BorrowReader<'_>) -> Self { + let len = u32::from_le_bytes(*reader.get_bytes_const()); + let bytes = reader.get_bytes(len as usize * size_of::()); + bytes + .as_chunks() + .0 + .iter() + .copied() + .map(jdouble::from_le_bytes) + .collect() } fn write(&self, writer: &mut impl Write) -> io::Result<()> { + writer.write_all( + &(u32::try_from(self.len()).expect("array len to be <= u32::MAX")).to_le_bytes(), + )?; writer.write_all(unsafe { - core::slice::from_raw_parts(self.as_ptr().cast::(), std::mem::size_of_val(*self)) + core::slice::from_raw_parts(self.as_ptr().cast::(), std::mem::size_of_val(&**self)) }) } } - -impl ArgumentEncode<'_> for Vec { +impl ArgumentEncode for Vec { fn read(reader: &mut BorrowReader<'_>) -> Self { let len = u32::from_le_bytes(*reader.get_bytes_const()); - let bytes = reader.get_bytes(len as usize * size_of::()); + let bytes = reader.get_bytes(len as usize * size_of::()); bytes .as_chunks() .0 .iter() .copied() - .map(jdouble::from_le_bytes) + .map(jint::from_le_bytes) .collect() } @@ -110,7 +128,7 @@ impl ArgumentEncode<'_> for Vec { macro_rules! ignore_option_impls { ( $( $opt:ty ),+ ) => { $( - impl ArgumentEncode<'_> for Option<$opt> { + impl ArgumentEncode for Option<$opt> { fn read(_: &mut BorrowReader<'_>) -> Self { None } diff --git a/common/src/main/rust/fisher/src/lib.rs b/common/src/main/rust/fisher/src/lib.rs index a4bb8064..d02ab0bc 100644 --- a/common/src/main/rust/fisher/src/lib.rs +++ b/common/src/main/rust/fisher/src/lib.rs @@ -1,3 +1,7 @@ +use std::io::Write; + +use crate::imp::FISHER_WRITER; + #[cfg(feature = "enabled")] pub mod imp; @@ -11,11 +15,18 @@ macro_rules! decl_tracked_api { macro_rules! tracked_call { ( $( $any:tt )* ) => { $( $any )* }; } -#[cfg(not(feature = "enabled"))] -pub fn setup_trace() {} -#[cfg(feature = "enabled")] pub fn setup_trace() { + use std::sync::Once; + use crate::imp::Writer; - *imp::FISHER_WRITER.lock().unwrap() = Some(Writer::new("sable_trace.bin")); + static OPEN_FILE_ONCE: Once = Once::new(); + OPEN_FILE_ONCE + .call_once(|| *imp::FISHER_WRITER.lock().unwrap() = Some(Writer::new("sable_trace.bin"))); +} +pub fn finish_trace() { + if let Some(mut v) = FISHER_WRITER.lock().unwrap().take() { + v.flush().unwrap(); + drop(v); + } } diff --git a/common/src/main/rust/rapier/Cargo.toml b/common/src/main/rust/rapier/Cargo.toml index fc1855e2..9dfb1bbf 100644 --- a/common/src/main/rust/rapier/Cargo.toml +++ b/common/src/main/rust/rapier/Cargo.toml @@ -21,6 +21,7 @@ rapier3d = { git = "https://github.com/ryanhcode/rapier", rev = "38e92f117590862 rayon = "1.10.0" dashmap = "7.0.0-rc2" +ctor = { version = "0.10.1", optional = true } [lib] crate-type = ["cdylib", "lib"] @@ -38,4 +39,4 @@ criterion = "0.8.2" #TODO remove default = ["recording"] -recording = ["fisher/enabled"] +recording = ["fisher/enabled", "dep:ctor"] diff --git a/common/src/main/rust/rapier/src/api.rs b/common/src/main/rust/rapier/src/api.rs index 39dc51e4..9c53cbeb 100644 --- a/common/src/main/rust/rapier/src/api.rs +++ b/common/src/main/rust/rapier/src/api.rs @@ -1,4 +1,6 @@ -use fisher::{setup_trace, tracked_call}; +use core::slice; + +use fisher::tracked_call; use jni::{ JNIEnv, JavaVM, objects::{JClass, JDoubleArray, JIntArray, JObject}, @@ -38,14 +40,6 @@ macro_rules! extract_jdouble_array { }}; } -macro_rules! extract_jint_array { - ($env:expr, $jarr:expr, $len:expr) => {{ - let mut arr = [0 as jint; $len]; - $env.get_int_array_region($jarr, 0, &mut arr).unwrap(); - arr - }}; -} - fisher::decl_tracked_api! { pub mod recording { add_chunk, add_linear_velocities, apply_force, apply_force_and_torque, create_box, remove_box, change_block, clear_collisions, @@ -194,9 +188,14 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_add z: jint, data: JIntArray<'local>, ) { - let ints = extract_jint_array!(env, data, 4096); + let mut buf = Box::<[i32; 4096]>::new_uninit(); + env.get_int_array_region(data, 0, unsafe { + slice::from_raw_parts_mut((*buf).as_mut_ptr().cast(), 4096) + }) + .expect("to copy int array"); + let buf = unsafe { buf.assume_init() }; tracked_call!(add_kinematic_contraption_chunk_section( - scene_id, id, x, y, z, &ints + scene_id, id, x, y, z, buf )); } @@ -450,7 +449,6 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_ini z: jdouble, universal_drag: jdouble, ) { - setup_trace(); let vm = unsafe { JavaVM::from_raw(env.get_java_vm().unwrap().get_java_vm_pointer()).unwrap() }; tracked_call!(initialize(Some(vm), scene_id, x, y, z, universal_drag)); } @@ -560,8 +558,13 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_add global: jboolean, object_id: jint, ) { - let ints = extract_jint_array!(env, data, 4096); - tracked_call!(add_chunk(scene_id, x, y, z, &ints, global, object_id)); + let mut buf = Box::<[i32; 4096]>::new_uninit(); + env.get_int_array_region(data, 0, unsafe { + slice::from_raw_parts_mut((*buf).as_mut_ptr().cast(), 4096) + }) + .expect("to copy int array"); + let buf = unsafe { buf.assume_init() }; + tracked_call!(add_chunk(scene_id, x, y, z, buf, global, object_id)); } #[unsafe(no_mangle)] @@ -935,3 +938,13 @@ pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_cle ) { tracked_call!(clear_voxel_collider_boxes(index)); } +#[cfg(feature = "recording")] +#[ctor::ctor] +pub fn ctor() { + fisher::setup_trace(); +} +#[cfg(feature = "recording")] +#[ctor::dtor] +pub fn dtor() { + fisher::finish_trace(); +} diff --git a/common/src/main/rust/rapier/src/contraptions.rs b/common/src/main/rust/rapier/src/contraptions.rs index 841fd412..3f58ba74 100644 --- a/common/src/main/rust/rapier/src/contraptions.rs +++ b/common/src/main/rust/rapier/src/contraptions.rs @@ -159,10 +159,10 @@ pub fn add_kinematic_contraption_chunk_section( x: jint, y: jint, z: jint, - ints: &[i32; 4096], + ints: Box<[jint; 4096]>, ) { let mut blocks = Vec::with_capacity(ints.len()); - for block in ints { + for block in ints.into_iter() { let block_collider_id = (block >> 16) as u16; let voxel_state_id = (block & 0xFFFF) as u16; blocks.push(( diff --git a/common/src/main/rust/rapier/src/lib.rs b/common/src/main/rust/rapier/src/lib.rs index f9c888b4..5bbd02d5 100644 --- a/common/src/main/rust/rapier/src/lib.rs +++ b/common/src/main/rust/rapier/src/lib.rs @@ -1,7 +1,7 @@ #![allow(static_mut_refs)] pub mod algo; -mod api; +pub mod api; pub mod boxes; mod buoyancy; mod collider; @@ -16,8 +16,8 @@ pub mod rope; mod scene; mod voxel_collider; -use jni::sys::{jboolean, jdouble, jint}; use jni::JavaVM; +use jni::sys::{jboolean, jdouble, jint}; use rapier3d::glamx::Quat; use rapier3d::math::Vector; use std::collections::HashMap; @@ -664,13 +664,13 @@ pub fn add_chunk( x: jint, y: jint, z: jint, - ints: &[jint; 4096], + ints: Box<[jint; 4096]>, global: jboolean, object_id: jint, ) { let mut blocks = Vec::with_capacity(ints.len()); - for block in ints { + for block in ints.into_iter() { // split it in half let block_collider_id = (block >> 16) as u16; let voxel_state_id = (block & 0xFFFF) as u16; From c792d27e0e1527f493d328a3741fabdf41978773 Mon Sep 17 00:00:00 2001 From: Random-Scientist Date: Sun, 26 Apr 2026 16:56:22 -0700 Subject: [PATCH 4/4] (terribly ugly) fix to release recording lock before calling --- .../src/main/rust/fisher-playback/src/main.rs | 2 +- common/src/main/rust/fisher/src/imp.rs | 68 +++++++++++++++---- 2 files changed, 57 insertions(+), 13 deletions(-) diff --git a/common/src/main/rust/fisher-playback/src/main.rs b/common/src/main/rust/fisher-playback/src/main.rs index dba2af24..7c46fec2 100644 --- a/common/src/main/rust/fisher-playback/src/main.rs +++ b/common/src/main/rust/fisher-playback/src/main.rs @@ -1,4 +1,4 @@ -use std::{fs::File, path::PathBuf, time::Instant}; +use std::{path::PathBuf, time::Instant}; use clap::Parser; use fisher::imp::BorrowReader; diff --git a/common/src/main/rust/fisher/src/imp.rs b/common/src/main/rust/fisher/src/imp.rs index 17d14bcc..f29f6eaa 100644 --- a/common/src/main/rust/fisher/src/imp.rs +++ b/common/src/main/rust/fisher/src/imp.rs @@ -87,23 +87,67 @@ macro_rules! tracked_call { }; let mut w = $crate::imp::get_writer(); - if let ::std::option::Option::Some(w) = &mut *w { - w.frame(TRACKED_ID).unwrap(); - // TODO is there a way to drop w before calling f to shorten the critical section here? - $f($( - { - let arg = $arg; - $crate::imp::ArgumentEncode::write(&arg, &mut **w).expect("write to succeed"); - arg - } - ),* - ) + if w.is_some() { + w.as_mut().unwrap().frame(TRACKED_ID).unwrap(); + let mut w_opt = Some(w); + $crate::tracked_call! { is_last_arg($f, w_opt): () $( $arg ),* } } else { - drop(w); $f( $( $arg ),* ) } } }; + // munch non-last arg + ( + is_last_arg($f:ident, $w:ident): ( + $( ($out:expr, $last:literal) ),* + ) + $arg:expr, $( $args:expr ),+ + ) => { + $crate::tracked_call! { + is_last_arg($f, $w): ( + $( ( $out, $last ), )* + ($arg, false) + ) + $( $args ),+ + } + }; + // munch last arg + ( + is_last_arg($f:ident, $w:ident): ( + $( ($out:expr, $last:literal) ),* + ) + $arg:expr $(,)? + ) => { + $crate::tracked_call! { + is_last_arg($f, $w): ( + $( ( $out, $last ), )* + ($arg, true) + ) + + } + }; + // call with some args + ( is_last_arg($f:ident, $w:ident): ( $( ($arg:expr, $last:literal) ),+ ) ) => { + $f( + $( + { + let arg = $arg; + $crate::imp::ArgumentEncode::write(&arg, &mut **($w.as_deref_mut().map(Option::as_mut).flatten().expect("lock to be held"))).expect("write to succeed"); + if $last { + // release lock on recording + ::std::mem::drop(::std::mem::take(&mut $w)); + } + arg + } + ),+ + ) + }; + // call with no args + ( is_last_arg($f:ident, $w:ident): () ) => { + ::std::mem::drop($w); + $f() + }; + } pub struct Writer(BufWriter);