Support CHROMIUM_path_rendering

This is partial support for CHROMIUM_path_rendering
and implements basic path management and non-instanced
rendering.

BUG=angleproject:1382

Change-Id: I9c0e88183e0a915d522889323933439d25b45b5f
Reviewed-on: https://chromium-review.googlesource.com/348630
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Commit-Queue: Jamie Madill <jmadill@chromium.org>
diff --git a/src/common/mathutil.cpp b/src/common/mathutil.cpp
index 927b6eb..acbcbdf 100644
--- a/src/common/mathutil.cpp
+++ b/src/common/mathutil.cpp
@@ -64,4 +64,4 @@
     *blue = inputData->B * pow(2.0f, (int)inputData->E - g_sharedexp_bias - g_sharedexp_mantissabits);
 }
 
-}
+}  // namespace gl
diff --git a/src/common/mathutil.h b/src/common/mathutil.h
index 27329ba..06421f0 100644
--- a/src/common/mathutil.h
+++ b/src/common/mathutil.h
@@ -721,8 +721,42 @@
     return ((bitCast<uint32_t>(f) & 0x7f800000u) == 0x7f800000u) && !(bitCast<uint32_t>(f) & 0x7fffffu);
 }
 
+namespace priv
+{
+template <unsigned int N, unsigned int R>
+struct iSquareRoot
+{
+    static constexpr unsigned int solve()
+    {
+        return (R * R > N)
+                   ? 0
+                   : ((R * R == N) ? R : static_cast<unsigned int>(iSquareRoot<N, R + 1>::value));
+    }
+    enum Result
+    {
+        value = iSquareRoot::solve()
+    };
+};
+
+template <unsigned int N>
+struct iSquareRoot<N, N>
+{
+    enum result
+    {
+        value = N
+    };
+};
+
+}  // namespace priv
+
+template <unsigned int N>
+constexpr unsigned int iSquareRoot()
+{
+    return priv::iSquareRoot<N, 1>::value;
 }
 
+}  // namespace gl
+
 namespace rx
 {
 
diff --git a/src/common/matrix_utils.h b/src/common/matrix_utils.h
index 6f3187c..aa3f895 100644
--- a/src/common/matrix_utils.h
+++ b/src/common/matrix_utils.h
@@ -16,6 +16,7 @@
 #include <vector>
 
 #include "common/debug.h"
+#include "common/mathutil.h"
 
 namespace angle
 {
@@ -337,6 +338,42 @@
         return result;
     }
 
+    void setToIdentity()
+    {
+        ASSERT(rows() == columns());
+
+        const auto one  = T(1);
+        const auto zero = T(0);
+
+        for (auto &e : mElements)
+            e = zero;
+
+        for (unsigned int i = 0; i < rows(); ++i)
+        {
+            const auto pos = i * columns() + (i % columns());
+            mElements[pos] = one;
+        }
+    }
+
+    template <unsigned int Size>
+    static void setToIdentity(T(&matrix)[Size])
+    {
+        static_assert(gl::iSquareRoot<Size>() != 0, "Matrix is not square.");
+
+        const auto cols = gl::iSquareRoot<Size>();
+        const auto one  = T(1);
+        const auto zero = T(0);
+
+        for (auto &e : matrix)
+            e = zero;
+
+        for (unsigned int i = 0; i < cols; ++i)
+        {
+            const auto pos = i * cols + (i % cols);
+            matrix[pos]    = one;
+        }
+    }
+
   private:
     std::vector<T> mElements;
     unsigned int mRows;
diff --git a/src/libANGLE/Caps.cpp b/src/libANGLE/Caps.cpp
index 97f920e..63d964b 100644
--- a/src/libANGLE/Caps.cpp
+++ b/src/libANGLE/Caps.cpp
@@ -163,7 +163,8 @@
       colorBufferFloat(false),
       multisampleCompatibility(false),
       framebufferMixedSamples(false),
-      textureNorm16(false)
+      textureNorm16(false),
+      pathRendering(false)
 {
 }
 
@@ -240,6 +241,7 @@
     InsertExtensionString("GL_EXT_multisample_compatibility",      multisampleCompatibility,  &extensionStrings);
     InsertExtensionString("GL_CHROMIUM_framebuffer_mixed_samples", framebufferMixedSamples,   &extensionStrings);
     InsertExtensionString("GL_EXT_texture_norm16",                 textureNorm16,             &extensionStrings);
+    InsertExtensionString("GL_CHROMIUM_path_rendering",            pathRendering,             &extensionStrings);
     // clang-format on
 
     return extensionStrings;
diff --git a/src/libANGLE/Caps.h b/src/libANGLE/Caps.h
index f1f5ddd..0cbc290 100644
--- a/src/libANGLE/Caps.h
+++ b/src/libANGLE/Caps.h
@@ -303,6 +303,9 @@
     // GL_EXT_texture_norm16
     // written against ES 3.1 but can apply to ES 3.0 as well.
     bool textureNorm16;
+
+    // GL_CHROMIUM_path_rendering
+    bool pathRendering;
 };
 
 struct Limitations
diff --git a/src/libANGLE/Context.cpp b/src/libANGLE/Context.cpp
index 2ab9f3a..2559657 100644
--- a/src/libANGLE/Context.cpp
+++ b/src/libANGLE/Context.cpp
@@ -11,7 +11,9 @@
 
 #include <iterator>
 #include <sstream>
+#include <string.h>
 
+#include "common/matrix_utils.h"
 #include "common/platform.h"
 #include "common/utilities.h"
 #include "libANGLE/Buffer.h"
@@ -20,6 +22,7 @@
 #include "libANGLE/Fence.h"
 #include "libANGLE/Framebuffer.h"
 #include "libANGLE/FramebufferAttachment.h"
+#include "libANGLE/Path.h"
 #include "libANGLE/Program.h"
 #include "libANGLE/Query.h"
 #include "libANGLE/Renderbuffer.h"
@@ -437,6 +440,17 @@
     return reinterpret_cast<GLsync>(static_cast<uintptr_t>(handle));
 }
 
+GLuint Context::createPaths(GLsizei range)
+{
+    auto resultOrError = mResourceManager->createPaths(mImplementation.get(), range);
+    if (resultOrError.isError())
+    {
+        handleError(resultOrError.getError());
+        return 0;
+    }
+    return resultOrError.getResult();
+}
+
 GLuint Context::createVertexArray()
 {
     GLuint vertexArray           = mVertexArrayHandleAllocator.allocate();
@@ -534,6 +548,96 @@
     mResourceManager->deleteFenceSync(static_cast<GLuint>(reinterpret_cast<uintptr_t>(fenceSync)));
 }
 
+void Context::deletePaths(GLuint first, GLsizei range)
+{
+    mResourceManager->deletePaths(first, range);
+}
+
+bool Context::hasPathData(GLuint path) const
+{
+    const auto *pathObj = mResourceManager->getPath(path);
+    if (pathObj == nullptr)
+        return false;
+
+    return pathObj->hasPathData();
+}
+
+bool Context::hasPath(GLuint path) const
+{
+    return mResourceManager->hasPath(path);
+}
+
+void Context::setPathCommands(GLuint path,
+                              GLsizei numCommands,
+                              const GLubyte *commands,
+                              GLsizei numCoords,
+                              GLenum coordType,
+                              const void *coords)
+{
+    auto *pathObject = mResourceManager->getPath(path);
+
+    handleError(pathObject->setCommands(numCommands, commands, numCoords, coordType, coords));
+}
+
+void Context::setPathParameterf(GLuint path, GLenum pname, GLfloat value)
+{
+    auto *pathObj = mResourceManager->getPath(path);
+
+    switch (pname)
+    {
+        case GL_PATH_STROKE_WIDTH_CHROMIUM:
+            pathObj->setStrokeWidth(value);
+            break;
+        case GL_PATH_END_CAPS_CHROMIUM:
+            pathObj->setEndCaps(static_cast<GLenum>(value));
+            break;
+        case GL_PATH_JOIN_STYLE_CHROMIUM:
+            pathObj->setJoinStyle(static_cast<GLenum>(value));
+            break;
+        case GL_PATH_MITER_LIMIT_CHROMIUM:
+            pathObj->setMiterLimit(value);
+            break;
+        case GL_PATH_STROKE_BOUND_CHROMIUM:
+            pathObj->setStrokeBound(value);
+            break;
+        default:
+            UNREACHABLE();
+            break;
+    }
+}
+
+void Context::getPathParameterfv(GLuint path, GLenum pname, GLfloat *value) const
+{
+    const auto *pathObj = mResourceManager->getPath(path);
+
+    switch (pname)
+    {
+        case GL_PATH_STROKE_WIDTH_CHROMIUM:
+            *value = pathObj->getStrokeWidth();
+            break;
+        case GL_PATH_END_CAPS_CHROMIUM:
+            *value = static_cast<GLfloat>(pathObj->getEndCaps());
+            break;
+        case GL_PATH_JOIN_STYLE_CHROMIUM:
+            *value = static_cast<GLfloat>(pathObj->getJoinStyle());
+            break;
+        case GL_PATH_MITER_LIMIT_CHROMIUM:
+            *value = pathObj->getMiterLimit();
+            break;
+        case GL_PATH_STROKE_BOUND_CHROMIUM:
+            *value = pathObj->getStrokeBound();
+            break;
+        default:
+            UNREACHABLE();
+            break;
+    }
+}
+
+void Context::setPathStencilFunc(GLenum func, GLint ref, GLuint mask)
+{
+    mGLState.setPathStencilFunc(func, ref, mask);
+}
+
 void Context::deleteVertexArray(GLuint vertexArray)
 {
     auto iter = mVertexArrayMap.find(vertexArray);
@@ -1016,6 +1120,16 @@
       case GL_MAX_TEXTURE_LOD_BIAS:
         *params = mCaps.maxLODBias;
         break;
+
+      case GL_PATH_MODELVIEW_MATRIX_CHROMIUM:
+      case GL_PATH_PROJECTION_MATRIX_CHROMIUM:
+      {
+          ASSERT(mExtensions.pathRendering);
+          const GLfloat *m = mGLState.getPathRenderingMatrix(pname);
+          memcpy(params, m, 16 * sizeof(GLfloat));
+      }
+      break;
+
       default:
           mGLState.getFloatv(pname, params);
           break;
@@ -1269,6 +1383,94 @@
     mGLState.setCoverageModulation(components);
 }
 
+void Context::loadPathRenderingMatrix(GLenum matrixMode, const GLfloat *matrix)
+{
+    mGLState.loadPathRenderingMatrix(matrixMode, matrix);
+}
+
+void Context::loadPathRenderingIdentityMatrix(GLenum matrixMode)
+{
+    GLfloat I[16];
+    angle::Matrix<GLfloat>::setToIdentity(I);
+
+    mGLState.loadPathRenderingMatrix(matrixMode, I);
+}
+
+void Context::stencilFillPath(GLuint path, GLenum fillMode, GLuint mask)
+{
+    const auto *pathObj = mResourceManager->getPath(path);
+    if (!pathObj)
+        return;
+
+    // TODO(svaisanen@nvidia.com): maybe sync only state required for path rendering?
+    syncRendererState();
+
+    mImplementation->stencilFillPath(pathObj, fillMode, mask);
+}
+
+void Context::stencilStrokePath(GLuint path, GLint reference, GLuint mask)
+{
+    const auto *pathObj = mResourceManager->getPath(path);
+    if (!pathObj)
+        return;
+
+    // TODO(svaisanen@nvidia.com): maybe sync only state required for path rendering?
+    syncRendererState();
+
+    mImplementation->stencilStrokePath(pathObj, reference, mask);
+}
+
+void Context::coverFillPath(GLuint path, GLenum coverMode)
+{
+    const auto *pathObj = mResourceManager->getPath(path);
+    if (!pathObj)
+        return;
+
+    // TODO(svaisanen@nvidia.com): maybe sync only state required for path rendering?
+    syncRendererState();
+
+    mImplementation->coverFillPath(pathObj, coverMode);
+}
+
+void Context::coverStrokePath(GLuint path, GLenum coverMode)
+{
+    const auto *pathObj = mResourceManager->getPath(path);
+    if (!pathObj)
+        return;
+
+    // TODO(svaisanen@nvidia.com): maybe sync only state required for path rendering?
+    syncRendererState();
+
+    mImplementation->coverStrokePath(pathObj, coverMode);
+}
+
+void Context::stencilThenCoverFillPath(GLuint path, GLenum fillMode, GLuint mask, GLenum coverMode)
+{
+    const auto *pathObj = mResourceManager->getPath(path);
+    if (!pathObj)
+        return;
+
+    // TODO(svaisanen@nvidia.com): maybe sync only state required for path rendering?
+    syncRendererState();
+
+    mImplementation->stencilThenCoverFillPath(pathObj, fillMode, mask, coverMode);
+}
+
+void Context::stencilThenCoverStrokePath(GLuint path,
+                                         GLint reference,
+                                         GLuint mask,
+                                         GLenum coverMode)
+{
+    const auto *pathObj = mResourceManager->getPath(path);
+    if (!pathObj)
+        return;
+
+    // TODO(svaisanen@nvidia.com): maybe sync only state required for path rendering?
+    syncRendererState();
+
+    mImplementation->stencilThenCoverStrokePath(pathObj, reference, mask, coverMode);
+}
+
 void Context::handleError(const Error &error)
 {
     if (error.isError())
diff --git a/src/libANGLE/Context.h b/src/libANGLE/Context.h
index c855a0c..8b6fb07 100644
--- a/src/libANGLE/Context.h
+++ b/src/libANGLE/Context.h
@@ -81,6 +81,7 @@
     GLuint createSampler();
     GLuint createTransformFeedback();
     GLsync createFenceSync();
+    GLuint createPaths(GLsizei range);
 
     void deleteBuffer(GLuint buffer);
     void deleteShader(GLuint shader);
@@ -90,6 +91,20 @@
     void deleteSampler(GLuint sampler);
     void deleteTransformFeedback(GLuint transformFeedback);
     void deleteFenceSync(GLsync fenceSync);
+    void deletePaths(GLuint first, GLsizei range);
+
+    // CHROMIUM_path_rendering
+    bool hasPathData(GLuint path) const;
+    bool hasPath(GLuint path) const;
+    void setPathCommands(GLuint path,
+                         GLsizei numCommands,
+                         const GLubyte *commands,
+                         GLsizei numCoords,
+                         GLenum coordType,
+                         const void *coords);
+    void setPathParameterf(GLuint path, GLenum pname, GLfloat value);
+    void getPathParameterfv(GLuint path, GLenum pname, GLfloat *value) const;
+    void setPathStencilFunc(GLenum func, GLint ref, GLuint mask);
 
     // Framebuffers are owned by the Context, so these methods do not pass through
     GLuint createFramebuffer();
@@ -467,6 +482,16 @@
     // CHROMIUM_framebuffer_mixed_samples
     void setCoverageModulation(GLenum components);
 
+    // CHROMIUM_path_rendering
+    void loadPathRenderingMatrix(GLenum matrixMode, const GLfloat *matrix);
+    void loadPathRenderingIdentityMatrix(GLenum matrixMode);
+    void stencilFillPath(GLuint path, GLenum fillMode, GLuint mask);
+    void stencilStrokePath(GLuint path, GLint reference, GLuint mask);
+    void coverFillPath(GLuint path, GLenum coverMode);
+    void coverStrokePath(GLuint path, GLenum coverMode);
+    void stencilThenCoverFillPath(GLuint path, GLenum fillMode, GLuint mask, GLenum coverMode);
+    void stencilThenCoverStrokePath(GLuint path, GLint reference, GLuint mask, GLenum coverMode);
+
     void handleError(const Error &error) override;
 
     GLenum getError();
diff --git a/src/libANGLE/ContextState.cpp b/src/libANGLE/ContextState.cpp
index 2b39b44..a824be5 100644
--- a/src/libANGLE/ContextState.cpp
+++ b/src/libANGLE/ContextState.cpp
@@ -312,6 +312,18 @@
         }
     }
 
