ES31: Add unsized array length support in SSBO

Bug: angleproject:1951

Change-Id: I10c798c62a741b156f5b614e0df0795c0e845108
Reviewed-on: https://chromium-review.googlesource.com/c/1365154
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Geoff Lang <geofflang@chromium.org>
Commit-Queue: Jiajia Qin <jiajia.qin@intel.com>
diff --git a/src/compiler/translator/OutputHLSL.cpp b/src/compiler/translator/OutputHLSL.cpp
index 07499a5..aa81c11 100644
--- a/src/compiler/translator/OutputHLSL.cpp
+++ b/src/compiler/translator/OutputHLSL.cpp
@@ -1837,6 +1837,13 @@
             // tested in GLSLTest and results are consistent with GL.
             outputTriplet(out, visit, "firstbithigh(", "", ")");
             break;
+        case EOpArrayLength:
+        {
+            TIntermTyped *operand = node->getOperand();
+            ASSERT(IsInShaderStorageBlock(operand));
+            mSSBOOutputHLSL->outputLengthFunctionCall(operand);
+            return false;
+        }
         default:
             UNREACHABLE();
     }
diff --git a/src/compiler/translator/ShaderStorageBlockFunctionHLSL.cpp b/src/compiler/translator/ShaderStorageBlockFunctionHLSL.cpp
index de59747..090292e 100644
--- a/src/compiler/translator/ShaderStorageBlockFunctionHLSL.cpp
+++ b/src/compiler/translator/ShaderStorageBlockFunctionHLSL.cpp
@@ -189,6 +189,15 @@
     }
 }
 
+// static
+void ShaderStorageBlockFunctionHLSL::OutputSSBOLengthFunctionBody(TInfoSinkBase &out,
+                                                                  int unsizedArrayStride)
+{
+    out << "    uint dim = 0;\n";
+    out << "    buffer.GetDimensions(dim);\n";
+    out << "    return int((dim - loc)/uint(" << unsizedArrayStride << "));\n";
+}
+
 bool ShaderStorageBlockFunctionHLSL::ShaderStorageBlockFunction::operator<(
     const ShaderStorageBlockFunction &rhs) const
 {
@@ -201,11 +210,30 @@
     TLayoutBlockStorage storage,
     bool rowMajor,
     int matrixStride,
+    int unsizedArrayStride,
     TIntermSwizzle *swizzleNode)
 {
     ShaderStorageBlockFunction ssboFunction;
-    ssboFunction.typeString = TypeString(type);
     ssboFunction.method     = method;
+    switch (method)
+    {
+        case SSBOMethod::LOAD:
+            ssboFunction.functionName = "_Load_";
+            break;
+        case SSBOMethod::STORE:
+            ssboFunction.functionName = "_Store_";
+            break;
+        case SSBOMethod::LENGTH:
+            ssboFunction.unsizedArrayStride = unsizedArrayStride;
+            ssboFunction.functionName       = "_Length_" + str(unsizedArrayStride);
+            mRegisteredShaderStorageBlockFunctions.insert(ssboFunction);
+            return ssboFunction.functionName;
+        default:
+            UNREACHABLE();
+    }
+
+    ssboFunction.typeString = TypeString(type);
+    ssboFunction.functionName += ssboFunction.typeString;
     ssboFunction.type       = type;
     if (swizzleNode != nullptr)
     {
@@ -230,20 +258,7 @@
     }
     ssboFunction.rowMajor     = rowMajor;
     ssboFunction.matrixStride = matrixStride;
-    ssboFunction.functionName =
-        TString(getBlockStorageString(storage)) + "_" + ssboFunction.typeString;
-
-    switch (method)
-    {
-        case SSBOMethod::LOAD:
-            ssboFunction.functionName += "_Load";
-            break;
-        case SSBOMethod::STORE:
-            ssboFunction.functionName += "_Store";
-            break;
-        default:
-            UNREACHABLE();
-    }
+    ssboFunction.functionName += "_" + TString(getBlockStorageString(storage));
 
     if (rowMajor)
     {
@@ -291,7 +306,7 @@
             out << "{\n";
             OutputSSBOLoadFunctionBody(out, ssboFunction);
         }
-        else
+        else if (ssboFunction.method == SSBOMethod::STORE)
         {
             // Function header
             out << "void " << ssboFunction.functionName << "(RWByteAddressBuffer buffer, uint loc, "
@@ -299,6 +314,14 @@
             out << "{\n";
             OutputSSBOStoreFunctionBody(out, ssboFunction);
         }
+        else
+        {
+            ASSERT(ssboFunction.method == SSBOMethod::LENGTH);
+            out << "int " << ssboFunction.functionName
+                << "(RWByteAddressBuffer buffer, uint loc)\n";
+            out << "{\n";
+            OutputSSBOLengthFunctionBody(out, ssboFunction.unsizedArrayStride);
+        }
 
         out << "}\n"
                "\n";
diff --git a/src/compiler/translator/ShaderStorageBlockFunctionHLSL.h b/src/compiler/translator/ShaderStorageBlockFunctionHLSL.h
index d9e603d..7dba673 100644
--- a/src/compiler/translator/ShaderStorageBlockFunctionHLSL.h
+++ b/src/compiler/translator/ShaderStorageBlockFunctionHLSL.h
@@ -38,7 +38,8 @@
 enum class SSBOMethod
 {
     LOAD,
-    STORE
+    STORE,
+    LENGTH
 };
 
 class ShaderStorageBlockFunctionHLSL final : angle::NonCopyable
@@ -49,6 +50,7 @@
                                                TLayoutBlockStorage storage,
                                                bool rowMajor,
                                                int matrixStride,
+                                               int unsizedArrayStride,
                                                TIntermSwizzle *node);
 
     void shaderStorageBlockFunctionHeader(TInfoSinkBase &out);
@@ -63,14 +65,16 @@
         TType type;
         bool rowMajor;
         int matrixStride;
+        int unsizedArrayStride;
         TVector<int> swizzleOffsets;
         bool isDefaultSwizzle;
     };
 
     static void OutputSSBOLoadFunctionBody(TInfoSinkBase &out,
-                                           const ShaderStorageBlockFunction &imageFunction);
+                                           const ShaderStorageBlockFunction &ssboFunction);
     static void OutputSSBOStoreFunctionBody(TInfoSinkBase &out,
-                                            const ShaderStorageBlockFunction &imageFunction);
+                                            const ShaderStorageBlockFunction &ssboFunction);
+    static void OutputSSBOLengthFunctionBody(TInfoSinkBase &out, int unsizedArrayStride);
     using ShaderStorageBlockFunctionSet = std::set<ShaderStorageBlockFunction>;
     ShaderStorageBlockFunctionSet mRegisteredShaderStorageBlockFunctions;
 };
diff --git a/src/compiler/translator/ShaderStorageBlockOutputHLSL.cpp b/src/compiler/translator/ShaderStorageBlockOutputHLSL.cpp
index 323ece3..9a9bd62 100644
--- a/src/compiler/translator/ShaderStorageBlockOutputHLSL.cpp
+++ b/src/compiler/translator/ShaderStorageBlockOutputHLSL.cpp
@@ -87,8 +87,10 @@
 }
 
 // It's possible that the current type has lost the original layout information. So we should pass
