Commit 6494c415 by Qin Jiajia Committed by Commit Bot

ES31: Add swizzle support in SSBO (Part 1)

This patch adds the swizzle process if the swizzle node is the last node in ssbo access chain. Bug: angleproject:1951 Change-Id: Iecc95baa45e8cc40be9111a15398c7e858bfb99e Reviewed-on: https://chromium-review.googlesource.com/c/1341234 Commit-Queue: Jiajia Qin <jiajia.qin@intel.com> Reviewed-by: 's avatarGeoff Lang <geofflang@chromium.org>
parent 471b8d4c
......@@ -420,12 +420,13 @@ class TIntermSwizzle : public TIntermExpression
TIntermTyped *getOperand() { return mOperand; }
void writeOffsetsAsXYZW(TInfoSinkBase *out) const;
const TVector<int> &getSwizzleOffsets() { return mSwizzleOffsets; }
bool hasDuplicateOffsets() const;
void setHasFoldedDuplicateOffsets(bool hasFoldedDuplicateOffsets);
bool offsetsMatch(int offset) const;
TIntermTyped *fold(TDiagnostics *diagnostics) override;
protected:
TIntermTyped *mOperand;
TVector<int> mSwizzleOffsets;
......@@ -574,8 +575,7 @@ class TIntermAggregate : public TIntermOperator, public TIntermAggregateBase
// This covers all built-in function calls - whether they are associated with an op or not.
static TIntermAggregate *CreateBuiltInFunctionCall(const TFunction &func,
TIntermSequence *arguments);
static TIntermAggregate *CreateConstructor(const TType &type,
TIntermSequence *arguments);
static TIntermAggregate *CreateConstructor(const TType &type, TIntermSequence *arguments);
~TIntermAggregate() {}
// Note: only supported for nodes that can be a part of an expression.
......@@ -760,6 +760,7 @@ class TIntermDeclaration : public TIntermNode, public TIntermAggregateBase
TIntermSequence *getSequence() override { return &mDeclarators; }
const TIntermSequence *getSequence() const override { return &mDeclarators; }
protected:
TIntermSequence mDeclarators;
};
......
......@@ -8,6 +8,7 @@
#include "compiler/translator/ShaderStorageBlockFunctionHLSL.h"
#include "common/utilities.h"
#include "compiler/translator/UtilsHLSL.h"
#include "compiler/translator/blocklayout.h"
#include "compiler/translator/blocklayoutHLSL.h"
......@@ -41,20 +42,29 @@ void ShaderStorageBlockFunctionHLSL::OutputSSBOLoadFunctionBody(
return;
}
size_t bytesPerComponent =
gl::VariableComponentSize(gl::VariableComponentType(GLVariableType(ssboFunction.type)));
out << " " << ssboFunction.typeString << " result";
if (ssboFunction.type.isScalar())
{
out << " = " << convertString << "buffer.Load(loc));\n";
size_t offset = ssboFunction.swizzleOffsets[0] * bytesPerComponent;
out << " = " << convertString << "buffer.Load(loc + " << offset << "));\n ";
}
else if (ssboFunction.type.isVector())
{
if (ssboFunction.rowMajor)
if (ssboFunction.rowMajor || !ssboFunction.isDefaultSwizzle)
{
size_t componentStride = bytesPerComponent;
if (ssboFunction.rowMajor)
{
componentStride = ssboFunction.matrixStride;
}
out << " = {";
for (int index = 0; index < ssboFunction.type.getNominalSize(); index++)
for (const int offset : ssboFunction.swizzleOffsets)
{
out << convertString << "buffer.Load(loc + " << index * ssboFunction.matrixStride
<< ")),";
size_t offsetInBytes = offset * componentStride;
out << convertString << "buffer.Load(loc + " << offsetInBytes << ")),";
}
out << "};\n";
}
......@@ -105,43 +115,50 @@ void ShaderStorageBlockFunctionHLSL::OutputSSBOStoreFunctionBody(
TInfoSinkBase &out,
const ShaderStorageBlockFunction &ssboFunction)
{
size_t bytesPerComponent =
gl::VariableComponentSize(gl::VariableComponentType(GLVariableType(ssboFunction.type)));
if (ssboFunction.type.isScalar())
{
size_t offset = ssboFunction.swizzleOffsets[0] * bytesPerComponent;
if (ssboFunction.type.getBasicType() == EbtBool)
{
out << " uint _tmp = uint(value);\n"
<< " buffer.Store(loc, _tmp);\n";
out << " buffer.Store(loc + " << offset << ", uint(value));\n";
}
else
{
out << " buffer.Store(loc, asuint(value));\n";
out << " buffer.Store(loc + " << offset << ", asuint(value));\n";
}
}
else if (ssboFunction.type.isVector())
{
if (ssboFunction.rowMajor)
out << " uint" << ssboFunction.type.getNominalSize() << " _value;\n";
if (ssboFunction.type.getBasicType() == EbtBool)
{
for (int index = 0; index < ssboFunction.type.getNominalSize(); index++)
{
// Don't need to worry about bool value since there is no bool matrix.
out << "buffer.Store(loc + " << index * ssboFunction.matrixStride
<< ", asuint(value[" << index << "]));\n";
}
out << " _value = uint" << ssboFunction.type.getNominalSize() << "(value);\n";
}
else
{
if (ssboFunction.type.getBasicType() == EbtBool)
out << " _value = asuint(value);\n";
}
if (ssboFunction.rowMajor || !ssboFunction.isDefaultSwizzle)
{
size_t componentStride = bytesPerComponent;
if (ssboFunction.rowMajor)
{
out << " uint" << ssboFunction.type.getNominalSize() << " _tmp = uint"
<< ssboFunction.type.getNominalSize() << "(value);\n";
out << " buffer.Store" << ssboFunction.type.getNominalSize() << "(loc, _tmp);\n";
componentStride = ssboFunction.matrixStride;
}
else
const TVector<int> &swizzleOffsets = ssboFunction.swizzleOffsets;
for (int index = 0; index < static_cast<int>(swizzleOffsets.size()); index++)
{
out << " buffer.Store" << ssboFunction.type.getNominalSize()
<< "(loc, asuint(value));\n";
size_t offsetInBytes = swizzleOffsets[index] * componentStride;
out << "buffer.Store(loc + " << offsetInBytes << ", _value[" << index << "]);\n";
}
}
else
{
out << " buffer.Store" << ssboFunction.type.getNominalSize() << "(loc, _value);\n";
}
}
else if (ssboFunction.type.isMatrix())
{
......@@ -175,8 +192,7 @@ void ShaderStorageBlockFunctionHLSL::OutputSSBOStoreFunctionBody(
bool ShaderStorageBlockFunctionHLSL::ShaderStorageBlockFunction::operator<(
const ShaderStorageBlockFunction &rhs) const
{
return std::tie(functionName, typeString, method) <
std::tie(rhs.functionName, rhs.typeString, rhs.method);
return functionName < rhs.functionName;
}
TString ShaderStorageBlockFunctionHLSL::registerShaderStorageBlockFunction(
......@@ -184,12 +200,34 @@ TString ShaderStorageBlockFunctionHLSL::registerShaderStorageBlockFunction(
SSBOMethod method,
TLayoutBlockStorage storage,
bool rowMajor,
int matrixStride)
int matrixStride,
TIntermSwizzle *swizzleNode)
{
ShaderStorageBlockFunction ssboFunction;
ssboFunction.typeString = TypeString(type);
ssboFunction.method = method;
ssboFunction.type = type;
if (swizzleNode != nullptr)
{
ssboFunction.swizzleOffsets = swizzleNode->getSwizzleOffsets();
ssboFunction.isDefaultSwizzle = false;
}
else
{
if (ssboFunction.type.getNominalSize() > 1)
{
for (int index = 0; index < ssboFunction.type.getNominalSize(); index++)
{
ssboFunction.swizzleOffsets.push_back(index);
}
}
else
{
ssboFunction.swizzleOffsets.push_back(0);
}
ssboFunction.isDefaultSwizzle = true;
}
ssboFunction.rowMajor = rowMajor;
ssboFunction.matrixStride = matrixStride;
ssboFunction.functionName =
......@@ -215,6 +253,28 @@ TString ShaderStorageBlockFunctionHLSL::registerShaderStorageBlockFunction(
{
ssboFunction.functionName += "_cm_";
}
for (const int offset : ssboFunction.swizzleOffsets)
{
switch (offset)
{
case 0:
ssboFunction.functionName += "x";
break;
case 1:
ssboFunction.functionName += "y";
break;
case 2:
ssboFunction.functionName += "z";
break;
case 3:
ssboFunction.functionName += "w";
break;
default:
UNREACHABLE();
}
}
mRegisteredShaderStorageBlockFunctions.insert(ssboFunction);
return ssboFunction.functionName;
}
......
......@@ -34,6 +34,7 @@
namespace sh
{
class TIntermSwizzle;
enum class SSBOMethod
{
LOAD,
......@@ -47,7 +48,8 @@ class ShaderStorageBlockFunctionHLSL final : angle::NonCopyable
SSBOMethod method,
TLayoutBlockStorage storage,
bool rowMajor,
int matrixStride);
int matrixStride,
TIntermSwizzle *node);
void shaderStorageBlockFunctionHeader(TInfoSinkBase &out);
......@@ -59,9 +61,10 @@ class ShaderStorageBlockFunctionHLSL final : angle::NonCopyable
TString typeString;
SSBOMethod method;
TType type;
TLayoutBlockStorage storage;
bool rowMajor;
int matrixStride;
TVector<int> swizzleOffsets;
bool isDefaultSwizzle;
};
static void OutputSSBOLoadFunctionBody(TInfoSinkBase &out,
......
......@@ -323,42 +323,53 @@ void ShaderStorageBlockOutputHLSL::outputLoadFunctionCall(TIntermTyped *node)
traverseSSBOAccess(node, SSBOMethod::LOAD);
}
void ShaderStorageBlockOutputHLSL::traverseSSBOAccess(TIntermTyped *node, SSBOMethod method)
// 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.
void ShaderStorageBlockOutputHLSL::setMatrixStride(TIntermTyped *node,
TLayoutBlockStorage storage,
bool rowMajor)
{
mMatrixStride = 0;
mRowMajor = false;
// Note that we don't have correct BlockMemberInfo from mBlockMemberInfoMap at the current
// point. But we must use those information to generate the right function name. So here we have
// to calculate them again.
TLayoutBlockStorage storage;
bool rowMajor;
GetBlockLayoutInfo(node, false, &storage, &rowMajor);
if (node->getType().isMatrix())
{
mMatrixStride = GetMatrixStride(node->getType(), storage, rowMajor);
mRowMajor = rowMajor;
return;
}
// 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
// lost the matrix type info once we enter ShaderStorageBlockFunctionHLSL.
if (node->getType().isVector())
{
TIntermBinary *binaryNode = node->getAsBinaryNode();
if (binaryNode)
{
const TType &leftType = binaryNode->getLeft()->getType();
if (leftType.isMatrix())
return setMatrixStride(binaryNode->getLeft(), storage, rowMajor);
}
else
{
TIntermSwizzle *swizzleNode = node->getAsSwizzleNode();
if (swizzleNode)
{
mMatrixStride = GetMatrixStride(leftType, storage, rowMajor);
mRowMajor = rowMajor;
return setMatrixStride(swizzleNode->getOperand(), storage, rowMajor);
}
}
}
else if (node->getType().isMatrix())
{
mMatrixStride = GetMatrixStride(node->getType(), storage, rowMajor);
mRowMajor = rowMajor;
}
}
void ShaderStorageBlockOutputHLSL::traverseSSBOAccess(TIntermTyped *node, SSBOMethod method)
{
mMatrixStride = 0;
mRowMajor = false;
// Note that we don't have correct BlockMemberInfo from mBlockMemberInfoMap at the current
// point. But we must use those information to generate the right function name. So here we have
// to calculate them again.
TLayoutBlockStorage storage;
bool rowMajor;
GetBlockLayoutInfo(node, false, &storage, &rowMajor);
setMatrixStride(node, storage, rowMajor);
const TString &functionName = mSSBOFunctionHLSL->registerShaderStorageBlockFunction(
node->getType(), method, storage, mRowMajor, mMatrixStride);
node->getType(), method, storage, mRowMajor, mMatrixStride, node->getAsSwizzleNode());
TInfoSinkBase &out = mOutputHLSL->getInfoSink();
out << functionName;
out << "(";
......@@ -475,8 +486,9 @@ bool ShaderStorageBlockOutputHLSL::visitSwizzle(Visit visit, TIntermSwizzle *nod
}
TInfoSinkBase &out = mOutputHLSL->getInfoSink();
// TODO(jiajia.qin@intel.com): add swizzle process.
if (mIsLoadFunctionCall)
// 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())
{
out << ")";
}
......
......@@ -63,6 +63,7 @@ class ShaderStorageBlockOutputHLSL : public TIntermTraverser
private:
void traverseSSBOAccess(TIntermTyped *node, SSBOMethod method);
void setMatrixStride(TIntermTyped *node, TLayoutBlockStorage storage, bool rowMajor);
bool isEndOfSSBOAccessChain();
void writeEOpIndexDirectOrIndirectOutput(TInfoSinkBase &out, Visit visit, TIntermBinary *node);
// Common part in dot operations.
......
......@@ -37,6 +37,25 @@ struct MatrixCase
const unsigned int kBytesPerComponent = sizeof(float);
};
struct VectorCase
{
VectorCase(unsigned components,
const char *computeShaderSource,
const GLuint *inputData,
const GLuint *expectedData)
: mComponents(components),
mComputeShaderSource(computeShaderSource),
mInputdata(inputData),
mExpectedData(expectedData)
{
}
unsigned int mComponents;
const char *mComputeShaderSource;
const GLuint *mInputdata;
const GLuint *mExpectedData;
const unsigned int kBytesPerComponent = sizeof(GLuint);
};
class ShaderStorageBufferTest31 : public ANGLETest
{
protected:
......@@ -90,6 +109,42 @@ class ShaderStorageBufferTest31 : public ANGLETest
EXPECT_GL_NO_ERROR();
}
void runVectorTest(const VectorCase &vectorCase)
{
ANGLE_GL_COMPUTE_PROGRAM(program, vectorCase.mComputeShaderSource);
glUseProgram(program);
// Create shader storage buffer
GLBuffer shaderStorageBuffer[2];
glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[0]);
glBufferData(GL_SHADER_STORAGE_BUFFER,
vectorCase.mComponents * vectorCase.kBytesPerComponent, vectorCase.mInputdata,
GL_STATIC_DRAW);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[1]);
glBufferData(GL_SHADER_STORAGE_BUFFER,
vectorCase.mComponents * vectorCase.kBytesPerComponent, 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]);
const GLuint *ptr = reinterpret_cast<const GLuint *>(glMapBufferRange(
GL_SHADER_STORAGE_BUFFER, 0, vectorCase.mComponents * vectorCase.kBytesPerComponent,
GL_MAP_READ_BIT));
for (unsigned int idx = 0; idx < vectorCase.mComponents; idx++)
{
EXPECT_EQ(vectorCase.mExpectedData[idx], *(ptr + idx));
}
EXPECT_GL_NO_ERROR();
}
};
// Matched block names within a shader interface must match in terms of having the same number of
......@@ -272,40 +327,126 @@ TEST_P(ShaderStorageBufferTest31, ShaderStorageBufferVector)
}
)";
ANGLE_GL_COMPUTE_PROGRAM(program, kComputeShaderSource);
constexpr unsigned int kComponentCount = 2;
constexpr GLuint kInputValues[kComponentCount] = {3u, 4u};
glUseProgram(program.get());
VectorCase vectorCase(kComponentCount, kComputeShaderSource, kInputValues, kInputValues);
runVectorTest(vectorCase);
}
constexpr unsigned int kComponentCount = 2;
constexpr unsigned int kBytesPerComponent = sizeof(unsigned int);
constexpr unsigned int kExpectedValues[kComponentCount] = {3u, 4u};
// Create shader storage buffer
GLBuffer shaderStorageBuffer[2];
glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[0]);
glBufferData(GL_SHADER_STORAGE_BUFFER, kComponentCount * kBytesPerComponent, kExpectedValues,
GL_STATIC_DRAW);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[1]);
glBufferData(GL_SHADER_STORAGE_BUFFER, kComponentCount * kBytesPerComponent, nullptr,
GL_STATIC_DRAW);
// Test that access/write to swizzle scalar data in shader storage block.
TEST_P(ShaderStorageBufferTest31, ScalarSwizzleTest)
{
constexpr char kComputeShaderSource[] =
R"(#version 310 es
layout(local_size_x=1, local_size_y=1, local_size_z=1) in;
layout(std140, binding = 0) buffer blockIn {
uvec2 data;
} instanceIn;
layout(std140, binding = 1) buffer blockOut {
uvec2 data;
} instanceOut;
void main()
{
instanceOut.data.x = instanceIn.data.y;
instanceOut.data.y = instanceIn.data.x;
}
)";
// Bind shader storage buffer
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, shaderStorageBuffer[0]);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, shaderStorageBuffer[1]);
constexpr unsigned int kComponentCount = 2;
constexpr GLuint kInputValues[kComponentCount] = {3u, 4u};
constexpr GLuint kExpectedValues[kComponentCount] = {4u, 3u};
glDispatchCompute(1, 1, 1);
VectorCase vectorCase(kComponentCount, kComputeShaderSource, kInputValues, kExpectedValues);
runVectorTest(vectorCase);
}
glFinish();
// Test that access/write to swizzle vector data in shader storage block.
TEST_P(ShaderStorageBufferTest31, VectorSwizzleTest)
{
constexpr char kComputeShaderSource[] =
R"(#version 310 es
layout(local_size_x=1, local_size_y=1, local_size_z=1) in;
layout(std140, binding = 0) buffer blockIn {
uvec2 data;
} instanceIn;
layout(std140, binding = 1) buffer blockOut {
uvec2 data;
} instanceOut;
void main()
{
instanceOut.data.yx = instanceIn.data.xy;
}
)";
// Read back shader storage buffer
glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[1]);
const GLuint *ptr = reinterpret_cast<const GLuint *>(glMapBufferRange(
GL_SHADER_STORAGE_BUFFER, 0, kComponentCount * kBytesPerComponent, GL_MAP_READ_BIT));
for (unsigned int idx = 0; idx < kComponentCount; idx++)
{
EXPECT_EQ(kExpectedValues[idx], *(ptr + idx));
}
constexpr unsigned int kComponentCount = 2;
constexpr GLuint kInputValues[kComponentCount] = {3u, 4u};
constexpr GLuint kExpectedValues[kComponentCount] = {4u, 3u};
EXPECT_GL_NO_ERROR();
VectorCase vectorCase(kComponentCount, kComputeShaderSource, kInputValues, kExpectedValues);
runVectorTest(vectorCase);
}
// Test that access/write to swizzle vector data in column_major matrix in shader storage block.
TEST_P(ShaderStorageBufferTest31, VectorSwizzleInColumnMajorMatrixTest)
{
constexpr char kComputeShaderSource[] =
R"(#version 310 es
layout(local_size_x=1, local_size_y=1, local_size_z=1) in;
layout(std140, binding = 0) buffer blockIn {
layout(column_major) mat2x3 data;
} instanceIn;
layout(std140, binding = 1) buffer blockOut {
layout(column_major) mat2x3 data;
} instanceOut;
void main()
{
instanceOut.data[0].xyz = instanceIn.data[0].xyz;
instanceOut.data[1].xyz = instanceIn.data[1].xyz;
}
)";
constexpr unsigned int kColumns = 2;
constexpr unsigned int kRows = 3;
constexpr unsigned int kBytesPerComponent = sizeof(float);
constexpr unsigned int kMatrixStride = 16;
constexpr float kInputDada[kColumns * (kMatrixStride / kBytesPerComponent)] = {
0.1, 0.2, 0.3, 0.0, 0.4, 0.5, 0.6, 0.0};
MatrixCase matrixCase(kRows, kColumns, kMatrixStride, kComputeShaderSource, kInputDada);
runMatrixTest(matrixCase);
}
// Test that access/write to swizzle vector data in row_major matrix in shader storage block.
TEST_P(ShaderStorageBufferTest31, VectorSwizzleInRowMajorMatrixTest)
{
ANGLE_SKIP_TEST_IF(IsAndroid());
constexpr char kComputeShaderSource[] =
R"(#version 310 es
layout(local_size_x=1, local_size_y=1, local_size_z=1) in;
layout(std140, binding = 0) buffer blockIn {
layout(row_major) mat2x3 data;
} instanceIn;
layout(std140, binding = 1) buffer blockOut {
layout(row_major) mat2x3 data;
} instanceOut;
void main()
{
instanceOut.data[0].xyz = instanceIn.data[0].xyz;
instanceOut.data[1].xyz = instanceIn.data[1].xyz;
}
)";
constexpr unsigned int kColumns = 2;
constexpr unsigned int kRows = 3;
constexpr unsigned int kBytesPerComponent = sizeof(float);
// std140 layout requires that base alignment and stride of arrays of scalars and vectors are
// rounded up a multiple of the base alignment of a vec4.
constexpr unsigned int kMatrixStride = 16;
constexpr float kInputDada[kRows * (kMatrixStride / kBytesPerComponent)] = {
0.1, 0.2, 0.0, 0.0, 0.3, 0.4, 0.0, 0.0, 0.5, 0.6, 0.0, 0.0};
MatrixCase matrixCase(kColumns, kRows, kMatrixStride, kComputeShaderSource, kInputDada);
runMatrixTest(matrixCase);
}
// Test that access/write to scalar data in matrix in shader storage block with row major.
......
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