p3nGu1nZz commited on
Commit
78c9607
·
1 Parent(s): 57af44f

consolidation

Browse files
Files changed (1) hide show
  1. index.html +48 -120
index.html CHANGED
@@ -2,20 +2,15 @@
2
  <html lang="en">
3
 
4
  <head>
5
- <meta charset="UTF-8">
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
- <title>WebGPU Text Rendering</title>
8
  </head>
9
 
10
  <body>
11
  <canvas></canvas>
12
  <script type="module">
13
- // WebGPU Simple Textured Quad - Import Canvas
14
- // from https://webgpufundamentals.org/webgpu/webgpu-simple-textured-quad-import-canvas.html
15
-
16
-
17
  import { mat4 } from 'https://webgpufundamentals.org/3rdparty/wgpu-matrix.module.js';
18
-
19
  const glyphWidth = 32;
20
  const glyphHeight = 40;
21
  const glyphsAcrossTexture = 16;
@@ -28,16 +23,13 @@
28
  ctx.textBaseline = 'middle';
29
  ctx.textAlign = 'center';
30
  ctx.fillStyle = 'white';
31
-
32
  for (let c = 33, x = 0, y = 0; c < 128; ++c) {
33
  ctx.fillText(String.fromCodePoint(c), x + glyphWidth / 2, y + glyphHeight / 2);
34
  x = (x + glyphWidth) % canvas.width;
35
  if (x === 0) y += glyphHeight;
36
  }
37
-
38
  return canvas;
39
  }
40
-
41
  async function main() {
42
  const adapter = await navigator.gpu?.requestAdapter();
43
  const device = await adapter?.requestDevice();
@@ -45,8 +37,6 @@
45
  fail('need a browser that supports WebGPU');
46
  return;
47
  }
48
-
49
- // Get a WebGPU context from the canvas and configure it
50
  const canvas = document.querySelector('canvas');
51
  const context = canvas.getContext('webgpu');
52
  const presentationFormat = navigator.gpu.getPreferredCanvasFormat();
@@ -54,7 +44,6 @@
54
  device,
55
  format: presentationFormat,
56
  });
57
-
58
  const module = device.createShaderModule({
59
  label: 'our hardcoded textured quad shaders',
60
  code: `
@@ -92,15 +81,12 @@
92
  }
93
  `,
94
  });
95
-
96
  const glyphCanvas = genreateGlyphTextureAtlas();
97
- // so we can see it
98
  document.body.appendChild(glyphCanvas);
99
  glyphCanvas.style.backgroundColor = '#222';
100
-
101
  const maxGlyphs = 100;
102
- const floatsPerVertex = 2 + 2 + 4; // 2(pos) + 2(texcoord) + 4(color)
103
- const vertexSize = floatsPerVertex * 4; // 4 bytes each float
104
  const vertsPerGlyph = 6;
105
  const vertexBufferSize = maxGlyphs * vertsPerGlyph * vertexSize;
106
  const vertexBuffer = device.createBuffer({
@@ -113,7 +99,6 @@
113
  size: maxGlyphs * vertsPerGlyph * 4,
114
  usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST,
115
  });
116
- // pre fill index buffer with quad indices
117
  {
118
  const indices = [];
119
  for (let i = 0; i < maxGlyphs; ++i) {
@@ -122,59 +107,54 @@
122
  }
123
  device.queue.writeBuffer(indexBuffer, 0, new Uint32Array(indices));
124
  }
