MaliOfflineCompiler.cpp 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. // Copyright (C) 2009-2022, 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/MaliOfflineCompiler.h>
  6. #include <AnKi/Util/Process.h>
  7. #include <AnKi/Util/Filesystem.h>
  8. #include <AnKi/Util/File.h>
  9. #include <regex>
  10. namespace anki {
  11. static MaliOfflineCompilerHwUnit strToHwUnit(CString str)
  12. {
  13. MaliOfflineCompilerHwUnit out = MaliOfflineCompilerHwUnit::NONE;
  14. if(str.find("FMA") == 0)
  15. {
  16. out = MaliOfflineCompilerHwUnit::FMA;
  17. }
  18. else if(str.find("CVT") == 0)
  19. {
  20. out = MaliOfflineCompilerHwUnit::CVT;
  21. }
  22. else if(str.find("SFU") == 0)
  23. {
  24. out = MaliOfflineCompilerHwUnit::SFU;
  25. }
  26. else if(str.find("LS") == 0)
  27. {
  28. out = MaliOfflineCompilerHwUnit::LOAD_STORE;
  29. }
  30. else if(str.find("V") == 0)
  31. {
  32. out = MaliOfflineCompilerHwUnit::VARYING;
  33. }
  34. else if(str.find("T") == 0)
  35. {
  36. out = MaliOfflineCompilerHwUnit::TEXTURE;
  37. }
  38. else
  39. {
  40. ANKI_ASSERT(0);
  41. }
  42. return out;
  43. }
  44. static CString hwUnitToStr(MaliOfflineCompilerHwUnit u)
  45. {
  46. CString out;
  47. switch(u)
  48. {
  49. case MaliOfflineCompilerHwUnit::FMA:
  50. out = "FMA";
  51. break;
  52. case MaliOfflineCompilerHwUnit::CVT:
  53. out = "CVT";
  54. break;
  55. case MaliOfflineCompilerHwUnit::SFU:
  56. out = "SFU";
  57. break;
  58. case MaliOfflineCompilerHwUnit::LOAD_STORE:
  59. out = "LS";
  60. break;
  61. case MaliOfflineCompilerHwUnit::VARYING:
  62. out = "VAR";
  63. break;
  64. case MaliOfflineCompilerHwUnit::TEXTURE:
  65. out = "TEX";
  66. break;
  67. default:
  68. ANKI_ASSERT(0);
  69. }
  70. return out;
  71. }
  72. void MaliOfflineCompilerOut::toString(StringAuto& str) const
  73. {
  74. str.destroy();
  75. str.sprintf("Regs %u Spilling %u "
  76. "FMA %f CVT %f SFU %f LS %f VAR %f TEX %f Bound %s "
  77. "FP16 %f%%",
  78. m_workRegisters, m_spilling, m_fma, m_cvt, m_sfu, m_loadStore, m_varying, m_texture,
  79. hwUnitToStr(m_boundUnit).cstr(), m_fp16ArithmeticPercentage);
  80. }
  81. static Error runMaliOfflineCompilerInternal(CString maliocExecutable, CString spirvFilename, ShaderType shaderType,
  82. GenericMemoryPoolAllocator<U8> tmpAlloc, MaliOfflineCompilerOut& out)
  83. {
  84. out = {};
  85. // Set the arguments
  86. Array<CString, 3> args;
  87. switch(shaderType)
  88. {
  89. case ShaderType::VERTEX:
  90. args[0] = "-v";
  91. break;
  92. case ShaderType::FRAGMENT:
  93. args[0] = "-f";
  94. break;
  95. case ShaderType::COMPUTE:
  96. args[0] = "-C";
  97. break;
  98. default:
  99. ANKI_ASSERT(0 && "Unhandled case");
  100. }
  101. args[1] = "--vulkan";
  102. args[2] = spirvFilename;
  103. // Execute
  104. Process proc;
  105. ANKI_CHECK(proc.start(maliocExecutable, args, {}));
  106. ProcessStatus status;
  107. I32 exitCode;
  108. ANKI_CHECK(proc.wait(-1.0, &status, &exitCode));
  109. if(exitCode != 0)
  110. {
  111. StringAuto stderre(tmpAlloc);
  112. const Error err = proc.readFromStderr(stderre);
  113. ANKI_SHADER_COMPILER_LOGE("Mali offline compiler failed with exit code %d. Stderr: %s", exitCode,
  114. (err || stderre.isEmpty()) ? "<no text>" : stderre.cstr());
  115. return Error::FUNCTION_FAILED;
  116. }
  117. // Get stdout
  118. StringAuto stdouts(tmpAlloc);
  119. ANKI_CHECK(proc.readFromStdout(stdouts));
  120. const std::string stdoutstl(stdouts.cstr());
  121. // Work registers
  122. std::smatch match;
  123. if(std::regex_search(stdoutstl, match, std::regex("Work registers: ([0-9]+)")))
  124. {
  125. ANKI_CHECK(CString(match[1].str().c_str()).toNumber(out.m_workRegisters));
  126. }
  127. else
  128. {
  129. ANKI_SHADER_COMPILER_LOGE("Error parsing work registers");
  130. return Error::FUNCTION_FAILED;
  131. }
  132. #define ANKI_FLOAT_REGEX "([0-9]+[.]?[0-9]*)"
  133. // Instructions
  134. if(shaderType == ShaderType::VERTEX)
  135. {
  136. // Add the instructions in position and varying variants
  137. std::string stdoutstl2(stdouts.cstr());
  138. out.m_fma = 0.0;
  139. out.m_cvt = 0.0;
  140. out.m_sfu = 0.0;
  141. out.m_loadStore = 0.0;
  142. out.m_texture = 0.0;
  143. U32 count = 0;
  144. while(std::regex_search(stdoutstl2, match,
  145. std::regex("Total instruction cycles:\\s*" ANKI_FLOAT_REGEX "\\s*" ANKI_FLOAT_REGEX
  146. "\\s*" ANKI_FLOAT_REGEX "\\s*" ANKI_FLOAT_REGEX "\\s*" ANKI_FLOAT_REGEX
  147. "\\s*([A-Z]+)")))
  148. {
  149. ANKI_ASSERT(match.size() == 7);
  150. Array<F32, 5> floats;
  151. for(U32 i = 0; i < floats.getSize(); ++i)
  152. {
  153. ANKI_CHECK(CString(match[i + 1].str().c_str()).toNumber(floats[i]));
  154. }
  155. out.m_fma += floats[0];
  156. out.m_cvt += floats[1];
  157. out.m_sfu += floats[2];
  158. out.m_loadStore += floats[3];
  159. out.m_texture += floats[4];
  160. out.m_boundUnit = strToHwUnit(match[6].str().c_str());
  161. // Advance
  162. ++count;
  163. stdoutstl2 = match.suffix();
  164. }
  165. if(count == 0)
  166. {
  167. ANKI_SHADER_COMPILER_LOGE("Error parsing instruction cycles");
  168. return Error::FUNCTION_FAILED;
  169. }
  170. }
  171. else if(shaderType == ShaderType::FRAGMENT)
  172. {
  173. if(std::regex_search(stdoutstl, match,
  174. std::regex("Total instruction cycles:\\s*" ANKI_FLOAT_REGEX "\\s*" ANKI_FLOAT_REGEX
  175. "\\s*" ANKI_FLOAT_REGEX "\\s*" ANKI_FLOAT_REGEX "\\s*" ANKI_FLOAT_REGEX
  176. "\\s*" ANKI_FLOAT_REGEX "\\s*([A-Z]+)")))
  177. {
  178. ANKI_ASSERT(match.size() == 8);
  179. U32 count = 1;
  180. ANKI_CHECK(CString(match[count++].str().c_str()).toNumber(out.m_fma));
  181. ANKI_CHECK(CString(match[count++].str().c_str()).toNumber(out.m_cvt));
  182. ANKI_CHECK(CString(match[count++].str().c_str()).toNumber(out.m_sfu));
  183. ANKI_CHECK(CString(match[count++].str().c_str()).toNumber(out.m_loadStore));
  184. ANKI_CHECK(CString(match[count++].str().c_str()).toNumber(out.m_varying));
  185. ANKI_CHECK(CString(match[count++].str().c_str()).toNumber(out.m_texture));
  186. out.m_boundUnit = strToHwUnit(match[count++].str().c_str());
  187. }
  188. else
  189. {
  190. ANKI_SHADER_COMPILER_LOGE("Error parsing instruction cycles");
  191. return Error::FUNCTION_FAILED;
  192. }
  193. }
  194. else
  195. {
  196. ANKI_ASSERT(shaderType == ShaderType::COMPUTE);
  197. if(std::regex_search(stdoutstl, match,
  198. std::regex("Total instruction cycles:\\s*" ANKI_FLOAT_REGEX "\\s*" ANKI_FLOAT_REGEX
  199. "\\s*" ANKI_FLOAT_REGEX "\\s*" ANKI_FLOAT_REGEX "\\s*" ANKI_FLOAT_REGEX
  200. "\\s*([A-Z]+)")))
  201. {
  202. ANKI_ASSERT(match.size() == 7);
  203. U32 count = 1;
  204. ANKI_CHECK(CString(match[count++].str().c_str()).toNumber(out.m_fma));
  205. ANKI_CHECK(CString(match[count++].str().c_str()).toNumber(out.m_cvt));
  206. ANKI_CHECK(CString(match[count++].str().c_str()).toNumber(out.m_sfu));
  207. ANKI_CHECK(CString(match[count++].str().c_str()).toNumber(out.m_loadStore));
  208. ANKI_CHECK(CString(match[count++].str().c_str()).toNumber(out.m_texture));
  209. out.m_boundUnit = strToHwUnit(match[count++].str().c_str());
  210. }
  211. else
  212. {
  213. ANKI_SHADER_COMPILER_LOGE("Error parsing instruction cycles");
  214. return Error::FUNCTION_FAILED;
  215. }
  216. }
  217. #undef ANKI_FLOAT_REGEX
  218. // Spilling
  219. {
  220. std::string stdoutstl2(stdouts.cstr());
  221. while(std::regex_search(stdoutstl2, match, std::regex("Stack spilling:\\s([0-9]+)")))
  222. {
  223. U32 spill;
  224. ANKI_CHECK(CString(match[1].str().c_str()).toNumber(spill));
  225. out.m_spilling += spill;
  226. // Advance
  227. stdoutstl2 = match.suffix();
  228. }
  229. }
  230. // FP16
  231. {
  232. std::string stdoutstl2(stdouts.cstr());
  233. U32 count = 0;
  234. while(std::regex_search(stdoutstl2, match, std::regex("16-bit arithmetic:\\s(?:([0-9]+|N\\/A))")))
  235. {
  236. if(CString(match[1].str().c_str()) == "N/A")
  237. {
  238. // Do nothing
  239. }
  240. else
  241. {
  242. U32 percentage;
  243. ANKI_CHECK(CString(match[1].str().c_str()).toNumber(percentage));
  244. out.m_fp16ArithmeticPercentage += F32(percentage);
  245. }
  246. // Advance
  247. stdoutstl2 = match.suffix();
  248. ++count;
  249. }
  250. if(count == 0)
  251. {
  252. ANKI_SHADER_COMPILER_LOGE("Error parsing 16-bit arithmetic");
  253. return Error::FUNCTION_FAILED;
  254. }
  255. }
  256. // Debug
  257. if(false)
  258. {
  259. printf("%s\n", stdouts.cstr());
  260. StringAuto str(tmpAlloc);
  261. out.toString(str);
  262. printf("%s\n", str.cstr());
  263. }
  264. return Error::NONE;
  265. }
  266. Error runMaliOfflineCompiler(CString maliocExecutable, ConstWeakArray<U8> spirv, ShaderType shaderType,
  267. GenericMemoryPoolAllocator<U8> tmpAlloc, MaliOfflineCompilerOut& out)
  268. {
  269. ANKI_ASSERT(spirv.getSize() > 0);
  270. // Create temp file to dump the spirv
  271. StringAuto tmpDir(tmpAlloc);
  272. ANKI_CHECK(getTempDirectory(tmpDir));
  273. StringAuto spirvFilename(tmpAlloc);
  274. spirvFilename.sprintf("%s/AnKiMaliocTmpSpirv_%llu.spv", tmpDir.cstr(), getRandom());
  275. File spirvFile;
  276. ANKI_CHECK(spirvFile.open(spirvFilename, FileOpenFlag::WRITE | FileOpenFlag::BINARY));
  277. Error err = spirvFile.write(spirv.getBegin(), spirv.getSizeInBytes());
  278. spirvFile.close();
  279. // Call malioc
  280. if(!err)
  281. {
  282. err = runMaliOfflineCompilerInternal(maliocExecutable, spirvFilename, shaderType, tmpAlloc, out);
  283. }
  284. // Cleanup
  285. const Error err2 = removeFile(spirvFilename);
  286. return (err) ? err : err2;
  287. }
  288. } // end namespace anki