Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
f6c90a2
Plans and initial experiments
Jarbuckle Jun 3, 2026
fdfcd85
Forgot one!
Jarbuckle Jun 3, 2026
191a41e
Merge branch 'main' into joel/DT-9593-webgpu-rendering
Jarbuckle Jun 4, 2026
d5f2bdb
Work-in-progress and Agent plan
Jarbuckle Jun 4, 2026
66860e5
Type-safe WGSL native types; binding graph & resource progress
Jarbuckle Jun 11, 2026
0424247
Removed the 1:1 assumption about abstract resources vs. bound resources
Jarbuckle Jun 24, 2026
2edc925
Added base Buffer Manager and BatchPool implementation
Jarbuckle Jun 25, 2026
0cb85de
Initial run of updates
Jarbuckle Jun 26, 2026
778aa43
Simplified binding graph creation
Jarbuckle Jun 26, 2026
af23f8d
Phases 3a - 3c of implementation
Jarbuckle Jun 29, 2026
a596a13
Inter-phase changes: make group() easier to use
Jarbuckle Jun 29, 2026
03fd077
Clean up some patterns
Jarbuckle Jun 30, 2026
fecd599
Moved pipeline management into a rendering context class for healthie…
Jarbuckle Jun 30, 2026
0dc24f3
Updated plan and config; implemented buffer management for resources
Jarbuckle Jun 30, 2026
0cb949d
Fixed various typecheck issues
Jarbuckle Jun 30, 2026
a23d6be
First full implementation of drawables
Jarbuckle Jun 30, 2026
f746135
Removal of old files
Jarbuckle Jun 30, 2026
abeae95
Implemented full original plan; includes WebGPU encoding and Scene st…
Jarbuckle Jul 1, 2026
c646fc5
Added a sweep capability to clean up unused/invalid bind groups intel…
Jarbuckle Jul 1, 2026
f3c90f7
Vertex input interface
Jarbuckle Jul 2, 2026
b40c002
Working example page in Vis doc site
Jarbuckle Jul 3, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ coverage

# generated types
.astro/
*.tsbuildinfo

# logs
npm-debug.log*
Expand Down
9 changes: 6 additions & 3 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
"dist"
],
"scripts": {
"typecheck": "tsc --noEmit",
"typecheck": "tsc --build",
"build": "parcel build --no-cache",
"dev": "parcel watch --port 1235",
"test": "vitest --watch",
Expand All @@ -53,12 +53,15 @@
"access": "public"
},
"devDependencies": {
"@types/lodash": "4.17.24"
"@types/lodash": "4.17.24",
"@webgpu/types": "0.1.70"
},
"dependencies": {
"@alleninstitute/vis-geometry": "workspace:*",
"lodash": "4.18.1",
"regl": "2.1.1",
"uuid": "14.0.0"
"uuid": "14.0.0",
"webgpu-utils": "^2.1.1",
"zod": "4.3.6"
}
}
30 changes: 13 additions & 17 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,21 @@
export { beginLongRunningFrame } from './render-queue';
export { AsyncDataCache } from './dataset-cache';
export { ReglLayer2D } from './layers/layer-2D';
export * from './layers/buffer-pair';
export * from './resources';
export * from './errors';
export * from './colors';

export { beginFrame, buildAsyncRenderer, type RenderFrameConfig } from './abstract/async-frame';
export type { CachedTexture, CachedVertexBuffer, ReglCacheEntry, Renderer } from './abstract/types';
export { RenderServer } from './abstract/render-server';

export type { CachedTexture, CachedVertexBuffer, ReglCacheEntry, Renderer } from './abstract/types';
export * from './colors';
export { AsyncDataCache } from './dataset-cache';
export * from './errors';
export * from './layers/buffer-pair';
export { ReglLayer2D } from './layers/layer-2D';
export { Logger, logger } from './logger';
export { PriorityCache, AsyncPriorityCache, type Cacheable } from './shared-priority-cache/priority-cache';
export { beginLongRunningFrame } from './render-queue';
export * from './resources';
export { AsyncPriorityCache, type Cacheable, PriorityCache } from './shared-priority-cache/priority-cache';
export { SharedPriorityCache } from './shared-priority-cache/shared-cache';

export {
type WorkerMessage,
type WorkerMessageWithId,
HEARTBEAT_RATE_MS,
isWorkerMessage,
isWorkerMessageWithId,
HEARTBEAT_RATE_MS,
type WorkerMessage,
type WorkerMessageWithId,
} from './workers/messages';

export { WorkerPool, type WorkerInit } from './workers/worker-pool';
export { type WorkerInit, WorkerPool } from './workers/worker-pool';
173 changes: 173 additions & 0 deletions packages/core/src/rendering/webgpu/context-types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
/**
* Type-only companion to `context.ts`.
*
* Houses the public `RenderingContext` **interface** (plus its spec / stats / resource-init
* shapes) so that leaf modules like `drawable.ts` and `encoder/encoder.ts` can depend on the
* context's shape without importing the concrete `RenderingContextImpl` class — which would
* form a runtime import cycle (`context.ts` already imports those modules for their builders).
*
* Every import here is `type`-only; this module contributes no runtime code and therefore
* cannot participate in a runtime cycle.
*/

