q5 2.12.12 → 2.13.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -11,11 +11,13 @@ Q5.renderers.webgpu.canvas = ($, q) => {
11
11
  c.width = $.width = 500;
12
12
  c.height = $.height = 500;
13
13
 
14
+ // q2d graphics context
15
+ $._g = $.createGraphics(1, 1);
16
+
14
17
  if ($.colorMode) $.colorMode('rgb', 1);
15
18
 
16
19
  let pass,
17
20
  mainView,
18
- colorsLayout,
19
21
  colorIndex = 1,
20
22
  colorStackIndex = 8;
21
23
 
@@ -27,7 +29,6 @@ Q5.renderers.webgpu.canvas = ($, q) => {
27
29
  let drawStack = ($.drawStack = []);
28
30
 
29
31
  // colors used for each draw call
30
-
31
32
  let colorStack = ($.colorStack = new Float32Array(1e6));
32
33
 
33
34
  // prettier-ignore
@@ -36,43 +37,28 @@ Q5.renderers.webgpu.canvas = ($, q) => {
36
37
  1, 1, 1, 1 // white
37
38
  ]);
38
39
 
39
- $._transformLayout = Q5.device.createBindGroupLayout({
40
- label: 'transformLayout',
40
+ let mainLayout = Q5.device.createBindGroupLayout({
41
+ label: 'mainLayout',
41
42
  entries: [
42
43
  {
43
44
  binding: 0,
44
45
  visibility: GPUShaderStage.VERTEX,
45
- buffer: {
46
- type: 'uniform',
47
- hasDynamicOffset: false
48
- }
46
+ buffer: { type: 'uniform' }
49
47
  },
50
48
  {
51
49
  binding: 1,
52
50
  visibility: GPUShaderStage.VERTEX,
53
- buffer: {
54
- type: 'read-only-storage',
55
- hasDynamicOffset: false
56
- }
57
- }
58
- ]
59
- });
60
-
61
- colorsLayout = Q5.device.createBindGroupLayout({
62
- label: 'colorsLayout',
63
- entries: [
51
+ buffer: { type: 'read-only-storage' }
52
+ },
64
53
  {
65
- binding: 0,
54
+ binding: 2,
66
55
  visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT,
67
- buffer: {
68
- type: 'read-only-storage',
69
- hasDynamicOffset: false
70
- }
56
+ buffer: { type: 'read-only-storage' }
71
57
  }
72
58
  ]
73
59
  });
74
60
 
75
- $.bindGroupLayouts = [$._transformLayout, colorsLayout];
61
+ $.bindGroupLayouts = [mainLayout];
76
62
 
77
63
  let uniformBuffer = Q5.device.createBuffer({
78
64
  size: 8, // Size of two floats
@@ -102,7 +88,6 @@ Q5.renderers.webgpu.canvas = ($, q) => {
102
88
  Q5.device.queue.writeBuffer(uniformBuffer, 0, new Float32Array([$.canvas.hw, $.canvas.hh]));
103
89
 
104
90
  createMainView();
105
-
106
91
  return c;
107
92
  };
108
93
 
@@ -146,7 +131,7 @@ Q5.renderers.webgpu.canvas = ($, q) => {
146
131
  };
147
132
 
148
133
  $._stroke = 0;
149
- $._fill = 1;
134
+ $._fill = $._tint = $._globalAlpha = 1;
150
135
  $._doFill = $._doStroke = true;
151
136
 
152
137
  $.fill = (r, g, b, a) => {
@@ -159,42 +144,53 @@ Q5.renderers.webgpu.canvas = ($, q) => {
159
144
  $._doStroke = $._strokeSet = true;
160
145
  $._stroke = colorIndex;
161
146
  };
147
+ $.tint = (r, g, b, a) => {
148
+ addColor(r, g, b, a);
149
+ $._tint = colorIndex;
150
+ };
151
+ $.opacity = (a) => ($._globalAlpha = a);
162
152
 
163
153
  $.noFill = () => ($._doFill = false);
164
154
  $.noStroke = () => ($._doStroke = false);
155
+ $.noTint = () => ($._tint = 1);
165
156
 
166
157
  $._strokeWeight = 1;
167
158
  $.strokeWeight = (v) => ($._strokeWeight = Math.abs(v));
168
159
 
169
- $.resetMatrix = () => {
170
- // initialize the transformation matrix as 4x4 identity matrix
171
-
172
- // prettier-ignore
173
- $._matrix = [
174
- 1, 0, 0, 0,
175
- 0, 1, 0, 0,
176
- 0, 0, 1, 0,
177
- 0, 0, 0, 1
178
- ];
179
- $._transformIndex = 0;
180
- };
181
- $.resetMatrix();
160
+ const MAX_TRANSFORMS = 1e7, // or whatever maximum you need
161
+ MATRIX_SIZE = 16, // 4x4 matrix
162
+ transforms = new Float32Array(MAX_TRANSFORMS * MATRIX_SIZE),
163
+ matrices = [],
164
+ matricesIndexStack = [];
165
+
166
+ let matrix;
182
167
 
183
168
  // tracks if the matrix has been modified
184
169
  $._matrixDirty = false;
185
170
 
186
- // array to store transformation matrices for the render pass
187
- let transformStates = [$._matrix.slice()];
171
+ // initialize with a 4x4 identity matrix
172
+ // prettier-ignore
173
+ matrices.push([
174
+ 1, 0, 0, 0,
175
+ 0, 1, 0, 0,
176
+ 0, 0, 1, 0,
177
+ 0, 0, 0, 1
178
+ ]);
188
179
 
189
- // stack to keep track of transformation matrix indexes
190
- $._transformIndexStack = [];
180
+ transforms.set(matrices[0]);
181
+
182
+ $.resetMatrix = () => {
183
+ matrix = matrices[0].slice();
184
+ $._matrixIndex = 0;
185
+ };
186
+ $.resetMatrix();
191
187
 
192
188
  $.translate = (x, y, z) => {
193
189
  if (!x && !y && !z) return;
194
190
  // update the translation values
195
- $._matrix[12] += x;
196
- $._matrix[13] -= y;
197
- $._matrix[14] += z || 0;
191
+ matrix[12] += x;
192
+ matrix[13] -= y;
193
+ matrix[14] += z || 0;
198
194
  $._matrixDirty = true;
199
195
  };
200
196
 
@@ -202,12 +198,10 @@ Q5.renderers.webgpu.canvas = ($, q) => {
202
198
  if (!a) return;
203
199
  if ($._angleMode) a *= $._DEGTORAD;
204
200
 
205
- let cosR = Math.cos(a);
206
- let sinR = Math.sin(a);
207
-
208
- let m = $._matrix;
209
-
210
- let m0 = m[0],
201
+ let cosR = Math.cos(a),
202
+ sinR = Math.sin(a),
203
+ m = matrix,
204
+ m0 = m[0],
211
205
  m1 = m[1],
212
206
  m4 = m[4],
213
207
  m5 = m[5];
@@ -232,7 +226,7 @@ Q5.renderers.webgpu.canvas = ($, q) => {
232
226
  $.scale = (x = 1, y, z = 1) => {
233
227
  y ??= x;
234
228
 
235
- let m = $._matrix;
229
+ let m = matrix;
236
230
 
237
231
  m[0] *= x;
238
232
  m[1] *= x;
@@ -254,15 +248,15 @@ Q5.renderers.webgpu.canvas = ($, q) => {
254
248
  if (!ang) return;
255
249
  if ($._angleMode) ang *= $._DEGTORAD;
256
250
 
257
- let tanAng = Math.tan(ang);
258
-
259
- let m0 = $._matrix[0],
260
- m1 = $._matrix[1],
261
- m4 = $._matrix[4],
262
- m5 = $._matrix[5];
251
+ let tanAng = Math.tan(ang),
252
+ m = matrix,
253
+ m0 = m[0],
254
+ m1 = m[1],
255
+ m4 = m[4],
256
+ m5 = m[5];
263
257
 
264
- $._matrix[0] = m0 + m4 * tanAng;
265
- $._matrix[1] = m1 + m5 * tanAng;
258
+ m[0] = m0 + m4 * tanAng;
259
+ m[1] = m1 + m5 * tanAng;
266
260
 
267
261
  $._matrixDirty = true;
268
262
  };
@@ -271,15 +265,15 @@ Q5.renderers.webgpu.canvas = ($, q) => {
271
265
  if (!ang) return;
272
266
  if ($._angleMode) ang *= $._DEGTORAD;
273
267
 
274
- let tanAng = Math.tan(ang);
275
-
276
- let m0 = $._matrix[0],
277
- m1 = $._matrix[1],
278
- m4 = $._matrix[4],
279
- m5 = $._matrix[5];
268
+ let tanAng = Math.tan(ang),
269
+ m = matrix,
270
+ m0 = m[0],
271
+ m1 = m[1],
272
+ m4 = m[4],
273
+ m5 = m[5];
280
274
 
281
- $._matrix[4] = m4 + m0 * tanAng;
282
- $._matrix[5] = m5 + m1 * tanAng;
275
+ m[4] = m4 + m0 * tanAng;
276
+ m[5] = m5 + m1 * tanAng;
283
277
 
284
278
  $._matrixDirty = true;
285
279
  };
@@ -297,31 +291,32 @@ Q5.renderers.webgpu.canvas = ($, q) => {
297
291
  }
298
292
 
299
293
  // overwrite the current transformation matrix
300
- $._matrix = m.slice();
294
+ matrix = m.slice();
301
295
  $._matrixDirty = true;
302
296
  };
303
297
 
304
298
  // function to save the current matrix state if dirty
305
299
  $._saveMatrix = () => {
306
- transformStates.push($._matrix.slice());
307
- $._transformIndex = transformStates.length - 1;
300
+ transforms.set(matrix, matrices.length * MATRIX_SIZE);
301
+ $._matrixIndex = matrices.length;
302
+ matrices.push(matrix.slice());
308
303
  $._matrixDirty = false;
309
304
  };
310
305
 
311
306
  // push the current matrix index onto the stack
312
307
  $.pushMatrix = () => {
313
308
  if ($._matrixDirty) $._saveMatrix();
314
- $._transformIndexStack.push($._transformIndex);
309
+ matricesIndexStack.push($._matrixIndex);
315
310
  };
316
311
 
317
312
  $.popMatrix = () => {
318
- if (!$._transformIndexStack.length) {
313
+ if (!matricesIndexStack.length) {
319
314
  return console.warn('Matrix index stack is empty!');
320
315
  }
321
316
  // pop the last matrix index and set it as the current matrix index
322
- let idx = $._transformIndexStack.pop();
323
- $._matrix = transformStates[idx].slice();
324
- $._transformIndex = idx;
317
+ let idx = matricesIndexStack.pop();
318
+ matrix = matrices[idx].slice();
319
+ $._matrixIndex = idx;
325
320
  $._matrixDirty = false;
326
321
  };
327
322
 
@@ -446,26 +441,14 @@ Q5.renderers.webgpu.canvas = ($, q) => {
446
441
  };
447
442
 
448
443
  $._render = () => {
449
- if (transformStates.length > 1 || !$._transformBindGroup) {
450
- let transformBuffer = Q5.device.createBuffer({
451
- size: transformStates.length * 64, // 64 is the size of 16 floats
452
- usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
453
- mappedAtCreation: true
454
- });
455
-
456
- new Float32Array(transformBuffer.getMappedRange()).set(transformStates.flat());
457
- transformBuffer.unmap();
458
-
459
- $._transformBindGroup = Q5.device.createBindGroup({
460
- layout: $._transformLayout,
461
- entries: [
462
- { binding: 0, resource: { buffer: uniformBuffer } },
463
- { binding: 1, resource: { buffer: transformBuffer } }
464
- ]
465
- });
466
- }
444
+ let transformBuffer = Q5.device.createBuffer({
445
+ size: matrices.length * MATRIX_SIZE * 4, // 4 bytes per float
446
+ usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
447
+ mappedAtCreation: true
448
+ });
467
449
 
468
- pass.setBindGroup(0, $._transformBindGroup);
450
+ new Float32Array(transformBuffer.getMappedRange()).set(transforms.slice(0, matrices.length * MATRIX_SIZE));
451
+ transformBuffer.unmap();
469
452
 
470
453
  let colorsBuffer = Q5.device.createBuffer({
471
454
  size: colorStackIndex * 4,
@@ -476,12 +459,16 @@ Q5.renderers.webgpu.canvas = ($, q) => {
476
459
  new Float32Array(colorsBuffer.getMappedRange()).set(colorStack.slice(0, colorStackIndex));
477
460
  colorsBuffer.unmap();
478
461
 
479
- $._colorsBindGroup = Q5.device.createBindGroup({
480
- layout: colorsLayout,
481
- entries: [{ binding: 0, resource: { buffer: colorsBuffer } }]
462
+ mainBindGroup = Q5.device.createBindGroup({
463
+ layout: mainLayout,
464
+ entries: [
465
+ { binding: 0, resource: { buffer: uniformBuffer } },
466
+ { binding: 1, resource: { buffer: transformBuffer } },
467
+ { binding: 2, resource: { buffer: colorsBuffer } }
468
+ ]
482
469
  });
483
470
 
484
- $.pass.setBindGroup(1, $._colorsBindGroup);
471
+ pass.setBindGroup(0, mainBindGroup);
485
472
 
486
473
  for (let m of $._hooks.preRender) m();
487
474
 
@@ -505,18 +492,20 @@ Q5.renderers.webgpu.canvas = ($, q) => {
505
492
  pass.draw(v, 1, drawVertOffset);
506
493
  drawVertOffset += v;
507
494
  } else if (curPipelineIndex == 1) {
495
+ // let instanceCount = drawStack[i + 2];
508
496
  // draw images
509
497
  if (curTextureIndex != v) {
510
498
  // v is the texture index
511
- pass.setBindGroup(2, $._textureBindGroups[v]);
499
+ pass.setBindGroup(1, $._textureBindGroups[v]);
512
500
  }
513
501
  pass.draw(4, 1, imageVertOffset);
514
502
  imageVertOffset += 4;
503
+ // i++;
515
504
  } else if (curPipelineIndex == 2) {
516
505
  // draw text
517
506
  let o = drawStack[i + 2];
518
- pass.setBindGroup(2, $._fonts[o].bindGroup);
519
- pass.setBindGroup(3, $._textBindGroup);
507
+ pass.setBindGroup(1, $._fonts[o].bindGroup);
508
+ pass.setBindGroup(2, $._textBindGroup);
520
509
 
521
510
  // v is the number of characters in the text
522
511
  pass.draw(4, v, 0, textCharOffset);
@@ -540,8 +529,9 @@ Q5.renderers.webgpu.canvas = ($, q) => {
540
529
  colorIndex = 1;
541
530
  colorStackIndex = 8;
542
531
  rotation = 0;
543
- transformStates.length = 1;
544
- $._transformIndexStack.length = 0;
532
+ transforms.length = MATRIX_SIZE;
533
+ matrices.length = 1;
534
+ matricesIndexStack.length = 0;
545
535
  };
546
536
  };
547
537
 
@@ -552,7 +542,10 @@ Q5.initWebGPU = async () => {
552
542
  }
553
543
  if (!Q5.device) {
554
544
  let adapter = await navigator.gpu.requestAdapter();
555
- if (!adapter) throw new Error('No appropriate GPUAdapter found.');
545
+ if (!adapter) {
546
+ console.warn('q5 WebGPU could not start! No appropriate GPUAdapter found, vulkan may need to be enabled.');
547
+ return false;
548
+ }
556
549
  Q5.device = await adapter.requestDevice();
557
550
  }
558
551
  return true;
@@ -4,46 +4,40 @@ Q5.renderers.webgpu.drawing = ($, q) => {
4
4
  vertexStack = new Float32Array(1e7),
5
5
  vertIndex = 0;
6
6
 
7
- let vertexShader = Q5.device.createShaderModule({
8
- label: 'drawingVertexShader',
7
+ let drawingShader = Q5.device.createShaderModule({
8
+ label: 'drawingShader',
9
9
  code: `
10
- struct VertexInput {
10
+ struct Uniforms {
11
+ halfWidth: f32,
12
+ halfHeight: f32
13
+ }
14
+ struct VertexParams {
11
15
  @location(0) pos: vec2f,
12
16
  @location(1) colorIndex: f32,
13
- @location(2) transformIndex: f32
17
+ @location(2) matrixIndex: f32
14
18
  }
15
- struct VertexOutput {
19
+ struct FragmentParams {
16
20
  @builtin(position) position: vec4f,
17
21
  @location(0) color: vec4f
18
22
  }
19
- struct Uniforms {
20
- halfWidth: f32,
21
- halfHeight: f32
22
- }
23
23
 
24
24
  @group(0) @binding(0) var<uniform> uniforms: Uniforms;
25
25
  @group(0) @binding(1) var<storage> transforms: array<mat4x4<f32>>;
26
-
27
- @group(1) @binding(0) var<storage> colors : array<vec4f>;
26
+ @group(0) @binding(2) var<storage> colors : array<vec4f>;
28
27
 
29
28
  @vertex
30
- fn vertexMain(input: VertexInput) -> VertexOutput {
31
- var vert = vec4f(input.pos, 0.0, 1.0);
32
- vert = transforms[i32(input.transformIndex)] * vert;
29
+ fn vertexMain(v: VertexParams) -> FragmentParams {
30
+ var vert = vec4f(v.pos, 0.0, 1.0);
31
+ vert = transforms[i32(v.matrixIndex)] * vert;
33
32
  vert.x /= uniforms.halfWidth;
34
33
  vert.y /= uniforms.halfHeight;
35
34
 
36
- var output: VertexOutput;
37
- output.position = vert;
38
- output.color = colors[i32(input.colorIndex)];
39
- return output;
35
+ var f: FragmentParams;
36
+ f.position = vert;
37
+ f.color = colors[i32(v.colorIndex)];
38
+ return f;
40
39
  }
41
- `
42
- });
43
40
 
44
- let fragmentShader = Q5.device.createShaderModule({
45
- label: 'drawingFragmentShader',
46
- code: `
47
41
  @fragment
48
42
  fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
49
43
  return color;
@@ -56,7 +50,7 @@ fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
56
50
  attributes: [
57
51
  { format: 'float32x2', offset: 0, shaderLocation: 0 }, // position
58
52
  { format: 'float32', offset: 8, shaderLocation: 1 }, // colorIndex
59
- { format: 'float32', offset: 12, shaderLocation: 2 } // transformIndex
53
+ { format: 'float32', offset: 12, shaderLocation: 2 } // matrixIndex
60
54
  ]
61
55
  };
62
56
 
@@ -69,19 +63,17 @@ fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
69
63
  label: 'drawingPipeline',
70
64
  layout: pipelineLayout,
71
65
  vertex: {
72
- module: vertexShader,
66
+ module: drawingShader,
73
67
  entryPoint: 'vertexMain',
74
68
  buffers: [vertexBufferLayout]
75
69
  },
76
70
  fragment: {
77
- module: fragmentShader,
71
+ module: drawingShader,
78
72
  entryPoint: 'fragmentMain',
79
73
  targets: [{ format: 'bgra8unorm', blend: $.blendConfigs.normal }]
80
74
  },
81
75
  primitive: { topology: 'triangle-strip', stripIndexFormat: 'uint32' },
82
- multisample: {
83
- count: 4
84
- }
76
+ multisample: { count: 4 }
85
77
  };
86
78
 
87
79
  $._pipelines[0] = Q5.device.createRenderPipeline($._pipelineConfigs[0]);
@@ -210,7 +202,7 @@ fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
210
202
  let [l, r, t, b] = $._calcBox(x, y, w, h, $._rectMode);
211
203
  let ci, ti;
212
204
  if ($._matrixDirty) $._saveMatrix();
213
- ti = $._transformIndex;
205
+ ti = $._matrixIndex;
214
206
 
215
207
  if ($._doFill) {
216
208
  ci = $._fill;
@@ -284,7 +276,7 @@ fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
284
276
  let b = w == h ? a : Math.max(h, 1) / 2;
285
277
 
286
278
  if ($._matrixDirty) $._saveMatrix();
287
- let ti = $._transformIndex;
279
+ let ti = $._matrixIndex;
288
280
 
289
281
  if ($._doFill) {
290
282
  addEllipse(x, y, a, b, n, $._fill, ti);
@@ -300,7 +292,7 @@ fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
300
292
 
301
293
  $.point = (x, y) => {
302
294
  if ($._matrixDirty) $._saveMatrix();
303
- let ti = $._transformIndex,
295
+ let ti = $._matrixIndex,
304
296
  ci = $._stroke,
305
297
  sw = $._strokeWeight;
306
298
 
@@ -320,7 +312,7 @@ fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
320
312
 
321
313
  $.line = (x1, y1, x2, y2) => {
322
314
  if ($._matrixDirty) $._saveMatrix();
323
- let ti = $._transformIndex,
315
+ let ti = $._matrixIndex,
324
316
  ci = $._stroke,
325
317
  sw = $._strokeWeight,
326
318
  hsw = sw / 2;
@@ -355,7 +347,7 @@ fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
355
347
 
356
348
  $.vertex = (x, y) => {
357
349
  if ($._matrixDirty) $._saveMatrix();
358
- sv.push(x, -y, $._fill, $._transformIndex);
350
+ sv.push(x, -y, $._fill, $._matrixIndex);
359
351
  shapeVertCount++;
360
352
  };
361
353
 
@@ -400,7 +392,7 @@ fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
400
392
  (2 * p0.y - 5 * p1.y + 4 * p2.y - p3.y) * t2 +
401
393
  (-p0.y + 3 * p1.y - 3 * p2.y + p3.y) * t3);
402
394
 
403
- sv.push(x, y, $._fill, $._transformIndex);
395
+ sv.push(x, y, $._fill, $._matrixIndex);
404
396
  shapeVertCount++;
405
397
  }
406
398
  }