From 6cef5a35b2a7c47074e548591d58b7a2f67fc472 Mon Sep 17 00:00:00 2001 From: Jared Miller Date: Thu, 12 Mar 2026 20:14:59 -0400 Subject: [PATCH] Add debugging notes --- docs/debug/air-color-corruption.md | 161 +++++++++++++++++++++++++++++ 1 file changed, 161 insertions(+) create mode 100644 docs/debug/air-color-corruption.md diff --git a/docs/debug/air-color-corruption.md b/docs/debug/air-color-corruption.md new file mode 100644 index 0000000..615b41e --- /dev/null +++ b/docs/debug/air-color-corruption.md @@ -0,0 +1,161 @@ +air cell corruption debug log +============================= + +problem +------- +air cells (materialId=0) in the world render target get corrupted to +materialId=255 after ~10 simulation frames. this causes air to render +as BLACK instead of any intended color, because the material color +lookup texture has no entry at index 255 (returns all zeros). + +the original request was to change air from black to light blue. +the shader change is trivial but has no effect because the materialId +is wrong — the shader's `materialId == MAT_AIR` branch never fires. + + +what we confirmed +----------------- +1. init data is correct: air cells = [0,0,0,0], sand cells = [1,0,0,0] + (sync readback immediately after generateSideViewWorld + copyTextureToTexture) + +2. frame 0 is clean: every render pass in the pipeline produces [0,0,0,0] + for an air cell at top-center (px=512, py=1023). checked after each: + - sandPhysicsRT: [0,0,0,0] + - worldBlurredRT: [0,0,0,0] + - antsDiscreteRT: [0,0,0,0] + - worldRT OUTPUT: [0,0,0,0] + +3. by frame 10, worldRT INPUT (read before sandPhysics) = [255,0,0,0] + meaning the corruption enters between frame 9's worldCompute output + (which passed the check) and frame 10's sandPhysics input + +4. renderToScreen runs between those frames (the sim loop does ~1 step + per rAF, then calls renderToScreen). so the corruption window is + during renderToScreen or between renderToScreen and the next sim step. + +5. the screen shader IS being used for the full viewport (confirmed by + outputting solid blue — entire screen turned blue) + +6. MAT_AIR define is correctly 0 (logged from ScreenScene constructor) + +7. the shader source reaching the material IS the correct file + (first 200 chars logged and verified) + + +what we ruled out +----------------- +- shader not loading: confirmed via all-blue test and console.debug of source +- material color lookup texture: registry has correct values, texture + generation code (generateColorData) is straightforward +- texture filtering: all render targets use NearestFilter +- sand physics: passes through materialId unchanged for non-swapping cells +- worldBlur: outputs s0.x (material ID) unchanged +- worldCompute (world.frag): preserves materialId from input, only changes + it for deposits (depositMatId > 0) or cell clears (discreteAnts.z == 1) +- draw.frag: preserves materialId unless actively painting (drawMode >= 0) +- vite HMR: shaders load as raw strings at construction, HMR doesn't + reconstruct ShaderMaterial. confirmed shader changes take effect after + full dev server restart +- copyFramebufferToTexture: disabled it, corruption still happened at + frame 10. the copy was suspected because it writes to worldRT.texture + while that texture may still be bound from DrawScene's tWorld uniform. + BUT disabling it did not fix the issue. (this call still needs a proper + fix — either use a render pass copy or unbind the texture first) + + +what we have NOT tested +----------------------- +- whether the corruption happens without renderToScreen at all (skip it + entirely — won't see anything on screen but could log worldRT state) +- whether ColonyStats.update (readRenderTargetPixels on antTarget) has + side effects that corrupt worldRT. it changes the bound render target. +- whether the draw scene render itself corrupts worldRT via some WebGL + state leak (even though it writes to worldRenderTargetCopy, maybe + binding worldRT.texture as tWorld uniform causes a side effect) +- a minimal repro: disable ALL passes except sandPhysics + worldBlur + + worldCompute and see if corruption still happens (isolate whether + ant passes or screen passes introduce it) +- reading worldRT between renderToScreen and the next renderSimulation + (requires readback in the rAF callback, after renderToScreen returns) +- whether THREE.js 0.173 has known issues with Float32 render targets + and copyFramebufferToTexture / readRenderTargetPixels + + +render pipeline (per frame) +--------------------------- +in renderSimulation(): + 1. sandPhysics: reads worldRT -> writes sandPhysicsRT + 2. worldBlur: reads sandPhysicsRT -> writes worldBlurredRT + 3. antsPresence: cleared + 4. antsCompute: reads worldBlurredRT + antsPresenceRT -> writes antsComputeTarget (MRT) + 5. antsDiscretize: reads antsComputeTarget -> writes antsDiscreteRT (NOT cleared per frame!) + 6. worldCompute: reads worldBlurredRT + antsDiscreteRT -> writes worldRT + 7. ColonyStats.update: readRenderTargetPixels on antsComputeTarget (CPU readback) + +in renderToScreen(): + 8. drawScene: reads worldRT (tWorld) -> writes worldRenderTargetCopy + 9. copyFramebufferToTexture: copies worldRenderTargetCopy framebuffer -> worldRT.texture + (CURRENTLY DISABLED in local changes) + 10. screenScene: reads worldRenderTargetCopy (map) -> writes to canvas (null target) + +worldRT is the persistent world state. it feeds back into itself: +worldRT -> sandPhysics -> worldBlur -> worldCompute -> worldRT + + +current local changes (uncommitted) +------------------------------------ +1. src/shaders/screenWorld.frag: + - air background color changed from white vec3(1,1,1) to light blue vec3(0.53, 0.81, 0.92) + - this change works correctly IF materialId is 0 for air cells + - also: the committed version has stale debug grayscale code from a previous session + that needs to be cleaned up + +2. src/Renderer.ts: + - copyFramebufferToTexture disabled (replaced with comment) + - minor whitespace change + - both should be reverted when the real fix is found + +3. src/scenes/ScreenScene.ts: + - groundDefines extracted to variable (cosmetic, from debug session) + +4. src/materials/registry.ts: + - air color changed from [0,0,0,0] to [0.53,0.81,0.92,1.0] + - this was an early attempt that doesn't matter since the shader + hardcodes the air branch (doesn't use the lookup for air) + + +key file locations +------------------ +- world init: src/WorldInit.ts (generateSideViewWorld) +- render pipeline: src/Renderer.ts (renderSimulation + renderToScreen) +- world texture format: RGBA Float32, R=materialId, G=scentToHome, B=scentToFood, A=repellent +- screen shader: src/shaders/screenWorld.frag +- world compute: src/shaders/world.frag +- sand physics: src/shaders/sandPhysics.frag +- pheromone blur: src/shaders/worldBlur.frag +- draw shader: src/shaders/draw.frag +- material constants: src/constants.ts (MAT_AIR=0 through MAT_HOME=5) +- material registry: src/materials/registry.ts +- color lookup texture gen: src/materials/lookupTexture.ts + + +suggested next steps +-------------------- +1. try disabling renderToScreen entirely and adding a single + console.debug readback of worldRT after 20 frames. if worldRT stays + [0,0,0,0] for air, the corruption is caused by renderToScreen or + something it triggers. + +2. if step 1 still shows corruption, try disabling the ant passes + (antsCompute, antsDiscretize, antsPresence clear) and see if the + corruption disappears. the world pipeline without ants is just + sandPhysics -> worldBlur -> worldCompute, which should be a no-op + for air cells. + +3. if step 2 still shows corruption, the issue might be in sandPhysics + (Margolus block CA) or worldCompute. try disabling each individually. + +4. check THREE.js 0.173 changelog/issues for Float32 render target bugs. + +5. consider adding a "sanitizer" pass that clamps materialId to [0,5] + at the start of each frame as a workaround while debugging.