blob: 5363d683e58603003aa753aedce08005b4e6d6c9 [file] [log] [blame]
// 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/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->OnSwapBuffersComplete(&ack);
}
};
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;
}
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());
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(map.find(999)->second));
EXPECT_EQ(1u, delegated_impl->Resources().count(map.find(555)->second));
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:
// Retrieve unused resources to the main thread.
// TODO(danakj): Shouldn't need to commit to get resources.
layer_tree_host()->SetNeedsCommit();
return;
case 3:
// 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);
AddTextureQuad(frame.get(), 444);
AddTransferableResource(frame.get(), 444);
delegated_->SetFrameData(frame.Pass());
break;
case 4:
// Retrieve unused resources to the main thread.
// TODO(danakj): Shouldn't need to commit to get resources.
layer_tree_host()->SetNeedsCommit();
return;
case 5:
// 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 6:
// Postpone collecting resources for a frame. They should still be there
// the next frame.
layer_tree_host()->SetNeedsCommit();
return;
case 7:
// 444 and 999 are no longer in use.
delegated_->TakeUnusedResourcesForChildCompositor(&resources);
{
unsigned expected[] = {444, 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:
// Retrieve unused resources to the main thread.
// TODO(danakj): Shouldn't need to commit to get resources.
layer_tree_host()->SetNeedsCommit();
return;
case 3:
// 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);
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);
AddTextureQuad(frame.get(), 444);
delegated_->SetFrameData(frame.Pass());
break;
case 4:
// Retrieve unused resources to the main thread.
// TODO(danakj): Shouldn't need to commit to get resources.
layer_tree_host()->SetNeedsCommit();
return;
case 5:
// The 999 resource is the only unused one.
delegated_->TakeUnusedResourcesForChildCompositor(&resources);
{
unsigned expected[] = {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:
// Retrieve unused resources to the main thread.
// TODO(danakj): Shouldn't need to commit to get resources.
layer_tree_host()->SetNeedsCommit();
return;
case 3:
// 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);
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 4:
// Retrieve unused resources to the main thread.
// TODO(danakj): Shouldn't need to commit to get resources.
layer_tree_host()->SetNeedsCommit();
return;
case 5:
delegated_->TakeUnusedResourcesForChildCompositor(&resources);
{
unsigned expected[] = {444, 555};
EXPECT_RESOURCES(expected, resources);
}
// The child compositor sends a frame before receiving an for the
// second frame. It uses 999, 444, and 555 again.
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() != 5)
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(map.find(999)->second));
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:
// Retrieve unused resources to the main thread.
// TODO(danakj): Shouldn't need to commit to get resources.
layer_tree_host()->SetNeedsCommit();
return;
case 3:
// 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 4:
// Retrieve unused resources to the main thread.
// TODO(danakj): Shouldn't need to commit to get resources.
layer_tree_host()->SetNeedsCommit();
return;
case 5:
// 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 6:
// Retrieve unused resources to the main thread.
// TODO(danakj): Shouldn't need to commit to get resources.
layer_tree_host()->SetNeedsCommit();
return;
case 7:
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() != 5)
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(map.find(999)->second));
EXPECT_EQ(1u, delegated_impl->Resources().count(map.find(555)->second));
EXPECT_EQ(1u, delegated_impl->Resources().count(map.find(444)->second));
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:
// Retrieve unused resources to the main thread.
// TODO(danakj): Shouldn't need to commit to get resources.
layer_tree_host()->SetNeedsCommit();
return;
case 3:
// 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 4:
// Retrieve unused resources to the main thread.
// TODO(danakj): Shouldn't need to commit to get resources.
layer_tree_host()->SetNeedsCommit();
return;
case 5:
// 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 6:
// Retrieve unused resources to the main thread.
// TODO(danakj): Shouldn't need to commit to get resources.
layer_tree_host()->SetNeedsCommit();
return;
case 7:
// 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(map.find(999)->second));
EXPECT_EQ(1u, delegated_impl->Resources().count(map.find(555)->second));
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 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(map.find(999)->second));
EXPECT_EQ(1u, delegated_impl->Resources().count(map.find(555)->second));
// 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 5:
// Resources given to our parent compositor will be returned now, but
// the DelegatedRendererLayerImpl doesn't know about it until the next
// commit.
// TODO(danakj): Shouldn't need a commit to return resources to the
// DelegatedRendererLayerImpl or to the main thread.
break;
case 6: {
// 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(map.find(999)->second));
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(map.find(555)->second));
}
virtual void AfterTest() OVERRIDE {}
};
SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostDelegatedTestUnnamedResource);
class LayerTreeHostDelegatedTestDontLeakResource
: 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.
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 impl side will get back the resource at some point.
// TODO(piman): The test should work without this.
layer_tree_host()->SetNeedsCommit();
break;
case 4:
// 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(map.find(555)->second));
}
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);
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.
// TODO(danakj): The test should work without this.
layer_tree_host()->SetNeedsCommit();
break;
case 4:
// 999 was returned from the grandparent and could be released.
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());
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(map.find(999)->second));
EXPECT_EQ(1u, delegated_impl->Resources().count(map.find(555)->second));
// 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(map.find(555)->second));
// Receive 999 back from the grandparent.
CompositorFrameAck ack;
output_surface()->ReturnResource(map.find(999)->second, &ack);
host_impl->OnSwapBuffersComplete(&ack);
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 {}
TransferableResource resource_in_grandparent;
};
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(map.find(999)->second));
EXPECT_EQ(1u, delegated_impl->Resources().count(map.find(555)->second));
EXPECT_EQ(1u, delegated_impl->Resources().count(map.find(444)->second));
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(map.find(555)->second));
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(map.find(999)->second));
EXPECT_EQ(1u, delegated_impl->Resources().count(map.find(555)->second));
}
}
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