summaryrefslogtreecommitdiffstats
path: root/gpu/command_buffer/service
diff options
context:
space:
mode:
Diffstat (limited to 'gpu/command_buffer/service')
-rw-r--r--gpu/command_buffer/service/BUILD.gn2
-rw-r--r--gpu/command_buffer/service/context_group.cc8
-rw-r--r--gpu/command_buffer/service/context_group.h5
-rw-r--r--gpu/command_buffer/service/context_state_autogen.h3
-rw-r--r--gpu/command_buffer/service/context_state_impl_autogen.h48
-rw-r--r--gpu/command_buffer/service/feature_info.cc23
-rw-r--r--gpu/command_buffer/service/feature_info.h3
-rw-r--r--gpu/command_buffer/service/feature_info_unittest.cc43
-rw-r--r--gpu/command_buffer/service/gles2_cmd_decoder.cc497
-rw-r--r--gpu/command_buffer/service/gles2_cmd_decoder_autogen.h52
-rw-r--r--gpu/command_buffer/service/gles2_cmd_decoder_unittest_0_autogen.h5
-rw-r--r--gpu/command_buffer/service/gles2_cmd_decoder_unittest_4_autogen.h15
-rw-r--r--gpu/command_buffer/service/gles2_cmd_decoder_unittest_extensions.cc941
-rw-r--r--gpu/command_buffer/service/gles2_cmd_decoder_unittest_extensions_autogen.h49
-rw-r--r--gpu/command_buffer/service/gles2_cmd_validation_autogen.h6
-rw-r--r--gpu/command_buffer/service/gles2_cmd_validation_implementation_autogen.h53
-rw-r--r--gpu/command_buffer/service/gpu_switches.cc42
-rw-r--r--gpu/command_buffer/service/gpu_switches.h1
-rw-r--r--gpu/command_buffer/service/path_manager.cc252
-rw-r--r--gpu/command_buffer/service/path_manager.h68
-rw-r--r--gpu/command_buffer/service/path_manager_unittest.cc164
21 files changed, 2246 insertions, 34 deletions
diff --git a/gpu/command_buffer/service/BUILD.gn b/gpu/command_buffer/service/BUILD.gn
index 14f2402..62fce76 100644
--- a/gpu/command_buffer/service/BUILD.gn
+++ b/gpu/command_buffer/service/BUILD.gn
@@ -100,6 +100,8 @@ source_set("service_sources") {
"mailbox_manager_sync.h",
"memory_program_cache.cc",
"memory_program_cache.h",
+ "path_manager.cc",
+ "path_manager.h",
"program_cache.cc",
"program_cache.h",
"program_manager.cc",
diff --git a/gpu/command_buffer/service/context_group.cc b/gpu/command_buffer/service/context_group.cc
index 4a82c56..1cc8f24 100644
--- a/gpu/command_buffer/service/context_group.cc
+++ b/gpu/command_buffer/service/context_group.cc
@@ -16,6 +16,7 @@
#include "gpu/command_buffer/service/gpu_switches.h"
#include "gpu/command_buffer/service/mailbox_manager_impl.h"
#include "gpu/command_buffer/service/memory_tracking.h"
+#include "gpu/command_buffer/service/path_manager.h"
#include "gpu/command_buffer/service/program_manager.h"
#include "gpu/command_buffer/service/renderbuffer_manager.h"
#include "gpu/command_buffer/service/shader_manager.h"
@@ -294,6 +295,8 @@ bool ContextGroup::Initialize(
feature_info_->workarounds().max_vertex_uniform_vectors));
}
+ path_manager_.reset(new PathManager());
+
program_manager_.reset(new ProgramManager(
program_cache_, max_varying_vectors_));
@@ -365,6 +368,11 @@ void ContextGroup::Destroy(GLES2Decoder* decoder, bool have_context) {
texture_manager_.reset();
}
+ if (path_manager_ != NULL) {
+ path_manager_->Destroy(have_context);
+ path_manager_.reset();
+ }
+
if (program_manager_ != NULL) {
program_manager_->Destroy(have_context);
program_manager_.reset();
diff --git a/gpu/command_buffer/service/context_group.h b/gpu/command_buffer/service/context_group.h
index 466f918..32dbbf3 100644
--- a/gpu/command_buffer/service/context_group.h
+++ b/gpu/command_buffer/service/context_group.h
@@ -32,6 +32,7 @@ class GLES2Decoder;
class FramebufferManager;
class MailboxManager;
class RenderbufferManager;
+class PathManager;
class ProgramManager;
class ShaderManager;
class TextureManager;
@@ -153,6 +154,8 @@ class GPU_EXPORT ContextGroup : public base::RefCounted<ContextGroup> {
return texture_manager_.get();
}
+ PathManager* path_manager() const { return path_manager_.get(); }
+
ProgramManager* program_manager() const {
return program_manager_.get();
}
@@ -291,6 +294,8 @@ class GPU_EXPORT ContextGroup : public base::RefCounted<ContextGroup> {
scoped_ptr<TextureManager> texture_manager_;
+ scoped_ptr<PathManager> path_manager_;
+
scoped_ptr<ProgramManager> program_manager_;
scoped_ptr<ShaderManager> shader_manager_;
diff --git a/gpu/command_buffer/service/context_state_autogen.h b/gpu/command_buffer/service/context_state_autogen.h
index e2b65bf..7ca2faa 100644
--- a/gpu/command_buffer/service/context_state_autogen.h
+++ b/gpu/command_buffer/service/context_state_autogen.h
@@ -74,6 +74,9 @@ GLenum hint_fragment_shader_derivative;
GLfloat line_width;
GLfloat modelview_matrix[16];
GLfloat projection_matrix[16];
+GLenum stencil_path_func;
+GLint stencil_path_ref;
+GLuint stencil_path_mask;
GLint pack_alignment;
GLint unpack_alignment;
GLfloat polygon_offset_factor;
diff --git a/gpu/command_buffer/service/context_state_impl_autogen.h b/gpu/command_buffer/service/context_state_impl_autogen.h
index fa5462f..2dde530 100644
--- a/gpu/command_buffer/service/context_state_impl_autogen.h
+++ b/gpu/command_buffer/service/context_state_impl_autogen.h
@@ -104,6 +104,9 @@ void ContextState::Initialize() {
projection_matrix[13] = 0.0f;
projection_matrix[14] = 0.0f;
projection_matrix[15] = 1.0f;
+ stencil_path_func = GL_ALWAYS;
+ stencil_path_ref = 0;
+ stencil_path_mask = 0xFFFFFFFFU;
pack_alignment = 4;
unpack_alignment = 4;
polygon_offset_factor = 0.0f;
@@ -276,6 +279,12 @@ void ContextState::InitState(const ContextState* prev_state) const {
glMatrixLoadfEXT(GL_PATH_PROJECTION_CHROMIUM, projection_matrix);
}
}
+ if (feature_info_->feature_flags().chromium_path_rendering)
+ if ((stencil_path_func != prev_state->stencil_path_func) ||
+ (stencil_path_ref != prev_state->stencil_path_ref) ||
+ (stencil_path_mask != prev_state->stencil_path_mask))
+ glPathStencilFuncNV(stencil_path_func, stencil_path_ref,
+ stencil_path_mask);
if (prev_state->pack_alignment != pack_alignment) {
glPixelStorei(GL_PACK_ALIGNMENT, pack_alignment);
}
@@ -355,6 +364,9 @@ void ContextState::InitState(const ContextState* prev_state) const {
if (feature_info_->feature_flags().chromium_path_rendering) {
glMatrixLoadfEXT(GL_PATH_PROJECTION_CHROMIUM, projection_matrix);
}
+ if (feature_info_->feature_flags().chromium_path_rendering)
+ glPathStencilFuncNV(stencil_path_func, stencil_path_ref,
+ stencil_path_mask);
glPixelStorei(GL_PACK_ALIGNMENT, pack_alignment);
glPixelStorei(GL_UNPACK_ALIGNMENT, unpack_alignment);
glPolygonOffset(polygon_offset_factor, polygon_offset_units);
@@ -547,6 +559,24 @@ bool ContextState::GetStateAsGLint(GLenum pname,
}
}
return true;
+ case GL_PATH_STENCIL_FUNC_CHROMIUM:
+ *num_written = 1;
+ if (params) {
+ params[0] = static_cast<GLint>(stencil_path_func);
+ }
+ return true;
+ case GL_PATH_STENCIL_REF_CHROMIUM:
+ *num_written = 1;
+ if (params) {
+ params[0] = static_cast<GLint>(stencil_path_ref);
+ }
+ return true;
+ case GL_PATH_STENCIL_VALUE_MASK_CHROMIUM:
+ *num_written = 1;
+ if (params) {
+ params[0] = static_cast<GLint>(stencil_path_mask);
+ }
+ return true;
case GL_PACK_ALIGNMENT:
*num_written = 1;
if (params) {
@@ -897,6 +927,24 @@ bool ContextState::GetStateAsGLfloat(GLenum pname,
memcpy(params, projection_matrix, sizeof(GLfloat) * 16);
}
return true;
+ case GL_PATH_STENCIL_FUNC_CHROMIUM:
+ *num_written = 1;
+ if (params) {
+ params[0] = static_cast<GLfloat>(stencil_path_func);
+ }
+ return true;
+ case GL_PATH_STENCIL_REF_CHROMIUM:
+ *num_written = 1;
+ if (params) {
+ params[0] = static_cast<GLfloat>(stencil_path_ref);
+ }
+ return true;
+ case GL_PATH_STENCIL_VALUE_MASK_CHROMIUM:
+ *num_written = 1;
+ if (params) {
+ params[0] = static_cast<GLfloat>(stencil_path_mask);
+ }
+ return true;
case GL_PACK_ALIGNMENT:
*num_written = 1;
if (params) {
diff --git a/gpu/command_buffer/service/feature_info.cc b/gpu/command_buffer/service/feature_info.cc
index 0ce9980..8caf05c 100644
--- a/gpu/command_buffer/service/feature_info.cc
+++ b/gpu/command_buffer/service/feature_info.cc
@@ -209,6 +209,9 @@ void FeatureInfo::InitializeBasicState(const base::CommandLine& command_line) {
enable_unsafe_es3_apis_switch_ =
command_line.HasSwitch(switches::kEnableUnsafeES3APIs);
+ enable_gl_path_rendering_switch_ =
+ command_line.HasSwitch(switches::kEnableGLPathRendering);
+
unsafe_es3_apis_enabled_ = false;
}
@@ -1004,14 +1007,18 @@ void FeatureInfo::InitializeFeatures() {
}
}
- if (extensions.Contains("GL_NV_path_rendering")) {
- if (extensions.Contains("GL_EXT_direct_state_access") ||
- gl_version_info_->is_es3) {
- AddExtensionString("GL_CHROMIUM_path_rendering");
- feature_flags_.chromium_path_rendering = true;
- validators_.g_l_state.AddValue(GL_PATH_MODELVIEW_MATRIX_CHROMIUM);
- validators_.g_l_state.AddValue(GL_PATH_PROJECTION_MATRIX_CHROMIUM);
- }
+ if (enable_gl_path_rendering_switch_ &&
+ !workarounds_.disable_gl_path_rendering &&
+ extensions.Contains("GL_NV_path_rendering") &&
+ (extensions.Contains("GL_EXT_direct_state_access") ||
+ gl_version_info_->is_es3)) {
+ AddExtensionString("GL_CHROMIUM_path_rendering");
+ feature_flags_.chromium_path_rendering = true;
+ validators_.g_l_state.AddValue(GL_PATH_MODELVIEW_MATRIX_CHROMIUM);
+ validators_.g_l_state.AddValue(GL_PATH_PROJECTION_MATRIX_CHROMIUM);
+ validators_.g_l_state.AddValue(GL_PATH_STENCIL_FUNC_CHROMIUM);
+ validators_.g_l_state.AddValue(GL_PATH_STENCIL_REF_CHROMIUM);
+ validators_.g_l_state.AddValue(GL_PATH_STENCIL_VALUE_MASK_CHROMIUM);
}
if ((gl_version_info_->is_es3 || gl_version_info_->is_desktop_core_profile ||
diff --git a/gpu/command_buffer/service/feature_info.h b/gpu/command_buffer/service/feature_info.h
index 1ebff2a..aa9c0ba 100644
--- a/gpu/command_buffer/service/feature_info.h
+++ b/gpu/command_buffer/service/feature_info.h
@@ -164,6 +164,9 @@ class GPU_EXPORT FeatureInfo : public base::RefCounted<FeatureInfo> {
bool unsafe_es3_apis_enabled_;
+ // Whether the command line switch kEnableGLPathRendering is passed in.
+ bool enable_gl_path_rendering_switch_;
+
scoped_ptr<gfx::GLVersionInfo> gl_version_info_;
DISALLOW_COPY_AND_ASSIGN(FeatureInfo);
diff --git a/gpu/command_buffer/service/feature_info_unittest.cc b/gpu/command_buffer/service/feature_info_unittest.cc
index fdff175..8321e68 100644
--- a/gpu/command_buffer/service/feature_info_unittest.cc
+++ b/gpu/command_buffer/service/feature_info_unittest.cc
@@ -8,6 +8,7 @@
#include "base/memory/scoped_ptr.h"
#include "base/strings/string_number_conversions.h"
#include "gpu/command_buffer/service/gpu_service_test.h"
+#include "gpu/command_buffer/service/gpu_switches.h"
#include "gpu/command_buffer/service/test_helper.h"
#include "gpu/command_buffer/service/texture_manager.h"
#include "gpu/config/gpu_driver_bug_workaround_type.h"
@@ -77,6 +78,18 @@ class FeatureInfoTest
info_->Initialize();
}
+ void SetupInitExpectationsWithGLVersionAndCommandLine(
+ const char* extensions,
+ const char* renderer,
+ const char* version,
+ const base::CommandLine& command_line) {
+ GpuServiceTest::SetUpWithGLVersion(version, extensions);
+ TestHelper::SetupFeatureInfoInitExpectationsWithGLVersion(
+ gl_.get(), extensions, renderer, version);
+ info_ = new FeatureInfo(command_line);
+ info_->Initialize();
+ }
+
void SetupWithCommandLine(const base::CommandLine& command_line) {
GpuServiceTest::SetUp();
info_ = new FeatureInfo(command_line);
@@ -1191,31 +1204,49 @@ TEST_P(FeatureInfoTest, BlendEquationAdvancedDisabled) {
EXPECT_FALSE(info_->feature_flags().blend_equation_advanced_coherent);
}
-TEST_P(FeatureInfoTest, InitializeCHROMIUM_path_rendering) {
+TEST_P(FeatureInfoTest, InitializeCHROMIUM_path_rendering_no_cmdline_switch) {
SetupInitExpectationsWithGLVersion(
"GL_ARB_compatibility GL_NV_path_rendering GL_EXT_direct_state_access",
"", "4.3");
+ EXPECT_FALSE(info_->feature_flags().chromium_path_rendering);
+ EXPECT_THAT(info_->extensions(),
+ Not(HasSubstr("GL_CHROMIUM_path_rendering")));
+}
+
+TEST_P(FeatureInfoTest, InitializeCHROMIUM_path_rendering) {
+ base::CommandLine command_line(0, NULL);
+ command_line.AppendSwitch(switches::kEnableGLPathRendering);
+ SetupInitExpectationsWithGLVersionAndCommandLine(
+ "GL_ARB_compatibility GL_NV_path_rendering GL_EXT_direct_state_access",
+ "", "4.3", command_line);
EXPECT_TRUE(info_->feature_flags().chromium_path_rendering);
EXPECT_THAT(info_->extensions(), HasSubstr("GL_CHROMIUM_path_rendering"));
}
TEST_P(FeatureInfoTest, InitializeCHROMIUM_path_rendering2) {
- SetupInitExpectationsWithGLVersion(
- "GL_NV_path_rendering", "", "OpenGL ES 3.1");
+ base::CommandLine command_line(0, NULL);
+ command_line.AppendSwitch(switches::kEnableGLPathRendering);
+ SetupInitExpectationsWithGLVersionAndCommandLine(
+ "GL_NV_path_rendering", "", "OpenGL ES 3.1", command_line);
EXPECT_TRUE(info_->feature_flags().chromium_path_rendering);
EXPECT_THAT(info_->extensions(), HasSubstr("GL_CHROMIUM_path_rendering"));
}
TEST_P(FeatureInfoTest, InitializeNoCHROMIUM_path_rendering) {
- SetupInitExpectationsWithGLVersion("GL_ARB_compatibility", "", "4.3");
+ base::CommandLine command_line(0, NULL);
+ command_line.AppendSwitch(switches::kEnableGLPathRendering);
+ SetupInitExpectationsWithGLVersionAndCommandLine("GL_ARB_compatibility", "",
+ "4.3", command_line);
EXPECT_FALSE(info_->feature_flags().chromium_path_rendering);
EXPECT_THAT(info_->extensions(),
Not(HasSubstr("GL_CHROMIUM_path_rendering")));
}
TEST_P(FeatureInfoTest, InitializeNoCHROMIUM_path_rendering2) {
- SetupInitExpectationsWithGLVersion(
- "GL_ARB_compatibility GL_NV_path_rendering", "", "4.3");
+ base::CommandLine command_line(0, NULL);
+ command_line.AppendSwitch(switches::kEnableGLPathRendering);
+ SetupInitExpectationsWithGLVersionAndCommandLine(
+ "GL_ARB_compatibility GL_NV_path_rendering", "", "4.3", command_line);
EXPECT_FALSE(info_->feature_flags().chromium_path_rendering);
EXPECT_THAT(info_->extensions(),
Not(HasSubstr("GL_CHROMIUM_path_rendering")));
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc
index 4e35d55..3f70846 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc
@@ -47,6 +47,7 @@
#include "gpu/command_buffer/service/logger.h"
#include "gpu/command_buffer/service/mailbox_manager.h"
#include "gpu/command_buffer/service/memory_tracking.h"
+#include "gpu/command_buffer/service/path_manager.h"
#include "gpu/command_buffer/service/program_manager.h"
#include "gpu/command_buffer/service/query_manager.h"
#include "gpu/command_buffer/service/renderbuffer_manager.h"
@@ -791,6 +792,8 @@ class GLES2DecoderImpl : public GLES2Decoder,
void DeleteQueriesEXTHelper(GLsizei n, const GLuint* client_ids);
bool GenVertexArraysOESHelper(GLsizei n, const GLuint* client_ids);
void DeleteVertexArraysOESHelper(GLsizei n, const GLuint* client_ids);
+ bool GenPathsCHROMIUMHelper(GLuint first_client_id, GLsizei range);
+ bool DeletePathsCHROMIUMHelper(GLuint first_client_id, GLsizei range);
// Helper for async upload token completion notification callback.
base::Closure AsyncUploadTokenCompletionClosure(uint32 async_upload_token,
@@ -823,6 +826,8 @@ class GLES2DecoderImpl : public GLES2Decoder,
return group_->valuebuffer_manager();
}
+ PathManager* path_manager() { return group_->path_manager(); }
+
ProgramManager* program_manager() {
return group_->program_manager();
}
@@ -1563,6 +1568,7 @@ class GLES2DecoderImpl : public GLES2Decoder,
bool DoIsShader(GLuint client_id);
bool DoIsTexture(GLuint client_id);
bool DoIsVertexArrayOES(GLuint client_id);
+ bool DoIsPathCHROMIUM(GLuint client_id);
// Wrapper for glLinkProgram
void DoLinkProgram(GLuint program);
@@ -3350,6 +3356,42 @@ bool GLES2DecoderImpl::GenTexturesHelper(GLsizei n, const GLuint* client_ids) {
return true;
}
+bool GLES2DecoderImpl::GenPathsCHROMIUMHelper(GLuint first_client_id,
+ GLsizei range) {
+ GLuint last_client_id;
+ if (!SafeAddUint32(first_client_id, range - 1, &last_client_id))
+ return false;
+
+ if (path_manager()->HasPathsInRange(first_client_id, last_client_id))
+ return false;
+
+ GLuint first_service_id = glGenPathsNV(range);
+ if (first_service_id == 0) {
+ // We have to fail the connection here, because client has already
+ // succeeded in allocating the ids. This happens if we allocate
+ // the whole path id space (two allocations of 0x7FFFFFFF paths, for
+ // example).
+ return false;
+ }
+ // GenPathsNV does not wrap.
+ DCHECK(first_service_id + range - 1 >= first_service_id);
+
+ path_manager()->CreatePathRange(first_client_id, last_client_id,
+ first_service_id);
+
+ return true;
+}
+
+bool GLES2DecoderImpl::DeletePathsCHROMIUMHelper(GLuint first_client_id,
+ GLsizei range) {
+ GLuint last_client_id;
+ if (!SafeAddUint32(first_client_id, range - 1, &last_client_id))
+ return false;
+
+ path_manager()->RemovePaths(first_client_id, last_client_id);
+ return true;
+}
+
void GLES2DecoderImpl::DeleteBuffersHelper(
GLsizei n, const GLuint* client_ids) {
for (GLsizei ii = 0; ii < n; ++ii) {
@@ -11748,6 +11790,12 @@ bool GLES2DecoderImpl::DoIsVertexArrayOES(GLuint client_id) {
return vao && vao->IsValid() && !vao->IsDeleted();
}
+bool GLES2DecoderImpl::DoIsPathCHROMIUM(GLuint client_id) {
+ GLuint service_id = 0;
+ return path_manager()->GetPath(client_id, &service_id) &&
+ glIsPathNV(service_id) == GL_TRUE;
+}
+
#if defined(OS_MACOSX)
void GLES2DecoderImpl::ReleaseIOSurfaceForTexture(GLuint texture_id) {
TextureToIOSurfaceMap::iterator it = texture_to_io_surface_map_.find(
@@ -13833,6 +13881,455 @@ void GLES2DecoderImpl::OnOutOfMemoryError() {
}
}
+error::Error GLES2DecoderImpl::HandleGenPathsCHROMIUM(
+ uint32 immediate_data_size,
+ const void* cmd_data) {
+ static const char kFunctionName[] = "glGenPathsCHROMIUM";
+ const gles2::cmds::GenPathsCHROMIUM& c =
+ *static_cast<const gles2::cmds::GenPathsCHROMIUM*>(cmd_data);
+ if (!features().chromium_path_rendering) {
+ LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, kFunctionName,
+ "function not available");
+ return error::kNoError;
+ }
+
+ GLsizei range = static_cast<GLsizei>(c.range);
+ if (range < 0) {
+ LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, kFunctionName, "range < 0");
+ return error::kNoError;
+ }
+
+ GLuint first_client_id = static_cast<GLuint>(c.first_client_id);
+ if (first_client_id == 0)
+ return error::kInvalidArguments;
+
+ if (range == 0)
+ return error::kNoError;
+
+ if (!GenPathsCHROMIUMHelper(first_client_id, range))
+ return error::kInvalidArguments;
+
+ return error::kNoError;
+}
+error::Error GLES2DecoderImpl::HandleDeletePathsCHROMIUM(
+ uint32_t immediate_data_size,
+ const void* cmd_data) {
+ static const char kFunctionName[] = "glDeletePathsCHROMIUM";
+ const gles2::cmds::DeletePathsCHROMIUM& c =
+ *static_cast<const gles2::cmds::DeletePathsCHROMIUM*>(cmd_data);
+ if (!features().chromium_path_rendering) {
+ LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, kFunctionName,
+ "function not available");
+ return error::kNoError;
+ }
+
+ GLsizei range = static_cast<GLsizei>(c.range);
+ if (range < 0) {
+ LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, kFunctionName, "range < 0");
+ return error::kNoError;
+ }
+
+ if (range == 0)
+ return error::kNoError;
+
+ GLuint first_client_id = c.first_client_id;
+ // first_client_id can be 0, because non-existing path ids are skipped.
+
+ if (!DeletePathsCHROMIUMHelper(first_client_id, range))
+ return error::kInvalidArguments;
+
+ return error::kNoError;
+}
+
+error::Error GLES2DecoderImpl::HandlePathCommandsCHROMIUM(
+ uint32 immediate_data_size,
+ const void* cmd_data) {
+ static const char kFunctionName[] = "glPathCommandsCHROMIUM";
+ const gles2::cmds::PathCommandsCHROMIUM& c =
+ *static_cast<const gles2::cmds::PathCommandsCHROMIUM*>(cmd_data);
+ if (!features().chromium_path_rendering) {
+ LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, kFunctionName,
+ "function not available");
+ return error::kNoError;
+ }
+
+ GLuint service_id = 0;
+ if (!path_manager()->GetPath(static_cast<GLuint>(c.path), &service_id)) {
+ LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, kFunctionName,
+ "invalid path name");
+ return error::kNoError;
+ }
+
+ GLsizei num_commands = static_cast<GLsizei>(c.numCommands);
+ if (num_commands < 0) {
+ LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, kFunctionName, "numCommands < 0");
+ return error::kNoError;
+ }
+
+ GLsizei num_coords = static_cast<uint32>(c.numCoords);
+ if (num_coords < 0) {
+ LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, kFunctionName, "numCoords < 0");
+ return error::kNoError;
+ }
+
+ GLenum coord_type = static_cast<uint32>(c.coordType);
+ if (!validators_->path_coord_type.IsValid(static_cast<GLint>(coord_type))) {
+ LOCAL_SET_GL_ERROR(GL_INVALID_ENUM, kFunctionName, "invalid coordType");
+ return error::kNoError;
+ }
+
+ const GLubyte* commands = NULL;
+ base::CheckedNumeric<GLsizei> num_coords_expected = 0;
+
+ if (num_commands > 0) {
+ uint32 commands_shm_id = static_cast<uint32>(c.commands_shm_id);
+ uint32 commands_shm_offset = static_cast<uint32>(c.commands_shm_offset);
+ if (commands_shm_id != 0 || commands_shm_offset != 0)
+ commands = GetSharedMemoryAs<const GLubyte*>(
+ commands_shm_id, commands_shm_offset, num_commands);
+
+ if (!commands)
+ return error::kOutOfBounds;
+
+ for (GLsizei i = 0; i < num_commands; ++i) {
+ switch (commands[i]) {
+ case GL_CLOSE_PATH_CHROMIUM:
+ // Close has no coords.
+ break;
+ case GL_MOVE_TO_CHROMIUM:
+ // Fallthrough.
+ case GL_LINE_TO_CHROMIUM:
+ num_coords_expected += 2;
+ break;
+ case GL_QUADRATIC_CURVE_TO_CHROMIUM:
+ num_coords_expected += 4;
+ break;
+ case GL_CUBIC_CURVE_TO_CHROMIUM:
+ num_coords_expected += 6;
+ break;
+ case GL_CONIC_CURVE_TO_CHROMIUM:
+ num_coords_expected += 5;
+ break;
+ default:
+ LOCAL_SET_GL_ERROR(GL_INVALID_ENUM, kFunctionName, "invalid command");
+ return error::kNoError;
+ }
+ }
+ }
+
+ if (!num_coords_expected.IsValid() ||
+ num_coords != num_coords_expected.ValueOrDie()) {
+ LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, kFunctionName,
+ "numCoords does not match commands");
+ return error::kNoError;
+ }
+
+ const void* coords = NULL;
+
+ if (num_coords > 0) {
+ uint32 coords_size = 0;
+ uint32 coord_type_size =
+ GLES2Util::GetGLTypeSizeForPathCoordType(coord_type);
+ if (!SafeMultiplyUint32(num_coords, coord_type_size, &coords_size))
+ return error::kOutOfBounds;
+
+ uint32 coords_shm_id = static_cast<uint32>(c.coords_shm_id);
+ uint32 coords_shm_offset = static_cast<uint32>(c.coords_shm_offset);
+ if (coords_shm_id != 0 || coords_shm_offset != 0)
+ coords = GetSharedMemoryAs<const void*>(coords_shm_id, coords_shm_offset,
+ coords_size);
+
+ if (!coords)
+ return error::kOutOfBounds;
+ }
+
+ glPathCommandsNV(service_id, num_commands, commands, num_coords, coord_type,
+ coords);
+
+ return error::kNoError;
+}
+
+error::Error GLES2DecoderImpl::HandlePathParameterfCHROMIUM(
+ uint32 immediate_data_size,
+ const void* cmd_data) {
+ static const char kFunctionName[] = "glPathParameterfCHROMIUM";
+ const gles2::cmds::PathParameterfCHROMIUM& c =
+ *static_cast<const gles2::cmds::PathParameterfCHROMIUM*>(cmd_data);
+ if (!features().chromium_path_rendering) {
+ LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, kFunctionName,
+ "function not available");
+ return error::kNoError;
+ }
+ GLuint service_id = 0;
+ if (!path_manager()->GetPath(static_cast<GLuint>(c.path), &service_id)) {
+ LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, kFunctionName,
+ "invalid path name");
+ return error::kNoError;
+ }
+
+ GLenum pname = static_cast<GLenum>(c.pname);
+ GLfloat value = static_cast<GLfloat>(c.value);
+ bool hasValueError = false;
+
+ switch (pname) {
+ case GL_PATH_STROKE_WIDTH_CHROMIUM:
+ case GL_PATH_MITER_LIMIT_CHROMIUM:
+ hasValueError = std::isnan(value) || !std::isfinite(value) || value < 0;
+ break;
+ case GL_PATH_STROKE_BOUND_CHROMIUM:
+ value = std::max(std::min(1.0f, value), 0.0f);
+ break;
+ case GL_PATH_END_CAPS_CHROMIUM:
+ hasValueError = !validators_->path_parameter_cap_values.IsValid(
+ static_cast<GLint>(value));
+ break;
+ case GL_PATH_JOIN_STYLE_CHROMIUM:
+ hasValueError = !validators_->path_parameter_join_values.IsValid(
+ static_cast<GLint>(value));
+ break;
+ default:
+ DCHECK(!validators_->path_parameter.IsValid(pname));
+ LOCAL_SET_GL_ERROR_INVALID_ENUM(kFunctionName, pname, "pname");
+ return error::kNoError;
+ }
+ DCHECK(validators_->path_parameter.IsValid(pname));
+
+ if (hasValueError) {
+ LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, kFunctionName, "value not correct");
+ return error::kNoError;
+ }
+
+ glPathParameterfNV(service_id, pname, value);
+ return error::kNoError;
+}
+
+error::Error GLES2DecoderImpl::HandlePathParameteriCHROMIUM(
+ uint32 immediate_data_size,
+ const void* cmd_data) {
+ static const char kFunctionName[] = "glPathParameteriCHROMIUM";
+ const gles2::cmds::PathParameteriCHROMIUM& c =
+ *static_cast<const gles2::cmds::PathParameteriCHROMIUM*>(cmd_data);
+ if (!features().chromium_path_rendering) {
+ LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, kFunctionName,
+ "function not available");
+ return error::kNoError;
+ }
+ GLuint service_id = 0;
+ if (!path_manager()->GetPath(static_cast<GLuint>(c.path), &service_id)) {
+ LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, kFunctionName,
+ "invalid path name");
+ return error::kNoError;
+ }
+
+ GLenum pname = static_cast<GLenum>(c.pname);
+ GLint value = static_cast<GLint>(c.value);
+ bool hasValueError = false;
+
+ switch (pname) {
+ case GL_PATH_STROKE_WIDTH_CHROMIUM:
+ case GL_PATH_MITER_LIMIT_CHROMIUM:
+ hasValueError = value < 0;
+ break;
+ case GL_PATH_STROKE_BOUND_CHROMIUM:
+ value = std::max(std::min(1, value), 0);
+ break;
+ case GL_PATH_END_CAPS_CHROMIUM:
+ hasValueError = !validators_->path_parameter_cap_values.IsValid(value);
+ break;
+ case GL_PATH_JOIN_STYLE_CHROMIUM:
+ hasValueError = !validators_->path_parameter_join_values.IsValid(value);
+ break;
+ default:
+ DCHECK(!validators_->path_parameter.IsValid(pname));
+ LOCAL_SET_GL_ERROR_INVALID_ENUM(kFunctionName, pname, "pname");
+ return error::kNoError;
+ }
+ DCHECK(validators_->path_parameter.IsValid(pname));
+
+ if (hasValueError) {
+ LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, kFunctionName, "value not correct");
+ return error::kNoError;
+ }
+
+ glPathParameteriNV(service_id, pname, value);
+ return error::kNoError;
+}
+
+error::Error GLES2DecoderImpl::HandleStencilFillPathCHROMIUM(
+ uint32 immediate_data_size,
+ const void* cmd_data) {
+ static const char kFunctionName[] = "glStencilFillPathCHROMIUM";
+ const gles2::cmds::StencilFillPathCHROMIUM& c =
+ *static_cast<const gles2::cmds::StencilFillPathCHROMIUM*>(cmd_data);
+ if (!features().chromium_path_rendering) {
+ LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, kFunctionName,
+ "function not available");
+ return error::kNoError;
+ }
+ GLenum fill_mode = static_cast<GLenum>(c.fillMode);
+ if (!validators_->path_fill_mode.IsValid(fill_mode)) {
+ LOCAL_SET_GL_ERROR_INVALID_ENUM(kFunctionName, fill_mode, "fillMode");
+ return error::kNoError;
+ }
+ GLuint mask = static_cast<GLuint>(c.mask);
+ if ((fill_mode == GL_COUNT_UP_CHROMIUM ||
+ fill_mode == GL_COUNT_DOWN_CHROMIUM) &&
+ GLES2Util::IsNPOT(mask + 1)) {
+ LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, kFunctionName,
+ "mask + 1 is not power of two");
+ return error::kNoError;
+ }
+ GLuint service_id = 0;
+ if (!path_manager()->GetPath(static_cast<GLuint>(c.path), &service_id)) {
+ // "If /path/ does not name an existing path object, the command does
+ // nothing (and no error is generated)."
+ // This holds for other rendering functions, too.
+ return error::kNoError;
+ }
+ ApplyDirtyState();
+ glStencilFillPathNV(service_id, fill_mode, mask);
+ return error::kNoError;
+}
+
+error::Error GLES2DecoderImpl::HandleStencilStrokePathCHROMIUM(
+ uint32 immediate_data_size,
+ const void* cmd_data) {
+ static const char kFunctionName[] = "glStencilStrokePathCHROMIUM";
+ const gles2::cmds::StencilStrokePathCHROMIUM& c =
+ *static_cast<const gles2::cmds::StencilStrokePathCHROMIUM*>(cmd_data);
+ if (!features().chromium_path_rendering) {
+ LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, kFunctionName,
+ "function not available");
+ return error::kNoError;
+ }
+ GLuint service_id = 0;
+ if (!path_manager()->GetPath(static_cast<GLuint>(c.path), &service_id)) {
+ return error::kNoError;
+ }
+ GLint reference = static_cast<GLint>(c.reference);
+ GLuint mask = static_cast<GLuint>(c.mask);
+ ApplyDirtyState();
+ glStencilStrokePathNV(service_id, reference, mask);
+ return error::kNoError;
+}
+
+error::Error GLES2DecoderImpl::HandleCoverFillPathCHROMIUM(
+ uint32 immediate_data_size,
+ const void* cmd_data) {
+ static const char kFunctionName[] = "glCoverFillPathCHROMIUM";
+ const gles2::cmds::CoverFillPathCHROMIUM& c =
+ *static_cast<const gles2::cmds::CoverFillPathCHROMIUM*>(cmd_data);
+ if (!features().chromium_path_rendering) {
+ LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, kFunctionName,
+ "function not available");
+ return error::kNoError;
+ }
+ GLenum cover_mode = static_cast<GLenum>(c.coverMode);
+ if (!validators_->path_cover_mode.IsValid(cover_mode)) {
+ LOCAL_SET_GL_ERROR_INVALID_ENUM(kFunctionName, cover_mode, "coverMode");
+ return error::kNoError;
+ }
+ GLuint service_id = 0;
+ if (!path_manager()->GetPath(static_cast<GLuint>(c.path), &service_id))
+ return error::kNoError;
+
+ ApplyDirtyState();
+ glCoverFillPathNV(service_id, cover_mode);
+ return error::kNoError;
+}
+
+error::Error GLES2DecoderImpl::HandleCoverStrokePathCHROMIUM(
+ uint32 immediate_data_size,
+ const void* cmd_data) {
+ static const char kFunctionName[] = "glCoverStrokePathCHROMIUM";
+ const gles2::cmds::CoverStrokePathCHROMIUM& c =
+ *static_cast<const gles2::cmds::CoverStrokePathCHROMIUM*>(cmd_data);
+ if (!features().chromium_path_rendering) {
+ LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, kFunctionName,
+ "function not available");
+ return error::kNoError;
+ }
+ GLenum cover_mode = static_cast<GLenum>(c.coverMode);
+ if (!validators_->path_cover_mode.IsValid(cover_mode)) {
+ LOCAL_SET_GL_ERROR_INVALID_ENUM(kFunctionName, cover_mode, "coverMode");
+ return error::kNoError;
+ }
+ GLuint service_id = 0;
+ if (!path_manager()->GetPath(static_cast<GLuint>(c.path), &service_id))
+ return error::kNoError;
+
+ ApplyDirtyState();
+ glCoverStrokePathNV(service_id, cover_mode);
+ return error::kNoError;
+}
+
+error::Error GLES2DecoderImpl::HandleStencilThenCoverFillPathCHROMIUM(
+ uint32 immediate_data_size,
+ const void* cmd_data) {
+ static const char kFunctionName[] = "glStencilThenCoverFillPathCHROMIUM";
+ const gles2::cmds::StencilThenCoverFillPathCHROMIUM& c =
+ *static_cast<const gles2::cmds::StencilThenCoverFillPathCHROMIUM*>(
+ cmd_data);
+ if (!features().chromium_path_rendering) {
+ LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, kFunctionName,
+ "function not available");
+ return error::kNoError;
+ }
+ GLenum fill_mode = static_cast<GLenum>(c.fillMode);
+ if (!validators_->path_fill_mode.IsValid(fill_mode)) {
+ LOCAL_SET_GL_ERROR_INVALID_ENUM(kFunctionName, fill_mode, "fillMode");
+ return error::kNoError;
+ }
+ GLuint mask = static_cast<GLuint>(c.mask);
+ if ((fill_mode == GL_COUNT_UP_CHROMIUM ||
+ fill_mode == GL_COUNT_DOWN_CHROMIUM) &&
+ GLES2Util::IsNPOT(mask + 1)) {
+ LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, kFunctionName,
+ "mask + 1 is not power of two");
+ return error::kNoError;
+ }
+ GLenum cover_mode = static_cast<GLenum>(c.coverMode);
+ if (!validators_->path_cover_mode.IsValid(cover_mode)) {
+ LOCAL_SET_GL_ERROR_INVALID_ENUM(kFunctionName, cover_mode, "coverMode");
+ return error::kNoError;
+ }
+ GLuint service_id = 0;
+ if (!path_manager()->GetPath(static_cast<GLuint>(c.path), &service_id))
+ return error::kNoError;
+
+ ApplyDirtyState();
+ glStencilThenCoverFillPathNV(service_id, fill_mode, mask, cover_mode);
+ return error::kNoError;
+}
+
+error::Error GLES2DecoderImpl::HandleStencilThenCoverStrokePathCHROMIUM(
+ uint32 immediate_data_size,
+ const void* cmd_data) {
+ static const char kFunctionName[] = "glStencilThenCoverStrokePathCHROMIUM";
+ const gles2::cmds::StencilThenCoverStrokePathCHROMIUM& c =
+ *static_cast<const gles2::cmds::StencilThenCoverStrokePathCHROMIUM*>(
+ cmd_data);
+ if (!features().chromium_path_rendering) {
+ LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, kFunctionName,
+ "function not available");
+ return error::kNoError;
+ }
+ GLenum cover_mode = static_cast<GLenum>(c.coverMode);
+ if (!validators_->path_cover_mode.IsValid(cover_mode)) {
+ LOCAL_SET_GL_ERROR_INVALID_ENUM(kFunctionName, cover_mode, "coverMode");
+ return error::kNoError;
+ }
+ GLuint service_id = 0;
+ if (!path_manager()->GetPath(static_cast<GLuint>(c.path), &service_id))
+ return error::kNoError;
+
+ GLint reference = static_cast<GLint>(c.reference);
+ GLuint mask = static_cast<GLuint>(c.mask);
+ ApplyDirtyState();
+ glStencilThenCoverStrokePathNV(service_id, reference, mask, cover_mode);
+ return error::kNoError;
+}
+
// Include the auto-generated part of this file. We split this because it means
// we can easily edit the non-auto generated parts right here in this file
// instead of having to edit some template or the code generator.
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h b/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h
index 1314d0d..615d287 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h
@@ -4946,6 +4946,58 @@ error::Error GLES2DecoderImpl::HandleMatrixLoadIdentityCHROMIUM(
return error::kNoError;
}
+error::Error GLES2DecoderImpl::HandleIsPathCHROMIUM(
+ uint32_t immediate_data_size,
+ const void* cmd_data) {
+ const gles2::cmds::IsPathCHROMIUM& c =
+ *static_cast<const gles2::cmds::IsPathCHROMIUM*>(cmd_data);
+ (void)c;
+ if (!features().chromium_path_rendering) {
+ LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glIsPathCHROMIUM",
+ "function not available");
+ return error::kNoError;
+ }
+
+ GLuint path = c.path;
+ typedef cmds::IsPathCHROMIUM::Result Result;
+ Result* result_dst = GetSharedMemoryAs<Result*>(
+ c.result_shm_id, c.result_shm_offset, sizeof(*result_dst));
+ if (!result_dst) {
+ return error::kOutOfBounds;
+ }
+ *result_dst = DoIsPathCHROMIUM(path);
+ return error::kNoError;
+}
+
+error::Error GLES2DecoderImpl::HandlePathStencilFuncCHROMIUM(
+ uint32_t immediate_data_size,
+ const void* cmd_data) {
+ const gles2::cmds::PathStencilFuncCHROMIUM& c =
+ *static_cast<const gles2::cmds::PathStencilFuncCHROMIUM*>(cmd_data);
+ (void)c;
+ if (!features().chromium_path_rendering) {
+ LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glPathStencilFuncCHROMIUM",
+ "function not available");
+ return error::kNoError;
+ }
+
+ GLenum func = static_cast<GLenum>(c.func);
+ GLint ref = static_cast<GLint>(c.ref);
+ GLuint mask = static_cast<GLuint>(c.mask);
+ if (!validators_->cmp_function.IsValid(func)) {
+ LOCAL_SET_GL_ERROR_INVALID_ENUM("glPathStencilFuncCHROMIUM", func, "func");
+ return error::kNoError;
+ }
+ if (state_.stencil_path_func != func || state_.stencil_path_ref != ref ||
+ state_.stencil_path_mask != mask) {
+ state_.stencil_path_func = func;
+ state_.stencil_path_ref = ref;
+ state_.stencil_path_mask = mask;
+ glPathStencilFuncNV(func, ref, mask);
+ }
+ return error::kNoError;
+}
+
error::Error GLES2DecoderImpl::HandleBlendBarrierKHR(
uint32_t immediate_data_size,
const void* cmd_data) {
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_0_autogen.h b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_0_autogen.h
index 1620423..dd11716 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_0_autogen.h
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_0_autogen.h
@@ -71,6 +71,11 @@ void GLES2DecoderTestBase::SetupInitStateExpectations() {
.Times(1)
.RetiresOnSaturation();
}
+ if (group_->feature_info()->feature_flags().chromium_path_rendering) {
+ EXPECT_CALL(*gl_, PathStencilFuncNV(GL_ALWAYS, 0, 0xFFFFFFFFU))
+ .Times(1)
+ .RetiresOnSaturation();
+ }
EXPECT_CALL(*gl_, PixelStorei(GL_PACK_ALIGNMENT, 4))
.Times(1)
.RetiresOnSaturation();
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_4_autogen.h b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_4_autogen.h
new file mode 100644
index 0000000..9abba52
--- /dev/null
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_4_autogen.h
@@ -0,0 +1,15 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file is auto-generated from
+// gpu/command_buffer/build_gles2_cmd_buffer.py
+// It's formatted by clang-format using chromium coding style:
+// clang-format -i -style=chromium filename
+// DO NOT EDIT!
+
+// It is included by gles2_cmd_decoder_unittest_4.cc
+#ifndef GPU_COMMAND_BUFFER_SERVICE_GLES2_CMD_DECODER_UNITTEST_4_AUTOGEN_H_
+#define GPU_COMMAND_BUFFER_SERVICE_GLES2_CMD_DECODER_UNITTEST_4_AUTOGEN_H_
+
+#endif // GPU_COMMAND_BUFFER_SERVICE_GLES2_CMD_DECODER_UNITTEST_4_AUTOGEN_H_
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_extensions.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_extensions.cc
index f87a4bd..916f2aa 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_extensions.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_extensions.cc
@@ -4,21 +4,134 @@
#include "gpu/command_buffer/service/gles2_cmd_decoder.h"
+#include "base/command_line.h"
#include "gpu/command_buffer/common/gles2_cmd_format.h"
#include "gpu/command_buffer/common/gles2_cmd_utils.h"
#include "gpu/command_buffer/service/gles2_cmd_decoder_unittest.h"
+#include "gpu/command_buffer/service/gpu_switches.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gl/gl_mock.h"
using ::gfx::MockGLInterface;
using ::testing::_;
+using ::testing::Return;
namespace gpu {
namespace gles2 {
+// Class to use to test that functions which need feature flags or
+// extensions always return INVALID_OPERATION if the feature flags is not
+// enabled or extension is not present.
+class GLES2DecoderTestDisabledExtensions : public GLES2DecoderTest {
+ public:
+ GLES2DecoderTestDisabledExtensions() {}
+};
+INSTANTIATE_TEST_CASE_P(Service,
+ GLES2DecoderTestDisabledExtensions,
+ ::testing::Bool());
+
+TEST_P(GLES2DecoderTestDisabledExtensions, CHROMIUMPathRenderingDisabled) {
+ const GLuint kClientPathId = 0;
+ {
+ cmds::MatrixLoadfCHROMIUMImmediate& cmd =
+ *GetImmediateAs<cmds::MatrixLoadfCHROMIUMImmediate>();
+ GLfloat temp[16] = {
+ 0,
+ };
+ cmd.Init(GL_PATH_MODELVIEW_CHROMIUM, temp);
+ EXPECT_EQ(error::kNoError, ExecuteImmediateCmd(cmd, sizeof(temp)));
+ EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
+ }
+ {
+ cmds::MatrixLoadIdentityCHROMIUM cmd;
+ cmd.Init(GL_PATH_PROJECTION_CHROMIUM);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+ EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
+ }
+ {
+ cmds::GenPathsCHROMIUM cmd;
+ cmd.Init(0, 0);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+ EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
+ }
+ {
+ cmds::DeletePathsCHROMIUM cmd;
+ cmd.Init(0, 0);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+ EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
+ }
+ {
+ cmds::IsPathCHROMIUM cmd;
+ cmd.Init(kClientPathId, shared_memory_id_, shared_memory_offset_);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+ EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
+ }
+ {
+ cmds::PathCommandsCHROMIUM cmd;
+ cmd.Init(kClientPathId, 0, 0, 0, 0, GL_FLOAT, 0, 0);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+ EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
+ }
+ {
+ cmds::PathParameterfCHROMIUM cmd;
+ cmd.Init(kClientPathId, GL_PATH_STROKE_WIDTH_CHROMIUM, 1.0f);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+ EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
+ }
+ {
+ cmds::PathParameteriCHROMIUM cmd;
+ cmd.Init(kClientPathId, GL_PATH_STROKE_WIDTH_CHROMIUM, 1);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+ EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
+ }
+ {
+ cmds::PathStencilFuncCHROMIUM cmd;
+ cmd.Init(GL_NEVER, 2, 3);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+ EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
+ }
+ {
+ cmds::StencilFillPathCHROMIUM cmd;
+ cmd.Init(kClientPathId, GL_COUNT_UP_CHROMIUM, 1);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+ EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
+ }
+ {
+ cmds::StencilStrokePathCHROMIUM cmd;
+ cmd.Init(kClientPathId, 1, 2);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+ EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
+ }
+ {
+ cmds::CoverFillPathCHROMIUM cmd;
+ cmd.Init(kClientPathId, GL_BOUNDING_BOX_CHROMIUM);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+ EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
+ }
+ {
+ cmds::CoverStrokePathCHROMIUM cmd;
+ cmd.Init(kClientPathId, GL_BOUNDING_BOX_CHROMIUM);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+ EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
+ }
+ {
+ cmds::StencilThenCoverFillPathCHROMIUM cmd;
+ cmd.Init(kClientPathId, GL_COUNT_UP_CHROMIUM, 1, GL_BOUNDING_BOX_CHROMIUM);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+ EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
+ }
+ {
+ cmds::StencilThenCoverStrokePathCHROMIUM cmd;
+ cmd.Init(kClientPathId, 1, 2, GL_BOUNDING_BOX_CHROMIUM);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+ EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
+ }
+}
+
class GLES2DecoderTestWithCHROMIUMPathRendering : public GLES2DecoderTest {
public:
- GLES2DecoderTestWithCHROMIUMPathRendering() {}
+ GLES2DecoderTestWithCHROMIUMPathRendering() : client_path_id_(125) {}
+
void SetUp() override {
InitState init;
init.gl_version = "opengl es 3.1";
@@ -28,8 +141,24 @@ class GLES2DecoderTestWithCHROMIUMPathRendering : public GLES2DecoderTest {
init.request_depth = true;
init.bind_generates_resource = true;
init.extensions = "GL_NV_path_rendering";
- InitDecoder(init);
+ base::CommandLine command_line(0, NULL);
+ command_line.AppendSwitch(switches::kEnableGLPathRendering);
+ InitDecoderWithCommandLine(init, &command_line);
+
+ EXPECT_CALL(*gl_, GenPathsNV(1))
+ .WillOnce(Return(kServicePathId))
+ .RetiresOnSaturation();
+ cmds::GenPathsCHROMIUM cmd;
+ cmd.Init(client_path_id_, 1);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
}
+
+ protected:
+ template <typename TypeParam>
+ void TestPathCommandsCHROMIUMCoordTypes();
+
+ GLuint client_path_id_;
+ static const GLuint kServicePathId = 311;
};
INSTANTIATE_TEST_CASE_P(Service,
@@ -56,6 +185,814 @@ INSTANTIATE_TEST_CASE_P(Service,
GLES2DecoderTestWithBlendEquationAdvanced,
::testing::Bool());
+TEST_P(GLES2DecoderTestWithCHROMIUMPathRendering, GenDeletePaths) {
+ static GLuint kFirstClientID = client_path_id_ + 88;
+ static GLsizei kPathCount = 58;
+ static GLuint kFirstCreatedServiceID = 8000;
+
+ // GenPaths range 0 causes no calls.
+ cmds::GenPathsCHROMIUM gen_cmd;
+ gen_cmd.Init(kFirstClientID, 0);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(gen_cmd));
+ EXPECT_EQ(GL_NO_ERROR, GetGLError());
+
+ // DeletePaths range 0 causes no calls.
+ cmds::DeletePathsCHROMIUM delete_cmd;
+ delete_cmd.Init(kFirstClientID, 0);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(delete_cmd));
+ EXPECT_EQ(GL_NO_ERROR, GetGLError());
+
+ // DeletePaths client 0 causes no calls and no errors.
+ delete_cmd.Init(0, 1);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(delete_cmd));
+ EXPECT_EQ(GL_NO_ERROR, GetGLError());
+
+ // DeletePaths with a big range should not cause any deletes.
+ delete_cmd.Init(client_path_id_ + 1,
+ std::numeric_limits<GLsizei>::max() - client_path_id_ - 1);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(delete_cmd));
+ EXPECT_EQ(GL_NO_ERROR, GetGLError());
+
+ delete_cmd.Init(std::numeric_limits<GLsizei>::max() + 1,
+ std::numeric_limits<GLsizei>::max());
+ EXPECT_EQ(error::kNoError, ExecuteCmd(delete_cmd));
+ EXPECT_EQ(GL_NO_ERROR, GetGLError());
+
+ // Normal Gen and Delete should cause the normal calls.
+ EXPECT_CALL(*gl_, GenPathsNV(kPathCount))
+ .WillOnce(Return(kFirstCreatedServiceID))
+ .RetiresOnSaturation();
+
+ gen_cmd.Init(kFirstClientID, kPathCount);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(gen_cmd));
+ EXPECT_EQ(GL_NO_ERROR, GetGLError());
+
+ EXPECT_CALL(*gl_, DeletePathsNV(kFirstCreatedServiceID, kPathCount))
+ .RetiresOnSaturation();
+
+ delete_cmd.Init(kFirstClientID, kPathCount);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(delete_cmd));
+ EXPECT_EQ(GL_NO_ERROR, GetGLError());
+}
+
+TEST_P(GLES2DecoderTestWithCHROMIUMPathRendering, GenDeleteRanges) {
+ static GLuint kFirstClientID = client_path_id_ + 77;
+ static GLsizei kPathCount = 5800;
+ static GLuint kFirstCreatedServiceID = 8000;
+
+ // Create a range of path names, delete one in middle and then
+ // the rest. Expect 3 DeletePath calls.
+ EXPECT_CALL(*gl_, GenPathsNV(kPathCount))
+ .WillOnce(Return(kFirstCreatedServiceID))
+ .RetiresOnSaturation();
+ cmds::GenPathsCHROMIUM gen_cmd;
+ gen_cmd.Init(kFirstClientID, kPathCount);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(gen_cmd));
+ EXPECT_EQ(GL_NO_ERROR, GetGLError());
+
+ EXPECT_CALL(*gl_, DeletePathsNV(kFirstCreatedServiceID + (kPathCount / 2), 1))
+ .RetiresOnSaturation();
+
+ cmds::DeletePathsCHROMIUM delete_cmd;
+ delete_cmd.Init(kFirstClientID + (kPathCount / 2), 1);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(delete_cmd));
+ EXPECT_EQ(GL_NO_ERROR, GetGLError());
+
+ EXPECT_CALL(*gl_, DeletePathsNV(kFirstCreatedServiceID, (kPathCount / 2)))
+ .RetiresOnSaturation();
+ EXPECT_CALL(*gl_, DeletePathsNV(kFirstCreatedServiceID + (kPathCount / 2) + 1,
+ (kPathCount / 2) - 1)).RetiresOnSaturation();
+
+ delete_cmd.Init(kFirstClientID, kPathCount);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(delete_cmd));
+ EXPECT_EQ(GL_NO_ERROR, GetGLError());
+}
+
+TEST_P(GLES2DecoderTestWithCHROMIUMPathRendering, GenDeleteManyPaths) {
+ static GLuint kFirstClientID = client_path_id_ + 1;
+ static GLsizei kPathCount = std::numeric_limits<GLsizei>::max();
+ static GLuint kFirstCreatedServiceID = 8000;
+
+ EXPECT_CALL(*gl_, GenPathsNV(kPathCount))
+ .WillOnce(Return(kFirstCreatedServiceID))
+ .RetiresOnSaturation();
+
+ // GenPaths with big range.
+ cmds::GenPathsCHROMIUM gen_cmd;
+ gen_cmd.Init(kFirstClientID, kPathCount);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(gen_cmd));
+ EXPECT_EQ(GL_NO_ERROR, GetGLError());
+
+ // Path range wraps, so we get connection error.
+ gen_cmd.Init(kFirstClientID + kPathCount, kPathCount);
+ EXPECT_EQ(error::kInvalidArguments, ExecuteCmd(gen_cmd));
+ EXPECT_EQ(GL_NO_ERROR, GetGLError());
+
+ EXPECT_CALL(*gl_, DeletePathsNV(kFirstCreatedServiceID, kPathCount))
+ .RetiresOnSaturation();
+
+ cmds::DeletePathsCHROMIUM delete_cmd;
+ delete_cmd.Init(kFirstClientID, kPathCount);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(delete_cmd));
+ EXPECT_EQ(GL_NO_ERROR, GetGLError());
+
+ // Delete every possible path.
+ // We run into the one created for client_path_id_.
+ EXPECT_CALL(*gl_, DeletePathsNV(kServicePathId, 1)).RetiresOnSaturation();
+
+ delete_cmd.Init(1u, kPathCount);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(delete_cmd));
+ EXPECT_EQ(GL_NO_ERROR, GetGLError());
+
+ delete_cmd.Init(static_cast<GLuint>(kPathCount) + 1u, kPathCount);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(delete_cmd));
+ EXPECT_EQ(GL_NO_ERROR, GetGLError());
+
+ // Allocate every possible path, delete few, allocate them back and
+ // expect minimum amount of calls.
+ EXPECT_CALL(*gl_, GenPathsNV(kPathCount))
+ .WillOnce(Return(static_cast<GLuint>(1u)))
+ .WillOnce(Return(static_cast<GLuint>(kPathCount) + 1u))
+ .RetiresOnSaturation();
+
+ gen_cmd.Init(1u, kPathCount);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(gen_cmd));
+ EXPECT_EQ(GL_NO_ERROR, GetGLError());
+
+ gen_cmd.Init(static_cast<GLuint>(kPathCount) + 1u, kPathCount);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(gen_cmd));
+ EXPECT_EQ(GL_NO_ERROR, GetGLError());
+
+ gen_cmd.Init(static_cast<GLuint>(kPathCount) * 2u + 2u, kPathCount);
+ EXPECT_EQ(error::kInvalidArguments, ExecuteCmd(gen_cmd));
+ EXPECT_EQ(GL_NO_ERROR, GetGLError());
+
+ EXPECT_CALL(*gl_, DeletePathsNV(kFirstClientID, 4)).RetiresOnSaturation();
+
+ delete_cmd.Init(kFirstClientID, 4);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(delete_cmd));
+ EXPECT_EQ(GL_NO_ERROR, GetGLError());
+
+ EXPECT_CALL(*gl_, DeletePathsNV(kFirstClientID * 3, 1)).RetiresOnSaturation();
+
+ delete_cmd.Init(kFirstClientID * 3, 1);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(delete_cmd));
+ EXPECT_EQ(GL_NO_ERROR, GetGLError());
+
+ EXPECT_CALL(*gl_, GenPathsNV(1))
+ .WillOnce(Return(kFirstClientID))
+ .WillOnce(Return(kFirstClientID + 1))
+ .WillOnce(Return(kFirstClientID + 2))
+ .WillOnce(Return(kFirstClientID + 3))
+ .RetiresOnSaturation();
+
+ for (int i = 0; i < 4; ++i) {
+ gen_cmd.Init(kFirstClientID + i, 1);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(gen_cmd));
+ EXPECT_EQ(GL_NO_ERROR, GetGLError());
+ }
+
+ EXPECT_CALL(*gl_, GenPathsNV(1))
+ .WillOnce(Return(kFirstClientID * 3))
+ .RetiresOnSaturation();
+ gen_cmd.Init(kFirstClientID * 3, 1);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(gen_cmd));
+ EXPECT_EQ(GL_NO_ERROR, GetGLError());
+
+ EXPECT_CALL(*gl_, DeletePathsNV(1u, kPathCount)).RetiresOnSaturation();
+ EXPECT_CALL(*gl_, DeletePathsNV(static_cast<GLuint>(kPathCount) + 1u,
+ kPathCount)).RetiresOnSaturation();
+
+ delete_cmd.Init(1u, kPathCount);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(delete_cmd));
+ EXPECT_EQ(GL_NO_ERROR, GetGLError());
+
+ delete_cmd.Init(static_cast<GLuint>(kPathCount) + 1u, kPathCount);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(delete_cmd));
+ EXPECT_EQ(GL_NO_ERROR, GetGLError());
+
+ // Cleanup: return the client_path_id_ as a path.
+ EXPECT_CALL(*gl_, GenPathsNV(1))
+ .WillOnce(Return(static_cast<GLuint>(kServicePathId)))
+ .RetiresOnSaturation();
+
+ gen_cmd.Init(client_path_id_, 1);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(gen_cmd));
+ EXPECT_EQ(GL_NO_ERROR, GetGLError());
+}
+
+TEST_P(GLES2DecoderTestWithCHROMIUMPathRendering,
+ GenPathsCHROMIUMInvalidCalls) {
+ static GLuint kFirstClientID = client_path_id_ + 88;
+ static GLsizei kPathCount = 5800;
+ static GLuint kFirstCreatedServiceID = 8000;
+
+ // Range < 0 is causes gl error.
+ cmds::GenPathsCHROMIUM gen_cmd;
+ gen_cmd.Init(kFirstClientID, -1);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(gen_cmd));
+ EXPECT_EQ(GL_INVALID_VALUE, GetGLError());
+
+ // Path 0 is invalid client id, connection error.
+ gen_cmd.Init(0, kPathCount);
+ EXPECT_EQ(error::kInvalidArguments, ExecuteCmd(gen_cmd));
+ EXPECT_EQ(GL_NO_ERROR, GetGLError());
+
+ // Too big range causes client id to wrap, connection error.
+ gen_cmd.Init(std::numeric_limits<GLsizei>::max() + 3,
+ std::numeric_limits<GLsizei>::max());
+ EXPECT_EQ(error::kInvalidArguments, ExecuteCmd(gen_cmd));
+ EXPECT_EQ(GL_NO_ERROR, GetGLError());
+
+ // Creating duplicate client_ids cause connection error.
+ EXPECT_CALL(*gl_, GenPathsNV(kPathCount))
+ .WillOnce(Return(kFirstCreatedServiceID))
+ .RetiresOnSaturation();
+
+ gen_cmd.Init(kFirstClientID, kPathCount);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(gen_cmd));
+ EXPECT_EQ(GL_NO_ERROR, GetGLError());
+
+ // Create duplicate by executing the same cmd.
+ EXPECT_EQ(error::kInvalidArguments, ExecuteCmd(gen_cmd));
+ EXPECT_EQ(GL_NO_ERROR, GetGLError());
+
+ // Create duplicate by creating a range that contains
+ // an already existing client path id.
+ gen_cmd.Init(kFirstClientID - 1, 2);
+ EXPECT_EQ(error::kInvalidArguments, ExecuteCmd(gen_cmd));
+ EXPECT_EQ(GL_NO_ERROR, GetGLError());
+
+ // Cleanup.
+ EXPECT_CALL(*gl_, DeletePathsNV(kFirstCreatedServiceID, kPathCount))
+ .RetiresOnSaturation();
+ cmds::DeletePathsCHROMIUM delete_cmd;
+ delete_cmd.Init(kFirstClientID, kPathCount);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(delete_cmd));
+ EXPECT_EQ(GL_NO_ERROR, GetGLError());
+}
+
+TEST_P(GLES2DecoderTestWithCHROMIUMPathRendering,
+ DeletePathsCHROMIUMInvalidCalls) {
+ static GLuint kFirstClientID = client_path_id_ + 88;
+
+ // Range < 0 is causes gl error.
+ cmds::DeletePathsCHROMIUM delete_cmd;
+ delete_cmd.Init(kFirstClientID, -1);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(delete_cmd));
+ EXPECT_EQ(GL_INVALID_VALUE, GetGLError());
+
+ // Too big range causes client id to wrap, connection error.
+ delete_cmd.Init(std::numeric_limits<GLsizei>::max() + 3,
+ std::numeric_limits<GLsizei>::max());
+ EXPECT_EQ(error::kInvalidArguments, ExecuteCmd(delete_cmd));
+ EXPECT_EQ(GL_NO_ERROR, GetGLError());
+}
+
+TEST_P(GLES2DecoderTestWithCHROMIUMPathRendering,
+ PathCommandsCHROMIUMInvalidCalls) {
+ static const GLsizei kCorrectCoordCount = 19;
+ static const GLsizei kCorrectCommandCount = 6;
+ static const GLenum kInvalidCoordType = GL_NONE;
+
+ GLfloat* coords = GetSharedMemoryAs<GLfloat*>();
+ unsigned commands_offset = sizeof(GLfloat) * kCorrectCoordCount;
+ GLubyte* commands = GetSharedMemoryAsWithOffset<GLubyte*>(commands_offset);
+ for (int i = 0; i < kCorrectCoordCount; ++i) {
+ coords[i] = 5.0f * i;
+ }
+ commands[0] = GL_MOVE_TO_CHROMIUM;
+ commands[1] = GL_CLOSE_PATH_CHROMIUM;
+ commands[2] = GL_LINE_TO_CHROMIUM;
+ commands[3] = GL_QUADRATIC_CURVE_TO_CHROMIUM;
+ commands[4] = GL_CUBIC_CURVE_TO_CHROMIUM;
+ commands[5] = GL_CONIC_CURVE_TO_CHROMIUM;
+
+ EXPECT_CALL(*gl_, PathCommandsNV(kServicePathId, kCorrectCommandCount,
+ commands, kCorrectCoordCount, GL_FLOAT,
+ coords)).RetiresOnSaturation();
+
+ cmds::PathCommandsCHROMIUM cmd;
+
+ // Reference call -- this succeeds.
+ cmd.Init(client_path_id_, kCorrectCommandCount, shared_memory_id_,
+ shared_memory_offset_ + commands_offset, kCorrectCoordCount,
+ GL_FLOAT, shared_memory_id_, shared_memory_offset_);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+ EXPECT_EQ(GL_NO_ERROR, GetGLError());
+
+ EXPECT_CALL(*gl_, PathCommandsNV(_, _, _, _, _, _)).Times(0);
+
+ // Invalid client id fails.
+ cmd.Init(client_path_id_ - 1, kCorrectCommandCount, shared_memory_id_,
+ shared_memory_offset_, kCorrectCoordCount, GL_FLOAT,
+ shared_memory_id_, shared_memory_offset_);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+ EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
+
+ // The numCommands < 0.
+ cmd.Init(client_path_id_, -1, shared_memory_id_, shared_memory_offset_,
+ kCorrectCoordCount, GL_FLOAT, shared_memory_id_,
+ shared_memory_offset_);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+ EXPECT_EQ(GL_INVALID_VALUE, GetGLError());
+
+ // The numCoords < 0.
+ cmd.Init(client_path_id_, kCorrectCommandCount, shared_memory_id_,
+ shared_memory_offset_, -1, GL_FLOAT, shared_memory_id_,
+ shared_memory_offset_);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+ EXPECT_EQ(GL_INVALID_VALUE, GetGLError());
+
+ // Invalid coordType fails.
+ cmd.Init(client_path_id_, kCorrectCommandCount, shared_memory_id_,
+ shared_memory_offset_, kCorrectCoordCount, kInvalidCoordType,
+ shared_memory_id_, shared_memory_offset_);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+ EXPECT_EQ(GL_INVALID_ENUM, GetGLError());
+
+ // Big command counts.
+ cmd.Init(client_path_id_, std::numeric_limits<GLsizei>::max(),
+ shared_memory_id_, shared_memory_offset_ + commands_offset,
+ kCorrectCoordCount, GL_FLOAT, shared_memory_id_,
+ shared_memory_offset_);
+ EXPECT_EQ(error::kOutOfBounds, ExecuteCmd(cmd));
+ EXPECT_EQ(GL_NO_ERROR, GetGLError());
+
+ // Invalid SHM cases.
+ cmd.Init(client_path_id_, kCorrectCommandCount, kInvalidSharedMemoryId,
+ shared_memory_offset_ + commands_offset, kCorrectCoordCount,
+ GL_FLOAT, shared_memory_id_, shared_memory_offset_);
+ EXPECT_EQ(error::kOutOfBounds, ExecuteCmd(cmd));
+ EXPECT_EQ(GL_NO_ERROR, GetGLError());
+
+ cmd.Init(client_path_id_, kCorrectCommandCount, shared_memory_id_,
+ kInvalidSharedMemoryOffset, kCorrectCoordCount, GL_FLOAT,
+ shared_memory_id_, shared_memory_offset_);
+ EXPECT_EQ(error::kOutOfBounds, ExecuteCmd(cmd));
+ EXPECT_EQ(GL_NO_ERROR, GetGLError());
+
+ cmd.Init(client_path_id_, kCorrectCommandCount, shared_memory_id_,
+ shared_memory_offset_ + commands_offset, kCorrectCoordCount,
+ GL_FLOAT, kInvalidSharedMemoryId, shared_memory_offset_);
+ EXPECT_EQ(error::kOutOfBounds, ExecuteCmd(cmd));
+ EXPECT_EQ(GL_NO_ERROR, GetGLError());
+
+ cmd.Init(client_path_id_, kCorrectCommandCount, shared_memory_id_,
+ shared_memory_offset_ + commands_offset, kCorrectCoordCount,
+ GL_FLOAT, shared_memory_id_, kInvalidSharedMemoryOffset);
+ EXPECT_EQ(error::kOutOfBounds, ExecuteCmd(cmd));
+ EXPECT_EQ(GL_NO_ERROR, GetGLError());
+
+ // NULL shm command id with non-zero command count.
+ cmd.Init(client_path_id_, kCorrectCommandCount, 0, 0, kCorrectCoordCount,
+ GL_FLOAT, shared_memory_id_, shared_memory_offset_);
+ EXPECT_EQ(error::kOutOfBounds, ExecuteCmd(cmd));
+ EXPECT_EQ(GL_NO_ERROR, GetGLError());
+
+ // NULL shm coord id with non-zero coord count.
+ cmd.Init(client_path_id_, kCorrectCommandCount, shared_memory_id_,
+ shared_memory_offset_ + commands_offset, kCorrectCoordCount,
+ GL_FLOAT, 0, 0);
+ EXPECT_EQ(error::kOutOfBounds, ExecuteCmd(cmd));
+ EXPECT_EQ(GL_NO_ERROR, GetGLError());
+
+ // The coordCount not matching what is in commands.
+ // Expects kCorrectCoordCount+2 coords.
+ commands[1] = GL_MOVE_TO_CHROMIUM;
+ cmd.Init(client_path_id_, kCorrectCommandCount, shared_memory_id_,
+ shared_memory_offset_ + commands_offset, kCorrectCoordCount,
+ GL_FLOAT, shared_memory_id_, shared_memory_offset_);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+ EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
+
+ // The coordCount not matching what is in commands.
+ // Expects kCorrectCoordCount-2 coords.
+ commands[0] = GL_CLOSE_PATH_CHROMIUM;
+ commands[1] = GL_CLOSE_PATH_CHROMIUM;
+ EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+ EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
+
+ // NULL shm coord ids. Currently causes gl error, though client should not let
+ // this through.
+ cmd.Init(client_path_id_, kCorrectCommandCount, shared_memory_id_,
+ shared_memory_offset_ + commands_offset, kCorrectCoordCount,
+ GL_FLOAT, 0, 0);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+ EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
+}
+
+TEST_P(GLES2DecoderTestWithCHROMIUMPathRendering,
+ PathCommandsCHROMIUMEmptyCommands) {
+ EXPECT_CALL(*gl_, PathCommandsNV(kServicePathId, 0, NULL, 0, GL_FLOAT, NULL))
+ .RetiresOnSaturation();
+ cmds::PathCommandsCHROMIUM cmd;
+ cmd.Init(client_path_id_, 0, 0, 0, 0, GL_FLOAT, 0, 0);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+ EXPECT_EQ(GL_NO_ERROR, GetGLError());
+}
+
+TEST_P(GLES2DecoderTestWithCHROMIUMPathRendering,
+ PathCommandsCHROMIUMInvalidCommands) {
+ EXPECT_CALL(*gl_, PathCommandsNV(_, _, _, _, _, _)).Times(0);
+
+ cmds::PathCommandsCHROMIUM cmd;
+
+ {
+ const GLsizei kCoordCount = 2;
+ const GLsizei kCommandCount = 2;
+ GLfloat* coords = GetSharedMemoryAs<GLfloat*>();
+ unsigned commands_offset = sizeof(GLfloat) * kCoordCount;
+ GLubyte* commands = GetSharedMemoryAsWithOffset<GLubyte*>(commands_offset);
+
+ coords[0] = 5.0f;
+ coords[1] = 5.0f;
+ commands[0] = 0x3; // Token MOVE_TO_RELATIVE in NV_path_rendering.
+ commands[1] = GL_CLOSE_PATH_CHROMIUM;
+
+ cmd.Init(client_path_id_ - 1, kCommandCount, shared_memory_id_,
+ shared_memory_offset_, kCoordCount, GL_FLOAT, shared_memory_id_,
+ shared_memory_offset_);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+ EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
+ }
+ {
+ const GLsizei kCoordCount = 8;
+ const GLsizei kCommandCount = 4;
+ GLfloat* coords = GetSharedMemoryAs<GLfloat*>();
+ unsigned commands_offset = sizeof(GLfloat) * kCoordCount;
+ GLubyte* commands = GetSharedMemoryAsWithOffset<GLubyte*>(commands_offset);
+
+ for (int i = 0; i < kCoordCount; ++i) {
+ coords[i] = 5.0f * i;
+ }
+ commands[0] = GL_MOVE_TO_CHROMIUM;
+ commands[1] = GL_MOVE_TO_CHROMIUM;
+ commands[2] = 'M'; // Synonym to MOVE_TO in NV_path_rendering.
+ commands[3] = GL_MOVE_TO_CHROMIUM;
+
+ cmd.Init(client_path_id_ - 1, kCommandCount, shared_memory_id_,
+ shared_memory_offset_, kCoordCount, GL_FLOAT, shared_memory_id_,
+ shared_memory_offset_);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+ EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
+ }
+}
+
+TEST_P(GLES2DecoderTestWithCHROMIUMPathRendering, PathParameterXCHROMIUM) {
+ static GLuint kFirstClientID = client_path_id_ + 88;
+ static GLsizei kPathCount = 2;
+ static GLuint kFirstCreatedServiceID = 8000;
+
+ // Create a paths so that we do not modify client_path_id_
+ EXPECT_CALL(*gl_, GenPathsNV(kPathCount))
+ .WillOnce(Return(kFirstCreatedServiceID))
+ .RetiresOnSaturation();
+ cmds::GenPathsCHROMIUM gen_cmd;
+ gen_cmd.Init(kFirstClientID, kPathCount);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(gen_cmd));
+ EXPECT_EQ(GL_NO_ERROR, GetGLError());
+
+ cmds::PathParameterfCHROMIUM fcmd;
+ cmds::PathParameteriCHROMIUM icmd;
+ const struct {
+ GLenum pname;
+ GLfloat value;
+ GLfloat expected_value;
+ } kTestcases[] = {
+ {GL_PATH_STROKE_WIDTH_CHROMIUM, 1.0f, 1.0f},
+ {GL_PATH_STROKE_WIDTH_CHROMIUM, 0.0f, 0.0f},
+ {GL_PATH_MITER_LIMIT_CHROMIUM, 500.0f, 500.0f},
+ {GL_PATH_STROKE_BOUND_CHROMIUM, .80f, .80f},
+ {GL_PATH_STROKE_BOUND_CHROMIUM, 1.80f, 1.0f},
+ {GL_PATH_STROKE_BOUND_CHROMIUM, -1.0f, 0.0f},
+ {GL_PATH_END_CAPS_CHROMIUM, GL_FLAT_CHROMIUM, GL_FLAT_CHROMIUM},
+ {GL_PATH_END_CAPS_CHROMIUM, GL_SQUARE_CHROMIUM, GL_SQUARE_CHROMIUM},
+ {GL_PATH_JOIN_STYLE_CHROMIUM,
+ GL_MITER_REVERT_CHROMIUM,
+ GL_MITER_REVERT_CHROMIUM},
+ };
+
+ for (auto& testcase : kTestcases) {
+ EXPECT_CALL(*gl_, PathParameterfNV(kFirstCreatedServiceID, testcase.pname,
+ testcase.expected_value))
+ .Times(1)
+ .RetiresOnSaturation();
+ fcmd.Init(kFirstClientID, testcase.pname, testcase.value);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(fcmd));
+ EXPECT_EQ(GL_NO_ERROR, GetGLError());
+
+ EXPECT_CALL(*gl_,
+ PathParameteriNV(kFirstCreatedServiceID + 1, testcase.pname,
+ static_cast<GLint>(testcase.expected_value)))
+ .Times(1)
+ .RetiresOnSaturation();
+ icmd.Init(kFirstClientID + 1, testcase.pname,
+ static_cast<GLint>(testcase.value));
+ EXPECT_EQ(error::kNoError, ExecuteCmd(icmd));
+ EXPECT_EQ(GL_NO_ERROR, GetGLError());
+ }
+
+ // Cleanup.
+ EXPECT_CALL(*gl_, DeletePathsNV(kFirstCreatedServiceID, kPathCount))
+ .RetiresOnSaturation();
+
+ cmds::DeletePathsCHROMIUM delete_cmd;
+ delete_cmd.Init(kFirstClientID, kPathCount);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(delete_cmd));
+ EXPECT_EQ(GL_NO_ERROR, GetGLError());
+}
+
+TEST_P(GLES2DecoderTestWithCHROMIUMPathRendering,
+ PathParameterXCHROMIUMInvalidArgs) {
+ static GLuint kFirstClientID = client_path_id_ + 88;
+ static GLsizei kPathCount = 2;
+ static GLuint kFirstCreatedServiceID = 8000;
+
+ // Create a paths so that we do not modify client_path_id_
+ EXPECT_CALL(*gl_, GenPathsNV(kPathCount))
+ .WillOnce(Return(kFirstCreatedServiceID))
+ .RetiresOnSaturation();
+ cmds::GenPathsCHROMIUM gen_cmd;
+ gen_cmd.Init(kFirstClientID, kPathCount);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(gen_cmd));
+ EXPECT_EQ(GL_NO_ERROR, GetGLError());
+
+ cmds::PathParameterfCHROMIUM fcmd;
+ cmds::PathParameteriCHROMIUM icmd;
+ const struct {
+ GLenum pname;
+ GLfloat value;
+ bool try_int_version;
+ GLint error;
+ } kTestcases[] = {
+ {GL_PATH_STROKE_WIDTH_CHROMIUM, -1.0f, true, GL_INVALID_VALUE},
+ {GL_PATH_MITER_LIMIT_CHROMIUM,
+ std::numeric_limits<float>::infinity(),
+ false,
+ GL_INVALID_VALUE},
+ {GL_PATH_MITER_LIMIT_CHROMIUM,
+ std::numeric_limits<float>::quiet_NaN(),
+ false,
+ GL_INVALID_VALUE},
+ {GL_PATH_END_CAPS_CHROMIUM, 0x4, true, GL_INVALID_VALUE},
+ {GL_PATH_END_CAPS_CHROMIUM,
+ GL_MITER_REVERT_CHROMIUM,
+ true,
+ GL_INVALID_VALUE},
+ {GL_PATH_JOIN_STYLE_CHROMIUM, GL_FLAT_CHROMIUM, true, GL_INVALID_VALUE},
+ {GL_PATH_MODELVIEW_CHROMIUM, GL_FLAT_CHROMIUM, true, GL_INVALID_ENUM},
+ };
+
+ EXPECT_CALL(*gl_, PathParameterfNV(_, _, _)).Times(0);
+ EXPECT_CALL(*gl_, PathParameteriNV(_, _, _)).Times(0);
+
+ for (auto& testcase : kTestcases) {
+ fcmd.Init(kFirstClientID, testcase.pname, testcase.value);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(fcmd));
+ EXPECT_EQ(testcase.error, GetGLError());
+ if (!testcase.try_int_version)
+ continue;
+
+ icmd.Init(kFirstClientID + 1, testcase.pname,
+ static_cast<GLint>(testcase.value));
+ EXPECT_EQ(error::kNoError, ExecuteCmd(icmd));
+ EXPECT_EQ(testcase.error, GetGLError());
+ }
+
+ // Cleanup.
+ EXPECT_CALL(*gl_, DeletePathsNV(kFirstCreatedServiceID, kPathCount))
+ .RetiresOnSaturation();
+
+ cmds::DeletePathsCHROMIUM delete_cmd;
+ delete_cmd.Init(kFirstClientID, kPathCount);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(delete_cmd));
+ EXPECT_EQ(GL_NO_ERROR, GetGLError());
+}
+
+TEST_P(GLES2DecoderTestWithCHROMIUMPathRendering, StencilFillPathCHROMIUM) {
+ SetupExpectationsForApplyingDefaultDirtyState();
+
+ cmds::StencilFillPathCHROMIUM cmd;
+ cmds::StencilThenCoverFillPathCHROMIUM tcmd;
+
+ static const GLenum kFillModes[] = {
+ GL_INVERT, GL_COUNT_UP_CHROMIUM, GL_COUNT_DOWN_CHROMIUM};
+ static const GLuint kMask = 0x7F;
+
+ for (auto& fill_mode : kFillModes) {
+ EXPECT_CALL(*gl_, StencilFillPathNV(kServicePathId, fill_mode, kMask))
+ .RetiresOnSaturation();
+ cmd.Init(client_path_id_, fill_mode, kMask);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+ EXPECT_EQ(GL_NO_ERROR, GetGLError());
+
+ EXPECT_CALL(*gl_, StencilThenCoverFillPathNV(kServicePathId, fill_mode,
+ kMask, GL_BOUNDING_BOX_NV))
+ .RetiresOnSaturation();
+ tcmd.Init(client_path_id_, fill_mode, kMask, GL_BOUNDING_BOX_CHROMIUM);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(tcmd));
+ EXPECT_EQ(GL_NO_ERROR, GetGLError());
+ }
+
+ // Non-existent path: no error, no call.
+ cmd.Init(client_path_id_ - 1, GL_INVERT, 0x80);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+ EXPECT_EQ(GL_NO_ERROR, GetGLError());
+
+ tcmd.Init(client_path_id_ - 1, GL_INVERT, 0x80, GL_BOUNDING_BOX_CHROMIUM);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(tcmd));
+ EXPECT_EQ(GL_NO_ERROR, GetGLError());
+}
+
+TEST_P(GLES2DecoderTestWithCHROMIUMPathRendering,
+ StencilFillPathCHROMIUMInvalidArgs) {
+ EXPECT_CALL(*gl_, StencilFillPathNV(_, _, _)).Times(0);
+ EXPECT_CALL(*gl_, StencilThenCoverFillPathNV(_, _, _, GL_BOUNDING_BOX_NV))
+ .Times(0);
+
+ cmds::StencilFillPathCHROMIUM cmd;
+ cmds::StencilThenCoverFillPathCHROMIUM tcmd;
+
+ cmd.Init(client_path_id_, GL_INVERT - 1, 0x80);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+ EXPECT_EQ(GL_INVALID_ENUM, GetGLError());
+
+ tcmd.Init(client_path_id_, GL_INVERT - 1, 0x80, GL_BOUNDING_BOX_CHROMIUM);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(tcmd));
+ EXPECT_EQ(GL_INVALID_ENUM, GetGLError());
+
+ // The /mask/+1 is not power of two -> invalid value.
+ cmd.Init(client_path_id_, GL_COUNT_UP_CHROMIUM, 0x80);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+ EXPECT_EQ(GL_INVALID_VALUE, GetGLError());
+
+ tcmd.Init(client_path_id_, GL_COUNT_UP_CHROMIUM, 0x80,
+ GL_BOUNDING_BOX_CHROMIUM);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(tcmd));
+ EXPECT_EQ(GL_INVALID_VALUE, GetGLError());
+
+ cmd.Init(client_path_id_, GL_COUNT_DOWN_CHROMIUM, 5);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+ EXPECT_EQ(GL_INVALID_VALUE, GetGLError());
+
+ tcmd.Init(client_path_id_, GL_COUNT_DOWN_CHROMIUM, 5,
+ GL_BOUNDING_BOX_CHROMIUM);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(tcmd));
+ EXPECT_EQ(GL_INVALID_VALUE, GetGLError());
+}
+
+TEST_P(GLES2DecoderTestWithCHROMIUMPathRendering, StencilStrokePathCHROMIUM) {
+ SetupExpectationsForApplyingDefaultDirtyState();
+
+ EXPECT_CALL(*gl_, StencilStrokePathNV(kServicePathId, 1, 0x80))
+ .RetiresOnSaturation();
+ EXPECT_CALL(*gl_, StencilThenCoverStrokePathNV(kServicePathId, 1, 0x80,
+ GL_BOUNDING_BOX_NV))
+ .RetiresOnSaturation();
+
+ cmds::StencilStrokePathCHROMIUM cmd;
+ cmds::StencilThenCoverStrokePathCHROMIUM tcmd;
+
+ cmd.Init(client_path_id_, 1, 0x80);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+ EXPECT_EQ(GL_NO_ERROR, GetGLError());
+
+ tcmd.Init(client_path_id_, 1, 0x80, GL_BOUNDING_BOX_CHROMIUM);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(tcmd));
+ EXPECT_EQ(GL_NO_ERROR, GetGLError());
+
+ EXPECT_CALL(*gl_, StencilThenCoverStrokePathNV(kServicePathId, 1, 0x80,
+ GL_CONVEX_HULL_NV))
+ .RetiresOnSaturation();
+
+ tcmd.Init(client_path_id_, 1, 0x80, GL_CONVEX_HULL_CHROMIUM);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(tcmd));
+ EXPECT_EQ(GL_NO_ERROR, GetGLError());
+
+ // Non-existent path: no error, no call.
+ cmd.Init(client_path_id_ - 1, 1, 0x80);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+ EXPECT_EQ(GL_NO_ERROR, GetGLError());
+
+ tcmd.Init(client_path_id_ - 1, 1, 0x80, GL_BOUNDING_BOX_CHROMIUM);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(tcmd));
+ EXPECT_EQ(GL_NO_ERROR, GetGLError());
+}
+
+TEST_P(GLES2DecoderTestWithCHROMIUMPathRendering, CoverFillPathCHROMIUM) {
+ SetupExpectationsForApplyingDefaultDirtyState();
+
+ EXPECT_CALL(*gl_, CoverFillPathNV(kServicePathId, GL_BOUNDING_BOX_NV))
+ .RetiresOnSaturation();
+ cmds::CoverFillPathCHROMIUM cmd;
+ cmd.Init(client_path_id_, GL_BOUNDING_BOX_CHROMIUM);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+ EXPECT_EQ(GL_NO_ERROR, GetGLError());
+
+ EXPECT_CALL(*gl_, CoverFillPathNV(kServicePathId, GL_CONVEX_HULL_NV))
+ .RetiresOnSaturation();
+ cmd.Init(client_path_id_, GL_CONVEX_HULL_CHROMIUM);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+ EXPECT_EQ(GL_NO_ERROR, GetGLError());
+
+ // Non-existent path: no error, no call.
+ cmd.Init(client_path_id_ - 1, GL_BOUNDING_BOX_CHROMIUM);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+ EXPECT_EQ(GL_NO_ERROR, GetGLError());
+}
+
+TEST_P(GLES2DecoderTestWithCHROMIUMPathRendering, CoverStrokePathCHROMIUM) {
+ SetupExpectationsForApplyingDefaultDirtyState();
+ EXPECT_CALL(*gl_, CoverStrokePathNV(kServicePathId, GL_BOUNDING_BOX_NV))
+ .RetiresOnSaturation();
+ cmds::CoverStrokePathCHROMIUM cmd;
+ cmd.Init(client_path_id_, GL_BOUNDING_BOX_CHROMIUM);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+ EXPECT_EQ(GL_NO_ERROR, GetGLError());
+
+ EXPECT_CALL(*gl_, CoverStrokePathNV(kServicePathId, GL_CONVEX_HULL_NV))
+ .RetiresOnSaturation();
+ cmd.Init(client_path_id_, GL_CONVEX_HULL_CHROMIUM);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+ EXPECT_EQ(GL_NO_ERROR, GetGLError());
+
+ // Non-existent path: no error, no call.
+ cmd.Init(client_path_id_ - 1, GL_BOUNDING_BOX_CHROMIUM);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+ EXPECT_EQ(GL_NO_ERROR, GetGLError());
+}
+
+namespace {
+template <typename T>
+struct gl_type_enum {};
+template <>
+struct gl_type_enum<GLbyte> {
+ enum { kGLType = GL_BYTE };
+};
+template <>
+struct gl_type_enum<GLubyte> {
+ enum { kGLType = GL_UNSIGNED_BYTE };
+};
+template <>
+struct gl_type_enum<GLshort> {
+ enum { kGLType = GL_SHORT };
+};
+template <>
+struct gl_type_enum<GLushort> {
+ enum { kGLType = GL_UNSIGNED_SHORT };
+};
+template <>
+struct gl_type_enum<GLfloat> {
+ enum { kGLType = GL_FLOAT };
+};
+}
+
+template <typename TypeParam>
+void GLES2DecoderTestWithCHROMIUMPathRendering::
+ TestPathCommandsCHROMIUMCoordTypes() {
+ static const GLsizei kCorrectCoordCount = 19;
+ static const GLsizei kCorrectCommandCount = 6;
+
+ TypeParam* coords = GetSharedMemoryAs<TypeParam*>();
+ unsigned commands_offset = sizeof(TypeParam) * kCorrectCoordCount;
+ GLubyte* commands = GetSharedMemoryAsWithOffset<GLubyte*>(commands_offset);
+ for (int i = 0; i < kCorrectCoordCount; ++i) {
+ coords[i] = static_cast<TypeParam>(5 * i);
+ }
+ commands[0] = GL_MOVE_TO_CHROMIUM;
+ commands[1] = GL_CLOSE_PATH_CHROMIUM;
+ commands[2] = GL_LINE_TO_CHROMIUM;
+ commands[3] = GL_QUADRATIC_CURVE_TO_CHROMIUM;
+ commands[4] = GL_CUBIC_CURVE_TO_CHROMIUM;
+ commands[5] = GL_CONIC_CURVE_TO_CHROMIUM;
+
+ EXPECT_CALL(*gl_, PathCommandsNV(kServicePathId, kCorrectCommandCount,
+ commands, kCorrectCoordCount,
+ gl_type_enum<TypeParam>::kGLType, coords))
+ .RetiresOnSaturation();
+
+ cmds::PathCommandsCHROMIUM cmd;
+
+ cmd.Init(client_path_id_, kCorrectCommandCount, shared_memory_id_,
+ shared_memory_offset_ + commands_offset, kCorrectCoordCount,
+ gl_type_enum<TypeParam>::kGLType, shared_memory_id_,
+ shared_memory_offset_);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+ EXPECT_EQ(GL_NO_ERROR, GetGLError());
+}
+
+TEST_P(GLES2DecoderTestWithCHROMIUMPathRendering,
+ PathCommandsCHROMIUMCoordTypes) {
+ // Not using a typed test case, because the base class is already parametrized
+ // test case and uses GetParam.
+ TestPathCommandsCHROMIUMCoordTypes<GLbyte>();
+ TestPathCommandsCHROMIUMCoordTypes<GLubyte>();
+ TestPathCommandsCHROMIUMCoordTypes<GLshort>();
+ TestPathCommandsCHROMIUMCoordTypes<GLushort>();
+ TestPathCommandsCHROMIUMCoordTypes<GLfloat>();
+}
+
#include "gpu/command_buffer/service/gles2_cmd_decoder_unittest_extensions_autogen.h"
} // namespace gles2
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_extensions_autogen.h b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_extensions_autogen.h
index 57db672..5c22b6c 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_extensions_autogen.h
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_extensions_autogen.h
@@ -44,6 +44,55 @@ TEST_P(GLES2DecoderTestWithCHROMIUMPathRendering,
EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
}
+// TODO(gman): GenPathsCHROMIUM
+
+// TODO(gman): DeletePathsCHROMIUM
+
+TEST_P(GLES2DecoderTestWithCHROMIUMPathRendering, IsPathCHROMIUMValidArgs) {
+ EXPECT_CALL(*gl_, IsPathNV(kServicePathId));
+ SpecializedSetup<cmds::IsPathCHROMIUM, 0>(true);
+ cmds::IsPathCHROMIUM cmd;
+ cmd.Init(client_path_id_, shared_memory_id_, shared_memory_offset_);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+ EXPECT_EQ(GL_NO_ERROR, GetGLError());
+}
+
+TEST_P(GLES2DecoderTestWithCHROMIUMPathRendering,
+ IsPathCHROMIUMInvalidArgsBadSharedMemoryId) {
+ EXPECT_CALL(*gl_, IsPathNV(kServicePathId)).Times(0);
+ SpecializedSetup<cmds::IsPathCHROMIUM, 0>(false);
+ cmds::IsPathCHROMIUM cmd;
+ cmd.Init(client_path_id_, kInvalidSharedMemoryId, shared_memory_offset_);
+ EXPECT_EQ(error::kOutOfBounds, ExecuteCmd(cmd));
+ cmd.Init(client_path_id_, shared_memory_id_, kInvalidSharedMemoryOffset);
+ EXPECT_EQ(error::kOutOfBounds, ExecuteCmd(cmd));
+}
+// TODO(gman): PathCommandsCHROMIUM
+
+// TODO(gman): PathParameterfCHROMIUM
+
+// TODO(gman): PathParameteriCHROMIUM
+
+TEST_P(GLES2DecoderTestWithCHROMIUMPathRendering,
+ PathStencilFuncCHROMIUMValidArgs) {
+ EXPECT_CALL(*gl_, PathStencilFuncNV(GL_NEVER, 2, 3));
+ SpecializedSetup<cmds::PathStencilFuncCHROMIUM, 0>(true);
+ cmds::PathStencilFuncCHROMIUM cmd;
+ cmd.Init(GL_NEVER, 2, 3);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+ EXPECT_EQ(GL_NO_ERROR, GetGLError());
+}
+// TODO(gman): StencilFillPathCHROMIUM
+
+// TODO(gman): StencilStrokePathCHROMIUM
+
+// TODO(gman): CoverFillPathCHROMIUM
+
+// TODO(gman): CoverStrokePathCHROMIUM
+
+// TODO(gman): StencilThenCoverFillPathCHROMIUM
+
+// TODO(gman): StencilThenCoverStrokePathCHROMIUM
TEST_P(GLES2DecoderTestWithBlendEquationAdvanced, BlendBarrierKHRValidArgs) {
EXPECT_CALL(*gl_, BlendBarrierKHR());
diff --git a/gpu/command_buffer/service/gles2_cmd_validation_autogen.h b/gpu/command_buffer/service/gles2_cmd_validation_autogen.h
index ec96c6b..ccaeb59 100644
--- a/gpu/command_buffer/service/gles2_cmd_validation_autogen.h
+++ b/gpu/command_buffer/service/gles2_cmd_validation_autogen.h
@@ -46,6 +46,12 @@ ValueValidator<GLenum> internal_format_parameter;
ValueValidator<GLenum> invalidate_frame_buffer_target;
ValueValidator<GLenum> map_buffer_access;
ValueValidator<GLenum> matrix_mode;
+ValueValidator<GLenum> path_coord_type;
+ValueValidator<GLenum> path_cover_mode;
+ValueValidator<GLenum> path_fill_mode;
+ValueValidator<GLenum> path_parameter;
+ValueValidator<GLint> path_parameter_cap_values;
+ValueValidator<GLint> path_parameter_join_values;
ValueValidator<GLenum> pixel_store;
ValueValidator<GLint> pixel_store_alignment;
ValueValidator<GLenum> pixel_type;
diff --git a/gpu/command_buffer/service/gles2_cmd_validation_implementation_autogen.h b/gpu/command_buffer/service/gles2_cmd_validation_implementation_autogen.h
index 1c4cf97..5e23712 100644
--- a/gpu/command_buffer/service/gles2_cmd_validation_implementation_autogen.h
+++ b/gpu/command_buffer/service/gles2_cmd_validation_implementation_autogen.h
@@ -474,6 +474,45 @@ static const GLenum valid_matrix_mode_table[] = {
GL_PATH_MODELVIEW_CHROMIUM,
};
+static const GLenum valid_path_coord_type_table[] = {
+ GL_BYTE,
+ GL_UNSIGNED_BYTE,
+ GL_SHORT,
+ GL_UNSIGNED_SHORT,
+ GL_FLOAT,
+};
+
+static const GLenum valid_path_cover_mode_table[] = {
+ GL_CONVEX_HULL_CHROMIUM,
+ GL_BOUNDING_BOX_CHROMIUM,
+};
+
+static const GLenum valid_path_fill_mode_table[] = {
+ GL_INVERT,
+ GL_COUNT_UP_CHROMIUM,
+ GL_COUNT_DOWN_CHROMIUM,
+};
+
+static const GLenum valid_path_parameter_table[] = {
+ GL_PATH_STROKE_WIDTH_CHROMIUM,
+ GL_PATH_END_CAPS_CHROMIUM,
+ GL_PATH_JOIN_STYLE_CHROMIUM,
+ GL_PATH_MITER_LIMIT_CHROMIUM,
+ GL_PATH_STROKE_BOUND_CHROMIUM,
+};
+
+static const GLint valid_path_parameter_cap_values_table[] = {
+ GL_FLAT,
+ GL_SQUARE_CHROMIUM,
+ GL_ROUND_CHROMIUM,
+};
+
+static const GLint valid_path_parameter_join_values_table[] = {
+ GL_MITER_REVERT_CHROMIUM,
+ GL_BEVEL_CHROMIUM,
+ GL_ROUND_CHROMIUM,
+};
+
static const GLenum valid_pixel_store_table[] = {
GL_PACK_ALIGNMENT,
GL_UNPACK_ALIGNMENT,
@@ -1148,6 +1187,20 @@ Validators::Validators()
map_buffer_access(valid_map_buffer_access_table,
arraysize(valid_map_buffer_access_table)),
matrix_mode(valid_matrix_mode_table, arraysize(valid_matrix_mode_table)),
+ path_coord_type(valid_path_coord_type_table,
+ arraysize(valid_path_coord_type_table)),
+ path_cover_mode(valid_path_cover_mode_table,
+ arraysize(valid_path_cover_mode_table)),
+ path_fill_mode(valid_path_fill_mode_table,
+ arraysize(valid_path_fill_mode_table)),
+ path_parameter(valid_path_parameter_table,
+ arraysize(valid_path_parameter_table)),
+ path_parameter_cap_values(
+ valid_path_parameter_cap_values_table,
+ arraysize(valid_path_parameter_cap_values_table)),
+ path_parameter_join_values(
+ valid_path_parameter_join_values_table,
+ arraysize(valid_path_parameter_join_values_table)),
pixel_store(valid_pixel_store_table, arraysize(valid_pixel_store_table)),
pixel_store_alignment(valid_pixel_store_alignment_table,
arraysize(valid_pixel_store_alignment_table)),
diff --git a/gpu/command_buffer/service/gpu_switches.cc b/gpu/command_buffer/service/gpu_switches.cc
index d2ca490..171f543 100644
--- a/gpu/command_buffer/service/gpu_switches.cc
+++ b/gpu/command_buffer/service/gpu_switches.cc
@@ -71,25 +71,31 @@ const char kGLShaderIntermOutput[] = "gl-shader-interm-output";
// round intermediate values in ANGLE.
const char kEmulateShaderPrecision[] = "emulate-shader-precision";
+// Enables using path rendering implementation implemented currently
+// in NV_path_rendering OpenGL extension. Requires compatible hardware
+// and driver. This is used in GPU rasterization.
+const char kEnableGLPathRendering[] = "enable-gl-path-rendering";
+
const char* kGpuSwitches[] = {
- kCompileShaderAlwaysSucceeds,
- kDisableGLErrorLimit,
- kDisableGLSLTranslator,
- kDisableGpuDriverBugWorkarounds,
- kDisableShaderNameHashing,
- kEnableGPUCommandLogging,
- kEnableGPUDebugging,
- kEnableGPUServiceLoggingGPU,
- kDisableGpuProgramCache,
- kEnforceGLMinimums,
- kForceGpuMemAvailableMb,
- kGpuDriverBugWorkarounds,
- kGpuProgramCacheSizeKb,
- kDisableGpuShaderDiskCache,
- kEnableShareGroupAsyncTextureUpload,
- kEnableSubscribeUniformExtension,
- kGLShaderIntermOutput,
- kEmulateShaderPrecision,
+ kCompileShaderAlwaysSucceeds,
+ kDisableGLErrorLimit,
+ kDisableGLSLTranslator,
+ kDisableGpuDriverBugWorkarounds,
+ kDisableShaderNameHashing,
+ kEnableGPUCommandLogging,
+ kEnableGPUDebugging,
+ kEnableGPUServiceLoggingGPU,
+ kDisableGpuProgramCache,
+ kEnforceGLMinimums,
+ kForceGpuMemAvailableMb,
+ kGpuDriverBugWorkarounds,
+ kGpuProgramCacheSizeKb,
+ kDisableGpuShaderDiskCache,
+ kEnableShareGroupAsyncTextureUpload,
+ kEnableSubscribeUniformExtension,
+ kGLShaderIntermOutput,
+ kEmulateShaderPrecision,
+ kEnableGLPathRendering,
};
const int kNumGpuSwitches = arraysize(kGpuSwitches);
diff --git a/gpu/command_buffer/service/gpu_switches.h b/gpu/command_buffer/service/gpu_switches.h
index ac6883a..ac347e4 100644
--- a/gpu/command_buffer/service/gpu_switches.h
+++ b/gpu/command_buffer/service/gpu_switches.h
@@ -30,6 +30,7 @@ GPU_EXPORT extern const char kEnableSubscribeUniformExtension[];
GPU_EXPORT extern const char kEnableThreadedTextureMailboxes[];
GPU_EXPORT extern const char kGLShaderIntermOutput[];
GPU_EXPORT extern const char kEmulateShaderPrecision[];
+GPU_EXPORT extern const char kEnableGLPathRendering[];
GPU_EXPORT extern const char* kGpuSwitches[];
GPU_EXPORT extern const int kNumGpuSwitches;
diff --git a/gpu/command_buffer/service/path_manager.cc b/gpu/command_buffer/service/path_manager.cc
new file mode 100644
index 0000000..3e0d257
--- /dev/null
+++ b/gpu/command_buffer/service/path_manager.cc
@@ -0,0 +1,252 @@
+// Copyright (c) 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "gpu/command_buffer/service/path_manager.h"
+
+#include <algorithm>
+
+#include "base/logging.h"
+#include "gpu/command_buffer/common/gles2_cmd_utils.h"
+#include "ui/gl/gl_bindings.h"
+
+namespace gpu {
+namespace gles2 {
+
+namespace {
+void CallDeletePaths(GLuint first_id, GLuint range) {
+ while (range > 0) {
+ GLsizei irange;
+ if (range > static_cast<GLuint>(std::numeric_limits<GLsizei>::max()))
+ irange = std::numeric_limits<GLsizei>::max();
+ else
+ irange = static_cast<GLsizei>(range);
+
+ glDeletePathsNV(first_id, irange);
+ range -= irange;
+ first_id += irange;
+ }
+}
+
+template <typename RangeIterator>
+static GLuint RangeSize(const RangeIterator& it) {
+ return it->second.last_client_id - it->first + 1;
+}
+template <typename RangeIterator>
+static GLuint FirstClientId(const RangeIterator& it) {
+ return it->first;
+}
+template <typename RangeIterator>
+static GLuint FirstServiceId(const RangeIterator& it) {
+ return it->second.first_service_id;
+}
+template <typename RangeIterator>
+static GLuint LastServiceId(const RangeIterator& it) {
+ return FirstServiceId(it) + RangeSize(it) - 1;
+}
+static GLuint LastClientId(PathManager::PathRangeMap::const_iterator& it) {
+ return it->second.last_client_id;
+}
+// Note: this one can be assigned to.
+static GLuint& LastClientId(PathManager::PathRangeMap::iterator& it) {
+ return it->second.last_client_id;
+}
+
+template <typename T>
+struct IteratorSelector {
+ typedef typename T::iterator iterator;
+};
+template <typename T>
+struct IteratorSelector<const T> {
+ typedef typename T::const_iterator iterator;
+};
+
+// Returns the range position that contains |client_id| or
+// |PathRangeMap::iterator::end()| if |client_id| is not found.
+template <typename MapType>
+static typename IteratorSelector<MapType>::iterator GetContainingRange(
+ MapType& path_map,
+ GLuint client_id) {
+ auto it = path_map.lower_bound(client_id);
+ if (it != path_map.end() && FirstClientId(it) == client_id)
+ return it;
+ if (it != path_map.begin()) {
+ --it;
+ if (LastClientId(it) >= client_id)
+ return it;
+ }
+ return path_map.end();
+}
+
+// Returns the range position that contains |client_id|. If that is
+// not available, returns the range that has smallest
+// |first_client_id| that is bigger than |client_id|. Returns
+// |PathRangeMap::iterator::end()| if there is no such range.
+template <typename MapType>
+static typename IteratorSelector<MapType>::iterator GetContainingOrNextRange(
+ MapType& path_map,
+ GLuint client_id) {
+ auto it = path_map.lower_bound(client_id);
+ if (it != path_map.end() && FirstClientId(it) == client_id) {
+ return it;
+ }
+ if (it != path_map.begin()) {
+ --it;
+ if (LastClientId(it) >= client_id)
+ return it;
+ ++it;
+ }
+ return it;
+}
+
+} // anonymous namespace
+
+PathManager::PathManager() {
+}
+
+PathManager::~PathManager() {
+ DCHECK(path_map_.empty());
+}
+
+void PathManager::Destroy(bool have_context) {
+ if (have_context) {
+ for (PathRangeMap::const_iterator it = path_map_.begin();
+ it != path_map_.end(); ++it)
+ CallDeletePaths(FirstServiceId(it), RangeSize(it));
+ }
+ path_map_.clear();
+}
+
+void PathManager::CreatePathRange(GLuint first_client_id,
+ GLuint last_client_id,
+ GLuint first_service_id) {
+ DCHECK(first_service_id > 0u);
+ DCHECK(first_client_id > 0u);
+ DCHECK(!HasPathsInRange(first_client_id, last_client_id));
+ DCHECK(CheckConsistency());
+
+ PathRangeMap::iterator range =
+ GetContainingRange(path_map_, first_client_id - 1u);
+
+ if (range != path_map_.end() &&
+ LastServiceId(range) == first_service_id - 1u) {
+ DCHECK_EQ(LastClientId(range), first_client_id - 1u);
+ LastClientId(range) = last_client_id;
+ } else {
+ auto result = path_map_.insert(
+ std::make_pair(first_client_id,
+ PathRangeDescription(last_client_id, first_service_id)));
+ DCHECK(result.second);
+ range = result.first;
+ }
+
+ PathRangeMap::iterator next_range = range;
+ ++next_range;
+ if (next_range != path_map_.end()) {
+ if (LastClientId(range) == FirstClientId(next_range) - 1u &&
+ LastServiceId(range) == FirstServiceId(next_range) - 1u) {
+ LastClientId(range) = LastClientId(next_range);
+ path_map_.erase(next_range);
+ }
+ }
+ DCHECK(CheckConsistency());
+}
+
+bool PathManager::HasPathsInRange(GLuint first_client_id,
+ GLuint last_client_id) const {
+ PathRangeMap::const_iterator it =
+ GetContainingOrNextRange(path_map_, first_client_id);
+ if (it == path_map_.end())
+ return false;
+
+ return FirstClientId(it) <= last_client_id;
+}
+
+bool PathManager::GetPath(GLuint client_id, GLuint* service_id) const {
+ PathRangeMap::const_iterator range = GetContainingRange(path_map_, client_id);
+ if (range == path_map_.end())
+ return false;
+
+ *service_id = FirstServiceId(range) + client_id - FirstClientId(range);
+ return true;
+}
+
+void PathManager::RemovePaths(GLuint first_client_id, GLuint last_client_id) {
+ DCHECK(CheckConsistency());
+ PathRangeMap::iterator it =
+ GetContainingOrNextRange(path_map_, first_client_id);
+
+ while (it != path_map_.end() && FirstClientId(it) <= last_client_id) {
+ GLuint delete_first_client_id =
+ std::max(first_client_id, FirstClientId(it));
+ GLuint delete_last_client_id = std::min(last_client_id, LastClientId(it));
+ GLuint delete_first_service_id =
+ FirstServiceId(it) + delete_first_client_id - FirstClientId(it);
+ GLuint delete_range = delete_last_client_id - delete_first_client_id + 1u;
+
+ CallDeletePaths(delete_first_service_id, delete_range);
+
+ PathRangeMap::iterator current = it;
+ ++it;
+
+ GLuint current_last_client_id = LastClientId(current);
+
+ if (FirstClientId(current) < delete_first_client_id)
+ LastClientId(current) = delete_first_client_id - 1u;
+ else
+ path_map_.erase(current);
+
+ if (current_last_client_id > delete_last_client_id) {
+ path_map_.insert(std::make_pair(
+ delete_last_client_id + 1u,
+ PathRangeDescription(current_last_client_id,
+ delete_first_service_id + delete_range)));
+ DCHECK(delete_last_client_id == last_client_id);
+ // This is necessarily the last range to check. Return early due to
+ // consistency. Iterator increment would skip the inserted range. The
+ // algorithm would work ok, but it looks weird.
+ DCHECK(CheckConsistency());
+ return;
+ }
+ }
+ DCHECK(CheckConsistency());
+}
+
+bool PathManager::CheckConsistency() {
+ GLuint prev_first_client_id = 0u;
+ GLuint prev_last_client_id = 0u;
+ GLuint prev_first_service_id = 0u;
+ for (PathRangeMap::iterator range = path_map_.begin();
+ range != path_map_.end(); ++range) {
+ // Code relies on ranges not starting at 0. Also, the above initialization
+ // is only
+ // correct then.
+ if (FirstClientId(range) == 0u || FirstServiceId(range) == 0u)
+ return false;
+
+ // Each range is consistent.
+ if (FirstClientId(range) > LastClientId(range))
+ return false;
+
+ if (prev_first_client_id != 0u) {
+ // No overlapping ranges. (The iteration is sorted).
+ if (FirstClientId(range) <= prev_last_client_id)
+ return false;
+
+ // No mergeable ranges.
+ bool is_mergeable_client =
+ FirstClientId(range) - 1u == prev_last_client_id;
+ bool is_mergeable_service =
+ FirstServiceId(range) - 1u == prev_first_service_id;
+ if (is_mergeable_client && is_mergeable_service)
+ return false;
+ }
+ prev_first_client_id = FirstClientId(range);
+ prev_last_client_id = LastClientId(range);
+ prev_first_service_id = FirstServiceId(range);
+ }
+ return true;
+}
+
+} // namespace gles2
+} // namespace gpu
diff --git a/gpu/command_buffer/service/path_manager.h b/gpu/command_buffer/service/path_manager.h
new file mode 100644
index 0000000..78a53b7
--- /dev/null
+++ b/gpu/command_buffer/service/path_manager.h
@@ -0,0 +1,68 @@
+// Copyright (c) 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef GPU_COMMAND_BUFFER_SERVICE_PATH_MANAGER_H_
+#define GPU_COMMAND_BUFFER_SERVICE_PATH_MANAGER_H_
+
+#include <map>
+
+#include "base/basictypes.h"
+#include "gpu/command_buffer/service/gl_utils.h"
+#include "gpu/gpu_export.h"
+
+namespace gpu {
+namespace gles2 {
+
+// This class keeps track of paths and their client and service ids
+// so we can correctly clear them.
+class GPU_EXPORT PathManager {
+ public:
+ PathManager();
+ ~PathManager();
+
+ // Must call before destruction.
+ void Destroy(bool have_context);
+
+ // Creates a path mapping between closed intervals
+ // [first_client_id, last_client_id] -> [first_service_id, ...].
+ void CreatePathRange(GLuint first_client_id,
+ GLuint last_client_id,
+ GLuint first_service_id);
+
+ // Returns true if any path exists in the closed interval
+ // [first_client_id, last_client_id].
+ bool HasPathsInRange(GLuint first_client_id, GLuint last_client_id) const;
+
+ // Gets the path id corresponding the client path id.
+ // Returns false if no such service path id was not found.
+ bool GetPath(GLuint client_id, GLuint* service_id) const;
+
+ // Removes a closed interval of paths [first_client_id, last_client_id].
+ void RemovePaths(GLuint first_client_id, GLuint last_client_id);
+
+ // Mapping between client id and service id.
+ // Should be used only by the implementation.
+ struct PathRangeDescription {
+ PathRangeDescription(GLuint last_client, GLuint first_service)
+ : last_client_id(last_client), first_service_id(first_service) {}
+ GLuint last_client_id;
+ GLuint first_service_id;
+ typedef GLuint ServiceIdType;
+ };
+ typedef std::map<GLuint, PathRangeDescription> PathRangeMap;
+
+ private:
+ // Checks for consistency inside the book-keeping structures. Used as
+ // DCHECK pre/post condition in mutating functions.
+ bool CheckConsistency();
+
+ PathRangeMap path_map_;
+
+ DISALLOW_COPY_AND_ASSIGN(PathManager);
+};
+
+} // namespace gles2
+} // namespace gpu
+
+#endif // GPU_COMMAND_BUFFER_SERVICE_PATH_MANAGER_H_
diff --git a/gpu/command_buffer/service/path_manager_unittest.cc b/gpu/command_buffer/service/path_manager_unittest.cc
new file mode 100644
index 0000000..8a20db5
--- /dev/null
+++ b/gpu/command_buffer/service/path_manager_unittest.cc
@@ -0,0 +1,164 @@
+// Copyright (c) 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "gpu/command_buffer/service/path_manager.h"
+
+#include "base/memory/scoped_ptr.h"
+#include "gpu/command_buffer/service/gpu_service_test.h"
+#include "gpu/command_buffer/service/mocks.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gl/gl_mock.h"
+
+namespace gpu {
+namespace gles2 {
+
+class PathManagerTest : public GpuServiceTest {
+ public:
+ PathManagerTest() {}
+
+ protected:
+ void SetUp() override {
+ SetUpWithGLVersion("3.0", "GL_NV_path_rendering");
+ manager_.reset(new PathManager());
+ }
+
+ void TearDown() override {
+ manager_->Destroy(true);
+ manager_.reset();
+ GpuServiceTest::TearDown();
+ }
+
+ scoped_ptr<PathManager> manager_;
+};
+
+TEST_F(PathManagerTest, Basic) {
+ const GLuint kClient1Id = 1;
+ const GLuint kService1Id = 11;
+ const GLuint kClient2Id = 2;
+ GLuint service_id = 0;
+ manager_->CreatePathRange(kClient1Id, kClient1Id, kService1Id);
+ ASSERT_TRUE(manager_->HasPathsInRange(kClient1Id, kClient1Id));
+ EXPECT_TRUE(manager_->GetPath(kClient1Id, &service_id));
+ EXPECT_EQ(kService1Id, service_id);
+
+ // Check we get nothing for a non-existent path.
+ service_id = 123u;
+ ASSERT_FALSE(manager_->HasPathsInRange(kClient2Id, kClient2Id));
+ EXPECT_FALSE(manager_->GetPath(kClient2Id, &service_id));
+ EXPECT_EQ(123u, service_id);
+
+ // Check trying to remove non-existent paths does not crash.
+ manager_->RemovePaths(kClient2Id, kClient2Id);
+
+ // Check that it gets deleted when the last reference is released.
+ EXPECT_CALL(*gl_, DeletePathsNV(kService1Id, 1))
+ .Times(1)
+ .RetiresOnSaturation();
+
+ // Check we can't get the path after we remove it.
+ manager_->RemovePaths(kClient1Id, kClient1Id);
+ ASSERT_FALSE(manager_->HasPathsInRange(kClient1Id, kClient1Id));
+ EXPECT_FALSE(manager_->GetPath(kClient1Id, &service_id));
+}
+
+// Tests that path manager does not merge ranges that contain service ids that
+// prevent the merging. Path ranges A and B can be merged if
+// * client ids of B start immediately after the last client id of A
+// * service ids of B start immediately after the last service id of A
+// and similarly for the 'before' case.
+TEST_F(PathManagerTest, NonContiguousServiceIds) {
+ const GLuint kMergeCheckRange = 54;
+
+ const struct {
+ GLuint first_client_id;
+ GLuint last_client_id;
+ GLuint first_service_id;
+ } kIdRanges[] = {{500, 1000, 900}, {1001, 1155, 1}, {200, 499, 4888}};
+ for (auto& range : kIdRanges) {
+ manager_->CreatePathRange(range.first_client_id, range.last_client_id,
+ range.first_service_id);
+ ASSERT_TRUE(manager_->HasPathsInRange(range.first_client_id,
+ range.first_client_id));
+ ASSERT_TRUE(
+ manager_->HasPathsInRange(range.last_client_id, range.last_client_id));
+ ASSERT_TRUE(
+ manager_->HasPathsInRange(range.first_client_id, range.last_client_id));
+ GLuint service_id = 0u;
+ EXPECT_TRUE(manager_->GetPath(range.first_client_id + 5u, &service_id));
+ EXPECT_EQ(range.first_service_id + 5u, service_id);
+ }
+
+ // Insert a mergeable range last, to check that merges
+ // work. Otherwise the test could succeed because merges were not
+ // working.
+ auto& merge_candidate = kIdRanges[1];
+ GLuint merge_candidate_range =
+ merge_candidate.last_client_id - merge_candidate.first_client_id + 1;
+ manager_->CreatePathRange(
+ merge_candidate.last_client_id + 1,
+ merge_candidate.last_client_id + kMergeCheckRange,
+ merge_candidate.first_service_id + merge_candidate_range);
+
+ // We detect that ranges were not merged accidentally by detecting individual
+ // deletes.
+ for (auto& range : kIdRanges) {
+ if (&range == &merge_candidate)
+ continue;
+ GLsizei range_amount = range.last_client_id - range.first_client_id + 1;
+ EXPECT_CALL(*gl_, DeletePathsNV(range.first_service_id, range_amount))
+ .Times(1)
+ .RetiresOnSaturation();
+ }
+
+ // Just a check that merges work.
+ EXPECT_CALL(*gl_, DeletePathsNV(merge_candidate.first_service_id,
+ merge_candidate_range + kMergeCheckRange))
+ .Times(1)
+ .RetiresOnSaturation();
+
+ // Remove all ids. This should cause the expected amount of DeletePathsNV
+ // calls.
+ manager_->RemovePaths(1, std::numeric_limits<GLsizei>::max());
+
+ for (auto& range : kIdRanges) {
+ ASSERT_FALSE(
+ manager_->HasPathsInRange(range.first_client_id, range.last_client_id));
+ }
+}
+
+TEST_F(PathManagerTest, DeleteBigRange) {
+ // Allocates two ranges which in path manager end up merging as one
+ // big range. The range will be too big to fit in one DeletePaths
+ // call. Test that the range is deleted correctly with two calls.
+ const GLuint kFirstClientId1 = 1;
+ const GLsizei kRange1 = std::numeric_limits<GLsizei>::max() - 3;
+ const GLuint kLastClientId1 = kFirstClientId1 + kRange1 - 1;
+ const GLuint kFirstServiceId1 = 77;
+ const GLuint kLastServiceId1 = kFirstServiceId1 + kRange1 - 1;
+
+ const GLuint kFirstClientId2 = kLastClientId1 + 1;
+ const GLsizei kRange2 = 15;
+ const GLuint kLastClientId2 = kFirstClientId2 + kRange2 - 1;
+ const GLuint kFirstServiceId2 = kLastServiceId1 + 1;
+
+ const GLsizei kFirstDeleteRange = std::numeric_limits<GLsizei>::max();
+ const GLsizei kSecondDeleteRange = kRange2 - (kFirstDeleteRange - kRange1);
+ const GLuint kSecondDeleteFirstServiceId =
+ kFirstServiceId1 + kFirstDeleteRange;
+
+ EXPECT_CALL(*gl_, DeletePathsNV(kFirstServiceId1,
+ std::numeric_limits<GLsizei>::max()))
+ .RetiresOnSaturation();
+
+ EXPECT_CALL(*gl_, DeletePathsNV(kSecondDeleteFirstServiceId,
+ kSecondDeleteRange)).RetiresOnSaturation();
+
+ manager_->CreatePathRange(kFirstClientId1, kLastClientId1, kFirstServiceId1);
+ manager_->CreatePathRange(kFirstClientId2, kLastClientId2, kFirstServiceId2);
+ manager_->RemovePaths(0, std::numeric_limits<GLuint>::max());
+}
+
+} // namespace gles2
+
+} // namespace gpu