Browse Source

Integrate Mali offline compiler

Panagiotis Christopoulos Charitos 4 years ago
parent
commit
683cc9a5d7

+ 2 - 1
AnKi/Gr/Vulkan/ShaderImpl.cpp

@@ -57,7 +57,8 @@ Error ShaderImpl::init(const ShaderInitInfo& inf)
 #if ANKI_DUMP_SHADERS
 	{
 		StringAuto fnameSpirv(getAllocator());
-		fnameSpirv.sprintf("%s/%05u.spv", getManager().getCacheDirectory().cstr(), getUuid());
+		fnameSpirv.sprintf("%s/%s_t%u_%05u.spv", getManager().getCacheDirectory().cstr(), getName().cstr(),
+						   U(m_shaderType), getUuid());
 
 		File fileSpirv;
 		ANKI_CHECK(

+ 4 - 4
AnKi/Importer/ImageImporter.cpp

@@ -8,6 +8,7 @@
 #include <AnKi/Resource/Stb.h>
 #include <AnKi/Util/Process.h>
 #include <AnKi/Util/File.h>
+#include <AnKi/Util/Filesystem.h>
 
 namespace anki {
 
@@ -123,12 +124,11 @@ public:
 	{
 		if(!m_fileToDelete.isEmpty())
 		{
-			const int err = std::remove(m_fileToDelete.cstr());
-			if(err)
+			const Error err = removeFile(m_fileToDelete);
+			if(!err)
 			{
-				ANKI_IMPORTER_LOGE("Couldn't delete file: %s", m_fileToDelete.cstr());
+				ANKI_IMPORTER_LOGV("Deleted %s", m_fileToDelete.cstr());
 			}
-			ANKI_IMPORTER_LOGV("Deleted %s", m_fileToDelete.cstr());
 		}
 	}
 };

+ 17 - 0
AnKi/Resource/ShaderProgramResource.cpp

@@ -10,6 +10,7 @@
 #include <AnKi/Gr/GrManager.h>
 #include <AnKi/Util/Filesystem.h>
 #include <AnKi/Util/Functions.h>
+#include <AnKi/ShaderCompiler/MaliOfflineCompiler.h>
 
 namespace anki {
 
@@ -386,6 +387,22 @@ void ShaderProgramResource::initVariant(const ShaderProgramResourceVariantInitIn
 			inf.m_constValues.setArray((constValueCount) ? constValues.getBegin() : nullptr, constValueCount);
 			ShaderPtr shader = getManager().getGrManager().newShader(inf);
 
+			if(false)
+			{
+				MaliOfflineCompilerOut maliocOut;
+				const Error err =
+					runMaliOfflineCompiler(ANKI_SOURCE_DIRECTORY "/ThirdParty/Bin/MaliOfflineCompiler/malioc",
+										   binary.m_codeBlocks[binaryVariant->m_codeBlockIndices[shaderType]].m_binary,
+										   inf.m_shaderType, getAllocator(), maliocOut);
+
+				if(!err)
+				{
+					StringAuto maliocOutStr(getAllocator());
+					maliocOut.toString(maliocOutStr);
+					ANKI_RESOURCE_LOGI("Mali offline compiler: %s: %s", cprogName, maliocOutStr.cstr());
+				}
+			}
+
 			const ShaderTypeBit shaderBit = ShaderTypeBit(1 << shaderType);
 			if(!!(shaderBit & ShaderTypeBit::ALL_GRAPHICS))
 			{

+ 2 - 1
AnKi/ShaderCompiler.h

@@ -6,5 +6,6 @@
 #pragma once
 
 #include <AnKi/ShaderCompiler/ShaderProgramCompiler.h>
+#include <AnKi/ShaderCompiler/MaliOfflineCompiler.h>
 
-/// @defgroup shader_compiler Shader compiler
+/// @defgroup shader_compiler Shader compiler and shader tools

+ 237 - 0
AnKi/ShaderCompiler/MaliOfflineCompiler.cpp

@@ -0,0 +1,237 @@
+// Copyright (C) 2009-2021, 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 {
+
+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, "
+				"FP16: %f%%",
+				m_workRegisters, m_spilling, m_fma, m_cvt, m_sfu, m_loadStore, m_varying, m_texture,
+				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(10.0_sec, &status, &exitCode));
+	if(status == ProcessStatus::CRASH_EXIT || 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)))
+		{
+			ANKI_ASSERT(match.size() == 6);
+			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];
+
+			// Advance
+			++count;
+			stdoutstl2 = match.suffix();
+		}
+
+		if(count == 0)
+		{
+			ANKI_SHADER_COMPILER_LOGE("Error parsing instruction cycles");
+			return Error::FUNCTION_FAILED;
+		}
+	}
+	else
+	{
+		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)))
+		{
+			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_varying));
+			ANKI_CHECK(CString(match[count++].str().c_str()).toNumber(out.m_texture));
+		}
+		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

