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);
+}