Skip to content

Commit 0672d73

Browse files
committed
WGSL article edits
* Explain builtins sliglty better. * Format builtin table to be, hopefully, less confusing.
1 parent a2ac9b6 commit 0672d73

2 files changed

Lines changed: 167 additions & 70 deletions

File tree

webgpu/lessons/webgpu-wgsl.css

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.builtins td:last-child {
2+
text-align: left;
3+
}

webgpu/lessons/webgpu-wgsl.md

Lines changed: 164 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -446,136 +446,103 @@ struct Foo {
446446

447447
Above `blap.vNdx` gets its value from the builtin `vertex_index` and `blap.iNdx` gets its value from the builtin `instance_index`.
448448

449-
<div class="webgpu_center center data-table">
449+
<div class="webgpu_center center data-table builtins" style="max-width: 900px;">
450+
<h1>Compute Shader Builtins</h1>
450451
<table class="data">
451452
<thead>
452453
<tr>
453454
<th>Builtin Name</th>
454-
<th>Stage</th>
455455
<th>IO</th>
456456
<th>Type</th>
457457
<th>Description </th>
458458
</tr>
459459
</thead>
460460
<tbody>
461-
<tr>
462-
<td><dfn class="dfn-paneled" data-dfn-for="built-in values" data-dfn-type="dfn" data-noexport="" id="built-in-values-vertex_index">vertex_index</dfn> </td>
463-
<td>vertex </td>
464-
<td>input </td>
465-
<td>u32 </td>
466-
<td style="width:50%">
467-
Index of the current vertex within the current API-level draw command,
468-
independent of draw instancing.
469-
<p>For a non-indexed draw, the first vertex has an index equal to the <code>firstVertex</code> argument
470-
of the draw, whether provided directly or indirectly.
471-
The index is incremented by one for each additional vertex in the draw instance.</p>
472-
<p>For an indexed draw, the index is equal to the index buffer entry for the
473-
vertex, plus the <code>baseVertex</code> argument of the draw, whether provided directly or indirectly.</p></td>
474-
</tr>
475-
<tr>
476-
<td><dfn class="dfn-paneled" data-dfn-for="built-in values" data-dfn-type="dfn" data-noexport="" id="built-in-values-instance_index">instance_index</dfn> </td>
477-
<td>vertex </td>
478-
<td>input </td>
479-
<td>u32 </td>
480-
<td style="width:50%">
481-
Instance index of the current vertex within the current API-level draw command.
482-
<p>The first instance has an index equal to the <code>firstInstance</code> argument of the draw,
483-
whether provided directly or indirectly.
484-
The index is incremented by one for each additional instance in the draw.</p></td>
485-
</tr>
486-
<tr>
487-
<td rowspan="2"><dfn class="dfn-paneled" data-dfn-for="built-in values" data-dfn-type="dfn" data-noexport="" id="built-in-values-position">position</dfn> </td>
488-
<td>vertex </td>
489-
<td>output </td>
490-
<td>vec4&lt;f32&gt; </td>
491-
<td style="width:50%">Output position of the current vertex, using homogeneous coordinates.
492-
After homogeneous normalization (where each of the <em>x</em>, <em>y</em>, and <em>z</em> components
493-
are divided by the <em>w</em> component), the position is in the WebGPU normalized device
494-
coordinate space.
495-
See <a href="https://www.w3.org/TR/webgpu/#coordinate-systems"><cite>WebGPU</cite> § 3.3 Coordinate Systems</a>. </td>
496-
</tr>
497-
<tr>
498-
<td>fragment </td>
499-
<td>input </td>
500-
<td>vec4&lt;f32&gt; </td>
501-
<td style="width:50%">Framebuffer position of the current fragment in <a data-link-type="dfn" href="https://gpuweb.github.io/gpuweb/#framebuffer" id="ref-for-framebuffer">framebuffer</a> space.
502-
(The <em>x</em>, <em>y</em>, and <em>z</em> components have already been scaled such that <em>w</em> is now 1.)
503-
See <a href="https://www.w3.org/TR/webgpu/#coordinate-systems"><cite>WebGPU</cite> § 3.3 Coordinate Systems</a>. </td>
504-
</tr>
505-
<tr>
506-
<td><dfn class="dfn-paneled" data-dfn-for="built-in values" data-dfn-type="dfn" data-noexport="" id="built-in-values-front_facing">front_facing</dfn> </td>
507-
<td>fragment </td>
508-
<td>input </td>
509-
<td>bool </td>
510-
<td style="width:50%">True when the current fragment is on a <a data-link-type="dfn" href="https://gpuweb.github.io/gpuweb/#front-facing" id="ref-for-front-facing">front-facing</a> primitive.
511-
False otherwise. </td>
512-
</tr>
513-
<tr>
514-
<td><dfn class="dfn-paneled" data-dfn-for="built-in values" data-dfn-type="dfn" data-noexport="" id="built-in-values-frag_depth">frag_depth</dfn> </td>
515-
<td>fragment </td>
516-
<td>output </td>
517-
<td>f32 </td>
518-
<td style="width:50%">Updated depth of the fragment, in the viewport depth range.
519-
See <a href="https://www.w3.org/TR/webgpu/#coordinate-systems"><cite>WebGPU</cite> § 3.3 Coordinate Systems</a>. </td>
520-
</tr>
521461
<tr>
522462
<td><dfn class="dfn-paneled" data-dfn-for="built-in values" data-dfn-type="dfn" data-noexport="" id="built-in-values-local_invocation_id">local_invocation_id</dfn> </td>
523-
<td>compute </td>
524463
<td>input </td>
525464
<td>vec3&lt;u32&gt; </td>
526465
<td style="width:50%">The current invocation’s <a data-link-type="dfn" href="#local-invocation-id" id="ref-for-local-invocation-id①">local invocation ID</a>,
527466
i.e. its position in the <a data-link-type="dfn" href="#workgroup-grid" id="ref-for-workgroup-grid①">workgroup grid</a>. </td>
528467
</tr>
529468
<tr>
530469
<td><dfn class="dfn-paneled" data-dfn-for="built-in values" data-dfn-type="dfn" data-noexport="" id="built-in-values-local_invocation_index">local_invocation_index</dfn> </td>
531-
<td>compute </td>
532470
<td>input </td>
533471
<td>u32 </td>
534472
<td style="width:50%">The current invocation’s <a data-link-type="dfn" href="#local-invocation-index" id="ref-for-local-invocation-index">local invocation index</a>, a linearized index of
535473
the invocation’s position within the <a data-link-type="dfn" href="#workgroup-grid" id="ref-for-workgroup-grid②">workgroup grid</a>. </td>
536474
</tr>
537475
<tr>
538476
<td><dfn class="dfn-paneled" data-dfn-for="built-in values" data-dfn-type="dfn" data-noexport="" id="built-in-values-global_invocation_id">global_invocation_id</dfn> </td>
539-
<td>compute </td>
540477
<td>input </td>
541478
<td>vec3&lt;u32&gt; </td>
542479
<td style="width:50%">The current invocation’s <a data-link-type="dfn" href="#global-invocation-id" id="ref-for-global-invocation-id">global invocation ID</a>,
543480
i.e. its position in the <a data-link-type="dfn" href="#compute-shader-grid" id="ref-for-compute-shader-grid">compute shader grid</a>. </td>
544481
</tr>
545482
<tr>
546483
<td><dfn class="dfn-paneled" data-dfn-for="built-in values" data-dfn-type="dfn" data-noexport="" id="built-in-values-workgroup_id">workgroup_id</dfn> </td>
547-
<td>compute </td>
548484
<td>input </td>
549485
<td>vec3&lt;u32&gt; </td>
550486
<td style="width:50%">The current invocation’s <a data-link-type="dfn" href="#workgroup-id" id="ref-for-workgroup-id">workgroup ID</a>,
551487
i.e. the position of the workgroup in the <a data-link-type="dfn" href="#compute-shader-grid" id="ref-for-compute-shader-grid">compute shader grid</a>. </td>
552488
</tr>
553489
<tr>
554490
<td><dfn class="dfn-paneled" data-dfn-for="built-in values" data-dfn-type="dfn" data-noexport="" id="built-in-values-num_workgroups">num_workgroups</dfn> </td>
555-
<td>compute </td>
556491
<td>input </td>
557492
<td>vec3&lt;u32&gt; </td>
558493
<td style="width:50%">The <a data-link-type="dfn" href="#dispatch-size" id="ref-for-dispatch-size">dispatch size</a>, <code>vec&lt;u32&gt;(group_count_x, group_count_y, group_count_z)</code>, of the compute shader <a href="https://www.w3.org/TR/webgpu/#compute-pass-encoder-dispatch">dispatched</a> by the API. </td>
559494
</tr>
495+
</tbody>
496+
</table>
497+
<h1>Fragment Shader Builtins</h1>
498+
<table class="data">
499+
<thead>
500+
<tr>
501+
<th>Builtin Name</th>
502+
<th>IO</th>
503+
<th>Type</th>
504+
<th>Description </th>
505+
</tr>
506+
</thead>
507+
<tbody>
508+
<tr>
509+
<td><dfn class="dfn-paneled" data-dfn-for="built-in values" data-dfn-type="dfn" data-noexport="" id="built-in-values-position">position</dfn> </td>
510+
<td>input </td>
511+
<td>vec4&lt;f32&gt; </td>
512+
<td style="width:50%">Framebuffer position of the current fragment in <a data-link-type="dfn" href="https://gpuweb.github.io/gpuweb/#framebuffer" id="ref-for-framebuffer">framebuffer</a> space.
513+
(The <em>x</em>, <em>y</em>, and <em>z</em> components have already been scaled such that <em>w</em> is now 1.)
514+
See <a href="https://www.w3.org/TR/webgpu/#coordinate-systems"><cite>WebGPU</cite> § 3.3 Coordinate Systems</a>. </td>
515+
</tr>
516+
<tr>
517+
<td><dfn class="dfn-paneled" data-dfn-for="built-in values" data-dfn-type="dfn" data-noexport="" id="built-in-values-front_facing">front_facing</dfn> </td>
518+
<td>input </td>
519+
<td>bool </td>
520+
<td style="width:50%">True when the current fragment is on a <a data-link-type="dfn" href="https://gpuweb.github.io/gpuweb/#front-facing" id="ref-for-front-facing">front-facing</a> primitive.
521+
False otherwise. </td>
522+
</tr>
523+
<tr>
524+
<td><dfn class="dfn-paneled" data-dfn-for="built-in values" data-dfn-type="dfn" data-noexport="" id="built-in-values-frag_depth">frag_depth</dfn> </td>
525+
<td>output </td>
526+
<td>f32 </td>
527+
<td style="width:50%">Updated depth of the fragment, in the viewport depth range.
528+
See <a href="https://www.w3.org/TR/webgpu/#coordinate-systems"><cite>WebGPU</cite> § 3.3 Coordinate Systems</a>. </td>
529+
</tr>
560530
<tr>
561531
<td><dfn class="dfn-paneled" data-dfn-for="built-in values" data-dfn-type="dfn" data-noexport="" id="built-in-values-sample_index">sample_index</dfn> </td>
562-
<td>fragment </td>
563532
<td>input </td>
564533
<td>u32 </td>
565534
<td style="width:50%">Sample index for the current fragment.
566535
The value is least 0 and at most <code>sampleCount</code>-1, where <code>sampleCount</code> is the MSAA sample <code class="idl"><a data-link-type="idl" href="https://www.w3.org/TR/webgpu/#dom-gpumultisamplestate-count" id="ref-for-dom-gpumultisamplestate-count">count</a></code> specified for the GPU render pipeline. <br>See <a href="https://www.w3.org/TR/webgpu/#gpurenderpipeline"><cite>WebGPU</cite> § 10.3 GPURenderPipeline</a>. </td>
567536
</tr>
568537
<tr>
569538
<td rowspan="2"><dfn class="dfn-paneled" data-dfn-for="built-in values" data-dfn-type="dfn" data-noexport="" id="built-in-values-sample_mask">sample_mask</dfn> </td>
570-
<td>fragment </td>
571539
<td>input </td>
572540
<td>u32 </td>
573541
<td style="width:50%">Sample coverage mask for the current fragment.
574542
It contains a bitmask indicating which samples in this fragment are covered
575543
by the primitive being rendered. <br>See <a href="https://www.w3.org/TR/webgpu/#sample-masking"><cite>WebGPU</cite> § 23.3.11 Sample Masking</a>. </td>
576544
</tr>
577545
<tr>
578-
<td>fragment </td>
579546
<td>output </td>
580547
<td>u32 </td>
581548
<td style="width:50%">Sample coverage mask control for the current fragment.
@@ -584,9 +551,134 @@ Above `blap.vNdx` gets its value from the builtin `vertex_index` and `blap.iNdx`
584551
the color attachments to be discarded. <br>See <a href="https://www.w3.org/TR/webgpu/#sample-masking"><cite>WebGPU</cite> § 23.3.11 Sample Masking</a>. </td>
585552
</tr>
586553
</tbody>
554+
</table>
555+
<h1>Vertex Shader Builtins</h1>
556+
<table class="data">
557+
<thead>
558+
<tr>
559+
<th>Builtin Name</th>
560+
<th>IO</th>
561+
<th>Type</th>
562+
<th>Description </th>
563+
</tr>
564+
</thead>
565+
<tbody>
566+
<tr>
567+
<td><dfn class="dfn-paneled" data-dfn-for="built-in values" data-dfn-type="dfn" data-noexport="" id="built-in-values-vertex_index">vertex_index</dfn> </td>
568+
<td>input </td>
569+
<td>u32 </td>
570+
<td style="width:50%">
571+
Index of the current vertex within the current API-level draw command,
572+
independent of draw instancing.
573+
<p>For a non-indexed draw, the first vertex has an index equal to the <code>firstVertex</code> argument
574+
of the draw, whether provided directly or indirectly.
575+
The index is incremented by one for each additional vertex in the draw instance.</p>
576+
<p>For an indexed draw, the index is equal to the index buffer entry for the
577+
vertex, plus the <code>baseVertex</code> argument of the draw, whether provided directly or indirectly.</p></td>
578+
</tr>
579+
<tr>
580+
<td><dfn class="dfn-paneled" data-dfn-for="built-in values" data-dfn-type="dfn" data-noexport="" id="built-in-values-instance_index">instance_index</dfn> </td>
581+
<td>input </td>
582+
<td>u32 </td>
583+
<td style="width:50%">
584+
Instance index of the current vertex within the current API-level draw command.
585+
<p>The first instance has an index equal to the <code>firstInstance</code> argument of the draw,
586+
whether provided directly or indirectly.
587+
The index is incremented by one for each additional instance in the draw.</p></td>
588+
</tr>
589+
<tr>
590+
<td><dfn class="dfn-paneled" data-dfn-for="built-in values" data-dfn-type="dfn" data-noexport="" id="built-in-values-position">position</dfn> </td>
591+
<td>output </td>
592+
<td>vec4&lt;f32&gt; </td>
593+
<td style="width:50%">Output position of the current vertex, using homogeneous coordinates.
594+
After homogeneous normalization (where each of the <em>x</em>, <em>y</em>, and <em>z</em> components
595+
are divided by the <em>w</em> component), the position is in the WebGPU normalized device
596+
coordinate space.
597+
See <a href="https://www.w3.org/TR/webgpu/#coordinate-systems"><cite>WebGPU</cite> § 3.3 Coordinate Systems</a>. </td>
598+
</tr>
599+
</tbody>
587600
</table>
588601
</div>
589602

603+
It's important to note here, there isn't one builtin called `position`. There are 2 builtins, an output called `position` used
604+
in vertex shaders, and an input called `position` used in fragment shaders. This is no different than having 2 functions in JavaScript
605+
606+
```js
607+
/**
608+
* function that has position as an output
609+
* @param \{{array: number[], index: number, position: Float32Array}} params
610+
*/
611+
function getVertex(params) {
612+
const { array, index, position } = params;
613+
position[0] = array[index];
614+
position[1] = array[index + 1];
615+
position[2] = array[index + 2];
616+
}
617+
618+
/**
619+
* function that has position as an input
620+
* @param \{{position: Float32Array}} params
621+
*/
622+
function printValue(params) {
623+
const { position } = params;
624+
return [...position].map(v => v.toString()).join(', ');
625+
}
626+
```
627+
628+
Above are 2 functions that both have a parameter called `position`. They are unrelated to each other.
629+
The same is true of `@builtin(position)` in a vertex shader and `@builtin(position)` in a fragment shader.
630+
The 2 are unrelated to each other. The confusion often comes from the fact that one struct declaration can
631+
be used in a single shader module.
632+
633+
```wgsl
634+
struct VOut {
635+
@builtin(position) p: vec4f;
636+
};
637+
638+
@vertex fn vs() -> VOut {
639+
// this is setting the vertex shader's @builtin(position)
640+
return VOut(vec4f(0, 0, 0, 1));
641+
}
642+
643+
@fragment fn fs(v: VOut) {
644+
// this is reading the fragment shader's @builtin(position)
645+
return v.p;
646+
}
647+
```
648+
649+
From the point of view of WGSL, VOut is declared twice. Once when used in `vs` and again in `fs`.
650+
651+
To maybe see this more clearly, you can declare these to shaders in separate modules.
652+
653+
```wgsl
654+
struct VOut {
655+
@builtin(position) p: vec4f;
656+
};
657+
658+
@vertex fn vs() -> VOut {
659+
// this is setting the vertex shader's @builtin(position)
660+
return VOut(vec4f(0, 0, 0, 1));
661+
}
662+
```
663+
664+
```wgsl
665+
struct VIn {
666+
@builtin(position) fragPosition: vec4f;
667+
};
668+
669+
@fragment fn fs(v: VIn) {
670+
// this is reading the fragment shader's @builtin(position)
671+
return v.fragPosition;
672+
}
673+
```
674+
675+
These 2 shader modules, combined into a render pipeline, are equivalent to the
676+
previous one declared in one module.
677+
678+
The benefit to them both being called `position` is it allows them to be used in the same
679+
shader module. If that wasn't true, you'd be required to declare a different struct
680+
for vertex shader output vs fragment shader input.
681+
590682
## flow control
591683

592684
Like most computer languages, WGSL has flow control statements.
@@ -904,3 +996,5 @@ so they will both show up as a required bindings when using `vs2` in a pipeline.
904996

905997
<p class="copyright" data-fill-with="copyright"> <a href="https://www.w3.org/Consortium/Legal/ipr-notice#Copyright">Copyright</a> © 2023 <a href="https://www.w3.org/">World Wide Web Consortium</a>. <abbr title="World Wide Web Consortium">W3C</abbr><sup>®</sup> <a href="https://www.w3.org/Consortium/Legal/ipr-notice#Legal_Disclaimer">liability</a>, <a href="https://www.w3.org/Consortium/Legal/ipr-notice#W3C_Trademarks">trademark</a> and <a href="https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document" rel="license">permissive document license</a> rules apply. </p>
906998

999+
<!-- keep this at the bottom of the article -->
1000+
<link href="webgpu-wgsl.css" rel="stylesheet">

0 commit comments

Comments
 (0)