@itwin/core-frontend
Version:
iTwin.js frontend components
208 lines • 9.85 kB
JavaScript
"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