q5 2.5.5 → 2.6.1
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.
- package/package.json +2 -2
- package/q5.js +600 -518
- package/q5.min.js +2 -2
- package/src/q5-2d-canvas.js +2 -4
- package/src/q5-core.js +2 -2
- package/src/q5-webgpu-canvas.js +176 -44
- package/src/q5-webgpu-drawing.js +298 -291
- package/src/q5-webgpu-image.js +25 -38
- package/src/q5-webgpu-text.js +95 -137
- package/src/readme.md +8 -18
package/src/q5-webgpu-canvas.js
CHANGED
|
@@ -11,18 +11,30 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
11
11
|
c.width = $.width = 500;
|
|
12
12
|
c.height = $.height = 500;
|
|
13
13
|
|
|
14
|
-
if ($.colorMode) $.colorMode('rgb',
|
|
14
|
+
if ($.colorMode) $.colorMode('rgb', 1);
|
|
15
15
|
|
|
16
|
-
let pass
|
|
16
|
+
let pass,
|
|
17
|
+
mainView,
|
|
18
|
+
colorsLayout,
|
|
19
|
+
colorIndex = 1,
|
|
20
|
+
colorStackIndex = 8;
|
|
17
21
|
|
|
18
|
-
$.
|
|
22
|
+
$._pipelineConfigs = [];
|
|
23
|
+
$._pipelines = [];
|
|
19
24
|
|
|
20
25
|
// local variables used for slightly better performance
|
|
21
26
|
// stores pipeline shifts and vertex counts/image indices
|
|
22
27
|
let drawStack = ($.drawStack = []);
|
|
23
28
|
|
|
24
29
|
// colors used for each draw call
|
|
25
|
-
|
|
30
|
+
|
|
31
|
+
let colorStack = ($.colorStack = new Float32Array(1e6));
|
|
32
|
+
|
|
33
|
+
// prettier-ignore
|
|
34
|
+
colorStack.set([
|
|
35
|
+
0, 0, 0, 1, // black
|
|
36
|
+
1, 1, 1, 1 // white
|
|
37
|
+
]);
|
|
26
38
|
|
|
27
39
|
$._transformLayout = Q5.device.createBindGroupLayout({
|
|
28
40
|
label: 'transformLayout',
|
|
@@ -46,32 +58,70 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
46
58
|
]
|
|
47
59
|
});
|
|
48
60
|
|
|
49
|
-
|
|
61
|
+
colorsLayout = Q5.device.createBindGroupLayout({
|
|
62
|
+
label: 'colorsLayout',
|
|
63
|
+
entries: [
|
|
64
|
+
{
|
|
65
|
+
binding: 0,
|
|
66
|
+
visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT,
|
|
67
|
+
buffer: {
|
|
68
|
+
type: 'read-only-storage',
|
|
69
|
+
hasDynamicOffset: false
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
]
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
$.bindGroupLayouts = [$._transformLayout, colorsLayout];
|
|
50
76
|
|
|
51
77
|
let uniformBuffer = Q5.device.createBuffer({
|
|
52
78
|
size: 8, // Size of two floats
|
|
53
79
|
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
|
|
54
80
|
});
|
|
55
81
|
|
|
82
|
+
let createMainView = () => {
|
|
83
|
+
mainView = Q5.device
|
|
84
|
+
.createTexture({
|
|
85
|
+
size: [$.canvas.width, $.canvas.height],
|
|
86
|
+
sampleCount: 4,
|
|
87
|
+
format: 'bgra8unorm',
|
|
88
|
+
usage: GPUTextureUsage.RENDER_ATTACHMENT
|
|
89
|
+
})
|
|
90
|
+
.createView();
|
|
91
|
+
};
|
|
92
|
+
|
|
56
93
|
$._createCanvas = (w, h, opt) => {
|
|
57
94
|
q.ctx = q.drawingContext = c.getContext('webgpu');
|
|
58
95
|
|
|
59
96
|
opt.format ??= navigator.gpu.getPreferredCanvasFormat();
|
|
60
97
|
opt.device ??= Q5.device;
|
|
61
98
|
|
|
99
|
+
// needed for other blend modes but couldn't get it working
|
|
100
|
+
// opt.alphaMode = 'premultiplied';
|
|
101
|
+
|
|
62
102
|
$.ctx.configure(opt);
|
|
63
103
|
|
|
64
104
|
Q5.device.queue.writeBuffer(uniformBuffer, 0, new Float32Array([$.canvas.hw, $.canvas.hh]));
|
|
65
105
|
|
|
106
|
+
createMainView();
|
|
107
|
+
|
|
66
108
|
return c;
|
|
67
109
|
};
|
|
68
110
|
|
|
69
111
|
$._resizeCanvas = (w, h) => {
|
|
70
112
|
$._setCanvasSize(w, h);
|
|
113
|
+
createMainView();
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
$.pixelDensity = (v) => {
|
|
117
|
+
if (!v || v == $._pixelDensity) return $._pixelDensity;
|
|
118
|
+
$._pixelDensity = v;
|
|
119
|
+
$._setCanvasSize(c.w, c.h);
|
|
120
|
+
createMainView();
|
|
121
|
+
return v;
|
|
71
122
|
};
|
|
72
123
|
|
|
73
124
|
// current color index, used to associate a vertex with a color
|
|
74
|
-
let colorIndex = 0;
|
|
75
125
|
let addColor = (r, g, b, a = 1) => {
|
|
76
126
|
if (typeof r == 'string') r = $.color(r);
|
|
77
127
|
else if (b == undefined) {
|
|
@@ -79,21 +129,35 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
79
129
|
a = g ?? 1;
|
|
80
130
|
g = b = r;
|
|
81
131
|
}
|
|
82
|
-
if (r._q5Color)
|
|
83
|
-
|
|
132
|
+
if (r._q5Color) {
|
|
133
|
+
a = r.a;
|
|
134
|
+
b = r.b;
|
|
135
|
+
g = r.g;
|
|
136
|
+
r = r.r;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
let cs = colorStack,
|
|
140
|
+
i = colorStackIndex;
|
|
141
|
+
cs[i++] = r;
|
|
142
|
+
cs[i++] = g;
|
|
143
|
+
cs[i++] = b;
|
|
144
|
+
cs[i++] = a;
|
|
145
|
+
colorStackIndex = i;
|
|
146
|
+
|
|
84
147
|
colorIndex++;
|
|
85
148
|
};
|
|
86
149
|
|
|
87
|
-
$._fillIndex = $._strokeIndex =
|
|
150
|
+
$._fillIndex = $._strokeIndex = 0;
|
|
151
|
+
$._doFill = $._doStroke = true;
|
|
88
152
|
|
|
89
153
|
$.fill = (r, g, b, a) => {
|
|
90
154
|
addColor(r, g, b, a);
|
|
91
|
-
$._doFill = true;
|
|
155
|
+
$._doFill = $._fillSet = true;
|
|
92
156
|
$._fillIndex = colorIndex;
|
|
93
157
|
};
|
|
94
158
|
$.stroke = (r, g, b, a) => {
|
|
95
159
|
addColor(r, g, b, a);
|
|
96
|
-
$._doStroke = true;
|
|
160
|
+
$._doStroke = $._strokeSet = true;
|
|
97
161
|
$._strokeIndex = colorIndex;
|
|
98
162
|
};
|
|
99
163
|
|
|
@@ -295,6 +359,68 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
295
359
|
return [l, r, t, b];
|
|
296
360
|
};
|
|
297
361
|
|
|
362
|
+
// prettier-ignore
|
|
363
|
+
let blendFactors = [
|
|
364
|
+
'zero', // 0
|
|
365
|
+
'one', // 1
|
|
366
|
+
'src-alpha', // 2
|
|
367
|
+
'one-minus-src-alpha', // 3
|
|
368
|
+
'dst', // 4
|
|
369
|
+
'dst-alpha', // 5
|
|
370
|
+
'one-minus-dst-alpha', // 6
|
|
371
|
+
'one-minus-src' // 7
|
|
372
|
+
];
|
|
373
|
+
let blendOps = [
|
|
374
|
+
'add', // 0
|
|
375
|
+
'subtract', // 1
|
|
376
|
+
'reverse-subtract', // 2
|
|
377
|
+
'min', // 3
|
|
378
|
+
'max' // 4
|
|
379
|
+
];
|
|
380
|
+
|
|
381
|
+
const blendModes = {
|
|
382
|
+
normal: [2, 3, 0, 2, 3, 0],
|
|
383
|
+
// destination_over: [6, 1, 0, 6, 1, 0],
|
|
384
|
+
additive: [1, 1, 0, 1, 1, 0]
|
|
385
|
+
// source_in: [5, 0, 0, 5, 0, 0],
|
|
386
|
+
// destination_in: [0, 2, 0, 0, 2, 0],
|
|
387
|
+
// source_out: [6, 0, 0, 6, 0, 0],
|
|
388
|
+
// destination_out: [0, 3, 0, 0, 3, 0],
|
|
389
|
+
// source_atop: [5, 3, 0, 5, 3, 0],
|
|
390
|
+
// destination_atop: [6, 2, 0, 6, 2, 0]
|
|
391
|
+
};
|
|
392
|
+
|
|
393
|
+
$.blendConfigs = {};
|
|
394
|
+
|
|
395
|
+
for (const [name, mode] of Object.entries(blendModes)) {
|
|
396
|
+
$.blendConfigs[name] = {
|
|
397
|
+
color: {
|
|
398
|
+
srcFactor: blendFactors[mode[0]],
|
|
399
|
+
dstFactor: blendFactors[mode[1]],
|
|
400
|
+
operation: blendOps[mode[2]]
|
|
401
|
+
},
|
|
402
|
+
alpha: {
|
|
403
|
+
srcFactor: blendFactors[mode[3]],
|
|
404
|
+
dstFactor: blendFactors[mode[4]],
|
|
405
|
+
operation: blendOps[mode[5]]
|
|
406
|
+
}
|
|
407
|
+
};
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
$._blendMode = 'normal';
|
|
411
|
+
$.blendMode = (mode) => {
|
|
412
|
+
if (mode == $._blendMode) return;
|
|
413
|
+
if (mode == 'source-over') mode = 'normal';
|
|
414
|
+
if (mode == 'lighter') mode = 'additive';
|
|
415
|
+
mode = mode.toLowerCase().replace(/[ -]/g, '_');
|
|
416
|
+
$._blendMode = mode;
|
|
417
|
+
|
|
418
|
+
for (let i = 0; i < $._pipelines.length; i++) {
|
|
419
|
+
$._pipelineConfigs[i].fragment.targets[0].blend = $.blendConfigs[mode];
|
|
420
|
+
$._pipelines[i] = Q5.device.createRenderPipeline($._pipelineConfigs[i]);
|
|
421
|
+
}
|
|
422
|
+
};
|
|
423
|
+
|
|
298
424
|
$.clear = () => {};
|
|
299
425
|
|
|
300
426
|
$._beginRender = () => {
|
|
@@ -304,7 +430,8 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
304
430
|
label: 'q5-webgpu',
|
|
305
431
|
colorAttachments: [
|
|
306
432
|
{
|
|
307
|
-
view:
|
|
433
|
+
view: mainView,
|
|
434
|
+
resolveTarget: $.ctx.getCurrentTexture().createView(),
|
|
308
435
|
loadOp: 'clear',
|
|
309
436
|
storeOp: 'store'
|
|
310
437
|
}
|
|
@@ -315,58 +442,62 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
315
442
|
$._render = () => {
|
|
316
443
|
if (transformStates.length > 1 || !$._transformBindGroup) {
|
|
317
444
|
let transformBuffer = Q5.device.createBuffer({
|
|
318
|
-
size: transformStates.length * 64, //
|
|
319
|
-
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST
|
|
445
|
+
size: transformStates.length * 64, // 64 is the size of 16 floats
|
|
446
|
+
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
|
|
447
|
+
mappedAtCreation: true
|
|
320
448
|
});
|
|
321
449
|
|
|
322
|
-
|
|
450
|
+
new Float32Array(transformBuffer.getMappedRange()).set(transformStates.flat());
|
|
451
|
+
transformBuffer.unmap();
|
|
323
452
|
|
|
324
453
|
$._transformBindGroup = Q5.device.createBindGroup({
|
|
325
454
|
layout: $._transformLayout,
|
|
326
455
|
entries: [
|
|
327
|
-
{
|
|
328
|
-
|
|
329
|
-
resource: {
|
|
330
|
-
buffer: uniformBuffer
|
|
331
|
-
}
|
|
332
|
-
},
|
|
333
|
-
{
|
|
334
|
-
binding: 1,
|
|
335
|
-
resource: {
|
|
336
|
-
buffer: transformBuffer
|
|
337
|
-
}
|
|
338
|
-
}
|
|
456
|
+
{ binding: 0, resource: { buffer: uniformBuffer } },
|
|
457
|
+
{ binding: 1, resource: { buffer: transformBuffer } }
|
|
339
458
|
]
|
|
340
459
|
});
|
|
341
460
|
}
|
|
342
461
|
|
|
343
462
|
pass.setBindGroup(0, $._transformBindGroup);
|
|
344
463
|
|
|
464
|
+
let colorsBuffer = Q5.device.createBuffer({
|
|
465
|
+
size: colorStackIndex * 4,
|
|
466
|
+
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
|
|
467
|
+
mappedAtCreation: true
|
|
468
|
+
});
|
|
469
|
+
|
|
470
|
+
new Float32Array(colorsBuffer.getMappedRange()).set(colorStack.slice(0, colorStackIndex));
|
|
471
|
+
colorsBuffer.unmap();
|
|
472
|
+
|
|
473
|
+
$._colorsBindGroup = Q5.device.createBindGroup({
|
|
474
|
+
layout: colorsLayout,
|
|
475
|
+
entries: [{ binding: 0, resource: { buffer: colorsBuffer } }]
|
|
476
|
+
});
|
|
477
|
+
|
|
478
|
+
$.pass.setBindGroup(1, $._colorsBindGroup);
|
|
479
|
+
|
|
345
480
|
for (let m of $._hooks.preRender) m();
|
|
346
481
|
|
|
347
|
-
let drawVertOffset = 0
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
482
|
+
let drawVertOffset = 0,
|
|
483
|
+
imageVertOffset = 0,
|
|
484
|
+
textCharOffset = 0,
|
|
485
|
+
curPipelineIndex = -1,
|
|
486
|
+
curTextureIndex = -1;
|
|
352
487
|
|
|
353
|
-
for (let i = 0; i < drawStack.length; i +=
|
|
488
|
+
for (let i = 0; i < drawStack.length; i += 3) {
|
|
354
489
|
let v = drawStack[i + 1];
|
|
355
|
-
|
|
356
|
-
if (drawStack[i] == -1) {
|
|
357
|
-
v();
|
|
358
|
-
continue;
|
|
359
|
-
}
|
|
490
|
+
let o = drawStack[i + 2];
|
|
360
491
|
|
|
361
492
|
if (curPipelineIndex != drawStack[i]) {
|
|
362
493
|
curPipelineIndex = drawStack[i];
|
|
363
|
-
pass.setPipeline($.
|
|
494
|
+
pass.setPipeline($._pipelines[curPipelineIndex]);
|
|
364
495
|
}
|
|
365
496
|
|
|
366
497
|
if (curPipelineIndex == 0) {
|
|
367
498
|
// v is the number of vertices
|
|
368
|
-
pass.
|
|
369
|
-
drawVertOffset +=
|
|
499
|
+
pass.drawIndexed(v, 1, 0, drawVertOffset);
|
|
500
|
+
drawVertOffset += o;
|
|
370
501
|
} else if (curPipelineIndex == 1) {
|
|
371
502
|
if (curTextureIndex != v) {
|
|
372
503
|
// v is the texture index
|
|
@@ -375,7 +506,7 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
375
506
|
pass.draw(6, 1, imageVertOffset);
|
|
376
507
|
imageVertOffset += 6;
|
|
377
508
|
} else if (curPipelineIndex == 2) {
|
|
378
|
-
pass.setBindGroup(2, $.
|
|
509
|
+
pass.setBindGroup(2, $._fonts[o].bindGroup);
|
|
379
510
|
pass.setBindGroup(3, $._textBindGroup);
|
|
380
511
|
|
|
381
512
|
// v is the number of characters in the text
|
|
@@ -391,12 +522,13 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
391
522
|
pass.end();
|
|
392
523
|
let commandBuffer = $.encoder.finish();
|
|
393
524
|
Q5.device.queue.submit([commandBuffer]);
|
|
525
|
+
|
|
394
526
|
q.pass = $.encoder = null;
|
|
395
527
|
|
|
396
528
|
// clear the stacks for the next frame
|
|
397
529
|
$.drawStack.length = 0;
|
|
398
|
-
|
|
399
|
-
|
|
530
|
+
colorIndex = 1;
|
|
531
|
+
colorStackIndex = 8;
|
|
400
532
|
rotation = 0;
|
|
401
533
|
transformStates.length = 1;
|
|
402
534
|
$._transformIndexStack.length = 0;
|