Add support for std140 uniform block layout for basic types.

This support does not include structures.

TRAC #23083

Signed-off-by: Nicolas Capens
Signed-off-by: Shannon Woods
Authored-by: Jamie Madill
diff --git a/src/compiler/OutputHLSL.cpp b/src/compiler/OutputHLSL.cpp
index 450ce83..29b4559 100644
--- a/src/compiler/OutputHLSL.cpp
+++ b/src/compiler/OutputHLSL.cpp
@@ -124,6 +124,7 @@
 
     mSamplerRegister = 0;
     mInterfaceBlockRegister = 2; // Reserve registers for the default uniform block and driver constants
+    mPaddingCounter = 0;
 }
 
 OutputHLSL::~OutputHLSL()
@@ -235,17 +236,113 @@
     }
 }
 
-TString OutputHLSL::interfaceBlockMemberString(const TTypeList &typeList)
+TString OutputHLSL::std140PrePaddingString(const TType &type, int *elementIndex)
+{
+    if (type.getBasicType() == EbtStruct || type.isMatrix() || type.isArray())
+    {
+        // no padding needed, HLSL will align the field to a new register
+        *elementIndex = 0;
+        return "";
+    }
+
+    const GLenum glType = glVariableType(type);
+    const int numComponents = gl::UniformComponentCount(glType);
+
+    if (numComponents >= 4)
+    {
+        // no padding needed, HLSL will align the field to a new register
+        *elementIndex = 0;
+        return "";
+    }
+
+    if (*elementIndex + numComponents > 4)
+    {
+        // no padding needed, HLSL will align the field to a new register
+        *elementIndex = numComponents;
+        return "";
+    }
+
+    TString padding;
+
+    const int alignment = numComponents == 3 ? 4 : numComponents;
+    const int paddingOffset = (*elementIndex % alignment);
+
+    if (paddingOffset != 0)
+    {
+        // padding is neccessary
+        for (int paddingIndex = paddingOffset; paddingIndex < alignment; paddingIndex++)
+        {
+            padding += "    float pad_" + str(mPaddingCounter++) + ";\n";
+        }
+
+        *elementIndex += (alignment - paddingOffset);
+    }
+
+    *elementIndex += numComponents;
+    *elementIndex %= 4;
+
+    return padding;
+}
+
+TString OutputHLSL::std140PostPaddingString(const TType &type)
+{
+    if (!type.isMatrix() && !type.isArray())
+    {
+        return "";
+    }
+
+    const GLenum glType = glVariableType(type);
+    int numComponents = 0;
+
+    if (type.isMatrix())
+    {
+        const bool isRowMajorMatrix = (type.getLayoutQualifier().matrixPacking == EmpRowMajor);
+        numComponents = gl::MatrixComponentCount(glType, isRowMajorMatrix);
+    }
+    else
+    {
+        numComponents = gl::UniformComponentCount(glType);
+    }
+
+    TString padding;
+    for (int paddingOffset = numComponents; paddingOffset < 4; paddingOffset++)
+    {
+        padding += "    float pad_" + str(mPaddingCounter++) + ";\n";
+    }
+    return padding;
+}
+
+TString OutputHLSL::interfaceBlockMemberString(const TTypeList &typeList, TLayoutBlockStorage blockStorage)
 {
     TString hlsl;
 
-    // TODO: padding for standard layout
+    int elementIndex = 0;
 
     for (unsigned int typeIndex = 0; typeIndex < typeList.size(); typeIndex++)
     {
         const TType &memberType = *typeList[typeIndex].type;
+
+        if (blockStorage == EbsStd140)
+        {
+            if (memberType.getBasicType() == EbtStruct)
+            {
+                UNIMPLEMENTED();
+            }
+            else
+            {
+                // 2 and 3 component vector types in some cases need pre-padding
+                hlsl += std140PrePaddingString(memberType, &elementIndex);
+            }
+        }
+
         hlsl += "    " + interfaceBlockMemberTypeString(memberType) +
                 " " + decorate(memberType.getFieldName()) + arrayString(memberType) + ";\n";
+
+        // must pad out after matrices and arrays, where HLSL usually allows itself room to pack stuff
+        if (blockStorage == EbsStd140)
+        {
+            hlsl += std140PostPaddingString(memberType);
+        }
     }
 
     return hlsl;
@@ -254,10 +351,11 @@
 TString OutputHLSL::interfaceBlockStructString(const TType &interfaceBlockType)
 {
     const TTypeList &typeList = *interfaceBlockType.getStruct();
+    const TLayoutBlockStorage blockStorage = interfaceBlockType.getLayoutQualifier().blockStorage;
 
     return "struct " + interfaceBlockStructName(interfaceBlockType) + "\n"
            "{\n" +
-           interfaceBlockMemberString(typeList) +
+           interfaceBlockMemberString(typeList, blockStorage) +
            "};\n\n";
 }
 
@@ -277,7 +375,8 @@
     else
     {
         const TTypeList &typeList = *interfaceBlockType.getStruct();
-        hlsl += interfaceBlockMemberString(typeList);
+        const TLayoutBlockStorage blockStorage = interfaceBlockType.getLayoutQualifier().blockStorage;
+        hlsl += interfaceBlockMemberString(typeList, blockStorage);
     }
 
     hlsl += "};\n\n";
@@ -316,6 +415,17 @@
     }
 }
 
