@itwin/core-frontend
Version:
iTwin.js frontend components
312 lines • 14.7 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.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