ShaderProgramCompilerMain.cpp 5.8 KB

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