// Copyright 2013 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/trees/layer_tree_host.h" #include <algorithm> #include "base/bind.h" #include "base/location.h" #include "base/synchronization/waitable_event.h" #include "base/threading/thread.h" #include "base/time/time.h" #include "cc/layers/delegated_renderer_layer.h" #include "cc/layers/delegated_renderer_layer_client.h" #include "cc/layers/delegated_renderer_layer_impl.h" #include "cc/output/compositor_frame.h" #include "cc/output/compositor_frame_ack.h" #include "cc/output/delegated_frame_data.h" #include "cc/quads/render_pass_draw_quad.h" #include "cc/quads/shared_quad_state.h" #include "cc/quads/texture_draw_quad.h" #include "cc/resources/returned_resource.h" #include "cc/test/fake_delegated_renderer_layer.h" #include "cc/test/fake_delegated_renderer_layer_impl.h" #include "cc/test/fake_output_surface.h" #include "cc/test/layer_tree_test.h" #include "cc/trees/layer_tree_impl.h" #include "gpu/GLES2/gl2extchromium.h" #include "third_party/WebKit/public/platform/WebGraphicsContext3D.h" namespace cc { namespace { bool ReturnedResourceLower(const ReturnedResource& a, const ReturnedResource& b) { return a.id < b.id; } // Tests if the list of resources matches an expectation, modulo the order. bool ResourcesMatch(ReturnedResourceArray actual, unsigned* expected, size_t expected_count) { std::sort(actual.begin(), actual.end(), ReturnedResourceLower); std::sort(expected, expected + expected_count); size_t actual_index = 0; // for each element of the expected array, count off one of the actual array // (after checking it matches). for (size_t expected_index = 0; expected_index < expected_count; ++expected_index) { EXPECT_LT(actual_index, actual.size()); if (actual_index >= actual.size()) return false; EXPECT_EQ(actual[actual_index].id, expected[expected_index]); if (actual[actual_index].id != expected[expected_index]) return false; EXPECT_GT(actual[actual_index].count, 0); if (actual[actual_index].count <= 0) { return false; } else { --actual[actual_index].count; if (actual[actual_index].count == 0) ++actual_index; } } EXPECT_EQ(actual_index, actual.size()); return actual_index == actual.size(); } #define EXPECT_RESOURCES(expected, actual) \ EXPECT_TRUE(ResourcesMatch(actual, expected, arraysize(expected))); // These tests deal with delegated renderer layers. class LayerTreeHostDelegatedTest : public LayerTreeTest { protected: scoped_ptr<DelegatedFrameData> CreateFrameData(gfx::Rect root_output_rect, gfx::Rect root_damage_rect) { scoped_ptr<DelegatedFrameData> frame(new DelegatedFrameData); scoped_ptr<RenderPass> root_pass(RenderPass::Create()); root_pass->SetNew(RenderPass::Id(1, 1), root_output_rect, root_damage_rect, gfx::Transform()); frame->render_pass_list.push_back(root_pass.Pass()); return frame.Pass(); } scoped_ptr<DelegatedFrameData> CreateInvalidFrameData( gfx::Rect root_output_rect, gfx::Rect root_damage_rect) { scoped_ptr<DelegatedFrameData> frame(new DelegatedFrameData); scoped_ptr<RenderPass> root_pass(RenderPass::Create()); root_pass->SetNew(RenderPass::Id(1, 1), root_output_rect, root_damage_rect, gfx::Transform()); scoped_ptr<SharedQuadState> shared_quad_state = SharedQuadState::Create(); gfx::Rect rect = root_output_rect; gfx::Rect opaque_rect = root_output_rect; // An invalid resource id! The resource isn't part of the frame. unsigned resource_id = 5; bool premultiplied_alpha = false; gfx::PointF uv_top_left = gfx::PointF(0.f, 0.f); gfx::PointF uv_bottom_right = gfx::PointF(1.f, 1.f); SkColor background_color = 0; float vertex_opacity[4] = {1.f, 1.f, 1.f, 1.f}; bool flipped = false; scoped_ptr<TextureDrawQuad> invalid_draw_quad = TextureDrawQuad::Create(); invalid_draw_quad->SetNew(shared_quad_state.get(), rect, opaque_rect, resource_id, premultiplied_alpha, uv_top_left, uv_bottom_right, background_color, vertex_opacity, flipped); root_pass->quad_list.push_back(invalid_draw_quad.PassAs<DrawQuad>()); root_pass->shared_quad_state_list.push_back(shared_quad_state.Pass()); frame->render_pass_list.push_back(root_pass.Pass()); return frame.Pass(); } void AddTransferableResource(DelegatedFrameData* frame, ResourceProvider::ResourceId resource_id) { TransferableResource resource; resource.id = resource_id; frame->resource_list.push_back(resource); } void AddTextureQuad(DelegatedFrameData* frame, ResourceProvider::ResourceId resource_id) { scoped_ptr<SharedQuadState> sqs = SharedQuadState::Create(); scoped_ptr<TextureDrawQuad> quad = TextureDrawQuad::Create(); float vertex_opacity[4] = { 1.f, 1.f, 1.f, 1.f }; quad->SetNew(sqs.get(), gfx::Rect(0, 0, 10, 10), gfx::Rect(0, 0, 10, 10), resource_id, false, gfx::PointF(0.f, 0.f), gfx::PointF(1.f, 1.f), SK_ColorTRANSPARENT, vertex_opacity, false); frame->render_pass_list[0]->shared_quad_state_list.push_back(sqs.Pass()); frame->render_pass_list[0]->quad_list.push_back(quad.PassAs<DrawQuad>()); } void AddRenderPass(DelegatedFrameData* frame, RenderPass::Id id, gfx::Rect output_rect, gfx::Rect damage_rect, const FilterOperations& filters, const FilterOperations& background_filters) { for (size_t i = 0; i < frame->render_pass_list.size(); ++i) DCHECK(id != frame->render_pass_list[i]->id); scoped_ptr<RenderPass> pass(RenderPass::Create()); pass->SetNew(id, output_rect, damage_rect, gfx::Transform()); frame->render_pass_list.push_back(pass.Pass()); scoped_ptr<SharedQuadState> sqs = SharedQuadState::Create(); scoped_ptr<RenderPassDrawQuad> quad = RenderPassDrawQuad::Create(); quad->SetNew(sqs.get(), output_rect, id, false, // is_replica 0, // mask_resource_id damage_rect, gfx::Rect(0, 0, 1, 1), // mask_uv_rect filters, skia::RefPtr<SkImageFilter>(), background_filters); frame->render_pass_list[0]->shared_quad_state_list.push_back(sqs.Pass()); frame->render_pass_list[0]->quad_list.push_back(quad.PassAs<DrawQuad>()); } scoped_ptr<DelegatedFrameData> CreateEmptyFrameData() { scoped_ptr<DelegatedFrameData> frame(new DelegatedFrameData); return frame.Pass(); } static ResourceProvider::ResourceId AppendResourceId( std::vector<ResourceProvider::ResourceId>* resources_in_last_sent_frame, ResourceProvider::ResourceId resource_id) { resources_in_last_sent_frame->push_back(resource_id); return resource_id; } void ReturnUnusedResourcesFromParent(LayerTreeHostImpl* host_impl) { DelegatedFrameData* delegated_frame_data = output_surface()->last_sent_frame().delegated_frame_data.get(); if (!delegated_frame_data) return; std::vector<ResourceProvider::ResourceId> resources_in_last_sent_frame; for (size_t i = 0; i < delegated_frame_data->render_pass_list.size(); ++i) { RenderPass* pass = delegated_frame_data->render_pass_list.at(i); for (size_t j = 0; j < pass->quad_list.size(); ++j) { DrawQuad* quad = pass->quad_list[j]; quad->IterateResources(base::Bind(&AppendResourceId, &resources_in_last_sent_frame)); } } std::vector<ResourceProvider::ResourceId> resources_to_return; const TransferableResourceArray& resources_held_by_parent = output_surface()->resources_held_by_parent(); for (size_t i = 0; i < resources_held_by_parent.size(); ++i) { ResourceProvider::ResourceId resource_in_parent = resources_held_by_parent[i].id; bool resource_in_parent_is_not_part_of_frame = std::find(resources_in_last_sent_frame.begin(), resources_in_last_sent_frame.end(), resource_in_parent) == resources_in_last_sent_frame.end(); if (resource_in_parent_is_not_part_of_frame) resources_to_return.push_back(resource_in_parent); } if (resources_to_return.empty()) return; CompositorFrameAck ack; for (size_t i = 0; i < resources_to_return.size(); ++i) output_surface()->ReturnResource(resources_to_return[i], &ack); host_impl->ReclaimResources(&ack); host_impl->OnSwapBuffersComplete(); } }; class LayerTreeHostDelegatedTestCaseSingleDelegatedLayer : public LayerTreeHostDelegatedTest, public DelegatedRendererLayerClient { public: virtual void SetupTree() OVERRIDE { root_ = Layer::Create(); root_->SetAnchorPoint(gfx::PointF()); root_->SetBounds(gfx::Size(10, 10)); delegated_ = FakeDelegatedRendererLayer::Create(this); delegated_->SetAnchorPoint(gfx::PointF()); delegated_->SetBounds(gfx::Size(10, 10)); delegated_->SetIsDrawable(true); root_->AddChild(delegated_); layer_tree_host()->SetRootLayer(root_); LayerTreeHostDelegatedTest::SetupTree(); } virtual void BeginTest() OVERRIDE { PostSetNeedsCommitToMainThread(); } virtual void AfterTest() OVERRIDE {} virtual void DidCommitFrameData() OVERRIDE {} protected: scoped_refptr<Layer> root_; scoped_refptr<DelegatedRendererLayer> delegated_; }; class LayerTreeHostDelegatedTestClientDidCommitCallback : public LayerTreeHostDelegatedTestCaseSingleDelegatedLayer { public: LayerTreeHostDelegatedTestClientDidCommitCallback() : LayerTreeHostDelegatedTestCaseSingleDelegatedLayer(), num_did_commit_frame_data_(0) {} virtual void DidCommit() OVERRIDE { if (TestEnded()) return; EXPECT_EQ(1, num_did_commit_frame_data_); EndTest(); } virtual void BeginTest() OVERRIDE { delegated_->SetFrameData(CreateFrameData(gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1))); PostSetNeedsCommitToMainThread(); } virtual void DidCommitFrameData() OVERRIDE { num_did_commit_frame_data_++; } protected: int num_did_commit_frame_data_; }; SINGLE_AND_MULTI_THREAD_TEST_F( LayerTreeHostDelegatedTestClientDidCommitCallback); class LayerTreeHostDelegatedTestCreateChildId : public LayerTreeHostDelegatedTestCaseSingleDelegatedLayer { public: LayerTreeHostDelegatedTestCreateChildId() : LayerTreeHostDelegatedTestCaseSingleDelegatedLayer(), num_activates_(0), did_reset_child_id_(false) {} virtual void DidCommit() OVERRIDE { if (TestEnded()) return; delegated_->SetFrameData(CreateFrameData(gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1))); } virtual void DidActivateTreeOnThread(LayerTreeHostImpl* host_impl) OVERRIDE { LayerImpl* root_impl = host_impl->active_tree()->root_layer(); FakeDelegatedRendererLayerImpl* delegated_impl = static_cast<FakeDelegatedRendererLayerImpl*>(root_impl->children()[0]); ContextProvider* context_provider = host_impl->output_surface()->context_provider(); ++num_activates_; switch (num_activates_) { case 2: EXPECT_TRUE(delegated_impl->ChildId()); EXPECT_FALSE(did_reset_child_id_); context_provider->Context3d()->loseContextCHROMIUM( GL_GUILTY_CONTEXT_RESET_ARB, GL_INNOCENT_CONTEXT_RESET_ARB); break; case 3: EXPECT_TRUE(delegated_impl->ChildId()); EXPECT_TRUE(did_reset_child_id_); EndTest(); break; } } virtual void InitializedRendererOnThread(LayerTreeHostImpl* host_impl, bool success) OVERRIDE { EXPECT_TRUE(success); if (num_activates_ < 2) return; LayerImpl* root_impl = host_impl->active_tree()->root_layer(); FakeDelegatedRendererLayerImpl* delegated_impl = static_cast<FakeDelegatedRendererLayerImpl*>(root_impl->children()[0]); EXPECT_EQ(2, num_activates_); EXPECT_FALSE(delegated_impl->ChildId()); did_reset_child_id_ = true; } virtual void AfterTest() OVERRIDE {} protected: int num_activates_; bool did_reset_child_id_; }; SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostDelegatedTestCreateChildId); class LayerTreeHostDelegatedTestOffscreenContext_NoFilters : public LayerTreeHostDelegatedTestCaseSingleDelegatedLayer { protected: virtual void BeginTest() OVERRIDE { scoped_ptr<DelegatedFrameData> frame = CreateFrameData(gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1)); delegated_->SetFrameData(frame.Pass()); PostSetNeedsCommitToMainThread(); } virtual void DrawLayersOnThread(LayerTreeHostImpl* host_impl) OVERRIDE { EXPECT_FALSE(host_impl->offscreen_context_provider()); EndTest(); } virtual void AfterTest() OVERRIDE {} }; SINGLE_AND_MULTI_THREAD_TEST_F( LayerTreeHostDelegatedTestOffscreenContext_NoFilters); class LayerTreeHostDelegatedTestOffscreenContext_Filters : public LayerTreeHostDelegatedTestCaseSingleDelegatedLayer { protected: virtual void BeginTest() OVERRIDE { scoped_ptr<DelegatedFrameData> frame = CreateFrameData(gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1)); FilterOperations filters; filters.Append(FilterOperation::CreateGrayscaleFilter(0.5f)); AddRenderPass(frame.get(), RenderPass::Id(2, 1), gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1), filters, FilterOperations()); delegated_->SetFrameData(frame.Pass()); PostSetNeedsCommitToMainThread(); } virtual void DrawLayersOnThread(LayerTreeHostImpl* host_impl) OVERRIDE { bool expect_context = !delegating_renderer(); EXPECT_EQ(expect_context, !!host_impl->offscreen_context_provider()); EndTest(); } virtual void AfterTest() OVERRIDE {} }; SINGLE_AND_MULTI_THREAD_TEST_F( LayerTreeHostDelegatedTestOffscreenContext_Filters); class LayerTreeHostDelegatedTestOffscreenContext_BackgroundFilters : public LayerTreeHostDelegatedTestCaseSingleDelegatedLayer { protected: virtual void BeginTest() OVERRIDE { scoped_ptr<DelegatedFrameData> frame = CreateFrameData(gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1)); FilterOperations filters; filters.Append(FilterOperation::CreateGrayscaleFilter(0.5f)); AddRenderPass(frame.get(), RenderPass::Id(2, 1), gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1), FilterOperations(), filters); delegated_->SetFrameData(frame.Pass()); PostSetNeedsCommitToMainThread(); } virtual void DrawLayersOnThread(LayerTreeHostImpl* host_impl) OVERRIDE { bool expect_context = !delegating_renderer(); EXPECT_EQ(expect_context, !!host_impl->offscreen_context_provider()); EndTest(); } virtual void AfterTest() OVERRIDE {} }; SINGLE_AND_MULTI_THREAD_TEST_F( LayerTreeHostDelegatedTestOffscreenContext_BackgroundFilters); class LayerTreeHostDelegatedTestOffscreenContext_Filters_AddedToTree : public LayerTreeHostDelegatedTestCaseSingleDelegatedLayer { protected: virtual void BeginTest() OVERRIDE { scoped_ptr<DelegatedFrameData> frame = CreateFrameData(gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1)); FilterOperations filters; filters.Append(FilterOperation::CreateGrayscaleFilter(0.5f)); AddRenderPass(frame.get(), RenderPass::Id(2, 1), gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1), filters, FilterOperations()); delegated_->RemoveFromParent(); delegated_->SetFrameData(frame.Pass()); layer_tree_host()->root_layer()->AddChild(delegated_); PostSetNeedsCommitToMainThread(); } virtual void DrawLayersOnThread(LayerTreeHostImpl* host_impl) OVERRIDE { bool expect_context = !delegating_renderer(); EXPECT_EQ(expect_context, !!host_impl->offscreen_context_provider()); EndTest(); } virtual void AfterTest() OVERRIDE {} }; SINGLE_AND_MULTI_THREAD_TEST_F( LayerTreeHostDelegatedTestOffscreenContext_Filters_AddedToTree); class LayerTreeHostDelegatedTestLayerUsesFrameDamage : public LayerTreeHostDelegatedTestCaseSingleDelegatedLayer { public: LayerTreeHostDelegatedTestLayerUsesFrameDamage() : LayerTreeHostDelegatedTestCaseSingleDelegatedLayer(), first_draw_for_source_frame_(true) {} virtual void DidCommit() OVERRIDE { int next_source_frame_number = layer_tree_host()->source_frame_number(); switch (next_source_frame_number) { case 1: // The first time the layer gets a frame the whole layer should be // damaged. delegated_->SetFrameData(CreateFrameData(gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1))); break; case 2: // Should create a total amount of gfx::Rect(2, 2, 10, 6) damage. // The frame size is 20x20 while the layer is 10x10, so this should // produce a gfx::Rect(1, 1, 5, 3) damage rect. delegated_->SetFrameData(CreateFrameData(gfx::Rect(0, 0, 20, 20), gfx::Rect(2, 2, 5, 5))); delegated_->SetFrameData(CreateFrameData(gfx::Rect(0, 0, 20, 20), gfx::Rect(7, 2, 5, 6))); break; case 3: // Should create zero damage. layer_tree_host()->SetNeedsCommit(); break; case 4: // Should damage the full viewport. delegated_->SetBounds(gfx::Size(2, 2)); break; case 5: // Should create zero damage. layer_tree_host()->SetNeedsCommit(); break; case 6: // Should damage the full layer. delegated_->SetBounds(gfx::Size(6, 6)); delegated_->SetFrameData(CreateFrameData(gfx::Rect(0, 0, 5, 5), gfx::Rect(1, 1, 2, 2))); break; case 7: // Should create zero damage. layer_tree_host()->SetNeedsCommit(); break; case 8: // Should damage the full layer. delegated_->SetDisplaySize(gfx::Size(10, 10)); break; case 9: // Should create zero damage. layer_tree_host()->SetNeedsCommit(); break; case 10: // Setting an empty frame should damage the whole layer the // first time. delegated_->SetFrameData(CreateEmptyFrameData()); break; case 11: // Setting an empty frame shouldn't damage anything after the // first time. delegated_->SetFrameData(CreateEmptyFrameData()); break; case 12: // Having valid content to display agains should damage the whole layer. delegated_->SetFrameData(CreateFrameData(gfx::Rect(0, 0, 10, 10), gfx::Rect(5, 5, 1, 1))); break; case 13: // An invalid frame isn't used, so it should not cause damage. delegated_->SetFrameData(CreateInvalidFrameData(gfx::Rect(0, 0, 10, 10), gfx::Rect(5, 5, 1, 1))); break; case 14: // Should create gfx::Rect(1, 1, 2, 2) of damage. The frame size is // 5x5 and the display size is now set to 10x10, so this should result // in a gfx::Rect(2, 2, 4, 4) damage rect. delegated_->SetFrameData(CreateFrameData(gfx::Rect(0, 0, 5, 5), gfx::Rect(1, 1, 2, 2))); break; case 15: // Should create zero damage. layer_tree_host()->SetNeedsCommit(); break; case 16: // Moving the layer out of the tree and back in will damage the whole // impl layer. delegated_->RemoveFromParent(); layer_tree_host()->root_layer()->AddChild(delegated_); break; case 17: // Make a larger frame with lots of damage. Then a frame smaller than // the first frame's damage. The entire layer should be damaged, but // nothing more. delegated_->SetFrameData(CreateFrameData(gfx::Rect(0, 0, 10, 10), gfx::Rect(0, 0, 10, 10))); delegated_->SetFrameData(CreateFrameData(gfx::Rect(0, 0, 5, 5), gfx::Rect(1, 1, 2, 2))); break; case 18: // Make a frame with lots of damage. Then replace it with an empty // frame. The entire layer should be damaged, but nothing more. delegated_->SetFrameData(CreateFrameData(gfx::Rect(0, 0, 10, 10), gfx::Rect(0, 0, 10, 10))); delegated_->SetFrameData(CreateEmptyFrameData()); break; } first_draw_for_source_frame_ = true; } virtual bool PrepareToDrawOnThread(LayerTreeHostImpl* host_impl, LayerTreeHostImpl::FrameData* frame, bool result) OVERRIDE { EXPECT_TRUE(result); if (!first_draw_for_source_frame_) return result; gfx::RectF damage_rect; if (!frame->has_no_damage) { damage_rect = frame->render_passes.back()->damage_rect; } else { // If there is no damage, then we have no render passes to send. EXPECT_TRUE(frame->render_passes.empty()); } switch (host_impl->active_tree()->source_frame_number()) { case 0: // First frame is damaged because of viewport resize. EXPECT_EQ(gfx::RectF(0.f, 0.f, 10.f, 10.f).ToString(), damage_rect.ToString()); break; case 1: EXPECT_EQ(gfx::RectF(0.f, 0.f, 10.f, 10.f).ToString(), damage_rect.ToString()); break; case 2: EXPECT_EQ(gfx::RectF(1.f, 1.f, 5.f, 3.f).ToString(), damage_rect.ToString()); break; case 3: EXPECT_EQ(gfx::RectF(0.f, 0.f, 0.f, 0.f).ToString(), damage_rect.ToString()); break; case 4: EXPECT_EQ(gfx::RectF(0.f, 0.f, 10.f, 10.f).ToString(), damage_rect.ToString()); break; case 5: EXPECT_EQ(gfx::RectF(0.f, 0.f, 0.f, 0.f).ToString(), damage_rect.ToString()); break; case 6: EXPECT_EQ(gfx::RectF(0.f, 0.f, 6.f, 6.f).ToString(), damage_rect.ToString()); break; case 7: EXPECT_EQ(gfx::RectF(0.f, 0.f, 0.f, 0.f).ToString(), damage_rect.ToString()); break; case 8: EXPECT_EQ(gfx::RectF(0.f, 0.f, 6.f, 6.f).ToString(), damage_rect.ToString()); break; case 9: EXPECT_EQ(gfx::RectF(0.f, 0.f, 0.f, 0.f).ToString(), damage_rect.ToString()); break; case 10: EXPECT_EQ(gfx::RectF(0.f, 0.f, 6.f, 6.f).ToString(), damage_rect.ToString()); break; case 11: EXPECT_EQ(gfx::RectF(0.f, 0.f, 0.f, 0.f).ToString(), damage_rect.ToString()); break; case 12: EXPECT_EQ(gfx::RectF(0.f, 0.f, 6.f, 6.f).ToString(), damage_rect.ToString()); break; case 13: EXPECT_EQ(gfx::RectF(0.f, 0.f, 0.f, 0.f).ToString(), damage_rect.ToString()); break; case 14: EXPECT_EQ(gfx::RectF(2.f, 2.f, 4.f, 4.f).ToString(), damage_rect.ToString()); break; case 15: EXPECT_EQ(gfx::RectF(0.f, 0.f, 0.f, 0.f).ToString(), damage_rect.ToString()); break; case 16: EXPECT_EQ(gfx::RectF(0.f, 0.f, 6.f, 6.f).ToString(), damage_rect.ToString()); break; case 17: EXPECT_EQ(gfx::RectF(0.f, 0.f, 6.f, 6.f).ToString(), damage_rect.ToString()); break; case 18: EXPECT_EQ(gfx::RectF(0.f, 0.f, 6.f, 6.f).ToString(), damage_rect.ToString()); EndTest(); break; } return result; } protected: bool first_draw_for_source_frame_; }; SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostDelegatedTestLayerUsesFrameDamage); class LayerTreeHostDelegatedTestMergeResources : public LayerTreeHostDelegatedTestCaseSingleDelegatedLayer { public: virtual void BeginTest() OVERRIDE { // Push two frames to the delegated renderer layer with no commit between. // The first frame has resource 999. scoped_ptr<DelegatedFrameData> frame1 = CreateFrameData(gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1)); AddTextureQuad(frame1.get(), 999); AddTransferableResource(frame1.get(), 999); delegated_->SetFrameData(frame1.Pass()); // The second frame uses resource 999 still, but also adds 555. scoped_ptr<DelegatedFrameData> frame2 = CreateFrameData(gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1)); AddTextureQuad(frame2.get(), 999); AddTransferableResource(frame2.get(), 999); AddTextureQuad(frame2.get(), 555); AddTransferableResource(frame2.get(), 555); delegated_->SetFrameData(frame2.Pass()); // The resource 999 from frame1 is returned since it is still on the main // thread. ReturnedResourceArray returned_resources; delegated_->TakeUnusedResourcesForChildCompositor(&returned_resources); { unsigned expected[] = {999}; EXPECT_RESOURCES(expected, returned_resources); } PostSetNeedsCommitToMainThread(); } virtual void DidActivateTreeOnThread(LayerTreeHostImpl* host_impl) OVERRIDE { LayerImpl* root_impl = host_impl->active_tree()->root_layer(); FakeDelegatedRendererLayerImpl* delegated_impl = static_cast<FakeDelegatedRendererLayerImpl*>(root_impl->children()[0]); const ResourceProvider::ResourceIdMap& map = host_impl->resource_provider()->GetChildToParentMap( delegated_impl->ChildId()); // Both frames' resources should be in the parent's resource provider. EXPECT_EQ(2u, map.size()); EXPECT_EQ(1u, map.count(999)); EXPECT_EQ(1u, map.count(555)); EXPECT_EQ(2u, delegated_impl->Resources().size()); EXPECT_EQ(1u, delegated_impl->Resources().count(999)); EXPECT_EQ(1u, delegated_impl->Resources().count(555)); EndTest(); } virtual void AfterTest() OVERRIDE {} }; SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostDelegatedTestMergeResources); class LayerTreeHostDelegatedTestRemapResourcesInQuads : public LayerTreeHostDelegatedTestCaseSingleDelegatedLayer { public: virtual void BeginTest() OVERRIDE { // Generate a frame with two resources in it. scoped_ptr<DelegatedFrameData> frame = CreateFrameData(gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1)); AddTextureQuad(frame.get(), 999); AddTransferableResource(frame.get(), 999); AddTextureQuad(frame.get(), 555); AddTransferableResource(frame.get(), 555); delegated_->SetFrameData(frame.Pass()); PostSetNeedsCommitToMainThread(); } virtual void DidActivateTreeOnThread(LayerTreeHostImpl* host_impl) OVERRIDE { LayerImpl* root_impl = host_impl->active_tree()->root_layer(); FakeDelegatedRendererLayerImpl* delegated_impl = static_cast<FakeDelegatedRendererLayerImpl*>(root_impl->children()[0]); const ResourceProvider::ResourceIdMap& map = host_impl->resource_provider()->GetChildToParentMap( delegated_impl->ChildId()); // The frame's resource should be in the parent's resource provider. EXPECT_EQ(2u, map.size()); EXPECT_EQ(1u, map.count(999)); EXPECT_EQ(1u, map.count(555)); ResourceProvider::ResourceId parent_resource_id1 = map.find(999)->second; EXPECT_NE(parent_resource_id1, 999u); ResourceProvider::ResourceId parent_resource_id2 = map.find(555)->second; EXPECT_NE(parent_resource_id2, 555u); // The resources in the quads should be remapped to the parent's namespace. const TextureDrawQuad* quad1 = TextureDrawQuad::MaterialCast( delegated_impl->RenderPassesInDrawOrder()[0]->quad_list[0]); EXPECT_EQ(parent_resource_id1, quad1->resource_id); const TextureDrawQuad* quad2 = TextureDrawQuad::MaterialCast( delegated_impl->RenderPassesInDrawOrder()[0]->quad_list[1]); EXPECT_EQ(parent_resource_id2, quad2->resource_id); EndTest(); } virtual void AfterTest() OVERRIDE {} }; SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostDelegatedTestRemapResourcesInQuads); class LayerTreeHostDelegatedTestReturnUnusedResources : public LayerTreeHostDelegatedTestCaseSingleDelegatedLayer { public: virtual void BeginTest() OVERRIDE { PostSetNeedsCommitToMainThread(); } virtual void DidCommitAndDrawFrame() OVERRIDE { scoped_ptr<DelegatedFrameData> frame; ReturnedResourceArray resources; int next_source_frame_number = layer_tree_host()->source_frame_number(); switch (next_source_frame_number) { case 1: // Generate a frame with two resources in it. frame = CreateFrameData(gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1)); AddTextureQuad(frame.get(), 999); AddTransferableResource(frame.get(), 999); AddTextureQuad(frame.get(), 555); AddTransferableResource(frame.get(), 555); delegated_->SetFrameData(frame.Pass()); break; case 2: // All of the resources are in use. delegated_->TakeUnusedResourcesForChildCompositor(&resources); EXPECT_EQ(0u, resources.size()); // Keep using 999 but stop using 555. frame = CreateFrameData(gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1)); AddTextureQuad(frame.get(), 999); AddTransferableResource(frame.get(), 999); AddTextureQuad(frame.get(), 444); AddTransferableResource(frame.get(), 444); delegated_->SetFrameData(frame.Pass()); break; case 3: // 555 is no longer in use. delegated_->TakeUnusedResourcesForChildCompositor(&resources); { unsigned expected[] = {555}; EXPECT_RESOURCES(expected, resources); } // Stop using any resources. frame = CreateFrameData(gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1)); delegated_->SetFrameData(frame.Pass()); break; case 4: // Postpone collecting resources for a frame. They should still be there // the next frame. layer_tree_host()->SetNeedsCommit(); return; case 5: // 444 and 999 are no longer in use. We sent two refs to 999, so we // should get two back. delegated_->TakeUnusedResourcesForChildCompositor(&resources); { unsigned expected[] = {444, 999, 999}; EXPECT_RESOURCES(expected, resources); } EndTest(); break; } // Resource are never immediately released. ReturnedResourceArray empty_resources; delegated_->TakeUnusedResourcesForChildCompositor(&empty_resources); EXPECT_TRUE(empty_resources.empty()); } virtual void SwapBuffersOnThread(LayerTreeHostImpl* host_impl, bool result) OVERRIDE { ReturnUnusedResourcesFromParent(host_impl); } virtual void AfterTest() OVERRIDE {} }; SINGLE_AND_MULTI_THREAD_TEST_F( LayerTreeHostDelegatedTestReturnUnusedResources); class LayerTreeHostDelegatedTestReusedResources : public LayerTreeHostDelegatedTestCaseSingleDelegatedLayer { public: virtual void BeginTest() OVERRIDE { PostSetNeedsCommitToMainThread(); } virtual void DidCommitAndDrawFrame() OVERRIDE { scoped_ptr<DelegatedFrameData> frame; ReturnedResourceArray resources; int next_source_frame_number = layer_tree_host()->source_frame_number(); switch (next_source_frame_number) { case 1: // Generate a frame with some resources in it. frame = CreateFrameData(gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1)); AddTextureQuad(frame.get(), 999); AddTransferableResource(frame.get(), 999); AddTextureQuad(frame.get(), 555); AddTransferableResource(frame.get(), 555); AddTextureQuad(frame.get(), 444); AddTransferableResource(frame.get(), 444); delegated_->SetFrameData(frame.Pass()); break; case 2: // All of the resources are in use. delegated_->TakeUnusedResourcesForChildCompositor(&resources); EXPECT_EQ(0u, resources.size()); // Keep using 999 but stop using 555 and 444. frame = CreateFrameData(gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1)); AddTextureQuad(frame.get(), 999); AddTransferableResource(frame.get(), 999); delegated_->SetFrameData(frame.Pass()); // Resource are not immediately released. delegated_->TakeUnusedResourcesForChildCompositor(&resources); EXPECT_EQ(0u, resources.size()); // Now using 555 and 444 again, but not 999. frame = CreateFrameData(gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1)); AddTextureQuad(frame.get(), 555); AddTransferableResource(frame.get(), 555); AddTextureQuad(frame.get(), 444); AddTransferableResource(frame.get(), 444); delegated_->SetFrameData(frame.Pass()); break; case 3: // The 999 resource is the only unused one. Two references were sent, so // two should be returned. delegated_->TakeUnusedResourcesForChildCompositor(&resources); { unsigned expected[] = {999, 999}; EXPECT_RESOURCES(expected, resources); } EndTest(); break; } } virtual void SwapBuffersOnThread(LayerTreeHostImpl* host_impl, bool result) OVERRIDE { ReturnUnusedResourcesFromParent(host_impl); } virtual void AfterTest() OVERRIDE {} }; SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostDelegatedTestReusedResources); class LayerTreeHostDelegatedTestFrameBeforeAck : public LayerTreeHostDelegatedTestCaseSingleDelegatedLayer { public: virtual void BeginTest() OVERRIDE { PostSetNeedsCommitToMainThread(); } virtual void DidCommitAndDrawFrame() OVERRIDE { scoped_ptr<DelegatedFrameData> frame; ReturnedResourceArray resources; int next_source_frame_number = layer_tree_host()->source_frame_number(); switch (next_source_frame_number) { case 1: // Generate a frame with some resources in it. frame = CreateFrameData(gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1)); AddTextureQuad(frame.get(), 999); AddTransferableResource(frame.get(), 999); AddTextureQuad(frame.get(), 555); AddTransferableResource(frame.get(), 555); AddTextureQuad(frame.get(), 444); AddTransferableResource(frame.get(), 444); delegated_->SetFrameData(frame.Pass()); break; case 2: // All of the resources are in use. delegated_->TakeUnusedResourcesForChildCompositor(&resources); EXPECT_EQ(0u, resources.size()); // Keep using 999 but stop using 555 and 444. frame = CreateFrameData(gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1)); AddTextureQuad(frame.get(), 999); AddTransferableResource(frame.get(), 999); delegated_->SetFrameData(frame.Pass()); // Resource are not immediately released. delegated_->TakeUnusedResourcesForChildCompositor(&resources); EXPECT_EQ(0u, resources.size()); // The parent compositor (this one) does a commit. break; case 3: delegated_->TakeUnusedResourcesForChildCompositor(&resources); { unsigned expected[] = {444, 555}; EXPECT_RESOURCES(expected, resources); } // The child compositor sends a frame referring to resources not in the // frame. frame = CreateFrameData(gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1)); AddTextureQuad(frame.get(), 999); AddTextureQuad(frame.get(), 555); AddTextureQuad(frame.get(), 444); delegated_->SetFrameData(frame.Pass()); break; } } virtual void DidActivateTreeOnThread(LayerTreeHostImpl* host_impl) OVERRIDE { if (host_impl->active_tree()->source_frame_number() != 3) return; LayerImpl* root_impl = host_impl->active_tree()->root_layer(); FakeDelegatedRendererLayerImpl* delegated_impl = static_cast<FakeDelegatedRendererLayerImpl*>(root_impl->children()[0]); const ResourceProvider::ResourceIdMap& map = host_impl->resource_provider()->GetChildToParentMap( delegated_impl->ChildId()); // The bad frame should be dropped. So we should only have one quad (the // one with resource 999) on the impl tree. And only 999 will be present // in the parent's resource provider. EXPECT_EQ(1u, map.size()); EXPECT_EQ(1u, map.count(999)); EXPECT_EQ(1u, delegated_impl->Resources().size()); EXPECT_EQ(1u, delegated_impl->Resources().count(999)); const RenderPass* pass = delegated_impl->RenderPassesInDrawOrder()[0]; EXPECT_EQ(1u, pass->quad_list.size()); const TextureDrawQuad* quad = TextureDrawQuad::MaterialCast( pass->quad_list[0]); EXPECT_EQ(map.find(999)->second, quad->resource_id); EndTest(); } virtual void SwapBuffersOnThread(LayerTreeHostImpl* host_impl, bool result) OVERRIDE { ReturnUnusedResourcesFromParent(host_impl); } virtual void AfterTest() OVERRIDE {} }; SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostDelegatedTestFrameBeforeAck); class LayerTreeHostDelegatedTestFrameBeforeTakeResources : public LayerTreeHostDelegatedTestCaseSingleDelegatedLayer { public: virtual void BeginTest() OVERRIDE { PostSetNeedsCommitToMainThread(); } virtual void DidCommitAndDrawFrame() OVERRIDE { scoped_ptr<DelegatedFrameData> frame; ReturnedResourceArray resources; int next_source_frame_number = layer_tree_host()->source_frame_number(); switch (next_source_frame_number) { case 1: // Generate a frame with some resources in it. frame = CreateFrameData(gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1)); AddTextureQuad(frame.get(), 999); AddTransferableResource(frame.get(), 999); AddTextureQuad(frame.get(), 555); AddTransferableResource(frame.get(), 555); AddTextureQuad(frame.get(), 444); AddTransferableResource(frame.get(), 444); delegated_->SetFrameData(frame.Pass()); break; case 2: // All of the resources are in use. delegated_->TakeUnusedResourcesForChildCompositor(&resources); EXPECT_EQ(0u, resources.size()); // Keep using 999 but stop using 555 and 444. frame = CreateFrameData(gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1)); AddTextureQuad(frame.get(), 999); AddTransferableResource(frame.get(), 999); delegated_->SetFrameData(frame.Pass()); // Resource are not immediately released. delegated_->TakeUnusedResourcesForChildCompositor(&resources); EXPECT_EQ(0u, resources.size()); // The parent compositor (this one) does a commit. break; case 3: // The child compositor sends a frame before taking resources back // from the previous commit. This frame makes use of the resources 555 // and 444, which were just released during commit. frame = CreateFrameData(gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1)); AddTextureQuad(frame.get(), 999); AddTransferableResource(frame.get(), 999); AddTextureQuad(frame.get(), 555); AddTransferableResource(frame.get(), 555); AddTextureQuad(frame.get(), 444); AddTransferableResource(frame.get(), 444); delegated_->SetFrameData(frame.Pass()); // The resources are used by the new frame but are returned anyway since // we passed them again. delegated_->TakeUnusedResourcesForChildCompositor(&resources); { unsigned expected[] = {444, 555}; EXPECT_RESOURCES(expected, resources); } break; case 4: delegated_->TakeUnusedResourcesForChildCompositor(&resources); EXPECT_EQ(0u, resources.size()); EndTest(); break; } } virtual void DidActivateTreeOnThread(LayerTreeHostImpl* host_impl) OVERRIDE { if (host_impl->active_tree()->source_frame_number() != 3) return; LayerImpl* root_impl = host_impl->active_tree()->root_layer(); FakeDelegatedRendererLayerImpl* delegated_impl = static_cast<FakeDelegatedRendererLayerImpl*>(root_impl->children()[0]); const ResourceProvider::ResourceIdMap& map = host_impl->resource_provider()->GetChildToParentMap( delegated_impl->ChildId()); // The third frame has all of the resources in it again, the delegated // renderer layer should continue to own the resources for it. EXPECT_EQ(3u, map.size()); EXPECT_EQ(1u, map.count(999)); EXPECT_EQ(1u, map.count(555)); EXPECT_EQ(1u, map.count(444)); EXPECT_EQ(3u, delegated_impl->Resources().size()); EXPECT_EQ(1u, delegated_impl->Resources().count(999)); EXPECT_EQ(1u, delegated_impl->Resources().count(555)); EXPECT_EQ(1u, delegated_impl->Resources().count(444)); const RenderPass* pass = delegated_impl->RenderPassesInDrawOrder()[0]; EXPECT_EQ(3u, pass->quad_list.size()); const TextureDrawQuad* quad1 = TextureDrawQuad::MaterialCast( pass->quad_list[0]); EXPECT_EQ(map.find(999)->second, quad1->resource_id); const TextureDrawQuad* quad2 = TextureDrawQuad::MaterialCast( pass->quad_list[1]); EXPECT_EQ(map.find(555)->second, quad2->resource_id); const TextureDrawQuad* quad3 = TextureDrawQuad::MaterialCast( pass->quad_list[2]); EXPECT_EQ(map.find(444)->second, quad3->resource_id); } virtual void SwapBuffersOnThread(LayerTreeHostImpl* host_impl, bool result) OVERRIDE { ReturnUnusedResourcesFromParent(host_impl); } virtual void AfterTest() OVERRIDE {} }; SINGLE_AND_MULTI_THREAD_TEST_F( LayerTreeHostDelegatedTestFrameBeforeTakeResources); class LayerTreeHostDelegatedTestBadFrame : public LayerTreeHostDelegatedTestCaseSingleDelegatedLayer { public: virtual void BeginTest() OVERRIDE { PostSetNeedsCommitToMainThread(); } virtual void DidCommitAndDrawFrame() OVERRIDE { scoped_ptr<DelegatedFrameData> frame; ReturnedResourceArray resources; int next_source_frame_number = layer_tree_host()->source_frame_number(); switch (next_source_frame_number) { case 1: // Generate a frame with some resources in it. frame = CreateFrameData(gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1)); AddTextureQuad(frame.get(), 999); AddTransferableResource(frame.get(), 999); AddTextureQuad(frame.get(), 555); AddTransferableResource(frame.get(), 555); delegated_->SetFrameData(frame.Pass()); break; case 2: // All of the resources are in use. delegated_->TakeUnusedResourcesForChildCompositor(&resources); EXPECT_EQ(0u, resources.size()); // Generate a bad frame with a resource the layer doesn't have. The // 885 and 775 resources are unknown, while ownership of the legit 444 // resource is passed in here. The bad frame does not use any of the // previous resources, 999 or 555. // A bad quad is present both before and after the good quad. frame = CreateFrameData(gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1)); AddTextureQuad(frame.get(), 885); AddTextureQuad(frame.get(), 444); AddTransferableResource(frame.get(), 444); AddTextureQuad(frame.get(), 775); delegated_->SetFrameData(frame.Pass()); // The parent compositor (this one) does a commit. break; case 3: // The bad frame's resource is given back to the child compositor. delegated_->TakeUnusedResourcesForChildCompositor(&resources); { unsigned expected[] = {444}; EXPECT_RESOURCES(expected, resources); } // Now send a good frame with 999 again. frame = CreateFrameData(gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1)); AddTextureQuad(frame.get(), 999); delegated_->SetFrameData(frame.Pass()); break; case 4: // The unused 555 from the last good frame is now released. delegated_->TakeUnusedResourcesForChildCompositor(&resources); { unsigned expected[] = {555}; EXPECT_RESOURCES(expected, resources); } EndTest(); break; } } virtual void SwapBuffersOnThread(LayerTreeHostImpl* host_impl, bool result) OVERRIDE { if (host_impl->active_tree()->source_frame_number() < 1) return; ReturnUnusedResourcesFromParent(host_impl); LayerImpl* root_impl = host_impl->active_tree()->root_layer(); FakeDelegatedRendererLayerImpl* delegated_impl = static_cast<FakeDelegatedRendererLayerImpl*>(root_impl->children()[0]); const ResourceProvider::ResourceIdMap& map = host_impl->resource_provider()->GetChildToParentMap( delegated_impl->ChildId()); switch (host_impl->active_tree()->source_frame_number()) { case 1: { // We have the first good frame with just 990 and 555 in it. // layer. EXPECT_EQ(2u, map.size()); EXPECT_EQ(1u, map.count(999)); EXPECT_EQ(1u, map.count(555)); EXPECT_EQ(2u, delegated_impl->Resources().size()); EXPECT_EQ(1u, delegated_impl->Resources().count(999)); EXPECT_EQ(1u, delegated_impl->Resources().count(555)); const RenderPass* pass = delegated_impl->RenderPassesInDrawOrder()[0]; EXPECT_EQ(2u, pass->quad_list.size()); const TextureDrawQuad* quad1 = TextureDrawQuad::MaterialCast( pass->quad_list[0]); EXPECT_EQ(map.find(999)->second, quad1->resource_id); const TextureDrawQuad* quad2 = TextureDrawQuad::MaterialCast( pass->quad_list[1]); EXPECT_EQ(map.find(555)->second, quad2->resource_id); break; } case 2: { // We only keep resources from the last valid frame. EXPECT_EQ(2u, map.size()); EXPECT_EQ(1u, map.count(999)); EXPECT_EQ(1u, map.count(555)); EXPECT_EQ(2u, delegated_impl->Resources().size()); EXPECT_EQ(1u, delegated_impl->Resources().count(999)); EXPECT_EQ(1u, delegated_impl->Resources().count(555)); // The bad frame is dropped though, we still have the frame with 999 and // 555 in it. const RenderPass* pass = delegated_impl->RenderPassesInDrawOrder()[0]; EXPECT_EQ(2u, pass->quad_list.size()); const TextureDrawQuad* quad1 = TextureDrawQuad::MaterialCast( pass->quad_list[0]); EXPECT_EQ(map.find(999)->second, quad1->resource_id); const TextureDrawQuad* quad2 = TextureDrawQuad::MaterialCast( pass->quad_list[1]); EXPECT_EQ(map.find(555)->second, quad2->resource_id); break; } case 3: { // We have the new good frame with just 999 in it. EXPECT_EQ(1u, map.size()); EXPECT_EQ(1u, map.count(999)); EXPECT_EQ(1u, delegated_impl->Resources().size()); EXPECT_EQ(1u, delegated_impl->Resources().count(999)); const RenderPass* pass = delegated_impl->RenderPassesInDrawOrder()[0]; EXPECT_EQ(1u, pass->quad_list.size()); const TextureDrawQuad* quad1 = TextureDrawQuad::MaterialCast( pass->quad_list[0]); EXPECT_EQ(map.find(999)->second, quad1->resource_id); break; } } } virtual void AfterTest() OVERRIDE {} }; SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostDelegatedTestBadFrame); class LayerTreeHostDelegatedTestUnnamedResource : public LayerTreeHostDelegatedTestCaseSingleDelegatedLayer { public: virtual void BeginTest() OVERRIDE { PostSetNeedsCommitToMainThread(); } virtual void DidCommit() OVERRIDE { scoped_ptr<DelegatedFrameData> frame; ReturnedResourceArray resources; int next_source_frame_number = layer_tree_host()->source_frame_number(); switch (next_source_frame_number) { case 1: // This frame includes two resources in it, but only uses one. frame = CreateFrameData(gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1)); AddTransferableResource(frame.get(), 999); AddTextureQuad(frame.get(), 555); AddTransferableResource(frame.get(), 555); delegated_->SetFrameData(frame.Pass()); break; case 2: // The unused resource should be returned. delegated_->TakeUnusedResourcesForChildCompositor(&resources); { unsigned expected[] = {999}; EXPECT_RESOURCES(expected, resources); } EndTest(); break; } } virtual void DidActivateTreeOnThread(LayerTreeHostImpl* host_impl) OVERRIDE { if (host_impl->active_tree()->source_frame_number() != 1) return; LayerImpl* root_impl = host_impl->active_tree()->root_layer(); FakeDelegatedRendererLayerImpl* delegated_impl = static_cast<FakeDelegatedRendererLayerImpl*>(root_impl->children()[0]); const ResourceProvider::ResourceIdMap& map = host_impl->resource_provider()->GetChildToParentMap( delegated_impl->ChildId()); // The layer only held on to the resource that was used. EXPECT_EQ(1u, map.size()); EXPECT_EQ(1u, map.count(555)); EXPECT_EQ(1u, delegated_impl->Resources().size()); EXPECT_EQ(1u, delegated_impl->Resources().count(555)); } virtual void AfterTest() OVERRIDE {} }; SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostDelegatedTestUnnamedResource); class LayerTreeHostDelegatedTestDontLeakResource : public LayerTreeHostDelegatedTestCaseSingleDelegatedLayer { public: virtual void BeginTest() OVERRIDE { PostSetNeedsCommitToMainThread(); } virtual void DidCommitAndDrawFrame() OVERRIDE { scoped_ptr<DelegatedFrameData> frame; ReturnedResourceArray resources; int next_source_frame_number = layer_tree_host()->source_frame_number(); switch (next_source_frame_number) { case 1: // This frame includes two resources in it. frame = CreateFrameData(gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1)); AddTextureQuad(frame.get(), 999); AddTransferableResource(frame.get(), 999); AddTextureQuad(frame.get(), 555); AddTransferableResource(frame.get(), 555); delegated_->SetFrameData(frame.Pass()); // But then we immediately stop using 999. frame = CreateFrameData(gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1)); AddTextureQuad(frame.get(), 555); AddTransferableResource(frame.get(), 555); delegated_->SetFrameData(frame.Pass()); break; case 2: // The unused resources should be returned. 555 is still used, but it's // returned once to account for the first frame. delegated_->TakeUnusedResourcesForChildCompositor(&resources); { unsigned expected[] = {555, 999}; EXPECT_RESOURCES(expected, resources); } // Send a frame with no resources in it. frame = CreateFrameData(gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1)); delegated_->SetFrameData(frame.Pass()); break; case 3: // The now unused resource 555 should be returned. resources.clear(); delegated_->TakeUnusedResourcesForChildCompositor(&resources); { unsigned expected[] = {555}; EXPECT_RESOURCES(expected, resources); } EndTest(); break; } } virtual void DidActivateTreeOnThread(LayerTreeHostImpl* host_impl) OVERRIDE { if (host_impl->active_tree()->source_frame_number() != 1) return; LayerImpl* root_impl = host_impl->active_tree()->root_layer(); FakeDelegatedRendererLayerImpl* delegated_impl = static_cast<FakeDelegatedRendererLayerImpl*>(root_impl->children()[0]); const ResourceProvider::ResourceIdMap& map = host_impl->resource_provider()->GetChildToParentMap( delegated_impl->ChildId()); // The layer only held on to the resource that was used. EXPECT_EQ(1u, map.size()); EXPECT_EQ(1u, map.count(555)); EXPECT_EQ(1u, delegated_impl->Resources().size()); EXPECT_EQ(1u, delegated_impl->Resources().count(555)); } virtual void SwapBuffersOnThread(LayerTreeHostImpl* host_impl, bool result) OVERRIDE { ReturnUnusedResourcesFromParent(host_impl); } virtual void AfterTest() OVERRIDE {} }; SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostDelegatedTestDontLeakResource); class LayerTreeHostDelegatedTestResourceSentToParent : public LayerTreeHostDelegatedTestCaseSingleDelegatedLayer { public: virtual void DidCommitAndDrawFrame() OVERRIDE { scoped_ptr<DelegatedFrameData> frame; ReturnedResourceArray resources; int next_source_frame_number = layer_tree_host()->source_frame_number(); switch (next_source_frame_number) { case 1: // This frame includes two resources in it. frame = CreateFrameData(gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1)); AddTextureQuad(frame.get(), 999); AddTransferableResource(frame.get(), 999); AddTextureQuad(frame.get(), 555); AddTransferableResource(frame.get(), 555); delegated_->SetFrameData(frame.Pass()); break; case 2: delegated_->TakeUnusedResourcesForChildCompositor(&resources); EXPECT_EQ(0u, resources.size()); // 999 is in use in the grandparent compositor, generate a frame without // it present. frame = CreateFrameData(gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1)); AddTextureQuad(frame.get(), 555); AddTransferableResource(frame.get(), 555); delegated_->SetFrameData(frame.Pass()); break; case 3: // Since 999 is in the grandparent it is not returned. delegated_->TakeUnusedResourcesForChildCompositor(&resources); EXPECT_EQ(0u, resources.size()); // The impl side will get back the resource at some point. ImplThreadTaskRunner()->PostTask(FROM_HERE, receive_resource_on_thread_); break; } } void ReceiveResourceOnThread(LayerTreeHostImpl* host_impl) { LayerImpl* root_impl = host_impl->active_tree()->root_layer(); FakeDelegatedRendererLayerImpl* delegated_impl = static_cast<FakeDelegatedRendererLayerImpl*>(root_impl->children()[0]); const ResourceProvider::ResourceIdMap& map = host_impl->resource_provider()->GetChildToParentMap( delegated_impl->ChildId()); // Receive 999 back from the grandparent. CompositorFrameAck ack; output_surface()->ReturnResource(map.find(999)->second, &ack); host_impl->ReclaimResources(&ack); host_impl->OnSwapBuffersComplete(); // And then it should be released by the DelegatedRendererLayer. MainThreadTaskRunner()->PostTask( FROM_HERE, base::Bind(&LayerTreeHostDelegatedTestResourceSentToParent:: DidReceiveResourceOnMainThread, base::Unretained(this))); } void DidReceiveResourceOnMainThread() { ReturnedResourceArray resources; // 999 was returned from the grandparent and could be released. delegated_->TakeUnusedResourcesForChildCompositor(&resources); { unsigned expected[] = {999}; EXPECT_RESOURCES(expected, resources); } EndTest(); } virtual void DidActivateTreeOnThread(LayerTreeHostImpl* host_impl) OVERRIDE { if (host_impl->active_tree()->source_frame_number() < 1) return; LayerImpl* root_impl = host_impl->active_tree()->root_layer(); FakeDelegatedRendererLayerImpl* delegated_impl = static_cast<FakeDelegatedRendererLayerImpl*>(root_impl->children()[0]); const ResourceProvider::ResourceIdMap& map = host_impl->resource_provider()->GetChildToParentMap( delegated_impl->ChildId()); switch (host_impl->active_tree()->source_frame_number()) { case 1: { EXPECT_EQ(2u, map.size()); EXPECT_EQ(1u, map.count(999)); EXPECT_EQ(1u, map.count(555)); EXPECT_EQ(2u, delegated_impl->Resources().size()); EXPECT_EQ(1u, delegated_impl->Resources().count(999)); EXPECT_EQ(1u, delegated_impl->Resources().count(555)); // The 999 resource will be sent to a grandparent compositor. break; } case 2: { EXPECT_EQ(2u, map.size()); EXPECT_EQ(1u, map.count(999)); EXPECT_EQ(1u, map.count(555)); // 999 is in the parent, so not held by delegated renderer layer. EXPECT_EQ(1u, delegated_impl->Resources().size()); EXPECT_EQ(1u, delegated_impl->Resources().count(555)); receive_resource_on_thread_ = base::Bind(&LayerTreeHostDelegatedTestResourceSentToParent:: ReceiveResourceOnThread, base::Unretained(this), host_impl); break; } case 3: // 999 should be released. EXPECT_EQ(1u, map.size()); EXPECT_EQ(1u, map.count(555)); EXPECT_EQ(1u, delegated_impl->Resources().size()); EXPECT_EQ(1u, delegated_impl->Resources().count(map.find(555)->second)); break; } } virtual void AfterTest() OVERRIDE {} base::Closure receive_resource_on_thread_; }; SINGLE_AND_MULTI_THREAD_DELEGATING_RENDERER_TEST_F( LayerTreeHostDelegatedTestResourceSentToParent); class LayerTreeHostDelegatedTestCommitWithoutTake : public LayerTreeHostDelegatedTestCaseSingleDelegatedLayer { public: virtual void BeginTest() OVERRIDE { // Prevent drawing with resources that are sent to the grandparent. layer_tree_host()->SetViewportSize(gfx::Size()); PostSetNeedsCommitToMainThread(); } virtual void DidCommit() OVERRIDE { scoped_ptr<DelegatedFrameData> frame; ReturnedResourceArray resources; int next_source_frame_number = layer_tree_host()->source_frame_number(); switch (next_source_frame_number) { case 1: frame = CreateFrameData(gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1)); AddTextureQuad(frame.get(), 999); AddTransferableResource(frame.get(), 999); AddTextureQuad(frame.get(), 555); AddTransferableResource(frame.get(), 555); AddTextureQuad(frame.get(), 444); AddTransferableResource(frame.get(), 444); delegated_->SetFrameData(frame.Pass()); break; case 2: delegated_->TakeUnusedResourcesForChildCompositor(&resources); EXPECT_EQ(0u, resources.size()); // Stop using 999 and 444 in this frame and commit. frame = CreateFrameData(gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1)); AddTextureQuad(frame.get(), 555); AddTransferableResource(frame.get(), 555); delegated_->SetFrameData(frame.Pass()); // 999 and 444 will be returned for frame 1, but not 555 since it's in // the current frame. break; case 3: // Don't take resources here, but set a new frame that uses 999 again. frame = CreateFrameData(gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1)); AddTextureQuad(frame.get(), 999); AddTransferableResource(frame.get(), 999); AddTextureQuad(frame.get(), 555); AddTransferableResource(frame.get(), 555); delegated_->SetFrameData(frame.Pass()); break; case 4: // 555 from frame 1 and 2 isn't returned since it's still in use. 999 // from frame 1 is returned though. delegated_->TakeUnusedResourcesForChildCompositor(&resources); { unsigned expected[] = {444, 999}; EXPECT_RESOURCES(expected, resources); } frame = CreateFrameData(gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1)); delegated_->SetFrameData(frame.Pass()); // 555 will be returned 3 times for frames 1 2 and 3, and 999 will be // returned once for frame 3. break; case 5: delegated_->TakeUnusedResourcesForChildCompositor(&resources); { unsigned expected[] = {555, 555, 555, 999}; EXPECT_RESOURCES(expected, resources); } EndTest(); break; } } virtual void DidActivateTreeOnThread(LayerTreeHostImpl* host_impl) OVERRIDE { if (host_impl->active_tree()->source_frame_number() < 1) return; LayerImpl* root_impl = host_impl->active_tree()->root_layer(); FakeDelegatedRendererLayerImpl* delegated_impl = static_cast<FakeDelegatedRendererLayerImpl*>(root_impl->children()[0]); const ResourceProvider::ResourceIdMap& map = host_impl->resource_provider()->GetChildToParentMap( delegated_impl->ChildId()); switch (host_impl->active_tree()->source_frame_number()) { case 1: EXPECT_EQ(3u, map.size()); EXPECT_EQ(1u, map.count(999)); EXPECT_EQ(1u, map.count(555)); EXPECT_EQ(1u, map.count(444)); EXPECT_EQ(3u, delegated_impl->Resources().size()); EXPECT_EQ(1u, delegated_impl->Resources().count(999)); EXPECT_EQ(1u, delegated_impl->Resources().count(555)); EXPECT_EQ(1u, delegated_impl->Resources().count(444)); break; case 2: EXPECT_EQ(1u, map.size()); EXPECT_EQ(1u, map.count(555)); EXPECT_EQ(1u, delegated_impl->Resources().size()); EXPECT_EQ(1u, delegated_impl->Resources().count(555)); break; case 3: EXPECT_EQ(2u, map.size()); EXPECT_EQ(1u, map.count(999)); EXPECT_EQ(1u, map.count(555)); EXPECT_EQ(2u, delegated_impl->Resources().size()); EXPECT_EQ(1u, delegated_impl->Resources().count(999)); EXPECT_EQ(1u, delegated_impl->Resources().count(555)); } } virtual void AfterTest() OVERRIDE {} }; SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostDelegatedTestCommitWithoutTake); class DelegatedFrameIsActivatedDuringCommit : public LayerTreeHostDelegatedTestCaseSingleDelegatedLayer { protected: DelegatedFrameIsActivatedDuringCommit() : wait_thread_("WAIT"), wait_event_(false, false) { wait_thread_.Start(); } virtual void BeginTest() OVERRIDE { activate_count_ = 0; scoped_ptr<DelegatedFrameData> frame = CreateFrameData(gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1)); AddTextureQuad(frame.get(), 999); AddTransferableResource(frame.get(), 999); delegated_->SetFrameData(frame.Pass()); PostSetNeedsCommitToMainThread(); } virtual void WillActivateTreeOnThread(LayerTreeHostImpl* impl) OVERRIDE { // Slow down activation so the main thread DidCommit() will run if // not blocked. wait_thread_.message_loop()->PostDelayedTask( FROM_HERE, base::Bind(&base::WaitableEvent::Signal, base::Unretained(&wait_event_)), base::TimeDelta::FromMilliseconds(10)); wait_event_.Wait(); base::AutoLock lock(activate_lock_); ++activate_count_; } virtual void DidActivateTreeOnThread(LayerTreeHostImpl* impl) OVERRIDE { // The main thread is awake now, and will run DidCommit() immediately. // Run DidActivate() afterwards by posting it now. proxy()->MainThreadTaskRunner()->PostTask( FROM_HERE, base::Bind(&DelegatedFrameIsActivatedDuringCommit::DidActivate, base::Unretained(this))); } void DidActivate() { base::AutoLock lock(activate_lock_); switch (activate_count_) { case 1: { // The first frame has been activated. Set a new frame, and // expect the next commit to finish *after* it is activated. scoped_ptr<DelegatedFrameData> frame = CreateFrameData(gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1)); AddTextureQuad(frame.get(), 555); AddTransferableResource(frame.get(), 555); delegated_->SetFrameData(frame.Pass()); // So this commit number should complete after the second activate. EXPECT_EQ(1, layer_tree_host()->source_frame_number()); break; } case 2: // The second frame has been activated. Remove the layer from // the tree to cause another commit/activation. The commit should // finish *after* the layer is removed from the active tree. delegated_->RemoveFromParent(); // So this commit number should complete after the third activate. EXPECT_EQ(2, layer_tree_host()->source_frame_number()); break; case 3: EndTest(); break; } } virtual void DidCommit() OVERRIDE { switch (layer_tree_host()->source_frame_number()) { case 2: { // The activate for the 2nd frame should have happened before now. base::AutoLock lock(activate_lock_); EXPECT_EQ(2, activate_count_); break; } case 3: { // The activate to remove the layer should have happened before now. base::AutoLock lock(activate_lock_); EXPECT_EQ(3, activate_count_); break; } } } virtual void AfterTest() OVERRIDE {} base::Thread wait_thread_; base::WaitableEvent wait_event_; base::Lock activate_lock_; int activate_count_; }; SINGLE_AND_MULTI_THREAD_TEST_F( DelegatedFrameIsActivatedDuringCommit); } // namespace } // namespace cc