1 //
2 // Copyright 2022 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6 // MemoryShaderCache: Stores compiled shader in memory so they don't
7 // always have to be re-compiled. Can be used in conjunction with the platform
8 // layer to warm up the cache from disk.
9
10 #include "libANGLE/MemoryShaderCache.h"
11
12 #include <GLSLANG/ShaderVars.h>
13 #include <anglebase/sha1.h>
14
15 #include "common/BinaryStream.h"
16 #include "common/utilities.h"
17 #include "libANGLE/Compiler.h"
18 #include "libANGLE/Context.h"
19 #include "libANGLE/Debug.h"
20 #include "libANGLE/Uniform.h"
21 #include "libANGLE/histogram_macros.h"
22 #include "libANGLE/renderer/ShaderImpl.h"
23 #include "platform/PlatformMethods.h"
24
25 namespace gl
26 {
27
28 namespace
29 {
30 // Limit decompressed programs to 5MB. If they're larger then this there is a good chance the data
31 // is not what we expect. This limits the amount of memory we will allocate based on a binary blob
32 // we believe is compressed data.
33 static constexpr size_t kMaxUncompressedShaderSize = 5 * 1024 * 1024;
34 } // namespace
35
MemoryShaderCache(egl::BlobCache & blobCache)36 MemoryShaderCache::MemoryShaderCache(egl::BlobCache &blobCache) : mBlobCache(blobCache) {}
37
~MemoryShaderCache()38 MemoryShaderCache::~MemoryShaderCache() {}
39
getShader(const Context * context,Shader * shader,const egl::BlobCache::Key & shaderHash)40 egl::CacheGetResult MemoryShaderCache::getShader(const Context *context,
41 Shader *shader,
42 const egl::BlobCache::Key &shaderHash)
43 {
44 // If caching is effectively disabled, don't bother calculating the hash.
45 if (!mBlobCache.isCachingEnabled())
46 {
47 return egl::CacheGetResult::NotFound;
48 }
49
50 angle::MemoryBuffer uncompressedData;
51 const egl::BlobCache::GetAndDecompressResult result = mBlobCache.getAndDecompress(
52 context->getScratchBuffer(), shaderHash, kMaxUncompressedShaderSize, &uncompressedData);
53 switch (result)
54 {
55 case egl::BlobCache::GetAndDecompressResult::DecompressFailure:
56 ANGLE_PERF_WARNING(context->getState().getDebug(), GL_DEBUG_SEVERITY_LOW,
57 "Error decompressing shader binary data from cache.");
58 mBlobCache.remove(shaderHash);
59 return egl::CacheGetResult::NotFound;
60
61 case egl::BlobCache::GetAndDecompressResult::NotFound:
62 return egl::CacheGetResult::NotFound;
63
64 case egl::BlobCache::GetAndDecompressResult::Success:
65 if (shader->loadBinary(context, uncompressedData.data(),
66 static_cast<int>(uncompressedData.size())))
67 {
68 return egl::CacheGetResult::Success;
69 }
70
71 // Cache load failed, evict.
72 ANGLE_PERF_WARNING(context->getState().getDebug(), GL_DEBUG_SEVERITY_LOW,
73 "Failed to load shader binary from cache.");
74 mBlobCache.remove(shaderHash);
75 return egl::CacheGetResult::Rejected;
76 }
77
78 UNREACHABLE();
79 return egl::CacheGetResult::NotFound;
80 }
81
putShader(const Context * context,const egl::BlobCache::Key & shaderHash,const Shader * shader)82 angle::Result MemoryShaderCache::putShader(const Context *context,
83 const egl::BlobCache::Key &shaderHash,
84 const Shader *shader)
85 {
86 // If caching is effectively disabled, don't bother serializing the shader.
87 if (!mBlobCache.isCachingEnabled())
88 {
89 return angle::Result::Continue;
90 }
91
92 angle::MemoryBuffer serializedShader;
93 ANGLE_TRY(shader->serialize(nullptr, &serializedShader));
94
95 size_t compressedSize;
96 if (!mBlobCache.compressAndPut(shaderHash, std::move(serializedShader), &compressedSize))
97 {
98 ANGLE_PERF_WARNING(context->getState().getDebug(), GL_DEBUG_SEVERITY_LOW,
99 "Error compressing shader binary data for insertion into cache.");
100 return angle::Result::Continue;
101 }
102
103 return angle::Result::Continue;
104 }
105
clear()106 void MemoryShaderCache::clear()
107 {
108 mBlobCache.clear();
109 }
110
maxSize() const111 size_t MemoryShaderCache::maxSize() const
112 {
113 return mBlobCache.maxSize();
114 }
115
116 } // namespace gl
117