+    if (getExtensions().pathRendering)
+    {
+        switch (pname)
+        {
+            case GL_PATH_MODELVIEW_MATRIX_CHROMIUM:
+            case GL_PATH_PROJECTION_MATRIX_CHROMIUM:
+                *type = GL_FLOAT;
+                *numParams = 16;
+                return true;
+        }
+    }
+
     // Check for ES3.0+ parameter names which are also exposed as ES2 extensions
     switch (pname)
     {
diff --git a/src/libANGLE/HandleRangeAllocator.cpp b/src/libANGLE/HandleRangeAllocator.cpp
new file mode 100644
index 0000000..500a796
--- /dev/null
+++ b/src/libANGLE/HandleRangeAllocator.cpp
@@ -0,0 +1,225 @@
+//
+// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+// HandleRangeAllocator.cpp : Implementation for HandleRangeAllocator.h
+
+#include "libANGLE/HandleRangeAllocator.h"
+
+#include <algorithm>
+#include <limits>
+#include <utility>
+
+#include "common/angleutils.h"
+#include "common/debug.h"
+
+namespace gl
+{
+
+const GLuint HandleRangeAllocator::kInvalidHandle = 0;
+
+HandleRangeAllocator::HandleRangeAllocator()
+{
+    // Simplify the code by making sure that lower_bound(id) never
+    // returns the beginning of the map, if id is valid (eg != kInvalidHandle).
+    mUsed.insert(std::make_pair(0u, 0u));
+}
+
+GLuint HandleRangeAllocator::allocate()
+{
+    return allocateRange(1u);
+}
+
+GLuint HandleRangeAllocator::allocateAtOrAbove(GLuint wanted)
+{
+    if (wanted == 0u || wanted == 1u)
+        return allocateRange(1u);
+
+    auto current = mUsed.lower_bound(wanted);
+    auto next = current;
+    if (current == mUsed.end() || current->first > wanted)
+    {
+        current--;
+    }
+    else
+    {
+        next++;
+    }
+
+    GLuint firstId = current->first;
+    GLuint lastId = current->second;
+    ASSERT(wanted >= firstId);
+
+    if (wanted - 1u <= lastId)
+    {
+        // Append to current range.
+        lastId++;
+        if (lastId == 0)
+        {
+            // The increment overflowed.
+            return allocateRange(1u);
+        }
+
+        current->second = lastId;
+
+        if (next != mUsed.end() && next->first - 1u == lastId)
+        {
+            // Merge with next range.
+            current->second = next->second;
+            mUsed.erase(next);
+        }
+        return lastId;
+    }
+    else if (next != mUsed.end() && next->first - 1u == wanted)
+    {
+        // Prepend to next range.
+        GLuint lastExisting = next->second;
+        mUsed.erase(next);
+        mUsed.insert(std::make_pair(wanted, lastExisting));
+        return wanted;
+    }
+    mUsed.insert(std::make_pair(wanted, wanted));
+    return wanted;
+}
+
+GLuint HandleRangeAllocator::allocateRange(GLuint range)
+{
+    ASSERT(range != 0);
+
+    auto current = mUsed.begin();
+    auto next    = current;
+
+    while (++next != mUsed.end())
+    {
+        if (next->first - current->second > range)
+            break;
+        current = next;
+    }
+    const GLuint firstId = current->second + 1u;
+    const GLuint lastId  = firstId + range - 1u;
+
+    // deal with wraparound
+    if (firstId == 0u || lastId < firstId)
+        return kInvalidHandle;
+
+    current->second = lastId;
+
+    if (next != mUsed.end() && next->first - 1u == lastId)
+    {
+        // merge with next range
+        current->second = next->second;
+        mUsed.erase(next);
+    }
+    return firstId;
+}
+
+bool HandleRangeAllocator::markAsUsed(GLuint handle)
+{
+    ASSERT(handle);
+    auto current = mUsed.lower_bound(handle);
+    if (current != mUsed.end() && current->first == handle)
+        return false;
+
+    auto next = current;
+    --current;
+
+    if (current->second >= handle)
+        return false;
+
+    ASSERT(current->first < handle && current->second < handle);
+
+    if (current->second + 1u == handle)
+    {
+        // Append to current range.
+        current->second = handle;
+        if (next != mUsed.end() && next->first - 1u == handle)
+        {
+            // Merge with next range.
+            current->second = next->second;
+            mUsed.erase(next);
+        }
+        return true;
+    }
+    else if (next != mUsed.end() && next->first - 1u == handle)
+    {
+        // Prepend to next range.
+        GLuint lastExisting = next->second;
+        mUsed.erase(next);
+        mUsed.insert(std::make_pair(handle, lastExisting));
+        return true;
+    }
+
+    mUsed.insert(std::make_pair(handle, handle));
+    return true;
+}
+
+void HandleRangeAllocator::release(GLuint handle)
+{
+    releaseRange(handle, 1u);
+}
+
+void HandleRangeAllocator::releaseRange(GLuint first, GLuint range)
+{
+    if (range == 0u || (first == 0u && range == 1u))
+        return;
+
+    if (first == 0u)
+    {
+        first++;
+        range--;
+    }
+
+    GLuint last = first + range - 1u;
+    if (last < first)
+        last = std::numeric_limits<GLuint>::max();
+
+    while (true)
+    {
+        auto current = mUsed.lower_bound(last);
+        if (current == mUsed.end() || current->first > last)
+            --current;
+
+        if (current->second < first)
+            return;
+
+        if (current->first >= first)
+        {
+            const GLuint lastExisting = current->second;
+            mUsed.erase(current);
+            if (last < lastExisting)
+            {
+                mUsed.insert(std::make_pair(last + 1u, lastExisting));
+            }
+        }
+        else if (current->second <= last)
+        {
+            current->second = first - 1u;
+        }
+        else
+        {
+            ASSERT(current->first < first && current->second > last);
+            const GLuint lastExisting = current->second;
+            current->second = first - 1u;
+            mUsed.insert(std::make_pair(last + 1u, lastExisting));
+        }
+    }
+}
+
+bool HandleRangeAllocator::isUsed(GLuint handle) const
+{
+    if (handle == kInvalidHandle)
+        return false;
+
+    auto current = mUsed.lower_bound(handle);
+    if (current != mUsed.end())
+    {
+        if (current->first == handle)
+            return true;
+    }
+    --current;
+    return current->second >= handle;
+}
+
+}  // namespace gl
\ No newline at end of file
diff --git a/src/libANGLE/HandleRangeAllocator.h b/src/libANGLE/HandleRangeAllocator.h
new file mode 100644
index 0000000..20f9a11
--- /dev/null
+++ b/src/libANGLE/HandleRangeAllocator.h
@@ -0,0 +1,59 @@
+//
+// Copyright (c) 2002-2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+// HandleRangeAllocator.h: Defines the gl::HandleRangeAllocator class, which is used to
+// allocate contiguous ranges of GL path handles.
+
+#ifndef LIBANGLE_HANDLERANGEALLOCATOR_H_
+#define LIBANGLE_HANDLERANGEALLOCATOR_H_
+
+#include <map>
+
+#include "angle_gl.h"
+#include "common/angleutils.h"
+
+namespace gl
+{
+
+// Allocates contiguous ranges of path object handles.
+class HandleRangeAllocator final : angle::NonCopyable
+{
+  public:
+    static const GLuint kInvalidHandle;
+
+    HandleRangeAllocator();
+
+    // Allocates a new path handle.
+    GLuint allocate();
+
+    // Allocates a handle starting at or above the value of |wanted|.
+    // Note: may wrap if it starts near limit.
+    GLuint allocateAtOrAbove(GLuint wanted);
+
+    // Allocates |range| amount of contiguous paths.
+    // Returns the first id to |first_id| or |kInvalidHandle| if
+    // allocation failed.
+    GLuint allocateRange(GLuint range);
+
+    // Marks an id as used. Returns false if handle was already used.
+    bool markAsUsed(GLuint handle);
+
+    // Release handle.
+    void release(GLuint handle);
+
+    // Release a |range| amount of contiguous handles, starting from |first|
+    void releaseRange(GLuint first, GLuint range);
+
+    // Checks whether or not a resource ID is in use.
+    bool isUsed(GLuint handle) const;
+
+  private:
+    std::map<GLuint, GLuint> mUsed;
+};
+
+}  // namespace gl
+
+#endif  // LIBANGLE_HANDLERANGEALLOCATOR_H_
\ No newline at end of file
diff --git a/src/libANGLE/HandleRangeAllocator_unittest.cpp b/src/libANGLE/HandleRangeAllocator_unittest.cpp
new file mode 100644
index 0000000..1d825f7
--- /dev/null
+++ b/src/libANGLE/HandleRangeAllocator_unittest.cpp
@@ -0,0 +1,275 @@
+//
+// Copyright 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Unit tests for HandleRangeAllocator.
+//
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+#include "libANGLE/HandleRangeAllocator.h"
+
+namespace
+{
+
+class HandleRangeAllocatorTest : public testing::Test
+{
+  protected:
+    gl::HandleRangeAllocator *getAllocator() { return &mAllocator; }
+
+  private:
+    gl::HandleRangeAllocator mAllocator;
+};
+
+// Checks basic functionality: allocate, release, isUsed.
+TEST_F(HandleRangeAllocatorTest, TestBasic)
+{
+    auto *allocator = getAllocator();
+    // Check that resource 1 is not in use
+    EXPECT_FALSE(allocator->isUsed(1));
+
+    // Allocate an ID, check that it's in use.
+    GLuint id1 = allocator->allocate();
+    EXPECT_TRUE(allocator->isUsed(id1));
+
+    // Allocate another ID, check that it's in use, and different from the first
+    // one.
+    GLuint id2 = allocator->allocate();
+    EXPECT_TRUE(allocator->isUsed(id2));
+    EXPECT_NE(id1, id2);
+
+    // Free one of the IDs, check that it's not in use any more.
+    allocator->release(id1);
+    EXPECT_FALSE(allocator->isUsed(id1));
+
+    // Frees the other ID, check that it's not in use any more.
+    allocator->release(id2);
+    EXPECT_FALSE(allocator->isUsed(id2));
+}
+
+// Checks that the resource handles are re-used after being freed.
+TEST_F(HandleRangeAllocatorTest, TestAdvanced)
+{
+    auto *allocator = getAllocator();
+
+    // Allocate the highest possible ID, to make life awkward.
+    allocator->allocateAtOrAbove(~static_cast<GLuint>(0));
+
+    // Allocate a significant number of resources.
+    const unsigned int kNumResources = 100;
+    GLuint ids[kNumResources];
+    for (unsigned int i = 0; i < kNumResources; ++i)
+    {
+        ids[i] = allocator->allocate();
+        EXPECT_TRUE(allocator->isUsed(ids[i]));
+    }
+
+    // Check that a new allocation re-uses the resource we just freed.
+    GLuint id1 = ids[kNumResources / 2];
+    allocator->release(id1);
+    EXPECT_FALSE(allocator->isUsed(id1));
+    GLuint id2 = allocator->allocate();
+    EXPECT_TRUE(allocator->isUsed(id2));
+    EXPECT_EQ(id1, id2);
+}
+
+// Checks that we can choose our own ids and they won't be reused.
+TEST_F(HandleRangeAllocatorTest, MarkAsUsed)
+{
+    auto *allocator = getAllocator();
+    GLuint id = allocator->allocate();
+    allocator->release(id);
+    EXPECT_FALSE(allocator->isUsed(id));
+    EXPECT_TRUE(allocator->markAsUsed(id));
+    EXPECT_TRUE(allocator->isUsed(id));
+    GLuint id2 = allocator->allocate();
+    EXPECT_NE(id, id2);
+    EXPECT_TRUE(allocator->markAsUsed(id2 + 1));
+    GLuint id3 = allocator->allocate();
+    // Checks our algorithm. If the algorithm changes this check should be
+    // changed.
+    EXPECT_EQ(id3, id2 + 2);
+}
+
+// Checks allocateAtOrAbove.
+TEST_F(HandleRangeAllocatorTest, AllocateAtOrAbove)
+{
+    const GLuint kOffset = 123456;
+    auto *allocator      = getAllocator();
+    GLuint id1 = allocator->allocateAtOrAbove(kOffset);
+    EXPECT_EQ(kOffset, id1);
+    GLuint id2 = allocator->allocateAtOrAbove(kOffset);
+    EXPECT_GT(id2, kOffset);
+    GLuint id3 = allocator->allocateAtOrAbove(kOffset);
+    EXPECT_GT(id3, kOffset);
+}
+
+// Checks that allocateAtOrAbove wraps around at the maximum value.
+TEST_F(HandleRangeAllocatorTest, AllocateIdAtOrAboveWrapsAround)
+{
+    const GLuint kMaxPossibleOffset = ~static_cast<GLuint>(0);
+    auto *allocator                 = getAllocator();
+    GLuint id1 = allocator->allocateAtOrAbove(kMaxPossibleOffset);
+    EXPECT_EQ(kMaxPossibleOffset, id1);
+    GLuint id2 = allocator->allocateAtOrAbove(kMaxPossibleOffset);
+    EXPECT_EQ(1u, id2);
+    GLuint id3 = allocator->allocateAtOrAbove(kMaxPossibleOffset);
+    EXPECT_EQ(2u, id3);
+}
+
+// Checks that freeing an already freed range causes no harm.
+TEST_F(HandleRangeAllocatorTest, RedundantFreeIsIgnored)
+{
+    auto *allocator = getAllocator();
+    GLuint id1 = allocator->allocate();
+    allocator->release(0);
+    allocator->release(id1);
+    allocator->release(id1);
+    allocator->release(id1 + 1);
+    GLuint id2 = allocator->allocate();
+    GLuint id3 = allocator->allocate();
+    EXPECT_NE(id2, id3);
+    EXPECT_NE(allocator->kInvalidHandle, id2);
+    EXPECT_NE(allocator->kInvalidHandle, id3);
+}
+
+// Check allocating and releasing multiple ranges.
+TEST_F(HandleRangeAllocatorTest, allocateRange)
+{
+    const GLuint kMaxPossibleOffset = std::numeric_limits<GLuint>::max();
+
+    auto *allocator = getAllocator();
+
+    GLuint id1 = allocator->allocateRange(1);
+    EXPECT_EQ(1u, id1);
+    GLuint id2 = allocator->allocateRange(2);
+    EXPECT_EQ(2u, id2);
+    GLuint id3 = allocator->allocateRange(3);
+    EXPECT_EQ(4u, id3);
+    GLuint id4 = allocator->allocate();
+    EXPECT_EQ(7u, id4);
+    allocator->release(3);
+    GLuint id5 = allocator->allocateRange(1);
+    EXPECT_EQ(3u, id5);
+    allocator->release(5);
+    allocator->release(2);
+    allocator->release(4);
+    GLuint id6 = allocator->allocateRange(2);
+    EXPECT_EQ(4u, id6);
+    GLuint id7 = allocator->allocateAtOrAbove(kMaxPossibleOffset);
+    EXPECT_EQ(kMaxPossibleOffset, id7);
+    GLuint id8 = allocator->allocateAtOrAbove(kMaxPossibleOffset);
+    EXPECT_EQ(2u, id8);
+    GLuint id9 = allocator->allocateRange(50);
+    EXPECT_EQ(8u, id9);
+    GLuint id10 = allocator->allocateRange(50);
+    EXPECT_EQ(58u, id10);
+    // Remove all the low-numbered ids.
+    allocator->release(1);
+    allocator->release(15);
+    allocator->releaseRange(2, 107);
+    GLuint id11 = allocator->allocateRange(100);
+    EXPECT_EQ(1u, id11);
+    allocator->release(kMaxPossibleOffset);
+    GLuint id12 = allocator->allocateRange(100);
+    EXPECT_EQ(101u, id12);
+
+    GLuint id13 = allocator->allocateAtOrAbove(kMaxPossibleOffset - 2u);
+    EXPECT_EQ(kMaxPossibleOffset - 2u, id13);
+    GLuint id14 = allocator->allocateRange(3);
+    EXPECT_EQ(201u, id14);
+}
+
+// Checks that having allocated a high range doesn't interfere
+// with normal low range allocation.
+TEST_F(HandleRangeAllocatorTest, AllocateRangeEndNoEffect)
+{
+    const GLuint kMaxPossibleOffset = std::numeric_limits<GLuint>::max();
+
+    auto *allocator = getAllocator();
+    GLuint id1 = allocator->allocateAtOrAbove(kMaxPossibleOffset - 2u);
+    EXPECT_EQ(kMaxPossibleOffset - 2u, id1);
+    GLuint id3 = allocator->allocateRange(3);
+    EXPECT_EQ(1u, id3);
+    GLuint id2 = allocator->allocateRange(2);
+    EXPECT_EQ(4u, id2);
+}
+
+// Checks allocating a range that consumes the whole uint32 space.
+TEST_F(HandleRangeAllocatorTest, AllocateMax)
+{
+    const uint32_t kMaxPossibleRange = std::numeric_limits<uint32_t>::max();
+
+    auto *allocator = getAllocator();
+    GLuint id = allocator->allocateRange(kMaxPossibleRange);
+    EXPECT_EQ(1u, id);
+    allocator->releaseRange(id, kMaxPossibleRange - 1u);
+    GLuint id2 = allocator->allocateRange(kMaxPossibleRange);
+    EXPECT_EQ(0u, id2);
+    allocator->releaseRange(id, kMaxPossibleRange);
+    GLuint id3 = allocator->allocateRange(kMaxPossibleRange);
+    EXPECT_EQ(1u, id3);
+}
+
+// Checks allocating a range that consumes the whole uint32 space
+// causes next allocation to fail.
+// Subsequently checks that once the big range is reduced new allocations
+// are possible.
+TEST_F(HandleRangeAllocatorTest, AllocateFullRange)
+{
+    const uint32_t kMaxPossibleRange = std::numeric_limits<uint32_t>::max();
+    const GLuint kFreedId            = 555u;
+    auto *allocator                  = getAllocator();
+
+    GLuint id1 = allocator->allocateRange(kMaxPossibleRange);
+    EXPECT_EQ(1u, id1);
+    GLuint id2 = allocator->allocate();
+    EXPECT_EQ(gl::HandleRangeAllocator::kInvalidHandle, id2);
+    allocator->release(kFreedId);
+    GLuint id3 = allocator->allocate();
+    EXPECT_EQ(kFreedId, id3);
+    GLuint id4 = allocator->allocate();
+    EXPECT_EQ(0u, id4);
+    allocator->release(kFreedId + 1u);
+    allocator->release(kFreedId + 4u);
+    allocator->release(kFreedId + 3u);
+    allocator->release(kFreedId + 5u);
+    allocator->release(kFreedId + 2u);
+    GLuint id5 = allocator->allocateRange(5);
+    EXPECT_EQ(kFreedId + 1u, id5);
+}
+
+// Checks that allocating a range that exceeds uint32
+// does not wrap incorrectly and fails.
+TEST_F(HandleRangeAllocatorTest, AllocateRangeNoWrapInRange)
+{
+    const uint32_t kMaxPossibleRange = std::numeric_limits<uint32_t>::max();
+    const GLuint kAllocId            = 10u;
+    auto *allocator                  = getAllocator();
+
+    GLuint id1 = allocator->allocateAtOrAbove(kAllocId);
+    EXPECT_EQ(kAllocId, id1);
+    GLuint id2 = allocator->allocateRange(kMaxPossibleRange - 5u);
+    EXPECT_EQ(0u, id2);
+    GLuint id3 = allocator->allocateRange(kMaxPossibleRange - kAllocId);
+    EXPECT_EQ(kAllocId + 1u, id3);
+}
+
+// Check special cases for 0 range allocations and zero handles.
+TEST_F(HandleRangeAllocatorTest, ZeroIdCases)
+{
+    auto *allocator = getAllocator();
+    EXPECT_FALSE(allocator->isUsed(0));
+    GLuint id1 = allocator->allocateAtOrAbove(0);
+    EXPECT_NE(0u, id1);
+    EXPECT_FALSE(allocator->isUsed(0));
+    allocator->release(0);
+    EXPECT_FALSE(allocator->isUsed(0));
+    EXPECT_TRUE(allocator->isUsed(id1));
+    allocator->release(id1);
+    EXPECT_FALSE(allocator->isUsed(id1));
+}
+
+}  // namespace
\ No newline at end of file
diff --git a/src/libANGLE/Path.cpp b/src/libANGLE/Path.cpp
new file mode 100644
index 0000000..e0c45db
--- /dev/null
+++ b/src/libANGLE/Path.cpp
@@ -0,0 +1,78 @@
+//
+// Copyright (c) 2002-2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+// Path.h: Defines the gl::Path class, representing CHROMIUM_path_rendering
+// path object.
+
+#include "libANGLE/Path.h"
+#include "libANGLE/renderer/PathImpl.h"
+
+#include "common/mathutil.h"
+#include "common/debug.h"
+
+namespace gl
+{
+
+Path::Path(rx::PathImpl *impl)
+    : mPath(impl),
+      mHasData(false),
+      mEndCaps(GL_FLAT_CHROMIUM),
+      mJoinStyle(GL_MITER_REVERT_CHROMIUM),
+      mStrokeWidth(1.0f),
+      mStrokeBound(0.2f),
+      mMiterLimit(4.0f)
+{
+}
+
+Path::~Path()
+{
+    delete mPath;
+}
+
+Error Path::setCommands(GLsizei numCommands,
+                        const GLubyte *commands,
+                        GLsizei numCoords,
+                        GLenum coordType,
+                        const void *coords)
+{
+    ANGLE_TRY(mPath->setCommands(numCommands, commands, numCoords, coordType, coords));
+
+    mHasData = true;
+
+    return gl::NoError();
+}
+
+void Path::setStrokeWidth(GLfloat width)
+{
+    mStrokeWidth = width;
+    mPath->setPathParameter(GL_PATH_STROKE_WIDTH_CHROMIUM, mStrokeWidth);
+}
+
+void Path::setStrokeBound(GLfloat bound)
+{
+    mStrokeBound = clamp(bound, 0.0f, 1.0f);
+    mPath->setPathParameter(GL_PATH_STROKE_BOUND_CHROMIUM, mStrokeBound);
+}
+
+void Path::setEndCaps(GLenum type)
+{
+    mEndCaps = type;
+    mPath->setPathParameter(GL_PATH_END_CAPS_CHROMIUM, static_cast<GLfloat>(type));
+}
+
+void Path::setJoinStyle(GLenum type)
+{
+    mJoinStyle = type;
+    mPath->setPathParameter(GL_PATH_JOIN_STYLE_CHROMIUM, static_cast<GLfloat>(type));
+}
+
+void Path::setMiterLimit(GLfloat value)
+{
+    mMiterLimit = value;
+    mPath->setPathParameter(GL_PATH_MITER_LIMIT_CHROMIUM, value);
+}
+
+}  // gl
diff --git a/src/libANGLE/Path.h b/src/libANGLE/Path.h
new file mode 100644
index 0000000..40e2cf2
--- /dev/null
+++ b/src/libANGLE/Path.h
@@ -0,0 +1,73 @@
+//
+// Copyright (c) 2002-2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+// Path.h: Defines the gl::Path class, representing CHROMIUM_path_rendering
+// path object.
+
+#ifndef LIBANGLE_PATH_H_
+#define LIBANGLE_PATH_H_
+
+#include "angle_gl.h"
+#include "common/angleutils.h"
+#include "libANGLE/Error.h"
+#include "libANGLE/RefCountObject.h"
+
+namespace rx
+{
+class PathImpl;
+}
+
+namespace gl
+{
+class Path final : angle::NonCopyable
+{
+  public:
+    Path(rx::PathImpl *impl);
+
+    ~Path();
+
+    Error setCommands(GLsizei numCommands,
+                      const GLubyte *commands,
+                      GLsizei numCoords,
+                      GLenum coordType,
+                      const void *coords);
+
+    void setStrokeWidth(GLfloat width);
+    void setStrokeBound(GLfloat bound);
+    void setEndCaps(GLenum type);
+    void setJoinStyle(GLenum type);
+    void setMiterLimit(GLfloat value);
+
+    GLfloat getStrokeWidth() const { return mStrokeWidth; }
+    GLfloat getStrokeBound() const { return mStrokeBound; }
+    GLfloat getMiterLimit() const { return mMiterLimit; }
+    GLenum getEndCaps() const { return mEndCaps; }
+    GLenum getJoinStyle() const { return mJoinStyle; }
+
+    bool hasPathData() const { return mHasData; }
+
+    const rx::PathImpl *getImplementation() const { return mPath; }
+
+    rx::PathImpl *getImplementation() { return mPath; }
+
+  private:
+    rx::PathImpl *mPath;
+
+    // a Path object is not actually considered "a path"
+    // untill it has been specified with data. So we'll
+    // keep this flag to support this semantics.
+    bool mHasData;
+
+    GLenum mEndCaps;
+    GLenum mJoinStyle;
+    GLfloat mStrokeWidth;
+    GLfloat mStrokeBound;
+    GLfloat mMiterLimit;
+};
+
+}  // namespace gl
+
+#endif  // LIBANGLE_PATH_H_
\ No newline at end of file
diff --git a/src/libANGLE/ResourceManager.cpp b/src/libANGLE/ResourceManager.cpp
index 7fb7109..7f96387 100644
--- a/src/libANGLE/ResourceManager.cpp
+++ b/src/libANGLE/ResourceManager.cpp
@@ -4,13 +4,14 @@
 // found in the LICENSE file.
 //
 
-// ResourceManager.cpp: Implements the gl::ResourceManager class, which tracks and 
+// ResourceManager.cpp: Implements the gl::ResourceManager class, which tracks and
 // retrieves objects which may be shared by multiple Contexts.
 
 #include "libANGLE/ResourceManager.h"
 
 #include "libANGLE/Buffer.h"
 #include "libANGLE/Fence.h"
+#include "libANGLE/Path.h"
 #include "libANGLE/Program.h"
 #include "libANGLE/Renderbuffer.h"
 #include "libANGLE/Sampler.h"
@@ -60,6 +61,12 @@
     {
         deleteFenceSync(mFenceSyncMap.begin()->first);
     }
+
+    for (auto it = mPathMap.begin(); it != mPathMap.end(); ++it)
+    {
+        const auto *p = it->second;
+        delete p;
+    }
 }
 
 void ResourceManager::addRef()
