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
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)
{
const TType &fieldType = *field->type();
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())
{
......@@ -96,7 +146,22 @@ void SetShaderStorageBlockMembersOffset(const TInterfaceBlock *interfaceBlock)
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
......@@ -360,23 +425,58 @@ void ShaderStorageBlockOutputHLSL::writeEOpIndexDirectOrIndirectOutput(TInfoSink
if (visit == InVisit)
{
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 =
BlockLayoutEncoder::ComponentsPerRegister * BlockLayoutEncoder::BytesPerComponent;
out << " + " << str(matrixStride);
if (type.isArrayOfArrays())
{
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;
out << " + " << str(scalarStride);
}
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 << " * ";
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
{
out << " + ";
out << field->getArrayStride();
if (fieldType.isArrayOfArrays())
{
out << " * (";
}
}
if (mIsLoadFunctionCall && isEndOfSSBOAccessChain())
{
......
......@@ -172,6 +172,11 @@ BlockMemberInfo BlockLayoutEncoder::encodeType(GLenum type,
return memberInfo;
}
void BlockLayoutEncoder::increaseCurrentOffset(size_t offsetInBytes)
{
mCurrentOffset += (offsetInBytes / BytesPerComponent);
}
// static
size_t BlockLayoutEncoder::getBlockRegister(const BlockMemberInfo &info)
{
......
......@@ -78,6 +78,7 @@ class BlockLayoutEncoder
bool isRowMajorMatrix);
size_t getBlockSize() const { return mCurrentOffset * BytesPerComponent; }
void increaseCurrentOffset(size_t offsetInBytes);
virtual void enterAggregateType() = 0;
virtual void exitAggregateType() = 0;
......
......@@ -313,6 +313,215 @@ void main()
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_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