Reland "Add workaround for unused std140 and shared uniform blocks on MacOS"

On some Mac drivers with shader version 4.1, they will
treat unused std140 and shared uniform blocks' members as inactive. However,
WebGL2.0 based on OpenGL ES3.0.4 requires all members of a named uniform block
declared with a shared or std140 layout qualifier to be considered active.
The uniform block itself is also considered active.

This workaround is to reference all members of unused std140 and shared uniform blocks
at the beginning of the vertex/fragment shader's main().

BUG=chromium:618464
TEST=UniformBufferTest.ActiveUniformBlockNumber

Change-Id: I18da4e2b61b0170068bf5ea38ce54667b0737780
Reviewed-on: https://chromium-review.googlesource.com/395648
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
diff --git a/src/compiler/translator/UseInterfaceBlockFields.cpp b/src/compiler/translator/UseInterfaceBlockFields.cpp
new file mode 100644
index 0000000..859507f
--- /dev/null
+++ b/src/compiler/translator/UseInterfaceBlockFields.cpp
@@ -0,0 +1,165 @@
+//
+// Copyright 2016 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.
+//
+
+// UseInterfaceBlockFields.cpp: insert statements to reference all members in InterfaceBlock list at
+// the beginning of main. This is to work around a Mac driver that treats unused standard/shared
+// uniform blocks as inactive.
+
+#include "compiler/translator/UseInterfaceBlockFields.h"
+
+#include "compiler/translator/IntermNode.h"
+#include "compiler/translator/util.h"
+
+namespace sh
+{
+
+namespace
+{
+
+class UseUniformBlockMembers : public TIntermTraverser
+{
+  public:
+    UseUniformBlockMembers(const InterfaceBlockList &blocks)
+        : TIntermTraverser(true, false, false), mBlocks(blocks), mCodeInserted(false)
+    {
+    }
+
+  protected:
+    bool visitAggregate(Visit visit, TIntermAggregate *node) override;
+
+  private:
+    void insertUseCode(TIntermSequence *sequence);
+    void AddFieldUseStatements(const ShaderVariable &var, TIntermSequence *sequence);
+
+    const InterfaceBlockList &mBlocks;
+    bool mCodeInserted;
+};
+
+bool UseUniformBlockMembers::visitAggregate(Visit visit, TIntermAggregate *node)
+{
+    bool visitChildren = !mCodeInserted;
+    switch (node->getOp())
+    {
+        case EOpFunction:
+        {
+            ASSERT(visit == PreVisit);
+            if (node->getFunctionSymbolInfo()->isMain())
+            {
+                TIntermSequence *sequence = node->getSequence();
+                ASSERT(sequence->size() == 2);
+                TIntermBlock *body = (*sequence)[1]->getAsBlock();
+                ASSERT(body);
+                insertUseCode(body->getSequence());
+                mCodeInserted = true;
+                visitChildren = false;
+            }
+            break;
+        }
+        default:
+            visitChildren = false;
+            break;
+    }
+    return visitChildren;
+}
+
+void UseUniformBlockMembers::AddFieldUseStatements(const ShaderVariable &var,
+                                                   TIntermSequence *sequence)
+{
+    TString name = TString(var.name.c_str());
+    TType type   = GetShaderVariableType(var);
+
+    if (var.isArray())
+    {
+        size_t pos = name.find_last_of('[');
+        if (pos != TString::npos)
+        {
+            name = name.substr(0, pos);
+        }
+        TType elementType = type;
+        elementType.clearArrayness();
+
+        TIntermSymbol *arraySymbol = new TIntermSymbol(0, name, type);
+        for (unsigned int i = 0; i < var.arraySize; ++i)
+        {
+            TIntermBinary *element =
+                new TIntermBinary(EOpIndexDirect, arraySymbol, TIntermTyped::CreateIndexNode(i));
+
+            sequence->insert(sequence->begin(), element);
+        }
+    }
+    else if (var.isStruct())
+    {
+        TIntermSymbol *structSymbol = new TIntermSymbol(0, name, type);
+        for (unsigned int i = 0; i < var.fields.size(); ++i)
+        {
+            TIntermBinary *element = new TIntermBinary(EOpIndexDirectStruct, structSymbol,
+                                                       TIntermTyped::CreateIndexNode(i));
+
+            sequence->insert(sequence->begin(), element);
+        }
+    }
+    else
+    {
+        TIntermSymbol *symbol = new TIntermSymbol(0, name, type);
+
+        sequence->insert(sequence->begin(), symbol);
+    }
+}
+
+void UseUniformBlockMembers::insertUseCode(TIntermSequence *sequence)
+{
+    for (const auto &block : mBlocks)
+    {
+        if (block.instanceName.empty())
+        {
+            for (const auto &var : block.fields)
+            {
+                AddFieldUseStatements(var, sequence);
+            }
+        }
+        else if (block.arraySize > 0)
+        {
+            TType type                 = GetInterfaceBlockType(block);
+            TString name               = TString(block.instanceName.c_str());
+            TIntermSymbol *arraySymbol = new TIntermSymbol(0, name, type);
+            for (unsigned int i = 0; i < block.arraySize; ++i)
+            {
+                TIntermBinary *instanceSymbol = new TIntermBinary(EOpIndexDirect, arraySymbol,
+                                                                  TIntermTyped::CreateIndexNode(i));
+                for (unsigned int j = 0; j < block.fields.size(); ++j)
+                {
+                    TIntermBinary *element =
+                        new TIntermBinary(EOpIndexDirectInterfaceBlock, instanceSymbol,
+                                          TIntermTyped::CreateIndexNode(j));
+                    sequence->insert(sequence->begin(), element);
+                }
+            }
+        }
+        else
+        {
+            TType type                 = GetInterfaceBlockType(block);
+            TString name               = TString(block.instanceName.c_str());
+            TIntermSymbol *blockSymbol = new TIntermSymbol(0, name, type);
+            for (unsigned int i = 0; i < block.fields.size(); ++i)
+            {
+                TIntermBinary *element = new TIntermBinary(
+                    EOpIndexDirectInterfaceBlock, blockSymbol, TIntermTyped::CreateIndexNode(i));
+
+                sequence->insert(sequence->begin(), element);
+            }
+        }
+    }
+}
+
+}  // namespace anonymous
+
+void UseInterfaceBlockFields(TIntermNode *root, const InterfaceBlockList &blocks)
+{
+    UseUniformBlockMembers useUniformBlock(blocks);
+    root->traverse(&useUniformBlock);
+}
+
+}  // namespace sh