Introduce MemoryProgramCache.
This class will manage GPU binary programs in memory. It will be
owned by the Display, and shared between Contexts. Currently this
CL just refactors the Program binary saving and loading into static
members of this new class.
BUG=angleproject:1897
Change-Id: I34f5afb2c02416f6fd80dd65ba3827a8637ce190
Reviewed-on: https://chromium-review.googlesource.com/522873
Commit-Queue: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
diff --git a/src/libANGLE/MemoryProgramCache.cpp b/src/libANGLE/MemoryProgramCache.cpp
new file mode 100644
index 0000000..f1eac54
--- /dev/null
+++ b/src/libANGLE/MemoryProgramCache.cpp
@@ -0,0 +1,364 @@
+//
+// 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 "common/version.h"
+#include "libANGLE/BinaryStream.h"
+#include "libANGLE/Context.h"
+#include "libANGLE/Uniform.h"
+#include "libANGLE/renderer/ProgramImpl.h"
+
+namespace gl
+{
+
+namespace
+{
+
+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->writeInt(var.arraySize);
+ stream->writeInt(var.staticUse);
+ 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();
+ var->arraySize = stream->readInt<unsigned int>();
+ var->staticUse = stream->readBool();
+ var->structName = stream->readString();
+}
+
+} // anonymous namespace
+
+// static
+LinkResult 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 false;
+ }
+
+ 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 false;
+ }
+
+ state->mComputeShaderLocalSize[0] = stream.readInt<int>();
+ state->mComputeShaderLocalSize[1] = stream.readInt<int>();
+ state->mComputeShaderLocalSize[2] = stream.readInt<int>();
+
+ static_assert(MAX_VERTEX_ATTRIBS <= sizeof(unsigned long) * 8,
+ "Too many vertex attribs for mask");
+ state->mActiveAttribLocationsMask = stream.readInt<unsigned long>();
+
+ 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.blockIndex = 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();
+
+ 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.readString(&variable.name);
+ stream.readInt(&variable.element);
+ stream.readInt(&variable.index);
+ stream.readBool(&variable.used);
+ 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)
+ {
+ UniformBlock uniformBlock;
+ stream.readString(&uniformBlock.name);
+ stream.readBool(&uniformBlock.isArray);
+ stream.readInt(&uniformBlock.arrayElement);
+ stream.readInt(&uniformBlock.binding);
+ stream.readInt(&uniformBlock.dataSize);
+ stream.readBool(&uniformBlock.vertexStaticUse);
+ stream.readBool(&uniformBlock.fragmentStaticUse);
+
+ unsigned int numMembers = stream.readInt<unsigned int>();
+ for (unsigned int blockMemberIndex = 0; blockMemberIndex < numMembers; blockMemberIndex++)
+ {
+ uniformBlock.memberUniformIndexes.push_back(stream.readInt<unsigned int>());
+ }
+
+ state->mUniformBlocks.push_back(uniformBlock);
+ }
+
+ for (GLuint bindingIndex = 0; bindingIndex < state->mUniformBlockBindings.size();
+ ++bindingIndex)
+ {
+ stream.readInt(&state->mUniformBlockBindings[bindingIndex]);
+ state->mActiveUniformBlockBindings.set(bindingIndex,
+ state->mUniformBlockBindings[bindingIndex] != 0);
+ }
+
+ unsigned int transformFeedbackVaryingCount = stream.readInt<unsigned int>();
+ ASSERT(state->mLinkedTransformFeedbackVaryings.empty());
+ for (unsigned int transformFeedbackVaryingIndex = 0;
+ transformFeedbackVaryingIndex < transformFeedbackVaryingCount;
+ ++transformFeedbackVaryingIndex)
+ {
+ sh::Varying varying;
+ stream.readInt(&varying.arraySize);
+ 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>();
+ state->mOutputVariables.push_back(output);
+ }
+
+ unsigned int outputVarCount = stream.readInt<unsigned int>();
+ for (unsigned int outputIndex = 0; outputIndex < outputVarCount; ++outputIndex)
+ {
+ int locationIndex = stream.readInt<int>();
+ VariableLocation locationData;
+ stream.readInt(&locationData.element);
+ stream.readInt(&locationData.index);
+ stream.readString(&locationData.name);
+ state->mOutputLocations[locationIndex] = 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 < 8 * sizeof(uint32_t),
+ "All bits of DrawBufferMask can be contained in an uint32_t");
+ state->mActiveOutputVariables = stream.readInt<uint32_t>();
+
+ unsigned int low = stream.readInt<unsigned int>();
+ unsigned int high = stream.readInt<unsigned int>();
+ state->mSamplerUniformRange = RangeUI(low, high);
+
+ unsigned int samplerCount = stream.readInt<unsigned int>();
+ for (unsigned int samplerIndex = 0; samplerIndex < samplerCount; ++samplerIndex)
+ {
+ GLenum textureType = stream.readInt<GLenum>();
+ size_t bindingCount = stream.readInt<size_t>();
+ state->mSamplerBindings.emplace_back(SamplerBinding(textureType, bindingCount));
+ }
+
+ 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]);
+
+ 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.blockIndex);
+ 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.writeString(variable.name);
+ stream.writeInt(variable.element);
+ stream.writeInt(variable.index);
+ stream.writeInt(variable.used);
+ stream.writeInt(variable.ignored);
+ }
+
+ stream.writeInt(state.getUniformBlocks().size());
+ for (const UniformBlock &uniformBlock : state.getUniformBlocks())
+ {
+ stream.writeString(uniformBlock.name);
+ stream.writeInt(uniformBlock.isArray);
+ stream.writeInt(uniformBlock.arrayElement);
+ stream.writeInt(uniformBlock.binding);
+ stream.writeInt(uniformBlock.dataSize);
+
+ stream.writeInt(uniformBlock.vertexStaticUse);
+ stream.writeInt(uniformBlock.fragmentStaticUse);
+
+ stream.writeInt(uniformBlock.memberUniformIndexes.size());
+ for (unsigned int memberUniformIndex : uniformBlock.memberUniformIndexes)
+ {
+ stream.writeInt(memberUniformIndex);
+ }
+ }
+
+ for (GLuint binding : state.getUniformBlockBindings())
+ {
+ stream.writeInt(binding);
+ }
+
+ stream.writeInt(state.getLinkedTransformFeedbackVaryings().size());
+ for (const auto &var : state.getLinkedTransformFeedbackVaryings())
+ {
+ stream.writeInt(var.arraySize);
+ 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(state.getOutputLocations().size());
+ for (const auto &outputPair : state.getOutputLocations())
+ {
+ stream.writeInt(outputPair.first);
+ stream.writeIntOrNegOne(outputPair.second.element);
+ stream.writeInt(outputPair.second.index);
+ stream.writeString(outputPair.second.name);
+ }
+
+ stream.writeInt(state.mOutputVariableTypes.size());
+ for (const auto &outputVariableType : state.mOutputVariableTypes)
+ {
+ stream.writeInt(outputVariableType);
+ }
+
+ static_assert(IMPLEMENTATION_MAX_DRAW_BUFFERS < 8 * sizeof(uint32_t),
+ "All bits of DrawBufferMask can be contained in an uint32_t");
+ stream.writeInt(static_cast<uint32_t>(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.writeInt(samplerBinding.textureType);
+ stream.writeInt(samplerBinding.boundTextureUnits.size());
+ }
+
+ program->getImplementation()->save(&stream);
+
+ ASSERT(binaryOut);
+ binaryOut->resize(stream.length());
+ memcpy(binaryOut->data(), stream.data(), stream.length());
+}
+
+} // namespace gl