p3nGu1nZz commited on
Commit
6d53e86
1 Parent(s): b82d7fb

move code to index.js

Browse files
Files changed (3) hide show
  1. index.html +2 -202
  2. index.js +199 -0
  3. style.css +2 -0
index.html CHANGED
@@ -9,207 +9,7 @@
9
 
10
  <body>
11
  <canvas></canvas>
12
- <script type="module">
13
- import { mat4 } from 'https://webgpufundamentals.org/3rdparty/wgpu-matrix.module.js';
14
- import { fetchShaderCode, generateGlyphTextureAtlas } from './utility.js';
15
- import { CONFIG } from './config.js';
16
- import { CANVAS, CTX, COLORS, RENDER_PASS_DESCRIPTOR } from './constants.js';
17
-
18
- const canvas = document.querySelector('canvas');
19
- const context = canvas.getContext('webgpu');
20
- const presentationFormat = navigator.gpu.getPreferredCanvasFormat();
21
- let device;
22
- let pipeline;
23
- let vertexBuffer;
24
- let indexBuffer;
25
- let uniformBuffer;
26
- let texture;
27
- let sampler;
28
- let bindGroup;
29
-
30
- async function main() {
31
- const adapter = await navigator.gpu?.requestAdapter();
32
- device = await adapter?.requestDevice();
33
- if (!device) {
34
- alert('need a browser that supports WebGPU');
35
- return;
36
- }
37
-
38
- context.configure({
39
- device,
40
- format: presentationFormat,
41
- });
42
-
43
- const shaderCode = await fetchShaderCode('shaders.wgsl');
44
- const module = device.createShaderModule({
45
- label: 'textured quad shaders',
46
- code: shaderCode,
47
- });
48
-
49
- const glyphCanvas = generateGlyphTextureAtlas(CANVAS, CTX, CONFIG);
50
- document.body.appendChild(glyphCanvas);
51
- glyphCanvas.style.backgroundColor = '#222';
52
-
53
- const vertexSize = CONFIG.floatsPerVertex * 4;
54
- const vertexBufferSize = CONFIG.maxGlyphs * CONFIG.vertsPerGlyph * vertexSize;
55
- vertexBuffer = device.createBuffer({
56
- label: 'vertices',
57
- size: vertexBufferSize,
58
- usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
59
- });
60
-
61
- indexBuffer = device.createBuffer({
62
- label: 'indices',
63
- size: CONFIG.maxGlyphs * CONFIG.vertsPerGlyph * 4,
64
- usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST,
65
- });
66
-
67
- const indices = Array.from({ length: CONFIG.maxGlyphs * 6 }, (_, i) => {
68
- const ndx = Math.floor(i / 6) * 4;
69
- return (i % 6 < 3 ? [ndx, ndx + 1, ndx + 2] : [ndx + 2, ndx + 1, ndx + 3])[i % 3];
70
- });
71
- device.queue.writeBuffer(indexBuffer, 0, new Uint32Array(indices));
72
-
73
- const { vertexData, numGlyphs, width, height } = generateGlyphVerticesForText('Hello\nworld!\nText in\nWebGPU!', COLORS, glyphCanvas);
74
- device.queue.writeBuffer(vertexBuffer, 0, vertexData);
75
-
76
- pipeline = device.createRenderPipeline({
77
- label: 'textured quad pipeline',
78
- layout: 'auto',
79
- vertex: {
80
- module,
81
- entryPoint: 'vs',
82
- buffers: [
83
- {
84
- arrayStride: vertexSize,
85
- attributes: [
86
- { shaderLocation: 0, offset: 0, format: 'float32x2' }, // pos
87
- { shaderLocation: 1, offset: 8, format: 'float32x2' }, // tex
88
- { shaderLocation: 2, offset: 16, format: 'float32x4' } // col
89
- ],
90
- },
91
- ],
92
- },
93
- fragment: {
94
- module,
95
- entryPoint: 'fs',
96
- targets: [{
97
- format: presentationFormat,
98
- blend: {
99
- color: { srcFactor: 'one', dstFactor: 'one-minus-src-alpha', operation: 'add' },
100
- alpha: { srcFactor: 'one', dstFactor: 'one-minus-src-alpha', operation: 'add' }
101
- },
102
- }],
103
- },
104
- });
105
-
106
- texture = createTextureFromSource(device, glyphCanvas, { mips: true });
107
- sampler = device.createSampler({
108
- minFilter: 'linear',
109
- magFilter: 'linear'
110
- });
111
-
112
- uniformBuffer = device.createBuffer({
113
- label: 'uniforms for quad',
114
- size: CONFIG.uniformBufferSize,
115
- usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
116
- });
117
-
118
- const uniformValues = new Float32Array(CONFIG.uniformBufferSize / 4);
119
- const matrix = uniformValues.subarray(0, 16);
120
-
121
- bindGroup = device.createBindGroup({
122
- layout: pipeline.getBindGroupLayout(0),
123
- entries: [
124
- { binding: 0, resource: sampler },
125
- { binding: 1, resource: texture.createView() },
126
- { binding: 2, resource: { buffer: uniformBuffer } },
127
- ],
128
- });
129
-
130
- function render(time) {
131
- time *= CONFIG.time.phase;
132
- const fov = 60 * Math.PI / 180;
133
- const aspect = canvas.clientWidth / canvas.clientHeight;
134
- const projectionMatrix = mat4.perspective(fov, aspect, CONFIG.render.zNear, CONFIG.render.zFar);
135
- const viewMatrix = mat4.lookAt([0, 0, 5], [0, 0, 0], [0, 1, 0]);
136
- const viewProjectionMatrix = mat4.multiply(projectionMatrix, viewMatrix);
137
- RENDER_PASS_DESCRIPTOR.colorAttachments[0].view = context.getCurrentTexture().createView();
138
- const encoder = device.createCommandEncoder();
139
- const pass = encoder.beginRenderPass(RENDER_PASS_DESCRIPTOR);
140
- pass.setPipeline(pipeline);
141
- mat4.rotateY(viewProjectionMatrix, time, matrix);
142
- mat4.translate(matrix, [-width / 2, -height / 2, 0], matrix);
143
- device.queue.writeBuffer(uniformBuffer, 0, uniformValues);
144
- pass.setBindGroup(0, bindGroup);
145
- pass.setVertexBuffer(0, vertexBuffer);
146
- pass.setIndexBuffer(indexBuffer, 'uint32');
147
- pass.drawIndexed(numGlyphs * 6);
148
- pass.end();
149
- device.queue.submit([encoder.finish()]);
150
- requestAnimationFrame(render);
151
- }
152
-
153
- requestAnimationFrame(render);
154
- }
155
-
156
- function generateGlyphVerticesForText(s, COLORS, glyphCanvas) {
157
- const vertexData = new Float32Array(CONFIG.maxGlyphs * CONFIG.floatsPerVertex * CONFIG.vertsPerGlyph);
158
- const glyphUVWidth = CONFIG.glyphWidth / glyphCanvas.width;
159
- const glyphUVHeight = CONFIG.glyphHeight / glyphCanvas.height;
160
- let offset = 0, x0 = 0, y0 = 0, x1 = 1, y1 = 1, width = 0;
161
- let colorNdx = 0;
162
-
163
- const addVertex = (x, y, u, v, color) => {
164
- vertexData.set([x, y, u, v, ...color], offset);
165
- offset += 8;
166
- };
167
-
168
- for (let i = 0; i < s.length; ++i) {
169
- const c = s.charCodeAt(i);
170
- if (c >= 33) {
171
- const cIndex = c - 33;
172
- const glyphX = cIndex % CONFIG.glyphsAcrossTexture;
173
- const glyphY = Math.floor(cIndex / CONFIG.glyphsAcrossTexture);
174
- const u0 = glyphX * CONFIG.glyphWidth / glyphCanvas.width;
175
- const v1 = glyphY * CONFIG.glyphHeight / glyphCanvas.height;
176
- const u1 = u0 + glyphUVWidth;
177
- const v0 = v1 + glyphUVHeight;
178
- width = Math.max(x1, width);
179
- addVertex(x0, y0, u0, v0, COLORS[colorNdx]);
180
- addVertex(x1, y0, u1, v0, COLORS[colorNdx]);
181
- addVertex(x0, y1, u0, v1, COLORS[colorNdx]);
182
- addVertex(x1, y1, u1, v1, COLORS[colorNdx]);
183
- } else {
184
- colorNdx = (colorNdx + 1) % COLORS.length;
185
- if (c === 10) { // Newline
186
- x0 = 0; x1 = 1; y0--; y1 = y0 + 1;
187
- continue;
188
- }
189
- }
190
- x0 += 0.55; x1 = x0 + 1;
191
- }
192
- return { vertexData, numGlyphs: offset / CONFIG.floatsPerVertex, width, height: y1 };
193
- }
194
-
195
- function createTextureFromSource(device, source, options = {}) {
196
- const texture = device.createTexture({
197
- format: 'rgba8unorm',
198
- size: [source.width, source.height],
199
- usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.RENDER_ATTACHMENT,
200
- });
201
-
202
- device.queue.copyExternalImageToTexture(
203
- { source, flipY: options.flipY },
204
- { texture, premultipliedAlpha: true },
205
- { width: source.width, height: source.height }
206
- );
207
-
208
- return texture;
209
- }
210
-
211
- main();
212
- </script>
213
  </body>
