q5 2.2.1 → 2.2.3
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 +3 -3
- package/package.json +1 -1
- package/q5-q2d.js +59 -43
- package/q5-q2d.min.js +1 -1
- package/q5.js +302 -96
- package/q5.min.js +1 -1
- package/src/q5-2d-canvas.js +6 -1
- package/src/q5-2d-drawing.js +8 -6
- package/src/q5-color.js +30 -14
- package/src/q5-core.js +1 -1
- package/src/q5-math.js +15 -27
- package/src/q5-webgpu-canvas.js +38 -16
- package/src/q5-webgpu-drawing.js +18 -36
- package/src/q5-webgpu-image.js +187 -1
- package/src/readme.md +24 -16
package/q5.js
CHANGED
|
@@ -239,7 +239,7 @@ function Q5(scope, parent, renderer) {
|
|
|
239
239
|
raf($._draw);
|
|
240
240
|
}
|
|
241
241
|
|
|
242
|
-
if ((arguments.length && scope != 'namespace') || preloadDefined) {
|
|
242
|
+
if ((arguments.length && scope != 'namespace' && renderer != 'webgpu') || preloadDefined) {
|
|
243
243
|
$.preload();
|
|
244
244
|
_start();
|
|
245
245
|
} else {
|
|
@@ -551,7 +551,12 @@ Q5.renderers.q2d.canvas = ($, q) => {
|
|
|
551
551
|
$.ctx.lineWidth = n || 0.0001;
|
|
552
552
|
};
|
|
553
553
|
$.noStroke = () => ($._doStroke = false);
|
|
554
|
-
$.clear = () =>
|
|
554
|
+
$.clear = () => {
|
|
555
|
+
$.ctx.save();
|
|
556
|
+
$.ctx.resetTransform();
|
|
557
|
+
$.ctx.clearRect(0, 0, $.canvas.width, $.canvas.height);
|
|
558
|
+
$.ctx.restore();
|
|
559
|
+
};
|
|
555
560
|
|
|
556
561
|
// DRAWING MATRIX
|
|
557
562
|
|
|
@@ -717,15 +722,17 @@ Q5.renderers.q2d.drawing = ($) => {
|
|
|
717
722
|
// DRAWING
|
|
718
723
|
|
|
719
724
|
$.background = function (c) {
|
|
720
|
-
if (c.canvas) return $.image(c, 0, 0, $.width, $.height);
|
|
721
725
|
$.ctx.save();
|
|
722
726
|
$.ctx.resetTransform();
|
|
723
|
-
if (
|
|
724
|
-
|
|
725
|
-
|
|
727
|
+
if (c.canvas) $.image(c, 0, 0, $.width, $.height);
|
|
728
|
+
else {
|
|
729
|
+
if (Q5.Color) {
|
|
730
|
+
if (!c._q5Color && typeof c != 'string') c = $.color(...arguments);
|
|
731
|
+
else if ($._namedColors[c]) c = $.color(...$._namedColors[c]);
|
|
732
|
+
}
|
|
733
|
+
$.ctx.fillStyle = c.toString();
|
|
734
|
+
$.ctx.fillRect(0, 0, $.canvas.width, $.canvas.height);
|
|
726
735
|
}
|
|
727
|
-
$.ctx.fillStyle = c.toString();
|
|
728
|
-
$.ctx.fillRect(0, 0, $.canvas.width, $.canvas.height);
|
|
729
736
|
$.ctx.restore();
|
|
730
737
|
};
|
|
731
738
|
|
|
@@ -1728,13 +1735,20 @@ Q5.modules.color = ($, q) => {
|
|
|
1728
1735
|
c0 = parseInt(c0.slice(1, 3), 16);
|
|
1729
1736
|
}
|
|
1730
1737
|
} else if ($._namedColors[c0]) {
|
|
1731
|
-
c0 = $._namedColors[c0];
|
|
1738
|
+
[c0, c1, c2, c3] = $._namedColors[c0];
|
|
1732
1739
|
} else {
|
|
1733
1740
|
console.error(
|
|
1734
1741
|
"q5 can't parse color: " + c0 + '\nOnly numeric input, hex, and common named colors are supported.'
|
|
1735
1742
|
);
|
|
1736
1743
|
return new C(0, 0, 0);
|
|
1737
1744
|
}
|
|
1745
|
+
|
|
1746
|
+
if ($._colorFormat == 1) {
|
|
1747
|
+
c0 /= 255;
|
|
1748
|
+
if (c1) c1 /= 255;
|
|
1749
|
+
if (c2) c2 /= 255;
|
|
1750
|
+
if (c3) c3 /= 255;
|
|
1751
|
+
}
|
|
1738
1752
|
}
|
|
1739
1753
|
if (Array.isArray(c0)) {
|
|
1740
1754
|
c1 = c0[1];
|
|
@@ -1744,13 +1758,6 @@ Q5.modules.color = ($, q) => {
|
|
|
1744
1758
|
}
|
|
1745
1759
|
}
|
|
1746
1760
|
|
|
1747
|
-
if ($._colorFormat == 1) {
|
|
1748
|
-
c0 /= 255;
|
|
1749
|
-
if (c1) c1 /= 255;
|
|
1750
|
-
if (c2) c2 /= 255;
|
|
1751
|
-
if (c3) c3 /= 255;
|
|
1752
|
-
}
|
|
1753
|
-
|
|
1754
1761
|
if (c2 == undefined) return new C(c0, c0, c0, c1);
|
|
1755
1762
|
return new C(c0, c1, c2, c3);
|
|
1756
1763
|
};
|
|
@@ -1760,8 +1767,29 @@ Q5.modules.color = ($, q) => {
|
|
|
1760
1767
|
$.blue = (c) => c.b;
|
|
1761
1768
|
$.alpha = (c) => c.a;
|
|
1762
1769
|
$.lightness = (c) => {
|
|
1770
|
+
if ($._colorMode == 'oklch') return c.l;
|
|
1763
1771
|
return ((0.2126 * c.r + 0.7152 * c.g + 0.0722 * c.b) * 100) / 255;
|
|
1764
1772
|
};
|
|
1773
|
+
$.hue = (c) => {
|
|
1774
|
+
if ($._colorMode == 'oklch') return c.h;
|
|
1775
|
+
let r = c.r;
|
|
1776
|
+
let g = c.g;
|
|
1777
|
+
let b = c.b;
|
|
1778
|
+
if ($._colorFormat == 255) {
|
|
1779
|
+
r /= 255;
|
|
1780
|
+
g /= 255;
|
|
1781
|
+
b /= 255;
|
|
1782
|
+
}
|
|
1783
|
+
let max = Math.max(r, g, b);
|
|
1784
|
+
let min = Math.min(r, g, b);
|
|
1785
|
+
let h;
|
|
1786
|
+
if (max == min) h = 0;
|
|
1787
|
+
else if (max == r) h = (60 * (g - b)) / (max - min);
|
|
1788
|
+
else if (max == g) h = (60 * (b - r)) / (max - min) + 120;
|
|
1789
|
+
else h = (60 * (r - g)) / (max - min) + 240;
|
|
1790
|
+
if (h < 0) h += 360;
|
|
1791
|
+
return h;
|
|
1792
|
+
};
|
|
1765
1793
|
|
|
1766
1794
|
$.lerpColor = (a, b, t) => {
|
|
1767
1795
|
if ($._colorMode == 'rgb') {
|
|
@@ -2216,33 +2244,21 @@ Q5.modules.math = ($, q) => {
|
|
|
2216
2244
|
$.norm = (value, start, stop) => $.map(value, start, stop, 0, 1);
|
|
2217
2245
|
$.sq = (x) => x * x;
|
|
2218
2246
|
$.fract = (x) => x - Math.floor(x);
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
return a;
|
|
2235
|
-
};
|
|
2236
|
-
$.acos = (x) => {
|
|
2237
|
-
let a = Math.acos(x);
|
|
2238
|
-
if ($._angleMode == 'degrees') a = $.degrees(a);
|
|
2239
|
-
return a;
|
|
2240
|
-
};
|
|
2241
|
-
$.atan = (x) => {
|
|
2242
|
-
let a = Math.atan(x);
|
|
2243
|
-
if ($._angleMode == 'degrees') a = $.degrees(a);
|
|
2244
|
-
return a;
|
|
2245
|
-
};
|
|
2247
|
+
|
|
2248
|
+
for (let fn of ['sin', 'cos', 'tan']) {
|
|
2249
|
+
$[fn] = (a) => {
|
|
2250
|
+
if ($._angleMode == 'degrees') a = $.radians(a);
|
|
2251
|
+
return Math[fn](a);
|
|
2252
|
+
};
|
|
2253
|
+
}
|
|
2254
|
+
|
|
2255
|
+
for (let fn of ['asin', 'acos', 'atan']) {
|
|
2256
|
+
$[fn] = (x) => {
|
|
2257
|
+
let a = Math[fn](x);
|
|
2258
|
+
if ($._angleMode == 'degrees') a = $.degrees(a);
|
|
2259
|
+
return a;
|
|
2260
|
+
};
|
|
2261
|
+
}
|
|
2246
2262
|
$.atan2 = (y, x) => {
|
|
2247
2263
|
let a = Math.atan2(y, x);
|
|
2248
2264
|
if ($._angleMode == 'degrees') a = $.degrees(a);
|
|
@@ -2906,7 +2922,7 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
2906
2922
|
|
|
2907
2923
|
if ($.colorMode) $.colorMode('rgb', 'float');
|
|
2908
2924
|
|
|
2909
|
-
let colorsStack, envBindGroup, transformBindGroup;
|
|
2925
|
+
let pass, colorsStack, envBindGroup, transformBindGroup;
|
|
2910
2926
|
|
|
2911
2927
|
$._createCanvas = (w, h, opt) => {
|
|
2912
2928
|
q.ctx = q.drawingContext = c.getContext('webgpu');
|
|
@@ -3079,7 +3095,7 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
3079
3095
|
$._beginRender = () => {
|
|
3080
3096
|
$.encoder = Q5.device.createCommandEncoder();
|
|
3081
3097
|
|
|
3082
|
-
q.pass = $.encoder.beginRenderPass({
|
|
3098
|
+
pass = q.pass = $.encoder.beginRenderPass({
|
|
3083
3099
|
colorAttachments: [
|
|
3084
3100
|
{
|
|
3085
3101
|
view: ctx.getCurrentTexture().createView(),
|
|
@@ -3091,7 +3107,7 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
3091
3107
|
};
|
|
3092
3108
|
|
|
3093
3109
|
$._render = () => {
|
|
3094
|
-
|
|
3110
|
+
pass.setBindGroup(0, envBindGroup);
|
|
3095
3111
|
|
|
3096
3112
|
if (transformStates.length > 1 || !transformBindGroup) {
|
|
3097
3113
|
const transformBuffer = Q5.device.createBuffer({
|
|
@@ -3114,24 +3130,31 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
3114
3130
|
});
|
|
3115
3131
|
}
|
|
3116
3132
|
|
|
3117
|
-
|
|
3133
|
+
pass.setBindGroup(1, transformBindGroup);
|
|
3118
3134
|
|
|
3119
3135
|
// run pre-render methods
|
|
3120
3136
|
for (let m of $._hooks.preRender) m();
|
|
3121
3137
|
|
|
3122
|
-
$.pass.setPipeline($.pipelines[0]);
|
|
3123
|
-
|
|
3124
3138
|
// local variables used for performance
|
|
3125
3139
|
let drawStack = $.drawStack;
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3140
|
+
|
|
3141
|
+
let drawVertOffset = 0;
|
|
3142
|
+
let curPipelineIndex = -1;
|
|
3143
|
+
|
|
3144
|
+
for (let i = 0; i < drawStack.length; i += 2) {
|
|
3145
|
+
if (curPipelineIndex != drawStack[i]) {
|
|
3146
|
+
curPipelineIndex = drawStack[i];
|
|
3147
|
+
pass.setPipeline($.pipelines[curPipelineIndex]);
|
|
3148
|
+
}
|
|
3149
|
+
|
|
3150
|
+
let vertCount = drawStack[i + 1];
|
|
3151
|
+
pass.draw(vertCount, 1, drawVertOffset, 0);
|
|
3152
|
+
drawVertOffset += vertCount;
|
|
3130
3153
|
}
|
|
3131
3154
|
};
|
|
3132
3155
|
|
|
3133
3156
|
$._finishRender = () => {
|
|
3134
|
-
|
|
3157
|
+
pass.end();
|
|
3135
3158
|
const commandBuffer = $.encoder.finish();
|
|
3136
3159
|
Q5.device.queue.submit([commandBuffer]);
|
|
3137
3160
|
q.pass = $.encoder = null;
|
|
@@ -3149,7 +3172,7 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
3149
3172
|
$._transformIndexStack.length = 0;
|
|
3150
3173
|
};
|
|
3151
3174
|
|
|
3152
|
-
|
|
3175
|
+
function addColor(r, g, b, a = 1) {
|
|
3153
3176
|
if (typeof r == 'string') r = Q5.color(r);
|
|
3154
3177
|
// grayscale mode `fill(1, 0.5)`
|
|
3155
3178
|
if (b == undefined) {
|
|
@@ -3159,17 +3182,32 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
3159
3182
|
if (r._q5Color) colorsStack.push(...r.levels);
|
|
3160
3183
|
else colorsStack.push(r, g, b, a);
|
|
3161
3184
|
$._colorIndex++;
|
|
3185
|
+
}
|
|
3186
|
+
|
|
3187
|
+
$.fill = function () {
|
|
3188
|
+
addColor(...arguments);
|
|
3189
|
+
$._doFill = true;
|
|
3190
|
+
$._fillIndex = $._colorIndex;
|
|
3191
|
+
};
|
|
3192
|
+
$.stroke = function () {
|
|
3193
|
+
addColor(...arguments);
|
|
3194
|
+
$._doStroke = true;
|
|
3195
|
+
$._fillIndex = $._colorIndex;
|
|
3162
3196
|
};
|
|
3163
|
-
|
|
3164
|
-
$.
|
|
3165
|
-
$.noStroke = () =>
|
|
3197
|
+
|
|
3198
|
+
$.noFill = () => ($._doFill = false);
|
|
3199
|
+
$.noStroke = () => ($._doStroke = false);
|
|
3200
|
+
|
|
3201
|
+
$._strokeWeight = 1;
|
|
3202
|
+
$.strokeWeight = (v) => ($._strokeWeight = v);
|
|
3166
3203
|
|
|
3167
3204
|
$.clear = () => {};
|
|
3168
3205
|
};
|
|
3169
3206
|
|
|
3170
3207
|
Q5.webgpu = async function (scope, parent) {
|
|
3208
|
+
if (!scope || scope == 'global') Q5._hasGlobal = true;
|
|
3171
3209
|
if (!navigator.gpu) {
|
|
3172
|
-
console.
|
|
3210
|
+
console.warn('q5 WebGPU not supported on this browser!');
|
|
3173
3211
|
let q = new Q5(scope, parent);
|
|
3174
3212
|
q.colorMode('rgb', 1);
|
|
3175
3213
|
q._beginRender = () => q.translate(q.canvas.hw, q.canvas.hh);
|
|
@@ -3272,16 +3310,9 @@ fn fragmentMain(@location(1) colorIndex: f32) -> @location(0) vec4<f32> {
|
|
|
3272
3310
|
fragment: {
|
|
3273
3311
|
module: fragmentShader,
|
|
3274
3312
|
entryPoint: 'fragmentMain',
|
|
3275
|
-
targets: [
|
|
3276
|
-
{
|
|
3277
|
-
format: 'bgra8unorm',
|
|
3278
|
-
blend: blendConfig
|
|
3279
|
-
}
|
|
3280
|
-
]
|
|
3313
|
+
targets: [{ format: 'bgra8unorm', blend: blendConfig }]
|
|
3281
3314
|
},
|
|
3282
|
-
primitive: {
|
|
3283
|
-
topology: 'triangle-list'
|
|
3284
|
-
}
|
|
3315
|
+
primitive: { topology: 'triangle-list' }
|
|
3285
3316
|
});
|
|
3286
3317
|
};
|
|
3287
3318
|
|
|
@@ -3395,7 +3426,7 @@ fn fragmentMain(@location(1) colorIndex: f32) -> @location(0) vec4<f32> {
|
|
|
3395
3426
|
}
|
|
3396
3427
|
|
|
3397
3428
|
verticesStack.push(...triangles);
|
|
3398
|
-
drawStack.push(triangles.length / 4);
|
|
3429
|
+
drawStack.push(0, triangles.length / 4);
|
|
3399
3430
|
shapeVertices = [];
|
|
3400
3431
|
};
|
|
3401
3432
|
|
|
@@ -3420,33 +3451,22 @@ fn fragmentMain(@location(1) colorIndex: f32) -> @location(0) vec4<f32> {
|
|
|
3420
3451
|
if ($._matrixDirty) $._saveMatrix();
|
|
3421
3452
|
let ti = $._transformIndex;
|
|
3422
3453
|
// two triangles make a rectangle
|
|
3454
|
+
// prettier-ignore
|
|
3423
3455
|
verticesStack.push(
|
|
3424
|
-
left,
|
|
3425
|
-
top,
|
|
3426
|
-
ci,
|
|
3427
|
-
ti,
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
ci,
|
|
3431
|
-
ti,
|
|
3432
|
-
left,
|
|
3433
|
-
bottom,
|
|
3434
|
-
ci,
|
|
3435
|
-
ti,
|
|
3436
|
-
right,
|
|
3437
|
-
top,
|
|
3438
|
-
ci,
|
|
3439
|
-
ti,
|
|
3440
|
-
left,
|
|
3441
|
-
bottom,
|
|
3442
|
-
ci,
|
|
3443
|
-
ti,
|
|
3444
|
-
right,
|
|
3445
|
-
bottom,
|
|
3446
|
-
ci,
|
|
3447
|
-
ti
|
|
3456
|
+
left, top, ci, ti,
|
|
3457
|
+
right, top, ci, ti,
|
|
3458
|
+
left, bottom, ci, ti,
|
|
3459
|
+
right, top, ci, ti,
|
|
3460
|
+
left, bottom, ci, ti,
|
|
3461
|
+
right, bottom, ci, ti
|
|
3448
3462
|
);
|
|
3449
|
-
drawStack.push(6);
|
|
3463
|
+
drawStack.push(0, 6);
|
|
3464
|
+
};
|
|
3465
|
+
|
|
3466
|
+
$.point = (x, y) => {
|
|
3467
|
+
let sw = $._strokeWeight;
|
|
3468
|
+
if (sw == 1) $.rect(x, y, 1, 1);
|
|
3469
|
+
else $.ellipse(x, y, sw, sw);
|
|
3450
3470
|
};
|
|
3451
3471
|
|
|
3452
3472
|
$.background = () => {};
|
|
@@ -3511,7 +3531,7 @@ fn fragmentMain(@location(1) colorIndex: f32) -> @location(0) vec4<f32> {
|
|
|
3511
3531
|
verticesStack.push(x, y, ci, ti, vx1, vy1, ci, ti, vx2, vy2, ci, ti);
|
|
3512
3532
|
}
|
|
3513
3533
|
|
|
3514
|
-
drawStack.push(n * 3);
|
|
3534
|
+
drawStack.push(0, n * 3);
|
|
3515
3535
|
};
|
|
3516
3536
|
|
|
3517
3537
|
$.circle = (x, y, d) => $.ellipse(x, y, d, d);
|
|
@@ -3550,5 +3570,191 @@ fn fragmentMain(@location(1) colorIndex: f32) -> @location(0) vec4<f32> {
|
|
|
3550
3570
|
$.pass.setBindGroup(2, colorsBindGroup);
|
|
3551
3571
|
});
|
|
3552
3572
|
};
|
|
3553
|
-
Q5.renderers.webgpu.image = ($, q) => {
|
|
3573
|
+
Q5.renderers.webgpu.image = ($, q) => {
|
|
3574
|
+
$.imageStack = [];
|
|
3575
|
+
$.textures = [];
|
|
3576
|
+
|
|
3577
|
+
$._hooks.postCanvas.push(() => {
|
|
3578
|
+
let imageVertexShader = Q5.device.createShaderModule({
|
|
3579
|
+
code: `
|
|
3580
|
+
struct VertexOutput {
|
|
3581
|
+
@builtin(position) position: vec4<f32>,
|
|
3582
|
+
@location(0) texCoord: vec2<f32>,
|
|
3583
|
+
@location(1) textureIndex: f32
|
|
3584
|
+
};
|
|
3585
|
+
|
|
3586
|
+
struct Uniforms {
|
|
3587
|
+
halfWidth: f32,
|
|
3588
|
+
halfHeight: f32
|
|
3589
|
+
};
|
|
3590
|
+
|
|
3591
|
+
@group(0) @binding(0) var<uniform> uniforms: Uniforms;
|
|
3592
|
+
@group(1) @binding(0) var<storage, read> transforms: array<mat4x4<f32>>;
|
|
3593
|
+
|
|
3594
|
+
@vertex
|
|
3595
|
+
fn vertexMain(@location(0) pos: vec2<f32>, @location(1) texCoord: vec2<f32>, @location(2) transformIndex: f32, @location(3) textureIndex: f32) -> VertexOutput {
|
|
3596
|
+
var vert = vec4<f32>(pos, 0.0, 1.0);
|
|
3597
|
+
vert *= transforms[i32(transformIndex)];
|
|
3598
|
+
vert.x /= uniforms.halfWidth;
|
|
3599
|
+
vert.y /= uniforms.halfHeight;
|
|
3600
|
+
|
|
3601
|
+
var output: VertexOutput;
|
|
3602
|
+
output.position = vert;
|
|
3603
|
+
output.texCoord = texCoord;
|
|
3604
|
+
output.textureIndex = textureIndex;
|
|
3605
|
+
return output;
|
|
3606
|
+
}
|
|
3607
|
+
`
|
|
3608
|
+
});
|
|
3609
|
+
|
|
3610
|
+
let imageFragmentShader = Q5.device.createShaderModule({
|
|
3611
|
+
code: `
|
|
3612
|
+
@group(0) @binding(0) var samp: sampler;
|
|
3613
|
+
@group(0) @binding(1) var textures: array<texture_2d<f32>>;
|
|
3614
|
+
|
|
3615
|
+
@fragment
|
|
3616
|
+
fn fragmentMain(@location(0) texCoord: vec2<f32>, @location(1) textureIndex: f32) -> @location(0) vec4<f32> {
|
|
3617
|
+
return textureSample(textures[i32(textureIndex)], samp, texCoord);
|
|
3618
|
+
}
|
|
3619
|
+
`
|
|
3620
|
+
});
|
|
3621
|
+
|
|
3622
|
+
const bindGroupLayouts = [
|
|
3623
|
+
Q5.device.createBindGroupLayout({
|
|
3624
|
+
entries: [
|
|
3625
|
+
{ binding: 0, visibility: GPUShaderStage.VERTEX, buffer: { type: 'uniform' } },
|
|
3626
|
+
{ binding: 1, visibility: GPUShaderStage.FRAGMENT, sampler: {} },
|
|
3627
|
+
{ binding: 2, visibility: GPUShaderStage.FRAGMENT, texture: { viewDimension: '2d', sampleType: 'float' } }
|
|
3628
|
+
]
|
|
3629
|
+
}),
|
|
3630
|
+
Q5.device.createBindGroupLayout({
|
|
3631
|
+
entries: [{ binding: 0, visibility: GPUShaderStage.VERTEX, buffer: { type: 'read-only-storage' } }]
|
|
3632
|
+
})
|
|
3633
|
+
];
|
|
3634
|
+
|
|
3635
|
+
const pipelineLayout = Q5.device.createPipelineLayout({
|
|
3636
|
+
bindGroupLayouts: bindGroupLayouts
|
|
3637
|
+
});
|
|
3638
|
+
|
|
3639
|
+
$.pipelines[1] = Q5.device.createRenderPipeline({
|
|
3640
|
+
layout: pipelineLayout,
|
|
3641
|
+
vertex: {
|
|
3642
|
+
module: imageVertexShader,
|
|
3643
|
+
entryPoint: 'vertexMain',
|
|
3644
|
+
buffers: [
|
|
3645
|
+
{
|
|
3646
|
+
arrayStride: 5 * 4, // 4 floats for position and texCoord, 1 float for textureIndex
|
|
3647
|
+
attributes: [
|
|
3648
|
+
{ shaderLocation: 0, offset: 0, format: 'float32x2' },
|
|
3649
|
+
{ shaderLocation: 1, offset: 2 * 4, format: 'float32x2' },
|
|
3650
|
+
{ shaderLocation: 2, offset: 4 * 4, format: 'float32' } // textureIndex
|
|
3651
|
+
]
|
|
3652
|
+
}
|
|
3653
|
+
]
|
|
3654
|
+
},
|
|
3655
|
+
fragment: {
|
|
3656
|
+
module: imageFragmentShader,
|
|
3657
|
+
entryPoint: 'fragmentMain',
|
|
3658
|
+
targets: [{ format: 'bgra8unorm' }]
|
|
3659
|
+
},
|
|
3660
|
+
primitive: {
|
|
3661
|
+
topology: 'triangle-list'
|
|
3662
|
+
}
|
|
3663
|
+
});
|
|
3664
|
+
|
|
3665
|
+
$.sampler = Q5.device.createSampler({
|
|
3666
|
+
magFilter: 'linear',
|
|
3667
|
+
minFilter: 'linear'
|
|
3668
|
+
});
|
|
3669
|
+
});
|
|
3670
|
+
|
|
3671
|
+
$.loadImage = async (src) => {
|
|
3672
|
+
const img = new Image();
|
|
3673
|
+
img.onload = async () => {
|
|
3674
|
+
const imageBitmap = await createImageBitmap(img);
|
|
3675
|
+
const texture = Q5.device.createTexture({
|
|
3676
|
+
size: [img.width, img.height, 1],
|
|
3677
|
+
format: 'bgra8unorm',
|
|
3678
|
+
usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.RENDER_ATTACHMENT
|
|
3679
|
+
});
|
|
3680
|
+
|
|
3681
|
+
Q5.device.queue.copyExternalImageToTexture({ source: imageBitmap }, { texture }, [img.width, img.height, 1]);
|
|
3682
|
+
|
|
3683
|
+
img.texture = texture;
|
|
3684
|
+
img.index = $.textures.length;
|
|
3685
|
+
$.textures.push(texture);
|
|
3686
|
+
};
|
|
3687
|
+
img.onerror = reject;
|
|
3688
|
+
img.src = src;
|
|
3689
|
+
return img;
|
|
3690
|
+
};
|
|
3691
|
+
|
|
3692
|
+
$._hooks.preRender.push(() => {
|
|
3693
|
+
if (!$.imageStack.length) return;
|
|
3694
|
+
|
|
3695
|
+
// Switch to image pipeline
|
|
3696
|
+
$.pass.setPipeline($.pipelines[1]);
|
|
3697
|
+
|
|
3698
|
+
// Create a vertex buffer for the image quads
|
|
3699
|
+
const vertices = new Float32Array($.vertexStack);
|
|
3700
|
+
|
|
3701
|
+
const vertexBuffer = Q5.device.createBuffer({
|
|
3702
|
+
size: vertices.byteLength,
|
|
3703
|
+
usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST
|
|
3704
|
+
});
|
|
3705
|
+
|
|
3706
|
+
Q5.device.queue.writeBuffer(vertexBuffer, 0, vertices);
|
|
3707
|
+
$.pass.setVertexBuffer(0, vertexBuffer);
|
|
3708
|
+
|
|
3709
|
+
// Set the bind group for the sampler and textures
|
|
3710
|
+
if ($.textures.length !== previousTextureCount) {
|
|
3711
|
+
previousTextureCount = $.textures.length;
|
|
3712
|
+
|
|
3713
|
+
// Create the bind group for all textures
|
|
3714
|
+
const textureViews = $.textures.map((tex) => tex.createView());
|
|
3715
|
+
|
|
3716
|
+
$.textureBindGroup = Q5.device.createBindGroup({
|
|
3717
|
+
layout: $.pipelines[1].getBindGroupLayout(0),
|
|
3718
|
+
entries: [
|
|
3719
|
+
{ binding: 0, resource: $.sampler },
|
|
3720
|
+
...textureViews.map((view, i) => ({ binding: i + 1, resource: view }))
|
|
3721
|
+
]
|
|
3722
|
+
});
|
|
3723
|
+
}
|
|
3724
|
+
|
|
3725
|
+
// Set the bind group for the sampler and textures
|
|
3726
|
+
$.pass.setBindGroup(0, $.textureBindGroup);
|
|
3727
|
+
});
|
|
3728
|
+
|
|
3729
|
+
$.image = (img, x, y, w, h) => {
|
|
3730
|
+
if ($._matrixDirty) $._saveMatrix();
|
|
3731
|
+
let ti = $._transformIndex;
|
|
3732
|
+
|
|
3733
|
+
$.imageStack.push(img.index);
|
|
3734
|
+
|
|
3735
|
+
// Calculate half-width and half-height
|
|
3736
|
+
let hw = w / 2;
|
|
3737
|
+
let hh = h / 2;
|
|
3738
|
+
|
|
3739
|
+
// Calculate vertices positions
|
|
3740
|
+
let left = x - hw;
|
|
3741
|
+
let right = x + hw;
|
|
3742
|
+
let top = -(y - hh); // y is inverted in WebGPU
|
|
3743
|
+
let bottom = -(y + hh);
|
|
3744
|
+
|
|
3745
|
+
let ii = img.index;
|
|
3746
|
+
|
|
3747
|
+
// prettier-ignore
|
|
3748
|
+
$.vertexStack.push(
|
|
3749
|
+
left, top, 0, 0, ti, ii,
|
|
3750
|
+
right, top, 1, 0, ti, ii,
|
|
3751
|
+
left, bottom, 0, 1, ti, ii,
|
|
3752
|
+
right, top, 1, 0, ti, ii,
|
|
3753
|
+
left, bottom, 0, 1, ti, ii,
|
|
3754
|
+
right, bottom, 1, 1, ti, ii
|
|
3755
|
+
);
|
|
3756
|
+
|
|
3757
|
+
$.drawStack.push(6);
|
|
3758
|
+
};
|
|
3759
|
+
};
|
|
3554
3760
|
Q5.renderers.webgpu.text = ($, q) => {};
|