shaderc.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664
  1. /*
  2. * Copyright 2011-2012 Branimir Karadzic. All rights reserved.
  3. */
  4. #include <stdio.h>
  5. #include <stdint.h>
  6. #include <stdlib.h>
  7. #include <string.h>
  8. #include <string>
  9. #include <vector>
  10. #include "glsl_optimizer.h"
  11. #define MAX_TAGS 256
  12. extern "C" {
  13. # include <fpp.h>
  14. } // extern "C"
  15. #if 0
  16. # define BX_TRACE(_format, ...) fprintf(stderr, "" _format "\n", ##__VA_ARGS__)
  17. #endif // DEBUG
  18. #define BX_NAMESPACE 1
  19. #include <bx/bx.h>
  20. #if BX_PLATFORM_LINUX
  21. # define _stricmp strcasecmp
  22. #endif // BX_PLATFORM_LINUX
  23. #include <bx/commandline.h>
  24. #include <bx/countof.h>
  25. #include <bx/endian.h>
  26. #include <bx/uint32_t.h>
  27. #if BX_PLATFORM_WINDOWS
  28. # include <d3dx9.h>
  29. #endif // BX_PLATFORM_WINDOWS
  30. long int fsize(FILE* _file)
  31. {
  32. long int pos = ftell(_file);
  33. fseek(_file, 0L, SEEK_END);
  34. long int size = ftell(_file);
  35. fseek(_file, pos, SEEK_SET);
  36. return size;
  37. }
  38. struct ConstantType
  39. {
  40. enum Enum
  41. {
  42. Uniform1i,
  43. Uniform1f,
  44. End,
  45. Uniform1iv,
  46. Uniform1fv,
  47. Uniform2fv,
  48. Uniform3fv,
  49. Uniform4fv,
  50. Uniform3x3fv,
  51. Uniform4x4fv,
  52. Count,
  53. TypeMask = 0x7f,
  54. FragmentBit = 0x80
  55. };
  56. };
  57. static const char* s_constantTypeName[ConstantType::Count] =
  58. {
  59. "int",
  60. "float",
  61. NULL,
  62. "int",
  63. "float",
  64. "float2",
  65. "float3",
  66. "float4",
  67. "float3x3",
  68. "float4x4",
  69. };
  70. struct Uniform
  71. {
  72. std::string name;
  73. ConstantType::Enum type;
  74. uint8_t num;
  75. uint16_t regIndex;
  76. uint16_t regCount;
  77. };
  78. typedef std::vector<Uniform> UniformArray;
  79. #if BX_PLATFORM_WINDOWS
  80. struct ConstRemap
  81. {
  82. ConstantType::Enum id;
  83. D3DXPARAMETER_CLASS paramClass;
  84. D3DXPARAMETER_TYPE paramType;
  85. uint32_t paramBytes;
  86. };
  87. static const ConstRemap s_constRemap[7] =
  88. {
  89. { ConstantType::Uniform1iv, D3DXPC_SCALAR, D3DXPT_INT, 4 },
  90. { ConstantType::Uniform1fv, D3DXPC_SCALAR, D3DXPT_FLOAT, 4 },
  91. { ConstantType::Uniform2fv, D3DXPC_VECTOR, D3DXPT_FLOAT, 8 },
  92. { ConstantType::Uniform3fv, D3DXPC_VECTOR, D3DXPT_FLOAT, 12 },
  93. { ConstantType::Uniform4fv, D3DXPC_VECTOR, D3DXPT_FLOAT, 16 },
  94. { ConstantType::Uniform3x3fv, D3DXPC_MATRIX_COLUMNS, D3DXPT_FLOAT, 36 },
  95. { ConstantType::Uniform4x4fv, D3DXPC_MATRIX_COLUMNS, D3DXPT_FLOAT, 64 },
  96. };
  97. ConstantType::Enum findConstantType(const D3DXCONSTANT_DESC& constDesc)
  98. {
  99. uint32_t count = sizeof(s_constRemap)/sizeof(ConstRemap);
  100. for (uint32_t ii = 0; ii < count; ++ii)
  101. {
  102. const ConstRemap& remap = s_constRemap[ii];
  103. if (remap.paramClass == constDesc.Class
  104. && remap.paramType == constDesc.Type
  105. && (constDesc.Bytes%remap.paramBytes) == 0)
  106. {
  107. return remap.id;
  108. }
  109. }
  110. return ConstantType::Count;
  111. }
  112. static uint32_t s_optimizationLevel[4] =
  113. {
  114. D3DXSHADER_OPTIMIZATION_LEVEL0,
  115. D3DXSHADER_OPTIMIZATION_LEVEL1,
  116. D3DXSHADER_OPTIMIZATION_LEVEL2,
  117. D3DXSHADER_OPTIMIZATION_LEVEL3,
  118. };
  119. #endif // BX_PLATFORM_WINDOWS
  120. class Stream
  121. {
  122. public:
  123. Stream(FILE* _file, bool _bigEndian = false)
  124. : m_file(_file)
  125. , m_bigEndian(_bigEndian)
  126. {
  127. }
  128. ~Stream()
  129. {
  130. }
  131. void close()
  132. {
  133. m_file = NULL;
  134. }
  135. void write(const char* _str)
  136. {
  137. if (NULL != m_file)
  138. {
  139. fwrite(_str, strlen(_str), 1, m_file);
  140. }
  141. }
  142. void write(const void* _data, size_t _size)
  143. {
  144. if (NULL != m_file)
  145. {
  146. fwrite(_data, _size, 1, m_file);
  147. }
  148. }
  149. template<typename Ty>
  150. void write(Ty _value)
  151. {
  152. Ty temp = m_bigEndian ? bx::bigEndian<Ty>(_value) : _value;
  153. write(&temp, sizeof(Ty) );
  154. }
  155. private:
  156. FILE* m_file;
  157. bool m_bigEndian;
  158. };
  159. bool compileGLSLShader(CommandLine& _cmdLine, const std::string& _code, const char* _outFilePath)
  160. {
  161. const glslopt_shader_type type = (0 == _stricmp(_cmdLine.findOption('\0', "type"), "fragment") ) ? kGlslOptShaderFragment : kGlslOptShaderVertex;
  162. glslopt_ctx* ctx = glslopt_initialize(false);
  163. glslopt_shader* shader = glslopt_optimize(ctx, type, _code.c_str(), 0);
  164. if( !glslopt_get_status(shader) )
  165. {
  166. fprintf(stderr, "Error %s\n%s\n", _code.c_str(), glslopt_get_log(shader) );
  167. glslopt_cleanup(ctx);
  168. return false;
  169. }
  170. const char* optimizedShader = glslopt_get_output(shader);
  171. FILE* out = fopen(_outFilePath, "wb");
  172. if (NULL == out)
  173. {
  174. fprintf(stderr, "Unable to open output file '%s'.", _outFilePath);
  175. glslopt_cleanup(ctx);
  176. return false;
  177. }
  178. Stream stream(out);
  179. stream.write("precision highp float;\n\n");
  180. stream.write(optimizedShader, strlen(optimizedShader) );
  181. uint8_t nul = 0;
  182. stream.write(nul);
  183. stream.close();
  184. fclose(out);
  185. glslopt_cleanup(ctx);
  186. return true;
  187. }
  188. bool compileHLSLShader(CommandLine& _cmdLine, const std::string& _code, const char* _outFilePath)
  189. {
  190. #if BX_PLATFORM_WINDOWS
  191. const char* profile = _cmdLine.findOption('p');
  192. if (NULL == profile)
  193. {
  194. printf("Shader profile must be specified.\n");
  195. return false;
  196. }
  197. bool bigEndian = _cmdLine.hasArg('\0', "xbox360");
  198. uint32_t flags = 0;
  199. flags |= _cmdLine.hasArg('\0', "debug") ? D3DXSHADER_DEBUG : 0;
  200. flags |= _cmdLine.hasArg('\0', "avoid-flow-control") ? D3DXSHADER_AVOID_FLOW_CONTROL : 0;
  201. flags |= _cmdLine.hasArg('\0', "no-preshader") ? D3DXSHADER_NO_PRESHADER : 0;
  202. flags |= _cmdLine.hasArg('\0', "partial-precision") ? D3DXSHADER_PARTIALPRECISION : 0;
  203. flags |= _cmdLine.hasArg('\0', "prefer-flow-control") ? D3DXSHADER_PREFER_FLOW_CONTROL : 0;
  204. uint32_t optimization = 3;
  205. if (_cmdLine.hasArg(optimization, 'O') )
  206. {
  207. optimization = bx::uint32_min(optimization, countof(s_optimizationLevel)-1);
  208. flags |= s_optimizationLevel[optimization];
  209. }
  210. else
  211. {
  212. flags |= D3DXSHADER_SKIPOPTIMIZATION;
  213. }
  214. BX_TRACE("Profile: %s", profile);
  215. BX_TRACE("Flags: 0x%08x", flags);
  216. BX_TRACE("Big Endian: %s", bigEndian?"true":"false");
  217. LPD3DXBUFFER code;
  218. LPD3DXBUFFER errorMsg;
  219. LPD3DXCONSTANTTABLE constantTable;
  220. HRESULT hr = D3DXCompileShader(_code.c_str()
  221. , _code.size()
  222. , NULL
  223. , NULL
  224. , "main"
  225. , profile
  226. , flags
  227. , &code
  228. , &errorMsg
  229. , &constantTable
  230. );
  231. if (FAILED(hr) )
  232. {
  233. fprintf(stderr, "0x%08x: %s\n", hr, errorMsg->GetBufferPointer() );
  234. return false;
  235. }
  236. D3DXCONSTANTTABLE_DESC desc;
  237. hr = constantTable->GetDesc(&desc);
  238. if (FAILED(hr) )
  239. {
  240. fprintf(stderr, "Error 0x%08x\n", hr);
  241. return false;
  242. }
  243. BX_TRACE("Creator: %s 0x%08x", desc.Creator, desc.Version);
  244. BX_TRACE("Num constants: %d", desc.Constants);
  245. BX_TRACE("# cl ty RxC S By Name");
  246. UniformArray uniforms;
  247. for (uint32_t ii = 0; ii < desc.Constants; ++ii)
  248. {
  249. D3DXHANDLE handle = constantTable->GetConstant(NULL, ii);
  250. D3DXCONSTANT_DESC constDesc;
  251. uint32_t count;
  252. constantTable->GetConstantDesc(handle, &constDesc, &count);
  253. BX_TRACE("%3d %2d %2d [%dx%d] %d %3d %s[%d] c%d (%d)"
  254. , ii
  255. , constDesc.Class
  256. , constDesc.Type
  257. , constDesc.Rows
  258. , constDesc.Columns
  259. , constDesc.StructMembers
  260. , constDesc.Bytes
  261. , constDesc.Name
  262. , constDesc.Elements
  263. , constDesc.RegisterIndex
  264. , constDesc.RegisterCount
  265. );
  266. ConstantType::Enum type = findConstantType(constDesc);
  267. if (ConstantType::Count != type)
  268. {
  269. Uniform un;
  270. un.name = '$' == constDesc.Name[0] ? constDesc.Name+1 : constDesc.Name;
  271. un.type = type;
  272. un.num = constDesc.Elements;
  273. un.regIndex = constDesc.RegisterIndex;
  274. un.regCount = constDesc.RegisterCount;
  275. uniforms.push_back(un);
  276. }
  277. }
  278. FILE* out = fopen(_outFilePath, "wb");
  279. if (NULL == out)
  280. {
  281. fprintf(stderr, "Unable to open output file '%s'.", _outFilePath);
  282. return false;
  283. }
  284. Stream stream(out, bigEndian);
  285. uint16_t count = (uint16_t)uniforms.size();
  286. stream.write(count);
  287. uint32_t fragmentBit = profile[0] == 'p' ? ConstantType::FragmentBit : 0;
  288. for (UniformArray::const_iterator it = uniforms.begin(); it != uniforms.end(); ++it)
  289. {
  290. const Uniform& un = *it;
  291. uint8_t nameSize = (uint8_t)un.name.size();
  292. stream.write(nameSize);
  293. stream.write(un.name.c_str(), nameSize);
  294. stream.write<uint8_t>(un.type|fragmentBit);
  295. stream.write(un.num);
  296. stream.write(un.regIndex);
  297. stream.write(un.regCount);
  298. BX_TRACE("%s, %s, %d, %d, %d"
  299. , un.name.c_str()
  300. , s_constantTypeName[un.type]
  301. , un.num
  302. , un.regIndex
  303. , un.regCount
  304. );
  305. }
  306. uint16_t shaderSize = (uint16_t)code->GetBufferSize();
  307. stream.write(shaderSize);
  308. stream.write(code->GetBufferPointer(), shaderSize);
  309. uint8_t nul = 0;
  310. stream.write(nul);
  311. stream.close();
  312. fclose(out);
  313. if (NULL != code)
  314. {
  315. code->Release();
  316. }
  317. if (NULL != errorMsg)
  318. {
  319. errorMsg->Release();
  320. }
  321. if (NULL != constantTable)
  322. {
  323. constantTable->Release();
  324. }
  325. return true;
  326. #else
  327. fprintf(stderr, "HLSL compiler is not supported on this platform.\n");
  328. return false;
  329. #endif // BX_PLATFORM_WINDOWS
  330. }
  331. struct Preprocessor
  332. {
  333. Preprocessor(const char* _filePath)
  334. : m_tagptr(m_tags)
  335. , m_scratchPos(0)
  336. , m_fgetsPos(0)
  337. {
  338. m_filePath = scratch(_filePath);
  339. m_tagptr->tag = FPPTAG_USERDATA;
  340. m_tagptr->data = this;
  341. m_tagptr++;
  342. m_tagptr->tag = FPPTAG_INPUT;
  343. m_tagptr->data = (void*)fppInput;
  344. m_tagptr++;
  345. m_tagptr->tag = FPPTAG_OUTPUT;
  346. m_tagptr->data = (void*)fppOutput;
  347. m_tagptr++;
  348. m_tagptr->tag = FPPTAG_ERROR;
  349. m_tagptr->data = (void*)fppError;
  350. m_tagptr++;
  351. m_tagptr->tag = FPPTAG_IGNOREVERSION;
  352. m_tagptr->data = (void*)0;
  353. m_tagptr++;
  354. m_tagptr->tag = FPPTAG_LINE;
  355. m_tagptr->data = (void*)0;
  356. m_tagptr++;
  357. m_tagptr->tag = FPPTAG_INPUT_NAME;
  358. m_tagptr->data = m_filePath;
  359. m_tagptr++;
  360. m_default = "#define lowp\n#define mediump\n#define highp\n";
  361. }
  362. void setDefine(const char* _define)
  363. {
  364. m_tagptr->tag = FPPTAG_DEFINE;
  365. m_tagptr->data = scratch(_define);
  366. m_tagptr++;
  367. }
  368. void setDefaultDefine(const char* _name)
  369. {
  370. char temp[1024];
  371. _snprintf(temp, countof(temp)
  372. , "#ifndef %s\n"
  373. "# define %s 0\n"
  374. "#endif // %s\n"
  375. "\n"
  376. , _name
  377. , _name
  378. , _name
  379. );
  380. m_default += temp;
  381. }
  382. bool run()
  383. {
  384. m_fgetsPos = 0;
  385. FILE* file = fopen(m_filePath, "r");
  386. long int size = fsize(file);
  387. char* input = new char[size+1];
  388. fread(input, size, 1, file);
  389. input[size] = '\0';
  390. m_input = m_default;
  391. m_input += input;
  392. fclose(file);
  393. fppTag* tagptr = m_tagptr;
  394. tagptr->tag = FPPTAG_END;
  395. tagptr->data = 0;
  396. tagptr++;
  397. int result = fppPreProcess(m_tags);
  398. return 0 == result;
  399. }
  400. char* fgets(char* _buffer, int _size)
  401. {
  402. int ii = 0;
  403. for (char ch = m_input[m_fgetsPos]; m_fgetsPos < m_input.size() && ii < _size-1; ch = m_input[++m_fgetsPos])
  404. {
  405. _buffer[ii++] = ch;
  406. if (ch == '\n' || ii == _size)
  407. {
  408. _buffer[ii] = '\0';
  409. m_fgetsPos++;
  410. return _buffer;
  411. }
  412. }
  413. return NULL;
  414. }
  415. static char* fppInput(char* _buffer, int _size, void* _userData)
  416. {
  417. Preprocessor* thisClass = (Preprocessor*)_userData;
  418. return thisClass->fgets(_buffer, _size);
  419. }
  420. static void fppOutput(int _ch, void* _userData)
  421. {
  422. Preprocessor* thisClass = (Preprocessor*)_userData;
  423. thisClass->m_preprocessed += _ch;
  424. }
  425. static void fppError(void* _userData, char* _format, va_list _vargs)
  426. {
  427. vfprintf(stderr, _format, _vargs);
  428. }
  429. char* scratch(const char* _str)
  430. {
  431. char* result = &m_scratch[m_scratchPos];
  432. strcpy(result, _str);
  433. m_scratchPos += strlen(_str)+1;
  434. return result;
  435. }
  436. fppTag m_tags[MAX_TAGS];
  437. fppTag* m_tagptr;
  438. char* m_filePath;
  439. std::string m_default;
  440. std::string m_input;
  441. std::string m_preprocessed;
  442. char m_scratch[16<<10];
  443. uint32_t m_scratchPos;
  444. uint32_t m_fgetsPos;
  445. };
  446. int main(int _argc, const char* _argv[])
  447. {
  448. CommandLine cmdLine(_argc, _argv);
  449. const char* filePath = cmdLine.findOption('f');
  450. if (NULL == filePath)
  451. {
  452. fprintf(stderr, "Shader file name must be specified.\n");
  453. return 1;
  454. }
  455. const char* outFilePath = cmdLine.findOption('o');
  456. if (NULL == outFilePath)
  457. {
  458. fprintf(stderr, "Output file name must be specified.\n");
  459. return 1;
  460. }
  461. const char* type = cmdLine.findOption('\0', "type");
  462. if (NULL == type)
  463. {
  464. fprintf(stderr, "Must specify shader type.");
  465. return 1;
  466. }
  467. const char* platform = cmdLine.findOption('\0', "platform");
  468. if (NULL == platform)
  469. {
  470. fprintf(stderr, "Must specify platform.\n");
  471. return 1;
  472. }
  473. bool preprocessOnly = cmdLine.hasArg("preprocess");
  474. Preprocessor preprocessor(filePath);
  475. preprocessor.setDefaultDefine("BX_PLATFORM_ANDROID");
  476. preprocessor.setDefaultDefine("BX_PLATFORM_NACL");
  477. preprocessor.setDefaultDefine("BX_PLATFORM_WINDOWS");
  478. preprocessor.setDefaultDefine("BX_PLATFORM_XBOX360");
  479. preprocessor.setDefaultDefine("BGFX_SHADER_LANGUAGE_GLSL");
  480. preprocessor.setDefaultDefine("BGFX_SHADER_LANGUAGE_HLSL");
  481. preprocessor.setDefaultDefine("BGFX_SHADER_TYPE_FRAGMENT");
  482. preprocessor.setDefaultDefine("BGFX_SHADER_TYPE_VERTEX");
  483. bool glsl = false;
  484. if (0 == _stricmp(platform, "android") )
  485. {
  486. preprocessor.setDefine("BX_PLATFORM_ANDROID=1");
  487. preprocessor.setDefine("BGFX_SHADER_LANGUAGE_GLSL=1");
  488. glsl = true;
  489. }
  490. else if (0 == _stricmp(platform, "nacl") )
  491. {
  492. preprocessor.setDefine("BX_PLATFORM_NACL=1");
  493. preprocessor.setDefine("BGFX_SHADER_LANGUAGE_GLSL=1");
  494. glsl = true;
  495. }
  496. else if (0 == _stricmp(platform, "windows") )
  497. {
  498. preprocessor.setDefine("BX_PLATFORM_WINDOWS=1");
  499. preprocessor.setDefine("BGFX_SHADER_LANGUAGE_HLSL=1");
  500. }
  501. else if (0 == _stricmp(platform, "xbox360") )
  502. {
  503. preprocessor.setDefine("BX_PLATFORM_XBOX360=1");
  504. preprocessor.setDefine("BGFX_SHADER_LANGUAGE_HLSL=1");
  505. }
  506. else
  507. {
  508. fprintf(stderr, "Unknown platform %s?!", platform);
  509. return 1;
  510. }
  511. if (0 == _stricmp(type, "fragment") )
  512. {
  513. preprocessor.setDefine("BGFX_SHADER_TYPE_FRAGMENT=1");
  514. }
  515. else
  516. {
  517. preprocessor.setDefine("BGFX_SHADER_TYPE_VERTEX=1");
  518. }
  519. if (preprocessor.run() )
  520. {
  521. BX_TRACE("Input file: %s", filePath);
  522. BX_TRACE("Output file: %s", outFilePath);
  523. if (preprocessOnly)
  524. {
  525. FILE* out = fopen(outFilePath, "wb");
  526. if (NULL == out)
  527. {
  528. fprintf(stderr, "Unable to open output file '%s'.", outFilePath);
  529. return false;
  530. }
  531. Stream stream(out);
  532. if (glsl)
  533. {
  534. stream.write("precision highp float;\n\n");
  535. }
  536. stream.write(preprocessor.m_preprocessed.c_str(), preprocessor.m_preprocessed.size() );
  537. stream.close();
  538. fclose(out);
  539. return EXIT_SUCCESS;
  540. }
  541. if (glsl)
  542. {
  543. if (compileGLSLShader(cmdLine, preprocessor.m_preprocessed, outFilePath) )
  544. {
  545. return EXIT_SUCCESS;
  546. }
  547. }
  548. else
  549. {
  550. if (compileHLSLShader(cmdLine, preprocessor.m_preprocessed, outFilePath) )
  551. {
  552. return EXIT_SUCCESS;
  553. }
  554. }
  555. }
  556. fprintf(stderr, "Failed to build shader.\n");
  557. return EXIT_FAILURE;
  558. }