From fbbe3f353b07034219765d08ce03edffef69ef78 Mon Sep 17 00:00:00 2001 From: Eli Bogomolny Date: Thu, 6 Jan 2022 16:33:54 -0500 Subject: [PATCH 01/32] SceneFramebuffer MSAA --- Source/Renderer/Framebuffer.js | 10 ++ Source/Renderer/FramebufferManager.js | 127 +++++++++++++++++-- Source/Renderer/MultisampleFramebuffer.js | 87 +++++++++++++ Source/Renderer/Renderbuffer.js | 13 +- Source/Renderer/RenderbufferFormat.js | 6 +- Source/Scene/AutoExposure.js | 3 +- Source/Scene/GlobeDepth.js | 2 +- Source/Scene/GlobeTranslucencyFramebuffer.js | 8 +- Source/Scene/PostProcessStageCollection.js | 43 ++++++- Source/Scene/PostProcessStageLibrary.js | 8 ++ Source/Scene/PostProcessStageTextureCache.js | 43 +++++-- Source/Scene/Scene.js | 18 ++- Source/Scene/SceneFramebuffer.js | 19 ++- 13 files changed, 354 insertions(+), 33 deletions(-) create mode 100644 Source/Renderer/MultisampleFramebuffer.js diff --git a/Source/Renderer/Framebuffer.js b/Source/Renderer/Framebuffer.js index 17eb57c16866..7e788555ff8b 100644 --- a/Source/Renderer/Framebuffer.js +++ b/Source/Renderer/Framebuffer.js @@ -362,6 +362,16 @@ Framebuffer.prototype._unBind = function () { gl.bindFramebuffer(gl.FRAMEBUFFER, null); }; +Framebuffer.prototype.bindDraw = function () { + var gl = this._gl; + gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, this._framebuffer); +}; + +Framebuffer.prototype.bindRead = function () { + var gl = this._gl; + gl.bindFramebuffer(gl.READ_FRAMEBUFFER, this._framebuffer); +}; + Framebuffer.prototype._getActiveColorAttachments = function () { return this._activeColorAttachments; }; diff --git a/Source/Renderer/FramebufferManager.js b/Source/Renderer/FramebufferManager.js index 954675b7ff7f..33600dcffa5f 100644 --- a/Source/Renderer/FramebufferManager.js +++ b/Source/Renderer/FramebufferManager.js @@ -1,4 +1,5 @@ import Framebuffer from "./Framebuffer.js"; +import MultisampleFramebuffer from "./MultisampleFramebuffer.js"; import PixelDatatype from "./PixelDatatype.js"; import Renderbuffer from "./Renderbuffer.js"; import RenderbufferFormat from "./RenderbufferFormat.js"; @@ -31,6 +32,7 @@ import PixelFormat from "../Core/PixelFormat.js"; */ function FramebufferManager(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); + this._numSamples = 1; this._colorAttachmentsLength = defaultValue( options.colorAttachmentsLength, 1 @@ -75,7 +77,9 @@ function FramebufferManager(options) { this._colorTextures = undefined; if (this._color) { this._colorTextures = new Array(this._colorAttachmentsLength); + this._colorRenderbuffers = new Array(this._colorAttachmentsLength); } + this._colorRenderbuffer = undefined; this._depthStencilRenderbuffer = undefined; this._depthStencilTexture = undefined; this._depthRenderbuffer = undefined; @@ -87,6 +91,9 @@ function FramebufferManager(options) { Object.defineProperties(FramebufferManager.prototype, { framebuffer: { get: function () { + if (this._numSamples > 1) { + return this._multisampleFramebuffer.getFramebuffer(); + } return this._framebuffer; }, }, @@ -100,10 +107,12 @@ Object.defineProperties(FramebufferManager.prototype, { FramebufferManager.prototype.isDirty = function ( width, height, + numSamples, pixelDatatype, pixelFormat ) { var dimensionChanged = this._width !== width || this._height !== height; + var samplesChanged = this._numSamples !== numSamples; var pixelChanged = (defined(pixelDatatype) && this._pixelDatatype !== pixelDatatype) || (defined(pixelFormat) && this._pixelFormat !== pixelFormat); @@ -111,6 +120,7 @@ FramebufferManager.prototype.isDirty = function ( return ( this._attachmentsDirty || dimensionChanged || + samplesChanged || pixelChanged || !defined(this._framebuffer) || (this._color && !defined(this._colorTextures[0])) @@ -121,6 +131,7 @@ FramebufferManager.prototype.update = function ( context, width, height, + numSamples, pixelDatatype, pixelFormat ) { @@ -129,6 +140,7 @@ FramebufferManager.prototype.update = function ( throw new DeveloperError("width and height must be defined."); } //>>includeEnd('debug'); + numSamples = defaultValue(numSamples, 1); pixelDatatype = defaultValue( pixelDatatype, this._color @@ -140,10 +152,11 @@ FramebufferManager.prototype.update = function ( this._color ? defaultValue(this._pixelFormat, PixelFormat.RGBA) : undefined ); - if (this.isDirty(width, height, pixelDatatype, pixelFormat)) { + if (this.isDirty(width, height, numSamples, pixelDatatype, pixelFormat)) { this.destroy(); this._width = width; this._height = height; + this._numSamples = numSamples; this._pixelDatatype = pixelDatatype; this._pixelFormat = pixelFormat; this._attachmentsDirty = false; @@ -159,6 +172,15 @@ FramebufferManager.prototype.update = function ( pixelDatatype: pixelDatatype, sampler: Sampler.NEAREST, }); + if (this._numSamples > 1) { + this._colorRenderbuffers[i] = new Renderbuffer({ + context: context, + width: width, + height: height, + format: RenderbufferFormat.RGBA8, + numSamples: this._numSamples, + }); + } } } @@ -173,6 +195,15 @@ FramebufferManager.prototype.update = function ( pixelDatatype: PixelDatatype.UNSIGNED_INT_24_8, sampler: Sampler.NEAREST, }); + if (this._numSamples > 1) { + this._depthStencilRenderbuffer = new Renderbuffer({ + context: context, + width: width, + height: height, + format: RenderbufferFormat.DEPTH24_STENCIL8, + numSamples: this._numSamples, + }); + } } else { this._depthStencilRenderbuffer = new Renderbuffer({ context: context, @@ -194,6 +225,7 @@ FramebufferManager.prototype.update = function ( pixelDatatype: PixelDatatype.UNSIGNED_INT, sampler: Sampler.NEAREST, }); + // TODO@eli create depth RB if numSamples > 1 } else { this._depthRenderbuffer = new Renderbuffer({ context: context, @@ -204,15 +236,26 @@ FramebufferManager.prototype.update = function ( } } - this._framebuffer = new Framebuffer({ - context: context, - colorTextures: this._colorTextures, - depthTexture: this._depthTexture, - depthRenderbuffer: this._depthRenderbuffer, - depthStencilTexture: this._depthStencilTexture, - depthStencilRenderbuffer: this._depthStencilRenderbuffer, - destroyAttachments: false, - }); + if (this._numSamples > 1) { + this._multisampleFramebuffer = new MultisampleFramebuffer({ + context: context, + colorTextures: this._colorTextures, + colorRenderbuffers: this._colorRenderbuffers, + depthStencilTexture: this._depthStencilTexture, + depthStencilRenderbuffer: this._depthStencilRenderbuffer, + destroyAttachments: false, + }); + } else { + this._framebuffer = new Framebuffer({ + context: context, + colorTextures: this._colorTextures, + depthTexture: this._depthTexture, + depthRenderbuffer: this._depthRenderbuffer, + depthStencilTexture: this._depthStencilTexture, + depthStencilRenderbuffer: this._depthStencilRenderbuffer, + destroyAttachments: false, + }); + } } }; @@ -246,6 +289,39 @@ FramebufferManager.prototype.setColorTexture = function (texture, index) { this._colorTextures[index] = texture; }; +FramebufferManager.prototype.getColorRenderbuffer = function (index) { + index = defaultValue(index, 0); + //>>includeStart('debug', pragmas.debug); + if (index >= this._colorAttachmentsLength) { + throw new DeveloperError( + "index must be smaller than total number of color attachments." + ); + } + //>>includeEnd('debug'); + return this._colorRenderbuffers[index]; +}; + +FramebufferManager.prototype.setColorRenderbuffer = function ( + renderbuffer, + index +) { + index = defaultValue(index, 0); + //>>includeStart('debug', pragmas.debug); + if (this._createColorAttachments) { + throw new DeveloperError( + "createColorAttachments must be false if setColorRenderbuffer is called." + ); + } + if (index >= this._colorAttachmentsLength) { + throw new DeveloperError( + "index must be smaller than total number of color attachments." + ); + } + //>>includeEnd('debug'); + this._attachmentsDirty = renderbuffer !== this._colorRenderbuffers[index]; + this._colorRenderbuffers[index] = renderbuffer; +}; + FramebufferManager.prototype.getDepthRenderbuffer = function () { return this._depthRenderbuffer; }; @@ -312,6 +388,12 @@ FramebufferManager.prototype.setDepthStencilTexture = function (texture) { this._depthStencilTexture = texture; }; +FramebufferManager.prototype.blitFramebuffers = function (context) { + if (this._numSamples > 1) { + return this._multisampleFramebuffer.blitFramebuffers(context); + } +}; + FramebufferManager.prototype.clear = function ( context, clearCommand, @@ -319,26 +401,45 @@ FramebufferManager.prototype.clear = function ( ) { var framebuffer = clearCommand.framebuffer; - clearCommand.framebuffer = this._framebuffer; - clearCommand.execute(context, passState); + if (this._numSamples > 1) { + clearCommand.framebuffer = this._multisampleFramebuffer.getRenderFramebuffer(); + clearCommand.execute(context, passState); + + clearCommand.framebuffer = this._multisampleFramebuffer.getColorFramebuffer(); + clearCommand.execute(context, passState); + } else { + clearCommand.framebuffer = this._framebuffer; + clearCommand.execute(context, passState); + } clearCommand.framebuffer = framebuffer; }; FramebufferManager.prototype.destroyFramebuffer = function () { this._framebuffer = this._framebuffer && this._framebuffer.destroy(); + this._multisampleFramebuffer = + this._multisampleFramebuffer && this._multisampleFramebuffer.destroy(); }; FramebufferManager.prototype.destroy = function () { if (this._color && this._createColorAttachments) { + var i; var length = this._colorTextures.length; - for (var i = 0; i < length; ++i) { + for (i = 0; i < length; ++i) { var texture = this._colorTextures[i]; if (defined(texture) && !texture.isDestroyed()) { this._colorTextures[i].destroy(); this._colorTextures[i] = undefined; } } + length = this._colorRenderbuffers.length; + for (i = 0; i < length; ++i) { + var renderbuffer = this._colorRenderbuffers[i]; + if (defined(renderbuffer) && !renderbuffer.isDestroyed()) { + this._colorRenderbuffers[i].destroy(); + this._colorRenderbuffers[i] = undefined; + } + } } if (this._depthStencil && this._createDepthAttachments) { diff --git a/Source/Renderer/MultisampleFramebuffer.js b/Source/Renderer/MultisampleFramebuffer.js new file mode 100644 index 000000000000..34790676ca9c --- /dev/null +++ b/Source/Renderer/MultisampleFramebuffer.js @@ -0,0 +1,87 @@ +import Check from "../Core/Check.js"; +import defaultValue from "../Core/defaultValue.js"; +import defined from "../Core/defined.js"; +import Framebuffer from "./Framebuffer.js"; + +/** + * Creates a multisampling wrapper around two framebuffers with optional initial color, depth, and stencil attachments. + * + * @param {Object} options The initial framebuffer attachments as shown in the example below. context is required. The possible properties are colorTextures, colorRenderbuffers, depthTexture, depthRenderbuffer, stencilRenderbuffer, depthStencilTexture, and depthStencilRenderbuffer. + * + * @private + * @constructor + */ +function MultisampleFramebuffer(options) { + options = defaultValue(options, defaultValue.EMPTY_OBJECT); + + var context = options.context; + var colorRenderbuffers = options.colorRenderbuffers; + var colorTextures = options.colorTextures; + //>>includeStart('debug', pragmas.debug); + Check.defined("options.context", context); + Check.defined("options.colorRenderbuffers", colorRenderbuffers); + Check.defined("options.colorTextures", colorTextures); + //>>includeEnd('debug'); + + var depthStencilRenderbuffer = options.depthStencilRenderbuffer; + var depthStencilTexture = options.depthStencilTexture; + if ( + (defined(depthStencilRenderbuffer) && !defined(depthStencilTexture)) || + (defined(depthStencilTexture) && !defined(depthStencilRenderbuffer)) + ) { + // throw new DeveloperError("If multisampling depth stencil attachments, both a Renderbuffer and Texture must be provided.") + } + + this._renderFramebuffer = new Framebuffer({ + context: context, + colorRenderbuffers: colorRenderbuffers, + depthStencilRenderbuffer: depthStencilRenderbuffer, + destroyAttachments: options.destroyAttachments, + }); + this._colorFramebuffer = new Framebuffer({ + context: context, + colorTextures: colorTextures, + depthStencilTexture: depthStencilTexture, + destroyAttachments: options.destroyAttachments, + }); +} + +MultisampleFramebuffer.prototype.getFramebuffer = function () { + return this._renderFramebuffer; +}; + +MultisampleFramebuffer.prototype.getRenderFramebuffer = function () { + return this._renderFramebuffer; +}; + +MultisampleFramebuffer.prototype.getColorFramebuffer = function () { + return this._colorFramebuffer; +}; + +MultisampleFramebuffer.prototype.blitFramebuffers = function (context) { + this._renderFramebuffer.bindRead(); + this._colorFramebuffer.bindDraw(); + var width = context.canvas.clientWidth; + var height = context.canvas.clientHeight; + var gl = context._gl; + gl.blitFramebuffer( + 0, + 0, + width, + height, + 0, + 0, + width, + height, + gl.COLOR_BUFFER_BIT, + gl.LINEAR + ); + return this._colorFramebuffer; +}; + +MultisampleFramebuffer.prototype.destroy = function () { + this._renderFramebuffer.destroy(); + this._colorFramebuffer.destroy(); +}; + +export default MultisampleFramebuffer; diff --git a/Source/Renderer/Renderbuffer.js b/Source/Renderer/Renderbuffer.js index 7417d7f99f09..6ee89796d68f 100644 --- a/Source/Renderer/Renderbuffer.js +++ b/Source/Renderer/Renderbuffer.js @@ -25,6 +25,7 @@ function Renderbuffer(options) { var height = defined(options.height) ? options.height : gl.drawingBufferHeight; + var numSamples = defaultValue(options.numSamples, 1); //>>includeStart('debug', pragmas.debug); if (!RenderbufferFormat.validate(format)) { @@ -59,7 +60,17 @@ function Renderbuffer(options) { this._renderbuffer = this._gl.createRenderbuffer(); gl.bindRenderbuffer(gl.RENDERBUFFER, this._renderbuffer); - gl.renderbufferStorage(gl.RENDERBUFFER, format, width, height); + if (numSamples > 1) { + gl.renderbufferStorageMultisample( + gl.RENDERBUFFER, + numSamples, + format, + this._width, + this._height + ); + } else { + gl.renderbufferStorage(gl.RENDERBUFFER, format, width, height); + } gl.bindRenderbuffer(gl.RENDERBUFFER, null); } diff --git a/Source/Renderer/RenderbufferFormat.js b/Source/Renderer/RenderbufferFormat.js index 0ec056a10c22..5634b20115d2 100644 --- a/Source/Renderer/RenderbufferFormat.js +++ b/Source/Renderer/RenderbufferFormat.js @@ -5,20 +5,24 @@ import WebGLConstants from "../Core/WebGLConstants.js"; */ var RenderbufferFormat = { RGBA4: WebGLConstants.RGBA4, + RGBA8: WebGLConstants.RGBA8, RGB5_A1: WebGLConstants.RGB5_A1, RGB565: WebGLConstants.RGB565, DEPTH_COMPONENT16: WebGLConstants.DEPTH_COMPONENT16, STENCIL_INDEX8: WebGLConstants.STENCIL_INDEX8, DEPTH_STENCIL: WebGLConstants.DEPTH_STENCIL, + DEPTH24_STENCIL8: WebGLConstants.DEPTH24_STENCIL8, validate: function (renderbufferFormat) { return ( renderbufferFormat === RenderbufferFormat.RGBA4 || + renderbufferFormat === RenderbufferFormat.RGBA8 || renderbufferFormat === RenderbufferFormat.RGB5_A1 || renderbufferFormat === RenderbufferFormat.RGB565 || renderbufferFormat === RenderbufferFormat.DEPTH_COMPONENT16 || renderbufferFormat === RenderbufferFormat.STENCIL_INDEX8 || - renderbufferFormat === RenderbufferFormat.DEPTH_STENCIL + renderbufferFormat === RenderbufferFormat.DEPTH_STENCIL || + renderbufferFormat === RenderbufferFormat.DEPTH24_STENCIL8 ); }, }; diff --git a/Source/Scene/AutoExposure.js b/Source/Scene/AutoExposure.js index 9a7309abb336..60a46f5e753f 100644 --- a/Source/Scene/AutoExposure.js +++ b/Source/Scene/AutoExposure.js @@ -140,7 +140,7 @@ function createFramebuffers(autoexposure, context) { width = Math.max(Math.ceil(width / 3.0), 1.0); height = Math.max(Math.ceil(height / 3.0), 1.0); framebuffers[i] = new FramebufferManager(); - framebuffers[i].update(context, width, height, pixelDatatype); + framebuffers[i].update(context, width, height, 1, pixelDatatype); } var lastTexture = framebuffers[length - 1].getColorTexture(0); @@ -148,6 +148,7 @@ function createFramebuffers(autoexposure, context) { context, lastTexture.width, lastTexture.height, + 1, pixelDatatype ); autoexposure._framebuffers = framebuffers; diff --git a/Source/Scene/GlobeDepth.js b/Source/Scene/GlobeDepth.js index fb6adad11836..7bf47367af2e 100644 --- a/Source/Scene/GlobeDepth.js +++ b/Source/Scene/GlobeDepth.js @@ -224,7 +224,7 @@ GlobeDepth.prototype.update = function ( ? PixelDatatype.HALF_FLOAT : PixelDatatype.FLOAT : PixelDatatype.UNSIGNED_BYTE; - this._colorFramebuffer.update(context, width, height, pixelDatatype); + this._colorFramebuffer.update(context, width, height, 1, pixelDatatype); this._copyDepthFramebuffer.update(context, width, height); updateCopyCommands(this, context, width, height, passState); context.uniformState.globeDepthTexture = undefined; diff --git a/Source/Scene/GlobeTranslucencyFramebuffer.js b/Source/Scene/GlobeTranslucencyFramebuffer.js index 7ae6473ae142..746d5d5282bb 100644 --- a/Source/Scene/GlobeTranslucencyFramebuffer.js +++ b/Source/Scene/GlobeTranslucencyFramebuffer.js @@ -76,7 +76,13 @@ function updateResources(globeTranslucency, context, width, height, hdr) { ? PixelDatatype.HALF_FLOAT : PixelDatatype.FLOAT : PixelDatatype.UNSIGNED_BYTE; - globeTranslucency._framebuffer.update(context, width, height, pixelDatatype); + globeTranslucency._framebuffer.update( + context, + width, + height, + 1, + pixelDatatype + ); globeTranslucency._packedDepthFramebuffer.update(context, width, height); } diff --git a/Source/Scene/PostProcessStageCollection.js b/Source/Scene/PostProcessStageCollection.js index 04e18c1c49cc..02a16309966f 100644 --- a/Source/Scene/PostProcessStageCollection.js +++ b/Source/Scene/PostProcessStageCollection.js @@ -35,6 +35,7 @@ var stackScratch = []; */ function PostProcessStageCollection() { var fxaa = PostProcessStageLibrary.createFXAAStage(); + var passThrough = PostProcessStageLibrary.createPassThroughStage(); var ao = PostProcessStageLibrary.createAmbientOcclusionStage(); var bloom = PostProcessStageLibrary.createBloomStage(); @@ -51,6 +52,7 @@ function PostProcessStageCollection() { var tonemapping = this._tonemapping; fxaa.enabled = false; + passThrough.enabled = false; ao.enabled = false; bloom.enabled = false; tonemapping.enabled = false; // will be enabled if necessary in update @@ -59,7 +61,7 @@ function PostProcessStageCollection() { var stageNames = {}; var stack = stackScratch; - stack.push(fxaa, ao, bloom, tonemapping); + stack.push(fxaa, passThrough, ao, bloom, tonemapping); while (stack.length > 0) { var stage = stack.pop(); stageNames[stage.name] = stage; @@ -87,11 +89,13 @@ function PostProcessStageCollection() { this._ao = ao; this._bloom = bloom; this._fxaa = fxaa; + this._passThrough = passThrough; this._aoEnabled = undefined; this._bloomEnabled = undefined; this._tonemappingEnabled = undefined; this._fxaaEnabled = undefined; + this._passThroughEnabled = undefined; this._activeStagesChanged = false; this._stagesRemoved = false; @@ -120,11 +124,14 @@ Object.defineProperties(PostProcessStageCollection.prototype, { } var fxaa = this._fxaa; + var passThrough = this._passThrough; var ao = this._ao; var bloom = this._bloom; var tonemapping = this._tonemapping; readyAndEnabled = readyAndEnabled || (fxaa.ready && fxaa.enabled); + readyAndEnabled = + readyAndEnabled || (passThrough.ready && passThrough.enabled); readyAndEnabled = readyAndEnabled || (ao.ready && ao.enabled); readyAndEnabled = readyAndEnabled || (bloom.ready && bloom.enabled); readyAndEnabled = @@ -133,6 +140,18 @@ Object.defineProperties(PostProcessStageCollection.prototype, { return readyAndEnabled; }, }, + /** + * A no-op post-process stage. + * + * @memberof PostProcessStageCollection.prototype + * @type {PostProcessStage} + * @readonly + */ + passThrough: { + get: function () { + return this._passThrough; + }, + }, /** * A post-process stage for Fast Approximate Anti-aliasing. *

@@ -256,6 +275,11 @@ Object.defineProperties(PostProcessStageCollection.prototype, { return this.getOutputTexture(fxaa.name); } + var passThrough = this._passThrough; + if (passThrough.enabled && passThrough.ready) { + return this.getOutputTexture(passThrough.name); + } + var stages = this._stages; var length = stages.length; for (var i = length - 1; i >= 0; --i) { @@ -592,6 +616,7 @@ PostProcessStageCollection.prototype.update = function ( var autoexposure = this._autoExposure; var tonemapping = this._tonemapping; var fxaa = this._fxaa; + var passThrough = this._passThrough; tonemapping.enabled = useHdr; @@ -600,6 +625,8 @@ PostProcessStageCollection.prototype.update = function ( var tonemappingEnabled = tonemapping.enabled && tonemapping._isSupported(context); var fxaaEnabled = fxaa.enabled && fxaa._isSupported(context); + var passThroughEnabled = + passThrough.enabled && passThrough._isSupported(context); if ( activeStagesChanged || @@ -607,7 +634,8 @@ PostProcessStageCollection.prototype.update = function ( aoEnabled !== this._aoEnabled || bloomEnabled !== this._bloomEnabled || tonemappingEnabled !== this._tonemappingEnabled || - fxaaEnabled !== this._fxaaEnabled + fxaaEnabled !== this._fxaaEnabled || + passThroughEnabled !== this._passThroughEnabled ) { // The number of stages to execute has changed. // Update dependencies and recreate framebuffers. @@ -617,6 +645,7 @@ PostProcessStageCollection.prototype.update = function ( this._bloomEnabled = bloomEnabled; this._tonemappingEnabled = tonemappingEnabled; this._fxaaEnabled = fxaaEnabled; + this._passThroughEnabled = passThroughEnabled; this._textureCacheDirty = false; } @@ -653,6 +682,7 @@ PostProcessStageCollection.prototype.update = function ( this._textureCache.update(context); fxaa.update(context, useLogDepth); + passThrough.update(context, useLogDepth); ao.update(context, useLogDepth); bloom.update(context, useLogDepth); tonemapping.update(context, useLogDepth); @@ -764,6 +794,7 @@ PostProcessStageCollection.prototype.execute = function ( var activeStages = this._activeStages; var length = activeStages.length; var fxaa = this._fxaa; + var passThrough = this._passThrough; var ao = this._ao; var bloom = this._bloom; var autoexposure = this._autoExposure; @@ -775,9 +806,12 @@ PostProcessStageCollection.prototype.execute = function ( var tonemappingEnabled = tonemapping.enabled && tonemapping._isSupported(context); var fxaaEnabled = fxaa.enabled && fxaa._isSupported(context); + var passThroughEnabled = + passThrough.enabled && passThrough._isSupported(context); if ( !fxaaEnabled && + !passThroughEnabled && !aoEnabled && !bloomEnabled && !tonemappingEnabled && @@ -822,6 +856,10 @@ PostProcessStageCollection.prototype.execute = function ( if (fxaaEnabled && fxaa.ready) { execute(fxaa, context, lastTexture, depthTexture, idTexture); } + + if (passThroughEnabled && passThrough.ready) { + execute(passThrough, context, lastTexture, depthTexture, idTexture); + } }; /** @@ -879,6 +917,7 @@ PostProcessStageCollection.prototype.isDestroyed = function () { */ PostProcessStageCollection.prototype.destroy = function () { this._fxaa.destroy(); + this._passThrough.destroy(); this._ao.destroy(); this._bloom.destroy(); this._autoExposure.destroy(); diff --git a/Source/Scene/PostProcessStageLibrary.js b/Source/Scene/PostProcessStageLibrary.js index 2df2faae801e..3edab273469e 100644 --- a/Source/Scene/PostProcessStageLibrary.js +++ b/Source/Scene/PostProcessStageLibrary.js @@ -15,6 +15,7 @@ import DepthView from "../Shaders/PostProcessStages/DepthView.js"; import EdgeDetection from "../Shaders/PostProcessStages/EdgeDetection.js"; import FilmicTonemapping from "../Shaders/PostProcessStages/FilmicTonemapping.js"; import FXAA from "../Shaders/PostProcessStages/FXAA.js"; +import PassThrough from "../Shaders/PostProcessStages/PassThrough.js"; import GaussianBlur1D from "../Shaders/PostProcessStages/GaussianBlur1D.js"; import LensFlare from "../Shaders/PostProcessStages/LensFlare.js"; import ModifiedReinhardTonemapping from "../Shaders/PostProcessStages/ModifiedReinhardTonemapping.js"; @@ -666,6 +667,13 @@ PostProcessStageLibrary.createFXAAStage = function () { }); }; +PostProcessStageLibrary.createPassThroughStage = function () { + return new PostProcessStage({ + name: "czm_PassThrough", + fragmentShader: PassThrough, + }); +}; + /** * Creates a post-process stage that applies ACES tonemapping operator. * @param {Boolean} useAutoExposure Whether or not to use auto-exposure. diff --git a/Source/Scene/PostProcessStageTextureCache.js b/Source/Scene/PostProcessStageTextureCache.js index 44198e03b5a5..c7432cb05d6d 100644 --- a/Source/Scene/PostProcessStageTextureCache.js +++ b/Source/Scene/PostProcessStageTextureCache.js @@ -3,7 +3,8 @@ import defined from "../Core/defined.js"; import destroyObject from "../Core/destroyObject.js"; import CesiumMath from "../Core/Math.js"; import ClearCommand from "../Renderer/ClearCommand.js"; -import FramebufferManager from "../Renderer/FramebufferManager.js"; +import Framebuffer from "../Renderer/Framebuffer.js"; +import Texture from "../Renderer/Texture.js"; /** * Creates a minimal amount of textures and framebuffers. @@ -147,6 +148,7 @@ function getDependencies(collection, context) { var bloom = collection.bloom; var tonemapping = collection._tonemapping; var fxaa = collection.fxaa; + var passThrough = collection.passThrough; var previousName = getCompositeDependencies( collection, @@ -176,6 +178,13 @@ function getDependencies(collection, context) { collection, previousName ); + previousName = getStageDependencies( + collection, + context, + dependencies, + passThrough, + previousName + ); getStageDependencies(collection, context, dependencies, fxaa, previousName); } else { getCompositeDependencies( @@ -244,10 +253,7 @@ function getFramebuffer(cache, stageName, dependencies) { pixelDatatype: pixelDatatype, clearColor: clearColor, stages: [stageName], - buffer: new FramebufferManager({ - pixelFormat: pixelFormat, - pixelDatatype: pixelDatatype, - }), + buffer: undefined, clear: undefined, }; @@ -273,7 +279,8 @@ function releaseResources(cache) { var length = framebuffers.length; for (var i = 0; i < length; ++i) { var framebuffer = framebuffers[i]; - framebuffer.buffer.destroy(); + framebuffer.buffer = framebuffer.buffer && framebuffer.buffer.destroy(); + framebuffer.buffer = undefined; } } @@ -299,10 +306,21 @@ function updateFramebuffers(cache, context) { textureHeight = size; } - framebuffer.buffer.update(context, textureWidth, textureHeight); + framebuffer.buffer = new Framebuffer({ + context: context, + colorTextures: [ + new Texture({ + context: context, + width: textureWidth, + height: textureHeight, + pixelFormat: framebuffer.pixelFormat, + pixelDatatype: framebuffer.pixelDatatype, + }), + ], + }); framebuffer.clear = new ClearCommand({ color: framebuffer.clearColor, - framebuffer: framebuffer.buffer.framebuffer, + framebuffer: framebuffer.buffer, }); } } @@ -335,13 +353,18 @@ PostProcessStageTextureCache.prototype.update = function (context) { defined(collection.fxaa) && collection.fxaa.enabled && collection.fxaa._isSupported(context); + var passThroughEnabled = + defined(collection.passThrough) && + collection.passThrough.enabled && + collection.passThrough._isSupported(context); var needsCheckDimensionsUpdate = !defined(collection._activeStages) || collection._activeStages.length > 0 || aoEnabled || bloomEnabled || tonemappingEnabled || - fxaaEnabled; + fxaaEnabled || + passThroughEnabled; if ( updateDependencies || (!needsCheckDimensionsUpdate && this._framebuffers.length > 0) @@ -416,7 +439,7 @@ PostProcessStageTextureCache.prototype.getFramebuffer = function (name) { if (!defined(framebuffer)) { return undefined; } - return framebuffer.buffer.framebuffer; + return framebuffer.buffer; }; /** diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js index e746ec8916a6..a9e5b90b2634 100644 --- a/Source/Scene/Scene.js +++ b/Source/Scene/Scene.js @@ -589,6 +589,13 @@ function Scene(options) { */ this.eyeSeparation = undefined; + /** + * The number of samples for MSAA (MSAA is disabled when numberSamples = 1). + * @type {Number} + * @default 1 + */ + this.numberSamples = 1; + /** * Post processing effects applied to the final render. * @type {PostProcessStageCollection} @@ -3389,10 +3396,16 @@ function updateAndClearFramebuffers(scene, passState, clearColor) { postProcess.length > 0 || postProcess.ambientOcclusion.enabled || postProcess.fxaa.enabled || + postProcess.passThrough.enabled || postProcess.bloom.enabled)); environmentState.usePostProcessSelected = false; if (usePostProcess) { - view.sceneFramebuffer.update(context, view.viewport, scene._hdr); + view.sceneFramebuffer.update( + context, + view.viewport, + scene._hdr, + scene.numberSamples + ); view.sceneFramebuffer.clear(context, passState, clearColor); postProcess.update(context, frameState.useLogDepth, scene._hdr); @@ -3491,6 +3504,9 @@ Scene.prototype.resolveFramebuffers = function (passState) { } if (usePostProcess) { + if (this.numberSamples > 1) { + sceneFramebuffer = view.sceneFramebuffer.blitFramebuffers(context); + } var inputFramebuffer = sceneFramebuffer; if (useGlobeDepthFramebuffer && !useOIT) { inputFramebuffer = globeFramebuffer; diff --git a/Source/Scene/SceneFramebuffer.js b/Source/Scene/SceneFramebuffer.js index a416b0d993d6..46707b278545 100644 --- a/Source/Scene/SceneFramebuffer.js +++ b/Source/Scene/SceneFramebuffer.js @@ -44,7 +44,12 @@ Object.defineProperties(SceneFramebuffer.prototype, { }, }); -SceneFramebuffer.prototype.update = function (context, viewport, hdr) { +SceneFramebuffer.prototype.update = function ( + context, + viewport, + hdr, + numSamples +) { var width = viewport.width; var height = viewport.height; var pixelDatatype = hdr @@ -52,7 +57,13 @@ SceneFramebuffer.prototype.update = function (context, viewport, hdr) { ? PixelDatatype.HALF_FLOAT : PixelDatatype.FLOAT : PixelDatatype.UNSIGNED_BYTE; - this._colorFramebuffer.update(context, width, height, pixelDatatype); + this._colorFramebuffer.update( + context, + width, + height, + numSamples, + pixelDatatype + ); this._idFramebuffer.update(context, width, height); }; @@ -71,6 +82,10 @@ SceneFramebuffer.prototype.getIdFramebuffer = function () { return this._idFramebuffer.framebuffer; }; +SceneFramebuffer.prototype.blitFramebuffers = function (context) { + return this._colorFramebuffer.blitFramebuffers(context); +}; + SceneFramebuffer.prototype.isDestroyed = function () { return false; }; From f86c95bbe00211da09058b6e7edca53bbab2f63e Mon Sep 17 00:00:00 2001 From: Eli Bogomolny Date: Wed, 19 Jan 2022 13:42:09 -0500 Subject: [PATCH 02/32] Invert classification --- Source/Renderer/Framebuffer.js | 3 ++ Source/Renderer/FramebufferManager.js | 30 ++++++++++++-- Source/Renderer/MultisampleFramebuffer.js | 37 +++++++++++++++-- Source/Scene/GlobeDepth.js | 43 ++++++++++++++++---- Source/Scene/InvertClassification.js | 25 +++++++++--- Source/Scene/OIT.js | 2 +- Source/Scene/Scene.js | 49 ++++++++++++++++++----- Source/Scene/SceneFramebuffer.js | 13 +++++- 8 files changed, 170 insertions(+), 32 deletions(-) diff --git a/Source/Renderer/Framebuffer.js b/Source/Renderer/Framebuffer.js index 7e788555ff8b..9700bea8e533 100644 --- a/Source/Renderer/Framebuffer.js +++ b/Source/Renderer/Framebuffer.js @@ -7,6 +7,8 @@ import PixelFormat from "../Core/PixelFormat.js"; import ContextLimits from "./ContextLimits.js"; import PixelDatatype from "./PixelDatatype.js"; +import createGuid from "../Core/createGuid.js"; + function attachTexture(framebuffer, attachment, texture) { var gl = framebuffer._gl; gl.framebufferTexture2D( @@ -73,6 +75,7 @@ function attachRenderbuffer(framebuffer, attachment, renderbuffer) { * @constructor */ function Framebuffer(options) { + this._id = createGuid(); options = defaultValue(options, defaultValue.EMPTY_OBJECT); var context = options.context; diff --git a/Source/Renderer/FramebufferManager.js b/Source/Renderer/FramebufferManager.js index 33600dcffa5f..d028fcb65407 100644 --- a/Source/Renderer/FramebufferManager.js +++ b/Source/Renderer/FramebufferManager.js @@ -74,6 +74,7 @@ function FramebufferManager(options) { this._height = undefined; this._framebuffer = undefined; + this._multisampleFramebuffer = undefined; this._colorTextures = undefined; if (this._color) { this._colorTextures = new Array(this._colorAttachmentsLength); @@ -99,6 +100,9 @@ Object.defineProperties(FramebufferManager.prototype, { }, status: { get: function () { + if (this._numSamples > 1) { + return this._multisampleFramebuffer.getFramebuffer().status; + } return this._framebuffer.status; }, }, @@ -111,18 +115,23 @@ FramebufferManager.prototype.isDirty = function ( pixelDatatype, pixelFormat ) { + // return true; var dimensionChanged = this._width !== width || this._height !== height; var samplesChanged = this._numSamples !== numSamples; var pixelChanged = (defined(pixelDatatype) && this._pixelDatatype !== pixelDatatype) || (defined(pixelFormat) && this._pixelFormat !== pixelFormat); + var framebufferDefined = + numSamples === 1 + ? defined(this._framebuffer) + : defined(this._multisampleFramebuffer); return ( this._attachmentsDirty || dimensionChanged || samplesChanged || pixelChanged || - !defined(this._framebuffer) || + !framebufferDefined || (this._color && !defined(this._colorTextures[0])) ); }; @@ -225,6 +234,15 @@ FramebufferManager.prototype.update = function ( pixelDatatype: PixelDatatype.UNSIGNED_INT, sampler: Sampler.NEAREST, }); + if (this._numSamples > 1) { + this._depthRenderbuffer = new Renderbuffer({ + context: context, + width: width, + height: height, + format: RenderbufferFormat.DEPTH_COMPONENT16, + numSamples: this._numSamples, + }); + } // TODO@eli create depth RB if numSamples > 1 } else { this._depthRenderbuffer = new Renderbuffer({ @@ -388,9 +406,15 @@ FramebufferManager.prototype.setDepthStencilTexture = function (texture) { this._depthStencilTexture = texture; }; -FramebufferManager.prototype.blitFramebuffers = function (context) { +FramebufferManager.prototype.prepareColorFramebuffer = function (context) { + if (this._numSamples > 1) { + this._multisampleFramebuffer.blitFramebuffers(context); + } +}; + +FramebufferManager.prototype.prepareRenderFramebuffer = function () { if (this._numSamples > 1) { - return this._multisampleFramebuffer.blitFramebuffers(context); + this._multisampleFramebuffer.setRenderAsDefault(true); } }; diff --git a/Source/Renderer/MultisampleFramebuffer.js b/Source/Renderer/MultisampleFramebuffer.js index 34790676ca9c..80c6ba0ea5e5 100644 --- a/Source/Renderer/MultisampleFramebuffer.js +++ b/Source/Renderer/MultisampleFramebuffer.js @@ -1,6 +1,7 @@ import Check from "../Core/Check.js"; import defaultValue from "../Core/defaultValue.js"; import defined from "../Core/defined.js"; +import destroyObject from "../Core/destroyObject.js"; import Framebuffer from "./Framebuffer.js"; /** @@ -44,9 +45,14 @@ function MultisampleFramebuffer(options) { depthStencilTexture: depthStencilTexture, destroyAttachments: options.destroyAttachments, }); + + this._defaultToRender = true; + this._blitReady = false; } MultisampleFramebuffer.prototype.getFramebuffer = function () { + // if (this._defaultToRender) return this._renderFramebuffer; + // if (this._blitReady) return this._colorFramebuffer; return this._renderFramebuffer; }; @@ -59,11 +65,23 @@ MultisampleFramebuffer.prototype.getColorFramebuffer = function () { }; MultisampleFramebuffer.prototype.blitFramebuffers = function (context) { + // if (!this._blitReady) { + // clearCommand.execute(context); this._renderFramebuffer.bindRead(); this._colorFramebuffer.bindDraw(); var width = context.canvas.clientWidth; var height = context.canvas.clientHeight; var gl = context._gl; + var mask = 0; + if (this._colorFramebuffer._colorTextures.length > 0) { + mask |= gl.COLOR_BUFFER_BIT; + } + if (defined(this._colorFramebuffer.depthTexture)) { + mask |= gl.DEPTH_BUFFER_BIT; + } + if (defined(this._colorFramebuffer.depthStencilTexture)) { + mask |= gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT; + } gl.blitFramebuffer( 0, 0, @@ -73,15 +91,28 @@ MultisampleFramebuffer.prototype.blitFramebuffers = function (context) { 0, width, height, - gl.COLOR_BUFFER_BIT, - gl.LINEAR + mask, + gl.NEAREST ); - return this._colorFramebuffer; + gl.bindFramebuffer(gl.READ_FRAMEBUFFER, null); + gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, null); + // this._blitReady = true; + // } +}; + +MultisampleFramebuffer.prototype.setRenderAsDefault = function (value) { + this._defaultToRender = value; +}; + +MultisampleFramebuffer.prototype.isDestroyed = function () { + return false; }; MultisampleFramebuffer.prototype.destroy = function () { + this._blitReady = false; this._renderFramebuffer.destroy(); this._colorFramebuffer.destroy(); + return destroyObject(this); }; export default MultisampleFramebuffer; diff --git a/Source/Scene/GlobeDepth.js b/Source/Scene/GlobeDepth.js index 7bf47367af2e..ef228820475d 100644 --- a/Source/Scene/GlobeDepth.js +++ b/Source/Scene/GlobeDepth.js @@ -17,6 +17,7 @@ import StencilOperation from "./StencilOperation.js"; * @private */ function GlobeDepth() { + this._numSamples = 1; this._tempCopyDepthTexture = undefined; this._colorFramebuffer = new FramebufferManager({ @@ -55,6 +56,11 @@ Object.defineProperties(GlobeDepth.prototype, { return this._colorFramebuffer.framebuffer; }, }, + depthStencilTexture: { + get: function () { + return this._colorFramebuffer.getDepthStencilTexture(); + }, + }, }); function destroyFramebuffers(globeDepth) { @@ -132,6 +138,7 @@ function updateCopyCommands(globeDepth, context, width, height, passState) { globeDepth._copyDepthCommand = context.createViewportQuadCommand( PassThroughDepth, { + // framebuffer: globeDepth._colorFramebuffer._multisampleFramebuffer.getColorFramebuffer(), uniformMap: { u_depthTexture: function () { return globeDepth._colorFramebuffer.getDepthStencilTexture(); @@ -166,6 +173,7 @@ function updateCopyCommands(globeDepth, context, width, height, passState) { globeDepth._tempCopyDepthCommand = context.createViewportQuadCommand( PassThroughDepth, { + // framebuffer: globeDepth.framebuffer, uniformMap: { u_depthTexture: function () { return globeDepth._tempCopyDepthTexture; @@ -213,6 +221,7 @@ GlobeDepth.prototype.update = function ( context, passState, viewport, + numSamples, hdr, clearGlobeDepth ) { @@ -224,7 +233,14 @@ GlobeDepth.prototype.update = function ( ? PixelDatatype.HALF_FLOAT : PixelDatatype.FLOAT : PixelDatatype.UNSIGNED_BYTE; - this._colorFramebuffer.update(context, width, height, 1, pixelDatatype); + this._numSamples = numSamples; + this._colorFramebuffer.update( + context, + width, + height, + numSamples, + pixelDatatype + ); this._copyDepthFramebuffer.update(context, width, height); updateCopyCommands(this, context, width, height, passState); context.uniformState.globeDepthTexture = undefined; @@ -235,6 +251,7 @@ GlobeDepth.prototype.update = function ( GlobeDepth.prototype.executeCopyDepth = function (context, passState) { if (defined(this._copyDepthCommand)) { + this._colorFramebuffer._multisampleFramebuffer.blitFramebuffers(context); this._copyDepthCommand.execute(context, passState); context.uniformState.globeDepthTexture = this._copyDepthFramebuffer.getColorTexture(); } @@ -243,9 +260,12 @@ GlobeDepth.prototype.executeCopyDepth = function (context, passState) { GlobeDepth.prototype.executeUpdateDepth = function ( context, passState, - clearGlobeDepth + clearGlobeDepth, + depthTexture ) { - var depthTextureToCopy = passState.framebuffer.depthStencilTexture; + var depthTextureToCopy = defined(depthTexture) + ? depthTexture + : passState.framebuffer.depthStencilTexture; if ( clearGlobeDepth || depthTextureToCopy !== this._colorFramebuffer.getDepthStencilTexture() @@ -268,11 +288,8 @@ GlobeDepth.prototype.executeUpdateDepth = function ( this._tempCopyDepthFramebuffer.update(context, width, height); var colorTexture = this._copyDepthFramebuffer.getColorTexture(); - var depthStencilTexture = passState.framebuffer.depthStencilTexture; this._updateDepthFramebuffer.setColorTexture(colorTexture, 0); - this._updateDepthFramebuffer.setDepthStencilTexture( - depthStencilTexture - ); + this._updateDepthFramebuffer.setDepthStencilTexture(depthTextureToCopy); this._updateDepthFramebuffer.update(context, width, height); updateCopyCommands(this, context, width, height, passState); @@ -304,6 +321,18 @@ GlobeDepth.prototype.clear = function (context, passState, clearColor) { } }; +GlobeDepth.prototype.prepareColorFramebuffer = function (context) { + if (this._numSamples > 1) { + this._colorFramebuffer.prepareColorFramebuffer(context); + } +}; + +GlobeDepth.prototype.prepareRenderFramebuffer = function () { + if (this._numSamples > 1) { + this._colorFramebuffer.prepareRenderFramebuffer(); + } +}; + GlobeDepth.prototype.isDestroyed = function () { return false; }; diff --git a/Source/Scene/InvertClassification.js b/Source/Scene/InvertClassification.js index 22f2e713f14f..ee7c7d8a9ccf 100644 --- a/Source/Scene/InvertClassification.js +++ b/Source/Scene/InvertClassification.js @@ -5,6 +5,8 @@ import PixelFormat from "../Core/PixelFormat.js"; import ClearCommand from "../Renderer/ClearCommand.js"; import FramebufferManager from "../Renderer/FramebufferManager.js"; import PixelDatatype from "../Renderer/PixelDatatype.js"; +import Renderbuffer from "../Renderer/Renderbuffer.js"; +import RenderbufferFormat from "../Renderer/RenderbufferFormat.js"; import RenderState from "../Renderer/RenderState.js"; import ShaderSource from "../Renderer/ShaderSource.js"; import Texture from "../Renderer/Texture.js"; @@ -22,6 +24,7 @@ function InvertClassification() { this._previousFramebuffer = undefined; this._depthStencilTexture = undefined; + this._depthStencilRenderbuffer = undefined; this._fbo = new FramebufferManager({ createDepthAttachments: false, }); @@ -167,7 +170,7 @@ var opaqueFS = "#endif\n" + "}\n"; -InvertClassification.prototype.update = function (context) { +InvertClassification.prototype.update = function (context, numSamples) { var texture = this._fbo.getColorTexture(); var previousFramebufferChanged = this.previousFramebuffer !== this._previousFramebuffer; @@ -190,6 +193,15 @@ InvertClassification.prototype.update = function (context) { pixelFormat: PixelFormat.DEPTH_STENCIL, pixelDatatype: PixelDatatype.UNSIGNED_INT_24_8, }); + if (this._fbo._numSamples > 1) { + this._depthStencilRenderbuffer = new Renderbuffer({ + context: context, + width: width, + height: height, + format: RenderbufferFormat.DEPTH24_STENCIL8, + numSamples: this._fbo._numSamples, + }); + } } } @@ -204,18 +216,18 @@ InvertClassification.prototype.update = function (context) { var depthStencilTexture; var depthStencilRenderbuffer; if (defined(this._previousFramebuffer)) { - depthStencilTexture = this._previousFramebuffer.depthStencilTexture; - depthStencilRenderbuffer = this._previousFramebuffer - .depthStencilRenderbuffer; + depthStencilTexture = this._previousFramebuffer.getDepthStencilTexture(); + depthStencilRenderbuffer = this._previousFramebuffer.getDepthStencilRenderbuffer(); } else { depthStencilTexture = this._depthStencilTexture; + depthStencilRenderbuffer = this._depthStencilRenderbuffer; } this._fbo.setDepthStencilTexture(depthStencilTexture); if (defined(depthStencilRenderbuffer)) { this._fbo.setDepthStencilRenderbuffer(depthStencilRenderbuffer); } - this._fbo.update(context, width, height); + this._fbo.update(context, width, height, numSamples); if (!defined(this._previousFramebuffer)) { this._fboClassified.setDepthStencilTexture(depthStencilTexture); @@ -302,6 +314,7 @@ InvertClassification.prototype.executeClassified = function ( if (!defined(this._previousFramebuffer)) { var framebuffer = passState.framebuffer; + this._fbo._multisampleFramebuffer.blitFramebuffers(context); passState.framebuffer = this._fboClassified.framebuffer; this._translucentCommand.execute(context, passState); @@ -326,6 +339,8 @@ InvertClassification.prototype.destroy = function () { this._fboClassified.destroy(); this._depthStencilTexture = this._depthStencilTexture && this._depthStencilTexture.destroy(); + this._depthStencilRenderbuffer = + this._depthStencilRenderbuffer && this._depthStencilRenderbuffer.destroy(); if (defined(this._unclassifiedCommand)) { this._unclassifiedCommand.shaderProgram = diff --git a/Source/Scene/OIT.js b/Source/Scene/OIT.js index b66b0b18b7b3..bc5a92dc4d52 100644 --- a/Source/Scene/OIT.js +++ b/Source/Scene/OIT.js @@ -843,7 +843,7 @@ OIT.prototype.clear = function (context, passState, clearColor) { }; OIT.prototype.isSupported = function () { - return this._translucentMRTSupport || this._translucentMultipassSupport; + return false; //this._translucentMRTSupport || this._translucentMultipassSupport; }; OIT.prototype.isDestroyed = function () { diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js index a9e5b90b2634..3e6e31d70c46 100644 --- a/Source/Scene/Scene.js +++ b/Source/Scene/Scene.js @@ -594,7 +594,7 @@ function Scene(options) { * @type {Number} * @default 1 */ - this.numberSamples = 1; + this.numberSamples = 4; /** * Post processing effects applied to the final render. @@ -2383,9 +2383,15 @@ function executeCommands(scene, passState) { } clearDepth.execute(context, passState); + // passState.framebuffer = view.globeDepth._colorFramebuffer._multisampleFramebuffer.getColorFramebuffer(); + // clearDepth.execute(context, passState); + // passState.framebuffer = view.globeDepth._colorFramebuffer._multisampleFramebuffer.getRenderFramebuffer(); if (context.stencilBuffer) { clearStencil.execute(context, passState); + // passState.framebuffer = view.globeDepth._colorFramebuffer._multisampleFramebuffer.getColorFramebuffer(); + // clearStencil.execute(context, passState); + // passState.framebuffer = view.globeDepth._colorFramebuffer._multisampleFramebuffer.getRenderFramebuffer(); } us.updatePass(Pass.GLOBE); @@ -2434,6 +2440,9 @@ function executeCommands(scene, passState) { if (clearGlobeDepth) { clearDepth.execute(context, passState); + // passState.framebuffer = globeDepth._colorFramebuffer._multisampleFramebuffer.getColorFramebuffer(); + // clearDepth.execute(context, passState); + // passState.framebuffer = globeDepth._colorFramebuffer._multisampleFramebuffer.getRenderFramebuffer(); if (useDepthPlane) { depthPlane.execute(context, passState); } @@ -2456,7 +2465,12 @@ function executeCommands(scene, passState) { if (length > 0) { if (defined(globeDepth) && environmentState.useGlobeDepthFramebuffer) { + globeDepth._colorFramebuffer._multisampleFramebuffer.blitFramebuffers( + context + ); + passState.framebuffer = globeDepth._colorFramebuffer._multisampleFramebuffer.getColorFramebuffer(); globeDepth.executeUpdateDepth(context, passState, clearGlobeDepth); + passState.framebuffer = globeDepth._colorFramebuffer._multisampleFramebuffer.getRenderFramebuffer(); } // Draw classifications. Modifies 3D Tiles color. @@ -2505,6 +2519,7 @@ function executeCommands(scene, passState) { // scene._invertClassification.clear(context, passState); + // var opaqueClassificationFramebuffer = view.globeDepth._colorFramebuffer._multisampleFramebuffer.getColorFramebuffer(); var opaqueClassificationFramebuffer = passState.framebuffer; passState.framebuffer = scene._invertClassification._fbo.framebuffer; @@ -2517,7 +2532,17 @@ function executeCommands(scene, passState) { } if (defined(globeDepth) && environmentState.useGlobeDepthFramebuffer) { - globeDepth.executeUpdateDepth(context, passState, clearGlobeDepth); + scene._invertClassification._fbo._multisampleFramebuffer.blitFramebuffers( + context + ); + passState.framebuffer = scene._invertClassification._fbo._multisampleFramebuffer.getColorFramebuffer(); + globeDepth.executeUpdateDepth( + context, + passState, + clearGlobeDepth, + globeDepth.depthStencilTexture + ); + passState.framebuffer = scene._invertClassification._fbo._multisampleFramebuffer.getRenderFramebuffer(); } // Set stencil @@ -2625,7 +2650,7 @@ function executeCommands(scene, passState) { // PERFORMANCE_IDEA: Use MRT to avoid the extra copy. var depthStencilTexture = renderTranslucentDepthForPick ? passState.framebuffer.depthStencilTexture - : globeDepth.framebuffer.depthStencilTexture; + : globeDepth.depthStencilTexture; var pickDepth = scene._picking.getPickDepth(scene, index); pickDepth.update(context, depthStencilTexture); pickDepth.executeCopyDepth(context, passState); @@ -3373,6 +3398,7 @@ function updateAndClearFramebuffers(scene, passState, clearColor) { context, passState, view.viewport, + scene.numberSamples, scene._hdr, environmentState.clearGlobeDepth ); @@ -3435,13 +3461,14 @@ function updateAndClearFramebuffers(scene, passState, clearColor) { var depthFramebuffer; if (scene.frameState.invertClassificationColor.alpha === 1.0) { if (environmentState.useGlobeDepthFramebuffer) { - depthFramebuffer = view.globeDepth.framebuffer; + // view.globeDepth._colorFramebuffer._multisampleFramebuffer.blitFramebuffers(context); + depthFramebuffer = view.globeDepth._colorFramebuffer; //._multisampleFramebuffer.getRenderFramebuffer(); } } if (defined(depthFramebuffer) || context.depthTexture) { scene._invertClassification.previousFramebuffer = depthFramebuffer; - scene._invertClassification.update(context); + scene._invertClassification.update(context, scene.numberSamples); scene._invertClassification.clear(context, passState); if (scene.frameState.invertClassificationColor.alpha < 1.0 && useOIT) { @@ -3482,9 +3509,10 @@ Scene.prototype.resolveFramebuffers = function (passState) { var usePostProcess = environmentState.usePostProcess; var defaultFramebuffer = environmentState.originalFramebuffer; - var globeFramebuffer = useGlobeDepthFramebuffer - ? globeDepth.framebuffer - : undefined; + globeDepth._colorFramebuffer._multisampleFramebuffer.blitFramebuffers( + context + ); + var globeFramebuffer = useGlobeDepthFramebuffer ? globeDepth : undefined; var sceneFramebuffer = view.sceneFramebuffer.framebuffer; var idFramebuffer = view.sceneFramebuffer.idFramebuffer; @@ -3504,9 +3532,8 @@ Scene.prototype.resolveFramebuffers = function (passState) { } if (usePostProcess) { - if (this.numberSamples > 1) { - sceneFramebuffer = view.sceneFramebuffer.blitFramebuffers(context); - } + view.sceneFramebuffer.prepareColorFramebuffer(context); + sceneFramebuffer = view.sceneFramebuffer.framebuffer; // todo@eli: need color FB here var inputFramebuffer = sceneFramebuffer; if (useGlobeDepthFramebuffer && !useOIT) { inputFramebuffer = globeFramebuffer; diff --git a/Source/Scene/SceneFramebuffer.js b/Source/Scene/SceneFramebuffer.js index 46707b278545..bbfc9f1aa2dd 100644 --- a/Source/Scene/SceneFramebuffer.js +++ b/Source/Scene/SceneFramebuffer.js @@ -8,6 +8,7 @@ import PixelDatatype from "../Renderer/PixelDatatype.js"; * @private */ function SceneFramebuffer() { + this._numSamples = 1; this._colorFramebuffer = new FramebufferManager({ depthStencil: true, supportsDepthTexture: true, @@ -42,6 +43,11 @@ Object.defineProperties(SceneFramebuffer.prototype, { return this._idFramebuffer.framebuffer; }, }, + depthStencilTexture: { + get: function () { + return this._colorFramebuffer.getDepthStencilTexture(); + }, + }, }); SceneFramebuffer.prototype.update = function ( @@ -57,6 +63,7 @@ SceneFramebuffer.prototype.update = function ( ? PixelDatatype.HALF_FLOAT : PixelDatatype.FLOAT : PixelDatatype.UNSIGNED_BYTE; + this._numSamples = numSamples; this._colorFramebuffer.update( context, width, @@ -82,8 +89,10 @@ SceneFramebuffer.prototype.getIdFramebuffer = function () { return this._idFramebuffer.framebuffer; }; -SceneFramebuffer.prototype.blitFramebuffers = function (context) { - return this._colorFramebuffer.blitFramebuffers(context); +SceneFramebuffer.prototype.prepareColorFramebuffer = function (context) { + if (this._numSamples > 1) { + this._colorFramebuffer.prepareColorFramebuffer(context); + } }; SceneFramebuffer.prototype.isDestroyed = function () { From 59d7d2b4de8c019a74fd32c84c7715b37c30e351 Mon Sep 17 00:00:00 2001 From: Eli Bogomolny Date: Thu, 20 Jan 2022 20:26:21 -0500 Subject: [PATCH 03/32] Invert classification with changing numSamples, cleanup --- Source/Renderer/FramebufferManager.js | 15 +------- Source/Scene/GlobeDepth.js | 22 ++++------- Source/Scene/InvertClassification.js | 39 ++++++++++++++----- Source/Scene/OIT.js | 25 ++++++++++--- Source/Scene/Scene.js | 54 ++++++++++++--------------- 5 files changed, 81 insertions(+), 74 deletions(-) diff --git a/Source/Renderer/FramebufferManager.js b/Source/Renderer/FramebufferManager.js index d028fcb65407..6f4612d3aebb 100644 --- a/Source/Renderer/FramebufferManager.js +++ b/Source/Renderer/FramebufferManager.js @@ -100,10 +100,7 @@ Object.defineProperties(FramebufferManager.prototype, { }, status: { get: function () { - if (this._numSamples > 1) { - return this._multisampleFramebuffer.getFramebuffer().status; - } - return this._framebuffer.status; + return this.framebuffer.status; }, }, }); @@ -115,7 +112,6 @@ FramebufferManager.prototype.isDirty = function ( pixelDatatype, pixelFormat ) { - // return true; var dimensionChanged = this._width !== width || this._height !== height; var samplesChanged = this._numSamples !== numSamples; var pixelChanged = @@ -412,12 +408,6 @@ FramebufferManager.prototype.prepareColorFramebuffer = function (context) { } }; -FramebufferManager.prototype.prepareRenderFramebuffer = function () { - if (this._numSamples > 1) { - this._multisampleFramebuffer.setRenderAsDefault(true); - } -}; - FramebufferManager.prototype.clear = function ( context, clearCommand, @@ -455,9 +445,6 @@ FramebufferManager.prototype.destroy = function () { this._colorTextures[i].destroy(); this._colorTextures[i] = undefined; } - } - length = this._colorRenderbuffers.length; - for (i = 0; i < length; ++i) { var renderbuffer = this._colorRenderbuffers[i]; if (defined(renderbuffer) && !renderbuffer.isDestroyed()) { this._colorRenderbuffers[i].destroy(); diff --git a/Source/Scene/GlobeDepth.js b/Source/Scene/GlobeDepth.js index ef228820475d..d1cb13251d83 100644 --- a/Source/Scene/GlobeDepth.js +++ b/Source/Scene/GlobeDepth.js @@ -138,7 +138,6 @@ function updateCopyCommands(globeDepth, context, width, height, passState) { globeDepth._copyDepthCommand = context.createViewportQuadCommand( PassThroughDepth, { - // framebuffer: globeDepth._colorFramebuffer._multisampleFramebuffer.getColorFramebuffer(), uniformMap: { u_depthTexture: function () { return globeDepth._colorFramebuffer.getDepthStencilTexture(); @@ -173,7 +172,6 @@ function updateCopyCommands(globeDepth, context, width, height, passState) { globeDepth._tempCopyDepthCommand = context.createViewportQuadCommand( PassThroughDepth, { - // framebuffer: globeDepth.framebuffer, uniformMap: { u_depthTexture: function () { return globeDepth._tempCopyDepthTexture; @@ -249,9 +247,15 @@ GlobeDepth.prototype.update = function ( this._clearGlobeDepth = clearGlobeDepth; }; +GlobeDepth.prototype.prepareTextures = function (context) { + if (this._numSamples > 1) { + this._colorFramebuffer._multisampleFramebuffer.blitFramebuffers(context); + } +}; + GlobeDepth.prototype.executeCopyDepth = function (context, passState) { if (defined(this._copyDepthCommand)) { - this._colorFramebuffer._multisampleFramebuffer.blitFramebuffers(context); + this.prepareTextures(context); this._copyDepthCommand.execute(context, passState); context.uniformState.globeDepthTexture = this._copyDepthFramebuffer.getColorTexture(); } @@ -321,18 +325,6 @@ GlobeDepth.prototype.clear = function (context, passState, clearColor) { } }; -GlobeDepth.prototype.prepareColorFramebuffer = function (context) { - if (this._numSamples > 1) { - this._colorFramebuffer.prepareColorFramebuffer(context); - } -}; - -GlobeDepth.prototype.prepareRenderFramebuffer = function () { - if (this._numSamples > 1) { - this._colorFramebuffer.prepareRenderFramebuffer(); - } -}; - GlobeDepth.prototype.isDestroyed = function () { return false; }; diff --git a/Source/Scene/InvertClassification.js b/Source/Scene/InvertClassification.js index ee7c7d8a9ccf..9fe03a362a03 100644 --- a/Source/Scene/InvertClassification.js +++ b/Source/Scene/InvertClassification.js @@ -20,6 +20,7 @@ import StencilOperation from "./StencilOperation.js"; * @private */ function InvertClassification() { + this._numSamples = 1; this.previousFramebuffer = undefined; this._previousFramebuffer = undefined; @@ -170,20 +171,29 @@ var opaqueFS = "#endif\n" + "}\n"; -InvertClassification.prototype.update = function (context, numSamples) { +InvertClassification.prototype.update = function ( + context, + numSamples, + globeFBM +) { var texture = this._fbo.getColorTexture(); var previousFramebufferChanged = this.previousFramebuffer !== this._previousFramebuffer; this._previousFramebuffer = this.previousFramebuffer; + var samplesChanged = this._numSamples !== numSamples; var width = context.drawingBufferWidth; var height = context.drawingBufferHeight; var textureChanged = !defined(texture) || texture.width !== width || texture.height !== height; - if (textureChanged || previousFramebufferChanged) { + if (textureChanged || previousFramebufferChanged || samplesChanged) { + this._numSamples = numSamples; this._depthStencilTexture = this._depthStencilTexture && this._depthStencilTexture.destroy(); + this._depthStencilRenderbuffer = + this._depthStencilRenderbuffer && + this._depthStencilRenderbuffer.destroy(); if (!defined(this._previousFramebuffer)) { this._depthStencilTexture = new Texture({ @@ -193,13 +203,13 @@ InvertClassification.prototype.update = function (context, numSamples) { pixelFormat: PixelFormat.DEPTH_STENCIL, pixelDatatype: PixelDatatype.UNSIGNED_INT_24_8, }); - if (this._fbo._numSamples > 1) { + if (numSamples > 1) { this._depthStencilRenderbuffer = new Renderbuffer({ context: context, width: width, height: height, format: RenderbufferFormat.DEPTH24_STENCIL8, - numSamples: this._fbo._numSamples, + numSamples: numSamples, }); } } @@ -208,7 +218,8 @@ InvertClassification.prototype.update = function (context, numSamples) { if ( !defined(this._fbo.framebuffer) || textureChanged || - previousFramebufferChanged + previousFramebufferChanged || + samplesChanged ) { this._fbo.destroy(); this._fboClassified.destroy(); @@ -216,8 +227,8 @@ InvertClassification.prototype.update = function (context, numSamples) { var depthStencilTexture; var depthStencilRenderbuffer; if (defined(this._previousFramebuffer)) { - depthStencilTexture = this._previousFramebuffer.getDepthStencilTexture(); - depthStencilRenderbuffer = this._previousFramebuffer.getDepthStencilRenderbuffer(); + depthStencilTexture = globeFBM.getDepthStencilTexture(); + depthStencilRenderbuffer = globeFBM.getDepthStencilRenderbuffer(); } else { depthStencilTexture = this._depthStencilTexture; depthStencilRenderbuffer = this._depthStencilRenderbuffer; @@ -241,7 +252,11 @@ InvertClassification.prototype.update = function (context, numSamples) { this._rsDefault = RenderState.fromCache(rsDefault); } - if (!defined(this._unclassifiedCommand) || previousFramebufferChanged) { + if ( + !defined(this._unclassifiedCommand) || + previousFramebufferChanged || + samplesChanged + ) { if (defined(this._unclassifiedCommand)) { this._unclassifiedCommand.shaderProgram = this._unclassifiedCommand.shaderProgram && @@ -298,6 +313,12 @@ InvertClassification.prototype.update = function (context, numSamples) { } }; +InvertClassification.prototype.prepareTextures = function (context) { + if (this._fbo._numSamples > 1) { + this._fbo._multisampleFramebuffer.blitFramebuffers(context); + } +}; + InvertClassification.prototype.clear = function (context, passState) { if (defined(this._previousFramebuffer)) { this._fbo.clear(context, this._clearColorCommand, passState); @@ -314,7 +335,7 @@ InvertClassification.prototype.executeClassified = function ( if (!defined(this._previousFramebuffer)) { var framebuffer = passState.framebuffer; - this._fbo._multisampleFramebuffer.blitFramebuffers(context); + this.prepareTextures(context); passState.framebuffer = this._fboClassified.framebuffer; this._translucentCommand.execute(context, passState); diff --git a/Source/Scene/OIT.js b/Source/Scene/OIT.js index bc5a92dc4d52..e0de727d4df8 100644 --- a/Source/Scene/OIT.js +++ b/Source/Scene/OIT.js @@ -20,6 +20,7 @@ import BlendFunction from "./BlendFunction.js"; * @private */ function OIT(context) { + this._numSamples = 1; // We support multipass for the Chrome D3D9 backend and ES 2.0 on mobile. this._translucentMultipassSupport = false; this._translucentMRTSupport = false; @@ -201,14 +202,20 @@ function updateFramebuffers(oit, context) { return supported; } -OIT.prototype.update = function (context, passState, framebuffer, useHDR) { +OIT.prototype.update = function ( + context, + passState, + framebuffer, + useHDR, + numSamples +) { if (!this.isSupported()) { return; } this._opaqueFBO = framebuffer; this._opaqueTexture = framebuffer.getColorTexture(0); - this._depthStencilTexture = framebuffer.depthStencilTexture; + this._depthStencilTexture = framebuffer.getDepthStencilTexture(); var width = this._opaqueTexture.width; var height = this._opaqueTexture.height; @@ -219,11 +226,17 @@ OIT.prototype.update = function (context, passState, framebuffer, useHDR) { accumulationTexture.width !== width || accumulationTexture.height !== height || useHDR !== this._useHDR; - if (textureChanged) { + var samplesChanged = this._numSamples !== numSamples; + this._numSamples = numSamples; + if (textureChanged || samplesChanged) { updateTextures(this, context, width, height); } - if (!defined(this._translucentFBO.framebuffer) || textureChanged) { + if ( + !defined(this._translucentFBO.framebuffer) || + textureChanged || + samplesChanged + ) { if (!updateFramebuffers(this, context)) { // framebuffer creation failed return; @@ -824,7 +837,7 @@ OIT.prototype.execute = function (context, passState) { OIT.prototype.clear = function (context, passState, clearColor) { var framebuffer = passState.framebuffer; - passState.framebuffer = this._opaqueFBO; + passState.framebuffer = this._opaqueFBO.framebuffer; Color.clone(clearColor, this._opaqueClearCommand.color); this._opaqueClearCommand.execute(context, passState); @@ -843,7 +856,7 @@ OIT.prototype.clear = function (context, passState, clearColor) { }; OIT.prototype.isSupported = function () { - return false; //this._translucentMRTSupport || this._translucentMultipassSupport; + return this._translucentMRTSupport || this._translucentMultipassSupport; }; OIT.prototype.isDestroyed = function () { diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js index 3e6e31d70c46..eee1c83d685e 100644 --- a/Source/Scene/Scene.js +++ b/Source/Scene/Scene.js @@ -594,7 +594,7 @@ function Scene(options) { * @type {Number} * @default 1 */ - this.numberSamples = 4; + this.numberSamples = 1; /** * Post processing effects applied to the final render. @@ -2383,15 +2383,9 @@ function executeCommands(scene, passState) { } clearDepth.execute(context, passState); - // passState.framebuffer = view.globeDepth._colorFramebuffer._multisampleFramebuffer.getColorFramebuffer(); - // clearDepth.execute(context, passState); - // passState.framebuffer = view.globeDepth._colorFramebuffer._multisampleFramebuffer.getRenderFramebuffer(); if (context.stencilBuffer) { clearStencil.execute(context, passState); - // passState.framebuffer = view.globeDepth._colorFramebuffer._multisampleFramebuffer.getColorFramebuffer(); - // clearStencil.execute(context, passState); - // passState.framebuffer = view.globeDepth._colorFramebuffer._multisampleFramebuffer.getRenderFramebuffer(); } us.updatePass(Pass.GLOBE); @@ -2440,9 +2434,6 @@ function executeCommands(scene, passState) { if (clearGlobeDepth) { clearDepth.execute(context, passState); - // passState.framebuffer = globeDepth._colorFramebuffer._multisampleFramebuffer.getColorFramebuffer(); - // clearDepth.execute(context, passState); - // passState.framebuffer = globeDepth._colorFramebuffer._multisampleFramebuffer.getRenderFramebuffer(); if (useDepthPlane) { depthPlane.execute(context, passState); } @@ -2465,12 +2456,13 @@ function executeCommands(scene, passState) { if (length > 0) { if (defined(globeDepth) && environmentState.useGlobeDepthFramebuffer) { - globeDepth._colorFramebuffer._multisampleFramebuffer.blitFramebuffers( - context + globeDepth.prepareTextures(context); + globeDepth.executeUpdateDepth( + context, + passState, + clearGlobeDepth, + globeDepth.depthStencilTexture ); - passState.framebuffer = globeDepth._colorFramebuffer._multisampleFramebuffer.getColorFramebuffer(); - globeDepth.executeUpdateDepth(context, passState, clearGlobeDepth); - passState.framebuffer = globeDepth._colorFramebuffer._multisampleFramebuffer.getRenderFramebuffer(); } // Draw classifications. Modifies 3D Tiles color. @@ -2519,7 +2511,6 @@ function executeCommands(scene, passState) { // scene._invertClassification.clear(context, passState); - // var opaqueClassificationFramebuffer = view.globeDepth._colorFramebuffer._multisampleFramebuffer.getColorFramebuffer(); var opaqueClassificationFramebuffer = passState.framebuffer; passState.framebuffer = scene._invertClassification._fbo.framebuffer; @@ -2532,17 +2523,13 @@ function executeCommands(scene, passState) { } if (defined(globeDepth) && environmentState.useGlobeDepthFramebuffer) { - scene._invertClassification._fbo._multisampleFramebuffer.blitFramebuffers( - context - ); - passState.framebuffer = scene._invertClassification._fbo._multisampleFramebuffer.getColorFramebuffer(); + scene._invertClassification.prepareTextures(context); globeDepth.executeUpdateDepth( context, passState, clearGlobeDepth, - globeDepth.depthStencilTexture + scene._invertClassification._fbo.getDepthStencilTexture() ); - passState.framebuffer = scene._invertClassification._fbo._multisampleFramebuffer.getRenderFramebuffer(); } // Set stencil @@ -3410,7 +3397,13 @@ function updateAndClearFramebuffers(scene, passState, clearColor) { var useOIT = (environmentState.useOIT = !picking && defined(oit) && oit.isSupported()); if (useOIT) { - oit.update(context, passState, view.globeDepth.framebuffer, scene._hdr); + oit.update( + context, + passState, + view.globeDepth._colorFramebuffer, + scene._hdr, + scene.numberSamples + ); oit.clear(context, passState, clearColor); environmentState.useOIT = oit.isSupported(); } @@ -3461,14 +3454,17 @@ function updateAndClearFramebuffers(scene, passState, clearColor) { var depthFramebuffer; if (scene.frameState.invertClassificationColor.alpha === 1.0) { if (environmentState.useGlobeDepthFramebuffer) { - // view.globeDepth._colorFramebuffer._multisampleFramebuffer.blitFramebuffers(context); - depthFramebuffer = view.globeDepth._colorFramebuffer; //._multisampleFramebuffer.getRenderFramebuffer(); + depthFramebuffer = view.globeDepth.framebuffer; } } if (defined(depthFramebuffer) || context.depthTexture) { scene._invertClassification.previousFramebuffer = depthFramebuffer; - scene._invertClassification.update(context, scene.numberSamples); + scene._invertClassification.update( + context, + scene.numberSamples, + view.globeDepth._colorFramebuffer + ); scene._invertClassification.clear(context, passState); if (scene.frameState.invertClassificationColor.alpha < 1.0 && useOIT) { @@ -3509,9 +3505,7 @@ Scene.prototype.resolveFramebuffers = function (passState) { var usePostProcess = environmentState.usePostProcess; var defaultFramebuffer = environmentState.originalFramebuffer; - globeDepth._colorFramebuffer._multisampleFramebuffer.blitFramebuffers( - context - ); + globeDepth.prepareTextures(context); var globeFramebuffer = useGlobeDepthFramebuffer ? globeDepth : undefined; var sceneFramebuffer = view.sceneFramebuffer.framebuffer; var idFramebuffer = view.sceneFramebuffer.idFramebuffer; @@ -3533,7 +3527,7 @@ Scene.prototype.resolveFramebuffers = function (passState) { if (usePostProcess) { view.sceneFramebuffer.prepareColorFramebuffer(context); - sceneFramebuffer = view.sceneFramebuffer.framebuffer; // todo@eli: need color FB here + sceneFramebuffer = view.sceneFramebuffer._colorFramebuffer._multisampleFramebuffer.getColorFramebuffer(); // todo@eli: need color FB here var inputFramebuffer = sceneFramebuffer; if (useGlobeDepthFramebuffer && !useOIT) { inputFramebuffer = globeFramebuffer; From b9ac432625104340ce0da6972824401e5617095f Mon Sep 17 00:00:00 2001 From: Eli Bogomolny Date: Fri, 21 Jan 2022 13:17:07 -0500 Subject: [PATCH 04/32] Cleanup for shared attachments --- Source/Renderer/FramebufferManager.js | 65 ++++++++++++++++++++------- Source/Renderer/Renderbuffer.js | 2 + Source/Scene/InvertClassification.js | 8 ++-- Source/Scene/Scene.js | 4 +- 4 files changed, 60 insertions(+), 19 deletions(-) diff --git a/Source/Renderer/FramebufferManager.js b/Source/Renderer/FramebufferManager.js index 6f4612d3aebb..df1d743b8e45 100644 --- a/Source/Renderer/FramebufferManager.js +++ b/Source/Renderer/FramebufferManager.js @@ -436,35 +436,70 @@ FramebufferManager.prototype.destroyFramebuffer = function () { }; FramebufferManager.prototype.destroy = function () { - if (this._color && this._createColorAttachments) { + if (this._color) { var i; var length = this._colorTextures.length; for (i = 0; i < length; ++i) { var texture = this._colorTextures[i]; - if (defined(texture) && !texture.isDestroyed()) { - this._colorTextures[i].destroy(); + if (this._createColorAttachments) { + if (defined(texture) && !texture.isDestroyed()) { + this._colorTextures[i].destroy(); + this._colorTextures[i] = undefined; + } + } + if (defined(texture) && texture.isDestroyed()) { this._colorTextures[i] = undefined; } var renderbuffer = this._colorRenderbuffers[i]; - if (defined(renderbuffer) && !renderbuffer.isDestroyed()) { - this._colorRenderbuffers[i].destroy(); + if (this._createColorAttachments) { + if (defined(renderbuffer) && !renderbuffer.isDestroyed()) { + this._colorRenderbuffers[i].destroy(); + this._colorRenderbuffers[i] = undefined; + } + } + if (defined(renderbuffer) && renderbuffer.isDestroyed()) { this._colorRenderbuffers[i] = undefined; } } } - if (this._depthStencil && this._createDepthAttachments) { - this._depthStencilTexture = - this._depthStencilTexture && this._depthStencilTexture.destroy(); - this._depthStencilRenderbuffer = - this._depthStencilRenderbuffer && - this._depthStencilRenderbuffer.destroy(); + if (this._depthStencil) { + if (this._createDepthAttachments) { + this._depthStencilTexture = + this._depthStencilTexture && this._depthStencilTexture.destroy(); + this._depthStencilRenderbuffer = + this._depthStencilRenderbuffer && + this._depthStencilRenderbuffer.destroy(); + } + if ( + defined(this._depthStencilTexture) && + this._depthStencilTexture.isDestroyed() + ) { + this._depthStencilTexture = undefined; + } + if ( + defined(this._depthStencilRenderbuffer) && + this._depthStencilRenderbuffer.isDestroyed() + ) { + this._depthStencilRenderbuffer = undefined; + } } - if (this._depth && this._createDepthAttachments) { - this._depthTexture = this._depthTexture && this._depthTexture.destroy(); - this._depthRenderbuffer = - this._depthRenderbuffer && this._depthRenderbuffer.destroy(); + if (this._depth) { + if (this._createDepthAttachments) { + this._depthTexture = this._depthTexture && this._depthTexture.destroy(); + this._depthRenderbuffer = + this._depthRenderbuffer && this._depthRenderbuffer.destroy(); + } + if (defined(this._depthTexture) && this._depthTexture.isDestroyed()) { + this._depthTexture = undefined; + } + if ( + defined(this._depthRenderbuffer) && + this._depthRenderbuffer.isDestroyed() + ) { + this._depthRenderbuffer = undefined; + } } this.destroyFramebuffer(); diff --git a/Source/Renderer/Renderbuffer.js b/Source/Renderer/Renderbuffer.js index 6ee89796d68f..6f3c8ddbf680 100644 --- a/Source/Renderer/Renderbuffer.js +++ b/Source/Renderer/Renderbuffer.js @@ -5,11 +5,13 @@ import destroyObject from "../Core/destroyObject.js"; import DeveloperError from "../Core/DeveloperError.js"; import ContextLimits from "./ContextLimits.js"; import RenderbufferFormat from "./RenderbufferFormat.js"; +import createGuid from "../Core/createGuid.js"; /** * @private */ function Renderbuffer(options) { + this._id = createGuid(); options = defaultValue(options, defaultValue.EMPTY_OBJECT); //>>includeStart('debug', pragmas.debug); diff --git a/Source/Scene/InvertClassification.js b/Source/Scene/InvertClassification.js index 9fe03a362a03..054c978991da 100644 --- a/Source/Scene/InvertClassification.js +++ b/Source/Scene/InvertClassification.js @@ -27,9 +27,11 @@ function InvertClassification() { this._depthStencilTexture = undefined; this._depthStencilRenderbuffer = undefined; this._fbo = new FramebufferManager({ + depthStencil: true, createDepthAttachments: false, }); this._fboClassified = new FramebufferManager({ + depthStencil: true, createDepthAttachments: false, }); @@ -174,7 +176,7 @@ var opaqueFS = InvertClassification.prototype.update = function ( context, numSamples, - globeFBM + globeFramebuffer ) { var texture = this._fbo.getColorTexture(); var previousFramebufferChanged = @@ -227,8 +229,8 @@ InvertClassification.prototype.update = function ( var depthStencilTexture; var depthStencilRenderbuffer; if (defined(this._previousFramebuffer)) { - depthStencilTexture = globeFBM.getDepthStencilTexture(); - depthStencilRenderbuffer = globeFBM.getDepthStencilRenderbuffer(); + depthStencilTexture = globeFramebuffer.getDepthStencilTexture(); + depthStencilRenderbuffer = globeFramebuffer.getDepthStencilRenderbuffer(); } else { depthStencilTexture = this._depthStencilTexture; depthStencilRenderbuffer = this._depthStencilRenderbuffer; diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js index eee1c83d685e..f5d6216b3c28 100644 --- a/Source/Scene/Scene.js +++ b/Source/Scene/Scene.js @@ -3499,13 +3499,15 @@ Scene.prototype.resolveFramebuffers = function (passState) { var environmentState = this._environmentState; var view = this._view; var globeDepth = view.globeDepth; + if (defined(globeDepth)) { + globeDepth.prepareTextures(context); + } var useOIT = environmentState.useOIT; var useGlobeDepthFramebuffer = environmentState.useGlobeDepthFramebuffer; var usePostProcess = environmentState.usePostProcess; var defaultFramebuffer = environmentState.originalFramebuffer; - globeDepth.prepareTextures(context); var globeFramebuffer = useGlobeDepthFramebuffer ? globeDepth : undefined; var sceneFramebuffer = view.sceneFramebuffer.framebuffer; var idFramebuffer = view.sceneFramebuffer.idFramebuffer; From bd876c56787b7477927ffd57da22f36a68c1f4f3 Mon Sep 17 00:00:00 2001 From: Eli Bogomolny Date: Wed, 26 Jan 2022 14:13:25 -0500 Subject: [PATCH 05/32] blit globeDepth before OIT translucent commands --- Source/DataSources/EntityCluster.js | 8 ++++---- Source/Scene/OIT.js | 2 ++ Source/Scene/Scene.js | 16 ++++++++++------ 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/Source/DataSources/EntityCluster.js b/Source/DataSources/EntityCluster.js index 69793172c5cf..133bcf701b47 100644 --- a/Source/DataSources/EntityCluster.js +++ b/Source/DataSources/EntityCluster.js @@ -852,7 +852,7 @@ function updateEnable(entityCluster) { * queues the draw commands for billboards/points/labels created for entities. * @private */ -EntityCluster.prototype.update = function (frameState) { +EntityCluster.prototype.update = function (frameState, numSamples) { if (!this.show) { return; } @@ -908,13 +908,13 @@ EntityCluster.prototype.update = function (frameState) { } if (defined(this._labelCollection)) { - this._labelCollection.update(frameState); + this._labelCollection.update(frameState, numSamples); } if (defined(this._billboardCollection)) { - this._billboardCollection.update(frameState); + this._billboardCollection.update(frameState, numSamples); } if (defined(this._pointCollection)) { - this._pointCollection.update(frameState); + this._pointCollection.update(frameState, numSamples); } }; diff --git a/Source/Scene/OIT.js b/Source/Scene/OIT.js index e0de727d4df8..1979808cc6ef 100644 --- a/Source/Scene/OIT.js +++ b/Source/Scene/OIT.js @@ -40,10 +40,12 @@ function OIT(context) { colorAttachmentsLength: this._translucentMRTSupport ? 2 : 1, createColorAttachments: false, createDepthAttachments: false, + depth: true, }); this._alphaFBO = new FramebufferManager({ createColorAttachments: false, createDepthAttachments: false, + depth: true, }); this._adjustTranslucentFBO = new FramebufferManager({ diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js index f5d6216b3c28..05ac5c0d7f3c 100644 --- a/Source/Scene/Scene.js +++ b/Source/Scene/Scene.js @@ -2326,6 +2326,7 @@ function executeCommands(scene, passState) { commands, invertClassification ) { + view.globeDepth.prepareTextures(context); view.oit.executeCommands( scene, executeFunction, @@ -3508,13 +3509,15 @@ Scene.prototype.resolveFramebuffers = function (passState) { var usePostProcess = environmentState.usePostProcess; var defaultFramebuffer = environmentState.originalFramebuffer; - var globeFramebuffer = useGlobeDepthFramebuffer ? globeDepth : undefined; - var sceneFramebuffer = view.sceneFramebuffer.framebuffer; + var globeFramebuffer = useGlobeDepthFramebuffer + ? globeDepth._colorFramebuffer + : undefined; + var sceneFramebuffer = view.sceneFramebuffer._colorFramebuffer; var idFramebuffer = view.sceneFramebuffer.idFramebuffer; if (useOIT) { passState.framebuffer = usePostProcess - ? sceneFramebuffer + ? sceneFramebuffer.framebuffer : defaultFramebuffer; view.oit.execute(context, passState); } @@ -3529,7 +3532,6 @@ Scene.prototype.resolveFramebuffers = function (passState) { if (usePostProcess) { view.sceneFramebuffer.prepareColorFramebuffer(context); - sceneFramebuffer = view.sceneFramebuffer._colorFramebuffer._multisampleFramebuffer.getColorFramebuffer(); // todo@eli: need color FB here var inputFramebuffer = sceneFramebuffer; if (useGlobeDepthFramebuffer && !useOIT) { inputFramebuffer = globeFramebuffer; @@ -3538,8 +3540,10 @@ Scene.prototype.resolveFramebuffers = function (passState) { var postProcess = this.postProcessStages; var colorTexture = inputFramebuffer.getColorTexture(0); var idTexture = idFramebuffer.getColorTexture(0); - var depthTexture = defaultValue(globeFramebuffer, sceneFramebuffer) - .depthStencilTexture; + var depthTexture = defaultValue( + globeFramebuffer, + sceneFramebuffer + ).getDepthStencilTexture(); postProcess.execute(context, colorTexture, depthTexture, idTexture); postProcess.copy(context, defaultFramebuffer); } From 5532131911ebc9dbf10cb25121ba3c752d68ff64 Mon Sep 17 00:00:00 2001 From: Eli Bogomolny Date: Wed, 26 Jan 2022 18:16:18 -0500 Subject: [PATCH 06/32] Fix OIT debug framebufer --- Source/DataSources/EntityCluster.js | 8 ++++---- Source/Scene/OIT.js | 4 ++-- Source/Scene/Scene.js | 9 +++++++++ Specs/Renderer/FramebufferManagerSpec.js | 4 ++-- 4 files changed, 17 insertions(+), 8 deletions(-) diff --git a/Source/DataSources/EntityCluster.js b/Source/DataSources/EntityCluster.js index 133bcf701b47..69793172c5cf 100644 --- a/Source/DataSources/EntityCluster.js +++ b/Source/DataSources/EntityCluster.js @@ -852,7 +852,7 @@ function updateEnable(entityCluster) { * queues the draw commands for billboards/points/labels created for entities. * @private */ -EntityCluster.prototype.update = function (frameState, numSamples) { +EntityCluster.prototype.update = function (frameState) { if (!this.show) { return; } @@ -908,13 +908,13 @@ EntityCluster.prototype.update = function (frameState, numSamples) { } if (defined(this._labelCollection)) { - this._labelCollection.update(frameState, numSamples); + this._labelCollection.update(frameState); } if (defined(this._billboardCollection)) { - this._billboardCollection.update(frameState, numSamples); + this._billboardCollection.update(frameState); } if (defined(this._pointCollection)) { - this._pointCollection.update(frameState, numSamples); + this._pointCollection.update(frameState); } }; diff --git a/Source/Scene/OIT.js b/Source/Scene/OIT.js index 1979808cc6ef..645c7bd0b0ad 100644 --- a/Source/Scene/OIT.js +++ b/Source/Scene/OIT.js @@ -671,7 +671,7 @@ function executeTranslucentCommandsSortedMultipass( passState.framebuffer = oit._adjustAlphaFBO.framebuffer; oit._adjustAlphaCommand.execute(context, passState); - var debugFramebuffer = oit._opaqueFBO; + var debugFramebuffer = oit._opaqueFBO.framebuffer; passState.framebuffer = oit._translucentFBO.framebuffer; for (j = 0; j < length; ++j) { @@ -762,7 +762,7 @@ function executeTranslucentCommandsSortedMRT( passState.framebuffer = oit._adjustTranslucentFBO.framebuffer; oit._adjustTranslucentCommand.execute(context, passState); - var debugFramebuffer = oit._opaqueFBO; + var debugFramebuffer = oit._opaqueFBO.framebuffer; passState.framebuffer = oit._translucentFBO.framebuffer; var command; diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js index 05ac5c0d7f3c..3edc831ce863 100644 --- a/Source/Scene/Scene.js +++ b/Source/Scene/Scene.js @@ -2635,6 +2635,11 @@ function executeCommands(scene, passState) { (environmentState.useGlobeDepthFramebuffer || renderTranslucentDepthForPick) ) { + if (renderTranslucentDepthForPick) { + globeDepth.prepareTextures(context); + if (scene.numberSamples > 1) + passState.framebuffer = globeDepth._colorFramebuffer._multisampleFramebuffer.getColorFramebuffer(); + } // PERFORMANCE_IDEA: Use MRT to avoid the extra copy. var depthStencilTexture = renderTranslucentDepthForPick ? passState.framebuffer.depthStencilTexture @@ -2642,6 +2647,10 @@ function executeCommands(scene, passState) { var pickDepth = scene._picking.getPickDepth(scene, index); pickDepth.update(context, depthStencilTexture); pickDepth.executeCopyDepth(context, passState); + if (renderTranslucentDepthForPick) { + if (scene.numberSamples > 1) + passState.framebuffer = globeDepth._colorFramebuffer._multisampleFramebuffer.getRenderFramebuffer(); + } } if (picking || !usePostProcessSelected) { diff --git a/Specs/Renderer/FramebufferManagerSpec.js b/Specs/Renderer/FramebufferManagerSpec.js index cc9257d5740a..013ecd0cc6b2 100644 --- a/Specs/Renderer/FramebufferManagerSpec.js +++ b/Specs/Renderer/FramebufferManagerSpec.js @@ -389,7 +389,7 @@ describe( }); spyOn(FramebufferManager.prototype, "destroy").and.callThrough(); fbm.update(context, 1, 1); - fbm.update(context, 1, 1, PixelDatatype.UNSIGNED_BYTE); + fbm.update(context, 1, 1, 1, PixelDatatype.UNSIGNED_BYTE); expect(FramebufferManager.prototype.destroy.calls.count()).toEqual(2); }); @@ -399,7 +399,7 @@ describe( }); spyOn(FramebufferManager.prototype, "destroy").and.callThrough(); fbm.update(context, 1, 1); - fbm.update(context, 1, 1, undefined, PixelFormat.RGBA); + fbm.update(context, 1, 1, 1, undefined, PixelFormat.RGBA); expect(FramebufferManager.prototype.destroy.calls.count()).toEqual(2); }); From f56f3e4602b6444a11c32ef60cd791662801e761 Mon Sep 17 00:00:00 2001 From: Eli Bogomolny Date: Wed, 26 Jan 2022 18:32:01 -0500 Subject: [PATCH 07/32] Fix translucent tile classification spec --- Source/Renderer/FramebufferManager.js | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/Renderer/FramebufferManager.js b/Source/Renderer/FramebufferManager.js index df1d743b8e45..de1e5e11f03a 100644 --- a/Source/Renderer/FramebufferManager.js +++ b/Source/Renderer/FramebufferManager.js @@ -112,6 +112,7 @@ FramebufferManager.prototype.isDirty = function ( pixelDatatype, pixelFormat ) { + numSamples = defaultValue(numSamples, 1); var dimensionChanged = this._width !== width || this._height !== height; var samplesChanged = this._numSamples !== numSamples; var pixelChanged = From 183782fba84c56f8dfba1d58f4982df99029c694 Mon Sep 17 00:00:00 2001 From: Eli Bogomolny Date: Thu, 27 Jan 2022 11:02:28 -0500 Subject: [PATCH 08/32] Add let-const in changed files --- Source/Renderer/Framebuffer.js | 4 +- Source/Renderer/FramebufferManager.js | 16 ++-- Source/Renderer/MultisampleFramebuffer.js | 18 ++--- Source/Renderer/Renderbuffer.js | 2 +- Source/Scene/GlobeDepth.js | 4 +- Source/Scene/InvertClassification.js | 6 +- Source/Scene/OIT.js | 6 +- Source/Scene/PostProcessStageCollection.js | 78 ++++++++++---------- Source/Scene/PostProcessStageTextureCache.js | 22 +++--- Source/Scene/Scene.js | 28 +++---- Source/Scene/SceneFramebuffer.js | 6 +- 11 files changed, 95 insertions(+), 95 deletions(-) diff --git a/Source/Renderer/Framebuffer.js b/Source/Renderer/Framebuffer.js index 4fb4ce09d6db..847e5f91bca0 100644 --- a/Source/Renderer/Framebuffer.js +++ b/Source/Renderer/Framebuffer.js @@ -366,12 +366,12 @@ Framebuffer.prototype._unBind = function () { }; Framebuffer.prototype.bindDraw = function () { - var gl = this._gl; + const gl = this._gl; gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, this._framebuffer); }; Framebuffer.prototype.bindRead = function () { - var gl = this._gl; + const gl = this._gl; gl.bindFramebuffer(gl.READ_FRAMEBUFFER, this._framebuffer); }; diff --git a/Source/Renderer/FramebufferManager.js b/Source/Renderer/FramebufferManager.js index 607051ea6304..91106225214a 100644 --- a/Source/Renderer/FramebufferManager.js +++ b/Source/Renderer/FramebufferManager.js @@ -113,12 +113,12 @@ FramebufferManager.prototype.isDirty = function ( pixelFormat ) { numSamples = defaultValue(numSamples, 1); - var dimensionChanged = this._width !== width || this._height !== height; - var samplesChanged = this._numSamples !== numSamples; - var pixelChanged = + const dimensionChanged = this._width !== width || this._height !== height; + const samplesChanged = this._numSamples !== numSamples; + const pixelChanged = (defined(pixelDatatype) && this._pixelDatatype !== pixelDatatype) || (defined(pixelFormat) && this._pixelFormat !== pixelFormat); - var framebufferDefined = + const framebufferDefined = numSamples === 1 ? defined(this._framebuffer) : defined(this._multisampleFramebuffer); @@ -438,10 +438,10 @@ FramebufferManager.prototype.destroyFramebuffer = function () { FramebufferManager.prototype.destroy = function () { if (this._color) { - var i; - var length = this._colorTextures.length; + let i; + const length = this._colorTextures.length; for (i = 0; i < length; ++i) { - var texture = this._colorTextures[i]; + const texture = this._colorTextures[i]; if (this._createColorAttachments) { if (defined(texture) && !texture.isDestroyed()) { this._colorTextures[i].destroy(); @@ -451,7 +451,7 @@ FramebufferManager.prototype.destroy = function () { if (defined(texture) && texture.isDestroyed()) { this._colorTextures[i] = undefined; } - var renderbuffer = this._colorRenderbuffers[i]; + const renderbuffer = this._colorRenderbuffers[i]; if (this._createColorAttachments) { if (defined(renderbuffer) && !renderbuffer.isDestroyed()) { this._colorRenderbuffers[i].destroy(); diff --git a/Source/Renderer/MultisampleFramebuffer.js b/Source/Renderer/MultisampleFramebuffer.js index 80c6ba0ea5e5..623f17a93668 100644 --- a/Source/Renderer/MultisampleFramebuffer.js +++ b/Source/Renderer/MultisampleFramebuffer.js @@ -15,17 +15,17 @@ import Framebuffer from "./Framebuffer.js"; function MultisampleFramebuffer(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); - var context = options.context; - var colorRenderbuffers = options.colorRenderbuffers; - var colorTextures = options.colorTextures; + const context = options.context; + const colorRenderbuffers = options.colorRenderbuffers; + const colorTextures = options.colorTextures; //>>includeStart('debug', pragmas.debug); Check.defined("options.context", context); Check.defined("options.colorRenderbuffers", colorRenderbuffers); Check.defined("options.colorTextures", colorTextures); //>>includeEnd('debug'); - var depthStencilRenderbuffer = options.depthStencilRenderbuffer; - var depthStencilTexture = options.depthStencilTexture; + const depthStencilRenderbuffer = options.depthStencilRenderbuffer; + const depthStencilTexture = options.depthStencilTexture; if ( (defined(depthStencilRenderbuffer) && !defined(depthStencilTexture)) || (defined(depthStencilTexture) && !defined(depthStencilRenderbuffer)) @@ -69,10 +69,10 @@ MultisampleFramebuffer.prototype.blitFramebuffers = function (context) { // clearCommand.execute(context); this._renderFramebuffer.bindRead(); this._colorFramebuffer.bindDraw(); - var width = context.canvas.clientWidth; - var height = context.canvas.clientHeight; - var gl = context._gl; - var mask = 0; + const width = context.canvas.clientWidth; + const height = context.canvas.clientHeight; + const gl = context._gl; + let mask = 0; if (this._colorFramebuffer._colorTextures.length > 0) { mask |= gl.COLOR_BUFFER_BIT; } diff --git a/Source/Renderer/Renderbuffer.js b/Source/Renderer/Renderbuffer.js index 7fc66f3f314f..85250cb3d5b1 100644 --- a/Source/Renderer/Renderbuffer.js +++ b/Source/Renderer/Renderbuffer.js @@ -27,7 +27,7 @@ function Renderbuffer(options) { const height = defined(options.height) ? options.height : gl.drawingBufferHeight; - var numSamples = defaultValue(options.numSamples, 1); + const numSamples = defaultValue(options.numSamples, 1); //>>includeStart('debug', pragmas.debug); if (!RenderbufferFormat.validate(format)) { diff --git a/Source/Scene/GlobeDepth.js b/Source/Scene/GlobeDepth.js index 01dd80868401..065fde4a8edc 100644 --- a/Source/Scene/GlobeDepth.js +++ b/Source/Scene/GlobeDepth.js @@ -267,7 +267,7 @@ GlobeDepth.prototype.executeUpdateDepth = function ( clearGlobeDepth, depthTexture ) { - var depthTextureToCopy = defined(depthTexture) + const depthTextureToCopy = defined(depthTexture) ? depthTexture : passState.framebuffer.depthStencilTexture; if ( @@ -291,7 +291,7 @@ GlobeDepth.prototype.executeUpdateDepth = function ( this._tempCopyDepthFramebuffer.destroy(); this._tempCopyDepthFramebuffer.update(context, width, height); - var colorTexture = this._copyDepthFramebuffer.getColorTexture(); + const colorTexture = this._copyDepthFramebuffer.getColorTexture(); this._updateDepthFramebuffer.setColorTexture(colorTexture, 0); this._updateDepthFramebuffer.setDepthStencilTexture(depthTextureToCopy); this._updateDepthFramebuffer.update(context, width, height); diff --git a/Source/Scene/InvertClassification.js b/Source/Scene/InvertClassification.js index 9e97708dd533..00eb0d965e3e 100644 --- a/Source/Scene/InvertClassification.js +++ b/Source/Scene/InvertClassification.js @@ -178,11 +178,11 @@ InvertClassification.prototype.update = function ( numSamples, globeFramebuffer ) { - var texture = this._fbo.getColorTexture(); - var previousFramebufferChanged = + const texture = this._fbo.getColorTexture(); + const previousFramebufferChanged = this.previousFramebuffer !== this._previousFramebuffer; this._previousFramebuffer = this.previousFramebuffer; - var samplesChanged = this._numSamples !== numSamples; + const samplesChanged = this._numSamples !== numSamples; const width = context.drawingBufferWidth; const height = context.drawingBufferHeight; diff --git a/Source/Scene/OIT.js b/Source/Scene/OIT.js index cab57c0e98a9..e882d52d40d7 100644 --- a/Source/Scene/OIT.js +++ b/Source/Scene/OIT.js @@ -228,7 +228,7 @@ OIT.prototype.update = function ( accumulationTexture.width !== width || accumulationTexture.height !== height || useHDR !== this._useHDR; - var samplesChanged = this._numSamples !== numSamples; + const samplesChanged = this._numSamples !== numSamples; this._numSamples = numSamples; if (textureChanged || samplesChanged) { updateTextures(this, context, width, height); @@ -671,7 +671,7 @@ function executeTranslucentCommandsSortedMultipass( passState.framebuffer = oit._adjustAlphaFBO.framebuffer; oit._adjustAlphaCommand.execute(context, passState); - var debugFramebuffer = oit._opaqueFBO.framebuffer; + const debugFramebuffer = oit._opaqueFBO.framebuffer; passState.framebuffer = oit._translucentFBO.framebuffer; for (j = 0; j < length; ++j) { @@ -762,7 +762,7 @@ function executeTranslucentCommandsSortedMRT( passState.framebuffer = oit._adjustTranslucentFBO.framebuffer; oit._adjustTranslucentCommand.execute(context, passState); - var debugFramebuffer = oit._opaqueFBO.framebuffer; + const debugFramebuffer = oit._opaqueFBO.framebuffer; passState.framebuffer = oit._translucentFBO.framebuffer; let command; diff --git a/Source/Scene/PostProcessStageCollection.js b/Source/Scene/PostProcessStageCollection.js index 15183cf950b8..ba081b42c0b7 100644 --- a/Source/Scene/PostProcessStageCollection.js +++ b/Source/Scene/PostProcessStageCollection.js @@ -34,10 +34,10 @@ const stackScratch = []; * @constructor */ function PostProcessStageCollection() { - var fxaa = PostProcessStageLibrary.createFXAAStage(); - var passThrough = PostProcessStageLibrary.createPassThroughStage(); - var ao = PostProcessStageLibrary.createAmbientOcclusionStage(); - var bloom = PostProcessStageLibrary.createBloomStage(); + const fxaa = PostProcessStageLibrary.createFXAAStage(); + const passThrough = PostProcessStageLibrary.createPassThroughStage(); + const ao = PostProcessStageLibrary.createAmbientOcclusionStage(); + const bloom = PostProcessStageLibrary.createBloomStage(); // Auto-exposure is currently disabled because most shaders output a value in [0.0, 1.0]. // Some shaders, such as the atmosphere and ground atmosphere, output values slightly over 1.0. @@ -59,8 +59,8 @@ function PostProcessStageCollection() { const textureCache = new PostProcessStageTextureCache(this); - var stageNames = {}; - var stack = stackScratch; + const stageNames = {}; + const stack = stackScratch; stack.push(fxaa, passThrough, ao, bloom, tonemapping); while (stack.length > 0) { const stage = stack.pop(); @@ -123,11 +123,11 @@ Object.defineProperties(PostProcessStageCollection.prototype, { readyAndEnabled = readyAndEnabled || (stage.ready && stage.enabled); } - var fxaa = this._fxaa; - var passThrough = this._passThrough; - var ao = this._ao; - var bloom = this._bloom; - var tonemapping = this._tonemapping; + const fxaa = this._fxaa; + const passThrough = this._passThrough; + const ao = this._ao; + const bloom = this._bloom; + const tonemapping = this._tonemapping; readyAndEnabled = readyAndEnabled || (fxaa.ready && fxaa.enabled); readyAndEnabled = @@ -275,15 +275,15 @@ Object.defineProperties(PostProcessStageCollection.prototype, { return this.getOutputTexture(fxaa.name); } - var passThrough = this._passThrough; + const passThrough = this._passThrough; if (passThrough.enabled && passThrough.ready) { return this.getOutputTexture(passThrough.name); } - var stages = this._stages; - var length = stages.length; - for (var i = length - 1; i >= 0; --i) { - var stage = stages[i]; + const stages = this._stages; + const length = stages.length; + for (let i = length - 1; i >= 0; --i) { + const stage = stages[i]; if (defined(stage) && stage.ready && stage.enabled) { return this.getOutputTexture(stage.name); } @@ -611,12 +611,12 @@ PostProcessStageCollection.prototype.update = function ( } } - var ao = this._ao; - var bloom = this._bloom; - var autoexposure = this._autoExposure; - var tonemapping = this._tonemapping; - var fxaa = this._fxaa; - var passThrough = this._passThrough; + const ao = this._ao; + const bloom = this._bloom; + const autoexposure = this._autoExposure; + const tonemapping = this._tonemapping; + const fxaa = this._fxaa; + const passThrough = this._passThrough; tonemapping.enabled = useHdr; @@ -624,8 +624,8 @@ PostProcessStageCollection.prototype.update = function ( const bloomEnabled = bloom.enabled && bloom._isSupported(context); const tonemappingEnabled = tonemapping.enabled && tonemapping._isSupported(context); - var fxaaEnabled = fxaa.enabled && fxaa._isSupported(context); - var passThroughEnabled = + const fxaaEnabled = fxaa.enabled && fxaa._isSupported(context); + const passThroughEnabled = passThrough.enabled && passThrough._isSupported(context); if ( @@ -791,22 +791,22 @@ PostProcessStageCollection.prototype.execute = function ( depthTexture, idTexture ) { - var activeStages = this._activeStages; - var length = activeStages.length; - var fxaa = this._fxaa; - var passThrough = this._passThrough; - var ao = this._ao; - var bloom = this._bloom; - var autoexposure = this._autoExposure; - var tonemapping = this._tonemapping; - - var aoEnabled = ao.enabled && ao._isSupported(context); - var bloomEnabled = bloom.enabled && bloom._isSupported(context); - var autoExposureEnabled = this._autoExposureEnabled; - var tonemappingEnabled = + const activeStages = this._activeStages; + const length = activeStages.length; + const fxaa = this._fxaa; + const passThrough = this._passThrough; + const ao = this._ao; + const bloom = this._bloom; + const autoexposure = this._autoExposure; + const tonemapping = this._tonemapping; + + const aoEnabled = ao.enabled && ao._isSupported(context); + const bloomEnabled = bloom.enabled && bloom._isSupported(context); + const autoExposureEnabled = this._autoExposureEnabled; + const tonemappingEnabled = tonemapping.enabled && tonemapping._isSupported(context); - var fxaaEnabled = fxaa.enabled && fxaa._isSupported(context); - var passThroughEnabled = + const fxaaEnabled = fxaa.enabled && fxaa._isSupported(context); + const passThroughEnabled = passThrough.enabled && passThrough._isSupported(context); if ( diff --git a/Source/Scene/PostProcessStageTextureCache.js b/Source/Scene/PostProcessStageTextureCache.js index 8388b50ffeff..30b3edca10f9 100644 --- a/Source/Scene/PostProcessStageTextureCache.js +++ b/Source/Scene/PostProcessStageTextureCache.js @@ -144,11 +144,11 @@ function getDependencies(collection, context) { const dependencies = {}; if (defined(collection.ambientOcclusion)) { - var ao = collection.ambientOcclusion; - var bloom = collection.bloom; - var tonemapping = collection._tonemapping; - var fxaa = collection.fxaa; - var passThrough = collection.passThrough; + const ao = collection.ambientOcclusion; + const bloom = collection.bloom; + const tonemapping = collection._tonemapping; + const fxaa = collection.fxaa; + const passThrough = collection.passThrough; let previousName = getCompositeDependencies( collection, @@ -275,10 +275,10 @@ function createFramebuffers(cache, context) { } function releaseResources(cache) { - var framebuffers = cache._framebuffers; - var length = framebuffers.length; - for (var i = 0; i < length; ++i) { - var framebuffer = framebuffers[i]; + const framebuffers = cache._framebuffers; + const length = framebuffers.length; + for (let i = 0; i < length; ++i) { + const framebuffer = framebuffers[i]; framebuffer.buffer = framebuffer.buffer && framebuffer.buffer.destroy(); framebuffer.buffer = undefined; } @@ -353,11 +353,11 @@ PostProcessStageTextureCache.prototype.update = function (context) { defined(collection.fxaa) && collection.fxaa.enabled && collection.fxaa._isSupported(context); - var passThroughEnabled = + const passThroughEnabled = defined(collection.passThrough) && collection.passThrough.enabled && collection.passThrough._isSupported(context); - var needsCheckDimensionsUpdate = + const needsCheckDimensionsUpdate = !defined(collection._activeStages) || collection._activeStages.length > 0 || aoEnabled || diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js index 8b83c035eb44..259c9a0ae8a3 100644 --- a/Source/Scene/Scene.js +++ b/Source/Scene/Scene.js @@ -2645,7 +2645,7 @@ function executeCommands(scene, passState) { const depthStencilTexture = renderTranslucentDepthForPick ? passState.framebuffer.depthStencilTexture : globeDepth.depthStencilTexture; - var pickDepth = scene._picking.getPickDepth(scene, index); + const pickDepth = scene._picking.getPickDepth(scene, index); pickDepth.update(context, depthStencilTexture); pickDepth.executeCopyDepth(context, passState); if (renderTranslucentDepthForPick) { @@ -3505,10 +3505,10 @@ function updateAndClearFramebuffers(scene, passState, clearColor) { * @private */ Scene.prototype.resolveFramebuffers = function (passState) { - var context = this._context; - var environmentState = this._environmentState; - var view = this._view; - var globeDepth = view.globeDepth; + const context = this._context; + const environmentState = this._environmentState; + const view = this._view; + const globeDepth = view.globeDepth; if (defined(globeDepth)) { globeDepth.prepareTextures(context); } @@ -3517,12 +3517,12 @@ Scene.prototype.resolveFramebuffers = function (passState) { const useGlobeDepthFramebuffer = environmentState.useGlobeDepthFramebuffer; const usePostProcess = environmentState.usePostProcess; - var defaultFramebuffer = environmentState.originalFramebuffer; - var globeFramebuffer = useGlobeDepthFramebuffer + const defaultFramebuffer = environmentState.originalFramebuffer; + const globeFramebuffer = useGlobeDepthFramebuffer ? globeDepth._colorFramebuffer : undefined; - var sceneFramebuffer = view.sceneFramebuffer._colorFramebuffer; - var idFramebuffer = view.sceneFramebuffer.idFramebuffer; + const sceneFramebuffer = view.sceneFramebuffer._colorFramebuffer; + const idFramebuffer = view.sceneFramebuffer.idFramebuffer; if (useOIT) { passState.framebuffer = usePostProcess @@ -3541,15 +3541,15 @@ Scene.prototype.resolveFramebuffers = function (passState) { if (usePostProcess) { view.sceneFramebuffer.prepareColorFramebuffer(context); - var inputFramebuffer = sceneFramebuffer; + let inputFramebuffer = sceneFramebuffer; if (useGlobeDepthFramebuffer && !useOIT) { inputFramebuffer = globeFramebuffer; } - var postProcess = this.postProcessStages; - var colorTexture = inputFramebuffer.getColorTexture(0); - var idTexture = idFramebuffer.getColorTexture(0); - var depthTexture = defaultValue( + const postProcess = this.postProcessStages; + const colorTexture = inputFramebuffer.getColorTexture(0); + const idTexture = idFramebuffer.getColorTexture(0); + const depthTexture = defaultValue( globeFramebuffer, sceneFramebuffer ).getDepthStencilTexture(); diff --git a/Source/Scene/SceneFramebuffer.js b/Source/Scene/SceneFramebuffer.js index bbfc9f1aa2dd..145230bfb7b5 100644 --- a/Source/Scene/SceneFramebuffer.js +++ b/Source/Scene/SceneFramebuffer.js @@ -56,9 +56,9 @@ SceneFramebuffer.prototype.update = function ( hdr, numSamples ) { - var width = viewport.width; - var height = viewport.height; - var pixelDatatype = hdr + const width = viewport.width; + const height = viewport.height; + const pixelDatatype = hdr ? context.halfFloatingPointTexture ? PixelDatatype.HALF_FLOAT : PixelDatatype.FLOAT From 9209c99d6e8f9522c0adcd6ec67944147f98ae33 Mon Sep 17 00:00:00 2001 From: Eli Bogomolny Date: Thu, 27 Jan 2022 13:59:23 -0500 Subject: [PATCH 09/32] MultisampleFramebuffer specs, doc cleanup --- Source/Renderer/Framebuffer.js | 2 +- Source/Renderer/FramebufferManager.js | 16 +- Source/Renderer/MultisampleFramebuffer.js | 64 ++-- Source/Scene/GlobeDepth.js | 6 +- Source/Scene/Scene.js | 10 +- Source/Scene/SceneFramebuffer.js | 4 +- Specs/Renderer/FramebufferManagerSpec.js | 13 +- Specs/Renderer/MultisampleFramebufferSpec.js | 377 +++++++++++++++++++ 8 files changed, 434 insertions(+), 58 deletions(-) create mode 100644 Specs/Renderer/MultisampleFramebufferSpec.js diff --git a/Source/Renderer/Framebuffer.js b/Source/Renderer/Framebuffer.js index 847e5f91bca0..24ecf8b5e143 100644 --- a/Source/Renderer/Framebuffer.js +++ b/Source/Renderer/Framebuffer.js @@ -35,7 +35,7 @@ function attachRenderbuffer(framebuffer, attachment, renderbuffer) { * Framebuffers are used for render-to-texture effects; they allow us to render to * textures in one pass, and read from it in a later pass. * - * @param {Object} options The initial framebuffer attachments as shown in the example below. context is required. The possible properties are colorTextures, colorRenderbuffers, depthTexture, depthRenderbuffer, stencilRenderbuffer, depthStencilTexture, and depthStencilRenderbuffer. + * @param {Object} options The initial framebuffer attachments as shown in the example below. context is required. The possible properties are colorTextures, colorRenderbuffers, depthTexture, depthRenderbuffer, stencilRenderbuffer, depthStencilTexture, depthStencilRenderbuffer, and destroyAttachments. * * @exception {DeveloperError} Cannot have both color texture and color renderbuffer attachments. * @exception {DeveloperError} Cannot have both a depth texture and depth renderbuffer attachment. diff --git a/Source/Renderer/FramebufferManager.js b/Source/Renderer/FramebufferManager.js index 91106225214a..471eee1d7063 100644 --- a/Source/Renderer/FramebufferManager.js +++ b/Source/Renderer/FramebufferManager.js @@ -93,7 +93,7 @@ Object.defineProperties(FramebufferManager.prototype, { framebuffer: { get: function () { if (this._numSamples > 1) { - return this._multisampleFramebuffer.getFramebuffer(); + return this._multisampleFramebuffer.getRenderFramebuffer(); } return this._framebuffer; }, @@ -231,16 +231,6 @@ FramebufferManager.prototype.update = function ( pixelDatatype: PixelDatatype.UNSIGNED_INT, sampler: Sampler.NEAREST, }); - if (this._numSamples > 1) { - this._depthRenderbuffer = new Renderbuffer({ - context: context, - width: width, - height: height, - format: RenderbufferFormat.DEPTH_COMPONENT16, - numSamples: this._numSamples, - }); - } - // TODO@eli create depth RB if numSamples > 1 } else { this._depthRenderbuffer = new Renderbuffer({ context: context, @@ -254,6 +244,8 @@ FramebufferManager.prototype.update = function ( if (this._numSamples > 1) { this._multisampleFramebuffer = new MultisampleFramebuffer({ context: context, + width: this._width, + height: this._height, colorTextures: this._colorTextures, colorRenderbuffers: this._colorRenderbuffers, depthStencilTexture: this._depthStencilTexture, @@ -403,7 +395,7 @@ FramebufferManager.prototype.setDepthStencilTexture = function (texture) { this._depthStencilTexture = texture; }; -FramebufferManager.prototype.prepareColorFramebuffer = function (context) { +FramebufferManager.prototype.prepareTextures = function (context) { if (this._numSamples > 1) { this._multisampleFramebuffer.blitFramebuffers(context); } diff --git a/Source/Renderer/MultisampleFramebuffer.js b/Source/Renderer/MultisampleFramebuffer.js index 623f17a93668..f954cd81db75 100644 --- a/Source/Renderer/MultisampleFramebuffer.js +++ b/Source/Renderer/MultisampleFramebuffer.js @@ -2,12 +2,20 @@ import Check from "../Core/Check.js"; import defaultValue from "../Core/defaultValue.js"; import defined from "../Core/defined.js"; import destroyObject from "../Core/destroyObject.js"; +import DeveloperError from "../Core/DeveloperError.js"; import Framebuffer from "./Framebuffer.js"; /** - * Creates a multisampling wrapper around two framebuffers with optional initial color, depth, and stencil attachments. + * Creates a multisampling wrapper around two framebuffers with optional initial + * color and depth-stencil attachments. The first framebuffer has multisampled + * renderbuffer attachments and is bound to READ_FRAMEBUFFER during the blit. The + * second is bound to DRAW_FRAMEBUFFER during the blit, and has texture attachments + * to store the copied pixels. * - * @param {Object} options The initial framebuffer attachments as shown in the example below. context is required. The possible properties are colorTextures, colorRenderbuffers, depthTexture, depthRenderbuffer, stencilRenderbuffer, depthStencilTexture, and depthStencilRenderbuffer. + * @param {Object} options The initial framebuffer attachments. context, width, and height are required. The possible properties are colorTextures, colorRenderbuffers, depthStencilTexture, depthStencilRenderbuffer, and destroyAttachments. + * + * @exception {DeveloperError} Both color renderbuffer and texture attachments must be provided. + * @exception {DeveloperError} Both depth-stencil renderbuffer and texture attachments must be provided. * * @private * @constructor @@ -16,13 +24,24 @@ function MultisampleFramebuffer(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); const context = options.context; - const colorRenderbuffers = options.colorRenderbuffers; - const colorTextures = options.colorTextures; + const width = options.width; + const height = options.height; //>>includeStart('debug', pragmas.debug); Check.defined("options.context", context); - Check.defined("options.colorRenderbuffers", colorRenderbuffers); - Check.defined("options.colorTextures", colorTextures); + Check.defined("options.width", width); + Check.defined("options.height", height); //>>includeEnd('debug'); + this._width = width; + this._height = height; + + const colorRenderbuffers = options.colorRenderbuffers; + const colorTextures = options.colorTextures; + if ( + (defined(colorRenderbuffers) && !defined(colorTextures)) || + (defined(colorTextures) && !defined(colorRenderbuffers)) + ) { + throw new DeveloperError("Both color renderbuffer and texture attachments must be provided.") + } const depthStencilRenderbuffer = options.depthStencilRenderbuffer; const depthStencilTexture = options.depthStencilTexture; @@ -30,7 +49,7 @@ function MultisampleFramebuffer(options) { (defined(depthStencilRenderbuffer) && !defined(depthStencilTexture)) || (defined(depthStencilTexture) && !defined(depthStencilRenderbuffer)) ) { - // throw new DeveloperError("If multisampling depth stencil attachments, both a Renderbuffer and Texture must be provided.") + throw new DeveloperError("Both depth-stencil renderbuffer and texture attachments must be provided.") } this._renderFramebuffer = new Framebuffer({ @@ -45,17 +64,8 @@ function MultisampleFramebuffer(options) { depthStencilTexture: depthStencilTexture, destroyAttachments: options.destroyAttachments, }); - - this._defaultToRender = true; - this._blitReady = false; } -MultisampleFramebuffer.prototype.getFramebuffer = function () { - // if (this._defaultToRender) return this._renderFramebuffer; - // if (this._blitReady) return this._colorFramebuffer; - return this._renderFramebuffer; -}; - MultisampleFramebuffer.prototype.getRenderFramebuffer = function () { return this._renderFramebuffer; }; @@ -65,43 +75,30 @@ MultisampleFramebuffer.prototype.getColorFramebuffer = function () { }; MultisampleFramebuffer.prototype.blitFramebuffers = function (context) { - // if (!this._blitReady) { - // clearCommand.execute(context); this._renderFramebuffer.bindRead(); this._colorFramebuffer.bindDraw(); - const width = context.canvas.clientWidth; - const height = context.canvas.clientHeight; const gl = context._gl; let mask = 0; if (this._colorFramebuffer._colorTextures.length > 0) { mask |= gl.COLOR_BUFFER_BIT; } - if (defined(this._colorFramebuffer.depthTexture)) { - mask |= gl.DEPTH_BUFFER_BIT; - } if (defined(this._colorFramebuffer.depthStencilTexture)) { mask |= gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT; } gl.blitFramebuffer( 0, 0, - width, - height, + this._width, + this._height, 0, 0, - width, - height, + this._width, + this._height, mask, gl.NEAREST ); gl.bindFramebuffer(gl.READ_FRAMEBUFFER, null); gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, null); - // this._blitReady = true; - // } -}; - -MultisampleFramebuffer.prototype.setRenderAsDefault = function (value) { - this._defaultToRender = value; }; MultisampleFramebuffer.prototype.isDestroyed = function () { @@ -109,7 +106,6 @@ MultisampleFramebuffer.prototype.isDestroyed = function () { }; MultisampleFramebuffer.prototype.destroy = function () { - this._blitReady = false; this._renderFramebuffer.destroy(); this._colorFramebuffer.destroy(); return destroyObject(this); diff --git a/Source/Scene/GlobeDepth.js b/Source/Scene/GlobeDepth.js index 065fde4a8edc..4a8513868190 100644 --- a/Source/Scene/GlobeDepth.js +++ b/Source/Scene/GlobeDepth.js @@ -247,15 +247,15 @@ GlobeDepth.prototype.update = function ( this._clearGlobeDepth = clearGlobeDepth; }; -GlobeDepth.prototype.prepareTextures = function (context) { +GlobeDepth.prototype.prepareColorTextures = function (context) { if (this._numSamples > 1) { - this._colorFramebuffer._multisampleFramebuffer.blitFramebuffers(context); + this._colorFramebuffer.prepareTextures(context); } }; GlobeDepth.prototype.executeCopyDepth = function (context, passState) { if (defined(this._copyDepthCommand)) { - this.prepareTextures(context); + this.prepareColorTextures(context); this._copyDepthCommand.execute(context, passState); context.uniformState.globeDepthTexture = this._copyDepthFramebuffer.getColorTexture(); } diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js index 259c9a0ae8a3..5abb1b7b859d 100644 --- a/Source/Scene/Scene.js +++ b/Source/Scene/Scene.js @@ -2327,7 +2327,7 @@ function executeCommands(scene, passState) { commands, invertClassification ) { - view.globeDepth.prepareTextures(context); + view.globeDepth.prepareColorTextures(context); view.oit.executeCommands( scene, executeFunction, @@ -2458,7 +2458,7 @@ function executeCommands(scene, passState) { if (length > 0) { if (defined(globeDepth) && environmentState.useGlobeDepthFramebuffer) { - globeDepth.prepareTextures(context); + globeDepth.prepareColorTextures(context); globeDepth.executeUpdateDepth( context, passState, @@ -2637,7 +2637,7 @@ function executeCommands(scene, passState) { renderTranslucentDepthForPick) ) { if (renderTranslucentDepthForPick) { - globeDepth.prepareTextures(context); + globeDepth.prepareColorTextures(context); if (scene.numberSamples > 1) passState.framebuffer = globeDepth._colorFramebuffer._multisampleFramebuffer.getColorFramebuffer(); } @@ -3510,7 +3510,7 @@ Scene.prototype.resolveFramebuffers = function (passState) { const view = this._view; const globeDepth = view.globeDepth; if (defined(globeDepth)) { - globeDepth.prepareTextures(context); + globeDepth.prepareColorTextures(context); } const useOIT = environmentState.useOIT; @@ -3540,7 +3540,7 @@ Scene.prototype.resolveFramebuffers = function (passState) { } if (usePostProcess) { - view.sceneFramebuffer.prepareColorFramebuffer(context); + view.sceneFramebuffer.prepareColorTextures(context); let inputFramebuffer = sceneFramebuffer; if (useGlobeDepthFramebuffer && !useOIT) { inputFramebuffer = globeFramebuffer; diff --git a/Source/Scene/SceneFramebuffer.js b/Source/Scene/SceneFramebuffer.js index 145230bfb7b5..274c623a5e8a 100644 --- a/Source/Scene/SceneFramebuffer.js +++ b/Source/Scene/SceneFramebuffer.js @@ -89,9 +89,9 @@ SceneFramebuffer.prototype.getIdFramebuffer = function () { return this._idFramebuffer.framebuffer; }; -SceneFramebuffer.prototype.prepareColorFramebuffer = function (context) { +SceneFramebuffer.prototype.prepareColorTextures = function (context) { if (this._numSamples > 1) { - this._colorFramebuffer.prepareColorFramebuffer(context); + this._colorFramebuffer.prepareTextures(context); } }; diff --git a/Specs/Renderer/FramebufferManagerSpec.js b/Specs/Renderer/FramebufferManagerSpec.js index 63e2653676ad..0059b4b91e5f 100644 --- a/Specs/Renderer/FramebufferManagerSpec.js +++ b/Specs/Renderer/FramebufferManagerSpec.js @@ -17,7 +17,7 @@ describe( let fbm; beforeAll(function () { - context = createContext(); + context = createContext({ requestWebgl2: true }); }); afterAll(function () { @@ -383,6 +383,17 @@ describe( expect(FramebufferManager.prototype.destroy.calls.count()).toEqual(2); }); + it("destroys resources after numSamples changes", function () { + if (context.webgl2) { + return; + } + fbm = new FramebufferManager(); + spyOn(FramebufferManager.prototype, "destroy").and.callThrough(); + fbm.update(context, 1, 1); + fbm.update(context, 1, 1, 2); + expect(FramebufferManager.prototype.destroy.calls.count()).toEqual(2); + }); + it("destroys resources after pixel datatype changes", function () { fbm = new FramebufferManager({ pixelDatatype: PixelDatatype.UNSIGNED_INT, diff --git a/Specs/Renderer/MultisampleFramebufferSpec.js b/Specs/Renderer/MultisampleFramebufferSpec.js new file mode 100644 index 000000000000..58a2be11f8e4 --- /dev/null +++ b/Specs/Renderer/MultisampleFramebufferSpec.js @@ -0,0 +1,377 @@ +import { ClearCommand } from "../../Source/Cesium.js"; +import { Color } from "../../Source/Cesium.js"; +import { PrimitiveType } from "../../Source/Cesium.js"; +import { Buffer } from "../../Source/Cesium.js"; +import { BufferUsage } from "../../Source/Cesium.js"; +import { DrawCommand } from "../../Source/Cesium.js"; +import { MultisampleFramebuffer } from "../../Source/Cesium.js"; +import { PixelDatatype } from "../../Source/Cesium.js"; +import { PixelFormat } from "../../Source/Cesium.js"; +import { Texture } from "../../Source/Cesium.js"; +import { Renderbuffer } from "../../Source/Cesium.js"; +import { RenderbufferFormat } from "../../Source/Cesium.js"; +import { RenderState } from "../../Source/Cesium.js"; +import { ShaderProgram } from "../../Source/Cesium.js"; +import { VertexArray } from "../../Source/Cesium.js"; +import createContext from "../createContext.js"; + +describe( + "Renderer/MultisampleFramebuffer", + function () { + let context; + let sp; + let va; + let framebuffer; + + beforeAll(function () { + context = createContext({ requestWebgl2: true }); + }); + + afterAll(function () { + context.destroyForSpecs(); + }); + + afterEach(function () { + sp = sp && sp.destroy(); + va = va && va.destroy(); + framebuffer = framebuffer && framebuffer.destroy(); + }); + + it("throws when missing a color attachment", function () { + expect(function () { + framebuffer = new MultisampleFramebuffer({ + context: context, + width: 1, + height: 1, + colorTextures: [], + }); + }).toThrowDeveloperError(); + expect(function () { + framebuffer = new MultisampleFramebuffer({ + context: context, + width: 1, + height: 1, + colorRenderbuffers: [], + }); + }).toThrowDeveloperError(); + }); + + it("throws when missing a depth-stencil attachment", function () { + expect(function () { + framebuffer = new MultisampleFramebuffer({ + context: context, + width: 1, + height: 1, + depthStencilTexture: [], + }); + }).toThrowDeveloperError(); + expect(function () { + framebuffer = new MultisampleFramebuffer({ + context: context, + width: 1, + height: 1, + depthStencilRenderbuffer: [], + }); + }).toThrowDeveloperError(); + }); + + it("creates read and draw framebuffers", function () { + framebuffer = new MultisampleFramebuffer({ + context: context, + width: 1, + height: 1, + colorTextures: [ + new Texture({ + context: context, + width: 1, + height: 1, + }), + ], + colorRenderbuffers: [ + new Renderbuffer({ + context: context, + format: RenderbufferFormat.RGBA8, + }), + ], + depthStencilTexture: new Texture({ + context: context, + width: 1, + height: 1, + pixelFormat: PixelFormat.DEPTH_STENCIL, + pixelDatatype: PixelDatatype.UNSIGNED_INT_24_8, + }), + depthStencilRenderbuffer: new Renderbuffer({ + context: context, + format: RenderbufferFormat.DEPTH_STENCIL, + }), + }); + const readFB = framebuffer.getRenderFramebuffer(); + const drawFB = framebuffer.getColorFramebuffer(); + expect(readFB).toBeDefined(); + expect(readFB.getColorRenderbuffer(0)).toBeDefined(); + expect(readFB.depthStencilRenderbuffer).toBeDefined(); + expect(drawFB).toBeDefined(); + expect(drawFB.getColorTexture(0)).toBeDefined(); + expect(drawFB.depthStencilTexture).toBeDefined(); + }); + + function renderColor(framebuffer, color) { + const vs = + "attribute vec4 position; void main() { gl_PointSize = 1.0; gl_Position = position; }"; + const fs = "uniform vec4 color; void main() { gl_FragColor = color; }"; + sp = ShaderProgram.fromCache({ + context: context, + vertexShaderSource: vs, + fragmentShaderSource: fs, + attributeLocations: { + position: 0, + }, + }); + + va = new VertexArray({ + context: context, + attributes: [ + { + index: 0, + vertexBuffer: Buffer.createVertexBuffer({ + context: context, + typedArray: new Float32Array([0, 0, 0, 1]), + usage: BufferUsage.STATIC_DRAW, + }), + componentsPerAttribute: 4, + }, + ], + }); + + const uniformMap = { + color: function () { + return color; + }, + }; + + const command = new DrawCommand({ + primitiveType: PrimitiveType.POINTS, + shaderProgram: sp, + vertexArray: va, + uniformMap: uniformMap, + framebuffer: framebuffer, + }); + command.execute(context); + } + + it("blits color attachments", function () { + if (!context.webgl2) { + return; + } + + framebuffer = new MultisampleFramebuffer({ + context: context, + width: 1, + height: 1, + colorTextures: [ + new Texture({ + context: context, + width: 1, + height: 1, + }), + ], + colorRenderbuffers: [ + new Renderbuffer({ + context: context, + format: RenderbufferFormat.RGBA8, + numSamples: 2, + }), + ], + }); + + const renderFB = framebuffer.getRenderFramebuffer(); + renderColor(renderFB, new Color(0.0, 1.0, 0.0, 1.0)); + framebuffer.blitFramebuffers(context); + const colorFB = framebuffer.getColorFramebuffer(); + expect({ + context: context, + framebuffer: colorFB, + }).toReadPixels([0, 255, 0, 255]); + }); + + function renderAndBlitDepthAttachment(framebuffer) { + const renderFB = framebuffer.getRenderFramebuffer(); + const colorFB = framebuffer.getColorFramebuffer(); + ClearCommand.ALL.execute(context); + + const framebufferClear = new ClearCommand({ + depth: 1.0, + framebuffer: renderFB, + }); + + framebufferClear.execute(context); + + // 1 of 3. Render green point into color attachment. + const vs = + "attribute vec4 position; void main() { gl_PointSize = 1.0; gl_Position = position; }"; + const fs = "void main() { gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0); }"; + sp = ShaderProgram.fromCache({ + context: context, + vertexShaderSource: vs, + fragmentShaderSource: fs, + attributeLocations: { + position: 0, + }, + }); + + va = new VertexArray({ + context: context, + attributes: [ + { + index: 0, + vertexBuffer: Buffer.createVertexBuffer({ + context: context, + typedArray: new Float32Array([0, 0, 0, 1]), + usage: BufferUsage.STATIC_DRAW, + }), + componentsPerAttribute: 4, + }, + ], + }); + + let command = new DrawCommand({ + primitiveType: PrimitiveType.POINTS, + shaderProgram: sp, + vertexArray: va, + framebuffer: renderFB, + renderState: RenderState.fromCache({ + depthTest: { + enabled: true, + }, + }), + }); + command.execute(context); + + // 2 of 3. Verify default color buffer is still black. + expect(context).toReadPixels([0, 0, 0, 255]); + + framebuffer.blitFramebuffers(context); + + // 3 of 3. Render green to default color buffer by reading from blitted color attachment + const vs2 = + "attribute vec4 position; void main() { gl_PointSize = 1.0; gl_Position = position; }"; + const fs2 = + "uniform sampler2D u_texture; void main() { gl_FragColor = texture2D(u_texture, vec2(0.0)).rrrr; }"; + let sp2 = ShaderProgram.fromCache({ + context: context, + vertexShaderSource: vs2, + fragmentShaderSource: fs2, + attributeLocations: { + position: 0, + }, + }); + const uniformMap = { + u_texture: function () { + return colorFB.depthStencilTexture; + }, + }; + + command = new DrawCommand({ + primitiveType: PrimitiveType.POINTS, + shaderProgram: sp2, + vertexArray: va, + uniformMap: uniformMap, + }); + command.execute(context); + + sp2 = sp2.destroy(); + + return context.readPixels(); + } + + it("blits depth-stencil attachments", function () { + if (!context.webgl2 || !context.depthTexture) { + return; + } + + framebuffer = new MultisampleFramebuffer({ + context: context, + width: 1, + height: 1, + colorTextures: [ + new Texture({ + context: context, + width: 1, + height: 1, + }), + ], + colorRenderbuffers: [ + new Renderbuffer({ + context: context, + format: RenderbufferFormat.RGBA8, + numSamples: 2, + }), + ], + depthStencilTexture: new Texture({ + context: context, + width: 1, + height: 1, + pixelFormat: PixelFormat.DEPTH_STENCIL, + pixelDatatype: PixelDatatype.UNSIGNED_INT_24_8, + }), + depthStencilRenderbuffer: new Renderbuffer({ + context: context, + width: 1, + height: 1, + format: RenderbufferFormat.DEPTH24_STENCIL8, + numSamples: 2, + }), + }); + + expect( + renderAndBlitDepthAttachment(framebuffer) + ).toEqualEpsilon([128, 128, 128, 255], 1); + }); + + it("destroys", function () { + const f = new MultisampleFramebuffer({ + context: context, + width: 1, + height: 1, + }); + expect(f.isDestroyed()).toEqual(false); + f.destroy(); + expect(f.isDestroyed()).toEqual(true); + }); + + it("fails to destroy", function () { + const f = new MultisampleFramebuffer({ + context: context, + width: 1, + height: 1, + }); + f.destroy(); + + expect(function () { + f.destroy(); + }).toThrowDeveloperError(); + }); + + it("throws when there is no context", function () { + expect(function () { + return new MultisampleFramebuffer(); + }).toThrowDeveloperError(); + }); + + it("throws when there is no width or height", function () { + expect(function () { + return new MultisampleFramebuffer({ + context: context, + height: 1, + }); + }).toThrowDeveloperError(); + expect(function () { + return new MultisampleFramebuffer({ + context: context, + width: 1, + }); + }).toThrowDeveloperError(); + }); + }, + "WebGL" +); \ No newline at end of file From 614c88c2598b14fbbcbe39286efb4a9605b40d9f Mon Sep 17 00:00:00 2001 From: Eli Bogomolny Date: Thu, 27 Jan 2022 16:03:54 -0500 Subject: [PATCH 10/32] Clean up renderTranslucentDepthForPick logic in Scene.js --- Source/Scene/Scene.js | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js index 5abb1b7b859d..d11967c4a5e2 100644 --- a/Source/Scene/Scene.js +++ b/Source/Scene/Scene.js @@ -2636,22 +2636,12 @@ function executeCommands(scene, passState) { (environmentState.useGlobeDepthFramebuffer || renderTranslucentDepthForPick) ) { - if (renderTranslucentDepthForPick) { - globeDepth.prepareColorTextures(context); - if (scene.numberSamples > 1) - passState.framebuffer = globeDepth._colorFramebuffer._multisampleFramebuffer.getColorFramebuffer(); - } // PERFORMANCE_IDEA: Use MRT to avoid the extra copy. - const depthStencilTexture = renderTranslucentDepthForPick - ? passState.framebuffer.depthStencilTexture - : globeDepth.depthStencilTexture; + globeDepth.prepareColorTextures(context); + const depthStencilTexture = globeDepth.depthStencilTexture; const pickDepth = scene._picking.getPickDepth(scene, index); pickDepth.update(context, depthStencilTexture); pickDepth.executeCopyDepth(context, passState); - if (renderTranslucentDepthForPick) { - if (scene.numberSamples > 1) - passState.framebuffer = globeDepth._colorFramebuffer._multisampleFramebuffer.getRenderFramebuffer(); - } } if (picking || !usePostProcessSelected) { From d22ee26a180ea41d3edebf542cd6b24a22d5eb5c Mon Sep 17 00:00:00 2001 From: Eli Bogomolny Date: Fri, 28 Jan 2022 11:14:39 -0500 Subject: [PATCH 11/32] Prettier, fix Webgl2 specs --- Source/Renderer/MultisampleFramebuffer.js | 8 ++++++-- Specs/Renderer/FramebufferManagerSpec.js | 3 ++- Specs/Renderer/MultisampleFramebufferSpec.js | 13 +++++++++---- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/Source/Renderer/MultisampleFramebuffer.js b/Source/Renderer/MultisampleFramebuffer.js index f954cd81db75..40f29703f92e 100644 --- a/Source/Renderer/MultisampleFramebuffer.js +++ b/Source/Renderer/MultisampleFramebuffer.js @@ -40,7 +40,9 @@ function MultisampleFramebuffer(options) { (defined(colorRenderbuffers) && !defined(colorTextures)) || (defined(colorTextures) && !defined(colorRenderbuffers)) ) { - throw new DeveloperError("Both color renderbuffer and texture attachments must be provided.") + throw new DeveloperError( + "Both color renderbuffer and texture attachments must be provided." + ); } const depthStencilRenderbuffer = options.depthStencilRenderbuffer; @@ -49,7 +51,9 @@ function MultisampleFramebuffer(options) { (defined(depthStencilRenderbuffer) && !defined(depthStencilTexture)) || (defined(depthStencilTexture) && !defined(depthStencilRenderbuffer)) ) { - throw new DeveloperError("Both depth-stencil renderbuffer and texture attachments must be provided.") + throw new DeveloperError( + "Both depth-stencil renderbuffer and texture attachments must be provided." + ); } this._renderFramebuffer = new Framebuffer({ diff --git a/Specs/Renderer/FramebufferManagerSpec.js b/Specs/Renderer/FramebufferManagerSpec.js index 0059b4b91e5f..49bd6e0d6a29 100644 --- a/Specs/Renderer/FramebufferManagerSpec.js +++ b/Specs/Renderer/FramebufferManagerSpec.js @@ -384,9 +384,10 @@ describe( }); it("destroys resources after numSamples changes", function () { - if (context.webgl2) { + if (!context.webgl2) { return; } + fbm = new FramebufferManager(); spyOn(FramebufferManager.prototype, "destroy").and.callThrough(); fbm.update(context, 1, 1); diff --git a/Specs/Renderer/MultisampleFramebufferSpec.js b/Specs/Renderer/MultisampleFramebufferSpec.js index 58a2be11f8e4..7f97bb26f66e 100644 --- a/Specs/Renderer/MultisampleFramebufferSpec.js +++ b/Specs/Renderer/MultisampleFramebufferSpec.js @@ -76,6 +76,10 @@ describe( }); it("creates read and draw framebuffers", function () { + if (!context.depthTexture) { + return; + } + framebuffer = new MultisampleFramebuffer({ context: context, width: 1, @@ -323,9 +327,10 @@ describe( }), }); - expect( - renderAndBlitDepthAttachment(framebuffer) - ).toEqualEpsilon([128, 128, 128, 255], 1); + expect(renderAndBlitDepthAttachment(framebuffer)).toEqualEpsilon( + [128, 128, 128, 255], + 1 + ); }); it("destroys", function () { @@ -374,4 +379,4 @@ describe( }); }, "WebGL" -); \ No newline at end of file +); From cbd8c451b694e3591fbe8b1c8b15d6cf776d2ee9 Mon Sep 17 00:00:00 2001 From: Eli Bogomolny Date: Fri, 28 Jan 2022 13:52:46 -0500 Subject: [PATCH 12/32] Disable multisampling during pick frames --- Source/Scene/Scene.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js index d11967c4a5e2..c3bd397c87aa 100644 --- a/Source/Scene/Scene.js +++ b/Source/Scene/Scene.js @@ -3353,6 +3353,11 @@ function updateAndClearFramebuffers(scene, passState, clearColor) { const picking = passes.pick; const useWebVR = environmentState.useWebVR; + const originalSamples = scene.numberSamples; + if (picking) { + scene.numberSamples = 1; + } + // Preserve the reference to the original framebuffer. environmentState.originalFramebuffer = passState.framebuffer; @@ -3489,6 +3494,8 @@ function updateAndClearFramebuffers(scene, passState, clearColor) { passState ); } + + scene.numberSamples = originalSamples; } /** From a3ad889bc616fead3fe7441bd3ed2a8a9adbacf5 Mon Sep 17 00:00:00 2001 From: Eli Bogomolny Date: Fri, 28 Jan 2022 14:07:14 -0500 Subject: [PATCH 13/32] Disable webgl2 in failing FramebufferManager test --- Specs/Renderer/FramebufferManagerSpec.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Specs/Renderer/FramebufferManagerSpec.js b/Specs/Renderer/FramebufferManagerSpec.js index 49bd6e0d6a29..0cd895ffe241 100644 --- a/Specs/Renderer/FramebufferManagerSpec.js +++ b/Specs/Renderer/FramebufferManagerSpec.js @@ -295,16 +295,19 @@ describe( depth: true, supportsDepthTexture: true, }); - // Disable extension + // Disable extensions const depthTexture = context._depthTexture; context._depthTexture = false; + const webgl2 = context._webgl2; + context._webgl2 = false; fbm.update(context, 1, 1); expect(fbm.getDepthTexture()).toBeUndefined(); expect(fbm.getDepthRenderbuffer()).toBeDefined(); - // Re-enable extension + // Re-enable extensions context._depthTexture = depthTexture; + context._webgl2 = webgl2; }); it("destroys attachments and framebuffer", function () { From b443feb3a9be26d5330edbcebc383e0331c67bfb Mon Sep 17 00:00:00 2001 From: Eli Bogomolny Date: Fri, 28 Jan 2022 14:11:44 -0500 Subject: [PATCH 14/32] Remove extra createGuid() calls --- Source/Renderer/Framebuffer.js | 3 --- Source/Renderer/Renderbuffer.js | 2 -- 2 files changed, 5 deletions(-) diff --git a/Source/Renderer/Framebuffer.js b/Source/Renderer/Framebuffer.js index 24ecf8b5e143..e9363082f8a1 100644 --- a/Source/Renderer/Framebuffer.js +++ b/Source/Renderer/Framebuffer.js @@ -7,8 +7,6 @@ import PixelFormat from "../Core/PixelFormat.js"; import ContextLimits from "./ContextLimits.js"; import PixelDatatype from "./PixelDatatype.js"; -import createGuid from "../Core/createGuid.js"; - function attachTexture(framebuffer, attachment, texture) { const gl = framebuffer._gl; gl.framebufferTexture2D( @@ -75,7 +73,6 @@ function attachRenderbuffer(framebuffer, attachment, renderbuffer) { * @constructor */ function Framebuffer(options) { - this._id = createGuid(); options = defaultValue(options, defaultValue.EMPTY_OBJECT); const context = options.context; diff --git a/Source/Renderer/Renderbuffer.js b/Source/Renderer/Renderbuffer.js index 85250cb3d5b1..b91f5a068974 100644 --- a/Source/Renderer/Renderbuffer.js +++ b/Source/Renderer/Renderbuffer.js @@ -5,13 +5,11 @@ import destroyObject from "../Core/destroyObject.js"; import DeveloperError from "../Core/DeveloperError.js"; import ContextLimits from "./ContextLimits.js"; import RenderbufferFormat from "./RenderbufferFormat.js"; -import createGuid from "../Core/createGuid.js"; /** * @private */ function Renderbuffer(options) { - this._id = createGuid(); options = defaultValue(options, defaultValue.EMPTY_OBJECT); //>>includeStart('debug', pragmas.debug); From 8c26a139ddb75e427e0b964eacde0b79d34b20c8 Mon Sep 17 00:00:00 2001 From: Eli Bogomolny Date: Fri, 28 Jan 2022 14:14:49 -0500 Subject: [PATCH 15/32] Remove PassThrough stage from PostProcessStageCollection --- Source/Scene/PostProcessStage.js | 6 +-- Source/Scene/PostProcessStageCollection.js | 43 +------------------- Source/Scene/PostProcessStageComposite.js | 2 +- Source/Scene/PostProcessStageLibrary.js | 12 +----- Source/Scene/PostProcessStageTextureCache.js | 43 +++++--------------- 5 files changed, 18 insertions(+), 88 deletions(-) diff --git a/Source/Scene/PostProcessStage.js b/Source/Scene/PostProcessStage.js index 2fc807d064de..208ff1404959 100644 --- a/Source/Scene/PostProcessStage.js +++ b/Source/Scene/PostProcessStage.js @@ -47,7 +47,7 @@ import PostProcessStageSampleMode from "./PostProcessStageSampleMode.js"; * * @example * // Simple stage to change the color - * var fs = + * const fs = * 'uniform sampler2D colorTexture;\n' + * 'varying vec2 v_textureCoordinates;\n' + * 'uniform float scale;\n' + @@ -69,7 +69,7 @@ import PostProcessStageSampleMode from "./PostProcessStageSampleMode.js"; * @example * // Simple stage to change the color of what is selected. * // If czm_selected returns true, the current fragment belongs to geometry in the selected array. - * var fs = + * const fs = * 'uniform sampler2D colorTexture;\n' + * 'varying vec2 v_textureCoordinates;\n' + * 'uniform vec4 highlight;\n' + @@ -82,7 +82,7 @@ import PostProcessStageSampleMode from "./PostProcessStageSampleMode.js"; * ' gl_FragColor = color;\n' + * ' }\n' + * '}\n'; - * var stage = scene.postProcessStages.add(new Cesium.PostProcessStage({ + * const stage = scene.postProcessStages.add(new Cesium.PostProcessStage({ * fragmentShader : fs, * uniforms : { * highlight : function() { diff --git a/Source/Scene/PostProcessStageCollection.js b/Source/Scene/PostProcessStageCollection.js index ba081b42c0b7..12893ddca0a0 100644 --- a/Source/Scene/PostProcessStageCollection.js +++ b/Source/Scene/PostProcessStageCollection.js @@ -35,7 +35,6 @@ const stackScratch = []; */ function PostProcessStageCollection() { const fxaa = PostProcessStageLibrary.createFXAAStage(); - const passThrough = PostProcessStageLibrary.createPassThroughStage(); const ao = PostProcessStageLibrary.createAmbientOcclusionStage(); const bloom = PostProcessStageLibrary.createBloomStage(); @@ -52,7 +51,6 @@ function PostProcessStageCollection() { const tonemapping = this._tonemapping; fxaa.enabled = false; - passThrough.enabled = false; ao.enabled = false; bloom.enabled = false; tonemapping.enabled = false; // will be enabled if necessary in update @@ -61,7 +59,7 @@ function PostProcessStageCollection() { const stageNames = {}; const stack = stackScratch; - stack.push(fxaa, passThrough, ao, bloom, tonemapping); + stack.push(fxaa, ao, bloom, tonemapping); while (stack.length > 0) { const stage = stack.pop(); stageNames[stage.name] = stage; @@ -89,13 +87,11 @@ function PostProcessStageCollection() { this._ao = ao; this._bloom = bloom; this._fxaa = fxaa; - this._passThrough = passThrough; this._aoEnabled = undefined; this._bloomEnabled = undefined; this._tonemappingEnabled = undefined; this._fxaaEnabled = undefined; - this._passThroughEnabled = undefined; this._activeStagesChanged = false; this._stagesRemoved = false; @@ -124,14 +120,11 @@ Object.defineProperties(PostProcessStageCollection.prototype, { } const fxaa = this._fxaa; - const passThrough = this._passThrough; const ao = this._ao; const bloom = this._bloom; const tonemapping = this._tonemapping; readyAndEnabled = readyAndEnabled || (fxaa.ready && fxaa.enabled); - readyAndEnabled = - readyAndEnabled || (passThrough.ready && passThrough.enabled); readyAndEnabled = readyAndEnabled || (ao.ready && ao.enabled); readyAndEnabled = readyAndEnabled || (bloom.ready && bloom.enabled); readyAndEnabled = @@ -140,18 +133,6 @@ Object.defineProperties(PostProcessStageCollection.prototype, { return readyAndEnabled; }, }, - /** - * A no-op post-process stage. - * - * @memberof PostProcessStageCollection.prototype - * @type {PostProcessStage} - * @readonly - */ - passThrough: { - get: function () { - return this._passThrough; - }, - }, /** * A post-process stage for Fast Approximate Anti-aliasing. *

@@ -275,11 +256,6 @@ Object.defineProperties(PostProcessStageCollection.prototype, { return this.getOutputTexture(fxaa.name); } - const passThrough = this._passThrough; - if (passThrough.enabled && passThrough.ready) { - return this.getOutputTexture(passThrough.name); - } - const stages = this._stages; const length = stages.length; for (let i = length - 1; i >= 0; --i) { @@ -616,7 +592,6 @@ PostProcessStageCollection.prototype.update = function ( const autoexposure = this._autoExposure; const tonemapping = this._tonemapping; const fxaa = this._fxaa; - const passThrough = this._passThrough; tonemapping.enabled = useHdr; @@ -625,8 +600,6 @@ PostProcessStageCollection.prototype.update = function ( const tonemappingEnabled = tonemapping.enabled && tonemapping._isSupported(context); const fxaaEnabled = fxaa.enabled && fxaa._isSupported(context); - const passThroughEnabled = - passThrough.enabled && passThrough._isSupported(context); if ( activeStagesChanged || @@ -634,8 +607,7 @@ PostProcessStageCollection.prototype.update = function ( aoEnabled !== this._aoEnabled || bloomEnabled !== this._bloomEnabled || tonemappingEnabled !== this._tonemappingEnabled || - fxaaEnabled !== this._fxaaEnabled || - passThroughEnabled !== this._passThroughEnabled + fxaaEnabled !== this._fxaaEnabled ) { // The number of stages to execute has changed. // Update dependencies and recreate framebuffers. @@ -645,7 +617,6 @@ PostProcessStageCollection.prototype.update = function ( this._bloomEnabled = bloomEnabled; this._tonemappingEnabled = tonemappingEnabled; this._fxaaEnabled = fxaaEnabled; - this._passThroughEnabled = passThroughEnabled; this._textureCacheDirty = false; } @@ -682,7 +653,6 @@ PostProcessStageCollection.prototype.update = function ( this._textureCache.update(context); fxaa.update(context, useLogDepth); - passThrough.update(context, useLogDepth); ao.update(context, useLogDepth); bloom.update(context, useLogDepth); tonemapping.update(context, useLogDepth); @@ -794,7 +764,6 @@ PostProcessStageCollection.prototype.execute = function ( const activeStages = this._activeStages; const length = activeStages.length; const fxaa = this._fxaa; - const passThrough = this._passThrough; const ao = this._ao; const bloom = this._bloom; const autoexposure = this._autoExposure; @@ -806,12 +775,9 @@ PostProcessStageCollection.prototype.execute = function ( const tonemappingEnabled = tonemapping.enabled && tonemapping._isSupported(context); const fxaaEnabled = fxaa.enabled && fxaa._isSupported(context); - const passThroughEnabled = - passThrough.enabled && passThrough._isSupported(context); if ( !fxaaEnabled && - !passThroughEnabled && !aoEnabled && !bloomEnabled && !tonemappingEnabled && @@ -856,10 +822,6 @@ PostProcessStageCollection.prototype.execute = function ( if (fxaaEnabled && fxaa.ready) { execute(fxaa, context, lastTexture, depthTexture, idTexture); } - - if (passThroughEnabled && passThrough.ready) { - execute(passThrough, context, lastTexture, depthTexture, idTexture); - } }; /** @@ -917,7 +879,6 @@ PostProcessStageCollection.prototype.isDestroyed = function () { */ PostProcessStageCollection.prototype.destroy = function () { this._fxaa.destroy(); - this._passThrough.destroy(); this._ao.destroy(); this._bloom.destroy(); this._autoExposure.destroy(); diff --git a/Source/Scene/PostProcessStageComposite.js b/Source/Scene/PostProcessStageComposite.js index 123e8d9cd18e..cd6b5c897752 100644 --- a/Source/Scene/PostProcessStageComposite.js +++ b/Source/Scene/PostProcessStageComposite.js @@ -57,7 +57,7 @@ import destroyObject from "../Core/destroyObject.js"; * * @example * // Example 3: create a uniform alias - * var uniforms = {}; + * const uniforms = {}; * Cesium.defineProperties(uniforms, { * filterSize : { * get : function() { diff --git a/Source/Scene/PostProcessStageLibrary.js b/Source/Scene/PostProcessStageLibrary.js index 0ee0c4ec31f7..64f28bffc23c 100644 --- a/Source/Scene/PostProcessStageLibrary.js +++ b/Source/Scene/PostProcessStageLibrary.js @@ -15,7 +15,6 @@ import DepthView from "../Shaders/PostProcessStages/DepthView.js"; import EdgeDetection from "../Shaders/PostProcessStages/EdgeDetection.js"; import FilmicTonemapping from "../Shaders/PostProcessStages/FilmicTonemapping.js"; import FXAA from "../Shaders/PostProcessStages/FXAA.js"; -import PassThrough from "../Shaders/PostProcessStages/PassThrough.js"; import GaussianBlur1D from "../Shaders/PostProcessStages/GaussianBlur1D.js"; import LensFlare from "../Shaders/PostProcessStages/LensFlare.js"; import ModifiedReinhardTonemapping from "../Shaders/PostProcessStages/ModifiedReinhardTonemapping.js"; @@ -227,11 +226,11 @@ PostProcessStageLibrary.isDepthOfFieldSupported = function (scene) { * * @example * // multiple silhouette effects - * var yellowEdge = Cesium.PostProcessLibrary.createEdgeDetectionStage(); + * const yellowEdge = Cesium.PostProcessLibrary.createEdgeDetectionStage(); * yellowEdge.uniforms.color = Cesium.Color.YELLOW; * yellowEdge.selected = [feature0]; * - * var greenEdge = Cesium.PostProcessLibrary.createEdgeDetectionStage(); + * const greenEdge = Cesium.PostProcessLibrary.createEdgeDetectionStage(); * greenEdge.uniforms.color = Cesium.Color.LIME; * greenEdge.selected = [feature1]; * @@ -667,13 +666,6 @@ PostProcessStageLibrary.createFXAAStage = function () { }); }; -PostProcessStageLibrary.createPassThroughStage = function () { - return new PostProcessStage({ - name: "czm_PassThrough", - fragmentShader: PassThrough, - }); -}; - /** * Creates a post-process stage that applies ACES tonemapping operator. * @param {Boolean} useAutoExposure Whether or not to use auto-exposure. diff --git a/Source/Scene/PostProcessStageTextureCache.js b/Source/Scene/PostProcessStageTextureCache.js index 30b3edca10f9..27dae7265101 100644 --- a/Source/Scene/PostProcessStageTextureCache.js +++ b/Source/Scene/PostProcessStageTextureCache.js @@ -3,8 +3,7 @@ import defined from "../Core/defined.js"; import destroyObject from "../Core/destroyObject.js"; import CesiumMath from "../Core/Math.js"; import ClearCommand from "../Renderer/ClearCommand.js"; -import Framebuffer from "../Renderer/Framebuffer.js"; -import Texture from "../Renderer/Texture.js"; +import FramebufferManager from "../Renderer/FramebufferManager.js"; /** * Creates a minimal amount of textures and framebuffers. @@ -148,7 +147,6 @@ function getDependencies(collection, context) { const bloom = collection.bloom; const tonemapping = collection._tonemapping; const fxaa = collection.fxaa; - const passThrough = collection.passThrough; let previousName = getCompositeDependencies( collection, @@ -178,13 +176,6 @@ function getDependencies(collection, context) { collection, previousName ); - previousName = getStageDependencies( - collection, - context, - dependencies, - passThrough, - previousName - ); getStageDependencies(collection, context, dependencies, fxaa, previousName); } else { getCompositeDependencies( @@ -253,7 +244,10 @@ function getFramebuffer(cache, stageName, dependencies) { pixelDatatype: pixelDatatype, clearColor: clearColor, stages: [stageName], - buffer: undefined, + buffer: new FramebufferManager({ + pixelFormat: pixelFormat, + pixelDatatype: pixelDatatype, + }), clear: undefined, }; @@ -279,8 +273,7 @@ function releaseResources(cache) { const length = framebuffers.length; for (let i = 0; i < length; ++i) { const framebuffer = framebuffers[i]; - framebuffer.buffer = framebuffer.buffer && framebuffer.buffer.destroy(); - framebuffer.buffer = undefined; + framebuffer.buffer.destroy(); } } @@ -306,21 +299,10 @@ function updateFramebuffers(cache, context) { textureHeight = size; } - framebuffer.buffer = new Framebuffer({ - context: context, - colorTextures: [ - new Texture({ - context: context, - width: textureWidth, - height: textureHeight, - pixelFormat: framebuffer.pixelFormat, - pixelDatatype: framebuffer.pixelDatatype, - }), - ], - }); + framebuffer.buffer.update(context, textureWidth, textureHeight); framebuffer.clear = new ClearCommand({ color: framebuffer.clearColor, - framebuffer: framebuffer.buffer, + framebuffer: framebuffer.buffer.framebuffer, }); } } @@ -353,18 +335,13 @@ PostProcessStageTextureCache.prototype.update = function (context) { defined(collection.fxaa) && collection.fxaa.enabled && collection.fxaa._isSupported(context); - const passThroughEnabled = - defined(collection.passThrough) && - collection.passThrough.enabled && - collection.passThrough._isSupported(context); const needsCheckDimensionsUpdate = !defined(collection._activeStages) || collection._activeStages.length > 0 || aoEnabled || bloomEnabled || tonemappingEnabled || - fxaaEnabled || - passThroughEnabled; + fxaaEnabled; if ( updateDependencies || (!needsCheckDimensionsUpdate && this._framebuffers.length > 0) @@ -439,7 +416,7 @@ PostProcessStageTextureCache.prototype.getFramebuffer = function (name) { if (!defined(framebuffer)) { return undefined; } - return framebuffer.buffer; + return framebuffer.buffer.framebuffer; }; /** From dd0a7f833add705ce5168847467794d62621cf36 Mon Sep 17 00:00:00 2001 From: Eli Bogomolny Date: Fri, 28 Jan 2022 15:57:53 -0500 Subject: [PATCH 16/32] Remove passThrough check in Scene.js --- Source/Scene/Scene.js | 1 - 1 file changed, 1 deletion(-) diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js index c3bd397c87aa..8cffbf44da63 100644 --- a/Source/Scene/Scene.js +++ b/Source/Scene/Scene.js @@ -3420,7 +3420,6 @@ function updateAndClearFramebuffers(scene, passState, clearColor) { postProcess.length > 0 || postProcess.ambientOcclusion.enabled || postProcess.fxaa.enabled || - postProcess.passThrough.enabled || postProcess.bloom.enabled)); environmentState.usePostProcessSelected = false; if (usePostProcess) { From 1301dddaceba1652a34f5ab9b69394b31d00dcdc Mon Sep 17 00:00:00 2001 From: Eli Bogomolny Date: Fri, 28 Jan 2022 18:39:51 -0500 Subject: [PATCH 17/32] Check for dirty OIT frames --- Source/Scene/OIT.js | 17 +++++++++++------ Source/Scene/Scene.js | 4 +++- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/Source/Scene/OIT.js b/Source/Scene/OIT.js index e882d52d40d7..cb27a6fbb1f5 100644 --- a/Source/Scene/OIT.js +++ b/Source/Scene/OIT.js @@ -20,7 +20,6 @@ import BlendFunction from "./BlendFunction.js"; * @private */ function OIT(context) { - this._numSamples = 1; // We support multipass for the Chrome D3D9 backend and ES 2.0 on mobile. this._translucentMultipassSupport = false; this._translucentMRTSupport = false; @@ -89,6 +88,14 @@ function OIT(context) { this._useHDR = false; } +Object.defineProperties(OIT.prototype, { + numSamples: { + get: function () { + return defined(this._opaqueFBO) ? this._opaqueFBO._numSamples : 1; + }, + }, +}); + function destroyTextures(oit) { oit._accumulationTexture = oit._accumulationTexture && @@ -209,7 +216,7 @@ OIT.prototype.update = function ( passState, framebuffer, useHDR, - numSamples + samplesDirty ) { if (!this.isSupported()) { return; @@ -228,16 +235,14 @@ OIT.prototype.update = function ( accumulationTexture.width !== width || accumulationTexture.height !== height || useHDR !== this._useHDR; - const samplesChanged = this._numSamples !== numSamples; - this._numSamples = numSamples; - if (textureChanged || samplesChanged) { + if (textureChanged || samplesDirty) { updateTextures(this, context, width, height); } if ( !defined(this._translucentFBO.framebuffer) || textureChanged || - samplesChanged + samplesDirty ) { if (!updateFramebuffers(this, context)) { // framebuffer creation failed diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js index 8cffbf44da63..977392816ca2 100644 --- a/Source/Scene/Scene.js +++ b/Source/Scene/Scene.js @@ -150,6 +150,7 @@ const requestRenderAfterFrame = function (scene) { * }); */ function Scene(options) { + this._samplesDirty = false; options = defaultValue(options, defaultValue.EMPTY_OBJECT); const canvas = options.canvas; let creditContainer = options.creditContainer; @@ -3356,6 +3357,7 @@ function updateAndClearFramebuffers(scene, passState, clearColor) { const originalSamples = scene.numberSamples; if (picking) { scene.numberSamples = 1; + scene.samplesDirty = view.oit.numSamples !== scene.numberSamples; } // Preserve the reference to the original framebuffer. @@ -3407,7 +3409,7 @@ function updateAndClearFramebuffers(scene, passState, clearColor) { passState, view.globeDepth._colorFramebuffer, scene._hdr, - scene.numberSamples + scene.samplesDirty ); oit.clear(context, passState, clearColor); environmentState.useOIT = oit.isSupported(); From dd29f44e861f3a4fd06bac7dcb94475d796214b9 Mon Sep 17 00:00:00 2001 From: Eli Bogomolny Date: Fri, 28 Jan 2022 18:55:37 -0500 Subject: [PATCH 18/32] Check view.oit is defined --- Source/Scene/Scene.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js index 977392816ca2..1cb927fc426a 100644 --- a/Source/Scene/Scene.js +++ b/Source/Scene/Scene.js @@ -3357,7 +3357,7 @@ function updateAndClearFramebuffers(scene, passState, clearColor) { const originalSamples = scene.numberSamples; if (picking) { scene.numberSamples = 1; - scene.samplesDirty = view.oit.numSamples !== scene.numberSamples; + scene.samplesDirty = defined(view.oit) && view.oit.numSamples !== scene.numberSamples; } // Preserve the reference to the original framebuffer. From cd2b7411d4f7a760bd1c506b2d918e5b27d88522 Mon Sep 17 00:00:00 2001 From: Eli Bogomolny Date: Mon, 31 Jan 2022 10:07:00 -0500 Subject: [PATCH 19/32] Scene.js prettier --- Source/Scene/Scene.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js index 1cb927fc426a..cf362a176b79 100644 --- a/Source/Scene/Scene.js +++ b/Source/Scene/Scene.js @@ -3357,7 +3357,8 @@ function updateAndClearFramebuffers(scene, passState, clearColor) { const originalSamples = scene.numberSamples; if (picking) { scene.numberSamples = 1; - scene.samplesDirty = defined(view.oit) && view.oit.numSamples !== scene.numberSamples; + scene.samplesDirty = + defined(view.oit) && view.oit.numSamples !== scene.numberSamples; } // Preserve the reference to the original framebuffer. From 1bfb1f1da2b5f7e0aae3421a74f9e4fbd1a43cbe Mon Sep 17 00:00:00 2001 From: Eli Bogomolny Date: Tue, 1 Feb 2022 14:26:11 -0500 Subject: [PATCH 20/32] Store a picking-only Framebuffer in GlobeDepth for pick frames --- Source/Scene/GlobeDepth.js | 53 ++++++++++++++++++++++++++++---------- Source/Scene/OIT.js | 10 ++++--- Source/Scene/Scene.js | 17 +++++------- 3 files changed, 53 insertions(+), 27 deletions(-) diff --git a/Source/Scene/GlobeDepth.js b/Source/Scene/GlobeDepth.js index 4a8513868190..1f8ae7998c1a 100644 --- a/Source/Scene/GlobeDepth.js +++ b/Source/Scene/GlobeDepth.js @@ -17,9 +17,14 @@ import StencilOperation from "./StencilOperation.js"; * @private */ function GlobeDepth() { + this._picking = false; this._numSamples = 1; this._tempCopyDepthTexture = undefined; + this._pickColorFramebuffer = new FramebufferManager({ + depthStencil: true, + supportsDepthTexture: true, + }); this._colorFramebuffer = new FramebufferManager({ depthStencil: true, supportsDepthTexture: true, @@ -51,19 +56,35 @@ function GlobeDepth() { } Object.defineProperties(GlobeDepth.prototype, { + colorFramebufferManager: { + get: function () { + return this._picking + ? this._pickColorFramebuffer + : this._colorFramebuffer; + }, + }, framebuffer: { get: function () { - return this._colorFramebuffer.framebuffer; + return this.colorFramebufferManager.framebuffer; }, }, depthStencilTexture: { get: function () { - return this._colorFramebuffer.getDepthStencilTexture(); + return this.colorFramebufferManager.getDepthStencilTexture(); + }, + }, + picking: { + get: function () { + return this._picking; + }, + set: function (value) { + this._picking = value; }, }, }); function destroyFramebuffers(globeDepth) { + globeDepth._pickColorFramebuffer.destroy(); globeDepth._colorFramebuffer.destroy(); globeDepth._copyDepthFramebuffer.destroy(); globeDepth._tempCopyDepthFramebuffer.destroy(); @@ -140,7 +161,7 @@ function updateCopyCommands(globeDepth, context, width, height, passState) { { uniformMap: { u_depthTexture: function () { - return globeDepth._colorFramebuffer.getDepthStencilTexture(); + return globeDepth.colorFramebufferManager.getDepthStencilTexture(); }, }, owner: globeDepth, @@ -158,7 +179,7 @@ function updateCopyCommands(globeDepth, context, width, height, passState) { { uniformMap: { colorTexture: function () { - return globeDepth._colorFramebuffer.getColorTexture(); + return globeDepth.colorFramebufferManager.getColorTexture(); }, }, owner: globeDepth, @@ -232,13 +253,17 @@ GlobeDepth.prototype.update = function ( : PixelDatatype.FLOAT : PixelDatatype.UNSIGNED_BYTE; this._numSamples = numSamples; - this._colorFramebuffer.update( - context, - width, - height, - numSamples, - pixelDatatype - ); + if (this.picking) { + this._pickColorFramebuffer.update(context, width, height, 1, pixelDatatype); + } else { + this._colorFramebuffer.update( + context, + width, + height, + numSamples, + pixelDatatype + ); + } this._copyDepthFramebuffer.update(context, width, height); updateCopyCommands(this, context, width, height, passState); context.uniformState.globeDepthTexture = undefined; @@ -248,7 +273,7 @@ GlobeDepth.prototype.update = function ( }; GlobeDepth.prototype.prepareColorTextures = function (context) { - if (this._numSamples > 1) { + if (!this.picking && this._numSamples > 1) { this._colorFramebuffer.prepareTextures(context); } }; @@ -272,7 +297,7 @@ GlobeDepth.prototype.executeUpdateDepth = function ( : passState.framebuffer.depthStencilTexture; if ( clearGlobeDepth || - depthTextureToCopy !== this._colorFramebuffer.getDepthStencilTexture() + depthTextureToCopy !== this.colorFramebufferManager.getDepthStencilTexture() ) { // First copy the depth to a temporary globe depth texture, then update the // main globe depth texture where the stencil bit for 3D Tiles is set. @@ -321,7 +346,7 @@ GlobeDepth.prototype.clear = function (context, passState, clearColor) { const clear = this._clearGlobeColorCommand; if (defined(clear)) { Color.clone(clearColor, clear.color); - this._colorFramebuffer.clear(context, clear, passState); + this.colorFramebufferManager.clear(context, clear, passState); } }; diff --git a/Source/Scene/OIT.js b/Source/Scene/OIT.js index cb27a6fbb1f5..6f12d7ee8cb1 100644 --- a/Source/Scene/OIT.js +++ b/Source/Scene/OIT.js @@ -20,6 +20,7 @@ import BlendFunction from "./BlendFunction.js"; * @private */ function OIT(context) { + this._numSamples = 1; // We support multipass for the Chrome D3D9 backend and ES 2.0 on mobile. this._translucentMultipassSupport = false; this._translucentMRTSupport = false; @@ -216,7 +217,7 @@ OIT.prototype.update = function ( passState, framebuffer, useHDR, - samplesDirty + numSamples ) { if (!this.isSupported()) { return; @@ -235,14 +236,17 @@ OIT.prototype.update = function ( accumulationTexture.width !== width || accumulationTexture.height !== height || useHDR !== this._useHDR; - if (textureChanged || samplesDirty) { + const samplesChanged = this._numSamples !== numSamples; + + if (textureChanged || samplesChanged) { + this._numSamples = numSamples; updateTextures(this, context, width, height); } if ( !defined(this._translucentFBO.framebuffer) || textureChanged || - samplesDirty + samplesChanged ) { if (!updateFramebuffers(this, context)) { // framebuffer creation failed diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js index cf362a176b79..ca0bef73c4f9 100644 --- a/Source/Scene/Scene.js +++ b/Source/Scene/Scene.js @@ -150,7 +150,6 @@ const requestRenderAfterFrame = function (scene) { * }); */ function Scene(options) { - this._samplesDirty = false; options = defaultValue(options, defaultValue.EMPTY_OBJECT); const canvas = options.canvas; let creditContainer = options.creditContainer; @@ -3352,14 +3351,12 @@ function updateAndClearFramebuffers(scene, passState, clearColor) { const passes = scene._frameState.passes; const picking = passes.pick; + if (defined(view.globeDepth)) { + view.globeDepth.picking = picking; + } const useWebVR = environmentState.useWebVR; const originalSamples = scene.numberSamples; - if (picking) { - scene.numberSamples = 1; - scene.samplesDirty = - defined(view.oit) && view.oit.numSamples !== scene.numberSamples; - } // Preserve the reference to the original framebuffer. environmentState.originalFramebuffer = passState.framebuffer; @@ -3408,9 +3405,9 @@ function updateAndClearFramebuffers(scene, passState, clearColor) { oit.update( context, passState, - view.globeDepth._colorFramebuffer, + view.globeDepth.colorFramebufferManager, scene._hdr, - scene.samplesDirty + scene.numberSamples ); oit.clear(context, passState, clearColor); environmentState.useOIT = oit.isSupported(); @@ -3470,7 +3467,7 @@ function updateAndClearFramebuffers(scene, passState, clearColor) { scene._invertClassification.update( context, scene.numberSamples, - view.globeDepth._colorFramebuffer + view.globeDepth.colorFramebufferManager ); scene._invertClassification.clear(context, passState); @@ -3518,7 +3515,7 @@ Scene.prototype.resolveFramebuffers = function (passState) { const defaultFramebuffer = environmentState.originalFramebuffer; const globeFramebuffer = useGlobeDepthFramebuffer - ? globeDepth._colorFramebuffer + ? globeDepth.colorFramebufferManager : undefined; const sceneFramebuffer = view.sceneFramebuffer._colorFramebuffer; const idFramebuffer = view.sceneFramebuffer.idFramebuffer; From 87997e26da66a2de111d342fd02449a117df026a Mon Sep 17 00:00:00 2001 From: Eli Bogomolny Date: Wed, 2 Feb 2022 14:39:05 -0500 Subject: [PATCH 21/32] Rename numberSamples to msaaSamples, fix TranslucentTileClassification --- Source/Renderer/FramebufferManager.js | 22 ++++++++----------- Source/Renderer/MultisampleFramebuffer.js | 10 ++------- Source/Renderer/Renderbuffer.js | 4 ++-- Source/Scene/GlobeDepth.js | 12 +++++----- Source/Scene/OIT.js | 8 ------- Source/Scene/Scene.js | 18 ++++++--------- Source/Scene/TranslucentTileClassification.js | 9 ++++---- 7 files changed, 30 insertions(+), 53 deletions(-) diff --git a/Source/Renderer/FramebufferManager.js b/Source/Renderer/FramebufferManager.js index 471eee1d7063..de90e2405367 100644 --- a/Source/Renderer/FramebufferManager.js +++ b/Source/Renderer/FramebufferManager.js @@ -14,6 +14,7 @@ import PixelFormat from "../Core/PixelFormat.js"; * Creates a wrapper object around a framebuffer and its resources. * * @param {Object} options Object with the following properties: + * @param {Number} [options.numSamples=1] The multisampling rate of the render targets. Requires a WebGL2 context. * @param {Number} [options.colorAttachmentsLength=1] The number of color attachments this FramebufferManager will create. * @param {Boolean} [options.color=true] Whether the FramebufferManager will use color attachments. * @param {Boolean} [options.depth=false] Whether the FramebufferManager will use depth attachments. @@ -32,7 +33,7 @@ import PixelFormat from "../Core/PixelFormat.js"; */ function FramebufferManager(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); - this._numSamples = 1; + this._numSamples = defaultValue(options.numSamples, 1); this._colorAttachmentsLength = defaultValue( options.colorAttachmentsLength, 1 @@ -98,6 +99,11 @@ Object.defineProperties(FramebufferManager.prototype, { return this._framebuffer; }, }, + numSamples: { + get: function () { + return this._numSamples; + }, + }, status: { get: function () { return this.framebuffer.status; @@ -407,18 +413,8 @@ FramebufferManager.prototype.clear = function ( passState ) { const framebuffer = clearCommand.framebuffer; - - if (this._numSamples > 1) { - clearCommand.framebuffer = this._multisampleFramebuffer.getRenderFramebuffer(); - clearCommand.execute(context, passState); - - clearCommand.framebuffer = this._multisampleFramebuffer.getColorFramebuffer(); - clearCommand.execute(context, passState); - } else { - clearCommand.framebuffer = this._framebuffer; - clearCommand.execute(context, passState); - } - + clearCommand.framebuffer = this.framebuffer; + clearCommand.execute(context, passState); clearCommand.framebuffer = framebuffer; }; diff --git a/Source/Renderer/MultisampleFramebuffer.js b/Source/Renderer/MultisampleFramebuffer.js index 40f29703f92e..8eac84be5765 100644 --- a/Source/Renderer/MultisampleFramebuffer.js +++ b/Source/Renderer/MultisampleFramebuffer.js @@ -36,10 +36,7 @@ function MultisampleFramebuffer(options) { const colorRenderbuffers = options.colorRenderbuffers; const colorTextures = options.colorTextures; - if ( - (defined(colorRenderbuffers) && !defined(colorTextures)) || - (defined(colorTextures) && !defined(colorRenderbuffers)) - ) { + if (defined(colorRenderbuffers) !== defined(colorTextures)) { throw new DeveloperError( "Both color renderbuffer and texture attachments must be provided." ); @@ -47,10 +44,7 @@ function MultisampleFramebuffer(options) { const depthStencilRenderbuffer = options.depthStencilRenderbuffer; const depthStencilTexture = options.depthStencilTexture; - if ( - (defined(depthStencilRenderbuffer) && !defined(depthStencilTexture)) || - (defined(depthStencilTexture) && !defined(depthStencilRenderbuffer)) - ) { + if (defined(depthStencilRenderbuffer) !== defined(depthStencilTexture)) { throw new DeveloperError( "Both depth-stencil renderbuffer and texture attachments must be provided." ); diff --git a/Source/Renderer/Renderbuffer.js b/Source/Renderer/Renderbuffer.js index b91f5a068974..52dbbf3bd53a 100644 --- a/Source/Renderer/Renderbuffer.js +++ b/Source/Renderer/Renderbuffer.js @@ -65,8 +65,8 @@ function Renderbuffer(options) { gl.RENDERBUFFER, numSamples, format, - this._width, - this._height + width, + height ); } else { gl.renderbufferStorage(gl.RENDERBUFFER, format, width, height); diff --git a/Source/Scene/GlobeDepth.js b/Source/Scene/GlobeDepth.js index 1f8ae7998c1a..8ef6f6e3a40c 100644 --- a/Source/Scene/GlobeDepth.js +++ b/Source/Scene/GlobeDepth.js @@ -25,7 +25,7 @@ function GlobeDepth() { depthStencil: true, supportsDepthTexture: true, }); - this._colorFramebuffer = new FramebufferManager({ + this._outputFramebuffer = new FramebufferManager({ depthStencil: true, supportsDepthTexture: true, }); @@ -60,7 +60,7 @@ Object.defineProperties(GlobeDepth.prototype, { get: function () { return this._picking ? this._pickColorFramebuffer - : this._colorFramebuffer; + : this._outputFramebuffer; }, }, framebuffer: { @@ -85,7 +85,7 @@ Object.defineProperties(GlobeDepth.prototype, { function destroyFramebuffers(globeDepth) { globeDepth._pickColorFramebuffer.destroy(); - globeDepth._colorFramebuffer.destroy(); + globeDepth._outputFramebuffer.destroy(); globeDepth._copyDepthFramebuffer.destroy(); globeDepth._tempCopyDepthFramebuffer.destroy(); globeDepth._updateDepthFramebuffer.destroy(); @@ -254,9 +254,9 @@ GlobeDepth.prototype.update = function ( : PixelDatatype.UNSIGNED_BYTE; this._numSamples = numSamples; if (this.picking) { - this._pickColorFramebuffer.update(context, width, height, 1, pixelDatatype); + this._pickColorFramebuffer.update(context, width, height); } else { - this._colorFramebuffer.update( + this._outputFramebuffer.update( context, width, height, @@ -274,7 +274,7 @@ GlobeDepth.prototype.update = function ( GlobeDepth.prototype.prepareColorTextures = function (context) { if (!this.picking && this._numSamples > 1) { - this._colorFramebuffer.prepareTextures(context); + this._outputFramebuffer.prepareTextures(context); } }; diff --git a/Source/Scene/OIT.js b/Source/Scene/OIT.js index 6f12d7ee8cb1..dd05ad3f2d4a 100644 --- a/Source/Scene/OIT.js +++ b/Source/Scene/OIT.js @@ -89,14 +89,6 @@ function OIT(context) { this._useHDR = false; } -Object.defineProperties(OIT.prototype, { - numSamples: { - get: function () { - return defined(this._opaqueFBO) ? this._opaqueFBO._numSamples : 1; - }, - }, -}); - function destroyTextures(oit) { oit._accumulationTexture = oit._accumulationTexture && diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js index ca0bef73c4f9..a24aa953474c 100644 --- a/Source/Scene/Scene.js +++ b/Source/Scene/Scene.js @@ -590,11 +590,11 @@ function Scene(options) { this.eyeSeparation = undefined; /** - * The number of samples for MSAA (MSAA is disabled when numberSamples = 1). + * The number of samples for MSAA (MSAA is disabled when msaaSamples = 1). * @type {Number} * @default 1 */ - this.numberSamples = 1; + this.msaaSamples = 1; /** * Post processing effects applied to the final render. @@ -2620,7 +2620,7 @@ function executeCommands(scene, passState) { executeCommand, passState, commands, - globeDepth.framebuffer + globeDepth.depthStencilTexture ); view.translucentTileClassification.executeClassificationCommands( scene, @@ -3356,8 +3356,6 @@ function updateAndClearFramebuffers(scene, passState, clearColor) { } const useWebVR = environmentState.useWebVR; - const originalSamples = scene.numberSamples; - // Preserve the reference to the original framebuffer. environmentState.originalFramebuffer = passState.framebuffer; @@ -3390,7 +3388,7 @@ function updateAndClearFramebuffers(scene, passState, clearColor) { context, passState, view.viewport, - scene.numberSamples, + scene.msaaSamples, scene._hdr, environmentState.clearGlobeDepth ); @@ -3407,7 +3405,7 @@ function updateAndClearFramebuffers(scene, passState, clearColor) { passState, view.globeDepth.colorFramebufferManager, scene._hdr, - scene.numberSamples + scene.msaaSamples ); oit.clear(context, passState, clearColor); environmentState.useOIT = oit.isSupported(); @@ -3427,7 +3425,7 @@ function updateAndClearFramebuffers(scene, passState, clearColor) { context, view.viewport, scene._hdr, - scene.numberSamples + scene.msaaSamples ); view.sceneFramebuffer.clear(context, passState, clearColor); @@ -3466,7 +3464,7 @@ function updateAndClearFramebuffers(scene, passState, clearColor) { scene._invertClassification.previousFramebuffer = depthFramebuffer; scene._invertClassification.update( context, - scene.numberSamples, + scene.msaaSamples, view.globeDepth.colorFramebufferManager ); scene._invertClassification.clear(context, passState); @@ -3493,8 +3491,6 @@ function updateAndClearFramebuffers(scene, passState, clearColor) { passState ); } - - scene.numberSamples = originalSamples; } /** diff --git a/Source/Scene/TranslucentTileClassification.js b/Source/Scene/TranslucentTileClassification.js index b21637f226db..4b36673d889c 100644 --- a/Source/Scene/TranslucentTileClassification.js +++ b/Source/Scene/TranslucentTileClassification.js @@ -133,14 +133,13 @@ function updateResources( transpClass, context, passState, - globeDepthFramebuffer + globeDepthStencilTexture ) { if (!transpClass.isSupported()) { return; } - transpClass._opaqueDepthStencilTexture = - globeDepthFramebuffer.depthStencilTexture; + transpClass._opaqueDepthStencilTexture = globeDepthStencilTexture; const width = transpClass._opaqueDepthStencilTexture.width; const height = transpClass._opaqueDepthStencilTexture.height; @@ -345,7 +344,7 @@ TranslucentTileClassification.prototype.executeTranslucentCommands = function ( executeCommand, passState, commands, - globeDepthFramebuffer + globeDepthStencilTexture ) { // Check for translucent commands that should be classified const length = commands.length; @@ -370,7 +369,7 @@ TranslucentTileClassification.prototype.executeTranslucentCommands = function ( return; } - updateResources(this, context, passState, globeDepthFramebuffer); + updateResources(this, context, passState, globeDepthStencilTexture); // Get translucent depth passState.framebuffer = this._drawClassificationFBO.framebuffer; From 895c64137c9c5e8dcb887244f5162509628cee54 Mon Sep 17 00:00:00 2001 From: Eli Bogomolny Date: Thu, 3 Feb 2022 13:52:31 -0500 Subject: [PATCH 22/32] Fix HDR, add float and half float RenderbufferFormats --- Source/Renderer/FramebufferManager.js | 3 ++- Source/Renderer/RenderbufferFormat.js | 13 +++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/Source/Renderer/FramebufferManager.js b/Source/Renderer/FramebufferManager.js index de90e2405367..38efac962c5f 100644 --- a/Source/Renderer/FramebufferManager.js +++ b/Source/Renderer/FramebufferManager.js @@ -185,11 +185,12 @@ FramebufferManager.prototype.update = function ( sampler: Sampler.NEAREST, }); if (this._numSamples > 1) { + const format = RenderbufferFormat.getColorFormat(pixelDatatype); this._colorRenderbuffers[i] = new Renderbuffer({ context: context, width: width, height: height, - format: RenderbufferFormat.RGBA8, + format: format, numSamples: this._numSamples, }); } diff --git a/Source/Renderer/RenderbufferFormat.js b/Source/Renderer/RenderbufferFormat.js index 9f8743f591d4..d3874f6249d7 100644 --- a/Source/Renderer/RenderbufferFormat.js +++ b/Source/Renderer/RenderbufferFormat.js @@ -6,6 +6,8 @@ import WebGLConstants from "../Core/WebGLConstants.js"; const RenderbufferFormat = { RGBA4: WebGLConstants.RGBA4, RGBA8: WebGLConstants.RGBA8, + RGBA16F: WebGLConstants.RGBA16F, + RGBA32F: WebGLConstants.RGBA32F, RGB5_A1: WebGLConstants.RGB5_A1, RGB565: WebGLConstants.RGB565, DEPTH_COMPONENT16: WebGLConstants.DEPTH_COMPONENT16, @@ -17,6 +19,8 @@ const RenderbufferFormat = { return ( renderbufferFormat === RenderbufferFormat.RGBA4 || renderbufferFormat === RenderbufferFormat.RGBA8 || + renderbufferFormat === RenderbufferFormat.RGBA16F || + renderbufferFormat === RenderbufferFormat.RGBA32F || renderbufferFormat === RenderbufferFormat.RGB5_A1 || renderbufferFormat === RenderbufferFormat.RGB565 || renderbufferFormat === RenderbufferFormat.DEPTH_COMPONENT16 || @@ -25,5 +29,14 @@ const RenderbufferFormat = { renderbufferFormat === RenderbufferFormat.DEPTH24_STENCIL8 ); }, + + getColorFormat: function (datatype) { + if (datatype === WebGLConstants.FLOAT) { + return RenderbufferFormat.RGBA32F; + } else if (datatype === WebGLConstants.HALF_FLOAT_OES) { + return RenderbufferFormat.RGBA16F; + } + return RenderbufferFormat.RGBA8; + }, }; export default Object.freeze(RenderbufferFormat); From 0fa481149dddda7097ed09f294133edb93d18fab Mon Sep 17 00:00:00 2001 From: Eli Bogomolny Date: Thu, 3 Feb 2022 15:47:19 -0500 Subject: [PATCH 23/32] Add msaaSamples to constructor options, clamp value to GL_MAX_SAMPLES --- Source/Renderer/Context.js | 16 +++++++++ Source/Renderer/ContextLimits.js | 12 +++++++ Source/Renderer/FramebufferManager.js | 2 +- Source/Scene/Scene.js | 39 +++++++++++++++++---- Source/Widgets/CesiumWidget/CesiumWidget.js | 2 ++ Source/Widgets/Viewer/Viewer.js | 2 ++ Specs/Renderer/FramebufferManagerSpec.js | 16 +++++++++ 7 files changed, 81 insertions(+), 8 deletions(-) diff --git a/Source/Renderer/Context.js b/Source/Renderer/Context.js index 4d57ac6c39ee..37a4a58e3baf 100644 --- a/Source/Renderer/Context.js +++ b/Source/Renderer/Context.js @@ -256,6 +256,10 @@ function Context(canvas, options) { gl.MAX_VERTEX_UNIFORM_VECTORS ); // min: 128 + ContextLimits._maximumSamples = gl.getParameter( + gl.MAX_SAMPLES + ); + const aliasedLineWidthRange = gl.getParameter(gl.ALIASED_LINE_WIDTH_RANGE); // must include 1 ContextLimits._minimumAliasedLineWidth = aliasedLineWidthRange[0]; ContextLimits._maximumAliasedLineWidth = aliasedLineWidthRange[1]; @@ -598,6 +602,18 @@ Object.defineProperties(Context.prototype, { }, }, + /** + * true if the WebGL context supports multisample antialiasing. Requires + * WebGL2. + * @memberof Context.prototype + * @type {Boolean} + */ + msaa: { + get: function () { + return this._webgl2; + }, + }, + /** * true if the OES_standard_derivatives extension is supported. This * extension provides access to dFdx, dFdy, and fwidth diff --git a/Source/Renderer/ContextLimits.js b/Source/Renderer/ContextLimits.js index afbcde605b5a..287f44e494b2 100644 --- a/Source/Renderer/ContextLimits.js +++ b/Source/Renderer/ContextLimits.js @@ -21,6 +21,7 @@ const ContextLimits = { _maximumTextureFilterAnisotropy: 0, _maximumDrawBuffers: 0, _maximumColorAttachments: 0, + _maximumSamples: 0, _highpFloatSupported: false, _highpIntSupported: false, }; @@ -260,6 +261,17 @@ Object.defineProperties(ContextLimits, { }, }, + /** + * The maximum number of samples supported for multisampling. + * @memberof ContextLimits + * @type {Number} + */ + maximumSamples: { + get: function () { + return ContextLimits._maximumSamples; + }, + }, + /** * High precision float supported (highp) in fragment shaders. * @memberof ContextLimits diff --git a/Source/Renderer/FramebufferManager.js b/Source/Renderer/FramebufferManager.js index 38efac962c5f..e86255792b2a 100644 --- a/Source/Renderer/FramebufferManager.js +++ b/Source/Renderer/FramebufferManager.js @@ -152,7 +152,7 @@ FramebufferManager.prototype.update = function ( throw new DeveloperError("width and height must be defined."); } //>>includeEnd('debug'); - numSamples = defaultValue(numSamples, 1); + numSamples = context.msaa ? defaultValue(numSamples, 1) : 1; pixelDatatype = defaultValue( pixelDatatype, this._color diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js index a24aa953474c..b8e7d6adbfaa 100644 --- a/Source/Scene/Scene.js +++ b/Source/Scene/Scene.js @@ -134,6 +134,7 @@ const requestRenderAfterFrame = function (scene) { * @param {MapMode2D} [options.mapMode2D=MapMode2D.INFINITE_SCROLL] Determines if the 2D map is rotatable or can be scrolled infinitely in the horizontal direction. * @param {Boolean} [options.requestRenderMode=false] If true, rendering a frame will only occur when needed as determined by changes within the scene. Enabling improves performance of the application, but requires using {@link Scene#requestRender} to render a new frame explicitly in this mode. This will be necessary in many cases after making changes to the scene in other parts of the API. See {@link https://cesium.com/blog/2018/01/24/cesium-scene-rendering-performance/|Improving Performance with Explicit Rendering}. * @param {Number} [options.maximumRenderTimeChange=0.0] If requestRenderMode is true, this value defines the maximum change in simulation time allowed before a render is requested. See {@link https://cesium.com/blog/2018/01/24/cesium-scene-rendering-performance/|Improving Performance with Explicit Rendering}. + * @param {Number} [options.msaaSamples=1] If provided, this value controls the rate of multisample antialiasing. Typical multisampling rates are 2, 4, and sometimes 8 samples per pixel. Higher sampling rates of MSAA may impact performance in exchange for improved visual quality. This value only applies to WebGL2 contexts that support multisample render targets. * * @see CesiumWidget * @see {@link http://www.khronos.org/registry/webgl/specs/latest/#5.2|WebGLContextAttributes} @@ -260,6 +261,8 @@ function Scene(options) { this._minimumDisableDepthTestDistance = 0.0; this._debugInspector = new DebugInspector(); + this._msaaSamples = defaultValue(options.msaaSamples, 1); + /** * Exceptions occurring in render are always caught in order to raise the * renderError event. If this property is true, the error is rethrown @@ -589,13 +592,6 @@ function Scene(options) { */ this.eyeSeparation = undefined; - /** - * The number of samples for MSAA (MSAA is disabled when msaaSamples = 1). - * @type {Number} - * @default 1 - */ - this.msaaSamples = 1; - /** * Post processing effects applied to the final render. * @type {PostProcessStageCollection} @@ -1600,6 +1596,35 @@ Object.defineProperties(Scene.prototype, { }, }, + /** + * Whether or not the camera is underneath the globe. + * @memberof Scene.prototype + * @type {Boolean} + * @readonly + * @default false + */ + msaaSamples: { + get: function () { + return this._msaaSamples; + }, + set: function (value) { + value = Math.min(value, ContextLimits.maximumSamples); + this._msaaSamples = value; + }, + }, + + /** + * Returns true if the Scene's context supports MSAA. + * @memberof Scene.prototype + * @type {Boolean} + * @readonly + */ + msaaSupported: { + get: function () { + return this._context.msaa; + }, + }, + /** * Ratio between a pixel and a density-independent pixel. Provides a standard unit of * measure for real pixel measurements appropriate to a particular device. diff --git a/Source/Widgets/CesiumWidget/CesiumWidget.js b/Source/Widgets/CesiumWidget/CesiumWidget.js index 73089a696040..343814e13b4c 100644 --- a/Source/Widgets/CesiumWidget/CesiumWidget.js +++ b/Source/Widgets/CesiumWidget/CesiumWidget.js @@ -148,6 +148,7 @@ function configureCameraFrustum(widget) { * @param {MapMode2D} [options.mapMode2D=MapMode2D.INFINITE_SCROLL] Determines if the 2D map is rotatable or can be scrolled infinitely in the horizontal direction. * @param {Boolean} [options.requestRenderMode=false] If true, rendering a frame will only occur when needed as determined by changes within the scene. Enabling improves performance of the application, but requires using {@link Scene#requestRender} to render a new frame explicitly in this mode. This will be necessary in many cases after making changes to the scene in other parts of the API. See {@link https://cesium.com/blog/2018/01/24/cesium-scene-rendering-performance/|Improving Performance with Explicit Rendering}. * @param {Number} [options.maximumRenderTimeChange=0.0] If requestRenderMode is true, this value defines the maximum change in simulation time allowed before a render is requested. See {@link https://cesium.com/blog/2018/01/24/cesium-scene-rendering-performance/|Improving Performance with Explicit Rendering}. + * @param {Number} [options.msaaSamples=1] If provided, this value controls the rate of multisample antialiasing. Typical multisampling rates are 2, 4, and sometimes 8 samples per pixel. Higher sampling rates of MSAA may impact performance in exchange for improved visual quality. This value only applies to WebGL2 contexts that support multisample render targets. * * @exception {DeveloperError} Element with id "container" does not exist in the document. * @@ -274,6 +275,7 @@ function CesiumWidget(container, options) { mapMode2D: options.mapMode2D, requestRenderMode: options.requestRenderMode, maximumRenderTimeChange: options.maximumRenderTimeChange, + msaaSamples: options.msaaSamples, }); this._scene = scene; diff --git a/Source/Widgets/Viewer/Viewer.js b/Source/Widgets/Viewer/Viewer.js index 4f602739356d..fd2c163b6e98 100644 --- a/Source/Widgets/Viewer/Viewer.js +++ b/Source/Widgets/Viewer/Viewer.js @@ -340,6 +340,7 @@ function enableVRUI(viewer, enabled) { * @property {Boolean} [projectionPicker=false] If set to true, the ProjectionPicker widget will be created. * @property {Boolean} [requestRenderMode=false] If true, rendering a frame will only occur when needed as determined by changes within the scene. Enabling reduces the CPU/GPU usage of your application and uses less battery on mobile, but requires using {@link Scene#requestRender} to render a new frame explicitly in this mode. This will be necessary in many cases after making changes to the scene in other parts of the API. See {@link https://cesium.com/blog/2018/01/24/cesium-scene-rendering-performance/|Improving Performance with Explicit Rendering}. * @property {Number} [maximumRenderTimeChange=0.0] If requestRenderMode is true, this value defines the maximum change in simulation time allowed before a render is requested. See {@link https://cesium.com/blog/2018/01/24/cesium-scene-rendering-performance/|Improving Performance with Explicit Rendering}. + * @property {Number} [msaaSamples=1] If provided, this value controls the rate of multisample antialiasing. Typical multisampling rates are 2, 4, and sometimes 8 samples per pixel. Higher sampling rates of MSAA may impact performance in exchange for improved visual quality. This value only applies to WebGL2 contexts that support multisample render targets. */ /** @@ -504,6 +505,7 @@ Either specify options.terrainProvider instead or set options.baseLayerPicker to mapMode2D: options.mapMode2D, requestRenderMode: options.requestRenderMode, maximumRenderTimeChange: options.maximumRenderTimeChange, + msaaSamples: options.msaaSamples, }); let dataSourceCollection = options.dataSources; diff --git a/Specs/Renderer/FramebufferManagerSpec.js b/Specs/Renderer/FramebufferManagerSpec.js index 0cd895ffe241..b8c0967d358d 100644 --- a/Specs/Renderer/FramebufferManagerSpec.js +++ b/Specs/Renderer/FramebufferManagerSpec.js @@ -310,6 +310,22 @@ describe( context._webgl2 = webgl2; }); + it("doesn't create multisample resources if msaa is unsupported", function () { + fbm = new FramebufferManager({ + depth: true, + supportsDepthTexture: true, + }); + // Disable extensions + const webgl2 = context._webgl2; + context._webgl2 = false; + + fbm.update(context, 1, 1, 4); + expect(fbm._multisampleFramebuffer).toBeUndefined(); + + // Re-enable extensions + context._webgl2 = webgl2; + }); + it("destroys attachments and framebuffer", function () { if (!context.drawBuffers) { return; From 67cdee4c2fde165cce05370bb1c6411a06f9ba80 Mon Sep 17 00:00:00 2001 From: Eli Bogomolny Date: Thu, 3 Feb 2022 16:27:51 -0500 Subject: [PATCH 24/32] Prettier, update getWebGLStub for GL_MAX_SAMPLES --- Source/Renderer/Context.js | 6 ++---- Source/Scene/Scene.js | 4 ++-- Specs/getWebGLStub.js | 1 + 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/Source/Renderer/Context.js b/Source/Renderer/Context.js index 37a4a58e3baf..e186e7e0a304 100644 --- a/Source/Renderer/Context.js +++ b/Source/Renderer/Context.js @@ -256,9 +256,7 @@ function Context(canvas, options) { gl.MAX_VERTEX_UNIFORM_VECTORS ); // min: 128 - ContextLimits._maximumSamples = gl.getParameter( - gl.MAX_SAMPLES - ); + ContextLimits._maximumSamples = gl.getParameter(gl.MAX_SAMPLES); const aliasedLineWidthRange = gl.getParameter(gl.ALIASED_LINE_WIDTH_RANGE); // must include 1 ContextLimits._minimumAliasedLineWidth = aliasedLineWidthRange[0]; @@ -608,7 +606,7 @@ Object.defineProperties(Context.prototype, { * @memberof Context.prototype * @type {Boolean} */ - msaa: { + msaa: { get: function () { return this._webgl2; }, diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js index b8e7d6adbfaa..3998a30c031f 100644 --- a/Source/Scene/Scene.js +++ b/Source/Scene/Scene.js @@ -1603,7 +1603,7 @@ Object.defineProperties(Scene.prototype, { * @readonly * @default false */ - msaaSamples: { + msaaSamples: { get: function () { return this._msaaSamples; }, @@ -1619,7 +1619,7 @@ Object.defineProperties(Scene.prototype, { * @type {Boolean} * @readonly */ - msaaSupported: { + msaaSupported: { get: function () { return this._context.msaa; }, diff --git a/Specs/getWebGLStub.js b/Specs/getWebGLStub.js index 477b0335ab38..b5ee486a5616 100644 --- a/Specs/getWebGLStub.js +++ b/Specs/getWebGLStub.js @@ -222,6 +222,7 @@ function getParameterStub(options) { parameterStubValues[WebGLConstants.MAX_TEXTURE_MAX_ANISOTROPY_EXT] = 16; // Assuming extension parameterStubValues[WebGLConstants.MAX_DRAW_BUFFERS] = 8; // Assuming extension parameterStubValues[WebGLConstants.MAX_COLOR_ATTACHMENTS] = 8; // Assuming extension + parameterStubValues[WebGLConstants.MAX_SAMPLES] = 8; // Assuming WebGL2 return function (pname) { const value = parameterStubValues[pname]; From fd84fd958444bce3c8ba318bfaa8ba33195f6e27 Mon Sep 17 00:00:00 2001 From: Eli Bogomolny Date: Thu, 3 Feb 2022 17:41:54 -0500 Subject: [PATCH 25/32] Update CHANGES.md and add Sandcastle example --- Apps/Sandcastle/gallery/MSAA.html | 173 ++++++++++++++++++++++++++++++ Apps/Sandcastle/gallery/MSAA.jpg | Bin 0 -> 34466 bytes CHANGES.md | 1 + 3 files changed, 174 insertions(+) create mode 100644 Apps/Sandcastle/gallery/MSAA.html create mode 100644 Apps/Sandcastle/gallery/MSAA.jpg diff --git a/Apps/Sandcastle/gallery/MSAA.html b/Apps/Sandcastle/gallery/MSAA.html new file mode 100644 index 000000000000..73765c409da9 --- /dev/null +++ b/Apps/Sandcastle/gallery/MSAA.html @@ -0,0 +1,173 @@ + + + + + + + + + Cesium Demo + + + + + + +

+

Loading...

+
+ + + diff --git a/Apps/Sandcastle/gallery/MSAA.jpg b/Apps/Sandcastle/gallery/MSAA.jpg new file mode 100644 index 0000000000000000000000000000000000000000..4d238f127b3a00cfd357b79d0f6675d1affd0d19 GIT binary patch literal 34466 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 S0EJH^dm8;}zw^Jnpa0oti&n(| literal 0 HcmV?d00001 diff --git a/CHANGES.md b/CHANGES.md index 2104ee93b957..4e173c427510 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -4,6 +4,7 @@ ##### Additions :tada: +- Added MSAA support for WebGL2. Enabled on viewer creation with the multisampling rate as the `msaaSamples` option and can be controlled through `Scene.msaaSamples`. - glTF contents now use `ModelExperimental` by default. [#10055](https://github.com/CesiumGS/cesium/pull/10055) - Added the ability to toggle back-face culling in `ModelExperimental`. [#10070](https://github.com/CesiumGS/cesium/pull/10070) - Added `depthPlaneEllipsoidOffset` to Viewer and Scene constructors to address rendering artefacts below ellipsoid zero elevation. [#9200](https://github.com/CesiumGS/cesium/pull/9200) From 4c2d6f6a0b3c6181d302e67bd82a0200a6140b47 Mon Sep 17 00:00:00 2001 From: Eli Bogomolny Date: Thu, 3 Feb 2022 17:43:05 -0500 Subject: [PATCH 26/32] Sandcastle prettier --- Apps/Sandcastle/gallery/MSAA.html | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/Apps/Sandcastle/gallery/MSAA.html b/Apps/Sandcastle/gallery/MSAA.html index 73765c409da9..0ad769e2f337 100644 --- a/Apps/Sandcastle/gallery/MSAA.html +++ b/Apps/Sandcastle/gallery/MSAA.html @@ -110,14 +110,18 @@ }) ); viewer.camera.setView({ - destination: new Cesium.Cartesian3(1234138.7804841248, -5086063.633843134, 3633284.606361642), + destination: new Cesium.Cartesian3( + 1234138.7804841248, + -5086063.633843134, + 3633284.606361642 + ), orientation: { heading: 0.4304630387656614, pitch: -0.16969316864215878, - roll: 6.283184816241989 + roll: 6.283184816241989, }, }); - } + }, }, { text: "Hot Air Balloon", @@ -137,25 +141,25 @@ text: "MSAA off", onselect: function () { scene.msaaSamples = 1; - } + }, }, { text: "MSAA 2x", onselect: function () { scene.msaaSamples = 2; - } + }, }, { text: "MSAA 4x", onselect: function () { scene.msaaSamples = 4; - } + }, }, { text: "MSAA 8x", onselect: function () { scene.msaaSamples = 8; - } + }, }, ]; From 496b1c1a985152e46b235f47966eee1f32b56083 Mon Sep 17 00:00:00 2001 From: Eli Bogomolny Date: Fri, 4 Feb 2022 15:02:27 -0500 Subject: [PATCH 27/32] Remove blit before pickDepth update --- Source/Scene/Scene.js | 1 - 1 file changed, 1 deletion(-) diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js index 406a39bdc538..ef73618ff9c8 100644 --- a/Source/Scene/Scene.js +++ b/Source/Scene/Scene.js @@ -2663,7 +2663,6 @@ function executeCommands(scene, passState) { renderTranslucentDepthForPick) ) { // PERFORMANCE_IDEA: Use MRT to avoid the extra copy. - globeDepth.prepareColorTextures(context); const depthStencilTexture = globeDepth.depthStencilTexture; const pickDepth = scene._picking.getPickDepth(scene, index); pickDepth.update(context, depthStencilTexture); From a2fa0e44b9424b52b0d8fb6421efc427acb85d66 Mon Sep 17 00:00:00 2001 From: Eli Bogomolny Date: Thu, 17 Feb 2022 15:35:21 -0500 Subject: [PATCH 28/32] Move MSAA Sandcastle to Showcases, change balloon view, fix MSAA getter docs --- Apps/Sandcastle/gallery/MSAA.html | 10 ++++++++-- Source/Scene/Scene.js | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/Apps/Sandcastle/gallery/MSAA.html b/Apps/Sandcastle/gallery/MSAA.html index 0ad769e2f337..c62dfd959f19 100644 --- a/Apps/Sandcastle/gallery/MSAA.html +++ b/Apps/Sandcastle/gallery/MSAA.html @@ -8,7 +8,7 @@ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Cesium Demo