summaryrefslogtreecommitdiffstats
path: root/dom/canvas/test/webgl-conf/checkout/deqp/functional/gles3/es3fMultisampleTests.js
diff options
context:
space:
mode:
Diffstat (limited to 'dom/canvas/test/webgl-conf/checkout/deqp/functional/gles3/es3fMultisampleTests.js')
-rw-r--r--dom/canvas/test/webgl-conf/checkout/deqp/functional/gles3/es3fMultisampleTests.js1741
1 files changed, 1741 insertions, 0 deletions
diff --git a/dom/canvas/test/webgl-conf/checkout/deqp/functional/gles3/es3fMultisampleTests.js b/dom/canvas/test/webgl-conf/checkout/deqp/functional/gles3/es3fMultisampleTests.js
new file mode 100644
index 000000000..1babbd35d
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/deqp/functional/gles3/es3fMultisampleTests.js
@@ -0,0 +1,1741 @@
+/*-------------------------------------------------------------------------
+ * drawElements Quality Program OpenGL ES Utilities
+ * ------------------------------------------------
+ *
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+'use strict';
+goog.provide('functional.gles3.es3fMultisampleTests');
+goog.require('framework.common.tcuTestCase');
+goog.require('framework.delibs.debase.deMath');
+goog.require('framework.delibs.debase.deRandom');
+goog.require('framework.delibs.debase.deString');
+goog.require('framework.opengl.gluShaderProgram');
+goog.require('framework.opengl.gluStrUtil');
+goog.require('framework.opengl.gluTextureUtil');
+goog.require('framework.common.tcuImageCompare');
+goog.require('framework.common.tcuLogImage');
+goog.require('framework.common.tcuRGBA');
+goog.require('framework.common.tcuSurface');
+goog.require('framework.common.tcuTexture');
+goog.require('modules.shared.glsTextureTestUtil');
+
+goog.scope(function() {
+ /** @type {?WebGL2RenderingContext} */ var gl;
+ var es3fMultisampleTests = functional.gles3.es3fMultisampleTests;
+ var deMath = framework.delibs.debase.deMath;
+ var deRandom = framework.delibs.debase.deRandom;
+ var deString = framework.delibs.debase.deString;
+ var gluShaderProgram = framework.opengl.gluShaderProgram;
+ var tcuRGBA = framework.common.tcuRGBA;
+ var tcuSurface = framework.common.tcuSurface;
+ var tcuTestCase = framework.common.tcuTestCase;
+ var tcuTexture = framework.common.tcuTexture;
+ var gluStrUtil = framework.opengl.gluStrUtil;
+ var glsTextureTestUtil = modules.shared.glsTextureTestUtil;
+ var tcuImageCompare = framework.common.tcuImageCompare;
+ var gluTextureUtil = framework.opengl.gluTextureUtil;
+ var tcuLogImage = framework.common.tcuLogImage;
+
+ /**
+ * @constructor
+ * @struct
+ * @param {Array<number>} p0_
+ * @param {Array<number>} p1_
+ * @param {Array<number>} p2_
+ * @param {Array<number>} p3_
+ */
+ es3fMultisampleTests.QuadCorners = function(p0_, p1_, p2_, p3_) {
+ /** @type {Array<number>} */ this.p0 = p0_;
+ /** @type {Array<number>} */ this.p1 = p1_;
+ /** @type {Array<number>} */ this.p2 = p2_;
+ /** @type {Array<number>} */ this.p3 = p3_;
+ };
+
+ /**
+ * @param {number} defaultCount
+ * @return {number}
+ */
+ es3fMultisampleTests.getIterationCount = function(defaultCount) {
+ // The C++ test takes an argument from the command line.
+ // Leaving this function in case we want to be able to take an argument from the URL
+ return defaultCount;
+ };
+
+ /**
+ * @param {Array<number>} point
+ * @param {Array<number>} p0
+ * @param {Array<number>} p1
+ * @param {Array<number>} p2
+ * @param {Array<number>} p3
+ * @return {boolean}
+ */
+ es3fMultisampleTests.isInsideQuad = function(point, p0, p1, p2, p3) {
+ /** @type {number} */ var dot0 = (point[0] - p0[0]) * (p1[1] - p0[1]) + (point[1] - p0[1]) * (p0[0] - p1[0]);
+ /** @type {number} */ var dot1 = (point[0] - p1[0]) * (p2[1] - p1[1]) + (point[1] - p1[1]) * (p1[0] - p2[0]);
+ /** @type {number} */ var dot2 = (point[0] - p2[0]) * (p3[1] - p2[1]) + (point[1] - p2[1]) * (p2[0] - p3[0]);
+ /** @type {number} */ var dot3 = (point[0] - p3[0]) * (p0[1] - p3[1]) + (point[1] - p3[1]) * (p3[0] - p0[0]);
+
+ return (dot0 > 0) == (dot1 > 0) && (dot1 > 0) == (dot2 > 0) && (dot2 > 0) == (dot3 > 0);
+ };
+
+ /**
+ * Check if a region in an image is unicolored.
+ *
+ * Checks if the pixels in img inside the convex quadilateral defined by
+ * p0, p1, p2 and p3 are all (approximately) of the same color.
+ *
+ * @param {tcuSurface.Surface} img
+ * @param {Array<number>} p0
+ * @param {Array<number>} p1
+ * @param {Array<number>} p2
+ * @param {Array<number>} p3
+ * @return {boolean}
+ */
+ es3fMultisampleTests.isPixelRegionUnicolored = function(img, p0, p1, p2, p3) {
+ /** @type {number} */ var xMin = deMath.clamp(Math.min(p0[0], p1[0], p2[0], p3[0]), 0, img.getWidth() - 1);
+ /** @type {number} */ var yMin = deMath.clamp(Math.min(p0[1], p1[1], p2[1], p3[1]), 0, img.getHeight() - 1);
+ /** @type {number} */ var xMax = deMath.clamp(Math.max(p0[0], p1[0], p2[0], p3[0]), 0, img.getWidth() - 1);
+ /** @type {number} */ var yMax = deMath.clamp(Math.max(p0[1], p1[1], p2[1], p3[1]), 0, img.getHeight() - 1);
+ /** @type {boolean} */ var insideEncountered = false; //!< Whether we have already seen at least one pixel inside the region.
+ /** @type {tcuRGBA.RGBA} */ var insideColor; //!< Color of the first pixel inside the region.
+ /** @type {tcuRGBA.RGBA} */ var threshold = tcuRGBA.newRGBAComponents(3, 3, 3, 3);
+ for (var y = yMin; y <= yMax; y++)
+ for (var x = xMin; x <= xMax; x++)
+ if (es3fMultisampleTests.isInsideQuad([x, y], p0, p1, p2, p3)) {
+ /** @type {tcuRGBA.RGBA} */ var pixColor = new tcuRGBA.RGBA(img.getPixel(x, y));
+
+ if (insideEncountered)
+ if (!tcuRGBA.compareThreshold(pixColor, insideColor, threshold)) // Pixel color differs from already-detected color inside same region - region not unicolored.
+ return false;
+ else {
+ insideEncountered = true;
+ insideColor = pixColor;
+ }
+ }
+ return true;
+ };
+
+ /**
+ * [drawUnicolorTestErrors description]
+ * @param {tcuSurface.Surface} img
+ * @param {tcuTexture.PixelBufferAccess} errorImg
+ * @param {Array<number>} p0
+ * @param {Array<number>} p1
+ * @param {Array<number>} p2
+ * @param {Array<number>} p3
+ * @return {boolean}
+ */
+ es3fMultisampleTests.drawUnicolorTestErrors = function(img, errorImg, p0, p1, p2, p3) {
+ /** @type {number} */ var xMin = deMath.clamp(Math.min(p0[0], p1[0], p2[0], p3[0]), 0, img.getWidth() - 1);
+ /** @type {number} */ var yMin = deMath.clamp(Math.min(p0[1], p1[1], p2[1], p3[1]), 0, img.getHeight() - 1);
+ /** @type {number} */ var xMax = deMath.clamp(Math.max(p0[0], p1[0], p2[0], p3[0]), 0, img.getWidth() - 1);
+ /** @type {number} */ var yMax = deMath.clamp(Math.max(p0[1], p1[1], p2[1], p3[1]), 0, img.getHeight() - 1);
+ /** @type {tcuRGBA.RGBA} */ var refColor = new tcuRGBA.RGBA(img.getPixel(Math.floor((xMin + xMax) / 2), Math.floor((yMin + yMax) / 2)));
+ /** @type {tcuRGBA.RGBA} */ var threshold = tcuRGBA.newRGBAComponents(3, 3, 3, 3);
+ for (var y = yMin; y <= yMax; y++)
+ for (var x = xMin; x <= xMax; x++)
+ if (es3fMultisampleTests.isInsideQuad([x, y], p0, p1, p2, p3)) {
+ if (!tcuRGBA.compareThreshold(new tcuRGBA.RGBA(img.getPixel(x, y)), refColor, threshold)) {
+ img.setPixel(x, y, tcuRGBA.RGBA.red.toVec()); // TODO: this might also be toIVec()
+ errorImg.setPixel([1.0, 0.0, 0.0, 1.0], x, y);
+ }
+ }
+ return true;
+ };
+
+ /**
+ * @constructor
+ * @struct
+ * @param {number=} numSamples_
+ * @param {boolean=} useDepth_
+ * @param {boolean=} useStencil_
+ */
+ es3fMultisampleTests.FboParams = function(numSamples_, useDepth_, useStencil_) {
+ /** @type {boolean} */ var useFbo_ = true;
+ if (numSamples_ === undefined && useDepth_ === undefined && useStencil_ === undefined)
+ useFbo_ = false;
+ /** @type {boolean} */ this.useFbo = useFbo_;
+ /** @type {number} */ this.numSamples = numSamples_ === undefined ? -1 : numSamples_;
+ /** @type {boolean} */ this.useDepth = useDepth_ === undefined ? false : useDepth_;
+ /** @type {boolean} */ this.useStencil = useStencil_ === undefined ? false : useStencil_;
+
+ };
+
+ /**
+ * @constructor
+ * @extends {tcuTestCase.DeqpTest}
+ * @param {string} name
+ * @param {string} desc
+ * @param {number} desiredViewportSize
+ * @param {es3fMultisampleTests.FboParams} fboParams
+ */
+ es3fMultisampleTests.MultisampleCase = function(name, desc, desiredViewportSize, fboParams) {
+ tcuTestCase.DeqpTest.call(this, name, desc);
+ /** @type {number} */ this.m_numSamples = 0;
+ /** @type {number} */ this.m_viewportSize = 0;
+ /** @type {number} */ this.m_desiredViewportSize = desiredViewportSize;
+ /** @type {es3fMultisampleTests.FboParams} */ this.m_fboParams = fboParams;
+ /** @type {WebGLRenderbuffer} */ this.m_msColorRbo = null;
+ /** @type {WebGLRenderbuffer} */ this.m_msDepthStencilRbo = null;
+ /** @type {WebGLRenderbuffer} */ this.m_resolveColorRbo = null;
+ /** @type {WebGLFramebuffer} */ this.m_msFbo = null;
+ /** @type {WebGLFramebuffer} */ this.m_resolveFbo = null;
+ /** @type {gluShaderProgram.ShaderProgram} */ this.m_program = null;
+ /** @type {number} */ this.m_attrPositionLoc = -1;
+ /** @type {number} */ this.m_attrColorLoc = -1;
+ /** @type {number} */ this.m_renderWidth = fboParams.useFbo ? 2 * desiredViewportSize : gl.drawingBufferWidth;
+ /** @type {number} */ this.m_renderHeight = fboParams.useFbo ? 2 * desiredViewportSize : gl.drawingBufferHeight;
+ /** @type {number} */ this.m_viewportX = 0;
+ /** @type {number} */ this.m_viewportY = 0;
+ /** @type {deRandom.Random} */ this.m_rnd = new deRandom.Random(deString.deStringHash(this.name));
+ if (this.m_fboParams.useFbo)
+ assertMsgOptions(this.m_fboParams.numSamples >= 0, 'fboParams.numSamples < 0', false, true);
+ };
+
+ es3fMultisampleTests.MultisampleCase.prototype = Object.create(tcuTestCase.DeqpTest.prototype);
+
+ /** Copy the constructor */
+ es3fMultisampleTests.MultisampleCase.prototype.constructor = es3fMultisampleTests.MultisampleCase;
+
+ /* Rest states */
+ es3fMultisampleTests.MultisampleCase.prototype.deinit = function() {
+ gl.colorMask(true, true, true, true);
+ gl.depthMask(true);
+
+ gl.clearColor(0.0, 0.0, 0.0, 0.0);
+ gl.clearDepth(1.0);
+ gl.clearStencil(0);
+
+ gl.disable(gl.STENCIL_TEST);
+ gl.disable(gl.DEPTH_TEST);
+ gl.disable(gl.BLEND)
+ gl.disable(gl.SAMPLE_COVERAGE);
+ gl.disable(gl.SAMPLE_ALPHA_TO_COVERAGE);
+
+ if (this.m_program) {
+ gl.deleteProgram(this.m_program.getProgram());
+ this.m_program = null;
+ }
+ if (this.m_msColorRbo) {
+ gl.deleteRenderbuffer(this.m_msColorRbo);
+ this.m_msColorRbo = null;
+ }
+ if (this.m_msDepthStencilRbo) {
+ gl.deleteRenderbuffer(this.m_msDepthStencilRbo);
+ this.m_msDepthStencilRbo = null;
+ }
+ if (this.m_resolveColorRbo) {
+ gl.deleteRenderbuffer(this.m_resolveColorRbo);
+ this.m_resolveColorRbo = null;
+ }
+
+ if (this.m_msFbo) {
+ gl.deleteFramebuffer(this.m_msFbo);
+ this.m_msFbo = null;
+ }
+ if (this.m_resolveFbo) {
+ gl.deleteFramebuffer(this.m_resolveFbo);
+ this.m_resolveFbo = null;
+ }
+
+ gl.bindRenderbuffer(gl.RENDERBUFFER, null);
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
+ }
+
+ /**
+ * @protected
+ * @param {Array<number>} p0
+ * @param {Array<number>} p1
+ * @param {Array<number>} p2
+ * @param {Array<number>} c0
+ * @param {Array<number>} c1
+ * @param {Array<number>} c2
+ */
+ es3fMultisampleTests.MultisampleCase.prototype.renderTriangle_pAsVec3cAsVec4 = function(p0, p1, p2, c0, c1, c2) {
+ /** @type {Array<number>} */ var vertexPositions = [
+ p0[0], p0[1], p0[2], 1.0,
+ p1[0], p1[1], p1[2], 1.0,
+ p2[0], p2[1], p2[2], 1.0
+ ];
+ /** @type {Array<number>} */ var vertexColors = [
+ c0[0], c0[1], c0[2], c0[3],
+ c1[0], c1[1], c1[2], c1[3],
+ c2[0], c2[1], c2[2], c2[3]
+ ];
+
+ var posGLBuffer = gl.createBuffer();
+ /** @type {ArrayBufferView} */ var posBuffer = new Float32Array(vertexPositions);
+ gl.bindBuffer(gl.ARRAY_BUFFER, posGLBuffer);
+ gl.bufferData(gl.ARRAY_BUFFER, posBuffer, gl.STATIC_DRAW);
+
+ gl.enableVertexAttribArray(this.m_attrPositionLoc);
+ gl.vertexAttribPointer(this.m_attrPositionLoc, 4, gl.FLOAT, false, 0, 0);
+
+ var colGLBuffer = gl.createBuffer();
+ /** @type {ArrayBufferView} */ var colBuffer = new Float32Array(vertexColors);
+ gl.bindBuffer(gl.ARRAY_BUFFER, colGLBuffer);
+ gl.bufferData(gl.ARRAY_BUFFER, colBuffer, gl.STATIC_DRAW);
+
+ gl.enableVertexAttribArray(this.m_attrColorLoc);
+ gl.vertexAttribPointer(this.m_attrColorLoc, 4, gl.FLOAT, false, 0, 0);
+
+ gl.useProgram(this.m_program.getProgram());
+ gl.drawArrays(gl.TRIANGLES, 0, 3);
+
+ gl.bindBuffer(gl.ARRAY_BUFFER, null);
+ gl.deleteBuffer(colGLBuffer);
+ gl.deleteBuffer(posGLBuffer);
+ };
+
+ /**
+ * @protected
+ * @param {Array<number>} p0
+ * @param {Array<number>} p1
+ * @param {Array<number>} p2
+ * @param {Array<number>} color
+ */
+ es3fMultisampleTests.MultisampleCase.prototype.renderTriangle_pAsVec3WithColor = function(p0, p1, p2, color) {
+ this.renderTriangle_pAsVec3cAsVec4(p0, p1, p2, color, color, color);
+ };
+
+ /**
+ * @protected
+ * @param {Array<number>} p0
+ * @param {Array<number>} p1
+ * @param {Array<number>} p2
+ * @param {Array<number>} c0
+ * @param {Array<number>} c1
+ * @param {Array<number>} c2
+ */
+ es3fMultisampleTests.MultisampleCase.prototype.renderTriangle_pAsVec2 = function(p0, p1, p2, c0, c1, c2) {
+ this.renderTriangle_pAsVec3cAsVec4(
+ [p0[0], p0[1], 0.0],
+ [p1[0], p1[1], 0.0],
+ [p2[0], p2[1], 0.0],
+ c0, c1, c2);
+ };
+
+ /**
+ * @protected
+ * @param {Array<number>} p0
+ * @param {Array<number>} p1
+ * @param {Array<number>} p2
+ * @param {Array<number>} color
+ */
+ es3fMultisampleTests.MultisampleCase.prototype.renderTriangle_pAsVec2WithColor = function(p0, p1, p2, color) {
+ this.renderTriangle_pAsVec2(p0, p1, p2, color, color, color);
+ };
+
+ /**
+ * @protected
+ * @param {Array<number>} p0
+ * @param {Array<number>} p1
+ * @param {Array<number>} p2
+ * @param {Array<number>} p3
+ * @param {Array<number>} c0
+ * @param {Array<number>} c1
+ * @param {Array<number>} c2
+ * @param {Array<number>} c3
+ */
+ es3fMultisampleTests.MultisampleCase.prototype.renderQuad = function(p0, p1, p2, p3, c0, c1, c2, c3) {
+ this.renderTriangle_pAsVec2(p0, p1, p2, c0, c1, c2);
+ this.renderTriangle_pAsVec2(p2, p1, p3, c2, c1, c3);
+ };
+
+ /**
+ * @protected
+ * @param {Array<number>} p0
+ * @param {Array<number>} p1
+ * @param {Array<number>} p2
+ * @param {Array<number>} p3
+ * @param {Array<number>} color
+ */
+ es3fMultisampleTests.MultisampleCase.prototype.renderQuad_WithColor = function(p0, p1, p2, p3, color) {
+ this.renderQuad(p0, p1, p2, p3, color, color, color, color);
+ };
+
+ /**
+ * @protected
+ * @param {Array<number>} p0
+ * @param {Array<number>} p1
+ * @param {Array<number>} color
+ */
+ es3fMultisampleTests.MultisampleCase.prototype.renderLine = function(p0, p1, color) {
+ /** @type {Array<number>} */ var vertexPositions = [
+ p0[0], p0[1], 0.0, 1.0,
+ p1[0], p1[1], 0.0, 1.0
+ ];
+ /** @type {Array<number>} */ var vertexColors = [
+ color[0], color[1], color[2], color[3],
+ color[0], color[1], color[2], color[3]
+ ];
+
+ var posGLBuffer = gl.createBuffer();
+ /** @type {ArrayBufferView} */ var posBuffer = new Float32Array(vertexPositions);
+ gl.bindBuffer(gl.ARRAY_BUFFER, posGLBuffer);
+ gl.bufferData(gl.ARRAY_BUFFER, posBuffer, gl.STATIC_DRAW);
+
+ gl.enableVertexAttribArray(this.m_attrPositionLoc);
+ gl.vertexAttribPointer(this.m_attrPositionLoc, 4, gl.FLOAT, false, 0, 0);
+
+ var colGLBuffer = gl.createBuffer();
+ /** @type {ArrayBufferView} */ var colBuffer = new Float32Array(vertexColors);
+ gl.bindBuffer(gl.ARRAY_BUFFER, colGLBuffer);
+ gl.bufferData(gl.ARRAY_BUFFER, colBuffer, gl.STATIC_DRAW);
+
+ gl.enableVertexAttribArray(this.m_attrColorLoc);
+ gl.vertexAttribPointer(this.m_attrColorLoc, 4, gl.FLOAT, false, 0, 0);
+
+ gl.useProgram(this.m_program.getProgram());
+ gl.drawArrays(gl.LINES, 0, 2);
+
+ gl.bindBuffer(gl.ARRAY_BUFFER, null);
+ gl.deleteBuffer(colGLBuffer);
+ gl.deleteBuffer(posGLBuffer);
+ };
+
+ /**
+ * @protected
+ */
+ es3fMultisampleTests.MultisampleCase.prototype.randomizeViewport = function() {
+ this.m_viewportX = this.m_rnd.getInt(0, this.m_renderWidth - this.m_viewportSize);
+ this.m_viewportY = this.m_rnd.getInt(0, this.m_renderHeight - this.m_viewportSize);
+
+ gl.viewport(this.m_viewportX, this.m_viewportY, this.m_viewportSize, this.m_viewportSize);
+ };
+
+ /**
+ * @protected
+ * @return {tcuSurface.Surface}
+ */
+ es3fMultisampleTests.MultisampleCase.prototype.readImage = function() {
+ /** @type {tcuSurface.Surface} */
+ var dst = new tcuSurface.Surface(this.m_viewportSize, this.m_viewportSize);
+ /** @type {number} */ var pixelSize = dst.getAccess().getFormat().getPixelSize();
+ /** @type {number} */ var param = deMath.deIsPowerOfTwo32(pixelSize) ? Math.min(pixelSize, 8) : 1;
+ /** @type {gluTextureUtil.TransferFormat} */ var format = gluTextureUtil.getTransferFormat(dst.getAccess().getFormat());
+ /** @type {number} */ var width = dst.getAccess().getWidth();
+ /** @type {number} */ var height = dst.getAccess().getHeight();
+ if (this.m_fboParams.useFbo) {
+ gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, this.m_resolveFbo);
+ gl.blitFramebuffer(0, 0, this.m_renderWidth, this.m_renderHeight, 0, 0, this.m_renderWidth, this.m_renderHeight, gl.COLOR_BUFFER_BIT, gl.NEAREST);
+ gl.bindFramebuffer(gl.READ_FRAMEBUFFER, this.m_resolveFbo);
+
+ gl.pixelStorei(gl.PACK_ALIGNMENT, param);
+ gl.readPixels(this.m_viewportX, this.m_viewportY, width, height, format.format, format.dataType, dst.getAccess().getDataPtr());
+
+ gl.bindFramebuffer(gl.FRAMEBUFFER, this.m_msFbo);
+ }
+ else {
+ gl.pixelStorei(gl.PACK_ALIGNMENT, param);
+ gl.readPixels(this.m_viewportX, this.m_viewportY, width, height, format.format, format.dataType, dst.getAccess().getDataPtr());
+ }
+ return dst;
+ };
+
+ es3fMultisampleTests.MultisampleCase.prototype.init = function() {
+ /** @type {string} */ var vertShaderSource = '' +
+ '#version 300 es\n' +
+ 'in highp vec4 a_position;\n' +
+ 'in mediump vec4 a_color;\n' +
+ 'out mediump vec4 v_color;\n' +
+ 'void main()\n' +
+ '{\n' +
+ ' gl_Position = a_position;\n' +
+ ' v_color = a_color;\n' +
+ '}\n';
+
+ /** @type {string} */ var fragShaderSource = '' +
+ '#version 300 es\n' +
+ 'in mediump vec4 v_color;\n' +
+ 'layout(location = 0) out mediump vec4 o_color;\n' +
+ 'void main()\n' +
+ '{\n' +
+ ' o_color = v_color;\n' +
+ '}\n';
+
+ var numSamples = /** @type {number} */ (gl.getParameter(gl.SAMPLES));
+ if (!this.m_fboParams.useFbo && numSamples <= 1) {
+ var msg = 'No multisample buffers';
+ checkMessage(false, msg);
+ return false;
+ }
+
+ if (this.m_fboParams.useFbo) {
+ if (this.m_fboParams.numSamples > 0)
+ this.m_numSamples = this.m_fboParams.numSamples;
+ else {
+ bufferedLogToConsole('Querying maximum number of samples for ' + gluStrUtil.getPixelFormatName(gl.RGBA8) + ' with gl.getInternalformatParameter()');
+ var supportedSampleCountArray = /** @type {Int32Array} */ (gl.getInternalformatParameter(gl.RENDERBUFFER, gl.RGBA8, gl.SAMPLES));
+ if (supportedSampleCountArray.length == 0) {
+ var msg = 'No supported sample counts';
+ checkMessage(false, msg);
+ return false;
+ }
+ this.m_numSamples = supportedSampleCountArray[0];
+ }
+
+ bufferedLogToConsole('Using FBO of size (' + this.m_renderWidth + ', ' + this.m_renderHeight + ') with ' + this.m_numSamples + ' samples');
+ }
+ else {
+ // Query and log number of samples per pixel.
+ this.m_numSamples = numSamples;
+ bufferedLogToConsole('gl.SAMPLES =' + this.m_numSamples);
+ }
+
+ // Prepare program.
+
+ assertMsgOptions(!this.m_program, 'Program loaded when it should not be.', false, true);
+
+ this.m_program = new gluShaderProgram.ShaderProgram(
+ gl,
+ gluShaderProgram.makeVtxFragSources(vertShaderSource, fragShaderSource));
+
+ if (!this.m_program.isOk())
+ throw new Error('Failed to compile program');
+
+ this.m_attrPositionLoc = gl.getAttribLocation(this.m_program.getProgram(), 'a_position');
+ this.m_attrColorLoc = gl.getAttribLocation(this.m_program.getProgram(), 'a_color');
+
+ if (this.m_attrPositionLoc < 0 || this.m_attrColorLoc < 0) {
+ this.m_program = null;
+ throw new Error('Invalid attribute locations');
+ }
+
+ if (this.m_fboParams.useFbo) {
+ // Setup ms color RBO.
+ this.m_msColorRbo = gl.createRenderbuffer();
+ gl.bindRenderbuffer(gl.RENDERBUFFER, this.m_msColorRbo);
+
+ // If glRenderbufferStorageMultisample() fails, check if it's because of a too high sample count.
+ // \note We don't do the check until now because some implementations can't handle the gl.SAMPLES query with glGetInternalformativ(),
+ // and we don't want that to be the cause of test case failure.
+ try {
+ gl.renderbufferStorageMultisample(gl.RENDERBUFFER, this.m_numSamples, gl.RGBA8, this.m_renderWidth, this.m_renderHeight);
+ }
+ catch (e) {
+ /** @type {Int32Array} */ var supportedSampleCountArray = /** @type {Int32Array} */ (gl.getInternalformatParameter(gl.RENDERBUFFER, gl.RGBA8, gl.SAMPLES));
+ var maxSampleCount = supportedSampleCountArray[0];
+ if (maxSampleCount < this.m_numSamples)
+ throw new Error('Maximum sample count returned by gl.getInternalformatParameter() for ' + gluStrUtil.getPixelFormatName(gl.RGBA8) + ' is only ' + maxSampleCount);
+ else
+ throw new Error('Unspecified error.');
+ }
+
+ if (this.m_fboParams.useDepth || this.m_fboParams.useStencil) {
+ // Setup ms depth & stencil RBO.
+ this.m_msDepthStencilRbo = gl.createRenderbuffer();
+ gl.bindRenderbuffer(gl.RENDERBUFFER, this.m_msDepthStencilRbo);
+ gl.renderbufferStorageMultisample(gl.RENDERBUFFER, this.m_numSamples, gl.DEPTH24_STENCIL8, this.m_renderWidth, this.m_renderHeight);
+ }
+
+ // Setup ms FBO.
+ this.m_msFbo = gl.createFramebuffer();
+ gl.bindFramebuffer(gl.FRAMEBUFFER, this.m_msFbo);
+ gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, this.m_msColorRbo);
+ gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, this.m_msDepthStencilRbo);
+
+ // Setup resolve color RBO.
+ this.m_resolveColorRbo = gl.createRenderbuffer();
+ gl.bindRenderbuffer(gl.RENDERBUFFER, this.m_resolveColorRbo);
+ gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA8, this.m_renderWidth, this.m_renderHeight);
+
+ // Setup resolve FBO.
+ this.m_resolveFbo = gl.createFramebuffer();
+ gl.bindFramebuffer(gl.FRAMEBUFFER, this.m_resolveFbo);
+ gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, this.m_resolveColorRbo);
+
+ // Use ms FBO.
+ gl.bindFramebuffer(gl.FRAMEBUFFER, this.m_msFbo);
+ }
+
+ // Get suitable viewport size.
+
+ this.m_viewportSize = Math.min(this.m_desiredViewportSize, this.m_renderWidth, this.m_renderHeight);
+ this.randomizeViewport();
+ return true;
+ };
+
+ /**
+ * Base class for cases testing the value of sample count.
+ *
+ * Draws a test pattern (defined by renderPattern() of an inheriting class)
+ * and counts the number of distinct colors in the resulting image. That
+ * number should be at least the value of sample count plus one. This is
+ * repeated with increased values of m_currentIteration until this correct
+ * number of colors is detected or m_currentIteration reaches
+ * m_maxNumIterations.
+ *
+ * @extends {es3fMultisampleTests.MultisampleCase}
+ * @constructor
+ * @param {string} name
+ * @param {string} desc
+ * @param {es3fMultisampleTests.FboParams} fboParams
+ */
+ es3fMultisampleTests.NumSamplesCase = function(name, desc, fboParams) {
+ es3fMultisampleTests.MultisampleCase.call(this, name, desc, 256, fboParams);
+ /** @type {number} */ var DEFAULT_MAX_NUM_ITERATIONS = 16;
+ /** @type {number} */ this.m_currentIteration = 0;
+ /** @type {number} */ this.m_maxNumIterations = es3fMultisampleTests.getIterationCount(DEFAULT_MAX_NUM_ITERATIONS);
+ /** @type {Array<tcuRGBA.RGBA>} */ this.m_detectedColors = [];
+ };
+
+ es3fMultisampleTests.NumSamplesCase.prototype = Object.create(es3fMultisampleTests.MultisampleCase.prototype);
+
+ /** Copy the constructor */
+ es3fMultisampleTests.NumSamplesCase.prototype.constructor = es3fMultisampleTests.NumSamplesCase;
+
+ /**
+ * @return {tcuTestCase.IterateResult}
+ */
+ es3fMultisampleTests.NumSamplesCase.prototype.iterate = function() {
+ this.randomizeViewport();
+
+ gl.clearColor(0.0, 0.0, 0.0, 1.0);
+ gl.clear(gl.COLOR_BUFFER_BIT);
+
+ this.renderPattern();
+
+ // Read and log rendered image.
+
+ /** @type {tcuSurface.Surface} */ var renderedImg = this.readImage();
+ tcuLogImage.logImage('RenderedImage', 'Rendered image', renderedImg.getAccess());
+
+ // Detect new, previously unseen colors from image.
+
+ /** @type {number} */ var requiredNumDistinctColors = this.m_numSamples + 1;
+
+ for (var y = 0; y < renderedImg.getHeight() && this.m_detectedColors.length < requiredNumDistinctColors; y++)
+ for (var x = 0; x < renderedImg.getWidth() && this.m_detectedColors.length < requiredNumDistinctColors; x++) {
+ /** @type {tcuRGBA.RGBA} */ var color = new tcuRGBA.RGBA(renderedImg.getPixel(x, y));
+
+ /** @type {number} */ var i;
+ for (i = 0; i < this.m_detectedColors.length; i++) {
+ if (tcuRGBA.compareThreshold(color, this.m_detectedColors[i], tcuRGBA.newRGBAComponents(3, 3, 3, 3)))
+ break;
+ }
+
+ if (i === this.m_detectedColors.length)
+ this.m_detectedColors.push(color); // Color not previously detected.
+ }
+
+ // Log results.
+
+ bufferedLogToConsole('Number of distinct colors detected so far: ' + (this.m_detectedColors.length >= requiredNumDistinctColors ? 'at least ' : '') + this.m_detectedColors.length);
+
+
+ if (this.m_detectedColors.length < requiredNumDistinctColors) {
+ // Haven't detected enough different colors yet.
+
+ this.m_currentIteration++;
+
+ if (this.m_currentIteration >= this.m_maxNumIterations) {
+ testFailedOptions('Failure: Number of distinct colors detected is lower than sample count+1', false);
+ return tcuTestCase.IterateResult.STOP;
+ }
+ else {
+ bufferedLogToConsole('The number of distinct colors detected is lower than sample count+1 - trying again with a slightly altered pattern');
+ return tcuTestCase.IterateResult.CONTINUE;
+ }
+ }
+ else {
+ testPassedOptions('Success: The number of distinct colors detected is at least sample count+1', true);
+ return tcuTestCase.IterateResult.STOP;
+ }
+ };
+
+ /**
+ * @extends {es3fMultisampleTests.NumSamplesCase}
+ * @constructor
+ * @param {string} name
+ * @param {string} desc
+ * @param {number=} numFboSamples
+ */
+ es3fMultisampleTests.PolygonNumSamplesCase = function(name, desc, numFboSamples) {
+ numFboSamples = numFboSamples === undefined ? 0 : numFboSamples;
+ /** @type {es3fMultisampleTests.FboParams} */
+ var params = numFboSamples >= 0 ? new es3fMultisampleTests.FboParams(numFboSamples, false, false) : new es3fMultisampleTests.FboParams();
+ es3fMultisampleTests.NumSamplesCase.call(this, name, desc, params);
+ };
+
+ es3fMultisampleTests.PolygonNumSamplesCase.prototype = Object.create(es3fMultisampleTests.NumSamplesCase.prototype);
+
+ /** Copy the constructor */
+ es3fMultisampleTests.PolygonNumSamplesCase.prototype.constructor = es3fMultisampleTests.PolygonNumSamplesCase;
+
+ es3fMultisampleTests.PolygonNumSamplesCase.prototype.renderPattern = function() {
+ // The test pattern consists of several triangles with edges at different angles.
+
+ /** @type {number} */ var numTriangles = 25;
+ for (var i = 0; i < numTriangles; i++) {
+ /** @type {number} */ var angle0 = 2.0 * Math.PI * i / numTriangles + 0.001 * this.m_currentIteration;
+ /** @type {number} */ var angle1 = 2.0 * Math.PI * (i + 0.5) / numTriangles + 0.001 * this.m_currentIteration;
+
+ this.renderTriangle_pAsVec2WithColor(
+ [0.0, 0.0],
+ [Math.cos(angle0) * 0.95, Math.sin(angle0) * 0.95],
+ [Math.cos(angle1) * 0.95, Math.sin(angle1) * 0.95],
+ [1.0, 1.0, 1.0, 1.0]);
+ }
+ };
+
+ /**
+ * @extends {es3fMultisampleTests.NumSamplesCase}
+ * @constructor
+ * @param {string} name
+ * @param {string} desc
+ * @param {number=} numFboSamples
+ */
+ es3fMultisampleTests.LineNumSamplesCase = function(name, desc, numFboSamples) {
+ numFboSamples = numFboSamples === undefined ? 0 : numFboSamples;
+ /** @type {es3fMultisampleTests.FboParams} */
+ var params = numFboSamples >= 0 ? new es3fMultisampleTests.FboParams(numFboSamples, false, false) : new es3fMultisampleTests.FboParams();
+ es3fMultisampleTests.NumSamplesCase.call(this, name, desc, params);
+ };
+
+ es3fMultisampleTests.LineNumSamplesCase.prototype = Object.create(es3fMultisampleTests.NumSamplesCase.prototype);
+
+ /** Copy the constructor */
+ es3fMultisampleTests.LineNumSamplesCase.prototype.constructor = es3fMultisampleTests.LineNumSamplesCase;
+
+ es3fMultisampleTests.LineNumSamplesCase.prototype.renderPattern = function() {
+ // The test pattern consists of several lines at different angles.
+
+ // We scale the number of lines based on the viewport size. This is because a gl line's thickness is
+ // constant in pixel units, i.e. they get relatively thicker as viewport size decreases. Thus we must
+ // decrease the number of lines in order to decrease the extent of overlap among the lines in the
+ // center of the pattern.
+ /** @type {number} */ var numLines = Math.floor(100.0 * Math.sqrt(this.m_viewportSize / 256.0));
+
+ for (var i = 0; i < numLines; i++) {
+ /** @type {number} */ var angle = 2.0 * Math.PI * i / numLines + 0.001 * this.m_currentIteration;
+ this.renderLine([0.0, 0.0], [Math.cos(angle) * 0.95, Math.sin(angle) * 0.95], [1.0, 1.0, 1.0, 1.0]);
+ }
+ };
+
+ /**
+ * Case testing behaviour of common edges when multisampling.
+ *
+ * Draws a number of test patterns, each with a number of quads, each made
+ * of two triangles, rotated at different angles. The inner edge inside the
+ * quad (i.e. the common edge of the two triangles) still should not be
+ * visible, despite multisampling - i.e. the two triangles forming the quad
+ * should never get any common coverage bits in any pixel.
+ *
+ * @extends {es3fMultisampleTests.MultisampleCase}
+ * @constructor
+ * @param {string} name
+ * @param {string} desc
+ * @param {es3fMultisampleTests.CommonEdgeCase.CaseType} caseType
+ * @param {number} numFboSamples
+ */
+ es3fMultisampleTests.CommonEdgeCase = function(name, desc, caseType, numFboSamples) {
+ /** @type {number} */ var cases = caseType === es3fMultisampleTests.CommonEdgeCase.CaseType.SMALL_QUADS ? 128 : 32;
+ numFboSamples = numFboSamples === undefined ? 0 : numFboSamples;
+ /** @type {es3fMultisampleTests.FboParams} */
+ var params = numFboSamples >= 0 ? new es3fMultisampleTests.FboParams(numFboSamples, false, false) : new es3fMultisampleTests.FboParams();
+
+ es3fMultisampleTests.MultisampleCase.call(this, name, desc, cases, params);
+ /** @type {number} */ var DEFAULT_SMALL_QUADS_ITERATIONS = 16;
+ /** @type {number} */ var DEFAULT_BIGGER_THAN_VIEWPORT_QUAD_ITERATIONS = 64; // 8*8
+ /** @type {es3fMultisampleTests.CommonEdgeCase.CaseType} */ this.m_caseType = caseType;
+ /** @type {number} */ this.m_currentIteration = 0;
+ /** @type {number} */
+ this.m_numIterations = caseType === es3fMultisampleTests.CommonEdgeCase.CaseType.SMALL_QUADS ? es3fMultisampleTests.getIterationCount(DEFAULT_SMALL_QUADS_ITERATIONS) :
+ caseType === es3fMultisampleTests.CommonEdgeCase.CaseType.BIGGER_THAN_VIEWPORT_QUAD ? es3fMultisampleTests.getIterationCount(DEFAULT_BIGGER_THAN_VIEWPORT_QUAD_ITERATIONS) :
+ 8;
+ };
+
+ es3fMultisampleTests.CommonEdgeCase.prototype = Object.create(es3fMultisampleTests.MultisampleCase.prototype);
+
+ /** Copy the constructor */
+ es3fMultisampleTests.CommonEdgeCase.prototype.constructor = es3fMultisampleTests.CommonEdgeCase;
+
+ /**
+ * @enum {number}
+ */
+ es3fMultisampleTests.CommonEdgeCase.CaseType = {
+ SMALL_QUADS: 0, //!< Draw several small quads per iteration.
+ BIGGER_THAN_VIEWPORT_QUAD: 1, //!< Draw one bigger-than-viewport quad per iteration.
+ FIT_VIEWPORT_QUAD: 2 //!< Draw one exactly viewport-sized, axis aligned quad per iteration.
+ };
+
+ es3fMultisampleTests.CommonEdgeCase.prototype.init = function() {
+ var inited = es3fMultisampleTests.MultisampleCase.prototype.init.call(this);
+ if (!inited) {
+ return false;
+ }
+
+ if (this.m_caseType === es3fMultisampleTests.CommonEdgeCase.CaseType.SMALL_QUADS) {
+ // Check for a big enough viewport. With too small viewports the test case can't analyze the resulting image well enough.
+
+ /** @type {number} */ var minViewportSize = 32;
+
+ if (this.m_viewportSize < minViewportSize)
+ throw new Error('Render target width or height too low (is ' + this.m_viewportSize + ', should be at least ' + minViewportSize + ')');
+ }
+
+ gl.enable(gl.BLEND);
+ gl.blendEquation(gl.FUNC_ADD);
+ gl.blendFunc(gl.ONE, gl.ONE);
+ bufferedLogToConsole('Additive blending enabled in order to detect (erroneously) overlapping samples');
+ };
+
+ /**
+ * @return {tcuTestCase.IterateResult}
+ */
+ es3fMultisampleTests.CommonEdgeCase.prototype.iterate = function() {
+ /** @type {tcuSurface.Surface} */ var errorImg = new tcuSurface.Surface(this.m_viewportSize, this.m_viewportSize);
+
+ this.randomizeViewport();
+
+ gl.clearColor(0.0, 0.0, 0.0, 1.0);
+ gl.clear(gl.COLOR_BUFFER_BIT);
+
+ // Draw test pattern. Test patterns consist of quads formed with two triangles.
+ // After drawing the pattern, we check that the interior pixels of each quad are
+ // all the same color - this is meant to verify that there are no artifacts on the inner edge.
+
+ /** @type {Array<es3fMultisampleTests.QuadCorners>} */ var unicoloredRegions = [];
+
+ /** @type {Array<Array<number>>} */ var corners;
+ /** @type {number} */ var angleCos;
+ /** @type {number} */ var angleSin;
+ /** @type {number} */ var angle;
+ /** @type {number} */ var quadDiagLen;
+ /** @type {number} */ var unicolorRegionScale;
+ /** @type {number} */ var quadBaseAngleNdx
+ /** @type {number} */ var quadSubAngleNdx;
+
+ if (this.m_caseType == es3fMultisampleTests.CommonEdgeCase.CaseType.SMALL_QUADS) {
+ // Draw several quads, rotated at different angles.
+
+ quadDiagLen = 2.0 / 3.0 * 0.9; // \note Fit 3 quads in both x and y directions.
+
+
+ // \note First and second iteration get exact 0 (and 90, 180, 270) and 45 (and 135, 225, 315) angle quads, as they are kind of a special case.
+
+ if (this.m_currentIteration === 0) {
+ angleCos = 1.0;
+ angleSin = 0.0;
+ }
+ else if (this.m_currentIteration === 1) {
+ angleCos = Math.SQRT1_2;
+ angleSin = Math.SQRT1_2;
+ }
+ else {
+ angle = 0.5 * Math.PI * (this.m_currentIteration - 1) / (this.m_numIterations - 1);
+ angleCos = Math.cos(angle);
+ angleSin = Math.sin(angle);
+ }
+
+ corners = [
+ deMath.scale([angleCos, angleSin], 0.5 * quadDiagLen),
+ deMath.scale([-angleSin, angleCos], 0.5 * quadDiagLen),
+ deMath.scale([-angleCos, -angleSin], 0.5 * quadDiagLen),
+ deMath.scale([angleSin, -angleCos], 0.5 * quadDiagLen)
+ ];
+
+ // Draw 8 quads.
+ // First four are rotated at angles angle+0, angle+90, angle+180 and angle+270.
+ // Last four are rotated the same angles as the first four, but the ordering of the last triangle's vertices is reversed.
+
+ for (var quadNdx = 0; quadNdx < 8; quadNdx++) {
+ /** @type {Array<number>} */
+ var center = deMath.addScalar(
+ deMath.scale([quadNdx % 3, quadNdx / 3], (2.0 - quadDiagLen)/ 2.0),
+ (-0.5 * (2.0 - quadDiagLen)));
+
+ this.renderTriangle_pAsVec2WithColor(
+ deMath.add(corners[(0 + quadNdx) % 4], center),
+ deMath.add(corners[(1 + quadNdx) % 4], center),
+ deMath.add(corners[(2 + quadNdx) % 4], center),
+ [0.5, 0.5, 0.5, 1.0]);
+
+ if (quadNdx >= 4) {
+ this.renderTriangle_pAsVec2WithColor(
+ deMath.add(corners[(3 + quadNdx) % 4], center),
+ deMath.add(corners[(2 + quadNdx) % 4], center),
+ deMath.add(corners[(0 + quadNdx) % 4], center),
+ [0.5, 0.5, 0.5, 1.0]);
+ }
+ else {
+ this.renderTriangle_pAsVec2WithColor(
+ deMath.add(corners[(0 + quadNdx) % 4], center),
+ deMath.add(corners[(2 + quadNdx) % 4], center),
+ deMath.add(corners[(3 + quadNdx) % 4], center),
+ [0.5, 0.5, 0.5, 1.0]);
+ }
+
+ // The size of the 'interior' of a quad is assumed to be approximately unicolorRegionScale*<actual size of quad>.
+ // By 'interior' we here mean the region of non-boundary pixels of the rendered quad for which we can safely assume
+ // that it has all coverage bits set to 1, for every pixel.
+ unicolorRegionScale = 1.0 - 6.0 * 2.0 / this.m_viewportSize / quadDiagLen;
+ unicoloredRegions.push(
+ new es3fMultisampleTests.QuadCorners(
+ deMath.add(center, deMath.scale(corners[0], unicolorRegionScale)),
+ deMath.add(center, deMath.scale(corners[1], unicolorRegionScale)),
+ deMath.add(center, deMath.scale(corners[2], unicolorRegionScale)),
+ deMath.add(center, deMath.scale(corners[3], unicolorRegionScale))));
+ }
+ }
+ else if (this.m_caseType === es3fMultisampleTests.CommonEdgeCase.CaseType.BIGGER_THAN_VIEWPORT_QUAD) {
+ // Draw a bigger-than-viewport quad, rotated at an angle depending on m_currentIteration.
+
+ quadBaseAngleNdx = Math.floor(this.m_currentIteration / 8);
+ quadSubAngleNdx = this.m_currentIteration % 8;
+
+ if (quadBaseAngleNdx === 0) {
+ angleCos = 1.0;
+ angleSin = 0.0;
+ }
+ else if (quadBaseAngleNdx === 1) {
+ angleCos = Math.SQRT1_2;
+ angleSin = Math.SQRT1_2;
+ }
+ else {
+ angle = 0.5 * Math.PI * (this.m_currentIteration - 1) / (this.m_numIterations - 1);
+ angleCos = Math.cos(angle);
+ angleSin = Math.sin(angle);
+ }
+
+ quadDiagLen = 2.5 / Math.max(angleCos, angleSin);
+
+ corners = [
+ deMath.scale([angleCos, angleSin], 0.5 * quadDiagLen),
+ deMath.scale([-angleSin, angleCos], 0.5 * quadDiagLen),
+ deMath.scale([-angleCos, -angleSin], 0.5 * quadDiagLen),
+ deMath.scale([angleSin, -angleCos], 0.5 * quadDiagLen)
+ ];
+
+ this.renderTriangle_pAsVec2WithColor(
+ corners[(0 + quadSubAngleNdx) % 4],
+ corners[(1 + quadSubAngleNdx) % 4],
+ corners[(2 + quadSubAngleNdx) % 4],
+ [0.5, 0.5, 0.5, 1.0]);
+
+ if (quadSubAngleNdx >= 4) {
+ this.renderTriangle_pAsVec2WithColor(
+ corners[(3 + quadSubAngleNdx) % 4],
+ corners[(2 + quadSubAngleNdx) % 4],
+ corners[(0 + quadSubAngleNdx) % 4],
+ [0.5, 0.5, 0.5, 1.0]);
+ }
+ else {
+ this.renderTriangle_pAsVec2WithColor(
+ corners[(0 + quadSubAngleNdx) % 4],
+ corners[(2 + quadSubAngleNdx) % 4],
+ corners[(3 + quadSubAngleNdx) % 4],
+ [0.5, 0.5, 0.5, 1.0]);
+ }
+
+ unicolorRegionScale = 1.0 - 6.0 * 2.0 / this.m_viewportSize / quadDiagLen;
+ unicoloredRegions.push(
+ new es3fMultisampleTests.QuadCorners(
+ deMath.scale(corners[0], unicolorRegionScale),
+ deMath.scale(corners[1], unicolorRegionScale),
+ deMath.scale(corners[2], unicolorRegionScale),
+ deMath.scale(corners[3], unicolorRegionScale)));
+ }
+ else if (this.m_caseType === es3fMultisampleTests.CommonEdgeCase.CaseType.FIT_VIEWPORT_QUAD) {
+ // Draw an exactly viewport-sized quad, rotated by multiples of 90 degrees angle depending on m_currentIteration.
+
+ quadSubAngleNdx = this.m_currentIteration % 8;
+
+ corners = [
+ [1.0, 1.0],
+ [-1.0, 1.0],
+ [-1.0, -1.0],
+ [1.0, -1.0]
+ ];
+
+ this.renderTriangle_pAsVec2WithColor(
+ corners[(0 + quadSubAngleNdx) % 4],
+ corners[(1 + quadSubAngleNdx) % 4],
+ corners[(2 + quadSubAngleNdx) % 4],
+ [0.5, 0.5, 0.5, 1.0]);
+
+ if (quadSubAngleNdx >= 4) {
+ this.renderTriangle_pAsVec2WithColor(
+ corners[(3 + quadSubAngleNdx) % 4],
+ corners[(2 + quadSubAngleNdx) % 4],
+ corners[(0 + quadSubAngleNdx) % 4],
+ [0.5, 0.5, 0.5, 1.0]);
+ }
+ else {
+ this.renderTriangle_pAsVec2WithColor(
+ corners[(0 + quadSubAngleNdx) % 4],
+ corners[(2 + quadSubAngleNdx) % 4],
+ corners[(3 + quadSubAngleNdx) % 4],
+ [0.5, 0.5, 0.5, 1.0]);
+ }
+
+ unicoloredRegions.push(new es3fMultisampleTests.QuadCorners(corners[0], corners[1], corners[2], corners[3]));
+ }
+ else
+ throw new Error('CaseType not supported.');
+
+ // Read pixels and check unicolored regions.
+
+ /** @type {tcuSurface.Surface} */ var renderedImg = this.readImage();
+
+ errorImg.getAccess().clear([0.0, 1.0, 0.0, 1.0]);
+ tcuLogImage.logImage('RenderedImage', 'Rendered image', renderedImg.getAccess());
+
+ /** @type {boolean} */ var errorsDetected = false;
+ for (var i = 0; i < unicoloredRegions.length; i++) {
+ /** @type {es3fMultisampleTests.QuadCorners} */ var region = unicoloredRegions[i];
+ /** @type {Array<number>} */ var p0Win = deMath.scale(deMath.addScalar(region.p0, 1.0), 0.5 * (this.m_viewportSize - 1) + 0.5);
+ /** @type {Array<number>} */ var p1Win = deMath.scale(deMath.addScalar(region.p1, 1.0), 0.5 * (this.m_viewportSize - 1) + 0.5);
+ /** @type {Array<number>} */ var p2Win = deMath.scale(deMath.addScalar(region.p2, 1.0), 0.5 * (this.m_viewportSize - 1) + 0.5);
+ /** @type {Array<number>} */ var p3Win = deMath.scale(deMath.addScalar(region.p3, 1.0), 0.5 * (this.m_viewportSize - 1) + 0.5);
+ /** @type {boolean} */ var errorsInCurrentRegion = !es3fMultisampleTests.isPixelRegionUnicolored(renderedImg, p0Win, p1Win, p2Win, p3Win);
+
+ if (errorsInCurrentRegion)
+ es3fMultisampleTests.drawUnicolorTestErrors(renderedImg, errorImg.getAccess(), p0Win, p1Win, p2Win, p3Win);
+
+ errorsDetected = errorsDetected || errorsInCurrentRegion;
+ }
+
+ this.m_currentIteration++;
+
+ if (errorsDetected) {
+ bufferedLogToConsole('Failure: Not all quad interiors seem unicolored - common-edge artifacts?');
+ bufferedLogToConsole('Erroneous pixels are drawn red in the following image');
+ tcuLogImage.logImage('RenderedImageWithErrors', 'Rendered image with errors marked', renderedImg.getAccess());
+ tcuLogImage.logImage('ErrorsOnly', 'Image with error pixels only', errorImg.getAccess());
+ testFailedOptions('Failed: iteration ' + (this.m_currentIteration - 1), false);
+ return tcuTestCase.IterateResult.STOP;
+ }
+ else if (this.m_currentIteration < this.m_numIterations) {
+ bufferedLogToConsole('Quads seem OK - moving on to next pattern');
+ return tcuTestCase.IterateResult.CONTINUE;
+ }
+ else {
+ bufferedLogToConsole('Success: All quad interiors seem unicolored (no common-edge artifacts)');
+ testPassedOptions('Passed: iteration ' + (this.m_currentIteration - 1), true);
+ return tcuTestCase.IterateResult.STOP;
+ }
+ };
+
+ /**
+ * Test that depth values are per-sample.
+ *
+ * Draws intersecting, differently-colored polygons and checks that there
+ * are at least sample count+1 distinct colors present, due to some of the
+ * samples at the intersection line belonging to one and some to another
+ * polygon.
+ *
+ * @extends {es3fMultisampleTests.NumSamplesCase}
+ * @constructor
+ * @param {string} name
+ * @param {string} desc
+ * @param {number=} numFboSamples
+ */
+ es3fMultisampleTests.SampleDepthCase = function(name, desc, numFboSamples) {
+ numFboSamples = numFboSamples === undefined ? 0 : numFboSamples;
+ /** @type {es3fMultisampleTests.FboParams} */
+ var params = numFboSamples >= 0 ? new es3fMultisampleTests.FboParams(numFboSamples, true, false) : new es3fMultisampleTests.FboParams();
+ es3fMultisampleTests.NumSamplesCase.call(this, name, desc, params);
+ };
+
+ es3fMultisampleTests.SampleDepthCase.prototype = Object.create(es3fMultisampleTests.NumSamplesCase.prototype);
+
+ /** Copy the constructor */
+ es3fMultisampleTests.SampleDepthCase.prototype.constructor = es3fMultisampleTests.SampleDepthCase;
+
+ es3fMultisampleTests.SampleDepthCase.prototype.init = function() {
+ var inited = es3fMultisampleTests.MultisampleCase.prototype.init.call(this);
+ if (!inited) {
+ return false;
+ }
+
+ gl.enable(gl.DEPTH_TEST);
+ gl.depthFunc(gl.LESS);
+
+ bufferedLogToConsole('Depth test enabled, depth func is gl.LESS');
+ bufferedLogToConsole('Drawing several bigger-than-viewport black or white polygons intersecting each other');
+ };
+
+ es3fMultisampleTests.SampleDepthCase.prototype.renderPattern = function() {
+ gl.clearColor(0.0, 0.0, 0.0, 0.0);
+ gl.clearDepth(1.0);
+ gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
+
+ /** @type {number} */ var numPolygons = 50;
+
+ for (var i = 0; i < numPolygons; i++) {
+ /** @type {Array<number>} */ var color = i % 2 == 0 ? [1.0, 1.0, 1.0, 1.0] : [0.0, 0.0, 0.0, 1.0];
+ /** @type {number} */ var angle = 2.0 * Math.PI * i / numPolygons + 0.001 * this.m_currentIteration;
+ /** @type {Array<number>} */ var pt0 = [3.0 * Math.cos(angle + 2.0 * Math.PI * 0.0 / 3.0), 3.0 * Math.sin(angle + 2.0 * Math.PI * 0.0 / 3.0), 1.0];
+ /** @type {Array<number>} */ var pt1 = [3.0 * Math.cos(angle + 2.0 * Math.PI * 1.0 / 3.0), 3.0 * Math.sin(angle + 2.0 * Math.PI * 1.0 / 3.0), 0.0];
+ /** @type {Array<number>} */ var pt2 = [3.0 * Math.cos(angle + 2.0 * Math.PI * 2.0 / 3.0), 3.0 * Math.sin(angle + 2.0 * Math.PI * 2.0 / 3.0), 0.0];
+
+ this.renderTriangle_pAsVec3WithColor(pt0, pt1, pt2, color);
+ }
+ };
+
+ /**
+ * Test that stencil buffer values are per-sample.
+ *
+ * Draws a unicolored pattern and marks drawn samples in stencil buffer;
+ * then clears and draws a viewport-size quad with that color and with
+ * proper stencil test such that the resulting image should be exactly the
+ * same as after the pattern was first drawn.
+ *
+ * @extends {es3fMultisampleTests.MultisampleCase}
+ * @constructor
+ * @param {string} name
+ * @param {string} desc
+ * @param {number=} numFboSamples
+ */
+ es3fMultisampleTests.SampleStencilCase = function(name, desc, numFboSamples) {
+ numFboSamples = numFboSamples === undefined ? 0 : numFboSamples;
+ /** @type {es3fMultisampleTests.FboParams} */
+ var params = numFboSamples >= 0 ? new es3fMultisampleTests.FboParams(numFboSamples, false, true) : new es3fMultisampleTests.FboParams();
+ es3fMultisampleTests.MultisampleCase.call(this, name, desc, 256, params);
+ };
+
+ es3fMultisampleTests.SampleStencilCase.prototype = Object.create(es3fMultisampleTests.MultisampleCase.prototype);
+
+ /** Copy the constructor */
+ es3fMultisampleTests.SampleStencilCase.prototype.constructor = es3fMultisampleTests.SampleStencilCase;
+
+ /**
+ * @return {tcuTestCase.IterateResult}
+ */
+ es3fMultisampleTests.SampleStencilCase.prototype.iterate = function() {
+ this.randomizeViewport();
+
+ gl.clearColor(0.0, 0.0, 0.0, 1.0);
+ gl.clearStencil(0);
+ gl.clear(gl.COLOR_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);
+ gl.enable(gl.STENCIL_TEST);
+ gl.stencilFunc(gl.ALWAYS, 1, 1);
+ gl.stencilOp(gl.KEEP, gl.KEEP, gl.REPLACE);
+
+ bufferedLogToConsole('Drawing a pattern with gl.stencilFunc(gl.ALWAYS, 1, 1) and gl.stencilOp(gl.KEEP, gl.KEEP, gl.REPLACE)');
+
+ /** @type {number} */ var numTriangles = 25;
+ for (var i = 0; i < numTriangles; i++) {
+ /** @type {number} */ var angle0 = 2.0 * Math.PI * i / numTriangles;
+ /** @type {number} */ var angle1 = 2.0 * Math.PI * (i + 0.5) / numTriangles;
+
+ this.renderTriangle_pAsVec2WithColor(
+ [0.0, 0.0],
+ [Math.cos(angle0) * 0.95, Math.sin(angle0) * 0.95],
+ [Math.cos(angle1) * 0.95, Math.sin(angle1) * 0.95],
+ [1.0, 1.0, 1.0, 1.0]);
+ }
+
+ /** @type {tcuSurface.Surface} */ var renderedImgFirst = this.readImage();
+ tcuLogImage.logImage('RenderedImgFirst', 'First image rendered', renderedImgFirst.getAccess());
+ bufferedLogToConsole('Clearing color buffer to black');
+
+ gl.clear(gl.COLOR_BUFFER_BIT);
+ gl.stencilFunc(gl.EQUAL, 1, 1);
+ gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP);
+
+ bufferedLogToConsole('Checking that color buffer was actually cleared to black');
+
+ /** @type {tcuSurface.Surface} */ var clearedImg = this.readImage();
+
+ for (var y = 0; y < clearedImg.getHeight(); y++)
+ for (var x = 0; x < clearedImg.getWidth(); x++) {
+ /** @type {tcuRGBA.RGBA} */ var clr = new tcuRGBA.RGBA(clearedImg.getPixel(x, y));
+ if (!clr.equals(tcuRGBA.RGBA.black)) {
+ bufferedLogToConsole('Failure: first non-black pixel, color ' + clr.toString() + ', detected at coordinates (' + x + ', ' + y + ')');
+ tcuLogImage.logImage('ClearedImg', 'Image after clearing, erroneously non-black', clearedImg.getAccess());
+ testFailedOptions('Failed', false);
+ return tcuTestCase.IterateResult.STOP;
+ }
+ }
+
+ bufferedLogToConsole('Drawing a viewport-sized quad with gl.stencilFunc(gl.EQUAL, 1, 1) and gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP) - should result in same image as the first');
+
+ this.renderQuad_WithColor(
+ [-1.0, -1.0],
+ [1.0, -1.0],
+ [-1.0, 1.0],
+ [1.0, 1.0],
+ [1.0, 1.0, 1.0, 1.0]);
+
+ /** @type {tcuSurface.Surface} */ var renderedImgSecond = this.readImage();
+ tcuLogImage.logImage('RenderedImgSecond', 'Second image rendered', renderedImgSecond.getAccess());
+ /** @type {boolean} */
+ var passed = tcuImageCompare.pixelThresholdCompare(
+ 'ImageCompare',
+ 'Image comparison',
+ renderedImgFirst,
+ renderedImgSecond,
+ [0,0,0,0]);
+
+ if (passed) {
+ bufferedLogToConsole('Success: The two images rendered are identical');
+ testPassedOptions('Passed', true);
+ }
+ else
+ testFailedOptions('Failed', false);
+
+ return tcuTestCase.IterateResult.STOP;
+ };
+
+ /**
+ * Tests coverage mask generation proportionality property.
+ *
+ * Tests that the number of coverage bits in a coverage mask created by
+ * gl.SAMPLE_ALPHA_TO_COVERAGE or gl.SAMPLE_COVERAGE is, on average,
+ * proportional to the alpha or coverage value, respectively. Draws
+ * multiple frames, each time increasing the alpha or coverage value used,
+ * and checks that the average color is changing appropriately.
+ *
+ * @extends {es3fMultisampleTests.MultisampleCase}
+ * @constructor
+ * @param {string} name
+ * @param {string} desc
+ * @param {es3fMultisampleTests.MaskProportionalityCase.CaseType} type
+ * @param {number=} numFboSamples
+ */
+ es3fMultisampleTests.MaskProportionalityCase = function(name, desc, type, numFboSamples) {
+ numFboSamples = numFboSamples === undefined ? 0 : numFboSamples;
+ /** @type {es3fMultisampleTests.FboParams} */
+ var params = numFboSamples >= 0 ? new es3fMultisampleTests.FboParams(numFboSamples, false, false) : new es3fMultisampleTests.FboParams();
+ es3fMultisampleTests.MultisampleCase.call(this, name, desc, 32, params);
+ /** @type {es3fMultisampleTests.MaskProportionalityCase.CaseType} */ this.m_type = type;
+ /** @type {number} */ this.m_numIterations;
+ /** @type {number} */ this.m_currentIteration = 0;
+ /** @type {number} */ this.m_previousIterationColorSum = -1;
+ };
+
+ es3fMultisampleTests.MaskProportionalityCase.prototype = Object.create(es3fMultisampleTests.MultisampleCase.prototype);
+
+ /** Copy the constructor */
+ es3fMultisampleTests.MaskProportionalityCase.prototype.constructor = es3fMultisampleTests.MaskProportionalityCase;
+
+ /**
+ * @enum {number}
+ */
+ es3fMultisampleTests.MaskProportionalityCase.CaseType = {
+ ALPHA_TO_COVERAGE: 0,
+ SAMPLE_COVERAGE: 1,
+ SAMPLE_COVERAGE_INVERTED: 2
+ };
+
+ es3fMultisampleTests.MaskProportionalityCase.prototype.init = function() {
+ var inited = es3fMultisampleTests.MultisampleCase.prototype.init.call(this);
+ if (!inited) {
+ return false;
+ }
+
+ if (this.m_type == es3fMultisampleTests.MaskProportionalityCase.CaseType.ALPHA_TO_COVERAGE) {
+ gl.enable(gl.SAMPLE_ALPHA_TO_COVERAGE);
+ bufferedLogToConsole('gl.SAMPLE_ALPHA_TO_COVERAGE is enabled');
+ }
+ else {
+ assertMsgOptions(
+ this.m_type == es3fMultisampleTests.MaskProportionalityCase.CaseType.SAMPLE_COVERAGE ||
+ this.m_type == es3fMultisampleTests.MaskProportionalityCase.CaseType.SAMPLE_COVERAGE_INVERTED,
+ 'CaseType should be SAMPLE_COVERAGE or SAMPLE_COVERAGE_INVERTED', false, true);
+
+ gl.enable(gl.SAMPLE_COVERAGE);
+ bufferedLogToConsole('gl.SAMPLE_COVERAGE is enabled');
+ }
+
+ this.m_numIterations = Math.max(2, es3fMultisampleTests.getIterationCount(this.m_numSamples * 5));
+
+ this.randomizeViewport(); // \note Using the same viewport for every iteration since coverage mask may depend on window-relative pixel coordinate.
+ };
+
+ /**
+ * @return {tcuTestCase.IterateResult}
+ */
+ es3fMultisampleTests.MaskProportionalityCase.prototype.iterate = function() {
+ bufferedLogToConsole('Clearing color to black');
+ gl.colorMask(true, true, true, true);
+ gl.clearColor(0.0, 0.0, 0.0, 1.0);
+ gl.clear(gl.COLOR_BUFFER_BIT);
+
+ if (this.m_type === es3fMultisampleTests.MaskProportionalityCase.CaseType.ALPHA_TO_COVERAGE) {
+ gl.colorMask(true, true, true, false);
+ bufferedLogToConsole('Using color mask TRUE, TRUE, TRUE, FALSE');
+ }
+
+ // Draw quad.
+
+ /** @type {Array<number>} */ var pt0 = [-1.0, -1.0];
+ /** @type {Array<number>} */ var pt1 = [1.0, -1.0];
+ /** @type {Array<number>} */ var pt2 = [-1.0, 1.0];
+ /** @type {Array<number>} */ var pt3 = [1.0, 1.0];
+ /** @type {Array<number>} */ var quadColor = [1.0, 0.0, 0.0, 1.0];
+ /** @type {number} */ var alphaOrCoverageValue = this.m_currentIteration / (this.m_numIterations-1);
+
+ if (this.m_type === es3fMultisampleTests.MaskProportionalityCase.CaseType.ALPHA_TO_COVERAGE) {
+ bufferedLogToConsole('Drawing a red quad using alpha value ' + alphaOrCoverageValue);
+ quadColor[3] = alphaOrCoverageValue;
+ }
+ else {
+ assertMsgOptions(
+ this.m_type === es3fMultisampleTests.MaskProportionalityCase.CaseType.SAMPLE_COVERAGE ||
+ this.m_type === es3fMultisampleTests.MaskProportionalityCase.CaseType.SAMPLE_COVERAGE_INVERTED,
+ 'CaseType should be SAMPLE_COVERAGE or SAMPLE_COVERAGE_INVERTED', false, true);
+
+ /** @type {boolean} */ var isInverted = (this.m_type === es3fMultisampleTests.MaskProportionalityCase.CaseType.SAMPLE_COVERAGE_INVERTED);
+ /** @type {number} */ var coverageValue = isInverted ? 1.0 - alphaOrCoverageValue : alphaOrCoverageValue;
+ bufferedLogToConsole('Drawing a red quad using sample coverage value ' + coverageValue + (isInverted ? ' (inverted)' : ''));
+ gl.sampleCoverage(coverageValue, isInverted ? true : false);
+ }
+
+ this.renderQuad_WithColor(pt0, pt1, pt2, pt3, quadColor);
+
+ // Read and log image.
+ /** @type {tcuSurface.Surface} */ var renderedImg = this.readImage();
+ /** @type {number} */ var numPixels = renderedImg.getWidth() * renderedImg.getHeight();
+
+ tcuLogImage.logImage('RenderedImage', 'Rendered image', renderedImg.getAccess());
+ // Compute average red component in rendered image.
+
+ /** @type {number} */ var sumRed = 0;
+
+ for (var y = 0; y < renderedImg.getHeight(); y++)
+ for (var x = 0; x < renderedImg.getWidth(); x++)
+ sumRed += new tcuRGBA.RGBA(renderedImg.getPixel(x, y)).getRed();
+
+ bufferedLogToConsole('Average red color component: ' + (sumRed / 255.0 / numPixels));
+
+ // Check if average color has decreased from previous frame's color.
+
+ if (sumRed < this.m_previousIterationColorSum) {
+ bufferedLogToConsole('Failure: Current average red color component is lower than previous');
+ testFailedOptions('Failed', false);
+ return tcuTestCase.IterateResult.STOP;
+ }
+
+ // Check if coverage mask is not all-zeros if alpha or coverage value is 0 (or 1, if inverted).
+
+ if (this.m_currentIteration == 0 && sumRed != 0)
+ {
+ bufferedLogToConsole('Failure: Image should be completely black');
+ testFailedOptions('Failed', false);
+ return tcuTestCase.IterateResult.STOP;
+ }
+
+ if (this.m_currentIteration == this.m_numIterations-1 && sumRed != 0xff*numPixels)
+ {
+ bufferedLogToConsole('Failure: Image should be completely red');
+
+ testFailedOptions('Failed', false);
+ return tcuTestCase.IterateResult.STOP;
+ }
+
+ this.m_previousIterationColorSum = sumRed;
+
+ this.m_currentIteration++;
+
+ if (this.m_currentIteration >= this.m_numIterations)
+ {
+ bufferedLogToConsole('Success: Number of coverage mask bits set appears to be, on average, proportional to ' +
+ (this.m_type == es3fMultisampleTests.MaskProportionalityCase.CaseType.ALPHA_TO_COVERAGE ? 'alpha' :
+ this.m_type == es3fMultisampleTests.MaskProportionalityCase.CaseType.SAMPLE_COVERAGE ? 'sample coverage value' :
+ 'inverted sample coverage value'));
+
+ testPassedOptions('Passed', true);
+ return tcuTestCase.IterateResult.STOP;
+ }
+ else
+ return tcuTestCase.IterateResult.CONTINUE;
+ };
+
+ /**
+ * Tests coverage mask generation constancy property.
+ *
+ * Tests that the coverage mask created by gl.SAMPLE_ALPHA_TO_COVERAGE or
+ * gl.SAMPLE_COVERAGE is constant at given pixel coordinates, with a given
+ * alpha component or coverage value, respectively. Draws two quads, with
+ * the second one fully overlapping the first one such that at any given
+ * pixel, both quads have the same alpha or coverage value. This way, if
+ * the constancy property is fulfilled, only the second quad should be
+ * visible.
+ * @extends {es3fMultisampleTests.MultisampleCase}
+ * @constructor
+ * @param {string} name
+ * @param {string} desc
+ * @param {es3fMultisampleTests.MaskConstancyCase.CaseType} type
+ * @param {number=} numFboSamples
+ */
+ es3fMultisampleTests.MaskConstancyCase = function(name, desc, type, numFboSamples) {
+ numFboSamples = numFboSamples === undefined ? 0 : numFboSamples;
+ /** @type {es3fMultisampleTests.FboParams} */
+ var params = numFboSamples >= 0 ? new es3fMultisampleTests.FboParams(numFboSamples, false, false) : new es3fMultisampleTests.FboParams();
+ es3fMultisampleTests.MultisampleCase.call(this, name, desc, 256, params);
+ var CaseType = es3fMultisampleTests.MaskConstancyCase.CaseType;
+ /** @type {boolean} */ this.m_isAlphaToCoverageCase = (type === CaseType.ALPHA_TO_COVERAGE || type === CaseType.BOTH || type === CaseType.BOTH_INVERTED);
+ /** @type {boolean} */ this.m_isSampleCoverageCase = (type === CaseType.SAMPLE_COVERAGE || type === CaseType.SAMPLE_COVERAGE_INVERTED || type === CaseType.BOTH || type === CaseType.BOTH_INVERTED);
+ /** @type {boolean} */ this.m_isInvertedSampleCoverageCase = (type === CaseType.SAMPLE_COVERAGE_INVERTED || type === CaseType.BOTH_INVERTED);
+ };
+
+ es3fMultisampleTests.MaskConstancyCase.prototype = Object.create(es3fMultisampleTests.MultisampleCase.prototype);
+
+ /** Copy the constructor */
+ es3fMultisampleTests.MaskConstancyCase.prototype.constructor = es3fMultisampleTests.MaskConstancyCase;
+
+ /**
+ * @enum {number}
+ */
+ es3fMultisampleTests.MaskConstancyCase.CaseType = {
+ ALPHA_TO_COVERAGE: 0, //!< Use only alpha-to-coverage.
+ SAMPLE_COVERAGE: 1, //!< Use only sample coverage.
+ SAMPLE_COVERAGE_INVERTED: 2, //!< Use only inverted sample coverage.
+ BOTH: 3, //!< Use both alpha-to-coverage and sample coverage.
+ BOTH_INVERTED: 4 //!< Use both alpha-to-coverage and inverted sample coverage.
+ };
+
+ /**
+ * @return {tcuTestCase.IterateResult}
+ */
+ es3fMultisampleTests.MaskConstancyCase.prototype.iterate = function() {
+ this.randomizeViewport();
+
+ bufferedLogToConsole('Clearing color to black');
+ gl.clearColor(0.0, 0.0, 0.0, 1.0);
+ gl.clear(gl.COLOR_BUFFER_BIT);
+
+ if (this.m_isAlphaToCoverageCase) {
+ gl.enable(gl.SAMPLE_ALPHA_TO_COVERAGE);
+ gl.colorMask(true, true, true, false);
+ bufferedLogToConsole('gl.SAMPLE_ALPHA_TO_COVERAGE is enabled');
+ bufferedLogToConsole('Color mask is TRUE, TRUE, TRUE, FALSE');
+ }
+
+ if (this.m_isSampleCoverageCase) {
+ gl.enable(gl.SAMPLE_COVERAGE);
+ bufferedLogToConsole('gl.SAMPLE_COVERAGE is enabled');
+ }
+
+ bufferedLogToConsole('Drawing several green quads, each fully overlapped by a red quad with the same ' +
+ (this.m_isAlphaToCoverageCase ? 'alpha' : '') +
+ (this.m_isAlphaToCoverageCase && this.m_isSampleCoverageCase ? ' and ' : '') +
+ (this.m_isInvertedSampleCoverageCase ? 'inverted ' : '') +
+ (this.m_isSampleCoverageCase ? 'sample coverage' : '') +
+ ' values');
+
+ /** @type {number} */ var numQuadRowsCols = this.m_numSamples * 4;
+
+ for (var row = 0; row < numQuadRowsCols; row++) {
+ for (var col = 0; col < numQuadRowsCols; col++) {
+ /** @type {number} */ var x0 = (col + 0) / numQuadRowsCols * 2.0 - 1.0;
+ /** @type {number} */ var x1 = (col + 1) / numQuadRowsCols * 2.0 - 1.0;
+ /** @type {number} */ var y0 = (row + 0) / numQuadRowsCols * 2.0 - 1.0;
+ /** @type {number} */ var y1 = (row + 1) / numQuadRowsCols * 2.0 - 1.0;
+ /** @type {Array<number>} */ var baseGreen = [0.0, 1.0, 0.0, 0.0];
+ /** @type {Array<number>} */ var baseRed = [1.0, 0.0, 0.0, 0.0];
+ /** @type {Array<number>} */ var alpha0 = [0.0, 0.0, 0.0, this.m_isAlphaToCoverageCase ? col / (numQuadRowsCols - 1) : 1.0];
+ /** @type {Array<number>} */ var alpha1 = [0.0, 0.0, 0.0, this.m_isAlphaToCoverageCase ? row / (numQuadRowsCols - 1) : 1.0];
+
+ if (this.m_isSampleCoverageCase) {
+ /** @type {number} */ var value = (row*numQuadRowsCols + col) / (numQuadRowsCols*numQuadRowsCols - 1);
+ gl.sampleCoverage(this.m_isInvertedSampleCoverageCase ? 1.0 - value : value, this.m_isInvertedSampleCoverageCase ? true : false);
+ }
+
+ this.renderQuad([x0, y0], [x1, y0], [x0, y1], [x1, y1],
+ deMath.add(baseGreen, alpha0), deMath.add(baseGreen, alpha1),
+ deMath.add(baseGreen, alpha0), deMath.add(baseGreen, alpha1));
+ this.renderQuad([x0, y0], [x1, y0], [x0, y1], [x1, y1],
+ deMath.add(baseRed, alpha0), deMath.add(baseRed, alpha1),
+ deMath.add(baseRed, alpha0), deMath.add(baseRed, alpha1));
+ }
+ }
+
+ /** @type {tcuSurface.Surface} */ var renderedImg = this.readImage();
+
+ tcuLogImage.logImage('RenderedImage', 'Rendered image', renderedImg.getAccess());
+ for (var y = 0; y < renderedImg.getHeight(); y++)
+ for (var x = 0; x < renderedImg.getWidth(); x++) {
+ if (new tcuRGBA.RGBA(renderedImg.getPixel(x, y)).getGreen() > 0) {
+ bufferedLogToConsole('Failure: Non-zero green color component detected - should have been completely overwritten by red quad');
+ testFailedOptions('Failed', false);
+ return tcuTestCase.IterateResult.STOP;
+ }
+ }
+
+ bufferedLogToConsole('Success: Coverage mask appears to be constant at a given pixel coordinate with a given ' +
+ (this.m_isAlphaToCoverageCase ? 'alpha' : '') +
+ (this.m_isAlphaToCoverageCase && this.m_isSampleCoverageCase ? ' and ' : '') +
+ (this.m_isSampleCoverageCase ? 'coverage value' : ''));
+
+ testPassedOptions('Passed', true);
+
+ return tcuTestCase.IterateResult.STOP;
+ }
+
+
+ /**
+ * Tests coverage mask inversion validity.
+ *
+ * Tests that the coverage masks obtained by glSampleCoverage(..., true)
+ * and glSampleCoverage(..., false) are indeed each others' inverses.
+ * This is done by drawing a pattern, with varying coverage values,
+ * overlapped by a pattern that has inverted masks and is otherwise
+ * identical. The resulting image is compared to one obtained by drawing
+ * the same pattern but with all-ones coverage masks.
+ * @extends {es3fMultisampleTests.MultisampleCase}
+ * @constructor
+ * @param {string} name
+ * @param {string} desc
+ * @param {number=} numFboSamples
+ */
+ es3fMultisampleTests.CoverageMaskInvertCase = function(name, desc, numFboSamples) {
+ numFboSamples = numFboSamples === undefined ? 0 : numFboSamples;
+ /** @type {es3fMultisampleTests.FboParams} */
+ var params = numFboSamples >= 0 ? new es3fMultisampleTests.FboParams(numFboSamples, false, false) : new es3fMultisampleTests.FboParams();
+ es3fMultisampleTests.MultisampleCase.call(this, name, desc, 256, params);
+ };
+
+ es3fMultisampleTests.CoverageMaskInvertCase.prototype = Object.create(es3fMultisampleTests.MultisampleCase.prototype);
+
+ /** Copy the constructor */
+ es3fMultisampleTests.CoverageMaskInvertCase.prototype.constructor = es3fMultisampleTests.CoverageMaskInvertCase;
+
+ /**
+ * @param {boolean} invertSampleCoverage
+ */
+ es3fMultisampleTests.CoverageMaskInvertCase.prototype.drawPattern = function(invertSampleCoverage) {
+ /** @type {number} */ var numTriangles = 25;
+ for (var i = 0; i < numTriangles; i++) {
+ gl.sampleCoverage(i / (numTriangles - 1), invertSampleCoverage ? true : false);
+
+ /** @type {number} */ var angle0 = 2.0 * Math.PI * i / numTriangles;
+ /** @type {number} */ var angle1 = 2.0 * Math.PI * (i + 0.5) / numTriangles;
+
+ this.renderTriangle_pAsVec2WithColor(
+ [0.0, 0.0],
+ [Math.cos(angle0) * 0.95, Math.sin(angle0) * 0.95],
+ [Math.cos(angle1) * 0.95, Math.sin(angle1) * 0.95],
+ [0.4 + i / numTriangles * 0.6,
+ 0.5 + i / numTriangles * 0.3,
+ 0.6 - i / numTriangles * 0.5,
+ 0.7 - i / numTriangles * 0.7]);
+ }
+ };
+
+ /**
+ * @return {tcuTestCase.IterateResult}
+ */
+ es3fMultisampleTests.CoverageMaskInvertCase.prototype.iterate = function() {
+ this.randomizeViewport();
+
+ gl.enable(gl.BLEND);
+ gl.blendEquation(gl.FUNC_ADD);
+ gl.blendFunc(gl.ONE, gl.ONE);
+ bufferedLogToConsole('Additive blending enabled in order to detect (erroneously) overlapping samples');
+
+ bufferedLogToConsole('Clearing color to all-zeros');
+ gl.clearColor(0.0, 0.0, 0.0, 0.0);
+ gl.clear(gl.COLOR_BUFFER_BIT);
+ bufferedLogToConsole('Drawing the pattern with gl.SAMPLE_COVERAGE disabled');
+ this.drawPattern(false);
+ /** @type {tcuSurface.Surface} */ var renderedImgNoSampleCoverage = this.readImage();
+
+ tcuLogImage.logImage('RenderedImageNoSampleCoverage', 'Rendered image with gl.SAMPLE_COVERAGE disabled', renderedImgNoSampleCoverage.getAccess());
+ bufferedLogToConsole('Clearing color to all-zeros');
+ gl.clear(gl.COLOR_BUFFER_BIT);
+ gl.enable(gl.SAMPLE_COVERAGE);
+ bufferedLogToConsole('Drawing the pattern with gl.SAMPLE_COVERAGE enabled, using non-inverted masks');
+ this.drawPattern(false);
+ bufferedLogToConsole('Drawing the pattern with gl.SAMPLE_COVERAGE enabled, using same sample coverage values but inverted masks');
+ this.drawPattern(true);
+ /** @type {tcuSurface.Surface} */ var renderedImgSampleCoverage = this.readImage();
+
+ tcuLogImage.logImage('RenderedImageSampleCoverage', 'Rendered image with gl.SAMPLE_COVERAGE enabled', renderedImgSampleCoverage.getAccess());
+ /** @type {boolean} */ var passed = tcuImageCompare.pixelThresholdCompare(
+ 'CoverageVsNoCoverage',
+ 'Comparison of same pattern with gl.SAMPLE_COVERAGE disabled and enabled',
+ renderedImgNoSampleCoverage,
+ renderedImgSampleCoverage,
+ [0, 0, 0, 0]);
+
+ if (passed) {
+ bufferedLogToConsole('Success: The two images rendered are identical');
+ testPassedOptions('Passed', true);
+ }
+ else {
+ testFailedOptions('Failed', false);
+ }
+
+ return tcuTestCase.IterateResult.STOP;
+ };
+
+ es3fMultisampleTests.init = function() {
+ var testGroup = tcuTestCase.runner.testCases;
+ /**
+ * @enum {number}
+ */
+ var CaseType = {
+ DEFAULT_FRAMEBUFFER: 0,
+ FBO_4_SAMPLES: 1,
+ FBO_8_SAMPLES: 2,
+ FBO_MAX_SAMPLES: 3
+ };
+
+ for (var caseTypeI in CaseType) {
+ /** @type {CaseType} */ var caseType = CaseType[caseTypeI];
+ /** @type {number} */
+ var numFboSamples = caseType === CaseType.DEFAULT_FRAMEBUFFER ? -1 :
+ caseType === CaseType.FBO_4_SAMPLES ? 4 :
+ caseType === CaseType.FBO_8_SAMPLES ? 8 :
+ caseType === CaseType.FBO_MAX_SAMPLES ? 0 :
+ -2;
+
+ /** @type {?string} */
+ var name = caseType === CaseType.DEFAULT_FRAMEBUFFER ? 'default_framebuffer' :
+ caseType === CaseType.FBO_4_SAMPLES ? 'fbo_4_samples' :
+ caseType === CaseType.FBO_8_SAMPLES ? 'fbo_8_samples' :
+ caseType === CaseType.FBO_MAX_SAMPLES ? 'fbo_max_samples' :
+ null;
+ /** @type {?string} */
+ var desc = caseType === CaseType.DEFAULT_FRAMEBUFFER ? 'Render into default framebuffer' :
+ caseType === CaseType.FBO_4_SAMPLES ? 'Render into a framebuffer object with 4 samples' :
+ caseType === CaseType.FBO_8_SAMPLES ? 'Render into a framebuffer object with 8 samples' :
+ caseType === CaseType.FBO_MAX_SAMPLES ? 'Render into a framebuffer object with the maximum number of samples' :
+ null;
+
+ /** @type {tcuTestCase.DeqpTest} */ var group = tcuTestCase.newTest(name, desc);
+
+ assertMsgOptions(group.name != null, 'Error: No Test Name', false, true);
+ assertMsgOptions(group.description != null, 'Error: No Test Description', false, true);
+ assertMsgOptions(numFboSamples >= -1, 'Assert Failed: numFboSamples >= -1', false, true);
+ testGroup.addChild(group);
+
+ group.addChild(new es3fMultisampleTests.PolygonNumSamplesCase(
+ 'num_samples_polygon',
+ 'Test sanity of the sample count, with polygons',
+ numFboSamples));
+
+ group.addChild(new es3fMultisampleTests.LineNumSamplesCase(
+ 'num_samples_line',
+ 'Test sanity of the sample count, with lines',
+ numFboSamples));
+
+ group.addChild(new es3fMultisampleTests.CommonEdgeCase(
+ 'common_edge_small_quads',
+ 'Test polygons\'s common edges with small quads',
+ es3fMultisampleTests.CommonEdgeCase.CaseType.SMALL_QUADS,
+ numFboSamples));
+
+ group.addChild(new es3fMultisampleTests.CommonEdgeCase(
+ 'common_edge_big_quad',
+ 'Test polygon\'s common edges with bigger-than-viewport quads',
+ es3fMultisampleTests.CommonEdgeCase.CaseType.BIGGER_THAN_VIEWPORT_QUAD,
+ numFboSamples));
+
+ group.addChild(new es3fMultisampleTests.CommonEdgeCase(
+ 'common_edge_viewport_quad',
+ 'Test polygons\' common edges with exactly viewport-sized quads',
+ es3fMultisampleTests.CommonEdgeCase.CaseType.FIT_VIEWPORT_QUAD,
+ numFboSamples));
+
+ group.addChild(new es3fMultisampleTests.SampleDepthCase(
+ 'depth',
+ 'Test that depth values are per-sample',
+ numFboSamples));
+
+ group.addChild(new es3fMultisampleTests.SampleStencilCase(
+ 'stencil',
+ 'Test that stencil values are per-sample',
+ numFboSamples));
+
+ group.addChild(new es3fMultisampleTests.CoverageMaskInvertCase(
+ 'sample_coverage_invert',
+ 'Test that non-inverted and inverted sample coverage masks are each other\'s negations',
+ numFboSamples));
+
+
+ group.addChild(new es3fMultisampleTests.MaskProportionalityCase(
+ 'proportionality_alpha_to_coverage',
+ 'Test the proportionality property of GL_SAMPLE_ALPHA_TO_COVERAGE',
+ es3fMultisampleTests.MaskProportionalityCase.CaseType.ALPHA_TO_COVERAGE,
+ numFboSamples));
+ group.addChild(new es3fMultisampleTests.MaskProportionalityCase(
+ 'proportionality_sample_coverage',
+ 'Test the proportionality property of GL_SAMPLE_COVERAGE',
+ es3fMultisampleTests.MaskProportionalityCase.CaseType.SAMPLE_COVERAGE,
+ numFboSamples));
+ group.addChild(new es3fMultisampleTests.MaskProportionalityCase(
+ 'proportionality_sample_coverage_inverted',
+ 'Test the proportionality property of inverted-mask GL_SAMPLE_COVERAGE',
+ es3fMultisampleTests.MaskProportionalityCase.CaseType.SAMPLE_COVERAGE_INVERTED,
+ numFboSamples));
+
+ group.addChild(new es3fMultisampleTests.MaskConstancyCase(
+ 'constancy_alpha_to_coverage',
+ 'Test that coverage mask is constant at given coordinates with a given alpha or coverage value, using GL_SAMPLE_ALPHA_TO_COVERAGE',
+ es3fMultisampleTests.MaskConstancyCase.CaseType.ALPHA_TO_COVERAGE,
+ numFboSamples));
+ group.addChild(new es3fMultisampleTests.MaskConstancyCase(
+ 'constancy_sample_coverage',
+ 'Test that coverage mask is constant at given coordinates with a given alpha or coverage value, using GL_SAMPLE_COVERAGE',
+ es3fMultisampleTests.MaskConstancyCase.CaseType.SAMPLE_COVERAGE,
+ numFboSamples));
+ group.addChild(new es3fMultisampleTests.MaskConstancyCase(
+ 'constancy_sample_coverage_inverted',
+ 'Test that coverage mask is constant at given coordinates with a given alpha or coverage value, using inverted-mask GL_SAMPLE_COVERAGE',
+ es3fMultisampleTests.MaskConstancyCase.CaseType.SAMPLE_COVERAGE_INVERTED,
+ numFboSamples));
+ group.addChild(new es3fMultisampleTests.MaskConstancyCase(
+ 'constancy_both',
+ 'Test that coverage mask is constant at given coordinates with a given alpha or coverage value, using GL_SAMPLE_ALPHA_TO_COVERAGE and GL_SAMPLE_COVERAGE',
+ es3fMultisampleTests.MaskConstancyCase.CaseType.BOTH,
+ numFboSamples));
+ group.addChild(new es3fMultisampleTests.MaskConstancyCase(
+ 'constancy_both_inverted',
+ 'Test that coverage mask is constant at given coordinates with a given alpha or coverage value, using GL_SAMPLE_ALPHA_TO_COVERAGE and inverted-mask GL_SAMPLE_COVERAGE',
+ es3fMultisampleTests.MaskConstancyCase.CaseType.BOTH_INVERTED,
+ numFboSamples));
+ }
+ };
+
+ /**
+ * Run test
+ * @param {WebGL2RenderingContext} context
+ */
+ es3fMultisampleTests.run = function(context) {
+ gl = context;
+ //Set up Test Root parameters
+ var testName = 'multisample';
+ var testDescription = 'Multisample Tests';
+ var state = tcuTestCase.runner;
+
+ state.testName = testName;
+ state.setRoot(tcuTestCase.newTest(testName, testDescription, null));
+
+ //Set up name and description of this test series.
+ setCurrentTestName(testName);
+ description(testDescription);
+
+ try {
+ //Create test cases
+ es3fMultisampleTests.init();
+ //Run test cases
+ tcuTestCase.runTestCases();
+ }
+ catch (err) {
+ testFailedOptions('Failed to es3fMultisampleTests.run tests', false);
+ tcuTestCase.runner.terminate();
+ }
+ };
+
+});