214
 
215
- </html>
 
9
 
10
  <body>
11
  <canvas></canvas>
12
+ <script type="module" src="index.js"></script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
  </body>
14
 
15
+ </html>
index.js ADDED
@@ -0,0 +1,199 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { mat4 } from 'https://webgpufundamentals.org/3rdparty/wgpu-matrix.module.js';
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,
53
+ });
54
+
55
+ const indices = Array.from({ length: CONFIG.maxGlyphs * 6 }, (_, i) => {
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) {
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(render);
139
+ }
140
+
141
+ requestAnimationFrame(render);
142
+ }
143
+
144
+ function generateGlyphVerticesForText(s, COLORS, glyphCanvas) {
145
+ const vertexData = new Float32Array(CONFIG.maxGlyphs * CONFIG.floatsPerVertex * CONFIG.vertsPerGlyph);
146
+ const glyphUVWidth = CONFIG.glyphWidth / glyphCanvas.width;
147
+ const glyphUVHeight = CONFIG.glyphHeight / glyphCanvas.height;
148
+ let offset = 0, x0 = 0, y0 = 0, x1 = 1, y1 = 1, width = 0;
149
+ let colorNdx = 0;
150
+
151
+ const addVertex = (x, y, u, v, color) => {
152
+ vertexData.set([x, y, u, v, ...color], offset);
153
+ offset += 8;
154
+ };
155
+
156
+ for (let i = 0; i < s.length; ++i) {
157
+ const c = s.charCodeAt(i);
158
+ if (c >= 33) {
159
+ const cIndex = c - 33;
160
+ const glyphX = cIndex % CONFIG.glyphsAcrossTexture;
161
+ const glyphY = Math.floor(cIndex / CONFIG.glyphsAcrossTexture);
162
+ const u0 = glyphX * CONFIG.glyphWidth / glyphCanvas.width;
163
+ const v1 = glyphY * CONFIG.glyphHeight / glyphCanvas.height;
164
+ const u1 = u0 + glyphUVWidth;
165
+ const v0 = v1 + glyphUVHeight;
166
+ width = Math.max(x1, width);
167
+ addVertex(x0, y0, u0, v0, COLORS[colorNdx]);
168
+ addVertex(x1, y0, u1, v0, COLORS[colorNdx]);
169
+ addVertex(x0, y1, u0, v1, COLORS[colorNdx]);
170
+ addVertex(x1, y1, u1, v1, COLORS[colorNdx]);
171
+ } else {
172
+ colorNdx = (colorNdx + 1) % COLORS.length;
173
+ if (c === 10) { // Newline
174
+ x0 = 0; x1 = 1; y0--; y1 = y0 + 1;
175
+ continue;
176
+ }
177
+ }
178
+ x0 += 0.55; x1 = x0 + 1;
179
+ }
180
+ return { vertexData, numGlyphs: offset / CONFIG.floatsPerVertex, width, height: y1 };
181
+ }
182
+
183
+ function createTextureFromSource(device, source, options = {}) {
184
+ const texture = device.createTexture({
185
+ format: 'rgba8unorm',
186
+ size: [source.width, source.height],
187
+ usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.RENDER_ATTACHMENT,
188
+ });
189
+
190
+ device.queue.copyExternalImageToTexture(
191
+ { source, flipY: options.flipY },
192
+ { texture, premultipliedAlpha: true },
193
+ { width: source.width, height: source.height }
194
+ );
195
+
196
+ return texture;
197
+ }
198
+
199
+ main();
style.css CHANGED
@@ -1,3 +1,5 @@
 
 
1
  body {
2
  padding: 0;
3
  margin: 0;
 
1
+ /* style.css */
2
+
3
  body {
4
  padding: 0;
5
  margin: 0;