125
-
126
  function generateGlyphVerticesForText(s, colors = [[1, 1, 1, 1]]) {
127
  const vertexData = new Float32Array(maxGlyphs * floatsPerVertex * vertsPerGlyph);
128
  const glyphUVWidth = glyphWidth / glyphCanvas.width;
129
  const glyphUVHeight = glyphHeight / glyphCanvas.height;
130
  let offset = 0, x0 = 0, y0 = 0, x1 = 1, y1 = 1, width = 0;
131
  let colorNdx = 0;
132
-
133
- const addVertex = (x, y, u, v, r, g, b, a) => {
134
- vertexData.set([x, y, u, v, r, g, b, a], offset);
135
  offset += 8;
136
  };
137
-
138
  for (let i = 0; i < s.length; ++i) {
139
  const c = s.charCodeAt(i);
140
  if (c >= 33) {
141
- const glyphX = (c - 33) % glyphsAcrossTexture;
142
- const glyphY = Math.floor((c - 33) / glyphsAcrossTexture);
143
- const u0 = (glyphX * glyphWidth) / glyphCanvas.width;
144
- const v0 = ((glyphY + 1) * glyphHeight) / glyphCanvas.height;
 
145
  const u1 = u0 + glyphUVWidth;
146
- const v1 = v0 - glyphUVHeight;
147
  width = Math.max(x1, width);
148
 
149
- addVertex(x0, y0, u0, v0, ...colors[colorNdx]);
150
- addVertex(x1, y0, u1, v0, ...colors[colorNdx]);
151
- addVertex(x0, y1, u0, v1, ...colors[colorNdx]);
152
- addVertex(x1, y1, u1, v1, ...colors[colorNdx]);
153
  } else {
154
  colorNdx = (colorNdx + 1) % colors.length;
155
- if (c === 10) {
156
  x0 = 0; x1 = 1; y0--; y1 = y0 + 1;
157
  continue;
158
  }
159
  }
160
  x0 += 0.55; x1 = x0 + 1;
161
  }
162
-
163
  return { vertexData, numGlyphs: offset / floatsPerVertex, width, height: y1 };
164
  }
165
-
166
- const { vertexData, numGlyphs, width, height } = generateGlyphVerticesForText(
167
- 'Hello\nworld!\nText in\nWebGPU!', [
168
  [1, 1, 0, 1],
169
  [0, 1, 1, 1],
170
  [1, 0, 1, 1],
171
  [1, 0, 0, 1],
172
- [0, .5, 1, 1],
173
- ]);
 
174
  device.queue.writeBuffer(vertexBuffer, 0, vertexData);
175
-
176
  const pipeline = device.createRenderPipeline({
177
- label: 'hardcoded textured quad pipeline',
178
  layout: 'auto',
179
  vertex: {
180
  module,
@@ -185,7 +165,7 @@
185
  attributes: [
186
  { shaderLocation: 0, offset: 0, format: 'float32x2' }, // position
187
  { shaderLocation: 1, offset: 8, format: 'float32x2' }, // texcoord
188
- { shaderLocation: 2, offset: 16, format: 'float32x4' }, // color
189
  ],
190
  },
191
  ],
@@ -193,66 +173,44 @@
193
  fragment: {
194
  module,
195
  entryPoint: 'fs',
196
- targets: [
197
- {
198
- format: presentationFormat,
199
- blend: {
200
- color: {
201
- srcFactor: 'one',
202
- dstFactor: 'one-minus-src-alpha',
203
- operation: 'add',
204
- },
205
- alpha: {
206
- srcFactor: 'one',
207
- dstFactor: 'one-minus-src-alpha',
208
- operation: 'add',
209
- },
210
- },
211
  },
212
- ],
213
  },
214
  });
215
-
216
  function copySourceToTexture(device, texture, source, { flipY } = {}) {
217
  device.queue.copyExternalImageToTexture(
218
- { source, flipY, },
219
  { texture, premultipliedAlpha: true },
220
- { width: source.width, height: source.height },
221
  );
222
  }
223
-
224
  function createTextureFromSource(device, source, options = {}) {
225
  const texture = device.createTexture({
226
  format: 'rgba8unorm',
227
  size: [source.width, source.height],
228
- usage: GPUTextureUsage.TEXTURE_BINDING |
229
- GPUTextureUsage.COPY_DST |
230
- GPUTextureUsage.RENDER_ATTACHMENT,
231
  });
232
  copySourceToTexture(device, texture, source, options);
233
  return texture;
234
  }
235
-
236
  const texture = createTextureFromSource(device, glyphCanvas, { mips: true });
237
  const sampler = device.createSampler({
238
  minFilter: 'linear',
239
- magFilter: 'linear',
240
  });
241
-
242
- // create a buffer for the uniform values
243
- const uniformBufferSize =
244
- 16 * 4; // matrix is 16 32bit floats (4bytes each)
245
  const uniformBuffer = device.createBuffer({
246
  label: 'uniforms for quad',
247
  size: uniformBufferSize,
248
- usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
249
  });
