Add state for uniform blocks to ProgramBinary, along with loading and saving of the new binary format.

Updated Version.h with this patch.

TRAC #22858

Signed-off-by: Geoff Lang
Signed-off-by: Nicolas Capens
Author: Jamie Madill
Conflicts:

	src/common/version.h

git-svn-id: https://angleproject.googlecode.com/svn/branches/es3proto@2306 736b8ea6-26fd-11df-bfd4-992fa37f6226
diff --git a/src/libGLESv2/ProgramBinary.cpp b/src/libGLESv2/ProgramBinary.cpp
index 90d4fab..b050167 100644
--- a/src/libGLESv2/ProgramBinary.cpp
+++ b/src/libGLESv2/ProgramBinary.cpp
@@ -107,6 +107,12 @@
         delete mUniforms.back();
         mUniforms.pop_back();
     }
+
+    while (!mUniformBlocks.empty())
+    {
+        delete mUniformBlocks.back();
+        mUniformBlocks.pop_back();
+    }
 }
 
 unsigned int ProgramBinary::getSerial() const
@@ -1461,13 +1467,27 @@
         GLenum precision;
         std::string name;
         unsigned int arraySize;
+        int blockIndex;
 
         stream.read(&type);
         stream.read(&precision);
         stream.read(&name);
         stream.read(&arraySize);
+        stream.read(&blockIndex);
 
-        mUniforms[i] = new Uniform(type, precision, name, arraySize);
+        int offset;
+        int arrayStride;
+        int matrixStride;
+        bool isRowMajorMatrix;
+
+        stream.read(&offset);
+        stream.read(&arrayStride);
+        stream.read(&matrixStride);
+        stream.read(&isRowMajorMatrix);
+
+        const sh::BlockMemberInfo blockInfo(offset, arrayStride, matrixStride, isRowMajorMatrix);
+
+        mUniforms[i] = new Uniform(type, precision, name, arraySize, blockIndex, blockInfo);
         
         stream.read(&mUniforms[i]->psRegisterIndex);
         stream.read(&mUniforms[i]->vsRegisterIndex);
@@ -1481,6 +1501,39 @@
         return false;
     }
 
+    mUniformBlocks.resize(size);
+    for (unsigned int uniformBlockIndex = 0; uniformBlockIndex < size; ++uniformBlockIndex)
+    {
+        std::string name;
+        unsigned int elementIndex;
+        unsigned int dataSize;
+
+        stream.read(&name);
+        stream.read(&elementIndex);
+        stream.read(&dataSize);
+
+        mUniformBlocks[uniformBlockIndex] = new UniformBlock(name, elementIndex, dataSize);
+
+        UniformBlock& uniformBlock = *mUniformBlocks[uniformBlockIndex];
+        stream.read(&uniformBlock.psRegisterIndex);
+        stream.read(&uniformBlock.vsRegisterIndex);
+
+        size_t numMembers;
+        stream.read(&numMembers);
+        uniformBlock.memberUniformIndexes.resize(numMembers);
+        for (unsigned int blockMemberIndex = 0; blockMemberIndex < numMembers; blockMemberIndex++)
+        {
+            stream.read(&uniformBlock.memberUniformIndexes[blockMemberIndex]);
+        }
+    }
+
+    stream.read(&size);
+    if (stream.error())
+    {
+        infoLog.append("Invalid program binary.");
+        return false;
+    }
+
     mUniformIndex.resize(size);
     for (unsigned int i = 0; i < size; ++i)
     {
@@ -1592,16 +1645,43 @@
     stream.write(mUsesPointSize);
 
     stream.write(mUniforms.size());
-    for (unsigned int i = 0; i < mUniforms.size(); ++i)
+    for (unsigned int uniformIndex = 0; uniformIndex < mUniforms.size(); ++uniformIndex)
     {
-        stream.write(mUniforms[i]->type);
-        stream.write(mUniforms[i]->precision);
-        stream.write(mUniforms[i]->name);
-        stream.write(mUniforms[i]->arraySize);
+        const Uniform &uniform = *mUniforms[uniformIndex];
 
-        stream.write(mUniforms[i]->psRegisterIndex);
-        stream.write(mUniforms[i]->vsRegisterIndex);
-        stream.write(mUniforms[i]->registerCount);
+        stream.write(uniform.type);
+        stream.write(uniform.precision);
+        stream.write(uniform.name);
+        stream.write(uniform.arraySize);
+        stream.write(uniform.blockIndex);
+
+        stream.write(uniform.blockInfo.offset);
+        stream.write(uniform.blockInfo.arrayStride);
+        stream.write(uniform.blockInfo.matrixStride);
+        stream.write(uniform.blockInfo.isRowMajorMatrix);
+
+        stream.write(uniform.psRegisterIndex);
+        stream.write(uniform.vsRegisterIndex);
+        stream.write(uniform.registerCount);
+    }
+
+    stream.write(mUniformBlocks.size());
+    for (unsigned int uniformBlockIndex = 0; uniformBlockIndex < mUniformBlocks.size(); ++uniformBlockIndex)
+    {
+        const UniformBlock& uniformBlock = *mUniformBlocks[uniformBlockIndex];
+
+        stream.write(uniformBlock.name);
+        stream.write(uniformBlock.elementIndex);
+        stream.write(uniformBlock.dataSize);
+
+        stream.write(uniformBlock.memberUniformIndexes.size());
+        for (unsigned int blockMemberIndex = 0; blockMemberIndex < uniformBlock.memberUniformIndexes.size(); blockMemberIndex++)
+        {
+            stream.write(uniformBlock.memberUniformIndexes[blockMemberIndex]);
+        }
+
+        stream.write(uniformBlock.psRegisterIndex);
+        stream.write(uniformBlock.vsRegisterIndex);
     }
 
     stream.write(mUniformIndex.size());
@@ -1748,9 +1828,9 @@
     // special case for gl_DepthRange, the only built-in uniform (also a struct)
     if (vertexShader->mUsesDepthRange || fragmentShader->mUsesDepthRange)
     {
-        mUniforms.push_back(new Uniform(GL_FLOAT, GL_HIGH_FLOAT, "gl_DepthRange.near", 0));
-        mUniforms.push_back(new Uniform(GL_FLOAT, GL_HIGH_FLOAT, "gl_DepthRange.far", 0));
-        mUniforms.push_back(new Uniform(GL_FLOAT, GL_HIGH_FLOAT, "gl_DepthRange.diff", 0));
+        mUniforms.push_back(new Uniform(GL_FLOAT, GL_HIGH_FLOAT, "gl_DepthRange.near", 0, -1, sh::BlockMemberInfo::defaultBlockInfo));
+        mUniforms.push_back(new Uniform(GL_FLOAT, GL_HIGH_FLOAT, "gl_DepthRange.far", 0, -1, sh::BlockMemberInfo::defaultBlockInfo));
+        mUniforms.push_back(new Uniform(GL_FLOAT, GL_HIGH_FLOAT, "gl_DepthRange.diff", 0, -1, sh::BlockMemberInfo::defaultBlockInfo));
     }
 
     return success;
@@ -1914,7 +1994,7 @@
     }
     else
     {
-        uniform = new Uniform(constant.type, constant.precision, constant.name, constant.arraySize);
+        uniform = new Uniform(constant.type, constant.precision, constant.name, constant.arraySize, -1, sh::BlockMemberInfo::defaultBlockInfo);
     }
 
     if (!uniform)
