q5 2.13.1 → 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 +141 -171
- package/q5.min.js +1 -1
- package/src/q5-2d-canvas.js +1 -0
- package/src/q5-webgpu-canvas.js +50 -76
- package/src/q5-webgpu-drawing.js +19 -27
- package/src/q5-webgpu-image.js +45 -40
- package/src/q5-webgpu-text.js +26 -28
package/src/q5-webgpu-canvas.js
CHANGED
|
@@ -18,7 +18,6 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
18
18
|
|
|
19
19
|
let pass,
|
|
20
20
|
mainView,
|
|
21
|
-
colorsLayout,
|
|
22
21
|
colorIndex = 1,
|
|
23
22
|
colorStackIndex = 8;
|
|
24
23
|
|
|
@@ -30,7 +29,6 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
30
29
|
let drawStack = ($.drawStack = []);
|
|
31
30
|
|
|
32
31
|
// colors used for each draw call
|
|
33
|
-
|
|
34
32
|
let colorStack = ($.colorStack = new Float32Array(1e6));
|
|
35
33
|
|
|
36
34
|
// prettier-ignore
|
|
@@ -39,43 +37,28 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
39
37
|
1, 1, 1, 1 // white
|
|
40
38
|
]);
|
|
41
39
|
|
|
42
|
-
|
|
43
|
-
label: '
|
|
40
|
+
let mainLayout = Q5.device.createBindGroupLayout({
|
|
41
|
+
label: 'mainLayout',
|
|
44
42
|
entries: [
|
|
45
43
|
{
|
|
46
44
|
binding: 0,
|
|
47
45
|
visibility: GPUShaderStage.VERTEX,
|
|
48
|
-
buffer: {
|
|
49
|
-
type: 'uniform',
|
|
50
|
-
hasDynamicOffset: false
|
|
51
|
-
}
|
|
46
|
+
buffer: { type: 'uniform' }
|
|
52
47
|
},
|
|
53
48
|
{
|
|
54
49
|
binding: 1,
|
|
55
50
|
visibility: GPUShaderStage.VERTEX,
|
|
56
|
-
buffer: {
|
|
57
|
-
|
|
58
|
-
hasDynamicOffset: false
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
]
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
colorsLayout = Q5.device.createBindGroupLayout({
|
|
65
|
-
label: 'colorsLayout',
|
|
66
|
-
entries: [
|
|
51
|
+
buffer: { type: 'read-only-storage' }
|
|
52
|
+
},
|
|
67
53
|
{
|
|
68
|
-
binding:
|
|
54
|
+
binding: 2,
|
|
69
55
|
visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT,
|
|
70
|
-
buffer: {
|
|
71
|
-
type: 'read-only-storage',
|
|
72
|
-
hasDynamicOffset: false
|
|
73
|
-
}
|
|
56
|
+
buffer: { type: 'read-only-storage' }
|
|
74
57
|
}
|
|
75
58
|
]
|
|
76
59
|
});
|
|
77
60
|
|
|
78
|
-
$.bindGroupLayouts = [
|
|
61
|
+
$.bindGroupLayouts = [mainLayout];
|
|
79
62
|
|
|
80
63
|
let uniformBuffer = Q5.device.createBuffer({
|
|
81
64
|
size: 8, // Size of two floats
|
|
@@ -105,7 +88,6 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
105
88
|
Q5.device.queue.writeBuffer(uniformBuffer, 0, new Float32Array([$.canvas.hw, $.canvas.hh]));
|
|
106
89
|
|
|
107
90
|
createMainView();
|
|
108
|
-
|
|
109
91
|
return c;
|
|
110
92
|
};
|
|
111
93
|
|
|
@@ -149,7 +131,7 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
149
131
|
};
|
|
150
132
|
|
|
151
133
|
$._stroke = 0;
|
|
152
|
-
$._fill = $._tint = 1;
|
|
134
|
+
$._fill = $._tint = $._globalAlpha = 1;
|
|
153
135
|
$._doFill = $._doStroke = true;
|
|
154
136
|
|
|
155
137
|
$.fill = (r, g, b, a) => {
|
|
@@ -166,6 +148,7 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
166
148
|
addColor(r, g, b, a);
|
|
167
149
|
$._tint = colorIndex;
|
|
168
150
|
};
|
|
151
|
+
$.opacity = (a) => ($._globalAlpha = a);
|
|
169
152
|
|
|
170
153
|
$.noFill = () => ($._doFill = false);
|
|
171
154
|
$.noStroke = () => ($._doStroke = false);
|
|
@@ -179,6 +162,7 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
179
162
|
transforms = new Float32Array(MAX_TRANSFORMS * MATRIX_SIZE),
|
|
180
163
|
matrices = [],
|
|
181
164
|
matricesIndexStack = [];
|
|
165
|
+
|
|
182
166
|
let matrix;
|
|
183
167
|
|
|
184
168
|
// tracks if the matrix has been modified
|
|
@@ -214,12 +198,10 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
214
198
|
if (!a) return;
|
|
215
199
|
if ($._angleMode) a *= $._DEGTORAD;
|
|
216
200
|
|
|
217
|
-
let cosR = Math.cos(a)
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
let m0 = m[0],
|
|
201
|
+
let cosR = Math.cos(a),
|
|
202
|
+
sinR = Math.sin(a),
|
|
203
|
+
m = matrix,
|
|
204
|
+
m0 = m[0],
|
|
223
205
|
m1 = m[1],
|
|
224
206
|
m4 = m[4],
|
|
225
207
|
m5 = m[5];
|
|
@@ -266,15 +248,15 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
266
248
|
if (!ang) return;
|
|
267
249
|
if ($._angleMode) ang *= $._DEGTORAD;
|
|
268
250
|
|
|
269
|
-
let tanAng = Math.tan(ang)
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
m1 =
|
|
273
|
-
m4 =
|
|
274
|
-
m5 =
|
|
251
|
+
let tanAng = Math.tan(ang),
|
|
252
|
+
m = matrix,
|
|
253
|
+
m0 = m[0],
|
|
254
|
+
m1 = m[1],
|
|
255
|
+
m4 = m[4],
|
|
256
|
+
m5 = m[5];
|
|
275
257
|
|
|
276
|
-
|
|
277
|
-
|
|
258
|
+
m[0] = m0 + m4 * tanAng;
|
|
259
|
+
m[1] = m1 + m5 * tanAng;
|
|
278
260
|
|
|
279
261
|
$._matrixDirty = true;
|
|
280
262
|
};
|
|
@@ -283,15 +265,15 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
283
265
|
if (!ang) return;
|
|
284
266
|
if ($._angleMode) ang *= $._DEGTORAD;
|
|
285
267
|
|
|
286
|
-
let tanAng = Math.tan(ang)
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
m1 =
|
|
290
|
-
m4 =
|
|
291
|
-
m5 =
|
|
268
|
+
let tanAng = Math.tan(ang),
|
|
269
|
+
m = matrix,
|
|
270
|
+
m0 = m[0],
|
|
271
|
+
m1 = m[1],
|
|
272
|
+
m4 = m[4],
|
|
273
|
+
m5 = m[5];
|
|
292
274
|
|
|
293
|
-
|
|
294
|
-
|
|
275
|
+
m[4] = m4 + m0 * tanAng;
|
|
276
|
+
m[5] = m5 + m1 * tanAng;
|
|
295
277
|
|
|
296
278
|
$._matrixDirty = true;
|
|
297
279
|
};
|
|
@@ -459,26 +441,14 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
459
441
|
};
|
|
460
442
|
|
|
461
443
|
$._render = () => {
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
});
|
|
468
|
-
|
|
469
|
-
new Float32Array(transformBuffer.getMappedRange()).set(transforms.slice(0, matrices.length * MATRIX_SIZE));
|
|
470
|
-
transformBuffer.unmap();
|
|
471
|
-
|
|
472
|
-
$._transformBindGroup = Q5.device.createBindGroup({
|
|
473
|
-
layout: $._transformLayout,
|
|
474
|
-
entries: [
|
|
475
|
-
{ binding: 0, resource: { buffer: uniformBuffer } },
|
|
476
|
-
{ binding: 1, resource: { buffer: transformBuffer } }
|
|
477
|
-
]
|
|
478
|
-
});
|
|
479
|
-
}
|
|
444
|
+
let transformBuffer = Q5.device.createBuffer({
|
|
445
|
+
size: matrices.length * MATRIX_SIZE * 4, // 4 bytes per float
|
|
446
|
+
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
|
|
447
|
+
mappedAtCreation: true
|
|
448
|
+
});
|
|
480
449
|
|
|
481
|
-
|
|
450
|
+
new Float32Array(transformBuffer.getMappedRange()).set(transforms.slice(0, matrices.length * MATRIX_SIZE));
|
|
451
|
+
transformBuffer.unmap();
|
|
482
452
|
|
|
483
453
|
let colorsBuffer = Q5.device.createBuffer({
|
|
484
454
|
size: colorStackIndex * 4,
|
|
@@ -489,12 +459,16 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
489
459
|
new Float32Array(colorsBuffer.getMappedRange()).set(colorStack.slice(0, colorStackIndex));
|
|
490
460
|
colorsBuffer.unmap();
|
|
491
461
|
|
|
492
|
-
|
|
493
|
-
layout:
|
|
494
|
-
entries: [
|
|
462
|
+
mainBindGroup = Q5.device.createBindGroup({
|
|
463
|
+
layout: mainLayout,
|
|
464
|
+
entries: [
|
|
465
|
+
{ binding: 0, resource: { buffer: uniformBuffer } },
|
|
466
|
+
{ binding: 1, resource: { buffer: transformBuffer } },
|
|
467
|
+
{ binding: 2, resource: { buffer: colorsBuffer } }
|
|
468
|
+
]
|
|
495
469
|
});
|
|
496
470
|
|
|
497
|
-
pass.setBindGroup(
|
|
471
|
+
pass.setBindGroup(0, mainBindGroup);
|
|
498
472
|
|
|
499
473
|
for (let m of $._hooks.preRender) m();
|
|
500
474
|
|
|
@@ -518,11 +492,11 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
518
492
|
pass.draw(v, 1, drawVertOffset);
|
|
519
493
|
drawVertOffset += v;
|
|
520
494
|
} else if (curPipelineIndex == 1) {
|
|
521
|
-
// let
|
|
495
|
+
// let instanceCount = drawStack[i + 2];
|
|
522
496
|
// draw images
|
|
523
497
|
if (curTextureIndex != v) {
|
|
524
498
|
// v is the texture index
|
|
525
|
-
pass.setBindGroup(
|
|
499
|
+
pass.setBindGroup(1, $._textureBindGroups[v]);
|
|
526
500
|
}
|
|
527
501
|
pass.draw(4, 1, imageVertOffset);
|
|
528
502
|
imageVertOffset += 4;
|
|
@@ -530,8 +504,8 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
530
504
|
} else if (curPipelineIndex == 2) {
|
|
531
505
|
// draw text
|
|
532
506
|
let o = drawStack[i + 2];
|
|
533
|
-
pass.setBindGroup(
|
|
534
|
-
pass.setBindGroup(
|
|
507
|
+
pass.setBindGroup(1, $._fonts[o].bindGroup);
|
|
508
|
+
pass.setBindGroup(2, $._textBindGroup);
|
|
535
509
|
|
|
536
510
|
// v is the number of characters in the text
|
|
537
511
|
pass.draw(4, v, 0, textCharOffset);
|
package/src/q5-webgpu-drawing.js
CHANGED
|
@@ -4,46 +4,40 @@ Q5.renderers.webgpu.drawing = ($, q) => {
|
|
|
4
4
|
vertexStack = new Float32Array(1e7),
|
|
5
5
|
vertIndex = 0;
|
|
6
6
|
|
|
7
|
-
let
|
|
8
|
-
label: '
|
|
7
|
+
let drawingShader = Q5.device.createShaderModule({
|
|
8
|
+
label: 'drawingShader',
|
|
9
9
|
code: `
|
|
10
|
-
struct
|
|
10
|
+
struct Uniforms {
|
|
11
|
+
halfWidth: f32,
|
|
12
|
+
halfHeight: f32
|
|
13
|
+
}
|
|
14
|
+
struct VertexParams {
|
|
11
15
|
@location(0) pos: vec2f,
|
|
12
16
|
@location(1) colorIndex: f32,
|
|
13
17
|
@location(2) matrixIndex: f32
|
|
14
18
|
}
|
|
15
|
-
struct
|
|
19
|
+
struct FragmentParams {
|
|
16
20
|
@builtin(position) position: vec4f,
|
|
17
21
|
@location(0) color: vec4f
|
|
18
22
|
}
|
|
19
|
-
struct Uniforms {
|
|
20
|
-
halfWidth: f32,
|
|
21
|
-
halfHeight: f32
|
|
22
|
-
}
|
|
23
23
|
|
|
24
24
|
@group(0) @binding(0) var<uniform> uniforms: Uniforms;
|
|
25
25
|
@group(0) @binding(1) var<storage> transforms: array<mat4x4<f32>>;
|
|
26
|
-
|
|
27
|
-
@group(1) @binding(0) var<storage> colors : array<vec4f>;
|
|
26
|
+
@group(0) @binding(2) var<storage> colors : array<vec4f>;
|
|
28
27
|
|
|
29
28
|
@vertex
|
|
30
|
-
fn vertexMain(
|
|
31
|
-
var vert = vec4f(
|
|
32
|
-
vert = transforms[i32(
|
|
29
|
+
fn vertexMain(v: VertexParams) -> FragmentParams {
|
|
30
|
+
var vert = vec4f(v.pos, 0.0, 1.0);
|
|
31
|
+
vert = transforms[i32(v.matrixIndex)] * vert;
|
|
33
32
|
vert.x /= uniforms.halfWidth;
|
|
34
33
|
vert.y /= uniforms.halfHeight;
|
|
35
34
|
|
|
36
|
-
var
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
return
|
|
35
|
+
var f: FragmentParams;
|
|
36
|
+
f.position = vert;
|
|
37
|
+
f.color = colors[i32(v.colorIndex)];
|
|
38
|
+
return f;
|
|
40
39
|
}
|
|
41
|
-
`
|
|
42
|
-
});
|
|
43
40
|
|
|
44
|
-
let fragmentShader = Q5.device.createShaderModule({
|
|
45
|
-
label: 'drawingFragmentShader',
|
|
46
|
-
code: `
|
|
47
41
|
@fragment
|
|
48
42
|
fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
|
|
49
43
|
return color;
|
|
@@ -69,19 +63,17 @@ fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
|
|
|
69
63
|
label: 'drawingPipeline',
|
|
70
64
|
layout: pipelineLayout,
|
|
71
65
|
vertex: {
|
|
72
|
-
module:
|
|
66
|
+
module: drawingShader,
|
|
73
67
|
entryPoint: 'vertexMain',
|
|
74
68
|
buffers: [vertexBufferLayout]
|
|
75
69
|
},
|
|
76
70
|
fragment: {
|
|
77
|
-
module:
|
|
71
|
+
module: drawingShader,
|
|
78
72
|
entryPoint: 'fragmentMain',
|
|
79
73
|
targets: [{ format: 'bgra8unorm', blend: $.blendConfigs.normal }]
|
|
80
74
|
},
|
|
81
75
|
primitive: { topology: 'triangle-strip', stripIndexFormat: 'uint32' },
|
|
82
|
-
multisample: {
|
|
83
|
-
count: 4
|
|
84
|
-
}
|
|
76
|
+
multisample: { count: 4 }
|
|
85
77
|
};
|
|
86
78
|
|
|
87
79
|
$._pipelines[0] = Q5.device.createRenderPipeline($._pipelineConfigs[0]);
|
package/src/q5-webgpu-image.js
CHANGED
|
@@ -1,61 +1,64 @@
|
|
|
1
1
|
Q5.renderers.webgpu.image = ($, q) => {
|
|
2
|
-
$._textureBindGroups = [];
|
|
3
2
|
let vertexStack = new Float32Array(1e7),
|
|
4
3
|
vertIndex = 0;
|
|
5
4
|
|
|
6
5
|
let imageShader = Q5.device.createShaderModule({
|
|
7
6
|
label: 'imageShader',
|
|
8
7
|
code: `
|
|
9
|
-
struct
|
|
8
|
+
struct Uniforms {
|
|
9
|
+
halfWidth: f32,
|
|
10
|
+
halfHeight: f32
|
|
11
|
+
}
|
|
12
|
+
struct VertexParams {
|
|
10
13
|
@location(0) pos: vec2f,
|
|
11
14
|
@location(1) texCoord: vec2f,
|
|
12
15
|
@location(2) tintIndex: f32,
|
|
13
|
-
@location(3) matrixIndex: f32
|
|
16
|
+
@location(3) matrixIndex: f32,
|
|
17
|
+
@location(4) globalAlpha: f32
|
|
14
18
|
}
|
|
15
|
-
struct
|
|
19
|
+
struct FragmentParams {
|
|
16
20
|
@builtin(position) position: vec4f,
|
|
17
21
|
@location(0) texCoord: vec2f,
|
|
18
|
-
@location(1) tintIndex: f32
|
|
19
|
-
|
|
20
|
-
struct Uniforms {
|
|
21
|
-
halfWidth: f32,
|
|
22
|
-
halfHeight: f32
|
|
22
|
+
@location(1) tintIndex: f32,
|
|
23
|
+
@location(2) globalAlpha: f32
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
@group(0) @binding(0) var<uniform> uniforms: Uniforms;
|
|
26
27
|
@group(0) @binding(1) var<storage> transforms: array<mat4x4<f32>>;
|
|
28
|
+
@group(0) @binding(2) var<storage> colors : array<vec4f>;
|
|
27
29
|
|
|
28
|
-
@group(1) @binding(0) var
|
|
29
|
-
|
|
30
|
-
@group(2) @binding(0) var samp: sampler;
|
|
31
|
-
@group(2) @binding(1) var texture: texture_2d<f32>;
|
|
30
|
+
@group(1) @binding(0) var samp: sampler;
|
|
31
|
+
@group(1) @binding(1) var texture: texture_2d<f32>;
|
|
32
32
|
|
|
33
33
|
@vertex
|
|
34
|
-
fn vertexMain(
|
|
35
|
-
var vert = vec4f(
|
|
36
|
-
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;
|
|
37
37
|
vert.x /= uniforms.halfWidth;
|
|
38
38
|
vert.y /= uniforms.halfHeight;
|
|
39
39
|
|
|
40
|
-
var
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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;
|
|
45
46
|
}
|
|
46
47
|
|
|
47
48
|
@fragment
|
|
48
|
-
fn fragmentMain(
|
|
49
|
-
let texColor = textureSample(texture, samp, texCoord);
|
|
50
|
-
let tintColor = colors[i32(tintIndex)];
|
|
49
|
+
fn fragmentMain(f: FragmentParams) -> @location(0) vec4f {
|
|
50
|
+
let texColor = textureSample(texture, samp, f.texCoord);
|
|
51
|
+
let tintColor = colors[i32(f.tintIndex)];
|
|
51
52
|
|
|
52
53
|
// Mix original and tinted colors using tint alpha as blend factor
|
|
53
|
-
let tinted = vec4f(texColor.rgb * tintColor.rgb, texColor.a);
|
|
54
|
+
let tinted = vec4f(texColor.rgb * tintColor.rgb, texColor.a * f.globalAlpha);
|
|
54
55
|
return mix(texColor, tinted, tintColor.a);
|
|
55
56
|
}
|
|
56
57
|
`
|
|
57
58
|
});
|
|
58
59
|
|
|
60
|
+
$._textureBindGroups = [];
|
|
61
|
+
|
|
59
62
|
let textureLayout = Q5.device.createBindGroupLayout({
|
|
60
63
|
label: 'textureLayout',
|
|
61
64
|
entries: [
|
|
@@ -73,12 +76,13 @@ fn fragmentMain(@location(0) texCoord: vec2f, @location(1) tintIndex: f32) -> @l
|
|
|
73
76
|
});
|
|
74
77
|
|
|
75
78
|
const vertexBufferLayout = {
|
|
76
|
-
arrayStride:
|
|
79
|
+
arrayStride: 28,
|
|
77
80
|
attributes: [
|
|
78
81
|
{ shaderLocation: 0, offset: 0, format: 'float32x2' },
|
|
79
82
|
{ shaderLocation: 1, offset: 8, format: 'float32x2' },
|
|
80
83
|
{ shaderLocation: 2, offset: 16, format: 'float32' }, // tintIndex
|
|
81
|
-
{ shaderLocation: 3, offset: 20, format: 'float32' } // matrixIndex
|
|
84
|
+
{ shaderLocation: 3, offset: 20, format: 'float32' }, // matrixIndex
|
|
85
|
+
{ shaderLocation: 4, offset: 24, format: 'float32' } // globalAlpha
|
|
82
86
|
]
|
|
83
87
|
};
|
|
84
88
|
|
|
@@ -184,7 +188,7 @@ fn fragmentMain(@location(0) texCoord: vec2f, @location(1) tintIndex: f32) -> @l
|
|
|
184
188
|
|
|
185
189
|
$.imageMode = (x) => ($._imageMode = x);
|
|
186
190
|
|
|
187
|
-
const addVert = (x, y, u, v, ci, ti) => {
|
|
191
|
+
const addVert = (x, y, u, v, ci, ti, ga) => {
|
|
188
192
|
let s = vertexStack,
|
|
189
193
|
i = vertIndex;
|
|
190
194
|
s[i++] = x;
|
|
@@ -193,6 +197,7 @@ fn fragmentMain(@location(0) texCoord: vec2f, @location(1) tintIndex: f32) -> @l
|
|
|
193
197
|
s[i++] = v;
|
|
194
198
|
s[i++] = ci;
|
|
195
199
|
s[i++] = ti;
|
|
200
|
+
s[i++] = ga;
|
|
196
201
|
vertIndex = i;
|
|
197
202
|
};
|
|
198
203
|
|
|
@@ -203,16 +208,15 @@ fn fragmentMain(@location(0) texCoord: vec2f, @location(1) tintIndex: f32) -> @l
|
|
|
203
208
|
|
|
204
209
|
if ($._matrixDirty) $._saveMatrix();
|
|
205
210
|
|
|
206
|
-
let
|
|
207
|
-
|
|
208
|
-
|
|
211
|
+
let w = img.width,
|
|
212
|
+
h = img.height,
|
|
213
|
+
pd = g._pixelDensity || 1;
|
|
209
214
|
|
|
210
215
|
dw ??= g.defaultWidth;
|
|
211
216
|
dh ??= g.defaultHeight;
|
|
212
217
|
sw ??= w;
|
|
213
218
|
sh ??= h;
|
|
214
219
|
|
|
215
|
-
let pd = g._pixelDensity || 1;
|
|
216
220
|
dw *= pd;
|
|
217
221
|
dh *= pd;
|
|
218
222
|
|
|
@@ -221,14 +225,15 @@ fn fragmentMain(@location(0) texCoord: vec2f, @location(1) tintIndex: f32) -> @l
|
|
|
221
225
|
let u0 = sx / w,
|
|
222
226
|
v0 = sy / h,
|
|
223
227
|
u1 = (sx + sw) / w,
|
|
224
|
-
v1 = (sy + sh) / h
|
|
225
|
-
|
|
226
|
-
|
|
228
|
+
v1 = (sy + sh) / h,
|
|
229
|
+
ti = $._matrixIndex,
|
|
230
|
+
ci = $._tint,
|
|
231
|
+
ga = $._globalAlpha;
|
|
227
232
|
|
|
228
|
-
addVert(l, t, u0, v0, ci, ti);
|
|
229
|
-
addVert(r, t, u1, v0, ci, ti);
|
|
230
|
-
addVert(l, b, u0, v1, ci, ti);
|
|
231
|
-
addVert(r, b, u1, v1, ci, ti);
|
|
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);
|
|
232
237
|
|
|
233
238
|
$.drawStack.push(1, img.textureIndex);
|
|
234
239
|
};
|
|
@@ -240,7 +245,7 @@ fn fragmentMain(@location(0) texCoord: vec2f, @location(1) tintIndex: f32) -> @l
|
|
|
240
245
|
$.pass.setPipeline($._pipelines[1]);
|
|
241
246
|
|
|
242
247
|
let vertexBuffer = Q5.device.createBuffer({
|
|
243
|
-
size: vertIndex *
|
|
248
|
+
size: vertIndex * 5,
|
|
244
249
|
usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
|
|
245
250
|
mappedAtCreation: true
|
|
246
251
|
});
|
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
|
|
@@ -27,43 +28,40 @@ struct Text {
|
|
|
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
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
|
});
|