ShaderProgramCompilerMain.cpp 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  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. )";
  19. class CmdLineArgs
  20. {
  21. public:
  22. String m_inputFname;
  23. String m_outFname;
  24. String m_includePath;
  25. U32 m_threadCount = getCpuCoresCount();
  26. DynamicArray<String> m_defineNames;
  27. DynamicArray<ShaderCompilerDefine> m_defines;
  28. Bool m_spirv = false;
  29. Bool m_dxil = false;
  30. Bool m_debugInfo = false;
  31. };
  32. static Error parseCommandLineArgs(int argc, char** argv, CmdLineArgs& info)
  33. {
  34. // Parse config
  35. if(argc < 2)
  36. {
  37. return Error::kUserData;
  38. }
  39. for(I i = 1; i < argc - 1; i++)
  40. {
  41. if(strcmp(argv[i], "-o") == 0)
  42. {
  43. ++i;
  44. if(i < argc)
  45. {
  46. if(std::strlen(argv[i]) > 0)
  47. {
  48. info.m_outFname.sprintf("%s", argv[i]);
  49. }
  50. else
  51. {
  52. return Error::kUserData;
  53. }
  54. }
  55. else
  56. {
  57. return Error::kUserData;
  58. }
  59. }
  60. else if(strcmp(argv[i], "-j") == 0)
  61. {
  62. ++i;
  63. if(i < argc)
  64. {
  65. ANKI_CHECK(CString(argv[i]).toNumber(info.m_threadCount));
  66. }
  67. else
  68. {
  69. return Error::kUserData;
  70. }
  71. }
  72. else if(strcmp(argv[i], "-I") == 0)
  73. {
  74. ++i;
  75. if(i < argc)
  76. {
  77. if(std::strlen(argv[i]) > 0)
  78. {
  79. info.m_includePath.sprintf("%s", argv[i]);
  80. }
  81. else
  82. {
  83. return Error::kUserData;
  84. }
  85. }
  86. else
  87. {
  88. return Error::kUserData;
  89. }
  90. }
  91. else if(CString(argv[i]).find("-D") == 0)
  92. {
  93. CString a = argv[i];
  94. if(a.getLength() < 5)
  95. {
  96. return Error::kUserData;
  97. }
  98. const String arg(a.getBegin() + 2, a.getEnd());
  99. StringList tokens;
  100. tokens.splitString(arg, '=');
  101. if(tokens.getSize() != 2)
  102. {
  103. return Error::kUserData;
  104. }
  105. info.m_defineNames.emplaceBack(tokens.getFront());
  106. I32 val;
  107. const Error err = (tokens.getBegin() + 1)->toNumber(val);
  108. if(err)
  109. {
  110. return Error::kUserData;
  111. }
  112. info.m_defines.emplaceBack(ShaderCompilerDefine{info.m_defineNames.getBack().toCString(), val});
  113. }
  114. else if(strcmp(argv[i], "-spirv") == 0)
  115. {
  116. info.m_spirv = true;
  117. }
  118. else if(strcmp(argv[i], "-dxil") == 0)
  119. {
  120. info.m_dxil = true;
  121. }
  122. else if(strcmp(argv[i], "-g") == 0)
  123. {
  124. info.m_debugInfo = true;
  125. }
  126. else
  127. {
  128. return Error::kUserData;
  129. }
  130. }
  131. info.m_inputFname = argv[argc - 1];
  132. return Error::kNone;
  133. }
  134. static Error work(const CmdLineArgs& info)
  135. {
  136. HeapMemoryPool pool(allocAligned, nullptr, "ProgramPool");
  137. // Load interface
  138. class FSystem : public ShaderCompilerFilesystemInterface
  139. {
  140. public:
  141. CString m_includePath;
  142. U32 m_fileReadCount = 0;
  143. Error readAllTextInternal(CString filename, ShaderCompilerString& txt)
  144. {
  145. String fname;
  146. // The first file is the input file. Don't append the include path to it
  147. if(m_fileReadCount == 0)
  148. {
  149. fname.sprintf("%s", filename.cstr());
  150. }
  151. else
  152. {
  153. fname.sprintf("%s/%s", m_includePath.cstr(), filename.cstr());
  154. }
  155. ++m_fileReadCount;
  156. File file;
  157. ANKI_CHECK(file.open(fname, FileOpenFlag::kRead));
  158. ANKI_CHECK(file.readAllText(txt));
  159. return Error::kNone;
  160. }
  161. Error readAllText(CString filename, ShaderCompilerString& txt) final
  162. {
  163. const Error err = readAllTextInternal(filename, txt);
  164. if(err)
  165. {
  166. ANKI_LOGE("Failed to read file: %s", filename.cstr());
  167. }
  168. return err;
  169. }
  170. } fsystem;
  171. fsystem.m_includePath = info.m_includePath;
  172. // Threading interface
  173. class TaskManager : public ShaderCompilerAsyncTaskInterface
  174. {
  175. public:
  176. UniquePtr<ThreadJobManager, SingletonMemoryPoolDeleter<DefaultMemoryPool>> m_jobManager;
  177. void enqueueTask(void (*callback)(void* userData), void* userData) final
  178. {
  179. m_jobManager->dispatchTask([callback, userData]([[maybe_unused]] U32 threadIdx) {
  180. callback(userData);
  181. });
  182. }
  183. Error joinTasks()
  184. {
  185. m_jobManager->waitForAllTasksToFinish();
  186. return Error::kNone;
  187. }
  188. } taskManager;
  189. taskManager.m_jobManager.reset((info.m_threadCount) ? newInstance<ThreadJobManager>(DefaultMemoryPool::getSingleton(), info.m_threadCount, true)
  190. : nullptr);
  191. // Compile
  192. ShaderBinary* binary = nullptr;
  193. ANKI_CHECK(compileShaderProgram(info.m_inputFname, info.m_spirv, info.m_debugInfo, fsystem, nullptr,
  194. (info.m_threadCount) ? &taskManager : nullptr, info.m_defines, binary));
  195. class Dummy
  196. {
  197. public:
  198. ShaderBinary* m_binary;
  199. ~Dummy()
  200. {
  201. freeShaderBinary(m_binary);
  202. }
  203. } dummy{binary};
  204. // Store the binary
  205. {
  206. File file;
  207. ANKI_CHECK(file.open(info.m_outFname, FileOpenFlag::kWrite | FileOpenFlag::kBinary));
  208. BinarySerializer serializer;
  209. ANKI_CHECK(serializer.serialize(*binary, ShaderCompilerMemoryPool::getSingleton(), file));
  210. }
  211. return Error::kNone;
  212. }
  213. ANKI_MAIN_FUNCTION(myMain)
  214. int myMain(int argc, char** argv)
  215. {
  216. class Dummy
  217. {
  218. public:
  219. ~Dummy()
  220. {
  221. DefaultMemoryPool::freeSingleton();
  222. ShaderCompilerMemoryPool::freeSingleton();
  223. }
  224. } dummy;
  225. DefaultMemoryPool::allocateSingleton(allocAligned, nullptr);
  226. ShaderCompilerMemoryPool::allocateSingleton(allocAligned, nullptr);
  227. CmdLineArgs info;
  228. if(parseCommandLineArgs(argc, argv, info))
  229. {
  230. ANKI_LOGE(kUsage, argv[0]);
  231. return 1;
  232. }
  233. if(info.m_spirv == info.m_dxil)
  234. {
  235. ANKI_LOGE(kUsage, argv[0]);
  236. return 1;
  237. }
  238. if(info.m_outFname.isEmpty())
  239. {
  240. getFilepathFilename(info.m_inputFname, info.m_outFname);
  241. info.m_outFname += "bin";
  242. }
  243. if(info.m_includePath.isEmpty())
  244. {
  245. info.m_includePath = "./";
  246. }
  247. if(work(info))
  248. {
  249. ANKI_LOGE("Compilation failed");
  250. return 1;
  251. }
  252. return 0;
  253. }