Skip to content

Commit 4750831

Browse files
committed
Reuse mapped buffers so we can cache bind groups
1 parent efb288d commit 4750831

1 file changed

Lines changed: 66 additions & 15 deletions

File tree

src/webgpu/p5.RendererWebGPU.js

Lines changed: 66 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ function rendererWebGPU(p5, fn) {
5151
this.uniformBufferAlignment = 256;
5252
this.activeUniformBuffers = [];
5353
this.currentUniformBuffer = undefined;
54+
this.uniformBufferPool = [];
55+
this.resettingUniformBuffers = [];
5456

5557
// Cache for current frame's canvas texture view
5658
this.currentCanvasColorTexture = null;
@@ -660,7 +662,11 @@ function rendererWebGPU(p5, fn) {
660662

661663
shader._groupEntries = groupEntries;
662664
shader._bindGroupLayouts = [...bindGroupLayouts.values()];
665+
// Reuse bind groups if they don't change
663666
shader._cachedBindGroup = {};
667+
// Remember which dynamic buffer we last used, so that we can
668+
// possibly cache bind groups if unchanged
669+
shader._lastDynamicBuffer = {};
664670
shader._pipelineLayout = this.device.createPipelineLayout({
665671
bindGroupLayouts: shader._bindGroupLayouts,
666672
});
@@ -1194,6 +1200,9 @@ function rendererWebGPU(p5, fn) {
11941200
) {
11951201
// We can fit this next block of uniforms into the current active memory chunk
11961202
buffer = this.currentUniformBuffer;
1203+
} else if (this.uniformBufferPool.length > 0) {
1204+
buffer = this.uniformBufferPool.pop();
1205+
this.activeUniformBuffers.push(buffer);
11971206
} else {
11981207
// Kinda arbitrary. Each dynamic offset has to be in groups of 256, but then
11991208
// we can choose how many things we want to be able to fit into a block.
@@ -1208,10 +1217,14 @@ function rendererWebGPU(p5, fn) {
12081217
offset: 0,
12091218
size,
12101219
buffer: this.device.createBuffer({
1211-
size: size,
1212-
usage: GPUBufferUsage.UNIFORM,
1220+
size,
1221+
usage: GPUBufferUsage.MAP_WRITE | GPUBufferUsage.COPY_SRC,
12131222
mappedAtCreation: true,
12141223
}),
1224+
uniformBuffer: this.device.createBuffer({
1225+
size,
1226+
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
1227+
}),
12151228
}
12161229

12171230
buffer.data = new Float32Array(buffer.buffer.getMappedRange());
@@ -1239,10 +1252,14 @@ function rendererWebGPU(p5, fn) {
12391252
bufferGroup.bufferPool.push(bufferGroup.nextBufferPool.pop());
12401253
}
12411254
for (const bufferInfo of bufferGroup.buffersInUse.keys()) {
1242-
bufferGroup.nextBufferPool.push(bufferInfo);
1255+
if (bufferInfo !== bufferGroup.currentBuffer) {
1256+
bufferGroup.nextBufferPool.push(bufferInfo);
1257+
}
12431258
}
1244-
bufferGroup.currentBuffer = null;
12451259
bufferGroup.buffersInUse.clear();
1260+
if (bufferGroup.currentBuffer) {
1261+
bufferGroup.buffersInUse.add(bufferGroup.currentBuffer);
1262+
}
12461263
}
12471264
}
12481265
}
@@ -1256,13 +1273,37 @@ function rendererWebGPU(p5, fn) {
12561273
this._pendingCommandEncoders = [];
12571274
this._hasPendingDraws = false;
12581275

1259-
for (const bufferInfo of this.activeUniformBuffers) {
1260-
bufferInfo.buffer.unmap();
1276+
if (this.activeUniformBuffers.length > 0) {
1277+
const encoder = this.device.createCommandEncoder();
1278+
for (const bufferInfo of this.activeUniformBuffers) {
1279+
bufferInfo.buffer.unmap();
1280+
encoder.copyBufferToBuffer(
1281+
bufferInfo.buffer,
1282+
bufferInfo.uniformBuffer,
1283+
);
1284+
}
1285+
commandsToSubmit.unshift(encoder.finish());
12611286
}
12621287

12631288
// Submit the commands
12641289
this.queue.submit(commandsToSubmit);
12651290

1291+
for (const buf of this.activeUniformBuffers) {
1292+
// buf.buffer = this.device.createBuffer({
1293+
// size: buf.size,
1294+
// usage: GPUBufferUsage.MAP_WRITE | GPUBufferUsage.COPY_SRC,
1295+
// mappedAtCreation: true,
1296+
// });
1297+
buf.offset = 0;
1298+
buf.lastOffset = 0;
1299+
this.resettingUniformBuffers.push(
1300+
buf.buffer.mapAsync(GPUMapMode.WRITE).then(() => {
1301+
buf.data = new Float32Array(buf.buffer.getMappedRange());
1302+
buf.dataView = new DataView(buf.data.buffer);
1303+
return buf;
1304+
})
1305+
)
1306+
}
12661307
this.activeUniformBuffers = [];
12671308
this.currentUniformBuffer = undefined;
12681309

@@ -1339,6 +1380,9 @@ function rendererWebGPU(p5, fn) {
13391380
this._markGeometryBuffersForReturn(geometry);
13401381
}
13411382

1383+
this.uniformBufferPool.push(...(await Promise.all(this.resettingUniformBuffers)));
1384+
this.resettingUniformBuffers = [];
1385+
13421386
// Return all vertex buffers to their pools
13431387
this._returnVertexBuffersToPool();
13441388

@@ -1409,13 +1453,16 @@ function rendererWebGPU(p5, fn) {
14091453
// Bind uniforms into a part of a big dynamic memory block because
14101454
// the group changes often
14111455
const uniformBufferInfo = this._getDynamicUniformBufferFromPool(bufferGroup);
1456+
if (currentShader._lastDynamicBuffer[bufferGroup.cacheKey] !== uniformBufferInfo) {
1457+
currentShader._cachedBindGroup[bufferGroup.group] = undefined;
1458+
currentShader._lastDynamicBuffer[bufferGroup.cacheKey] = uniformBufferInfo;
1459+
}
14121460
this._packUniformGroup(currentShader, bufferGroup.uniforms, uniformBufferInfo);
14131461
uniformBufferInfo.lastOffset = uniformBufferInfo.offset;
14141462
uniformBufferInfo.offset += Math.ceil(bufferGroup.size / this.uniformBufferAlignment) * this.uniformBufferAlignment;
14151463

14161464
// Make a shallow copy so that we keep track of the last offset for this uniform
14171465
this._uniformBuffersForBinding.set(bufferGroup.cacheKey, { ...uniformBufferInfo });
1418-
currentShader._cachedBindGroup[bufferGroup.group] = undefined;
14191466
} else {
14201467
// Bind uniforms to a binding-specific buffer, which may be cached for performance
14211468
let bufferInfo;
@@ -1453,20 +1500,27 @@ function rendererWebGPU(p5, fn) {
14531500
for (const [group, entries] of currentShader._groupEntries) {
14541501
const bgEntries = entries.map(entry => {
14551502
// Check if this is a uniform buffer binding
1456-
const uniformBufferInfo = this._uniformBuffersForBinding.get(group + ',' + entry.binding);
1503+
const uniformBufferInfo = entry.bufferGroup &&
1504+
this._uniformBuffersForBinding.get(entry.bufferGroup.cacheKey);
14571505
if (uniformBufferInfo && entry.bufferGroup) {
14581506
return {
14591507
binding: entry.binding,
14601508
resource: entry.bufferGroup.dynamic
14611509
? {
1462-
buffer: uniformBufferInfo.buffer,
1510+
buffer: uniformBufferInfo.uniformBuffer,
14631511
offset: 0,
14641512
size: Math.ceil(entry.bufferGroup.size / this.uniformBufferAlignment) * this.uniformBufferAlignment,
14651513
}
14661514
: { buffer: uniformBufferInfo.buffer },
14671515
};
14681516
}
14691517

1518+
const key = entry.uniform.group + ',' + entry.uniform.binding;
1519+
if (currentShader.buffersDirty[key]) {
1520+
currentShader._cachedBindGroup[group] = undefined;
1521+
currentShader.buffersDirty[key] = false;
1522+
}
1523+
14701524
return {
14711525
binding: entry.binding,
14721526
resource: entry.uniform.type === 'sampler'
@@ -1483,13 +1537,11 @@ function rendererWebGPU(p5, fn) {
14831537
entries: bgEntries,
14841538
});
14851539
}
1540+
currentShader._cachedBindGroup[group] = bindGroup;
14861541
const dynamicEntryOffsets = entries
14871542
.map(e => e.bufferGroup && this._uniformBuffersForBinding.get(e.bufferGroup.cacheKey))
14881543
.filter(b => b?.dynamic)
14891544
.map(b => b.lastOffset);
1490-
if (dynamicEntryOffsets.length === 0) {
1491-
currentShader._cachedBindGroup[group] = bindGroup;
1492-
}
14931545
passEncoder.setBindGroup(
14941546
group,
14951547
bindGroup,
@@ -1821,10 +1873,9 @@ function rendererWebGPU(p5, fn) {
18211873
if (uniform.isSampler) {
18221874
uniform.texture =
18231875
data instanceof Texture ? data : this.getTexture(data);
1824-
} else {
1825-
shader.buffersDirty = shader.buffersDirty || {};
1826-
shader.buffersDirty[uniform.group + ',' + uniform.binding] = true;
18271876
}
1877+
shader.buffersDirty = shader.buffersDirty || {};
1878+
shader.buffersDirty[uniform.group + ',' + uniform.binding] = true;
18281879
}
18291880

18301881
_updateTexture(uniform, tex) {

0 commit comments

Comments
 (0)