Hash :
bc9ff5e6
        
        Author :
  
        
        Date :
2025-07-11T13:43:05
        
      
Don't cache shaders/programs that are too large Currently, ANGLE compresses and caches every shader program, but can then fail to decompress the cached program because the uncompressed size is too large. Instead, check if the program size is too large before compressing and caching the program, so ANGLE doesn't generate an error later while attempting to decompress the cached program. This change is applied to both shaders and programs: * MemoryShaderCache::putShader() * MemoryProgramCache::putProgram() Bug: b/279980674 Change-Id: I67a3b542b460a41dbbc4a3d8bad4031a212223e0 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/6728876 Reviewed-by: Charlie Lao <cclao@google.com> Reviewed-by: Cody Northrop <cnorthrop@google.com> Commit-Queue: Tim Van Patten <timvp@google.com>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
//
// 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
{
namespace
{
// Limit decompressed programs to 5MB. If they're larger then this there is a good chance the data
// is not what we expect. This limits the amount of memory we will allocate based on a binary blob
// we believe is compressed data.
static constexpr size_t kMaxUncompressedShaderSize = 5 * 1024 * 1024;
}  // namespace
MemoryShaderCache::MemoryShaderCache(egl::BlobCache &blobCache) : mBlobCache(blobCache) {}
MemoryShaderCache::~MemoryShaderCache() {}
egl::CacheGetResult MemoryShaderCache::getShader(const Context *context,
                                                 Shader *shader,
                                                 const egl::BlobCache::Key &shaderHash,
                                                 angle::JobResultExpectancy resultExpectancy)
{
    // If caching is effectively disabled, don't bother calculating the hash.
    if (!mBlobCache.isCachingEnabled(context))
    {
        return egl::CacheGetResult::NotFound;
    }
    angle::MemoryBuffer uncompressedData;
    const egl::BlobCache::GetAndDecompressResult result =
        mBlobCache.getAndDecompress(context, context->getScratchBuffer(), shaderHash,
                                    kMaxUncompressedShaderSize, &uncompressedData);
    switch (result)
    {
        case egl::BlobCache::GetAndDecompressResult::DecompressFailure:
            ANGLE_PERF_WARNING(context->getState().getDebug(), GL_DEBUG_SEVERITY_LOW,
                               "Error decompressing shader binary data from cache.");
            mBlobCache.remove(shaderHash);
            return egl::CacheGetResult::NotFound;
        case egl::BlobCache::GetAndDecompressResult::NotFound:
            return egl::CacheGetResult::NotFound;
        case egl::BlobCache::GetAndDecompressResult::Success:
            if (shader->loadBinary(context, uncompressedData.data(),
                                   static_cast<int>(uncompressedData.size()), resultExpectancy))
            {
                return egl::CacheGetResult::Success;
            }
            // 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 egl::CacheGetResult::Rejected;
    }
    UNREACHABLE();
    return egl::CacheGetResult::NotFound;
}
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(context))
    {
        return angle::Result::Continue;
    }
    angle::MemoryBuffer serializedShader;
    ANGLE_TRY(shader->serialize(nullptr, &serializedShader));
    if (serializedShader.size() > kMaxUncompressedShaderSize)
    {
        std::ostringstream warningMessage;
        warningMessage << "Shader is too large to cache: ";
        warningMessage << "shader size: " << serializedShader.size()
                       << ", max size: " << kMaxUncompressedShaderSize;
        ANGLE_PERF_WARNING(context->getState().getDebug(), GL_DEBUG_SEVERITY_LOW, "%s",
                           warningMessage.str().c_str());
        return angle::Result::Continue;
    }
    size_t compressedSize;
    if (!mBlobCache.compressAndPut(context, 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::Continue;
    }
    return angle::Result::Continue;
}
void MemoryShaderCache::clear()
{
    mBlobCache.clear();
}
size_t MemoryShaderCache::maxSize() const
{
    return mBlobCache.maxSize();
}
}  // namespace gl