@@ -153,6 +160,31 @@
     return handle;
 }
 
+ErrorOrResult<GLuint> ResourceManager::createPaths(rx::GLImplFactory *factory, GLsizei range)
+{
+    // Allocate client side handles.
+    const GLuint client = mPathHandleAllocator.allocateRange(static_cast<GLuint>(range));
+    if (client == HandleRangeAllocator::kInvalidHandle)
+        return gl::Error(GL_OUT_OF_MEMORY, "Failed to allocate path handle range.");
+
+    const auto &paths = factory->createPaths(range);
+    if (paths.empty())
+    {
+        mPathHandleAllocator.releaseRange(client, range);
+        return gl::Error(GL_OUT_OF_MEMORY, "Failed to allocate path objects.");
+    }
+
+    auto hint = mPathMap.begin();
+
+    for (GLsizei i = 0; i < range; ++i)
+    {
+        const auto impl = paths[static_cast<unsigned>(i)];
+        const auto id   = client + i;
+        hint            = mPathMap.insert(hint, std::make_pair(id, new Path(impl)));
+    }
+    return client;
+}
+
 void ResourceManager::deleteBuffer(GLuint buffer)
 {
     auto bufferObject = mBufferMap.find(buffer);
@@ -197,7 +229,7 @@
             mProgramMap.erase(programObject);
         }
         else
-        { 
+        {
             programObject->second->flagForDeletion();
         }
     }
@@ -251,6 +283,21 @@
     }
 }
 
+void ResourceManager::deletePaths(GLuint first, GLsizei range)
+{
+    for (GLsizei i = 0; i < range; ++i)
+    {
+        const auto id = first + i;
+        const auto it = mPathMap.find(id);
+        if (it == mPathMap.end())
+            continue;
+        Path *p = it->second;
+        delete p;
+        mPathMap.erase(it);
+    }
+    mPathHandleAllocator.releaseRange(first, static_cast<GLuint>(range));
+}
+
 Buffer *ResourceManager::getBuffer(unsigned int handle)
 {
     auto buffer = mBufferMap.find(handle);
@@ -352,6 +399,28 @@
     }
 }
 
+const Path *ResourceManager::getPath(GLuint handle) const
+{
+    auto it = mPathMap.find(handle);
+    if (it == std::end(mPathMap))
+        return nullptr;
+    return it->second;
+}
+
+Path *ResourceManager::getPath(GLuint handle)
+{
+    auto it = mPathMap.find(handle);
+    if (it == std::end(mPathMap))
+        return nullptr;
+
+    return it->second;
+}
+
+bool ResourceManager::hasPath(GLuint handle) const
+{
+    return mPathHandleAllocator.isUsed(handle);
+}
+
 void ResourceManager::setRenderbuffer(GLuint handle, Renderbuffer *buffer)
 {
     mRenderbufferMap[handle] = buffer;
diff --git a/src/libANGLE/ResourceManager.h b/src/libANGLE/ResourceManager.h
index 9001315..a673257 100644
--- a/src/libANGLE/ResourceManager.h
+++ b/src/libANGLE/ResourceManager.h
@@ -13,7 +13,9 @@
 #include "angle_gl.h"
 #include "common/angleutils.h"
 #include "libANGLE/angletypes.h"
+#include "libANGLE/Error.h"
 #include "libANGLE/HandleAllocator.h"
+#include "libANGLE/HandleRangeAllocator.h"
 
 namespace rx
 {
@@ -25,6 +27,7 @@
 class Buffer;
 class FenceSync;
 struct Limitations;
+class Path;
 class Program;
 class Renderbuffer;
 class Sampler;
@@ -49,6 +52,7 @@
     GLuint createRenderbuffer();
     GLuint createSampler();
     GLuint createFenceSync(rx::GLImplFactory *factory);
+    ErrorOrResult<GLuint> createPaths(rx::GLImplFactory *factory, GLsizei range);
 
     void deleteBuffer(GLuint buffer);
     void deleteShader(GLuint shader);
@@ -57,6 +61,7 @@
     void deleteRenderbuffer(GLuint renderbuffer);
     void deleteSampler(GLuint sampler);
     void deleteFenceSync(GLuint fenceSync);
+    void deletePaths(GLuint first, GLsizei range);
 
     Buffer *getBuffer(GLuint handle);
     Shader *getShader(GLuint handle);
@@ -66,6 +71,11 @@
     Sampler *getSampler(GLuint handle);
     FenceSync *getFenceSync(GLuint handle);
 
+    // CHROMIUM_path_rendering
+    const Path *getPath(GLuint path) const;
+    Path *getPath(GLuint path);
+    bool hasPath(GLuint path) const;
+
     void setRenderbuffer(GLuint handle, Renderbuffer *renderbuffer);
 
     Buffer *checkBufferAllocation(rx::GLImplFactory *factory, GLuint handle);
@@ -78,7 +88,6 @@
   private:
     void createTextureInternal(GLuint handle);
 
-    ;
     std::size_t mRefCount;
 
     ResourceMap<Buffer> mBufferMap;
@@ -100,6 +109,9 @@
 
     ResourceMap<FenceSync> mFenceSyncMap;
     HandleAllocator mFenceSyncHandleAllocator;
+
+    ResourceMap<Path> mPathMap;
+    HandleRangeAllocator mPathHandleAllocator;
 };
 
 }  // namespace gl
diff --git a/src/libANGLE/State.cpp b/src/libANGLE/State.cpp
index 08a6b8c..a5ae996 100644
--- a/src/libANGLE/State.cpp
+++ b/src/libANGLE/State.cpp
@@ -8,7 +8,12 @@
 
 #include "libANGLE/State.h"
 
+#include <limits>
+#include <string.h>
+
 #include "common/BitSetIterator.h"
+#include "common/matrix_utils.h"
+#include "common/mathutil.h"
 #include "libANGLE/Context.h"
 #include "libANGLE/Caps.h"
 #include "libANGLE/Debug.h"
@@ -180,6 +185,12 @@
     }
 
     mCoverageModulation = GL_NONE;
+
+    angle::Matrix<GLfloat>::setToIdentity(mPathMatrixProj);
+    angle::Matrix<GLfloat>::setToIdentity(mPathMatrixMV);
+    mPathStencilFunc = GL_ALWAYS;
+    mPathStencilRef  = 0;
+    mPathStencilMask = std::numeric_limits<GLuint>::max();
 }
 
 void State::reset()
@@ -227,6 +238,12 @@
 
     mProgram = NULL;
 
+    angle::Matrix<GLfloat>::setToIdentity(mPathMatrixProj);
+    angle::Matrix<GLfloat>::setToIdentity(mPathMatrixMV);
+    mPathStencilFunc = GL_ALWAYS;
+    mPathStencilRef  = 0;
+    mPathStencilMask = std::numeric_limits<GLuint>::max();
+
     // TODO(jmadill): Is this necessary?
     setAllDirtyBits();
 }
@@ -1362,6 +1379,62 @@
     return mCoverageModulation;
 }
 
+void State::loadPathRenderingMatrix(GLenum matrixMode, const GLfloat *matrix)
+{
+    if (matrixMode == GL_PATH_MODELVIEW_CHROMIUM)
+    {
+        memcpy(mPathMatrixMV, matrix, 16 * sizeof(GLfloat));
+        mDirtyBits.set(DIRTY_BIT_PATH_RENDERING_MATRIX_MV);
+    }
+    else if (matrixMode == GL_PATH_PROJECTION_CHROMIUM)
+    {
+        memcpy(mPathMatrixProj, matrix, 16 * sizeof(GLfloat));
+        mDirtyBits.set(DIRTY_BIT_PATH_RENDERING_MATRIX_PROJ);
+    }
+    else
+    {
+        UNREACHABLE();
+    }
+}
+
+const GLfloat *State::getPathRenderingMatrix(GLenum which) const
+{
+    if (which == GL_PATH_MODELVIEW_MATRIX_CHROMIUM)
+    {
+        return mPathMatrixMV;
+    }
+    else if (which == GL_PATH_PROJECTION_MATRIX_CHROMIUM)
+    {
+        return mPathMatrixProj;
+    }
+
+    UNREACHABLE();
+    return nullptr;
+}
+
+void State::setPathStencilFunc(GLenum func, GLint ref, GLuint mask)
+{
+    mPathStencilFunc = func;
+    mPathStencilRef  = ref;
+    mPathStencilMask = mask;
+    mDirtyBits.set(DIRTY_BIT_PATH_RENDERING_STENCIL_STATE);
+}
+
+GLenum State::getPathStencilFunc() const
+{
+    return mPathStencilFunc;
+}
+
+GLint State::getPathStencilRef() const
+{
+    return mPathStencilRef;
+}
+
+GLuint State::getPathStencilMask() const
+{
+    return mPathStencilMask;
+}
+
 void State::getBooleanv(GLenum pname, GLboolean *params)
 {
     switch (pname)
diff --git a/src/libANGLE/State.h b/src/libANGLE/State.h
index 5aa8b68..5089139 100644
--- a/src/libANGLE/State.h
+++ b/src/libANGLE/State.h
@@ -276,6 +276,15 @@
     void setCoverageModulation(GLenum components);
     GLenum getCoverageModulation() const;
 
+    // CHROMIUM_path_rendering
+    void loadPathRenderingMatrix(GLenum matrixMode, const GLfloat *matrix);
+    const GLfloat *getPathRenderingMatrix(GLenum which) const;
+    void setPathStencilFunc(GLenum func, GLint ref, GLuint mask);
+
+    GLenum getPathStencilFunc() const;
+    GLint getPathStencilRef() const;
+    GLuint getPathStencilMask() const;
+
     // State query functions
     void getBooleanv(GLenum pname, GLboolean *params);
     void getFloatv(GLenum pname, GLfloat *params);
@@ -344,7 +353,10 @@
         DIRTY_BIT_PROGRAM_BINDING,
         DIRTY_BIT_MULTISAMPLING,
         DIRTY_BIT_SAMPLE_ALPHA_TO_ONE,
-        DIRTY_BIT_COVERAGE_MODULATION,  // CHROMIUM_framebuffer_mixed_samples
+        DIRTY_BIT_COVERAGE_MODULATION,         // CHROMIUM_framebuffer_mixed_samples
+        DIRTY_BIT_PATH_RENDERING_MATRIX_MV,    // CHROMIUM_path_rendering path model view matrix
+        DIRTY_BIT_PATH_RENDERING_MATRIX_PROJ,  // CHROMIUM_path_rendering path projection matrix
+        DIRTY_BIT_PATH_RENDERING_STENCIL_STATE,
         DIRTY_BIT_CURRENT_VALUE_0,
         DIRTY_BIT_CURRENT_VALUE_MAX = DIRTY_BIT_CURRENT_VALUE_0 + MAX_VERTEX_ATTRIBS,
         DIRTY_BIT_INVALID           = DIRTY_BIT_CURRENT_VALUE_MAX,
@@ -452,6 +464,13 @@
 
     GLenum mCoverageModulation;
 
+    // CHROMIUM_path_rendering
+    GLfloat mPathMatrixMV[16];
+    GLfloat mPathMatrixProj[16];
+    GLenum mPathStencilFunc;
+    GLint mPathStencilRef;
+    GLuint mPathStencilMask;
+
     DirtyBits mDirtyBits;
     DirtyObjects mDirtyObjects;
 };
diff --git a/src/libANGLE/renderer/ContextImpl.cpp b/src/libANGLE/renderer/ContextImpl.cpp
index e02b5a7..c08ab5a 100644
--- a/src/libANGLE/renderer/ContextImpl.cpp
+++ b/src/libANGLE/renderer/ContextImpl.cpp
@@ -20,4 +20,40 @@
 {
 }
 
+void ContextImpl::stencilFillPath(const gl::Path *path, GLenum fillMode, GLuint mask)
+{
+    UNREACHABLE();
+}
+
+void ContextImpl::stencilStrokePath(const gl::Path *path, GLint reference, GLuint mask)
+{
+    UNREACHABLE();
+}
+
+void ContextImpl::coverFillPath(const gl::Path *path, GLenum coverMode)
+{
+    UNREACHABLE();
+}
+
+void ContextImpl::coverStrokePath(const gl::Path *path, GLenum coverMode)
+{
+    UNREACHABLE();
+}
+
+void ContextImpl::stencilThenCoverFillPath(const gl::Path *path,
+                                           GLenum fillMode,
+                                           GLuint mask,
+                                           GLenum coverMode)
+{
+    UNREACHABLE();
+}
+
+void ContextImpl::stencilThenCoverStrokePath(const gl::Path *path,
+                                             GLint reference,
+                                             GLuint mask,
+                                             GLenum coverMode)
+{
+    UNREACHABLE();
+}
+
 }  // namespace rx
diff --git a/src/libANGLE/renderer/ContextImpl.h b/src/libANGLE/renderer/ContextImpl.h
index 25c017c..7283b95 100644
--- a/src/libANGLE/renderer/ContextImpl.h
+++ b/src/libANGLE/renderer/ContextImpl.h
@@ -14,6 +14,11 @@
 #include "libANGLE/ContextState.h"
 #include "libANGLE/renderer/GLImplFactory.h"
 
+namespace gl
+{
+class Path;
+}
+
 namespace rx
 {
 class ContextImpl : public GLImplFactory
@@ -54,6 +59,21 @@
                                         const GLvoid *indices,
                                         const gl::IndexRange &indexRange) = 0;
 
+    // CHROMIUM_path_rendering path drawing methods.
+    virtual void stencilFillPath(const gl::Path *path, GLenum fillMode, GLuint mask);
+    virtual void stencilStrokePath(const gl::Path *path, GLint reference, GLuint mask);
+    virtual void coverFillPath(const gl::Path *path, GLenum coverMode);
+    virtual void coverStrokePath(const gl::Path *path, GLenum coverMode);
+    virtual void stencilThenCoverFillPath(const gl::Path *path,
+                                          GLenum fillMode,
+                                          GLuint mask,
+                                          GLenum coverMode);
+
+    virtual void stencilThenCoverStrokePath(const gl::Path *path,
+                                            GLint reference,
+                                            GLuint mask,
+                                            GLenum coverMode);
+
     // TODO(jmadill): Investigate proper impl methods for this.
     virtual void notifyDeviceLost() = 0;
     virtual bool isDeviceLost() const = 0;
diff --git a/src/libANGLE/renderer/GLImplFactory.h b/src/libANGLE/renderer/GLImplFactory.h
index da93b3b..5043a78 100644
--- a/src/libANGLE/renderer/GLImplFactory.h
+++ b/src/libANGLE/renderer/GLImplFactory.h
@@ -10,6 +10,9 @@
 #ifndef LIBANGLE_RENDERER_GLIMPLFACTORY_H_
 #define LIBANGLE_RENDERER_GLIMPLFACTORY_H_
 
+#include <vector>
+
+#include "angle_gl.h"
 #include "libANGLE/Framebuffer.h"
 #include "libANGLE/Program.h"
 #include "libANGLE/Shader.h"
@@ -28,6 +31,7 @@
 class FenceNVImpl;
 class FenceSyncImpl;
 class FramebufferImpl;
+class PathImpl;
 class ProgramImpl;
 class QueryImpl;
 class RenderbufferImpl;
@@ -73,6 +77,8 @@
 
     // Sampler object creation
     virtual SamplerImpl *createSampler() = 0;
+
+    virtual std::vector<PathImpl *> createPaths(GLsizei range) = 0;
 };
 
 }  // namespace rx
diff --git a/src/libANGLE/renderer/PathImpl.h b/src/libANGLE/renderer/PathImpl.h
new file mode 100644
index 0000000..3607f69
--- /dev/null
+++ b/src/libANGLE/renderer/PathImpl.h
@@ -0,0 +1,36 @@
+//
+// Copyright (c) 2002-2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+// PathImpl.h: Defines the Path implementation interface for
+// CHROMIUM_path_rendering path objects.
+
+#ifndef LIBANGLE_RENDERER_PATHIMPL_H_
+#define LIBANGLE_RENDERER_PATHIMPL_H_
+
+#include "angle_gl.h"
+#include "common/angleutils.h"
+#include "libANGLE/Error.h"
+
+namespace rx
+{
+
+class PathImpl : angle::NonCopyable
+{
+  public:
+    virtual ~PathImpl() {}
+
+    virtual gl::Error setCommands(GLsizei numCommands,
+                                  const GLubyte *commands,
+                                  GLsizei numCoords,
+                                  GLenum coordType,
+                                  const void *coords) = 0;
+
+    virtual void setPathParameter(GLenum pname, GLfloat value) = 0;
+};
+
+}  // namespace rx
+
+#endif  // LIBANGLE_RENDERER_PATHIMPL_H_
\ No newline at end of file
diff --git a/src/libANGLE/renderer/d3d/d3d11/Context11.cpp b/src/libANGLE/renderer/d3d/d3d11/Context11.cpp
index b7b8272..5eb28e7 100644
--- a/src/libANGLE/renderer/d3d/d3d11/Context11.cpp
+++ b/src/libANGLE/renderer/d3d/d3d11/Context11.cpp
@@ -131,6 +131,11 @@
     return new SamplerD3D();
 }
 
+std::vector<PathImpl *> Context11::createPaths(GLsizei)
+{
+    return std::vector<PathImpl *>();
+}
+
 gl::Error Context11::flush()
 {
     return mRenderer->flush();
diff --git a/src/libANGLE/renderer/d3d/d3d11/Context11.h b/src/libANGLE/renderer/d3d/d3d11/Context11.h
index 2ee3576..2f42260 100644
--- a/src/libANGLE/renderer/d3d/d3d11/Context11.h
+++ b/src/libANGLE/renderer/d3d/d3d11/Context11.h
@@ -55,6 +55,9 @@
     // Sampler object creation
     SamplerImpl *createSampler() override;
 
+    // Path object creation.
+    std::vector<PathImpl *> createPaths(GLsizei) override;
+
     // Flush and finish.
     gl::Error flush() override;
     gl::Error finish() override;
diff --git a/src/libANGLE/renderer/d3d/d3d9/Context9.cpp b/src/libANGLE/renderer/d3d/d3d9/Context9.cpp
index 9ae8701..5c5358a 100644
--- a/src/libANGLE/renderer/d3d/d3d9/Context9.cpp
+++ b/src/libANGLE/renderer/d3d/d3d9/Context9.cpp
@@ -120,6 +120,11 @@
     return new SamplerD3D();
 }
 
+std::vector<PathImpl *> Context9::createPaths(GLsizei)
+{
+    return std::vector<PathImpl *>();
+}
+
 gl::Error Context9::flush()
 {
     return mRenderer->flush();
diff --git a/src/libANGLE/renderer/d3d/d3d9/Context9.h b/src/libANGLE/renderer/d3d/d3d9/Context9.h
index b450aea..aa788e1 100644
--- a/src/libANGLE/renderer/d3d/d3d9/Context9.h
+++ b/src/libANGLE/renderer/d3d/d3d9/Context9.h
@@ -55,6 +55,9 @@
     // Sampler object creation
     SamplerImpl *createSampler() override;
 
+    // Path object creation
+    std::vector<PathImpl *> createPaths(GLsizei) override;
+
     // Flush and finish.
     gl::Error flush() override;
     gl::Error finish() override;
diff --git a/src/libANGLE/renderer/gl/ContextGL.cpp b/src/libANGLE/renderer/gl/ContextGL.cpp
index ded0eca..b943558 100644
--- a/src/libANGLE/renderer/gl/ContextGL.cpp
+++ b/src/libANGLE/renderer/gl/ContextGL.cpp
@@ -13,7 +13,9 @@
 #include "libANGLE/renderer/gl/CompilerGL.h"
 #include "libANGLE/renderer/gl/FenceNVGL.h"
 #include "libANGLE/renderer/gl/FenceSyncGL.h"
+#include "libANGLE/renderer/gl/FunctionsGL.h"
 #include "libANGLE/renderer/gl/FramebufferGL.h"
+#include "libANGLE/renderer/gl/PathGL.h"
 #include "libANGLE/renderer/gl/ProgramGL.h"
 #include "libANGLE/renderer/gl/QueryGL.h"
 #include "libANGLE/renderer/gl/RenderbufferGL.h"
@@ -110,6 +112,26 @@
     return new SamplerGL(getFunctions(), getStateManager());
 }
 
+std::vector<PathImpl *> ContextGL::createPaths(GLsizei range)
+{
+    const FunctionsGL *funcs = getFunctions();
+
+    std::vector<PathImpl *> ret;
+    ret.reserve(range);
+
+    const GLuint first = funcs->genPathsNV(range);
+    if (first == 0)
+        return ret;
+
+    for (GLsizei i = 0; i < range; ++i)
+    {
+        const auto id = first + i;
+        ret.push_back(new PathGL(funcs, id));
+    }
+
+    return ret;
+}
+
 gl::Error ContextGL::flush()
 {
     return mRenderer->flush();
@@ -164,6 +186,42 @@
     return mRenderer->drawRangeElements(mState, mode, start, end, count, type, indices, indexRange);
 }
 
