blob: c1eb7c0fc534ac818b42e56c21506e4e5f4257bc [file] [log] [blame]
//
// Copyright 2017 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.
//
// MemoryProgramCache: Stores compiled and linked programs 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/MemoryProgramCache.h"
#include <GLSLANG/ShaderVars.h>
#include <anglebase/sha1.h>
#include "common/utilities.h"
#include "common/version.h"
#include "libANGLE/BinaryStream.h"
#include "libANGLE/Context.h"
#include "libANGLE/Uniform.h"
#include "libANGLE/histogram_macros.h"
#include "libANGLE/renderer/ProgramImpl.h"
#include "platform/Platform.h"
namespace gl
{
namespace
{
constexpr unsigned int kWarningLimit = 3;
void WriteShaderVar(BinaryOutputStream *stream, const sh::ShaderVariable &var)
{
stream->writeInt(var.type);
stream->writeInt(var.precision);
stream->writeString(var.name);
stream->writeString(var.mappedName);
stream->writeIntVector(var.arraySizes);
stream->writeInt(var.staticUse);
stream->writeInt(var.active);
stream->writeString(var.structName);
ASSERT(var.fields.empty());
}
void LoadShaderVar(BinaryInputStream *stream, sh::ShaderVariable *var)
{
var->type = stream->readInt<GLenum>();
var->precision = stream->readInt<GLenum>();
var->name = stream->readString();
var->mappedName = stream->readString();
stream->readIntVector<unsigned int>(&var->arraySizes);
var->staticUse = stream->readBool();
var->active = stream->readBool();
var->structName = stream->readString();
}
void WriteShaderVariableBuffer(BinaryOutputStream *stream, const ShaderVariableBuffer &var)
{
stream->writeInt(var.binding);
stream->writeInt(var.dataSize);
for (ShaderType shaderType : AllShaderTypes())
{
stream->writeInt(var.isActive(shaderType));
}
stream->writeInt(var.memberIndexes.size());
for (unsigned int memberCounterIndex : var.memberIndexes)
{
stream->writeInt(memberCounterIndex);
}
}
void LoadShaderVariableBuffer(BinaryInputStream *stream, ShaderVariableBuffer *var)
{
var->binding = stream->readInt<int>();
var->dataSize = stream->readInt<unsigned int>();
for (ShaderType shaderType : AllShaderTypes())
{
var->setActive(shaderType, stream->readBool());
}
unsigned int numMembers = stream->readInt<unsigned int>();
for (unsigned int blockMemberIndex = 0; blockMemberIndex < numMembers; blockMemberIndex++)
{
var->memberIndexes.push_back(stream->readInt<unsigned int>());
}
}
void WriteBufferVariable(BinaryOutputStream *stream, const BufferVariable &var)
{
WriteShaderVar(stream, var);
stream->writeInt(var.bufferIndex);
stream->writeInt(var.blockInfo.offset);
stream->writeInt(var.blockInfo.arrayStride);
stream->writeInt(var.blockInfo.matrixStride);
stream->writeInt(var.blockInfo.isRowMajorMatrix);
stream->writeInt(var.blockInfo.topLevelArrayStride);
stream->writeInt(var.topLevelArraySize);
for (ShaderType shaderType : AllShaderTypes())
{
stream->writeInt(var.isActive(shaderType));
}
}
void LoadBufferVariable(BinaryInputStream *stream, BufferVariable *var)
{
LoadShaderVar(stream, var);
var->bufferIndex = stream->readInt<int>();
var->blockInfo.offset = stream->readInt<int>();
var->blockInfo.arrayStride = stream->readInt<int>();
var->blockInfo.matrixStride = stream->readInt<int>();
var->blockInfo.isRowMajorMatrix = stream->readBool();
var->blockInfo.topLevelArrayStride = stream->readInt<int>();
var->topLevelArraySize = stream->readInt<int>();
for (ShaderType shaderType : AllShaderTypes())
{
var->setActive(shaderType, stream->readBool());
}
}
void WriteInterfaceBlock(BinaryOutputStream *stream, const InterfaceBlock &block)
{
stream->writeString(block.name);
stream->writeString(block.mappedName);
stream->writeInt(block.isArray);
stream->writeInt(block.arrayElement);
WriteShaderVariableBuffer(stream, block);
}
void LoadInterfaceBlock(BinaryInputStream *stream, InterfaceBlock *block)
{
block->name = stream->readString();
block->mappedName = stream->readString();
block->isArray = stream->readBool();
block->arrayElement = stream->readInt<unsigned int>();
LoadShaderVariableBuffer(stream, block);
}
class HashStream final : angle::NonCopyable
{
public:
std::string str() { return mStringStream.str(); }
template <typename T>
HashStream &operator<<(T value)
{
mStringStream << value << kSeparator;
return *this;
}
private:
static constexpr char kSeparator = ':';
std::ostringstream mStringStream;
};
HashStream &operator<<(HashStream &stream, const Shader *shader)
{
if (shader)
{
stream << shader->getSourceString().c_str() << shader->getSourceString().length()
<< shader->getCompilerResourcesString().c_str();
}
return stream;
}
HashStream &operator<<(HashStream &stream, const ProgramBindings &bindings)
{
for (const auto &binding : bindings)
{
stream << binding.first << binding.second;
}
return stream;
}
HashStream &operator<<(HashStream &stream, const std::vector<std::string> &strings)
{
for (const auto &str : strings)
{
stream << str;
}
return stream;
}
HashStream &operator<<(HashStream &stream, const std::vector<gl::VariableLocation> &locations)
{
for (const auto &loc : locations)
{
stream << loc.index << loc.arrayIndex << loc.ignored;
}
return stream;
}
} // anonymous namespace
MemoryProgramCache::MemoryProgramCache(egl::BlobCache &blobCache)
: mBlobCache(blobCache), mIssuedWarnings(0)
{}
MemoryProgramCache::~MemoryProgramCache() {}
// static
angle::Result MemoryProgramCache::Deserialize(const Context *context,
const Program *program,
ProgramState *state,
const uint8_t *binary,
size_t length,
InfoLog &infoLog)
{
BinaryInputStream stream(binary, length);
unsigned char commitString[ANGLE_COMMIT_HASH_SIZE];
stream.readBytes(commitString, ANGLE_COMMIT_HASH_SIZE);
if (memcmp(commitString, ANGLE_COMMIT_HASH, sizeof(unsigned char) * ANGLE_COMMIT_HASH_SIZE) !=
0)
{
infoLog << "Invalid program binary version.";
return angle::Result::Incomplete;
}
int majorVersion = stream.readInt<int>();
int minorVersion = stream.readInt<int>();
if (majorVersion != context->getClientMajorVersion() ||
minorVersion != context->getClientMinorVersion())
{
infoLog << "Cannot load program binaries across different ES context versions.";
return angle::Result::Incomplete;
}
state->mComputeShaderLocalSize[0] = stream.readInt<int>();
state->mComputeShaderLocalSize[1] = stream.readInt<int>();
state->mComputeShaderLocalSize[2] = stream.readInt<int>();
state->mGeometryShaderInputPrimitiveType = stream.readEnum<PrimitiveMode>();
state->mGeometryShaderOutputPrimitiveType = stream.readEnum<PrimitiveMode>();
state->mGeometryShaderInvocations = stream.readInt<int>();
state->mGeometryShaderMaxVertices = stream.readInt<int>();
state->mNumViews = stream.readInt<int>();
static_assert(MAX_VERTEX_ATTRIBS * 2 <= sizeof(uint32_t) * 8,
"All bits of mAttributesTypeMask types and mask fit into 32 bits each");
state->mAttributesTypeMask = gl::ComponentTypeMask(stream.readInt<uint32_t>());
state->mAttributesMask = stream.readInt<gl::AttributesMask>();
static_assert(MAX_VERTEX_ATTRIBS <= sizeof(unsigned long) * 8,
"Too many vertex attribs for mask");
state->mActiveAttribLocationsMask = stream.readInt<gl::AttributesMask>();
unsigned int attribCount = stream.readInt<unsigned int>();
ASSERT(state->mAttributes.empty());
for (unsigned int attribIndex = 0; attribIndex < attribCount; ++attribIndex)
{
sh::Attribute attrib;
LoadShaderVar(&stream, &attrib);
attrib.location = stream.readInt<int>();
state->mAttributes.push_back(attrib);
}
unsigned int uniformCount = stream.readInt<unsigned int>();
ASSERT(state->mUniforms.empty());
for (unsigned int uniformIndex = 0; uniformIndex < uniformCount; ++uniformIndex)
{
LinkedUniform uniform;
LoadShaderVar(&stream, &uniform);
uniform.bufferIndex = stream.readInt<int>();
uniform.blockInfo.offset = stream.readInt<int>();
uniform.blockInfo.arrayStride = stream.readInt<int>();
uniform.blockInfo.matrixStride = stream.readInt<int>();
uniform.blockInfo.isRowMajorMatrix = stream.readBool();
uniform.typeInfo = &GetUniformTypeInfo(uniform.type);
state->mUniforms.push_back(uniform);
}
const unsigned int uniformIndexCount = stream.readInt<unsigned int>();
ASSERT(state->mUniformLocations.empty());
for (unsigned int uniformIndexIndex = 0; uniformIndexIndex < uniformIndexCount;
uniformIndexIndex++)
{
VariableLocation variable;
stream.readInt(&variable.arrayIndex);
stream.readInt(&variable.index);
stream.readBool(&variable.ignored);
state->mUniformLocations.push_back(variable);
}
unsigned int uniformBlockCount = stream.readInt<unsigned int>();
ASSERT(state->mUniformBlocks.empty());
for (unsigned int uniformBlockIndex = 0; uniformBlockIndex < uniformBlockCount;
++uniformBlockIndex)
{
InterfaceBlock uniformBlock;
LoadInterfaceBlock(&stream, &uniformBlock);
state->mUniformBlocks.push_back(uniformBlock);
state->mActiveUniformBlockBindings.set(uniformBlockIndex, uniformBlock.binding != 0);
}
unsigned int bufferVariableCount = stream.readInt<unsigned int>();
ASSERT(state->mBufferVariables.empty());
for (unsigned int index = 0; index < bufferVariableCount; ++index)
{
BufferVariable bufferVariable;
LoadBufferVariable(&stream, &bufferVariable);
state->mBufferVariables.push_back(bufferVariable);
}
unsigned int shaderStorageBlockCount = stream.readInt<unsigned int>();
ASSERT(state->mShaderStorageBlocks.empty());
for (unsigned int shaderStorageBlockIndex = 0;
shaderStorageBlockIndex < shaderStorageBlockCount; ++shaderStorageBlockIndex)
{
InterfaceBlock shaderStorageBlock;
LoadInterfaceBlock(&stream, &shaderStorageBlock);
state->mShaderStorageBlocks.push_back(shaderStorageBlock);
}
unsigned int atomicCounterBufferCount = stream.readInt<unsigned int>();
ASSERT(state->mAtomicCounterBuffers.empty());
for (unsigned int bufferIndex = 0; bufferIndex < atomicCounterBufferCount; ++bufferIndex)
{
AtomicCounterBuffer atomicCounterBuffer;
LoadShaderVariableBuffer(&stream, &atomicCounterBuffer);
state->mAtomicCounterBuffers.push_back(atomicCounterBuffer);
}
unsigned int transformFeedbackVaryingCount = stream.readInt<unsigned int>();
// Reject programs that use transform feedback varyings if the hardware cannot support them.
if (transformFeedbackVaryingCount > 0 &&
context->getWorkarounds().disableProgramCachingForTransformFeedback)
{
infoLog << "Current driver does not support transform feedback in binary programs.";
return angle::Result::Incomplete;
}
ASSERT(state->mLinkedTransformFeedbackVaryings.empty());
for (unsigned int transformFeedbackVaryingIndex = 0;
transformFeedbackVaryingIndex < transformFeedbackVaryingCount;
++transformFeedbackVaryingIndex)
{
sh::Varying varying;
stream.readIntVector<unsigned int>(&varying.arraySizes);
stream.readInt(&varying.type);
stream.readString(&varying.name);
GLuint arrayIndex = stream.readInt<GLuint>();
state->mLinkedTransformFeedbackVaryings.emplace_back(varying, arrayIndex);
}
stream.readInt(&state->mTransformFeedbackBufferMode);
unsigned int outputCount = stream.readInt<unsigned int>();
ASSERT(state->mOutputVariables.empty());
for (unsigned int outputIndex = 0; outputIndex < outputCount; ++outputIndex)
{
sh::OutputVariable output;
LoadShaderVar(&stream, &output);
output.location = stream.readInt<int>();
output.index = stream.readInt<int>();
state->mOutputVariables.push_back(output);
}
unsigned int outputVarCount = stream.readInt<unsigned int>();
ASSERT(state->mOutputLocations.empty());
for (unsigned int outputIndex = 0; outputIndex < outputVarCount; ++outputIndex)
{
VariableLocation locationData;
stream.readInt(&locationData.arrayIndex);
stream.readInt(&locationData.index);
stream.readBool(&locationData.ignored);
state->mOutputLocations.push_back(locationData);
}
unsigned int secondaryOutputVarCount = stream.readInt<unsigned int>();
ASSERT(state->mSecondaryOutputLocations.empty());
for (unsigned int outputIndex = 0; outputIndex < secondaryOutputVarCount; ++outputIndex)
{
VariableLocation locationData;
stream.readInt(&locationData.arrayIndex);
stream.readInt(&locationData.index);
stream.readBool(&locationData.ignored);
state->mSecondaryOutputLocations.push_back(locationData);
}
unsigned int outputTypeCount = stream.readInt<unsigned int>();
for (unsigned int outputIndex = 0; outputIndex < outputTypeCount; ++outputIndex)
{
state->mOutputVariableTypes.push_back(stream.readInt<GLenum>());
}
static_assert(IMPLEMENTATION_MAX_DRAW_BUFFERS * 2 <= 8 * sizeof(uint32_t),
"All bits of mDrawBufferTypeMask and mActiveOutputVariables types and mask fit "
"into 32 bits each");
state->mDrawBufferTypeMask = gl::ComponentTypeMask(stream.readInt<uint32_t>());
state->mActiveOutputVariables = stream.readInt<gl::DrawBufferMask>();
unsigned int samplerRangeLow = stream.readInt<unsigned int>();
unsigned int samplerRangeHigh = stream.readInt<unsigned int>();
state->mSamplerUniformRange = RangeUI(samplerRangeLow, samplerRangeHigh);
unsigned int samplerCount = stream.readInt<unsigned int>();
for (unsigned int samplerIndex = 0; samplerIndex < samplerCount; ++samplerIndex)
{
TextureType textureType = stream.readEnum<TextureType>();
SamplerFormat format = stream.readEnum<SamplerFormat>();
size_t bindingCount = stream.readInt<size_t>();
bool unreferenced = stream.readBool();
state->mSamplerBindings.emplace_back(textureType, format, bindingCount, unreferenced);
}
unsigned int imageRangeLow = stream.readInt<unsigned int>();
unsigned int imageRangeHigh = stream.readInt<unsigned int>();
state->mImageUniformRange = RangeUI(imageRangeLow, imageRangeHigh);
unsigned int imageBindingCount = stream.readInt<unsigned int>();
for (unsigned int imageIndex = 0; imageIndex < imageBindingCount; ++imageIndex)
{
unsigned int elementCount = stream.readInt<unsigned int>();
ImageBinding imageBinding(elementCount);
for (unsigned int i = 0; i < elementCount; ++i)
{
imageBinding.boundImageUnits[i] = stream.readInt<unsigned int>();
}
state->mImageBindings.emplace_back(imageBinding);
}
unsigned int atomicCounterRangeLow = stream.readInt<unsigned int>();
unsigned int atomicCounterRangeHigh = stream.readInt<unsigned int>();
state->mAtomicCounterUniformRange = RangeUI(atomicCounterRangeLow, atomicCounterRangeHigh);
static_assert(static_cast<unsigned long>(ShaderType::EnumCount) <= sizeof(unsigned long) * 8,
"Too many shader types");
state->mLinkedShaderStages = ShaderBitSet(stream.readInt<uint8_t>());
state->updateTransformFeedbackStrides();
state->updateActiveSamplers();
state->updateActiveImages();
return program->getImplementation()->load(context, infoLog, &stream);
}
// static
void MemoryProgramCache::Serialize(const Context *context,
const gl::Program *program,
angle::MemoryBuffer *binaryOut)
{
BinaryOutputStream stream;
stream.writeBytes(reinterpret_cast<const unsigned char *>(ANGLE_COMMIT_HASH),
ANGLE_COMMIT_HASH_SIZE);
// nullptr context is supported when computing binary length.
if (context)
{
stream.writeInt(context->getClientVersion().major);
stream.writeInt(context->getClientVersion().minor);
}
else
{
stream.writeInt(2);
stream.writeInt(0);
}
const auto &state = program->getState();
const auto &computeLocalSize = state.getComputeShaderLocalSize();
stream.writeInt(computeLocalSize[0]);
stream.writeInt(computeLocalSize[1]);
stream.writeInt(computeLocalSize[2]);
ASSERT(state.mGeometryShaderInvocations >= 1 && state.mGeometryShaderMaxVertices >= 0);
stream.writeEnum(state.mGeometryShaderInputPrimitiveType);
stream.writeEnum(state.mGeometryShaderOutputPrimitiveType);
stream.writeInt(state.mGeometryShaderInvocations);
stream.writeInt(state.mGeometryShaderMaxVertices);
stream.writeInt(state.mNumViews);
static_assert(MAX_VERTEX_ATTRIBS * 2 <= sizeof(uint32_t) * 8,
"All bits of mAttributesTypeMask types and mask fit into 32 bits each");
stream.writeInt(static_cast<int>(state.mAttributesTypeMask.to_ulong()));
stream.writeInt(static_cast<int>(state.mAttributesMask.to_ulong()));
stream.writeInt(state.getActiveAttribLocationsMask().to_ulong());
stream.writeInt(state.getAttributes().size());
for (const sh::Attribute &attrib : state.getAttributes())
{
WriteShaderVar(&stream, attrib);
stream.writeInt(attrib.location);
}
stream.writeInt(state.getUniforms().size());
for (const LinkedUniform &uniform : state.getUniforms())
{
WriteShaderVar(&stream, uniform);
// FIXME: referenced
stream.writeInt(uniform.bufferIndex);
stream.writeInt(uniform.blockInfo.offset);
stream.writeInt(uniform.blockInfo.arrayStride);
stream.writeInt(uniform.blockInfo.matrixStride);
stream.writeInt(uniform.blockInfo.isRowMajorMatrix);
}
stream.writeInt(state.getUniformLocations().size());
for (const auto &variable : state.getUniformLocations())
{
stream.writeInt(variable.arrayIndex);
stream.writeIntOrNegOne(variable.index);
stream.writeInt(variable.ignored);
}
stream.writeInt(state.getUniformBlocks().size());
for (const InterfaceBlock &uniformBlock : state.getUniformBlocks())
{
WriteInterfaceBlock(&stream, uniformBlock);
}
stream.writeInt(state.getBufferVariables().size());
for (const BufferVariable &bufferVariable : state.getBufferVariables())
{
WriteBufferVariable(&stream, bufferVariable);
}
stream.writeInt(state.getShaderStorageBlocks().size());
for (const InterfaceBlock &shaderStorageBlock : state.getShaderStorageBlocks())
{
WriteInterfaceBlock(&stream, shaderStorageBlock);
}
stream.writeInt(state.mAtomicCounterBuffers.size());
for (const auto &atomicCounterBuffer : state.mAtomicCounterBuffers)
{
WriteShaderVariableBuffer(&stream, atomicCounterBuffer);
}
// Warn the app layer if saving a binary with unsupported transform feedback.
if (!state.getLinkedTransformFeedbackVaryings().empty() &&
context->getWorkarounds().disableProgramCachingForTransformFeedback)
{
WARN() << "Saving program binary with transform feedback, which is not supported on this "
"driver.";
}
stream.writeInt(state.getLinkedTransformFeedbackVaryings().size());
for (const auto &var : state.getLinkedTransformFeedbackVaryings())
{
stream.writeIntVector(var.arraySizes);
stream.writeInt(var.type);
stream.writeString(var.name);
stream.writeIntOrNegOne(var.arrayIndex);
}
stream.writeInt(state.getTransformFeedbackBufferMode());
stream.writeInt(state.getOutputVariables().size());
for (const sh::OutputVariable &output : state.getOutputVariables())
{
WriteShaderVar(&stream, output);
stream.writeInt(output.location);
stream.writeInt(output.index);
}
stream.writeInt(state.getOutputLocations().size());
for (const auto &outputVar : state.getOutputLocations())
{
stream.writeInt(outputVar.arrayIndex);
stream.writeIntOrNegOne(outputVar.index);
stream.writeInt(outputVar.ignored);
}
stream.writeInt(state.getSecondaryOutputLocations().size());
for (const auto &outputVar : state.getSecondaryOutputLocations())
{
stream.writeInt(outputVar.arrayIndex);
stream.writeIntOrNegOne(outputVar.index);
stream.writeInt(outputVar.ignored);
}
stream.writeInt(state.mOutputVariableTypes.size());
for (const auto &outputVariableType : state.mOutputVariableTypes)
{
stream.writeInt(outputVariableType);
}
static_assert(
IMPLEMENTATION_MAX_DRAW_BUFFERS * 2 <= 8 * sizeof(uint32_t),
"All bits of mDrawBufferTypeMask and mActiveOutputVariables can be contained in 32 bits");
stream.writeInt(static_cast<int>(state.mDrawBufferTypeMask.to_ulong()));
stream.writeInt(static_cast<int>(state.mActiveOutputVariables.to_ulong()));
stream.writeInt(state.getSamplerUniformRange().low());
stream.writeInt(state.getSamplerUniformRange().high());
stream.writeInt(state.getSamplerBindings().size());
for (const auto &samplerBinding : state.getSamplerBindings())
{
stream.writeEnum(samplerBinding.textureType);
stream.writeEnum(samplerBinding.format);
stream.writeInt(samplerBinding.boundTextureUnits.size());
stream.writeInt(samplerBinding.unreferenced);
}
stream.writeInt(state.getImageUniformRange().low());
stream.writeInt(state.getImageUniformRange().high());
stream.writeInt(state.getImageBindings().size());
for (const auto &imageBinding : state.getImageBindings())
{
stream.writeInt(imageBinding.boundImageUnits.size());
for (size_t i = 0; i < imageBinding.boundImageUnits.size(); ++i)
{
stream.writeInt(imageBinding.boundImageUnits[i]);
}
}
stream.writeInt(state.getAtomicCounterUniformRange().low());
stream.writeInt(state.getAtomicCounterUniformRange().high());
stream.writeInt(state.getLinkedShaderStages().to_ulong());
program->getImplementation()->save(context, &stream);
ASSERT(binaryOut);
binaryOut->resize(stream.length());
memcpy(binaryOut->data(), stream.data(), stream.length());
}
// static
void MemoryProgramCache::ComputeHash(const Context *context,
const Program *program,
egl::BlobCache::Key *hashOut)
{
// Compute the program hash. Start with the shader hashes and resource strings.
HashStream hashStream;
for (ShaderType shaderType : AllShaderTypes())
{
hashStream << program->getAttachedShader(shaderType);
}
// Add some ANGLE metadata and Context properties, such as version and back-end.
hashStream << ANGLE_COMMIT_HASH << context->getClientMajorVersion()
<< context->getClientMinorVersion() << context->getString(GL_RENDERER);
// Hash pre-link program properties.
hashStream << program->getAttributeBindings() << program->getUniformLocationBindings()
<< program->getFragmentInputBindings()
<< program->getState().getTransformFeedbackVaryingNames()
<< program->getState().getTransformFeedbackBufferMode()
<< program->getState().getOutputLocations()
<< program->getState().getSecondaryOutputLocations();
// Call the secure SHA hashing function.
const std::string &programKey = hashStream.str();
angle::base::SHA1HashBytes(reinterpret_cast<const unsigned char *>(programKey.c_str()),
programKey.length(), hashOut->data());
}
angle::Result MemoryProgramCache::getProgram(const Context *context,
const Program *program,
ProgramState *state,
egl::BlobCache::Key *hashOut)
{
ComputeHash(context, program, hashOut);
egl::BlobCache::Value binaryProgram;
if (get(context, *hashOut, &binaryProgram))
{
InfoLog infoLog;
angle::Result result = Deserialize(context, program, state, binaryProgram.data(),
binaryProgram.size(), infoLog);
ANGLE_HISTOGRAM_BOOLEAN("GPU.ANGLE.ProgramCache.LoadBinarySuccess",
result == angle::Result::Continue);
ANGLE_TRY(result);
if (result == angle::Result::Continue)
return angle::Result::Continue;
// Cache load failed, evict.
if (mIssuedWarnings++ < kWarningLimit)
{
WARN() << "Failed to load binary from cache: " << infoLog.str();
if (mIssuedWarnings == kWarningLimit)
{
WARN() << "Reaching warning limit for cache load failures, silencing "
"subsequent warnings.";
}
}
remove(*hashOut);
}
return angle::Result::Incomplete;
}
bool MemoryProgramCache::get(const Context *context,
const egl::BlobCache::Key &programHash,
egl::BlobCache::Value *programOut)
{
return mBlobCache.get(context->getScratchBuffer(), programHash, programOut);
}
bool MemoryProgramCache::getAt(size_t index,
const egl::BlobCache::Key **hashOut,
egl::BlobCache::Value *programOut)
{
return mBlobCache.getAt(index, hashOut, programOut);
}
void MemoryProgramCache::remove(const egl::BlobCache::Key &programHash)
{
mBlobCache.remove(programHash);
}
void MemoryProgramCache::putProgram(const egl::BlobCache::Key &programHash,
const Context *context,
const Program *program)
{
angle::MemoryBuffer serializedProgram;
Serialize(context, program, &serializedProgram);
ANGLE_HISTOGRAM_COUNTS("GPU.ANGLE.ProgramCache.ProgramBinarySizeBytes",
static_cast<int>(serializedProgram.size()));
// TODO(syoussefi): to be removed. Compatibility for Chrome until it supports
// EGL_ANDROID_blob_cache. http://anglebug.com/2516
auto *platform = ANGLEPlatformCurrent();
platform->cacheProgram(platform, programHash, serializedProgram.size(),
serializedProgram.data());
mBlobCache.put(programHash, std::move(serializedProgram));
}
void MemoryProgramCache::updateProgram(const Context *context, const Program *program)
{
egl::BlobCache::Key programHash;
ComputeHash(context, program, &programHash);
putProgram(programHash, context, program);
}
void MemoryProgramCache::putBinary(const egl::BlobCache::Key &programHash,
const uint8_t *binary,
size_t length)
{
// Copy the binary.
angle::MemoryBuffer newEntry;
newEntry.resize(length);
memcpy(newEntry.data(), binary, length);
// Store the binary.
mBlobCache.populate(programHash, std::move(newEntry));
}
void MemoryProgramCache::clear()
{
mBlobCache.clear();
mIssuedWarnings = 0;
}
void MemoryProgramCache::resize(size_t maxCacheSizeBytes)
{
mBlobCache.resize(maxCacheSizeBytes);
}
size_t MemoryProgramCache::entryCount() const
{
return mBlobCache.entryCount();
}
size_t MemoryProgramCache::trim(size_t limit)
{
return mBlobCache.trim(limit);
}
size_t MemoryProgramCache::size() const
{
return mBlobCache.size();
}
size_t MemoryProgramCache::maxSize() const
{
return mBlobCache.maxSize();
}
} // namespace gl