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/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