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;
diff --git a/src/tests/gl_tests/ShaderStorageBufferTest.cpp b/src/tests/gl_tests/ShaderStorageBufferTest.cpp
index a638b21..e855480 100644
--- a/src/tests/gl_tests/ShaderStorageBufferTest.cpp
+++ b/src/tests/gl_tests/ShaderStorageBufferTest.cpp
@@ -1539,6 +1539,68 @@
EXPECT_GL_NO_ERROR();
}
+// Test that the length of unsized array is supported.
+TEST_P(ShaderStorageBufferTest31, UnsizedArrayLength)
+{
+ constexpr char kComputeShaderSource[] =
+ R"(#version 310 es
+layout (local_size_x=1) in;
+layout(std430, binding = 0) buffer Storage0 {
+ uint buf1[2];
+ uint buf2[];
+} sb_load;
+layout(std430, binding = 1) buffer Storage1 {
+ int unsizedArrayLength;
+ uint buf1[2];
+ uint buf2[];
+} sb_store;
+
+void main()
+{
+ sb_store.unsizedArrayLength = sb_store.buf2.length();
+ for (int i = 0; i < sb_load.buf1.length(); i++) {
+ sb_store.buf1[i] = sb_load.buf1[i];
+ }
+ for (int i = 0; i < sb_load.buf2.length(); i++) {
+ sb_store.buf2[i] = sb_load.buf2[i];
+ }
+}
+)";
+
+ ANGLE_GL_COMPUTE_PROGRAM(program, kComputeShaderSource);
+ glUseProgram(program);
+
+ constexpr unsigned int kBytesPerComponent = sizeof(unsigned int);
+ constexpr unsigned int kLoadBlockElementCount = 5;
+ constexpr unsigned int kStoreBlockElementCount = 6;
+ constexpr unsigned int kInputValues[kLoadBlockElementCount] = {1u, 2u, 3u, 4u, 5u};
+ constexpr unsigned int kExpectedValues[kStoreBlockElementCount] = {3u, 1u, 2u, 3u, 4u, 5u};
+ GLBuffer shaderStorageBuffer[2];
+ glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[0]);
+ glBufferData(GL_SHADER_STORAGE_BUFFER, kLoadBlockElementCount * kBytesPerComponent,
+ &kInputValues, GL_STATIC_DRAW);
+ glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[1]);
+ glBufferData(GL_SHADER_STORAGE_BUFFER, kStoreBlockElementCount * kBytesPerComponent, nullptr,
+ GL_STATIC_DRAW);
+
+ glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, shaderStorageBuffer[0]);
+ glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, shaderStorageBuffer[1]);
+
+ glDispatchCompute(1, 1, 1);
+ glFinish();
+
+ glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[1]);
+ const GLuint *ptr = reinterpret_cast<const GLuint *>(
+ glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, kStoreBlockElementCount * kBytesPerComponent,
+ GL_MAP_READ_BIT));
+ for (unsigned int i = 0; i < kStoreBlockElementCount; i++)
+ {
+ EXPECT_EQ(kExpectedValues[i], *(ptr + i));
+ }
+
+ EXPECT_GL_NO_ERROR();
+}
+
// Test that compond assignment operator for buffer variable is correctly handled.
TEST_P(ShaderStorageBufferTest31, CompoundAssignmentOperator)
{