ShaderProgramCompilerMain.cpp 5.4 KB

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