+BlockLayoutType convertBlockLayoutType(TLayoutBlockStorage blockStorage)
+{
+    switch (blockStorage)
+    {
+      case EbsPacked: return BLOCKLAYOUT_PACKED;
+      case EbsShared: return BLOCKLAYOUT_SHARED;
+      case EbsStd140: return BLOCKLAYOUT_STANDARD;
+      default: UNREACHABLE(); return BLOCKLAYOUT_SHARED;
+    }
+}
+
 void OutputHLSL::header()
 {
     ShShaderType shaderType = mContext.shaderType;
@@ -366,8 +476,8 @@
 
         mInterfaceBlockRegister += std::max(1u, interfaceBlock.arraySize);
 
-        // TODO: handle other block layouts
-        setBlockLayout(&interfaceBlock, BLOCKLAYOUT_SHARED);
+        BlockLayoutType blockLayoutType = convertBlockLayoutType(interfaceBlockType.getLayoutQualifier().blockStorage);
+        setBlockLayout(&interfaceBlock, blockLayoutType);
         mActiveInterfaceBlocks.push_back(interfaceBlock);
 
         if (interfaceBlockType.hasInstanceName())
diff --git a/src/compiler/OutputHLSL.h b/src/compiler/OutputHLSL.h
index 6fa8bca..2b7bce3 100644
--- a/src/compiler/OutputHLSL.h
+++ b/src/compiler/OutputHLSL.h
@@ -173,6 +173,7 @@
     int mUniformRegister;
     int mInterfaceBlockRegister;
     int mSamplerRegister;
+    int mPaddingCounter;
 
     TString registerString(TIntermSymbol *operand);
     int samplerRegister(TIntermSymbol *sampler);
@@ -185,9 +186,11 @@
     TString interfaceBlockStructName(const TType &interfaceBlockType);
     TString interfaceBlockInstanceString(const TType& interfaceBlockType, unsigned int arrayIndex);
     TString interfaceBlockMemberTypeString(const TType &memberType);
-    TString interfaceBlockMemberString(const TTypeList &typeList);
+    TString interfaceBlockMemberString(const TTypeList &typeList, TLayoutBlockStorage blockStorage);
     TString interfaceBlockStructString(const TType &interfaceBlockType);
     TString interfaceBlockString(const TType &interfaceBlockType, unsigned int registerIndex, unsigned int arrayIndex);
+    TString std140PrePaddingString(const TType &type, int *elementIndex);
+    TString std140PostPaddingString(const TType &type);
     
     static GLenum glVariableType(const TType &type);
     static GLenum glVariablePrecision(const TType &type);