Compiler.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670
  1. #include <iostream>
  2. #include <fstream>
  3. #include <string>
  4. #include <utility>
  5. // filesystem
  6. #include <WallpaperEngine/FileSystem/FileSystem.h>
  7. // shader compiler
  8. #include <WallpaperEngine/Render/Shaders/Compiler.h>
  9. #include <WallpaperEngine/Core/Objects/Effects/Constants/CShaderConstantVector3.h>
  10. #include <WallpaperEngine/Core/Objects/Effects/Constants/CShaderConstantInteger.h>
  11. #include <WallpaperEngine/Core/Objects/Effects/Constants/CShaderConstantFloat.h>
  12. #include "WallpaperEngine/Render/Shaders/Variables/CShaderVariable.h"
  13. #include "WallpaperEngine/Render/Shaders/Variables/CShaderVariableFloat.h"
  14. #include "WallpaperEngine/Render/Shaders/Variables/CShaderVariableInteger.h"
  15. #include "WallpaperEngine/Render/Shaders/Variables/CShaderVariableVector2.h"
  16. #include "WallpaperEngine/Render/Shaders/Variables/CShaderVariableVector3.h"
  17. #include "WallpaperEngine/Render/Shaders/Variables/CShaderVariableVector4.h"
  18. using namespace WallpaperEngine::Core;
  19. using namespace WallpaperEngine::Assets;
  20. namespace WallpaperEngine::Render::Shaders
  21. {
  22. Compiler::Compiler (
  23. CContainer* container,
  24. std::string filename,
  25. Type type,
  26. std::map<std::string, int> combos,
  27. const std::map<std::string, CShaderConstant*>& constants,
  28. bool recursive) :
  29. m_combos (combos),
  30. m_recursive (recursive),
  31. m_type (type),
  32. m_file (std::move(filename)),
  33. m_error (""),
  34. m_errorInfo (""),
  35. m_constants (constants),
  36. m_container (container)
  37. {
  38. if (type == Type_Vertex)
  39. this->m_content = this->m_container->readVertexShader (this->m_file);
  40. else if (type == Type_Pixel)
  41. this->m_content = this->m_container->readFragmentShader (this->m_file);
  42. else if (type == Type_Include)
  43. this->m_content = this->m_container->readIncludeShader (this->m_file);
  44. }
  45. bool Compiler::peekString(std::string str, std::string::const_iterator& it)
  46. {
  47. std::string::const_iterator check = str.begin();
  48. std::string::const_iterator cur = it;
  49. while (cur != this->m_content.end () && check != str.end ())
  50. {
  51. if (*cur != *check) return false;
  52. cur ++; check ++;
  53. }
  54. if (cur == this->m_content.end ())
  55. {
  56. return false;
  57. }
  58. if (check != str.end ())
  59. {
  60. return false;
  61. }
  62. it = cur;
  63. return true;
  64. }
  65. bool Compiler::expectSemicolon (std::string::const_iterator& it)
  66. {
  67. if (*it != ';')
  68. {
  69. this->m_error = true;
  70. this->m_errorInfo = "Expected semicolon but got " + *it;
  71. return false;
  72. }
  73. it ++;
  74. return true;
  75. }
  76. void Compiler::ignoreSpaces(std::string::const_iterator &it)
  77. {
  78. while (it != this->m_content.end() && (*it == ' ' || *it == '\t')) it ++;
  79. }
  80. void Compiler::ignoreUpToNextLineFeed (std::string::const_iterator& it)
  81. {
  82. while (it != this->m_content.end() && *it != '\n') it ++;
  83. }
  84. void Compiler::ignoreUpToBlockCommentEnd (std::string::const_iterator& it)
  85. {
  86. while (it != this->m_content.end() && this->peekString ("*/", it) == false) it ++;
  87. }
  88. std::string Compiler::extractType (std::string::const_iterator& it)
  89. {
  90. auto cur = sTypes.begin ();
  91. auto end = sTypes.end ();
  92. while (cur != end)
  93. {
  94. if (this->peekString (*cur, it) == true)
  95. {
  96. return *cur;
  97. }
  98. cur ++;
  99. }
  100. this->m_error = true;
  101. this->m_errorInfo = "Expected type";
  102. return "";
  103. }
  104. std::string Compiler::extractName (std::string::const_iterator& it)
  105. {
  106. std::string::const_iterator cur = it;
  107. std::string::const_iterator begin = cur;
  108. // first character has to be a valid alphabetic characer
  109. if (this->isChar (cur) == false && *cur != '_')
  110. {
  111. this->m_error = true;
  112. this->m_errorInfo = "Expected name doesn't start with a valid character";
  113. return "";
  114. }
  115. cur ++;
  116. while (cur != this->m_content.end () && (this->isChar (cur) == true || *cur == '_' || this->isNumeric (cur) == true)) cur ++;
  117. it = cur;
  118. return std::string (begin, cur);
  119. }
  120. std::string Compiler::extractArray(std::string::const_iterator &it, bool mustExists)
  121. {
  122. std::string::const_iterator cur = it;
  123. std::string::const_iterator begin = cur;
  124. if (*cur != '[')
  125. {
  126. if (mustExists == false)
  127. return "";
  128. this->m_error = true;
  129. this->m_errorInfo = "Expected an array but found nothing";
  130. return "";
  131. }
  132. cur ++;
  133. while (cur != this->m_content.end () && *cur != ']') cur ++;
  134. it = ++cur;
  135. return std::string (begin, cur);
  136. }
  137. bool Compiler::isChar (std::string::const_iterator& it)
  138. {
  139. return ((*it) >= 'A' && (*it) <= 'Z') || ((*it) >= 'a' && (*it) <= 'z');
  140. }
  141. bool Compiler::isNumeric (std::string::const_iterator& it)
  142. {
  143. return (*it) >= '0' && (*it) <= '9';
  144. }
  145. std::string Compiler::extractQuotedValue(std::string::const_iterator& it)
  146. {
  147. std::string::const_iterator cur = it;
  148. if (*cur != '"')
  149. {
  150. m_error = true;
  151. m_errorInfo = "Expected opening \" but got " + (*cur);
  152. return "";
  153. }
  154. cur ++;
  155. while (cur != this->m_content.end () && *cur != '\n' && *cur != '"') cur ++;
  156. if (cur == this->m_content.end ())
  157. {
  158. m_error = true;
  159. m_errorInfo = "Expected closing \" not found";
  160. it = cur;
  161. return "";
  162. }
  163. std::string filename = std::string (++it, cur);
  164. it = ++cur;
  165. return filename;
  166. }
  167. std::string Compiler::lookupShaderFile (std::string filename)
  168. {
  169. // now compile the new shader
  170. // do not include the default header (as it's already included in the parent)
  171. Compiler loader (this->m_container, std::move (filename), Type_Include, this->m_combos, this->m_constants, true);
  172. loader.precompile ();
  173. return loader.getCompiled ();
  174. }
  175. std::string& Compiler::getCompiled ()
  176. {
  177. return this->m_compiledContent;
  178. }
  179. void Compiler::precompile()
  180. {
  181. #define BREAK_IF_ERROR if (this->m_error == true) { throw std::runtime_error ("ERROR PRE-COMPILING SHADER" + this->m_errorInfo); }
  182. // parse the shader and find #includes and such things and translate them to the correct name
  183. // also remove any #version definition to prevent errors
  184. std::string::const_iterator it = this->m_content.begin ();
  185. // reset error indicator
  186. this->m_error = false;
  187. this->m_errorInfo = "";
  188. // search preprocessor macros and parse them
  189. while (it != this->m_content.end () && this->m_error == false)
  190. {
  191. if (*it == ' ' || *it == '\t' || *it == '\n' || *it == '\r' || *it == '\0' || *it == '{' || *it == '}' || *it == '[' || *it == ']' || *it == '.')
  192. {
  193. this->m_compiledContent += *it;
  194. it ++;
  195. }
  196. else if (*it == '#')
  197. {
  198. if (this->peekString ("#include", it) == true)
  199. {
  200. std::string filename = "";
  201. // ignore whitespaces
  202. this->ignoreSpaces (it); BREAK_IF_ERROR
  203. // extract value between quotes
  204. filename = this->extractQuotedValue (it); BREAK_IF_ERROR
  205. // try to find the file first
  206. this->m_compiledContent += "// begin of included from file " + filename + "\r\n";
  207. this->m_compiledContent += this->lookupShaderFile (filename);
  208. this->m_compiledContent += "\r\n// end of included from file " + filename + "\r\n";
  209. }
  210. else
  211. {
  212. this->m_compiledContent += '#';
  213. it ++;
  214. }
  215. }
  216. else if (*it == 'u')
  217. {
  218. // uniforms might have extra information for their values
  219. if (this->peekString ("uniform", it) == true)
  220. {
  221. this->ignoreSpaces (it);
  222. std::string type = this->extractType (it); BREAK_IF_ERROR
  223. this->ignoreSpaces (it);
  224. std::string name = this->extractName (it); BREAK_IF_ERROR
  225. this->ignoreSpaces (it);
  226. std::string array = this->extractArray (it, false); BREAK_IF_ERROR
  227. this->ignoreSpaces (it);
  228. this->expectSemicolon (it); BREAK_IF_ERROR
  229. this->ignoreSpaces (it);
  230. // check if there is any actual extra information and parse it
  231. if (this->peekString ("//", it) == true)
  232. {
  233. this->ignoreSpaces (it);
  234. std::string::const_iterator begin = it;
  235. this->ignoreUpToNextLineFeed (it);
  236. std::string configuration; configuration.append (begin, it);
  237. // parse the parameter information
  238. this->parseParameterConfiguration (type, name, configuration); BREAK_IF_ERROR
  239. this->m_compiledContent += "uniform " + type + " " + name + array + "; // " + configuration;
  240. }
  241. else
  242. {
  243. this->m_compiledContent += "uniform " + type + " " + name + array + ";";
  244. }
  245. }
  246. }
  247. /*else if (*it == 'a')
  248. {
  249. if (this->peekString ("attribute", it) == true)
  250. {
  251. this->ignoreSpaces (it);
  252. }
  253. }*/
  254. // else if (*it == 'a')
  255. // {
  256. // // find attribute definitions
  257. // if (this->peekString ("attribute", it) == true)
  258. // {
  259. // this->ignoreSpaces (it);
  260. // std::string type = this->extractType (it); BREAK_IF_ERROR
  261. // this->ignoreSpaces (it);
  262. // std::string name = this->extractName (it); BREAK_IF_ERROR
  263. // this->ignoreSpaces (it);
  264. // std::string array = this->extractArray (it, false); BREAK_IF_ERROR
  265. // this->ignoreSpaces (it);
  266. // this->expectSemicolon (it); BREAK_IF_ERROR
  267. //
  268. // this->m_compiledContent += "// attribute " + type + " " + name + array;
  269. // this->m_compiledContent += "; /* replaced by " + this->lookupReplaceSymbol (name) + " */";
  270. // }
  271. // else
  272. // {
  273. // // check for types first
  274. // std::string type = this->extractType (it);
  275. //
  276. // // types not found, try names
  277. // if (this->m_error == false)
  278. // {
  279. // this->m_compiledContent += type;
  280. // }
  281. // else
  282. // {
  283. // this->m_error = false;
  284. // std::string name = this->extractName (it);
  285. //
  286. // if (this->m_error == false)
  287. // {
  288. // // check if the name is a translated one or not
  289. // this->m_compiledContent += this->lookupReplaceSymbol (name);
  290. // }
  291. // else
  292. // {
  293. // this->m_error = false;
  294. // this->m_compiledContent += *it;
  295. // it ++;
  296. // }
  297. // }
  298. // }
  299. // }
  300. else if (*it == '/')
  301. {
  302. if (this->peekString ("//", it) == true)
  303. {
  304. std::string::const_iterator begin = it - 2;
  305. // is there a COMBO mark to take care of?
  306. this->ignoreSpaces (it);
  307. if (this->peekString ("[COMBO]", it) == true)
  308. {
  309. // parse combo json data to define the proper variables
  310. this->ignoreSpaces (it);
  311. begin = it;
  312. this->ignoreUpToNextLineFeed (it);
  313. std::string configuration; configuration.append (begin, it);
  314. this->m_compiledContent += "// [COMBO] " + configuration;
  315. this->parseComboConfiguration (configuration); BREAK_IF_ERROR;
  316. }
  317. else if (this->peekString ("[COMBO_OFF]", it) == true)
  318. {
  319. // parse combo json data to define the proper variables
  320. this->ignoreSpaces (it);
  321. begin = it;
  322. this->ignoreUpToNextLineFeed (it);
  323. std::string configuration; configuration.append (begin, it);
  324. this->m_compiledContent += "// [COMBO_OFF] " + configuration;
  325. this->parseComboConfiguration (configuration); BREAK_IF_ERROR;
  326. }
  327. else
  328. {
  329. this->ignoreUpToNextLineFeed (it);
  330. this->m_compiledContent.append (begin, it);
  331. }
  332. }
  333. else if (this->peekString ("/*", it) == true)
  334. {
  335. std::string::const_iterator begin = it - 2;
  336. this->ignoreUpToBlockCommentEnd (it);
  337. this->m_compiledContent.append (begin, it);
  338. }
  339. else
  340. {
  341. this->m_compiledContent += *it;
  342. it ++;
  343. }
  344. }
  345. else
  346. {
  347. // check for types first
  348. std::string type = this->extractType (it);
  349. // types not found, try names
  350. if (this->m_error == false)
  351. {
  352. this->m_compiledContent += type;
  353. }
  354. else
  355. {
  356. this->m_error = false;
  357. std::string name = this->extractName (it);
  358. if (this->m_error == false)
  359. {
  360. // check if the name is a translated one or not
  361. this->m_compiledContent += name;
  362. }
  363. else
  364. {
  365. this->m_error = false;
  366. this->m_compiledContent += *it++;
  367. }
  368. }
  369. }
  370. }
  371. std::string finalCode;
  372. if (this->m_recursive == false)
  373. {
  374. // add the opengl compatibility at the top
  375. finalCode = "#version 120\n"
  376. "#define highp\n"
  377. "#define mediump\n"
  378. "#define lowp\n"
  379. "#define mul(x, y) (y * x)\n"
  380. "#define frac fract\n"
  381. "#define CAST2(x) (vec2(x))\n"
  382. "#define CAST3(x) (vec3(x))\n"
  383. "#define CAST4(x) (vec4(x))\n"
  384. "#define CAST3X3(x) (mat3(x))\n"
  385. "#define saturate(x) (clamp(x, 0.0, 1.0))\n"
  386. "#define texSample2D texture2D\n"
  387. "#define texSample2DLod texture2DLod\n"
  388. "#define texture2DLod texture2D\n"
  389. "#define atan2 atan\n"
  390. "#define ddx dFdx\n"
  391. "#define ddy(x) dFdy(-(x))\n"
  392. "#define GLSL 1\n\n";
  393. // add combo values
  394. auto cur = this->m_combos.begin ();
  395. auto end = this->m_combos.end ();
  396. for (; cur != end; cur ++)
  397. {
  398. finalCode += "#define " + (*cur).first + " " + std::to_string ((*cur).second) + "\n";
  399. }
  400. }
  401. finalCode += this->m_compiledContent;
  402. if (DEBUG && this->m_recursive == false)
  403. {
  404. if (this->m_type == Type_Vertex)
  405. std::cout << "======================== COMPILED VERTEX SHADER " << this->m_file.c_str () << " ========================" << std::endl;
  406. else
  407. std::cout << "======================== COMPILED FRAGMENT SHADER " << this->m_file.c_str () << " ========================" << std::endl;
  408. std::cout << finalCode << std::endl;
  409. }
  410. // store the final final code here
  411. this->m_compiledContent = finalCode;
  412. #undef BREAK_IF_ERROR
  413. }
  414. void Compiler::parseComboConfiguration (const std::string& content)
  415. {
  416. json data = json::parse (content);
  417. auto combo = jsonFindRequired (data, "combo", "cannot parse combo information");
  418. auto defvalue = jsonFindRequired (data, "default", "cannot parse combo information");
  419. // add line feed just in case
  420. this->m_compiledContent += "\n";
  421. // check the combos
  422. std::map<std::string, int>::const_iterator entry = this->m_combos.find ((*combo).get <std::string> ());
  423. // if the combo was not found in the predefined values this means that the default value in the JSON data can be used
  424. // so only define the ones that are not already defined
  425. if (entry == this->m_combos.end ())
  426. {
  427. // if no combo is defined just load the default settings
  428. if ((*defvalue).is_number_float ())
  429. {
  430. throw std::runtime_error ("float combos not supported");
  431. }
  432. else if ((*defvalue).is_number_integer ())
  433. {
  434. this->m_combos.insert (std::make_pair <std::string, int> (*combo, (*defvalue).get <irr::s32> ()));
  435. }
  436. else if ((*defvalue).is_string ())
  437. {
  438. throw std::runtime_error ("string combos not supported");
  439. }
  440. else
  441. {
  442. throw std::runtime_error ("cannot parse combo information, unknown type");
  443. }
  444. }
  445. }
  446. void Compiler::parseParameterConfiguration (const std::string& type, const std::string& name, const std::string& content)
  447. {
  448. json data = json::parse (content);
  449. auto material = data.find ("material");
  450. auto defvalue = data.find ("default");
  451. auto range = data.find ("range");
  452. // this is not a real parameter
  453. if (material == data.end ())
  454. return;
  455. auto constant = this->m_constants.find (*material);
  456. if (constant == this->m_constants.end () && defvalue == data.end ())
  457. {
  458. if (type != "sampler2D")
  459. throw std::runtime_error ("cannot parse parameter data");
  460. }
  461. Variables::CShaderVariable* parameter = nullptr;
  462. // TODO: SUPPORT VALUES FOR ALL THESE TYPES
  463. if (type == "vec4")
  464. {
  465. parameter = new Variables::CShaderVariableVector4 (
  466. WallpaperEngine::Core::aToVector4 (*defvalue)
  467. );
  468. }
  469. else if (type == "vec3")
  470. {
  471. parameter = new Variables::CShaderVariableVector3 (
  472. constant == this->m_constants.end ()
  473. ? WallpaperEngine::Core::aToVector3 (*defvalue)
  474. : *(*constant).second->as <CShaderConstantVector3> ()->getValue ()
  475. );
  476. }
  477. else if (type == "vec2")
  478. {
  479. parameter = new Variables::CShaderVariableVector2 (
  480. WallpaperEngine::Core::aToVector2 (*defvalue)
  481. );
  482. }
  483. else if (type == "float")
  484. {
  485. float value = 0;
  486. if (constant == this->m_constants.end ())
  487. value = (*defvalue).get <float> ();
  488. else if ((*constant).second->is <CShaderConstantFloat> () == true)
  489. value = *(*constant).second->as <CShaderConstantFloat> ()->getValue ();
  490. else if ((*constant).second->is <CShaderConstantInteger> () == true)
  491. value = *(*constant).second->as <CShaderConstantInteger> ()->getValue ();
  492. parameter = new Variables::CShaderVariableFloat (value);
  493. }
  494. else if (type == "int")
  495. {
  496. irr::s32 value = 0;
  497. if (constant == this->m_constants.end ())
  498. value = (*defvalue).get <irr::s32> ();
  499. else if ((*constant).second->is <CShaderConstantFloat> () == true)
  500. value = *(*constant).second->as <CShaderConstantFloat> ()->getValue ();
  501. else if ((*constant).second->is <CShaderConstantInteger> () == true)
  502. value = *(*constant).second->as <CShaderConstantInteger> ()->getValue ();
  503. parameter = new Variables::CShaderVariableInteger (value);
  504. }
  505. else if (type == "sampler2D")
  506. {
  507. // samplers can have special requirements, check what sampler we're working with and create definitions
  508. // if needed
  509. auto combo = data.find ("combo");
  510. auto textureName = data.find ("default");
  511. if (combo != data.end ())
  512. {
  513. // TODO: CHECK WHAT TEXTURE THIS REFERS TO
  514. // add the new combo to the list
  515. this->m_combos.insert (std::make_pair<std::string, int> (*combo, 1));
  516. uint32_t textureSize = 0;
  517. // also ensure that the textureName is loaded and we know about it
  518. void* textureData = this->m_container->readTexture ((*textureName).get <std::string> (), &textureSize);
  519. // now generate our opengl textureName
  520. CTexture* texture = new CTexture (textureData);
  521. // extract the texture number from the name
  522. char value = name.at (std::string("g_Texture").length ());
  523. // now convert it to integer
  524. int index = value - '0';
  525. this->m_textures.insert (
  526. std::make_pair (index, texture)
  527. );
  528. }
  529. // samplers are not saved, we can ignore them for now
  530. return;
  531. }
  532. else
  533. {
  534. this->m_error = true;
  535. this->m_errorInfo = "Unknown parameter type: " + type + " for " + name;
  536. return;
  537. }
  538. parameter->setIdentifierName (*material);
  539. parameter->setName (name);
  540. this->m_parameters.push_back (parameter);
  541. }
  542. Variables::CShaderVariable* Compiler::findParameter (const std::string& identifier)
  543. {
  544. auto cur = this->m_parameters.begin ();
  545. auto end = this->m_parameters.end ();
  546. for (; cur != end; cur ++)
  547. {
  548. if ((*cur)->getIdentifierName () == identifier)
  549. {
  550. return (*cur);
  551. }
  552. }
  553. return nullptr;
  554. }
  555. const std::vector <Variables::CShaderVariable*>& Compiler::getParameters () const
  556. {
  557. return this->m_parameters;
  558. }
  559. const std::map <std::string, int>& Compiler::getCombos () const
  560. {
  561. return this->m_combos;
  562. }
  563. const std::map <int, CTexture*>& Compiler::getTextures () const
  564. {
  565. return this->m_textures;
  566. }
  567. std::vector<std::string> Compiler::sTypes =
  568. {
  569. "vec4", "vec3", "vec2", "float", "sampler2D", "mat4"
  570. };
  571. }