tor-browser

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

es3fShaderIndexingTests.js (59940B)


      1 /*-------------------------------------------------------------------------
      2 * drawElements Quality Program OpenGL ES Utilities
      3 * ------------------------------------------------
      4 *
      5 * Copyright 2014 The Android Open Source Project
      6 *
      7 * Licensed under the Apache License, Version 2.0 (the "License");
      8 * you may not use this file except in compliance with the License.
      9 * You may obtain a copy of the License at
     10 *
     11 *      http://www.apache.org/licenses/LICENSE-2.0
     12 *
     13 * Unless required by applicable law or agreed to in writing, software
     14 * distributed under the License is distributed on an "AS IS" BASIS,
     15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     16 * See the License for the specific language governing permissions and
     17 * limitations under the License.
     18 *
     19 */
     20 
     21 'use strict';
     22 goog.provide('functional.gles3.es3fShaderIndexingTests');
     23 goog.require('framework.common.tcuImageCompare');
     24 goog.require('framework.common.tcuStringTemplate');
     25 goog.require('framework.common.tcuTestCase');
     26 goog.require('framework.delibs.debase.deMath');
     27 goog.require('framework.opengl.gluShaderUtil');
     28 goog.require('framework.opengl.gluShaderProgram');
     29 goog.require('framework.opengl.gluTexture');
     30 goog.require('modules.shared.glsShaderRenderCase');
     31 
     32 goog.scope(function() {
     33    /** @type {?WebGL2RenderingContext} */ var gl;
     34    var es3fShaderIndexingTests = functional.gles3.es3fShaderIndexingTests;
     35    var deMath = framework.delibs.debase.deMath;
     36    var glsShaderRenderCase = modules.shared.glsShaderRenderCase;
     37    var gluShaderUtil = framework.opengl.gluShaderUtil;
     38    var gluTexture = framework.opengl.gluTexture;
     39    var gluShaderProgram = framework.opengl.gluShaderProgram;
     40    var tcuTestCase = framework.common.tcuTestCase;
     41    var tcuStringTemplate = framework.common.tcuStringTemplate;
     42    /**
     43     * @enum {number}
     44     */
     45    es3fShaderIndexingTests.IndexAccessType = {
     46        STATIC: 0,
     47        DYNAMIC: 1,
     48        STATIC_LOOP: 2,
     49        DYNAMIC_LOOP: 3
     50    };
     51 
     52    /**
     53     * @param {es3fShaderIndexingTests.IndexAccessType} accessType
     54     * @return {string}
     55     */
     56    es3fShaderIndexingTests.getIndexAccessTypeName = function(accessType) {
     57        /** @type {Array<string>} */ var s_names = [
     58            'static',
     59            'dynamic',
     60            'static_loop',
     61            'dynamic_loop'
     62        ];
     63        return s_names[accessType];
     64    };
     65 
     66    /**
     67     * @enum {number}
     68     */
     69    es3fShaderIndexingTests.VectorAccessType = {
     70        DIRECT: 0,
     71        COMPONENT: 1,
     72        SUBSCRIPT_STATIC: 2,
     73        SUBSCRIPT_DYNAMIC: 3,
     74        SUBSCRIPT_STATIC_LOOP: 4,
     75        SUBSCRIPT_DYNAMIC_LOOP: 5
     76    };
     77 
     78    /**
     79     * @param {es3fShaderIndexingTests.VectorAccessType} accessType
     80     * @return {string}
     81     */
     82    es3fShaderIndexingTests.getVectorAccessTypeName = function(accessType) {
     83        /** @type {Array<string>} */ var s_names = [
     84            'direct',
     85            'component',
     86            'static_subscript',
     87            'dynamic_subscript',
     88            'static_loop_subscript',
     89            'dynamic_loop_subscript'
     90        ];
     91        assertMsgOptions(deMath.deInBounds32(accessType, 0, s_names.length), 'Index out of bounds', false, true);
     92        return s_names[accessType];
     93    };
     94 
     95    /** @param {glsShaderRenderCase.ShaderEvalContext} c */
     96    es3fShaderIndexingTests.evalArrayCoordsFloat = function(c) {
     97        c.color[0] = 1.875 * c.coords[0];
     98    };
     99 
    100    /** @param {glsShaderRenderCase.ShaderEvalContext} c */
    101    es3fShaderIndexingTests.evalArrayCoordsVec2 = function(c) {
    102        var swizzled = deMath.swizzle(c.coords, [0, 1]);
    103        c.color[0] = 1.875 * swizzled[0];
    104        c.color[1] = 1.875 * swizzled[1];
    105    };
    106 
    107    /** @param {glsShaderRenderCase.ShaderEvalContext} c */
    108    es3fShaderIndexingTests.evalArrayCoordsVec3 = function(c) {
    109        var swizzled = deMath.swizzle(c.coords, [0, 1, 2]);
    110        c.color[0] = 1.875 * swizzled[0];
    111        c.color[1] = 1.875 * swizzled[1];
    112        c.color[2] = 1.875 * swizzled[2];
    113    };
    114 
    115    /** @param {glsShaderRenderCase.ShaderEvalContext} c */
    116    es3fShaderIndexingTests.evalArrayCoordsVec4 = function(c) {
    117        c.color = deMath.scale(c.coords, 1.875);
    118    };
    119 
    120    /**
    121     * @param {gluShaderUtil.DataType} dataType
    122     * @return {function(glsShaderRenderCase.ShaderEvalContext)}
    123     */
    124    es3fShaderIndexingTests.getArrayCoordsEvalFunc = function(dataType) {
    125        if (dataType === gluShaderUtil.DataType.FLOAT) return es3fShaderIndexingTests.evalArrayCoordsFloat;
    126        else if (dataType === gluShaderUtil.DataType.FLOAT_VEC2) return es3fShaderIndexingTests.evalArrayCoordsVec2;
    127        else if (dataType === gluShaderUtil.DataType.FLOAT_VEC3) return es3fShaderIndexingTests.evalArrayCoordsVec3;
    128        else if (dataType === gluShaderUtil.DataType.FLOAT_VEC4) return es3fShaderIndexingTests.evalArrayCoordsVec4;
    129        else throw new Error('Invalid data type.');
    130    };
    131 
    132 
    133    /** @param {glsShaderRenderCase.ShaderEvalContext} c */
    134    es3fShaderIndexingTests.evalArrayUniformFloat = function(c) {
    135        c.color[0] = 1.875 * c.constCoords[0];
    136    };
    137 
    138    /** @param {glsShaderRenderCase.ShaderEvalContext} c */
    139    es3fShaderIndexingTests.evalArrayUniformVec2 = function(c) {
    140        var swizzled = deMath.swizzle(c.constCoords, [0, 1]);
    141        c.color[0] = 1.875 * swizzled[0];
    142        c.color[1] = 1.875 * swizzled[1];
    143    };
    144 
    145    /** @param {glsShaderRenderCase.ShaderEvalContext} c */
    146    es3fShaderIndexingTests.evalArrayUniformVec3 = function(c) {
    147        var swizzled = deMath.swizzle(c.constCoords, [0, 1, 2]);
    148        c.color[0] = 1.875 * swizzled[0];
    149        c.color[1] = 1.875 * swizzled[1];
    150        c.color[2] = 1.875 * swizzled[2];
    151    };
    152 
    153    /** @param {glsShaderRenderCase.ShaderEvalContext} c */
    154    es3fShaderIndexingTests.evalArrayUniformVec4 = function(c) {
    155        c.color = deMath.scale(c.constCoords, 1.875);
    156    };
    157 
    158    /**
    159     * @param {gluShaderUtil.DataType} dataType
    160     * @return {function(glsShaderRenderCase.ShaderEvalContext)}
    161     */
    162    es3fShaderIndexingTests.getArrayUniformEvalFunc = function(dataType) {
    163        if (dataType === gluShaderUtil.DataType.FLOAT) return es3fShaderIndexingTests.evalArrayUniformFloat;
    164        else if (dataType === gluShaderUtil.DataType.FLOAT_VEC2) return es3fShaderIndexingTests.evalArrayUniformVec2;
    165        else if (dataType === gluShaderUtil.DataType.FLOAT_VEC3) return es3fShaderIndexingTests.evalArrayUniformVec3;
    166        else if (dataType === gluShaderUtil.DataType.FLOAT_VEC4) return es3fShaderIndexingTests.evalArrayUniformVec4;
    167        else throw new Error('Invalid data type.');
    168    };
    169 
    170    /**
    171     * @constructor
    172     * @extends {glsShaderRenderCase.ShaderRenderCase}
    173     * @param {string} name
    174     * @param {string} description
    175     * @param {boolean} isVertexCase
    176     * @param {gluShaderUtil.DataType} varType
    177     * @param {function(glsShaderRenderCase.ShaderEvalContext)} evalFunc
    178     * @param {string} vertShaderSource
    179     * @param {string} fragShaderSource
    180     */
    181    es3fShaderIndexingTests.ShaderIndexingCase = function(name, description, isVertexCase, varType, evalFunc, vertShaderSource, fragShaderSource) {
    182        glsShaderRenderCase.ShaderRenderCase.call(this, name, description, isVertexCase, evalFunc);
    183        /** @type {gluShaderUtil.DataType} */ this.m_varType = varType;
    184        /** @type {string} */ this.m_vertShaderSource    = vertShaderSource;
    185        /** @type {string} */ this.m_fragShaderSource    = fragShaderSource;
    186    };
    187 
    188    es3fShaderIndexingTests.ShaderIndexingCase.prototype = Object.create(glsShaderRenderCase.ShaderRenderCase.prototype);
    189    es3fShaderIndexingTests.ShaderIndexingCase.prototype.constructor = es3fShaderIndexingTests.ShaderIndexingCase;
    190 
    191    /**
    192     * @param {?WebGLProgram} programID
    193     * @param {Array<number>} constCoords
    194     */
    195    es3fShaderIndexingTests.ShaderIndexingCase.prototype.setupUniforms = function(programID, constCoords) {
    196        /** @type {(Array<number>|Float32Array)} */ var arr = [];
    197        /** @type {Array<number>} */ var array1d = [];
    198        /** @type {?WebGLUniformLocation} */ var arrLoc = gl.getUniformLocation(programID, 'u_arr');
    199        if (arrLoc != null) {
    200            if (this.m_varType === gluShaderUtil.DataType.FLOAT) {
    201                arr[0] = constCoords[0];
    202                arr[1] = constCoords[0] * 0.5;
    203                arr[2] = constCoords[0] * 0.25;
    204                arr[3] = constCoords[0] * 0.125;
    205                gl.uniform1fv(arrLoc, arr);
    206            }
    207            else if (this.m_varType === gluShaderUtil.DataType.FLOAT_VEC2) {
    208                arr[0] = deMath.swizzle(constCoords, [0, 1]);
    209                arr[1] = deMath.scale(deMath.swizzle(constCoords, [0, 1]), 0.5);
    210                arr[2] = deMath.scale(deMath.swizzle(constCoords, [0, 1]), 0.25);
    211                arr[3] = deMath.scale(deMath.swizzle(constCoords, [0, 1]), 0.125);
    212                for (var i = 0; i < arr.length; i++)
    213                    array1d = array1d.concat(arr[i]);
    214                gl.uniform2fv(arrLoc, array1d);
    215            }
    216            else if (this.m_varType === gluShaderUtil.DataType.FLOAT_VEC3) {
    217                arr[0] = deMath.swizzle(constCoords, [0, 1, 2]);
    218                arr[1] = deMath.scale(deMath.swizzle(constCoords, [0, 1, 2]), 0.5);
    219                arr[2] = deMath.scale(deMath.swizzle(constCoords, [0, 1, 2]), 0.25);
    220                arr[3] = deMath.scale(deMath.swizzle(constCoords, [0, 1, 2]), 0.125);
    221                for (var i = 0; i < arr.length; i++)
    222                    array1d = array1d.concat(arr[i]);
    223                gl.uniform3fv(arrLoc, array1d);
    224            }
    225            else if (this.m_varType === gluShaderUtil.DataType.FLOAT_VEC4) {
    226                arr[0] = deMath.swizzle(constCoords, [0,1,2,3]);
    227                arr[1] = deMath.scale(deMath.swizzle(constCoords, [0, 1, 2, 3]), 0.5);
    228                arr[2] = deMath.scale(deMath.swizzle(constCoords, [0, 1, 2, 3]), 0.25);
    229                arr[3] = deMath.scale(deMath.swizzle(constCoords, [0, 1, 2, 3]), 0.125);
    230                for (var i = 0; i < arr.length; i++)
    231                    array1d = array1d.concat(arr[i]);
    232                gl.uniform4fv(arrLoc, array1d);
    233            }
    234            else
    235                throw new Error('u_arr should not have location assigned in this test case');
    236        }
    237    };
    238 
    239    /**
    240     * @param  {string} caseName
    241     * @param  {string} description
    242     * @param  {gluShaderUtil.DataType} varType
    243     * @param  {es3fShaderIndexingTests.IndexAccessType} vertAccess
    244     * @param  {es3fShaderIndexingTests.IndexAccessType} fragAccess
    245     * @return {es3fShaderIndexingTests.ShaderIndexingCase}
    246     */
    247    es3fShaderIndexingTests.createVaryingArrayCase = function(caseName, description, varType, vertAccess, fragAccess) {
    248        /** @type {string} */ var vtx = '';
    249        vtx += '#version 300 es\n' +
    250               'in highp vec4 a_position;\n' +
    251               'in highp vec4 a_coords;\n';
    252 
    253        if (vertAccess === es3fShaderIndexingTests.IndexAccessType.DYNAMIC)
    254            vtx += 'uniform mediump int ui_zero, ui_one, ui_two, ui_three;\n';
    255        else if (vertAccess === es3fShaderIndexingTests.IndexAccessType.DYNAMIC_LOOP)
    256            vtx += 'uniform mediump int ui_four;\n';
    257 
    258        vtx += 'out ${PRECISION} ${VAR_TYPE} var[${ARRAY_LEN}];\n' +
    259               '\n' +
    260               'void main()\n' +
    261               '{\n' +
    262               '    gl_Position = a_position;\n';
    263 
    264        if (vertAccess === es3fShaderIndexingTests.IndexAccessType.STATIC) {
    265            vtx += '    var[0] = ${VAR_TYPE}(a_coords);\n' +
    266                   '    var[1] = ${VAR_TYPE}(a_coords) * 0.5;\n' +
    267                   '    var[2] = ${VAR_TYPE}(a_coords) * 0.25;\n' +
    268                   '    var[3] = ${VAR_TYPE}(a_coords) * 0.125;\n';
    269        }
    270        else if (vertAccess === es3fShaderIndexingTests.IndexAccessType.DYNAMIC) {
    271            vtx += '    var[ui_zero]  = ${VAR_TYPE}(a_coords);\n' +
    272                   '    var[ui_one]   = ${VAR_TYPE}(a_coords) * 0.5;\n' +
    273                   '    var[ui_two]   = ${VAR_TYPE}(a_coords) * 0.25;\n' +
    274                   '    var[ui_three] = ${VAR_TYPE}(a_coords) * 0.125;\n';
    275        }
    276        else if (vertAccess === es3fShaderIndexingTests.IndexAccessType.STATIC_LOOP) {
    277            vtx += '	${PRECISION} ${VAR_TYPE} coords = ${VAR_TYPE}(a_coords);\n' +
    278                   '    for (int i = 0; i < 4; i++)\n' +
    279                   '    {\n' +
    280                   '		var[i] = ${VAR_TYPE}(coords);\n' +
    281                   '        coords = coords * 0.5;\n' +
    282                   '    }\n';
    283        }
    284        else {
    285            assertMsgOptions(vertAccess === es3fShaderIndexingTests.IndexAccessType.DYNAMIC_LOOP, 'Not Dynamic_Loop', false, true);
    286            vtx += '	${PRECISION} ${VAR_TYPE} coords = ${VAR_TYPE}(a_coords);\n' +
    287                   '    for (int i = 0; i < ui_four; i++)\n' +
    288                   '    {\n' +
    289                   '		var[i] = ${VAR_TYPE}(coords);\n' +
    290                   '        coords = coords * 0.5;\n' +
    291                   '    }\n';
    292        }
    293        vtx += '}\n';
    294 
    295        /** @type {string} */ var frag = '';
    296        frag += '#version 300 es\n' +
    297                'precision mediump int;\n' +
    298                'layout(location = 0) out mediump vec4 o_color;\n';
    299 
    300        if (fragAccess === es3fShaderIndexingTests.IndexAccessType.DYNAMIC)
    301            frag += 'uniform mediump int ui_zero, ui_one, ui_two, ui_three;\n';
    302        else if (fragAccess === es3fShaderIndexingTests.IndexAccessType.DYNAMIC_LOOP)
    303            frag += 'uniform int ui_four;\n';
    304 
    305        frag += 'in ${PRECISION} ${VAR_TYPE} var[${ARRAY_LEN}];\n' +
    306                '\n' +
    307                'void main()\n' +
    308                '{\n' +
    309                '	${PRECISION} ${VAR_TYPE} res = ${VAR_TYPE}(0.0);\n';
    310 
    311        if (fragAccess === es3fShaderIndexingTests.IndexAccessType.STATIC) {
    312            frag += '    res += var[0];\n' +
    313                    '    res += var[1];\n' +
    314                    '    res += var[2];\n' +
    315                    '    res += var[3];\n';
    316        }
    317        else if (fragAccess === es3fShaderIndexingTests.IndexAccessType.DYNAMIC) {
    318            frag += '    res += var[ui_zero];\n' +
    319                    '    res += var[ui_one];\n' +
    320                    '    res += var[ui_two];\n' +
    321                    '    res += var[ui_three];\n';
    322        }
    323        else if (fragAccess === es3fShaderIndexingTests.IndexAccessType.STATIC_LOOP) {
    324            frag += '    for (int i = 0; i < 4; i++)\n' +
    325                    '        res += var[i];\n';
    326        }
    327        else {
    328            assertMsgOptions(fragAccess === es3fShaderIndexingTests.IndexAccessType.DYNAMIC_LOOP, 'Not Dynamic_Loop', false, true);
    329            frag += '    for (int i = 0; i < ui_four; i++)\n' +
    330                    '        res += var[i];\n';
    331        }
    332        frag += '	o_color = vec4(res${PADDING});\n' +
    333                '}\n';
    334 
    335        // Fill in shader templates.
    336        /** @type {Object} */ var params = {};
    337     params['VAR_TYPE'] = gluShaderUtil.getDataTypeName(varType);
    338     params['ARRAY_LEN'] = '4';
    339     params['PRECISION'] = 'mediump';
    340 
    341        if (varType === gluShaderUtil.DataType.FLOAT)
    342            params['PADDING'] = ', 0.0, 0.0, 1.0';
    343        else if (varType === gluShaderUtil.DataType.FLOAT_VEC2)
    344            params['PADDING'] = ', 0.0, 1.0';
    345        else if (varType === gluShaderUtil.DataType.FLOAT_VEC3)
    346            params['PADDING'] = ', 1.0';
    347        else
    348            params['PADDING'] = '';
    349 
    350        /** @type {string} */ var vertexShaderSource = tcuStringTemplate.specialize(vtx, params);
    351    	/** @type {string} */ var fragmentShaderSource = tcuStringTemplate.specialize(frag, params);
    352 
    353        /** @type {function(glsShaderRenderCase.ShaderEvalContext)} */
    354        var evalFunc = es3fShaderIndexingTests.getArrayCoordsEvalFunc(varType);
    355        return new es3fShaderIndexingTests.ShaderIndexingCase(caseName, description, true, varType, evalFunc, vertexShaderSource, fragmentShaderSource);
    356    };
    357 
    358    /**
    359     * @param {string} caseName
    360     * @param {string} description
    361     * @param {boolean} isVertexCase
    362     * @param {gluShaderUtil.DataType} varType
    363     * @param {es3fShaderIndexingTests.IndexAccessType} readAccess
    364     * @return {es3fShaderIndexingTests.ShaderIndexingCase}
    365     */
    366    es3fShaderIndexingTests.createUniformArrayCase = function(caseName, description, isVertexCase, varType, readAccess) {
    367        /** @type {string} */ var vtx = '';
    368        /** @type {string} */ var frag = '';
    369        /** @type {string} */ var op = '';
    370 
    371        vtx += '#version 300 es\n';
    372        frag += '#version 300 es\n';
    373 
    374        vtx += 'in highp vec4 a_position;\n';
    375        vtx += 'in highp vec4 a_coords;\n';
    376        frag += 'layout(location = 0) out mediump vec4 o_color;\n';
    377 
    378        if (isVertexCase) {
    379            vtx += 'out mediump vec4 v_color;\n';
    380            frag += 'in mediump vec4 v_color;\n';
    381        }
    382        else {
    383            vtx += 'out mediump vec4 v_coords;\n';
    384            frag += 'in mediump vec4 v_coords;\n';
    385        }
    386 
    387        if (readAccess === es3fShaderIndexingTests.IndexAccessType.DYNAMIC)
    388            op += 'uniform mediump int ui_zero, ui_one, ui_two, ui_three;\n';
    389        else if (readAccess === es3fShaderIndexingTests.IndexAccessType.DYNAMIC_LOOP)
    390            op += 'uniform mediump int ui_four;\n';
    391 
    392        op += 'uniform ${PRECISION} ${VAR_TYPE} u_arr[${ARRAY_LEN}];\n';
    393 
    394        vtx += isVertexCase ? op : '';
    395        frag += isVertexCase ? '' : op;
    396        op = '';
    397 
    398        vtx += '\n';
    399        vtx += 'void main()\n';
    400        vtx += '{\n';
    401        vtx += '    gl_Position = a_position;\n';
    402 
    403        frag += '\n';
    404        frag += 'void main()\n';
    405        frag += '{\n';
    406 
    407        // Read array.
    408        op += '	${PRECISION} ${VAR_TYPE} res = ${VAR_TYPE}(0.0);\n';
    409        if (readAccess === es3fShaderIndexingTests.IndexAccessType.STATIC) {
    410            op += '    res += u_arr[0];\n';
    411            op += '    res += u_arr[1];\n';
    412            op += '    res += u_arr[2];\n';
    413            op += '    res += u_arr[3];\n';
    414        }
    415        else if (readAccess === es3fShaderIndexingTests.IndexAccessType.DYNAMIC) {
    416            op += '    res += u_arr[ui_zero];\n';
    417            op += '    res += u_arr[ui_one];\n';
    418            op += '    res += u_arr[ui_two];\n';
    419            op += '    res += u_arr[ui_three];\n';
    420        }
    421        else if (readAccess === es3fShaderIndexingTests.IndexAccessType.STATIC_LOOP) {
    422            op += '    for (int i = 0; i < 4; i++)\n';
    423            op += '        res += u_arr[i];\n';
    424        }
    425        else {
    426            assertMsgOptions(readAccess === es3fShaderIndexingTests.IndexAccessType.DYNAMIC_LOOP, 'readAccess not supported.', false, true);
    427            op += '    for (int i = 0; i < ui_four; i++)\n';
    428            op += '        res += u_arr[i];\n';
    429        }
    430 
    431        vtx += isVertexCase ? op : '';
    432        frag += isVertexCase ? '' : op;
    433        op = '';
    434 
    435        if (isVertexCase) {
    436            vtx += '    v_color = vec4(res${PADDING});\n';
    437            frag += '    o_color = v_color;\n';
    438        }
    439        else {
    440            vtx += '    v_coords = a_coords;\n';
    441            frag += '    o_color = vec4(res${PADDING});\n';
    442        }
    443 
    444        vtx += '}\n';
    445        frag += '}\n';
    446 
    447        // Fill in shader templates.
    448        /** @type {Object} */ var params = {};
    449     params['VAR_TYPE'] = gluShaderUtil.getDataTypeName(varType);
    450     params['ARRAY_LEN'] = '4';
    451     params['PRECISION'] = 'mediump';
    452 
    453        if (varType === gluShaderUtil.DataType.FLOAT)
    454            params['PADDING'] = ', 0.0, 0.0, 1.0';
    455        else if (varType === gluShaderUtil.DataType.FLOAT_VEC2)
    456            params['PADDING'] = ', 0.0, 1.0';
    457        else if (varType === gluShaderUtil.DataType.FLOAT_VEC3)
    458            params['PADDING'] = ', 1.0';
    459        else
    460            params['PADDING'] = '';
    461 
    462 
    463        /** @type {string} */ var vertexShaderSource = tcuStringTemplate.specialize(vtx, params);
    464        /** @type {string} */ var fragmentShaderSource = tcuStringTemplate.specialize(frag, params);
    465 
    466        /** @type {function(glsShaderRenderCase.ShaderEvalContext)} */
    467        var evalFunc = es3fShaderIndexingTests.getArrayUniformEvalFunc(varType);
    468        return new es3fShaderIndexingTests.ShaderIndexingCase(caseName, description, isVertexCase, varType, evalFunc, vertexShaderSource, fragmentShaderSource);
    469    };
    470 
    471    /**
    472     * @param {string} caseName
    473     * @param {string} description
    474     * @param {boolean} isVertexCase
    475     * @param {gluShaderUtil.DataType} varType
    476     * @param {es3fShaderIndexingTests.IndexAccessType} writeAccess
    477     * @param {es3fShaderIndexingTests.IndexAccessType} readAccess
    478     * @return {es3fShaderIndexingTests.ShaderIndexingCase}
    479     */
    480    es3fShaderIndexingTests.createTmpArrayCase = function(caseName, description, isVertexCase, varType, writeAccess, readAccess)    {
    481        /** @type {string} */ var vtx = '';
    482        /** @type {string} */ var frag = '';
    483        /** @type {string} */ var op = '';
    484 
    485        vtx += '#version 300 es\n';
    486        frag += '#version 300 es\n';
    487 
    488        vtx += 'in highp vec4 a_position;\n' +
    489               'in highp vec4 a_coords;\n';
    490        frag += 'layout(location = 0) out mediump vec4 o_color;\n';
    491 
    492        if (isVertexCase) {
    493            vtx += 'out mediump vec4 v_color;\n';
    494            frag += 'in mediump vec4 v_color;\n';
    495        }
    496        else {
    497            vtx += 'out mediump vec4 v_coords;\n';
    498            frag += 'in mediump vec4 v_coords;\n';
    499        }
    500 
    501        if (writeAccess === es3fShaderIndexingTests.IndexAccessType.DYNAMIC || readAccess === es3fShaderIndexingTests.IndexAccessType.DYNAMIC)
    502            op += 'uniform mediump int ui_zero, ui_one, ui_two, ui_three;\n';
    503 
    504        if (writeAccess === es3fShaderIndexingTests.IndexAccessType.DYNAMIC_LOOP || readAccess === es3fShaderIndexingTests.IndexAccessType.DYNAMIC_LOOP)
    505            op += 'uniform mediump int ui_four;\n';
    506 
    507        vtx += isVertexCase ? op : '';
    508        frag += isVertexCase ? '' : op;
    509        op = '';
    510 
    511        vtx += '\n' +
    512               'void main()\n' +
    513               '{\n' +
    514               '    gl_Position = a_position;\n';
    515 
    516        frag += '\n' +
    517                'void main()\n' +
    518                '{\n';
    519 
    520        // Write array.
    521        if (isVertexCase)
    522            op += '	${PRECISION} ${VAR_TYPE} coords = ${VAR_TYPE}(a_coords);\n';
    523        else
    524            op += '	${PRECISION} ${VAR_TYPE} coords = ${VAR_TYPE}(v_coords);\n';
    525 
    526        op += '	${PRECISION} ${VAR_TYPE} arr[${ARRAY_LEN}];\n';
    527        if (writeAccess === es3fShaderIndexingTests.IndexAccessType.STATIC) {
    528            op += '	arr[0] = ${VAR_TYPE}(coords);\n' +
    529    		      '	arr[1] = ${VAR_TYPE}(coords) * 0.5;\n' +
    530    		      '	arr[2] = ${VAR_TYPE}(coords) * 0.25;\n' +
    531    		      '	arr[3] = ${VAR_TYPE}(coords) * 0.125;\n';
    532        }
    533        else if (writeAccess === es3fShaderIndexingTests.IndexAccessType.DYNAMIC) {
    534    		op += '	arr[ui_zero]  = ${VAR_TYPE}(coords);\n' +
    535    		      '	arr[ui_one]   = ${VAR_TYPE}(coords) * 0.5;\n' +
    536    		      '	arr[ui_two]   = ${VAR_TYPE}(coords) * 0.25;\n' +
    537    		      '	arr[ui_three] = ${VAR_TYPE}(coords) * 0.125;\n';
    538        }
    539        else if (writeAccess === es3fShaderIndexingTests.IndexAccessType.STATIC_LOOP) {
    540            op += '    for (int i = 0; i < 4; i++)\n' +
    541                  '    {\n' +
    542                  '        arr[i] = ${VAR_TYPE}(coords);\n' +
    543                  '        coords = coords * 0.5;\n' +
    544                  '    }\n';
    545        }
    546        else {
    547            assertMsgOptions(writeAccess === es3fShaderIndexingTests.IndexAccessType.DYNAMIC_LOOP, 'writeAccess not supported', false, true);
    548            op += '    for (int i = 0; i < ui_four; i++)\n' +
    549                  '    {\n' +
    550                  '        arr[i] = ${VAR_TYPE}(coords);\n' +
    551                  '        coords = coords * 0.5;\n' +
    552                  '    }\n';
    553        }
    554 
    555        // Read array.
    556        op += '	${PRECISION} ${VAR_TYPE} res = ${VAR_TYPE}(0.0);\n';
    557        if (readAccess === es3fShaderIndexingTests.IndexAccessType.STATIC) {
    558            op += '    res += arr[0];\n' +
    559                  '    res += arr[1];\n' +
    560                  '    res += arr[2];\n' +
    561                  '    res += arr[3];\n';
    562        }
    563        else if (readAccess === es3fShaderIndexingTests.IndexAccessType.DYNAMIC) {
    564            op += '    res += arr[ui_zero];\n' +
    565                  '    res += arr[ui_one];\n' +
    566                  '    res += arr[ui_two];\n' +
    567                  '    res += arr[ui_three];\n';
    568        }
    569        else if (readAccess === es3fShaderIndexingTests.IndexAccessType.STATIC_LOOP) {
    570            op += '    for (int i = 0; i < 4; i++)\n' +
    571                  '        res += arr[i];\n';
    572        }
    573        else {
    574            assertMsgOptions(readAccess === es3fShaderIndexingTests.IndexAccessType.DYNAMIC_LOOP, 'readAccess not supported.', false, true);
    575            op += '    for (int i = 0; i < ui_four; i++)\n' +
    576                  '        res += arr[i];\n';
    577        }
    578 
    579        vtx += isVertexCase ? op : '';
    580        frag += isVertexCase ? '' : op;
    581        op = '';
    582 
    583        if (isVertexCase) {
    584            vtx += '    v_color = vec4(res${PADDING});\n';
    585            frag += '    o_color = v_color;\n';
    586        }
    587        else {
    588            vtx += '    v_coords = a_coords;\n';
    589            frag += '    o_color = vec4(res${PADDING});\n';
    590        }
    591 
    592        vtx += '}\n';
    593        frag += '}\n';
    594 
    595        // Fill in shader templates.
    596        /** @type {Object} */ var params = {};
    597    	params["VAR_TYPE"] = gluShaderUtil.getDataTypeName(varType);
    598    	params["ARRAY_LEN"] = "4";
    599    	params["PRECISION"] = "mediump";
    600 
    601        if (varType === gluShaderUtil.DataType.FLOAT)
    602            params['PADDING'] = ', 0.0, 0.0, 1.0';
    603        else if (varType === gluShaderUtil.DataType.FLOAT_VEC2)
    604            params['PADDING'] = ', 0.0, 1.0';
    605        else if (varType === gluShaderUtil.DataType.FLOAT_VEC3)
    606            params['PADDING'] = ', 1.0';
    607        else
    608            params['PADDING'] = '';
    609 
    610    	/** @type {string} */ var vertexShaderSource = tcuStringTemplate.specialize(vtx, params);
    611    	/** @type {string} */ var fragmentShaderSource = tcuStringTemplate.specialize(frag, params);
    612 
    613        /** @type {function(glsShaderRenderCase.ShaderEvalContext)} */
    614        var evalFunc = es3fShaderIndexingTests.getArrayCoordsEvalFunc(varType);
    615        return new es3fShaderIndexingTests.ShaderIndexingCase(caseName, description, isVertexCase, varType, evalFunc, vertexShaderSource, fragmentShaderSource);
    616    };
    617 
    618    // VECTOR SUBSCRIPT.
    619 
    620    /** @param {glsShaderRenderCase.ShaderEvalContext} c */
    621    es3fShaderIndexingTests.evalSubscriptVec2 = function(c) {
    622        c.color[0] = c.coords[0] + 0.5 * c.coords[1];
    623        c.color[1] = c.coords[0] + 0.5 * c.coords[1];
    624        c.color[2] = c.coords[0] + 0.5 * c.coords[1];
    625    };
    626    /** @param {glsShaderRenderCase.ShaderEvalContext} c */
    627    es3fShaderIndexingTests.evalSubscriptVec3 = function(c) {
    628        c.color[0] = c.coords[0] + 0.5 * c.coords[1] + 0.25 * c.coords[2];
    629        c.color[1] = c.coords[0] + 0.5 * c.coords[1] + 0.25 * c.coords[2];
    630        c.color[2] = c.coords[0] + 0.5 * c.coords[1] + 0.25 * c.coords[2];
    631    };
    632    /** @param {glsShaderRenderCase.ShaderEvalContext} c */
    633    es3fShaderIndexingTests.evalSubscriptVec4 = function(c) {
    634        c.color[0] = c.coords[0] + 0.5 * c.coords[1] + 0.25 * c.coords[2] + 0.125 * c.coords[3];
    635        c.color[1] = c.coords[0] + 0.5 * c.coords[1] + 0.25 * c.coords[2] + 0.125 * c.coords[3];
    636        c.color[2] = c.coords[0] + 0.5 * c.coords[1] + 0.25 * c.coords[2] + 0.125 * c.coords[3];
    637    };
    638 
    639    /**
    640     * @param {gluShaderUtil.DataType} dataType
    641     * @return {function(glsShaderRenderCase.ShaderEvalContext)}
    642     */
    643    es3fShaderIndexingTests.getVectorSubscriptEvalFunc = function(dataType) {
    644        if (dataType === gluShaderUtil.DataType.FLOAT_VEC2) return es3fShaderIndexingTests.evalSubscriptVec2;
    645        else if (dataType === gluShaderUtil.DataType.FLOAT_VEC3) return es3fShaderIndexingTests.evalSubscriptVec3;
    646        else if (dataType === gluShaderUtil.DataType.FLOAT_VEC4) return es3fShaderIndexingTests.evalSubscriptVec4;
    647        else throw new Error('Invalid data type.');
    648    };
    649 
    650    /**
    651     * @param {string} caseName
    652     * @param {string} description
    653     * @param {boolean} isVertexCase
    654     * @param {gluShaderUtil.DataType} varType
    655     * @param {es3fShaderIndexingTests.VectorAccessType} writeAccess
    656     * @param {es3fShaderIndexingTests.VectorAccessType} readAccess
    657     * @return {es3fShaderIndexingTests.ShaderIndexingCase}
    658     */
    659    es3fShaderIndexingTests.createVectorSubscriptCase = function(caseName, description, isVertexCase, varType, writeAccess, readAccess) {
    660        /** @type {string} */ var vtx = '';
    661        /** @type {string} */ var frag = '';
    662        /** @type {string} */ var op = '' ;
    663 
    664        /** @type {number} */ var vecLen = gluShaderUtil.getDataTypeScalarSize(varType);
    665        /** @type {string} */ var vecLenName = glsShaderRenderCase.getIntUniformName(vecLen);
    666 
    667        vtx += '#version 300 es\n';
    668        frag += '#version 300 es\n';
    669 
    670        vtx += 'in highp vec4 a_position;\n' +
    671               'in highp vec4 a_coords;\n';
    672        frag += 'layout(location = 0) out mediump vec4 o_color;\n';
    673 
    674        if (isVertexCase) {
    675            vtx += 'out mediump vec3 v_color;\n';
    676            frag += 'in mediump vec3 v_color;\n';
    677        }
    678        else {
    679            vtx += 'out mediump vec4 v_coords;\n';
    680            frag += 'in mediump vec4 v_coords;\n';
    681        }
    682 
    683        if (writeAccess === es3fShaderIndexingTests.VectorAccessType.SUBSCRIPT_DYNAMIC ||
    684            readAccess === es3fShaderIndexingTests.VectorAccessType.SUBSCRIPT_DYNAMIC){
    685            op += 'uniform mediump int ui_zero';
    686            if (vecLen >= 2) op += ', ui_one';
    687            if (vecLen >= 3) op += ', ui_two';
    688            if (vecLen >= 4) op += ', ui_three';
    689            op += ';\n';
    690        }
    691 
    692        if (writeAccess === es3fShaderIndexingTests.VectorAccessType.SUBSCRIPT_DYNAMIC_LOOP ||
    693            readAccess === es3fShaderIndexingTests.VectorAccessType.SUBSCRIPT_DYNAMIC_LOOP)
    694            op += 'uniform mediump int ' + vecLenName + ';\n';
    695 
    696        vtx += isVertexCase ? op : '';
    697        frag += isVertexCase ? '' : op;
    698        op = '';
    699 
    700        vtx += '\n' +
    701               'void main()\n' +
    702               '{\n' +
    703               '    gl_Position = a_position;\n';
    704 
    705        frag += '\n' +
    706                'void main()\n' +
    707                '{\n';
    708 
    709        // Write vector.
    710        if (isVertexCase)
    711            op += '	${PRECISION} ${VAR_TYPE} coords = ${VAR_TYPE}(a_coords);\n';
    712        else
    713            op += '	${PRECISION} ${VAR_TYPE} coords = ${VAR_TYPE}(v_coords);\n';
    714 
    715        op += '	${PRECISION} ${VAR_TYPE} tmp;\n';
    716        if (writeAccess === es3fShaderIndexingTests.VectorAccessType.DIRECT)
    717            op += '    tmp = coords.${SWIZZLE} * vec4(1.0, 0.5, 0.25, 0.125).${SWIZZLE};\n';
    718        else if (writeAccess === es3fShaderIndexingTests.VectorAccessType.COMPONENT) {
    719            op += '    tmp.x = coords.x;\n';
    720            if (vecLen >= 2) op += '    tmp.y = coords.y * 0.5;\n';
    721            if (vecLen >= 3) op += '    tmp.z = coords.z * 0.25;\n';
    722            if (vecLen >= 4) op += '    tmp.w = coords.w * 0.125;\n';
    723        }
    724        else if (writeAccess === es3fShaderIndexingTests.VectorAccessType.SUBSCRIPT_STATIC) {
    725            op += '    tmp[0] = coords.x;\n';
    726            if (vecLen >= 2) op += '    tmp[1] = coords.y * 0.5;\n';
    727            if (vecLen >= 3) op += '    tmp[2] = coords.z * 0.25;\n';
    728            if (vecLen >= 4) op += '    tmp[3] = coords.w * 0.125;\n';
    729        }
    730        else if (writeAccess === es3fShaderIndexingTests.VectorAccessType.SUBSCRIPT_DYNAMIC) {
    731            op += '    tmp[ui_zero]  = coords.x;\n';
    732            if (vecLen >= 2) op += '    tmp[ui_one]   = coords.y * 0.5;\n';
    733            if (vecLen >= 3) op += '    tmp[ui_two]   = coords.z * 0.25;\n';
    734            if (vecLen >= 4) op += '    tmp[ui_three] = coords.w * 0.125;\n';
    735        }
    736        else if (writeAccess === es3fShaderIndexingTests.VectorAccessType.SUBSCRIPT_STATIC_LOOP) {
    737            op += '    for (int i = 0; i < ' + vecLen + '; i++)\n';
    738            op += '    {\n';
    739            op += '        tmp[i] = coords.x;\n';
    740            op += '        coords = coords.${ROT_SWIZZLE} * 0.5;\n';
    741            op += '    }\n';
    742        }
    743        else {
    744            assertMsgOptions(writeAccess === es3fShaderIndexingTests.VectorAccessType.SUBSCRIPT_DYNAMIC_LOOP, 'writeAccess not supported.', false, true);
    745            op += '    for (int i = 0; i < ' + vecLenName + '; i++)\n';
    746            op += '    {\n';
    747            op += '        tmp[i] = coords.x;\n';
    748            op += '        coords = coords.${ROT_SWIZZLE} * 0.5;\n';
    749            op += '    }\n';
    750        }
    751 
    752        // Read vector.
    753        op += '	${PRECISION} float res = 0.0;\n';
    754        if (readAccess === es3fShaderIndexingTests.VectorAccessType.DIRECT)
    755            op += '	res = dot(tmp, ${VAR_TYPE}(1.0));\n';
    756        else if (readAccess === es3fShaderIndexingTests.VectorAccessType.COMPONENT) {
    757            op += '    res += tmp.x;\n';
    758            if (vecLen >= 2) op += '    res += tmp.y;\n';
    759            if (vecLen >= 3) op += '    res += tmp.z;\n';
    760            if (vecLen >= 4) op += '    res += tmp.w;\n';
    761        }
    762        else if (readAccess === es3fShaderIndexingTests.VectorAccessType.SUBSCRIPT_STATIC) {
    763            op += '    res += tmp[0];\n';
    764            if (vecLen >= 2) op += '    res += tmp[1];\n';
    765            if (vecLen >= 3) op += '    res += tmp[2];\n';
    766            if (vecLen >= 4) op += '    res += tmp[3];\n';
    767        }
    768        else if (readAccess === es3fShaderIndexingTests.VectorAccessType.SUBSCRIPT_DYNAMIC) {
    769            op += '    res += tmp[ui_zero];\n';
    770            if (vecLen >= 2) op += '    res += tmp[ui_one];\n';
    771            if (vecLen >= 3) op += '    res += tmp[ui_two];\n';
    772            if (vecLen >= 4) op += '    res += tmp[ui_three];\n';
    773        }
    774        else if (readAccess === es3fShaderIndexingTests.VectorAccessType.SUBSCRIPT_STATIC_LOOP) {
    775            op += '    for (int i = 0; i < ' + vecLen + '; i++)\n';
    776            op += '        res += tmp[i];\n';
    777        }
    778        else {
    779            assertMsgOptions(readAccess === es3fShaderIndexingTests.VectorAccessType.SUBSCRIPT_DYNAMIC_LOOP, 'readAccess not supported', false, true);
    780            op += '    for (int i = 0; i < ' + vecLenName + '; i++)\n';
    781            op += '        res += tmp[i];\n';
    782        }
    783 
    784        vtx += isVertexCase ? op : '';
    785        frag += isVertexCase ? '' : op;
    786        op = '';
    787 
    788        if (isVertexCase) {
    789            vtx += '    v_color = vec3(res);\n';
    790            frag += '    o_color = vec4(v_color.rgb, 1.0);\n';
    791        }
    792        else {
    793            vtx += '    v_coords = a_coords;\n';
    794            frag += '    o_color = vec4(vec3(res), 1.0);\n';
    795        }
    796 
    797        vtx += '}\n';
    798        frag += '}\n';
    799 
    800        // Fill in shader templates.
    801        /** @type {Array<string>} */ var s_swizzles = ['', 'x', 'xy', 'xyz', 'xyzw'];
    802        /** @type {Array<string>} */ var s_rotSwizzles = ['', 'x', 'yx', 'yzx', 'yzwx'];
    803 
    804        /** @type {Object} */ var params = {};
    805        params["VAR_TYPE"] = gluShaderUtil.getDataTypeName(varType);
    806     params["PRECISION"] = "mediump";
    807     params["SWIZZLE"] = s_swizzles[vecLen];
    808     params["ROT_SWIZZLE"] = s_rotSwizzles[vecLen];
    809 
    810        /** @type {string} */ var vertexShaderSource = tcuStringTemplate.specialize(vtx, params);
    811        /** @type {string} */ var fragmentShaderSource = tcuStringTemplate.specialize(frag, params);
    812 
    813        /** @type {function(glsShaderRenderCase.ShaderEvalContext)} */
    814        var evalFunc = es3fShaderIndexingTests.getVectorSubscriptEvalFunc(varType);
    815        return new es3fShaderIndexingTests.ShaderIndexingCase(caseName, description, isVertexCase, varType, evalFunc, vertexShaderSource, fragmentShaderSource);
    816    };
    817 
    818    // MATRIX SUBSCRIPT.
    819 
    820    /** @param {glsShaderRenderCase.ShaderEvalContext} c */
    821    es3fShaderIndexingTests.evalSubscriptMat2 = function(c) {
    822        var swizzle01 = deMath.swizzle(c.coords, [0, 1]);
    823        var swizzle12 = deMath.swizzle(c.coords, [1, 2]);
    824        c.color[0] = swizzle01[0] + 0.5 * swizzle12[0];
    825        c.color[1] = swizzle01[1] + 0.5 * swizzle12[1];
    826    };
    827 
    828    /** @param {glsShaderRenderCase.ShaderEvalContext} c */
    829    es3fShaderIndexingTests.evalSubscriptMat2x3 = function(c) {
    830        var swizzle012 = deMath.swizzle(c.coords, [0, 1, 2]);
    831        var swizzle123 = deMath.swizzle(c.coords, [1, 2, 3]);
    832        c.color[0] = swizzle012[0] + 0.5 * swizzle123[0];
    833        c.color[1] = swizzle012[1] + 0.5 * swizzle123[1];
    834        c.color[2] = swizzle012[2] + 0.5 * swizzle123[2];
    835    };
    836 
    837    /** @param {glsShaderRenderCase.ShaderEvalContext} c */
    838    es3fShaderIndexingTests.evalSubscriptMat2x4 = function(c) {
    839        c.color = deMath.add(
    840            deMath.swizzle(c.coords, [0,1,2,3]),
    841            deMath.scale(deMath.swizzle(c.coords, [1,2,3,0]), 0.5));
    842    };
    843 
    844    /** @param {glsShaderRenderCase.ShaderEvalContext} c */
    845    es3fShaderIndexingTests.evalSubscriptMat3x2 = function(c) {
    846        var swizzle01 = deMath.swizzle(c.coords, [0, 1]);
    847        var swizzle12 = deMath.swizzle(c.coords, [1, 2]);
    848        var swizzle23 = deMath.swizzle(c.coords, [2, 3]);
    849        c.color[0] = swizzle01[0] + 0.5 * swizzle12[0] + 0.25 * swizzle23[0];
    850        c.color[1] = swizzle01[1] + 0.5 * swizzle12[1] + 0.25 * swizzle23[1];
    851    };
    852 
    853    /** @param {glsShaderRenderCase.ShaderEvalContext} c */
    854    es3fShaderIndexingTests.evalSubscriptMat3 = function(c) {
    855        var swizzle012 = deMath.swizzle(c.coords, [0, 1, 2]);
    856        var swizzle123 = deMath.swizzle(c.coords, [1, 2, 3]);
    857        var swizzle230 = deMath.swizzle(c.coords, [2, 3, 0]);
    858        c.color[0] = swizzle012[0] + 0.5 * swizzle123[0] + 0.25 * swizzle230[0];
    859        c.color[1] = swizzle012[1] + 0.5 * swizzle123[1] + 0.25 * swizzle230[1];
    860        c.color[2] = swizzle012[2] + 0.5 * swizzle123[2] + 0.25 * swizzle230[2];
    861    };
    862 
    863    /** @param {glsShaderRenderCase.ShaderEvalContext} c */
    864    es3fShaderIndexingTests.evalSubscriptMat3x4 = function(c) {
    865        var swizzle0123 = deMath.swizzle(c.coords, [0, 1, 2, 3]);
    866        var swizzle1230 = deMath.swizzle(c.coords, [1, 2, 3, 0]);
    867        var swizzle2301 = deMath.swizzle(c.coords, [2, 3, 0, 1]);
    868        c.color = deMath.add(
    869            swizzle0123,
    870            deMath.add(
    871                deMath.scale(swizzle1230, 0.5),
    872                deMath.scale(swizzle2301, 0.25)));
    873    };
    874 
    875    /** @param {glsShaderRenderCase.ShaderEvalContext} c */
    876    es3fShaderIndexingTests.evalSubscriptMat4x2 = function(c) {
    877        var swizzle01 = deMath.swizzle(c.coords, [0, 1]);
    878        var swizzle12 = deMath.swizzle(c.coords, [1, 2]);
    879        var swizzle23 = deMath.swizzle(c.coords, [2, 3]);
    880        var swizzle30 = deMath.swizzle(c.coords, [3, 0]);
    881        c.color[0] = swizzle01[0] + 0.5 * swizzle12[0] + 0.25 * swizzle23[0] + 0.125 * swizzle30[0];
    882        c.color[1] = swizzle01[1] + 0.5 * swizzle12[1] + 0.25 * swizzle23[1] + 0.125 * swizzle30[1];
    883    };
    884 
    885    /** @param {glsShaderRenderCase.ShaderEvalContext} c */
    886    es3fShaderIndexingTests.evalSubscriptMat4x3 = function(c) {
    887        var swizzle012 = deMath.swizzle(c.coords, [0, 1, 2]);
    888        var swizzle123 = deMath.swizzle(c.coords, [1, 2, 3]);
    889        var swizzle230 = deMath.swizzle(c.coords, [2, 3, 0]);
    890        var swizzle301 = deMath.swizzle(c.coords, [3, 0, 1]);
    891        c.color[0] = swizzle012[0] + 0.5 * swizzle123[0] + 0.25 * swizzle230[0] + 0.125 * swizzle301[0];
    892        c.color[1] = swizzle012[1] + 0.5 * swizzle123[1] + 0.25 * swizzle230[1] + 0.125 * swizzle301[1];
    893        c.color[2] = swizzle012[2] + 0.5 * swizzle123[2] + 0.25 * swizzle230[2] + 0.125 * swizzle301[2];
    894    };
    895 
    896    /** @param {glsShaderRenderCase.ShaderEvalContext} c */
    897    es3fShaderIndexingTests.evalSubscriptMat4 = function(c) {
    898        var swizzle1230 = deMath.swizzle(c.coords, [1, 2, 3, 0]);
    899        var swizzle2301 = deMath.swizzle(c.coords, [2, 3, 0, 1]);
    900        var swizzle3012 = deMath.swizzle(c.coords, [3, 0, 1, 2]);
    901        c.color = deMath.add(
    902            c.coords,
    903            deMath.add(
    904                deMath.scale(swizzle1230, 0.5),
    905                deMath.add(
    906                    deMath.scale(swizzle2301, 0.25),
    907                    deMath.scale(swizzle3012, 0.125))));
    908    };
    909 
    910    /**
    911     * @param  {gluShaderUtil.DataType} dataType
    912     * @return {function(glsShaderRenderCase.ShaderEvalContext)}
    913     */
    914    es3fShaderIndexingTests.getMatrixSubscriptEvalFunc = function(dataType) {
    915        switch (dataType) {
    916            case gluShaderUtil.DataType.FLOAT_MAT2: return es3fShaderIndexingTests.evalSubscriptMat2;
    917            case gluShaderUtil.DataType.FLOAT_MAT2X3: return es3fShaderIndexingTests.evalSubscriptMat2x3;
    918            case gluShaderUtil.DataType.FLOAT_MAT2X4: return es3fShaderIndexingTests.evalSubscriptMat2x4;
    919            case gluShaderUtil.DataType.FLOAT_MAT3X2: return es3fShaderIndexingTests.evalSubscriptMat3x2;
    920            case gluShaderUtil.DataType.FLOAT_MAT3: return es3fShaderIndexingTests.evalSubscriptMat3;
    921            case gluShaderUtil.DataType.FLOAT_MAT3X4: return es3fShaderIndexingTests.evalSubscriptMat3x4;
    922            case gluShaderUtil.DataType.FLOAT_MAT4X2: return es3fShaderIndexingTests.evalSubscriptMat4x2;
    923            case gluShaderUtil.DataType.FLOAT_MAT4X3: return es3fShaderIndexingTests.evalSubscriptMat4x3;
    924            case gluShaderUtil.DataType.FLOAT_MAT4: return es3fShaderIndexingTests.evalSubscriptMat4;
    925            default:
    926                throw new Error('Invalid data type.');
    927        }
    928    };
    929 
    930    /**
    931     * @param {string} caseName
    932     * @param {string} description
    933     * @param {boolean} isVertexCase
    934     * @param {gluShaderUtil.DataType} varType
    935     * @param {es3fShaderIndexingTests.IndexAccessType} writeAccess
    936     * @param {es3fShaderIndexingTests.IndexAccessType} readAccess
    937     * @return {es3fShaderIndexingTests.ShaderIndexingCase}
    938     */
    939    es3fShaderIndexingTests.createMatrixSubscriptCase = function(caseName, description, isVertexCase, varType, writeAccess, readAccess) {
    940        /** @type {string} */ var vtx = '';
    941        /** @type {string} */ var frag = '';
    942        /** @type {string} */ var op = '';
    943 
    944        /** @type {number} */ var numCols = gluShaderUtil.getDataTypeMatrixNumColumns(varType);
    945        /** @type {number} */ var numRows = gluShaderUtil.getDataTypeMatrixNumRows(varType);
    946        /** @type {string} */ var matSizeName = glsShaderRenderCase.getIntUniformName(numCols);
    947        /** @type {gluShaderUtil.DataType} */ var vecType = gluShaderUtil.getDataTypeFloatVec(numRows);
    948 
    949        vtx += '#version 300 es\n';
    950        frag += '#version 300 es\n';
    951 
    952        vtx += 'in highp vec4 a_position;\n' +
    953               'in highp vec4 a_coords;\n';
    954        frag += 'layout(location = 0) out mediump vec4 o_color;\n';
    955 
    956        if (isVertexCase) {
    957            vtx += 'out mediump vec4 v_color;\n';
    958            frag += 'in mediump vec4 v_color;\n';
    959        }
    960        else {
    961            vtx += 'out mediump vec4 v_coords;\n';
    962            frag += 'in mediump vec4 v_coords;\n';
    963        }
    964 
    965        if (writeAccess === es3fShaderIndexingTests.IndexAccessType.DYNAMIC || readAccess === es3fShaderIndexingTests.IndexAccessType.DYNAMIC) {
    966            op += 'uniform mediump int ui_zero';
    967            if (numCols >= 2) op += ', ui_one';
    968            if (numCols >= 3) op += ', ui_two';
    969            if (numCols >= 4) op += ', ui_three';
    970            op += ';\n';
    971        }
    972 
    973        if (writeAccess === es3fShaderIndexingTests.IndexAccessType.DYNAMIC_LOOP || readAccess === es3fShaderIndexingTests.IndexAccessType.DYNAMIC_LOOP)
    974            op += 'uniform mediump int ' + matSizeName + ';\n';
    975 
    976        vtx += isVertexCase ? op : '';
    977        frag += isVertexCase ? '' : op;
    978        op = '';
    979 
    980        vtx += '\n' +
    981               'void main()\n' +
    982               '{\n' +
    983               '    gl_Position = a_position;\n';
    984 
    985        frag += '\n' +
    986                'void main()\n' +
    987                '{\n';
    988 
    989        // Write matrix.
    990        if (isVertexCase)
    991            op += '	${PRECISION} vec4 coords = a_coords;\n';
    992        else
    993            op += '	${PRECISION} vec4 coords = v_coords;\n';
    994 
    995        op += '	${PRECISION} ${MAT_TYPE} tmp;\n';
    996        if (writeAccess === es3fShaderIndexingTests.IndexAccessType.STATIC) {
    997            op += '	tmp[0] = ${VEC_TYPE}(coords);\n';
    998            if (numCols >= 2) op += '    tmp[1] = ${VEC_TYPE}(coords.yzwx) * 0.5;\n';
    999            if (numCols >= 3) op += '    tmp[2] = ${VEC_TYPE}(coords.zwxy) * 0.25;\n';
   1000            if (numCols >= 4) op += '    tmp[3] = ${VEC_TYPE}(coords.wxyz) * 0.125;\n';
   1001        }
   1002        else if (writeAccess === es3fShaderIndexingTests.IndexAccessType.DYNAMIC) {
   1003            op += '	tmp[ui_zero]  = ${VEC_TYPE}(coords);\n';
   1004            if (numCols >= 2) op += '    tmp[ui_one]   = ${VEC_TYPE}(coords.yzwx) * 0.5;\n';
   1005            if (numCols >= 3) op += '    tmp[ui_two]   = ${VEC_TYPE}(coords.zwxy) * 0.25;\n';
   1006            if (numCols >= 4) op += '    tmp[ui_three] = ${VEC_TYPE}(coords.wxyz) * 0.125;\n';
   1007        }
   1008        else if (writeAccess === es3fShaderIndexingTests.IndexAccessType.STATIC_LOOP) {
   1009            op += '    for (int i = 0; i < ' + numCols + '; i++)\n';
   1010            op += '    {\n';
   1011            op += '		tmp[i] = ${VEC_TYPE}(coords);\n';
   1012            op += '        coords = coords.yzwx * 0.5;\n';
   1013            op += '    }\n';
   1014        }
   1015        else {
   1016            assertMsgOptions(writeAccess === es3fShaderIndexingTests.IndexAccessType.DYNAMIC_LOOP, 'writeAccess not supported', false, true);
   1017            op += '    for (int i = 0; i < ' + matSizeName + '; i++)\n';
   1018            op += '    {\n';
   1019            op += '        tmp[i] = ${VEC_TYPE}(coords);\n';
   1020            op += '        coords = coords.yzwx * 0.5;\n';
   1021            op += '    }\n';
   1022        }
   1023 
   1024        // Read matrix.
   1025        op += '	${PRECISION} ${VEC_TYPE} res = ${VEC_TYPE}(0.0);\n';
   1026        if (readAccess === es3fShaderIndexingTests.IndexAccessType.STATIC) {
   1027            op += '    res += tmp[0];\n';
   1028            if (numCols >= 2) op += '    res += tmp[1];\n';
   1029            if (numCols >= 3) op += '    res += tmp[2];\n';
   1030            if (numCols >= 4) op += '    res += tmp[3];\n';
   1031        }
   1032        else if (readAccess === es3fShaderIndexingTests.IndexAccessType.DYNAMIC) {
   1033            op += '    res += tmp[ui_zero];\n';
   1034            if (numCols >= 2) op += '    res += tmp[ui_one];\n';
   1035            if (numCols >= 3) op += '    res += tmp[ui_two];\n';
   1036            if (numCols >= 4) op += '    res += tmp[ui_three];\n';
   1037        }
   1038        else if (readAccess === es3fShaderIndexingTests.IndexAccessType.STATIC_LOOP) {
   1039            op += '    for (int i = 0; i < ' + numCols + '; i++)\n';
   1040            op += '        res += tmp[i];\n';
   1041        }
   1042        else {
   1043            assertMsgOptions(readAccess === es3fShaderIndexingTests.IndexAccessType.DYNAMIC_LOOP, 'readAccess not supported', false, true);
   1044            op += '    for (int i = 0; i < ' + matSizeName + '; i++)\n';
   1045            op += '        res += tmp[i];\n';
   1046        }
   1047 
   1048        vtx += isVertexCase ? op : '';
   1049        frag += isVertexCase ? '' : op;
   1050        op = '';
   1051 
   1052        if (isVertexCase) {
   1053            vtx += '    v_color = vec4(res${PADDING});\n';
   1054            frag += '    o_color = v_color;\n';
   1055        }
   1056        else {
   1057            vtx += '    v_coords = a_coords;\n';
   1058            frag += '    o_color = vec4(res${PADDING});\n';
   1059        }
   1060 
   1061        vtx += '}\n';
   1062        frag += '}\n';
   1063 
   1064        // Fill in shader templates.
   1065 
   1066        /** @type {Object} */ var params = {};
   1067        params['MAT_TYPE'] = gluShaderUtil.getDataTypeName(varType);
   1068        params['VEC_TYPE'] = gluShaderUtil.getDataTypeName(vecType);
   1069        params['PRECISION'] = "mediump";
   1070 
   1071 
   1072        if (numRows === 2)
   1073              params['PADDING'] = ', 0.0, 1.0';
   1074        else if (numRows === 3)
   1075              params['PADDING'] = ', 1.0';
   1076        else
   1077              params['PADDING'] = '';
   1078 
   1079      	/** @type {string} */ var vertexShaderSource = tcuStringTemplate.specialize(vtx, params);
   1080      	/** @type {string} */ var fragmentShaderSource = tcuStringTemplate.specialize(frag, params);
   1081 
   1082        /** @type {function(glsShaderRenderCase.ShaderEvalContext)} */
   1083        var evalFunc = es3fShaderIndexingTests.getMatrixSubscriptEvalFunc(varType);
   1084        return new es3fShaderIndexingTests.ShaderIndexingCase(caseName, description, isVertexCase, varType, evalFunc, vertexShaderSource, fragmentShaderSource);
   1085    };
   1086 
   1087    /**
   1088     * @constructor
   1089     * @extends {tcuTestCase.DeqpTest}
   1090     */
   1091    es3fShaderIndexingTests.ShaderIndexingTests = function() {
   1092        tcuTestCase.DeqpTest.call(this, 'indexing', 'Indexing Tests');
   1093    };
   1094 
   1095    es3fShaderIndexingTests.ShaderIndexingTests.prototype = Object.create(tcuTestCase.DeqpTest.prototype);
   1096    es3fShaderIndexingTests.ShaderIndexingTests.prototype.constructor = es3fShaderIndexingTests.ShaderIndexingTests;
   1097 
   1098    es3fShaderIndexingTests.ShaderIndexingTests.prototype.init = function() {
   1099        var testGroup = tcuTestCase.runner.testCases;
   1100        /** @type {Array<gluShaderProgram.shaderType>} */ var s_shaderTypes = [
   1101            gluShaderProgram.shaderType.VERTEX,
   1102            gluShaderProgram.shaderType.FRAGMENT
   1103        ];
   1104        /** @type {Array<gluShaderUtil.DataType>} */ var s_floatAndVecTypes = [
   1105            gluShaderUtil.DataType.FLOAT,
   1106            gluShaderUtil.DataType.FLOAT_VEC2,
   1107            gluShaderUtil.DataType.FLOAT_VEC3,
   1108            gluShaderUtil.DataType.FLOAT_VEC4
   1109        ];
   1110        /** @type {string} */ var name;
   1111        /** @type {string} */ var desc;
   1112        /** @type {string} */ var shaderTypeName;
   1113        /** @type {boolean} */ var isVertexCase;
   1114        /** @type {gluShaderProgram.shaderType} */ var shaderType;
   1115        /** @type {string} */ var writeAccessName;
   1116        /** @type {string} */ var readAccessName;
   1117        // Varying array access cases.
   1118        /** @type {tcuTestCase.DeqpTest} */ var varyingGroup = tcuTestCase.newTest('varying_array', 'Varying array access tests.');
   1119        testGroup.addChild(varyingGroup);
   1120        /** @type {gluShaderUtil.DataType} */ var varType;
   1121        for (var typeNdx = 0; typeNdx < s_floatAndVecTypes.length; typeNdx++) {
   1122            varType = s_floatAndVecTypes[typeNdx];
   1123            for (var vertAccessStr in es3fShaderIndexingTests.IndexAccessType) {
   1124                for (var fragAccessStr in es3fShaderIndexingTests.IndexAccessType) {
   1125                    var vertAccess = es3fShaderIndexingTests.IndexAccessType[vertAccessStr];
   1126                    var fragAccess = es3fShaderIndexingTests.IndexAccessType[fragAccessStr];
   1127                    /** @type {string} */ var vertAccessName = es3fShaderIndexingTests.getIndexAccessTypeName(vertAccess);
   1128                    /** @type {string} */ var fragAccessName = es3fShaderIndexingTests.getIndexAccessTypeName(fragAccess);
   1129                    name = gluShaderUtil.getDataTypeName(varType) + '_' + vertAccessName + '_write_' + fragAccessName + '_read';
   1130                    desc = 'Varying array with ' + vertAccessName + ' write in vertex shader and ' + fragAccessName + ' read in fragment shader.';
   1131                    varyingGroup.addChild(es3fShaderIndexingTests.createVaryingArrayCase(name, desc, varType, vertAccess, fragAccess));
   1132                }
   1133            }
   1134        }
   1135 
   1136        // Uniform array access cases.
   1137        /** @type {tcuTestCase.DeqpTest} */ var uniformGroup = tcuTestCase.newTest("uniform_array", "Uniform array access tests.");
   1138        testGroup.addChild(uniformGroup);
   1139 
   1140        for (var typeNdx = 0; typeNdx < s_floatAndVecTypes.length; typeNdx++) {
   1141            varType = s_floatAndVecTypes[typeNdx];
   1142            for (var readAccessStr in es3fShaderIndexingTests.IndexAccessType) {
   1143                var readAccess = es3fShaderIndexingTests.IndexAccessType[readAccessStr];
   1144                readAccessName = es3fShaderIndexingTests.getIndexAccessTypeName(readAccess);
   1145                for (var shaderTypeNdx = 0; shaderTypeNdx < s_shaderTypes.length; shaderTypeNdx++) {
   1146                    shaderType = s_shaderTypes[shaderTypeNdx];
   1147                    shaderTypeName = gluShaderProgram.getShaderTypeName(shaderType);
   1148                    name = gluShaderUtil.getDataTypeName(varType) + "_" + readAccessName + "_read_" + shaderTypeName;
   1149                    desc = "Uniform array with " + readAccessName + " read in " + shaderTypeName + " shader.";
   1150                    isVertexCase = shaderType === gluShaderProgram.shaderType.VERTEX;
   1151                    uniformGroup.addChild(es3fShaderIndexingTests.createUniformArrayCase(name, desc, isVertexCase, varType, readAccess));
   1152                }
   1153            }
   1154        }
   1155 
   1156        // Temporary array access cases.
   1157        /** @type {tcuTestCase.DeqpTest} */ var tmpGroup = tcuTestCase.newTest("tmp_array", "Temporary array access tests.");
   1158        testGroup.addChild(tmpGroup);
   1159 
   1160        for (var typeNdx = 0; typeNdx < s_floatAndVecTypes.length; typeNdx++) {
   1161            varType = s_floatAndVecTypes[typeNdx];
   1162            for (var writeAccess in es3fShaderIndexingTests.IndexAccessType) {
   1163                for (var readAccess in es3fShaderIndexingTests.IndexAccessType) {
   1164                    writeAccessName = es3fShaderIndexingTests.getIndexAccessTypeName(es3fShaderIndexingTests.IndexAccessType[writeAccess]);
   1165                    readAccessName = es3fShaderIndexingTests.getIndexAccessTypeName(es3fShaderIndexingTests.IndexAccessType[readAccess]);
   1166 
   1167                    for (var shaderTypeNdx = 0; shaderTypeNdx < s_shaderTypes.length; shaderTypeNdx++) {
   1168                        shaderType = s_shaderTypes[shaderTypeNdx];
   1169                        shaderTypeName = gluShaderProgram.getShaderTypeName(shaderType);
   1170                        name = gluShaderUtil.getDataTypeName(varType) + "_" + writeAccessName + "_write_" + readAccessName + "_read_" + shaderTypeName;
   1171                        desc = "Temporary array with " + writeAccessName + " write and " + readAccessName + " read in " + shaderTypeName + " shader.";
   1172                        isVertexCase = (shaderType === gluShaderProgram.shaderType.VERTEX);
   1173                        tmpGroup.addChild(es3fShaderIndexingTests.createTmpArrayCase(name, desc, isVertexCase, varType, es3fShaderIndexingTests.IndexAccessType[writeAccess], es3fShaderIndexingTests.IndexAccessType[readAccess]));
   1174                    }
   1175                }
   1176            }
   1177        }
   1178 
   1179        // Vector indexing with subscripts.
   1180 
   1181        /** @type {Array<gluShaderUtil.DataType>} */ var s_vectorTypes = [
   1182            gluShaderUtil.DataType.FLOAT_VEC2,
   1183            gluShaderUtil.DataType.FLOAT_VEC3,
   1184            gluShaderUtil.DataType.FLOAT_VEC4
   1185        ];
   1186 
   1187        for (var typeNdx = 0; typeNdx < s_vectorTypes.length; typeNdx++) {
   1188            /** @type {tcuTestCase.DeqpTest} */ var vecGroup = tcuTestCase.newTest("vector_subscript", "Vector subscript indexing.");
   1189            testGroup.addChild(vecGroup);
   1190 
   1191            varType = s_vectorTypes[typeNdx];
   1192            for (var writeAccess in es3fShaderIndexingTests.VectorAccessType) {
   1193                for (var readAccess in es3fShaderIndexingTests.VectorAccessType) {
   1194                    writeAccessName = es3fShaderIndexingTests.getVectorAccessTypeName(es3fShaderIndexingTests.VectorAccessType[writeAccess]);
   1195                    readAccessName = es3fShaderIndexingTests.getVectorAccessTypeName(es3fShaderIndexingTests.VectorAccessType[readAccess]);
   1196 
   1197                    for (var shaderTypeNdx = 0; shaderTypeNdx < s_shaderTypes.length; shaderTypeNdx++) {
   1198                        shaderType = s_shaderTypes[shaderTypeNdx];
   1199                        shaderTypeName = gluShaderProgram.getShaderTypeName(shaderType);
   1200                        name = gluShaderUtil.getDataTypeName(varType) + "_" + writeAccessName + "_write_" + readAccessName + "_read_" + shaderTypeName;
   1201                        desc = "Vector subscript access with " + writeAccessName + " write and " + readAccessName + " read in " + shaderTypeName + " shader.";
   1202                        isVertexCase = shaderType === gluShaderProgram.shaderType.VERTEX;
   1203                        vecGroup.addChild(es3fShaderIndexingTests.createVectorSubscriptCase(name, desc, isVertexCase, varType, es3fShaderIndexingTests.VectorAccessType[writeAccess], es3fShaderIndexingTests.VectorAccessType[readAccess]));
   1204                    }
   1205                }
   1206            }
   1207        }
   1208 
   1209        // Matrix indexing with subscripts.
   1210        /** @type {Array<tcuTestCase.DeqpTest>} */ var matGroup = [
   1211            tcuTestCase.newTest("matrix_subscript", "Matrix subscript indexing."),
   1212            tcuTestCase.newTest("matrix_subscript", "Matrix subscript indexing."),
   1213            tcuTestCase.newTest("matrix_subscript", "Matrix subscript indexing."),
   1214        ];
   1215        for (var ii = 0; ii < matGroup.length; ++ii) {
   1216            testGroup.addChild(matGroup[ii]);
   1217        }
   1218 
   1219        /** @type {Array<gluShaderUtil.DataType>} */ var s_matrixTypes = [
   1220            gluShaderUtil.DataType.FLOAT_MAT2,
   1221            gluShaderUtil.DataType.FLOAT_MAT2X3,
   1222            gluShaderUtil.DataType.FLOAT_MAT2X4,
   1223            gluShaderUtil.DataType.FLOAT_MAT3X2,
   1224            gluShaderUtil.DataType.FLOAT_MAT3,
   1225            gluShaderUtil.DataType.FLOAT_MAT3X4,
   1226            gluShaderUtil.DataType.FLOAT_MAT4X2,
   1227            gluShaderUtil.DataType.FLOAT_MAT4X3,
   1228            gluShaderUtil.DataType.FLOAT_MAT4
   1229        ];
   1230 
   1231        for (var typeNdx = 0; typeNdx < s_matrixTypes.length; typeNdx++) {
   1232            varType = s_matrixTypes[typeNdx];
   1233            for (var writeAccess in es3fShaderIndexingTests.IndexAccessType) {
   1234                for (var readAccess in es3fShaderIndexingTests.IndexAccessType) {
   1235                    writeAccessName = es3fShaderIndexingTests.getIndexAccessTypeName(es3fShaderIndexingTests.IndexAccessType[writeAccess]);
   1236                    readAccessName = es3fShaderIndexingTests.getIndexAccessTypeName(es3fShaderIndexingTests.IndexAccessType[readAccess]);
   1237 
   1238                    for (var shaderTypeNdx = 0; shaderTypeNdx < s_shaderTypes.length; shaderTypeNdx++) {
   1239                        shaderType = s_shaderTypes[shaderTypeNdx];
   1240                        shaderTypeName = gluShaderProgram.getShaderTypeName(shaderType);
   1241                        name = gluShaderUtil.getDataTypeName(varType) + "_" + writeAccessName + "_write_" + readAccessName + "_read_" + shaderTypeName;
   1242                        desc = "Vector subscript access with " + writeAccessName + " write and " + readAccessName + " read in " + shaderTypeName + " shader.";
   1243                        isVertexCase = shaderType === gluShaderProgram.shaderType.VERTEX;
   1244                        matGroup[typeNdx % matGroup.length].addChild(es3fShaderIndexingTests.createMatrixSubscriptCase(
   1245                            name, desc, isVertexCase, varType, es3fShaderIndexingTests.IndexAccessType[writeAccess], es3fShaderIndexingTests.IndexAccessType[readAccess]));
   1246                    }
   1247                }
   1248            }
   1249        }
   1250    };
   1251 
   1252    /**
   1253     * Run test
   1254     * @param {WebGL2RenderingContext} context
   1255     */
   1256    es3fShaderIndexingTests.run = function(context, range) {
   1257        gl = context;
   1258        //Set up Test Root parameters
   1259        var state = tcuTestCase.runner;
   1260        state.setRoot(new es3fShaderIndexingTests.ShaderIndexingTests());
   1261 
   1262        //Set up name and description of this test series.
   1263        setCurrentTestName(state.testCases.fullName());
   1264        description(state.testCases.getDescription());
   1265 
   1266        try {
   1267            if (range)
   1268                state.setRange(range);
   1269            //Run test cases
   1270            tcuTestCase.runTestCases();
   1271        }
   1272        catch (err) {
   1273            testFailedOptions('Failed to es3fShaderIndexingTests.run tests', false);
   1274            tcuTestCase.runner.terminate();
   1275        }
   1276    };
   1277 
   1278 });