@fjandin/react-shader 0.0.19 → 0.0.20
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/README.md +248 -22
- package/dist/ReactGpuShader.d.ts +3 -4
- package/dist/ReactGpuShader.d.ts.map +1 -1
- package/dist/hooks/useWebGPU.d.ts +2 -2
- package/dist/hooks/useWebGPU.d.ts.map +1 -1
- package/dist/index.cjs +174 -134
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +174 -134
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -220,30 +220,15 @@ function isVec3(value) {
|
|
|
220
220
|
function isVec4(value) {
|
|
221
221
|
return Array.isArray(value) && value.length === 4 && typeof value[0] === "number";
|
|
222
222
|
}
|
|
223
|
-
function isVec4Array(value) {
|
|
224
|
-
return Array.isArray(value) && value.length > 0 && Array.isArray(value[0]) && value[0].length === 4;
|
|
225
|
-
}
|
|
226
223
|
function inferWgslType(value) {
|
|
227
|
-
if (typeof value === "number")
|
|
228
|
-
return
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
arrayLength: 100
|
|
236
|
-
};
|
|
237
|
-
}
|
|
238
|
-
if (isVec4(value)) {
|
|
239
|
-
return { wgslType: "vec4f", baseType: "vec4f", isArray: false, arrayLength: 0 };
|
|
240
|
-
}
|
|
241
|
-
if (isVec3(value)) {
|
|
242
|
-
return { wgslType: "vec3f", baseType: "vec3f", isArray: false, arrayLength: 0 };
|
|
243
|
-
}
|
|
244
|
-
if (isVec2(value)) {
|
|
245
|
-
return { wgslType: "vec2f", baseType: "vec2f", isArray: false, arrayLength: 0 };
|
|
246
|
-
}
|
|
224
|
+
if (typeof value === "number")
|
|
225
|
+
return "f32";
|
|
226
|
+
if (isVec4(value))
|
|
227
|
+
return "vec4f";
|
|
228
|
+
if (isVec3(value))
|
|
229
|
+
return "vec3f";
|
|
230
|
+
if (isVec2(value))
|
|
231
|
+
return "vec2f";
|
|
247
232
|
throw new Error(`Unsupported uniform value type: ${typeof value}`);
|
|
248
233
|
}
|
|
249
234
|
function getTypeAlignment(type) {
|
|
@@ -269,7 +254,6 @@ function getTypeSize(type) {
|
|
|
269
254
|
return 16;
|
|
270
255
|
}
|
|
271
256
|
}
|
|
272
|
-
var UNIFORM_ARRAY_STRIDE = 16;
|
|
273
257
|
var DEFAULT_UNIFORMS = [
|
|
274
258
|
{ name: "iTime", baseType: "f32" },
|
|
275
259
|
{ name: "iMouseLeftDown", baseType: "f32" },
|
|
@@ -284,49 +268,20 @@ function calculateUniformLayout(customUniforms) {
|
|
|
284
268
|
const alignment = getTypeAlignment(baseType);
|
|
285
269
|
const size = getTypeSize(baseType);
|
|
286
270
|
offset = Math.ceil(offset / alignment) * alignment;
|
|
287
|
-
fields.push({ name, type: baseType, offset, size
|
|
271
|
+
fields.push({ name, type: baseType, offset, size });
|
|
288
272
|
offset += size;
|
|
289
273
|
};
|
|
290
|
-
const addArrayField = (name, baseType, arrayLength) => {
|
|
291
|
-
offset = Math.ceil(offset / 16) * 16;
|
|
292
|
-
const totalSize = arrayLength * UNIFORM_ARRAY_STRIDE;
|
|
293
|
-
fields.push({
|
|
294
|
-
name,
|
|
295
|
-
type: `array<${baseType}, ${arrayLength}>`,
|
|
296
|
-
offset,
|
|
297
|
-
size: totalSize,
|
|
298
|
-
isArray: true,
|
|
299
|
-
arrayLength,
|
|
300
|
-
elementStride: UNIFORM_ARRAY_STRIDE
|
|
301
|
-
});
|
|
302
|
-
offset += totalSize;
|
|
303
|
-
};
|
|
304
274
|
for (const u of DEFAULT_UNIFORMS) {
|
|
305
275
|
addField(u.name, u.baseType);
|
|
306
276
|
}
|
|
307
277
|
if (customUniforms) {
|
|
308
|
-
const
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
scalarEntries.push({ name, inferred });
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
for (const { name } of arrayEntries) {
|
|
319
|
-
scalarEntries.push({
|
|
320
|
-
name: `${name}_count`,
|
|
321
|
-
inferred: { wgslType: "f32", baseType: "f32", isArray: false, arrayLength: 0 }
|
|
322
|
-
});
|
|
323
|
-
}
|
|
324
|
-
scalarEntries.sort((a, b) => getTypeAlignment(b.inferred.baseType) - getTypeAlignment(a.inferred.baseType));
|
|
325
|
-
for (const { name, inferred } of scalarEntries) {
|
|
326
|
-
addField(name, inferred.baseType);
|
|
327
|
-
}
|
|
328
|
-
for (const { name, inferred } of arrayEntries) {
|
|
329
|
-
addArrayField(name, inferred.baseType, inferred.arrayLength);
|
|
278
|
+
const entries = Object.entries(customUniforms).map(([name, value]) => ({
|
|
279
|
+
name,
|
|
280
|
+
baseType: inferWgslType(value)
|
|
281
|
+
}));
|
|
282
|
+
entries.sort((a, b) => getTypeAlignment(b.baseType) - getTypeAlignment(a.baseType));
|
|
283
|
+
for (const { name, baseType } of entries) {
|
|
284
|
+
addField(name, baseType);
|
|
330
285
|
}
|
|
331
286
|
}
|
|
332
287
|
const bufferSize = Math.ceil(offset / 16) * 16;
|
|
@@ -341,21 +296,9 @@ ${members}
|
|
|
341
296
|
}
|
|
342
297
|
function packUniformValue(field, value, floatData) {
|
|
343
298
|
const floatOffset = field.offset / 4;
|
|
344
|
-
if (
|
|
345
|
-
const stride = field.elementStride / 4;
|
|
346
|
-
const maxLen = field.arrayLength;
|
|
347
|
-
if (isVec4Array(value)) {
|
|
348
|
-
for (let i = 0;i < value.length && i < maxLen; i++) {
|
|
349
|
-
const elemOffset = floatOffset + i * stride;
|
|
350
|
-
floatData[elemOffset] = value[i][0];
|
|
351
|
-
floatData[elemOffset + 1] = value[i][1];
|
|
352
|
-
floatData[elemOffset + 2] = value[i][2];
|
|
353
|
-
floatData[elemOffset + 3] = value[i][3];
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
} else if (typeof value === "number") {
|
|
299
|
+
if (typeof value === "number") {
|
|
357
300
|
floatData[floatOffset] = value;
|
|
358
|
-
} else if (Array.isArray(value)
|
|
301
|
+
} else if (Array.isArray(value)) {
|
|
359
302
|
for (let i = 0;i < value.length; i++) {
|
|
360
303
|
floatData[floatOffset + i] = value[i];
|
|
361
304
|
}
|
|
@@ -383,11 +326,60 @@ fn main(@builtin(vertex_index) vertexIndex: u32) -> VertexOutput {
|
|
|
383
326
|
return output;
|
|
384
327
|
}
|
|
385
328
|
`;
|
|
386
|
-
function
|
|
329
|
+
function getStorageBufferNames(defs) {
|
|
330
|
+
if (!defs)
|
|
331
|
+
return [];
|
|
332
|
+
return Object.keys(defs).sort();
|
|
333
|
+
}
|
|
334
|
+
function createStorageBuffer(device, name, binding, data) {
|
|
335
|
+
const length = Math.max(data.length, 1);
|
|
336
|
+
const byteSize = length * 16;
|
|
337
|
+
const buffer = device.createBuffer({
|
|
338
|
+
label: `storage: ${name}`,
|
|
339
|
+
size: byteSize,
|
|
340
|
+
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST
|
|
341
|
+
});
|
|
342
|
+
const packingArray = new Float32Array(length * 4);
|
|
343
|
+
for (let i = 0;i < data.length; i++) {
|
|
344
|
+
const off = i * 4;
|
|
345
|
+
packingArray[off] = data[i][0];
|
|
346
|
+
packingArray[off + 1] = data[i][1];
|
|
347
|
+
packingArray[off + 2] = data[i][2];
|
|
348
|
+
packingArray[off + 3] = data[i][3];
|
|
349
|
+
}
|
|
350
|
+
device.queue.writeBuffer(buffer, 0, packingArray);
|
|
351
|
+
return { name, binding, buffer, currentLength: length, dataLength: data.length, packingArray };
|
|
352
|
+
}
|
|
353
|
+
function packAndUploadStorageBuffer(device, entry, data) {
|
|
354
|
+
const arr = entry.packingArray;
|
|
355
|
+
for (let i = 0;i < data.length; i++) {
|
|
356
|
+
const off = i * 4;
|
|
357
|
+
arr[off] = data[i][0];
|
|
358
|
+
arr[off + 1] = data[i][1];
|
|
359
|
+
arr[off + 2] = data[i][2];
|
|
360
|
+
arr[off + 3] = data[i][3];
|
|
361
|
+
}
|
|
362
|
+
const uploadLength = Math.max(entry.dataLength, 1);
|
|
363
|
+
device.queue.writeBuffer(entry.buffer, 0, arr, 0, uploadLength * 4);
|
|
364
|
+
}
|
|
365
|
+
function rebuildBindGroup(state) {
|
|
366
|
+
const entries = [{ binding: 0, resource: { buffer: state.uniformBuffer } }];
|
|
367
|
+
for (const sb of state.storageBuffers) {
|
|
368
|
+
entries.push({ binding: sb.binding, resource: { buffer: sb.buffer } });
|
|
369
|
+
}
|
|
370
|
+
return state.device.createBindGroup({
|
|
371
|
+
layout: state.bindGroupLayout,
|
|
372
|
+
entries
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
function createFragmentShader(userCode, layout, storageBufferNames) {
|
|
376
|
+
const storageDeclarations = storageBufferNames.map((name, i) => `@group(0) @binding(${i + 1}) var<storage, read> ${name}: array<vec4f>;`).join(`
|
|
377
|
+
`);
|
|
387
378
|
return `
|
|
388
379
|
${generateUniformStruct(layout)}
|
|
389
380
|
|
|
390
381
|
@group(0) @binding(0) var<uniform> uniforms: Uniforms;
|
|
382
|
+
${storageDeclarations}
|
|
391
383
|
|
|
392
384
|
${userCode}
|
|
393
385
|
|
|
@@ -400,7 +392,7 @@ fn main(@location(0) uv: vec2f) -> @location(0) vec4f {
|
|
|
400
392
|
}
|
|
401
393
|
`;
|
|
402
394
|
}
|
|
403
|
-
async function initializeWebGPU(canvas, fragmentSource, customUniforms) {
|
|
395
|
+
async function initializeWebGPU(canvas, fragmentSource, customUniforms, storageBufferDefs) {
|
|
404
396
|
if (!navigator.gpu) {
|
|
405
397
|
throw new Error("WebGPU not supported in this browser");
|
|
406
398
|
}
|
|
@@ -423,36 +415,43 @@ async function initializeWebGPU(canvas, fragmentSource, customUniforms) {
|
|
|
423
415
|
alphaMode: "premultiplied"
|
|
424
416
|
});
|
|
425
417
|
const uniformLayout = calculateUniformLayout(customUniforms);
|
|
418
|
+
const storageBufferNames = getStorageBufferNames(storageBufferDefs);
|
|
426
419
|
const vertexModule = device.createShaderModule({
|
|
427
420
|
label: "vertex shader",
|
|
428
421
|
code: VERTEX_SHADER
|
|
429
422
|
});
|
|
430
423
|
const fragmentModule = device.createShaderModule({
|
|
431
424
|
label: "fragment shader",
|
|
432
|
-
code: createFragmentShader(fragmentSource, uniformLayout)
|
|
425
|
+
code: createFragmentShader(fragmentSource, uniformLayout, storageBufferNames)
|
|
433
426
|
});
|
|
434
427
|
const uniformBuffer = device.createBuffer({
|
|
435
428
|
label: "uniforms",
|
|
436
429
|
size: uniformLayout.bufferSize,
|
|
437
430
|
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
|
|
438
431
|
});
|
|
439
|
-
const
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
432
|
+
const storageBuffers = storageBufferNames.map((name, i) => createStorageBuffer(device, name, i + 1, storageBufferDefs?.[name] ?? []));
|
|
433
|
+
const layoutEntries = [
|
|
434
|
+
{
|
|
435
|
+
binding: 0,
|
|
436
|
+
visibility: GPUShaderStage.FRAGMENT,
|
|
437
|
+
buffer: { type: "uniform" }
|
|
438
|
+
}
|
|
439
|
+
];
|
|
440
|
+
for (const sb of storageBuffers) {
|
|
441
|
+
layoutEntries.push({
|
|
442
|
+
binding: sb.binding,
|
|
443
|
+
visibility: GPUShaderStage.FRAGMENT,
|
|
444
|
+
buffer: { type: "read-only-storage" }
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
const bindGroupLayout = device.createBindGroupLayout({ entries: layoutEntries });
|
|
448
|
+
const bindGroupEntries = [{ binding: 0, resource: { buffer: uniformBuffer } }];
|
|
449
|
+
for (const sb of storageBuffers) {
|
|
450
|
+
bindGroupEntries.push({ binding: sb.binding, resource: { buffer: sb.buffer } });
|
|
451
|
+
}
|
|
448
452
|
const uniformBindGroup = device.createBindGroup({
|
|
449
453
|
layout: bindGroupLayout,
|
|
450
|
-
entries:
|
|
451
|
-
{
|
|
452
|
-
binding: 0,
|
|
453
|
-
resource: { buffer: uniformBuffer }
|
|
454
|
-
}
|
|
455
|
-
]
|
|
454
|
+
entries: bindGroupEntries
|
|
456
455
|
});
|
|
457
456
|
const pipelineLayout = device.createPipelineLayout({
|
|
458
457
|
bindGroupLayouts: [bindGroupLayout]
|
|
@@ -476,10 +475,26 @@ async function initializeWebGPU(canvas, fragmentSource, customUniforms) {
|
|
|
476
475
|
pipeline,
|
|
477
476
|
uniformBuffer,
|
|
478
477
|
uniformBindGroup,
|
|
479
|
-
uniformLayout
|
|
478
|
+
uniformLayout,
|
|
479
|
+
bindGroupLayout,
|
|
480
|
+
storageBuffers,
|
|
481
|
+
renderPassDescriptor: {
|
|
482
|
+
colorAttachments: [
|
|
483
|
+
{
|
|
484
|
+
view: undefined,
|
|
485
|
+
clearValue: { r: 0, g: 0, b: 0, a: 1 },
|
|
486
|
+
loadOp: "clear",
|
|
487
|
+
storeOp: "store"
|
|
488
|
+
}
|
|
489
|
+
]
|
|
490
|
+
},
|
|
491
|
+
submitArray: [null]
|
|
480
492
|
};
|
|
481
493
|
}
|
|
482
494
|
function cleanupWebGPU(state) {
|
|
495
|
+
for (const sb of state.storageBuffers) {
|
|
496
|
+
sb.buffer.destroy();
|
|
497
|
+
}
|
|
483
498
|
state.uniformBuffer.destroy();
|
|
484
499
|
state.device.destroy();
|
|
485
500
|
}
|
|
@@ -491,6 +506,7 @@ function useWebGPU(options) {
|
|
|
491
506
|
const lastFrameTimeRef = useRef2(0);
|
|
492
507
|
const mouseRef = useRef2([0, 0]);
|
|
493
508
|
const mouseNormalizedRef = useRef2([0, 0]);
|
|
509
|
+
const resolutionRef = useRef2([0, 0]);
|
|
494
510
|
const mouseLeftDownRef = useRef2(false);
|
|
495
511
|
const canvasRectRef = useRef2(null);
|
|
496
512
|
const onErrorRef = useRef2(options.onError);
|
|
@@ -503,7 +519,10 @@ function useWebGPU(options) {
|
|
|
503
519
|
const timeScaleRef = useRef2(options.timeScale ?? 1);
|
|
504
520
|
const fragmentRef = useRef2(options.fragment);
|
|
505
521
|
const uniformsRef = useRef2(options.uniforms);
|
|
522
|
+
const storageBuffersRef = useRef2(options.storageBuffers);
|
|
506
523
|
const dprRef = useRef2(window.devicePixelRatio || 1);
|
|
524
|
+
const uniformDataRef = useRef2(null);
|
|
525
|
+
const allValuesRef = useRef2({});
|
|
507
526
|
const frameInfoRef = useRef2({
|
|
508
527
|
deltaTime: 0,
|
|
509
528
|
time: 0,
|
|
@@ -522,6 +541,7 @@ function useWebGPU(options) {
|
|
|
522
541
|
timeScaleRef.current = options.timeScale ?? 1;
|
|
523
542
|
fragmentRef.current = options.fragment;
|
|
524
543
|
uniformsRef.current = options.uniforms;
|
|
544
|
+
storageBuffersRef.current = options.storageBuffers;
|
|
525
545
|
const render = useCallback2((time) => {
|
|
526
546
|
const state = stateRef.current;
|
|
527
547
|
const canvas = canvasRef.current;
|
|
@@ -531,18 +551,18 @@ function useWebGPU(options) {
|
|
|
531
551
|
const deltaTime = lastFrameTimeRef.current === 0 ? 0 : (time - lastFrameTimeRef.current) / 1000;
|
|
532
552
|
lastFrameTimeRef.current = time;
|
|
533
553
|
elapsedTimeRef.current += deltaTime * timeScaleRef.current;
|
|
534
|
-
frameInfoRef.current
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
554
|
+
const info = frameInfoRef.current;
|
|
555
|
+
info.deltaTime = deltaTime;
|
|
556
|
+
info.time = elapsedTimeRef.current;
|
|
557
|
+
info.resolution[0] = canvas.width;
|
|
558
|
+
info.resolution[1] = canvas.height;
|
|
559
|
+
info.mouse = mouseRef.current;
|
|
560
|
+
info.mouseNormalized = mouseNormalizedRef.current;
|
|
561
|
+
info.mouseLeftDown = mouseLeftDownRef.current;
|
|
542
562
|
if (onFrameRef.current) {
|
|
543
563
|
onFrameRef.current(frameInfoRef.current);
|
|
544
564
|
}
|
|
545
|
-
const { device, context, pipeline, uniformBuffer,
|
|
565
|
+
const { device, context, pipeline, uniformBuffer, uniformLayout } = state;
|
|
546
566
|
const dpr = dprRef.current;
|
|
547
567
|
const displayWidth = canvas.clientWidth;
|
|
548
568
|
const displayHeight = canvas.clientHeight;
|
|
@@ -556,22 +576,21 @@ function useWebGPU(options) {
|
|
|
556
576
|
canvas.width = bufferWidth;
|
|
557
577
|
canvas.height = bufferHeight;
|
|
558
578
|
}
|
|
559
|
-
const
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
}
|
|
579
|
+
const allValues = allValuesRef.current;
|
|
580
|
+
allValues.iTime = elapsedTimeRef.current;
|
|
581
|
+
allValues.iMouseLeftDown = mouseLeftDownRef.current ? 1 : 0;
|
|
582
|
+
resolutionRef.current[0] = canvas.width;
|
|
583
|
+
resolutionRef.current[1] = canvas.height;
|
|
584
|
+
allValues.iResolution = resolutionRef.current;
|
|
585
|
+
allValues.iMouse = mouseRef.current;
|
|
586
|
+
allValues.iMouseNormalized = mouseNormalizedRef.current;
|
|
587
|
+
const customs = uniformsRef.current;
|
|
588
|
+
if (customs) {
|
|
589
|
+
for (const name in customs) {
|
|
590
|
+
allValues[name] = customs[name];
|
|
572
591
|
}
|
|
573
592
|
}
|
|
574
|
-
const uniformData =
|
|
593
|
+
const uniformData = uniformDataRef.current;
|
|
575
594
|
for (const field of uniformLayout.fields) {
|
|
576
595
|
const value = allValues[field.name];
|
|
577
596
|
if (value === undefined) {
|
|
@@ -580,23 +599,41 @@ function useWebGPU(options) {
|
|
|
580
599
|
packUniformValue(field, value, uniformData);
|
|
581
600
|
}
|
|
582
601
|
device.queue.writeBuffer(uniformBuffer, 0, uniformData);
|
|
602
|
+
let needsBindGroupRebuild = false;
|
|
603
|
+
for (const entry of state.storageBuffers) {
|
|
604
|
+
const data = storageBuffersRef.current?.[entry.name];
|
|
605
|
+
if (!data)
|
|
606
|
+
continue;
|
|
607
|
+
const requiredLength = Math.max(data.length, 1);
|
|
608
|
+
if (requiredLength > entry.currentLength || requiredLength < entry.currentLength / 2) {
|
|
609
|
+
const allocLength = Math.max(Math.ceil(requiredLength * 1.5), 1);
|
|
610
|
+
entry.buffer.destroy();
|
|
611
|
+
const byteSize = allocLength * 16;
|
|
612
|
+
entry.buffer = device.createBuffer({
|
|
613
|
+
label: `storage: ${entry.name}`,
|
|
614
|
+
size: byteSize,
|
|
615
|
+
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST
|
|
616
|
+
});
|
|
617
|
+
entry.packingArray = new Float32Array(allocLength * 4);
|
|
618
|
+
entry.currentLength = allocLength;
|
|
619
|
+
needsBindGroupRebuild = true;
|
|
620
|
+
}
|
|
621
|
+
entry.dataLength = data.length;
|
|
622
|
+
packAndUploadStorageBuffer(device, entry, data);
|
|
623
|
+
}
|
|
624
|
+
if (needsBindGroupRebuild) {
|
|
625
|
+
state.uniformBindGroup = rebuildBindGroup(state);
|
|
626
|
+
}
|
|
583
627
|
const commandEncoder = device.createCommandEncoder();
|
|
584
628
|
const textureView = context.getCurrentTexture().createView();
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
{
|
|
588
|
-
view: textureView,
|
|
589
|
-
clearValue: { r: 0, g: 0, b: 0, a: 1 },
|
|
590
|
-
loadOp: "clear",
|
|
591
|
-
storeOp: "store"
|
|
592
|
-
}
|
|
593
|
-
]
|
|
594
|
-
});
|
|
629
|
+
state.renderPassDescriptor.colorAttachments[0].view = textureView;
|
|
630
|
+
const renderPass = commandEncoder.beginRenderPass(state.renderPassDescriptor);
|
|
595
631
|
renderPass.setPipeline(pipeline);
|
|
596
|
-
renderPass.setBindGroup(0, uniformBindGroup);
|
|
632
|
+
renderPass.setBindGroup(0, state.uniformBindGroup);
|
|
597
633
|
renderPass.draw(3);
|
|
598
634
|
renderPass.end();
|
|
599
|
-
|
|
635
|
+
state.submitArray[0] = commandEncoder.finish();
|
|
636
|
+
device.queue.submit(state.submitArray);
|
|
600
637
|
animationFrameRef.current = requestAnimationFrame(render);
|
|
601
638
|
}, []);
|
|
602
639
|
useEffect(() => {
|
|
@@ -606,12 +643,13 @@ function useWebGPU(options) {
|
|
|
606
643
|
let mounted = true;
|
|
607
644
|
const initialize = async () => {
|
|
608
645
|
try {
|
|
609
|
-
const state = await initializeWebGPU(canvas, fragmentRef.current, uniformsRef.current);
|
|
646
|
+
const state = await initializeWebGPU(canvas, fragmentRef.current, uniformsRef.current, storageBuffersRef.current);
|
|
610
647
|
if (!mounted) {
|
|
611
648
|
cleanupWebGPU(state);
|
|
612
649
|
return;
|
|
613
650
|
}
|
|
614
651
|
stateRef.current = state;
|
|
652
|
+
uniformDataRef.current = new Float32Array(state.uniformLayout.bufferSize / 4);
|
|
615
653
|
elapsedTimeRef.current = 0;
|
|
616
654
|
lastFrameTimeRef.current = 0;
|
|
617
655
|
animationFrameRef.current = requestAnimationFrame(render);
|
|
@@ -728,6 +766,7 @@ function ReactGpuShader({
|
|
|
728
766
|
className,
|
|
729
767
|
fragment,
|
|
730
768
|
uniforms,
|
|
769
|
+
storageBuffers,
|
|
731
770
|
fullscreen = false,
|
|
732
771
|
timeScale,
|
|
733
772
|
onFrame,
|
|
@@ -748,6 +787,7 @@ function ReactGpuShader({
|
|
|
748
787
|
const { canvasRef } = useWebGPU({
|
|
749
788
|
fragment,
|
|
750
789
|
uniforms,
|
|
790
|
+
storageBuffers,
|
|
751
791
|
onError: handleError,
|
|
752
792
|
timeScale,
|
|
753
793
|
onFrame,
|
|
@@ -999,11 +1039,11 @@ function isVec2Array(value) {
|
|
|
999
1039
|
function isVec3Array(value) {
|
|
1000
1040
|
return Array.isArray(value) && value.length > 0 && Array.isArray(value[0]) && value[0].length === 3;
|
|
1001
1041
|
}
|
|
1002
|
-
function
|
|
1042
|
+
function isVec4Array(value) {
|
|
1003
1043
|
return Array.isArray(value) && value.length > 0 && Array.isArray(value[0]) && value[0].length === 4;
|
|
1004
1044
|
}
|
|
1005
1045
|
function isArrayUniform(value) {
|
|
1006
|
-
return isFloatArray(value) || isVec2Array(value) || isVec3Array(value) ||
|
|
1046
|
+
return isFloatArray(value) || isVec2Array(value) || isVec3Array(value) || isVec4Array(value);
|
|
1007
1047
|
}
|
|
1008
1048
|
function setUniform(gl, location, value) {
|
|
1009
1049
|
if (location === null) {
|
|
@@ -1011,7 +1051,7 @@ function setUniform(gl, location, value) {
|
|
|
1011
1051
|
}
|
|
1012
1052
|
if (typeof value === "number") {
|
|
1013
1053
|
gl.uniform1f(location, value);
|
|
1014
|
-
} else if (
|
|
1054
|
+
} else if (isVec4Array(value)) {
|
|
1015
1055
|
gl.uniform4fv(location, value.flat());
|
|
1016
1056
|
} else if (isVec3Array(value)) {
|
|
1017
1057
|
gl.uniform3fv(location, value.flat());
|
|
@@ -1067,7 +1107,7 @@ function getUniformType(value) {
|
|
|
1067
1107
|
if (typeof value === "number") {
|
|
1068
1108
|
return "float";
|
|
1069
1109
|
}
|
|
1070
|
-
if (
|
|
1110
|
+
if (isVec4Array(value)) {
|
|
1071
1111
|
return `vec4[${MAX_ARRAY_LENGTH}]`;
|
|
1072
1112
|
}
|
|
1073
1113
|
if (isVec3Array(value)) {
|
package/dist/types.d.ts
CHANGED
|
@@ -5,6 +5,8 @@ export type FloatArray = number[];
|
|
|
5
5
|
export type Vec2Array = Vec2[];
|
|
6
6
|
export type Vec3Array = Vec3[];
|
|
7
7
|
export type Vec4Array = Vec4[];
|
|
8
|
+
export type GpuUniformValue = number | Vec2 | Vec3 | Vec4;
|
|
9
|
+
export type GpuStorageBuffers = Record<string, Vec4Array>;
|
|
8
10
|
export type TextureSource = HTMLImageElement | HTMLCanvasElement | HTMLVideoElement | ImageBitmap | ImageData | OffscreenCanvas;
|
|
9
11
|
export type TextureWrap = "repeat" | "clamp" | "mirror";
|
|
10
12
|
export type TextureMinFilter = "nearest" | "linear" | "mipmap";
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;AACnC,MAAM,MAAM,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;AAC3C,MAAM,MAAM,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;AAEnD,MAAM,MAAM,UAAU,GAAG,MAAM,EAAE,CAAA;AACjC,MAAM,MAAM,SAAS,GAAG,IAAI,EAAE,CAAA;AAC9B,MAAM,MAAM,SAAS,GAAG,IAAI,EAAE,CAAA;AAC9B,MAAM,MAAM,SAAS,GAAG,IAAI,EAAE,CAAA;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|