ShaderProgramCompilerMain.cpp 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  1. // Copyright (C) 2009-present, Panagiotis Christopoulos Charitos and contributors.
  2. // All rights reserved.
  3. // Code licensed under the BSD License.
  4. // http://www.anki3d.org/LICENSE
  5. #include <AnKi/ShaderCompiler/ShaderCompiler.h>
  6. #include <AnKi/Util.h>
  7. using namespace anki;
  8. static constexpr const char* kUsage = R"(Compile an AnKi shader program
  9. Usage: %s [options] input_shader_program_file
  10. Options:
  11. -o <name of output> : The name of the output binary
  12. -j <thread count> : Number of threads. Defaults to system's max
  13. -I <include path> : The path of the #include files
  14. -D<define_name:val> : Extra defines to pass to the compiler
  15. -spirv : Compile SPIR-V
  16. -dxil : Compile DXIL
  17. -g : Include debug info
  18. -sm : Shader mode. "6_7" or "6_8". Default "6_8"
  19. )";
  20. class CmdLineArgs
  21. {
  22. public:
  23. String m_inputFname;
  24. String m_outFname;
  25. String m_includePath;
  26. U32 m_threadCount = getCpuCoresCount();
  27. DynamicArray<String> m_defineNames;
  28. DynamicArray<ShaderCompilerDefine> m_defines;
  29. Bool m_spirv = false;
  30. Bool m_dxil = false;
  31. Bool m_debugInfo = false;
  32. ShaderModel m_sm = ShaderModel::k6_8;
  33. };
  34. static Error parseCommandLineArgs(int argc, char** argv, CmdLineArgs& info)
  35. {
  36. // Parse config
  37. if(argc < 2)
  38. {
  39. return Error::kUserData;
  40. }
  41. for(I i = 1; i < argc - 1; i++)
  42. {
  43. if(strcmp(argv[i], "-o") == 0)
  44. {
  45. ++i;
  46. if(i < argc)
  47. {
  48. if(std::strlen(argv[i]) > 0)
  49. {
  50. info.m_outFname.sprintf("%s", argv[i]);
  51. }
  52. else
  53. {
  54. return Error::kUserData;
  55. }
  56. }
  57. else
  58. {
  59. return Error::kUserData;
  60. }
  61. }
  62. else if(strcmp(argv[i], "-j") == 0)
  63. {
  64. ++i;
  65. if(i < argc)
  66. {
  67. ANKI_CHECK(CString(argv[i]).toNumber(info.m_threadCount));
  68. }
  69. else
  70. {
  71. return Error::kUserData;
  72. }
  73. }
  74. else if(strcmp(argv[i], "-I") == 0)
  75. {
  76. ++i;
  77. if(i < argc)
  78. {
  79. if(std::strlen(argv[i]) > 0)
  80. {
  81. info.m_includePath.sprintf("%s", argv[i]);
  82. }
  83. else
  84. {
  85. return Error::kUserData;
  86. }
  87. }
  88. else
  89. {
  90. return Error::kUserData;
  91. }
  92. }
  93. else if(CString(argv[i]).find("-D") == 0)
  94. {
  95. CString a = argv[i];
  96. if(a.getLength() < 5)
  97. {
  98. return Error::kUserData;
  99. }
  100. const String arg(a.getBegin() + 2, a.getEnd());
  101. StringList tokens;
  102. tokens.splitString(arg, '=');
  103. if(tokens.getSize() != 2)
  104. {
  105. return Error::kUserData;
  106. }
  107. info.m_defineNames.emplaceBack(tokens.getFront());
  108. I32 val;
  109. const Error err = (tokens.getBegin() + 1)->toNumber(val);
  110. if(err)
  111. {
  112. return Error::kUserData;
  113. }
  114. info.m_defines.emplaceBack(ShaderCompilerDefine{info.m_defineNames.getBack().toCString(), val});
  115. }
  116. else if(strcmp(argv[i], "-spirv") == 0)
  117. {
  118. info.m_spirv = true;
  119. }
  120. else if(strcmp(argv[i], "-dxil") == 0)
  121. {
  122. info.m_dxil = true;
  123. }
  124. else if(strcmp(argv[i], "-g") == 0)
  125. {
  126. info.m_debugInfo = true;
  127. }
  128. else if(strcmp(argv[i], "-sm") == 0)
  129. {
  130. ++i;
  131. if(i < argc)
  132. {
  133. if(argv[i] == CString("6_7"))
  134. {
  135. info.m_sm = ShaderModel::k6_7;
  136. }
  137. else if(argv[i] == CString("6_8"))
  138. {
  139. info.m_sm = ShaderModel::k6_8;
  140. }
  141. else
  142. {
  143. return Error::kUserData;
  144. }
  145. }
  146. else
  147. {
  148. return Error::kUserData;
  149. }
  150. }
  151. else
  152. {
  153. return Error::kUserData;
  154. }
  155. }
  156. info.m_inputFname = argv[argc - 1];
  157. return Error::kNone;
  158. }
  159. static Error work(const CmdLineArgs& info)
  160. {
  161. HeapMemoryPool pool(allocAligned, nullptr, "ProgramPool");
  162. // Load interface
  163. class FSystem : public ShaderCompilerFilesystemInterface
  164. {
  165. public:
  166. CString m_includePath;
  167. U32 m_fileReadCount = 0;
  168. Error readAllTextInternal(CString filename, ShaderCompilerString& txt)
  169. {
  170. String fname;
  171. // The first file is the input file. Don't append the include path to it
  172. if(m_fileReadCount == 0)
  173. {
  174. fname.sprintf("%s", filename.cstr());
  175. }
  176. else
  177. {
  178. fname.sprintf("%s/%s", m_includePath.cstr(), filename.cstr());
  179. }
  180. ++m_fileReadCount;
  181. File file;
  182. ANKI_CHECK(file.open(fname, FileOpenFlag::kRead));
  183. ANKI_CHECK(file.readAllText(txt));
  184. return Error::kNone;
  185. }
  186. Error readAllText(CString filename, ShaderCompilerString& txt) final
  187. {
  188. const Error err = readAllTextInternal(filename, txt);
  189. if(err)
  190. {
  191. ANKI_LOGE("Failed to read file: %s", filename.cstr());
  192. }
  193. return err;
  194. }
  195. } fsystem;
  196. fsystem.m_includePath = info.m_includePath;
  197. // Threading interface
  198. class TaskManager : public ShaderCompilerAsyncTaskInterface
  199. {
  200. public:
  201. UniquePtr<ThreadJobManager, SingletonMemoryPoolDeleter<DefaultMemoryPool>> m_jobManager;
  202. void enqueueTask(void (*callback)(void* userData), void* userData) final
  203. {
  204. m_jobManager->dispatchTask([callback, userData]([[maybe_unused]] U32 threadIdx) {
  205. callback(userData);
  206. });
  207. }
  208. Error joinTasks()
  209. {
  210. m_jobManager->waitForAllTasksToFinish();
  211. return Error::kNone;
  212. }
  213. } taskManager;
  214. taskManager.m_jobManager.reset((info.m_threadCount) ? newInstance<ThreadJobManager>(DefaultMemoryPool::getSingleton(), info.m_threadCount, true)
  215. : nullptr);
  216. // Compile
  217. ShaderBinary* binary = nullptr;
  218. ANKI_CHECK(compileShaderProgram(info.m_inputFname, info.m_spirv, info.m_debugInfo, info.m_sm, fsystem, nullptr,
  219. (info.m_threadCount) ? &taskManager : nullptr, info.m_defines, binary));
  220. class Dummy
  221. {
  222. public:
  223. ShaderBinary* m_binary;
  224. ~Dummy()
  225. {
  226. freeShaderBinary(m_binary);
  227. }
  228. } dummy{binary};
  229. // Store the binary
  230. {
  231. File file;
  232. ANKI_CHECK(file.open(info.m_outFname, FileOpenFlag::kWrite | FileOpenFlag::kBinary));
  233. BinarySerializer serializer;
  234. ANKI_CHECK(serializer.serialize(*binary, ShaderCompilerMemoryPool::getSingleton(), file));
  235. }
  236. return Error::kNone;
  237. }
  238. ANKI_MAIN_FUNCTION(myMain)
  239. int myMain(int argc, char** argv)
  240. {
  241. class Dummy
  242. {
  243. public:
  244. ~Dummy()
  245. {
  246. DefaultMemoryPool::freeSingleton();
  247. ShaderCompilerMemoryPool::freeSingleton();
  248. }
  249. } dummy;
  250. DefaultMemoryPool::allocateSingleton(allocAligned, nullptr);
  251. ShaderCompilerMemoryPool::allocateSingleton(allocAligned, nullptr);
  252. CmdLineArgs info;
  253. if(parseCommandLineArgs(argc, argv, info))
  254. {
  255. ANKI_LOGE(kUsage, argv[0]);
  256. return 1;
  257. }
  258. if(info.m_spirv == info.m_dxil)
  259. {
  260. ANKI_LOGE(kUsage, argv[0]);
  261. return 1;
  262. }
  263. if(info.m_outFname.isEmpty())
  264. {
  265. getFilepathFilename(info.m_inputFname, info.m_outFname);
  266. info.m_outFname += "bin";
  267. }
  268. if(info.m_includePath.isEmpty())
  269. {
  270. info.m_includePath = "./";
  271. }
  272. if(work(info))
  273. {
  274. ANKI_LOGE("Compilation failed");
  275. return 1;
  276. }
  277. return 0;
  278. }