q5 2.14.3 → 2.14.5

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.
@@ -1,556 +0,0 @@
1
- /**
2
- * q5-webgpu
3
- *
4
- * EXPERIMENTAL, for developer testing only!
5
- */
6
- Q5.renderers.webgpu = {};
7
-
8
- Q5.renderers.webgpu.canvas = ($, q) => {
9
- let c = $.canvas;
10
-
11
- c.width = $.width = 500;
12
- c.height = $.height = 500;
13
-
14
- // q2d graphics context
15
- $._g = $.createGraphics(1, 1);
16
-
17
- if ($.colorMode) $.colorMode('rgb', 1);
18
-
19
- let pass,
20
- mainView,
21
- colorIndex = 1,
22
- colorStackIndex = 8;
23
-
24
- $._pipelineConfigs = [];
25
- $._pipelines = [];
26
-
27
- // local variables used for slightly better performance
28
- // stores pipeline shifts and vertex counts/image indices
29
- let drawStack = ($.drawStack = []);
30
-
31
- // colors used for each draw call
32
- let colorStack = ($.colorStack = new Float32Array(1e6));
33
-
34
- // prettier-ignore
35
- colorStack.set([
36
- 0, 0, 0, 1, // black
37
- 1, 1, 1, 1 // white
38
- ]);
39
-
40
- let mainLayout = Q5.device.createBindGroupLayout({
41
- label: 'mainLayout',
42
- entries: [
43
- {
44
- binding: 0,
45
- visibility: GPUShaderStage.VERTEX,
46
- buffer: { type: 'uniform' }
47
- },
48
- {
49
- binding: 1,
50
- visibility: GPUShaderStage.VERTEX,
51
- buffer: { type: 'read-only-storage' }
52
- },
53
- {
54
- binding: 2,
55
- visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT,
56
- buffer: { type: 'read-only-storage' }
57
- }
58
- ]
59
- });
60
-
61
- $.bindGroupLayouts = [mainLayout];
62
-
63
- let uniformBuffer = Q5.device.createBuffer({
64
- size: 8, // Size of two floats
65
- usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
66
- });
67
-
68
- let createMainView = () => {
69
- mainView = Q5.device
70
- .createTexture({
71
- size: [$.canvas.width, $.canvas.height],
72
- sampleCount: 4,
73
- format: 'bgra8unorm',
74
- usage: GPUTextureUsage.RENDER_ATTACHMENT
75
- })
76
- .createView();
77
- };
78
-
79
- $._createCanvas = (w, h, opt) => {
80
- q.ctx = q.drawingContext = c.getContext('webgpu');
81
-
82
- opt.format ??= navigator.gpu.getPreferredCanvasFormat();
83
- opt.device ??= Q5.device;
84
- if (opt.alpha) opt.alphaMode = 'premultiplied';
85
-
86
- $.ctx.configure(opt);
87
-
88
- Q5.device.queue.writeBuffer(uniformBuffer, 0, new Float32Array([$.canvas.hw, $.canvas.hh]));
89
-
90
- createMainView();
91
- return c;
92
- };
93
-
94
- $._resizeCanvas = (w, h) => {
95
- $._setCanvasSize(w, h);
96
- createMainView();
97
- };
98
-
99
- $.pixelDensity = (v) => {
100
- if (!v || v == $._pixelDensity) return $._pixelDensity;
101
- $._pixelDensity = v;
102
- $._setCanvasSize(c.w, c.h);
103
- createMainView();
104
- return v;
105
- };
106
-
107
- // current color index, used to associate a vertex with a color
108
- let addColor = (r, g, b, a = 1) => {
109
- if (typeof r == 'string') r = $.color(r);
110
- else if (b == undefined) {
111
- // grayscale mode `fill(1, 0.5)`
112
- a = g ?? 1;
113
- g = b = r;
114
- }
115
- if (r._q5Color) {
116
- a = r.a;
117
- b = r.b;
118
- g = r.g;
119
- r = r.r;
120
- }
121
-
122
- let cs = colorStack,
123
- i = colorStackIndex;
124
- cs[i++] = r;
125
- cs[i++] = g;
126
- cs[i++] = b;
127
- cs[i++] = a;
128
- colorStackIndex = i;
129
-
130
- colorIndex++;
131
- };
132
-
133
- $._stroke = 0;
134
- $._fill = $._tint = $._globalAlpha = 1;
135
- $._doFill = $._doStroke = true;
136
-
137
- $.fill = (r, g, b, a) => {
138
- addColor(r, g, b, a);
139
- $._doFill = $._fillSet = true;
140
- $._fill = colorIndex;
141
- };
142
- $.stroke = (r, g, b, a) => {
143
- addColor(r, g, b, a);
144
- $._doStroke = $._strokeSet = true;
145
- $._stroke = colorIndex;
146
- };
147
- $.tint = (r, g, b, a) => {
148
- addColor(r, g, b, a);
149
- $._tint = colorIndex;
150
- };
151
- $.opacity = (a) => ($._globalAlpha = a);
152
-
153
- $.noFill = () => ($._doFill = false);
154
- $.noStroke = () => ($._doStroke = false);
155
- $.noTint = () => ($._tint = 1);
156
-
157
- $._strokeWeight = 1;
158
- $.strokeWeight = (v) => ($._strokeWeight = Math.abs(v));
159
-
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
-
164
- let matrix,
165
- matrices = [],
166
- matricesIndexStack = [];
167
-
168
- // tracks if the matrix has been modified
169
- $._matrixDirty = false;
170
-
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
- ]);
179
-
180
- transforms.set(matrices[0]);
181
-
182
- $.resetMatrix = () => {
183
- matrix = matrices[0].slice();
184
- $._matrixIndex = 0;
185
- };
186
- $.resetMatrix();
187
-
188
- $.translate = (x, y, z) => {
189
- if (!x && !y && !z) return;
190
- // update the translation values
191
- matrix[12] += x;
192
- matrix[13] -= y;
193
- matrix[14] += z || 0;
194
- $._matrixDirty = true;
195
- };
196
-
197
- $.rotate = (a) => {
198
- if (!a) return;
199
- if ($._angleMode) a *= $._DEGTORAD;
200
-
201
- let cosR = Math.cos(a),
202
- sinR = Math.sin(a),
203
- m = matrix,
204
- m0 = m[0],
205
- m1 = m[1],
206
- m4 = m[4],
207
- m5 = m[5];
208
-
209
- // if identity matrix, just set the rotation values
210
- if (m0 == 1 && !m1 && !m4 && m5 == 1) {
211
- m[0] = cosR;
212
- m[1] = -sinR;
213
- m[4] = sinR;
214
- m[5] = cosR;
215
- } else {
216
- // combine the current rotation with the new rotation
217
- m[0] = m0 * cosR + m1 * sinR;
218
- m[1] = m1 * cosR - m0 * sinR;
219
- m[4] = m4 * cosR + m5 * sinR;
220
- m[5] = m5 * cosR - m4 * sinR;
221
- }
222
-
223
- $._matrixDirty = true;
224
- };
225
-
226
- $.scale = (x = 1, y, z = 1) => {
227
- y ??= x;
228
-
229
- $._scale = Math.max(Math.abs(x), Math.abs(y));
230
- $._scaledSW = $._strokeWeight * $._scale;
231
-
232
- let m = matrix;
233
-
234
- m[0] *= x;
235
- m[1] *= x;
236
- m[2] *= x;
237
- m[3] *= x;
238
- m[4] *= y;
239
- m[5] *= y;
240
- m[6] *= y;
241
- m[7] *= y;
242
- m[8] *= z;
243
- m[9] *= z;
244
- m[10] *= z;
245
- m[11] *= z;
246
-
247
- $._matrixDirty = true;
248
- };
249
-
250
- $.shearX = (ang) => {
251
- if (!ang) return;
252
- if ($._angleMode) ang *= $._DEGTORAD;
253
-
254
- let tanAng = Math.tan(ang),
255
- m = matrix,
256
- m0 = m[0],
257
- m1 = m[1],
258
- m4 = m[4],
259
- m5 = m[5];
260
-
261
- m[0] = m0 + m4 * tanAng;
262
- m[1] = m1 + m5 * tanAng;
263
-
264
- $._matrixDirty = true;
265
- };
266
-
267
- $.shearY = (ang) => {
268
- if (!ang) return;
269
- if ($._angleMode) ang *= $._DEGTORAD;
270
-
271
- let tanAng = Math.tan(ang),
272
- m = matrix,
273
- m0 = m[0],
274
- m1 = m[1],
275
- m4 = m[4],
276
- m5 = m[5];
277
-
278
- m[4] = m4 + m0 * tanAng;
279
- m[5] = m5 + m1 * tanAng;
280
-
281
- $._matrixDirty = true;
282
- };
283
-
284
- $.applyMatrix = (...args) => {
285
- let m;
286
- if (args.length == 1) m = args[0];
287
- else m = args;
288
-
289
- if (m.length == 9) {
290
- // convert 3x3 matrix to 4x4 matrix
291
- m = [m[0], m[1], 0, m[2], m[3], m[4], 0, m[5], 0, 0, 1, 0, m[6], m[7], 0, m[8]];
292
- } else if (m.length != 16) {
293
- throw new Error('Matrix must be a 3x3 or 4x4 array.');
294
- }
295
-
296
- // overwrite the current transformation matrix
297
- matrix = m.slice();
298
- $._matrixDirty = true;
299
- };
300
-
301
- // function to save the current matrix state if dirty
302
- $._saveMatrix = () => {
303
- transforms.set(matrix, matrices.length * MATRIX_SIZE);
304
- $._matrixIndex = matrices.length;
305
- matrices.push(matrix.slice());
306
- $._matrixDirty = false;
307
- };
308
-
309
- // push the current matrix index onto the stack
310
- $.pushMatrix = () => {
311
- if ($._matrixDirty) $._saveMatrix();
312
- matricesIndexStack.push($._matrixIndex);
313
- };
314
-
315
- $.popMatrix = () => {
316
- if (!matricesIndexStack.length) {
317
- return console.warn('Matrix index stack is empty!');
318
- }
319
- // pop the last matrix index and set it as the current matrix index
320
- let idx = matricesIndexStack.pop();
321
- matrix = matrices[idx].slice();
322
- $._matrixIndex = idx;
323
- $._matrixDirty = false;
324
- };
325
-
326
- $.push = () => {
327
- $.pushMatrix();
328
- $.pushStyles();
329
- };
330
-
331
- $.pop = () => {
332
- $.popMatrix();
333
- $.popStyles();
334
- };
335
-
336
- $._calcBox = (x, y, w, h, mode) => {
337
- let hw = w / 2;
338
- let hh = h / 2;
339
-
340
- // left, right, top, bottom
341
- let l, r, t, b;
342
- if (!mode || mode == 'corner') {
343
- l = x;
344
- r = x + w;
345
- t = -y;
346
- b = -(y + h);
347
- } else if (mode == 'center') {
348
- l = x - hw;
349
- r = x + hw;
350
- t = -(y - hh);
351
- b = -(y + hh);
352
- } else {
353
- // CORNERS
354
- l = x;
355
- r = w;
356
- t = -y;
357
- b = -h;
358
- }
359
-
360
- return [l, r, t, b];
361
- };
362
-
363
- // prettier-ignore
364
- let blendFactors = [
365
- 'zero', // 0
366
- 'one', // 1
367
- 'src-alpha', // 2
368
- 'one-minus-src-alpha', // 3
369
- 'dst', // 4
370
- 'dst-alpha', // 5
371
- 'one-minus-dst-alpha', // 6
372
- 'one-minus-src' // 7
373
- ];
374
- let blendOps = [
375
- 'add', // 0
376
- 'subtract', // 1
377
- 'reverse-subtract', // 2
378
- 'min', // 3
379
- 'max' // 4
380
- ];
381
-
382
- // other blend modes are not supported yet
383
- const blendModes = {
384
- normal: [2, 3, 0, 2, 3, 0],
385
- // destination_over: [6, 1, 0, 6, 1, 0],
386
- additive: [1, 1, 0, 1, 1, 0]
387
- // source_in: [5, 0, 0, 5, 0, 0],
388
- // destination_in: [0, 2, 0, 0, 2, 0],
389
- // source_out: [6, 0, 0, 6, 0, 0],
390
- // destination_out: [0, 3, 0, 0, 3, 0],
391
- // source_atop: [5, 3, 0, 5, 3, 0],
392
- // destination_atop: [6, 2, 0, 6, 2, 0]
393
- };
394
-
395
- $.blendConfigs = {};
396
-
397
- for (const [name, mode] of Object.entries(blendModes)) {
398
- $.blendConfigs[name] = {
399
- color: {
400
- srcFactor: blendFactors[mode[0]],
401
- dstFactor: blendFactors[mode[1]],
402
- operation: blendOps[mode[2]]
403
- },
404
- alpha: {
405
- srcFactor: blendFactors[mode[3]],
406
- dstFactor: blendFactors[mode[4]],
407
- operation: blendOps[mode[5]]
408
- }
409
- };
410
- }
411
-
412
- $._blendMode = 'normal';
413
-
414
- $.blendMode = (mode) => {
415
- if (mode == $._blendMode) return;
416
- if (mode == 'source-over') mode = 'normal';
417
- if (mode == 'lighter') mode = 'additive';
418
- mode = mode.toLowerCase().replace(/[ -]/g, '_');
419
- $._blendMode = mode;
420
-
421
- for (let i = 0; i < $._pipelines.length; i++) {
422
- $._pipelineConfigs[i].fragment.targets[0].blend = $.blendConfigs[mode];
423
- $._pipelines[i] = Q5.device.createRenderPipeline($._pipelineConfigs[i]);
424
- }
425
- };
426
-
427
- $.clear = () => {};
428
-
429
- $._beginRender = () => {
430
- $.encoder = Q5.device.createCommandEncoder();
431
-
432
- pass = q.pass = $.encoder.beginRenderPass({
433
- label: 'q5-webgpu',
434
- colorAttachments: [
435
- {
436
- view: mainView,
437
- resolveTarget: $.ctx.getCurrentTexture().createView(),
438
- loadOp: 'clear',
439
- storeOp: 'store',
440
- clearValue: [0, 0, 0, 0]
441
- }
442
- ]
443
- });
444
- };
445
-
446
- $._render = () => {
447
- let transformBuffer = Q5.device.createBuffer({
448
- size: matrices.length * MATRIX_SIZE * 4, // 4 bytes per float
449
- usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
450
- mappedAtCreation: true
451
- });
452
-
453
- new Float32Array(transformBuffer.getMappedRange()).set(transforms.slice(0, matrices.length * MATRIX_SIZE));
454
- transformBuffer.unmap();
455
-
456
- let colorsBuffer = Q5.device.createBuffer({
457
- size: colorStackIndex * 4,
458
- usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
459
- mappedAtCreation: true
460
- });
461
-
462
- new Float32Array(colorsBuffer.getMappedRange()).set(colorStack.slice(0, colorStackIndex));
463
- colorsBuffer.unmap();
464
-
465
- let mainBindGroup = Q5.device.createBindGroup({
466
- layout: mainLayout,
467
- entries: [
468
- { binding: 0, resource: { buffer: uniformBuffer } },
469
- { binding: 1, resource: { buffer: transformBuffer } },
470
- { binding: 2, resource: { buffer: colorsBuffer } }
471
- ]
472
- });
473
-
474
- pass.setBindGroup(0, mainBindGroup);
475
-
476
- for (let m of $._hooks.preRender) m();
477
-
478
- let drawVertOffset = 0,
479
- imageVertOffset = 0,
480
- textCharOffset = 0,
481
- curPipelineIndex = -1;
482
-
483
- for (let i = 0; i < drawStack.length; i += 2) {
484
- let v = drawStack[i + 1];
485
-
486
- if (curPipelineIndex != drawStack[i]) {
487
- curPipelineIndex = drawStack[i];
488
- pass.setPipeline($._pipelines[curPipelineIndex]);
489
- }
490
-
491
- if (curPipelineIndex == 0) {
492
- // draw shapes
493
- // v is the number of vertices
494
- pass.draw(v, 1, drawVertOffset);
495
- drawVertOffset += v;
496
- } else if (curPipelineIndex == 1) {
497
- // draw images
498
- // v is the texture index
499
- pass.setBindGroup(1, $._textureBindGroups[v]);
500
- pass.draw(4, 1, imageVertOffset);
501
- imageVertOffset += 4;
502
- } else if (curPipelineIndex == 2) {
503
- // draw text
504
- let o = drawStack[i + 2];
505
- pass.setBindGroup(1, $._fonts[o].bindGroup);
506
- pass.setBindGroup(2, $._textBindGroup);
507
-
508
- // v is the number of characters in the text
509
- pass.draw(4, v, 0, textCharOffset);
510
- textCharOffset += v;
511
- i++;
512
- }
513
- }
514
- };
515
-
516
- $._finishRender = () => {
517
- pass.end();
518
- let commandBuffer = $.encoder.finish();
519
- Q5.device.queue.submit([commandBuffer]);
520
-
521
- q.pass = $.encoder = null;
522
-
523
- // clear the stacks for the next frame
524
- $.drawStack = drawStack = [];
525
- colorIndex = 1;
526
- colorStackIndex = 8;
527
- matrices = [matrices[0]];
528
- matricesIndexStack = [];
529
-
530
- for (let m of $._hooks.postRender) m();
531
- };
532
- };
533
-
534
- Q5.initWebGPU = async () => {
535
- if (!navigator.gpu) {
536
- console.warn('q5 WebGPU not supported on this browser! Use Google Chrome or Edge.');
537
- return false;
538
- }
539
- if (!Q5.device) {
540
- let adapter = await navigator.gpu.requestAdapter();
541
- if (!adapter) {
542
- console.warn('q5 WebGPU could not start! No appropriate GPUAdapter found, vulkan may need to be enabled.');
543
- return false;
544
- }
545
- Q5.device = await adapter.requestDevice();
546
- }
547
- return true;
548
- };
549
-
550
- Q5.webgpu = async function (scope, parent) {
551
- if (!scope || scope == 'global') Q5._hasGlobal = true;
552
- if (!(await Q5.initWebGPU())) {
553
- return new Q5(scope, parent, 'webgpu-fallback');
554
- }
555
- return new Q5(scope, parent, 'webgpu');
556
- };