playcanvas
Version:
Open-source WebGL/WebGPU 3D engine for the web
247 lines (246 loc) • 8.82 kB
JavaScript
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
};