Directory portal: juejin.cn/post/701059…


www.w3.org/TR/webgpu/#…

GPUPipelineBase Exercises ├ GPURenderPipeline ├Copy the code

pipeline

Pipeline represents a certain calculation process. In WebGPU, there are two kinds of rendering pipeline and calculation pipeline.

This process requires objects or resources such as binding groups, VBos, shaders, etc., and then eventually outputs something like a rendering pipeline that outputs a color value (in the form of a color attachment), and a computed pipeline that outputs it to its designated location, which I won’t go into too much detail here.

Structurally, a pipeline consists of a series of programmable phases and a number of fixed states.

Note that depending on the operating system and the graphics driver, some fixed states are compiled into the shader code, so it is a good idea to combine them into a pipeline object.

Both pipeline objects can be created from device objects.

In the corresponding channel encoder, the pipeline can be switched for different calculation procedures.

1 Basic pipeline

dictionary GPUPipelineDescriptorBase : GPUObjectDescriptorBase {
	GPUPipelineLayout layout;
};

interface mixin GPUPipelineBase {
	GPUBindGroupLayout getBindGroupLayout(unsigned long index);
};
Copy the code

Two lines were need parameter object, at the time of creating parameter objects have their own different kinds of concrete, but are inherited from the GPUPipelineDescriptorBase type here.

Both pipelines also inherit from the base pipeline type GPUPipelineBase.

1.1 getBindGroupLayout method of base pipeline

Each pipeline object has this method, which takes a number of type unsigned long as an argument and returns a binding group layout object at the corresponding position in the pipeline layout object.

Note: If no layout object is passed when the pipeline is created, this method automatically constructs the binding group layout object based on the group feature in the shader code.

It is important to note that this numeric parameter is smaller than the maxBindGroups value in the device restriction list.

1.2 Default pipeline layout

If a pipeline layout object is not set when it is created, a default layout object is automatically created internally. This process is not fully explained in the documentation, but is described as a “reflection” technique, which is then constructed step by step.

For details about how to create a default pipeline layout, see WebGPU Spec 10.1.1 in the documentation.

The default pipeline layout object whose bindGroupLayouts array is an empty array.

1.3 Programmable stage: GPUProgrammableStage

The parameter object is used when creating the pipeline object. The parameter object has different programmable stages that can be set. Each stage is a GPUProgrammableStage object. This is detailed in the pipeline creation section below.

An object of type GPUProgrammableStage organizes what GPUShaderModule is used for a specific stage in the pipeline, what WGSL entry point function name is used, and what constant values should be passed.

We’ll talk about it in detail.

dictionary GPUProgrammableStage {
  required GPUShaderModule module;
  required USVString entryPoint;
  record<USVString, GPUPipelineConstantValue> constants;
};

typedef double GPUPipelineConstantValue;
Copy the code

For each such object, there are two mandatory arguments:

  • Module, variable of type GPUShaderModule, shader module, see shader module section

  • EntryPoint, a string that specifies an entry function in the Module shader code

There is also an optional parameter Constants, which is a JavaScript object of type GPUPipelineConstantValue that is used to pass a constant value of the Override property in the shader code.

The key of this object can be the I in the parentheses of the OVERRIDE (I) feature in WGSL, or it can be a constant name. For example (take the vertex phase) :

