Add support to the HLSL translator for arrays of named interface blocks.

TRAC #22930

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

git-svn-id: https://angleproject.googlecode.com/svn/branches/es3proto@2347 736b8ea6-26fd-11df-bfd4-992fa37f6226
diff --git a/src/compiler/OutputHLSL.cpp b/src/compiler/OutputHLSL.cpp
index 6057e6b..89bb72d 100644
--- a/src/compiler/OutputHLSL.cpp
+++ b/src/compiler/OutputHLSL.cpp
@@ -168,6 +168,81 @@
     }
 }
 
+TString OutputHLSL::decoratePrivate(const TString &privateText)
+{
+    return "dx_" + privateText;
+}
+
+TString OutputHLSL::interfaceBlockStructName(const TType &interfaceBlockType)
+{
+    return decoratePrivate(interfaceBlockType.getTypeName()) + "_type";
+}
+
+TString OutputHLSL::interfaceBlockInstanceString(const TType& interfaceBlockType, unsigned int arrayIndex)
+{
+    if (!interfaceBlockType.hasInstanceName())
+    {
+        return "";
+    }
+    else if (interfaceBlockType.isArray())
+    {
+        return decoratePrivate(interfaceBlockType.getInstanceName()) + "_" + str(arrayIndex);
+    }
+    else
+    {
+        return decorate(interfaceBlockType.getInstanceName());
+    }
+}
+
+TString OutputHLSL::interfaceBlockMemberString(const TTypeList &typeList)
+{
+    TString hlsl;
+
+    // TODO: padding for standard layout
+
+    for (unsigned int typeIndex = 0; typeIndex < typeList.size(); typeIndex++)
+    {
+        const TType &memberType = *typeList[typeIndex].type;
+        hlsl += "    " + typeString(memberType) + " " + decorate(memberType.getFieldName()) + arrayString(memberType) + ";\n";
+    }
+
+    return hlsl;
+}
+
+TString OutputHLSL::interfaceBlockStructString(const TType &interfaceBlockType)
+{
+    const TTypeList &typeList = *interfaceBlockType.getStruct();
+
+    return "struct " + interfaceBlockStructName(interfaceBlockType) + "\n"
+           "{\n" +
+           interfaceBlockMemberString(typeList) +
+           "};\n\n";
+}
+
+TString OutputHLSL::interfaceBlockString(const TType &interfaceBlockType, unsigned int registerIndex, unsigned int arrayIndex)
+{
+    const TString &arrayIndexString =  (arrayIndex != GL_INVALID_INDEX ? decorate(str(arrayIndex)) : "");
+    const TString &blockName = interfaceBlockType.getTypeName() + arrayIndexString;
+    TString hlsl;
+
+    hlsl += "cbuffer " + blockName + " : register(b" + str(registerIndex) + ")\n"
+            "{\n";
+
+    if (interfaceBlockType.hasInstanceName())
+    {
+        hlsl += "    " + interfaceBlockStructName(interfaceBlockType) + " " + interfaceBlockInstanceString(interfaceBlockType, arrayIndex) + ";\n";
+    }
+    else
+    {
+        const TTypeList &typeList = *interfaceBlockType.getStruct();
+        hlsl += interfaceBlockMemberString(typeList);
+    }
+
+    hlsl += "};\n\n";
+
+    return hlsl;
+}
+
 void OutputHLSL::header()
 {
     ShShaderType shaderType = mContext.shaderType;
@@ -185,6 +260,7 @@
 
     TString uniforms;
     TString interfaceBlocks;
+    TString interfaceBlockInit;
     TString varyings;
     TString attributes;
 
@@ -217,7 +293,8 @@
         const TString &blockName = interfaceBlockType.getTypeName();
         const TTypeList &typeList = *interfaceBlockType.getStruct();
 
-        sh::InterfaceBlock interfaceBlock(blockName.c_str(), 0, mInterfaceBlockRegister++);
+        const unsigned int arraySize = interfaceBlockType.isArray() ? interfaceBlockType.getArraySize() : 0;
+        sh::InterfaceBlock interfaceBlock(blockName.c_str(), arraySize, mInterfaceBlockRegister);
         for (unsigned int typeIndex = 0; typeIndex < typeList.size(); typeIndex++)
         {
             const TType &memberType = *typeList[typeIndex].type;
@@ -225,31 +302,43 @@
             declareUniformToList(memberType, fullUniformName, typeIndex, interfaceBlock.activeUniforms);
         }
 
+        mInterfaceBlockRegister += std::max(1u, interfaceBlock.arraySize);
+
         // TODO: handle other block layouts
         interfaceBlock.setPackedBlockLayout();
         mActiveInterfaceBlocks.push_back(interfaceBlock);
 
-        interfaceBlocks += "cbuffer " + blockName + " : register(b" + str(interfaceBlock.registerIndex) + ")\n"
-                           "{\n";
-
         if (interfaceBlockType.hasInstanceName())
         {
-            interfaceBlocks += "  struct {\n";
+            interfaceBlocks += interfaceBlockStructString(interfaceBlockType);
         }
 
-        for (unsigned int typeIndex = 0; typeIndex < typeList.size(); typeIndex++)
+        if (arraySize > 0)
         {
-            // TODO: padding for standard layout
-            const TType &memberType = *typeList[typeIndex].type;
-            interfaceBlocks += "    " + typeString(memberType) + " " + decorate(memberType.getFieldName()) + arrayString(memberType) + ";\n";
-        }
+            interfaceBlocks += "static " + interfaceBlockStructName(interfaceBlockType) + " " + decorate(interfaceBlockType.getInstanceName()) +
+                               arrayString(interfaceBlockType) + ";\n\n";
 
-        if (interfaceBlockType.hasInstanceName())
+            for (unsigned int arrayIndex = 0; arrayIndex < arraySize; arrayIndex++)
+            {
+                interfaceBlocks += interfaceBlockString(interfaceBlockType, interfaceBlock.registerIndex + arrayIndex, arrayIndex);
+
+                const TString &instanceName = interfaceBlockType.getInstanceName();
+                interfaceBlockInit += "   " + decorate(instanceName) + "[" + str(arrayIndex) + "] = " +
+                                      interfaceBlockInstanceString(interfaceBlockType, arrayIndex) + ";\n";
+            }
+        }
+        else
         {
-            interfaceBlocks += "  } " + decorate(interfaceBlockType.getInstanceName()) + ";\n";
+            interfaceBlocks += interfaceBlockString(interfaceBlockType, interfaceBlock.registerIndex, GL_INVALID_INDEX);
         }
