Commit ed049ab4 by Olli Etuaho Committed by Commit Bot

HLSL: Fix handling arrays of structs in interface blocks

In HLSL output, structs in interface blocks are not accessed directly. Rather they get copied from the D3D constant buffer to static structs in the shader header. Fix generating the copy/init code in the header to handle arrays of structs correctly. BUG=angleproject:2084 TEST=angle_end2end_tests Change-Id: If66bd5be3f3570ba591b8b62c5284c06fc83dd45 Reviewed-on: https://chromium-review.googlesource.com/608448Reviewed-by: 's avatarCorentin Wallez <cwallez@chromium.org> Reviewed-by: 's avatarGeoff Lang <geofflang@chromium.org> Commit-Queue: Olli Etuaho <oetuaho@nvidia.com>
parent 47bb4933
......@@ -249,45 +249,58 @@ int OutputHLSL::vectorSize(const TType &type) const
return elementSize * arraySize;
}
TString OutputHLSL::structInitializerString(int indent,
const TStructure &structure,
const TString &rhsStructName)
TString OutputHLSL::structInitializerString(int indent, const TType &type, const TString &name)
{
TString init;
TString preIndentString;
TString fullIndentString;
for (int spaces = 0; spaces < (indent * 4); spaces++)
{
preIndentString += ' ';
}
for (int spaces = 0; spaces < ((indent + 1) * 4); spaces++)
TString indentString;
for (int spaces = 0; spaces < indent; spaces++)
{
fullIndentString += ' ';
indentString += " ";
}
init += preIndentString + "{\n";
const TFieldList &fields = structure.fields();
for (unsigned int fieldIndex = 0; fieldIndex < fields.size(); fieldIndex++)
if (type.isArray())
{
const TField &field = *fields[fieldIndex];
const TString &fieldName = rhsStructName + "." + Decorate(field.name());
const TType &fieldType = *field.type();
if (fieldType.getStruct())
init += indentString + "{\n";
for (unsigned int arrayIndex = 0u; arrayIndex < type.getArraySize(); ++arrayIndex)
{
init += structInitializerString(indent + 1, *fieldType.getStruct(), fieldName);
TStringStream indexedString;
indexedString << name << "[" << arrayIndex << "]";
TType elementType = type;
elementType.clearArrayness();
init += structInitializerString(indent + 1, elementType, indexedString.str());
if (arrayIndex < type.getArraySize() - 1)
{
init += ",";
}
init += "\n";
}
else
init += indentString + "}";
}
else if (type.getBasicType() == EbtStruct)
{
init += indentString + "{\n";
const TStructure &structure = *type.getStruct();
const TFieldList &fields = structure.fields();
for (unsigned int fieldIndex = 0; fieldIndex < fields.size(); fieldIndex++)
{
init += fullIndentString + fieldName + ",\n";
const TField &field = *fields[fieldIndex];
const TString &fieldName = name + "." + Decorate(field.name());
const TType &fieldType = *field.type();
init += structInitializerString(indent + 1, fieldType, fieldName);
if (fieldIndex < fields.size() - 1)
{
init += ",";
}
init += "\n";
}
init += indentString + "}";
}
else
{
init += indentString + name;
}
init += preIndentString + "}" + (indent == 0 ? ";" : ",") + "\n";
return init;
}
......@@ -307,9 +320,14 @@ void OutputHLSL::header(TInfoSinkBase &out, const BuiltInFunctionEmulator *built
const TStructure &structure = *structNode->getType().getStruct();
const TString &originalName = mFlaggedStructOriginalNames[structNode];
flaggedStructs += "static " + Decorate(structure.name()) + " " + mappedName + " =\n";
flaggedStructs += structInitializerString(0, structure, originalName);
flaggedStructs += "\n";
flaggedStructs += "static " + Decorate(structure.name()) + " " + mappedName;
if (structNode->isArray())
{
flaggedStructs += ArrayString(structNode->getType());
}
flaggedStructs += " =\n";
flaggedStructs += structInitializerString(0, structNode->getType(), originalName);
flaggedStructs += ";\n";
}
for (ReferencedSymbols::const_iterator varying = mReferencedVaryings.begin();
......
......@@ -197,9 +197,7 @@ class OutputHLSL : public TIntermTraverser
TIntermSymbol *mExcessiveLoopIndex;
TString structInitializerString(int indent,
const TStructure &structure,
const TString &rhsStructName);
TString structInitializerString(int indent, const TType &type, const TString &name);
std::map<TIntermTyped *, TString> mFlaggedStructMappedNames;
std::map<TIntermTyped *, TString> mFlaggedStructOriginalNames;
......
......@@ -29,30 +29,16 @@ class UniformBufferTest : public ANGLETest
{
ANGLETest::SetUp();
const std::string vertexShaderSource = SHADER_SOURCE
( #version 300 es\n
in vec4 position;
void main()
{
gl_Position = position;
}
);
const std::string fragmentShaderSource = SHADER_SOURCE
( #version 300 es\n
precision highp float;
uniform uni {
vec4 color;
};
out vec4 fragColor;
void main()
{
fragColor = color;
}
);
mProgram = CompileProgram(vertexShaderSource, fragmentShaderSource);
mVertexShaderSource = SHADER_SOURCE(#version 300 es\n in vec4 position;
void main() { gl_Position = position; });
mFragmentShaderSource =
SHADER_SOURCE(#version 300 es\n precision highp float; uniform uni { vec4 color; };
out vec4 fragColor;
void main() { fragColor = color; });
mProgram = CompileProgram(mVertexShaderSource, mFragmentShaderSource);
ASSERT_NE(mProgram, 0u);
mUniformBufferIndex = glGetUniformBlockIndex(mProgram, "uni");
......@@ -70,6 +56,8 @@ class UniformBufferTest : public ANGLETest
ANGLETest::TearDown();
}
std::string mVertexShaderSource;
std::string mFragmentShaderSource;
GLuint mProgram;
GLint mUniformBufferIndex;
GLuint mUniformBuffer;
......@@ -118,16 +106,15 @@ TEST_P(UniformBufferTest, UniformBufferRange)
// Let's create a buffer which contains two vec4.
GLuint vec4Size = 4 * sizeof(float);
GLuint stride = 0;
GLuint stride = 0;
do
{
stride += alignment;
}
while (stride < vec4Size);
} while (stride < vec4Size);
std::vector<char> v(2 * stride);
float *first = reinterpret_cast<float*>(v.data());
float *second = reinterpret_cast<float*>(v.data() + stride);
float *first = reinterpret_cast<float *>(v.data());
float *second = reinterpret_cast<float *>(v.data() + stride);
first[0] = 10.f / 255.f;
first[1] = 20.f / 255.f;
......@@ -156,10 +143,9 @@ TEST_P(UniformBufferTest, UniformBufferRange)
EXPECT_PIXEL_EQ(px, py, 10, 20, 30, 40);
// Bind the second part of the uniform buffer and draw
// Furthermore the D3D11.1 backend will internally round the vec4Size (16 bytes) to a stride (256 bytes)
// hence it will try to map the range [stride, 2 * stride] which is
// out-of-bound of the buffer bufferSize = stride + vec4Size < 2 * stride.
// Ensure that this behaviour works.
// Furthermore the D3D11.1 backend will internally round the vec4Size (16 bytes) to a stride
// (256 bytes) hence it will try to map the range [stride, 2 * stride] which is out-of-bound of
// the buffer bufferSize = stride + vec4Size < 2 * stride. Ensure that this behaviour works.
glBindBufferRange(GL_UNIFORM_BUFFER, 0, mUniformBuffer, stride, vec4Size);
drawQuad(mProgram, "position", 0.5f);
EXPECT_GL_NO_ERROR();
......@@ -177,7 +163,7 @@ TEST_P(UniformBufferTest, UniformBufferBindings)
// Let's create a buffer which contains one vec4.
GLuint vec4Size = 4 * sizeof(float);
std::vector<char> v(vec4Size);
float *first = reinterpret_cast<float*>(v.data());
float *first = reinterpret_cast<float *>(v.data());
first[0] = 10.f / 255.f;
first[1] = 20.f / 255.f;
......@@ -210,7 +196,8 @@ TEST_P(UniformBufferTest, UniformBufferBindings)
}
// Test that ANGLE handles used but unbound UBO.
// TODO: A test case shouldn't depend on the error code of an undefined behaviour. Move this to unit tests of the validation layer.
// TODO: A test case shouldn't depend on the error code of an undefined behaviour. Move this to unit
// tests of the validation layer.
TEST_P(UniformBufferTest, UnboundUniformBuffer)
{
glUniformBlockBinding(mProgram, mUniformBufferIndex, 0);
......@@ -285,18 +272,17 @@ TEST_P(UniformBufferTest, ManyUniformBufferRange)
// Let's create a buffer which contains eight vec4.
GLuint vec4Size = 4 * sizeof(float);
GLuint stride = 0;
GLuint stride = 0;
do
{
stride += alignment;
}
while (stride < vec4Size);
} while (stride < vec4Size);
std::vector<char> v(8 * stride);
for (size_t i = 0; i < 8; ++i)
{
float *data = reinterpret_cast<float*>(v.data() + i * stride);
float *data = reinterpret_cast<float *>(v.data() + i * stride);
data[0] = (i + 10.f) / 255.f;
data[1] = (i + 20.f) / 255.f;
......@@ -823,6 +809,103 @@ TEST_P(UniformBufferTest31, BindingMustBeBothSpecified)
ASSERT_EQ(0u, program);
}
// Test with a block containing an array of structs.
TEST_P(UniformBufferTest, BlockContainingArrayOfStructs)
{
const std::string &fragmentShader =
"#version 300 es\n"
"precision highp float;\n"
"out vec4 my_FragColor;\n"
"struct light_t {\n"
" vec4 intensity;\n"
"};\n"
"const int maxLights = 2;\n"
"layout(std140) uniform lightData { light_t lights[maxLights]; };\n"
"vec4 processLight(vec4 lighting, light_t light)\n"
"{\n"
" return lighting + light.intensity;\n"
"}\n"
"void main()\n"
"{\n"
" vec4 lighting = vec4(0, 0, 0, 1);\n"
" for (int n = 0; n < maxLights; n++)\n"
" {\n"
" lighting = processLight(lighting, lights[n]);\n"
" }\n"
" my_FragColor = lighting;\n"
"}\n";
ANGLE_GL_PROGRAM(program, mVertexShaderSource, fragmentShader);
GLint uniformBufferIndex = glGetUniformBlockIndex(program, "lightData");
glBindBuffer(GL_UNIFORM_BUFFER, mUniformBuffer);
const GLsizei kStructCount = 2;
const GLsizei kVectorElementCount = 4;
const GLsizei kBytesPerElement = 4;
const GLsizei kDataSize = kStructCount * kVectorElementCount * kBytesPerElement;
std::vector<GLubyte> v(kDataSize, 0);
float *vAsFloat = reinterpret_cast<float *>(v.data());
vAsFloat[1] = 0.5f;
vAsFloat[kVectorElementCount + 1] = 0.5f;
glBufferData(GL_UNIFORM_BUFFER, kDataSize, v.data(), GL_STATIC_DRAW);
glBindBufferBase(GL_UNIFORM_BUFFER, 0, mUniformBuffer);
glUniformBlockBinding(program, uniformBufferIndex, 0);
drawQuad(program.get(), "position", 0.5f);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
}
// Test with a block containing an array of structs containing arrays.
TEST_P(UniformBufferTest, BlockContainingArrayOfStructsContainingArrays)
{
const std::string &fragmentShader =
"#version 300 es\n"
"precision highp float;\n"
"out vec4 my_FragColor;\n"
"struct light_t {\n"
" vec4 intensity[3];\n"
"};\n"
"const int maxLights = 2;\n"
"layout(std140) uniform lightData { light_t lights[maxLights]; };\n"
"vec4 processLight(vec4 lighting, light_t light)\n"
"{\n"
" return lighting + light.intensity[1];\n"
"}\n"
"void main()\n"
"{\n"
" vec4 lighting = vec4(0, 0, 0, 1);\n"
" for (int n = 0; n < maxLights; n++)\n"
" {\n"
" lighting = processLight(lighting, lights[n]);\n"
" }\n"
" my_FragColor = lighting;\n"
"}\n";
ANGLE_GL_PROGRAM(program, mVertexShaderSource, fragmentShader);
GLint uniformBufferIndex = glGetUniformBlockIndex(program, "lightData");
glBindBuffer(GL_UNIFORM_BUFFER, mUniformBuffer);
const GLsizei kStructCount = 2;
const GLsizei kVectorsPerStruct = 3;
const GLsizei kElementsPerVector = 4;
const GLsizei kBytesPerElement = 4;
const GLsizei kDataSize =
kStructCount * kVectorsPerStruct * kElementsPerVector * kBytesPerElement;
std::vector<GLubyte> v(kDataSize, 0);
float *vAsFloat = reinterpret_cast<float *>(v.data());
vAsFloat[kElementsPerVector + 1] = 0.5f;
vAsFloat[kVectorsPerStruct * kElementsPerVector + kElementsPerVector + 1] = 0.5f;
glBufferData(GL_UNIFORM_BUFFER, kDataSize, v.data(), GL_STATIC_DRAW);
glBindBufferBase(GL_UNIFORM_BUFFER, 0, mUniformBuffer);
glUniformBlockBinding(program, uniformBufferIndex, 0);
drawQuad(program.get(), "position", 0.5f);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
}
// Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against.
ANGLE_INSTANTIATE_TEST(UniformBufferTest,
ES3_D3D11(),
......
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