ShaderCompiler.cpp 25 KB

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