+void ContextGL::stencilFillPath(const gl::Path *path, GLenum fillMode, GLuint mask)
+{
+    mRenderer->stencilFillPath(mState, path, fillMode, mask);
+}
+
+void ContextGL::stencilStrokePath(const gl::Path *path, GLint reference, GLuint mask)
+{
+    mRenderer->stencilStrokePath(mState, path, reference, mask);
+}
+
+void ContextGL::coverFillPath(const gl::Path *path, GLenum coverMode)
+{
+    mRenderer->coverFillPath(mState, path, coverMode);
+}
+
+void ContextGL::coverStrokePath(const gl::Path *path, GLenum coverMode)
+{
+    mRenderer->coverStrokePath(mState, path, coverMode);
+}
+
+void ContextGL::stencilThenCoverFillPath(const gl::Path *path,
+                                         GLenum fillMode,
+                                         GLuint mask,
+                                         GLenum coverMode)
+{
+    mRenderer->stencilThenCoverFillPath(mState, path, fillMode, mask, coverMode);
+}
+
+void ContextGL::stencilThenCoverStrokePath(const gl::Path *path,
+                                           GLint reference,
+                                           GLuint mask,
+                                           GLenum coverMode)
+{
+    mRenderer->stencilThenCoverStrokePath(mState, path, reference, mask, coverMode);
+}
+
 void ContextGL::notifyDeviceLost()
 {
     mRenderer->notifyDeviceLost();
diff --git a/src/libANGLE/renderer/gl/ContextGL.h b/src/libANGLE/renderer/gl/ContextGL.h
index 5dd73b9..7a43756 100644
--- a/src/libANGLE/renderer/gl/ContextGL.h
+++ b/src/libANGLE/renderer/gl/ContextGL.h
@@ -63,6 +63,9 @@
     // Sampler object creation
     SamplerImpl *createSampler() override;
 
+    // Path object creation
+    std::vector<PathImpl *> createPaths(GLsizei range) override;
+
     // Flush and finish.
     gl::Error flush() override;
     gl::Error finish() override;
@@ -93,6 +96,20 @@
                                 const GLvoid *indices,
                                 const gl::IndexRange &indexRange) override;
 
+    // CHROMIUM_path_rendering implementation
+    void stencilFillPath(const gl::Path *path, GLenum fillMode, GLuint mask) override;
+    void stencilStrokePath(const gl::Path *path, GLint reference, GLuint mask) override;
+    void coverFillPath(const gl::Path *path, GLenum coverMode) override;
+    void coverStrokePath(const gl::Path *path, GLenum coverMode) override;
+    void stencilThenCoverFillPath(const gl::Path *path,
+                                  GLenum fillMode,
+                                  GLuint mask,
+                                  GLenum coverMode) override;
+    void stencilThenCoverStrokePath(const gl::Path *path,
+                                    GLint reference,
+                                    GLuint mask,
+                                    GLenum coverMode) override;
+
     // TODO(jmadill): Investigate proper impl methods for this.
     void notifyDeviceLost() override;
     bool isDeviceLost() const override;
diff --git a/src/libANGLE/renderer/gl/FunctionsGL.cpp b/src/libANGLE/renderer/gl/FunctionsGL.cpp
index ae76991..f129717 100644
--- a/src/libANGLE/renderer/gl/FunctionsGL.cpp
+++ b/src/libANGLE/renderer/gl/FunctionsGL.cpp
@@ -418,6 +418,22 @@
       texImage3DMultisample(nullptr),
       waitSync(nullptr),
 
+      matrixLoadEXT(nullptr),
+      genPathsNV(nullptr),
+      delPathsNV(nullptr),
+      pathCommandsNV(nullptr),
+      setPathParameterfNV(nullptr),
+      setPathParameteriNV(nullptr),
+      getPathParameterfNV(nullptr),
+      getPathParameteriNV(nullptr),
+      pathStencilFuncNV(nullptr),
+      stencilFillPathNV(nullptr),
+      stencilStrokePathNV(nullptr),
+      coverFillPathNV(nullptr),
+      coverStrokePathNV(nullptr),
+      stencilThenCoverFillPathNV(nullptr),
+      stencilThenCoverStrokePathNV(nullptr),
+
       bindFragDataLocationIndexed(nullptr),
       bindSampler(nullptr),
       deleteSamplers(nullptr),
@@ -824,6 +840,26 @@
     // Even though extensions are written against specific versions of GL, many drivers expose the extensions
     // in even older versions.  Always try loading the extensions regardless of GL version.
 
+    // EXT_direct_state_access (loading only functions relevant to GL_NV_path_rendering here)
+    AssignGLExtensionEntryPoint(extensions, "GL_EXT_direct_state_access", loadProcAddress("glMatrixLoadfEXT"), &matrixLoadEXT);
+
+    // GL_NV_path_rendering
+    AssignGLExtensionEntryPoint(extensions, "GL_NV_path_rendering", loadProcAddress("glGenPathsNV"), &genPathsNV);
+    AssignGLExtensionEntryPoint(extensions, "GL_NV_path_rendering", loadProcAddress("glDeletePathsNV"), &delPathsNV);
+    AssignGLExtensionEntryPoint(extensions, "GL_NV_path_rendering", loadProcAddress("glPathCommandsNV"), &pathCommandsNV);
+    AssignGLExtensionEntryPoint(extensions, "GL_NV_path_rendering", loadProcAddress("glIsPathNV"), &isPathNV);
+    AssignGLExtensionEntryPoint(extensions, "GL_NV_path_rendering", loadProcAddress("glPathParameterfNV"), &setPathParameterfNV);
+    AssignGLExtensionEntryPoint(extensions, "GL_NV_path_rendering", loadProcAddress("glPathParameteriNV"), &setPathParameteriNV);
+    AssignGLExtensionEntryPoint(extensions, "GL_NV_path_rendering", loadProcAddress("glGetPathParameterfvNV"), &getPathParameterfNV);
+    AssignGLExtensionEntryPoint(extensions, "GL_NV_path_rendering", loadProcAddress("glGetPathParameterivNV"), &getPathParameteriNV);
+    AssignGLExtensionEntryPoint(extensions, "GL_NV_path_rendering", loadProcAddress("glPathStencilFuncNV"), &pathStencilFuncNV);
+    AssignGLExtensionEntryPoint(extensions, "GL_NV_path_rendering", loadProcAddress("glStencilFillPathNV"), &stencilFillPathNV);
+    AssignGLExtensionEntryPoint(extensions, "GL_NV_path_rendering", loadProcAddress("glStencilStrokePathNV"), &stencilStrokePathNV);
+    AssignGLExtensionEntryPoint(extensions, "GL_NV_path_rendering", loadProcAddress("glCoverFillPathNV"), &coverFillPathNV);
+    AssignGLExtensionEntryPoint(extensions, "GL_NV_path_rendering", loadProcAddress("glCoverStrokePathNV"), &coverStrokePathNV);
+    AssignGLExtensionEntryPoint(extensions, "GL_NV_path_rendering", loadProcAddress("glStencilThenCoverFillPathNV"), &stencilThenCoverFillPathNV);
+    AssignGLExtensionEntryPoint(extensions, "GL_NV_path_rendering", loadProcAddress("glStencilThenCoverStrokePathNV"), &stencilThenCoverStrokePathNV);
+
     // GL_NV_framebuffer_mixed_samples
     AssignGLExtensionEntryPoint(extensions, "GL_NV_framebuffer_mixed_samples", loadProcAddress("glCoverageModulationNV"), &coverageModulationNV);
 
@@ -1722,6 +1758,25 @@
     profile = 0;
 
     // clang-format off
+    // EXT_direct_state_access (loading only functions relevant to GL_NV_path_rendering here)
+    AssignGLExtensionEntryPoint(extensions, "GL_EXT_direct_state_access", loadProcAddress("glMatrixLoadfEXT"), &matrixLoadEXT);
+
+    // GL_NV_path_rendering
+    AssignGLExtensionEntryPoint(extensions, "GL_NV_path_rendering", loadProcAddress("glGenPathsNV"), &genPathsNV);
+    AssignGLExtensionEntryPoint(extensions, "GL_NV_path_rendering", loadProcAddress("glDeletePathsNV"), &delPathsNV);
+    AssignGLExtensionEntryPoint(extensions, "GL_NV_path_rendering", loadProcAddress("glPathCommandsNV"), &pathCommandsNV);
+    AssignGLExtensionEntryPoint(extensions, "GL_NV_path_rendering", loadProcAddress("glIsPathNV"), &isPathNV);
+    AssignGLExtensionEntryPoint(extensions, "GL_NV_path_rendering", loadProcAddress("glPathParameterfNV"), &setPathParameterfNV);
+    AssignGLExtensionEntryPoint(extensions, "GL_NV_path_rendering", loadProcAddress("glPathParameteriNV"), &setPathParameteriNV);
+    AssignGLExtensionEntryPoint(extensions, "GL_NV_path_rendering", loadProcAddress("glGetPathParameterfvNV"), &getPathParameterfNV);
+    AssignGLExtensionEntryPoint(extensions, "GL_NV_path_rendering", loadProcAddress("glGetPathParameterivNV"), &getPathParameteriNV);
+    AssignGLExtensionEntryPoint(extensions, "GL_NV_path_rendering", loadProcAddress("glPathStencilFuncNV"), &pathStencilFuncNV);
+    AssignGLExtensionEntryPoint(extensions, "GL_NV_path_rendering", loadProcAddress("glStencilFillPathNV"), &stencilFillPathNV);
+    AssignGLExtensionEntryPoint(extensions, "GL_NV_path_rendering", loadProcAddress("glStencilStrokePathNV"), &stencilStrokePathNV);
+    AssignGLExtensionEntryPoint(extensions, "GL_NV_path_rendering", loadProcAddress("glCoverFillPathNV"), &coverFillPathNV);
+    AssignGLExtensionEntryPoint(extensions, "GL_NV_path_rendering", loadProcAddress("glCoverStrokePathNV"), &coverStrokePathNV);
+    AssignGLExtensionEntryPoint(extensions, "GL_NV_path_rendering", loadProcAddress("glStencilThenCoverFillPathNV"), &stencilThenCoverFillPathNV);
+    AssignGLExtensionEntryPoint(extensions, "GL_NV_path_rendering", loadProcAddress("glStencilThenCoverStrokePathNV"), &stencilThenCoverStrokePathNV);
 
     // GL_OES_texture_3D
     AssignGLExtensionEntryPoint(extensions, "GL_OES_texture_3D", loadProcAddress("glTexImage3DOES"), &texImage3D);
diff --git a/src/libANGLE/renderer/gl/FunctionsGL.h b/src/libANGLE/renderer/gl/FunctionsGL.h
index 44a1004..a5697cb 100644
--- a/src/libANGLE/renderer/gl/FunctionsGL.h
+++ b/src/libANGLE/renderer/gl/FunctionsGL.h
@@ -392,6 +392,27 @@
     PFNGLTEXIMAGE3DMULTISAMPLEPROC texImage3DMultisample;
     PFNGLWAITSYNCPROC waitSync;
 
+    // EXT_direct_state_access. Needed by NV_path_rendering.
+    PFNGLMATRIXLOADFEXTPROC matrixLoadEXT;
+
+    // NV_path_rendering (originally written against 3.2 compatibility profile)
+    PFNGLGENPATHSNVPROC genPathsNV;
+    PFNGLDELETEPATHSNVPROC delPathsNV;
+    PFNGLPATHCOMMANDSNVPROC pathCommandsNV;
+    PFNGLISPATHNVPROC isPathNV;
+    PFNGLPATHPARAMETERFNVPROC setPathParameterfNV;
+    PFNGLPATHPARAMETERINVPROC setPathParameteriNV;
+    PFNGLGETPATHPARAMETERFVNVPROC getPathParameterfNV;
+    PFNGLGETPATHPARAMETERIVNVPROC getPathParameteriNV;
+    PFNGLPATHSTENCILFUNCNVPROC pathStencilFuncNV;
+
+    PFNGLSTENCILFILLPATHNVPROC stencilFillPathNV;
+    PFNGLSTENCILSTROKEPATHNVPROC stencilStrokePathNV;
+    PFNGLCOVERFILLPATHNVPROC coverFillPathNV;
+    PFNGLCOVERSTROKEPATHNVPROC coverStrokePathNV;
+    PFNGLSTENCILTHENCOVERFILLPATHNVPROC stencilThenCoverFillPathNV;
+    PFNGLSTENCILTHENCOVERSTROKEPATHNVPROC stencilThenCoverStrokePathNV;
+
     // 3.3
     PFNGLBINDFRAGDATALOCATIONINDEXEDPROC bindFragDataLocationIndexed;
     PFNGLBINDSAMPLERPROC bindSampler;
diff --git a/src/libANGLE/renderer/gl/PathGL.cpp b/src/libANGLE/renderer/gl/PathGL.cpp
new file mode 100644
index 0000000..a859bd7
--- /dev/null
+++ b/src/libANGLE/renderer/gl/PathGL.cpp
@@ -0,0 +1,38 @@
+//
+// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+// PathGL.cpp: Implementation for PathGL class.
+
+#include "libANGLE/renderer/gl/PathGL.h"
+#include "libANGLE/renderer/gl/FunctionsGL.h"
+
+namespace rx
+{
+
+PathGL::PathGL(const FunctionsGL *functions, GLuint path) : mFunctions(functions), mPathID(path)
+{
+}
+
+PathGL::~PathGL()
+{
+}
+
+gl::Error PathGL::setCommands(GLsizei numCommands,
+                              const GLubyte *commands,
+                              GLsizei numCoords,
+                              GLenum coordType,
+                              const void *coords)
+{
+    mFunctions->pathCommandsNV(mPathID, numCommands, commands, numCoords, coordType, coords);
+    return gl::Error(GL_NO_ERROR);
+}
+
+void PathGL::setPathParameter(GLenum pname, GLfloat value)
+{
+    mFunctions->setPathParameterfNV(mPathID, pname, value);
+}
+
+}  // rx
\ No newline at end of file
diff --git a/src/libANGLE/renderer/gl/PathGL.h b/src/libANGLE/renderer/gl/PathGL.h
new file mode 100644
index 0000000..461d39a
--- /dev/null
+++ b/src/libANGLE/renderer/gl/PathGL.h
@@ -0,0 +1,44 @@
+//
+// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+// PathGL.h: Class definition for CHROMIUM_path_rendering path object for the
+// GL backend.
+
+#ifndef LIBANGLE_RENDERER_GL_PATHIMPL_H_
+#define LIBANGLE_RENDERER_GL_PATHIMPL_H_
+
+#include "libANGLE/renderer/PathImpl.h"
+
+namespace rx
+{
+
+class FunctionsGL;
+
+class PathGL : public PathImpl
+{
+  public:
+    PathGL(const FunctionsGL *functions, GLuint path);
+    ~PathGL();
+
+    gl::Error setCommands(GLsizei numCommands,
+                          const GLubyte *commands,
+                          GLsizei numCoords,
+                          GLenum coordType,
+                          const void *coords) override;
+
+    void setPathParameter(GLenum pname, GLfloat value) override;
+
+    GLuint getPathID() const { return mPathID; }
+
+  private:
+    const FunctionsGL *mFunctions;
+
+    GLuint mPathID;
+};
+
+}  // namespace rx
+
+#endif  // LIBANGLE_RENDERER_GL_PATHIMPL_H_
diff --git a/src/libANGLE/renderer/gl/RendererGL.cpp b/src/libANGLE/renderer/gl/RendererGL.cpp
index 8fbdca9..682a895 100644
--- a/src/libANGLE/renderer/gl/RendererGL.cpp
+++ b/src/libANGLE/renderer/gl/RendererGL.cpp
@@ -13,6 +13,7 @@
 #include "common/debug.h"
 #include "libANGLE/AttributeMap.h"
 #include "libANGLE/ContextState.h"
+#include "libANGLE/Path.h"
 #include "libANGLE/Surface.h"
 #include "libANGLE/renderer/gl/BlitGL.h"
 #include "libANGLE/renderer/gl/BufferGL.h"
@@ -22,6 +23,7 @@
 #include "libANGLE/renderer/gl/FenceSyncGL.h"
 #include "libANGLE/renderer/gl/FramebufferGL.h"
 #include "libANGLE/renderer/gl/FunctionsGL.h"
+#include "libANGLE/renderer/gl/PathGL.h"
 #include "libANGLE/renderer/gl/ProgramGL.h"
 #include "libANGLE/renderer/gl/QueryGL.h"
 #include "libANGLE/renderer/gl/RenderbufferGL.h"
@@ -242,6 +244,77 @@
     return gl::Error(GL_NO_ERROR);
 }
 
+void RendererGL::stencilFillPath(const gl::ContextState &state,
+                                 const gl::Path *path,
+                                 GLenum fillMode,
+                                 GLuint mask)
+{
+    const auto *pathObj = GetImplAs<PathGL>(path);
+
+    mFunctions->stencilFillPathNV(pathObj->getPathID(), fillMode, mask);
+
+    ASSERT(mFunctions->getError() == GL_NO_ERROR);
+}
+
+void RendererGL::stencilStrokePath(const gl::ContextState &state,
+                                   const gl::Path *path,
+                                   GLint reference,
+                                   GLuint mask)
+{
+    const auto *pathObj = GetImplAs<PathGL>(path);
+
+    mFunctions->stencilStrokePathNV(pathObj->getPathID(), reference, mask);
+
+    ASSERT(mFunctions->getError() == GL_NO_ERROR);
+}
+
+void RendererGL::coverFillPath(const gl::ContextState &state,
+                               const gl::Path *path,
+                               GLenum coverMode)
+{
+
+    const auto *pathObj = GetImplAs<PathGL>(path);
+    mFunctions->coverFillPathNV(pathObj->getPathID(), coverMode);
+
+    ASSERT(mFunctions->getError() == GL_NO_ERROR);
+}
+
+void RendererGL::coverStrokePath(const gl::ContextState &state,
+                                 const gl::Path *path,
+                                 GLenum coverMode)
+{
+    const auto *pathObj = GetImplAs<PathGL>(path);
+    mFunctions->coverStrokePathNV(pathObj->getPathID(), coverMode);
+
+    ASSERT(mFunctions->getError() == GL_NO_ERROR);
+}
+
+void RendererGL::stencilThenCoverFillPath(const gl::ContextState &state,
+                                          const gl::Path *path,
+                                          GLenum fillMode,
+                                          GLuint mask,
+                                          GLenum coverMode)
+{
+
+    const auto *pathObj = GetImplAs<PathGL>(path);
+    mFunctions->stencilThenCoverFillPathNV(pathObj->getPathID(), fillMode, mask, coverMode);
+
+    ASSERT(mFunctions->getError() == GL_NO_ERROR);
+}
+
+void RendererGL::stencilThenCoverStrokePath(const gl::ContextState &state,
+                                            const gl::Path *path,
+                                            GLint reference,
+                                            GLuint mask,
+                                            GLenum coverMode)
+{
+
+    const auto *pathObj = GetImplAs<PathGL>(path);
+    mFunctions->stencilThenCoverStrokePathNV(pathObj->getPathID(), reference, mask, coverMode);
+
+    ASSERT(mFunctions->getError() == GL_NO_ERROR);
+}
+
 ContextImpl *RendererGL::createContext(const gl::ContextState &state)
 {
     return new ContextGL(state, this);
diff --git a/src/libANGLE/renderer/gl/RendererGL.h b/src/libANGLE/renderer/gl/RendererGL.h
index 132f4a6..cb82484 100644
--- a/src/libANGLE/renderer/gl/RendererGL.h
+++ b/src/libANGLE/renderer/gl/RendererGL.h
@@ -18,6 +18,7 @@
 {
 class ContextState;
 struct IndexRange;
+class Path;
 }
 
 namespace egl
@@ -77,6 +78,28 @@
                                 const GLvoid *indices,
                                 const gl::IndexRange &indexRange);
 
+    // CHROMIUM_path_rendering implementation
+    void stencilFillPath(const gl::ContextState &state,
+                         const gl::Path *path,
+                         GLenum fillMode,
+                         GLuint mask);
+    void stencilStrokePath(const gl::ContextState &state,
+                           const gl::Path *path,
+                           GLint reference,
+                           GLuint mask);
+    void coverFillPath(const gl::ContextState &state, const gl::Path *path, GLenum coverMode);
+    void coverStrokePath(const gl::ContextState &state, const gl::Path *path, GLenum coverMode);
+    void stencilThenCoverFillPath(const gl::ContextState &state,
+                                  const gl::Path *path,
+                                  GLenum fillMode,
+                                  GLuint mask,
+                                  GLenum coverMode);
+    void stencilThenCoverStrokePath(const gl::ContextState &state,
+                                    const gl::Path *path,
+                                    GLint reference,
+                                    GLuint mask,
+                                    GLenum coverMode);
+
     // EXT_debug_marker
     void insertEventMarker(GLsizei length, const char *marker);
     void pushGroupMarker(GLsizei length, const char *marker);
diff --git a/src/libANGLE/renderer/gl/StateManagerGL.cpp b/src/libANGLE/renderer/gl/StateManagerGL.cpp
index f76f785..2be962d 100644
--- a/src/libANGLE/renderer/gl/StateManagerGL.cpp
+++ b/src/libANGLE/renderer/gl/StateManagerGL.cpp
@@ -8,7 +8,12 @@
 
 #include "libANGLE/renderer/gl/StateManagerGL.h"
 
