This directory contains a complete WebGPU-based rendering system built around modern architectural patterns including Entity-Component-System (ECS) scene management, modular mesh rendering, and efficient resource management.
The WebGPU system provides:
WebGPU System Architecture
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ Scene.ts │ │ ResourceManager │ │ BufferManager │
│ │ │ │ │ │
│ • ECS Scene │◄──►│ • GPU Resources │◄──►│ • Ring Buffers │
│ • Transforms │ │ • Pipeline Cache │ │ • Memory Alloc │
│ • Behaviors │ │ • Uniform Mgmt │ │ • Alignment │
└─────────────────┘ └──────────────────┘ └─────────────────┘
│ │ │
▼ ▼ ▼
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ Mesh System │ │ Render Utils │ │ Demos │
│ │ │ │ │ │
│ • CubeMesh │ │ • Render Bundles │ │ • WebGPUSketch │
│ • TriangleMesh │ │ • Pipeline Mgmt │ │ • Particle Sim │
│ • MeshRegistry │ │ • Batch Rendering│ │ • Examples │
└─────────────────┘ └──────────────────┘ └─────────────────┘
Scene.ts - ECS-style scene management with transforms, renderables, and behaviorsResourceManager.ts - Centralized GPU resource management and cachingBufferManager.ts - Ring buffer implementation for dynamic GPU memory allocationcamera.ts - 3D camera system with view/projection matricesrenderable/ - Unified renderable system with extensible types (meshes, particles, etc.)utils/ - Rendering utilities, math helpers, and shader managementdemos/ - Example applications and interactive demosgizmos/ - Debug visualization tools and 3D manipulatorsshaders/ - WGSL shader source filesui/ - User interface components and text renderinggeometry.ts - Geometric primitives and calculationscolor.ts - Color utilities and conversionsmsdfText.ts - Multi-channel signed distance field text renderingtypes.ts - TypeScript type definitionsThe scene system uses an Entity-Component-System pattern:
// Components are stored in separate maps
interface SceneState {
nodes: Map<NodeID, HierarchyComponent>; // Scene graph structure
transforms: Map<NodeID, TransformComponent>; // Position, rotation, scale
renderables: Map<NodeID, RenderableComponent>; // Mesh type, render bundles
behaviors: Map<NodeID, BehaviorComponent[]>; // Animation, instancing
}
// Systems operate on components
Scene.updateTransforms(sceneState); // Transform system
Scene.applyBehaviors(sceneState, dt); // Behavior system
The ResourceManager centralizes GPU resource lifecycle:
// Automatic pipeline caching
const pipeline = resourceManager.getOrCreateSharedPipeline(key, factory);
// Dynamic buffer allocation
const offset = resourceManager.allocRenderUniformBlock(transformData);
// Named buffer management
const buffer = resourceManager.getOrCreateBuffer(name, descriptor);
Ring buffers provide efficient dynamic allocation:
// Reset each frame
resourceManager.resetAllRingBuffers();
// Allocate with alignment
const { offset } = ringBuffer.allocData(data, alignment);
// Automatic wraparound when buffer fills
Modular mesh architecture with shared resources:
// Register mesh types
getRenderableRegistry().register("mesh", "cube", new CubeMesh());
// Create render bundles
const bundle = createMeshRenderBundle(
"cube", device, resourceManager, renderable, colorFormat, depthFormat
);
Render bundles are cached until geometry or shaders change:
if (renderable.needsRebundle || !renderable.bundle) {
renderable.bundle = createMeshRenderBundle(/* ... */);
renderable.needsRebundle = false;
}
Automatic uniform offset reorganization eliminates memory fragmentation:
// Called after scene modifications
Scene.reorganizeUniformOffsets(sceneState, resourceManager);
import * as Scene from "./scene/scene.ts";
import { ResourceManager } from "./ResourceManager.ts";
// Create scene state
const sceneState = Scene.createSceneState();
// Add objects
Scene.addNode(sceneState, parentId, "My Cube", {
renderable: { type: "cube" },
behavior: { type: "spinning", rotationSpeed: [1, 0, 0] }
}, resourceManager);
// Update loop
function render(deltaTime: number) {
Scene.applyBehaviors(sceneState, deltaTime);
Scene.updateTransforms(sceneState);
// ... render logic
}
import { getRenderableRegistry, MeshInterface } from "./renderable/index.ts";
class CustomMesh implements MeshInterface {
readonly meshType = "custom";
ensureResources(device: GPUDevice, resourceManager: ResourceManager) {
// Create vertex buffers, shaders
}
createRenderBundle(/* ... */): GPURenderBundle {
// Create optimized render bundle
}
}
// Register the mesh
getRenderableRegistry().register("mesh", "custom", new CustomMesh());
const stats = calculateRenderStats(renderables, getVertexCount, getTriangleCount);
console.log(`Rendering ${stats.totalTriangles} triangles in ${stats.totalBundles} bundles`);
// Monitor ring buffer usage
const usage = ringBuffer.head / ringBuffer.size;
if (usage > 0.8) console.warn("Ring buffer nearly full");
const cacheStats = renderBundleCache.getStats();
console.log(`Cache hit rate: ${(cacheStats.hitRate * 100).toFixed(1)}%`);
resourceManager.resetAllRingBuffers()destroy() methods when doneMeshRegistry// Runtime object creation
const nodeId = Scene.addNode(sceneState, parentId, name, {
renderable: { type: meshType },
transform: { position: [x, y, z] }
}, resourceManager);
// Reorganize buffers after modifications
Scene.reorganizeUniformOffsets(sceneState, resourceManager);
// Add spinning behavior
Scene.addBehavior(sceneState, nodeId, {
type: "spinning",
rotationSpeed: [0, Math.PI, 0], // Y-axis rotation
rotationEnabled: [false, true, false]
});
// Apply in render loop
Scene.applyBehaviors(sceneState, deltaTime);
// Custom render pass
const bundles: GPURenderBundle[] = [];
for (const [id, renderable] of sceneState.renderables.entries()) {
if (renderable.bundle) {
mainPass.setBindGroup(0, uniformBindGroup, [renderable.uniformOffset]);
mainPass.executeBundles([renderable.bundle]);
}
}
// OLD: Direct renderer calls
const bundle = getCubeRenderBundle(device, resourceManager, /* ... */);
// NEW: Mesh system
getRenderableRegistry().register("mesh", "cube", new CubeMesh());
const bundle = createMeshRenderBundle("cube", device, resourceManager, /* ... */);
// Extend existing interfaces
interface ExtendedRenderableComponent extends RenderableComponent {
customProperty: string;
}
// Create new component maps
const customComponents = new Map<NodeID, CustomComponent>();
See individual README files in subdirectories for detailed API documentation:
When adding new features:
Render bundles not updating: Check needsRebundle flag
Uniform offset errors: Call reorganizeUniformOffsets after scene changes
Pipeline creation fails: Verify shader compatibility and buffer layouts
Memory leaks: Ensure destroy() methods are called on cleanup
Low frame rate: Check triangle count and draw call batching Memory usage: Monitor ring buffer usage and reset frequency GPU stalls: Verify uniform buffer alignment and usage patterns