UNPKG

@itwin/core-frontend

Version:
312 lines • 14.7 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.FrameBufferStack = exports.FrameBuffer = void 0; const core_bentley_1 = require("@itwin/core-bentley"); const GL_1 = require("./GL"); const RenderBuffer_1 = require("./RenderBuffer"); const System_1 = require("./System"); const Texture_1 = require("./Texture"); /** @internal */ class FrameBuffer { _fbo; _fboMs; _bindState = 0 /* FrameBufferBindState.Unbound */; _colorTextures = []; _colorMsBuffers = []; _colorAttachments = []; _colorMsFilters = []; depthBuffer; depthBufferMs; get isDisposed() { return this._fbo === undefined; } get isBound() { return 1 /* FrameBufferBindState.Bound */ <= this._bindState && 4 /* FrameBufferBindState.BoundWithAttachmentsMultisampled */ >= this._bindState; } get isBoundMultisampled() { return 3 /* FrameBufferBindState.BoundMultisampled */ === this._bindState || 4 /* FrameBufferBindState.BoundWithAttachmentsMultisampled */ === this._bindState; } get isSuspended() { return 5 /* FrameBufferBindState.Suspended */ === this._bindState; } get isMultisampled() { return this._colorMsBuffers.length > 0 || undefined !== this.depthBufferMs; } getColor(ndx) { (0, core_bentley_1.assert)(ndx < this._colorTextures.length); if (ndx < this._colorMsBuffers.length && this._colorMsBuffers[ndx].isDirty) { this.blitMsBuffersToTextures(false, ndx); } return this._colorTextures[ndx]; } getColorTargets(useMSBuffers, ndx) { let msBuf; if (useMSBuffers) { (0, core_bentley_1.assert)(ndx < this._colorMsBuffers.length); msBuf = this._colorMsBuffers[ndx]; } (0, core_bentley_1.assert)(ndx < this._colorTextures.length); return { tex: this._colorTextures[ndx], msBuf }; } constructor(fbo, colorTextures, depthBuffer, colorMsBuffers, msFilters, depthBufferMs) { this._fbo = fbo; const gl = System_1.System.instance.context; this.bind(false); let i = 0; for (const colTex of colorTextures) { const attachmentEnum = gl.COLOR_ATTACHMENT0 + i; this._colorAttachments.push(attachmentEnum); this._colorTextures.push(colTex); const texHandle = colTex.getHandle(); if (undefined !== texHandle) gl.framebufferTexture2D(gl.FRAMEBUFFER, attachmentEnum, gl.TEXTURE_2D, texHandle, 0); i++; } if (depthBuffer !== undefined) { this.depthBuffer = depthBuffer; const dbHandle = depthBuffer.getHandle(); if (undefined !== dbHandle) { if (depthBuffer instanceof RenderBuffer_1.RenderBuffer) { gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, dbHandle); } else if (depthBuffer instanceof RenderBuffer_1.RenderBufferMultiSample) { gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, dbHandle); } else { // Looks like we only get a 24 bit depth buffer anyway, so use a 24-8 with a stencil. // gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, dbHandle, 0); gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.TEXTURE_2D, dbHandle, 0); } } } this.unbind(); if (undefined !== colorMsBuffers && colorMsBuffers.length === colorTextures.length && undefined !== msFilters && msFilters.length === colorMsBuffers.length) { // Create a matching FBO with multisampling render buffers. const fbo2 = System_1.System.instance.context.createFramebuffer(); if (null !== fbo2) { this._fboMs = fbo2; this.bind(false, true); i = 0; for (const colMsBuff of colorMsBuffers) { const attachmentEnum = gl.COLOR_ATTACHMENT0 + i; this._colorMsBuffers.push(colMsBuff); this._colorMsFilters.push(msFilters[i]); const msBuffHandle = colMsBuff.getHandle(); if (undefined !== msBuffHandle) gl.framebufferRenderbuffer(gl.FRAMEBUFFER, attachmentEnum, gl.RENDERBUFFER, msBuffHandle); i++; } if (depthBufferMs !== undefined) { this.depthBufferMs = depthBufferMs; const dbHandleMs = depthBufferMs.getHandle(); if (undefined !== dbHandleMs) { gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, dbHandleMs); } } this.unbind(); } } } static create(colorTextures, depthBuffer, colorMsBuffers, msFilters, depthBufferMs) { const fbo = System_1.System.instance.context.createFramebuffer(); if (null === fbo) { return undefined; } return new FrameBuffer(fbo, colorTextures, depthBuffer, colorMsBuffers, msFilters, depthBufferMs); } [Symbol.dispose]() { // NB: The FrameBuffer does not *own* the textures and depth buffer. if (!this.isDisposed) { System_1.System.instance.context.deleteFramebuffer((0, core_bentley_1.expectDefined)(this._fbo)); this._fbo = undefined; if (undefined !== this._fboMs) { System_1.System.instance.context.deleteFramebuffer(this._fboMs); this._fboMs = undefined; } } } bind(bindAttachments = false, bindMS = false) { (0, core_bentley_1.assert)(undefined !== this._fbo); (0, core_bentley_1.assert)(!this.isBound); if (undefined === this._fbo) return false; const gl = System_1.System.instance.context; if (bindMS && undefined !== this._fboMs) { gl.bindFramebuffer(GL_1.GL.FrameBuffer.TARGET, this._fboMs); this._bindState = 3 /* FrameBufferBindState.BoundMultisampled */; } else { gl.bindFramebuffer(GL_1.GL.FrameBuffer.TARGET, this._fbo); this._bindState = 1 /* FrameBufferBindState.Bound */; } if (bindAttachments) { System_1.System.instance.setDrawBuffers(this._colorAttachments); this._bindState++; } return true; } unbind() { (0, core_bentley_1.assert)(this.isBound); System_1.System.instance.context.bindFramebuffer(GL_1.GL.FrameBuffer.TARGET, null); this._bindState = 0 /* FrameBufferBindState.Unbound */; } suspend() { (0, core_bentley_1.assert)(this.isBound); this._bindState = 5 /* FrameBufferBindState.Suspended */; } markTargetsDirty() { for (const msBuff of this._colorMsBuffers) { msBuff.markBufferDirty(true); } if (undefined !== this.depthBufferMs) this.depthBufferMs.markBufferDirty(true); } /** blitDepth is true to blit the depth/stencil buffer. ndx is index of single attachment to blit. * All color attachments are blitted if ndx is undefined, none are blitted if ndx is -1. */ blitMsBuffersToTextures(blitDepth, ndx) { if (!this._fboMs) return; System_1.System.instance.frameBufferStack.suspend(); const gl2 = System_1.System.instance.context; const attachments = []; const max = (undefined === ndx ? this._colorMsBuffers.length : ndx + 1); for (let i = 0; i < max; ++i) { if (undefined !== ndx && i < ndx) { attachments.push(gl2.NONE); // skip this one, but first add a NONE for it in the attachment list continue; } if (this._colorMsBuffers[i].isDirty) { gl2.bindFramebuffer(gl2.READ_FRAMEBUFFER, this._fboMs); gl2.readBuffer(this._colorAttachments[i]); gl2.bindFramebuffer(gl2.DRAW_FRAMEBUFFER, (0, core_bentley_1.expectDefined)(this._fbo)); attachments.push(this._colorAttachments[i]); gl2.drawBuffers(attachments); attachments.pop(); attachments.push(gl2.NONE); gl2.blitFramebuffer(0, 0, this._colorTextures[i].width, this._colorTextures[i].height, 0, 0, this._colorTextures[i].width, this._colorTextures[i].height, GL_1.GL.BufferBit.Color, this._colorMsFilters[i]); this._colorMsBuffers[i].markBufferDirty(false); if (undefined !== ndx && i === ndx) break; } } if (blitDepth && undefined !== this.depthBuffer && undefined !== this.depthBufferMs && this.depthBufferMs.isDirty) { const mask = GL_1.GL.BufferBit.Depth; // (this.depthBuffer instanceof RenderBuffer ? GL.BufferBit.Depth : GL.BufferBit.Depth | GL.BufferBit.Stencil); gl2.bindFramebuffer(gl2.READ_FRAMEBUFFER, this._fboMs); gl2.bindFramebuffer(gl2.DRAW_FRAMEBUFFER, (0, core_bentley_1.expectDefined)(this._fbo)); gl2.blitFramebuffer(0, 0, this.depthBuffer.width, this.depthBuffer.height, 0, 0, this.depthBuffer.width, this.depthBuffer.height, mask, GL_1.GL.MultiSampling.Filter.Nearest); this.depthBufferMs.markBufferDirty(false); } gl2.bindFramebuffer(gl2.READ_FRAMEBUFFER, null); gl2.bindFramebuffer(gl2.DRAW_FRAMEBUFFER, null); System_1.System.instance.frameBufferStack.resume(); } /** invDepth is true to invalidate depth buffer. invStencil is true to invalidate stencil buffer. ndx is index of single color attachment to invalidate. * All color attachments are invalidated if ndx is undefined, none are invalidated if ndx is -1. * Set withMultiSampling to true to invalidate the MS buffers. */ invalidate(invDepth, invStencil, withMultiSampling, indices) { const gl = System_1.System.instance.context; const attachments = invDepth ? (invStencil ? [gl.DEPTH_STENCIL_ATTACHMENT] : [System_1.System.instance.context.DEPTH_ATTACHMENT]) : (invDepth ? [gl.STENCIL_ATTACHMENT] : []); if (undefined !== indices) { if (indices.length > 0) { for (const i of indices) attachments.push(gl.COLOR_ATTACHMENT0 + i); } } else { attachments.concat(this._colorAttachments); } System_1.System.instance.frameBufferStack.execute(this, true, withMultiSampling, () => { System_1.System.instance.invalidateFrameBuffer(attachments); }); } // Chiefly for debugging currently - assumes RGBA, unsigned byte, want all pixels. get debugPixels() { if (!this.isBound || 0 === this._colorTextures.length || !(this._colorTextures[0] instanceof Texture_1.TextureHandle)) return undefined; const tex = this._colorTextures[0]; if (GL_1.GL.Texture.Format.Rgba !== tex.format || GL_1.GL.Texture.DataType.UnsignedByte !== tex.dataType) return undefined; const buffer = new Uint8Array(tex.width * tex.height * 4); for (let i = 0; i < buffer.length; i += 4) { buffer[i] = 0xba; buffer[i + 1] = 0xad; buffer[i + 2] = 0xf0; buffer[i + 3] = 0x0d; } System_1.System.instance.context.readPixels(0, 0, tex.width, tex.height, tex.format, tex.dataType, buffer); return buffer; } } exports.FrameBuffer = FrameBuffer; /** @internal */ class FrameBufferStack { // FrameBuffers within this array are not owned, as this is only a storage device holding references _stack = []; get _top() { return !this.isEmpty ? this._stack[this._stack.length - 1] : undefined; } push(fbo, withAttachments, withMultSampling) { if (undefined !== this._top) { this._top.fbo.suspend(); } (0, core_bentley_1.assert)(!fbo.isBound); fbo.bind(withAttachments, withMultSampling); (0, core_bentley_1.assert)(fbo.isBound); this._stack.push({ fbo, withAttachments, withMultSampling }); } pop() { (0, core_bentley_1.assert)(!this.isEmpty); if (undefined === this._top) { return; } const fbo = this._top.fbo; this._stack.pop(); (0, core_bentley_1.assert)(fbo.isBound); fbo.unbind(); (0, core_bentley_1.assert)(!fbo.isBound); if (this.isEmpty) { System_1.System.instance.context.bindFramebuffer(GL_1.GL.FrameBuffer.TARGET, null); } else { const top = this._top; (0, core_bentley_1.assert)(top.fbo.isSuspended); top.fbo.bind(top.withAttachments, top.withMultSampling); (0, core_bentley_1.assert)(top.fbo.isBound); } } get currentColorBuffer() { (0, core_bentley_1.assert)(!this.isEmpty); return undefined !== this._top ? this._top.fbo.getColor(0) : undefined; } get currentFbMultisampled() { return undefined !== this._top ? this._top.fbo.isBoundMultisampled : false; } get isEmpty() { return 0 === this._stack.length; } execute(fbo, withAttachments, withMultSampling, func) { this.push(fbo, withAttachments, withMultSampling); func(); this.pop(); } markTargetsDirty() { const top = this._top; if (undefined !== top) top.fbo.markTargetsDirty(); } suspend() { if (undefined !== this._top) { this._top.fbo.suspend(); } } resume() { if (undefined === this._top) { return; } if (this.isEmpty) { System_1.System.instance.context.bindFramebuffer(GL_1.GL.FrameBuffer.TARGET, null); } else { const top = this._top; top.fbo.bind(top.withAttachments, top.withMultSampling); (0, core_bentley_1.assert)(top.fbo.isBound); } } } exports.FrameBufferStack = FrameBufferStack; //# sourceMappingURL=FrameBuffer.js.map