Add support for structures in interface and uniform blocks.

Also redesigns the uniform block layout computation to be more general.

TRAC #23018

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

git-svn-id: https://angleproject.googlecode.com/svn/branches/es3proto@2387 736b8ea6-26fd-11df-bfd4-992fa37f6226
diff --git a/src/compiler/Uniform.cpp b/src/compiler/Uniform.cpp
index e55af75..917e08f 100644
--- a/src/compiler/Uniform.cpp
+++ b/src/compiler/Uniform.cpp
@@ -33,151 +33,172 @@
 InterfaceBlock::InterfaceBlock(const char *name, unsigned int arraySize, unsigned int registerIndex)
     : name(name),
       arraySize(arraySize),
+      layout(BLOCKLAYOUT_SHARED),
       registerIndex(registerIndex)
 {
 }
 
 // Use the same layout for packed and shared
-void InterfaceBlock::setSharedBlockLayout()
+void InterfaceBlock::setBlockLayout(BlockLayoutType newLayout)
 {
-    setPackedBlockLayout();
+    layout = newLayout;
+
+    const size_t componentSize = 4;
+    unsigned int currentOffset = 0;
+
+    blockInfo.clear();
+    getBlockLayoutInfo(activeUniforms, &currentOffset);
+
+    dataSize = currentOffset * componentSize;
+}
+
+void InterfaceBlock::getBlockLayoutInfo(const sh::ActiveUniforms &fields, unsigned int *currentOffset)
+{
+    const size_t componentSize = 4;
+
+    // TODO: row major matrices
+    bool isRowMajorMatrix = false;
+
+    for (unsigned int fieldIndex = 0; fieldIndex < fields.size(); fieldIndex++)
+    {
+        int arrayStride;
+        int matrixStride;
+
+        const sh::Uniform &uniform = fields[fieldIndex];
+
+        if (getBlockLayoutInfo(uniform, currentOffset, &arrayStride, &matrixStride))
+        {
+            const BlockMemberInfo memberInfo(*currentOffset * componentSize, arrayStride * componentSize, matrixStride * componentSize, isRowMajorMatrix);
+            blockInfo.push_back(memberInfo);
+
+            if (uniform.arraySize > 0)
+            {
+                *currentOffset += arrayStride * uniform.arraySize;
+            }
+            else if (gl::IsMatrixType(uniform.type))
+            {
+                const int componentGroups = (isRowMajorMatrix ? gl::VariableRowCount(uniform.type) : gl::VariableColumnCount(uniform.type));
+                *currentOffset += matrixStride * componentGroups;
+            }
+            else
+            {
+                *currentOffset += gl::UniformComponentCount(uniform.type);
+            }
+        }
+    }
+}
+
+bool InterfaceBlock::getBlockLayoutInfo(const sh::Uniform &uniform, unsigned int *currentOffset, int *arrayStrideOut, int *matrixStrideOut)
+{
+    if (!uniform.fields.empty())
+    {
+        getBlockLayoutInfo(uniform.fields, currentOffset);
+        return false;
+    }
+
+    switch (layout)
+    {
+      case BLOCKLAYOUT_SHARED:
+      case BLOCKLAYOUT_PACKED:
+        getD3DLayoutInfo(uniform, currentOffset, arrayStrideOut, matrixStrideOut);
+        return true;
+
+      case BLOCKLAYOUT_STANDARD:
+        getStandardLayoutInfo(uniform, currentOffset, arrayStrideOut, matrixStrideOut);
+        return true;
+
+      default:
+        UNREACHABLE();
+        return false;
+    }
 }
 
 // Block layout packed according to the default D3D11 register packing rules
 // See http://msdn.microsoft.com/en-us/library/windows/desktop/bb509632(v=vs.85).aspx
