UNPKG

@itwin/core-frontend

Version:
847 lines (846 loc) • 128 kB
"use strict"; /*--------------------------------------------------------------------------------------------- * 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 */ Object.defineProperty(exports, "__esModule", { value: true }); exports.SceneCompositor = void 0; exports.collectTextureStatistics = collectTextureStatistics; exports.collectGeometryStatistics = collectGeometryStatistics; const core_bentley_1 = require("@itwin/core-bentley"); const core_geometry_1 = require("@itwin/core-geometry"); const core_common_1 = require("@itwin/core-common"); const webgl_compatibility_1 = require("@itwin/webgl-compatibility"); const Pixel_1 = require("../../../render/Pixel"); const BranchState_1 = require("./BranchState"); const CachedGeometry_1 = require("./CachedGeometry"); const Diagnostics_1 = require("./Diagnostics"); const DrawCommand_1 = require("./DrawCommand"); const FrameBuffer_1 = require("./FrameBuffer"); const GL_1 = require("./GL"); const IModelFrameLifecycle_1 = require("./IModelFrameLifecycle"); const Matrix_1 = require("./Matrix"); const RenderFlags_1 = require("./RenderFlags"); const RenderState_1 = require("./RenderState"); const ScratchDrawParams_1 = require("./ScratchDrawParams"); const SolarShadowMap_1 = require("./SolarShadowMap"); const System_1 = require("./System"); const Texture_1 = require("./Texture"); const RenderBuffer_1 = require("./RenderBuffer"); const EDL_1 = require("./EDL"); function collectTextureStatistics(texture, stats) { if (undefined !== texture) stats.addTextureAttachment(texture.bytesUsed); } function collectMsBufferStatistics(msBuff, stats) { if (undefined !== msBuff) stats.addTextureAttachment(msBuff.bytesUsed); } // Maintains the textures used by a SceneCompositor. The textures are reallocated when the dimensions of the viewport change. class Textures { accumulation; revealage; color; featureId; depthAndOrder; depthAndOrderHidden; // only used if AO and multisampling contours; contoursMsBuff; hilite; occlusion; occlusionBlur; volClassBlend; colorMsBuff; featureIdMsBuff; featureIdMsBuffHidden; depthAndOrderMsBuff; depthAndOrderMsBuffHidden; hiliteMsBuff; volClassBlendMsBuff; get isDisposed() { return undefined === this.accumulation && undefined === this.revealage && undefined === this.color && undefined === this.featureId && undefined === this.depthAndOrder && undefined === this.contours && undefined === this.contoursMsBuff && undefined === this.depthAndOrderHidden && undefined === this.hilite && undefined === this.occlusion && undefined === this.occlusionBlur && undefined === this.volClassBlend && undefined === this.colorMsBuff && undefined === this.featureIdMsBuff && undefined === this.featureIdMsBuffHidden && undefined === this.depthAndOrderMsBuff && undefined === this.depthAndOrderMsBuffHidden && undefined === this.hiliteMsBuff && undefined === this.volClassBlendMsBuff; } [Symbol.dispose]() { this.accumulation = (0, core_bentley_1.dispose)(this.accumulation); this.revealage = (0, core_bentley_1.dispose)(this.revealage); this.color = (0, core_bentley_1.dispose)(this.color); this.featureId = (0, core_bentley_1.dispose)(this.featureId); this.depthAndOrder = (0, core_bentley_1.dispose)(this.depthAndOrder); this.contours = (0, core_bentley_1.dispose)(this.contours); this.contoursMsBuff = (0, core_bentley_1.dispose)(this.contoursMsBuff); this.depthAndOrderHidden = (0, core_bentley_1.dispose)(this.depthAndOrderHidden); this.hilite = (0, core_bentley_1.dispose)(this.hilite); this.occlusion = (0, core_bentley_1.dispose)(this.occlusion); this.occlusionBlur = (0, core_bentley_1.dispose)(this.occlusionBlur); this.colorMsBuff = (0, core_bentley_1.dispose)(this.colorMsBuff); this.featureIdMsBuff = (0, core_bentley_1.dispose)(this.featureIdMsBuff); this.featureIdMsBuffHidden = (0, core_bentley_1.dispose)(this.featureIdMsBuffHidden); this.depthAndOrderMsBuff = (0, core_bentley_1.dispose)(this.depthAndOrderMsBuff); this.depthAndOrderMsBuffHidden = (0, core_bentley_1.dispose)(this.depthAndOrderMsBuffHidden); this.hiliteMsBuff = (0, core_bentley_1.dispose)(this.hiliteMsBuff); this.volClassBlend = (0, core_bentley_1.dispose)(this.volClassBlend); this.volClassBlendMsBuff = (0, core_bentley_1.dispose)(this.volClassBlendMsBuff); } collectStatistics(stats) { collectTextureStatistics(this.accumulation, stats); collectTextureStatistics(this.revealage, stats); collectTextureStatistics(this.color, stats); collectTextureStatistics(this.featureId, stats); collectTextureStatistics(this.depthAndOrder, stats); collectTextureStatistics(this.contours, stats); collectMsBufferStatistics(this.contoursMsBuff, stats); collectTextureStatistics(this.depthAndOrderHidden, stats); collectTextureStatistics(this.hilite, stats); collectTextureStatistics(this.occlusion, stats); collectTextureStatistics(this.occlusionBlur, stats); collectTextureStatistics(this.volClassBlend, stats); collectMsBufferStatistics(this.colorMsBuff, stats); collectMsBufferStatistics(this.featureIdMsBuff, stats); collectMsBufferStatistics(this.featureIdMsBuffHidden, stats); collectMsBufferStatistics(this.depthAndOrderMsBuff, stats); collectMsBufferStatistics(this.depthAndOrderMsBuffHidden, stats); collectMsBufferStatistics(this.hiliteMsBuff, stats); collectMsBufferStatistics(this.volClassBlendMsBuff, stats); } init(width, height, numSamples) { (0, core_bentley_1.assert)(undefined === this.accumulation); let pixelDataType = GL_1.GL.Texture.DataType.UnsignedByte; switch (System_1.System.instance.maxRenderType) { case webgl_compatibility_1.RenderType.TextureFloat: { pixelDataType = GL_1.GL.Texture.DataType.Float; break; } case webgl_compatibility_1.RenderType.TextureHalfFloat: { pixelDataType = System_1.System.instance.context.HALF_FLOAT; break; } /* falls through */ case webgl_compatibility_1.RenderType.TextureUnsignedByte: { break; } } // NB: Both of these must be of the same type, because they are borrowed by pingpong and bound to the same frame buffer. this.accumulation = Texture_1.TextureHandle.createForAttachment(width, height, GL_1.GL.Texture.Format.Rgba, pixelDataType); this.revealage = Texture_1.TextureHandle.createForAttachment(width, height, GL_1.GL.Texture.Format.Rgba, pixelDataType); // Hilite texture is a simple on-off, but the smallest texture format WebGL allows us to use as output is RGBA with a byte per component. this.hilite = Texture_1.TextureHandle.createForAttachment(width, height, GL_1.GL.Texture.Format.Rgba, GL_1.GL.Texture.DataType.UnsignedByte); this.color = Texture_1.TextureHandle.createForAttachment(width, height, GL_1.GL.Texture.Format.Rgba, GL_1.GL.Texture.DataType.UnsignedByte); this.featureId = Texture_1.TextureHandle.createForAttachment(width, height, GL_1.GL.Texture.Format.Rgba, GL_1.GL.Texture.DataType.UnsignedByte); this.depthAndOrder = Texture_1.TextureHandle.createForAttachment(width, height, GL_1.GL.Texture.Format.Rgba, GL_1.GL.Texture.DataType.UnsignedByte); this.contours = Texture_1.TextureHandle.createForAttachment(width, height, GL_1.GL.Texture.Format.Rgba, GL_1.GL.Texture.DataType.UnsignedByte); let rVal = undefined !== this.accumulation && undefined !== this.revealage && undefined !== this.color && undefined !== this.featureId && undefined !== this.depthAndOrder && undefined !== this.contours && undefined !== this.hilite; if (rVal && numSamples > 1) { rVal = this.enableMultiSampling(width, height, numSamples); } return rVal; } enableOcclusion(width, height, numSamples) { (0, core_bentley_1.assert)(undefined === this.occlusion && undefined === this.occlusionBlur); this.occlusion = Texture_1.TextureHandle.createForAttachment(width, height, GL_1.GL.Texture.Format.Rgba, GL_1.GL.Texture.DataType.UnsignedByte); this.occlusionBlur = Texture_1.TextureHandle.createForAttachment(width, height, GL_1.GL.Texture.Format.Rgba, GL_1.GL.Texture.DataType.UnsignedByte); let rVal = undefined !== this.occlusion && undefined !== this.occlusionBlur; if (numSamples > 1) { // If multisampling then we need a texture for storing depth and order for hidden edges. this.depthAndOrderHidden = Texture_1.TextureHandle.createForAttachment(width, height, GL_1.GL.Texture.Format.Rgba, GL_1.GL.Texture.DataType.UnsignedByte); rVal = rVal && undefined !== this.depthAndOrderHidden; } return rVal; } disableOcclusion() { (0, core_bentley_1.assert)(undefined !== this.occlusion && undefined !== this.occlusionBlur); this.occlusion = (0, core_bentley_1.dispose)(this.occlusion); this.occlusionBlur = (0, core_bentley_1.dispose)(this.occlusionBlur); this.depthAndOrderHidden = (0, core_bentley_1.dispose)(this.depthAndOrderHidden); } enableVolumeClassifier(width, height, numSamples) { (0, core_bentley_1.assert)(undefined === this.volClassBlend); this.volClassBlend = Texture_1.TextureHandle.createForAttachment(width, height, GL_1.GL.Texture.Format.Rgba, GL_1.GL.Texture.DataType.UnsignedByte); let rVal = undefined !== this.volClassBlend; if (rVal && undefined !== numSamples && numSamples > 1) { this.volClassBlendMsBuff = RenderBuffer_1.RenderBufferMultiSample.create(width, height, WebGL2RenderingContext.RGBA8, numSamples); rVal = undefined !== this.volClassBlendMsBuff; } return rVal; } disableVolumeClassifier() { this.volClassBlend = (0, core_bentley_1.dispose)(this.volClassBlend); this.volClassBlendMsBuff = (0, core_bentley_1.dispose)(this.volClassBlendMsBuff); } enableMultiSampling(width, height, numSamples) { this.colorMsBuff = RenderBuffer_1.RenderBufferMultiSample.create(width, height, WebGL2RenderingContext.RGBA8, numSamples); this.featureIdMsBuff = RenderBuffer_1.RenderBufferMultiSample.create(width, height, WebGL2RenderingContext.RGBA8, numSamples); this.featureIdMsBuffHidden = RenderBuffer_1.RenderBufferMultiSample.create(width, height, WebGL2RenderingContext.RGBA8, numSamples); this.depthAndOrderMsBuff = RenderBuffer_1.RenderBufferMultiSample.create(width, height, WebGL2RenderingContext.RGBA8, numSamples); this.depthAndOrderMsBuffHidden = RenderBuffer_1.RenderBufferMultiSample.create(width, height, WebGL2RenderingContext.RGBA8, numSamples); this.contoursMsBuff = RenderBuffer_1.RenderBufferMultiSample.create(width, height, WebGL2RenderingContext.RGBA8, numSamples); this.hiliteMsBuff = RenderBuffer_1.RenderBufferMultiSample.create(width, height, WebGL2RenderingContext.RGBA8, numSamples); return undefined !== this.colorMsBuff && undefined !== this.featureIdMsBuff && undefined !== this.featureIdMsBuffHidden && undefined !== this.depthAndOrderMsBuff && undefined !== this.depthAndOrderMsBuffHidden && undefined !== this.contoursMsBuff && undefined !== this.hiliteMsBuff; } disableMultiSampling() { this.colorMsBuff = (0, core_bentley_1.dispose)(this.colorMsBuff); this.featureIdMsBuff = (0, core_bentley_1.dispose)(this.featureIdMsBuff); this.featureIdMsBuffHidden = (0, core_bentley_1.dispose)(this.featureIdMsBuffHidden); this.depthAndOrderMsBuff = (0, core_bentley_1.dispose)(this.depthAndOrderMsBuff); this.depthAndOrderMsBuffHidden = (0, core_bentley_1.dispose)(this.depthAndOrderMsBuffHidden); this.contoursMsBuff = (0, core_bentley_1.dispose)(this.contoursMsBuff); this.hiliteMsBuff = (0, core_bentley_1.dispose)(this.hiliteMsBuff); return true; } } // Maintains the framebuffers used by a SceneCompositor. The color attachments are supplied by a Textures object. class FrameBuffers { opaqueColor; opaqueAndCompositeColor; depthAndOrder; contours; hilite; hiliteUsingStencil; stencilSet; occlusion; occlusionBlur; altZOnly; volClassCreateBlend; volClassCreateBlendAltZ; opaqueAll; opaqueAndCompositeAll; opaqueAndCompositeAllHidden; pingPong; pingPongMS; translucent; clearTranslucent; idsAndZ; idsAndAltZ; idsAndZComposite; idsAndAltZComposite; edlDrawCol; init(textures, depth, depthMS) { if (!this.initPotentialMSFbos(textures, depth, depthMS)) return false; this.depthAndOrder = FrameBuffer_1.FrameBuffer.create([textures.depthAndOrder], depth); this.contours = FrameBuffer_1.FrameBuffer.create([textures.contours], depth); this.hilite = FrameBuffer_1.FrameBuffer.create([textures.hilite], depth); this.hiliteUsingStencil = FrameBuffer_1.FrameBuffer.create([textures.hilite], depth); if (!this.depthAndOrder || !this.contours || !this.hilite || !this.hiliteUsingStencil) return false; (0, core_bentley_1.assert)(undefined === this.opaqueAll); if (!this.initPotentialMSMRTFbos(textures, depth, depthMS)) return false; (0, core_bentley_1.assert)(undefined !== textures.accumulation && undefined !== textures.revealage); const colors = [textures.accumulation, textures.revealage]; this.translucent = FrameBuffer_1.FrameBuffer.create(colors, depth); this.clearTranslucent = FrameBuffer_1.FrameBuffer.create(colors); // We borrow the SceneCompositor's accum and revealage textures for the surface pass. // First we render edges, writing to our textures. // Then we copy our textures to borrowed textures. // Finally we render surfaces, writing to our textures and reading from borrowed textures. (0, core_bentley_1.assert)(undefined !== textures.accumulation && undefined !== textures.revealage); const pingPong = [textures.accumulation, textures.revealage]; this.pingPong = FrameBuffer_1.FrameBuffer.create(pingPong); return undefined !== this.translucent && undefined !== this.clearTranslucent && undefined !== this.pingPong; } initPotentialMSFbos(textures, depth, depthMS) { const boundColor = System_1.System.instance.frameBufferStack.currentColorBuffer; (0, core_bentley_1.assert)(undefined !== boundColor && undefined !== textures.color); if (undefined === depthMS) { this.opaqueColor = FrameBuffer_1.FrameBuffer.create([boundColor], depth); this.opaqueAndCompositeColor = FrameBuffer_1.FrameBuffer.create([textures.color], depth); } else { (0, core_bentley_1.assert)(undefined !== textures.colorMsBuff); this.opaqueColor = FrameBuffer_1.FrameBuffer.create([boundColor], depth, [textures.colorMsBuff], [GL_1.GL.MultiSampling.Filter.Linear], depthMS); this.opaqueAndCompositeColor = FrameBuffer_1.FrameBuffer.create([textures.color], depth, [textures.colorMsBuff], [GL_1.GL.MultiSampling.Filter.Linear], depthMS); } return undefined !== this.opaqueColor && undefined !== this.opaqueAndCompositeColor; } initPotentialMSMRTFbos(textures, depth, depthMs) { const boundColor = System_1.System.instance.frameBufferStack.currentColorBuffer; (0, core_bentley_1.assert)(undefined !== boundColor && undefined !== textures.color && undefined !== textures.featureId && undefined !== textures.depthAndOrder && undefined !== textures.contours && undefined !== textures.accumulation && undefined !== textures.revealage); const colorAndPick = [boundColor, textures.featureId, textures.depthAndOrder, textures.contours]; if (undefined === depthMs) { this.opaqueAll = FrameBuffer_1.FrameBuffer.create(colorAndPick, depth); colorAndPick[0] = textures.color; this.opaqueAndCompositeAll = FrameBuffer_1.FrameBuffer.create(colorAndPick, depth); } else { (0, core_bentley_1.assert)(undefined !== textures.colorMsBuff && undefined !== textures.featureIdMsBuff && undefined !== textures.featureIdMsBuffHidden && undefined !== textures.contoursMsBuff && undefined !== textures.depthAndOrderMsBuff && undefined !== textures.depthAndOrderMsBuffHidden); const colorAndPickMsBuffs = [textures.colorMsBuff, textures.featureIdMsBuff, textures.depthAndOrderMsBuff, textures.contoursMsBuff]; const colorAndPickFilters = [GL_1.GL.MultiSampling.Filter.Linear, GL_1.GL.MultiSampling.Filter.Nearest, GL_1.GL.MultiSampling.Filter.Nearest, GL_1.GL.MultiSampling.Filter.Nearest]; this.opaqueAll = FrameBuffer_1.FrameBuffer.create(colorAndPick, depth, colorAndPickMsBuffs, colorAndPickFilters, depthMs); colorAndPick[0] = textures.color; this.opaqueAndCompositeAll = FrameBuffer_1.FrameBuffer.create(colorAndPick, depth, colorAndPickMsBuffs, colorAndPickFilters, depthMs); } return undefined !== this.opaqueAll && undefined !== this.opaqueAndCompositeAll; } enableOcclusion(textures, depth, depthMs) { (0, core_bentley_1.assert)(undefined !== textures.occlusion && undefined !== textures.occlusionBlur); this.occlusion = FrameBuffer_1.FrameBuffer.create([textures.occlusion]); this.occlusionBlur = FrameBuffer_1.FrameBuffer.create([textures.occlusionBlur]); let rVal = undefined !== this.occlusion && undefined !== this.occlusionBlur; if (undefined === depthMs) { // If not using multisampling then we can use the accumulation and revealage textures for the hidden pick buffers, (0, core_bentley_1.assert)(undefined !== textures.color && undefined !== textures.accumulation && undefined !== textures.revealage); const colorAndPick = [textures.color, textures.accumulation, textures.revealage]; this.opaqueAndCompositeAllHidden = FrameBuffer_1.FrameBuffer.create(colorAndPick, depth); rVal = rVal && undefined !== this.opaqueAndCompositeAllHidden; } else { // If multisampling then we cannot use the revealage texture for depthAndOrder for the hidden edges since it is of the wrong type for blitting, // so instead use a special depthAndOrderHidden texture just for this purpose. // The featureId texture is not needed for hidden edges, so the accumulation texture can be used for it if we don't blit from the multisample bufffer into it. (0, core_bentley_1.assert)(undefined !== textures.color && undefined !== textures.accumulation && undefined !== textures.depthAndOrderHidden); (0, core_bentley_1.assert)(undefined !== textures.colorMsBuff && undefined !== textures.featureIdMsBuffHidden && undefined !== textures.depthAndOrderMsBuffHidden); const colorAndPick = [textures.color, textures.accumulation, textures.depthAndOrderHidden]; const colorAndPickMsBuffs = [textures.colorMsBuff, textures.featureIdMsBuffHidden, textures.depthAndOrderMsBuffHidden]; const colorAndPickFilters = [GL_1.GL.MultiSampling.Filter.Linear, GL_1.GL.MultiSampling.Filter.Nearest, GL_1.GL.MultiSampling.Filter.Nearest]; this.opaqueAndCompositeAllHidden = FrameBuffer_1.FrameBuffer.create(colorAndPick, depth, colorAndPickMsBuffs, colorAndPickFilters, depthMs); // We will also need a frame buffer for copying the real pick data buffers into these hidden edge pick data buffers. const pingPong = [textures.accumulation, textures.depthAndOrderHidden]; const pingPongMSBuffs = [textures.featureIdMsBuffHidden, textures.depthAndOrderMsBuffHidden]; const pingPongFilters = [GL_1.GL.MultiSampling.Filter.Nearest, GL_1.GL.MultiSampling.Filter.Nearest]; this.pingPongMS = FrameBuffer_1.FrameBuffer.create(pingPong, depth, pingPongMSBuffs, pingPongFilters, depthMs); rVal = rVal && undefined !== this.opaqueAndCompositeAllHidden && (undefined === depthMs || undefined !== this.pingPongMS); } return rVal; } disableOcclusion() { if (undefined !== this.occlusion) { this.occlusion = (0, core_bentley_1.dispose)(this.occlusion); this.occlusionBlur = (0, core_bentley_1.dispose)(this.occlusionBlur); } this.opaqueAndCompositeAllHidden = (0, core_bentley_1.dispose)(this.opaqueAndCompositeAllHidden); this.pingPongMS = (0, core_bentley_1.dispose)(this.pingPongMS); } enableVolumeClassifier(textures, depth, volClassDepth, depthMS, volClassDepthMS) { const boundColor = System_1.System.instance.frameBufferStack.currentColorBuffer; if (undefined === boundColor) return; if (undefined === this.stencilSet) { if (undefined !== depthMS) { // if multisampling use the multisampled depth everywhere this.stencilSet = FrameBuffer_1.FrameBuffer.create([], depth, [], [], depthMS); this.altZOnly = FrameBuffer_1.FrameBuffer.create([], volClassDepth, [], [], volClassDepthMS); this.volClassCreateBlend = FrameBuffer_1.FrameBuffer.create([textures.volClassBlend], depth, [textures.volClassBlendMsBuff], [GL_1.GL.MultiSampling.Filter.Nearest], depthMS); this.volClassCreateBlendAltZ = FrameBuffer_1.FrameBuffer.create([textures.volClassBlend], volClassDepth, [textures.volClassBlendMsBuff], [GL_1.GL.MultiSampling.Filter.Nearest], volClassDepthMS); } else if (undefined !== volClassDepth) { this.stencilSet = FrameBuffer_1.FrameBuffer.create([], depth); this.altZOnly = FrameBuffer_1.FrameBuffer.create([], volClassDepth); this.volClassCreateBlend = FrameBuffer_1.FrameBuffer.create([textures.volClassBlend], depth); this.volClassCreateBlendAltZ = FrameBuffer_1.FrameBuffer.create([textures.volClassBlend], volClassDepth); } } if (undefined !== this.opaqueAll && undefined !== this.opaqueAndCompositeAll) { if (undefined !== volClassDepth) { let ids = [this.opaqueAll.getColor(0), this.opaqueAll.getColor(1)]; this.idsAndZ = FrameBuffer_1.FrameBuffer.create(ids, depth); this.idsAndAltZ = FrameBuffer_1.FrameBuffer.create(ids, volClassDepth); ids = [this.opaqueAndCompositeAll.getColor(0), this.opaqueAndCompositeAll.getColor(1)]; this.idsAndZComposite = FrameBuffer_1.FrameBuffer.create(ids, depth); this.idsAndAltZComposite = FrameBuffer_1.FrameBuffer.create(ids, volClassDepth); } } } disableVolumeClassifier() { if (undefined !== this.stencilSet) { this.stencilSet = (0, core_bentley_1.dispose)(this.stencilSet); this.altZOnly = (0, core_bentley_1.dispose)(this.altZOnly); this.volClassCreateBlend = (0, core_bentley_1.dispose)(this.volClassCreateBlend); this.volClassCreateBlendAltZ = (0, core_bentley_1.dispose)(this.volClassCreateBlendAltZ); } if (undefined !== this.idsAndZ) { this.idsAndZ = (0, core_bentley_1.dispose)(this.idsAndZ); this.idsAndAltZ = (0, core_bentley_1.dispose)(this.idsAndAltZ); this.idsAndZComposite = (0, core_bentley_1.dispose)(this.idsAndZComposite); this.idsAndAltZComposite = (0, core_bentley_1.dispose)(this.idsAndAltZComposite); } } enableMultiSampling(textures, depth, depthMS) { this.opaqueColor = (0, core_bentley_1.dispose)(this.opaqueColor); this.opaqueAndCompositeColor = (0, core_bentley_1.dispose)(this.opaqueAndCompositeColor); let rVal = this.initPotentialMSFbos(textures, depth, depthMS); this.opaqueAll = (0, core_bentley_1.dispose)(this.opaqueAll); this.opaqueAndCompositeAll = (0, core_bentley_1.dispose)(this.opaqueAndCompositeAll); rVal = this.initPotentialMSMRTFbos(textures, depth, depthMS); return rVal; } disableMultiSampling(textures, depth) { this.opaqueAll = (0, core_bentley_1.dispose)(this.opaqueAll); this.opaqueAndCompositeAll = (0, core_bentley_1.dispose)(this.opaqueAndCompositeAll); if (!this.initPotentialMSMRTFbos(textures, depth, undefined)) return false; this.opaqueColor = (0, core_bentley_1.dispose)(this.opaqueColor); this.opaqueAndCompositeColor = (0, core_bentley_1.dispose)(this.opaqueAndCompositeColor); return this.initPotentialMSFbos(textures, depth, undefined); } get isDisposed() { return undefined === this.opaqueColor && undefined === this.opaqueAndCompositeColor && undefined === this.depthAndOrder && undefined === this.contours && undefined === this.hilite && undefined === this.hiliteUsingStencil && undefined === this.occlusion && undefined === this.occlusionBlur && undefined === this.stencilSet && undefined === this.altZOnly && undefined === this.volClassCreateBlend && undefined === this.volClassCreateBlendAltZ && undefined === this.opaqueAll && undefined === this.opaqueAndCompositeAll && undefined === this.opaqueAndCompositeAllHidden && undefined === this.pingPong && undefined === this.pingPongMS && undefined === this.translucent && undefined === this.clearTranslucent && undefined === this.idsAndZ && undefined === this.idsAndAltZ && undefined === this.idsAndZComposite && undefined === this.idsAndAltZComposite && undefined === this.edlDrawCol; } [Symbol.dispose]() { this.opaqueColor = (0, core_bentley_1.dispose)(this.opaqueColor); this.opaqueAndCompositeColor = (0, core_bentley_1.dispose)(this.opaqueAndCompositeColor); this.depthAndOrder = (0, core_bentley_1.dispose)(this.depthAndOrder); this.contours = (0, core_bentley_1.dispose)(this.contours); this.hilite = (0, core_bentley_1.dispose)(this.hilite); this.hiliteUsingStencil = (0, core_bentley_1.dispose)(this.hiliteUsingStencil); this.occlusion = (0, core_bentley_1.dispose)(this.occlusion); this.occlusionBlur = (0, core_bentley_1.dispose)(this.occlusionBlur); this.stencilSet = (0, core_bentley_1.dispose)(this.stencilSet); this.altZOnly = (0, core_bentley_1.dispose)(this.altZOnly); this.volClassCreateBlend = (0, core_bentley_1.dispose)(this.volClassCreateBlend); this.volClassCreateBlendAltZ = (0, core_bentley_1.dispose)(this.volClassCreateBlendAltZ); this.opaqueAll = (0, core_bentley_1.dispose)(this.opaqueAll); this.opaqueAndCompositeAll = (0, core_bentley_1.dispose)(this.opaqueAndCompositeAll); this.opaqueAndCompositeAll = (0, core_bentley_1.dispose)(this.opaqueAndCompositeAllHidden); this.pingPong = (0, core_bentley_1.dispose)(this.pingPong); this.pingPongMS = (0, core_bentley_1.dispose)(this.pingPongMS); this.translucent = (0, core_bentley_1.dispose)(this.translucent); this.clearTranslucent = (0, core_bentley_1.dispose)(this.clearTranslucent); this.idsAndZ = (0, core_bentley_1.dispose)(this.idsAndZ); this.idsAndAltZ = (0, core_bentley_1.dispose)(this.idsAndAltZ); this.idsAndZComposite = (0, core_bentley_1.dispose)(this.idsAndZComposite); this.idsAndAltZComposite = (0, core_bentley_1.dispose)(this.idsAndAltZComposite); this.edlDrawCol = (0, core_bentley_1.dispose)(this.edlDrawCol); } } function collectGeometryStatistics(geom, stats) { if (undefined !== geom) geom.collectStatistics(stats); } // Maintains the geometry used to execute screenspace operations for a SceneCompositor. class Geometry { composite; volClassColorStencil; volClassCopyZ; volClassSetBlend; volClassBlend; occlusion; occlusionXBlur; occlusionYBlur; copyPickBuffers; clearTranslucent; clearPickAndColor; collectStatistics(stats) { collectGeometryStatistics(this.composite, stats); collectGeometryStatistics(this.volClassColorStencil, stats); collectGeometryStatistics(this.volClassCopyZ, stats); collectGeometryStatistics(this.volClassSetBlend, stats); collectGeometryStatistics(this.volClassBlend, stats); collectGeometryStatistics(this.occlusion, stats); collectGeometryStatistics(this.occlusionXBlur, stats); collectGeometryStatistics(this.occlusionYBlur, stats); collectGeometryStatistics(this.copyPickBuffers, stats); collectGeometryStatistics(this.clearTranslucent, stats); collectGeometryStatistics(this.clearPickAndColor, stats); } init(textures) { (0, core_bentley_1.assert)(undefined === this.composite); this.composite = CachedGeometry_1.CompositeGeometry.createGeometry(textures.color.getHandle(), textures.accumulation.getHandle(), textures.revealage.getHandle(), textures.hilite.getHandle()); if (undefined === this.composite) return false; (0, core_bentley_1.assert)(undefined === this.copyPickBuffers); this.copyPickBuffers = CachedGeometry_1.CopyPickBufferGeometry.createGeometry(textures.featureId.getHandle(), textures.depthAndOrder.getHandle()); this.clearTranslucent = CachedGeometry_1.ViewportQuadGeometry.create(16 /* TechniqueId.OITClearTranslucent */); this.clearPickAndColor = CachedGeometry_1.ViewportQuadGeometry.create(21 /* TechniqueId.ClearPickAndColor */); return undefined !== this.copyPickBuffers && undefined !== this.clearTranslucent && undefined !== this.clearPickAndColor; } enableOcclusion(textures, depth) { (0, core_bentley_1.assert)(undefined !== textures.occlusion && undefined !== textures.occlusionBlur && undefined !== textures.depthAndOrder && undefined !== textures.occlusionBlur); this.composite.occlusion = textures.occlusion.getHandle(); this.occlusion = CachedGeometry_1.AmbientOcclusionGeometry.createGeometry(textures.depthAndOrder.getHandle(), depth.getHandle()); this.occlusionXBlur = CachedGeometry_1.BlurGeometry.createGeometry(textures.occlusion.getHandle(), textures.depthAndOrder.getHandle(), undefined, new core_geometry_1.Vector2d(1.0, 0.0), CachedGeometry_1.BlurType.NoTest); const depthAndOrderHidden = (undefined === textures.depthAndOrderHidden ? textures.revealage?.getHandle() : textures.depthAndOrderHidden.getHandle()); this.occlusionYBlur = CachedGeometry_1.BlurGeometry.createGeometry(textures.occlusionBlur.getHandle(), textures.depthAndOrder.getHandle(), depthAndOrderHidden, new core_geometry_1.Vector2d(0.0, 1.0), CachedGeometry_1.BlurType.TestOrder); } disableOcclusion() { this.composite.occlusion = undefined; this.occlusion = (0, core_bentley_1.dispose)(this.occlusion); this.occlusionXBlur = (0, core_bentley_1.dispose)(this.occlusionXBlur); this.occlusionYBlur = (0, core_bentley_1.dispose)(this.occlusionYBlur); } enableVolumeClassifier(textures, depth) { (0, core_bentley_1.assert)(undefined === this.volClassColorStencil && undefined === this.volClassCopyZ && undefined === this.volClassSetBlend && undefined === this.volClassBlend); this.volClassColorStencil = CachedGeometry_1.ViewportQuadGeometry.create(20 /* TechniqueId.VolClassColorUsingStencil */); this.volClassCopyZ = CachedGeometry_1.SingleTexturedViewportQuadGeometry.createGeometry(depth.getHandle(), 31 /* TechniqueId.VolClassCopyZ */); this.volClassSetBlend = CachedGeometry_1.VolumeClassifierGeometry.createVCGeometry(depth.getHandle()); this.volClassBlend = CachedGeometry_1.SingleTexturedViewportQuadGeometry.createGeometry(textures.volClassBlend.getHandle(), 33 /* TechniqueId.VolClassBlend */); return undefined !== this.volClassColorStencil && undefined !== this.volClassCopyZ && undefined !== this.volClassSetBlend && undefined !== this.volClassBlend; } disableVolumeClassifier() { if (undefined !== this.volClassColorStencil) { this.volClassColorStencil = (0, core_bentley_1.dispose)(this.volClassColorStencil); this.volClassCopyZ = (0, core_bentley_1.dispose)(this.volClassCopyZ); this.volClassSetBlend = (0, core_bentley_1.dispose)(this.volClassSetBlend); this.volClassBlend = (0, core_bentley_1.dispose)(this.volClassBlend); } } get isDisposed() { return undefined === this.composite && undefined === this.occlusion && undefined === this.occlusionXBlur && undefined === this.occlusionYBlur && undefined === this.volClassColorStencil && undefined === this.volClassCopyZ && undefined === this.volClassSetBlend && undefined === this.volClassBlend && undefined === this.copyPickBuffers && undefined === this.clearTranslucent && undefined === this.clearPickAndColor; } [Symbol.dispose]() { this.composite = (0, core_bentley_1.dispose)(this.composite); this.occlusion = (0, core_bentley_1.dispose)(this.occlusion); this.occlusionXBlur = (0, core_bentley_1.dispose)(this.occlusionXBlur); this.occlusionYBlur = (0, core_bentley_1.dispose)(this.occlusionYBlur); this.disableVolumeClassifier(); this.copyPickBuffers = (0, core_bentley_1.dispose)(this.copyPickBuffers); this.clearTranslucent = (0, core_bentley_1.dispose)(this.clearTranslucent); this.clearPickAndColor = (0, core_bentley_1.dispose)(this.clearPickAndColor); } } // Represents a view of data read from a region of the frame buffer. class PixelBuffer { _rect; _selector; _featureId; _depthAndOrder; _contours; _batchState; _scratchModelFeature = core_common_1.ModelFeature.create(); get _numPixels() { return this._rect.width * this._rect.height; } getPixelIndex(x, y) { if (x < this._rect.left || y < this._rect.top) return this._numPixels; x -= this._rect.left; y -= this._rect.top; if (x >= this._rect.width || y >= this._rect.height) return this._numPixels; // NB: View coords have origin at top-left; GL at bottom-left. So our rows are upside-down. y = this._rect.height - 1 - y; return y * this._rect.width + x; } getPixel32(data, pixelIndex) { return pixelIndex < data.length ? data[pixelIndex] : undefined; } getFeature(pixelIndex, result) { const featureId = this.getFeatureId(pixelIndex); return undefined !== featureId ? this._batchState.getFeature(featureId, result) : undefined; } getFeatureId(pixelIndex) { return undefined !== this._featureId ? this.getPixel32(this._featureId, pixelIndex) : undefined; } getBatchInfo(pixelIndex) { const featureId = this.getFeatureId(pixelIndex); if (undefined !== featureId) { const batch = this._batchState.find(featureId); if (undefined !== batch) { return { featureTable: batch.featureTable, iModel: batch.batchIModel, transformFromIModel: batch.transformFromBatchIModel, tileId: batch.tileId, viewAttachmentId: batch.viewAttachmentId, inSectionDrawingAttachment: batch.inSectionDrawingAttachment, }; } } return undefined; } _scratchUint32Array = new Uint32Array(1); _scratchUint8Array = new Uint8Array(this._scratchUint32Array.buffer); _scratchVector3d = new core_geometry_1.Vector3d(); _mult = new core_geometry_1.Vector3d(1.0, 1.0 / 255.0, 1.0 / 65025.0); decodeDepthRgba(depthAndOrder) { this._scratchUint32Array[0] = depthAndOrder; const bytes = this._scratchUint8Array; const fpt = core_geometry_1.Vector3d.create(bytes[1] / 255.0, bytes[2] / 255.0, bytes[3] / 255.0, this._scratchVector3d); let depth = fpt.dotProduct(this._mult); (0, core_bentley_1.assert)(0.0 <= depth); (0, core_bentley_1.assert)(1.01 >= depth); // rounding error... depth = Math.min(1.0, depth); depth = Math.max(0.0, depth); return depth; } decodeRenderOrderRgba(depthAndOrder) { return this.decodeUint8(depthAndOrder, 16); } decodeUint8(rgba32, basis) { this._scratchUint32Array[0] = rgba32; const encByte = this._scratchUint8Array[0]; const enc = encByte / 255.0; const dec = Math.floor(basis * enc + 0.5); return dec; } _invalidPixelData = new Pixel_1.Pixel.Data(); getPixel(x, y) { const px = this._invalidPixelData; const index = this.getPixelIndex(x, y); if (index >= this._numPixels) return px; // Initialize to the defaults... let distanceFraction = px.distanceFraction; let geometryType = px.type; let planarity = px.planarity; const haveFeatureIds = Pixel_1.Pixel.Selector.None !== (this._selector & Pixel_1.Pixel.Selector.Feature); const feature = haveFeatureIds ? this.getFeature(index, this._scratchModelFeature) : undefined; const batchInfo = haveFeatureIds ? this.getBatchInfo(index) : undefined; if (Pixel_1.Pixel.Selector.None !== (this._selector & Pixel_1.Pixel.Selector.GeometryAndDistance) && undefined !== this._depthAndOrder) { const depthAndOrder = this.getPixel32(this._depthAndOrder, index); if (undefined !== depthAndOrder) { distanceFraction = this.decodeDepthRgba(depthAndOrder); const orderWithPlanarBit = this.decodeRenderOrderRgba(depthAndOrder); const order = orderWithPlanarBit & ~8 /* RenderOrder.PlanarBit */; planarity = (orderWithPlanarBit === order) ? Pixel_1.Pixel.Planarity.NonPlanar : Pixel_1.Pixel.Planarity.Planar; switch (order) { case 0 /* RenderOrder.None */: geometryType = Pixel_1.Pixel.GeometryType.None; planarity = Pixel_1.Pixel.Planarity.None; break; case 1 /* RenderOrder.Background */: case 2 /* RenderOrder.BlankingRegion */: case 4 /* RenderOrder.LitSurface */: case 3 /* RenderOrder.UnlitSurface */: geometryType = Pixel_1.Pixel.GeometryType.Surface; break; case 5 /* RenderOrder.Linear */: geometryType = Pixel_1.Pixel.GeometryType.Linear; break; case 6 /* RenderOrder.Edge */: geometryType = Pixel_1.Pixel.GeometryType.Edge; break; case 7 /* RenderOrder.Silhouette */: geometryType = Pixel_1.Pixel.GeometryType.Silhouette; break; default: // ###TODO: may run into issues with point clouds - they are not written correctly in C++. (0, core_bentley_1.assert)(false, "Invalid render order"); geometryType = Pixel_1.Pixel.GeometryType.None; planarity = Pixel_1.Pixel.Planarity.None; break; } } } let contour; if (this._contours) { const contour32 = this.getPixel32(this._contours.data, index); if (contour32) { // undefined means out of bounds; zero means not a contour. const groupIndexAndType = this.decodeUint8(contour32, 32); const groupIndex = groupIndexAndType & ~(8 | 16); const group = this._contours.display.groups[groupIndex]; if (group) { const elevationFraction = this.decodeDepthRgba(contour32); let elevation = elevationFraction * (this._contours.zHigh - this._contours.zLow) + this._contours.zLow; // The shader rounds to the nearest contour elevation using single-precision arithmetic. // Re-round here using double-precision to get closer. const interval = group.contourDef.minorInterval; elevation = (elevation >= 0 ? Math.floor((elevation + interval / 2) / interval) : Math.ceil((elevation - interval / 2) / interval)) * interval; contour = { group, elevation, isMajor: groupIndexAndType > 15, }; } } } let featureTable, iModel, transformToIModel, tileId, viewAttachmentId, inSectionDrawingAttachment; if (undefined !== batchInfo) { featureTable = batchInfo.featureTable; iModel = batchInfo.iModel; transformToIModel = batchInfo.transformFromIModel; tileId = batchInfo.tileId; viewAttachmentId = batchInfo.viewAttachmentId; inSectionDrawingAttachment = batchInfo.inSectionDrawingAttachment; } return new Pixel_1.Pixel.Data({ feature, distanceFraction, type: geometryType, planarity, batchType: featureTable?.type, iModel, transformFromIModel: transformToIModel, tileId, viewAttachmentId, inSectionDrawingAttachment, contour, }); } constructor(rect, selector, compositor) { this._rect = rect.clone(); this._selector = selector; this._batchState = compositor.target.uniforms.batch.state; if (Pixel_1.Pixel.Selector.None !== (selector & Pixel_1.Pixel.Selector.GeometryAndDistance)) { const depthAndOrderBytes = compositor.readDepthAndOrder(rect); if (undefined !== depthAndOrderBytes) this._depthAndOrder = new Uint32Array(depthAndOrderBytes.buffer); else this._selector &= ~Pixel_1.Pixel.Selector.GeometryAndDistance; } if (Pixel_1.Pixel.Selector.None !== (selector & Pixel_1.Pixel.Selector.Feature)) { const features = compositor.readFeatureIds(rect); if (undefined !== features) this._featureId = new Uint32Array(features.buffer); else this._selector &= ~Pixel_1.Pixel.Selector.Feature; } // Note: readContours is a no-op unless contours are actually being drawn. if (Pixel_1.Pixel.Selector.None !== (selector & Pixel_1.Pixel.Selector.Contours)) { this._contours = compositor.readContours(rect); } } get isEmpty() { return Pixel_1.Pixel.Selector.None === this._selector; } static create(rect, selector, compositor) { const pdb = new PixelBuffer(rect, selector, compositor); return pdb.isEmpty ? undefined : pdb; } } /** Orchestrates rendering of the scene on behalf of a Target. * This base class exists only so we don't have to export all the types of the shared Compositor members like Textures, FrameBuffers, etc. * @internal */ class SceneCompositor { target; solarShadowMap; eyeDomeLighting; _needHiddenEdges; get needHiddenEdges() { return this._needHiddenEdges; } constructor(target) { this.target = target; this.solarShadowMap = new SolarShadowMap_1.SolarShadowMap(target); this.eyeDomeLighting = new EDL_1.EyeDomeLighting(target); this._needHiddenEdges = false; } static create(target) { return new Compositor(target); } } exports.SceneCompositor = SceneCompositor; // This describes what types of primitives a compositor should draw. See the `drawPrimitive` method of Compositor. var PrimitiveDrawState; (function (PrimitiveDrawState) { PrimitiveDrawState[PrimitiveDrawState["Both"] = 0] = "Both"; PrimitiveDrawState[PrimitiveDrawState["Pickable"] = 1] = "Pickable"; PrimitiveDrawState[PrimitiveDrawState["NonPickable"] = 2] = "NonPickable"; })(PrimitiveDrawState || (PrimitiveDrawState = {})); // The actual base class. Specializations are provided based on whether or not multiple render targets are supported. class Compositor extends SceneCompositor { _width = -1; _height = -1; _includeOcclusion = false; _textures = new Textures(); _depth; _depthMS; // multisample depth buffer _fbos; _geom; _readPickDataFromPingPong = true; _opaqueRenderState = new RenderState_1.RenderState(); _layerRenderState = new RenderState_1.RenderState(); _translucentRenderState = new RenderState_1.RenderState(); _hiliteRenderState = new RenderState_1.RenderState(); _noDepthMaskRenderState = new RenderState_1.RenderState(); _backgroundMapRenderState = new RenderState_1.RenderState(); _pointCloudRenderState = new RenderState_1.RenderState(); _debugStencil = 0; // 0 to draw stencil volumes normally, 1 to draw as opaque, 2 to draw blended _vcBranchState; _vcSetStencilRenderState; _vcCopyZRenderState; _vcColorRenderState; _vcBlendRenderState; _vcPickDataRenderState; _vcDebugRenderState; _vcAltDepthStencil; _vcAltDepthStencilMS; _haveVolumeClassifier = false; _antialiasSamples = 1; _viewProjectionMatrix = new Matrix_1.Matrix4(); _primitiveDrawState = PrimitiveDrawState.Both; // used by drawPrimitive to decide whether a primitive needs to be drawn. forceBufferChange() { this._width = this._height = -1; } get featureIds() { return this.getSamplerTexture(this._readPickDataFromPingPong ? 0 : 1); } get depthAndOrder() { return this.getSamplerTexture(this._readPickDataFromPingPong ? 1 : 2); } get _samplerFbo() { return this._readPickDataFromPingPong ? this._fbos.pingPong : this._fbos.opaqueAll; } getSamplerTexture(index) { return this._samplerFbo.getColor(index); } drawPrimitive(primitive, exec, outputsToPick) { if ((outputsToPick && this._primitiveDrawState !== PrimitiveDrawState.NonPickable) || (!outputsToPick && this._primitiveDrawState !== PrimitiveDrawState.Pickable)) primitive.draw(exec); } clearOpaque(needComposite) { const fbo = needComposite ? this._fbos.opaqueAndCompositeAll : this._fbos.opaqueAll; const system = System_1.System.instance; system.frameBufferStack.execute(fbo, true, this.useMsBuffers, () => { // Clear pick data buffers to 0's and color buffer to background color // (0,0,0,0) in elementID0 and ElementID1 buffers indicates invalid element id // (0,0,0,0) in DepthAndOrder buffer indicates render order 0 and encoded depth of 0 (= far plane) system.applyRenderState(this._noDepthMaskRenderState); const params = (0, ScratchDrawParams_1.getDrawParams)(this.target, this._geom.clearPickAndColor); this.target.techniques.draw(params); // Clear depth buffer system.applyRenderState(RenderState_1.RenderState.defaults); // depthMask == true. system.context.clearDepth(1.0); system.context.clear(GL_1.GL.BufferBit.Depth); }); } renderLayers(commands, needComposite, pass) { const fbo = (needComposite ? this._fbos.opaqueAndCompositeAll : this._fbos.opaqueAll); const useMsBuffers = 1 /* RenderPass.OpaqueLayers */ === pass && fbo.isMultisampled && this.useMsBuffers; this._readPickDataFromPingPong = !useMsBuffers; System_1.System.instance.frameBufferStack.execute(fbo, true, useMsBuffers, () => { this.drawPass(commands, pass, true); }); this._readPickDataFromPingPong = false; } renderOpaque(commands, compositeFlags, renderForReadPixels) { if (0 /* CompositeFlags.None */ !== (compositeFlags & 4 /* CompositeFlags.AmbientOcclusion */) && !renderForReadPixels) { this.renderOpaqueAO(commands); return; } const needComposite = 0 /* CompositeFlags.None */ !== compositeFlags; const fbStack = System_1.System.instance.frameBufferStack; // Output the first 2 passes to color and pick data buffers. (All 3 in the case of rendering for readPixels() or ambient occlusion). let fbo = (needComposite ? this._fbos.opaqueAndCompositeAll : this._fbos.opaqueAll); const useMsBuffers = fbo.isMultisampled && this.useMsBuffers; this._readPickDataFromPingPong = !useMsBuffers; // if multisampling then can read pick textures directly.