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 });