tor-browser

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

glsShaderLibraryCase.js (51318B)


      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.glsShaderLibraryCase');
     23 goog.require('framework.common.tcuTestCase');
     24 goog.require('framework.opengl.gluDrawUtil');
     25 goog.require('framework.opengl.gluShaderProgram');
     26 goog.require('framework.opengl.gluShaderUtil');
     27 
     28 goog.scope(function() {
     29 
     30 var glsShaderLibraryCase = modules.shared.glsShaderLibraryCase;
     31 var tcuTestCase = framework.common.tcuTestCase;
     32 var gluShaderProgram = framework.opengl.gluShaderProgram;
     33 var gluShaderUtil = framework.opengl.gluShaderUtil;
     34 var gluDrawUtil = framework.opengl.gluDrawUtil;
     35 
     36    /** @const @type {number} */ glsShaderLibraryCase.VIEWPORT_WIDTH = 128;
     37    /** @const @type {number} */ glsShaderLibraryCase.VIEWPORT_HEIGHT = 128;
     38 
     39 /**
     40 * Shader compilation expected result enum
     41 * @enum {number}
     42 */
     43 glsShaderLibraryCase.expectResult = {
     44    EXPECT_PASS: 0,
     45    EXPECT_COMPILE_FAIL: 1,
     46    EXPECT_LINK_FAIL: 2,
     47    EXPECT_COMPILE_LINK_FAIL: 3,
     48    EXPECT_VALIDATION_FAIL: 4,
     49    EXPECT_BUILD_SUCCESSFUL: 5
     50 };
     51 
     52 /**
     53 * Test case type
     54 * @enum {number}
     55 */
     56 glsShaderLibraryCase.caseType = {
     57    CASETYPE_COMPLETE: 0, //!< Has all shaders specified separately.
     58    CASETYPE_VERTEX_ONLY: 1, //!< "Both" case, vertex shader sub case.
     59    CASETYPE_FRAGMENT_ONLY: 2 //!< "Both" case, fragment shader sub case.
     60 };
     61 
     62 /**
     63 * glsShaderLibraryCase.BeforeDrawValidator target type enum
     64 * @enum {number}
     65 */
     66 glsShaderLibraryCase.targetType = {
     67    PROGRAM: 0,
     68    PIPELINE: 1
     69 };
     70 
     71 /**
     72 * Shader case type enum
     73 * @enum {number}
     74 */
     75 glsShaderLibraryCase.shaderCase = {
     76    STORAGE_INPUT: 0,
     77    STORAGE_OUTPUT: 1,
     78    STORAGE_UNIFORM: 2
     79 };
     80 
     81 /**
     82 * Checks if shader uses in/out qualifiers depending on the version
     83 * @param {string} version
     84 * @return {boolean} version
     85 */
     86 glsShaderLibraryCase.usesShaderInoutQualifiers = function(version) {
     87    switch (version) {
     88        case '100':
     89        case '130':
     90        case '140':
     91        case '150':
     92            return false;
     93 
     94        default:
     95            return true;
     96    }
     97 };
     98 
     99 /**
    100 * Checks if version supports fragment highp precision
    101 * @param {string} version
    102 * @return {boolean} version ,True when is different from version 100
    103 */
    104 glsShaderLibraryCase.supportsFragmentHighp = function(version) {
    105    return version !== '100';
    106 };
    107 
    108 /**
    109 * This functions builds a matching vertex shader for a 'both' case, when
    110 * the fragment shader is being tested.
    111 * We need to build attributes and varyings for each 'input'.
    112 * @param { {values:Array}} valueBlock
    113 * @return {string} res
    114 */
    115 glsShaderLibraryCase.genVertexShader = function(valueBlock) {
    116    /** @type {string} */ var res = '';
    117    /** @type {Object} */ var state = tcuTestCase.runner;
    118    /** @type {boolean} */ var usesInout = glsShaderLibraryCase.usesShaderInoutQualifiers(state.currentTest.spec.targetVersion);
    119    /** @type {string} */ var vtxIn = usesInout ? 'in' : 'attribute';
    120    /** @type {string} */ var vtxOut = usesInout ? 'out' : 'varying';
    121 
    122    res += '#version ' + state.currentTest.spec.targetVersion + '\n';
    123    res += 'precision highp float;\n';
    124    res += 'precision highp int;\n';
    125    res += '\n';
    126    res += vtxIn + ' highp vec4 dEQP_Position;\n';
    127 
    128    for (var ndx = 0; ndx < valueBlock.values.length; ndx++) {
    129        var val = valueBlock.values[ndx];
    130        if (val.storageType === glsShaderLibraryCase.shaderCase.STORAGE_INPUT) {
    131            /** @type {string} */ var floatType = gluShaderUtil.getDataTypeFloatScalars(val.dataType);
    132            res += vtxIn + ' ' + floatType + ' a_' + val.valueName + ';\n';
    133 
    134            if (gluShaderUtil.getDataTypeScalarType(val.dataType) === 'float')
    135                res += vtxOut + ' ' + floatType + ' ' + val.valueName + ';\n';
    136            else
    137                res += vtxOut + ' ' + floatType + ' v_' + val.valueName + ';\n';
    138        }
    139    }
    140    res += '\n';
    141 
    142    // Main function.
    143    // - gl_Position = dEQP_Position;
    144    // - for each input: write attribute directly to varying
    145    res += 'void main()\n';
    146    res += ' {\n';
    147    res += '\tgl_Position = dEQP_Position;\n';
    148    for (var ndx = 0; ndx < valueBlock.values.length; ndx++) {
    149        var val = valueBlock.values[ndx];
    150        if (val.storageType === glsShaderLibraryCase.shaderCase.STORAGE_INPUT) {
    151        /** @type {string} */ var name = val.valueName;
    152            if (gluShaderUtil.getDataTypeScalarType(val.dataType) === 'float')
    153                res += '\t' + name + ' = a_' + name + ';\n';
    154            else
    155                res += '\tv_' + name + ' = a_' + name + ';\n';
    156        }
    157    }
    158 
    159    res += '}\n';
    160    return res;
    161 };
    162 
    163 /**
    164 * @param { {values:Array}} valueBlock
    165 * @param {boolean} useFloatTypes
    166 * @return {string} stream
    167 */
    168 glsShaderLibraryCase.genCompareFunctions = function(valueBlock, useFloatTypes) {
    169    var cmpTypeFound = {};
    170    /** @type {string} */ var stream = '';
    171 
    172    for (var ndx = 0; ndx < valueBlock.values.length; ndx++) {
    173    /** @type {Array} */ var val = valueBlock.values[ndx];
    174        if (val.storageType === glsShaderLibraryCase.shaderCase.STORAGE_OUTPUT)
    175            cmpTypeFound[gluShaderUtil.getDataTypeName(val.dataType)] = true;
    176 
    177    }
    178    if (useFloatTypes) {
    179        if (cmpTypeFound['bool']) stream += 'bool isOk (float a, bool b) { return ((a > 0.5) == b); }\n';
    180        if (cmpTypeFound['bvec2']) stream += 'bool isOk (vec2 a, bvec2 b) { return (greaterThan(a, vec2(0.5)) == b); }\n';
    181        if (cmpTypeFound['bvec3']) stream += 'bool isOk (vec3 a, bvec3 b) { return (greaterThan(a, vec3(0.5)) == b); }\n';
    182        if (cmpTypeFound['bvec4']) stream += 'bool isOk (vec4 a, bvec4 b) { return (greaterThan(a, vec4(0.5)) == b); }\n';
    183        if (cmpTypeFound['int']) stream += 'bool isOk (float a, int b) { float atemp = a+0.5; return (float(b) <= atemp && atemp <= float(b+1)); }\n';
    184        if (cmpTypeFound['ivec2']) stream += 'bool isOk (vec2 a, ivec2 b) { return (ivec2(floor(a + 0.5)) == b); }\n';
    185        if (cmpTypeFound['ivec3']) stream += 'bool isOk (vec3 a, ivec3 b) { return (ivec3(floor(a + 0.5)) == b); }\n';
    186        if (cmpTypeFound['ivec4']) stream += 'bool isOk (vec4 a, ivec4 b) { return (ivec4(floor(a + 0.5)) == b); }\n';
    187        if (cmpTypeFound['uint']) stream += 'bool isOk (float a, uint b) { float atemp = a+0.5; return (float(b) <= atemp && atemp <= float(b+1u)); }\n';
    188        if (cmpTypeFound['uvec2']) stream += 'bool isOk (vec2 a, uvec2 b) { return (uvec2(floor(a + 0.5)) == b); }\n';
    189        if (cmpTypeFound['uvec3']) stream += 'bool isOk (vec3 a, uvec3 b) { return (uvec3(floor(a + 0.5)) == b); }\n';
    190        if (cmpTypeFound['uvec4']) stream += 'bool isOk (vec4 a, uvec4 b) { return (uvec4(floor(a + 0.5)) == b); }\n';
    191    } else {
    192        if (cmpTypeFound['bool']) stream += 'bool isOk (bool a, bool b) { return (a == b); }\n';
    193        if (cmpTypeFound['bvec2']) stream += 'bool isOk (bvec2 a, bvec2 b) { return (a == b); }\n';
    194        if (cmpTypeFound['bvec3']) stream += 'bool isOk (bvec3 a, bvec3 b) { return (a == b); }\n';
    195        if (cmpTypeFound['bvec4']) stream += 'bool isOk (bvec4 a, bvec4 b) { return (a == b); }\n';
    196        if (cmpTypeFound['int']) stream += 'bool isOk (int a, int b) { return (a == b); }\n';
    197        if (cmpTypeFound['ivec2']) stream += 'bool isOk (ivec2 a, ivec2 b) { return (a == b); }\n';
    198        if (cmpTypeFound['ivec3']) stream += 'bool isOk (ivec3 a, ivec3 b) { return (a == b); }\n';
    199        if (cmpTypeFound['ivec4']) stream += 'bool isOk (ivec4 a, ivec4 b) { return (a == b); }\n';
    200        if (cmpTypeFound['uint']) stream += 'bool isOk (uint a, uint b) { return (a == b); }\n';
    201        if (cmpTypeFound['uvec2']) stream += 'bool isOk (uvec2 a, uvec2 b) { return (a == b); }\n';
    202        if (cmpTypeFound['uvec3']) stream += 'bool isOk (uvec3 a, uvec3 b) { return (a == b); }\n';
    203        if (cmpTypeFound['uvec4']) stream += 'bool isOk (uvec4 a, uvec4 b) { return (a == b); }\n';
    204    }
    205 
    206    if (cmpTypeFound['float'])
    207        stream += 'bool isOk (float a, float b, float eps) { return (abs(a-b) <= (eps*abs(b) + eps)); }\n';
    208    if (cmpTypeFound['vec2'])
    209        stream += 'bool isOk (vec2 a, vec2 b, float eps) { return all(lessThanEqual(abs(a-b), (eps*abs(b) + eps))); }\n';
    210    if (cmpTypeFound['vec3'])
    211        stream += 'bool isOk (vec3 a, vec3 b, float eps) { return all(lessThanEqual(abs(a-b), (eps*abs(b) + eps))); }\n';
    212    if (cmpTypeFound['vec4'])
    213        stream += 'bool isOk (vec4 a, vec4 b, float eps) { return all(lessThanEqual(abs(a-b), (eps*abs(b) + eps))); }\n';
    214 
    215    if (cmpTypeFound['mat2'])
    216        stream += 'bool isOk (mat2 a, mat2 b, float eps) { vec2 diff = max(abs(a[0]-b[0]), abs(a[1]-b[1])); return all(lessThanEqual(diff, vec2(eps))); }\n';
    217    if (cmpTypeFound['mat2x3'])
    218        stream += 'bool isOk (mat2x3 a, mat2x3 b, float eps) { vec3 diff = max(abs(a[0]-b[0]), abs(a[1]-b[1])); return all(lessThanEqual(diff, vec3(eps))); }\n';
    219    if (cmpTypeFound['mat2x4'])
    220        stream += 'bool isOk (mat2x4 a, mat2x4 b, float eps) { vec4 diff = max(abs(a[0]-b[0]), abs(a[1]-b[1])); return all(lessThanEqual(diff, vec4(eps))); }\n';
    221    if (cmpTypeFound['mat3x2'])
    222        stream += 'bool isOk (mat3x2 a, mat3x2 b, float eps) { vec2 diff = max(max(abs(a[0]-b[0]), abs(a[1]-b[1])), abs(a[2]-b[2])); return all(lessThanEqual(diff, vec2(eps))); }\n';
    223    if (cmpTypeFound['mat3'])
    224        stream += 'bool isOk (mat3 a, mat3 b, float eps) { vec3 diff = max(max(abs(a[0]-b[0]), abs(a[1]-b[1])), abs(a[2]-b[2])); return all(lessThanEqual(diff, vec3(eps))); }\n';
    225    if (cmpTypeFound['mat3x4'])
    226        stream += 'bool isOk (mat3x4 a, mat3x4 b, float eps) { vec4 diff = max(max(abs(a[0]-b[0]), abs(a[1]-b[1])), abs(a[2]-b[2])); return all(lessThanEqual(diff, vec4(eps))); }\n';
    227    if (cmpTypeFound['mat4x2'])
    228        stream += 'bool isOk (mat4x2 a, mat4x2 b, float eps) { vec2 diff = max(max(abs(a[0]-b[0]), abs(a[1]-b[1])), max(abs(a[2]-b[2]), abs(a[3]-b[3]))); return all(lessThanEqual(diff, vec2(eps))); }\n';
    229    if (cmpTypeFound['mat4x3'])
    230        stream += 'bool isOk (mat4x3 a, mat4x3 b, float eps) { vec3 diff = max(max(abs(a[0]-b[0]), abs(a[1]-b[1])), max(abs(a[2]-b[2]), abs(a[3]-b[3]))); return all(lessThanEqual(diff, vec3(eps))); }\n';
    231    if (cmpTypeFound['mat4'])
    232        stream += 'bool isOk (mat4 a, mat4 b, float eps) { vec4 diff = max(max(abs(a[0]-b[0]), abs(a[1]-b[1])), max(abs(a[2]-b[2]), abs(a[3]-b[3]))); return all(lessThanEqual(diff, vec4(eps))); }\n';
    233 
    234    return stream;
    235 };
    236 
    237 /**
    238 * @param {string} dstVec4Var
    239 * @param { {values:Array}} valueBlock
    240 * @param {string} nonFloatNamePrefix
    241 * @param {?string=} checkVarName
    242 * @return {string} output
    243 */
    244 glsShaderLibraryCase.genCompareOp = function(dstVec4Var, valueBlock, nonFloatNamePrefix, checkVarName) {
    245 
    246    /** @type {boolean} */ var isFirstOutput = true;
    247    /** @type {string} */ var output = '';
    248 
    249    for (var ndx = 0; ndx < valueBlock.values.length; ndx++) {
    250    /** @type {Array} */ var val = valueBlock.values[ndx];
    251    /** @type {string} */ var valueName = val.valueName;
    252 
    253        if (val.storageType === glsShaderLibraryCase.shaderCase.STORAGE_OUTPUT) {
    254            // Check if we're only interested in one variable (then skip if not the right one).
    255            if (checkVarName && (valueName !== checkVarName))
    256                continue;
    257 
    258            // Prefix.
    259            if (isFirstOutput) {
    260                output += 'bool RES = ';
    261                isFirstOutput = false;
    262            } else
    263                output += 'RES = RES && ';
    264 
    265            // Generate actual comparison.
    266            if (gluShaderUtil.getDataTypeScalarType(val.dataType) === 'float')
    267                output += 'isOk(' + valueName + ', ref_' + valueName + ', 0.05);\n';
    268            else
    269                output += 'isOk(' + nonFloatNamePrefix + valueName + ', ref_' + valueName + ');\n';
    270        }
    271        // \note Uniforms are already declared in shader.
    272    }
    273 
    274    if (isFirstOutput)
    275        output += dstVec4Var + ' = vec4(1.0);\n'; // \todo [petri] Should we give warning if not expect-failure case?
    276    else
    277        output += dstVec4Var + ' = vec4(RES, RES, RES, 1.0);\n';
    278 
    279    return output;
    280 };
    281 
    282 /**
    283 * @param { {values:Array}} valueBlock
    284 * @return {string} shader
    285 */
    286 glsShaderLibraryCase.genFragmentShader = function(valueBlock) {
    287    /** @type {string} */ var shader = '';
    288    /** @type {Object} */ var state = tcuTestCase.runner;
    289    /** @type {boolean} */ var usesInout = glsShaderLibraryCase.usesShaderInoutQualifiers(state.currentTest.spec.targetVersion);
    290    /** @type {string} */ var vtxIn = usesInout ? 'in' : 'attribute';
    291    /** @type {string} */ var vtxOut = usesInout ? 'out' : 'varying';
    292    /** @type {boolean} */ var customColorOut = usesInout;
    293    /** @type {string} */ var fragIn = usesInout ? 'in' : 'varying';
    294    /** @type {string} */ var prec = glsShaderLibraryCase.supportsFragmentHighp(state.currentTest.spec.targetVersion) ? 'highp' : 'mediump';
    295 
    296    shader += '#version ' + state.currentTest.spec.targetVersion + '\n';
    297 
    298    shader += 'precision ' + prec + ' float;\n';
    299    shader += 'precision ' + prec + ' int;\n';
    300    shader += '\n';
    301 
    302    if (customColorOut) {
    303        shader += 'layout(location = 0) out mediump vec4 dEQP_FragColor;\n';
    304        shader += '\n';
    305    }
    306 
    307    shader += glsShaderLibraryCase.genCompareFunctions(valueBlock, true);
    308    shader += '\n';
    309 
    310    // Declarations (varying, reference for each output).
    311    for (var ndx = 0; ndx < valueBlock.values.length; ndx++) {
    312    /** @type {Array} */ var val = valueBlock.values[ndx];
    313    /** @type {string} */ var floatType = gluShaderUtil.getDataTypeFloatScalars(val.dataType);
    314    /** @type {string} */ var refType = gluShaderUtil.getDataTypeName(val.dataType);
    315 
    316        if (val.storageType == glsShaderLibraryCase.shaderCase.STORAGE_OUTPUT) {
    317            if (gluShaderUtil.getDataTypeScalarType(val.dataType) === 'float')
    318                shader += fragIn + ' ' + floatType + ' ' + val.valueName + ';\n';
    319            else
    320                shader += fragIn + ' ' + floatType + ' v_' + val.valueName + ';\n';
    321 
    322            shader += 'uniform ' + refType + ' ref_' + val.valueName + ';\n';
    323        }
    324    }
    325 
    326    shader += '\n';
    327    shader += 'void main()\n';
    328    shader += ' {\n';
    329 
    330    shader += '\t';
    331    shader += glsShaderLibraryCase.genCompareOp(customColorOut ? 'dEQP_FragColor' : 'gl_FragColor', valueBlock, 'v_', null);
    332 
    333    shader += '}\n';
    334    return shader;
    335 };
    336 
    337 glsShaderLibraryCase.caseRequirement = (function() {
    338 
    339 /**
    340 * @constructor
    341 */
    342 var CaseRequirement = function() {
    343 
    344 /**
    345 * @param {number} shaderType
    346 * @return {boolean}
    347 */
    348    this.isAffected = function(shaderType) {
    349        for (var i = 0; i < this.shaderTypes.length; i++)
    350            if (this.shaderTypes[i] === shaderType)
    351                return true;
    352        return false;
    353    };
    354 
    355    this.checkRequirements = function(gl) {
    356        if (this.type === requirementType.EXTENSION) {
    357            var extns = gl.getSupportedExtensions();
    358            for (var i = 0; i < extns.length; i++)
    359                for (var j = 0; j < this.requirements.length; j++)
    360                    if (extns[i] === this.requirements[j]) {
    361                        this.supportedExtension = this.requirements[j];
    362                        return true;
    363                    }
    364            if (this.requirements.length === 1)
    365                throw Error('Test requires extension of ' + this.requirements[0]);
    366            else
    367                throw Error('Test requires any extension of ' + this.requirements);
    368        } else if (this.type === requirementType.IMPLEMENTATION_LIMIT) {
    369            var value = gl.getParameter(this.enumName);
    370            assertMsgOptions(gl.getError() === gl.NO_ERROR, 'Failed to read parameter ' + this.enumName, false, true);
    371 
    372            if (!(value > this.referenceValue))
    373                throw Error('Test requires ' + this.enumName + ' (' + value + ') > ' + this.referenceValue);
    374        }
    375    };
    376 
    377    this.getSupportedExtension = function() {
    378        return this.supportedExtension;
    379    };
    380 
    381 };
    382 
    383 var createAnyExtensionRequirement = function(requirements, shaderTypes) {
    384    var cr = new CaseRequirement();
    385    cr.type = requirementType.EXTENSION;
    386    cr.requirements = requirements;
    387    cr.shaderTypes = shaderTypes;
    388    return cr;
    389 };
    390 
    391 var createLimitRequirement = function(enumName, ref) {
    392    var cr = new CaseRequirement();
    393    cr.type = requirementType.IMPLEMENTATION_LIMIT;
    394    cr.enumName = enumName;
    395    cr.referenceValue = ref;
    396 };
    397 
    398 /**
    399 * @enum {number}
    400 */
    401 var requirementType = {
    402    EXTENSION: 0,
    403    IMPLEMENTATION_LIMIT: 1
    404 };
    405 
    406 return {
    407    createAnyExtensionRequirement: createAnyExtensionRequirement,
    408    createLimitRequirement: createLimitRequirement,
    409    requirementType: requirementType
    410 };
    411 
    412 }());
    413 
    414 /** Specialize a shader only for the vertex test case.
    415 * @param {string} baseCode
    416 * @param {number} shaderType
    417 * @param {Array<Object>} requirements
    418 * @return {string} resultBuf
    419 */
    420 glsShaderLibraryCase.injectExtensionRequirements = function(baseCode, shaderType, requirements) {
    421 /**
    422 * @param {Array<Object>} requirements
    423 * @param {number} shaderType
    424 * @return {string} buf
    425 */
    426    var generateExtensionStatements = function(requirements, shaderType) {
    427        /** @type {string} */ var buf = '';
    428 
    429        if (requirements)
    430            for (var ndx = 0; ndx < requirements.length; ndx++)
    431                if (requirements[ndx].type === glsShaderLibraryCase.caseRequirement.requirementType.EXTENSION &&
    432                    requirements[ndx].isAffected(shaderType))
    433                    buf += '#extension ' + requirements[ndx].getSupportedExtension() + ' : require\n';
    434 
    435        return buf;
    436    };
    437 
    438    /** @type {string} */ var extensions = generateExtensionStatements(requirements, shaderType);
    439 
    440    if (extensions.length === 0)
    441        return baseCode;
    442 
    443    /** @type {Array<string>} */ var splitLines = baseCode.split('\n');
    444    /** @type {boolean} */ var firstNonPreprocessorLine = true;
    445    /** @type {string} */ var resultBuf = '';
    446 
    447    for (var i = 0; i < splitLines.length; i++) {
    448        /** @const @type {boolean} */ var isPreprocessorDirective = (splitLines[i].match(/^\s*#/) !== null);
    449 
    450        if (!isPreprocessorDirective && firstNonPreprocessorLine) {
    451            firstNonPreprocessorLine = false;
    452            resultBuf += extensions;
    453        }
    454 
    455        resultBuf += splitLines[i] + '\n';
    456    }
    457 
    458    return resultBuf;
    459 };
    460 
    461 /** Specialize a shader for the vertex shader test case.
    462 * @param {string} src
    463 * @param { {values:Array}} valueBlock
    464 * @return {string} withExt
    465 */
    466 glsShaderLibraryCase.specializeVertexShader = function(src, valueBlock) {
    467    /** @type {string} */ var decl = '';
    468    /** @type {string} */ var setup = '';
    469    /** @type {string} */ var output = '';
    470    /** @type {Object} */ var state = tcuTestCase.runner;
    471    /** @type {boolean} */ var usesInout = glsShaderLibraryCase.usesShaderInoutQualifiers(state.currentTest.spec.targetVersion);
    472    /** @type {string} */ var vtxIn = usesInout ? 'in' : 'attribute';
    473    /** @type {string} */ var vtxOut = usesInout ? 'out' : 'varying';
    474 
    475    // Output (write out position).
    476    output += 'gl_Position = dEQP_Position;\n';
    477 
    478    // Declarations (position + attribute for each input, varying for each output).
    479    decl += vtxIn + ' highp vec4 dEQP_Position;\n';
    480    for (var ndx = 0; ndx < valueBlock.values.length; ndx++) {
    481    /** @type {Array} */ var val = valueBlock.values[ndx];
    482    /** @type {string} */ var valueName = val.valueName;
    483    /** @type {string} */ var floatType = gluShaderUtil.getDataTypeFloatScalars(val.dataType);
    484    /** @type {string} */ var dataTypeName = gluShaderUtil.getDataTypeName(val.dataType);
    485 
    486        if (val.storageType === glsShaderLibraryCase.shaderCase.STORAGE_INPUT) {
    487            if (gluShaderUtil.getDataTypeScalarType(val.dataType) === 'float') {
    488                decl += vtxIn + ' ' + floatType + ' ' + valueName + ';\n';
    489            } else {
    490                decl += vtxIn + ' ' + floatType + ' a_' + valueName + ';\n';
    491                setup += dataTypeName + ' ' + valueName + ' = ' + dataTypeName + '(a_' + valueName + ');\n';
    492            }
    493        } else if (val.storageType === glsShaderLibraryCase.shaderCase.STORAGE_OUTPUT) {
    494            if (gluShaderUtil.getDataTypeScalarType(val.dataType) === 'float')
    495                decl += vtxOut + ' ' + floatType + ' ' + valueName + ';\n';
    496            else {
    497                decl += vtxOut + ' ' + floatType + ' v_' + valueName + ';\n';
    498                decl += dataTypeName + ' ' + valueName + ';\n';
    499 
    500                output += 'v_' + valueName + ' = ' + floatType + '(' + valueName + ');\n';
    501            }
    502        }
    503    }
    504 
    505    /** @type {string} */
    506    var baseSrc = src
    507                    .replace(/\$\{DECLARATIONS\}/g, decl)
    508                    .replace(/\$\{DECLARATIONS:single-line\}/g, decl.replace(/\n/g, ' '))
    509                    .replace(/\$\{SETUP\}/g, setup)
    510                    .replace(/\$\{OUTPUT\}/g, output)
    511                    .replace(/\$\{POSITION_FRAG_COLOR\}/g, 'gl_Position');
    512 
    513    /** @type {string} */
    514    var withExt = glsShaderLibraryCase.injectExtensionRequirements(baseSrc, gluShaderProgram.shaderType.VERTEX, state.currentTest.spec.requirements);
    515 
    516    return withExt;
    517 };
    518 
    519 /** Specialize a shader only for the vertex test case.
    520 * @param {string} src
    521 * @param { {values:Array}} valueBlock
    522 * @return {string} withExt
    523 */
    524 glsShaderLibraryCase.specializeVertexOnly = function(src, valueBlock) {
    525    /** @type {string} */ var decl = '';
    526    /** @type {string} */ var setup = '';
    527    /** @type {string} */ var output = '';
    528    /** @type {Object} */ var state = tcuTestCase.runner;
    529    /** @type {boolean} */ var usesInout = glsShaderLibraryCase.usesShaderInoutQualifiers(state.currentTest.spec.targetVersion);
    530    /** @type {string} */ var vtxIn = usesInout ? 'in' : 'attribute';
    531 
    532    // Output (write out position).
    533    output += 'gl_Position = dEQP_Position;\n';
    534 
    535    // Declarations (position + attribute for each input, varying for each output).
    536    decl += vtxIn + ' highp vec4 dEQP_Position;\n';
    537 
    538    for (var ndx = 0; ndx < valueBlock.values.length; ndx++) {
    539    /** @type {Array} */ var val = valueBlock.values[ndx];
    540    /** @type {string} */ var valueName = val.valueName;
    541    /** @type {string} */ var type = gluShaderUtil.getDataTypeName(val.dataType);
    542 
    543        if (val.storageType === glsShaderLibraryCase.shaderCase.STORAGE_INPUT) {
    544            if (gluShaderUtil.getDataTypeScalarType(val.dataType) === 'float') {
    545                decl += vtxIn + ' ' + type + ' ' + valueName + ';\n';
    546            } else {
    547                /** @type {string} */ var floatType = gluShaderUtil.getDataTypeFloatScalars(val.dataType);
    548 
    549                decl += vtxIn + ' ' + floatType + ' a_' + valueName + ';\n';
    550                setup += type + ' ' + valueName + ' = ' + type + '(a_' + valueName + ');\n';
    551            }
    552        } else if (val.storageType === glsShaderLibraryCase.shaderCase.STORAGE_UNIFORM &&
    553                    !val.valueName.match('\\.'))
    554            decl += 'uniform ' + type + ' ' + valueName + ';\n';
    555    }
    556 
    557    /** @type {string} */
    558    var baseSrc = src
    559                    .replace(/\$\{VERTEX_DECLARATIONS\}/g, decl)
    560                    .replace(/\$\{VERTEX_DECLARATIONS:single-line\}/g, decl.replace(/\n/g, ' '))
    561                    .replace(/\$\{VERTEX_SETUP\}/g, setup)
    562                    .replace(/\$\{VERTEX_OUTPUT\}/g, output);
    563 
    564    /** @type {string} */
    565    var withExt = glsShaderLibraryCase.injectExtensionRequirements(baseSrc, gluShaderProgram.shaderType.VERTEX, state.currentTest.spec.requirements);
    566 
    567    return withExt;
    568 };
    569 
    570 /** Specialize a shader for the fragment shader test case.
    571 * @param {string} src
    572 * @param { {values:Array}} valueBlock
    573 * @return {string} withExt
    574 */
    575 glsShaderLibraryCase.specializeFragmentShader = function(src, valueBlock) {
    576    /** @type {string} */ var decl = '';
    577    /** @type {string} */ var setup = '';
    578    /** @type {string} */ var output = '';
    579 
    580    /** @type {Object} */ var state = tcuTestCase.runner;
    581 
    582    /** @type {boolean} */ var usesInout = glsShaderLibraryCase.usesShaderInoutQualifiers(state.currentTest.spec.targetVersion);
    583    /** @type {boolean} */ var customColorOut = usesInout;
    584    /** @type {string} */ var fragIn = usesInout ? 'in' : 'varying';
    585    /** @type {string} */ var fragColor = customColorOut ? 'dEQP_FragColor' : 'gl_FragColor';
    586 
    587    decl += glsShaderLibraryCase.genCompareFunctions(valueBlock, false);
    588    output += glsShaderLibraryCase.genCompareOp(fragColor, valueBlock, '', null);
    589 
    590    if (customColorOut)
    591        decl += 'layout(location = 0) out mediump vec4 dEQP_FragColor;\n';
    592 
    593    for (var ndx = 0; ndx < valueBlock.values.length; ndx++) {
    594    /** @type {Array} */ var val = valueBlock.values[ndx];
    595    /** @type {string} */ var valueName = val.valueName;
    596    /** @type {string} */ var floatType = gluShaderUtil.getDataTypeFloatScalars(val.dataType);
    597    /** @type {string} */ var refType = gluShaderUtil.getDataTypeName(val.dataType);
    598 
    599        if (val.storageType === glsShaderLibraryCase.shaderCase.STORAGE_INPUT) {
    600            if (gluShaderUtil.getDataTypeScalarType(val.dataType) === 'float')
    601                decl += fragIn + ' ' + floatType + ' ' + valueName + ';\n';
    602            else {
    603                decl += fragIn + ' ' + floatType + ' v_' + valueName + ';\n';
    604                var offset = gluShaderUtil.isDataTypeIntOrIVec(val.dataType) ? ' * 1.0025' : ''; // \todo [petri] bit of a hack to avoid errors in chop() due to varying interpolation
    605                setup += refType + ' ' + valueName + ' = ' + refType + '(v_' + valueName + offset + ');\n';
    606            }
    607        } else if (val.storageType === glsShaderLibraryCase.shaderCase.STORAGE_OUTPUT) {
    608            decl += 'uniform ' + refType + ' ref_' + valueName + ';\n';
    609            decl += refType + ' ' + valueName + ';\n';
    610        }
    611    }
    612 
    613    /* \todo [2010-04-01 petri] Check all outputs. */
    614 
    615    /** @type {string} */
    616    var baseSrc = src
    617                    .replace(/\$\{DECLARATIONS\}/g, decl)
    618                    .replace(/\$\{DECLARATIONS:single-line\}/g, decl.replace(/\n/g, ' '))
    619                    .replace(/\$\{SETUP\}/g, setup)
    620                    .replace(/\$\{OUTPUT\}/g, output)
    621                    .replace(/\$\{POSITION_FRAG_COLOR\}/g, fragColor);
    622 
    623    /** @type {string} */
    624    var withExt = glsShaderLibraryCase.injectExtensionRequirements(baseSrc, gluShaderProgram.shaderType.FRAGMENT, state.currentTest.spec.requirements);
    625 
    626    return withExt;
    627 };
    628 
    629 /** Specialize a shader only for the fragment test case.
    630 * @param {string} src
    631 * @param { {values:Array}} valueBlock
    632 * @return {string} withExt
    633 */
    634 glsShaderLibraryCase.specializeFragmentOnly = function(src, valueBlock) {
    635    /** @type {string} */ var decl = '';
    636    /** @type {string} */ var output = '';
    637    /** @type {Object} */ var state = tcuTestCase.runner;
    638    /** @type {boolean} */ var usesInout = glsShaderLibraryCase.usesShaderInoutQualifiers(state.currentTest.spec.targetVersion);
    639    /** @type {boolean} */ var customColorOut = usesInout;
    640    /** @type {string} */ var fragIn = usesInout ? 'in' : 'varying';
    641    /** @type {string} */ var fragColor = customColorOut ? 'dEQP_FragColor' : 'gl_FragColor';
    642 
    643    decl += glsShaderLibraryCase.genCompareFunctions(valueBlock, false);
    644    output += glsShaderLibraryCase.genCompareOp(fragColor, valueBlock, '', null);
    645 
    646    if (customColorOut)
    647        decl += 'layout(location = 0) out mediump vec4 dEQP_FragColor;\n';
    648 
    649    for (var ndx = 0; ndx < valueBlock.values.length; ndx++) {
    650    /** @type {Array} */ var val = valueBlock.values[ndx];
    651    /** @type {string} */ var valueName = val.valueName;
    652    /** @type {string} */ var floatType = gluShaderUtil.getDataTypeFloatScalars(val.dataType);
    653    /** @type {string} */ var refType = gluShaderUtil.getDataTypeName(val.dataType);
    654 
    655        if (val.storageType === glsShaderLibraryCase.shaderCase.STORAGE_OUTPUT) {
    656            decl += 'uniform ' + refType + ' ref_' + valueName + ';\n';
    657            decl += refType + ' ' + valueName + ';\n';
    658        } else if (val.storageType === glsShaderLibraryCase.shaderCase.STORAGE_UNIFORM &&
    659                   !valueName.match('\\.'))
    660            decl += 'uniform ' + refType + ' ' + valueName + ';\n';
    661    }
    662 
    663    /** @type {string} */
    664    var baseSrc = src
    665                     .replace(/\$\{FRAGMENT_DECLARATIONS\}/g, decl)
    666                     .replace(/\$\{FRAGMENT_DECLARATIONS:single-line\}/g, decl.replace(/\n/g, ' '))
    667                     .replace(/\$\{FRAGMENT_OUTPUT\}/g, output)
    668                     .replace(/\$\{FRAG_COLOR\}/g, fragColor);
    669 
    670    /** @type {string} */
    671    var withExt = glsShaderLibraryCase.injectExtensionRequirements(baseSrc, gluShaderProgram.shaderType.FRAGMENT, state.currentTest.spec.requirements);
    672 
    673    return withExt;
    674 };
    675 
    676 /**
    677 * Is tessellation present
    678 * @return {boolean} True if tessellation is present
    679 */
    680 glsShaderLibraryCase.isTessellationPresent = function() {
    681    /* TODO: GLES 3.1: implement */
    682    return false;
    683 };
    684 
    685 glsShaderLibraryCase.setUniformValue = function(gl, pipelinePrograms, name, val, arrayNdx) {
    686    /** @type {boolean} */ var foundAnyMatch = false;
    687 
    688    for (var programNdx = 0; programNdx < pipelinePrograms.length; ++programNdx) {
    689        /** @const @type {WebGLUniformLocation} */ var loc = gl.getUniformLocation(pipelinePrograms[programNdx], name);
    690        /** @const @type {number} */ var scalarSize = gluShaderUtil.getDataTypeScalarSize(val.dataType);
    691        /** @const @type {number} */ var elemNdx = (val.arrayLength === 1) ? (0) : (arrayNdx * scalarSize);
    692 
    693        if (!loc)
    694            continue;
    695 
    696        foundAnyMatch = true;
    697 
    698        gl.useProgram(pipelinePrograms[programNdx]);
    699 
    700        /** @type {Array} */ var element = val.elements.slice(elemNdx, elemNdx + scalarSize);
    701        switch (val.dataType) {
    702            case gluShaderUtil.DataType.FLOAT: gl.uniform1fv(loc, new Float32Array(element)); break;
    703            case gluShaderUtil.DataType.FLOAT_VEC2: gl.uniform2fv(loc, new Float32Array(element)); break;
    704            case gluShaderUtil.DataType.FLOAT_VEC3: gl.uniform3fv(loc, new Float32Array(element)); break;
    705            case gluShaderUtil.DataType.FLOAT_VEC4: gl.uniform4fv(loc, new Float32Array(element)); break;
    706            case gluShaderUtil.DataType.FLOAT_MAT2: gl.uniformMatrix2fv(loc, false, new Float32Array(element)); break;
    707            case gluShaderUtil.DataType.FLOAT_MAT3: gl.uniformMatrix3fv(loc, false, new Float32Array(element)); break;
    708            case gluShaderUtil.DataType.FLOAT_MAT4: gl.uniformMatrix4fv(loc, false, new Float32Array(element)); break;
    709            case gluShaderUtil.DataType.INT: gl.uniform1iv(loc, new Int32Array(element)); break;
    710            case gluShaderUtil.DataType.INT_VEC2: gl.uniform2iv(loc, new Int32Array(element)); break;
    711            case gluShaderUtil.DataType.INT_VEC3: gl.uniform3iv(loc, new Int32Array(element)); break;
    712            case gluShaderUtil.DataType.INT_VEC4: gl.uniform4iv(loc, new Int32Array(element)); break;
    713 
    714            /** TODO: What type should be used for bool uniforms? */
    715            case gluShaderUtil.DataType.BOOL: gl.uniform1iv(loc, new Int32Array(element)); break;
    716            case gluShaderUtil.DataType.BOOL_VEC2: gl.uniform2iv(loc, new Int32Array(element)); break;
    717            case gluShaderUtil.DataType.BOOL_VEC3: gl.uniform3iv(loc, new Int32Array(element)); break;
    718            case gluShaderUtil.DataType.BOOL_VEC4: gl.uniform4iv(loc, new Int32Array(element)); break;
    719 
    720            case gluShaderUtil.DataType.UINT: gl.uniform1uiv(loc, new Uint32Array(element)); break;
    721            case gluShaderUtil.DataType.UINT_VEC2: gl.uniform2uiv(loc, new Uint32Array(element)); break;
    722            case gluShaderUtil.DataType.UINT_VEC3: gl.uniform3uiv(loc, new Uint32Array(element)); break;
    723            case gluShaderUtil.DataType.UINT_VEC4: gl.uniform4uiv(loc, new Uint32Array(element)); break;
    724            case gluShaderUtil.DataType.FLOAT_MAT2X3: gl.uniformMatrix2x3fv(loc, false, new Float32Array(element)); break;
    725            case gluShaderUtil.DataType.FLOAT_MAT2X4: gl.uniformMatrix2x4fv(loc, false, new Float32Array(element)); break;
    726            case gluShaderUtil.DataType.FLOAT_MAT3X2: gl.uniformMatrix3x2fv(loc, false, new Float32Array(element)); break;
    727            case gluShaderUtil.DataType.FLOAT_MAT3X4: gl.uniformMatrix3x4fv(loc, false, new Float32Array(element)); break;
    728            case gluShaderUtil.DataType.FLOAT_MAT4X2: gl.uniformMatrix4x2fv(loc, false, new Float32Array(element)); break;
    729            case gluShaderUtil.DataType.FLOAT_MAT4X3: gl.uniformMatrix4x3fv(loc, false, new Float32Array(element)); break;
    730 
    731            default:
    732                testFailed('Unknown data type ' + val.dataType);
    733        }
    734    }
    735 
    736    if (!foundAnyMatch)
    737        bufferedLogToConsole('WARNING // Uniform \"' + name + '\" location is not valid, location = -1. Cannot set value to the uniform.');
    738 };
    739 
    740 /**
    741 * Evaluates pixels, if they are white, black or there is any unexpected result
    742 * @param {gluDrawUtil.Surface} surface
    743 * @param {number} minX
    744 * @param {number} maxX
    745 * @param {number} minY
    746 * @param {number} maxY
    747 * @return {boolean} True if tessellation is present
    748 */
    749 glsShaderLibraryCase.checkPixels = function(surface, minX, maxX, minY, maxY) {
    750    /** @type {boolean} */ var allWhite = true;
    751    /** @type {boolean} */ var allBlack = true;
    752    /** @type {boolean} */ var anyUnexpected = false;
    753 
    754    assertMsgOptions((maxX > minX) && (maxY > minY), 'glsShaderLibraryCase.checkPixels sanity check', false, true);
    755 
    756    for (var y = minY; y <= maxY; y++) {
    757        for (var x = minX; x <= maxX; x++) {
    758            /** @type {number} */ var pixel = surface.getPixelUintRGB8(x, y);
    759            /** @type {boolean} */ var isWhite = (pixel == 0xFFFFFF);
    760            /** @type {boolean} */ var isBlack = (pixel == 0x000000);
    761 
    762            allWhite = allWhite && isWhite;
    763            allBlack = allBlack && isBlack;
    764            anyUnexpected = anyUnexpected || (!isWhite && !isBlack);
    765 
    766            // Early terminate as soon as we know the check hasn't passed
    767            if (!allWhite && !allBlack)
    768                break;
    769        }
    770    }
    771 
    772    if (!allWhite) {
    773        if (anyUnexpected)
    774            testFailed('WARNING: expecting all rendered pixels to be white or black, but got other colors as well!');
    775        else if (!allBlack)
    776            testFailed('WARNING: got inconsistent results over the image, when all pixels should be the same color!');
    777 
    778        return false;
    779    }
    780    return true;
    781 };
    782 
    783 /**
    784 * Initialize a test case
    785 */
    786 glsShaderLibraryCase.init = function() {
    787 /** @type {Object} */ var state = tcuTestCase.runner;
    788 /** @type {Object} */ var test = state.currentTest;
    789 
    790    bufferedLogToConsole('Processing ' + test.fullName());
    791 
    792    if (!test.spec.valueBlockList.length)
    793        test.spec.valueBlockList.push(glsShaderLibraryCase.genValueBlock());
    794    /** @type { {values:Array}} */ var valueBlock = test.spec.valueBlockList[0];
    795 
    796    if (test.spec.requirements)
    797        for (var ndx = 0; ndx < test.spec.requirements.length; ++ndx)
    798            test.spec.requirements[ndx].checkRequirements();
    799 
    800    /** @type {Array<gluShaderProgram.ShaderInfo>} */ var sources = [];
    801 
    802    if (test.spec.caseType === glsShaderLibraryCase.caseType.CASETYPE_COMPLETE) {
    803    /** @type {string} */ var vertex = glsShaderLibraryCase.specializeVertexOnly(test.spec.vertexSource, valueBlock);
    804    /** @type {string} */ var fragment = glsShaderLibraryCase.specializeFragmentOnly(test.spec.fragmentSource, valueBlock);
    805        sources.push(gluShaderProgram.genVertexSource(vertex));
    806        sources.push(gluShaderProgram.genFragmentSource(fragment));
    807    } else if (test.spec.caseType === glsShaderLibraryCase.caseType.CASETYPE_VERTEX_ONLY) {
    808        sources.push(gluShaderProgram.genVertexSource(glsShaderLibraryCase.specializeVertexShader(test.spec.vertexSource, valueBlock)));
    809        sources.push(gluShaderProgram.genFragmentSource(glsShaderLibraryCase.genFragmentShader(valueBlock)));
    810    } else if (test.spec.caseType === glsShaderLibraryCase.caseType.CASETYPE_FRAGMENT_ONLY) {
    811        sources.push(gluShaderProgram.genVertexSource(glsShaderLibraryCase.genVertexShader(valueBlock)));
    812        sources.push(gluShaderProgram.genFragmentSource(glsShaderLibraryCase.specializeFragmentShader(test.spec.fragmentSource, valueBlock)));
    813    }
    814 
    815    test.programs = [];
    816    test.programs.push({
    817            programSources: {
    818                sources: sources
    819            }
    820        }
    821    );
    822 
    823 };
    824 
    825 /**
    826 * Execute a test case
    827 * @return {boolean} True if test case passed
    828 */
    829 glsShaderLibraryCase.execute = function() {
    830    /** @const @type {number} */ var quadSize = 1.0;
    831    /** @const @type {Array<number>} */
    832    var s_positions = [
    833        -quadSize, -quadSize, 0.0, 1.0,
    834        -quadSize, +quadSize, 0.0, 1.0,
    835        +quadSize, -quadSize, 0.0, 1.0,
    836        +quadSize, +quadSize, 0.0, 1.0
    837    ];
    838 
    839    /** @const @type {Array<number>} */
    840    var s_indices = [
    841        0, 1, 2,
    842        1, 3, 2
    843    ];
    844 
    845    var wtu = WebGLTestUtils;
    846    /** @type {WebGL2RenderingContext} */ var gl = wtu.create3DContext('canvas');
    847    /** @type {Object} */ var state = tcuTestCase.runner;
    848    /** @type {Object} */ var test = state.currentTest;
    849    /** @type {Object} */ var spec = test.spec;
    850 
    851    // Compute viewport.
    852    /* TODO: original code used random number generator to compute viewport, we use whole canvas */
    853    /** @const @type {number} */ var width = Math.min(canvas.width, glsShaderLibraryCase.VIEWPORT_WIDTH);
    854    /** @const @type {number} */ var height = Math.min(canvas.height, glsShaderLibraryCase.VIEWPORT_HEIGHT);
    855    /** @const @type {number} */ var viewportX = 0;
    856    /** @const @type {number} */ var viewportY = 0;
    857    /** @const @type {number} */ var numVerticesPerDraw = 4;
    858    /** @const @type {boolean} */ var tessellationPresent = glsShaderLibraryCase.isTessellationPresent();
    859 
    860    /** @type {boolean} */ var allCompilesOk = true;
    861    /** @type {boolean} */ var allLinksOk = true;
    862    /** @type {?string} */ var failReason = null;
    863 
    864    /** @type {number} */ var vertexProgramID = -1;
    865    /** @type {Array<WebGLProgram>} */ var pipelineProgramIDs = [];
    866    /** @type {Array<gluShaderProgram.ShaderProgram>} */ var programs = [];
    867    var programPipeline;
    868 
    869    // Set the name of the current test so testFailedOptions/testPassedOptions can use it.
    870    setCurrentTestName(test.fullName());
    871    debug('Start testcase: ' + test.fullName());
    872    assertMsgOptions(gl.getError() === gl.NO_ERROR, 'Start testcase: ' + test.fullName(), false, true);
    873 
    874    /** @type {gluShaderProgram.ShaderProgram} */ var program = new gluShaderProgram.ShaderProgram(gl, test.programs[0].programSources);
    875 
    876    vertexProgramID = program.getProgram();
    877    pipelineProgramIDs.push(program.getProgram());
    878    programs.push(program);
    879 
    880    // Check that compile/link results are what we expect.
    881 
    882    for (var i = 0; i < program.shaders.length; i++) {
    883        if (!program.shaders[i].info.compileOk)
    884            allCompilesOk = false;
    885    }
    886 
    887    if (!program.getProgramInfo().linkOk)
    888        allLinksOk = false;
    889 
    890    switch (spec.expectResult) {
    891        case glsShaderLibraryCase.expectResult.EXPECT_PASS:
    892        case glsShaderLibraryCase.expectResult.EXPECT_VALIDATION_FAIL:
    893        case glsShaderLibraryCase.expectResult.EXPECT_BUILD_SUCCESSFUL:
    894            if (!allCompilesOk)
    895                failReason = 'expected shaders to compile and link properly, but failed to compile.';
    896            else if (!allLinksOk)
    897                failReason = 'expected shaders to compile and link properly, but failed to link.';
    898            break;
    899 
    900        case glsShaderLibraryCase.expectResult.EXPECT_COMPILE_FAIL:
    901            if (allCompilesOk && !allLinksOk)
    902                failReason = 'expected compilation to fail, but shaders compiled and link failed.';
    903            else if (allCompilesOk)
    904                failReason = 'expected compilation to fail, but shaders compiled correctly.';
    905            break;
    906 
    907        case glsShaderLibraryCase.expectResult.EXPECT_LINK_FAIL:
    908            if (!allCompilesOk)
    909                failReason = 'expected linking to fail, but unable to compile.';
    910            else if (allLinksOk)
    911                failReason = 'expected linking to fail, but passed.';
    912            break;
    913 
    914        case glsShaderLibraryCase.expectResult.EXPECT_COMPILE_LINK_FAIL:
    915            if (allCompilesOk && allLinksOk)
    916                failReason = 'expected compile or link to fail, but passed.';
    917            break;
    918 
    919        default:
    920            testFailedOptions('Unknown expected result', true);
    921            return false;
    922    }
    923 
    924    if (failReason != null) {
    925        // \todo [2010-06-07 petri] These should be handled in the test case?
    926 
    927        // If implementation parses shader at link time, report it as quality warning.
    928        if (spec.expectResult === glsShaderLibraryCase.expectResult.EXPECT_COMPILE_FAIL && allCompilesOk && !allLinksOk)
    929            bufferedLogToConsole('Quality warning: implementation parses shader at link time: ' + failReason);
    930        else {
    931            bufferedLogToConsole('ERROR: ' + failReason);
    932            testFailedOptions(failReason, true);
    933        }
    934        return false;
    935    }
    936 
    937    // Return if compile/link expected to fail.
    938    if (spec.expectResult === glsShaderLibraryCase.expectResult.EXPECT_COMPILE_FAIL ||
    939        spec.expectResult === glsShaderLibraryCase.expectResult.EXPECT_COMPILE_LINK_FAIL ||
    940        spec.expectResult === glsShaderLibraryCase.expectResult.EXPECT_LINK_FAIL ||
    941        spec.expectResult === glsShaderLibraryCase.expectResult.EXPECT_BUILD_SUCCESSFUL) {
    942        if (spec.expectResult === glsShaderLibraryCase.expectResult.EXPECT_BUILD_SUCCESSFUL) {
    943            testPassedOptions('Compile/link is expected to succeed', true);
    944        } else {
    945            testPassedOptions('Compile/link is expected to fail', true);
    946        }
    947        setCurrentTestName('');
    948        return (failReason === null);
    949    }
    950 
    951    // Setup viewport.
    952    gl.viewport(viewportX, viewportY, width, height);
    953 
    954    // Start using program
    955    gl.useProgram(vertexProgramID);
    956    assertMsgOptions(gl.getError() === gl.NO_ERROR, 'glUseProgram()', false, true);
    957 
    958    // Fetch location for positions positions.
    959    /** @type {number} */ var positionLoc = gl.getAttribLocation(vertexProgramID, 'dEQP_Position');
    960    if (positionLoc === -1) {
    961        testFailedOptions("no location found for attribute 'dEQP_Position'", true);
    962        return false;
    963    }
    964 
    965    // Iterate all value blocks.
    966    for (var blockNdx = 0; blockNdx < spec.valueBlockList.length; blockNdx++) {
    967    /** @type { {values:Array}} */ var block = spec.valueBlockList[blockNdx];
    968 
    969        // always render at least one pass even if there is no input/output data
    970        /** @const @type {number} */ var numRenderPasses = Math.max(block.arrayLength, 1);
    971 
    972        // Iterate all array sub-cases.
    973        for (var arrayNdx = 0; arrayNdx < numRenderPasses; arrayNdx++) {
    974            /** @const @type {number} */ var numValues = block.values.length;
    975            /** @type {Array<gluDrawUtil.VertexArrayBinding>} */ var vertexArrays = [];
    976            /** @type {number} */ var attribValueNdx = 0;
    977            /** @type {number} */ var postDrawError;
    978 
    979            vertexArrays.push(new gluDrawUtil.VertexArrayBinding(gl.FLOAT, positionLoc, 4, numVerticesPerDraw, s_positions));
    980 
    981            // Collect VA pointer for inputs
    982            for (var valNdx = 0; valNdx < numValues; valNdx++) {
    983                var val = block.values[valNdx];
    984                /** @const @type {string} */ var valueName = val.valueName;
    985                /** @const @type {gluShaderUtil.DataType} */ var dataType = val.dataType;
    986                /** @const @type {number} */ var scalarSize = gluShaderUtil.getDataTypeScalarSize(val.dataType);
    987 
    988                if (val.storageType === glsShaderLibraryCase.shaderCase.STORAGE_INPUT) {
    989                    // Replicate values four times.
    990                /** @type {Array} */ var scalars = [];
    991 
    992                    for (var repNdx = 0; repNdx < numVerticesPerDraw; repNdx++)
    993                        for (var ndx = 0; ndx < scalarSize; ndx++)
    994                            scalars[repNdx * scalarSize + ndx] = val.elements[arrayNdx * scalarSize + ndx];
    995 
    996                    // Attribute name prefix.
    997                    /** @type {string} */ var attribPrefix = '';
    998                    // \todo [2010-05-27 petri] Should latter condition only apply for vertex cases (or actually non-fragment cases)?
    999                    if ((spec.caseType === glsShaderLibraryCase.caseType.CASETYPE_FRAGMENT_ONLY) || (gluShaderUtil.getDataTypeScalarType(dataType) !== 'float'))
   1000                        attribPrefix = 'a_';
   1001 
   1002                    // Input always given as attribute.
   1003                    /** @type {string} */ var attribName = attribPrefix + valueName;
   1004                    /** @type {number} */ var attribLoc = gl.getAttribLocation(vertexProgramID, attribName);
   1005                    if (attribLoc === -1) {
   1006                        bufferedLogToConsole("Warning: no location found for attribute '" + attribName + "'");
   1007                        continue;
   1008                    }
   1009 
   1010                    if (gluShaderUtil.isDataTypeMatrix(dataType)) {
   1011                        var numCols = gluShaderUtil.getDataTypeMatrixNumColumns(dataType);
   1012                        var numRows = gluShaderUtil.getDataTypeMatrixNumRows(dataType);
   1013 
   1014                        assertMsgOptions(scalarSize === numCols * numRows, 'Matrix size sanity check', false, true);
   1015 
   1016                        for (var i = 0; i < numCols; i++)
   1017                            vertexArrays.push(new gluDrawUtil.VertexArrayBinding(gl.FLOAT, attribLoc + i, numRows, numVerticesPerDraw, scalars, scalarSize * 4, i * numRows * 4));
   1018                    } else
   1019                            vertexArrays.push(new gluDrawUtil.VertexArrayBinding(gl.FLOAT, attribLoc, scalarSize, numVerticesPerDraw, scalars));
   1020 
   1021                    assertMsgOptions(gl.getError() === gl.NO_ERROR, 'set vertex attrib array', false, true);
   1022                }
   1023            }
   1024 
   1025            assertMsgOptions(gl.getError() === gl.NO_ERROR, 'before set uniforms', false, true);
   1026 
   1027            // set uniform values for outputs (refs).
   1028            for (var valNdx = 0; valNdx < numValues; valNdx++) {
   1029            /** @type {Array} */ var val1 = block.values[valNdx];
   1030            /** @type {string} */ var valueName1 = val1.valueName;
   1031 
   1032                if (val1.storageType === glsShaderLibraryCase.shaderCase.STORAGE_OUTPUT) {
   1033                    // Set reference value.
   1034                    glsShaderLibraryCase.setUniformValue(gl, pipelineProgramIDs, 'ref_' + valueName1, val1, arrayNdx);
   1035                    assertMsgOptions(gl.getError() === gl.NO_ERROR, 'set reference uniforms', false, true);
   1036                } else if (val1.storageType === glsShaderLibraryCase.shaderCase.STORAGE_UNIFORM) {
   1037                    glsShaderLibraryCase.setUniformValue(gl, pipelineProgramIDs, valueName1, val1, arrayNdx);
   1038                    assertMsgOptions(gl.getError() === gl.NO_ERROR, 'set uniforms', false, true);
   1039                }
   1040            }
   1041 
   1042            // Clear.
   1043            gl.clearColor(0.125, 0.25, 0.5, 1);
   1044            gl.clear(gl.COLOR_BUFFER_BIT);
   1045            assertMsgOptions(gl.getError() === gl.NO_ERROR, 'clear buffer', false, true);
   1046 
   1047            // Use program or pipeline
   1048            if (spec.separatePrograms)
   1049                gl.useProgram(null);
   1050            else
   1051                gl.useProgram(vertexProgramID);
   1052 
   1053            // Draw.
   1054            // if (tessellationPresent) {
   1055            //     gl.patchParameteri(gl.PATCH_VERTICES, 3);
   1056            //     assertMsgOptions(gl.getError() === gl.NO_ERROR, 'set patchParameteri(PATCH_VERTICES, 3)', false, true);
   1057            // }
   1058 
   1059            gluDrawUtil.draw(gl, vertexProgramID, vertexArrays, gluDrawUtil.triangles(s_indices));
   1060 
   1061            postDrawError = gl.getError();
   1062 
   1063            if (spec.expectResult === glsShaderLibraryCase.expectResult.EXPECT_PASS) {
   1064                /** @type {gluDrawUtil.Surface} */ var surface = new gluDrawUtil.Surface();
   1065                /** @const @type {number} */ var w = s_positions[3];
   1066                /** @const @type {number} */ var minY = Math.ceil(((-quadSize / w) * 0.5 + 0.5) * height + 1.0);
   1067                /** @const @type {number} */ var maxY = Math.floor(((+quadSize / w) * 0.5 + 0.5) * height - 0.5);
   1068                /** @const @type {number} */ var minX = Math.ceil(((-quadSize / w) * 0.5 + 0.5) * width + 1.0);
   1069                /** @const @type {number} */ var maxX = Math.floor(((+quadSize / w) * 0.5 + 0.5) * width - 0.5);
   1070 
   1071                assertMsgOptions(postDrawError === gl.NO_ERROR, 'draw', false, true);
   1072 
   1073                surface.readSurface(gl, viewportX, viewportY, width, height);
   1074                assertMsgOptions(gl.getError() === gl.NO_ERROR, 'read pixels', false, true);
   1075 
   1076                if (!glsShaderLibraryCase.checkPixels(surface, minX, maxX, minY, maxY)) {
   1077                    testFailedOptions((
   1078                        'INCORRECT RESULT for (value block ' + (blockNdx + 1) +
   1079                        ' of ' + spec.valueBlockList.length + ', sub-case ' +
   1080                        (arrayNdx + 1) + ' of ' + block.arrayLength + '):'
   1081                    ), true);
   1082 
   1083                    /* TODO: Port */
   1084                    /*
   1085                    log << TestLog::Message << "Failing shader input/output values:" << TestLog::EndMessage;
   1086                    dumpValues(block, arrayNdx);
   1087 
   1088                    // Dump image on failure.
   1089                    log << TestLog::Image("Result", "Rendered result image", surface);
   1090 
   1091                    */
   1092                    gl.useProgram(null);
   1093 
   1094                    return false;
   1095                }
   1096            } else if (spec.expectResult === glsShaderLibraryCase.expectResult.EXPECT_VALIDATION_FAIL) {
   1097                /** TODO: GLES 3.1: Implement */
   1098                testFailedOptions('Unsupported test case', true);
   1099            }
   1100        }
   1101    }
   1102    gl.useProgram(null);
   1103 
   1104    assertMsgOptions(gl.getError() === gl.NO_ERROR, '', true, true);
   1105    setCurrentTestName('');
   1106 
   1107    return true;
   1108 };
   1109 
   1110 glsShaderLibraryCase.runTestCases = function() {
   1111 /** @type {Object} */ var state = tcuTestCase.runner;
   1112    if (state.next()) {
   1113        try {
   1114            glsShaderLibraryCase.init();
   1115            glsShaderLibraryCase.execute();
   1116        } catch (err) {
   1117           bufferedLogToConsole(err);
   1118        }
   1119        tcuTestCase.runner.runCallback(glsShaderLibraryCase.runTestCases);
   1120    } else
   1121    tcuTestCase.runner.terminate();
   1122 
   1123 };
   1124 
   1125 glsShaderLibraryCase.genValueBlock = function() {
   1126    return {
   1127    /** @type {Array} */ values: [],
   1128    /** @type {number} */ arrayLength: 0
   1129    };
   1130 };
   1131 
   1132 });