UNPKG

playcanvas

Version:

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

143 lines (142 loc) 3.74 kB
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 };