Commit 8edb7188 by Qin Jiajia Committed by Commit Bot

ES31: Add structure field member support in SSBO

This patch adds the structure field member support in SSBO. To support it, below things are done: 1. Calculate the offset and arrayStride in SSBO structure. 2. Support array of arrays translation. Bug: angleproject:1951 Change-Id: If8f233e6388d5bb905e78c39a85112d394298138 Reviewed-on: https://chromium-review.googlesource.com/c/1278097 Commit-Queue: Jiajia Qin <jiajia.qin@intel.com> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org>
parent c1fd7376
...@@ -49,14 +49,64 @@ const TField *GetFieldMemberInShaderStorageBlock(const TInterfaceBlock *interfac ...@@ -49,14 +49,64 @@ const TField *GetFieldMemberInShaderStorageBlock(const TInterfaceBlock *interfac
return nullptr; return nullptr;
} }
void SetShaderStorageBlockFieldMemberInfo(const TFieldList &fields, sh::BlockLayoutEncoder *encoder) void SetShaderStorageBlockFieldMemberInfo(const TFieldList &fields,
sh::BlockLayoutEncoder *encoder,
TLayoutBlockStorage storage);
size_t SetBlockFieldMemberInfoAndReturnBlockSize(const TFieldList &fields,
TLayoutBlockStorage storage)
{
sh::Std140BlockEncoder std140Encoder;
sh::HLSLBlockEncoder hlslEncoder(sh::HLSLBlockEncoder::ENCODE_PACKED, false);
sh::BlockLayoutEncoder *structureEncoder = nullptr;
if (storage == EbsStd140)
{
structureEncoder = &std140Encoder;
}
else
{
// TODO(jiajia.qin@intel.com): add std430 support.
structureEncoder = &hlslEncoder;
}
SetShaderStorageBlockFieldMemberInfo(fields, structureEncoder, storage);
structureEncoder->exitAggregateType();
return structureEncoder->getBlockSize();
}
// TODO(jiajia.qin@intel.com): Save the offset/arrystride into a std::map<const TField *,
// BlockMemberInfo> BlockLayoutMap instead of adding offset/arrayStride to the TField.
// http://anglebug.com/1951
void SetShaderStorageBlockFieldMemberInfo(const TFieldList &fields,
sh::BlockLayoutEncoder *encoder,
TLayoutBlockStorage storage)
{ {
for (TField *field : fields) for (TField *field : fields)
{ {
const TType &fieldType = *field->type(); const TType &fieldType = *field->type();
if (fieldType.getStruct()) if (fieldType.getStruct())
{ {
// TODO(jiajia.qin@intel.com): Add structure field member support. encoder->enterAggregateType();
field->setOffset(encoder->getBlockSize());
// This is to set structure member offset and array stride using a new encoder to ensure
// that the first field member offset in structure is always zero.
size_t structureStride =
SetBlockFieldMemberInfoAndReturnBlockSize(fieldType.getStruct()->fields(), storage);
field->setArrayStride(structureStride);
// Below if-else is in order to get correct offset for the field members after structure
// field.
if (fieldType.isArray())
{
size_t size = fieldType.getArraySizeProduct() * structureStride;
encoder->increaseCurrentOffset(size);
}
else
{
encoder->increaseCurrentOffset(structureStride);
}
} }
else if (fieldType.isArrayOfArrays()) else if (fieldType.isArrayOfArrays())
{ {
...@@ -96,7 +146,22 @@ void SetShaderStorageBlockMembersOffset(const TInterfaceBlock *interfaceBlock) ...@@ -96,7 +146,22 @@ void SetShaderStorageBlockMembersOffset(const TInterfaceBlock *interfaceBlock)
encoder = &hlslEncoder; encoder = &hlslEncoder;
} }
SetShaderStorageBlockFieldMemberInfo(interfaceBlock->fields(), encoder); SetShaderStorageBlockFieldMemberInfo(interfaceBlock->fields(), encoder,
interfaceBlock->blockStorage());
}
bool IsInArrayOfArraysChain(TIntermTyped *node)
{
if (node->getType().isArrayOfArrays())
return true;
TIntermBinary *binaryNode = node->getAsBinaryNode();
if (binaryNode)
{
if (binaryNode->getLeft()->getType().isArrayOfArrays())
return true;
}
return false;
} }
} // anonymous namespace } // anonymous namespace
...@@ -360,23 +425,58 @@ void ShaderStorageBlockOutputHLSL::writeEOpIndexDirectOrIndirectOutput(TInfoSink ...@@ -360,23 +425,58 @@ void ShaderStorageBlockOutputHLSL::writeEOpIndexDirectOrIndirectOutput(TInfoSink
if (visit == InVisit) if (visit == InVisit)
{ {
const TType &type = node->getLeft()->getType(); const TType &type = node->getLeft()->getType();
if (node->getType().isVector() && type.isMatrix()) // For array of arrays, we calculate the offset using the formula below:
// elementStride * (a3 * a2 * a1 * i0 + a3 * a2 * i1 + a3 * i2 + i3)
// Note: assume that there are 4 dimensions.
// a0, a1, a2, a3 is the size of the array in each dimension. (S s[a0][a1][a2][a3])
// i0, i1, i2, i3 is the index of the array in each dimension. (s[i0][i1][i2][i3])
if (IsInArrayOfArraysChain(node->getLeft()))
{ {
int matrixStride = if (type.isArrayOfArrays())
BlockLayoutEncoder::ComponentsPerRegister * BlockLayoutEncoder::BytesPerComponent; {
out << " + " << str(matrixStride); const TVector<unsigned int> &arraySizes = *type.getArraySizes();
// Don't need to concern the tail comma which will be used to multiply the index.
for (unsigned int i = 0; i < (arraySizes.size() - 1); i++)
{
out << arraySizes[i];
out << " * ";
}
}
} }
else if (node->getType().isScalar() && !type.isArray()) else
{ {
int scalarStride = BlockLayoutEncoder::BytesPerComponent; if (node->getType().isVector() && type.isMatrix())
out << " + " << str(scalarStride); {
} int matrixStride = BlockLayoutEncoder::ComponentsPerRegister *
BlockLayoutEncoder::BytesPerComponent;
out << " + " << str(matrixStride);
}
else if (node->getType().isScalar() && !type.isArray())
{
int scalarStride = BlockLayoutEncoder::BytesPerComponent;
out << " + " << str(scalarStride);
}
out << " * "; out << " * ";
}
} }
else if (visit == PostVisit && mIsLoadFunctionCall && isEndOfSSBOAccessChain()) else if (visit == PostVisit)
{ {
out << ")"; // This is used to output the '+' in the array of arrays formula in above.
if (node->getType().isArray() && !isEndOfSSBOAccessChain())
{
out << " + ";
}
// This corresponds to '(' in writeDotOperatorOutput when fieldType.isArrayOfArrays() is
// true.
if (IsInArrayOfArraysChain(node->getLeft()) && !node->getType().isArray())
{
out << ")";
}
if (mIsLoadFunctionCall && isEndOfSSBOAccessChain())
{
out << ")";
}
} }
} }
...@@ -389,6 +489,10 @@ void ShaderStorageBlockOutputHLSL::writeDotOperatorOutput(TInfoSinkBase &out, co ...@@ -389,6 +489,10 @@ void ShaderStorageBlockOutputHLSL::writeDotOperatorOutput(TInfoSinkBase &out, co
{ {
out << " + "; out << " + ";
out << field->getArrayStride(); out << field->getArrayStride();
if (fieldType.isArrayOfArrays())
{
out << " * (";
}
} }
if (mIsLoadFunctionCall && isEndOfSSBOAccessChain()) if (mIsLoadFunctionCall && isEndOfSSBOAccessChain())
{ {
......
...@@ -172,6 +172,11 @@ BlockMemberInfo BlockLayoutEncoder::encodeType(GLenum type, ...@@ -172,6 +172,11 @@ BlockMemberInfo BlockLayoutEncoder::encodeType(GLenum type,
return memberInfo; return memberInfo;
} }
void BlockLayoutEncoder::increaseCurrentOffset(size_t offsetInBytes)
{
mCurrentOffset += (offsetInBytes / BytesPerComponent);
}
// static // static
size_t BlockLayoutEncoder::getBlockRegister(const BlockMemberInfo &info) size_t BlockLayoutEncoder::getBlockRegister(const BlockMemberInfo &info)
{ {
......
...@@ -78,6 +78,7 @@ class BlockLayoutEncoder ...@@ -78,6 +78,7 @@ class BlockLayoutEncoder
bool isRowMajorMatrix); bool isRowMajorMatrix);
size_t getBlockSize() const { return mCurrentOffset * BytesPerComponent; } size_t getBlockSize() const { return mCurrentOffset * BytesPerComponent; }
void increaseCurrentOffset(size_t offsetInBytes);
virtual void enterAggregateType() = 0; virtual void enterAggregateType() = 0;
virtual void exitAggregateType() = 0; virtual void exitAggregateType() = 0;
......
...@@ -313,6 +313,215 @@ void main() ...@@ -313,6 +313,215 @@ void main()
EXPECT_GL_NO_ERROR(); EXPECT_GL_NO_ERROR();
} }
// Test that access/write to structure data in shader storage buffer.
TEST_P(ShaderStorageBufferTest31, ShaderStorageBufferStructureArray)
{
constexpr char kComputeShaderSource[] =
R"(#version 310 es
layout(local_size_x=1, local_size_y=1, local_size_z=1) in;
struct S
{
uvec2 uvData;
uint uiData[2];
};
layout(std140, binding = 0) buffer blockIn {
S s[2];
uint lastData;
} instanceIn;
layout(std140, binding = 1) buffer blockOut {
S s[2];
uint lastData;
} instanceOut;
void main()
{
instanceOut.s[0].uvData = instanceIn.s[0].uvData;
instanceOut.s[0].uiData[0] = instanceIn.s[0].uiData[0];
instanceOut.s[0].uiData[1] = instanceIn.s[0].uiData[1];
instanceOut.s[1].uvData = instanceIn.s[1].uvData;
instanceOut.s[1].uiData[0] = instanceIn.s[1].uiData[0];
instanceOut.s[1].uiData[1] = instanceIn.s[1].uiData[1];
instanceOut.lastData = instanceIn.lastData;
}
)";
ANGLE_GL_COMPUTE_PROGRAM(program, kComputeShaderSource);
glUseProgram(program);
std::array<GLuint, 4> kUVData = {{
1u, 2u, 0u, 0u,
}};
std::array<GLuint, 8> kUIData = {{
3u, 0u, 0u, 0u, 4u, 0u, 0u, 0u,
}};
GLuint kLastData = 5u;
constexpr unsigned int kBytesPerComponent = sizeof(GLuint);
constexpr unsigned int kStructureStride = 48;
constexpr unsigned int totalSize = kStructureStride * 2 + sizeof(kLastData);
// Create shader storage buffer
GLBuffer shaderStorageBuffer[2];
glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[0]);
glBufferData(GL_SHADER_STORAGE_BUFFER, totalSize, nullptr, GL_STATIC_DRAW);
GLint offset = 0;
// upload data to instanceIn.s[0]
glBufferSubData(GL_SHADER_STORAGE_BUFFER, offset, kUVData.size() * kBytesPerComponent,
kUVData.data());
offset += (kUVData.size() * kBytesPerComponent);
glBufferSubData(GL_SHADER_STORAGE_BUFFER, offset, kUIData.size() * kBytesPerComponent,
kUIData.data());
offset += (kUIData.size() * kBytesPerComponent);
// upload data to instanceIn.s[1]
glBufferSubData(GL_SHADER_STORAGE_BUFFER, offset, kUVData.size() * kBytesPerComponent,
kUVData.data());
offset += (kUVData.size() * kBytesPerComponent);
glBufferSubData(GL_SHADER_STORAGE_BUFFER, offset, kUIData.size() * kBytesPerComponent,
kUIData.data());
offset += (kUIData.size() * kBytesPerComponent);
// upload data to instanceIn.lastData
glBufferSubData(GL_SHADER_STORAGE_BUFFER, offset, sizeof(kLastData), &kLastData);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[1]);
glBufferData(GL_SHADER_STORAGE_BUFFER, totalSize, nullptr, GL_STATIC_DRAW);
// Bind shader storage buffer
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, shaderStorageBuffer[0]);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, shaderStorageBuffer[1]);
glDispatchCompute(1, 1, 1);
glFinish();
// Read back shader storage buffer
glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[1]);
constexpr float kExpectedValues[5] = {1u, 2u, 3u, 4u, 5u};
const GLuint *ptr = reinterpret_cast<const GLuint *>(
glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, totalSize, GL_MAP_READ_BIT));
// instanceOut.s[0]
EXPECT_EQ(kExpectedValues[0], *ptr);
EXPECT_EQ(kExpectedValues[1], *(ptr + 1));
EXPECT_EQ(kExpectedValues[2], *(ptr + 4));
EXPECT_EQ(kExpectedValues[3], *(ptr + 8));
// instanceOut.s[1]
ptr += kStructureStride / kBytesPerComponent;
EXPECT_EQ(kExpectedValues[0], *ptr);
EXPECT_EQ(kExpectedValues[1], *(ptr + 1));
EXPECT_EQ(kExpectedValues[2], *(ptr + 4));
EXPECT_EQ(kExpectedValues[3], *(ptr + 8));
// instanceOut.lastData
ptr += kStructureStride / kBytesPerComponent;
EXPECT_EQ(kExpectedValues[4], *(ptr));
EXPECT_GL_NO_ERROR();
}
// Test that access/write to array of array structure data in shader storage buffer.
TEST_P(ShaderStorageBufferTest31, ShaderStorageBufferStructureArrayOfArray)
{
constexpr char kComputeShaderSource[] =
R"(#version 310 es
layout(local_size_x=1, local_size_y=1, local_size_z=1) in;
struct S
{
uvec2 uvData;
uint uiData[2];
};
layout(std140, binding = 0) buffer blockIn {
S s[3][2];
uint lastData;
} instanceIn;
layout(std140, binding = 1) buffer blockOut {
S s[3][2];
uint lastData;
} instanceOut;
void main()
{
instanceOut.s[1][0].uvData = instanceIn.s[1][0].uvData;
instanceOut.s[1][0].uiData[0] = instanceIn.s[1][0].uiData[0];
instanceOut.s[1][0].uiData[1] = instanceIn.s[1][0].uiData[1];
instanceOut.s[1][1].uvData = instanceIn.s[1][1].uvData;
instanceOut.s[1][1].uiData[0] = instanceIn.s[1][1].uiData[0];
instanceOut.s[1][1].uiData[1] = instanceIn.s[1][1].uiData[1];
instanceOut.lastData = instanceIn.lastData;
}
)";
ANGLE_GL_COMPUTE_PROGRAM(program, kComputeShaderSource);
glUseProgram(program);
std::array<GLuint, 4> kUVData = {{
1u, 2u, 0u, 0u,
}};
std::array<GLuint, 8> kUIData = {{
3u, 0u, 0u, 0u, 4u, 0u, 0u, 0u,
}};
GLuint kLastData = 5u;
constexpr unsigned int kBytesPerComponent = sizeof(GLuint);
constexpr unsigned int kStructureStride = 48;
constexpr unsigned int kStructureArrayDimension0 = 3;
constexpr unsigned int kStructureArrayDimension1 = 2;
constexpr unsigned int kLastDataOffset =
kStructureStride * kStructureArrayDimension0 * kStructureArrayDimension1;
constexpr unsigned int totalSize = kLastDataOffset + sizeof(kLastData);
GLBuffer shaderStorageBuffer[2];
glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[0]);
glBufferData(GL_SHADER_STORAGE_BUFFER, totalSize, nullptr, GL_STATIC_DRAW);
// offset of instanceIn.s[1][0]
GLint offset = kStructureStride * (kStructureArrayDimension1 * 1 + 0);
GLuint uintOffset = offset / kBytesPerComponent;
// upload data to instanceIn.s[1][0]
glBufferSubData(GL_SHADER_STORAGE_BUFFER, offset, kUVData.size() * kBytesPerComponent,
kUVData.data());
offset += (kUVData.size() * kBytesPerComponent);
glBufferSubData(GL_SHADER_STORAGE_BUFFER, offset, kUIData.size() * kBytesPerComponent,
kUIData.data());
offset += (kUIData.size() * kBytesPerComponent);
// upload data to instanceIn.s[1][1]
glBufferSubData(GL_SHADER_STORAGE_BUFFER, offset, kUVData.size() * kBytesPerComponent,
kUVData.data());
offset += (kUVData.size() * kBytesPerComponent);
glBufferSubData(GL_SHADER_STORAGE_BUFFER, offset, kUIData.size() * kBytesPerComponent,
kUIData.data());
// upload data to instanceIn.lastData
glBufferSubData(GL_SHADER_STORAGE_BUFFER, kLastDataOffset, sizeof(kLastData), &kLastData);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[1]);
glBufferData(GL_SHADER_STORAGE_BUFFER, totalSize, 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();
// Read back shader storage buffer
glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[1]);
constexpr float kExpectedValues[5] = {1u, 2u, 3u, 4u, 5u};
const GLuint *ptr = reinterpret_cast<const GLuint *>(
glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, totalSize, GL_MAP_READ_BIT));
// instanceOut.s[0][0]
EXPECT_EQ(kExpectedValues[0], *(ptr + uintOffset));
EXPECT_EQ(kExpectedValues[1], *(ptr + uintOffset + 1));
EXPECT_EQ(kExpectedValues[2], *(ptr + uintOffset + 4));
EXPECT_EQ(kExpectedValues[3], *(ptr + uintOffset + 8));
// instanceOut.s[0][1]
EXPECT_EQ(kExpectedValues[0], *(ptr + uintOffset + 12));
EXPECT_EQ(kExpectedValues[1], *(ptr + uintOffset + 13));
EXPECT_EQ(kExpectedValues[2], *(ptr + uintOffset + 16));
EXPECT_EQ(kExpectedValues[3], *(ptr + uintOffset + 20));
// instanceOut.lastData
EXPECT_EQ(kExpectedValues[4], *(ptr + (kLastDataOffset / kBytesPerComponent)));
EXPECT_GL_NO_ERROR();
}
// Test atomic memory functions. // Test atomic memory functions.
TEST_P(ShaderStorageBufferTest31, AtomicMemoryFunctions) TEST_P(ShaderStorageBufferTest31, AtomicMemoryFunctions)
{ {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment