crispigt.

Evaluation of performance, perception, and the final call

The accuracy post settled whether the math is right. Next is whether it's fast.

stress test setup

I skipped micro-benchmarks and went for the number a game would care about, 100 unity spheres, 712 triangles each, dropped into rolling Gerstner waves, measured with a simple FPSLogger script that averages frame count over 1-second windows and prints to the console.

Three configurations, same scene, same waves. The linear path has all 100 bodies on the C++ DLL's built-in triangle clipper with no adaptive refinement, adaptive N=1 takes one refinement sample per intersecting chord (the lightest adaptive cost possible), and adaptive N=2 takes two per chord.

Stress test results

the numbers

The linear path averages 363 FPS and stays flat for the whole 10-second run, 6× the 60 FPS real-time target with 100 simultaneous physics bodies. The C++ DLL and the Burst-compiled wave sampler hold up fine.

Spheres

The adaptive paths don't. Both start okay (N=1 opens at 206 FPS, N=2 at 44 FPS) and then fall off over time. By 7 seconds N=2 is at 2 FPS. By 35 seconds N=1 is at 3 FPS.

why it gets worse over time

When the spheres are falling through the air, zero triangles are intersecting the water, so the adaptive clipper has nothing to do. As more spheres land and reach equilibrium, more triangles straddle the waterline every physics update. At equilibrium, a floating sphere has roughly a belt of triangles permanently half-submerged, maximising the number of SampleHeight calls per frame.

Each SampleHeight is a 2-iteration Newton solve evaluating three Gerstner waves with 6 trig calls per iteration. With N=2 adaptive samples, each intersecting triangle fires 2 extra SampleHeight calls. Across 100 spheres with 712 triangles each, a good fraction of them intersecting the surface, that's tens of thousands of Newton solves per FixedUpdate.

On top of that, the AdaptiveClipper builds its sub-triangle lists with C# List<T>, managed heap allocations the garbage collector has to clean up every frame. Trig overhead plus GC pressure feeds on itself, slower frames queue more physics steps, more physics steps mean more work per frame, which means slower frames.