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.
@@ -1,51 +1,64 @@
1
1
  Q5.renderers.webgpu.image = ($, q) => {
2
- $._textureBindGroups = [];
3
- let vertexStack = [];
2
+ let vertexStack = new Float32Array(1e7),
3
+ vertIndex = 0;
4
4
 
5
- let vertexShader = Q5.device.createShaderModule({
6
- label: 'imageVertexShader',
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(@location(0) pos: vec2f, @location(1) texCoord: vec2f, @location(2) transformIndex: f32) -> VertexOutput {
22
- var vert = vec4f(pos, 0.0, 1.0);
23
- vert = transforms[i32(transformIndex)] * vert;
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 output: VertexOutput;
28
- output.position = vert;
29
- output.texCoord = texCoord;
30
- return output;
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(@location(0) texCoord: vec2f) -> @location(0) vec4f {
43
- // Sample the texture using the interpolated texture coordinate
44
- return textureSample(texture, samp, texCoord);
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: 20,
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' } // transformIndex
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: vertexShader,
98
+ module: imageShader,
84
99
  entryPoint: 'vertexMain',
85
100
  buffers: [{ arrayStride: 0, attributes: [] }, vertexBufferLayout]
86
101
  },
87
102
  fragment: {
88
- module: fragmentShader,
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
- const img = new Image();
165
- img.crossOrigin = 'Anonymous';
166
- img.onload = () => {
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
- img.src = src;
178
- return img;
185
+ });
186
+ return g;
179
187
  };
180
188
 
181
189
  $.imageMode = (x) => ($._imageMode = x);
182
190
 
183
- $.image = (img, dx, dy, dw, dh, sx = 0, sy = 0, sw, sh) => {
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
- let h = img.height;
211
+ let w = img.width,
212
+ h = img.height,
213
+ pd = g._pixelDensity || 1;
192
214
 
193
- dw ??= img.defaultWidth;
194
- dh ??= img.defaultHeight;
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
- let v0 = sy / h;
206
- let u1 = (sx + sw) / w;
207
- let v1 = (sy + sh) / h;
208
-
209
- // prettier-ignore
210
- vertexStack.push(
211
- l, t, u0, v0, ti,
212
- r, t, u1, v0, ti,
213
- l, b, u0, v1, ti,
214
- r, b, u1, v1, ti
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
- const vertexBuffer = Q5.device.createBuffer({
227
- size: vertexStack.length * 4,
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
- vertexStack.length = 0;
260
+ vertIndex = 0;
240
261
  });
241
262
  };
242
263
 
@@ -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
- // Positions for simple quad geometry
6
- const pos = array(vec2f(0, -1), vec2f(1, -1), vec2f(0, 0), vec2f(1, 0));
7
-
8
- struct VertexInput {
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 VertexOutput {
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
- transformIndex: f32,
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<storage> colors : array<vec4f>;
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 fontTexture: texture_2d<f32>;
41
- @group(2) @binding(1) var fontSampler: sampler;
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
- @group(3) @binding(0) var<storage> textChars: array<vec4f>;
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(input : VertexInput) -> VertexOutput {
49
- let char = textChars[input.instance];
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 = ((pos[input.vertex] * fontChar.size + char.xy + fontChar.offset) * text.scale) + text.pos;
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.transformIndex)] * vert;
56
+ vert = transforms[i32(text.matrixIndex)] * vert;
59
57
  vert.x /= uniforms.halfWidth;
60
58
  vert.y /= uniforms.halfHeight;
61
59
 
62
- var output : VertexOutput;
63
- output.position = vert;
64
- output.texCoord = (pos[input.vertex] * vec2f(1, -1)) * fontChar.texExtent + fontChar.texOffset;
65
- output.fillColor = colors[i32(text.fillIndex)];
66
- return output;
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(input : VertexOutput) -> @location(0) vec4f {
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(input.texCoord.x), dpdyFine(input.texCoord.x)));
81
- let dy = sz.y*length(vec2f(dpdxFine(input.texCoord.y), dpdyFine(input.texCoord.y)));
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(input.texCoord) - 0.5;
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(input.fillColor.rgb, input.fillColor.a * alpha);
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
- // q2d graphics context to use for text image creation
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 g.loadFont(url, cb);
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] = $._transformIndex;
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
- g.textSize($._textSize);
483
+ $._g.textSize($._textSize);
488
484
 
489
485
  if ($._doFill) {
490
486
  let fi = $._fill * 4;
491
- g.fill(colorStack.slice(fi, fi + 4));
487
+ $._g.fill(colorStack.slice(fi, fi + 4));
492
488
  }
493
489
  if ($._doStroke) {
494
490
  let si = $._stroke * 4;
495
- g.stroke(colorStack.slice(si, si + 4));
491
+ $._g.stroke(colorStack.slice(si, si + 4));
496
492
  }
497
493
 
498
- let img = g.createTextImage(str, w, h);
494
+ let img = $._g.createTextImage(str, w, h);
499
495
 
500
496
  if (img.canvas.textureIndex == undefined) {
501
497
  $._createTexture(img);