This commit is contained in:
parent
6bc125be1c
commit
ed5a4379db
76 changed files with 1418 additions and 988 deletions
|
|
@ -2,14 +2,16 @@ import { vec2 } from 'gl-matrix';
|
|||
|
||||
import { createBindGroupCache } from '../../../utils/graphics/bind-group-cache';
|
||||
import { smartCompile } from '../../../utils/graphics/smart-compile';
|
||||
import { dispatchAgentWorkgroups } from '../agent-dispatch';
|
||||
import {
|
||||
dispatchAgentWorkgroups,
|
||||
getAgentWorkgroupSize,
|
||||
substituteAgentWorkgroupSize,
|
||||
} from '../agent-dispatch';
|
||||
import { AGENT_SIZE_IN_BYTES, getMaxSupportedAgentCount } from '../agent-limits';
|
||||
import compactionShader from './agent-compaction.wgsl?raw';
|
||||
import resizeShader from './agent-resize.wgsl?raw';
|
||||
import agentSchema from './agent-schema.wgsl?raw';
|
||||
|
||||
export { AGENT_FLOAT_COUNT } from '../agent-limits';
|
||||
|
||||
export class AgentGenerationPipeline {
|
||||
private static readonly UNIFORM_COUNT = 4;
|
||||
private static readonly COUNTER_COUNT = 1;
|
||||
|
|
@ -34,6 +36,7 @@ export class AgentGenerationPipeline {
|
|||
private readonly resizePipeline: GPUComputePipeline;
|
||||
private readonly compactionPipeline: GPUComputePipeline;
|
||||
private readonly clearCompactedTailPipeline: GPUComputePipeline;
|
||||
private readonly workgroupSize: number;
|
||||
|
||||
private activeAgentsBuffer: GPUBuffer;
|
||||
private inactiveAgentsBuffer: GPUBuffer;
|
||||
|
|
@ -90,7 +93,7 @@ export class AgentGenerationPipeline {
|
|||
});
|
||||
|
||||
this.activeAgentsBuffer = this.createAgentsBuffer();
|
||||
this.inactiveAgentsBuffer = this.createInactivePlaceholderBuffer();
|
||||
this.inactiveAgentsBuffer = this.createAgentsBuffer();
|
||||
|
||||
this.countersBuffer = this.device.createBuffer({
|
||||
size: AgentGenerationPipeline.COUNTER_COUNT * Uint32Array.BYTES_PER_ELEMENT,
|
||||
|
|
@ -107,17 +110,20 @@ export class AgentGenerationPipeline {
|
|||
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
|
||||
});
|
||||
|
||||
this.workgroupSize = getAgentWorkgroupSize(device);
|
||||
const sizedSchema = substituteAgentWorkgroupSize(device, agentSchema);
|
||||
|
||||
this.resizePipeline = device.createComputePipeline({
|
||||
layout: device.createPipelineLayout({
|
||||
bindGroupLayouts: [emptyBindGroupLayout, this.bindGroupLayout],
|
||||
}),
|
||||
compute: {
|
||||
module: smartCompile(device, agentSchema, resizeShader),
|
||||
module: smartCompile(device, sizedSchema, resizeShader),
|
||||
entryPoint: 'main',
|
||||
},
|
||||
});
|
||||
|
||||
const compactionModule = smartCompile(device, agentSchema, compactionShader);
|
||||
const compactionModule = smartCompile(device, sizedSchema, compactionShader);
|
||||
|
||||
this.compactionPipeline = device.createComputePipeline({
|
||||
layout: device.createPipelineLayout({
|
||||
|
|
@ -151,16 +157,6 @@ export class AgentGenerationPipeline {
|
|||
});
|
||||
}
|
||||
|
||||
// The inactive slot only needs a real allocation during compaction. The rest of
|
||||
// the time we keep a one-agent placeholder so the bind group at binding 3 stays
|
||||
// valid for resize without holding a second N-agent buffer in GPU memory.
|
||||
private createInactivePlaceholderBuffer(): GPUBuffer {
|
||||
return this.device.createBuffer({
|
||||
size: AGENT_SIZE_IN_BYTES,
|
||||
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST | GPUBufferUsage.COPY_SRC,
|
||||
});
|
||||
}
|
||||
|
||||
public get maxAgentCount(): number {
|
||||
return this.allocatedMaxAgentCount;
|
||||
}
|
||||
|
|
@ -187,9 +183,11 @@ export class AgentGenerationPipeline {
|
|||
)
|
||||
);
|
||||
const previousActiveAgentsBuffer = this.activeAgentsBuffer;
|
||||
const previousInactiveAgentsBuffer = this.inactiveAgentsBuffer;
|
||||
const previousMaxAgentCount = this.allocatedMaxAgentCount;
|
||||
this.allocatedMaxAgentCount = nextMaxAgentCount;
|
||||
this.activeAgentsBuffer = this.createAgentsBuffer();
|
||||
this.inactiveAgentsBuffer = this.createAgentsBuffer();
|
||||
|
||||
const copyAgentCount = Math.min(
|
||||
Math.max(0, Math.floor(activeAgentCount)),
|
||||
|
|
@ -209,10 +207,9 @@ export class AgentGenerationPipeline {
|
|||
}
|
||||
|
||||
// GPUBuffer.destroy() defers actual freeing until pending submissions
|
||||
// finish, so calling it synchronously after submit is safe and avoids the
|
||||
// transient 4-buffers-live spike that pushes iOS Safari past its per-tab
|
||||
// memory ceiling.
|
||||
// finish, so calling it synchronously after submit is safe.
|
||||
previousActiveAgentsBuffer.destroy();
|
||||
previousInactiveAgentsBuffer.destroy();
|
||||
return this.allocatedMaxAgentCount;
|
||||
}
|
||||
|
||||
|
|
@ -251,7 +248,7 @@ export class AgentGenerationPipeline {
|
|||
const passEncoder = commandEncoder.beginComputePass();
|
||||
passEncoder.setPipeline(this.resizePipeline);
|
||||
passEncoder.setBindGroup(1, this.getBindGroup());
|
||||
dispatchAgentWorkgroups(passEncoder, agentCount);
|
||||
dispatchAgentWorkgroups(passEncoder, this.workgroupSize, agentCount);
|
||||
passEncoder.end();
|
||||
|
||||
this.device.queue.submit([commandEncoder.finish()]);
|
||||
|
|
@ -262,12 +259,6 @@ export class AgentGenerationPipeline {
|
|||
return 0;
|
||||
}
|
||||
|
||||
// Stash the placeholder, swap in a real N-agent destination buffer just
|
||||
// for this compaction so the rest of the time we only carry one full
|
||||
// agent buffer in memory.
|
||||
const placeholder = this.inactiveAgentsBuffer;
|
||||
this.inactiveAgentsBuffer = this.createAgentsBuffer();
|
||||
|
||||
this.agentCountUniformValues[0] = agentCount;
|
||||
this.device.queue.writeBuffer(this.uniforms, 0, this.agentCountUniformValues);
|
||||
|
||||
|
|
@ -276,10 +267,11 @@ export class AgentGenerationPipeline {
|
|||
const passEncoder = commandEncoder.beginComputePass();
|
||||
passEncoder.setPipeline(this.compactionPipeline);
|
||||
passEncoder.setBindGroup(1, this.getBindGroup());
|
||||
dispatchAgentWorkgroups(passEncoder, agentCount);
|
||||
dispatchAgentWorkgroups(passEncoder, this.workgroupSize, agentCount);
|
||||
passEncoder.setPipeline(this.clearCompactedTailPipeline);
|
||||
dispatchAgentWorkgroups(
|
||||
passEncoder,
|
||||
this.workgroupSize,
|
||||
Math.ceil(agentCount / AgentGenerationPipeline.CLEAR_COMPACTED_TAIL_STRIDE)
|
||||
);
|
||||
passEncoder.end();
|
||||
|
|
@ -295,13 +287,6 @@ export class AgentGenerationPipeline {
|
|||
this.device.queue.submit([commandEncoder.finish()]);
|
||||
this.swapAgentBuffers();
|
||||
|
||||
// After swap, inactive is the previous active (full size). Destroy it and
|
||||
// restore the placeholder; the destroy is deferred by WebGPU until the
|
||||
// submitted compaction work has finished.
|
||||
const previousActiveAgentsBuffer = this.inactiveAgentsBuffer;
|
||||
this.inactiveAgentsBuffer = placeholder;
|
||||
previousActiveAgentsBuffer.destroy();
|
||||
|
||||
await this.countersStagingBuffer.mapAsync(GPUMapMode.READ);
|
||||
const compactedCount = new Uint32Array(
|
||||
this.countersStagingBuffer.getMappedRange(),
|
||||
|
|
|
|||
Loading…
Reference in a new issue