UNPKG

playcanvas

Version:

Open-source WebGL/WebGPU 3D engine for the web

198 lines (197 loc) 6.73 kB
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); class ComputeParameter { constructor() { __publicField(this, "value"); /** @type {ScopeId} */ __publicField(this, "scopeId", null); } } class Compute { /** * Create a compute instance. Note that this is supported on WebGPU only and is a no-op on * other platforms. * * @param {GraphicsDevice} graphicsDevice - * The graphics device. * @param {Shader} shader - The compute shader. * @param {string} [name] - The name of the compute instance, used for debugging only. */ constructor(graphicsDevice, shader, name = "Unnamed") { /** * A compute shader. * * @type {Shader|null} * @ignore */ __publicField(this, "shader", null); /** * The non-unique name of an instance of the class. Defaults to 'Unnamed'. * * @type {string} */ __publicField(this, "name"); /** * @type {Map<string, ComputeParameter>} * @ignore */ __publicField(this, "parameters", /* @__PURE__ */ new Map()); /** @ignore */ __publicField(this, "countX", 1); /** * @type {number|undefined} * @ignore */ __publicField(this, "countY"); /** * @type {number|undefined} * @ignore */ __publicField(this, "countZ"); /** * Slot index in the indirect dispatch buffer, or -1 for direct dispatch. * * @ignore */ __publicField(this, "indirectSlotIndex", -1); /** * Custom buffer for indirect dispatch, or null to use device's built-in buffer. * * @type {StorageBuffer|null} * @ignore */ __publicField(this, "indirectBuffer", null); /** * Frame stamp (device.renderVersion) when indirect slot was set. Used for validation * when using the built-in buffer. * * @ignore */ __publicField(this, "indirectFrameStamp", 0); this.device = graphicsDevice; this.shader = shader; this.name = name; if (graphicsDevice.supportsCompute) { this.impl = graphicsDevice.createComputeImpl(this); } } /** * Sets a shader parameter on a compute instance. * * @param {string} name - The name of the parameter to set. * @param {number|number[]|Float32Array|Texture|StorageBuffer|VertexBuffer|IndexBuffer|TextureView} value - * The value for the specified parameter. */ setParameter(name, value) { let param = this.parameters.get(name); if (!param) { param = new ComputeParameter(); param.scopeId = this.device.scope.resolve(name); this.parameters.set(name, param); } param.value = value; } /** * Returns the value of a shader parameter from the compute instance. * * @param {string} name - The name of the parameter to get. * @returns {number|number[]|Float32Array|Texture|StorageBuffer|VertexBuffer|IndexBuffer|TextureView|undefined} * The value of the specified parameter. */ getParameter(name) { return this.parameters.get(name)?.value; } /** * Deletes a shader parameter from the compute instance. * * @param {string} name - The name of the parameter to delete. */ deleteParameter(name) { this.parameters.delete(name); } /** * Frees resources associated with this compute instance. */ destroy() { this.impl?.destroy(); this.impl = null; } /** * Apply the parameters to the scope. * * @ignore */ applyParameters() { for (const [, param] of this.parameters) { param.scopeId.setValue(param.value); } } /** * Prepare the compute work dispatch. * * @param {number} x - X dimension of the grid of work-groups to dispatch. * @param {number} [y] - Y dimension of the grid of work-groups to dispatch. * @param {number} [z] - Z dimension of the grid of work-groups to dispatch. */ setupDispatch(x, y, z) { this.countX = x; this.countY = y; this.countZ = z; this.indirectSlotIndex = -1; this.indirectBuffer = null; } /** * Prepare the compute work dispatch to use indirect parameters from a buffer. The dispatch * parameters (x, y, z workgroup counts) are read from the buffer at the specified slot index. * * When using the device's built-in buffer (buffer parameter is null), this method must be * called each frame as slots are only valid for the current frame. * * @param {number} slotIndex - Slot index in the indirect dispatch buffer. When using the * device's built-in buffer, obtain this by calling {@link GraphicsDevice#getIndirectDispatchSlot}. * @param {StorageBuffer|null} [buffer] - Optional custom storage buffer containing dispatch * parameters. If not provided, uses the device's built-in {@link GraphicsDevice#indirectDispatchBuffer}. * When providing a custom buffer, the user is responsible for its lifetime and contents. * @example * // Reserve a slot in the indirect dispatch buffer * const slot = device.getIndirectDispatchSlot(); * * // First compute shader writes dispatch parameters to the buffer * prepareCompute.setParameter('indirectBuffer', device.indirectDispatchBuffer); * prepareCompute.setParameter('slot', slot); * prepareCompute.setupDispatch(1, 1, 1); * device.computeDispatch([prepareCompute]); * * // Second compute shader uses indirect dispatch * processCompute.setupIndirectDispatch(slot); * device.computeDispatch([processCompute]); */ setupIndirectDispatch(slotIndex, buffer = null) { this.indirectSlotIndex = slotIndex; this.indirectBuffer = buffer; this.indirectFrameStamp = this.device.renderVersion; } /** * Calculate near-square 2D dispatch dimensions for a given workgroup count, * respecting the WebGPU per-dimension limit. When the count fits within a single * dimension, Y is 1. Otherwise, dimensions are chosen to be roughly square to * minimize wasted padding threads. * * @param {number} count - Total number of workgroups needed. * @param {Vec2} result - Output vector to receive X (x) and Y (y) dimensions. * @param {number} [maxDimension] - Maximum workgroups per dimension. * @returns {Vec2} The result vector with dimensions set. * @ignore */ static calcDispatchSize(count, result, maxDimension = 65535) { if (count <= maxDimension) { return result.set(count, 1); } const y = Math.ceil(count / maxDimension); return result.set(Math.ceil(count / y), y); } } export { Compute };