250
-
251
- // create a typedarray to hold the values for the uniforms in JavaScript
252
- const kMatrixOffset = 0;
253
  const uniformValues = new Float32Array(uniformBufferSize / 4);
254
- const matrix = uniformValues.subarray(kMatrixOffset, 16);
255
-
256
  const bindGroup = device.createBindGroup({
257
  layout: pipeline.getBindGroupLayout(0),
258
  entries: [
@@ -261,73 +219,43 @@
261
  { binding: 2, resource: { buffer: uniformBuffer } },
262
  ],
263
  });
264
-
265
  const renderPassDescriptor = {
266
- label: 'our basic canvas renderPass',
267
- colorAttachments: [
268
- {
269
- // view: <- to be filled out when we render
270
- clearValue: [0.3, 0.3, 0.3, 1],
271
- loadOp: 'clear',
272
- storeOp: 'store',
273
- },
274
- ],
275
  };
276
-
277
  function render(time) {
278
  time *= 0.001;
279
-
280
- const fov = 60 * Math.PI / 180; // 60 degrees in radians
281
  const aspect = canvas.clientWidth / canvas.clientHeight;
282
- const zNear = 0.001;
283
- const zFar = 50;
284
  const projectionMatrix = mat4.perspective(fov, aspect, zNear, zFar);
285
-
286
- const cameraPosition = [0, 0, 5];
287
- const up = [0, 1, 0];
288
- const target = [0, 0, 0];
289
- const viewMatrix = mat4.lookAt(cameraPosition, target, up);
290
  const viewProjectionMatrix = mat4.multiply(projectionMatrix, viewMatrix);
291
-
292
- // Get the current texture from the canvas context and
293
- // set it as the texture to render to.
294
- renderPassDescriptor.colorAttachments[0].view =
295
- context.getCurrentTexture().createView();
296
-
297
- const encoder = device.createCommandEncoder({
298
- label: 'render quad encoder',
299
- });
300
  const pass = encoder.beginRenderPass(renderPassDescriptor);
301
  pass.setPipeline(pipeline);
302
-
303
  mat4.rotateY(viewProjectionMatrix, time, matrix);
304
  mat4.translate(matrix, [-width / 2, -height / 2, 0], matrix);
305
-
306
- // copy the values from JavaScript to the GPU
307
  device.queue.writeBuffer(uniformBuffer, 0, uniformValues);
308
-
309
  pass.setBindGroup(0, bindGroup);
310
  pass.setVertexBuffer(0, vertexBuffer);
311
  pass.setIndexBuffer(indexBuffer, 'uint32');
312
  pass.drawIndexed(numGlyphs * 6);
313
-
314
  pass.end();
315
-
316
- const commandBuffer = encoder.finish();
317
- device.queue.submit([commandBuffer]);
318
-
319
  requestAnimationFrame(render);
320
  }
321
  requestAnimationFrame(render);
322
  }
323
-
324
  function fail(msg) {
325
- // eslint-disable-next-line no-alert
326
  alert(msg);
327
  }
328
-
329
  main();
330
-
331
  </script>
332
  </body>
333
 
 
2
  <html lang="en">
3
 
4
  <head>
5
+ <meta charset="utf-8">
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>Plasma-Arc: WebGPU Experiment</title>
8
  </head>
9
 
10
  <body>
11
  <canvas></canvas>
12
  <script type="module">
 
 
 
 
13
  import { mat4 } from 'https://webgpufundamentals.org/3rdparty/wgpu-matrix.module.js';
 
14
  const glyphWidth = 32;
15
  const glyphHeight = 40;
16
  const glyphsAcrossTexture = 16;
 
23
  ctx.textBaseline = 'middle';
24
  ctx.textAlign = 'center';
25
  ctx.fillStyle = 'white';
 
26
  for (let c = 33, x = 0, y = 0; c < 128; ++c) {
27
  ctx.fillText(String.fromCodePoint(c), x + glyphWidth / 2, y + glyphHeight / 2);
28
  x = (x + glyphWidth) % canvas.width;
29
  if (x === 0) y += glyphHeight;
30
  }
 
31
  return canvas;
32
  }
 
33
  async function main() {
34
  const adapter = await navigator.gpu?.requestAdapter();
35
  const device = await adapter?.requestDevice();
 
37
  fail('need a browser that supports WebGPU');
38
  return;
39
  }
 
 
