ES31: Support shader storage block in D3D11 compiler - Part1
This patch is the first step to implement a basic skeleton to translate
shader storage block to HLSL RWByteAddressBuffer.
In GLSL each shader storage block is just one structured block and in API side
it corresponds to a buffer range where stores the whole structure.
RWStructuredBuffer is an array-like object and can have many structured
elements. The structured element doesn't support unsized array and also have
a small limitation on the element size. So we choose RWByteAddressBuffer as
the counterpart of shader storage block in HLSL.
Due to RWByteAddressBuffer does not support using an index to reference a
specific location, we must use Load and Store to process the read/write
operation of a buffer variable. Moreover, in the compiler tree, since we
can't use variable name to get the resource value in RWByteAddressBuffer,
we have to calculate the offset of buffer variable in a shader storage block,
then call the corresponding wrapper function to get the right value.
In this patch, we only process below situations:
assign_to_ssbo := ssbo_access_chain = expr_no_ssbo;
assign_from_ssbo := lvalue_no_ssbo = ssbo_access_chain;
The translation is like below:
// GLSL
#version 310 es
layout(local_size_x=8) in;
layout(std140, binding = 0) buffer blockA {
float f[8];
} instanceA;
layout(std140, binding = 1) buffer blockB {
float f[8];
};
void main()
{
float data = instanceA.f[gl_LocalInvocationIndex];
f[gl_LocalInvocationIndex] = data;
}
// HLSL
RWByteAddressBuffer _instanceA: register(u0);
RWByteAddressBuffer _blockB: register(u1);
float float_Load(RWByteAddressBuffer buffer, uint loc)
{
float result = asfloat(buffer.Load(loc));
return result;
}
void float_Store(RWByteAddressBuffer buffer, uint loc, float value)
{
buffer.Store(loc, asuint(value));
}
void gl_main()
{
float _data = float_Load(_instanceA, 0 + 16 * gl_LocalInvocationIndex);
float_Store(_blockB, 0 + 16 * gl_LocalInvocationIndex, _data);
}
We will do below things in the following patches:
1. Modify the intermediate tree to flatten all ssbo usages to:
assign_to_ssbo := ssbo_access_chain = expr_no_ssbo;
assign_from_ssbo := lvalue_no_ssbo = ssbo_access_chain;
e.g.
intanceA.a +=1;
->tmp = intanceA.a;
intanceA.a = tmp + 1;
while(++instanceA.a < 16) {
}
->
int PreIncrement(out int a)
{
a += 1;
return a;
}
tmp = instanceA.a;
while(PreIncrement(tmp) < 16) {
instanceA.a = tmp
}
2. Add offset calculation for structure and array of arrays.
TODOs have been marked in the corresponding places in this patch.
3. Improve helper functions so that they can process all possible types.
TODOs have been marked in the corresponding places in this patch.
4. Process the swizzle situation.
TODOs have been marked in the corresponding places in this patch.
A possible method is to extend current helper functions like below:
*_Load(RWByteAddressBuffer buffer, uint loc, bool isSwizzle, uint4 swizzleOffset)
Bug: angleproject:1951
Test: angle_end2end_tests
Change-Id: I68ae68d5bb77d0d5627c8272627a7f689b8dc38b
Reviewed-on: https://chromium-review.googlesource.com/848215
Reviewed-by: Olli Etuaho <oetuaho@nvidia.com>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Commit-Queue: Jiajia Qin <jiajia.qin@intel.com>
diff --git a/src/compiler.gni b/src/compiler.gni
index 985ec38..82acff0 100644
--- a/src/compiler.gni
+++ b/src/compiler.gni
@@ -229,6 +229,10 @@
"src/compiler/translator/OutputHLSL.h",
"src/compiler/translator/ResourcesHLSL.cpp",
"src/compiler/translator/ResourcesHLSL.h",
+ "src/compiler/translator/ShaderStorageBlockFunctionHLSL.cpp",
+ "src/compiler/translator/ShaderStorageBlockFunctionHLSL.h",
+ "src/compiler/translator/ShaderStorageBlockOutputHLSL.cpp",
+ "src/compiler/translator/ShaderStorageBlockOutputHLSL.h",
"src/compiler/translator/StructureHLSL.cpp",
"src/compiler/translator/StructureHLSL.h",
"src/compiler/translator/TextureFunctionHLSL.cpp",
diff --git a/src/compiler/translator/OutputHLSL.cpp b/src/compiler/translator/OutputHLSL.cpp
index 0f360b9..65925f7 100644
--- a/src/compiler/translator/OutputHLSL.cpp
+++ b/src/compiler/translator/OutputHLSL.cpp
@@ -59,22 +59,25 @@
variable->getQualifier() == EvqConst || variable->getQualifier() == EvqShared);
}
-bool IsInStd140InterfaceBlock(TIntermTyped *node)
+bool IsInStd140UniformBlock(TIntermTyped *node)
{
TIntermBinary *binaryNode = node->getAsBinaryNode();
if (binaryNode)
{
- return IsInStd140InterfaceBlock(binaryNode->getLeft());
+ return IsInStd140UniformBlock(binaryNode->getLeft());
}
const TType &type = node->getType();
- // determine if we are in the standard layout
- const TInterfaceBlock *interfaceBlock = type.getInterfaceBlock();
- if (interfaceBlock)
+ if (type.getQualifier() == EvqUniform)
{
- return (interfaceBlock->blockStorage() == EbsStd140);
+ // determine if we are in the standard layout
+ const TInterfaceBlock *interfaceBlock = type.getInterfaceBlock();
+ if (interfaceBlock)
+ {
+ return (interfaceBlock->blockStorage() == EbsStd140);
+ }
}
return false;
@@ -249,10 +252,13 @@
// Reserve registers for the default uniform block and driver constants
mResourcesHLSL->reserveUniformBlockRegisters(2);
+
+ mSSBOOutputHLSL = new ShaderStorageBlockOutputHLSL(this, symbolTable, mResourcesHLSL);
}
OutputHLSL::~OutputHLSL()
{
+ SafeDelete(mSSBOOutputHLSL);
SafeDelete(mStructureHLSL);
SafeDelete(mResourcesHLSL);
SafeDelete(mTextureFunctionHLSL);
@@ -384,9 +390,20 @@
{
const TInterfaceBlock *interfaceBlock =
mappedStruct.blockDeclarator->getType().getInterfaceBlock();
- if (mReferencedUniformBlocks.count(interfaceBlock->uniqueId().get()) == 0)
+ TQualifier qualifier = mappedStruct.blockDeclarator->getType().getQualifier();
+ switch (qualifier)
{
- continue;
+ case EvqUniform:
+ if (mReferencedUniformBlocks.count(interfaceBlock->uniqueId().get()) == 0)
+ {
+ continue;
+ }
+ break;
+ case EvqBuffer:
+ continue;
+ default:
+ UNREACHABLE();
+ return mappedStructs;
}
unsigned int instanceCount = 1u;
@@ -409,7 +426,7 @@
unsigned int instanceStringArrayIndex = GL_INVALID_INDEX;
if (isInstanceArray)
instanceStringArrayIndex = instanceArrayIndex;
- TString instanceString = mResourcesHLSL->UniformBlockInstanceString(
+ TString instanceString = mResourcesHLSL->InterfaceBlockInstanceString(
instanceName, instanceStringArrayIndex);
originalName += instanceString;
mappedName += instanceString;
@@ -473,6 +490,7 @@
mResourcesHLSL->uniformsHeader(out, mOutputType, mReferencedUniforms, mSymbolTable);
out << mResourcesHLSL->uniformBlocksHeader(mReferencedUniformBlocks);
+ mSSBOOutputHLSL->writeShaderStorageBlocksHeader(out);
if (!mEqualityFunctions.empty())
{
@@ -905,7 +923,7 @@
TInfoSinkBase &out = getInfoSink();
// Handle accessing std140 structs by value
- if (IsInStd140InterfaceBlock(node) && node->getBasicType() == EbtStruct)
+ if (IsInStd140UniformBlock(node) && node->getBasicType() == EbtStruct)
{
out << "map";
}
@@ -949,6 +967,10 @@
out << DecorateVariableIfNeeded(variable);
}
+ else if (qualifier == EvqBuffer)
+ {
+ UNREACHABLE();
+ }
else if (qualifier == EvqAttribute || qualifier == EvqVertexIn)
{
mReferencedAttributes[uniqueId.get()] = &variable;
@@ -1193,6 +1215,29 @@
out << ")";
return false;
}
+ else if (IsInShaderStorageBlock(node->getLeft()))
+ {
+ mSSBOOutputHLSL->outputStoreFunctionCallPrefix(node->getLeft());
+ out << ", ";
+ if (IsInShaderStorageBlock(node->getRight()))
+ {
+ mSSBOOutputHLSL->outputLoadFunctionCall(node->getRight());
+ }
+ else
+ {
+ node->getRight()->traverse(this);
+ }
+
+ out << ")";
+ return false;
+ }
+ else if (IsInShaderStorageBlock(node->getRight()))
+ {
+ node->getLeft()->traverse(this);
+ out << " = ";
+ mSSBOOutputHLSL->outputLoadFunctionCall(node->getRight());
+ return false;
+ }
outputAssign(visit, node->getType(), out);
break;
@@ -1224,6 +1269,11 @@
else if (visit == InVisit)
{
out << " = ";
+ if (IsInShaderStorageBlock(node->getRight()))
+ {
+ mSSBOOutputHLSL->outputLoadFunctionCall(node->getRight());
+ return false;
+ }
}
break;
case EOpAddAssign:
@@ -1303,13 +1353,15 @@
{
TIntermSymbol *instanceArraySymbol = node->getLeft()->getAsSymbolNode();
const TInterfaceBlock *interfaceBlock = leftType.getInterfaceBlock();
+
+ ASSERT(leftType.getQualifier() == EvqUniform);
if (mReferencedUniformBlocks.count(interfaceBlock->uniqueId().get()) == 0)
{
mReferencedUniformBlocks[interfaceBlock->uniqueId().get()] =
new TReferencedBlock(interfaceBlock, &instanceArraySymbol->variable());
}
const int arrayIndex = node->getRight()->getAsConstantUnion()->getIConst(0);
- out << mResourcesHLSL->UniformBlockInstanceString(
+ out << mResourcesHLSL->InterfaceBlockInstanceString(
instanceArraySymbol->getName(), arrayIndex);
return false;
}
@@ -1370,9 +1422,10 @@
break;
case EOpIndexDirectInterfaceBlock:
{
- bool structInStd140Block =
- node->getBasicType() == EbtStruct && IsInStd140InterfaceBlock(node->getLeft());
- if (visit == PreVisit && structInStd140Block)
+ ASSERT(!IsInShaderStorageBlock(node->getLeft()));
+ bool structInStd140UniformBlock =
+ node->getBasicType() == EbtStruct && IsInStd140UniformBlock(node->getLeft());
+ if (visit == PreVisit && structInStd140UniformBlock)
{
out << "map";
}
@@ -1382,7 +1435,7 @@
node->getLeft()->getType().getInterfaceBlock();
const TIntermConstantUnion *index = node->getRight()->getAsConstantUnion();
const TField *field = interfaceBlock->fields()[index->getIConst(0)];
- if (structInStd140Block)
+ if (structInStd140UniformBlock)
{
out << "_";
}
diff --git a/src/compiler/translator/OutputHLSL.h b/src/compiler/translator/OutputHLSL.h
index 0042406..7a2690d 100644
--- a/src/compiler/translator/OutputHLSL.h
+++ b/src/compiler/translator/OutputHLSL.h
@@ -16,6 +16,7 @@
#include "compiler/translator/Compiler.h"
#include "compiler/translator/FlagStd140Structs.h"
#include "compiler/translator/ImmutableString.h"
+#include "compiler/translator/ShaderStorageBlockOutputHLSL.h"
#include "compiler/translator/tree_util/IntermTraverse.h"
class BuiltInFunctionEmulator;
@@ -30,16 +31,6 @@
class TVariable;
class UnfoldShortCircuit;
-struct TReferencedBlock : angle::NonCopyable
-{
- POOL_ALLOCATOR_NEW_DELETE();
- TReferencedBlock(const TInterfaceBlock *block, const TVariable *instanceVariable);
- const TInterfaceBlock *block;
- const TVariable *instanceVariable; // May be nullptr if the block is not instanced.
-};
-
-// Maps from uniqueId to a variable.
-using ReferencedInterfaceBlocks = std::map<int, const TReferencedBlock *>;
using ReferencedVariables = std::map<int, const TVariable *>;
class OutputHLSL : public TIntermTraverser
@@ -73,6 +64,8 @@
}
protected:
+ friend class ShaderStorageBlockOutputHLSL;
+
void writeReferencedAttributes(TInfoSinkBase &out) const;
void writeReferencedVaryings(TInfoSinkBase &out) const;
void header(TInfoSinkBase &out,
@@ -263,6 +256,7 @@
TString generateStructMapping(const std::vector<MappedStruct> &std140Structs) const;
ImmutableString samplerNamePrefixFromStruct(TIntermTyped *node);
bool ancestorEvaluatesToSamplerInStruct();
+ ShaderStorageBlockOutputHLSL *mSSBOOutputHLSL;
};
}
diff --git a/src/compiler/translator/ResourcesHLSL.cpp b/src/compiler/translator/ResourcesHLSL.cpp
index dc7c548..4cb4943 100644
--- a/src/compiler/translator/ResourcesHLSL.cpp
+++ b/src/compiler/translator/ResourcesHLSL.cpp
@@ -102,7 +102,7 @@
: mUniformRegister(firstUniformRegister),
mUniformBlockRegister(0),
mTextureRegister(0),
- mRWTextureRegister(0),
+ mUAVRegister(0),
mSamplerCount(0),
mStructureHLSL(structureHLSL),
mOutputType(outputType),
@@ -148,7 +148,7 @@
}
else if (IsImage(type.getBasicType()))
{
- registerIndex = mRWTextureRegister;
+ registerIndex = mUAVRegister;
}
else
{
@@ -166,7 +166,7 @@
}
else if (IsImage(type.getBasicType()))
{
- mRWTextureRegister += registerCount;
+ mUAVRegister += registerCount;
}
else
{
@@ -553,6 +553,40 @@
return (interfaceBlocks.empty() ? "" : ("// Uniform Blocks\n\n" + interfaceBlocks));
}
+TString ResourcesHLSL::shaderStorageBlocksHeader(
+ const ReferencedInterfaceBlocks &referencedInterfaceBlocks)
+{
+ TString interfaceBlocks;
+
+ for (const auto &interfaceBlockReference : referencedInterfaceBlocks)
+ {
+ const TInterfaceBlock &interfaceBlock = *interfaceBlockReference.second->block;
+ const TVariable *instanceVariable = interfaceBlockReference.second->instanceVariable;
+
+ unsigned int activeRegister = mUAVRegister;
+ mShaderStorageBlockRegisterMap[interfaceBlock.name().data()] = activeRegister;
+
+ if (instanceVariable != nullptr && instanceVariable->getType().isArray())
+ {
+ unsigned int instanceArraySize = instanceVariable->getType().getOutermostArraySize();
+ for (unsigned int arrayIndex = 0; arrayIndex < instanceArraySize; arrayIndex++)
+ {
+ interfaceBlocks += shaderStorageBlockString(
+ interfaceBlock, instanceVariable, activeRegister + arrayIndex, arrayIndex);
+ }
+ mUAVRegister += instanceArraySize;
+ }
+ else
+ {
+ interfaceBlocks += shaderStorageBlockString(interfaceBlock, instanceVariable,
+ activeRegister, GL_INVALID_INDEX);
+ mUAVRegister += 1u;
+ }
+ }
+
+ return (interfaceBlocks.empty() ? "" : ("// Shader Storage Blocks\n\n" + interfaceBlocks));
+}
+
TString ResourcesHLSL::uniformBlockString(const TInterfaceBlock &interfaceBlock,
const TVariable *instanceVariable,
unsigned int registerIndex,
@@ -569,7 +603,7 @@
if (instanceVariable != nullptr)
{
hlsl += " " + InterfaceBlockStructName(interfaceBlock) + " " +
- UniformBlockInstanceString(instanceVariable->name(), arrayIndex) + ";\n";
+ InterfaceBlockInstanceString(instanceVariable->name(), arrayIndex) + ";\n";
}
else
{
@@ -582,8 +616,28 @@
return hlsl;
}
-TString ResourcesHLSL::UniformBlockInstanceString(const ImmutableString &instanceName,
- unsigned int arrayIndex)
+TString ResourcesHLSL::shaderStorageBlockString(const TInterfaceBlock &interfaceBlock,
+ const TVariable *instanceVariable,
+ unsigned int registerIndex,
+ unsigned int arrayIndex)
+{
+ TString hlsl;
+ if (instanceVariable != nullptr)
+ {
+ hlsl += "RWByteAddressBuffer " +
+ InterfaceBlockInstanceString(instanceVariable->name(), arrayIndex) +
+ ": register(u" + str(registerIndex) + ");\n";
+ }
+ else
+ {
+ hlsl += "RWByteAddressBuffer " + Decorate(interfaceBlock.name()) + ": register(u" +
+ str(registerIndex) + ");\n";
+ }
+ return hlsl;
+}
+
+TString ResourcesHLSL::InterfaceBlockInstanceString(const ImmutableString &instanceName,
+ unsigned int arrayIndex)
{
if (arrayIndex != GL_INVALID_INDEX)
{
diff --git a/src/compiler/translator/ResourcesHLSL.h b/src/compiler/translator/ResourcesHLSL.h
index 5f67ea0..1cd344e 100644
--- a/src/compiler/translator/ResourcesHLSL.h
+++ b/src/compiler/translator/ResourcesHLSL.h
@@ -38,10 +38,11 @@
void samplerMetadataUniforms(TInfoSinkBase &out, const char *reg);
TString uniformBlocksHeader(const ReferencedInterfaceBlocks &referencedInterfaceBlocks);
+ TString shaderStorageBlocksHeader(const ReferencedInterfaceBlocks &referencedInterfaceBlocks);
// Used for direct index references
- static TString UniformBlockInstanceString(const ImmutableString &instanceName,
- unsigned int arrayIndex);
+ static TString InterfaceBlockInstanceString(const ImmutableString &instanceName,
+ unsigned int arrayIndex);
const std::map<std::string, unsigned int> &getUniformBlockRegisterMap() const
{
@@ -57,6 +58,11 @@
const TVariable *instanceVariable,
unsigned int registerIndex,
unsigned int arrayIndex);
+
+ TString shaderStorageBlockString(const TInterfaceBlock &interfaceBlock,
+ const TVariable *instanceVariable,
+ unsigned int registerIndex,
+ unsigned int arrayIndex);
TString uniformBlockMembersString(const TInterfaceBlock &interfaceBlock,
TLayoutBlockStorage blockStorage);
TString uniformBlockStructString(const TInterfaceBlock &interfaceBlock);
@@ -104,13 +110,14 @@
unsigned int mUniformRegister;
unsigned int mUniformBlockRegister;
unsigned int mTextureRegister;
- unsigned int mRWTextureRegister;
+ unsigned int mUAVRegister;
unsigned int mSamplerCount;
StructureHLSL *mStructureHLSL;
ShShaderOutput mOutputType;
const std::vector<Uniform> &mUniforms;
std::map<std::string, unsigned int> mUniformBlockRegisterMap;
+ std::map<std::string, unsigned int> mShaderStorageBlockRegisterMap;
std::map<std::string, unsigned int> mUniformRegisterMap;
};
}
diff --git a/src/compiler/translator/ShaderStorageBlockFunctionHLSL.cpp b/src/compiler/translator/ShaderStorageBlockFunctionHLSL.cpp
new file mode 100644
index 0000000..a48b36a
--- /dev/null
+++ b/src/compiler/translator/ShaderStorageBlockFunctionHLSL.cpp
@@ -0,0 +1,123 @@
+//
+// Copyright 2018 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.
+//
+// ShaderStorageBlockFunctionHLSL: Wrapper functions for RWByteAddressBuffer Load/Store functions.
+//
+
+#include "compiler/translator/ShaderStorageBlockFunctionHLSL.h"
+
+#include "compiler/translator/UtilsHLSL.h"
+
+namespace sh
+{
+
+// static
+void ShaderStorageBlockFunctionHLSL::OutputSSBOLoadFunctionBody(
+ TInfoSinkBase &out,
+ const ShaderStorageBlockFunction &ssboFunction)
+{
+ if (ssboFunction.type.isScalar())
+ {
+ TString convertString;
+ switch (ssboFunction.type.getBasicType())
+ {
+ case EbtFloat:
+ convertString = "asfloat(";
+ break;
+ case EbtInt:
+ convertString = "asint(";
+ break;
+ case EbtUInt:
+ convertString = "asuint(";
+ break;
+ case EbtBool:
+ convertString = "asint(";
+ break;
+ default:
+ UNREACHABLE();
+ break;
+ }
+
+ out << " " << ssboFunction.typeString << " result = " << convertString
+ << "buffer.Load(loc));\n";
+ out << " return result;\n";
+ return;
+ }
+
+ // TODO(jiajia.qin@intel.com): Process all possible return types.
+ out << " return 1.0;\n";
+}
+
+// static
+void ShaderStorageBlockFunctionHLSL::OutputSSBOStoreFunctionBody(
+ TInfoSinkBase &out,
+ const ShaderStorageBlockFunction &ssboFunction)
+{
+ if (ssboFunction.type.isScalar())
+ {
+ out << " buffer.Store(loc, asuint(value));\n";
+ }
+
+ // TODO(jiajia.qin@intel.com): Process all possible return types.
+}
+
+bool ShaderStorageBlockFunctionHLSL::ShaderStorageBlockFunction::operator<(
+ const ShaderStorageBlockFunction &rhs) const
+{
+ return std::tie(functionName, typeString, method) <
+ std::tie(rhs.functionName, rhs.typeString, rhs.method);
+}
+
+TString ShaderStorageBlockFunctionHLSL::registerShaderStorageBlockFunction(const TType &type,
+ SSBOMethod method)
+{
+ ShaderStorageBlockFunction ssboFunction;
+ ssboFunction.typeString = TypeString(type);
+ ssboFunction.method = method;
+ ssboFunction.type = type;
+
+ switch (method)
+ {
+ case SSBOMethod::LOAD:
+ ssboFunction.functionName = ssboFunction.typeString + "_Load";
+ break;
+ case SSBOMethod::STORE:
+ ssboFunction.functionName = ssboFunction.typeString + "_Store";
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ mRegisteredShaderStorageBlockFunctions.insert(ssboFunction);
+ return ssboFunction.functionName;
+}
+
+void ShaderStorageBlockFunctionHLSL::shaderStorageBlockFunctionHeader(TInfoSinkBase &out)
+{
+ for (const ShaderStorageBlockFunction &ssboFunction : mRegisteredShaderStorageBlockFunctions)
+ {
+ if (ssboFunction.method == SSBOMethod::LOAD)
+ {
+ // Function header
+ out << ssboFunction.typeString << " " << ssboFunction.functionName
+ << "(RWByteAddressBuffer buffer, uint loc)\n";
+ out << "{\n";
+ OutputSSBOLoadFunctionBody(out, ssboFunction);
+ }
+ else
+ {
+ // Function header
+ out << "void " << ssboFunction.functionName << "(RWByteAddressBuffer buffer, uint loc, "
+ << ssboFunction.typeString << " value)\n";
+ out << "{\n";
+ OutputSSBOStoreFunctionBody(out, ssboFunction);
+ }
+
+ out << "}\n"
+ "\n";
+ }
+}
+
+} // namespace sh
diff --git a/src/compiler/translator/ShaderStorageBlockFunctionHLSL.h b/src/compiler/translator/ShaderStorageBlockFunctionHLSL.h
new file mode 100644
index 0000000..36d67a6
--- /dev/null
+++ b/src/compiler/translator/ShaderStorageBlockFunctionHLSL.h
@@ -0,0 +1,70 @@
+//
+// Copyright 2018 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.
+//
+// ShaderStorageBlockOutputHLSL: A traverser to translate a ssbo_access_chain to an offset of
+// RWByteAddressBuffer.
+// //EOpIndexDirectInterfaceBlock
+// ssbo_variable :=
+// | the name of the SSBO
+// | the name of a variable in an SSBO backed interface block
+
+// // EOpIndexInDirect
+// // EOpIndexDirect
+// ssbo_array_indexing := ssbo_access_chain[expr_no_ssbo]
+
+// // EOpIndexDirectStruct
+// ssbo_structure_access := ssbo_access_chain.identifier
+
+// ssbo_access_chain :=
+// | ssbo_variable
+// | ssbo_array_indexing
+// | ssbo_structure_access
+//
+
+#ifndef COMPILER_TRANSLATOR_SHADERSTORAGEBLOCKFUNCTIONHLSL_H_
+#define COMPILER_TRANSLATOR_SHADERSTORAGEBLOCKFUNCTIONHLSL_H_
+
+#include <set>
+
+#include "compiler/translator/InfoSink.h"
+#include "compiler/translator/Types.h"
+
+namespace sh
+{
+
+enum class SSBOMethod
+{
+ LOAD,
+ STORE
+};
+
+class ShaderStorageBlockFunctionHLSL final : angle::NonCopyable
+{
+ public:
+ TString registerShaderStorageBlockFunction(const TType &type, SSBOMethod method);
+
+ void shaderStorageBlockFunctionHeader(TInfoSinkBase &out);
+
+ private:
+ struct ShaderStorageBlockFunction
+ {
+ bool operator<(const ShaderStorageBlockFunction &rhs) const;
+ TString functionName;
+ TString typeString;
+ SSBOMethod method;
+ TType type;
+ };
+
+ static void OutputSSBOLoadFunctionBody(TInfoSinkBase &out,
+ const ShaderStorageBlockFunction &imageFunction);
+ static void OutputSSBOStoreFunctionBody(TInfoSinkBase &out,
+ const ShaderStorageBlockFunction &imageFunction);
+ using ShaderStorageBlockFunctionSet = std::set<ShaderStorageBlockFunction>;
+ ShaderStorageBlockFunctionSet mRegisteredShaderStorageBlockFunctions;
+};
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_SHADERSTORAGEBLOCKFUNCTIONHLSL_H_
diff --git a/src/compiler/translator/ShaderStorageBlockOutputHLSL.cpp b/src/compiler/translator/ShaderStorageBlockOutputHLSL.cpp
new file mode 100644
index 0000000..28b7ac8
--- /dev/null
+++ b/src/compiler/translator/ShaderStorageBlockOutputHLSL.cpp
@@ -0,0 +1,360 @@
+//
+// Copyright 2018 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.
+//
+// ShaderStorageBlockOutputHLSL: A traverser to translate a ssbo_access_chain to an offset of
+// RWByteAddressBuffer.
+// //EOpIndexDirectInterfaceBlock
+// ssbo_variable :=
+// | the name of the SSBO
+// | the name of a variable in an SSBO backed interface block
+
+// // EOpIndexInDirect
+// // EOpIndexDirect
+// ssbo_array_indexing := ssbo_access_chain[expr_no_ssbo]
+
+// // EOpIndexDirectStruct
+// ssbo_structure_access := ssbo_access_chain.identifier
+
+// ssbo_access_chain :=
+// | ssbo_variable
+// | ssbo_array_indexing
+// | ssbo_structure_access
+//
+
+#include "compiler/translator/ShaderStorageBlockOutputHLSL.h"
+
+#include "compiler/translator/ResourcesHLSL.h"
+#include "compiler/translator/blocklayout.h"
+#include "compiler/translator/blocklayoutHLSL.h"
+#include "compiler/translator/util.h"
+
+namespace sh
+{
+
+namespace
+{
+
+const TField *GetFieldMemberInShaderStorageBlock(const TInterfaceBlock *interfaceBlock,
+ const ImmutableString &variableName)
+{
+ for (const TField *field : interfaceBlock->fields())
+ {
+ if (field->name() == variableName)
+ {
+ return field;
+ }
+ }
+ return nullptr;
+}
+
+void SetShaderStorageBlockFieldMemberInfo(const TFieldList &fields, sh::BlockLayoutEncoder *encoder)
+{
+ for (TField *field : fields)
+ {
+ const TType &fieldType = *field->type();
+ if (fieldType.getStruct())
+ {
+ // TODO(jiajia.qin@intel.com): Add structure field member support.
+ }
+ else if (fieldType.isArrayOfArrays())
+ {
+ // TODO(jiajia.qin@intel.com): Add array of array field member support.
+ }
+ else
+ {
+ std::vector<unsigned int> fieldArraySizes;
+ if (auto *arraySizes = fieldType.getArraySizes())
+ {
+ fieldArraySizes.assign(arraySizes->begin(), arraySizes->end());
+ }
+ const bool isRowMajorLayout =
+ (fieldType.getLayoutQualifier().matrixPacking == EmpRowMajor);
+ const BlockMemberInfo &memberInfo =
+ encoder->encodeType(GLVariableType(fieldType), fieldArraySizes,
+ isRowMajorLayout && fieldType.isMatrix());
+ field->setOffset(memberInfo.offset);
+ field->setArrayStride(memberInfo.arrayStride);
+ }
+ }
+}
+
+void SetShaderStorageBlockMembersOffset(const TInterfaceBlock *interfaceBlock)
+{
+ sh::Std140BlockEncoder std140Encoder;
+ sh::HLSLBlockEncoder hlslEncoder(sh::HLSLBlockEncoder::ENCODE_PACKED, false);
+ sh::BlockLayoutEncoder *encoder = nullptr;
+
+ if (interfaceBlock->blockStorage() == EbsStd140)
+ {
+ encoder = &std140Encoder;
+ }
+ else
+ {
+ // TODO(jiajia.qin@intel.com): add std430 support.
+ encoder = &hlslEncoder;
+ }
+
+ SetShaderStorageBlockFieldMemberInfo(interfaceBlock->fields(), encoder);
+}
+
+} // anonymous namespace
+
+ShaderStorageBlockOutputHLSL::ShaderStorageBlockOutputHLSL(OutputHLSL *outputHLSL,
+ TSymbolTable *symbolTable,
+ ResourcesHLSL *resourcesHLSL)
+ : TIntermTraverser(true, true, true, symbolTable),
+ mIsLoadFunctionCall(false),
+ mOutputHLSL(outputHLSL),
+ mResourcesHLSL(resourcesHLSL)
+{
+ mSSBOFunctionHLSL = new ShaderStorageBlockFunctionHLSL;
+}
+
+ShaderStorageBlockOutputHLSL::~ShaderStorageBlockOutputHLSL()
+{
+ SafeDelete(mSSBOFunctionHLSL);
+}
+
+void ShaderStorageBlockOutputHLSL::outputStoreFunctionCallPrefix(TIntermTyped *node)
+{
+ mIsLoadFunctionCall = false;
+ traverseSSBOAccess(node, SSBOMethod::STORE);
+}
+
+void ShaderStorageBlockOutputHLSL::outputLoadFunctionCall(TIntermTyped *node)
+{
+ mIsLoadFunctionCall = true;
+ traverseSSBOAccess(node, SSBOMethod::LOAD);
+}
+
+void ShaderStorageBlockOutputHLSL::traverseSSBOAccess(TIntermTyped *node, SSBOMethod method)
+{
+ const TString &functionName =
+ mSSBOFunctionHLSL->registerShaderStorageBlockFunction(node->getType(), method);
+ TInfoSinkBase &out = mOutputHLSL->getInfoSink();
+ out << functionName;
+ out << "(";
+ node->traverse(this);
+}
+
+void ShaderStorageBlockOutputHLSL::writeShaderStorageBlocksHeader(TInfoSinkBase &out) const
+{
+ out << mResourcesHLSL->shaderStorageBlocksHeader(mReferencedShaderStorageBlocks);
+ mSSBOFunctionHLSL->shaderStorageBlockFunctionHeader(out);
+}
+
+// Check if the current node is the end of the sssbo access chain. If true, we should output ')' for
+// Load method.
+bool ShaderStorageBlockOutputHLSL::isEndOfSSBOAccessChain()
+{
+ TIntermNode *parent = getParentNode();
+ if (parent)
+ {
+ TIntermBinary *parentBinary = parent->getAsBinaryNode();
+ if (parentBinary != nullptr)
+ {
+ switch (parentBinary->getOp())
+ {
+ case EOpIndexDirectStruct:
+ case EOpIndexDirect:
+ case EOpIndexIndirect:
+ {
+ return false;
+ }
+ default:
+ return true;
+ }
+ }
+
+ const TIntermSwizzle *parentSwizzle = parent->getAsSwizzleNode();
+ if (parentSwizzle)
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+void ShaderStorageBlockOutputHLSL::visitSymbol(TIntermSymbol *node)
+{
+ TInfoSinkBase &out = mOutputHLSL->getInfoSink();
+ const TVariable &variable = node->variable();
+ TQualifier qualifier = variable.getType().getQualifier();
+
+ if (qualifier == EvqBuffer)
+ {
+ const TType &variableType = variable.getType();
+ const TInterfaceBlock *interfaceBlock = variableType.getInterfaceBlock();
+ ASSERT(interfaceBlock);
+ if (mReferencedShaderStorageBlocks.count(interfaceBlock->uniqueId().get()) == 0)
+ {
+ const TVariable *instanceVariable = nullptr;
+ if (variableType.isInterfaceBlock())
+ {
+ instanceVariable = &variable;
+ }
+ mReferencedShaderStorageBlocks[interfaceBlock->uniqueId().get()] =
+ new TReferencedBlock(interfaceBlock, instanceVariable);
+ SetShaderStorageBlockMembersOffset(interfaceBlock);
+ }
+ if (variableType.isInterfaceBlock())
+ {
+ out << DecorateVariableIfNeeded(variable);
+ }
+ else
+ {
+ out << Decorate(interfaceBlock->name());
+ out << ", ";
+
+ const TField *field =
+ GetFieldMemberInShaderStorageBlock(interfaceBlock, variable.name());
+ writeDotOperatorOutput(out, field);
+ }
+ }
+ else
+ {
+ return mOutputHLSL->visitSymbol(node);
+ }
+}
+
+void ShaderStorageBlockOutputHLSL::visitConstantUnion(TIntermConstantUnion *node)
+{
+ TInfoSinkBase &out = mOutputHLSL->getInfoSink();
+ mOutputHLSL->writeConstantUnion(out, node->getType(), node->getConstantValue());
+}
+
+bool ShaderStorageBlockOutputHLSL::visitSwizzle(Visit visit, TIntermSwizzle *node)
+{
+ TInfoSinkBase &out = mOutputHLSL->getInfoSink();
+ if (visit == PostVisit)
+ {
+ // TODO(jiajia.qin@intel.com): add swizzle process.
+ if (mIsLoadFunctionCall)
+ {
+ out << ")";
+ }
+ }
+ return true;
+}
+
+bool ShaderStorageBlockOutputHLSL::visitBinary(Visit visit, TIntermBinary *node)
+{
+ TInfoSinkBase &out = mOutputHLSL->getInfoSink();
+
+ switch (node->getOp())
+ {
+ case EOpIndexDirect:
+ {
+ const TType &leftType = node->getLeft()->getType();
+ if (leftType.isInterfaceBlock())
+ {
+ if (visit == PreVisit)
+ {
+ ASSERT(leftType.getQualifier() == EvqBuffer);
+ TIntermSymbol *instanceArraySymbol = node->getLeft()->getAsSymbolNode();
+ const TInterfaceBlock *interfaceBlock = leftType.getInterfaceBlock();
+
+ if (mReferencedShaderStorageBlocks.count(interfaceBlock->uniqueId().get()) == 0)
+ {
+ mReferencedShaderStorageBlocks[interfaceBlock->uniqueId().get()] =
+ new TReferencedBlock(interfaceBlock, &instanceArraySymbol->variable());
+ SetShaderStorageBlockMembersOffset(interfaceBlock);
+ }
+
+ const int arrayIndex = node->getRight()->getAsConstantUnion()->getIConst(0);
+ out << mResourcesHLSL->InterfaceBlockInstanceString(
+ instanceArraySymbol->getName(), arrayIndex);
+ return false;
+ }
+ }
+ else
+ {
+ writeEOpIndexDirectOrIndirectOutput(out, visit, node);
+ }
+ break;
+ }
+ case EOpIndexIndirect:
+ // We do not currently support indirect references to interface blocks
+ ASSERT(node->getLeft()->getBasicType() != EbtInterfaceBlock);
+ writeEOpIndexDirectOrIndirectOutput(out, visit, node);
+ break;
+ case EOpIndexDirectStruct:
+ if (visit == InVisit)
+ {
+ ASSERT(IsInShaderStorageBlock(node->getLeft()));
+ const TStructure *structure = node->getLeft()->getType().getStruct();
+ const TIntermConstantUnion *index = node->getRight()->getAsConstantUnion();
+ const TField *field = structure->fields()[index->getIConst(0)];
+ writeDotOperatorOutput(out, field);
+ return false;
+ }
+ break;
+ case EOpIndexDirectInterfaceBlock:
+ if (visit == InVisit)
+ {
+ ASSERT(IsInShaderStorageBlock(node->getLeft()));
+ out << ", ";
+ const TInterfaceBlock *interfaceBlock =
+ node->getLeft()->getType().getInterfaceBlock();
+ const TIntermConstantUnion *index = node->getRight()->getAsConstantUnion();
+ const TField *field = interfaceBlock->fields()[index->getIConst(0)];
+ writeDotOperatorOutput(out, field);
+ return false;
+ }
+ break;
+ default:
+ // It may have other operators in EOpIndexIndirect. Such as buffer.attribs[(y * gridSize
+ // + x) * 6u + 0u]
+ return mOutputHLSL->visitBinary(visit, node);
+ }
+
+ return true;
+}
+
+void ShaderStorageBlockOutputHLSL::writeEOpIndexDirectOrIndirectOutput(TInfoSinkBase &out,
+ Visit visit,
+ TIntermBinary *node)
+{
+ ASSERT(IsInShaderStorageBlock(node->getLeft()));
+ if (visit == InVisit)
+ {
+ const TType &type = node->getLeft()->getType();
+ if (node->getType().isVector() && type.isMatrix())
+ {
+ int matrixStride =
+ BlockLayoutEncoder::ComponentsPerRegister * BlockLayoutEncoder::BytesPerComponent;
+ out << " + " << str(matrixStride);
+ }
+ else if (node->getType().isScalar() && !type.isArray())
+ {
+ int scalarStride = BlockLayoutEncoder::BytesPerComponent;
+ out << " + " << str(scalarStride);
+ }
+
+ out << " * ";
+ }
+ else if (visit == PostVisit && mIsLoadFunctionCall && isEndOfSSBOAccessChain())
+ {
+ out << ")";
+ }
+}
+
+void ShaderStorageBlockOutputHLSL::writeDotOperatorOutput(TInfoSinkBase &out, const TField *field)
+{
+ out << str(field->getOffset());
+
+ const TType &fieldType = *field->type();
+ if (fieldType.isArray() && !isEndOfSSBOAccessChain())
+ {
+ out << " + ";
+ out << field->getArrayStride();
+ }
+ if (mIsLoadFunctionCall && isEndOfSSBOAccessChain())
+ {
+ out << ")";
+ }
+}
+
+} // namespace sh
diff --git a/src/compiler/translator/ShaderStorageBlockOutputHLSL.h b/src/compiler/translator/ShaderStorageBlockOutputHLSL.h
new file mode 100644
index 0000000..0e978ff
--- /dev/null
+++ b/src/compiler/translator/ShaderStorageBlockOutputHLSL.h
@@ -0,0 +1,72 @@
+//
+// Copyright 2018 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.
+//
+// ShaderStorageBlockOutputHLSL: A traverser to translate a buffer variable of shader storage block
+// to an offset of RWByteAddressBuffer.
+//
+
+#ifndef COMPILER_TRANSLATOR_SHADERSTORAGEBLOCKOUTPUTHLSL_H_
+#define COMPILER_TRANSLATOR_SHADERSTORAGEBLOCKOUTPUTHLSL_H_
+
+#include "compiler/translator/ShaderStorageBlockFunctionHLSL.h"
+#include "compiler/translator/tree_util/IntermTraverse.h"
+
+namespace sh
+{
+class ResourcesHLSL;
+class OutputHLSL;
+class TSymbolTable;
+
+struct TReferencedBlock : angle::NonCopyable
+{
+ POOL_ALLOCATOR_NEW_DELETE();
+ TReferencedBlock(const TInterfaceBlock *block, const TVariable *instanceVariable);
+ const TInterfaceBlock *block;
+ const TVariable *instanceVariable; // May be nullptr if the block is not instanced.
+};
+
+// Maps from uniqueId to a variable.
+using ReferencedInterfaceBlocks = std::map<int, const TReferencedBlock *>;
+
+class ShaderStorageBlockOutputHLSL : public TIntermTraverser
+{
+ public:
+ ShaderStorageBlockOutputHLSL(OutputHLSL *outputHLSL,
+ TSymbolTable *symbolTable,
+ ResourcesHLSL *resourcesHLSL);
+
+ ~ShaderStorageBlockOutputHLSL();
+
+ // This writes part of the function call to store a value to a SSBO to the output stream. After
+ // 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.
+ void outputLoadFunctionCall(TIntermTyped *node);
+
+ void writeShaderStorageBlocksHeader(TInfoSinkBase &out) const;
+
+ protected:
+ void visitSymbol(TIntermSymbol *) override;
+ void visitConstantUnion(TIntermConstantUnion *) override;
+ bool visitSwizzle(Visit visit, TIntermSwizzle *node) override;
+ bool visitBinary(Visit visit, TIntermBinary *) override;
+
+ private:
+ void traverseSSBOAccess(TIntermTyped *node, SSBOMethod method);
+ bool isEndOfSSBOAccessChain();
+ void writeEOpIndexDirectOrIndirectOutput(TInfoSinkBase &out, Visit visit, TIntermBinary *node);
+ // Common part in dot operations.
+ void writeDotOperatorOutput(TInfoSinkBase &out, const TField *field);
+
+ bool mIsLoadFunctionCall;
+ OutputHLSL *mOutputHLSL;
+ ShaderStorageBlockFunctionHLSL *mSSBOFunctionHLSL;
+ ResourcesHLSL *mResourcesHLSL;
+ ReferencedInterfaceBlocks mReferencedShaderStorageBlocks;
+};
+}
+
+#endif // COMPILER_TRANSLATOR_SHADERSTORAGEBLOCKOUTPUTHLSL_H_
diff --git a/src/compiler/translator/StructureHLSL.cpp b/src/compiler/translator/StructureHLSL.cpp
index c5eaf34..0e91c31 100644
--- a/src/compiler/translator/StructureHLSL.cpp
+++ b/src/compiler/translator/StructureHLSL.cpp
@@ -174,7 +174,7 @@
return "";
}
- int numComponents = 0;
+ int numComponents = 0;
const TStructure *structure = type.getStruct();
if (type.isMatrix())
diff --git a/src/compiler/translator/Types.h b/src/compiler/translator/Types.h
index 537d270..d71cd62 100644
--- a/src/compiler/translator/Types.h
+++ b/src/compiler/translator/Types.h
@@ -32,7 +32,12 @@
public:
POOL_ALLOCATOR_NEW_DELETE();
TField(TType *type, const ImmutableString &name, const TSourceLoc &line, SymbolType symbolType)
- : mType(type), mName(name), mLine(line), mSymbolType(symbolType)
+ : mType(type),
+ mName(name),
+ mLine(line),
+ mSymbolType(symbolType),
+ mOffset(0),
+ mArrayStride(0)
{
ASSERT(mSymbolType != SymbolType::Empty);
}
@@ -44,12 +49,18 @@
const ImmutableString &name() const { return mName; }
const TSourceLoc &line() const { return mLine; }
SymbolType symbolType() const { return mSymbolType; }
+ unsigned int getOffset() const { return mOffset; }
+ unsigned int getArrayStride() const { return mArrayStride; }
+ void setOffset(unsigned int offset) { mOffset = offset; }
+ void setArrayStride(int arrayStride) { mArrayStride = arrayStride; }
private:
TType *mType;
const ImmutableString mName;
const TSourceLoc mLine;
const SymbolType mSymbolType;
+ unsigned int mOffset;
+ unsigned int mArrayStride;
};
typedef TVector<TField *> TFieldList;
diff --git a/src/compiler/translator/tree_ops/RemoveDynamicIndexing.cpp b/src/compiler/translator/tree_ops/RemoveDynamicIndexing.cpp
index 61f6860..84a0d2a 100644
--- a/src/compiler/translator/tree_ops/RemoveDynamicIndexing.cpp
+++ b/src/compiler/translator/tree_ops/RemoveDynamicIndexing.cpp
@@ -3,8 +3,10 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
-// RemoveDynamicIndexing is an AST traverser to remove dynamic indexing of vectors and matrices,
-// replacing them with calls to functions that choose which component to return or write.
+// RemoveDynamicIndexing is an AST traverser to remove dynamic indexing of non-SSBO vectors and
+// matrices, replacing them with calls to functions that choose which component to return or write.
+// We don't need to consider dynamic indexing in SSBO since it can be directly as part of the offset
+// of RWByteAddressBuffer.
//
#include "compiler/translator/tree_ops/RemoveDynamicIndexing.h"
@@ -374,7 +376,7 @@
TIntermSymbol *tempIndex = CreateTempSymbolNode(indexVariable);
queueReplacementWithParent(node, node->getRight(), tempIndex, OriginalNode::IS_DROPPED);
}
- else if (IntermNodePatternMatcher::IsDynamicIndexingOfVectorOrMatrix(node))
+ else if (IntermNodePatternMatcher::IsDynamicIndexingOfNonSSBOVectorOrMatrix(node))
{
mPerfDiagnostics->warning(node->getLine(),
"Performance: dynamic indexing of vectors and "
@@ -429,7 +431,7 @@
TIntermBinary *leftBinary = node->getLeft()->getAsBinaryNode();
if (leftBinary != nullptr &&
- IntermNodePatternMatcher::IsDynamicIndexingOfVectorOrMatrix(leftBinary))
+ IntermNodePatternMatcher::IsDynamicIndexingOfNonSSBOVectorOrMatrix(leftBinary))
{
// This is a case like:
// mat2 m;
diff --git a/src/compiler/translator/tree_ops/RemoveDynamicIndexing.h b/src/compiler/translator/tree_ops/RemoveDynamicIndexing.h
index 93343c0..500c88e 100644
--- a/src/compiler/translator/tree_ops/RemoveDynamicIndexing.h
+++ b/src/compiler/translator/tree_ops/RemoveDynamicIndexing.h
@@ -3,8 +3,10 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
-// RemoveDynamicIndexing is an AST traverser to remove dynamic indexing of vectors and matrices,
-// replacing them with calls to functions that choose which component to return or write.
+// RemoveDynamicIndexing is an AST traverser to remove dynamic indexing of non-SSBO vectors and
+// matrices, replacing them with calls to functions that choose which component to return or write.
+// We don't need to consider dynamic indexing in SSBO since it can be directly as part of the offset
+// of RWByteAddressBuffer.
//
#ifndef COMPILER_TRANSLATOR_TREEOPS_REMOVEDYNAMICINDEXING_H_
diff --git a/src/compiler/translator/tree_util/IntermNodePatternMatcher.cpp b/src/compiler/translator/tree_util/IntermNodePatternMatcher.cpp
index 752b9e2..0a57090 100644
--- a/src/compiler/translator/tree_util/IntermNodePatternMatcher.cpp
+++ b/src/compiler/translator/tree_util/IntermNodePatternMatcher.cpp
@@ -12,6 +12,7 @@
#include "compiler/translator/IntermNode.h"
#include "compiler/translator/SymbolTable.h"
+#include "compiler/translator/util.h"
namespace sh
{
@@ -48,6 +49,12 @@
}
// static
+bool IntermNodePatternMatcher::IsDynamicIndexingOfNonSSBOVectorOrMatrix(TIntermBinary *node)
+{
+ return IsDynamicIndexingOfVectorOrMatrix(node) && !IsInShaderStorageBlock(node->getLeft());
+}
+
+// static
bool IntermNodePatternMatcher::IsDynamicIndexingOfVectorOrMatrix(TIntermBinary *node)
{
return node->getOp() == EOpIndexIndirect && !node->getLeft()->isArray() &&
diff --git a/src/compiler/translator/tree_util/IntermNodePatternMatcher.h b/src/compiler/translator/tree_util/IntermNodePatternMatcher.h
index 1e7db4e..25a46ff 100644
--- a/src/compiler/translator/tree_util/IntermNodePatternMatcher.h
+++ b/src/compiler/translator/tree_util/IntermNodePatternMatcher.h
@@ -24,6 +24,7 @@
class IntermNodePatternMatcher
{
public:
+ static bool IsDynamicIndexingOfNonSSBOVectorOrMatrix(TIntermBinary *node);
static bool IsDynamicIndexingOfVectorOrMatrix(TIntermBinary *node);
enum PatternType
diff --git a/src/compiler/translator/util.cpp b/src/compiler/translator/util.cpp
index c01df81..37bf48e 100644
--- a/src/compiler/translator/util.cpp
+++ b/src/compiler/translator/util.cpp
@@ -751,4 +751,28 @@
return output == SH_GLSL_VULKAN_OUTPUT;
}
+bool IsInShaderStorageBlock(TIntermTyped *node)
+{
+ TIntermBinary *binaryNode = nullptr;
+ TIntermSwizzle *swizzleNode = node->getAsSwizzleNode();
+ if (swizzleNode)
+ {
+ binaryNode = swizzleNode->getOperand()->getAsBinaryNode();
+ if (binaryNode)
+ {
+ return IsInShaderStorageBlock(binaryNode->getLeft());
+ }
+ }
+ binaryNode = node->getAsBinaryNode();
+
+ if (binaryNode)
+ {
+ return IsInShaderStorageBlock(binaryNode->getLeft());
+ }
+
+ const TType &type = node->getType();
+
+ return type.getQualifier() == EvqBuffer;
+}
+
} // namespace sh
diff --git a/src/compiler/translator/util.h b/src/compiler/translator/util.h
index 0152533..cf23373 100644
--- a/src/compiler/translator/util.h
+++ b/src/compiler/translator/util.h
@@ -25,6 +25,7 @@
{
class TIntermBlock;
class TSymbolTable;
+class TIntermTyped;
float NumericLexFloat32OutOfRangeToInfinity(const std::string &str);
@@ -62,6 +63,8 @@
bool IsOutputGLSL(ShShaderOutput output);
bool IsOutputHLSL(ShShaderOutput output);
bool IsOutputVulkan(ShShaderOutput output);
+
+bool IsInShaderStorageBlock(TIntermTyped *node);
} // namespace sh
#endif // COMPILER_TRANSLATOR_UTIL_H_
diff --git a/src/tests/deqp_support/deqp_gles31_test_expectations.txt b/src/tests/deqp_support/deqp_gles31_test_expectations.txt
index 6536bed..78c3508 100644
--- a/src/tests/deqp_support/deqp_gles31_test_expectations.txt
+++ b/src/tests/deqp_support/deqp_gles31_test_expectations.txt
@@ -58,6 +58,16 @@
1951 D3D11 : dEQP-GLES31.functional.ssbo.* = SKIP
1442 D3D11 : dEQP-GLES31.functional.layout_binding.image.* = SKIP
1951 D3D11 : dEQP-GLES31.functional.shaders.linkage.es31.shader_storage_block.* = SKIP
+// This case is skipped since it uses atomic counter builtin functions which haven't been implemented.
+1729 D3D11 : dEQP-GLES31.functional.state_query.program.active_atomic_counter_buffers_get_programiv = SKIP
+// Below cases are skipped since it uses variable * ssbo.matrix as the expression which haven't been implemented,
+// that will result the code entering unreachable.
+1951 D3D11 : dEQP-GLES31.functional.debug.negative_coverage.callbacks.ssbo_block.ssbo_block_interface_matching_tests = SKIP
+1951 D3D11 : dEQP-GLES31.functional.debug.negative_coverage.callbacks.ssbo_block.ssbo_using_shared_qualifier_tests = SKIP
+1951 D3D11 : dEQP-GLES31.functional.debug.negative_coverage.get_error.ssbo_block.ssbo_block_interface_matching_tests = SKIP
+1951 D3D11 : dEQP-GLES31.functional.debug.negative_coverage.get_error.ssbo_block.ssbo_using_shared_qualifier_tests = SKIP
+1951 D3D11 : dEQP-GLES31.functional.debug.negative_coverage.log.ssbo_block.ssbo_block_interface_matching_tests = SKIP
+1951 D3D11 : dEQP-GLES31.functional.debug.negative_coverage.log.ssbo_block.ssbo_using_shared_qualifier_tests = SKIP
// D3D11 Failing Tests
1442 D3D11 : dEQP-GLES31.functional.state_query.integer.max_compute_shared_memory_size_* = FAIL
@@ -80,7 +90,6 @@
1442 D3D11 : dEQP-GLES31.functional.debug.error_groups.case_3 = FAIL
1442 D3D11 : dEQP-GLES31.functional.debug.error_groups.case_9 = FAIL
1442 D3D11 : dEQP-GLES31.functional.debug.error_groups.case_10 = FAIL
-1442 D3D11 : dEQP-GLES31.functional.state_query.program.active_atomic_counter_buffers_get_programiv = FAIL
1442 D3D11 : dEQP-GLES31.functional.layout_binding.ubo.* = FAIL
1663 D3D11 : dEQP-GLES31.functional.draw_indirect.compute_interop.* = FAIL
1442 D3D11 : dEQP-GLES31.functional.shaders.builtin_constants.core.max_vertex_attribs = FAIL
diff --git a/src/tests/gl_tests/ComputeShaderTest.cpp b/src/tests/gl_tests/ComputeShaderTest.cpp
index f1c129e..6bc1a22 100644
--- a/src/tests/gl_tests/ComputeShaderTest.cpp
+++ b/src/tests/gl_tests/ComputeShaderTest.cpp
@@ -1803,6 +1803,185 @@
EXPECT_EQ(100u, outputValue);
}
+// Test that scalar buffer variables are supported.
+TEST_P(ComputeShaderTest, ShaderStorageBlocksScalar)
+{
+ const char kCSSource[] =
+ R"(#version 310 es
+ layout(local_size_x=1) in;
+ layout(std140, binding = 0) buffer blockA {
+ uvec3 uv;
+ float f;
+ } instanceA;
+ layout(std140, binding = 1) buffer blockB {
+ vec2 v;
+ uint u[3];
+ float f;
+ };
+ void main()
+ {
+ f = instanceA.f;
+ })";
+
+ ANGLE_GL_COMPUTE_PROGRAM(program, kCSSource);
+ EXPECT_GL_NO_ERROR();
+}
+
+// Test that vector buffer variables are supported.
+TEST_P(ComputeShaderTest, ShaderStorageBlocksVector)
+{
+ const char kCSSource[] =
+ R"(#version 310 es
+ layout(local_size_x=1) in;
+ layout(std140, binding = 0) buffer blockA {
+ vec2 f;
+ } instanceA;
+ layout(std140, binding = 1) buffer blockB {
+ vec3 f;
+ };
+ void main()
+ {
+ f[1] = instanceA.f[0];
+ })";
+
+ ANGLE_GL_COMPUTE_PROGRAM(program, kCSSource);
+ EXPECT_GL_NO_ERROR();
+}
+
+// Test that matrix buffer variables are supported.
+TEST_P(ComputeShaderTest, ShaderStorageBlocksMatrix)
+{
+ const char kCSSource[] =
+ R"(#version 310 es
+ layout(local_size_x=1) in;
+ layout(std140, binding = 0) buffer blockA {
+ mat3x4 m;
+ } instanceA;
+ layout(std140, binding = 1) buffer blockB {
+ mat3x4 m;
+ };
+ void main()
+ {
+ m[0][1] = instanceA.m[0][1];
+ })";
+
+ ANGLE_GL_COMPUTE_PROGRAM(program, kCSSource);
+ EXPECT_GL_NO_ERROR();
+}
+
+// Test that scalar array buffer variables are supported.
+TEST_P(ComputeShaderTest, ShaderStorageBlocksScalarArray)
+{
+ const char kCSSource[] =
+ R"(#version 310 es
+ layout(local_size_x=8) in;
+ layout(std140, binding = 0) buffer blockA {
+ float f[8];
+ } instanceA;
+ layout(std140, binding = 1) buffer blockB {
+ float f[8];
+ };
+ void main()
+ {
+ f[gl_LocalInvocationIndex] = instanceA.f[gl_LocalInvocationIndex];
+ })";
+
+ ANGLE_GL_COMPUTE_PROGRAM(program, kCSSource);
+ EXPECT_GL_NO_ERROR();
+}
+
+// Test that vector array buffer variables are supported.
+TEST_P(ComputeShaderTest, ShaderStorageBlocksVectorArray)
+{
+ const char kCSSource[] =
+ R"(#version 310 es
+ layout(local_size_x=4) in;
+ layout(std140, binding = 0) buffer blockA {
+ vec2 v[4];
+ } instanceA;
+ layout(std140, binding = 1) buffer blockB {
+ vec4 v[4];
+ };
+ void main()
+ {
+ v[0][gl_LocalInvocationIndex] = instanceA.v[gl_LocalInvocationIndex][1];
+ })";
+
+ ANGLE_GL_COMPUTE_PROGRAM(program, kCSSource);
+ EXPECT_GL_NO_ERROR();
+}
+
+// Test that matrix array buffer variables are supported.
+TEST_P(ComputeShaderTest, ShaderStorageBlocksMatrixArray)
+{
+ const char kCSSource[] =
+ R"(#version 310 es
+ layout(local_size_x=8) in;
+ layout(std140, binding = 0) buffer blockA {
+ float v1[5];
+ mat4 m[8];
+ } instanceA;
+ layout(std140, binding = 1) buffer blockB {
+ vec2 v1[3];
+ mat4 m[8];
+ };
+ void main()
+ {
+ float data = instanceA.m[gl_LocalInvocationIndex][0][0];
+ m[gl_LocalInvocationIndex][gl_LocalInvocationIndex][gl_LocalInvocationIndex] = data;
+ })";
+
+ ANGLE_GL_COMPUTE_PROGRAM(program, kCSSource);
+ EXPECT_GL_NO_ERROR();
+}
+
+// Test that shader storage blocks only in assignment right is supported.
+TEST_P(ComputeShaderTest, ShaderStorageBlocksInAssignmentRight)
+{
+ const char kCSSource[] =
+ R"(#version 310 es
+ layout(local_size_x=8) in;
+ layout(std140, binding = 0) buffer blockA {
+ float data[8];
+ } instanceA;
+ layout(r32f, binding = 0) writeonly uniform highp image2D imageOut;
+
+ void main()
+ {
+ float data = 1.0;
+ data = instanceA.data[gl_LocalInvocationIndex];
+ imageStore(imageOut, ivec2(gl_LocalInvocationID.xy), vec4(data));
+ }
+ )";
+
+ ANGLE_GL_COMPUTE_PROGRAM(program, kCSSource);
+ EXPECT_GL_NO_ERROR();
+}
+
+// Test that shader storage blocks with unsized array are supported.
+TEST_P(ComputeShaderTest, ShaderStorageBlocksWithUnsizedArray)
+{
+ const char kCSSource[] =
+ R"(#version 310 es
+ layout(local_size_x=8) in;
+ layout(std140, binding = 0) buffer blockA {
+ float v[];
+ } instanceA;
+ layout(std140, binding = 0) buffer blockB {
+ float v[];
+ } instanceB[1];
+
+ void main()
+ {
+ float data = instanceA.v[gl_LocalInvocationIndex];
+ instanceB[0].v[gl_LocalInvocationIndex * 2u + 1u] = data;
+ }
+ )";
+
+ ANGLE_GL_COMPUTE_PROGRAM(program, kCSSource);
+ EXPECT_GL_NO_ERROR();
+}
+
// Check that it is not possible to create a compute shader when the context does not support ES
// 3.10
TEST_P(ComputeShaderTestES3, NotSupported)