const pipeline = device.createRenderPipeline({
  vertex: {
    / *... * /
    constants: {
      1300: 2.0.// Will be passed to the 1300th gain constant in the shader code
      depth: -1.// The depth constant will be passed to the shader code}}})Copy the code

Corresponding shader code:

[[override(1300)]] let gain: f32; [[override]] let depth: f32; [[override(0)]] let has_point_light: bool = true; [[Override (1200)]] Let specular_param: f32 = 2.3; [[override]] let width: f32 = 0.0; // Other codeCopy the code

If you want to override any of the WGSL constants with default values, you can pass an object like this:

const pipeline = device.createRenderPipeline({
  vertex: {
    / *... * /
    constants: {
      1300: 2.0.// Will be passed to the 1300th gain constant in the shader code
      depth: -1.// The depth constant will be passed to the shader code
      0: false.1200: 3.0.width: 20,}}})Copy the code

When a JavaScript numeric type is converted to WGSL, it is automatically converted based on the specific type (bool, i32, u32, f32) of the constant in WGSL.

1.4 Supplementary information: pipeline and coloring stage

The WebGPU provides instructions to the GPU by issuing drawing commands or scheduling commands.

The behavior of the computation that the pipeline performs on the GPU is described as a series of stages, some of which are programmable. In WebGPU, a pipeline needs to be created before drawing or scheduling, which is called rendering pipeline and calculation pipeline.

1.5 How do I verify the compliance of programmable phase objects

When creating a pipeline, there are requirements for a stage object (called stage) and a pipeline layout object (called Layout) :

  • Stage. module must be a valid oneGPUShaderModuleobject
  • Stage. module shader code must have an entry function with the same name as stage.entryPoint
  • Verify the binding for each bound variable used in the entry function
  • For each sampled texture used in the entry function, set the texture to texture, the sampler to sampler, and sampler. Type if it is “filtering”, then texture. SampleType cannot be “unfilterable-float”.
  • For all constants in stage.constants, there must be a corresponding Override constant in the shader; If constants in the shader are not given default values using initialization syntax, then values must be given in stage.Constants.

1.6 How can I verify the compliance of bound variables in a shader

As with the caption intent, this section guides the syntax writing and organization of bound variables in the shader.

Call the shader variable bindGroupId and bindId and call the pipelineLayout object pipelineLayout. Binding group layout can be made of pipelineLayout. BindGroupLayouts [bindGroupId] getting and for bindGroupLayout:

  • BindGroupLayout must have an entry object with the same binding value as bindId;

  • Iterate through entries in bindGroupLayout and set the traversed GPUBindGroupLayoutEntry variable to Entry:

    • If the type is buffer, and entry.buffer.type is

      • “Uniform”, then the shader code uses this variablevar<uniform>The statement
      • “Storage”, then the shader code uses this variablevar<storage, read_write>The statement
      • “Read-only -storage”, then the shader code uses this variablevar<storage, read>The statement
    • If is a buffer type and entry. Buffer. MinBindingSize is not zero:

      • If shader code in a structure of a final field is unbounded array, then the entry. The buffer. The minBindingSize must be greater than or equal to the array of offsets and with pace
      • If shader code is not a structure the last field in unbounded array, then the entry. The buffer. The minBindingSize must be greater than or equal to the size of the structure
    • If it is of sampler type and entry.sampler. Type is

      • “Filtering”, so variable is usedsamplertype
      • “Comparison”, then variable is usedcomparison_samplertype
    • If it is texture type, if and only if entry. Texture. Multisampled is true, The variable must be texture_multiSAMpled_2d

      or texture_depth_multiSAMpled_2d

      .

    • If it is the texture type, when entry.texture. Type is

      • “Float”, “unfilterable-float”, “sint”, “uint” when variable is usedtexture_1d<T>.texture_2d<T>.texture_2d_array<T>.texture_cube<T>.texture_cube_array<T>.texture_3d<T>Or,texture_multisampled_2d<T>Type to declare; SampleType is f32 when “float”, “unfilterable-float”, T is i32 when “sint”, T is u32 when “uint”;
      • “Depth”, variable to usetexture_depth_2d.texture_depth_2d_array.texture_depth_cube.texture_depth_cube_arraytexture_depth_multisampled_2dType to declare
    • If the texture type, when entry. Texture. ViewDimension is

      • “1d”, variable is usedtexture_1d<T>The statement;
      • “2d”, variable is usedtexture_2d<T>texture_multisampled_2d<T>The statement;
      • “2D-array”, variable is usedtexture_2d_array<T>The statement;
      • “Cube”, variable is usedtexture_cube<T>The statement;
      • “Cube-array”, variable is usedtexture_cube_array<T>The statement;
      • “3D”, variable is usedtexture_3d<T>The statement;
    • If it is storageTexture type, when entry. StorageTexture. ViewDimension is

      • “1d”, the variable is declared using texture_storage_1D

        ;
        ,>

      • “2d”, variable is declared using texture_storage_2d

        ;
        ,>

      • “2D-array”, variable is declared using texture_storage_2d_array

        ;
        ,>

      • “3d”, variable is declared using texture_storage_3D

        ;
        ,>

        Entry. StorageTexture. Access is A “write – only”, the generic in A write is used, the generic type T to entry. StorageTexture. The format.

2 Rendering pipeline

The GPURenderPipeline is a pipeline that controls the vertex coloring and chip coloring phases.

It can be used by the render channel encoder GPURenderPassEncoder and the render package encoder GPURenderBundleEncoder.

The inputs to the render pipeline are:

  • throughGPUPipelineLayoutThe resource bound to the binding group provided by
  • Vertex and index data, used by the following vertex shader phase
  • Color attachment, described by color target state
  • An optional depth template attachment, described by the depth template state

The output of the render pipeline is:

  • The type of the binding group is"storage"GPUBuffer resources
  • Binding in the groupaccessProperties for"write-only"GPUStorageTexture resources
  • Color in attachment
  • Optional depth template attachment

The rendering pipeline has the following rendering stages:

  • Vertex Fetch is obtained from Vertex FetchGPUVertexStateObject buffers property to describe and control
  • Vertex Shader, by Vertex ShaderGPUVertexStateObject description and control
  • Primitive Assembly, byGPUPrimitiveStateObject description and control
  • Rasterization, byGPUPrimitiveState,GPUDepthStencilStateGPUMultisampleStateObject to describe and control
  • Fragment Shader, byGPUFragmentStateObject to control
  • Template test and operation (StencilTest) and DepthTest and write (DepthTest), followed byGPUDepthStencilStateObject description and control
  • Fusion output, byGPUFragmentStateTargets property of the object to describe and control

The above types of GPUVertexState, GPUPrimitiveState, GPUFragmentState, GPUDepthStencilState, and GPUMultisampleState are listed below Vertex coloring stage, pixel assembly stage, slice coloring stage, deep template testing stage and multiple sampling stage are introduced in several sections. Corresponds to the rendering pipeline to create next Section GPURenderPipelineDescriptor object of vertex, primitive, fragments, depthStencil, fragments field.

Of these five phases, only vertex coloring phase and chip coloring phase are programmable.

The WebIDL definition for the render pipeline is as follows:

[Exposed=(Window, DedicatedWorker), SecureContext]
interface GPURenderPipeline {
};
GPURenderPipeline includes GPUObjectBase;
GPURenderPipeline includes GPUPipelineBase;
Copy the code

2.1 Pipeline creation

The render pipeline is created through the device object’s createRenderPipeline method.

Create the rendering pipeline needs a GPURenderPipelineDescriptor type of object as a parameter.

dictionary GPURenderPipelineDescriptor : GPUPipelineDescriptorBase {
  required GPUVertexState vertex;
  GPUPrimitiveState primitive = {};
  GPUDepthStencilState depthStencil;
  GPUMultisampleState multisample = {};
  GPUFragmentState fragment;
};
Copy the code

In this object, there are several phase states, and the purpose of each field is briefly described here, with their corresponding classes described in more detail in the following sections.

  • vertexFields,GPUVertexStateIn addition to the attributes of the programmable stage object, it also describes the entry point function of the vertex shader, and the layout of the vertex data input into the pipeline.
  • primitiveFields,GPUPrimitiveStateType of JavaScript object that describes information about primitive assembly. By default, the object is empty.
  • depthStencilFields,GPUDepthStencilStateType JavaScript object, which is an optional object that describes deep template test information;
  • multisampleFields,GPUMultisampleStateA JavaScript object of type, which by default is an empty object, describing the multisample information of the pipeline;
  • fragmentFields,GPUFragmentStateIn addition to the properties of the programmable phase object, it also describes the entry point function of the slice shader and how to output the slice color. If it is null, then the render pipeline will not output color, only in colorless output mode, which is normally used only for depth template testing.

2.1.1 Creating a Pipeline asynchronously

Besides synchronized createRenderPipeline method device object to create, can also through the device object createRenderPipelineAsync object to asynchronous created, it returns the resolve value object rendering pipeline is a Promise, You can call in an asynchronous function await to get it resolve value, asynchronous create functions also use GPURenderPipelineDescriptor parameter object.

2.1.2 how to validate GPURenderPipelineDescriptor compliance

Meet all the following conditions:

  • Verify that the programmable properties of the vertex shader stage object are not problematic
  • GPUVertexState = GPUVertexState = GPUVertexState
  • Fragment if descriptor. Fragment exists (not null) :
    • Verify that the programmable properties of the chip coloring phase object are not problematic
    • Verify that the chip coloring phase object itself has a problem
    • If the fragment shader used in “sample_mask” built-in variables, then descriptor. The multisample. AlphaToCoverageEnable to set to false
  • Verify that there is a problem with the primitive assembly stage object
  • If Descriptor. DepthStencil is not null, then verify that the depth template test phase object has a problem
  • Verify that there is no problem with the multiple sampling stage object
  • For user-defined exits in vertex shaders, the slice shader must have corresponding entrances, as discussed in WGSL
  • For the slice shader and the custom components in the slice shader, the total number of components passed between the two shaders by location is smaller than the number in the device restriction listmaxInterStageShaderComponentsValue.

2.2 Pixel assembly stage

GPURenderPipelineDescriptor Object in the primitive field values, is a JavaScript Object.

enum GPUPrimitiveTopology {
  "point-list",
  "line-list",
  "line-strip",
  "triangle-list",
  "triangle-strip"
};

enum GPUFrontFace {
  "ccw",
  "cw"
};

enum GPUCullMode {
  "none",
  "front",
  "back"
};

dictionary GPUPrimitiveState {
  GPUPrimitiveTopology topology = "triangle-list";
  GPUIndexFormat stripIndexFormat;
  GPUFrontFace frontFace = "ccw";
  GPUCullMode cullMode = "none";

  boolean clampDepth = false;
};
Copy the code

The primitive assembly stage object is of type GPUPrimitiveState, and all parameters are optional, as is itself optional.

  • parametertopology.GPUPrimitiveTopologyString enumeration type of value indicating how vertices are assembled, default is triangle list"triangle-list"And the triangle belt"triangle-strip", the line list"line-list", the line with"line-strip"And simply draw points"piont-list"Is similar to the mode parameter of drawArray in WebGL;
  • parameterstripIndexFormatIf it is an index triangle, specify the numeric type of the index value, which is a string enumeration typeGPUIndexFormat, see the definition of WebIDL in the vertex coloring stage;
  • parameterfrontFace, optional. The default value is"ccw", is a string enumeration typeGPUFrontFace, refers to the rotation direction of the triangle formed by points, CCW is counterclockwise, cw is clockwise.
  • parametercullMode, optional. The default value is"none", is a string enumeration typeGPUCullMode“, refers to the culling mode. In addition to None, there are both forward and back culling.
  • parameterclampDepthThis parameter is optional. The default value is false. If enabled, the depth value will be truncated. Need to be enabled when requesting device objectsclamp-depthFunction.

2.2.1 Vertex IndexFormat GPUIndexFormat

enum GPUIndexFormat {
  "uint16",
  "uint32"
};
Copy the code

The vertex index format, GPUIndexFormat, determines the data type of index values in the VBO and the starting value of primiples when the primiples are “line-strip” and “triangle-strip”.

The Primitive Restart Value indicates where to Restart a Primitive rather than continue to construct a triangle using previously indexed vertices.

[GPUPrimitiveState](#2.2 Primitive assembly phase), if its “Topology” field specifies “line-strip” or “triangle-strip”, Its “stripIndexFormat” field needs to be set accordingly so that the pipeline can be created using primitives to recalculate the value.

If “triangle-list”, “line-list”, and “point-list” are used, then “stripIndexFormat” should be set to undefined, The index is set using the setIndexBuffer method of the GPURenderPassEncoder.

.

GPUIndexFormat is used in the Vertex shader phase (Vertex State).

Uint16 represents the reset value of 2byte (0xFFFF), that is, 2byte takes an index number; Uint32 Indicates that the reset value is 4 bytes (0xFFFFFFFF), that is, an index number is taken from 4 bytes.

2.2.2 How to verify the compliance of objects in the pixel assembly stage

If all of the following conditions are met, then the primitive assembly stage object is fine:

  • The topology field for the primitive assembly phase object is “line-strip” or “triangle-strip”, so stripIndexFormat cannot be undefined; The topology is something else, stripIndexFormat has to be undefined;
  • ClampDepth of objects in the assembly stage is true, so the device’s function list should include “depth-clamp” function

2.2.3 Code Examples

const renderPipeline = device.createRenderPipeline({
  / *... * /
  primitive: {
    topology: "triangle-list",}})Copy the code

2.3 Vertex coloring stage

Vertex shader stage Object, is that the value of the vertex in GPURenderPipelineDescriptor Object, is a JavaScript Object, To satisfy the GPUVertexState type (including its parent type GPUProgrammableStage) :

dictionary GPUVertexState: GPUProgrammableStage { sequence<GPUVertexBufferLayout? > buffers = []; }; dictionary GPUVertexBufferLayout { required GPUSize64 arrayStride; GPUVertexStepMode stepMode = "vertex"; required sequence<GPUVertexAttribute> attributes; }; enum GPUVertexStepMode { "vertex", "instance" };Copy the code

The GPUVertexState object requires a Buffers field, which is an array of the GPUVertexBufferLayout object.

The GPUVertexBufferLayout object takes two mandatory arguments:

  • arrayStride, unsigned longlong type, indicating the step size of all data (coordinates, colors, normals, texture coordinates, etc.) contained in a vertex;
  • attributes, an array of elements of typeGPUVertexAttributeObject describing the number of vertices in this piece of dataVertex attribute;

There is also an optional argument, GPUVertexStepMode character enumeration type of the field stepMode, the default value is “vertex”; It represents how to access vertex data and has two values:

  • “Vertex”, meaning that no matter how many times the render channel encoder issues a draw (whatever the instanceCount parameter of the draw method is), the vertex data will be read from the beginning of the VertexBuffer, but will continue reading from the end of the first read;
  • “Instance”, which means that even if the render channel encoder draws multiple times (the instanceCount parameter of the draw method is greater than 1), after the first draw (after the vertex shader has run once), the vertex data will still be fetched from the same starting point of the VertexBuffer;

2.3.1 VertexBuffer and GPUVertexAttribute

Conceptually, the vertex cache is a descriptive view of the vertex data in video memory. Specifically, it is an array. The distance between the preceding array element and the following array element is called the ArrayStride (byte), or the length of the element.

Each element, called a vertex data, consists of several vertex attributes. The location of each vertex property in the vertex shader is unique.

dictionary GPUVertexAttribute {
  required GPUVertexFormat format;
  required GPUSize64 offset;
  required GPUIndex32 shaderLocation;
};
Copy the code

In the Attributes array of the GPUVertexBufferLayout object, each element is the GPUVertexAttribute object.

Each GPUVertexAttribute object takes three mandatory parameters:

  • format.GPUVertexFormatThe character enumeration type, see below [vertex format](# vertex format GPUVertexFormat), determines the numeric type and element composition of certain vertex attributes;
  • offset, type unsigned longlong, specifying its offset in bytes for each vertex.
  • shaderLocationUnsigned long, which specifies its location in the WGSL vertex shader.

2.3.2 VertexFormat GPUVertexFormat

enum GPUVertexFormat {
  "uint8x2",
  "uint8x4",
  "sint8x2",
  "sint8x4",
  "unorm8x2",
  "unorm8x4",
  "snorm8x2",
  "snorm8x4",
  "uint16x2",
  "uint16x4",
  "sint16x2",
  "sint16x4",
  "unorm16x2",
  "unorm16x4",
  "snorm16x2",
  "snorm16x4",
  "float16x2",
  "float16x4",
  "float32",
  "float32x2",
  "float32x3",
  "float32x4",
  "uint32",
  "uint32x2",
  "uint32x3",
  "uint32x4",
  "sint32",
  "sint32x2",
  "sint32x3",
  "sint32x4",
};
Copy the code

GPUVertexFormat enumeration of vertexFormat in WebIDL contains two parts of information according to the component type of the vertex (two, three, four dimensions) and the number type of each dimension. Some of these are abbreviated:

  • unorm= unsigned normalized u (unsigned) + normalized norm (normalized)
  • snorm= normalized s (signed) + normalized norm (normalized)
  • uint= unsigned integer
  • sint= signed integer
  • float= floating point number

Using the common “float32x3” (note that the x is a lowercase x) example, the value type for a Vertex property is three float32 numbers (not necessarily coordinates, but other Vertex attributes).

2.3.3 Code Examples

const renderPipeline = device.createRenderPipeline({
  / *... * /
  vertex: {
    module: device.createShaderModule({
      code: ` /* wgsl vertex shader code */ `,}).entryPoint: 'vertex_main'.buffers: [{arrayStride: 4 * 5.// One vertex takes 20 bytes
        attributes: [{// for Position VertexAttribute
            shaderLocation: 0.offset: 0.format: "float32x3" // The coordinate attributes of the vertices account for 12 bytes, with three FLOAT32 digits
          },
          {
            // for UV0 VertexAttribute
            shaderLocation: 1.offset: 3 * 4.format: "float32x2" // The vertex texture coordinates take up 8 bytes, with two Float32 digits}]}]}})Copy the code

Example above:

  • Use ofGPUVertexFormat“Float32x3”, “float32X2”;
  • A vertex data block (block) is 20 bytes long. That is, it takes a “stride” of 20 bytes to get from the head of one vertex data to the head of the next vertex data.
  • For a block of vertex data arranged in this VertexBuffer, there are twoGPUVertexAttribute, one is the coordinate, followed by the second is the 2d texture coordinate, so the texture coordinate data should start at the 12th byte of the vertex data block (0 to 11 bytes are the coordinate’s three float32 digits).

2.3.4 How do I verify the compliance of GPUVertexBufferLayout

Call a GPUVertexBufferLayout object a Layout if it meets the following criteria:

  • Layout. arrayStride ≤ in the device restriction listmaxVertexBufferArrayStrideAnd is a multiple of 4
  • For each element in layout.attributes, make it attrib:
    • If layout.arrayStride is 0, attrib.offset + sizeof(attrib.format) ≤ in the device restriction listmaxVertexBufferArrayStride
    • Otherwise, attrib.offset + sizeof(attrib.format) ≤ layout.arrayStride
    • Attrib. offset must be at least a multiple of min(4, sizeof(attrib.format))
    • Attrib. ShaderLocation < in the device restriction listmaxVertexAttributes
  • For each vertex attribute of the entry function in the vertex shader code, each GPUVertexAttribute element object in layout.attributes must satisfy:
    • Vertex property variable type in shader code, compared to attrib.format:
      • When attrib.format is one of “unorm”, “snorm”, or “float”, the variable type in shader code isvecN<f32>f32
      • When attrib.format is one of the “uint” types, the variable type in shader code isvecN<u32>u32
      • When attrib.format is one of the “sint” types, the variable type in shader code isvecN<i32>i32
    • The location property value of the vertex property in the shader should be equal to attrib. ShaderLocation

2.3.5 How do I verify the compliance of GPUVertexState, a vertex shader object

Let a GPUVertexState object be state if it meets the following criteria:

  • State.buffers. Length ≤ in the device limit listmaxVertexBuffers
  • Every element in state.buffers should be validated by the vertex cache layout object.
  • The sum of attributes for all elements of the state.buffers array ≤ in the device limit listmaxVertexAttributes
  • All GPUVertexAttribute vertex attribute objects must have unique shaderLocation

2.4 Chip coloring stage

Fragment shader stage Object, is that the value of the fragments in GPURenderPipelineDescriptor Object, is a JavaScript Object, To satisfy the GPUFragmentState type (including its parent type GPUProgrammableStage) :

dictionary GPUFragmentState: GPUProgrammableStage {
	required sequence<GPUColorTargetState> targets;
};
Copy the code
  • parametertargetsIs an element ofGPUColorTargetStateAn array of objects of type, is a mandatory parameter, see [2.6 Color Output stage](#2.6 color Output stage)

How to verify the compliance of the object in the chip coloring phase

Let the GPUFragmentState object at the fragment coloring stage be state, which meets the following requirements:

  • State. The targets. Length 8 or less
  • Targets for each element, called colorState:
    • ColorState. The format must beWebGPU Spec 24.1.1 Plain Color FormatsThe bidding forRENDER_ATTACHMENTA kind of
    • Colorstate. blend = undefined:
      • Colorstate. format is selected in WebGPU Spec 24.1.1 Plain color formats
      • Colorstate.blend. color, colorState.blend.alpha must be valid
    • ColorState writeMask 16 or less
    • The chip shader entry function has an output location matching state.targets. The type of the pipeline output object must correspond to colorstate. format, otherwise colorState.writeMask must be 0

Colorstate.blend. color, colorstate.blend. alpha Call it component. If component.operation is “min” or “Max”, then component.srcFactor and component.dstFactor must both be “one”.

For example,

const renderPipeline = device.createRenderPipeline({
  / *... * /
  fragment: {
    module: device.createShaderModule({
      code: `/* wgsl fragment shader source code */`,}).entryPoint: "fragment_main".targets: [{
      format: "bgra8unorm"}]}})Copy the code

2.5 Deep Template Testing Phase

Depth template testing phase Object is GPURenderPipelineDescriptor depthStencil field values in the Object, is a JavaScript Object, to meet GPUDepthStencilState type:

dictionary GPUDepthStencilState {
  required GPUTextureFormat format;

  boolean depthWriteEnabled = false;
  GPUCompareFunction depthCompare = "always";

  GPUStencilFaceState stencilFront = {};
  GPUStencilFaceState stencilBack = {};

  GPUStencilValue stencilReadMask = 0xFFFFFFFF;
  GPUStencilValue stencilWriteMask = 0xFFFFFFFF;

  GPUDepthBias depthBias = 0;
  float depthBiasSlopeScale = 0;
  float depthBiasClamp = 0;
};

dictionary GPUStencilFaceState {
  GPUCompareFunction compare = "always";
  GPUStencilOperation failOp = "keep";
  GPUStencilOperation depthFailOp = "keep";
  GPUStencilOperation passOp = "keep";
};

enum GPUStencilOperation {
  "keep",
  "zero",
  "replace",
  "invert",
  "increment-clamp",
  "decrement-clamp",
  "increment-wrap",
  "decrement-wrap"
};
Copy the code

Since there is no text introduction for this part of the official, it will be specially added when there are examples to study later.

How do I verify the compliance of objects in the deep template test phase

For the GPUDepthStencilState object in the deep template test phase, set it to state, as long as:

  • Format is one of the WebGPU Spec 24.1.2 Depth/ Stencil formats;
  • If state. DepthWriteEnabled is true, or if state. DepthCompare is not “always”, then state
  • If state.stencilFront or state.stencilBack is not the default, then state.format must be in template format

2.6 Color output stage

Object color output stage, GPUColorTargeState type, is GPURenderPipelineDescriptor object fragments the targets in the field the type of the array element in the field.

dictionary GPUColorTargetState {
  required GPUTextureFormat format;

  GPUBlendState blend;
  GPUColorWriteFlags writeMask = 0xF;  // GPUColorWrite.ALL
};
Copy the code

Subtypes:

dictionary GPUBlendState {
  required GPUBlendComponent color;
  required GPUBlendComponent alpha;
};

typedef [EnforceRange] unsigned long GPUColorWriteFlags;
[Exposed=(Window, DedicatedWorker)]
namespace GPUColorWrite {
  const GPUFlagsConstant RED   = 0x1;
  const GPUFlagsConstant GREEN = 0x2;
  const GPUFlagsConstant BLUE  = 0x4;
  const GPUFlagsConstant ALPHA = 0x8;
  const GPUFlagsConstant ALL   = 0xF;
};
Copy the code

Mixed mode

dictionary GPUBlendComponent {
  GPUBlendOperation operation = "add";
  GPUBlendFactor srcFactor = "one";
  GPUBlendFactor dstFactor = "zero";
};

enum GPUBlendFactor {
  "zero",
  "one",
  "src",
  "one-minus-src",
  "src-alpha",
  "one-minus-src-alpha",
  "dst",
  "one-minus-dst",
  "dst-alpha",
  "one-minus-dst-alpha",
  "src-alpha-saturated",
  "constant",
  "one-minus-constant"
};

enum GPUBlendOperation {
  "add",
  "subtract",
  "reverse-subtract",
  "min",
  "max"
};
Copy the code

Since there is no text introduction for this part of the official, it will be specially added when there are examples to study later.

2.7 Multiple sampling stage

Object color output stage, GPUMultisampleState type, is that the value of the multisample GPURenderPipelineDescriptor object.

dictionary GPUMultisampleState {
  GPUSize32 count = 1;
  GPUSampleMask mask = 0xFFFFFFFF;
  boolean alphaToCoverageEnabled = false;
};
Copy the code
  • parametercount, sampling times, can only be 1 or 4, the default is 1;
  • parametermask, sample mask value, type is unsigned long, default value is0xFFFFFFFF;
  • parameteralphaToCoverageEnabledThe default value is false.

The latter two values are described in the WebGPU Spec 21.3.10 sampling mask. A slice with a sampling mask value of 0 will not output color. It consists of three mask variables and. One shader-output-mask is determined by the built-in variable sample_mask in the chip shader, which is formed by the alphaToCoverageEnabled and mask values at this point. Here is a brief description.

How do I verify compliance of multiple sampling phase objects

AlphaToCoverageEnabled If true, count must be greater than 1.

The code for

const renderPipeline = device.createRenderPipeline({
  / *... * /
  multisample: {
    count: 4}})Copy the code

2.8 Comprehensive code examples

const renderPipeline = device.createRenderPipeline({
  layout: [/* GPURenderPipelineLayout[] */].vertex: { /* {}: GPUVertexState */ },
  fragment: { /* {}: GPUVertexState */ },
  primitive: { /* {}: GPUPrimitiveState */ },
  multisample: { /* {}: GPUMultisampleState */}})Copy the code

3 Pipeline Calculation

Calculation pipeline, GPUComputePipeline type, is a pipeline that can be used in the calculation channel encoder GPUComputePassEncoder to complete a general calculation process.

The input and output of the compute pipeline are bound in a binding group and provided via GPUPipelineLayout (see the Resource Binding section).

It does not have specific output results like the render pipeline. Its results will be written to two resources in the binding group, buffer with type “storage” and storageTexture with type “write-only”.

There is only one stage for calculating the pipeline: calculating the shader.

Here is the interface definition for calculating pipelines:

[Exposed=(Window, DedicatedWorker), SecureContext]
interface GPUComputePipeline {
};
GPUComputePipeline includes GPUObjectBase;
GPUComputePipeline includes GPUPipelineBase;
Copy the code

Calculate the pipeline creation

The device object createComputePipeline can create a computational pipeline, it needs a GPUComputePipelineDescriptor types of JavaScript object as parameters.

dictionary GPUComputePipelineDescriptor : GPUPipelineDescriptorBase {
	required GPUProgrammableStage compute;
};
Copy the code

GPUComputePipelineDescriptor type inherits from GPUPipelineDescriptorBase type, means also to follow the parent type field rules.

Can also create calculation through the device object createComputePipelineAsync method of asynchronous pipeline, grammar and asynchronous create rendering pipeline, parameter and synchronization to create calculation pipeline.

How to validate GPUComputePipelineDescriptor parameter object compliance

Make GPUComputePipelineDescriptor object called descriptor.

  • Descriptor. Layout and device object must be valid
  • Verify that the programmable properties of the computed coloring phase object are not problematic
  • The size of the workgroup used to compute the shader in Descriptor.com pute should not be greater than that in the device object restricted listmaxComputeWorkgroupStorageSizeValue, which is used by each workgroup not greater than the device object restriction listmaxComputeInvocationsPerWorkgroupValue;
  • Descriptor.com pute to compute the shaderworkgroup_sizeNone of the three component values of a feature must be greater than those in the device object restriction listmaxComputeWorkgroupSizeX,maxComputeWorkgroupSizeY,maxComputeWorkgroupSizeZThe value of the;

If all of the above passes, a correct calculation pipeline object is returned.

The code for

const computePipeline = device.createComputePipeline({
  compute: {
    module: device.createShaderModule({
      code: `/* wgsl compute shader source code */`
    }),
    entryPoint: "main",}})Copy the code

4.

This part is quite long, and I haven’t practiced some of it yet, so it’s quite difficult to study and translate.

But when you boil it down to the backbone, there are only four things:

  • What are pipelines, what are the inputs and outputs, how are they created, what types of pipelines are there
  • The pipeline is made up of phases, three of which (vertex coloring, slice coloring, and general computation) are programmable phases
  • The wiring rules between the binding group layout, vertex cache layout and the pit bits of the shader code that accompany the pipeline
  • Very important concepts in the vertex shader phase: VertexBuffer and vertex properties

A pipeline is the smallest unit in a WebGPU that performs a complete computation (or rendering, or general-purpose computation), with its own inputs and outputs.

You can pass different Vertexbuffers and resource binding groups to the channel encoder in the next article, but the channel encoder does more than just set data to the pipeline, as we’ll see below.