q5 2.21.1 → 2.21.4
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 +2 -2
- package/q5.d.ts +63 -32
- package/q5.js +213 -159
- package/q5.min.js +1 -1
package/q5.js
CHANGED
|
@@ -1317,7 +1317,7 @@ Q5.renderers.c2d.image = ($, q) => {
|
|
|
1317
1317
|
};
|
|
1318
1318
|
});
|
|
1319
1319
|
|
|
1320
|
-
img.src = url;
|
|
1320
|
+
g.src = img.src = url;
|
|
1321
1321
|
|
|
1322
1322
|
if ($._disablePreload) return g._loader;
|
|
1323
1323
|
return g;
|
|
@@ -4520,12 +4520,13 @@ struct Q5 {
|
|
|
4520
4520
|
|
|
4521
4521
|
if ($.colorMode) $.colorMode('rgb', 1);
|
|
4522
4522
|
|
|
4523
|
-
let
|
|
4523
|
+
let encoder,
|
|
4524
|
+
pass,
|
|
4524
4525
|
mainView,
|
|
4525
4526
|
frameA,
|
|
4526
4527
|
frameB,
|
|
4528
|
+
frameLayout,
|
|
4527
4529
|
frameSampler,
|
|
4528
|
-
framePipeline,
|
|
4529
4530
|
frameBindGroup,
|
|
4530
4531
|
colorIndex = 1,
|
|
4531
4532
|
colorStackIndex = 8;
|
|
@@ -4533,6 +4534,7 @@ struct Q5 {
|
|
|
4533
4534
|
$._pipelineConfigs = [];
|
|
4534
4535
|
$._pipelines = [];
|
|
4535
4536
|
$._buffers = [];
|
|
4537
|
+
$._framePL = 0;
|
|
4536
4538
|
|
|
4537
4539
|
// local variables used for slightly better performance
|
|
4538
4540
|
// stores pipeline shifts and vertex counts/image indices
|
|
@@ -4599,18 +4601,40 @@ struct Q5 {
|
|
|
4599
4601
|
$._frameA = frameA = Q5.device.createTexture({ size, format, usage });
|
|
4600
4602
|
$._frameB = frameB = Q5.device.createTexture({ size, format, usage });
|
|
4601
4603
|
|
|
4602
|
-
|
|
4603
|
-
|
|
4604
|
-
|
|
4605
|
-
|
|
4606
|
-
|
|
4604
|
+
$._frameShaderCode =
|
|
4605
|
+
$._baseShaderCode +
|
|
4606
|
+
/* wgsl */ `
|
|
4607
|
+
struct VertexParams {
|
|
4608
|
+
@builtin(vertex_index) vertexIndex: u32
|
|
4609
|
+
}
|
|
4610
|
+
struct FragParams {
|
|
4611
|
+
@builtin(position) position: vec4f,
|
|
4612
|
+
@location(0) texCoord: vec2f
|
|
4613
|
+
}
|
|
4614
|
+
|
|
4615
|
+
const ndc = array(vec2f(-1,-1), vec2f(1,-1), vec2f(-1,1), vec2f(1,1));
|
|
4616
|
+
const quad = array(vec2f(0,1), vec2f(1,1), vec2f(0,0), vec2f(1,0));
|
|
4617
|
+
|
|
4618
|
+
@group(0) @binding(0) var<uniform> q: Q5;
|
|
4619
|
+
@group(0) @binding(1) var samp: sampler;
|
|
4620
|
+
@group(0) @binding(2) var tex: texture_2d<f32>;
|
|
4621
|
+
|
|
4622
|
+
@vertex
|
|
4623
|
+
fn vertexMain(v: VertexParams) -> FragParams {
|
|
4624
|
+
var f: FragParams;
|
|
4625
|
+
f.position = vec4f(ndc[v.vertexIndex], 0.0, 1.0);
|
|
4626
|
+
f.texCoord = quad[v.vertexIndex];
|
|
4627
|
+
return f;
|
|
4607
4628
|
}
|
|
4608
|
-
|
|
4609
|
-
@
|
|
4610
|
-
|
|
4611
|
-
|
|
4612
|
-
|
|
4613
|
-
|
|
4629
|
+
|
|
4630
|
+
@fragment
|
|
4631
|
+
fn fragMain(f: FragParams ) -> @location(0) vec4f {
|
|
4632
|
+
return textureSample(tex, samp, f.texCoord);
|
|
4633
|
+
}`;
|
|
4634
|
+
|
|
4635
|
+
let frameShader = Q5.device.createShaderModule({
|
|
4636
|
+
label: 'frameShader',
|
|
4637
|
+
code: $._frameShaderCode
|
|
4614
4638
|
});
|
|
4615
4639
|
|
|
4616
4640
|
frameSampler = Q5.device.createSampler({
|
|
@@ -4618,18 +4642,45 @@ struct Q5 {
|
|
|
4618
4642
|
minFilter: 'linear'
|
|
4619
4643
|
});
|
|
4620
4644
|
|
|
4621
|
-
|
|
4622
|
-
|
|
4623
|
-
|
|
4624
|
-
|
|
4645
|
+
frameLayout = Q5.device.createBindGroupLayout({
|
|
4646
|
+
label: 'frameLayout',
|
|
4647
|
+
entries: [
|
|
4648
|
+
{
|
|
4649
|
+
binding: 0,
|
|
4650
|
+
visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT,
|
|
4651
|
+
buffer: { type: 'uniform' }
|
|
4652
|
+
},
|
|
4653
|
+
{
|
|
4654
|
+
binding: 1,
|
|
4655
|
+
visibility: GPUShaderStage.FRAGMENT,
|
|
4656
|
+
sampler: { type: 'filtering' }
|
|
4657
|
+
},
|
|
4658
|
+
{
|
|
4659
|
+
binding: 2,
|
|
4660
|
+
visibility: GPUShaderStage.FRAGMENT,
|
|
4661
|
+
texture: { viewDimension: '2d', sampleType: 'float' }
|
|
4662
|
+
}
|
|
4663
|
+
]
|
|
4664
|
+
});
|
|
4665
|
+
|
|
4666
|
+
let framePipelineLayout = Q5.device.createPipelineLayout({
|
|
4667
|
+
bindGroupLayouts: [frameLayout]
|
|
4668
|
+
});
|
|
4669
|
+
|
|
4670
|
+
$._pipelineConfigs[0] = {
|
|
4671
|
+
layout: framePipelineLayout,
|
|
4672
|
+
vertex: { module: frameShader, entryPoint: 'vertexMain' },
|
|
4625
4673
|
fragment: {
|
|
4626
|
-
module:
|
|
4627
|
-
entryPoint: '
|
|
4628
|
-
targets: [{ format,
|
|
4674
|
+
module: frameShader,
|
|
4675
|
+
entryPoint: 'fragMain',
|
|
4676
|
+
targets: [{ format, blend: $.blendConfigs.normal }]
|
|
4629
4677
|
},
|
|
4630
4678
|
primitive: { topology: 'triangle-strip' },
|
|
4631
4679
|
multisample: { count: 4 }
|
|
4632
|
-
}
|
|
4680
|
+
};
|
|
4681
|
+
|
|
4682
|
+
// Create a pipeline for rendering frames
|
|
4683
|
+
$._pipelines[0] = Q5.device.createRenderPipeline($._pipelineConfigs[0]);
|
|
4633
4684
|
};
|
|
4634
4685
|
|
|
4635
4686
|
$._createCanvas = (w, h, opt) => {
|
|
@@ -4992,33 +5043,27 @@ struct Q5 {
|
|
|
4992
5043
|
}
|
|
4993
5044
|
};
|
|
4994
5045
|
|
|
4995
|
-
let shouldClear
|
|
5046
|
+
let shouldClear;
|
|
4996
5047
|
$.clear = () => {
|
|
4997
5048
|
shouldClear = true;
|
|
4998
5049
|
};
|
|
4999
5050
|
|
|
5000
|
-
const _drawFrame = () => {
|
|
5001
|
-
pass.setPipeline(framePipeline);
|
|
5002
|
-
pass.setBindGroup(0, frameBindGroup);
|
|
5003
|
-
pass.draw(4);
|
|
5004
|
-
};
|
|
5005
|
-
|
|
5006
5051
|
$._beginRender = () => {
|
|
5052
|
+
if (encoder) return;
|
|
5053
|
+
|
|
5007
5054
|
// swap the frame textures
|
|
5008
5055
|
const temp = frameA;
|
|
5009
5056
|
frameA = frameB;
|
|
5010
5057
|
frameB = temp;
|
|
5011
5058
|
|
|
5012
|
-
|
|
5059
|
+
encoder = Q5.device.createCommandEncoder();
|
|
5013
5060
|
|
|
5014
|
-
|
|
5015
|
-
|
|
5016
|
-
pass = q.pass = $.encoder.beginRenderPass({
|
|
5061
|
+
$._pass = pass = encoder.beginRenderPass({
|
|
5017
5062
|
label: 'q5-webgpu',
|
|
5018
5063
|
colorAttachments: [
|
|
5019
5064
|
{
|
|
5020
5065
|
view: mainView,
|
|
5021
|
-
resolveTarget:
|
|
5066
|
+
resolveTarget: frameA.createView(),
|
|
5022
5067
|
loadOp: 'clear',
|
|
5023
5068
|
storeOp: 'store',
|
|
5024
5069
|
clearValue: [0, 0, 0, 0]
|
|
@@ -5026,17 +5071,21 @@ struct Q5 {
|
|
|
5026
5071
|
]
|
|
5027
5072
|
});
|
|
5028
5073
|
|
|
5029
|
-
|
|
5030
|
-
|
|
5031
|
-
|
|
5032
|
-
|
|
5033
|
-
|
|
5034
|
-
|
|
5035
|
-
|
|
5036
|
-
|
|
5074
|
+
frameBindGroup = Q5.device.createBindGroup({
|
|
5075
|
+
layout: frameLayout,
|
|
5076
|
+
entries: [
|
|
5077
|
+
{ binding: 0, resource: { buffer: uniformBuffer } },
|
|
5078
|
+
{ binding: 1, resource: frameSampler },
|
|
5079
|
+
{ binding: 2, resource: frameB.createView() }
|
|
5080
|
+
]
|
|
5081
|
+
});
|
|
5037
5082
|
|
|
5038
|
-
|
|
5083
|
+
if (!shouldClear) {
|
|
5084
|
+
pass.setPipeline($._pipelines[0]);
|
|
5085
|
+
pass.setBindGroup(0, frameBindGroup);
|
|
5086
|
+
pass.draw(4);
|
|
5039
5087
|
}
|
|
5088
|
+
shouldClear = false;
|
|
5040
5089
|
};
|
|
5041
5090
|
|
|
5042
5091
|
$._render = () => {
|
|
@@ -5102,7 +5151,7 @@ struct Q5 {
|
|
|
5102
5151
|
pass.setPipeline($._pipelines[curPipelineIndex]);
|
|
5103
5152
|
}
|
|
5104
5153
|
|
|
5105
|
-
if (curPipelineIndex ==
|
|
5154
|
+
if (curPipelineIndex == 4 || curPipelineIndex >= 4000) {
|
|
5106
5155
|
// draw text
|
|
5107
5156
|
let o = drawStack[i + 2];
|
|
5108
5157
|
pass.setBindGroup(1, $._fonts[o].bindGroup);
|
|
@@ -5112,13 +5161,13 @@ struct Q5 {
|
|
|
5112
5161
|
pass.draw(4, v, 0, textCharOffset);
|
|
5113
5162
|
textCharOffset += v;
|
|
5114
5163
|
i++;
|
|
5115
|
-
} else if (curPipelineIndex ==
|
|
5164
|
+
} else if (curPipelineIndex == 2 || curPipelineIndex == 3 || curPipelineIndex >= 2000) {
|
|
5116
5165
|
// draw an image or video frame
|
|
5117
5166
|
// v is the texture index
|
|
5118
5167
|
pass.setBindGroup(1, $._textureBindGroups[v]);
|
|
5119
5168
|
pass.draw(4, 1, imageVertOffset);
|
|
5120
5169
|
imageVertOffset += 4;
|
|
5121
|
-
} else if (curPipelineIndex ==
|
|
5170
|
+
} else if (curPipelineIndex == 1 || curPipelineIndex >= 1000) {
|
|
5122
5171
|
// draw a shape
|
|
5123
5172
|
// v is the number of vertices
|
|
5124
5173
|
pass.draw(v, 1, drawVertOffset);
|
|
@@ -5130,32 +5179,33 @@ struct Q5 {
|
|
|
5130
5179
|
$._finishRender = () => {
|
|
5131
5180
|
pass.end();
|
|
5132
5181
|
|
|
5133
|
-
|
|
5134
|
-
|
|
5135
|
-
|
|
5136
|
-
|
|
5137
|
-
|
|
5138
|
-
|
|
5139
|
-
|
|
5140
|
-
|
|
5141
|
-
|
|
5142
|
-
|
|
5143
|
-
|
|
5144
|
-
});
|
|
5182
|
+
pass = encoder.beginRenderPass({
|
|
5183
|
+
colorAttachments: [
|
|
5184
|
+
{
|
|
5185
|
+
view: mainView,
|
|
5186
|
+
resolveTarget: $.ctx.getCurrentTexture().createView(),
|
|
5187
|
+
loadOp: 'clear',
|
|
5188
|
+
storeOp: 'store',
|
|
5189
|
+
clearValue: [0, 0, 0, 0]
|
|
5190
|
+
}
|
|
5191
|
+
]
|
|
5192
|
+
});
|
|
5145
5193
|
|
|
5146
|
-
|
|
5147
|
-
|
|
5148
|
-
|
|
5149
|
-
|
|
5150
|
-
|
|
5151
|
-
|
|
5152
|
-
|
|
5153
|
-
|
|
5154
|
-
|
|
5155
|
-
|
|
5156
|
-
|
|
5194
|
+
frameBindGroup = Q5.device.createBindGroup({
|
|
5195
|
+
layout: frameLayout,
|
|
5196
|
+
entries: [
|
|
5197
|
+
{ binding: 0, resource: { buffer: uniformBuffer } },
|
|
5198
|
+
{ binding: 1, resource: frameSampler },
|
|
5199
|
+
{ binding: 2, resource: frameA.createView() }
|
|
5200
|
+
]
|
|
5201
|
+
});
|
|
5202
|
+
|
|
5203
|
+
pass.setPipeline($._pipelines[$._framePL]);
|
|
5204
|
+
pass.setBindGroup(0, frameBindGroup);
|
|
5205
|
+
pass.draw(4);
|
|
5206
|
+
pass.end();
|
|
5157
5207
|
|
|
5158
|
-
Q5.device.queue.submit([
|
|
5208
|
+
Q5.device.queue.submit([encoder.finish()]);
|
|
5159
5209
|
|
|
5160
5210
|
// destroy buffers
|
|
5161
5211
|
Q5.device.queue.onSubmittedWorkDone().then(() => {
|
|
@@ -5163,7 +5213,7 @@ struct Q5 {
|
|
|
5163
5213
|
$._buffers = [];
|
|
5164
5214
|
});
|
|
5165
5215
|
|
|
5166
|
-
|
|
5216
|
+
$._pass = pass = encoder = null;
|
|
5167
5217
|
|
|
5168
5218
|
// clear the stacks for the next frame
|
|
5169
5219
|
drawStack.splice(0, drawStack.length);
|
|
@@ -5205,7 +5255,7 @@ Q5.webgpu = async function (scope, parent) {
|
|
|
5205
5255
|
return new Q5(scope, parent, 'webgpu');
|
|
5206
5256
|
};
|
|
5207
5257
|
Q5.renderers.webgpu.shapes = ($) => {
|
|
5208
|
-
$._shapesPL =
|
|
5258
|
+
$._shapesPL = 1;
|
|
5209
5259
|
|
|
5210
5260
|
$._shapesShaderCode =
|
|
5211
5261
|
$._baseShaderCode +
|
|
@@ -5275,7 +5325,7 @@ fn fragMain(f: FragParams) -> @location(0) vec4f {
|
|
|
5275
5325
|
bindGroupLayouts: $.bindGroupLayouts
|
|
5276
5326
|
});
|
|
5277
5327
|
|
|
5278
|
-
$._pipelineConfigs[
|
|
5328
|
+
$._pipelineConfigs[1] = {
|
|
5279
5329
|
label: 'shapesPipeline',
|
|
5280
5330
|
layout: pipelineLayout,
|
|
5281
5331
|
vertex: {
|
|
@@ -5292,7 +5342,7 @@ fn fragMain(f: FragParams) -> @location(0) vec4f {
|
|
|
5292
5342
|
multisample: { count: 4 }
|
|
5293
5343
|
};
|
|
5294
5344
|
|
|
5295
|
-
$._pipelines[
|
|
5345
|
+
$._pipelines[1] = Q5.device.createRenderPipeline($._pipelineConfigs[1]);
|
|
5296
5346
|
|
|
5297
5347
|
const addVert = (x, y, ci, ti) => {
|
|
5298
5348
|
let v = vertexStack,
|
|
@@ -5831,7 +5881,7 @@ fn fragMain(f: FragParams) -> @location(0) vec4f {
|
|
|
5831
5881
|
};
|
|
5832
5882
|
|
|
5833
5883
|
$._hooks.preRender.push(() => {
|
|
5834
|
-
$.
|
|
5884
|
+
$._pass.setPipeline($._pipelines[1]);
|
|
5835
5885
|
|
|
5836
5886
|
let vertexBuffer = Q5.device.createBuffer({
|
|
5837
5887
|
size: vertIndex * 4,
|
|
@@ -5842,7 +5892,7 @@ fn fragMain(f: FragParams) -> @location(0) vec4f {
|
|
|
5842
5892
|
new Float32Array(vertexBuffer.getMappedRange()).set(vertexStack.slice(0, vertIndex));
|
|
5843
5893
|
vertexBuffer.unmap();
|
|
5844
5894
|
|
|
5845
|
-
$.
|
|
5895
|
+
$._pass.setVertexBuffer(0, vertexBuffer);
|
|
5846
5896
|
|
|
5847
5897
|
$._buffers.push(vertexBuffer);
|
|
5848
5898
|
});
|
|
@@ -5852,8 +5902,8 @@ fn fragMain(f: FragParams) -> @location(0) vec4f {
|
|
|
5852
5902
|
});
|
|
5853
5903
|
};
|
|
5854
5904
|
Q5.renderers.webgpu.image = ($, q) => {
|
|
5855
|
-
$._imagePL =
|
|
5856
|
-
$._videoPL =
|
|
5905
|
+
$._imagePL = 2;
|
|
5906
|
+
$._videoPL = 3;
|
|
5857
5907
|
|
|
5858
5908
|
$._imageShaderCode =
|
|
5859
5909
|
$._baseShaderCode +
|
|
@@ -5881,7 +5931,7 @@ struct FragParams {
|
|
|
5881
5931
|
@group(1) @binding(1) var tex: texture_2d<f32>;
|
|
5882
5932
|
|
|
5883
5933
|
fn transformVertex(pos: vec2f, matrixIndex: f32) -> vec4f {
|
|
5884
|
-
var vert = vec4f(pos,
|
|
5934
|
+
var vert = vec4f(pos, 0f, 1f);
|
|
5885
5935
|
vert = transforms[i32(matrixIndex)] * vert;
|
|
5886
5936
|
vert.x /= q.halfWidth;
|
|
5887
5937
|
vert.y /= q.halfHeight;
|
|
@@ -5985,7 +6035,7 @@ fn fragMain(f: FragParams) -> @location(0) vec4f {
|
|
|
5985
6035
|
bindGroupLayouts: [...$.bindGroupLayouts, videoTextureLayout]
|
|
5986
6036
|
});
|
|
5987
6037
|
|
|
5988
|
-
$._pipelineConfigs[
|
|
6038
|
+
$._pipelineConfigs[2] = {
|
|
5989
6039
|
label: 'imagePipeline',
|
|
5990
6040
|
layout: imagePipelineLayout,
|
|
5991
6041
|
vertex: {
|
|
@@ -6002,9 +6052,9 @@ fn fragMain(f: FragParams) -> @location(0) vec4f {
|
|
|
6002
6052
|
multisample: { count: 4 }
|
|
6003
6053
|
};
|
|
6004
6054
|
|
|
6005
|
-
$._pipelines[
|
|
6055
|
+
$._pipelines[2] = Q5.device.createRenderPipeline($._pipelineConfigs[2]);
|
|
6006
6056
|
|
|
6007
|
-
$._pipelineConfigs[
|
|
6057
|
+
$._pipelineConfigs[3] = {
|
|
6008
6058
|
label: 'videoPipeline',
|
|
6009
6059
|
layout: videoPipelineLayout,
|
|
6010
6060
|
vertex: {
|
|
@@ -6021,10 +6071,66 @@ fn fragMain(f: FragParams) -> @location(0) vec4f {
|
|
|
6021
6071
|
multisample: { count: 4 }
|
|
6022
6072
|
};
|
|
6023
6073
|
|
|
6024
|
-
$._pipelines[
|
|
6074
|
+
$._pipelines[3] = Q5.device.createRenderPipeline($._pipelineConfigs[3]);
|
|
6025
6075
|
|
|
6026
6076
|
$._textureBindGroups = [];
|
|
6027
6077
|
|
|
6078
|
+
$._saveCanvas = async (data, ext) => {
|
|
6079
|
+
let texture = data.texture,
|
|
6080
|
+
w = texture.width,
|
|
6081
|
+
h = texture.height,
|
|
6082
|
+
bytesPerRow = Math.ceil((w * 4) / 256) * 256;
|
|
6083
|
+
|
|
6084
|
+
let buffer = Q5.device.createBuffer({
|
|
6085
|
+
size: bytesPerRow * h,
|
|
6086
|
+
usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ
|
|
6087
|
+
});
|
|
6088
|
+
|
|
6089
|
+
$._buffers.push(buffer);
|
|
6090
|
+
|
|
6091
|
+
let en = Q5.device.createCommandEncoder();
|
|
6092
|
+
|
|
6093
|
+
en.copyTextureToBuffer({ texture }, { buffer, bytesPerRow, rowsPerImage: h }, { width: w, height: h });
|
|
6094
|
+
|
|
6095
|
+
Q5.device.queue.submit([en.finish()]);
|
|
6096
|
+
|
|
6097
|
+
await buffer.mapAsync(GPUMapMode.READ);
|
|
6098
|
+
|
|
6099
|
+
let pad = new Uint8Array(buffer.getMappedRange());
|
|
6100
|
+
data = new Uint8Array(w * h * 4); // unpadded data
|
|
6101
|
+
|
|
6102
|
+
// Remove padding from each row and swap BGR to RGB
|
|
6103
|
+
for (let y = 0; y < h; y++) {
|
|
6104
|
+
const p = y * bytesPerRow; // padded row offset
|
|
6105
|
+
const u = y * w * 4; // unpadded row offset
|
|
6106
|
+
for (let x = 0; x < w; x++) {
|
|
6107
|
+
const pp = p + x * 4; // padded pixel offset
|
|
6108
|
+
const up = u + x * 4; // unpadded pixel offset
|
|
6109
|
+
data[up + 0] = pad[pp + 2]; // R <- B
|
|
6110
|
+
data[up + 1] = pad[pp + 1]; // G <- G
|
|
6111
|
+
data[up + 2] = pad[pp + 0]; // B <- R
|
|
6112
|
+
data[up + 3] = pad[pp + 3]; // A <- A
|
|
6113
|
+
}
|
|
6114
|
+
}
|
|
6115
|
+
|
|
6116
|
+
buffer.unmap();
|
|
6117
|
+
|
|
6118
|
+
let colorSpace = $.canvas.colorSpace;
|
|
6119
|
+
data = new Uint8ClampedArray(data.buffer);
|
|
6120
|
+
data = new ImageData(data, w, h, { colorSpace });
|
|
6121
|
+
let cnv = new $._Canvas(w, h);
|
|
6122
|
+
let ctx = cnv.getContext('2d', { colorSpace });
|
|
6123
|
+
ctx.putImageData(data, 0, 0);
|
|
6124
|
+
|
|
6125
|
+
// Convert to blob then data URL
|
|
6126
|
+
let blob = await cnv.convertToBlob({ type: 'image/' + ext });
|
|
6127
|
+
return await new Promise((resolve) => {
|
|
6128
|
+
let r = new FileReader();
|
|
6129
|
+
r.onloadend = () => resolve(r.result);
|
|
6130
|
+
r.readAsDataURL(blob);
|
|
6131
|
+
});
|
|
6132
|
+
};
|
|
6133
|
+
|
|
6028
6134
|
let makeSampler = (filter) => {
|
|
6029
6135
|
$._imageSampler = Q5.device.createSampler({
|
|
6030
6136
|
magFilter: filter,
|
|
@@ -6065,7 +6171,8 @@ fn fragMain(f: FragParams) -> @location(0) vec4f {
|
|
|
6065
6171
|
img.texture = texture;
|
|
6066
6172
|
img.textureIndex = tIdx + vidFrames;
|
|
6067
6173
|
|
|
6068
|
-
$._textureBindGroups[
|
|
6174
|
+
$._textureBindGroups[img.textureIndex] = Q5.device.createBindGroup({
|
|
6175
|
+
label: img.src || 'canvas',
|
|
6069
6176
|
layout: textureLayout,
|
|
6070
6177
|
entries: [
|
|
6071
6178
|
{ binding: 0, resource: $._imageSampler },
|
|
@@ -6206,67 +6313,11 @@ fn fragMain(f: FragParams) -> @location(0) vec4f {
|
|
|
6206
6313
|
}
|
|
6207
6314
|
};
|
|
6208
6315
|
|
|
6209
|
-
$._saveCanvas = async (data, ext) => {
|
|
6210
|
-
let texture = data.texture,
|
|
6211
|
-
w = texture.width,
|
|
6212
|
-
h = texture.height,
|
|
6213
|
-
bytesPerRow = Math.ceil((w * 4) / 256) * 256;
|
|
6214
|
-
|
|
6215
|
-
let buffer = Q5.device.createBuffer({
|
|
6216
|
-
size: bytesPerRow * h,
|
|
6217
|
-
usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ
|
|
6218
|
-
});
|
|
6219
|
-
|
|
6220
|
-
$._buffers.push(buffer);
|
|
6221
|
-
|
|
6222
|
-
let en = Q5.device.createCommandEncoder();
|
|
6223
|
-
|
|
6224
|
-
en.copyTextureToBuffer({ texture }, { buffer, bytesPerRow, rowsPerImage: h }, { width: w, height: h });
|
|
6225
|
-
|
|
6226
|
-
Q5.device.queue.submit([en.finish()]);
|
|
6227
|
-
|
|
6228
|
-
await buffer.mapAsync(GPUMapMode.READ);
|
|
6229
|
-
|
|
6230
|
-
let pad = new Uint8Array(buffer.getMappedRange());
|
|
6231
|
-
data = new Uint8Array(w * h * 4); // unpadded data
|
|
6232
|
-
|
|
6233
|
-
// Remove padding from each row and swap BGR to RGB
|
|
6234
|
-
for (let y = 0; y < h; y++) {
|
|
6235
|
-
const p = y * bytesPerRow; // padded row offset
|
|
6236
|
-
const u = y * w * 4; // unpadded row offset
|
|
6237
|
-
for (let x = 0; x < w; x++) {
|
|
6238
|
-
const pp = p + x * 4; // padded pixel offset
|
|
6239
|
-
const up = u + x * 4; // unpadded pixel offset
|
|
6240
|
-
data[up + 0] = pad[pp + 2]; // R <- B
|
|
6241
|
-
data[up + 1] = pad[pp + 1]; // G <- G
|
|
6242
|
-
data[up + 2] = pad[pp + 0]; // B <- R
|
|
6243
|
-
data[up + 3] = pad[pp + 3]; // A <- A
|
|
6244
|
-
}
|
|
6245
|
-
}
|
|
6246
|
-
|
|
6247
|
-
buffer.unmap();
|
|
6248
|
-
|
|
6249
|
-
let colorSpace = $.canvas.colorSpace;
|
|
6250
|
-
data = new Uint8ClampedArray(data.buffer);
|
|
6251
|
-
data = new ImageData(data, w, h, { colorSpace });
|
|
6252
|
-
let cnv = new $._Canvas(w, h);
|
|
6253
|
-
let ctx = cnv.getContext('2d', { colorSpace });
|
|
6254
|
-
ctx.putImageData(data, 0, 0);
|
|
6255
|
-
|
|
6256
|
-
// Convert to blob then data URL
|
|
6257
|
-
let blob = await cnv.convertToBlob({ type: 'image/' + ext });
|
|
6258
|
-
return await new Promise((resolve) => {
|
|
6259
|
-
let r = new FileReader();
|
|
6260
|
-
r.onloadend = () => resolve(r.result);
|
|
6261
|
-
r.readAsDataURL(blob);
|
|
6262
|
-
});
|
|
6263
|
-
};
|
|
6264
|
-
|
|
6265
6316
|
$._hooks.preRender.push(() => {
|
|
6266
6317
|
if (!vertIndex) return;
|
|
6267
6318
|
|
|
6268
6319
|
// Switch to image pipeline
|
|
6269
|
-
$.
|
|
6320
|
+
$._pass.setPipeline($._pipelines[2]);
|
|
6270
6321
|
|
|
6271
6322
|
let vertexBuffer = Q5.device.createBuffer({
|
|
6272
6323
|
size: vertIndex * 5,
|
|
@@ -6277,14 +6328,14 @@ fn fragMain(f: FragParams) -> @location(0) vec4f {
|
|
|
6277
6328
|
new Float32Array(vertexBuffer.getMappedRange()).set(vertexStack.slice(0, vertIndex));
|
|
6278
6329
|
vertexBuffer.unmap();
|
|
6279
6330
|
|
|
6280
|
-
$.
|
|
6331
|
+
$._pass.setVertexBuffer(1, vertexBuffer);
|
|
6281
6332
|
|
|
6282
6333
|
$._buffers.push(vertexBuffer);
|
|
6283
6334
|
|
|
6284
6335
|
if (vidFrames) {
|
|
6285
6336
|
// Switch to video pipeline
|
|
6286
|
-
$.
|
|
6287
|
-
$.
|
|
6337
|
+
$._pass.setPipeline($._pipelines[3]);
|
|
6338
|
+
$._pass.setVertexBuffer(1, vertexBuffer);
|
|
6288
6339
|
}
|
|
6289
6340
|
});
|
|
6290
6341
|
|
|
@@ -6304,7 +6355,7 @@ Q5.DILATE = 6;
|
|
|
6304
6355
|
Q5.ERODE = 7;
|
|
6305
6356
|
Q5.BLUR = 8;
|
|
6306
6357
|
Q5.renderers.webgpu.text = ($, q) => {
|
|
6307
|
-
$._textPL =
|
|
6358
|
+
$._textPL = 4;
|
|
6308
6359
|
|
|
6309
6360
|
$._textShaderCode =
|
|
6310
6361
|
$._baseShaderCode +
|
|
@@ -6347,6 +6398,7 @@ struct Text {
|
|
|
6347
6398
|
@group(2) @binding(1) var<storage> textMetadata: array<Text>;
|
|
6348
6399
|
|
|
6349
6400
|
const quad = array(vec2f(0, -1), vec2f(1, -1), vec2f(0, 0), vec2f(1, 0));
|
|
6401
|
+
const uvs = array(vec2f(0, 1), vec2f(1, 1), vec2f(0, 0), vec2f(1, 0));
|
|
6350
6402
|
|
|
6351
6403
|
fn calcPos(i: u32, char: vec4f, fontChar: Char, text: Text) -> vec2f {
|
|
6352
6404
|
return ((quad[i] * fontChar.size + char.xy + fontChar.offset) *
|
|
@@ -6354,8 +6406,7 @@ fn calcPos(i: u32, char: vec4f, fontChar: Char, text: Text) -> vec2f {
|
|
|
6354
6406
|
}
|
|
6355
6407
|
|
|
6356
6408
|
fn calcUV(i: u32, fontChar: Char) -> vec2f {
|
|
6357
|
-
return
|
|
6358
|
-
fontChar.texExtent + fontChar.texOffset;
|
|
6409
|
+
return uvs[i] * fontChar.texExtent + fontChar.texOffset;
|
|
6359
6410
|
}
|
|
6360
6411
|
|
|
6361
6412
|
fn transformVertex(pos: vec2f, matrixIndex: f32) -> vec4f {
|
|
@@ -6475,7 +6526,7 @@ fn fragMain(f : FragParams) -> @location(0) vec4f {
|
|
|
6475
6526
|
bindGroupLayouts: [...$.bindGroupLayouts, fontBindGroupLayout, textBindGroupLayout]
|
|
6476
6527
|
});
|
|
6477
6528
|
|
|
6478
|
-
$._pipelineConfigs[
|
|
6529
|
+
$._pipelineConfigs[4] = {
|
|
6479
6530
|
label: 'textPipeline',
|
|
6480
6531
|
layout: fontPipelineLayout,
|
|
6481
6532
|
vertex: { module: textShader, entryPoint: 'vertexMain' },
|
|
@@ -6488,7 +6539,7 @@ fn fragMain(f : FragParams) -> @location(0) vec4f {
|
|
|
6488
6539
|
multisample: { count: 4 }
|
|
6489
6540
|
};
|
|
6490
6541
|
|
|
6491
|
-
$._pipelines[
|
|
6542
|
+
$._pipelines[4] = Q5.device.createRenderPipeline($._pipelineConfigs[4]);
|
|
6492
6543
|
|
|
6493
6544
|
class MsdfFont {
|
|
6494
6545
|
constructor(bindGroup, lineHeight, chars, kernings) {
|
|
@@ -6934,13 +6985,14 @@ fn fragMain(f : FragParams) -> @location(0) vec4f {
|
|
|
6934
6985
|
});
|
|
6935
6986
|
};
|
|
6936
6987
|
Q5.renderers.webgpu.shaders = ($) => {
|
|
6937
|
-
let pipelineTypes = ['shapes', 'image', 'video', 'text'];
|
|
6988
|
+
let pipelineTypes = ['frame', 'shapes', 'image', 'video', 'text'];
|
|
6938
6989
|
|
|
6939
6990
|
let plCounters = {
|
|
6940
|
-
|
|
6941
|
-
|
|
6942
|
-
|
|
6943
|
-
|
|
6991
|
+
frame: 10,
|
|
6992
|
+
shapes: 1000,
|
|
6993
|
+
image: 2000,
|
|
6994
|
+
video: 3000,
|
|
6995
|
+
text: 4000
|
|
6944
6996
|
};
|
|
6945
6997
|
|
|
6946
6998
|
$._createShader = (code, type = 'shapes') => {
|
|
@@ -6976,12 +7028,14 @@ Q5.renderers.webgpu.shaders = ($) => {
|
|
|
6976
7028
|
let pl = plCounters[type];
|
|
6977
7029
|
$._pipelines[pl] = Q5.device.createRenderPipeline(config);
|
|
6978
7030
|
shader.pipelineIndex = pl;
|
|
7031
|
+
|
|
6979
7032
|
plCounters[type]++;
|
|
6980
7033
|
|
|
6981
7034
|
return shader;
|
|
6982
7035
|
};
|
|
6983
7036
|
|
|
6984
7037
|
$.createShader = $.createShapesShader = $._createShader;
|
|
7038
|
+
$.createFrameShader = (code) => $._createShader(code, 'frame');
|
|
6985
7039
|
$.createImageShader = (code) => $._createShader(code, 'image');
|
|
6986
7040
|
$.createVideoShader = (code) => $._createShader(code, 'video');
|
|
6987
7041
|
$.createTextShader = (code) => $._createShader(code, 'text');
|
|
@@ -6995,10 +7049,10 @@ Q5.renderers.webgpu.shaders = ($) => {
|
|
|
6995
7049
|
};
|
|
6996
7050
|
|
|
6997
7051
|
$.resetShaders = () => {
|
|
6998
|
-
$.
|
|
6999
|
-
$.
|
|
7000
|
-
$.
|
|
7001
|
-
$.
|
|
7002
|
-
$.
|
|
7052
|
+
$._framePL = 0;
|
|
7053
|
+
$._shapesPL = 1;
|
|
7054
|
+
$._imagePL = 2;
|
|
7055
|
+
$._videoPL = 3;
|
|
7056
|
+
$._textPL = 4;
|
|
7003
7057
|
};
|
|
7004
7058
|
};
|