Commit af713a24 by jchen10 Committed by Commit Bot

ES31: Implement binding layout for uniform blocks

The binding point of uniform blocks can be specified in shaders with this CL. See spec ESSL 3.10, section 4.4.4, page 58 for more info. dEQP-GLES31.functional.ubo.* still can't completely pass as the missing of arrays-of-arrays feature. Neither can dEQP-GLES31.functional.layout_binding.ubo.* due to the incomplete implementation of program interface APIs. TEST=angle_end2end_tests:UniformBufferTest BUG=angleproject:1442 Change-Id: If95d468fc109834a132b9b817730d3fdc3a615da Reviewed-on: https://chromium-review.googlesource.com/483848 Commit-Queue: Jie A Chen <jie.a.chen@intel.com> Commit-Queue: Corentin Wallez <cwallez@chromium.org> Reviewed-by: 's avatarOlli Etuaho <oetuaho@nvidia.com> Reviewed-by: 's avatarCorentin Wallez <cwallez@chromium.org>
parent 0ffc441e
......@@ -380,6 +380,9 @@ struct ShBuiltInResources
// maximum number of buffer object storage in machine units
int MaxAtomicCounterBufferSize;
// maximum number of uniform block bindings
int MaxUniformBufferBindings;
};
//
......
......@@ -216,6 +216,7 @@ struct InterfaceBlock
unsigned int arraySize;
BlockLayoutType layout;
bool isRowMajorLayout;
int binding;
bool staticUse;
std::vector<InterfaceBlockField> fields;
};
......
......@@ -113,6 +113,7 @@ TParseContext::TParseContext(TSymbolTable &symt,
mMaxImageUnits(resources.MaxImageUnits),
mMaxCombinedTextureImageUnits(resources.MaxCombinedTextureImageUnits),
mMaxUniformLocations(resources.MaxUniformLocations),
mMaxUniformBufferBindings(resources.MaxUniformBufferBindings),
mDeclaringFunction(false)
{
mComputeShaderLocalSize.fill(-1);
......@@ -1337,6 +1338,16 @@ void TParseContext::checkSamplerBindingIsValid(const TSourceLoc &location,
}
}
void TParseContext::checkBlockBindingIsValid(const TSourceLoc &location, int binding, int arraySize)
{
int size = (arraySize == 0 ? 1 : arraySize);
if (binding + size > mMaxUniformBufferBindings)
{
error(location, "interface block binding greater than MAX_UNIFORM_BUFFER_BINDINGS",
"binding");
}
}
void TParseContext::checkUniformLocationInRange(const TSourceLoc &location,
int objectLocationCount,
const TLayoutQualifier &layoutQualifier)
......@@ -2771,8 +2782,22 @@ TIntermDeclaration *TParseContext::addInterfaceBlock(
checkMemoryQualifierIsNotSpecified(typeQualifier.memoryQualifier, typeQualifier.line);
// TODO(oetuaho): Remove this and support binding for blocks.
checkBindingIsNotSpecified(typeQualifier.line, typeQualifier.layoutQualifier.binding);
// add array index
unsigned int arraySize = 0;
if (arrayIndex != nullptr)
{
arraySize = checkIsValidArraySize(arrayIndexLine, arrayIndex);
}
if (mShaderVersion < 310)
{
checkBindingIsNotSpecified(typeQualifier.line, typeQualifier.layoutQualifier.binding);
}
else
{
checkBlockBindingIsValid(typeQualifier.line, typeQualifier.layoutQualifier.binding,
arraySize);
}
checkYuvIsNotSpecified(typeQualifier.line, typeQualifier.layoutQualifier.yuv);
......@@ -2832,6 +2857,7 @@ TIntermDeclaration *TParseContext::addInterfaceBlock(
// check layout qualifiers
TLayoutQualifier fieldLayoutQualifier = fieldType->getLayoutQualifier();
checkLocationIsNotSpecified(field->line(), fieldLayoutQualifier);
checkBindingIsNotSpecified(field->line(), fieldLayoutQualifier.binding);
if (fieldLayoutQualifier.blockStorage != EbsUnspecified)
{
......@@ -2853,13 +2879,6 @@ TIntermDeclaration *TParseContext::addInterfaceBlock(
fieldType->setLayoutQualifier(fieldLayoutQualifier);
}
// add array index
unsigned int arraySize = 0;
if (arrayIndex != nullptr)
{
arraySize = checkIsValidArraySize(arrayIndexLine, arrayIndex);
}
TInterfaceBlock *interfaceBlock =
new TInterfaceBlock(&blockName, fieldList, instanceName, arraySize, blockLayoutQualifier);
TType interfaceBlockType(interfaceBlock, typeQualifier.qualifier, blockLayoutQualifier,
......
......@@ -397,6 +397,7 @@ class TParseContext : angle::NonCopyable
void checkBindingIsNotSpecified(const TSourceLoc &location, int binding);
void checkImageBindingIsValid(const TSourceLoc &location, int binding, int arraySize);
void checkSamplerBindingIsValid(const TSourceLoc &location, int binding, int arraySize);
void checkBlockBindingIsValid(const TSourceLoc &location, int binding, int arraySize);
void checkUniformLocationInRange(const TSourceLoc &location,
int objectLocationCount,
......@@ -480,6 +481,7 @@ class TParseContext : angle::NonCopyable
int mMaxImageUnits;
int mMaxCombinedTextureImageUnits;
int mMaxUniformLocations;
int mMaxUniformBufferBindings;
// keeps track whether we are declaring / defining a function
bool mDeclaringFunction;
};
......
......@@ -222,6 +222,8 @@ void InitBuiltInResources(ShBuiltInResources *resources)
resources->MaxFragmentAtomicCounterBuffers = 0;
resources->MaxCombinedAtomicCounterBuffers = 1;
resources->MaxAtomicCounterBufferSize = 32;
resources->MaxUniformBufferBindings = 32;
}
//
......
......@@ -361,7 +361,11 @@ bool Varying::isSameVaryingAtLinkTime(const Varying &other, int shaderVersion) c
}
InterfaceBlock::InterfaceBlock()
: arraySize(0), layout(BLOCKLAYOUT_PACKED), isRowMajorLayout(false), staticUse(false)
: arraySize(0),
layout(BLOCKLAYOUT_PACKED),
isRowMajorLayout(false),
binding(-1),
staticUse(false)
{
}
......@@ -376,6 +380,7 @@ InterfaceBlock::InterfaceBlock(const InterfaceBlock &other)
arraySize(other.arraySize),
layout(other.layout),
isRowMajorLayout(other.isRowMajorLayout),
binding(other.binding),
staticUse(other.staticUse),
fields(other.fields)
{
......@@ -389,6 +394,7 @@ InterfaceBlock &InterfaceBlock::operator=(const InterfaceBlock &other)
arraySize = other.arraySize;
layout = other.layout;
isRowMajorLayout = other.isRowMajorLayout;
binding = other.binding;
staticUse = other.staticUse;
fields = other.fields;
return *this;
......@@ -403,7 +409,7 @@ bool InterfaceBlock::isSameInterfaceBlockAtLinkTime(const InterfaceBlock &other)
{
if (name != other.name || mappedName != other.mappedName || arraySize != other.arraySize ||
layout != other.layout || isRowMajorLayout != other.isRowMajorLayout ||
fields.size() != other.fields.size())
binding != other.binding || fields.size() != other.fields.size())
{
return false;
}
......
......@@ -157,7 +157,8 @@ class TInterfaceBlock : public TFieldListCollection
mInstanceName(instanceName),
mArraySize(arraySize),
mBlockStorage(layoutQualifier.blockStorage),
mMatrixPacking(layoutQualifier.matrixPacking)
mMatrixPacking(layoutQualifier.matrixPacking),
mBinding(layoutQualifier.binding)
{
}
......@@ -167,6 +168,7 @@ class TInterfaceBlock : public TFieldListCollection
int arraySize() const { return mArraySize; }
TLayoutBlockStorage blockStorage() const { return mBlockStorage; }
TLayoutMatrixPacking matrixPacking() const { return mMatrixPacking; }
int blockBinding() const { return mBinding; }
const TString &mangledName() const
{
if (mMangledName.empty())
......@@ -179,6 +181,7 @@ class TInterfaceBlock : public TFieldListCollection
int mArraySize; // 0 if not an array
TLayoutBlockStorage mBlockStorage;
TLayoutMatrixPacking mMatrixPacking;
int mBinding;
};
//
......
......@@ -549,6 +549,7 @@ InterfaceBlock CollectVariables::recordInterfaceBlock(const TIntermSymbol &varia
(blockType->hasInstanceName() ? blockType->instanceName().c_str() : "");
interfaceBlock.arraySize = variable.getArraySize();
interfaceBlock.isRowMajorLayout = (blockType->matrixPacking() == EmpRowMajor);
interfaceBlock.binding = blockType->blockBinding();
interfaceBlock.layout = GetBlockLayoutType(blockType->blockStorage());
// Gather field information
......
......@@ -112,6 +112,8 @@ Compiler::Compiler(rx::GLImplFactory *implFactory, const ContextState &state)
mResources.MaxCombinedAtomicCounterBuffers = caps.maxCombinedAtomicCounterBuffers;
mResources.MaxAtomicCounterBufferSize = caps.maxAtomicCounterBufferSize;
mResources.MaxUniformBufferBindings = caps.maxUniformBufferBindings;
if (state.getClientMajorVersion() == 2 && !extensions.drawBuffers)
{
mResources.MaxDrawBuffers = 1;
......
......@@ -878,6 +878,7 @@ Error Program::loadBinary(const Context *context,
stream.readString(&uniformBlock.name);
stream.readBool(&uniformBlock.isArray);
stream.readInt(&uniformBlock.arrayElement);
stream.readInt(&uniformBlock.binding);
stream.readInt(&uniformBlock.dataSize);
stream.readBool(&uniformBlock.vertexStaticUse);
stream.readBool(&uniformBlock.fragmentStaticUse);
......@@ -1025,6 +1026,7 @@ Error Program::saveBinary(const Context *context,
stream.writeString(uniformBlock.name);
stream.writeInt(uniformBlock.isArray);
stream.writeInt(uniformBlock.arrayElement);
stream.writeInt(uniformBlock.binding);
stream.writeInt(uniformBlock.dataSize);
stream.writeInt(uniformBlock.vertexStaticUse);
......@@ -1794,12 +1796,8 @@ GLint Program::getActiveUniformBlockMaxLength() const
const UniformBlock &uniformBlock = mState.mUniformBlocks[uniformBlockIndex];
if (!uniformBlock.name.empty())
{
const int length = static_cast<int>(uniformBlock.name.length()) + 1;
// Counting in "[0]".
const int arrayLength = (uniformBlock.isArray ? 3 : 0);
maxLength = std::max(length + arrayLength, maxLength);
int length = static_cast<int>(uniformBlock.nameWithArrayIndex().length());
maxLength = std::max(length + 1, maxLength);
}
}
}
......@@ -2283,7 +2281,9 @@ bool Program::areMatchingInterfaceBlocks(InfoLog &infoLog,
<< "' between vertex and fragment shaders";
return false;
}
if (vertexInterfaceBlock.layout != fragmentInterfaceBlock.layout || vertexInterfaceBlock.isRowMajorLayout != fragmentInterfaceBlock.isRowMajorLayout)
if (vertexInterfaceBlock.layout != fragmentInterfaceBlock.layout ||
vertexInterfaceBlock.isRowMajorLayout != fragmentInterfaceBlock.isRowMajorLayout ||
vertexInterfaceBlock.binding != fragmentInterfaceBlock.binding)
{
infoLog << "Layout qualifiers differ for interface block '" << blockName
<< "' between vertex and fragment shaders";
......@@ -2787,6 +2787,12 @@ void Program::gatherInterfaceBlockInfo()
defineUniformBlock(fragmentBlock, GL_FRAGMENT_SHADER);
visitedList.insert(fragmentBlock.name);
}
// Set initial bindings from shader.
for (unsigned int blockIndex = 0; blockIndex < mState.mUniformBlocks.size(); blockIndex++)
{
UniformBlock &uniformBlock = mState.mUniformBlocks[blockIndex];
bindUniformBlock(blockIndex, uniformBlock.binding);
}
}
template <typename VarT>
......@@ -2831,18 +2837,6 @@ void Program::defineUniformBlock(const sh::InterfaceBlock &interfaceBlock, GLenu
int blockIndex = static_cast<int>(mState.mUniformBlocks.size());
size_t blockSize = 0;
// Don't define this block at all if it's not active in the implementation.
std::stringstream blockNameStr;
blockNameStr << interfaceBlock.name;
if (interfaceBlock.arraySize > 0)
{
blockNameStr << "[0]";
}
if (!mProgram->getUniformBlockSize(blockNameStr.str(), &blockSize))
{
return;
}
// Track the first and last uniform index to determine the range of active uniforms in the
// block.
size_t firstBlockUniformIndex = mState.mUniforms.size();
......@@ -2855,12 +2849,22 @@ void Program::defineUniformBlock(const sh::InterfaceBlock &interfaceBlock, GLenu
{
blockUniformIndexes.push_back(static_cast<unsigned int>(blockUniformIndex));
}
// ESSL 3.10 section 4.4.4 page 58:
// Any uniform or shader storage block declared without a binding qualifier is initially
// assigned to block binding point zero.
int blockBinding = (interfaceBlock.binding == -1 ? 0 : interfaceBlock.binding);
if (interfaceBlock.arraySize > 0)
{
for (unsigned int arrayElement = 0; arrayElement < interfaceBlock.arraySize; ++arrayElement)
{
UniformBlock block(interfaceBlock.name, true, arrayElement);
// Don't define this block at all if it's not active in the implementation.
if (!mProgram->getUniformBlockSize(interfaceBlock.name + ArrayString(arrayElement),
&blockSize))
{
continue;
}
UniformBlock block(interfaceBlock.name, true, arrayElement,
blockBinding + arrayElement);
block.memberUniformIndexes = blockUniformIndexes;
switch (shaderType)
......@@ -2893,7 +2897,11 @@ void Program::defineUniformBlock(const sh::InterfaceBlock &interfaceBlock, GLenu
}
else
{
UniformBlock block(interfaceBlock.name, false, 0);
if (!mProgram->getUniformBlockSize(interfaceBlock.name, &blockSize))
{
return;
}
UniformBlock block(interfaceBlock.name, false, 0, blockBinding);
block.memberUniformIndexes = blockUniformIndexes;
switch (shaderType)
......
......@@ -137,6 +137,7 @@ const uint8_t *LinkedUniform::getDataPtrToElement(size_t elementIndex) const
UniformBlock::UniformBlock()
: isArray(false),
arrayElement(0),
binding(0),
dataSize(0),
vertexStaticUse(false),
fragmentStaticUse(false),
......@@ -144,10 +145,14 @@ UniformBlock::UniformBlock()
{
}
UniformBlock::UniformBlock(const std::string &nameIn, bool isArrayIn, unsigned int arrayElementIn)
UniformBlock::UniformBlock(const std::string &nameIn,
bool isArrayIn,
unsigned int arrayElementIn,
int bindingIn)
: name(nameIn),
isArray(isArrayIn),
arrayElement(arrayElementIn),
binding(bindingIn),
dataSize(0),
vertexStaticUse(false),
fragmentStaticUse(false),
......
......@@ -59,7 +59,10 @@ struct LinkedUniform : public sh::Uniform
struct UniformBlock
{
UniformBlock();
UniformBlock(const std::string &nameIn, bool isArrayIn, unsigned int arrayElementIn);
UniformBlock(const std::string &nameIn,
bool isArrayIn,
unsigned int arrayElementIn,
int bindingIn);
UniformBlock(const UniformBlock &other) = default;
UniformBlock &operator=(const UniformBlock &other) = default;
......@@ -68,6 +71,7 @@ struct UniformBlock
std::string name;
bool isArray;
unsigned int arrayElement;
int binding;
unsigned int dataSize;
bool vertexStaticUse;
......
......@@ -636,6 +636,193 @@ TEST_P(UniformBufferTest, VeryLargeReadback)
glUnmapBuffer(GL_UNIFORM_BUFFER);
}
class UniformBufferTest31 : public ANGLETest
{
protected:
UniformBufferTest31()
{
setWindowWidth(128);
setWindowHeight(128);
setConfigRedBits(8);
setConfigGreenBits(8);
setConfigBlueBits(8);
setConfigAlphaBits(8);
}
};
// Test uniform block bindings greater than GL_MAX_UNIFORM_BUFFER_BINDINGS cause compile error.
TEST_P(UniformBufferTest31, MaxUniformBufferBindingsExceeded)
{
GLint maxUniformBufferBindings;
glGetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, &maxUniformBufferBindings);
std::string source =
"#version 310 es\n"
"in vec4 position;\n"
"layout(binding = ";
std::stringstream ss;
ss << maxUniformBufferBindings;
source = source + ss.str() +
") uniform uni {\n"
" vec4 color;\n"
"};\n"
"void main()\n"
"{\n"
" gl_Position = position;\n"
"}";
GLuint shader = CompileShader(GL_VERTEX_SHADER, source);
EXPECT_EQ(0u, shader);
}
// Test uniform block bindings specified by layout in shader work properly.
TEST_P(UniformBufferTest31, UniformBufferBindings)
{
const std::string &vertexShaderSource =
"#version 310 es\n"
"in vec4 position;\n"
"void main()\n"
"{\n"
" gl_Position = position;\n"
"}";
const std::string &fragmentShaderSource =
"#version 310 es\n"
"precision highp float;\n"
"layout(binding = 2) uniform uni {\n"
" vec4 color;\n"
"};\n"
"out vec4 fragColor;\n"
"void main()\n"
"{"
" fragColor = color;\n"
"}";
ANGLE_GL_PROGRAM(program, vertexShaderSource, fragmentShaderSource);
GLuint uniformBufferIndex = glGetUniformBlockIndex(program, "uni");
ASSERT_NE(GL_INVALID_INDEX, uniformBufferIndex);
GLBuffer uniformBuffer;
int px = getWindowWidth() / 2;
int py = getWindowHeight() / 2;
ASSERT_GL_NO_ERROR();
// 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());
first[0] = 10.f / 255.f;
first[1] = 20.f / 255.f;
first[2] = 30.f / 255.f;
first[3] = 40.f / 255.f;
glBindBuffer(GL_UNIFORM_BUFFER, uniformBuffer.get());
glBufferData(GL_UNIFORM_BUFFER, vec4Size, v.data(), GL_STATIC_DRAW);
EXPECT_GL_NO_ERROR();
glBindBufferBase(GL_UNIFORM_BUFFER, 2, uniformBuffer.get());
drawQuad(program, "position", 0.5f);
EXPECT_GL_NO_ERROR();
EXPECT_PIXEL_EQ(px, py, 10, 20, 30, 40);
// Clear the framebuffer
glClearColor(0.0, 0.0, 0.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT);
EXPECT_PIXEL_EQ(px, py, 0, 0, 0, 0);
// Try to bind the buffer to another binding point
glUniformBlockBinding(program, uniformBufferIndex, 5);
glBindBufferBase(GL_UNIFORM_BUFFER, 5, uniformBuffer.get());
drawQuad(program, "position", 0.5f);
EXPECT_GL_NO_ERROR();
EXPECT_PIXEL_EQ(px, py, 10, 20, 30, 40);
}
// Test uniform blocks used as instanced array take next binding point for each subsequent element.
TEST_P(UniformBufferTest31, ConsecutiveBindingsForBlockArray)
{
const std::string &vertexShaderSource =
"#version 310 es\n"
"in vec4 position;\n"
"void main()\n"
"{\n"
" gl_Position = position;\n"
"}";
const std::string &fragmentShaderSource =
"#version 310 es\n"
"precision highp float;\n"
"layout(binding = 2) uniform uni {\n"
" vec4 color;\n"
"} blocks[2];\n"
"out vec4 fragColor;\n"
"void main()\n"
"{\n"
" fragColor = blocks[0].color + blocks[1].color;\n"
"}";
ANGLE_GL_PROGRAM(program, vertexShaderSource, fragmentShaderSource);
std::array<GLBuffer, 2> uniformBuffers;
int px = getWindowWidth() / 2;
int py = getWindowHeight() / 2;
ASSERT_GL_NO_ERROR();
// 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());
first[0] = 10.f / 255.f;
first[1] = 20.f / 255.f;
first[2] = 30.f / 255.f;
first[3] = 40.f / 255.f;
glBindBuffer(GL_UNIFORM_BUFFER, uniformBuffers[0].get());
glBufferData(GL_UNIFORM_BUFFER, vec4Size, v.data(), GL_STATIC_DRAW);
EXPECT_GL_NO_ERROR();
glBindBufferBase(GL_UNIFORM_BUFFER, 2, uniformBuffers[0].get());
ASSERT_GL_NO_ERROR();
glBindBuffer(GL_UNIFORM_BUFFER, uniformBuffers[1].get());
glBufferData(GL_UNIFORM_BUFFER, vec4Size, v.data(), GL_STATIC_DRAW);
EXPECT_GL_NO_ERROR();
glBindBufferBase(GL_UNIFORM_BUFFER, 3, uniformBuffers[1].get());
drawQuad(program, "position", 0.5f);
EXPECT_GL_NO_ERROR();
EXPECT_PIXEL_EQ(px, py, 20, 40, 60, 80);
}
// Test the layout qualifier binding must be both specified(ESSL 3.10.4 section 9.2).
TEST_P(UniformBufferTest31, BindingMustBeBothSpecified)
{
const std::string &vertexShaderSource =
"#version 310 es\n"
"in vec4 position;\n"
"uniform uni\n"
"{\n"
" vec4 color;\n"
"} block;\n"
"void main()\n"
"{\n"
" gl_Position = position + block.color;\n"
"}";
const std::string &fragmentShaderSource =
"#version 310 es\n"
"precision highp float;\n"
"layout(binding = 0) uniform uni\n"
"{\n"
" vec4 color;\n"
"} block;\n"
"out vec4 fragColor;\n"
"void main()\n"
"{\n"
" fragColor = block.color;\n"
"}";
GLuint program = CompileProgram(vertexShaderSource, fragmentShaderSource);
ASSERT_EQ(0u, program);
}
// 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(),
......@@ -643,5 +830,6 @@ ANGLE_INSTANTIATE_TEST(UniformBufferTest,
ES3_D3D11_FL11_1_REFERENCE(),
ES3_OPENGL(),
ES3_OPENGLES());
ANGLE_INSTANTIATE_TEST(UniformBufferTest31, ES31_D3D11(), ES31_OPENGL(), ES31_OPENGLES());
} // namespace
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