tor-browser

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

es3fFragmentOutputTests.js (71637B)


      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.es3fFragmentOutputTests');
     23 goog.require('framework.common.tcuImageCompare');
     24 goog.require('framework.common.tcuTestCase');
     25 goog.require('framework.common.tcuTexture');
     26 goog.require('framework.common.tcuTextureUtil');
     27 goog.require('framework.delibs.debase.deMath');
     28 goog.require('framework.delibs.debase.deRandom');
     29 goog.require('framework.opengl.gluShaderProgram');
     30 goog.require('framework.opengl.gluShaderUtil');
     31 goog.require('framework.opengl.gluTextureUtil');
     32 goog.require('functional.gles3.es3fFboTestUtil');
     33 
     34 goog.scope(function() {
     35 
     36 var es3fFragmentOutputTests = functional.gles3.es3fFragmentOutputTests;
     37 var gluShaderProgram = framework.opengl.gluShaderProgram;
     38 var es3fFboTestUtil = functional.gles3.es3fFboTestUtil;
     39 var gluShaderUtil = framework.opengl.gluShaderUtil;
     40 var deRandom = framework.delibs.debase.deRandom;
     41 var tcuTestCase = framework.common.tcuTestCase;
     42 var gluTextureUtil = framework.opengl.gluTextureUtil;
     43 var tcuTexture = framework.common.tcuTexture;
     44 var tcuTextureUtil = framework.common.tcuTextureUtil;
     45 var deMath = framework.delibs.debase.deMath;
     46 var tcuImageCompare = framework.common.tcuImageCompare;
     47 
     48    /** @type {WebGL2RenderingContext} */ var gl;
     49 
     50    var DE_ASSERT = function(x) {
     51        if (!x)
     52            throw new Error('Assert failed');
     53    };
     54 
     55    /**
     56     * es3fFragmentOutputTests.BufferSpec. Constructs the es3fFragmentOutputTests.BufferSpec object
     57     * @constructor
     58     * @param {WebGLRenderingContextBase.GLenum} format_
     59     * @param {number} width_
     60     * @param {number} height_
     61     * @param {number} samples_
     62     */
     63    es3fFragmentOutputTests.BufferSpec = function(format_, width_, height_, samples_) {
     64        this.format = format_;
     65        this.width = width_;
     66        this.height = height_;
     67        this.samples = samples_;
     68    };
     69 
     70    /**
     71     * es3fFragmentOutputTests.FragmentOutput. Constructs the es3fFragmentOutputTests.FragmentOutput object
     72     * @constructor
     73     * @param {gluShaderUtil.DataType} type_
     74     * @param {gluShaderUtil.precision} precision_
     75     * @param {number} location_
     76     * @param {number=} arrayLength_
     77     */
     78    es3fFragmentOutputTests.FragmentOutput = function(type_, precision_, location_, arrayLength_) {
     79        this.type = type_;
     80        this.precision = precision_;
     81        this.location = location_;
     82        this.arrayLength = arrayLength_ || 0;
     83    };
     84 
     85    /**
     86     * es3fFragmentOutputTests.FragmentOutputCase. Constructs the es3fFragmentOutputTests.FragmentOutputCase object
     87     * @constructor
     88     * @extends {tcuTestCase.DeqpTest}
     89     * @param {string} name
     90     * @param {string} description
     91     * @param {Array<es3fFragmentOutputTests.BufferSpec>} fboSpec
     92     * @param {Array<es3fFragmentOutputTests.FragmentOutput>} outputs
     93     * @return {Object} The currently modified object
     94     */
     95    es3fFragmentOutputTests.FragmentOutputCase = function(name, description, fboSpec, outputs) {
     96        tcuTestCase.DeqpTest.call(this, name, description);
     97        /** @type {Array<es3fFragmentOutputTests.BufferSpec>} */ this.m_fboSpec = fboSpec;
     98        /** @type {Array<es3fFragmentOutputTests.FragmentOutput>} */ this.m_outputs = outputs;
     99        /** @type {gluShaderProgram.ShaderProgram} */ this.m_program = null;
    100        /** @type {WebGLFramebuffer} */ this.m_framebuffer = null;
    101 
    102        /** @type {WebGLRenderbuffer} */ this.m_renderbuffer = null;
    103    };
    104 
    105    es3fFragmentOutputTests.FragmentOutputCase.prototype = Object.create(tcuTestCase.DeqpTest.prototype);
    106    es3fFragmentOutputTests.FragmentOutputCase.prototype.constructor = es3fFragmentOutputTests.FragmentOutputCase;
    107 
    108    /**
    109     * es3fFragmentOutputTests.createProgram. Returns a ShaderProgram object
    110     * @param {Array<es3fFragmentOutputTests.FragmentOutput>} outputs
    111     * @return {gluShaderProgram.ShaderProgram} program
    112     */
    113    es3fFragmentOutputTests.createProgram = function(outputs) {
    114 
    115        var vtx = '';
    116        var frag = '';
    117 
    118        vtx = '#version 300 es\n' + 'in highp vec4 a_position;\n';
    119        frag = '#version 300 es\n';
    120 
    121    /** @type {es3fFragmentOutputTests.FragmentOutput} */ var output = null;
    122    /** @type {boolean} */ var isArray = false;
    123     // Input-output declarations.
    124        for (var outNdx = 0; outNdx < outputs.length; outNdx++) {
    125            output = outputs[outNdx];
    126            isArray = output.arrayLength > 0;
    127            /** @type {string} */ var typeName = gluShaderUtil.getDataTypeName(output.type);
    128            /** @type {string} */ var precName = gluShaderUtil.getPrecisionName(output.precision);
    129            /** @type {boolean} */ var isFloat = gluShaderUtil.isDataTypeFloatOrVec(output.type);
    130            /** @type {string} */ var interp = isFloat ? 'smooth' : 'flat';
    131 
    132            if (isArray) {
    133                for (var elemNdx = 0; elemNdx < output.arrayLength; elemNdx++) {
    134                    vtx += 'in ' + precName + ' ' + typeName + ' in' + outNdx + '_' + elemNdx + ';\n' +
    135                    interp + ' out ' + precName + ' ' + typeName + ' var' + outNdx + '_' + elemNdx + ';\n';
    136                    frag += interp + ' in ' + precName + ' ' + typeName + ' var' + outNdx + '_' + elemNdx + ';\n';
    137                }
    138                frag += 'layout(location = ' + output.location + ') out ' + precName + ' ' + typeName + ' out' + outNdx + '[' + output.arrayLength + '];\n';
    139            } else {
    140                vtx += 'in ' + precName + ' ' + typeName + ' in' + outNdx + ';\n' +
    141                interp + ' out ' + precName + ' ' + typeName + ' var' + outNdx + ';\n';
    142                frag += interp + ' in ' + precName + ' ' + typeName + ' var' + outNdx + ';\n' +
    143                'layout(location = ' + output.location + ') out ' + precName + ' ' + typeName + ' out' + outNdx + ';\n';
    144            }
    145        }
    146 
    147        vtx += '\nvoid main()\n{\n';
    148        frag += '\nvoid main()\n{\n';
    149 
    150        vtx += ' gl_Position = a_position;\n';
    151 
    152        // Copy body
    153        for (var outNdx = 0; outNdx < outputs.length; outNdx++) {
    154            output = outputs[outNdx];
    155            isArray = output.arrayLength > 0;
    156 
    157            if (isArray) {
    158                for (var elemNdx = 0; elemNdx < output.arrayLength; elemNdx++) {
    159                    vtx += '\tvar' + outNdx + '_' + elemNdx + ' = in' + outNdx + '_' + elemNdx + ';\n';
    160                    frag += '\tout' + outNdx + '[' + elemNdx + '] = var' + outNdx + '_' + elemNdx + ';\n';
    161                }
    162            } else {
    163                vtx += '\tvar' + outNdx + ' = in' + outNdx + ';\n';
    164                frag += '\tout' + outNdx + ' = var' + outNdx + ';\n';
    165            }
    166        }
    167 
    168        vtx += '}\n';
    169        frag += '}\n';
    170 
    171        /** @type {gluShaderProgram.ShaderProgram} */
    172        var program = new gluShaderProgram.ShaderProgram(gl, gluShaderProgram.makeVtxFragSources(vtx, frag));
    173        return program;
    174    };
    175 
    176    es3fFragmentOutputTests.FragmentOutputCase.prototype.init = function() {
    177        // Check that all attachments are supported
    178        for (var iter = 0; iter < this.m_fboSpec.length; ++iter) {
    179            if (!gluTextureUtil.isSizedFormatColorRenderable(this.m_fboSpec[iter].format))
    180                throw new Error('Unsupported attachment format');
    181        }
    182 
    183        DE_ASSERT(!this.m_program);
    184        this.m_program = es3fFragmentOutputTests.createProgram(this.m_outputs);
    185 
    186       // log << *m_program;
    187        if (!this.m_program.isOk())
    188            throw new Error('Compile failed. Program no created');
    189 
    190        /*
    191        // Print render target info to log.
    192        log << TestLog::Section("Framebuffer", "Framebuffer configuration");
    193 
    194        for (int ndx = 0; ndx < (int)m_fboSpec.size(); ndx++)
    195            log << TestLog::Message << "COLOR_ATTACHMENT" << ndx << ": "
    196                                    << glu::getPixelFormatStr(m_fboSpec[ndx].format) << ", "
    197                                    << m_fboSpec[ndx].width << "x" << m_fboSpec[ndx].height << ", "
    198                                    << m_fboSpec[ndx].samples << " samples"
    199                << TestLog::EndMessage;
    200 
    201        log << TestLog::EndSection;*/
    202 
    203        // Create framebuffer.
    204        this.m_framebuffer = gl.createFramebuffer();
    205        gl.bindFramebuffer(gl.FRAMEBUFFER, this.m_framebuffer);
    206 
    207        for (var bufNdx = 0; bufNdx < /* m_renderbuffers.size() */ this.m_fboSpec.length; bufNdx++) {
    208            this.m_renderbuffer = gl.createRenderbuffer();
    209            /** @type {es3fFragmentOutputTests.BufferSpec} */ var bufSpec = this.m_fboSpec[bufNdx];
    210            /** @type {number} */ var attachment = gl.COLOR_ATTACHMENT0 + bufNdx;
    211 
    212            gl.bindRenderbuffer(gl.RENDERBUFFER, this.m_renderbuffer);
    213 
    214            gl.renderbufferStorageMultisample(gl.RENDERBUFFER, bufSpec.samples, bufSpec.format, bufSpec.width, bufSpec.height);
    215            gl.framebufferRenderbuffer(gl.FRAMEBUFFER, attachment, gl.RENDERBUFFER, this.m_renderbuffer);
    216        }
    217        /** @type {number} */ var fboStatus = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
    218 
    219        if (fboStatus == gl.FRAMEBUFFER_UNSUPPORTED)
    220            throw new Error('Framebuffer not supported');
    221        else if (fboStatus != gl.FRAMEBUFFER_COMPLETE)
    222            throw new Error('Incomplete framebuffer');
    223            // throw tcu::TestError((string("Incomplete framebuffer: ") + glu::getFramebufferStatusStr(fboStatus), "", __FILE__, __LINE__);
    224 
    225        // gl.bindRenderbuffer(gl.RENDERBUFFER, null); // TODO: maybe needed?
    226        gl.bindFramebuffer(gl.FRAMEBUFFER, null);
    227    };
    228 
    229    es3fFragmentOutputTests.FragmentOutputCase.prototype.deinit = function() {
    230        // TODO: implement?
    231    };
    232 
    233    /**
    234     * es3fFragmentOutputTests.getMinSize.
    235     * @param {Array<es3fFragmentOutputTests.BufferSpec>} fboSpec
    236     * @return {Array<number>} minSize
    237     */
    238    es3fFragmentOutputTests.getMinSize = function(fboSpec) {
    239        /** @type {Array<number>} */ var minSize = [0x7fffffff, 0x7fffffff];
    240        for (var i = 0; i < fboSpec.length; i++) {
    241            minSize[0] = Math.min(minSize[0], fboSpec[i].width);
    242            minSize[1] = Math.min(minSize[1], fboSpec[i].height);
    243        }
    244        return minSize;
    245    };
    246 
    247    /**
    248     * es3fFragmentOutputTests.getNumInputVectors. Returns the length of the array of all the outputs (es3fFragmentOutputTests.FragmentOutput object)
    249     * @param {Array<es3fFragmentOutputTests.FragmentOutput>} outputs
    250     * @return {number} numVecs
    251     */
    252    es3fFragmentOutputTests.getNumInputVectors = function(outputs) {
    253        /** @type {number} */ var numVecs = 0;
    254        for (var i = 0; i < outputs.length; i++)
    255            numVecs += (outputs[i].arrayLength > 0 ? outputs[i].arrayLength : 1);
    256        return numVecs;
    257    };
    258 
    259    /**
    260     * es3fFragmentOutputTests.getFloatRange
    261     * @param {gluShaderUtil.precision} precision
    262     * @return {Array<number>} Vec2
    263     */
    264    es3fFragmentOutputTests.getFloatRange = function(precision) {
    265        /** @type {Array<Array<number>>} */
    266        var ranges = // Vec2
    267        [
    268            [-2.0, 2.0],
    269            [-16000.0, 16000.0],
    270            [-1e35, 1e35]
    271        ];
    272        // DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(ranges) == glu::PRECISION_LAST);
    273        // DE_ASSERT(de::inBounds<int>(precision, 0, DE_LENGTH_OF_ARRAY(ranges)));
    274        return ranges[precision];
    275    };
    276 
    277    /**
    278     * es3fFragmentOutputTests.getIntRange
    279     * @param {gluShaderUtil.precision} precision
    280     * @return {Array<number>} IVec2
    281     */
    282    es3fFragmentOutputTests.getIntRange = function(precision) {
    283        /** @type {Array<Array<number>>} */
    284        var ranges = // IVec2
    285        [
    286            [-(1 << 7), (1 << 7) - 1],
    287            [-(1 << 15), (1 << 15) - 1],
    288            [-0x80000000, 0x7fffffff]
    289        ];
    290        // DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(ranges) == glu::PRECISION_LAST);
    291        // DE_ASSERT(de::inBounds<int>(precision, 0, DE_LENGTH_OF_ARRAY(ranges)));
    292        return ranges[precision];
    293    };
    294 
    295    /**
    296     * es3fFragmentOutputTests.getUintRange
    297     * @param {gluShaderUtil.precision} precision
    298     * @return {Array<number>} UVec2
    299     */
    300    es3fFragmentOutputTests.getUintRange = function(precision) {
    301        /** @type {Array<Array<number>>} */
    302        var ranges = // UVec2
    303        [
    304            [0, (1 << 8) - 1],
    305            [0, (1 << 16) - 1],
    306            [0, 0xffffffff]
    307        ];
    308        // DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(ranges) == glu::PRECISION_LAST);
    309        // DE_ASSERT(de::inBounds<int>(precision, 0, DE_LENGTH_OF_ARRAY(ranges)));
    310        return ranges[precision];
    311 
    312    };
    313 
    314    /**
    315     * es3fFragmentOutputTests.readVec4
    316     * @param {Array<number>} ptr
    317     * @param {number} index
    318     * @param {number} numComponents
    319     * @return {Array<number>} Vec4
    320     */
    321    es3fFragmentOutputTests.readVec4 = function(ptr, index, numComponents) {
    322        DE_ASSERT(numComponents >= 1);
    323        return [
    324                ptr[index + 0],
    325                numComponents >= 2 ? ptr[index + 1] : 0.0,
    326                numComponents >= 3 ? ptr[index + 2] : 0.0,
    327                numComponents >= 4 ? ptr[index + 3] : 0.0
    328                ];
    329    };
    330 
    331    /**
    332     * es3fFragmentOutputTests.readIVec4
    333     * @param {Array<number>} ptr
    334     * @param {number} numComponents
    335     * @return {Array<number>} IVec4
    336     */
    337    es3fFragmentOutputTests.readIVec4 = function(ptr, index, numComponents) {
    338        DE_ASSERT(numComponents >= 1);
    339        return [
    340                ptr[index + 0],
    341                numComponents >= 2 ? ptr[index + 1] : 0,
    342                numComponents >= 3 ? ptr[index + 2] : 0,
    343                numComponents >= 4 ? ptr[index + 3] : 0
    344                ];
    345    };
    346 
    347    /**
    348     * es3fFragmentOutputTests.renderFloatReference
    349     * @param {tcuTexture.PixelBufferAccess} dst
    350     * @param {number} gridWidth
    351     * @param {number} gridHeight
    352     * @param {number} numComponents
    353     * @param {Array<number>} vertices
    354     */
    355    es3fFragmentOutputTests.renderFloatReference = function(dst, gridWidth, gridHeight, numComponents, vertices) {
    356 
    357        /** @type {boolean} */ var isSRGB = dst.getFormat().order == tcuTexture.ChannelOrder.sRGB || dst.getFormat().order == tcuTexture.ChannelOrder.sRGBA;
    358        /** @type {number} */ var cellW = dst.getWidth() / (gridWidth - 1);
    359        /** @type {number} */ var cellH = dst.getHeight() / (gridHeight - 1);
    360 
    361        for (var y = 0; y < dst.getHeight(); y++) {
    362            for (var x = 0; x < dst.getWidth(); x++) {
    363                /** @type {number} */ var cellX = deMath.clamp(Math.floor(x / cellW), 0, gridWidth - 2);
    364                /** @type {number} */ var cellY = deMath.clamp(Math.floor(y / cellH), 0, gridHeight - 2);
    365                /** @type {number} */ var xf = (x - cellX * cellW + 0.5) / cellW;
    366                /** @type {number} */ var yf = (y - cellY * cellH + 0.5) / cellH;
    367 
    368                /** @type {Array<number>} */ var v00 = es3fFragmentOutputTests.readVec4(vertices, ((cellY + 0) * gridWidth + cellX + 0) * numComponents, numComponents); // Vec4
    369                /** @type {Array<number>} */ var v01 = es3fFragmentOutputTests.readVec4(vertices, ((cellY + 1) * gridWidth + cellX + 0) * numComponents, numComponents); // Vec4
    370                /** @type {Array<number>} */ var v10 = es3fFragmentOutputTests.readVec4(vertices, ((cellY + 0) * gridWidth + cellX + 1) * numComponents, numComponents); // Vec4
    371                /** @type {Array<number>} */ var v11 = es3fFragmentOutputTests.readVec4(vertices, ((cellY + 1) * gridWidth + cellX + 1) * numComponents, numComponents); // Vec4
    372 
    373                /** @type {boolean} */ var tri = xf + yf >= 1.0;
    374                /** @type {Array<number>} */ var v0 = tri ? v11 : v00; // Vec4&
    375                /** @type {Array<number>} */ var v1 = tri ? v01 : v10; // Vec4&
    376                /** @type {Array<number>} */ var v2 = tri ? v10 : v01; // Vec4&
    377                /** @type {number} */ var s = tri ? 1.0 - xf : xf;
    378                /** @type {number} */ var t = tri ? 1.0 - yf : yf;
    379                /** @type {Array<number>} */ var color = deMath.add(v0, deMath.add(deMath.multiply((deMath.subtract(v1, v0)), [s, s, s, s]), deMath.multiply((deMath.subtract(v2, v0)), [t, t, t, t]))); // Vec4
    380 
    381                dst.setPixel(isSRGB ? tcuTextureUtil.linearToSRGB(color) : color, x, y);
    382            }
    383        }
    384    };
    385 
    386    /**
    387     * es3fFragmentOutputTests.renderIntReference
    388     * @param {tcuTexture.PixelBufferAccess} dst
    389     * @param {number} gridWidth
    390     * @param {number} gridHeight
    391     * @param {number} numComponents
    392     * @param {Array<number>} vertices
    393     */
    394    es3fFragmentOutputTests.renderIntReference = function(dst, gridWidth, gridHeight, numComponents, vertices) {
    395 
    396        /** @type {number} */ var cellW = dst.getWidth() / (gridWidth - 1);
    397        /** @type {number} */ var cellH = dst.getHeight() / (gridHeight - 1);
    398 
    399        for (var y = 0; y < dst.getHeight(); y++) {
    400            for (var x = 0; x < dst.getWidth(); x++) {
    401                /** @type {number} */ var cellX = deMath.clamp(Math.floor(x / cellW), 0, gridWidth - 2);
    402                /** @type {number} */ var cellY = deMath.clamp(Math.floor(y / cellH), 0, gridHeight - 2);
    403                /** @type {Array<number>} */ var c = es3fFragmentOutputTests.readIVec4(vertices, (cellY * gridWidth + cellX + 1) * numComponents, numComponents); // IVec4
    404 
    405                dst.setPixelInt(c, x, y);
    406            }
    407        }
    408    };
    409 
    410    /**
    411     * es3fFragmentOutputTests.s_swizzles
    412     * @return {Array<Array<number>>}
    413     */
    414    es3fFragmentOutputTests.s_swizzles = function() {
    415        var mat_swizzles = [
    416            [0, 1, 2, 3],
    417            [1, 2, 3, 0],
    418            [2, 3, 0, 1],
    419            [3, 0, 1, 2],
    420            [3, 2, 1, 0],
    421            [2, 1, 0, 3],
    422            [1, 0, 3, 2],
    423            [0, 3, 2, 1]
    424        ];
    425 
    426        return mat_swizzles;
    427    };
    428 
    429    /**
    430     * es3fFragmentOutputTests.swizzleVec. Returns an Array from a position contained in the Array es3fFragmentOutputTests.s_swizzles []
    431     * @param {Array<number>} vec
    432     * @param {number} swzNdx
    433     * @return {Array<number>} Swizzled array
    434     */
    435    es3fFragmentOutputTests.swizzleVec = function(vec, swzNdx) {
    436    /** @type {Array<number>} */ var swz = es3fFragmentOutputTests.s_swizzles()[swzNdx % es3fFragmentOutputTests.s_swizzles().length];
    437 
    438        return deMath.swizzle(vec, swz);
    439    };
    440 
    441    /**
    442     * es3fFragmentOutputTests.AttachmentData struct class
    443     * @constructor
    444     * @return {Object}
    445     */
    446    es3fFragmentOutputTests.AttachmentData = function() {
    447        return {
    448        /** @type {tcuTexture.TextureFormat} */ format: null, //!< Actual format of attachment.
    449        /** @type {tcuTexture.TextureFormat} */ referenceFormat: null, //!< Used for reference rendering.
    450        /** @type {tcuTexture.TextureFormat} */ readFormat: null,
    451        /** @type {number} */ numWrittenChannels: 0,
    452        /** @type {gluShaderUtil.precision} */ outPrecision: gluShaderUtil.precision.PRECISION_LOWP,
    453        /** @type {ArrayBuffer} */ renderedData: null,
    454        /** @type {ArrayBuffer} */ referenceData: null
    455        };
    456    };
    457 
    458    es3fFragmentOutputTests.FragmentOutputCase.prototype.iterate = function() {
    459        // Compute grid size & index list.
    460        /** @type {number} */ var minCellSize = 8;
    461        /** @type {Array<number>} */ var minBufSize = es3fFragmentOutputTests.getMinSize(this.m_fboSpec); // IVec2
    462        /** @type {number} */ var gridWidth = deMath.clamp(Math.floor(minBufSize[0] / minCellSize), 1, 255) + 1;
    463        /** @type {number} */ var gridHeight = deMath.clamp(Math.floor(minBufSize[1] / minCellSize), 1, 255) + 1;
    464        /** @type {number} */ var numVertices = gridWidth * gridHeight;
    465        /** @type {number} */ var numQuads = (gridWidth - 1) * (gridHeight - 1);
    466        /** @type {number} */ var numIndices = numQuads * 6;
    467 
    468        /** @type {number} */ var numInputVecs = es3fFragmentOutputTests.getNumInputVectors(this.m_outputs);
    469        /** @type {Array<Array<number>>} */ var inputs = []; // originally vector<vector<deUint32>
    470 
    471        for (var inputNdx = 0; inputNdx < numInputVecs; inputNdx++)
    472            inputs[inputNdx] = []; // inputs.length = numInputVecs;
    473 
    474        /** @type {Array<number>} */ var positions = []; // originally vector<float>
    475        /** @type {Array<number>} */ var indices = []; // originally vector<deUint16>
    476 
    477        /** @type {number} */ var readAlignment = 4;
    478        /** @type {number} */ var viewportW = minBufSize[0];
    479        /** @type {number} */ var viewportH = minBufSize[1];
    480        /** @type {number} */ var numAttachments = this.m_fboSpec.length;
    481 
    482        /** @type {Array<number>} */ var drawBuffers = []; // originally vector<deUint32>
    483        /** @type {Array<es3fFragmentOutputTests.AttachmentData>} */ var attachments = [];
    484        /** @type {number} */ var attachmentW;
    485        /** @type {number} */ var attachmentH;
    486 
    487        // Initialize attachment data.
    488        for (var ndx = 0; ndx < numAttachments; ndx++) {
    489            /** @type {tcuTexture.TextureFormat} */ var texFmt = gluTextureUtil.mapGLInternalFormat(this.m_fboSpec[ndx].format);
    490            /** @type {tcuTexture.TextureChannelClass} */ var chnClass = tcuTexture.getTextureChannelClass(texFmt.type);
    491            /** @type {boolean} */ var isFixedPoint = (chnClass == tcuTexture.TextureChannelClass.SIGNED_FIXED_POINT ||
    492                                                              chnClass == tcuTexture.TextureChannelClass.UNSIGNED_FIXED_POINT);
    493 
    494            // \note Fixed-point formats use float reference to enable more accurate result verification.
    495            /** @type {tcuTexture.TextureFormat} */ var refFmt = isFixedPoint ? new tcuTexture.TextureFormat(texFmt.order, tcuTexture.ChannelType.FLOAT) : texFmt;
    496            /** @type {tcuTexture.TextureFormat} */ var readFmt = es3fFboTestUtil.getFramebufferReadFormat(texFmt);
    497            attachmentW = this.m_fboSpec[ndx].width;
    498            attachmentH = this.m_fboSpec[ndx].height;
    499 
    500            drawBuffers[ndx] = gl.COLOR_ATTACHMENT0 + ndx;
    501            attachments[ndx] = new es3fFragmentOutputTests.AttachmentData();
    502            attachments[ndx].format = texFmt;
    503            attachments[ndx].readFormat = readFmt;
    504            attachments[ndx].referenceFormat = refFmt;
    505            attachments[ndx].renderedData = new ArrayBuffer(readFmt.getPixelSize() * attachmentW * attachmentH);
    506            attachments[ndx].referenceData = new ArrayBuffer(refFmt.getPixelSize() * attachmentW * attachmentH);
    507        }
    508 
    509        // Initialize indices.
    510        for (var quadNdx = 0; quadNdx < numQuads; quadNdx++) {
    511            /** @type {number} */ var quadY = Math.floor(quadNdx / (gridWidth - 1));
    512            /** @type {number} */ var quadX = quadNdx - quadY * (gridWidth - 1);
    513 
    514            indices[quadNdx * 6 + 0] = quadX + quadY * gridWidth;
    515            indices[quadNdx * 6 + 1] = quadX + (quadY + 1) * gridWidth;
    516            indices[quadNdx * 6 + 2] = quadX + quadY * gridWidth + 1;
    517            indices[quadNdx * 6 + 3] = indices[quadNdx * 6 + 1];
    518            indices[quadNdx * 6 + 4] = quadX + (quadY + 1) * gridWidth + 1;
    519            indices[quadNdx * 6 + 5] = indices[quadNdx * 6 + 2];
    520        }
    521 
    522        /** @type {number} */ var xf = 0;
    523        /** @type {number} */ var yf = 0;
    524        for (var y = 0; y < gridHeight; y++) {
    525            for (var x = 0; x < gridWidth; x++) {
    526                xf = x / (gridWidth - 1);
    527                yf = y / (gridHeight - 1);
    528 
    529                positions[(y * gridWidth + x) * 4 + 0] = 2.0 * xf - 1.0;
    530                positions[(y * gridWidth + x) * 4 + 1] = 2.0 * yf - 1.0;
    531                positions[(y * gridWidth + x) * 4 + 2] = 0.0;
    532                positions[(y * gridWidth + x) * 4 + 3] = 1.0;
    533            }
    534        }
    535        /** @type {es3fFragmentOutputTests.FragmentOutput} */ var output;
    536        /** @type {boolean} */ var isArray;
    537        /** @type {boolean} */ var isFloat;
    538        /** @type {boolean} */ var isInt;
    539        /** @type {boolean} */ var isUint;
    540        /** @type {number} */ var numVecs;
    541        /** @type {number} */ var numScalars;
    542 
    543        var curInVec = 0;
    544        for (var outputNdx = 0; outputNdx < this.m_outputs.length; outputNdx++) {
    545            output = this.m_outputs[outputNdx];
    546            isFloat = gluShaderUtil.isDataTypeFloatOrVec(output.type);
    547            isInt = gluShaderUtil.isDataTypeIntOrIVec(output.type);
    548            isUint = gluShaderUtil.isDataTypeUintOrUVec(output.type);
    549            numVecs = output.arrayLength > 0 ? output.arrayLength : 1;
    550            numScalars = gluShaderUtil.getDataTypeScalarSize(output.type);
    551 
    552            for (var vecNdx = 0; vecNdx < numVecs; vecNdx++) {
    553                inputs[curInVec].length = numVertices * numScalars;
    554 
    555                // Record how many outputs are written in attachment.
    556                DE_ASSERT(output.location + vecNdx < attachments.length);
    557                attachments[output.location + vecNdx].numWrittenChannels = numScalars;
    558                attachments[output.location + vecNdx].outPrecision = output.precision;
    559 
    560                /** @type {Array<number>} */ var range = null;
    561                /** @type {Array<number>} */ var minVal = null;
    562                /** @type {Array<number>} */ var maxVal = null;
    563                /** @type {Array<number>} */ var fmtBits = null;
    564                /** @type {Array<number>} */ var fmtMaxVal = [];
    565                /** @type {Array<number>} */ var rangeDiv = null;
    566                /** @type {Array<number>} */ var step = [];
    567                /** @type {number} */ var ix = 0;
    568                /** @type {number} */ var iy = 0;
    569                /** @type {Array<number>} */ var c = null;
    570                /** @type {number} */ var pos = 0;
    571               if (isFloat) {
    572                    range = es3fFragmentOutputTests.getFloatRange(output.precision); // Vec2
    573                    minVal = [range[0], range[0], range[0], range[0]]; // Vec4
    574                    maxVal = [range[1], range[1], range[1], range[1]]; // Vec4
    575 
    576                    if (deMath.deInBounds32(output.location + vecNdx, 0, attachments.length)) {
    577                    // \note Floating-point precision conversion is not well-defined. For that reason we must
    578                    // limit value range to intersection of both data type and render target value ranges.
    579                    /** @type {tcuTextureUtil.TextureFormatInfo} */ var fmtInfo = tcuTextureUtil.getTextureFormatInfo(attachments[output.location + vecNdx].format);
    580                        minVal = deMath.max(minVal, fmtInfo.valueMin);
    581                        maxVal = deMath.min(maxVal, fmtInfo.valueMax);
    582                    }
    583 
    584                    bufferedLogToConsole('out ' + curInVec + ' value range: ' + minVal + ' -> ' + maxVal);
    585 
    586                    for (var y = 0; y < gridHeight; y++) {
    587                        for (var x = 0; x < gridWidth; x++) {
    588                            xf = x / (gridWidth - 1);
    589                            yf = y / (gridHeight - 1);
    590                            /** @type {number} */ var f0 = (xf + yf) * 0.5;
    591                            /** @type {number} */ var f1 = 0.5 + (xf - yf) * 0.5;
    592 
    593                            /** @type {Array<number>} */ var f = es3fFragmentOutputTests.swizzleVec([f0, f1, 1.0 - f0, 1.0 - f1], curInVec); // Vec4
    594                            c = deMath.add(minVal, deMath.multiply(deMath.subtract(maxVal, minVal), f)); // Vec4
    595 
    596                            pos = (y * gridWidth + x) * numScalars;
    597 
    598                            for (var ndx = 0; ndx < numScalars; ndx++)
    599                                inputs[curInVec][pos + ndx] = c[ndx];
    600                        }
    601                    }
    602                } else if (isInt) {
    603                    range = es3fFragmentOutputTests.getIntRange(output.precision); // IVec2
    604                    minVal = [range[0], range[0], range[0], range[0]]; // IVec4
    605                    maxVal = [range[1], range[1], range[1], range[1]]; // IVec4
    606 
    607                    if (deMath.deInBounds32(output.location + vecNdx, 0, attachments.length)) {
    608                        // Limit to range of output format as conversion mode is not specified.
    609                        fmtBits = tcuTextureUtil.getTextureFormatBitDepth(attachments[output.location + vecNdx].format); // IVec4
    610                        /** @type {Array<boolean>} */ var isZero = deMath.lessThanEqual(fmtBits, [0, 0, 0, 0]); // BVec4, array of booleans, size = 4
    611 
    612                        /** @type {Array<number>} */ var fmtMinVal = []; // IVec4
    613 
    614                        for (var i = 0; i < 4; i++) {
    615 
    616                            // const IVec4 fmtMinVal = (-(tcu::Vector<deInt64, 4>(1) << (fmtBits - 1 ).cast<deInt64>())).asInt();
    617                            fmtMinVal[i] = -1 * Math.pow(2, fmtBits[i] - 1); // TODO: check implementation, original above
    618                            // const IVec4 fmtMaxVal = ((tcu::Vector<deInt64, 4>(1) << (fmtBits - 1 ).cast<deInt64>()) - deInt64(1)).asInt();
    619                            fmtMaxVal[i] = Math.pow(2, fmtBits[i] - 1) - 1; // TODO: check implementation, original above
    620                        }
    621 
    622                        minVal = tcuTextureUtil.select(minVal, deMath.max(minVal, fmtMinVal), isZero);
    623                        maxVal = tcuTextureUtil.select(maxVal, deMath.min(maxVal, fmtMaxVal), isZero);
    624                    }
    625 
    626                    bufferedLogToConsole('out ' + curInVec + ' value range: ' + minVal + ' -> ' + maxVal);
    627 
    628                    rangeDiv = es3fFragmentOutputTests.swizzleVec([gridWidth - 1, gridHeight - 1, gridWidth - 1, gridHeight - 1], curInVec); // IVec4
    629                    for (var i = 0; i < 4; i++) {
    630                        // const IVec4 step = ((maxVal.cast<deInt64>() - minVal.cast<deInt64>()) / (rangeDiv.cast<deInt64>())).asInt();
    631                        step[i] = Math.floor((maxVal[i] - minVal[i]) / rangeDiv[i]); // TODO: check with the above line of code
    632                    }
    633 
    634                    for (var y = 0; y < gridHeight; y++) {
    635                        for (var x = 0; x < gridWidth; x++) {
    636                            ix = gridWidth - x - 1;
    637                            iy = gridHeight - y - 1;
    638                            c = deMath.add(minVal, deMath.multiply(step, es3fFragmentOutputTests.swizzleVec([x, y, ix, iy], curInVec))); // IVec4
    639 
    640                            pos = (y * gridWidth + x) * numScalars;
    641 
    642                            for (var ndx = 0; ndx < numScalars; ndx++)
    643                                inputs[curInVec][pos + ndx] = c[ndx];
    644                        }
    645                    }
    646                } else if (isUint) {
    647                    range = es3fFragmentOutputTests.getUintRange(output.precision); // UVec2
    648                    maxVal = [range[1], range[1], range[1], range[1]]; // UVec4
    649 
    650                    if (deMath.deInBounds32(output.location + vecNdx, 0, attachments.length)) {
    651                        // Limit to range of output format as conversion mode is not specified.
    652                        fmtBits = tcuTextureUtil.getTextureFormatBitDepth(attachments[output.location + vecNdx].format); // IVec4
    653 
    654                        for (var i = 0; i < 4; i++) {
    655                            fmtMaxVal[i] = Math.pow(2, fmtBits[i]) - 1;
    656                        }
    657 
    658                        maxVal = deMath.min(maxVal, fmtMaxVal);
    659                    }
    660 
    661                    bufferedLogToConsole('out ' + curInVec + ' value range: ' + minVal + ' -> ' + maxVal);
    662 
    663                    rangeDiv = es3fFragmentOutputTests.swizzleVec([gridWidth - 1, gridHeight - 1, gridWidth - 1, gridHeight - 1], curInVec); // IVec4
    664 
    665                    for (var stepPos = 0; stepPos < maxVal.length; stepPos++) {
    666                        step[stepPos] = Math.floor(maxVal[stepPos] / rangeDiv[stepPos]);
    667                    }
    668 
    669                    DE_ASSERT(range[0] == 0);
    670 
    671                    for (var y = 0; y < gridHeight; y++) {
    672                        for (var x = 0; x < gridWidth; x++) {
    673                            ix = gridWidth - x - 1;
    674                            iy = gridHeight - y - 1;
    675                            c = deMath.multiply(step, es3fFragmentOutputTests.swizzleVec([x, y, ix, iy], curInVec)); // UVec4
    676                            pos = (y * gridWidth + x) * numScalars;
    677 
    678                            DE_ASSERT(deMath.boolAll(deMath.lessThanEqual(c, maxVal))); // TODO: sometimes crashes here, condition not asserted
    679 
    680                            for (var ndx = 0; ndx < numScalars; ndx++)
    681                                inputs[curInVec][pos + ndx] = c[ndx];
    682                        }
    683                    }
    684                } else
    685                    DE_ASSERT(false);
    686 
    687                curInVec += 1;
    688            }
    689        }
    690 
    691        // Render using gl.
    692        gl.useProgram(this.m_program.getProgram());
    693        gl.bindFramebuffer(gl.FRAMEBUFFER, this.m_framebuffer);
    694        gl.viewport(0, 0, viewportW, viewportH);
    695        gl.drawBuffers(drawBuffers);
    696        gl.disable(gl.DITHER); // Dithering causes issues with unorm formats. Those issues could be worked around in threshold, but it makes validation less accurate.
    697 
    698        /** @type {WebGLBuffer} */ var buffer = null;
    699        /** @type {string} */ var name;
    700        curInVec = 0;
    701        for (var outputNdx = 0; outputNdx < this.m_outputs.length; outputNdx++) {
    702            output = this.m_outputs[outputNdx];
    703            isArray = output.arrayLength > 0;
    704            isFloat = gluShaderUtil.isDataTypeFloatOrVec(output.type);
    705            isInt = gluShaderUtil.isDataTypeIntOrIVec(output.type);
    706            isUint = gluShaderUtil.isDataTypeUintOrUVec(output.type);
    707            /** @type {number} */ var scalarSize = gluShaderUtil.getDataTypeScalarSize(output.type);
    708            /** @type {number} */ var glScalarType = isFloat ? /* gluShaderUtil.DataType.FLOAT */ gl.FLOAT :
    709                                                     isInt ? /* gluShaderUtil.DataType.INT */ gl.INT :
    710                                                     isUint ? /* gluShaderUtil.DataType.UINT */ gl.UNSIGNED_INT : /* gluShaderUtil.DataType.INVALID */ gl.NONE;
    711            numVecs = isArray ? output.arrayLength : 1;
    712 
    713            for (var vecNdx = 0; vecNdx < numVecs; vecNdx++) {
    714                name = 'in' + outputNdx + (isArray ? '_' + vecNdx : '');
    715                /** @type {number} */ var loc = gl.getAttribLocation(this.m_program.getProgram(), name);
    716 
    717                if (loc >= 0) {
    718                    buffer = gl.createBuffer();
    719                    gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
    720 
    721                    gl.enableVertexAttribArray(loc);
    722                    if (isFloat) {
    723                        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(inputs[curInVec]), gl.STATIC_DRAW);
    724                        // KHRONOS WebGL 1.0 specification:
    725                        // void vertexAttribPointer(GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, GLintptr offset);
    726                        gl.vertexAttribPointer(loc, scalarSize, glScalarType, false, 0, 0); // offset = 0
    727                    } else {
    728                        gl.bufferData(gl.ARRAY_BUFFER, new Int32Array(inputs[curInVec]), gl.STATIC_DRAW);
    729                        // KHRONOS WebGL 2.0 specification:
    730                        // void vertexAttribIPointer(GLuint index, GLint size, GLenum type, GLsizei stride, GLintptr offset)
    731                        gl.vertexAttribIPointer(loc, scalarSize, glScalarType, 0, 0); // offset = 0
    732                    }
    733                } else
    734                    bufferedLogToConsole('Warning: No location for attribute "' + name + '" found.');
    735 
    736                curInVec += 1;
    737            }
    738        }
    739 
    740        /** @type {number} */ var posLoc = gl.getAttribLocation(this.m_program.getProgram(), 'a_position');
    741        // TCU_CHECK(posLoc >= 0);
    742        buffer = gl.createBuffer();
    743        gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
    744        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
    745 
    746        gl.enableVertexAttribArray(posLoc);
    747        gl.vertexAttribPointer(posLoc, 4, gl.FLOAT, false, 0, 0); // offset = 0
    748 
    749        /** @type {WebGLBuffer} */ var indexObject = gl.createBuffer();
    750        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexObject);
    751        gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW);
    752 
    753        gl.drawElements(gl.TRIANGLES, numIndices, gl.UNSIGNED_SHORT, 0); // offset = 0
    754 
    755        // Render reference images.
    756 
    757        var curInNdx = 0;
    758        for (var outputNdx = 0; outputNdx < this.m_outputs.length; outputNdx++) {
    759            output = this.m_outputs[outputNdx];
    760            isArray = output.arrayLength > 0;
    761            isFloat = gluShaderUtil.isDataTypeFloatOrVec(output.type);
    762            isInt = gluShaderUtil.isDataTypeIntOrIVec(output.type);
    763            isUint = gluShaderUtil.isDataTypeUintOrUVec(output.type);
    764            scalarSize = gluShaderUtil.getDataTypeScalarSize(output.type);
    765            numVecs = isArray ? output.arrayLength : 1;
    766 
    767            for (var vecNdx = 0; vecNdx < numVecs; vecNdx++) {
    768                /** @type {number} */ var location = output.location + vecNdx;
    769                /** @type {Array<number>} */ var inputData = inputs[curInNdx];
    770 
    771                DE_ASSERT(deMath.deInBounds32(location, 0, this.m_fboSpec.length));
    772 
    773                /** @type {number} */ var bufW = this.m_fboSpec[location].width;
    774                /** @type {number} */ var bufH = this.m_fboSpec[location].height;
    775                /** @type {Object} */ var descriptor = {
    776                        format: attachments[location].referenceFormat,
    777                        width: bufW,
    778                        height: bufH,
    779                        depth: 1,
    780                        data: attachments[location].referenceData // ArrayBuffer
    781                };
    782                /** @type {tcuTexture.PixelBufferAccess} */ var buf = new tcuTexture.PixelBufferAccess(descriptor);
    783                /** @type {tcuTexture.PixelBufferAccess} */ var viewportBuf = tcuTextureUtil.getSubregion(buf, 0, 0, 0, viewportW, viewportH, 1);
    784 
    785                if (isInt || isUint)
    786                    es3fFragmentOutputTests.renderIntReference(viewportBuf, gridWidth, gridHeight, scalarSize, inputData);
    787                else if (isFloat)
    788                    es3fFragmentOutputTests.renderFloatReference(viewportBuf, gridWidth, gridHeight, scalarSize, inputData);
    789                else
    790                    DE_ASSERT(false);
    791 
    792                curInNdx += 1;
    793            }
    794        }
    795 
    796        // Compare all images.
    797        /** @type {boolean} */ var allLevelsOk = true;
    798        for (var attachNdx = 0; attachNdx < numAttachments; attachNdx++) {
    799            attachmentW = this.m_fboSpec[attachNdx].width;
    800            attachmentH = this.m_fboSpec[attachNdx].height;
    801            /** @type {number} */ var numValidChannels = attachments[attachNdx].numWrittenChannels;
    802            /** @type {Array<boolean>} */ var cmpMask = [numValidChannels >= 1, numValidChannels >= 2, numValidChannels >= 3, numValidChannels >= 4];
    803            /** @type {gluShaderUtil.precision} */ var outPrecision = attachments[attachNdx].outPrecision;
    804            /** @type {tcuTexture.TextureFormat} */ var format = attachments[attachNdx].format;
    805            /** @type {Object} */
    806            var renderedDescriptor = {
    807                    format: attachments[attachNdx].readFormat,
    808                    width: attachmentW,
    809                    height: attachmentH,
    810                    depth: 1,
    811                    rowPitch: deMath.deAlign32(attachments[attachNdx].readFormat.getPixelSize() * attachmentW, readAlignment),
    812                    slicePitch: 0,
    813                    data: attachments[attachNdx].renderedData // ArrayBuffer
    814            };
    815            /** @type {tcuTexture.PixelBufferAccess} */ var rendered = new tcuTexture.PixelBufferAccess(renderedDescriptor);
    816            /** @type {gluTextureUtil.TransferFormat} */ var transferFmt = gluTextureUtil.getTransferFormat(attachments[attachNdx].readFormat);
    817            gl.readBuffer(gl.COLOR_ATTACHMENT0 + attachNdx);
    818            gl.readPixels(0, 0, attachmentW, attachmentH, transferFmt.format, transferFmt.dataType, rendered.getDataPtr());
    819 
    820            /** @type {Object} */
    821            var referenceDescriptor = {
    822                    format: attachments[attachNdx].referenceFormat,
    823                    width: attachmentW,
    824                    height: attachmentH,
    825                    depth: 1,
    826                    data: attachments[attachNdx].referenceData // ArrayBuffer
    827            };
    828            /** @type {tcuTexture.ConstPixelBufferAccess} */ var reference = new tcuTexture.ConstPixelBufferAccess(referenceDescriptor);
    829            /** @type {tcuTexture.TextureChannelClass} */ var texClass = tcuTexture.getTextureChannelClass(format.type);
    830            /** @type {boolean} */ var isOk = true;
    831            name = 'Attachment ' + attachNdx;
    832            /** @type {string} */ var desc = 'Color attachment ' + attachNdx;
    833            /** @type {Array<number>} */ var threshold;
    834 
    835            bufferedLogToConsole('Attachment ' + attachNdx + ': ' + numValidChannels + ' channels have defined values and used for comparison');
    836 
    837            switch (texClass) {
    838                case tcuTexture.TextureChannelClass.FLOATING_POINT: {
    839                    /** @type {Array<number>} */ var formatThreshold = []; // UVec4 //!< Threshold computed based on format.
    840                    formatThreshold.length = 4;
    841                    /** @type {number} */ var precThreshold = 0; // deUint32 //!< Threshold computed based on output type precision
    842                    /** @type {Array<number>} */ var finalThreshold = []; // UVec4
    843                    finalThreshold.length = 4;
    844 
    845                    switch (format.type) {
    846                        case tcuTexture.ChannelType.FLOAT:
    847                            formatThreshold = [4, 4, 4, 4]; // UVec4
    848                            break;
    849                        case tcuTexture.ChannelType.HALF_FLOAT:
    850                            formatThreshold = [(1 << 13) + 4, (1 << 13) + 4, (1 << 13) + 4, (1 << 13) + 4]; // UVec4
    851                            break;
    852                        case tcuTexture.ChannelType.UNSIGNED_INT_11F_11F_10F_REV:
    853                            formatThreshold = [(1 << 17) + 4, (1 << 17) + 4, (1 << 18) + 4, 4]; // UVec4
    854                            break;
    855                        default:
    856                            DE_ASSERT(false);
    857                            break;
    858                    }
    859 
    860                    switch (outPrecision) {
    861                        case gluShaderUtil.precision.PRECISION_LOWP:
    862                            precThreshold = (1 << 21);
    863                            break;
    864                        case gluShaderUtil.precision.PRECISION_MEDIUMP:
    865                            precThreshold = (1 << 13);
    866                            break;
    867                        case gluShaderUtil.precision.PRECISION_HIGHP:
    868                            precThreshold = 0;
    869                            break;
    870                        default:
    871                            DE_ASSERT(false);
    872                    }
    873 
    874                    finalThreshold = tcuTextureUtil.select(
    875                                    deMath.max(formatThreshold, [precThreshold, precThreshold, precThreshold, precThreshold]),
    876                                    [0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff], // C++ version: UVec4(~0u) bitwise not, all bits in the integer will be flipped
    877                                    cmpMask);
    878 
    879                    isOk = tcuImageCompare.floatUlpThresholdCompare(name, desc, reference, rendered, finalThreshold /*, tcu::COMPARE_LOG_RESULT*/);
    880                    break;
    881                }
    882 
    883                case tcuTexture.TextureChannelClass.UNSIGNED_FIXED_POINT: {
    884                    // \note glReadPixels() allows only 8 bits to be read. This means that RGB10_A2 will loose some
    885                    // bits in the process and it must be taken into account when computing threshold.
    886                    /** @type {Array<number>} */ var bits = deMath.min([8, 8, 8, 8], tcuTextureUtil.getTextureFormatBitDepth(format)); // IVec4
    887 
    888                    /** @type {Array<number>} */ var baseThreshold = []; // Vec4
    889                    baseThreshold.length = 4;
    890                    for (var inc = 0; inc < baseThreshold.length; inc++) {
    891                        // TODO: check the operation below: baseThreshold = 1.0f / ((IVec4(1) << bits)-1).asFloat();
    892                        baseThreshold[inc] = 1.0 / ((1 << bits[inc]) - 1);
    893                    }
    894 
    895                    threshold = tcuTextureUtil.select(baseThreshold, [2.0, 2.0, 2.0, 2.0], cmpMask); // Vec4
    896 
    897                    isOk = tcuImageCompare.floatThresholdCompare(name, desc, reference, rendered, threshold/*, tcu::COMPARE_LOG_RESULT*/);
    898                    break;
    899                }
    900 
    901                case tcuTexture.TextureChannelClass.SIGNED_INTEGER:
    902                case tcuTexture.TextureChannelClass.UNSIGNED_INTEGER: {
    903                    // The C++ dEQP code uses ~0u but ~0 is -1 in Javascript
    904                    var UINT_MAX = Math.pow(2.0, 32.0) - 1;
    905                    threshold = tcuTextureUtil.select(
    906                                    [0, 0, 0, 0],
    907                                    [UINT_MAX, UINT_MAX, UINT_MAX, UINT_MAX],
    908                                    cmpMask
    909                                    ); // UVec4
    910                    isOk = tcuImageCompare.intThresholdCompare(name, desc, reference, rendered, threshold/*, tcu::COMPARE_LOG_RESULT*/);
    911                    break;
    912                }
    913 
    914                default:
    915                    testFailedOptions('Unsupported comparison', true);
    916                    break;
    917            }
    918 
    919            if (!isOk)
    920                allLevelsOk = false;
    921        }
    922 
    923        if (numAttachments > 1) {
    924            if (allLevelsOk)
    925                testPassed('Image comparison passed for ' +  numAttachments + ' attachments');
    926            else
    927                testFailed('Image comparison failed for some of ' +  numAttachments + ' attachments');
    928        } else {
    929            if (allLevelsOk)
    930                testPassed('Image comparison passed');
    931            else
    932                testFailed('Image comparison failed');
    933        }
    934 
    935        return tcuTestCase.IterateResult.STOP;
    936    };
    937 
    938    /**
    939     * es3fFragmentOutputTests.createRandomCase. Constructs the es3fFragmentOutputTests.createRandomCase, child class of es3fFragmentOutputTests.FragmentOutputCase
    940     * @constructor
    941     * @param {number} minRenderTargets
    942     * @param {number} maxRenderTargets
    943     * @param {number} seed
    944     * @return {es3fFragmentOutputTests.FragmentOutputCase} The currently modified object
    945     */
    946    es3fFragmentOutputTests.createRandomCase = function(minRenderTargets, maxRenderTargets, seed, colorBufferFloatSupported) {
    947 
    948        /** @type {Array<gluShaderUtil.DataType>} */
    949        var outputTypes = [
    950                           gluShaderUtil.DataType.FLOAT,
    951                           gluShaderUtil.DataType.FLOAT_VEC2,
    952                           gluShaderUtil.DataType.FLOAT_VEC3,
    953                           gluShaderUtil.DataType.FLOAT_VEC4,
    954                           gluShaderUtil.DataType.INT,
    955                           gluShaderUtil.DataType.INT_VEC2,
    956                           gluShaderUtil.DataType.INT_VEC3,
    957                           gluShaderUtil.DataType.INT_VEC4,
    958                           gluShaderUtil.DataType.UINT,
    959                           gluShaderUtil.DataType.UINT_VEC2,
    960                           gluShaderUtil.DataType.UINT_VEC3,
    961                           gluShaderUtil.DataType.UINT_VEC4
    962                           ];
    963 
    964        /** @type {Array<gluShaderUtil.precision>} */
    965        var precisions = [
    966                          gluShaderUtil.precision.PRECISION_LOWP,
    967                          gluShaderUtil.precision.PRECISION_MEDIUMP,
    968                          gluShaderUtil.precision.PRECISION_HIGHP
    969                          ];
    970 
    971        /** @type {Array<WebGLRenderingContextBase.GLenum>} */
    972        var floatFormats = [
    973                            gl.RGBA32F,
    974                            gl.RGBA16F,
    975                            gl.R11F_G11F_B10F,
    976                            gl.RG32F,
    977                            gl.RG16F,
    978                            gl.R32F,
    979                            gl.R16F,
    980                            gl.RGBA8,
    981                            gl.SRGB8_ALPHA8,
    982                            gl.RGB10_A2,
    983                            gl.RGBA4,
    984                            gl.RGB5_A1,
    985                            gl.RGB8,
    986                            gl.RGB565,
    987                            gl.RG8,
    988                            gl.R8
    989                            ];
    990 
    991        /** @type {Array<WebGLRenderingContextBase.GLenum>} */
    992        var colorBufferFloatFormats = [
    993                                       gl.RGBA32F,
    994                                       gl.RGBA16F,
    995                                       gl.R11F_G11F_B10F,
    996                                       gl.RG32F,
    997                                       gl.RG16F,
    998                                       gl.R32F,
    999                                       gl.R16F
   1000        ];
   1001 
   1002 
   1003        /** @type {Array<WebGLRenderingContextBase.GLenum>} */
   1004        var intFormats = [
   1005                            gl.RGBA32I,
   1006                            gl.RGBA16I,
   1007                            gl.RGBA8I,
   1008                            gl.RG32I,
   1009                            gl.RG16I,
   1010                            gl.RG8I,
   1011                            gl.R32I,
   1012                            gl.R16I,
   1013                            gl.R8I
   1014                            ];
   1015 
   1016        /** @type {Array<WebGLRenderingContextBase.GLenum>} */
   1017        var uintFormats = [
   1018                           gl.RGBA32UI,
   1019                           gl.RGBA16UI,
   1020                           gl.RGBA8UI,
   1021                           gl.RGB10_A2UI,
   1022                           gl.RG32UI,
   1023                           gl.RG16UI,
   1024                           gl.RG8UI,
   1025                           gl.R32UI,
   1026                           gl.R16UI,
   1027                           gl.R8UI
   1028                           ];
   1029 
   1030        /** @type {deRandom.Random} */ var rnd = new deRandom.Random(seed);
   1031        /** @type {Array<es3fFragmentOutputTests.FragmentOutput>} */ var outputs = [];
   1032        /** @type {Array<es3fFragmentOutputTests.BufferSpec>} */ var targets = [];
   1033        /** @type {Array<gluShaderUtil.DataType>} */ var outTypes = [];
   1034 
   1035        /** @type {number} */ var numTargets = rnd.getInt(minRenderTargets, maxRenderTargets);
   1036        /** @type {number} */ var width = 128; // \todo [2012-04-10 pyry] Separate randomized sizes per target?
   1037        /** @type {number} */ var height = 64;
   1038        /** @type {number} */ var samples = 0;
   1039 
   1040        // Compute outputs.
   1041        /** @type {number} */ var curLoc = 0;
   1042        while (curLoc < numTargets) {
   1043            /** @type {boolean} */ var useArray = rnd.getFloat() < 0.3;
   1044            /** @type {number} */ var maxArrayLen = numTargets - curLoc;
   1045            /** @type {number} */ var arrayLen = useArray ? rnd.getInt(1, maxArrayLen) : 0;
   1046            /** @type {Array<gluShaderUtil.DataType>} */ var basicTypeArray = rnd.choose(outputTypes, undefined, 1);
   1047            /** @type {gluShaderUtil.DataType} */ var basicType = basicTypeArray[0];
   1048            /** @type {Array<gluShaderUtil.precision>} */ var precisionArray = rnd.choose(precisions, undefined, 1);
   1049            /** @type {gluShaderUtil.precision} */ var precision = precisionArray[0];
   1050            /** @type {number} */ var numLocations = useArray ? arrayLen : 1;
   1051 
   1052            outputs.push(new es3fFragmentOutputTests.FragmentOutput(basicType, precision, curLoc, arrayLen));
   1053 
   1054            for (var ndx = 0; ndx < numLocations; ndx++)
   1055                outTypes.push(basicType);
   1056 
   1057            curLoc += numLocations;
   1058        }
   1059        DE_ASSERT(curLoc == numTargets);
   1060        DE_ASSERT(outTypes.length == numTargets);
   1061 
   1062        // Compute buffers.
   1063        while (targets.length < numTargets) {
   1064            /** @type {gluShaderUtil.DataType} */ var outType = outTypes[targets.length];
   1065            /** @type {boolean} */ var isFloat = gluShaderUtil.isDataTypeFloatOrVec(outType);
   1066            /** @type {boolean} */ var isInt = gluShaderUtil.isDataTypeIntOrIVec(outType);
   1067            /** @type {boolean} */ var isUint = gluShaderUtil.isDataTypeUintOrUVec(outType);
   1068            /** @type {Array} */ var formatArray = [];
   1069            /** @type {number} */ var format = 0;
   1070 
   1071            if (isFloat) {
   1072                formatArray = rnd.choose(floatFormats, undefined, 1);
   1073                format = formatArray[0];
   1074                if (colorBufferFloatFormats.indexOf(format) >= 0 && !colorBufferFloatSupported)
   1075                    return null;
   1076            } else if (isInt) {
   1077                formatArray = rnd.choose(intFormats, undefined, 1);
   1078                format = formatArray[0];
   1079            } else if (isUint) {
   1080                formatArray = rnd.choose(uintFormats, undefined, 1);
   1081                format = formatArray[0];
   1082            } else
   1083                DE_ASSERT(false);
   1084 
   1085            targets.push(new es3fFragmentOutputTests.BufferSpec(format, width, height, samples));
   1086        }
   1087 
   1088        return new es3fFragmentOutputTests.FragmentOutputCase(seed.toString(), '', targets, outputs);
   1089 
   1090    };
   1091 
   1092    es3fFragmentOutputTests.init = function(gl) {
   1093        var state = tcuTestCase.runner;
   1094        state.testCases = tcuTestCase.newTest('fragment_outputs', 'Top level');
   1095        /** @const @type {tcuTestCase.DeqpTest} */ var testGroup = state.testCases;
   1096 
   1097        /** @type {Array<WebGLRenderingContextBase.GLenum>} */
   1098        var requiredFloatFormats = [
   1099            gl.RGBA32F,
   1100            gl.RGBA16F,
   1101            gl.R11F_G11F_B10F,
   1102            gl.RG32F,
   1103            gl.RG16F,
   1104            gl.R32F,
   1105            gl.R16F
   1106        ];
   1107 
   1108        /** @type {Array<WebGLRenderingContextBase.GLenum>} */
   1109        var requiredFixedFormats = [
   1110            gl.RGBA8,
   1111            gl.SRGB8_ALPHA8,
   1112            gl.RGB10_A2,
   1113            gl.RGBA4,
   1114            gl.RGB5_A1,
   1115            gl.RGB8,
   1116            gl.RGB565,
   1117            gl.RG8,
   1118            gl.R8
   1119        ];
   1120 
   1121        /** @type {Array<WebGLRenderingContextBase.GLenum>} */
   1122        var requiredIntFormats = [
   1123            gl.RGBA32I,
   1124            gl.RGBA16I,
   1125            gl.RGBA8I,
   1126            gl.RG32I,
   1127            gl.RG16I,
   1128            gl.RG8I,
   1129            gl.R32I,
   1130            gl.R16I,
   1131            gl.R8I
   1132        ];
   1133 
   1134        /** @type {Array<WebGLRenderingContextBase.GLenum>} */
   1135        var requiredUintFormats = [
   1136            gl.RGBA32UI,
   1137            gl.RGBA16UI,
   1138            gl.RGBA8UI,
   1139            gl.RGB10_A2UI,
   1140            gl.RG32UI,
   1141            gl.RG16UI,
   1142            gl.RG8UI,
   1143            gl.R32UI,
   1144            gl.R16UI,
   1145            gl.R8UI
   1146        ];
   1147 
   1148        /** @type {Array<gluShaderUtil.precision>} */
   1149        var precisions = [
   1150 
   1151            gluShaderUtil.precision.PRECISION_LOWP,
   1152            gluShaderUtil.precision.PRECISION_MEDIUMP,
   1153            gluShaderUtil.precision.PRECISION_HIGHP
   1154 
   1155        ];
   1156 
   1157     // .basic.
   1158 
   1159        /** @const @type {number} */ var width = 64;
   1160        /** @const @type {number} */ var height = 64;
   1161        /** @const @type {number} */ var samples = 0;
   1162        /** @type {Array<es3fFragmentOutputTests.BufferSpec>} */ var fboSpec = null;
   1163        /** @type {gluShaderUtil.precision} */ var prec;
   1164        /** @type {string} */ var precName;
   1165 
   1166    // .float
   1167        if (gl.getExtension('EXT_color_buffer_float')) {
   1168            /** @type {tcuTestCase.DeqpTest} */ var floatGroup = tcuTestCase.newTest('basic.float', 'Floating-point output tests');
   1169            testGroup.addChild(floatGroup);
   1170 
   1171            for (var fmtNdx = 0; fmtNdx < requiredFloatFormats.length; fmtNdx++) {
   1172                var format = requiredFloatFormats[fmtNdx];
   1173                var fmtName = es3fFboTestUtil.getFormatName(format);
   1174                fboSpec = [];
   1175 
   1176                fboSpec.push(new es3fFragmentOutputTests.BufferSpec(format, width, height, samples));
   1177 
   1178                for (var precNdx = 0; precNdx < precisions.length; precNdx++) {
   1179                    prec = precisions[precNdx];
   1180                    precName = gluShaderUtil.getPrecisionName(prec);
   1181 
   1182                    // NOTE: Eliminated original OutputVec and toVec(), as it only returned an element of the outputs array in OutputVec
   1183                    floatGroup.addChild(new es3fFragmentOutputTests.FragmentOutputCase(fmtName + '_' + precName + '_float', '', fboSpec, [new es3fFragmentOutputTests.FragmentOutput(gluShaderUtil.DataType.FLOAT, prec, 0)]));
   1184                    floatGroup.addChild(new es3fFragmentOutputTests.FragmentOutputCase(fmtName + '_' + precName + '_vec2', '', fboSpec, [new es3fFragmentOutputTests.FragmentOutput(gluShaderUtil.DataType.FLOAT_VEC2, prec, 0)]));
   1185                    floatGroup.addChild(new es3fFragmentOutputTests.FragmentOutputCase(fmtName + '_' + precName + '_vec3', '', fboSpec, [new es3fFragmentOutputTests.FragmentOutput(gluShaderUtil.DataType.FLOAT_VEC3, prec, 0)]));
   1186                    floatGroup.addChild(new es3fFragmentOutputTests.FragmentOutputCase(fmtName + '_' + precName + '_vec4', '', fboSpec, [new es3fFragmentOutputTests.FragmentOutput(gluShaderUtil.DataType.FLOAT_VEC4, prec, 0)]));
   1187                }
   1188            }
   1189        }
   1190 
   1191        // .fixed
   1192        /** @type {tcuTestCase.DeqpTest} */ var fixedGroup = tcuTestCase.newTest('basic.fixed', 'Fixed-point output tests');
   1193        testGroup.addChild(fixedGroup);
   1194        for (var fmtNdx = 0; fmtNdx < requiredFixedFormats.length; fmtNdx++) {
   1195            var format = requiredFixedFormats[fmtNdx];
   1196            var fmtName = es3fFboTestUtil.getFormatName(format);
   1197            fboSpec = [];
   1198 
   1199            fboSpec.push(new es3fFragmentOutputTests.BufferSpec(format, width, height, samples));
   1200 
   1201            for (var precNdx = 0; precNdx < precisions.length; precNdx++) {
   1202                prec = precisions[precNdx];
   1203                precName = gluShaderUtil.getPrecisionName(prec);
   1204 
   1205                fixedGroup.addChild(new es3fFragmentOutputTests.FragmentOutputCase(fmtName + '_' + precName + '_float', '', fboSpec, [new es3fFragmentOutputTests.FragmentOutput(gluShaderUtil.DataType.FLOAT, prec, 0)]));
   1206                fixedGroup.addChild(new es3fFragmentOutputTests.FragmentOutputCase(fmtName + '_' + precName + '_vec2', '', fboSpec, [new es3fFragmentOutputTests.FragmentOutput(gluShaderUtil.DataType.FLOAT_VEC2, prec, 0)]));
   1207                fixedGroup.addChild(new es3fFragmentOutputTests.FragmentOutputCase(fmtName + '_' + precName + '_vec3', '', fboSpec, [new es3fFragmentOutputTests.FragmentOutput(gluShaderUtil.DataType.FLOAT_VEC3, prec, 0)]));
   1208                fixedGroup.addChild(new es3fFragmentOutputTests.FragmentOutputCase(fmtName + '_' + precName + '_vec4', '', fboSpec, [new es3fFragmentOutputTests.FragmentOutput(gluShaderUtil.DataType.FLOAT_VEC4, prec, 0)]));
   1209            }
   1210        }
   1211 
   1212        // .int
   1213        /** @type {tcuTestCase.DeqpTest} */ var intGroup = tcuTestCase.newTest('basic.int', 'Integer output tests');
   1214        testGroup.addChild(intGroup);
   1215        for (var fmtNdx = 0; fmtNdx < requiredIntFormats.length; fmtNdx++) {
   1216            var format = requiredIntFormats[fmtNdx];
   1217            var fmtName = es3fFboTestUtil.getFormatName(format);
   1218            fboSpec = [];
   1219 
   1220            fboSpec.push(new es3fFragmentOutputTests.BufferSpec(format, width, height, samples));
   1221 
   1222            for (var precNdx = 0; precNdx < precisions.length; precNdx++) {
   1223                prec = precisions[precNdx];
   1224                precName = gluShaderUtil.getPrecisionName(prec);
   1225 
   1226                intGroup.addChild(new es3fFragmentOutputTests.FragmentOutputCase(fmtName + '_' + precName + '_int', '', fboSpec, [new es3fFragmentOutputTests.FragmentOutput(gluShaderUtil.DataType.INT, prec, 0)]));
   1227                intGroup.addChild(new es3fFragmentOutputTests.FragmentOutputCase(fmtName + '_' + precName + '_ivec2', '', fboSpec, [new es3fFragmentOutputTests.FragmentOutput(gluShaderUtil.DataType.INT_VEC2, prec, 0)]));
   1228                intGroup.addChild(new es3fFragmentOutputTests.FragmentOutputCase(fmtName + '_' + precName + '_ivec3', '', fboSpec, [new es3fFragmentOutputTests.FragmentOutput(gluShaderUtil.DataType.INT_VEC3, prec, 0)]));
   1229                intGroup.addChild(new es3fFragmentOutputTests.FragmentOutputCase(fmtName + '_' + precName + '_ivec4', '', fboSpec, [new es3fFragmentOutputTests.FragmentOutput(gluShaderUtil.DataType.INT_VEC4, prec, 0)]));
   1230            }
   1231        }
   1232 
   1233        // .uint
   1234        /** @type {tcuTestCase.DeqpTest} */ var uintGroup = tcuTestCase.newTest('basic.uint', 'Usigned integer output tests');
   1235        testGroup.addChild(uintGroup);
   1236        for (var fmtNdx = 0; fmtNdx < requiredUintFormats.length; fmtNdx++) {
   1237            var format = requiredUintFormats[fmtNdx];
   1238            var fmtName = es3fFboTestUtil.getFormatName(format);
   1239            fboSpec = [];
   1240 
   1241            fboSpec.push(new es3fFragmentOutputTests.BufferSpec(format, width, height, samples));
   1242 
   1243            for (var precNdx = 0; precNdx < precisions.length; precNdx++) {
   1244                prec = precisions[precNdx];
   1245                precName = gluShaderUtil.getPrecisionName(prec);
   1246 
   1247                uintGroup.addChild(new es3fFragmentOutputTests.FragmentOutputCase(fmtName + '_' + precName + '_uint', '', fboSpec, [new es3fFragmentOutputTests.FragmentOutput(gluShaderUtil.DataType.UINT, prec, 0)]));
   1248                uintGroup.addChild(new es3fFragmentOutputTests.FragmentOutputCase(fmtName + '_' + precName + '_uvec2', '', fboSpec, [new es3fFragmentOutputTests.FragmentOutput(gluShaderUtil.DataType.UINT_VEC2, prec, 0)]));
   1249                uintGroup.addChild(new es3fFragmentOutputTests.FragmentOutputCase(fmtName + '_' + precName + '_uvec3', '', fboSpec, [new es3fFragmentOutputTests.FragmentOutput(gluShaderUtil.DataType.UINT_VEC3, prec, 0)]));
   1250                uintGroup.addChild(new es3fFragmentOutputTests.FragmentOutputCase(fmtName + '_' + precName + '_uvec4', '', fboSpec, [new es3fFragmentOutputTests.FragmentOutput(gluShaderUtil.DataType.UINT_VEC4, prec, 0)]));
   1251 
   1252            }
   1253        }
   1254 
   1255     // .array
   1256 
   1257        /** @type {number} */ var numTargets = 3;
   1258 
   1259        // .float
   1260        if (gl.getExtension('EXT_color_buffer_float')) {
   1261            /** @type {tcuTestCase.DeqpTest} */ var arrayFloatGroup = tcuTestCase.newTest('array.float', 'Floating-point output tests');
   1262            testGroup.addChild(arrayFloatGroup);
   1263            for (var fmtNdx = 0; fmtNdx < requiredFloatFormats.length; fmtNdx++) {
   1264                var format = requiredFloatFormats[fmtNdx];
   1265                var fmtName = es3fFboTestUtil.getFormatName(format);
   1266                fboSpec = [];
   1267 
   1268                for (var ndx = 0; ndx < numTargets; ndx++)
   1269                    fboSpec.push(new es3fFragmentOutputTests.BufferSpec(format, width, height, samples));
   1270 
   1271                for (var precNdx = 0; precNdx < precisions.length; precNdx++) {
   1272                    prec = precisions[precNdx];
   1273                    precName = gluShaderUtil.getPrecisionName(prec);
   1274 
   1275                    arrayFloatGroup.addChild(new es3fFragmentOutputTests.FragmentOutputCase(fmtName + '_' + precName + '_float', '', fboSpec, [new es3fFragmentOutputTests.FragmentOutput(gluShaderUtil.DataType.FLOAT, prec, 0, numTargets)]));
   1276                    arrayFloatGroup.addChild(new es3fFragmentOutputTests.FragmentOutputCase(fmtName + '_' + precName + '_vec2', '', fboSpec, [new es3fFragmentOutputTests.FragmentOutput(gluShaderUtil.DataType.FLOAT_VEC2, prec, 0, numTargets)]));
   1277                    arrayFloatGroup.addChild(new es3fFragmentOutputTests.FragmentOutputCase(fmtName + '_' + precName + '_vec3', '', fboSpec, [new es3fFragmentOutputTests.FragmentOutput(gluShaderUtil.DataType.FLOAT_VEC3, prec, 0, numTargets)]));
   1278                    arrayFloatGroup.addChild(new es3fFragmentOutputTests.FragmentOutputCase(fmtName + '_' + precName + '_vec4', '', fboSpec, [new es3fFragmentOutputTests.FragmentOutput(gluShaderUtil.DataType.FLOAT_VEC4, prec, 0, numTargets)]));
   1279                }
   1280            }
   1281        }
   1282 
   1283        // .fixed
   1284        /** @type {tcuTestCase.DeqpTest} */ var arrayFixedGroup = tcuTestCase.newTest('array.fixed', 'Fixed-point output tests');
   1285        testGroup.addChild(arrayFixedGroup);
   1286        for (var fmtNdx = 0; fmtNdx < requiredFixedFormats.length; fmtNdx++) {
   1287            var format = requiredFixedFormats[fmtNdx];
   1288            var fmtName = es3fFboTestUtil.getFormatName(format);
   1289            fboSpec = [];
   1290 
   1291            for (var ndx = 0; ndx < numTargets; ndx++)
   1292                fboSpec.push(new es3fFragmentOutputTests.BufferSpec(format, width, height, samples));
   1293 
   1294            for (var precNdx = 0; precNdx < precisions.length; precNdx++) {
   1295                prec = precisions[precNdx];
   1296                precName = gluShaderUtil.getPrecisionName(prec);
   1297 
   1298                arrayFixedGroup.addChild(new es3fFragmentOutputTests.FragmentOutputCase(fmtName + '_' + precName + '_float', '', fboSpec, [new es3fFragmentOutputTests.FragmentOutput(gluShaderUtil.DataType.FLOAT, prec, 0, numTargets)]));
   1299                arrayFixedGroup.addChild(new es3fFragmentOutputTests.FragmentOutputCase(fmtName + '_' + precName + '_vec2', '', fboSpec, [new es3fFragmentOutputTests.FragmentOutput(gluShaderUtil.DataType.FLOAT_VEC2, prec, 0, numTargets)]));
   1300                arrayFixedGroup.addChild(new es3fFragmentOutputTests.FragmentOutputCase(fmtName + '_' + precName + '_vec3', '', fboSpec, [new es3fFragmentOutputTests.FragmentOutput(gluShaderUtil.DataType.FLOAT_VEC3, prec, 0, numTargets)]));
   1301                arrayFixedGroup.addChild(new es3fFragmentOutputTests.FragmentOutputCase(fmtName + '_' + precName + '_vec4', '', fboSpec, [new es3fFragmentOutputTests.FragmentOutput(gluShaderUtil.DataType.FLOAT_VEC4, prec, 0, numTargets)]));
   1302            }
   1303        }
   1304 
   1305        // .int
   1306        /** @type {tcuTestCase.DeqpTest} */ var arrayIntGroup = tcuTestCase.newTest('array.int', 'Integer output tests');
   1307        testGroup.addChild(arrayIntGroup);
   1308        for (var fmtNdx = 0; fmtNdx < requiredIntFormats.length; fmtNdx++) {
   1309            var format = requiredIntFormats[fmtNdx];
   1310            var fmtName = es3fFboTestUtil.getFormatName(format);
   1311            fboSpec = [];
   1312 
   1313            for (var ndx = 0; ndx < numTargets; ndx++)
   1314                fboSpec.push(new es3fFragmentOutputTests.BufferSpec(format, width, height, samples));
   1315 
   1316            for (var precNdx = 0; precNdx < precisions.length; precNdx++) {
   1317                prec = precisions[precNdx];
   1318                precName = gluShaderUtil.getPrecisionName(prec);
   1319 
   1320                arrayIntGroup.addChild(new es3fFragmentOutputTests.FragmentOutputCase(fmtName + '_' + precName + '_int', '', fboSpec, [new es3fFragmentOutputTests.FragmentOutput(gluShaderUtil.DataType.INT, prec, 0, numTargets)]));
   1321                arrayIntGroup.addChild(new es3fFragmentOutputTests.FragmentOutputCase(fmtName + '_' + precName + '_ivec2', '', fboSpec, [new es3fFragmentOutputTests.FragmentOutput(gluShaderUtil.DataType.INT_VEC2, prec, 0, numTargets)]));
   1322                arrayIntGroup.addChild(new es3fFragmentOutputTests.FragmentOutputCase(fmtName + '_' + precName + '_ivec3', '', fboSpec, [new es3fFragmentOutputTests.FragmentOutput(gluShaderUtil.DataType.INT_VEC3, prec, 0, numTargets)]));
   1323                arrayIntGroup.addChild(new es3fFragmentOutputTests.FragmentOutputCase(fmtName + '_' + precName + '_ivec4', '', fboSpec, [new es3fFragmentOutputTests.FragmentOutput(gluShaderUtil.DataType.INT_VEC4, prec, 0, numTargets)]));
   1324            }
   1325        }
   1326 
   1327        // .uint
   1328        /** @type {tcuTestCase.DeqpTest} */ var arrayUintGroup = tcuTestCase.newTest('array.uint', 'Usigned integer output tests');
   1329        testGroup.addChild(arrayUintGroup);
   1330        for (var fmtNdx = 0; fmtNdx < requiredUintFormats.length; fmtNdx++) {
   1331            var format = requiredUintFormats[fmtNdx];
   1332            var fmtName = es3fFboTestUtil.getFormatName(format);
   1333            fboSpec = [];
   1334 
   1335            for (var ndx = 0; ndx < numTargets; ndx++)
   1336                fboSpec.push(new es3fFragmentOutputTests.BufferSpec(format, width, height, samples));
   1337 
   1338            for (var precNdx = 0; precNdx < precisions.length; precNdx++) {
   1339                prec = precisions[precNdx];
   1340                precName = gluShaderUtil.getPrecisionName(prec);
   1341 
   1342                arrayUintGroup.addChild(new es3fFragmentOutputTests.FragmentOutputCase(fmtName + '_' + precName + '_uint', '', fboSpec, [new es3fFragmentOutputTests.FragmentOutput(gluShaderUtil.DataType.UINT, prec, 0, numTargets)]));
   1343                arrayUintGroup.addChild(new es3fFragmentOutputTests.FragmentOutputCase(fmtName + '_' + precName + '_uvec2', '', fboSpec, [new es3fFragmentOutputTests.FragmentOutput(gluShaderUtil.DataType.UINT_VEC2, prec, 0, numTargets)]));
   1344                arrayUintGroup.addChild(new es3fFragmentOutputTests.FragmentOutputCase(fmtName + '_' + precName + '_uvec3', '', fboSpec, [new es3fFragmentOutputTests.FragmentOutput(gluShaderUtil.DataType.UINT_VEC3, prec, 0, numTargets)]));
   1345                arrayUintGroup.addChild(new es3fFragmentOutputTests.FragmentOutputCase(fmtName + '_' + precName + '_uvec4', '', fboSpec, [new es3fFragmentOutputTests.FragmentOutput(gluShaderUtil.DataType.UINT_VEC4, prec, 0, numTargets)]));
   1346            }
   1347        }
   1348 
   1349    // .random
   1350 
   1351        /** @type {Array<tcuTestCase.DeqpTest>} */ var randomGroup = [];
   1352        var numRandomGroups = 3;
   1353        for (var ii = 0; ii < numRandomGroups; ++ii) {
   1354            randomGroup[ii] = tcuTestCase.newTest('random', 'Random fragment output cases');
   1355            testGroup.addChild(randomGroup[ii]);
   1356        }
   1357 
   1358        /** @type {boolean} */ var colorBufferFloatSupported = (gl.getExtension('EXT_color_buffer_float') != null);
   1359        for (var seed = 0; seed < 100; seed++) {
   1360            var test = es3fFragmentOutputTests.createRandomCase(2, 4, seed, colorBufferFloatSupported);
   1361            if (test !== null) {
   1362                randomGroup[seed % numRandomGroups].addChild(test);
   1363            }
   1364        }
   1365 
   1366    };
   1367 
   1368    /**
   1369     * Create and execute the test cases
   1370     */
   1371    es3fFragmentOutputTests.run = function(context, range) {
   1372        gl = context;
   1373      //Set up Test Root parameters
   1374        var testName = 'fragment_output';
   1375        var testDescription = 'Fragment Output Tests';
   1376        var state = tcuTestCase.runner;
   1377 
   1378        state.testName = testName;
   1379        state.testCases = tcuTestCase.newTest(testName, testDescription, null);
   1380 
   1381      //Set up name and description of this test series.
   1382        setCurrentTestName(testName);
   1383        description(testDescription);
   1384 
   1385        try {
   1386            es3fFragmentOutputTests.init(gl);
   1387            if (range)
   1388                state.setRange(range);
   1389            tcuTestCase.runTestCases();
   1390        } catch (err) {
   1391            testFailedOptions('Failed to es3fFragmentOutputTests.run tests', false);
   1392            bufferedLogToConsole(err);
   1393            tcuTestCase.runner.terminate();
   1394        }
   1395 
   1396    };
   1397 
   1398 });