40
  const canvas = document.querySelector('canvas');
41
  const context = canvas.getContext('webgpu');
42
  const presentationFormat = navigator.gpu.getPreferredCanvasFormat();
 
44
  device,
45
  format: presentationFormat,
46
  });
 
47
  const module = device.createShaderModule({
48
  label: 'our hardcoded textured quad shaders',
49
  code: `
 
81
  }
82
  `,
83
  });
 
84
  const glyphCanvas = genreateGlyphTextureAtlas();
 
85
  document.body.appendChild(glyphCanvas);
86
  glyphCanvas.style.backgroundColor = '#222';
 
87
  const maxGlyphs = 100;
88
+ const floatsPerVertex = 2 + 2 + 4;
89
+ const vertexSize = floatsPerVertex * 4;
90
  const vertsPerGlyph = 6;
91
  const vertexBufferSize = maxGlyphs * vertsPerGlyph * vertexSize;
92
  const vertexBuffer = device.createBuffer({
 
99
  size: maxGlyphs * vertsPerGlyph * 4,
100
  usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST,
101
  });
 
102
  {
103
  const indices = [];
104
  for (let i = 0; i < maxGlyphs; ++i) {
 
107
  }
108
  device.queue.writeBuffer(indexBuffer, 0, new Uint32Array(indices));
109
  }
 
110
  function generateGlyphVerticesForText(s, colors = [[1, 1, 1, 1]]) {
111
  const vertexData = new Float32Array(maxGlyphs * floatsPerVertex * vertsPerGlyph);
112
  const glyphUVWidth = glyphWidth / glyphCanvas.width;
113
  const glyphUVHeight = glyphHeight / glyphCanvas.height;
114
  let offset = 0, x0 = 0, y0 = 0, x1 = 1, y1 = 1, width = 0;
115
  let colorNdx = 0;
116
+ const addVertex = (x, y, u, v, color) => {
117
+ vertexData.set([x, y, u, v, ...color], offset);
 
118
  offset += 8;
119
  };
 
120
  for (let i = 0; i < s.length; ++i) {
121
  const c = s.charCodeAt(i);
122
  if (c >= 33) {
123
+ const cIndex = c - 33;
124
+ const glyphX = cIndex % glyphsAcrossTexture;
125
+ const glyphY = Math.floor(cIndex / glyphsAcrossTexture);
126
+ const u0 = glyphX * glyphWidth / glyphCanvas.width;
127
+ const v1 = glyphY * glyphHeight / glyphCanvas.height;
128
  const u1 = u0 + glyphUVWidth;
129
+ const v0 = v1 + glyphUVHeight;
130
  width = Math.max(x1, width);
131
 
132
+ addVertex(x0, y0, u0, v0, colors[colorNdx]);
133
+ addVertex(x1, y0, u1, v0, colors[colorNdx]);
134
+ addVertex(x0, y1, u0, v1, colors[colorNdx]);
135
+ addVertex(x1, y1, u1, v1, colors[colorNdx]);
136
  } else {
137
  colorNdx = (colorNdx + 1) % colors.length;
138
+ if (c === 10) { // Newline character
139
  x0 = 0; x1 = 1; y0--; y1 = y0 + 1;
140
  continue;
141
  }
142
  }
143
  x0 += 0.55; x1 = x0 + 1;
144
  }
 
145
  return { vertexData, numGlyphs: offset / floatsPerVertex, width, height: y1 };
146
  }
147
+ const colors = [
 
 
148
  [1, 1, 0, 1],
149
  [0, 1, 1, 1],
150
  [1, 0, 1, 1],
151
  [1, 0, 0, 1],
152
+ [0, .5, 1, 1]
153
+ ];
154
+ const { vertexData, numGlyphs, width, height } = generateGlyphVerticesForText('Hello\nworld!\nText in\nWebGPU!', colors);
155
  device.queue.writeBuffer(vertexBuffer, 0, vertexData);
 
