Shader serialization experiment (with Mali Offline Compiler analysis)
1) Adds a --writeShaders option to fm. When that's included, the GPU
backend is run with a persistent cache (and no binary caching).
Then we dump all of the fragment shaders (GLSL for now) that were
created to the passed-in directory, along with a JSON digest listing
the number of times each one was referenced in the cache.
2) Adds a python script that invokes the Mali Offline Compiler on a
directory generated from #1, scraping the output of each compile for
cycle counts and writing the collated information to stdout.
Change-Id: Ie4b58ddac4f62e936707c6fec44f4fe605c213fa
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/206162
Commit-Queue: Brian Osman <brianosman@google.com>
Reviewed-by: Mike Klein <mtklein@google.com>
diff --git a/tools/gpu/MemoryCache.cpp b/tools/gpu/MemoryCache.cpp
index 7fc3e1b..771d1a1 100644
--- a/tools/gpu/MemoryCache.cpp
+++ b/tools/gpu/MemoryCache.cpp
@@ -5,8 +5,12 @@
* found in the LICENSE file.
*/
+#include "GrPersistentCacheUtils.h"
#include "MemoryCache.h"
#include "SkBase64.h"
+#include "SkJSONWriter.h"
+#include "SkMD5.h"
+#include "SkTHash.h"
// Change this to 1 to log cache hits/misses/stores using SkDebugf.
#define LOG_MEMORY_CACHE 0
@@ -40,9 +44,10 @@
}
if (LOG_MEMORY_CACHE) {
SkDebugf("Load Key: %s\n\tFound Data: %s\n\n", data_to_str(key).c_str(),
- data_to_str(*result->second).c_str());
+ data_to_str(*result->second.fData).c_str());
}
- return result->second;
+ result->second.fHitCount++;
+ return result->second.fData;
}
void MemoryCache::store(const SkData& key, const SkData& data) {
@@ -50,7 +55,52 @@
SkDebugf("Store Key: %s\n\tData: %s\n\n", data_to_str(key).c_str(),
data_to_str(data).c_str());
}
- fMap[Key(key)] = SkData::MakeWithCopy(data.data(), data.size());
+ fMap[Key(key)] = Value(data);
+}
+
+void MemoryCache::writeShadersToDisk(const char* path, GrBackendApi api) {
+ if (GrBackendApi::kOpenGL != api) {
+ // TODO: Add SPIRV support, too.
+ return;
+ }
+
+ // Default extensions detected by the Mali Offline Compiler
+ const char* extensions[kGrShaderTypeCount] = { "vert", "geom", "frag" };
+
+ // For now, we only dump fragment shaders. They are the biggest factor in performance, and
+ // the shaders for other stages tend to be heavily reused.
+ SkString jsonPath = SkStringPrintf("%s/%s.json", path, extensions[kFragment_GrShaderType]);
+ SkFILEWStream jsonFile(jsonPath.c_str());
+ SkJSONWriter writer(&jsonFile, SkJSONWriter::Mode::kPretty);
+ writer.beginArray();
+
+ for (auto it = fMap.begin(); it != fMap.end(); ++it) {
+ SkMD5 hash;
+ hash.write(it->first.fKey->bytes(), it->first.fKey->size());
+ SkMD5::Digest digest = hash.finish();
+ SkString md5;
+ for (int i = 0; i < 16; ++i) {
+ md5.appendf("%02x", digest.data[i]);
+ }
+
+ // Write [ hash, hitCount ] to JSON digest
+ writer.beginArray(nullptr, false);
+ writer.appendString(md5.c_str());
+ writer.appendS32(it->second.fHitCount);
+ writer.endArray();
+
+ SkReader32 reader(it->second.fData->data(), it->second.fData->size());
+ SkSL::Program::Inputs inputsIgnored;
+ SkSL::String glsl[kGrShaderTypeCount];
+ GrPersistentCacheUtils::UnpackCachedGLSL(reader, &inputsIgnored, glsl);
+
+ SkString filename = SkStringPrintf("%s/%s.%s", path, md5.c_str(),
+ extensions[kFragment_GrShaderType]);
+ SkFILEWStream file(filename.c_str());
+ file.write(glsl[kFragment_GrShaderType].c_str(), glsl[kFragment_GrShaderType].size());
+ }
+
+ writer.endArray();
}
} // namespace sk_gpu_test