当前位置:网站首页>Webgl and webgpu comparison [4] - uniform

Webgl and webgpu comparison [4] - uniform

2022-06-25 15:56:00 Song of the four seasons

as everyone knows , stay GPU When running a programmable pipeline , Shaders run in parallel , Each shader entry function will be in GPU In parallel . Each shader charges a large piece of data in a unified format , reflect GPU The advantages of multi-core , Small cores can process data at the same time ; however , Some data is the same for every shader , The type of data is “uniform”, Also known as uniform value .

This article lists the original WebGL 1/2 Medium uniform Information , as well as WebGPU Medium uniform Information , There are some examples for reference , To compare the differences between them .

1. WebGL 1.0 Uniform

1.1. use WebGLUniformLocation Addressing

stay WebGL 1.0 in , Usually in JavaScript End save WebGLUniformLocation To pass... To the shader program uniform It's worth it .

Use gl.getUniformLocation() Method to get this location, There are several ways

  • full name :gl.getUniformLocation(program, 'u_someUniformVar')
  • component : Usually part of a vector , for example gl.getUniformLocation(program, 'u_someVec3[0]') Is to get the 0 Elements ( The type of element is vec3) Of location
  • Structural members :gl.getUniformLocation(program, 'u_someStruct.someMember')

The shader code corresponding to the above three cases :

//  full name 
uniform float u_someUniformVar;

//  component 
uniform vec3 u_someVec3[3]; //  Be careful , Here is  3  individual  vec3

//  Structural members 
struct SomeStruct {
  bool someMember;
};
uniform SomeStruct u_someStruct;  

There are three types of value transmission , Scalar / vector 、 matrix 、 Sample texture , See below .

1.2. Matrix assignment is done with uniformMatrix[234]fv

