p5 2.2.1-rc.0 → 2.2.1
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/dist/accessibility/color_namer.js +5 -6
- package/dist/accessibility/describe.js +4 -26
- package/dist/accessibility/index.js +5 -6
- package/dist/accessibility/outputs.js +6 -38
- package/dist/app.js +5 -6
- package/dist/color/color_conversion.js +5 -6
- package/dist/color/creating_reading.js +1 -1
- package/dist/color/index.js +2 -2
- package/dist/color/p5.Color.js +1 -1
- package/dist/color/setting.js +59 -357
- package/dist/{constants-DEJVKr9Z.js → constants-DQyACdzq.js} +11 -61
- package/dist/core/constants.js +1 -1
- package/dist/core/environment.js +26 -158
- package/dist/core/filterShaders.js +1 -1
- package/dist/core/friendly_errors/fes_core.js +1 -1
- package/dist/core/friendly_errors/file_errors.js +1 -1
- package/dist/core/friendly_errors/index.js +1 -1
- package/dist/core/friendly_errors/param_validator.js +1 -1
- package/dist/core/friendly_errors/sketch_verifier.js +1 -1
- package/dist/core/helpers.js +1 -1
- package/dist/core/init.js +5 -6
- package/dist/core/internationalization.js +1 -1
- package/dist/core/legacy.js +5 -6
- package/dist/core/main.js +5 -6
- package/dist/core/p5.Graphics.js +4 -5
- package/dist/core/p5.Renderer.js +3 -4
- package/dist/core/p5.Renderer2D.js +5 -6
- package/dist/core/p5.Renderer3D.js +4 -5
- package/dist/core/rendering.js +4 -5
- package/dist/core/structure.js +13 -52
- package/dist/core/transform.js +32 -176
- package/dist/{creating_reading-CgHCHxqN.js → creating_reading-ZXzcZEsb.js} +3 -196
- package/dist/data/local_storage.js +4 -30
- package/dist/dom/dom.js +24 -159
- package/dist/dom/index.js +2 -2
- package/dist/dom/p5.Element.js +31 -208
- package/dist/dom/p5.File.js +1 -32
- package/dist/dom/p5.MediaElement.js +10 -113
- package/dist/events/acceleration.js +11 -64
- package/dist/events/keyboard.js +13 -81
- package/dist/events/pointer.js +18 -160
- package/dist/image/const.js +1 -1
- package/dist/image/filterRenderer2D.js +4 -5
- package/dist/image/image.js +4 -5
- package/dist/image/index.js +4 -5
- package/dist/image/loading_displaying.js +4 -5
- package/dist/image/p5.Image.js +3 -4
- package/dist/image/pixels.js +17 -100
- package/dist/io/files.js +4 -5
- package/dist/io/index.js +4 -5
- package/dist/io/p5.Table.js +66 -158
- package/dist/io/p5.TableRow.js +48 -71
- package/dist/io/p5.XML.js +6 -99
- package/dist/io/utilities.js +8 -3
- package/dist/{main-_RXV5Lx8.js → main-DvN69W3f.js} +13 -42
- package/dist/math/Matrices/Matrix.js +87 -126
- package/dist/math/Matrices/MatrixNumjs.js +1 -5
- package/dist/math/calculation.js +10 -112
- package/dist/math/index.js +1 -1
- package/dist/math/math.js +2 -12
- package/dist/math/noise.js +5 -32
- package/dist/math/p5.Matrix.js +3 -3
- package/dist/math/p5.Vector.js +104 -345
- package/dist/math/random.js +5 -32
- package/dist/math/trigonometry.js +15 -105
- package/dist/{p5.Renderer-QoFcvj3f.js → p5.Renderer-D-5LdCRz.js} +25 -178
- package/dist/{rendering-CsICjEXA.js → rendering-h9unX5K0.js} +254 -1156
- package/dist/shape/2d_primitives.js +33 -194
- package/dist/shape/attributes.js +12 -73
- package/dist/shape/curves.js +30 -95
- package/dist/shape/custom_shapes.js +63 -144
- package/dist/shape/index.js +2 -2
- package/dist/shape/vertex.js +21 -106
- package/dist/strands/p5.strands.js +248 -46
- package/dist/type/index.js +3 -4
- package/dist/type/p5.Font.js +4 -49
- package/dist/type/textCore.js +5 -158
- package/dist/utilities/conversion.js +17 -104
- package/dist/utilities/time_date.js +3 -40
- package/dist/utilities/utility_functions.js +6 -48
- package/dist/webgl/3d_primitives.js +4 -5
- package/dist/webgl/GeometryBuilder.js +1 -2
- package/dist/webgl/ShapeBuilder.js +22 -2
- package/dist/webgl/enums.js +1 -1
- package/dist/webgl/index.js +4 -5
- package/dist/webgl/interaction.js +6 -33
- package/dist/webgl/light.js +4 -5
- package/dist/webgl/loading.js +12 -46
- package/dist/webgl/material.js +4 -5
- package/dist/webgl/p5.Camera.js +4 -5
- package/dist/webgl/p5.DataArray.js +0 -4
- package/dist/webgl/p5.Framebuffer.js +4 -5
- package/dist/webgl/p5.Geometry.js +12 -106
- package/dist/webgl/p5.Quat.js +1 -1
- package/dist/webgl/p5.RendererGL.js +7 -18
- package/dist/webgl/p5.Shader.js +12 -36
- package/dist/webgl/p5.Texture.js +4 -5
- package/dist/webgl/text.js +4 -5
- package/dist/webgl/utils.js +4 -5
- package/dist/webgpu/index.js +1 -1
- package/dist/webgpu/p5.RendererWebGPU.js +529 -208
- package/dist/webgpu/shaders/color.js +32 -17
- package/dist/webgpu/shaders/filters/base.js +18 -7
- package/dist/webgpu/shaders/font.js +52 -40
- package/dist/webgpu/shaders/line.js +50 -36
- package/dist/webgpu/shaders/material.js +90 -83
- package/dist/webgpu/strands_wgslBackend.js +5 -2
- package/lib/p5.esm.js +5576 -7811
- package/lib/p5.esm.min.js +1 -1
- package/lib/p5.js +5576 -7811
- package/lib/p5.min.js +1 -1
- package/lib/p5.webgpu.esm.js +786 -453
- package/lib/p5.webgpu.js +786 -453
- package/lib/p5.webgpu.min.js +1 -1
- package/package.json +13 -13
- package/types/global.d.ts +16905 -16783
- package/types/p5.d.ts +11142 -11081
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { W as WEBGPU, T as TRIANGLE_STRIP, L as LIGHTEST, D as DARKEST, S as SUBTRACT, R as REPLACE, E as EXCLUSION, a as SCREEN, M as MULTIPLY, b as REMOVE, A as ADD, B as BLEND, c as TRIANGLES, U as UNSIGNED_BYTE, F as FLOAT, H as HALF_FLOAT, d as UNSIGNED_INT, e as MIRROR, f as REPEAT, C as CLAMP, g as LINEAR, N as NEAREST } from '../constants-
|
|
1
|
+
import { W as WEBGPU, T as TRIANGLE_STRIP, L as LIGHTEST, D as DARKEST, S as SUBTRACT, R as REPLACE, E as EXCLUSION, a as SCREEN, M as MULTIPLY, b as REMOVE, A as ADD, B as BLEND, c as TRIANGLES, U as UNSIGNED_BYTE, F as FLOAT, H as HALF_FLOAT, d as UNSIGNED_INT, e as MIRROR, f as REPEAT, C as CLAMP, g as LINEAR, N as NEAREST } from '../constants-DQyACdzq.js';
|
|
2
2
|
import { getStrokeDefs } from '../webgl/enums.js';
|
|
3
3
|
import { DataType } from '../strands/ir_types.js';
|
|
4
4
|
import { colorVertexShader, colorFragmentShader } from './shaders/color.js';
|
|
@@ -41,10 +41,27 @@ function rendererWebGPU(p5, fn) {
|
|
|
41
41
|
constructor(pInst, w, h, isMainCanvas, elt) {
|
|
42
42
|
super(pInst, w, h, isMainCanvas, elt);
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
// Used to group draws into one big render pass
|
|
45
|
+
this.activeRenderPass = null;
|
|
46
|
+
this.activeRenderPassEncoder = null;
|
|
47
|
+
this.activeShaderOptions = null;
|
|
48
|
+
this.activeShader = null;
|
|
45
49
|
|
|
46
50
|
this.samplers = new Map();
|
|
47
51
|
|
|
52
|
+
// Some uniforms update every frame, like model matrices and sometimes colors.
|
|
53
|
+
// The fastest way to handle these is to use mapped memory. We'll batch those
|
|
54
|
+
// into bigger buffers with dynamic offsets, separate from the usual system
|
|
55
|
+
// where bind groups have their own little buffers that get cached when they
|
|
56
|
+
// are unchanged
|
|
57
|
+
this.uniformBufferAlignment = 256;
|
|
58
|
+
this.activeUniformBuffers = [];
|
|
59
|
+
this.currentUniformBuffer = undefined;
|
|
60
|
+
this.uniformBufferPool = [];
|
|
61
|
+
this.resettingUniformBuffers = [];
|
|
62
|
+
|
|
63
|
+
this.dynamicEntryOffsets = new Uint32Array(64);
|
|
64
|
+
|
|
48
65
|
// Cache for current frame's canvas texture view
|
|
49
66
|
this.currentCanvasColorTexture = null;
|
|
50
67
|
this.currentCanvasColorTextureView = null;
|
|
@@ -208,6 +225,68 @@ function rendererWebGPU(p5, fn) {
|
|
|
208
225
|
return this.currentCanvasColorTextureView;
|
|
209
226
|
}
|
|
210
227
|
|
|
228
|
+
_beginActiveRenderPass() {
|
|
229
|
+
if (this.activeRenderPass) return;
|
|
230
|
+
|
|
231
|
+
// Use framebuffer texture if active, otherwise use canvas texture
|
|
232
|
+
const activeFramebuffer = this.activeFramebuffer();
|
|
233
|
+
|
|
234
|
+
const colorAttachment = {
|
|
235
|
+
view: activeFramebuffer
|
|
236
|
+
? (activeFramebuffer.aaColorTexture
|
|
237
|
+
? activeFramebuffer.aaColorTextureView
|
|
238
|
+
: activeFramebuffer.colorTextureView)
|
|
239
|
+
: this._getCanvasColorTextureView(),
|
|
240
|
+
loadOp: "load",
|
|
241
|
+
storeOp: "store",
|
|
242
|
+
// If using multisampled texture, resolve to non-multisampled texture
|
|
243
|
+
resolveTarget: activeFramebuffer && activeFramebuffer.aaColorTexture
|
|
244
|
+
? activeFramebuffer.colorTextureView
|
|
245
|
+
: undefined,
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
// Use framebuffer depth texture if active, otherwise use canvas depth texture
|
|
249
|
+
const depthTextureView = activeFramebuffer
|
|
250
|
+
? (activeFramebuffer.aaDepthTexture
|
|
251
|
+
? activeFramebuffer.aaDepthTextureView
|
|
252
|
+
: activeFramebuffer.depthTextureView)
|
|
253
|
+
: this.depthTextureView;
|
|
254
|
+
const renderPassDescriptor = {
|
|
255
|
+
colorAttachments: [colorAttachment],
|
|
256
|
+
depthStencilAttachment: depthTextureView
|
|
257
|
+
? {
|
|
258
|
+
view: depthTextureView,
|
|
259
|
+
depthLoadOp: "load",
|
|
260
|
+
depthStoreOp: "store",
|
|
261
|
+
depthClearValue: 1.0,
|
|
262
|
+
stencilLoadOp: "load",
|
|
263
|
+
stencilStoreOp: "store",
|
|
264
|
+
depthReadOnly: false,
|
|
265
|
+
stencilReadOnly: false,
|
|
266
|
+
}
|
|
267
|
+
: undefined,
|
|
268
|
+
};
|
|
269
|
+
const commandEncoder = this.device.createCommandEncoder();
|
|
270
|
+
const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
|
|
271
|
+
this.activeRenderPassEncoder = commandEncoder;
|
|
272
|
+
this.activeRenderPass = passEncoder;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
_finishActiveRenderPass() {
|
|
276
|
+
if (!this.activeRenderPass) return;
|
|
277
|
+
|
|
278
|
+
const commandEncoder = this.activeRenderPassEncoder;
|
|
279
|
+
const passEncoder = this.activeRenderPass;
|
|
280
|
+
passEncoder.end();
|
|
281
|
+
|
|
282
|
+
// Store the command encoder for later submission
|
|
283
|
+
this._pendingCommandEncoders.push(commandEncoder.finish());
|
|
284
|
+
this.activeRenderPassEncoder = null;
|
|
285
|
+
this.activeRenderPass = null;
|
|
286
|
+
this.activeShader = null;
|
|
287
|
+
this.activeShaderOptions = null;
|
|
288
|
+
}
|
|
289
|
+
|
|
211
290
|
clear(...args) {
|
|
212
291
|
const _r = args[0] || 0;
|
|
213
292
|
const _g = args[1] || 0;
|
|
@@ -219,6 +298,8 @@ function rendererWebGPU(p5, fn) {
|
|
|
219
298
|
this._frameState = FRAME_STATE.UNPROMOTED;
|
|
220
299
|
}
|
|
221
300
|
|
|
301
|
+
this._finishActiveRenderPass();
|
|
302
|
+
|
|
222
303
|
const commandEncoder = this.device.createCommandEncoder();
|
|
223
304
|
|
|
224
305
|
// Use framebuffer texture if active, otherwise use canvas texture
|
|
@@ -273,6 +354,7 @@ function rendererWebGPU(p5, fn) {
|
|
|
273
354
|
* occlude anything subsequently drawn.
|
|
274
355
|
*/
|
|
275
356
|
clearDepth(depth = 1) {
|
|
357
|
+
this._finishActiveRenderPass();
|
|
276
358
|
const commandEncoder = this.device.createCommandEncoder();
|
|
277
359
|
|
|
278
360
|
// Use framebuffer texture if active, otherwise use canvas texture
|
|
@@ -363,7 +445,6 @@ function rendererWebGPU(p5, fn) {
|
|
|
363
445
|
const loc = attr.location;
|
|
364
446
|
if (!this.registerEnabled.has(loc)) {
|
|
365
447
|
// TODO
|
|
366
|
-
// this.renderPass.setVertexBuffer(loc, buffer);
|
|
367
448
|
this.registerEnabled.add(loc);
|
|
368
449
|
}
|
|
369
450
|
}
|
|
@@ -446,6 +527,14 @@ function rendererWebGPU(p5, fn) {
|
|
|
446
527
|
}
|
|
447
528
|
}
|
|
448
529
|
|
|
530
|
+
_shaderOptionsDifferent(newOptions) {
|
|
531
|
+
if (!this.activeShaderOptions) return true;
|
|
532
|
+
for (const key in this.activeShaderOptions) {
|
|
533
|
+
if (this.activeShaderOptions[key] !== newOptions[key]) return true;
|
|
534
|
+
}
|
|
535
|
+
return false;
|
|
536
|
+
}
|
|
537
|
+
|
|
449
538
|
_initShader(shader) {
|
|
450
539
|
const device = this.device;
|
|
451
540
|
|
|
@@ -500,39 +589,39 @@ function rendererWebGPU(p5, fn) {
|
|
|
500
589
|
}
|
|
501
590
|
|
|
502
591
|
_finalizeShader(shader) {
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
shader.
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
592
|
+
// Per-group buffer pools. We will pull from these when we draw multiple
|
|
593
|
+
// times using the shader in a render pass. These are per group instead of
|
|
594
|
+
// global so that we can reuse the last used buffer when uniform values
|
|
595
|
+
// don't change.
|
|
596
|
+
shader._uniformBufferGroups = [];
|
|
597
|
+
shader.buffersDirty = new Set();
|
|
598
|
+
|
|
599
|
+
for (const group of shader._uniformGroups) {
|
|
600
|
+
// Calculate the size needed for this group's uniforms
|
|
601
|
+
const groupUniforms = Object.values(group.uniforms);
|
|
602
|
+
const rawSize = Math.max(
|
|
603
|
+
0,
|
|
604
|
+
...groupUniforms.map(u => u.offsetEnd)
|
|
605
|
+
);
|
|
606
|
+
const alignedSize = Math.ceil(rawSize / 16) * 16;
|
|
607
|
+
|
|
608
|
+
shader._uniformBufferGroups.push({
|
|
609
|
+
group: group.group,
|
|
610
|
+
binding: group.binding,
|
|
611
|
+
cacheKey: group.group * 1000 + group.binding,
|
|
612
|
+
varName: group.varName,
|
|
613
|
+
structType: group.structType,
|
|
614
|
+
uniforms: groupUniforms,
|
|
615
|
+
size: alignedSize,
|
|
616
|
+
|
|
617
|
+
bufferPool: [],
|
|
618
|
+
nextBufferPool: [],
|
|
619
|
+
|
|
620
|
+
dynamic: groupUniforms.some(u => u.name.startsWith('uModel')),
|
|
621
|
+
buffersInUse: new Set(),
|
|
622
|
+
currentBuffer: null, // For caching
|
|
623
|
+
});
|
|
624
|
+
}
|
|
536
625
|
|
|
537
626
|
// Register this shader in our registry for pool cleanup
|
|
538
627
|
this._shadersWithPools.push(shader);
|
|
@@ -540,12 +629,22 @@ function rendererWebGPU(p5, fn) {
|
|
|
540
629
|
const bindGroupLayouts = new Map(); // group index -> bindGroupLayout
|
|
541
630
|
const groupEntries = new Map(); // group index -> array of entries
|
|
542
631
|
|
|
543
|
-
//
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
632
|
+
// Add all uniform group bindings to group 0
|
|
633
|
+
const structEntries = new Map();
|
|
634
|
+
for (const bufferGroup of shader._uniformBufferGroups) {
|
|
635
|
+
const entries = structEntries.get(bufferGroup.group) || [];
|
|
636
|
+
entries.push({
|
|
637
|
+
bufferGroup,
|
|
638
|
+
binding: bufferGroup.binding,
|
|
639
|
+
visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT,
|
|
640
|
+
buffer: { type: 'uniform', hasDynamicOffset: bufferGroup.dynamic },
|
|
641
|
+
});
|
|
642
|
+
structEntries.set(bufferGroup.group, entries);
|
|
643
|
+
}
|
|
644
|
+
for (const [group, entries] of structEntries.entries()) {
|
|
645
|
+
entries.sort((a, b) => a.binding - b.binding);
|
|
646
|
+
groupEntries.set(group, entries);
|
|
647
|
+
}
|
|
549
648
|
|
|
550
649
|
// Add the variable amount of samplers and texture bindings that can come after
|
|
551
650
|
for (const sampler of shader.samplers) {
|
|
@@ -567,17 +666,25 @@ function rendererWebGPU(p5, fn) {
|
|
|
567
666
|
uniform: sampler,
|
|
568
667
|
});
|
|
569
668
|
|
|
669
|
+
entries.sort((a, b) => a.binding - b.binding);
|
|
570
670
|
groupEntries.set(group, entries);
|
|
571
671
|
}
|
|
572
672
|
|
|
573
673
|
// Create layouts and bind groups
|
|
674
|
+
const groupEntriesArr = [];
|
|
574
675
|
for (const [group, entries] of groupEntries) {
|
|
575
676
|
const layout = this.device.createBindGroupLayout({ entries });
|
|
576
677
|
bindGroupLayouts.set(group, layout);
|
|
678
|
+
groupEntriesArr.push([group, entries]);
|
|
577
679
|
}
|
|
578
680
|
|
|
579
|
-
shader._groupEntries =
|
|
681
|
+
shader._groupEntries = groupEntriesArr;
|
|
580
682
|
shader._bindGroupLayouts = [...bindGroupLayouts.values()];
|
|
683
|
+
// Reuse bind groups if they don't change
|
|
684
|
+
shader._cachedBindGroup = {};
|
|
685
|
+
// Remember which dynamic buffer we last used, so that we can
|
|
686
|
+
// possibly cache bind groups if unchanged
|
|
687
|
+
shader._lastDynamicBuffer = {};
|
|
581
688
|
shader._pipelineLayout = this.device.createPipelineLayout({
|
|
582
689
|
bindGroupLayouts: shader._bindGroupLayouts,
|
|
583
690
|
});
|
|
@@ -766,22 +873,25 @@ function rendererWebGPU(p5, fn) {
|
|
|
766
873
|
}
|
|
767
874
|
|
|
768
875
|
_getVertexBuffers(shader) {
|
|
769
|
-
|
|
876
|
+
if (!shader._vertexBuffers) {
|
|
877
|
+
const buffers = [];
|
|
770
878
|
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
879
|
+
for (const attrName in shader.attributes) {
|
|
880
|
+
const attr = shader.attributes[attrName];
|
|
881
|
+
if (!attr || attr.location === -1) continue;
|
|
774
882
|
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
883
|
+
// Get the vertex buffer info associated with this attribute
|
|
884
|
+
const renderBuffer =
|
|
885
|
+
this.buffers[shader.shaderType].find(buf => buf.attr === attrName) ||
|
|
886
|
+
this.buffers.user.find(buf => buf.attr === attrName);
|
|
887
|
+
if (!renderBuffer) continue;
|
|
780
888
|
|
|
781
|
-
|
|
889
|
+
buffers.push(renderBuffer);
|
|
890
|
+
}
|
|
891
|
+
shader._vertexBuffers = buffers;
|
|
782
892
|
}
|
|
783
893
|
|
|
784
|
-
return
|
|
894
|
+
return shader._vertexBuffers;
|
|
785
895
|
}
|
|
786
896
|
|
|
787
897
|
_getFormatFromSize(size) {
|
|
@@ -823,6 +933,7 @@ function rendererWebGPU(p5, fn) {
|
|
|
823
933
|
}
|
|
824
934
|
|
|
825
935
|
_resetBuffersBeforeDraw() {
|
|
936
|
+
this._finishActiveRenderPass();
|
|
826
937
|
// Set state to PENDING - we'll decide on first draw
|
|
827
938
|
this._frameState = FRAME_STATE.PENDING;
|
|
828
939
|
|
|
@@ -1076,50 +1187,106 @@ function rendererWebGPU(p5, fn) {
|
|
|
1076
1187
|
// Uniform buffer pool management
|
|
1077
1188
|
//////////////////////////////////////////////
|
|
1078
1189
|
|
|
1079
|
-
_getUniformBufferFromPool(
|
|
1190
|
+
_getUniformBufferFromPool(bufferGroup) {
|
|
1080
1191
|
// Try to get a buffer from the pool
|
|
1081
|
-
if (
|
|
1082
|
-
const bufferInfo =
|
|
1083
|
-
|
|
1192
|
+
if (bufferGroup.bufferPool.length > 0) {
|
|
1193
|
+
const bufferInfo = bufferGroup.bufferPool.pop();
|
|
1194
|
+
bufferGroup.buffersInUse.add(bufferInfo);
|
|
1084
1195
|
return bufferInfo;
|
|
1085
1196
|
}
|
|
1086
1197
|
|
|
1087
1198
|
// No buffers available, create a new one
|
|
1088
1199
|
const newBuffer = this.device.createBuffer({
|
|
1089
|
-
size:
|
|
1200
|
+
size: bufferGroup.size,
|
|
1090
1201
|
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
|
|
1091
1202
|
});
|
|
1092
|
-
const newData = new Float32Array(
|
|
1203
|
+
const newData = new Float32Array(bufferGroup.size / 4);
|
|
1093
1204
|
const newDataView = new DataView(newData.buffer);
|
|
1094
|
-
|
|
1095
1205
|
const bufferInfo = {
|
|
1096
1206
|
buffer: newBuffer,
|
|
1097
1207
|
data: newData,
|
|
1098
1208
|
dataView: newDataView
|
|
1099
1209
|
};
|
|
1100
1210
|
|
|
1101
|
-
|
|
1211
|
+
bufferGroup.buffersInUse.add(bufferInfo);
|
|
1102
1212
|
return bufferInfo;
|
|
1103
1213
|
}
|
|
1104
1214
|
|
|
1215
|
+
_getDynamicUniformBufferFromPool(bufferGroup) {
|
|
1216
|
+
//
|
|
1217
|
+
let buffer;
|
|
1218
|
+
if (
|
|
1219
|
+
this.currentUniformBuffer &&
|
|
1220
|
+
this.currentUniformBuffer.offset + bufferGroup.size < this.currentUniformBuffer.size
|
|
1221
|
+
) {
|
|
1222
|
+
// We can fit this next block of uniforms into the current active memory chunk
|
|
1223
|
+
buffer = this.currentUniformBuffer;
|
|
1224
|
+
} else if (this.uniformBufferPool.length > 0) {
|
|
1225
|
+
buffer = this.uniformBufferPool.pop();
|
|
1226
|
+
this.activeUniformBuffers.push(buffer);
|
|
1227
|
+
} else {
|
|
1228
|
+
// Kinda arbitrary. Each dynamic offset has to be in groups of 256, but then
|
|
1229
|
+
// we can choose how many things we want to be able to fit into a block.
|
|
1230
|
+
// There's some overhead to each block so if we're drawing a lot of stuff,
|
|
1231
|
+
// bigger is better. But it's also a lot of wasted memory if we AREN'T drawing
|
|
1232
|
+
// a lot of stuff. So.... right now it's 40. Feel free to update this if
|
|
1233
|
+
// a better balance can be achieved.
|
|
1234
|
+
const size = 256 * 40;
|
|
1235
|
+
buffer = {
|
|
1236
|
+
dynamic: true,
|
|
1237
|
+
lastOffset: 0,
|
|
1238
|
+
offset: 0,
|
|
1239
|
+
size,
|
|
1240
|
+
buffer: this.device.createBuffer({
|
|
1241
|
+
size,
|
|
1242
|
+
usage: GPUBufferUsage.MAP_WRITE | GPUBufferUsage.COPY_SRC,
|
|
1243
|
+
mappedAtCreation: true,
|
|
1244
|
+
}),
|
|
1245
|
+
uniformBuffer: this.device.createBuffer({
|
|
1246
|
+
size,
|
|
1247
|
+
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
|
|
1248
|
+
}),
|
|
1249
|
+
};
|
|
1250
|
+
|
|
1251
|
+
buffer.data = new Float32Array(buffer.buffer.getMappedRange());
|
|
1252
|
+
buffer.dataView = new DataView(buffer.data.buffer);
|
|
1253
|
+
|
|
1254
|
+
this.activeUniformBuffers.push(buffer);
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
this.currentUniformBuffer = buffer;
|
|
1258
|
+
|
|
1259
|
+
return buffer;
|
|
1260
|
+
}
|
|
1261
|
+
|
|
1105
1262
|
_returnUniformBuffersToPool() {
|
|
1106
1263
|
// Return all used buffers back to their pools for all registered shaders
|
|
1107
1264
|
for (const shader of this._shadersWithPools) {
|
|
1108
|
-
|
|
1109
|
-
this._returnShaderBuffersToPool(shader);
|
|
1110
|
-
}
|
|
1265
|
+
this._returnShaderBuffersToPool(shader);
|
|
1111
1266
|
}
|
|
1112
1267
|
}
|
|
1113
1268
|
|
|
1114
1269
|
_returnShaderBuffersToPool(shader) {
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1270
|
+
if (shader._uniformBufferGroups) {
|
|
1271
|
+
for (const bufferGroup of shader._uniformBufferGroups) {
|
|
1272
|
+
while (bufferGroup.nextBufferPool.length > 0) {
|
|
1273
|
+
bufferGroup.bufferPool.push(bufferGroup.nextBufferPool.pop());
|
|
1274
|
+
}
|
|
1275
|
+
for (const bufferInfo of bufferGroup.buffersInUse.keys()) {
|
|
1276
|
+
if (bufferInfo !== bufferGroup.currentBuffer) {
|
|
1277
|
+
bufferGroup.nextBufferPool.push(bufferInfo);
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1280
|
+
bufferGroup.buffersInUse.clear();
|
|
1281
|
+
if (bufferGroup.currentBuffer) {
|
|
1282
|
+
bufferGroup.buffersInUse.add(bufferGroup.currentBuffer);
|
|
1283
|
+
}
|
|
1284
|
+
}
|
|
1119
1285
|
}
|
|
1120
1286
|
}
|
|
1121
1287
|
|
|
1122
1288
|
flushDraw() {
|
|
1289
|
+
this._finishActiveRenderPass();
|
|
1123
1290
|
// Only submit if we actually had any draws
|
|
1124
1291
|
if (this._hasPendingDraws) {
|
|
1125
1292
|
// Create a copy of pending command encoders
|
|
@@ -1127,9 +1294,41 @@ function rendererWebGPU(p5, fn) {
|
|
|
1127
1294
|
this._pendingCommandEncoders = [];
|
|
1128
1295
|
this._hasPendingDraws = false;
|
|
1129
1296
|
|
|
1297
|
+
if (this.activeUniformBuffers.length > 0) {
|
|
1298
|
+
const encoder = this.device.createCommandEncoder();
|
|
1299
|
+
for (const bufferInfo of this.activeUniformBuffers) {
|
|
1300
|
+
bufferInfo.buffer.unmap();
|
|
1301
|
+
encoder.copyBufferToBuffer(
|
|
1302
|
+
bufferInfo.buffer,
|
|
1303
|
+
bufferInfo.uniformBuffer,
|
|
1304
|
+
);
|
|
1305
|
+
}
|
|
1306
|
+
commandsToSubmit.unshift(encoder.finish());
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1130
1309
|
// Submit the commands
|
|
1131
1310
|
this.queue.submit(commandsToSubmit);
|
|
1132
1311
|
|
|
1312
|
+
for (const buf of this.activeUniformBuffers) {
|
|
1313
|
+
// buf.buffer = this.device.createBuffer({
|
|
1314
|
+
// size: buf.size,
|
|
1315
|
+
// usage: GPUBufferUsage.MAP_WRITE | GPUBufferUsage.COPY_SRC,
|
|
1316
|
+
// mappedAtCreation: true,
|
|
1317
|
+
// });
|
|
1318
|
+
buf.offset = 0;
|
|
1319
|
+
buf.lastOffset = 0;
|
|
1320
|
+
// this.resettingUniformBuffers.push(
|
|
1321
|
+
buf.buffer.mapAsync(GPUMapMode.WRITE).then(() => {
|
|
1322
|
+
buf.data = new Float32Array(buf.buffer.getMappedRange());
|
|
1323
|
+
buf.dataView = new DataView(buf.data.buffer);
|
|
1324
|
+
this.uniformBufferPool.push(buf);
|
|
1325
|
+
return buf;
|
|
1326
|
+
});
|
|
1327
|
+
// )
|
|
1328
|
+
}
|
|
1329
|
+
this.activeUniformBuffers = [];
|
|
1330
|
+
this.currentUniformBuffer = undefined;
|
|
1331
|
+
|
|
1133
1332
|
// Execute post-submit callbacks after GPU work completes
|
|
1134
1333
|
if (this._postSubmitCallbacks.length > 0) {
|
|
1135
1334
|
const callbacks = this._postSubmitCallbacks;
|
|
@@ -1203,15 +1402,21 @@ function rendererWebGPU(p5, fn) {
|
|
|
1203
1402
|
this._markGeometryBuffersForReturn(geometry);
|
|
1204
1403
|
}
|
|
1205
1404
|
|
|
1405
|
+
// this.uniformBufferPool.push(...(await Promise.all(this.resettingUniformBuffers)));
|
|
1406
|
+
this.resettingUniformBuffers = [];
|
|
1407
|
+
|
|
1206
1408
|
// Return all vertex buffers to their pools
|
|
1207
1409
|
this._returnVertexBuffersToPool();
|
|
1208
1410
|
|
|
1209
1411
|
// Destroy all retired buffers
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1412
|
+
const retired = this._retiredBuffers;
|
|
1413
|
+
this._postSubmitCallbacks.push(() => {
|
|
1414
|
+
for (const buffer of retired) {
|
|
1415
|
+
if (buffer && buffer.destroy) {
|
|
1416
|
+
buffer.destroy();
|
|
1417
|
+
}
|
|
1213
1418
|
}
|
|
1214
|
-
}
|
|
1419
|
+
});
|
|
1215
1420
|
this._retiredBuffers = [];
|
|
1216
1421
|
|
|
1217
1422
|
if (this._frameState === FRAME_STATE.PROMOTED) {
|
|
@@ -1239,50 +1444,15 @@ function rendererWebGPU(p5, fn) {
|
|
|
1239
1444
|
this._promoteToFramebufferWithoutCopy();
|
|
1240
1445
|
}
|
|
1241
1446
|
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
// Use framebuffer texture if active, otherwise use canvas texture
|
|
1245
|
-
const activeFramebuffer = this.activeFramebuffer();
|
|
1246
|
-
|
|
1247
|
-
const colorAttachment = {
|
|
1248
|
-
view: activeFramebuffer
|
|
1249
|
-
? (activeFramebuffer.aaColorTexture
|
|
1250
|
-
? activeFramebuffer.aaColorTextureView
|
|
1251
|
-
: activeFramebuffer.colorTextureView)
|
|
1252
|
-
: this._getCanvasColorTextureView(),
|
|
1253
|
-
loadOp: "load",
|
|
1254
|
-
storeOp: "store",
|
|
1255
|
-
// If using multisampled texture, resolve to non-multisampled texture
|
|
1256
|
-
resolveTarget: activeFramebuffer && activeFramebuffer.aaColorTexture
|
|
1257
|
-
? activeFramebuffer.colorTextureView
|
|
1258
|
-
: undefined,
|
|
1259
|
-
};
|
|
1260
|
-
|
|
1261
|
-
// Use framebuffer depth texture if active, otherwise use canvas depth texture
|
|
1262
|
-
const depthTextureView = activeFramebuffer
|
|
1263
|
-
? (activeFramebuffer.aaDepthTexture
|
|
1264
|
-
? activeFramebuffer.aaDepthTextureView
|
|
1265
|
-
: activeFramebuffer.depthTextureView)
|
|
1266
|
-
: this.depthTextureView;
|
|
1267
|
-
const renderPassDescriptor = {
|
|
1268
|
-
colorAttachments: [colorAttachment],
|
|
1269
|
-
depthStencilAttachment: depthTextureView
|
|
1270
|
-
? {
|
|
1271
|
-
view: depthTextureView,
|
|
1272
|
-
depthLoadOp: "load",
|
|
1273
|
-
depthStoreOp: "store",
|
|
1274
|
-
depthClearValue: 1.0,
|
|
1275
|
-
stencilLoadOp: "load",
|
|
1276
|
-
stencilStoreOp: "store",
|
|
1277
|
-
depthReadOnly: false,
|
|
1278
|
-
stencilReadOnly: false,
|
|
1279
|
-
}
|
|
1280
|
-
: undefined,
|
|
1281
|
-
};
|
|
1282
|
-
|
|
1283
|
-
const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
|
|
1447
|
+
this._beginActiveRenderPass();
|
|
1448
|
+
const passEncoder = this.activeRenderPass;
|
|
1284
1449
|
const currentShader = this._curShader;
|
|
1285
|
-
|
|
1450
|
+
const shaderOptions = this._shaderOptions({ mode });
|
|
1451
|
+
if (this.activeShader !== currentShader || this._shaderOptionsDifferent(shaderOptions)) {
|
|
1452
|
+
passEncoder.setPipeline(currentShader.getPipeline(shaderOptions));
|
|
1453
|
+
}
|
|
1454
|
+
this.activeShader = currentShader;
|
|
1455
|
+
this.activeShaderOptions = shaderOptions;
|
|
1286
1456
|
|
|
1287
1457
|
// Set stencil reference value for clipping
|
|
1288
1458
|
const drawTarget = this.drawTarget();
|
|
@@ -1296,52 +1466,125 @@ function rendererWebGPU(p5, fn) {
|
|
|
1296
1466
|
passEncoder.setStencilReference(1);
|
|
1297
1467
|
}
|
|
1298
1468
|
// Bind vertex buffers
|
|
1299
|
-
for (const buffer of this._getVertexBuffers(currentShader)) {
|
|
1469
|
+
for (const buffer of currentShader._vertexBuffers || this._getVertexBuffers(currentShader)) {
|
|
1300
1470
|
const location = currentShader.attributes[buffer.attr].location;
|
|
1301
1471
|
const gpuBuffer = buffers[buffer.dst];
|
|
1302
1472
|
passEncoder.setVertexBuffer(location, gpuBuffer, 0);
|
|
1303
1473
|
}
|
|
1304
|
-
// Bind uniforms - get a buffer from the pool
|
|
1305
|
-
const uniformBufferInfo = this._getUniformBufferFromPool(currentShader);
|
|
1306
|
-
this._packUniforms(currentShader, uniformBufferInfo);
|
|
1307
|
-
this.device.queue.writeBuffer(
|
|
1308
|
-
uniformBufferInfo.buffer,
|
|
1309
|
-
0,
|
|
1310
|
-
uniformBufferInfo.data.buffer,
|
|
1311
|
-
uniformBufferInfo.data.byteOffset,
|
|
1312
|
-
uniformBufferInfo.data.byteLength
|
|
1313
|
-
);
|
|
1314
1474
|
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1475
|
+
for (const bufferGroup of currentShader._uniformBufferGroups) {
|
|
1476
|
+
if (bufferGroup.dynamic) {
|
|
1477
|
+
// Bind uniforms into a part of a big dynamic memory block because
|
|
1478
|
+
// the group changes often
|
|
1479
|
+
const uniformBufferInfo = this._getDynamicUniformBufferFromPool(bufferGroup);
|
|
1480
|
+
if (currentShader._lastDynamicBuffer[bufferGroup.cacheKey] !== uniformBufferInfo) {
|
|
1481
|
+
currentShader._cachedBindGroup[bufferGroup.group] = undefined;
|
|
1482
|
+
currentShader._lastDynamicBuffer[bufferGroup.cacheKey] = uniformBufferInfo;
|
|
1323
1483
|
}
|
|
1484
|
+
this._packUniformGroup(currentShader, bufferGroup.uniforms, uniformBufferInfo);
|
|
1485
|
+
uniformBufferInfo.lastOffset = uniformBufferInfo.offset;
|
|
1486
|
+
uniformBufferInfo.offset += Math.ceil(bufferGroup.size / this.uniformBufferAlignment) * this.uniformBufferAlignment;
|
|
1324
1487
|
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1488
|
+
// Make a shallow copy so that we keep track of the last offset for this uniform
|
|
1489
|
+
bufferGroup.currentDynamicBuffer = uniformBufferInfo;
|
|
1490
|
+
bufferGroup.lastOffset = uniformBufferInfo.lastOffset;
|
|
1491
|
+
} else {
|
|
1492
|
+
// Bind uniforms to a binding-specific buffer, which may be cached for performance
|
|
1493
|
+
let bufferInfo;
|
|
1494
|
+
const dataChanged = this._hasGroupDataChanged(currentShader, bufferGroup);
|
|
1495
|
+
|
|
1496
|
+
if (!dataChanged && bufferGroup.currentBuffer) {
|
|
1497
|
+
// Reuse the cached buffer - no need to pack or write
|
|
1498
|
+
bufferInfo = bufferGroup.currentBuffer;
|
|
1499
|
+
bufferGroup.buffersInUse.add(bufferInfo);
|
|
1500
|
+
} else {
|
|
1501
|
+
// Data changed - get a new buffer and write to it
|
|
1502
|
+
bufferInfo = this._getUniformBufferFromPool(bufferGroup);
|
|
1503
|
+
this._packUniformGroup(currentShader, bufferGroup.uniforms, bufferInfo);
|
|
1504
|
+
this.device.queue.writeBuffer(
|
|
1505
|
+
bufferInfo.buffer,
|
|
1506
|
+
0,
|
|
1507
|
+
bufferInfo.data.buffer,
|
|
1508
|
+
bufferInfo.data.byteOffset,
|
|
1509
|
+
bufferInfo.data.byteLength
|
|
1328
1510
|
);
|
|
1511
|
+
|
|
1512
|
+
currentShader.buffersDirty.delete(bufferGroup.group * 1000 + bufferGroup.binding);
|
|
1513
|
+
currentShader._cachedBindGroup[bufferGroup.group] = undefined;
|
|
1514
|
+
|
|
1515
|
+
// Cache this buffer and data for next frame
|
|
1516
|
+
bufferGroup.currentBuffer = bufferInfo;
|
|
1329
1517
|
}
|
|
1518
|
+
}
|
|
1519
|
+
}
|
|
1520
|
+
for (const sampler of currentShader.samplers) {
|
|
1521
|
+
const key = sampler.group * 1000 + sampler.binding;
|
|
1522
|
+
if (currentShader.buffersDirty.has(key)) {
|
|
1523
|
+
currentShader._cachedBindGroup[sampler.group] = undefined;
|
|
1524
|
+
currentShader.buffersDirty.delete(key);
|
|
1525
|
+
}
|
|
1526
|
+
}
|
|
1330
1527
|
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1528
|
+
// Bind sampler/texture uniforms and uniform buffers
|
|
1529
|
+
for (const iter of currentShader._groupEntries) {
|
|
1530
|
+
const group = iter[0];
|
|
1531
|
+
const entries = iter[1];
|
|
1532
|
+
let dynamicOffsetIdx = 0;
|
|
1533
|
+
const bgEntries = [];
|
|
1534
|
+
let bindGroup = currentShader._cachedBindGroup[group];
|
|
1535
|
+
for (const entry of entries) {
|
|
1536
|
+
const bufferGroup = entry.bufferGroup;
|
|
1537
|
+
// Check if this is a uniform buffer binding
|
|
1538
|
+
const uniformBufferInfo =
|
|
1539
|
+
bufferGroup?.currentBuffer || bufferGroup?.currentDynamicBuffer;
|
|
1540
|
+
if (uniformBufferInfo) {
|
|
1541
|
+
if (bufferGroup.dynamic) {
|
|
1542
|
+
this.dynamicEntryOffsets[dynamicOffsetIdx++] = bufferGroup.lastOffset;
|
|
1543
|
+
}
|
|
1544
|
+
if (!bindGroup) {
|
|
1545
|
+
bgEntries.push({
|
|
1546
|
+
binding: entry.binding,
|
|
1547
|
+
resource: bufferGroup.dynamic
|
|
1548
|
+
? {
|
|
1549
|
+
buffer: uniformBufferInfo.uniformBuffer,
|
|
1550
|
+
offset: 0,
|
|
1551
|
+
size: Math.ceil(bufferGroup.size / this.uniformBufferAlignment) * this.uniformBufferAlignment,
|
|
1552
|
+
}
|
|
1553
|
+
: { buffer: uniformBufferInfo.buffer },
|
|
1554
|
+
});
|
|
1555
|
+
}
|
|
1556
|
+
} else if (!bindGroup) {
|
|
1557
|
+
bgEntries.push({
|
|
1558
|
+
binding: entry.binding,
|
|
1559
|
+
resource: entry.uniform.type === 'sampler'
|
|
1560
|
+
? (entry.uniform.textureSource.texture || this._getEmptyTexture()).getSampler()
|
|
1561
|
+
: (entry.uniform.texture || this._getEmptyTexture()).textureHandle.view,
|
|
1562
|
+
});
|
|
1563
|
+
}
|
|
1564
|
+
}
|
|
1338
1565
|
|
|
1339
1566
|
const layout = currentShader._bindGroupLayouts[group];
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1567
|
+
if (!bindGroup) {
|
|
1568
|
+
bindGroup = this.device.createBindGroup({
|
|
1569
|
+
layout,
|
|
1570
|
+
entries: bgEntries,
|
|
1571
|
+
});
|
|
1572
|
+
}
|
|
1573
|
+
currentShader._cachedBindGroup[group] = bindGroup;
|
|
1574
|
+
if (dynamicOffsetIdx === 0) {
|
|
1575
|
+
passEncoder.setBindGroup(
|
|
1576
|
+
group,
|
|
1577
|
+
bindGroup,
|
|
1578
|
+
);
|
|
1579
|
+
} else {
|
|
1580
|
+
passEncoder.setBindGroup(
|
|
1581
|
+
group,
|
|
1582
|
+
bindGroup,
|
|
1583
|
+
this.dynamicEntryOffsets,
|
|
1584
|
+
0,
|
|
1585
|
+
dynamicOffsetIdx
|
|
1586
|
+
);
|
|
1587
|
+
}
|
|
1345
1588
|
}
|
|
1346
1589
|
|
|
1347
1590
|
if (currentShader.shaderType === "fill") {
|
|
@@ -1366,11 +1609,6 @@ function rendererWebGPU(p5, fn) {
|
|
|
1366
1609
|
passEncoder.draw(geometry.lineVertices.length / 3, count, 0, 0);
|
|
1367
1610
|
}
|
|
1368
1611
|
|
|
1369
|
-
passEncoder.end();
|
|
1370
|
-
|
|
1371
|
-
// Store the command encoder for later submission
|
|
1372
|
-
this._pendingCommandEncoders.push(commandEncoder.finish());
|
|
1373
|
-
|
|
1374
1612
|
// Mark that we have pending draws that need submission
|
|
1375
1613
|
this._hasPendingDraws = true;
|
|
1376
1614
|
}
|
|
@@ -1379,46 +1617,62 @@ function rendererWebGPU(p5, fn) {
|
|
|
1379
1617
|
// SHADER
|
|
1380
1618
|
//////////////////////////////////////////////
|
|
1381
1619
|
|
|
1382
|
-
|
|
1620
|
+
_packUniformGroup(shader, groupUniforms, bufferInfo) {
|
|
1621
|
+
// Pack a single group's uniforms into a buffer
|
|
1383
1622
|
const data = bufferInfo.data;
|
|
1384
1623
|
const dataView = bufferInfo.dataView;
|
|
1385
1624
|
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1625
|
+
const offset = bufferInfo.offset || 0;
|
|
1626
|
+
for (const uniform of groupUniforms) {
|
|
1627
|
+
const fullUniform = shader.uniforms[uniform.name];
|
|
1628
|
+
if (!fullUniform || fullUniform.isSampler) continue;
|
|
1629
|
+
const uniformData = fullUniform._mappedData;
|
|
1389
1630
|
|
|
1390
|
-
if (
|
|
1391
|
-
if (
|
|
1392
|
-
|
|
1393
|
-
dataView.setUint32(uniform.offset, uniform._cachedData, true);
|
|
1631
|
+
if (fullUniform.baseType === 'u32') {
|
|
1632
|
+
if (fullUniform.size === 4) {
|
|
1633
|
+
dataView.setUint32(offset + fullUniform.offset, uniformData, true);
|
|
1394
1634
|
} else {
|
|
1395
|
-
// Vector of u32s
|
|
1396
|
-
const uniformData = uniform._cachedData;
|
|
1397
1635
|
for (let i = 0; i < uniformData.length; i++) {
|
|
1398
|
-
dataView.setUint32(
|
|
1636
|
+
dataView.setUint32(offset + fullUniform.offset + i * 4, uniformData[i], true);
|
|
1399
1637
|
}
|
|
1400
1638
|
}
|
|
1401
|
-
} else if (
|
|
1402
|
-
if (
|
|
1403
|
-
|
|
1404
|
-
dataView.setInt32(uniform.offset, uniform._cachedData, true);
|
|
1639
|
+
} else if (fullUniform.baseType === 'i32') {
|
|
1640
|
+
if (fullUniform.size === 4) {
|
|
1641
|
+
dataView.setInt32(offset + fullUniform.offset, uniformData, true);
|
|
1405
1642
|
} else {
|
|
1406
|
-
// Vector of i32s
|
|
1407
|
-
const uniformData = uniform._cachedData;
|
|
1408
1643
|
for (let i = 0; i < uniformData.length; i++) {
|
|
1409
|
-
dataView.setInt32(
|
|
1644
|
+
dataView.setInt32(offset + fullUniform.offset + i * 4, uniformData[i], true);
|
|
1410
1645
|
}
|
|
1411
1646
|
}
|
|
1412
|
-
} else if (
|
|
1413
|
-
//
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
data
|
|
1647
|
+
} else if (fullUniform.packInPlace) {
|
|
1648
|
+
// In-place packing for mat3: write directly to buffer with padding
|
|
1649
|
+
const baseOffset = (offset + fullUniform.offset) / 4;
|
|
1650
|
+
// Column 0
|
|
1651
|
+
data[baseOffset + 0] = uniformData[0];
|
|
1652
|
+
data[baseOffset + 1] = uniformData[1];
|
|
1653
|
+
data[baseOffset + 2] = uniformData[2];
|
|
1654
|
+
// Column 1
|
|
1655
|
+
data[baseOffset + 4] = uniformData[3];
|
|
1656
|
+
data[baseOffset + 5] = uniformData[4];
|
|
1657
|
+
data[baseOffset + 6] = uniformData[5];
|
|
1658
|
+
// Column 2
|
|
1659
|
+
data[baseOffset + 8] = uniformData[6];
|
|
1660
|
+
data[baseOffset + 9] = uniformData[7];
|
|
1661
|
+
data[baseOffset + 10] = uniformData[8];
|
|
1662
|
+
} else if (fullUniform.size === 4) {
|
|
1663
|
+
data.set([uniformData], (offset + fullUniform.offset) / 4);
|
|
1664
|
+
} else if (uniformData !== undefined) {
|
|
1665
|
+
data.set(uniformData, (offset + fullUniform.offset) / 4);
|
|
1418
1666
|
}
|
|
1419
1667
|
}
|
|
1420
1668
|
}
|
|
1421
1669
|
|
|
1670
|
+
_hasGroupDataChanged(shader, bufferGroup) {
|
|
1671
|
+
// First time
|
|
1672
|
+
if (!bufferGroup.currentBuffer) return true;
|
|
1673
|
+
return shader.buffersDirty.has(bufferGroup.group * 1000 + bufferGroup.binding);
|
|
1674
|
+
}
|
|
1675
|
+
|
|
1422
1676
|
_parseStruct(shaderSource, structName) {
|
|
1423
1677
|
const structMatch = shaderSource.match(
|
|
1424
1678
|
new RegExp(`struct\\s+${structName}\\s*\\{([^\\}]+)\\}`)
|
|
@@ -1462,17 +1716,16 @@ function rendererWebGPU(p5, fn) {
|
|
|
1462
1716
|
const align = dim === 2 ? 8 : 16;
|
|
1463
1717
|
// Each column must be aligned
|
|
1464
1718
|
const size = Math.ceil(dim * 4 / align) * align * dim;
|
|
1719
|
+
// For mat3, use in-place packing to avoid array allocation
|
|
1465
1720
|
const pack = dim === 3
|
|
1466
1721
|
? (data) => [
|
|
1467
1722
|
...data.slice(0, 3),
|
|
1468
|
-
0,
|
|
1469
1723
|
...data.slice(3, 6),
|
|
1470
|
-
0,
|
|
1471
1724
|
...data.slice(6, 9),
|
|
1472
|
-
0
|
|
1473
1725
|
]
|
|
1474
1726
|
: undefined;
|
|
1475
|
-
|
|
1727
|
+
const packInPlace = dim === 3;
|
|
1728
|
+
return { align, size, pack, packInPlace, items: dim * dim, baseType: 'f32' };
|
|
1476
1729
|
}
|
|
1477
1730
|
if (/^array<.+>$/.test(type)) {
|
|
1478
1731
|
const [, subtype, rawLength] = type.match(/^array<(.+),\s*(\d+)>/);
|
|
@@ -1509,7 +1762,7 @@ function rendererWebGPU(p5, fn) {
|
|
|
1509
1762
|
|
|
1510
1763
|
while ((match = elementRegex.exec(structBody)) !== null) {
|
|
1511
1764
|
const [_, location, name, type] = match;
|
|
1512
|
-
const { size, align, pack, baseType } = baseAlignAndSize(type);
|
|
1765
|
+
const { size, align, pack, packInPlace, baseType } = baseAlignAndSize(type);
|
|
1513
1766
|
offset = Math.ceil(offset / align) * align;
|
|
1514
1767
|
const offsetEnd = offset + size;
|
|
1515
1768
|
elements[name] = {
|
|
@@ -1521,6 +1774,7 @@ function rendererWebGPU(p5, fn) {
|
|
|
1521
1774
|
offset,
|
|
1522
1775
|
offsetEnd,
|
|
1523
1776
|
pack,
|
|
1777
|
+
packInPlace,
|
|
1524
1778
|
baseType
|
|
1525
1779
|
};
|
|
1526
1780
|
index++;
|
|
@@ -1546,22 +1800,64 @@ function rendererWebGPU(p5, fn) {
|
|
|
1546
1800
|
}
|
|
1547
1801
|
|
|
1548
1802
|
getUniformMetadata(shader) {
|
|
1549
|
-
//
|
|
1550
|
-
//
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
//
|
|
1554
|
-
|
|
1555
|
-
const
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1803
|
+
// Parse all uniform struct bindings in group 0.
|
|
1804
|
+
// TODO: support non-sampler uniforms being in other groups
|
|
1805
|
+
|
|
1806
|
+
// Each binding represents a logical group of uniforms, since they get
|
|
1807
|
+
// updated or cached all at once.
|
|
1808
|
+
|
|
1809
|
+
const uniformGroups = [];
|
|
1810
|
+
const uniformVarRegex = /@group\((\d+)\)\s+@binding\((\d+)\)\s+var<uniform>\s+(\w+)\s*:\s*(\w+);/g;
|
|
1811
|
+
|
|
1812
|
+
let match;
|
|
1813
|
+
while ((match = uniformVarRegex.exec(shader.vertSrc())) !== null) {
|
|
1814
|
+
const [_, groupNum, binding, varName, structType] = match;
|
|
1815
|
+
const bindingIndex = parseInt(binding);
|
|
1816
|
+
const uniforms = this._parseStruct(shader.vertSrc(), structType);
|
|
1817
|
+
|
|
1818
|
+
uniformGroups.push({
|
|
1819
|
+
group: parseInt(groupNum),
|
|
1820
|
+
binding: bindingIndex,
|
|
1821
|
+
varName,
|
|
1822
|
+
structType,
|
|
1823
|
+
uniforms
|
|
1824
|
+
});
|
|
1825
|
+
}
|
|
1826
|
+
|
|
1827
|
+
if (uniformGroups.length === 0) {
|
|
1828
|
+
throw new Error('Expected at least one uniform struct bound to @group(0)');
|
|
1829
|
+
}
|
|
1830
|
+
|
|
1831
|
+
// While we're also keeping track of the groups, the API we expose
|
|
1832
|
+
// to users of p5 is just a flat list of uniforms (which can be the
|
|
1833
|
+
// individual struct items in the group.)
|
|
1834
|
+
const allUniforms = {};
|
|
1835
|
+
for (const group of uniformGroups) {
|
|
1836
|
+
for (const [uniformName, uniformData] of Object.entries(group.uniforms)) {
|
|
1837
|
+
allUniforms[uniformName] = {
|
|
1838
|
+
...uniformData,
|
|
1839
|
+
group: group.group,
|
|
1840
|
+
binding: group.binding,
|
|
1841
|
+
varName: group.varName
|
|
1842
|
+
};
|
|
1843
|
+
}
|
|
1844
|
+
}
|
|
1845
|
+
|
|
1846
|
+
// Store uniform groups for buffer pooling
|
|
1847
|
+
shader._uniformGroups = uniformGroups;
|
|
1848
|
+
|
|
1561
1849
|
// Extract samplers from group bindings
|
|
1562
1850
|
const samplers = {};
|
|
1563
1851
|
// TODO: support other texture types
|
|
1564
1852
|
const samplerRegex = /@group\((\d+)\)\s*@binding\((\d+)\)\s*var\s+(\w+)\s*:\s*(texture_2d<f32>|sampler);/g;
|
|
1853
|
+
|
|
1854
|
+
// Track which bindings are taken by the struct properties we've parsed
|
|
1855
|
+
// (the rest should be textures/samplers)
|
|
1856
|
+
const structUniformBindings = {};
|
|
1857
|
+
for (const g of uniformGroups) {
|
|
1858
|
+
structUniformBindings[g.group + ',' + g.binding] = true;
|
|
1859
|
+
}
|
|
1860
|
+
|
|
1565
1861
|
for (const [src, visibility] of [
|
|
1566
1862
|
[shader.vertSrc(), GPUShaderStage.VERTEX],
|
|
1567
1863
|
[shader.fragSrc(), GPUShaderStage.FRAGMENT]
|
|
@@ -1571,10 +1867,8 @@ function rendererWebGPU(p5, fn) {
|
|
|
1571
1867
|
const [_, group, binding, name, type] = match;
|
|
1572
1868
|
const groupIndex = parseInt(group);
|
|
1573
1869
|
const bindingIndex = parseInt(binding);
|
|
1574
|
-
//
|
|
1575
|
-
|
|
1576
|
-
// uniforms
|
|
1577
|
-
if (groupIndex === 0 && bindingIndex === 0) continue;
|
|
1870
|
+
// Skip struct uniform bindings which we've already parsed
|
|
1871
|
+
if (structUniformBindings[groupIndex + ',' + bindingIndex]) continue;
|
|
1578
1872
|
|
|
1579
1873
|
const key = `${groupIndex},${bindingIndex}`;
|
|
1580
1874
|
samplers[key] = {
|
|
@@ -1603,17 +1897,17 @@ function rendererWebGPU(p5, fn) {
|
|
|
1603
1897
|
}
|
|
1604
1898
|
}
|
|
1605
1899
|
}
|
|
1606
|
-
return [...Object.values(
|
|
1900
|
+
return [...Object.values(allUniforms).sort((a, b) => a.index - b.index), ...Object.values(samplers)];
|
|
1607
1901
|
}
|
|
1608
1902
|
|
|
1609
|
-
getNextBindingIndex(
|
|
1903
|
+
getNextBindingIndex({ vert, frag }, group = 0) {
|
|
1610
1904
|
// Get the highest binding index in the specified group and return the next available
|
|
1611
1905
|
const samplerRegex = /@group\((\d+)\)\s*@binding\((\d+)\)\s*var\s+(\w+)\s*:\s*(texture_2d<f32>|sampler|uniform)/g;
|
|
1612
1906
|
let maxBindingIndex = -1;
|
|
1613
1907
|
|
|
1614
1908
|
for (const [src, visibility] of [
|
|
1615
|
-
[
|
|
1616
|
-
[
|
|
1909
|
+
[vert, GPUShaderStage.VERTEX],
|
|
1910
|
+
[frag, GPUShaderStage.FRAGMENT]
|
|
1617
1911
|
]) {
|
|
1618
1912
|
let match;
|
|
1619
1913
|
while ((match = samplerRegex.exec(src)) !== null) {
|
|
@@ -1627,11 +1921,14 @@ function rendererWebGPU(p5, fn) {
|
|
|
1627
1921
|
return maxBindingIndex + 1;
|
|
1628
1922
|
}
|
|
1629
1923
|
|
|
1630
|
-
updateUniformValue(
|
|
1924
|
+
updateUniformValue(shader, uniform, data) {
|
|
1631
1925
|
if (uniform.isSampler) {
|
|
1632
1926
|
uniform.texture =
|
|
1633
1927
|
data instanceof Texture ? data : this.getTexture(data);
|
|
1928
|
+
} else {
|
|
1929
|
+
uniform._mappedData = this._mapUniformData(uniform, uniform._cachedData);
|
|
1634
1930
|
}
|
|
1931
|
+
shader.buffersDirty.add(uniform.group * 1000 + uniform.binding);
|
|
1635
1932
|
}
|
|
1636
1933
|
|
|
1637
1934
|
_updateTexture(uniform, tex) {
|
|
@@ -1868,6 +2165,7 @@ function rendererWebGPU(p5, fn) {
|
|
|
1868
2165
|
}
|
|
1869
2166
|
|
|
1870
2167
|
_clearClipBuffer() {
|
|
2168
|
+
this._finishActiveRenderPass();
|
|
1871
2169
|
const commandEncoder = this.device.createCommandEncoder();
|
|
1872
2170
|
|
|
1873
2171
|
const activeFramebuffer = this.activeFramebuffer();
|
|
@@ -1951,12 +2249,33 @@ function rendererWebGPU(p5, fn) {
|
|
|
1951
2249
|
}
|
|
1952
2250
|
}
|
|
1953
2251
|
|
|
1954
|
-
|
|
2252
|
+
// Inject hook uniforms as a separate struct at a new binding
|
|
2253
|
+
let hookUniformFields = '';
|
|
1955
2254
|
for (const key in shader.hooks.uniforms) {
|
|
1956
2255
|
// WGSL format: "name: type"
|
|
1957
|
-
|
|
2256
|
+
hookUniformFields += ` ${key},\n`;
|
|
2257
|
+
}
|
|
2258
|
+
|
|
2259
|
+
if (hookUniformFields) {
|
|
2260
|
+
// Find the next available binding in group 0
|
|
2261
|
+
// Use the source we're currently building (preMain) which has texture bindings. We can't call `fragSrc()`
|
|
2262
|
+
// or `vertSrc()` because we may be in one of those calls already, and might infinite loop
|
|
2263
|
+
const nextBinding = this.getNextBindingIndex({
|
|
2264
|
+
vert: shaderType === 'vertex' ? preMain + (shader.hooks.vertex?.declarations ?? '') + shader.hooks.declarations : shader._vertSrc,
|
|
2265
|
+
frag: shaderType === 'fragment' ? preMain + (shader.hooks.fragment?.declarations ?? '') + shader.hooks.declarations : shader._fragSrc,
|
|
2266
|
+
}, 0);
|
|
2267
|
+
|
|
2268
|
+
// Create HookUniforms struct and binding
|
|
2269
|
+
const hookUniformsDecl = `
|
|
2270
|
+
// Hook Uniforms (from .modify())
|
|
2271
|
+
struct HookUniforms {
|
|
2272
|
+
${hookUniformFields}}
|
|
2273
|
+
|
|
2274
|
+
@group(0) @binding(${nextBinding}) var<uniform> hooks: HookUniforms;
|
|
2275
|
+
`;
|
|
2276
|
+
// Insert before the first @group binding
|
|
2277
|
+
preMain = preMain.replace(/(@group\(0\)\s+@binding)/, `${hookUniformsDecl}\n$1`);
|
|
1958
2278
|
}
|
|
1959
|
-
preMain = preMain.replace(/struct\s+Uniforms\s+\{/, `$&\n${uniforms}`);
|
|
1960
2279
|
|
|
1961
2280
|
// Handle varying variables by injecting them into VertexOutput and FragmentInput structs
|
|
1962
2281
|
if (shader.hooks.varyingVariables && shader.hooks.varyingVariables.length > 0) {
|
|
@@ -2427,6 +2746,7 @@ function rendererWebGPU(p5, fn) {
|
|
|
2427
2746
|
}
|
|
2428
2747
|
|
|
2429
2748
|
_clearFramebufferTextures(framebuffer) {
|
|
2749
|
+
this._finishActiveRenderPass();
|
|
2430
2750
|
const commandEncoder = this.device.createCommandEncoder();
|
|
2431
2751
|
|
|
2432
2752
|
// Clear the color texture (and multisampled texture if it exists)
|
|
@@ -2867,6 +3187,7 @@ function rendererWebGPU(p5, fn) {
|
|
|
2867
3187
|
* Copy framebuffer content directly to WebGPU texture mip level
|
|
2868
3188
|
*/
|
|
2869
3189
|
_accumulateMipLevel(framebuffer, mipmapData, mipLevel, width, height) {
|
|
3190
|
+
this.flushDraw();
|
|
2870
3191
|
// Copy from framebuffer texture to the mip level
|
|
2871
3192
|
const commandEncoder = this.device.createCommandEncoder();
|
|
2872
3193
|
|