UNPKG

@itwin/core-frontend

Version:
942 lines • 52.3 kB
/*--------------------------------------------------------------------------------------------- * Copyright (c) Bentley Systems, Incorporated. All rights reserved. * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ /** @packageDocumentation * @module WebGL */ var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) { if (value !== null && value !== void 0) { if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected."); var dispose, inner; if (async) { if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined."); dispose = value[Symbol.asyncDispose]; } if (dispose === void 0) { if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined."); dispose = value[Symbol.dispose]; if (async) inner = dispose; } if (typeof dispose !== "function") throw new TypeError("Object not disposable."); if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } }; env.stack.push({ value: value, dispose: dispose, async: async }); } else if (async) { env.stack.push({ async: true }); } return value; }; var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) { return function (env) { function fail(e) { env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e; env.hasError = true; } var r, s = 0; function next() { while (r = env.stack.pop()) { try { if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next); if (r.dispose) { var result = r.dispose.call(r.value); if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); }); } else s |= 1; } catch (e) { fail(e); } } if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve(); if (env.hasError) throw env.error; } return next(); }; })(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) { var e = new Error(message); return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e; }); import { assert, dispose } from "@itwin/core-bentley"; import { BlurType } from "./CachedGeometry"; import { createClippingProgram } from "./ClippingProgram"; import { createAmbientOcclusionProgram } from "./glsl/AmbientOcclusion"; import { createBlurProgram } from "./glsl/Blur"; import { createEDLCalcBasicProgram, createEDLCalcFullProgram, createEDLFilterProgram, createEDLMixProgram } from "./glsl/EDL"; import { createClearPickAndColorProgram } from "./glsl/ClearPickAndColor"; import { createClearTranslucentProgram } from "./glsl/ClearTranslucent"; import { createCombine3TexturesProgram } from "./glsl/Combine3Textures"; import { createCombineTexturesProgram } from "./glsl/CombineTextures"; import { addEyeSpace, addFrustum, addShaderFlags } from "./glsl/Common"; import { createCompositeProgram } from "./glsl/Composite"; import { createCopyColorProgram } from "./glsl/CopyColor"; import { createCopyPickBuffersProgram } from "./glsl/CopyPickBuffers"; import { createVolClassBlendProgram, createVolClassColorUsingStencilProgram, createVolClassCopyZProgram, createVolClassSetBlendProgram, } from "./glsl/CopyStencil"; import { createEdgeBuilder } from "./glsl/Edge"; import { createEVSMProgram } from "./glsl/EVSMFromDepth"; import { addFeatureId, addFeatureSymbology, addRenderOrder, addUniformFeatureSymbology, mixFeatureColor } from "./glsl/FeatureSymbology"; import { addFragColorWithPreMultipliedAlpha, addPickBufferOutputs } from "./glsl/Fragment"; import { addLogDepth } from "./glsl/LogarithmicDepthBuffer"; import { addUnlitMonochrome } from "./glsl/Monochrome"; import createPlanarGridProgram from "./glsl/PlanarGrid"; import { createPointCloudBuilder, createPointCloudHiliter } from "./glsl/PointCloud"; import { createPointStringBuilder, createPointStringHiliter } from "./glsl/PointString"; import { createPolylineBuilder, createPolylineHiliter } from "./glsl/Polyline"; import { addColorOverrideMix, createClassifierRealityMeshHiliter, createRealityMeshBuilder, createRealityMeshHiliter, } from "./glsl/RealityMesh"; import { createSkyBoxProgram } from "./glsl/SkyBox"; import { createSkySphereBuilder } from "./glsl/SkySphere"; import { createSurfaceBuilder, createSurfaceHiliter } from "./glsl/Surface"; import { addTranslucency } from "./glsl/Translucency"; import { addModelViewMatrix } from "./glsl/Vertex"; import { ShaderProgramExecutor } from "./ShaderProgram"; import { System } from "./System"; import { TechniqueFlags, } from "./TechniqueFlags"; import { computeCompositeTechniqueId } from "./TechniqueId"; /** A rendering technique implemented using a single shader program, typically for some specialized purpose. * @internal */ export class SingularTechnique { program; // Note: Technique assumes ownership of a program constructor(program) { this.program = program; } getShader(_flags) { return this.program; } getShaderByIndex(_index) { return this.program; } getShaderCount() { return 1; } compileShaders() { return this.program.compile() === 0 /* CompileStatus.Success */; } get isDisposed() { return this.program.isDisposed; } [Symbol.dispose]() { this.program[Symbol.dispose](); } /** @deprecated in 5.0 - will not be removed until after 2026-06-13. Use [Symbol.dispose] instead. */ dispose() { this[Symbol.dispose](); } } function numFeatureVariants(numBaseShaders) { return numBaseShaders * 3; } const numHiliteVariants = 2; // instanced and non-instanced. const featureModes = [0 /* FeatureMode.None */, 1 /* FeatureMode.Pick */, 2 /* FeatureMode.Overrides */]; const scratchTechniqueFlags = new TechniqueFlags(); const scratchHiliteFlags = new TechniqueFlags(); /** A rendering technique implemented using multiple shader programs, selected based on TechniqueFlags. * @internal */ export class VariedTechnique { _basicPrograms = []; _clippingPrograms = []; /** TechniqueFlags identifying shader programs for which the fragment shader writes depth but does not contain any discards. * Buggy Intel HD 620/630 drivers incorrectly apply early-Z optimization in this case; we must insert a never-executed * conditional discard to prevent that. */ _earlyZFlags = []; compileShaders() { let allCompiled = true; for (const program of this._basicPrograms) { if (program.compile() !== 0 /* CompileStatus.Success */) allCompiled = false; } for (const clipper of this._clippingPrograms) if (!clipper.compile()) allCompiled = false; return allCompiled; } finishConstruction() { this._earlyZFlags.length = 0; // Confirm no empty entries in our array. let emptyShaderIndex = -1; assert(-1 === (emptyShaderIndex = this._basicPrograms.findIndex((prog) => undefined === prog)), `Shader index ${emptyShaderIndex} is undefined in ${this.constructor.name}`); } _isDisposed = false; get isDisposed() { return this._isDisposed; } [Symbol.dispose]() { if (this._isDisposed) return; for (const program of this._basicPrograms) { assert(undefined !== program); program[Symbol.dispose](); } this._basicPrograms.length = 0; for (const clipShaderObj of this._clippingPrograms) { assert(undefined !== clipShaderObj); clipShaderObj[Symbol.dispose](); } this._clippingPrograms.length = 0; this._isDisposed = true; } /** @deprecated in 5.0 - will not be removed until after 2026-06-13. Use [Symbol.dispose] instead. */ dispose() { this[Symbol.dispose](); } constructor(numPrograms) { this._basicPrograms.length = numPrograms; } addShader(builder, flags, gl) { const descr = `${this._debugDescription}: ${flags.buildDescription()}`; builder.setDebugDescription(descr); if (System.instance.supportsLogZBuffer) { addLogDepth(builder); assert(!builder.frag.requiresEarlyZWorkaround); if (System.instance.fragDepthDoesNotDisableEarlyZ) builder.frag.requiresEarlyZWorkaround = -1 !== this._earlyZFlags.findIndex((x) => x.equals(flags)); } const index = this.getShaderIndex(flags); this.addProgram(builder, index, gl); assert(!builder.frag.requiresEarlyZWorkaround); } addProgram(builder, index, gl) { assert(this._basicPrograms[index] === undefined); this._basicPrograms[index] = builder.buildProgram(gl); assert(this._basicPrograms[index] !== undefined); // Clipping programs always include a discard, so never require workaround. builder.frag.requiresEarlyZWorkaround = false; assert(this._clippingPrograms[index] === undefined); this._clippingPrograms[index] = createClippingProgram(builder); assert(this._clippingPrograms[index] !== undefined); } addHiliteShader(gl, instanced, classified, posType, create) { const builder = create(instanced, classified, posType); scratchHiliteFlags.initForHilite(0, instanced, classified, posType); this.addShader(builder, scratchHiliteFlags, gl); } addTranslucentShader(builder, flags, gl) { flags.isTranslucent = true; addTranslucency(builder); this.addShader(builder, flags, gl); } addFeatureId(builder, feat) { const frag = builder.frag; if (0 /* FeatureMode.None */ === feat) { addFragColorWithPreMultipliedAlpha(frag); } else { const vert = builder.vert; addFrustum(builder); addEyeSpace(builder); addModelViewMatrix(vert); addRenderOrder(frag); addFeatureId(builder, false); addPickBufferOutputs(frag); } } getShaderIndex(flags) { assert(!flags.isHilite || (!flags.isTranslucent && (flags.isClassified === 1 /* IsClassified.Yes */ || flags.hasFeatures)), "invalid technique flags"); const index = this.computeShaderIndex(flags); assert(index < this._basicPrograms.length, "shader index out of bounds"); return index; } getShader(flags) { const index = this.getShaderIndex(flags); let program; if (flags.hasClip) { const entry = this._clippingPrograms[index]; assert(undefined !== entry); program = entry.getProgram(flags.numClipPlanes); } if (program === undefined) program = this._basicPrograms[index]; return program; } // NB: Will ignore clipping shaders. getShaderByIndex(index) { return this._basicPrograms[index]; } // NB: Will ignore clipping shaders. getShaderCount() { return this._basicPrograms.length; } /** For tests. */ forEachProgram(func) { for (const basic of this._basicPrograms) func(basic); for (const clip of this._clippingPrograms) { const prog = clip.getProgram(1); assert(undefined !== prog); func(prog); } } } const positionTypes = ["quantized", "unquantized"]; class SurfaceTechnique extends VariedTechnique { static _kOpaque = 0; static _kTranslucent = 1; static _kInstanced = 2; static _kAnimated = 4; static _kWiremesh = 8; static _kShadowable = 16; static _kThematic = 32; static _kFeature = 48; static _kEdgeTestNeeded = SurfaceTechnique._kFeature * 3; // only when hasFeatures static _kHilite = SurfaceTechnique._kEdgeTestNeeded + SurfaceTechnique._kFeature * 2; // Classifiers are never animated or instanced. They do support shadows, thematic display, and translucency. // There are 3 base variations - 1 per feature mode - each with translucent/shadowed/thematic variants; plus 1 for hilite. static _kClassified = SurfaceTechnique._kHilite + numHiliteVariants; // 3 base classified variations - 1 per feature mode. // Plus thematic variant of each and shadowable variant of each = 9 // Plus translucent variant of each of those = 18 // Plus 1 hilite shader = 19 static _kUnquantized = SurfaceTechnique._kClassified + 19; constructor(gl) { super(SurfaceTechnique._kUnquantized * 2); this._earlyZFlags = [ TechniqueFlags.fromDescription("Opaque-Hilite-Overrides"), TechniqueFlags.fromDescription("Opaque-Instanced-Hilite-Overrides"), TechniqueFlags.fromDescription("Opaque-Hilite-Classified"), TechniqueFlags.fromDescription("Unquantized-Opaque-Hilite-Overrides"), TechniqueFlags.fromDescription("Unquantized-Opaque-Instanced-Hilite-Overrides"), TechniqueFlags.fromDescription("Unquantized-Opaque-Hilite-Classified"), ]; const flags = scratchTechniqueFlags; for (const posType of positionTypes) { for (let instanced = 0 /* IsInstanced.No */; instanced <= 1 /* IsInstanced.Yes */; instanced++) { this.addHiliteShader(gl, instanced, 0 /* IsClassified.No */, posType, createSurfaceHiliter); for (let iAnimate = 0 /* IsAnimated.No */; iAnimate <= 1 /* IsAnimated.Yes */; iAnimate++) { for (let shadowable = 0 /* IsShadowable.No */; shadowable <= 1 /* IsShadowable.Yes */; shadowable++) { for (let wiremesh = 0 /* IsWiremesh.No */; wiremesh <= 1 /* IsWiremesh.Yes */; wiremesh++) { for (let thematic = 0 /* IsThematic.No */; thematic <= 1 /* IsThematic.Yes */; thematic++) { for (let edgeTestNeeded = 0 /* IsEdgeTestNeeded.No */; edgeTestNeeded <= 1 /* IsEdgeTestNeeded.Yes */; edgeTestNeeded++) { for (const featureMode of featureModes) { for (let iTranslucent = 0; iTranslucent <= 1; iTranslucent++) { if (0 /* FeatureMode.None */ !== featureMode || 0 /* IsEdgeTestNeeded.No */ === edgeTestNeeded) { if (1 /* IsThematic.Yes */ === thematic && 1 /* IsShadowable.Yes */ === shadowable) continue; // currently this combination is disallowed. flags.reset(featureMode, instanced, shadowable, thematic, posType); flags.isAnimated = iAnimate; flags.isEdgeTestNeeded = edgeTestNeeded; flags.isTranslucent = 1 === iTranslucent; flags.isWiremesh = wiremesh; const builder = createSurfaceBuilder(flags); this.addShader(builder, flags, gl); } } } } } } } } } } this.addHiliteShader(gl, 0 /* IsInstanced.No */, 1 /* IsClassified.Yes */, "quantized", createSurfaceHiliter); this.addHiliteShader(gl, 0 /* IsInstanced.No */, 1 /* IsClassified.Yes */, "unquantized", createSurfaceHiliter); for (const posType of positionTypes) { for (let translucent = 0; translucent < 2; translucent++) { for (let shadowable = 0 /* IsShadowable.No */; shadowable <= 1 /* IsShadowable.Yes */; shadowable++) { for (let thematic = 0 /* IsThematic.No */; thematic <= 1 /* IsThematic.Yes */; thematic++) { for (const featureMode of featureModes) { if (1 /* IsThematic.Yes */ === thematic && 1 /* IsShadowable.Yes */ === shadowable) continue; // currently this combination is disallowed. flags.reset(featureMode, 0 /* IsInstanced.No */, shadowable, thematic, posType); flags.isClassified = 1 /* IsClassified.Yes */; flags.isTranslucent = (0 !== translucent); const builder = createSurfaceBuilder(flags); if (flags.isTranslucent) addTranslucency(builder); this.addShader(builder, flags, gl); } } } } } this.finishConstruction(); } get _debugDescription() { return "Surface"; } computeShaderIndex(flags) { assert(!(flags.isThematic && flags.isShadowable)); const idxOffset = flags.positionType === "unquantized" ? SurfaceTechnique._kUnquantized : 0; if (flags.isClassified) { assert(!flags.isAnimated); assert(!flags.isInstanced); assert(!flags.isEdgeTestNeeded); // First classified shader is for hilite if (flags.isHilite) return SurfaceTechnique._kClassified + idxOffset; // The rest are organized in 3 groups of 6 - one group per feature mode. // Each group contains opaque, translucent, opaque+thematic, translucent+thematic, opaque+shadowable, and translucent+shadowable variants. let baseIndex = SurfaceTechnique._kClassified + 1; if (flags.isTranslucent) baseIndex += 1; if (flags.isShadowable) baseIndex += 2; if (flags.isThematic) baseIndex += 4; const featureOffset = 6 * flags.featureMode; return baseIndex + featureOffset + idxOffset; } else if (flags.isHilite) { assert(flags.hasFeatures); return SurfaceTechnique._kHilite + flags.isInstanced + idxOffset; } assert(flags.hasFeatures || flags.isEdgeTestNeeded === 0 /* IsEdgeTestNeeded.No */); let index = flags.isTranslucent ? SurfaceTechnique._kTranslucent : SurfaceTechnique._kOpaque; index += SurfaceTechnique._kInstanced * flags.isInstanced; index += SurfaceTechnique._kAnimated * flags.isAnimated; index += SurfaceTechnique._kShadowable * flags.isShadowable; index += SurfaceTechnique._kThematic * flags.isThematic; index += SurfaceTechnique._kWiremesh * flags.isWiremesh; if (flags.isEdgeTestNeeded) index += SurfaceTechnique._kEdgeTestNeeded + (flags.featureMode - 1) * SurfaceTechnique._kFeature; else index += SurfaceTechnique._kFeature * flags.featureMode; return index + idxOffset; } } class PolylineTechnique extends VariedTechnique { static _kOpaque = 0; static _kTranslucent = 1; static _kInstanced = 2; static _kFeature = 4; static _kHilite = numFeatureVariants(PolylineTechnique._kFeature); static _kUnquantized = PolylineTechnique._kHilite + numHiliteVariants; constructor(gl) { super(PolylineTechnique._kUnquantized * 2); this._earlyZFlags = [ TechniqueFlags.fromDescription("Opaque-Hilite-Overrides"), TechniqueFlags.fromDescription("Opaque-Instanced-Hilite-Overrides"), TechniqueFlags.fromDescription("Unquantized-Opaque-Hilite-Overrides"), TechniqueFlags.fromDescription("Unquantized-Opaque-Instanced-Hilite-Overrides"), ]; const flags = scratchTechniqueFlags; for (const posType of positionTypes) { for (let instanced = 0 /* IsInstanced.No */; instanced <= 1 /* IsInstanced.Yes */; instanced++) { this.addHiliteShader(gl, instanced, 0 /* IsClassified.No */, posType, (inst, _class, pos) => createPolylineHiliter(inst, pos)); for (const featureMode of featureModes) { flags.reset(featureMode, instanced, 0 /* IsShadowable.No */, 0 /* IsThematic.No */, posType); const builder = createPolylineBuilder(instanced, posType); addUnlitMonochrome(builder.frag); // The translucent shaders do not need the element IDs. const builderTrans = createPolylineBuilder(instanced, posType); addUnlitMonochrome(builderTrans.frag); if (2 /* FeatureMode.Overrides */ === featureMode) { addFeatureSymbology(builderTrans, featureMode, 31 /* FeatureSymbologyOptions.Linear */); addFeatureSymbology(builder, featureMode, 31 /* FeatureSymbologyOptions.Linear */); this.addTranslucentShader(builderTrans, flags, gl); } else { this.addTranslucentShader(builderTrans, flags, gl); addFeatureSymbology(builder, featureMode, 0 /* FeatureSymbologyOptions.None */); } this.addFeatureId(builder, featureMode); flags.reset(featureMode, instanced, 0 /* IsShadowable.No */, 0 /* IsThematic.No */, posType); this.addShader(builder, flags, gl); } } } this.finishConstruction(); } get _debugDescription() { return "Polyline"; } computeShaderIndex(flags) { const idxOffset = flags.positionType === "unquantized" ? PolylineTechnique._kUnquantized : 0; if (flags.isHilite) { assert(flags.hasFeatures); return PolylineTechnique._kHilite + flags.isInstanced + idxOffset; } let index = flags.isTranslucent ? PolylineTechnique._kTranslucent : PolylineTechnique._kOpaque; index += PolylineTechnique._kFeature * flags.featureMode; index += PolylineTechnique._kInstanced * flags.isInstanced; return index + idxOffset; } } class EdgeTechnique extends VariedTechnique { static _kOpaque = 0; static _kTranslucent = 1; static _kAnimated = 2; static _kInstanced = 4; static _kFeature = 8; static _kUnquantized = numFeatureVariants(EdgeTechnique._kFeature); _type; constructor(gl, type) { super(EdgeTechnique._kUnquantized * 2); this._type = type; const flags = scratchTechniqueFlags; for (const posType of positionTypes) { for (let instanced = 0 /* IsInstanced.No */; instanced <= 1 /* IsInstanced.Yes */; instanced++) { for (let iAnimate = 0 /* IsAnimated.No */; iAnimate <= 1 /* IsAnimated.Yes */; iAnimate++) { for (const featureMode of featureModes) { flags.reset(featureMode, instanced, 0 /* IsShadowable.No */, 0 /* IsThematic.No */, posType); flags.isAnimated = iAnimate; const builder = createEdgeBuilder(type, flags.isInstanced, flags.isAnimated, posType); addUnlitMonochrome(builder.frag); // The translucent shaders do not need the element IDs. const builderTrans = createEdgeBuilder(type, flags.isInstanced, flags.isAnimated, posType); addUnlitMonochrome(builderTrans.frag); if (2 /* FeatureMode.Overrides */ === featureMode) { addFeatureSymbology(builderTrans, featureMode, 31 /* FeatureSymbologyOptions.Linear */); addFeatureSymbology(builder, featureMode, 31 /* FeatureSymbologyOptions.Linear */); this.addTranslucentShader(builderTrans, flags, gl); } else { this.addTranslucentShader(builderTrans, flags, gl); addFeatureSymbology(builder, featureMode, 0 /* FeatureSymbologyOptions.None */); } this.addFeatureId(builder, featureMode); flags.reset(featureMode, instanced, 0 /* IsShadowable.No */, 0 /* IsThematic.No */, posType); flags.isAnimated = iAnimate; this.addShader(builder, flags, gl); } } } } this.finishConstruction(); } get _debugDescription() { return this._type; } computeShaderIndex(flags) { let index = flags.isTranslucent ? EdgeTechnique._kTranslucent : EdgeTechnique._kOpaque; index += EdgeTechnique._kFeature * flags.featureMode; if (flags.isAnimated) index += EdgeTechnique._kAnimated; if (flags.isInstanced) index += EdgeTechnique._kInstanced; if ("unquantized" === flags.positionType) index += EdgeTechnique._kUnquantized; return index; } } class PointStringTechnique extends VariedTechnique { static _kOpaque = 0; static _kTranslucent = 1; static _kInstanced = 2; static _kFeature = 4; static _kHilite = numFeatureVariants(PointStringTechnique._kFeature); static _kUnquantized = PointStringTechnique._kHilite + numHiliteVariants; constructor(gl) { super(PointStringTechnique._kUnquantized * 2); const flags = scratchTechniqueFlags; for (const posType of positionTypes) { for (let instanced = 0 /* IsInstanced.No */; instanced <= 1 /* IsInstanced.Yes */; instanced++) { this.addHiliteShader(gl, instanced, 0 /* IsClassified.No */, posType, (inst, _class, pos) => createPointStringHiliter(inst, pos)); for (const featureMode of featureModes) { flags.reset(featureMode, instanced, 0 /* IsShadowable.No */, 0 /* IsThematic.No */, posType); const builder = createPointStringBuilder(instanced, posType); addUnlitMonochrome(builder.frag); // The translucent shaders do not need the element IDs. const builderTrans = createPointStringBuilder(instanced, posType); addUnlitMonochrome(builderTrans.frag); if (2 /* FeatureMode.Overrides */ === featureMode) { addFeatureSymbology(builderTrans, featureMode, 29 /* FeatureSymbologyOptions.Point */); addFeatureSymbology(builder, featureMode, 29 /* FeatureSymbologyOptions.Point */); this.addTranslucentShader(builderTrans, flags, gl); } else { this.addTranslucentShader(builderTrans, flags, gl); addFeatureSymbology(builder, featureMode, 0 /* FeatureSymbologyOptions.None */); } this.addFeatureId(builder, featureMode); flags.reset(featureMode, instanced, 0 /* IsShadowable.No */, 0 /* IsThematic.No */, posType); this.addShader(builder, flags, gl); } } } this.finishConstruction(); } get _debugDescription() { return "PointString"; } computeShaderIndex(flags) { const idxOffset = "quantized" === flags.positionType ? 0 : PointStringTechnique._kUnquantized; if (flags.isHilite) { assert(flags.hasFeatures); return PointStringTechnique._kHilite + flags.isInstanced + idxOffset; } let index = flags.isTranslucent ? PointStringTechnique._kTranslucent : PointStringTechnique._kOpaque; index += PointStringTechnique._kFeature * flags.featureMode; index += PointStringTechnique._kInstanced * flags.isInstanced; return index + idxOffset; } } class PointCloudTechnique extends VariedTechnique { static _kHilite = 8; constructor(gl) { super(PointCloudTechnique._kHilite + 2); for (let iClassified = 0 /* IsClassified.No */; iClassified <= 1 /* IsClassified.Yes */; iClassified++) { this.addHiliteShader(gl, 0 /* IsInstanced.No */, iClassified, "quantized", (_inst, classified) => createPointCloudHiliter(classified)); const flags = scratchTechniqueFlags; for (let thematic = 0 /* IsThematic.No */; thematic <= 1 /* IsThematic.Yes */; thematic++) { const pointCloudFeatureModes = [0 /* FeatureMode.None */, 2 /* FeatureMode.Overrides */]; for (const featureMode of pointCloudFeatureModes) { flags.reset(featureMode, 0 /* IsInstanced.No */, 0 /* IsShadowable.No */, thematic, "quantized"); flags.isClassified = iClassified; const builder = createPointCloudBuilder(flags.isClassified, featureMode, thematic); if (2 /* FeatureMode.Overrides */ === featureMode) { addUniformFeatureSymbology(builder, true); addColorOverrideMix(builder.vert); builder.vert.set(7 /* VertexShaderComponent.ApplyFeatureColor */, mixFeatureColor); } this.addFeatureId(builder, featureMode); this.addShader(builder, flags, gl); } } } this.finishConstruction(); } get _debugDescription() { return "PointCloud"; } computeShaderIndex(flags) { assert(flags.positionType === "quantized", "Unquantized point cloud positions not currently supported"); if (flags.isHilite) return PointCloudTechnique._kHilite + flags.isClassified; else { let ndx = 0; if (flags.isClassified) ndx++; if (flags.featureMode !== 0 /* FeatureMode.None */) ndx += 2; if (flags.isThematic) ndx += 4; return ndx; } } } class RealityMeshTechnique extends VariedTechnique { static _numVariants = 194; constructor(gl) { super(RealityMeshTechnique._numVariants); this._earlyZFlags = [ TechniqueFlags.fromDescription("Opaque-Hilite-Overrides"), TechniqueFlags.fromDescription("Opaque-Hilite-Classified"), ]; this.addHiliteShader(gl, 0 /* IsInstanced.No */, 0 /* IsClassified.No */, "quantized", createRealityMeshHiliter); this.addHiliteShader(gl, 0 /* IsInstanced.No */, 1 /* IsClassified.Yes */, "quantized", createClassifierRealityMeshHiliter); for (let iClassified = 0 /* IsClassified.No */; iClassified <= 1 /* IsClassified.Yes */; iClassified++) { for (let iTranslucent = 0; iTranslucent <= 1; iTranslucent++) { for (let shadowable = 0 /* IsShadowable.No */; shadowable <= 1 /* IsShadowable.Yes */; shadowable++) { for (let thematic = 0 /* IsThematic.No */; thematic <= 1 /* IsThematic.Yes */; thematic++) { for (let wiremesh = 0 /* IsWiremesh.No */; wiremesh <= 1 /* IsWiremesh.Yes */; wiremesh++) { for (let enableAtmosphere = 0 /* EnableAtmosphere.No */; enableAtmosphere <= 1 /* EnableAtmosphere.Yes */; enableAtmosphere++) { const flags = scratchTechniqueFlags; for (const featureMode of featureModes) { flags.reset(featureMode, 0 /* IsInstanced.No */, shadowable, thematic, "quantized"); flags.isClassified = iClassified; flags.isWiremesh = wiremesh; flags.isTranslucent = 1 === iTranslucent; flags.enableAtmosphere = enableAtmosphere; const builder = createRealityMeshBuilder(flags); if (flags.isTranslucent) { addShaderFlags(builder); addTranslucency(builder); } else this.addFeatureId(builder, featureMode); this.addShader(builder, flags, gl); } } } } } } } this.finishConstruction(); } get _debugDescription() { return "RealityMesh"; } computeShaderIndex(flags) { assert("quantized" === flags.positionType, "Unquantized reality mesh positions not currently supported."); if (flags.isHilite) return flags.isClassified ? 1 : 0; let ndx = 2; if (flags.isClassified) ndx++; if (flags.isShadowable) ndx += 2; if (flags.isTranslucent) ndx += 4; ndx += 8 * flags.featureMode; if (flags.isThematic) ndx += 24; if (flags.isWiremesh) ndx += 48; if (flags.enableAtmosphere) ndx += 96; return ndx; } } /** * More generalized version of VariedTechnique, without assuming usage of clipping, logDepth, eyeSpace, etc. * Similar to SingularTechnique in its simplicity, but with support for multiple shader programs per technique. */ class MultipleTechnique { _programs = []; _isDisposed = false; get isDisposed() { return this._isDisposed; } constructor(numPrograms) { this._programs.length = numPrograms; } getShaderIndex(flags) { assert(!flags.isHilite || (!flags.isTranslucent && (flags.isClassified === 1 /* IsClassified.Yes */ || flags.hasFeatures)), "invalid technique flags"); const index = this.computeShaderIndex(flags); assert(index < this._programs.length, "shader index out of bounds"); return index; } getShader(flags) { const index = this.getShaderIndex(flags); let program; if (program === undefined) program = this._programs[index]; return program; } getShaderByIndex(index) { return this._programs[index]; } getShaderCount() { return this._programs.length; } compileShaders() { let allCompiled = true; for (const program of this._programs) { if (program.compile() !== 0 /* CompileStatus.Success */) allCompiled = false; } return allCompiled; } [Symbol.dispose]() { if (this._isDisposed) return; for (const program of this._programs) { assert(undefined !== program); dispose(program); } this._programs.length = 0; this._isDisposed = true; } addShader(builder, flags, gl) { const descr = `${this._debugDescription}: ${flags.buildDescription()}`; builder.setDebugDescription(descr); const index = this.getShaderIndex(flags); this.addProgram(builder, index, gl); assert(!builder.frag.requiresEarlyZWorkaround); } addProgram(builder, index, gl) { assert(this._programs[index] === undefined); this._programs[index] = builder.buildProgram(gl); assert(this._programs[index] !== undefined); } finishConstruction() { // Confirm no empty entries in our array. let emptyShaderIndex = -1; assert(-1 === (emptyShaderIndex = this._programs.findIndex((prog) => undefined === prog)), `Shader index ${emptyShaderIndex} is undefined in ${this.constructor.name}`); } } class SkySphereTechnique extends MultipleTechnique { static _numVariants = 2; // one binary flag (2 ** 1) _isGradient; constructor(gl, isGradient) { super(SkySphereTechnique._numVariants); this._isGradient = isGradient; for (let enableAtmosphere = 0 /* EnableAtmosphere.No */; enableAtmosphere <= 1 /* EnableAtmosphere.Yes */; enableAtmosphere++) { const tempFlags = scratchTechniqueFlags; tempFlags.reset(0 /* FeatureMode.None */, 0 /* IsInstanced.No */, 0 /* IsShadowable.No */, 0 /* IsThematic.No */, "quantized"); tempFlags.enableAtmosphere = enableAtmosphere; const builder = createSkySphereBuilder(isGradient, tempFlags); this.addShader(builder, tempFlags, gl); } this.finishConstruction(); } get _debugDescription() { return `SkySphere-${this._isGradient ? "Gradient" : "Texture"}`; } computeShaderIndex(flags) { let index = 0; if (flags.enableAtmosphere) index += 1 << 0; return index; } } const techniquesByPriority = [ // Compile these specific shader variations first because they seem most likely to be used immediately upon opening a file. { techniqueId: 0 /* TechniqueId.Surface */, specificShader: { featureMode: 0 /* FeatureMode.None */, isInstanced: 0 /* IsInstanced.No */, isShadowable: 0 /* IsShadowable.No */, isEdgeTestedNeeded: 0 /* IsEdgeTestNeeded.No */, isTranslucent: false } }, { techniqueId: 0 /* TechniqueId.Surface */, specificShader: { featureMode: 1 /* FeatureMode.Pick */, isInstanced: 0 /* IsInstanced.No */, isShadowable: 0 /* IsShadowable.No */, isEdgeTestedNeeded: 0 /* IsEdgeTestNeeded.No */, isTranslucent: false } }, { techniqueId: 0 /* TechniqueId.Surface */, specificShader: { featureMode: 1 /* FeatureMode.Pick */, isInstanced: 0 /* IsInstanced.No */, isShadowable: 0 /* IsShadowable.No */, isEdgeTestedNeeded: 1 /* IsEdgeTestNeeded.Yes */, isTranslucent: false } }, { techniqueId: 0 /* TechniqueId.Surface */, specificShader: { featureMode: 2 /* FeatureMode.Overrides */, isInstanced: 0 /* IsInstanced.No */, isShadowable: 0 /* IsShadowable.No */, isEdgeTestedNeeded: 0 /* IsEdgeTestNeeded.No */, isTranslucent: false } }, { techniqueId: 0 /* TechniqueId.Surface */, specificShader: { featureMode: 2 /* FeatureMode.Overrides */, isInstanced: 0 /* IsInstanced.No */, isShadowable: 0 /* IsShadowable.No */, isEdgeTestedNeeded: 1 /* IsEdgeTestNeeded.Yes */, isTranslucent: false } }, { techniqueId: 0 /* TechniqueId.Surface */, specificShader: { featureMode: 0 /* FeatureMode.None */, isInstanced: 0 /* IsInstanced.No */, isShadowable: 0 /* IsShadowable.No */, isEdgeTestedNeeded: 0 /* IsEdgeTestNeeded.No */, isTranslucent: true } }, { techniqueId: 0 /* TechniqueId.Surface */, specificShader: { featureMode: 1 /* FeatureMode.Pick */, isInstanced: 0 /* IsInstanced.No */, isShadowable: 0 /* IsShadowable.No */, isEdgeTestedNeeded: 0 /* IsEdgeTestNeeded.No */, isTranslucent: true } }, { techniqueId: 0 /* TechniqueId.Surface */, specificShader: { featureMode: 1 /* FeatureMode.Pick */, isInstanced: 0 /* IsInstanced.No */, isShadowable: 0 /* IsShadowable.No */, isEdgeTestedNeeded: 1 /* IsEdgeTestNeeded.Yes */, isTranslucent: true } }, { techniqueId: 0 /* TechniqueId.Surface */, specificShader: { featureMode: 2 /* FeatureMode.Overrides */, isInstanced: 0 /* IsInstanced.No */, isShadowable: 0 /* IsShadowable.No */, isEdgeTestedNeeded: 0 /* IsEdgeTestNeeded.No */, isTranslucent: true } }, { techniqueId: 0 /* TechniqueId.Surface */, specificShader: { featureMode: 2 /* FeatureMode.Overrides */, isInstanced: 0 /* IsInstanced.No */, isShadowable: 0 /* IsShadowable.No */, isEdgeTestedNeeded: 1 /* IsEdgeTestNeeded.Yes */, isTranslucent: true } }, // Next, compile all shaders in specific techniques. // Do surfaces first because (1) they are the most commonly used and (2) they take longer to compile. { techniqueId: 0 /* TechniqueId.Surface */ }, { techniqueId: 4 /* TechniqueId.Edge */ }, { techniqueId: 5 /* TechniqueId.SilhouetteEdge */ }, { techniqueId: 1 /* TechniqueId.Polyline */ }, { techniqueId: 3 /* TechniqueId.PointString */ }, { techniqueId: 2 /* TechniqueId.PointCloud */ }, { techniqueId: 7 /* TechniqueId.RealityMesh */ }, // The following techniques take a trivial amount of time to compile - do them last { techniqueId: 16 /* TechniqueId.OITClearTranslucent */ }, { techniqueId: 17 /* TechniqueId.CopyPickBuffers */ }, { techniqueId: 18 /* TechniqueId.CopyColor */ }, { techniqueId: 19 /* TechniqueId.CopyColorNoAlpha */ }, { techniqueId: 21 /* TechniqueId.ClearPickAndColor */ }, { techniqueId: 10 /* TechniqueId.CompositeTranslucent */ }, { techniqueId: 9 /* TechniqueId.CompositeHilite */ }, { techniqueId: 11 /* TechniqueId.CompositeHiliteAndTranslucent */ }, { techniqueId: 12 /* TechniqueId.CompositeOcclusion */ }, { techniqueId: 13 /* TechniqueId.CompositeTranslucentAndOcclusion */ }, { techniqueId: 14 /* TechniqueId.CompositeHiliteAndOcclusion */ }, { techniqueId: 15 /* TechniqueId.CompositeAll */ }, { techniqueId: 20 /* TechniqueId.VolClassColorUsingStencil */ }, { techniqueId: 22 /* TechniqueId.EVSMFromDepth */ }, { techniqueId: 23 /* TechniqueId.SkyBox */ }, { techniqueId: 24 /* TechniqueId.SkySphereGradient */ }, { techniqueId: 25 /* TechniqueId.SkySphereTexture */ }, { techniqueId: 26 /* TechniqueId.AmbientOcclusion */ }, { techniqueId: 27 /* TechniqueId.Blur */ }, { techniqueId: 28 /* TechniqueId.BlurTestOrder */ }, { techniqueId: 29 /* TechniqueId.CombineTextures */ }, { techniqueId: 31 /* TechniqueId.VolClassCopyZ */ }, { techniqueId: 32 /* TechniqueId.VolClassSetBlend */ }, { techniqueId: 33 /* TechniqueId.VolClassBlend */ }, { techniqueId: 30 /* TechniqueId.Combine3Textures */ }, { techniqueId: 8 /* TechniqueId.PlanarGrid */ }, { techniqueId: 34 /* TechniqueId.EDLCalcBasic */ }, { techniqueId: 35 /* TechniqueId.EDLCalcFull */ }, { techniqueId: 36 /* TechniqueId.EDLFilter */ }, { techniqueId: 37 /* TechniqueId.EDLMix */ }, ]; const numTechniquesByPriority = techniquesByPriority.length; /** A collection of rendering techniques accessed by ID. * @internal */ export class Techniques { _list = new Array(); // indexed by TechniqueId, which may exceed TechniqueId.NumBuiltIn for dynamic techniques. _dynamicTechniqueIds = new Array(); // technique ID = (index in this array) + TechniqueId.NumBuiltIn _techniqueByPriorityIndex = 0; _shaderIndex = 0; static create(gl) { const techs = new Techniques(); techs.initializeBuiltIns(gl); return techs; } getTechnique(id) { assert(id < this._list.length, "technique index out of bounds"); return this._list[id]; } get numTechniques() { return this._list.length; } addDynamicTechnique(technique, name) { const id = this.getDynamicTechniqueId(name); if (undefined !== id) return id; this._dynamicTechniqueIds.push(name); this._list.push(technique); return 38 /* TechniqueId.NumBuiltIn */ + this._dynamicTechniqueIds.length - 1; } getDynamicTechniqueId(name) { const index = this._dynamicTechniqueIds.indexOf(name); return -1 !== index ? index + 38 /* TechniqueId.NumBuiltIn */ + index : undefined; } /** Execute each command in the list */ execute(target, commands, renderPass) { const env_1 = { stack: [], error: void 0, hasError: false }; try { assert(255 /* RenderPass.None */ !== renderPass, "invalid render pass"); const executor = __addDisposableResource(env_1, new ShaderProgramExecutor(target, renderPass), false); for (const command of commands) command.execute(executor); System.instance.frameBufferStack.markTargetsDirty(); } catch (e_1) { env_1.error = e_1; env_1.hasError = true; } finally { __disposeResources(env_1); } } /** Execute the commands for a single given classification primitive (the first 3 commands should be a push, the primitive, then a pop) */ executeForIndexedClassifier(target, cmdsByIndex, renderPass) { // ###TODO: Disable shadows. Probably in the ClassifierTileTree's ViewFlagOverrides. this.execute(target, cmdsByIndex, renderPass); } /** Draw a single primitive. Usually used for special-purpose rendering techniques. */ draw(params) { const env_2 = { stack: [], error: void 0, hasError: false }; try { const tech = this.getTechnique(params.geometry.techniqueId); const program = tech.getShader(TechniqueFlags.defaults); const executor = __addDisposableResource(env_2, new ShaderProgramExecutor(params.target, params.renderPass, program), false); assert(executor.isValid); if (executor.isValid) { executor.draw(params); } System.instance.frameBufferStack.markTargetsDirty(); } catch (e_2) { env_2.error = e_2; env_2.hasError = true; } finally { __disposeResources(env_2); } } get isDisposed() { return 0 === this._list.length; } [Symbol.dispose]() { for (const tech of this._list) dispose(tech); this._list.length = 0; } /** @deprecated in 5.0 - will not be removed until after 2026-06-13. Use [Symbol.dispose] instead. */ dispose() { this[Symbol.dispose](); } // Chiefly for tests - compiles all shader programs - more generally programs are compiled on demand. compileShaders() { let allCompiled = true; for (const tech of this._list) { if (!tech.compileShaders()) allCompiled = false; } return allCompiled; } /** Compile shader of next highest priority. Called when possible during an idle situation before any viewports exist. */ idleCompileNextShader() { let compileStatus = 0 /* CompileStatus.Success */; let wasPreviouslyCompiled = false; do { if (this._techniqueByPriorityIndex >= numTechniquesByPriority) return false; let shader; let numShaders = 0; const pTech = techniquesByPriority[this._techniqueByPriorityIndex]; const tech = this._list[pTech.techniqueId]; if (pTech.specificShader !== undefined) { // if this entry consists of a specific shader, just compile that const flags = scratchTechniqueFlags; flags.reset(pTech.specificShader.featureMode, pTech.specificShader.isInstanced, pTech.specificShader.isShadowable, 0 /* IsThematic.No */, "quantized"); flags.isEdgeTestNeeded = pTech.specificShader.isEdgeTestedNeeded; flags.isTranslucent = pTech.specificShader.isTranslucent; shader = tech.getShader(flags); } else { // if this entry only contains a techniqueId, then compile all uncompiled shaders for that technique shader = tech.getShaderByIndex(this._shaderIndex); this._shaderIndex++; numShaders = tech.getShaderCount(); } if (shader.isCompiled) wasPreviouslyCompiled = true; else { compileStatus = shader.compile(); wasPreviouslyCompiled = false; } if (this._shaderIndex >= numShaders) { this._techniqueByPriorityIndex++; this._shaderIndex = 0; } } while (wasPreviouslyCompiled); return compileStatus === 0 /* CompileStatus.Success */; } /** For tests. */ forEachVariedProgram(func) { for (const technique of this._list) if (technique instanceof VariedTechnique) technique.forEachProgram(func); } constructor() { } initializeBuiltIns(gl) { this._list[16 /* TechniqueId.OITClearTranslucent */] = new SingularTechnique(createClearTranslucentProgram(gl)); this._list[21 /* TechniqueId.ClearPickAndColor */] = new SingularTechnique(createClearPickAndColorProgram(gl)); this._list[18 /* TechniqueId.CopyColor */] = new SingularTechnique(createCopyColorProgram(gl)); this._list[19 /* TechniqueId.CopyColorNoAlpha */] = new SingularTechnique(createCopyColorProgram(gl, false)); this._list[17 /* TechniqueId.CopyPickBuffers */] = new SingularTechnique(createCopyPickBuffersProgram(gl)); this._list[22 /* TechniqueId.EVSMFromDepth */] = new SingularTechnique(createEVSMProgram(gl)); this._list[23 /* TechniqueId.SkyBox */] = new SingularTechnique(createSkyBoxProgram(gl)); this._list[24 /* TechniqueId.SkySphereGradient */] = new SkySphereTechnique(gl, true); this._list[25 /* TechniqueId.SkySphereTexture */] = new SkySphereTechnique(gl, false); this._list[26 /* TechniqueId.AmbientOcclusion */] = new SingularTechnique(createAmbientOcclusionProgram(gl)); this._list[27 /* TechniqueId.Blur */] = new SingularTechnique(createBlurProgram(gl, BlurType.NoTest)); this._list[28 /* TechniqueId.BlurTestOrder */] = new SingularTechnique(createBlurProgram(gl, BlurType.TestO