SCWindow.Project.Shaders.NonGraph.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  1. /// <summary>
  2. /// Shader Control - (C) Copyright 2016-2022 Ramiro Oliva (Kronnect)
  3. /// </summary>
  4. ///
  5. using UnityEngine;
  6. using UnityEditor;
  7. using System;
  8. using System.IO;
  9. using System.Collections.Generic;
  10. using System.Text;
  11. namespace ShaderControl {
  12. public partial class SCWindow : EditorWindow {
  13. void ScanShaderNonGraph(SCShader shader) {
  14. // Reads shader
  15. string[] shaderLines = File.ReadAllLines(shader.path);
  16. string[] separator = new string[] { " " };
  17. SCShaderPass currentPass = new SCShaderPass();
  18. SCShaderPass basePass = null;
  19. int pragmaControl = 0;
  20. int pass = -1;
  21. bool blockComment = false;
  22. SCKeywordLine keywordLine = new SCKeywordLine();
  23. for (int k = 0; k < shaderLines.Length; k++) {
  24. string line = shaderLines[k].Trim();
  25. if (line.Length == 0)
  26. continue;
  27. int lineCommentIndex = line.IndexOf("//");
  28. int blocCommentIndex = line.IndexOf("/*");
  29. int endCommentIndex = line.IndexOf("*/");
  30. if (blocCommentIndex > 0 && (lineCommentIndex > blocCommentIndex || lineCommentIndex < 0)) {
  31. blockComment = true;
  32. }
  33. if (endCommentIndex > blocCommentIndex && (lineCommentIndex > endCommentIndex || lineCommentIndex < 0)) {
  34. blockComment = false;
  35. }
  36. if (blockComment)
  37. continue;
  38. string lineUPPER = line.ToUpper();
  39. if (lineUPPER.Equals("PASS") || lineUPPER.StartsWith("PASS ")) {
  40. if (pass >= 0) {
  41. currentPass.pass = pass;
  42. if (basePass != null)
  43. currentPass.Add(basePass.keywordLines);
  44. shader.Add(currentPass);
  45. } else if (currentPass.keywordCount > 0) {
  46. basePass = currentPass;
  47. }
  48. currentPass = new SCShaderPass();
  49. pass++;
  50. continue;
  51. }
  52. int j = line.IndexOf(PRAGMA_COMMENT_MARK);
  53. if (j >= 0) {
  54. pragmaControl = 1;
  55. } else {
  56. j = line.IndexOf(PRAGMA_DISABLED_MARK);
  57. if (j >= 0)
  58. pragmaControl = 3;
  59. }
  60. if (lineCommentIndex == 0 && pragmaControl != 1 && pragmaControl != 3) {
  61. continue; // do not process lines commented by user
  62. }
  63. PragmaType pragmaType = PragmaType.Unknown;
  64. int offset = 0;
  65. j = line.IndexOf(SCKeywordLine.PRAGMA_MULTICOMPILE_GLOBAL);
  66. if (j >= 0) {
  67. pragmaType = PragmaType.MultiCompileGlobal;
  68. offset = SCKeywordLine.PRAGMA_MULTICOMPILE_GLOBAL.Length;
  69. } else {
  70. j = line.IndexOf(SCKeywordLine.PRAGMA_FEATURE_GLOBAL);
  71. if (j >= 0) {
  72. pragmaType = PragmaType.FeatureGlobal;
  73. offset = SCKeywordLine.PRAGMA_FEATURE_GLOBAL.Length;
  74. } else {
  75. j = line.IndexOf(SCKeywordLine.PRAGMA_MULTICOMPILE_LOCAL);
  76. if (j >= 0) {
  77. pragmaType = PragmaType.MultiCompileLocal;
  78. offset = SCKeywordLine.PRAGMA_MULTICOMPILE_LOCAL.Length;
  79. } else {
  80. j = line.IndexOf(SCKeywordLine.PRAGMA_FEATURE_LOCAL);
  81. if (j >= 0) {
  82. pragmaType = PragmaType.FeatureLocal;
  83. offset = SCKeywordLine.PRAGMA_FEATURE_LOCAL.Length;
  84. }
  85. }
  86. }
  87. }
  88. if (j >= 0) {
  89. if (pragmaControl != 2) {
  90. keywordLine = new SCKeywordLine();
  91. }
  92. keywordLine.pragmaType = pragmaType;
  93. // exclude potential comments inside the #pragma line
  94. int lastStringPos = line.IndexOf("//", j + offset);
  95. if (lastStringPos < 0) {
  96. lastStringPos = line.Length;
  97. }
  98. int length = lastStringPos - j - offset;
  99. string[] kk = line.Substring(j + offset, length).Split(separator, StringSplitOptions.RemoveEmptyEntries);
  100. // Sanitize keywords
  101. for (int i = 0; i < kk.Length; i++) {
  102. kk[i] = kk[i].Trim();
  103. }
  104. // Act on keywords
  105. switch (pragmaControl) {
  106. case 1: // Edited by Shader Control line
  107. shader.editedByShaderControl = true;
  108. // Add original keywords to current line
  109. for (int s = 0; s < kk.Length; s++) {
  110. keywordLine.Add(shader.GetKeyword(kk[s]), k);
  111. }
  112. pragmaControl = 2;
  113. break;
  114. case 2:
  115. // check enabled keywords
  116. keywordLine.DisableKeywords();
  117. for (int s = 0; s < kk.Length; s++) {
  118. SCKeyword keyword = keywordLine.GetKeyword(kk[s]);
  119. if (keyword != null)
  120. keyword.enabled = true;
  121. }
  122. currentPass.Add(keywordLine);
  123. pragmaControl = 0;
  124. break;
  125. case 3: // disabled by Shader Control line
  126. shader.editedByShaderControl = true;
  127. // Add original keywords to current line
  128. for (int s = 0; s < kk.Length; s++) {
  129. SCKeyword keyword = shader.GetKeyword(kk[s]);
  130. keyword.enabled = false;
  131. keywordLine.Add(keyword, k);
  132. }
  133. currentPass.Add(keywordLine);
  134. pragmaControl = 0;
  135. break;
  136. case 0:
  137. // Add keywords to current line
  138. for (int s = 0; s < kk.Length; s++) {
  139. keywordLine.Add(shader.GetKeyword(kk[s]), k);
  140. }
  141. currentPass.Add(keywordLine);
  142. break;
  143. }
  144. }
  145. }
  146. currentPass.pass = Mathf.Max(pass, 0);
  147. if (basePass != null) {
  148. currentPass.Add(basePass.keywordLines);
  149. }
  150. shader.Add(currentPass);
  151. shader.UpdateVariantCount();
  152. }
  153. void UpdateShaderNonGraph(SCShader shader) {
  154. // Reads and updates shader from disk
  155. string[] shaderLines = File.ReadAllLines(shader.path);
  156. string[] separator = new string[] { " " };
  157. StringBuilder sb = new StringBuilder();
  158. int pragmaControl = 0;
  159. shader.editedByShaderControl = false;
  160. SCKeywordLine keywordLine = new SCKeywordLine();
  161. bool blockComment = false;
  162. for (int k = 0; k < shaderLines.Length; k++) {
  163. int lineCommentIndex = shaderLines[k].IndexOf("//");
  164. int blocCommentIndex = shaderLines[k].IndexOf("/*");
  165. int endCommentIndex = shaderLines[k].IndexOf("*/");
  166. if (blocCommentIndex > 0 && (lineCommentIndex > blocCommentIndex || lineCommentIndex < 0)) {
  167. blockComment = true;
  168. }
  169. if (endCommentIndex > blocCommentIndex && (lineCommentIndex > endCommentIndex || lineCommentIndex < 0)) {
  170. blockComment = false;
  171. }
  172. int j = -1;
  173. PragmaType pragmaType = PragmaType.Unknown;
  174. if (!blockComment) {
  175. j = shaderLines[k].IndexOf(PRAGMA_COMMENT_MARK);
  176. if (j >= 0) {
  177. pragmaControl = 1;
  178. }
  179. j = shaderLines[k].IndexOf(SCKeywordLine.PRAGMA_MULTICOMPILE_GLOBAL);
  180. if (j >= 0) {
  181. pragmaType = PragmaType.MultiCompileGlobal;
  182. } else {
  183. j = shaderLines[k].IndexOf(SCKeywordLine.PRAGMA_FEATURE_GLOBAL);
  184. if (j >= 0) {
  185. pragmaType = PragmaType.FeatureGlobal;
  186. } else {
  187. j = shaderLines[k].IndexOf(SCKeywordLine.PRAGMA_MULTICOMPILE_LOCAL);
  188. if (j >= 0) {
  189. pragmaType = PragmaType.MultiCompileLocal;
  190. } else {
  191. j = shaderLines[k].IndexOf(SCKeywordLine.PRAGMA_FEATURE_LOCAL);
  192. if (j >= 0) {
  193. pragmaType = PragmaType.FeatureLocal;
  194. }
  195. }
  196. }
  197. }
  198. if (pragmaControl != 1 && lineCommentIndex == 0 && shaderLines[k].IndexOf(PRAGMA_DISABLED_MARK) < 0) {
  199. // do not process a commented line
  200. j = -1;
  201. }
  202. }
  203. if (j >= 0) {
  204. if (pragmaControl != 2) {
  205. keywordLine.Clear();
  206. }
  207. keywordLine.pragmaType = pragmaType;
  208. j = shaderLines[k].IndexOf(' ', j + 20) + 1; // first space after pragma declaration
  209. if (j >= shaderLines[k].Length) continue;
  210. // exclude potential comments inside the #pragma line
  211. int lastStringPos = shaderLines[k].IndexOf("//", j);
  212. if (lastStringPos < 0) {
  213. lastStringPos = shaderLines[k].Length;
  214. }
  215. int length = lastStringPos - j;
  216. string[] kk = shaderLines[k].Substring(j, length).Split(separator, StringSplitOptions.RemoveEmptyEntries);
  217. // Sanitize keywords
  218. for (int i = 0; i < kk.Length; i++) {
  219. kk[i] = kk[i].Trim();
  220. }
  221. // Act on keywords
  222. switch (pragmaControl) {
  223. case 1:
  224. // Read original keywords
  225. for (int s = 0; s < kk.Length; s++) {
  226. SCKeyword keyword = shader.GetKeyword(kk[s]);
  227. keywordLine.Add(keyword);
  228. }
  229. pragmaControl = 2;
  230. break;
  231. case 0:
  232. case 2:
  233. if (pragmaControl == 0) {
  234. for (int s = 0; s < kk.Length; s++) {
  235. SCKeyword keyword = shader.GetKeyword(kk[s]);
  236. keywordLine.Add(keyword);
  237. }
  238. }
  239. int kCount = keywordLine.keywordCount;
  240. int kEnabledCount = keywordLine.keywordsEnabledCount;
  241. if (kEnabledCount < kCount) {
  242. // write original keywords
  243. if (kEnabledCount == 0) {
  244. sb.Append(PRAGMA_DISABLED_MARK);
  245. } else {
  246. sb.Append(PRAGMA_COMMENT_MARK);
  247. }
  248. shader.editedByShaderControl = true;
  249. sb.Append(keywordLine.GetPragma());
  250. if (keywordLine.hasUnderscoreVariant)
  251. sb.Append(PRAGMA_UNDERSCORE);
  252. for (int s = 0; s < kCount; s++) {
  253. SCKeyword keyword = keywordLine.keywords[s];
  254. sb.Append(keyword.name);
  255. if (s < kCount - 1)
  256. sb.Append(" ");
  257. }
  258. sb.AppendLine();
  259. }
  260. if (kEnabledCount > 0) {
  261. // Write actual keywords
  262. sb.Append(keywordLine.GetPragma());
  263. if (keywordLine.hasUnderscoreVariant)
  264. sb.Append(PRAGMA_UNDERSCORE);
  265. for (int s = 0; s < kCount; s++) {
  266. SCKeyword keyword = keywordLine.keywords[s];
  267. if (keyword.enabled) {
  268. sb.Append(keyword.name);
  269. if (s < kCount - 1)
  270. sb.Append(" ");
  271. }
  272. }
  273. sb.AppendLine();
  274. }
  275. pragmaControl = 0;
  276. break;
  277. }
  278. } else {
  279. sb.AppendLine(shaderLines[k]);
  280. }
  281. }
  282. // Writes modified shader
  283. File.WriteAllText(shader.path, sb.ToString());
  284. AssetDatabase.Refresh();
  285. }
  286. void ConvertToLocalNonGraph(SCKeyword keyword, SCShader shader) {
  287. string path = shader.path;
  288. if (!File.Exists(path)) return;
  289. string[] lines = File.ReadAllLines(path);
  290. bool changed = false;
  291. for (int k = 0; k < lines.Length; k++) {
  292. // Just convert to local shader_features for now since multi_compile global keywords can be nabled using the Shader global API
  293. if (lines[k].IndexOf(SCKeywordLine.PRAGMA_FEATURE_GLOBAL, StringComparison.InvariantCultureIgnoreCase) >= 0 && lines[k].IndexOf(keyword.name, StringComparison.InvariantCultureIgnoreCase) >= 0) {
  294. lines[k] = lines[k].Replace(SCKeywordLine.PRAGMA_FEATURE_GLOBAL, SCKeywordLine.PRAGMA_FEATURE_LOCAL);
  295. lines[k] = lines[k].Replace(SCKeywordLine.PRAGMA_FEATURE_GLOBAL.ToUpper(), SCKeywordLine.PRAGMA_FEATURE_LOCAL);
  296. changed = true;
  297. }
  298. }
  299. if (changed) {
  300. MakeBackup(shader);
  301. File.WriteAllLines(path, lines, Encoding.UTF8);
  302. }
  303. }
  304. }
  305. }