tor-browser

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

glsShaderExecUtil.js (33658B)


      1 /*-------------------------------------------------------------------------
      2 * drawElements Quality Program OpenGL (ES) Module
      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 * \file
     21 * \brief Shader execution utilities.
     22 *//*--------------------------------------------------------------------*/
     23 'use strict';
     24 goog.provide('modules.shared.glsShaderExecUtil');
     25 goog.require('framework.common.tcuMatrix');
     26 goog.require('framework.common.tcuMatrixUtil');
     27 goog.require('framework.common.tcuTexture');
     28 goog.require('framework.opengl.gluDrawUtil');
     29 goog.require('framework.opengl.gluShaderProgram');
     30 goog.require('framework.opengl.gluShaderUtil');
     31 goog.require('framework.opengl.gluTextureUtil');
     32 goog.require('framework.opengl.gluVarType');
     33 
     34 goog.scope(function() {
     35 
     36    var glsShaderExecUtil = modules.shared.glsShaderExecUtil;
     37    var gluVarType = framework.opengl.gluVarType;
     38    var gluShaderUtil = framework.opengl.gluShaderUtil;
     39    var gluShaderProgram = framework.opengl.gluShaderProgram;
     40    var gluDrawUtil = framework.opengl.gluDrawUtil;
     41    var gluTextureUtil = framework.opengl.gluTextureUtil;
     42    var tcuTexture = framework.common.tcuTexture;
     43    var tcuMatrix = framework.common.tcuMatrix;
     44    var tcuMatrixUtil = framework.common.tcuMatrixUtil;
     45 
     46    var DE_ASSERT = function(x) {
     47        if (!x)
     48            throw new Error('Assert failed');
     49    };
     50 
     51    var setParentClass = function(child, parent) {
     52        child.prototype = Object.create(parent.prototype);
     53        child.prototype.constructor = child;
     54    };
     55 
     56    /**
     57     * @constructor
     58     * @param {string=} name
     59     * @param {gluVarType.VarType=} varType
     60     */
     61    glsShaderExecUtil.Symbol = function(name, varType) {
     62        name = name === undefined ? '<unnamed>' : name;
     63        /** @type {string} */ this.name = name;
     64        /** @type {gluVarType.VarType} */ this.varType = varType || null;
     65    };
     66 
     67    //! Complete shader specification.
     68    /**
     69     * @constructor
     70     */
     71    glsShaderExecUtil.ShaderSpec = function() {
     72        /** @type {gluShaderUtil.GLSLVersion} */ this.version = gluShaderUtil.GLSLVersion.V300_ES; //!< Shader version.
     73        /** @type {Array<glsShaderExecUtil.Symbol>} */ this.inputs = [];
     74        /** @type {Array<glsShaderExecUtil.Symbol>} */ this.outputs = [];
     75        /** @type {string} */ this.globalDeclarations = ''; //!< These are placed into global scope. Can contain uniform declarations for example.
     76        /** @type {*} */ this.source; //!< Source snippet to be executed.
     77    };
     78 
     79    /**
     80     * Base class for shader executor.
     81     * @constructor
     82     * @param {glsShaderExecUtil.ShaderSpec} shaderSpec
     83     */
     84    glsShaderExecUtil.ShaderExecutor = function(shaderSpec) {
     85        /** @type {Array<glsShaderExecUtil.Symbol>} */ this.m_inputs = shaderSpec.inputs;
     86        /** @type {Array<glsShaderExecUtil.Symbol>} */ this.m_outputs = shaderSpec.outputs;
     87    };
     88 
     89    glsShaderExecUtil.ShaderExecutor.prototype.useProgram = function() {
     90        DE_ASSERT(this.isOk);
     91        gl.useProgram(this.getProgram());
     92    };
     93 
     94    /**
     95     * @return {boolean}
     96     */
     97    glsShaderExecUtil.ShaderExecutor.prototype.isOk = function() {
     98        throw new Error('Virtual function. Please override.');
     99    };
    100 
    101    /**
    102     * @return {WebGLProgram}
    103     */
    104    glsShaderExecUtil.ShaderExecutor.prototype.getProgram = function() {
    105        throw new Error('Virtual function. Please override.');
    106    };
    107 
    108    /**
    109     * @param {number} numValues
    110     * @param {Array<Array<number>>} inputs
    111     * @return {Array<goog.TypedArray>} outputs
    112     */
    113    glsShaderExecUtil.ShaderExecutor.prototype.execute = function(numValues, inputs) {
    114        throw new Error('Virtual function. Please override.');
    115    };
    116 
    117    /**
    118     * Base class for shader executor.
    119     * @param {gluShaderProgram.shaderType} shaderType
    120     * @param {glsShaderExecUtil.ShaderSpec} shaderSpec
    121     * @return {glsShaderExecUtil.ShaderExecutor}
    122     */
    123    glsShaderExecUtil.createExecutor = function(shaderType, shaderSpec) {
    124        switch (shaderType) {
    125            case gluShaderProgram.shaderType.VERTEX: return new glsShaderExecUtil.VertexShaderExecutor(shaderSpec);
    126            case gluShaderProgram.shaderType.FRAGMENT: return new glsShaderExecUtil.FragmentShaderExecutor(shaderSpec);
    127            default:
    128                throw new Error('Unsupported shader type: ' + shaderType);
    129        }
    130    };
    131 
    132    /**
    133     * @param {glsShaderExecUtil.ShaderSpec} shaderSpec
    134     * @return {string}
    135     */
    136    glsShaderExecUtil.generateVertexShader = function(shaderSpec) {
    137        /** @type {boolean} */ var usesInout = true;
    138        /** @type {string} */ var in_ = usesInout ? 'in' : 'attribute';
    139        /** @type {string} */ var out = usesInout ? 'out' : 'varying';
    140        /** @type {string} */ var src = '';
    141        /** @type {number} */ var vecSize;
    142        /** @type {gluShaderUtil.DataType} */ var intBaseType;
    143 
    144        src += '#version 300 es\n';
    145 
    146        if (shaderSpec.globalDeclarations.length > 0)
    147            src += (shaderSpec.globalDeclarations + '\n');
    148 
    149        for (var i = 0; i < shaderSpec.inputs.length; ++i)
    150            src += (in_ + ' ' + gluVarType.declareVariable(shaderSpec.inputs[i].varType, shaderSpec.inputs[i].name) + ';\n');
    151 
    152        for (var i = 0; i < shaderSpec.outputs.length; i++) {
    153            var output = shaderSpec.outputs[i];
    154            DE_ASSERT(output.varType.isBasicType());
    155 
    156            if (gluShaderUtil.isDataTypeBoolOrBVec(output.varType.getBasicType())) {
    157                vecSize = gluShaderUtil.getDataTypeScalarSize(output.varType.getBasicType());
    158                intBaseType = vecSize > 1 ? gluShaderUtil.getDataTypeVector(gluShaderUtil.DataType.INT, vecSize) : gluShaderUtil.DataType.INT;
    159                /** @type {gluVarType.VarType} */ var intType = new gluVarType.VarType().VarTypeBasic(intBaseType, gluShaderUtil.precision.PRECISION_HIGHP);
    160 
    161                src += ('flat ' + out + ' ' + gluVarType.declareVariable(intType, 'o_' + output.name) + ';\n');
    162            } else
    163                src += ('flat ' + out + ' ' + gluVarType.declareVariable(output.varType, output.name) + ';\n');
    164        }
    165 
    166        src += '\n' +
    167            'void main (void)\n' +
    168            '{\n' +
    169            ' gl_Position = vec4(0.0);\n' +
    170            ' gl_PointSize = 1.0;\n\n';
    171 
    172        // Declare necessary output variables (bools).
    173        for (var i = 0; i < shaderSpec.outputs.length; i++) {
    174            if (gluShaderUtil.isDataTypeBoolOrBVec(shaderSpec.outputs[i].varType.getBasicType()))
    175                src += ('\t' + gluVarType.declareVariable(shaderSpec.outputs[i].varType, shaderSpec.outputs[i].name) + ';\n');
    176        }
    177 
    178        //Operation - indented to correct level.
    179        // TODO: Add indenting
    180        src += shaderSpec.source;
    181 
    182        // Assignments to outputs.
    183        for (var i = 0; i < shaderSpec.outputs.length; i++) {
    184            if (gluShaderUtil.isDataTypeBoolOrBVec(output.varType.getBasicType())) {
    185                vecSize = gluShaderUtil.getDataTypeScalarSize(output.varType.getBasicType());
    186                intBaseType = vecSize > 1 ? gluShaderUtil.getDataTypeVector(gluShaderUtil.DataType.INT, vecSize) : gluShaderUtil.DataType.INT;
    187 
    188                src += ('\to_' + output.name + ' = ' + gluShaderUtil.getDataTypeName(intBaseType) + '(' + output.name + ');\n');
    189            }
    190        }
    191 
    192        src += '}\n';
    193 
    194        return src;
    195    };
    196 
    197    /**
    198     * @return {string}
    199     */
    200    glsShaderExecUtil.generateEmptyFragmentSource = function() {
    201        /** @type {string} */ var src;
    202 
    203        src = '#version 300 es\n';
    204        src += 'out lowp vec4 color;\n';
    205        src += 'void main (void)\n{\n';
    206        src += ' color = vec4(0.0);\n';
    207        src += '}\n';
    208 
    209        return src;
    210    };
    211 
    212    /**
    213     * @param {glsShaderExecUtil.ShaderSpec} shaderSpec
    214     * @param {string} inputPrefix
    215     * @param {string} outputPrefix
    216     * @return {string}
    217     */
    218    glsShaderExecUtil.generatePassthroughVertexShader = function(shaderSpec, inputPrefix, outputPrefix) {
    219        // flat qualifier is not present in earlier versions?
    220        // DE_ASSERT(glu::glslVersionUsesInOutQualifiers(shaderSpec.version));
    221 
    222        /** @type {string} */ var src;
    223 
    224        src = '#version 300 es\n' +
    225            'in highp vec4 a_position;\n';
    226 
    227        for (var i = 0; i < shaderSpec.inputs.length; i++) {
    228            src += ('in ' + gluVarType.declareVariable(shaderSpec.inputs[i].varType, inputPrefix + shaderSpec.inputs[i].name) + ';\n' +
    229                'flat out ' + gluVarType.declareVariable(shaderSpec.inputs[i].varType, outputPrefix + shaderSpec.inputs[i].name) + ';\n');
    230        }
    231 
    232        src += '\nvoid main (void)\n{\n' +
    233            ' gl_Position = a_position;\n' +
    234            ' gl_PointSize = 1.0;\n';
    235 
    236        for (var i = 0; i < shaderSpec.inputs.length; i++)
    237            src += ('\t' + outputPrefix + shaderSpec.inputs[i].name + ' = ' + inputPrefix + shaderSpec.inputs[i].name + ';\n');
    238 
    239        src += '}\n';
    240 
    241        return src;
    242    };
    243 
    244    /**
    245     * @param {glsShaderExecUtil.ShaderSpec} shaderSpec
    246     * @param {boolean} useIntOutputs
    247     * @param {*} outLocationMap
    248     * @return {string}
    249     */
    250    glsShaderExecUtil.generateFragmentShader = function(shaderSpec, useIntOutputs, outLocationMap) {
    251        /** @type {number} */ var vecSize;
    252        /** @type {number} */ var numVecs;
    253        /** @type {gluShaderUtil.DataType} */ var intBasicType;
    254        /** @type {gluShaderUtil.DataType} */ var uintBasicType;
    255        /** @type {gluVarType.VarType} */ var uintType;
    256        /** @type {gluVarType.VarType} */ var intType;
    257 
    258        /** @type {string} */ var src;
    259        src = '#version 300 es\n';
    260 
    261        if (!shaderSpec.globalDeclarations.length > 0)
    262            src += (shaderSpec.globalDeclarations + '\n');
    263 
    264        for (var i = 0; i < shaderSpec.inputs.length; i++)
    265            src += ('flat in ' + gluVarType.declareVariable(shaderSpec.inputs[i].varType, shaderSpec.inputs[i].name) + ';\n');
    266 
    267        for (var outNdx = 0; outNdx < shaderSpec.outputs.length; ++outNdx) {
    268            /** @type {glsShaderExecUtil.Symbol} */ var output = shaderSpec.outputs[outNdx];
    269            /** @type {number} */ var location = outLocationMap[output.name];
    270            /** @type {string} */ var outVarName = 'o_' + output.name;
    271            /** @type {gluVarType.VariableDeclaration} */ var decl = new gluVarType.VariableDeclaration(output.varType, outVarName, gluVarType.Storage.STORAGE_OUT, undefined, new gluVarType.Layout(location));
    272 
    273            DE_ASSERT(output.varType.isBasicType());
    274 
    275            if (useIntOutputs && gluShaderUtil.isDataTypeFloatOrVec(output.varType.getBasicType())) {
    276                vecSize = gluShaderUtil.getDataTypeScalarSize(output.varType.getBasicType());
    277                uintBasicType = vecSize > 1 ? gluShaderUtil.getDataTypeVector(gluShaderUtil.DataType.UINT, vecSize) : gluShaderUtil.DataType.UINT;
    278                uintType = gluVarType.newTypeBasic(uintBasicType, gluShaderUtil.precision.PRECISION_HIGHP);
    279 
    280                decl.varType = uintType;
    281                src += (decl + ';\n');
    282            } else if (gluShaderUtil.isDataTypeBoolOrBVec(output.varType.getBasicType())) {
    283                vecSize = gluShaderUtil.getDataTypeScalarSize(output.varType.getBasicType());
    284                intBasicType = vecSize > 1 ? gluShaderUtil.getDataTypeVector(gluShaderUtil.DataType.INT, vecSize) : gluShaderUtil.DataType.INT;
    285                intType = gluVarType.newTypeBasic(intBasicType, gluShaderUtil.precision.PRECISION_HIGHP);
    286 
    287                decl.varType = intType;
    288                src += (decl + ';\n');
    289            } else if (gluShaderUtil.isDataTypeMatrix(output.varType.getBasicType())) {
    290                vecSize = gluShaderUtil.getDataTypeMatrixNumRows(output.varType.getBasicType());
    291                numVecs = gluShaderUtil.getDataTypeMatrixNumColumns(output.varType.getBasicType());
    292                uintBasicType = gluShaderUtil.getDataTypeVector(gluShaderUtil.DataType.UINT, vecSize);
    293                uintType = gluVarType.newTypeBasic(uintBasicType, gluShaderUtil.precision.PRECISION_HIGHP);
    294 
    295                decl.varType = uintType;
    296                for (var vecNdx = 0; vecNdx < numVecs; ++vecNdx) {
    297                    decl.name = outVarName + '_' + (vecNdx);
    298                    decl.layout.location = location + vecNdx;
    299                    src += (decl + ';\n');
    300                }
    301            } else //src += '';//glu::VariableDeclaration(output.varType, output.name, glu::STORAGE_OUT, glu::INTERPOLATION_LAST, location) << ";\n";
    302                src += new gluVarType.VariableDeclaration(output.varType, output.name, gluVarType.Storage.STORAGE_OUT, undefined, new gluVarType.Layout(location)) + ';\n';
    303        }
    304 
    305        src += '\nvoid main (void)\n{\n';
    306 
    307        for (var i = 0; i < shaderSpec.outputs.length; i++) {
    308            if ((useIntOutputs && gluShaderUtil.isDataTypeFloatOrVec(shaderSpec.outputs[i].varType.getBasicType())) ||
    309                gluShaderUtil.isDataTypeBoolOrBVec(shaderSpec.outputs[i].varType.getBasicType()) ||
    310                gluShaderUtil.isDataTypeMatrix(shaderSpec.outputs[i].varType.getBasicType()))
    311                src += ('\t' + gluVarType.declareVariable(shaderSpec.outputs[i].varType, shaderSpec.outputs[i].name) + ';\n');
    312        }
    313 
    314        // Operation - indented to correct level.
    315        // TODO: Add indenting
    316        src += shaderSpec.source;
    317        // {
    318        //     std::istringstream opSrc (shaderSpec.source);
    319        //     /** @type{number} */ var line;
    320        //
    321        //     while (std::getline(opSrc, line))
    322        //         src += ('\t' << line << '\n');
    323        // }
    324 
    325        for (var i = 0; i < shaderSpec.outputs.length; i++) {
    326            if (useIntOutputs && gluShaderUtil.isDataTypeFloatOrVec(shaderSpec.outputs[i].varType.getBasicType()))
    327                src += (' o_' + shaderSpec.outputs[i].name + ' = floatBitsToUint(' + shaderSpec.outputs[i].name + ');\n');
    328            else if (gluShaderUtil.isDataTypeMatrix(shaderSpec.outputs[i].varType.getBasicType())) {
    329                numVecs = gluShaderUtil.getDataTypeMatrixNumColumns(shaderSpec.outputs[i].varType.getBasicType());
    330 
    331                for (var vecNdx = 0; vecNdx < numVecs; ++vecNdx)
    332                    if (useIntOutputs)
    333                        src += ('\to_' + shaderSpec.outputs[i].name + '_' + vecNdx + ' = floatBitsToUint(' + shaderSpec.outputs[i].name + '[' + vecNdx + ']);\n');
    334                    else
    335                        src += ('\to_' + shaderSpec.outputs[i].name + '_' + vecNdx + ' = ' + shaderSpec.outputs[i].name + '[' + vecNdx + '];\n');
    336            } else if (gluShaderUtil.isDataTypeBoolOrBVec(shaderSpec.outputs[i].varType.getBasicType())) {
    337                vecSize = gluShaderUtil.getDataTypeScalarSize(shaderSpec.outputs[i].varType.getBasicType());
    338                intBasicType = vecSize > 1 ? gluShaderUtil.getDataTypeVector(gluShaderUtil.DataType.INT, vecSize) : gluShaderUtil.DataType.INT;
    339 
    340                src += ('\to_' + shaderSpec.outputs[i].name + ' = ' + gluShaderUtil.getDataTypeName(intBasicType) + '(' + shaderSpec.outputs[i].name + ');\n');
    341            }
    342        }
    343 
    344        src += '}\n';
    345 
    346        return src;
    347    };
    348 
    349    /**
    350     * @param {Array<glsShaderExecUtil.Symbol>} outputs
    351     * @return {gluShaderProgram.TransformFeedbackVaryings}
    352     */
    353    glsShaderExecUtil.getTFVaryings = function(outputs) {
    354        var names = [];
    355        for (var i = 0; i < outputs.length; i++) {
    356            if (gluShaderUtil.isDataTypeBoolOrBVec(outputs[i].varType.getBasicType())) {
    357                names.push('o_' + outputs[i].name);
    358            } else {
    359                names.push(outputs[i].name);
    360            }
    361        }
    362        return new gluShaderProgram.TransformFeedbackVaryings(names);
    363    };
    364 
    365    // VertexProcessorExecutor (base class for vertex and geometry executors)
    366 
    367    /**
    368     * @constructor
    369     * @extends {glsShaderExecUtil.ShaderExecutor}
    370     * @param {glsShaderExecUtil.ShaderSpec} shaderSpec
    371     * @param {gluShaderProgram.ProgramSources} sources
    372     */
    373    glsShaderExecUtil.VertexProcessorExecutor = function(shaderSpec, sources) {
    374        sources.add(glsShaderExecUtil.getTFVaryings(shaderSpec.outputs));
    375        sources.add(new gluShaderProgram.TransformFeedbackMode(gl.INTERLEAVED_ATTRIBS));
    376        glsShaderExecUtil.ShaderExecutor.call(this, shaderSpec);
    377        this.m_program = new gluShaderProgram.ShaderProgram(gl, sources);
    378    };
    379 
    380    setParentClass(glsShaderExecUtil.VertexProcessorExecutor, glsShaderExecUtil.ShaderExecutor);
    381 
    382    /**
    383     * @return {boolean}
    384     */
    385    glsShaderExecUtil.VertexProcessorExecutor.prototype.isOk = function() {
    386        return this.m_program.isOk();
    387    };
    388 
    389    /**
    390     * @return {WebGLProgram}
    391     */
    392    glsShaderExecUtil.VertexProcessorExecutor.prototype.getProgram = function() {
    393        return this.m_program.getProgram();
    394    };
    395 
    396    /**
    397     * @param {Array<*>} arr
    398     * @return {number}
    399     */
    400    glsShaderExecUtil.computeTotalScalarSize = function(arr) {
    401        /** @type {number} */ var size = 0;
    402        for (var i = 0; i < arr.length; i++)
    403            size += arr[i].varType.getScalarSize();
    404        return size;
    405    };
    406 
    407    /**
    408     * @param {Array<number>} ptr
    409     * @param {number} colNdx
    410     * @param {number} size Column size
    411     * @return {Array<number>}
    412     */
    413    glsShaderExecUtil.getColumn = function(ptr, colNdx, size) {
    414        var begin = colNdx * size;
    415        var end = (colNdx + 1) * size;
    416        return ptr.slice(begin, end);
    417    };
    418 
    419    glsShaderExecUtil.VertexProcessorExecutor.prototype.execute = function(numValues, inputs) {
    420        /** @type {glsShaderExecUtil.Symbol} */ var symbol;
    421        var outputs = [];
    422        /** @type {boolean} */ var useTFObject = true;
    423        /** @type {Array<gluDrawUtil.VertexArrayBinding>} */ var vertexArrays = [];
    424        var transformFeedback = gl.createTransformFeedback();
    425        var outputBuffer = gl.createBuffer();
    426 
    427        /** @type {number} */ var outputBufferStride = glsShaderExecUtil.computeTotalScalarSize(this.m_outputs) * 4;
    428 
    429        // Setup inputs.
    430        for (var inputNdx = 0; inputNdx < this.m_inputs.length; inputNdx++) {
    431            symbol = this.m_inputs[inputNdx];
    432            /*const void* */var ptr = inputs[inputNdx];
    433            /** @type {gluShaderUtil.DataType} */ var basicType = symbol.varType.getBasicType();
    434            /** @type {number} */ var vecSize = gluShaderUtil.getDataTypeScalarSize(basicType);
    435 
    436            if (gluShaderUtil.isDataTypeFloatOrVec(basicType))
    437                vertexArrays.push(gluDrawUtil.newFloatVertexArrayBinding(symbol.name, vecSize, numValues, 0, ptr));
    438            else if (gluShaderUtil.isDataTypeIntOrIVec(basicType))
    439                vertexArrays.push(gluDrawUtil.newInt32VertexArrayBinding(symbol.name, vecSize, numValues, 0, ptr));
    440            else if (gluShaderUtil.isDataTypeUintOrUVec(basicType))
    441                vertexArrays.push(gluDrawUtil.newUint32VertexArrayBinding(symbol.name, vecSize, numValues, 0, ptr));
    442            else if (gluShaderUtil.isDataTypeMatrix(basicType)) {
    443                /** @type {number} */ var numRows = gluShaderUtil.getDataTypeMatrixNumRows(basicType);
    444                /** @type {number} */ var numCols = gluShaderUtil.getDataTypeMatrixNumColumns(basicType);
    445                // A matrix consists of several (column-major) vectors. A buffer is created for
    446                // every vector in gluDrawUtil.draw() below. Data in every buffer will be tightly
    447                // packed. So the stride should be 0. This is different from the code in native
    448                // deqp, which use only one buffer for a matrix, the data is interleaved.
    449                /** @type {number} */ var stride = 0;
    450 
    451                for (var colNdx = 0; colNdx < numCols; ++colNdx)
    452                    vertexArrays.push(gluDrawUtil.newFloatColumnVertexArrayBinding(symbol.name,
    453                        colNdx,
    454                        numRows,
    455                        numValues,
    456                        stride,
    457                        glsShaderExecUtil.getColumn(ptr, colNdx, numRows * numValues)));
    458            } else
    459                DE_ASSERT(false);
    460        }
    461 
    462        // Setup TF outputs.
    463        if (useTFObject)
    464            gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, transformFeedback);
    465        gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, outputBuffer);
    466        gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, outputBufferStride * numValues, gl.STREAM_READ);
    467        gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, outputBuffer);
    468 
    469        // Draw with rasterization disabled.
    470        gl.beginTransformFeedback(gl.POINTS);
    471        gl.enable(gl.RASTERIZER_DISCARD);
    472        gluDrawUtil.draw(gl, this.m_program.getProgram(), vertexArrays,
    473            new gluDrawUtil.PrimitiveList(gluDrawUtil.primitiveType.POINTS, numValues));
    474        gl.disable(gl.RASTERIZER_DISCARD);
    475        gl.endTransformFeedback();
    476 
    477        // Read back data.
    478        var result = new ArrayBuffer(outputBufferStride * numValues);
    479        gl.getBufferSubData(gl.TRANSFORM_FEEDBACK_BUFFER, 0, new Uint8Array(result));
    480        /** @type {number} */ var curOffset = 0; // Offset in buffer in bytes.
    481 
    482        for (var outputNdx = 0; outputNdx < this.m_outputs.length; outputNdx++) {
    483            symbol = this.m_outputs[outputNdx];
    484            /** @type {number} */ var scalarSize = symbol.varType.getScalarSize();
    485            var readPtr = new Uint8Array(result, curOffset);
    486 
    487            if (scalarSize * 4 === outputBufferStride)
    488                outputs[outputNdx] = readPtr;
    489            else {
    490                var dstPtr = new Uint8Array(scalarSize * numValues * 4);
    491 
    492                for (var ndx = 0; ndx < numValues; ndx++)
    493                    for (var j = 0; j < scalarSize * 4; j++) {
    494                        dstPtr[scalarSize * 4 * ndx + j] = readPtr[ndx * outputBufferStride + j];
    495                    }
    496                outputs[outputNdx] = dstPtr;
    497            }
    498            curOffset += scalarSize * 4;
    499          }
    500 
    501        if (useTFObject)
    502            gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, null);
    503        gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, null);
    504 
    505        return outputs;
    506    };
    507 
    508    // VertexShaderExecutor
    509 
    510    /**
    511     * @constructor
    512     * @extends {glsShaderExecUtil.VertexProcessorExecutor}
    513     * @param {glsShaderExecUtil.ShaderSpec} shaderSpec
    514     */
    515    glsShaderExecUtil.VertexShaderExecutor = function(shaderSpec) {
    516        var sources = gluShaderProgram.makeVtxFragSources(glsShaderExecUtil.generateVertexShader(shaderSpec),
    517            glsShaderExecUtil.generateEmptyFragmentSource());
    518        glsShaderExecUtil.VertexProcessorExecutor.call(this, shaderSpec, sources);
    519    };
    520 
    521    setParentClass(glsShaderExecUtil.VertexShaderExecutor, glsShaderExecUtil.VertexProcessorExecutor);
    522 
    523    /**
    524     * @constructor
    525     * @extends {glsShaderExecUtil.ShaderExecutor}
    526     * @param {glsShaderExecUtil.ShaderSpec} shaderSpec
    527     */
    528    glsShaderExecUtil.FragmentShaderExecutor = function(shaderSpec) {
    529        glsShaderExecUtil.ShaderExecutor.call(this, shaderSpec);
    530        /** @type {Array<glsShaderExecUtil.Symbol>} */ this.m_outLocationSymbols = [];
    531        this.m_outLocationMap = glsShaderExecUtil.generateLocationMap(this.m_outputs, this.m_outLocationSymbols);
    532        var sources = gluShaderProgram.makeVtxFragSources(glsShaderExecUtil.generatePassthroughVertexShader(shaderSpec, 'a_', ''),
    533            glsShaderExecUtil.generateFragmentShader(shaderSpec, true, this.m_outLocationMap));
    534        this.m_program = new gluShaderProgram.ShaderProgram(gl, sources);
    535    };
    536 
    537    setParentClass(glsShaderExecUtil.FragmentShaderExecutor, glsShaderExecUtil.ShaderExecutor);
    538 
    539    /**
    540     * @return {boolean}
    541     */
    542    glsShaderExecUtil.FragmentShaderExecutor.prototype.isOk = function() {
    543        return this.m_program.isOk();
    544    };
    545 
    546    /**
    547     * @return {WebGLProgram}
    548     */
    549    glsShaderExecUtil.FragmentShaderExecutor.prototype.getProgram = function() {
    550        return this.m_program.getProgram();
    551    };
    552 
    553    /**
    554     * @param {gluVarType.VarType} outputType
    555     * @param {boolean} useIntOutputs
    556     * @return {tcuTexture.TextureFormat}
    557     */
    558    glsShaderExecUtil.getRenderbufferFormatForOutput = function(outputType, useIntOutputs) {
    559        var channelOrderMap = [
    560            tcuTexture.ChannelOrder.R,
    561            tcuTexture.ChannelOrder.RG,
    562            tcuTexture.ChannelOrder.RGBA, // No RGB variants available.
    563            tcuTexture.ChannelOrder.RGBA
    564        ];
    565 
    566        var basicType = outputType.getBasicType();
    567        var numComps = gluShaderUtil.getDataTypeNumComponents(basicType);
    568        var channelType;
    569 
    570        switch (gluShaderUtil.getDataTypeScalarType(basicType)) {
    571            case 'uint': channelType = tcuTexture.ChannelType.UNSIGNED_INT32; break;
    572            case 'int': channelType = tcuTexture.ChannelType.SIGNED_INT32; break;
    573            case 'bool': channelType = tcuTexture.ChannelType.SIGNED_INT32; break;
    574            case 'float': channelType = useIntOutputs ? tcuTexture.ChannelType.UNSIGNED_INT32 : tcuTexture.ChannelType.FLOAT; break;
    575            default:
    576                throw new Error('Invalid output type ' + gluShaderUtil.getDataTypeScalarType(basicType));
    577        }
    578 
    579        return new tcuTexture.TextureFormat(channelOrderMap[numComps - 1], channelType);
    580    };
    581 
    582    glsShaderExecUtil.FragmentShaderExecutor.prototype.execute = function(numValues, inputs) {
    583        /** @type {boolean} */ var useIntOutputs = true;
    584        /** @type {glsShaderExecUtil.Symbol} */ var symbol;
    585        var outputs = [];
    586        var maxRenderbufferSize = /** @type {number} */ (gl.getParameter(gl.MAX_RENDERBUFFER_SIZE));
    587        /** @type {number} */ var framebufferW = Math.min(maxRenderbufferSize, numValues);
    588        /** @type {number} */ var framebufferH = Math.ceil(numValues / framebufferW);
    589 
    590        var framebuffer = gl.createFramebuffer();
    591        var renderbuffers = [];
    592        for (var i = 0; i < this.m_outLocationSymbols.length; i++)
    593         renderbuffers.push(gl.createRenderbuffer());
    594 
    595        var vertexArrays = [];
    596        var positions = [];
    597 
    598        if (framebufferH > maxRenderbufferSize)
    599            throw new Error('Value count is too high for maximum supported renderbuffer size');
    600 
    601        // Compute positions - 1px points are used to drive fragment shading.
    602        for (var valNdx = 0; valNdx < numValues; valNdx++) {
    603            /** @type {number} */ var ix = valNdx % framebufferW;
    604            /** @type {number} */ var iy = Math.floor(valNdx / framebufferW);
    605            var fx = -1 + 2 * (ix + 0.5) / framebufferW;
    606            var fy = -1 + 2 * (iy + 0.5) / framebufferH;
    607 
    608            positions[2 * valNdx] = fx;
    609            positions[2 * valNdx + 1] = fy;
    610        }
    611 
    612        // Vertex inputs.
    613        vertexArrays.push(gluDrawUtil.newFloatVertexArrayBinding('a_position', 2, numValues, 0, positions));
    614 
    615        for (var inputNdx = 0; inputNdx < this.m_inputs.length; inputNdx++) {
    616            symbol = this.m_inputs[inputNdx];
    617            var attribName = 'a_' + symbol.name;
    618            var ptr = inputs[inputNdx];
    619            /** @type {gluShaderUtil.DataType} */ var basicType = symbol.varType.getBasicType();
    620            /** @type {number} */ var vecSize = gluShaderUtil.getDataTypeScalarSize(basicType);
    621 
    622            if (gluShaderUtil.isDataTypeFloatOrVec(basicType))
    623                vertexArrays.push(gluDrawUtil.newFloatVertexArrayBinding(attribName, vecSize, numValues, 0, ptr));
    624            else if (gluShaderUtil.isDataTypeIntOrIVec(basicType))
    625                vertexArrays.push(gluDrawUtil.newInt32VertexArrayBinding(attribName, vecSize, numValues, 0, ptr));
    626            else if (gluShaderUtil.isDataTypeUintOrUVec(basicType))
    627                vertexArrays.push(gluDrawUtil.newUint32VertexArrayBinding(attribName, vecSize, numValues, 0, ptr));
    628            else if (gluShaderUtil.isDataTypeMatrix(basicType)) {
    629                var numRows = gluShaderUtil.getDataTypeMatrixNumRows(basicType);
    630                var numCols = gluShaderUtil.getDataTypeMatrixNumColumns(basicType);
    631                // A matrix consists of several (column-major) vectors. A buffer is created for
    632                // every vector in gluDrawUtil.draw() below. Data in every buffer will be tightly
    633                // packed. So the stride should be 0. This is different from the code in native
    634                // deqp, which use only one buffer for a matrix, the data is interleaved.
    635                var stride = 0;
    636 
    637                for (var colNdx = 0; colNdx < numCols; ++colNdx)
    638                    vertexArrays.push(gluDrawUtil.newFloatColumnVertexArrayBinding(attribName,
    639                       colNdx,
    640                       numRows,
    641                       numValues,
    642                       stride,
    643                       glsShaderExecUtil.getColumn(ptr, colNdx, numRows * numValues)));
    644            } else
    645                DE_ASSERT(false);
    646        }
    647 
    648        // Construct framebuffer.
    649        gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
    650 
    651        for (var outNdx = 0; outNdx < this.m_outLocationSymbols.length; ++outNdx) {
    652            symbol = this.m_outLocationSymbols[outNdx];
    653            var renderbuffer = renderbuffers[outNdx];
    654            var format = gluTextureUtil.getInternalFormat(glsShaderExecUtil.getRenderbufferFormatForOutput(symbol.varType, useIntOutputs));
    655 
    656            gl.bindRenderbuffer(gl.RENDERBUFFER, renderbuffer);
    657            gl.renderbufferStorage(gl.RENDERBUFFER, format, framebufferW, framebufferH);
    658            gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + outNdx, gl.RENDERBUFFER, renderbuffer);
    659        }
    660        gl.bindRenderbuffer(gl.RENDERBUFFER, null);
    661        assertMsgOptions(gl.checkFramebufferStatus(gl.FRAMEBUFFER) == gl.FRAMEBUFFER_COMPLETE, 'Framebuffer is incomplete', false, true);
    662 
    663        var drawBuffers = [];
    664        for (var ndx = 0; ndx < this.m_outLocationSymbols.length; ndx++)
    665            drawBuffers[ndx] = gl.COLOR_ATTACHMENT0 + ndx;
    666        gl.drawBuffers(drawBuffers);
    667 
    668        // Render
    669        gl.viewport(0, 0, framebufferW, framebufferH);
    670        gluDrawUtil.draw(gl, this.m_program.getProgram(), vertexArrays,
    671        new gluDrawUtil.PrimitiveList(gluDrawUtil.primitiveType.POINTS, numValues));
    672 
    673        // Read back pixels.
    674 
    675        // \todo [2013-08-07 pyry] Some fast-paths could be added here.
    676 
    677        for (var outNdx = 0; outNdx < this.m_outputs.length; ++outNdx) {
    678            symbol = this.m_outputs[outNdx];
    679            /** @type {number} */ var outSize = symbol.varType.getScalarSize();
    680            /** @type {number} */ var outVecSize = gluShaderUtil.getDataTypeNumComponents(symbol.varType.getBasicType());
    681            /** @type {number} */ var outNumLocs = gluShaderUtil.getDataTypeNumLocations(symbol.varType.getBasicType());
    682            var format = glsShaderExecUtil.getRenderbufferFormatForOutput(symbol.varType, useIntOutputs);
    683            var readFormat = new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RGBA, format.type);
    684            var transferFormat = gluTextureUtil.getTransferFormat(readFormat);
    685            /** @type {number} */ var outLocation = this.m_outLocationMap[symbol.name];
    686            var tmpBuf = new tcuTexture.TextureLevel(readFormat, framebufferW, framebufferH);
    687 
    688            for (var locNdx = 0; locNdx < outNumLocs; ++locNdx) {
    689                gl.readBuffer(gl.COLOR_ATTACHMENT0 + outLocation + locNdx);
    690                gl.readPixels(0, 0, framebufferW, framebufferH, transferFormat.format, transferFormat.dataType, tmpBuf.getAccess().getDataPtr());
    691 
    692                if (outSize == 4 && outNumLocs == 1) {
    693                    outputs[outNdx] = new Uint8Array(tmpBuf.getAccess().getBuffer());
    694                } else {
    695                    if (locNdx == 0)
    696                        outputs[outNdx] = new Uint32Array(numValues * outVecSize);
    697                    var srcPtr = new Uint32Array(tmpBuf.getAccess().getBuffer());
    698                    for (var valNdx = 0; valNdx < numValues; valNdx++) {
    699                        var srcOffset = valNdx * 4;
    700                        var dstOffset = outSize * valNdx + outVecSize * locNdx;
    701                        for (var j = 0; j < outVecSize; j++)
    702                        outputs[outNdx][dstOffset + j] = srcPtr[srcOffset + j];
    703                    }
    704                }
    705            }
    706        }
    707 
    708        // \todo [2013-08-07 pyry] Clear draw buffers & viewport?
    709        gl.bindFramebuffer(gl.FRAMEBUFFER, null);
    710        return outputs;
    711    };
    712 
    713    glsShaderExecUtil.generateLocationMap = function(symbols, locationSymbols) {
    714        var ret = [];
    715        locationSymbols.length = 0;
    716        var location = 0;
    717 
    718        for (var i = 0; i < symbols.length; i++) {
    719            var symbol = symbols[i];
    720            var numLocations = gluShaderUtil.getDataTypeNumLocations(symbol.varType.getBasicType());
    721            ret[symbol.name] = location;
    722            location += numLocations;
    723 
    724            for (var ndx = 0; ndx < numLocations; ++ndx)
    725                locationSymbols.push(symbol);
    726        }
    727 
    728        return ret;
    729    };
    730 
    731 });