Add support for passing nested structs in standard layout by value.

We add support for this by using global scratch values as storage for the structs in
uniform blocks. Any structs in std140 layouts that are referenced by value are initialized
in the shader scope, without any packing, so the type of the structs are equivalent with what
a GLSL program would expect.

TRAC #23327

Signed-off-by: Geoff Lang
Signed-off-by: Shannon Woods
Authored-by: Jamie Madill
diff --git a/src/compiler/FlagStd140Structs.cpp b/src/compiler/FlagStd140Structs.cpp
new file mode 100644
index 0000000..1aaf50b
--- /dev/null
+++ b/src/compiler/FlagStd140Structs.cpp
@@ -0,0 +1,77 @@
+//
+// Copyright (c) 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.
+//
+
+#include "compiler/FlagStd140Structs.h"
+
+namespace sh
+{
+
+bool FlagStd140Structs::visitBinary(Visit visit, TIntermBinary *binaryNode)
+{
+    if (binaryNode->getRight()->getBasicType() == EbtStruct)
+    {
+        switch (binaryNode->getOp())
+        {
+          case EOpIndexDirectInterfaceBlock:
+          case EOpIndexDirectStruct:
+            if (isInStd140InterfaceBlock(binaryNode->getLeft()))
+            {
+                mFlaggedNodes.push_back(binaryNode);
+            }
+            break;
+
+          default: break;
+        }
+        return false;
+    }
+
+    if (binaryNode->getOp() == EOpIndexDirectStruct)
+    {
+        return false;
+    }
+
+    return visit == PreVisit;
+}
+
+void FlagStd140Structs::visitSymbol(TIntermSymbol *symbol)
+{
+    if (isInStd140InterfaceBlock(symbol) && symbol->getBasicType() == EbtStruct)
+    {
+        mFlaggedNodes.push_back(symbol);
+    }
+}
+
+bool FlagStd140Structs::isInStd140InterfaceBlock(TIntermTyped *node) const
+{
+    TIntermBinary *binaryNode = node->getAsBinaryNode();
+
+    if (binaryNode)
+    {
+        return isInStd140InterfaceBlock(binaryNode->getLeft());
+    }
+
+    const TType &type = node->getType();
+
+    if (type.isInterfaceBlockMember() || type.getBasicType() == EbtInterfaceBlock)
+    {
+        // determine if we are in the standard layout
+        const TType &interfaceBlockType = (type.isInterfaceBlockMember() ? *type.getInterfaceBlockType() : type);
+        return (interfaceBlockType.getLayoutQualifier().blockStorage == EbsStd140);
+    }
+
+    return false;
+}
+
+std::vector<TIntermTyped *> FlagStd140ValueStructs(TIntermNode *node)
+{
+    FlagStd140Structs flaggingTraversal;
+
+    node->traverse(&flaggingTraversal);
+
+    return flaggingTraversal.getFlaggedNodes();
+}
+
+}
diff --git a/src/compiler/FlagStd140Structs.h b/src/compiler/FlagStd140Structs.h
new file mode 100644
index 0000000..29ae910
--- /dev/null
+++ b/src/compiler/FlagStd140Structs.h
@@ -0,0 +1,37 @@
+//
+// Copyright (c) 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.
+//
+
+#ifndef COMPILER_FLAGSTD140STRUCTS_H_
+#define COMPILER_FLAGSTD140STRUCTS_H_
+
+#include "compiler/intermediate.h"
+
+namespace sh
+{
+
+// This class finds references to nested structs of std140 blocks that access
+// the nested struct "by value", where the padding added in the translator
+// conflicts with the "natural" unpadded type.
+class FlagStd140Structs : public TIntermTraverser
+{
+  public:
+    const std::vector<TIntermTyped *> getFlaggedNodes() const { return mFlaggedNodes; }
+
+  protected:
+    virtual bool visitBinary(Visit visit, TIntermBinary *binaryNode);
+    virtual void visitSymbol(TIntermSymbol *symbol);
+
+  private:
+    bool isInStd140InterfaceBlock(TIntermTyped *node) const;
+
+    std::vector<TIntermTyped *> mFlaggedNodes;
+};
+
+std::vector<TIntermTyped *> FlagStd140ValueStructs(TIntermNode *node);
+
+}
+
+#endif // COMPILER_FLAGSTD140STRUCTS_H_
diff --git a/src/compiler/OutputHLSL.cpp b/src/compiler/OutputHLSL.cpp
index d93b68d..4e9bfab 100644
--- a/src/compiler/OutputHLSL.cpp
+++ b/src/compiler/OutputHLSL.cpp
@@ -14,6 +14,7 @@
 #include "compiler/SearchSymbol.h"
 #include "compiler/UnfoldShortCircuit.h"
 #include "compiler/HLSLLayoutEncoder.h"