-void InterfaceBlock::setPackedBlockLayout()
+void InterfaceBlock::getD3DLayoutInfo(const sh::Uniform &uniform, unsigned int *currentOffset, int *arrayStrideOut, int *matrixStrideOut)
 {
-    const size_t componentSize = 4;
+    ASSERT(uniform.fields.empty());
+
     const unsigned int registerSize = 4;
-    unsigned int currentOffset = 0;
+    const size_t componentSize = 4;
 
-    blockInfo.clear();
+    // TODO: row major matrices
+    bool isRowMajorMatrix = false;
+    // We assume we are only dealing with 4 byte components (no doubles or half-words currently)
+    ASSERT(gl::UniformComponentSize(gl::UniformComponentType(uniform.type)) == componentSize);
+    int matrixStride = 0;
+    int arrayStride = 0;
 
-    for (unsigned int uniformIndex = 0; uniformIndex < activeUniforms.size(); uniformIndex++)
+    if (gl::IsMatrixType(uniform.type))
     {
-        const sh::Uniform &uniform = activeUniforms[uniformIndex];
+        *currentOffset = rx::roundUp(*currentOffset, 4u);
+        matrixStride = registerSize;
 
-        // TODO: structs
-        // TODO: row major matrices
-        bool isRowMajorMatrix = false;
-
-        // We assume we are only dealing with 4 byte components (no doubles or half-words currently)
-        ASSERT(gl::UniformComponentSize(gl::UniformComponentType(uniform.type)) == componentSize);
-
-        int arrayStride = 0;
-        int matrixStride = 0;
-
-        if (gl::IsMatrixType(uniform.type))
-        {
-            currentOffset = rx::roundUp(currentOffset, 4u);
-            matrixStride = registerSize;
-
-            if (uniform.arraySize > 0)
-            {
-                const int componentGroups = (isRowMajorMatrix ? gl::VariableColumnCount(uniform.type) : gl::VariableRowCount(uniform.type));
-                arrayStride = matrixStride * componentGroups;
-            }
-        }
-        else if (uniform.arraySize > 0)
-        {
-            currentOffset = rx::roundUp(currentOffset, registerSize);
-            arrayStride = registerSize;
-        }
-        else
-        {
-            int numComponents = gl::UniformComponentCount(uniform.type);
-            if ((numComponents + (currentOffset % registerSize)) >= registerSize)
-            {
-                currentOffset = rx::roundUp(currentOffset, registerSize);
-            }
-        }
-
-        BlockMemberInfo memberInfo(currentOffset * componentSize, arrayStride * componentSize, matrixStride * componentSize, isRowMajorMatrix);
-        blockInfo.push_back(memberInfo);
-
-        // for arrays/matrices, the next element is in a multiple of register size
         if (uniform.arraySize > 0)
         {
-            currentOffset += arrayStride * uniform.arraySize;
+            const int componentGroups = (isRowMajorMatrix ? gl::VariableRowCount(uniform.type) : gl::VariableColumnCount(uniform.type));
+            arrayStride = matrixStride * componentGroups;
         }
-        else if (gl::IsMatrixType(uniform.type))
+    }
+    else if (uniform.arraySize > 0)
+    {
+        *currentOffset = rx::roundUp(*currentOffset, registerSize);
+        arrayStride = registerSize;
+    }
+    else
+    {
+        int numComponents = gl::UniformComponentCount(uniform.type);
+        if ((numComponents + (*currentOffset % registerSize)) >= registerSize)
         {
-            const int componentGroups = (isRowMajorMatrix ? gl::VariableColumnCount(uniform.type) : gl::VariableRowCount(uniform.type));
-            currentOffset += matrixStride * componentGroups;
-        }
-        else
-        {
-            currentOffset += gl::UniformComponentCount(uniform.type);
+            *currentOffset = rx::roundUp(*currentOffset, registerSize);
         }
     }
 
-    dataSize = currentOffset * componentSize;
+    *matrixStrideOut = matrixStride;
+    *arrayStrideOut = arrayStride;
 }
 
