// 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. #include #include #include "cc/base/region.h" #include "cc/output/compositor_frame_metadata.h" #include "cc/output/gl_renderer.h" #include "cc/output/output_surface.h" #include "cc/output/output_surface_client.h" #include "cc/output/overlay_candidate_validator.h" #include "cc/output/overlay_processor.h" #include "cc/output/overlay_strategy_single_on_top.h" #include "cc/output/overlay_strategy_underlay.h" #include "cc/quads/render_pass.h" #include "cc/quads/solid_color_draw_quad.h" #include "cc/quads/stream_video_draw_quad.h" #include "cc/quads/texture_draw_quad.h" #include "cc/resources/resource_provider.h" #include "cc/resources/texture_mailbox.h" #include "cc/test/fake_output_surface_client.h" #include "cc/test/fake_resource_provider.h" #include "cc/test/geometry_test_utils.h" #include "cc/test/test_context_provider.h" #include "cc/test/test_shared_bitmap_manager.h" #include "cc/test/test_web_graphics_context_3d.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/gfx/geometry/rect_conversions.h" using testing::_; using testing::Mock; namespace cc { namespace { const gfx::Size kDisplaySize(256, 256); const gfx::Rect kOverlayRect(0, 0, 128, 128); const gfx::Rect kOverlayTopLeftRect(0, 0, 64, 64); const gfx::Rect kOverlayBottomRightRect(64, 64, 64, 64); const gfx::Rect kOverlayClipRect(0, 0, 128, 128); const gfx::PointF kUVTopLeft(0.1f, 0.2f); const gfx::PointF kUVBottomRight(1.0f, 1.0f); const gfx::Transform kNormalTransform = gfx::Transform(0.9f, 0, 0, 0.8f, 0.1f, 0.2f); // x,y -> x,y. const gfx::Transform kXMirrorTransform = gfx::Transform(-0.9f, 0, 0, 0.8f, 1.0f, 0.2f); // x,y -> 1-x,y. const gfx::Transform kYMirrorTransform = gfx::Transform(0.9f, 0, 0, -0.8f, 0.1f, 1.0f); // x,y -> x,1-y. const gfx::Transform kBothMirrorTransform = gfx::Transform(-0.9f, 0, 0, -0.8f, 1.0f, 1.0f); // x,y -> 1-x,1-y. const gfx::Transform kSwapTransform = gfx::Transform(0, 1, 1, 0, 0, 0); // x,y -> y,x. void MailboxReleased(const gpu::SyncToken& sync_token, bool lost_resource, BlockingTaskRunner* main_thread_task_runner) {} class SingleOverlayValidator : public OverlayCandidateValidator { public: void GetStrategies(OverlayProcessor::StrategyList* strategies) override { strategies->push_back( make_scoped_ptr(new OverlayStrategySingleOnTop(this))); strategies->push_back(make_scoped_ptr(new OverlayStrategyUnderlay(this))); } bool AllowCALayerOverlays() override { return false; } void CheckOverlaySupport(OverlayCandidateList* surfaces) override { // We may have 1 or 2 surfaces depending on whether this ran through the // full renderer and picked up the output surface, or not. ASSERT_LE(1U, surfaces->size()); ASSERT_GE(2U, surfaces->size()); OverlayCandidate& candidate = surfaces->back(); EXPECT_TRUE(!candidate.use_output_surface_for_resource); if (candidate.display_rect.width() == 64) { EXPECT_EQ(gfx::RectF(kOverlayBottomRightRect), candidate.display_rect); } else { EXPECT_NEAR(kOverlayRect.x(), candidate.display_rect.x(), 0.01f); EXPECT_NEAR(kOverlayRect.y(), candidate.display_rect.y(), 0.01f); EXPECT_NEAR(kOverlayRect.width(), candidate.display_rect.width(), 0.01f); EXPECT_NEAR(kOverlayRect.height(), candidate.display_rect.height(), 0.01f); } EXPECT_FLOAT_RECT_EQ(BoundingRect(kUVTopLeft, kUVBottomRight), candidate.uv_rect); if (!candidate.clip_rect.IsEmpty()) { EXPECT_EQ(true, candidate.is_clipped); EXPECT_EQ(kOverlayClipRect, candidate.clip_rect); } candidate.overlay_handled = true; } }; class CALayerValidator : public OverlayCandidateValidator { public: void GetStrategies(OverlayProcessor::StrategyList* strategies) override {} bool AllowCALayerOverlays() override { return true; } void CheckOverlaySupport(OverlayCandidateList* surfaces) override {} }; class SingleOnTopOverlayValidator : public SingleOverlayValidator { public: void GetStrategies(OverlayProcessor::StrategyList* strategies) override { strategies->push_back( make_scoped_ptr(new OverlayStrategySingleOnTop(this))); } }; class UnderlayOverlayValidator : public SingleOverlayValidator { public: void GetStrategies(OverlayProcessor::StrategyList* strategies) override { strategies->push_back(make_scoped_ptr(new OverlayStrategyUnderlay(this))); } }; class DefaultOverlayProcessor : public OverlayProcessor { public: explicit DefaultOverlayProcessor(OutputSurface* surface); size_t GetStrategyCount(); }; DefaultOverlayProcessor::DefaultOverlayProcessor(OutputSurface* surface) : OverlayProcessor(surface) { } size_t DefaultOverlayProcessor::GetStrategyCount() { return strategies_.size(); } class OverlayOutputSurface : public OutputSurface { public: explicit OverlayOutputSurface(scoped_refptr context_provider) : OutputSurface(context_provider) { surface_size_ = kDisplaySize; device_scale_factor_ = 1; is_displayed_as_overlay_plane_ = true; } void SetScaleFactor(float scale_factor) { device_scale_factor_ = scale_factor; } // OutputSurface implementation void BindFramebuffer() override { OutputSurface::BindFramebuffer(); bind_framebuffer_count_ += 1; } void SwapBuffers(CompositorFrame* frame) override { client_->DidSwapBuffers(); } void OnSwapBuffersComplete() override { client_->DidSwapBuffersComplete(); } void SetOverlayCandidateValidator(OverlayCandidateValidator* validator) { overlay_candidate_validator_.reset(validator); } OverlayCandidateValidator* GetOverlayCandidateValidator() const override { return overlay_candidate_validator_.get(); } bool IsDisplayedAsOverlayPlane() const override { return is_displayed_as_overlay_plane_; } unsigned GetOverlayTextureId() const override { return 10000; } void set_is_displayed_as_overlay_plane(bool value) { is_displayed_as_overlay_plane_ = value; } unsigned bind_framebuffer_count() const { return bind_framebuffer_count_; } private: scoped_ptr overlay_candidate_validator_; bool is_displayed_as_overlay_plane_; unsigned bind_framebuffer_count_ = 0; }; scoped_ptr CreateRenderPass() { RenderPassId id(1, 0); gfx::Rect output_rect(0, 0, 256, 256); bool has_transparent_background = true; scoped_ptr pass = RenderPass::Create(); pass->SetAll(id, output_rect, output_rect, gfx::Transform(), has_transparent_background); SharedQuadState* shared_state = pass->CreateAndAppendSharedQuadState(); shared_state->opacity = 1.f; return pass; } ResourceId CreateResource(ResourceProvider* resource_provider, const gfx::Size& size, bool is_overlay_candidate) { TextureMailbox mailbox = TextureMailbox(gpu::Mailbox::Generate(), gpu::SyncToken(), GL_TEXTURE_2D, size, is_overlay_candidate, false); scoped_ptr release_callback = SingleReleaseCallbackImpl::Create(base::Bind(&MailboxReleased)); return resource_provider->CreateResourceFromTextureMailbox( mailbox, std::move(release_callback)); } SolidColorDrawQuad* CreateSolidColorQuadAt( const SharedQuadState* shared_quad_state, SkColor color, RenderPass* render_pass, const gfx::Rect& rect) { SolidColorDrawQuad* quad = render_pass->CreateAndAppendDrawQuad(); quad->SetNew(shared_quad_state, rect, rect, color, false); return quad; } TextureDrawQuad* CreateCandidateQuadAt(ResourceProvider* resource_provider, const SharedQuadState* shared_quad_state, RenderPass* render_pass, const gfx::Rect& rect) { bool premultiplied_alpha = false; bool flipped = false; bool nearest_neighbor = false; float vertex_opacity[4] = {1.0f, 1.0f, 1.0f, 1.0f}; gfx::Size resource_size_in_pixels = gfx::Size(64, 64); bool is_overlay_candidate = true; ResourceId resource_id = CreateResource( resource_provider, resource_size_in_pixels, is_overlay_candidate); TextureDrawQuad* overlay_quad = render_pass->CreateAndAppendDrawQuad(); overlay_quad->SetNew(shared_quad_state, rect, rect, rect, resource_id, premultiplied_alpha, kUVTopLeft, kUVBottomRight, SK_ColorTRANSPARENT, vertex_opacity, flipped, nearest_neighbor); overlay_quad->set_resource_size_in_pixels(resource_size_in_pixels); return overlay_quad; } StreamVideoDrawQuad* CreateCandidateVideoQuadAt( ResourceProvider* resource_provider, const SharedQuadState* shared_quad_state, RenderPass* render_pass, const gfx::Rect& rect, const gfx::Transform& transform) { gfx::Size resource_size_in_pixels = gfx::Size(64, 64); bool is_overlay_candidate = true; ResourceId resource_id = CreateResource( resource_provider, resource_size_in_pixels, is_overlay_candidate); StreamVideoDrawQuad* overlay_quad = render_pass->CreateAndAppendDrawQuad(); overlay_quad->SetNew(shared_quad_state, rect, rect, rect, resource_id, resource_size_in_pixels, transform); return overlay_quad; } TextureDrawQuad* CreateFullscreenCandidateQuad( ResourceProvider* resource_provider, const SharedQuadState* shared_quad_state, RenderPass* render_pass) { return CreateCandidateQuadAt( resource_provider, shared_quad_state, render_pass, kOverlayRect); } StreamVideoDrawQuad* CreateFullscreenCandidateVideoQuad( ResourceProvider* resource_provider, const SharedQuadState* shared_quad_state, RenderPass* render_pass, const gfx::Transform& transform) { return CreateCandidateVideoQuadAt(resource_provider, shared_quad_state, render_pass, kOverlayRect, transform); } void CreateOpaqueQuadAt(ResourceProvider* resource_provider, const SharedQuadState* shared_quad_state, RenderPass* render_pass, const gfx::Rect& rect) { SolidColorDrawQuad* color_quad = render_pass->CreateAndAppendDrawQuad(); color_quad->SetNew(shared_quad_state, rect, rect, SK_ColorBLACK, false); } void CreateFullscreenOpaqueQuad(ResourceProvider* resource_provider, const SharedQuadState* shared_quad_state, RenderPass* render_pass) { CreateOpaqueQuadAt(resource_provider, shared_quad_state, render_pass, kOverlayRect); } static void CompareRenderPassLists(const RenderPassList& expected_list, const RenderPassList& actual_list) { EXPECT_EQ(expected_list.size(), actual_list.size()); for (size_t i = 0; i < actual_list.size(); ++i) { RenderPass* expected = expected_list[i].get(); RenderPass* actual = actual_list[i].get(); EXPECT_EQ(expected->id, actual->id); EXPECT_EQ(expected->output_rect, actual->output_rect); EXPECT_EQ(expected->transform_to_root_target, actual->transform_to_root_target); EXPECT_EQ(expected->damage_rect, actual->damage_rect); EXPECT_EQ(expected->has_transparent_background, actual->has_transparent_background); EXPECT_EQ(expected->shared_quad_state_list.size(), actual->shared_quad_state_list.size()); EXPECT_EQ(expected->quad_list.size(), actual->quad_list.size()); for (auto exp_iter = expected->quad_list.cbegin(), act_iter = actual->quad_list.cbegin(); exp_iter != expected->quad_list.cend(); ++exp_iter, ++act_iter) { EXPECT_EQ(exp_iter->rect.ToString(), act_iter->rect.ToString()); EXPECT_EQ(exp_iter->shared_quad_state->quad_layer_bounds.ToString(), act_iter->shared_quad_state->quad_layer_bounds.ToString()); } } } template class OverlayTest : public testing::Test { protected: void SetUp() override { provider_ = TestContextProvider::Create(); output_surface_.reset(new OverlayOutputSurface(provider_)); EXPECT_TRUE(output_surface_->BindToClient(&client_)); output_surface_->SetOverlayCandidateValidator( new OverlayCandidateValidatorType); shared_bitmap_manager_.reset(new TestSharedBitmapManager()); resource_provider_ = FakeResourceProvider::Create( output_surface_.get(), shared_bitmap_manager_.get()); overlay_processor_.reset(new OverlayProcessor(output_surface_.get())); overlay_processor_->Initialize(); } scoped_refptr provider_; scoped_ptr output_surface_; FakeOutputSurfaceClient client_; scoped_ptr shared_bitmap_manager_; scoped_ptr resource_provider_; scoped_ptr overlay_processor_; gfx::Rect damage_rect_; }; typedef OverlayTest SingleOverlayOnTopTest; typedef OverlayTest UnderlayTest; typedef OverlayTest CALayerOverlayTest; TEST(OverlayTest, NoOverlaysByDefault) { scoped_refptr provider = TestContextProvider::Create(); OverlayOutputSurface output_surface(provider); EXPECT_EQ(NULL, output_surface.GetOverlayCandidateValidator()); output_surface.SetOverlayCandidateValidator(new SingleOverlayValidator); EXPECT_TRUE(output_surface.GetOverlayCandidateValidator() != NULL); } TEST(OverlayTest, OverlaysProcessorHasStrategy) { scoped_refptr provider = TestContextProvider::Create(); OverlayOutputSurface output_surface(provider); FakeOutputSurfaceClient client; EXPECT_TRUE(output_surface.BindToClient(&client)); output_surface.SetOverlayCandidateValidator(new SingleOverlayValidator); scoped_ptr shared_bitmap_manager( new TestSharedBitmapManager()); scoped_ptr resource_provider = FakeResourceProvider::Create( &output_surface, shared_bitmap_manager.get()); scoped_ptr overlay_processor( new DefaultOverlayProcessor(&output_surface)); overlay_processor->Initialize(); EXPECT_GE(2U, overlay_processor->GetStrategyCount()); } TEST_F(SingleOverlayOnTopTest, SuccessfulOverlay) { scoped_ptr pass = CreateRenderPass(); TextureDrawQuad* original_quad = CreateFullscreenCandidateQuad(resource_provider_.get(), pass->shared_quad_state_list.back(), pass.get()); unsigned original_resource_id = original_quad->resource_id(); // Add something behind it. CreateFullscreenOpaqueQuad(resource_provider_.get(), pass->shared_quad_state_list.back(), pass.get()); CreateFullscreenOpaqueQuad(resource_provider_.get(), pass->shared_quad_state_list.back(), pass.get()); // Check for potential candidates. OverlayCandidateList candidate_list; overlay_processor_->ProcessForOverlays(resource_provider_.get(), pass.get(), &candidate_list, nullptr, &damage_rect_); ASSERT_EQ(1U, candidate_list.size()); RenderPass* main_pass = pass.get(); // Check that the quad is gone. EXPECT_EQ(2U, main_pass->quad_list.size()); const QuadList& quad_list = main_pass->quad_list; for (QuadList::ConstBackToFrontIterator it = quad_list.BackToFrontBegin(); it != quad_list.BackToFrontEnd(); ++it) { EXPECT_NE(DrawQuad::TEXTURE_CONTENT, it->material); } // Check that the right resource id got extracted. EXPECT_EQ(original_resource_id, candidate_list.back().resource_id); } TEST_F(SingleOverlayOnTopTest, DamageRect) { scoped_ptr pass = CreateRenderPass(); CreateFullscreenCandidateQuad(resource_provider_.get(), pass->shared_quad_state_list.back(), pass.get()); damage_rect_ = kOverlayRect; // Add something behind it. CreateFullscreenOpaqueQuad(resource_provider_.get(), pass->shared_quad_state_list.back(), pass.get()); CreateFullscreenOpaqueQuad(resource_provider_.get(), pass->shared_quad_state_list.back(), pass.get()); // Check for potential candidates. OverlayCandidateList candidate_list; // Primary plane. OverlayCandidate output_surface_plane; output_surface_plane.display_rect = gfx::RectF(kOverlayRect); output_surface_plane.quad_rect_in_target_space = kOverlayRect; output_surface_plane.use_output_surface_for_resource = true; output_surface_plane.overlay_handled = true; candidate_list.push_back(output_surface_plane); overlay_processor_->ProcessForOverlays(resource_provider_.get(), pass.get(), &candidate_list, nullptr, &damage_rect_); DCHECK(damage_rect_.IsEmpty()); } TEST_F(SingleOverlayOnTopTest, NoCandidates) { scoped_ptr pass = CreateRenderPass(); CreateFullscreenOpaqueQuad(resource_provider_.get(), pass->shared_quad_state_list.back(), pass.get()); CreateFullscreenOpaqueQuad(resource_provider_.get(), pass->shared_quad_state_list.back(), pass.get()); RenderPassList pass_list; pass_list.push_back(std::move(pass)); RenderPassList original_pass_list; RenderPass::CopyAll(pass_list, &original_pass_list); OverlayCandidateList candidate_list; overlay_processor_->ProcessForOverlays( resource_provider_.get(), pass_list.back().get(), &candidate_list, nullptr, &damage_rect_); EXPECT_EQ(0U, candidate_list.size()); // There should be nothing new here. CompareRenderPassLists(pass_list, original_pass_list); } TEST_F(SingleOverlayOnTopTest, OccludedCandidates) { scoped_ptr pass = CreateRenderPass(); CreateFullscreenOpaqueQuad(resource_provider_.get(), pass->shared_quad_state_list.back(), pass.get()); CreateFullscreenOpaqueQuad(resource_provider_.get(), pass->shared_quad_state_list.back(), pass.get()); CreateFullscreenCandidateQuad(resource_provider_.get(), pass->shared_quad_state_list.back(), pass.get()); RenderPassList pass_list; pass_list.push_back(std::move(pass)); RenderPassList original_pass_list; RenderPass::CopyAll(pass_list, &original_pass_list); OverlayCandidateList candidate_list; overlay_processor_->ProcessForOverlays( resource_provider_.get(), pass_list.back().get(), &candidate_list, nullptr, &damage_rect_); EXPECT_EQ(0U, candidate_list.size()); // There should be nothing new here. CompareRenderPassLists(pass_list, original_pass_list); } // Test with multiple render passes. TEST_F(SingleOverlayOnTopTest, MultipleRenderPasses) { scoped_ptr pass = CreateRenderPass(); CreateFullscreenCandidateQuad(resource_provider_.get(), pass->shared_quad_state_list.back(), pass.get()); // Add something behind it. CreateFullscreenOpaqueQuad(resource_provider_.get(), pass->shared_quad_state_list.back(), pass.get()); CreateFullscreenOpaqueQuad(resource_provider_.get(), pass->shared_quad_state_list.back(), pass.get()); // Check for potential candidates. OverlayCandidateList candidate_list; overlay_processor_->ProcessForOverlays(resource_provider_.get(), pass.get(), &candidate_list, nullptr, &damage_rect_); EXPECT_EQ(1U, candidate_list.size()); } TEST_F(SingleOverlayOnTopTest, RejectPremultipliedAlpha) { scoped_ptr pass = CreateRenderPass(); TextureDrawQuad* quad = CreateFullscreenCandidateQuad(resource_provider_.get(), pass->shared_quad_state_list.back(), pass.get()); quad->premultiplied_alpha = true; OverlayCandidateList candidate_list; overlay_processor_->ProcessForOverlays(resource_provider_.get(), pass.get(), &candidate_list, nullptr, &damage_rect_); EXPECT_EQ(0U, candidate_list.size()); } TEST_F(SingleOverlayOnTopTest, RejectBlending) { scoped_ptr pass = CreateRenderPass(); TextureDrawQuad* quad = CreateFullscreenCandidateQuad(resource_provider_.get(), pass->shared_quad_state_list.back(), pass.get()); quad->needs_blending = true; OverlayCandidateList candidate_list; overlay_processor_->ProcessForOverlays(resource_provider_.get(), pass.get(), &candidate_list, nullptr, &damage_rect_); EXPECT_EQ(0U, candidate_list.size()); } TEST_F(SingleOverlayOnTopTest, RejectBackgroundColor) { scoped_ptr pass = CreateRenderPass(); TextureDrawQuad* quad = CreateFullscreenCandidateQuad(resource_provider_.get(), pass->shared_quad_state_list.back(), pass.get()); quad->background_color = SK_ColorBLACK; OverlayCandidateList candidate_list; overlay_processor_->ProcessForOverlays(resource_provider_.get(), pass.get(), &candidate_list, nullptr, &damage_rect_); EXPECT_EQ(0U, candidate_list.size()); } TEST_F(SingleOverlayOnTopTest, RejectBlendMode) { scoped_ptr pass = CreateRenderPass(); CreateFullscreenCandidateQuad(resource_provider_.get(), pass->shared_quad_state_list.back(), pass.get()); pass->shared_quad_state_list.back()->blend_mode = SkXfermode::kScreen_Mode; OverlayCandidateList candidate_list; overlay_processor_->ProcessForOverlays(resource_provider_.get(), pass.get(), &candidate_list, nullptr, &damage_rect_); EXPECT_EQ(0U, candidate_list.size()); } TEST_F(SingleOverlayOnTopTest, RejectOpacity) { scoped_ptr pass = CreateRenderPass(); CreateFullscreenCandidateQuad(resource_provider_.get(), pass->shared_quad_state_list.back(), pass.get()); pass->shared_quad_state_list.back()->opacity = 0.5f; OverlayCandidateList candidate_list; overlay_processor_->ProcessForOverlays(resource_provider_.get(), pass.get(), &candidate_list, nullptr, &damage_rect_); EXPECT_EQ(0U, candidate_list.size()); } TEST_F(SingleOverlayOnTopTest, RejectNonAxisAlignedTransform) { scoped_ptr pass = CreateRenderPass(); CreateFullscreenCandidateQuad(resource_provider_.get(), pass->shared_quad_state_list.back(), pass.get()); pass->shared_quad_state_list.back() ->quad_to_target_transform.RotateAboutXAxis(45.f); OverlayCandidateList candidate_list; overlay_processor_->ProcessForOverlays(resource_provider_.get(), pass.get(), &candidate_list, nullptr, &damage_rect_); EXPECT_EQ(0U, candidate_list.size()); } TEST_F(SingleOverlayOnTopTest, AllowClipped) { scoped_ptr pass = CreateRenderPass(); CreateFullscreenCandidateQuad(resource_provider_.get(), pass->shared_quad_state_list.back(), pass.get()); pass->shared_quad_state_list.back()->is_clipped = true; pass->shared_quad_state_list.back()->clip_rect = kOverlayClipRect; OverlayCandidateList candidate_list; overlay_processor_->ProcessForOverlays(resource_provider_.get(), pass.get(), &candidate_list, nullptr, &damage_rect_); EXPECT_EQ(1U, candidate_list.size()); } TEST_F(SingleOverlayOnTopTest, AllowVerticalFlip) { gfx::Rect rect = kOverlayRect; rect.set_width(rect.width() / 2); rect.Offset(0, -rect.height()); scoped_ptr pass = CreateRenderPass(); CreateCandidateQuadAt(resource_provider_.get(), pass->shared_quad_state_list.back(), pass.get(), rect); pass->shared_quad_state_list.back()->quad_to_target_transform.Scale(2.0f, -1.0f); OverlayCandidateList candidate_list; overlay_processor_->ProcessForOverlays(resource_provider_.get(), pass.get(), &candidate_list, nullptr, &damage_rect_); ASSERT_EQ(1U, candidate_list.size()); EXPECT_EQ(gfx::OVERLAY_TRANSFORM_FLIP_VERTICAL, candidate_list.back().transform); } TEST_F(SingleOverlayOnTopTest, AllowHorizontalFlip) { gfx::Rect rect = kOverlayRect; rect.set_height(rect.height() / 2); rect.Offset(-rect.width(), 0); scoped_ptr pass = CreateRenderPass(); CreateCandidateQuadAt(resource_provider_.get(), pass->shared_quad_state_list.back(), pass.get(), rect); pass->shared_quad_state_list.back()->quad_to_target_transform.Scale(-1.0f, 2.0f); OverlayCandidateList candidate_list; overlay_processor_->ProcessForOverlays(resource_provider_.get(), pass.get(), &candidate_list, nullptr, &damage_rect_); ASSERT_EQ(1U, candidate_list.size()); EXPECT_EQ(gfx::OVERLAY_TRANSFORM_FLIP_HORIZONTAL, candidate_list.back().transform); } TEST_F(SingleOverlayOnTopTest, AllowPositiveScaleTransform) { gfx::Rect rect = kOverlayRect; rect.set_width(rect.width() / 2); scoped_ptr pass = CreateRenderPass(); CreateCandidateQuadAt(resource_provider_.get(), pass->shared_quad_state_list.back(), pass.get(), rect); pass->shared_quad_state_list.back()->quad_to_target_transform.Scale(2.0f, 1.0f); OverlayCandidateList candidate_list; overlay_processor_->ProcessForOverlays(resource_provider_.get(), pass.get(), &candidate_list, nullptr, &damage_rect_); EXPECT_EQ(1U, candidate_list.size()); } TEST_F(SingleOverlayOnTopTest, Allow90DegreeRotation) { gfx::Rect rect = kOverlayRect; rect.Offset(0, -rect.height()); scoped_ptr pass = CreateRenderPass(); CreateCandidateQuadAt(resource_provider_.get(), pass->shared_quad_state_list.back(), pass.get(), rect); pass->shared_quad_state_list.back() ->quad_to_target_transform.RotateAboutZAxis(90.f); OverlayCandidateList candidate_list; overlay_processor_->ProcessForOverlays(resource_provider_.get(), pass.get(), &candidate_list, nullptr, &damage_rect_); ASSERT_EQ(1U, candidate_list.size()); EXPECT_EQ(gfx::OVERLAY_TRANSFORM_ROTATE_90, candidate_list.back().transform); } TEST_F(SingleOverlayOnTopTest, Allow180DegreeRotation) { gfx::Rect rect = kOverlayRect; rect.Offset(-rect.width(), -rect.height()); scoped_ptr pass = CreateRenderPass(); CreateCandidateQuadAt(resource_provider_.get(), pass->shared_quad_state_list.back(), pass.get(), rect); pass->shared_quad_state_list.back() ->quad_to_target_transform.RotateAboutZAxis(180.f); OverlayCandidateList candidate_list; overlay_processor_->ProcessForOverlays(resource_provider_.get(), pass.get(), &candidate_list, nullptr, &damage_rect_); ASSERT_EQ(1U, candidate_list.size()); EXPECT_EQ(gfx::OVERLAY_TRANSFORM_ROTATE_180, candidate_list.back().transform); } TEST_F(SingleOverlayOnTopTest, Allow270DegreeRotation) { gfx::Rect rect = kOverlayRect; rect.Offset(-rect.width(), 0); scoped_ptr pass = CreateRenderPass(); CreateCandidateQuadAt(resource_provider_.get(), pass->shared_quad_state_list.back(), pass.get(), rect); pass->shared_quad_state_list.back() ->quad_to_target_transform.RotateAboutZAxis(270.f); OverlayCandidateList candidate_list; overlay_processor_->ProcessForOverlays(resource_provider_.get(), pass.get(), &candidate_list, nullptr, &damage_rect_); ASSERT_EQ(1U, candidate_list.size()); EXPECT_EQ(gfx::OVERLAY_TRANSFORM_ROTATE_270, candidate_list.back().transform); } TEST_F(SingleOverlayOnTopTest, AllowNotTopIfNotOccluded) { scoped_ptr pass = CreateRenderPass(); CreateOpaqueQuadAt(resource_provider_.get(), pass->shared_quad_state_list.back(), pass.get(), kOverlayTopLeftRect); CreateCandidateQuadAt(resource_provider_.get(), pass->shared_quad_state_list.back(), pass.get(), kOverlayBottomRightRect); OverlayCandidateList candidate_list; overlay_processor_->ProcessForOverlays(resource_provider_.get(), pass.get(), &candidate_list, nullptr, &damage_rect_); EXPECT_EQ(1U, candidate_list.size()); } TEST_F(SingleOverlayOnTopTest, AllowTransparentOnTop) { scoped_ptr pass = CreateRenderPass(); SharedQuadState* shared_state = pass->CreateAndAppendSharedQuadState(); shared_state->opacity = 0.f; CreateSolidColorQuadAt(shared_state, SK_ColorBLACK, pass.get(), kOverlayBottomRightRect); shared_state = pass->CreateAndAppendSharedQuadState(); shared_state->opacity = 1.f; CreateCandidateQuadAt(resource_provider_.get(), shared_state, pass.get(), kOverlayBottomRightRect); OverlayCandidateList candidate_list; overlay_processor_->ProcessForOverlays(resource_provider_.get(), pass.get(), &candidate_list, nullptr, &damage_rect_); EXPECT_EQ(1U, candidate_list.size()); } TEST_F(SingleOverlayOnTopTest, AllowTransparentColorOnTop) { scoped_ptr pass = CreateRenderPass(); CreateSolidColorQuadAt(pass->shared_quad_state_list.back(), SK_ColorTRANSPARENT, pass.get(), kOverlayBottomRightRect); CreateCandidateQuadAt(resource_provider_.get(), pass->shared_quad_state_list.back(), pass.get(), kOverlayBottomRightRect); OverlayCandidateList candidate_list; overlay_processor_->ProcessForOverlays(resource_provider_.get(), pass.get(), &candidate_list, nullptr, &damage_rect_); EXPECT_EQ(1U, candidate_list.size()); } TEST_F(SingleOverlayOnTopTest, RejectOpaqueColorOnTop) { scoped_ptr pass = CreateRenderPass(); SharedQuadState* shared_state = pass->CreateAndAppendSharedQuadState(); shared_state->opacity = 0.5f; CreateSolidColorQuadAt(shared_state, SK_ColorBLACK, pass.get(), kOverlayBottomRightRect); shared_state = pass->CreateAndAppendSharedQuadState(); shared_state->opacity = 1.f; CreateCandidateQuadAt(resource_provider_.get(), shared_state, pass.get(), kOverlayBottomRightRect); OverlayCandidateList candidate_list; overlay_processor_->ProcessForOverlays(resource_provider_.get(), pass.get(), &candidate_list, nullptr, &damage_rect_); EXPECT_EQ(0U, candidate_list.size()); } TEST_F(SingleOverlayOnTopTest, RejectTransparentColorOnTopWithoutBlending) { scoped_ptr pass = CreateRenderPass(); SharedQuadState* shared_state = pass->CreateAndAppendSharedQuadState(); CreateSolidColorQuadAt(shared_state, SK_ColorTRANSPARENT, pass.get(), kOverlayBottomRightRect)->opaque_rect = kOverlayBottomRightRect; CreateCandidateQuadAt(resource_provider_.get(), shared_state, pass.get(), kOverlayBottomRightRect); OverlayCandidateList candidate_list; overlay_processor_->ProcessForOverlays(resource_provider_.get(), pass.get(), &candidate_list, nullptr, &damage_rect_); EXPECT_EQ(0U, candidate_list.size()); } TEST_F(SingleOverlayOnTopTest, RejectVideoSwapTransform) { scoped_ptr pass = CreateRenderPass(); CreateFullscreenCandidateVideoQuad(resource_provider_.get(), pass->shared_quad_state_list.back(), pass.get(), kSwapTransform); OverlayCandidateList candidate_list; overlay_processor_->ProcessForOverlays(resource_provider_.get(), pass.get(), &candidate_list, nullptr, &damage_rect_); EXPECT_EQ(0U, candidate_list.size()); } TEST_F(SingleOverlayOnTopTest, AllowVideoXMirrorTransform) { scoped_ptr pass = CreateRenderPass(); CreateFullscreenCandidateVideoQuad(resource_provider_.get(), pass->shared_quad_state_list.back(), pass.get(), kXMirrorTransform); OverlayCandidateList candidate_list; overlay_processor_->ProcessForOverlays(resource_provider_.get(), pass.get(), &candidate_list, nullptr, &damage_rect_); EXPECT_EQ(1U, candidate_list.size()); } TEST_F(SingleOverlayOnTopTest, AllowVideoBothMirrorTransform) { scoped_ptr pass = CreateRenderPass(); CreateFullscreenCandidateVideoQuad(resource_provider_.get(), pass->shared_quad_state_list.back(), pass.get(), kBothMirrorTransform); OverlayCandidateList candidate_list; overlay_processor_->ProcessForOverlays(resource_provider_.get(), pass.get(), &candidate_list, nullptr, &damage_rect_); EXPECT_EQ(1U, candidate_list.size()); } TEST_F(SingleOverlayOnTopTest, AllowVideoNormalTransform) { scoped_ptr pass = CreateRenderPass(); CreateFullscreenCandidateVideoQuad(resource_provider_.get(), pass->shared_quad_state_list.back(), pass.get(), kNormalTransform); OverlayCandidateList candidate_list; overlay_processor_->ProcessForOverlays(resource_provider_.get(), pass.get(), &candidate_list, nullptr, &damage_rect_); EXPECT_EQ(1U, candidate_list.size()); } TEST_F(SingleOverlayOnTopTest, AllowVideoYMirrorTransform) { scoped_ptr pass = CreateRenderPass(); CreateFullscreenCandidateVideoQuad(resource_provider_.get(), pass->shared_quad_state_list.back(), pass.get(), kYMirrorTransform); OverlayCandidateList candidate_list; overlay_processor_->ProcessForOverlays(resource_provider_.get(), pass.get(), &candidate_list, nullptr, &damage_rect_); EXPECT_EQ(1U, candidate_list.size()); } TEST_F(UnderlayTest, OverlayLayerUnderMainLayer) { scoped_ptr pass = CreateRenderPass(); CreateFullscreenOpaqueQuad(resource_provider_.get(), pass->shared_quad_state_list.back(), pass.get()); CreateCandidateQuadAt(resource_provider_.get(), pass->shared_quad_state_list.back(), pass.get(), kOverlayBottomRightRect); OverlayCandidateList candidate_list; overlay_processor_->ProcessForOverlays(resource_provider_.get(), pass.get(), &candidate_list, nullptr, &damage_rect_); ASSERT_EQ(1U, candidate_list.size()); EXPECT_EQ(-1, candidate_list[0].plane_z_order); EXPECT_EQ(2U, pass->quad_list.size()); // The overlay quad should have changed to a SOLID_COLOR quad. EXPECT_EQ(pass->quad_list.back()->material, DrawQuad::SOLID_COLOR); } TEST_F(UnderlayTest, AllowOnTop) { scoped_ptr pass = CreateRenderPass(); CreateFullscreenCandidateQuad(resource_provider_.get(), pass->shared_quad_state_list.back(), pass.get()); pass->CreateAndAppendSharedQuadState()->opacity = 0.5f; CreateFullscreenOpaqueQuad(resource_provider_.get(), pass->shared_quad_state_list.back(), pass.get()); OverlayCandidateList candidate_list; overlay_processor_->ProcessForOverlays(resource_provider_.get(), pass.get(), &candidate_list, nullptr, &damage_rect_); ASSERT_EQ(1U, candidate_list.size()); EXPECT_EQ(-1, candidate_list[0].plane_z_order); // The overlay quad should have changed to a SOLID_COLOR quad. EXPECT_EQ(pass->quad_list.front()->material, DrawQuad::SOLID_COLOR); } // The first time an underlay is scheduled its damage must not be subtracted. TEST_F(UnderlayTest, InitialUnderlayDamageNotSubtracted) { scoped_ptr pass = CreateRenderPass(); CreateFullscreenCandidateQuad(resource_provider_.get(), pass->shared_quad_state_list.back(), pass.get()); damage_rect_ = kOverlayRect; OverlayCandidateList candidate_list; overlay_processor_->ProcessForOverlays(resource_provider_.get(), pass.get(), &candidate_list, nullptr, &damage_rect_); EXPECT_EQ(kOverlayRect, damage_rect_); } // An identical underlay for two frames in a row means the damage can be // subtracted the second time. TEST_F(UnderlayTest, DamageSubtractedForConsecutiveIdenticalUnderlays) { for (int i = 0; i < 2; ++i) { scoped_ptr pass = CreateRenderPass(); CreateFullscreenCandidateQuad(resource_provider_.get(), pass->shared_quad_state_list.back(), pass.get()); damage_rect_ = kOverlayRect; // Add something behind it. CreateFullscreenOpaqueQuad(resource_provider_.get(), pass->shared_quad_state_list.back(), pass.get()); OverlayCandidateList candidate_list; overlay_processor_->ProcessForOverlays(resource_provider_.get(), pass.get(), &candidate_list, nullptr, &damage_rect_); } // The second time the same overlay rect is scheduled it will be subtracted // from the damage rect. EXPECT_TRUE(damage_rect_.IsEmpty()); } // Underlay damage can only be subtracted if the previous frame's underlay // was the same rect. TEST_F(UnderlayTest, DamageNotSubtractedForNonIdenticalConsecutiveUnderlays) { gfx::Rect overlay_rects[] = {kOverlayBottomRightRect, kOverlayRect}; for (int i = 0; i < 2; ++i) { scoped_ptr pass = CreateRenderPass(); CreateCandidateQuadAt(resource_provider_.get(), pass->shared_quad_state_list.back(), pass.get(), overlay_rects[i]); damage_rect_ = overlay_rects[i]; OverlayCandidateList candidate_list; overlay_processor_->ProcessForOverlays(resource_provider_.get(), pass.get(), &candidate_list, nullptr, &damage_rect_); EXPECT_EQ(overlay_rects[i], damage_rect_); } } TEST_F(UnderlayTest, DamageNotSubtractedWhenQuadsAboveOverlap) { for (int i = 0; i < 2; ++i) { scoped_ptr pass = CreateRenderPass(); // Add an overlapping quad above the candidate. CreateFullscreenOpaqueQuad(resource_provider_.get(), pass->shared_quad_state_list.back(), pass.get()); CreateFullscreenCandidateQuad(resource_provider_.get(), pass->shared_quad_state_list.back(), pass.get()); damage_rect_ = kOverlayRect; OverlayCandidateList candidate_list; overlay_processor_->ProcessForOverlays(resource_provider_.get(), pass.get(), &candidate_list, nullptr, &damage_rect_); } EXPECT_EQ(kOverlayRect, damage_rect_); } TEST_F(UnderlayTest, DamageSubtractedWhenQuadsAboveDontOverlap) { for (int i = 0; i < 2; ++i) { scoped_ptr pass = CreateRenderPass(); // Add a non-overlapping quad above the candidate. CreateOpaqueQuadAt(resource_provider_.get(), pass->shared_quad_state_list.back(), pass.get(), kOverlayTopLeftRect); CreateCandidateQuadAt(resource_provider_.get(), pass->shared_quad_state_list.back(), pass.get(), kOverlayBottomRightRect); damage_rect_ = kOverlayBottomRightRect; OverlayCandidateList candidate_list; overlay_processor_->ProcessForOverlays(resource_provider_.get(), pass.get(), &candidate_list, nullptr, &damage_rect_); } EXPECT_TRUE(damage_rect_.IsEmpty()); } OverlayCandidateList BackbufferOverlayList(const RenderPass* root_render_pass) { OverlayCandidateList list; OverlayCandidate output_surface_plane; output_surface_plane.display_rect = gfx::RectF(root_render_pass->output_rect); output_surface_plane.quad_rect_in_target_space = root_render_pass->output_rect; output_surface_plane.use_output_surface_for_resource = true; output_surface_plane.overlay_handled = true; list.push_back(output_surface_plane); return list; } TEST_F(CALayerOverlayTest, AllowNonAxisAlignedTransform) { scoped_ptr pass = CreateRenderPass(); CreateFullscreenCandidateQuad(resource_provider_.get(), pass->shared_quad_state_list.back(), pass.get()); pass->shared_quad_state_list.back() ->quad_to_target_transform.RotateAboutZAxis(45.f); gfx::Rect damage_rect; CALayerOverlayList ca_layer_list; OverlayCandidateList overlay_list(BackbufferOverlayList(pass.get())); overlay_processor_->ProcessForOverlays(resource_provider_.get(), pass.get(), &overlay_list, &ca_layer_list, &damage_rect); EXPECT_EQ(0U, pass->quad_list.size()); EXPECT_EQ(0U, overlay_list.size()); EXPECT_EQ(1U, ca_layer_list.size()); EXPECT_EQ(0U, output_surface_->bind_framebuffer_count()); } TEST_F(CALayerOverlayTest, ThreeDTransform) { scoped_ptr pass = CreateRenderPass(); CreateFullscreenCandidateQuad(resource_provider_.get(), pass->shared_quad_state_list.back(), pass.get()); pass->shared_quad_state_list.back() ->quad_to_target_transform.RotateAboutXAxis(45.f); gfx::Rect damage_rect; CALayerOverlayList ca_layer_list; OverlayCandidateList overlay_list(BackbufferOverlayList(pass.get())); overlay_processor_->ProcessForOverlays(resource_provider_.get(), pass.get(), &overlay_list, &ca_layer_list, &damage_rect); EXPECT_EQ(0U, overlay_list.size()); EXPECT_EQ(1U, ca_layer_list.size()); gfx::Transform expected_transform; expected_transform.RotateAboutXAxis(45.f); gfx::Transform actual_transform(ca_layer_list.back().transform); EXPECT_EQ(expected_transform.ToString(), actual_transform.ToString()); EXPECT_EQ(0U, output_surface_->bind_framebuffer_count()); } TEST_F(CALayerOverlayTest, AllowContainingClip) { scoped_ptr pass = CreateRenderPass(); CreateFullscreenCandidateQuad(resource_provider_.get(), pass->shared_quad_state_list.back(), pass.get()); pass->shared_quad_state_list.back()->is_clipped = true; pass->shared_quad_state_list.back()->clip_rect = kOverlayRect; gfx::Rect damage_rect; CALayerOverlayList ca_layer_list; OverlayCandidateList overlay_list(BackbufferOverlayList(pass.get())); overlay_processor_->ProcessForOverlays(resource_provider_.get(), pass.get(), &overlay_list, &ca_layer_list, &damage_rect); EXPECT_EQ(0U, pass->quad_list.size()); EXPECT_EQ(0U, overlay_list.size()); EXPECT_EQ(1U, ca_layer_list.size()); EXPECT_EQ(0U, output_surface_->bind_framebuffer_count()); } TEST_F(CALayerOverlayTest, NontrivialClip) { scoped_ptr pass = CreateRenderPass(); CreateFullscreenCandidateQuad(resource_provider_.get(), pass->shared_quad_state_list.back(), pass.get()); pass->shared_quad_state_list.back()->is_clipped = true; pass->shared_quad_state_list.back()->clip_rect = gfx::Rect(64, 64, 128, 128); gfx::Rect damage_rect; CALayerOverlayList ca_layer_list; OverlayCandidateList overlay_list(BackbufferOverlayList(pass.get())); overlay_processor_->ProcessForOverlays(resource_provider_.get(), pass.get(), &overlay_list, &ca_layer_list, &damage_rect); EXPECT_EQ(0U, pass->quad_list.size()); EXPECT_EQ(0U, overlay_list.size()); EXPECT_EQ(1U, ca_layer_list.size()); EXPECT_TRUE(ca_layer_list.back().is_clipped); EXPECT_EQ(gfx::RectF(64, 64, 128, 128), ca_layer_list.back().clip_rect); EXPECT_EQ(0U, output_surface_->bind_framebuffer_count()); } TEST_F(CALayerOverlayTest, SkipTransparent) { scoped_ptr pass = CreateRenderPass(); CreateFullscreenCandidateQuad(resource_provider_.get(), pass->shared_quad_state_list.back(), pass.get()); pass->shared_quad_state_list.back()->opacity = 0; gfx::Rect damage_rect; CALayerOverlayList ca_layer_list; OverlayCandidateList overlay_list(BackbufferOverlayList(pass.get())); overlay_processor_->ProcessForOverlays(resource_provider_.get(), pass.get(), &overlay_list, &ca_layer_list, &damage_rect); EXPECT_EQ(0U, pass->quad_list.size()); EXPECT_EQ(0U, overlay_list.size()); EXPECT_EQ(0U, ca_layer_list.size()); EXPECT_EQ(0U, output_surface_->bind_framebuffer_count()); } class OverlayInfoRendererGL : public GLRenderer { public: OverlayInfoRendererGL(RendererClient* client, const RendererSettings* settings, OutputSurface* output_surface, ResourceProvider* resource_provider) : GLRenderer(client, settings, output_surface, resource_provider, NULL, 0), expect_overlays_(false) {} MOCK_METHOD3(DoDrawQuad, void(DrawingFrame* frame, const DrawQuad* quad, const gfx::QuadF* draw_region)); using GLRenderer::BeginDrawingFrame; void FinishDrawingFrame(DrawingFrame* frame) override { GLRenderer::FinishDrawingFrame(frame); if (!expect_overlays_) { EXPECT_EQ(0U, frame->overlay_list.size()); return; } ASSERT_EQ(2U, frame->overlay_list.size()); EXPECT_GE(frame->overlay_list.back().resource_id, 0U); } void set_expect_overlays(bool expect_overlays) { expect_overlays_ = expect_overlays; } private: bool expect_overlays_; }; class FakeRendererClient : public RendererClient { public: // RendererClient methods. void SetFullRootLayerDamage() override {} }; class MockOverlayScheduler { public: MOCK_METHOD5(Schedule, void(int plane_z_order, gfx::OverlayTransform plane_transform, unsigned overlay_texture_id, const gfx::Rect& display_bounds, const gfx::RectF& uv_rect)); }; class GLRendererWithOverlaysTest : public testing::Test { protected: GLRendererWithOverlaysTest() { provider_ = TestContextProvider::Create(); output_surface_.reset(new OverlayOutputSurface(provider_)); CHECK(output_surface_->BindToClient(&output_surface_client_)); resource_provider_ = FakeResourceProvider::Create(output_surface_.get(), nullptr); provider_->support()->SetScheduleOverlayPlaneCallback(base::Bind( &MockOverlayScheduler::Schedule, base::Unretained(&scheduler_))); } void Init(bool use_validator) { if (use_validator) output_surface_->SetOverlayCandidateValidator(new SingleOverlayValidator); renderer_ = make_scoped_ptr(new OverlayInfoRendererGL(&renderer_client_, &settings_, output_surface_.get(), resource_provider_.get())); } void SwapBuffers() { renderer_->SwapBuffers(CompositorFrameMetadata()); output_surface_->OnSwapBuffersComplete(); renderer_->SwapBuffersComplete(); } void SwapBuffersWithoutComplete() { renderer_->SwapBuffers(CompositorFrameMetadata()); } void SwapBuffersComplete() { output_surface_->OnSwapBuffersComplete(); renderer_->SwapBuffersComplete(); } RendererSettings settings_; FakeOutputSurfaceClient output_surface_client_; scoped_ptr output_surface_; FakeRendererClient renderer_client_; scoped_ptr resource_provider_; scoped_ptr renderer_; scoped_refptr provider_; MockOverlayScheduler scheduler_; }; TEST_F(GLRendererWithOverlaysTest, OverlayQuadNotDrawn) { bool use_validator = true; Init(use_validator); renderer_->set_expect_overlays(true); gfx::Rect viewport_rect(16, 16); scoped_ptr pass = CreateRenderPass(); CreateCandidateQuadAt(resource_provider_.get(), pass->shared_quad_state_list.back(), pass.get(), kOverlayBottomRightRect); CreateFullscreenOpaqueQuad(resource_provider_.get(), pass->shared_quad_state_list.back(), pass.get()); CreateFullscreenOpaqueQuad(resource_provider_.get(), pass->shared_quad_state_list.back(), pass.get()); RenderPassList pass_list; pass_list.push_back(std::move(pass)); // Candidate pass was taken out and extra skipped pass added, // so only draw 2 quads. EXPECT_CALL(*renderer_, DoDrawQuad(_, _, _)).Times(2); EXPECT_CALL(scheduler_, Schedule(0, gfx::OVERLAY_TRANSFORM_NONE, _, gfx::Rect(kDisplaySize), gfx::RectF(0, 0, 1, 1))) .Times(1); EXPECT_CALL(scheduler_, Schedule(1, gfx::OVERLAY_TRANSFORM_NONE, _, kOverlayBottomRightRect, BoundingRect(kUVTopLeft, kUVBottomRight))) .Times(1); renderer_->DrawFrame(&pass_list, 1.f, viewport_rect, viewport_rect, false); EXPECT_EQ(1U, output_surface_->bind_framebuffer_count()); SwapBuffers(); Mock::VerifyAndClearExpectations(renderer_.get()); Mock::VerifyAndClearExpectations(&scheduler_); } TEST_F(GLRendererWithOverlaysTest, OccludedQuadInUnderlay) { bool use_validator = true; Init(use_validator); renderer_->set_expect_overlays(true); gfx::Rect viewport_rect(16, 16); scoped_ptr pass = CreateRenderPass(); CreateFullscreenOpaqueQuad(resource_provider_.get(), pass->shared_quad_state_list.back(), pass.get()); CreateFullscreenOpaqueQuad(resource_provider_.get(), pass->shared_quad_state_list.back(), pass.get()); CreateFullscreenCandidateQuad(resource_provider_.get(), pass->shared_quad_state_list.back(), pass.get()); RenderPassList pass_list; pass_list.push_back(std::move(pass)); // Candidate quad should fail to be overlaid on top because of occlusion. // Expect to be replaced with transparent hole quad and placed in underlay. EXPECT_CALL(*renderer_, DoDrawQuad(_, _, _)).Times(3); EXPECT_CALL(scheduler_, Schedule(0, gfx::OVERLAY_TRANSFORM_NONE, _, gfx::Rect(kDisplaySize), gfx::RectF(0, 0, 1, 1))) .Times(1); EXPECT_CALL(scheduler_, Schedule(-1, gfx::OVERLAY_TRANSFORM_NONE, _, kOverlayRect, BoundingRect(kUVTopLeft, kUVBottomRight))) .Times(1); renderer_->DrawFrame(&pass_list, 1.f, viewport_rect, viewport_rect, false); EXPECT_EQ(1U, output_surface_->bind_framebuffer_count()); SwapBuffers(); Mock::VerifyAndClearExpectations(renderer_.get()); Mock::VerifyAndClearExpectations(&scheduler_); } TEST_F(GLRendererWithOverlaysTest, NoValidatorNoOverlay) { bool use_validator = false; Init(use_validator); renderer_->set_expect_overlays(false); gfx::Rect viewport_rect(16, 16); scoped_ptr pass = CreateRenderPass(); CreateFullscreenCandidateQuad(resource_provider_.get(), pass->shared_quad_state_list.back(), pass.get()); CreateFullscreenOpaqueQuad(resource_provider_.get(), pass->shared_quad_state_list.back(), pass.get()); CreateFullscreenOpaqueQuad(resource_provider_.get(), pass->shared_quad_state_list.back(), pass.get()); RenderPassList pass_list; pass_list.push_back(std::move(pass)); // Should not see the primary surface's overlay. output_surface_->set_is_displayed_as_overlay_plane(false); EXPECT_CALL(*renderer_, DoDrawQuad(_, _, _)).Times(3); EXPECT_CALL(scheduler_, Schedule(_, _, _, _, _)).Times(0); renderer_->DrawFrame(&pass_list, 1.f, viewport_rect, viewport_rect, false); EXPECT_EQ(1U, output_surface_->bind_framebuffer_count()); SwapBuffers(); Mock::VerifyAndClearExpectations(renderer_.get()); Mock::VerifyAndClearExpectations(&scheduler_); } // GLRenderer skips drawing occluded quads when partial swap is enabled. TEST_F(GLRendererWithOverlaysTest, OccludedQuadNotDrawnWhenPartialSwapEnabled) { provider_->TestContext3d()->set_have_post_sub_buffer(true); settings_.partial_swap_enabled = true; bool use_validator = true; Init(use_validator); renderer_->set_expect_overlays(true); gfx::Rect viewport_rect(16, 16); scoped_ptr pass = CreateRenderPass(); CreateFullscreenCandidateQuad(resource_provider_.get(), pass->shared_quad_state_list.back(), pass.get()); CreateFullscreenOpaqueQuad(resource_provider_.get(), pass->shared_quad_state_list.back(), pass.get()); CreateFullscreenOpaqueQuad(resource_provider_.get(), pass->shared_quad_state_list.back(), pass.get()); RenderPassList pass_list; pass_list.push_back(std::move(pass)); output_surface_->set_is_displayed_as_overlay_plane(true); EXPECT_CALL(*renderer_, DoDrawQuad(_, _, _)).Times(0); EXPECT_CALL(scheduler_, Schedule(_, _, _, _, _)).Times(2); renderer_->DrawFrame(&pass_list, 1.f, viewport_rect, viewport_rect, false); EXPECT_EQ(1U, output_surface_->bind_framebuffer_count()); SwapBuffers(); Mock::VerifyAndClearExpectations(renderer_.get()); Mock::VerifyAndClearExpectations(&scheduler_); } // GLRenderer skips drawing occluded quads when empty swap is enabled. TEST_F(GLRendererWithOverlaysTest, OccludedQuadNotDrawnWhenEmptySwapAllowed) { provider_->TestContext3d()->set_have_commit_overlay_planes(true); bool use_validator = true; Init(use_validator); renderer_->set_expect_overlays(true); gfx::Rect viewport_rect(16, 16); scoped_ptr pass = CreateRenderPass(); CreateFullscreenCandidateQuad(resource_provider_.get(), pass->shared_quad_state_list.back(), pass.get()); CreateFullscreenOpaqueQuad(resource_provider_.get(), pass->shared_quad_state_list.back(), pass.get()); CreateFullscreenOpaqueQuad(resource_provider_.get(), pass->shared_quad_state_list.back(), pass.get()); RenderPassList pass_list; pass_list.push_back(std::move(pass)); output_surface_->set_is_displayed_as_overlay_plane(true); EXPECT_CALL(*renderer_, DoDrawQuad(_, _, _)).Times(0); EXPECT_CALL(scheduler_, Schedule(_, _, _, _, _)).Times(2); renderer_->DrawFrame(&pass_list, 1.f, viewport_rect, viewport_rect, false); EXPECT_EQ(1U, output_surface_->bind_framebuffer_count()); SwapBuffers(); Mock::VerifyAndClearExpectations(renderer_.get()); Mock::VerifyAndClearExpectations(&scheduler_); } TEST_F(GLRendererWithOverlaysTest, ResourcesExportedAndReturnedWithDelay) { bool use_validator = true; Init(use_validator); renderer_->set_expect_overlays(true); ResourceId resource1 = CreateResource(resource_provider_.get(), gfx::Size(32, 32), true); ResourceId resource2 = CreateResource(resource_provider_.get(), gfx::Size(32, 32), true); ResourceId resource3 = CreateResource(resource_provider_.get(), gfx::Size(32, 32), true); scoped_ptr pass = CreateRenderPass(); RenderPassList pass_list; pass_list.push_back(std::move(pass)); DirectRenderer::DrawingFrame frame1; frame1.render_passes_in_draw_order = &pass_list; frame1.overlay_list.resize(2); frame1.overlay_list.front().use_output_surface_for_resource = true; OverlayCandidate& overlay1 = frame1.overlay_list.back(); overlay1.resource_id = resource1; overlay1.plane_z_order = 1; DirectRenderer::DrawingFrame frame2; frame2.render_passes_in_draw_order = &pass_list; frame2.overlay_list.resize(2); frame2.overlay_list.front().use_output_surface_for_resource = true; OverlayCandidate& overlay2 = frame2.overlay_list.back(); overlay2.resource_id = resource2; overlay2.plane_z_order = 1; DirectRenderer::DrawingFrame frame3; frame3.render_passes_in_draw_order = &pass_list; frame3.overlay_list.resize(2); frame3.overlay_list.front().use_output_surface_for_resource = true; OverlayCandidate& overlay3 = frame3.overlay_list.back(); overlay3.resource_id = resource3; overlay3.plane_z_order = 1; EXPECT_CALL(scheduler_, Schedule(_, _, _, _, _)).Times(2); renderer_->BeginDrawingFrame(&frame1); renderer_->FinishDrawingFrame(&frame1); EXPECT_TRUE(resource_provider_->InUseByConsumer(resource1)); EXPECT_FALSE(resource_provider_->InUseByConsumer(resource2)); SwapBuffers(); Mock::VerifyAndClearExpectations(&scheduler_); EXPECT_CALL(scheduler_, Schedule(_, _, _, _, _)).Times(2); renderer_->BeginDrawingFrame(&frame2); renderer_->FinishDrawingFrame(&frame2); EXPECT_TRUE(resource_provider_->InUseByConsumer(resource1)); EXPECT_TRUE(resource_provider_->InUseByConsumer(resource2)); SwapBuffers(); EXPECT_TRUE(resource_provider_->InUseByConsumer(resource1)); EXPECT_TRUE(resource_provider_->InUseByConsumer(resource2)); Mock::VerifyAndClearExpectations(&scheduler_); EXPECT_CALL(scheduler_, Schedule(_, _, _, _, _)).Times(2); renderer_->BeginDrawingFrame(&frame3); renderer_->FinishDrawingFrame(&frame3); EXPECT_TRUE(resource_provider_->InUseByConsumer(resource1)); EXPECT_TRUE(resource_provider_->InUseByConsumer(resource2)); EXPECT_TRUE(resource_provider_->InUseByConsumer(resource3)); SwapBuffers(); EXPECT_FALSE(resource_provider_->InUseByConsumer(resource1)); EXPECT_TRUE(resource_provider_->InUseByConsumer(resource2)); EXPECT_TRUE(resource_provider_->InUseByConsumer(resource3)); Mock::VerifyAndClearExpectations(&scheduler_); // No overlays, release the resource. EXPECT_CALL(scheduler_, Schedule(_, _, _, _, _)).Times(0); DirectRenderer::DrawingFrame frame_no_overlays; frame_no_overlays.render_passes_in_draw_order = &pass_list; renderer_->set_expect_overlays(false); renderer_->BeginDrawingFrame(&frame_no_overlays); renderer_->FinishDrawingFrame(&frame_no_overlays); EXPECT_FALSE(resource_provider_->InUseByConsumer(resource1)); EXPECT_TRUE(resource_provider_->InUseByConsumer(resource2)); EXPECT_TRUE(resource_provider_->InUseByConsumer(resource3)); SwapBuffers(); EXPECT_FALSE(resource_provider_->InUseByConsumer(resource1)); EXPECT_FALSE(resource_provider_->InUseByConsumer(resource2)); EXPECT_TRUE(resource_provider_->InUseByConsumer(resource3)); Mock::VerifyAndClearExpectations(&scheduler_); // Use the same buffer twice. renderer_->set_expect_overlays(true); EXPECT_CALL(scheduler_, Schedule(_, _, _, _, _)).Times(2); renderer_->BeginDrawingFrame(&frame1); renderer_->FinishDrawingFrame(&frame1); EXPECT_TRUE(resource_provider_->InUseByConsumer(resource1)); SwapBuffers(); Mock::VerifyAndClearExpectations(&scheduler_); EXPECT_CALL(scheduler_, Schedule(_, _, _, _, _)).Times(2); renderer_->BeginDrawingFrame(&frame1); renderer_->FinishDrawingFrame(&frame1); EXPECT_TRUE(resource_provider_->InUseByConsumer(resource1)); SwapBuffers(); EXPECT_TRUE(resource_provider_->InUseByConsumer(resource1)); Mock::VerifyAndClearExpectations(&scheduler_); EXPECT_CALL(scheduler_, Schedule(_, _, _, _, _)).Times(0); renderer_->set_expect_overlays(false); renderer_->BeginDrawingFrame(&frame_no_overlays); renderer_->FinishDrawingFrame(&frame_no_overlays); EXPECT_TRUE(resource_provider_->InUseByConsumer(resource1)); SwapBuffers(); EXPECT_TRUE(resource_provider_->InUseByConsumer(resource1)); Mock::VerifyAndClearExpectations(&scheduler_); EXPECT_CALL(scheduler_, Schedule(_, _, _, _, _)).Times(0); renderer_->set_expect_overlays(false); renderer_->BeginDrawingFrame(&frame_no_overlays); renderer_->FinishDrawingFrame(&frame_no_overlays); EXPECT_TRUE(resource_provider_->InUseByConsumer(resource1)); SwapBuffers(); EXPECT_FALSE(resource_provider_->InUseByConsumer(resource1)); Mock::VerifyAndClearExpectations(&scheduler_); } TEST_F(GLRendererWithOverlaysTest, ResourcesExportedAndReturnedAtSwapComplete) { bool use_validator = true; settings_.release_overlay_resources_on_swap_complete = true; Init(use_validator); renderer_->set_expect_overlays(true); ResourceId resource1 = CreateResource(resource_provider_.get(), gfx::Size(32, 32), true); ResourceId resource2 = CreateResource(resource_provider_.get(), gfx::Size(32, 32), true); ResourceId resource3 = CreateResource(resource_provider_.get(), gfx::Size(32, 32), true); scoped_ptr pass = CreateRenderPass(); RenderPassList pass_list; pass_list.push_back(std::move(pass)); DirectRenderer::DrawingFrame frame1; frame1.render_passes_in_draw_order = &pass_list; frame1.overlay_list.resize(2); frame1.overlay_list.front().use_output_surface_for_resource = true; OverlayCandidate& overlay1 = frame1.overlay_list.back(); overlay1.resource_id = resource1; overlay1.plane_z_order = 1; DirectRenderer::DrawingFrame frame2; frame2.render_passes_in_draw_order = &pass_list; frame2.overlay_list.resize(2); frame2.overlay_list.front().use_output_surface_for_resource = true; OverlayCandidate& overlay2 = frame2.overlay_list.back(); overlay2.resource_id = resource2; overlay2.plane_z_order = 1; DirectRenderer::DrawingFrame frame3; frame3.render_passes_in_draw_order = &pass_list; frame3.overlay_list.resize(2); frame3.overlay_list.front().use_output_surface_for_resource = true; OverlayCandidate& overlay3 = frame3.overlay_list.back(); overlay3.resource_id = resource3; overlay3.plane_z_order = 1; // First frame, with no swap completion. EXPECT_CALL(scheduler_, Schedule(_, _, _, _, _)).Times(2); renderer_->BeginDrawingFrame(&frame1); renderer_->FinishDrawingFrame(&frame1); EXPECT_TRUE(resource_provider_->InUseByConsumer(resource1)); SwapBuffersWithoutComplete(); EXPECT_TRUE(resource_provider_->InUseByConsumer(resource1)); Mock::VerifyAndClearExpectations(&scheduler_); // Second frame, with no swap completion. EXPECT_CALL(scheduler_, Schedule(_, _, _, _, _)).Times(2); renderer_->BeginDrawingFrame(&frame2); renderer_->FinishDrawingFrame(&frame2); EXPECT_TRUE(resource_provider_->InUseByConsumer(resource1)); EXPECT_TRUE(resource_provider_->InUseByConsumer(resource2)); SwapBuffersWithoutComplete(); EXPECT_TRUE(resource_provider_->InUseByConsumer(resource1)); EXPECT_TRUE(resource_provider_->InUseByConsumer(resource2)); Mock::VerifyAndClearExpectations(&scheduler_); // Third frame, still with no swap completion (where the resources would // otherwise have been released). EXPECT_CALL(scheduler_, Schedule(_, _, _, _, _)).Times(2); renderer_->BeginDrawingFrame(&frame3); renderer_->FinishDrawingFrame(&frame3); EXPECT_TRUE(resource_provider_->InUseByConsumer(resource1)); EXPECT_TRUE(resource_provider_->InUseByConsumer(resource2)); EXPECT_TRUE(resource_provider_->InUseByConsumer(resource3)); SwapBuffersWithoutComplete(); EXPECT_TRUE(resource_provider_->InUseByConsumer(resource1)); EXPECT_TRUE(resource_provider_->InUseByConsumer(resource2)); EXPECT_TRUE(resource_provider_->InUseByConsumer(resource3)); Mock::VerifyAndClearExpectations(&scheduler_); // This completion corresponds to the first frame. SwapBuffersComplete(); EXPECT_FALSE(resource_provider_->InUseByConsumer(resource1)); EXPECT_TRUE(resource_provider_->InUseByConsumer(resource2)); EXPECT_TRUE(resource_provider_->InUseByConsumer(resource3)); // This completion corresponds to the second frame. SwapBuffersComplete(); EXPECT_FALSE(resource_provider_->InUseByConsumer(resource1)); EXPECT_FALSE(resource_provider_->InUseByConsumer(resource2)); EXPECT_TRUE(resource_provider_->InUseByConsumer(resource3)); // This completion corresponds to the third frame. SwapBuffersComplete(); EXPECT_FALSE(resource_provider_->InUseByConsumer(resource1)); EXPECT_FALSE(resource_provider_->InUseByConsumer(resource2)); EXPECT_FALSE(resource_provider_->InUseByConsumer(resource3)); } } // namespace } // namespace cc