+#include "compiler/FlagStd140Structs.h"
 
 #include <algorithm>
 #include <cfloat>
@@ -174,6 +175,8 @@
 void OutputHLSL::output()
 {
     mContainsLoopDiscontinuity = mContext.shaderType == SH_FRAGMENT_SHADER && containsLoopDiscontinuity(mContext.treeRoot);
+    const std::vector<TIntermTyped*> &flaggedStructs = FlagStd140ValueStructs(mContext.treeRoot);
+    makeFlaggedStructMaps(flaggedStructs);
 
     mContext.treeRoot->traverse(this);   // Output the body first to determine what has to go in the header
     header();
@@ -182,6 +185,28 @@
     mContext.infoSink().obj << mBody.c_str();
 }
 
+void OutputHLSL::makeFlaggedStructMaps(const std::vector<TIntermTyped *> &flaggedStructs)
+{
+    for (unsigned int structIndex = 0; structIndex < flaggedStructs.size(); structIndex++)
+    {
+        TIntermTyped *flaggedNode = flaggedStructs[structIndex];
+
+        // This will mark the necessary block elements as referenced
+        flaggedNode->traverse(this);
+        TString structName(mBody.c_str());
+        mBody.erase();
+
+        mFlaggedStructOriginalNames[flaggedNode] = structName;
+
+        for (size_t pos = structName.find('.'); pos != std::string::npos; pos = structName.find('.'))
+        {
+            structName.erase(pos, 1);
+        }
+
+        mFlaggedStructMappedNames[flaggedNode] = "map" + structName;
+    }
+}
+
 TInfoSinkBase &OutputHLSL::getBodyStream()
 {
     return mBody;
@@ -476,6 +501,46 @@
     }
 }
 
+TString OutputHLSL::structInitializerString(int indent, const TTypeList &structMembers, const TString &structName)
+{
+    TString init;
+
+    TString preIndentString;
+    TString fullIndentString;
+
+    for (int spaces = 0; spaces < (indent * 4); spaces++)
+    {
+        preIndentString += ' ';
+    }
+
+    for (int spaces = 0; spaces < ((indent+1) * 4); spaces++)
+    {
+        fullIndentString += ' ';
+    }
+
+    init += preIndentString + "{\n";
+
+    for (unsigned int memberIndex = 0; memberIndex < structMembers.size(); memberIndex++)
+    {
+        const TType &memberType = *structMembers[memberIndex].type;
+        const TString &fieldName = decorate(memberType.getFieldName());
+
+        if (memberType.getBasicType() == EbtStruct)
+        {
+            const TTypeList &nestedStructMembers = *memberType.getStruct();
+            init += structInitializerString(indent + 1, nestedStructMembers, structName + "." + fieldName);
+        }
+        else
+        {
+            init += fullIndentString + structName + "." + fieldName + ",\n";
+        }
+    }
+
+    init += preIndentString + "}" + (indent == 0 ? ";" : ",") + "\n";
+
+    return init;
+}
+
 void OutputHLSL::header()
 {
     TInfoSinkBase &out = mHeader;
@@ -484,6 +549,7 @@
     TString interfaceBlocks;
     TString varyings;
     TString attributes;
+    TString flaggedStructs;
 
     for (ReferencedSymbols::const_iterator uniform = mReferencedUniforms.begin(); uniform != mReferencedUniforms.end(); uniform++)
     {
@@ -547,6 +613,19 @@
         }
     }
 
+    for (auto flaggedStructIt = mFlaggedStructMappedNames.begin(); flaggedStructIt != mFlaggedStructMappedNames.end(); flaggedStructIt++)
+    {
+        TIntermTyped *structNode = flaggedStructIt->first;
+        const TString &mappedName = flaggedStructIt->second;
+        const TType &structType = structNode->getType();
+        const TTypeList &structMembers = *structType.getStruct();
+        const TString &originalName = mFlaggedStructOriginalNames[structNode];
+
+        flaggedStructs += "static " + decorate(structType.getTypeName()) + " " + mappedName + " =\n";
+        flaggedStructs += structInitializerString(0, structMembers, originalName);
+        flaggedStructs += "\n";
+    }
+
     for (ReferencedSymbols::const_iterator varying = mReferencedVaryings.begin(); varying != mReferencedVaryings.end(); varying++)
     {
         const TType &type = varying->second->getType();
@@ -706,6 +785,14 @@
         {
             out << interfaceBlocks;
             out << "\n";
+
+            if (!flaggedStructs.empty())
+            {
+                out << "// Std140 Structures accessed by value\n";
+                out << "\n";
+                out << flaggedStructs;
+                out << "\n";
+            }
         }
 
         if (usingMRTExtension && mNumRenderTargets > 1)
@@ -786,6 +873,14 @@
         {
             out << interfaceBlocks;
             out << "\n";
+
+            if (!flaggedStructs.empty())
+            {
+                out << "// Std140 Structures accessed by value\n";
+                out << "\n";
+                out << flaggedStructs;
+                out << "\n";
+            }
         }
     }
 
