diff options
Diffstat (limited to 'gpu/command_buffer/service')
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 |