q5 2.13.1 → 2.13.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/q5.js CHANGED
@@ -105,6 +105,7 @@ function Q5(scope, parent, renderer) {
105
105
  for (let m of Q5.methods.post) m.call($);
106
106
  if ($._render) $._render();
107
107
  if ($._finishRender) $._finishRender();
108
+ $.postProcess();
108
109
  q.pmouseX = $.mouseX;
109
110
  q.pmouseY = $.mouseY;
110
111
  q.moveX = q.moveY = 0;
@@ -218,10 +219,12 @@ function Q5(scope, parent, renderer) {
218
219
  $.preload = t.preload;
219
220
  $.setup = t.setup;
220
221
  $.draw = t.draw;
222
+ $.postProcess = t.postProcess;
221
223
  }
222
224
  $.preload ??= () => {};
223
225
  $.setup ??= () => {};
224
226
  $.draw ??= () => {};
227
+ $.postProcess ??= () => {};
225
228
 
226
229
  let userFns = [
227
230
  'mouseMoved',
@@ -843,6 +846,7 @@ Q5.renderers.q2d.canvas = ($, q) => {
843
846
  if ($.ctx) {
844
847
  $.ctx.resetTransform();
845
848
  $.scale($._pixelDensity);
849
+ if ($._webgpuFallback) $.translate($.canvas.hw, $.canvas.hh);
846
850
  }
847
851
  };
848
852
 
@@ -3601,7 +3605,6 @@ Q5.renderers.webgpu.canvas = ($, q) => {
3601
3605
 
3602
3606
  let pass,
3603
3607
  mainView,
3604
- colorsLayout,
3605
3608
  colorIndex = 1,
3606
3609
  colorStackIndex = 8;
3607
3610
 
@@ -3613,7 +3616,6 @@ Q5.renderers.webgpu.canvas = ($, q) => {
3613
3616
  let drawStack = ($.drawStack = []);
3614
3617
 
3615
3618
  // colors used for each draw call
3616
-
3617
3619
  let colorStack = ($.colorStack = new Float32Array(1e6));
3618
3620
 
3619
3621
  // prettier-ignore
@@ -3622,43 +3624,28 @@ Q5.renderers.webgpu.canvas = ($, q) => {
3622
3624
  1, 1, 1, 1 // white
3623
3625
  ]);
3624
3626
 
3625
- $._transformLayout = Q5.device.createBindGroupLayout({
3626
- label: 'transformLayout',
3627
+ let mainLayout = Q5.device.createBindGroupLayout({
3628
+ label: 'mainLayout',
3627
3629
  entries: [
3628
3630
  {
3629
3631
  binding: 0,
3630
3632
  visibility: GPUShaderStage.VERTEX,
3631
- buffer: {
3632
- type: 'uniform',
3633
- hasDynamicOffset: false
3634
- }
3633
+ buffer: { type: 'uniform' }
3635
3634
  },
3636
3635
  {
3637
3636
  binding: 1,
3638
3637
  visibility: GPUShaderStage.VERTEX,
3639
- buffer: {
3640
- type: 'read-only-storage',
3641
- hasDynamicOffset: false
3642
- }
3643
- }
3644
- ]
3645
- });
3646
-
3647
- colorsLayout = Q5.device.createBindGroupLayout({
3648
- label: 'colorsLayout',
3649
- entries: [
3638
+ buffer: { type: 'read-only-storage' }
3639
+ },
3650
3640
  {
3651
- binding: 0,
3641
+ binding: 2,
3652
3642
  visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT,
3653
- buffer: {
3654
- type: 'read-only-storage',
3655
- hasDynamicOffset: false
3656
- }
3643
+ buffer: { type: 'read-only-storage' }
3657
3644
  }
3658
3645
  ]
3659
3646
  });
3660
3647
 
3661
- $.bindGroupLayouts = [$._transformLayout, colorsLayout];
3648
+ $.bindGroupLayouts = [mainLayout];
3662
3649
 
3663
3650
  let uniformBuffer = Q5.device.createBuffer({
3664
3651
  size: 8, // Size of two floats
@@ -3688,7 +3675,6 @@ Q5.renderers.webgpu.canvas = ($, q) => {
3688
3675
  Q5.device.queue.writeBuffer(uniformBuffer, 0, new Float32Array([$.canvas.hw, $.canvas.hh]));
3689
3676
 
3690
3677
  createMainView();
3691
-
3692
3678
  return c;
3693
3679
  };
3694
3680
 
@@ -3732,7 +3718,7 @@ Q5.renderers.webgpu.canvas = ($, q) => {
3732
3718
  };
3733
3719
 
3734
3720
  $._stroke = 0;
3735
- $._fill = $._tint = 1;
3721
+ $._fill = $._tint = $._globalAlpha = 1;
3736
3722
  $._doFill = $._doStroke = true;
3737
3723
 
3738
3724
  $.fill = (r, g, b, a) => {
@@ -3749,6 +3735,7 @@ Q5.renderers.webgpu.canvas = ($, q) => {
3749
3735
  addColor(r, g, b, a);
3750
3736
  $._tint = colorIndex;
3751
3737
  };
3738
+ $.opacity = (a) => ($._globalAlpha = a);
3752
3739
 
3753
3740
  $.noFill = () => ($._doFill = false);
3754
3741
  $.noStroke = () => ($._doStroke = false);
@@ -3762,6 +3749,7 @@ Q5.renderers.webgpu.canvas = ($, q) => {
3762
3749
  transforms = new Float32Array(MAX_TRANSFORMS * MATRIX_SIZE),
3763
3750
  matrices = [],
3764
3751
  matricesIndexStack = [];
3752
+
3765
3753
  let matrix;
3766
3754
 
3767
3755
  // tracks if the matrix has been modified
@@ -3797,12 +3785,10 @@ Q5.renderers.webgpu.canvas = ($, q) => {
3797
3785
  if (!a) return;
3798
3786
  if ($._angleMode) a *= $._DEGTORAD;
3799
3787
 
3800
- let cosR = Math.cos(a);
3801
- let sinR = Math.sin(a);
3802
-
3803
- let m = matrix;
3804
-
3805
- let m0 = m[0],
3788
+ let cosR = Math.cos(a),
3789
+ sinR = Math.sin(a),
3790
+ m = matrix,
3791
+ m0 = m[0],
3806
3792
  m1 = m[1],
3807
3793
  m4 = m[4],
3808
3794
  m5 = m[5];
@@ -3849,15 +3835,15 @@ Q5.renderers.webgpu.canvas = ($, q) => {
3849
3835
  if (!ang) return;
3850
3836
  if ($._angleMode) ang *= $._DEGTORAD;
3851
3837
 
3852
- let tanAng = Math.tan(ang);
3853
-
3854
- let m0 = matrix[0],
3855
- m1 = matrix[1],
3856
- m4 = matrix[4],
3857
- m5 = matrix[5];
3838
+ let tanAng = Math.tan(ang),
3839
+ m = matrix,
3840
+ m0 = m[0],
3841
+ m1 = m[1],
3842
+ m4 = m[4],
3843
+ m5 = m[5];
3858
3844
 
3859
- matrix[0] = m0 + m4 * tanAng;
3860
- matrix[1] = m1 + m5 * tanAng;
3845
+ m[0] = m0 + m4 * tanAng;
3846
+ m[1] = m1 + m5 * tanAng;
3861
3847
 
3862
3848
  $._matrixDirty = true;
3863
3849
  };
@@ -3866,15 +3852,15 @@ Q5.renderers.webgpu.canvas = ($, q) => {
3866
3852
  if (!ang) return;
3867
3853
  if ($._angleMode) ang *= $._DEGTORAD;
3868
3854
 
3869
- let tanAng = Math.tan(ang);
3870
-
3871
- let m0 = matrix[0],
3872
- m1 = matrix[1],
3873
- m4 = matrix[4],
3874
- m5 = matrix[5];
3855
+ let tanAng = Math.tan(ang),
3856
+ m = matrix,
3857
+ m0 = m[0],
3858
+ m1 = m[1],
3859
+ m4 = m[4],
3860
+ m5 = m[5];
3875
3861
 
3876
- matrix[4] = m4 + m0 * tanAng;
3877
- matrix[5] = m5 + m1 * tanAng;
3862
+ m[4] = m4 + m0 * tanAng;
3863
+ m[5] = m5 + m1 * tanAng;
3878
3864
 
3879
3865
  $._matrixDirty = true;
3880
3866
  };
@@ -4042,26 +4028,14 @@ Q5.renderers.webgpu.canvas = ($, q) => {
4042
4028
  };
4043
4029
 
4044
4030
  $._render = () => {
4045
- if (matrices.length > 1 || !$._transformBindGroup) {
4046
- let transformBuffer = Q5.device.createBuffer({
4047
- size: matrices.length * MATRIX_SIZE * 4, // 4 bytes per float
4048
- usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
4049
- mappedAtCreation: true
4050
- });
4051
-
4052
- new Float32Array(transformBuffer.getMappedRange()).set(transforms.slice(0, matrices.length * MATRIX_SIZE));
4053
- transformBuffer.unmap();
4054
-
4055
- $._transformBindGroup = Q5.device.createBindGroup({
4056
- layout: $._transformLayout,
4057
- entries: [
4058
- { binding: 0, resource: { buffer: uniformBuffer } },
4059
- { binding: 1, resource: { buffer: transformBuffer } }
4060
- ]
4061
- });
4062
- }
4031
+ let transformBuffer = Q5.device.createBuffer({
4032
+ size: matrices.length * MATRIX_SIZE * 4, // 4 bytes per float
4033
+ usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
4034
+ mappedAtCreation: true
4035
+ });
4063
4036
 
4064
- pass.setBindGroup(0, $._transformBindGroup);
4037
+ new Float32Array(transformBuffer.getMappedRange()).set(transforms.slice(0, matrices.length * MATRIX_SIZE));
4038
+ transformBuffer.unmap();
4065
4039
 
4066
4040
  let colorsBuffer = Q5.device.createBuffer({
4067
4041
  size: colorStackIndex * 4,
@@ -4072,20 +4046,23 @@ Q5.renderers.webgpu.canvas = ($, q) => {
4072
4046
  new Float32Array(colorsBuffer.getMappedRange()).set(colorStack.slice(0, colorStackIndex));
4073
4047
  colorsBuffer.unmap();
4074
4048
 
4075
- $._colorsBindGroup = Q5.device.createBindGroup({
4076
- layout: colorsLayout,
4077
- entries: [{ binding: 0, resource: { buffer: colorsBuffer } }]
4049
+ mainBindGroup = Q5.device.createBindGroup({
4050
+ layout: mainLayout,
4051
+ entries: [
4052
+ { binding: 0, resource: { buffer: uniformBuffer } },
4053
+ { binding: 1, resource: { buffer: transformBuffer } },
4054
+ { binding: 2, resource: { buffer: colorsBuffer } }
4055
+ ]
4078
4056
  });
4079
4057
 
4080
- pass.setBindGroup(1, $._colorsBindGroup);
4058
+ pass.setBindGroup(0, mainBindGroup);
4081
4059
 
4082
4060
  for (let m of $._hooks.preRender) m();
4083
4061
 
4084
4062
  let drawVertOffset = 0,
4085
4063
  imageVertOffset = 0,
4086
4064
  textCharOffset = 0,
4087
- curPipelineIndex = -1,
4088
- curTextureIndex = -1;
4065
+ curPipelineIndex = -1;
4089
4066
 
4090
4067
  for (let i = 0; i < drawStack.length; i += 2) {
4091
4068
  let v = drawStack[i + 1];
@@ -4101,20 +4078,16 @@ Q5.renderers.webgpu.canvas = ($, q) => {
4101
4078
  pass.draw(v, 1, drawVertOffset);
4102
4079
  drawVertOffset += v;
4103
4080
  } else if (curPipelineIndex == 1) {
4104
- // let vertCount = drawStack[i + 2];
4105
4081
  // draw images
4106
- if (curTextureIndex != v) {
4107
- // v is the texture index
4108
- pass.setBindGroup(2, $._textureBindGroups[v]);
4109
- }
4082
+ // v is the texture index
4083
+ pass.setBindGroup(1, $._textureBindGroups[v]);
4110
4084
  pass.draw(4, 1, imageVertOffset);
4111
4085
  imageVertOffset += 4;
4112
- // i++;
4113
4086
  } else if (curPipelineIndex == 2) {
4114
4087
  // draw text
4115
4088
  let o = drawStack[i + 2];
4116
- pass.setBindGroup(2, $._fonts[o].bindGroup);
4117
- pass.setBindGroup(3, $._textBindGroup);
4089
+ pass.setBindGroup(1, $._fonts[o].bindGroup);
4090
+ pass.setBindGroup(2, $._textBindGroup);
4118
4091
 
4119
4092
  // v is the number of characters in the text
4120
4093
  pass.draw(4, v, 0, textCharOffset);
@@ -4173,46 +4146,40 @@ Q5.renderers.webgpu.drawing = ($, q) => {
4173
4146
  vertexStack = new Float32Array(1e7),
4174
4147
  vertIndex = 0;
4175
4148
 
4176
- let vertexShader = Q5.device.createShaderModule({
4177
- label: 'drawingVertexShader',
4149
+ let drawingShader = Q5.device.createShaderModule({
4150
+ label: 'drawingShader',
4178
4151
  code: `
4179
- struct VertexInput {
4152
+ struct Uniforms {
4153
+ halfWidth: f32,
4154
+ halfHeight: f32
4155
+ }
4156
+ struct VertexParams {
4180
4157
  @location(0) pos: vec2f,
4181
4158
  @location(1) colorIndex: f32,
4182
4159
  @location(2) matrixIndex: f32
4183
4160
  }
4184
- struct VertexOutput {
4161
+ struct FragmentParams {
4185
4162
  @builtin(position) position: vec4f,
4186
4163
  @location(0) color: vec4f
4187
4164
  }
4188
- struct Uniforms {
4189
- halfWidth: f32,
4190
- halfHeight: f32
4191
- }
4192
4165
 
4193
4166
  @group(0) @binding(0) var<uniform> uniforms: Uniforms;
4194
4167
  @group(0) @binding(1) var<storage> transforms: array<mat4x4<f32>>;
4195
-
4196
- @group(1) @binding(0) var<storage> colors : array<vec4f>;
4168
+ @group(0) @binding(2) var<storage> colors : array<vec4f>;
4197
4169
 
4198
4170
  @vertex
4199
- fn vertexMain(input: VertexInput) -> VertexOutput {
4200
- var vert = vec4f(input.pos, 0.0, 1.0);
4201
- vert = transforms[i32(input.matrixIndex)] * vert;
4171
+ fn vertexMain(v: VertexParams) -> FragmentParams {
4172
+ var vert = vec4f(v.pos, 0.0, 1.0);
4173
+ vert = transforms[i32(v.matrixIndex)] * vert;
4202
4174
  vert.x /= uniforms.halfWidth;
4203
4175
  vert.y /= uniforms.halfHeight;
4204
4176
 
4205
- var output: VertexOutput;
4206
- output.position = vert;
4207
- output.color = colors[i32(input.colorIndex)];
4208
- return output;
4177
+ var f: FragmentParams;
4178
+ f.position = vert;
4179
+ f.color = colors[i32(v.colorIndex)];
4180
+ return f;
4209
4181
  }
4210
- `
4211
- });
4212
4182
 
4213
- let fragmentShader = Q5.device.createShaderModule({
4214
- label: 'drawingFragmentShader',
4215
- code: `
4216
4183
  @fragment
4217
4184
  fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
4218
4185
  return color;
@@ -4221,7 +4188,7 @@ fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
4221
4188
  });
4222
4189
 
4223
4190
  let vertexBufferLayout = {
4224
- arrayStride: 16, // 2 coordinates + 1 color index + 1 transform index * 4 bytes each
4191
+ arrayStride: 16, // 4 floats * 4 bytes
4225
4192
  attributes: [
4226
4193
  { format: 'float32x2', offset: 0, shaderLocation: 0 }, // position
4227
4194
  { format: 'float32', offset: 8, shaderLocation: 1 }, // colorIndex
@@ -4238,19 +4205,17 @@ fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
4238
4205
  label: 'drawingPipeline',
4239
4206
  layout: pipelineLayout,
4240
4207
  vertex: {
4241
- module: vertexShader,
4208
+ module: drawingShader,
4242
4209
  entryPoint: 'vertexMain',
4243
4210
  buffers: [vertexBufferLayout]
4244
4211
  },
4245
4212
  fragment: {
4246
- module: fragmentShader,
4213
+ module: drawingShader,
4247
4214
  entryPoint: 'fragmentMain',
4248
4215
  targets: [{ format: 'bgra8unorm', blend: $.blendConfigs.normal }]
4249
4216
  },
4250
4217
  primitive: { topology: 'triangle-strip', stripIndexFormat: 'uint32' },
4251
- multisample: {
4252
- count: 4
4253
- }
4218
+ multisample: { count: 4 }
4254
4219
  };
4255
4220
 
4256
4221
  $._pipelines[0] = Q5.device.createRenderPipeline($._pipelineConfigs[0]);
@@ -4686,63 +4651,66 @@ fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
4686
4651
  });
4687
4652
  };
4688
4653
  Q5.renderers.webgpu.image = ($, q) => {
4689
- $._textureBindGroups = [];
4690
4654
  let vertexStack = new Float32Array(1e7),
4691
4655
  vertIndex = 0;
4692
4656
 
4693
4657
  let imageShader = Q5.device.createShaderModule({
4694
4658
  label: 'imageShader',
4695
4659
  code: `
4696
- struct VertexInput {
4660
+ struct Uniforms {
4661
+ halfWidth: f32,
4662
+ halfHeight: f32
4663
+ }
4664
+ struct VertexParams {
4697
4665
  @location(0) pos: vec2f,
4698
4666
  @location(1) texCoord: vec2f,
4699
4667
  @location(2) tintIndex: f32,
4700
- @location(3) matrixIndex: f32
4668
+ @location(3) matrixIndex: f32,
4669
+ @location(4) globalAlpha: f32
4701
4670
  }
4702
- struct VertexOutput {
4671
+ struct FragmentParams {
4703
4672
  @builtin(position) position: vec4f,
4704
4673
  @location(0) texCoord: vec2f,
4705
- @location(1) tintIndex: f32
4706
- }
4707
- struct Uniforms {
4708
- halfWidth: f32,
4709
- halfHeight: f32
4674
+ @location(1) tintIndex: f32,
4675
+ @location(2) globalAlpha: f32
4710
4676
  }
4711
4677
 
4712
4678
  @group(0) @binding(0) var<uniform> uniforms: Uniforms;
4713
4679
  @group(0) @binding(1) var<storage> transforms: array<mat4x4<f32>>;
4680
+ @group(0) @binding(2) var<storage> colors : array<vec4f>;
4714
4681
 
4715
- @group(1) @binding(0) var<storage> colors : array<vec4f>;
4716
-
4717
- @group(2) @binding(0) var samp: sampler;
4718
- @group(2) @binding(1) var texture: texture_2d<f32>;
4682
+ @group(1) @binding(0) var samp: sampler;
4683
+ @group(1) @binding(1) var texture: texture_2d<f32>;
4719
4684
 
4720
4685
  @vertex
4721
- fn vertexMain(input: VertexInput) -> VertexOutput {
4722
- var vert = vec4f(input.pos, 0.0, 1.0);
4723
- vert = transforms[i32(input.matrixIndex)] * vert;
4686
+ fn vertexMain(v: VertexParams) -> FragmentParams {
4687
+ var vert = vec4f(v.pos, 0.0, 1.0);
4688
+ vert = transforms[i32(v.matrixIndex)] * vert;
4724
4689
  vert.x /= uniforms.halfWidth;
4725
4690
  vert.y /= uniforms.halfHeight;
4726
4691
 
4727
- var output: VertexOutput;
4728
- output.position = vert;
4729
- output.texCoord = input.texCoord;
4730
- output.tintIndex = input.tintIndex;
4731
- return output;
4692
+ var f: FragmentParams;
4693
+ f.position = vert;
4694
+ f.texCoord = v.texCoord;
4695
+ f.tintIndex = v.tintIndex;
4696
+ f.globalAlpha = v.globalAlpha;
4697
+ return f;
4732
4698
  }
4733
4699
 
4734
4700
  @fragment
4735
- fn fragmentMain(@location(0) texCoord: vec2f, @location(1) tintIndex: f32) -> @location(0) vec4f {
4736
- let texColor = textureSample(texture, samp, texCoord);
4737
- let tintColor = colors[i32(tintIndex)];
4701
+ fn fragmentMain(f: FragmentParams) -> @location(0) vec4f {
4702
+ let texColor = textureSample(texture, samp, f.texCoord);
4703
+ let tintColor = colors[i32(f.tintIndex)];
4738
4704
 
4739
4705
  // Mix original and tinted colors using tint alpha as blend factor
4740
- let tinted = vec4f(texColor.rgb * tintColor.rgb, texColor.a);
4706
+ let tinted = vec4f(texColor.rgb * tintColor.rgb, texColor.a * f.globalAlpha);
4741
4707
  return mix(texColor, tinted, tintColor.a);
4742
4708
  }
4743
4709
  `
4744
4710
  });
4745
4711
 
4712
+ $._textureBindGroups = [];
4713
+
4746
4714
  let textureLayout = Q5.device.createBindGroupLayout({
4747
4715
  label: 'textureLayout',
4748
4716
  entries: [
@@ -4760,12 +4728,13 @@ fn fragmentMain(@location(0) texCoord: vec2f, @location(1) tintIndex: f32) -> @l
4760
4728
  });
4761
4729
 
4762
4730
  const vertexBufferLayout = {
4763
- arrayStride: 24,
4731
+ arrayStride: 28,
4764
4732
  attributes: [
4765
4733
  { shaderLocation: 0, offset: 0, format: 'float32x2' },
4766
4734
  { shaderLocation: 1, offset: 8, format: 'float32x2' },
4767
4735
  { shaderLocation: 2, offset: 16, format: 'float32' }, // tintIndex
4768
- { shaderLocation: 3, offset: 20, format: 'float32' } // matrixIndex
4736
+ { shaderLocation: 3, offset: 20, format: 'float32' }, // matrixIndex
4737
+ { shaderLocation: 4, offset: 24, format: 'float32' } // globalAlpha
4769
4738
  ]
4770
4739
  };
4771
4740
 
@@ -4801,14 +4770,11 @@ fn fragmentMain(@location(0) texCoord: vec2f, @location(1) tintIndex: f32) -> @l
4801
4770
  minFilter: filter
4802
4771
  });
4803
4772
  };
4804
- makeSampler('linear');
4805
4773
 
4806
- $.smooth = () => {
4807
- makeSampler('linear');
4808
- };
4809
- $.noSmooth = () => {
4810
- makeSampler('nearest');
4811
- };
4774
+ $.smooth = () => makeSampler('linear');
4775
+ $.noSmooth = () => makeSampler('nearest');
4776
+
4777
+ $.smooth();
4812
4778
 
4813
4779
  let MAX_TEXTURES = 12000;
4814
4780
 
@@ -4871,7 +4837,7 @@ fn fragmentMain(@location(0) texCoord: vec2f, @location(1) tintIndex: f32) -> @l
4871
4837
 
4872
4838
  $.imageMode = (x) => ($._imageMode = x);
4873
4839
 
4874
- const addVert = (x, y, u, v, ci, ti) => {
4840
+ const addVert = (x, y, u, v, ci, ti, ga) => {
4875
4841
  let s = vertexStack,
4876
4842
  i = vertIndex;
4877
4843
  s[i++] = x;
@@ -4880,6 +4846,7 @@ fn fragmentMain(@location(0) texCoord: vec2f, @location(1) tintIndex: f32) -> @l
4880
4846
  s[i++] = v;
4881
4847
  s[i++] = ci;
4882
4848
  s[i++] = ti;
4849
+ s[i++] = ga;
4883
4850
  vertIndex = i;
4884
4851
  };
4885
4852
 
@@ -4890,16 +4857,15 @@ fn fragmentMain(@location(0) texCoord: vec2f, @location(1) tintIndex: f32) -> @l
4890
4857
 
4891
4858
  if ($._matrixDirty) $._saveMatrix();
4892
4859
 
4893
- let ti = $._matrixIndex,
4894
- w = img.width,
4895
- h = img.height;
4860
+ let w = img.width,
4861
+ h = img.height,
4862
+ pd = g._pixelDensity || 1;
4896
4863
 
4897
4864
  dw ??= g.defaultWidth;
4898
4865
  dh ??= g.defaultHeight;
4899
4866
  sw ??= w;
4900
4867
  sh ??= h;
4901
4868
 
4902
- let pd = g._pixelDensity || 1;
4903
4869
  dw *= pd;
4904
4870
  dh *= pd;
4905
4871
 
@@ -4908,14 +4874,15 @@ fn fragmentMain(@location(0) texCoord: vec2f, @location(1) tintIndex: f32) -> @l
4908
4874
  let u0 = sx / w,
4909
4875
  v0 = sy / h,
4910
4876
  u1 = (sx + sw) / w,
4911
- v1 = (sy + sh) / h;
4877
+ v1 = (sy + sh) / h,
4878
+ ti = $._matrixIndex,
4879
+ ci = $._tint,
4880
+ ga = $._globalAlpha;
4912
4881
 
4913
- let ci = $._tint;
4914
-
4915
- addVert(l, t, u0, v0, ci, ti);
4916
- addVert(r, t, u1, v0, ci, ti);
4917
- addVert(l, b, u0, v1, ci, ti);
4918
- addVert(r, b, u1, v1, ci, ti);
4882
+ addVert(l, t, u0, v0, ci, ti, ga);
4883
+ addVert(r, t, u1, v0, ci, ti, ga);
4884
+ addVert(l, b, u0, v1, ci, ti, ga);
4885
+ addVert(r, b, u1, v1, ci, ti, ga);
4919
4886
 
4920
4887
  $.drawStack.push(1, img.textureIndex);
4921
4888
  };
@@ -4927,7 +4894,7 @@ fn fragmentMain(@location(0) texCoord: vec2f, @location(1) tintIndex: f32) -> @l
4927
4894
  $.pass.setPipeline($._pipelines[1]);
4928
4895
 
4929
4896
  let vertexBuffer = Q5.device.createBuffer({
4930
- size: vertIndex * 4,
4897
+ size: vertIndex * 5,
4931
4898
  usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
4932
4899
  mappedAtCreation: true
4933
4900
  });
@@ -4955,14 +4922,15 @@ Q5.renderers.webgpu.text = ($, q) => {
4955
4922
  let textShader = Q5.device.createShaderModule({
4956
4923
  label: 'MSDF text shader',
4957
4924
  code: `
4958
- // Positions for simple quad geometry
4959
- const pos = array(vec2f(0, -1), vec2f(1, -1), vec2f(0, 0), vec2f(1, 0));
4960
-
4961
- struct VertexInput {
4925
+ struct Uniforms {
4926
+ halfWidth: f32,
4927
+ halfHeight: f32
4928
+ }
4929
+ struct VertexParams {
4962
4930
  @builtin(vertex_index) vertex : u32,
4963
4931
  @builtin(instance_index) instance : u32
4964
4932
  }
4965
- struct VertexOutput {
4933
+ struct FragmentParams {
4966
4934
  @builtin(position) position : vec4f,
4967
4935
  @location(0) texCoord : vec2f,
4968
4936
  @location(1) fillColor : vec4f
@@ -4980,43 +4948,40 @@ struct Text {
4980
4948
  fillIndex: f32,
4981
4949
  strokeIndex: f32
4982
4950
  }
4983
- struct Uniforms {
4984
- halfWidth: f32,
4985
- halfHeight: f32
4986
- }
4987
4951
 
4988
4952
  @group(0) @binding(0) var<uniform> uniforms: Uniforms;
4989
4953
  @group(0) @binding(1) var<storage> transforms: array<mat4x4<f32>>;
4954
+ @group(0) @binding(2) var<storage> colors : array<vec4f>;
4990
4955
 
4991
- @group(1) @binding(0) var<storage> colors : array<vec4f>;
4956
+ @group(1) @binding(0) var fontTexture: texture_2d<f32>;
4957
+ @group(1) @binding(1) var fontSampler: sampler;
4958
+ @group(1) @binding(2) var<storage> fontChars: array<Char>;
4992
4959
 
4993
- @group(2) @binding(0) var fontTexture: texture_2d<f32>;
4994
- @group(2) @binding(1) var fontSampler: sampler;
4995
- @group(2) @binding(2) var<storage> fontChars: array<Char>;
4960
+ @group(2) @binding(0) var<storage> textChars: array<vec4f>;
4961
+ @group(2) @binding(1) var<storage> textMetadata: array<Text>;
4996
4962
 
4997
- @group(3) @binding(0) var<storage> textChars: array<vec4f>;
4998
- @group(3) @binding(1) var<storage> textMetadata: array<Text>;
4963
+ const quad = array(vec2f(0, -1), vec2f(1, -1), vec2f(0, 0), vec2f(1, 0));
4999
4964
 
5000
4965
  @vertex
5001
- fn vertexMain(input : VertexInput) -> VertexOutput {
5002
- let char = textChars[input.instance];
4966
+ fn vertexMain(v : VertexParams) -> FragmentParams {
4967
+ let char = textChars[v.instance];
5003
4968
 
5004
4969
  let text = textMetadata[i32(char.w)];
5005
4970
 
5006
4971
  let fontChar = fontChars[i32(char.z)];
5007
4972
 
5008
- let charPos = ((pos[input.vertex] * fontChar.size + char.xy + fontChar.offset) * text.scale) + text.pos;
4973
+ let charPos = ((quad[v.vertex] * fontChar.size + char.xy + fontChar.offset) * text.scale) + text.pos;
5009
4974
 
5010
4975
  var vert = vec4f(charPos, 0.0, 1.0);
5011
4976
  vert = transforms[i32(text.matrixIndex)] * vert;
5012
4977
  vert.x /= uniforms.halfWidth;
5013
4978
  vert.y /= uniforms.halfHeight;
5014
4979
 
5015
- var output : VertexOutput;
5016
- output.position = vert;
5017
- output.texCoord = (pos[input.vertex] * vec2f(1, -1)) * fontChar.texExtent + fontChar.texOffset;
5018
- output.fillColor = colors[i32(text.fillIndex)];
5019
- return output;
4980
+ var f : FragmentParams;
4981
+ f.position = vert;
4982
+ f.texCoord = (quad[v.vertex] * vec2f(1, -1)) * fontChar.texExtent + fontChar.texOffset;
4983
+ f.fillColor = colors[i32(text.fillIndex)];
4984
+ return f;
5020
4985
  }
5021
4986
 
5022
4987
  fn sampleMsdf(texCoord: vec2f) -> f32 {
@@ -5025,22 +4990,22 @@ fn sampleMsdf(texCoord: vec2f) -> f32 {
5025
4990
  }
5026
4991
 
5027
4992
  @fragment
5028
- fn fragmentMain(input : VertexOutput) -> @location(0) vec4f {
4993
+ fn fragmentMain(f : FragmentParams) -> @location(0) vec4f {
5029
4994
  // pxRange (AKA distanceRange) comes from the msdfgen tool,
5030
4995
  // uses the default which is 4.
5031
4996
  let pxRange = 4.0;
5032
4997
  let sz = vec2f(textureDimensions(fontTexture, 0));
5033
- let dx = sz.x*length(vec2f(dpdxFine(input.texCoord.x), dpdyFine(input.texCoord.x)));
5034
- let dy = sz.y*length(vec2f(dpdxFine(input.texCoord.y), dpdyFine(input.texCoord.y)));
4998
+ let dx = sz.x*length(vec2f(dpdxFine(f.texCoord.x), dpdyFine(f.texCoord.x)));
4999
+ let dy = sz.y*length(vec2f(dpdxFine(f.texCoord.y), dpdyFine(f.texCoord.y)));
5035
5000
  let toPixels = pxRange * inverseSqrt(dx * dx + dy * dy);
5036
- let sigDist = sampleMsdf(input.texCoord) - 0.5;
5001
+ let sigDist = sampleMsdf(f.texCoord) - 0.5;
5037
5002
  let pxDist = sigDist * toPixels;
5038
5003
  let edgeWidth = 0.5;
5039
5004
  let alpha = smoothstep(-edgeWidth, edgeWidth, pxDist);
5040
5005
  if (alpha < 0.001) {
5041
5006
  discard;
5042
5007
  }
5043
- return vec4f(input.fillColor.rgb, input.fillColor.a * alpha);
5008
+ return vec4f(f.fillColor.rgb, f.fillColor.a * alpha);
5044
5009
  }
5045
5010
  `
5046
5011
  });
@@ -5274,8 +5239,8 @@ fn fragmentMain(input : VertexOutput) -> @location(0) vec4f {
5274
5239
  if (vert) $._textBaseline = vert;
5275
5240
  };
5276
5241
 
5277
- $._charStack = [];
5278
- $._textStack = [];
5242
+ let charStack = [],
5243
+ textStack = [];
5279
5244
 
5280
5245
  let measureText = (font, text, charCallback) => {
5281
5246
  let maxWidth = 0,
@@ -5372,7 +5337,7 @@ fn fragmentMain(input : VertexOutput) -> @location(0) vec4f {
5372
5337
 
5373
5338
  let ta = $._textAlign,
5374
5339
  tb = $._textBaseline,
5375
- textIndex = $._textStack.length,
5340
+ textIndex = textStack.length,
5376
5341
  o = 0, // offset
5377
5342
  measurements;
5378
5343
 
@@ -5412,20 +5377,20 @@ fn fragmentMain(input : VertexOutput) -> @location(0) vec4f {
5412
5377
  o += 4;
5413
5378
  });
5414
5379
  }
5415
- $._charStack.push(charsData);
5380
+ charStack.push(charsData);
5416
5381
 
5417
- let text = [];
5382
+ let txt = [];
5418
5383
 
5419
5384
  if ($._matrixDirty) $._saveMatrix();
5420
5385
 
5421
- text[0] = x;
5422
- text[1] = -y;
5423
- text[2] = $._textSize / 44;
5424
- text[3] = $._matrixIndex;
5425
- text[4] = $._fillSet ? $._fill : 0;
5426
- text[5] = $._stroke;
5386
+ txt[0] = x;
5387
+ txt[1] = -y;
5388
+ txt[2] = $._textSize / 44;
5389
+ txt[3] = $._matrixIndex;
5390
+ txt[4] = $._fillSet ? $._fill : 0;
5391
+ txt[5] = $._stroke;
5427
5392
 
5428
- $._textStack.push(text);
5393
+ textStack.push(txt);
5429
5394
  $.drawStack.push(2, measurements.printedCharCount, $._font.index);
5430
5395
  };
5431
5396
 
@@ -5486,11 +5451,11 @@ fn fragmentMain(input : VertexOutput) -> @location(0) vec4f {
5486
5451
  };
5487
5452
 
5488
5453
  $._hooks.preRender.push(() => {
5489
- if (!$._charStack.length) return;
5454
+ if (!charStack.length) return;
5490
5455
 
5491
5456
  // calculate total buffer size for text data
5492
5457
  let totalTextSize = 0;
5493
- for (let charsData of $._charStack) {
5458
+ for (let charsData of charStack) {
5494
5459
  totalTextSize += charsData.length * 4;
5495
5460
  }
5496
5461
 
@@ -5502,11 +5467,11 @@ fn fragmentMain(input : VertexOutput) -> @location(0) vec4f {
5502
5467
  });
5503
5468
 
5504
5469
  // copy all the text data into the buffer
5505
- new Float32Array(charBuffer.getMappedRange()).set($._charStack.flat());
5470
+ new Float32Array(charBuffer.getMappedRange()).set(charStack.flat());
5506
5471
  charBuffer.unmap();
5507
5472
 
5508
5473
  // calculate total buffer size for metadata
5509
- let totalMetadataSize = $._textStack.length * 6 * 4;
5474
+ let totalMetadataSize = textStack.length * 6 * 4;
5510
5475
 
5511
5476
  // create a single buffer for all metadata
5512
5477
  let textBuffer = Q5.device.createBuffer({
@@ -5517,7 +5482,7 @@ fn fragmentMain(input : VertexOutput) -> @location(0) vec4f {
5517
5482
  });
5518
5483
 
5519
5484
  // copy all metadata into the buffer
5520
- new Float32Array(textBuffer.getMappedRange()).set($._textStack.flat());
5485
+ new Float32Array(textBuffer.getMappedRange()).set(textStack.flat());
5521
5486
  textBuffer.unmap();
5522
5487
 
5523
5488
  // create a single bind group for the text buffer and metadata buffer
@@ -5532,7 +5497,7 @@ fn fragmentMain(input : VertexOutput) -> @location(0) vec4f {
5532
5497
  });
5533
5498
 
5534
5499
  $._hooks.postRender.push(() => {
5535
- $._charStack.length = 0;
5536
- $._textStack.length = 0;
5500
+ charStack.length = 0;
5501
+ textStack.length = 0;
5537
5502
  });
5538
5503
  };