playcanvas
Version:
Open-source WebGL/WebGPU 3D engine for the web
105 lines (104 loc) • 4.38 kB
JavaScript
var __defProp = Object.defineProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
import { Debug, DebugHelper } from "../../../core/debug.js";
import { BindGroup } from "../bind-group.js";
import { DebugGraphics } from "../debug-graphics.js";
import { UniformBuffer } from "../uniform-buffer.js";
const _indirectDispatchEntryByteSize = 3 * 4;
class WebgpuCompute {
constructor(compute) {
/** @type {UniformBuffer[]} */
__publicField(this, "uniformBuffers", []);
/**
* Bind groups, indexed by bind group index. A caller-provided format occupies group 0;
* auto-reflected resources occupy their own group (0 when no caller format, otherwise 1).
* The array is dense (no gaps), as required by WebGPU pipeline layouts.
*
* @type {BindGroup[]}
*/
__publicField(this, "bindGroups", []);
this.compute = compute;
const { device, shader } = compute;
DebugGraphics.pushGpuMarker(device, `Compute:${compute.name}`);
const {
computeBindGroupFormat,
computeUniformBufferFormats,
computeReflectedBindGroupFormat,
computeReflectedUniformBufferFormat,
computeReflectedGroupIndex
} = shader.impl;
Debug.assert(
!computeUniformBufferFormats || computeBindGroupFormat,
"Compute shader specifies computeUniformBufferFormats but no computeBindGroupFormat to bind them into",
shader
);
const formats = [];
if (computeBindGroupFormat) {
const bindGroup = new BindGroup(device, computeBindGroupFormat);
DebugHelper.setName(bindGroup, `Compute-BindGroup_${bindGroup.id}`);
if (computeUniformBufferFormats) {
for (const name in computeUniformBufferFormats) {
if (computeUniformBufferFormats.hasOwnProperty(name)) {
const ub = new UniformBuffer(device, computeUniformBufferFormats[name], true);
this.uniformBuffers.push(ub);
bindGroup.setUniformBuffer(name, ub);
}
}
}
formats[0] = computeBindGroupFormat;
this.bindGroups[0] = bindGroup;
}
if (computeReflectedBindGroupFormat) {
const reflectedBindGroup = new BindGroup(device, computeReflectedBindGroupFormat);
DebugHelper.setName(reflectedBindGroup, `Compute-ReflectedBindGroup_${reflectedBindGroup.id}`);
if (computeReflectedUniformBufferFormat) {
const ub = new UniformBuffer(device, computeReflectedUniformBufferFormat, true);
this.uniformBuffers.push(ub);
reflectedBindGroup.setUniformBuffer("ub_compute", ub);
}
formats[computeReflectedGroupIndex] = computeReflectedBindGroupFormat;
this.bindGroups[computeReflectedGroupIndex] = reflectedBindGroup;
}
this.pipeline = device.computePipeline.get(shader, formats);
DebugGraphics.popGpuMarker(device);
}
destroy() {
this.uniformBuffers.forEach((ub) => ub.destroy());
this.uniformBuffers.length = 0;
this.bindGroups.forEach((bindGroup) => bindGroup.destroy());
this.bindGroups.length = 0;
}
updateBindGroup() {
for (let i = 0; i < this.bindGroups.length; i++) {
const bindGroup = this.bindGroups[i];
bindGroup.updateUniformBuffers();
bindGroup.update();
}
}
dispatch(x, y, z) {
const device = this.compute.device;
for (let i = 0; i < this.bindGroups.length; i++) {
device.setBindGroup(i, this.bindGroups[i]);
}
const passEncoder = device.passEncoder;
passEncoder.setPipeline(this.pipeline);
const { indirectSlotIndex, indirectBuffer, indirectFrameStamp } = this.compute;
if (indirectSlotIndex >= 0) {
let gpuBuffer;
if (indirectBuffer) {
gpuBuffer = indirectBuffer.impl.buffer;
} else {
Debug.assert(indirectFrameStamp === device.renderVersion, "Indirect dispatch slot must be set each frame using setupIndirectDispatch()");
gpuBuffer = device.indirectDispatchBuffer.impl.buffer;
}
const offset = indirectSlotIndex * _indirectDispatchEntryByteSize;
passEncoder.dispatchWorkgroupsIndirect(gpuBuffer, offset);
} else {
passEncoder.dispatchWorkgroups(x, y, z);
}
}
}
export {
WebgpuCompute
};