zincjs 2.4.1-beta.5 → 2.4.1-beta.6
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/build/zinc.js +5 -5
- package/build/zinc.js.map +1 -1
- package/package.json +1 -1
- package/src/primitives/glyphset.js +1 -2
- package/src/renderer.js +4 -4
- package/src/tsl/glyphTransform.js +85 -54
package/package.json
CHANGED
|
@@ -1042,8 +1042,7 @@ const Glyphset = function () {
|
|
|
1042
1042
|
const renderer = this.region?.getScene?.()?.getRenderer?.();
|
|
1043
1043
|
if (!renderer) return;
|
|
1044
1044
|
renderer.compute(glyphCompute.compute);
|
|
1045
|
-
const colorResult =
|
|
1046
|
-
await renderer.getArrayBufferAsync(glyphCompute.outputs.color.value));
|
|
1045
|
+
const colorResult = (await readbackGlyphTransform(renderer, glyphCompute)).color;
|
|
1047
1046
|
// setColorAt(0, ...) is only here to lazily allocate instanceColor the
|
|
1048
1047
|
// same way the stock InstancedMesh API would; the loop below overwrites
|
|
1049
1048
|
// every instance (including 0) with the real snapshot right after.
|
package/src/renderer.js
CHANGED
|
@@ -152,10 +152,10 @@ const Renderer = function (containerIn) {
|
|
|
152
152
|
//WebGPURenderer otherwise requests a device with the default
|
|
153
153
|
//maxTextureArrayLayers (256 on many adapters), which is too low for
|
|
154
154
|
//some currnetly sypported scaffold, and the default
|
|
155
|
-
//maxStorageBuffersPerShaderStage (8 on many adapters)
|
|
156
|
-
//
|
|
157
|
-
//
|
|
158
|
-
//for both.
|
|
155
|
+
//maxStorageBuffersPerShaderStage (8 on many adapters). The glyph
|
|
156
|
+
//transform compute pass only needs two storage buffers, the higher
|
|
157
|
+
//limit is kept as headroom. We will request the highest possible on
|
|
158
|
+
//the device for both.
|
|
159
159
|
if (parameters.device === undefined && parameters.requiredLimits === undefined &&
|
|
160
160
|
typeof navigator !== 'undefined' && navigator.gpu) {
|
|
161
161
|
try {
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
int,
|
|
8
8
|
float,
|
|
9
9
|
vec3,
|
|
10
|
+
vec4,
|
|
10
11
|
uniform,
|
|
11
12
|
mix,
|
|
12
13
|
select,
|
|
@@ -28,6 +29,9 @@ const repeatModeToInt = (repeat_mode) => {
|
|
|
28
29
|
}
|
|
29
30
|
}
|
|
30
31
|
|
|
32
|
+
//Order of the vec4 fields of each glyph instance in the output buffer
|
|
33
|
+
const OUTPUT_FIELDS = { position: 0, axis1: 1, axis2: 2, axis3: 3, color: 4 };
|
|
34
|
+
|
|
31
35
|
const multiplierForRepeatMode = (repeat_mode) => {
|
|
32
36
|
if (repeat_mode == "AXES_2D" || repeat_mode == "MIRROR") return 2;
|
|
33
37
|
if (repeat_mode == "AXES_3D") return 3;
|
|
@@ -66,9 +70,10 @@ const multiplierForRepeatMode = (repeat_mode) => {
|
|
|
66
70
|
* @param {Array<number>} params.scaleFactors
|
|
67
71
|
* @returns {Object} { uniforms, compute, outputs } - update
|
|
68
72
|
* uniforms.bottomFrame/topFrame/proportion.value then pass `compute` to
|
|
69
|
-
* renderer.compute()/computeAsync(); read results back
|
|
70
|
-
* outputs.
|
|
71
|
-
*
|
|
73
|
+
* renderer.compute()/computeAsync(); read results back with
|
|
74
|
+
* readbackGlyphTransform(). outputs.buffer is the interleaved storage
|
|
75
|
+
* buffer (outputs.stride vec4 per instance), outputs.position/axis1/
|
|
76
|
+
* axis2/axis3/color provide element(index) accessors for materials.
|
|
72
77
|
*/
|
|
73
78
|
function createGlyphTransformCompute({
|
|
74
79
|
baseCount,
|
|
@@ -87,18 +92,38 @@ function createGlyphTransformCompute({
|
|
|
87
92
|
}) {
|
|
88
93
|
const multiplier = multiplierForRepeatMode(repeat_mode);
|
|
89
94
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
const
|
|
99
|
-
const
|
|
100
|
-
const
|
|
101
|
-
const
|
|
95
|
+
// All inputs are interleaved into a single storage buffer and all outputs
|
|
96
|
+
// into another, one vec4 per field. Separate buffers per field needed up
|
|
97
|
+
// to 11 storage buffers in the compute stage, more than some devices
|
|
98
|
+
// support (e.g. 10 on iOS). vec4 also avoids three.js padding vec3
|
|
99
|
+
// storage data into an extra copy.
|
|
100
|
+
const hasColor = !!colorData;
|
|
101
|
+
const inputSources = [positionsData, axis1Data, axis2Data, axis3Data, scaleData];
|
|
102
|
+
if (hasColor) inputSources.push(colorData);
|
|
103
|
+
const inputStride = inputSources.length;
|
|
104
|
+
const outputStride = hasColor ? OUTPUT_FIELDS.color + 1 : OUTPUT_FIELDS.axis3 + 1;
|
|
105
|
+
const recordCount = positionsData.length / 3;
|
|
106
|
+
const inputData = new Float32Array(recordCount * inputStride * 4);
|
|
107
|
+
for (let record = 0; record < recordCount; record++) {
|
|
108
|
+
for (let field = 0; field < inputStride; field++) {
|
|
109
|
+
const source = inputSources[field];
|
|
110
|
+
const target = (record * inputStride + field) * 4;
|
|
111
|
+
inputData[target] = source[record * 3];
|
|
112
|
+
inputData[target + 1] = source[record * 3 + 1];
|
|
113
|
+
inputData[target + 2] = source[record * 3 + 2];
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
const inputBuffer = attributeArray(inputData, 'vec4');
|
|
117
|
+
const outputBuffer = attributeArray(outputCount * outputStride, 'vec4');
|
|
118
|
+
|
|
119
|
+
const readInput = (recordIdx, field) =>
|
|
120
|
+
inputBuffer.element(recordIdx.mul(int(inputStride)).add(int(field))).xyz;
|
|
121
|
+
const writeOutput = (outIdx, field, value) =>
|
|
122
|
+
outputBuffer.element(outIdx.mul(int(outputStride)).add(int(field))).assign(vec4(value, 0));
|
|
123
|
+
//Accessor used by the render material to read an output field
|
|
124
|
+
const outputField = (field) => ({
|
|
125
|
+
element: (idx) => outputBuffer.element(int(idx).mul(int(outputStride)).add(int(field))).xyz,
|
|
126
|
+
});
|
|
102
127
|
|
|
103
128
|
const uniforms = {
|
|
104
129
|
bottomFrame: uniform(0, 'int'),
|
|
@@ -121,11 +146,11 @@ function createGlyphTransformCompute({
|
|
|
121
146
|
const topIdx = uniforms.topFrame.mul(int(baseCount)).add(inputIdx);
|
|
122
147
|
|
|
123
148
|
// Matches updateMorphGlyphsets(): bottom*proportion + top*(1-proportion).
|
|
124
|
-
const point = mix(
|
|
125
|
-
const axis1 = mix(
|
|
126
|
-
const axis2 = mix(
|
|
127
|
-
const axis3 = mix(
|
|
128
|
-
const scale = mix(
|
|
149
|
+
const point = mix(readInput(topIdx, 0), readInput(bottomIdx, 0), uniforms.proportion);
|
|
150
|
+
const axis1 = mix(readInput(topIdx, 1), readInput(bottomIdx, 1), uniforms.proportion);
|
|
151
|
+
const axis2 = mix(readInput(topIdx, 2), readInput(bottomIdx, 2), uniforms.proportion);
|
|
152
|
+
const axis3 = mix(readInput(topIdx, 3), readInput(bottomIdx, 3), uniforms.proportion);
|
|
153
|
+
const scale = mix(readInput(topIdx, 4), readInput(bottomIdx, 4), uniforms.proportion);
|
|
129
154
|
|
|
130
155
|
// NOTE: each branch below assigns straight into the output storage
|
|
131
156
|
// buffer elements (outPosition.element(outIdx).assign(...) etc.),
|
|
@@ -175,10 +200,10 @@ function createGlyphTransformCompute({
|
|
|
175
200
|
a3.assign(a3.negate());
|
|
176
201
|
});
|
|
177
202
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
203
|
+
writeOutput(outIdx, OUTPUT_FIELDS.position, p);
|
|
204
|
+
writeOutput(outIdx, OUTPUT_FIELDS.axis1, a1);
|
|
205
|
+
writeOutput(outIdx, OUTPUT_FIELDS.axis2, a2);
|
|
206
|
+
writeOutput(outIdx, OUTPUT_FIELDS.axis3, a3);
|
|
182
207
|
|
|
183
208
|
}).Else(() => {
|
|
184
209
|
|
|
@@ -220,16 +245,16 @@ function createGlyphTransformCompute({
|
|
|
220
245
|
.div(max(mag2, 1e-8));
|
|
221
246
|
const finalAxis2 = select(mag2.greaterThan(0), axis2Raw.mul(scaling2), axis2Raw);
|
|
222
247
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
248
|
+
writeOutput(outIdx, OUTPUT_FIELDS.position, finalPoint);
|
|
249
|
+
writeOutput(outIdx, OUTPUT_FIELDS.axis1, finalAxis1);
|
|
250
|
+
writeOutput(outIdx, OUTPUT_FIELDS.axis2, finalAxis2);
|
|
251
|
+
writeOutput(outIdx, OUTPUT_FIELDS.axis3, finalAxis3);
|
|
227
252
|
|
|
228
253
|
});
|
|
229
254
|
|
|
230
|
-
if (
|
|
231
|
-
const color = mix(
|
|
232
|
-
|
|
255
|
+
if (hasColor) {
|
|
256
|
+
const color = mix(readInput(topIdx, 5), readInput(bottomIdx, 5), uniforms.proportion);
|
|
257
|
+
writeOutput(outIdx, OUTPUT_FIELDS.color, color);
|
|
233
258
|
}
|
|
234
259
|
|
|
235
260
|
} )().compute( outputCount );
|
|
@@ -238,11 +263,14 @@ function createGlyphTransformCompute({
|
|
|
238
263
|
uniforms,
|
|
239
264
|
compute: computeFn,
|
|
240
265
|
outputs: {
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
266
|
+
buffer: outputBuffer,
|
|
267
|
+
stride: outputStride,
|
|
268
|
+
count: outputCount,
|
|
269
|
+
position: outputField(OUTPUT_FIELDS.position),
|
|
270
|
+
axis1: outputField(OUTPUT_FIELDS.axis1),
|
|
271
|
+
axis2: outputField(OUTPUT_FIELDS.axis2),
|
|
272
|
+
axis3: outputField(OUTPUT_FIELDS.axis3),
|
|
273
|
+
color: hasColor ? outputField(OUTPUT_FIELDS.color) : undefined,
|
|
246
274
|
},
|
|
247
275
|
};
|
|
248
276
|
}
|
|
@@ -264,27 +292,30 @@ function createGlyphTransformCompute({
|
|
|
264
292
|
* @param {THREE.WebGPURenderer} renderer
|
|
265
293
|
* @param {Object} glyphCompute - return value of createGlyphTransformCompute().
|
|
266
294
|
* @returns {Promise<Object>} { position, axis1, axis2, axis3, color } -
|
|
267
|
-
* plain Float32Arrays
|
|
268
|
-
* present when the glyphset has colours).
|
|
295
|
+
* plain Float32Arrays with a 4 float stride per output glyph instance
|
|
296
|
+
* (color only present when the glyphset has colours).
|
|
269
297
|
*/
|
|
270
298
|
async function readbackGlyphTransform(renderer, glyphCompute) {
|
|
271
|
-
const {
|
|
272
|
-
//
|
|
273
|
-
|
|
274
|
-
//
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
299
|
+
const { buffer, stride, count, color } = glyphCompute.outputs;
|
|
300
|
+
//A single readback of the interleaved output buffer
|
|
301
|
+
const data = new Float32Array(await renderer.getArrayBufferAsync(buffer.value));
|
|
302
|
+
//Split into one array per field, each with a 4 float stride per instance
|
|
303
|
+
const extract = (field) => {
|
|
304
|
+
const result = new Float32Array(count * 4);
|
|
305
|
+
for (let i = 0; i < count; i++) {
|
|
306
|
+
const source = (i * stride + field) * 4;
|
|
307
|
+
result[i * 4] = data[source];
|
|
308
|
+
result[i * 4 + 1] = data[source + 1];
|
|
309
|
+
result[i * 4 + 2] = data[source + 2];
|
|
310
|
+
}
|
|
311
|
+
return result;
|
|
312
|
+
};
|
|
282
313
|
return {
|
|
283
|
-
position:
|
|
284
|
-
axis1:
|
|
285
|
-
axis2:
|
|
286
|
-
axis3:
|
|
287
|
-
color:
|
|
314
|
+
position: extract(OUTPUT_FIELDS.position),
|
|
315
|
+
axis1: extract(OUTPUT_FIELDS.axis1),
|
|
316
|
+
axis2: extract(OUTPUT_FIELDS.axis2),
|
|
317
|
+
axis3: extract(OUTPUT_FIELDS.axis3),
|
|
318
|
+
color: color ? extract(OUTPUT_FIELDS.color) : undefined,
|
|
288
319
|
};
|
|
289
320
|
}
|
|
290
321
|
|