UNPKG

playcanvas

Version:

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

260 lines (257 loc) 9.43 kB
import { PIXELFORMAT_RGBA32F, getWgslShaderType, pixelFormatInfo, SHADERSTAGE_COMPUTE, SAMPLETYPE_FLOAT, PIXELFORMAT_RGBA16F, getGlslShaderType, SAMPLETYPE_UINT, SAMPLETYPE_INT } from '../../platform/graphics/constants.js'; import { hashCode } from '../../core/hash.js'; import { GSPLAT_STREAM_INSTANCE, GSPLAT_STREAM_RESOURCE } 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 gsplatContainerFloatReadVS$1 from '../shader-lib/glsl/chunks/gsplat/vert/formats/containerFloatRead.js'; import gsplatContainerFloatReadVS 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 { get hash() { if (this._hash === undefined) { 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, undefined, 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 = undefined; 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: gsplatContainerFloatReadVS$1, readWGSL: gsplatContainerFloatReadVS }); } static createSimpleFormat(device) { return new GSplatFormat(device, [ { name: 'dataCenter', format: PIXELFORMAT_RGBA32F }, { name: 'dataColor', format: PIXELFORMAT_RGBA16F } ], { readGLSL: glslContainerSimpleRead, readWGSL: wgslContainerSimpleRead }); } constructor(device, streams, options){ this.allowStreamRemoval = false; this._extraStreams = []; this._streamNames = new Set(); this._extraStreamsVersion = 0; this._resourceStreams = null; this._instanceStreams = null; 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; } } export { GSplatFormat };