tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

es3fMultisampleTests.js (82373B)


      1 /*-------------------------------------------------------------------------
      2 * drawElements Quality Program OpenGL ES Utilities
      3 * ------------------------------------------------
      4 *
      5 * Copyright 2014 The Android Open Source Project
      6 *
      7 * Licensed under the Apache License, Version 2.0 (the "License");
      8 * you may not use this file except in compliance with the License.
      9 * You may obtain a copy of the License at
     10 *
     11 *      http://www.apache.org/licenses/LICENSE-2.0
     12 *
     13 * Unless required by applicable law or agreed to in writing, software
     14 * distributed under the License is distributed on an "AS IS" BASIS,
     15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     16 * See the License for the specific language governing permissions and
     17 * limitations under the License.
     18 *
     19 */
     20 
     21 'use strict';
     22 goog.provide('functional.gles3.es3fMultisampleTests');
     23 goog.require('framework.common.tcuTestCase');
     24 goog.require('framework.delibs.debase.deMath');
     25 goog.require('framework.delibs.debase.deRandom');
     26 goog.require('framework.delibs.debase.deString');
     27 goog.require('framework.opengl.gluShaderProgram');
     28 goog.require('framework.opengl.gluStrUtil');
     29 goog.require('framework.opengl.gluTextureUtil');
     30 goog.require('framework.common.tcuImageCompare');
     31 goog.require('framework.common.tcuLogImage');
     32 goog.require('framework.common.tcuRGBA');
     33 goog.require('framework.common.tcuSurface');
     34 goog.require('framework.common.tcuTexture');
     35 goog.require('modules.shared.glsTextureTestUtil');
     36 
     37 goog.scope(function() {
     38    /** @type {?WebGL2RenderingContext} */ var gl;
     39    var es3fMultisampleTests = functional.gles3.es3fMultisampleTests;
     40    var deMath = framework.delibs.debase.deMath;
     41    var deRandom = framework.delibs.debase.deRandom;
     42    var deString = framework.delibs.debase.deString;
     43    var gluShaderProgram = framework.opengl.gluShaderProgram;
     44    var tcuRGBA = framework.common.tcuRGBA;
     45    var tcuSurface = framework.common.tcuSurface;
     46    var tcuTestCase = framework.common.tcuTestCase;
     47    var tcuTexture = framework.common.tcuTexture;
     48    var gluStrUtil = framework.opengl.gluStrUtil;
     49    var glsTextureTestUtil = modules.shared.glsTextureTestUtil;
     50    var tcuImageCompare = framework.common.tcuImageCompare;
     51    var gluTextureUtil = framework.opengl.gluTextureUtil;
     52    var tcuLogImage = framework.common.tcuLogImage;
     53 
     54    /**
     55     * @constructor
     56     * @struct
     57     * @param {Array<number>} p0_
     58     * @param {Array<number>} p1_
     59     * @param {Array<number>} p2_
     60     * @param {Array<number>} p3_
     61     */
     62    es3fMultisampleTests.QuadCorners = function(p0_, p1_, p2_, p3_) {
     63        /** @type {Array<number>} */ this.p0 = p0_;
     64        /** @type {Array<number>} */ this.p1 = p1_;
     65        /** @type {Array<number>} */ this.p2 = p2_;
     66        /** @type {Array<number>} */ this.p3 = p3_;
     67    };
     68 
     69    /**
     70     * @param {number} defaultCount
     71     * @return {number}
     72     */
     73    es3fMultisampleTests.getIterationCount = function(defaultCount) {
     74        // The C++ test takes an argument from the command line.
     75        // Leaving this function in case we want to be able to take an argument from the URL
     76        return defaultCount;
     77    };
     78 
     79    /**
     80     * @param  {Array<number>} point
     81     * @param  {Array<number>} p0
     82     * @param  {Array<number>} p1
     83     * @param  {Array<number>} p2
     84     * @param  {Array<number>} p3
     85     * @return {boolean}
     86     */
     87    es3fMultisampleTests.isInsideQuad = function(point, p0, p1, p2, p3) {
     88        /** @type {number} */ var dot0 = (point[0] - p0[0]) * (p1[1] - p0[1]) + (point[1] - p0[1]) * (p0[0] - p1[0]);
     89        /** @type {number} */ var dot1 = (point[0] - p1[0]) * (p2[1] - p1[1]) + (point[1] - p1[1]) * (p1[0] - p2[0]);
     90        /** @type {number} */ var dot2 = (point[0] - p2[0]) * (p3[1] - p2[1]) + (point[1] - p2[1]) * (p2[0] - p3[0]);
     91        /** @type {number} */ var dot3 = (point[0] - p3[0]) * (p0[1] - p3[1]) + (point[1] - p3[1]) * (p3[0] - p0[0]);
     92 
     93        return (dot0 > 0) == (dot1 > 0) && (dot1 > 0) == (dot2 > 0) && (dot2 > 0) == (dot3 > 0);
     94    };
     95 
     96    /**
     97     * Check if a region in an image is unicolored.
     98     *
     99     * Checks if the pixels in img inside the convex quadilateral defined by
    100     * p0, p1, p2 and p3 are all (approximately) of the same color.
    101     *
    102     * @param  {tcuSurface.Surface} img
    103     * @param  {Array<number>} p0
    104     * @param  {Array<number>} p1
    105     * @param  {Array<number>} p2
    106     * @param  {Array<number>} p3
    107     * @return {boolean}
    108     */
    109    es3fMultisampleTests.isPixelRegionUnicolored = function(img, p0, p1, p2, p3) {
    110        /** @type {number} */ var xMin = deMath.clamp(Math.min(p0[0], p1[0], p2[0], p3[0]), 0, img.getWidth() - 1);
    111        /** @type {number} */ var yMin = deMath.clamp(Math.min(p0[1], p1[1], p2[1], p3[1]), 0, img.getHeight() - 1);
    112        /** @type {number} */ var xMax = deMath.clamp(Math.max(p0[0], p1[0], p2[0], p3[0]), 0, img.getWidth() - 1);
    113        /** @type {number} */ var yMax = deMath.clamp(Math.max(p0[1], p1[1], p2[1], p3[1]), 0, img.getHeight() - 1);
    114        /** @type {boolean} */ var insideEncountered = false; //!< Whether we have already seen at least one pixel inside the region.
    115        /** @type {tcuRGBA.RGBA} */ var insideColor; //!< Color of the first pixel inside the region.
    116        /** @type {tcuRGBA.RGBA} */ var threshold = tcuRGBA.newRGBAComponents(3, 3, 3, 3);
    117        for (var y = yMin; y <= yMax; y++)
    118        for (var x = xMin; x <= xMax; x++)
    119            if (es3fMultisampleTests.isInsideQuad([x, y], p0, p1, p2, p3)) {
    120                /** @type {tcuRGBA.RGBA} */ var pixColor = new tcuRGBA.RGBA(img.getPixel(x, y));
    121 
    122                if (insideEncountered)
    123                    if (!tcuRGBA.compareThreshold(pixColor, insideColor, threshold)) // Pixel color differs from already-detected color inside same region - region not unicolored.
    124                        return false;
    125                else {
    126                    insideEncountered = true;
    127                    insideColor = pixColor;
    128                }
    129            }
    130        return true;
    131    };
    132 
    133    /**
    134     * [drawUnicolorTestErrors description]
    135     * @param  {tcuSurface.Surface} img
    136     * @param  {tcuTexture.PixelBufferAccess} errorImg
    137     * @param  {Array<number>} p0
    138     * @param  {Array<number>} p1
    139     * @param  {Array<number>} p2
    140     * @param  {Array<number>} p3
    141     * @return {boolean}
    142     */
    143    es3fMultisampleTests.drawUnicolorTestErrors = function(img, errorImg, p0, p1, p2, p3) {
    144        /** @type {number} */ var xMin = deMath.clamp(Math.min(p0[0], p1[0], p2[0], p3[0]), 0, img.getWidth() - 1);
    145        /** @type {number} */ var yMin = deMath.clamp(Math.min(p0[1], p1[1], p2[1], p3[1]), 0, img.getHeight() - 1);
    146        /** @type {number} */ var xMax = deMath.clamp(Math.max(p0[0], p1[0], p2[0], p3[0]), 0, img.getWidth() - 1);
    147        /** @type {number} */ var yMax = deMath.clamp(Math.max(p0[1], p1[1], p2[1], p3[1]), 0, img.getHeight() - 1);
    148        /** @type {tcuRGBA.RGBA} */ var refColor = new tcuRGBA.RGBA(img.getPixel(Math.floor((xMin + xMax) / 2), Math.floor((yMin + yMax) / 2)));
    149        /** @type {tcuRGBA.RGBA} */ var threshold = tcuRGBA.newRGBAComponents(3, 3, 3, 3);
    150        for (var y = yMin; y <= yMax; y++)
    151        for (var x = xMin; x <= xMax; x++)
    152            if (es3fMultisampleTests.isInsideQuad([x, y], p0, p1, p2, p3)) {
    153                if (!tcuRGBA.compareThreshold(new tcuRGBA.RGBA(img.getPixel(x, y)), refColor, threshold)) {
    154                    img.setPixel(x, y, tcuRGBA.RGBA.red.toVec()); // TODO: this might also be toIVec()
    155                    errorImg.setPixel([1.0, 0.0, 0.0, 1.0], x, y);
    156                }
    157            }
    158        return true;
    159    };
    160 
    161    /**
    162     * @constructor
    163     * @struct
    164     * @param {number=} numSamples_
    165     * @param {boolean=} useDepth_
    166     * @param {boolean=} useStencil_
    167     */
    168    es3fMultisampleTests.FboParams = function(numSamples_, useDepth_, useStencil_) {
    169        /** @type {boolean} */ var useFbo_ = true;
    170        if (numSamples_ === undefined && useDepth_ === undefined && useStencil_ === undefined)
    171            useFbo_ = false;
    172        /** @type {boolean} */ this.useFbo = useFbo_;
    173        /** @type {number} */ this.numSamples = numSamples_ === undefined ? -1 : numSamples_;
    174        /** @type {boolean} */ this.useDepth = useDepth_ === undefined ? false : useDepth_;
    175        /** @type {boolean} */ this.useStencil = useStencil_ === undefined ? false : useStencil_;
    176 
    177    };
    178 
    179    /**
    180     * @constructor
    181     * @extends {tcuTestCase.DeqpTest}
    182     * @param {string} name
    183     * @param {string} desc
    184     * @param {number} desiredViewportSize
    185     * @param {es3fMultisampleTests.FboParams} fboParams
    186     */
    187    es3fMultisampleTests.MultisampleCase = function(name, desc, desiredViewportSize, fboParams) {
    188        tcuTestCase.DeqpTest.call(this, name, desc);
    189        /** @type {number} */ this.m_numSamples = 0;
    190        /** @type {number} */ this.m_viewportSize = 0;
    191        /** @type {number} */ this.m_desiredViewportSize = desiredViewportSize;
    192        /** @type {es3fMultisampleTests.FboParams} */ this.m_fboParams = fboParams;
    193        /** @type {WebGLRenderbuffer} */ this.m_msColorRbo = null;
    194        /** @type {WebGLRenderbuffer} */ this.m_msDepthStencilRbo = null;
    195        /** @type {WebGLRenderbuffer} */ this.m_resolveColorRbo = null;
    196        /** @type {WebGLFramebuffer} */ this.m_msFbo = null;
    197        /** @type {WebGLFramebuffer} */ this.m_resolveFbo = null;
    198        /** @type {gluShaderProgram.ShaderProgram} */ this.m_program = null;
    199        /** @type {number} */ this.m_attrPositionLoc = -1;
    200        /** @type {number} */ this.m_attrColorLoc = -1;
    201        /** @type {number} */ this.m_renderWidth = fboParams.useFbo ? 2 * desiredViewportSize : gl.drawingBufferWidth;
    202        /** @type {number} */ this.m_renderHeight = fboParams.useFbo ? 2 * desiredViewportSize : gl.drawingBufferHeight;
    203        /** @type {number} */ this.m_viewportX = 0;
    204        /** @type {number} */ this.m_viewportY = 0;
    205        /** @type {deRandom.Random} */ this.m_rnd = new deRandom.Random(deString.deStringHash(this.name));
    206        if (this.m_fboParams.useFbo)
    207            assertMsgOptions(this.m_fboParams.numSamples >= 0, 'fboParams.numSamples < 0', false, true);
    208    };
    209 
    210    es3fMultisampleTests.MultisampleCase.prototype = Object.create(tcuTestCase.DeqpTest.prototype);
    211 
    212    /** Copy the constructor */
    213    es3fMultisampleTests.MultisampleCase.prototype.constructor = es3fMultisampleTests.MultisampleCase;
    214 
    215    /* Rest states */
    216    es3fMultisampleTests.MultisampleCase.prototype.deinit = function() {
    217        gl.colorMask(true, true, true, true);
    218        gl.depthMask(true);
    219 
    220        gl.clearColor(0.0, 0.0, 0.0, 0.0);
    221        gl.clearDepth(1.0);
    222        gl.clearStencil(0);
    223 
    224        gl.disable(gl.STENCIL_TEST);
    225        gl.disable(gl.DEPTH_TEST);
    226        gl.disable(gl.BLEND)
    227        gl.disable(gl.SAMPLE_COVERAGE);
    228        gl.disable(gl.SAMPLE_ALPHA_TO_COVERAGE);
    229 
    230        if (this.m_program) {
    231            gl.deleteProgram(this.m_program.getProgram());
    232            this.m_program = null;
    233        }
    234        if (this.m_msColorRbo) {
    235          gl.deleteRenderbuffer(this.m_msColorRbo);
    236          this.m_msColorRbo = null;
    237        }
    238        if (this.m_msDepthStencilRbo) {
    239          gl.deleteRenderbuffer(this.m_msDepthStencilRbo);
    240          this.m_msDepthStencilRbo = null;
    241        }
    242        if (this.m_resolveColorRbo) {
    243          gl.deleteRenderbuffer(this.m_resolveColorRbo);
    244          this.m_resolveColorRbo = null;
    245        }
    246 
    247        if (this.m_msFbo) {
    248          gl.deleteFramebuffer(this.m_msFbo);
    249          this.m_msFbo = null;
    250        }
    251        if (this.m_resolveFbo) {
    252          gl.deleteFramebuffer(this.m_resolveFbo);
    253          this.m_resolveFbo = null;
    254        }
    255 
    256        gl.bindRenderbuffer(gl.RENDERBUFFER, null);
    257        gl.bindFramebuffer(gl.FRAMEBUFFER, null);
    258    }
    259 
    260    /**
    261     * @protected
    262     * @param  {Array<number>} p0
    263     * @param  {Array<number>} p1
    264     * @param  {Array<number>} p2
    265     * @param  {Array<number>} c0
    266     * @param  {Array<number>} c1
    267     * @param  {Array<number>} c2
    268     */
    269    es3fMultisampleTests.MultisampleCase.prototype.renderTriangle_pAsVec3cAsVec4 = function(p0, p1, p2, c0, c1, c2) {
    270        /** @type {Array<number>} */ var vertexPositions = [
    271            p0[0], p0[1], p0[2], 1.0,
    272            p1[0], p1[1], p1[2], 1.0,
    273            p2[0], p2[1], p2[2], 1.0
    274        ];
    275        /** @type {Array<number>} */ var vertexColors = [
    276            c0[0], c0[1], c0[2], c0[3],
    277            c1[0], c1[1], c1[2], c1[3],
    278            c2[0], c2[1], c2[2], c2[3]
    279        ];
    280 
    281        var posGLBuffer = gl.createBuffer();
    282        /** @type {ArrayBufferView} */ var posBuffer = new Float32Array(vertexPositions);
    283        gl.bindBuffer(gl.ARRAY_BUFFER, posGLBuffer);
    284        gl.bufferData(gl.ARRAY_BUFFER, posBuffer, gl.STATIC_DRAW);
    285 
    286        gl.enableVertexAttribArray(this.m_attrPositionLoc);
    287        gl.vertexAttribPointer(this.m_attrPositionLoc, 4, gl.FLOAT, false, 0, 0);
    288 
    289        var colGLBuffer = gl.createBuffer();
    290        /** @type {ArrayBufferView} */ var colBuffer = new Float32Array(vertexColors);
    291        gl.bindBuffer(gl.ARRAY_BUFFER, colGLBuffer);
    292        gl.bufferData(gl.ARRAY_BUFFER, colBuffer, gl.STATIC_DRAW);
    293 
    294        gl.enableVertexAttribArray(this.m_attrColorLoc);
    295        gl.vertexAttribPointer(this.m_attrColorLoc, 4, gl.FLOAT, false, 0, 0);
    296 
    297        gl.useProgram(this.m_program.getProgram());
    298        gl.drawArrays(gl.TRIANGLES, 0, 3);
    299 
    300        gl.bindBuffer(gl.ARRAY_BUFFER, null);
    301        gl.deleteBuffer(colGLBuffer);
    302        gl.deleteBuffer(posGLBuffer);
    303    };
    304 
    305    /**
    306     * @protected
    307     * @param  {Array<number>} p0
    308     * @param  {Array<number>} p1
    309     * @param  {Array<number>} p2
    310     * @param  {Array<number>} color
    311     */
    312    es3fMultisampleTests.MultisampleCase.prototype.renderTriangle_pAsVec3WithColor = function(p0, p1, p2, color) {
    313        this.renderTriangle_pAsVec3cAsVec4(p0, p1, p2, color, color, color);
    314    };
    315 
    316    /**
    317     * @protected
    318     * @param  {Array<number>} p0
    319     * @param  {Array<number>} p1
    320     * @param  {Array<number>} p2
    321     * @param  {Array<number>} c0
    322     * @param  {Array<number>} c1
    323     * @param  {Array<number>} c2
    324     */
    325    es3fMultisampleTests.MultisampleCase.prototype.renderTriangle_pAsVec2 = function(p0, p1, p2, c0, c1, c2) {
    326        this.renderTriangle_pAsVec3cAsVec4(
    327            [p0[0], p0[1], 0.0],
    328            [p1[0], p1[1], 0.0],
    329            [p2[0], p2[1], 0.0],
    330            c0, c1, c2);
    331    };
    332 
    333    /**
    334     * @protected
    335     * @param  {Array<number>} p0
    336     * @param  {Array<number>} p1
    337     * @param  {Array<number>} p2
    338     * @param  {Array<number>} color
    339     */
    340    es3fMultisampleTests.MultisampleCase.prototype.renderTriangle_pAsVec2WithColor = function(p0, p1, p2, color) {
    341        this.renderTriangle_pAsVec2(p0, p1, p2, color, color, color);
    342    };
    343 
    344    /**
    345     * @protected
    346     * @param  {Array<number>} p0
    347     * @param  {Array<number>} p1
    348     * @param  {Array<number>} p2
    349     * @param  {Array<number>} p3
    350     * @param  {Array<number>} c0
    351     * @param  {Array<number>} c1
    352     * @param  {Array<number>} c2
    353     * @param  {Array<number>} c3
    354     */
    355    es3fMultisampleTests.MultisampleCase.prototype.renderQuad = function(p0, p1, p2, p3, c0, c1, c2, c3) {
    356        this.renderTriangle_pAsVec2(p0, p1, p2, c0, c1, c2);
    357        this.renderTriangle_pAsVec2(p2, p1, p3, c2, c1, c3);
    358    };
    359 
    360    /**
    361     * @protected
    362     * @param  {Array<number>} p0
    363     * @param  {Array<number>} p1
    364     * @param  {Array<number>} p2
    365     * @param  {Array<number>} p3
    366     * @param  {Array<number>} color
    367     */
    368    es3fMultisampleTests.MultisampleCase.prototype.renderQuad_WithColor = function(p0, p1, p2, p3, color) {
    369        this.renderQuad(p0, p1, p2, p3, color, color, color, color);
    370    };
    371 
    372    /**
    373     * @protected
    374     * @param  {Array<number>} p0
    375     * @param  {Array<number>} p1
    376     * @param  {Array<number>} color
    377     */
    378    es3fMultisampleTests.MultisampleCase.prototype.renderLine = function(p0, p1, color) {
    379        /** @type {Array<number>} */ var vertexPositions = [
    380            p0[0], p0[1], 0.0, 1.0,
    381            p1[0], p1[1], 0.0, 1.0
    382        ];
    383        /** @type {Array<number>} */ var vertexColors = [
    384            color[0], color[1], color[2], color[3],
    385            color[0], color[1], color[2], color[3]
    386        ];
    387 
    388        var posGLBuffer = gl.createBuffer();
    389        /** @type {ArrayBufferView} */ var posBuffer = new Float32Array(vertexPositions);
    390        gl.bindBuffer(gl.ARRAY_BUFFER, posGLBuffer);
    391        gl.bufferData(gl.ARRAY_BUFFER, posBuffer, gl.STATIC_DRAW);
    392 
    393        gl.enableVertexAttribArray(this.m_attrPositionLoc);
    394        gl.vertexAttribPointer(this.m_attrPositionLoc, 4, gl.FLOAT, false, 0, 0);
    395 
    396        var colGLBuffer = gl.createBuffer();
    397        /** @type {ArrayBufferView} */ var colBuffer = new Float32Array(vertexColors);
    398        gl.bindBuffer(gl.ARRAY_BUFFER, colGLBuffer);
    399        gl.bufferData(gl.ARRAY_BUFFER, colBuffer, gl.STATIC_DRAW);
    400 
    401        gl.enableVertexAttribArray(this.m_attrColorLoc);
    402        gl.vertexAttribPointer(this.m_attrColorLoc, 4, gl.FLOAT, false, 0, 0);
    403 
    404        gl.useProgram(this.m_program.getProgram());
    405        gl.drawArrays(gl.LINES, 0, 2);
    406 
    407        gl.bindBuffer(gl.ARRAY_BUFFER, null);
    408        gl.deleteBuffer(colGLBuffer);
    409        gl.deleteBuffer(posGLBuffer);
    410    };
    411 
    412    /**
    413     * @protected
    414     */
    415    es3fMultisampleTests.MultisampleCase.prototype.randomizeViewport = function() {
    416        this.m_viewportX = this.m_rnd.getInt(0, this.m_renderWidth - this.m_viewportSize);
    417        this.m_viewportY = this.m_rnd.getInt(0, this.m_renderHeight - this.m_viewportSize);
    418 
    419        gl.viewport(this.m_viewportX, this.m_viewportY, this.m_viewportSize, this.m_viewportSize);
    420    };
    421 
    422    /**
    423     * @protected
    424     * @return {tcuSurface.Surface}
    425     */
    426    es3fMultisampleTests.MultisampleCase.prototype.readImage = function() {
    427        /** @type {tcuSurface.Surface} */
    428        var dst = new tcuSurface.Surface(this.m_viewportSize, this.m_viewportSize);
    429        /** @type {number} */ var pixelSize = dst.getAccess().getFormat().getPixelSize();
    430        /** @type {number} */ var param = deMath.deIsPowerOfTwo32(pixelSize) ? Math.min(pixelSize, 8) : 1;
    431        /** @type {gluTextureUtil.TransferFormat} */ var format = gluTextureUtil.getTransferFormat(dst.getAccess().getFormat());
    432        /** @type {number} */ var width = dst.getAccess().getWidth();
    433        /** @type {number} */ var height = dst.getAccess().getHeight();
    434        if (this.m_fboParams.useFbo) {
    435            gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, this.m_resolveFbo);
    436            gl.blitFramebuffer(0, 0, this.m_renderWidth, this.m_renderHeight, 0, 0, this.m_renderWidth, this.m_renderHeight, gl.COLOR_BUFFER_BIT, gl.NEAREST);
    437            gl.bindFramebuffer(gl.READ_FRAMEBUFFER, this.m_resolveFbo);
    438 
    439            gl.pixelStorei(gl.PACK_ALIGNMENT, param);
    440            gl.readPixels(this.m_viewportX, this.m_viewportY, width, height, format.format, format.dataType, dst.getAccess().getDataPtr());
    441 
    442            gl.bindFramebuffer(gl.FRAMEBUFFER, this.m_msFbo);
    443        }
    444        else {
    445            gl.pixelStorei(gl.PACK_ALIGNMENT, param);
    446            gl.readPixels(this.m_viewportX, this.m_viewportY, width, height, format.format, format.dataType, dst.getAccess().getDataPtr());
    447        }
    448        return dst;
    449    };
    450 
    451    es3fMultisampleTests.MultisampleCase.prototype.init = function() {
    452        /** @type {string} */ var vertShaderSource = '' +
    453            '#version 300 es\n' +
    454            'in highp vec4 a_position;\n' +
    455            'in mediump vec4 a_color;\n' +
    456            'out mediump vec4 v_color;\n' +
    457            'void main()\n' +
    458            '{\n' +
    459            '    gl_Position = a_position;\n' +
    460            '    v_color = a_color;\n' +
    461            '}\n';
    462 
    463        /** @type {string} */ var fragShaderSource = '' +
    464            '#version 300 es\n' +
    465            'in mediump vec4 v_color;\n' +
    466            'layout(location = 0) out mediump vec4 o_color;\n' +
    467            'void main()\n' +
    468            '{\n' +
    469            '    o_color = v_color;\n' +
    470            '}\n';
    471 
    472        var numSamples = /** @type {number} */  (gl.getParameter(gl.SAMPLES));
    473        if (!this.m_fboParams.useFbo && numSamples <= 1) {
    474            var msg = 'No multisample buffers';
    475            checkMessage(false, msg);
    476            return false;
    477        }
    478 
    479        if (this.m_fboParams.useFbo) {
    480            if (this.m_fboParams.numSamples > 0)
    481                this.m_numSamples = this.m_fboParams.numSamples;
    482            else {
    483                bufferedLogToConsole('Querying maximum number of samples for ' + gluStrUtil.getPixelFormatName(gl.RGBA8) + ' with gl.getInternalformatParameter()');
    484                var supportedSampleCountArray = /** @type {Int32Array} */ (gl.getInternalformatParameter(gl.RENDERBUFFER, gl.RGBA8, gl.SAMPLES));
    485                if (supportedSampleCountArray.length == 0) {
    486                    var msg = 'No supported sample counts';
    487                    checkMessage(false, msg);
    488                    return false;
    489                }
    490                this.m_numSamples = supportedSampleCountArray[0];
    491            }
    492 
    493            bufferedLogToConsole('Using FBO of size (' + this.m_renderWidth + ', ' + this.m_renderHeight + ') with ' + this.m_numSamples + ' samples');
    494        }
    495        else {
    496            // Query and log number of samples per pixel.
    497            this.m_numSamples =  numSamples;
    498            bufferedLogToConsole('gl.SAMPLES =' + this.m_numSamples);
    499        }
    500 
    501        // Prepare program.
    502 
    503        assertMsgOptions(!this.m_program, 'Program loaded when it should not be.', false, true);
    504 
    505        this.m_program = new gluShaderProgram.ShaderProgram(
    506            gl,
    507            gluShaderProgram.makeVtxFragSources(vertShaderSource, fragShaderSource));
    508 
    509        if (!this.m_program.isOk())
    510            throw new Error('Failed to compile program');
    511 
    512        this.m_attrPositionLoc = gl.getAttribLocation(this.m_program.getProgram(), 'a_position');
    513        this.m_attrColorLoc = gl.getAttribLocation(this.m_program.getProgram(), 'a_color');
    514 
    515        if (this.m_attrPositionLoc < 0 || this.m_attrColorLoc < 0) {
    516            this.m_program = null;
    517            throw new Error('Invalid attribute locations');
    518        }
    519 
    520        if (this.m_fboParams.useFbo) {
    521            // Setup ms color RBO.
    522            this.m_msColorRbo = gl.createRenderbuffer();
    523            gl.bindRenderbuffer(gl.RENDERBUFFER, this.m_msColorRbo);
    524 
    525            /** @type {Int32Array} */ var supportedSampleCountArray = /** @type {Int32Array} */ (gl.getInternalformatParameter(gl.RENDERBUFFER, gl.RGBA8, gl.SAMPLES));
    526            var maxSampleCount = supportedSampleCountArray[0];
    527            if (maxSampleCount < this.m_numSamples) {
    528                bufferedLogToConsole('skipping test: ' + this.m_numSamples + ' samples not supported; max is ' + maxSampleCount);
    529                return false;
    530            }
    531 
    532            assertMsgOptions(gl.getError() === gl.NO_ERROR, 'should be no GL error before renderbufferStorageMultisample');
    533            gl.renderbufferStorageMultisample(gl.RENDERBUFFER, this.m_numSamples, gl.RGBA8, this.m_renderWidth, this.m_renderHeight);
    534            assertMsgOptions(gl.getError() === gl.NO_ERROR, 'should be no GL error after renderbufferStorageMultisample');
    535 
    536 
    537            if (this.m_fboParams.useDepth || this.m_fboParams.useStencil) {
    538                // Setup ms depth & stencil RBO.
    539                this.m_msDepthStencilRbo = gl.createRenderbuffer();
    540                gl.bindRenderbuffer(gl.RENDERBUFFER, this.m_msDepthStencilRbo);
    541                gl.renderbufferStorageMultisample(gl.RENDERBUFFER, this.m_numSamples, gl.DEPTH24_STENCIL8, this.m_renderWidth, this.m_renderHeight);
    542            }
    543 
    544            // Setup ms FBO.
    545            this.m_msFbo = gl.createFramebuffer();
    546            gl.bindFramebuffer(gl.FRAMEBUFFER, this.m_msFbo);
    547            gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, this.m_msColorRbo);
    548            gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, this.m_msDepthStencilRbo);
    549 
    550            // Setup resolve color RBO.
    551            this.m_resolveColorRbo = gl.createRenderbuffer();
    552            gl.bindRenderbuffer(gl.RENDERBUFFER, this.m_resolveColorRbo);
    553            gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA8, this.m_renderWidth, this.m_renderHeight);
    554 
    555            // Setup resolve FBO.
    556            this.m_resolveFbo = gl.createFramebuffer();
    557            gl.bindFramebuffer(gl.FRAMEBUFFER, this.m_resolveFbo);
    558            gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, this.m_resolveColorRbo);
    559 
    560            // Use ms FBO.
    561            gl.bindFramebuffer(gl.FRAMEBUFFER, this.m_msFbo);
    562        }
    563 
    564        // Get suitable viewport size.
    565 
    566        this.m_viewportSize = Math.min(this.m_desiredViewportSize, this.m_renderWidth, this.m_renderHeight);
    567        this.randomizeViewport();
    568        return true;
    569    };
    570 
    571    /**
    572     * Base class for cases testing the value of sample count.
    573     *
    574     * Draws a test pattern (defined by renderPattern() of an inheriting class)
    575     * and counts the number of distinct colors in the resulting image. That
    576     * number should be at least the value of sample count plus one. This is
    577     * repeated with increased values of m_currentIteration until this correct
    578     * number of colors is detected or m_currentIteration reaches
    579     * m_maxNumIterations.
    580     *
    581     * @extends {es3fMultisampleTests.MultisampleCase}
    582     * @constructor
    583     * @param {string} name
    584     * @param {string} desc
    585     * @param {es3fMultisampleTests.FboParams} fboParams
    586     */
    587    es3fMultisampleTests.NumSamplesCase = function(name, desc, fboParams) {
    588        es3fMultisampleTests.MultisampleCase.call(this, name, desc, 256, fboParams);
    589        /** @type {number} */ var DEFAULT_MAX_NUM_ITERATIONS = 16;
    590        /** @type {number} */ this.m_currentIteration = 0;
    591        /** @type {number} */ this.m_maxNumIterations = es3fMultisampleTests.getIterationCount(DEFAULT_MAX_NUM_ITERATIONS);
    592        /** @type {Array<tcuRGBA.RGBA>} */ this.m_detectedColors = [];
    593    };
    594 
    595    es3fMultisampleTests.NumSamplesCase.prototype = Object.create(es3fMultisampleTests.MultisampleCase.prototype);
    596 
    597    /** Copy the constructor */
    598    es3fMultisampleTests.NumSamplesCase.prototype.constructor = es3fMultisampleTests.NumSamplesCase;
    599 
    600    /**
    601     * @return {tcuTestCase.IterateResult}
    602     */
    603    es3fMultisampleTests.NumSamplesCase.prototype.iterate = function() {
    604        this.randomizeViewport();
    605 
    606        gl.clearColor(0.0, 0.0, 0.0, 1.0);
    607        gl.clear(gl.COLOR_BUFFER_BIT);
    608 
    609        this.renderPattern();
    610 
    611        // Read and log rendered image.
    612 
    613        /** @type {tcuSurface.Surface} */ var renderedImg = this.readImage();
    614        tcuLogImage.logImage('RenderedImage', 'Rendered image', renderedImg.getAccess());
    615 
    616        // Detect new, previously unseen colors from image.
    617 
    618        /** @type {number} */ var requiredNumDistinctColors = this.m_numSamples + 1;
    619 
    620        // If the number of samples is high (64 or more), we need to lower the threshold for detecting unique colors, otherwise two expected unique colors would be treated as the same color.
    621        var threshold = Math.min(3, Math.floor(255 / this.m_numSamples) - 1);
    622        var thresholdRGBA = tcuRGBA.newRGBAComponents(threshold, threshold, threshold, threshold);
    623 
    624        for (var y = 0; y < renderedImg.getHeight() && this.m_detectedColors.length < requiredNumDistinctColors; y++)
    625        for (var x = 0; x < renderedImg.getWidth() && this.m_detectedColors.length < requiredNumDistinctColors; x++) {
    626            /** @type {tcuRGBA.RGBA} */ var color = new tcuRGBA.RGBA(renderedImg.getPixel(x, y));
    627 
    628            /** @type {number} */ var i;
    629            for (i = 0; i < this.m_detectedColors.length; i++) {
    630                if (tcuRGBA.compareThreshold(color, this.m_detectedColors[i], thresholdRGBA))
    631                    break;
    632            }
    633 
    634            if (i === this.m_detectedColors.length)
    635                this.m_detectedColors.push(color); // Color not previously detected.
    636        }
    637 
    638        // Log results.
    639 
    640            bufferedLogToConsole('Number of distinct colors detected so far: ' + (this.m_detectedColors.length >= requiredNumDistinctColors ? 'at least ' : '') + this.m_detectedColors.length);
    641 
    642 
    643        if (this.m_detectedColors.length < requiredNumDistinctColors) {
    644            // Haven't detected enough different colors yet.
    645 
    646            this.m_currentIteration++;
    647 
    648            if (this.m_currentIteration >= this.m_maxNumIterations) {
    649                testFailedOptions('Failure: Number of distinct colors detected is lower than sample count+1', false);
    650                return tcuTestCase.IterateResult.STOP;
    651            }
    652            else {
    653                bufferedLogToConsole('The number of distinct colors detected is lower than sample count+1 - trying again with a slightly altered pattern');
    654                return tcuTestCase.IterateResult.CONTINUE;
    655            }
    656        }
    657        else {
    658            testPassedOptions('Success: The number of distinct colors detected is at least sample count+1', true);
    659            return tcuTestCase.IterateResult.STOP;
    660        }
    661    };
    662 
    663    /**
    664    * @extends {es3fMultisampleTests.NumSamplesCase}
    665    * @constructor
    666    * @param {string} name
    667    * @param {string} desc
    668    * @param {number=} numFboSamples
    669    */
    670    es3fMultisampleTests.PolygonNumSamplesCase = function(name, desc, numFboSamples) {
    671        numFboSamples = numFboSamples === undefined ? 0 : numFboSamples;
    672        /** @type {es3fMultisampleTests.FboParams} */
    673        var params = numFboSamples >= 0 ? new es3fMultisampleTests.FboParams(numFboSamples, false, false) : new es3fMultisampleTests.FboParams();
    674        es3fMultisampleTests.NumSamplesCase.call(this, name, desc, params);
    675    };
    676 
    677    es3fMultisampleTests.PolygonNumSamplesCase.prototype = Object.create(es3fMultisampleTests.NumSamplesCase.prototype);
    678 
    679    /** Copy the constructor */
    680    es3fMultisampleTests.PolygonNumSamplesCase.prototype.constructor = es3fMultisampleTests.PolygonNumSamplesCase;
    681 
    682    es3fMultisampleTests.PolygonNumSamplesCase.prototype.renderPattern = function() {
    683        // The test pattern consists of several triangles with edges at different angles.
    684 
    685        /** @type {number} */ var numTriangles = 25;
    686        for (var i = 0; i < numTriangles; i++) {
    687            /** @type {number} */ var angle0 = 2.0 * Math.PI * i / numTriangles + 0.001 * this.m_currentIteration;
    688            /** @type {number} */ var angle1 = 2.0 * Math.PI * (i + 0.5) / numTriangles + 0.001 * this.m_currentIteration;
    689 
    690            this.renderTriangle_pAsVec2WithColor(
    691                [0.0, 0.0],
    692                [Math.cos(angle0) * 0.95, Math.sin(angle0) * 0.95],
    693                [Math.cos(angle1) * 0.95, Math.sin(angle1) * 0.95],
    694                [1.0, 1.0, 1.0, 1.0]);
    695        }
    696    };
    697 
    698    /**
    699    * @extends {es3fMultisampleTests.NumSamplesCase}
    700    * @constructor
    701    * @param {string} name
    702    * @param {string} desc
    703    * @param {number=} numFboSamples
    704    */
    705    es3fMultisampleTests.LineNumSamplesCase = function(name, desc, numFboSamples) {
    706        numFboSamples = numFboSamples === undefined ? 0 : numFboSamples;
    707        /** @type {es3fMultisampleTests.FboParams} */
    708        var params = numFboSamples >= 0 ? new es3fMultisampleTests.FboParams(numFboSamples, false, false) : new es3fMultisampleTests.FboParams();
    709        es3fMultisampleTests.NumSamplesCase.call(this, name, desc, params);
    710    };
    711 
    712    es3fMultisampleTests.LineNumSamplesCase.prototype = Object.create(es3fMultisampleTests.NumSamplesCase.prototype);
    713 
    714    /** Copy the constructor */
    715    es3fMultisampleTests.LineNumSamplesCase.prototype.constructor = es3fMultisampleTests.LineNumSamplesCase;
    716 
    717    es3fMultisampleTests.LineNumSamplesCase.prototype.renderPattern = function() {
    718        // The test pattern consists of several lines at different angles.
    719 
    720        // We scale the number of lines based on the viewport size. This is because a gl line's thickness is
    721        // constant in pixel units, i.e. they get relatively thicker as viewport size decreases. Thus we must
    722        // decrease the number of lines in order to decrease the extent of overlap among the lines in the
    723        // center of the pattern.
    724        /** @type {number} */ var numLines = Math.floor(100.0 * Math.sqrt(this.m_viewportSize / 256.0));
    725 
    726        for (var i = 0; i < numLines; i++) {
    727            /** @type {number} */ var angle = 2.0 * Math.PI * i / numLines + 0.001 * this.m_currentIteration;
    728            this.renderLine([0.0, 0.0], [Math.cos(angle) * 0.95, Math.sin(angle) * 0.95], [1.0, 1.0, 1.0, 1.0]);
    729        }
    730    };
    731 
    732    /**
    733     * Case testing behaviour of common edges when multisampling.
    734     *
    735     * Draws a number of test patterns, each with a number of quads, each made
    736     * of two triangles, rotated at different angles. The inner edge inside the
    737     * quad (i.e. the common edge of the two triangles) still should not be
    738     * visible, despite multisampling - i.e. the two triangles forming the quad
    739     * should never get any common coverage bits in any pixel.
    740     *
    741     * @extends {es3fMultisampleTests.MultisampleCase}
    742     * @constructor
    743     * @param {string} name
    744     * @param {string} desc
    745     * @param {es3fMultisampleTests.CommonEdgeCase.CaseType} caseType
    746     * @param {number} numFboSamples
    747     */
    748    es3fMultisampleTests.CommonEdgeCase = function(name, desc, caseType, numFboSamples) {
    749        /** @type {number} */ var cases = caseType === es3fMultisampleTests.CommonEdgeCase.CaseType.SMALL_QUADS ? 128 : 32;
    750        numFboSamples = numFboSamples === undefined ? 0 : numFboSamples;
    751        /** @type {es3fMultisampleTests.FboParams} */
    752        var params = numFboSamples >= 0 ? new es3fMultisampleTests.FboParams(numFboSamples, false, false) : new es3fMultisampleTests.FboParams();
    753 
    754        es3fMultisampleTests.MultisampleCase.call(this, name, desc, cases, params);
    755        /** @type {number} */ var DEFAULT_SMALL_QUADS_ITERATIONS = 16;
    756        /** @type {number} */ var DEFAULT_BIGGER_THAN_VIEWPORT_QUAD_ITERATIONS = 64; // 8*8
    757        /** @type {es3fMultisampleTests.CommonEdgeCase.CaseType} */ this.m_caseType = caseType;
    758        /** @type {number} */ this.m_currentIteration = 0;
    759        /** @type {number} */
    760        this.m_numIterations = caseType === es3fMultisampleTests.CommonEdgeCase.CaseType.SMALL_QUADS ? es3fMultisampleTests.getIterationCount(DEFAULT_SMALL_QUADS_ITERATIONS) :
    761                               caseType === es3fMultisampleTests.CommonEdgeCase.CaseType.BIGGER_THAN_VIEWPORT_QUAD ? es3fMultisampleTests.getIterationCount(DEFAULT_BIGGER_THAN_VIEWPORT_QUAD_ITERATIONS) :
    762                               8;
    763    };
    764 
    765    es3fMultisampleTests.CommonEdgeCase.prototype = Object.create(es3fMultisampleTests.MultisampleCase.prototype);
    766 
    767    /** Copy the constructor */
    768    es3fMultisampleTests.CommonEdgeCase.prototype.constructor = es3fMultisampleTests.CommonEdgeCase;
    769 
    770    /**
    771     * @enum {number}
    772     */
    773    es3fMultisampleTests.CommonEdgeCase.CaseType = {
    774        SMALL_QUADS: 0,                  //!< Draw several small quads per iteration.
    775        BIGGER_THAN_VIEWPORT_QUAD: 1, //!< Draw one bigger-than-viewport quad per iteration.
    776        FIT_VIEWPORT_QUAD: 2          //!< Draw one exactly viewport-sized, axis aligned quad per iteration.
    777    };
    778 
    779    es3fMultisampleTests.CommonEdgeCase.prototype.init = function() {
    780        var inited = es3fMultisampleTests.MultisampleCase.prototype.init.call(this);
    781        if (!inited) {
    782            return false;
    783        }
    784 
    785        if (this.m_caseType === es3fMultisampleTests.CommonEdgeCase.CaseType.SMALL_QUADS) {
    786            // Check for a big enough viewport. With too small viewports the test case can't analyze the resulting image well enough.
    787 
    788            /** @type {number} */ var minViewportSize = 32;
    789 
    790            if (this.m_viewportSize < minViewportSize)
    791                throw new Error('Render target width or height too low (is ' + this.m_viewportSize + ', should be at least ' + minViewportSize + ')');
    792        }
    793 
    794        gl.enable(gl.BLEND);
    795        gl.blendEquation(gl.FUNC_ADD);
    796        gl.blendFunc(gl.ONE, gl.ONE);
    797        bufferedLogToConsole('Additive blending enabled in order to detect (erroneously) overlapping samples');
    798    };
    799 
    800    /**
    801     * @return {tcuTestCase.IterateResult}
    802     */
    803    es3fMultisampleTests.CommonEdgeCase.prototype.iterate = function() {
    804        /** @type {tcuSurface.Surface} */ var errorImg = new tcuSurface.Surface(this.m_viewportSize, this.m_viewportSize);
    805 
    806        this.randomizeViewport();
    807 
    808        gl.clearColor(0.0, 0.0, 0.0, 1.0);
    809        gl.clear(gl.COLOR_BUFFER_BIT);
    810 
    811        // Draw test pattern. Test patterns consist of quads formed with two triangles.
    812        // After drawing the pattern, we check that the interior pixels of each quad are
    813        // all the same color - this is meant to verify that there are no artifacts on the inner edge.
    814 
    815        /** @type {Array<es3fMultisampleTests.QuadCorners>} */ var unicoloredRegions = [];
    816 
    817        /** @type {Array<Array<number>>} */ var corners;
    818        /** @type {number} */ var angleCos;
    819        /** @type {number} */ var angleSin;
    820        /** @type {number} */ var angle;
    821        /** @type {number} */ var quadDiagLen;
    822        /** @type {number} */ var unicolorRegionScale;
    823        /** @type {number} */ var quadBaseAngleNdx
    824        /** @type {number} */ var quadSubAngleNdx;
    825 
    826        if (this.m_caseType == es3fMultisampleTests.CommonEdgeCase.CaseType.SMALL_QUADS) {
    827            // Draw several quads, rotated at different angles.
    828 
    829            quadDiagLen = 2.0 / 3.0 * 0.9; // \note Fit 3 quads in both x and y directions.
    830 
    831 
    832            // \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.
    833 
    834            if (this.m_currentIteration === 0) {
    835                angleCos = 1.0;
    836                angleSin = 0.0;
    837            }
    838            else if (this.m_currentIteration === 1) {
    839                angleCos = Math.SQRT1_2;
    840                angleSin = Math.SQRT1_2;
    841            }
    842            else {
    843                angle = 0.5 * Math.PI * (this.m_currentIteration - 1) / (this.m_numIterations - 1);
    844                angleCos = Math.cos(angle);
    845                angleSin = Math.sin(angle);
    846            }
    847 
    848            corners = [
    849                deMath.scale([angleCos, angleSin], 0.5 * quadDiagLen),
    850                deMath.scale([-angleSin, angleCos], 0.5 * quadDiagLen),
    851                deMath.scale([-angleCos, -angleSin], 0.5 * quadDiagLen),
    852                deMath.scale([angleSin, -angleCos], 0.5 * quadDiagLen)
    853            ];
    854 
    855            // Draw 8 quads.
    856            // First four are rotated at angles angle+0, angle+90, angle+180 and angle+270.
    857            // Last four are rotated the same angles as the first four, but the ordering of the last triangle's vertices is reversed.
    858 
    859            for (var quadNdx = 0; quadNdx < 8; quadNdx++) {
    860                /** @type {Array<number>} */
    861                var center = deMath.addScalar(
    862                    deMath.scale([quadNdx % 3, quadNdx / 3], (2.0 - quadDiagLen)/ 2.0),
    863                    (-0.5 * (2.0 - quadDiagLen)));
    864 
    865                this.renderTriangle_pAsVec2WithColor(
    866                    deMath.add(corners[(0 + quadNdx) % 4], center),
    867                    deMath.add(corners[(1 + quadNdx) % 4], center),
    868                    deMath.add(corners[(2 + quadNdx) % 4], center),
    869                    [0.5, 0.5, 0.5, 1.0]);
    870 
    871                if (quadNdx >= 4) {
    872                    this.renderTriangle_pAsVec2WithColor(
    873                        deMath.add(corners[(3 + quadNdx) % 4], center),
    874                        deMath.add(corners[(2 + quadNdx) % 4], center),
    875                        deMath.add(corners[(0 + quadNdx) % 4], center),
    876                        [0.5, 0.5, 0.5, 1.0]);
    877                }
    878                else {
    879                    this.renderTriangle_pAsVec2WithColor(
    880                        deMath.add(corners[(0 + quadNdx) % 4], center),
    881                        deMath.add(corners[(2 + quadNdx) % 4], center),
    882                        deMath.add(corners[(3 + quadNdx) % 4], center),
    883                        [0.5, 0.5, 0.5, 1.0]);
    884                }
    885 
    886                // The size of the 'interior' of a quad is assumed to be approximately unicolorRegionScale*<actual size of quad>.
    887                // By 'interior' we here mean the region of non-boundary pixels of the rendered quad for which we can safely assume
    888                // that it has all coverage bits set to 1, for every pixel.
    889                unicolorRegionScale = 1.0 - 6.0 * 2.0 / this.m_viewportSize / quadDiagLen;
    890                unicoloredRegions.push(
    891                    new es3fMultisampleTests.QuadCorners(
    892                        deMath.add(center, deMath.scale(corners[0], unicolorRegionScale)),
    893                        deMath.add(center, deMath.scale(corners[1], unicolorRegionScale)),
    894                        deMath.add(center, deMath.scale(corners[2], unicolorRegionScale)),
    895                        deMath.add(center, deMath.scale(corners[3], unicolorRegionScale))));
    896            }
    897        }
    898        else if (this.m_caseType === es3fMultisampleTests.CommonEdgeCase.CaseType.BIGGER_THAN_VIEWPORT_QUAD) {
    899            // Draw a bigger-than-viewport quad, rotated at an angle depending on m_currentIteration.
    900 
    901            quadBaseAngleNdx = Math.floor(this.m_currentIteration / 8);
    902            quadSubAngleNdx = this.m_currentIteration % 8;
    903 
    904            if (quadBaseAngleNdx === 0) {
    905                angleCos = 1.0;
    906                angleSin = 0.0;
    907            }
    908            else if (quadBaseAngleNdx === 1) {
    909                angleCos = Math.SQRT1_2;
    910                angleSin = Math.SQRT1_2;
    911            }
    912            else {
    913                angle = 0.5 * Math.PI * (this.m_currentIteration - 1) / (this.m_numIterations - 1);
    914                angleCos = Math.cos(angle);
    915                angleSin = Math.sin(angle);
    916            }
    917 
    918            quadDiagLen = 2.5 / Math.max(angleCos, angleSin);
    919 
    920            corners = [
    921                deMath.scale([angleCos, angleSin], 0.5 * quadDiagLen),
    922                deMath.scale([-angleSin, angleCos], 0.5 * quadDiagLen),
    923                deMath.scale([-angleCos, -angleSin], 0.5 * quadDiagLen),
    924                deMath.scale([angleSin, -angleCos], 0.5 * quadDiagLen)
    925            ];
    926 
    927            this.renderTriangle_pAsVec2WithColor(
    928                corners[(0 + quadSubAngleNdx) % 4],
    929                corners[(1 + quadSubAngleNdx) % 4],
    930                corners[(2 + quadSubAngleNdx) % 4],
    931                [0.5, 0.5, 0.5, 1.0]);
    932 
    933            if (quadSubAngleNdx >= 4) {
    934                this.renderTriangle_pAsVec2WithColor(
    935                    corners[(3 + quadSubAngleNdx) % 4],
    936                    corners[(2 + quadSubAngleNdx) % 4],
    937                    corners[(0 + quadSubAngleNdx) % 4],
    938                    [0.5, 0.5, 0.5, 1.0]);
    939            }
    940            else {
    941                this.renderTriangle_pAsVec2WithColor(
    942                    corners[(0 + quadSubAngleNdx) % 4],
    943                    corners[(2 + quadSubAngleNdx) % 4],
    944                    corners[(3 + quadSubAngleNdx) % 4],
    945                    [0.5, 0.5, 0.5, 1.0]);
    946            }
    947 
    948            unicolorRegionScale = 1.0 - 6.0 * 2.0 / this.m_viewportSize / quadDiagLen;
    949            unicoloredRegions.push(
    950                new es3fMultisampleTests.QuadCorners(
    951                    deMath.scale(corners[0], unicolorRegionScale),
    952                    deMath.scale(corners[1], unicolorRegionScale),
    953                    deMath.scale(corners[2], unicolorRegionScale),
    954                    deMath.scale(corners[3], unicolorRegionScale)));
    955        }
    956        else if (this.m_caseType === es3fMultisampleTests.CommonEdgeCase.CaseType.FIT_VIEWPORT_QUAD) {
    957            // Draw an exactly viewport-sized quad, rotated by multiples of 90 degrees angle depending on m_currentIteration.
    958 
    959            quadSubAngleNdx = this.m_currentIteration % 8;
    960 
    961            corners = [
    962                [1.0, 1.0],
    963                [-1.0, 1.0],
    964                [-1.0, -1.0],
    965                [1.0, -1.0]
    966            ];
    967 
    968            this.renderTriangle_pAsVec2WithColor(
    969                corners[(0 + quadSubAngleNdx) % 4],
    970                corners[(1 + quadSubAngleNdx) % 4],
    971                corners[(2 + quadSubAngleNdx) % 4],
    972                [0.5, 0.5, 0.5, 1.0]);
    973 
    974            if (quadSubAngleNdx >= 4) {
    975                this.renderTriangle_pAsVec2WithColor(
    976                    corners[(3 + quadSubAngleNdx) % 4],
    977                    corners[(2 + quadSubAngleNdx) % 4],
    978                    corners[(0 + quadSubAngleNdx) % 4],
    979                    [0.5, 0.5, 0.5, 1.0]);
    980            }
    981            else {
    982                this.renderTriangle_pAsVec2WithColor(
    983                    corners[(0 + quadSubAngleNdx) % 4],
    984                    corners[(2 + quadSubAngleNdx) % 4],
    985                    corners[(3 + quadSubAngleNdx) % 4],
    986                    [0.5, 0.5, 0.5, 1.0]);
    987            }
    988 
    989            unicoloredRegions.push(new es3fMultisampleTests.QuadCorners(corners[0], corners[1], corners[2], corners[3]));
    990        }
    991        else
    992            throw new Error('CaseType not supported.');
    993 
    994        // Read pixels and check unicolored regions.
    995 
    996        /** @type {tcuSurface.Surface} */ var renderedImg = this.readImage();
    997 
    998        errorImg.getAccess().clear([0.0, 1.0, 0.0, 1.0]);
    999        tcuLogImage.logImage('RenderedImage', 'Rendered image', renderedImg.getAccess());
   1000 
   1001        /** @type {boolean} */ var errorsDetected = false;
   1002        for (var i = 0; i < unicoloredRegions.length; i++) {
   1003            /** @type {es3fMultisampleTests.QuadCorners} */ var region = unicoloredRegions[i];
   1004            /** @type {Array<number>} */ var p0Win = deMath.scale(deMath.addScalar(region.p0, 1.0), 0.5 * (this.m_viewportSize - 1) + 0.5);
   1005            /** @type {Array<number>} */ var p1Win = deMath.scale(deMath.addScalar(region.p1, 1.0), 0.5 * (this.m_viewportSize - 1) + 0.5);
   1006            /** @type {Array<number>} */ var p2Win = deMath.scale(deMath.addScalar(region.p2, 1.0), 0.5 * (this.m_viewportSize - 1) + 0.5);
   1007            /** @type {Array<number>} */ var p3Win = deMath.scale(deMath.addScalar(region.p3, 1.0), 0.5 * (this.m_viewportSize - 1) + 0.5);
   1008            /** @type {boolean} */ var errorsInCurrentRegion = !es3fMultisampleTests.isPixelRegionUnicolored(renderedImg, p0Win, p1Win, p2Win, p3Win);
   1009 
   1010            if (errorsInCurrentRegion)
   1011                es3fMultisampleTests.drawUnicolorTestErrors(renderedImg, errorImg.getAccess(), p0Win, p1Win, p2Win, p3Win);
   1012 
   1013            errorsDetected = errorsDetected || errorsInCurrentRegion;
   1014        }
   1015 
   1016        this.m_currentIteration++;
   1017 
   1018        if (errorsDetected) {
   1019            bufferedLogToConsole('Failure: Not all quad interiors seem unicolored - common-edge artifacts?');
   1020            bufferedLogToConsole('Erroneous pixels are drawn red in the following image');
   1021            tcuLogImage.logImage('RenderedImageWithErrors', 'Rendered image with errors marked', renderedImg.getAccess());
   1022            tcuLogImage.logImage('ErrorsOnly', 'Image with error pixels only', errorImg.getAccess());
   1023            testFailedOptions('Failed: iteration ' + (this.m_currentIteration - 1), false);
   1024            return tcuTestCase.IterateResult.STOP;
   1025        }
   1026        else if (this.m_currentIteration < this.m_numIterations) {
   1027            bufferedLogToConsole('Quads seem OK - moving on to next pattern');
   1028            return tcuTestCase.IterateResult.CONTINUE;
   1029        }
   1030        else {
   1031            bufferedLogToConsole('Success: All quad interiors seem unicolored (no common-edge artifacts)');
   1032            testPassedOptions('Passed: iteration ' + (this.m_currentIteration - 1), true);
   1033            return tcuTestCase.IterateResult.STOP;
   1034        }
   1035    };
   1036 
   1037    /**
   1038     * Test that depth values are per-sample.
   1039     *
   1040     * Draws intersecting, differently-colored polygons and checks that there
   1041     * are at least sample count+1 distinct colors present, due to some of the
   1042     * samples at the intersection line belonging to one and some to another
   1043     * polygon.
   1044     *
   1045     * @extends {es3fMultisampleTests.NumSamplesCase}
   1046     * @constructor
   1047     * @param {string} name
   1048     * @param {string} desc
   1049     * @param {number=} numFboSamples
   1050     */
   1051    es3fMultisampleTests.SampleDepthCase = function(name, desc, numFboSamples) {
   1052        numFboSamples = numFboSamples === undefined ? 0 : numFboSamples;
   1053        /** @type {es3fMultisampleTests.FboParams} */
   1054        var params = numFboSamples >= 0 ? new es3fMultisampleTests.FboParams(numFboSamples, true, false) : new es3fMultisampleTests.FboParams();
   1055        es3fMultisampleTests.NumSamplesCase.call(this, name, desc, params);
   1056    };
   1057 
   1058    es3fMultisampleTests.SampleDepthCase.prototype = Object.create(es3fMultisampleTests.NumSamplesCase.prototype);
   1059 
   1060    /** Copy the constructor */
   1061    es3fMultisampleTests.SampleDepthCase.prototype.constructor = es3fMultisampleTests.SampleDepthCase;
   1062 
   1063    es3fMultisampleTests.SampleDepthCase.prototype.init = function() {
   1064        var inited = es3fMultisampleTests.MultisampleCase.prototype.init.call(this);
   1065        if (!inited) {
   1066            return false;
   1067        }
   1068 
   1069        gl.enable(gl.DEPTH_TEST);
   1070        gl.depthFunc(gl.LESS);
   1071 
   1072        bufferedLogToConsole('Depth test enabled, depth func is gl.LESS');
   1073        bufferedLogToConsole('Drawing several bigger-than-viewport black or white polygons intersecting each other');
   1074    };
   1075 
   1076    es3fMultisampleTests.SampleDepthCase.prototype.renderPattern = function() {
   1077        gl.clearColor(0.0, 0.0, 0.0, 0.0);
   1078        gl.clearDepth(1.0);
   1079        gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
   1080 
   1081        /** @type {number} */ var numPolygons = 50;
   1082 
   1083        for (var i = 0; i < numPolygons; i++) {
   1084            /** @type {Array<number>} */ var color = i % 2 == 0 ? [1.0, 1.0, 1.0, 1.0] : [0.0, 0.0, 0.0, 1.0];
   1085            /** @type {number} */ var angle = 2.0 * Math.PI * i / numPolygons + 0.001 * this.m_currentIteration;
   1086            /** @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];
   1087            /** @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];
   1088            /** @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];
   1089 
   1090            this.renderTriangle_pAsVec3WithColor(pt0, pt1, pt2, color);
   1091        }
   1092    };
   1093 
   1094    /**
   1095     * Test that stencil buffer values are per-sample.
   1096     *
   1097     * Draws a unicolored pattern and marks drawn samples in stencil buffer;
   1098     * then clears and draws a viewport-size quad with that color and with
   1099     * proper stencil test such that the resulting image should be exactly the
   1100     * same as after the pattern was first drawn.
   1101     *
   1102     * @extends {es3fMultisampleTests.MultisampleCase}
   1103     * @constructor
   1104     * @param {string} name
   1105     * @param {string} desc
   1106     * @param {number=} numFboSamples
   1107     */
   1108    es3fMultisampleTests.SampleStencilCase = function(name, desc, numFboSamples) {
   1109        numFboSamples = numFboSamples === undefined ? 0 : numFboSamples;
   1110        /** @type {es3fMultisampleTests.FboParams} */
   1111        var params = numFboSamples >= 0 ? new es3fMultisampleTests.FboParams(numFboSamples, false, true) : new es3fMultisampleTests.FboParams();
   1112        es3fMultisampleTests.MultisampleCase.call(this, name, desc, 256, params);
   1113    };
   1114 
   1115    es3fMultisampleTests.SampleStencilCase.prototype = Object.create(es3fMultisampleTests.MultisampleCase.prototype);
   1116 
   1117    /** Copy the constructor */
   1118    es3fMultisampleTests.SampleStencilCase.prototype.constructor = es3fMultisampleTests.SampleStencilCase;
   1119 
   1120    /**
   1121     * @return {tcuTestCase.IterateResult}
   1122     */
   1123    es3fMultisampleTests.SampleStencilCase.prototype.iterate = function() {
   1124        this.randomizeViewport();
   1125 
   1126        gl.clearColor(0.0, 0.0, 0.0, 1.0);
   1127        gl.clearStencil(0);
   1128        gl.clear(gl.COLOR_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);
   1129        gl.enable(gl.STENCIL_TEST);
   1130        gl.stencilFunc(gl.ALWAYS, 1, 1);
   1131        gl.stencilOp(gl.KEEP, gl.KEEP, gl.REPLACE);
   1132 
   1133        bufferedLogToConsole('Drawing a pattern with gl.stencilFunc(gl.ALWAYS, 1, 1) and gl.stencilOp(gl.KEEP, gl.KEEP, gl.REPLACE)');
   1134 
   1135        /** @type {number} */ var numTriangles = 25;
   1136        for (var i = 0; i < numTriangles; i++) {
   1137            /** @type {number} */ var angle0 = 2.0 * Math.PI * i / numTriangles;
   1138            /** @type {number} */ var angle1 = 2.0 * Math.PI * (i + 0.5) / numTriangles;
   1139 
   1140            this.renderTriangle_pAsVec2WithColor(
   1141                [0.0, 0.0],
   1142                [Math.cos(angle0) * 0.95, Math.sin(angle0) * 0.95],
   1143                [Math.cos(angle1) * 0.95, Math.sin(angle1) * 0.95],
   1144                [1.0, 1.0, 1.0, 1.0]);
   1145        }
   1146 
   1147        /** @type {tcuSurface.Surface} */ var renderedImgFirst = this.readImage();
   1148        tcuLogImage.logImage('RenderedImgFirst', 'First image rendered', renderedImgFirst.getAccess());
   1149        bufferedLogToConsole('Clearing color buffer to black');
   1150 
   1151        gl.clear(gl.COLOR_BUFFER_BIT);
   1152        gl.stencilFunc(gl.EQUAL, 1, 1);
   1153        gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP);
   1154 
   1155        bufferedLogToConsole('Checking that color buffer was actually cleared to black');
   1156 
   1157        /** @type {tcuSurface.Surface} */ var clearedImg = this.readImage();
   1158 
   1159        for (var y = 0; y < clearedImg.getHeight(); y++)
   1160        for (var x = 0; x < clearedImg.getWidth(); x++) {
   1161            /** @type {tcuRGBA.RGBA} */ var clr = new tcuRGBA.RGBA(clearedImg.getPixel(x, y));
   1162            if (!clr.equals(tcuRGBA.RGBA.black)) {
   1163                bufferedLogToConsole('Failure: first non-black pixel, color ' + clr.toString() + ', detected at coordinates (' + x + ', ' + y + ')');
   1164                tcuLogImage.logImage('ClearedImg', 'Image after clearing, erroneously non-black', clearedImg.getAccess());
   1165                testFailedOptions('Failed', false);
   1166                return tcuTestCase.IterateResult.STOP;
   1167            }
   1168        }
   1169 
   1170        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');
   1171 
   1172        this.renderQuad_WithColor(
   1173            [-1.0, -1.0],
   1174            [1.0, -1.0],
   1175            [-1.0, 1.0],
   1176            [1.0, 1.0],
   1177            [1.0, 1.0, 1.0, 1.0]);
   1178 
   1179        /** @type {tcuSurface.Surface} */ var renderedImgSecond = this.readImage();
   1180        tcuLogImage.logImage('RenderedImgSecond', 'Second image rendered', renderedImgSecond.getAccess());
   1181        /** @type {boolean} */
   1182        var passed = tcuImageCompare.pixelThresholdCompare(
   1183            'ImageCompare',
   1184            'Image comparison',
   1185            renderedImgFirst,
   1186            renderedImgSecond,
   1187            [0,0,0,0]);
   1188 
   1189        if (passed) {
   1190            bufferedLogToConsole('Success: The two images rendered are identical');
   1191            testPassedOptions('Passed', true);
   1192        }
   1193        else
   1194            testFailedOptions('Failed', false);
   1195 
   1196        return tcuTestCase.IterateResult.STOP;
   1197    };
   1198 
   1199    /**
   1200     * Tests coverage mask generation proportionality property.
   1201     *
   1202     * Tests that the number of coverage bits in a coverage mask created by
   1203     * gl.SAMPLE_ALPHA_TO_COVERAGE or gl.SAMPLE_COVERAGE is, on average,
   1204     * proportional to the alpha or coverage value, respectively. Draws
   1205     * multiple frames, each time increasing the alpha or coverage value used,
   1206     * and checks that the average color is changing appropriately.
   1207     *
   1208     * @extends {es3fMultisampleTests.MultisampleCase}
   1209     * @constructor
   1210     * @param {string} name
   1211     * @param {string} desc
   1212     * @param {es3fMultisampleTests.MaskProportionalityCase.CaseType} type
   1213     * @param {number=} numFboSamples
   1214     */
   1215    es3fMultisampleTests.MaskProportionalityCase = function(name, desc, type, numFboSamples) {
   1216        numFboSamples = numFboSamples === undefined ? 0 : numFboSamples;
   1217        /** @type {es3fMultisampleTests.FboParams} */
   1218        var params = numFboSamples >= 0 ? new es3fMultisampleTests.FboParams(numFboSamples, false, false) : new es3fMultisampleTests.FboParams();
   1219        es3fMultisampleTests.MultisampleCase.call(this, name, desc, 32, params);
   1220        /** @type {es3fMultisampleTests.MaskProportionalityCase.CaseType} */ this.m_type = type;
   1221        /** @type {number} */ this.m_numIterations;
   1222        /** @type {number} */ this.m_currentIteration = 0;
   1223        /** @type {number} */ this.m_previousIterationColorSum = -1;
   1224    };
   1225 
   1226    es3fMultisampleTests.MaskProportionalityCase.prototype = Object.create(es3fMultisampleTests.MultisampleCase.prototype);
   1227 
   1228    /** Copy the constructor */
   1229    es3fMultisampleTests.MaskProportionalityCase.prototype.constructor = es3fMultisampleTests.MaskProportionalityCase;
   1230 
   1231    /**
   1232     * @enum {number}
   1233     */
   1234    es3fMultisampleTests.MaskProportionalityCase.CaseType = {
   1235        ALPHA_TO_COVERAGE: 0,
   1236        SAMPLE_COVERAGE: 1,
   1237        SAMPLE_COVERAGE_INVERTED: 2
   1238    };
   1239 
   1240    es3fMultisampleTests.MaskProportionalityCase.prototype.init = function() {
   1241        var inited = es3fMultisampleTests.MultisampleCase.prototype.init.call(this);
   1242        if (!inited) {
   1243            return false;
   1244        }
   1245 
   1246        if (this.m_type == es3fMultisampleTests.MaskProportionalityCase.CaseType.ALPHA_TO_COVERAGE) {
   1247            gl.enable(gl.SAMPLE_ALPHA_TO_COVERAGE);
   1248            bufferedLogToConsole('gl.SAMPLE_ALPHA_TO_COVERAGE is enabled');
   1249        }
   1250        else {
   1251            assertMsgOptions(
   1252                this.m_type == es3fMultisampleTests.MaskProportionalityCase.CaseType.SAMPLE_COVERAGE ||
   1253                this.m_type == es3fMultisampleTests.MaskProportionalityCase.CaseType.SAMPLE_COVERAGE_INVERTED,
   1254                'CaseType should be SAMPLE_COVERAGE or SAMPLE_COVERAGE_INVERTED', false, true);
   1255 
   1256            gl.enable(gl.SAMPLE_COVERAGE);
   1257            bufferedLogToConsole('gl.SAMPLE_COVERAGE is enabled');
   1258        }
   1259 
   1260        this.m_numIterations = Math.max(2, es3fMultisampleTests.getIterationCount(this.m_numSamples * 5));
   1261 
   1262        this.randomizeViewport(); // \note Using the same viewport for every iteration since coverage mask may depend on window-relative pixel coordinate.
   1263    };
   1264 
   1265    /**
   1266     * @return {tcuTestCase.IterateResult}
   1267     */
   1268    es3fMultisampleTests.MaskProportionalityCase.prototype.iterate = function() {
   1269        bufferedLogToConsole('Clearing color to black');
   1270        gl.colorMask(true, true, true, true);
   1271        gl.clearColor(0.0, 0.0, 0.0, 1.0);
   1272        gl.clear(gl.COLOR_BUFFER_BIT);
   1273 
   1274        if (this.m_type === es3fMultisampleTests.MaskProportionalityCase.CaseType.ALPHA_TO_COVERAGE) {
   1275            gl.colorMask(true, true, true, false);
   1276            bufferedLogToConsole('Using color mask TRUE, TRUE, TRUE, FALSE');
   1277        }
   1278 
   1279        // Draw quad.
   1280 
   1281        /** @type {Array<number>} */ var pt0 = [-1.0, -1.0];
   1282        /** @type {Array<number>} */ var pt1 = [1.0, -1.0];
   1283        /** @type {Array<number>} */ var pt2 = [-1.0, 1.0];
   1284        /** @type {Array<number>} */ var pt3 = [1.0, 1.0];
   1285        /** @type {Array<number>} */ var quadColor = [1.0, 0.0, 0.0, 1.0];
   1286        /** @type {number} */ var alphaOrCoverageValue    = this.m_currentIteration / (this.m_numIterations-1);
   1287 
   1288        if (this.m_type === es3fMultisampleTests.MaskProportionalityCase.CaseType.ALPHA_TO_COVERAGE) {
   1289            bufferedLogToConsole('Drawing a red quad using alpha value ' + alphaOrCoverageValue);
   1290            quadColor[3] = alphaOrCoverageValue;
   1291        }
   1292        else {
   1293            assertMsgOptions(
   1294                this.m_type === es3fMultisampleTests.MaskProportionalityCase.CaseType.SAMPLE_COVERAGE ||
   1295                this.m_type === es3fMultisampleTests.MaskProportionalityCase.CaseType.SAMPLE_COVERAGE_INVERTED,
   1296                'CaseType should be SAMPLE_COVERAGE or SAMPLE_COVERAGE_INVERTED', false, true);
   1297 
   1298            /** @type {boolean} */ var isInverted = (this.m_type === es3fMultisampleTests.MaskProportionalityCase.CaseType.SAMPLE_COVERAGE_INVERTED);
   1299            /** @type {number} */ var coverageValue    = isInverted ? 1.0 - alphaOrCoverageValue : alphaOrCoverageValue;
   1300            bufferedLogToConsole('Drawing a red quad using sample coverage value ' + coverageValue + (isInverted ? ' (inverted)' : ''));
   1301            gl.sampleCoverage(coverageValue, isInverted ? true : false);
   1302        }
   1303 
   1304        this.renderQuad_WithColor(pt0, pt1, pt2, pt3, quadColor);
   1305 
   1306        // Read and log image.
   1307        /** @type {tcuSurface.Surface} */ var renderedImg = this.readImage();
   1308        /** @type {number} */ var numPixels = renderedImg.getWidth() * renderedImg.getHeight();
   1309 
   1310        tcuLogImage.logImage('RenderedImage', 'Rendered image', renderedImg.getAccess());
   1311        // Compute average red component in rendered image.
   1312 
   1313        /** @type {number} */ var sumRed = 0;
   1314 
   1315        for (var y = 0; y < renderedImg.getHeight(); y++)
   1316        for (var x = 0; x < renderedImg.getWidth(); x++)
   1317            sumRed += new tcuRGBA.RGBA(renderedImg.getPixel(x, y)).getRed();
   1318 
   1319        bufferedLogToConsole('Average red color component: ' + (sumRed / 255.0 / numPixels));
   1320 
   1321        // Check if average color has decreased from previous frame's color.
   1322 
   1323        if (sumRed < this.m_previousIterationColorSum) {
   1324            bufferedLogToConsole('Failure: Current average red color component is lower than previous');
   1325            testFailedOptions('Failed', false);
   1326            return tcuTestCase.IterateResult.STOP;
   1327        }
   1328 
   1329        // Check if coverage mask is not all-zeros if alpha or coverage value is 0 (or 1, if inverted).
   1330 
   1331        if (this.m_currentIteration == 0 && sumRed != 0)
   1332        {
   1333            bufferedLogToConsole('Failure: Image should be completely black');
   1334            testFailedOptions('Failed', false);
   1335            return tcuTestCase.IterateResult.STOP;
   1336        }
   1337 
   1338        if (this.m_currentIteration == this.m_numIterations-1 && sumRed != 0xff*numPixels)
   1339        {
   1340            bufferedLogToConsole('Failure: Image should be completely red');
   1341 
   1342            testFailedOptions('Failed', false);
   1343            return tcuTestCase.IterateResult.STOP;
   1344        }
   1345 
   1346        this.m_previousIterationColorSum = sumRed;
   1347 
   1348        this.m_currentIteration++;
   1349 
   1350        if (this.m_currentIteration >= this.m_numIterations)
   1351        {
   1352            bufferedLogToConsole('Success: Number of coverage mask bits set appears to be, on average, proportional to ' +
   1353                (this.m_type == es3fMultisampleTests.MaskProportionalityCase.CaseType.ALPHA_TO_COVERAGE ? 'alpha' :
   1354                this.m_type == es3fMultisampleTests.MaskProportionalityCase.CaseType.SAMPLE_COVERAGE ? 'sample coverage value' :
   1355                'inverted sample coverage value'));
   1356 
   1357            testPassedOptions('Passed', true);
   1358            return tcuTestCase.IterateResult.STOP;
   1359        }
   1360        else
   1361            return tcuTestCase.IterateResult.CONTINUE;
   1362    };
   1363 
   1364    /**
   1365     * Tests coverage mask generation constancy property.
   1366     *
   1367     * Tests that the coverage mask created by gl.SAMPLE_ALPHA_TO_COVERAGE or
   1368     * gl.SAMPLE_COVERAGE is constant at given pixel coordinates, with a given
   1369     * alpha component or coverage value, respectively. Draws two quads, with
   1370     * the second one fully overlapping the first one such that at any given
   1371     * pixel, both quads have the same alpha or coverage value. This way, if
   1372     * the constancy property is fulfilled, only the second quad should be
   1373     * visible.
   1374     * @extends {es3fMultisampleTests.MultisampleCase}
   1375     * @constructor
   1376     * @param {string} name
   1377     * @param {string} desc
   1378     * @param {es3fMultisampleTests.MaskConstancyCase.CaseType} type
   1379     * @param {number=} numFboSamples
   1380     */
   1381    es3fMultisampleTests.MaskConstancyCase = function(name, desc, type, numFboSamples) {
   1382       numFboSamples = numFboSamples === undefined ? 0 : numFboSamples;
   1383       /** @type {es3fMultisampleTests.FboParams} */
   1384       var params = numFboSamples >= 0 ? new es3fMultisampleTests.FboParams(numFboSamples, false, false) : new es3fMultisampleTests.FboParams();
   1385       es3fMultisampleTests.MultisampleCase.call(this, name, desc, 256, params);
   1386       var CaseType = es3fMultisampleTests.MaskConstancyCase.CaseType;
   1387       /** @type {boolean} */ this.m_isAlphaToCoverageCase = (type === CaseType.ALPHA_TO_COVERAGE || type === CaseType.BOTH || type === CaseType.BOTH_INVERTED);
   1388       /** @type {boolean} */ this.m_isSampleCoverageCase = (type === CaseType.SAMPLE_COVERAGE || type === CaseType.SAMPLE_COVERAGE_INVERTED || type === CaseType.BOTH || type === CaseType.BOTH_INVERTED);
   1389       /** @type {boolean} */ this.m_isInvertedSampleCoverageCase = (type === CaseType.SAMPLE_COVERAGE_INVERTED || type === CaseType.BOTH_INVERTED);
   1390    };
   1391 
   1392    es3fMultisampleTests.MaskConstancyCase.prototype = Object.create(es3fMultisampleTests.MultisampleCase.prototype);
   1393 
   1394    /** Copy the constructor */
   1395    es3fMultisampleTests.MaskConstancyCase.prototype.constructor = es3fMultisampleTests.MaskConstancyCase;
   1396 
   1397    /**
   1398     * @enum {number}
   1399     */
   1400    es3fMultisampleTests.MaskConstancyCase.CaseType = {
   1401        ALPHA_TO_COVERAGE:  0,        //!< Use only alpha-to-coverage.
   1402        SAMPLE_COVERAGE: 1,            //!< Use only sample coverage.
   1403        SAMPLE_COVERAGE_INVERTED: 2,    //!< Use only inverted sample coverage.
   1404        BOTH: 3,                        //!< Use both alpha-to-coverage and sample coverage.
   1405        BOTH_INVERTED: 4                //!< Use both alpha-to-coverage and inverted sample coverage.
   1406    };
   1407 
   1408    /**
   1409     * @return {tcuTestCase.IterateResult}
   1410     */
   1411    es3fMultisampleTests.MaskConstancyCase.prototype.iterate = function() {
   1412        this.randomizeViewport();
   1413 
   1414        bufferedLogToConsole('Clearing color to black');
   1415        gl.clearColor(0.0, 0.0, 0.0, 1.0);
   1416        gl.clear(gl.COLOR_BUFFER_BIT);
   1417 
   1418        if (this.m_isAlphaToCoverageCase) {
   1419            gl.enable(gl.SAMPLE_ALPHA_TO_COVERAGE);
   1420            gl.colorMask(true, true, true, false);
   1421            bufferedLogToConsole('gl.SAMPLE_ALPHA_TO_COVERAGE is enabled');
   1422            bufferedLogToConsole('Color mask is TRUE, TRUE, TRUE, FALSE');
   1423        }
   1424 
   1425        if (this.m_isSampleCoverageCase) {
   1426            gl.enable(gl.SAMPLE_COVERAGE);
   1427            bufferedLogToConsole('gl.SAMPLE_COVERAGE is enabled');
   1428        }
   1429 
   1430        bufferedLogToConsole('Drawing several green quads, each fully overlapped by a red quad with the same ' +
   1431            (this.m_isAlphaToCoverageCase ? 'alpha' : '') +
   1432            (this.m_isAlphaToCoverageCase && this.m_isSampleCoverageCase ? ' and ' : '') +
   1433            (this.m_isInvertedSampleCoverageCase ? 'inverted ' : '') +
   1434            (this.m_isSampleCoverageCase ? 'sample coverage' : '') +
   1435            ' values');
   1436 
   1437        /** @type {number} */ var numQuadRowsCols = this.m_numSamples * 4;
   1438 
   1439        for (var row = 0; row < numQuadRowsCols; row++) {
   1440            for (var col = 0; col < numQuadRowsCols; col++) {
   1441                /** @type {number} */ var x0 = (col + 0) / numQuadRowsCols * 2.0 - 1.0;
   1442                /** @type {number} */ var x1 = (col + 1) / numQuadRowsCols * 2.0 - 1.0;
   1443                /** @type {number} */ var y0 = (row + 0) / numQuadRowsCols * 2.0 - 1.0;
   1444                /** @type {number} */ var y1 = (row + 1) / numQuadRowsCols * 2.0 - 1.0;
   1445                /** @type {Array<number>} */ var baseGreen = [0.0, 1.0, 0.0, 0.0];
   1446                /** @type {Array<number>} */ var baseRed = [1.0, 0.0, 0.0, 0.0];
   1447                /** @type {Array<number>} */ var alpha0 = [0.0, 0.0, 0.0, this.m_isAlphaToCoverageCase ? col / (numQuadRowsCols - 1) : 1.0];
   1448                /** @type {Array<number>} */ var alpha1 = [0.0, 0.0, 0.0, this.m_isAlphaToCoverageCase ? row / (numQuadRowsCols - 1) : 1.0];
   1449 
   1450                if (this.m_isSampleCoverageCase) {
   1451                    /** @type {number} */ var value = (row*numQuadRowsCols + col) / (numQuadRowsCols*numQuadRowsCols - 1);
   1452                    gl.sampleCoverage(this.m_isInvertedSampleCoverageCase ? 1.0 - value : value, this.m_isInvertedSampleCoverageCase ? true : false);
   1453                }
   1454 
   1455                this.renderQuad([x0, y0], [x1, y0], [x0, y1], [x1, y1],
   1456                    deMath.add(baseGreen, alpha0), deMath.add(baseGreen, alpha1),
   1457                    deMath.add(baseGreen, alpha0), deMath.add(baseGreen, alpha1));
   1458                this.renderQuad([x0, y0], [x1, y0], [x0, y1], [x1, y1],
   1459                    deMath.add(baseRed, alpha0), deMath.add(baseRed, alpha1),
   1460                    deMath.add(baseRed, alpha0), deMath.add(baseRed, alpha1));
   1461            }
   1462        }
   1463 
   1464        /** @type {tcuSurface.Surface} */ var renderedImg = this.readImage();
   1465 
   1466        tcuLogImage.logImage('RenderedImage', 'Rendered image', renderedImg.getAccess());
   1467        for (var y = 0; y < renderedImg.getHeight(); y++)
   1468        for (var x = 0; x < renderedImg.getWidth(); x++) {
   1469            if (new tcuRGBA.RGBA(renderedImg.getPixel(x, y)).getGreen() > 0) {
   1470                bufferedLogToConsole('Failure: Non-zero green color component detected - should have been completely overwritten by red quad');
   1471                testFailedOptions('Failed', false);
   1472                return tcuTestCase.IterateResult.STOP;
   1473            }
   1474        }
   1475 
   1476        bufferedLogToConsole('Success: Coverage mask appears to be constant at a given pixel coordinate with a given ' +
   1477            (this.m_isAlphaToCoverageCase ? 'alpha' : '') +
   1478            (this.m_isAlphaToCoverageCase && this.m_isSampleCoverageCase ? ' and ' : '') +
   1479            (this.m_isSampleCoverageCase ? 'coverage value' : ''));
   1480 
   1481        testPassedOptions('Passed', true);
   1482 
   1483        return tcuTestCase.IterateResult.STOP;
   1484    }
   1485 
   1486 
   1487    /**
   1488     * Tests coverage mask inversion validity.
   1489     *
   1490     * Tests that the coverage masks obtained by glSampleCoverage(..., true)
   1491     * and glSampleCoverage(..., false) are indeed each others' inverses.
   1492     * This is done by drawing a pattern, with varying coverage values,
   1493     * overlapped by a pattern that has inverted masks and is otherwise
   1494     * identical. The resulting image is compared to one obtained by drawing
   1495     * the same pattern but with all-ones coverage masks.
   1496     * @extends {es3fMultisampleTests.MultisampleCase}
   1497     * @constructor
   1498     * @param {string} name
   1499     * @param {string} desc
   1500     * @param {number=} numFboSamples
   1501     */
   1502    es3fMultisampleTests.CoverageMaskInvertCase = function(name, desc, numFboSamples) {
   1503      numFboSamples = numFboSamples === undefined ? 0 : numFboSamples;
   1504      /** @type {es3fMultisampleTests.FboParams} */
   1505      var params = numFboSamples >= 0 ? new es3fMultisampleTests.FboParams(numFboSamples, false, false) : new es3fMultisampleTests.FboParams();
   1506      es3fMultisampleTests.MultisampleCase.call(this, name, desc, 256, params);
   1507    };
   1508 
   1509    es3fMultisampleTests.CoverageMaskInvertCase.prototype = Object.create(es3fMultisampleTests.MultisampleCase.prototype);
   1510 
   1511    /** Copy the constructor */
   1512    es3fMultisampleTests.CoverageMaskInvertCase.prototype.constructor = es3fMultisampleTests.CoverageMaskInvertCase;
   1513 
   1514    /**
   1515     * @param {boolean} invertSampleCoverage
   1516     */
   1517    es3fMultisampleTests.CoverageMaskInvertCase.prototype.drawPattern = function(invertSampleCoverage) {
   1518        /** @type {number} */ var numTriangles = 25;
   1519        for (var i = 0; i < numTriangles; i++) {
   1520            gl.sampleCoverage(i / (numTriangles - 1), invertSampleCoverage ? true : false);
   1521 
   1522            /** @type {number} */ var angle0 = 2.0 * Math.PI * i / numTriangles;
   1523            /** @type {number} */ var angle1 = 2.0 * Math.PI * (i + 0.5) / numTriangles;
   1524 
   1525            this.renderTriangle_pAsVec2WithColor(
   1526                [0.0, 0.0],
   1527                [Math.cos(angle0) * 0.95, Math.sin(angle0) * 0.95],
   1528                [Math.cos(angle1) * 0.95, Math.sin(angle1) * 0.95],
   1529                [0.4 + i / numTriangles * 0.6,
   1530                 0.5 + i / numTriangles * 0.3,
   1531                 0.6 - i / numTriangles * 0.5,
   1532                 0.7 - i / numTriangles * 0.7]);
   1533        }
   1534    };
   1535 
   1536    /**
   1537    * @return {tcuTestCase.IterateResult}
   1538    */
   1539    es3fMultisampleTests.CoverageMaskInvertCase.prototype.iterate = function() {
   1540        this.randomizeViewport();
   1541 
   1542        gl.enable(gl.BLEND);
   1543        gl.blendEquation(gl.FUNC_ADD);
   1544        gl.blendFunc(gl.ONE, gl.ONE);
   1545        bufferedLogToConsole('Additive blending enabled in order to detect (erroneously) overlapping samples');
   1546 
   1547        bufferedLogToConsole('Clearing color to all-zeros');
   1548        gl.clearColor(0.0, 0.0, 0.0, 0.0);
   1549        gl.clear(gl.COLOR_BUFFER_BIT);
   1550        bufferedLogToConsole('Drawing the pattern with gl.SAMPLE_COVERAGE disabled');
   1551        this.drawPattern(false);
   1552        /** @type {tcuSurface.Surface} */ var renderedImgNoSampleCoverage = this.readImage();
   1553 
   1554        tcuLogImage.logImage('RenderedImageNoSampleCoverage', 'Rendered image with gl.SAMPLE_COVERAGE disabled', renderedImgNoSampleCoverage.getAccess());
   1555        bufferedLogToConsole('Clearing color to all-zeros');
   1556        gl.clear(gl.COLOR_BUFFER_BIT);
   1557        gl.enable(gl.SAMPLE_COVERAGE);
   1558        bufferedLogToConsole('Drawing the pattern with gl.SAMPLE_COVERAGE enabled, using non-inverted masks');
   1559        this.drawPattern(false);
   1560        bufferedLogToConsole('Drawing the pattern with gl.SAMPLE_COVERAGE enabled, using same sample coverage values but inverted masks');
   1561        this.drawPattern(true);
   1562        /** @type {tcuSurface.Surface} */ var renderedImgSampleCoverage = this.readImage();
   1563 
   1564        tcuLogImage.logImage('RenderedImageSampleCoverage', 'Rendered image with gl.SAMPLE_COVERAGE enabled', renderedImgSampleCoverage.getAccess());
   1565        /** @type {boolean} */ var passed = tcuImageCompare.pixelThresholdCompare(
   1566            'CoverageVsNoCoverage',
   1567            'Comparison of same pattern with gl.SAMPLE_COVERAGE disabled and enabled',
   1568            renderedImgNoSampleCoverage,
   1569            renderedImgSampleCoverage,
   1570            [0, 0, 0, 0]);
   1571 
   1572        if (passed) {
   1573            bufferedLogToConsole('Success: The two images rendered are identical');
   1574            testPassedOptions('Passed', true);
   1575        }
   1576        else {
   1577            testFailedOptions('Failed', false);
   1578        }
   1579 
   1580        return tcuTestCase.IterateResult.STOP;
   1581    };
   1582 
   1583    es3fMultisampleTests.init = function() {
   1584        var testGroup = tcuTestCase.runner.testCases;
   1585        /**
   1586         * @enum {number}
   1587         */
   1588        var CaseType = {
   1589            DEFAULT_FRAMEBUFFER: 0,
   1590            FBO_4_SAMPLES: 1,
   1591            FBO_8_SAMPLES: 2,
   1592            FBO_MAX_SAMPLES: 3
   1593        };
   1594 
   1595        for (var caseTypeI in CaseType) {
   1596            /** @type {CaseType} */ var caseType = CaseType[caseTypeI];
   1597            /** @type {number} */
   1598            var numFboSamples = caseType === CaseType.DEFAULT_FRAMEBUFFER ? -1 :
   1599                                caseType === CaseType.FBO_4_SAMPLES ? 4 :
   1600                                caseType === CaseType.FBO_8_SAMPLES ? 8 :
   1601                                caseType === CaseType.FBO_MAX_SAMPLES ? 0 :
   1602                                -2;
   1603 
   1604            /** @type {?string} */
   1605            var name = caseType === CaseType.DEFAULT_FRAMEBUFFER ? 'default_framebuffer' :
   1606                       caseType === CaseType.FBO_4_SAMPLES ? 'fbo_4_samples' :
   1607                       caseType === CaseType.FBO_8_SAMPLES ? 'fbo_8_samples' :
   1608                       caseType === CaseType.FBO_MAX_SAMPLES ? 'fbo_max_samples' :
   1609                       null;
   1610            /** @type {?string} */
   1611            var desc = caseType === CaseType.DEFAULT_FRAMEBUFFER ? 'Render into default framebuffer' :
   1612                       caseType === CaseType.FBO_4_SAMPLES ? 'Render into a framebuffer object with 4 samples' :
   1613                       caseType === CaseType.FBO_8_SAMPLES ? 'Render into a framebuffer object with 8 samples' :
   1614                       caseType === CaseType.FBO_MAX_SAMPLES ? 'Render into a framebuffer object with the maximum number of samples' :
   1615                       null;
   1616 
   1617            /** @type {tcuTestCase.DeqpTest} */ var group = tcuTestCase.newTest(name, desc);
   1618 
   1619            assertMsgOptions(group.name != null, 'Error: No Test Name', false, true);
   1620            assertMsgOptions(group.description != null, 'Error: No Test Description', false, true);
   1621            assertMsgOptions(numFboSamples >= -1, 'Assert Failed: numFboSamples >= -1', false, true);
   1622            testGroup.addChild(group);
   1623 
   1624            group.addChild(new es3fMultisampleTests.PolygonNumSamplesCase(
   1625                'num_samples_polygon',
   1626                'Test sanity of the sample count, with polygons',
   1627                numFboSamples));
   1628 
   1629            group.addChild(new es3fMultisampleTests.LineNumSamplesCase(
   1630                'num_samples_line',
   1631                'Test sanity of the sample count, with lines',
   1632                numFboSamples));
   1633 
   1634            group.addChild(new es3fMultisampleTests.CommonEdgeCase(
   1635                'common_edge_small_quads',
   1636                'Test polygons\'s common edges with small quads',
   1637                es3fMultisampleTests.CommonEdgeCase.CaseType.SMALL_QUADS,
   1638                numFboSamples));
   1639 
   1640            group.addChild(new es3fMultisampleTests.CommonEdgeCase(
   1641                'common_edge_big_quad',
   1642                'Test polygon\'s common edges with bigger-than-viewport quads',
   1643                es3fMultisampleTests.CommonEdgeCase.CaseType.BIGGER_THAN_VIEWPORT_QUAD,
   1644                numFboSamples));
   1645 
   1646            group.addChild(new es3fMultisampleTests.CommonEdgeCase(
   1647                'common_edge_viewport_quad',
   1648                'Test polygons\' common edges with exactly viewport-sized quads',
   1649                es3fMultisampleTests.CommonEdgeCase.CaseType.FIT_VIEWPORT_QUAD,
   1650                numFboSamples));
   1651 
   1652            group.addChild(new es3fMultisampleTests.SampleDepthCase(
   1653                'depth',
   1654                'Test that depth values are per-sample',
   1655                numFboSamples));
   1656 
   1657            group.addChild(new es3fMultisampleTests.SampleStencilCase(
   1658                'stencil',
   1659                'Test that stencil values are per-sample',
   1660                numFboSamples));
   1661 
   1662            group.addChild(new es3fMultisampleTests.CoverageMaskInvertCase(
   1663                'sample_coverage_invert',
   1664                'Test that non-inverted and inverted sample coverage masks are each other\'s negations',
   1665                numFboSamples));
   1666 
   1667 
   1668            group.addChild(new es3fMultisampleTests.MaskProportionalityCase(
   1669                'proportionality_alpha_to_coverage',
   1670                'Test the proportionality property of GL_SAMPLE_ALPHA_TO_COVERAGE',
   1671                es3fMultisampleTests.MaskProportionalityCase.CaseType.ALPHA_TO_COVERAGE,
   1672                numFboSamples));
   1673            group.addChild(new es3fMultisampleTests.MaskProportionalityCase(
   1674                'proportionality_sample_coverage',
   1675                'Test the proportionality property of GL_SAMPLE_COVERAGE',
   1676                es3fMultisampleTests.MaskProportionalityCase.CaseType.SAMPLE_COVERAGE,
   1677                numFboSamples));
   1678            group.addChild(new es3fMultisampleTests.MaskProportionalityCase(
   1679                'proportionality_sample_coverage_inverted',
   1680                'Test the proportionality property of inverted-mask GL_SAMPLE_COVERAGE',
   1681                es3fMultisampleTests.MaskProportionalityCase.CaseType.SAMPLE_COVERAGE_INVERTED,
   1682                numFboSamples));
   1683 
   1684            group.addChild(new es3fMultisampleTests.MaskConstancyCase(
   1685                'constancy_alpha_to_coverage',
   1686                'Test that coverage mask is constant at given coordinates with a given alpha or coverage value, using GL_SAMPLE_ALPHA_TO_COVERAGE',
   1687                es3fMultisampleTests.MaskConstancyCase.CaseType.ALPHA_TO_COVERAGE,
   1688                numFboSamples));
   1689            group.addChild(new es3fMultisampleTests.MaskConstancyCase(
   1690                'constancy_sample_coverage',
   1691                'Test that coverage mask is constant at given coordinates with a given alpha or coverage value, using GL_SAMPLE_COVERAGE',
   1692                es3fMultisampleTests.MaskConstancyCase.CaseType.SAMPLE_COVERAGE,
   1693                numFboSamples));
   1694            group.addChild(new es3fMultisampleTests.MaskConstancyCase(
   1695                'constancy_sample_coverage_inverted',
   1696                'Test that coverage mask is constant at given coordinates with a given alpha or coverage value, using inverted-mask GL_SAMPLE_COVERAGE',
   1697                es3fMultisampleTests.MaskConstancyCase.CaseType.SAMPLE_COVERAGE_INVERTED,
   1698                numFboSamples));
   1699            group.addChild(new es3fMultisampleTests.MaskConstancyCase(
   1700                'constancy_both',
   1701                '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',
   1702                es3fMultisampleTests.MaskConstancyCase.CaseType.BOTH,
   1703                numFboSamples));
   1704            group.addChild(new es3fMultisampleTests.MaskConstancyCase(
   1705                'constancy_both_inverted',
   1706                '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',
   1707                es3fMultisampleTests.MaskConstancyCase.CaseType.BOTH_INVERTED,
   1708                numFboSamples));
   1709        }
   1710    };
   1711 
   1712    /**
   1713     * Run test
   1714     * @param {WebGL2RenderingContext} context
   1715     */
   1716    es3fMultisampleTests.run = function(context, range) {
   1717        gl = context;
   1718        //Set up Test Root parameters
   1719        var testName = 'multisample';
   1720        var testDescription = 'Multisample Tests';
   1721        var state = tcuTestCase.runner;
   1722 
   1723        state.testName = testName;
   1724        state.setRoot(tcuTestCase.newTest(testName, testDescription, null));
   1725 
   1726        //Set up name and description of this test series.
   1727        setCurrentTestName(testName);
   1728        description(testDescription);
   1729 
   1730        try {
   1731            //Create test cases
   1732            es3fMultisampleTests.init();
   1733            if (range)
   1734                state.setRange(range);
   1735            //Run test cases
   1736            tcuTestCase.runTestCases();
   1737        }
   1738        catch (err) {
   1739            testFailedOptions('Failed to es3fMultisampleTests.run tests', false);
   1740            tcuTestCase.runner.terminate();
   1741        }
   1742    };
   1743 
   1744 });