+    }
 
-        interfaceBlocks += "};\n\n";
+    if (!interfaceBlockInit.empty())
+    {
+        interfaceBlocks += "void dx_initConstantBuffers()\n"
+                           "{\n" +
+                           interfaceBlockInit +
+                           "}\n\n";
     }
 
     for (ReferencedSymbols::const_iterator varying = mReferencedVaryings.begin(); varying != mReferencedVaryings.end(); varying++)
diff --git a/src/compiler/OutputHLSL.h b/src/compiler/OutputHLSL.h
index 20660ae..09c3856 100644
--- a/src/compiler/OutputHLSL.h
+++ b/src/compiler/OutputHLSL.h
@@ -175,6 +175,12 @@
     void declareUniform(const TType &type, const TString &name, int index);
 
     TString interfaceBlockUniformName(const TType &interfaceBlockType, const TType &uniformType);
+    TString decoratePrivate(const TString &privateText);
+    TString interfaceBlockStructName(const TType &interfaceBlockType);
+    TString interfaceBlockInstanceString(const TType& interfaceBlockType, unsigned int arrayIndex);
+    TString interfaceBlockMemberString(const TTypeList &typeList);
+    TString interfaceBlockStructString(const TType &interfaceBlockType);
+    TString interfaceBlockString(const TType &interfaceBlockType, unsigned int registerIndex, unsigned int arrayIndex);
     
     static GLenum glVariableType(const TType &type);
     static GLenum glVariablePrecision(const TType &type);
diff --git a/src/compiler/ParseHelper.cpp b/src/compiler/ParseHelper.cpp
index 455097b..9295e0b 100644
--- a/src/compiler/ParseHelper.cpp
+++ b/src/compiler/ParseHelper.cpp
@@ -1686,6 +1686,11 @@
                 error(location, "", "[", "array must be redeclared with a size before being indexed with a variable");
                 recover();
             }
+            else if (baseExpression->getBasicType() == EbtInterfaceBlock)
+            {
+                error(location, "", "[", "array indexes for interface blocks arrays must be constant integeral expressions");
+                recover();
+            }
 
             indexedExpression = intermediate.addIndex(EOpIndexIndirect, baseExpression, indexExpression, location);
         }
@@ -1700,7 +1705,9 @@
     {
         if (baseExpression->getType().getStruct())
         {
-            indexedExpression->setType(TType(baseExpression->getType().getStruct(), baseExpression->getType().getTypeName()));
+            TType copyOfType(baseExpression->getType().getStruct(), baseExpression->getType().getTypeName());
+            copyOfType.setBasicType(baseExpression->getType().getBasicType()); // necessary to preserve interface block basic type
+            indexedExpression->setType(copyOfType);
         }
         else
         {
diff --git a/src/compiler/VariableInfo.cpp b/src/compiler/VariableInfo.cpp
index eb6bea9..bf7a13e 100644
--- a/src/compiler/VariableInfo.cpp
+++ b/src/compiler/VariableInfo.cpp
@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2002-2010 The ANGLE Project Authors. All rights reserved.
+// Copyright (c) 2002-2013 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.
 //
@@ -208,7 +208,7 @@
                 const TIntermSymbol* variable = (*i)->getAsSymbolNode();
                 // The only case in which the sequence will not contain a
                 // TIntermSymbol node is initialization. It will contain a
-                // TInterBinary node in that case. Since attributes and unifroms
+                // TIntermBinary node in that case. Since attributes and uniforms
                 // cannot be initialized in a shader, we must have only
                 // TIntermSymbol nodes in the sequence.
                 ASSERT(variable != NULL);
diff --git a/src/libGLESv2/ProgramBinary.cpp b/src/libGLESv2/ProgramBinary.cpp
index ca92742..64efe44 100644
--- a/src/libGLESv2/ProgramBinary.cpp
+++ b/src/libGLESv2/ProgramBinary.cpp
@@ -1178,6 +1178,12 @@
         vertexHLSL += "(input." + decorateAttribute(attribute->name) + ");\n";
     }
 
+    if (vertexHLSL.find("dx_initConstantBuffers") != std::string::npos)
+    {
+        vertexHLSL += "\n"
+                      "    dx_initConstantBuffers();\n";
+    }
+
     if (shaderModel >= 4)
     {
         vertexHLSL += "\n"
@@ -1421,6 +1427,12 @@
         else UNREACHABLE();
     }
 
+    if (pixelHLSL.find("dx_initConstantBuffers") != std::string::npos)
+    {
+        pixelHLSL += "\n"
+                     "    dx_initConstantBuffers();\n";
+    }
+
     pixelHLSL += "\n"
                  "    gl_main();\n"
                  "\n"