q5 2.12.12 → 2.13.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/q5.js +250 -247
- package/q5.min.js +2 -2
- package/src/q5-2d-canvas.js +1 -0
- package/src/q5-core.js +1 -1
- package/src/q5-webgpu-canvas.js +100 -107
- package/src/q5-webgpu-drawing.js +27 -35
- package/src/q5-webgpu-image.js +86 -65
- package/src/q5-webgpu-text.js +35 -39
package/src/q5-webgpu-image.js
CHANGED
|
@@ -1,51 +1,64 @@
|
|
|
1
1
|
Q5.renderers.webgpu.image = ($, q) => {
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
let vertexStack = new Float32Array(1e7),
|
|
3
|
+
vertIndex = 0;
|
|
4
4
|
|
|
5
|
-
let
|
|
6
|
-
label: '
|
|
5
|
+
let imageShader = Q5.device.createShaderModule({
|
|
6
|
+
label: 'imageShader',
|
|
7
7
|
code: `
|
|
8
|
-
struct VertexOutput {
|
|
9
|
-
@builtin(position) position: vec4f,
|
|
10
|
-
@location(0) texCoord: vec2f
|
|
11
|
-
}
|
|
12
8
|
struct Uniforms {
|
|
13
9
|
halfWidth: f32,
|
|
14
10
|
halfHeight: f32
|
|
15
11
|
}
|
|
12
|
+
struct VertexParams {
|
|
13
|
+
@location(0) pos: vec2f,
|
|
14
|
+
@location(1) texCoord: vec2f,
|
|
15
|
+
@location(2) tintIndex: f32,
|
|
16
|
+
@location(3) matrixIndex: f32,
|
|
17
|
+
@location(4) globalAlpha: f32
|
|
18
|
+
}
|
|
19
|
+
struct FragmentParams {
|
|
20
|
+
@builtin(position) position: vec4f,
|
|
21
|
+
@location(0) texCoord: vec2f,
|
|
22
|
+
@location(1) tintIndex: f32,
|
|
23
|
+
@location(2) globalAlpha: f32
|
|
24
|
+
}
|
|
16
25
|
|
|
17
26
|
@group(0) @binding(0) var<uniform> uniforms: Uniforms;
|
|
18
27
|
@group(0) @binding(1) var<storage> transforms: array<mat4x4<f32>>;
|
|
28
|
+
@group(0) @binding(2) var<storage> colors : array<vec4f>;
|
|
29
|
+
|
|
30
|
+
@group(1) @binding(0) var samp: sampler;
|
|
31
|
+
@group(1) @binding(1) var texture: texture_2d<f32>;
|
|
19
32
|
|
|
20
33
|
@vertex
|
|
21
|
-
fn vertexMain(
|
|
22
|
-
var vert = vec4f(pos, 0.0, 1.0);
|
|
23
|
-
vert = transforms[i32(
|
|
34
|
+
fn vertexMain(v: VertexParams) -> FragmentParams {
|
|
35
|
+
var vert = vec4f(v.pos, 0.0, 1.0);
|
|
36
|
+
vert = transforms[i32(v.matrixIndex)] * vert;
|
|
24
37
|
vert.x /= uniforms.halfWidth;
|
|
25
38
|
vert.y /= uniforms.halfHeight;
|
|
26
39
|
|
|
27
|
-
var
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
40
|
+
var f: FragmentParams;
|
|
41
|
+
f.position = vert;
|
|
42
|
+
f.texCoord = v.texCoord;
|
|
43
|
+
f.tintIndex = v.tintIndex;
|
|
44
|
+
f.globalAlpha = v.globalAlpha;
|
|
45
|
+
return f;
|
|
31
46
|
}
|
|
32
|
-
`
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
let fragmentShader = Q5.device.createShaderModule({
|
|
36
|
-
label: 'imageFragmentShader',
|
|
37
|
-
code: `
|
|
38
|
-
@group(2) @binding(0) var samp: sampler;
|
|
39
|
-
@group(2) @binding(1) var texture: texture_2d<f32>;
|
|
40
47
|
|
|
41
48
|
@fragment
|
|
42
|
-
fn fragmentMain(
|
|
43
|
-
|
|
44
|
-
|
|
49
|
+
fn fragmentMain(f: FragmentParams) -> @location(0) vec4f {
|
|
50
|
+
let texColor = textureSample(texture, samp, f.texCoord);
|
|
51
|
+
let tintColor = colors[i32(f.tintIndex)];
|
|
52
|
+
|
|
53
|
+
// Mix original and tinted colors using tint alpha as blend factor
|
|
54
|
+
let tinted = vec4f(texColor.rgb * tintColor.rgb, texColor.a * f.globalAlpha);
|
|
55
|
+
return mix(texColor, tinted, tintColor.a);
|
|
45
56
|
}
|
|
46
57
|
`
|
|
47
58
|
});
|
|
48
59
|
|
|
60
|
+
$._textureBindGroups = [];
|
|
61
|
+
|
|
49
62
|
let textureLayout = Q5.device.createBindGroupLayout({
|
|
50
63
|
label: 'textureLayout',
|
|
51
64
|
entries: [
|
|
@@ -63,11 +76,13 @@ fn fragmentMain(@location(0) texCoord: vec2f) -> @location(0) vec4f {
|
|
|
63
76
|
});
|
|
64
77
|
|
|
65
78
|
const vertexBufferLayout = {
|
|
66
|
-
arrayStride:
|
|
79
|
+
arrayStride: 28,
|
|
67
80
|
attributes: [
|
|
68
81
|
{ shaderLocation: 0, offset: 0, format: 'float32x2' },
|
|
69
82
|
{ shaderLocation: 1, offset: 8, format: 'float32x2' },
|
|
70
|
-
{ shaderLocation: 2, offset: 16, format: 'float32' } //
|
|
83
|
+
{ shaderLocation: 2, offset: 16, format: 'float32' }, // tintIndex
|
|
84
|
+
{ shaderLocation: 3, offset: 20, format: 'float32' }, // matrixIndex
|
|
85
|
+
{ shaderLocation: 4, offset: 24, format: 'float32' } // globalAlpha
|
|
71
86
|
]
|
|
72
87
|
};
|
|
73
88
|
|
|
@@ -80,12 +95,12 @@ fn fragmentMain(@location(0) texCoord: vec2f) -> @location(0) vec4f {
|
|
|
80
95
|
label: 'imagePipeline',
|
|
81
96
|
layout: pipelineLayout,
|
|
82
97
|
vertex: {
|
|
83
|
-
module:
|
|
98
|
+
module: imageShader,
|
|
84
99
|
entryPoint: 'vertexMain',
|
|
85
100
|
buffers: [{ arrayStride: 0, attributes: [] }, vertexBufferLayout]
|
|
86
101
|
},
|
|
87
102
|
fragment: {
|
|
88
|
-
module:
|
|
103
|
+
module: imageShader,
|
|
89
104
|
entryPoint: 'fragmentMain',
|
|
90
105
|
targets: [{ format: 'bgra8unorm', blend: $.blendConfigs.normal }]
|
|
91
106
|
},
|
|
@@ -161,58 +176,64 @@ fn fragmentMain(@location(0) texCoord: vec2f) -> @location(0) vec4f {
|
|
|
161
176
|
|
|
162
177
|
$.loadImage = (src, cb) => {
|
|
163
178
|
q._preloadCount++;
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
// calculate the default width and height that the image
|
|
168
|
-
// should be drawn at if the user doesn't specify a display size
|
|
169
|
-
img.defaultWidth = img.width * $._defaultImageScale;
|
|
170
|
-
img.defaultHeight = img.height * $._defaultImageScale;
|
|
171
|
-
img.pixelDensity = 1;
|
|
172
|
-
|
|
179
|
+
let g = $._g.loadImage(src, (img) => {
|
|
180
|
+
g.defaultWidth = img.width * $._defaultImageScale;
|
|
181
|
+
g.defaultHeight = img.height * $._defaultImageScale;
|
|
173
182
|
$._createTexture(img);
|
|
174
183
|
q._preloadCount--;
|
|
175
184
|
if (cb) cb(img);
|
|
176
|
-
};
|
|
177
|
-
|
|
178
|
-
return img;
|
|
185
|
+
});
|
|
186
|
+
return g;
|
|
179
187
|
};
|
|
180
188
|
|
|
181
189
|
$.imageMode = (x) => ($._imageMode = x);
|
|
182
190
|
|
|
183
|
-
|
|
191
|
+
const addVert = (x, y, u, v, ci, ti, ga) => {
|
|
192
|
+
let s = vertexStack,
|
|
193
|
+
i = vertIndex;
|
|
194
|
+
s[i++] = x;
|
|
195
|
+
s[i++] = y;
|
|
196
|
+
s[i++] = u;
|
|
197
|
+
s[i++] = v;
|
|
198
|
+
s[i++] = ci;
|
|
199
|
+
s[i++] = ti;
|
|
200
|
+
s[i++] = ga;
|
|
201
|
+
vertIndex = i;
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
$.image = (img, dx = 0, dy = 0, dw, dh, sx = 0, sy = 0, sw, sh) => {
|
|
205
|
+
let g = img;
|
|
184
206
|
if (img.canvas) img = img.canvas;
|
|
185
207
|
if (img.textureIndex == undefined) return;
|
|
186
208
|
|
|
187
209
|
if ($._matrixDirty) $._saveMatrix();
|
|
188
|
-
let ti = $._transformIndex;
|
|
189
210
|
|
|
190
|
-
let w = img.width
|
|
191
|
-
|
|
211
|
+
let w = img.width,
|
|
212
|
+
h = img.height,
|
|
213
|
+
pd = g._pixelDensity || 1;
|
|
192
214
|
|
|
193
|
-
dw ??=
|
|
194
|
-
dh ??=
|
|
215
|
+
dw ??= g.defaultWidth;
|
|
216
|
+
dh ??= g.defaultHeight;
|
|
195
217
|
sw ??= w;
|
|
196
218
|
sh ??= h;
|
|
197
219
|
|
|
198
|
-
let pd = img.pixelDensity || 1;
|
|
199
220
|
dw *= pd;
|
|
200
221
|
dh *= pd;
|
|
201
222
|
|
|
202
223
|
let [l, r, t, b] = $._calcBox(dx, dy, dw, dh, $._imageMode);
|
|
203
224
|
|
|
204
|
-
let u0 = sx / w
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
);
|
|
225
|
+
let u0 = sx / w,
|
|
226
|
+
v0 = sy / h,
|
|
227
|
+
u1 = (sx + sw) / w,
|
|
228
|
+
v1 = (sy + sh) / h,
|
|
229
|
+
ti = $._matrixIndex,
|
|
230
|
+
ci = $._tint,
|
|
231
|
+
ga = $._globalAlpha;
|
|
232
|
+
|
|
233
|
+
addVert(l, t, u0, v0, ci, ti, ga);
|
|
234
|
+
addVert(r, t, u1, v0, ci, ti, ga);
|
|
235
|
+
addVert(l, b, u0, v1, ci, ti, ga);
|
|
236
|
+
addVert(r, b, u1, v1, ci, ti, ga);
|
|
216
237
|
|
|
217
238
|
$.drawStack.push(1, img.textureIndex);
|
|
218
239
|
};
|
|
@@ -223,20 +244,20 @@ fn fragmentMain(@location(0) texCoord: vec2f) -> @location(0) vec4f {
|
|
|
223
244
|
// Switch to image pipeline
|
|
224
245
|
$.pass.setPipeline($._pipelines[1]);
|
|
225
246
|
|
|
226
|
-
|
|
227
|
-
size:
|
|
247
|
+
let vertexBuffer = Q5.device.createBuffer({
|
|
248
|
+
size: vertIndex * 5,
|
|
228
249
|
usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
|
|
229
250
|
mappedAtCreation: true
|
|
230
251
|
});
|
|
231
252
|
|
|
232
|
-
new Float32Array(vertexBuffer.getMappedRange()).set(vertexStack);
|
|
253
|
+
new Float32Array(vertexBuffer.getMappedRange()).set(vertexStack.slice(0, vertIndex));
|
|
233
254
|
vertexBuffer.unmap();
|
|
234
255
|
|
|
235
256
|
$.pass.setVertexBuffer(1, vertexBuffer);
|
|
236
257
|
});
|
|
237
258
|
|
|
238
259
|
$._hooks.postRender.push(() => {
|
|
239
|
-
|
|
260
|
+
vertIndex = 0;
|
|
240
261
|
});
|
|
241
262
|
};
|
|
242
263
|
|
package/src/q5-webgpu-text.js
CHANGED
|
@@ -2,14 +2,15 @@ Q5.renderers.webgpu.text = ($, q) => {
|
|
|
2
2
|
let textShader = Q5.device.createShaderModule({
|
|
3
3
|
label: 'MSDF text shader',
|
|
4
4
|
code: `
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
struct Uniforms {
|
|
6
|
+
halfWidth: f32,
|
|
7
|
+
halfHeight: f32
|
|
8
|
+
}
|
|
9
|
+
struct VertexParams {
|
|
9
10
|
@builtin(vertex_index) vertex : u32,
|
|
10
11
|
@builtin(instance_index) instance : u32
|
|
11
12
|
}
|
|
12
|
-
struct
|
|
13
|
+
struct FragmentParams {
|
|
13
14
|
@builtin(position) position : vec4f,
|
|
14
15
|
@location(0) texCoord : vec2f,
|
|
15
16
|
@location(1) fillColor : vec4f
|
|
@@ -23,47 +24,44 @@ struct Char {
|
|
|
23
24
|
struct Text {
|
|
24
25
|
pos: vec2f,
|
|
25
26
|
scale: f32,
|
|
26
|
-
|
|
27
|
+
matrixIndex: f32,
|
|
27
28
|
fillIndex: f32,
|
|
28
29
|
strokeIndex: f32
|
|
29
30
|
}
|
|
30
|
-
struct Uniforms {
|
|
31
|
-
halfWidth: f32,
|
|
32
|
-
halfHeight: f32
|
|
33
|
-
}
|
|
34
31
|
|
|
35
32
|
@group(0) @binding(0) var<uniform> uniforms: Uniforms;
|
|
36
33
|
@group(0) @binding(1) var<storage> transforms: array<mat4x4<f32>>;
|
|
34
|
+
@group(0) @binding(2) var<storage> colors : array<vec4f>;
|
|
37
35
|
|
|
38
|
-
@group(1) @binding(0) var
|
|
36
|
+
@group(1) @binding(0) var fontTexture: texture_2d<f32>;
|
|
37
|
+
@group(1) @binding(1) var fontSampler: sampler;
|
|
38
|
+
@group(1) @binding(2) var<storage> fontChars: array<Char>;
|
|
39
39
|
|
|
40
|
-
@group(2) @binding(0) var
|
|
41
|
-
@group(2) @binding(1) var
|
|
42
|
-
@group(2) @binding(2) var<storage> fontChars: array<Char>;
|
|
40
|
+
@group(2) @binding(0) var<storage> textChars: array<vec4f>;
|
|
41
|
+
@group(2) @binding(1) var<storage> textMetadata: array<Text>;
|
|
43
42
|
|
|
44
|
-
|
|
45
|
-
@group(3) @binding(1) var<storage> textMetadata: array<Text>;
|
|
43
|
+
const quad = array(vec2f(0, -1), vec2f(1, -1), vec2f(0, 0), vec2f(1, 0));
|
|
46
44
|
|
|
47
45
|
@vertex
|
|
48
|
-
fn vertexMain(
|
|
49
|
-
let char = textChars[
|
|
46
|
+
fn vertexMain(v : VertexParams) -> FragmentParams {
|
|
47
|
+
let char = textChars[v.instance];
|
|
50
48
|
|
|
51
49
|
let text = textMetadata[i32(char.w)];
|
|
52
50
|
|
|
53
51
|
let fontChar = fontChars[i32(char.z)];
|
|
54
52
|
|
|
55
|
-
let charPos = ((
|
|
53
|
+
let charPos = ((quad[v.vertex] * fontChar.size + char.xy + fontChar.offset) * text.scale) + text.pos;
|
|
56
54
|
|
|
57
55
|
var vert = vec4f(charPos, 0.0, 1.0);
|
|
58
|
-
vert = transforms[i32(text.
|
|
56
|
+
vert = transforms[i32(text.matrixIndex)] * vert;
|
|
59
57
|
vert.x /= uniforms.halfWidth;
|
|
60
58
|
vert.y /= uniforms.halfHeight;
|
|
61
59
|
|
|
62
|
-
var
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
return
|
|
60
|
+
var f : FragmentParams;
|
|
61
|
+
f.position = vert;
|
|
62
|
+
f.texCoord = (quad[v.vertex] * vec2f(1, -1)) * fontChar.texExtent + fontChar.texOffset;
|
|
63
|
+
f.fillColor = colors[i32(text.fillIndex)];
|
|
64
|
+
return f;
|
|
67
65
|
}
|
|
68
66
|
|
|
69
67
|
fn sampleMsdf(texCoord: vec2f) -> f32 {
|
|
@@ -72,22 +70,22 @@ fn sampleMsdf(texCoord: vec2f) -> f32 {
|
|
|
72
70
|
}
|
|
73
71
|
|
|
74
72
|
@fragment
|
|
75
|
-
fn fragmentMain(
|
|
73
|
+
fn fragmentMain(f : FragmentParams) -> @location(0) vec4f {
|
|
76
74
|
// pxRange (AKA distanceRange) comes from the msdfgen tool,
|
|
77
75
|
// uses the default which is 4.
|
|
78
76
|
let pxRange = 4.0;
|
|
79
77
|
let sz = vec2f(textureDimensions(fontTexture, 0));
|
|
80
|
-
let dx = sz.x*length(vec2f(dpdxFine(
|
|
81
|
-
let dy = sz.y*length(vec2f(dpdxFine(
|
|
78
|
+
let dx = sz.x*length(vec2f(dpdxFine(f.texCoord.x), dpdyFine(f.texCoord.x)));
|
|
79
|
+
let dy = sz.y*length(vec2f(dpdxFine(f.texCoord.y), dpdyFine(f.texCoord.y)));
|
|
82
80
|
let toPixels = pxRange * inverseSqrt(dx * dx + dy * dy);
|
|
83
|
-
let sigDist = sampleMsdf(
|
|
81
|
+
let sigDist = sampleMsdf(f.texCoord) - 0.5;
|
|
84
82
|
let pxDist = sigDist * toPixels;
|
|
85
83
|
let edgeWidth = 0.5;
|
|
86
84
|
let alpha = smoothstep(-edgeWidth, edgeWidth, pxDist);
|
|
87
85
|
if (alpha < 0.001) {
|
|
88
86
|
discard;
|
|
89
87
|
}
|
|
90
|
-
return vec4f(
|
|
88
|
+
return vec4f(f.fillColor.rgb, f.fillColor.a * alpha);
|
|
91
89
|
}
|
|
92
90
|
`
|
|
93
91
|
});
|
|
@@ -279,13 +277,11 @@ fn fragmentMain(input : VertexOutput) -> @location(0) vec4f {
|
|
|
279
277
|
if (cb) cb(fontName);
|
|
280
278
|
};
|
|
281
279
|
|
|
282
|
-
|
|
283
|
-
let g = $.createGraphics(1, 1);
|
|
284
|
-
g.colorMode($.RGB, 1);
|
|
280
|
+
$._g.colorMode($.RGB, 1);
|
|
285
281
|
|
|
286
282
|
$.loadFont = (url, cb) => {
|
|
287
283
|
let ext = url.slice(url.lastIndexOf('.') + 1);
|
|
288
|
-
if (ext != 'json') return
|
|
284
|
+
if (ext != 'json') return $._g.loadFont(url, cb);
|
|
289
285
|
let fontName = url.slice(url.lastIndexOf('/') + 1, url.lastIndexOf('-'));
|
|
290
286
|
createFont(url, fontName, cb);
|
|
291
287
|
return fontName;
|
|
@@ -470,7 +466,7 @@ fn fragmentMain(input : VertexOutput) -> @location(0) vec4f {
|
|
|
470
466
|
text[0] = x;
|
|
471
467
|
text[1] = -y;
|
|
472
468
|
text[2] = $._textSize / 44;
|
|
473
|
-
text[3] = $.
|
|
469
|
+
text[3] = $._matrixIndex;
|
|
474
470
|
text[4] = $._fillSet ? $._fill : 0;
|
|
475
471
|
text[5] = $._stroke;
|
|
476
472
|
|
|
@@ -484,18 +480,18 @@ fn fragmentMain(input : VertexOutput) -> @location(0) vec4f {
|
|
|
484
480
|
};
|
|
485
481
|
|
|
486
482
|
$.createTextImage = (str, w, h) => {
|
|
487
|
-
|
|
483
|
+
$._g.textSize($._textSize);
|
|
488
484
|
|
|
489
485
|
if ($._doFill) {
|
|
490
486
|
let fi = $._fill * 4;
|
|
491
|
-
|
|
487
|
+
$._g.fill(colorStack.slice(fi, fi + 4));
|
|
492
488
|
}
|
|
493
489
|
if ($._doStroke) {
|
|
494
490
|
let si = $._stroke * 4;
|
|
495
|
-
|
|
491
|
+
$._g.stroke(colorStack.slice(si, si + 4));
|
|
496
492
|
}
|
|
497
493
|
|
|
498
|
-
let img =
|
|
494
|
+
let img = $._g.createTextImage(str, w, h);
|
|
499
495
|
|
|
500
496
|
if (img.canvas.textureIndex == undefined) {
|
|
501
497
|
$._createTexture(img);
|