156
  const pipeline = device.createRenderPipeline({
157
+ label: 'textured quad pipeline',
158
  layout: 'auto',
159
  vertex: {
160
  module,
 
165
  attributes: [
166
  { shaderLocation: 0, offset: 0, format: 'float32x2' }, // position
167
  { shaderLocation: 1, offset: 8, format: 'float32x2' }, // texcoord
168
+ { shaderLocation: 2, offset: 16, format: 'float32x4' } // color
169
  ],
170
  },
171
  ],
 
173
  fragment: {
174
  module,
175
  entryPoint: 'fs',
176
+ targets: [{
177
+ format: presentationFormat,
178
+ blend: {
179
+ color: { srcFactor: 'one', dstFactor: 'one-minus-src-alpha', operation: 'add' },
180
+ alpha: { srcFactor: 'one', dstFactor: 'one-minus-src-alpha', operation: 'add' }
 
 
 
 
 
 
 
 
 
 
181
  },
182
+ }],
183
  },
184
  });
 
185
  function copySourceToTexture(device, texture, source, { flipY } = {}) {
186
  device.queue.copyExternalImageToTexture(
187
+ { source, flipY },
188
  { texture, premultipliedAlpha: true },
189
+ { width: source.width, height: source.height }
190
  );
191
  }
 
192
  function createTextureFromSource(device, source, options = {}) {
193
  const texture = device.createTexture({
194
  format: 'rgba8unorm',
195
  size: [source.width, source.height],
196
+ usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.RENDER_ATTACHMENT,
 
 
197
  });
198
  copySourceToTexture(device, texture, source, options);
199
  return texture;
200
  }
 
201
  const texture = createTextureFromSource(device, glyphCanvas, { mips: true });
202
  const sampler = device.createSampler({
203
  minFilter: 'linear',
204
+ magFilter: 'linear'
205
  });
206
+ const uniformBufferSize = 16 * 4; //f32
 
 
 
207
  const uniformBuffer = device.createBuffer({
208
  label: 'uniforms for quad',
209
  size: uniformBufferSize,
210
+ usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
211
  });
 
 
 
212
  const uniformValues = new Float32Array(uniformBufferSize / 4);
213
+ const matrix = uniformValues.subarray(0, 16);
 
214
  const bindGroup = device.createBindGroup({
215
  layout: pipeline.getBindGroupLayout(0),
216
  entries: [
 
219
  { binding: 2, resource: { buffer: uniformBuffer } },
220
  ],
221
  });
 
222
  const renderPassDescriptor = {
223
+ label: 'canvas render pass',
224
+ colorAttachments: [{
225
+ clearValue: [0.3, 0.3, 0.3, 1],
226
+ loadOp: 'clear',
227
+ storeOp: 'store',
228
+ }],
 
 
 
229
  };
 
230
  function render(time) {
231
  time *= 0.001;
232
+ const fov = 60 * Math.PI / 180;
 
233
  const aspect = canvas.clientWidth / canvas.clientHeight;
234
+ const zNear = 0.001, zFar = 50;
 
235
  const projectionMatrix = mat4.perspective(fov, aspect, zNear, zFar);
236
+ const viewMatrix = mat4.lookAt([0, 0, 5], [0, 0, 0], [0, 1, 0]);
 
 
 
 
237
  const viewProjectionMatrix = mat4.multiply(projectionMatrix, viewMatrix);
238
+ renderPassDescriptor.colorAttachments[0].view = context.getCurrentTexture().createView();
239
+ const encoder = device.createCommandEncoder();
 
 
 
 
 
 
 
240
  const pass = encoder.beginRenderPass(renderPassDescriptor);
241
  pass.setPipeline(pipeline);
 
242
  mat4.rotateY(viewProjectionMatrix, time, matrix);
243
  mat4.translate(matrix, [-width / 2, -height / 2, 0], matrix);
 
 
244
  device.queue.writeBuffer(uniformBuffer, 0, uniformValues);
 
245
  pass.setBindGroup(0, bindGroup);
246
  pass.setVertexBuffer(0, vertexBuffer);
247
  pass.setIndexBuffer(indexBuffer, 'uint32');
248
  pass.drawIndexed(numGlyphs * 6);
 
249
  pass.end();
250
+ device.queue.submit([encoder.finish()]);
 
 
 
251
  requestAnimationFrame(render);
252
  }
253
  requestAnimationFrame(render);
254
  }
 
255
  function fail(msg) {
 
256
  alert(msg);
257
  }
 
258
  main();
 
259
  </script>
260
  </body>
261