|  | // 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 "cc/base/scoped_ptr_vector.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/quads/checkerboard_draw_quad.h" | 
|  | #include "cc/quads/render_pass.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/geometry_test_utils.h" | 
|  | #include "cc/test/test_context_provider.h" | 
|  | #include "cc/test/test_shared_bitmap_manager.h" | 
|  | #include "testing/gmock/include/gmock/gmock.h" | 
|  | #include "testing/gtest/include/gtest/gtest.h" | 
|  |  | 
|  | using testing::_; | 
|  | using testing::Mock; | 
|  |  | 
|  | namespace cc { | 
|  | namespace { | 
|  |  | 
|  | 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::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(unsigned sync_point, | 
|  | bool lost_resource, | 
|  | BlockingTaskRunner* main_thread_task_runner) { | 
|  | } | 
|  |  | 
|  | class SingleOverlayValidator : public OverlayCandidateValidator { | 
|  | public: | 
|  | void CheckOverlaySupport(OverlayCandidateList* surfaces) override; | 
|  | }; | 
|  |  | 
|  | void SingleOverlayValidator::CheckOverlaySupport( | 
|  | OverlayCandidateList* surfaces) { | 
|  | ASSERT_EQ(2U, surfaces->size()); | 
|  |  | 
|  | OverlayCandidate& candidate = surfaces->back(); | 
|  | if (candidate.display_rect.width() == 64) | 
|  | EXPECT_EQ(kOverlayBottomRightRect, candidate.display_rect); | 
|  | else | 
|  | EXPECT_EQ(kOverlayRect, candidate.display_rect); | 
|  | EXPECT_EQ(BoundingRect(kUVTopLeft, kUVBottomRight).ToString(), | 
|  | candidate.uv_rect.ToString()); | 
|  | candidate.overlay_handled = true; | 
|  | } | 
|  |  | 
|  | class SingleOverlayProcessor : public OverlayProcessor { | 
|  | public: | 
|  | SingleOverlayProcessor(OutputSurface* surface, | 
|  | ResourceProvider* resource_provider); | 
|  | // Virtual to allow testing different strategies. | 
|  | void Initialize() override; | 
|  | }; | 
|  |  | 
|  | SingleOverlayProcessor::SingleOverlayProcessor( | 
|  | OutputSurface* surface, | 
|  | ResourceProvider* resource_provider) | 
|  | : OverlayProcessor(surface, resource_provider) { | 
|  | EXPECT_EQ(surface, surface_); | 
|  | EXPECT_EQ(resource_provider, resource_provider_); | 
|  | } | 
|  |  | 
|  | void SingleOverlayProcessor::Initialize() { | 
|  | OverlayCandidateValidator* candidates = | 
|  | surface_->overlay_candidate_validator(); | 
|  | ASSERT_TRUE(candidates != NULL); | 
|  | strategies_.push_back(scoped_ptr<Strategy>( | 
|  | new OverlayStrategySingleOnTop(candidates, resource_provider_))); | 
|  | } | 
|  |  | 
|  | class DefaultOverlayProcessor : public OverlayProcessor { | 
|  | public: | 
|  | DefaultOverlayProcessor(OutputSurface* surface, | 
|  | ResourceProvider* resource_provider); | 
|  | size_t GetStrategyCount(); | 
|  | }; | 
|  |  | 
|  | DefaultOverlayProcessor::DefaultOverlayProcessor( | 
|  | OutputSurface* surface, | 
|  | ResourceProvider* resource_provider) | 
|  | : OverlayProcessor(surface, resource_provider) {} | 
|  |  | 
|  | size_t DefaultOverlayProcessor::GetStrategyCount() { | 
|  | return strategies_.size(); | 
|  | } | 
|  |  | 
|  | class OverlayOutputSurface : public OutputSurface { | 
|  | public: | 
|  | explicit OverlayOutputSurface(scoped_refptr<ContextProvider> context_provider) | 
|  | : OutputSurface(context_provider) {} | 
|  |  | 
|  | // OutputSurface implementation | 
|  | void SwapBuffers(CompositorFrame* frame) override; | 
|  |  | 
|  | void InitWithSingleOverlayValidator() { | 
|  | overlay_candidate_validator_.reset(new SingleOverlayValidator); | 
|  | } | 
|  | }; | 
|  |  | 
|  | void OverlayOutputSurface::SwapBuffers(CompositorFrame* frame) { | 
|  | client_->DidSwapBuffers(); | 
|  | client_->DidSwapBuffersComplete(); | 
|  | } | 
|  |  | 
|  | scoped_ptr<RenderPass> CreateRenderPass() { | 
|  | RenderPassId id(1, 0); | 
|  | gfx::Rect output_rect(0, 0, 256, 256); | 
|  | bool has_transparent_background = true; | 
|  |  | 
|  | scoped_ptr<RenderPass> 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.Pass(); | 
|  | } | 
|  |  | 
|  | ResourceProvider::ResourceId CreateResource( | 
|  | ResourceProvider* resource_provider) { | 
|  | unsigned sync_point = 0; | 
|  | TextureMailbox mailbox = | 
|  | TextureMailbox(gpu::Mailbox::Generate(), GL_TEXTURE_2D, sync_point); | 
|  | mailbox.set_allow_overlay(true); | 
|  | scoped_ptr<SingleReleaseCallbackImpl> release_callback = | 
|  | SingleReleaseCallbackImpl::Create(base::Bind(&MailboxReleased)); | 
|  |  | 
|  | return resource_provider->CreateResourceFromTextureMailbox( | 
|  | mailbox, release_callback.Pass()); | 
|  | } | 
|  |  | 
|  | SolidColorDrawQuad* CreateSolidColorQuadAt( | 
|  | const SharedQuadState* shared_quad_state, | 
|  | SkColor color, | 
|  | RenderPass* render_pass, | 
|  | const gfx::Rect& rect) { | 
|  | SolidColorDrawQuad* quad = | 
|  | render_pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>(); | 
|  | 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) { | 
|  | ResourceProvider::ResourceId resource_id = CreateResource(resource_provider); | 
|  | bool premultiplied_alpha = false; | 
|  | bool flipped = false; | 
|  | bool nearest_neighbor = false; | 
|  | float vertex_opacity[4] = {1.0f, 1.0f, 1.0f, 1.0f}; | 
|  |  | 
|  | TextureDrawQuad* overlay_quad = | 
|  | render_pass->CreateAndAppendDrawQuad<TextureDrawQuad>(); | 
|  | overlay_quad->SetNew(shared_quad_state, | 
|  | rect, | 
|  | rect, | 
|  | rect, | 
|  | resource_id, | 
|  | premultiplied_alpha, | 
|  | kUVTopLeft, | 
|  | kUVBottomRight, | 
|  | SK_ColorTRANSPARENT, | 
|  | vertex_opacity, | 
|  | flipped, | 
|  | nearest_neighbor); | 
|  |  | 
|  | return overlay_quad; | 
|  | } | 
|  |  | 
|  | StreamVideoDrawQuad* CreateCandidateVideoQuadAt( | 
|  | ResourceProvider* resource_provider, | 
|  | const SharedQuadState* shared_quad_state, | 
|  | RenderPass* render_pass, | 
|  | const gfx::Rect& rect, | 
|  | const gfx::Transform& transform) { | 
|  | ResourceProvider::ResourceId resource_id = CreateResource(resource_provider); | 
|  |  | 
|  | StreamVideoDrawQuad* overlay_quad = | 
|  | render_pass->CreateAndAppendDrawQuad<StreamVideoDrawQuad>(); | 
|  | overlay_quad->SetNew(shared_quad_state, rect, rect, rect, resource_id, | 
|  | 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 CreateCheckeredQuadAt(ResourceProvider* resource_provider, | 
|  | const SharedQuadState* shared_quad_state, | 
|  | RenderPass* render_pass, | 
|  | const gfx::Rect& rect) { | 
|  | CheckerboardDrawQuad* checkerboard_quad = | 
|  | render_pass->CreateAndAppendDrawQuad<CheckerboardDrawQuad>(); | 
|  | checkerboard_quad->SetNew(shared_quad_state, rect, rect, SkColor(), 1.f); | 
|  | } | 
|  |  | 
|  | void CreateFullscreenCheckeredQuad(ResourceProvider* resource_provider, | 
|  | const SharedQuadState* shared_quad_state, | 
|  | RenderPass* render_pass) { | 
|  | CreateCheckeredQuadAt( | 
|  | 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]; | 
|  | RenderPass* actual = actual_list[i]; | 
|  |  | 
|  | 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->content_bounds.ToString(), | 
|  | act_iter->shared_quad_state->content_bounds.ToString()); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | TEST(OverlayTest, NoOverlaysByDefault) { | 
|  | scoped_refptr<TestContextProvider> provider = TestContextProvider::Create(); | 
|  | OverlayOutputSurface output_surface(provider); | 
|  | EXPECT_EQ(NULL, output_surface.overlay_candidate_validator()); | 
|  |  | 
|  | output_surface.InitWithSingleOverlayValidator(); | 
|  | EXPECT_TRUE(output_surface.overlay_candidate_validator() != NULL); | 
|  | } | 
|  |  | 
|  | TEST(OverlayTest, OverlaysProcessorHasStrategy) { | 
|  | scoped_refptr<TestContextProvider> provider = TestContextProvider::Create(); | 
|  | OverlayOutputSurface output_surface(provider); | 
|  | FakeOutputSurfaceClient client; | 
|  | EXPECT_TRUE(output_surface.BindToClient(&client)); | 
|  | output_surface.InitWithSingleOverlayValidator(); | 
|  | EXPECT_TRUE(output_surface.overlay_candidate_validator() != NULL); | 
|  |  | 
|  | scoped_ptr<SharedBitmapManager> shared_bitmap_manager( | 
|  | new TestSharedBitmapManager()); | 
|  | scoped_ptr<ResourceProvider> resource_provider(ResourceProvider::Create( | 
|  | &output_surface, shared_bitmap_manager.get(), NULL, NULL, 0, false, 1)); | 
|  |  | 
|  | scoped_ptr<DefaultOverlayProcessor> overlay_processor( | 
|  | new DefaultOverlayProcessor(&output_surface, resource_provider.get())); | 
|  | overlay_processor->Initialize(); | 
|  | EXPECT_GE(1U, overlay_processor->GetStrategyCount()); | 
|  | } | 
|  |  | 
|  | class SingleOverlayOnTopTest : public testing::Test { | 
|  | protected: | 
|  | void SetUp() override { | 
|  | provider_ = TestContextProvider::Create(); | 
|  | output_surface_.reset(new OverlayOutputSurface(provider_)); | 
|  | EXPECT_TRUE(output_surface_->BindToClient(&client_)); | 
|  | output_surface_->InitWithSingleOverlayValidator(); | 
|  | EXPECT_TRUE(output_surface_->overlay_candidate_validator() != NULL); | 
|  |  | 
|  | shared_bitmap_manager_.reset(new TestSharedBitmapManager()); | 
|  | resource_provider_ = ResourceProvider::Create(output_surface_.get(), | 
|  | shared_bitmap_manager_.get(), | 
|  | NULL, | 
|  | NULL, | 
|  | 0, | 
|  | false, | 
|  | 1); | 
|  |  | 
|  | overlay_processor_.reset(new SingleOverlayProcessor( | 
|  | output_surface_.get(), resource_provider_.get())); | 
|  | overlay_processor_->Initialize(); | 
|  | } | 
|  |  | 
|  | scoped_refptr<TestContextProvider> provider_; | 
|  | scoped_ptr<OverlayOutputSurface> output_surface_; | 
|  | FakeOutputSurfaceClient client_; | 
|  | scoped_ptr<SharedBitmapManager> shared_bitmap_manager_; | 
|  | scoped_ptr<ResourceProvider> resource_provider_; | 
|  | scoped_ptr<SingleOverlayProcessor> overlay_processor_; | 
|  | }; | 
|  |  | 
|  | TEST_F(SingleOverlayOnTopTest, SuccessfullOverlay) { | 
|  | scoped_ptr<RenderPass> 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. | 
|  | CreateFullscreenCheckeredQuad(resource_provider_.get(), | 
|  | pass->shared_quad_state_list.back(), | 
|  | pass.get()); | 
|  | CreateFullscreenCheckeredQuad(resource_provider_.get(), | 
|  | pass->shared_quad_state_list.back(), | 
|  | pass.get()); | 
|  |  | 
|  | RenderPassList pass_list; | 
|  | pass_list.push_back(pass.Pass()); | 
|  |  | 
|  | // Check for potential candidates. | 
|  | OverlayCandidateList candidate_list; | 
|  | overlay_processor_->ProcessForOverlays(&pass_list, &candidate_list); | 
|  |  | 
|  | ASSERT_EQ(1U, pass_list.size()); | 
|  | ASSERT_EQ(2U, candidate_list.size()); | 
|  |  | 
|  | RenderPass* main_pass = pass_list.back(); | 
|  | // 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, NoCandidates) { | 
|  | scoped_ptr<RenderPass> pass = CreateRenderPass(); | 
|  | CreateFullscreenCheckeredQuad(resource_provider_.get(), | 
|  | pass->shared_quad_state_list.back(), | 
|  | pass.get()); | 
|  | CreateFullscreenCheckeredQuad(resource_provider_.get(), | 
|  | pass->shared_quad_state_list.back(), | 
|  | pass.get()); | 
|  |  | 
|  | RenderPassList pass_list; | 
|  | pass_list.push_back(pass.Pass()); | 
|  |  | 
|  | RenderPassList original_pass_list; | 
|  | RenderPass::CopyAll(pass_list, &original_pass_list); | 
|  |  | 
|  | OverlayCandidateList candidate_list; | 
|  | overlay_processor_->ProcessForOverlays(&pass_list, &candidate_list); | 
|  | EXPECT_EQ(0U, candidate_list.size()); | 
|  | // There should be nothing new here. | 
|  | CompareRenderPassLists(pass_list, original_pass_list); | 
|  | } | 
|  |  | 
|  | TEST_F(SingleOverlayOnTopTest, OccludedCandidates) { | 
|  | scoped_ptr<RenderPass> pass = CreateRenderPass(); | 
|  | CreateFullscreenCheckeredQuad(resource_provider_.get(), | 
|  | pass->shared_quad_state_list.back(), | 
|  | pass.get()); | 
|  | CreateFullscreenCheckeredQuad(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(pass.Pass()); | 
|  |  | 
|  | RenderPassList original_pass_list; | 
|  | RenderPass::CopyAll(pass_list, &original_pass_list); | 
|  |  | 
|  | OverlayCandidateList candidate_list; | 
|  | overlay_processor_->ProcessForOverlays(&pass_list, &candidate_list); | 
|  | 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) { | 
|  | RenderPassList pass_list; | 
|  | pass_list.push_back(CreateRenderPass()); | 
|  |  | 
|  | scoped_ptr<RenderPass> pass = CreateRenderPass(); | 
|  | CreateFullscreenCandidateQuad(resource_provider_.get(), | 
|  | pass->shared_quad_state_list.back(), | 
|  | pass.get()); | 
|  |  | 
|  | // Add something behind it. | 
|  | CreateFullscreenCheckeredQuad(resource_provider_.get(), | 
|  | pass->shared_quad_state_list.back(), | 
|  | pass.get()); | 
|  | CreateFullscreenCheckeredQuad(resource_provider_.get(), | 
|  | pass->shared_quad_state_list.back(), | 
|  | pass.get()); | 
|  |  | 
|  | pass_list.push_back(pass.Pass()); | 
|  |  | 
|  | RenderPassList original_pass_list; | 
|  | RenderPass::CopyAll(pass_list, &original_pass_list); | 
|  |  | 
|  | // Check for potential candidates. | 
|  | OverlayCandidateList candidate_list; | 
|  | overlay_processor_->ProcessForOverlays(&pass_list, &candidate_list); | 
|  | EXPECT_EQ(2U, candidate_list.size()); | 
|  |  | 
|  | // This should be the same. | 
|  | ASSERT_EQ(2U, pass_list.size()); | 
|  | } | 
|  |  | 
|  | TEST_F(SingleOverlayOnTopTest, RejectPremultipliedAlpha) { | 
|  | scoped_ptr<RenderPass> pass = CreateRenderPass(); | 
|  | TextureDrawQuad* quad = | 
|  | CreateFullscreenCandidateQuad(resource_provider_.get(), | 
|  | pass->shared_quad_state_list.back(), | 
|  | pass.get()); | 
|  | quad->premultiplied_alpha = true; | 
|  |  | 
|  | RenderPassList pass_list; | 
|  | pass_list.push_back(pass.Pass()); | 
|  | OverlayCandidateList candidate_list; | 
|  | overlay_processor_->ProcessForOverlays(&pass_list, &candidate_list); | 
|  | EXPECT_EQ(1U, pass_list.size()); | 
|  | EXPECT_EQ(0U, candidate_list.size()); | 
|  | } | 
|  |  | 
|  | TEST_F(SingleOverlayOnTopTest, RejectBlending) { | 
|  | scoped_ptr<RenderPass> pass = CreateRenderPass(); | 
|  | TextureDrawQuad* quad = | 
|  | CreateFullscreenCandidateQuad(resource_provider_.get(), | 
|  | pass->shared_quad_state_list.back(), | 
|  | pass.get()); | 
|  | quad->needs_blending = true; | 
|  |  | 
|  | RenderPassList pass_list; | 
|  | pass_list.push_back(pass.Pass()); | 
|  | OverlayCandidateList candidate_list; | 
|  | overlay_processor_->ProcessForOverlays(&pass_list, &candidate_list); | 
|  | ASSERT_EQ(1U, pass_list.size()); | 
|  | EXPECT_EQ(0U, candidate_list.size()); | 
|  | } | 
|  |  | 
|  | TEST_F(SingleOverlayOnTopTest, RejectBackgroundColor) { | 
|  | scoped_ptr<RenderPass> pass = CreateRenderPass(); | 
|  | TextureDrawQuad* quad = | 
|  | CreateFullscreenCandidateQuad(resource_provider_.get(), | 
|  | pass->shared_quad_state_list.back(), | 
|  | pass.get()); | 
|  | quad->background_color = SK_ColorBLACK; | 
|  |  | 
|  | RenderPassList pass_list; | 
|  | pass_list.push_back(pass.Pass()); | 
|  | OverlayCandidateList candidate_list; | 
|  | overlay_processor_->ProcessForOverlays(&pass_list, &candidate_list); | 
|  | ASSERT_EQ(1U, pass_list.size()); | 
|  | EXPECT_EQ(0U, candidate_list.size()); | 
|  | } | 
|  |  | 
|  | TEST_F(SingleOverlayOnTopTest, RejectBlendMode) { | 
|  | scoped_ptr<RenderPass> 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; | 
|  |  | 
|  | RenderPassList pass_list; | 
|  | pass_list.push_back(pass.Pass()); | 
|  | OverlayCandidateList candidate_list; | 
|  | overlay_processor_->ProcessForOverlays(&pass_list, &candidate_list); | 
|  | ASSERT_EQ(1U, pass_list.size()); | 
|  | EXPECT_EQ(0U, candidate_list.size()); | 
|  | } | 
|  |  | 
|  | TEST_F(SingleOverlayOnTopTest, RejectOpacity) { | 
|  | scoped_ptr<RenderPass> pass = CreateRenderPass(); | 
|  | CreateFullscreenCandidateQuad(resource_provider_.get(), | 
|  | pass->shared_quad_state_list.back(), | 
|  | pass.get()); | 
|  | pass->shared_quad_state_list.back()->opacity = 0.5f; | 
|  |  | 
|  | RenderPassList pass_list; | 
|  | pass_list.push_back(pass.Pass()); | 
|  | OverlayCandidateList candidate_list; | 
|  | overlay_processor_->ProcessForOverlays(&pass_list, &candidate_list); | 
|  | ASSERT_EQ(1U, pass_list.size()); | 
|  | EXPECT_EQ(0U, candidate_list.size()); | 
|  | } | 
|  |  | 
|  | TEST_F(SingleOverlayOnTopTest, RejectNonScaleTransform) { | 
|  | scoped_ptr<RenderPass> pass = CreateRenderPass(); | 
|  | CreateFullscreenCandidateQuad(resource_provider_.get(), | 
|  | pass->shared_quad_state_list.back(), | 
|  | pass.get()); | 
|  | pass->shared_quad_state_list.back() | 
|  | ->content_to_target_transform.RotateAboutXAxis(45.f); | 
|  |  | 
|  | RenderPassList pass_list; | 
|  | pass_list.push_back(pass.Pass()); | 
|  | OverlayCandidateList candidate_list; | 
|  | overlay_processor_->ProcessForOverlays(&pass_list, &candidate_list); | 
|  | ASSERT_EQ(1U, pass_list.size()); | 
|  | EXPECT_EQ(0U, candidate_list.size()); | 
|  | } | 
|  |  | 
|  | TEST_F(SingleOverlayOnTopTest, RejectNegativeScaleTransform) { | 
|  | scoped_ptr<RenderPass> pass = CreateRenderPass(); | 
|  | CreateFullscreenCandidateQuad(resource_provider_.get(), | 
|  | pass->shared_quad_state_list.back(), | 
|  | pass.get()); | 
|  | pass->shared_quad_state_list.back()->content_to_target_transform.Scale(2.0f, | 
|  | -1.0f); | 
|  |  | 
|  | RenderPassList pass_list; | 
|  | pass_list.push_back(pass.Pass()); | 
|  | OverlayCandidateList candidate_list; | 
|  | overlay_processor_->ProcessForOverlays(&pass_list, &candidate_list); | 
|  | ASSERT_EQ(1U, pass_list.size()); | 
|  | EXPECT_EQ(0U, candidate_list.size()); | 
|  | } | 
|  |  | 
|  | TEST_F(SingleOverlayOnTopTest, AllowPositiveScaleTransform) { | 
|  | gfx::Rect rect = kOverlayRect; | 
|  | rect.set_width(rect.width() / 2); | 
|  | scoped_ptr<RenderPass> pass = CreateRenderPass(); | 
|  | CreateCandidateQuadAt(resource_provider_.get(), | 
|  | pass->shared_quad_state_list.back(), pass.get(), rect); | 
|  | pass->shared_quad_state_list.back()->content_to_target_transform.Scale(2.0f, | 
|  | 1.0f); | 
|  |  | 
|  | RenderPassList pass_list; | 
|  | pass_list.push_back(pass.Pass()); | 
|  | OverlayCandidateList candidate_list; | 
|  | overlay_processor_->ProcessForOverlays(&pass_list, &candidate_list); | 
|  | ASSERT_EQ(1U, pass_list.size()); | 
|  | EXPECT_EQ(2U, candidate_list.size()); | 
|  | } | 
|  |  | 
|  | TEST_F(SingleOverlayOnTopTest, AllowNotTopIfNotOccluded) { | 
|  | scoped_ptr<RenderPass> pass = CreateRenderPass(); | 
|  | CreateCheckeredQuadAt(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); | 
|  |  | 
|  | RenderPassList pass_list; | 
|  | pass_list.push_back(pass.Pass()); | 
|  |  | 
|  | RenderPassList original_pass_list; | 
|  | RenderPass::CopyAll(pass_list, &original_pass_list); | 
|  |  | 
|  | OverlayCandidateList candidate_list; | 
|  | overlay_processor_->ProcessForOverlays(&pass_list, &candidate_list); | 
|  | EXPECT_EQ(1U, pass_list.size()); | 
|  | EXPECT_EQ(2U, candidate_list.size()); | 
|  | } | 
|  |  | 
|  | TEST_F(SingleOverlayOnTopTest, AllowTransparentOnTop) { | 
|  | scoped_ptr<RenderPass> 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); | 
|  |  | 
|  | RenderPassList pass_list; | 
|  | pass_list.push_back(pass.Pass()); | 
|  |  | 
|  | RenderPassList original_pass_list; | 
|  | RenderPass::CopyAll(pass_list, &original_pass_list); | 
|  |  | 
|  | OverlayCandidateList candidate_list; | 
|  | overlay_processor_->ProcessForOverlays(&pass_list, &candidate_list); | 
|  | EXPECT_EQ(1U, pass_list.size()); | 
|  | EXPECT_EQ(2U, candidate_list.size()); | 
|  | } | 
|  |  | 
|  | TEST_F(SingleOverlayOnTopTest, AllowTransparentColorOnTop) { | 
|  | scoped_ptr<RenderPass> 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); | 
|  |  | 
|  | RenderPassList pass_list; | 
|  | pass_list.push_back(pass.Pass()); | 
|  |  | 
|  | RenderPassList original_pass_list; | 
|  | RenderPass::CopyAll(pass_list, &original_pass_list); | 
|  |  | 
|  | OverlayCandidateList candidate_list; | 
|  | overlay_processor_->ProcessForOverlays(&pass_list, &candidate_list); | 
|  | EXPECT_EQ(1U, pass_list.size()); | 
|  | EXPECT_EQ(2U, candidate_list.size()); | 
|  | } | 
|  |  | 
|  | TEST_F(SingleOverlayOnTopTest, RejectOpaqueColorOnTop) { | 
|  | scoped_ptr<RenderPass> 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); | 
|  |  | 
|  | RenderPassList pass_list; | 
|  | pass_list.push_back(pass.Pass()); | 
|  |  | 
|  | RenderPassList original_pass_list; | 
|  | RenderPass::CopyAll(pass_list, &original_pass_list); | 
|  |  | 
|  | OverlayCandidateList candidate_list; | 
|  | overlay_processor_->ProcessForOverlays(&pass_list, &candidate_list); | 
|  | EXPECT_EQ(1U, pass_list.size()); | 
|  | EXPECT_EQ(0U, candidate_list.size()); | 
|  | } | 
|  |  | 
|  | TEST_F(SingleOverlayOnTopTest, RejectTransparentColorOnTopWithoutBlending) { | 
|  | scoped_ptr<RenderPass> 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); | 
|  |  | 
|  | RenderPassList pass_list; | 
|  | pass_list.push_back(pass.Pass()); | 
|  |  | 
|  | RenderPassList original_pass_list; | 
|  | RenderPass::CopyAll(pass_list, &original_pass_list); | 
|  |  | 
|  | OverlayCandidateList candidate_list; | 
|  | overlay_processor_->ProcessForOverlays(&pass_list, &candidate_list); | 
|  | EXPECT_EQ(1U, pass_list.size()); | 
|  | EXPECT_EQ(0U, candidate_list.size()); | 
|  | } | 
|  |  | 
|  | TEST_F(SingleOverlayOnTopTest, RejectVideoSwapTransform) { | 
|  | scoped_ptr<RenderPass> pass = CreateRenderPass(); | 
|  | CreateFullscreenCandidateVideoQuad(resource_provider_.get(), | 
|  | pass->shared_quad_state_list.back(), | 
|  | pass.get(), kSwapTransform); | 
|  |  | 
|  | RenderPassList pass_list; | 
|  | pass_list.push_back(pass.Pass()); | 
|  | OverlayCandidateList candidate_list; | 
|  | overlay_processor_->ProcessForOverlays(&pass_list, &candidate_list); | 
|  | ASSERT_EQ(1U, pass_list.size()); | 
|  | EXPECT_EQ(0U, candidate_list.size()); | 
|  | } | 
|  |  | 
|  | TEST_F(SingleOverlayOnTopTest, AllowVideoXMirrorTransform) { | 
|  | scoped_ptr<RenderPass> pass = CreateRenderPass(); | 
|  | CreateFullscreenCandidateVideoQuad(resource_provider_.get(), | 
|  | pass->shared_quad_state_list.back(), | 
|  | pass.get(), kXMirrorTransform); | 
|  |  | 
|  | RenderPassList pass_list; | 
|  | pass_list.push_back(pass.Pass()); | 
|  | OverlayCandidateList candidate_list; | 
|  | overlay_processor_->ProcessForOverlays(&pass_list, &candidate_list); | 
|  | ASSERT_EQ(1U, pass_list.size()); | 
|  | EXPECT_EQ(2U, candidate_list.size()); | 
|  | } | 
|  |  | 
|  | TEST_F(SingleOverlayOnTopTest, AllowVideoBothMirrorTransform) { | 
|  | scoped_ptr<RenderPass> pass = CreateRenderPass(); | 
|  | CreateFullscreenCandidateVideoQuad(resource_provider_.get(), | 
|  | pass->shared_quad_state_list.back(), | 
|  | pass.get(), kBothMirrorTransform); | 
|  |  | 
|  | RenderPassList pass_list; | 
|  | pass_list.push_back(pass.Pass()); | 
|  | OverlayCandidateList candidate_list; | 
|  | overlay_processor_->ProcessForOverlays(&pass_list, &candidate_list); | 
|  | ASSERT_EQ(1U, pass_list.size()); | 
|  | EXPECT_EQ(2U, candidate_list.size()); | 
|  | } | 
|  |  | 
|  | TEST_F(SingleOverlayOnTopTest, AllowVideoNormalTransform) { | 
|  | scoped_ptr<RenderPass> pass = CreateRenderPass(); | 
|  | CreateFullscreenCandidateVideoQuad(resource_provider_.get(), | 
|  | pass->shared_quad_state_list.back(), | 
|  | pass.get(), kNormalTransform); | 
|  |  | 
|  | RenderPassList pass_list; | 
|  | pass_list.push_back(pass.Pass()); | 
|  | OverlayCandidateList candidate_list; | 
|  | overlay_processor_->ProcessForOverlays(&pass_list, &candidate_list); | 
|  | ASSERT_EQ(1U, pass_list.size()); | 
|  | EXPECT_EQ(2U, candidate_list.size()); | 
|  | } | 
|  |  | 
|  | TEST_F(SingleOverlayOnTopTest, AllowVideoYMirrorTransform) { | 
|  | scoped_ptr<RenderPass> pass = CreateRenderPass(); | 
|  | CreateFullscreenCandidateVideoQuad(resource_provider_.get(), | 
|  | pass->shared_quad_state_list.back(), | 
|  | pass.get(), kYMirrorTransform); | 
|  |  | 
|  | RenderPassList pass_list; | 
|  | pass_list.push_back(pass.Pass()); | 
|  | OverlayCandidateList candidate_list; | 
|  | overlay_processor_->ProcessForOverlays(&pass_list, &candidate_list); | 
|  | ASSERT_EQ(1U, pass_list.size()); | 
|  | EXPECT_EQ(2U, candidate_list.size()); | 
|  | } | 
|  |  | 
|  | 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_NE(0U, frame->overlay_list.back().resource_id); | 
|  | } | 
|  |  | 
|  | 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_ = ResourceProvider::Create( | 
|  | output_surface_.get(), NULL, NULL, NULL, 0, false, 1); | 
|  |  | 
|  | provider_->support()->SetScheduleOverlayPlaneCallback(base::Bind( | 
|  | &MockOverlayScheduler::Schedule, base::Unretained(&scheduler_))); | 
|  | } | 
|  |  | 
|  | void Init(bool use_validator) { | 
|  | if (use_validator) | 
|  | output_surface_->InitWithSingleOverlayValidator(); | 
|  |  | 
|  | renderer_ = | 
|  | make_scoped_ptr(new OverlayInfoRendererGL(&renderer_client_, | 
|  | &settings_, | 
|  | output_surface_.get(), | 
|  | resource_provider_.get())); | 
|  | } | 
|  |  | 
|  | void SwapBuffers() { renderer_->SwapBuffers(CompositorFrameMetadata()); } | 
|  |  | 
|  | RendererSettings settings_; | 
|  | FakeOutputSurfaceClient output_surface_client_; | 
|  | scoped_ptr<OverlayOutputSurface> output_surface_; | 
|  | FakeRendererClient renderer_client_; | 
|  | scoped_ptr<ResourceProvider> resource_provider_; | 
|  | scoped_ptr<OverlayInfoRendererGL> renderer_; | 
|  | scoped_refptr<TestContextProvider> 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<RenderPass> pass = CreateRenderPass(); | 
|  |  | 
|  | CreateFullscreenCandidateQuad(resource_provider_.get(), | 
|  | pass->shared_quad_state_list.back(), | 
|  | pass.get()); | 
|  |  | 
|  | CreateFullscreenCheckeredQuad(resource_provider_.get(), | 
|  | pass->shared_quad_state_list.back(), | 
|  | pass.get()); | 
|  | CreateFullscreenCheckeredQuad(resource_provider_.get(), | 
|  | pass->shared_quad_state_list.back(), | 
|  | pass.get()); | 
|  |  | 
|  | RenderPassList pass_list; | 
|  | pass_list.push_back(pass.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(1, | 
|  | gfx::OVERLAY_TRANSFORM_NONE, | 
|  | _, | 
|  | kOverlayRect, | 
|  | BoundingRect(kUVTopLeft, kUVBottomRight))).Times(1); | 
|  | renderer_->DrawFrame(&pass_list, 1.f, viewport_rect, viewport_rect, false); | 
|  |  | 
|  | SwapBuffers(); | 
|  |  | 
|  | Mock::VerifyAndClearExpectations(renderer_.get()); | 
|  | Mock::VerifyAndClearExpectations(&scheduler_); | 
|  | } | 
|  |  | 
|  | TEST_F(GLRendererWithOverlaysTest, OccludedQuadDrawn) { | 
|  | bool use_validator = true; | 
|  | Init(use_validator); | 
|  | renderer_->set_expect_overlays(false); | 
|  | gfx::Rect viewport_rect(16, 16); | 
|  |  | 
|  | scoped_ptr<RenderPass> pass = CreateRenderPass(); | 
|  |  | 
|  | CreateFullscreenCheckeredQuad(resource_provider_.get(), | 
|  | pass->shared_quad_state_list.back(), | 
|  | pass.get()); | 
|  | CreateFullscreenCheckeredQuad(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(pass.Pass()); | 
|  |  | 
|  | // 3 quads in the pass, all should draw. | 
|  | EXPECT_CALL(*renderer_, DoDrawQuad(_, _, _)).Times(3); | 
|  | EXPECT_CALL(scheduler_, Schedule(_, _, _, _, _)).Times(0); | 
|  | renderer_->DrawFrame(&pass_list, 1.f, viewport_rect, viewport_rect, false); | 
|  |  | 
|  | 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<RenderPass> pass = CreateRenderPass(); | 
|  |  | 
|  | CreateFullscreenCandidateQuad(resource_provider_.get(), | 
|  | pass->shared_quad_state_list.back(), | 
|  | pass.get()); | 
|  |  | 
|  | CreateFullscreenCheckeredQuad(resource_provider_.get(), | 
|  | pass->shared_quad_state_list.back(), | 
|  | pass.get()); | 
|  | CreateFullscreenCheckeredQuad(resource_provider_.get(), | 
|  | pass->shared_quad_state_list.back(), | 
|  | pass.get()); | 
|  |  | 
|  | RenderPassList pass_list; | 
|  | pass_list.push_back(pass.Pass()); | 
|  |  | 
|  | // Should see no overlays. | 
|  | EXPECT_CALL(*renderer_, DoDrawQuad(_, _, _)).Times(3); | 
|  | EXPECT_CALL(scheduler_, Schedule(_, _, _, _, _)).Times(0); | 
|  | renderer_->DrawFrame(&pass_list, 1.f, viewport_rect, viewport_rect, false); | 
|  |  | 
|  | SwapBuffers(); | 
|  |  | 
|  | Mock::VerifyAndClearExpectations(renderer_.get()); | 
|  | Mock::VerifyAndClearExpectations(&scheduler_); | 
|  | } | 
|  |  | 
|  | TEST_F(GLRendererWithOverlaysTest, ResourcesExportedAndReturned) { | 
|  | bool use_validator = true; | 
|  | Init(use_validator); | 
|  | renderer_->set_expect_overlays(true); | 
|  |  | 
|  | ResourceProvider::ResourceId resource1 = | 
|  | CreateResource(resource_provider_.get()); | 
|  | ResourceProvider::ResourceId resource2 = | 
|  | CreateResource(resource_provider_.get()); | 
|  |  | 
|  | scoped_ptr<RenderPass> pass = CreateRenderPass(); | 
|  | RenderPassList pass_list; | 
|  | pass_list.push_back(pass.Pass()); | 
|  |  | 
|  | DirectRenderer::DrawingFrame frame1; | 
|  | frame1.render_passes_in_draw_order = &pass_list; | 
|  | frame1.overlay_list.resize(2); | 
|  | 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); | 
|  | OverlayCandidate& overlay2 = frame2.overlay_list.back(); | 
|  | overlay2.resource_id = resource2; | 
|  | overlay2.plane_z_order = 1; | 
|  |  | 
|  | EXPECT_CALL(scheduler_, Schedule(_, _, _, _, _)).Times(1); | 
|  | 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(1); | 
|  | renderer_->BeginDrawingFrame(&frame2); | 
|  | renderer_->FinishDrawingFrame(&frame2); | 
|  | EXPECT_TRUE(resource_provider_->InUseByConsumer(resource1)); | 
|  | EXPECT_TRUE(resource_provider_->InUseByConsumer(resource2)); | 
|  | SwapBuffers(); | 
|  | EXPECT_FALSE(resource_provider_->InUseByConsumer(resource1)); | 
|  | Mock::VerifyAndClearExpectations(&scheduler_); | 
|  |  | 
|  | EXPECT_CALL(scheduler_, Schedule(_, _, _, _, _)).Times(1); | 
|  | renderer_->BeginDrawingFrame(&frame1); | 
|  | renderer_->FinishDrawingFrame(&frame1); | 
|  | EXPECT_TRUE(resource_provider_->InUseByConsumer(resource1)); | 
|  | EXPECT_TRUE(resource_provider_->InUseByConsumer(resource2)); | 
|  | SwapBuffers(); | 
|  | EXPECT_FALSE(resource_provider_->InUseByConsumer(resource2)); | 
|  | Mock::VerifyAndClearExpectations(&scheduler_); | 
|  |  | 
|  | // No overlays, release the resource. | 
|  | EXPECT_CALL(scheduler_, Schedule(_, _, _, _, _)).Times(0); | 
|  | DirectRenderer::DrawingFrame frame3; | 
|  | frame3.render_passes_in_draw_order = &pass_list; | 
|  | renderer_->set_expect_overlays(false); | 
|  | renderer_->BeginDrawingFrame(&frame3); | 
|  | renderer_->FinishDrawingFrame(&frame3); | 
|  | EXPECT_TRUE(resource_provider_->InUseByConsumer(resource1)); | 
|  | EXPECT_FALSE(resource_provider_->InUseByConsumer(resource2)); | 
|  | SwapBuffers(); | 
|  | EXPECT_FALSE(resource_provider_->InUseByConsumer(resource1)); | 
|  | Mock::VerifyAndClearExpectations(&scheduler_); | 
|  |  | 
|  | // Use the same buffer twice. | 
|  | renderer_->set_expect_overlays(true); | 
|  | EXPECT_CALL(scheduler_, Schedule(_, _, _, _, _)).Times(1); | 
|  | renderer_->BeginDrawingFrame(&frame1); | 
|  | renderer_->FinishDrawingFrame(&frame1); | 
|  | EXPECT_TRUE(resource_provider_->InUseByConsumer(resource1)); | 
|  | SwapBuffers(); | 
|  | Mock::VerifyAndClearExpectations(&scheduler_); | 
|  |  | 
|  | EXPECT_CALL(scheduler_, Schedule(_, _, _, _, _)).Times(1); | 
|  | 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(&frame3); | 
|  | renderer_->FinishDrawingFrame(&frame3); | 
|  | EXPECT_TRUE(resource_provider_->InUseByConsumer(resource1)); | 
|  | SwapBuffers(); | 
|  | EXPECT_FALSE(resource_provider_->InUseByConsumer(resource1)); | 
|  | Mock::VerifyAndClearExpectations(&scheduler_); | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  | }  // namespace cc |