Skip to content

Add performance, asset pipeline, WebGPU/TSL and R3F skills - #13

Draft
AgentEnder wants to merge 14 commits into
CloudAI-X:mainfrom
AgentEnder:feat/performance-webgpu-r3f-skills
Draft

AgentEnder wants to merge 14 commits into
CloudAI-X:mainfrom
AgentEnder:feat/performance-webgpu-r3f-skills

Conversation

@AgentEnder

Copy link
Copy Markdown

Summary

Adds four skills and expands nine existing ones, covering the performance/platform axis the current set documents only in passing. The existing skills are a strong API reference (what exists, correct signatures); this adds the budget and trade-off layer (what to reach for, what it costs).

New skills

Skill Covers
threejs-performance Draw call budgets, InstancedMesh vs BatchedMesh vs merging, GPU disposal and leak hunting, object pooling, profiling (renderer.info, stats-gl, Spector.js), three-mesh-bvh, context loss
threejs-asset-pipeline Draco vs Meshopt, KTX2/Basis (UASTC vs ETC1S), gltf-transform, decoder path setup, lazy loading, code splitting, Core Web Vitals
threejs-webgpu-tsl WebGPURenderer, TSL node materials, Fn, compute shaders, instancedArray, storage textures, workgroup memory, indirect draws, node post-processing
threejs-react-three-fiber useFrame mutation, frameloop="demand"/invalidate, re-render avoidance, Suspense, drei, disposal on unmount

Updated skills

