| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334 |
- // Copyright (C) 2009-2022, Panagiotis Christopoulos Charitos and contributors.
- // All rights reserved.
- // Code licensed under the BSD License.
- // http://www.anki3d.org/LICENSE
- #include <AnKi/ShaderCompiler/MaliOfflineCompiler.h>
- #include <AnKi/Util/Process.h>
- #include <AnKi/Util/Filesystem.h>
- #include <AnKi/Util/File.h>
- #include <regex>
- namespace anki {
- static MaliOfflineCompilerHwUnit strToHwUnit(CString str)
- {
- MaliOfflineCompilerHwUnit out = MaliOfflineCompilerHwUnit::NONE;
- if(str.find("FMA") == 0)
- {
- out = MaliOfflineCompilerHwUnit::FMA;
- }
- else if(str.find("CVT") == 0)
- {
- out = MaliOfflineCompilerHwUnit::CVT;
- }
- else if(str.find("SFU") == 0)
- {
- out = MaliOfflineCompilerHwUnit::SFU;
- }
- else if(str.find("LS") == 0)
- {
- out = MaliOfflineCompilerHwUnit::LOAD_STORE;
- }
- else if(str.find("V") == 0)
- {
- out = MaliOfflineCompilerHwUnit::VARYING;
- }
- else if(str.find("T") == 0)
- {
- out = MaliOfflineCompilerHwUnit::TEXTURE;
- }
- else
- {
- ANKI_ASSERT(0);
- }
- return out;
- }
- static CString hwUnitToStr(MaliOfflineCompilerHwUnit u)
- {
- CString out;
- switch(u)
- {
- case MaliOfflineCompilerHwUnit::FMA:
- out = "FMA";
- break;
- case MaliOfflineCompilerHwUnit::CVT:
- out = "CVT";
- break;
- case MaliOfflineCompilerHwUnit::SFU:
- out = "SFU";
- break;
- case MaliOfflineCompilerHwUnit::LOAD_STORE:
- out = "LS";
- break;
- case MaliOfflineCompilerHwUnit::VARYING:
- out = "VAR";
- break;
- case MaliOfflineCompilerHwUnit::TEXTURE:
- out = "TEX";
- break;
- default:
- ANKI_ASSERT(0);
- }
- return out;
- }
- void MaliOfflineCompilerOut::toString(StringAuto& str) const
- {
- str.destroy();
- str.sprintf("Regs %u Spilling %u "
- "FMA %f CVT %f SFU %f LS %f VAR %f TEX %f Bound %s "
- "FP16 %f%%",
- m_workRegisters, m_spilling, m_fma, m_cvt, m_sfu, m_loadStore, m_varying, m_texture,
- hwUnitToStr(m_boundUnit).cstr(), m_fp16ArithmeticPercentage);
- }
- static Error runMaliOfflineCompilerInternal(CString maliocExecutable, CString spirvFilename, ShaderType shaderType,
- GenericMemoryPoolAllocator<U8> tmpAlloc, MaliOfflineCompilerOut& out)
- {
- out = {};
- // Set the arguments
- Array<CString, 3> args;
- switch(shaderType)
- {
- case ShaderType::VERTEX:
- args[0] = "-v";
- break;
- case ShaderType::FRAGMENT:
- args[0] = "-f";
- break;
- case ShaderType::COMPUTE:
- args[0] = "-C";
- break;
- default:
- ANKI_ASSERT(0 && "Unhandled case");
- }
- args[1] = "--vulkan";
- args[2] = spirvFilename;
- // Execute
- Process proc;
- ANKI_CHECK(proc.start(maliocExecutable, args, {}));
- ProcessStatus status;
- I32 exitCode;
- ANKI_CHECK(proc.wait(-1.0, &status, &exitCode));
- if(exitCode != 0)
- {
- StringAuto stderre(tmpAlloc);
- const Error err = proc.readFromStderr(stderre);
- ANKI_SHADER_COMPILER_LOGE("Mali offline compiler failed with exit code %d. Stderr: %s", exitCode,
- (err || stderre.isEmpty()) ? "<no text>" : stderre.cstr());
- return Error::FUNCTION_FAILED;
- }
- // Get stdout
- StringAuto stdouts(tmpAlloc);
- ANKI_CHECK(proc.readFromStdout(stdouts));
- const std::string stdoutstl(stdouts.cstr());
- // Work registers
- std::smatch match;
- if(std::regex_search(stdoutstl, match, std::regex("Work registers: ([0-9]+)")))
- {
- ANKI_CHECK(CString(match[1].str().c_str()).toNumber(out.m_workRegisters));
- }
- else
- {
- ANKI_SHADER_COMPILER_LOGE("Error parsing work registers");
- return Error::FUNCTION_FAILED;
- }
- #define ANKI_FLOAT_REGEX "([0-9]+[.]?[0-9]*)"
- // Instructions
- if(shaderType == ShaderType::VERTEX)
- {
- // Add the instructions in position and varying variants
- std::string stdoutstl2(stdouts.cstr());
- out.m_fma = 0.0;
- out.m_cvt = 0.0;
- out.m_sfu = 0.0;
- out.m_loadStore = 0.0;
- out.m_texture = 0.0;
- U32 count = 0;
- while(std::regex_search(stdoutstl2, match,
- std::regex("Total instruction cycles:\\s*" ANKI_FLOAT_REGEX "\\s*" ANKI_FLOAT_REGEX
- "\\s*" ANKI_FLOAT_REGEX "\\s*" ANKI_FLOAT_REGEX "\\s*" ANKI_FLOAT_REGEX
- "\\s*([A-Z]+)")))
- {
- ANKI_ASSERT(match.size() == 7);
- Array<F32, 5> floats;
- for(U32 i = 0; i < floats.getSize(); ++i)
- {
- ANKI_CHECK(CString(match[i + 1].str().c_str()).toNumber(floats[i]));
- }
- out.m_fma += floats[0];
- out.m_cvt += floats[1];
- out.m_sfu += floats[2];
- out.m_loadStore += floats[3];
- out.m_texture += floats[4];
- out.m_boundUnit = strToHwUnit(match[6].str().c_str());
- // Advance
- ++count;
- stdoutstl2 = match.suffix();
- }
- if(count == 0)
- {
- ANKI_SHADER_COMPILER_LOGE("Error parsing instruction cycles");
- return Error::FUNCTION_FAILED;
- }
- }
- else if(shaderType == ShaderType::FRAGMENT)
- {
- if(std::regex_search(stdoutstl, match,
- std::regex("Total instruction cycles:\\s*" ANKI_FLOAT_REGEX "\\s*" ANKI_FLOAT_REGEX
- "\\s*" ANKI_FLOAT_REGEX "\\s*" ANKI_FLOAT_REGEX "\\s*" ANKI_FLOAT_REGEX
- "\\s*" ANKI_FLOAT_REGEX "\\s*([A-Z]+)")))
- {
- ANKI_ASSERT(match.size() == 8);
- U32 count = 1;
- ANKI_CHECK(CString(match[count++].str().c_str()).toNumber(out.m_fma));
- ANKI_CHECK(CString(match[count++].str().c_str()).toNumber(out.m_cvt));
- ANKI_CHECK(CString(match[count++].str().c_str()).toNumber(out.m_sfu));
- ANKI_CHECK(CString(match[count++].str().c_str()).toNumber(out.m_loadStore));
- ANKI_CHECK(CString(match[count++].str().c_str()).toNumber(out.m_varying));
- ANKI_CHECK(CString(match[count++].str().c_str()).toNumber(out.m_texture));
- out.m_boundUnit = strToHwUnit(match[count++].str().c_str());
- }
- else
- {
- ANKI_SHADER_COMPILER_LOGE("Error parsing instruction cycles");
- return Error::FUNCTION_FAILED;
- }
- }
- else
- {
- ANKI_ASSERT(shaderType == ShaderType::COMPUTE);
- if(std::regex_search(stdoutstl, match,
- std::regex("Total instruction cycles:\\s*" ANKI_FLOAT_REGEX "\\s*" ANKI_FLOAT_REGEX
- "\\s*" ANKI_FLOAT_REGEX "\\s*" ANKI_FLOAT_REGEX "\\s*" ANKI_FLOAT_REGEX
- "\\s*([A-Z]+)")))
- {
- ANKI_ASSERT(match.size() == 7);
- U32 count = 1;
- ANKI_CHECK(CString(match[count++].str().c_str()).toNumber(out.m_fma));
- ANKI_CHECK(CString(match[count++].str().c_str()).toNumber(out.m_cvt));
- ANKI_CHECK(CString(match[count++].str().c_str()).toNumber(out.m_sfu));
- ANKI_CHECK(CString(match[count++].str().c_str()).toNumber(out.m_loadStore));
- ANKI_CHECK(CString(match[count++].str().c_str()).toNumber(out.m_texture));
- out.m_boundUnit = strToHwUnit(match[count++].str().c_str());
- }
- else
- {
- ANKI_SHADER_COMPILER_LOGE("Error parsing instruction cycles");
- return Error::FUNCTION_FAILED;
- }
- }
- #undef ANKI_FLOAT_REGEX
- // Spilling
- {
- std::string stdoutstl2(stdouts.cstr());
- while(std::regex_search(stdoutstl2, match, std::regex("Stack spilling:\\s([0-9]+)")))
- {
- U32 spill;
- ANKI_CHECK(CString(match[1].str().c_str()).toNumber(spill));
- out.m_spilling += spill;
- // Advance
- stdoutstl2 = match.suffix();
- }
- }
- // FP16
- {
- std::string stdoutstl2(stdouts.cstr());
- U32 count = 0;
- while(std::regex_search(stdoutstl2, match, std::regex("16-bit arithmetic:\\s(?:([0-9]+|N\\/A))")))
- {
- if(CString(match[1].str().c_str()) == "N/A")
- {
- // Do nothing
- }
- else
- {
- U32 percentage;
- ANKI_CHECK(CString(match[1].str().c_str()).toNumber(percentage));
- out.m_fp16ArithmeticPercentage += F32(percentage);
- }
- // Advance
- stdoutstl2 = match.suffix();
- ++count;
- }
- if(count == 0)
- {
- ANKI_SHADER_COMPILER_LOGE("Error parsing 16-bit arithmetic");
- return Error::FUNCTION_FAILED;
- }
- }
- // Debug
- if(false)
- {
- printf("%s\n", stdouts.cstr());
- StringAuto str(tmpAlloc);
- out.toString(str);
- printf("%s\n", str.cstr());
- }
- return Error::NONE;
- }
- Error runMaliOfflineCompiler(CString maliocExecutable, ConstWeakArray<U8> spirv, ShaderType shaderType,
- GenericMemoryPoolAllocator<U8> tmpAlloc, MaliOfflineCompilerOut& out)
- {
- ANKI_ASSERT(spirv.getSize() > 0);
- // Create temp file to dump the spirv
- StringAuto tmpDir(tmpAlloc);
- ANKI_CHECK(getTempDirectory(tmpDir));
- StringAuto spirvFilename(tmpAlloc);
- spirvFilename.sprintf("%s/AnKiMaliocTmpSpirv_%llu.spv", tmpDir.cstr(), getRandom());
- File spirvFile;
- ANKI_CHECK(spirvFile.open(spirvFilename, FileOpenFlag::WRITE | FileOpenFlag::BINARY));
- Error err = spirvFile.write(spirv.getBegin(), spirv.getSizeInBytes());
- spirvFile.close();
- // Call malioc
- if(!err)
- {
- err = runMaliOfflineCompilerInternal(maliocExecutable, spirvFilename, shaderType, tmpAlloc, out);
- }
- // Cleanup
- const Error err2 = removeFile(spirvFilename);
- return (err) ? err : err2;
- }
- } // end namespace anki
|