@@ -2241,17 +2321,12 @@
       case GL_UNIFORM_TYPE:         return static_cast<GLint>(uniform.type);
       case GL_UNIFORM_SIZE:         return static_cast<GLint>(uniform.elementCount());
       case GL_UNIFORM_NAME_LENGTH:  return static_cast<GLint>(uniform.name.size() + 1);
+      case GL_UNIFORM_BLOCK_INDEX:  return uniform.blockIndex;
 
-      case GL_UNIFORM_BLOCK_INDEX:
-      case GL_UNIFORM_OFFSET:
-      case GL_UNIFORM_ARRAY_STRIDE:
-      case GL_UNIFORM_MATRIX_STRIDE:
-        // the default block gives a value of -1 for these parameters
-        return -1;
-
-      case GL_UNIFORM_IS_ROW_MAJOR:
-        // TODO: column/row major layout for uniform blocks
-        return 0;
+      case GL_UNIFORM_OFFSET:       return uniform.blockInfo.offset;
+      case GL_UNIFORM_ARRAY_STRIDE: return uniform.blockInfo.arrayStride;
+      case GL_UNIFORM_MATRIX_STRIDE: return uniform.blockInfo.matrixStride;
+      case GL_UNIFORM_IS_ROW_MAJOR: return static_cast<GLint>(uniform.blockInfo.isRowMajorMatrix);
 
       default:
         UNREACHABLE();
diff --git a/src/libGLESv2/ProgramBinary.h b/src/libGLESv2/ProgramBinary.h
index 2589d82..709d43a 100644
--- a/src/libGLESv2/ProgramBinary.h
+++ b/src/libGLESv2/ProgramBinary.h
@@ -184,6 +184,7 @@
     bool mUsesPointSize;
 
     UniformArray mUniforms;
+    UniformBlockArray mUniformBlocks;
     typedef std::vector<UniformLocation> UniformIndex;
     UniformIndex mUniformIndex;
 
