| // |
| // Copyright 2022 The ANGLE Project Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| // |
| // MemoryShaderCache: Stores compiled shader in memory so they don't |
| // always have to be re-compiled. Can be used in conjunction with the platform |
| // layer to warm up the cache from disk. |
| |
| #include "libANGLE/MemoryShaderCache.h" |
| |
| #include <GLSLANG/ShaderVars.h> |
| #include <anglebase/sha1.h> |
| |
| #include "common/BinaryStream.h" |
| #include "common/utilities.h" |
| #include "libANGLE/Compiler.h" |
| #include "libANGLE/Context.h" |
| #include "libANGLE/Debug.h" |
| #include "libANGLE/Uniform.h" |
| #include "libANGLE/histogram_macros.h" |
| #include "libANGLE/renderer/ShaderImpl.h" |
| #include "platform/PlatformMethods.h" |
| |
| namespace gl |
| { |
| |
| MemoryShaderCache::MemoryShaderCache(egl::BlobCache &blobCache) : mBlobCache(blobCache) {} |
| |
| MemoryShaderCache::~MemoryShaderCache() {} |
| |
| angle::Result MemoryShaderCache::getShader(const Context *context, |
| Shader *shader, |
| const ShCompileOptions &compileOptions, |
| const ShCompilerInstance &compilerInstance, |
| const egl::BlobCache::Key &shaderHash) |
| { |
| // If caching is effectively disabled, don't bother calculating the hash. |
| if (!mBlobCache.isCachingEnabled()) |
| { |
| return angle::Result::Incomplete; |
| } |
| |
| angle::MemoryBuffer uncompressedData; |
| switch (mBlobCache.getAndDecompress(context->getScratchBuffer(), shaderHash, &uncompressedData)) |
| { |
| case egl::BlobCache::GetAndDecompressResult::DecompressFailure: |
| ANGLE_PERF_WARNING(context->getState().getDebug(), GL_DEBUG_SEVERITY_LOW, |
| "Error decompressing shader binary data from cache."); |
| return angle::Result::Incomplete; |
| |
| case egl::BlobCache::GetAndDecompressResult::NotFound: |
| return angle::Result::Incomplete; |
| |
| case egl::BlobCache::GetAndDecompressResult::GetSuccess: |
| angle::Result result = shader->loadBinary(context, uncompressedData.data(), |
| static_cast<int>(uncompressedData.size())); |
| |
| { |
| std::scoped_lock<std::mutex> lock(mHistogramMutex); |
| ANGLE_HISTOGRAM_BOOLEAN("GPU.ANGLE.ShaderCache.LoadBinarySuccess", |
| result == angle::Result::Continue); |
| } |
| ANGLE_TRY(result); |
| |
| if (result == angle::Result::Continue) |
| return angle::Result::Continue; |
| |
| // Cache load failed, evict. |
| ANGLE_PERF_WARNING(context->getState().getDebug(), GL_DEBUG_SEVERITY_LOW, |
| "Failed to load shader binary from cache."); |
| mBlobCache.remove(shaderHash); |
| return angle::Result::Incomplete; |
| } |
| |
| UNREACHABLE(); |
| return angle::Result::Incomplete; |
| } |
| |
| angle::Result MemoryShaderCache::putShader(const Context *context, |
| const egl::BlobCache::Key &shaderHash, |
| const Shader *shader) |
| { |
| // If caching is effectively disabled, don't bother serializing the shader. |
| if (!mBlobCache.isCachingEnabled()) |
| { |
| return angle::Result::Incomplete; |
| } |
| |
| angle::MemoryBuffer serializedShader; |
| ANGLE_TRY(shader->serialize(nullptr, &serializedShader)); |
| |
| size_t compressedSize; |
| if (!mBlobCache.compressAndPut(shaderHash, std::move(serializedShader), &compressedSize)) |
| { |
| ANGLE_PERF_WARNING(context->getState().getDebug(), GL_DEBUG_SEVERITY_LOW, |
| "Error compressing shader binary data for insertion into cache."); |
| return angle::Result::Incomplete; |
| } |
| |
| { |
| std::scoped_lock<std::mutex> lock(mHistogramMutex); |
| ANGLE_HISTOGRAM_COUNTS("GPU.ANGLE.ShaderCache.ShaderBinarySizeBytes", |
| static_cast<int>(compressedSize)); |
| } |
| |
| return angle::Result::Continue; |
| } |
| |
| void MemoryShaderCache::clear() |
| { |
| mBlobCache.clear(); |
| } |
| |
| size_t MemoryShaderCache::maxSize() const |
| { |
| return mBlobCache.maxSize(); |
| } |
| |
| } // namespace gl |