-void InterfaceBlock::setStandardBlockLayout()
+// Block layout according to the std140 block layout
+// See "Standard Uniform Block Layout" in Section 2.11.6 of the OpenGL ES 3.0 specification
+void InterfaceBlock::getStandardLayoutInfo(const sh::Uniform &uniform, unsigned int *currentOffset, int *arrayStrideOut, int *matrixStrideOut)
 {
+    ASSERT(uniform.fields.empty());
+
     const size_t componentSize = 4;
-    unsigned int currentOffset = 0;
 
-    blockInfo.clear();
+    // TODO: row major matrices
+    bool isRowMajorMatrix = false;
 
-    for (unsigned int uniformIndex = 0; uniformIndex < activeUniforms.size(); uniformIndex++)
+    // We assume we are only dealing with 4 byte components (no doubles or half-words currently)
+    ASSERT(gl::UniformComponentSize(gl::UniformComponentType(uniform.type)) == componentSize);
+
+    int numComponents = gl::UniformComponentCount(uniform.type);
+    size_t baseAlignment = static_cast<size_t>(numComponents == 3 ? 4 : numComponents);
+    int matrixStride = 0;
+    int arrayStride = 0;
+
+    if (gl::IsMatrixType(uniform.type))
     {
-        const sh::Uniform &uniform = activeUniforms[uniformIndex];
-
-        // TODO: structs
-        // TODO: row major matrices
-        bool isRowMajorMatrix = false;
-
-        // We assume we are only dealing with 4 byte components (no doubles or half-words currently)
-        ASSERT(gl::UniformComponentSize(gl::UniformComponentType(uniform.type)) == componentSize);
-
-        int numComponents = gl::UniformComponentCount(uniform.type);
-        size_t baseAlignment = static_cast<size_t>(numComponents == 3 ? 4 : numComponents);
-        int arrayStride = 0;
-        int matrixStride = 0;
-
-        if (gl::IsMatrixType(uniform.type))
-        {
-            numComponents = (isRowMajorMatrix ? gl::VariableRowCount(uniform.type) : gl::VariableColumnCount(uniform.type));
-            baseAlignment = rx::roundUp(baseAlignment, 4u);
-            matrixStride = baseAlignment;
-
-            if (uniform.arraySize > 0)
-            {
-                const int componentGroups = (isRowMajorMatrix ? gl::VariableColumnCount(uniform.type) : gl::VariableRowCount(uniform.type));
-                arrayStride = matrixStride * componentGroups;
-            }
-        }
-        else if (uniform.arraySize > 0)
-        {
-            baseAlignment = rx::roundUp(baseAlignment, 4u);
-            arrayStride = baseAlignment;
-        }
-
-        const unsigned int alignedOffset = rx::roundUp(currentOffset, baseAlignment);
-
-        BlockMemberInfo memberInfo(alignedOffset * componentSize, arrayStride * componentSize, matrixStride * componentSize, isRowMajorMatrix);
-        blockInfo.push_back(memberInfo);
+        numComponents = (isRowMajorMatrix ? gl::VariableColumnCount(uniform.type) : gl::VariableRowCount(uniform.type));
+        baseAlignment = rx::roundUp(baseAlignment, 4u);
+        matrixStride = baseAlignment;
 
         if (uniform.arraySize > 0)
         {
-            currentOffset += arrayStride * uniform.arraySize;
-            currentOffset = rx::roundUp(currentOffset, baseAlignment);
-        }
-        else if (gl::IsMatrixType(uniform.type))
-        {
-            const int componentGroups = (isRowMajorMatrix ? gl::VariableColumnCount(uniform.type) : gl::VariableRowCount(uniform.type));
-            currentOffset += matrixStride * componentGroups;
-            currentOffset = rx::roundUp(currentOffset, baseAlignment);
-        }
-        else
-        {
-            currentOffset += gl::UniformComponentCount(uniform.type);
+            const int componentGroups = (isRowMajorMatrix ? gl::VariableRowCount(uniform.type) : gl::VariableColumnCount(uniform.type));
+            arrayStride = matrixStride * componentGroups;
         }
     }
+    else if (uniform.arraySize > 0)
+    {
+        baseAlignment = rx::roundUp(baseAlignment, 4u);
+        arrayStride = baseAlignment;
+    }
 
-    dataSize = currentOffset * componentSize;
+    *currentOffset = rx::roundUp(*currentOffset, baseAlignment);
+
+    *matrixStrideOut = matrixStride;
+    *arrayStrideOut = arrayStride;
 }
 
 }