modernizeShader.js 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. import defined from '../Core/defined.js';
  2. import DeveloperError from '../Core/DeveloperError.js';
  3. /**
  4. * A function to port GLSL shaders from GLSL ES 1.00 to GLSL ES 3.00
  5. *
  6. * This function is nowhere near comprehensive or complete. It just
  7. * handles some common cases.
  8. *
  9. * Note that this function requires the presence of the
  10. * "#define OUTPUT_DECLARATION" line that is appended
  11. * by ShaderSource.
  12. *
  13. * @private
  14. */
  15. function modernizeShader(source, isFragmentShader) {
  16. var outputDeclarationRegex = /#define OUTPUT_DECLARATION/;
  17. var splitSource = source.split('\n');
  18. if (/#version 300 es/g.test(source)) {
  19. return source;
  20. }
  21. var outputDeclarationLine = -1;
  22. var i, line;
  23. for (i = 0; i < splitSource.length; ++i) {
  24. line = splitSource[i];
  25. if (outputDeclarationRegex.test(line)) {
  26. outputDeclarationLine = i;
  27. break;
  28. }
  29. }
  30. if (outputDeclarationLine === -1) {
  31. throw new DeveloperError('Could not find a #define OUTPUT_DECLARATION!');
  32. }
  33. var outputVariables = [];
  34. for (i = 0; i < 10; i++) {
  35. var fragDataString = 'gl_FragData\\[' + i + '\\]';
  36. var newOutput = 'czm_out' + i;
  37. var regex = new RegExp(fragDataString, 'g');
  38. if (regex.test(source)) {
  39. setAdd(newOutput, outputVariables);
  40. replaceInSourceString(fragDataString, newOutput, splitSource);
  41. splitSource.splice(outputDeclarationLine, 0, 'layout(location = ' + i + ') out vec4 ' + newOutput + ';');
  42. outputDeclarationLine += 1;
  43. }
  44. }
  45. var czmFragColor = 'czm_fragColor';
  46. if (findInSource('gl_FragColor', splitSource)) {
  47. setAdd(czmFragColor, outputVariables);
  48. replaceInSourceString('gl_FragColor', czmFragColor, splitSource);
  49. splitSource.splice(outputDeclarationLine, 0, 'layout(location = 0) out vec4 czm_fragColor;');
  50. outputDeclarationLine += 1;
  51. }
  52. var variableMap = getVariablePreprocessorBranch(outputVariables, splitSource);
  53. var lineAdds = {};
  54. for (i = 0; i < splitSource.length; i++) {
  55. line = splitSource[i];
  56. for (var variable in variableMap) {
  57. if (variableMap.hasOwnProperty(variable)) {
  58. var matchVar = new RegExp('(layout)[^]+(out)[^]+(' + variable + ')[^]+', 'g');
  59. if (matchVar.test(line)) {
  60. lineAdds[line] = variable;
  61. }
  62. }
  63. }
  64. }
  65. for (var layoutDeclaration in lineAdds) {
  66. if (lineAdds.hasOwnProperty(layoutDeclaration)) {
  67. var variableName = lineAdds[layoutDeclaration];
  68. var lineNumber = splitSource.indexOf(layoutDeclaration);
  69. var entry = variableMap[variableName];
  70. var depth = entry.length;
  71. var d;
  72. for (d = 0; d < depth; d++) {
  73. splitSource.splice(lineNumber, 0, entry[d]);
  74. }
  75. lineNumber += depth + 1;
  76. for (d = depth - 1; d >= 0; d--) {
  77. splitSource.splice(lineNumber, 0, '#endif //' + entry[d]);
  78. }
  79. }
  80. }
  81. var versionThree = '#version 300 es';
  82. var foundVersion = false;
  83. for (i = 0; i < splitSource.length; i++) {
  84. if (/#version/.test(splitSource[i])) {
  85. splitSource[i] = versionThree;
  86. foundVersion = true;
  87. }
  88. }
  89. if (!foundVersion) {
  90. splitSource.splice(0, 0, versionThree);
  91. }
  92. removeExtension('EXT_draw_buffers', splitSource);
  93. removeExtension('EXT_frag_depth', splitSource);
  94. replaceInSourceString('texture2D', 'texture', splitSource);
  95. replaceInSourceString('texture3D', 'texture', splitSource);
  96. replaceInSourceString('textureCube', 'texture', splitSource);
  97. replaceInSourceString('gl_FragDepthEXT', 'gl_FragDepth', splitSource);
  98. if (isFragmentShader) {
  99. replaceInSourceString('varying', 'in', splitSource);
  100. } else {
  101. replaceInSourceString('attribute', 'in', splitSource);
  102. replaceInSourceString('varying', 'out', splitSource);
  103. }
  104. return compileSource(splitSource);
  105. }
  106. // Note that this fails if your string looks like
  107. // searchString[singleCharacter]searchString
  108. function replaceInSourceString(str, replacement, splitSource) {
  109. var regexStr = '(^|[^\\w])(' + str + ')($|[^\\w])';
  110. var regex = new RegExp(regexStr, 'g');
  111. var splitSourceLength = splitSource.length;
  112. for (var i = 0; i < splitSourceLength; ++i) {
  113. var line = splitSource[i];
  114. splitSource[i] = line.replace(regex, '$1' + replacement + '$3');
  115. }
  116. }
  117. function replaceInSourceRegex(regex, replacement, splitSource) {
  118. var splitSourceLength = splitSource.length;
  119. for (var i = 0; i < splitSourceLength; ++i) {
  120. var line = splitSource[i];
  121. splitSource[i] = line.replace(regex, replacement);
  122. }
  123. }
  124. function findInSource(str, splitSource) {
  125. var regexStr = '(^|[^\\w])(' + str + ')($|[^\\w])';
  126. var regex = new RegExp(regexStr, 'g');
  127. var splitSourceLength = splitSource.length;
  128. for (var i = 0; i < splitSourceLength; ++i) {
  129. var line = splitSource[i];
  130. if (regex.test(line)) {
  131. return true;
  132. }
  133. }
  134. return false;
  135. }
  136. function compileSource(splitSource) {
  137. var wholeSource = '';
  138. var splitSourceLength = splitSource.length;
  139. for (var i = 0; i < splitSourceLength; ++i) {
  140. wholeSource += splitSource[i] + '\n';
  141. }
  142. return wholeSource;
  143. }
  144. function setAdd(variable, set) {
  145. if (set.indexOf(variable) === -1) {
  146. set.push(variable);
  147. }
  148. }
  149. function getVariablePreprocessorBranch(layoutVariables, splitSource) {
  150. var variableMap = {};
  151. var numLayoutVariables = layoutVariables.length;
  152. var stack = [];
  153. for (var i = 0; i < splitSource.length; ++i) {
  154. var line = splitSource[i];
  155. var hasIF = /(#ifdef|#if)/g.test(line);
  156. var hasELSE = /#else/g.test(line);
  157. var hasENDIF = /#endif/g.test(line);
  158. if (hasIF) {
  159. stack.push(line);
  160. } else if (hasELSE) {
  161. var top = stack[stack.length - 1];
  162. var op = top.replace('ifdef', 'ifndef');
  163. if (/if/g.test(op)) {
  164. op = op.replace(/(#if\s+)(\S*)([^]*)/, '$1!($2)$3');
  165. }
  166. stack.pop();
  167. stack.push(op);
  168. } else if (hasENDIF) {
  169. stack.pop();
  170. } else if (!/layout/g.test(line)) {
  171. for (var varIndex = 0; varIndex < numLayoutVariables; ++varIndex) {
  172. var varName = layoutVariables[varIndex];
  173. if (line.indexOf(varName) !== -1) {
  174. if (!defined(variableMap[varName])) {
  175. variableMap[varName] = stack.slice();
  176. } else {
  177. variableMap[varName] = variableMap[varName].filter(function(x) {
  178. return stack.indexOf(x) >= 0;
  179. });
  180. }
  181. }
  182. }
  183. }
  184. }
  185. return variableMap;
  186. }
  187. function removeExtension(name, splitSource) {
  188. var regex = '#extension\\s+GL_' + name + '\\s+:\\s+[a-zA-Z0-9]+\\s*$';
  189. replaceInSourceRegex(new RegExp(regex, 'g'), '', splitSource);
  190. }
  191. export default modernizeShader;