/*------------------------------------------------------------------------- * drawElements Quality Program OpenGL (ES) Module * ----------------------------------------------- * * Copyright 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *//*! * \file * \brief Shader execution utilities. *//*--------------------------------------------------------------------*/ 'use strict'; goog.provide('modules.shared.glsShaderExecUtil'); goog.require('framework.common.tcuMatrix'); goog.require('framework.common.tcuMatrixUtil'); goog.require('framework.common.tcuTexture'); goog.require('framework.opengl.gluDrawUtil'); goog.require('framework.opengl.gluShaderProgram'); goog.require('framework.opengl.gluShaderUtil'); goog.require('framework.opengl.gluTextureUtil'); goog.require('framework.opengl.gluVarType'); goog.scope(function() { var glsShaderExecUtil = modules.shared.glsShaderExecUtil; var gluVarType = framework.opengl.gluVarType; var gluShaderUtil = framework.opengl.gluShaderUtil; var gluShaderProgram = framework.opengl.gluShaderProgram; var gluDrawUtil = framework.opengl.gluDrawUtil; var gluTextureUtil = framework.opengl.gluTextureUtil; var tcuTexture = framework.common.tcuTexture; var tcuMatrix = framework.common.tcuMatrix; var tcuMatrixUtil = framework.common.tcuMatrixUtil; var DE_ASSERT = function(x) { if (!x) throw new Error('Assert failed'); }; var setParentClass = function(child, parent) { child.prototype = Object.create(parent.prototype); child.prototype.constructor = child; }; /** * @constructor * @param {string=} name * @param {gluVarType.VarType=} varType */ glsShaderExecUtil.Symbol = function(name, varType) { name = name === undefined ? '' : name; /** @type {string} */ this.name = name; /** @type {gluVarType.VarType} */ this.varType = varType || null; }; //! Complete shader specification. /** * @constructor */ glsShaderExecUtil.ShaderSpec = function() { /** @type {gluShaderUtil.GLSLVersion} */ this.version = gluShaderUtil.GLSLVersion.V300_ES; //!< Shader version. /** @type {Array} */ this.inputs = []; /** @type {Array} */ this.outputs = []; /** @type {string} */ this.globalDeclarations = ''; //!< These are placed into global scope. Can contain uniform declarations for example. /** @type {*} */ this.source; //!< Source snippet to be executed. }; /** * Base class for shader executor. * @constructor * @param {glsShaderExecUtil.ShaderSpec} shaderSpec */ glsShaderExecUtil.ShaderExecutor = function(shaderSpec) { /** @type {Array} */ this.m_inputs = shaderSpec.inputs; /** @type {Array} */ this.m_outputs = shaderSpec.outputs; }; glsShaderExecUtil.ShaderExecutor.prototype.useProgram = function() { DE_ASSERT(this.isOk); gl.useProgram(this.getProgram()); }; /** * @return {boolean} */ glsShaderExecUtil.ShaderExecutor.prototype.isOk = function() { throw new Error('Virtual function. Please override.'); }; /** * @return {WebGLProgram} */ glsShaderExecUtil.ShaderExecutor.prototype.getProgram = function() { throw new Error('Virtual function. Please override.'); }; /** * @param {number} numValues * @param {Array>} inputs * @return {Array} outputs */ glsShaderExecUtil.ShaderExecutor.prototype.execute = function(numValues, inputs) { throw new Error('Virtual function. Please override.'); }; /** * Base class for shader executor. * @param {gluShaderProgram.shaderType} shaderType * @param {glsShaderExecUtil.ShaderSpec} shaderSpec * @return {glsShaderExecUtil.ShaderExecutor} */ glsShaderExecUtil.createExecutor = function(shaderType, shaderSpec) { switch (shaderType) { case gluShaderProgram.shaderType.VERTEX: return new glsShaderExecUtil.VertexShaderExecutor(shaderSpec); case gluShaderProgram.shaderType.FRAGMENT: return new glsShaderExecUtil.FragmentShaderExecutor(shaderSpec); default: throw new Error('Unsupported shader type: ' + shaderType); } }; /** * @param {glsShaderExecUtil.ShaderSpec} shaderSpec * @return {string} */ glsShaderExecUtil.generateVertexShader = function(shaderSpec) { /** @type {boolean} */ var usesInout = true; /** @type {string} */ var in_ = usesInout ? 'in' : 'attribute'; /** @type {string} */ var out = usesInout ? 'out' : 'varying'; /** @type {string} */ var src = ''; /** @type {number} */ var vecSize; /** @type {gluShaderUtil.DataType} */ var intBaseType; src += '#version 300 es\n'; if (shaderSpec.globalDeclarations.length > 0) src += (shaderSpec.globalDeclarations + '\n'); for (var i = 0; i < shaderSpec.inputs.length; ++i) src += (in_ + ' ' + gluVarType.declareVariable(shaderSpec.inputs[i].varType, shaderSpec.inputs[i].name) + ';\n'); for (var i = 0; i < shaderSpec.outputs.length; i++) { var output = shaderSpec.outputs[i]; DE_ASSERT(output.varType.isBasicType()); if (gluShaderUtil.isDataTypeBoolOrBVec(output.varType.getBasicType())) { vecSize = gluShaderUtil.getDataTypeScalarSize(output.varType.getBasicType()); intBaseType = vecSize > 1 ? gluShaderUtil.getDataTypeVector(gluShaderUtil.DataType.INT, vecSize) : gluShaderUtil.DataType.INT; /** @type {gluVarType.VarType} */ var intType = new gluVarType.VarType().VarTypeBasic(intBaseType, gluShaderUtil.precision.PRECISION_HIGHP); src += ('flat ' + out + ' ' + gluVarType.declareVariable(intType, 'o_' + output.name) + ';\n'); } else src += ('flat ' + out + ' ' + gluVarType.declareVariable(output.varType, output.name) + ';\n'); } src += '\n' + 'void main (void)\n' + '{\n' + ' gl_Position = vec4(0.0);\n' + ' gl_PointSize = 1.0;\n\n'; // Declare necessary output variables (bools). for (var i = 0; i < shaderSpec.outputs.length; i++) { if (gluShaderUtil.isDataTypeBoolOrBVec(shaderSpec.outputs[i].varType.getBasicType())) src += ('\t' + gluVarType.declareVariable(shaderSpec.outputs[i].varType, shaderSpec.outputs[i].name) + ';\n'); } //Operation - indented to correct level. // TODO: Add indenting src += shaderSpec.source; // Assignments to outputs. for (var i = 0; i < shaderSpec.outputs.length; i++) { if (gluShaderUtil.isDataTypeBoolOrBVec(output.varType.getBasicType())) { vecSize = gluShaderUtil.getDataTypeScalarSize(output.varType.getBasicType()); intBaseType = vecSize > 1 ? gluShaderUtil.getDataTypeVector(gluShaderUtil.DataType.INT, vecSize) : gluShaderUtil.DataType.INT; src += ('\to_' + output.name + ' = ' + gluShaderUtil.getDataTypeName(intBaseType) + '(' + output.name + ');\n'); } } src += '}\n'; return src; }; /** * @return {string} */ glsShaderExecUtil.generateEmptyFragmentSource = function() { /** @type {string} */ var src; src = '#version 300 es\n'; src += 'out lowp vec4 color;\n'; src += 'void main (void)\n{\n'; src += ' color = vec4(0.0);\n'; src += '}\n'; return src; }; /** * @param {glsShaderExecUtil.ShaderSpec} shaderSpec * @param {string} inputPrefix * @param {string} outputPrefix * @return {string} */ glsShaderExecUtil.generatePassthroughVertexShader = function(shaderSpec, inputPrefix, outputPrefix) { // flat qualifier is not present in earlier versions? // DE_ASSERT(glu::glslVersionUsesInOutQualifiers(shaderSpec.version)); /** @type {string} */ var src; src = '#version 300 es\n' + 'in highp vec4 a_position;\n'; for (var i = 0; i < shaderSpec.inputs.length; i++) { src += ('in ' + gluVarType.declareVariable(shaderSpec.inputs[i].varType, inputPrefix + shaderSpec.inputs[i].name) + ';\n' + 'flat out ' + gluVarType.declareVariable(shaderSpec.inputs[i].varType, outputPrefix + shaderSpec.inputs[i].name) + ';\n'); } src += '\nvoid main (void)\n{\n' + ' gl_Position = a_position;\n' + ' gl_PointSize = 1.0;\n'; for (var i = 0; i < shaderSpec.inputs.length; i++) src += ('\t' + outputPrefix + shaderSpec.inputs[i].name + ' = ' + inputPrefix + shaderSpec.inputs[i].name + ';\n'); src += '}\n'; return src; }; /** * @param {glsShaderExecUtil.ShaderSpec} shaderSpec * @param {boolean} useIntOutputs * @param {*} outLocationMap * @return {string} */ glsShaderExecUtil.generateFragmentShader = function(shaderSpec, useIntOutputs, outLocationMap) { /** @type {number} */ var vecSize; /** @type {number} */ var numVecs; /** @type {gluShaderUtil.DataType} */ var intBasicType; /** @type {gluShaderUtil.DataType} */ var uintBasicType; /** @type {gluVarType.VarType} */ var uintType; /** @type {gluVarType.VarType} */ var intType; /** @type {string} */ var src; src = '#version 300 es\n'; if (!shaderSpec.globalDeclarations.length > 0) src += (shaderSpec.globalDeclarations + '\n'); for (var i = 0; i < shaderSpec.inputs.length; i++) src += ('flat in ' + gluVarType.declareVariable(shaderSpec.inputs[i].varType, shaderSpec.inputs[i].name) + ';\n'); for (var outNdx = 0; outNdx < shaderSpec.outputs.length; ++outNdx) { /** @type {glsShaderExecUtil.Symbol} */ var output = shaderSpec.outputs[outNdx]; /** @type {number} */ var location = outLocationMap[output.name]; /** @type {string} */ var outVarName = 'o_' + output.name; /** @type {gluVarType.VariableDeclaration} */ var decl = new gluVarType.VariableDeclaration(output.varType, outVarName, gluVarType.Storage.STORAGE_OUT, undefined, new gluVarType.Layout(location)); DE_ASSERT(output.varType.isBasicType()); if (useIntOutputs && gluShaderUtil.isDataTypeFloatOrVec(output.varType.getBasicType())) { vecSize = gluShaderUtil.getDataTypeScalarSize(output.varType.getBasicType()); uintBasicType = vecSize > 1 ? gluShaderUtil.getDataTypeVector(gluShaderUtil.DataType.UINT, vecSize) : gluShaderUtil.DataType.UINT; uintType = gluVarType.newTypeBasic(uintBasicType, gluShaderUtil.precision.PRECISION_HIGHP); decl.varType = uintType; src += (decl + ';\n'); } else if (gluShaderUtil.isDataTypeBoolOrBVec(output.varType.getBasicType())) { vecSize = gluShaderUtil.getDataTypeScalarSize(output.varType.getBasicType()); intBasicType = vecSize > 1 ? gluShaderUtil.getDataTypeVector(gluShaderUtil.DataType.INT, vecSize) : gluShaderUtil.DataType.INT; intType = gluVarType.newTypeBasic(intBasicType, gluShaderUtil.precision.PRECISION_HIGHP); decl.varType = intType; src += (decl + ';\n'); } else if (gluShaderUtil.isDataTypeMatrix(output.varType.getBasicType())) { vecSize = gluShaderUtil.getDataTypeMatrixNumRows(output.varType.getBasicType()); numVecs = gluShaderUtil.getDataTypeMatrixNumColumns(output.varType.getBasicType()); uintBasicType = gluShaderUtil.getDataTypeVector(gluShaderUtil.DataType.UINT, vecSize); uintType = gluVarType.newTypeBasic(uintBasicType, gluShaderUtil.precision.PRECISION_HIGHP); decl.varType = uintType; for (var vecNdx = 0; vecNdx < numVecs; ++vecNdx) { decl.name = outVarName + '_' + (vecNdx); decl.layout.location = location + vecNdx; src += (decl + ';\n'); } } else //src += '';//glu::VariableDeclaration(output.varType, output.name, glu::STORAGE_OUT, glu::INTERPOLATION_LAST, location) << ";\n"; src += new gluVarType.VariableDeclaration(output.varType, output.name, gluVarType.Storage.STORAGE_OUT, undefined, new gluVarType.Layout(location)) + ';\n'; } src += '\nvoid main (void)\n{\n'; for (var i = 0; i < shaderSpec.outputs.length; i++) { if ((useIntOutputs && gluShaderUtil.isDataTypeFloatOrVec(shaderSpec.outputs[i].varType.getBasicType())) || gluShaderUtil.isDataTypeBoolOrBVec(shaderSpec.outputs[i].varType.getBasicType()) || gluShaderUtil.isDataTypeMatrix(shaderSpec.outputs[i].varType.getBasicType())) src += ('\t' + gluVarType.declareVariable(shaderSpec.outputs[i].varType, shaderSpec.outputs[i].name) + ';\n'); } // Operation - indented to correct level. // TODO: Add indenting src += shaderSpec.source; // { // std::istringstream opSrc (shaderSpec.source); // /** @type{number} */ var line; // // while (std::getline(opSrc, line)) // src += ('\t' << line << '\n'); // } for (var i = 0; i < shaderSpec.outputs.length; i++) { if (useIntOutputs && gluShaderUtil.isDataTypeFloatOrVec(shaderSpec.outputs[i].varType.getBasicType())) src += (' o_' + shaderSpec.outputs[i].name + ' = floatBitsToUint(' + shaderSpec.outputs[i].name + ');\n'); else if (gluShaderUtil.isDataTypeMatrix(shaderSpec.outputs[i].varType.getBasicType())) { numVecs = gluShaderUtil.getDataTypeMatrixNumColumns(shaderSpec.outputs[i].varType.getBasicType()); for (var vecNdx = 0; vecNdx < numVecs; ++vecNdx) if (useIntOutputs) src += ('\to_' + shaderSpec.outputs[i].name + '_' + vecNdx + ' = floatBitsToUint(' + shaderSpec.outputs[i].name + '[' + vecNdx + ']);\n'); else src += ('\to_' + shaderSpec.outputs[i].name + '_' + vecNdx + ' = ' + shaderSpec.outputs[i].name + '[' + vecNdx + '];\n'); } else if (gluShaderUtil.isDataTypeBoolOrBVec(shaderSpec.outputs[i].varType.getBasicType())) { vecSize = gluShaderUtil.getDataTypeScalarSize(shaderSpec.outputs[i].varType.getBasicType()); intBasicType = vecSize > 1 ? gluShaderUtil.getDataTypeVector(gluShaderUtil.DataType.INT, vecSize) : gluShaderUtil.DataType.INT; src += ('\to_' + shaderSpec.outputs[i].name + ' = ' + gluShaderUtil.getDataTypeName(intBasicType) + '(' + shaderSpec.outputs[i].name + ');\n'); } } src += '}\n'; return src; }; /** * @param {Array} outputs * @return {gluShaderProgram.TransformFeedbackVaryings} */ glsShaderExecUtil.getTFVaryings = function(outputs) { var names = []; for (var i = 0; i < outputs.length; i++) { if (gluShaderUtil.isDataTypeBoolOrBVec(outputs[i].varType.getBasicType())) { names.push('o_' + outputs[i].name); } else { names.push(outputs[i].name); } } return new gluShaderProgram.TransformFeedbackVaryings(names); }; // VertexProcessorExecutor (base class for vertex and geometry executors) /** * @constructor * @extends {glsShaderExecUtil.ShaderExecutor} * @param {glsShaderExecUtil.ShaderSpec} shaderSpec * @param {gluShaderProgram.ProgramSources} sources */ glsShaderExecUtil.VertexProcessorExecutor = function(shaderSpec, sources) { sources.add(glsShaderExecUtil.getTFVaryings(shaderSpec.outputs)); sources.add(new gluShaderProgram.TransformFeedbackMode(gl.INTERLEAVED_ATTRIBS)); glsShaderExecUtil.ShaderExecutor.call(this, shaderSpec); this.m_program = new gluShaderProgram.ShaderProgram(gl, sources); }; setParentClass(glsShaderExecUtil.VertexProcessorExecutor, glsShaderExecUtil.ShaderExecutor); /** * @return {boolean} */ glsShaderExecUtil.VertexProcessorExecutor.prototype.isOk = function() { return this.m_program.isOk(); }; /** * @return {WebGLProgram} */ glsShaderExecUtil.VertexProcessorExecutor.prototype.getProgram = function() { return this.m_program.getProgram(); }; /** * @param {Array<*>} arr * @return {number} */ glsShaderExecUtil.computeTotalScalarSize = function(arr) { /** @type {number} */ var size = 0; for (var i = 0; i < arr.length; i++) size += arr[i].varType.getScalarSize(); return size; }; /** * @param {Array} ptr * @param {number} colNdx * @param {number} size Column size * @return {Array} */ glsShaderExecUtil.getColumn = function(ptr, colNdx, size) { var begin = colNdx * size; var end = (colNdx + 1) * size; return ptr.slice(begin, end); }; glsShaderExecUtil.VertexProcessorExecutor.prototype.execute = function(numValues, inputs) { /** @type {glsShaderExecUtil.Symbol} */ var symbol; var outputs = []; /** @type {boolean} */ var useTFObject = true; /** @type {Array} */ var vertexArrays = []; var transformFeedback = gl.createTransformFeedback(); var outputBuffer = gl.createBuffer(); /** @type {number} */ var outputBufferStride = glsShaderExecUtil.computeTotalScalarSize(this.m_outputs) * 4; // Setup inputs. for (var inputNdx = 0; inputNdx < this.m_inputs.length; inputNdx++) { symbol = this.m_inputs[inputNdx]; /*const void* */var ptr = inputs[inputNdx]; /** @type {gluShaderUtil.DataType} */ var basicType = symbol.varType.getBasicType(); /** @type {number} */ var vecSize = gluShaderUtil.getDataTypeScalarSize(basicType); if (gluShaderUtil.isDataTypeFloatOrVec(basicType)) vertexArrays.push(gluDrawUtil.newFloatVertexArrayBinding(symbol.name, vecSize, numValues, 0, ptr)); else if (gluShaderUtil.isDataTypeIntOrIVec(basicType)) vertexArrays.push(gluDrawUtil.newInt32VertexArrayBinding(symbol.name, vecSize, numValues, 0, ptr)); else if (gluShaderUtil.isDataTypeUintOrUVec(basicType)) vertexArrays.push(gluDrawUtil.newUint32VertexArrayBinding(symbol.name, vecSize, numValues, 0, ptr)); else if (gluShaderUtil.isDataTypeMatrix(basicType)) { /** @type {number} */ var numRows = gluShaderUtil.getDataTypeMatrixNumRows(basicType); /** @type {number} */ var numCols = gluShaderUtil.getDataTypeMatrixNumColumns(basicType); // A matrix consists of several (column-major) vectors. A buffer is created for // every vector in gluDrawUtil.draw() below. Data in every buffer will be tightly // packed. So the stride should be 0. This is different from the code in native // deqp, which use only one buffer for a matrix, the data is interleaved. /** @type {number} */ var stride = 0; for (var colNdx = 0; colNdx < numCols; ++colNdx) vertexArrays.push(gluDrawUtil.newFloatColumnVertexArrayBinding(symbol.name, colNdx, numRows, numValues, stride, glsShaderExecUtil.getColumn(ptr, colNdx, numRows * numValues))); } else DE_ASSERT(false); } // Setup TF outputs. if (useTFObject) gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, transformFeedback); gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, outputBuffer); gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, outputBufferStride * numValues, gl.STREAM_READ); gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, outputBuffer); // Draw with rasterization disabled. gl.beginTransformFeedback(gl.POINTS); gl.enable(gl.RASTERIZER_DISCARD); gluDrawUtil.draw(gl, this.m_program.getProgram(), vertexArrays, new gluDrawUtil.PrimitiveList(gluDrawUtil.primitiveType.POINTS, numValues)); gl.disable(gl.RASTERIZER_DISCARD); gl.endTransformFeedback(); // Read back data. var result = new ArrayBuffer(outputBufferStride * numValues); gl.getBufferSubData(gl.TRANSFORM_FEEDBACK_BUFFER, 0, new Uint8Array(result)); /** @type {number} */ var curOffset = 0; // Offset in buffer in bytes. for (var outputNdx = 0; outputNdx < this.m_outputs.length; outputNdx++) { symbol = this.m_outputs[outputNdx]; /** @type {number} */ var scalarSize = symbol.varType.getScalarSize(); var readPtr = new Uint8Array(result, curOffset); if (scalarSize * 4 === outputBufferStride) outputs[outputNdx] = readPtr; else { var dstPtr = new Uint8Array(scalarSize * numValues * 4); for (var ndx = 0; ndx < numValues; ndx++) for (var j = 0; j < scalarSize * 4; j++) { dstPtr[scalarSize * 4 * ndx + j] = readPtr[ndx * outputBufferStride + j]; } outputs[outputNdx] = dstPtr; } curOffset += scalarSize * 4; } if (useTFObject) gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, null); gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, null); return outputs; }; // VertexShaderExecutor /** * @constructor * @extends {glsShaderExecUtil.VertexProcessorExecutor} * @param {glsShaderExecUtil.ShaderSpec} shaderSpec */ glsShaderExecUtil.VertexShaderExecutor = function(shaderSpec) { var sources = gluShaderProgram.makeVtxFragSources(glsShaderExecUtil.generateVertexShader(shaderSpec), glsShaderExecUtil.generateEmptyFragmentSource()); glsShaderExecUtil.VertexProcessorExecutor.call(this, shaderSpec, sources); }; setParentClass(glsShaderExecUtil.VertexShaderExecutor, glsShaderExecUtil.VertexProcessorExecutor); /** * @constructor * @extends {glsShaderExecUtil.ShaderExecutor} * @param {glsShaderExecUtil.ShaderSpec} shaderSpec */ glsShaderExecUtil.FragmentShaderExecutor = function(shaderSpec) { glsShaderExecUtil.ShaderExecutor.call(this, shaderSpec); /** @type {Array} */ this.m_outLocationSymbols = []; this.m_outLocationMap = glsShaderExecUtil.generateLocationMap(this.m_outputs, this.m_outLocationSymbols); var sources = gluShaderProgram.makeVtxFragSources(glsShaderExecUtil.generatePassthroughVertexShader(shaderSpec, 'a_', ''), glsShaderExecUtil.generateFragmentShader(shaderSpec, true, this.m_outLocationMap)); this.m_program = new gluShaderProgram.ShaderProgram(gl, sources); }; setParentClass(glsShaderExecUtil.FragmentShaderExecutor, glsShaderExecUtil.ShaderExecutor); /** * @return {boolean} */ glsShaderExecUtil.FragmentShaderExecutor.prototype.isOk = function() { return this.m_program.isOk(); }; /** * @return {WebGLProgram} */ glsShaderExecUtil.FragmentShaderExecutor.prototype.getProgram = function() { return this.m_program.getProgram(); }; /** * @param {gluVarType.VarType} outputType * @param {boolean} useIntOutputs * @return {tcuTexture.TextureFormat} */ glsShaderExecUtil.getRenderbufferFormatForOutput = function(outputType, useIntOutputs) { var channelOrderMap = [ tcuTexture.ChannelOrder.R, tcuTexture.ChannelOrder.RG, tcuTexture.ChannelOrder.RGBA, // No RGB variants available. tcuTexture.ChannelOrder.RGBA ]; var basicType = outputType.getBasicType(); var numComps = gluShaderUtil.getDataTypeNumComponents(basicType); var channelType; switch (gluShaderUtil.getDataTypeScalarType(basicType)) { case 'uint': channelType = tcuTexture.ChannelType.UNSIGNED_INT32; break; case 'int': channelType = tcuTexture.ChannelType.SIGNED_INT32; break; case 'bool': channelType = tcuTexture.ChannelType.SIGNED_INT32; break; case 'float': channelType = useIntOutputs ? tcuTexture.ChannelType.UNSIGNED_INT32 : tcuTexture.ChannelType.FLOAT; break; default: throw new Error('Invalid output type ' + gluShaderUtil.getDataTypeScalarType(basicType)); } return new tcuTexture.TextureFormat(channelOrderMap[numComps - 1], channelType); }; glsShaderExecUtil.FragmentShaderExecutor.prototype.execute = function(numValues, inputs) { /** @type {boolean} */ var useIntOutputs = true; /** @type {glsShaderExecUtil.Symbol} */ var symbol; var outputs = []; var maxRenderbufferSize = /** @type {number} */ (gl.getParameter(gl.MAX_RENDERBUFFER_SIZE)); /** @type {number} */ var framebufferW = Math.min(maxRenderbufferSize, numValues); /** @type {number} */ var framebufferH = Math.ceil(numValues / framebufferW); var framebuffer = gl.createFramebuffer(); var renderbuffers = []; for (var i = 0; i < this.m_outLocationSymbols.length; i++) renderbuffers.push(gl.createRenderbuffer()); var vertexArrays = []; var positions = []; if (framebufferH > maxRenderbufferSize) throw new Error('Value count is too high for maximum supported renderbuffer size'); // Compute positions - 1px points are used to drive fragment shading. for (var valNdx = 0; valNdx < numValues; valNdx++) { /** @type {number} */ var ix = valNdx % framebufferW; /** @type {number} */ var iy = Math.floor(valNdx / framebufferW); var fx = -1 + 2 * (ix + 0.5) / framebufferW; var fy = -1 + 2 * (iy + 0.5) / framebufferH; positions[2 * valNdx] = fx; positions[2 * valNdx + 1] = fy; } // Vertex inputs. vertexArrays.push(gluDrawUtil.newFloatVertexArrayBinding('a_position', 2, numValues, 0, positions)); for (var inputNdx = 0; inputNdx < this.m_inputs.length; inputNdx++) { symbol = this.m_inputs[inputNdx]; var attribName = 'a_' + symbol.name; var ptr = inputs[inputNdx]; /** @type {gluShaderUtil.DataType} */ var basicType = symbol.varType.getBasicType(); /** @type {number} */ var vecSize = gluShaderUtil.getDataTypeScalarSize(basicType); if (gluShaderUtil.isDataTypeFloatOrVec(basicType)) vertexArrays.push(gluDrawUtil.newFloatVertexArrayBinding(attribName, vecSize, numValues, 0, ptr)); else if (gluShaderUtil.isDataTypeIntOrIVec(basicType)) vertexArrays.push(gluDrawUtil.newInt32VertexArrayBinding(attribName, vecSize, numValues, 0, ptr)); else if (gluShaderUtil.isDataTypeUintOrUVec(basicType)) vertexArrays.push(gluDrawUtil.newUint32VertexArrayBinding(attribName, vecSize, numValues, 0, ptr)); else if (gluShaderUtil.isDataTypeMatrix(basicType)) { var numRows = gluShaderUtil.getDataTypeMatrixNumRows(basicType); var numCols = gluShaderUtil.getDataTypeMatrixNumColumns(basicType); // A matrix consists of several (column-major) vectors. A buffer is created for // every vector in gluDrawUtil.draw() below. Data in every buffer will be tightly // packed. So the stride should be 0. This is different from the code in native // deqp, which use only one buffer for a matrix, the data is interleaved. var stride = 0; for (var colNdx = 0; colNdx < numCols; ++colNdx) vertexArrays.push(gluDrawUtil.newFloatColumnVertexArrayBinding(attribName, colNdx, numRows, numValues, stride, glsShaderExecUtil.getColumn(ptr, colNdx, numRows * numValues))); } else DE_ASSERT(false); } // Construct framebuffer. gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer); for (var outNdx = 0; outNdx < this.m_outLocationSymbols.length; ++outNdx) { symbol = this.m_outLocationSymbols[outNdx]; var renderbuffer = renderbuffers[outNdx]; var format = gluTextureUtil.getInternalFormat(glsShaderExecUtil.getRenderbufferFormatForOutput(symbol.varType, useIntOutputs)); gl.bindRenderbuffer(gl.RENDERBUFFER, renderbuffer); gl.renderbufferStorage(gl.RENDERBUFFER, format, framebufferW, framebufferH); gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + outNdx, gl.RENDERBUFFER, renderbuffer); } gl.bindRenderbuffer(gl.RENDERBUFFER, null); assertMsgOptions(gl.checkFramebufferStatus(gl.FRAMEBUFFER) == gl.FRAMEBUFFER_COMPLETE, 'Framebuffer is incomplete', false, true); var drawBuffers = []; for (var ndx = 0; ndx < this.m_outLocationSymbols.length; ndx++) drawBuffers[ndx] = gl.COLOR_ATTACHMENT0 + ndx; gl.drawBuffers(drawBuffers); // Render gl.viewport(0, 0, framebufferW, framebufferH); gluDrawUtil.draw(gl, this.m_program.getProgram(), vertexArrays, new gluDrawUtil.PrimitiveList(gluDrawUtil.primitiveType.POINTS, numValues)); // Read back pixels. // \todo [2013-08-07 pyry] Some fast-paths could be added here. for (var outNdx = 0; outNdx < this.m_outputs.length; ++outNdx) { symbol = this.m_outputs[outNdx]; /** @type {number} */ var outSize = symbol.varType.getScalarSize(); /** @type {number} */ var outVecSize = gluShaderUtil.getDataTypeNumComponents(symbol.varType.getBasicType()); /** @type {number} */ var outNumLocs = gluShaderUtil.getDataTypeNumLocations(symbol.varType.getBasicType()); var format = glsShaderExecUtil.getRenderbufferFormatForOutput(symbol.varType, useIntOutputs); var readFormat = new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RGBA, format.type); var transferFormat = gluTextureUtil.getTransferFormat(readFormat); /** @type {number} */ var outLocation = this.m_outLocationMap[symbol.name]; var tmpBuf = new tcuTexture.TextureLevel(readFormat, framebufferW, framebufferH); for (var locNdx = 0; locNdx < outNumLocs; ++locNdx) { gl.readBuffer(gl.COLOR_ATTACHMENT0 + outLocation + locNdx); gl.readPixels(0, 0, framebufferW, framebufferH, transferFormat.format, transferFormat.dataType, tmpBuf.getAccess().getDataPtr()); if (outSize == 4 && outNumLocs == 1) { outputs[outNdx] = new Uint8Array(tmpBuf.getAccess().getBuffer()); } else { if (locNdx == 0) outputs[outNdx] = new Uint32Array(numValues * outVecSize); var srcPtr = new Uint32Array(tmpBuf.getAccess().getBuffer()); for (var valNdx = 0; valNdx < numValues; valNdx++) { var srcOffset = valNdx * 4; var dstOffset = outSize * valNdx + outVecSize * locNdx; for (var j = 0; j < outVecSize; j++) outputs[outNdx][dstOffset + j] = srcPtr[srcOffset + j]; } } } } // \todo [2013-08-07 pyry] Clear draw buffers & viewport? gl.bindFramebuffer(gl.FRAMEBUFFER, null); return outputs; }; glsShaderExecUtil.generateLocationMap = function(symbols, locationSymbols) { var ret = []; locationSymbols.length = 0; var location = 0; for (var i = 0; i < symbols.length; i++) { var symbol = symbols[i]; var numLocations = gluShaderUtil.getDataTypeNumLocations(symbol.varType.getBasicType()); ret[symbol.name] = location; location += numLocations; for (var ndx = 0; ndx < numLocations; ++ndx) locationSymbols.push(symbol); } return ret; }; });