GLShaderProcessor.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533
  1. //
  2. // Urho3D Engine
  3. // Copyright (c) 2008-2011 Lasse Öörni
  4. //
  5. // Permission is hereby granted, free of charge, to any person obtaining a copy
  6. // of this software and associated documentation files (the "Software"), to deal
  7. // in the Software without restriction, including without limitation the rights
  8. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. // copies of the Software, and to permit persons to whom the Software is
  10. // furnished to do so, subject to the following conditions:
  11. //
  12. // The above copyright notice and this permission notice shall be included in
  13. // all copies or substantial portions of the Software.
  14. //
  15. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  21. // THE SOFTWARE.
  22. //
  23. #include "Context.h"
  24. #include "File.h"
  25. #include "FileSystem.h"
  26. #include "List.h"
  27. #include "Map.h"
  28. #include "Mutex.h"
  29. #include "ProcessUtils.h"
  30. #include "Set.h"
  31. #include "StringUtils.h"
  32. #include "Thread.h"
  33. #include "XMLFile.h"
  34. #include <cstdio>
  35. #include <cstring>
  36. #include "DebugNew.h"
  37. enum ShaderType
  38. {
  39. VS = 0,
  40. PS,
  41. Both
  42. };
  43. struct Parameter
  44. {
  45. Parameter()
  46. {
  47. }
  48. Parameter(const String& name, unsigned index) :
  49. name_(name),
  50. index_(index)
  51. {
  52. }
  53. bool operator < (const Parameter& rhs) const
  54. {
  55. if (index_ != rhs.index_)
  56. return index_ < rhs.index_;
  57. else
  58. return name_ < rhs.name_;
  59. }
  60. bool operator > (const Parameter& rhs) const
  61. {
  62. if (index_ != rhs.index_)
  63. return index_ > rhs.index_;
  64. else
  65. return name_ > rhs.name_;
  66. }
  67. bool operator == (const Parameter& rhs) const { return index_ == rhs.index_ && name_ == rhs.name_; }
  68. bool operator != (const Parameter& rhs) const { return index_ != rhs.index_ || name_ != rhs.name_; }
  69. String name_;
  70. unsigned index_;
  71. };
  72. struct Variation
  73. {
  74. Variation()
  75. {
  76. }
  77. Variation(const String& name, bool isOption) :
  78. name_(name),
  79. option_(isOption)
  80. {
  81. }
  82. String name_;
  83. Vector<String> defines_;
  84. Vector<String> excludes_;
  85. Vector<String> includes_;
  86. Vector<String> requires_;
  87. bool option_;
  88. };
  89. struct ProcessedVariation
  90. {
  91. ShaderType type_;
  92. String name_;
  93. Vector<String> defines_;
  94. };
  95. struct Shader
  96. {
  97. Shader(const String& name, ShaderType type) :
  98. name_(name),
  99. type_(type)
  100. {
  101. }
  102. String name_;
  103. ShaderType type_;
  104. Vector<Variation> variations_;
  105. };
  106. SharedPtr<Context> context_(new Context());
  107. SharedPtr<FileSystem> fileSystem_(new FileSystem(context_));
  108. String inDir_;
  109. String inFile_;
  110. String outDir_;
  111. Set<Parameter> constants_;
  112. Set<Parameter> textureUnits_;
  113. Vector<String> defines_;
  114. Vector<String> glslCode_;
  115. int main(int argc, char** argv);
  116. void Run(const Vector<String>& arguments);
  117. void ProcessVariations(const Shader& baseShader, XMLElement& shaders);
  118. int main(int argc, char** argv)
  119. {
  120. Vector<String> arguments;
  121. for (int i = 1; i < argc; ++i)
  122. {
  123. String argument(argv[i]);
  124. arguments.Push(GetInternalPath(argument));
  125. }
  126. Run(arguments);
  127. return 0;
  128. }
  129. void Run(const Vector<String>& arguments)
  130. {
  131. if (arguments.Size() < 2)
  132. {
  133. ErrorExit(
  134. "Usage: GLShaderProcessor <definitionfile> <outputpath> [define1] [define2]\n\n"
  135. "GLSL files will be loaded from definition file directory, and finalized GLSL +\n"
  136. "XML files are saved to the output path, preserving the subdirectory structure.\n"
  137. );
  138. }
  139. unsigned pos = arguments[0].FindLast('/');
  140. if (pos != String::NPOS)
  141. {
  142. inDir_ = arguments[0].Substring(0, pos);
  143. inFile_ = arguments[0].Substring(pos + 1);
  144. }
  145. else
  146. {
  147. inFile_ = arguments[0];
  148. }
  149. outDir_ = arguments[1];
  150. inDir_ = AddTrailingSlash(inDir_);
  151. outDir_ = AddTrailingSlash(outDir_);
  152. for (unsigned i = 2; i < arguments.Size(); ++i)
  153. {
  154. String arg = arguments[i].ToUpper();
  155. defines_.Push(arg);
  156. }
  157. XMLFile doc(context_);
  158. File source(context_);
  159. source.Open(arguments[0]);
  160. if (!doc.Load(source))
  161. ErrorExit("Could not open input file " + arguments[0]);
  162. XMLElement shaders = doc.GetRoot("shaders");
  163. if (!shaders)
  164. ErrorExit("No shaders element in " + source.GetName());
  165. XMLFile outDoc(context_);
  166. XMLElement outShaders = outDoc.CreateRoot("shaders");
  167. XMLElement shader = shaders.GetChild("shader");
  168. while (shader)
  169. {
  170. String source = shader.GetAttribute("name");
  171. ShaderType compileType = Both;
  172. String type = shader.GetAttribute("type");
  173. if (type == "VS" || type == "vs")
  174. compileType = VS;
  175. if (type == "PS" || type == "ps")
  176. compileType = PS;
  177. Shader baseShader(source, compileType);
  178. XMLElement variation = shader.GetChild("");
  179. while (variation)
  180. {
  181. String value = variation.GetName();
  182. if (value == "variation" || value == "option")
  183. {
  184. String name = variation.GetAttribute("name");
  185. Variation newVar(name, value == "option");
  186. String simpleDefine = variation.GetAttribute("define");
  187. if (!simpleDefine.Empty())
  188. newVar.defines_.Push(simpleDefine);
  189. String simpleExclude = variation.GetAttribute("exclude");
  190. if (!simpleExclude.Empty())
  191. newVar.excludes_.Push(simpleExclude);
  192. String simpleInclude = variation.GetAttribute("include");
  193. if (!simpleInclude.Empty())
  194. newVar.includes_.Push(simpleInclude);
  195. String simpleRequire = variation.GetAttribute("require");
  196. if (!simpleRequire.Empty())
  197. newVar.requires_.Push(simpleRequire);
  198. XMLElement define = variation.GetChild("define");
  199. while (define)
  200. {
  201. newVar.defines_.Push(define.GetAttribute("name"));
  202. define = define.GetNext("define");
  203. }
  204. XMLElement exclude = variation.GetChild("exclude");
  205. while (exclude)
  206. {
  207. newVar.excludes_.Push(exclude.GetAttribute("name"));
  208. exclude = exclude.GetNext("exclude");
  209. }
  210. XMLElement include = variation.GetChild("include");
  211. while (include)
  212. {
  213. newVar.includes_.Push(include.GetAttribute("name"));
  214. include = include.GetNext("include");
  215. }
  216. XMLElement require = variation.GetChild("require");
  217. while (require)
  218. {
  219. newVar.requires_.Push(require.GetAttribute("name"));
  220. require = require.GetNext("require");
  221. }
  222. baseShader.variations_.Push(newVar);
  223. }
  224. variation = variation.GetNext();
  225. }
  226. if (baseShader.type_ != Both)
  227. ProcessVariations(baseShader, outShaders);
  228. else
  229. {
  230. baseShader.type_ = VS;
  231. ProcessVariations(baseShader, outShaders);
  232. baseShader.type_ = PS;
  233. ProcessVariations(baseShader, outShaders);
  234. }
  235. shader = shader.GetNext("shader");
  236. }
  237. }
  238. void ProcessVariations(const Shader& baseShader, XMLElement& shaders)
  239. {
  240. constants_.Clear();
  241. textureUnits_.Clear();
  242. unsigned combinations = 1;
  243. Set<unsigned> usedCombinations;
  244. Map<String, unsigned> nameToIndex;
  245. Vector<ProcessedVariation> processedVariations;
  246. unsigned numVariationGroups = 0;
  247. const Vector<Variation>& variations = baseShader.variations_;
  248. if (variations.Size() > 32)
  249. ErrorExit("Maximum amount of variations exceeded");
  250. // Load the shader source code
  251. String inputFileName = inDir_ + baseShader.name_;
  252. if (baseShader.type_ == VS)
  253. inputFileName += ".vert";
  254. if (baseShader.type_ == PS)
  255. inputFileName += ".frag";
  256. {
  257. File glslFile(context_, inputFileName);
  258. if (!glslFile.IsOpen())
  259. ErrorExit("Could not open input file " + inputFileName);
  260. glslCode_.Clear();
  261. while (!glslFile.IsEof())
  262. glslCode_.Push(glslFile.ReadLine());
  263. }
  264. // Process the code for includes
  265. for (unsigned i = 0; i < glslCode_.Size(); ++i)
  266. {
  267. if (glslCode_[i].Find("#include") == 0)
  268. {
  269. unsigned quoteStart = glslCode_[i].Find('<');
  270. unsigned quoteEnd = glslCode_[i].FindLast('>');
  271. if (quoteStart == String::NPOS)
  272. {
  273. quoteStart = glslCode_[i].Find('\"');
  274. quoteEnd = glslCode_[i].FindLast('\"');
  275. }
  276. if (quoteStart != String::NPOS)
  277. {
  278. ++quoteStart;
  279. String includeFileName = glslCode_[i].Substring(quoteStart, quoteEnd - quoteStart);
  280. String inputFilePath = GetPath(inputFileName);
  281. while (includeFileName.Find("../") == 0 || includeFileName.Find("..\\") == 0)
  282. {
  283. includeFileName = includeFileName.Substring(3);
  284. inputFilePath = GetParentPath(inputFilePath);
  285. }
  286. includeFileName = inputFilePath + includeFileName;
  287. File glslIncludeFile(context_, includeFileName);
  288. if (!glslIncludeFile.IsOpen())
  289. ErrorExit("Could not open input file " + includeFileName);
  290. // Remove the #include line, then include the code
  291. glslCode_.Erase(i);
  292. unsigned pos = i;
  293. while (!glslIncludeFile.IsEof())
  294. {
  295. glslCode_.Insert(pos, glslIncludeFile.ReadLine());
  296. ++pos;
  297. }
  298. // Finally insert an empty line to mark the space between files
  299. glslCode_.Insert(pos, "");
  300. }
  301. }
  302. }
  303. for (unsigned i = 0; i < variations.Size(); ++i)
  304. {
  305. combinations *= 2;
  306. nameToIndex[variations[i].name_] = i;
  307. if (!variations[i].option_ && (i == 0 || variations[i - 1].option_))
  308. ++numVariationGroups;
  309. }
  310. for (unsigned i = 0; i < combinations; ++i)
  311. {
  312. unsigned active = i; // Variations/options active on this particular combination
  313. unsigned variationsActive = 0;
  314. bool skipThis = false;
  315. // Check for excludes/includes/requires
  316. for (unsigned j = 0; j < variations.Size(); ++j)
  317. {
  318. if ((active >> j) & 1)
  319. {
  320. for (unsigned k = 0; k < variations[j].includes_.Size(); ++k)
  321. {
  322. if (nameToIndex.Find(variations[j].includes_[k]) != nameToIndex.End())
  323. active |= (1 << nameToIndex[variations[j].includes_[k]]);
  324. }
  325. for (unsigned k = 0; k < variations[j].excludes_.Size(); ++k)
  326. {
  327. if (nameToIndex.Find(variations[j].excludes_[k]) != nameToIndex.End())
  328. active &= ~(1 << nameToIndex[variations[j].excludes_[k]]);
  329. }
  330. // Skip dummy separators (options without name and defines)
  331. if (variations[j].name_.Empty() && variations[j].option_ && variations[j].defines_.Empty())
  332. active &= ~(1 << j);
  333. // If it's a variation, exclude all other variations in the same group
  334. if (!variations[j].option_)
  335. {
  336. for (unsigned k = j - 1; k < variations.Size(); --k)
  337. {
  338. if (!variations[k].option_)
  339. active &= ~(1 << k);
  340. else
  341. break;
  342. }
  343. for (unsigned k = j + 1; k < variations.Size(); ++k)
  344. {
  345. if (!variations[k].option_)
  346. active &= ~(1 << k);
  347. else
  348. break;
  349. }
  350. ++variationsActive;
  351. }
  352. for (unsigned k = 0; k < variations[j].requires_.Size(); ++k)
  353. {
  354. bool requireFound = false;
  355. for (unsigned l = 0; l < defines_.Size(); ++l)
  356. {
  357. if (defines_[l] == variations[j].requires_[k])
  358. {
  359. requireFound = true;
  360. break;
  361. }
  362. }
  363. for (unsigned l = 0; l < variations.Size(); ++l)
  364. {
  365. if ((active >> l) & 1 && l != j)
  366. {
  367. if (variations[l].name_ == variations[j].requires_[k])
  368. {
  369. requireFound = true;
  370. break;
  371. }
  372. for (unsigned m = 0; m < variations[l].defines_.Size(); ++m)
  373. {
  374. if (variations[l].defines_[m] == variations[j].requires_[k])
  375. {
  376. requireFound = true;
  377. break;
  378. }
  379. }
  380. }
  381. if (requireFound)
  382. break;
  383. }
  384. if (!requireFound)
  385. skipThis = true;
  386. }
  387. }
  388. }
  389. // If variations are included, check that one from each group is active
  390. if (variationsActive < numVariationGroups)
  391. continue;
  392. if (skipThis)
  393. continue;
  394. // Check that this combination is unique
  395. if (usedCombinations.Contains(active))
  396. continue;
  397. // Build shader variation name & define active variations
  398. String outName;
  399. Vector<String> defines;
  400. for (unsigned j = 0; j < variations.Size(); ++j)
  401. {
  402. if (active & (1 << j))
  403. {
  404. if (variations[j].name_.Length())
  405. outName += variations[j].name_;
  406. for (unsigned k = 0; k < variations[j].defines_.Size(); ++k)
  407. defines.Push(variations[j].defines_[k]);
  408. }
  409. }
  410. ProcessedVariation processed;
  411. processed.type_ = baseShader.type_;
  412. processed.name_ = outName;
  413. processed.defines_ = defines;
  414. processedVariations.Push(processed);
  415. usedCombinations.Insert(active);
  416. }
  417. // Build the output file
  418. String glslOutFileName = outDir_ + inDir_ + baseShader.name_;
  419. glslOutFileName += baseShader.type_ == VS ? ".vert" : ".frag";
  420. String xmlOutFileName = glslOutFileName + ".xml";
  421. File outFile(context_, glslOutFileName, FILE_WRITE);
  422. if (!outFile.IsOpen())
  423. ErrorExit("Could not open output file " + glslOutFileName);
  424. for (unsigned i = 0; i < glslCode_.Size(); ++i)
  425. {
  426. if (!glslCode_[i].Empty())
  427. outFile.Write(&glslCode_[i][0], glslCode_[i].Length());
  428. outFile.WriteByte(10);
  429. }
  430. outFile.Close();
  431. XMLFile xmlOutFile(context_);
  432. XMLElement shaderElem = xmlOutFile.CreateRoot("shader");
  433. shaderElem.SetString("type", baseShader.type_ == VS ? "vs" : "ps");
  434. for (unsigned i = 0; i < processedVariations.Size(); ++i)
  435. {
  436. XMLElement variationElem = shaderElem.CreateChild("variation");
  437. variationElem.SetAttribute("name", processedVariations[i].name_);
  438. String allDefines;
  439. for (unsigned j = 0; j < processedVariations[i].defines_.Size(); ++j)
  440. {
  441. if (j)
  442. allDefines += " ";
  443. allDefines += processedVariations[i].defines_[j];
  444. }
  445. variationElem.SetAttribute("defines", allDefines);
  446. }
  447. outFile.Open(xmlOutFileName, FILE_WRITE);
  448. if (!outFile.IsOpen())
  449. ErrorExit("Could not open output file " + xmlOutFileName);
  450. xmlOutFile.Save(outFile);
  451. outFile.Close();
  452. }