Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion apps/typegpu-docs/src/content/docs/apis/buffers.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -549,7 +549,7 @@ const Particle = d.struct({

const particleBuffer = root.createBuffer(Particle);
const backupParticleBuffer = root.createBuffer(Particle);
const encoder = root['~unstable'].createCommandEncoder();
const encoder = root.createCommandEncoder();
// ---cut---
particleBuffer.clear(encoder);
backupParticleBuffer.copyFrom(particleBuffer, encoder);
Expand Down
14 changes: 5 additions & 9 deletions apps/typegpu-docs/src/content/docs/apis/pipelines.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -619,10 +619,6 @@ The caller remains responsible for ending the pass or finishing the bundle.

## Command encoders and passes

:::caution[Experimental]
`createCommandEncoder` and typed passes are *unstable* features. The API may be subject to change in the near future.
:::

When a pipeline is executed directly via `draw` or `dispatchWorkgroups`, it records its own pass and submits it to the GPU queue immediately.
For scenarios that require more control, such as executing multiple pipelines in a single render pass or batching multiple passes into a single submission, TypeGPU provides a typed equivalent of the WebGPU command encoder.
It can be created with the `createCommandEncoder` method on the root object and mirrors `GPUCommandEncoder`, while accepting TypeGPU resources directly.
Expand Down Expand Up @@ -650,7 +646,7 @@ const skyPipeline = createPipeline();
const mesh = { vertexCount: 36 };
const lightCount = 4;
// ---cut---
const encoder = root['~unstable'].createCommandEncoder();
const encoder = root.createCommandEncoder();

const pass = encoder.beginRenderPass({
colorAttachments: [{
Expand Down Expand Up @@ -694,7 +690,7 @@ const bindGroupLayout = tgpu.bindGroupLayout({ size: { uniform: d.vec2u } });
const bindGroup = root.createBindGroup(bindGroupLayout, {
size: root.createBuffer(d.vec2u).$usage('uniform'),
});
const encoder = root['~unstable'].createCommandEncoder();
const encoder = root.createCommandEncoder();
const pass = encoder.beginRenderPass({ colorAttachments: [] });
// ---cut---
pass.setPipeline(renderPipeline);
Expand Down Expand Up @@ -722,7 +718,7 @@ const computePipeline = root.createComputePipeline({
compute: tgpu.computeFn({ workgroupSize: [1] })(() => {}),
});
// ---cut---
const encoder = root['~unstable'].createCommandEncoder();
const encoder = root.createCommandEncoder();
const pass = encoder.beginComputePass();
computePipeline.with(pass).dispatchWorkgroups(16);
pass.end();
Expand Down Expand Up @@ -798,7 +794,7 @@ pipeline
.draw(3);

// on the pass
const encoder = root['~unstable'].createCommandEncoder();
const encoder = root.createCommandEncoder();
const pass = encoder.beginRenderPass({ colorAttachments: [{ view: context }] });
pass.setImmediates(tint, d.vec4f(1, 0, 0, 1));
pipeline.with(pass).draw(3);
Expand All @@ -823,7 +819,7 @@ const root = await tgpu.init();
const mainVertex = tgpu.vertexFn({ out: { pos: d.builtin.position } })(() => ({ pos: d.vec4f() }));
const mainFragment = tgpu.fragmentFn({ out: d.vec4f })(() => d.vec4f());
const viewProjMatrix = d.mat4x4f();
const encoder = root['~unstable'].createCommandEncoder();
const encoder = root.createCommandEncoder();
const pass = encoder.beginRenderPass({ colorAttachments: [] });
// ---cut---
const viewProj = tgpu.accessor(d.mat4x4f);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -573,7 +573,7 @@ const runner = hrc.create({
});
declare const producer: TgpuComputePipeline;
// ---cut---
const encoder = root['~unstable'].createCommandEncoder();
const encoder = root.createCommandEncoder();
const pass = encoder.beginComputePass();
producer.with(pass).dispatchWorkgroups(64, 64);
runner.run({ pass });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -469,7 +469,7 @@ function frame(timestamp: number) {
outsideOpacityUniform.write(std.saturate(outsideOpacity.value));
fadeInUniform.write(std.saturate(controls.in3dTransition * 2 - 1));

const encoder = root['~unstable'].createCommandEncoder();
const encoder = root.createCommandEncoder();
const pass = encoder.beginRenderPass({
colorAttachments: targets.colorAttachments,
depthStencilAttachment: { view: targets.zBuffer, depthStoreOp: 'discard' },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -449,7 +449,7 @@ function frame() {
params.patch({ stepsPerDispatch: innerSteps });
const dispatchCount = Math.ceil(stepsToRun / innerSteps);

const simEncoder = root['~unstable'].createCommandEncoder();
const simEncoder = root.createCommandEncoder();
const simPass = simEncoder.beginComputePass();
const boundPipeline = simulatePipeline.with(simBindGroups[ga.current]).with(simPass);
for (let dispatch = 0; dispatch < dispatchCount; dispatch++) {
Expand All @@ -466,7 +466,7 @@ function frame() {
ga.precomputeFitness(population);
const bg = reductionBindGroups[ga.current];

const reductionEncoder = root['~unstable'].createCommandEncoder();
const reductionEncoder = root.createCommandEncoder();
reductionPackedBuffer.clear(reductionEncoder);
const reductionPass = reductionEncoder.beginComputePass();
reductionPipeline.with(bg).with(reductionPass).dispatchWorkgroups(workgroupCount(population));
Expand Down
8 changes: 4 additions & 4 deletions apps/typegpu-docs/src/examples/algorithms/sort/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ function render(encoder: TgpuCommandEncoder) {
function generateRandomArray() {
const workgroupsTotal = Math.ceil(state.arraySize / WORKGROUP_SIZE);
const [workgroupsX, workgroupsY, workgroupsZ] = decomposeWorkgroups(workgroupsTotal);
const encoder = root['~unstable'].createCommandEncoder();
const encoder = root.createCommandEncoder();

initSeed.write(Math.random());
initPipeline
Expand Down Expand Up @@ -256,7 +256,7 @@ async function sort() {
}
showOverlay('Sorting...');
const timestamps = querySet?.available ? querySet : null;
const encoder = root['~unstable'].createCommandEncoder();
const encoder = root.createCommandEncoder();

if (timestamps) {
recordTimed(encoder, sorter, timestamps);
Expand Down Expand Up @@ -284,7 +284,7 @@ async function benchmarkSorter(
): Promise<string> {
await plan.initAsync();
for (let i = 0; i < BENCH_WARMUP; i++) {
const encoder = root['~unstable'].createCommandEncoder();
const encoder = root.createCommandEncoder();
work.copyFrom(input, encoder);
plan.run({ encoder });
encoder.submit();
Expand All @@ -293,7 +293,7 @@ async function benchmarkSorter(

const times: number[] = [];
for (let i = 0; i < BENCH_RUNS; i++) {
const encoder = root['~unstable'].createCommandEncoder();
const encoder = root.createCommandEncoder();
work.copyFrom(input, encoder);
recordTimed(encoder, plan, timestamps);
encoder.submit();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ export class DepthRelightingRenderer {
this.#depthParams.patch({ reset: this.#firstFrame ? 1 : 0 });
}

const encoder = this.#root['~unstable'].createCommandEncoder();
const encoder = this.#root.createCommandEncoder();
const externalFrame = this.#root.device.importExternalTexture({ source: frame.source });
if (updateDepth) {
const pass = encoder.beginComputePass();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ function render(time: number) {
previousFrameTime = time;
paramsUniform.patch({ time: time * 0.001 });

const encoder = root['~unstable'].createCommandEncoder();
const encoder = root.createCommandEncoder();
const pass = encoder.beginRenderPass({
colorAttachments: [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ function updateLighting(encoder: TgpuCommandEncoder) {

let frameId = requestAnimationFrame(frame);
function frame() {
const encoder = root['~unstable'].createCommandEncoder();
const encoder = root.createCommandEncoder();

if (lightingDirty) {
updateLighting(encoder);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ let frameId = requestAnimationFrame(frame);
function frame(timestamp: number) {
interaction.update(timestamp);

const encoder = root['~unstable'].createCommandEncoder();
const encoder = root.createCommandEncoder();
if (sceneDirty) {
stamp(encoder);
lighting.run({ encoder });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ function frame() {
});
}

const encoder = root['~unstable'].createCommandEncoder();
const encoder = root.createCommandEncoder();

const pass = encoder.beginRenderPass({
colorAttachments: [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ function generateCubes(count: number) {
}

function buildBundle(): GPURenderBundle {
const bundleEncoder = root['~unstable'].createRenderBundleEncoder({
const bundleEncoder = root.createRenderBundleEncoder({
colorFormats: [presentationFormat],
depthStencilFormat: 'depth24plus',
});
Expand Down Expand Up @@ -158,7 +158,7 @@ function frame() {
});
}

const encoder = root['~unstable'].createCommandEncoder();
const encoder = root.createCommandEncoder();
const pass = encoder.beginRenderPass({
colorAttachments: [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ let frameId: number | null = null;
function render() {
frameId = requestAnimationFrame(render);

const encoder = root['~unstable'].createCommandEncoder();
const encoder = root.createCommandEncoder();

const shadowPass = encoder.beginRenderPass({
depthStencilAttachment: { view: shadowTextures.shadowMap },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ const splitComparison = createSplitComparison(canvas, 'Triplanar Mapping', 'Mesh
let frameId: number;
function frame() {
const splitX = Math.round(canvas.width * splitRatio);
const encoder = root['~unstable'].createCommandEncoder();
const encoder = root.createCommandEncoder();
const pass = encoder.beginRenderPass({
colorAttachments: [{ view: context }],
depthStencilAttachment: { view: depthTexture },
Expand Down
2 changes: 1 addition & 1 deletion packages/typegpu-algorithm/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ to record the work into instead of submitting on its own. Both raw WebGPU and
TypeGPU encoders and passes are accepted:

```ts
const encoder = root['~unstable'].createCommandEncoder();
const encoder = root.createCommandEncoder();

sorter.run({ encoder }); // records, does not submit
// ... encode more work ...
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -421,7 +421,7 @@ export function create(options: Options): Executor {
sectors,
byteSize,
run({ encoder, pass, iterations = 1 } = {}) {
const commands = encoder ?? (pass ? undefined : root['~unstable'].createCommandEncoder());
const commands = encoder ?? (pass ? undefined : root.createCommandEncoder());
const computePass = (pass ?? commands!.beginComputePass()) as TgpuComputePass;

for (let iteration = 0; iteration < iterations; iteration++) {
Expand Down
2 changes: 1 addition & 1 deletion packages/typegpu-radiance-cascades/src/runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ export function create(options: CascadesOptions): RadianceCascadesExecutor<Outpu
}

function run(commandEncoder?: TgpuCommandEncoder) {
const encoder = commandEncoder ?? root['~unstable'].createCommandEncoder();
const encoder = commandEncoder ?? root.createCommandEncoder();
const pass = encoder.beginComputePass();

for (const { pipeline, workgroups } of prebuiltCascadePasses) {
Expand Down
2 changes: 1 addition & 1 deletion packages/typegpu/src/core/commandEncoder/commandEncoder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export interface CommandEncoderInternals {
*
* @example
* ```ts
* const encoder = root['~unstable'].createCommandEncoder();
* const encoder = root.createCommandEncoder();
* const pass = encoder.beginRenderPass({
* colorAttachments: [{ view: msaaTexture, resolveTarget: context }],
* });
Expand Down
2 changes: 1 addition & 1 deletion packages/typegpu/src/core/pipeline/drawState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ function warnAboutUnreachableSubmission(core: object, what: string): void {
'suspicious',
core,
what,
`${what} is ignored when recording into a raw GPUCommandEncoder, since there is no submission to report after. Use root['~unstable'].createCommandEncoder() instead.`,
`${what} is ignored when recording into a raw GPUCommandEncoder, since there is no submission to report after. Use root.createCommandEncoder() instead.`,
);
}

Expand Down
78 changes: 42 additions & 36 deletions packages/typegpu/src/core/root/rootTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -660,6 +660,46 @@ export interface TgpuRoot extends Unwrapper, WithBinding {

createComparisonSampler(props: WgslComparisonSamplerProps): TgpuFixedComparisonSampler;

/**
* Creates a {@link TgpuCommandEncoder} for batching multiple render/compute
* passes (and draws within them) into a single submission.
*
* @example
* ```ts
* const encoder = root.createCommandEncoder();
* const pass = encoder.beginRenderPass({
* colorAttachments: [{ view: msaaTexture, resolveTarget: context }],
* });
* scenePipeline.with(pass).draw(vertexCount);
* skyPipeline.with(pass).draw(3);
* pass.end();
* encoder.submit();
* ```
*/
createCommandEncoder(descriptor?: GPUCommandEncoderDescriptor): TgpuCommandEncoder;

/**
* Creates a {@link TgpuRenderBundleEncoder} for recording draw commands into
* a {@link GPURenderBundle}. Call `finish()` on the encoder to obtain the
* bundle, replayable in a render pass via `pass.executeBundles`.
*
* The caller is responsible for ensuring that the `descriptor` (e.g.
* `colorFormats`, `depthStencilFormat`) is compatible with the render pass
* in which the bundle will be executed.
*
* @param descriptor - Describes the formats the bundle must be compatible with.
*
* @example
* ```ts
* const bundleEncoder = root.createRenderBundleEncoder({
* colorFormats: ['rgba8unorm'],
* });
* scenePipeline.with(bundleEncoder).draw(vertexCount);
* const bundle = bundleEncoder.finish();
* ```
*/
createRenderBundleEncoder(descriptor: GPURenderBundleEncoderDescriptor): TgpuRenderBundleEncoder;

/**
* Creates a query set for collecting timestamps or occlusion queries.
*
Expand Down Expand Up @@ -784,44 +824,10 @@ export interface ExperimentalTgpuRoot
CreateTextureResult<TSize, TFormat, TMipLevelCount, TSampleCount, TViewFormats, TDimension>
>;

/**
* Creates a {@link TgpuCommandEncoder} for batching multiple render/compute
* passes (and draws within them) into a single submission.
*
* @example
* ```ts
* const encoder = root['~unstable'].createCommandEncoder();
* const pass = encoder.beginRenderPass({
* colorAttachments: [{ view: msaaTexture, resolveTarget: context }],
* });
* scenePipeline.with(pass).draw(vertexCount);
* skyPipeline.with(pass).draw(3);
* pass.end();
* encoder.submit();
* ```
*/
/** @deprecated This feature is now stable, use `root.createCommandEncoder`. */

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/** @deprecated This feature is now stable, use `root.createCommandEncoder`. */
/** @deprecated This feature is now stable, use {@link TgpuRoot.createCommandEncoder | root.createCommandEncoder}. */

createCommandEncoder(descriptor?: GPUCommandEncoderDescriptor): TgpuCommandEncoder;

/**
* Creates a {@link TgpuRenderBundleEncoder} for recording draw commands into
* a {@link GPURenderBundle}. Call `finish()` on the encoder to obtain the
* bundle, replayable in a render pass via `pass.executeBundles`.
*
* The caller is responsible for ensuring that the `descriptor` (e.g.
* `colorFormats`, `depthStencilFormat`) is compatible with the render pass
* in which the bundle will be executed.
*
* @param descriptor - Describes the formats the bundle must be compatible with.
*
* @example
* ```ts
* const bundleEncoder = root['~unstable'].createRenderBundleEncoder({
* colorFormats: ['rgba8unorm'],
* });
* scenePipeline.with(bundleEncoder).draw(vertexCount);
* const bundle = bundleEncoder.finish();
* ```
*/
/** @deprecated This feature is now stable, use `root.createRenderBundleEncoder`. */

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as above

createRenderBundleEncoder(descriptor: GPURenderBundleEncoderDescriptor): TgpuRenderBundleEncoder;

/** @deprecated Use `root.createSampler` instead. */
Expand Down
Loading
Loading