feat(ludic.physics): phys_own - a body owns a shape made for it alone, and phys_remove frees it with the body
A chunk's trunks and boulders put in and taken out made a new shape each time and freed none. The table is by the body's index, made once at the world's size. tests/reuse_test: 1000 owned bodies put and removed hold no more shapes and 32 bytes of heap. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
404ee2ee3b
commit
5cd53d4024
6 changed files with 54 additions and 1 deletions
|
|
@ -36,11 +36,12 @@ export function phys_count(physics_st: PhysicsState) -> int {
|
||||||
return jph_world_bodies(physics_st.ph_world)
|
return jph_world_bodies(physics_st.ph_world)
|
||||||
}
|
}
|
||||||
|
|
||||||
# gone from the world, for good: what a struck tent needs
|
# gone from the world, for good: what a struck tent needs, and the shape it owned (owned.ludic) with it
|
||||||
export function phys_remove(physics_st: mut PhysicsState, id: int) -> void {
|
export function phys_remove(physics_st: mut PhysicsState, id: int) -> void {
|
||||||
if physics_st.ph_world == null or id < 0 { return }
|
if physics_st.ph_world == null or id < 0 { return }
|
||||||
phys_sink(physics_st, id)
|
phys_sink(physics_st, id)
|
||||||
jph_body_remove(physics_st.ph_world, id)
|
jph_body_remove(physics_st.ph_world, id)
|
||||||
|
ph_own_free(physics_st, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
# where a body is, how it is turned and how fast it goes, into the caller's own record (a query asked
|
# where a body is, how it is turned and how fast it goes, into the caller's own record (a query asked
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import "ludic.base"
|
||||||
import "native.ludic"
|
import "native.ludic"
|
||||||
import "state.ludic"
|
import "state.ludic"
|
||||||
import "shape_ids.ludic"
|
import "shape_ids.ludic"
|
||||||
|
import "owned.ludic"
|
||||||
import "shapes.ludic"
|
import "shapes.ludic"
|
||||||
import "bodies.ludic"
|
import "bodies.ludic"
|
||||||
import "queries.ludic"
|
import "queries.ludic"
|
||||||
|
|
|
||||||
28
packages/ludic.physics/owned.ludic
Normal file
28
packages/ludic.physics/owned.ludic
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
# owned.ludic - a shape a body owns: made for that body alone (a trunk's pillar, a boulder's scaled
|
||||||
|
# hull, a dropped thing's box) and freed when the body is removed, so a still thing put in and taken out
|
||||||
|
# again and again - a chunk that comes and goes - holds no more shapes than are standing
|
||||||
|
const PH_INDEX: int = 8388607 # a body id's low 23 bits are its index (Jolt's BodyID)
|
||||||
|
|
||||||
|
# every body owns nothing: at a world's opening, the table made once at its size and kept after
|
||||||
|
function ph_own_reset(physics_st: mut PhysicsState, max_bodies: int) -> void {
|
||||||
|
if len(physics_st.ph_own) < max_bodies {
|
||||||
|
List.clear(physics_st.ph_own)
|
||||||
|
for i in 0 .. max_bodies { push(physics_st.ph_own, -1) }
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for i in 0 .. len(physics_st.ph_own) { physics_st.ph_own[i] = -1 }
|
||||||
|
}
|
||||||
|
|
||||||
|
# body `id` owns `shape`: phys_remove frees it with the body
|
||||||
|
export function phys_own(physics_st: mut PhysicsState, id: int, shape: int) -> void {
|
||||||
|
if id < 0 or shape < 0 { return }
|
||||||
|
let i = id & PH_INDEX
|
||||||
|
if i < len(physics_st.ph_own) { physics_st.ph_own[i] = shape }
|
||||||
|
}
|
||||||
|
|
||||||
|
function ph_own_free(physics_st: mut PhysicsState, id: int) -> void {
|
||||||
|
let i = id & PH_INDEX
|
||||||
|
if i >= len(physics_st.ph_own) or physics_st.ph_own[i] < 0 { return }
|
||||||
|
phys_shape_free(physics_st, physics_st.ph_own[i])
|
||||||
|
physics_st.ph_own[i] = -1
|
||||||
|
}
|
||||||
|
|
@ -6,6 +6,7 @@ export function phys_open(physics_st: mut PhysicsState, max_bodies: int, threads
|
||||||
phys_close(physics_st)
|
phys_close(physics_st)
|
||||||
ph_buffers()
|
ph_buffers()
|
||||||
physics_st.ph_world = jph_world_new(max_bodies, threads)
|
physics_st.ph_world = jph_world_new(max_bodies, threads)
|
||||||
|
ph_own_reset(physics_st, max_bodies)
|
||||||
physics_st.ph_gen += 1
|
physics_st.ph_gen += 1
|
||||||
physics_st.ph_acc = 0.0
|
physics_st.ph_acc = 0.0
|
||||||
physics_st.ph_steps = 0
|
physics_st.ph_steps = 0
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ export state PhysicsState {
|
||||||
ph_world: pointer = null
|
ph_world: pointer = null
|
||||||
ph_shapes: []pointer = new []pointer # a shape id is its place here; null once freed
|
ph_shapes: []pointer = new []pointer # a shape id is its place here; null once freed
|
||||||
ph_spare: []int = new []int # the freed places, handed out again (shape_ids.ludic)
|
ph_spare: []int = new []int # the freed places, handed out again (shape_ids.ludic)
|
||||||
|
ph_own: []int = new []int # by a body's index: the shape it owns, freed with it (owned.ludic)
|
||||||
ph_step: float = 0.016666668 # seconds a step
|
ph_step: float = 0.016666668 # seconds a step
|
||||||
ph_sub: int = 1 # collision steps in each
|
ph_sub: int = 1 # collision steps in each
|
||||||
ph_max_steps: int = 4 # a slow frame catches up this far and no further
|
ph_max_steps: int = 4 # a slow frame catches up this far and no further
|
||||||
|
|
|
||||||
|
|
@ -39,4 +39,25 @@ program ReuseTest {
|
||||||
expect_eq(phys_shapes(physics_st), 1)
|
expect_eq(phys_shapes(physics_st), 1)
|
||||||
phys_close(physics_st)
|
phys_close(physics_st)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "a body that owns its shape takes the shape with it, a thousand times over" (physics_st: mut PhysicsState) {
|
||||||
|
expect(phys_open(physics_st, 64, 1))
|
||||||
|
let s0 = phys_cylinder(physics_st, 5.0, 0.3)
|
||||||
|
phys_own(physics_st, phys_static_add(physics_st, s0, 0.0, 0.0, 0.0, 0.0), s0)
|
||||||
|
let first = phys_static_add(physics_st, phys_cylinder(physics_st, 5.0, 0.3), 3.0, 0.0, 0.0, 0.0)
|
||||||
|
phys_remove(physics_st, first)
|
||||||
|
let before = Os.heap_bytes()
|
||||||
|
for k in 0 .. 1000 {
|
||||||
|
let s = phys_cylinder(physics_st, 5.0, 0.3)
|
||||||
|
let b = phys_static_add(physics_st, s, float(k % 10), 0.0, 5.0, 0.0)
|
||||||
|
phys_own(physics_st, b, s)
|
||||||
|
phys_remove(physics_st, b)
|
||||||
|
}
|
||||||
|
let grew = Os.heap_bytes() - before
|
||||||
|
print(`reuse: 1000 owned bodies put and removed, {phys_shapes(physics_st)} shapes held in {len(physics_st.ph_shapes)} places, heap {grew} bytes`)
|
||||||
|
expect(phys_shapes(physics_st) <= 2)
|
||||||
|
expect(len(physics_st.ph_shapes) <= 3)
|
||||||
|
expect(grew < long(1024))
|
||||||
|
phys_close(physics_st)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue