tor-browser

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

es3fShaderDerivateTests.js (89234B)


      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.es3fShaderDerivateTests');
     23 goog.require('framework.delibs.debase.deMath');
     24 goog.require('framework.delibs.debase.deRandom');
     25 goog.require('framework.delibs.debase.deString');
     26 goog.require('framework.opengl.gluDrawUtil');
     27 goog.require('framework.opengl.gluPixelTransfer');
     28 goog.require('framework.opengl.gluShaderProgram');
     29 goog.require('framework.opengl.gluShaderUtil');
     30 goog.require('framework.opengl.gluTexture');
     31 goog.require('framework.opengl.gluTextureUtil');
     32 goog.require('framework.common.tcuInterval');
     33 goog.require('framework.common.tcuFloat');
     34 goog.require('framework.common.tcuLogImage');
     35 goog.require('framework.common.tcuMatrix');
     36 goog.require('framework.common.tcuPixelFormat');
     37 goog.require('framework.common.tcuRGBA');
     38 goog.require('framework.common.tcuStringTemplate');
     39 goog.require('framework.common.tcuSurface');
     40 goog.require('framework.common.tcuTexture');
     41 goog.require('framework.common.tcuTextureUtil');
     42 goog.require('framework.common.tcuTestCase');
     43 goog.require('modules.shared.glsShaderRenderCase');
     44 
     45 goog.scope(function() {
     46    var es3fShaderDerivateTests = functional.gles3.es3fShaderDerivateTests;
     47    var deMath = framework.delibs.debase.deMath;
     48    var deRandom = framework.delibs.debase.deRandom;
     49    var deString = framework.delibs.debase.deString;
     50    var gluDrawUtil = framework.opengl.gluDrawUtil;
     51    var gluPixelTransfer = framework.opengl.gluPixelTransfer;
     52    var gluShaderProgram = framework.opengl.gluShaderProgram;
     53    var gluShaderUtil = framework.opengl.gluShaderUtil;
     54    var gluTexture = framework.opengl.gluTexture;
     55    var gluTextureUtil = framework.opengl.gluTextureUtil;
     56    var tcuInterval = framework.common.tcuInterval;
     57    var tcuFloat = framework.common.tcuFloat;
     58    var tcuLogImage = framework.common.tcuLogImage;
     59    var tcuMatrix = framework.common.tcuMatrix;
     60    var tcuPixelFormat = framework.common.tcuPixelFormat;
     61    var tcuRGBA = framework.common.tcuRGBA;
     62    var tcuStringTemplate = framework.common.tcuStringTemplate;
     63    var tcuSurface = framework.common.tcuSurface;
     64    var tcuTexture = framework.common.tcuTexture;
     65    var tcuTextureUtil = framework.common.tcuTextureUtil;
     66    var tcuTestCase = framework.common.tcuTestCase;
     67    var glsShaderRenderCase = modules.shared.glsShaderRenderCase;
     68 
     69    /** @const {number} */ es3fShaderDerivateTests.VIEWPORT_WIDTH = 167;
     70    /** @const {number} */ es3fShaderDerivateTests.VIEWPORT_HEIGHT = 103;
     71    /** @const {number} */ es3fShaderDerivateTests.FBO_WIDTH = 99;
     72    /** @const {number} */ es3fShaderDerivateTests.FBO_HEIGHT = 133;
     73    /** @const {number} */ es3fShaderDerivateTests.MAX_FAILED_MESSAGES = 10;
     74    /** @const {number} */ es3fShaderDerivateTests.INTERPOLATION_LOST_BITS = 3; // number mantissa of bits allowed to be lost in varying interpolation
     75    /**
     76     * @enum {number}
     77     */
     78    es3fShaderDerivateTests.DerivateFunc = {
     79        DFDX: 0,
     80        DFDY: 1,
     81        FWIDTH: 2
     82    };
     83 
     84    /**
     85     * @enum {number}
     86     */
     87    es3fShaderDerivateTests.SurfaceType = {
     88        DEFAULT_FRAMEBUFFER: 0,
     89        UNORM_FBO: 1,
     90        FLOAT_FBO: 2 // \note Uses RGBA32UI fbo actually, since FP rendertargets are not in core spec.
     91    };
     92 
     93    /**
     94     * @enum {number}
     95     */
     96    es3fShaderDerivateTests.VerificationLogging = {
     97        LOG_ALL: 0,
     98        LOG_NOTHING: 1
     99    };
    100 
    101    /**
    102     * @param {es3fShaderDerivateTests.DerivateFunc} func
    103     * @return {string}
    104     */
    105    es3fShaderDerivateTests.getDerivateFuncName = function(func) {
    106        switch (func) {
    107            case es3fShaderDerivateTests.DerivateFunc.DFDX: return 'dFdx';
    108            case es3fShaderDerivateTests.DerivateFunc.DFDY: return 'dFdy';
    109            case es3fShaderDerivateTests.DerivateFunc.FWIDTH: return 'fwidth';
    110            default: throw new Error('Derivate Func not supported.');
    111        }
    112    };
    113 
    114    /**
    115     * @param {es3fShaderDerivateTests.DerivateFunc} func
    116     * @return {string}
    117     */
    118    es3fShaderDerivateTests.getDerivateFuncCaseName = function(func) {
    119        switch (func) {
    120            case es3fShaderDerivateTests.DerivateFunc.DFDX: return 'dfdx';
    121            case es3fShaderDerivateTests.DerivateFunc.DFDY: return 'dfdy';
    122            case es3fShaderDerivateTests.DerivateFunc.FWIDTH: return 'fwidth';
    123            default: throw new Error('Derivate Func not supported.');
    124        }
    125    };
    126 
    127    /**
    128     * @param {?gluShaderUtil.DataType} type
    129     * @return {Array<boolean>}
    130     */
    131    es3fShaderDerivateTests.getDerivateMask = function(type) {
    132        switch (type) {
    133            case gluShaderUtil.DataType.FLOAT: return [true, false, false, false];
    134            case gluShaderUtil.DataType.FLOAT_VEC2: return [true, true, false, false];
    135            case gluShaderUtil.DataType.FLOAT_VEC3: return [true, true, true, false];
    136            case gluShaderUtil.DataType.FLOAT_VEC4: return [true, true, true, true];
    137            default: throw new Error('Data Type not supported.');
    138        }
    139    };
    140 
    141    /**
    142     * @param {tcuTexture.ConstPixelBufferAccess} surface
    143     * @param {Array<number>} derivScale
    144     * @param {Array<number>} derivBias
    145     * @param {number} x
    146     * @param {number} y
    147     * @return {Array<number>}
    148     */
    149    es3fShaderDerivateTests.readDerivate = function(surface, derivScale, derivBias, x, y) {
    150        return deMath.divide(deMath.subtract(surface.getPixel(x, y), derivBias), derivScale);
    151    };
    152 
    153    /**
    154     * @param {Array<number>} v
    155     * @return {Array<number>}
    156     */
    157    es3fShaderDerivateTests.getCompExpBits = function(v) {
    158        return [tcuFloat.newFloat32(v[0]).exponentBits(),
    159            tcuFloat.newFloat32(v[1]).exponentBits(),
    160            tcuFloat.newFloat32(v[2]).exponentBits(),
    161            tcuFloat.newFloat32(v[3]).exponentBits()];
    162    };
    163 
    164    /**
    165     * @param {number} value
    166     * @param {number} numAccurateBits
    167     * @return {number}
    168     */
    169    es3fShaderDerivateTests.computeFloatingPointError = function(value, numAccurateBits) {
    170        /** @type {number} */ var numGarbageBits = 23 - numAccurateBits;
    171        /** @type {number} */ var mask = (1 << numGarbageBits) - 1 ;
    172        /** @type {number} */ var exp = tcuFloat.newFloat32(value).exponent();
    173 
    174        return (new tcuFloat.deFloat()).construct(1, exp, (1 << 23) | mask).getValue() - (new tcuFloat.deFloat()).construct(1, exp, 1 << 23).getValue();
    175    };
    176 
    177    /**
    178       * @param {?gluShaderUtil.precision} precision
    179     * @return {number}
    180     */
    181    es3fShaderDerivateTests.getNumMantissaBits = function(precision) {
    182        switch (precision) {
    183            case gluShaderUtil.precision.PRECISION_HIGHP: return 23;
    184            case gluShaderUtil.precision.PRECISION_MEDIUMP: return 10;
    185            case gluShaderUtil.precision.PRECISION_LOWP: return 6;
    186            default:
    187                throw new Error('Precision not supported: ' + precision);
    188        }
    189    };
    190 
    191    /**
    192     * @param {?gluShaderUtil.precision} precision
    193     * @return {number}
    194     */
    195    es3fShaderDerivateTests.getMinExponent = function(precision) {
    196        switch (precision) {
    197            case gluShaderUtil.precision.PRECISION_HIGHP: return -126;
    198            case gluShaderUtil.precision.PRECISION_MEDIUMP: return -14;
    199            case gluShaderUtil.precision.PRECISION_LOWP: return -8;
    200            default:
    201                throw new Error('Precision not supported: ' + precision);
    202        }
    203    };
    204 
    205    /**
    206     * @param {number} exp
    207     * @param {number} numMantissaBits
    208     * @return {number}
    209     */
    210    es3fShaderDerivateTests.getSingleULPForExponent = function(exp, numMantissaBits) {
    211        if (numMantissaBits > 0) {
    212            assertMsgOptions(numMantissaBits <= 23, 'numMantissaBits must be less or equal than 23.', false, true);
    213 
    214            /** @type {number} */ var ulpBitNdx = 23 - numMantissaBits;
    215 
    216            return (new tcuFloat.deFloat()).construct(1, exp, (1 << 23) | (1 << ulpBitNdx)).getValue() - (new tcuFloat.deFloat()).construct(1, exp, 1 << 23).getValue();
    217        } else {
    218            assertMsgOptions(numMantissaBits === 0, 'numMantissaBits must equal to 0.', false, true);
    219            return (new tcuFloat.deFloat()).construct(1, exp, (1 << 23)).getValue()
    220        }
    221    };
    222 
    223    /**
    224     * @param {number} value
    225     * @param {number} numMantissaBits
    226     * @return {number}
    227     */
    228    es3fShaderDerivateTests.getSingleULPForValue = function(value, numMantissaBits) {
    229        /** @type {number} */ var exp = (new tcuFloat.deFloat().deFloatNumber(value)).exponent();
    230        return es3fShaderDerivateTests.getSingleULPForExponent(exp, numMantissaBits);
    231    };
    232 
    233    /**
    234     * @param {number} value
    235     * @param {number} minExponent
    236     * @param {number} numAccurateBits
    237     * @return {number}
    238     */
    239    es3fShaderDerivateTests.convertFloorFlushToZero = function(value, minExponent, numAccurateBits) {
    240        if (value === 0.0) {
    241            return 0.0;
    242        } else {
    243            /** @type {tcuFloat.deFloat} */ var inputFloat = new tcuFloat.deFloat().deFloatNumber(value);
    244            /** @type {number} */ var numTruncatedBits = 23 - numAccurateBits;
    245            /** @type {number} */ var truncMask = (1 << numTruncatedBits) - 1;
    246 
    247            if (value > 0.0) {
    248                if (value > 0.0 && (new tcuFloat.deFloat().deFloatNumber(value)).exponent() < minExponent) {
    249                    // flush to zero if possible
    250                    return 0.0;
    251                } else {
    252                    // just mask away non-representable bits
    253                    return (new tcuFloat.deFloat()).construct(1, inputFloat.exponent(), inputFloat.mantissa() & ~truncMask).getValue();
    254                }
    255            } else {
    256                if (inputFloat.mantissa() & truncMask) {
    257                    // decrement one ulp if truncated bits are non-zero (i.e. if value is not representable)
    258                    return (new tcuFloat.deFloat()).construct(-1, inputFloat.exponent(), inputFloat.mantissa() & ~truncMask).getValue() - es3fShaderDerivateTests.getSingleULPForExponent(inputFloat.exponent(), numAccurateBits);
    259                } else {
    260                    // value is representable, no need to do anything
    261                    return value;
    262                }
    263            }
    264        }
    265    };
    266 
    267    /**
    268     * @param {number} value
    269     * @param {number} minExponent
    270     * @param {number} numAccurateBits
    271     * @return {number}
    272     */
    273    es3fShaderDerivateTests.convertCeilFlushToZero = function(value, minExponent, numAccurateBits) {
    274        return -es3fShaderDerivateTests.convertFloorFlushToZero(-value, minExponent, numAccurateBits);
    275    };
    276 
    277    /**
    278     * @param {number} value
    279     * @param {number} numUlps
    280     * @param {number} numMantissaBits
    281     * @return {number}
    282     */
    283    es3fShaderDerivateTests.addErrorUlp = function(value, numUlps, numMantissaBits) {
    284        return value + numUlps * es3fShaderDerivateTests.getSingleULPForValue(value, numMantissaBits);
    285    };
    286 
    287    /**
    288     * @param {?gluShaderUtil.precision} precision
    289     * @param {Array<number>} valueMin
    290     * @param {Array<number>} valueMax
    291     * @param {Array<number>} expectedDerivate
    292     * @return {Array<number>}
    293     */
    294    es3fShaderDerivateTests.getDerivateThreshold = function(precision, valueMin, valueMax, expectedDerivate) {
    295        /** @type {number} */ var baseBits = es3fShaderDerivateTests.getNumMantissaBits(precision);
    296        /** @type {Array<number>} */ var derivExp = es3fShaderDerivateTests.getCompExpBits(expectedDerivate);
    297        /** @type {Array<number>} */ var maxValueExp = deMath.max(es3fShaderDerivateTests.getCompExpBits(valueMin), es3fShaderDerivateTests.getCompExpBits(valueMax));
    298        /** @type {Array<number>} */ var numBitsLost = deMath.subtract(maxValueExp, deMath.min(maxValueExp, derivExp));
    299        /** @type {Array<number>} */
    300        var numAccurateBits = deMath.max(
    301            deMath.addScalar(
    302                deMath.subtract(
    303                    [baseBits, baseBits, baseBits, baseBits],
    304                    numBitsLost),
    305                -es3fShaderDerivateTests.INTERPOLATION_LOST_BITS),
    306            [0, 0, 0, 0]);
    307 
    308        return [es3fShaderDerivateTests.computeFloatingPointError(expectedDerivate[0], numAccurateBits[0]),
    309                es3fShaderDerivateTests.computeFloatingPointError(expectedDerivate[1], numAccurateBits[1]),
    310                es3fShaderDerivateTests.computeFloatingPointError(expectedDerivate[2], numAccurateBits[2]),
    311                es3fShaderDerivateTests.computeFloatingPointError(expectedDerivate[3], numAccurateBits[3])];
    312    };
    313 
    314    /**
    315     * @param {tcuTexture.ConstPixelBufferAccess} result
    316     * @param {tcuTexture.PixelBufferAccess} errorMask
    317     * @param {?gluShaderUtil.DataType} dataType
    318     * @param {Array<number>} reference
    319     * @param {Array<number>} threshold
    320     * @param {Array<number>} scale
    321     * @param {Array<number>} bias
    322     * @param {es3fShaderDerivateTests.VerificationLogging=} logPolicy
    323     * @return {boolean}
    324     */
    325    es3fShaderDerivateTests.verifyConstantDerivate = function(result, errorMask, dataType, reference, threshold, scale, bias, logPolicy) {
    326        logPolicy = logPolicy === undefined ? es3fShaderDerivateTests.VerificationLogging.LOG_ALL : logPolicy;
    327        /** @type {Array<boolean>} */ var mask = deMath.logicalNotBool(es3fShaderDerivateTests.getDerivateMask(dataType));
    328        /** @type {number} */ var numFailedPixels = 0;
    329 
    330        if (logPolicy === es3fShaderDerivateTests.VerificationLogging.LOG_ALL)
    331            bufferedLogToConsole('Expecting ' + reference + ' with threshold ' + threshold);
    332 
    333        for (var y = 0; y < result.getHeight(); y++) {
    334            for (var x = 0; x < result.getWidth(); x++) {
    335                /** @type {Array<number>} */ var resDerivate = es3fShaderDerivateTests.readDerivate(result, scale, bias, x, y);
    336                /** @type {boolean} */
    337                var isOk = deMath.boolAll(
    338                    deMath.logicalOrBool(
    339                        deMath.lessThanEqual(
    340                            deMath.abs(deMath.subtract(reference, resDerivate)),
    341                            threshold),
    342                        mask));
    343 
    344                if (!isOk) {
    345                    if (numFailedPixels < es3fShaderDerivateTests.MAX_FAILED_MESSAGES && logPolicy === es3fShaderDerivateTests.VerificationLogging.LOG_ALL)
    346                        bufferedLogToConsole('FAIL: got ' + resDerivate + ', diff = ' + deMath.abs(deMath.subtract(reference, resDerivate)) + ', at x = ' + x + ', y = ' + y);
    347                    numFailedPixels += 1;
    348                    errorMask.setPixel(tcuRGBA.RGBA.red.toVec(), x, y);
    349                }
    350            }
    351        }
    352 
    353        if (numFailedPixels >= es3fShaderDerivateTests.MAX_FAILED_MESSAGES && logPolicy === es3fShaderDerivateTests.VerificationLogging.LOG_ALL)
    354            bufferedLogToConsole('...');
    355 
    356        if (numFailedPixels > 0 && logPolicy === es3fShaderDerivateTests.VerificationLogging.LOG_ALL)
    357            bufferedLogToConsole('FAIL: found ' + numFailedPixels + ' failed pixels');
    358 
    359        return numFailedPixels === 0;
    360    };
    361 
    362    /**
    363     *      .-----.
    364     *      | s_x |
    365     *  M x | s_y |
    366     *      | 1.0 |
    367     *      '-----'
    368     * @struct
    369     * @constructor
    370     */
    371    es3fShaderDerivateTests.Linear2DFunctionEvaluator = function() {
    372        /** @type {tcuMatrix.Matrix} */ this.matrix = new tcuMatrix.Matrix(4, 3);
    373    };
    374 
    375    es3fShaderDerivateTests.Linear2DFunctionEvaluator.prototype.evaluateAt = function(screenX, screenY) {
    376        /** @type {Array<number>} */ var position = [screenX, screenY, 1.0];
    377        return tcuMatrix.multiplyMatVec(this.matrix, position);
    378    };
    379 
    380    /**
    381     * @param {tcuTexture.ConstPixelBufferAccess} result
    382     * @param {tcuTexture.PixelBufferAccess} errorMask
    383     * @param {?gluShaderUtil.DataType} dataType
    384     * @param {?gluShaderUtil.precision} precision
    385     * @param {Array<number>} derivScale
    386     * @param {Array<number>} derivBias
    387     * @param {Array<number>} surfaceThreshold
    388     * @param {es3fShaderDerivateTests.DerivateFunc} derivateFunc
    389     * @param {es3fShaderDerivateTests.Linear2DFunctionEvaluator} func
    390     * @return {boolean}
    391     */
    392    es3fShaderDerivateTests.reverifyConstantDerivateWithFlushRelaxations = function(result, errorMask, dataType, precision, derivScale, derivBias, surfaceThreshold, derivateFunc, func) {
    393        assertMsgOptions(result.getWidth() === errorMask.getWidth(), 'Dimensions of result and errorMask inconsistent.', false, true);
    394        assertMsgOptions(result.getHeight() === errorMask.getHeight(), 'Dimensions of result and errorMask inconsistent.', false, true);
    395        assertMsgOptions(derivateFunc === es3fShaderDerivateTests.DerivateFunc.DFDX || derivateFunc === es3fShaderDerivateTests.DerivateFunc.DFDY, 'Derivate Function should be DFDX or DFDY.', false, true);
    396 
    397        /** @type {Array<number>} */ var red = [255, 0, 0, 255];
    398        /** @type {Array<number>} */ var green = [0, 255, 0, 255];
    399        /** @type {number} */ var divisionErrorUlps = 2.5;
    400 
    401        /** @type {number} */ var numComponents = gluShaderUtil.getDataTypeScalarTypeAsDataType(dataType);
    402        /** @type {number} */ var numBits = es3fShaderDerivateTests.getNumMantissaBits(precision);
    403        /** @type {number} */ var minExponent = es3fShaderDerivateTests.getMinExponent(precision);
    404 
    405        /** @type {number} */ var numVaryingSampleBits = numBits - es3fShaderDerivateTests.INTERPOLATION_LOST_BITS;
    406        /** @type {number} */ var numFailedPixels = 0;
    407 
    408        errorMask.clear(green);
    409 
    410        // search for failed pixels
    411        for (var y = 0; y < result.getHeight(); ++y)
    412        for (var x = 0; x < result.getWidth(); ++x) {
    413            //                 flushToZero?(f2z?(functionValueCurrent) - f2z?(functionValueBefore))
    414            // flushToZero? ( ------------------------------------------------------------------------ +- 2.5 ULP )
    415            //                                                  dx
    416 
    417            /** @type {Array<number>} */ var resultDerivative = es3fShaderDerivateTests.readDerivate(result, derivScale, derivBias, x, y);
    418 
    419            // sample at the front of the back pixel and the back of the front pixel to cover the whole area of
    420            // legal sample positions. In general case this is NOT OK, but we know that the target funtion is
    421            // (mostly*) linear which allows us to take the sample points at arbitrary points. This gets us the
    422            // maximum difference possible in exponents which are used in error bound calculations.
    423            // * non-linearity may happen around zero or with very high function values due to subnorms not
    424            //   behaving well.
    425            /** @type {Array<number>} */ var functionValueForward = (derivateFunc === es3fShaderDerivateTests.DerivateFunc.DFDX) ?
    426                                                        (func.evaluateAt(x + 2.0, y + 0.5)) :
    427                                                        (func.evaluateAt(x + 0.5, y + 2.0));
    428            /** @type {Array<number>} */ var functionValueBackward = (derivateFunc === es3fShaderDerivateTests.DerivateFunc.DFDX) ?
    429                                                        (func.evaluateAt(x - 1.0, y + 0.5)) :
    430                                                        (func.evaluateAt(x + 0.5, y - 1.0));
    431 
    432            /** @type {boolean} */ var anyComponentFailed = false;
    433 
    434            // check components separately
    435            for (var c = 0; c < numComponents; ++c) {
    436                // interpolation value range
    437                /** @type {tcuInterval.Interval} */ var forwardComponent = tcuInterval.withIntervals(
    438                    new tcuInterval.Interval(es3fShaderDerivateTests.convertFloorFlushToZero(
    439                        es3fShaderDerivateTests.addErrorUlp(functionValueForward[c], -0.5, numVaryingSampleBits), minExponent, numBits)),
    440                    new tcuInterval.Interval(es3fShaderDerivateTests.convertCeilFlushToZero(
    441                        es3fShaderDerivateTests.addErrorUlp(functionValueForward[c], +0.5, numVaryingSampleBits), minExponent, numBits))
    442                );
    443 
    444                /** @type {tcuInterval.Interval} */ var backwardComponent = tcuInterval.withIntervals(
    445                    new tcuInterval.Interval(es3fShaderDerivateTests.convertFloorFlushToZero(
    446                        es3fShaderDerivateTests.addErrorUlp(functionValueBackward[c], -0.5, numVaryingSampleBits), minExponent, numBits)),
    447                    new tcuInterval.Interval(es3fShaderDerivateTests.convertCeilFlushToZero(
    448                        es3fShaderDerivateTests.addErrorUlp(functionValueBackward[c], +0.5, numVaryingSampleBits), minExponent, numBits))
    449                );
    450 
    451                /** @type {number} */
    452                var maxValueExp = Math.max(
    453                        (new tcuFloat.deFloat().deFloatNumber(forwardComponent.lo())).exponent(),
    454                        (new tcuFloat.deFloat().deFloatNumber(forwardComponent.hi())).exponent(),
    455                        (new tcuFloat.deFloat().deFloatNumber(backwardComponent.lo())).exponent(),
    456                        (new tcuFloat.deFloat().deFloatNumber(backwardComponent.hi())).exponent());
    457 
    458                // subtraction in nominator will likely cause a cancellation of the most
    459                // significant bits. Apply error bounds.
    460                /** @type {tcuInterval.Interval} */ var nominator = tcuInterval.Interval.operatorSub(forwardComponent, backwardComponent);
    461                /** @type {number} */ var nominatorLoExp = (new tcuFloat.deFloat().deFloatNumber(nominator.lo())).exponent();
    462                /** @type {number} */ var nominatorHiExp = (new tcuFloat.deFloat().deFloatNumber(nominator.hi())).exponent();
    463                /** @type {number} */ var nominatorLoBitsLost = maxValueExp - nominatorLoExp;
    464                /** @type {number} */ var nominatorHiBitsLost = maxValueExp - nominatorHiExp;
    465                /** @type {number} */ var nominatorLoBits = Math.max(0, numBits - nominatorLoBitsLost);
    466                /** @type {number} */ var nominatorHiBits = Math.max(0, numBits - nominatorHiBitsLost);
    467 
    468                /** @type {tcuInterval.Interval} */ var nominatorRange = tcuInterval.withIntervals(
    469                    new tcuInterval.Interval(es3fShaderDerivateTests.convertFloorFlushToZero(nominator.lo(), minExponent, nominatorLoBits)),
    470                    new tcuInterval.Interval(es3fShaderDerivateTests.convertCeilFlushToZero(nominator.hi(), minExponent, nominatorHiBits)));
    471                //
    472                /** @type {tcuInterval.Interval} */ var divisionRange = tcuInterval.Interval.operatorDiv(nominatorRange, new tcuInterval.Interval(3.0)); // legal sample area is anywhere within this and neighboring pixels (i.e. size = 3)
    473                /** @type {tcuInterval.Interval} */ var divisionResultRange = tcuInterval.withIntervals(
    474                    new tcuInterval.Interval(es3fShaderDerivateTests.convertFloorFlushToZero(es3fShaderDerivateTests.addErrorUlp(divisionRange.lo(), -divisionErrorUlps, numBits), minExponent, numBits)),
    475                    new tcuInterval.Interval(es3fShaderDerivateTests.convertCeilFlushToZero(es3fShaderDerivateTests.addErrorUlp(divisionRange.hi(), divisionErrorUlps, numBits), minExponent, numBits)));
    476                /** @type {tcuInterval.Interval} */ var finalResultRange = tcuInterval.withIntervals(
    477                    new tcuInterval.Interval(divisionResultRange.lo() - surfaceThreshold[c]),
    478                    new tcuInterval.Interval(divisionResultRange.hi() + surfaceThreshold[c]));
    479 
    480                if (resultDerivative[c] >= finalResultRange.lo() && resultDerivative[c] <= finalResultRange.hi()) {
    481                    // value ok
    482                } else {
    483                    if (numFailedPixels < es3fShaderDerivateTests.MAX_FAILED_MESSAGES)
    484                        bufferedLogToConsole('Error in pixel at ' + x + ', ' + y + ' with component ' + c + ' (channel ' + ('rgba'[c]) + ')\n' +
    485                            '\tGot pixel value ' + result.getPixelInt(x, y) + '\n' +
    486                            '\t\tdFd' + ((derivateFunc === es3fShaderDerivateTests.DerivateFunc.DFDX) ? 'x' : 'y') + ' ~= ' + resultDerivative[c] + '\n' +
    487                            '\t\tdifference to a valid range: ' +
    488                            ((resultDerivative[c] < finalResultRange.lo()) ? '-' : '+') +
    489                            ((resultDerivative[c] < finalResultRange.lo()) ? (finalResultRange.lo() - resultDerivative[c]) : (resultDerivative[c] - finalResultRange.hi())) +
    490                            '\n' +
    491                            '\tDerivative value range:\n' +
    492                            '\t\tMin: ' + finalResultRange.lo() + '\n' +
    493                            '\t\tMax: ' + finalResultRange.hi() + '\n');
    494 
    495                    ++numFailedPixels;
    496                    anyComponentFailed = true;
    497                }
    498            }
    499 
    500            if (anyComponentFailed)
    501                errorMask.setPixel(red, x, y);
    502        }
    503 
    504        if (numFailedPixels >= es3fShaderDerivateTests.MAX_FAILED_MESSAGES)
    505            bufferedLogToConsole('...');
    506 
    507        if (numFailedPixels > 0)
    508            bufferedLogToConsole('FAIL: found ' + numFailedPixels + ' failed pixels');
    509 
    510        return numFailedPixels === 0;
    511    };
    512 
    513    /**
    514     * @constructor
    515     * @extends {tcuTestCase.DeqpTest}
    516     * @param {string} name
    517     * @param {string} description
    518     */
    519    es3fShaderDerivateTests.TriangleDerivateCase = function(name, description) {
    520        tcuTestCase.DeqpTest.call(this, name, description);
    521        /** @type {?gluShaderUtil.DataType} */ this.m_dataType = null;
    522        /** @type {?gluShaderUtil.precision} */ this.m_precision = null;
    523 
    524        /** @type {?gluShaderUtil.DataType} */ this.m_coordDataType = null;
    525        /** @type {?gluShaderUtil.precision} */ this.m_coordPrecision = null;
    526 
    527        /** @type {string} */ this.m_fragmentSrc;
    528 
    529        /** @type {Array<number>} */ this.m_coordMin = [];
    530        /** @type {Array<number>} */ this.m_coordMax = [];
    531        /** @type {Array<number>} */ this.m_derivScale = [];
    532        /** @type {Array<number>} */ this.m_derivBias = [];
    533 
    534        /** @type {es3fShaderDerivateTests.SurfaceType} */ this.m_surfaceType = es3fShaderDerivateTests.SurfaceType.DEFAULT_FRAMEBUFFER;
    535        /** @type {number} */ this.m_numSamples = 0;
    536        /** @type {number} */ this.m_hint = gl.DONT_CARE;
    537 
    538        assertMsgOptions(this.m_surfaceType !== es3fShaderDerivateTests.SurfaceType.DEFAULT_FRAMEBUFFER || this.m_numSamples === 0, 'Did not expect surfaceType = DEFAULT_FRAMEBUFFER or numSamples = 0', false, true);
    539    };
    540 
    541    es3fShaderDerivateTests.TriangleDerivateCase.prototype = Object.create(tcuTestCase.DeqpTest.prototype);
    542    es3fShaderDerivateTests.TriangleDerivateCase.prototype.constructor = es3fShaderDerivateTests.TriangleDerivateCase;
    543 
    544    es3fShaderDerivateTests.TriangleDerivateCase.prototype.deinit = function() {};
    545 
    546    /** @param {WebGLProgram} program */
    547    es3fShaderDerivateTests.TriangleDerivateCase.prototype.setupRenderState = function(program) {};
    548 
    549    /**
    550     * @param {?gluShaderUtil.DataType} coordType
    551     * @param {?gluShaderUtil.precision} precision
    552     * @return {string}
    553     */
    554    es3fShaderDerivateTests.genVertexSource = function(coordType, precision) {
    555        assertMsgOptions(gluShaderUtil.isDataTypeFloatOrVec(coordType), 'Coord Type not supported', false, true);
    556 
    557        /** @type {string} */ var vertexTmpl = '' +
    558            '#version 300 es\n' +
    559            'in highp vec4 a_position;\n' +
    560            'in ${PRECISION} ${DATATYPE} a_coord;\n' +
    561            'out ${PRECISION} ${DATATYPE} v_coord;\n' +
    562            'void main (void)\n' +
    563            '{\n' +
    564            ' gl_Position = a_position;\n' +
    565            ' v_coord = a_coord;\n' +
    566            '}\n';
    567 
    568        /** @type {Object} */ var vertexParams = {};
    569 
    570        vertexParams['PRECISION'] = gluShaderUtil.getPrecisionName(precision);
    571        vertexParams['DATATYPE'] = gluShaderUtil.getDataTypeName(coordType);
    572 
    573        return tcuStringTemplate.specialize(vertexTmpl, vertexParams);
    574    };
    575 
    576    /**
    577     * @return {Array<number>}
    578     */
    579    es3fShaderDerivateTests.TriangleDerivateCase.prototype.getViewportSize = function() {
    580        if (this.m_surfaceType === es3fShaderDerivateTests.SurfaceType.DEFAULT_FRAMEBUFFER) {
    581            /** @type {number} */ var width = Math.min(gl.drawingBufferWidth, es3fShaderDerivateTests.VIEWPORT_WIDTH);
    582            /** @type {number} */ var height = Math.min(gl.drawingBufferHeight, es3fShaderDerivateTests.VIEWPORT_HEIGHT);
    583            return [width, height];
    584        } else
    585            return [es3fShaderDerivateTests.FBO_WIDTH, es3fShaderDerivateTests.FBO_HEIGHT];
    586    };
    587 
    588    /**
    589     * @return {tcuTestCase.IterateResult}
    590     */
    591    es3fShaderDerivateTests.TriangleDerivateCase.prototype.iterate = function() {
    592        /** @type {gluShaderProgram.ShaderProgram} */ var program = new gluShaderProgram.ShaderProgram(gl, gluShaderProgram.makeVtxFragSources(es3fShaderDerivateTests.genVertexSource(this.m_coordDataType, this.m_coordPrecision), this.m_fragmentSrc));
    593        /** @type {deRandom.Random} */ var rnd = new deRandom.Random(deString.deStringHash(this.name) ^ 0xbbc24);
    594        /** @type {boolean} */ var useFbo = this.m_surfaceType != es3fShaderDerivateTests.SurfaceType.DEFAULT_FRAMEBUFFER;
    595        /** @type {number} */ var fboFormat = this.m_surfaceType === es3fShaderDerivateTests.SurfaceType.FLOAT_FBO ? gl.RGBA32UI : gl.RGBA8;
    596        /** @type {Array<number>} */ var viewportSize = this.getViewportSize();
    597        /** @type {number} */ var viewportX = useFbo ? 0 : rnd.getInt(0, gl.drawingBufferWidth - viewportSize[0]);
    598        /** @type {number} */ var viewportY = useFbo ? 0 : rnd.getInt(0, gl.drawingBufferHeight - viewportSize[1]);
    599        /** @type {?WebGLFramebuffer} */ var fbo = null;
    600        /** @type {?WebGLRenderbuffer} */ var rbo = null;
    601        /** @type {tcuTexture.TextureLevel} */ var result = null;
    602 
    603        bufferedLogToConsole(program.getProgramInfo().infoLog);
    604 
    605        if (!program.isOk())
    606            assertMsgOptions(false, 'Compile failed', false, true);
    607 
    608        if (useFbo) {
    609            bufferedLogToConsole('Rendering to FBO, format = ' + wtu.glEnumToString(gl, fboFormat) + ', samples = ' + this.m_numSamples);
    610 
    611            fbo = gl.createFramebuffer();
    612            rbo = gl.createRenderbuffer();
    613 
    614            gl.bindRenderbuffer(gl.RENDERBUFFER, rbo);
    615            gl.renderbufferStorageMultisample(gl.RENDERBUFFER, this.m_numSamples, fboFormat, viewportSize[0], viewportSize[1]);
    616            gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
    617            gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, rbo);
    618        } else {
    619            /** @type {tcuPixelFormat.PixelFormat} */ var pixelFormat = tcuPixelFormat.PixelFormatFromContext(gl);
    620 
    621            bufferedLogToConsole('Rendering to default framebuffer\n' +
    622                '\tColor depth: R=' + pixelFormat.redBits + ', G=' + pixelFormat.greenBits + ', B=' + pixelFormat.blueBits + ', A=' + pixelFormat.alphaBits);
    623        }
    624 
    625        bufferedLogToConsole('in: ' + this.m_coordMin + ' ' + this.m_coordMax + '\n' +
    626            'v_coord.x = in.x * x\n' +
    627            'v_coord.y = in.y * y\n' +
    628            'v_coord.z = in.z * (x+y)/2\n' +
    629            'v_coord.w = in.w * (1 - (x+y)/2)\n' +
    630            '\n' +
    631            'u_scale: ' + this.m_derivScale + ', u_bias: ' + this.m_derivBias + ' (displayed values have scale/bias removed)' +
    632            'Viewport: ' + viewportSize[0] + 'x' + viewportSize[1] +
    633            'gl.FRAGMENT_SHADER_DERIVATE_HINT: ' + wtu.glEnumToString(gl, this.m_hint));
    634        // Draw
    635        /** @type {Array<number>} */ var positions = [
    636            -1.0, -1.0, 0.0, 1.0,
    637            -1.0, 1.0, 0.0, 1.0,
    638            1.0, -1.0, 0.0, 1.0,
    639            1.0, 1.0, 0.0, 1.0
    640        ];
    641 
    642        /** @type {Array<number>} */ var coords =[
    643            this.m_coordMin[0], this.m_coordMin[1], this.m_coordMin[2], this.m_coordMax[3],
    644            this.m_coordMin[0], this.m_coordMax[1], (this.m_coordMin[2] + this.m_coordMax[2]) * 0.5, (this.m_coordMin[3]+this.m_coordMax[3]) * 0.5,
    645            this.m_coordMax[0], this.m_coordMin[1], (this.m_coordMin[2] + this.m_coordMax[2]) * 0.5, (this.m_coordMin[3]+this.m_coordMax[3]) * 0.5,
    646            this.m_coordMax[0], this.m_coordMax[1], this.m_coordMax[2], this.m_coordMin[3]
    647        ];
    648 
    649        /** @type {Array<gluDrawUtil.VertexArrayBinding>} */ var vertexArrays = [
    650            gluDrawUtil.newFloatVertexArrayBinding('a_position', 4, 4, 0, positions),
    651            gluDrawUtil.newFloatVertexArrayBinding('a_coord', 4, 4, 0, coords)
    652        ];
    653 
    654        /** @type {Array<number>} */ var indices = [0, 2, 1, 2, 3, 1];
    655 
    656        gl.clearColor(0.125, 0.25, 0.5, 1.0);
    657        // We can't really call clear() on gl.COLOR_BUFFER_BIT here as in c++ deqp.
    658        // The fbo format might be of integer type and WebGL2 requires an INVALID_OPERATION to be generated.
    659        var formatObj = gluTextureUtil.mapGLInternalFormat(fboFormat);
    660        var fmtClass = tcuTexture.getTextureChannelClass(formatObj.type);
    661        switch (fmtClass) {
    662            case tcuTexture.TextureChannelClass.FLOATING_POINT:
    663            case tcuTexture.TextureChannelClass.SIGNED_FIXED_POINT:
    664            case tcuTexture.TextureChannelClass.UNSIGNED_FIXED_POINT:
    665                gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);
    666                break;
    667            case tcuTexture.TextureChannelClass.UNSIGNED_INTEGER:
    668                gl.clearBufferuiv(gl.COLOR, 0, new Uint32Array([31, 63, 127, 255]));
    669                gl.clear(gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);
    670                break;
    671            case tcuTexture.TextureChannelClass.SIGNED_INTEGER:
    672                gl.clearBufferiv(gl.COLOR, 0, new Int32Array([31, 63, 127, 255]));
    673                gl.clear(gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);
    674                break;
    675            default:
    676                throw new Error('Invalid channelclass ' + fmtClass);
    677        }
    678        gl.disable(gl.DITHER);
    679 
    680        gl.useProgram(program.getProgram());
    681 
    682        /** @type {WebGLUniformLocation} */ var scaleLoc = gl.getUniformLocation(program.getProgram(), 'u_scale');
    683        /** @type {WebGLUniformLocation} */ var biasLoc = gl.getUniformLocation(program.getProgram(), 'u_bias');
    684 
    685        switch (this.m_dataType) {
    686            case gluShaderUtil.DataType.FLOAT:
    687                gl.uniform1f(scaleLoc, this.m_derivScale[0]);
    688                gl.uniform1f(biasLoc, this.m_derivBias[0]);
    689                break;
    690 
    691            case gluShaderUtil.DataType.FLOAT_VEC2:
    692                gl.uniform2fv(scaleLoc, this.m_derivScale.slice(0,2));
    693                gl.uniform2fv(biasLoc, this.m_derivBias.slice(0,2));
    694                break;
    695 
    696            case gluShaderUtil.DataType.FLOAT_VEC3:
    697                gl.uniform3fv(scaleLoc, this.m_derivScale.slice(0,3));
    698                gl.uniform3fv(biasLoc, this.m_derivBias.slice(0,3));
    699                break;
    700 
    701            case gluShaderUtil.DataType.FLOAT_VEC4:
    702                gl.uniform4fv(scaleLoc, this.m_derivScale);
    703                gl.uniform4fv(biasLoc, this.m_derivBias);
    704                break;
    705 
    706            default:
    707                throw new Error('Data Type not supported: ' + this.m_dataType);
    708        }
    709 
    710        glsShaderRenderCase.setupDefaultUniforms(program.getProgram());
    711        this.setupRenderState(program.getProgram());
    712 
    713        gl.hint(gl.FRAGMENT_SHADER_DERIVATIVE_HINT, this.m_hint);
    714 
    715        gl.viewport(viewportX, viewportY, viewportSize[0], viewportSize[1]);
    716        gluDrawUtil.draw(gl, program.getProgram(), vertexArrays, gluDrawUtil.triangles(indices));
    717 
    718        // Read back results
    719 
    720        /** @type {boolean} */ var isMSAA = useFbo && this.m_numSamples > 0;
    721        /** @type {?WebGLFramebuffer} */ var resFbo = null;
    722        /** @type {?WebGLRenderbuffer} */ var resRbo = null;
    723 
    724        // Resolve if necessary
    725        if (isMSAA) {
    726            resFbo = gl.createFramebuffer();
    727            resRbo = gl.createRenderbuffer();
    728 
    729            gl.bindRenderbuffer(gl.RENDERBUFFER, resRbo);
    730            gl.renderbufferStorageMultisample(gl.RENDERBUFFER, 0, fboFormat, viewportSize[0], viewportSize[1]);
    731            gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, resFbo);
    732            gl.framebufferRenderbuffer(gl.DRAW_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, resRbo);
    733 
    734            gl.blitFramebuffer(0, 0, viewportSize[0], viewportSize[1], 0, 0, viewportSize[0], viewportSize[1], gl.COLOR_BUFFER_BIT, gl.NEAREST);
    735 
    736            gl.bindFramebuffer(gl.READ_FRAMEBUFFER, resFbo);
    737        }
    738        switch (this.m_surfaceType) {
    739            case es3fShaderDerivateTests.SurfaceType.DEFAULT_FRAMEBUFFER:
    740            case es3fShaderDerivateTests.SurfaceType.UNORM_FBO:
    741                var dataFormat = new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RGBA, tcuTexture.ChannelType.UNORM_INT8);
    742                result = new tcuTexture.TextureLevel(dataFormat, viewportSize[0], viewportSize[1]);
    743                gluPixelTransfer.readPixels(gl, viewportX, viewportY, dataFormat, result);
    744                break;
    745 
    746            case es3fShaderDerivateTests.SurfaceType.FLOAT_FBO:
    747                var dataFormat = new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RGBA, tcuTexture.ChannelType.FLOAT);
    748                var transferFormat = new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RGBA, tcuTexture.ChannelType.UNSIGNED_INT32);
    749                result = new tcuTexture.TextureLevel(dataFormat, viewportSize[0], viewportSize[1]);
    750                gluPixelTransfer.readPixels(gl, viewportX, viewportY, transferFormat, result);
    751                break;
    752 
    753            default:
    754                throw new Error('Surface Type not supported: ' + this.m_surfaceType);
    755        }
    756 
    757        // Verify
    758        /** @type {tcuSurface.Surface} */
    759        var errorMask = new tcuSurface.Surface(result.getWidth(), result.getHeight());
    760 
    761        errorMask.getAccess().clear(tcuRGBA.RGBA.green.toVec());
    762 
    763        /** @type {boolean} */ var isOk = this.verify(result.getAccess(), errorMask.getAccess());
    764 
    765        if (!isOk) {
    766            tcuLogImage.logImage('Rendered', 'Rendered image', result.getAccess());
    767            tcuLogImage.logImage('ErrorMask', 'Error mask', errorMask.getAccess());
    768            testFailedOptions('Fail', false);
    769        } else
    770            testPassedOptions('Pass', true);
    771 
    772        // Cleaning up buffers
    773        gl.deleteFramebuffer(fbo);
    774        gl.deleteRenderbuffer(rbo);
    775        gl.deleteFramebuffer(resFbo);
    776        gl.deleteRenderbuffer(resRbo);
    777 
    778        return tcuTestCase.IterateResult.STOP;
    779    };
    780 
    781    /**
    782     * @return {Array<number>}
    783     */
    784    es3fShaderDerivateTests.TriangleDerivateCase.prototype.getSurfaceThreshold = function() {
    785        switch (this.m_surfaceType) {
    786            case es3fShaderDerivateTests.SurfaceType.DEFAULT_FRAMEBUFFER:
    787                /** @type {tcuPixelFormat.PixelFormat} */ var pixelFormat = tcuPixelFormat.PixelFormatFromContext(gl);
    788                /** @type {Array<number>} */ var channelBits = [pixelFormat.redBits, pixelFormat.greenBits, pixelFormat.blueBits, pixelFormat.alphaBits];
    789                /** @type {Array<number>} */ var intThreshold = deMath.arrayShiftLeft([1, 1, 1, 1], deMath.subtract([8, 8, 8, 8], channelBits));
    790                /** @type {Array<number>} */ var normThreshold = deMath.scale(intThreshold, 1.0/255.0);
    791 
    792                return normThreshold;
    793 
    794            case es3fShaderDerivateTests.SurfaceType.UNORM_FBO: return deMath.scale([1, 1, 1, 1], 1.0/255.0);
    795            case es3fShaderDerivateTests.SurfaceType.FLOAT_FBO: return [0.0, 0.0, 0.0, 0.0];
    796            default:
    797                assertMsgOptions(false, 'Surface Type not supported. Falling back to default retun value [0.0, 0.0, 0.0, 0.0]', false, false);
    798                return [0.0, 0.0, 0.0, 0.0];
    799        }
    800    };
    801 
    802    /**
    803     * @constructor
    804     * @extends {es3fShaderDerivateTests.TriangleDerivateCase}
    805     * @param {string} name
    806     * @param {string} description
    807     * @param {es3fShaderDerivateTests.DerivateFunc} func
    808     * @param {gluShaderUtil.DataType} type
    809     */
    810    es3fShaderDerivateTests.ConstantDerivateCase = function(name, description, func, type) {
    811        es3fShaderDerivateTests.TriangleDerivateCase.call(this, name, description);
    812        /** @type {es3fShaderDerivateTests.DerivateFunc} */ this.m_func = func;
    813        this.m_dataType = type;
    814        this.m_precision = gluShaderUtil.precision.PRECISION_HIGHP;
    815        this.m_coordDataType = this.m_dataType;
    816        this.m_coordPrecision = this.m_precision;
    817    };
    818 
    819    es3fShaderDerivateTests.ConstantDerivateCase.prototype = Object.create(es3fShaderDerivateTests.TriangleDerivateCase.prototype);
    820    es3fShaderDerivateTests.ConstantDerivateCase.prototype.constructor = es3fShaderDerivateTests.ConstantDerivateCase;
    821 
    822    es3fShaderDerivateTests.ConstantDerivateCase.prototype.init = function() {
    823        /** @type {string} */ var fragmentTmpl = '' +
    824            '#version 300 es\n' +
    825            'layout(location = 0) out mediump vec4 o_color;\n' +
    826            'uniform ${PRECISION} ${DATATYPE} u_scale;\n' +
    827            'uniform ${PRECISION} ${DATATYPE} u_bias;\n' +
    828            'void main (void)\n' +
    829            '{\n' +
    830            ' ${PRECISION} ${DATATYPE} res = ${FUNC}(${VALUE}) * u_scale + u_bias;\n' +
    831            ' o_color = ${CAST_TO_OUTPUT};\n' +
    832            '}\n';
    833 
    834        /** @type {Object} */ var fragmentParams = {};
    835        fragmentParams['PRECISION'] = gluShaderUtil.getPrecisionName(this.m_precision);
    836        fragmentParams['DATATYPE'] = gluShaderUtil.getDataTypeName(this.m_dataType);
    837        fragmentParams['FUNC'] = es3fShaderDerivateTests.getDerivateFuncName(this.m_func);
    838        fragmentParams['VALUE'] = this.m_dataType === gluShaderUtil.DataType.FLOAT_VEC4 ? 'vec4(1.0, 7.2, -1e5, 0.0)' :
    839            this.m_dataType === gluShaderUtil.DataType.FLOAT_VEC3 ? 'vec3(1e2, 8.0, 0.01)' :
    840            this.m_dataType === gluShaderUtil.DataType.FLOAT_VEC2 ? 'vec2(-0.0, 2.7)' :
    841            '7.7';
    842        fragmentParams['CAST_TO_OUTPUT'] = this.m_dataType === gluShaderUtil.DataType.FLOAT_VEC4 ? 'res' :
    843            this.m_dataType === gluShaderUtil.DataType.FLOAT_VEC3 ? 'vec4(res, 1.0)' :
    844            this.m_dataType === gluShaderUtil.DataType.FLOAT_VEC2 ? 'vec4(res, 0.0, 1.0)' :
    845            'vec4(res, 0.0, 0.0, 1.0)';
    846 
    847        this.m_fragmentSrc = tcuStringTemplate.specialize(fragmentTmpl, fragmentParams);
    848 
    849        this.m_derivScale = [1e3, 1e3, 1e3, 1e3];
    850        this.m_derivBias = [0.5, 0.5, 0.5, 0.5];
    851    };
    852 
    853    /**
    854     * @param {tcuTexture.ConstPixelBufferAccess} result
    855     * @param {tcuTexture.PixelBufferAccess} errorMask
    856     * @return {boolean}
    857     */
    858    es3fShaderDerivateTests.ConstantDerivateCase.prototype.verify = function(result, errorMask) {
    859        /** @type {Array<number>} */ var reference = [0.0, 0.0, 0.0, 0.0]; // Derivate of constant argument should always be 0
    860        /** @type {Array<number>} */ var threshold = deMath.divide(this.getSurfaceThreshold(), deMath.abs(this.m_derivScale));
    861        return es3fShaderDerivateTests.verifyConstantDerivate(result, errorMask, this.m_dataType,
    862            reference, threshold, this.m_derivScale, this.m_derivBias);
    863    };
    864 
    865    /**
    866     * @constructor
    867     * @extends {es3fShaderDerivateTests.TriangleDerivateCase}
    868     * @param {string} name
    869     * @param {string} description
    870     * @param {es3fShaderDerivateTests.DerivateFunc} func
    871     * @param {gluShaderUtil.DataType} type
    872     * @param {gluShaderUtil.precision} precision
    873     * @param {number} hint
    874     * @param {es3fShaderDerivateTests.SurfaceType} surfaceType
    875     * @param {number} numSamples
    876     * @param {string} fragmentSrcTmpl
    877     */
    878    es3fShaderDerivateTests.LinearDerivateCase = function(name, description, func, type, precision, hint, surfaceType, numSamples, fragmentSrcTmpl) {
    879        es3fShaderDerivateTests.TriangleDerivateCase.call(this, name, description);
    880        /** @type {es3fShaderDerivateTests.DerivateFunc} */ this.m_func = func;
    881        /** @type {string} */ this.m_fragmentTmpl = fragmentSrcTmpl;
    882        this.m_dataType = type;
    883        this.m_precision = precision;
    884        this.m_coordDataType = this.m_dataType;
    885        this.m_coordPrecision = this.m_precision;
    886        this.m_hint = hint;
    887        this.m_surfaceType = surfaceType;
    888        this.m_numSamples = numSamples;
    889    };
    890 
    891    es3fShaderDerivateTests.LinearDerivateCase.prototype = Object.create(es3fShaderDerivateTests.TriangleDerivateCase.prototype);
    892    es3fShaderDerivateTests.LinearDerivateCase.prototype.constructor = es3fShaderDerivateTests.LinearDerivateCase;
    893 
    894    es3fShaderDerivateTests.LinearDerivateCase.prototype.init = function() {
    895        /** @type {Array<number>} */ var viewportSize = this.getViewportSize();
    896        /** @type {number} */ var w = viewportSize[0];
    897        /** @type {number} */ var h = viewportSize[1];
    898        /** @type {boolean} */ var packToInt = this.m_surfaceType === es3fShaderDerivateTests.SurfaceType.FLOAT_FBO;
    899 
    900        /** @type {Object} */ var fragmentParams = {};
    901        fragmentParams['OUTPUT_TYPE'] = gluShaderUtil.getDataTypeName(packToInt ? gluShaderUtil.DataType.UINT_VEC4 : gluShaderUtil.DataType.FLOAT_VEC4);
    902        fragmentParams['OUTPUT_PREC'] = gluShaderUtil.getPrecisionName(packToInt ? gluShaderUtil.precision.PRECISION_HIGHP : this.m_precision);
    903        fragmentParams['PRECISION'] = gluShaderUtil.getPrecisionName(this.m_precision);
    904        fragmentParams['DATATYPE'] = gluShaderUtil.getDataTypeName(this.m_dataType);
    905        fragmentParams['FUNC'] = es3fShaderDerivateTests.getDerivateFuncName(this.m_func);
    906 
    907        if (packToInt) {
    908            fragmentParams['CAST_TO_OUTPUT'] = this.m_dataType === gluShaderUtil.DataType.FLOAT_VEC4 ? 'floatBitsToUint(res)' :
    909                this.m_dataType === gluShaderUtil.DataType.FLOAT_VEC3 ? 'floatBitsToUint(vec4(res, 1.0))' :
    910                this.m_dataType === gluShaderUtil.DataType.FLOAT_VEC2 ? 'floatBitsToUint(vec4(res, 0.0, 1.0))' :
    911                'floatBitsToUint(vec4(res, 0.0, 0.0, 1.0))';
    912        } else {
    913            fragmentParams['CAST_TO_OUTPUT'] = this.m_dataType === gluShaderUtil.DataType.FLOAT_VEC4 ? 'res' :
    914                this.m_dataType === gluShaderUtil.DataType.FLOAT_VEC3 ? 'vec4(res, 1.0)' :
    915                this.m_dataType === gluShaderUtil.DataType.FLOAT_VEC2 ? 'vec4(res, 0.0, 1.0)' :
    916                'vec4(res, 0.0, 0.0, 1.0)';
    917        }
    918 
    919        this.m_fragmentSrc = tcuStringTemplate.specialize(this.m_fragmentTmpl, fragmentParams);
    920 
    921        switch (this.m_precision) {
    922            case gluShaderUtil.precision.PRECISION_HIGHP:
    923                this.m_coordMin = [-97., 0.2, 71., 74.];
    924                this.m_coordMax = [-13.2, -77., 44., 76.];
    925                break;
    926 
    927            case gluShaderUtil.precision.PRECISION_MEDIUMP:
    928                this.m_coordMin = [-37.0, 47., -7., 0.0];
    929                this.m_coordMax = [-1.0, 12., 7., 19.];
    930                break;
    931 
    932            case gluShaderUtil.precision.PRECISION_LOWP:
    933                this.m_coordMin = [0.0, -1.0, 0.0, 1.0];
    934                this.m_coordMax = [1.0, 1.0, -1.0, -1.0];
    935                break;
    936 
    937            default:
    938                throw new Error('Precision not supported: ' + this.m_precision);
    939        }
    940 
    941        if (this.m_surfaceType === es3fShaderDerivateTests.SurfaceType.FLOAT_FBO) {
    942            // No scale or bias used for accuracy.
    943            this.m_derivScale = [1.0, 1.0, 1.0, 1.0];
    944            this.m_derivBias = [0.0, 0.0, 0.0, 0.0];
    945        } else {
    946            // Compute scale - bias that normalizes to 0..1 range.
    947            /** @type {Array<number>} */ var dx = deMath.divide(deMath.subtract(this.m_coordMax, this.m_coordMin), [w, w, w * 0.5, -w * 0.5]);
    948            /** @type {Array<number>} */ var dy = deMath.divide(deMath.subtract(this.m_coordMax, this.m_coordMin), [h, h, h * 0.5, -h * 0.5]);
    949 
    950            switch (this.m_func) {
    951                case es3fShaderDerivateTests.DerivateFunc.DFDX:
    952                    this.m_derivScale = deMath.divide([0.5, 0.5, 0.5, 0.5], dx);
    953                    break;
    954 
    955                case es3fShaderDerivateTests.DerivateFunc.DFDY:
    956                    this.m_derivScale = deMath.divide([0.5, 0.5, 0.5, 0.5], dy);
    957                    break;
    958 
    959                case es3fShaderDerivateTests.DerivateFunc.FWIDTH:
    960                    this.m_derivScale = deMath.divide([0.5, 0.5, 0.5, 0.5], deMath.add(deMath.abs(dx), deMath.abs(dy)));
    961                    break;
    962 
    963                default:
    964                    throw new Error('Derivate Function not supported: ' + this.m_func);
    965            }
    966 
    967            this.m_derivBias = [0.0, 0.0, 0.0, 0.0];
    968        }
    969    };
    970 
    971    /**
    972     * @param {tcuTexture.ConstPixelBufferAccess} result
    973     * @param {tcuTexture.PixelBufferAccess} errorMask
    974     * @return {boolean}
    975     */
    976    es3fShaderDerivateTests.LinearDerivateCase.prototype.verify = function(result, errorMask) {
    977        /** @type {Array<number>} */ var xScale = [1.0, 0.0, 0.5, -0.5];
    978        /** @type {Array<number>} */ var yScale = [0.0, 1.0, 0.5, -0.5];
    979        /** @type {Array<number>} */ var surfaceThreshold = deMath.divide(this.getSurfaceThreshold(), deMath.abs(this.m_derivScale));
    980 
    981        /** @type {number} */ var w;
    982        /** @type {number} */ var h;
    983        /** @type {Array<number>} */ var reference;
    984        /** @type {Array<number>} */ var threshold;
    985 
    986        if (this.m_func === es3fShaderDerivateTests.DerivateFunc.DFDX || this.m_func === es3fShaderDerivateTests.DerivateFunc.DFDY) {
    987            /** @type {boolean} */ var isX = this.m_func === es3fShaderDerivateTests.DerivateFunc.DFDX;
    988            /** @type {number} */ var div = isX ? result.getWidth() : result.getHeight();
    989            /** @type {Array<number>} */ var scale = isX ? xScale : yScale;
    990            reference = deMath.multiply(deMath.scale(deMath.subtract(this.m_coordMax, this.m_coordMin), 1/div), scale);
    991            /** @type {Array<number>} */ var opThreshold = es3fShaderDerivateTests.getDerivateThreshold(this.m_precision, deMath.multiply(this.m_coordMin, scale), deMath.multiply(this.m_coordMax, scale), reference);
    992            threshold = deMath.max(surfaceThreshold, opThreshold);
    993            bufferedLogToConsole('Verifying result image.\n' +
    994                '\tValid derivative is ' + reference + ' with threshold ' + threshold);
    995 
    996            // short circuit if result is strictly within the normal value error bounds.
    997            // This improves performance significantly.
    998            if (es3fShaderDerivateTests.verifyConstantDerivate(result, errorMask,
    999                this.m_dataType, reference, threshold, this.m_derivScale,
   1000                this.m_derivBias, es3fShaderDerivateTests.VerificationLogging.LOG_NOTHING)) {
   1001                bufferedLogToConsole('No incorrect derivatives found, result valid.');
   1002                return true;
   1003            }
   1004 
   1005            // some pixels exceed error bounds calculated for normal values. Verify that these
   1006            // potentially invalid pixels are in fact valid due to (for example) subnorm flushing.
   1007 
   1008            bufferedLogToConsole('Initial verification failed, verifying image by calculating accurate error bounds for each result pixel.\n' +
   1009                '\tVerifying each result derivative is within its range of legal result values.');
   1010 
   1011            /** @type {Array<number>} */ var viewportSize = this.getViewportSize();
   1012            /** @type {Array<number>} */ var valueRamp = deMath.subtract(this.m_coordMax, this.m_coordMin);
   1013            /** @type {es3fShaderDerivateTests.Linear2DFunctionEvaluator} */ var function_ = new es3fShaderDerivateTests.Linear2DFunctionEvaluator();
   1014            w = viewportSize[0];
   1015            h = viewportSize[1];
   1016 
   1017            function_.matrix.setRow(0, [valueRamp[0] / w, 0.0, this.m_coordMin[0]]);
   1018            function_.matrix.setRow(1, [0.0, valueRamp[1] / h, this.m_coordMin[1]]);
   1019            function_.matrix.setRow(2, deMath.scale([valueRamp[2] / w, valueRamp[2] / h, this.m_coordMin[2] + this.m_coordMin[2]], 1 / 2.0));
   1020            function_.matrix.setRow(3, deMath.scale([-valueRamp[3] / w, -valueRamp[3] / h, this.m_coordMax[3] + this.m_coordMax[3]], 1 / 2.0));
   1021 
   1022            return es3fShaderDerivateTests.reverifyConstantDerivateWithFlushRelaxations(
   1023                result, errorMask, this.m_dataType, this.m_precision, this.m_derivScale,
   1024                this.m_derivBias, surfaceThreshold, this.m_func, function_);
   1025        } else {
   1026            assertMsgOptions(this.m_func === es3fShaderDerivateTests.DerivateFunc.FWIDTH, 'Expected DerivateFunc.FWIDTH', false, true);
   1027            w = result.getWidth();
   1028            h = result.getHeight();
   1029 
   1030            /** @type {Array<number>} */ var dx = deMath.multiply(deMath.scale(deMath.subtract(this.m_coordMax, this.m_coordMin), 1 / w), xScale);
   1031            /** @type {Array<number>} */ var dy = deMath.multiply(deMath.scale(deMath.subtract(this.m_coordMax, this.m_coordMin), 1 / h), yScale);
   1032            reference = deMath.add(deMath.abs(dx), deMath.abs(dy));
   1033            /** @type {Array<number>} */ var dxThreshold = es3fShaderDerivateTests.getDerivateThreshold(this.m_precision, deMath.multiply(this.m_coordMin, xScale), deMath.multiply(this.m_coordMax, xScale), dx);
   1034            /** @type {Array<number>} */ var dyThreshold = es3fShaderDerivateTests.getDerivateThreshold(this.m_precision, deMath.multiply(this.m_coordMin, yScale), deMath.multiply(this.m_coordMax, yScale), dy);
   1035            threshold = deMath.max(surfaceThreshold, deMath.max(dxThreshold, dyThreshold));
   1036 
   1037            return es3fShaderDerivateTests.verifyConstantDerivate(result, errorMask, this.m_dataType,
   1038                                          reference, threshold, this.m_derivScale, this.m_derivBias);
   1039        }
   1040    };
   1041 
   1042    /**
   1043     * @constructor
   1044     * @extends {es3fShaderDerivateTests.TriangleDerivateCase}
   1045     * @param {string} name
   1046     * @param {string} description
   1047     * @param {es3fShaderDerivateTests.DerivateFunc} func
   1048     * @param {gluShaderUtil.DataType} type
   1049     * @param {gluShaderUtil.precision} precision
   1050     * @param {number} hint
   1051     * @param {es3fShaderDerivateTests.SurfaceType} surfaceType
   1052     * @param {number} numSamples
   1053     */
   1054    es3fShaderDerivateTests.TextureDerivateCase = function(name, description, func, type, precision, hint, surfaceType, numSamples) {
   1055        es3fShaderDerivateTests.TriangleDerivateCase.call(this, name, description);
   1056        /** @type {es3fShaderDerivateTests.DerivateFunc} */ this.m_func = func;
   1057        /** @type {gluTexture.Texture2D} */ this.m_texture = null;
   1058        /** @type {Array<number>} */ this.m_texValueMin = [];
   1059        /** @type {Array<number>} */ this.m_texValueMax = [];
   1060        this.m_dataType = type;
   1061        this.m_precision = precision;
   1062        this.m_coordDataType = gluShaderUtil.DataType.FLOAT_VEC2;
   1063        this.m_coordPrecision = gluShaderUtil.precision.PRECISION_HIGHP;
   1064        this.m_hint = hint;
   1065        this.m_surfaceType = surfaceType;
   1066        this.m_numSamples = numSamples;
   1067    };
   1068 
   1069    es3fShaderDerivateTests.TextureDerivateCase.prototype = Object.create(es3fShaderDerivateTests.TriangleDerivateCase.prototype);
   1070    es3fShaderDerivateTests.TextureDerivateCase.prototype.constructor = es3fShaderDerivateTests.TextureDerivateCase;
   1071 
   1072    es3fShaderDerivateTests.TextureDerivateCase.prototype.deinit = function() {
   1073            this.m_texture = null;
   1074    };
   1075 
   1076    es3fShaderDerivateTests.TextureDerivateCase.prototype.init = function() {
   1077        // Generate shader
   1078        /** @type {string} */ var fragmentTmpl = '' +
   1079            '#version 300 es\n' +
   1080            'in highp vec2 v_coord;\n' +
   1081            'layout(location = 0) out ${OUTPUT_PREC} ${OUTPUT_TYPE} o_color;\n' +
   1082            'uniform ${PRECISION} sampler2D u_sampler;\n' +
   1083            'uniform ${PRECISION} ${DATATYPE} u_scale;\n' +
   1084            'uniform ${PRECISION} ${DATATYPE} u_bias;\n' +
   1085            'void main (void)\n' +
   1086            '{\n' +
   1087            ' ${PRECISION} vec4 tex = texture(u_sampler, v_coord);\n' +
   1088            ' ${PRECISION} ${DATATYPE} res = ${FUNC}(tex${SWIZZLE}) * u_scale + u_bias;\n' +
   1089            ' o_color = ${CAST_TO_OUTPUT};\n' +
   1090            '}\n';
   1091 
   1092        /** @type {boolean} */ var packToInt = this.m_surfaceType === es3fShaderDerivateTests.SurfaceType.FLOAT_FBO;
   1093        /** @type {Object} */ var fragmentParams = {};
   1094        /** @type {Array<number>} */ var viewportSize;
   1095        fragmentParams['OUTPUT_TYPE'] = gluShaderUtil.getDataTypeName(packToInt ? gluShaderUtil.DataType.UINT_VEC4 : gluShaderUtil.DataType.FLOAT_VEC4);
   1096        fragmentParams['OUTPUT_PREC'] = gluShaderUtil.getPrecisionName(packToInt ? gluShaderUtil.precision.PRECISION_HIGHP : this.m_precision);
   1097        fragmentParams['PRECISION'] = gluShaderUtil.getPrecisionName(this.m_precision);
   1098        fragmentParams['DATATYPE'] = gluShaderUtil.getDataTypeName(this.m_dataType);
   1099        fragmentParams['FUNC'] = es3fShaderDerivateTests.getDerivateFuncName(this.m_func);
   1100        fragmentParams['SWIZZLE'] = this.m_dataType === gluShaderUtil.DataType.FLOAT_VEC4 ? '' :
   1101            this.m_dataType === gluShaderUtil.DataType.FLOAT_VEC3 ? '.xyz' :
   1102            this.m_dataType === gluShaderUtil.DataType.FLOAT_VEC2 ? '.xy' :
   1103            '.x';
   1104 
   1105        if (packToInt) {
   1106            fragmentParams['CAST_TO_OUTPUT'] = this.m_dataType === gluShaderUtil.DataType.FLOAT_VEC4 ? 'floatBitsToUint(res)' :
   1107                this.m_dataType === gluShaderUtil.DataType.FLOAT_VEC3 ? 'floatBitsToUint(vec4(res, 1.0))' :
   1108                this.m_dataType === gluShaderUtil.DataType.FLOAT_VEC2 ? 'floatBitsToUint(vec4(res, 0.0, 1.0))' :
   1109                'floatBitsToUint(vec4(res, 0.0, 0.0, 1.0))';
   1110        } else {
   1111            fragmentParams['CAST_TO_OUTPUT'] = this.m_dataType === gluShaderUtil.DataType.FLOAT_VEC4 ? 'res' :
   1112                this.m_dataType === gluShaderUtil.DataType.FLOAT_VEC3 ? 'vec4(res, 1.0)' :
   1113                this.m_dataType === gluShaderUtil.DataType.FLOAT_VEC2 ? 'vec4(res, 0.0, 1.0)' :
   1114                'vec4(res, 0.0, 0.0, 1.0)';
   1115        }
   1116 
   1117        this.m_fragmentSrc = tcuStringTemplate.specialize(fragmentTmpl, fragmentParams);
   1118 
   1119        // Texture size matches viewport and nearest sampling is used. Thus texture sampling
   1120        // is equal to just interpolating the texture value range.
   1121 
   1122        // Determine value range for texture.
   1123 
   1124        switch (this.m_precision) {
   1125            case gluShaderUtil.precision.PRECISION_HIGHP:
   1126                this.m_texValueMin = [-97., 0.2, 71., 74.];
   1127                this.m_texValueMax = [-13.2, -77., 44., 76.];
   1128                break;
   1129 
   1130            case gluShaderUtil.precision.PRECISION_MEDIUMP:
   1131                this.m_texValueMin = [-37.0, 47., -7., 0.0];
   1132                this.m_texValueMax = [-1.0, 12., 7., 19.];
   1133                break;
   1134 
   1135            case gluShaderUtil.precision.PRECISION_LOWP:
   1136                this.m_texValueMin = [0.0, -1.0, 0.0, 1.0];
   1137                this.m_texValueMax = [1.0, 1.0, -1.0, -1.0];
   1138                break;
   1139 
   1140            default:
   1141                throw new Error(false, 'Precision not supported:' + this.m_precision);
   1142        }
   1143 
   1144        // Lowp and mediump cases use RGBA16F format, while highp uses RGBA32F.
   1145        viewportSize = this.getViewportSize();
   1146        assertMsgOptions(!this.m_texture, 'Texture not null', false, true);
   1147        this.m_texture = gluTexture.texture2DFromInternalFormat(gl, this.m_precision === gluShaderUtil.precision.PRECISION_HIGHP ? gl.RGBA32F : gl.RGBA16F, viewportSize[0], viewportSize[1]);
   1148        this.m_texture.getRefTexture().allocLevel(0);
   1149 
   1150        // Texture coordinates
   1151        this.m_coordMin = [0.0, 0.0, 0.0, 0.0];
   1152        this.m_coordMax = [1.0, 1.0, 1.0, 1.0];
   1153 
   1154        // Fill with gradients.
   1155        /** @type {tcuTexture.PixelBufferAccess} */ var level0 = this.m_texture.getRefTexture().getLevel(0);
   1156        for (var y = 0; y < level0.getHeight(); y++) {
   1157            for (var x = 0; x < level0.getWidth(); x++) {
   1158                /** @type {number} */ var xf = (x + 0.5) / level0.getWidth();
   1159                /** @type {number} */ var yf = (y + 0.5) / level0.getHeight();
   1160                /** @type {Array<number>} */ var s = [xf, yf, (xf + yf) / 2.0, 1.0 - (xf + yf) / 2.0];
   1161 
   1162                level0.setPixel(deMath.add(this.m_texValueMin, deMath.multiply(deMath.subtract(this.m_texValueMax, this.m_texValueMin), s)), x, y);
   1163            }
   1164        }
   1165 
   1166        this.m_texture.upload();
   1167 
   1168        if (this.m_surfaceType === es3fShaderDerivateTests.SurfaceType.FLOAT_FBO) {
   1169            // No scale or bias used for accuracy.
   1170            this.m_derivScale = [1.0, 1.0, 1.0, 1.0];
   1171            this.m_derivBias = [0.0, 0.0, 0.0, 0.0];
   1172        } else {
   1173            // Compute scale - bias that normalizes to 0..1 range.
   1174            viewportSize = this.getViewportSize();
   1175            /** @type {number} */ var w = viewportSize[0];
   1176            /** @type {number} */ var h = viewportSize[1];
   1177            /** @type {Array<number>} */ var dx = deMath.divide(deMath.subtract(this.m_texValueMax, this.m_texValueMin), [w, w, w * 0.5, -w * 0.5]);
   1178            /** @type {Array<number>} */ var dy = deMath.divide(deMath.subtract(this.m_texValueMax, this.m_texValueMin), [h, h, h * 0.5, -h * 0.5]);
   1179 
   1180            switch (this.m_func) {
   1181                case es3fShaderDerivateTests.DerivateFunc.DFDX:
   1182                    this.m_derivScale = deMath.divide([0.5, 0.5, 0.5, 0.5], dx);
   1183                    break;
   1184 
   1185                case es3fShaderDerivateTests.DerivateFunc.DFDY:
   1186                    this.m_derivScale = deMath.divide([0.5, 0.5, 0.5, 0.5], dy);
   1187                    break;
   1188 
   1189                case es3fShaderDerivateTests.DerivateFunc.FWIDTH:
   1190                    this.m_derivScale = deMath.divide([0.5, 0.5, 0.5, 0.5], deMath.add(deMath.abs(dx), deMath.abs(dy)));
   1191                    break;
   1192 
   1193                default:
   1194                    throw new Error('Derivate Function not supported: ' + this.m_func);
   1195            }
   1196 
   1197            this.m_derivBias = [0.0, 0.0, 0.0, 0.0];
   1198        }
   1199    };
   1200 
   1201    /**
   1202     * @param {WebGLProgram} program
   1203     */
   1204    es3fShaderDerivateTests.TextureDerivateCase.prototype.setupRenderState = function(program) {
   1205        /** @type {number} */ var texUnit = 1;
   1206 
   1207        gl.activeTexture(gl.TEXTURE0 + texUnit);
   1208        gl.bindTexture(gl.TEXTURE_2D, this.m_texture.getGLTexture());
   1209        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
   1210        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
   1211        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
   1212        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
   1213 
   1214        gl.uniform1i(gl.getUniformLocation(program, 'u_sampler'), texUnit);
   1215    };
   1216 
   1217    /**
   1218     * @param {tcuTexture.PixelBufferAccess} result
   1219     * @param {tcuTexture.PixelBufferAccess} errorMask
   1220     * @return {boolean}
   1221     */
   1222    es3fShaderDerivateTests.TextureDerivateCase.prototype.verify = function(result, errorMask) {
   1223        // \note Edges are ignored in comparison
   1224        if (result.getWidth() < 2 || result.getHeight() < 2)
   1225            throw new Error('Too small viewport');
   1226 
   1227        /** @type {tcuTexture.PixelBufferAccess} */ var compareArea = tcuTextureUtil.getSubregion(result, 1, 1, 0, result.getWidth() - 2, result.getHeight() - 2, 1);
   1228        /** @type {tcuTexture.PixelBufferAccess} */ var maskArea = tcuTextureUtil.getSubregion(errorMask, 1, 1, 0, errorMask.getWidth() - 2, errorMask.getHeight() - 2, 1);
   1229        /** @type {Array<number>} */ var xScale = [1.0, 0.0, 0.5, -0.5];
   1230        /** @type {Array<number>} */ var yScale = [0.0, 1.0, 0.5, -0.5];
   1231        /** @type {number} */ var w = result.getWidth();
   1232        /** @type {number} */ var h = result.getHeight();
   1233 
   1234        /** @type {Array<number>} */ var surfaceThreshold = deMath.divide(this.getSurfaceThreshold(), deMath.abs(this.m_derivScale));
   1235        /** @type {Array<number>} */ var reference;
   1236        /** @type {Array<number>} */ var threshold;
   1237        if (this.m_func == es3fShaderDerivateTests.DerivateFunc.DFDX || this.m_func == es3fShaderDerivateTests.DerivateFunc.DFDY) {
   1238            /** @type {boolean} */ var isX = this.m_func == es3fShaderDerivateTests.DerivateFunc.DFDX;
   1239            /** @type {number} */ var div = isX ? w : h;
   1240            /** @type {Array<number>} */ var scale = isX ? xScale : yScale;
   1241            reference = deMath.multiply(deMath.scale(deMath.subtract(this.m_texValueMax, this.m_texValueMin), 1 / div), scale);
   1242            /** @type {Array<number>} */ var opThreshold = es3fShaderDerivateTests.getDerivateThreshold(this.m_precision, deMath.multiply(this.m_texValueMin, scale), deMath.multiply(this.m_texValueMax, scale), reference);
   1243            threshold = deMath.max(surfaceThreshold, opThreshold);
   1244 
   1245            bufferedLogToConsole('Verifying result image.\n'+
   1246                '\tValid derivative is ' + reference + ' with threshold ' + threshold);
   1247 
   1248            // short circuit if result is strictly within the normal value error bounds.
   1249            // This improves performance significantly.
   1250            if (es3fShaderDerivateTests.verifyConstantDerivate(compareArea, maskArea, this.m_dataType,
   1251                reference, threshold, this.m_derivScale, this.m_derivBias,
   1252                es3fShaderDerivateTests.VerificationLogging.LOG_NOTHING)) {
   1253                    bufferedLogToConsole('No incorrect derivatives found, result valid.');
   1254                    return true;
   1255            }
   1256            // some pixels exceed error bounds calculated for normal values. Verify that these
   1257            // potentially invalid pixels are in fact valid due to (for example) subnorm flushing.
   1258 
   1259            bufferedLogToConsole('Initial verification failed, verifying image by calculating accurate error bounds for each result pixel.\n' +
   1260                '\tVerifying each result derivative is within its range of legal result values.');
   1261 
   1262            /** @type {Array<number>} */ var valueRamp = deMath.subtract(this.m_texValueMax, this.m_texValueMin);
   1263            /** @type {es3fShaderDerivateTests.Linear2DFunctionEvaluator} */ var function_ = new es3fShaderDerivateTests.Linear2DFunctionEvaluator();
   1264 
   1265            function_.matrix.setRow(0, [valueRamp[0] / w, 0.0, this.m_texValueMin[0]]);
   1266            function_.matrix.setRow(1, [0.0, valueRamp[1] / h, this.m_texValueMin[1]]);
   1267            function_.matrix.setRow(2, deMath.scale([valueRamp[2] / w, valueRamp[2] / h, this.m_texValueMin[2] + this.m_texValueMin[2]], 1 / 2.0));
   1268            function_.matrix.setRow(3, deMath.scale([-valueRamp[3] / w, -valueRamp[3] / h, this.m_texValueMax[3] + this.m_texValueMax[3]], 1 / 2.0));
   1269 
   1270            return es3fShaderDerivateTests.reverifyConstantDerivateWithFlushRelaxations(compareArea, maskArea, this.m_dataType, this.m_precision,
   1271                this.m_derivScale, this.m_derivBias, surfaceThreshold, this.m_func, function_);
   1272        } else {
   1273            assertMsgOptions(this.m_func == es3fShaderDerivateTests.DerivateFunc.FWIDTH, 'Expected Derivate Function FWIDTH', false, true);
   1274            /** @type {Array<number>} */ var dx = deMath.multiply(deMath.scale(deMath.subtract(this.m_texValueMax, this.m_texValueMin), 1 / w), xScale);
   1275            /** @type {Array<number>} */ var dy = deMath.multiply(deMath.scale(deMath.subtract(this.m_texValueMax, this.m_texValueMin), 1 / h), yScale);
   1276            reference = deMath.add(deMath.abs(dx), deMath.abs(dy));
   1277            /** @type {Array<number>} */ var dxThreshold = es3fShaderDerivateTests.getDerivateThreshold(this.m_precision, deMath.multiply(this.m_texValueMin, xScale), deMath.multiply(this.m_texValueMax, xScale), dx);
   1278            /** @type {Array<number>} */ var dyThreshold = es3fShaderDerivateTests.getDerivateThreshold(this.m_precision, deMath.multiply(this.m_texValueMin, yScale), deMath.multiply(this.m_texValueMax, yScale), dy);
   1279            threshold = deMath.max(surfaceThreshold, deMath.max(dxThreshold, dyThreshold));
   1280 
   1281            return es3fShaderDerivateTests.verifyConstantDerivate(compareArea, maskArea, this.m_dataType,
   1282                reference, threshold, this.m_derivScale, this.m_derivBias);
   1283        };
   1284    };
   1285 
   1286    /**
   1287     * @constructor
   1288     * @extends {tcuTestCase.DeqpTest}
   1289     */
   1290    es3fShaderDerivateTests.ShaderDerivateTests = function() {
   1291        tcuTestCase.DeqpTest.call(this, 'derivate', 'Derivate Function Tests');
   1292    };
   1293 
   1294    es3fShaderDerivateTests.ShaderDerivateTests.prototype = Object.create(tcuTestCase.DeqpTest.prototype);
   1295    es3fShaderDerivateTests.ShaderDerivateTests.prototype.constructor = es3fShaderDerivateTests.ShaderDerivateTests
   1296 
   1297    /**
   1298     * @struct
   1299     * @constructor
   1300     * @param {string} name
   1301     * @param {es3fShaderDerivateTests.DerivateFunc} func
   1302     * @param {gluShaderUtil.DataType} dataType_
   1303     * @param {gluShaderUtil.precision} precision_
   1304     */
   1305    es3fShaderDerivateTests.FunctionSpec = function(name, func, dataType_, precision_) {
   1306        this.name = name;
   1307        this.function_ = func;
   1308        this.dataType = dataType_;
   1309        this.precision = precision_;
   1310    };
   1311 
   1312    es3fShaderDerivateTests.ShaderDerivateTests.prototype.init = function() {
   1313        var testGroup = tcuTestCase.runner.testCases;
   1314        /**
   1315         * @struct
   1316         * @constructor
   1317         * @param {string} name
   1318         * @param {string} description
   1319         * @param {string} source
   1320         */
   1321        var LinearDerivateCase = function(name, description, source) {
   1322            /** @type {string} */ this.name = name;
   1323            /** @type {string} */ this.description = description;
   1324            /** @type {string} */ this.source = source;
   1325        };
   1326 
   1327        /** @type {Array<LinearDerivateCase>} */
   1328        var s_linearDerivateCases = [
   1329            new LinearDerivateCase(
   1330                'linear',
   1331                'Basic derivate of linearly interpolated argument',
   1332                '#version 300 es\n' +
   1333                'in ${PRECISION} ${DATATYPE} v_coord;\n' +
   1334                'layout(location = 0) out ${OUTPUT_PREC} ${OUTPUT_TYPE} o_color;\n' +
   1335                'uniform ${PRECISION} ${DATATYPE} u_scale;\n' +
   1336                'uniform ${PRECISION} ${DATATYPE} u_bias;\n' +
   1337                'void main (void)\n' +
   1338                '{\n' +
   1339                ' ${PRECISION} ${DATATYPE} res = ${FUNC}(v_coord) * u_scale + u_bias;\n' +
   1340                ' o_color = ${CAST_TO_OUTPUT};\n' +
   1341                '}\n'),
   1342            new LinearDerivateCase(
   1343                'in_function',
   1344                'Derivate of linear function argument',
   1345                '#version 300 es\n' +
   1346                'in ${PRECISION} ${DATATYPE} v_coord;\n' +
   1347                'layout(location = 0) out ${OUTPUT_PREC} ${OUTPUT_TYPE} o_color;\n' +
   1348                'uniform ${PRECISION} ${DATATYPE} u_scale;\n' +
   1349                'uniform ${PRECISION} ${DATATYPE} u_bias;\n' +
   1350                '\n' +
   1351                '${PRECISION} ${DATATYPE} computeRes (${PRECISION} ${DATATYPE} value)\n' +
   1352                '{\n' +
   1353                ' return ${FUNC}(v_coord) * u_scale + u_bias;\n' +
   1354                '}\n' +
   1355                '\n' +
   1356                'void main (void)\n' +
   1357                '{\n' +
   1358                ' ${PRECISION} ${DATATYPE} res = computeRes(v_coord);\n' +
   1359                ' o_color = ${CAST_TO_OUTPUT};\n' +
   1360                '}\n'),
   1361            new LinearDerivateCase(
   1362                'static_if',
   1363                'Derivate of linearly interpolated value in static if',
   1364                '#version 300 es\n' +
   1365                'in ${PRECISION} ${DATATYPE} v_coord;\n' +
   1366                'layout(location = 0) out ${OUTPUT_PREC} ${OUTPUT_TYPE} o_color;\n' +
   1367                'uniform ${PRECISION} ${DATATYPE} u_scale;\n' +
   1368                'uniform ${PRECISION} ${DATATYPE} u_bias;\n' +
   1369                'void main (void)\n' +
   1370                '{\n' +
   1371                ' ${PRECISION} ${DATATYPE} res;\n' +
   1372                ' if (false)\n' +
   1373                ' res = ${FUNC}(-v_coord) * u_scale + u_bias;\n' +
   1374                ' else\n' +
   1375                ' res = ${FUNC}(v_coord) * u_scale + u_bias;\n' +
   1376                ' o_color = ${CAST_TO_OUTPUT};\n' +
   1377                '}\n'),
   1378            new LinearDerivateCase(
   1379                'static_loop',
   1380                'Derivate of linearly interpolated value in static loop',
   1381                '#version 300 es\n' +
   1382                'in ${PRECISION} ${DATATYPE} v_coord;\n' +
   1383                'layout(location = 0) out ${OUTPUT_PREC} ${OUTPUT_TYPE} o_color;\n' +
   1384                'uniform ${PRECISION} ${DATATYPE} u_scale;\n' +
   1385                'uniform ${PRECISION} ${DATATYPE} u_bias;\n' +
   1386                'void main (void)\n' +
   1387                '{\n' +
   1388                ' ${PRECISION} ${DATATYPE} res = ${DATATYPE}(0.0);\n' +
   1389                ' for (int i = 0; i < 2; i++)\n' +
   1390                ' res += ${FUNC}(v_coord * float(i));\n' +
   1391                ' res = res * u_scale + u_bias;\n' +
   1392                ' o_color = ${CAST_TO_OUTPUT};\n' +
   1393                '}\n'),
   1394            new LinearDerivateCase(
   1395                'static_switch',
   1396                'Derivate of linearly interpolated value in static switch',
   1397                '#version 300 es\n' +
   1398                'in ${PRECISION} ${DATATYPE} v_coord;\n' +
   1399                'layout(location = 0) out ${OUTPUT_PREC} ${OUTPUT_TYPE} o_color;\n' +
   1400                'uniform ${PRECISION} ${DATATYPE} u_scale;\n' +
   1401                'uniform ${PRECISION} ${DATATYPE} u_bias;\n' +
   1402                'void main (void)\n' +
   1403                '{\n' +
   1404                ' ${PRECISION} ${DATATYPE} res;\n' +
   1405                ' switch (1)\n' +
   1406                ' {\n' +
   1407                ' case 0: res = ${FUNC}(-v_coord) * u_scale + u_bias; break;\n' +
   1408                ' case 1: res = ${FUNC}(v_coord) * u_scale + u_bias; break;\n' +
   1409                ' }\n' +
   1410                ' o_color = ${CAST_TO_OUTPUT};\n' +
   1411                '}\n'),
   1412            new LinearDerivateCase(
   1413                'uniform_if',
   1414                'Derivate of linearly interpolated value in uniform if',
   1415                '#version 300 es\n' +
   1416                'in ${PRECISION} ${DATATYPE} v_coord;\n' +
   1417                'layout(location = 0) out ${OUTPUT_PREC} ${OUTPUT_TYPE} o_color;\n' +
   1418                'uniform ${PRECISION} ${DATATYPE} u_scale;\n' +
   1419                'uniform ${PRECISION} ${DATATYPE} u_bias;\n' +
   1420                'uniform bool ub_true;\n' +
   1421                'void main (void)\n' +
   1422                '{\n' +
   1423                ' ${PRECISION} ${DATATYPE} res;\n' +
   1424                ' if (ub_true)\n' +
   1425                ' res = ${FUNC}(v_coord) * u_scale + u_bias;\n' +
   1426                ' else\n' +
   1427                ' res = ${FUNC}(-v_coord) * u_scale + u_bias;\n' +
   1428                ' o_color = ${CAST_TO_OUTPUT};\n' +
   1429                '}\n'),
   1430            new LinearDerivateCase(
   1431                'uniform_loop',
   1432                'Derivate of linearly interpolated value in uniform loop',
   1433                '#version 300 es\n' +
   1434                'in ${PRECISION} ${DATATYPE} v_coord;\n' +
   1435                'layout(location = 0) out ${OUTPUT_PREC} ${OUTPUT_TYPE} o_color;\n' +
   1436                'uniform ${PRECISION} ${DATATYPE} u_scale;\n' +
   1437                'uniform ${PRECISION} ${DATATYPE} u_bias;\n' +
   1438                'uniform int ui_two;\n' +
   1439                'void main (void)\n' +
   1440                '{\n' +
   1441                ' ${PRECISION} ${DATATYPE} res = ${DATATYPE}(0.0);\n' +
   1442                ' for (int i = 0; i < ui_two; i++)\n' +
   1443                ' res += ${FUNC}(v_coord * float(i));\n' +
   1444                ' res = res * u_scale + u_bias;\n' +
   1445                ' o_color = ${CAST_TO_OUTPUT};\n' +
   1446                '}\n'),
   1447            new LinearDerivateCase(
   1448                'uniform_switch',
   1449                'Derivate of linearly interpolated value in uniform switch',
   1450                '#version 300 es\n' +
   1451                'in ${PRECISION} ${DATATYPE} v_coord;\n' +
   1452                'layout(location = 0) out ${OUTPUT_PREC} ${OUTPUT_TYPE} o_color;\n' +
   1453                'uniform ${PRECISION} ${DATATYPE} u_scale;\n' +
   1454                'uniform ${PRECISION} ${DATATYPE} u_bias;\n' +
   1455                'uniform int ui_one;\n' +
   1456                'void main (void)\n' +
   1457                '{\n' +
   1458                ' ${PRECISION} ${DATATYPE} res;\n' +
   1459                ' switch (ui_one)\n' +
   1460                ' {\n' +
   1461                ' case 0: res = ${FUNC}(-v_coord) * u_scale + u_bias; break;\n' +
   1462                ' case 1: res = ${FUNC}(v_coord) * u_scale + u_bias; break;\n' +
   1463                ' }\n' +
   1464                ' o_color = ${CAST_TO_OUTPUT};\n' +
   1465                '}\n')
   1466        ];
   1467 
   1468        /**
   1469         * @struct
   1470         * @constructor
   1471         * @param {string} name
   1472         * @param {es3fShaderDerivateTests.SurfaceType} surfaceType
   1473         * @param {number} numSamples
   1474         */
   1475        var FboConfig = function(name, surfaceType, numSamples) {
   1476            /** @type {string} */ this.name = name;
   1477            /** @type {es3fShaderDerivateTests.SurfaceType} */ this.surfaceType = surfaceType;
   1478            /** @type {number} */ this.numSamples = numSamples;
   1479        };
   1480 
   1481        /** @type {Array<FboConfig>} */ var s_fboConfigs = [
   1482            new FboConfig('fbo', es3fShaderDerivateTests.SurfaceType.DEFAULT_FRAMEBUFFER, 0),
   1483            new FboConfig('fbo_msaa2', es3fShaderDerivateTests.SurfaceType.UNORM_FBO, 2),
   1484            new FboConfig('fbo_msaa4', es3fShaderDerivateTests.SurfaceType.UNORM_FBO, 4),
   1485            new FboConfig('fbo_float', es3fShaderDerivateTests.SurfaceType.FLOAT_FBO, 0)
   1486        ];
   1487 
   1488        /**
   1489         * @struct
   1490         * @constructor
   1491         * @param {string} name
   1492         * @param {number} hint
   1493         */
   1494        var Hint = function(name, hint) {
   1495            /** @type {string} */ this.name = name;
   1496            /** @type {number} */ this.hint = hint;
   1497        };
   1498 
   1499        /** @type {Array<Hint>} */ var s_hints = [
   1500            new Hint('fastest', gl.FASTEST),
   1501            new Hint('nicest', gl.NICEST)
   1502        ];
   1503 
   1504        /**
   1505         * @struct
   1506         * @constructor
   1507         * @param {string} name
   1508         * @param {es3fShaderDerivateTests.SurfaceType} surfaceType
   1509         * @param {number} numSamples
   1510         */
   1511        var HintFboConfig = function(name, surfaceType, numSamples) {
   1512            /** @type {string} */ this.name = name;
   1513            /** @type {es3fShaderDerivateTests.SurfaceType} */ this.surfaceType = surfaceType;
   1514            /** @type {number} */ this.numSamples = numSamples;
   1515        };
   1516 
   1517        /** @type {Array<HintFboConfig>} */ var s_hintFboConfigs = [
   1518            new HintFboConfig('default', es3fShaderDerivateTests.SurfaceType.DEFAULT_FRAMEBUFFER, 0),
   1519            new HintFboConfig('fbo_msaa4', es3fShaderDerivateTests.SurfaceType.UNORM_FBO, 4),
   1520            new HintFboConfig('fbo_float', es3fShaderDerivateTests.SurfaceType.FLOAT_FBO, 0)
   1521        ];
   1522 
   1523        /**
   1524         * @struct
   1525         * @constructor
   1526         * @param {string} name
   1527         * @param {es3fShaderDerivateTests.SurfaceType} surfaceType
   1528         * @param {number} numSamples
   1529         * @param {number} hint
   1530         */
   1531        var TextureConfig = function(name, surfaceType, numSamples, hint) {
   1532            /** @type {string} */ this.name = name;
   1533            /** @type {es3fShaderDerivateTests.SurfaceType} */ this.surfaceType = surfaceType;
   1534            /** @type {number} */ this.numSamples = numSamples;
   1535            /** @type {number} */ this.hint = hint;
   1536        };
   1537 
   1538        /** @type {Array<TextureConfig>} */ var s_textureConfigs = [
   1539            new TextureConfig('basic', es3fShaderDerivateTests.SurfaceType.DEFAULT_FRAMEBUFFER, 0, gl.DONT_CARE),
   1540            new TextureConfig('msaa4', es3fShaderDerivateTests.SurfaceType.UNORM_FBO, 4, gl.DONT_CARE),
   1541            new TextureConfig('float_fastest', es3fShaderDerivateTests.SurfaceType.FLOAT_FBO, 0, gl.FASTEST),
   1542            new TextureConfig('float_nicest', es3fShaderDerivateTests.SurfaceType.FLOAT_FBO, 0, gl.NICEST)
   1543        ];
   1544 
   1545        /** @type {gluShaderUtil.DataType} */ var dataType;
   1546        /** @type {string} */ var source;
   1547        /** @type {gluShaderUtil.precision} */ var precision;
   1548        /** @type {es3fShaderDerivateTests.SurfaceType} */ var surfaceType;
   1549        /** @type {number} */ var numSamples;
   1550        /** @type {number} */ var hint;
   1551        /** @type {string} */ var caseName;
   1552        /** @type {tcuTestCase.DeqpTest} */ var fboGroup;
   1553 
   1554        // .dfdx, .dfdy, .fwidth
   1555        for (var funcNdx in es3fShaderDerivateTests.DerivateFunc) {
   1556            /** @type {es3fShaderDerivateTests.DerivateFunc} */ var function_ = es3fShaderDerivateTests.DerivateFunc[funcNdx];
   1557            /** @type {tcuTestCase.DeqpTest} */ var functionGroup = tcuTestCase.newTest(es3fShaderDerivateTests.getDerivateFuncCaseName(function_), es3fShaderDerivateTests.getDerivateFuncName(function_));
   1558            testGroup.addChild(functionGroup);
   1559 
   1560            // .constant - no precision variants, checks that derivate of constant arguments is 0
   1561            /** @type {tcuTestCase.DeqpTest} */ var constantGroup = tcuTestCase.newTest('constant', 'Derivate of constant argument');
   1562            functionGroup.addChild(constantGroup);
   1563 
   1564            for (var vecSize = 1; vecSize <= 4; vecSize++) {
   1565                dataType = vecSize > 1 ? gluShaderUtil.getDataTypeFloatVec(vecSize) : gluShaderUtil.DataType.FLOAT;
   1566                constantGroup.addChild(new es3fShaderDerivateTests.ConstantDerivateCase(gluShaderUtil.getDataTypeName(dataType), '', function_, dataType));
   1567            }
   1568 
   1569            // Cases based on LinearDerivateCase
   1570            for (var caseNdx = 0; caseNdx < s_linearDerivateCases.length; caseNdx++) {
   1571                /** @type {tcuTestCase.DeqpTest} */ var linearCaseGroup = tcuTestCase.newTest(s_linearDerivateCases[caseNdx].name, s_linearDerivateCases[caseNdx].description);
   1572                source = s_linearDerivateCases[caseNdx].source;
   1573                functionGroup.addChild(linearCaseGroup);
   1574 
   1575                for (var vecSize = 1; vecSize <= 4; vecSize++)
   1576                for (var precNdx in gluShaderUtil.precision) {
   1577                    dataType = vecSize > 1 ? gluShaderUtil.getDataTypeFloatVec(vecSize) : gluShaderUtil.DataType.FLOAT;
   1578                    precision = gluShaderUtil.precision[precNdx];
   1579                    surfaceType = es3fShaderDerivateTests.SurfaceType.DEFAULT_FRAMEBUFFER;
   1580                    numSamples = 0;
   1581                    hint = gl.DONT_CARE;
   1582 
   1583                    if (caseNdx !== 0 && precision === gluShaderUtil.precision.PRECISION_LOWP)
   1584                        continue; // Skip as lowp doesn't actually produce any bits when rendered to default FB.
   1585 
   1586                    caseName = gluShaderUtil.getDataTypeName(dataType) + '_' + gluShaderUtil.getPrecisionName(precision);
   1587 
   1588                    linearCaseGroup.addChild(new es3fShaderDerivateTests.LinearDerivateCase(caseName, '', function_, dataType, precision, hint, surfaceType, numSamples, source));
   1589                }
   1590            }
   1591 
   1592            // Fbo cases
   1593            for (var caseNdx = 0; caseNdx < s_fboConfigs.length; caseNdx++) {
   1594                fboGroup = tcuTestCase.newTest(s_fboConfigs[caseNdx].name, 'Derivate usage when rendering into FBO');
   1595                source = s_linearDerivateCases[0].source; // use source from .linear group
   1596                surfaceType = s_fboConfigs[caseNdx].surfaceType;
   1597                numSamples = s_fboConfigs[caseNdx].numSamples;
   1598                functionGroup.addChild(fboGroup);
   1599 
   1600                for (var vecSize = 1; vecSize <= 4; vecSize++)
   1601                for (var precNdx in gluShaderUtil.precision) {
   1602                    dataType = vecSize > 1 ? gluShaderUtil.getDataTypeFloatVec(vecSize) : gluShaderUtil.DataType.FLOAT;
   1603                    precision = gluShaderUtil.precision[precNdx];
   1604                    hint = gl.DONT_CARE;
   1605 
   1606                    if (surfaceType !== es3fShaderDerivateTests.SurfaceType.FLOAT_FBO && precision === gluShaderUtil.precision.PRECISION_LOWP)
   1607                        continue; // Skip as lowp doesn't actually produce any bits when rendered to U8 RT.
   1608 
   1609                    caseName = gluShaderUtil.getDataTypeName(dataType) + '_' + gluShaderUtil.getPrecisionName(precision);
   1610 
   1611                    fboGroup.addChild(new es3fShaderDerivateTests.LinearDerivateCase(caseName, '', function_, dataType, precision, hint, surfaceType, numSamples, source));
   1612                }
   1613            }
   1614 
   1615            // .fastest, .nicest
   1616            for (var hintCaseNdx = 0; hintCaseNdx < s_hints.length; hintCaseNdx++) {
   1617                /** @type {tcuTestCase.DeqpTest} */ var hintGroup = tcuTestCase.newTest(s_hints[hintCaseNdx].name, 'Shader derivate hints');
   1618                source = s_linearDerivateCases[0].source; // use source from .linear group
   1619                hint = s_hints[hintCaseNdx].hint;
   1620                functionGroup.addChild(hintGroup);
   1621 
   1622                for (var fboCaseNdx = 0; fboCaseNdx < s_hintFboConfigs.length; fboCaseNdx++) {
   1623                    fboGroup = tcuTestCase.newTest(s_hintFboConfigs[fboCaseNdx].name, '');
   1624                    surfaceType = s_hintFboConfigs[fboCaseNdx].surfaceType;
   1625                    numSamples = s_hintFboConfigs[fboCaseNdx].numSamples;
   1626                    hintGroup.addChild(fboGroup);
   1627 
   1628                    for (var vecSize = 1; vecSize <= 4; vecSize++)
   1629                    for (var precNdx in gluShaderUtil.precision) {
   1630                        dataType = vecSize > 1 ? gluShaderUtil.getDataTypeFloatVec(vecSize) : gluShaderUtil.DataType.FLOAT;
   1631                        precision = gluShaderUtil.precision[precNdx];
   1632 
   1633                        if (surfaceType !== es3fShaderDerivateTests.SurfaceType.FLOAT_FBO && precision === gluShaderUtil.precision.PRECISION_LOWP)
   1634                            continue; // Skip as lowp doesn't actually produce any bits when rendered to U8 RT.
   1635 
   1636                        caseName = gluShaderUtil.getDataTypeName(dataType) + '_' + gluShaderUtil.getPrecisionName(precision);
   1637 
   1638                        fboGroup.addChild(new es3fShaderDerivateTests.LinearDerivateCase(caseName, '', function_, dataType, precision, hint, surfaceType, numSamples, source));
   1639                    }
   1640                }
   1641            }
   1642 
   1643            // .texture
   1644            /** @type {tcuTestCase.DeqpTest} */ var textureGroup = tcuTestCase.newTest('texture', 'Derivate of texture lookup result');
   1645            functionGroup.addChild(textureGroup);
   1646 
   1647            for (var texCaseNdx = 0; texCaseNdx < s_textureConfigs.length; texCaseNdx++) {
   1648                /** @type {tcuTestCase.DeqpTest} */ var caseGroup = tcuTestCase.newTest(s_textureConfigs[texCaseNdx].name, '');
   1649                surfaceType = s_textureConfigs[texCaseNdx].surfaceType;
   1650                numSamples = s_textureConfigs[texCaseNdx].numSamples;
   1651                hint = s_textureConfigs[texCaseNdx].hint;
   1652                textureGroup.addChild(caseGroup);
   1653 
   1654                for (var vecSize = 1; vecSize <= 4; vecSize++)
   1655                for (var precNdx in gluShaderUtil.precision) {
   1656                    dataType = vecSize > 1 ? gluShaderUtil.getDataTypeFloatVec(vecSize) : gluShaderUtil.DataType.FLOAT;
   1657                    precision = gluShaderUtil.precision[precNdx];
   1658 
   1659                    if (surfaceType !== es3fShaderDerivateTests.SurfaceType.FLOAT_FBO && precision === gluShaderUtil.precision.PRECISION_LOWP)
   1660                        continue; // Skip as lowp doesn't actually produce any bits when rendered to U8 RT.
   1661 
   1662                    caseName = gluShaderUtil.getDataTypeName(dataType) + '_' + gluShaderUtil.getPrecisionName(precision);
   1663 
   1664                    caseGroup.addChild(new es3fShaderDerivateTests.TextureDerivateCase(caseName, '', function_, dataType, precision, hint, surfaceType, numSamples));
   1665                }
   1666            }
   1667        }
   1668    };
   1669 
   1670    /**
   1671     * Run test
   1672     * @param {WebGL2RenderingContext} context
   1673     */
   1674    es3fShaderDerivateTests.run = function(context, range) {
   1675        gl = context;
   1676        //Set up Test Root parameters
   1677        var state = tcuTestCase.runner;
   1678        state.setRoot(new es3fShaderDerivateTests.ShaderDerivateTests());
   1679 
   1680        //Set up name and description of this test series.
   1681        setCurrentTestName(state.testCases.fullName());
   1682        description(state.testCases.getDescription());
   1683 
   1684        try {
   1685            if (range)
   1686                state.setRange(range);
   1687            //Run test cases
   1688            tcuTestCase.runTestCases();
   1689        }
   1690        catch (err) {
   1691            testFailedOptions('Failed to es3fShaderDerivateTests.run tests', false);
   1692            tcuTestCase.runner.terminate();
   1693        }
   1694    };
   1695 
   1696 });