For matrices , Use gl.uniformMatrix[234]fv() Method to pass , among ,f representative float,v representative vector, That is, if the incoming parameter is a vector ( It's an array );

To pass a 4×4 The matrix of :

//  obtain  location( On initialization )
const matrixLocation = gl.getUniformLocation(program, "u_matrix")

//  Create or update the column main order transformation matrix ( When rendering )
const matrix = [/* ... */]

//  Transfer value ( When rendering )
gl.uniformMatrix4fv(matrixLocation, false, matrix)

1.3. Scalar and vector use uniform[1234][fi][v]

For ordinary scalars and vectors , Use gl.uniform[1234][fi][v]() Method to pass , among ,1、2、3、4 Represents the dimension of a scalar or vector (1 It's scalar ),f/i representative float or int,v representative vector( That is, the data you pass will be parsed into a vector array in the shader ).

give an example :

  • sentence 1,gl.uniform1fv(someFloatLocation, [4.5, 7.1])
  • sentence 2,gl.uniform4i(someIVec4Location, 5, 2, 1, 3)
  • sentence 3,gl.uniform4iv(someIVec4Location, [5, 2, 1, 3, 2, 12, 0, 6])
  • sentence 4,gl.uniform3f (someVec3Location, 7.1, -0.8, 2.1)

Above 4 The code in the shader corresponding to an assignment statement is :

//  sentence  1  Can be adapted  1~N  A floating point number 
//  When only flyer element arrays , You can directly declare  uniform float u_someFloat;
uniform float u_someFloat[2];

//  sentence  2  Fit one  ivec4
uniform ivec4 u_someIVec4;

//  sentence  3  adapter   1~N  individual  ivec4
//  When only flyer element arrays , You can directly declare  uniform float u_someIVec4;
uniform ivec4 u_someIVec4[2];

//  sentence  4  Fit one  vec3
uniform vec3 u_someVec3;

here we are WebGL 2.0, There will be some extensions in the component value type , Please refer to the relevant documents .

1.4. Transfer texture

In the vertex shader phase , You can sample textures using the texture coordinates of vertices :

attribute vec3 a_pos;
attribute vec2 a_uv;
uniform sampler2D u_texture;
varying vec4 v_color;

void main() {
  v_color = texture2D(u_texture, a_uv);
  gl_Position = a_pos; //  Suppose the vertices don't need to be transformed 
}

that , stay JavaScript End , have access to gl.uniform1i() To tell the shader which texture pit I just passed the texture to :

const texture = gl.createTexture()
const samplerLocation = gl.getUniformLocation(/* ... */)

// ...  Set texture data  ...

gl.activeTexture(gl[`TEXTURE${5}`]) //  tell  WebGL  Use the  5  The texture of a pit 
gl.bindTexture(gl.TEXTURE_2D, texture)

gl.uniform1i(samplerLocation, 5) //  Tell the shader to read the texture later  5  A pit reading 

2. WebGL 2.0 Uniform

2.1. Scalar / vector / Extension of matrix value transfer method

WebGL 2.0 Of Uniform The system supports matrices of non square matrix type , for example

const mat2x3 = [
  1, 2, 3,
  4, 5, 6,
]
gl.uniformMatrix2x3fv(loc, false, mat2x3)

The above method passes 4×3 Matrix .

And for single values and vectors , Additional methods for unsigned values are provided , by uniform[1234][fi][v] Turned into uniform[1234][f/ui][v], It's down there 8 A new method :

gl.uniform1ui(/* ... */) //  Transfer data to  1  individual  uint
gl.uniform2ui(/* ... */) //  Transfer data to  1  individual  uvec2
gl.uniform3ui(/* ... */) //  Transfer data to  1  individual  uvec3
gl.uniform4ui(/* ... */) //  Transfer data to  1  individual  uvec4

gl.uniform1uiv(/* ... */) //  Transfer data to  uint  Array 
gl.uniform2uiv(/* ... */) //  Transfer data to  uvec2  Array 
gl.uniform3uiv(/* ... */) //  Transfer data to  uvec3  Array 
gl.uniform4uiv(/* ... */) //  Transfer data to  uvec4  Array 

Corresponding GLSL300 Medium uniform by :

#version 300 es
#define N ? // N  It depends on your needs ,JavaScript  The number of passes should also match 
  
uniform uint u_someUint;
uniform uvec2 u_someUVec2;
uniform uvec3 u_someUVec3;
uniform uvec4 u_someUVec4;

uniform uint u_someUintArr[N];
uniform uvec2 u_someUVec2Arr[N];
uniform uvec3 u_someUVec3Arr[N];
uniform uvec4 u_someUVec4Arr[N];

What needs extra attention is ,uint/uvec234 These types are in higher versions of glsl Can be used , In other words, it is not downward compatible WebGL 1.0 And GLSL100.

However ,WebGL 2.0 It's not just these minor repairs , The most important thing is UBO 了 , Start now .

2.1. What is? UniformBlock And UniformBuffer The creation of

stay WebGL 1.0 When , Only one uniform value of any kind can be set at a time , If within a frame uniform There are more updates , about WebGL It's not a good thing for this state machine , It will bring extra CPU to GPU The delivery overhead at the end .

stay WebGL 2.0, Allow sending a bunch at a time uniform, This pile uniform The polymer of , It's called UniformBuffer, Specific to the code :

First, GLSL 300

uniform Light {
  highp vec3 lightWorldPos;
  mediump vec4 lightColor;
};

And then there was JavaScript

const lightUniformBlockBuffer = gl.createBuffer()
const lightUniformBlockData = new Float32Array([
  0, 10, 30, 0,    // vec3,  Light source location ,  in order to  8 Byte  Align and fill a tail  0
  1, 1, 1, 1,     // vec4,  The color of light 
])
gl.bindBuffer(gl.UNIFORM_BUFFER, lightUniformBlockBuffer);
gl.bufferData(gl.UNIFORM_BUFFER, lightUniformBlockData, gl.STATIC_DRAW);

gl.bindBufferBase(gl.UNIFORM_BUFFER, 0, lightUniformBlockBuffer)

Don't rush to ask why , Step by step .

First you see , stay GLSL300 Multiple... Are allowed to be declared using a block syntax similar to a structure Uniform Variable , The coordinates of the light source and the color of the light source are used here , Different precision and data types are used respectively (vec3、vec4).

And then , stay JavaScript End , You see the new method gl.bindBufferBase() To bind a WebGLBuffer To 0 The location , This lightUniformBlockBuffer It's actually a collection of two Uniform Variable UniformBufferObject (UBO), In the shader, that piece is named Light Curly bracket area of , It's called UniformBlock.

Actually , Create a UBO And create ordinary VBO It's the same , binding 、 The assignment operation is almost the same ( The first parameter is different ). It's just UBO It may be more necessary to consider numerical design , for example 8 Byte alignment, etc , The same data type is usually used when designing shaders uniform Put variables together , Optimize memory usage .

2.2. State binding

stay WebGL 2.0 in ,JavaScript The client allows you to put... In the shader program UniformBlock Position bound to a variable :

const viewUniformBufferIndex = 0;
const materialUniformBufferIndex = 1;
const modelUniformBufferIndex = 2;
const lightUniformBufferIndex = 3;
gl.uniformBlockBinding(prg, gl.getUniformBlockIndex(prg, 'View'), viewUniformBufferIndex);
gl.uniformBlockBinding(prg, gl.getUniformBlockIndex(prg, 'Model'), modelUniformBufferIndex);
gl.uniformBlockBinding(prg, gl.getUniformBlockIndex(prg, 'Material'), materialUniformBufferIndex);
gl.uniformBlockBinding(prg, gl.getUniformBlockIndex(prg, 'Light'), lightUniformBufferIndex);

here , It uses gl.getUniformBlockIndex() obtain UniformBlock Position in shader program , And what binds this position to your favorite number is gl.uniformBlockBinding() Method .

There's a benefit to this , You can artificially specify each in your program UniformBlock The order of , Then use these index To update different UBO.

//  Use different  UBO  to update  materialUniformBufferIndex (=1)  Point to the  UniformBlock
gl.bindBufferBase(gl.UNIFORM_BUFFER, 1, redMaterialUBO)
gl.bindBufferBase(gl.UNIFORM_BUFFER, 1, greenMaterialUBO)
gl.bindBufferBase(gl.UNIFORM_BUFFER, 1, blueMaterialUBO)

Of course ,WebGL 2.0 Yes Uniform There are other extensions , There are no more examples here .

bindBufferBase Is similar to enableVertexAttribArray, tell WebGL I'm going to use which pit .

2.3. In shader Uniform

Shader use GLSL300 Grammar can only be used UniformBlock and New data types , Besides, and GLSL100 Do not have what difference . Of course ,GLSL300 There are many new grammars , Here's just some information about Uniform To write .

About uint/uvec234 type , stay 2.1 There are already examples in this section , I won't repeat it here .

And about the UniformBlock, There is one more point to add , That's it “ name ” problem .

UniformBlock The grammar is as follows :

uniform <BlockType> {
  <BlockBody>
} ?<blockName>;

//  give an example : Named definition 
uniform Model {
  mat4 world;
  mat4 worldInverseTranspose;
} model;

//  give an example : Unnamed definition 
uniform Light {
  highp vec3 lightWorldPos;
  mediump vec4 lightColor;
};

If you use a named definition , Then visit Block Members within need to use its name 了 , for example model.worldmodel.worldInverseTranspose etc. .

A complete example is as follows :

#version 300 es
precision highp float;
precision highp int;

// uniform  Block layout control 
layout(std140, column_major) uniform;

//  Statement  uniform  block :Transform, Name it  transform  For the main program 
//  You can also not name , Use directly  mvpMatrix  that will do 
uniform Transform
{
  mat4 mvpMatrix;
} transform;

layout(location = 0) in vec2 pos;

void main() {
  gl_Position = transform.mvpMatrix * vec4(pos, 0.0, 1.0);
}

Be careful , Even to UniformBlock Name it transform, But the facade mvpMatrix It can't be compared with other Block The members in it have a total of ,transform There is no namespace .

Look again JavaScript:

//#region  Get... In shader program  uniform  Position and bind 
const uniformTransformLocation = gl.getUniformBlockIndex(program, 'Transform')
gl.uniformBlockBinding(program, uniformTransformLocation, 0)
//endregion

//#region  establish  ubo
const uniformTransformBuffer = gl.createBuffer()
//#endregion

//#region  Required to create matrix  ArrayBufferView, Column main order 
const transformsMatrix = new Float32Array([
  1.0, 0.0, 0.0, 0.0,
  0.0, 1.0, 0.0, 0.0,
  0.0, 0.0, 1.0, 0.0,
  0.0, 0.0, 0.0, 1.0
])
//#endregion

//#region  Transfer data to  WebGLBuffer
gl.bindBuffer(gl.UNIFORM_BUFFER, uniformTransformBuffer)
gl.bufferData(gl.UNIFORM_BUFFER, transformsMatrix, gl.DYNAMIC_DRAW);
gl.bindBuffer(gl.UNIFORM_BUFFER, null)
//#endregion

// ----------  When you need to draw  ----------
//#region  binding  ubo  To  0  On the index number  uniformLocation  For use by shaders 
gl.bindBufferBase(gl.UNIFORM_BUFFER, 0, uniformTransformBuffer)
// ...  Rendering 
// -------------

2.4. Transfer texture

Texture and WebGL 1.0 Agreement , however GLSL300 The texture function of has changed , Readers are requested to find out the information by themselves .

3. WebGPU Uniform

WebGPU There are three types of Uniform resources : Scalar / vector / matrix 、 texture 、 Sampler .

Each has its own container , The first unified use GPUBuffer, It's called UBO; The second and third uses GPUTexture and GPUSampler.

3.1. Creation and group transmission of three types of resources

The above three types of resources , Group them by , That is to say GPUBindGroup, I call it resource binding group , Then passed to the shader module (GPUShaderModule) All kinds of pipelines (GPURenderPipelineGPUComputePipeline).

Unified and easy to handle , Here to save space , Data transfer is no longer detailed , Focus on the code of their binding group :

const someUbo = device.createBuffer({ /*  Be careful  usage  Want to have  UNIFORM */ })
const texture = device.createTexture({ /*  Create a regular texture  */ })
const sampler = device.createSampler({ /*  Create a general sampler  */ })

//  The layout object relates the pipeline layout and the binding group itself 
const bindGroupLayout = device.createBindGroupLayout({
  entries: [
    {
      binding: 0, // <-  Binding in  0  Resource No 
      visibility: GPUShaderStage.FRAGMENT,
      sampler: {
        type: 'filtering'
      }
    },
    {
      binding: 1, // <-  Binding in  1  Resource No 
      visibility: GPUShaderStage.FRAGMENT,
      texture: {
        sampleType: 'float'
      }
    },
    {
      binding: 2,
      visibility: GPUShaderStage.FRAGMENT,
      buffer: {
        type: 'uniform'
      }
    }
  ]
})
const bindGroup = device.createBindGroup({
  layout: bindGroupLayout,
  entries: [
    {
      binding: 0,
      resource: sampler, // <-  Pass in the sampler object 
    },
    {
      binding: 1,
      resource: texture.createView() // <-  Pass in the view of the texture object 
    },
    {
      binding: 2,
      resource: {
        buffer: someUbo // <-  Pass in  UBO
      }
    }
  ]
})

//  pipeline 
const pipelineLayout = device.createPipelineLayout({
  bindGroupLayouts: [bindGroupLayout]
})
const renderingPipeline = device.createRenderPipeline({
  layout: pipelineLayout
  // ...  Other configuration 
})

// ... renderPass  Switch  pipeline  and  bindGroup  Drawing  ...

3.2. to update Uniform The meaning of binding groups

to update Uniform Resources are actually very simple .

If it is UBO, Generally, the lights modified at the front end will be updated 、 texture of material 、 Time frame parameters and matrix of single frame change, etc , Use device.queue.writeBuffer that will do :

device.queue.writeBuffer(
  someUbo, //  To whom 
  0, 
  buffer, //  Pass on  ArrayBuffer, That is, the new data in the current frame 
  byteOffset, //  Where to start 
  byteLength //  How long 
)
Use writeBuffer You can ensure that the original creation is used GPUBuffer, It is associated with the binding group 、 The binding relationship of pipeline is still ; No mapping 、 The way of demapping is to reduce CPU/GPU Two terminal communication cost

If it's texture , Then use Image copy operation Several methods in texture object update ;

Generally, the sampler and texture are not updated directly , Instead, switch different binding groups on the encoder to switch the resources required by the pipeline . Especially the texture , If the data is updated frequently ,CPU/GPU The cost of two terminal communication will increase .

Deferred Shading 、 Off screen drawing and other color attachments that need to be updated , In fact, you just need to create a new colorAttachments Object can realize “ The next frame drawn from the previous frame I can use ”, You don't have to go directly from CPU The memory then brushes the data into GPU in .

to update Uniform You need to change almost every frame 、 Reasonably group almost unchanged resources , Into different binding groups , In this way, you can update , Without putting the pipeline 、 Bind group reset once , Just switch on the channel encoder .

3.3. In shader Uniform

Not much is involved here WGSL grammar .

And UniformBlock similar , You need to specify the “ A piece of stuff ”,WGSL Directly used structures .

First , yes UBO:

// --  Vertex shader  --

//  Declare a struct type 
struct Uniforms {
  modelViewProjectionMatrix: mat4x4<f32>;
};

//  The declaration specifies its binding ID yes 0, Binding group serial number is 0
@binding(2)
@group(0)
var<uniform> myUniforms: Uniforms;

// ——  And then this  myUniforms  Variables can be called in functions.  ——

Then texture and sampler :

@group(0)
@binding(1)
var mySampler: sampler;

@group(0)
@binding(2)
var myTexture: texture_2d<f32>;

// ...  Texture sampling in the main function of the slice shader 
textureSample(myTexture, mySampler, fragUV);

4. conclusion

WebGL With 2 As a benchmark , It is associated with WebGPU comparison , No resource binding group , No sampler object ( The sampling parameters are set in another way ).

Compared with WebGPU Biography of descriptor The style of writing , Use one method after another to switch UniformBlock、 Textures and other resources may be missing , This is one of the characteristics of global state writing . Of course , The upper encapsulation library will help us shield these problems .

Compared with grammatical style , Actually WebGPU More improvements are these uniform At each frame update CPU To GPU The load problem of , It is encoded into instruction buffer by encoder in advance, and finally sent at one time , Compared with WebGL Sending one by one is better , In graphic rendering 、GPU Computing places like this , Many a little make a mickle , The performance is higher .

About WebGL 2.0 Of Uniform and GLSL300 I'm not very knowledgeable , If there is any mistake, please point out .

5. Reference material

原网站

版权声明
本文为[Song of the four seasons]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/02/202202190928459297.html