+#include <limits>
+#include <string.h>
+
 #include "common/BitSetIterator.h"
+#include "common/mathutil.h"
+#include "common/matrix_utils.h"
 #include "libANGLE/ContextState.h"
 #include "libANGLE/Framebuffer.h"
 #include "libANGLE/TransformFeedback.h"
@@ -116,6 +121,9 @@
       mMultisamplingEnabled(true),
       mSampleAlphaToOneEnabled(false),
       mCoverageModulation(GL_NONE),
+      mPathStencilFunc(GL_ALWAYS),
+      mPathStencilRef(0),
+      mPathStencilMask(std::numeric_limits<GLuint>::max()),
       mLocalDirtyBits()
 {
     ASSERT(mFunctions);
@@ -145,6 +153,9 @@
             mFunctions->enable(GL_POINT_SPRITE);
         }
     }
+
+    angle::Matrix<GLfloat>::setToIdentity(mPathMatrixProj);
+    angle::Matrix<GLfloat>::setToIdentity(mPathMatrixMV);
 }
 
 void StateManagerGL::deleteProgram(GLuint program)
@@ -1523,6 +1534,18 @@
             case gl::State::DIRTY_BIT_COVERAGE_MODULATION:
                 setCoverageModulation(state.getCoverageModulation());
                 break;
+            case gl::State::DIRTY_BIT_PATH_RENDERING_MATRIX_MV:
+                setPathRenderingModelViewMatrix(
+                    state.getPathRenderingMatrix(GL_PATH_MODELVIEW_MATRIX_CHROMIUM));
+                break;
+            case gl::State::DIRTY_BIT_PATH_RENDERING_MATRIX_PROJ:
+                setPathRenderingProjectionMatrix(
+                    state.getPathRenderingMatrix(GL_PATH_PROJECTION_MATRIX_CHROMIUM));
+                break;
+            case gl::State::DIRTY_BIT_PATH_RENDERING_STENCIL_STATE:
+                setPathRenderingStencilState(state.getPathStencilFunc(), state.getPathStencilRef(),
+                                             state.getPathStencilMask());
+                break;
             default:
             {
                 ASSERT(dirtyBit >= gl::State::DIRTY_BIT_CURRENT_VALUE_0 &&
@@ -1600,6 +1623,41 @@
     }
 }
 
+void StateManagerGL::setPathRenderingModelViewMatrix(const GLfloat *m)
+{
+    if (memcmp(mPathMatrixMV, m, sizeof(mPathMatrixMV)) != 0)
+    {
+        memcpy(mPathMatrixMV, m, sizeof(mPathMatrixMV));
+        mFunctions->matrixLoadEXT(GL_PATH_MODELVIEW_CHROMIUM, m);
+
+        mLocalDirtyBits.set(gl::State::DIRTY_BIT_PATH_RENDERING_MATRIX_MV);
+    }
+}
+
+void StateManagerGL::setPathRenderingProjectionMatrix(const GLfloat *m)
+{
+    if (memcmp(mPathMatrixProj, m, sizeof(mPathMatrixProj)) != 0)
+    {
+        memcpy(mPathMatrixProj, m, sizeof(mPathMatrixProj));
+        mFunctions->matrixLoadEXT(GL_PATH_PROJECTION_CHROMIUM, m);
+
+        mLocalDirtyBits.set(gl::State::DIRTY_BIT_PATH_RENDERING_MATRIX_PROJ);
+    }
+}
+
+void StateManagerGL::setPathRenderingStencilState(GLenum func, GLint ref, GLuint mask)
+{
+    if (func != mPathStencilFunc || ref != mPathStencilRef || mask != mPathStencilMask)
+    {
+        mPathStencilFunc = func;
+        mPathStencilRef  = ref;
+        mPathStencilMask = mask;
+        mFunctions->pathStencilFuncNV(func, ref, mask);
+
+        mLocalDirtyBits.set(gl::State::DIRTY_BIT_PATH_RENDERING_STENCIL_STATE);
+    }
+}
+
 void StateManagerGL::setTextureCubemapSeamlessEnabled(bool enabled)
 {
     if (mTextureCubemapSeamlessEnabled != enabled)
diff --git a/src/libANGLE/renderer/gl/StateManagerGL.h b/src/libANGLE/renderer/gl/StateManagerGL.h
index c4fe748..10adb93 100644
--- a/src/libANGLE/renderer/gl/StateManagerGL.h
+++ b/src/libANGLE/renderer/gl/StateManagerGL.h
@@ -129,6 +129,10 @@
 
     void setCoverageModulation(GLenum components);
 
+    void setPathRenderingModelViewMatrix(const GLfloat *m);
+    void setPathRenderingProjectionMatrix(const GLfloat *m);
+    void setPathRenderingStencilState(GLenum func, GLint ref, GLuint mask);
+
     void onDeleteQueryObject(QueryGL *query);
 
     gl::Error setDrawArraysState(const gl::ContextState &data,
@@ -266,6 +270,12 @@
 
     GLenum mCoverageModulation;
 
+    GLfloat mPathMatrixMV[16];
+    GLfloat mPathMatrixProj[16];
+    GLenum mPathStencilFunc;
+    GLint mPathStencilRef;
+    GLuint mPathStencilMask;
+
     gl::State::DirtyBits mLocalDirtyBits;
 };
 
diff --git a/src/libANGLE/renderer/gl/renderergl_utils.cpp b/src/libANGLE/renderer/gl/renderergl_utils.cpp
index 9668c70..4f7d48a 100644
--- a/src/libANGLE/renderer/gl/renderergl_utils.cpp
+++ b/src/libANGLE/renderer/gl/renderergl_utils.cpp
@@ -670,6 +670,14 @@
     extensions->framebufferMixedSamples =
         functions->hasGLExtension("GL_NV_framebuffer_mixed_samples") ||
         functions->hasGLESExtension("GL_NV_framebuffer_mixed_samples");
+
+    // if NV_path_rendering is to be supported then EXT_direct_state_access
+    // must also be available. NV_path_rendering needs some of the matrix loads
+    // from this extension.
+    extensions->pathRendering = (functions->hasGLExtension("GL_NV_path_rendering") &&
+                                 functions->hasGLExtension("GL_EXT_direct_state_access")) ||
+                                (functions->hasGLESExtension("GL_NV_path_rendering") &&
+                                 functions->hasGLESExtension("GL_EXT_direct_state_access"));
 }
 
 void GenerateWorkarounds(const FunctionsGL *functions, WorkaroundsGL *workarounds)
diff --git a/src/libANGLE/renderer/vulkan/ContextVk.cpp b/src/libANGLE/renderer/vulkan/ContextVk.cpp
index 442dc0a..19d11cc 100644
--- a/src/libANGLE/renderer/vulkan/ContextVk.cpp
+++ b/src/libANGLE/renderer/vulkan/ContextVk.cpp
@@ -263,4 +263,9 @@
     return new SamplerVk();
 }
 
+std::vector<PathImpl *> ContextVk::createPaths(GLsizei)
+{
+    return std::vector<PathImpl *>();
+}
+
 }  // namespace rx
diff --git a/src/libANGLE/renderer/vulkan/ContextVk.h b/src/libANGLE/renderer/vulkan/ContextVk.h
index 85e054b..05070c9 100644
--- a/src/libANGLE/renderer/vulkan/ContextVk.h
+++ b/src/libANGLE/renderer/vulkan/ContextVk.h
@@ -116,6 +116,9 @@
     // Sampler object creation
     SamplerImpl *createSampler() override;
 
+    // Path object creation
+    std::vector<PathImpl *> createPaths(GLsizei) override;
+
   private:
     RendererVk *mRenderer;
 };
diff --git a/src/libANGLE/validationES2.cpp b/src/libANGLE/validationES2.cpp
index 9dd1eb0..30c6c74 100644
--- a/src/libANGLE/validationES2.cpp
+++ b/src/libANGLE/validationES2.cpp
@@ -7,6 +7,9 @@
 // validationES2.cpp: Validation functions for OpenGL ES 2.0 entry point parameters
 
 #include "libANGLE/validationES2.h"
+
+#include <cstdint>
+
 #include "libANGLE/validationES.h"
 #include "libANGLE/validationES3.h"
 #include "libANGLE/Context.h"
@@ -2091,4 +2094,462 @@
     return true;
 }
 
+// CHROMIUM_path_rendering
+
+bool ValidateMatrix(Context *context, GLenum matrixMode, const GLfloat *matrix)
+{
+    if (!context->getExtensions().pathRendering)
+    {
+        context->handleError(
+            Error(GL_INVALID_OPERATION, "GL_CHROMIUM_path_rendering is not available."));
+        return false;
+    }
+    if (matrixMode != GL_PATH_MODELVIEW_CHROMIUM && matrixMode != GL_PATH_PROJECTION_CHROMIUM)
+    {
+        context->handleError(Error(GL_INVALID_ENUM, "Invalid matrix mode."));
+        return false;
+    }
+    if (matrix == nullptr)
+    {
+        context->handleError(Error(GL_INVALID_OPERATION, "Invalid matrix."));
+        return false;
+    }
+    return true;
+}
+
+bool ValidateMatrixMode(Context *context, GLenum matrixMode)
+{
+    if (!context->getExtensions().pathRendering)
+    {
+        context->handleError(
+            Error(GL_INVALID_OPERATION, "GL_CHROMIUM_path_rendering is not available."));
+        return false;
+    }
+    if (matrixMode != GL_PATH_MODELVIEW_CHROMIUM && matrixMode != GL_PATH_PROJECTION_CHROMIUM)
+    {
+        context->handleError(Error(GL_INVALID_ENUM, "Invalid matrix mode."));
+        return false;
+    }
+    return true;
+}
+
+bool ValidateGenPaths(Context *context, GLsizei range)
+{
+    if (!context->getExtensions().pathRendering)
+    {
+        context->handleError(
+            Error(GL_INVALID_OPERATION, "GL_CHROMIUM_path_rendering is not available."));
+        return false;
+    }
+
+    // range = 0 is undefined in NV_path_rendering.
+    // we add stricter semantic check here and require a non zero positive range.
+    if (range <= 0)
+    {
+        context->handleError(Error(GL_INVALID_VALUE, "Invalid range."));
+        return false;
+    }
+
+    if (!angle::IsValueInRangeForNumericType<std::uint32_t>(range))
+    {
+        context->handleError(Error(GL_INVALID_OPERATION, "Range overflow."));
+        return false;
+    }
+
+    return true;
+}
+
+bool ValidateDeletePaths(Context *context, GLuint path, GLsizei range)
+{
+    if (!context->getExtensions().pathRendering)
+    {
+        context->handleError(
+            Error(GL_INVALID_OPERATION, "GL_CHROMIUM_path_rendering is not available."));
+        return false;
+    }
+
+    // range = 0 is undefined in NV_path_rendering.
+    // we add stricter semantic check here and require a non zero positive range.
+    if (range <= 0)
+    {
+        context->handleError(Error(GL_INVALID_VALUE, "Invalid range."));
+        return false;
+    }
+
+    angle::CheckedNumeric<std::uint32_t> checkedRange(path);
+    checkedRange += range;
+
+    if (!angle::IsValueInRangeForNumericType<std::uint32_t>(range) || !checkedRange.IsValid())
+    {
+        context->handleError(Error(GL_INVALID_OPERATION, "Range overflow."));
+        return false;
+    }
+    return true;
+}
+
+bool ValidatePathCommands(Context *context,
+                          GLuint path,
+                          GLsizei numCommands,
+                          const GLubyte *commands,
+                          GLsizei numCoords,
+                          GLenum coordType,
+                          const void *coords)
+{
+    if (!context->getExtensions().pathRendering)
+    {
+        context->handleError(
+            Error(GL_INVALID_OPERATION, "GL_CHROMIUM_path_rendering is not available."));
+        return false;
+    }
+    if (!context->hasPath(path))
+    {
+        context->handleError(Error(GL_INVALID_OPERATION, "No such path object."));
+        return false;
+    }
+
+    if (numCommands < 0)
+    {
+        context->handleError(Error(GL_INVALID_VALUE, "Invalid number of commands."));
+        return false;
+    }
+    else if (numCommands > 0)
+    {
+        if (!commands)
+        {
+            context->handleError(Error(GL_INVALID_VALUE, "No commands array given."));
+            return false;
+        }
+    }
+
+    if (numCoords < 0)
+    {
+        context->handleError(Error(GL_INVALID_VALUE, "Invalid number of coordinates."));
+        return false;
+    }
+    else if (numCoords > 0)
+    {
+        if (!coords)
+        {
+            context->handleError(Error(GL_INVALID_VALUE, "No coordinate array given."));
+            return false;
+        }
+    }
+
+    std::uint32_t coordTypeSize = 0;
+    switch (coordType)
+    {
+        case GL_BYTE:
+            coordTypeSize = sizeof(GLbyte);
+            break;
+
+        case GL_UNSIGNED_BYTE:
+            coordTypeSize = sizeof(GLubyte);
+            break;
+
+        case GL_SHORT:
+            coordTypeSize = sizeof(GLshort);
+            break;
+
+        case GL_UNSIGNED_SHORT:
+            coordTypeSize = sizeof(GLushort);
+            break;
+
+        case GL_FLOAT:
+            coordTypeSize = sizeof(GLfloat);
+            break;
+
+        default:
+            context->handleError(Error(GL_INVALID_ENUM, "Invalid coordinate type."));
+            return false;
+    }
+
+    angle::CheckedNumeric<std::uint32_t> checkedSize(numCommands);
+    checkedSize += (coordTypeSize * numCoords);
+    if (!checkedSize.IsValid())
+    {
+        context->handleError(Error(GL_INVALID_OPERATION, "Coord size overflow."));
+        return false;
+    }
+
+    // early return skips command data validation when it doesn't exist.
+    if (!commands)
+        return true;
+
+    GLsizei expectedNumCoords = 0;
+    for (GLsizei i = 0; i < numCommands; ++i)
+    {
+        switch (commands[i])
+        {
+            case GL_CLOSE_PATH_CHROMIUM:  // no coordinates.
+                break;
+            case GL_MOVE_TO_CHROMIUM:
+            case GL_LINE_TO_CHROMIUM:
+                expectedNumCoords += 2;
+                break;
+            case GL_QUADRATIC_CURVE_TO_CHROMIUM:
+                expectedNumCoords += 4;
+                break;
+            case GL_CUBIC_CURVE_TO_CHROMIUM:
+                expectedNumCoords += 6;
+                break;
+            case GL_CONIC_CURVE_TO_CHROMIUM:
+                expectedNumCoords += 5;
+                break;
+            default:
+                context->handleError(Error(GL_INVALID_ENUM, "Invalid command."));
+                return false;
+        }
+    }
+    if (expectedNumCoords != numCoords)
+    {
+        context->handleError(Error(GL_INVALID_VALUE, "Invalid number of coordinates."));
+        return false;
+    }
+
+    return true;
+}
+
+bool ValidateSetPathParameter(Context *context, GLuint path, GLenum pname, GLfloat value)
+{
+    if (!context->getExtensions().pathRendering)
+    {
+        context->handleError(
+            Error(GL_INVALID_OPERATION, "GL_CHROMIUM_path_rendering is not available."));
+        return false;
+    }
+    if (!context->hasPath(path))
+    {
+        context->handleError(Error(GL_INVALID_OPERATION, "No such path object."));
+        return false;
+    }
+
+    switch (pname)
+    {
+        case GL_PATH_STROKE_WIDTH_CHROMIUM:
+            if (value < 0.0f)
+            {
+                context->handleError(Error(GL_INVALID_VALUE, "Invalid stroke width."));
+                return false;
+            }
+            break;
+        case GL_PATH_END_CAPS_CHROMIUM:
+            switch (static_cast<GLenum>(value))
+            {
+                case GL_FLAT_CHROMIUM:
+                case GL_SQUARE_CHROMIUM:
+                case GL_ROUND_CHROMIUM:
+                    break;
+                default:
+                    context->handleError(Error(GL_INVALID_ENUM, "Invalid end caps."));
+                    return false;
+            }
+            break;
+        case GL_PATH_JOIN_STYLE_CHROMIUM:
+            switch (static_cast<GLenum>(value))
+            {
+                case GL_MITER_REVERT_CHROMIUM:
+                case GL_BEVEL_CHROMIUM:
+                case GL_ROUND_CHROMIUM:
+                    break;
+                default:
+                    context->handleError(Error(GL_INVALID_ENUM, "Invalid join style."));
+                    return false;
+            }
+        case GL_PATH_MITER_LIMIT_CHROMIUM:
+            if (value < 0.0f)
+            {
+                context->handleError(Error(GL_INVALID_VALUE, "Invalid miter limit."));
+                return false;
+            }
+            break;
+
+        case GL_PATH_STROKE_BOUND_CHROMIUM:
+            // no errors, only clamping.
+            break;
+
+        default:
+            context->handleError(Error(GL_INVALID_ENUM, "Invalid path parameter."));
+            return false;
+    }
+    return true;
+}
+
+bool ValidateGetPathParameter(Context *context, GLuint path, GLenum pname, GLfloat *value)
+{
+    if (!context->getExtensions().pathRendering)
+    {
+        context->handleError(
+            Error(GL_INVALID_OPERATION, "GL_CHROMIUM_path_rendering is not available."));
+        return false;
+    }
+
+    if (!context->hasPath(path))
+    {
+        context->handleError(Error(GL_INVALID_OPERATION, "No such path object."));
+        return false;
+    }
+    if (!value)
+    {
+        context->handleError(Error(GL_INVALID_VALUE, "No value array."));
+        return false;
+    }
+
+    switch (pname)
+    {
+        case GL_PATH_STROKE_WIDTH_CHROMIUM:
+        case GL_PATH_END_CAPS_CHROMIUM:
+        case GL_PATH_JOIN_STYLE_CHROMIUM:
+        case GL_PATH_MITER_LIMIT_CHROMIUM:
+        case GL_PATH_STROKE_BOUND_CHROMIUM:
+            break;
+
+        default:
+            context->handleError(Error(GL_INVALID_ENUM, "Invalid path parameter."));
+            return false;
+    }
+
+    return true;
+}
+
+bool ValidatePathStencilFunc(Context *context, GLenum func, GLint ref, GLuint mask)
+{
+    if (!context->getExtensions().pathRendering)
+    {
+        context->handleError(
+            Error(GL_INVALID_OPERATION, "GL_CHROMIUM_path_rendering is not available."));
+        return false;
+    }
+
+    switch (func)
+    {
+        case GL_NEVER:
+        case GL_ALWAYS:
+        case GL_LESS:
+        case GL_LEQUAL:
+        case GL_EQUAL:
+        case GL_GEQUAL:
+        case GL_GREATER:
+        case GL_NOTEQUAL:
+            break;
+        default:
+            context->handleError(Error(GL_INVALID_ENUM, "Invalid stencil function."));
+            return false;
+    }
+
+    return true;
+}
+
+// Note that the spec specifies that for the path drawing commands
+// if the path object is not an existing path object the command
+// does nothing and no error is generated.
+// However if the path object exists but has not been specified any
+// commands then an error is generated.
+
+bool ValidateStencilFillPath(Context *context, GLuint path, GLenum fillMode, GLuint mask)
+{
+    if (!context->getExtensions().pathRendering)
+    {
+        context->handleError(
+            Error(GL_INVALID_OPERATION, "GL_CHROMIUM_path_rendering is not available."));
+        return false;
+    }
+    if (context->hasPath(path) && !context->hasPathData(path))
+    {
+        context->handleError(Error(GL_INVALID_OPERATION, "No such path object."));
+        return false;
+    }
+
+    switch (fillMode)
+    {
+        case GL_COUNT_UP_CHROMIUM:
+        case GL_COUNT_DOWN_CHROMIUM:
+            break;
+        default:
+            context->handleError(Error(GL_INVALID_ENUM, "Invalid fill mode."));
+            return false;
+    }
+
+    if (!isPow2(mask + 1))
+    {
+        context->handleError(Error(GL_INVALID_VALUE, "Invalid stencil bit mask."));
+        return false;
+    }
+
+    return true;
+}
+
+bool ValidateStencilStrokePath(Context *context, GLuint path, GLint reference, GLuint mask)
+{
+    if (!context->getExtensions().pathRendering)
+    {
+        context->handleError(
+            Error(GL_INVALID_OPERATION, "GL_CHROMIUM_path_rendering is not available."));
+        return false;
+    }
+    if (context->hasPath(path) && !context->hasPathData(path))
+    {
+        context->handleError(Error(GL_INVALID_OPERATION, "No such path or path has no data."));
+        return false;
+    }
+
+    return true;
+}
+
+bool ValidateCoverPath(Context *context, GLuint path, GLenum coverMode)
+{
+    if (!context->getExtensions().pathRendering)
+    {
+        context->handleError(
+            Error(GL_INVALID_OPERATION, "GL_CHROMIUM_path_rendering is not available."));
+        return false;
+    }
+    if (context->hasPath(path) && !context->hasPathData(path))
+    {
+        context->handleError(Error(GL_INVALID_OPERATION, "No such path object."));
+        return false;
+    }
+
+    switch (coverMode)
+    {
+        case GL_CONVEX_HULL_CHROMIUM:
+        case GL_BOUNDING_BOX_CHROMIUM:
+            break;
+        default:
+            context->handleError(Error(GL_INVALID_ENUM, "Invalid cover mode."));
+            return false;
+    }
+    return true;
+}
+
+bool ValidateStencilThenCoverFillPath(Context *context,
+                                      GLuint path,
+                                      GLenum fillMode,
+                                      GLuint mask,
+                                      GLenum coverMode)
+{
+    return ValidateStencilFillPath(context, path, fillMode, mask) &&
+           ValidateCoverPath(context, path, coverMode);
+}
+
+bool ValidateStencilThenCoverStrokePath(Context *context,
+                                        GLuint path,
+                                        GLint reference,
+                                        GLuint mask,
+                                        GLenum coverMode)
+{
+    return ValidateStencilStrokePath(context, path, reference, mask) &&
+           ValidateCoverPath(context, path, coverMode);
+}
+
+bool ValidateIsPath(Context *context)
+{
+    if (!context->getExtensions().pathRendering)
+    {
+        context->handleError(
+            Error(GL_INVALID_OPERATION, "GL_CHROMIUM_path_rendering is not available."));
+        return false;
+    }
+    return true;
+}
+
 }  // namespace gl
