modernizeShader.js 7.0 KB

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