UNPKG

playcanvas

Version:

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

247 lines (246 loc) 8.82 kB
import { getGlslShaderType, getWgslShaderType, pixelFormatInfo, PIXELFORMAT_RGBA16F, PIXELFORMAT_RGBA32F, SAMPLETYPE_FLOAT, SAMPLETYPE_INT, SAMPLETYPE_UINT, SHADERSTAGE_COMPUTE } from "../../platform/graphics/constants.js"; import { hashCode } from "../../core/hash.js"; import { GSPLAT_STREAM_RESOURCE, GSPLAT_STREAM_INSTANCE } from "../constants.js"; import { BindTextureFormat } from "../../platform/graphics/bind-group-format.js"; import glslStreamDecl from "../shader-lib/glsl/chunks/gsplat/vert/gsplatStreamDecl.js"; import wgslStreamDecl from "../shader-lib/wgsl/chunks/gsplat/vert/gsplatStreamDecl.js"; import wgslComputeStreamDecl from "../shader-lib/wgsl/chunks/gsplat/vert/gsplatComputeStreamDecl.js"; import glslStreamOutput from "../shader-lib/glsl/chunks/gsplat/vert/gsplatStreamOutput.js"; import wgslStreamOutput from "../shader-lib/wgsl/chunks/gsplat/vert/gsplatStreamOutput.js"; import glslContainerFloatRead from "../shader-lib/glsl/chunks/gsplat/vert/formats/containerFloatRead.js"; import wgslContainerFloatRead from "../shader-lib/wgsl/chunks/gsplat/vert/formats/containerFloatRead.js"; import glslContainerSimpleRead from "../shader-lib/glsl/chunks/gsplat/vert/formats/containerSimpleRead.js"; import wgslContainerSimpleRead from "../shader-lib/wgsl/chunks/gsplat/vert/formats/containerSimpleRead.js"; const serializeStreams = (streams) => streams.map((s) => `${s.name}:${s.format}:${s.storage}`).join(","); const RE_NAME = /\{name\}/g; const RE_SAMPLER = /\{sampler\}/g; const RE_TEXTURE_TYPE = /\{textureType\}/g; const RE_RETURN_TYPE = /\{returnType\}/g; const RE_FUNC_NAME = /\{funcName\}/g; const RE_BINDING = /\{binding\}/g; const RE_INDEX = /\{index\}/g; const RE_COLOR_SLOT = /\{colorSlot\}/g; const RE_DEFINE_GUARD = /\{defineGuard\}/g; class GSplatFormat { _device; streams; _read; allowStreamRemoval = false; _extraStreams = []; _streamNames = /* @__PURE__ */ new Set(); _extraStreamsVersion = 0; _hash; _resourceStreams = null; _instanceStreams = null; constructor(device, streams, options) { this._device = device; this.streams = [...streams]; this._streamNames = new Set(this.streams.map((s) => s.name)); const isWebGPU = device.isWebGPU; this._read = isWebGPU ? options.readWGSL : options.readGLSL; } get hash() { if (this._hash === void 0) { const streamsStr = serializeStreams(this.streams); const extraStr = serializeStreams(this._extraStreams); this._hash = hashCode( streamsStr + extraStr + this._read ); } return this._hash; } get extraStreamsVersion() { return this._extraStreamsVersion; } get extraStreams() { return this._extraStreams; } get resourceStreams() { if (this._resourceStreams === null) { this._resourceStreams = [ ...this.streams.filter((s) => s.storage !== GSPLAT_STREAM_INSTANCE), ...this._extraStreams.filter((s) => s.storage !== GSPLAT_STREAM_INSTANCE) ]; } return this._resourceStreams; } get instanceStreams() { if (this._instanceStreams === null) { this._instanceStreams = this._extraStreams.filter((s) => s.storage === GSPLAT_STREAM_INSTANCE); } return this._instanceStreams; } addExtraStreams(streams) { if (!streams || streams.length === 0) return; let added = false; for (const s of streams) { if (this._streamNames.has(s.name)) { continue; } this._extraStreams.push({ name: s.name, format: s.format, storage: s.storage ?? GSPLAT_STREAM_RESOURCE }); this._streamNames.add(s.name); added = true; } if (added) { this._extraStreamsVersion++; this._invalidateCaches(); } } removeExtraStreams(names) { if (!this.allowStreamRemoval) { return; } let removed = false; for (const name of names) { const idx = this._extraStreams.findIndex((s) => s.name === name); if (idx !== -1) { this._extraStreams.splice(idx, 1); this._streamNames.delete(name); removed = true; } } if (removed) { this._extraStreamsVersion++; this._invalidateCaches(); } } getInputDeclarations(streamNames) { const isWebGPU = this._device.isWebGPU; const template = isWebGPU ? wgslStreamDecl : glslStreamDecl; const getShaderType = isWebGPU ? getWgslShaderType : getGlslShaderType; const lines = []; let streams = [...this.streams, ...this._extraStreams]; if (streamNames) { streams = streams.filter((s) => streamNames.includes(s.name)); } for (const stream of streams) { const info = getShaderType(stream.format); const funcName = stream.name.charAt(0).toUpperCase() + stream.name.slice(1); let textureType = info.textureType ?? ""; if (isWebGPU && stream.format === PIXELFORMAT_RGBA32F) { textureType = "texture_2d<uff>"; } const decl = template.replace(RE_NAME, stream.name).replace(RE_SAMPLER, info.sampler ?? "").replace(RE_TEXTURE_TYPE, textureType).replace(RE_RETURN_TYPE, info.returnType).replace(RE_FUNC_NAME, funcName); lines.push(decl); } return lines.join("\n"); } getReadCode() { return this._read; } getComputeInputDeclarations(startBinding, streamNames) { const lines = []; let streams = [...this.streams, ...this._extraStreams]; if (streamNames) { streams = streams.filter((s) => streamNames.includes(s.name)); } for (let i = 0; i < streams.length; i++) { const stream = streams[i]; const info = getWgslShaderType(stream.format); const funcName = stream.name.charAt(0).toUpperCase() + stream.name.slice(1); let textureType = info.textureType ?? ""; if (stream.format === PIXELFORMAT_RGBA32F) { textureType = "texture_2d<uff>"; } const decl = wgslComputeStreamDecl.replace(RE_BINDING, String(startBinding + i)).replace(RE_NAME, stream.name).replace(RE_TEXTURE_TYPE, textureType).replace(RE_RETURN_TYPE, info.returnType).replace(RE_FUNC_NAME, funcName); lines.push(decl); } return lines.join("\n"); } getComputeBindFormats(streamNames) { let streams = [...this.streams, ...this._extraStreams]; if (streamNames) { streams = streams.filter((s) => streamNames.includes(s.name)); } return streams.map((stream) => { const info = pixelFormatInfo.get(stream.format); let sampleType = SAMPLETYPE_FLOAT; if (info?.isUint) sampleType = SAMPLETYPE_UINT; else if (info?.isInt) sampleType = SAMPLETYPE_INT; return new BindTextureFormat(stream.name, SHADERSTAGE_COMPUTE, void 0, sampleType, false); }); } setWriteCode(writeGLSL, writeWGSL) { this._write = this._device.isWebGPU ? writeWGSL : writeGLSL; } getWriteCode() { return this._write; } getOutputDeclarations(outputStreams) { const isWebGPU = this._device.isWebGPU; const lines = []; const template = isWebGPU ? wgslStreamOutput : glslStreamOutput; const getShaderType = isWebGPU ? getWgslShaderType : getGlslShaderType; for (let i = 0; i < outputStreams.length; i++) { const stream = outputStreams[i]; const info = getShaderType(stream.format); const funcName = stream.name.charAt(0).toUpperCase() + stream.name.slice(1); const colorSlot = i === 0 ? "color" : `color${i}`; const decl = template.replace(RE_FUNC_NAME, funcName).replace(RE_RETURN_TYPE, info.returnType).replace(RE_INDEX, String(i)).replace(RE_COLOR_SLOT, colorSlot).replace(RE_DEFINE_GUARD, "1"); lines.push(decl); } return lines.join("\n"); } getOutputStubs(streams) { const isWebGPU = this._device.isWebGPU; const lines = []; const template = isWebGPU ? wgslStreamOutput : glslStreamOutput; const getShaderType = isWebGPU ? getWgslShaderType : getGlslShaderType; for (const stream of streams) { const info = getShaderType(stream.format); const funcName = stream.name.charAt(0).toUpperCase() + stream.name.slice(1); const stub = template.replace(RE_FUNC_NAME, funcName).replace(RE_RETURN_TYPE, info.returnType).replace(RE_DEFINE_GUARD, "0"); lines.push(stub); } return lines.join("\n"); } getStream(name) { let stream = this.streams.find((s) => s.name === name); if (!stream) { stream = this._extraStreams.find((s) => s.name === name); } return stream; } _invalidateCaches() { this._hash = void 0; this._resourceStreams = null; this._instanceStreams = null; } static createDefaultFormat(device) { return new GSplatFormat(device, [ { name: "dataColor", format: PIXELFORMAT_RGBA16F }, { name: "dataCenter", format: PIXELFORMAT_RGBA32F }, { name: "dataScale", format: PIXELFORMAT_RGBA16F }, { name: "dataRotation", format: PIXELFORMAT_RGBA16F } ], { readGLSL: glslContainerFloatRead, readWGSL: wgslContainerFloatRead }); } static createSimpleFormat(device) { return new GSplatFormat(device, [ { name: "dataCenter", format: PIXELFORMAT_RGBA32F }, { name: "dataColor", format: PIXELFORMAT_RGBA16F } ], { readGLSL: glslContainerSimpleRead, readWGSL: wgslContainerSimpleRead }); } } export { GSplatFormat };