ants/docs/INFRASTRUCTURE.md
2026-03-09 10:22:03 -04:00

308 lines
12 KiB
Markdown

# Infrastructure Requirements
Foundational data structures and capabilities needed to support all features
described in REALISM-IDEAS.md. These are the structural primitives — every
realism feature reduces to needing one or more of these five layers.
## A. Expanded ant state
Currently all 4 RGBA Float32 channels are used:
R = pos.x, G = pos.y, B = angle, A = packed(storage | isCarrying)
Most realism features need more per-ant data. This means a second ant
texture (RGBA Float32, same dimensions, same ping-pong pattern).
What needs to live in ant state:
personality/threshold float #3 individuality, #4 adaptive noise
steps since pickup int #1 distance-modulated deposition
cargo quality float #5 food quality encoding
path integration dx float #9 path integration
path integration dy float #9 path integration
role/caste float #11 caste switching
colony ID int #12 CHC recognition
That's 7 values. Two RGBA textures give 8 floats total, so with some
packing of ints into float bits (steps, colony ID, carrying flag are all
small integers) it fits in 2 textures. If the packing gets uncomfortable
a third texture is an option — WebGL supports up to 16 texture units per
shader, current shaders use 2-3, so headroom is fine.
Layout sketch for texture 2:
R = personality (0.0 = pure follower, 1.0 = pure explorer)
G = cargo quality (0.0 when not carrying, otherwise food quality value)
B = path integration dx (accumulated displacement since last reset)
A = packed(path_integration_dy | role | colony_id | steps_since_pickup)
The A channel packing is tight. Alternative: move steps_since_pickup into
texture 1's storage bits (current storage uses ~20 of 31 available bits)
and give path_integration_dy its own float in texture 2.A. Then role and
colony_id pack together (role quantized to 8 bits, colony_id in 8 bits =
16 bits, fits in the fractional part of another channel).
Pipeline impact:
- AntsComputeScene needs to read/write 2 textures per ant instead of 1
- Ping-pong doubles from 2 textures to 4
- antsCompute.frag gets a second sampler input and second render target
(requires MRT — multiple render targets — or a second pass)
- MRT is available in WebGL2 via gl.drawBuffers(), clean solution
Features unlocked: #1, #3, #4, #5, #9, #11, #12
## B. Generalized pheromone system
Currently 2 pheromone channels in the world texture:
G = scentToHome, B = scentToFood
A = unused (free slot for one more)
Minimum viable expansion: put repellent pheromone in world.A. That gives
3 channels with zero new textures.
But alarm pheromone (#6) and multicomponent blends (#8) need more. Options:
Option 1: Second world texture (RGBA Float32, same worldSize)
R = alarm pheromone
G = blend component A
B = blend component B
A = reserved
Option 2: Multiplex channels temporally or semantically
Less clean, harder to reason about, not recommended
Going with option 1, total pheromone channels = 6:
world1.G scentToHome
world1.B scentToFood
world1.A repellent
world2.R alarm
world2.G blend component A
world2.B blend component B
The blur/diffusion shader must generalize:
- Currently applies one blur radius and one fade-out factor uniformly
- Needs per-channel parameters:
channel decay rate diffusion radius notes
toHome medium medium standard trail
toFood medium medium standard trail
repellent slow (2x) narrow persists longer per biology
alarm fast wide spreads fast, fades fast
blend A configurable configurable depends on compound
blend B configurable configurable depends on compound
- Pass these as a uniform array or pack into a small config texture
- The blur pass runs once but processes all channels with their own params
Pipeline impact:
- worldBlur.frag becomes per-channel parameterized
- world.frag merges deposits into more channels
- antsCompute.frag reads more pheromone channels for decision-making
- antsDiscretize.frag may need more output channels (MRT again, or
expand discrete texture to RGBA Float32 to carry more deposit types)
Features unlocked: #2, #6, #8
## C. World cell metadata
Currently world.R uses 3 bits for cell flags:
bit 0 = hasFood, bit 1 = isHome, bit 2 = isObstacle
bits 3-31 = unused (29 bits free in float representation)
Several features need more per-cell information:
terrain type 3-4 bits #7 substrate-dependent decay
colony ownership 4-8 bits #12 multi-colony territories
food quality ~8 bits #5 quality encoding (quantized)
Terrain type (3 bits = 8 terrain types) and colony ownership (4 bits = 16
colonies) fit cleanly into world.R's unused bits:
bits 0-2: cell type flags (food, home, obstacle) — existing
bits 3-5: terrain type (0=default, 1-7 = surface variants)
bits 6-9: colony ID that owns this cell (0=neutral, 1-15 = colonies)
bits 10-17: food quality (0-255 quantized, only meaningful when hasFood)
bits 18-31: reserved
This keeps everything in a single channel with bit operations the shader
already uses (the existing code does bitwise AND/OR on world.R).
Terrain type feeds into the blur shader — the decay rate per cell becomes:
effective_decay = base_decay * terrain_decay_multiplier[terrain_type]
This means the blur shader needs to read the world texture (not just the
blurred copy) to know each cell's terrain type. Small perf cost but
the texture is already bound.
Food quality feeds into ant pickup behavior — when an ant grabs food, it
reads the quality bits and stores them in its cargo quality channel
(ant texture 2.G from section A).
Features unlocked: #5, #7, #12
## D. Ant-ant spatial interaction
Currently zero. Ants only interact through stigmergy (pheromone trails in
the world texture). No ant knows about any other ant's position or state.
The discrete ants texture maps ant deposits to grid cells but doesn't
preserve ant identity — it just accumulates pheromone values.
To enable ant-ant awareness, options:
Option 1: Identity-preserving discrete texture
Instead of (or in addition to) accumulating pheromone deposits,
store the ant index of whoever occupies each cell. Multiple ants
per cell requires either:
a) Last-write-wins (lossy but simple, GPU-friendly)
b) Linked list in a buffer (complex, needs WebGL2 atomic ops or
compute shaders)
c) Spatial hash with fixed bucket size (e.g. 4 ants per cell,
pack 4 ant indices into RGBA)
Option (a) is probably fine — tandem running only needs to know
"is there an ant near me" and check one neighbor, not enumerate
all neighbors.
Option 2: Proximity via world texture overlay
A separate texture where each cell stores the ID (or packed state)
of an ant occupying it. Ants sample neighboring cells to find nearby
ants. Radius-1 gives 8 neighbors, radius-2 gives 24.
For tandem running: leader deposits its ID in the cell. Follower
checks adjacent cells for the leader's ID, moves toward it.
For CHC recognition: ant reads neighbor cell, extracts colony ID
from neighbor's state, compares to own colony ID.
Implementation:
- New texture: antsPresenceTexture (RGBA Float32, worldSize x worldSize)
- R = ant index (or packed ant state subset)
- G = colony ID of occupant
- B = role/caste of occupant
- A = reserved
- Written during discretize pass, read during ant compute pass
- Cleared each frame before discretize
Tandem running specifics:
- Leader state: "has follower" flag, movement gated on follower proximity
- Follower state: "leader index" reference, moves toward leader cell
- Pairing logic: when a returning forager passes a naive ant, the naive
ant checks if the forager is available as a leader (not already paired)
- Race condition on pairing: last-write-wins means two ants might both
claim the same leader. Acceptable — worst case is a broken tandem that
reforms next frame.
Pipeline impact:
- New texture in discretize pass
- antsCompute.frag gets a new sampler for neighbor awareness
- Possibly a CPU readback for complex pairing logic that's too hard on GPU
Features unlocked: #10, #12
## E. Colony-level feedback
Currently no aggregate state. Individual ants have no information about the
colony as a whole — they only react to local pheromone concentrations.
Caste switching (#11) and alarm response scaling (#6) need colony-wide stats:
forager count how many ants are currently foraging
scout count how many ants are currently exploring
total food collected cumulative food delivered to nest
threat level number of alarm pheromone cells above threshold
colony size total ants (static, but relevant for ratios)
Two approaches:
Option 1: GPU reduction
Run a reduction shader that sums values across the ant texture:
- Count ants with isCarrying = 1 (foragers returning)
- Count ants with role > threshold (scouts)
- Sum food deposits at home cells
Requires multiple passes halving texture dimensions each time
(standard parallel reduction). Result lands in a 1x1 texture.
Read the 1x1 texture as a uniform for the next frame.
Pros: stays on GPU, no sync stall
Cons: multiple passes, more textures, latency (stats are 1 frame old)
Option 2: CPU readback
Use gl.readPixels on the ant texture (or a downsampled version).
Compute stats on CPU. Upload as uniforms next frame.
Pros: simpler to implement, flexible computation
Cons: GPU-CPU sync stall (readPixels is blocking in WebGL1, async in
WebGL2 via pixel buffer objects / fences). Could downsample first to
reduce transfer size.
Option 3: Hybrid
Reduce on GPU to a small texture (e.g. 4x4), read back 16 pixels
instead of thousands, compute final stats on CPU.
This is probably the pragmatic choice.
Colony stats feed back as uniforms into antsCompute.frag:
uniform float u_foragerRatio; // foragers / total ants
uniform float u_scoutRatio; // scouts / total ants
uniform float u_foodCollected; // cumulative food at nest
uniform float u_threatLevel; // alarm pheromone intensity
Individual ants use these to adjust their role variable:
- If foragerRatio is high and scoutRatio is low, some foragers transition
toward scouting (role variable drifts)
- If threatLevel is high, nearby ants shift toward defensive behavior
- Transitions are gradual (continuous role variable, not discrete switch)
Features unlocked: #6, #11
## Dependency graph
Which infrastructure layers does each feature need?
feature A B C D E
ant pher cell a2a agg
#1 distance-modulated deposition x
#2 negative pheromone x
#3 individual thresholds x
#4 adaptive noise x
#5 food quality encoding x x
#6 alarm pheromone x x
#7 substrate-dependent decay x x
#8 multicomponent blends x
#9 path integration x
#10 tandem running x x
#11 caste switching x x
#12 CHC recognition x x x
Layer A (expanded ant state) unlocks: 9 of 12 features
Layer B (generalized pheromones) : 4 of 12 features
Layer C (cell metadata) : 3 of 12 features
Layer D (ant-ant interaction) : 2 of 12 features
Layer E (colony feedback) : 2 of 12 features
Suggested build order based on feature unlock count and dependency:
1. Layer A — second ant texture + MRT (unlocks most features)
2. Layer B — pheromone channel expansion + per-channel blur params
3. Layer C — bit-packed cell metadata (small change, high leverage)
4. Layer E — colony aggregate readback (needed before caste switching)
5. Layer D — spatial neighbor queries (hardest, fewest features, do last)
## WebGL2 requirements
Several infrastructure pieces depend on WebGL2 features:
MRT (multiple render targets) layers A, D
gl.drawBuffers() writing 2+ textures per pass
pixel buffer objects layer E async readback
integer textures (optional) cleaner bit packing in layers A, C
The simulation already uses three.js WebGLRenderer. Confirm it's running
in WebGL2 mode (three.js defaults to WebGL2 when available). If targeting
WebGL1 fallback, MRT requires WEBGL_draw_buffers extension and some
features may not be feasible.