metal-shaderbuilder.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937
  1. #include "metal-shaderprocessor.hpp"
  2. #include <strstream>
  3. #include <vector>
  4. #include <map>
  5. #include <set>
  6. using namespace std;
  7. #define METAL_VERSION_1_1 ((1 << 16) | 1)
  8. #define METAL_VERSION_1_2 ((1 << 16) | 2)
  9. #define COMPILE_METAL_VERSION METAL_VERSION_1_2
  10. #define USE_PROGRAMMABLE_SAMPLER 1
  11. constexpr const char *UNIFORM_DATA_NAME = "UniformData";
  12. enum class ShaderTextureCallType
  13. {
  14. Sample,
  15. SampleBias,
  16. SampleGrad,
  17. SampleLevel,
  18. Load
  19. };
  20. struct ShaderFunctionInfo
  21. {
  22. bool useUniform;
  23. vector<string> useTextures;
  24. #if USE_PROGRAMMABLE_SAMPLER
  25. vector<string> useSamplers;
  26. #endif
  27. };
  28. struct ShaderBuilder
  29. {
  30. const gs_shader_type type;
  31. ShaderParser *parser;
  32. ostrstream output;
  33. set<string> constantNames;
  34. vector<struct shader_var*> textureVars;
  35. map<string, ShaderFunctionInfo> functionInfo;
  36. void Build(string &outputString);
  37. inline ShaderBuilder(gs_shader_type type, ShaderParser *parser)
  38. : type(type),
  39. parser(parser)
  40. {
  41. }
  42. bool isVertexShader() const {return type == GS_SHADER_VERTEX;}
  43. bool isPixelShader() const {return type == GS_SHADER_PIXEL;}
  44. private:
  45. struct shader_var *GetVariable(struct cf_token *token);
  46. bool IsNextCompareOperator(struct cf_token *&token);
  47. void AnalysisFunction(struct cf_token *&token, const char *end,
  48. ShaderFunctionInfo &info);
  49. void WriteType(const char *type);
  50. bool WriteTypeToken(struct cf_token *token);
  51. bool WriteMul(struct cf_token *&token);
  52. bool WriteConstantVariable(struct cf_token *token);
  53. bool WriteTextureCall(struct cf_token *&token,
  54. ShaderTextureCallType type);
  55. bool WriteTextureCode(struct cf_token *&token, struct shader_var *var);
  56. bool WriteIntrinsic(struct cf_token *&token);
  57. void WriteFunctionAdditionalParam(string funcionName);
  58. void WriteFunctionContent(struct cf_token *&token, const char *end);
  59. void WriteSamplerParamDelimitter(bool &first);
  60. void WriteSamplerFilter(enum gs_sample_filter filter, bool &first);
  61. void WriteSamplerAddress(enum gs_address_mode address,
  62. const char key, bool &first);
  63. void WriteSamplerMaxAnisotropy(int maxAnisotropy, bool &first);
  64. void WriteSamplerBorderColor(uint32_t borderColor, bool &first);
  65. void WriteVariable(const shader_var *var);
  66. void WriteSampler(struct shader_sampler *sampler);
  67. void WriteStruct(const shader_struct *str);
  68. void WriteFunction(const shader_func *func);
  69. void WriteInclude();
  70. void WriteVariables();
  71. void WriteSamplers();
  72. void WriteStructs();
  73. void WriteFunctions();
  74. };
  75. static inline const char *GetType(const string &type)
  76. {
  77. if (type == "texture2d")
  78. return "texture2d<float>";
  79. else if (type == "texture3d")
  80. return "texture3d<float>";
  81. else if (type == "texture_cube")
  82. return "texturecube<float>";
  83. else if (type == "texture_rect")
  84. throw "texture_rect is not supported in Metal";
  85. else if (type.compare(0, 4, "half") == 0) {
  86. switch (*(type.end() - 1)) {
  87. case '2': return "float2";
  88. case '3': return "float3";
  89. case '4': return "float4";
  90. case 'f': return "float";
  91. }
  92. throw "Unknown type";
  93. } else if (type.compare(0, 10, "min16float") == 0) {
  94. switch (*(type.end() - 1)) {
  95. case '2': return "half2";
  96. case '3': return "half3";
  97. case '4': return "half4";
  98. case 'f': return "half";
  99. }
  100. throw "Unknown type";
  101. } else if (type.compare(0, 10, "min10float") == 0)
  102. throw "min10float* is not supported in Metal";
  103. else if (type.compare(0, 6, "double") == 0)
  104. throw "double* is not supported in Metal";
  105. else if (type.compare(0, 8, "min16int") == 0) {
  106. switch (*(type.end() - 1)) {
  107. case '2': return "short2";
  108. case '3': return "short3";
  109. case '4': return "short4";
  110. case 't': return "short";
  111. }
  112. throw "Unknown type";
  113. } else if (type.compare(0, 9, "min16uint") == 0) {
  114. switch (*(type.end() - 1)) {
  115. case '2': return "ushort2";
  116. case '3': return "ushort3";
  117. case '4': return "ushort4";
  118. case 't': return "ushort";
  119. }
  120. throw "Unknown type";
  121. } else if (type.compare(0, 8, "min12int") == 0)
  122. throw "min12int* is not supported in Metal";
  123. return nullptr;
  124. }
  125. inline void ShaderBuilder::WriteType(const char *rawType)
  126. {
  127. string type(rawType);
  128. const char *newType = GetType(string(rawType));
  129. output << (newType != nullptr ? newType : type);
  130. }
  131. inline bool ShaderBuilder::WriteTypeToken(struct cf_token *token)
  132. {
  133. string type(token->str.array, token->str.len);
  134. const char *newType = GetType(type);
  135. if (newType == nullptr)
  136. return false;
  137. output << newType;
  138. return true;
  139. }
  140. inline void ShaderBuilder::WriteInclude()
  141. {
  142. output << "#include <metal_stdlib>" << endl
  143. << "using namespace metal;" << endl
  144. << endl;
  145. }
  146. inline void ShaderBuilder::WriteVariable(const shader_var *var)
  147. {
  148. if (var->var_type == SHADER_VAR_CONST)
  149. output << "constant ";
  150. WriteType(var->type);
  151. output << ' ' << var->name;
  152. }
  153. static inline const char *GetMapping(const char *rawMapping)
  154. {
  155. if (rawMapping == nullptr)
  156. return nullptr;
  157. string mapping(rawMapping);
  158. if (mapping == "POSITION")
  159. return "position";
  160. if (mapping == "COLOR")
  161. return "color(0)";
  162. return nullptr;
  163. }
  164. inline void ShaderBuilder::WriteVariables()
  165. {
  166. if (parser->params.num == 0)
  167. return;
  168. bool isFirst = true;
  169. for (struct shader_var *var = parser->params.array;
  170. var != parser->params.array + parser->params.num;
  171. var++) {
  172. if (isPixelShader() &&
  173. astrcmp_n("texture", var->type, 7) == 0) {
  174. textureVars.push_back(var);
  175. } else {
  176. if (isFirst) {
  177. output << "struct " << UNIFORM_DATA_NAME
  178. << " {" << endl;
  179. isFirst = false;
  180. }
  181. output << '\t';
  182. WriteVariable(var);
  183. const char* mapping = GetMapping(var->mapping);
  184. if (mapping != nullptr)
  185. output << " [[" << mapping << "]]";
  186. output << ';' << endl;
  187. constantNames.insert(var->name);
  188. }
  189. }
  190. if (!isFirst)
  191. output << "};" << endl << endl;
  192. }
  193. inline void ShaderBuilder::WriteSamplerParamDelimitter(bool &first)
  194. {
  195. if (!first)
  196. output << "," << endl;
  197. else
  198. first = false;
  199. }
  200. inline void ShaderBuilder::WriteSamplerFilter(enum gs_sample_filter filter,
  201. bool &first)
  202. {
  203. if (filter != GS_FILTER_POINT) {
  204. WriteSamplerParamDelimitter(first);
  205. switch (filter) {
  206. case GS_FILTER_LINEAR:
  207. case GS_FILTER_ANISOTROPIC:
  208. output << "\tfilter::linear";
  209. break;
  210. case GS_FILTER_MIN_MAG_POINT_MIP_LINEAR:
  211. output << "\tmag_filter::nearest," << endl
  212. << "\tmin_filter::nearest," << endl
  213. << "\tmip_filter::linear";
  214. break;
  215. case GS_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT:
  216. output << "\tmag_filter::nearest," << endl
  217. << "\tmin_filter::nearest," << endl
  218. << "\tmip_filter::linear";
  219. break;
  220. case GS_FILTER_MIN_POINT_MAG_MIP_LINEAR:
  221. output << "\tmag_filter::linear," << endl
  222. << "\tmin_filter::nearest," << endl
  223. << "\tmip_filter::linear";
  224. break;
  225. case GS_FILTER_MIN_LINEAR_MAG_MIP_POINT:
  226. output << "\tmag_filter::nearest," << endl
  227. << "\tmin_filter::linear," << endl
  228. << "\tmip_filter::nearest";
  229. break;
  230. case GS_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR:
  231. output << "\tmag_filter::nearest," << endl
  232. << "\tmin_filter::linear," << endl
  233. << "\tmip_filter::linear";
  234. break;
  235. case GS_FILTER_MIN_MAG_LINEAR_MIP_POINT:
  236. output << "\tmag_filter::linear," << endl
  237. << "\tmin_filter::linear," << endl
  238. << "\tmip_filter::nearest";
  239. break;
  240. case GS_FILTER_POINT:
  241. default:
  242. throw "Unknown error";
  243. }
  244. }
  245. }
  246. inline void ShaderBuilder::WriteSamplerAddress(enum gs_address_mode address,
  247. const char key, bool &first)
  248. {
  249. if (address != GS_ADDRESS_CLAMP) {
  250. WriteSamplerParamDelimitter(first);
  251. output << "\t" << key << "_address::";
  252. switch (address)
  253. {
  254. case GS_ADDRESS_WRAP:
  255. output << "repeat";
  256. break;
  257. case GS_ADDRESS_MIRROR:
  258. output << "mirrored_repeat";
  259. break;
  260. case GS_ADDRESS_BORDER:
  261. #if COMPILE_METAL_VERSION >= METAL_VERSION_1_2
  262. output << "clamp_to_border";
  263. break;
  264. #else
  265. throw "GS_ADDRESS_BORDER is not supported in MSL 1.1";
  266. #endif
  267. case GS_ADDRESS_MIRRORONCE:
  268. throw "GS_ADDRESS_MIRRORONCE is not supported in Metal";
  269. default:
  270. throw "Unknown error";
  271. }
  272. }
  273. }
  274. inline void ShaderBuilder::WriteSamplerMaxAnisotropy(int maxAnisotropy,
  275. bool &first)
  276. {
  277. if (maxAnisotropy >= 2 && maxAnisotropy <= 16) {
  278. WriteSamplerParamDelimitter(first);
  279. output << "\tmax_anisotropy(" << maxAnisotropy << ")";
  280. }
  281. }
  282. inline void ShaderBuilder::WriteSamplerBorderColor(uint32_t borderColor,
  283. bool &first)
  284. {
  285. const bool isNotTransBlack = (borderColor & 0x000000FF) != 0;
  286. const bool isOpaqueWhite = borderColor == 0xFFFFFFFF;
  287. if (isNotTransBlack || isOpaqueWhite) {
  288. WriteSamplerParamDelimitter(first);
  289. output << "\tborder_color::";
  290. if (isOpaqueWhite)
  291. output << "opaque_white";
  292. else if (isNotTransBlack)
  293. output << "opaque_black";
  294. }
  295. }
  296. inline void ShaderBuilder::WriteSampler(struct shader_sampler *sampler)
  297. {
  298. gs_sampler_info si;
  299. shader_sampler_convert(sampler, &si);
  300. output << "constexpr sampler " << sampler->name << "(" << endl;
  301. bool isFirst = true;
  302. WriteSamplerFilter(si.filter, isFirst);
  303. WriteSamplerAddress(si.address_u, 's', isFirst);
  304. WriteSamplerAddress(si.address_v, 't', isFirst);
  305. WriteSamplerAddress(si.address_w, 'r', isFirst);
  306. WriteSamplerMaxAnisotropy(si.max_anisotropy, isFirst);
  307. WriteSamplerBorderColor(si.border_color, isFirst);
  308. output << ");" << endl << endl;
  309. }
  310. inline void ShaderBuilder::WriteSamplers()
  311. {
  312. if (isPixelShader()) {
  313. for (struct shader_sampler *sampler = parser->samplers.array;
  314. sampler != parser->samplers.array + parser->samplers.num;
  315. sampler++)
  316. WriteSampler(sampler);
  317. }
  318. }
  319. inline void ShaderBuilder::WriteStruct(const shader_struct *str)
  320. {
  321. output << "struct " << str->name << " {" << endl;
  322. size_t attributeId = 0;
  323. for (struct shader_var *var = str->vars.array;
  324. var != str->vars.array + str->vars.num;
  325. var++) {
  326. output << '\t';
  327. WriteVariable(var);
  328. const char* mapping = GetMapping(var->mapping);
  329. if (isVertexShader()) {
  330. output << " [[attribute(" << attributeId++ << ")";
  331. if (mapping != nullptr)
  332. output << ", " << mapping;
  333. output << "]]";
  334. }
  335. output << ';' << endl;
  336. }
  337. output << "};" << endl << endl;
  338. }
  339. inline void ShaderBuilder::WriteStructs()
  340. {
  341. for (struct shader_struct *str = parser->structs.array;
  342. str != parser->structs.array + parser->structs.num;
  343. str++)
  344. WriteStruct(str);
  345. }
  346. /*
  347. * NOTE: HLSL -> MSL intrinsic conversions
  348. * clip -> (unsupported)
  349. * ddx -> dfdx
  350. * ddy -> dfdy
  351. * frac -> fract
  352. * lerp -> mix
  353. * mul -> (change to operator)
  354. * Sample -> sample
  355. * SampleBias -> sample(.., bias(..))
  356. * SampleGrad -> sample(.., gradient2d(..))
  357. * SampleLevel -> sample(.., level(..))
  358. * Load -> read
  359. * A cmp B -> all(A cmp B)
  360. *
  361. * All else can be left as-is
  362. */
  363. inline bool ShaderBuilder::WriteMul(struct cf_token *&token)
  364. {
  365. struct cf_parser *cfp = &parser->cfp;
  366. cfp->cur_token = token;
  367. if (!cf_next_token(cfp)) return false;
  368. if (!cf_token_is(cfp, "(")) return false;
  369. output << '(';
  370. WriteFunctionContent(cfp->cur_token, ",");
  371. output << ") * (";
  372. cf_next_token(cfp);
  373. WriteFunctionContent(cfp->cur_token, ")");
  374. output << "))";
  375. token = cfp->cur_token;
  376. return true;
  377. }
  378. inline bool ShaderBuilder::WriteConstantVariable(struct cf_token *token)
  379. {
  380. string str(token->str.array, token->str.len);
  381. if (constantNames.find(str) != constantNames.end()) {
  382. output << "uniforms." << str;
  383. return true;
  384. }
  385. return false;
  386. }
  387. inline bool ShaderBuilder::WriteTextureCall(struct cf_token *&token,
  388. ShaderTextureCallType type)
  389. {
  390. struct cf_parser *cfp = &parser->cfp;
  391. cfp->cur_token = token;
  392. /* ( */
  393. if (!cf_next_token(cfp)) return false;
  394. if (!cf_token_is(cfp, "(")) return false;
  395. /* sampler */
  396. if (type != ShaderTextureCallType::Load) {
  397. output << "sample(";
  398. if (!cf_next_token(cfp)) return false;
  399. if (cfp->cur_token->type != CFTOKEN_NAME) return false;
  400. output.write(cfp->cur_token->str.array,
  401. cfp->cur_token->str.len);
  402. if (!cf_next_token(cfp)) return false;
  403. if (!cf_token_is(cfp, ",")) return false;
  404. output << ", ";
  405. } else
  406. output << "read((u";
  407. /* location */
  408. if (!cf_next_token(cfp)) return false;
  409. if (type != ShaderTextureCallType::Sample &&
  410. type != ShaderTextureCallType::Load) {
  411. WriteFunctionContent(cfp->cur_token, ",");
  412. /* bias, gradient2d, level */
  413. switch (type)
  414. {
  415. case ShaderTextureCallType::SampleBias:
  416. output << "bias(";
  417. if (!cf_next_token(cfp)) return false;
  418. WriteFunctionContent(cfp->cur_token, ")");
  419. output << ')';
  420. break;
  421. case ShaderTextureCallType::SampleGrad:
  422. output << "gradient2d(";
  423. if (!cf_next_token(cfp)) return false;
  424. WriteFunctionContent(cfp->cur_token, ",");
  425. if (!cf_next_token(cfp)) return false;
  426. WriteFunctionContent(cfp->cur_token, ")");
  427. output << ')';
  428. break;
  429. case ShaderTextureCallType::SampleLevel:
  430. output << "level(";
  431. if (!cf_next_token(cfp)) return false;
  432. WriteFunctionContent(cfp->cur_token, ")");
  433. output << ')';
  434. break;
  435. default:
  436. break;
  437. }
  438. } else
  439. WriteFunctionContent(cfp->cur_token, ")");
  440. /* ) */
  441. if (type == ShaderTextureCallType::Load)
  442. output << ").xy)";
  443. else
  444. output << ')';
  445. return true;
  446. }
  447. inline bool ShaderBuilder::WriteTextureCode(struct cf_token *&token,
  448. struct shader_var *var)
  449. {
  450. struct cf_parser *cfp = &parser->cfp;
  451. bool succeeded = false;
  452. cfp->cur_token = token;
  453. if (!cf_next_token(cfp)) return false;
  454. if (!cf_token_is(cfp, ".")) return false;
  455. output << var->name << ".";
  456. if (!cf_next_token(cfp)) return false;
  457. if (cf_token_is(cfp, "Sample"))
  458. succeeded = WriteTextureCall(cfp->cur_token,
  459. ShaderTextureCallType::Sample);
  460. else if (cf_token_is(cfp, "SampleBias"))
  461. succeeded = WriteTextureCall(cfp->cur_token,
  462. ShaderTextureCallType::SampleBias);
  463. else if (cf_token_is(cfp, "SampleGrad"))
  464. succeeded = WriteTextureCall(cfp->cur_token,
  465. ShaderTextureCallType::SampleGrad);
  466. else if (cf_token_is(cfp, "SampleLevel"))
  467. succeeded = WriteTextureCall(cfp->cur_token,
  468. ShaderTextureCallType::SampleLevel);
  469. else if (cf_token_is(cfp, "Load"))
  470. succeeded = WriteTextureCall(cfp->cur_token,
  471. ShaderTextureCallType::Load);
  472. if (!succeeded)
  473. throw "Failed to write texture code";
  474. token = cfp->cur_token;
  475. return true;
  476. }
  477. inline struct shader_var *ShaderBuilder::GetVariable(struct cf_token *token)
  478. {
  479. for (struct shader_var *var = parser->params.array;
  480. var != parser->params.array + parser->params.num;
  481. var++) {
  482. if (strref_cmp(&token->str, var->name) == 0)
  483. return var;
  484. }
  485. return nullptr;
  486. }
  487. inline bool ShaderBuilder::WriteIntrinsic(struct cf_token *&token)
  488. {
  489. bool written = true;
  490. if (strref_cmp(&token->str, "clip") == 0)
  491. throw "clip is not supported in Metal";
  492. else if (strref_cmp(&token->str, "ddx") == 0)
  493. output << "dfdx";
  494. else if (strref_cmp(&token->str, "ddy") == 0)
  495. output << "dfdy";
  496. else if (strref_cmp(&token->str, "frac") == 0)
  497. output << "fract";
  498. else if (strref_cmp(&token->str, "lerp") == 0)
  499. output << "mix";
  500. else if (strref_cmp(&token->str, "mul") == 0)
  501. written = WriteMul(token);
  502. else {
  503. struct shader_var *var = GetVariable(token);
  504. if (var != nullptr && astrcmp_n(var->type, "texture", 7) == 0)
  505. written = WriteTextureCode(token, var);
  506. else
  507. written = false;
  508. }
  509. return written;
  510. }
  511. inline void ShaderBuilder::AnalysisFunction(struct cf_token *&token,
  512. const char *end, ShaderFunctionInfo &info)
  513. {
  514. while (token->type != CFTOKEN_NONE) {
  515. token++;
  516. if (strref_cmp(&token->str, end) == 0)
  517. break;
  518. if (token->type == CFTOKEN_NAME) {
  519. string name(token->str.array, token->str.len);
  520. /* Check function */
  521. const auto fi = functionInfo.find(name);
  522. if (fi != functionInfo.end()) {
  523. if (fi->second.useUniform)
  524. info.useUniform = true;
  525. info.useTextures.insert(info.useTextures.end(),
  526. fi->second.useTextures.begin(),
  527. fi->second.useTextures.end());
  528. #if USE_PROGRAMMABLE_SAMPLER
  529. info.useSamplers.insert(info.useSamplers.end(),
  530. fi->second.useSamplers.begin(),
  531. fi->second.useSamplers.end());
  532. #endif
  533. continue;
  534. }
  535. /* Check UniformData */
  536. if (!info.useUniform &&
  537. constantNames.find(name) != constantNames.end()) {
  538. info.useUniform = true;
  539. continue;
  540. }
  541. /* Check texture */
  542. if (isPixelShader()) {
  543. for (auto tex = textureVars.cbegin();
  544. tex != textureVars.cend();
  545. tex++) {
  546. if (name == (*tex)->name) {
  547. info.useTextures.emplace_back(
  548. name);
  549. break;
  550. }
  551. }
  552. #if USE_PROGRAMMABLE_SAMPLER
  553. for (struct shader_sampler *sampler =
  554. parser->samplers.array;
  555. sampler != parser->samplers.array +
  556. parser->samplers.num;
  557. sampler++) {
  558. if (name == sampler->name) {
  559. info.useSamplers.emplace_back(
  560. name);
  561. break;
  562. }
  563. }
  564. #endif
  565. }
  566. } else if (token->type == CFTOKEN_OTHER) {
  567. if (*token->str.array == '{')
  568. AnalysisFunction(token, "}", info);
  569. else if (*token->str.array == '(')
  570. AnalysisFunction(token, ")", info);
  571. }
  572. }
  573. }
  574. inline void ShaderBuilder::WriteFunctionAdditionalParam(string funcionName)
  575. {
  576. auto fi = functionInfo.find(funcionName);
  577. if (fi != functionInfo.end()) {
  578. if (fi->second.useUniform)
  579. output << ", uniforms";
  580. for (auto var = textureVars.cbegin();
  581. var != textureVars.cend();
  582. var++) {
  583. for (auto tex = fi->second.useTextures.cbegin();
  584. tex != fi->second.useTextures.cend();
  585. tex++) {
  586. if (*tex == (*var)->name) {
  587. output << ", " << *tex;
  588. break;
  589. }
  590. }
  591. }
  592. #if USE_PROGRAMMABLE_SAMPLER
  593. for (struct shader_sampler *sampler = parser->samplers.array;
  594. sampler != parser->samplers.array + parser->samplers.num;
  595. sampler++) {
  596. for (auto s = fi->second.useSamplers.cbegin();
  597. s != fi->second.useSamplers.cend();
  598. s++) {
  599. if (*s == sampler->name) {
  600. output << ", " << *s;
  601. break;
  602. }
  603. }
  604. }
  605. #endif
  606. }
  607. }
  608. inline bool ShaderBuilder::IsNextCompareOperator(struct cf_token *&token)
  609. {
  610. struct cf_token *token2 = token + 1;
  611. if (token2->type != CFTOKEN_OTHER)
  612. return false;
  613. if (astrcmp_n(token2->str.array, "==", token2->str.len) == 0 ||
  614. astrcmp_n(token2->str.array, "!=", token2->str.len) == 0 ||
  615. astrcmp_n(token2->str.array, "<", token2->str.len) == 0 ||
  616. astrcmp_n(token2->str.array, "<=", token2->str.len) == 0 ||
  617. astrcmp_n(token2->str.array, ">", token2->str.len) == 0 ||
  618. astrcmp_n(token2->str.array, ">=", token2->str.len) == 0)
  619. return true;
  620. return false;
  621. }
  622. inline void ShaderBuilder::WriteFunctionContent(struct cf_token *&token,
  623. const char *end)
  624. {
  625. string temp;
  626. if (token->type != CFTOKEN_NAME)
  627. output.write(token->str.array, token->str.len);
  628. else if ((!WriteTypeToken(token) && !WriteIntrinsic(token) &&
  629. !WriteConstantVariable(token))) {
  630. temp = string(token->str.array, token->str.len);
  631. output << temp;
  632. }
  633. bool dot = false;
  634. bool cmp = false;
  635. while (token->type != CFTOKEN_NONE) {
  636. token++;
  637. if (strref_cmp(&token->str, end) == 0)
  638. break;
  639. if (token->type == CFTOKEN_NAME) {
  640. if (!WriteTypeToken(token) && !WriteIntrinsic(token) &&
  641. (dot || !WriteConstantVariable(token))) {
  642. if (dot)
  643. dot = false;
  644. bool cmp2 = IsNextCompareOperator(token);
  645. if (cmp2)
  646. output << "all(";
  647. temp = string(token->str.array, token->str.len);
  648. output << temp;
  649. if (cmp) {
  650. output << ")";
  651. cmp = false;
  652. }
  653. cmp = cmp2;
  654. }
  655. } else if (token->type == CFTOKEN_OTHER) {
  656. if (*token->str.array == '{')
  657. WriteFunctionContent(token, "}");
  658. else if (*token->str.array == '(') {
  659. WriteFunctionContent(token, ")");
  660. WriteFunctionAdditionalParam(temp);
  661. } else if (*token->str.array == '.')
  662. dot = true;
  663. output.write(token->str.array, token->str.len);
  664. } else
  665. output.write(token->str.array, token->str.len);
  666. }
  667. }
  668. inline void ShaderBuilder::WriteFunction(const shader_func *func)
  669. {
  670. string funcName(func->name);
  671. const bool isMain = funcName == "main";
  672. if (isMain) {
  673. if (isVertexShader())
  674. output << "vertex ";
  675. else if (isPixelShader())
  676. output << "fragment ";
  677. else
  678. throw "Failed to add shader prefix";
  679. funcName = "_main";
  680. }
  681. ShaderFunctionInfo info = {};
  682. struct cf_token *token = func->start;
  683. AnalysisFunction(token, "}", info);
  684. unique(info.useTextures.begin(), info.useTextures.end());
  685. unique(info.useSamplers.begin(), info.useSamplers.end());
  686. functionInfo.emplace(funcName, info);
  687. output << func->return_type << ' ' << funcName << '(';
  688. bool isFirst = true;
  689. for (struct shader_var *param = func->params.array;
  690. param != func->params.array + func->params.num;
  691. param++) {
  692. if (!isFirst)
  693. output << ", ";
  694. WriteVariable(param);
  695. if (isMain) {
  696. if (!isFirst)
  697. throw "Failed to add type";
  698. output << " [[stage_in]]";
  699. }
  700. if (isFirst)
  701. isFirst = false;
  702. }
  703. if (constantNames.size() != 0 && (isMain || info.useUniform))
  704. {
  705. if (!isFirst)
  706. output << ", ";
  707. output << "constant " << UNIFORM_DATA_NAME << " &uniforms";
  708. if (isMain)
  709. output << " [[buffer(30)]]";
  710. if (isFirst)
  711. isFirst = false;
  712. }
  713. if (isPixelShader())
  714. {
  715. size_t textureId = 0;
  716. for (auto var = textureVars.cbegin();
  717. var != textureVars.cend();
  718. var++) {
  719. if (!isMain) {
  720. bool additional = false;
  721. for (auto tex = info.useTextures.cbegin();
  722. tex != info.useTextures.cend();
  723. tex++) {
  724. if (*tex == (*var)->name) {
  725. additional = true;
  726. break;
  727. }
  728. }
  729. if (!additional)
  730. continue;
  731. }
  732. if (!isFirst)
  733. output << ", ";
  734. WriteVariable(*var);
  735. if (isMain)
  736. output << " [[texture(" << textureId++ << ")]]";
  737. if (isFirst)
  738. isFirst = false;
  739. }
  740. #if USE_PROGRAMMABLE_SAMPLER
  741. size_t samplerId = 0;
  742. for (struct shader_sampler *sampler = parser->samplers.array;
  743. sampler != parser->samplers.array + parser->samplers.num;
  744. sampler++) {
  745. if (!isMain) {
  746. bool additional = false;
  747. for (auto s = info.useSamplers.cbegin();
  748. s != info.useSamplers.cend();
  749. s++) {
  750. if (*s == sampler->name) {
  751. additional = true;
  752. break;
  753. }
  754. }
  755. if (!additional)
  756. continue;
  757. }
  758. if (!isFirst)
  759. output << ", ";
  760. output << "sampler " << sampler->name;
  761. if (isMain)
  762. output << " [[sampler(" << samplerId++ << ")]]";
  763. if (isFirst)
  764. isFirst = false;
  765. }
  766. #endif
  767. }
  768. output << ")" << endl;
  769. token = func->start;
  770. WriteFunctionContent(token, "}");
  771. output << '}' << endl << endl;
  772. }
  773. inline void ShaderBuilder::WriteFunctions()
  774. {
  775. for (struct shader_func *func = parser->funcs.array;
  776. func != parser->funcs.array + parser->funcs.num;
  777. func++)
  778. WriteFunction(func);
  779. }
  780. void ShaderBuilder::Build(string &outputString)
  781. {
  782. WriteInclude();
  783. #if !USE_PROGRAMMABLE_SAMPLER
  784. WriteSamplers();
  785. #endif
  786. WriteVariables();
  787. WriteStructs();
  788. WriteFunctions();
  789. outputString = string(output.str(), output.pcount());
  790. output.freeze(false);
  791. }
  792. string build_shader(gs_shader_type type, ShaderParser *parser)
  793. {
  794. string output;
  795. ShaderBuilder(type, parser).Build(output);
  796. return output;
  797. }