diff --git a/webgpu/lessons/webgpu-constants.md b/webgpu/lessons/webgpu-constants.md index da7ecf31..98bd8d5f 100644 --- a/webgpu/lessons/webgpu-constants.md +++ b/webgpu/lessons/webgpu-constants.md @@ -122,12 +122,178 @@ The difference is, pipeline overridable constants can be applied AFTER the shader module has been created which makes them technically faster to apply then creating a new shader module. Creating a pipeline is not a fast operation though so it's not clear how much time this saves -on the overall process of creating a pipeline. I'd suspect the more -complex the shader the more time it saves. +on the overall process of creating a pipeline. It's possible though, +that the WebGPU implementation can use information from the first time +you created a pipeline with certain constants so that the next time +you create it with different constants, much less work is done. In any case, it is one way to get some small amount of data into a shader. -It is **not** common to use pipeline overridable constants to pass in a color. -That example was used because it's easy to understand and to show the results. -It *might* be useful for an iteration count, the size of an array (for -example the number of lights), etc... +## entry points are independently evaluated + +It's also important to remember that entry points are evaluated in +isolation as was partially covered in +[the article on inter-stage variables](webgpu-inter-stage-variables.html#a-builtin-position). + +It's as though the code passed to `createShaderModule` was striped +of everything not relevant to the current entry point. Pipeline +override constants are applied, then, the shader for that entry point is +created. + +Let's expand our example above. We'll change the shader so both the vertex +and fragment stages use the constants. We'll pass the vertex stage's value +to the fragment stage. We'll then draw every other vertical strip of 50 +pixels with one value or the other. + +```wgsl ++struct VOut { ++ @builtin(position) pos: vec4f, ++ @location(0) color: vec4f, ++} + +@vertex fn vs( + @builtin(vertex_index) vertexIndex : u32 +-) -> @builtin(position) vec4f { ++) -> VOut { + let pos = array( + vec2f( 0.0, 0.5), // top center + vec2f(-0.5, -0.5), // bottom left + vec2f( 0.5, -0.5) // bottom right + ); + +- return vec4f(pos[vertexIndex], 0.0, 1.0); ++ return VOut( ++ vec4f(pos[vertexIndex], 0.0, 1.0), ++ vec4f(red, green, blue, 1), ++ ); +} + +override red = 0.0; +override green = 0.0; +override blue = 0.0; + +-@fragment fn fs() -> @location(0) vec4f { +- return vec4f(red, green, blue, 1.0); ++@fragment fn fs(v: VOut) -> @location(0) vec4f { ++ let colorFromVertexShader = v.color; ++ let colorFromFragmentShader = vec4f(red, green, blue, 1.0); ++ // select one color or the other every 50 pixels ++ return select( ++ colorFromVertexShader, ++ colorFromFragmentShader, ++ v.pos.x % 100.0 > 50.0); +} +``` + +Now we'll pass different constants into the each entry point + +```js + const pipeline = device.createRenderPipeline({ + label: 'our hardcoded triangle pipeline', + layout: 'auto', + vertex: { + module, ++ constants: { ++ red: 1, ++ green: 1, ++ blue: 0, ++ }, + }, + fragment: { + module, + targets: [{ format: presentationFormat }], + constants: { + red: 1, + green: 0.5, + blue: 1, + }, + }, + }); +``` + +The result shows the constants were different in each stage + +{{{example url="../webgpu-constants-override-set-entry-points.html"}}} + +Again, functionally, the fact that we used one shader module with one WGSL `code` +is just a convenience. The code above is functionally equivalent to + +```js + const vertexModule = device.createShaderModule({ + code: ` + struct VOut { + @builtin(position) pos: vec4f, + @location(0) color: vec4f, + } + + @vertex fn vs( + @builtin(vertex_index) vertexIndex : u32 + ) -> VOut { + let pos = array( + vec2f( 0.0, 0.5), // top center + vec2f(-0.5, -0.5), // bottom left + vec2f( 0.5, -0.5) // bottom right + ); + + return VOut( + vec4f(pos[vertexIndex], 0.0, 1.0), + vec4f(red, green, blue, 1), + ); + } + + override red = 0.0; + override green = 0.0; + override blue = 0.0; + `, + }); + + const fragmentModule = device.createShaderModule({ + code: ` + struct VOut { + @builtin(position) pos: vec4f, + @location(0) color: vec4f, + } + + override red = 0.0; + override green = 0.0; + override blue = 0.0; + + @fragment fn fs(v: VOut) -> @location(0) vec4f { + let colorFromVertexShader = v.color; + let colorFromFragmentShader = vec4f(red, green, blue, 1.0); + // select one color or the other every 50 pixels + return select( + colorFromVertexShader, + colorFromFragmentShader, + v.pos.x % 100.0 > 50.0); + } + `, + }); + + const pipeline = device.createRenderPipeline({ + label: 'our hardcoded triangle pipeline', + layout: 'auto', + vertex: { +* module: vertexModule, + constants: { + red: 1, + green: 1, + blue: 0, + }, + }, + fragment: { +* module: fragmentModule, + targets: [{ format: presentationFormat }], + constants: { + red: 1, + green: 0.5, + blue: 1, + }, + }, + }); +``` + +{{{example url="../webgpu-constants-override-separate-modules.html"}}} + +Note: It is **not** common to use pipeline overridable constants to pass in a color. +We used a color because it's easy to understand and to show the results. \ No newline at end of file diff --git a/webgpu/webgpu-constants-override-separate-modules.html b/webgpu/webgpu-constants-override-separate-modules.html new file mode 100644 index 00000000..605086c9 --- /dev/null +++ b/webgpu/webgpu-constants-override-separate-modules.html @@ -0,0 +1,163 @@ + + + + + + WebGPU Simple Triangle with Canvas CSS + + + + + + + diff --git a/webgpu/webgpu-constants-override-set-entry-points.html b/webgpu/webgpu-constants-override-set-entry-points.html new file mode 100644 index 00000000..fd1cbcd7 --- /dev/null +++ b/webgpu/webgpu-constants-override-set-entry-points.html @@ -0,0 +1,151 @@ + + + + + + WebGPU Simple Triangle with Canvas CSS + + + + + + +