@@ -1337,6 +1432,13 @@
 {
     TInfoSinkBase &out = mBody;
 
+    // Handle accessing std140 structs by value
+    if (mFlaggedStructMappedNames.count(node) > 0)
+    {
+        out << mFlaggedStructMappedNames[node];
+        return;
+    }
+
     TString name = node->getSymbol();
 
     if (name == "gl_DepthRange")
@@ -1424,6 +1526,13 @@
 {
     TInfoSinkBase &out = mBody;
 
+    // Handle accessing std140 structs by value
+    if (mFlaggedStructMappedNames.count(node) > 0)
+    {
+        out << mFlaggedStructMappedNames[node];
+        return false;
+    }
+
     switch (node->getOp())
     {
       case EOpAssign:                  outputTriplet(visit, "(", " = ", ")");           break;
diff --git a/src/compiler/OutputHLSL.h b/src/compiler/OutputHLSL.h
index b097410..d578d93 100644
--- a/src/compiler/OutputHLSL.h
+++ b/src/compiler/OutputHLSL.h
@@ -199,6 +199,7 @@
     TString interfaceBlockString(const TType &interfaceBlockType, unsigned int registerIndex, unsigned int arrayIndex);
     TString std140PrePaddingString(const TType &type, int *elementIndex);
     TString std140PostPaddingString(const TType &type, bool useHLSLRowMajorPacking);
+    TString structInitializerString(int indent, const TTypeList &structMembers, const TString &structName);
     
     static GLenum glVariableType(const TType &type);
     static GLenum glVariablePrecision(const TType &type);
@@ -211,6 +212,10 @@
     ActiveShaderVariables mActiveOutputVariables;
     ActiveShaderVariables mActiveAttributes;
     std::map<TString, int> mStd140StructElementIndexes;
+    std::map<TIntermTyped*, TString> mFlaggedStructMappedNames;
+    std::map<TIntermTyped*, TString> mFlaggedStructOriginalNames;
+
+    void makeFlaggedStructMaps(const std::vector<TIntermTyped *> &flaggedStructs);
 };
 }
 
diff --git a/src/compiler/translator_hlsl.vcxproj b/src/compiler/translator_hlsl.vcxproj
index 1c4127d..c96aa99 100644
--- a/src/compiler/translator_hlsl.vcxproj
+++ b/src/compiler/translator_hlsl.vcxproj
@@ -143,6 +143,7 @@
   </ItemDefinitionGroup>

   <ItemGroup>

     <ClCompile Include="CodeGenHLSL.cpp" />

+    <ClCompile Include="FlagStd140Structs.cpp" />

     <ClCompile Include="DetectDiscontinuity.cpp" />

     <ClCompile Include="HLSLLayoutEncoder.cpp" />

     <ClCompile Include="OutputHLSL.cpp" />

@@ -152,6 +153,7 @@
     <ClCompile Include="Uniform.cpp" />

   </ItemGroup>

   <ItemGroup>

+    <ClInclude Include="FlagStd140Structs.h" />

     <ClInclude Include="DetectDiscontinuity.h" />

     <ClInclude Include="HLSLLayoutEncoder.h" />

     <ClInclude Include="OutputHLSL.h" />

diff --git a/src/compiler/translator_hlsl.vcxproj.filters b/src/compiler/translator_hlsl.vcxproj.filters
index 075319c..7a83a3d 100644
--- a/src/compiler/translator_hlsl.vcxproj.filters
+++ b/src/compiler/translator_hlsl.vcxproj.filters
@@ -35,6 +35,9 @@
     <ClCompile Include="HLSLLayoutEncoder.cpp">

       <Filter>Source Files</Filter>

     </ClCompile>

+    <ClCompile Include="FlagStd140Structs.cpp">

+      <Filter>Source Files</Filter>

+    </ClCompile>

   </ItemGroup>

   <ItemGroup>

     <ClInclude Include="DetectDiscontinuity.h">

@@ -58,5 +61,8 @@
     <ClInclude Include="HLSLLayoutEncoder.h">

       <Filter>Header Files</Filter>

     </ClInclude>

+    <ClInclude Include="FlagStd140Structs.h">

+      <Filter>Header Files</Filter>

+    </ClInclude>

   </ItemGroup>

 </Project>
\ No newline at end of file