Add UBO offset support for D3D11.1.
Also fixes the uniform count upper limit in glGetActiveUniformsiv,
as well as an assert hit with used but unbound uniform buffer.
BUG=angleproject:507
BUG=angleproject:962
Change-Id: I263b14df41d4e45a67304c1d145646398721cf0a
Reviewed-on: https://chromium-review.googlesource.com/263412
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Tested-by: Gregoire Payen de La Garanderie <Gregory.Payen@imgtec.com>
Reviewed-by: Geoff Lang <geofflang@chromium.org>
diff --git a/src/libANGLE/Program.cpp b/src/libANGLE/Program.cpp
index 1cafe33..daf0a40 100644
--- a/src/libANGLE/Program.cpp
+++ b/src/libANGLE/Program.cpp
@@ -1001,9 +1001,9 @@
return mProgram->applyUniforms();
}
-Error Program::applyUniformBuffers(const std::vector<gl::Buffer*> boundBuffers, const Caps &caps)
+Error Program::applyUniformBuffers(const gl::Data &data)
{
- return mProgram->applyUniformBuffers(boundBuffers, caps);
+ return mProgram->applyUniformBuffers(data, mUniformBlockBindings);
}
void Program::flagForDeletion()
@@ -1142,6 +1142,11 @@
return mProgram->getUniformBlockIndex(name);
}
+const UniformBlock *Program::getUniformBlockByIndex(GLuint index) const
+{
+ return mProgram->getUniformBlockByIndex(index);
+}
+
void Program::bindUniformBlock(GLuint uniformBlockIndex, GLuint uniformBlockBinding)
{
mUniformBlockBindings[uniformBlockIndex] = uniformBlockBinding;
diff --git a/src/libANGLE/Program.h b/src/libANGLE/Program.h
index 14deed9..38fc83d 100644
--- a/src/libANGLE/Program.h
+++ b/src/libANGLE/Program.h
@@ -184,7 +184,7 @@
void getUniformuiv(GLint location, GLuint *params);
Error applyUniforms();
- Error applyUniformBuffers(const std::vector<Buffer*> boundBuffers, const Caps &caps);
+ Error applyUniformBuffers(const gl::Data &data);
void getActiveUniformBlockName(GLuint uniformBlockIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformBlockName) const;
void getActiveUniformBlockiv(GLuint uniformBlockIndex, GLenum pname, GLint *params) const;
@@ -196,6 +196,8 @@
void bindUniformBlock(GLuint uniformBlockIndex, GLuint uniformBlockBinding);
GLuint getUniformBlockBinding(GLuint uniformBlockIndex) const;
+ const UniformBlock *getUniformBlockByIndex(GLuint index) const;
+
void setTransformFeedbackVaryings(GLsizei count, const GLchar *const *varyings, GLenum bufferMode);
void getTransformFeedbackVarying(GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name) const;
GLsizei getTransformFeedbackVaryingCount() const;
diff --git a/src/libANGLE/State.cpp b/src/libANGLE/State.cpp
index 30737f8..15274c6 100644
--- a/src/libANGLE/State.cpp
+++ b/src/libANGLE/State.cpp
@@ -940,6 +940,20 @@
return mUniformBuffers[index].get();
}
+GLintptr State::getIndexedUniformBufferOffset(GLuint index) const
+{
+ ASSERT(static_cast<size_t>(index) < mUniformBuffers.size());
+
+ return mUniformBuffers[index].getOffset();
+}
+
+GLsizeiptr State::getIndexedUniformBufferSize(GLuint index) const
+{
+ ASSERT(static_cast<size_t>(index) < mUniformBuffers.size());
+
+ return mUniformBuffers[index].getSize();
+}
+
void State::setGenericTransformFeedbackBufferBinding(Buffer *buffer)
{
mGenericTransformFeedbackBuffer.set(buffer);
diff --git a/src/libANGLE/State.h b/src/libANGLE/State.h
index 4d105e9..4370a2f 100644
--- a/src/libANGLE/State.h
+++ b/src/libANGLE/State.h
@@ -200,6 +200,8 @@
void setIndexedUniformBufferBinding(GLuint index, Buffer *buffer, GLintptr offset, GLsizeiptr size);
GLuint getIndexedUniformBufferId(GLuint index) const;
Buffer *getIndexedUniformBuffer(GLuint index) const;
+ GLintptr getIndexedUniformBufferOffset(GLuint index) const;
+ GLsizeiptr getIndexedUniformBufferSize(GLuint index) const;
// GL_TRANSFORM_FEEDBACK_BUFFER - Both indexed and generic targets
void setGenericTransformFeedbackBufferBinding(Buffer *buffer);
diff --git a/src/libANGLE/renderer/ProgramImpl.h b/src/libANGLE/renderer/ProgramImpl.h
index a17bb6d..1128ab6 100644
--- a/src/libANGLE/renderer/ProgramImpl.h
+++ b/src/libANGLE/renderer/ProgramImpl.h
@@ -94,7 +94,7 @@
const gl::Caps &caps) = 0;
virtual gl::Error applyUniforms() = 0;
- virtual gl::Error applyUniformBuffers(const std::vector<gl::Buffer*> boundBuffers, const gl::Caps &caps) = 0;
+ virtual gl::Error applyUniformBuffers(const gl::Data &data, GLuint uniformBlockBindings[]) = 0;
virtual bool assignUniformBlockRegister(gl::InfoLog &infoLog, gl::UniformBlock *uniformBlock, GLenum shader,
unsigned int registerIndex, const gl::Caps &caps) = 0;
diff --git a/src/libANGLE/renderer/d3d/ProgramD3D.cpp b/src/libANGLE/renderer/d3d/ProgramD3D.cpp
index e22db3b..9ce9a27 100644
--- a/src/libANGLE/renderer/d3d/ProgramD3D.cpp
+++ b/src/libANGLE/renderer/d3d/ProgramD3D.cpp
@@ -1117,12 +1117,20 @@
return gl::Error(GL_NO_ERROR);
}
-gl::Error ProgramD3D::applyUniformBuffers(const std::vector<gl::Buffer*> boundBuffers, const gl::Caps &caps)
+gl::Error ProgramD3D::applyUniformBuffers(const gl::Data &data, GLuint uniformBlockBindings[])
{
- ASSERT(boundBuffers.size() == mUniformBlocks.size());
+ GLint vertexUniformBuffers[gl::IMPLEMENTATION_MAX_VERTEX_SHADER_UNIFORM_BUFFERS];
+ GLint fragmentUniformBuffers[gl::IMPLEMENTATION_MAX_FRAGMENT_SHADER_UNIFORM_BUFFERS];
- const gl::Buffer *vertexUniformBuffers[gl::IMPLEMENTATION_MAX_VERTEX_SHADER_UNIFORM_BUFFERS] = {NULL};
- const gl::Buffer *fragmentUniformBuffers[gl::IMPLEMENTATION_MAX_FRAGMENT_SHADER_UNIFORM_BUFFERS] = {NULL};
+ for (unsigned int registerIndex = 0; registerIndex < gl::IMPLEMENTATION_MAX_VERTEX_SHADER_UNIFORM_BUFFERS; ++registerIndex)
+ {
+ vertexUniformBuffers[registerIndex] = -1;
+ }
+
+ for (unsigned int registerIndex = 0; registerIndex < gl::IMPLEMENTATION_MAX_FRAGMENT_SHADER_UNIFORM_BUFFERS; ++registerIndex)
+ {
+ fragmentUniformBuffers[registerIndex] = -1;
+ }
const unsigned int reservedBuffersInVS = mRenderer->getReservedVertexUniformBuffers();
const unsigned int reservedBuffersInFS = mRenderer->getReservedFragmentUniformBuffers();
@@ -1130,15 +1138,9 @@
for (unsigned int uniformBlockIndex = 0; uniformBlockIndex < mUniformBlocks.size(); uniformBlockIndex++)
{
gl::UniformBlock *uniformBlock = mUniformBlocks[uniformBlockIndex];
- gl::Buffer *uniformBuffer = boundBuffers[uniformBlockIndex];
+ GLuint blockBinding = uniformBlockBindings[uniformBlockIndex];
- ASSERT(uniformBlock && uniformBuffer);
-
- if (uniformBuffer->getSize() < uniformBlock->dataSize)
- {
- // undefined behaviour
- return gl::Error(GL_INVALID_OPERATION, "It is undefined behaviour to use a uniform buffer that is too small.");
- }
+ ASSERT(uniformBlock);
// Unnecessary to apply an unreferenced standard or shared UBO
if (!uniformBlock->isReferencedByVertexShader() && !uniformBlock->isReferencedByFragmentShader())
@@ -1149,21 +1151,21 @@
if (uniformBlock->isReferencedByVertexShader())
{
unsigned int registerIndex = uniformBlock->vsRegisterIndex - reservedBuffersInVS;
- ASSERT(vertexUniformBuffers[registerIndex] == NULL);
- ASSERT(registerIndex < caps.maxVertexUniformBlocks);
- vertexUniformBuffers[registerIndex] = uniformBuffer;
+ ASSERT(vertexUniformBuffers[registerIndex] == -1);
+ ASSERT(registerIndex < data.caps->maxVertexUniformBlocks);
+ vertexUniformBuffers[registerIndex] = blockBinding;
}
if (uniformBlock->isReferencedByFragmentShader())
{
unsigned int registerIndex = uniformBlock->psRegisterIndex - reservedBuffersInFS;
- ASSERT(fragmentUniformBuffers[registerIndex] == NULL);
- ASSERT(registerIndex < caps.maxFragmentUniformBlocks);
- fragmentUniformBuffers[registerIndex] = uniformBuffer;
+ ASSERT(fragmentUniformBuffers[registerIndex] == -1);
+ ASSERT(registerIndex < data.caps->maxFragmentUniformBlocks);
+ fragmentUniformBuffers[registerIndex] = blockBinding;
}
}
- return mRenderer->setUniformBuffers(vertexUniformBuffers, fragmentUniformBuffers);
+ return mRenderer->setUniformBuffers(data, vertexUniformBuffers, fragmentUniformBuffers);
}
bool ProgramD3D::assignUniformBlockRegister(gl::InfoLog &infoLog, gl::UniformBlock *uniformBlock, GLenum shader,
diff --git a/src/libANGLE/renderer/d3d/ProgramD3D.h b/src/libANGLE/renderer/d3d/ProgramD3D.h
index 3b76d44..6f3eade 100644
--- a/src/libANGLE/renderer/d3d/ProgramD3D.h
+++ b/src/libANGLE/renderer/d3d/ProgramD3D.h
@@ -81,7 +81,7 @@
void initializeUniformStorage();
gl::Error applyUniforms();
- gl::Error applyUniformBuffers(const std::vector<gl::Buffer*> boundBuffers, const gl::Caps &caps);
+ gl::Error applyUniformBuffers(const gl::Data &data, GLuint uniformBlockBindings[]) override;
bool assignUniformBlockRegister(gl::InfoLog &infoLog, gl::UniformBlock *uniformBlock, GLenum shader,
unsigned int registerIndex, const gl::Caps &caps);
void dirtyAllUniforms();
diff --git a/src/libANGLE/renderer/d3d/RendererD3D.cpp b/src/libANGLE/renderer/d3d/RendererD3D.cpp
index 61e95c6..ff9600e 100644
--- a/src/libANGLE/renderer/d3d/RendererD3D.cpp
+++ b/src/libANGLE/renderer/d3d/RendererD3D.cpp
@@ -133,7 +133,7 @@
return error;
}
- error = applyUniformBuffers(data);
+ error = program->applyUniformBuffers(data);
if (error.isError())
{
return error;
@@ -203,7 +203,7 @@
return error;
}
- error = applyUniformBuffers(data);
+ error = program->applyUniformBuffers(data);
if (error.isError())
{
return error;
@@ -474,32 +474,6 @@
return gl::Error(GL_NO_ERROR);
}
-gl::Error RendererD3D::applyUniformBuffers(const gl::Data &data)
-{
- gl::Program *program = data.state->getProgram();
-
- std::vector<gl::Buffer*> boundBuffers;
-
- for (unsigned int uniformBlockIndex = 0; uniformBlockIndex < program->getActiveUniformBlockCount(); uniformBlockIndex++)
- {
- GLuint blockBinding = program->getUniformBlockBinding(uniformBlockIndex);
-
- if (data.state->getIndexedUniformBuffer(blockBinding)->id() == 0)
- {
- // undefined behaviour
- return gl::Error(GL_INVALID_OPERATION, "It is undefined behaviour to have a used but unbound uniform buffer.");
- }
- else
- {
- gl::Buffer *uniformBuffer = data.state->getIndexedUniformBuffer(blockBinding);
- ASSERT(uniformBuffer);
- boundBuffers.push_back(uniformBuffer);
- }
- }
-
- return program->applyUniformBuffers(boundBuffers, *data.caps);
-}
-
bool RendererD3D::skipDraw(const gl::Data &data, GLenum drawMode)
{
if (drawMode == GL_POINTS)
diff --git a/src/libANGLE/renderer/d3d/RendererD3D.h b/src/libANGLE/renderer/d3d/RendererD3D.h
index fb4dfe6..3de6c20 100644
--- a/src/libANGLE/renderer/d3d/RendererD3D.h
+++ b/src/libANGLE/renderer/d3d/RendererD3D.h
@@ -105,7 +105,9 @@
virtual gl::Error setSamplerState(gl::SamplerType type, int index, gl::Texture *texture, const gl::SamplerState &sampler) = 0;
virtual gl::Error setTexture(gl::SamplerType type, int index, gl::Texture *texture) = 0;
- virtual gl::Error setUniformBuffers(const gl::Buffer *vertexUniformBuffers[], const gl::Buffer *fragmentUniformBuffers[]) = 0;
+ virtual gl::Error setUniformBuffers(const gl::Data &data,
+ const GLint vertexUniformBuffers[],
+ const GLint fragmentUniformBuffers[]) = 0;
virtual gl::Error setRasterizerState(const gl::RasterizerState &rasterState) = 0;
virtual gl::Error setBlendState(const gl::Framebuffer *framebuffer, const gl::BlendState &blendState, const gl::ColorF &blendColor,
@@ -207,7 +209,6 @@
gl::Error applyTextures(const gl::Data &data, gl::SamplerType shaderType,
const FramebufferTextureSerialArray &framebufferSerials, size_t framebufferSerialCount);
gl::Error applyTextures(const gl::Data &data);
- gl::Error applyUniformBuffers(const gl::Data &data);
bool skipDraw(const gl::Data &data, GLenum drawMode);
void markTransformFeedbackUsage(const gl::Data &data);
diff --git a/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp b/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp
index 2424f5f..e5228d2 100644
--- a/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp
+++ b/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp
@@ -143,6 +143,23 @@
return resource;
}
+void CalculateConstantBufferParams(GLintptr offset, GLsizeiptr size, UINT *outFirstConstant, UINT *outNumConstants)
+{
+ // The offset must be aligned to 256 bytes (should have been enforced by glBindBufferRange).
+ ASSERT(offset % 256 == 0);
+
+ // firstConstant and numConstants are expressed in constants of 16-bytes. Furthermore they must be a multiple of 16 constants.
+ *outFirstConstant = offset / 16;
+
+ // The GL size is not required to be aligned to a 256 bytes boundary.
+ // Round the size up to a 256 bytes boundary then express the results in constants of 16-bytes.
+ *outNumConstants = rx::roundUp(size, static_cast<GLsizeiptr>(256)) / 16;
+
+ // Since the size is rounded up, firstConstant + numConstants may be bigger than the actual size of the buffer.
+ // This behaviour is explictly allowed according to the documentation on ID3D11DeviceContext1::PSSetConstantBuffers1
+ // https://msdn.microsoft.com/en-us/library/windows/desktop/hh404649%28v=vs.85%29.aspx
+}
+
}
Renderer11::Renderer11(egl::Display *display)
@@ -168,6 +185,8 @@
mSyncQuery = NULL;
+ mSupportsConstantBufferOffsets = false;
+
mD3d11Module = NULL;
mDxgiModule = NULL;
@@ -502,6 +521,13 @@
const gl::Caps &rendererCaps = getRendererCaps();
+ if (getDeviceContext1IfSupported())
+ {
+ D3D11_FEATURE_DATA_D3D11_OPTIONS d3d11Options;
+ mDevice->CheckFeatureSupport(D3D11_FEATURE_D3D11_OPTIONS, &d3d11Options, sizeof(D3D11_FEATURE_DATA_D3D11_OPTIONS));
+ mSupportsConstantBufferOffsets = (d3d11Options.ConstantBufferOffsetting != FALSE);
+ }
+
mForceSetVertexSamplerStates.resize(rendererCaps.maxVertexTextureImageUnits);
mCurVertexSamplerStates.resize(rendererCaps.maxVertexTextureImageUnits);
@@ -792,11 +818,23 @@
return gl::Error(GL_NO_ERROR);
}
-gl::Error Renderer11::setUniformBuffers(const gl::Buffer *vertexUniformBuffers[], const gl::Buffer *fragmentUniformBuffers[])
+gl::Error Renderer11::setUniformBuffers(const gl::Data &data,
+ const GLint vertexUniformBuffers[],
+ const GLint fragmentUniformBuffers[])
{
- for (unsigned int uniformBufferIndex = 0; uniformBufferIndex < gl::IMPLEMENTATION_MAX_VERTEX_SHADER_UNIFORM_BUFFERS; uniformBufferIndex++)
+ for (unsigned int uniformBufferIndex = 0; uniformBufferIndex < data.caps->maxVertexUniformBlocks; uniformBufferIndex++)
{
- const gl::Buffer *uniformBuffer = vertexUniformBuffers[uniformBufferIndex];
+ GLint binding = vertexUniformBuffers[uniformBufferIndex];
+
+ if (binding == -1)
+ {
+ continue;
+ }
+
+ gl::Buffer *uniformBuffer = data.state->getIndexedUniformBuffer(binding);
+ GLintptr uniformBufferOffset = data.state->getIndexedUniformBufferOffset(binding);
+ GLsizeiptr uniformBufferSize = data.state->getIndexedUniformBufferSize(binding);
+
if (uniformBuffer)
{
Buffer11 *bufferStorage = Buffer11::makeBuffer11(uniformBuffer->getImplementation());
@@ -807,18 +845,44 @@
return gl::Error(GL_OUT_OF_MEMORY);
}
- if (mCurrentConstantBufferVS[uniformBufferIndex] != bufferStorage->getSerial())
+ if (mCurrentConstantBufferVS[uniformBufferIndex] != bufferStorage->getSerial() ||
+ mCurrentConstantBufferVSOffset[uniformBufferIndex] != uniformBufferOffset ||
+ mCurrentConstantBufferVSSize[uniformBufferIndex] != uniformBufferSize)
{
- mDeviceContext->VSSetConstantBuffers(getReservedVertexUniformBuffers() + uniformBufferIndex,
- 1, &constantBuffer);
+ if (mSupportsConstantBufferOffsets && uniformBufferSize != 0)
+ {
+ UINT firstConstant = 0, numConstants = 0;
+ CalculateConstantBufferParams(uniformBufferOffset, uniformBufferSize, &firstConstant, &numConstants);
+ mDeviceContext1->VSSetConstantBuffers1(getReservedVertexUniformBuffers() + uniformBufferIndex,
+ 1, &constantBuffer, &firstConstant, &numConstants);
+ }
+ else
+ {
+ ASSERT(uniformBufferOffset == 0);
+ mDeviceContext->VSSetConstantBuffers(getReservedVertexUniformBuffers() + uniformBufferIndex,
+ 1, &constantBuffer);
+ }
+
mCurrentConstantBufferVS[uniformBufferIndex] = bufferStorage->getSerial();
+ mCurrentConstantBufferVSOffset[uniformBufferIndex] = uniformBufferOffset;
+ mCurrentConstantBufferVSSize[uniformBufferIndex] = uniformBufferSize;
}
}
}
- for (unsigned int uniformBufferIndex = 0; uniformBufferIndex < gl::IMPLEMENTATION_MAX_FRAGMENT_SHADER_UNIFORM_BUFFERS; uniformBufferIndex++)
+ for (unsigned int uniformBufferIndex = 0; uniformBufferIndex < data.caps->maxFragmentUniformBlocks; uniformBufferIndex++)
{
- const gl::Buffer *uniformBuffer = fragmentUniformBuffers[uniformBufferIndex];
+ GLint binding = fragmentUniformBuffers[uniformBufferIndex];
+
+ if (binding == -1)
+ {
+ continue;
+ }
+
+ gl::Buffer *uniformBuffer = data.state->getIndexedUniformBuffer(binding);
+ GLintptr uniformBufferOffset = data.state->getIndexedUniformBufferOffset(binding);
+ GLsizeiptr uniformBufferSize = data.state->getIndexedUniformBufferSize(binding);
+
if (uniformBuffer)
{
Buffer11 *bufferStorage = Buffer11::makeBuffer11(uniformBuffer->getImplementation());
@@ -829,11 +893,27 @@
return gl::Error(GL_OUT_OF_MEMORY);
}
- if (mCurrentConstantBufferPS[uniformBufferIndex] != bufferStorage->getSerial())
+ if (mCurrentConstantBufferPS[uniformBufferIndex] != bufferStorage->getSerial() ||
+ mCurrentConstantBufferPSOffset[uniformBufferIndex] != uniformBufferOffset ||
+ mCurrentConstantBufferPSSize[uniformBufferIndex] != uniformBufferSize)
{
- mDeviceContext->PSSetConstantBuffers(getReservedFragmentUniformBuffers() + uniformBufferIndex,
- 1, &constantBuffer);
+ if (mSupportsConstantBufferOffsets && uniformBufferSize != 0)
+ {
+ UINT firstConstant = 0, numConstants = 0;
+ CalculateConstantBufferParams(uniformBufferOffset, uniformBufferSize, &firstConstant, &numConstants);
+ mDeviceContext1->PSSetConstantBuffers1(getReservedFragmentUniformBuffers() + uniformBufferIndex,
+ 1, &constantBuffer, &firstConstant, &numConstants);
+ }
+ else
+ {
+ ASSERT(uniformBufferOffset == 0);
+ mDeviceContext->PSSetConstantBuffers(getReservedFragmentUniformBuffers() + uniformBufferIndex,
+ 1, &constantBuffer);
+ }
+
mCurrentConstantBufferPS[uniformBufferIndex] = bufferStorage->getSerial();
+ mCurrentConstantBufferPSOffset[uniformBufferIndex] = uniformBufferOffset;
+ mCurrentConstantBufferPSSize[uniformBufferIndex] = uniformBufferSize;
}
}
}
@@ -2000,7 +2080,11 @@
for (unsigned int i = 0; i < gl::IMPLEMENTATION_MAX_VERTEX_SHADER_UNIFORM_BUFFERS; i++)
{
mCurrentConstantBufferVS[i] = static_cast<unsigned int>(-1);
+ mCurrentConstantBufferVSOffset[i] = 0;
+ mCurrentConstantBufferVSSize[i] = 0;
mCurrentConstantBufferPS[i] = static_cast<unsigned int>(-1);
+ mCurrentConstantBufferPSOffset[i] = 0;
+ mCurrentConstantBufferPSSize[i] = 0;
}
mCurrentVertexConstantBuffer = NULL;
@@ -3425,7 +3509,7 @@
void Renderer11::generateCaps(gl::Caps *outCaps, gl::TextureCapsMap *outTextureCaps, gl::Extensions *outExtensions) const
{
- d3d11_gl::GenerateCaps(mDevice, outCaps, outTextureCaps, outExtensions);
+ d3d11_gl::GenerateCaps(mDevice, mDeviceContext, outCaps, outTextureCaps, outExtensions);
}
Workarounds Renderer11::generateWorkarounds() const
diff --git a/src/libANGLE/renderer/d3d/d3d11/Renderer11.h b/src/libANGLE/renderer/d3d/d3d11/Renderer11.h
index 11d0783..7bfa000 100644
--- a/src/libANGLE/renderer/d3d/d3d11/Renderer11.h
+++ b/src/libANGLE/renderer/d3d/d3d11/Renderer11.h
@@ -87,7 +87,9 @@
virtual gl::Error setSamplerState(gl::SamplerType type, int index, gl::Texture *texture, const gl::SamplerState &sampler);
virtual gl::Error setTexture(gl::SamplerType type, int index, gl::Texture *texture);
- virtual gl::Error setUniformBuffers(const gl::Buffer *vertexUniformBuffers[], const gl::Buffer *fragmentUniformBuffers[]);
+ gl::Error setUniformBuffers(const gl::Data &data,
+ const GLint vertexUniformBuffers[],
+ const GLint fragmentUniformBuffers[]) override;
virtual gl::Error setRasterizerState(const gl::RasterizerState &rasterState);
gl::Error setBlendState(const gl::Framebuffer *framebuffer, const gl::BlendState &blendState, const gl::ColorF &blendColor,
@@ -344,12 +346,16 @@
ID3D11Buffer *mDriverConstantBufferVS;
ID3D11Buffer *mCurrentVertexConstantBuffer;
unsigned int mCurrentConstantBufferVS[gl::IMPLEMENTATION_MAX_VERTEX_SHADER_UNIFORM_BUFFERS];
+ GLintptr mCurrentConstantBufferVSOffset[gl::IMPLEMENTATION_MAX_VERTEX_SHADER_UNIFORM_BUFFERS];
+ GLsizeiptr mCurrentConstantBufferVSSize[gl::IMPLEMENTATION_MAX_VERTEX_SHADER_UNIFORM_BUFFERS];
dx_PixelConstants mPixelConstants;
dx_PixelConstants mAppliedPixelConstants;
ID3D11Buffer *mDriverConstantBufferPS;
ID3D11Buffer *mCurrentPixelConstantBuffer;
- unsigned int mCurrentConstantBufferPS[gl::IMPLEMENTATION_MAX_VERTEX_SHADER_UNIFORM_BUFFERS];
+ unsigned int mCurrentConstantBufferPS[gl::IMPLEMENTATION_MAX_FRAGMENT_SHADER_UNIFORM_BUFFERS];
+ GLintptr mCurrentConstantBufferPSOffset[gl::IMPLEMENTATION_MAX_FRAGMENT_SHADER_UNIFORM_BUFFERS];
+ GLsizeiptr mCurrentConstantBufferPSSize[gl::IMPLEMENTATION_MAX_FRAGMENT_SHADER_UNIFORM_BUFFERS];
ID3D11Buffer *mCurrentGeometryConstantBuffer;
@@ -374,6 +380,9 @@
// Sync query
ID3D11Query *mSyncQuery;
+ // Constant buffer offset support
+ bool mSupportsConstantBufferOffsets;
+
ID3D11Device *mDevice;
D3D_FEATURE_LEVEL mFeatureLevel;
ID3D11DeviceContext *mDeviceContext;
diff --git a/src/libANGLE/renderer/d3d/d3d11/renderer11_utils.cpp b/src/libANGLE/renderer/d3d/d3d11/renderer11_utils.cpp
index 1444d15..70b2b79 100644
--- a/src/libANGLE/renderer/d3d/d3d11/renderer11_utils.cpp
+++ b/src/libANGLE/renderer/d3d/d3d11/renderer11_utils.cpp
@@ -957,7 +957,7 @@
}
}
-void GenerateCaps(ID3D11Device *device, gl::Caps *caps, gl::TextureCapsMap *textureCapsMap, gl::Extensions *extensions)
+void GenerateCaps(ID3D11Device *device, ID3D11DeviceContext *deviceContext, gl::Caps *caps, gl::TextureCapsMap *textureCapsMap, gl::Extensions *extensions)
{
D3D_FEATURE_LEVEL featureLevel = device->GetFeatureLevel();
@@ -1054,6 +1054,22 @@
// Setting a large alignment forces uniform buffers to bind with zero offset
caps->uniformBufferOffsetAlignment = static_cast<GLuint>(std::numeric_limits<GLint>::max());
+ ID3D11DeviceContext1 *deviceContext1 = d3d11::DynamicCastComObject<ID3D11DeviceContext1>(deviceContext);
+
+ if (deviceContext1)
+ {
+ D3D11_FEATURE_DATA_D3D11_OPTIONS d3d11Options;
+ device->CheckFeatureSupport(D3D11_FEATURE_D3D11_OPTIONS, &d3d11Options, sizeof(D3D11_FEATURE_DATA_D3D11_OPTIONS));
+
+ if (d3d11Options.ConstantBufferOffsetting)
+ {
+ // With DirectX 11.1, constant buffer offset and size must be a multiple of 16 constants of 16 bytes each.
+ // https://msdn.microsoft.com/en-us/library/windows/desktop/hh404649%28v=vs.85%29.aspx
+ caps->uniformBufferOffsetAlignment = 256;
+ }
+
+ SafeRelease(deviceContext1);
+ }
caps->maxCombinedUniformBlocks = caps->maxVertexUniformBlocks + caps->maxFragmentUniformBlocks;
caps->maxCombinedVertexUniformComponents = (static_cast<GLint64>(caps->maxVertexUniformBlocks) * static_cast<GLint64>(caps->maxUniformBlockSize / 4)) +
diff --git a/src/libANGLE/renderer/d3d/d3d11/renderer11_utils.h b/src/libANGLE/renderer/d3d/d3d11/renderer11_utils.h
index e0dd810..207e6b5 100644
--- a/src/libANGLE/renderer/d3d/d3d11/renderer11_utils.h
+++ b/src/libANGLE/renderer/d3d/d3d11/renderer11_utils.h
@@ -51,7 +51,7 @@
{
GLint GetMaximumClientVersion(D3D_FEATURE_LEVEL featureLevel);
-void GenerateCaps(ID3D11Device *device, gl::Caps *caps, gl::TextureCapsMap *textureCapsMap, gl::Extensions *extensions);
+void GenerateCaps(ID3D11Device *device, ID3D11DeviceContext *deviceContext, gl::Caps *caps, gl::TextureCapsMap *textureCapsMap, gl::Extensions *extensions);
}
diff --git a/src/libANGLE/renderer/d3d/d3d9/Renderer9.cpp b/src/libANGLE/renderer/d3d/d3d9/Renderer9.cpp
index c524f13..eaaab34 100644
--- a/src/libANGLE/renderer/d3d/d3d9/Renderer9.cpp
+++ b/src/libANGLE/renderer/d3d/d3d9/Renderer9.cpp
@@ -860,7 +860,9 @@
return gl::Error(GL_NO_ERROR);
}
-gl::Error Renderer9::setUniformBuffers(const gl::Buffer* /*vertexUniformBuffers*/[], const gl::Buffer* /*fragmentUniformBuffers*/[])
+gl::Error Renderer9::setUniformBuffers(const gl::Data &/*data*/,
+ const GLint /*vertexUniformBuffers*/[],
+ const GLint /*fragmentUniformBuffers*/[])
{
// No effect in ES2/D3D9
return gl::Error(GL_NO_ERROR);
diff --git a/src/libANGLE/renderer/d3d/d3d9/Renderer9.h b/src/libANGLE/renderer/d3d/d3d9/Renderer9.h
index 37286f0..978c181 100644
--- a/src/libANGLE/renderer/d3d/d3d9/Renderer9.h
+++ b/src/libANGLE/renderer/d3d/d3d9/Renderer9.h
@@ -91,7 +91,9 @@
virtual gl::Error setSamplerState(gl::SamplerType type, int index, gl::Texture *texture, const gl::SamplerState &sampler);
virtual gl::Error setTexture(gl::SamplerType type, int index, gl::Texture *texture);
- virtual gl::Error setUniformBuffers(const gl::Buffer *vertexUniformBuffers[], const gl::Buffer *fragmentUniformBuffers[]);
+ gl::Error setUniformBuffers(const gl::Data &data,
+ const GLint vertexUniformBuffers[],
+ const GLint fragmentUniformBuffers[]) override;
virtual gl::Error setRasterizerState(const gl::RasterizerState &rasterState);
gl::Error setBlendState(const gl::Framebuffer *framebuffer, const gl::BlendState &blendState, const gl::ColorF &blendColor,
diff --git a/src/libANGLE/renderer/gl/ProgramGL.cpp b/src/libANGLE/renderer/gl/ProgramGL.cpp
index e934234..6e481d6 100644
--- a/src/libANGLE/renderer/gl/ProgramGL.cpp
+++ b/src/libANGLE/renderer/gl/ProgramGL.cpp
@@ -367,7 +367,7 @@
return gl::Error(GL_INVALID_OPERATION);
}
-gl::Error ProgramGL::applyUniformBuffers(const std::vector<gl::Buffer*> boundBuffers, const gl::Caps &caps)
+gl::Error ProgramGL::applyUniformBuffers(const gl::Data &data, GLuint uniformBlockBindings[])
{
UNIMPLEMENTED();
return gl::Error(GL_INVALID_OPERATION);
diff --git a/src/libANGLE/renderer/gl/ProgramGL.h b/src/libANGLE/renderer/gl/ProgramGL.h
index bc13c78..20e5a3b 100644
--- a/src/libANGLE/renderer/gl/ProgramGL.h
+++ b/src/libANGLE/renderer/gl/ProgramGL.h
@@ -79,7 +79,7 @@
const gl::Caps &caps) override;
gl::Error applyUniforms() override;
- gl::Error applyUniformBuffers(const std::vector<gl::Buffer*> boundBuffers, const gl::Caps &caps) override;
+ gl::Error applyUniformBuffers(const gl::Data &data, GLuint uniformBlockBindings[]) override;
bool assignUniformBlockRegister(gl::InfoLog &infoLog, gl::UniformBlock *uniformBlock, GLenum shader,
unsigned int registerIndex, const gl::Caps &caps) override;
diff --git a/src/libANGLE/validationES.cpp b/src/libANGLE/validationES.cpp
index 6469510..51f258f 100644
--- a/src/libANGLE/validationES.cpp
+++ b/src/libANGLE/validationES.cpp
@@ -1436,6 +1436,36 @@
}
}
+ // Uniform buffer validation
+ for (unsigned int uniformBlockIndex = 0; uniformBlockIndex < program->getActiveUniformBlockCount(); uniformBlockIndex++)
+ {
+ const gl::UniformBlock *uniformBlock = program->getUniformBlockByIndex(uniformBlockIndex);
+ GLuint blockBinding = program->getUniformBlockBinding(uniformBlockIndex);
+ const gl::Buffer *uniformBuffer = state.getIndexedUniformBuffer(blockBinding);
+
+ if (!uniformBuffer)
+ {
+ // undefined behaviour
+ context->recordError(Error(GL_INVALID_OPERATION, "It is undefined behaviour to have a used but unbound uniform buffer."));
+ return false;
+ }
+
+ GLsizeiptr uniformBufferSize = state.getIndexedUniformBufferSize(blockBinding);
+
+ if (uniformBufferSize == 0)
+ {
+ // Bind the whole buffer.
+ uniformBufferSize = uniformBuffer->getSize();
+ }
+
+ if (uniformBufferSize < uniformBlock->dataSize)
+ {
+ // undefined behaviour
+ context->recordError(Error(GL_INVALID_OPERATION, "It is undefined behaviour to use a uniform buffer that is too small."));
+ return false;
+ }
+ }
+
// No-op if zero count
return (count > 0);
}
diff --git a/src/libGLESv2/entry_points_gles_3_0.cpp b/src/libGLESv2/entry_points_gles_3_0.cpp
index 60f1041..d8bdcd2 100644
--- a/src/libGLESv2/entry_points_gles_3_0.cpp
+++ b/src/libGLESv2/entry_points_gles_3_0.cpp
@@ -2082,7 +2082,7 @@
return;
}
- if (uniformCount > 0)
+ if (uniformCount > programObject->getActiveUniformCount())
{
context->recordError(Error(GL_INVALID_VALUE));
return;
diff --git a/src/tests/angle_end2end_tests.gypi b/src/tests/angle_end2end_tests.gypi
index 7e32b46..c6234a0 100644
--- a/src/tests/angle_end2end_tests.gypi
+++ b/src/tests/angle_end2end_tests.gypi
@@ -45,6 +45,7 @@
'<(angle_path)/src/tests/end2end_tests/SwizzleTest.cpp',
'<(angle_path)/src/tests/end2end_tests/TextureTest.cpp',
'<(angle_path)/src/tests/end2end_tests/TransformFeedbackTest.cpp',
+ '<(angle_path)/src/tests/end2end_tests/UniformBufferTest.cpp',
'<(angle_path)/src/tests/end2end_tests/UniformTest.cpp',
'<(angle_path)/src/tests/end2end_tests/UnpackAlignmentTest.cpp',
'<(angle_path)/src/tests/end2end_tests/UnpackRowLength.cpp',
diff --git a/src/tests/end2end_tests/UniformBufferTest.cpp b/src/tests/end2end_tests/UniformBufferTest.cpp
new file mode 100644
index 0000000..159c5fa
--- /dev/null
+++ b/src/tests/end2end_tests/UniformBufferTest.cpp
@@ -0,0 +1,196 @@
+#include "ANGLETest.h"
+
+// Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against.
+ANGLE_TYPED_TEST_CASE(UniformBufferTest, ES3_D3D11_FL11_1, ES3_D3D11_FL11_1_REFERENCE);
+
+template<typename T>
+class UniformBufferTest : public ANGLETest
+{
+ protected:
+ UniformBufferTest() : ANGLETest(T::GetGlesMajorVersion(), T::GetPlatform())
+ {
+ setWindowWidth(128);
+ setWindowHeight(128);
+ setConfigRedBits(8);
+ setConfigGreenBits(8);
+ setConfigBlueBits(8);
+ setConfigAlphaBits(8);
+ }
+
+ virtual void SetUp()
+ {
+ 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);
+ ASSERT_NE(mProgram, 0u);
+
+ mUniformBufferIndex = glGetUniformBlockIndex(mProgram, "uni");
+ ASSERT_NE(mUniformBufferIndex, -1);
+
+ glGenBuffers(1, &mUniformBuffer);
+
+ ASSERT_GL_NO_ERROR();
+ }
+
+ virtual void TearDown()
+ {
+ glDeleteBuffers(1, &mUniformBuffer);
+ glDeleteProgram(mProgram);
+ ANGLETest::TearDown();
+ }
+
+ GLuint mProgram;
+ GLint mUniformBufferIndex;
+ GLuint mUniformBuffer;
+};
+
+// Test that using a UBO with a non-zero offset and size actually works.
+// The first step of this test renders a color from a UBO with a zero offset.
+// The second step renders a color from a UBO with a non-zero offset.
+TYPED_TEST(UniformBufferTest, UniformBufferRange)
+{
+ int px = getWindowWidth() / 2;
+ int py = getWindowHeight() / 2;
+
+ // Query the uniform buffer alignment requirement
+ GLint alignment;
+ glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &alignment);
+
+ GLint64 maxUniformBlockSize;
+ glGetInteger64v(GL_MAX_UNIFORM_BLOCK_SIZE, &maxUniformBlockSize);
+ if (alignment >= maxUniformBlockSize)
+ {
+ // ANGLE doesn't implement UBO offsets for this platform.
+ // Ignore the test case.
+ return;
+ }
+
+ ASSERT_GL_NO_ERROR();
+
+ // Let's create a buffer which contains two vec4.
+ GLuint vec4Size = 4 * sizeof(float);
+ GLuint stride = 0;
+ do
+ {
+ stride += alignment;
+ }
+ while (stride < vec4Size);
+
+ std::vector<char> v(2 * 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;
+ first[2] = 30.f / 255.f;
+ first[3] = 40.f / 255.f;
+
+ second[0] = 110.f / 255.f;
+ second[1] = 120.f / 255.f;
+ second[2] = 130.f / 255.f;
+ second[3] = 140.f / 255.f;
+
+ glBindBuffer(GL_UNIFORM_BUFFER, mUniformBuffer);
+ // We use on purpose a size which is not a multiple of the alignment.
+ glBufferData(GL_UNIFORM_BUFFER, stride + vec4Size, v.data(), GL_STATIC_DRAW);
+
+ glUniformBlockBinding(mProgram, mUniformBufferIndex, 0);
+
+ EXPECT_GL_NO_ERROR();
+
+ // Bind the first part of the uniform buffer and draw
+ // Use a size which is smaller than the alignment to check
+ // to check that this case is handle correctly in the conversion to 11.1.
+ glBindBufferRange(GL_UNIFORM_BUFFER, 0, mUniformBuffer, 0, vec4Size);
+ drawQuad(mProgram, "position", 0.5f);
+ EXPECT_GL_NO_ERROR();
+ 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.
+ glBindBufferRange(GL_UNIFORM_BUFFER, 0, mUniformBuffer, stride, vec4Size);
+ drawQuad(mProgram, "position", 0.5f);
+ EXPECT_GL_NO_ERROR();
+ EXPECT_PIXEL_EQ(px, py, 110, 120, 130, 140);
+}
+
+// Test uniform block bindings.
+TYPED_TEST(UniformBufferTest, UniformBufferBindings)
+{
+ 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, mUniformBuffer);
+ glBufferData(GL_UNIFORM_BUFFER, vec4Size, v.data(), GL_STATIC_DRAW);
+
+ EXPECT_GL_NO_ERROR();
+
+ // Try to bind the buffer to binding point 2
+ glUniformBlockBinding(mProgram, mUniformBufferIndex, 2);
+ glBindBufferBase(GL_UNIFORM_BUFFER, 2, mUniformBuffer);
+ drawQuad(mProgram, "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(mProgram, mUniformBufferIndex, 5);
+ glBindBufferBase(GL_UNIFORM_BUFFER, 5, mUniformBuffer);
+ drawQuad(mProgram, "position", 0.5f);
+ EXPECT_GL_NO_ERROR();
+ EXPECT_PIXEL_EQ(px, py, 10, 20, 30, 40);
+}
+
+// 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.
+TYPED_TEST(UniformBufferTest, UnboundUniformBuffer)
+{
+ glUniformBlockBinding(mProgram, mUniformBufferIndex, 0);
+ glBindBufferBase(GL_UNIFORM_BUFFER, 0, 0);
+ EXPECT_GL_NO_ERROR();
+
+ drawQuad(mProgram, "position", 0.5f);
+ EXPECT_GL_ERROR(GL_INVALID_OPERATION);
+}