diff --git a/src/libANGLE/validationES2.h b/src/libANGLE/validationES2.h
index 07f0440..5433e8d 100644
--- a/src/libANGLE/validationES2.h
+++ b/src/libANGLE/validationES2.h
@@ -190,6 +190,36 @@
 
 bool ValidateCoverageModulationCHROMIUM(Context *context, GLenum components);
 
+// CHROMIUM_path_rendering
+bool ValidateMatrix(Context *context, GLenum matrixMode, const GLfloat *matrix);
+bool ValidateMatrixMode(Context *context, GLenum matrixMode);
+bool ValidateGenPaths(Context *context, GLsizei range);
+bool ValidateDeletePaths(Context *context, GLuint first, GLsizei range);
+bool ValidatePathCommands(Context *context,
+                          GLuint path,
+                          GLsizei numCommands,
+                          const GLubyte *commands,
+                          GLsizei numCoords,
+                          GLenum coordType,
+                          const void *coords);
+bool ValidateSetPathParameter(Context *context, GLuint path, GLenum pname, GLfloat value);
+bool ValidateGetPathParameter(Context *context, GLuint path, GLenum pname, GLfloat *value);
+bool ValidatePathStencilFunc(Context *context, GLenum func, GLint ref, GLuint mask);
+bool ValidateStencilFillPath(Context *context, GLuint path, GLenum fillMode, GLuint mask);
+bool ValidateStencilStrokePath(Context *context, GLuint path, GLint reference, GLuint mask);
+bool ValidateCoverPath(Context *context, GLuint path, GLenum coverMode);
+bool ValidateStencilThenCoverFillPath(Context *context,
+                                      GLuint path,
+                                      GLenum fillMode,
+                                      GLuint mask,
+                                      GLenum coverMode);
+bool ValidateStencilThenCoverStrokePath(Context *context,
+                                        GLuint path,
+                                        GLint reference,
+                                        GLuint mask,
+                                        GLenum coverMode);
+bool ValidateIsPath(Context *context);
+
 }  // namespace gl
 
 #endif // LIBANGLE_VALIDATION_ES2_H_
diff --git a/src/libGLESv2.gypi b/src/libGLESv2.gypi
index 0150e5c..710c1c1 100644
--- a/src/libGLESv2.gypi
+++ b/src/libGLESv2.gypi
@@ -92,12 +92,16 @@
             'libANGLE/FramebufferAttachment.h',
             'libANGLE/HandleAllocator.cpp',
             'libANGLE/HandleAllocator.h',
+            'libANGLE/HandleRangeAllocator.h',
+            'libANGLE/HandleRangeAllocator.cpp',
             'libANGLE/Image.h',
             'libANGLE/Image.cpp',
             'libANGLE/ImageIndex.h',
             'libANGLE/ImageIndex.cpp',
             'libANGLE/IndexRangeCache.cpp',
             'libANGLE/IndexRangeCache.h',
+            'libANGLE/Path.h',
+            'libANGLE/Path.cpp',
             'libANGLE/Platform.cpp',
             'libANGLE/Program.cpp',
             'libANGLE/Program.h',
@@ -453,6 +457,8 @@
             'libANGLE/renderer/gl/FramebufferGL.h',
             'libANGLE/renderer/gl/FunctionsGL.cpp',
             'libANGLE/renderer/gl/FunctionsGL.h',
+            'libANGLE/renderer/gl/PathGL.h',
+            'libANGLE/renderer/gl/PathGL.cpp',
             'libANGLE/renderer/gl/ProgramGL.cpp',
             'libANGLE/renderer/gl/ProgramGL.h',
             'libANGLE/renderer/gl/QueryGL.cpp',
diff --git a/src/libGLESv2/entry_points_gles_2_0_ext.cpp b/src/libGLESv2/entry_points_gles_2_0_ext.cpp
index ff88e07..666ea10 100644
--- a/src/libGLESv2/entry_points_gles_2_0_ext.cpp
+++ b/src/libGLESv2/entry_points_gles_2_0_ext.cpp
@@ -1400,4 +1400,269 @@
         context->setCoverageModulation(components);
     }
 }
+
+// CHROMIUM_path_rendering
+ANGLE_EXPORT void GL_APIENTRY MatrixLoadfCHROMIUM(GLenum matrixMode, const GLfloat *matrix)
+{
+    EVENT("(GLenum matrixMode = %u)", matrixMode);
+
+    Context *context = GetValidGlobalContext();
+    if (context)
+    {
+        if (!context->skipValidation() && !ValidateMatrix(context, matrixMode, matrix))
+        {
+            return;
+        }
+        context->loadPathRenderingMatrix(matrixMode, matrix);
+    }
 }
+
+ANGLE_EXPORT void GL_APIENTRY MatrixLoadIdentityCHROMIUM(GLenum matrixMode)
+{
+    EVENT("(GLenum matrixMode = %u)", matrixMode);
+
+    Context *context = GetValidGlobalContext();
+    if (context)
+    {
+        if (!context->skipValidation() && !ValidateMatrixMode(context, matrixMode))
+        {
+            return;
+        }
+        context->loadPathRenderingIdentityMatrix(matrixMode);
+    }
+}
+
+ANGLE_EXPORT GLuint GL_APIENTRY GenPathsCHROMIUM(GLsizei range)
+{
+    EVENT("(GLsizei range = %d)", range);
+
+    Context *context = GetValidGlobalContext();
+    if (context)
+    {
+        if (!context->skipValidation() && !ValidateGenPaths(context, range))
+        {
+            return 0;
+        }
+        return context->createPaths(range);
+    }
+    return 0;
+}
+
+ANGLE_EXPORT void GL_APIENTRY DeletePathsCHROMIUM(GLuint first, GLsizei range)
+{
+    EVENT("(GLuint first = %u, GLsizei range = %d)", first, range);
+
+    Context *context = GetValidGlobalContext();
+    if (context)
+    {
+        if (!context->skipValidation() && !ValidateDeletePaths(context, first, range))
+        {
+            return;
+        }
+        context->deletePaths(first, range);
+    }
+}
+
+ANGLE_EXPORT GLboolean GL_APIENTRY IsPathCHROMIUM(GLuint path)
+{
+    EVENT("(GLuint path = %u)", path);
+
+    Context *context = GetValidGlobalContext();
+    if (context)
+    {
+        if (!context->skipValidation() && !ValidateIsPath(context))
+        {
+            return GL_FALSE;
+        }
+        return context->hasPathData(path);
+    }
+    return GL_FALSE;
+}
+
+ANGLE_EXPORT void GL_APIENTRY PathCommandsCHROMIUM(GLuint path,
+                                                   GLsizei numCommands,
+                                                   const GLubyte *commands,
+                                                   GLsizei numCoords,
+                                                   GLenum coordType,
+                                                   const void *coords)
+{
+    EVENT(
+        "(GLuint path = %u, GLsizei numCommands = %d, commands = %p, "
+        "GLsizei numCoords = %d, GLenum coordType = %u, void* coords = %p)",
+        path, numCommands, commands, numCoords, coordType, coords);
+
+    Context *context = GetValidGlobalContext();
+    if (context)
+    {
+        if (!context->skipValidation())
+        {
+            if (!ValidatePathCommands(context, path, numCommands, commands, numCoords, coordType,
+                                      coords))
+            {
+                return;
+            }
+        }
+        context->setPathCommands(path, numCommands, commands, numCoords, coordType, coords);
+    }
+}
+
+ANGLE_EXPORT void GL_APIENTRY PathParameterfCHROMIUM(GLuint path, GLenum pname, GLfloat value)
+{
+    EVENT("(GLuint path = %u, GLenum pname = %u, GLfloat value = %f)", path, pname, value);
+
+    Context *context = GetValidGlobalContext();
+    if (context)
+    {
+        if (!context->skipValidation() && !ValidateSetPathParameter(context, path, pname, value))
+        {
+            return;
+        }
+        context->setPathParameterf(path, pname, value);
+    }
+}
+
+ANGLE_EXPORT void GL_APIENTRY PathParameteriCHROMIUM(GLuint path, GLenum pname, GLint value)
+{
+    PathParameterfCHROMIUM(path, pname, static_cast<GLfloat>(value));
+}
+
+ANGLE_EXPORT void GL_APIENTRY GetPathParameterfCHROMIUM(GLuint path, GLenum pname, GLfloat *value)
+{
+    EVENT("(GLuint path = %u, GLenum pname = %u)", path, pname);
+
+    Context *context = GetValidGlobalContext();
+    if (context)
+    {
+        if (!context->skipValidation() && !ValidateGetPathParameter(context, path, pname, value))
+        {
+            return;
+        }
+        context->getPathParameterfv(path, pname, value);
+    }
+}
+
+ANGLE_EXPORT void GL_APIENTRY GetPathParameteriCHROMIUM(GLuint path, GLenum pname, GLint *value)
+{
+    GLfloat val = 0.0f;
+    GetPathParameterfCHROMIUM(path, pname, value != nullptr ? &val : nullptr);
+    if (value)
+        *value = static_cast<GLint>(val);
+}
+
+ANGLE_EXPORT void GL_APIENTRY PathStencilFuncCHROMIUM(GLenum func, GLint ref, GLuint mask)
+{
+    EVENT("(GLenum func = %u, GLint ref = %d, GLuint mask = %u)", func, ref, mask);
+
+    Context *context = GetValidGlobalContext();
+    if (context)
+    {
+        if (!context->skipValidation() && !ValidatePathStencilFunc(context, func, ref, mask))
+        {
+            return;
+        }
+        context->setPathStencilFunc(func, ref, mask);
+    }
+}
+
+ANGLE_EXPORT void GL_APIENTRY StencilFillPathCHROMIUM(GLuint path, GLenum fillMode, GLuint mask)
+{
+    EVENT("(GLuint path = %u, GLenum fillMode = %u, GLuint mask = %u)", path, fillMode, mask);
+
+    Context *context = GetValidGlobalContext();
+    if (context)
+    {
+        if (!context->skipValidation() && !ValidateStencilFillPath(context, path, fillMode, mask))
+        {
+            return;
+        }
+        context->stencilFillPath(path, fillMode, mask);
+    }
+}
+
+ANGLE_EXPORT void GL_APIENTRY StencilStrokePathCHROMIUM(GLuint path, GLint reference, GLuint mask)
+{
+    EVENT("(GLuint path = %u, GLint ference = %d, GLuint mask = %u)", path, reference, mask);
+
+    Context *context = GetValidGlobalContext();
+    if (context)
+    {
+        if (!context->skipValidation() &&
+            !ValidateStencilStrokePath(context, path, reference, mask))
+        {
+            return;
+        }
+        context->stencilStrokePath(path, reference, mask);
+    }
+}
+
+ANGLE_EXPORT void GL_APIENTRY CoverFillPathCHROMIUM(GLuint path, GLenum coverMode)
+{
+    EVENT("(GLuint path = %u, GLenum coverMode = %u)", path, coverMode);
+
+    Context *context = GetValidGlobalContext();
+    if (context)
+    {
+        if (!context->skipValidation() && !ValidateCoverPath(context, path, coverMode))
+        {
+            return;
+        }
+        context->coverFillPath(path, coverMode);
+    }
+}
+
+ANGLE_EXPORT void GL_APIENTRY CoverStrokePathCHROMIUM(GLuint path, GLenum coverMode)
+{
+    EVENT("(GLuint path = %u, GLenum coverMode = %u)", path, coverMode);
+
+    Context *context = GetValidGlobalContext();
+    if (context)
+    {
+        if (!context->skipValidation() && !ValidateCoverPath(context, path, coverMode))
+        {
+            return;
+        }
+        context->coverStrokePath(path, coverMode);
+    }
+}
+
+ANGLE_EXPORT void GL_APIENTRY StencilThenCoverFillPathCHROMIUM(GLuint path,
+                                                               GLenum fillMode,
+                                                               GLuint mask,
+                                                               GLenum coverMode)
+{
+    EVENT("(GLuint path = %u, GLenum fillMode = %u, GLuint mask = %u, GLenum coverMode = %u)", path,
+          fillMode, mask, coverMode);
+
+    Context *context = GetValidGlobalContext();
+    if (context)
+    {
+        if (!context->skipValidation() &&
+            !ValidateStencilThenCoverFillPath(context, path, fillMode, mask, coverMode))
+        {
+            return;
+        }
+        context->stencilThenCoverFillPath(path, fillMode, mask, coverMode);
+    }
+}
+
+ANGLE_EXPORT void GL_APIENTRY StencilThenCoverStrokePathCHROMIUM(GLuint path,
+                                                                 GLint reference,
+                                                                 GLuint mask,
+                                                                 GLenum coverMode)
+{
+    EVENT("(GLuint path = %u, GLint reference = %d, GLuint mask = %u, GLenum coverMode = %u)", path,
+          reference, mask, coverMode);
+
+    Context *context = GetValidGlobalContext();
+    if (context)
+    {
+        if (!context->skipValidation() &&
+            !ValidateStencilThenCoverStrokePath(context, path, reference, mask, coverMode))
+        {
+            return;
+        }
+        context->stencilThenCoverStrokePath(path, reference, mask, coverMode);
+    }
+}
+
+}  // gl
diff --git a/src/libGLESv2/entry_points_gles_2_0_ext.h b/src/libGLESv2/entry_points_gles_2_0_ext.h
index 06aab47..c920bd5 100644
--- a/src/libGLESv2/entry_points_gles_2_0_ext.h
+++ b/src/libGLESv2/entry_points_gles_2_0_ext.h
@@ -147,7 +147,38 @@
                                                           const GLchar *name);
 
 // GL_CHROMIUM_framebuffer_mixed_samples
+ANGLE_EXPORT void GL_APIENTRY MatrixLoadfCHROMIUM(GLenum matrixMode, const GLfloat *matrix);
+ANGLE_EXPORT void GL_APIENTRY MatrixLoadIdentityCHROMIUM(GLenum matrixMode);
+
 ANGLE_EXPORT void GL_APIENTRY CoverageModulationCHROMIUM(GLenum components);
+
+// GL_CHROMIUM_path_rendering
+ANGLE_EXPORT GLuint GL_APIENTRY GenPathsCHROMIUM(GLsizei chromium);
+ANGLE_EXPORT void GL_APIENTRY DeletePathsCHROMIUM(GLuint first, GLsizei range);
+ANGLE_EXPORT GLboolean GL_APIENTRY IsPathCHROMIUM(GLuint path);
+ANGLE_EXPORT void GL_APIENTRY PathCommandsCHROMIUM(GLuint path,
+                                                   GLsizei numCommands,
+                                                   const GLubyte *commands,
+                                                   GLsizei numCoords,
+                                                   GLenum coordType,
+                                                   const void *coords);
+ANGLE_EXPORT void GL_APIENTRY PathParameterfCHROMIUM(GLuint path, GLenum pname, GLfloat value);
+ANGLE_EXPORT void GL_APIENTRY PathParameteriCHROMIUM(GLuint path, GLenum pname, GLint value);
+ANGLE_EXPORT void GL_APIENTRY GetPathParameterfCHROMIUM(GLuint path, GLenum pname, GLfloat *value);
+ANGLE_EXPORT void GL_APIENTRY GetPathParameteriCHROMIUM(GLuint path, GLenum pname, GLint *value);
+ANGLE_EXPORT void GL_APIENTRY PathStencilFuncCHROMIUM(GLenum func, GLint ref, GLuint mask);
+ANGLE_EXPORT void GL_APIENTRY StencilFillPathCHROMIUM(GLuint path, GLenum fillMode, GLuint mask);
+ANGLE_EXPORT void GL_APIENTRY StencilStrokePathCHROMIUM(GLuint path, GLint reference, GLuint mask);
+ANGLE_EXPORT void GL_APIENTRY CoverFillPathCHROMIUM(GLuint path, GLenum coverMode);
+ANGLE_EXPORT void GL_APIENTRY CoverStrokePathCHROMIUM(GLuint path, GLenum coverMode);
+ANGLE_EXPORT void GL_APIENTRY StencilThenCoverFillPathCHROMIUM(GLuint path,
+                                                               GLenum fillMode,
+                                                               GLuint mask,
+                                                               GLenum coverMode);
+ANGLE_EXPORT void GL_APIENTRY StencilThenCoverStrokePathCHROMIUM(GLuint path,
+                                                                 GLint reference,
+                                                                 GLuint mask,
+                                                                 GLenum coverMode);
 }
 
 #endif // LIBGLESV2_ENTRYPOINTGLES20EXT_H_
diff --git a/src/libGLESv2/libGLESv2.cpp b/src/libGLESv2/libGLESv2.cpp
index 26b3e3c..25e95fe 100644
--- a/src/libGLESv2/libGLESv2.cpp
+++ b/src/libGLESv2/libGLESv2.cpp
@@ -1573,3 +1573,100 @@
     return gl::CoverageModulationCHROMIUM(components);
 }
 }
+
+// CHROMIUM_path_rendendering
+void GL_APIENTRY glMatrixLoadfCHROMIUM(GLenum matrixMode, const GLfloat *matrix)
+{
+    gl::MatrixLoadfCHROMIUM(matrixMode, matrix);
+}
+
+void GL_APIENTRY glMatrixLoadIdentityCHROMIUM(GLenum matrixMode)
+{
+    gl::MatrixLoadIdentityCHROMIUM(matrixMode);
+}
+
+GLuint GL_APIENTRY glGenPathsCHROMIUM(GLsizei range)
+{
+    return gl::GenPathsCHROMIUM(range);
+}
+
+void GL_APIENTRY glDeletePathsCHROMIUM(GLuint first, GLsizei range)
+{
+    gl::DeletePathsCHROMIUM(first, range);
+}
+
+GLboolean GL_APIENTRY glIsPathCHROMIUM(GLuint path)
+{
+    return gl::IsPathCHROMIUM(path);
+}
+
+void GL_APIENTRY glPathCommandsCHROMIUM(GLuint path,
+                                        GLsizei numCommands,
+                                        const GLubyte *commands,
+                                        GLsizei numCoords,
+                                        GLenum coordType,
+                                        const void *coords)
+{
+    gl::PathCommandsCHROMIUM(path, numCommands, commands, numCoords, coordType, coords);
+}
+
+void GL_APIENTRY glPathParameterfCHROMIUM(GLuint path, GLenum pname, GLfloat value)
+{
+    gl::PathParameterfCHROMIUM(path, pname, value);
+}
+
+void GL_APIENTRY glPathParameteriCHROMIUM(GLuint path, GLenum pname, GLint value)
+{
+    gl::PathParameterfCHROMIUM(path, pname, value);
+}
+
+void GL_APIENTRY glGetPathParameterfvCHROMIUM(GLuint path, GLenum pname, GLfloat *value)
+{
+    gl::GetPathParameterfCHROMIUM(path, pname, value);
+}
+
+void GL_APIENTRY glGetPathParameterivCHROMIUM(GLuint path, GLenum pname, GLint *value)
+{
+    gl::GetPathParameteriCHROMIUM(path, pname, value);
+}
+
+void GL_APIENTRY glPathStencilFuncCHROMIUM(GLenum func, GLint ref, GLuint mask)
+{
+    gl::PathStencilFuncCHROMIUM(func, ref, mask);
+}
+
+void GL_APIENTRY glStencilFillPathCHROMIUM(GLuint path, GLenum fillMode, GLuint mask)
+{
+    gl::StencilFillPathCHROMIUM(path, fillMode, mask);
+}
+
+void GL_APIENTRY glStencilStrokePathCHROMIUM(GLuint path, GLint reference, GLuint mask)
+{
+    gl::StencilStrokePathCHROMIUM(path, reference, mask);
+}
+
+void GL_APIENTRY glCoverFillPathCHROMIUM(GLuint path, GLenum coverMode)
+{
+    gl::CoverFillPathCHROMIUM(path, coverMode);
+}
+
+void GL_APIENTRY glCoverStrokePathCHROMIUM(GLuint path, GLenum coverMode)
+{
+    gl::CoverStrokePathCHROMIUM(path, coverMode);
+}
+
+void GL_APIENTRY glStencilThenCoverFillPathCHROMIUM(GLuint path,
+                                                    GLenum fillMode,
+                                                    GLuint mask,
+                                                    GLenum coverMode)
+{
+    gl::StencilThenCoverFillPathCHROMIUM(path, fillMode, mask, coverMode);
+}
+
+void GL_APIENTRY glStencilThenCoverStrokePathCHROMIUM(GLuint path,
+                                                      GLint reference,
+                                                      GLuint mask,
+                                                      GLenum coverMode)
+{
+    gl::StencilThenCoverStrokePathCHROMIUM(path, reference, mask, coverMode);
+}
\ No newline at end of file
diff --git a/src/libGLESv2/libGLESv2.def b/src/libGLESv2/libGLESv2.def
index 4b98faa..716303b 100644
--- a/src/libGLESv2/libGLESv2.def
+++ b/src/libGLESv2/libGLESv2.def
@@ -205,6 +205,24 @@
     glBindUniformLocationCHROMIUM   @318
     glCoverageModulationCHROMIUM    @319
 
