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());