p3nGu1nZz commited on
Commit
d13e19f
1 Parent(s): 551d60d

added pipeline and state classes

Browse files
Files changed (3) hide show
  1. index.js +50 -84
  2. wgpu-pipeline.js +40 -0
  3. wgpu-state.js +19 -0
index.js CHANGED
@@ -2,51 +2,44 @@ import { mat4 } from 'https://webgpufundamentals.org/3rdparty/wgpu-matrix.module
2
  import { fetchShaderCode, generateGlyphTextureAtlas } from './utility.js';
3
  import { config } from './config.js';
4
  import { CANVAS, CTX, COLORS, RENDER_PASS_DESCRIPTOR } from './constants.js';
 
 
5
 
6
  const canvas = document.querySelector('canvas');
7
  const context = canvas.getContext('webgpu');
8
  const presentationFormat = navigator.gpu.getPreferredCanvasFormat();
9
- let device;
10
- let pipeline;
11
- let vertexBuffer;
12
- let indexBuffer;
13
- let uniformBuffer;
14
- let texture;
15
- let sampler;
16
- let bindGroup;
17
 
18
  async function main() {
19
  const adapter = await navigator.gpu?.requestAdapter();
20
- device = await adapter?.requestDevice();
21
- if (!device) {
22
  alert('need a browser that supports WebGPU');
23
  return;
24
  }
25
 
26
  context.configure({
27
- device,
28
  format: presentationFormat,
29
  });
30
 
31
  const shaderCode = await fetchShaderCode('shaders.wgsl');
32
- const module = device.createShaderModule({
33
- label: 'textured quad shaders',
34
- code: shaderCode,
35
- });
36
 
37
  const glyphCanvas = generateGlyphTextureAtlas(CANVAS, CTX, config);
38
  document.body.appendChild(glyphCanvas);
39
  glyphCanvas.style.backgroundColor = '#222';
40
 
41
- const vertexSize = config.floatsPerVertex * 4;
42
  const vertexBufferSize = config.maxGlyphs * config.vertsPerGlyph * vertexSize;
43
- vertexBuffer = device.createBuffer({
44
  label: 'vertices',
45
  size: vertexBufferSize,
46
  usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
47
  });
48
 
49
- indexBuffer = device.createBuffer({
50
  label: 'indices',
51
  size: config.maxGlyphs * config.vertsPerGlyph * 4,
52
  usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST,
@@ -56,89 +49,62 @@ async function main() {
56
  const ndx = Math.floor(i / 6) * 4;
57
  return (i % 6 < 3 ? [ndx, ndx + 1, ndx + 2] : [ndx + 2, ndx + 1, ndx + 3])[i % 3];
58
  });
59
- device.queue.writeBuffer(indexBuffer, 0, new Uint32Array(indices));
60
 
61
  const { vertexData, numGlyphs, width, height } = generateGlyphVerticesForText('Hello\nworld!\nText in\nWebGPU!', COLORS, glyphCanvas);
62
- device.queue.writeBuffer(vertexBuffer, 0, vertexData);
63
-
64
- pipeline = device.createRenderPipeline({
65
- label: 'textured quad pipeline',
66
- layout: 'auto',
67
- vertex: {
68
- module,
69
- entryPoint: 'vs',
70
- buffers: [
71
- {
72
- arrayStride: vertexSize,
73
- attributes: [
74
- { shaderLocation: 0, offset: 0, format: 'float32x2' }, // pos
75
- { shaderLocation: 1, offset: 8, format: 'float32x2' }, // tex
76
- { shaderLocation: 2, offset: 16, format: 'float32x4' } // col
77
- ],
78
- },
79
- ],
80
- },
81
- fragment: {
82
- module,
83
- entryPoint: 'fs',
84
- targets: [{
85
- format: presentationFormat,
86
- blend: {
87
- color: { srcFactor: 'one', dstFactor: 'one-minus-src-alpha', operation: 'add' },
88
- alpha: { srcFactor: 'one', dstFactor: 'one-minus-src-alpha', operation: 'add' }
89
- },
90
- }],
91
- },
92
- });
93
 
94
- texture = createTextureFromSource(device, glyphCanvas, { mips: true });
95
- sampler = device.createSampler({
96
  minFilter: 'linear',
97
  magFilter: 'linear'
98
  });
99
 
100
- uniformBuffer = device.createBuffer({
101
  label: 'uniforms for quad',
102
  size: config.uniformBufferSize,
103
  usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
104
  });
105
 
106
- const uniformValues = new Float32Array(config.uniformBufferSize / 4);
107
- const matrix = uniformValues.subarray(0, 16);
108
 
109
- bindGroup = device.createBindGroup({
110
- layout: pipeline.getBindGroupLayout(0),
111
  entries: [
112
- { binding: 0, resource: sampler },
113
- { binding: 1, resource: texture.createView() },
114
- { binding: 2, resource: { buffer: uniformBuffer } },
115
  ],
116
  });
117
 
118
- function render(time, context, pipeline, uniformBuffer, uniformValues, bindGroup, vertexBuffer, indexBuffer, RENDER_PASS_DESCRIPTOR) {
119
- time *= config.time.phase;
120
- const fov = 60 * Math.PI / 180;
121
- const aspect = canvas.clientWidth / canvas.clientHeight;
122
- const projectionMatrix = mat4.perspective(fov, aspect, config.render.zNear, config.render.zFar);
123
- const viewMatrix = mat4.lookAt([0, 0, 5], [0, 0, 0], [0, 1, 0]);
124
- const viewProjectionMatrix = mat4.multiply(projectionMatrix, viewMatrix);
125
- RENDER_PASS_DESCRIPTOR.colorAttachments[0].view = context.getCurrentTexture().createView();
126
- const encoder = device.createCommandEncoder();
127
- const pass = encoder.beginRenderPass(RENDER_PASS_DESCRIPTOR);
128
- pass.setPipeline(pipeline);
129
- mat4.rotateY(viewProjectionMatrix, time, matrix);
130
- mat4.translate(matrix, [-width / 2, -height / 2, 0], matrix);
131
- device.queue.writeBuffer(uniformBuffer, 0, uniformValues);
132
- pass.setBindGroup(0, bindGroup);
133
- pass.setVertexBuffer(0, vertexBuffer);
134
- pass.setIndexBuffer(indexBuffer, 'uint32');
135
- pass.drawIndexed(numGlyphs * 6);
136
- pass.end();
137
- device.queue.submit([encoder.finish()]);
138
- requestAnimationFrame((time) => render(time, context, pipeline, uniformBuffer, uniformValues, bindGroup, vertexBuffer, indexBuffer, RENDER_PASS_DESCRIPTOR));
139
- }
140
-
141
- requestAnimationFrame((time) => render(time, context, pipeline, uniformBuffer, uniformValues, bindGroup, vertexBuffer, indexBuffer, RENDER_PASS_DESCRIPTOR));
 
 
 
 
142
  }
143
 
144
  function generateGlyphVerticesForText(s, COLORS, glyphCanvas) {
 
2
  import { fetchShaderCode, generateGlyphTextureAtlas } from './utility.js';
3
  import { config } from './config.js';
4
  import { CANVAS, CTX, COLORS, RENDER_PASS_DESCRIPTOR } from './constants.js';
5
+ import { createPipeline } from './wgpu-pipeline.js';
6
+ import { createState } from './wgpu-state.js';
7
 
8
  const canvas = document.querySelector('canvas');
9
  const context = canvas.getContext('webgpu');
10
  const presentationFormat = navigator.gpu.getPreferredCanvasFormat();
11
+ const state = createState(config);
 
 
 
 
 
 
 
12
 
13
  async function main() {
14
  const adapter = await navigator.gpu?.requestAdapter();
15
+ state.device = await adapter?.requestDevice();
16
+ if (!state.device) {
17
  alert('need a browser that supports WebGPU');
18
  return;
19
  }
20
 
21
  context.configure({
22
+ device: state.device,
23
  format: presentationFormat,
24
  });
25
 
26
  const shaderCode = await fetchShaderCode('shaders.wgsl');
27
+ const vertexSize = config.floatsPerVertex * 4;
28
+
29
+ state.pipeline = await createPipeline(state.device, presentationFormat, vertexSize, shaderCode);
 
30
 
31
  const glyphCanvas = generateGlyphTextureAtlas(CANVAS, CTX, config);
32
  document.body.appendChild(glyphCanvas);
33
  glyphCanvas.style.backgroundColor = '#222';
34
 
 
35
  const vertexBufferSize = config.maxGlyphs * config.vertsPerGlyph * vertexSize;
36
+ state.vertexBuffer = state.device.createBuffer({
37
  label: 'vertices',
38
  size: vertexBufferSize,
39
  usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
40
  });
41
 
42
+ state.indexBuffer = state.device.createBuffer({
43
  label: 'indices',
44
  size: config.maxGlyphs * config.vertsPerGlyph * 4,
45
  usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST,
 
49
  const ndx = Math.floor(i / 6) * 4;
50
  return (i % 6 < 3 ? [ndx, ndx + 1, ndx + 2] : [ndx + 2, ndx + 1, ndx + 3])[i % 3];
51
  });
52
+ state.device.queue.writeBuffer(state.indexBuffer, 0, new Uint32Array(indices));
53
 
54
  const { vertexData, numGlyphs, width, height } = generateGlyphVerticesForText('Hello\nworld!\nText in\nWebGPU!', COLORS, glyphCanvas);
55
+ state.device.queue.writeBuffer(state.vertexBuffer, 0, vertexData);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56
 
57
+ state.texture = createTextureFromSource(state.device, glyphCanvas, { mips: true });
58
+ state.sampler = state.device.createSampler({
59
  minFilter: 'linear',
60
  magFilter: 'linear'
61
  });
62
 
63
+ state.uniformBuffer = state.device.createBuffer({
64
  label: 'uniforms for quad',
65
  size: config.uniformBufferSize,
66
  usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
67
  });
68
 
69
+ state.matrix = state.uniformValues.subarray(0, 16);
 
70
 
71
+ state.bindGroup = state.device.createBindGroup({
72
+ layout: state.pipeline.getBindGroupLayout(0),
73
  entries: [
74
+ { binding: 0, resource: state.sampler },
75
+ { binding: 1, resource: state.texture.createView() },
76
+ { binding: 2, resource: { buffer: state.uniformBuffer } },
77
  ],
78
  });
79
 
80
+ state.numGlyphs = numGlyphs;
81
+ state.width = width;
82
+ state.height = height;
83
+
84
+ requestAnimationFrame((time) => render(time, context, state, RENDER_PASS_DESCRIPTOR));
85
+ }
86
+
87
+ function render(time, context, state, RENDER_PASS_DESCRIPTOR) {
88
+ time *= config.time.phase;
89
+ const fov = 60 * Math.PI / 180;
90
+ const aspect = canvas.clientWidth / canvas.clientHeight;
91
+ const projectionMatrix = mat4.perspective(fov, aspect, config.render.zNear, config.render.zFar);
92
+ const viewMatrix = mat4.lookAt([0, 0, 5], [0, 0, 0], [0, 1, 0]);
93
+ const viewProjectionMatrix = mat4.multiply(projectionMatrix, viewMatrix);
94
+ RENDER_PASS_DESCRIPTOR.colorAttachments[0].view = context.getCurrentTexture().createView();
95
+ const encoder = state.device.createCommandEncoder();
96
+ const pass = encoder.beginRenderPass(RENDER_PASS_DESCRIPTOR);
97
+ pass.setPipeline(state.pipeline);
98
+ mat4.rotateY(viewProjectionMatrix, time, state.matrix);
99
+ mat4.translate(state.matrix, [-state.width / 2, -state.height / 2, 0], state.matrix);
100
+ state.device.queue.writeBuffer(state.uniformBuffer, 0, state.uniformValues);
101
+ pass.setBindGroup(0, state.bindGroup);
102
+ pass.setVertexBuffer(0, state.vertexBuffer);
103
+ pass.setIndexBuffer(state.indexBuffer, 'uint32');
104
+ pass.drawIndexed(state.numGlyphs * 6);
105
+ pass.end();
106
+ state.device.queue.submit([encoder.finish()]);
107
+ requestAnimationFrame((t) => render(t, context, state, RENDER_PASS_DESCRIPTOR));
108
  }
109
 
110
  function generateGlyphVerticesForText(s, COLORS, glyphCanvas) {
wgpu-pipeline.js ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // wgpu-pipeline.js
2
+
3
+ export async function createPipeline(device, presentationFormat, vertexSize, shaderCode) {
4
+ const module = device.createShaderModule({
5
+ label: 'textured quad shaders',
6
+ code: shaderCode,
7
+ });
8
+
9
+ const pipeline = device.createRenderPipeline({
10
+ label: 'textured quad pipeline',
11
+ layout: 'auto',
12
+ vertex: {
13
+ module,
14
+ entryPoint: 'vs',
15
+ buffers: [
16
+ {
17
+ arrayStride: vertexSize,
18
+ attributes: [
19
+ { shaderLocation: 0, offset: 0, format: 'float32x2' }, // pos
20
+ { shaderLocation: 1, offset: 8, format: 'float32x2' }, // tex
21
+ { shaderLocation: 2, offset: 16, format: 'float32x4' } // col
22
+ ],
23
+ },
24
+ ],
25
+ },
26
+ fragment: {
27
+ module,
28
+ entryPoint: 'fs',
29
+ targets: [{
30
+ format: presentationFormat,
31
+ blend: {
32
+ color: { srcFactor: 'one', dstFactor: 'one-minus-src-alpha', operation: 'add' },
33
+ alpha: { srcFactor: 'one', dstFactor: 'one-minus-src-alpha', operation: 'add' }
34
+ },
35
+ }],
36
+ },
37
+ });
38
+
39
+ return pipeline;
40
+ }
wgpu-state.js ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // wgpu-state.js
2
+
3
+ export function createState(config) {
4
+ return {
5
+ device: null,
6
+ pipeline: null,
7
+ vertexBuffer: null,
8
+ indexBuffer: null,
9
+ uniformBuffer: null,
10
+ texture: null,
11
+ sampler: null,
12
+ bindGroup: null,
13
+ uniformValues: new Float32Array(config.uniformBufferSize / 4),
14
+ matrix: null,
15
+ numGlyphs: 0,
16
+ width: 0,
17
+ height: 0
18
+ };
19
+ }