gluShaderProgram.js (15560B)
1 /*------------------------------------------------------------------------- 2 * drawElements Quality gluShaderProgram.Program OpenGL ES Utilities 3 * ------------------------------------------------ 4 * 5 * Copyright 2014 The Android Open Source Project 6 * 7 * Licensed under the Apache License, Version 2.0 (the "License"); 8 * you may not use this file except in compliance with the License. 9 * You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, software 14 * distributed under the License is distributed on an "AS IS" BASIS, 15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 * See the License for the specific language governing permissions and 17 * limitations under the License. 18 * 19 */ 20 21 'use strict'; 22 goog.provide('framework.opengl.gluShaderProgram'); 23 24 goog.scope(function() { 25 26 var gluShaderProgram = framework.opengl.gluShaderProgram; 27 28 /** 29 * gluShaderProgram.Shader type enum 30 * @enum {number} 31 */ 32 gluShaderProgram.shaderType = { 33 VERTEX: 0, 34 FRAGMENT: 1 35 }; 36 37 /** 38 * gluShaderProgram.Shader type enum name 39 * @param {gluShaderProgram.shaderType} shaderType 40 * @return {string} 41 */ 42 gluShaderProgram.getShaderTypeName = function(shaderType) { 43 var s_names = 44 [ 45 'vertex', 46 'fragment' 47 ]; 48 49 return s_names[shaderType]; 50 }; 51 52 /** 53 * Get GL shader type from gluShaderProgram.shaderType 54 * @param {WebGL2RenderingContext} gl WebGL context 55 * @param {gluShaderProgram.shaderType} type gluShaderProgram.Shader Type 56 * @return {number} GL shader type 57 */ 58 gluShaderProgram.getGLShaderType = function(gl, type) { 59 var _glShaderType; 60 switch (type) { 61 case gluShaderProgram.shaderType.VERTEX: _glShaderType = gl.VERTEX_SHADER; break; 62 case gluShaderProgram.shaderType.FRAGMENT: _glShaderType = gl.FRAGMENT_SHADER; break; 63 default: 64 throw new Error('Unknown shader type ' + type); 65 } 66 return _glShaderType; 67 }; 68 69 /** 70 * Declares shader information 71 * @constructor 72 * @param {gluShaderProgram.shaderType} type 73 * @param {string=} source 74 */ 75 gluShaderProgram.ShaderInfo = function(type, source) { 76 this.type = type; /** gluShaderProgram.Shader type. */ 77 this.source = source; /** gluShaderProgram.Shader source. */ 78 this.infoLog; /** Compile info log. */ 79 this.compileOk = false; /** Did compilation succeed? */ 80 this.compileTimeUs = 0; /** Compile time in microseconds (us). */ 81 }; 82 83 /** 84 * Generates vertex shader info from source 85 * @param {string} source 86 * @return {gluShaderProgram.ShaderInfo} vertex shader info 87 */ 88 gluShaderProgram.genVertexSource = function(source) { 89 /** @type {gluShaderProgram.ShaderInfo} */ var shader = new gluShaderProgram.ShaderInfo(gluShaderProgram.shaderType.VERTEX, source); 90 return shader; 91 }; 92 93 /** 94 * Generates fragment shader info from source 95 * @param {string} source 96 * @return {gluShaderProgram.ShaderInfo} fragment shader info 97 */ 98 gluShaderProgram.genFragmentSource = function(source) { 99 /** @type {gluShaderProgram.ShaderInfo} */ var shader = new gluShaderProgram.ShaderInfo(gluShaderProgram.shaderType.FRAGMENT, source); 100 return shader; 101 }; 102 103 /** 104 * Generates shader from WebGL context and type 105 * @constructor 106 * @param {WebGL2RenderingContext} gl WebGL context 107 * @param {gluShaderProgram.shaderType} type gluShaderProgram.Shader Type 108 */ 109 gluShaderProgram.Shader = function(gl, type) { 110 this.gl = gl; 111 this.info = new gluShaderProgram.ShaderInfo(type); /** Client-side clone of state for debug / perf reasons. */ 112 this.shader = gl.createShader(gluShaderProgram.getGLShaderType(gl, type)); 113 assertMsgOptions(gl.getError() == gl.NO_ERROR, 'gl.createShader()', false, true); 114 115 this.setSources = function(source) { 116 this.gl.shaderSource(this.shader, source); 117 assertMsgOptions(this.gl.getError() == this.gl.NO_ERROR, 'glshaderSource()', false, true); 118 this.info.source = source; 119 }; 120 121 this.getCompileStatus = function() { 122 return this.info.compileOk; 123 }; 124 125 this.compile = function() { 126 this.info.compileOk = false; 127 this.info.compileTimeUs = 0; 128 this.info.infoLog = ''; 129 130 /** @type {Date} */ var compileStart = new Date(); 131 this.gl.compileShader(this.shader); 132 /** @type {Date} */ var compileEnd = new Date(); 133 this.info.compileTimeUs = 1000 * (compileEnd.getTime() - compileStart.getTime()); 134 135 assertMsgOptions(this.gl.getError() == this.gl.NO_ERROR, 'gl.compileShader()', false, true); 136 137 var compileStatus = this.gl.getShaderParameter(this.shader, this.gl.COMPILE_STATUS); 138 assertMsgOptions(this.gl.getError() == this.gl.NO_ERROR, 'glGetShaderParameter()', false, true); 139 140 this.info.compileOk = compileStatus; 141 this.info.infoLog = this.gl.getShaderInfoLog(this.shader); 142 }; 143 144 this.getShader = function() { 145 return this.shader; 146 }; 147 148 this.destroy = function() { 149 this.gl.deleteShader(this.shader); 150 }; 151 152 }; 153 /** 154 * Creates gluShaderProgram.ProgramInfo 155 * @constructor 156 */ 157 gluShaderProgram.ProgramInfo = function() { 158 /** @type {string} */ this.infoLog = ''; /** Link info log. */ 159 /** @type {boolean} */ this.linkOk = false; /** Did link succeed? */ 160 /** @type {number} */ this.linkTimeUs = 0; /** Link time in microseconds (us). */ 161 }; 162 163 /** 164 * Creates program. 165 * Inner methods: attach shaders, bind attributes location, link program and transform Feedback Varyings 166 * @constructor 167 * @param {WebGL2RenderingContext} gl WebGL context 168 * @param {WebGLProgram=} programID 169 */ 170 gluShaderProgram.Program = function(gl, programID) { 171 this.gl = gl; 172 this.program = programID || null; 173 this.info = new gluShaderProgram.ProgramInfo(); 174 175 if (!programID) { 176 this.program = gl.createProgram(); 177 assertMsgOptions(gl.getError() == gl.NO_ERROR, 'gl.createProgram()', false, true); 178 } 179 }; 180 181 /** 182 * @return {WebGLProgram} 183 */ 184 gluShaderProgram.Program.prototype.getProgram = function() { return this.program; }; 185 186 /** 187 * @return {gluShaderProgram.ProgramInfo} 188 */ 189 gluShaderProgram.Program.prototype.getInfo = function() { return this.info; }; 190 191 /** 192 * @param {WebGLShader} shader 193 */ 194 gluShaderProgram.Program.prototype.attachShader = function(shader) { 195 this.gl.attachShader(this.program, shader); 196 assertMsgOptions(this.gl.getError() == this.gl.NO_ERROR, 'gl.attachShader()', false, true); 197 }; 198 199 /** 200 * @param {WebGLShader} shader 201 */ 202 gluShaderProgram.Program.prototype.detachShader = function(shader) { 203 this.gl.detachShader(this.program, shader); 204 assertMsgOptions(this.gl.getError() == this.gl.NO_ERROR, 'gl.detachShader()', false, true); 205 }; 206 207 /** 208 * @param {number} location 209 * @param {string} name 210 */ 211 gluShaderProgram.Program.prototype.bindAttribLocation = function(location, name) { 212 this.gl.bindAttribLocation(this.program, location, name); 213 assertMsgOptions(this.gl.getError() == this.gl.NO_ERROR, 'gl.bindAttribLocation()', false, true); 214 }; 215 216 gluShaderProgram.Program.prototype.link = function() { 217 this.info.linkOk = false; 218 this.info.linkTimeUs = 0; 219 this.info.infoLog = ''; 220 221 /** @type {Date} */ var linkStart = new Date(); 222 this.gl.linkProgram(this.program); 223 /** @type {Date} */ var linkEnd = new Date(); 224 this.info.linkTimeUs = 1000 * (linkEnd.getTime() - linkStart.getTime()); 225 226 assertMsgOptions(this.gl.getError() == this.gl.NO_ERROR, 'gl.linkProgram()', false, true); 227 228 var linkStatus = this.gl.getProgramParameter(this.program, this.gl.LINK_STATUS); 229 assertMsgOptions(this.gl.getError() == this.gl.NO_ERROR, 'gl.getProgramParameter()', false, true); 230 this.info.linkOk = linkStatus; 231 this.info.infoLog = this.gl.getProgramInfoLog(this.program); 232 if (!this.info.linkOk) 233 bufferedLogToConsole("program linking: " + this.info.infoLog); 234 }; 235 236 /** 237 * return {boolean} 238 */ 239 gluShaderProgram.Program.prototype.getLinkStatus = function() { 240 return this.info.linkOk; 241 }; 242 243 /** 244 * @param {Array<string>} varyings 245 * @param {number} bufferMode 246 */ 247 gluShaderProgram.Program.prototype.transformFeedbackVaryings = function(varyings, bufferMode) { 248 this.gl.transformFeedbackVaryings(this.program, varyings, bufferMode); 249 assertMsgOptions(this.gl.getError() == this.gl.NO_ERROR, 'gl.transformFeedbackVaryings()', false, true); 250 }; 251 252 /** 253 * Assigns gl WebGL context and programSources. Declares array of shaders and new program() 254 * @constructor 255 * @param {WebGL2RenderingContext} gl WebGL context 256 * @param {gluShaderProgram.ProgramSources} programSources 257 */ 258 gluShaderProgram.ShaderProgram = function(gl, programSources) { 259 this.gl = gl; 260 this.programSources = programSources; 261 this.shaders = []; 262 this.program = new gluShaderProgram.Program(gl); 263 264 /** @type {boolean} */ this.shadersOK = true; 265 266 for (var i = 0; i < programSources.sources.length; i++) { 267 /** @type {gluShaderProgram.Shader} */ var shader = new gluShaderProgram.Shader(gl, programSources.sources[i].type); 268 shader.setSources(programSources.sources[i].source); 269 shader.compile(); 270 this.shaders.push(shader); 271 this.shadersOK = this.shadersOK && shader.getCompileStatus(); 272 if (!shader.getCompileStatus()) { 273 bufferedLogToConsole('gluShaderProgram.Shader:\n' + programSources.sources[i].source); 274 bufferedLogToConsole('Compile status: ' + shader.getCompileStatus()); 275 bufferedLogToConsole('Shader infoLog: ' + shader.info.infoLog); 276 } 277 } 278 279 if (this.shadersOK) { 280 for (var i = 0; i < this.shaders.length; i++) 281 this.program.attachShader(this.shaders[i].getShader()); 282 283 for (var attrib in programSources.attribLocationBindings) 284 this.program.bindAttribLocation(programSources.attribLocationBindings[attrib], attrib); 285 286 if (programSources.transformFeedbackBufferMode) 287 if (programSources.transformFeedbackBufferMode === gl.NONE) 288 assertMsgOptions(programSources.transformFeedbackVaryings.length === 0, 'Transform feedback sanity check', false, true); 289 else 290 this.program.transformFeedbackVaryings(programSources.transformFeedbackVaryings, programSources.transformFeedbackBufferMode); 291 292 /* TODO: GLES 3.1: set separable flag */ 293 294 this.program.link(); 295 296 } 297 }; 298 299 /** 300 * return {WebGLProgram} 301 */ 302 gluShaderProgram.ShaderProgram.prototype.getProgram = function() { 303 return this.program.program; 304 }; 305 306 /** 307 * @return {gluShaderProgram.ProgramInfo} 308 */ 309 gluShaderProgram.ShaderProgram.prototype.getProgramInfo = function() { 310 return this.program.info; 311 }; 312 313 gluShaderProgram.ShaderProgram.prototype.isOk = function() { 314 return this.shadersOK && this.program.getLinkStatus(); 315 }; 316 317 gluShaderProgram.containerTypes = { 318 ATTRIB_LOCATION_BINDING: 0, 319 TRANSFORM_FEEDBACK_MODE: 1, 320 TRANSFORM_FEEDBACK_VARYING: 2, 321 TRANSFORM_FEEDBACK_VARYINGS: 3, 322 SHADER_SOURCE: 4, 323 PROGRAM_SEPARABLE: 5, 324 PROGRAM_SOURCES: 6, 325 326 CONTAINER_TYPE_LAST: 7, 327 ATTACHABLE_BEGIN: 0, // ATTRIB_LOCATION_BINDING 328 ATTACHABLE_END: 5 + 1 // PROGRAM_SEPARABLE + 1 329 }; 330 331 /** 332 * @constructor 333 */ 334 gluShaderProgram.AttribLocationBinding = function(name, location) { 335 this.name = name; 336 this.location = location; 337 338 this.getContainerType = function() { 339 return gluShaderProgram.containerTypes.ATTRIB_LOCATION_BINDING; 340 }; 341 }; 342 343 /** 344 * @constructor 345 */ 346 gluShaderProgram.TransformFeedbackMode = function(mode) { 347 this.mode = mode; 348 349 this.getContainerType = function() { 350 return gluShaderProgram.containerTypes.TRANSFORM_FEEDBACK_MODE; 351 }; 352 }; 353 354 /** 355 * @constructor 356 * @param {string} name 357 */ 358 gluShaderProgram.TransformFeedbackVarying = function(name) { 359 /** @type {string} */ this.name = name; 360 361 this.getContainerType = function() { 362 return gluShaderProgram.containerTypes.TRANSFORM_FEEDBACK_VARYING; 363 }; 364 }; 365 366 /** 367 * @constructor 368 * @param {Array<string>} array 369 */ 370 gluShaderProgram.TransformFeedbackVaryings = function(array) { 371 /** @type {Array<string>} */ this.array = array; 372 373 this.getContainerType = function() { 374 return gluShaderProgram.containerTypes.TRANSFORM_FEEDBACK_VARYINGS; 375 }; 376 }; 377 378 /** 379 * @constructor 380 */ 381 gluShaderProgram.ProgramSeparable = function(separable) { 382 this.separable = separable; 383 384 this.getContainerType = function() { 385 return gluShaderProgram.containerTypes.PROGRAM_SEPARABLE; 386 }; 387 }; 388 389 /** 390 * @constructor 391 */ 392 gluShaderProgram.VertexSource = function(str) { 393 this.shaderType = gluShaderProgram.shaderType.VERTEX; 394 this.source = str; 395 396 this.getContainerType = function() { 397 return gluShaderProgram.containerTypes.SHADER_SOURCE; 398 }; 399 }; 400 401 /** 402 * @constructor 403 */ 404 gluShaderProgram.FragmentSource = function(str) { 405 this.shaderType = gluShaderProgram.shaderType.FRAGMENT; 406 this.source = str; 407 408 this.getContainerType = function() { 409 return gluShaderProgram.containerTypes.SHADER_SOURCE; 410 }; 411 }; 412 413 /** 414 * Create gluShaderProgram.ProgramSources. 415 * @constructor 416 */ 417 gluShaderProgram.ProgramSources = function() { 418 /** @type {Array<gluShaderProgram.ShaderInfo>} */ this.sources = []; 419 this.attribLocationBindings = []; 420 /** @type {Array<string>} */ this.transformFeedbackVaryings = []; 421 this.transformFeedbackBufferMode = 0; 422 this.separable = false; 423 }; 424 425 gluShaderProgram.ProgramSources.prototype.getContainerType = function() { 426 return gluShaderProgram.containerTypes.PROGRAM_SOURCES; 427 }; 428 429 gluShaderProgram.ProgramSources.prototype.add = function(item) { 430 var type = undefined; 431 if (typeof(item.getContainerType) == 'function') { 432 type = item.getContainerType(); 433 if ( 434 typeof(type) != 'number' || 435 type < gluShaderProgram.containerTypes.ATTACHABLE_BEGIN || 436 type >= gluShaderProgram.containerTypes.ATTACHABLE_END 437 ) { 438 type = undefined; 439 } 440 } 441 442 switch (type) { 443 case gluShaderProgram.containerTypes.ATTRIB_LOCATION_BINDING: 444 this.attribLocationBindings.push(item); 445 break; 446 447 case gluShaderProgram.containerTypes.TRANSFORM_FEEDBACK_MODE: 448 this.transformFeedbackBufferMode = item.mode; 449 break; 450 451 case gluShaderProgram.containerTypes.TRANSFORM_FEEDBACK_VARYING: 452 this.transformFeedbackVaryings.push(item.name); 453 break; 454 455 case gluShaderProgram.containerTypes.TRANSFORM_FEEDBACK_VARYINGS: 456 this.transformFeedbackVaryings = this.transformFeedbackVaryings.concat(item.array); 457 break; 458 459 case gluShaderProgram.containerTypes.SHADER_SOURCE: 460 this.sources.push(new gluShaderProgram.ShaderInfo(item.shaderType, item.source)); 461 break; 462 463 case gluShaderProgram.containerTypes.PROGRAM_SEPARABLE: 464 this.separable = item.separable; 465 break; 466 467 default: 468 throw new Error('Type \"' + type + '\" cannot be added to gluShaderProgram.ProgramSources.'); 469 break; 470 } 471 472 return this; 473 }; 474 475 /** 476 * //! Helper for constructing vertex-fragment source pair. 477 * @param {string} vertexSrc 478 * @param {string} fragmentSrc 479 * @return {gluShaderProgram.ProgramSources} 480 */ 481 gluShaderProgram.makeVtxFragSources = function(vertexSrc, fragmentSrc) { 482 /** @type {gluShaderProgram.ProgramSources} */ var sources = new gluShaderProgram.ProgramSources(); 483 sources.sources.push(gluShaderProgram.genVertexSource(vertexSrc)); 484 sources.sources.push(gluShaderProgram.genFragmentSource(fragmentSrc)); 485 return sources; 486 }; 487 488 });