diff --git a/Apps/Sandcastle/gallery/MSAA.html b/Apps/Sandcastle/gallery/MSAA.html new file mode 100644 index 000000000000..c62dfd959f19 --- /dev/null +++ b/Apps/Sandcastle/gallery/MSAA.html @@ -0,0 +1,183 @@ + + +
+ + + + + +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/Framebuffer.js b/Source/Renderer/Framebuffer.js
index 7bd6f90ee62d..61a1ef71fcf6 100644
--- a/Source/Renderer/Framebuffer.js
+++ b/Source/Renderer/Framebuffer.js
@@ -33,7 +33,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.
@@ -362,6 +362,16 @@ Framebuffer.prototype._unBind = function () {
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
};
+Framebuffer.prototype.bindDraw = function () {
+ const gl = this._gl;
+ gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, this._framebuffer);
+};
+
+Framebuffer.prototype.bindRead = function () {
+ const 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 72da83afc854..83d4a5d2b48f 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";
@@ -13,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.
@@ -31,6 +33,7 @@ import PixelFormat from "../Core/PixelFormat.js";
*/
function FramebufferManager(options) {
options = defaultValue(options, defaultValue.EMPTY_OBJECT);
+ this._numSamples = defaultValue(options.numSamples, 1);
this._colorAttachmentsLength = defaultValue(
options.colorAttachmentsLength,
1
@@ -72,10 +75,13 @@ function FramebufferManager(options) {
this._height = undefined;
this._framebuffer = undefined;
+ this._multisampleFramebuffer = undefined;
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,12 +93,20 @@ function FramebufferManager(options) {
Object.defineProperties(FramebufferManager.prototype, {
framebuffer: {
get: function () {
+ if (this._numSamples > 1) {
+ return this._multisampleFramebuffer.getRenderFramebuffer();
+ }
return this._framebuffer;
},
},
+ numSamples: {
+ get: function () {
+ return this._numSamples;
+ },
+ },
status: {
get: function () {
- return this._framebuffer.status;
+ return this.framebuffer.status;
},
},
});
@@ -100,19 +114,27 @@ Object.defineProperties(FramebufferManager.prototype, {
FramebufferManager.prototype.isDirty = function (
width,
height,
+ numSamples,
pixelDatatype,
pixelFormat
) {
+ numSamples = defaultValue(numSamples, 1);
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);
+ const 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]))
);
};
@@ -121,6 +143,7 @@ FramebufferManager.prototype.update = function (
context,
width,
height,
+ numSamples,
pixelDatatype,
pixelFormat
) {
@@ -129,6 +152,7 @@ FramebufferManager.prototype.update = function (
throw new DeveloperError("width and height must be defined.");
}
//>>includeEnd('debug');
+ numSamples = context.msaa ? defaultValue(numSamples, 1) : 1;
pixelDatatype = defaultValue(
pixelDatatype,
this._color
@@ -140,10 +164,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 +184,16 @@ FramebufferManager.prototype.update = function (
pixelDatatype: pixelDatatype,
sampler: Sampler.NEAREST,
});
+ if (this._numSamples > 1) {
+ const format = RenderbufferFormat.getColorFormat(pixelDatatype);
+ this._colorRenderbuffers[i] = new Renderbuffer({
+ context: context,
+ width: width,
+ height: height,
+ format: format,
+ numSamples: this._numSamples,
+ });
+ }
}
}
@@ -173,6 +208,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,
@@ -204,15 +248,28 @@ 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,
+ width: this._width,
+ height: this._height,
+ 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 +303,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,47 +402,94 @@ FramebufferManager.prototype.setDepthStencilTexture = function (texture) {
this._depthStencilTexture = texture;
};
+FramebufferManager.prototype.prepareTextures = function (context, blitStencil) {
+ if (this._numSamples > 1) {
+ this._multisampleFramebuffer.blitFramebuffers(context, blitStencil);
+ }
+};
+
FramebufferManager.prototype.clear = function (
context,
clearCommand,
passState
) {
const framebuffer = clearCommand.framebuffer;
-
- clearCommand.framebuffer = this._framebuffer;
+ 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) {
+ if (this._color) {
+ let i;
const length = this._colorTextures.length;
- for (let i = 0; i < length; ++i) {
+ for (i = 0; i < length; ++i) {
const 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;
}
+ const renderbuffer = this._colorRenderbuffers[i];
+ 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/MultisampleFramebuffer.js b/Source/Renderer/MultisampleFramebuffer.js
new file mode 100644
index 000000000000..9327cc49cc5e
--- /dev/null
+++ b/Source/Renderer/MultisampleFramebuffer.js
@@ -0,0 +1,115 @@
+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 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. 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
+ */
+function MultisampleFramebuffer(options) {
+ options = defaultValue(options, defaultValue.EMPTY_OBJECT);
+
+ const context = options.context;
+ const width = options.width;
+ const height = options.height;
+ //>>includeStart('debug', pragmas.debug);
+ Check.defined("options.context", context);
+ 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)) {
+ throw new DeveloperError(
+ "Both color renderbuffer and texture attachments must be provided."
+ );
+ }
+
+ const depthStencilRenderbuffer = options.depthStencilRenderbuffer;
+ const depthStencilTexture = options.depthStencilTexture;
+ if (defined(depthStencilRenderbuffer) !== defined(depthStencilTexture)) {
+ throw new DeveloperError(
+ "Both depth-stencil renderbuffer and texture attachments 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.getRenderFramebuffer = function () {
+ return this._renderFramebuffer;
+};
+
+MultisampleFramebuffer.prototype.getColorFramebuffer = function () {
+ return this._colorFramebuffer;
+};
+
+MultisampleFramebuffer.prototype.blitFramebuffers = function (
+ context,
+ blitStencil
+) {
+ this._renderFramebuffer.bindRead();
+ this._colorFramebuffer.bindDraw();
+ const gl = context._gl;
+ let mask = 0;
+ if (this._colorFramebuffer._colorTextures.length > 0) {
+ mask |= gl.COLOR_BUFFER_BIT;
+ }
+ if (defined(this._colorFramebuffer.depthStencilTexture)) {
+ mask |= gl.DEPTH_BUFFER_BIT | (blitStencil ? gl.STENCIL_BUFFER_BIT : 0);
+ }
+ gl.blitFramebuffer(
+ 0,
+ 0,
+ this._width,
+ this._height,
+ 0,
+ 0,
+ this._width,
+ this._height,
+ mask,
+ gl.NEAREST
+ );
+ gl.bindFramebuffer(gl.READ_FRAMEBUFFER, null);
+ gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, null);
+};
+
+MultisampleFramebuffer.prototype.isDestroyed = function () {
+ return false;
+};
+
+MultisampleFramebuffer.prototype.destroy = function () {
+ this._renderFramebuffer.destroy();
+ this._colorFramebuffer.destroy();
+ return destroyObject(this);
+};
+
+export default MultisampleFramebuffer;
diff --git a/Source/Renderer/Renderbuffer.js b/Source/Renderer/Renderbuffer.js
index 08102eebdbf0..ab875c2878db 100644
--- a/Source/Renderer/Renderbuffer.js
+++ b/Source/Renderer/Renderbuffer.js
@@ -25,6 +25,7 @@ function Renderbuffer(options) {
const height = defined(options.height)
? options.height
: gl.drawingBufferHeight;
+ const numSamples = defaultValue(options.numSamples, 1);
//>>includeStart('debug', pragmas.debug);
if (!RenderbufferFormat.validate(format)) {
@@ -55,7 +56,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,
+ width,
+ 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 6e3f4e4442e8..d3874f6249d7 100644
--- a/Source/Renderer/RenderbufferFormat.js
+++ b/Source/Renderer/RenderbufferFormat.js
@@ -5,21 +5,38 @@ 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,
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.RGBA16F ||
+ renderbufferFormat === RenderbufferFormat.RGBA32F ||
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
);
},
+
+ 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);
diff --git a/Source/Scene/AutoExposure.js b/Source/Scene/AutoExposure.js
index c42d134190ed..3a3f71e39ba8 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);
}
const 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 44d8ca322e74..562ec8ad0ccc 100644
--- a/Source/Scene/GlobeDepth.js
+++ b/Source/Scene/GlobeDepth.js
@@ -17,9 +17,15 @@ import StencilOperation from "./StencilOperation.js";
* @private
*/
function GlobeDepth() {
+ this._picking = false;
+ this._numSamples = 1;
this._tempCopyDepthTexture = undefined;
- this._colorFramebuffer = new FramebufferManager({
+ this._pickColorFramebuffer = new FramebufferManager({
+ depthStencil: true,
+ supportsDepthTexture: true,
+ });
+ this._outputFramebuffer = new FramebufferManager({
depthStencil: true,
supportsDepthTexture: true,
});
@@ -50,15 +56,36 @@ function GlobeDepth() {
}
Object.defineProperties(GlobeDepth.prototype, {
+ colorFramebufferManager: {
+ get: function () {
+ return this._picking
+ ? this._pickColorFramebuffer
+ : this._outputFramebuffer;
+ },
+ },
framebuffer: {
get: function () {
- return this._colorFramebuffer.framebuffer;
+ return this.colorFramebufferManager.framebuffer;
+ },
+ },
+ depthStencilTexture: {
+ get: function () {
+ return this.colorFramebufferManager.getDepthStencilTexture();
+ },
+ },
+ picking: {
+ get: function () {
+ return this._picking;
+ },
+ set: function (value) {
+ this._picking = value;
},
},
});
function destroyFramebuffers(globeDepth) {
- globeDepth._colorFramebuffer.destroy();
+ globeDepth._pickColorFramebuffer.destroy();
+ globeDepth._outputFramebuffer.destroy();
globeDepth._copyDepthFramebuffer.destroy();
globeDepth._tempCopyDepthFramebuffer.destroy();
globeDepth._updateDepthFramebuffer.destroy();
@@ -134,7 +161,7 @@ function updateCopyCommands(globeDepth, context, width, height, passState) {
{
uniformMap: {
u_depthTexture: function () {
- return globeDepth._colorFramebuffer.getDepthStencilTexture();
+ return globeDepth.colorFramebufferManager.getDepthStencilTexture();
},
},
owner: globeDepth,
@@ -152,7 +179,7 @@ function updateCopyCommands(globeDepth, context, width, height, passState) {
{
uniformMap: {
colorTexture: function () {
- return globeDepth._colorFramebuffer.getColorTexture();
+ return globeDepth.colorFramebufferManager.getColorTexture();
},
},
owner: globeDepth,
@@ -213,6 +240,7 @@ GlobeDepth.prototype.update = function (
context,
passState,
viewport,
+ numSamples,
hdr,
clearGlobeDepth
) {
@@ -224,7 +252,18 @@ GlobeDepth.prototype.update = function (
? PixelDatatype.HALF_FLOAT
: PixelDatatype.FLOAT
: PixelDatatype.UNSIGNED_BYTE;
- this._colorFramebuffer.update(context, width, height, pixelDatatype);
+ this._numSamples = numSamples;
+ if (this.picking) {
+ this._pickColorFramebuffer.update(context, width, height);
+ } else {
+ this._outputFramebuffer.update(
+ context,
+ width,
+ height,
+ numSamples,
+ pixelDatatype
+ );
+ }
this._copyDepthFramebuffer.update(context, width, height);
updateCopyCommands(this, context, width, height, passState);
context.uniformState.globeDepthTexture = undefined;
@@ -233,8 +272,15 @@ GlobeDepth.prototype.update = function (
this._clearGlobeDepth = clearGlobeDepth;
};
+GlobeDepth.prototype.prepareColorTextures = function (context, blitStencil) {
+ if (!this.picking && this._numSamples > 1) {
+ this._outputFramebuffer.prepareTextures(context, blitStencil);
+ }
+};
+
GlobeDepth.prototype.executeCopyDepth = function (context, passState) {
if (defined(this._copyDepthCommand)) {
+ this.prepareColorTextures(context);
this._copyDepthCommand.execute(context, passState);
context.uniformState.globeDepthTexture = this._copyDepthFramebuffer.getColorTexture();
}
@@ -243,12 +289,15 @@ GlobeDepth.prototype.executeCopyDepth = function (context, passState) {
GlobeDepth.prototype.executeUpdateDepth = function (
context,
passState,
- clearGlobeDepth
+ clearGlobeDepth,
+ depthTexture
) {
- const depthTextureToCopy = passState.framebuffer.depthStencilTexture;
+ const depthTextureToCopy = defined(depthTexture)
+ ? depthTexture
+ : 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.
@@ -268,11 +317,8 @@ GlobeDepth.prototype.executeUpdateDepth = function (
this._tempCopyDepthFramebuffer.update(context, width, height);
const colorTexture = this._copyDepthFramebuffer.getColorTexture();
- const 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);
@@ -300,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/GlobeTranslucencyFramebuffer.js b/Source/Scene/GlobeTranslucencyFramebuffer.js
index abd953cd702b..7aea7f86d402 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/InvertClassification.js b/Source/Scene/InvertClassification.js
index d54897dda064..3fead5009c91 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";
@@ -18,14 +20,18 @@ import StencilOperation from "./StencilOperation.js";
* @private
*/
function InvertClassification() {
+ this._numSamples = 1;
this.previousFramebuffer = undefined;
this._previousFramebuffer = undefined;
this._depthStencilTexture = undefined;
+ this._depthStencilRenderbuffer = undefined;
this._fbo = new FramebufferManager({
+ depthStencil: true,
createDepthAttachments: false,
});
this._fboClassified = new FramebufferManager({
+ depthStencil: true,
createDepthAttachments: false,
});
@@ -167,20 +173,29 @@ const opaqueFS =
"#endif\n" +
"}\n";
-InvertClassification.prototype.update = function (context) {
+InvertClassification.prototype.update = function (
+ context,
+ numSamples,
+ globeFramebuffer
+) {
const texture = this._fbo.getColorTexture();
const previousFramebufferChanged =
this.previousFramebuffer !== this._previousFramebuffer;
this._previousFramebuffer = this.previousFramebuffer;
+ const samplesChanged = this._numSamples !== numSamples;
const width = context.drawingBufferWidth;
const height = context.drawingBufferHeight;
const 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({
@@ -190,13 +205,23 @@ InvertClassification.prototype.update = function (context) {
pixelFormat: PixelFormat.DEPTH_STENCIL,
pixelDatatype: PixelDatatype.UNSIGNED_INT_24_8,
});
+ if (numSamples > 1) {
+ this._depthStencilRenderbuffer = new Renderbuffer({
+ context: context,
+ width: width,
+ height: height,
+ format: RenderbufferFormat.DEPTH24_STENCIL8,
+ numSamples: numSamples,
+ });
+ }
}
}
if (
!defined(this._fbo.framebuffer) ||
textureChanged ||
- previousFramebufferChanged
+ previousFramebufferChanged ||
+ samplesChanged
) {
this._fbo.destroy();
this._fboClassified.destroy();
@@ -204,18 +229,18 @@ InvertClassification.prototype.update = function (context) {
let depthStencilTexture;
let depthStencilRenderbuffer;
if (defined(this._previousFramebuffer)) {
- depthStencilTexture = this._previousFramebuffer.depthStencilTexture;
- depthStencilRenderbuffer = this._previousFramebuffer
- .depthStencilRenderbuffer;
+ depthStencilTexture = globeFramebuffer.getDepthStencilTexture();
+ depthStencilRenderbuffer = globeFramebuffer.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);
@@ -229,7 +254,11 @@ InvertClassification.prototype.update = function (context) {
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 &&
@@ -286,6 +315,15 @@ InvertClassification.prototype.update = function (context) {
}
};
+InvertClassification.prototype.prepareTextures = function (
+ context,
+ blitStencil
+) {
+ if (this._fbo._numSamples > 1) {
+ this._fbo.prepareTextures(context, blitStencil);
+ }
+};
+
InvertClassification.prototype.clear = function (context, passState) {
if (defined(this._previousFramebuffer)) {
this._fbo.clear(context, this._clearColorCommand, passState);
@@ -302,6 +340,7 @@ InvertClassification.prototype.executeClassified = function (
if (!defined(this._previousFramebuffer)) {
const framebuffer = passState.framebuffer;
+ this.prepareTextures(context, true);
passState.framebuffer = this._fboClassified.framebuffer;
this._translucentCommand.execute(context, passState);
@@ -326,6 +365,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 3cadb7a05c6f..819b0af44f44 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;
@@ -39,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({
@@ -201,14 +204,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();
const width = this._opaqueTexture.width;
const height = this._opaqueTexture.height;
@@ -219,11 +228,18 @@ OIT.prototype.update = function (context, passState, framebuffer, useHDR) {
accumulationTexture.width !== width ||
accumulationTexture.height !== height ||
useHDR !== this._useHDR;
- if (textureChanged) {
+ const samplesChanged = this._numSamples !== numSamples;
+
+ if (textureChanged || samplesChanged) {
+ this._numSamples = numSamples;
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;
@@ -656,7 +672,7 @@ function executeTranslucentCommandsSortedMultipass(
passState.framebuffer = oit._adjustAlphaFBO.framebuffer;
oit._adjustAlphaCommand.execute(context, passState);
- const debugFramebuffer = oit._opaqueFBO;
+ const debugFramebuffer = oit._opaqueFBO.framebuffer;
passState.framebuffer = oit._translucentFBO.framebuffer;
for (j = 0; j < length; ++j) {
@@ -747,7 +763,7 @@ function executeTranslucentCommandsSortedMRT(
passState.framebuffer = oit._adjustTranslucentFBO.framebuffer;
oit._adjustTranslucentCommand.execute(context, passState);
- const debugFramebuffer = oit._opaqueFBO;
+ const debugFramebuffer = oit._opaqueFBO.framebuffer;
passState.framebuffer = oit._translucentFBO.framebuffer;
let command;
@@ -824,7 +840,7 @@ OIT.prototype.execute = function (context, passState) {
OIT.prototype.clear = function (context, passState, clearColor) {
const framebuffer = passState.framebuffer;
- passState.framebuffer = this._opaqueFBO;
+ passState.framebuffer = this._opaqueFBO.framebuffer;
Color.clone(clearColor, this._opaqueClearCommand.color);
this._opaqueClearCommand.execute(context, passState);
diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js
index 1dbca11d453c..8a257ed62ecb 100644
--- a/Source/Scene/Scene.js
+++ b/Source/Scene/Scene.js
@@ -135,6 +135,7 @@ const requestRenderAfterFrame = function (scene) {
* @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} [depthPlaneEllipsoidOffset=0.0] Adjust the DepthPlane to address rendering artefacts below ellipsoid zero elevation.
+ * @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}
@@ -261,6 +262,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
@@ -1594,6 +1597,35 @@ Object.defineProperties(Scene.prototype, {
},
},
+ /**
+ * The sample rate of multisample antialiasing (values greater than 1 enable MSAA).
+ * @memberof Scene.prototype
+ * @type {Number}
+ * @readonly
+ * @default 1
+ */
+ 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.
@@ -2321,6 +2353,7 @@ function executeCommands(scene, passState) {
commands,
invertClassification
) {
+ view.globeDepth.prepareColorTextures(context);
view.oit.executeCommands(
scene,
executeFunction,
@@ -2451,7 +2484,15 @@ function executeCommands(scene, passState) {
if (length > 0) {
if (defined(globeDepth) && environmentState.useGlobeDepthFramebuffer) {
- globeDepth.executeUpdateDepth(context, passState, clearGlobeDepth);
+ // When clearGlobeDepth is true, executeUpdateDepth needs
+ // a globe depth texture with resolved stencil bits.
+ globeDepth.prepareColorTextures(context, clearGlobeDepth);
+ globeDepth.executeUpdateDepth(
+ context,
+ passState,
+ clearGlobeDepth,
+ globeDepth.depthStencilTexture
+ );
}
// Draw classifications. Modifies 3D Tiles color.
@@ -2512,7 +2553,13 @@ function executeCommands(scene, passState) {
}
if (defined(globeDepth) && environmentState.useGlobeDepthFramebuffer) {
- globeDepth.executeUpdateDepth(context, passState, clearGlobeDepth);
+ scene._invertClassification.prepareTextures(context);
+ globeDepth.executeUpdateDepth(
+ context,
+ passState,
+ clearGlobeDepth,
+ scene._invertClassification._fbo.getDepthStencilTexture()
+ );
}
// Set stencil
@@ -2601,7 +2648,7 @@ function executeCommands(scene, passState) {
executeCommand,
passState,
commands,
- globeDepth.framebuffer
+ globeDepth.depthStencilTexture
);
view.translucentTileClassification.executeClassificationCommands(
scene,
@@ -2618,9 +2665,7 @@ function executeCommands(scene, passState) {
renderTranslucentDepthForPick)
) {
// PERFORMANCE_IDEA: Use MRT to avoid the extra copy.
- const depthStencilTexture = renderTranslucentDepthForPick
- ? passState.framebuffer.depthStencilTexture
- : globeDepth.framebuffer.depthStencilTexture;
+ const depthStencilTexture = globeDepth.depthStencilTexture;
const pickDepth = scene._picking.getPickDepth(scene, index);
pickDepth.update(context, depthStencilTexture);
pickDepth.executeCopyDepth(context, passState);
@@ -3333,6 +3378,9 @@ 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;
// Preserve the reference to the original framebuffer.
@@ -3367,6 +3415,7 @@ function updateAndClearFramebuffers(scene, passState, clearColor) {
context,
passState,
view.viewport,
+ scene.msaaSamples,
scene._hdr,
environmentState.clearGlobeDepth
);
@@ -3378,7 +3427,13 @@ function updateAndClearFramebuffers(scene, passState, clearColor) {
const 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.colorFramebufferManager,
+ scene._hdr,
+ scene.msaaSamples
+ );
oit.clear(context, passState, clearColor);
environmentState.useOIT = oit.isSupported();
}
@@ -3393,7 +3448,12 @@ function updateAndClearFramebuffers(scene, passState, clearColor) {
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.msaaSamples
+ );
view.sceneFramebuffer.clear(context, passState, clearColor);
postProcess.update(context, frameState.useLogDepth, scene._hdr);
@@ -3429,7 +3489,11 @@ function updateAndClearFramebuffers(scene, passState, clearColor) {
if (defined(depthFramebuffer) || context.depthTexture) {
scene._invertClassification.previousFramebuffer = depthFramebuffer;
- scene._invertClassification.update(context);
+ scene._invertClassification.update(
+ context,
+ scene.msaaSamples,
+ view.globeDepth.colorFramebufferManager
+ );
scene._invertClassification.clear(context, passState);
if (scene.frameState.invertClassificationColor.alpha < 1.0 && useOIT) {
@@ -3464,6 +3528,9 @@ Scene.prototype.resolveFramebuffers = function (passState) {
const environmentState = this._environmentState;
const view = this._view;
const globeDepth = view.globeDepth;
+ if (defined(globeDepth)) {
+ globeDepth.prepareColorTextures(context);
+ }
const useOIT = environmentState.useOIT;
const useGlobeDepthFramebuffer = environmentState.useGlobeDepthFramebuffer;
@@ -3471,14 +3538,14 @@ Scene.prototype.resolveFramebuffers = function (passState) {
const defaultFramebuffer = environmentState.originalFramebuffer;
const globeFramebuffer = useGlobeDepthFramebuffer
- ? globeDepth.framebuffer
+ ? globeDepth.colorFramebufferManager
: undefined;
- const sceneFramebuffer = view.sceneFramebuffer.framebuffer;
+ const sceneFramebuffer = view.sceneFramebuffer._colorFramebuffer;
const idFramebuffer = view.sceneFramebuffer.idFramebuffer;
if (useOIT) {
passState.framebuffer = usePostProcess
- ? sceneFramebuffer
+ ? sceneFramebuffer.framebuffer
: defaultFramebuffer;
view.oit.execute(context, passState);
}
@@ -3492,6 +3559,7 @@ Scene.prototype.resolveFramebuffers = function (passState) {
}
if (usePostProcess) {
+ view.sceneFramebuffer.prepareColorTextures(context);
let inputFramebuffer = sceneFramebuffer;
if (useGlobeDepthFramebuffer && !useOIT) {
inputFramebuffer = globeFramebuffer;
@@ -3500,8 +3568,10 @@ Scene.prototype.resolveFramebuffers = function (passState) {
const postProcess = this.postProcessStages;
const colorTexture = inputFramebuffer.getColorTexture(0);
const idTexture = idFramebuffer.getColorTexture(0);
- const depthTexture = defaultValue(globeFramebuffer, sceneFramebuffer)
- .depthStencilTexture;
+ const depthTexture = defaultValue(
+ globeFramebuffer,
+ sceneFramebuffer
+ ).getDepthStencilTexture();
postProcess.execute(context, colorTexture, depthTexture, idTexture);
postProcess.copy(context, defaultFramebuffer);
}
diff --git a/Source/Scene/SceneFramebuffer.js b/Source/Scene/SceneFramebuffer.js
index c9f6e1156906..274c623a5e8a 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,9 +43,19 @@ Object.defineProperties(SceneFramebuffer.prototype, {
return this._idFramebuffer.framebuffer;
},
},
+ depthStencilTexture: {
+ get: function () {
+ return this._colorFramebuffer.getDepthStencilTexture();
+ },
+ },
});
-SceneFramebuffer.prototype.update = function (context, viewport, hdr) {
+SceneFramebuffer.prototype.update = function (
+ context,
+ viewport,
+ hdr,
+ numSamples
+) {
const width = viewport.width;
const height = viewport.height;
const pixelDatatype = hdr
@@ -52,7 +63,14 @@ SceneFramebuffer.prototype.update = function (context, viewport, hdr) {
? PixelDatatype.HALF_FLOAT
: PixelDatatype.FLOAT
: PixelDatatype.UNSIGNED_BYTE;
- this._colorFramebuffer.update(context, width, height, pixelDatatype);
+ this._numSamples = numSamples;
+ this._colorFramebuffer.update(
+ context,
+ width,
+ height,
+ numSamples,
+ pixelDatatype
+ );
this._idFramebuffer.update(context, width, height);
};
@@ -71,6 +89,12 @@ SceneFramebuffer.prototype.getIdFramebuffer = function () {
return this._idFramebuffer.framebuffer;
};
+SceneFramebuffer.prototype.prepareColorTextures = function (context) {
+ if (this._numSamples > 1) {
+ this._colorFramebuffer.prepareTextures(context);
+ }
+};
+
SceneFramebuffer.prototype.isDestroyed = function () {
return false;
};
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;
diff --git a/Source/Widgets/CesiumWidget/CesiumWidget.js b/Source/Widgets/CesiumWidget/CesiumWidget.js
index 58926d7e8b88..b470d38c3540 100644
--- a/Source/Widgets/CesiumWidget/CesiumWidget.js
+++ b/Source/Widgets/CesiumWidget/CesiumWidget.js
@@ -146,6 +146,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.
*
@@ -273,6 +274,7 @@ function CesiumWidget(container, options) {
requestRenderMode: options.requestRenderMode,
maximumRenderTimeChange: options.maximumRenderTimeChange,
depthPlaneEllipsoidOffset: options.depthPlaneEllipsoidOffset,
+ msaaSamples: options.msaaSamples,
});
this._scene = scene;
diff --git a/Source/Widgets/Viewer/Viewer.js b/Source/Widgets/Viewer/Viewer.js
index 88d029cba752..4f2c16ba1acf 100644
--- a/Source/Widgets/Viewer/Viewer.js
+++ b/Source/Widgets/Viewer/Viewer.js
@@ -338,6 +338,7 @@ function enableVRUI(viewer, enabled) {
* @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} [depthPlaneEllipsoidOffset=0.0] Adjust the DepthPlane to address rendering artefacts below ellipsoid zero elevation.
+ * @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.
*/
/**
@@ -503,6 +504,7 @@ Either specify options.terrainProvider instead or set options.baseLayerPicker to
requestRenderMode: options.requestRenderMode,
maximumRenderTimeChange: options.maximumRenderTimeChange,
depthPlaneEllipsoidOffset: options.depthPlaneEllipsoidOffset,
+ msaaSamples: options.msaaSamples,
});
let dataSourceCollection = options.dataSources;
diff --git a/Specs/Renderer/FramebufferManagerSpec.js b/Specs/Renderer/FramebufferManagerSpec.js
index f6017c0d7dfe..b8c0967d358d 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 () {
@@ -295,16 +295,35 @@ 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("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 () {
@@ -383,13 +402,25 @@ 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,
});
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 +430,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);
});
diff --git a/Specs/Renderer/MultisampleFramebufferSpec.js b/Specs/Renderer/MultisampleFramebufferSpec.js
new file mode 100644
index 000000000000..7f97bb26f66e
--- /dev/null
+++ b/Specs/Renderer/MultisampleFramebufferSpec.js
@@ -0,0 +1,382 @@
+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 () {
+ if (!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,
+ }),
+ ],
+ 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"
+);
diff --git a/Specs/Scene/TranslucentTileClassificationSpec.js b/Specs/Scene/TranslucentTileClassificationSpec.js
index dd12833b1669..03ba7ea19701 100644
--- a/Specs/Scene/TranslucentTileClassificationSpec.js
+++ b/Specs/Scene/TranslucentTileClassificationSpec.js
@@ -230,7 +230,7 @@ describe(
executeCommand,
passState,
translucentPrimitive.commands,
- globeDepthFramebuffer
+ undefined
);
expectResources(translucentTileClassification, false);
@@ -255,7 +255,7 @@ describe(
executeCommand,
passState,
[],
- globeDepthFramebuffer
+ globeDepthFramebuffer.depthStencilTexture
);
expectResources(translucentTileClassification, false);
@@ -265,7 +265,7 @@ describe(
executeCommand,
passState,
translucentPrimitive.commands,
- globeDepthFramebuffer
+ globeDepthFramebuffer.depthStencilTexture
);
expectResources(translucentTileClassification, true);
@@ -299,7 +299,7 @@ describe(
executeCommand,
passState,
translucentPrimitive.commands,
- globeDepthFramebuffer
+ globeDepthFramebuffer.depthStencilTexture
);
expect(translucentTileClassification.hasTranslucentDepth).toBe(true);
@@ -324,7 +324,7 @@ describe(
executeCommand,
passState,
translucentPrimitive.commands,
- globeDepthFramebuffer
+ globeDepthFramebuffer.depthStencilTexture
);
const drawClassificationFBO =
@@ -366,7 +366,7 @@ describe(
executeCommand,
passState,
translucentPrimitive.commands,
- globeDepthFramebuffer
+ globeDepthFramebuffer.depthStencilTexture
);
const accumulationFBO =
@@ -396,7 +396,7 @@ describe(
executeCommand,
passState,
translucentPrimitive.commands,
- globeDepthFramebuffer
+ globeDepthFramebuffer.depthStencilTexture
);
translucentTileClassification.executeClassificationCommands(
scene,
@@ -439,7 +439,7 @@ describe(
executeCommand,
passState,
[],
- globeDepthFramebuffer
+ globeDepthFramebuffer.depthStencilTexture
);
const preClassifyPixels = readPixels(drawClassificationFBO);
@@ -493,7 +493,7 @@ describe(
executeCommand,
passState,
translucentPrimitive.commands,
- globeDepthFramebuffer
+ globeDepthFramebuffer.depthStencilTexture
);
const frustumCommands = {
@@ -574,7 +574,7 @@ describe(
executeCommand,
passState,
translucentPrimitive.commands,
- globeDepthFramebuffer
+ globeDepthFramebuffer.depthStencilTexture
);
const frustumCommands = {
@@ -600,7 +600,7 @@ describe(
executeCommand,
passState,
translucentPrimitive.commands,
- globeDepthFramebuffer
+ globeDepthFramebuffer.depthStencilTexture
);
translucentTileClassification.executeClassificationCommands(
@@ -659,7 +659,7 @@ describe(
executeCommand,
passState,
[],
- globeDepthFramebuffer
+ globeDepthFramebuffer.depthStencilTexture
);
const frustumCommands = {
@@ -705,7 +705,7 @@ describe(
executeCommand,
passState,
translucentPrimitive.commands,
- globeDepthFramebuffer
+ globeDepthFramebuffer.depthStencilTexture
);
const drawClassificationFBO =
diff --git a/Specs/getWebGLStub.js b/Specs/getWebGLStub.js
index b11179893a37..9457b4f41ba2 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];