playcanvas
Version:
Open-source WebGL/WebGPU 3D engine for the web
143 lines (142 loc) • 3.74 kB
JavaScript
import { EventHandler } from "../../core/event-handler.js";
import { platform } from "../../core/platform.js";
import { UnifiedSortWorker } from "./gsplat-unified-sort-worker.js";
import { GSplatSortBinWeights } from "./gsplat-sort-bin-weights.js";
const _neededIds = /* @__PURE__ */ new Set();
class GSplatUnifiedSorter extends EventHandler {
worker;
bufferLength = 0;
availableOrderData = [];
// track how many jobs are in flight
jobsInFlight = 0;
// true if we have new version to process
hasNewVersion = false;
pendingSorted = null;
centersSet = /* @__PURE__ */ new Set();
_destroyed = false;
scene = null;
constructor(scene) {
super();
this.scene = scene ?? null;
const workerSource = `
const GSplatSortBinWeights = ${GSplatSortBinWeights.toString()};
(${UnifiedSortWorker.toString()})()
`;
if (platform.environment === "node") {
this.worker = new Worker(workerSource, {
eval: true
});
this.worker.on("message", this.onSorted.bind(this));
} else {
this.worker = new Worker(URL.createObjectURL(new Blob([workerSource], {
type: "application/javascript"
})));
this.worker.addEventListener("message", this.onSorted.bind(this));
}
}
onSorted(message) {
if (this._destroyed) {
return;
}
const msgData = message.data ?? message;
if (this.scene && msgData.sortTime !== void 0) {
this.scene.fire("gsplat:sorted", msgData.sortTime);
}
const orderData = new Uint32Array(msgData.order);
this.jobsInFlight--;
if (this.pendingSorted) {
this.releaseOrderData(this.pendingSorted.orderData);
}
this.pendingSorted = {
count: msgData.count,
version: msgData.version,
orderData
};
}
applyPendingSorted() {
if (this.pendingSorted) {
const { count, version, orderData } = this.pendingSorted;
this.pendingSorted = null;
this.fire("sorted", count, version, orderData);
this.releaseOrderData(orderData);
}
}
releaseOrderData(orderData) {
if (orderData.length === this.bufferLength) {
this.availableOrderData.push(orderData);
}
}
destroy() {
this._destroyed = true;
this.pendingSorted = null;
this.worker.terminate();
this.worker = null;
}
setCenters(id, centers) {
if (centers) {
if (!this.centersSet.has(id)) {
this.centersSet.add(id);
const centersBuffer = centers.buffer.slice();
this.worker.postMessage({
command: "addCenters",
id,
centers: centersBuffer
}, [centersBuffer]);
}
} else {
if (this.centersSet.has(id)) {
this.centersSet.delete(id);
this.worker.postMessage({
command: "removeCenters",
id
});
}
}
}
updateCentersForSplats(splats) {
for (const splat of splats) {
const id = splat.resource.id;
_neededIds.add(id);
if (!this.centersSet.has(id)) {
this.setCenters(id, splat.resource.centers);
}
}
for (const id of this.centersSet) {
if (!_neededIds.has(id)) {
this.setCenters(id, null);
}
}
_neededIds.clear();
}
setSortParameters(payload) {
this.hasNewVersion = true;
const { textureSize } = payload;
const newLength = textureSize * textureSize;
if (newLength !== this.bufferLength) {
this.bufferLength = newLength;
this.availableOrderData.length = 0;
}
this.worker.postMessage(payload);
}
setSortParams(params, radialSorting) {
if (this.hasNewVersion || this.jobsInFlight === 0) {
let orderData = this.availableOrderData.pop();
if (!orderData) {
orderData = new Uint32Array(this.bufferLength);
}
this.jobsInFlight++;
this.hasNewVersion = false;
this.worker.postMessage({
command: "sort",
sortParams: params,
radialSorting,
order: orderData.buffer
}, [
orderData.buffer
]);
}
}
}
export {
GSplatUnifiedSorter
};