ShaderCompiler.cpp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725
  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 "StringUtils.h"
  27. #include "XMLFile.h"
  28. #include <cstring>
  29. #include <fstream>
  30. #include <iostream>
  31. #include "DebugNew.h"
  32. enum ShaderType
  33. {
  34. VS,
  35. PS,
  36. Both
  37. };
  38. struct Variation
  39. {
  40. Variation(const std::string& name, bool isOption) :
  41. name_(name),
  42. option_(isOption)
  43. {
  44. }
  45. void addDefine(const std::string& def)
  46. {
  47. defines_.push_back(def);
  48. }
  49. void addExclude(const std::string& excl)
  50. {
  51. excludes_.push_back(excl);
  52. }
  53. void addInclude(const std::string& incl)
  54. {
  55. includes_.push_back(incl);
  56. }
  57. void addRequire(const std::string& req)
  58. {
  59. requires_.push_back(req);
  60. }
  61. std::string name_;
  62. std::vector<std::string> defines_;
  63. std::vector<std::string> excludes_;
  64. std::vector<std::string> includes_;
  65. std::vector<std::string> requires_;
  66. bool option_;
  67. };
  68. struct Shader
  69. {
  70. Shader(const std::string& name, ShaderType type) :
  71. name_(name),
  72. type_(type)
  73. {
  74. }
  75. void addVariation(const Variation& var)
  76. {
  77. variations_.push_back(var);
  78. }
  79. std::string name_;
  80. ShaderType type_;
  81. std::vector<Variation> variations_;
  82. };
  83. struct Parameter
  84. {
  85. std::string name_;
  86. unsigned index_;
  87. };
  88. struct Parameters
  89. {
  90. void addVSParam(const std::string& name, unsigned index)
  91. {
  92. Parameter newParam;
  93. newParam.name_ = name;
  94. newParam.index_ = index;
  95. vsParams_.push_back(newParam);
  96. }
  97. void addPSParam(const std::string& name, unsigned index)
  98. {
  99. Parameter newParam;
  100. newParam.name_ = name;
  101. newParam.index_ = index;
  102. psParams_.push_back(newParam);
  103. }
  104. void addTextureUnit(const std::string& name, unsigned index)
  105. {
  106. Parameter newParam;
  107. newParam.name_ = name;
  108. newParam.index_ = index;
  109. textureUnits_.push_back(newParam);
  110. }
  111. std::vector<Parameter> vsParams_;
  112. std::vector<Parameter> psParams_;
  113. std::vector<Parameter> textureUnits_;
  114. };
  115. SharedPtr<Context> context_(new Context());
  116. SharedPtr<FileSystem> fileSystem_(new FileSystem(context_));
  117. std::string inDir_;
  118. std::string inFile_;
  119. std::string outDir_;
  120. std::map<std::string, unsigned> vsParams_;
  121. std::map<std::string, unsigned> psParams_;
  122. std::map<std::string, unsigned> textureUnits_;
  123. std::vector<std::string> defines_;
  124. bool useSM3_ = false;
  125. int main(int argc, char** argv);
  126. void Run(const std::vector<std::string>& arguments);
  127. void CompileVariations(const Shader& baseShader, XMLElement& shaders);
  128. bool Compile(ShaderType type, const std::string& input, const std::string& output, const std::vector<std::string>& defines, Parameters& params);
  129. void ErrorExit(const std::string& error);
  130. int main(int argc, char** argv)
  131. {
  132. std::vector<std::string> arguments;
  133. for (int i = 1; i < argc; ++i)
  134. arguments.push_back(Replace(std::string(argv[i]), '/', '\\'));
  135. Run(arguments);
  136. return 0;
  137. }
  138. void Run(const std::vector<std::string>& arguments)
  139. {
  140. if (arguments.size() < 2)
  141. {
  142. ErrorExit(
  143. "Usage: ShaderCompiler <definitionfile> <outputpath> [SM3] [define1] [define2]\n\n"
  144. "HLSL files will be loaded from definition file directory, and binary code will\n"
  145. "be output to same directory as the output file.\n"
  146. );
  147. }
  148. size_t pos = arguments[0].rfind('\\');
  149. if (pos != std::string::npos)
  150. {
  151. inDir_ = arguments[0].substr(0, pos);
  152. inFile_ = arguments[0].substr(pos + 1);
  153. }
  154. else
  155. {
  156. inFile_ = arguments[0];
  157. }
  158. outDir_ = arguments[1];
  159. if (inDir_.empty())
  160. inDir_ = ".\\";
  161. if (outDir_.empty())
  162. outDir_ = ".\\";
  163. if (inDir_[inDir_.length()-1] != '\\')
  164. inDir_ = inDir_ + "\\";
  165. if (outDir_[outDir_.length()-1] != '\\')
  166. outDir_ = outDir_ + "\\";
  167. for (unsigned i = 2; i < arguments.size(); ++i)
  168. {
  169. std::string arg = ToUpper(arguments[i]);
  170. if (arg == "SM3")
  171. useSM3_ = true;
  172. else if (arg == "SM2")
  173. useSM3_ = false;
  174. defines_.push_back(arg);
  175. }
  176. XMLFile doc(context_);
  177. File source(context_);
  178. source.Open(arguments[0]);
  179. if (!doc.Load(source))
  180. ErrorExit("Could not open input file " + arguments[0]);
  181. XMLElement shaders = doc.GetRootElement("shaders");
  182. if (!shaders)
  183. ErrorExit("No shaders element in " + source.GetName());
  184. XMLFile outDoc(context_);
  185. XMLElement outShaders = outDoc.CreateRootElement("shaders");
  186. XMLElement shader = shaders.GetChildElement("shader");
  187. while (shader)
  188. {
  189. bool WriteOutput = false;
  190. std::string source = shader.GetString("name");
  191. ShaderType compileType = Both;
  192. std::string type = shader.GetString("type");
  193. if ((type == "VS") || (type == "vs"))
  194. compileType = VS;
  195. if ((type == "PS") || (type == "ps"))
  196. compileType = PS;
  197. if ((compileType == Both) || (compileType == PS))
  198. WriteOutput = true;
  199. Shader baseShader(source, compileType);
  200. XMLElement variation = shader.GetChildElement("");
  201. while (variation)
  202. {
  203. std::string value = variation.GetName();
  204. if ((value == "variation") || (value == "option"))
  205. {
  206. std::string name = variation.GetString("name");
  207. Variation newVar(name, value == "option");
  208. std::string simpleDefine = variation.GetString("define");
  209. if (!simpleDefine.empty())
  210. newVar.addDefine(simpleDefine);
  211. std::string simpleExclude = variation.GetString("exclude");
  212. if (!simpleExclude.empty())
  213. newVar.addExclude(simpleExclude);
  214. std::string simpleInclude = variation.GetString("include");
  215. if (!simpleInclude.empty())
  216. newVar.addInclude(simpleInclude);
  217. std::string simpleRequire = variation.GetString("require");
  218. if (!simpleRequire.empty())
  219. newVar.addRequire(simpleRequire);
  220. XMLElement define = variation.GetChildElement("define");
  221. while (define)
  222. {
  223. std::string name = define.GetString("name");
  224. newVar.addDefine(name);
  225. define = define.GetNextElement("define");
  226. }
  227. XMLElement exclude = variation.GetChildElement("exclude");
  228. while (exclude)
  229. {
  230. std::string name = exclude.GetString("name");
  231. newVar.addExclude(name);
  232. exclude = exclude.GetNextElement("exclude");
  233. }
  234. XMLElement include = variation.GetChildElement("include");
  235. while (include)
  236. {
  237. std::string name = include.GetString("name");
  238. newVar.addInclude(name);
  239. include = include.GetNextElement("include");
  240. }
  241. XMLElement require = variation.GetChildElement("require");
  242. while (require)
  243. {
  244. std::string name = require.GetString("name");
  245. newVar.addRequire(name);
  246. require = require.GetNextElement("require");
  247. }
  248. baseShader.addVariation(newVar);
  249. }
  250. variation = variation.GetNextElement();
  251. }
  252. if (baseShader.type_ != Both)
  253. CompileVariations(baseShader, outShaders);
  254. else
  255. {
  256. baseShader.type_ = VS;
  257. CompileVariations(baseShader, outShaders);
  258. baseShader.type_ = PS;
  259. CompileVariations(baseShader, outShaders);
  260. }
  261. if (WriteOutput)
  262. {
  263. std::string outFileName = outDir_ + inDir_ + source + ".xml";
  264. remove(outFileName.c_str());
  265. // Add global parameter & texture sampler definitions
  266. {
  267. XMLElement parameters = outShaders.CreateChildElement("vsparameters");
  268. std::multimap<unsigned, std::string> sorted;
  269. for (std::map<std::string, unsigned>::const_iterator i = vsParams_.begin(); i != vsParams_.end(); ++i)
  270. sorted.insert(std::pair<unsigned, std::string>(i->second, i->first));
  271. for (std::multimap<unsigned, std::string>::const_iterator i = sorted.begin(); i != sorted.end(); ++i)
  272. {
  273. XMLElement param = parameters.CreateChildElement("parameter");
  274. param.SetString("name", i->second);
  275. param.SetString("index", ToString(i->first));
  276. }
  277. }
  278. {
  279. XMLElement parameters = outShaders.CreateChildElement("psparameters");
  280. std::multimap<unsigned, std::string> sorted;
  281. for (std::map<std::string, unsigned>::const_iterator i = psParams_.begin(); i != psParams_.end(); ++i)
  282. sorted.insert(std::pair<unsigned, std::string>(i->second, i->first));
  283. for (std::multimap<unsigned, std::string>::const_iterator i = sorted.begin(); i != sorted.end(); ++i)
  284. {
  285. XMLElement param = parameters.CreateChildElement("parameter");
  286. param.SetString("name", i->second);
  287. param.SetString("index", ToString(i->first));
  288. }
  289. }
  290. {
  291. XMLElement parameters = outShaders.CreateChildElement("textureunits");
  292. std::multimap<unsigned, std::string> sorted;
  293. for (std::map<std::string, unsigned>::const_iterator i = textureUnits_.begin(); i != textureUnits_.end(); ++i)
  294. sorted.insert(std::pair<unsigned, std::string>(i->second, i->first));
  295. for (std::multimap<unsigned, std::string>::const_iterator i = sorted.begin(); i != sorted.end(); ++i)
  296. {
  297. XMLElement param = parameters.CreateChildElement("textureunit");
  298. param.SetString("name", i->second);
  299. param.SetString("index", ToString(i->first));
  300. }
  301. }
  302. File outFile(context_);
  303. outFile.Open(outFileName, FILE_WRITE);
  304. if (!outDoc.Save(outFile))
  305. ErrorExit("Could not save output file " + outFileName);
  306. }
  307. shader = shader.GetNextElement("shader");
  308. }
  309. }
  310. void CompileVariations(const Shader& baseShader, XMLElement& shaders)
  311. {
  312. unsigned combinations = 1;
  313. std::vector<unsigned> compiled;
  314. bool hasVariations = false;
  315. const std::vector<Variation>& variations = baseShader.variations_;
  316. std::map<std::string, unsigned> nameToIndex;
  317. if (variations.size() > 32)
  318. ErrorExit("Maximum amount of variations exceeded");
  319. for (unsigned i = 0; i < variations.size(); ++i)
  320. {
  321. combinations *= 2;
  322. nameToIndex[variations[i].name_] = i;
  323. if (!variations[i].option_)
  324. hasVariations = true;
  325. }
  326. for (unsigned i = 0; i < combinations; ++i)
  327. {
  328. unsigned active = i; // Variations/options active on this particular combination
  329. bool variationActive = false;
  330. bool skipThis = false;
  331. // Check for excludes/includes/requires
  332. for (unsigned j = 0; j < variations.size(); ++j)
  333. {
  334. if ((active >> j) & 1)
  335. {
  336. for (unsigned k = 0; k < variations[j].includes_.size(); ++k)
  337. {
  338. if (nameToIndex.find(variations[j].includes_[k]) != nameToIndex.end())
  339. active |= (1 << nameToIndex[variations[j].includes_[k]]);
  340. }
  341. for (unsigned k = 0; k < variations[j].excludes_.size(); ++k)
  342. {
  343. if (nameToIndex.find(variations[j].excludes_[k]) != nameToIndex.end())
  344. active &= ~(1 << nameToIndex[variations[j].excludes_[k]]);
  345. }
  346. // If it's a variation, exclude all other variations
  347. if (!variations[j].option_)
  348. {
  349. for (unsigned k = 0; k < variations.size(); ++k)
  350. {
  351. if ((k != j) && (!variations[k].option_))
  352. active &= ~(1 << k);
  353. }
  354. variationActive = true;
  355. }
  356. for (unsigned k = 0; k < variations[j].requires_.size(); ++k)
  357. {
  358. bool requireFound = false;
  359. for (unsigned l = 0; l < defines_.size(); ++l)
  360. {
  361. if (defines_[l] == variations[j].requires_[k])
  362. {
  363. requireFound = true;
  364. break;
  365. }
  366. }
  367. for (unsigned l = 0; l < variations.size(); ++l)
  368. {
  369. if (((active >> l) & 1) && (l != j))
  370. {
  371. if (variations[l].name_ == variations[j].requires_[k])
  372. {
  373. requireFound = true;
  374. break;
  375. }
  376. for (unsigned m = 0; m < variations[l].defines_.size(); ++m)
  377. {
  378. if (variations[l].defines_[m] == variations[j].requires_[k])
  379. {
  380. requireFound = true;
  381. break;
  382. }
  383. }
  384. }
  385. if (requireFound)
  386. break;
  387. }
  388. if (!requireFound)
  389. skipThis = true;
  390. }
  391. }
  392. }
  393. // If variations are included, check that one of them is active
  394. if ((hasVariations) && (!variationActive))
  395. continue;
  396. if (skipThis)
  397. continue;
  398. // Check that this combination is unique
  399. bool unique = true;
  400. for (unsigned j = 0; j < compiled.size(); ++j)
  401. {
  402. if (compiled[j] == active)
  403. {
  404. unique = false;
  405. break;
  406. }
  407. }
  408. if (unique)
  409. {
  410. bool firstSuffix = true;
  411. // Build output shader filename & defines from active variations
  412. std::string outName = baseShader.name_;
  413. std::vector<std::string> defines;
  414. for (unsigned j = 0; j < variations.size(); ++j)
  415. {
  416. if (active & (1 << j))
  417. {
  418. if (variations[j].name_.length())
  419. {
  420. if (firstSuffix)
  421. {
  422. outName = outName + "_" + variations[j].name_;
  423. firstSuffix = false;
  424. }
  425. else
  426. outName = outName + variations[j].name_;
  427. }
  428. for (unsigned k = 0; k < variations[j].defines_.size(); ++k)
  429. defines.push_back(variations[j].defines_[k]);
  430. }
  431. }
  432. Parameters params;
  433. bool ok = Compile(baseShader.type_, baseShader.name_, outName, defines, params);
  434. // If shader was unnecessary (for example SM2 does not support HQ variations)
  435. // no output may have been produced. Skip in that case.
  436. if (ok)
  437. {
  438. XMLElement shader = shaders.CreateChildElement("shader");
  439. shader.SetString("name", outName);
  440. switch (baseShader.type_)
  441. {
  442. case VS:
  443. shader.SetString("type", "vs");
  444. for (unsigned j = 0; j < params.vsParams_.size(); ++j)
  445. {
  446. XMLElement vsParam = shader.CreateChildElement("parameter");
  447. vsParam.SetString("name", params.vsParams_[j].name_);
  448. }
  449. break;
  450. case PS:
  451. shader.SetString("type", "ps");
  452. for (unsigned j = 0; j < params.psParams_.size(); ++j)
  453. {
  454. XMLElement psParam = shader.CreateChildElement("parameter");
  455. psParam.SetString("name", params.psParams_[j].name_);
  456. }
  457. for (unsigned j = 0; j < params.textureUnits_.size(); ++j)
  458. {
  459. XMLElement texture = shader.CreateChildElement("textureunit");
  460. texture.SetString("name", params.textureUnits_[j].name_);
  461. }
  462. break;
  463. }
  464. compiled.push_back(active);
  465. }
  466. }
  467. }
  468. }
  469. bool Compile(ShaderType type, const std::string& input, const std::string& output, const std::vector<std::string>& defines,
  470. Parameters& params)
  471. {
  472. bool compiled = false;
  473. std::string allDefines;
  474. for (unsigned i = 0; i < defines.size(); ++i)
  475. allDefines += "/D" + defines[i] + " ";
  476. for (unsigned i = 0; i < defines_.size(); ++i)
  477. allDefines += "/D" + defines_[i] + " ";
  478. if (type == VS)
  479. {
  480. if (!useSM3_)
  481. {
  482. std::string outFile = output + ".vs2";
  483. std::string command = "fxc /Tvs_2_0 /O3 /EVS /Fo" + outDir_ + inDir_ + outFile + " /Fcoutput.txt " + allDefines +
  484. inDir_ + input + ".hlsl";
  485. if (fileSystem_->SystemCommand(command))
  486. ErrorExit("Failed to compile shader " + outFile);
  487. compiled = true;
  488. }
  489. else
  490. {
  491. std::string outFile = output + ".vs3";
  492. std::string command = "fxc /Tvs_3_0 /O3 /EVS /Fo" + outDir_ + inDir_ + outFile + " /Fcoutput.txt " + allDefines +
  493. inDir_ + input + ".hlsl";
  494. if (fileSystem_->SystemCommand(command))
  495. ErrorExit("Failed to compile shader " + outFile);
  496. compiled = true;
  497. }
  498. }
  499. if (type == PS)
  500. {
  501. if (!useSM3_)
  502. {
  503. std::string outFile = output + ".ps2";
  504. std::string command = "fxc /Tps_2_0 /O3 /EPS /Fo" + outDir_ + inDir_ + outFile + " /Fcoutput.txt " + allDefines +
  505. inDir_ + input + ".hlsl";
  506. std::cout << command << std::endl;
  507. if (fileSystem_->SystemCommand(command))
  508. ErrorExit("Failed to compile shader " + outFile);
  509. compiled = true;
  510. }
  511. else
  512. {
  513. std::string outFile = output + ".ps3";
  514. std::string command = "fxc /Tps_3_0 /O3 /Gfp /EPS /Fo" + outDir_ + inDir_ + outFile + " /Fcoutput.txt " + allDefines +
  515. inDir_ + input + ".hlsl";
  516. std::cout << command << std::endl;
  517. if (fileSystem_->SystemCommand(command))
  518. ErrorExit("Failed to compile shader" + outFile);
  519. compiled = true;
  520. }
  521. }
  522. if (!compiled)
  523. return false;
  524. std::ifstream dump("output.txt");
  525. if (!dump.good())
  526. ErrorExit("Could not open dump file");
  527. bool paramsStarted = false;
  528. while (!dump.eof())
  529. {
  530. char line[256];
  531. dump.getline(line, 256);
  532. std::string lineStr(line);
  533. std::vector<std::string> elements = Split(lineStr, ' ');
  534. if (paramsStarted)
  535. {
  536. if ((!elements.size()) || (elements[0] != "//"))
  537. break;
  538. if ((elements.size() == 4) && (elements[0] == "//") && (elements[1][0] != '-'))
  539. {
  540. std::string name = elements[1];
  541. std::string reg = elements[2];
  542. bool isSampler = false;
  543. if (reg[0] == 's')
  544. isSampler = true;
  545. if ((name[0] == 'c') || (name[0] == 's'))
  546. name = name.substr(1, name.size() - 1);
  547. unsigned index = ToInt(reg.substr(1, reg.size() - 1));
  548. if (isSampler)
  549. {
  550. // Skip if it's a G-buffer sampler
  551. if (name.find("Buffer") == std::string::npos)
  552. {
  553. params.addTextureUnit(name, index);
  554. if (textureUnits_.find(name) != textureUnits_.end())
  555. {
  556. unsigned oldIndex = textureUnits_[name];
  557. if (oldIndex != index)
  558. ErrorExit("Texture " + name + " bound to several sampler Registers");
  559. }
  560. textureUnits_[name] = index;
  561. }
  562. }
  563. else
  564. {
  565. if (type == VS)
  566. {
  567. params.addVSParam(name, index);
  568. if (vsParams_.find(name) != vsParams_.end())
  569. {
  570. unsigned oldIndex = vsParams_[name];
  571. if (oldIndex != index)
  572. ErrorExit("Parameter " + name + " bound to several constant Registers");
  573. }
  574. vsParams_[name] = index;
  575. }
  576. else
  577. {
  578. params.addPSParam(name, index);
  579. if (psParams_.find(name) != psParams_.end())
  580. {
  581. unsigned oldIndex = psParams_[name];
  582. if (oldIndex != index)
  583. ErrorExit("Parameter " + name + " bound to several constant Registers");
  584. }
  585. psParams_[name] = index;
  586. }
  587. }
  588. }
  589. }
  590. else
  591. {
  592. if ((elements.size() == 4) && (elements[0] == "//") && (elements[1] == "Name"))
  593. paramsStarted = true;
  594. }
  595. }
  596. dump.close();
  597. remove("output.txt");
  598. return true;
  599. }
  600. void ErrorExit(const std::string& error)
  601. {
  602. std::cout << error;
  603. exit(1);
  604. }