"use strict";
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.EyeDomeLighting = exports.EDLMode = void 0;
/** @packageDocumentation
 * @module WebGL
 */
const core_bentley_1 = require("@itwin/core-bentley");
const CachedGeometry_1 = require("./CachedGeometry");
const FrameBuffer_1 = require("./FrameBuffer");
const GL_1 = require("./GL");
const RenderState_1 = require("./RenderState");
const SceneCompositor_1 = require("./SceneCompositor");
const ScratchDrawParams_1 = require("./ScratchDrawParams");
const System_1 = require("./System");
const Texture_1 = require("./Texture");
class Bundle {
    edlCalcTex1;
    edlCalcTex2;
    edlCalcTex4;
    edlFiltTex2;
    edlFiltTex4;
    edlCalcFbo1;
    edlCalcFbo2;
    edlCalcFbo4;
    edlFiltFbo2;
    edlFiltFbo4;
    edlCalcBasicGeom;
    edlCalcFullGeom;
    edlFiltGeom;
    edlMixGeom;
    constructor(edlCalcTex1, edlCalcTex2, edlCalcTex4, edlFiltTex2, edlFiltTex4, edlCalcFbo1, edlCalcFbo2, edlCalcFbo4, edlFiltFbo2, edlFiltFbo4, edlCalcBasicGeom, edlCalcFullGeom, edlFiltGeom, edlMixGeom) {
        this.edlCalcTex1 = edlCalcTex1;
        this.edlCalcTex2 = edlCalcTex2;
        this.edlCalcTex4 = edlCalcTex4;
        this.edlFiltTex2 = edlFiltTex2;
        this.edlFiltTex4 = edlFiltTex4;
        this.edlCalcFbo1 = edlCalcFbo1;
        this.edlCalcFbo2 = edlCalcFbo2;
        this.edlCalcFbo4 = edlCalcFbo4;
        this.edlFiltFbo2 = edlFiltFbo2;
        this.edlFiltFbo4 = edlFiltFbo4;
        this.edlCalcBasicGeom = edlCalcBasicGeom;
        this.edlCalcFullGeom = edlCalcFullGeom;
        this.edlFiltGeom = edlFiltGeom;
        this.edlMixGeom = edlMixGeom;
    }
    static create(width, height) {
        const edlCalcTex1 = Texture_1.TextureHandle.createForAttachment(width, height, GL_1.GL.Texture.Format.Rgba, GL_1.GL.Texture.DataType.UnsignedByte);
        const edlCalcTex2 = Texture_1.TextureHandle.createForAttachment(width >> 1, height >> 1, GL_1.GL.Texture.Format.Rgba, GL_1.GL.Texture.DataType.UnsignedByte);
        const edlCalcTex4 = Texture_1.TextureHandle.createForAttachment(width >> 2, height >> 2, GL_1.GL.Texture.Format.Rgba, GL_1.GL.Texture.DataType.UnsignedByte);
        const edlFiltTex2 = Texture_1.TextureHandle.createForAttachment(width >> 1, height >> 1, GL_1.GL.Texture.Format.Rgba, GL_1.GL.Texture.DataType.UnsignedByte);
        const edlFiltTex4 = Texture_1.TextureHandle.createForAttachment(width >> 2, height >> 2, GL_1.GL.Texture.Format.Rgba, GL_1.GL.Texture.DataType.UnsignedByte);
        if (undefined === edlCalcTex1 || undefined === edlCalcTex2 || undefined === edlCalcTex4 || undefined === edlFiltTex2 || undefined === edlFiltTex4) {
            (0, core_bentley_1.dispose)(edlCalcTex1);
            (0, core_bentley_1.dispose)(edlCalcTex2);
            (0, core_bentley_1.dispose)(edlCalcTex4);
            (0, core_bentley_1.dispose)(edlFiltTex2);
            (0, core_bentley_1.dispose)(edlFiltTex4);
            return undefined;
        }
        const edlCalcFbo1 = FrameBuffer_1.FrameBuffer.create([edlCalcTex1]);
        const edlCalcFbo2 = FrameBuffer_1.FrameBuffer.create([edlCalcTex2]);
        const edlCalcFbo4 = FrameBuffer_1.FrameBuffer.create([edlCalcTex4]);
        const edlFiltFbo2 = FrameBuffer_1.FrameBuffer.create([edlFiltTex2]);
        const edlFiltFbo4 = FrameBuffer_1.FrameBuffer.create([edlFiltTex4]);
        if (undefined === edlCalcFbo1 || undefined === edlCalcFbo2 || undefined === edlCalcFbo4 || undefined === edlFiltFbo2 || undefined === edlFiltFbo4) {
            (0, core_bentley_1.dispose)(edlCalcFbo1);
            (0, core_bentley_1.dispose)(edlCalcFbo2);
            (0, core_bentley_1.dispose)(edlCalcFbo4);
            (0, core_bentley_1.dispose)(edlFiltFbo2);
            (0, core_bentley_1.dispose)(edlFiltFbo4);
            return undefined;
        }
        return new Bundle(edlCalcTex1, edlCalcTex2, edlCalcTex4, edlFiltTex2, edlFiltTex4, edlCalcFbo1, edlCalcFbo2, edlCalcFbo4, edlFiltFbo2, edlFiltFbo4);
    }
    get isDisposed() {
        return undefined === this.edlCalcTex1
            && undefined === this.edlCalcTex2
            && undefined === this.edlCalcTex4
            && undefined === this.edlFiltTex2
            && undefined === this.edlFiltTex4
            && undefined === this.edlCalcFbo1
            && undefined === this.edlCalcFbo2
            && undefined === this.edlCalcFbo4
            && undefined === this.edlFiltFbo2
            && undefined === this.edlFiltFbo4
            && undefined === this.edlCalcBasicGeom
            && undefined === this.edlCalcFullGeom?.[0]
            && undefined === this.edlCalcFullGeom?.[1]
            && undefined === this.edlCalcFullGeom?.[2]
            && undefined === this.edlCalcFullGeom
            && undefined === this.edlFiltGeom?.[0]
            && undefined === this.edlFiltGeom?.[1]
            && undefined === this.edlFiltGeom
            && undefined === this.edlMixGeom;
    }
    [Symbol.dispose]() {
        this.edlCalcTex1 = (0, core_bentley_1.dispose)(this.edlCalcTex1);
        this.edlCalcTex2 = (0, core_bentley_1.dispose)(this.edlCalcTex2);
        this.edlCalcTex4 = (0, core_bentley_1.dispose)(this.edlCalcTex4);
        this.edlFiltTex2 = (0, core_bentley_1.dispose)(this.edlFiltTex2);
        this.edlFiltTex4 = (0, core_bentley_1.dispose)(this.edlFiltTex4);
        this.edlCalcFbo1 = (0, core_bentley_1.dispose)(this.edlCalcFbo1);
        this.edlCalcFbo2 = (0, core_bentley_1.dispose)(this.edlCalcFbo2);
        this.edlCalcFbo4 = (0, core_bentley_1.dispose)(this.edlCalcFbo4);
        this.edlFiltFbo2 = (0, core_bentley_1.dispose)(this.edlFiltFbo2);
        this.edlFiltFbo4 = (0, core_bentley_1.dispose)(this.edlFiltFbo4);
        this.edlCalcBasicGeom = (0, core_bentley_1.dispose)(this.edlCalcBasicGeom);
        if (this.edlCalcFullGeom) {
            this.edlCalcFullGeom[0] = (0, core_bentley_1.dispose)(this.edlCalcFullGeom?.[0]);
            this.edlCalcFullGeom[1] = (0, core_bentley_1.dispose)(this.edlCalcFullGeom?.[1]);
            this.edlCalcFullGeom[2] = (0, core_bentley_1.dispose)(this.edlCalcFullGeom?.[2]);
            this.edlCalcFullGeom = undefined;
        }
        if (this.edlFiltGeom) {
            this.edlFiltGeom[0] = (0, core_bentley_1.dispose)(this.edlFiltGeom?.[0]);
            this.edlFiltGeom[1] = (0, core_bentley_1.dispose)(this.edlFiltGeom?.[1]);
            this.edlFiltGeom = undefined;
        }
        this.edlMixGeom = (0, core_bentley_1.dispose)(this.edlMixGeom);
    }
}
/** @internal */
var EDLMode;
(function (EDLMode) {
    EDLMode[EDLMode["Off"] = 0] = "Off";
    EDLMode[EDLMode["On"] = 1] = "On";
    EDLMode[EDLMode["Full"] = 2] = "Full";
})(EDLMode || (exports.EDLMode = EDLMode = {}));
class EyeDomeLighting {
    _bundle;
    _width;
    _height;
    _depth; // depth buffer to read from, has to be non-MS and be up to date in draw if MS is used
    _edlFinalFbo;
    _edlFinalBufs;
    _target;
    getBundle() {
        if (undefined === this._bundle) {
            this._bundle = Bundle.create(this._width, this._height);
            (0, core_bentley_1.assert)(undefined !== this._bundle);
        }
        return this._bundle;
    }
    constructor(target) {
        this._target = target;
        this._width = target.viewRect.width;
        this._height = target.viewRect.height;
    }
    init(width, height, depth) {
        this._width = width;
        this._height = height;
        this._depth = depth;
        // don't create buffers until we know we will use them (first draw)
        return true;
    }
    collectStatistics(stats) {
        const bundle = this._bundle;
        if (undefined !== bundle) {
            (0, SceneCompositor_1.collectTextureStatistics)(bundle.edlCalcTex1, stats);
            (0, SceneCompositor_1.collectTextureStatistics)(bundle.edlCalcTex2, stats);
            (0, SceneCompositor_1.collectTextureStatistics)(bundle.edlCalcTex4, stats);
            (0, SceneCompositor_1.collectTextureStatistics)(bundle.edlFiltTex2, stats);
            (0, SceneCompositor_1.collectTextureStatistics)(bundle.edlFiltTex4, stats);
            (0, SceneCompositor_1.collectGeometryStatistics)(bundle.edlCalcBasicGeom, stats);
            (0, SceneCompositor_1.collectGeometryStatistics)(bundle.edlCalcFullGeom?.[0], stats);
            (0, SceneCompositor_1.collectGeometryStatistics)(bundle.edlCalcFullGeom?.[1], stats);
            (0, SceneCompositor_1.collectGeometryStatistics)(bundle.edlCalcFullGeom?.[2], stats);
            (0, SceneCompositor_1.collectGeometryStatistics)(bundle.edlFiltGeom?.[0], stats);
            (0, SceneCompositor_1.collectGeometryStatistics)(bundle.edlFiltGeom?.[1], stats);
            (0, SceneCompositor_1.collectGeometryStatistics)(bundle.edlMixGeom, stats);
        }
    }
    get isDisposed() { return undefined === this._bundle && undefined === this._edlFinalFbo; }
    [Symbol.dispose]() {
        this._bundle = (0, core_bentley_1.dispose)(this._bundle);
        this._edlFinalFbo = (0, core_bentley_1.dispose)(this._edlFinalFbo);
    }
    reset() {
        this[Symbol.dispose]();
    }
    /** calculate EyeDomeLighting at specified quality using screen space shaders
     * returns true if succeeds
     */
    draw(edlParams) {
        if (undefined === edlParams.inputTex || undefined === this._depth || undefined === edlParams.curFbo)
            return false;
        const bundle = this.getBundle();
        if (undefined === bundle)
            return false;
        // NB: have to test and create MS buffer as well if useMsBuffers, not outputting to depth
        const finalBufs = edlParams.curFbo.getColorTargets(edlParams.useMsBuffers, 0);
        if (undefined === this._edlFinalFbo || this._edlFinalBufs?.tex !== finalBufs.tex ||
            (edlParams.useMsBuffers && this._edlFinalBufs?.msBuf !== finalBufs.msBuf)) {
            this._edlFinalFbo = (0, core_bentley_1.dispose)(this._edlFinalFbo);
            this._edlFinalBufs = finalBufs;
            const filters = [GL_1.GL.MultiSampling.Filter.Linear];
            this._edlFinalFbo = FrameBuffer_1.FrameBuffer.create([this._edlFinalBufs.tex], undefined, edlParams.useMsBuffers && this._edlFinalBufs.msBuf ? [this._edlFinalBufs.msBuf] : undefined, filters, undefined);
            if (undefined === this._edlFinalFbo)
                return false;
        }
        const fbStack = System_1.System.instance.frameBufferStack;
        const useMsBuffers = edlParams.useMsBuffers;
        System_1.System.instance.applyRenderState(RenderState_1.RenderState.defaults);
        // ###TODO: should radius be (optionally?) voxel based instead of pixel here?
        if (edlParams.edlMode === EDLMode.On) {
            // draw using single pass version (still 8 samples, full size)
            fbStack.execute(this._edlFinalFbo, true, useMsBuffers, () => {
                if (bundle.edlCalcBasicGeom === undefined) {
                    const ct1 = edlParams.inputTex;
                    const ctd = (0, core_bentley_1.expectDefined)(this._depth?.getHandle());
                    bundle.edlCalcBasicGeom = CachedGeometry_1.EDLCalcBasicGeometry.createGeometry((0, core_bentley_1.expectDefined)(ct1.getHandle()), ctd, ct1.width, ct1.height);
                }
                const params = (0, ScratchDrawParams_1.getDrawParams)(this._target, (0, core_bentley_1.expectDefined)(bundle.edlCalcBasicGeom));
                this._target.techniques.draw(params);
            });
        }
        else { // EDLMode.Full
            // draw with full method based on original paper using full, 1/2, and 1/4 sizes
            const edlCalc2FB = [(0, core_bentley_1.expectDefined)(bundle.edlCalcFbo1), (0, core_bentley_1.expectDefined)(bundle.edlCalcFbo2), (0, core_bentley_1.expectDefined)(bundle.edlCalcFbo4)];
            if (bundle.edlCalcFullGeom === undefined) {
                const ct1 = edlParams.inputTex;
                const ct2 = bundle.edlCalcTex2;
                const ct4 = bundle.edlCalcTex4;
                const ctd = (0, core_bentley_1.expectDefined)(this._depth?.getHandle());
                bundle.edlCalcFullGeom = [CachedGeometry_1.EDLCalcFullGeometry.createGeometry((0, core_bentley_1.expectDefined)(ct1.getHandle()), ctd, 1, ct1.width, ct1.height),
                    CachedGeometry_1.EDLCalcFullGeometry.createGeometry((0, core_bentley_1.expectDefined)(ct1.getHandle()), ctd, 2, (0, core_bentley_1.expectDefined)(ct2?.width), (0, core_bentley_1.expectDefined)(ct2?.height)),
                    CachedGeometry_1.EDLCalcFullGeometry.createGeometry((0, core_bentley_1.expectDefined)(ct1.getHandle()), ctd, 4, (0, core_bentley_1.expectDefined)(ct4?.width), (0, core_bentley_1.expectDefined)(ct4?.height))];
            }
            const edlFiltFbos = [(0, core_bentley_1.expectDefined)(bundle.edlFiltFbo2), (0, core_bentley_1.expectDefined)(bundle.edlFiltFbo4)];
            if (bundle.edlFiltGeom === undefined) {
                const ft2 = bundle.edlCalcTex2;
                const ft4 = bundle.edlCalcTex4;
                const ftd = (0, core_bentley_1.expectDefined)(this._depth?.getHandle());
                bundle.edlFiltGeom = [CachedGeometry_1.EDLFilterGeometry.createGeometry((0, core_bentley_1.expectDefined)(ft2?.getHandle()), ftd, 2, (0, core_bentley_1.expectDefined)(ft2?.width), (0, core_bentley_1.expectDefined)(ft2?.height)),
                    CachedGeometry_1.EDLFilterGeometry.createGeometry((0, core_bentley_1.expectDefined)(ft4?.getHandle()), ftd, 4, (0, core_bentley_1.expectDefined)(ft4?.width), (0, core_bentley_1.expectDefined)(ft4?.height))];
            }
            const gl = System_1.System.instance.context;
            // Loop through the 3 sizes calculating an edl buffer, and if not first size, then optionally filtering those
            for (let i = 0; i < 3; ++i) {
                fbStack.execute(edlCalc2FB[i], true, false, () => {
                    const colTex = edlCalc2FB[i].getColor(0);
                    gl.viewport(0, 0, colTex.width, colTex.height); // have to set viewport to current texture size
                    const params = (0, ScratchDrawParams_1.getDrawParams)(this._target, (0, core_bentley_1.expectDefined)(bundle.edlCalcFullGeom?.[i]));
                    this._target.techniques.draw(params);
                });
                if (edlParams.edlFilter && i > 0) {
                    fbStack.execute(edlFiltFbos[i - 1], true, false, () => {
                        const params = (0, ScratchDrawParams_1.getDrawParams)(this._target, (0, core_bentley_1.expectDefined)(bundle.edlFiltGeom?.[i - 1]));
                        this._target.techniques.draw(params);
                    });
                }
            }
            gl.viewport(0, 0, this._width, this._height); // Restore viewport
            // Now combine the 3 results and output
            const tex1 = (0, core_bentley_1.expectDefined)(bundle.edlCalcTex1?.getHandle());
            const tex2 = (0, core_bentley_1.expectDefined)(edlParams.edlFilter ? bundle.edlFiltTex2?.getHandle() : bundle.edlCalcTex2?.getHandle());
            const tex4 = (0, core_bentley_1.expectDefined)(edlParams.edlFilter ? bundle.edlFiltTex4?.getHandle() : bundle.edlCalcTex4?.getHandle());
            fbStack.execute(this._edlFinalFbo, true, useMsBuffers, () => {
                if (bundle.edlMixGeom === undefined) {
                    bundle.edlMixGeom = CachedGeometry_1.EDLMixGeometry.createGeometry(tex1, tex2, tex4);
                }
                else {
                    if (bundle.edlMixGeom.colorTexture1 !== tex1 || bundle.edlMixGeom.colorTexture2 !== tex2 || bundle.edlMixGeom.colorTexture4 !== tex4) {
                        (0, core_bentley_1.dispose)(bundle.edlMixGeom);
                        bundle.edlMixGeom = CachedGeometry_1.EDLMixGeometry.createGeometry(tex1, tex2, tex4);
                    }
                }
                const params = (0, ScratchDrawParams_1.getDrawParams)(this._target, (0, core_bentley_1.expectDefined)(bundle.edlMixGeom));
                this._target.techniques.draw(params);
            });
        }
        return true;
    }
}
exports.EyeDomeLighting = EyeDomeLighting;
//# sourceMappingURL=EDL.js.map