+ 40 - 0
AnKi/ShaderCompiler/MaliOfflineCompiler.h

@@ -0,0 +1,40 @@
+// Copyright (C) 2009-2021, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#pragma once
+
+#include <AnKi/ShaderCompiler/Common.h>
+#include <AnKi/Util/WeakArray.h>
+
+namespace anki {
+
+/// @addtogroup shader_compiler
+/// @{
+
+/// Mali offline compiler output.
+class MaliOfflineCompilerOut
+{
+public:
+	// Instructions
+	F32 m_fma = -1.0f;
+	F32 m_cvt = -1.0f;
+	F32 m_sfu = -1.0f;
+	F32 m_loadStore = -1.0f;
+	F32 m_varying = -1.0f;
+	F32 m_texture = -1.0f;
+
+	U32 m_workRegisters = MAX_U32;
+	U32 m_spilling = 0;
+	F32 m_fp16ArithmeticPercentage = 0.0f;
+
+	void toString(StringAuto& str) const;
+};
+
+/// Run the mali offline compiler and get some info back.
+ANKI_USE_RESULT Error runMaliOfflineCompiler(CString maliocExecutable, ConstWeakArray<U8> spirv, ShaderType shaderType,
+											 GenericMemoryPoolAllocator<U8> tmpAlloc, MaliOfflineCompilerOut& out);
+/// @}
+
+} // end namespace anki

+ 11 - 0
AnKi/Util/Filesystem.cpp

@@ -57,4 +57,15 @@ void getParentFilepath(const CString& filename, StringAuto& out)
 	}
 }
 
+Error removeFile(const CString& filename)
+{
+	const int err = std::remove(filename.cstr());
+	if(err)
+	{
+		ANKI_UTIL_LOGE("Couldn't delete file: %s", filename.cstr());
+	}
+
+	return Error::NONE;
+}
+
 } // end namespace anki

+ 3 - 0
AnKi/Util/Filesystem.h

@@ -59,6 +59,9 @@ ANKI_USE_RESULT Error walkDirectoryTree(const CString& dir, GenericMemoryPoolAll
 /// @param alloc A temp allocator that this function requires.
 ANKI_USE_RESULT Error removeDirectory(const CString& dir, GenericMemoryPoolAllocator<U8> alloc);
 
+/// Remove a file.
+ANKI_USE_RESULT Error removeFile(const CString& filename);
+
 /// Equivalent to: mkdir dir
 ANKI_USE_RESULT Error createDirectory(const CString& dir);
 

+ 1 - 1
AnKi/Util/Memory.cpp

@@ -146,7 +146,7 @@ BaseMemoryPool::BaseMemoryPool(Type type, AllocAlignedCallback allocCb, void* al
 	, m_allocCbUserData(allocCbUserData)
 	, m_type(type)
 {
-	ANKI_ASSERT(allocCb == nullptr);
+	ANKI_ASSERT(allocCb != nullptr);
 
 	I64 len;
 	if(name && (len = strlen(name)) > 0)

+ 5 - 0
AnKi/Util/StdTypes.h

@@ -266,6 +266,11 @@ static constexpr unsigned long long int operator""_GB(unsigned long long int x)
 
 /// @name Time user literals
 /// @{
+static constexpr Second operator""_sec(long double x)
+{
+	return Second(x);
+}
+
 static constexpr Second operator""_ms(long double x)
 {
 	return Second(x) / 1000.0;

BIN
ThirdParty/Bin/MaliOfflineCompiler/graphics/libMali-Gxx_r32p0-00rel0.so


BIN
ThirdParty/Bin/MaliOfflineCompiler/graphics/libMali-T600_r23p0-00rel0.so


BIN
ThirdParty/Bin/MaliOfflineCompiler/malioc