+    glMatrixLoadfCHROMIUM           @320
+    glMatrixLoadIdentityCHROMIUM    @321
+    glGenPathsCHROMIUM              @322
+    glDeletePathsCHROMIUM           @323
+    glIsPathCHROMIUM                @324
+    glPathCommandsCHROMIUM          @325
+    glPathParameterfCHROMIUM        @326
+    glPathParameteriCHROMIUM        @327
+    glGetPathParameterfvCHROMIUM    @328
+    glGetPathParameterivCHROMIUM    @329
+    glPathStencilFuncCHROMIUM       @330
+    glStencilFillPathCHROMIUM       @331
+    glStencilStrokePathCHROMIUM     @332
+    glCoverFillPathCHROMIUM         @333
+    glCoverStrokePathCHROMIUM       @334
+    glStencilThenCoverFillPathCHROMIUM   @335
+    glStencilThenCoverStrokePathCHROMIUM @336
+
     ; GLES 3.0 Functions
     glReadBuffer                    @180
     glDrawRangeElements             @181
diff --git a/src/tests/angle_end2end_tests.gypi b/src/tests/angle_end2end_tests.gypi
index 625b185..a5be4bc 100644
--- a/src/tests/angle_end2end_tests.gypi
+++ b/src/tests/angle_end2end_tests.gypi
@@ -15,8 +15,6 @@
     {
         'angle_end2end_tests_sources':
         [
-            '<(angle_path)/src/tests/gl_tests/MultisampleCompatibilityTest.cpp',
-            '<(angle_path)/src/tests/gl_tests/FramebufferMixedSamplesTest.cpp',
             '<(angle_path)/src/tests/gl_tests/BindUniformLocationTest.cpp',
             '<(angle_path)/src/tests/gl_tests/BlendMinMaxTest.cpp',
             '<(angle_path)/src/tests/gl_tests/BlitFramebufferANGLETest.cpp',
@@ -35,6 +33,7 @@
             '<(angle_path)/src/tests/gl_tests/DrawElementsTest.cpp',
             '<(angle_path)/src/tests/gl_tests/ETCTextureTest.cpp',
             '<(angle_path)/src/tests/gl_tests/FenceSyncTests.cpp',
+            '<(angle_path)/src/tests/gl_tests/FramebufferMixedSamplesTest.cpp',
             '<(angle_path)/src/tests/gl_tests/FramebufferRenderMipmapTest.cpp',
             '<(angle_path)/src/tests/gl_tests/FramebufferTest.cpp',
             '<(angle_path)/src/tests/gl_tests/GLSLTest.cpp',
@@ -46,8 +45,10 @@
             '<(angle_path)/src/tests/gl_tests/LineLoopTest.cpp',
             '<(angle_path)/src/tests/gl_tests/MaxTextureSizeTest.cpp',
             '<(angle_path)/src/tests/gl_tests/MipmapTest.cpp',
+            '<(angle_path)/src/tests/gl_tests/MultisampleCompatibilityTest.cpp',
             '<(angle_path)/src/tests/gl_tests/media/pixel.inl',
             '<(angle_path)/src/tests/gl_tests/PackUnpackTest.cpp',
+            '<(angle_path)/src/tests/gl_tests/PathRenderingTest.cpp',
             '<(angle_path)/src/tests/gl_tests/PbufferTest.cpp',
             '<(angle_path)/src/tests/gl_tests/PBOExtensionTest.cpp',
             '<(angle_path)/src/tests/gl_tests/PointSpritesTest.cpp',
diff --git a/src/tests/angle_unittests.gypi b/src/tests/angle_unittests.gypi
index f998ad8..0a5d2e8 100644
--- a/src/tests/angle_unittests.gypi
+++ b/src/tests/angle_unittests.gypi
@@ -24,6 +24,7 @@
             '<(angle_path)/src/libANGLE/Config_unittest.cpp',
             '<(angle_path)/src/libANGLE/Fence_unittest.cpp',
             '<(angle_path)/src/libANGLE/HandleAllocator_unittest.cpp',
+            '<(angle_path)/src/libANGLE/HandleRangeAllocator_unittest.cpp',
             '<(angle_path)/src/libANGLE/Image_unittest.cpp',
             '<(angle_path)/src/libANGLE/ImageIndexIterator_unittest.cpp',
             '<(angle_path)/src/libANGLE/Program_unittest.cpp',
diff --git a/src/tests/angle_unittests_utils.h b/src/tests/angle_unittests_utils.h
index 99f9f0d..d32077e 100644
--- a/src/tests/angle_unittests_utils.h
+++ b/src/tests/angle_unittests_utils.h
@@ -59,6 +59,11 @@
 
     // Sampler object creation
     SamplerImpl *createSampler() override { return nullptr; }
+
+    std::vector<PathImpl *> createPaths(GLsizei range) override
+    {
+        return std::vector<PathImpl *>();
+    }
 };
 
 // A class with all the factory methods mocked.
@@ -79,6 +84,7 @@
     MOCK_METHOD0(createFenceSync, FenceSyncImpl *());
     MOCK_METHOD0(createTransformFeedback, TransformFeedbackImpl *());
     MOCK_METHOD0(createSampler, SamplerImpl *());
+    MOCK_METHOD1(createPaths, std::vector<PathImpl *>(GLsizei));
 };
 
 class MockEGLFactory : public EGLImplFactory
