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/package.json +1 -1
- package/q5.js +169 -204
- package/q5.min.js +1 -1
- package/src/q5-2d-canvas.js +1 -0
- package/src/q5-core.js +3 -0
- package/src/q5-webgpu-canvas.js +51 -82
- package/src/q5-webgpu-drawing.js +20 -28
- package/src/q5-webgpu-image.js +49 -47
- package/src/q5-webgpu-text.js +45 -47
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
|
-
|
|
3626
|
-
label: '
|
|
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
|
-
|
|
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:
|
|
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 = [
|
|
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
|
-
|
|
3802
|
-
|
|
3803
|
-
|
|
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
|
-
|
|
3855
|
-
m1 =
|
|
3856
|
-
m4 =
|
|
3857
|
-
m5 =
|
|
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
|
-
|
|
3860
|
-
|
|
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
|
-
|
|
3872
|
-
m1 =
|
|
3873
|
-
m4 =
|
|
3874
|
-
m5 =
|
|
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
|
-
|
|
3877
|
-
|
|
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
|
-
|
|
4046
|
-
|
|
4047
|
-
|
|
4048
|
-
|
|
4049
|
-
|
|
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
|
-
|
|
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
|
-
|
|
4076
|
-
layout:
|
|
4077
|
-
entries: [
|
|
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(
|
|
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
|
-
|
|
4107
|
-
|
|
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(
|
|
4117
|
-
pass.setBindGroup(
|
|
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
|
|
4177
|
-
label: '
|
|
4149
|
+
let drawingShader = Q5.device.createShaderModule({
|
|
4150
|
+
label: 'drawingShader',
|
|
4178
4151
|
code: `
|
|
4179
|
-
struct
|
|
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
|
|
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(
|
|
4200
|
-
var vert = vec4f(
|
|
4201
|
-
vert = transforms[i32(
|
|
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
|
|
4206
|
-
|
|
4207
|
-
|
|
4208
|
-
return
|
|
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, //
|
|
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:
|
|
4208
|
+
module: drawingShader,
|
|
4242
4209
|
entryPoint: 'vertexMain',
|
|
4243
4210
|
buffers: [vertexBufferLayout]
|
|
4244
4211
|
},
|
|
4245
4212
|
fragment: {
|
|
4246
|
-
module:
|
|
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
|
|
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
|
|
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
|
|
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(
|
|
4722
|
-
var vert = vec4f(
|
|
4723
|
-
vert = transforms[i32(
|
|
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
|
|
4728
|
-
|
|
4729
|
-
|
|
4730
|
-
|
|
4731
|
-
|
|
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(
|
|
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:
|
|
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
|
-
|
|
4808
|
-
|
|
4809
|
-
$.
|
|
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
|
|
4894
|
-
|
|
4895
|
-
|
|
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
|
-
|
|
4914
|
-
|
|
4915
|
-
addVert(l,
|
|
4916
|
-
addVert(r,
|
|
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 *
|
|
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
|
-
|
|
4959
|
-
|
|
4960
|
-
|
|
4961
|
-
|
|
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
|
|
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
|
|
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
|
|
4994
|
-
@group(2) @binding(1) var
|
|
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
|
-
|
|
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(
|
|
5002
|
-
let char = textChars[
|
|
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 = ((
|
|
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
|
|
5016
|
-
|
|
5017
|
-
|
|
5018
|
-
|
|
5019
|
-
return
|
|
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(
|
|
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(
|
|
5034
|
-
let dy = sz.y*length(vec2f(dpdxFine(
|
|
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(
|
|
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(
|
|
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
|
-
|
|
5278
|
-
|
|
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 =
|
|
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
|
-
|
|
5380
|
+
charStack.push(charsData);
|
|
5416
5381
|
|
|
5417
|
-
let
|
|
5382
|
+
let txt = [];
|
|
5418
5383
|
|
|
5419
5384
|
if ($._matrixDirty) $._saveMatrix();
|
|
5420
5385
|
|
|
5421
|
-
|
|
5422
|
-
|
|
5423
|
-
|
|
5424
|
-
|
|
5425
|
-
|
|
5426
|
-
|
|
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
|
-
|
|
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 (
|
|
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
|
|
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(
|
|
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 =
|
|
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(
|
|
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
|
-
|
|
5536
|
-
|
|
5500
|
+
charStack.length = 0;
|
|
5501
|
+
textStack.length = 0;
|
|
5537
5502
|
});
|
|
5538
5503
|
};
|