0
# Pipelines and Shaders
1
2
Render pipelines, compute pipelines, and shader modules for defining GPU programs and rendering state.
3
4
## Capabilities
5
6
### GPU Shader Module
7
8
Compiled shader code that can be used in pipelines.
9
10
```typescript { .api }
11
interface GPUShaderModule {
12
/** Optional debug label */
13
readonly label: string | undefined;
14
15
/**
16
* Gets compilation information including errors and warnings
17
* @returns Promise resolving to compilation info
18
*/
19
getCompilationInfo(): Promise<GPUCompilationInfo>;
20
}
21
22
interface GPUShaderModuleDescriptor extends GPUObjectDescriptorBase {
23
/** WGSL shader code */
24
code: string;
25
26
/** Optional compilation hints */
27
hints?: Record<string, GPUShaderModuleCompilationHint>;
28
}
29
30
interface GPUShaderModuleCompilationHint {
31
/** Entry point name */
32
entryPoint?: string;
33
34
/** Pipeline layout for optimization */
35
layout?: GPUPipelineLayout | "auto";
36
}
37
38
interface GPUCompilationInfo {
39
/** Array of compilation messages */
40
readonly messages: ReadonlyArray<GPUCompilationMessage>;
41
}
42
43
interface GPUCompilationMessage {
44
/** Message text */
45
readonly message: string;
46
47
/** Message type */
48
readonly type: GPUCompilationMessageType;
49
50
/** Line number (1-based) */
51
readonly lineNum: number;
52
53
/** Line position (1-based) */
54
readonly linePos: number;
55
56
/** Byte offset in source */
57
readonly offset: number;
58
59
/** Length of the relevant source region */
60
readonly length: number;
61
}
62
63
type GPUCompilationMessageType = "error" | "warning" | "info";
64
```
65
66
### GPU Pipeline Layout
67
68
Defines the resource binding layout for a pipeline.
69
70
```typescript { .api }
71
interface GPUPipelineLayout {
72
/** Optional debug label */
73
readonly label: string | undefined;
74
}
75
76
interface GPUPipelineLayoutDescriptor extends GPUObjectDescriptorBase {
77
/** Array of bind group layouts */
78
bindGroupLayouts: Iterable<GPUBindGroupLayout>;
79
}
80
```
81
82
### GPU Render Pipeline
83
84
Graphics pipeline for vertex processing and fragment shading.
85
86
```typescript { .api }
87
interface GPURenderPipeline {
88
/** Optional debug label */
89
readonly label: string | undefined;
90
91
/**
92
* Gets bind group layout at the specified index
93
* @param index - Bind group index
94
* @returns The bind group layout
95
*/
96
getBindGroupLayout(index: number): GPUBindGroupLayout;
97
}
98
99
interface GPURenderPipelineDescriptor extends GPUPipelineDescriptorBase {
100
/** Vertex stage configuration */
101
vertex: GPUVertexState;
102
103
/** Primitive assembly configuration */
104
primitive?: GPUPrimitiveState;
105
106
/** Depth/stencil state */
107
depthStencil?: GPUDepthStencilState;
108
109
/** Multisample state */
110
multisample?: GPUMultisampleState;
111
112
/** Fragment stage configuration */
113
fragment?: GPUFragmentState;
114
}
115
116
interface GPUPipelineDescriptorBase extends GPUObjectDescriptorBase {
117
/** Pipeline layout */
118
layout: GPUPipelineLayout | "auto";
119
}
120
121
interface GPUVertexState extends GPUProgrammableStage {
122
/** Vertex buffer layouts */
123
buffers?: Iterable<GPUVertexBufferLayout | null>;
124
}
125
126
interface GPUProgrammableStage {
127
/** Shader module */
128
module: GPUShaderModule;
129
130
/** Entry point function name */
131
entryPoint?: string;
132
133
/** Pipeline constants */
134
constants?: Record<string, GPUPipelineConstantValue>;
135
}
136
137
interface GPUVertexBufferLayout {
138
/** Stride between vertices in bytes */
139
arrayStride: GPUSize64;
140
141
/** Step mode for vertex fetching */
142
stepMode?: GPUVertexStepMode;
143
144
/** Vertex attributes */
145
attributes: Iterable<GPUVertexAttribute>;
146
}
147
148
interface GPUVertexAttribute {
149
/** Vertex format */
150
format: GPUVertexFormat;
151
152
/** Byte offset within vertex */
153
offset: GPUSize64;
154
155
/** Shader location binding */
156
shaderLocation: GPUIndex32;
157
}
158
159
interface GPUPrimitiveState {
160
/** Primitive topology */
161
topology?: GPUPrimitiveTopology;
162
163
/** Strip index format */
164
stripIndexFormat?: GPUIndexFormat;
165
166
/** Front face winding */
167
frontFace?: GPUFrontFace;
168
169
/** Face culling mode */
170
cullMode?: GPUCullMode;
171
172
/** Whether to enable conservative rasterization */
173
unclippedDepth?: boolean;
174
}
175
176
interface GPUMultisampleState {
177
/** Number of samples */
178
count?: GPUSize32;
179
180
/** Sample mask */
181
mask?: GPUSampleMask;
182
183
/** Alpha to coverage */
184
alphaToCoverageEnabled?: boolean;
185
}
186
187
interface GPUFragmentState extends GPUProgrammableStage {
188
/** Color target states */
189
targets: Iterable<GPUColorTargetState | null>;
190
}
191
192
interface GPUColorTargetState {
193
/** Color format */
194
format: GPUTextureFormat;
195
196
/** Blend state */
197
blend?: GPUBlendState;
198
199
/** Write mask */
200
writeMask?: GPUColorWriteFlags;
201
}
202
203
interface GPUBlendState {
204
/** Color blend component */
205
color: GPUBlendComponent;
206
207
/** Alpha blend component */
208
alpha: GPUBlendComponent;
209
}
210
211
interface GPUBlendComponent {
212
/** Blend operation */
213
operation?: GPUBlendOperation;
214
215
/** Source factor */
216
srcFactor?: GPUBlendFactor;
217
218
/** Destination factor */
219
dstFactor?: GPUBlendFactor;
220
}
221
222
interface GPUDepthStencilState {
223
/** Depth/stencil format */
224
format: GPUTextureFormat;
225
226
/** Whether depth writes are enabled */
227
depthWriteEnabled?: boolean;
228
229
/** Depth comparison function */
230
depthCompare?: GPUCompareFunction;
231
232
/** Stencil front face state */
233
stencilFront?: GPUStencilFaceState;
234
235
/** Stencil back face state */
236
stencilBack?: GPUStencilFaceState;
237
238
/** Stencil read mask */
239
stencilReadMask?: GPUStencilValue;
240
241
/** Stencil write mask */
242
stencilWriteMask?: GPUStencilValue;
243
244
/** Depth bias constant */
245
depthBias?: GPUDepthBias;
246
247
/** Depth bias slope scale */
248
depthBiasSlopeScale?: number;
249
250
/** Depth bias clamp */
251
depthBiasClamp?: number;
252
}
253
254
interface GPUStencilFaceState {
255
/** Comparison function */
256
compare?: GPUCompareFunction;
257
258
/** Fail operation */
259
failOp?: GPUStencilOperation;
260
261
/** Depth fail operation */
262
depthFailOp?: GPUStencilOperation;
263
264
/** Pass operation */
265
passOp?: GPUStencilOperation;
266
}
267
268
type GPUStencilOperation =
269
| "keep" | "zero" | "replace" | "invert"
270
| "increment-clamp" | "decrement-clamp"
271
| "increment-wrap" | "decrement-wrap";
272
```
273
274
### GPU Compute Pipeline
275
276
Compute shader pipeline for general-purpose GPU computing.
277
278
```typescript { .api }
279
interface GPUComputePipeline {
280
/** Optional debug label */
281
readonly label: string | undefined;
282
283
/**
284
* Gets bind group layout at the specified index
285
* @param index - Bind group index
286
* @returns The bind group layout
287
*/
288
getBindGroupLayout(index: number): GPUBindGroupLayout;
289
}
290
291
interface GPUComputePipelineDescriptor extends GPUPipelineDescriptorBase {
292
/** Compute stage configuration */
293
compute: GPUProgrammableStage;
294
}
295
```
296
297
### Pipeline Base Interface
298
299
Common functionality shared by all pipeline types.
300
301
```typescript { .api }
302
interface GPUPipelineBase {
303
/** Optional debug label */
304
readonly label: string | undefined;
305
306
/**
307
* Gets bind group layout at the specified index
308
* @param index - Bind group index
309
* @returns The bind group layout
310
*/
311
getBindGroupLayout(index: number): GPUBindGroupLayout;
312
}
313
```
314
315
## Usage Examples
316
317
### Shader Module Creation
318
319
```typescript
320
// Create a vertex shader
321
const vertexShader = device.createShaderModule({
322
code: `
323
struct VertexInput {
324
@location(0) position: vec3<f32>,
325
@location(1) normal: vec3<f32>,
326
@location(2) uv: vec2<f32>,
327
}
328
329
struct VertexOutput {
330
@builtin(position) position: vec4<f32>,
331
@location(0) normal: vec3<f32>,
332
@location(1) uv: vec2<f32>,
333
}
334
335
@group(0) @binding(0) var<uniform> mvpMatrix: mat4x4<f32>;
336
337
@vertex
338
fn main(input: VertexInput) -> VertexOutput {
339
var output: VertexOutput;
340
output.position = mvpMatrix * vec4<f32>(input.position, 1.0);
341
output.normal = input.normal;
342
output.uv = input.uv;
343
return output;
344
}
345
`,
346
label: "Vertex Shader"
347
});
348
349
// Create a fragment shader
350
const fragmentShader = device.createShaderModule({
351
code: `
352
struct FragmentInput {
353
@location(0) normal: vec3<f32>,
354
@location(1) uv: vec2<f32>,
355
}
356
357
@group(1) @binding(0) var baseColorTexture: texture_2d<f32>;
358
@group(1) @binding(1) var baseColorSampler: sampler;
359
360
@fragment
361
fn main(input: FragmentInput) -> @location(0) vec4<f32> {
362
let baseColor = textureSample(baseColorTexture, baseColorSampler, input.uv);
363
let lighting = max(dot(normalize(input.normal), vec3<f32>(0.0, 1.0, 0.0)), 0.1);
364
return vec4<f32>(baseColor.rgb * lighting, baseColor.a);
365
}
366
`,
367
label: "Fragment Shader"
368
});
369
370
// Check compilation info
371
const vertexInfo = await vertexShader.getCompilationInfo();
372
for (const message of vertexInfo.messages) {
373
if (message.type === "error") {
374
console.error(`Shader error at line ${message.lineNum}: ${message.message}`);
375
}
376
}
377
```
378
379
### Render Pipeline Creation
380
381
```typescript
382
// Create pipeline layout
383
const pipelineLayout = device.createPipelineLayout({
384
bindGroupLayouts: [
385
uniformsBindGroupLayout,
386
materialBindGroupLayout
387
],
388
label: "Render Pipeline Layout"
389
});
390
391
// Create render pipeline
392
const renderPipeline = device.createRenderPipeline({
393
layout: pipelineLayout,
394
vertex: {
395
module: vertexShader,
396
entryPoint: "main",
397
buffers: [
398
{
399
arrayStride: 32, // 3 * 4 + 3 * 4 + 2 * 4 = position + normal + uv
400
attributes: [
401
{
402
format: "float32x3",
403
offset: 0,
404
shaderLocation: 0 // position
405
},
406
{
407
format: "float32x3",
408
offset: 12,
409
shaderLocation: 1 // normal
410
},
411
{
412
format: "float32x2",
413
offset: 24,
414
shaderLocation: 2 // uv
415
}
416
]
417
}
418
]
419
},
420
fragment: {
421
module: fragmentShader,
422
entryPoint: "main",
423
targets: [
424
{
425
format: "bgra8unorm",
426
blend: {
427
color: {
428
srcFactor: "src-alpha",
429
dstFactor: "one-minus-src-alpha",
430
operation: "add"
431
},
432
alpha: {
433
srcFactor: "one",
434
dstFactor: "one-minus-src-alpha",
435
operation: "add"
436
}
437
}
438
}
439
]
440
},
441
primitive: {
442
topology: "triangle-list",
443
cullMode: "back",
444
frontFace: "ccw"
445
},
446
depthStencil: {
447
format: "depth24plus-stencil8",
448
depthWriteEnabled: true,
449
depthCompare: "less"
450
},
451
multisample: {
452
count: 4,
453
alphaToCoverageEnabled: false
454
},
455
label: "Mesh Render Pipeline"
456
});
457
```
458
459
### Compute Pipeline Creation
460
461
```typescript
462
// Create compute shader
463
const computeShader = device.createShaderModule({
464
code: `
465
@group(0) @binding(0) var<storage, read> inputData: array<f32>;
466
@group(0) @binding(1) var<storage, read_write> outputData: array<f32>;
467
468
@compute @workgroup_size(64)
469
fn main(@builtin(global_invocation_id) globalId: vec3<u32>) {
470
let index = globalId.x;
471
if (index >= arrayLength(&inputData)) {
472
return;
473
}
474
475
outputData[index] = inputData[index] * 2.0;
476
}
477
`,
478
label: "Double Values Compute Shader"
479
});
480
481
// Create compute pipeline
482
const computePipeline = device.createComputePipeline({
483
layout: "auto",
484
compute: {
485
module: computeShader,
486
entryPoint: "main"
487
},
488
label: "Double Values Pipeline"
489
});
490
491
// Get the auto-generated bind group layout
492
const computeBindGroupLayout = computePipeline.getBindGroupLayout(0);
493
```
494
495
### Advanced Shader Features
496
497
```typescript
498
// Shader with constants
499
const advancedShader = device.createShaderModule({
500
code: `
501
override workgroupSize: u32 = 64;
502
override multiplier: f32 = 1.0;
503
504
@group(0) @binding(0) var<storage, read_write> data: array<f32>;
505
506
@compute @workgroup_size(workgroupSize, 1, 1)
507
fn main(@builtin(global_invocation_id) globalId: vec3<u32>) {
508
let index = globalId.x;
509
if (index >= arrayLength(&data)) {
510
return;
511
}
512
513
data[index] = data[index] * multiplier;
514
}
515
`,
516
label: "Parameterized Compute Shader"
517
});
518
519
// Create pipeline with constants
520
const parameterizedPipeline = device.createComputePipeline({
521
layout: "auto",
522
compute: {
523
module: advancedShader,
524
entryPoint: "main",
525
constants: {
526
workgroupSize: 128,
527
multiplier: 2.5
528
}
529
},
530
label: "Parameterized Pipeline"
531
});
532
```
533
534
### Pipeline with Multiple Render Targets
535
536
```typescript
537
const multiTargetPipeline = device.createRenderPipeline({
538
layout: pipelineLayout,
539
vertex: {
540
module: vertexShader,
541
entryPoint: "main",
542
buffers: [vertexBufferLayout]
543
},
544
fragment: {
545
module: multiTargetFragmentShader,
546
entryPoint: "main",
547
targets: [
548
{ format: "rgba8unorm" }, // Color target 0
549
{ format: "rgba16float" }, // Color target 1 (HDR)
550
{ format: "rg16float" }, // Color target 2 (normals)
551
null, // Skip target 3
552
{ format: "r8uint" } // Color target 4 (object ID)
553
]
554
},
555
primitive: {
556
topology: "triangle-list"
557
},
558
label: "G-Buffer Pipeline"
559
});
560
```
561
562
### Async Pipeline Creation
563
564
```typescript
565
// Create pipelines asynchronously for better performance
566
const [renderPipelineAsync, computePipelineAsync] = await Promise.all([
567
device.createRenderPipelineAsync({
568
layout: renderPipelineLayout,
569
vertex: { module: vertexShader, entryPoint: "main" },
570
fragment: {
571
module: fragmentShader,
572
entryPoint: "main",
573
targets: [{ format: "bgra8unorm" }]
574
},
575
label: "Async Render Pipeline"
576
}),
577
578
device.createComputePipelineAsync({
579
layout: "auto",
580
compute: { module: computeShader, entryPoint: "main" },
581
label: "Async Compute Pipeline"
582
})
583
]);
584
585
console.log("Both pipelines created asynchronously");
586
```
587
588
### Error Handling in Shaders
589
590
```typescript
591
// Create shader with potential compilation issues
592
const shaderWithError = device.createShaderModule({
593
code: `
594
@vertex
595
fn main() -> @builtin(position) vec4<f32> {
596
// Missing required vertex output
597
return vec4<f32>(0.0, 0.0, 0.0, 1.0);
598
}
599
`,
600
label: "Test Shader"
601
});
602
603
// Check for compilation errors
604
const compilationInfo = await shaderWithError.getCompilationInfo();
605
if (compilationInfo.messages.length > 0) {
606
console.log("Compilation messages:");
607
for (const message of compilationInfo.messages) {
608
const level = message.type.toUpperCase();
609
console.log(`${level} at line ${message.lineNum}:${message.linePos} - ${message.message}`);
610
}
611
}
612
613
// Handle pipeline creation errors
614
device.pushErrorScope("validation");
615
616
const pipeline = device.createRenderPipeline({
617
layout: "auto",
618
vertex: { module: shaderWithError, entryPoint: "main" },
619
fragment: {
620
module: fragmentShader,
621
entryPoint: "main",
622
targets: [{ format: "bgra8unorm" }]
623
}
624
});
625
626
const error = await device.popErrorScope();
627
if (error) {
628
console.error("Pipeline creation failed:", error.message);
629
}
630
```
631
632
### Pipeline Specialization
633
634
```typescript
635
// Create multiple specialized pipelines from the same shader
636
const basePipelineDesc: GPURenderPipelineDescriptor = {
637
layout: pipelineLayout,
638
vertex: {
639
module: vertexShader,
640
entryPoint: "main",
641
buffers: [vertexBufferLayout]
642
},
643
fragment: {
644
module: fragmentShader,
645
entryPoint: "main",
646
targets: [{ format: "bgra8unorm" }]
647
}
648
};
649
650
// Opaque rendering pipeline
651
const opaquePipeline = device.createRenderPipeline({
652
...basePipelineDesc,
653
primitive: {
654
topology: "triangle-list",
655
cullMode: "back"
656
},
657
depthStencil: {
658
format: "depth24plus",
659
depthWriteEnabled: true,
660
depthCompare: "less"
661
},
662
label: "Opaque Pipeline"
663
});
664
665
// Transparent rendering pipeline
666
const transparentPipeline = device.createRenderPipeline({
667
...basePipelineDesc,
668
primitive: {
669
topology: "triangle-list",
670
cullMode: "none"
671
},
672
fragment: {
673
...basePipelineDesc.fragment!,
674
targets: [{
675
format: "bgra8unorm",
676
blend: {
677
color: {
678
srcFactor: "src-alpha",
679
dstFactor: "one-minus-src-alpha"
680
},
681
alpha: {
682
srcFactor: "one",
683
dstFactor: "one-minus-src-alpha"
684
}
685
}
686
}]
687
},
688
depthStencil: {
689
format: "depth24plus",
690
depthWriteEnabled: false,
691
depthCompare: "less-equal"
692
},
693
label: "Transparent Pipeline"
694
});
695
```