-// the right layout information to GetMatrixStride.
-unsigned int GetMatrixStride(const TType &type, TLayoutBlockStorage storage, bool rowMajor)
+// the right layout information to GetBlockMemberInfoByType.
+const BlockMemberInfo GetBlockMemberInfoByType(const TType &type,
+                                               TLayoutBlockStorage storage,
+                                               bool rowMajor)
 {
     sh::Std140BlockEncoder std140Encoder;
     sh::Std430BlockEncoder std430Encoder;
@@ -114,9 +116,7 @@
     {
         arraySizes.assign(typeArraySizes->begin(), typeArraySizes->end());
     }
-    const BlockMemberInfo &memberInfo =
-        encoder->encodeType(GLVariableType(type), arraySizes, rowMajor);
-    return memberInfo.matrixStride;
+    return encoder->encodeType(GLVariableType(type), arraySizes, rowMajor);
 }
 
 const TField *GetFieldMemberInShaderStorageBlock(const TInterfaceBlock *interfaceBlock,
@@ -299,7 +299,7 @@
     : TIntermTraverser(true, true, true, symbolTable),
       mMatrixStride(0),
       mRowMajor(false),
-      mIsLoadFunctionCall(false),
+      mLocationAsTheLastArgument(false),
       mOutputHLSL(outputHLSL),
       mResourcesHLSL(resourcesHLSL)
 {
@@ -313,16 +313,22 @@
 
 void ShaderStorageBlockOutputHLSL::outputStoreFunctionCallPrefix(TIntermTyped *node)
 {
-    mIsLoadFunctionCall = false;
+    mLocationAsTheLastArgument = false;
     traverseSSBOAccess(node, SSBOMethod::STORE);
 }
 
 void ShaderStorageBlockOutputHLSL::outputLoadFunctionCall(TIntermTyped *node)
 {
-    mIsLoadFunctionCall = true;
+    mLocationAsTheLastArgument = true;
     traverseSSBOAccess(node, SSBOMethod::LOAD);
 }
 
+void ShaderStorageBlockOutputHLSL::outputLengthFunctionCall(TIntermTyped *node)
+{
+    mLocationAsTheLastArgument = true;
+    traverseSSBOAccess(node, SSBOMethod::LENGTH);
+}
+
 // Note that we must calculate the matrix stride here instead of ShaderStorageBlockFunctionHLSL.
 // It's because that if the current node's type is a vector which comes from a matrix, we will
 // lose the matrix type info once we enter ShaderStorageBlockFunctionHLSL.
@@ -332,7 +338,7 @@
 {
     if (node->getType().isMatrix())
     {
-        mMatrixStride = GetMatrixStride(node->getType(), storage, rowMajor);
+        mMatrixStride = GetBlockMemberInfoByType(node->getType(), storage, rowMajor).matrixStride;
         mRowMajor     = rowMajor;
         return;
     }
@@ -366,10 +372,17 @@
     TLayoutBlockStorage storage;
     bool rowMajor;
     GetBlockLayoutInfo(node, false, &storage, &rowMajor);
+    int unsizedArrayStride = 0;
+    if (node->getType().isUnsizedArray())
+    {
+        unsizedArrayStride =
+            GetBlockMemberInfoByType(node->getType(), storage, rowMajor).arrayStride;
+    }
     setMatrixStride(node, storage, rowMajor);
 
     const TString &functionName = mSSBOFunctionHLSL->registerShaderStorageBlockFunction(
-        node->getType(), method, storage, mRowMajor, mMatrixStride, node->getAsSwizzleNode());
+        node->getType(), method, storage, mRowMajor, mMatrixStride, unsizedArrayStride,
+        node->getAsSwizzleNode());
     TInfoSinkBase &out = mOutputHLSL->getInfoSink();
     out << functionName;
     out << "(";
@@ -488,7 +501,7 @@
         TInfoSinkBase &out = mOutputHLSL->getInfoSink();
         // TODO(jiajia.qin@intel.com): add swizzle process if the swizzle node is not the last node
         // of ssbo access chain. Such as, data.xy[0]
-        if (mIsLoadFunctionCall && isEndOfSSBOAccessChain())
+        if (mLocationAsTheLastArgument && isEndOfSSBOAccessChain())
         {
             out << ")";
         }
@@ -657,7 +670,7 @@
         {
             out << ")";
         }
-        if (mIsLoadFunctionCall && isEndOfSSBOAccessChain())
+        if (mLocationAsTheLastArgument && isEndOfSSBOAccessChain())
         {
             out << ")";
         }
@@ -683,7 +696,7 @@
             out << " * (";
         }
     }
-    if (mIsLoadFunctionCall && isEndOfSSBOAccessChain())
+    if (mLocationAsTheLastArgument && isEndOfSSBOAccessChain())
     {
         out << ")";
     }
diff --git a/src/compiler/translator/ShaderStorageBlockOutputHLSL.h b/src/compiler/translator/ShaderStorageBlockOutputHLSL.h
index a44f173..527698f 100644
--- a/src/compiler/translator/ShaderStorageBlockOutputHLSL.h
+++ b/src/compiler/translator/ShaderStorageBlockOutputHLSL.h
@@ -47,8 +47,10 @@
     // calling this, ", <stored value>)" should be written to the output stream to complete the
     // function call.
     void outputStoreFunctionCallPrefix(TIntermTyped *node);
-    // This writes the funciton call to load a SSBO value to the output stream.
+    // This writes the function call to load a SSBO value to the output stream.
     void outputLoadFunctionCall(TIntermTyped *node);
+    // This writes the function call to get the lengh of unsized array member of SSBO.
+    void outputLengthFunctionCall(TIntermTyped *node);
 
     void writeShaderStorageBlocksHeader(TInfoSinkBase &out) const;
 
@@ -71,7 +73,7 @@
 
     int mMatrixStride;
     bool mRowMajor;
-    bool mIsLoadFunctionCall;
+    bool mLocationAsTheLastArgument;
     OutputHLSL *mOutputHLSL;
     ShaderStorageBlockFunctionHLSL *mSSBOFunctionHLSL;
     ResourcesHLSL *mResourcesHLSL;