ShaderProgram.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446
  1. /*global define*/
  2. define([
  3. '../Core/defined',
  4. '../Core/defineProperties',
  5. '../Core/destroyObject',
  6. '../Core/DeveloperError',
  7. '../Core/RuntimeError',
  8. './AutomaticUniforms',
  9. './Uniform',
  10. './UniformArray'
  11. ], function(
  12. defined,
  13. defineProperties,
  14. destroyObject,
  15. DeveloperError,
  16. RuntimeError,
  17. AutomaticUniforms,
  18. Uniform,
  19. UniformArray) {
  20. "use strict";
  21. /*global console*/
  22. var nextShaderProgramId = 0;
  23. /**
  24. * @private
  25. */
  26. var ShaderProgram = function(options) {
  27. this._gl = options.gl;
  28. this._logShaderCompilation = options.logShaderCompilation;
  29. this._debugShaders = options.debugShaders;
  30. this._attributeLocations = options.attributeLocations;
  31. this._program = undefined;
  32. this._numberOfVertexAttributes = undefined;
  33. this._vertexAttributes = undefined;
  34. this._uniformsByName = undefined;
  35. this._uniforms = undefined;
  36. this._automaticUniforms = undefined;
  37. this._manualUniforms = undefined;
  38. this._cachedShader = undefined; // Used by ShaderCache
  39. /**
  40. * @private
  41. */
  42. this.maximumTextureUnitIndex = undefined;
  43. this._vertexShaderSource = options.vertexShaderSource;
  44. this._vertexShaderText = options.vertexShaderText;
  45. this._fragmentShaderSource = options.fragmentShaderSource;
  46. this._fragmentShaderText = options.fragmentShaderText;
  47. /**
  48. * @private
  49. */
  50. this.id = nextShaderProgramId++;
  51. };
  52. defineProperties(ShaderProgram.prototype, {
  53. /**
  54. * GLSL source for the shader program's vertex shader.
  55. * @memberof ShaderProgram.prototype
  56. *
  57. * @type {ShaderSource}
  58. * @readonly
  59. */
  60. vertexShaderSource : {
  61. get : function() {
  62. return this._vertexShaderSource;
  63. }
  64. },
  65. /**
  66. * GLSL source for the shader program's fragment shader.
  67. * @memberof ShaderProgram.prototype
  68. *
  69. * @type {ShaderSource}
  70. * @readonly
  71. */
  72. fragmentShaderSource : {
  73. get : function() {
  74. return this._fragmentShaderSource;
  75. }
  76. },
  77. vertexAttributes : {
  78. get : function() {
  79. initialize(this);
  80. return this._vertexAttributes;
  81. }
  82. },
  83. numberOfVertexAttributes : {
  84. get : function() {
  85. initialize(this);
  86. return this._numberOfVertexAttributes;
  87. }
  88. },
  89. allUniforms : {
  90. get : function() {
  91. initialize(this);
  92. return this._uniformsByName;
  93. }
  94. }
  95. });
  96. var consolePrefix = '[Cesium WebGL] ';
  97. function createAndLinkProgram(gl, shader) {
  98. var vsSource = shader._vertexShaderText;
  99. var fsSource = shader._fragmentShaderText;
  100. var vertexShader = gl.createShader(gl.VERTEX_SHADER);
  101. gl.shaderSource(vertexShader, vsSource);
  102. gl.compileShader(vertexShader);
  103. var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
  104. gl.shaderSource(fragmentShader, fsSource);
  105. gl.compileShader(fragmentShader);
  106. var program = gl.createProgram();
  107. gl.attachShader(program, vertexShader);
  108. gl.attachShader(program, fragmentShader);
  109. gl.deleteShader(vertexShader);
  110. gl.deleteShader(fragmentShader);
  111. var attributeLocations = shader._attributeLocations;
  112. if (defined(attributeLocations)) {
  113. for ( var attribute in attributeLocations) {
  114. if (attributeLocations.hasOwnProperty(attribute)) {
  115. gl.bindAttribLocation(program, attributeLocations[attribute], attribute);
  116. }
  117. }
  118. }
  119. gl.linkProgram(program);
  120. var log;
  121. if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
  122. var debugShaders = shader._debugShaders;
  123. // For performance, only check compile errors if there is a linker error.
  124. if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) {
  125. log = gl.getShaderInfoLog(fragmentShader);
  126. console.error(consolePrefix + 'Fragment shader compile log: ' + log);
  127. if (defined(debugShaders)) {
  128. var fragmentSourceTranslation = debugShaders.getTranslatedShaderSource(fragmentShader);
  129. if (fragmentSourceTranslation !== '') {
  130. console.error(consolePrefix + 'Translated fragment shader source:\n' + fragmentSourceTranslation);
  131. } else {
  132. console.error(consolePrefix + 'Fragment shader translation failed.');
  133. }
  134. }
  135. gl.deleteProgram(program);
  136. throw new RuntimeError('Fragment shader failed to compile. Compile log: ' + log);
  137. }
  138. if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) {
  139. log = gl.getShaderInfoLog(vertexShader);
  140. console.error(consolePrefix + 'Vertex shader compile log: ' + log);
  141. if (defined(debugShaders)) {
  142. var vertexSourceTranslation = debugShaders.getTranslatedShaderSource(vertexShader);
  143. if (vertexSourceTranslation !== '') {
  144. console.error(consolePrefix + 'Translated vertex shader source:\n' + vertexSourceTranslation);
  145. } else {
  146. console.error(consolePrefix + 'Vertex shader translation failed.');
  147. }
  148. }
  149. gl.deleteProgram(program);
  150. throw new RuntimeError('Vertex shader failed to compile. Compile log: ' + log);
  151. }
  152. log = gl.getProgramInfoLog(program);
  153. console.error(consolePrefix + 'Shader program link log: ' + log);
  154. if (defined(debugShaders)) {
  155. console.error(consolePrefix + 'Translated vertex shader source:\n' + debugShaders.getTranslatedShaderSource(vertexShader));
  156. console.error(consolePrefix + 'Translated fragment shader source:\n' + debugShaders.getTranslatedShaderSource(fragmentShader));
  157. }
  158. gl.deleteProgram(program);
  159. throw new RuntimeError('Program failed to link. Link log: ' + log);
  160. }
  161. var logShaderCompilation = shader._logShaderCompilation;
  162. if (logShaderCompilation) {
  163. log = gl.getShaderInfoLog(vertexShader);
  164. if (defined(log) && (log.length > 0)) {
  165. console.log(consolePrefix + 'Vertex shader compile log: ' + log);
  166. }
  167. }
  168. if (logShaderCompilation) {
  169. log = gl.getShaderInfoLog(fragmentShader);
  170. if (defined(log) && (log.length > 0)) {
  171. console.log(consolePrefix + 'Fragment shader compile log: ' + log);
  172. }
  173. }
  174. if (logShaderCompilation) {
  175. log = gl.getProgramInfoLog(program);
  176. if (defined(log) && (log.length > 0)) {
  177. console.log(consolePrefix + 'Shader program link log: ' + log);
  178. }
  179. }
  180. return program;
  181. }
  182. function findVertexAttributes(gl, program, numberOfAttributes) {
  183. var attributes = {};
  184. for (var i = 0; i < numberOfAttributes; ++i) {
  185. var attr = gl.getActiveAttrib(program, i);
  186. var location = gl.getAttribLocation(program, attr.name);
  187. attributes[attr.name] = {
  188. name : attr.name,
  189. type : attr.type,
  190. index : location
  191. };
  192. }
  193. return attributes;
  194. }
  195. function findUniforms(gl, program) {
  196. var uniformsByName = {};
  197. var uniforms = [];
  198. var samplerUniforms = [];
  199. var numberOfUniforms = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS);
  200. for (var i = 0; i < numberOfUniforms; ++i) {
  201. var activeUniform = gl.getActiveUniform(program, i);
  202. var suffix = '[0]';
  203. var uniformName = activeUniform.name.indexOf(suffix, activeUniform.name.length - suffix.length) !== -1 ? activeUniform.name.slice(0, activeUniform.name.length - 3) : activeUniform.name;
  204. // Ignore GLSL built-in uniforms returned in Firefox.
  205. if (uniformName.indexOf('gl_') !== 0) {
  206. if (activeUniform.name.indexOf('[') < 0) {
  207. // Single uniform
  208. var location = gl.getUniformLocation(program, uniformName);
  209. // IE 11.0.9 needs this check since getUniformLocation can return null
  210. // if the uniform is not active (e.g., it is optimized out). Looks like
  211. // getActiveUniform() above returns uniforms that are not actually active.
  212. if (location !== null) {
  213. var uniform = new Uniform(gl, activeUniform, uniformName, location);
  214. uniformsByName[uniformName] = uniform;
  215. uniforms.push(uniform);
  216. if (uniform._setSampler) {
  217. samplerUniforms.push(uniform);
  218. }
  219. }
  220. } else {
  221. // Uniform array
  222. var uniformArray;
  223. var locations;
  224. var value;
  225. var loc;
  226. // On some platforms - Nexus 4 in Firefox for one - an array of sampler2D ends up being represented
  227. // as separate uniforms, one for each array element. Check for and handle that case.
  228. var indexOfBracket = uniformName.indexOf('[');
  229. if (indexOfBracket >= 0) {
  230. // We're assuming the array elements show up in numerical order - it seems to be true.
  231. uniformArray = uniformsByName[uniformName.slice(0, indexOfBracket)];
  232. // Nexus 4 with Android 4.3 needs this check, because it reports a uniform
  233. // with the strange name webgl_3467e0265d05c3c1[1] in our globe surface shader.
  234. if (!defined(uniformArray)) {
  235. continue;
  236. }
  237. locations = uniformArray._locations;
  238. // On the Nexus 4 in Chrome, we get one uniform per sampler, just like in Firefox,
  239. // but the size is not 1 like it is in Firefox. So if we push locations here,
  240. // we'll end up adding too many locations.
  241. if (locations.length <= 1) {
  242. value = uniformArray.value;
  243. loc = gl.getUniformLocation(program, uniformName);
  244. // Workaround for IE 11.0.9. See above.
  245. if (loc !== null) {
  246. locations.push(loc);
  247. value.push(gl.getUniform(program, loc));
  248. }
  249. }
  250. } else {
  251. locations = [];
  252. for (var j = 0; j < activeUniform.size; ++j) {
  253. loc = gl.getUniformLocation(program, uniformName + '[' + j + ']');
  254. // Workaround for IE 11.0.9. See above.
  255. if (loc !== null) {
  256. locations.push(loc);
  257. }
  258. }
  259. uniformArray = new UniformArray(gl, activeUniform, uniformName, locations);
  260. uniformsByName[uniformName] = uniformArray;
  261. uniforms.push(uniformArray);
  262. if (uniformArray._setSampler) {
  263. samplerUniforms.push(uniformArray);
  264. }
  265. }
  266. }
  267. }
  268. }
  269. return {
  270. uniformsByName : uniformsByName,
  271. uniforms : uniforms,
  272. samplerUniforms : samplerUniforms
  273. };
  274. }
  275. function partitionUniforms(uniforms) {
  276. var automaticUniforms = [];
  277. var manualUniforms = [];
  278. for ( var uniform in uniforms) {
  279. if (uniforms.hasOwnProperty(uniform)) {
  280. var automaticUniform = AutomaticUniforms[uniform];
  281. if (automaticUniform) {
  282. automaticUniforms.push({
  283. uniform : uniforms[uniform],
  284. automaticUniform : automaticUniform
  285. });
  286. } else {
  287. manualUniforms.push(uniforms[uniform]);
  288. }
  289. }
  290. }
  291. return {
  292. automaticUniforms : automaticUniforms,
  293. manualUniforms : manualUniforms
  294. };
  295. }
  296. function setSamplerUniforms(gl, program, samplerUniforms) {
  297. gl.useProgram(program);
  298. var textureUnitIndex = 0;
  299. var length = samplerUniforms.length;
  300. for (var i = 0; i < length; ++i) {
  301. textureUnitIndex = samplerUniforms[i]._setSampler(textureUnitIndex);
  302. }
  303. gl.useProgram(null);
  304. return textureUnitIndex;
  305. }
  306. function initialize(shader) {
  307. if (defined(shader._program)) {
  308. return;
  309. }
  310. var gl = shader._gl;
  311. var program = createAndLinkProgram(gl, shader, shader._debugShaders);
  312. var numberOfVertexAttributes = gl.getProgramParameter(program, gl.ACTIVE_ATTRIBUTES);
  313. var uniforms = findUniforms(gl, program);
  314. var partitionedUniforms = partitionUniforms(uniforms.uniformsByName);
  315. shader._program = program;
  316. shader._numberOfVertexAttributes = numberOfVertexAttributes;
  317. shader._vertexAttributes = findVertexAttributes(gl, program, numberOfVertexAttributes);
  318. shader._uniformsByName = uniforms.uniformsByName;
  319. shader._uniforms = uniforms.uniforms;
  320. shader._automaticUniforms = partitionedUniforms.automaticUniforms;
  321. shader._manualUniforms = partitionedUniforms.manualUniforms;
  322. shader.maximumTextureUnitIndex = setSamplerUniforms(gl, program, uniforms.samplerUniforms);
  323. }
  324. ShaderProgram.prototype._bind = function() {
  325. initialize(this);
  326. this._gl.useProgram(this._program);
  327. };
  328. ShaderProgram.prototype._setUniforms = function(uniformMap, uniformState, validate) {
  329. var len;
  330. var i;
  331. if (defined(uniformMap)) {
  332. var manualUniforms = this._manualUniforms;
  333. len = manualUniforms.length;
  334. for (i = 0; i < len; ++i) {
  335. var mu = manualUniforms[i];
  336. mu.value = uniformMap[mu.name]();
  337. }
  338. }
  339. var automaticUniforms = this._automaticUniforms;
  340. len = automaticUniforms.length;
  341. for (i = 0; i < len; ++i) {
  342. var au = automaticUniforms[i];
  343. au.uniform.value = au.automaticUniform.getValue(uniformState);
  344. }
  345. ///////////////////////////////////////////////////////////////////
  346. // It appears that assigning the uniform values above and then setting them here
  347. // (which makes the GL calls) is faster than removing this loop and making
  348. // the GL calls above. I suspect this is because each GL call pollutes the
  349. // L2 cache making our JavaScript and the browser/driver ping-pong cache lines.
  350. var uniforms = this._uniforms;
  351. len = uniforms.length;
  352. for (i = 0; i < len; ++i) {
  353. uniforms[i]._set();
  354. }
  355. if (validate) {
  356. var gl = this._gl;
  357. var program = this._program;
  358. gl.validateProgram(program);
  359. if (!gl.getProgramParameter(program, gl.VALIDATE_STATUS)) {
  360. throw new DeveloperError('Program validation failed. Program info log: ' + gl.getProgramInfoLog(program));
  361. }
  362. }
  363. };
  364. ShaderProgram.prototype.isDestroyed = function() {
  365. return false;
  366. };
  367. ShaderProgram.prototype.destroy = function() {
  368. this._cachedShader.cache.releaseShaderProgram(this);
  369. return undefined;
  370. };
  371. ShaderProgram.prototype.finalDestroy = function() {
  372. this._gl.deleteProgram(this._program);
  373. return destroyObject(this);
  374. };
  375. return ShaderProgram;
  376. });