modernizeShader.js 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  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(
  42. outputDeclarationLine,
  43. 0,
  44. "layout(location = " + i + ") out vec4 " + newOutput + ";"
  45. );
  46. outputDeclarationLine += 1;
  47. }
  48. }
  49. var czmFragColor = "czm_fragColor";
  50. if (findInSource("gl_FragColor", splitSource)) {
  51. setAdd(czmFragColor, outputVariables);
  52. replaceInSourceString("gl_FragColor", czmFragColor, splitSource);
  53. splitSource.splice(
  54. outputDeclarationLine,
  55. 0,
  56. "layout(location = 0) out vec4 czm_fragColor;"
  57. );
  58. outputDeclarationLine += 1;
  59. }
  60. var variableMap = getVariablePreprocessorBranch(outputVariables, splitSource);
  61. var lineAdds = {};
  62. for (i = 0; i < splitSource.length; i++) {
  63. line = splitSource[i];
  64. for (var variable in variableMap) {
  65. if (variableMap.hasOwnProperty(variable)) {
  66. var matchVar = new RegExp(
  67. "(layout)[^]+(out)[^]+(" + variable + ")[^]+",
  68. "g"
  69. );
  70. if (matchVar.test(line)) {
  71. lineAdds[line] = variable;
  72. }
  73. }
  74. }
  75. }
  76. for (var layoutDeclaration in lineAdds) {
  77. if (lineAdds.hasOwnProperty(layoutDeclaration)) {
  78. var variableName = lineAdds[layoutDeclaration];
  79. var lineNumber = splitSource.indexOf(layoutDeclaration);
  80. var entry = variableMap[variableName];
  81. var depth = entry.length;
  82. var d;
  83. for (d = 0; d < depth; d++) {
  84. splitSource.splice(lineNumber, 0, entry[d]);
  85. }
  86. lineNumber += depth + 1;
  87. for (d = depth - 1; d >= 0; d--) {
  88. splitSource.splice(lineNumber, 0, "#endif //" + entry[d]);
  89. }
  90. }
  91. }
  92. var webgl2UniqueID = "WEBGL_2";
  93. var webgl2DefineMacro = "#define " + webgl2UniqueID;
  94. var versionThree = "#version 300 es";
  95. var foundVersion = false;
  96. for (i = 0; i < splitSource.length; i++) {
  97. if (/#version/.test(splitSource[i])) {
  98. splitSource[i] = versionThree;
  99. foundVersion = true;
  100. break;
  101. }
  102. }
  103. if (!foundVersion) {
  104. splitSource.splice(0, 0, versionThree);
  105. }
  106. splitSource.splice(1, 0, webgl2DefineMacro);
  107. removeExtension("EXT_draw_buffers", webgl2UniqueID, splitSource);
  108. removeExtension("EXT_frag_depth", webgl2UniqueID, splitSource);
  109. removeExtension("OES_standard_derivatives", webgl2UniqueID, splitSource);
  110. replaceInSourceString("texture2D", "texture", splitSource);
  111. replaceInSourceString("texture3D", "texture", splitSource);
  112. replaceInSourceString("textureCube", "texture", splitSource);
  113. replaceInSourceString("gl_FragDepthEXT", "gl_FragDepth", splitSource);
  114. if (isFragmentShader) {
  115. replaceInSourceString("varying", "in", splitSource);
  116. } else {
  117. replaceInSourceString("attribute", "in", splitSource);
  118. replaceInSourceString("varying", "out", splitSource);
  119. }
  120. return compileSource(splitSource);
  121. }
  122. // Note that this fails if your string looks like
  123. // searchString[singleCharacter]searchString
  124. function replaceInSourceString(str, replacement, 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. splitSource[i] = line.replace(regex, "$1" + replacement + "$3");
  131. }
  132. }
  133. function replaceInSourceRegex(regex, replacement, splitSource) {
  134. var splitSourceLength = splitSource.length;
  135. for (var i = 0; i < splitSourceLength; ++i) {
  136. var line = splitSource[i];
  137. splitSource[i] = line.replace(regex, replacement);
  138. }
  139. }
  140. function findInSource(str, splitSource) {
  141. var regexStr = "(^|[^\\w])(" + str + ")($|[^\\w])";
  142. var regex = new RegExp(regexStr, "g");
  143. var splitSourceLength = splitSource.length;
  144. for (var i = 0; i < splitSourceLength; ++i) {
  145. var line = splitSource[i];
  146. if (regex.test(line)) {
  147. return true;
  148. }
  149. }
  150. return false;
  151. }
  152. function compileSource(splitSource) {
  153. var wholeSource = "";
  154. var splitSourceLength = splitSource.length;
  155. for (var i = 0; i < splitSourceLength; ++i) {
  156. wholeSource += splitSource[i] + "\n";
  157. }
  158. return wholeSource;
  159. }
  160. function setAdd(variable, set) {
  161. if (set.indexOf(variable) === -1) {
  162. set.push(variable);
  163. }
  164. }
  165. function getVariablePreprocessorBranch(layoutVariables, splitSource) {
  166. var variableMap = {};
  167. var numLayoutVariables = layoutVariables.length;
  168. var stack = [];
  169. for (var i = 0; i < splitSource.length; ++i) {
  170. var line = splitSource[i];
  171. var hasIF = /(#ifdef|#if)/g.test(line);
  172. var hasELSE = /#else/g.test(line);
  173. var hasENDIF = /#endif/g.test(line);
  174. if (hasIF) {
  175. stack.push(line);
  176. } else if (hasELSE) {
  177. var top = stack[stack.length - 1];
  178. var op = top.replace("ifdef", "ifndef");
  179. if (/if/g.test(op)) {
  180. op = op.replace(/(#if\s+)(\S*)([^]*)/, "$1!($2)$3");
  181. }
  182. stack.pop();
  183. stack.push(op);
  184. } else if (hasENDIF) {
  185. stack.pop();
  186. } else if (!/layout/g.test(line)) {
  187. for (var varIndex = 0; varIndex < numLayoutVariables; ++varIndex) {
  188. var varName = layoutVariables[varIndex];
  189. if (line.indexOf(varName) !== -1) {
  190. if (!defined(variableMap[varName])) {
  191. variableMap[varName] = stack.slice();
  192. } else {
  193. variableMap[varName] = variableMap[varName].filter(function (x) {
  194. return stack.indexOf(x) >= 0;
  195. });
  196. }
  197. }
  198. }
  199. }
  200. }
  201. return variableMap;
  202. }
  203. function removeExtension(name, webgl2UniqueID, splitSource) {
  204. var regex = "#extension\\s+GL_" + name + "\\s+:\\s+[a-zA-Z0-9]+\\s*$";
  205. replaceInSourceRegex(new RegExp(regex, "g"), "", splitSource);
  206. // replace any possible directive #ifdef (GL_EXT_extension) with WEBGL_2 unique directive
  207. replaceInSourceString("GL_" + name, webgl2UniqueID, splitSource);
  208. }
  209. export default modernizeShader;