threejs-lighting (light budget, PointLight's 6x shadow cost, CSM, shadowMap.autoUpdate, lightmaps, fake shadows) · threejs-postprocessing (pmndrs pass merging, renderer config, tone mapping placement) · threejs-shaders (mediump, varying budget, branchless, RGBA packing, program reuse) · threejs-geometry (BatchedMesh, batching strategy table) · threejs-materials (material sharing) · threejs-textures (KTX2 VRAM cost, array textures, ImageBitmap) · threejs-loaders (Meshopt, decoder checklist) · threejs-interaction (three-mesh-bvh) · threejs-fundamentals (setAnimationLoop, context loss)

Bug fixes included

Two existing snippets don't resolve against any three.js version:

  • threejs-lighting imported ContactShadows from three/examples/jsm/objects/ContactShadows.js. That file does not exist — ContactShadows is a drei component. Replaced with a vanilla gradient-plane recipe plus a pointer to drei.
  • threejs-postprocessing imported a lowercase postProcessing and effect nodes from three/addons/nodes/Nodes.js. PostProcessing comes from three/webgpu, pass() from three/tsl, and effect nodes from three/addons/tsl/display/.

Verification

New and changed APIs were checked against the three r185 source and export maps rather than from memory. Three things that differ from what's commonly published:

  • The TSL view-direction node is positionViewDirection; there is no viewDirection export.
  • bloom/fxaa/smaa/dof live in three/addons/tsl/display/*, not three/tsl, and compose via getTextureNode() rather than a .pipe() chain.
  • BatchedMesh changed after r156: addGeometry() returns a geometry id, addInstance() returns an instance id, and setMatrixAt takes the instance id. Code written against the older shape indexes the wrong thing silently.

Notes

  • 14 commits, one per skill area, for independent review or revert.
  • Files are left in the repo's existing (unformatted) style — running Prettier would rewrite all 14 skill files including untouched ones, so that's left as a separate call.
  • Draft: happy to adjust scope, split this up, or drop the new skills if you'd rather keep the set focused on the core API surface.

Covers the "make it fast, don't leak" axis that the existing skills
document only in passing.

- Draw call budget (<100/frame) as the governing constraint, with a
  strategy table for InstancedMesh vs BatchedMesh vs mergeGeometries
- Full GPU disposal sweep, including the ImageBitmap close() that GLTF
  textures need on top of dispose()
- Object pooling and texture caching to avoid GC pauses
- Profiling: renderer.info, stats-gl, lil-gui, Spector.js, DevTools
- three-mesh-bvh accelerated raycasting
- Context loss handling and setAnimationLoop
- A triage checklist ordered cheapest-fix-first

APIs verified against three r185.
Asset compression and loading strategy, previously uncovered.

- gltf-transform CLI as the primary tool, with per-operation recipes
- Draco vs Meshopt trade-off (compression ratio vs decode speed and
  decoder size)
- KTX2/Basis: why PNG/JPEG cost ~10x the VRAM, and when to pick UASTC
  (normal maps, hero textures) over ETC1S (diffuse, secondary)
- Decoder path setup checklist, the most common prod-only breakage
- Loading strategy for Core Web Vitals: code splitting, IntersectionObserver
  lazy loading, preload hints, progressive loading, streaming, workers
- Rough production budgets for GLB size, texture size and triangle count
No existing skill mentioned WebGPURenderer or TSL.

- WebGPURenderer setup, the mandatory await init(), automatic WebGL 2
  fallback, forceWebGL, browser support matrix and feature detection
- A "should you migrate?" table, since WebGPU is not universally faster
- TSL node materials, the Fn pattern for reusable shader logic, and the
  built-in MaterialX noise library
- Compute shaders: instancedArray GPU-persistent buffers, GPU particles,
  physics, storage textures, workgroup shared memory, indirect draws
- Node-based post-processing

All exports verified against the three r185 export maps. Note that the
view direction node is positionViewDirection; there is no viewDirection
export, and effect nodes live in three/addons/tsl/display/ rather than
three/tsl.
R3F had no coverage at all despite being how many projects consume
three.js.

- The core rule: React's render cycle and the animation loop are
  different clocks; mutate through refs rather than setState in useFrame
- Never allocate inside useFrame, and drive motion with delta
- On-demand rendering via frameloop="demand" plus invalidate(), including
  the gotcha where scene changes silently do not appear
- Re-render avoidance: useThree selectors, React.memo, visibility toggling
  instead of remounting
- Suspense, useGLTF.preload, drei Detailed for LOD
- Disposal on unmount, since R3F only auto-disposes what it created
- Adaptive quality and the dpr clamp, the highest-leverage mobile setting

Closes with a common-mistakes list.
The skill documented the light API thoroughly but gave little guidance on
what each option costs.

- The light budget: 3 or fewer active lights, since each is compiled into
  every lit material's shader
- Shadow map renders per light type, calling out that PointLight costs 6
  (one per cube face) so two shadowed PointLights over 10 casters is 120
  extra draw calls
- Shadow map sizing table; memory is quadratic in resolution
- renderer.shadowMap.autoUpdate = false for static casters, a large and
  cheap win for product viewers and walkthroughs
- Cascaded Shadow Maps, including the camera/parent options and the
  setupMaterial + update calls the addon requires
- Baked lightmaps and cheap fake contact shadows

Also fixes a broken import: ContactShadows was imported from
three/examples/jsm/objects/ContactShadows.js, which does not exist in
three.js. It is a drei component. Replaced with a vanilla gradient-plane
recipe and a pointer to drei for R3F users.
- Recommends pmndrs/postprocessing for WebGL, which merges compatible
  effects into a single fullscreen pass where EffectComposer runs one pass
  per effect
- Renderer configuration once a composer is present: antialias/stencil/
  depth off, tone mapping deferred to the end of the pipeline rather than
  applied early to already-uncompressed values
- Selective bloom via SelectiveBloomEffect instead of layer swapping
- Expanded performance section: pass merging, multisampling: 0, half-res
  effect rendering, AA last, bloom parameter ranges, render target disposal

Also fixes the WebGPU section, which imported a lowercase `postProcessing`
and effect nodes from three/addons/nodes/Nodes.js. Neither resolves.
PostProcessing comes from three/webgpu, pass() from three/tsl, and effect
nodes from three/addons/tsl/display/. The pipeline composes via
getTextureNode(), not a .pipe() chain.
Replaces a five-line performance list with guidance on what actually costs
time in a fragment shader.

- mediump on mobile, roughly 2x faster than highp, with a note on which
  values still need precision
- Branchless mix()/step(), and when a branch is still worth keeping
- Varying budget: under 3 on mobile, with a packing example
- RGBA channel packing to cut texture fetches, plus the NoColorSpace
  requirement for data textures
- Constant loop bounds with early break instead of dynamic bounds
- Shader program reuse and watching renderer.info.programs.length
- A pointer to TSL for anyone targeting WebGPU, since raw GLSL/WGSL means
  maintaining two shader codebases
- Adds a BatchedMesh section: many distinct geometries sharing one
  material in a single draw call, the variation InstancedMesh cannot give
- Documents the two-step id model (addGeometry returns a geometry id,
  addInstance returns an instance id, and setMatrixAt takes the instance
  id), which changed after the r156 API commonly cited in blog posts
- Adds the <100 draw calls per frame budget and a table for choosing
  between InstancedMesh, BatchedMesh, mergeGeometries and LOD, including
  what merging costs you in culling and raycast granularity
- Notes that frustum culling is only correct if bounding volumes are
Material sharing was one line in a list; it is the highest-leverage
material-level optimization there is.

- Shows the bad/good pair: a unique material per mesh defeats batching and
  multiplies compiled shader programs, each compile being a main thread
  stall
- Explains when three.js reuses a program, and why gratuitous variation
  (differing defines, a map present on one material and not another)
  proliferates them
- Points at per-instance data as the right way to get variation
- Adds a warning against creating materials inside a render callback
- Expands the compressed texture section to explain why it matters: PNG
  and JPEG are file compression and the GPU stores them decompressed, so a
  200KB PNG occupies 20MB+ of VRAM while KTX2/Basis stays compressed on
  the GPU for roughly 10x less
- Documents the UASTC vs ETC1S choice and makes detectSupport(renderer)
  explicitly mandatory
- Adds a DataArrayTexture section: indexed layers give visual variety with
  a single texture bind, pairing naturally with BatchedMesh
- Adds the ImageBitmap close() step, since GLTFLoader decodes textures
  into ImageBitmaps that survive texture.dispose() and leak
- Adds Meshopt as the Draco alternative: comparable compression once
  gzipped, faster decode, ~30KB decoder against Draco's ~200KB WASM
- Adds a decoder setup checklist, since misconfigured paths are the most
  common works-locally-breaks-in-production failure. Covers copying the
  decoder directories as a build step, version-pinning any CDN URL, the
  mandatory detectSupport call, preload() to keep WASM off the critical
  path, and loader disposal
- Replaces the unpinned CDN transcoder path with a local path
- Notes that removing a model from the scene does not free VRAM
Default raycasting tests every triangle, which is unusable at interactive
rates on detailed models.

- Adds the three-mesh-bvh prototype patch, per-geometry computeBoundsTree,
  the firstHitOnly flag that lets the BVH prune aggressively, and
  disposeBoundsTree for cleanup
- Notes when it is not worth it (a handful of simple primitives, where the
  BVH build cost exceeds the saving)
- Points R3F users at drei's <Bvh> wrapper
- Switches the Quick Start and Clock examples from requestAnimationFrame
  to renderer.setAnimationLoop, which handles WebXR sessions (where rAF
  does not work) and gives clean start/stop
- Adds context loss handling; unhandled, a lost context leaves the canvas
  permanently black, and preventDefault() is required for restoration
- Leads the performance section with measurement via renderer.info and the
  <100 draw call target, and adds the setPixelRatio clamp
- Notes that three.js never garbage-collects GPU resources, with a pointer
  to the full disposal sweep
- Adds a Performance & Platform table covering threejs-performance,
  threejs-asset-pipeline, threejs-webgpu-tsl and threejs-react-three-fiber
- Extends the auto-loading examples with the request shapes that trigger
  them
- Notes that the new skills were verified against the three r185 source
  and export maps
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant