gpu-curtains
Version:
gpu-curtains is a 3D WebGPU rendering engine. It can be used as a standalone 3D engine, but also includes extra classes focused on mapping 3d objects to DOM elements; It allows users to synchronize values such as position, sizing, or scale between them.
155 lines (152 loc) • 6.43 kB
JavaScript
import { isRenderer } from '../../core/renderers/utils.mjs';
import { Buffer } from '../../core/buffers/Buffer.mjs';
import { generateUUID } from '../../utils/utils.mjs';
var __typeError = (msg) => {
throw TypeError(msg);
};
var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
var _IndirectBuffer_instances, createIndirectBuffer_fn, addGeometryToIndirectMappedBuffer_fn;
const indirectBufferEntrySize = 5;
class IndirectBuffer {
/**
* IndirectBuffer constructor.
* @param renderer - {@link Renderer} or {@link GPUCurtains} class object used to create this {@link IndirectBuffer}.
* @param parameters - {@link IndirectBufferParams | parameters} used to create this {@link IndirectBuffer}.
*/
constructor(renderer, { label = "Indirect buffer", geometries = [], minEntrySize = indirectBufferEntrySize } = {}) {
__privateAdd(this, _IndirectBuffer_instances);
this.type = "IndirectBuffer";
this.setRenderer(renderer);
this.uuid = generateUUID();
this.options = {
label,
geometries,
minEntrySize
};
this.geometries = /* @__PURE__ */ new Map();
this.buffer = null;
this.addGeometries(geometries);
this.renderer.indirectBuffers.set(this.uuid, this);
}
/**
* Set or reset this {@link IndirectBuffer} {@link IndirectBuffer.renderer | renderer}.
* @param renderer - New {@link Renderer} or {@link GPUCurtains} instance to use.
*/
setRenderer(renderer) {
renderer = isRenderer(renderer, this.type);
this.renderer = renderer;
}
/**
* Get the number of unique {@link Geometry} and {@link IndexedGeometry} added to this {@link IndirectBuffer}.
* @returns - Number of unique {@link Geometry} and {@link IndexedGeometry} added to this {@link IndirectBuffer}.
* @readonly
*/
get size() {
return this.geometries.size;
}
/**
* Add multiple {@link Geometry} or {@link IndexedGeometry} to this {@link IndirectBuffer}.
* @param geometries - Array of {@link Geometry} or {@link IndexedGeometry} to add to this {@link IndirectBuffer}.
*/
addGeometries(geometries = []) {
geometries.forEach((geometry) => this.addGeometry(geometry));
}
/**
* Add a {@link Geometry} or {@link IndexedGeometry} to this {@link IndirectBuffer}.
* @param geometry - A {@link Geometry} or {@link IndexedGeometry} to add to this {@link IndirectBuffer}.
*/
addGeometry(geometry) {
this.geometries.set(geometry.uuid, geometry);
}
/**
* Get the byte offset in the {@link buffer} at a given index.
* @param index - Index to get the byte offset from.
* @returns - Byte offset in the {@link buffer} at a given index.
*/
getByteOffsetAtIndex(index = 0) {
return index * this.options.minEntrySize * Uint32Array.BYTES_PER_ELEMENT;
}
/**
* Create the {@link buffer} as soon as the {@link core/renderers/GPURenderer.GPURenderer#device | device} is ready.
*/
create() {
if (this.renderer.ready) {
__privateMethod(this, _IndirectBuffer_instances, createIndirectBuffer_fn).call(this);
} else {
const taskId = this.renderer.onBeforeCommandEncoderCreation.add(
() => {
if (this.renderer.device) {
this.renderer.onBeforeCommandEncoderCreation.remove(taskId);
__privateMethod(this, _IndirectBuffer_instances, createIndirectBuffer_fn).call(this);
}
},
{ once: false }
);
}
}
/**
* Destroy this {@link IndirectBuffer}. Reset all {@link geometries} {@link Geometry#indirectDraw | indirectDraw} properties and destroy the {@link Buffer}.
*/
destroy() {
this.renderer.removeBuffer(this.buffer);
this.renderer.indirectBuffers.delete(this.uuid);
this.geometries.forEach((geometry) => geometry.indirectDraw = null);
this.buffer?.destroy();
this.buffer = null;
this.geometries = null;
}
}
_IndirectBuffer_instances = new WeakSet();
/**
* Create the {@link buffer} (or destroy it if it already exists) with the right size, create its {@link GPUBuffer} in a mapped state, add all {@link geometries} attributes to the mapped buffer and tell the {@link geometries} to use this {@link IndirectBuffer}.
* @private
*/
createIndirectBuffer_fn = function() {
const size = this.getByteOffsetAtIndex(this.geometries.size);
if (this.buffer) {
this.buffer.destroy();
this.buffer.options.size = size;
} else {
this.buffer = new Buffer({
label: this.options.label,
size,
usage: ["copyDst", "indirect", "storage"],
mappedAtCreation: true
});
}
this.buffer.consumers.add(this.uuid);
this.buffer.createBuffer(this.renderer);
const indirectMappedBuffer = new Uint32Array(this.buffer.GPUBuffer.getMappedRange());
let offset = 0;
this.geometries.forEach((geometry) => {
__privateMethod(this, _IndirectBuffer_instances, addGeometryToIndirectMappedBuffer_fn).call(this, geometry, indirectMappedBuffer, offset * this.options.minEntrySize);
geometry.useIndirectBuffer({ buffer: this.buffer, offset: this.getByteOffsetAtIndex(offset) });
offset++;
});
this.buffer.GPUBuffer.unmap();
};
/**
* Add a {@link Geometry} or {@link IndexedGeometry} attributes to the {@link buffer} mapped array buffer.
* @param geometry - {@link Geometry} or {@link IndexedGeometry} to add the attributes from
* @param mappedBuffer - The {@link buffer} mapped array buffer
* @param index - Index in the {@link buffer} mapped array buffer at which to add the attributes.
* @private
*/
addGeometryToIndirectMappedBuffer_fn = function(geometry, mappedBuffer, index = 0) {
if ("indexBuffer" in geometry && geometry.indexBuffer) {
mappedBuffer[index] = geometry.indexBuffer.bufferLength;
mappedBuffer[index + 1] = geometry.instancesCount;
mappedBuffer[index + 2] = 0;
mappedBuffer[index + 3] = 0;
mappedBuffer[index + 4] = 0;
} else {
mappedBuffer[index] = geometry.verticesCount;
mappedBuffer[index + 1] = geometry.instancesCount;
mappedBuffer[index + 2] = 0;
mappedBuffer[index + 3] = 0;
mappedBuffer[index + 4] = 0;
}
};
export { IndirectBuffer };