UNPKG

@itwin/core-frontend

Version:
208 lines • 9.85 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.ScreenSpaceEffects = void 0; exports.createScreenSpaceEffectBuilder = createScreenSpaceEffectBuilder; const core_bentley_1 = require("@itwin/core-bentley"); const ScreenSpaceEffectBuilder_1 = require("../../../render/ScreenSpaceEffectBuilder"); const RenderState_1 = require("./RenderState"); const CachedGeometry_1 = require("./CachedGeometry"); const FrameBuffer_1 = require("./FrameBuffer"); const ScratchDrawParams_1 = require("./ScratchDrawParams"); const Technique_1 = require("./Technique"); const System_1 = require("./System"); const ScreenSpaceEffect_1 = require("./glsl/ScreenSpaceEffect"); function getUniformVariableType(type) { switch (type) { case ScreenSpaceEffectBuilder_1.UniformType.Bool: return 0 /* VariableType.Boolean */; case ScreenSpaceEffectBuilder_1.UniformType.Int: return 1 /* VariableType.Int */; case ScreenSpaceEffectBuilder_1.UniformType.Float: return 2 /* VariableType.Float */; case ScreenSpaceEffectBuilder_1.UniformType.Vec2: return 3 /* VariableType.Vec2 */; case ScreenSpaceEffectBuilder_1.UniformType.Vec3: return 4 /* VariableType.Vec3 */; case ScreenSpaceEffectBuilder_1.UniformType.Vec4: return 5 /* VariableType.Vec4 */; } } function getVaryingVariableType(type) { switch (type) { case ScreenSpaceEffectBuilder_1.VaryingType.Float: return 2 /* VariableType.Float */; case ScreenSpaceEffectBuilder_1.VaryingType.Vec2: return 3 /* VariableType.Vec2 */; case ScreenSpaceEffectBuilder_1.VaryingType.Vec3: return 4 /* VariableType.Vec3 */; case ScreenSpaceEffectBuilder_1.VaryingType.Vec4: return 5 /* VariableType.Vec4 */; } } class Builder { _name; _builder; _shiftsPixels; shouldApply; constructor(params) { this._name = params.name; this._builder = (0, ScreenSpaceEffect_1.createScreenSpaceEffectProgramBuilder)(params); this._builder.setDebugDescription(`Screen-space: ${this._name}`); this._shiftsPixels = undefined !== params.source.sampleSourcePixel; } get isWebGL2() { return true; } addUniform(params) { const name = params.name; const type = getUniformVariableType(params.type); const bind = params.bind; this._builder.addUniform(name, type, (prog) => { prog.addProgramUniform(name, (uniform, progParams) => { bind(uniform, progParams.target.screenSpaceEffectContext); }); }); } addUniformArray(params) { const { name, bind, length } = { ...params }; const type = getUniformVariableType(params.type); this._builder.addUniformArray(name, type, length, (prog) => { prog.addProgramUniform(name, (uniform, progParams) => { bind(uniform, progParams.target.screenSpaceEffectContext); }); }); } addVarying(name, type) { this._builder.addVarying(name, getVaryingVariableType(type)); } finish() { const program = this._builder.buildProgram(System_1.System.instance.context); // NB: compile() will throw with WebGL error log if compile/link fails. if (0 /* CompileStatus.Success */ !== program.compile()) throw new Error(`Failed to produce shader program for screen-space effect "${this._name}"`); const technique = new Technique_1.SingularTechnique(program); const techniqueId = System_1.System.instance.techniques.addDynamicTechnique(technique, this._name); const effect = new ScreenSpaceEffect(techniqueId, this._name, this._shiftsPixels, this.shouldApply); System_1.System.instance.screenSpaceEffects.add(effect); } } class ScreenSpaceEffect { techniqueId; name; _shouldApply; _shiftsPixels; constructor(techniqueId, name, shiftsPixels, shouldApply) { this.techniqueId = techniqueId; this.name = name; this._shouldApply = shouldApply; this._shiftsPixels = shiftsPixels; } shouldApply(target) { // Effects only apply during readPixels() if they move pixels around (we need to move pixels in the pick buffers correspondingly). if (target.isReadPixelsInProgress && !this._shiftsPixels) return false; return undefined === this._shouldApply || this._shouldApply(target.screenSpaceEffectContext); } } class ScreenSpaceGeometry extends CachedGeometry_1.ViewportQuadGeometry { setTechniqueId(id) { this._techniqueId = id; } } /** @internal */ class ScreenSpaceEffects { _effects = new Map(); _effectGeometry; _copyGeometry; _workingArray = []; constructor() { // We will change the geometry's TechniqueId before applying each technique. const effectGeometry = ScreenSpaceGeometry.create(-1 /* TechniqueId.Invalid */); (0, core_bentley_1.assert)(effectGeometry instanceof ScreenSpaceGeometry); this._effectGeometry = effectGeometry; // NB: We'll replace the texture each time we draw. const copyGeometry = CachedGeometry_1.SingleTexturedViewportQuadGeometry.createGeometry(System_1.System.instance.lineCodeTexture.getHandle(), 18 /* TechniqueId.CopyColor */); (0, core_bentley_1.assert)(undefined !== copyGeometry); this._copyGeometry = copyGeometry; } [Symbol.dispose]() { (0, core_bentley_1.dispose)(this._effectGeometry); (0, core_bentley_1.dispose)(this._copyGeometry); } add(effect) { if (undefined !== this._effects.get(effect.name)) throw new Error(`Screen-space effect "${effect.name}" is already registered.`); this._effects.set(effect.name, effect); } /** Return true if any effects should be applied to this Target. */ shouldApply(target) { return this.getApplicableEffects(target).length > 0; } getApplicableEffects(target) { const effects = this._workingArray; effects.length = 0; const names = target.screenSpaceEffects; for (const name of names) { const effect = this._effects.get(name); if (effect && effect.shouldApply(target)) effects.push(effect); } return effects; } /** Apply screen-space effects to the Target's rendered image. */ apply(target) { if (0 === this._effects.size) return; const effects = this.getApplicableEffects(target); if (0 === effects.length) return; if (target.isReadPixelsInProgress) { this.applyForReadPixels(effects, target); return; } const system = System_1.System.instance; system.applyRenderState(RenderState_1.RenderState.defaults); const copyFbo = target.compositor.screenSpaceEffectFbo; for (const effect of effects) { // Copy the rendered image to texture as input to the effect shader. this._copyGeometry.texture = system.frameBufferStack.currentColorBuffer.getHandle(); system.frameBufferStack.execute(copyFbo, true, false, () => { const copyParams = (0, ScratchDrawParams_1.getDrawParams)(target, this._copyGeometry); system.techniques.draw(copyParams); }); // Run the effect shader with a copy of the current image as input. this._effectGeometry.setTechniqueId(effect.techniqueId); const params = (0, ScratchDrawParams_1.getDrawParams)(target, this._effectGeometry); system.techniques.draw(params); } } applyForReadPixels(effects, target) { const system = System_1.System.instance; system.applyRenderState(RenderState_1.RenderState.defaults); // ###TODO: We could use MRT if available here rather than two passes. const copyFbo = target.compositor.screenSpaceEffectFbo; for (const effect of effects) { this._effectGeometry.setTechniqueId(effect.techniqueId); for (let i = 0; i <= 1; i++) { // Copy the pick buffer as input to the effect shader. const buffer = 0 === i ? target.compositor.featureIds : target.compositor.depthAndOrder; this._copyGeometry.texture = buffer.getHandle(); system.frameBufferStack.execute(copyFbo, true, false, () => { const copyParams = (0, ScratchDrawParams_1.getDrawParams)(target, this._copyGeometry); system.techniques.draw(copyParams); }); // Run the effect shader with a copy of the current pick buffer to output to pick buffer. // ###TODO: Avoid frequent framebuffer allocation. const effectFbo = FrameBuffer_1.FrameBuffer.create([buffer]); (0, core_bentley_1.assert)(undefined !== effectFbo); system.frameBufferStack.execute(effectFbo, true, false, () => { const effectParams = (0, ScratchDrawParams_1.getDrawParams)(target, this._effectGeometry); system.techniques.draw(effectParams); }); effectFbo[Symbol.dispose](); } } } } exports.ScreenSpaceEffects = ScreenSpaceEffects; function createScreenSpaceEffectBuilder(params) { return new Builder(params); } //# sourceMappingURL=ScreenSpaceEffect.js.map