q5 2.5.4 → 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/README.md +31 -337
- package/package.json +2 -2
- package/q5.d.ts +81 -20
- package/q5.js +620 -530
- package/q5.min.js +2 -2
- package/src/q5-2d-canvas.js +2 -4
- package/src/q5-core.js +2 -2
- package/src/q5-util.js +1 -1
- package/src/q5-webgpu-canvas.js +195 -55
- 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 +27 -59
package/q5.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* q5.js
|
|
3
|
-
* @version 2.
|
|
3
|
+
* @version 2.6
|
|
4
4
|
* @author quinton-ashley, Tezumie, and LingDong-
|
|
5
5
|
* @license LGPL-3.0
|
|
6
6
|
* @class Q5
|
|
@@ -259,7 +259,7 @@ Q5._nodejs = typeof process == 'object';
|
|
|
259
259
|
|
|
260
260
|
Q5._instanceCount = 0;
|
|
261
261
|
Q5._friendlyError = (msg, func) => {
|
|
262
|
-
|
|
262
|
+
console.error(func + ': ' + msg);
|
|
263
263
|
};
|
|
264
264
|
Q5._validateParameters = () => true;
|
|
265
265
|
|
|
@@ -635,8 +635,7 @@ Q5.renderers.q2d.canvas = ($, q) => {
|
|
|
635
635
|
};
|
|
636
636
|
|
|
637
637
|
$.fill = function (c) {
|
|
638
|
-
$._doFill = true;
|
|
639
|
-
$._fillSet = true;
|
|
638
|
+
$._doFill = $._fillSet = true;
|
|
640
639
|
if (Q5.Color) {
|
|
641
640
|
if (!c._q5Color) {
|
|
642
641
|
if (typeof c != 'string') c = $.color(...arguments);
|
|
@@ -648,8 +647,7 @@ Q5.renderers.q2d.canvas = ($, q) => {
|
|
|
648
647
|
};
|
|
649
648
|
$.noFill = () => ($._doFill = false);
|
|
650
649
|
$.stroke = function (c) {
|
|
651
|
-
$._doStroke = true;
|
|
652
|
-
$._strokeSet = true;
|
|
650
|
+
$._doStroke = $._strokeSet = true;
|
|
653
651
|
if (Q5.Color) {
|
|
654
652
|
if (!c._q5Color) {
|
|
655
653
|
if (typeof c != 'string') c = $.color(...arguments);
|
|
@@ -2759,7 +2757,7 @@ Q5.modules.util = ($, q) => {
|
|
|
2759
2757
|
return ret;
|
|
2760
2758
|
};
|
|
2761
2759
|
|
|
2762
|
-
$.
|
|
2760
|
+
$.loadText = (path, cb) => $._loadFile(path, cb, 'text');
|
|
2763
2761
|
$.loadJSON = (path, cb) => $._loadFile(path, cb, 'json');
|
|
2764
2762
|
$.loadCSV = (path, cb) => $._loadFile(path, cb, 'csv');
|
|
2765
2763
|
|
|
@@ -3075,18 +3073,30 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
3075
3073
|
c.width = $.width = 500;
|
|
3076
3074
|
c.height = $.height = 500;
|
|
3077
3075
|
|
|
3078
|
-
if ($.colorMode) $.colorMode('rgb',
|
|
3076
|
+
if ($.colorMode) $.colorMode('rgb', 1);
|
|
3079
3077
|
|
|
3080
|
-
let pass
|
|
3078
|
+
let pass,
|
|
3079
|
+
mainView,
|
|
3080
|
+
colorsLayout,
|
|
3081
|
+
colorIndex = 1,
|
|
3082
|
+
colorStackIndex = 8;
|
|
3081
3083
|
|
|
3082
|
-
$.
|
|
3084
|
+
$._pipelineConfigs = [];
|
|
3085
|
+
$._pipelines = [];
|
|
3083
3086
|
|
|
3084
3087
|
// local variables used for slightly better performance
|
|
3085
3088
|
// stores pipeline shifts and vertex counts/image indices
|
|
3086
3089
|
let drawStack = ($.drawStack = []);
|
|
3087
3090
|
|
|
3088
3091
|
// colors used for each draw call
|
|
3089
|
-
|
|
3092
|
+
|
|
3093
|
+
let colorStack = ($.colorStack = new Float32Array(1e6));
|
|
3094
|
+
|
|
3095
|
+
// prettier-ignore
|
|
3096
|
+
colorStack.set([
|
|
3097
|
+
0, 0, 0, 1, // black
|
|
3098
|
+
1, 1, 1, 1 // white
|
|
3099
|
+
]);
|
|
3090
3100
|
|
|
3091
3101
|
$._transformLayout = Q5.device.createBindGroupLayout({
|
|
3092
3102
|
label: 'transformLayout',
|
|
@@ -3110,32 +3120,70 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
3110
3120
|
]
|
|
3111
3121
|
});
|
|
3112
3122
|
|
|
3113
|
-
|
|
3123
|
+
colorsLayout = Q5.device.createBindGroupLayout({
|
|
3124
|
+
label: 'colorsLayout',
|
|
3125
|
+
entries: [
|
|
3126
|
+
{
|
|
3127
|
+
binding: 0,
|
|
3128
|
+
visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT,
|
|
3129
|
+
buffer: {
|
|
3130
|
+
type: 'read-only-storage',
|
|
3131
|
+
hasDynamicOffset: false
|
|
3132
|
+
}
|
|
3133
|
+
}
|
|
3134
|
+
]
|
|
3135
|
+
});
|
|
3136
|
+
|
|
3137
|
+
$.bindGroupLayouts = [$._transformLayout, colorsLayout];
|
|
3114
3138
|
|
|
3115
3139
|
let uniformBuffer = Q5.device.createBuffer({
|
|
3116
3140
|
size: 8, // Size of two floats
|
|
3117
3141
|
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
|
|
3118
3142
|
});
|
|
3119
3143
|
|
|
3144
|
+
let createMainView = () => {
|
|
3145
|
+
mainView = Q5.device
|
|
3146
|
+
.createTexture({
|
|
3147
|
+
size: [$.canvas.width, $.canvas.height],
|
|
3148
|
+
sampleCount: 4,
|
|
3149
|
+
format: 'bgra8unorm',
|
|
3150
|
+
usage: GPUTextureUsage.RENDER_ATTACHMENT
|
|
3151
|
+
})
|
|
3152
|
+
.createView();
|
|
3153
|
+
};
|
|
3154
|
+
|
|
3120
3155
|
$._createCanvas = (w, h, opt) => {
|
|
3121
3156
|
q.ctx = q.drawingContext = c.getContext('webgpu');
|
|
3122
3157
|
|
|
3123
3158
|
opt.format ??= navigator.gpu.getPreferredCanvasFormat();
|
|
3124
3159
|
opt.device ??= Q5.device;
|
|
3125
3160
|
|
|
3161
|
+
// needed for other blend modes but couldn't get it working
|
|
3162
|
+
// opt.alphaMode = 'premultiplied';
|
|
3163
|
+
|
|
3126
3164
|
$.ctx.configure(opt);
|
|
3127
3165
|
|
|
3128
3166
|
Q5.device.queue.writeBuffer(uniformBuffer, 0, new Float32Array([$.canvas.hw, $.canvas.hh]));
|
|
3129
3167
|
|
|
3168
|
+
createMainView();
|
|
3169
|
+
|
|
3130
3170
|
return c;
|
|
3131
3171
|
};
|
|
3132
3172
|
|
|
3133
3173
|
$._resizeCanvas = (w, h) => {
|
|
3134
3174
|
$._setCanvasSize(w, h);
|
|
3175
|
+
createMainView();
|
|
3176
|
+
};
|
|
3177
|
+
|
|
3178
|
+
$.pixelDensity = (v) => {
|
|
3179
|
+
if (!v || v == $._pixelDensity) return $._pixelDensity;
|
|
3180
|
+
$._pixelDensity = v;
|
|
3181
|
+
$._setCanvasSize(c.w, c.h);
|
|
3182
|
+
createMainView();
|
|
3183
|
+
return v;
|
|
3135
3184
|
};
|
|
3136
3185
|
|
|
3137
3186
|
// current color index, used to associate a vertex with a color
|
|
3138
|
-
let colorIndex = 0;
|
|
3139
3187
|
let addColor = (r, g, b, a = 1) => {
|
|
3140
3188
|
if (typeof r == 'string') r = $.color(r);
|
|
3141
3189
|
else if (b == undefined) {
|
|
@@ -3143,21 +3191,35 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
3143
3191
|
a = g ?? 1;
|
|
3144
3192
|
g = b = r;
|
|
3145
3193
|
}
|
|
3146
|
-
if (r._q5Color)
|
|
3147
|
-
|
|
3194
|
+
if (r._q5Color) {
|
|
3195
|
+
a = r.a;
|
|
3196
|
+
b = r.b;
|
|
3197
|
+
g = r.g;
|
|
3198
|
+
r = r.r;
|
|
3199
|
+
}
|
|
3200
|
+
|
|
3201
|
+
let cs = colorStack,
|
|
3202
|
+
i = colorStackIndex;
|
|
3203
|
+
cs[i++] = r;
|
|
3204
|
+
cs[i++] = g;
|
|
3205
|
+
cs[i++] = b;
|
|
3206
|
+
cs[i++] = a;
|
|
3207
|
+
colorStackIndex = i;
|
|
3208
|
+
|
|
3148
3209
|
colorIndex++;
|
|
3149
3210
|
};
|
|
3150
3211
|
|
|
3151
|
-
$._fillIndex = $._strokeIndex =
|
|
3212
|
+
$._fillIndex = $._strokeIndex = 0;
|
|
3213
|
+
$._doFill = $._doStroke = true;
|
|
3152
3214
|
|
|
3153
3215
|
$.fill = (r, g, b, a) => {
|
|
3154
3216
|
addColor(r, g, b, a);
|
|
3155
|
-
$._doFill = true;
|
|
3217
|
+
$._doFill = $._fillSet = true;
|
|
3156
3218
|
$._fillIndex = colorIndex;
|
|
3157
3219
|
};
|
|
3158
3220
|
$.stroke = (r, g, b, a) => {
|
|
3159
3221
|
addColor(r, g, b, a);
|
|
3160
|
-
$._doStroke = true;
|
|
3222
|
+
$._doStroke = $._strokeSet = true;
|
|
3161
3223
|
$._strokeIndex = colorIndex;
|
|
3162
3224
|
};
|
|
3163
3225
|
|
|
@@ -3185,7 +3247,7 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
3185
3247
|
$._matrixDirty = false;
|
|
3186
3248
|
|
|
3187
3249
|
// array to store transformation matrices for the render pass
|
|
3188
|
-
|
|
3250
|
+
let transformStates = [$._matrix.slice()];
|
|
3189
3251
|
|
|
3190
3252
|
// stack to keep track of transformation matrix indexes
|
|
3191
3253
|
$._transformIndexStack = [];
|
|
@@ -3302,8 +3364,8 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
3302
3364
|
|
|
3303
3365
|
// Function to save the current matrix state if dirty
|
|
3304
3366
|
$._saveMatrix = () => {
|
|
3305
|
-
|
|
3306
|
-
$._transformIndex =
|
|
3367
|
+
transformStates.push($._matrix.slice());
|
|
3368
|
+
$._transformIndex = transformStates.length - 1;
|
|
3307
3369
|
$._matrixDirty = false;
|
|
3308
3370
|
};
|
|
3309
3371
|
|
|
@@ -3318,7 +3380,7 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
3318
3380
|
}
|
|
3319
3381
|
// Pop the last matrix index and set it as the current matrix index
|
|
3320
3382
|
let idx = $._transformIndexStack.pop();
|
|
3321
|
-
$._matrix =
|
|
3383
|
+
$._matrix = transformStates[idx].slice();
|
|
3322
3384
|
$._transformIndex = idx;
|
|
3323
3385
|
$._matrixDirty = false;
|
|
3324
3386
|
};
|
|
@@ -3359,6 +3421,68 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
3359
3421
|
return [l, r, t, b];
|
|
3360
3422
|
};
|
|
3361
3423
|
|
|
3424
|
+
// prettier-ignore
|
|
3425
|
+
let blendFactors = [
|
|
3426
|
+
'zero', // 0
|
|
3427
|
+
'one', // 1
|
|
3428
|
+
'src-alpha', // 2
|
|
3429
|
+
'one-minus-src-alpha', // 3
|
|
3430
|
+
'dst', // 4
|
|
3431
|
+
'dst-alpha', // 5
|
|
3432
|
+
'one-minus-dst-alpha', // 6
|
|
3433
|
+
'one-minus-src' // 7
|
|
3434
|
+
];
|
|
3435
|
+
let blendOps = [
|
|
3436
|
+
'add', // 0
|
|
3437
|
+
'subtract', // 1
|
|
3438
|
+
'reverse-subtract', // 2
|
|
3439
|
+
'min', // 3
|
|
3440
|
+
'max' // 4
|
|
3441
|
+
];
|
|
3442
|
+
|
|
3443
|
+
const blendModes = {
|
|
3444
|
+
normal: [2, 3, 0, 2, 3, 0],
|
|
3445
|
+
// destination_over: [6, 1, 0, 6, 1, 0],
|
|
3446
|
+
additive: [1, 1, 0, 1, 1, 0]
|
|
3447
|
+
// source_in: [5, 0, 0, 5, 0, 0],
|
|
3448
|
+
// destination_in: [0, 2, 0, 0, 2, 0],
|
|
3449
|
+
// source_out: [6, 0, 0, 6, 0, 0],
|
|
3450
|
+
// destination_out: [0, 3, 0, 0, 3, 0],
|
|
3451
|
+
// source_atop: [5, 3, 0, 5, 3, 0],
|
|
3452
|
+
// destination_atop: [6, 2, 0, 6, 2, 0]
|
|
3453
|
+
};
|
|
3454
|
+
|
|
3455
|
+
$.blendConfigs = {};
|
|
3456
|
+
|
|
3457
|
+
for (const [name, mode] of Object.entries(blendModes)) {
|
|
3458
|
+
$.blendConfigs[name] = {
|
|
3459
|
+
color: {
|
|
3460
|
+
srcFactor: blendFactors[mode[0]],
|
|
3461
|
+
dstFactor: blendFactors[mode[1]],
|
|
3462
|
+
operation: blendOps[mode[2]]
|
|
3463
|
+
},
|
|
3464
|
+
alpha: {
|
|
3465
|
+
srcFactor: blendFactors[mode[3]],
|
|
3466
|
+
dstFactor: blendFactors[mode[4]],
|
|
3467
|
+
operation: blendOps[mode[5]]
|
|
3468
|
+
}
|
|
3469
|
+
};
|
|
3470
|
+
}
|
|
3471
|
+
|
|
3472
|
+
$._blendMode = 'normal';
|
|
3473
|
+
$.blendMode = (mode) => {
|
|
3474
|
+
if (mode == $._blendMode) return;
|
|
3475
|
+
if (mode == 'source-over') mode = 'normal';
|
|
3476
|
+
if (mode == 'lighter') mode = 'additive';
|
|
3477
|
+
mode = mode.toLowerCase().replace(/[ -]/g, '_');
|
|
3478
|
+
$._blendMode = mode;
|
|
3479
|
+
|
|
3480
|
+
for (let i = 0; i < $._pipelines.length; i++) {
|
|
3481
|
+
$._pipelineConfigs[i].fragment.targets[0].blend = $.blendConfigs[mode];
|
|
3482
|
+
$._pipelines[i] = Q5.device.createRenderPipeline($._pipelineConfigs[i]);
|
|
3483
|
+
}
|
|
3484
|
+
};
|
|
3485
|
+
|
|
3362
3486
|
$.clear = () => {};
|
|
3363
3487
|
|
|
3364
3488
|
$._beginRender = () => {
|
|
@@ -3368,7 +3492,8 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
3368
3492
|
label: 'q5-webgpu',
|
|
3369
3493
|
colorAttachments: [
|
|
3370
3494
|
{
|
|
3371
|
-
view:
|
|
3495
|
+
view: mainView,
|
|
3496
|
+
resolveTarget: $.ctx.getCurrentTexture().createView(),
|
|
3372
3497
|
loadOp: 'clear',
|
|
3373
3498
|
storeOp: 'store'
|
|
3374
3499
|
}
|
|
@@ -3379,58 +3504,62 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
3379
3504
|
$._render = () => {
|
|
3380
3505
|
if (transformStates.length > 1 || !$._transformBindGroup) {
|
|
3381
3506
|
let transformBuffer = Q5.device.createBuffer({
|
|
3382
|
-
size: transformStates.length * 64, //
|
|
3383
|
-
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST
|
|
3507
|
+
size: transformStates.length * 64, // 64 is the size of 16 floats
|
|
3508
|
+
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
|
|
3509
|
+
mappedAtCreation: true
|
|
3384
3510
|
});
|
|
3385
3511
|
|
|
3386
|
-
|
|
3512
|
+
new Float32Array(transformBuffer.getMappedRange()).set(transformStates.flat());
|
|
3513
|
+
transformBuffer.unmap();
|
|
3387
3514
|
|
|
3388
3515
|
$._transformBindGroup = Q5.device.createBindGroup({
|
|
3389
3516
|
layout: $._transformLayout,
|
|
3390
3517
|
entries: [
|
|
3391
|
-
{
|
|
3392
|
-
|
|
3393
|
-
resource: {
|
|
3394
|
-
buffer: uniformBuffer
|
|
3395
|
-
}
|
|
3396
|
-
},
|
|
3397
|
-
{
|
|
3398
|
-
binding: 1,
|
|
3399
|
-
resource: {
|
|
3400
|
-
buffer: transformBuffer
|
|
3401
|
-
}
|
|
3402
|
-
}
|
|
3518
|
+
{ binding: 0, resource: { buffer: uniformBuffer } },
|
|
3519
|
+
{ binding: 1, resource: { buffer: transformBuffer } }
|
|
3403
3520
|
]
|
|
3404
3521
|
});
|
|
3405
3522
|
}
|
|
3406
3523
|
|
|
3407
3524
|
pass.setBindGroup(0, $._transformBindGroup);
|
|
3408
3525
|
|
|
3526
|
+
let colorsBuffer = Q5.device.createBuffer({
|
|
3527
|
+
size: colorStackIndex * 4,
|
|
3528
|
+
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
|
|
3529
|
+
mappedAtCreation: true
|
|
3530
|
+
});
|
|
3531
|
+
|
|
3532
|
+
new Float32Array(colorsBuffer.getMappedRange()).set(colorStack.slice(0, colorStackIndex));
|
|
3533
|
+
colorsBuffer.unmap();
|
|
3534
|
+
|
|
3535
|
+
$._colorsBindGroup = Q5.device.createBindGroup({
|
|
3536
|
+
layout: colorsLayout,
|
|
3537
|
+
entries: [{ binding: 0, resource: { buffer: colorsBuffer } }]
|
|
3538
|
+
});
|
|
3539
|
+
|
|
3540
|
+
$.pass.setBindGroup(1, $._colorsBindGroup);
|
|
3541
|
+
|
|
3409
3542
|
for (let m of $._hooks.preRender) m();
|
|
3410
3543
|
|
|
3411
|
-
let drawVertOffset = 0
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3544
|
+
let drawVertOffset = 0,
|
|
3545
|
+
imageVertOffset = 0,
|
|
3546
|
+
textCharOffset = 0,
|
|
3547
|
+
curPipelineIndex = -1,
|
|
3548
|
+
curTextureIndex = -1;
|
|
3416
3549
|
|
|
3417
|
-
for (let i = 0; i < drawStack.length; i +=
|
|
3550
|
+
for (let i = 0; i < drawStack.length; i += 3) {
|
|
3418
3551
|
let v = drawStack[i + 1];
|
|
3419
|
-
|
|
3420
|
-
if (drawStack[i] == -1) {
|
|
3421
|
-
v();
|
|
3422
|
-
continue;
|
|
3423
|
-
}
|
|
3552
|
+
let o = drawStack[i + 2];
|
|
3424
3553
|
|
|
3425
3554
|
if (curPipelineIndex != drawStack[i]) {
|
|
3426
3555
|
curPipelineIndex = drawStack[i];
|
|
3427
|
-
pass.setPipeline($.
|
|
3556
|
+
pass.setPipeline($._pipelines[curPipelineIndex]);
|
|
3428
3557
|
}
|
|
3429
3558
|
|
|
3430
3559
|
if (curPipelineIndex == 0) {
|
|
3431
3560
|
// v is the number of vertices
|
|
3432
|
-
pass.
|
|
3433
|
-
drawVertOffset +=
|
|
3561
|
+
pass.drawIndexed(v, 1, 0, drawVertOffset);
|
|
3562
|
+
drawVertOffset += o;
|
|
3434
3563
|
} else if (curPipelineIndex == 1) {
|
|
3435
3564
|
if (curTextureIndex != v) {
|
|
3436
3565
|
// v is the texture index
|
|
@@ -3439,7 +3568,7 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
3439
3568
|
pass.draw(6, 1, imageVertOffset);
|
|
3440
3569
|
imageVertOffset += 6;
|
|
3441
3570
|
} else if (curPipelineIndex == 2) {
|
|
3442
|
-
pass.setBindGroup(2, $.
|
|
3571
|
+
pass.setBindGroup(2, $._fonts[o].bindGroup);
|
|
3443
3572
|
pass.setBindGroup(3, $._textBindGroup);
|
|
3444
3573
|
|
|
3445
3574
|
// v is the number of characters in the text
|
|
@@ -3455,69 +3584,84 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
3455
3584
|
pass.end();
|
|
3456
3585
|
let commandBuffer = $.encoder.finish();
|
|
3457
3586
|
Q5.device.queue.submit([commandBuffer]);
|
|
3587
|
+
|
|
3458
3588
|
q.pass = $.encoder = null;
|
|
3459
3589
|
|
|
3460
3590
|
// clear the stacks for the next frame
|
|
3461
3591
|
$.drawStack.length = 0;
|
|
3462
|
-
|
|
3463
|
-
|
|
3592
|
+
colorIndex = 1;
|
|
3593
|
+
colorStackIndex = 8;
|
|
3464
3594
|
rotation = 0;
|
|
3465
|
-
|
|
3595
|
+
transformStates.length = 1;
|
|
3466
3596
|
$._transformIndexStack.length = 0;
|
|
3467
3597
|
};
|
|
3468
3598
|
};
|
|
3469
3599
|
|
|
3470
|
-
Q5.
|
|
3471
|
-
if (!scope || scope == 'global') Q5._hasGlobal = true;
|
|
3600
|
+
Q5.initWebGPU = async () => {
|
|
3472
3601
|
if (!navigator.gpu) {
|
|
3473
3602
|
console.warn('q5 WebGPU not supported on this browser!');
|
|
3603
|
+
return false;
|
|
3604
|
+
}
|
|
3605
|
+
if (!Q5.device) {
|
|
3606
|
+
let adapter = await navigator.gpu.requestAdapter();
|
|
3607
|
+
if (!adapter) throw new Error('No appropriate GPUAdapter found.');
|
|
3608
|
+
Q5.device = await adapter.requestDevice();
|
|
3609
|
+
}
|
|
3610
|
+
return true;
|
|
3611
|
+
};
|
|
3612
|
+
|
|
3613
|
+
Q5.webgpu = async function (scope, parent) {
|
|
3614
|
+
if (!scope || scope == 'global') Q5._hasGlobal = true;
|
|
3615
|
+
if (!(await Q5.initWebGPU())) {
|
|
3474
3616
|
let q = new Q5(scope, parent);
|
|
3475
3617
|
q.colorMode('rgb', 1);
|
|
3476
3618
|
q._beginRender = () => q.translate(q.canvas.hw, q.canvas.hh);
|
|
3477
3619
|
return q;
|
|
3478
3620
|
}
|
|
3479
|
-
let adapter = await navigator.gpu.requestAdapter();
|
|
3480
|
-
if (!adapter) throw new Error('No appropriate GPUAdapter found.');
|
|
3481
|
-
Q5.device = await adapter.requestDevice();
|
|
3482
|
-
|
|
3483
3621
|
return new Q5(scope, parent, 'webgpu');
|
|
3484
3622
|
};
|
|
3485
3623
|
Q5.renderers.webgpu.drawing = ($, q) => {
|
|
3486
|
-
let c = $.canvas
|
|
3487
|
-
|
|
3488
|
-
|
|
3489
|
-
|
|
3490
|
-
|
|
3491
|
-
|
|
3492
|
-
|
|
3493
|
-
|
|
3624
|
+
let c = $.canvas,
|
|
3625
|
+
drawStack = $.drawStack,
|
|
3626
|
+
vertexStack = new Float32Array(1e7),
|
|
3627
|
+
indexStack = new Uint32Array(1e6),
|
|
3628
|
+
vertIndex = 0,
|
|
3629
|
+
vertCount = 0,
|
|
3630
|
+
idxBufferIndex = 0,
|
|
3631
|
+
colorIndex;
|
|
3494
3632
|
|
|
3495
3633
|
let vertexShader = Q5.device.createShaderModule({
|
|
3496
3634
|
label: 'drawingVertexShader',
|
|
3497
3635
|
code: `
|
|
3636
|
+
struct VertexInput {
|
|
3637
|
+
@location(0) pos: vec2f,
|
|
3638
|
+
@location(1) colorIndex: f32,
|
|
3639
|
+
@location(2) transformIndex: f32
|
|
3640
|
+
}
|
|
3498
3641
|
struct VertexOutput {
|
|
3499
3642
|
@builtin(position) position: vec4f,
|
|
3500
|
-
@location(0)
|
|
3501
|
-
}
|
|
3502
|
-
|
|
3643
|
+
@location(0) color: vec4f
|
|
3644
|
+
}
|
|
3503
3645
|
struct Uniforms {
|
|
3504
3646
|
halfWidth: f32,
|
|
3505
3647
|
halfHeight: f32
|
|
3506
|
-
}
|
|
3648
|
+
}
|
|
3507
3649
|
|
|
3508
3650
|
@group(0) @binding(0) var<uniform> uniforms: Uniforms;
|
|
3509
|
-
@group(0) @binding(1) var<storage
|
|
3651
|
+
@group(0) @binding(1) var<storage> transforms: array<mat4x4<f32>>;
|
|
3652
|
+
|
|
3653
|
+
@group(1) @binding(0) var<storage> colors : array<vec4f>;
|
|
3510
3654
|
|
|
3511
3655
|
@vertex
|
|
3512
|
-
fn vertexMain(
|
|
3513
|
-
var vert = vec4f(pos, 0.0, 1.0);
|
|
3514
|
-
vert = transforms[i32(transformIndex)] * vert;
|
|
3656
|
+
fn vertexMain(input: VertexInput) -> VertexOutput {
|
|
3657
|
+
var vert = vec4f(input.pos, 0.0, 1.0);
|
|
3658
|
+
vert = transforms[i32(input.transformIndex)] * vert;
|
|
3515
3659
|
vert.x /= uniforms.halfWidth;
|
|
3516
3660
|
vert.y /= uniforms.halfHeight;
|
|
3517
3661
|
|
|
3518
3662
|
var output: VertexOutput;
|
|
3519
3663
|
output.position = vert;
|
|
3520
|
-
output.
|
|
3664
|
+
output.color = colors[i32(input.colorIndex)];
|
|
3521
3665
|
return output;
|
|
3522
3666
|
}
|
|
3523
3667
|
`
|
|
@@ -3526,32 +3670,13 @@ fn vertexMain(@location(0) pos: vec2f, @location(1) colorIndex: f32, @location(2
|
|
|
3526
3670
|
let fragmentShader = Q5.device.createShaderModule({
|
|
3527
3671
|
label: 'drawingFragmentShader',
|
|
3528
3672
|
code: `
|
|
3529
|
-
@group(1) @binding(0) var<storage, read> colors : array<vec4f>;
|
|
3530
|
-
|
|
3531
3673
|
@fragment
|
|
3532
|
-
fn fragmentMain(@location(0)
|
|
3533
|
-
|
|
3534
|
-
return mix(colors[index], colors[index + 1], fract(colorIndex));
|
|
3674
|
+
fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
|
|
3675
|
+
return color;
|
|
3535
3676
|
}
|
|
3536
3677
|
`
|
|
3537
3678
|
});
|
|
3538
3679
|
|
|
3539
|
-
colorsLayout = Q5.device.createBindGroupLayout({
|
|
3540
|
-
label: 'colorsLayout',
|
|
3541
|
-
entries: [
|
|
3542
|
-
{
|
|
3543
|
-
binding: 0,
|
|
3544
|
-
visibility: GPUShaderStage.FRAGMENT,
|
|
3545
|
-
buffer: {
|
|
3546
|
-
type: 'read-only-storage',
|
|
3547
|
-
hasDynamicOffset: false
|
|
3548
|
-
}
|
|
3549
|
-
}
|
|
3550
|
-
]
|
|
3551
|
-
});
|
|
3552
|
-
|
|
3553
|
-
$.bindGroupLayouts.push(colorsLayout);
|
|
3554
|
-
|
|
3555
3680
|
let vertexBufferLayout = {
|
|
3556
3681
|
arrayStride: 16, // 2 coordinates + 1 color index + 1 transform index * 4 bytes each
|
|
3557
3682
|
attributes: [
|
|
@@ -3561,193 +3686,212 @@ fn fragmentMain(@location(0) colorIndex: f32) -> @location(0) vec4f {
|
|
|
3561
3686
|
]
|
|
3562
3687
|
};
|
|
3563
3688
|
|
|
3564
|
-
// prettier-ignore
|
|
3565
|
-
let blendFactors = [
|
|
3566
|
-
'zero', // 0
|
|
3567
|
-
'one', // 1
|
|
3568
|
-
'src-alpha', // 2
|
|
3569
|
-
'one-minus-src-alpha', // 3
|
|
3570
|
-
'dst', // 4
|
|
3571
|
-
'dst-alpha', // 5
|
|
3572
|
-
'one-minus-dst-alpha', // 6
|
|
3573
|
-
'one-minus-src' // 7
|
|
3574
|
-
];
|
|
3575
|
-
let blendOps = [
|
|
3576
|
-
'add', // 0
|
|
3577
|
-
'subtract', // 1
|
|
3578
|
-
'reverse-subtract', // 2
|
|
3579
|
-
'min', // 3
|
|
3580
|
-
'max' // 4
|
|
3581
|
-
];
|
|
3582
|
-
|
|
3583
|
-
const blendModes = {
|
|
3584
|
-
normal: [2, 3, 0, 2, 3, 0],
|
|
3585
|
-
lighter: [2, 1, 0, 2, 1, 0],
|
|
3586
|
-
subtract: [2, 1, 2, 2, 1, 2],
|
|
3587
|
-
multiply: [4, 0, 0, 5, 0, 0],
|
|
3588
|
-
screen: [1, 3, 0, 1, 3, 0],
|
|
3589
|
-
darken: [1, 3, 3, 1, 3, 3],
|
|
3590
|
-
lighten: [1, 3, 4, 1, 3, 4],
|
|
3591
|
-
overlay: [2, 3, 0, 2, 3, 0],
|
|
3592
|
-
hard_light: [2, 3, 0, 2, 3, 0],
|
|
3593
|
-
soft_light: [2, 3, 0, 2, 3, 0],
|
|
3594
|
-
difference: [2, 3, 2, 2, 3, 2],
|
|
3595
|
-
exclusion: [2, 3, 0, 2, 3, 0],
|
|
3596
|
-
color_dodge: [1, 7, 0, 1, 7, 0],
|
|
3597
|
-
color_burn: [6, 1, 0, 6, 1, 0],
|
|
3598
|
-
linear_dodge: [2, 1, 0, 2, 1, 0],
|
|
3599
|
-
linear_burn: [2, 7, 1, 2, 7, 1],
|
|
3600
|
-
vivid_light: [2, 7, 0, 2, 7, 0],
|
|
3601
|
-
pin_light: [2, 7, 0, 2, 7, 0],
|
|
3602
|
-
hard_mix: [2, 7, 0, 2, 7, 0]
|
|
3603
|
-
};
|
|
3604
|
-
|
|
3605
|
-
$.blendConfigs = {};
|
|
3606
|
-
|
|
3607
|
-
for (const [name, mode] of Object.entries(blendModes)) {
|
|
3608
|
-
$.blendConfigs[name] = {
|
|
3609
|
-
color: {
|
|
3610
|
-
srcFactor: blendFactors[mode[0]],
|
|
3611
|
-
dstFactor: blendFactors[mode[1]],
|
|
3612
|
-
operation: blendOps[mode[2]]
|
|
3613
|
-
},
|
|
3614
|
-
alpha: {
|
|
3615
|
-
srcFactor: blendFactors[mode[3]],
|
|
3616
|
-
dstFactor: blendFactors[mode[4]],
|
|
3617
|
-
operation: blendOps[mode[5]]
|
|
3618
|
-
}
|
|
3619
|
-
};
|
|
3620
|
-
}
|
|
3621
|
-
|
|
3622
|
-
$._blendMode = 'normal';
|
|
3623
|
-
$.blendMode = (mode) => {
|
|
3624
|
-
if (mode == $._blendMode) return;
|
|
3625
|
-
if (mode == 'source-over') mode = 'normal';
|
|
3626
|
-
mode = mode.toLowerCase().replace(/[ -]/g, '_');
|
|
3627
|
-
$._blendMode = mode;
|
|
3628
|
-
$.pipelines[0] = $._createPipeline($.blendConfigs[mode]);
|
|
3629
|
-
};
|
|
3630
|
-
|
|
3631
3689
|
let pipelineLayout = Q5.device.createPipelineLayout({
|
|
3632
3690
|
label: 'drawingPipelineLayout',
|
|
3633
3691
|
bindGroupLayouts: $.bindGroupLayouts
|
|
3634
3692
|
});
|
|
3635
3693
|
|
|
3636
|
-
$.
|
|
3637
|
-
|
|
3638
|
-
|
|
3639
|
-
|
|
3640
|
-
|
|
3641
|
-
|
|
3642
|
-
|
|
3643
|
-
|
|
3644
|
-
|
|
3645
|
-
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
|
|
3694
|
+
$._pipelineConfigs[0] = {
|
|
3695
|
+
label: 'drawingPipeline',
|
|
3696
|
+
layout: pipelineLayout,
|
|
3697
|
+
vertex: {
|
|
3698
|
+
module: vertexShader,
|
|
3699
|
+
entryPoint: 'vertexMain',
|
|
3700
|
+
buffers: [vertexBufferLayout]
|
|
3701
|
+
},
|
|
3702
|
+
fragment: {
|
|
3703
|
+
module: fragmentShader,
|
|
3704
|
+
entryPoint: 'fragmentMain',
|
|
3705
|
+
targets: [{ format: 'bgra8unorm', blend: $.blendConfigs.normal }]
|
|
3706
|
+
},
|
|
3707
|
+
primitive: { topology: 'triangle-list' },
|
|
3708
|
+
multisample: {
|
|
3709
|
+
count: 4
|
|
3710
|
+
}
|
|
3711
|
+
};
|
|
3712
|
+
|
|
3713
|
+
$._pipelines[0] = Q5.device.createRenderPipeline($._pipelineConfigs[0]);
|
|
3714
|
+
|
|
3715
|
+
const addVert = (x, y, ci, ti) => {
|
|
3716
|
+
let v = vertexStack,
|
|
3717
|
+
i = vertIndex;
|
|
3718
|
+
v[i++] = x;
|
|
3719
|
+
v[i++] = y;
|
|
3720
|
+
v[i++] = ci;
|
|
3721
|
+
v[i++] = ti;
|
|
3722
|
+
vertIndex = i;
|
|
3723
|
+
vertCount++;
|
|
3724
|
+
};
|
|
3725
|
+
|
|
3726
|
+
const addIndex = (i1, i2, i3) => {
|
|
3727
|
+
let is = indexStack,
|
|
3728
|
+
ii = idxBufferIndex;
|
|
3729
|
+
is[ii++] = i1;
|
|
3730
|
+
is[ii++] = i2;
|
|
3731
|
+
is[ii++] = i3;
|
|
3732
|
+
idxBufferIndex = ii;
|
|
3733
|
+
};
|
|
3734
|
+
|
|
3735
|
+
const addQuad = (x1, y1, x2, y2, x3, y3, x4, y4, ci, ti) => {
|
|
3736
|
+
let v = vertexStack,
|
|
3737
|
+
i = vertIndex;
|
|
3738
|
+
|
|
3739
|
+
let i1 = vertCount++;
|
|
3740
|
+
v[i++] = x1;
|
|
3741
|
+
v[i++] = y1;
|
|
3742
|
+
v[i++] = ci;
|
|
3743
|
+
v[i++] = ti;
|
|
3744
|
+
|
|
3745
|
+
let i2 = vertCount++;
|
|
3746
|
+
v[i++] = x2;
|
|
3747
|
+
v[i++] = y2;
|
|
3748
|
+
v[i++] = ci;
|
|
3749
|
+
v[i++] = ti;
|
|
3750
|
+
|
|
3751
|
+
let i3 = vertCount++;
|
|
3752
|
+
v[i++] = x3;
|
|
3753
|
+
v[i++] = y3;
|
|
3754
|
+
v[i++] = ci;
|
|
3755
|
+
v[i++] = ti;
|
|
3756
|
+
|
|
3757
|
+
let i4 = vertCount++;
|
|
3758
|
+
v[i++] = x4;
|
|
3759
|
+
v[i++] = y4;
|
|
3760
|
+
v[i++] = ci;
|
|
3761
|
+
v[i++] = ti;
|
|
3762
|
+
|
|
3763
|
+
vertIndex = i;
|
|
3764
|
+
|
|
3765
|
+
let is = indexStack,
|
|
3766
|
+
ii = idxBufferIndex;
|
|
3767
|
+
is[ii++] = i1;
|
|
3768
|
+
is[ii++] = i2;
|
|
3769
|
+
is[ii++] = i3;
|
|
3770
|
+
is[ii++] = i1;
|
|
3771
|
+
is[ii++] = i3;
|
|
3772
|
+
is[ii++] = i4;
|
|
3773
|
+
idxBufferIndex = ii;
|
|
3774
|
+
|
|
3775
|
+
drawStack.push(0, 6, 4);
|
|
3776
|
+
};
|
|
3777
|
+
|
|
3778
|
+
const addEllipse = (x, y, a, b, n, ci, ti) => {
|
|
3779
|
+
let t = 0,
|
|
3780
|
+
angleIncrement = $.TAU / n,
|
|
3781
|
+
indicesStart = vertIndex / 4;
|
|
3782
|
+
addVert(x, y, ci, ti); // Center vertex
|
|
3783
|
+
for (let i = 0; i <= n; i++) {
|
|
3784
|
+
let vx = x + a * Math.cos(t),
|
|
3785
|
+
vy = y + b * Math.sin(t);
|
|
3786
|
+
addVert(vx, vy, ci, ti);
|
|
3787
|
+
if (i > 0) {
|
|
3788
|
+
addIndex(indicesStart, indicesStart + i, indicesStart + i + 1);
|
|
3789
|
+
}
|
|
3790
|
+
t += angleIncrement;
|
|
3791
|
+
}
|
|
3792
|
+
drawStack.push(0, n * 3, n + 2);
|
|
3652
3793
|
};
|
|
3653
3794
|
|
|
3654
|
-
$.
|
|
3795
|
+
$.rectMode = (x) => ($._rectMode = x);
|
|
3655
3796
|
|
|
3656
|
-
|
|
3797
|
+
$.rect = (x, y, w, h) => {
|
|
3798
|
+
let [l, r, t, b] = $._calcBox(x, y, w, h, $._rectMode);
|
|
3799
|
+
let ci, ti;
|
|
3800
|
+
if ($._matrixDirty) $._saveMatrix();
|
|
3801
|
+
ti = $._transformIndex;
|
|
3657
3802
|
|
|
3658
|
-
|
|
3659
|
-
|
|
3660
|
-
};
|
|
3803
|
+
if ($._doStroke) {
|
|
3804
|
+
ci = $._strokeIndex;
|
|
3661
3805
|
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
|
|
3665
|
-
|
|
3806
|
+
// outer rectangle coordinates
|
|
3807
|
+
let sw = $._strokeWeight / 2;
|
|
3808
|
+
let to = t + sw,
|
|
3809
|
+
bo = b - sw,
|
|
3810
|
+
lo = l - sw,
|
|
3811
|
+
ro = r + sw;
|
|
3666
3812
|
|
|
3667
|
-
|
|
3668
|
-
|
|
3669
|
-
|
|
3670
|
-
|
|
3671
|
-
|
|
3672
|
-
|
|
3673
|
-
|
|
3674
|
-
|
|
3675
|
-
}
|
|
3676
|
-
if (close) {
|
|
3677
|
-
// Close the shape by adding the first vertex at the end
|
|
3678
|
-
v.push(v[0], v[1], v[2], v[3]);
|
|
3679
|
-
}
|
|
3680
|
-
// Convert the shape to triangles
|
|
3681
|
-
let triangles = [];
|
|
3682
|
-
for (let i = 4; i < v.length; i += 4) {
|
|
3683
|
-
triangles.push(
|
|
3684
|
-
v[0], // First vertex
|
|
3685
|
-
v[1],
|
|
3686
|
-
v[2],
|
|
3687
|
-
v[3],
|
|
3688
|
-
v[i - 4], // Previous vertex
|
|
3689
|
-
v[i - 3],
|
|
3690
|
-
v[i - 2],
|
|
3691
|
-
v[i - 1],
|
|
3692
|
-
v[i], // Current vertex
|
|
3693
|
-
v[i + 1],
|
|
3694
|
-
v[i + 2],
|
|
3695
|
-
v[i + 3]
|
|
3696
|
-
);
|
|
3813
|
+
// stroke is simply a bigger rectangle drawn first
|
|
3814
|
+
addQuad(lo, to, ro, to, ro, bo, lo, bo, ci, ti);
|
|
3815
|
+
|
|
3816
|
+
// inner rectangle coordinates
|
|
3817
|
+
t -= sw;
|
|
3818
|
+
b += sw;
|
|
3819
|
+
l += sw;
|
|
3820
|
+
r -= sw;
|
|
3697
3821
|
}
|
|
3698
|
-
shapeVertices = [];
|
|
3699
3822
|
|
|
3700
|
-
|
|
3701
|
-
|
|
3702
|
-
};
|
|
3823
|
+
if ($._doFill) {
|
|
3824
|
+
ci = colorIndex ?? $._fillIndex;
|
|
3703
3825
|
|
|
3704
|
-
|
|
3705
|
-
|
|
3706
|
-
|
|
3707
|
-
$.vertex(x2, y2);
|
|
3708
|
-
$.vertex(x3, y3);
|
|
3709
|
-
$.endShape(1);
|
|
3826
|
+
// two triangles make a rectangle
|
|
3827
|
+
addQuad(l, t, r, t, r, b, l, b, ci, ti);
|
|
3828
|
+
}
|
|
3710
3829
|
};
|
|
3711
3830
|
|
|
3712
|
-
$.
|
|
3713
|
-
$.beginShape();
|
|
3714
|
-
$.vertex(x1, y1);
|
|
3715
|
-
$.vertex(x2, y2);
|
|
3716
|
-
$.vertex(x3, y3);
|
|
3717
|
-
$.vertex(x4, y4);
|
|
3718
|
-
$.endShape(1);
|
|
3719
|
-
};
|
|
3831
|
+
$.square = (x, y, s) => $.rect(x, y, s, s);
|
|
3720
3832
|
|
|
3721
|
-
|
|
3833
|
+
// prettier-ignore
|
|
3834
|
+
const getArcSegments = (d) =>
|
|
3835
|
+
d < 4 ? 6 :
|
|
3836
|
+
d < 6 ? 8 :
|
|
3837
|
+
d < 10 ? 10 :
|
|
3838
|
+
d < 16 ? 12 :
|
|
3839
|
+
d < 20 ? 14 :
|
|
3840
|
+
d < 22 ? 16 :
|
|
3841
|
+
d < 24 ? 18 :
|
|
3842
|
+
d < 28 ? 20 :
|
|
3843
|
+
d < 34 ? 22 :
|
|
3844
|
+
d < 42 ? 24 :
|
|
3845
|
+
d < 48 ? 26 :
|
|
3846
|
+
d < 56 ? 28 :
|
|
3847
|
+
d < 64 ? 30 :
|
|
3848
|
+
d < 72 ? 32 :
|
|
3849
|
+
d < 84 ? 34 :
|
|
3850
|
+
d < 96 ? 36 :
|
|
3851
|
+
d < 98 ? 38 :
|
|
3852
|
+
d < 113 ? 40 :
|
|
3853
|
+
d < 149 ? 44 :
|
|
3854
|
+
d < 199 ? 48 :
|
|
3855
|
+
d < 261 ? 52 :
|
|
3856
|
+
d < 353 ? 56 :
|
|
3857
|
+
d < 461 ? 60 :
|
|
3858
|
+
d < 585 ? 64 :
|
|
3859
|
+
d < 1200 ? 70 :
|
|
3860
|
+
d < 1800 ? 80 :
|
|
3861
|
+
d < 2400 ? 90 :
|
|
3862
|
+
100;
|
|
3722
3863
|
|
|
3723
|
-
$.
|
|
3724
|
-
let [l, r, t, b] = $._calcBox(x, y, w, h, $._rectMode);
|
|
3864
|
+
$.ellipseMode = (x) => ($._ellipseMode = x);
|
|
3725
3865
|
|
|
3726
|
-
|
|
3866
|
+
$.ellipse = (x, y, w, h) => {
|
|
3867
|
+
let n = getArcSegments(w == h ? w : Math.max(w, h));
|
|
3868
|
+
let a = Math.max(w, 1) / 2;
|
|
3869
|
+
let b = w == h ? a : Math.max(h, 1) / 2;
|
|
3870
|
+
let ci;
|
|
3727
3871
|
if ($._matrixDirty) $._saveMatrix();
|
|
3728
3872
|
let ti = $._transformIndex;
|
|
3729
|
-
|
|
3730
|
-
|
|
3731
|
-
|
|
3732
|
-
|
|
3733
|
-
|
|
3734
|
-
|
|
3735
|
-
|
|
3736
|
-
|
|
3737
|
-
|
|
3738
|
-
);
|
|
3739
|
-
drawStack.push(0, 6);
|
|
3873
|
+
if ($._doStroke) {
|
|
3874
|
+
let sw = $._strokeWeight / 2;
|
|
3875
|
+
addEllipse(x, y, a + sw, b + sw, n, $._strokeIndex, ti);
|
|
3876
|
+
a -= sw;
|
|
3877
|
+
b -= sw;
|
|
3878
|
+
}
|
|
3879
|
+
if ($._doFill) {
|
|
3880
|
+
addEllipse(x, y, a, b, n, colorIndex ?? $._fillIndex, ti);
|
|
3881
|
+
}
|
|
3740
3882
|
};
|
|
3741
3883
|
|
|
3742
|
-
$.
|
|
3884
|
+
$.circle = (x, y, d) => $.ellipse(x, y, d, d);
|
|
3743
3885
|
|
|
3744
3886
|
$.point = (x, y) => {
|
|
3745
3887
|
colorIndex = $._strokeIndex;
|
|
3888
|
+
$._doStroke = false;
|
|
3746
3889
|
let sw = $._strokeWeight;
|
|
3747
3890
|
if (sw < 2) {
|
|
3748
3891
|
sw = Math.round(sw);
|
|
3749
3892
|
$.rect(x, y, sw, sw);
|
|
3750
3893
|
} else $.ellipse(x, y, sw, sw);
|
|
3894
|
+
$._doStroke = true;
|
|
3751
3895
|
colorIndex = null;
|
|
3752
3896
|
};
|
|
3753
3897
|
|
|
@@ -3755,19 +3899,97 @@ fn fragmentMain(@location(0) colorIndex: f32) -> @location(0) vec4f {
|
|
|
3755
3899
|
colorIndex = $._strokeIndex;
|
|
3756
3900
|
|
|
3757
3901
|
$.push();
|
|
3758
|
-
$.
|
|
3902
|
+
$._doStroke = false;
|
|
3903
|
+
$.translate(x1, -y1);
|
|
3759
3904
|
$.rotate($.atan2(y2 - y1, x2 - x1));
|
|
3760
3905
|
let length = Math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2);
|
|
3761
|
-
let sw = $._strokeWeight
|
|
3762
|
-
|
|
3906
|
+
let sw = $._strokeWeight,
|
|
3907
|
+
hsw = sw / 2;
|
|
3908
|
+
$._rectMode = 'corner';
|
|
3909
|
+
if (sw < 4) {
|
|
3910
|
+
$.rect(-hsw, -hsw, length + hsw, sw);
|
|
3911
|
+
} else {
|
|
3912
|
+
$._ellipseMode = 'center';
|
|
3913
|
+
$.ellipse(0, 0, sw, sw);
|
|
3914
|
+
$.ellipse(length, 0, sw, sw);
|
|
3915
|
+
$.rect(0, -hsw, length, sw);
|
|
3916
|
+
}
|
|
3917
|
+
|
|
3763
3918
|
$.pop();
|
|
3764
3919
|
|
|
3765
3920
|
colorIndex = null;
|
|
3766
3921
|
};
|
|
3767
3922
|
|
|
3923
|
+
let shapeVertCount;
|
|
3924
|
+
|
|
3925
|
+
$.beginShape = () => {
|
|
3926
|
+
shapeVertCount = 0;
|
|
3927
|
+
};
|
|
3928
|
+
|
|
3929
|
+
$.vertex = (x, y) => {
|
|
3930
|
+
if ($._matrixDirty) $._saveMatrix();
|
|
3931
|
+
addVert(x, -y, $._fillIndex, $._transformIndex);
|
|
3932
|
+
shapeVertCount++;
|
|
3933
|
+
};
|
|
3934
|
+
|
|
3935
|
+
$.endShape = (close) => {
|
|
3936
|
+
if (shapeVertCount < 3) {
|
|
3937
|
+
throw new Error('A shape must have at least 3 vertices.');
|
|
3938
|
+
}
|
|
3939
|
+
|
|
3940
|
+
let firstVert = vertCount - shapeVertCount;
|
|
3941
|
+
|
|
3942
|
+
if ($._doFill) {
|
|
3943
|
+
// make a simple triangle fan, starting from the first vertex
|
|
3944
|
+
for (let i = firstVert + 1; i < vertCount - 1; i++) {
|
|
3945
|
+
addIndex(firstVert, i, i + 1);
|
|
3946
|
+
}
|
|
3947
|
+
drawStack.push(0, (shapeVertCount - 2) * 3, shapeVertCount);
|
|
3948
|
+
}
|
|
3949
|
+
|
|
3950
|
+
if ($._doStroke) {
|
|
3951
|
+
let first = firstVert * 4,
|
|
3952
|
+
last = vertIndex - 4;
|
|
3953
|
+
for (let i = first; i < last; i += 4) {
|
|
3954
|
+
let x1 = vertexStack[i],
|
|
3955
|
+
y1 = vertexStack[i + 1],
|
|
3956
|
+
x2 = vertexStack[i + 4],
|
|
3957
|
+
y2 = vertexStack[i + 5];
|
|
3958
|
+
$.line(x1, y1, x2, y2);
|
|
3959
|
+
}
|
|
3960
|
+
if (close) {
|
|
3961
|
+
let x1 = vertexStack[last],
|
|
3962
|
+
y1 = vertexStack[last + 1],
|
|
3963
|
+
x2 = vertexStack[first],
|
|
3964
|
+
y2 = vertexStack[first + 1];
|
|
3965
|
+
$.line(x1, y1, x2, y2);
|
|
3966
|
+
}
|
|
3967
|
+
}
|
|
3968
|
+
|
|
3969
|
+
shapeVertCount = 0;
|
|
3970
|
+
};
|
|
3971
|
+
|
|
3972
|
+
$.triangle = (x1, y1, x2, y2, x3, y3) => {
|
|
3973
|
+
$.beginShape();
|
|
3974
|
+
$.vertex(x1, y1);
|
|
3975
|
+
$.vertex(x2, y2);
|
|
3976
|
+
$.vertex(x3, y3);
|
|
3977
|
+
$.endShape();
|
|
3978
|
+
};
|
|
3979
|
+
|
|
3980
|
+
$.quad = (x1, y1, x2, y2, x3, y3, x4, y4) => {
|
|
3981
|
+
$.beginShape();
|
|
3982
|
+
$.vertex(x1, y1);
|
|
3983
|
+
$.vertex(x2, y2);
|
|
3984
|
+
$.vertex(x3, y3);
|
|
3985
|
+
$.vertex(x4, y4);
|
|
3986
|
+
$.endShape();
|
|
3987
|
+
};
|
|
3988
|
+
|
|
3768
3989
|
$.background = (r, g, b, a) => {
|
|
3769
3990
|
$.push();
|
|
3770
3991
|
$.resetMatrix();
|
|
3992
|
+
$._doStroke = false;
|
|
3771
3993
|
if (r.src) {
|
|
3772
3994
|
let og = $._imageMode;
|
|
3773
3995
|
$._imageMode = 'corner';
|
|
@@ -3781,121 +4003,44 @@ fn fragmentMain(@location(0) colorIndex: f32) -> @location(0) vec4f {
|
|
|
3781
4003
|
$._rectMode = og;
|
|
3782
4004
|
}
|
|
3783
4005
|
$.pop();
|
|
4006
|
+
if (!$._fillSet) $._fillIndex = 1;
|
|
3784
4007
|
};
|
|
3785
4008
|
|
|
3786
|
-
/**
|
|
3787
|
-
* Derived from: ceil(Math.log(d) * 7) * 2 - ceil(28)
|
|
3788
|
-
* This lookup table is used for better performance.
|
|
3789
|
-
* @param {Number} d diameter of the circle
|
|
3790
|
-
* @returns n number of segments
|
|
3791
|
-
*/
|
|
3792
|
-
// prettier-ignore
|
|
3793
|
-
const getArcSegments = (d) =>
|
|
3794
|
-
d < 4 ? 6 :
|
|
3795
|
-
d < 6 ? 8 :
|
|
3796
|
-
d < 10 ? 10 :
|
|
3797
|
-
d < 16 ? 12 :
|
|
3798
|
-
d < 20 ? 14 :
|
|
3799
|
-
d < 22 ? 16 :
|
|
3800
|
-
d < 24 ? 18 :
|
|
3801
|
-
d < 28 ? 20 :
|
|
3802
|
-
d < 34 ? 22 :
|
|
3803
|
-
d < 42 ? 24 :
|
|
3804
|
-
d < 48 ? 26 :
|
|
3805
|
-
d < 56 ? 28 :
|
|
3806
|
-
d < 64 ? 30 :
|
|
3807
|
-
d < 72 ? 32 :
|
|
3808
|
-
d < 84 ? 34 :
|
|
3809
|
-
d < 96 ? 36 :
|
|
3810
|
-
d < 98 ? 38 :
|
|
3811
|
-
d < 113 ? 40 :
|
|
3812
|
-
d < 149 ? 44 :
|
|
3813
|
-
d < 199 ? 48 :
|
|
3814
|
-
d < 261 ? 52 :
|
|
3815
|
-
d < 353 ? 56 :
|
|
3816
|
-
d < 461 ? 60 :
|
|
3817
|
-
d < 585 ? 64 :
|
|
3818
|
-
d < 1200 ? 70 :
|
|
3819
|
-
d < 1800 ? 80 :
|
|
3820
|
-
d < 2400 ? 90 :
|
|
3821
|
-
100;
|
|
3822
|
-
|
|
3823
|
-
$.ellipseMode = (x) => ($._ellipseMode = x);
|
|
3824
|
-
|
|
3825
|
-
$.ellipse = (x, y, w, h) => {
|
|
3826
|
-
const n = getArcSegments(w == h ? w : Math.max(w, h));
|
|
3827
|
-
|
|
3828
|
-
let a = Math.max(w, 1) / 2;
|
|
3829
|
-
let b = w == h ? a : Math.max(h, 1) / 2;
|
|
3830
|
-
|
|
3831
|
-
let t = 0; // theta
|
|
3832
|
-
const angleIncrement = $.TAU / n;
|
|
3833
|
-
const ci = colorIndex ?? $._fillIndex;
|
|
3834
|
-
if ($._matrixDirty) $._saveMatrix();
|
|
3835
|
-
const ti = $._transformIndex;
|
|
3836
|
-
let vx1, vy1, vx2, vy2;
|
|
3837
|
-
for (let i = 0; i <= n; i++) {
|
|
3838
|
-
vx1 = vx2;
|
|
3839
|
-
vy1 = vy2;
|
|
3840
|
-
vx2 = x + a * Math.cos(t);
|
|
3841
|
-
vy2 = y + b * Math.sin(t);
|
|
3842
|
-
t += angleIncrement;
|
|
3843
|
-
|
|
3844
|
-
if (i == 0) continue;
|
|
3845
|
-
|
|
3846
|
-
verticesStack.push(x, y, ci, ti, vx1, vy1, ci, ti, vx2, vy2, ci, ti);
|
|
3847
|
-
}
|
|
3848
|
-
|
|
3849
|
-
drawStack.push(0, n * 3);
|
|
3850
|
-
};
|
|
3851
|
-
|
|
3852
|
-
$.circle = (x, y, d) => $.ellipse(x, y, d, d);
|
|
3853
|
-
|
|
3854
4009
|
$._hooks.preRender.push(() => {
|
|
3855
|
-
$.pass.setPipeline($.
|
|
4010
|
+
$.pass.setPipeline($._pipelines[0]);
|
|
3856
4011
|
|
|
3857
|
-
|
|
3858
|
-
|
|
3859
|
-
|
|
3860
|
-
|
|
3861
|
-
usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST
|
|
4012
|
+
let vertexBuffer = Q5.device.createBuffer({
|
|
4013
|
+
size: vertIndex * 4,
|
|
4014
|
+
usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
|
|
4015
|
+
mappedAtCreation: true
|
|
3862
4016
|
});
|
|
3863
4017
|
|
|
3864
|
-
|
|
4018
|
+
new Float32Array(vertexBuffer.getMappedRange()).set(vertexStack.slice(0, vertIndex));
|
|
4019
|
+
vertexBuffer.unmap();
|
|
4020
|
+
|
|
3865
4021
|
$.pass.setVertexBuffer(0, vertexBuffer);
|
|
3866
4022
|
|
|
3867
|
-
|
|
3868
|
-
size:
|
|
3869
|
-
usage: GPUBufferUsage.
|
|
4023
|
+
let indexBuffer = Q5.device.createBuffer({
|
|
4024
|
+
size: idxBufferIndex * 4,
|
|
4025
|
+
usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST,
|
|
4026
|
+
mappedAtCreation: true
|
|
3870
4027
|
});
|
|
3871
4028
|
|
|
3872
|
-
|
|
3873
|
-
|
|
3874
|
-
$._colorsBindGroup = Q5.device.createBindGroup({
|
|
3875
|
-
layout: colorsLayout,
|
|
3876
|
-
entries: [
|
|
3877
|
-
{
|
|
3878
|
-
binding: 0,
|
|
3879
|
-
resource: {
|
|
3880
|
-
buffer: colorsBuffer,
|
|
3881
|
-
offset: 0,
|
|
3882
|
-
size: colorsStack.length * 4
|
|
3883
|
-
}
|
|
3884
|
-
}
|
|
3885
|
-
]
|
|
3886
|
-
});
|
|
4029
|
+
new Uint32Array(indexBuffer.getMappedRange()).set(indexStack.slice(0, idxBufferIndex));
|
|
4030
|
+
indexBuffer.unmap();
|
|
3887
4031
|
|
|
3888
|
-
|
|
3889
|
-
$.pass.setBindGroup(1, $._colorsBindGroup);
|
|
4032
|
+
$.pass.setIndexBuffer(indexBuffer, 'uint32');
|
|
3890
4033
|
});
|
|
3891
4034
|
|
|
3892
4035
|
$._hooks.postRender.push(() => {
|
|
3893
|
-
|
|
4036
|
+
vertIndex = 0;
|
|
4037
|
+
vertCount = 0;
|
|
4038
|
+
idxBufferIndex = 0;
|
|
3894
4039
|
});
|
|
3895
4040
|
};
|
|
3896
4041
|
Q5.renderers.webgpu.image = ($, q) => {
|
|
3897
4042
|
$._textureBindGroups = [];
|
|
3898
|
-
let
|
|
4043
|
+
let vertexStack = [];
|
|
3899
4044
|
|
|
3900
4045
|
let vertexShader = Q5.device.createShaderModule({
|
|
3901
4046
|
label: 'imageVertexShader',
|
|
@@ -3903,15 +4048,14 @@ Q5.renderers.webgpu.image = ($, q) => {
|
|
|
3903
4048
|
struct VertexOutput {
|
|
3904
4049
|
@builtin(position) position: vec4f,
|
|
3905
4050
|
@location(0) texCoord: vec2f
|
|
3906
|
-
}
|
|
3907
|
-
|
|
4051
|
+
}
|
|
3908
4052
|
struct Uniforms {
|
|
3909
4053
|
halfWidth: f32,
|
|
3910
4054
|
halfHeight: f32
|
|
3911
|
-
}
|
|
4055
|
+
}
|
|
3912
4056
|
|
|
3913
4057
|
@group(0) @binding(0) var<uniform> uniforms: Uniforms;
|
|
3914
|
-
@group(0) @binding(1) var<storage
|
|
4058
|
+
@group(0) @binding(1) var<storage> transforms: array<mat4x4<f32>>;
|
|
3915
4059
|
|
|
3916
4060
|
@vertex
|
|
3917
4061
|
fn vertexMain(@location(0) pos: vec2f, @location(1) texCoord: vec2f, @location(2) transformIndex: f32) -> VertexOutput {
|
|
@@ -3972,7 +4116,7 @@ fn fragmentMain(@location(0) texCoord: vec2f) -> @location(0) vec4f {
|
|
|
3972
4116
|
bindGroupLayouts: [...$.bindGroupLayouts, textureLayout]
|
|
3973
4117
|
});
|
|
3974
4118
|
|
|
3975
|
-
$.
|
|
4119
|
+
$._pipelineConfigs[1] = {
|
|
3976
4120
|
label: 'imagePipeline',
|
|
3977
4121
|
layout: pipelineLayout,
|
|
3978
4122
|
vertex: {
|
|
@@ -3983,28 +4127,12 @@ fn fragmentMain(@location(0) texCoord: vec2f) -> @location(0) vec4f {
|
|
|
3983
4127
|
fragment: {
|
|
3984
4128
|
module: fragmentShader,
|
|
3985
4129
|
entryPoint: 'fragmentMain',
|
|
3986
|
-
targets: [
|
|
3987
|
-
{
|
|
3988
|
-
format: 'bgra8unorm',
|
|
3989
|
-
blend: $.blendConfigs?.normal || {
|
|
3990
|
-
color: {
|
|
3991
|
-
srcFactor: 'src-alpha',
|
|
3992
|
-
dstFactor: 'one-minus-src-alpha',
|
|
3993
|
-
operation: 'add'
|
|
3994
|
-
},
|
|
3995
|
-
alpha: {
|
|
3996
|
-
srcFactor: 'src-alpha',
|
|
3997
|
-
dstFactor: 'one-minus-src-alpha',
|
|
3998
|
-
operation: 'add'
|
|
3999
|
-
}
|
|
4000
|
-
}
|
|
4001
|
-
}
|
|
4002
|
-
]
|
|
4130
|
+
targets: [{ format: 'bgra8unorm', blend: $.blendConfigs.normal }]
|
|
4003
4131
|
},
|
|
4004
|
-
primitive: {
|
|
4005
|
-
|
|
4006
|
-
|
|
4007
|
-
|
|
4132
|
+
primitive: { topology: 'triangle-list' }
|
|
4133
|
+
};
|
|
4134
|
+
|
|
4135
|
+
$._pipelines[1] = Q5.device.createRenderPipeline($._pipelineConfigs[1]);
|
|
4008
4136
|
|
|
4009
4137
|
let sampler = Q5.device.createSampler({
|
|
4010
4138
|
magFilter: 'linear',
|
|
@@ -4029,7 +4157,11 @@ fn fragmentMain(@location(0) texCoord: vec2f) -> @location(0) vec4f {
|
|
|
4029
4157
|
|
|
4030
4158
|
Q5.device.queue.copyExternalImageToTexture(
|
|
4031
4159
|
{ source: img },
|
|
4032
|
-
{
|
|
4160
|
+
{
|
|
4161
|
+
texture,
|
|
4162
|
+
colorSpace: $.canvas.colorSpace
|
|
4163
|
+
// premultipliedAlpha: true
|
|
4164
|
+
},
|
|
4033
4165
|
textureSize
|
|
4034
4166
|
);
|
|
4035
4167
|
|
|
@@ -4082,7 +4214,7 @@ fn fragmentMain(@location(0) texCoord: vec2f) -> @location(0) vec4f {
|
|
|
4082
4214
|
let [l, r, t, b] = $._calcBox(x, y, w, h, $._imageMode);
|
|
4083
4215
|
|
|
4084
4216
|
// prettier-ignore
|
|
4085
|
-
|
|
4217
|
+
vertexStack.push(
|
|
4086
4218
|
l, t, 0, 0, ti,
|
|
4087
4219
|
r, t, 1, 0, ti,
|
|
4088
4220
|
l, b, 0, 1, ti,
|
|
@@ -4091,29 +4223,29 @@ fn fragmentMain(@location(0) texCoord: vec2f) -> @location(0) vec4f {
|
|
|
4091
4223
|
r, b, 1, 1, ti
|
|
4092
4224
|
);
|
|
4093
4225
|
|
|
4094
|
-
$.drawStack.push(1, img.textureIndex);
|
|
4226
|
+
$.drawStack.push(1, img.textureIndex, 0);
|
|
4095
4227
|
};
|
|
4096
4228
|
|
|
4097
4229
|
$._hooks.preRender.push(() => {
|
|
4098
4230
|
if (!$._textureBindGroups.length) return;
|
|
4099
4231
|
|
|
4100
4232
|
// Switch to image pipeline
|
|
4101
|
-
$.pass.setPipeline($.
|
|
4102
|
-
|
|
4103
|
-
// Create a vertex buffer for the image quads
|
|
4104
|
-
const vertices = new Float32Array(verticesStack);
|
|
4233
|
+
$.pass.setPipeline($._pipelines[1]);
|
|
4105
4234
|
|
|
4106
4235
|
const vertexBuffer = Q5.device.createBuffer({
|
|
4107
|
-
size:
|
|
4108
|
-
usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST
|
|
4236
|
+
size: vertexStack.length * 4,
|
|
4237
|
+
usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
|
|
4238
|
+
mappedAtCreation: true
|
|
4109
4239
|
});
|
|
4110
4240
|
|
|
4111
|
-
|
|
4241
|
+
new Float32Array(vertexBuffer.getMappedRange()).set(vertices);
|
|
4242
|
+
vertexBuffer.unmap();
|
|
4243
|
+
|
|
4112
4244
|
$.pass.setVertexBuffer(1, vertexBuffer);
|
|
4113
4245
|
});
|
|
4114
4246
|
|
|
4115
4247
|
$._hooks.postRender.push(() => {
|
|
4116
|
-
|
|
4248
|
+
vertexStack.length = 0;
|
|
4117
4249
|
});
|
|
4118
4250
|
};
|
|
4119
4251
|
|
|
@@ -4134,35 +4266,35 @@ const pos = array(vec2f(0, -1), vec2f(1, -1), vec2f(0, 0), vec2f(1, 0));
|
|
|
4134
4266
|
|
|
4135
4267
|
struct VertexInput {
|
|
4136
4268
|
@builtin(vertex_index) vertex : u32,
|
|
4137
|
-
@builtin(instance_index) instance : u32
|
|
4138
|
-
}
|
|
4269
|
+
@builtin(instance_index) instance : u32
|
|
4270
|
+
}
|
|
4139
4271
|
struct VertexOutput {
|
|
4140
4272
|
@builtin(position) position : vec4f,
|
|
4141
|
-
@location(0)
|
|
4142
|
-
@location(1)
|
|
4143
|
-
}
|
|
4273
|
+
@location(0) texCoord : vec2f,
|
|
4274
|
+
@location(1) fillColor : vec4f
|
|
4275
|
+
}
|
|
4144
4276
|
struct Char {
|
|
4145
4277
|
texOffset: vec2f,
|
|
4146
4278
|
texExtent: vec2f,
|
|
4147
4279
|
size: vec2f,
|
|
4148
4280
|
offset: vec2f,
|
|
4149
|
-
}
|
|
4281
|
+
}
|
|
4150
4282
|
struct Text {
|
|
4151
4283
|
pos: vec2f,
|
|
4152
4284
|
scale: f32,
|
|
4153
4285
|
transformIndex: f32,
|
|
4154
4286
|
fillIndex: f32,
|
|
4155
4287
|
strokeIndex: f32
|
|
4156
|
-
}
|
|
4288
|
+
}
|
|
4157
4289
|
struct Uniforms {
|
|
4158
4290
|
halfWidth: f32,
|
|
4159
4291
|
halfHeight: f32
|
|
4160
|
-
}
|
|
4292
|
+
}
|
|
4161
4293
|
|
|
4162
4294
|
@group(0) @binding(0) var<uniform> uniforms: Uniforms;
|
|
4163
|
-
@group(0) @binding(1) var<storage
|
|
4295
|
+
@group(0) @binding(1) var<storage> transforms: array<mat4x4<f32>>;
|
|
4164
4296
|
|
|
4165
|
-
@group(1) @binding(0) var<storage
|
|
4297
|
+
@group(1) @binding(0) var<storage> colors : array<vec4f>;
|
|
4166
4298
|
|
|
4167
4299
|
@group(2) @binding(0) var fontTexture: texture_2d<f32>;
|
|
4168
4300
|
@group(2) @binding(1) var fontSampler: sampler;
|
|
@@ -4188,13 +4320,13 @@ fn vertexMain(input : VertexInput) -> VertexOutput {
|
|
|
4188
4320
|
|
|
4189
4321
|
var output : VertexOutput;
|
|
4190
4322
|
output.position = vert;
|
|
4191
|
-
output.
|
|
4192
|
-
output.
|
|
4323
|
+
output.texCoord = (pos[input.vertex] * vec2f(1, -1)) * fontChar.texExtent + fontChar.texOffset;
|
|
4324
|
+
output.fillColor = colors[i32(text.fillIndex)];
|
|
4193
4325
|
return output;
|
|
4194
4326
|
}
|
|
4195
4327
|
|
|
4196
|
-
fn sampleMsdf(
|
|
4197
|
-
let c = textureSample(fontTexture, fontSampler,
|
|
4328
|
+
fn sampleMsdf(texCoord: vec2f) -> f32 {
|
|
4329
|
+
let c = textureSample(fontTexture, fontSampler, texCoord);
|
|
4198
4330
|
return max(min(c.r, c.g), min(max(c.r, c.g), c.b));
|
|
4199
4331
|
}
|
|
4200
4332
|
|
|
@@ -4204,25 +4336,83 @@ fn fragmentMain(input : VertexOutput) -> @location(0) vec4f {
|
|
|
4204
4336
|
// uses the default which is 4.
|
|
4205
4337
|
let pxRange = 4.0;
|
|
4206
4338
|
let sz = vec2f(textureDimensions(fontTexture, 0));
|
|
4207
|
-
let dx = sz.x*length(vec2f(dpdxFine(input.
|
|
4208
|
-
let dy = sz.y*length(vec2f(dpdxFine(input.
|
|
4339
|
+
let dx = sz.x*length(vec2f(dpdxFine(input.texCoord.x), dpdyFine(input.texCoord.x)));
|
|
4340
|
+
let dy = sz.y*length(vec2f(dpdxFine(input.texCoord.y), dpdyFine(input.texCoord.y)));
|
|
4209
4341
|
let toPixels = pxRange * inverseSqrt(dx * dx + dy * dy);
|
|
4210
|
-
let sigDist = sampleMsdf(input.
|
|
4342
|
+
let sigDist = sampleMsdf(input.texCoord) - 0.5;
|
|
4211
4343
|
let pxDist = sigDist * toPixels;
|
|
4212
4344
|
let edgeWidth = 0.5;
|
|
4213
4345
|
let alpha = smoothstep(-edgeWidth, edgeWidth, pxDist);
|
|
4214
4346
|
if (alpha < 0.001) {
|
|
4215
4347
|
discard;
|
|
4216
4348
|
}
|
|
4217
|
-
|
|
4218
|
-
return vec4f(fillColor.rgb, fillColor.a * alpha);
|
|
4349
|
+
return vec4f(input.fillColor.rgb, input.fillColor.a * alpha);
|
|
4219
4350
|
}
|
|
4220
4351
|
`
|
|
4221
4352
|
});
|
|
4222
4353
|
|
|
4354
|
+
let textBindGroupLayout = Q5.device.createBindGroupLayout({
|
|
4355
|
+
label: 'MSDF text group layout',
|
|
4356
|
+
entries: [
|
|
4357
|
+
{
|
|
4358
|
+
binding: 0,
|
|
4359
|
+
visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT,
|
|
4360
|
+
buffer: { type: 'read-only-storage' }
|
|
4361
|
+
},
|
|
4362
|
+
{
|
|
4363
|
+
binding: 1,
|
|
4364
|
+
visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT,
|
|
4365
|
+
buffer: { type: 'read-only-storage' }
|
|
4366
|
+
}
|
|
4367
|
+
]
|
|
4368
|
+
});
|
|
4369
|
+
|
|
4370
|
+
let fontSampler = Q5.device.createSampler({
|
|
4371
|
+
minFilter: 'linear',
|
|
4372
|
+
magFilter: 'linear',
|
|
4373
|
+
mipmapFilter: 'linear',
|
|
4374
|
+
maxAnisotropy: 16
|
|
4375
|
+
});
|
|
4376
|
+
let fontBindGroupLayout = Q5.device.createBindGroupLayout({
|
|
4377
|
+
label: 'MSDF font group layout',
|
|
4378
|
+
entries: [
|
|
4379
|
+
{
|
|
4380
|
+
binding: 0,
|
|
4381
|
+
visibility: GPUShaderStage.FRAGMENT,
|
|
4382
|
+
texture: {}
|
|
4383
|
+
},
|
|
4384
|
+
{
|
|
4385
|
+
binding: 1,
|
|
4386
|
+
visibility: GPUShaderStage.FRAGMENT,
|
|
4387
|
+
sampler: {}
|
|
4388
|
+
},
|
|
4389
|
+
{
|
|
4390
|
+
binding: 2,
|
|
4391
|
+
visibility: GPUShaderStage.VERTEX,
|
|
4392
|
+
buffer: { type: 'read-only-storage' }
|
|
4393
|
+
}
|
|
4394
|
+
]
|
|
4395
|
+
});
|
|
4396
|
+
|
|
4397
|
+
let fontPipelineLayout = Q5.device.createPipelineLayout({
|
|
4398
|
+
bindGroupLayouts: [...$.bindGroupLayouts, fontBindGroupLayout, textBindGroupLayout]
|
|
4399
|
+
});
|
|
4400
|
+
|
|
4401
|
+
$._pipelineConfigs[2] = {
|
|
4402
|
+
label: 'msdf font pipeline',
|
|
4403
|
+
layout: fontPipelineLayout,
|
|
4404
|
+
vertex: { module: textShader, entryPoint: 'vertexMain' },
|
|
4405
|
+
fragment: {
|
|
4406
|
+
module: textShader,
|
|
4407
|
+
entryPoint: 'fragmentMain',
|
|
4408
|
+
targets: [{ format: 'bgra8unorm', blend: $.blendConfigs.normal }]
|
|
4409
|
+
},
|
|
4410
|
+
primitive: { topology: 'triangle-strip', stripIndexFormat: 'uint32' }
|
|
4411
|
+
};
|
|
4412
|
+
$._pipelines[2] = Q5.device.createRenderPipeline($._pipelineConfigs[2]);
|
|
4413
|
+
|
|
4223
4414
|
class MsdfFont {
|
|
4224
|
-
constructor(
|
|
4225
|
-
this.pipeline = pipeline;
|
|
4415
|
+
constructor(bindGroup, lineHeight, chars, kernings) {
|
|
4226
4416
|
this.bindGroup = bindGroup;
|
|
4227
4417
|
this.lineHeight = lineHeight;
|
|
4228
4418
|
this.chars = chars;
|
|
@@ -4248,22 +4438,7 @@ fn fragmentMain(input : VertexOutput) -> @location(0) vec4f {
|
|
|
4248
4438
|
}
|
|
4249
4439
|
}
|
|
4250
4440
|
|
|
4251
|
-
|
|
4252
|
-
label: 'MSDF text group layout',
|
|
4253
|
-
entries: [
|
|
4254
|
-
{
|
|
4255
|
-
binding: 0,
|
|
4256
|
-
visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT,
|
|
4257
|
-
buffer: { type: 'read-only-storage' }
|
|
4258
|
-
},
|
|
4259
|
-
{
|
|
4260
|
-
binding: 1,
|
|
4261
|
-
visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT,
|
|
4262
|
-
buffer: { type: 'read-only-storage' }
|
|
4263
|
-
}
|
|
4264
|
-
]
|
|
4265
|
-
});
|
|
4266
|
-
|
|
4441
|
+
$._fonts = [];
|
|
4267
4442
|
let fonts = {};
|
|
4268
4443
|
|
|
4269
4444
|
let createFont = async (fontJsonUrl, fontName, cb) => {
|
|
@@ -4326,74 +4501,11 @@ fn fragmentMain(input : VertexOutput) -> @location(0) vec4f {
|
|
|
4326
4501
|
}
|
|
4327
4502
|
charsBuffer.unmap();
|
|
4328
4503
|
|
|
4329
|
-
let fontSampler = Q5.device.createSampler({
|
|
4330
|
-
minFilter: 'linear',
|
|
4331
|
-
magFilter: 'linear',
|
|
4332
|
-
mipmapFilter: 'linear',
|
|
4333
|
-
maxAnisotropy: 16
|
|
4334
|
-
});
|
|
4335
|
-
let fontBindGroupLayout = Q5.device.createBindGroupLayout({
|
|
4336
|
-
label: 'MSDF font group layout',
|
|
4337
|
-
entries: [
|
|
4338
|
-
{
|
|
4339
|
-
binding: 0,
|
|
4340
|
-
visibility: GPUShaderStage.FRAGMENT,
|
|
4341
|
-
texture: {}
|
|
4342
|
-
},
|
|
4343
|
-
{
|
|
4344
|
-
binding: 1,
|
|
4345
|
-
visibility: GPUShaderStage.FRAGMENT,
|
|
4346
|
-
sampler: {}
|
|
4347
|
-
},
|
|
4348
|
-
{
|
|
4349
|
-
binding: 2,
|
|
4350
|
-
visibility: GPUShaderStage.VERTEX,
|
|
4351
|
-
buffer: { type: 'read-only-storage' }
|
|
4352
|
-
}
|
|
4353
|
-
]
|
|
4354
|
-
});
|
|
4355
|
-
let fontPipeline = Q5.device.createRenderPipeline({
|
|
4356
|
-
label: 'msdf font pipeline',
|
|
4357
|
-
layout: Q5.device.createPipelineLayout({
|
|
4358
|
-
bindGroupLayouts: [...$.bindGroupLayouts, fontBindGroupLayout, textBindGroupLayout]
|
|
4359
|
-
}),
|
|
4360
|
-
vertex: {
|
|
4361
|
-
module: textShader,
|
|
4362
|
-
entryPoint: 'vertexMain'
|
|
4363
|
-
},
|
|
4364
|
-
fragment: {
|
|
4365
|
-
module: textShader,
|
|
4366
|
-
entryPoint: 'fragmentMain',
|
|
4367
|
-
targets: [
|
|
4368
|
-
{
|
|
4369
|
-
format: 'bgra8unorm',
|
|
4370
|
-
blend: {
|
|
4371
|
-
color: {
|
|
4372
|
-
srcFactor: 'src-alpha',
|
|
4373
|
-
dstFactor: 'one-minus-src-alpha'
|
|
4374
|
-
},
|
|
4375
|
-
alpha: {
|
|
4376
|
-
srcFactor: 'one',
|
|
4377
|
-
dstFactor: 'one'
|
|
4378
|
-
}
|
|
4379
|
-
}
|
|
4380
|
-
}
|
|
4381
|
-
]
|
|
4382
|
-
},
|
|
4383
|
-
primitive: {
|
|
4384
|
-
topology: 'triangle-strip',
|
|
4385
|
-
stripIndexFormat: 'uint32'
|
|
4386
|
-
}
|
|
4387
|
-
});
|
|
4388
|
-
|
|
4389
4504
|
let fontBindGroup = Q5.device.createBindGroup({
|
|
4390
4505
|
label: 'msdf font bind group',
|
|
4391
4506
|
layout: fontBindGroupLayout,
|
|
4392
4507
|
entries: [
|
|
4393
|
-
{
|
|
4394
|
-
binding: 0,
|
|
4395
|
-
resource: texture.createView()
|
|
4396
|
-
},
|
|
4508
|
+
{ binding: 0, resource: texture.createView() },
|
|
4397
4509
|
{ binding: 1, resource: fontSampler },
|
|
4398
4510
|
{ binding: 2, resource: { buffer: charsBuffer } }
|
|
4399
4511
|
]
|
|
@@ -4411,10 +4523,11 @@ fn fragmentMain(input : VertexOutput) -> @location(0) vec4f {
|
|
|
4411
4523
|
}
|
|
4412
4524
|
}
|
|
4413
4525
|
|
|
4414
|
-
$._font = new MsdfFont(
|
|
4526
|
+
$._font = new MsdfFont(fontBindGroup, atlas.common.lineHeight, chars, kernings);
|
|
4415
4527
|
|
|
4528
|
+
$._font.index = $._fonts.length;
|
|
4529
|
+
$._fonts.push($._font);
|
|
4416
4530
|
fonts[fontName] = $._font;
|
|
4417
|
-
$.pipelines[2] = $._font.pipeline;
|
|
4418
4531
|
|
|
4419
4532
|
q._preloadCount--;
|
|
4420
4533
|
|
|
@@ -4443,12 +4556,6 @@ fn fragmentMain(input : VertexOutput) -> @location(0) vec4f {
|
|
|
4443
4556
|
|
|
4444
4557
|
$.textFont = (fontName) => {
|
|
4445
4558
|
$._font = fonts[fontName];
|
|
4446
|
-
|
|
4447
|
-
// replay the change of font in the draw stack
|
|
4448
|
-
$.drawStack.push(-1, () => {
|
|
4449
|
-
$._font = fonts[fontName];
|
|
4450
|
-
$.pipelines[2] = $._font.pipeline;
|
|
4451
|
-
});
|
|
4452
4559
|
};
|
|
4453
4560
|
$.textSize = (size) => {
|
|
4454
4561
|
$._textSize = size;
|
|
@@ -4561,7 +4668,7 @@ fn fragmentMain(input : VertexOutput) -> @location(0) vec4f {
|
|
|
4561
4668
|
}
|
|
4562
4669
|
}
|
|
4563
4670
|
|
|
4564
|
-
let charsData =
|
|
4671
|
+
let charsData = [];
|
|
4565
4672
|
|
|
4566
4673
|
let ta = $._textAlign,
|
|
4567
4674
|
tb = $._textBaseline,
|
|
@@ -4607,7 +4714,7 @@ fn fragmentMain(input : VertexOutput) -> @location(0) vec4f {
|
|
|
4607
4714
|
}
|
|
4608
4715
|
$._charStack.push(charsData);
|
|
4609
4716
|
|
|
4610
|
-
let text =
|
|
4717
|
+
let text = [];
|
|
4611
4718
|
|
|
4612
4719
|
if ($._matrixDirty) $._saveMatrix();
|
|
4613
4720
|
|
|
@@ -4615,11 +4722,11 @@ fn fragmentMain(input : VertexOutput) -> @location(0) vec4f {
|
|
|
4615
4722
|
text[1] = -y;
|
|
4616
4723
|
text[2] = $._textSize / 44;
|
|
4617
4724
|
text[3] = $._transformIndex;
|
|
4618
|
-
text[4] = $._fillIndex;
|
|
4725
|
+
text[4] = $._fillSet ? $._fillIndex : 0;
|
|
4619
4726
|
text[5] = $._strokeIndex;
|
|
4620
4727
|
|
|
4621
4728
|
$._textStack.push(text);
|
|
4622
|
-
$.drawStack.push(2, measurements.printedCharCount);
|
|
4729
|
+
$.drawStack.push(2, measurements.printedCharCount, $._font.index);
|
|
4623
4730
|
};
|
|
4624
4731
|
|
|
4625
4732
|
$.textWidth = (str) => {
|
|
@@ -4632,11 +4739,11 @@ fn fragmentMain(input : VertexOutput) -> @location(0) vec4f {
|
|
|
4632
4739
|
|
|
4633
4740
|
if ($._doFill) {
|
|
4634
4741
|
let fi = $._fillIndex * 4;
|
|
4635
|
-
g.fill(
|
|
4742
|
+
g.fill(colorStack.slice(fi, fi + 4));
|
|
4636
4743
|
}
|
|
4637
4744
|
if ($._doStroke) {
|
|
4638
4745
|
let si = $._strokeIndex * 4;
|
|
4639
|
-
g.stroke(
|
|
4746
|
+
g.stroke(colorStack.slice(si, si + 4));
|
|
4640
4747
|
}
|
|
4641
4748
|
|
|
4642
4749
|
let img = g.createTextImage(str, w, h);
|
|
@@ -4687,21 +4794,15 @@ fn fragmentMain(input : VertexOutput) -> @location(0) vec4f {
|
|
|
4687
4794
|
totalTextSize += charsData.length * 4;
|
|
4688
4795
|
}
|
|
4689
4796
|
|
|
4690
|
-
// Create a single buffer for all
|
|
4797
|
+
// Create a single buffer for all char data
|
|
4691
4798
|
let charBuffer = Q5.device.createBuffer({
|
|
4692
|
-
label: 'charBuffer',
|
|
4693
4799
|
size: totalTextSize,
|
|
4694
4800
|
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
|
|
4695
4801
|
mappedAtCreation: true
|
|
4696
4802
|
});
|
|
4697
4803
|
|
|
4698
4804
|
// Copy all text data into the buffer
|
|
4699
|
-
|
|
4700
|
-
let o = 0;
|
|
4701
|
-
for (let array of $._charStack) {
|
|
4702
|
-
textArray.set(array, o);
|
|
4703
|
-
o += array.length;
|
|
4704
|
-
}
|
|
4805
|
+
new Float32Array(charBuffer.getMappedRange()).set($._charStack.flat());
|
|
4705
4806
|
charBuffer.unmap();
|
|
4706
4807
|
|
|
4707
4808
|
// Calculate total buffer size for metadata
|
|
@@ -4716,12 +4817,7 @@ fn fragmentMain(input : VertexOutput) -> @location(0) vec4f {
|
|
|
4716
4817
|
});
|
|
4717
4818
|
|
|
4718
4819
|
// Copy all metadata into the buffer
|
|
4719
|
-
|
|
4720
|
-
o = 0;
|
|
4721
|
-
for (let array of $._textStack) {
|
|
4722
|
-
metadataArray.set(array, o);
|
|
4723
|
-
o += array.length;
|
|
4724
|
-
}
|
|
4820
|
+
new Float32Array(textBuffer.getMappedRange()).set($._textStack.flat());
|
|
4725
4821
|
textBuffer.unmap();
|
|
4726
4822
|
|
|
4727
4823
|
// Create a single bind group for the text buffer and metadata buffer
|
|
@@ -4729,14 +4825,8 @@ fn fragmentMain(input : VertexOutput) -> @location(0) vec4f {
|
|
|
4729
4825
|
label: 'msdf text bind group',
|
|
4730
4826
|
layout: textBindGroupLayout,
|
|
4731
4827
|
entries: [
|
|
4732
|
-
{
|
|
4733
|
-
|
|
4734
|
-
resource: { buffer: charBuffer }
|
|
4735
|
-
},
|
|
4736
|
-
{
|
|
4737
|
-
binding: 1,
|
|
4738
|
-
resource: { buffer: textBuffer }
|
|
4739
|
-
}
|
|
4828
|
+
{ binding: 0, resource: { buffer: charBuffer } },
|
|
4829
|
+
{ binding: 1, resource: { buffer: textBuffer } }
|
|
4740
4830
|
]
|
|
4741
4831
|
});
|
|
4742
4832
|
});
|