diff --git a/src/libGLESv2/Uniform.cpp b/src/libGLESv2/Uniform.cpp
index ed8d490..eb73f83 100644
--- a/src/libGLESv2/Uniform.cpp
+++ b/src/libGLESv2/Uniform.cpp
@@ -12,17 +12,28 @@
 namespace gl
 {
 
-Uniform::Uniform(GLenum type, GLenum precision, const std::string &name, unsigned int arraySize)
-    : type(type), precision(precision), name(name), arraySize(arraySize)
+Uniform::Uniform(GLenum type, GLenum precision, const std::string &name, unsigned int arraySize, const int blockIndex, const sh::BlockMemberInfo &blockInfo)
+    : type(type),
+      precision(precision),
+      name(name),
+      arraySize(arraySize),
+      blockIndex(blockIndex),
+      blockInfo(blockInfo),
+      data(NULL),
+      dirty(true),
+      psRegisterIndex(GL_INVALID_INDEX),
+      vsRegisterIndex(GL_INVALID_INDEX),
+      registerCount(0)
 {
-    int bytes = gl::UniformInternalSize(type) * elementCount();
-    data = new unsigned char[bytes];
-    memset(data, 0, bytes);
-    dirty = true;
-
-    psRegisterIndex = GL_INVALID_INDEX;
-    vsRegisterIndex = GL_INVALID_INDEX;
-    registerCount = VariableRowCount(type) * elementCount();
+    // We use data storage for default block uniforms to cache values that are sent to D3D during rendering
+    // Uniform blocks/buffers are treated separately by the Renderer (ES3 path only)
+    if (isInDefaultBlock())
+    {
+        size_t bytes = UniformInternalSize(type) * elementCount();
+        data = new unsigned char[bytes];
+        memset(data, 0, bytes);
+        registerCount = VariableRowCount(type) * elementCount();
+    }
 }
 
 Uniform::~Uniform()
@@ -50,4 +61,33 @@
     return psRegisterIndex != GL_INVALID_INDEX;
 }
 
+bool Uniform::isInDefaultBlock() const
+{
+    return blockIndex == -1;
+}
+
+UniformBlock::UniformBlock(const std::string &name, unsigned int elementIndex, unsigned int dataSize)
+    : name(name),
+      elementIndex(elementIndex),
+      dataSize(dataSize),
+      psRegisterIndex(GL_INVALID_INDEX),
+      vsRegisterIndex(GL_INVALID_INDEX)
+{
+}
+
+bool UniformBlock::isArrayElement() const
+{
+    return elementIndex != GL_INVALID_INDEX;
+}
+
+bool UniformBlock::isReferencedByVertexShader() const
+{
+    return vsRegisterIndex != GL_INVALID_INDEX;
+}
+
+bool UniformBlock::isReferencedByFragmentShader() const
+{
+    return psRegisterIndex != GL_INVALID_INDEX;
+}
+
 }
diff --git a/src/libGLESv2/Uniform.h b/src/libGLESv2/Uniform.h
index 12e8e03..d2a9b75 100644
--- a/src/libGLESv2/Uniform.h
+++ b/src/libGLESv2/Uniform.h
@@ -15,6 +15,8 @@
 #include <GLES2/gl2.h>
 
 #include "common/debug.h"
+#include "angletypes.h"
+#include "compiler/Uniform.h"
 
 namespace gl
 {
@@ -22,7 +24,7 @@
 // Helper struct representing a single shader uniform
 struct Uniform
 {
-    Uniform(GLenum type, GLenum precision, const std::string &name, unsigned int arraySize);
+    Uniform(GLenum type, GLenum precision, const std::string &name, unsigned int arraySize, const int blockIndex, const sh::BlockMemberInfo &blockInfo);
 
     ~Uniform();
 
@@ -30,11 +32,14 @@
     unsigned int elementCount() const;
     bool isReferencedByVertexShader() const;
     bool isReferencedByFragmentShader() const;
+    bool isInDefaultBlock() const;
 
     const GLenum type;
     const GLenum precision;
     const std::string name;
     const unsigned int arraySize;
+    const int blockIndex;
+    const sh::BlockMemberInfo blockInfo;
 
     unsigned char *data;
     bool dirty;
@@ -46,6 +51,28 @@
 
 typedef std::vector<Uniform*> UniformArray;
 
+// Helper struct representing a single shader uniform block
+struct UniformBlock
+{
+    // use GL_INVALID_INDEX for non-array elements
+    UniformBlock(const std::string &name, unsigned int elementIndex, unsigned int dataSize);
+
+    bool isArrayElement() const;
+    bool isReferencedByVertexShader() const;
+    bool isReferencedByFragmentShader() const;
+
+    const std::string name;
+    const unsigned int elementIndex;
+    const unsigned int dataSize;
+
+    std::vector<unsigned int> memberUniformIndexes;
+
+    unsigned int psRegisterIndex;
+    unsigned int vsRegisterIndex;
+};
+
+typedef std::vector<UniformBlock*> UniformBlockArray;
+
 }
 
 #endif   // LIBGLESV2_UNIFORM_H_