tor-browser

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

glsDrawTests.js (148903B)


      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('modules.shared.glsDrawTests');
     23 goog.require('framework.common.tcuFloat');
     24 goog.require('framework.common.tcuImageCompare');
     25 goog.require('framework.common.tcuPixelFormat');
     26 goog.require('framework.common.tcuRGBA');
     27 goog.require('framework.common.tcuSurface');
     28 goog.require('framework.common.tcuTestCase');
     29 goog.require('framework.common.tcuTextureUtil');
     30 goog.require('framework.delibs.debase.deMath');
     31 goog.require('framework.delibs.debase.deRandom');
     32 goog.require('framework.opengl.gluShaderUtil');
     33 goog.require('framework.opengl.gluStrUtil');
     34 goog.require('framework.opengl.simplereference.sglrGLContext');
     35 goog.require('framework.opengl.simplereference.sglrReferenceContext');
     36 goog.require('framework.opengl.simplereference.sglrShaderProgram');
     37 goog.require('framework.referencerenderer.rrFragmentOperations');
     38 goog.require('framework.referencerenderer.rrGenericVector');
     39 goog.require('framework.referencerenderer.rrShadingContext');
     40 goog.require('framework.referencerenderer.rrVertexAttrib');
     41 goog.require('framework.referencerenderer.rrVertexPacket');
     42 
     43 goog.scope(function() {
     44 
     45    var glsDrawTests = modules.shared.glsDrawTests;
     46    var tcuTestCase = framework.common.tcuTestCase;
     47    var tcuRGBA = framework.common.tcuRGBA;
     48    var tcuFloat = framework.common.tcuFloat;
     49    var tcuPixelFormat = framework.common.tcuPixelFormat;
     50    var tcuSurface = framework.common.tcuSurface;
     51    var tcuImageCompare = framework.common.tcuImageCompare;
     52    var tcuTextureUtil = framework.common.tcuTextureUtil;
     53    var gluShaderUtil = framework.opengl.gluShaderUtil;
     54    var gluStrUtil = framework.opengl.gluStrUtil;
     55    var sglrGLContext = framework.opengl.simplereference.sglrGLContext;
     56    var sglrReferenceContext = framework.opengl.simplereference.sglrReferenceContext;
     57    var sglrShaderProgram = framework.opengl.simplereference.sglrShaderProgram;
     58    var deMath = framework.delibs.debase.deMath;
     59    var deRandom = framework.delibs.debase.deRandom;
     60    var rrFragmentOperations = framework.referencerenderer.rrFragmentOperations;
     61    var rrGenericVector = framework.referencerenderer.rrGenericVector;
     62    var rrShadingContext = framework.referencerenderer.rrShadingContext;
     63    var rrVertexAttrib = framework.referencerenderer.rrVertexAttrib;
     64    var rrVertexPacket = framework.referencerenderer.rrVertexPacket;
     65 
     66    /** @const {number} */ glsDrawTests.MAX_RENDER_TARGET_SIZE = 512;
     67 
     68    // Utils
     69 
     70    /**
     71     * @param {glsDrawTests.DrawTestSpec.Target} target
     72     * @return {number}
     73     */
     74    glsDrawTests.targetToGL = function(target) {
     75        assertMsgOptions(target != null, 'Target is null', false, true);
     76 
     77        var targets = [
     78            gl.ELEMENT_ARRAY_BUFFER, // TARGET_ELEMENT_ARRAY = 0,
     79            gl.ARRAY_BUFFER // TARGET_ARRAY,
     80        ];
     81 
     82        return targets[target];
     83    };
     84 
     85    /**
     86     * @param {?glsDrawTests.DrawTestSpec.Usage} usage
     87     * @return {number}
     88     */
     89    glsDrawTests.usageToGL = function(usage) {
     90        assertMsgOptions(usage != null, 'Usage is null', false, true);
     91 
     92        var usages = [
     93            gl.DYNAMIC_DRAW, // USAGE_DYNAMIC_DRAW = 0,
     94            gl.STATIC_DRAW, // USAGE_STATIC_DRAW,
     95            gl.STREAM_DRAW, // USAGE_STREAM_DRAW,
     96 
     97            gl.STREAM_READ, // USAGE_STREAM_READ,
     98            gl.STREAM_COPY, // USAGE_STREAM_COPY,
     99 
    100            gl.STATIC_READ, // USAGE_STATIC_READ,
    101            gl.STATIC_COPY, // USAGE_STATIC_COPY,
    102 
    103            gl.DYNAMIC_READ, // USAGE_DYNAMIC_READ,
    104            gl.DYNAMIC_COPY // USAGE_DYNAMIC_COPY,
    105        ];
    106        assertMsgOptions(usages.length == Object.keys(glsDrawTests.DrawTestSpec.Usage).length,
    107            'Amount of usage gl vlaues is different from amount of usages', false, true);
    108 
    109        return usages[usage];
    110    };
    111 
    112    /**
    113     * @param {?glsDrawTests.DrawTestSpec.InputType} type
    114     * @return {number}
    115     */
    116    glsDrawTests.inputTypeToGL = function(type) {
    117        assertMsgOptions(type != null, 'Input type is null', false, true);
    118 
    119        var types = [
    120            gl.FLOAT, // INPUTTYPE_FLOAT = 0,
    121            gl.BYTE, // INPUTTYPE_BYTE,
    122            gl.SHORT, // INPUTTYPE_SHORT,
    123            gl.UNSIGNED_BYTE, // INPUTTYPE_UNSIGNED_BYTE,
    124            gl.UNSIGNED_SHORT, // INPUTTYPE_UNSIGNED_SHORT,
    125 
    126            gl.INT, // INPUTTYPE_INT,
    127            gl.UNSIGNED_INT, // INPUTTYPE_UNSIGNED_INT,
    128            gl.HALF_FLOAT, // INPUTTYPE_HALF,
    129            gl.UNSIGNED_INT_2_10_10_10_REV, // INPUTTYPE_UNSIGNED_INT_2_10_10_10,
    130            gl.INT_2_10_10_10_REV // INPUTTYPE_INT_2_10_10_10,
    131        ];
    132        assertMsgOptions(types.length == Object.keys(glsDrawTests.DrawTestSpec.InputType).length,
    133            'Amount of gl input types is different from amount of input types', false, true);
    134 
    135        return types[type];
    136    };
    137 
    138    /**
    139     * @param {?glsDrawTests.DrawTestSpec.OutputType} type
    140     * @return {string}
    141     */
    142    glsDrawTests.outputTypeToGLType = function(type) {
    143        assertMsgOptions(type != null, 'Output type is null', false, true);
    144 
    145        var types = [
    146            'float', // OUTPUTTYPE_FLOAT = 0,
    147            'vec2', // OUTPUTTYPE_VEC2,
    148            'vec3', // OUTPUTTYPE_VEC3,
    149            'vec4', // OUTPUTTYPE_VEC4,
    150 
    151            'int', // OUTPUTTYPE_INT,
    152            'uint', // OUTPUTTYPE_UINT,
    153 
    154            'ivec2', // OUTPUTTYPE_IVEC2,
    155            'ivec3', // OUTPUTTYPE_IVEC3,
    156            'ivec4', // OUTPUTTYPE_IVEC4,
    157 
    158            'uvec2', // OUTPUTTYPE_UVEC2,
    159            'uvec3', // OUTPUTTYPE_UVEC3,
    160            'uvec4' // OUTPUTTYPE_UVEC4,
    161        ];
    162        assertMsgOptions(types.length == Object.keys(glsDrawTests.DrawTestSpec.OutputType).length,
    163            'Amount of output type names is different than amount of output types', false, true);
    164 
    165        return types[type];
    166    };
    167 
    168    /**
    169     * @param {?glsDrawTests.DrawTestSpec.Primitive} primitive
    170     * @return {number}
    171     */
    172    glsDrawTests.primitiveToGL = function(primitive) {
    173        var primitives = [
    174            gl.POINTS, // PRIMITIVE_POINTS = 0,
    175            gl.TRIANGLES, // PRIMITIVE_TRIANGLES,
    176            gl.TRIANGLE_FAN, // PRIMITIVE_TRIANGLE_FAN,
    177            gl.TRIANGLE_STRIP, // PRIMITIVE_TRIANGLE_STRIP,
    178            gl.LINES, // PRIMITIVE_LINES
    179            gl.LINE_STRIP, // PRIMITIVE_LINE_STRIP
    180            gl.LINE_LOOP
    181        ];
    182        assertMsgOptions(primitives.length == Object.keys(glsDrawTests.DrawTestSpec.Primitive).length,
    183            'Amount of gl primitive values is different than amount of primitives', false, true);
    184 
    185        return primitives[primitive];
    186    };
    187 
    188    /**
    189     * @param {?glsDrawTests.DrawTestSpec.IndexType} indexType
    190     * @return {number}
    191     */
    192    glsDrawTests.indexTypeToGL = function(indexType) {
    193        var indexTypes = [
    194            gl.UNSIGNED_BYTE, // INDEXTYPE_BYTE = 0,
    195            gl.UNSIGNED_SHORT, // INDEXTYPE_SHORT,
    196            gl.UNSIGNED_INT // INDEXTYPE_INT,
    197        ];
    198        assertMsgOptions(indexTypes.length == Object.keys(glsDrawTests.DrawTestSpec.IndexType).length,
    199            'Amount of gl index types is different than amount of index types', false, true);
    200 
    201        return indexTypes[indexType];
    202    };
    203 
    204    /**
    205     * @param {?glsDrawTests.DrawTestSpec.IndexType} indexType
    206     * @return {?glsDrawTests.DrawTestSpec.InputType}
    207     */
    208    glsDrawTests.indexTypeToInputType = function(indexType) {
    209        var inputTypes = [
    210            glsDrawTests.DrawTestSpec.InputType.UNSIGNED_BYTE, // INDEXTYPE_BYTE = 0,
    211            glsDrawTests.DrawTestSpec.InputType.UNSIGNED_SHORT, // INDEXTYPE_SHORT,
    212            glsDrawTests.DrawTestSpec.InputType.UNSIGNED_INT // INDEXTYPE_INT,
    213        ];
    214        assertMsgOptions(inputTypes.length == Object.keys(glsDrawTests.DrawTestSpec.IndexType).length,
    215            'Amount of relevant input types is different than amount of index types', false, true);
    216 
    217        return inputTypes[indexType];
    218    };
    219 
    220    /**
    221     * @param {?glsDrawTests.DrawTestSpec.InputType} type
    222     * @return {boolean}
    223     */
    224    glsDrawTests.inputTypeIsFloatType = function(type) {
    225        if (type == glsDrawTests.DrawTestSpec.InputType.FLOAT)
    226            return true;
    227        if (type == glsDrawTests.DrawTestSpec.InputType.HALF)
    228            return true;
    229        return false;
    230    };
    231 
    232    /**
    233     * @param {?glsDrawTests.DrawTestSpec.OutputType} type
    234     * @return {boolean}
    235     */
    236    glsDrawTests.outputTypeIsFloatType = function(type) {
    237        if (type == glsDrawTests.DrawTestSpec.OutputType.FLOAT ||
    238            type == glsDrawTests.DrawTestSpec.OutputType.VEC2 ||
    239            type == glsDrawTests.DrawTestSpec.OutputType.VEC3 ||
    240            type == glsDrawTests.DrawTestSpec.OutputType.VEC4)
    241            return true;
    242 
    243        return false;
    244    };
    245 
    246    /**
    247     * @param {?glsDrawTests.DrawTestSpec.OutputType} type
    248     * @return {boolean}
    249     */
    250    glsDrawTests.outputTypeIsIntType = function(type) {
    251        if (type == glsDrawTests.DrawTestSpec.OutputType.INT ||
    252            type == glsDrawTests.DrawTestSpec.OutputType.IVEC2 ||
    253            type == glsDrawTests.DrawTestSpec.OutputType.IVEC3 ||
    254            type == glsDrawTests.DrawTestSpec.OutputType.IVEC4)
    255            return true;
    256 
    257        return false;
    258    };
    259 
    260    /**
    261     * @param {?glsDrawTests.DrawTestSpec.OutputType} type
    262     * @return {boolean}
    263     */
    264    glsDrawTests.outputTypeIsUintType = function(type) {
    265        if (type == glsDrawTests.DrawTestSpec.OutputType.UINT ||
    266            type == glsDrawTests.DrawTestSpec.OutputType.UVEC2 ||
    267            type == glsDrawTests.DrawTestSpec.OutputType.UVEC3 ||
    268            type == glsDrawTests.DrawTestSpec.OutputType.UVEC4)
    269            return true;
    270 
    271        return false;
    272    };
    273 
    274    /**
    275     * @param {?glsDrawTests.DrawTestSpec.Primitive} primitive
    276     * @param {number} primitiveCount
    277     * @return {number}
    278     */
    279    glsDrawTests.getElementCount = function(primitive, primitiveCount) {
    280        switch (primitive) {
    281            case glsDrawTests.DrawTestSpec.Primitive.POINTS: return primitiveCount;
    282            case glsDrawTests.DrawTestSpec.Primitive.TRIANGLES: return primitiveCount * 3;
    283            case glsDrawTests.DrawTestSpec.Primitive.TRIANGLE_FAN: return primitiveCount + 2;
    284            case glsDrawTests.DrawTestSpec.Primitive.TRIANGLE_STRIP: return primitiveCount + 2;
    285            case glsDrawTests.DrawTestSpec.Primitive.LINES: return primitiveCount * 2;
    286            case glsDrawTests.DrawTestSpec.Primitive.LINE_STRIP: return primitiveCount + 1;
    287            case glsDrawTests.DrawTestSpec.Primitive.LINE_LOOP: return (primitiveCount == 1) ? (2) : (primitiveCount);
    288            default:
    289                throw new Error('Invalid primitive');
    290        }
    291    };
    292 
    293    //MethodInfo
    294 
    295    /**
    296     * @typedef {{indexed: boolean, instanced: boolean, ranged: boolean, first: boolean}}
    297     */
    298    glsDrawTests.MethodInfo = {
    299        /** @type {boolean} */ indexed: false,
    300        /** @type {boolean} */ instanced: false,
    301        /** @type {boolean} */ ranged: false,
    302        /** @type {boolean} */ first: false
    303    };
    304 
    305    /**
    306     * @param {?glsDrawTests.DrawTestSpec.DrawMethod} method
    307     * @return {glsDrawTests.MethodInfo}
    308     */
    309    glsDrawTests.getMethodInfo = function(method) {
    310        /** @type {Array<glsDrawTests.MethodInfo>} */ var infos = [{
    311                indexed: false, instanced: false, ranged: false, first: true //!< DRAWMETHOD_DRAWARRAYS,
    312            },{
    313                indexed: false, instanced: true, ranged: false, first: true //!< DRAWMETHOD_DRAWARRAYS_INSTANCED,
    314            },{
    315                indexed: true, instanced: false, ranged: false, first: false //!< DRAWMETHOD_DRAWELEMENTS,
    316            },{
    317                indexed: true, instanced: false, ranged: true, first: false //!< DRAWMETHOD_DRAWELEMENTS_RANGED,
    318            },{
    319                indexed: true, instanced: true, ranged: false, first: false //!< DRAWMETHOD_DRAWELEMENTS_INSTANCED
    320            }
    321        ];
    322 
    323        assertMsgOptions(infos.length == Object.keys(glsDrawTests.DrawTestSpec.DrawMethod).length,
    324            'Number of info names', false, true);
    325        assertMsgOptions(method < infos.length, 'Invalid method', false, true);
    326        return /** @type {glsDrawTests.MethodInfo} */ (infos[method]);
    327    };
    328 
    329    /**
    330     * @param {glsDrawTests.DrawTestSpec} a
    331     * @param {glsDrawTests.DrawTestSpec} b
    332     * @return {boolean}
    333     */
    334    glsDrawTests.checkSpecsShaderCompatible = function(a, b) {
    335        // Only the attributes matter
    336        if (a.attribs.length != b.attribs.length)
    337            return false;
    338 
    339        for (var ndx = 0; ndx < a.attribs.length; ++ndx) {
    340            // Only the output type (== shader input type) matters and the usage in the shader.
    341 
    342            if (a.attribs[ndx].additionalPositionAttribute != b.attribs[ndx].additionalPositionAttribute)
    343                return false;
    344 
    345            // component counts need not to match
    346            if (glsDrawTests.outputTypeIsFloatType(a.attribs[ndx].outputType) && glsDrawTests.outputTypeIsFloatType(b.attribs[ndx].outputType))
    347                continue;
    348            if (glsDrawTests.outputTypeIsIntType(a.attribs[ndx].outputType) && glsDrawTests.outputTypeIsIntType(b.attribs[ndx].outputType))
    349                continue;
    350            if (glsDrawTests.outputTypeIsUintType(a.attribs[ndx].outputType) && glsDrawTests.outputTypeIsUintType(b.attribs[ndx].outputType))
    351                continue;
    352 
    353            return false;
    354        }
    355 
    356        return true;
    357    };
    358 
    359    // generate random vectors in a way that does not depend on argument evaluation order
    360 
    361    /**
    362     * @param {deRandom.Random} random
    363     * @return {Array<number>}
    364     */
    365    glsDrawTests.generateRandomVec4 = function(random) {
    366        /** @type {Array<number>} */ var retVal = [];
    367 
    368        for (var i = 0; i < 4; ++i)
    369            retVal[i] = random.getFloat();
    370 
    371        return retVal;
    372    };
    373 
    374    /**
    375     * @param {deRandom.Random} random
    376     * @return {Array<number>}
    377     */
    378    glsDrawTests.generateRandomIVec4 = function(random) {
    379        /** @type {Array<number>} */ var retVal = [];
    380 
    381        for (var i = 0; i < 4; ++i)
    382            retVal[i] = random.getInt();
    383 
    384        return retVal;
    385    };
    386 
    387    /**
    388     * @param {deRandom.Random} random
    389     * @return {Array<number>}
    390     */
    391    glsDrawTests.generateRandomUVec4 = function(random) {
    392        /** @type {Array<number>} */ var retVal = [];
    393 
    394        for (var i = 0; i < 4; ++i)
    395            retVal[i] = Math.abs(random.getInt());
    396 
    397        return retVal;
    398    };
    399 
    400    //GLValue
    401 
    402    /**
    403     * glsDrawTests.GLValue class
    404     * @constructor
    405     */
    406    glsDrawTests.GLValue = function() {
    407        /** @type {goog.NumberArray} */ this.m_value = [0];
    408        /** @type {?glsDrawTests.DrawTestSpec.InputType} */ this.m_type;
    409    };
    410 
    411    /**
    412     * @param {goog.TypedArray} dst
    413     * @param {glsDrawTests.GLValue} val
    414     */
    415    glsDrawTests.copyGLValueToArray = function(dst, val) {
    416        /** @type {Uint8Array} */ var dst8 = new Uint8Array(dst.buffer).subarray(dst.byteOffset, dst.byteOffset + dst.byteLength);
    417        /** @type {Uint8Array} */ var val8 = new Uint8Array(val.m_value.buffer); // TODO: Fix encapsulation issue
    418        dst8.set(val8);
    419    };
    420 
    421    /**
    422     * @param {goog.TypedArray} dst
    423     * @param {goog.TypedArray} src
    424     */
    425    glsDrawTests.copyArray = function(dst, src) {
    426        /** @type {Uint8Array} */ var dst8 = new Uint8Array(dst.buffer).subarray(dst.byteOffset, dst.byteOffset + dst.byteLength);
    427        /** @type {Uint8Array} */ var src8 = new Uint8Array(src.buffer).subarray(src.byteOffset, src.byteOffset + src.byteLength);
    428        dst8.set(src8);
    429    };
    430 
    431    /**
    432     * typeToTypedArray function. Determines which type of array will store the value, and stores it.
    433     * @param {number} value
    434     * @param {?glsDrawTests.DrawTestSpec.InputType} type
    435     */
    436    glsDrawTests.GLValue.typeToTypedArray = function(value, type) {
    437        var array;
    438 
    439        switch (type) {
    440            case glsDrawTests.DrawTestSpec.InputType.FLOAT:
    441                array = new Float32Array(1);
    442                break;
    443 
    444            case glsDrawTests.DrawTestSpec.InputType.BYTE:
    445                array = new Int8Array(1);
    446                break;
    447            case glsDrawTests.DrawTestSpec.InputType.SHORT:
    448                array = new Int16Array(1);
    449                break;
    450 
    451            case glsDrawTests.DrawTestSpec.InputType.UNSIGNED_BYTE:
    452                array = new Uint8Array(1);
    453                break;
    454            case glsDrawTests.DrawTestSpec.InputType.UNSIGNED_SHORT:
    455                array = new Uint16Array(1);
    456                break;
    457 
    458            case glsDrawTests.DrawTestSpec.InputType.INT:
    459                array = new Int32Array(1);
    460                break;
    461            case glsDrawTests.DrawTestSpec.InputType.UNSIGNED_INT:
    462                array = new Uint32Array(1);
    463                break;
    464            case glsDrawTests.DrawTestSpec.InputType.HALF:
    465                array = new Uint16Array(1);
    466                value = glsDrawTests.GLValue.floatToHalf(value);
    467                break;
    468            case glsDrawTests.DrawTestSpec.InputType.UNSIGNED_INT_2_10_10_10:
    469                array = new Uint32Array(1);
    470                break;
    471            case glsDrawTests.DrawTestSpec.InputType.INT_2_10_10_10:
    472                array = new Int32Array(1);
    473                break;
    474            default:
    475                throw new Error('glsDrawTests.GLValue.typeToTypedArray - Invalid InputType');
    476        }
    477 
    478        array[0] = value;
    479        return array;
    480    };
    481 
    482    /**
    483     * glsDrawTests.GLValue.create
    484     * @param {number} value
    485     * @param {?glsDrawTests.DrawTestSpec.InputType} type
    486     */
    487    glsDrawTests.GLValue.create = function(value, type) {
    488        var v = new glsDrawTests.GLValue();
    489        v.m_value = glsDrawTests.GLValue.typeToTypedArray(value, type);
    490        v.m_type = type;
    491        return v;
    492    };
    493 
    494    /**
    495     * glsDrawTests.GLValue.halfToFloat
    496     * @param {number} value
    497     * @return {number}
    498     */
    499    glsDrawTests.GLValue.halfToFloat = function(value) {
    500        return tcuFloat.halfFloatToNumberNoDenorm(value);
    501    };
    502 
    503    /**
    504     * @param {number} f
    505     * @return {number}
    506     */
    507    glsDrawTests.GLValue.floatToHalf = function(f) {
    508        // No denorm support.
    509        return tcuFloat.numberToHalfFloatNoDenorm(f);
    510    };
    511 
    512    /**
    513     * glsDrawTests.GLValue.getMaxValue
    514     * @param {?glsDrawTests.DrawTestSpec.InputType} type
    515     * @return {glsDrawTests.GLValue}
    516     */
    517    glsDrawTests.GLValue.getMaxValue = function(type) {
    518        var value = 0;
    519 
    520        assertMsgOptions(type >= 0 && type < Object.keys(glsDrawTests.DrawTestSpec.InputType).length,
    521            'Invalid type for GLValue', false, true);
    522 
    523        switch (type) {
    524            case glsDrawTests.DrawTestSpec.InputType.FLOAT:
    525                value = 127;
    526                break;
    527            case glsDrawTests.DrawTestSpec.InputType.BYTE:
    528                value = 127;
    529                break;
    530            case glsDrawTests.DrawTestSpec.InputType.SHORT:
    531                value = 32760;
    532                break;
    533            case glsDrawTests.DrawTestSpec.InputType.UNSIGNED_BYTE:
    534                value = 255;
    535                break;
    536            case glsDrawTests.DrawTestSpec.InputType.UNSIGNED_SHORT:
    537                value = 65530;
    538                break;
    539            case glsDrawTests.DrawTestSpec.InputType.INT:
    540                value = 2147483647;
    541                break;
    542            case glsDrawTests.DrawTestSpec.InputType.UNSIGNED_INT:
    543                value = 4294967295;
    544                break;
    545            case glsDrawTests.DrawTestSpec.InputType.HALF:
    546                value = 256;
    547                break;
    548            default: //For any other valid type, return 0
    549                value = 0;
    550        }
    551 
    552        return glsDrawTests.GLValue.create(value, type);
    553    };
    554 
    555    /**
    556     * glsDrawTests.GLValue.getMinValue
    557     * @param {?glsDrawTests.DrawTestSpec.InputType} type
    558     * @return {glsDrawTests.GLValue}
    559     */
    560    glsDrawTests.GLValue.getMinValue = function(type) {
    561        var value = 0;
    562 
    563        assertMsgOptions(type >= 0 && type < Object.keys(glsDrawTests.DrawTestSpec.InputType).length,
    564            'Invalid type for GLValue', false, true);
    565 
    566        switch (type) {
    567            case glsDrawTests.DrawTestSpec.InputType.FLOAT:
    568                value = -127;
    569                break;
    570            case glsDrawTests.DrawTestSpec.InputType.BYTE:
    571                value = -127;
    572                break;
    573            case glsDrawTests.DrawTestSpec.InputType.SHORT:
    574                value = -32760;
    575                break;
    576            case glsDrawTests.DrawTestSpec.InputType.UNSIGNED_BYTE:
    577                value = 0;
    578                break;
    579            case glsDrawTests.DrawTestSpec.InputType.UNSIGNED_SHORT:
    580                value = 0;
    581                break;
    582            case glsDrawTests.DrawTestSpec.InputType.INT:
    583                value = -2147483647;
    584                break;
    585            case glsDrawTests.DrawTestSpec.InputType.UNSIGNED_INT:
    586                value = 0;
    587                break;
    588            case glsDrawTests.DrawTestSpec.InputType.HALF:
    589                value = -256;
    590                break;
    591            default: //For any other valid type, return 0
    592                value = 0;
    593        }
    594 
    595        return glsDrawTests.GLValue.create(value, type);
    596    };
    597 
    598    /**
    599     * glsDrawTests.GLValue.getRandom
    600     * @param {deRandom.Random} rnd
    601     * @param {glsDrawTests.GLValue} min
    602     * @param {glsDrawTests.GLValue} max
    603     * @return {glsDrawTests.GLValue}
    604     */
    605    glsDrawTests.GLValue.getRandom = function(rnd, min, max) {
    606        assertMsgOptions(min.getType() == max.getType(), 'Min and max types differ', false, true);
    607 
    608        var minv = min.interpret();
    609        var maxv = max.interpret();
    610        var type = min.getType();
    611        var value;
    612 
    613        if (maxv < minv)
    614            return min;
    615 
    616        switch (type) {
    617            case glsDrawTests.DrawTestSpec.InputType.FLOAT:
    618            case glsDrawTests.DrawTestSpec.InputType.HALF: {
    619                return glsDrawTests.GLValue.create(minv + rnd.getFloat() * (maxv - minv), type);
    620                break;
    621            }
    622 
    623            case glsDrawTests.DrawTestSpec.InputType.SHORT:
    624            case glsDrawTests.DrawTestSpec.InputType.UNSIGNED_SHORT:
    625            case glsDrawTests.DrawTestSpec.InputType.BYTE:
    626            case glsDrawTests.DrawTestSpec.InputType.UNSIGNED_BYTE:
    627            case glsDrawTests.DrawTestSpec.InputType.INT:
    628            case glsDrawTests.DrawTestSpec.InputType.UNSIGNED_INT: {
    629                return glsDrawTests.GLValue.create(minv + rnd.getInt() % (maxv - minv), type);
    630                break;
    631            }
    632 
    633            default:
    634                throw new Error('glsDrawTests.GLValue.getRandom - Invalid input type');
    635                break;
    636        }
    637    };
    638 
    639    // Minimum difference required between coordinates
    640 
    641    /**
    642     * @param {?glsDrawTests.DrawTestSpec.InputType} type
    643     * @return {glsDrawTests.GLValue}
    644     */
    645    glsDrawTests.GLValue.minValue = function(type) {
    646        switch (type) {
    647            case glsDrawTests.DrawTestSpec.InputType.FLOAT:
    648            case glsDrawTests.DrawTestSpec.InputType.BYTE:
    649            case glsDrawTests.DrawTestSpec.InputType.HALF:
    650                return glsDrawTests.GLValue.create(4, type);
    651            case glsDrawTests.DrawTestSpec.InputType.SHORT:
    652            case glsDrawTests.DrawTestSpec.InputType.UNSIGNED_SHORT:
    653                return glsDrawTests.GLValue.create(4 * 256, type);
    654            case glsDrawTests.DrawTestSpec.InputType.UNSIGNED_BYTE:
    655                return glsDrawTests.GLValue.create(4 * 2, type);
    656            case glsDrawTests.DrawTestSpec.InputType.INT:
    657            case glsDrawTests.DrawTestSpec.InputType.UNSIGNED_INT:
    658                return glsDrawTests.GLValue.create(4 * 16777216, type);
    659 
    660            default:
    661                throw new Error('glsDrawTests.GLValue.minValue - Invalid input type');
    662        }
    663    };
    664 
    665    /**
    666     * @param {glsDrawTests.GLValue} val
    667     * @return {glsDrawTests.GLValue}
    668     */
    669    glsDrawTests.GLValue.abs = function(val) {
    670        var type = val.getType();
    671        switch (type) {
    672            case glsDrawTests.DrawTestSpec.InputType.SHORT:
    673                return glsDrawTests.GLValue.create(0x7FFF & val.getValue(), type);
    674            case glsDrawTests.DrawTestSpec.InputType.BYTE:
    675                return glsDrawTests.GLValue.create(0x7F & val.getValue(), type);
    676            case glsDrawTests.DrawTestSpec.InputType.UNSIGNED_BYTE:
    677            case glsDrawTests.DrawTestSpec.InputType.UNSIGNED_SHORT:
    678            case glsDrawTests.DrawTestSpec.InputType.UNSIGNED_INT:
    679                return val;
    680            case glsDrawTests.DrawTestSpec.InputType.FLOAT:
    681            case glsDrawTests.DrawTestSpec.InputType.HALF:
    682                return glsDrawTests.GLValue.create(Math.abs(val.interpret()), type);
    683            case glsDrawTests.DrawTestSpec.InputType.INT:
    684                return glsDrawTests.GLValue.create(0x7FFFFFFF & val.getValue(), type);
    685            default:
    686                throw new Error('glsDrawTests.GLValue.abs - Invalid input type');
    687        }
    688    };
    689 
    690    /**
    691     * @return {?glsDrawTests.DrawTestSpec.InputType}
    692     */
    693    glsDrawTests.GLValue.prototype.getType = function() {
    694        return this.m_type;
    695    };
    696 
    697    /**
    698     * glsDrawTests.GLValue.toFloat
    699     * @return {number}
    700     */
    701    glsDrawTests.GLValue.prototype.toFloat = function() {
    702        return this.interpret();
    703    };
    704 
    705    /**
    706     * glsDrawTests.GLValue.getValue
    707     * @return {number}
    708     */
    709    glsDrawTests.GLValue.prototype.getValue = function() {
    710        return this.m_value[0];
    711    };
    712 
    713    /**
    714     * interpret function. Returns the m_value as a quantity so arithmetic operations can be performed on it
    715     * Only some types require this.
    716     * @return {number}
    717     */
    718    glsDrawTests.GLValue.prototype.interpret = function() {
    719        if (this.m_type == glsDrawTests.DrawTestSpec.InputType.HALF)
    720            return glsDrawTests.GLValue.halfToFloat(this.m_value[0]);
    721 
    722        return this.m_value[0];
    723    };
    724 
    725    /**
    726     * @param {glsDrawTests.GLValue} other
    727     * @return {glsDrawTests.GLValue}
    728     */
    729    glsDrawTests.GLValue.prototype.add = function(other) {
    730        return glsDrawTests.GLValue.create(this.interpret() + other.interpret(), this.m_type);
    731    };
    732 
    733    /**
    734     * @param {glsDrawTests.GLValue} other
    735     * @return {glsDrawTests.GLValue}
    736     */
    737    glsDrawTests.GLValue.prototype.mul = function(other) {
    738        return glsDrawTests.GLValue.create(this.interpret() * other.interpret(), this.m_type);
    739    };
    740 
    741    /**
    742     * @param {glsDrawTests.GLValue} other
    743     * @return {glsDrawTests.GLValue}
    744     */
    745    glsDrawTests.GLValue.prototype.div = function(other) {
    746        return glsDrawTests.GLValue.create(this.interpret() / other.interpret(), this.m_type);
    747    };
    748 
    749    /**
    750     * @param {glsDrawTests.GLValue} other
    751     * @return {glsDrawTests.GLValue}
    752     */
    753    glsDrawTests.GLValue.prototype.sub = function(other) {
    754        return glsDrawTests.GLValue.create(this.interpret() - other.interpret(), this.m_type);
    755    };
    756 
    757    /**
    758     * @param {glsDrawTests.GLValue} other
    759     * @return {glsDrawTests.GLValue}
    760     */
    761    glsDrawTests.GLValue.prototype.addToSelf = function(other) {
    762        this.m_value[0] = this.interpret() + other.interpret();
    763        return this;
    764    };
    765 
    766    /**
    767     * @param {glsDrawTests.GLValue} other
    768     * @return {glsDrawTests.GLValue}
    769     */
    770    glsDrawTests.GLValue.prototype.subToSelf = function(other) {
    771        this.m_value[0] = this.interpret() - other.interpret();
    772        return this;
    773    };
    774 
    775    /**
    776     * @param {glsDrawTests.GLValue} other
    777     * @return {glsDrawTests.GLValue}
    778     */
    779    glsDrawTests.GLValue.prototype.mulToSelf = function(other) {
    780        this.m_value[0] = this.interpret() * other.interpret();
    781        return this;
    782    };
    783 
    784    /**
    785     * @param {glsDrawTests.GLValue} other
    786     * @return {glsDrawTests.GLValue}
    787     */
    788    glsDrawTests.GLValue.prototype.divToSelf = function(other) {
    789        this.m_value[0] = this.interpret() / other.interpret();
    790        return this;
    791    };
    792 
    793    /**
    794     * @param {glsDrawTests.GLValue} other
    795     * @return {boolean}
    796     */
    797    glsDrawTests.GLValue.prototype.equals = function(other) {
    798        return this.m_value[0] == other.getValue();
    799    };
    800 
    801    /**
    802     * @param {glsDrawTests.GLValue} other
    803     * @return {boolean}
    804     */
    805    glsDrawTests.GLValue.prototype.lessThan = function(other) {
    806        return this.interpret() < other.interpret();
    807    };
    808 
    809    /**
    810     * @param {glsDrawTests.GLValue} other
    811     * @return {boolean}
    812     */
    813    glsDrawTests.GLValue.prototype.greaterThan = function(other) {
    814        return this.interpret() > other.interpret();
    815    };
    816 
    817    /**
    818     * @param {glsDrawTests.GLValue} other
    819     * @return {boolean}
    820     */
    821    glsDrawTests.GLValue.prototype.lessOrEqualThan = function(other) {
    822        return this.interpret() <= other.interpret();
    823    };
    824 
    825    /**
    826     * @param {glsDrawTests.GLValue} other
    827     * @return {boolean}
    828     */
    829    glsDrawTests.GLValue.prototype.greaterOrEqualThan = function(other) {
    830        return this.interpret() >= other.interpret();
    831    };
    832 
    833    // AttriuteArray
    834 
    835    /**
    836     * AttributeArray
    837     * @constructor
    838     * @param {?glsDrawTests.DrawTestSpec.Storage} storage
    839     * @param {sglrGLContext.GLContext | sglrReferenceContext.ReferenceContext} context
    840     */
    841    glsDrawTests.AttributeArray = function(storage, context) {
    842        /** @type {?glsDrawTests.DrawTestSpec.Storage} */ this.m_storage = storage;
    843        /** @type {sglrGLContext.GLContext | sglrReferenceContext.ReferenceContext} */ this.m_ctx = context;
    844        /** @type {WebGLBuffer|sglrReferenceContext.DataBuffer|null} */ this.m_glBuffer;
    845 
    846        /** @type {number} */ this.m_size = 0;
    847        /** @type {Uint8Array} */ this.m_data; //NOTE: Used in unsupported user storage
    848        /** @type {number} */ this.m_componentCount;
    849        /** @type {boolean} */ this.m_bound = false;
    850        /** @type {glsDrawTests.DrawTestSpec.Target} */ this.m_target = glsDrawTests.DrawTestSpec.Target.ARRAY;
    851        /** @type {?glsDrawTests.DrawTestSpec.InputType} */ this.m_inputType = glsDrawTests.DrawTestSpec.InputType.FLOAT;
    852        /** @type {?glsDrawTests.DrawTestSpec.OutputType} */ this.m_outputType = glsDrawTests.DrawTestSpec.OutputType.VEC4;
    853        /** @type {boolean} */ this.m_normalize = false;
    854        /** @type {number} */ this.m_stride = 0;
    855        /** @type {number} */ this.m_offset = 0;
    856        /** @type {Array<number>} */ this.m_defaultAttrib;
    857        /** @type {number} */ this.m_instanceDivisor = 0;
    858        /** @type {boolean} */ this.m_isPositionAttr = false;
    859 
    860        if (this.m_storage == glsDrawTests.DrawTestSpec.Storage.BUFFER) {
    861            this.m_glBuffer = this.m_ctx.createBuffer();
    862        }
    863    };
    864 
    865    /** @return {number} */ glsDrawTests.AttributeArray.prototype.getComponentCount = function() {return this.m_componentCount;};
    866 
    867    /** @return {?glsDrawTests.DrawTestSpec.Target} */ glsDrawTests.AttributeArray.prototype.getTarget = function() {return this.m_target;};
    868 
    869    /** @return {?glsDrawTests.DrawTestSpec.InputType} */ glsDrawTests.AttributeArray.prototype.getInputType = function() {return this.m_inputType;};
    870 
    871    /** @return {?glsDrawTests.DrawTestSpec.OutputType} */ glsDrawTests.AttributeArray.prototype.getOutputType = function() {return this.m_outputType;};
    872 
    873    /** @return {?glsDrawTests.DrawTestSpec.Storage} */ glsDrawTests.AttributeArray.prototype.getStorageType = function() {return this.m_storage;};
    874 
    875    /** @return {boolean} */ glsDrawTests.AttributeArray.prototype.getNormalized = function() {return this.m_normalize;};
    876 
    877    /** @return {number} */ glsDrawTests.AttributeArray.prototype.getStride = function() {return this.m_stride;};
    878 
    879    /** @return {boolean} */ glsDrawTests.AttributeArray.prototype.isBound = function() {return this.m_bound;};
    880 
    881    /** @return {boolean} */ glsDrawTests.AttributeArray.prototype.isPositionAttribute = function() {return this.m_isPositionAttr;};
    882 
    883    /**
    884     * @param {glsDrawTests.DrawTestSpec.Target} target
    885     * @param {number} size
    886     * @param {goog.TypedArray} ptr
    887     * @param {?glsDrawTests.DrawTestSpec.Usage} usage
    888     */
    889    glsDrawTests.AttributeArray.prototype.data = function(target, size, ptr, usage) {
    890        this.m_size = size;
    891        this.m_target = target;
    892 
    893        if (this.m_storage == glsDrawTests.DrawTestSpec.Storage.BUFFER) {
    894            this.m_ctx.bindBuffer(glsDrawTests.targetToGL(target), this.m_glBuffer);
    895            this.m_ctx.bufferData(glsDrawTests.targetToGL(target), ptr, glsDrawTests.usageToGL(usage));
    896        } else
    897            throw new Error('Wrong storage type');
    898    };
    899 
    900    /**
    901     * @param {glsDrawTests.DrawTestSpec.Target} target
    902     * @param {number} offset
    903     * @param {number} size
    904     * @param {goog.TypedArray} ptr
    905     */
    906    glsDrawTests.AttributeArray.prototype.subdata = function(target, offset, size, ptr) {
    907        this.m_target = target;
    908 
    909        if (this.m_storage == glsDrawTests.DrawTestSpec.Storage.BUFFER) {
    910            this.m_ctx.bindBuffer(glsDrawTests.targetToGL(target), this.m_glBuffer);
    911 
    912            this.m_ctx.bufferSubData(glsDrawTests.targetToGL(target), offset, size, ptr);
    913        } else
    914            throw new Error('Wrong storage type');
    915    };
    916 
    917    /**
    918     * @param {boolean} bound
    919     * @param {number} offset
    920     * @param {number} size
    921     * @param {?glsDrawTests.DrawTestSpec.InputType} inputType
    922     * @param {?glsDrawTests.DrawTestSpec.OutputType} outType
    923     * @param {boolean} normalized
    924     * @param {number} stride
    925     * @param {number} instanceDivisor
    926     * @param {Array<number>} defaultAttrib
    927     * @param {boolean} isPositionAttr
    928     */
    929    glsDrawTests.AttributeArray.prototype.setupArray = function(bound, offset, size, inputType, outType,
    930        normalized, stride, instanceDivisor, defaultAttrib, isPositionAttr) {
    931        this.m_componentCount = size;
    932        this.m_bound = bound;
    933        this.m_inputType = inputType;
    934        this.m_outputType = outType;
    935        this.m_normalize = normalized;
    936        this.m_stride = stride;
    937        this.m_offset = offset;
    938        this.m_defaultAttrib = defaultAttrib;
    939        this.m_instanceDivisor = instanceDivisor;
    940        this.m_isPositionAttr = isPositionAttr;
    941    };
    942 
    943    /**
    944     * @param {number} loc (32-bit)
    945     */
    946    glsDrawTests.AttributeArray.prototype.bindAttribute = function(loc) {
    947        if (!this.isBound()) {
    948            /** @type {Array<number>} */ var attr = this.m_defaultAttrib;
    949            switch (this.m_inputType) {
    950                case glsDrawTests.DrawTestSpec.InputType.FLOAT: {
    951                    switch (this.m_componentCount) {
    952                        case 1: this.m_ctx.vertexAttrib1f(loc, attr[0]); break;
    953                        case 2: this.m_ctx.vertexAttrib2f(loc, attr[0], attr[1]); break;
    954                        case 3: this.m_ctx.vertexAttrib3f(loc, attr[0], attr[1], attr[2]); break;
    955                        case 4: this.m_ctx.vertexAttrib4f(loc, attr[0], attr[1], attr[2], attr[3]); break;
    956                        default: throw new Error('Invalid component count'); break;
    957                    }
    958                    break;
    959                }
    960                case glsDrawTests.DrawTestSpec.InputType.INT: {
    961                    this.m_ctx.vertexAttribI4i(loc, attr[0], attr[1], attr[2], attr[3]);
    962                    break;
    963                }
    964                case glsDrawTests.DrawTestSpec.InputType.UNSIGNED_INT: {
    965                    this.m_ctx.vertexAttribI4ui(loc, attr[0], attr[1], attr[2], attr[3]);
    966                    break;
    967                }
    968                default:
    969                    throw new Error('Invalid input type');
    970                    break;
    971            }
    972        } else {
    973            /** @type {Uint8Array} */ var basePtr = null;
    974 
    975            if (this.m_storage == glsDrawTests.DrawTestSpec.Storage.BUFFER) {
    976                this.m_ctx.bindBuffer(glsDrawTests.targetToGL(this.m_target), this.m_glBuffer);
    977 
    978                basePtr = null;
    979            } else
    980                throw new Error('Invalid storage type');
    981 
    982            if (!glsDrawTests.inputTypeIsFloatType(this.m_inputType)) {
    983                // Input is not float type
    984 
    985                if (glsDrawTests.outputTypeIsFloatType(this.m_outputType)) {
    986                    var size = this.m_componentCount;
    987 
    988                    // Output type is float type
    989                    this.m_ctx.vertexAttribPointer(loc, size, glsDrawTests.inputTypeToGL(this.m_inputType), this.m_normalize, this.m_stride, this.m_offset);
    990                } else {
    991                    // Output type is int type
    992                    this.m_ctx.vertexAttribIPointer(loc, this.m_componentCount, glsDrawTests.inputTypeToGL(this.m_inputType), this.m_stride, this.m_offset);
    993                }
    994            } else {
    995                // Input type is float type
    996 
    997                // Output type must be float type
    998                assertMsgOptions(glsDrawTests.outputTypeIsFloatType(this.m_outputType), 'Output type is not float', false, true);
    999 
   1000                this.m_ctx.vertexAttribPointer(loc, this.m_componentCount, glsDrawTests.inputTypeToGL(this.m_inputType), this.m_normalize,
   1001                    this.m_stride, this.m_offset);
   1002            }
   1003 
   1004            if (this.m_instanceDivisor)
   1005                this.m_ctx.vertexAttribDivisor(loc, this.m_instanceDivisor);
   1006        }
   1007    };
   1008 
   1009    /**
   1010     * @param {glsDrawTests.DrawTestSpec.Target} target
   1011     */
   1012    glsDrawTests.AttributeArray.prototype.bindIndexArray = function(target) {
   1013        if (this.m_storage == glsDrawTests.DrawTestSpec.Storage.BUFFER) {
   1014            this.m_ctx.bindBuffer(glsDrawTests.targetToGL(target), this.m_glBuffer);
   1015        }
   1016    };
   1017 
   1018    // DrawTestShaderProgram
   1019 
   1020    /**
   1021     * @constructor
   1022     * @extends {sglrShaderProgram.ShaderProgram}
   1023     * @param {Array<glsDrawTests.AttributeArray>} arrays
   1024     */
   1025    glsDrawTests.DrawTestShaderProgram = function(arrays) {
   1026        sglrShaderProgram.ShaderProgram.call(this, this.createProgramDeclaration(arrays));
   1027 
   1028        this.m_componentCount = [];
   1029        this.m_isCoord = [];
   1030        this.m_attrType = [];
   1031 
   1032        for (var arrayNdx = 0; arrayNdx < arrays.length; arrayNdx++) {
   1033            this.m_componentCount[arrayNdx] = this.getComponentCount(arrays[arrayNdx].getOutputType());
   1034            this.m_isCoord[arrayNdx] = arrays[arrayNdx].isPositionAttribute();
   1035            this.m_attrType[arrayNdx] = this.mapOutputType(arrays[arrayNdx].getOutputType());
   1036        }
   1037    };
   1038 
   1039    glsDrawTests.DrawTestShaderProgram.prototype = Object.create(sglrShaderProgram.ShaderProgram.prototype);
   1040    glsDrawTests.DrawTestShaderProgram.prototype.constructor = glsDrawTests.DrawTestShaderProgram;
   1041 
   1042    /**
   1043     * @param {Array<number>} color
   1044     * @param {goog.NumberArray} attribValue
   1045     * @param {number} numComponents
   1046     * @return {Array<number>}
   1047     */
   1048    glsDrawTests.calcShaderColor = function(color, attribValue, numComponents) {
   1049        switch (numComponents) {
   1050            case 1:
   1051                color[0] = deMath.scale(color, attribValue[0])[0];
   1052                break;
   1053 
   1054            case 2:
   1055                color[0] = color[0] * attribValue[0];
   1056                color[1] = color[1] * attribValue[1];
   1057                break;
   1058 
   1059            case 3:
   1060                color[0] = color[0] * attribValue[0];
   1061                color[1] = color[1] * attribValue[1];
   1062                color[2] = color[2] * attribValue[2];
   1063                break;
   1064 
   1065            case 4:
   1066                color[0] = color[0] * attribValue[0] * attribValue[3];
   1067                color[1] = color[1] * attribValue[1] * attribValue[3];
   1068                color[2] = color[2] * attribValue[2] * attribValue[3];
   1069                break;
   1070 
   1071            default:
   1072                throw new Error('Invalid component count');
   1073        }
   1074 
   1075        return color;
   1076    };
   1077 
   1078    /**
   1079     * @param {Array<number>} coord
   1080     * @param {goog.NumberArray} attribValue
   1081     * @param {number} numComponents
   1082     * @return {Array<number>}
   1083     */
   1084    glsDrawTests.calcShaderCoord = function(coord, attribValue, numComponents) {
   1085        switch (numComponents) {
   1086            case 1:
   1087 
   1088                coord = deMath.add(coord, [attribValue[0], attribValue[0]]);
   1089                coord[0] = coord[0];
   1090                coord[1] = coord[1];
   1091                break;
   1092            case 2:
   1093                coord = deMath.add(coord, [attribValue[0], attribValue[1]]);
   1094                coord[0] = coord[0];
   1095                coord[1] = coord[1];
   1096                break;
   1097            case 3:
   1098                coord = deMath.add(coord, [attribValue[0] + attribValue[2], attribValue[1]]);
   1099                coord[0] = coord[0];
   1100                coord[1] = coord[1];
   1101                coord[2] = coord[2];
   1102                break;
   1103            case 4:
   1104                coord = deMath.add(coord, [attribValue[0] + attribValue[2], attribValue[1] + attribValue[3]]);
   1105                coord[0] = coord[0];
   1106                coord[1] = coord[1];
   1107                coord[2] = coord[2];
   1108                coord[3] = coord[3];
   1109                break;
   1110 
   1111            default:
   1112                throw new Error('Invalid component count');
   1113        }
   1114 
   1115        return coord;
   1116    };
   1117 
   1118    /**
   1119     * @param {Array<rrVertexAttrib.VertexAttrib>} inputs
   1120     * @param {Array<rrVertexPacket.VertexPacket>} packets
   1121     * @param {number} numPackets
   1122     */
   1123    glsDrawTests.DrawTestShaderProgram.prototype.shadeVertices = function(inputs, packets, numPackets) {
   1124        var u_coordScale = this.getUniformByName('u_coordScale').value;
   1125        var u_colorScale = this.getUniformByName('u_colorScale').value;
   1126 
   1127        for (var packetNdx = 0; packetNdx < numPackets; ++packetNdx) {
   1128            var varyingLocColor = 0;
   1129 
   1130            /** @type {rrVertexPacket.VertexPacket} */ var packet = packets[packetNdx];
   1131 
   1132            // Calc output color
   1133            /** @type {Array<number>} */ var coord = [0.0, 0.0];
   1134            /** @type {Array<number>} */ var color = [1.0, 1.0, 1.0];
   1135 
   1136            for (var attribNdx = 0; attribNdx < this.m_attrType.length; attribNdx++) {
   1137                var numComponents = this.m_componentCount[attribNdx];
   1138                /** @type {boolean} */ var isCoord = this.m_isCoord[attribNdx];
   1139 
   1140                var attrib = rrVertexAttrib.readVertexAttrib(inputs[attribNdx], packet.instanceNdx, packet.vertexNdx, this.m_attrType[attribNdx]);
   1141 
   1142                if (isCoord) {
   1143                    coord = glsDrawTests.calcShaderCoord(
   1144                        coord,
   1145                        attrib,
   1146                        numComponents
   1147                    );
   1148                } else {
   1149                    color = glsDrawTests.calcShaderColor(
   1150                        color,
   1151                        attrib,
   1152                        numComponents
   1153                    );
   1154                }
   1155            }
   1156 
   1157            // Transform position
   1158            packet.position = [u_coordScale * coord[0], u_coordScale * coord[1], 1.0, 1.0];
   1159            packet.pointSize = 1.0;
   1160 
   1161            // Pass color to FS
   1162            packet.outputs[varyingLocColor] = deMath.add(deMath.scale([u_colorScale * color[0], u_colorScale * color[1], u_colorScale * color[2], 1.0], 0.5), [0.5, 0.5, 0.5, 0.5]);
   1163        }
   1164    };
   1165 
   1166    /**
   1167     * @param {Array<rrFragmentOperations.Fragment>} packets
   1168     * @param {rrShadingContext.FragmentShadingContext} context
   1169     */
   1170    glsDrawTests.DrawTestShaderProgram.prototype.shadeFragments = function(packets, context) {
   1171        var varyingLocColor = 0;
   1172 
   1173        for (var packetNdx = 0; packetNdx < packets.length; ++packetNdx) {
   1174            /** @type {rrFragmentOperations.Fragment} */ var packet = packets[packetNdx];
   1175            packet.value = rrShadingContext.readVarying(packet, context, varyingLocColor);
   1176        }
   1177    };
   1178 
   1179    /**
   1180     * @param {Array<glsDrawTests.AttributeArray>} arrays
   1181     * @return {string}
   1182     */
   1183    glsDrawTests.DrawTestShaderProgram.prototype.genVertexSource = function(arrays) {
   1184        /** @type {Array<string>}*/ var params;
   1185        var vertexShaderTmpl = '';
   1186 
   1187        params = this.generateShaderParams();
   1188 
   1189        vertexShaderTmpl += params['VTX_HDR'];
   1190 
   1191        for (var arrayNdx = 0; arrayNdx < arrays.length; arrayNdx++) {
   1192            vertexShaderTmpl += params['VTX_IN'] + ' highp ' + glsDrawTests.outputTypeToGLType(arrays[arrayNdx].getOutputType()) + ' a_' + arrayNdx + ';\n';
   1193        }
   1194 
   1195        vertexShaderTmpl +=
   1196            'uniform highp float u_coordScale;\n' +
   1197            'uniform highp float u_colorScale;\n' +
   1198            params['VTX_OUT'] + ' ' + params['COL_PRECISION'] + ' vec4 v_color;\n' +
   1199            'void main(void)\n' +
   1200            '{\n' +
   1201            '\tgl_PointSize = 1.0;\n' +
   1202            '\thighp vec2 coord = vec2(0.0, 0.0);\n' +
   1203            '\thighp vec3 color = vec3(1.0, 1.0, 1.0);\n';
   1204 
   1205        for (var arrayNdx = 0; arrayNdx < arrays.length; arrayNdx++) {
   1206            var isPositionAttr = arrays[arrayNdx].isPositionAttribute();
   1207 
   1208            if (isPositionAttr) {
   1209                switch (arrays[arrayNdx].getOutputType()) {
   1210                    case glsDrawTests.DrawTestSpec.OutputType.FLOAT:
   1211                    case glsDrawTests.DrawTestSpec.OutputType.INT:
   1212                    case glsDrawTests.DrawTestSpec.OutputType.UINT:
   1213                        vertexShaderTmpl +=
   1214                            '\tcoord += vec2(float(a_' + arrayNdx + '), float(a_' + arrayNdx + '));\n';
   1215                        break;
   1216 
   1217                    case glsDrawTests.DrawTestSpec.OutputType.VEC2:
   1218                    case glsDrawTests.DrawTestSpec.OutputType.IVEC2:
   1219                    case glsDrawTests.DrawTestSpec.OutputType.UVEC2:
   1220                        vertexShaderTmpl +=
   1221                            '\tcoord += vec2(a_' + arrayNdx + '.xy);\n';
   1222                        break;
   1223 
   1224                    case glsDrawTests.DrawTestSpec.OutputType.VEC3:
   1225                    case glsDrawTests.DrawTestSpec.OutputType.IVEC3:
   1226                    case glsDrawTests.DrawTestSpec.OutputType.UVEC3:
   1227                        vertexShaderTmpl +=
   1228                            '\tcoord += vec2(a_' + arrayNdx + '.xy);\n' +
   1229                            '\tcoord.x += float(a_' + arrayNdx + '.z);\n';
   1230                        break;
   1231 
   1232                    case glsDrawTests.DrawTestSpec.OutputType.VEC4:
   1233                    case glsDrawTests.DrawTestSpec.OutputType.IVEC4:
   1234                    case glsDrawTests.DrawTestSpec.OutputType.UVEC4:
   1235                        vertexShaderTmpl +=
   1236                            '\tcoord += vec2(a_' + arrayNdx + '.xy);\n' +
   1237                            '\tcoord += vec2(a_' + arrayNdx + '.zw);\n';
   1238                        break;
   1239 
   1240                    default:
   1241                        throw new Error('Invalid output type');
   1242                        break;
   1243                }
   1244            } else {
   1245                switch (arrays[arrayNdx].getOutputType()) {
   1246                    case glsDrawTests.DrawTestSpec.OutputType.FLOAT:
   1247                    case glsDrawTests.DrawTestSpec.OutputType.INT:
   1248                    case glsDrawTests.DrawTestSpec.OutputType.UINT:
   1249                        vertexShaderTmpl +=
   1250                            '\tcolor = color * float(a_' + arrayNdx + ');\n';
   1251                        break;
   1252 
   1253                    case glsDrawTests.DrawTestSpec.OutputType.VEC2:
   1254                    case glsDrawTests.DrawTestSpec.OutputType.IVEC2:
   1255                    case glsDrawTests.DrawTestSpec.OutputType.UVEC2:
   1256                        vertexShaderTmpl +=
   1257                            '\tcolor.rg = color.rg * vec2(a_' + arrayNdx + '.xy);\n';
   1258                        break;
   1259 
   1260                    case glsDrawTests.DrawTestSpec.OutputType.VEC3:
   1261                    case glsDrawTests.DrawTestSpec.OutputType.IVEC3:
   1262                    case glsDrawTests.DrawTestSpec.OutputType.UVEC3:
   1263                        vertexShaderTmpl +=
   1264                            '\tcolor = color.rgb * vec3(a_' + arrayNdx + '.xyz);\n';
   1265                        break;
   1266 
   1267                    case glsDrawTests.DrawTestSpec.OutputType.VEC4:
   1268                    case glsDrawTests.DrawTestSpec.OutputType.IVEC4:
   1269                    case glsDrawTests.DrawTestSpec.OutputType.UVEC4:
   1270                        vertexShaderTmpl +=
   1271                            '\tcolor = color.rgb * vec3(a_' + arrayNdx + '.xyz) * float(a_' + arrayNdx + '.w);\n';
   1272                        break;
   1273 
   1274                    default:
   1275                        throw new Error('Invalid output type');
   1276                        break;
   1277                }
   1278            }
   1279        }
   1280 
   1281        vertexShaderTmpl +=
   1282            '\tv_color = vec4(u_colorScale * color, 1.0) * 0.5 + vec4(0.5, 0.5, 0.5, 0.5);\n' +
   1283            '\tgl_Position = vec4(u_coordScale * coord, 1.0, 1.0);\n' +
   1284            '}\n';
   1285 
   1286        return vertexShaderTmpl;
   1287    };
   1288 
   1289    /**
   1290     * @return {string}
   1291     */
   1292    glsDrawTests.DrawTestShaderProgram.prototype.genFragmentSource = function() {
   1293        /** @type {Array<string>} */ var params;
   1294 
   1295        params = this.generateShaderParams();
   1296 
   1297        var fragmentShaderTmpl = params['FRAG_HDR'] +
   1298            params['FRAG_IN'] + ' ' + params['COL_PRECISION'] + ' vec4 v_color;\n' +
   1299            'void main(void)\n' +
   1300            '{\n' +
   1301            '\t' + params['FRAG_COLOR'] + '= v_color;\n' +
   1302            '}\n';
   1303 
   1304        return fragmentShaderTmpl;
   1305    };
   1306 
   1307    /**
   1308     * @return {Array<string>}
   1309     */
   1310    glsDrawTests.DrawTestShaderProgram.prototype.generateShaderParams = function() {
   1311        /** @type {Array<string>} */ var params = [];
   1312        if (gluShaderUtil.isGLSLVersionSupported(gl, gluShaderUtil.GLSLVersion.V300_ES)) {
   1313            params['VTX_IN'] = 'in';
   1314            params['VTX_OUT'] = 'out';
   1315            params['FRAG_IN'] = 'in';
   1316            params['FRAG_COLOR'] = 'dEQP_FragColor';
   1317            params['VTX_HDR'] = '#version 300 es\n';
   1318            params['FRAG_HDR'] = '#version 300 es\nlayout(location = 0) out mediump vec4 dEQP_FragColor;\n';
   1319            params['COL_PRECISION'] = 'mediump';
   1320        } else if (gluShaderUtil.isGLSLVersionSupported(gl, gluShaderUtil.GLSLVersion.V100_ES)) {
   1321            params['VTX_IN'] = 'attribute';
   1322            params['VTX_OUT'] = 'varying';
   1323            params['FRAG_IN'] = 'varying';
   1324            params['FRAG_COLOR'] = 'gl_FragColor';
   1325            params['VTX_HDR'] = '';
   1326            params['FRAG_HDR'] = '';
   1327            params['COL_PRECISION'] = 'mediump';
   1328        } else
   1329            throw new Error('Invalid GL version');
   1330 
   1331        return params;
   1332    };
   1333 
   1334    /**
   1335     * @param {?glsDrawTests.DrawTestSpec.OutputType} type
   1336     * @return {rrGenericVector.GenericVecType}
   1337     */
   1338    glsDrawTests.DrawTestShaderProgram.prototype.mapOutputType = function(type) {
   1339        switch (type) {
   1340            case glsDrawTests.DrawTestSpec.OutputType.FLOAT:
   1341            case glsDrawTests.DrawTestSpec.OutputType.VEC2:
   1342            case glsDrawTests.DrawTestSpec.OutputType.VEC3:
   1343            case glsDrawTests.DrawTestSpec.OutputType.VEC4:
   1344                return rrGenericVector.GenericVecType.FLOAT;
   1345 
   1346            case glsDrawTests.DrawTestSpec.OutputType.INT:
   1347            case glsDrawTests.DrawTestSpec.OutputType.IVEC2:
   1348            case glsDrawTests.DrawTestSpec.OutputType.IVEC3:
   1349            case glsDrawTests.DrawTestSpec.OutputType.IVEC4:
   1350                return rrGenericVector.GenericVecType.INT32;
   1351 
   1352            case glsDrawTests.DrawTestSpec.OutputType.UINT:
   1353            case glsDrawTests.DrawTestSpec.OutputType.UVEC2:
   1354            case glsDrawTests.DrawTestSpec.OutputType.UVEC3:
   1355            case glsDrawTests.DrawTestSpec.OutputType.UVEC4:
   1356                return rrGenericVector.GenericVecType.UINT32;
   1357 
   1358            default:
   1359                throw new Error('Invalid output type');
   1360        }
   1361    };
   1362 
   1363    /**
   1364     * @param {?glsDrawTests.DrawTestSpec.OutputType} type
   1365     * @return {number}
   1366     */
   1367    glsDrawTests.DrawTestShaderProgram.prototype.getComponentCount = function(type) {
   1368        switch (type) {
   1369            case glsDrawTests.DrawTestSpec.OutputType.FLOAT:
   1370            case glsDrawTests.DrawTestSpec.OutputType.INT:
   1371            case glsDrawTests.DrawTestSpec.OutputType.UINT:
   1372                return 1;
   1373 
   1374            case glsDrawTests.DrawTestSpec.OutputType.VEC2:
   1375            case glsDrawTests.DrawTestSpec.OutputType.IVEC2:
   1376            case glsDrawTests.DrawTestSpec.OutputType.UVEC2:
   1377                return 2;
   1378 
   1379            case glsDrawTests.DrawTestSpec.OutputType.VEC3:
   1380            case glsDrawTests.DrawTestSpec.OutputType.IVEC3:
   1381            case glsDrawTests.DrawTestSpec.OutputType.UVEC3:
   1382                return 3;
   1383 
   1384            case glsDrawTests.DrawTestSpec.OutputType.VEC4:
   1385            case glsDrawTests.DrawTestSpec.OutputType.IVEC4:
   1386            case glsDrawTests.DrawTestSpec.OutputType.UVEC4:
   1387                return 4;
   1388 
   1389            default:
   1390                throw new Error('Invalid output type');
   1391        }
   1392    };
   1393 
   1394    /**
   1395     * @param {Array<glsDrawTests.AttributeArray>} arrays
   1396     * @return {sglrShaderProgram.ShaderProgramDeclaration}
   1397     */
   1398    glsDrawTests.DrawTestShaderProgram.prototype.createProgramDeclaration = function(arrays) {
   1399        /** @type {sglrShaderProgram.ShaderProgramDeclaration} */ var decl = new sglrShaderProgram.ShaderProgramDeclaration();
   1400 
   1401        for (var arrayNdx = 0; arrayNdx < arrays.length; arrayNdx++)
   1402            decl.pushVertexAttribute(new sglrShaderProgram.VertexAttribute('a_' + arrayNdx, this.mapOutputType(arrays[arrayNdx].getOutputType())));
   1403 
   1404        decl.pushVertexToFragmentVarying(new sglrShaderProgram.VertexToFragmentVarying(rrGenericVector.GenericVecType.FLOAT));
   1405        decl.pushFragmentOutput(new sglrShaderProgram.FragmentOutput(rrGenericVector.GenericVecType.FLOAT));
   1406 
   1407        decl.pushVertexSource(new sglrShaderProgram.VertexSource(this.genVertexSource(arrays)));
   1408        decl.pushFragmentSource(new sglrShaderProgram.FragmentSource(this.genFragmentSource()));
   1409 
   1410        decl.pushUniform(new sglrShaderProgram.Uniform('u_coordScale', gluShaderUtil.DataType.FLOAT));
   1411        decl.pushUniform(new sglrShaderProgram.Uniform('u_colorScale', gluShaderUtil.DataType.FLOAT));
   1412 
   1413        return decl;
   1414    };
   1415 
   1416    /**
   1417     * @typedef {glsDrawTests.RandomArrayGenerator}
   1418     */
   1419    glsDrawTests.RandomArrayGenerator = {};
   1420 
   1421    /**
   1422     * @param {goog.TypedArray} data
   1423     * @param {?glsDrawTests.DrawTestSpec.InputType} type
   1424     * @param {deRandom.Random} rnd
   1425     * @param {glsDrawTests.GLValue} min
   1426     * @param {glsDrawTests.GLValue} max
   1427     */
   1428    glsDrawTests.RandomArrayGenerator.setData = function(data, type, rnd, min, max) {
   1429        switch (type) {
   1430            case glsDrawTests.DrawTestSpec.InputType.FLOAT:
   1431            case glsDrawTests.DrawTestSpec.InputType.SHORT:
   1432            case glsDrawTests.DrawTestSpec.InputType.UNSIGNED_SHORT:
   1433            case glsDrawTests.DrawTestSpec.InputType.BYTE:
   1434            case glsDrawTests.DrawTestSpec.InputType.UNSIGNED_BYTE:
   1435            case glsDrawTests.DrawTestSpec.InputType.INT:
   1436            case glsDrawTests.DrawTestSpec.InputType.UNSIGNED_INT:
   1437            case glsDrawTests.DrawTestSpec.InputType.HALF:
   1438                glsDrawTests.copyGLValueToArray(data, glsDrawTests.GLValue.getRandom(rnd, min, max));
   1439                break;
   1440            default:
   1441                throw new Error('Invalid input type');
   1442        }
   1443    };
   1444 
   1445    /**
   1446     * createBasicArray
   1447     * @param {number} seed
   1448     * @param {number} elementCount
   1449     * @param {number} componentCount
   1450     * @param {number} offset
   1451     * @param {number} stride
   1452     * @param {?glsDrawTests.DrawTestSpec.InputType} type
   1453     * @param {number} first
   1454     * @param {?glsDrawTests.DrawTestSpec.Primitive} primitive
   1455     * @param {?goog.TypedArray} indices
   1456     * @param {number} indexSize
   1457     * @return {goog.TypedArray}
   1458     */
   1459    glsDrawTests.RandomArrayGenerator.createArray = function(seed, elementCount, componentCount, offset, stride, type, first, primitive, indices, indexSize) {
   1460        assertMsgOptions(componentCount >= 1 && componentCount <= 4, 'Unacceptable number of components', false, true);
   1461 
   1462        /** @type {glsDrawTests.GLValue} */ var min = glsDrawTests.GLValue.getMinValue(type);
   1463        /** @type {glsDrawTests.GLValue} */ var max = glsDrawTests.GLValue.getMaxValue(type);
   1464 
   1465        var packed = type == glsDrawTests.DrawTestSpec.InputType.INT_2_10_10_10 ||
   1466            type == glsDrawTests.DrawTestSpec.InputType.UNSIGNED_INT_2_10_10_10;
   1467        /** @type {number} */ var limit10 = (1 << 10);
   1468        /** @type {number} */ var limit2 = (1 << 2);
   1469 
   1470 
   1471        /** @type {number} */ var componentSize = glsDrawTests.DrawTestSpec.inputTypeSize(type);
   1472        /** @type {number} */ var elementSize = componentSize * componentCount;
   1473        /** @type {number} */ var bufferSize = offset + Math.max(elementCount * stride, elementCount * elementSize);
   1474 
   1475        var data = new ArrayBuffer(bufferSize);
   1476        var writePtr = new Uint8Array(data, offset);
   1477 
   1478        var previousComponentsFloat = [0, 0, 0, 0];
   1479        var rnd = new deRandom.Random(seed);
   1480 
   1481        for (var vertexNdx = 0; vertexNdx < elementCount; vertexNdx++) {
   1482            var components = [];
   1483 
   1484            for (var componentNdx = 0; componentNdx < componentCount; componentNdx++) {
   1485                var getRandomComponent = function() {
   1486                    // For packed formats we can't use GLValue
   1487                    if (packed) {
   1488                        if (componentNdx == 3) {
   1489                            return rnd.getInt() % limit2;
   1490                        } else {
   1491                            return rnd.getInt() % limit10;
   1492                        }
   1493                    } else {
   1494                        return glsDrawTests.GLValue.getRandom(rnd, min, max);
   1495                    }
   1496                };
   1497 
   1498                var component = getRandomComponent();
   1499                var componentFloat = (component instanceof glsDrawTests.GLValue) ? component.toFloat() : component;
   1500 
   1501                // Try to not create vertex near previous
   1502                if (vertexNdx != 0 && Math.abs(componentFloat - previousComponentsFloat[componentNdx]) < min.toFloat()) {
   1503                    // Too close, try again (but only once)
   1504                    component = getRandomComponent();
   1505                    componentFloat = (component instanceof glsDrawTests.GLValue) ? component.toFloat() : component;
   1506                }
   1507 
   1508                components.push(component);
   1509                previousComponentsFloat[componentNdx] = componentFloat;
   1510            }
   1511 
   1512            if (packed) {
   1513                var packedValue = deMath.binaryOp(
   1514                        deMath.shiftLeft(/** @type {Array<number>} */ (components)[3], 30), deMath.binaryOp(
   1515                            deMath.shiftLeft(/** @type {Array<number>} */ (components)[2], 20), deMath.binaryOp(
   1516                                deMath.shiftLeft(/** @type {Array<number>} */ (components)[1], 10), /** @type {Array<number>} */ (components)[0], deMath.BinaryOp.OR
   1517                                ), deMath.BinaryOp.OR
   1518                            ), deMath.BinaryOp.OR
   1519                        );
   1520                glsDrawTests.copyArray(writePtr, new Uint32Array([packedValue]));
   1521            } else {
   1522                for (var componentNdx = 0; componentNdx < componentCount; componentNdx++) {
   1523                    glsDrawTests.copyGLValueToArray(writePtr.subarray(componentNdx * componentSize), components[componentNdx]);
   1524                }
   1525            }
   1526 
   1527            writePtr = writePtr.subarray(stride);
   1528        }
   1529 
   1530        return new Uint8Array(data);
   1531    };
   1532 
   1533    /**
   1534     * @param {number} seed
   1535     * @param {number} elementCount
   1536     * @param {?glsDrawTests.DrawTestSpec.IndexType} type
   1537     * @param {number} offset
   1538     * @param {number} min
   1539     * @param {number} max
   1540     * @return {goog.TypedArray}
   1541     */
   1542    glsDrawTests.RandomArrayGenerator.generateIndices = function(seed, elementCount, type, offset, min, max) {
   1543        return glsDrawTests.RandomArrayGenerator.createIndices(seed, elementCount, offset, min, max, type);
   1544    };
   1545 
   1546    /**
   1547     * @param {number} seed
   1548     * @param {number} elementCount
   1549     * @param {number} offset
   1550     * @param {number} min
   1551     * @param {number} max
   1552     * @param {?glsDrawTests.DrawTestSpec.IndexType} type
   1553     * @return {goog.TypedArray}
   1554     */
   1555    glsDrawTests.RandomArrayGenerator.createIndices = function(seed, elementCount, offset, min, max, type) {
   1556        /** @type {number}*/ var elementSize = glsDrawTests.DrawTestSpec.indexTypeSize(type);
   1557        /** @type {number}*/ var bufferSize = offset + elementCount * elementSize;
   1558 
   1559        var data = new ArrayBuffer(bufferSize);
   1560        var writePtr = new Uint8Array(data).subarray(offset);
   1561 
   1562        var rnd = new deRandom.Random(seed);
   1563 
   1564        /* TODO: get limits for given index type --> if (min < 0 || min > std::numeric_limits<T>::max() ||
   1565            max < 0 || max > std::numeric_limits<T>::max() ||
   1566            min > max)
   1567            DE_ASSERT(!"Invalid range");*/
   1568 
   1569        // JS refrast requires shuffled unique keys
   1570        var keys = [];
   1571        for (var key = 0; key < elementCount; key++)
   1572            keys.push(glsDrawTests.GLValue.create(key, glsDrawTests.indexTypeToInputType(type)));
   1573 
   1574        for (var elementNdx = 0; elementNdx < elementCount; ++elementNdx) {
   1575            var randomkey = rnd.getInt(0, keys.length - 1);
   1576            var ndx = keys[randomkey];
   1577 
   1578            keys.splice(randomkey, 1);
   1579 
   1580            glsDrawTests.copyArray(
   1581                writePtr.subarray(elementSize * elementNdx),
   1582                new Uint8Array(ndx.m_value.buffer)
   1583            );
   1584        }
   1585 
   1586        return new Uint8Array(data);
   1587    };
   1588 
   1589    /**
   1590     * @param {number} seed
   1591     * @param {?glsDrawTests.DrawTestSpec.InputType} type
   1592     * @return {Array<number>}
   1593     */
   1594    glsDrawTests.RandomArrayGenerator.generateAttributeValue = function(seed, type) {
   1595        var random = new deRandom.Random(seed);
   1596 
   1597        switch (type) {
   1598            case glsDrawTests.DrawTestSpec.InputType.FLOAT:
   1599                return glsDrawTests.generateRandomVec4(random);
   1600 
   1601            case glsDrawTests.DrawTestSpec.InputType.INT:
   1602                return glsDrawTests.generateRandomIVec4(random);
   1603 
   1604            case glsDrawTests.DrawTestSpec.InputType.UNSIGNED_INT:
   1605                return glsDrawTests.generateRandomUVec4(random);
   1606 
   1607            default:
   1608                throw new Error('Invalid attribute type');
   1609        }
   1610    };
   1611 
   1612    // AttributePack
   1613 
   1614    /**
   1615     * @param {sglrReferenceContext.ReferenceContext | sglrGLContext.GLContext} drawContext
   1616     * @param {Array<number>} screenSize (2 positive elements in array)
   1617     * @param {boolean} useVao
   1618     * @param {boolean} logEnabled
   1619     * @constructor
   1620     */
   1621    glsDrawTests.AttributePack = function(drawContext, screenSize, useVao, logEnabled) {
   1622        /** @type {sglrReferenceContext.ReferenceContext | sglrGLContext.GLContext} */ this.m_ctx = drawContext;
   1623 
   1624        /** @type {Array<glsDrawTests.AttributeArray>} */ this.m_arrays = [];
   1625        /** @type {sglrShaderProgram.ShaderProgram} */ this.m_program;
   1626        /** @type {tcuSurface.Surface} */ this.m_screen = new tcuSurface.Surface(screenSize[0], screenSize[1]);
   1627        /** @type {boolean} */ this.m_useVao = useVao;
   1628        /** @type {boolean} */ this.m_logEnabled = logEnabled;
   1629        /** @type {WebGLProgram | sglrShaderProgram.ShaderProgram | null} */ this.m_programID = null;
   1630        /** @type {WebGLVertexArrayObject|sglrReferenceContext.VertexArray|null} */ this.m_vaoID = null;
   1631 
   1632        if (this.m_useVao)
   1633            this.m_vaoID = this.m_ctx.createVertexArray();
   1634    };
   1635 
   1636    /**
   1637     * @return {tcuSurface.Surface}
   1638     */
   1639    glsDrawTests.AttributePack.prototype.getSurface = function() {
   1640        return this.m_screen;
   1641    };
   1642 
   1643    /**
   1644     * @param {number} i
   1645     * @return {glsDrawTests.AttributeArray}
   1646     */
   1647    glsDrawTests.AttributePack.prototype.getArray = function(i) {
   1648        return this.m_arrays[i];
   1649    };
   1650 
   1651    /**
   1652     * @return number
   1653     */
   1654    glsDrawTests.AttributePack.prototype.getArrayCount = function() {
   1655        return this.m_arrays.length;
   1656    };
   1657 
   1658    /**
   1659     * @param {?glsDrawTests.DrawTestSpec.Storage} storage
   1660     */
   1661    glsDrawTests.AttributePack.prototype.newArray = function(storage) {
   1662        this.m_arrays.push(new glsDrawTests.AttributeArray(storage, this.m_ctx));
   1663    };
   1664 
   1665    /**
   1666     * clearArrays
   1667     */
   1668    glsDrawTests.AttributePack.prototype.clearArrays = function() {
   1669        this.m_arrays.length = 0;
   1670    };
   1671 
   1672    /**
   1673     * updateProgram
   1674     */
   1675    glsDrawTests.AttributePack.prototype.updateProgram = function() {
   1676        if (this.m_programID)
   1677            this.m_ctx.deleteProgram(this.m_programID);
   1678 
   1679        this.m_program = new glsDrawTests.DrawTestShaderProgram(this.m_arrays);
   1680        this.m_programID = this.m_ctx.createProgram(this.m_program);
   1681    };
   1682 
   1683    /**
   1684     * @param {?glsDrawTests.DrawTestSpec.Primitive} primitive
   1685     * @param {?glsDrawTests.DrawTestSpec.DrawMethod} drawMethod
   1686     * @param {number} firstVertex
   1687     * @param {number} vertexCount
   1688     * @param {?glsDrawTests.DrawTestSpec.IndexType} indexType
   1689     * @param {number} indexOffset
   1690     * @param {number} rangeStart
   1691     * @param {number} rangeEnd
   1692     * @param {number} instanceCount
   1693     * @param {number} coordScale
   1694     * @param {number} colorScale
   1695     * @param {glsDrawTests.AttributeArray} indexArray
   1696     */
   1697    glsDrawTests.AttributePack.prototype.render = function(primitive, drawMethod, firstVertex, vertexCount, indexType,
   1698        indexOffset, rangeStart, rangeEnd, instanceCount, coordScale, colorScale, indexArray) {
   1699        assertMsgOptions(this.m_program != null, 'Program is null', false, true);
   1700        assertMsgOptions(this.m_programID != null, 'No context created program', false, true);
   1701 
   1702        this.m_ctx.viewport(0, 0, this.m_screen.getWidth(), this.m_screen.getHeight());
   1703        this.m_ctx.clearColor(0.0, 0.0, 0.0, 1.0);
   1704        this.m_ctx.clear(gl.COLOR_BUFFER_BIT);
   1705 
   1706        this.m_ctx.useProgram(this.m_programID);
   1707 
   1708        this.m_ctx.uniform1f(this.m_ctx.getUniformLocation(this.m_programID, 'u_coordScale'), coordScale);
   1709        this.m_ctx.uniform1f(this.m_ctx.getUniformLocation(this.m_programID, 'u_colorScale'), colorScale);
   1710 
   1711        if (this.m_useVao)
   1712            this.m_ctx.bindVertexArray(this.m_vaoID);
   1713 
   1714        if (indexArray)
   1715            indexArray.bindIndexArray(glsDrawTests.DrawTestSpec.Target.ELEMENT_ARRAY);
   1716 
   1717        for (var arrayNdx = 0; arrayNdx < this.m_arrays.length; arrayNdx++) {
   1718            var attribName = '';
   1719            attribName += 'a_' + arrayNdx;
   1720 
   1721            var loc = this.m_ctx.getAttribLocation(this.m_programID, attribName);
   1722 
   1723            if (this.m_arrays[arrayNdx].isBound())
   1724                this.m_ctx.enableVertexAttribArray(loc);
   1725 
   1726            this.m_arrays[arrayNdx].bindAttribute(loc);
   1727        }
   1728 
   1729        if (drawMethod == glsDrawTests.DrawTestSpec.DrawMethod.DRAWARRAYS)
   1730            this.m_ctx.drawArrays(glsDrawTests.primitiveToGL(primitive), firstVertex, vertexCount);
   1731        else if (drawMethod == glsDrawTests.DrawTestSpec.DrawMethod.DRAWARRAYS_INSTANCED)
   1732            this.m_ctx.drawArraysInstanced(glsDrawTests.primitiveToGL(primitive), firstVertex, vertexCount, instanceCount);
   1733        else if (drawMethod == glsDrawTests.DrawTestSpec.DrawMethod.DRAWELEMENTS)
   1734            this.m_ctx.drawElements(glsDrawTests.primitiveToGL(primitive), vertexCount, glsDrawTests.indexTypeToGL(indexType), indexOffset);
   1735        else if (drawMethod == glsDrawTests.DrawTestSpec.DrawMethod.DRAWELEMENTS_RANGED)
   1736            this.m_ctx.drawRangeElements(glsDrawTests.primitiveToGL(primitive), rangeStart, rangeEnd, vertexCount, glsDrawTests.indexTypeToGL(indexType), indexOffset);
   1737        else if (drawMethod == glsDrawTests.DrawTestSpec.DrawMethod.DRAWELEMENTS_INSTANCED)
   1738            this.m_ctx.drawElementsInstanced(glsDrawTests.primitiveToGL(primitive), vertexCount, glsDrawTests.indexTypeToGL(indexType), indexOffset, instanceCount);
   1739        else
   1740            throw new Error('Invalid draw method');
   1741 
   1742        for (var arrayNdx = 0; arrayNdx < this.m_arrays.length; arrayNdx++) {
   1743            if (this.m_arrays[arrayNdx].isBound()) {
   1744                var attribName = '';
   1745                attribName += 'a_' + arrayNdx;
   1746 
   1747                var loc = this.m_ctx.getAttribLocation(this.m_programID, attribName);
   1748 
   1749                this.m_ctx.disableVertexAttribArray(loc);
   1750            }
   1751        }
   1752 
   1753        if (this.m_useVao)
   1754            this.m_ctx.bindVertexArray(null);
   1755 
   1756        this.m_ctx.useProgram(null);
   1757        this.m_screen.readViewport(this.m_ctx, [0 , 0, this.m_screen.getWidth(), this.m_screen.getHeight()]);
   1758    };
   1759 
   1760    // DrawTestSpec
   1761 
   1762    /**
   1763     * @constructor
   1764     */
   1765    glsDrawTests.DrawTestSpec = function() {
   1766        /** @type {?glsDrawTests.DrawTestSpec.Primitive} */ this.primitive = null;
   1767        /** @type {number} */ this.primitiveCount = 0; //!< number of primitives to draw (per instance)
   1768 
   1769        /** @type {?glsDrawTests.DrawTestSpec.DrawMethod} */ this.drawMethod = null;
   1770        /** @type {?glsDrawTests.DrawTestSpec.IndexType} */ this.indexType = null; //!< used only if drawMethod = DrawElements*
   1771        /** @type {number} */ this.indexPointerOffset = 0; //!< used only if drawMethod = DrawElements*
   1772        /** @type {?glsDrawTests.DrawTestSpec.Storage} */ this.indexStorage = null; //!< used only if drawMethod = DrawElements*
   1773        /** @type {number} */ this.first = 0; //!< used only if drawMethod = DrawArrays*
   1774        /** @type {number} */ this.indexMin = 0; //!< used only if drawMethod = Draw*Ranged
   1775        /** @type {number} */ this.indexMax = 0; //!< used only if drawMethod = Draw*Ranged
   1776        /** @type {number} */ this.instanceCount = 0; //!< used only if drawMethod = Draw*Instanced or Draw*Indirect
   1777        /** @type {number} */ this.indirectOffset = 0; //!< used only if drawMethod = Draw*Indirect
   1778        /** @type {number} */ this.baseVertex = 0; //!< used only if drawMethod = DrawElementsIndirect or *BaseVertex
   1779 
   1780        /** @type {Array<glsDrawTests.DrawTestSpec.AttributeSpec>} */ this.attribs = [];
   1781    };
   1782 
   1783    /**
   1784     * @param {glsDrawTests.DrawTestSpec.Target} target
   1785     * @return {string}
   1786     */
   1787    glsDrawTests.DrawTestSpec.targetToString = function(target) {
   1788        assertMsgOptions(target != null, 'Target is null', false, true);
   1789 
   1790        var targets = [
   1791            'element_array', // TARGET_ELEMENT_ARRAY = 0,
   1792            'array' // TARGET_ARRAY,
   1793        ];
   1794        assertMsgOptions(targets.length == Object.keys(glsDrawTests.DrawTestSpec.Target).length,
   1795            'The amount of target names is different than the amount of targets', false, true);
   1796 
   1797        return targets[target];
   1798    };
   1799 
   1800    /**
   1801     * @param {?glsDrawTests.DrawTestSpec.InputType} type
   1802     * @return {string}
   1803     */
   1804    glsDrawTests.DrawTestSpec.inputTypeToString = function(type) {
   1805        assertMsgOptions(type != null, 'Type is null', false, true);
   1806 
   1807        var types = [
   1808            'float', // INPUTTYPE_FLOAT = 0,
   1809 
   1810            'byte', // INPUTTYPE_BYTE,
   1811            'short', // INPUTTYPE_SHORT,
   1812 
   1813            'unsigned_byte', // INPUTTYPE_UNSIGNED_BYTE,
   1814            'unsigned_short', // INPUTTYPE_UNSIGNED_SHORT,
   1815 
   1816            'int', // INPUTTYPE_INT,
   1817            'unsigned_int', // INPUTTYPE_UNSIGNED_INT,
   1818            'half', // INPUTTYPE_HALF,
   1819            'unsigned_int2_10_10_10', // INPUTTYPE_UNSIGNED_INT_2_10_10_10,
   1820            'int2_10_10_10' // INPUTTYPE_INT_2_10_10_10,
   1821        ];
   1822        assertMsgOptions(types.length == Object.keys(glsDrawTests.DrawTestSpec.InputType).length,
   1823            'The amount of type names is different than the amount of types', false, true);
   1824 
   1825        return types[type];
   1826    };
   1827 
   1828    /**
   1829     * @param {?glsDrawTests.DrawTestSpec.OutputType} type
   1830     * @return {string}
   1831     */
   1832    glsDrawTests.DrawTestSpec.outputTypeToString = function(type) {
   1833        assertMsgOptions(type != null, 'Type is null', false, true);
   1834 
   1835        var types = [
   1836            'float', // OUTPUTTYPE_FLOAT = 0,
   1837            'vec2', // OUTPUTTYPE_VEC2,
   1838            'vec3', // OUTPUTTYPE_VEC3,
   1839            'vec4', // OUTPUTTYPE_VEC4,
   1840 
   1841            'int', // OUTPUTTYPE_INT,
   1842            'uint', // OUTPUTTYPE_UINT,
   1843 
   1844            'ivec2', // OUTPUTTYPE_IVEC2,
   1845            'ivec3', // OUTPUTTYPE_IVEC3,
   1846            'ivec4', // OUTPUTTYPE_IVEC4,
   1847 
   1848            'uvec2', // OUTPUTTYPE_UVEC2,
   1849            'uvec3', // OUTPUTTYPE_UVEC3,
   1850            'uvec4' // OUTPUTTYPE_UVEC4,
   1851        ];
   1852        assertMsgOptions(types.length == Object.keys(glsDrawTests.DrawTestSpec.OutputType).length,
   1853            'The amount of type names is different than the amount of types', false, true);
   1854 
   1855        return types[type];
   1856    };
   1857 
   1858    /**
   1859     * @param {?glsDrawTests.DrawTestSpec.Usage} usage
   1860     * @return {string}
   1861     */
   1862    glsDrawTests.DrawTestSpec.usageTypeToString = function(usage) {
   1863        assertMsgOptions(usage != null, 'Usage is null', false, true);
   1864 
   1865        var usages = [
   1866            'dynamic_draw', // USAGE_DYNAMIC_DRAW = 0,
   1867            'static_draw', // USAGE_STATIC_DRAW,
   1868            'stream_draw', // USAGE_STREAM_DRAW,
   1869 
   1870            'stream_read', // USAGE_STREAM_READ,
   1871            'stream_copy', // USAGE_STREAM_COPY,
   1872 
   1873            'static_read', // USAGE_STATIC_READ,
   1874            'static_copy', // USAGE_STATIC_COPY,
   1875 
   1876            'dynamic_read', // USAGE_DYNAMIC_READ,
   1877            'dynamic_copy' // USAGE_DYNAMIC_COPY,
   1878        ];
   1879        assertMsgOptions(usages.length == Object.keys(glsDrawTests.DrawTestSpec.Usage).length,
   1880            'The amount of usage names is different than the amount of usages', false, true);
   1881 
   1882        return usages[usage];
   1883    };
   1884 
   1885    /**
   1886     * @param {?glsDrawTests.DrawTestSpec.Storage} storage
   1887     * @return {string}
   1888     */
   1889    glsDrawTests.DrawTestSpec.storageToString = function(storage) {
   1890        assertMsgOptions(storage != null, 'Storage is null', false, true);
   1891 
   1892        var storages = [
   1893            'user_ptr', // STORAGE_USER = 0,
   1894            'buffer' // STORAGE_BUFFER,
   1895        ];
   1896        assertMsgOptions(storages.length == Object.keys(glsDrawTests.DrawTestSpec.Storage).length,
   1897            'The amount of storage names is different than the amount of storages', false, true);
   1898 
   1899        return storages[storage];
   1900    };
   1901 
   1902    /**
   1903     * @param {glsDrawTests.DrawTestSpec.Primitive} primitive
   1904     * @return {string}
   1905     */
   1906    glsDrawTests.DrawTestSpec.primitiveToString = function(primitive) {
   1907        assertMsgOptions(primitive != null, 'Primitive is null', false, true);
   1908 
   1909        var primitives = [
   1910            'points', // PRIMITIVE_POINTS ,
   1911            'triangles', // PRIMITIVE_TRIANGLES,
   1912            'triangle_fan', // PRIMITIVE_TRIANGLE_FAN,
   1913            'triangle_strip', // PRIMITIVE_TRIANGLE_STRIP,
   1914            'lines', // PRIMITIVE_LINES
   1915            'line_strip', // PRIMITIVE_LINE_STRIP
   1916            'line_loop'
   1917        ];
   1918        assertMsgOptions(primitives.length == Object.keys(glsDrawTests.DrawTestSpec.Primitive).length,
   1919            'The amount of primitive names is different than the amount of primitives', false, true);
   1920 
   1921        return primitives[primitive];
   1922    };
   1923 
   1924    /**
   1925     * @param {?glsDrawTests.DrawTestSpec.IndexType} type
   1926     * @return {string}
   1927     */
   1928    glsDrawTests.DrawTestSpec.indexTypeToString = function(type) {
   1929        assertMsgOptions(type != null, 'Index type is null', false, true);
   1930 
   1931        var indexTypes = [
   1932            'byte', // INDEXTYPE_BYTE = 0,
   1933            'short', // INDEXTYPE_SHORT,
   1934            'int' // INDEXTYPE_INT,
   1935        ];
   1936        assertMsgOptions(indexTypes.length == Object.keys(glsDrawTests.DrawTestSpec.IndexType).length,
   1937            'The amount of index type names is different than the amount of index types', false, true);
   1938 
   1939        return indexTypes[type];
   1940    };
   1941 
   1942    /**
   1943     * @param {?glsDrawTests.DrawTestSpec.DrawMethod} method
   1944     * @return {string}
   1945     */
   1946    glsDrawTests.DrawTestSpec.drawMethodToString = function(method) {
   1947        assertMsgOptions(method != null, 'Method is null', false, true);
   1948 
   1949        var methods = [
   1950            'draw_arrays', //!< DRAWMETHOD_DRAWARRAYS
   1951            'draw_arrays_instanced', //!< DRAWMETHOD_DRAWARRAYS_INSTANCED
   1952            'draw_elements', //!< DRAWMETHOD_DRAWELEMENTS
   1953            'draw_range_elements', //!< DRAWMETHOD_DRAWELEMENTS_RANGED
   1954            'draw_elements_instanced' //!< DRAWMETHOD_DRAWELEMENTS_INSTANCED
   1955        ];
   1956        assertMsgOptions(methods.length == Object.keys(glsDrawTests.DrawTestSpec.DrawMethod).length,
   1957        'The amount of method names is different than the amount of methods', false, true);
   1958 
   1959        return methods[method];
   1960    };
   1961 
   1962    /**
   1963     * @param {?glsDrawTests.DrawTestSpec.InputType} type
   1964     * @return {number}
   1965     */
   1966    glsDrawTests.DrawTestSpec.inputTypeSize = function(type) {
   1967        assertMsgOptions(type != null, 'Input type is null', false, true);
   1968 
   1969        var size = [
   1970            4, // INPUTTYPE_FLOAT = 0,
   1971 
   1972            1, // INPUTTYPE_BYTE,
   1973            2, // INPUTTYPE_SHORT,
   1974 
   1975            1, // INPUTTYPE_UNSIGNED_BYTE,
   1976            2, // INPUTTYPE_UNSIGNED_SHORT,
   1977 
   1978            4, // INPUTTYPE_INT,
   1979            4, // INPUTTYPE_UNSIGNED_INT,
   1980            2, // INPUTTYPE_HALF,
   1981            4 / 4, // INPUTTYPE_UNSIGNED_INT_2_10_10_10,
   1982            4 / 4 // INPUTTYPE_INT_2_10_10_10,
   1983        ];
   1984        assertMsgOptions(size.length == Object.keys(glsDrawTests.DrawTestSpec.InputType).length,
   1985            'The amount of type names is different than the amount of types', false, true);
   1986 
   1987        return size[type];
   1988    };
   1989 
   1990    /**
   1991     * @param {?glsDrawTests.DrawTestSpec.IndexType} type
   1992     * @return {number}
   1993     */
   1994    glsDrawTests.DrawTestSpec.indexTypeSize = function(type) {
   1995        assertMsgOptions(type != null, 'Type is null', false, true);
   1996 
   1997        var size = [
   1998            1, // INDEXTYPE_BYTE,
   1999            2, // INDEXTYPE_SHORT,
   2000            4 // INDEXTYPE_INT,
   2001        ];
   2002        assertMsgOptions(size.length == Object.keys(glsDrawTests.DrawTestSpec.IndexType).length,
   2003            'The amount of type names is different than the amount of types', false, true);
   2004 
   2005        return size[type];
   2006    };
   2007 
   2008    /**
   2009     * @return {string}
   2010     */
   2011    glsDrawTests.DrawTestSpec.prototype.getName = function() {
   2012        /** @type {glsDrawTests.MethodInfo} */ var methodInfo = glsDrawTests.getMethodInfo(this.drawMethod);
   2013        /** @type {boolean} */ var hasFirst = methodInfo.first;
   2014        /** @type {boolean} */ var instanced = methodInfo.instanced;
   2015        /** @type {boolean} */ var ranged = methodInfo.ranged;
   2016        /** @type {boolean} */ var indexed = methodInfo.indexed;
   2017 
   2018        var name = '';
   2019 
   2020        for (var ndx = 0; ndx < this.attribs.length; ++ndx) {
   2021            /** @type {glsDrawTests.DrawTestSpec.AttributeSpec}*/ var attrib = this.attribs[ndx];
   2022 
   2023            if (this.attribs.length > 1)
   2024                name += 'attrib' + ndx + '_';
   2025 
   2026            if (ndx == 0 || attrib.additionalPositionAttribute)
   2027                name += 'pos_';
   2028            else
   2029                name += 'col_';
   2030 
   2031            if (attrib.useDefaultAttribute) {
   2032                name += 'non_array_' +
   2033                    glsDrawTests.DrawTestSpec.inputTypeToString(/** @type {?glsDrawTests.DrawTestSpec.InputType} */ (attrib.inputType)) + '_' +
   2034                    attrib.componentCount + '_' +
   2035                    glsDrawTests.DrawTestSpec.outputTypeToString(attrib.outputType) + '_';
   2036            } else {
   2037                name += glsDrawTests.DrawTestSpec.storageToString(attrib.storage) + '_' +
   2038                    attrib.offset + '_' +
   2039                    attrib.stride + '_' +
   2040                    glsDrawTests.DrawTestSpec.inputTypeToString(/** @type {?glsDrawTests.DrawTestSpec.InputType} */ (attrib.inputType));
   2041                if (attrib.inputType != glsDrawTests.DrawTestSpec.InputType.UNSIGNED_INT_2_10_10_10 && attrib.inputType != glsDrawTests.DrawTestSpec.InputType.INT_2_10_10_10)
   2042                    name += attrib.componentCount;
   2043                name += '_' +
   2044                    (attrib.normalize ? 'normalized_' : '') +
   2045                    glsDrawTests.DrawTestSpec.outputTypeToString(attrib.outputType) + '_' +
   2046                    glsDrawTests.DrawTestSpec.usageTypeToString(attrib.usage) + '_' +
   2047                    attrib.instanceDivisor + '_';
   2048            }
   2049        }
   2050 
   2051        if (indexed)
   2052            name += 'index_' + glsDrawTests.DrawTestSpec.indexTypeToString(this.indexType) + '_' +
   2053                glsDrawTests.DrawTestSpec.storageToString(this.indexStorage) + '_' +
   2054                'offset' + this.indexPointerOffset + '_';
   2055        if (hasFirst)
   2056            name += 'first' + this.first + '_';
   2057        if (ranged)
   2058            name += 'ranged_' + this.indexMin + '_' + this.indexMax + '_';
   2059        if (instanced)
   2060            name += 'instances' + this.instanceCount + '_';
   2061 
   2062        switch (this.primitive) {
   2063            case glsDrawTests.DrawTestSpec.Primitive.POINTS:
   2064                name += 'points_';
   2065                break;
   2066            case glsDrawTests.DrawTestSpec.Primitive.TRIANGLES:
   2067                name += 'triangles_';
   2068                break;
   2069            case glsDrawTests.DrawTestSpec.Primitive.TRIANGLE_FAN:
   2070                name += 'triangle_fan_';
   2071                break;
   2072            case glsDrawTests.DrawTestSpec.Primitive.TRIANGLE_STRIP:
   2073                name += 'triangle_strip_';
   2074                break;
   2075            case glsDrawTests.DrawTestSpec.Primitive.LINES:
   2076                name += 'lines_';
   2077                break;
   2078            case glsDrawTests.DrawTestSpec.Primitive.LINE_STRIP:
   2079                name += 'line_strip_';
   2080                break;
   2081            case glsDrawTests.DrawTestSpec.Primitive.LINE_LOOP:
   2082                name += 'line_loop_';
   2083                break;
   2084            default:
   2085                throw new Error('Invalid primitive');
   2086                break;
   2087        }
   2088 
   2089        name += this.primitiveCount;
   2090 
   2091        return name;
   2092    };
   2093 
   2094    /**
   2095     * @return {string}
   2096     */
   2097    glsDrawTests.DrawTestSpec.prototype.getDesc = function() {
   2098        var desc = '';
   2099 
   2100        for (var ndx = 0; ndx < this.attribs.length; ++ndx) {
   2101            /** @type {glsDrawTests.DrawTestSpec.AttributeSpec} */ var attrib = this.attribs[ndx];
   2102 
   2103            if (attrib.useDefaultAttribute) {
   2104                desc += 'Attribute ' + ndx + ': default, ' + ((ndx == 0 || attrib.additionalPositionAttribute) ? ('position ,') : ('color ,')) +
   2105                    'input datatype ' + glsDrawTests.DrawTestSpec.inputTypeToString(/** @type {?glsDrawTests.DrawTestSpec.InputType} */ (attrib.inputType)) + ', ' +
   2106                    'input component count ' + attrib.componentCount + ', ' +
   2107                    'used as ' + glsDrawTests.DrawTestSpec.outputTypeToString(attrib.outputType) + ', ';
   2108            } else {
   2109                desc += 'Attribute ' + ndx + ': ' + ((ndx == 0 || attrib.additionalPositionAttribute) ? ('position ,') : ('color ,')) +
   2110                    'Storage in ' + glsDrawTests.DrawTestSpec.storageToString(attrib.storage) + ', ' +
   2111                    'stride ' + attrib.stride + ', ' +
   2112                    'input datatype ' + glsDrawTests.DrawTestSpec.inputTypeToString(/** @type {?glsDrawTests.DrawTestSpec.InputType} */ (attrib.inputType)) + ', ' +
   2113                    'input component count ' + attrib.componentCount + ', ' +
   2114                    (attrib.normalize ? 'normalized, ' : '') +
   2115                    'used as ' + glsDrawTests.DrawTestSpec.outputTypeToString(attrib.outputType) + ', ' +
   2116                    'instance divisor ' + attrib.instanceDivisor + ', ';
   2117            }
   2118        }
   2119 
   2120        if (this.drawMethod == glsDrawTests.DrawTestSpec.DrawMethod.DRAWARRAYS) {
   2121            desc += 'drawArrays(), ' +
   2122                'first ' + this.first + ', ';
   2123        } else if (this.drawMethod == glsDrawTests.DrawTestSpec.DrawMethod.DRAWARRAYS_INSTANCED) {
   2124            desc += 'drawArraysInstanced(), ' +
   2125                'first ' + this.first + ', ' +
   2126                'instance count ' + this.instanceCount + ', ';
   2127        } else if (this.drawMethod == glsDrawTests.DrawTestSpec.DrawMethod.DRAWELEMENTS) {
   2128            desc += 'drawElements(), ' +
   2129                'index type ' + glsDrawTests.DrawTestSpec.indexTypeToString(this.indexType) + ', ' +
   2130                'index storage in ' + glsDrawTests.DrawTestSpec.storageToString(this.indexStorage) + ', ' +
   2131                'index offset ' + this.indexPointerOffset + ', ';
   2132        } else if (this.drawMethod == glsDrawTests.DrawTestSpec.DrawMethod.DRAWELEMENTS_RANGED) {
   2133            desc += 'drawElementsRanged(), ' +
   2134                'index type ' + glsDrawTests.DrawTestSpec.indexTypeToString(this.indexType) + ', ' +
   2135                'index storage in ' + glsDrawTests.DrawTestSpec.storageToString(this.indexStorage) + ', ' +
   2136                'index offset ' + this.indexPointerOffset + ', ' +
   2137                'range start ' + this.indexMin + ', ' +
   2138                'range end ' + this.indexMax + ', ';
   2139        } else if (this.drawMethod == glsDrawTests.DrawTestSpec.DrawMethod.DRAWELEMENTS_INSTANCED) {
   2140            desc += 'drawElementsInstanced(), ' +
   2141                'index type ' + glsDrawTests.DrawTestSpec.indexTypeToString(this.indexType) + ', ' +
   2142                'index storage in ' + glsDrawTests.DrawTestSpec.storageToString(this.indexStorage) + ', ' +
   2143                'index offset ' + this.indexPointerOffset + ', ' +
   2144                'instance count ' + this.instanceCount + ', ';
   2145        } else
   2146            throw new Error('Invalid draw method');
   2147 
   2148        desc += this.primitiveCount;
   2149 
   2150        switch (this.primitive) {
   2151            case glsDrawTests.DrawTestSpec.Primitive.POINTS:
   2152                desc += 'points';
   2153                break;
   2154            case glsDrawTests.DrawTestSpec.Primitive.TRIANGLES:
   2155                desc += 'triangles';
   2156                break;
   2157            case glsDrawTests.DrawTestSpec.Primitive.TRIANGLE_FAN:
   2158                desc += 'triangles (fan)';
   2159                break;
   2160            case glsDrawTests.DrawTestSpec.Primitive.TRIANGLE_STRIP:
   2161                desc += 'triangles (strip)';
   2162                break;
   2163            case glsDrawTests.DrawTestSpec.Primitive.LINES:
   2164                desc += 'lines';
   2165                break;
   2166            case glsDrawTests.DrawTestSpec.Primitive.LINE_STRIP:
   2167                desc += 'lines (strip)';
   2168                break;
   2169            case glsDrawTests.DrawTestSpec.Primitive.LINE_LOOP:
   2170                desc += 'lines (loop)';
   2171                break;
   2172            default:
   2173                throw new Error('Invalid primitive');
   2174                break;
   2175        }
   2176 
   2177        return desc;
   2178    };
   2179 
   2180    /**
   2181     * @return {string}
   2182     */
   2183    glsDrawTests.DrawTestSpec.prototype.getMultilineDesc = function() {
   2184        var desc = '';
   2185 
   2186        for (var ndx = 0; ndx < this.attribs.length; ++ndx) {
   2187            /** @type {glsDrawTests.DrawTestSpec.AttributeSpec} */ var attrib = this.attribs[ndx];
   2188 
   2189            if (attrib.useDefaultAttribute) {
   2190                desc += 'Attribute ' + ndx + ': default, ' + ((ndx == 0 || attrib.additionalPositionAttribute) ? ('position\n') : ('color\n')) +
   2191                    '\tinput datatype ' + glsDrawTests.DrawTestSpec.inputTypeToString(/** @type {?glsDrawTests.DrawTestSpec.InputType} */ (attrib.inputType)) + '\n' +
   2192                    '\tinput component count ' + attrib.componentCount + '\n' +
   2193                    '\tused as ' + glsDrawTests.DrawTestSpec.outputTypeToString(attrib.outputType) + '\n';
   2194            } else {
   2195                desc += 'Attribute ' + ndx + ': ' + ((ndx == 0 || attrib.additionalPositionAttribute) ? ('position\n') : ('color\n')) +
   2196                    '\tStorage in ' + glsDrawTests.DrawTestSpec.storageToString(attrib.storage) + '\n' +
   2197                    '\tstride ' + attrib.stride + '\n' +
   2198                    '\tinput datatype ' + glsDrawTests.DrawTestSpec.inputTypeToString(/** @type {?glsDrawTests.DrawTestSpec.InputType} */ (attrib.inputType)) + '\n' +
   2199                    '\tinput component count ' + attrib.componentCount + '\n' +
   2200                    (attrib.normalize ? '\tnormalized\n' : '') +
   2201                    '\tused as ' + glsDrawTests.DrawTestSpec.outputTypeToString(attrib.outputType) + '\n' +
   2202                    '\tinstance divisor ' + attrib.instanceDivisor + '\n';
   2203            }
   2204        }
   2205 
   2206        if (this.drawMethod == glsDrawTests.DrawTestSpec.DrawMethod.DRAWARRAYS) {
   2207            desc += 'drawArrays()\n' +
   2208                '\tfirst ' + this.first + '\n';
   2209        } else if (this.drawMethod == glsDrawTests.DrawTestSpec.DrawMethod.DRAWARRAYS_INSTANCED) {
   2210            desc += 'drawArraysInstanced()\n' +
   2211                '\tfirst ' + this.first + '\n' +
   2212                '\tinstance count ' + this.instanceCount + '\n';
   2213        } else if (this.drawMethod == glsDrawTests.DrawTestSpec.DrawMethod.DRAWELEMENTS) {
   2214            desc += 'drawElements()\n' +
   2215                '\tindex type ' + glsDrawTests.DrawTestSpec.indexTypeToString(this.indexType) + '\n' +
   2216                '\tindex storage in ' + glsDrawTests.DrawTestSpec.storageToString(this.indexStorage) + '\n' +
   2217                '\tindex offset ' + this.indexPointerOffset + '\n';
   2218        } else if (this.drawMethod == glsDrawTests.DrawTestSpec.DrawMethod.DRAWELEMENTS_RANGED) {
   2219            desc += 'drawElementsRanged()\n' +
   2220                '\tindex type ' + glsDrawTests.DrawTestSpec.indexTypeToString(this.indexType) + '\n' +
   2221                '\tindex storage in ' + glsDrawTests.DrawTestSpec.storageToString(this.indexStorage) + '\n' +
   2222                '\tindex offset ' + this.indexPointerOffset + '\n' +
   2223                '\trange start ' + this.indexMin + '\n' +
   2224                '\trange end ' + this.indexMax + '\n';
   2225        } else if (this.drawMethod == glsDrawTests.DrawTestSpec.DrawMethod.DRAWELEMENTS_INSTANCED) {
   2226            desc += 'drawElementsInstanced()\n' +
   2227                '\tindex type ' + glsDrawTests.DrawTestSpec.indexTypeToString(this.indexType) + '\n' +
   2228                '\tindex storage in ' + glsDrawTests.DrawTestSpec.storageToString(this.indexStorage) + '\n' +
   2229                '\tindex offset ' + this.indexPointerOffset + '\n' +
   2230                '\tinstance count ' + this.instanceCount + '\n';
   2231        } else
   2232            throw new Error('Invalid draw method');
   2233 
   2234        desc += '\t' + this.primitiveCount + ' ';
   2235 
   2236        switch (this.primitive) {
   2237            case glsDrawTests.DrawTestSpec.Primitive.POINTS:
   2238                desc += 'points';
   2239                break;
   2240            case glsDrawTests.DrawTestSpec.Primitive.TRIANGLES:
   2241                desc += 'triangles';
   2242                break;
   2243            case glsDrawTests.DrawTestSpec.Primitive.TRIANGLE_FAN:
   2244                desc += 'triangles (fan)';
   2245                break;
   2246            case glsDrawTests.DrawTestSpec.Primitive.TRIANGLE_STRIP:
   2247                desc += 'triangles (strip)';
   2248                break;
   2249            case glsDrawTests.DrawTestSpec.Primitive.LINES:
   2250                desc += 'lines';
   2251                break;
   2252            case glsDrawTests.DrawTestSpec.Primitive.LINE_STRIP:
   2253                desc += 'lines (strip)';
   2254                break;
   2255            case glsDrawTests.DrawTestSpec.Primitive.LINE_LOOP:
   2256                desc += 'lines (loop)';
   2257                break;
   2258            default:
   2259                throw new Error('Invalid primitive');
   2260                break;
   2261        }
   2262 
   2263        desc += '\n';
   2264 
   2265        return desc;
   2266    };
   2267 
   2268    /**
   2269     * @enum {number}
   2270     */
   2271    glsDrawTests.DrawTestSpec.Target = {
   2272        ELEMENT_ARRAY: 0,
   2273        ARRAY: 1
   2274    };
   2275 
   2276    /**
   2277     * @enum {number}
   2278     */
   2279    glsDrawTests.DrawTestSpec.InputType = {
   2280        FLOAT: 0,
   2281 
   2282        BYTE: 1,
   2283        SHORT: 2,
   2284 
   2285        UNSIGNED_BYTE: 3,
   2286        UNSIGNED_SHORT: 4,
   2287 
   2288        INT: 5,
   2289        UNSIGNED_INT: 6,
   2290        HALF: 7,
   2291        UNSIGNED_INT_2_10_10_10: 8,
   2292        INT_2_10_10_10: 9
   2293    };
   2294 
   2295    /**
   2296     * @enum {number}
   2297     */
   2298    glsDrawTests.DrawTestSpec.OutputType = {
   2299        FLOAT: 0,
   2300        VEC2: 1,
   2301        VEC3: 2,
   2302        VEC4: 3,
   2303 
   2304        INT: 4,
   2305        UINT: 5,
   2306 
   2307        IVEC2: 6,
   2308        IVEC3: 7,
   2309        IVEC4: 8,
   2310 
   2311        UVEC2: 9,
   2312        UVEC3: 10,
   2313        UVEC4: 11
   2314    };
   2315 
   2316    /**
   2317     * @enum {number}
   2318     */
   2319    glsDrawTests.DrawTestSpec.Usage = {
   2320        DYNAMIC_DRAW: 0,
   2321        STATIC_DRAW: 1,
   2322        STREAM_DRAW: 2,
   2323 
   2324        STREAM_READ: 3,
   2325        STREAM_COPY: 4,
   2326 
   2327        STATIC_READ: 5,
   2328        STATIC_COPY: 6,
   2329 
   2330        DYNAMIC_READ: 7,
   2331        DYNAMIC_COPY: 8
   2332    };
   2333 
   2334    /**
   2335     * @enum {number}
   2336     */
   2337    glsDrawTests.DrawTestSpec.Storage = {
   2338        USER: 0,
   2339        BUFFER: 1
   2340    };
   2341 
   2342    /**
   2343     * @enum {number}
   2344     */
   2345    glsDrawTests.DrawTestSpec.Primitive = {
   2346        POINTS: 0,
   2347        TRIANGLES: 1,
   2348        TRIANGLE_FAN: 2,
   2349        TRIANGLE_STRIP: 3,
   2350        LINES: 4,
   2351        LINE_STRIP: 5,
   2352        LINE_LOOP: 6
   2353    };
   2354 
   2355    /**
   2356     * @enum {number}
   2357     */
   2358    glsDrawTests.DrawTestSpec.IndexType = {
   2359        BYTE: 0,
   2360        SHORT: 1,
   2361        INT: 2
   2362    };
   2363 
   2364    /**
   2365     * @enum {number}
   2366     */
   2367    glsDrawTests.DrawTestSpec.DrawMethod = {
   2368         DRAWARRAYS: 0,
   2369         DRAWARRAYS_INSTANCED: 1,
   2370         DRAWELEMENTS: 2,
   2371         DRAWELEMENTS_RANGED: 3,
   2372         DRAWELEMENTS_INSTANCED: 4
   2373    };
   2374 
   2375    /**
   2376     * @enum {number}
   2377     */
   2378    glsDrawTests.DrawTestSpec.CompatibilityTestType = {
   2379        NONE: 0,
   2380        UNALIGNED_OFFSET: 1,
   2381        UNALIGNED_STRIDE: 2
   2382    };
   2383 
   2384    /**
   2385     * @return {number}
   2386     */
   2387    glsDrawTests.DrawTestSpec.prototype.hash = function() {
   2388        // Use only drawmode-relevant values in "hashing" as the unrelevant values might not be set (causing non-deterministic behavior).
   2389        /** @type {glsDrawTests.MethodInfo} */ var methodInfo = glsDrawTests.getMethodInfo(this.drawMethod);
   2390        /** @type {boolean} */ var arrayed = methodInfo.first;
   2391        /** @type {boolean} */ var instanced = methodInfo.instanced;
   2392        /** @type {boolean} */ var ranged = methodInfo.ranged;
   2393        /** @type {boolean} */ var indexed = methodInfo.indexed;
   2394 
   2395        /** @type {number} */ var indexHash = (!indexed) ? (0) : (this.indexType + 10 * this.indexPointerOffset + 100 * this.indexStorage);
   2396        /** @type {number} */ var arrayHash = (!arrayed) ? (0) : (this.first);
   2397        /** @type {number} */ var indexRangeHash = (!ranged) ? (0) : (this.indexMin + 10 * this.indexMax);
   2398        /** @type {number} */ var instanceHash = (!instanced) ? (0) : (this.instanceCount);
   2399        /** @type {number} */ var basicHash = this.primitive + 10 * this.primitiveCount + 100 * this.drawMethod;
   2400 
   2401        return indexHash + 3 * arrayHash + 5 * indexRangeHash + 7 * instanceHash + 13 * basicHash + 17 * this.attribs.length + 19 * this.primitiveCount;
   2402    };
   2403 
   2404    /**
   2405     * @return {boolean}
   2406     */
   2407    glsDrawTests.DrawTestSpec.prototype.valid = function() {
   2408        assertMsgOptions(this.primitive != null, 'Primitive is null', false, true);
   2409        assertMsgOptions(this.drawMethod != null, 'Draw method is null', false, true);
   2410 
   2411        var methodInfo = glsDrawTests.getMethodInfo(this.drawMethod);
   2412 
   2413        if (methodInfo.ranged) {
   2414            var maxIndexValue = 0;
   2415            if (this.indexType == glsDrawTests.DrawTestSpec.IndexType.BYTE)
   2416                maxIndexValue = glsDrawTests.GLValue.getMaxValue(glsDrawTests.DrawTestSpec.InputType.UNSIGNED_BYTE).interpret();
   2417            else if (this.indexType == glsDrawTests.DrawTestSpec.IndexType.SHORT)
   2418                maxIndexValue = glsDrawTests.GLValue.getMaxValue(glsDrawTests.DrawTestSpec.InputType.UNSIGNED_SHORT).interpret();
   2419            else if (this.indexType == glsDrawTests.DrawTestSpec.IndexType.INT)
   2420                maxIndexValue = glsDrawTests.GLValue.getMaxValue(glsDrawTests.DrawTestSpec.InputType.UNSIGNED_INT).interpret();
   2421            else
   2422                throw new Error('Invalid index type');
   2423 
   2424            if (this.indexMin > this.indexMax)
   2425                return false;
   2426            if (this.indexMin < 0 || this.indexMax < 0)
   2427                return false;
   2428            if (this.indexMin > maxIndexValue || this.indexMax > maxIndexValue)
   2429                return false;
   2430        }
   2431 
   2432        if (methodInfo.first && this.first < 0)
   2433            return false;
   2434 
   2435        return true;
   2436    };
   2437 
   2438    /**
   2439     * @return {glsDrawTests.DrawTestSpec.CompatibilityTestType}
   2440     */
   2441    glsDrawTests.DrawTestSpec.prototype.isCompatibilityTest = function() {
   2442        var methodInfo = glsDrawTests.getMethodInfo(this.drawMethod);
   2443 
   2444        var bufferAlignmentBad = false;
   2445        var strideAlignmentBad = false;
   2446 
   2447        // Attribute buffer alignment
   2448        for (var ndx = 0; ndx < this.attribs.length; ++ndx)
   2449            if (!this.attribs[ndx].isBufferAligned())
   2450                bufferAlignmentBad = true;
   2451 
   2452        // Attribute stride alignment
   2453        for (var ndx = 0; ndx < this.attribs.length; ++ndx)
   2454            if (!this.attribs[ndx].isBufferStrideAligned())
   2455                strideAlignmentBad = true;
   2456 
   2457        // Index buffer alignment
   2458        if (methodInfo.indexed) {
   2459            if (this.indexStorage == glsDrawTests.DrawTestSpec.Storage.BUFFER) {
   2460                var indexSize = 0;
   2461                if (this.indexType == glsDrawTests.DrawTestSpec.IndexType.BYTE)
   2462                    indexSize = 1;
   2463                else if (this.indexType == glsDrawTests.DrawTestSpec.IndexType.SHORT)
   2464                    indexSize = 2;
   2465                else if (this.indexType == glsDrawTests.DrawTestSpec.IndexType.INT)
   2466                    indexSize = 4;
   2467                else
   2468                    throw new Error('');
   2469 
   2470                if (this.indexPointerOffset % indexSize != 0)
   2471                    bufferAlignmentBad = true;
   2472            }
   2473        }
   2474 
   2475        // \note combination bad alignment & stride is treated as bad offset
   2476        if (bufferAlignmentBad)
   2477            return glsDrawTests.DrawTestSpec.CompatibilityTestType.UNALIGNED_OFFSET;
   2478        else if (strideAlignmentBad)
   2479            return glsDrawTests.DrawTestSpec.CompatibilityTestType.UNALIGNED_STRIDE;
   2480        else
   2481            return glsDrawTests.DrawTestSpec.CompatibilityTestType.NONE;
   2482    };
   2483 
   2484    // DrawTestSpec.AttributeSpec
   2485 
   2486    /**
   2487     * @constructor
   2488     */
   2489    glsDrawTests.DrawTestSpec.AttributeSpec = function() {
   2490        /** @type {?glsDrawTests.DrawTestSpec.InputType} */ this.inputType = null;
   2491        /** @type {?glsDrawTests.DrawTestSpec.OutputType} */ this.outputType = null;
   2492        /** @type {?glsDrawTests.DrawTestSpec.Storage} */ this.storage = glsDrawTests.DrawTestSpec.Storage.BUFFER; //Always BUFFER in WebGL up to 2
   2493        /** @type {?glsDrawTests.DrawTestSpec.Usage} */ this.usage = null;
   2494        /** @type {number} */ this.componentCount = 0;
   2495        /** @type {number} */ this.offset = 0;
   2496        /** @type {number} */ this.stride = 0;
   2497        /** @type {boolean} */ this.normalize = false;
   2498        /** @type {number} */ this.instanceDivisor = 0; //!< used only if drawMethod = Draw*Instanced
   2499        /** @type {boolean} */ this.useDefaultAttribute = false;
   2500 
   2501        /** @type {boolean} */ this.additionalPositionAttribute = false; //!< treat this attribute as position attribute. Attribute at index 0 is alway treated as such. False by default
   2502    };
   2503 
   2504    /**
   2505     * @param {?glsDrawTests.DrawTestSpec.InputType} inputType
   2506     * @param {?glsDrawTests.DrawTestSpec.OutputType} outputType
   2507     * @param {?glsDrawTests.DrawTestSpec.Storage} storage
   2508     * @param {?glsDrawTests.DrawTestSpec.Usage} usage
   2509     * @param {number} componentCount
   2510     * @param {number} offset
   2511     * @param {number} stride
   2512     * @param {boolean} normalize
   2513     * @param {number} instanceDivisor
   2514     * @return {glsDrawTests.DrawTestSpec.AttributeSpec}
   2515     */
   2516    glsDrawTests.DrawTestSpec.AttributeSpec.createAttributeArray = function(inputType, outputType, storage, usage, componentCount,
   2517        offset, stride, normalize, instanceDivisor) {
   2518        /** @type {glsDrawTests.DrawTestSpec.AttributeSpec} */ var spec;
   2519 
   2520        spec.inputType = inputType;
   2521        spec.outputType = outputType;
   2522        spec.storage = storage;
   2523        spec.usage = usage;
   2524        spec.componentCount = componentCount;
   2525        spec.offset = offset;
   2526        spec.stride = stride;
   2527        spec.normalize = normalize;
   2528        spec.instanceDivisor = instanceDivisor;
   2529 
   2530        spec.useDefaultAttribute = false;
   2531 
   2532        return spec;
   2533    };
   2534 
   2535    /**
   2536     * @param {?glsDrawTests.DrawTestSpec.InputType} inputType
   2537     * @param {?glsDrawTests.DrawTestSpec.OutputType} outputType
   2538     * @param {number} componentCount
   2539     * @return {glsDrawTests.DrawTestSpec.AttributeSpec}
   2540     */
   2541    glsDrawTests.DrawTestSpec.AttributeSpec.createDefaultAttribute = function(inputType, outputType, componentCount) {
   2542        assertMsgOptions(inputType == glsDrawTests.DrawTestSpec.InputType.INT || inputType == glsDrawTests.DrawTestSpec.InputType.UNSIGNED_INT || inputType == glsDrawTests.DrawTestSpec.InputType.FLOAT, 'Invalid input type', false, true);
   2543        assertMsgOptions(inputType == glsDrawTests.DrawTestSpec.InputType.FLOAT || componentCount == 4, 'If not float, input type should have 4 components', false, true);
   2544 
   2545        /** @type {glsDrawTests.DrawTestSpec.AttributeSpec} */ var spec;
   2546 
   2547        spec.inputType = inputType;
   2548        spec.outputType = outputType;
   2549        spec.storage = glsDrawTests.DrawTestSpec.Storage.BUFFER; //Always BUFFER in WebGL up to 2
   2550        spec.usage = null;
   2551        spec.componentCount = componentCount;
   2552        spec.offset = 0;
   2553        spec.stride = 0;
   2554        spec.normalize = false;
   2555        spec.instanceDivisor = 0;
   2556 
   2557        spec.useDefaultAttribute = true;
   2558 
   2559        return spec;
   2560    };
   2561 
   2562    /**
   2563     * @return {number}
   2564     */
   2565    glsDrawTests.DrawTestSpec.AttributeSpec.prototype.hash = function() {
   2566        if (this.useDefaultAttribute) {
   2567            return 1 * this.inputType + 7 * this.outputType + 13 * this.componentCount;
   2568        } else {
   2569            return 1 * this.inputType + 2 * this.outputType + 3 * this.storage + 5 * this.usage + 7 * this.componentCount + 11 * this.offset + 13 * this.stride + 17 * (this.normalize ? 0 : 1) + 19 * this.instanceDivisor;
   2570        }
   2571    };
   2572 
   2573    /**
   2574     * @return {boolean}
   2575     */
   2576    glsDrawTests.DrawTestSpec.AttributeSpec.prototype.valid = function(/*ctxType*/) {
   2577        /** @type {boolean} */ var inputTypeFloat = this.inputType == glsDrawTests.DrawTestSpec.InputType.FLOAT || this.inputType == glsDrawTests.DrawTestSpec.InputType.HALF;
   2578        /** @type {boolean} */ var inputTypeUnsignedInteger = this.inputType == glsDrawTests.DrawTestSpec.InputType.UNSIGNED_BYTE || this.inputType == glsDrawTests.DrawTestSpec.InputType.UNSIGNED_SHORT || this.inputType == glsDrawTests.DrawTestSpec.InputType.UNSIGNED_INT || this.inputType == glsDrawTests.DrawTestSpec.InputType.UNSIGNED_INT_2_10_10_10;
   2579        /** @type {boolean} */ var inputTypeSignedInteger = this.inputType == glsDrawTests.DrawTestSpec.InputType.BYTE || this.inputType == glsDrawTests.DrawTestSpec.InputType.SHORT || this.inputType == glsDrawTests.DrawTestSpec.InputType.INT || this.inputType == glsDrawTests.DrawTestSpec.InputType.INT_2_10_10_10;
   2580        /** @type {boolean} */ var inputTypePacked = this.inputType == glsDrawTests.DrawTestSpec.InputType.UNSIGNED_INT_2_10_10_10 || this.inputType == glsDrawTests.DrawTestSpec.InputType.INT_2_10_10_10;
   2581 
   2582        /** @type {boolean} */ var outputTypeFloat = this.outputType == glsDrawTests.DrawTestSpec.OutputType.FLOAT || this.outputType == glsDrawTests.DrawTestSpec.OutputType.VEC2 || this.outputType == glsDrawTests.DrawTestSpec.OutputType.VEC3 || this.outputType == glsDrawTests.DrawTestSpec.OutputType.VEC4;
   2583        /** @type {boolean} */ var outputTypeSignedInteger = this.outputType == glsDrawTests.DrawTestSpec.OutputType.INT || this.outputType == glsDrawTests.DrawTestSpec.OutputType.IVEC2 || this.outputType == glsDrawTests.DrawTestSpec.OutputType.IVEC3 || this.outputType == glsDrawTests.DrawTestSpec.OutputType.IVEC4;
   2584        /** @type {boolean} */ var outputTypeUnsignedInteger = this.outputType == glsDrawTests.DrawTestSpec.OutputType.UINT || this.outputType == glsDrawTests.DrawTestSpec.OutputType.UVEC2 || this.outputType == glsDrawTests.DrawTestSpec.OutputType.UVEC3 || this.outputType == glsDrawTests.DrawTestSpec.OutputType.UVEC4;
   2585 
   2586        if (this.useDefaultAttribute) {
   2587            if (this.inputType != glsDrawTests.DrawTestSpec.InputType.INT && this.inputType != glsDrawTests.DrawTestSpec.InputType.UNSIGNED_INT && this.inputType != glsDrawTests.DrawTestSpec.InputType.FLOAT)
   2588                return false;
   2589 
   2590            if (this.inputType != glsDrawTests.DrawTestSpec.InputType.FLOAT && this.componentCount != 4)
   2591                return false;
   2592 
   2593            // no casting allowed (undefined results)
   2594            if (this.inputType == glsDrawTests.DrawTestSpec.InputType.INT && !outputTypeSignedInteger)
   2595                return false;
   2596            if (this.inputType == glsDrawTests.DrawTestSpec.InputType.UNSIGNED_INT && !outputTypeUnsignedInteger)
   2597                return false;
   2598        }
   2599 
   2600        if (inputTypePacked && this.componentCount != 4)
   2601            return false;
   2602 
   2603        // Invalid conversions:
   2604 
   2605        // float -> [u]int
   2606        if (inputTypeFloat && !outputTypeFloat)
   2607            return false;
   2608 
   2609        // uint -> int (undefined results)
   2610        if (inputTypeUnsignedInteger && outputTypeSignedInteger)
   2611            return false;
   2612 
   2613        // int -> uint (undefined results)
   2614        if (inputTypeSignedInteger && outputTypeUnsignedInteger)
   2615            return false;
   2616 
   2617        // packed -> non-float (packed formats are converted to floats)
   2618        if (inputTypePacked && !outputTypeFloat)
   2619            return false;
   2620 
   2621        // Invalid normalize. Normalize is only valid if output type is float
   2622        if (this.normalize && !outputTypeFloat)
   2623            return false;
   2624 
   2625        return true;
   2626    };
   2627 
   2628    /**
   2629     * @return {boolean}
   2630     */
   2631    glsDrawTests.DrawTestSpec.AttributeSpec.prototype.isBufferAligned = function() {
   2632        var inputTypePacked = this.inputType == glsDrawTests.DrawTestSpec.InputType.UNSIGNED_INT_2_10_10_10 || this.inputType == glsDrawTests.DrawTestSpec.InputType.INT_2_10_10_10;
   2633 
   2634        // Buffer alignment, offset is a multiple of underlying data type size?
   2635        if (this.storage == glsDrawTests.DrawTestSpec.Storage.BUFFER) {
   2636            var dataTypeSize = glsDrawTests.DrawTestSpec.inputTypeSize(this.inputType);
   2637            if (inputTypePacked)
   2638                dataTypeSize = 4;
   2639 
   2640            if (this.offset % dataTypeSize != 0)
   2641                return false;
   2642        }
   2643 
   2644        return true;
   2645    };
   2646 
   2647    /**
   2648     * @return {boolean}
   2649     */
   2650    glsDrawTests.DrawTestSpec.AttributeSpec.prototype.isBufferStrideAligned = function() {
   2651        var inputTypePacked = this.inputType == glsDrawTests.DrawTestSpec.InputType.UNSIGNED_INT_2_10_10_10 || this.inputType == glsDrawTests.DrawTestSpec.InputType.INT_2_10_10_10;
   2652 
   2653        // Buffer alignment, offset is a multiple of underlying data type size?
   2654        if (this.storage == glsDrawTests.DrawTestSpec.Storage.BUFFER) {
   2655            var dataTypeSize = glsDrawTests.DrawTestSpec.inputTypeSize(this.inputType);
   2656            if (inputTypePacked)
   2657                dataTypeSize = 4;
   2658 
   2659            if (this.stride % dataTypeSize != 0)
   2660                return false;
   2661        }
   2662 
   2663        return true;
   2664    };
   2665 
   2666    /**
   2667     * @constructor
   2668     * @extends {tcuTestCase.DeqpTest}
   2669     * @param {glsDrawTests.DrawTestSpec} spec
   2670     * @param {string} name
   2671     * @param {string} desc
   2672     */
   2673    glsDrawTests.DrawTest = function(spec, name, desc) {
   2674        tcuTestCase.DeqpTest.call(this, name, desc, spec);
   2675 
   2676        /** @type {WebGL2RenderingContext} */ this.m_renderCtx = gl;
   2677        /** @type {tcuPixelFormat.PixelFormat} */ this.m_pixelformat = new tcuPixelFormat.PixelFormat(
   2678            /** @type {number} */ (gl.getParameter(gl.RED_BITS)), /** @type {number} */ (gl.getParameter(gl.GREEN_BITS)),
   2679            /** @type {number} */ (gl.getParameter(gl.BLUE_BITS)), /** @type {number} */ (gl.getParameter(gl.ALPHA_BITS))
   2680        );
   2681 
   2682        /** @type {sglrReferenceContext.ReferenceContextBuffers} */ this.m_refBuffers = null;
   2683        /** @type {sglrReferenceContext.ReferenceContext} */ this.m_refContext = null;
   2684        /** @type {sglrGLContext.GLContext} */ this.m_glesContext = null;
   2685 
   2686        /** @type {glsDrawTests.AttributePack} */ this.m_glArrayPack = null;
   2687        /** @type {glsDrawTests.AttributePack} */ this.m_rrArrayPack = null;
   2688 
   2689        /** @type {number} */ this.m_maxDiffRed = -1;
   2690        /** @type {number} */ this.m_maxDiffGreen = -1;
   2691        /** @type {number} */ this.m_maxDiffBlue = -1;
   2692 
   2693        /** @type {Array<glsDrawTests.DrawTestSpec>} */ this.m_specs = [];
   2694        /** @type {Array<string>} */this.m_iteration_descriptions = [];
   2695        /** @type {number} */ this.m_iteration = 0;
   2696 
   2697        if (spec)
   2698            this.addIteration(spec);
   2699    };
   2700 
   2701    glsDrawTests.DrawTest.prototype = Object.create(tcuTestCase.DeqpTest.prototype);
   2702    glsDrawTests.DrawTest.prototype.constructor = glsDrawTests.DrawTest;
   2703 
   2704    /**
   2705     * @param {glsDrawTests.DrawTestSpec} spec
   2706     * @param {string=} description
   2707     */
   2708    glsDrawTests.DrawTest.prototype.addIteration = function(spec, description) {
   2709        // Validate spec
   2710        /** @type {boolean} */ var validSpec = spec.valid();
   2711 
   2712        if (!validSpec)
   2713            return;
   2714 
   2715        this.m_specs.push(spec);
   2716 
   2717        if (description)
   2718            this.m_iteration_descriptions.push(description);
   2719        else
   2720            this.m_iteration_descriptions.push('');
   2721    };
   2722 
   2723    /**
   2724     * init
   2725     */
   2726    glsDrawTests.DrawTest.prototype.init = function() {
   2727        var renderTargetWidth = Math.min(glsDrawTests.MAX_RENDER_TARGET_SIZE, gl.canvas.width);
   2728        var renderTargetHeight = Math.min(glsDrawTests.MAX_RENDER_TARGET_SIZE, gl.canvas.height);
   2729        /** @type {sglrReferenceContext.ReferenceContextLimits} */ var limits = new sglrReferenceContext.ReferenceContextLimits(gl);
   2730        /** @type {boolean} */ var useVao = true;
   2731 
   2732        this.m_glesContext = new sglrGLContext.GLContext(gl);
   2733 
   2734        assertMsgOptions(this.m_specs.length > 0, 'Specs is empty', false, true);
   2735 
   2736        this.m_refBuffers = new sglrReferenceContext.ReferenceContextBuffers(this.m_pixelformat, 0, 0, renderTargetWidth, renderTargetHeight);
   2737        this.m_refContext = new sglrReferenceContext.ReferenceContext(limits, this.m_refBuffers.getColorbuffer(), this.m_refBuffers.getDepthbuffer(), this.m_refBuffers.getStencilbuffer());
   2738 
   2739        this.m_glArrayPack = new glsDrawTests.AttributePack(this.m_glesContext, [renderTargetWidth, renderTargetHeight], useVao, true);
   2740        this.m_rrArrayPack = new glsDrawTests.AttributePack(this.m_refContext, [renderTargetWidth, renderTargetHeight], useVao, false);
   2741 
   2742        this.m_maxDiffRed = Math.ceil(256.0 * (15.0 / (1 << this.m_pixelformat.redBits)));
   2743        this.m_maxDiffGreen = Math.ceil(256.0 * (15.0 / (1 << this.m_pixelformat.greenBits)));
   2744        this.m_maxDiffBlue = Math.ceil(256.0 * (15.0 / (1 << this.m_pixelformat.blueBits)));
   2745    };
   2746 
   2747    /**
   2748     * @return {tcuTestCase.IterateResult}
   2749     */
   2750    glsDrawTests.DrawTest.prototype.iterate = function() {
   2751        var specNdx = Math.floor(this.m_iteration / 2);
   2752        var drawStep = (this.m_iteration % 2) == 0;
   2753        var compareStep = (this.m_iteration % 2) == 1;
   2754        /** @type {tcuTestCase.IterateResult} */ var iterateResult = (this.m_iteration + 1 == this.m_specs.length * 2) ? (tcuTestCase.IterateResult.STOP) : (tcuTestCase.IterateResult.CONTINUE);
   2755        /** @type {glsDrawTests.DrawTestSpec} */ var spec = this.m_specs[specNdx];
   2756        var updateProgram = (this.m_iteration == 0) || (drawStep && !glsDrawTests.checkSpecsShaderCompatible(this.m_specs[specNdx], this.m_specs[specNdx - 1])); // try to use the same shader in all iterations
   2757 
   2758        if (drawStep && this.m_specs.length != 1)
   2759            debug('Iteration ' + specNdx + ' of ' + (this.m_specs.length - 1) + ': ' + this.m_iteration_descriptions[specNdx]);
   2760 
   2761        this.m_iteration++;
   2762 
   2763        if (drawStep) {
   2764            /** @type {glsDrawTests.MethodInfo} */ var methodInfo = glsDrawTests.getMethodInfo(spec.drawMethod);
   2765            /** @type {boolean} */ var indexed = methodInfo.indexed;
   2766            /** @type {boolean} */ var instanced = methodInfo.instanced;
   2767            /** @type {boolean} */ var ranged = methodInfo.ranged;
   2768            /** @type {boolean} */ var hasFirst = methodInfo.first;
   2769 
   2770            /** @type {number} */ var primitiveElementCount = glsDrawTests.getElementCount(spec.primitive, spec.primitiveCount); // !< elements to be drawn
   2771            /** @type {number} */ var indexMin = (ranged) ? (spec.indexMin) : (0);
   2772            /** @type {number} */ var firstAddition = (hasFirst) ? (spec.first) : (0);
   2773            /** @type {number} */ var elementCount = primitiveElementCount + indexMin + firstAddition; // !< elements in buffer (buffer should have at least primitiveElementCount ACCESSIBLE (index range, first) elements)
   2774            /** @type {number} */ var maxElementIndex = primitiveElementCount + indexMin + firstAddition - 1;
   2775            /** @type {number} */ var indexMax = Math.max(0, (ranged) ? (deMath.clamp(spec.indexMax, 0, maxElementIndex)) : (maxElementIndex));
   2776            /** @type {number} */ var coordScale = this.getCoordScale(spec);
   2777            /** @type {number} */ var colorScale = this.getColorScale(spec);
   2778 
   2779            /** @type {Array<number>} */ var nullAttribValue = [];
   2780 
   2781            // Log info
   2782            bufferedLogToConsole(spec.getMultilineDesc());
   2783 
   2784            // Data
   2785            this.m_glArrayPack.clearArrays();
   2786            this.m_rrArrayPack.clearArrays();
   2787 
   2788            // indices
   2789            /** @type {number} */ var seed;
   2790            /** @type {number} */ var indexElementSize;
   2791            /** @type {number} */ var indexArraySize;
   2792            /** @type {goog.TypedArray} */ var indexArray;
   2793            /** @type {goog.TypedArray} */ var indexPointer;
   2794 
   2795            /** @type {glsDrawTests.AttributeArray}*/ var glArray;
   2796            /** @type {glsDrawTests.AttributeArray}*/ var rrArray;
   2797 
   2798            if (indexed) {
   2799                seed = spec.hash();
   2800                indexElementSize = glsDrawTests.DrawTestSpec.indexTypeSize(spec.indexType);
   2801                indexArraySize = spec.indexPointerOffset + indexElementSize * elementCount;
   2802                indexArray = glsDrawTests.RandomArrayGenerator.generateIndices(seed, elementCount, spec.indexType, spec.indexPointerOffset, indexMin, indexMax);
   2803                indexPointer = indexArray.subarray(spec.indexPointerOffset);
   2804 
   2805                glArray = new glsDrawTests.AttributeArray(spec.indexStorage, this.m_glesContext);
   2806                rrArray = new glsDrawTests.AttributeArray(spec.indexStorage, this.m_refContext);
   2807 
   2808                glArray.data(glsDrawTests.DrawTestSpec.Target.ELEMENT_ARRAY, indexArraySize, indexArray, glsDrawTests.DrawTestSpec.Usage.STATIC_DRAW);
   2809                rrArray.data(glsDrawTests.DrawTestSpec.Target.ELEMENT_ARRAY, indexArraySize, indexArray, glsDrawTests.DrawTestSpec.Usage.STATIC_DRAW);
   2810 
   2811                indexArray = null;
   2812            }
   2813 
   2814            // attributes
   2815            for (var attribNdx = 0; attribNdx < spec.attribs.length; attribNdx++) {
   2816                /** @type {glsDrawTests.DrawTestSpec.AttributeSpec} */ var attribSpec = spec.attribs[attribNdx];
   2817                var isPositionAttr = (attribNdx == 0) || (attribSpec.additionalPositionAttribute);
   2818 
   2819                if (attribSpec.useDefaultAttribute) {
   2820                    seed = 10 * attribSpec.hash() + 100 * spec.hash() + attribNdx;
   2821                    /** @type {Array<number>} */ var attribValue = glsDrawTests.RandomArrayGenerator.generateAttributeValue(seed, attribSpec.inputType);
   2822 
   2823                    // changed USER for BUFFER in JS version
   2824                    this.m_glArrayPack.newArray(glsDrawTests.DrawTestSpec.Storage.BUFFER);
   2825                    this.m_rrArrayPack.newArray(glsDrawTests.DrawTestSpec.Storage.BUFFER);
   2826 
   2827                    this.m_glArrayPack.getArray(attribNdx).setupArray(false, 0, attribSpec.componentCount, attribSpec.inputType, attribSpec.outputType, false, 0, 0, attribValue, isPositionAttr);
   2828                    this.m_rrArrayPack.getArray(attribNdx).setupArray(false, 0, attribSpec.componentCount, attribSpec.inputType, attribSpec.outputType, false, 0, 0, attribValue, isPositionAttr);
   2829                } else {
   2830                    seed = attribSpec.hash() + 100 * spec.hash() + attribNdx;
   2831                    /** @type {number} */ var elementSize = attribSpec.componentCount * glsDrawTests.DrawTestSpec.inputTypeSize(attribSpec.inputType);
   2832                    /** @type {number} */ var stride = (attribSpec.stride == 0) ? (elementSize) : (attribSpec.stride);
   2833                    /** @type {number} */ var evaluatedElementCount = (instanced && attribSpec.instanceDivisor > 0) ? (spec.instanceCount / attribSpec.instanceDivisor + 1) : (elementCount);
   2834                    /** @type {number} */ var referencedElementCount = (ranged) ? (Math.max(evaluatedElementCount, spec.indexMax + 1)) : (evaluatedElementCount);
   2835                    /** @type {number} */ var bufferSize = attribSpec.offset + stride * (referencedElementCount - 1) + elementSize;
   2836                    /** @type {goog.TypedArray} */ var data = glsDrawTests.RandomArrayGenerator.createArray(
   2837                        seed,
   2838                        referencedElementCount,
   2839                        attribSpec.componentCount,
   2840                        attribSpec.offset,
   2841                        stride,
   2842                        attribSpec.inputType,
   2843                        indexed ? 0 : spec.first,
   2844                        spec.primitive,
   2845                        indexed ? indexPointer : null,
   2846                        indexElementSize
   2847                    );
   2848 
   2849                    this.m_glArrayPack.newArray(attribSpec.storage);
   2850                    this.m_rrArrayPack.newArray(attribSpec.storage);
   2851 
   2852                    this.m_glArrayPack.getArray(attribNdx).data(glsDrawTests.DrawTestSpec.Target.ARRAY, bufferSize, data, attribSpec.usage);
   2853                    this.m_rrArrayPack.getArray(attribNdx).data(glsDrawTests.DrawTestSpec.Target.ARRAY, bufferSize, data, attribSpec.usage);
   2854 
   2855                    this.m_glArrayPack.getArray(attribNdx).setupArray(true, attribSpec.offset, attribSpec.componentCount, attribSpec.inputType, attribSpec.outputType, attribSpec.normalize, attribSpec.stride, attribSpec.instanceDivisor, nullAttribValue, isPositionAttr);
   2856                    this.m_rrArrayPack.getArray(attribNdx).setupArray(true, attribSpec.offset, attribSpec.componentCount, attribSpec.inputType, attribSpec.outputType, attribSpec.normalize, attribSpec.stride, attribSpec.instanceDivisor, nullAttribValue, isPositionAttr);
   2857 
   2858                    data = null;
   2859                }
   2860            }
   2861 
   2862            // Shader program
   2863            if (updateProgram) {
   2864                this.m_glArrayPack.updateProgram();
   2865                this.m_rrArrayPack.updateProgram();
   2866            }
   2867 
   2868            /** @type {glsDrawTests.DrawTestSpec.CompatibilityTestType} */ var ctype;
   2869 
   2870            // Draw
   2871            try {
   2872                // indices
   2873                if (indexed) {
   2874                    this.m_glArrayPack.render(spec.primitive, spec.drawMethod, 0, primitiveElementCount, spec.indexType, spec.indexPointerOffset, spec.indexMin, spec.indexMax, spec.instanceCount, coordScale, colorScale, glArray);
   2875                    this.m_rrArrayPack.render(spec.primitive, spec.drawMethod, 0, primitiveElementCount, spec.indexType, spec.indexPointerOffset, spec.indexMin, spec.indexMax, spec.instanceCount, coordScale, colorScale, rrArray);
   2876                } else {
   2877                    this.m_glArrayPack.render(spec.primitive, spec.drawMethod, spec.first, primitiveElementCount, null, 0, 0, 0, spec.instanceCount, coordScale, colorScale, null);
   2878                    this.m_rrArrayPack.render(spec.primitive, spec.drawMethod, spec.first, primitiveElementCount, null, 0, 0, 0, spec.instanceCount, coordScale, colorScale, null);
   2879                }
   2880            } catch (err) {
   2881                if (err instanceof wtu.GLErrorException) {
   2882                    // GL Errors are ok if the mode is not properly aligned
   2883                    ctype = spec.isCompatibilityTest();
   2884 
   2885                    bufferedLogToConsole('Got error: ' + err.message);
   2886 
   2887                    if (ctype == glsDrawTests.DrawTestSpec.CompatibilityTestType.UNALIGNED_OFFSET)
   2888                        checkMessage(false, 'Failed to draw with unaligned buffers.');
   2889                    else if (ctype == glsDrawTests.DrawTestSpec.CompatibilityTestType.UNALIGNED_STRIDE)
   2890                        checkMessage(false, 'Failed to draw with unaligned stride.');
   2891                    else
   2892                        throw err;
   2893                }
   2894            }
   2895        } else if (compareStep) {
   2896            if (!this.compare(spec.primitive)) {
   2897                ctype = spec.isCompatibilityTest();
   2898 
   2899                if (ctype == glsDrawTests.DrawTestSpec.CompatibilityTestType.UNALIGNED_OFFSET)
   2900                    checkMessage(false, 'Failed to draw with unaligned buffers.');
   2901                else if (ctype == glsDrawTests.DrawTestSpec.CompatibilityTestType.UNALIGNED_STRIDE)
   2902                    checkMessage(false, 'Failed to draw with unaligned stride.');
   2903                else
   2904                    testFailedOptions('Image comparison failed.', false);
   2905                return iterateResult;
   2906            }
   2907        } else {
   2908            testFailedOptions('Image comparison failed.', false);
   2909            return tcuTestCase.IterateResult.STOP;
   2910        }
   2911 
   2912        if (iterateResult == tcuTestCase.IterateResult.STOP)
   2913            testPassed('');
   2914 
   2915        return iterateResult;
   2916    };
   2917 
   2918    /**
   2919     * @enum {number} PrimitiveClass
   2920     */
   2921    glsDrawTests.PrimitiveClass = {
   2922        POINT: 0,
   2923        LINE: 1,
   2924        TRIANGLE: 2
   2925    };
   2926 
   2927    /**
   2928     * @param {?glsDrawTests.DrawTestSpec.Primitive} primitiveType
   2929     * @return {glsDrawTests.PrimitiveClass}
   2930     */
   2931    glsDrawTests.getDrawPrimitiveClass = function(primitiveType) {
   2932        switch (primitiveType) {
   2933            case glsDrawTests.DrawTestSpec.Primitive.POINTS:
   2934                return glsDrawTests.PrimitiveClass.POINT;
   2935 
   2936            case glsDrawTests.DrawTestSpec.Primitive.LINES:
   2937            case glsDrawTests.DrawTestSpec.Primitive.LINE_STRIP:
   2938            case glsDrawTests.DrawTestSpec.Primitive.LINE_LOOP:
   2939                return glsDrawTests.PrimitiveClass.LINE;
   2940 
   2941            case glsDrawTests.DrawTestSpec.Primitive.TRIANGLES:
   2942            case glsDrawTests.DrawTestSpec.Primitive.TRIANGLE_FAN:
   2943            case glsDrawTests.DrawTestSpec.Primitive.TRIANGLE_STRIP:
   2944                return glsDrawTests.PrimitiveClass.TRIANGLE;
   2945 
   2946            default:
   2947                throw new Error('Invalid primitive type');
   2948        }
   2949    };
   2950 
   2951    /**
   2952     * @param {number} c1
   2953     * @param {number} c2
   2954     * @param {Array<number>} threshold
   2955     * @return {boolean}
   2956     */
   2957    glsDrawTests.compareUintRGB8 = function(c1, c2, threshold) {
   2958        return (Math.abs(((c1 >> 16) & 0xff) - ((c2 >> 16) & 0xff)) <= threshold[0] &&  // Red
   2959                Math.abs(((c1 >> 8) & 0xff) - ((c2 >> 8) & 0xff)) <= threshold[1] &&    // Green
   2960                Math.abs((c1 & 0xff) - (c2 & 0xff)) <= threshold[2]);                   // Blue
   2961    };
   2962 
   2963    /**
   2964     * @param {number} c1
   2965     * @param {number} c2
   2966     * @param {number} c3
   2967     * @param {number} renderTargetDifference
   2968     * @return {boolean}
   2969     */
   2970    glsDrawTests.isEdgeTripletComponent = function(c1, c2, c3, renderTargetDifference) {
   2971        /** @type {number} */ var roundingDifference = 2 * renderTargetDifference; // src and dst pixels rounded to different directions
   2972        /** @type {number} */ var d1 = c2 - c1;
   2973        /** @type {number} */ var d2 = c3 - c2;
   2974        /** @type {number} */ var rampDiff = Math.abs(d2 - d1);
   2975 
   2976        return rampDiff > roundingDifference;
   2977    };
   2978 
   2979    /**
   2980     * @param {tcuRGBA.RGBA} c1
   2981     * @param {tcuRGBA.RGBA} c2
   2982     * @param {tcuRGBA.RGBA} c3
   2983     * @param {Array<number>} renderTargetThreshold
   2984     * @return {boolean}
   2985     */
   2986    glsDrawTests.isEdgeTriplet = function(c1, c2, c3, renderTargetThreshold) {
   2987        // black (background color) and non-black is always an edge
   2988        /** @type {boolean} */ var b1 = c1 == 0x000000;
   2989        /** @type {boolean} */ var b2 = c2 == 0x000000;
   2990        /** @type {boolean} */ var b3 = c3 == 0x000000;
   2991 
   2992        // both pixels with coverage and pixels without coverage
   2993        if ((b1 && b2 && b3) == false && (b1 || b2 || b3) == true)
   2994            return true;
   2995        // all black
   2996        if (b1 && b2 && b3)
   2997            return false;
   2998        // all with coverage
   2999        assertMsgOptions(!b1 && !b2 && !b3, 'All colors with coverage', false, true);
   3000 
   3001        // Color is always linearly interpolated => component values change nearly linearly
   3002        // in any constant direction on triangle hull. (df/dx ~= C).
   3003 
   3004        // Edge detection (this function) is run against the reference image
   3005        // => no dithering to worry about
   3006 
   3007        return glsDrawTests.isEdgeTripletComponent((c1 >> 16) && 0xff, (c2 >> 16) && 0xff, (c3 >> 16) && 0xff, renderTargetThreshold[0]) ||
   3008            glsDrawTests.isEdgeTripletComponent((c1 >> 8) && 0xff, (c2 >> 8) && 0xff, (c3 >> 8) && 0xff, renderTargetThreshold[1]) ||
   3009            glsDrawTests.isEdgeTripletComponent(c1 && 0xff, c2 && 0xff, c3 && 0xff, renderTargetThreshold[2]);
   3010    };
   3011 
   3012    /**
   3013     * @param {number} x
   3014     * @param {number} y
   3015     * @param {tcuSurface.Surface} ref
   3016     * @param {Array<number>} renderTargetThreshold
   3017     * @return {boolean}
   3018     */
   3019    glsDrawTests.pixelNearEdge = function(x, y, ref, renderTargetThreshold) {
   3020        // should not be called for edge pixels
   3021        assertMsgOptions(x >= 1 && x <= ref.getWidth() - 2, 'The pixel was on the edge', false, true);
   3022        assertMsgOptions(y >= 1 && y <= ref.getHeight() - 2, 'The pixel was on the edge', false, true);
   3023 
   3024        // horizontal
   3025 
   3026        /** @type {number} */ var c1;
   3027        /** @type {number} */ var c2;
   3028        /** @type {number} */ var c3;
   3029 
   3030        for (var dy = -1; dy < 2; ++dy) {
   3031            c1 = ref.getPixelUintRGB8(x - 1, y + dy);
   3032            c2 = ref.getPixelUintRGB8(x, y + dy);
   3033            c3 = ref.getPixelUintRGB8(x + 1, y + dy);
   3034            if (glsDrawTests.isEdgeTriplet(c1, c2, c3, renderTargetThreshold))
   3035                return true;
   3036        }
   3037 
   3038        // vertical
   3039 
   3040        for (var dx = -1; dx < 2; ++dx) {
   3041            c1 = ref.getPixelUintRGB8(x + dx, y - 1);
   3042            c2 = ref.getPixelUintRGB8(x + dx, y);
   3043            c3 = ref.getPixelUintRGB8(x + dx, y + 1);
   3044            if (glsDrawTests.isEdgeTriplet(c1, c2, c3, renderTargetThreshold))
   3045                return true;
   3046        }
   3047 
   3048        return false;
   3049    };
   3050 
   3051    /**
   3052     * @param {number} c
   3053     * @return {number}
   3054     */
   3055    glsDrawTests.getVisualizationGrayscaleColorUintRGB8 = function(c) {
   3056        // make triangle coverage and error pixels obvious by converting coverage to grayscale
   3057        if (c == 0x000000)
   3058            return 0;
   3059        else
   3060            return 50 + Math.floor((((c >> 16) & 0xff) + ((c >> 8) & 0xff) + (c & 0xff)) / 8);
   3061    };
   3062 
   3063    /**
   3064     * @param {number} x
   3065     * @param {number} y
   3066     * @param {tcuSurface.Surface} target
   3067     * @return {boolean}
   3068     */
   3069    glsDrawTests.pixelNearLineIntersection = function(x, y, target) {
   3070        // should not be called for edge pixels
   3071        assertMsgOptions(x >= 1 && x <= target.getWidth() - 2, 'Pixel is in the edge', false, true);
   3072        assertMsgOptions(y >= 1 && y <= target.getHeight() - 2, 'Pixel is in the edge', false, true);
   3073 
   3074        var coveredPixels = 0;
   3075 
   3076        for (var dy = -1; dy < 2; dy++)
   3077        for (var dx = -1; dx < 2; dx++) {
   3078            var targetCoverage = target.getPixelUintRGB8(x + dx, y + dy);
   3079            if (targetCoverage) {
   3080                ++coveredPixels;
   3081 
   3082                // A single thin line cannot have more than 3 covered pixels in a 3x3 area
   3083                if (coveredPixels >= 4)
   3084                    return true;
   3085            }
   3086        }
   3087 
   3088        return false;
   3089    };
   3090 
   3091    // search 3x3 are for matching color
   3092    /**
   3093     * @param {tcuSurface.Surface} target
   3094     * @param {number} x
   3095     * @param {number} y
   3096     * @param {tcuRGBA.RGBA} color
   3097     * @param {Array<number>} compareThreshold
   3098     * @return {boolean}
   3099     */
   3100    glsDrawTests.pixelNeighborhoodContainsColor = function(target, x, y, color, compareThreshold) {
   3101        // should not be called for edge pixels
   3102        assertMsgOptions(x >= 1 && x <= target.getWidth() - 2, 'Pixel is in the edge', false, true);
   3103        assertMsgOptions(y >= 1 && y <= target.getHeight() - 2, 'Pixel is in the edge', false, true);
   3104 
   3105        for (var dy = -1; dy < 2; dy++)
   3106        for (var dx = -1; dx < 2; dx++) {
   3107            if (glsDrawTests.compareUintRGB8(color, target.getPixelUintRGB8(x + dx, y + dy), compareThreshold))
   3108                return true;
   3109        }
   3110 
   3111        return false;
   3112    };
   3113 
   3114    // search 3x3 are for matching coverage (coverage == (color != background color))
   3115    /**
   3116     * @param {tcuSurface.Surface} target
   3117     * @param {number} x
   3118     * @param {number} y
   3119     * @param {boolean} coverage
   3120     * @return {boolean}
   3121     */
   3122    glsDrawTests.pixelNeighborhoodContainsCoverage = function(target, x, y, coverage) {
   3123        // should not be called for edge pixels
   3124        assertMsgOptions(x >= 1 && x <= target.getWidth() - 2, 'Pixel is in the edge', false, true);
   3125        assertMsgOptions(y >= 1 && y <= target.getHeight() - 2, 'Pixel is in the edge', false, true);
   3126 
   3127        for (var dy = -1; dy < 2; dy++)
   3128        for (var dx = -1; dx < 2; dx++) {
   3129            var targetCmpCoverage = target.getPixelUintRGB8(x + dx, y + dy) != 0x000000; // Pixel is not black
   3130            if (targetCmpCoverage == coverage)
   3131                return true;
   3132        }
   3133 
   3134        return false;
   3135    };
   3136 
   3137    /**
   3138     * @param {string} imageSetName
   3139     * @param {string} imageSetDesc
   3140     * @param {tcuSurface.Surface} reference
   3141     * @param {tcuSurface.Surface} result
   3142     * @param {Array<number>} compareThreshold
   3143     * @param {Array<number>} renderTargetThreshold
   3144     * @param {number} maxAllowedInvalidPixels
   3145     * @return {boolean}
   3146     */
   3147    glsDrawTests.edgeRelaxedImageCompare = function(imageSetName, imageSetDesc, reference, result, compareThreshold, renderTargetThreshold, maxAllowedInvalidPixels) {
   3148        assertMsgOptions(result.getWidth() == reference.getWidth() && result.getHeight() == reference.getHeight(), 'Reference and result images have different dimensions', false, true);
   3149 
   3150        /** @type {Array<number>} */ var green = [0, 255, 0, 255];
   3151        /** @type {Array<number>} */ var errorColor = [255, 0, 0, 255];
   3152        /** @type {number} */ var width = reference.getWidth();
   3153        /** @type {number} */ var height = reference.getHeight();
   3154        /** @type {tcuSurface.Surface} */ var errorMask = new tcuSurface.Surface(width, height);
   3155        /** @type {number} */ var numFailingPixels = 0;
   3156 
   3157        // clear errormask edges which would otherwise be transparent
   3158 
   3159        tcuTextureUtil.getSubregion(errorMask.getAccess(), 0, 0, 0, width, 1, 1).clear(green);
   3160        tcuTextureUtil.getSubregion(errorMask.getAccess(), 0, height - 1, 0, width, 1, 1).clear(green);
   3161        tcuTextureUtil.getSubregion(errorMask.getAccess(), 0, 0, 0, 1, height, 1).clear(green);
   3162        tcuTextureUtil.getSubregion(errorMask.getAccess(), width - 1, 0, 0, 1, height, 1).clear(green);
   3163 
   3164        // skip edge pixels since coverage on edge cannot be verified
   3165 
   3166        for (var y = 1; y < height - 1; ++y)
   3167        for (var x = 1; x < width - 1; ++x) {
   3168            /** @type {number} */ var refPixel = reference.getPixelUintRGB8(x, y);
   3169            /** @type {number} */ var screenPixel = result.getPixelUintRGB8(x, y);
   3170            /** @type {boolean} */ var isOkReferencePixel = glsDrawTests.pixelNeighborhoodContainsColor(result, x, y, refPixel, compareThreshold); // screen image has a matching pixel nearby (~= If something is drawn on reference, it must be drawn to screen too.)
   3171            /** @type {boolean} */ var isOkScreenPixel = glsDrawTests.pixelNeighborhoodContainsColor(reference, x, y, screenPixel, compareThreshold); // reference image has a matching pixel nearby (~= If something is drawn on screen, it must be drawn to reference too.)
   3172 
   3173            if (isOkScreenPixel && isOkReferencePixel) {
   3174                // pixel valid, write greenish pixels to make the result image easier to read
   3175                /** @type {number} */ var grayscaleValue = glsDrawTests.getVisualizationGrayscaleColorUintRGB8(screenPixel);
   3176                errorMask.getAccess().setPixel([grayscaleValue / 255, 1, grayscaleValue / 255, 1], x, y);
   3177            } else if (!glsDrawTests.pixelNearEdge(x, y, reference, renderTargetThreshold)) {
   3178                // non-edge pixel values must be within threshold of the reference values
   3179                errorMask.getAccess().setPixel(deMath.scale(errorColor, 1 / 255), x, y);
   3180                ++numFailingPixels;
   3181            } else {
   3182                // we are on/near an edge, verify only coverage (coverage == not background colored)
   3183                /** @type {boolean} */ var referenceCoverage = refPixel != 0x000000; // Not black
   3184                /** @type {boolean} */ var screenCoverage = screenPixel != 0x000000; // Not black
   3185                /** @type {boolean} */ var isOkReferenceCoverage = glsDrawTests.pixelNeighborhoodContainsCoverage(result, x, y, referenceCoverage); // Check reference pixel against screen pixel
   3186                /** @type {boolean} */ var isOkScreenCoverage = glsDrawTests.pixelNeighborhoodContainsCoverage(reference, x, y, screenCoverage); // Check screen pixels against reference pixel
   3187 
   3188                if (isOkScreenCoverage && isOkReferenceCoverage) {
   3189                    // pixel valid, write greenish pixels to make the result image easier to read
   3190                    var grayscaleValue = glsDrawTests.getVisualizationGrayscaleColorUintRGB8(screenPixel);
   3191                    errorMask.getAccess().setPixel([grayscaleValue / 255, 1, grayscaleValue / 255, 1], x, y);
   3192                } else {
   3193                    // coverage does not match
   3194                    errorMask.getAccess().setPixel(deMath.scale(errorColor, 1 / 255), x, y);
   3195                    ++numFailingPixels;
   3196                }
   3197            }
   3198        }
   3199 
   3200        bufferedLogToConsole(
   3201            'Comparing images:</br>' +
   3202            '<span> </span>allowed deviation in pixel positions = 1</br>' +
   3203            '<span> </span>number of allowed invalid pixels = ' + maxAllowedInvalidPixels + '</br>' +
   3204            '<span> </span>number of invalid pixels = ' + numFailingPixels
   3205        );
   3206 
   3207        if (numFailingPixels > maxAllowedInvalidPixels) {
   3208            debug('Image comparison failed. Color threshold = (' + compareThreshold[0] + ', ' + compareThreshold[1] + ', ' + compareThreshold[2] + ')');
   3209            tcuImageCompare.displayImages(result.getAccess(), reference.getAccess(), errorMask.getAccess());
   3210 
   3211            return false;
   3212        } else {
   3213            return true;
   3214        }
   3215    };
   3216 
   3217    /**
   3218     * @param {string} imageSetName
   3219     * @param {string} imageSetDesc
   3220     * @param {tcuSurface.Surface} reference
   3221     * @param {tcuSurface.Surface} result
   3222     * @param {Array<number>} compareThreshold
   3223     * @param {number} maxAllowedInvalidPixels
   3224     * @return {boolean}
   3225     */
   3226    glsDrawTests.intersectionRelaxedLineImageCompare = function(imageSetName, imageSetDesc, reference, result, compareThreshold, maxAllowedInvalidPixels) {
   3227        assertMsgOptions(result.getWidth() == reference.getWidth() && result.getHeight() == reference.getHeight(), 'Reference and result images have different dimensions', false, true);
   3228 
   3229        /** @type {Array<number>} */ var green = [0, 255, 0, 255];
   3230        /** @type {Array<number>} */ var errorColor = [255, 0, 0, 255];
   3231        var width = reference.getWidth();
   3232        var height = reference.getHeight();
   3233        /** @type {tcuSurface.Surface} */ var errorMask = new tcuSurface.Surface(width, height);
   3234        /** @type {number} */ var numFailingPixels = 0;
   3235 
   3236        // clear errormask edges which would otherwise be transparent
   3237 
   3238        tcuTextureUtil.getSubregion(errorMask.getAccess(), 0, 0, 0, width, 1, 1).clear(green);
   3239        tcuTextureUtil.getSubregion(errorMask.getAccess(), 0, height - 1, 0, width, 1, 1).clear(green);
   3240        tcuTextureUtil.getSubregion(errorMask.getAccess(), 0, 0, 0, 1, height, 1).clear(green);
   3241        tcuTextureUtil.getSubregion(errorMask.getAccess(), width - 1, 0, 0, 1, height, 1).clear(green);
   3242 
   3243        // skip edge pixels since coverage on edge cannot be verified
   3244 
   3245        for (var y = 1; y < height - 1; ++y)
   3246        for (var x = 1; x < width - 1; ++x) {
   3247            /** @type {number} */ var refPixel = reference.getPixelUintRGB8(x, y);
   3248            /** @type {number} */ var screenPixel = result.getPixelUintRGB8(x, y);
   3249            /** @type {boolean} */ var isOkScreenPixel = glsDrawTests.pixelNeighborhoodContainsColor(reference, x, y, screenPixel, compareThreshold); // reference image has a matching pixel nearby (~= If something is drawn on screen, it must be drawn to reference too.)
   3250            /** @type {boolean} */ var isOkReferencePixel = glsDrawTests.pixelNeighborhoodContainsColor(result, x, y, refPixel, compareThreshold); // screen image has a matching pixel nearby (~= If something is drawn on reference, it must be drawn to screen too.)
   3251 
   3252            /** @type {number} */ var grayscaleValue;
   3253 
   3254            if (isOkScreenPixel && isOkReferencePixel) {
   3255                // pixel valid, write greenish pixels to make the result image easier to read
   3256                grayscaleValue = glsDrawTests.getVisualizationGrayscaleColorUintRGB8(screenPixel);
   3257                errorMask.getAccess().setPixel([grayscaleValue / 255, 1, grayscaleValue / 255, 1], x, y);
   3258            } else if (!glsDrawTests.pixelNearLineIntersection(x, y, reference) &&
   3259                     !glsDrawTests.pixelNearLineIntersection(x, y, result)) {
   3260                // non-intersection pixel values must be within threshold of the reference values
   3261                errorMask.getAccess().setPixel(deMath.scale(errorColor, 1 / 255), x, y);
   3262                ++numFailingPixels;
   3263            } else {
   3264                // pixel is near a line intersection
   3265                // we are on/near an edge, verify only coverage (coverage == not background colored)
   3266                /** @type {boolean} */ var referenceCoverage = refPixel != 0x000000; // Not Black
   3267                /** @type {boolean} */ var screenCoverage = screenPixel != 0x000000; // Not Black
   3268                /** @type {boolean} */ var isOkScreenCoverage = glsDrawTests.pixelNeighborhoodContainsCoverage(reference, x, y, screenCoverage); // Check screen pixels against reference pixel
   3269                /** @type {boolean} */ var isOkReferenceCoverage = glsDrawTests.pixelNeighborhoodContainsCoverage(result, x, y, referenceCoverage); // Check reference pixel against screen pixel
   3270 
   3271                if (isOkScreenCoverage && isOkReferenceCoverage) {
   3272                    // pixel valid, write greenish pixels to make the result image easier to read
   3273                    grayscaleValue = glsDrawTests.getVisualizationGrayscaleColorUintRGB8(screenPixel);
   3274                    errorMask.getAccess().setPixel([grayscaleValue / 255, 1, grayscaleValue / 255, 1], x, y);
   3275                } else {
   3276                    // coverage does not match
   3277                    errorMask.getAccess().setPixel(deMath.scale(errorColor, 1 / 255), x, y);
   3278                    ++numFailingPixels;
   3279                }
   3280            }
   3281        }
   3282 
   3283        bufferedLogToConsole(
   3284            'Comparing images:</br>' +
   3285            '<span> </span>allowed deviation in pixel positions = 1</br>' +
   3286            '<span> </span>number of allowed invalid pixels = ' + maxAllowedInvalidPixels + '</br>' +
   3287            '<span> </span>number of invalid pixels = ' + numFailingPixels
   3288        );
   3289 
   3290        if (numFailingPixels > maxAllowedInvalidPixels) {
   3291            debug('Image comparison failed. Color threshold = (' + compareThreshold[0] + ', ' + compareThreshold[1] + ', ' + compareThreshold[2] + ')');
   3292            tcuImageCompare.displayImages(result.getAccess(), reference.getAccess(), errorMask.getAccess());
   3293 
   3294            return false;
   3295        } else {
   3296            return true;
   3297        }
   3298    };
   3299 
   3300    /**
   3301     * @param {?glsDrawTests.DrawTestSpec.Primitive} primitiveType
   3302     * @return {boolean}
   3303     */
   3304    glsDrawTests.DrawTest.prototype.compare = function(primitiveType) {
   3305        /** @type {tcuSurface.Surface} */ var ref = this.m_rrArrayPack.getSurface();
   3306        /** @type {tcuSurface.Surface} */ var screen = this.m_glArrayPack.getSurface();
   3307 
   3308        if (/** @type {number} */ (gl.getParameter(gl.SAMPLES)) > 1) {
   3309            // \todo [mika] Improve compare when using multisampling
   3310            bufferedLogToConsole('Warning: Comparision of result from multisample render targets are not as strict as without multisampling. Might produce false positives!');
   3311            return tcuImageCompare.fuzzyCompare('Compare Results', 'Compare Results', ref.getAccess(), screen.getAccess(), 0.3, tcuImageCompare.CompareLogMode.RESULT);
   3312        } else {
   3313            /** @type {glsDrawTests.PrimitiveClass} */ var primitiveClass = glsDrawTests.getDrawPrimitiveClass(primitiveType);
   3314 
   3315            switch (primitiveClass) {
   3316                case glsDrawTests.PrimitiveClass.POINT: {
   3317                    // Point are extremely unlikely to have overlapping regions, don't allow any no extra / missing pixels
   3318                    /**@type {number} */ var maxAllowedInvalidPixelsWithPoints = 0;
   3319                    return tcuImageCompare.intThresholdPositionDeviationErrorThresholdCompare(
   3320                        'CompareResult',
   3321                        'Result of rendering',
   3322                        ref.getAccess(),
   3323                        screen.getAccess(),
   3324                        [this.m_maxDiffRed, this.m_maxDiffGreen, this.m_maxDiffBlue, 256],
   3325                        [1, 1, 0], //!< 3x3 search kernel
   3326                        true, //!< relax comparison on the image boundary
   3327                        maxAllowedInvalidPixelsWithPoints //!< error threshold
   3328                    );
   3329                }
   3330 
   3331                case glsDrawTests.PrimitiveClass.LINE: {
   3332                    // Lines can potentially have a large number of overlapping pixels. Pixel comparison may potentially produce
   3333                    // false negatives in such pixels if for example the pixel in question is overdrawn by another line in the
   3334                    // reference image but not in the resultin image. Relax comparison near line intersection points (areas) and
   3335                    // compare only coverage, not color, in such pixels
   3336                    /**@type {number} */ var maxAllowedInvalidPixelsWithLines = 15; // line are allowed to have a few bad pixels
   3337                    return glsDrawTests.intersectionRelaxedLineImageCompare(
   3338                        'CompareResult',
   3339                        'Result of rendering',
   3340                        ref,
   3341                        screen,
   3342                        [this.m_maxDiffRed, this.m_maxDiffGreen, this.m_maxDiffBlue],
   3343                        maxAllowedInvalidPixelsWithLines
   3344                    );
   3345                }
   3346 
   3347                case glsDrawTests.PrimitiveClass.TRIANGLE: {
   3348                    // Triangles are likely to partially or fully overlap. Pixel difference comparison is fragile in pixels
   3349                    // where there could be potential overlapping since the pixels might be covered by one triangle in the
   3350                    // reference image and by the other in the result image. Relax comparsion near primitive edges and
   3351                    // compare only coverage, not color, in such pixels.
   3352                    /** @type {number} */ var maxAllowedInvalidPixelsWithTriangles = 10;
   3353 
   3354                    /* TODO: Implement
   3355                    var renderTargetThreshold = //TODO: get color threshold from the pixel format --> m_renderCtx.getRenderTarget().getPixelFormat().getColorThreshold().toIVec().xyz();
   3356                    */
   3357 
   3358                    /** @type {Array<number>} */ var renderTargetThreshold = [3, 3, 3, 3];
   3359 
   3360                    return glsDrawTests.edgeRelaxedImageCompare(
   3361                        'CompareResult',
   3362                        'Result of rendering',
   3363                        ref,
   3364                        screen,
   3365                        [this.m_maxDiffRed, this.m_maxDiffGreen, this.m_maxDiffBlue],
   3366                        renderTargetThreshold,
   3367                        maxAllowedInvalidPixelsWithTriangles
   3368                    );
   3369                }
   3370 
   3371                default:
   3372                    throw new Error('Invalid primitive class');
   3373            }
   3374        }
   3375    };
   3376 
   3377    /**
   3378     * @param {glsDrawTests.DrawTestSpec} spec
   3379     * @return {number}
   3380     */
   3381    glsDrawTests.DrawTest.prototype.getCoordScale = function(spec) {
   3382        var maxValue = 1.0;
   3383 
   3384        for (var arrayNdx = 0; arrayNdx < spec.attribs.length; arrayNdx++) {
   3385            /** @type {glsDrawTests.DrawTestSpec.AttributeSpec} */ var attribSpec = spec.attribs[arrayNdx];
   3386            /** @type {boolean} */ var isPositionAttr = (arrayNdx == 0) || (attribSpec.additionalPositionAttribute);
   3387            /** @type {number} */ var attrMaxValue = 0;
   3388 
   3389            if (!isPositionAttr)
   3390                continue;
   3391 
   3392            if (attribSpec.inputType == glsDrawTests.DrawTestSpec.InputType.UNSIGNED_INT_2_10_10_10) {
   3393                if (attribSpec.normalize)
   3394                    attrMaxValue += 1.0;
   3395                else
   3396                    attrMaxValue += 1024.0;
   3397            } else if (attribSpec.inputType == glsDrawTests.DrawTestSpec.InputType.INT_2_10_10_10) {
   3398                if (attribSpec.normalize)
   3399                    attrMaxValue += 1.0;
   3400                else
   3401                    attrMaxValue += 512.0;
   3402            } else {
   3403                var max = glsDrawTests.GLValue.getMaxValue(attribSpec.inputType).getValue();
   3404 
   3405                attrMaxValue += (attribSpec.normalize && !glsDrawTests.inputTypeIsFloatType(attribSpec.inputType)) ? (1.0) : (max * 1.1);
   3406            }
   3407 
   3408            if (attribSpec.outputType == glsDrawTests.DrawTestSpec.OutputType.VEC3 || attribSpec.outputType == glsDrawTests.DrawTestSpec.OutputType.VEC4 ||
   3409                attribSpec.outputType == glsDrawTests.DrawTestSpec.OutputType.IVEC3 || attribSpec.outputType == glsDrawTests.DrawTestSpec.OutputType.IVEC4 ||
   3410                attribSpec.outputType == glsDrawTests.DrawTestSpec.OutputType.UVEC3 || attribSpec.outputType == glsDrawTests.DrawTestSpec.OutputType.UVEC4)
   3411                    attrMaxValue *= 2;
   3412 
   3413            maxValue += attrMaxValue;
   3414        }
   3415 
   3416        return 1.0 / maxValue;
   3417    };
   3418 
   3419    /**
   3420     * @param {glsDrawTests.DrawTestSpec} spec
   3421     * @return {number}
   3422     */
   3423    glsDrawTests.DrawTest.prototype.getColorScale = function(spec) {
   3424        var colorScale = 1.0;
   3425 
   3426        for (var arrayNdx = 1; arrayNdx < spec.attribs.length; arrayNdx++) {
   3427            /** @type {glsDrawTests.DrawTestSpec.AttributeSpec} */ var attribSpec = spec.attribs[arrayNdx];
   3428            /** @type {boolean} */ var isPositionAttr = (arrayNdx == 0) || (attribSpec.additionalPositionAttribute);
   3429 
   3430            if (isPositionAttr)
   3431                continue;
   3432 
   3433            if (attribSpec.inputType == glsDrawTests.DrawTestSpec.InputType.UNSIGNED_INT_2_10_10_10) {
   3434                if (!attribSpec.normalize)
   3435                    colorScale *= 1.0 / 1024.0;
   3436            } else if (attribSpec.inputType == glsDrawTests.DrawTestSpec.InputType.INT_2_10_10_10) {
   3437                if (!attribSpec.normalize)
   3438                    colorScale *= 1.0 / 512.0;
   3439            } else {
   3440                var max = glsDrawTests.GLValue.getMaxValue(attribSpec.inputType).toFloat();
   3441 
   3442                colorScale *= (attribSpec.normalize && !glsDrawTests.inputTypeIsFloatType(attribSpec.inputType) ? 1.0 : (1.0 / max));
   3443                if (attribSpec.outputType == glsDrawTests.DrawTestSpec.OutputType.VEC4 ||
   3444                    attribSpec.outputType == glsDrawTests.DrawTestSpec.OutputType.UVEC4 ||
   3445                    attribSpec.outputType == glsDrawTests.DrawTestSpec.OutputType.IVEC4)
   3446                    colorScale *= (attribSpec.normalize && !glsDrawTests.inputTypeIsFloatType(attribSpec.inputType) ? 1.0 : 1.0 / max);
   3447            }
   3448        }
   3449 
   3450        return colorScale;
   3451    };
   3452 });