import type {
BufferResource,
ExternalTextureResource,
Resource,
SamplerResource,
StorageTextureResource,
TextureResource,
} from './data/resource';
import type { Drawable, DrawableSpec } from './drawable';
import type { GraphEncoder } from './encoder/encoder';
import type { BufferManager } from './memory/types';
import type { BindingGraph } from './pipelines/binding-graph';
import type { BuiltPipeline } from './pipelines/build';
import type { PipelineStateDescriptor } from './pipelines/pipeline-state';
import type {
ExternalTextureSlot,
ResourceSlot,
SamplerSlot,
StorageSlot,
StorageTextureSlot,
TextureSlot,
UniformSlot,
} from './resources/resource';
import type { Scene } from './scene/types';
import type { WgslShader } from './shaders';

/**
* Spec passed to `renderingContext()`.
*
* - `device`: the `GPUDevice` every built pipeline targets.
* - `label`: optional debug label; surfaces in error messages (e.g. use-after-dispose).
* - `bufferManager`: optional, user-constructed `BufferManager`. Phase 3 accepts but does not
* consume it. Phase 4 wires `ctx.resource(slot, init?)` over this reference.
*/
export interface RenderingContextSpec {
readonly device: GPUDevice;
readonly label?: string;
readonly bufferManager?: BufferManager;
}

/**
* Telemetry snapshot returned by `ctx.stats()`. Extended per phase: Phase 4 surfaces a
* read-through `{bytes, leasedBytes}` view of the externally-supplied `BufferManager` (the
* memory fields are absent when no `bufferManager` was provided). Phase 7 adds bind-group
* cache fields. Shape is intentionally a single object so callers can spread or destructure
* without churn.
*/
export interface RenderingContextStats {
readonly pipelines: number;
/** Number of `GPUBindGroup`s currently cached on this context (Phase 7). */
readonly bindGroups: number;
/** Bytes currently resident in the bound `BufferManager` (leased + free). Absent when no
* `bufferManager` is attached to this context. */
readonly bytes?: number;
/** Bytes corresponding to leased (in-use) buffers in the bound `BufferManager`. Absent
* when no `bufferManager` is attached. */
readonly leasedBytes?: number;
}

/**
* Per-kind initializer accepted by `ctx.resource(slot, init?)`. Conditional over the slot
* variant so callers get a single overload that produces the right `Resource` subtype.
*/
export type ResourceInit<S extends ResourceSlot> = S extends UniformSlot
? Partial<Record<string, unknown>>
: S extends StorageSlot
? Partial<Record<string, unknown>>
: S extends TextureSlot
? { texture: GPUTexture; view?: GPUTextureView }
: S extends StorageTextureSlot
? { texture: GPUTexture; view?: GPUTextureView }
: S extends SamplerSlot
? GPUSampler | GPUSamplerDescriptor
: S extends ExternalTextureSlot
? GPUExternalTexture
: never;

/** Output `Resource` subtype for a given slot variant. */
export type ResourceFor<S extends ResourceSlot> = S extends UniformSlot
? BufferResource
: S extends StorageSlot
? BufferResource
: S extends TextureSlot
? TextureResource
: S extends StorageTextureSlot
? StorageTextureResource
: S extends SamplerSlot
? SamplerResource
: S extends ExternalTextureSlot
? ExternalTextureResource
: never;

/**
* Device-scoped facade for pipeline build + resource / drawable / encoder construction.
*
* This is the **public interface**; the concrete implementation is `RenderingContextImpl` in
* `context.ts`, constructed via the lowercase factory `renderingContext(spec)`. Leaf modules
* depend on this interface rather than the class to keep the module graph acyclic.
*/
export interface RenderingContext {
readonly device: GPUDevice;
readonly label?: string;
readonly bufferManager?: BufferManager;

/** Number of `BuiltPipeline`s currently cached on this instance. */
readonly pipelineCount: number;
/** `true` once `dispose()` has been called. Further `pipeline()` calls will throw. */
readonly disposed: boolean;

/**
* Build (or return a cached) `BuiltPipeline` for `(graph, shader, state)` against this
* context's device. Identical inputs return the same instance; differing state (after
* canonical normalization) produces a distinct entry.
*/
pipeline(
graph: BindingGraph,
shader: WgslShader,
state: PipelineStateDescriptor
): BuiltPipeline;

/**
* Construct a data-bearing `Resource` for `slot`. Dispatches on `slot.kind`; requires a
* `bufferManager` for buffer-backed slot kinds. The returned resource starts with
* `refcount === 1` — pair every call with exactly one `destroy()`.
*/
resource<S extends ResourceSlot>(slot: S, init?: ResourceInit<S>): ResourceFor<S>;

/**
* Construct a `Drawable` — a pipeline + vertex / index / binding resources + draw call —
* against this context. Pair every call with exactly one `drawable.destroy()`.
*/
drawable(spec: DrawableSpec): Drawable;

/** Construct (or return the cached) `GraphEncoder` bound to this context. */
encoder(): GraphEncoder;

/** Encode + submit `scene` to the device queue in one call. */
submit(scene: Scene): GPUCommandBuffer;

/** Drop every cached `BuiltPipeline` without disposing the context. */
disposePipelineCache(): void;

/** Drop every cached `GPUBindGroup` without disposing the context. */
disposeBindGroupCache(): void;

/**
* Selectively drop the cached `GPUBindGroup`s referencing any of `resources`, leaving other
* entries intact. Resources built via `ctx.resource()` trigger this automatically on
* `commit()` / `destroy()`. Returns the number of bind groups removed.
*/
sweepBindGroups(resources: readonly Resource[]): number;

/**
* Tear down everything this context owns. Idempotent. Does **not** dispose the
* externally-supplied `bufferManager` — that lifetime belongs to the caller.
*/
dispose(): void;

/** Snapshot of current cache occupancy. Cheap; suitable for HUDs / instrumentation. */
stats(): RenderingContextStats;
}
Loading