MemoryShaderCache.cpp (5736B)
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/angle_version_info.h" 16 #include "common/utilities.h" 17 #include "libANGLE/BinaryStream.h" 18 #include "libANGLE/Compiler.h" 19 #include "libANGLE/Context.h" 20 #include "libANGLE/Debug.h" 21 #include "libANGLE/Uniform.h" 22 #include "libANGLE/histogram_macros.h" 23 #include "libANGLE/renderer/ShaderImpl.h" 24 #include "platform/PlatformMethods.h" 25 26 namespace gl 27 { 28 29 namespace 30 { 31 void ComputeHash(const Context *context, 32 const Shader *shader, 33 const ShCompileOptions &compileOptions, 34 const ShCompilerInstance &compilerInstance, 35 egl::BlobCache::Key *hashOut) 36 { 37 BinaryOutputStream hashStream; 38 // Compute the shader hash. Start with the shader hashes and resource strings. 39 hashStream.writeEnum(shader->getType()); 40 hashStream.writeString(shader->getSourceString()); 41 42 // Include the commit hash 43 hashStream.writeString(angle::GetANGLECommitHash()); 44 45 hashStream.writeEnum(Compiler::SelectShaderSpec(context->getState())); 46 hashStream.writeEnum(compilerInstance.getShaderOutputType()); 47 hashStream.writeBytes(reinterpret_cast<const uint8_t *>(&compileOptions), 48 sizeof(compileOptions)); 49 50 // Include the ShBuiltInResources, which represent the extensions and constants used by the 51 // shader. 52 const ShBuiltInResources resources = compilerInstance.getBuiltInResources(); 53 hashStream.writeBytes(reinterpret_cast<const uint8_t *>(&resources), sizeof(resources)); 54 55 // Call the secure SHA hashing function. 56 const std::vector<uint8_t> &shaderKey = hashStream.getData(); 57 angle::base::SHA1HashBytes(shaderKey.data(), shaderKey.size(), hashOut->data()); 58 } 59 } // namespace 60 61 MemoryShaderCache::MemoryShaderCache(egl::BlobCache &blobCache) : mBlobCache(blobCache) {} 62 63 MemoryShaderCache::~MemoryShaderCache() {} 64 65 angle::Result MemoryShaderCache::getShader(const Context *context, 66 Shader *shader, 67 const ShCompileOptions &compileOptions, 68 const ShCompilerInstance &compilerInstance, 69 egl::BlobCache::Key *hashOut) 70 { 71 // If caching is effectively disabled, don't bother calculating the hash. 72 if (!mBlobCache.isCachingEnabled()) 73 { 74 return angle::Result::Incomplete; 75 } 76 77 ComputeHash(context, shader, compileOptions, compilerInstance, hashOut); 78 79 angle::MemoryBuffer uncompressedData; 80 switch (mBlobCache.getAndDecompress(context->getScratchBuffer(), *hashOut, &uncompressedData)) 81 { 82 case egl::BlobCache::GetAndDecompressResult::DecompressFailure: 83 ANGLE_PERF_WARNING(context->getState().getDebug(), GL_DEBUG_SEVERITY_LOW, 84 "Error decompressing shader binary data from cache."); 85 return angle::Result::Incomplete; 86 87 case egl::BlobCache::GetAndDecompressResult::NotFound: 88 return angle::Result::Incomplete; 89 90 case egl::BlobCache::GetAndDecompressResult::GetSuccess: 91 angle::Result result = shader->loadBinary(context, uncompressedData.data(), 92 static_cast<int>(uncompressedData.size())); 93 94 { 95 std::scoped_lock<std::mutex> lock(mHistogramMutex); 96 ANGLE_HISTOGRAM_BOOLEAN("GPU.ANGLE.ShaderCache.LoadBinarySuccess", 97 result == angle::Result::Continue); 98 } 99 ANGLE_TRY(result); 100 101 if (result == angle::Result::Continue) 102 return angle::Result::Continue; 103 104 // Cache load failed, evict. 105 ANGLE_PERF_WARNING(context->getState().getDebug(), GL_DEBUG_SEVERITY_LOW, 106 "Failed to load shader binary from cache."); 107 mBlobCache.remove(*hashOut); 108 return angle::Result::Incomplete; 109 } 110 111 UNREACHABLE(); 112 return angle::Result::Incomplete; 113 } 114 115 angle::Result MemoryShaderCache::putShader(const Context *context, 116 const egl::BlobCache::Key &shaderHash, 117 const Shader *shader) 118 { 119 // If caching is effectively disabled, don't bother serializing the shader. 120 if (!mBlobCache.isCachingEnabled()) 121 { 122 return angle::Result::Incomplete; 123 } 124 125 angle::MemoryBuffer serializedShader; 126 ANGLE_TRY(shader->serialize(nullptr, &serializedShader)); 127 128 size_t compressedSize; 129 if (!mBlobCache.compressAndPut(shaderHash, std::move(serializedShader), &compressedSize)) 130 { 131 ANGLE_PERF_WARNING(context->getState().getDebug(), GL_DEBUG_SEVERITY_LOW, 132 "Error compressing shader binary data for insertion into cache."); 133 return angle::Result::Incomplete; 134 } 135 136 { 137 std::scoped_lock<std::mutex> lock(mHistogramMutex); 138 ANGLE_HISTOGRAM_COUNTS("GPU.ANGLE.ShaderCache.ShaderBinarySizeBytes", 139 static_cast<int>(compressedSize)); 140 } 141 142 return angle::Result::Continue; 143 } 144 145 void MemoryShaderCache::clear() 146 { 147 mBlobCache.clear(); 148 } 149 150 size_t MemoryShaderCache::maxSize() const 151 { 152 return mBlobCache.maxSize(); 153 } 154 155 } // namespace gl