diff --git a/src/tests/gl_tests/PathRenderingTest.cpp b/src/tests/gl_tests/PathRenderingTest.cpp
new file mode 100644
index 0000000..e1032c7
--- /dev/null
+++ b/src/tests/gl_tests/PathRenderingTest.cpp
@@ -0,0 +1,742 @@
+//
+// Copyright 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// CHROMIUMPathRenderingTest
+//   Test CHROMIUM subset of NV_path_rendering
+//   This extension allows to render geometric paths as first class GL objects.
+
+#include "test_utils/ANGLETest.h"
+#include "shader_utils.h"
+
+#include <cmath>
+#include <cstring>
+#include <cstddef>
+#include <fstream>
+
+using namespace angle;
+
+namespace
+{
+
+bool CheckPixels(GLint x,
+                 GLint y,
+                 GLsizei width,
+                 GLsizei height,
+                 GLint tolerance,
+                 const angle::GLColor &color)
+{
+    for (GLint yy = 0; yy < height; ++yy)
+    {
+        for (GLint xx = 0; xx < width; ++xx)
+        {
+            const auto px = x + xx;
+            const auto py = y + yy;
+            EXPECT_PIXEL_COLOR_EQ(px, py, color);
+        }
+    }
+
+    return true;
+}
+
+void ExpectEqualMatrix(const GLfloat *expected, const GLfloat *actual)
+{
+    for (size_t i = 0; i < 16; ++i)
+    {
+        EXPECT_EQ(expected[i], actual[i]);
+    }
+}
+
+void ExpectEqualMatrix(const GLfloat *expected, const GLint *actual)
+{
+    for (size_t i = 0; i < 16; ++i)
+    {
+        EXPECT_EQ(static_cast<GLint>(std::roundf(expected[i])), actual[i]);
+    }
+}
+
+const int kResolution = 300;
+
+class CHROMIUMPathRenderingTest : public ANGLETest
+{
+  protected:
+    CHROMIUMPathRenderingTest()
+    {
+        setWindowWidth(kResolution);
+        setWindowHeight(kResolution);
+        setConfigRedBits(8);
+        setConfigGreenBits(8);
+        setConfigBlueBits(8);
+        setConfigAlphaBits(8);
+        setConfigDepthBits(8);
+        setConfigStencilBits(8);
+    }
+
+    bool isApplicable() const { return extensionEnabled("GL_CHROMIUM_path_rendering"); }
+
+    void tryAllDrawFunctions(GLuint path, GLenum err)
+    {
+        glStencilFillPathCHROMIUM(path, GL_COUNT_UP_CHROMIUM, 0x7F);
+        EXPECT_GL_ERROR(err);
+
+        glStencilFillPathCHROMIUM(path, GL_COUNT_DOWN_CHROMIUM, 0x7F);
+        EXPECT_GL_ERROR(err);
+
+        glStencilStrokePathCHROMIUM(path, 0x80, 0x80);
+        EXPECT_GL_ERROR(err);
+
+        glCoverFillPathCHROMIUM(path, GL_BOUNDING_BOX_CHROMIUM);
+        EXPECT_GL_ERROR(err);
+
+        glCoverStrokePathCHROMIUM(path, GL_BOUNDING_BOX_CHROMIUM);
+        EXPECT_GL_ERROR(err);
+
+        glStencilThenCoverStrokePathCHROMIUM(path, 0x80, 0x80, GL_BOUNDING_BOX_CHROMIUM);
+        EXPECT_GL_ERROR(err);
+
+        glStencilThenCoverFillPathCHROMIUM(path, GL_COUNT_UP_CHROMIUM, 0x7F,
+                                           GL_BOUNDING_BOX_CHROMIUM);
+        EXPECT_GL_ERROR(err);
+    }
+};
+
+// Test setting and getting of path rendering matrices.
+TEST_P(CHROMIUMPathRenderingTest, TestMatrix)
+{
+    if (!isApplicable())
+        return;
+
+    static const GLfloat kIdentityMatrix[16] = {1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
+                                                0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f};
+
+    static const GLfloat kSeqMatrix[16] = {0.5f,   -0.5f,  -0.1f,  -0.8f, 4.4f,   5.5f,
+                                           6.6f,   7.7f,   8.8f,   9.9f,  10.11f, 11.22f,
+                                           12.33f, 13.44f, 14.55f, 15.66f};
+
+    static const GLenum kMatrixModes[] = {GL_PATH_MODELVIEW_CHROMIUM, GL_PATH_PROJECTION_CHROMIUM};
+
+    static const GLenum kGetMatrixModes[] = {GL_PATH_MODELVIEW_MATRIX_CHROMIUM,
+                                             GL_PATH_PROJECTION_MATRIX_CHROMIUM};
+
+    for (size_t i = 0; i < 2; ++i)
+    {
+        GLfloat mf[16];
+        GLint mi[16];
+        std::memset(mf, 0, sizeof(mf));
+        std::memset(mi, 0, sizeof(mi));
+        glGetFloatv(kGetMatrixModes[i], mf);
+        glGetIntegerv(kGetMatrixModes[i], mi);
+        ExpectEqualMatrix(kIdentityMatrix, mf);
+        ExpectEqualMatrix(kIdentityMatrix, mi);
+
+        glMatrixLoadfCHROMIUM(kMatrixModes[i], kSeqMatrix);
+        std::memset(mf, 0, sizeof(mf));
+        std::memset(mi, 0, sizeof(mi));
+        glGetFloatv(kGetMatrixModes[i], mf);
+        glGetIntegerv(kGetMatrixModes[i], mi);
+        ExpectEqualMatrix(kSeqMatrix, mf);
+        ExpectEqualMatrix(kSeqMatrix, mi);
+
+        glMatrixLoadIdentityCHROMIUM(kMatrixModes[i]);
+        std::memset(mf, 0, sizeof(mf));
+        std::memset(mi, 0, sizeof(mi));
+        glGetFloatv(kGetMatrixModes[i], mf);
+        glGetIntegerv(kGetMatrixModes[i], mi);
+        ExpectEqualMatrix(kIdentityMatrix, mf);
+        ExpectEqualMatrix(kIdentityMatrix, mi);
+
+        ASSERT_GL_NO_ERROR();
+    }
+}
+
+// Test that trying to set incorrect matrix target results
+// in a GL error.
+TEST_P(CHROMIUMPathRenderingTest, TestMatrixErrors)
+{
+    if (!isApplicable())
+        return;
+
+    GLfloat mf[16];
+    std::memset(mf, 0, sizeof(mf));
+
+    glMatrixLoadfCHROMIUM(GL_PATH_MODELVIEW_CHROMIUM, mf);
+    ASSERT_GL_NO_ERROR();
+
+    glMatrixLoadIdentityCHROMIUM(GL_PATH_PROJECTION_CHROMIUM);
+    ASSERT_GL_NO_ERROR();
+
+    // Test that invalid matrix targets fail.
+    glMatrixLoadfCHROMIUM(GL_PATH_MODELVIEW_CHROMIUM - 1, mf);
+    EXPECT_GL_ERROR(GL_INVALID_ENUM);
+
+    // Test that invalid matrix targets fail.
+    glMatrixLoadIdentityCHROMIUM(GL_PATH_PROJECTION_CHROMIUM + 1);
+    EXPECT_GL_ERROR(GL_INVALID_ENUM);
+}
+
+// Test basic path create and delete.
+TEST_P(CHROMIUMPathRenderingTest, TestGenDelete)
+{
+    if (!isApplicable())
+        return;
+
+    // This is unspecified in NV_path_rendering.
+    EXPECT_EQ(0u, glGenPathsCHROMIUM(0));
+    EXPECT_GL_ERROR(GL_INVALID_VALUE);
+
+    GLuint path = glGenPathsCHROMIUM(1);
+    EXPECT_NE(0u, path);
+    glDeletePathsCHROMIUM(path, 1);
+    ASSERT_GL_NO_ERROR();
+
+    GLuint first_path = glGenPathsCHROMIUM(5);
+    EXPECT_NE(0u, first_path);
+    glDeletePathsCHROMIUM(first_path, 5);
+    ASSERT_GL_NO_ERROR();
+
+    // Test deleting paths that are not actually allocated:
+    // "unused names in /paths/ are silently ignored".
+    first_path = glGenPathsCHROMIUM(5);
+    EXPECT_NE(0u, first_path);
+    glDeletePathsCHROMIUM(first_path, 6);
+    ASSERT_GL_NO_ERROR();
+
+    GLsizei big_range = 0xffff;
+    first_path = glGenPathsCHROMIUM(big_range);
+    EXPECT_NE(0u, first_path);
+    glDeletePathsCHROMIUM(first_path, big_range);
+    ASSERT_GL_NO_ERROR();
+
+    // Test glIsPathCHROMIUM(). A path object is not considered a path untill
+    // it has actually been specified with a path data.
+
+    path = glGenPathsCHROMIUM(1);
+    EXPECT_TRUE(glIsPathCHROMIUM(path) == GL_FALSE);
+
+    // specify the data.
+    GLubyte commands[] = {GL_MOVE_TO_CHROMIUM, GL_CLOSE_PATH_CHROMIUM};
+    GLfloat coords[] = {50.0f, 50.0f};
+    glPathCommandsCHROMIUM(path, 2, commands, 2, GL_FLOAT, coords);
+    EXPECT_TRUE(glIsPathCHROMIUM(path) == GL_TRUE);
+    glDeletePathsCHROMIUM(path, 1);
+    EXPECT_TRUE(glIsPathCHROMIUM(path) == GL_FALSE);
+}
+
+// Test incorrect path creation and deletion and expect GL errors.
+TEST_P(CHROMIUMPathRenderingTest, TestGenDeleteErrors)
+{
+    if (!isApplicable())
+        return;
+
+    // GenPaths / DeletePaths tests.
+    // std::numeric_limits<GLuint>::max() is wrong for GLsizei.
+    GLuint first_path = glGenPathsCHROMIUM(std::numeric_limits<GLuint>::max());
+    EXPECT_EQ(first_path, 0u);
+    EXPECT_GL_ERROR(GL_INVALID_VALUE);
+
+    first_path = glGenPathsCHROMIUM(-1);
+    EXPECT_EQ(0u, first_path);
+    EXPECT_GL_ERROR(GL_INVALID_VALUE);
+
+    glDeletePathsCHROMIUM(1, -5);
+    EXPECT_GL_ERROR(GL_INVALID_VALUE);
+
+    first_path = glGenPathsCHROMIUM(-1);
+    EXPECT_EQ(0u, first_path);
+    EXPECT_GL_ERROR(GL_INVALID_VALUE);
+}
+
+// Test setting and getting path parameters.
+TEST_P(CHROMIUMPathRenderingTest, TestPathParameter)
+{
+    if (!isApplicable())
+        return;
+
+    GLuint path = glGenPathsCHROMIUM(1);
+
+    // specify the data.
+    GLubyte commands[] = {GL_MOVE_TO_CHROMIUM, GL_CLOSE_PATH_CHROMIUM};
+    GLfloat coords[] = {50.0f, 50.0f};
+    glPathCommandsCHROMIUM(path, 2, commands, 2, GL_FLOAT, coords);
+    ASSERT_GL_NO_ERROR();
+    EXPECT_TRUE(glIsPathCHROMIUM(path) == GL_TRUE);
+
+    static const GLenum kEndCaps[] = {GL_FLAT_CHROMIUM, GL_SQUARE_CHROMIUM, GL_ROUND_CHROMIUM};
+    for (std::size_t i = 0; i < 3; ++i)
+    {
+        GLint x;
+        glPathParameteriCHROMIUM(path, GL_PATH_END_CAPS_CHROMIUM, static_cast<GLenum>(kEndCaps[i]));
+        ASSERT_GL_NO_ERROR();
+        glGetPathParameterivCHROMIUM(path, GL_PATH_END_CAPS_CHROMIUM, &x);
+        ASSERT_GL_NO_ERROR();
+        EXPECT_EQ(kEndCaps[i], static_cast<GLenum>(x));
+
+        GLfloat f;
+        glPathParameterfCHROMIUM(path, GL_PATH_END_CAPS_CHROMIUM,
+                                 static_cast<GLenum>(kEndCaps[(i + 1) % 3]));
+        glGetPathParameterfvCHROMIUM(path, GL_PATH_END_CAPS_CHROMIUM, &f);
+        ASSERT_GL_NO_ERROR();
+        EXPECT_EQ(kEndCaps[(i + 1) % 3], static_cast<GLenum>(f));
+    }
+
+    static const GLenum kJoinStyles[] = {GL_MITER_REVERT_CHROMIUM, GL_BEVEL_CHROMIUM,
+                                         GL_ROUND_CHROMIUM};
+    for (std::size_t i = 0; i < 3; ++i)
+    {
+        GLint x;
+        glPathParameteriCHROMIUM(path, GL_PATH_JOIN_STYLE_CHROMIUM,
+                                 static_cast<GLenum>(kJoinStyles[i]));
+        ASSERT_GL_NO_ERROR();
+        glGetPathParameterivCHROMIUM(path, GL_PATH_JOIN_STYLE_CHROMIUM, &x);
+        ASSERT_GL_NO_ERROR();
+        EXPECT_EQ(kJoinStyles[i], static_cast<GLenum>(x));
+
+        GLfloat f;
+        glPathParameterfCHROMIUM(path, GL_PATH_JOIN_STYLE_CHROMIUM,
+                                 static_cast<GLenum>(kJoinStyles[(i + 1) % 3]));
+        ASSERT_GL_NO_ERROR();
+        glGetPathParameterfvCHROMIUM(path, GL_PATH_JOIN_STYLE_CHROMIUM, &f);
+        ASSERT_GL_NO_ERROR();
+        EXPECT_EQ(kJoinStyles[(i + 1) % 3], static_cast<GLenum>(f));
+    }
+
+    {
+        glPathParameterfCHROMIUM(path, GL_PATH_STROKE_WIDTH_CHROMIUM, 5.0f);
+        ASSERT_GL_NO_ERROR();
+
+        GLfloat f;
+        glGetPathParameterfvCHROMIUM(path, GL_PATH_STROKE_WIDTH_CHROMIUM, &f);
+        EXPECT_EQ(5.0f, f);
+    }
+
+    glDeletePathsCHROMIUM(path, 1);
+}
+
+// Test that setting incorrect path parameter generates GL error.
+TEST_P(CHROMIUMPathRenderingTest, TestPathParameterErrors)
+{
+    if (!isApplicable())
+        return;
+
+    GLuint path = glGenPathsCHROMIUM(1);
+
+    // PathParameter*: Wrong value for the pname should fail.
+    glPathParameteriCHROMIUM(path, GL_PATH_JOIN_STYLE_CHROMIUM, GL_FLAT_CHROMIUM);
+    EXPECT_GL_ERROR(GL_INVALID_ENUM);
+
+    glPathParameterfCHROMIUM(path, GL_PATH_END_CAPS_CHROMIUM, GL_MITER_REVERT_CHROMIUM);
+    EXPECT_GL_ERROR(GL_INVALID_ENUM);
+
+    // PathParameter*: Wrong floating-point value should fail.
+    glPathParameterfCHROMIUM(path, GL_PATH_STROKE_WIDTH_CHROMIUM, -0.1f);
+    EXPECT_GL_ERROR(GL_INVALID_VALUE);
+
+    // PathParameter*: Wrong pname should fail.
+    glPathParameteriCHROMIUM(path, GL_PATH_STROKE_WIDTH_CHROMIUM - 1, 5);
+    EXPECT_GL_ERROR(GL_INVALID_ENUM);
+
+    glDeletePathsCHROMIUM(path, 1);
+}
+
+// Test expected path object state.
+TEST_P(CHROMIUMPathRenderingTest, TestPathObjectState)
+{
+    if (!isApplicable())
+        return;
+
+    glViewport(0, 0, kResolution, kResolution);
+    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+    glStencilMask(0xffffffff);
+    glClearStencil(0);
+    glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+    glPathStencilFuncCHROMIUM(GL_ALWAYS, 0, 0xFF);
+    glStencilOp(GL_KEEP, GL_KEEP, GL_ZERO);
+    ASSERT_GL_NO_ERROR();
+
+    // Test that trying to draw non-existing paths does not produce errors or results.
+    GLuint non_existing_paths[] = {0, 55, 74744};
+    for (auto &p : non_existing_paths)
+    {
+        EXPECT_TRUE(glIsPathCHROMIUM(p) == GL_FALSE);
+        ASSERT_GL_NO_ERROR();
+        tryAllDrawFunctions(p, GL_NO_ERROR);
+    }
+
+    // Path name marked as used but without path object state causes
+    // a GL error upon any draw command.
+    GLuint path = glGenPathsCHROMIUM(1);
+    EXPECT_TRUE(glIsPathCHROMIUM(path) == GL_FALSE);
+    tryAllDrawFunctions(path, GL_INVALID_OPERATION);
+    glDeletePathsCHROMIUM(path, 1);
+
+    // Document a bit of an inconsistency: path name marked as used but without
+    // path object state causes a GL error upon any draw command (tested above).
+    // Path name that had path object state, but then was "cleared", still has a
+    // path object state, even though the state is empty.
+    path = glGenPathsCHROMIUM(1);
+    EXPECT_TRUE(glIsPathCHROMIUM(path) == GL_FALSE);
+
+    GLubyte commands[] = {GL_MOVE_TO_CHROMIUM, GL_CLOSE_PATH_CHROMIUM};
+    GLfloat coords[] = {50.0f, 50.0f};
+    glPathCommandsCHROMIUM(path, 2, commands, 2, GL_FLOAT, coords);
+    EXPECT_TRUE(glIsPathCHROMIUM(path) == GL_TRUE);
+
+    glPathCommandsCHROMIUM(path, 0, NULL, 0, GL_FLOAT, NULL);
+    EXPECT_TRUE(glIsPathCHROMIUM(path) == GL_TRUE);  // The surprise.
+
+    tryAllDrawFunctions(path, GL_NO_ERROR);
+    glDeletePathsCHROMIUM(path, 1);
+
+    // Make sure nothing got drawn by the drawing commands that should not produce
+    // anything.
+    const angle::GLColor black = {0, 0, 0, 0};
+    EXPECT_TRUE(CheckPixels(0, 0, kResolution, kResolution, 0, black));
+}
+
+// Test that trying to use path object that doesn't exist generates
+// a GL error.
+TEST_P(CHROMIUMPathRenderingTest, TestUnnamedPathsErrors)
+{
+    if (!isApplicable())
+        return;
+
+    // Unnamed paths: Trying to create a path object with non-existing path name
+    // produces error.  (Not a error in real NV_path_rendering).
+    ASSERT_GL_NO_ERROR();
+    GLubyte commands[] = {GL_MOVE_TO_CHROMIUM, GL_CLOSE_PATH_CHROMIUM};
+    GLfloat coords[] = {50.0f, 50.0f};
+    glPathCommandsCHROMIUM(555, 2, commands, 2, GL_FLOAT, coords);
+    EXPECT_GL_ERROR(GL_INVALID_OPERATION);
+
+    // PathParameter*: Using non-existing path object produces error.
+    ASSERT_GL_NO_ERROR();
+    glPathParameterfCHROMIUM(555, GL_PATH_STROKE_WIDTH_CHROMIUM, 5.0f);
+    EXPECT_GL_ERROR(GL_INVALID_OPERATION);
+
+    ASSERT_GL_NO_ERROR();
+    glPathParameteriCHROMIUM(555, GL_PATH_JOIN_STYLE_CHROMIUM, GL_ROUND_CHROMIUM);
+    EXPECT_GL_ERROR(GL_INVALID_OPERATION);
+}
+
+// Test that setting incorrect path data generates a GL error.
+TEST_P(CHROMIUMPathRenderingTest, TestPathCommandsErrors)
+{
+    if (!isApplicable())
+        return;
+
+    static const GLenum kInvalidCoordType = GL_NONE;
+
+    GLuint path        = glGenPathsCHROMIUM(1);
+    GLubyte commands[] = {GL_MOVE_TO_CHROMIUM, GL_CLOSE_PATH_CHROMIUM};
+    GLfloat coords[]   = {50.0f, 50.0f};
+
+    glPathCommandsCHROMIUM(path, 2, commands, -4, GL_FLOAT, coords);
+    EXPECT_GL_ERROR(GL_INVALID_VALUE);
+
+    glPathCommandsCHROMIUM(path, -1, commands, 2, GL_FLOAT, coords);
+    EXPECT_GL_ERROR(GL_INVALID_VALUE);
+
+    glPathCommandsCHROMIUM(path, 2, commands, 2, kInvalidCoordType, coords);
+    EXPECT_GL_ERROR(GL_INVALID_ENUM);
+
+    // incorrect number of coordinates
+    glPathCommandsCHROMIUM(path, 2, commands, std::numeric_limits<GLsizei>::max(), GL_FLOAT,
+                           coords);
+    EXPECT_GL_ERROR(GL_INVALID_VALUE);
+
+    // This should fail due to cmd count + coord count * short size.
+    glPathCommandsCHROMIUM(path, 2, commands, std::numeric_limits<GLsizei>::max(), GL_SHORT,
+                           coords);
+    EXPECT_GL_ERROR(GL_INVALID_OPERATION);
+
+    glDeletePathsCHROMIUM(path, 1);
+}
+
+// Test that trying to render a path with invalid arguments
+// generates a GL error.
+TEST_P(CHROMIUMPathRenderingTest, TestPathRenderingInvalidArgs)
+{
+    if (!isApplicable())
+        return;
+
+    GLuint path = glGenPathsCHROMIUM(1);
+    glPathCommandsCHROMIUM(path, 0, NULL, 0, GL_FLOAT, NULL);
+
+    // Verify that normal calls work.
+    glStencilFillPathCHROMIUM(path, GL_COUNT_UP_CHROMIUM, 0x7F);
+    ASSERT_GL_NO_ERROR();
+    glStencilThenCoverFillPathCHROMIUM(path, GL_COUNT_UP_CHROMIUM, 0x7F, GL_BOUNDING_BOX_CHROMIUM);
+    ASSERT_GL_NO_ERROR();
+
+    // Using invalid fill mode causes INVALID_ENUM.
+    glStencilFillPathCHROMIUM(path, GL_COUNT_UP_CHROMIUM - 1, 0x7F);
+    EXPECT_GL_ERROR(GL_INVALID_ENUM);
+    glStencilThenCoverFillPathCHROMIUM(path, GL_COUNT_UP_CHROMIUM - 1, 0x7F,
+                                       GL_BOUNDING_BOX_CHROMIUM);
+    EXPECT_GL_ERROR(GL_INVALID_ENUM);
+
+    // Using invalid cover mode causes INVALID_ENUM.
+    glCoverFillPathCHROMIUM(path, GL_CONVEX_HULL_CHROMIUM - 1);
+    EXPECT_EQ(static_cast<GLenum>(GL_INVALID_ENUM), glGetError());
+    glStencilThenCoverFillPathCHROMIUM(path, GL_COUNT_UP_CHROMIUM, 0x7F,
+                                       GL_BOUNDING_BOX_CHROMIUM + 1);
+    EXPECT_GL_ERROR(GL_INVALID_ENUM);
+
+    // For instanced variants, we need this to error the same way
+    // regardless of whether # of paths == 0 would cause an early return.
+
+    // TODO: enable this once instanced path rendering is implemented.
+    // for (int path_count = 0; path_count <= 1; ++path_count)
+    // {
+    //     glStencilFillPathInstancedCHROMIUM(path_count, GL_UNSIGNED_INT, &path, 0,
+    //         GL_COUNT_UP_CHROMIUM - 1, 0x7F, GL_NONE, NULL);
+    //     EXPECT_EQ(static_cast<GLenum>(GL_INVALID_ENUM), glGetError());
+    //     glStencilThenCoverFillPathInstancedCHROMIUM(
+    //         path_count, GL_UNSIGNED_INT, &path, 0, GL_COUNT_UP_CHROMIUM - 1, 0x7F,
+    //         GL_BOUNDING_BOX_OF_BOUNDING_BOXES_CHROMIUM, GL_NONE, NULL);
+    //     EXPECT_EQ(static_cast<GLenum>(GL_INVALID_ENUM), glGetError());
+    // }
+
+    // Using mask+1 not being power of two causes INVALID_VALUE with up/down fill mode
+    glStencilFillPathCHROMIUM(path, GL_COUNT_UP_CHROMIUM, 0x40);
+    EXPECT_GL_ERROR(GL_INVALID_VALUE);
+    glStencilThenCoverFillPathCHROMIUM(path, GL_COUNT_DOWN_CHROMIUM, 12, GL_BOUNDING_BOX_CHROMIUM);
+    EXPECT_GL_ERROR(GL_INVALID_VALUE);
+
+    // TODO: enable this once instanced path rendering is implemented.
+    // for (int path_count = 0; path_count <= 1; ++path_count)
+    // {
+    //     glStencilFillPathInstancedCHROMIUM(path_count, GL_UNSIGNED_INT, &path, 0,
+    //         GL_COUNT_UP_CHROMIUM, 0x30, GL_NONE, NULL);
+    //     EXPECT_EQ(static_cast<GLenum>(GL_INVALID_VALUE), glGetError());
+    //     glStencilThenCoverFillPathInstancedCHROMIUM(
+    //         path_count, GL_UNSIGNED_INT, &path, 0, GL_COUNT_DOWN_CHROMIUM, 0xFE,
+    //         GL_BOUNDING_BOX_OF_BOUNDING_BOXES_CHROMIUM, GL_NONE, NULL);
+    //     EXPECT_EQ(static_cast<GLenum>(GL_INVALID_VALUE), glGetError());
+    // }
+
+    glDeletePathsCHROMIUM(path, 1);
+}
+
+const GLfloat kProjectionMatrix[16] = {2.0f / kResolution,
+                                       0.0f,
+                                       0.0f,
+                                       0.0f,
+                                       0.0f,
+                                       2.0f / kResolution,
+                                       0.0f,
+                                       0.0f,
+                                       0.0f,
+                                       0.0f,
+                                       -1.0f,
+                                       0.0f,
+                                       -1.0f,
+                                       -1.0f,
+                                       0.0f,
+                                       1.0f};
+
+class CHROMIUMPathRenderingDrawTest : public ANGLETest
+{
+  protected:
+    CHROMIUMPathRenderingDrawTest()
+    {
+        setWindowWidth(kResolution);
+        setWindowHeight(kResolution);
+        setConfigRedBits(8);
+        setConfigGreenBits(8);
+        setConfigBlueBits(8);
+        setConfigAlphaBits(8);
+        setConfigDepthBits(8);
+        setConfigStencilBits(8);
+    }
+
+    bool isApplicable() const { return extensionEnabled("GL_CHROMIUM_path_rendering"); }
+
+    void setupStateForTestPattern()
+    {
+        glViewport(0, 0, kResolution, kResolution);
+        glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+        glStencilMask(0xffffffff);
+        glClearStencil(0);
+        glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+        glEnable(GL_STENCIL_TEST);
+
+        static const char *kVertexShaderSource =
+            "void main() {\n"
+            "  gl_Position = vec4(1.0); \n"
+            "}";
+
+        static const char *kFragmentShaderSource =
+            "precision mediump float;\n"
+            "uniform vec4 color;\n"
+            "void main() {\n"
+            "  gl_FragColor = color;\n"
+            "}";
+
+        GLuint program = CompileProgram(kVertexShaderSource, kFragmentShaderSource);
+        glUseProgram(program);
+        mColorLoc = glGetUniformLocation(program, "color");
+        glDeleteProgram(program);
+
+        // Set up orthogonal projection with near/far plane distance of 2.
+        glMatrixLoadfCHROMIUM(GL_PATH_PROJECTION_CHROMIUM, kProjectionMatrix);
+        glMatrixLoadIdentityCHROMIUM(GL_PATH_MODELVIEW_CHROMIUM);
+
+        ASSERT_GL_NO_ERROR();
+    }
+
+    void setupPathStateForTestPattern(GLuint path)
+    {
+        static const GLubyte kCommands[] = {GL_MOVE_TO_CHROMIUM, GL_LINE_TO_CHROMIUM,
+                                            GL_QUADRATIC_CURVE_TO_CHROMIUM,
+                                            GL_CUBIC_CURVE_TO_CHROMIUM, GL_CLOSE_PATH_CHROMIUM};
+
+        static const GLfloat kCoords[] = {50.0f, 50.0f, 75.0f, 75.0f, 100.0f, 62.5f, 50.0f,
+                                          25.5f, 0.0f,  62.5f, 50.0f, 50.0f,  25.0f, 75.0f};
+
+        glPathCommandsCHROMIUM(path, 5, kCommands, 14, GL_FLOAT, kCoords);
+        glPathParameterfCHROMIUM(path, GL_PATH_STROKE_WIDTH_CHROMIUM, 5.0f);
+        glPathParameterfCHROMIUM(path, GL_PATH_MITER_LIMIT_CHROMIUM, 1.0f);
+        glPathParameterfCHROMIUM(path, GL_PATH_STROKE_BOUND_CHROMIUM, .02f);
+        glPathParameteriCHROMIUM(path, GL_PATH_JOIN_STYLE_CHROMIUM, GL_ROUND_CHROMIUM);
+        glPathParameteriCHROMIUM(path, GL_PATH_END_CAPS_CHROMIUM, GL_SQUARE_CHROMIUM);
+        ASSERT_GL_NO_ERROR();
+    }
+
+    void verifyTestPatternFill(float x, float y)
+    {
+        static const float kFillCoords[]  = {55.0f, 54.0f, 50.0f, 28.0f, 66.0f, 63.0f};
+        static const angle::GLColor kBlue = {0, 0, 255, 255};
+
+        for (size_t i = 0; i < 6; i += 2)
+        {
+            float fx = kFillCoords[i];
+            float fy = kFillCoords[i + 1];
+            EXPECT_TRUE(CheckPixels(x + fx, y + fy, 1, 1, 0, kBlue));
+        }
+    }
+    void verifyTestPatternBg(float x, float y)
+    {
+        static const float kBackgroundCoords[]     = {80.0f, 80.0f, 20.0f, 20.0f, 90.0f, 1.0f};
+        static const angle::GLColor kExpectedColor = {0, 0, 0, 0};
+
+        for (size_t i = 0; i < 6; i += 2)
+        {
+            float bx = kBackgroundCoords[i];
+            float by = kBackgroundCoords[i + 1];
+            EXPECT_TRUE(CheckPixels(x + bx, y + by, 1, 1, 0, kExpectedColor));
+        }
+    }
+
+    void verifyTestPatternStroke(float x, float y)
+    {
+        // Inside the stroke we should have green.
+        static const angle::GLColor kGreen = {0, 255, 0, 255};
+        EXPECT_TRUE(CheckPixels(x + 50, y + 53, 1, 1, 0, kGreen));
+        EXPECT_TRUE(CheckPixels(x + 26, y + 76, 1, 1, 0, kGreen));
+
+        // Outside the path we should have black.
+        static const angle::GLColor black = {0, 0, 0, 0};
+        EXPECT_TRUE(CheckPixels(x + 10, y + 10, 1, 1, 0, black));
+        EXPECT_TRUE(CheckPixels(x + 80, y + 80, 1, 1, 0, black));
+    }
+
+    GLuint mColorLoc;
+};
+
+// Tests that basic path rendering functions work.
+TEST_P(CHROMIUMPathRenderingDrawTest, TestPathRendering)
+{
+    if (!isApplicable())
+        return;
+
+    static const float kBlue[]  = {0.0f, 0.0f, 1.0f, 1.0f};
+    static const float kGreen[] = {0.0f, 1.0f, 0.0f, 1.0f};
+
+    setupStateForTestPattern();
+
+    GLuint path = glGenPathsCHROMIUM(1);
+    setupPathStateForTestPattern(path);
+
+    // Do the stencil fill, cover fill, stencil stroke, cover stroke
+    // in unconventional order:
+    // 1) stencil the stroke in stencil high bit
+    // 2) stencil the fill in low bits
+    // 3) cover the fill
+    // 4) cover the stroke
+    // This is done to check that glPathStencilFunc works, eg the mask
+    // goes through. Stencil func is not tested ATM, for simplicity.
+
+    glPathStencilFuncCHROMIUM(GL_ALWAYS, 0, 0xFF);
+    glStencilStrokePathCHROMIUM(path, 0x80, 0x80);
+
+    glPathStencilFuncCHROMIUM(GL_ALWAYS, 0, 0x7F);
+    glStencilFillPathCHROMIUM(path, GL_COUNT_UP_CHROMIUM, 0x7F);
+
+    glStencilFunc(GL_LESS, 0, 0x7F);
+    glStencilOp(GL_KEEP, GL_KEEP, GL_ZERO);
+    glUniform4fv(mColorLoc, 1, kBlue);
+    glCoverFillPathCHROMIUM(path, GL_BOUNDING_BOX_CHROMIUM);
+
+    glStencilFunc(GL_EQUAL, 0x80, 0x80);
+    glStencilOp(GL_KEEP, GL_KEEP, GL_ZERO);
+    glUniform4fv(mColorLoc, 1, kGreen);
+    glCoverStrokePathCHROMIUM(path, GL_CONVEX_HULL_CHROMIUM);
+
+    glDeletePathsCHROMIUM(path, 1);
+
+    ASSERT_GL_NO_ERROR();
+
+    // Verify the image.
+    verifyTestPatternFill(0.0f, 0.0f);
+    verifyTestPatternBg(0.0f, 0.0f);
+    verifyTestPatternStroke(0.0f, 0.0f);
+}
+
+// Test that StencilThen{Stroke,Fill} path rendering functions work
+TEST_P(CHROMIUMPathRenderingDrawTest, TestPathRenderingThenFunctions)
+{
+    if (!isApplicable())
+        return;
+
+    static float kBlue[]  = {0.0f, 0.0f, 1.0f, 1.0f};
+    static float kGreen[] = {0.0f, 1.0f, 0.0f, 1.0f};
+
+    setupStateForTestPattern();
+
+    GLuint path = glGenPathsCHROMIUM(1);
+    setupPathStateForTestPattern(path);
+
+    glPathStencilFuncCHROMIUM(GL_ALWAYS, 0, 0xFF);
+    glStencilFunc(GL_EQUAL, 0x80, 0x80);
+    glStencilOp(GL_KEEP, GL_KEEP, GL_ZERO);
+    glUniform4fv(mColorLoc, 1, kGreen);
+    glStencilThenCoverStrokePathCHROMIUM(path, 0x80, 0x80, GL_BOUNDING_BOX_CHROMIUM);
+
+    glPathStencilFuncCHROMIUM(GL_ALWAYS, 0, 0x7F);
+    glStencilFunc(GL_LESS, 0, 0x7F);
+    glStencilOp(GL_KEEP, GL_KEEP, GL_ZERO);
+    glUniform4fv(mColorLoc, 1, kBlue);
+    glStencilThenCoverFillPathCHROMIUM(path, GL_COUNT_UP_CHROMIUM, 0x7F, GL_CONVEX_HULL_CHROMIUM);
+
+    glDeletePathsCHROMIUM(path, 1);
+
+    // Verify the image.
+    verifyTestPatternFill(0.0f, 0.0f);
+    verifyTestPatternBg(0.0f, 0.0f);
+    verifyTestPatternStroke(0.0f, 0.0f);
+}
+
+}  // namespace
+
+ANGLE_INSTANTIATE_TEST(CHROMIUMPathRenderingTest,
+                       ES2_OPENGL(),
+                       ES2_OPENGLES(),
+                       ES3_OPENGL(),
+                       ES3_OPENGLES());
+ANGLE_INSTANTIATE_TEST(CHROMIUMPathRenderingDrawTest,
+                       ES2_OPENGL(),
+                       ES2_OPENGLES(),
+                       ES3_OPENGL(),
+                       ES3_OPENGLES());