MaliOfflineCompiler.cpp 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  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/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 Atomic<U32> g_nextFileId = {1};
  12. static MaliOfflineCompilerHwUnit strToHwUnit(CString str)
  13. {
  14. MaliOfflineCompilerHwUnit out = MaliOfflineCompilerHwUnit::kNone;
  15. if(str.find("FMA") == 0)
  16. {
  17. out = MaliOfflineCompilerHwUnit::kFma;
  18. }
  19. else if(str.find("LS") == 0)
  20. {
  21. out = MaliOfflineCompilerHwUnit::kLoadStore;
  22. }
  23. else if(str.find("V") == 0)
  24. {
  25. out = MaliOfflineCompilerHwUnit::kVarying;
  26. }
  27. else if(str.find("T") == 0)
  28. {
  29. out = MaliOfflineCompilerHwUnit::kTexture;
  30. }
  31. else if(str.find("A") == 0)
  32. {
  33. out = MaliOfflineCompilerHwUnit::kFma; // ????
  34. }
  35. else
  36. {
  37. ANKI_ASSERT(0);
  38. }
  39. return out;
  40. }
  41. static CString hwUnitToStr(MaliOfflineCompilerHwUnit u)
  42. {
  43. CString out;
  44. switch(u)
  45. {
  46. case MaliOfflineCompilerHwUnit::kFma:
  47. out = "FMA";
  48. break;
  49. case MaliOfflineCompilerHwUnit::kCvt:
  50. out = "CVT";
  51. break;
  52. case MaliOfflineCompilerHwUnit::kSfu:
  53. out = "SFU";
  54. break;
  55. case MaliOfflineCompilerHwUnit::kLoadStore:
  56. out = "LS";
  57. break;
  58. case MaliOfflineCompilerHwUnit::kVarying:
  59. out = "VAR";
  60. break;
  61. case MaliOfflineCompilerHwUnit::kTexture:
  62. out = "TEX";
  63. break;
  64. default:
  65. ANKI_ASSERT(0);
  66. }
  67. return out;
  68. }
  69. String MaliOfflineCompilerOut::toString() const
  70. {
  71. String str;
  72. str.sprintf("Regs %u Spilling %u "
  73. "FMA %f CVT %f SFU %f LS %f VAR %f TEX %f Bound %s "
  74. "FP16 %f%%",
  75. m_workRegisters, m_spilling, m_fma, m_cvt, m_sfu, m_loadStore, m_varying, m_texture, hwUnitToStr(m_boundUnit).cstr(),
  76. m_fp16ArithmeticPercentage);
  77. return str;
  78. }
  79. Error runMaliOfflineCompiler(ConstWeakArray<U8> spirv, ShaderType shaderType, MaliOfflineCompilerOut& out)
  80. {
  81. out = {};
  82. const U32 rand = g_nextFileId.fetchAdd(1) + getCurrentProcessId();
  83. // Create temp file to dump the spirv
  84. String tmpDir;
  85. ANKI_CHECK(getTempDirectory(tmpDir));
  86. String spirvFilename;
  87. spirvFilename.sprintf("%s/AnKiMaliocInputSpirv_%u.spv", tmpDir.cstr(), rand);
  88. File spirvFile;
  89. ANKI_CHECK(spirvFile.open(spirvFilename, FileOpenFlag::kWrite | FileOpenFlag::kBinary));
  90. ANKI_CHECK(spirvFile.write(spirv.getBegin(), spirv.getSizeInBytes()));
  91. spirvFile.close();
  92. CleanupFile cleanupSpirvFile(spirvFilename);
  93. // Tmp filename
  94. String analysisFilename;
  95. analysisFilename.sprintf("%s/AnKiMaliocOut_%u.txt", tmpDir.cstr(), rand);
  96. // Set the arguments
  97. Array<CString, 6> args;
  98. switch(shaderType)
  99. {
  100. case ShaderType::kVertex:
  101. args[0] = "-v";
  102. break;
  103. case ShaderType::kPixel:
  104. args[0] = "-f";
  105. break;
  106. case ShaderType::kCompute:
  107. args[0] = "-C";
  108. break;
  109. case ShaderType::kRayGen:
  110. args[0] = "--ray_generation";
  111. break;
  112. case ShaderType::kAnyHit:
  113. args[0] = "--ray_any_hit";
  114. break;
  115. case ShaderType::kClosestHit:
  116. args[0] = "--ray_closest_hit";
  117. break;
  118. case ShaderType::kMiss:
  119. args[0] = "--ray_miss";
  120. break;
  121. default:
  122. ANKI_ASSERT(0 && "Unhandled case");
  123. }
  124. args[1] = "-d";
  125. args[2] = "--vulkan";
  126. args[3] = spirvFilename;
  127. args[4] = "-o";
  128. args[5] = analysisFilename.cstr();
  129. // Execute
  130. I32 exitCode;
  131. #if ANKI_OS_LINUX
  132. CString maliocExecutable = ANKI_SOURCE_DIRECTORY "/ThirdParty/Bin/Linux64/MaliOfflineCompiler/malioc";
  133. #elif ANKI_OS_WINDOWS
  134. CString maliocExecutable = ANKI_SOURCE_DIRECTORY "/ThirdParty/Bin/Windows64/MaliOfflineCompiler/malioc.exe";
  135. #else
  136. CString maliocExecutable = "nothing";
  137. ANKI_ASSERT(0);
  138. #endif
  139. ANKI_CHECK(Process::callProcess(maliocExecutable, args, nullptr, nullptr, exitCode));
  140. if(exitCode != 0)
  141. {
  142. ANKI_SHADER_COMPILER_LOGE("Mali offline compiler failed with exit code %d", exitCode);
  143. return Error::kFunctionFailed;
  144. }
  145. CleanupFile rgaFileCleanup(analysisFilename);
  146. // Read the output file
  147. File analysisFile;
  148. ANKI_CHECK(analysisFile.open(analysisFilename, FileOpenFlag::kRead));
  149. String analysisText;
  150. ANKI_CHECK(analysisFile.readAllText(analysisText));
  151. analysisText.replaceAll("\r", "");
  152. analysisFile.close();
  153. const std::string analysisTextStl(analysisText.cstr());
  154. // printf("%d\n%s\n", (int)shaderType, analysisText.cstr());
  155. // Work registers
  156. std::smatch match;
  157. if(std::regex_search(analysisTextStl, match, std::regex("Work registers: ([0-9]+)")))
  158. {
  159. ANKI_CHECK(CString(match[1].str().c_str()).toNumber(out.m_workRegisters));
  160. }
  161. else
  162. {
  163. ANKI_SHADER_COMPILER_LOGE("Error parsing work registers");
  164. return Error::kFunctionFailed;
  165. }
  166. #define ANKI_FLOAT_REGEX "([0-9]+[.]?[0-9]*)"
  167. // Instructions
  168. if(shaderType == ShaderType::kVertex)
  169. {
  170. // Add the instructions in position and varying variants
  171. std::string stdoutstl2(analysisText.cstr());
  172. U32 count2 = 0;
  173. while(std::regex_search(stdoutstl2, match,
  174. std::regex("Total instruction cycles:\\s*" ANKI_FLOAT_REGEX "\\s*" ANKI_FLOAT_REGEX "\\s*" ANKI_FLOAT_REGEX
  175. "\\s*" ANKI_FLOAT_REGEX "\\s*" ANKI_FLOAT_REGEX "\\s*" ANKI_FLOAT_REGEX "\\s*([A-Z]+)")))
  176. {
  177. ANKI_ASSERT(match.size() == 8);
  178. U32 count = 2;
  179. ANKI_CHECK(CString(match[count++].str().c_str()).toNumber(out.m_fma));
  180. ANKI_CHECK(CString(match[count++].str().c_str()).toNumber(out.m_cvt));
  181. ANKI_CHECK(CString(match[count++].str().c_str()).toNumber(out.m_sfu));
  182. ANKI_CHECK(CString(match[count++].str().c_str()).toNumber(out.m_loadStore));
  183. ANKI_CHECK(CString(match[count++].str().c_str()).toNumber(out.m_texture));
  184. out.m_boundUnit = strToHwUnit(match[count++].str().c_str());
  185. ANKI_ASSERT(count == match.size());
  186. // Advance
  187. ++count2;
  188. stdoutstl2 = match.suffix();
  189. }
  190. if(count2 == 0)
  191. {
  192. ANKI_SHADER_COMPILER_LOGE("Error parsing instruction cycles");
  193. return Error::kFunctionFailed;
  194. }
  195. }
  196. else if(shaderType == ShaderType::kPixel)
  197. {
  198. if(std::regex_search(analysisTextStl, match,
  199. std::regex("Total instruction cycles:\\s*" ANKI_FLOAT_REGEX "\\s*" ANKI_FLOAT_REGEX "\\s*" ANKI_FLOAT_REGEX
  200. "\\s*" ANKI_FLOAT_REGEX "\\s*" ANKI_FLOAT_REGEX "\\s*" ANKI_FLOAT_REGEX "\\s*" ANKI_FLOAT_REGEX
  201. "\\s*([A-Z]+)")))
  202. {
  203. ANKI_ASSERT(match.size() == 9);
  204. U32 count = 2;
  205. ANKI_CHECK(CString(match[count++].str().c_str()).toNumber(out.m_fma));
  206. ANKI_CHECK(CString(match[count++].str().c_str()).toNumber(out.m_cvt));
  207. ANKI_CHECK(CString(match[count++].str().c_str()).toNumber(out.m_sfu));
  208. ANKI_CHECK(CString(match[count++].str().c_str()).toNumber(out.m_loadStore));
  209. ANKI_CHECK(CString(match[count++].str().c_str()).toNumber(out.m_varying));
  210. ANKI_CHECK(CString(match[count++].str().c_str()).toNumber(out.m_texture));
  211. out.m_boundUnit = strToHwUnit(match[count++].str().c_str());
  212. ANKI_ASSERT(count == match.size());
  213. }
  214. else
  215. {
  216. ANKI_SHADER_COMPILER_LOGE("Error parsing instruction cycles");
  217. return Error::kFunctionFailed;
  218. }
  219. }
  220. else
  221. {
  222. ANKI_ASSERT(shaderType == ShaderType::kCompute || (shaderType >= ShaderType::kFirstRayTracing && shaderType <= ShaderType::kLastRayTracing));
  223. if(std::regex_search(analysisTextStl, match,
  224. std::regex("Total instruction cycles:\\s*" ANKI_FLOAT_REGEX "\\s*" ANKI_FLOAT_REGEX "\\s*" ANKI_FLOAT_REGEX
  225. "\\s*" ANKI_FLOAT_REGEX "\\s*" ANKI_FLOAT_REGEX "\\s*" ANKI_FLOAT_REGEX "\\s*([A-Z]+)")))
  226. {
  227. ANKI_ASSERT(match.size() == 8);
  228. U32 count = 2;
  229. ANKI_CHECK(CString(match[count++].str().c_str()).toNumber(out.m_fma));
  230. ANKI_CHECK(CString(match[count++].str().c_str()).toNumber(out.m_cvt));
  231. ANKI_CHECK(CString(match[count++].str().c_str()).toNumber(out.m_sfu));
  232. ANKI_CHECK(CString(match[count++].str().c_str()).toNumber(out.m_loadStore));
  233. ANKI_CHECK(CString(match[count++].str().c_str()).toNumber(out.m_texture));
  234. out.m_boundUnit = strToHwUnit(match[count++].str().c_str());
  235. ANKI_ASSERT(count == match.size());
  236. }
  237. else
  238. {
  239. ANKI_SHADER_COMPILER_LOGE("Error parsing instruction cycles");
  240. return Error::kFunctionFailed;
  241. }
  242. }
  243. #undef ANKI_FLOAT_REGEX
  244. // Spilling
  245. {
  246. std::string stdoutstl2(analysisText.cstr());
  247. while(std::regex_search(stdoutstl2, match, std::regex("Stack spilling:\\s([0-9]+)")))
  248. {
  249. U32 spill;
  250. ANKI_CHECK(CString(match[1].str().c_str()).toNumber(spill));
  251. out.m_spilling += spill;
  252. // Advance
  253. stdoutstl2 = match.suffix();
  254. }
  255. }
  256. // FP16
  257. {
  258. std::string stdoutstl2(analysisText.cstr());
  259. U32 count = 0;
  260. while(std::regex_search(stdoutstl2, match, std::regex("16-bit arithmetic:\\s(?:([0-9]+|N\\/A))")))
  261. {
  262. if(CString(match[1].str().c_str()) == "N/A")
  263. {
  264. // Do nothing
  265. }
  266. else
  267. {
  268. U32 percentage;
  269. ANKI_CHECK(CString(match[1].str().c_str()).toNumber(percentage));
  270. out.m_fp16ArithmeticPercentage += F32(percentage);
  271. }
  272. // Advance
  273. stdoutstl2 = match.suffix();
  274. ++count;
  275. }
  276. if(count == 0)
  277. {
  278. ANKI_SHADER_COMPILER_LOGE("Error parsing 16-bit arithmetic");
  279. return Error::kFunctionFailed;
  280. }
  281. }
  282. // Debug
  283. if(false)
  284. {
  285. printf("%s\n", analysisText.cstr());
  286. String str = out.toString();
  287. printf("%s\n", str.cstr());
  288. }
  289. return Error::kNone;
  290. }
  291. } // end namespace anki