playcanvas
Version:
Open-source WebGL/WebGPU 3D engine for the web
181 lines (180 loc) • 6.71 kB
JavaScript
import { hashCode } from "../../core/hash.js";
import { SEMANTIC_POSITION, getGlslShaderType } from "../../platform/graphics/constants.js";
import { BlendState } from "../../platform/graphics/blend-state.js";
import { RenderTarget } from "../../platform/graphics/render-target.js";
import { QuadRender } from "../../scene/graphics/quad-render.js";
import { RenderPassShaderQuad } from "../../scene/graphics/render-pass-shader-quad.js";
import { ShaderUtils } from "../../scene/shader-lib/shader-utils.js";
import { GSPLAT_STREAM_INSTANCE } from "../../scene/constants.js";
import glslGsplatProcess from "../../scene/shader-lib/glsl/chunks/gsplat/frag/gsplatProcess.js";
import wgslGsplatProcess from "../../scene/shader-lib/wgsl/chunks/gsplat/frag/gsplatProcess.js";
class GSplatProcessor {
_device;
_source;
_destination;
_srcResource;
_dstResource;
_dstStreamDescriptors;
_dstStreamNames;
_useAllInputStreams;
_srcTextures = [];
_renderTarget = null;
_quadRender = null;
_renderPass = null;
_parameters = /* @__PURE__ */ new Map();
blendState = BlendState.NOBLEND;
constructor(device, source, destination, options) {
this._device = device;
this._source = source;
this._destination = destination;
this._srcResource = source.resource ?? source.component?.resource;
this._dstResource = destination.resource ?? destination.component?.resource;
this._dstStreamDescriptors = [];
this._dstStreamNames = /* @__PURE__ */ new Set();
for (const streamName of destination.streams) {
const stream = this._dstResource.format.getStream(streamName);
if (stream) {
this._dstStreamDescriptors.push(stream);
this._dstStreamNames.add(stream.name);
}
}
this._useAllInputStreams = !source.streams?.length;
const srcFormat = this._srcResource.format;
const srcStreams = this._useAllInputStreams ? [...srcFormat.streams, ...srcFormat.extraStreams] : source.streams.map((name) => ({ name }));
for (const stream of srcStreams) {
const texture = this._resolveTexture(source, stream.name, this._srcResource);
this._srcTextures.push({ name: stream.name, texture });
}
this._createRenderTarget();
this._createShader(options);
this._renderPass = new RenderPassShaderQuad(device);
this._renderPass.quadRender = this._quadRender;
this._renderPass.init(this._renderTarget);
this._renderPass.colorOps.clear = false;
this._renderPass.depthStencilOps.clearDepth = false;
}
destroy() {
this._renderTarget?.destroy();
this._renderTarget = null;
this._quadRender?.destroy();
this._quadRender = null;
this._renderPass?.destroy();
this._renderPass = null;
this._parameters.clear();
}
_resolveTexture(binding, name, resource) {
if (binding.component) {
const stream = resource.format.getStream(name);
if (stream?.storage === GSPLAT_STREAM_INSTANCE) {
const texture2 = binding.component.getInstanceTexture(name);
if (texture2) {
return texture2;
}
}
}
const texture = resource.getTexture(name);
if (!texture) {
}
return texture;
}
_createRenderTarget() {
const colorBuffers = [];
for (const stream of this._dstStreamDescriptors) {
const texture = this._resolveTexture(this._destination, stream.name, this._dstResource);
if (texture) {
colorBuffers.push(texture);
} else {
}
}
if (colorBuffers.length > 0) {
this._renderTarget = new RenderTarget({
name: "GSplatProcessor-MRT",
colorBuffers,
depth: false,
flipY: true
});
}
}
_createShader(options) {
const { processGLSL = "", processWGSL = "" } = options;
const device = this._device;
const srcFormat = this._srcResource.format;
const dstFormat = this._dstResource.format;
let inputDeclarations = "";
let readCode = "";
if (this._useAllInputStreams) {
const allStreams = [...srcFormat.streams, ...srcFormat.extraStreams];
const sameResource = this._srcResource === this._dstResource;
const inputStreamNames = allStreams.filter((s) => !sameResource || !this._dstStreamNames.has(s.name)).map((s) => s.name);
inputDeclarations = srcFormat.getInputDeclarations(inputStreamNames);
readCode = srcFormat.getReadCode();
} else {
inputDeclarations = srcFormat.getInputDeclarations(this._source.streams);
}
const outputDeclarations = dstFormat.getOutputDeclarations(this._dstStreamDescriptors);
const fragmentOutputTypes = this._dstStreamDescriptors.map((stream) => {
const info = getGlslShaderType(stream.format);
return info.returnType;
});
const defines = /* @__PURE__ */ new Map();
this._srcResource.configureMaterialDefines(defines);
defines.set("SH_BANDS", "0");
const isWebGPU = device.isWebGPU;
const includes = /* @__PURE__ */ new Map();
includes.set("gsplatProcessInputVS", inputDeclarations);
includes.set("gsplatProcessOutputVS", outputDeclarations);
includes.set("gsplatProcessReadVS", readCode);
includes.set("gsplatProcessChunk", isWebGPU ? processWGSL : processGLSL);
const hash = hashCode([
isWebGPU ? processWGSL : processGLSL,
this._useAllInputStreams ? "1" : "0"
].join("|"));
const outputStreams = this._dstStreamDescriptors.map((s) => s.name).join(",");
const shader = ShaderUtils.createShader(device, {
uniqueName: `GSplatProcessor:${srcFormat.hash};${hash};out=${outputStreams}`,
attributes: { vertex_position: SEMANTIC_POSITION },
vertexDefines: defines,
fragmentDefines: defines,
vertexChunk: "fullscreenQuadVS",
fragmentGLSL: glslGsplatProcess,
fragmentWGSL: wgslGsplatProcess,
fragmentIncludes: includes,
fragmentOutputTypes
});
this._quadRender = new QuadRender(shader);
}
setParameter(name, data) {
const scopeId = this._device.scope.resolve(name);
this._parameters.set(name, { scopeId, data });
}
getParameter(name) {
return this._parameters.get(name)?.data;
}
deleteParameter(name) {
this._parameters.delete(name);
}
process() {
if (!this._renderPass) {
return;
}
const device = this._device;
for (const { name, texture } of this._srcTextures) {
device.scope.resolve(name).setValue(texture);
}
device.scope.resolve("splatTextureSize").setValue(this._srcResource.textureDimensions.x);
device.scope.resolve("dstTextureSize").setValue(this._dstResource.textureDimensions.x);
device.scope.resolve("srcNumSplats").setValue(this._srcResource.numSplats);
device.scope.resolve("dstNumSplats").setValue(this._dstResource.numSplats);
for (const [name, value] of this._srcResource.parameters) {
device.scope.resolve(name).setValue(value);
}
for (const [, param] of this._parameters) {
param.scopeId.setValue(param.data);
}
this._renderPass.blendState = this.blendState;
this._renderPass.render();
}
}
export {
GSplatProcessor
};