diff options
Diffstat (limited to 'chromium/cc/layers')
59 files changed, 4448 insertions, 1909 deletions
diff --git a/chromium/cc/layers/compositing_reasons.h b/chromium/cc/layers/compositing_reasons.h index 28c2f7b4dc6..02354a03283 100644 --- a/chromium/cc/layers/compositing_reasons.h +++ b/chromium/cc/layers/compositing_reasons.h @@ -22,7 +22,6 @@ const uint64 kCompositingReasonFilters = GG_UINT64_C(1) << 7; const uint64 kCompositingReasonPositionFixed = GG_UINT64_C(1) << 8; const uint64 kCompositingReasonPositionSticky = GG_UINT64_C(1) << 9; const uint64 kCompositingReasonOverflowScrollingTouch = GG_UINT64_C(1) << 10; -const uint64 kCompositingReasonBlending = GG_UINT64_C(1) << 11; const uint64 kCompositingReasonAssumedOverlap = GG_UINT64_C(1) << 12; const uint64 kCompositingReasonOverlap = GG_UINT64_C(1) << 13; const uint64 kCompositingReasonNegativeZIndexChildren = GG_UINT64_C(1) << 14; @@ -52,6 +51,8 @@ const uint64 kCompositingReasonLayerForScrollingContainer = const uint64 kCompositingReasonLayerForForeground = GG_UINT64_C(1) << 29; const uint64 kCompositingReasonLayerForBackground = GG_UINT64_C(1) << 30; const uint64 kCompositingReasonLayerForMask = GG_UINT64_C(1) << 31; +const uint64 kCompositingReasonOverflowScrollingParent = GG_UINT64_C(1) << 32; +const uint64 kCompositingReasonOutOfFlowClipping = GG_UINT64_C(1) << 33; typedef uint64 CompositingReasons; diff --git a/chromium/cc/layers/content_layer.cc b/chromium/cc/layers/content_layer.cc index 2053ecbeac1..3d611eeef28 100644 --- a/chromium/cc/layers/content_layer.cc +++ b/chromium/cc/layers/content_layer.cc @@ -29,6 +29,12 @@ void ContentLayerPainter::Paint(SkCanvas* canvas, base::TimeTicks paint_start = base::TimeTicks::HighResNow(); client_->PaintContents(canvas, content_rect, opaque); base::TimeTicks paint_end = base::TimeTicks::HighResNow(); + // The start and end times might be the same if the paint was very fast or if + // our timer granularity is poor. Treat this as a very short time duration + // instead of none to avoid dividing by zero. + if (paint_end == paint_start) + paint_end += base::TimeDelta::FromMicroseconds(1); + double pixels_per_sec = (content_rect.width() * content_rect.height()) / (paint_end - paint_start).InSecondsF(); UMA_HISTOGRAM_CUSTOM_COUNTS("Renderer4.AccelContentPaintDurationMS", @@ -121,9 +127,8 @@ void ContentLayer::CreateUpdaterIfNeeded() { } updater_->SetOpaque(contents_opaque()); - unsigned texture_format = - layer_tree_host()->GetRendererCapabilities().best_texture_format; - SetTextureFormat(texture_format); + SetTextureFormat( + layer_tree_host()->GetRendererCapabilities().best_texture_format); } void ContentLayer::SetContentsOpaque(bool opaque) { @@ -145,4 +150,19 @@ bool ContentLayer::SupportsLCDText() const { return true; } +skia::RefPtr<SkPicture> ContentLayer::GetPicture() const { + if (!DrawsContent()) + return skia::RefPtr<SkPicture>(); + + int width = bounds().width(); + int height = bounds().height(); + gfx::RectF opaque; + + skia::RefPtr<SkPicture> picture = skia::AdoptRef(new SkPicture); + SkCanvas* canvas = picture->beginRecording(width, height); + client_->PaintContents(canvas, gfx::Rect(width, height), &opaque); + picture->endRecording(); + return picture; +} + } // namespace cc diff --git a/chromium/cc/layers/content_layer.h b/chromium/cc/layers/content_layer.h index f49215b6b40..a076c31dcac 100644 --- a/chromium/cc/layers/content_layer.h +++ b/chromium/cc/layers/content_layer.h @@ -52,6 +52,8 @@ class CC_EXPORT ContentLayer : public TiledLayer { virtual bool SupportsLCDText() const OVERRIDE; + virtual skia::RefPtr<SkPicture> GetPicture() const OVERRIDE; + protected: explicit ContentLayer(ContentLayerClient* client); virtual ~ContentLayer(); diff --git a/chromium/cc/layers/delegated_renderer_layer.cc b/chromium/cc/layers/delegated_renderer_layer.cc index 66064d9e849..cc49c753e25 100644 --- a/chromium/cc/layers/delegated_renderer_layer.cc +++ b/chromium/cc/layers/delegated_renderer_layer.cc @@ -7,6 +7,9 @@ #include "cc/layers/delegated_renderer_layer_client.h" #include "cc/layers/delegated_renderer_layer_impl.h" #include "cc/output/delegated_frame_data.h" +#include "cc/quads/render_pass_draw_quad.h" +#include "cc/trees/blocking_task_runner.h" +#include "cc/trees/layer_tree_host.h" namespace cc { @@ -19,7 +22,10 @@ scoped_refptr<DelegatedRendererLayer> DelegatedRendererLayer::Create( DelegatedRendererLayer::DelegatedRendererLayer( DelegatedRendererLayerClient* client) : Layer(), - client_(client) {} + client_(client), + needs_filter_context_(false), + main_thread_runner_(BlockingTaskRunner::current()), + weak_ptrs_(this) {} DelegatedRendererLayer::~DelegatedRendererLayer() {} @@ -29,6 +35,26 @@ scoped_ptr<LayerImpl> DelegatedRendererLayer::CreateLayerImpl( tree_impl, layer_id_).PassAs<LayerImpl>(); } +void DelegatedRendererLayer::SetLayerTreeHost(LayerTreeHost* host) { + if (layer_tree_host() == host) { + Layer::SetLayerTreeHost(host); + return; + } + + if (!host) { + // The active frame needs to be removed from the active tree and resources + // returned before the commit is called complete. + // TODO(danakj): Don't need to do this if the last frame commited was empty + // or we never commited a frame with resources. + SetNextCommitWaitsForActivation(); + } else { + if (needs_filter_context_) + host->set_needs_filter_context(); + } + + Layer::SetLayerTreeHost(host); +} + bool DelegatedRendererLayer::DrawsContent() const { return Layer::DrawsContent() && !frame_size_.IsEmpty(); } @@ -41,19 +67,23 @@ void DelegatedRendererLayer::PushPropertiesTo(LayerImpl* impl) { delegated_impl->SetDisplaySize(display_size_); + delegated_impl->CreateChildIdIfNeeded( + base::Bind(&DelegatedRendererLayer::ReceiveUnusedResourcesOnImplThread, + main_thread_runner_, + weak_ptrs_.GetWeakPtr())); + if (frame_data_) delegated_impl->SetFrameData(frame_data_.Pass(), damage_in_frame_); frame_data_.reset(); damage_in_frame_ = gfx::RectF(); - delegated_impl->CollectUnusedResources( - &unused_resources_for_child_compositor_); - + // The ResourceProvider will have the new frame as soon as we push it to the + // pending tree. So unused resources will be returned as well. if (client_) client_->DidCommitFrameData(); - // TODO(danakj): TakeUnusedResourcesForChildCompositor requires a push - // properties to happen in order to collect unused resources returned + // TODO(danakj): The DidCommitFrameData() notification requires a push + // properties to happen in order to notify about unused resources returned // from the parent compositor. crbug.com/259090 needs_push_properties_ = true; } @@ -67,48 +97,74 @@ void DelegatedRendererLayer::SetDisplaySize(gfx::Size size) { void DelegatedRendererLayer::SetFrameData( scoped_ptr<DelegatedFrameData> new_frame_data) { + DCHECK(new_frame_data); + if (frame_data_) { - // Copy the resources from the last provided frame into the new frame, as - // it may use resources that were transferred in the last frame. - new_frame_data->resource_list.insert(new_frame_data->resource_list.end(), - frame_data_->resource_list.begin(), - frame_data_->resource_list.end()); + // Copy the resources from the last provided frame into the unused resources + // list, as the new frame will provide its own resources. + TransferableResource::ReturnResources( + frame_data_->resource_list, + &unused_resources_for_child_compositor_); } frame_data_ = new_frame_data.Pass(); if (!frame_data_->render_pass_list.empty()) { RenderPass* root_pass = frame_data_->render_pass_list.back(); damage_in_frame_.Union(root_pass->damage_rect); frame_size_ = root_pass->output_rect.size(); - - // TODO(danakj): This could be optimized to only add resources to the - // frame_data_ if they are actually used in the frame. For now, it will - // cause the parent (this layer) to hold onto some resources it doesn't - // need to for an extra frame. - for (size_t i = 0; i < unused_resources_for_child_compositor_.size(); ++i) { - frame_data_->resource_list.push_back( - unused_resources_for_child_compositor_[i]); - } - unused_resources_for_child_compositor_.clear(); } else { frame_size_ = gfx::Size(); } + + // If any RenderPassDrawQuad has a filter operation, then we need a filter + // context to draw this layer's content. + for (size_t i = 0; + !needs_filter_context_ && i < frame_data_->render_pass_list.size(); + ++i) { + const QuadList& quad_list = frame_data_->render_pass_list[i]->quad_list; + for (size_t j = 0; !needs_filter_context_ && j < quad_list.size(); ++j) { + if (quad_list[j]->material != DrawQuad::RENDER_PASS) + continue; + const RenderPassDrawQuad* render_pass_quad = + RenderPassDrawQuad::MaterialCast(quad_list[j]); + if (!render_pass_quad->filters.IsEmpty() || + !render_pass_quad->background_filters.IsEmpty()) + needs_filter_context_ = true; + } + } + if (needs_filter_context_ && layer_tree_host()) + layer_tree_host()->set_needs_filter_context(); + SetNeedsCommit(); + // The active frame needs to be replaced and resources returned before the + // commit is called complete. + SetNextCommitWaitsForActivation(); } void DelegatedRendererLayer::TakeUnusedResourcesForChildCompositor( - TransferableResourceArray* array) { + ReturnedResourceArray* array) { DCHECK(array->empty()); array->clear(); array->swap(unused_resources_for_child_compositor_); } -bool DelegatedRendererLayer::BlocksPendingCommit() const { - // The active frame needs to be replaced and resources returned before the - // commit is called complete. This is true even whenever there may be - // resources to return, regardless of if the layer will draw in its new - // state. - return true; +void DelegatedRendererLayer::ReceiveUnusedResources( + const ReturnedResourceArray& unused) { + unused_resources_for_child_compositor_.insert( + unused_resources_for_child_compositor_.end(), + unused.begin(), + unused.end()); +} + +// static +void DelegatedRendererLayer::ReceiveUnusedResourcesOnImplThread( + scoped_refptr<BlockingTaskRunner> task_runner, + base::WeakPtr<DelegatedRendererLayer> self, + const ReturnedResourceArray& unused) { + task_runner->PostTask( + FROM_HERE, + base::Bind( + &DelegatedRendererLayer::ReceiveUnusedResources, self, unused)); } } // namespace cc diff --git a/chromium/cc/layers/delegated_renderer_layer.h b/chromium/cc/layers/delegated_renderer_layer.h index 3f5280eec5f..60e9c3c05d9 100644 --- a/chromium/cc/layers/delegated_renderer_layer.h +++ b/chromium/cc/layers/delegated_renderer_layer.h @@ -5,12 +5,15 @@ #ifndef CC_LAYERS_DELEGATED_RENDERER_LAYER_H_ #define CC_LAYERS_DELEGATED_RENDERER_LAYER_H_ +#include "base/memory/ref_counted.h" +#include "base/memory/weak_ptr.h" +#include "base/synchronization/lock.h" #include "cc/base/cc_export.h" #include "cc/layers/layer.h" -#include "cc/resources/transferable_resource.h" +#include "cc/resources/returned_resource.h" namespace cc { - +class BlockingTaskRunner; class DelegatedFrameData; class DelegatedRendererLayerClient; @@ -21,6 +24,7 @@ class CC_EXPORT DelegatedRendererLayer : public Layer { virtual scoped_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl) OVERRIDE; + virtual void SetLayerTreeHost(LayerTreeHost* host) OVERRIDE; virtual void PushPropertiesTo(LayerImpl* impl) OVERRIDE; virtual bool DrawsContent() const OVERRIDE; @@ -34,25 +38,34 @@ class CC_EXPORT DelegatedRendererLayer : public Layer { // Passes ownership of any unused resources that had been given by the child // compositor to the given array, so they can be given back to the child. - void TakeUnusedResourcesForChildCompositor(TransferableResourceArray* array); - - virtual bool BlocksPendingCommit() const OVERRIDE; + void TakeUnusedResourcesForChildCompositor(ReturnedResourceArray* array); protected: explicit DelegatedRendererLayer(DelegatedRendererLayerClient* client); virtual ~DelegatedRendererLayer(); private: + void ReceiveUnusedResources(const ReturnedResourceArray& unused); + static void ReceiveUnusedResourcesOnImplThread( + scoped_refptr<BlockingTaskRunner> task_runner, + base::WeakPtr<DelegatedRendererLayer> self, + const ReturnedResourceArray& unused); + scoped_ptr<DelegatedFrameData> frame_data_; gfx::RectF damage_in_frame_; gfx::Size frame_size_; gfx::Size display_size_; - TransferableResourceArray unused_resources_for_child_compositor_; DelegatedRendererLayerClient* client_; + bool needs_filter_context_; + + ReturnedResourceArray unused_resources_for_child_compositor_; + scoped_refptr<BlockingTaskRunner> main_thread_runner_; + base::WeakPtrFactory<DelegatedRendererLayer> weak_ptrs_; DISALLOW_COPY_AND_ASSIGN(DelegatedRendererLayer); }; } // namespace cc + #endif // CC_LAYERS_DELEGATED_RENDERER_LAYER_H_ diff --git a/chromium/cc/layers/delegated_renderer_layer_impl.cc b/chromium/cc/layers/delegated_renderer_layer_impl.cc index 2dfdac6eb2d..34a1cd86755 100644 --- a/chromium/cc/layers/delegated_renderer_layer_impl.cc +++ b/chromium/cc/layers/delegated_renderer_layer_impl.cc @@ -47,7 +47,7 @@ bool DelegatedRendererLayerImpl::HasContributingDelegatedRenderPasses() const { static ResourceProvider::ResourceId ResourceRemapHelper( bool* invalid_frame, const ResourceProvider::ResourceIdMap& child_to_parent_map, - ResourceProvider::ResourceIdSet *remapped_resources, + ResourceProvider::ResourceIdArray* resources_in_frame, ResourceProvider::ResourceId id) { ResourceProvider::ResourceIdMap::const_iterator it = @@ -59,7 +59,7 @@ static ResourceProvider::ResourceId ResourceRemapHelper( DCHECK_EQ(it->first, id); ResourceProvider::ResourceId remapped_id = it->second; - remapped_resources->insert(remapped_id); + resources_in_frame->push_back(id); return remapped_id; } @@ -85,23 +85,31 @@ void DelegatedRendererLayerImpl::PushPropertiesTo(LayerImpl* layer) { have_render_passes_to_push_ = false; } - // This is just a copy for testing since we keep the data on the pending layer - // for returning resources to the child for now. + // This is just a copy for testing, since resources are added to the + // ResourceProvider in the pending tree. delegated_layer->resources_ = resources_; } +void DelegatedRendererLayerImpl::CreateChildIdIfNeeded( + const ReturnCallback& return_callback) { + if (child_id_) + return; + + ResourceProvider* resource_provider = layer_tree_impl()->resource_provider(); + child_id_ = resource_provider->CreateChild(return_callback); + own_child_id_ = true; +} + void DelegatedRendererLayerImpl::SetFrameData( scoped_ptr<DelegatedFrameData> frame_data, gfx::RectF damage_in_frame) { DCHECK(frame_data); + DCHECK(child_id_) << "CreateChildIdIfNeeded must be called first."; // A frame with an empty root render pass is invalid. DCHECK(frame_data->render_pass_list.empty() || !frame_data->render_pass_list.back()->output_rect.IsEmpty()); - CreateChildIdIfNeeded(); - DCHECK(child_id_); - ResourceProvider* resource_provider = layer_tree_impl()->resource_provider(); const ResourceProvider::ResourceIdMap& resource_map = resource_provider->GetChildToParentMap(child_id_); @@ -109,12 +117,12 @@ void DelegatedRendererLayerImpl::SetFrameData( resource_provider->ReceiveFromChild(child_id_, frame_data->resource_list); bool invalid_frame = false; - ResourceProvider::ResourceIdSet used_resources; + ResourceProvider::ResourceIdArray resources_in_frame; DrawQuad::ResourceIteratorCallback remap_resources_to_parent_callback = base::Bind(&ResourceRemapHelper, &invalid_frame, resource_map, - &used_resources); + &resources_in_frame); for (size_t i = 0; i < frame_data->render_pass_list.size(); ++i) { RenderPass* pass = frame_data->render_pass_list[i]; for (size_t j = 0; j < pass->quad_list.size(); ++j) { @@ -123,8 +131,15 @@ void DelegatedRendererLayerImpl::SetFrameData( } } - if (invalid_frame) + if (invalid_frame) { + // Declare we are still using the last frame's resources. + resource_provider->DeclareUsedResourcesFromChild(child_id_, resources_); return; + } + + // Declare we are using the new frame's resources. + resources_.swap(resources_in_frame); + resource_provider->DeclareUsedResourcesFromChild(child_id_, resources_); // Display size is already set so we can compute what the damage rect // will be in layer space. @@ -140,33 +155,9 @@ void DelegatedRendererLayerImpl::SetFrameData( // Save the remapped quads on the layer. This steals the quads and render // passes from the frame_data. SetRenderPasses(&frame_data->render_pass_list); - resources_.swap(used_resources); have_render_passes_to_push_ = true; } -void DelegatedRendererLayerImpl::CollectUnusedResources( - TransferableResourceArray* resources_for_ack) { - CreateChildIdIfNeeded(); - DCHECK(child_id_); - - ResourceProvider* resource_provider = layer_tree_impl()->resource_provider(); - const ResourceProvider::ResourceIdMap& resource_map = - resource_provider->GetChildToParentMap(child_id_); - - ResourceProvider::ResourceIdArray unused_resources; - for (ResourceProvider::ResourceIdMap::const_iterator it = - resource_map.begin(); - it != resource_map.end(); - ++it) { - bool resource_is_in_current_frame = resources_.count(it->second) > 0; - bool resource_is_in_use = resource_provider->InUseByConsumer(it->second); - if (!resource_is_in_current_frame && !resource_is_in_use) - unused_resources.push_back(it->second); - } - resource_provider->PrepareSendToChild( - child_id_, unused_resources, resources_for_ack); -} - void DelegatedRendererLayerImpl::SetDisplaySize(gfx::Size size) { if (display_size_ == size) return; @@ -176,10 +167,6 @@ void DelegatedRendererLayerImpl::SetDisplaySize(gfx::Size size) { void DelegatedRendererLayerImpl::SetRenderPasses( ScopedPtrVector<RenderPass>* render_passes_in_draw_order) { - gfx::RectF old_root_damage; - if (!render_passes_in_draw_order_.empty()) - old_root_damage = render_passes_in_draw_order_.back()->damage_rect; - ClearRenderPasses(); for (size_t i = 0; i < render_passes_in_draw_order->size(); ++i) { @@ -192,9 +179,6 @@ void DelegatedRendererLayerImpl::SetRenderPasses( render_passes_in_draw_order_.push_back(taken_render_pass.Pass()); } - if (!render_passes_in_draw_order_.empty()) - render_passes_in_draw_order_.back()->damage_rect.Union(old_root_damage); - // Give back an empty array instead of nulls. render_passes_in_draw_order->clear(); } @@ -240,13 +224,19 @@ RenderPass::Id DelegatedRendererLayerImpl::NextContributingRenderPassId( return RenderPass::Id(previous.layer_id, previous.index + 1); } -RenderPass::Id DelegatedRendererLayerImpl::ConvertDelegatedRenderPassId( - RenderPass::Id delegated_render_pass_id) const { +bool DelegatedRendererLayerImpl::ConvertDelegatedRenderPassId( + RenderPass::Id delegated_render_pass_id, + RenderPass::Id* output_render_pass_id) const { base::hash_map<RenderPass::Id, int>::const_iterator found = render_passes_index_by_id_.find(delegated_render_pass_id); - DCHECK(found != render_passes_index_by_id_.end()); + if (found == render_passes_index_by_id_.end()) { + // Be robust against a RenderPass id that isn't part of the frame. + return false; + } unsigned delegated_render_pass_index = found->second; - return RenderPass::Id(id(), IndexToId(delegated_render_pass_index)); + *output_render_pass_id = + RenderPass::Id(id(), IndexToId(delegated_render_pass_index)); + return true; } void DelegatedRendererLayerImpl::AppendContributingRenderPasses( @@ -254,10 +244,14 @@ void DelegatedRendererLayerImpl::AppendContributingRenderPasses( DCHECK(HasContributingDelegatedRenderPasses()); for (size_t i = 0; i < render_passes_in_draw_order_.size() - 1; ++i) { - RenderPass::Id output_render_pass_id = - ConvertDelegatedRenderPassId(render_passes_in_draw_order_[i]->id); + RenderPass::Id output_render_pass_id(-1, -1); + bool present = + ConvertDelegatedRenderPassId(render_passes_in_draw_order_[i]->id, + &output_render_pass_id); // Don't clash with the RenderPass we generate if we own a RenderSurface. + DCHECK(present) << render_passes_in_draw_order_[i]->id.layer_id << ", " + << render_passes_in_draw_order_[i]->id.index; DCHECK_GT(output_render_pass_id.index, 0); render_pass_sink->AppendRenderPass( @@ -288,13 +282,13 @@ void DelegatedRendererLayerImpl::AppendQuads( DCHECK(root_delegated_render_pass->output_rect.origin().IsOrigin()); gfx::Size frame_size = root_delegated_render_pass->output_rect.size(); - // If the index of the EenderPassId is 0, then it is a RenderPass generated - // for a layer in this compositor, not the delegated renderer. Then we want to - // merge our root RenderPass with the target RenderPass. Otherwise, it is some - // RenderPass which we added from the delegated renderer. + // If the index of the RenderPassId is 0, then it is a RenderPass generated + // for a layer in this compositor, not the delegating renderer. Then we want + // to merge our root RenderPass with the target RenderPass. Otherwise, it is + // some RenderPass which we added from the delegating renderer. bool should_merge_root_render_pass_with_target = !target_render_pass_id.index; if (should_merge_root_render_pass_with_target) { - // Verify that the RenderPass we are appending to is created our + // Verify that the RenderPass we are appending to is created by our // render_target. DCHECK(target_render_pass_id.layer_id == render_target()->id()); @@ -421,6 +415,7 @@ void DelegatedRendererLayerImpl::AppendRenderPassQuads( if (render_target() == this) { DCHECK(!is_clipped()); DCHECK(render_surface()); + DCHECK_EQ(0, num_unclipped_descendants()); output_shared_quad_state->clip_rect = MathUtil::MapClippedRect( delegated_frame_to_target_transform, output_shared_quad_state->clip_rect); @@ -446,18 +441,26 @@ void DelegatedRendererLayerImpl::AppendRenderPassQuads( } else { RenderPass::Id delegated_contributing_render_pass_id = RenderPassDrawQuad::MaterialCast(delegated_quad)->render_pass_id; - RenderPass::Id output_contributing_render_pass_id = - ConvertDelegatedRenderPassId(delegated_contributing_render_pass_id); - DCHECK(output_contributing_render_pass_id != - append_quads_data->render_pass_id); - - output_quad = RenderPassDrawQuad::MaterialCast(delegated_quad)->Copy( - output_shared_quad_state, - output_contributing_render_pass_id).PassAs<DrawQuad>(); + RenderPass::Id output_contributing_render_pass_id(-1, -1); + + bool present = + ConvertDelegatedRenderPassId(delegated_contributing_render_pass_id, + &output_contributing_render_pass_id); + + // The frame may have a RenderPassDrawQuad that points to a RenderPass not + // part of the frame. Just ignore these quads. + if (present) { + DCHECK(output_contributing_render_pass_id != + append_quads_data->render_pass_id); + + output_quad = RenderPassDrawQuad::MaterialCast(delegated_quad)->Copy( + output_shared_quad_state, + output_contributing_render_pass_id).PassAs<DrawQuad>(); + } } - DCHECK(output_quad.get()); - quad_sink->Append(output_quad.Pass(), append_quads_data); + if (output_quad) + quad_sink->Append(output_quad.Pass(), append_quads_data); } } @@ -465,15 +468,6 @@ const char* DelegatedRendererLayerImpl::LayerTypeAsString() const { return "cc::DelegatedRendererLayerImpl"; } -void DelegatedRendererLayerImpl::CreateChildIdIfNeeded() { - if (child_id_) - return; - - ResourceProvider* resource_provider = layer_tree_impl()->resource_provider(); - child_id_ = resource_provider->CreateChild(); - own_child_id_ = true; -} - void DelegatedRendererLayerImpl::ClearChildId() { if (!child_id_) return; diff --git a/chromium/cc/layers/delegated_renderer_layer_impl.h b/chromium/cc/layers/delegated_renderer_layer_impl.h index 9cfdcb6a351..7d774f1c357 100644 --- a/chromium/cc/layers/delegated_renderer_layer_impl.h +++ b/chromium/cc/layers/delegated_renderer_layer_impl.h @@ -40,11 +40,14 @@ class CC_EXPORT DelegatedRendererLayerImpl : public LayerImpl { void AppendContributingRenderPasses(RenderPassSink* render_pass_sink); + // Creates an ID with the resource provider for the child renderer + // that will be sending quads to the layer. Registers the callback to + // inform when resources are no longer in use. + void CreateChildIdIfNeeded(const ReturnCallback& return_callback); + void SetFrameData(scoped_ptr<DelegatedFrameData> frame_data, gfx::RectF damage_in_frame); - void CollectUnusedResources(TransferableResourceArray* resources_for_ack); - void SetDisplaySize(gfx::Size size); protected: @@ -54,14 +57,11 @@ class CC_EXPORT DelegatedRendererLayerImpl : public LayerImpl { const ScopedPtrVector<RenderPass>& RenderPassesInDrawOrderForTesting() const { return render_passes_in_draw_order_; } - const ResourceProvider::ResourceIdSet& ResourcesForTesting() const { + const ResourceProvider::ResourceIdArray& ResourcesForTesting() const { return resources_; } private: - // Creates an ID with the resource provider for the child renderer - // that will be sending quads to the layer. - void CreateChildIdIfNeeded(); void ClearChildId(); void AppendRainbowDebugBorder(QuadSink* quad_sink, @@ -71,8 +71,11 @@ class CC_EXPORT DelegatedRendererLayerImpl : public LayerImpl { ScopedPtrVector<RenderPass>* render_passes_in_draw_order); void ClearRenderPasses(); - RenderPass::Id ConvertDelegatedRenderPassId( - RenderPass::Id delegated_render_pass_id) const; + // Returns |true| if the delegated_render_pass_id is part of the current + // frame and can be converted. + bool ConvertDelegatedRenderPassId( + RenderPass::Id delegated_render_pass_id, + RenderPass::Id* output_render_pass_id) const; gfx::Transform DelegatedFrameToLayerSpaceTransform(gfx::Size frame_size) const; @@ -89,7 +92,7 @@ class CC_EXPORT DelegatedRendererLayerImpl : public LayerImpl { bool have_render_passes_to_push_; ScopedPtrVector<RenderPass> render_passes_in_draw_order_; base::hash_map<RenderPass::Id, int> render_passes_index_by_id_; - ResourceProvider::ResourceIdSet resources_; + ResourceProvider::ResourceIdArray resources_; gfx::Size display_size_; int child_id_; diff --git a/chromium/cc/layers/delegated_renderer_layer_impl_unittest.cc b/chromium/cc/layers/delegated_renderer_layer_impl_unittest.cc index 1584782f109..a641e00eaf0 100644 --- a/chromium/cc/layers/delegated_renderer_layer_impl_unittest.cc +++ b/chromium/cc/layers/delegated_renderer_layer_impl_unittest.cc @@ -5,6 +5,7 @@ #include "cc/layers/delegated_renderer_layer_impl.h" #include "cc/base/scoped_ptr_vector.h" +#include "cc/debug/test_web_graphics_context_3d.h" #include "cc/layers/append_quads_data.h" #include "cc/layers/quad_sink.h" #include "cc/layers/solid_color_layer_impl.h" @@ -20,7 +21,6 @@ #include "cc/test/mock_quad_culler.h" #include "cc/test/render_pass_test_common.h" #include "cc/test/render_pass_test_utils.h" -#include "cc/test/test_web_graphics_context_3d.h" #include "cc/trees/layer_tree_host_impl.h" #include "cc/trees/layer_tree_impl.h" #include "cc/trees/single_thread_proxy.h" @@ -38,20 +38,15 @@ class DelegatedRendererLayerImplTest : public testing::Test { LayerTreeSettings settings; settings.minimum_occlusion_tracking_size = gfx::Size(); - host_impl_ = LayerTreeHostImpl::Create(settings, - &client_, - &proxy_, - &stats_instrumentation_); + host_impl_.reset(new FakeLayerTreeHostImpl(settings, &proxy_)); host_impl_->InitializeRenderer(CreateFakeOutputSurface()); host_impl_->SetViewportSize(gfx::Size(10, 10)); } protected: FakeProxy proxy_; - FakeLayerTreeHostImplClient client_; DebugScopedSetImplThreadAndMainThreadBlocked always_impl_thread_and_main_thread_blocked_; - FakeRenderingStatsInstrumentation stats_instrumentation_; scoped_ptr<LayerTreeHostImpl> host_impl_; }; @@ -472,8 +467,8 @@ class DelegatedRendererLayerImplTestTransform root_layer->SetBounds(gfx::Size(100, 100)); delegated_renderer_layer->SetPosition(gfx::Point(20, 20)); - delegated_renderer_layer->SetBounds(gfx::Size(30, 30)); - delegated_renderer_layer->SetContentBounds(gfx::Size(30, 30)); + delegated_renderer_layer->SetBounds(gfx::Size(75, 75)); + delegated_renderer_layer->SetContentBounds(gfx::Size(75, 75)); delegated_renderer_layer->SetDrawsContent(true); gfx::Transform transform; transform.Scale(2.0, 2.0); @@ -518,8 +513,8 @@ class DelegatedRendererLayerImplTestTransform quad_sink.Append(color_quad.PassAs<DrawQuad>(), &data); } - gfx::Size root_pass_content_bounds(50, 50); - gfx::Rect root_pass_rect(0, 0, 50, 50); + gfx::Size root_pass_content_bounds(100, 100); + gfx::Rect root_pass_rect(0, 0, 100, 100); gfx::Transform root_pass_transform; root_pass_transform.Scale(1.5, 1.5); root_pass_transform.Translate(7.0, 7.0); @@ -653,7 +648,7 @@ TEST_F(DelegatedRendererLayerImplTestTransform, QuadsUnclipped_NoSurface) { // When the quads don't have a clip of their own, the clip rect is set to // the drawable_content_rect of the delegated renderer layer. - EXPECT_EQ(gfx::Rect(42, 42, 120, 120).ToString(), + EXPECT_EQ(delegated_renderer_layer_->drawable_content_rect().ToString(), root_delegated_shared_quad_state->clip_rect.ToString()); // Even though the quads in the root pass have no clip of their own, they @@ -665,12 +660,12 @@ TEST_F(DelegatedRendererLayerImplTestTransform, QuadsUnclipped_NoSurface) { // Device scale factor is 2. expected.Scale(2.0, 2.0); // This is the transform from the layer's space to its target. - // The position (20) - the width / scale (30 / 2) = 20 - 15 = 5 - expected.Translate(5.0, 5.0); + // The position (20) - the width / scale (75 / 2) = 20 - 37.5 = -17.5 + expected.Translate(-17.5, -17.5); expected.Scale(2.0, 2.0); expected.Translate(8.0, 8.0); - // The frame has size 50x50 but the layer's bounds are 30x30. - expected.Scale(30.0 / 50.0, 30.0 / 50.0); + // The frame has size 100x100 but the layer's bounds are 75x75. + expected.Scale(75.0 / 100.0, 75.0 / 100.0); // This is the transform within the source frame. expected.Scale(1.5, 1.5); expected.Translate(7.0, 7.0); @@ -710,15 +705,15 @@ TEST_F(DelegatedRendererLayerImplTestTransform, QuadsClipped_NoSurface) { // Since the quads have a clip_rect it should be modified by delegated // renderer layer's draw_transform. // The position of the resulting clip_rect is: - // (clip rect position (10) * scale to layer (30/50) + translate (8)) * - // layer scale (2) + layer position (20) = 48 - // But the layer is centered, so: 48 - (width / 2) = 48 - 30 / 2 = 33 - // The device scale is 2, so everything gets doubled, giving 66. + // (clip rect position (10) * scale to layer (75/100) + translate (8)) * + // layer scale (2) + layer position (20) = 51 + // But the layer is centered, so: 51 - (75 / 2) = 51 - 75 / 2 = 13.5 + // The device scale is 2, so everything gets doubled, giving 27. // - // The size is 35x35 scaled to fit inside the layer's bounds at 30x30 from - // a frame at 50x50: 35 * 2 (device scale) * 30 / 50 = 42. The device scale - // doubles this to 84. - EXPECT_EQ(gfx::Rect(66, 66, 84, 84).ToString(), + // The size is 35x35 scaled to fit inside the layer's bounds at 75x75 from + // a frame at 100x100: 35 * 2 (device scale) * 75 / 100 = 52.5. The device + // scale doubles this to 105. + EXPECT_EQ(gfx::Rect(27, 27, 105, 105).ToString(), root_delegated_shared_quad_state->clip_rect.ToString()); // The quads had a clip and it should be preserved. @@ -728,12 +723,12 @@ TEST_F(DelegatedRendererLayerImplTestTransform, QuadsClipped_NoSurface) { // Device scale factor is 2. expected.Scale(2.0, 2.0); // This is the transform from the layer's space to its target. - // The position (20) - the width / scale (30 / 2) = 20 - 15 = 5 - expected.Translate(5.0, 5.0); + // The position (20) - the width / scale (75 / 2) = 20 - 37.5 = -17.5 + expected.Translate(-17.5, -17.5); expected.Scale(2.0, 2.0); expected.Translate(8.0, 8.0); - // The frame has size 50x50 but the layer's bounds are 30x30. - expected.Scale(30.0 / 50.0, 30.0 / 50.0); + // The frame has size 100x100 but the layer's bounds are 75x75. + expected.Scale(75.0 / 100.0, 75.0 / 100.0); // This is the transform within the source frame. expected.Scale(1.5, 1.5); expected.Translate(7.0, 7.0); @@ -775,10 +770,10 @@ TEST_F(DelegatedRendererLayerImplTestTransform, QuadsUnclipped_Surface) { // When the layer owns a surface, then its position and translation are not // a part of its draw transform. // The position of the resulting clip_rect is: - // (clip rect position (10) * scale to layer (30/50)) * device scale (2) = 12 - // The size is 35x35 scaled to fit inside the layer's bounds at 30x30 from - // a frame at 50x50: 35 * 2 (device scale) * 30 / 50 = 42. - EXPECT_EQ(gfx::Rect(12, 12, 42, 42).ToString(), + // (clip rect position (10) * scale to layer (75/100)) * device scale (2) = 15 + // The size is 35x35 scaled to fit inside the layer's bounds at 75x75 from + // a frame at 100x100: 35 * 2 (device scale) * 75 / 100 = 52.5. + EXPECT_EQ(gfx::Rect(15, 15, 53, 53).ToString(), root_delegated_shared_quad_state->clip_rect.ToString()); // Since the layer owns a surface it doesn't need to clip its quads, so @@ -788,8 +783,8 @@ TEST_F(DelegatedRendererLayerImplTestTransform, QuadsUnclipped_Surface) { gfx::Transform expected; // Device scale factor is 2. expected.Scale(2.0, 2.0); - // The frame has size 50x50 but the layer's bounds are 30x30. - expected.Scale(30.0 / 50.0, 30.0 / 50.0); + // The frame has size 100x100 but the layer's bounds are 75x75. + expected.Scale(75.0 / 100.0, 75.0 / 100.0); // This is the transform within the source frame. expected.Scale(1.5, 1.5); expected.Translate(7.0, 7.0); @@ -831,10 +826,10 @@ TEST_F(DelegatedRendererLayerImplTestTransform, QuadsClipped_Surface) { // When the layer owns a surface, then its position and translation are not // a part of its draw transform. // The position of the resulting clip_rect is: - // (clip rect position (10) * scale to layer (30/50)) * device scale (2) = 12 - // The size is 35x35 scaled to fit inside the layer's bounds at 30x30 from - // a frame at 50x50: 35 * 2 (device scale) * 30 / 50 = 42. - EXPECT_EQ(gfx::Rect(12, 12, 42, 42).ToString(), + // (clip rect position (10) * scale to layer (75/100)) * device scale (2) = 15 + // The size is 35x35 scaled to fit inside the layer's bounds at 75x75 from + // a frame at 100x100: 35 * 2 (device scale) * 75 / 100 = 52.5. + EXPECT_EQ(gfx::Rect(15, 15, 53, 53).ToString(), root_delegated_shared_quad_state->clip_rect.ToString()); // The quads had a clip and it should be preserved. @@ -843,8 +838,8 @@ TEST_F(DelegatedRendererLayerImplTestTransform, QuadsClipped_Surface) { gfx::Transform expected; // Device scale factor is 2. expected.Scale(2.0, 2.0); - // The frame has size 50x50 but the layer's bounds are 30x30. - expected.Scale(30.0 / 50.0, 30.0 / 50.0); + // The frame has size 100x100 but the layer's bounds are 75x75. + expected.Scale(75.0 / 100.0, 75.0 / 100.0); // This is the transform within the source frame. expected.Scale(1.5, 1.5); expected.Translate(7.0, 7.0); @@ -1250,5 +1245,58 @@ TEST_F(DelegatedRendererLayerImplTestClip, QuadsClipped_LayerClipped_Surface) { host_impl_->DidDrawAllLayers(frame); } +TEST_F(DelegatedRendererLayerImplTest, InvalidRenderPassDrawQuad) { + scoped_ptr<LayerImpl> root_layer = LayerImpl::Create( + host_impl_->active_tree(), 1).PassAs<LayerImpl>(); + scoped_ptr<FakeDelegatedRendererLayerImpl> delegated_renderer_layer = + FakeDelegatedRendererLayerImpl::Create(host_impl_->active_tree(), 4); + + host_impl_->SetViewportSize(gfx::Size(100, 100)); + + delegated_renderer_layer->SetPosition(gfx::Point(3, 3)); + delegated_renderer_layer->SetBounds(gfx::Size(10, 10)); + delegated_renderer_layer->SetContentBounds(gfx::Size(10, 10)); + delegated_renderer_layer->SetDrawsContent(true); + + ScopedPtrVector<RenderPass> delegated_render_passes; + TestRenderPass* pass1 = AddRenderPass( + &delegated_render_passes, + RenderPass::Id(9, 6), + gfx::Rect(0, 0, 10, 10), + gfx::Transform()); + AddQuad(pass1, gfx::Rect(0, 0, 6, 6), 33u); + + // This render pass isn't part of the frame. + scoped_ptr<TestRenderPass> missing_pass(TestRenderPass::Create()); + missing_pass->SetNew(RenderPass::Id(9, 7), + gfx::Rect(7, 7, 7, 7), + gfx::Rect(7, 7, 7, 7), + gfx::Transform()); + + // But a render pass quad refers to it. + AddRenderPassQuad(pass1, missing_pass.get()); + + delegated_renderer_layer->SetFrameDataForRenderPasses( + &delegated_render_passes); + + // The RenderPasses should be taken by the layer. + EXPECT_EQ(0u, delegated_render_passes.size()); + + root_layer->AddChild(delegated_renderer_layer.PassAs<LayerImpl>()); + host_impl_->active_tree()->SetRootLayer(root_layer.Pass()); + + LayerTreeHostImpl::FrameData frame; + EXPECT_TRUE(host_impl_->PrepareToDraw(&frame, gfx::Rect())); + + // The DelegatedRendererLayerImpl should drop the bad RenderPassDrawQuad. + ASSERT_EQ(1u, frame.render_passes.size()); + ASSERT_EQ(1u, frame.render_passes[0]->quad_list.size()); + EXPECT_EQ(DrawQuad::SOLID_COLOR, + frame.render_passes[0]->quad_list[0]->material); + + host_impl_->DrawLayers(&frame, base::TimeTicks::Now()); + host_impl_->DidDrawAllLayers(frame); +} + } // namespace } // namespace cc diff --git a/chromium/cc/layers/draw_properties.h b/chromium/cc/layers/draw_properties.h index bb80dce0adf..181d900f267 100644 --- a/chromium/cc/layers/draw_properties.h +++ b/chromium/cc/layers/draw_properties.h @@ -27,6 +27,7 @@ struct CC_EXPORT DrawProperties { contents_scale_x(1.f), contents_scale_y(1.f), num_descendants_that_draw_content(0), + num_unclipped_descendants(0), descendants_can_clip_selves(false), can_draw_directly_to_backbuffer(false), layer_or_descendant_has_copy_request(false) {} @@ -88,6 +89,10 @@ struct CC_EXPORT DrawProperties { // Does not include this layer itself, only its children and descendants. int num_descendants_that_draw_content; + // Number of descendants with a clip parent that is our ancestor. NB - this + // does not include our clip children because they are clipped by us. + int num_unclipped_descendants; + // If true, every descendant in the sub-tree can clip itself without the // need to use hardware sissoring or a new render target. bool descendants_can_clip_selves; diff --git a/chromium/cc/layers/heads_up_display_layer.cc b/chromium/cc/layers/heads_up_display_layer.cc index 92486cc8515..d9fab902ef9 100644 --- a/chromium/cc/layers/heads_up_display_layer.cc +++ b/chromium/cc/layers/heads_up_display_layer.cc @@ -55,4 +55,8 @@ scoped_ptr<LayerImpl> HeadsUpDisplayLayer::CreateLayerImpl( PassAs<LayerImpl>(); } +std::string HeadsUpDisplayLayer::DebugName() { + return std::string("Heads Up Display Layer"); +} + } // namespace cc diff --git a/chromium/cc/layers/heads_up_display_layer.h b/chromium/cc/layers/heads_up_display_layer.h index 7038cd480c3..f13d56bba67 100644 --- a/chromium/cc/layers/heads_up_display_layer.h +++ b/chromium/cc/layers/heads_up_display_layer.h @@ -5,6 +5,8 @@ #ifndef CC_LAYERS_HEADS_UP_DISPLAY_LAYER_H_ #define CC_LAYERS_HEADS_UP_DISPLAY_LAYER_H_ +#include <string> + #include "base/memory/scoped_ptr.h" #include "cc/base/cc_export.h" #include "cc/layers/contents_scaling_layer.h" @@ -23,6 +25,8 @@ class CC_EXPORT HeadsUpDisplayLayer : public ContentsScalingLayer { virtual scoped_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl) OVERRIDE; + virtual std::string DebugName() OVERRIDE; + protected: HeadsUpDisplayLayer(); diff --git a/chromium/cc/layers/heads_up_display_layer_impl.cc b/chromium/cc/layers/heads_up_display_layer_impl.cc index 8728d704ca4..c1fa9f37dbc 100644 --- a/chromium/cc/layers/heads_up_display_layer_impl.cc +++ b/chromium/cc/layers/heads_up_display_layer_impl.cc @@ -94,8 +94,9 @@ bool HeadsUpDisplayLayerImpl::WillDraw(DrawMode draw_mode, hud_resource_->Free(); if (!hud_resource_->id()) { - hud_resource_->Allocate( - content_bounds(), GL_RGBA, ResourceProvider::TextureUsageAny); + hud_resource_->Allocate(content_bounds(), + ResourceProvider::TextureUsageAny, + RGBA_8888); } return LayerImpl::WillDraw(draw_mode, resource_provider); diff --git a/chromium/cc/layers/image_layer.cc b/chromium/cc/layers/image_layer.cc index 8ba1908131d..bbf1aa82e34 100644 --- a/chromium/cc/layers/image_layer.cc +++ b/chromium/cc/layers/image_layer.cc @@ -56,9 +56,8 @@ void ImageLayer::CreateUpdaterIfNeeded() { return; updater_ = ImageLayerUpdater::Create(); - GLenum texture_format = - layer_tree_host()->GetRendererCapabilities().best_texture_format; - SetTextureFormat(texture_format); + SetTextureFormat( + layer_tree_host()->GetRendererCapabilities().best_texture_format); } LayerUpdater* ImageLayer::Updater() const { diff --git a/chromium/cc/layers/io_surface_layer_impl.cc b/chromium/cc/layers/io_surface_layer_impl.cc index f8577a37765..e8c627be08b 100644 --- a/chromium/cc/layers/io_surface_layer_impl.cc +++ b/chromium/cc/layers/io_surface_layer_impl.cc @@ -39,11 +39,11 @@ void IOSurfaceLayerImpl::DestroyTexture() { } if (io_surface_texture_id_) { - OutputSurface* output_surface = layer_tree_impl()->output_surface(); + ContextProvider* context_provider = + layer_tree_impl()->output_surface()->context_provider().get(); // TODO(skaslev): Implement this path for software compositing. - WebKit::WebGraphicsContext3D* context3d = output_surface->context3d(); - if (context3d) - context3d->deleteTexture(io_surface_texture_id_); + if (context_provider) + context_provider->Context3d()->deleteTexture(io_surface_texture_id_); io_surface_texture_id_ = 0; } } @@ -67,13 +67,15 @@ bool IOSurfaceLayerImpl::WillDraw(DrawMode draw_mode, return false; if (io_surface_changed_) { - WebKit::WebGraphicsContext3D* context3d = - resource_provider->GraphicsContext3D(); - if (!context3d) { + ContextProvider* context_provider = + layer_tree_impl()->output_surface()->context_provider().get(); + if (!context_provider) { // TODO(skaslev): Implement this path for software compositing. return false; } + WebKit::WebGraphicsContext3D* context3d = context_provider->Context3d(); + // TODO(ernstm): Do this in a way that we can track memory usage. if (!io_surface_texture_id_) { io_surface_texture_id_ = context3d->createTexture(); diff --git a/chromium/cc/layers/layer.cc b/chromium/cc/layers/layer.cc index aa4a002dff4..86887a6ad2c 100644 --- a/chromium/cc/layers/layer.cc +++ b/chromium/cc/layers/layer.cc @@ -6,12 +6,14 @@ #include <algorithm> +#include "base/debug/trace_event.h" #include "base/location.h" #include "base/metrics/histogram.h" #include "base/single_thread_task_runner.h" #include "cc/animation/animation.h" #include "cc/animation/animation_events.h" #include "cc/animation/layer_animation_controller.h" +#include "cc/layers/layer_client.h" #include "cc/layers/layer_impl.h" #include "cc/output/copy_output_request.h" #include "cc/output/copy_output_result.h" @@ -54,8 +56,11 @@ Layer::Layer() use_parent_backface_visibility_(false), draw_checkerboard_for_missing_tiles_(false), force_render_surface_(false), + scroll_parent_(NULL), + clip_parent_(NULL), replica_layer_(NULL), - raster_scale_(0.f) { + raster_scale_(0.f), + client_(NULL) { if (layer_id_ < 0) { s_next_layer_id = 1; layer_id_ = s_next_layer_id++; @@ -85,6 +90,9 @@ Layer::~Layer() { DCHECK_EQ(this, replica_layer_->parent()); replica_layer_->RemoveFromParent(); } + + RemoveFromScrollTree(); + RemoveFromClipTree(); } void Layer::SetLayerTreeHost(LayerTreeHost* host) { @@ -144,14 +152,11 @@ void Layer::SetNeedsFullTreeSync() { layer_tree_host_->SetNeedsFullTreeSync(); } -bool Layer::IsPropertyChangeAllowed() const { +void Layer::SetNextCommitWaitsForActivation() { if (!layer_tree_host_) - return true; - - if (!layer_tree_host_->settings().strict_layer_property_change_checking) - return true; + return; - return !layer_tree_host_->in_paint_layer_contents(); + layer_tree_host_->SetNextCommitWaitsForActivation(); } void Layer::SetNeedsPushProperties() { @@ -179,6 +184,16 @@ void Layer::RemoveDependentNeedsPushProperties() { parent_->RemoveDependentNeedsPushProperties(); } +bool Layer::IsPropertyChangeAllowed() const { + if (!layer_tree_host_) + return true; + + if (!layer_tree_host_->settings().strict_layer_property_change_checking) + return true; + + return !layer_tree_host_->in_paint_layer_contents(); +} + gfx::Rect Layer::LayerRectToContentRect(const gfx::RectF& layer_rect) const { gfx::RectF content_rect = gfx::ScaleRect(layer_rect, contents_scale_x(), contents_scale_y()); @@ -188,28 +203,14 @@ gfx::Rect Layer::LayerRectToContentRect(const gfx::RectF& layer_rect) const { return gfx::ToEnclosingRect(content_rect); } -bool Layer::BlocksPendingCommit() const { - return false; +skia::RefPtr<SkPicture> Layer::GetPicture() const { + return skia::RefPtr<SkPicture>(); } bool Layer::CanClipSelf() const { return false; } -bool Layer::BlocksPendingCommitRecursive() const { - if (BlocksPendingCommit()) - return true; - if (mask_layer() && mask_layer()->BlocksPendingCommitRecursive()) - return true; - if (replica_layer() && replica_layer()->BlocksPendingCommitRecursive()) - return true; - for (size_t i = 0; i < children_.size(); ++i) { - if (children_[i]->BlocksPendingCommitRecursive()) - return true; - } - return false; -} - void Layer::SetParent(Layer* layer) { DCHECK(!layer || !layer->HasAncestor(this)); @@ -559,6 +560,66 @@ bool Layer::TransformIsAnimating() const { return layer_animation_controller_->IsAnimatingProperty(Animation::Transform); } +void Layer::SetScrollParent(Layer* parent) { + DCHECK(IsPropertyChangeAllowed()); + if (scroll_parent_ == parent) + return; + + if (scroll_parent_) + scroll_parent_->RemoveScrollChild(this); + + scroll_parent_ = parent; + + if (scroll_parent_) + scroll_parent_->AddScrollChild(this); + + SetNeedsCommit(); +} + +void Layer::AddScrollChild(Layer* child) { + if (!scroll_children_) + scroll_children_.reset(new std::set<Layer*>); + scroll_children_->insert(child); + SetNeedsCommit(); +} + +void Layer::RemoveScrollChild(Layer* child) { + scroll_children_->erase(child); + if (scroll_children_->empty()) + scroll_children_.reset(); + SetNeedsCommit(); +} + +void Layer::SetClipParent(Layer* ancestor) { + DCHECK(IsPropertyChangeAllowed()); + if (clip_parent_ == ancestor) + return; + + if (clip_parent_) + clip_parent_->RemoveClipChild(this); + + clip_parent_ = ancestor; + + if (clip_parent_) + clip_parent_->AddClipChild(this); + + SetNeedsCommit(); +} + +void Layer::AddClipChild(Layer* child) { + if (!clip_children_) + clip_children_.reset(new std::set<Layer*>); + clip_children_->insert(child); + SetNeedsCommit(); +} + +void Layer::RemoveClipChild(Layer* child) { + clip_children_->erase(child); + if (clip_children_->empty()) + clip_children_.reset(); + SetNeedsCommit(); +} + void Layer::SetScrollOffset(gfx::Vector2d scroll_offset) { DCHECK(IsPropertyChangeAllowed()); if (scroll_offset_ == scroll_offset) @@ -627,6 +688,7 @@ void Layer::SetTouchEventHandlerRegion(const Region& region) { if (touch_event_handler_region_ == region) return; touch_event_handler_region_ = region; + SetNeedsCommit(); } void Layer::SetDrawCheckerboardForMissingTiles(bool checkerboard) { @@ -742,7 +804,15 @@ void Layer::PushPropertiesTo(LayerImpl* layer) { : bounds_); layer->SetContentBounds(content_bounds()); layer->SetContentsScale(contents_scale_x(), contents_scale_y()); - layer->SetDebugName(debug_name_); + + bool is_tracing; + TRACE_EVENT_CATEGORY_GROUP_ENABLED(TRACE_DISABLED_BY_DEFAULT("cc.debug"), + &is_tracing); + if (is_tracing) + layer->SetDebugName(DebugName()); + else + layer->SetDebugName(std::string()); + layer->SetCompositingReasons(compositing_reasons_); layer->SetDoubleSided(double_sided_); layer->SetDrawCheckerboardForMissingTiles( @@ -775,9 +845,50 @@ void Layer::PushPropertiesTo(LayerImpl* layer) { DCHECK(!(TransformIsAnimating() && layer->TransformIsAnimatingOnImplOnly())); layer->SetScrollable(scrollable_); - layer->SetScrollOffset(scroll_offset_); layer->SetMaxScrollOffset(max_scroll_offset_); + LayerImpl* scroll_parent = NULL; + if (scroll_parent_) + scroll_parent = layer->layer_tree_impl()->LayerById(scroll_parent_->id()); + + layer->SetScrollParent(scroll_parent); + if (scroll_children_) { + std::set<LayerImpl*>* scroll_children = new std::set<LayerImpl*>; + for (std::set<Layer*>::iterator it = scroll_children_->begin(); + it != scroll_children_->end(); ++it) + scroll_children->insert(layer->layer_tree_impl()->LayerById((*it)->id())); + layer->SetScrollChildren(scroll_children); + } + + LayerImpl* clip_parent = NULL; + if (clip_parent_) { + clip_parent = + layer->layer_tree_impl()->LayerById(clip_parent_->id()); + } + + layer->SetClipParent(clip_parent); + if (clip_children_) { + std::set<LayerImpl*>* clip_children = new std::set<LayerImpl*>; + for (std::set<Layer*>::iterator it = clip_children_->begin(); + it != clip_children_->end(); ++it) { + LayerImpl* clip_child = layer->layer_tree_impl()->LayerById((*it)->id()); + DCHECK(clip_child); + clip_children->insert(clip_child); + } + layer->SetClipChildren(clip_children); + } + + // Adjust the scroll delta to be just the scrolls that have happened since + // the begin frame was sent. This happens for impl-side painting + // in LayerImpl::ApplyScrollDeltasSinceBeginFrame in a separate tree walk. + if (layer->layer_tree_impl()->settings().impl_side_painting) { + layer->SetScrollOffset(scroll_offset_); + } else { + layer->SetScrollOffsetAndDelta( + scroll_offset_, layer->ScrollDelta() - layer->sent_scroll_delta()); + layer->SetSentScrollDelta(gfx::Vector2d()); + } + // Wrap the copy_requests_ in a PostTask to the main thread. ScopedPtrVector<CopyOutputRequest> main_thread_copy_requests; for (ScopedPtrVector<CopyOutputRequest>::iterator it = copy_requests_.begin(); @@ -805,23 +916,6 @@ void Layer::PushPropertiesTo(LayerImpl* layer) { update_rect_.Union(layer->update_rect()); layer->set_update_rect(update_rect_); - if (layer->layer_tree_impl()->settings().impl_side_painting) { - DCHECK(layer->layer_tree_impl()->IsPendingTree()); - LayerImpl* active_twin = - layer->layer_tree_impl()->FindActiveTreeLayerById(id()); - // Update the scroll delta from the active layer, which may have - // adjusted its scroll delta prior to this pending layer being created. - // This code is identical to that in LayerImpl::SetScrollDelta. - if (active_twin) { - DCHECK(layer->sent_scroll_delta().IsZero()); - layer->SetScrollDelta(active_twin->ScrollDelta() - - active_twin->sent_scroll_delta()); - } - } else { - layer->SetScrollDelta(layer->ScrollDelta() - layer->sent_scroll_delta()); - layer->SetSentScrollDelta(gfx::Vector2d()); - } - layer->SetStackingOrderChanged(stacking_order_changed_); layer_animation_controller_->PushAnimationUpdatesTo( @@ -868,9 +962,8 @@ bool Layer::NeedMoreUpdates() { return false; } -void Layer::SetDebugName(const std::string& debug_name) { - debug_name_ = debug_name; - SetNeedsCommit(); +std::string Layer::DebugName() { + return client_ ? client_->DebugName() : std::string(); } void Layer::SetCompositingReasons(CompositingReasons reasons) { @@ -967,7 +1060,7 @@ Region Layer::VisibleContentOpaqueRegion() const { return Region(); } -ScrollbarLayer* Layer::ToScrollbarLayer() { +ScrollbarLayerInterface* Layer::ToScrollbarLayer() { return NULL; } @@ -979,4 +1072,30 @@ bool Layer::SupportsLCDText() const { return false; } +void Layer::RemoveFromScrollTree() { + if (scroll_children_.get()) { + for (std::set<Layer*>::iterator it = scroll_children_->begin(); + it != scroll_children_->end(); ++it) + (*it)->scroll_parent_ = NULL; + } + + if (scroll_parent_) + scroll_parent_->RemoveScrollChild(this); + + scroll_parent_ = NULL; +} + +void Layer::RemoveFromClipTree() { + if (clip_children_.get()) { + for (std::set<Layer*>::iterator it = clip_children_->begin(); + it != clip_children_->end(); ++it) + (*it)->clip_parent_ = NULL; + } + + if (clip_parent_) + clip_parent_->RemoveClipChild(this); + + clip_parent_ = NULL; +} + } // namespace cc diff --git a/chromium/cc/layers/layer.h b/chromium/cc/layers/layer.h index 035e071a5fd..737e83df067 100644 --- a/chromium/cc/layers/layer.h +++ b/chromium/cc/layers/layer.h @@ -5,6 +5,7 @@ #ifndef CC_LAYERS_LAYER_H_ #define CC_LAYERS_LAYER_H_ +#include <set> #include <string> #include "base/callback.h" @@ -26,10 +27,15 @@ #include "skia/ext/refptr.h" #include "third_party/skia/include/core/SkColor.h" #include "third_party/skia/include/core/SkImageFilter.h" +#include "third_party/skia/include/core/SkPicture.h" #include "ui/gfx/rect.h" #include "ui/gfx/rect_f.h" #include "ui/gfx/transform.h" +namespace gfx { +class BoxF; +} + namespace cc { class Animation; @@ -38,13 +44,14 @@ struct AnimationEvent; class CopyOutputRequest; class LayerAnimationDelegate; class LayerAnimationEventObserver; +class LayerClient; class LayerImpl; class LayerTreeHost; class LayerTreeImpl; class PriorityCalculator; class RenderingStatsInstrumentation; class ResourceUpdateQueue; -class ScrollbarLayer; +class ScrollbarLayerInterface; struct AnimationEvent; // Base class for composited layers. Special layer types are derived from @@ -151,6 +158,34 @@ class CC_EXPORT Layer : public base::RefCounted<Layer>, const gfx::Transform& transform() const { return transform_; } bool TransformIsAnimating() const; + void SetScrollParent(Layer* parent); + + Layer* scroll_parent() { return scroll_parent_; } + const Layer* scroll_parent() const { return scroll_parent_; } + + void AddScrollChild(Layer* child); + void RemoveScrollChild(Layer* child); + + std::set<Layer*>* scroll_children() { return scroll_children_.get(); } + const std::set<Layer*>* scroll_children() const { + return scroll_children_.get(); + } + + void SetClipParent(Layer* ancestor); + + Layer* clip_parent() { return clip_parent_; } + const Layer* clip_parent() const { + return clip_parent_; + } + + void AddClipChild(Layer* child); + void RemoveClipChild(Layer* child); + + std::set<Layer*>* clip_children() { return clip_children_.get(); } + const std::set<Layer*>* clip_children() const { + return clip_children_.get(); + } + DrawProperties<Layer, RenderSurface>& draw_properties() { return draw_properties_; } @@ -201,6 +236,9 @@ class CC_EXPORT Layer : public base::RefCounted<Layer>, RenderSurface* render_surface() const { return draw_properties_.render_surface.get(); } + int num_unclipped_descendants() const { + return draw_properties_.num_unclipped_descendants; + } void SetScrollOffset(gfx::Vector2d scroll_offset); gfx::Vector2d scroll_offset() const { return scroll_offset_; } @@ -292,7 +330,10 @@ class CC_EXPORT Layer : public base::RefCounted<Layer>, virtual void SetIsMask(bool is_mask) {} virtual void ReduceMemoryUsage() {} - void SetDebugName(const std::string& debug_name); + virtual std::string DebugName(); + + void SetLayerClient(LayerClient* client) { client_ = client; } + void SetCompositingReasons(CompositingReasons reasons); virtual void PushPropertiesTo(LayerImpl* layer); @@ -316,7 +357,8 @@ class CC_EXPORT Layer : public base::RefCounted<Layer>, float* contents_scale_y, gfx::Size* content_bounds); - LayerTreeHost* layer_tree_host() const { return layer_tree_host_; } + LayerTreeHost* layer_tree_host() { return layer_tree_host_; } + const LayerTreeHost* layer_tree_host() const { return layer_tree_host_; } // Set the priority of all desired textures in this layer. virtual void SetTexturePriorities(const PriorityCalculator& priority_calc) {} @@ -328,6 +370,10 @@ class CC_EXPORT Layer : public base::RefCounted<Layer>, void SuspendAnimations(double monotonic_time); void ResumeAnimations(double monotonic_time); + bool AnimatedBoundsForBox(const gfx::BoxF& box, gfx::BoxF* bounds) { + return layer_animation_controller_->AnimatedBoundsForBox(box, bounds); + } + LayerAnimationController* layer_animation_controller() { return layer_animation_controller_.get(); } @@ -347,16 +393,11 @@ class CC_EXPORT Layer : public base::RefCounted<Layer>, virtual Region VisibleContentOpaqueRegion() const; - virtual ScrollbarLayer* ToScrollbarLayer(); + virtual ScrollbarLayerInterface* ToScrollbarLayer(); gfx::Rect LayerRectToContentRect(const gfx::RectF& layer_rect) const; - // In impl-side painting, this returns true if this layer type is not - // compatible with the main thread running freely, such as a double-buffered - // canvas that doesn't want to be triple-buffered across all three trees. - virtual bool BlocksPendingCommit() const; - // Returns true if anything in this tree blocksPendingCommit. - bool BlocksPendingCommitRecursive() const; + virtual skia::RefPtr<SkPicture> GetPicture() const; virtual bool CanClipSelf() const; @@ -407,7 +448,11 @@ class CC_EXPORT Layer : public base::RefCounted<Layer>, // Called when there's been a change in layer structure. Implies both // SetNeedsUpdate and SetNeedsCommit, but not SetNeedsPushProperties. void SetNeedsFullTreeSync(); - bool IsPropertyChangeAllowed() const; + + // Called when the next commit should wait until the pending tree is activated + // before finishing the commit and unblocking the main thread. Used to ensure + // unused resources on the impl thread are returned before commit completes. + void SetNextCommitWaitsForActivation(); void SetNeedsPushProperties(); void AddDependentNeedsPushProperties(); @@ -416,6 +461,16 @@ class CC_EXPORT Layer : public base::RefCounted<Layer>, return needs_push_properties() || descendant_needs_push_properties(); } + bool IsPropertyChangeAllowed() const; + + // If this layer has a scroll parent, it removes |this| from its list of + // scroll children. + void RemoveFromScrollTree(); + + // If this layer has a clip parent, it removes |this| from its list of clip + // children. + void RemoveFromClipTree(); + void reset_raster_scale_to_unknown() { raster_scale_ = 0.f; } // This flag is set when the layer needs to push properties to the impl @@ -486,7 +541,6 @@ class CC_EXPORT Layer : public base::RefCounted<Layer>, gfx::PointF position_; gfx::PointF anchor_point_; SkColor background_color_; - std::string debug_name_; CompositingReasons compositing_reasons_; float opacity_; skia::RefPtr<SkImageFilter> filter_; @@ -504,6 +558,11 @@ class CC_EXPORT Layer : public base::RefCounted<Layer>, bool use_parent_backface_visibility_; bool draw_checkerboard_for_missing_tiles_; bool force_render_surface_; + Layer* scroll_parent_; + scoped_ptr<std::set<Layer*> > scroll_children_; + + Layer* clip_parent_; + scoped_ptr<std::set<Layer*> > clip_children_; gfx::Transform transform_; gfx::Transform sublayer_transform_; @@ -514,6 +573,8 @@ class CC_EXPORT Layer : public base::RefCounted<Layer>, // Transient properties. float raster_scale_; + LayerClient* client_; + ScopedPtrVector<CopyOutputRequest> copy_requests_; base::Closure did_scroll_callback_; diff --git a/chromium/cc/layers/layer_client.h b/chromium/cc/layers/layer_client.h new file mode 100644 index 00000000000..73c0fd2514b --- /dev/null +++ b/chromium/cc/layers/layer_client.h @@ -0,0 +1,24 @@ +// 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. + +#ifndef CC_LAYERS_LAYER_CLIENT_H_ +#define CC_LAYERS_LAYER_CLIENT_H_ + +#include <string> + +#include "cc/base/cc_export.h" + +namespace cc { + +class CC_EXPORT LayerClient { + public: + virtual std::string DebugName() = 0; + + protected: + virtual ~LayerClient() {} +}; + +} // namespace cc + +#endif // CC_LAYERS_LAYER_CLIENT_H_ diff --git a/chromium/cc/layers/layer_impl.cc b/chromium/cc/layers/layer_impl.cc index b78cf1a45a8..b0c5399d32e 100644 --- a/chromium/cc/layers/layer_impl.cc +++ b/chromium/cc/layers/layer_impl.cc @@ -9,13 +9,14 @@ #include "cc/animation/animation_registrar.h" #include "cc/animation/scrollbar_animation_controller.h" #include "cc/animation/scrollbar_animation_controller_linear_fade.h" +#include "cc/animation/scrollbar_animation_controller_thinning.h" #include "cc/base/math_util.h" #include "cc/debug/debug_colors.h" #include "cc/debug/layer_tree_debug_state.h" #include "cc/debug/traced_value.h" #include "cc/input/layer_scroll_offset_delegate.h" +#include "cc/layers/painted_scrollbar_layer_impl.h" #include "cc/layers/quad_sink.h" -#include "cc/layers/scrollbar_layer_impl.h" #include "cc/output/copy_output_request.h" #include "cc/quads/debug_border_draw_quad.h" #include "cc/trees/layer_tree_impl.h" @@ -29,6 +30,8 @@ namespace cc { LayerImpl::LayerImpl(LayerTreeImpl* tree_impl, int id) : parent_(NULL), + scroll_parent_(NULL), + clip_parent_(NULL), mask_layer_id_(-1), replica_layer_id_(-1), layer_id_(id), @@ -73,6 +76,24 @@ LayerImpl::~LayerImpl() { layer_tree_impl_->UnregisterLayer(this); layer_animation_controller_->RemoveValueObserver(this); + + if (scroll_children_) { + for (std::set<LayerImpl*>::iterator it = scroll_children_->begin(); + it != scroll_children_->end(); ++it) + (*it)->scroll_parent_ = NULL; + } + + if (scroll_parent_) + scroll_parent_->RemoveScrollChild(this); + + if (clip_children_) { + for (std::set<LayerImpl*>::iterator it = clip_children_->begin(); + it != clip_children_->end(); ++it) + (*it)->clip_parent_ = NULL; + } + + if (clip_parent_) + clip_parent_->RemoveClipChild(this); } void LayerImpl::AddChild(scoped_ptr<LayerImpl> child) { @@ -104,6 +125,67 @@ void LayerImpl::ClearChildList() { layer_tree_impl()->set_needs_update_draw_properties(); } +bool LayerImpl::HasAncestor(const LayerImpl* ancestor) const { + if (!ancestor) + return false; + + for (const LayerImpl* layer = this; layer; layer = layer->parent()) { + if (layer == ancestor) + return true; + } + + return false; +} + +void LayerImpl::SetScrollParent(LayerImpl* parent) { + if (scroll_parent_ == parent) + return; + + // Having both a scroll parent and a scroll offset delegate is unsupported. + DCHECK(!scroll_offset_delegate_); + + if (scroll_parent_) + scroll_parent_->RemoveScrollChild(this); + + scroll_parent_ = parent; +} + +void LayerImpl::SetScrollChildren(std::set<LayerImpl*>* children) { + if (scroll_children_.get() == children) + return; + scroll_children_.reset(children); +} + +void LayerImpl::RemoveScrollChild(LayerImpl* child) { + DCHECK(scroll_children_); + scroll_children_->erase(child); + if (scroll_children_->empty()) + scroll_children_.reset(); +} + +void LayerImpl::SetClipParent(LayerImpl* ancestor) { + if (clip_parent_ == ancestor) + return; + + if (clip_parent_) + clip_parent_->RemoveClipChild(this); + + clip_parent_ = ancestor; +} + +void LayerImpl::SetClipChildren(std::set<LayerImpl*>* children) { + if (clip_children_.get() == children) + return; + clip_children_.reset(children); +} + +void LayerImpl::RemoveClipChild(LayerImpl* child) { + DCHECK(clip_children_); + clip_children_->erase(child); + if (clip_children_->empty()) + clip_children_.reset(); +} + void LayerImpl::PassCopyRequests(ScopedPtrVector<CopyOutputRequest>* requests) { if (requests->empty()) return; @@ -272,7 +354,7 @@ gfx::Vector2dF LayerImpl::ScrollBy(gfx::Vector2dF scroll) { return unscrolled; } -void LayerImpl::ApplySentScrollDeltas() { +void LayerImpl::ApplySentScrollDeltasFromAbortedCommit() { // Pending tree never has sent scroll deltas DCHECK(layer_tree_impl()->IsActiveTree()); @@ -292,6 +374,25 @@ void LayerImpl::ApplySentScrollDeltas() { sent_scroll_delta_ = gfx::Vector2d(); } +void LayerImpl::ApplyScrollDeltasSinceBeginFrame() { + // Only the pending tree can have missing scrolls. + DCHECK(layer_tree_impl()->IsPendingTree()); + if (!scrollable()) + return; + + // Pending tree should never have sent scroll deltas. + DCHECK(sent_scroll_delta().IsZero()); + + LayerImpl* active_twin = layer_tree_impl()->FindActiveTreeLayerById(id()); + if (active_twin) { + // Scrolls that happens after begin frame (where the sent scroll delta + // comes from) and commit need to be applied to the pending tree + // so that it is up to date with the total scroll. + SetScrollDelta(active_twin->ScrollDelta() - + active_twin->sent_scroll_delta()); + } +} + InputHandler::ScrollStatus LayerImpl::TryScroll( gfx::PointF screen_space_point, InputHandler::ScrollInputType type) const { @@ -419,9 +520,40 @@ void LayerImpl::PushPropertiesTo(LayerImpl* layer) { layer->SetTransform(transform_); layer->SetScrollable(scrollable_); - layer->SetScrollOffset(scroll_offset_); + layer->SetScrollOffsetAndDelta( + scroll_offset_, layer->ScrollDelta() - layer->sent_scroll_delta()); + layer->SetSentScrollDelta(gfx::Vector2d()); + layer->SetMaxScrollOffset(max_scroll_offset_); + LayerImpl* scroll_parent = NULL; + if (scroll_parent_) + scroll_parent = layer->layer_tree_impl()->LayerById(scroll_parent_->id()); + + layer->SetScrollParent(scroll_parent); + if (scroll_children_) { + std::set<LayerImpl*>* scroll_children = new std::set<LayerImpl*>; + for (std::set<LayerImpl*>::iterator it = scroll_children_->begin(); + it != scroll_children_->end(); ++it) + scroll_children->insert(layer->layer_tree_impl()->LayerById((*it)->id())); + layer->SetScrollChildren(scroll_children); + } + + LayerImpl* clip_parent = NULL; + if (clip_parent_) { + clip_parent = layer->layer_tree_impl()->LayerById( + clip_parent_->id()); + } + + layer->SetClipParent(clip_parent); + if (clip_children_) { + std::set<LayerImpl*>* clip_children = new std::set<LayerImpl*>; + for (std::set<LayerImpl*>::iterator it = clip_children_->begin(); + it != clip_children_->end(); ++it) + clip_children->insert(layer->layer_tree_impl()->LayerById((*it)->id())); + layer->SetClipChildren(clip_children); + } + layer->PassCopyRequests(©_requests_); // If the main thread commits multiple times before the impl thread actually @@ -431,9 +563,6 @@ void LayerImpl::PushPropertiesTo(LayerImpl* layer) { update_rect_.Union(layer->update_rect()); layer->set_update_rect(update_rect_); - layer->SetScrollDelta(layer->ScrollDelta() - layer->sent_scroll_delta()); - layer->SetSentScrollDelta(gfx::Vector2d()); - layer->SetStackingOrderChanged(stacking_order_changed_); // Reset any state that should be cleared for the next update. @@ -622,7 +751,7 @@ scoped_ptr<LayerImpl> LayerImpl::TakeReplicaLayer() { return replica_layer_.Pass(); } -ScrollbarLayerImpl* LayerImpl::ToScrollbarLayer() { +ScrollbarLayerImplBase* LayerImpl::ToScrollbarLayer() { return NULL; } @@ -847,10 +976,11 @@ void LayerImpl::UpdateScrollbarPositions() { return; last_scroll_offset_ = current_offset; - if (scrollbar_animation_controller_ && - !scrollbar_animation_controller_->IsScrollGestureInProgress()) { - scrollbar_animation_controller_->DidProgrammaticallyUpdateScroll( + if (scrollbar_animation_controller_) { + bool should_animate = scrollbar_animation_controller_->DidScrollUpdate( layer_tree_impl_->CurrentPhysicalTimeTicks()); + if (should_animate) + layer_tree_impl_->StartScrollbarAnimation(); } // Get the current_offset_.y() value for a sanity-check on scrolling @@ -863,6 +993,8 @@ void LayerImpl::UpdateScrollbarPositions() { void LayerImpl::SetScrollOffsetDelegate( LayerScrollOffsetDelegate* scroll_offset_delegate) { + // Having both a scroll parent and a scroll offset delegate is unsupported. + DCHECK(!scroll_parent_); if (!scroll_offset_delegate && scroll_offset_delegate_) { scroll_delta_ = scroll_offset_delegate_->GetTotalScrollOffset() - scroll_offset_; @@ -874,16 +1006,49 @@ void LayerImpl::SetScrollOffsetDelegate( } void LayerImpl::SetScrollOffset(gfx::Vector2d scroll_offset) { - if (scroll_offset_ == scroll_offset) - return; + SetScrollOffsetAndDelta(scroll_offset, ScrollDelta()); +} - scroll_offset_ = scroll_offset; +void LayerImpl::SetScrollOffsetAndDelta(gfx::Vector2d scroll_offset, + gfx::Vector2dF scroll_delta) { + bool changed = false; - if (scroll_offset_delegate_) - scroll_offset_delegate_->SetTotalScrollOffset(TotalScrollOffset()); + if (scroll_offset_ != scroll_offset) { + changed = true; + scroll_offset_ = scroll_offset; - NoteLayerPropertyChangedForSubtree(); - UpdateScrollbarPositions(); + if (scroll_offset_delegate_) + scroll_offset_delegate_->SetTotalScrollOffset(TotalScrollOffset()); + } + + if (ScrollDelta() != scroll_delta) { + changed = true; + if (layer_tree_impl()->IsActiveTree()) { + LayerImpl* pending_twin = + layer_tree_impl()->FindPendingTreeLayerById(id()); + if (pending_twin) { + // The pending twin can't mirror the scroll delta of the active + // layer. Although the delta - sent scroll delta difference is + // identical for both twins, the sent scroll delta for the pending + // layer is zero, as anything that has been sent has been baked + // into the layer's position/scroll offset as a part of commit. + DCHECK(pending_twin->sent_scroll_delta().IsZero()); + pending_twin->SetScrollDelta(scroll_delta - sent_scroll_delta()); + } + } + + if (scroll_offset_delegate_) { + scroll_offset_delegate_->SetTotalScrollOffset(scroll_offset_ + + scroll_delta); + } else { + scroll_delta_ = scroll_delta; + } + } + + if (changed) { + NoteLayerPropertyChangedForSubtree(); + UpdateScrollbarPositions(); + } } gfx::Vector2dF LayerImpl::ScrollDelta() const { @@ -893,31 +1058,7 @@ gfx::Vector2dF LayerImpl::ScrollDelta() const { } void LayerImpl::SetScrollDelta(gfx::Vector2dF scroll_delta) { - if (ScrollDelta() == scroll_delta) - return; - - if (layer_tree_impl()->IsActiveTree()) { - LayerImpl* pending_twin = layer_tree_impl()->FindPendingTreeLayerById(id()); - if (pending_twin) { - // The pending twin can't mirror the scroll delta of the active - // layer. Although the delta - sent scroll delta difference is - // identical for both twins, the sent scroll delta for the pending - // layer is zero, as anything that has been sent has been baked - // into the layer's position/scroll offset as a part of commit. - DCHECK(pending_twin->sent_scroll_delta().IsZero()); - pending_twin->SetScrollDelta(scroll_delta - sent_scroll_delta()); - } - } - - if (scroll_offset_delegate_) { - scroll_offset_delegate_->SetTotalScrollOffset( - scroll_offset_ + scroll_delta); - } else { - scroll_delta_ = scroll_delta; - } - - NoteLayerPropertyChangedForSubtree(); - UpdateScrollbarPositions(); + SetScrollOffsetAndDelta(scroll_offset_, scroll_delta); } gfx::Vector2dF LayerImpl::TotalScrollOffset() const { @@ -951,42 +1092,55 @@ void LayerImpl::SetMaxScrollOffset(gfx::Vector2d max_scroll_offset) { UpdateScrollbarPositions(); } -void LayerImpl::SetScrollbarOpacity(float opacity) { - if (horizontal_scrollbar_layer_) - horizontal_scrollbar_layer_->SetOpacity(opacity); - if (vertical_scrollbar_layer_) - vertical_scrollbar_layer_->SetOpacity(opacity); -} - void LayerImpl::DidBecomeActive() { - if (!layer_tree_impl_->settings().use_linear_fade_scrollbar_animator) + if (layer_tree_impl_->settings().scrollbar_animator == + LayerTreeSettings::NoAnimator) { return; + } bool need_scrollbar_animation_controller = horizontal_scrollbar_layer_ || vertical_scrollbar_layer_; - if (need_scrollbar_animation_controller) { - if (!scrollbar_animation_controller_) { - base::TimeDelta fadeout_delay = base::TimeDelta::FromMilliseconds( - layer_tree_impl_->settings().scrollbar_linear_fade_delay_ms); - base::TimeDelta fadeout_length = base::TimeDelta::FromMilliseconds( - layer_tree_impl_->settings().scrollbar_linear_fade_length_ms); - scrollbar_animation_controller_ = - ScrollbarAnimationControllerLinearFade::Create( - this, fadeout_delay, fadeout_length) - .PassAs<ScrollbarAnimationController>(); - } - } else { + if (!need_scrollbar_animation_controller) { scrollbar_animation_controller_.reset(); + return; + } + + if (scrollbar_animation_controller_) + return; + + switch (layer_tree_impl_->settings().scrollbar_animator) { + case LayerTreeSettings::LinearFade: { + base::TimeDelta fadeout_delay = base::TimeDelta::FromMilliseconds( + layer_tree_impl_->settings().scrollbar_linear_fade_delay_ms); + base::TimeDelta fadeout_length = base::TimeDelta::FromMilliseconds( + layer_tree_impl_->settings().scrollbar_linear_fade_length_ms); + + scrollbar_animation_controller_ = + ScrollbarAnimationControllerLinearFade::Create( + this, fadeout_delay, fadeout_length) + .PassAs<ScrollbarAnimationController>(); + break; + } + case LayerTreeSettings::Thinning: { + scrollbar_animation_controller_ = + ScrollbarAnimationControllerThinning::Create(this) + .PassAs<ScrollbarAnimationController>(); + break; + } + case LayerTreeSettings::NoAnimator: + NOTREACHED(); + break; } } void LayerImpl::SetHorizontalScrollbarLayer( - ScrollbarLayerImpl* scrollbar_layer) { + ScrollbarLayerImplBase* scrollbar_layer) { horizontal_scrollbar_layer_ = scrollbar_layer; if (horizontal_scrollbar_layer_) horizontal_scrollbar_layer_->set_scroll_layer_id(id()); } -void LayerImpl::SetVerticalScrollbarLayer(ScrollbarLayerImpl* scrollbar_layer) { +void LayerImpl::SetVerticalScrollbarLayer( + ScrollbarLayerImplBase* scrollbar_layer) { vertical_scrollbar_layer_ = scrollbar_layer; if (vertical_scrollbar_layer_) vertical_scrollbar_layer_->set_scroll_layer_id(id()); @@ -1034,9 +1188,6 @@ CompositingReasonsAsValue(CompositingReasons reasons) { if (reasons & kCompositingReasonOverflowScrollingTouch) reason_list->AppendString("Is a scrollable overflow element"); - if (reasons & kCompositingReasonBlending) - reason_list->AppendString("Has a blend mode"); - if (reasons & kCompositingReasonAssumedOverlap) reason_list->AppendString("Might overlap a composited animation"); @@ -1109,12 +1260,19 @@ CompositingReasonsAsValue(CompositingReasons reasons) { if (reasons & kCompositingReasonLayerForMask) reason_list->AppendString("Is a mask layer"); + if (reasons & kCompositingReasonOverflowScrollingParent) + reason_list->AppendString("Scroll parent is not an ancestor"); + + if (reasons & kCompositingReasonOutOfFlowClipping) + reason_list->AppendString("Has clipping ancestor"); + return reason_list.PassAs<base::Value>(); } void LayerImpl::AsValueInto(base::DictionaryValue* state) const { TracedValue::MakeDictIntoImplicitSnapshot(state, LayerTypeAsString(), this); state->SetInteger("layer_id", id()); + state->SetString("layer_name", debug_name()); state->Set("bounds", MathUtil::AsValue(bounds()).release()); state->SetInteger("draws_content", DrawsContent()); state->SetInteger("gpu_memory_usage", GPUMemoryUsageInBytes()); @@ -1128,6 +1286,20 @@ void LayerImpl::AsValueInto(base::DictionaryValue* state) const { &clipped); state->Set("layer_quad", MathUtil::AsValue(layer_quad).release()); + if (!touch_event_handler_region_.IsEmpty()) { + state->Set("touch_event_handler_region", + touch_event_handler_region_.AsValue().release()); + } + if (have_wheel_event_handlers_) { + gfx::Rect wheel_rect(content_bounds()); + Region wheel_region(wheel_rect); + state->Set("wheel_event_handler_region", + wheel_region.AsValue().release()); + } + if (!non_fast_scrollable_region_.IsEmpty()) { + state->Set("non_fast_scrollable_region", + non_fast_scrollable_region_.AsValue().release()); + } scoped_ptr<base::ListValue> children_list(new base::ListValue()); for (size_t i = 0; i < children_.size(); ++i) @@ -1137,6 +1309,12 @@ void LayerImpl::AsValueInto(base::DictionaryValue* state) const { state->Set("mask_layer", mask_layer_->AsValue().release()); if (replica_layer_) state->Set("replica_layer", replica_layer_->AsValue().release()); + + if (scroll_parent_) + state->SetInteger("scroll_parent", scroll_parent_->id()); + + if (clip_parent_) + state->SetInteger("clip_parent", clip_parent_->id()); } size_t LayerImpl::GPUMemoryUsageInBytes() const { return 0; } diff --git a/chromium/cc/layers/layer_impl.h b/chromium/cc/layers/layer_impl.h index 90da41a1c6c..ba0ff7b8216 100644 --- a/chromium/cc/layers/layer_impl.h +++ b/chromium/cc/layers/layer_impl.h @@ -5,6 +5,7 @@ #ifndef CC_LAYERS_LAYER_IMPL_H_ #define CC_LAYERS_LAYER_IMPL_H_ +#include <set> #include <string> #include "base/logging.h" @@ -44,7 +45,7 @@ class LayerTreeImpl; class QuadSink; class Renderer; class ScrollbarAnimationController; -class ScrollbarLayerImpl; +class ScrollbarLayerImplBase; class Layer; struct AppendQuadsData; @@ -83,6 +84,38 @@ class CC_EXPORT LayerImpl : LayerAnimationValueObserver { // Warning: This does not preserve tree structure invariants. void ClearChildList(); + bool HasAncestor(const LayerImpl* ancestor) const; + + void SetScrollParent(LayerImpl* parent); + + LayerImpl* scroll_parent() { return scroll_parent_; } + const LayerImpl* scroll_parent() const { return scroll_parent_; } + + void SetScrollChildren(std::set<LayerImpl*>* children); + void RemoveScrollChild(LayerImpl* child); + + std::set<LayerImpl*>* scroll_children() { return scroll_children_.get(); } + const std::set<LayerImpl*>* scroll_children() const { + return scroll_children_.get(); + } + + void SetClipParent(LayerImpl* ancestor); + + LayerImpl* clip_parent() { + return clip_parent_; + } + const LayerImpl* clip_parent() const { + return clip_parent_; + } + + void SetClipChildren(std::set<LayerImpl*>* children); + void RemoveClipChild(LayerImpl* child); + + std::set<LayerImpl*>* clip_children() { return clip_children_.get(); } + const std::set<LayerImpl*>* clip_children() const { + return clip_children_.get(); + } + void PassCopyRequests(ScopedPtrVector<CopyOutputRequest>* requests); void TakeCopyRequestsAndTransformToTarget( ScopedPtrVector<CopyOutputRequest>* request); @@ -128,7 +161,7 @@ class CC_EXPORT LayerImpl : LayerAnimationValueObserver { virtual void UpdateTilePriorities() {} - virtual ScrollbarLayerImpl* ToScrollbarLayer(); + virtual ScrollbarLayerImplBase* ToScrollbarLayer(); // Returns true if this layer has content to draw. void SetDrawsContent(bool draws_content); @@ -284,6 +317,9 @@ class CC_EXPORT LayerImpl : LayerAnimationValueObserver { RenderSurfaceImpl* render_surface() const { return draw_properties_.render_surface.get(); } + int num_unclipped_descendants() const { + return draw_properties_.num_unclipped_descendants; + } // The client should be responsible for setting bounds, content bounds and // contents scale to appropriate values. LayerImpl doesn't calculate any of @@ -310,6 +346,8 @@ class CC_EXPORT LayerImpl : LayerAnimationValueObserver { void SetScrollOffsetDelegate( LayerScrollOffsetDelegate* scroll_offset_delegate); void SetScrollOffset(gfx::Vector2d scroll_offset); + void SetScrollOffsetAndDelta(gfx::Vector2d scroll_offset, + gfx::Vector2dF scroll_delta); gfx::Vector2d scroll_offset() const { return scroll_offset_; } void SetMaxScrollOffset(gfx::Vector2d max_scroll_offset); @@ -330,7 +368,8 @@ class CC_EXPORT LayerImpl : LayerAnimationValueObserver { void SetScrollable(bool scrollable) { scrollable_ = scrollable; } bool scrollable() const { return scrollable_; } - void ApplySentScrollDeltas(); + void ApplySentScrollDeltasFromAbortedCommit(); + void ApplyScrollDeltasSinceBeginFrame(); void SetShouldScrollOnMainThread(bool should_scroll_on_main_thread) { should_scroll_on_main_thread_ = should_scroll_on_main_thread; @@ -413,15 +452,13 @@ class CC_EXPORT LayerImpl : LayerAnimationValueObserver { return scrollbar_animation_controller_.get(); } - void SetScrollbarOpacity(float opacity); - - void SetHorizontalScrollbarLayer(ScrollbarLayerImpl* scrollbar_layer); - ScrollbarLayerImpl* horizontal_scrollbar_layer() { + void SetHorizontalScrollbarLayer(ScrollbarLayerImplBase* scrollbar_layer); + ScrollbarLayerImplBase* horizontal_scrollbar_layer() { return horizontal_scrollbar_layer_; } - void SetVerticalScrollbarLayer(ScrollbarLayerImpl* scrollbar_layer); - ScrollbarLayerImpl* vertical_scrollbar_layer() { + void SetVerticalScrollbarLayer(ScrollbarLayerImplBase* scrollbar_layer); + ScrollbarLayerImplBase* vertical_scrollbar_layer() { return vertical_scrollbar_layer_; } @@ -475,6 +512,18 @@ class CC_EXPORT LayerImpl : LayerAnimationValueObserver { // Properties internal to LayerImpl LayerImpl* parent_; OwnedLayerImplList children_; + + LayerImpl* scroll_parent_; + + // Storing a pointer to a set rather than a set since this will be rarely + // used. If this pointer turns out to be too heavy, we could have this (and + // the scroll parent above) be stored in a LayerImpl -> scroll_info + // map somewhere. + scoped_ptr<std::set<LayerImpl*> > scroll_children_; + + LayerImpl* clip_parent_; + scoped_ptr<std::set<LayerImpl*> > clip_children_; + // mask_layer_ can be temporarily stolen during tree sync, we need this ID to // confirm newly assigned layer is still the previous one int mask_layer_id_; @@ -567,8 +616,8 @@ class CC_EXPORT LayerImpl : LayerAnimationValueObserver { // Weak pointers to this layer's scrollbars, if it has them. Updated during // tree synchronization. - ScrollbarLayerImpl* horizontal_scrollbar_layer_; - ScrollbarLayerImpl* vertical_scrollbar_layer_; + ScrollbarLayerImplBase* horizontal_scrollbar_layer_; + ScrollbarLayerImplBase* vertical_scrollbar_layer_; ScopedPtrVector<CopyOutputRequest> copy_requests_; diff --git a/chromium/cc/layers/layer_impl_unittest.cc b/chromium/cc/layers/layer_impl_unittest.cc index 9c6dbfe3790..1ab7a71b2ca 100644 --- a/chromium/cc/layers/layer_impl_unittest.cc +++ b/chromium/cc/layers/layer_impl_unittest.cc @@ -502,7 +502,7 @@ TEST_F(LayerImplScrollTest, ApplySentScrollsNoDelegate) { EXPECT_VECTOR_EQ(scroll_offset, layer()->scroll_offset()); EXPECT_VECTOR_EQ(sent_scroll_delta, layer()->sent_scroll_delta()); - layer()->ApplySentScrollDeltas(); + layer()->ApplySentScrollDeltasFromAbortedCommit(); EXPECT_VECTOR_EQ(scroll_offset + scroll_delta, layer()->TotalScrollOffset()); EXPECT_VECTOR_EQ(scroll_delta - sent_scroll_delta, layer()->ScrollDelta()); @@ -527,7 +527,7 @@ TEST_F(LayerImplScrollTest, ApplySentScrollsWithIgnoringDelegate) { EXPECT_VECTOR_EQ(scroll_offset, layer()->scroll_offset()); EXPECT_VECTOR_EQ(sent_scroll_delta, layer()->sent_scroll_delta()); - layer()->ApplySentScrollDeltas(); + layer()->ApplySentScrollDeltasFromAbortedCommit(); EXPECT_VECTOR_EQ(fixed_offset, layer()->TotalScrollOffset()); EXPECT_VECTOR_EQ(scroll_offset + sent_scroll_delta, layer()->scroll_offset()); @@ -551,7 +551,7 @@ TEST_F(LayerImplScrollTest, ApplySentScrollsWithAcceptingDelegate) { EXPECT_VECTOR_EQ(scroll_offset, layer()->scroll_offset()); EXPECT_VECTOR_EQ(sent_scroll_delta, layer()->sent_scroll_delta()); - layer()->ApplySentScrollDeltas(); + layer()->ApplySentScrollDeltasFromAbortedCommit(); EXPECT_VECTOR_EQ(scroll_offset + scroll_delta, layer()->TotalScrollOffset()); EXPECT_VECTOR_EQ(scroll_offset + sent_scroll_delta, layer()->scroll_offset()); diff --git a/chromium/cc/layers/layer_unittest.cc b/chromium/cc/layers/layer_unittest.cc index bd1e16fa658..c5c5286d2aa 100644 --- a/chromium/cc/layers/layer_unittest.cc +++ b/chromium/cc/layers/layer_unittest.cc @@ -559,7 +559,8 @@ TEST_F(LayerTest, CheckPropertyChangeCausesCorrectBehavior) { EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetTransform( gfx::Transform(0.0, 0.0, 0.0, 0.0, 0.0, 0.0))); EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetDoubleSided(false)); - EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetDebugName("Test Layer")); + EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetTouchEventHandlerRegion( + gfx::Rect(10, 10))); EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetDrawCheckerboardForMissingTiles( !test_layer->DrawCheckerboardForMissingTiles())); EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetForceRenderSurface(true)); diff --git a/chromium/cc/layers/nine_patch_layer.cc b/chromium/cc/layers/nine_patch_layer.cc index e32f6e0b125..5e000072f28 100644 --- a/chromium/cc/layers/nine_patch_layer.cc +++ b/chromium/cc/layers/nine_patch_layer.cc @@ -8,16 +8,55 @@ #include "cc/resources/prioritized_resource.h" #include "cc/resources/resource_update.h" #include "cc/resources/resource_update_queue.h" +#include "cc/resources/scoped_ui_resource.h" +#include "cc/resources/ui_resource_bitmap.h" #include "cc/trees/layer_tree_host.h" namespace cc { + +namespace { + +class ScopedUIResourceHolder : public NinePatchLayer::UIResourceHolder { + public: + static scoped_ptr<ScopedUIResourceHolder> Create(LayerTreeHost* host, + const SkBitmap& skbitmap) { + return make_scoped_ptr(new ScopedUIResourceHolder(host, skbitmap)); + } + virtual UIResourceId id() OVERRIDE { return resource_->id(); } + + private: + ScopedUIResourceHolder(LayerTreeHost* host, const SkBitmap& skbitmap) { + resource_ = ScopedUIResource::Create(host, UIResourceBitmap(skbitmap)); + } + + scoped_ptr<ScopedUIResource> resource_; +}; + +class SharedUIResourceHolder : public NinePatchLayer::UIResourceHolder { + public: + static scoped_ptr<SharedUIResourceHolder> Create(UIResourceId id) { + return make_scoped_ptr(new SharedUIResourceHolder(id)); + } + + virtual UIResourceId id() OVERRIDE { return id_; } + + private: + explicit SharedUIResourceHolder(UIResourceId id) : id_(id) {} + + UIResourceId id_; +}; + +} // anonymous namespace + + +NinePatchLayer::UIResourceHolder::~UIResourceHolder() {} + scoped_refptr<NinePatchLayer> NinePatchLayer::Create() { return make_scoped_refptr(new NinePatchLayer()); } -NinePatchLayer::NinePatchLayer() - : bitmap_dirty_(false) {} +NinePatchLayer::NinePatchLayer() : fill_center_(false) {} NinePatchLayer::~NinePatchLayer() {} @@ -26,90 +65,89 @@ scoped_ptr<LayerImpl> NinePatchLayer::CreateLayerImpl( return NinePatchLayerImpl::Create(tree_impl, id()).PassAs<LayerImpl>(); } -void NinePatchLayer::SetTexturePriorities( - const PriorityCalculator& priority_calc) { - if (resource_ && !resource_->texture()->resource_manager()) { - // Release the resource here, as it is no longer tied to a resource manager. - resource_.reset(); - if (!bitmap_.isNull()) - CreateResource(); - } else if (bitmap_dirty_ && DrawsContent()) { - CreateResource(); - } +void NinePatchLayer::SetLayerTreeHost(LayerTreeHost* host) { + if (host == layer_tree_host()) + return; - if (resource_) { - resource_->texture()->set_request_priority( - PriorityCalculator::UIPriority(true)); - GLenum texture_format = - layer_tree_host()->GetRendererCapabilities().best_texture_format; - resource_->texture()->SetDimensions( - gfx::Size(bitmap_.width(), bitmap_.height()), texture_format); - } -} + Layer::SetLayerTreeHost(host); -void NinePatchLayer::SetBitmap(const SkBitmap& bitmap, gfx::Rect aperture) { - bitmap_ = bitmap; - image_aperture_ = aperture; - bitmap_dirty_ = true; - SetNeedsDisplay(); + // Recreate the resource hold against the new LTH. + RecreateUIResourceHolder(); } -bool NinePatchLayer::Update(ResourceUpdateQueue* queue, - const OcclusionTracker* occlusion) { - bool updated = Layer::Update(queue, occlusion); - - CreateUpdaterIfNeeded(); - - if (resource_ && - (bitmap_dirty_ || resource_->texture()->resource_id() == 0)) { - gfx::Rect content_rect(0, 0, bitmap_.width(), bitmap_.height()); - ResourceUpdate upload = ResourceUpdate::Create(resource_->texture(), - &bitmap_, - content_rect, - content_rect, - gfx::Vector2d()); - queue->AppendFullUpload(upload); - bitmap_dirty_ = false; - updated = true; - } - return updated; +void NinePatchLayer::RecreateUIResourceHolder() { + ui_resource_holder_.reset(); + if (!layer_tree_host() || bitmap_.empty()) + return; + + ui_resource_holder_ = + ScopedUIResourceHolder::Create(layer_tree_host(), bitmap_); } -void NinePatchLayer::CreateUpdaterIfNeeded() { - if (updater_.get()) +void NinePatchLayer::SetBorder(gfx::Rect border) { + if (border == border_) return; + border_ = border; + SetNeedsCommit(); +} - updater_ = ImageLayerUpdater::Create(); +void NinePatchLayer::SetBitmap(const SkBitmap& skbitmap, gfx::Rect aperture) { + image_aperture_ = aperture; + bitmap_ = skbitmap; + + // TODO(ccameron): Remove this. This provides the default border that was + // provided before borders were required to be explicitly provided. Once Blink + // fixes its callers to call SetBorder, this can be removed. + SetBorder(gfx::Rect(aperture.x(), + aperture.y(), + skbitmap.width() - aperture.width(), + skbitmap.height() - aperture.height())); + RecreateUIResourceHolder(); + SetNeedsCommit(); } -void NinePatchLayer::CreateResource() { - DCHECK(!bitmap_.isNull()); - CreateUpdaterIfNeeded(); - updater_->SetBitmap(bitmap_); +void NinePatchLayer::SetUIResourceId(UIResourceId resource_id, + gfx::Rect aperture) { + if (ui_resource_holder_ && ui_resource_holder_->id() == resource_id && + image_aperture_ == aperture) + return; - if (!resource_) { - resource_ = updater_->CreateResource( - layer_tree_host()->contents_texture_manager()); + image_aperture_ = aperture; + if (resource_id) { + ui_resource_holder_ = SharedUIResourceHolder::Create(resource_id); + } else { + ui_resource_holder_.reset(); } + + SetNeedsCommit(); +} + +void NinePatchLayer::SetFillCenter(bool fill_center) { + if (fill_center_ == fill_center) + return; + + fill_center_ = fill_center; + SetNeedsCommit(); } bool NinePatchLayer::DrawsContent() const { - bool draws = !bitmap_.isNull() && - Layer::DrawsContent() && - bitmap_.width() && - bitmap_.height(); - return draws; + return ui_resource_holder_ && ui_resource_holder_->id() && + Layer::DrawsContent(); } void NinePatchLayer::PushPropertiesTo(LayerImpl* layer) { Layer::PushPropertiesTo(layer); NinePatchLayerImpl* layer_impl = static_cast<NinePatchLayerImpl*>(layer); - if (resource_) { - DCHECK(!bitmap_.isNull()); - layer_impl->SetResourceId(resource_->texture()->resource_id()); - layer_impl->SetLayout( - gfx::Size(bitmap_.width(), bitmap_.height()), image_aperture_); + if (!ui_resource_holder_) { + layer_impl->SetUIResourceId(0); + } else { + DCHECK(layer_tree_host()); + + gfx::Size image_size = + layer_tree_host()->GetUIResourceSize(ui_resource_holder_->id()); + layer_impl->SetUIResourceId(ui_resource_holder_->id()); + layer_impl->SetLayout(image_size, image_aperture_, border_, fill_center_); } } diff --git a/chromium/cc/layers/nine_patch_layer.h b/chromium/cc/layers/nine_patch_layer.h index 35d08812cff..5880635389a 100644 --- a/chromium/cc/layers/nine_patch_layer.h +++ b/chromium/cc/layers/nine_patch_layer.h @@ -8,47 +8,60 @@ #include "base/memory/scoped_ptr.h" #include "cc/base/cc_export.h" #include "cc/layers/layer.h" -#include "cc/resources/image_layer_updater.h" -#include "third_party/skia/include/core/SkBitmap.h" +#include "cc/resources/ui_resource_client.h" #include "ui/gfx/rect.h" namespace cc { -class ResourceUpdateQueue; +class LayerTreeHost; +class ScopedUIResource; class CC_EXPORT NinePatchLayer : public Layer { public: static scoped_refptr<NinePatchLayer> Create(); virtual bool DrawsContent() const OVERRIDE; - virtual void SetTexturePriorities(const PriorityCalculator& priority_calc) - OVERRIDE; - virtual bool Update(ResourceUpdateQueue* queue, - const OcclusionTracker* occlusion) OVERRIDE; + virtual void PushPropertiesTo(LayerImpl* layer) OVERRIDE; + virtual void SetLayerTreeHost(LayerTreeHost* host) OVERRIDE; + + // |border| is the space around the center rectangular region in layer space + // (known as aperture in image space). |border.x()| and |border.y()| are the + // size of the left and top boundary, respectively. + // |border.width()-border.x()| and |border.height()-border.y()| are the size + // of the right and bottom boundary, respectively. + void SetBorder(gfx::Rect border); + // aperture is in the pixel space of the bitmap resource and refers to // the center patch of the ninepatch (which is unused in this // implementation). We split off eight rects surrounding it and stick them // on the edges of the layer. The corners are unscaled, the top and bottom // rects are x-stretched to fit, and the left and right rects are // y-stretched to fit. - void SetBitmap(const SkBitmap& bitmap, gfx::Rect aperture); + void SetBitmap(const SkBitmap& skbitmap, gfx::Rect aperture); + + // An alternative way of setting the resource to allow for sharing. + void SetUIResourceId(UIResourceId resource_id, gfx::Rect aperture); + void SetFillCenter(bool fill_center); + + class UIResourceHolder { + public: + virtual UIResourceId id() = 0; + virtual ~UIResourceHolder(); + }; private: NinePatchLayer(); virtual ~NinePatchLayer(); virtual scoped_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl) OVERRIDE; + void RecreateUIResourceHolder(); - void CreateUpdaterIfNeeded(); - void CreateResource(); - - scoped_refptr<ImageLayerUpdater> updater_; - scoped_ptr<LayerUpdater::Resource> resource_; - + gfx::Rect border_; + bool fill_center_; + scoped_ptr<UIResourceHolder> ui_resource_holder_; SkBitmap bitmap_; - bool bitmap_dirty_; // The transparent center region that shows the parent layer's contents in // image space. diff --git a/chromium/cc/layers/nine_patch_layer_impl.cc b/chromium/cc/layers/nine_patch_layer_impl.cc index 103f7656eb8..f609c3d2fda 100644 --- a/chromium/cc/layers/nine_patch_layer_impl.cc +++ b/chromium/cc/layers/nine_patch_layer_impl.cc @@ -6,15 +6,18 @@ #include "base/strings/stringprintf.h" #include "base/values.h" +#include "cc/base/math_util.h" #include "cc/layers/quad_sink.h" #include "cc/quads/texture_draw_quad.h" +#include "cc/trees/layer_tree_impl.h" #include "ui/gfx/rect_f.h" namespace cc { NinePatchLayerImpl::NinePatchLayerImpl(LayerTreeImpl* tree_impl, int id) : LayerImpl(tree_impl, id), - resource_id_(0) {} + fill_center_(false), + ui_resource_id_(0) {} NinePatchLayerImpl::~NinePatchLayerImpl() {} @@ -31,11 +34,8 @@ void NinePatchLayerImpl::PushPropertiesTo(LayerImpl* layer) { LayerImpl::PushPropertiesTo(layer); NinePatchLayerImpl* layer_impl = static_cast<NinePatchLayerImpl*>(layer); - if (!resource_id_) - return; - - layer_impl->SetResourceId(resource_id_); - layer_impl->SetLayout(image_bounds_, image_aperture_); + layer_impl->SetUIResourceId(ui_resource_id_); + layer_impl->SetLayout(image_bounds_, image_aperture_, border_, fill_center_); } static gfx::RectF NormalizedRect(float x, @@ -50,120 +50,188 @@ static gfx::RectF NormalizedRect(float x, height / total_height); } -void NinePatchLayerImpl::SetLayout(gfx::Size image_bounds, gfx::Rect aperture) { +void NinePatchLayerImpl::SetUIResourceId(UIResourceId uid) { + if (uid == ui_resource_id_) + return; + ui_resource_id_ = uid; + NoteLayerPropertyChanged(); +} + +void NinePatchLayerImpl::SetLayout(gfx::Size image_bounds, + gfx::Rect aperture, + gfx::Rect border, + bool fill_center) { + // This check imposes an ordering on the call sequence. An UIResource must + // exist before SetLayout can be called. + DCHECK(ui_resource_id_); + + // TODO(ccameron): the following "greater than or equal to" (GE) checks should + // be greater than (GT) to avoid degenerate nine-patches. The relaxed + // condition "equal to" is a workaround for the overhang shadow use case and + // should be investigated further. + + // |border| is in layer space. It cannot exceed the bounds of the layer. + DCHECK(!border.size().IsEmpty()); + DCHECK_GE(bounds().width(), border.width()); + DCHECK_GE(bounds().height(), border.height()); + + // Sanity Check on |border| + DCHECK_LT(border.x(), border.width()); + DCHECK_LT(border.y(), border.height()); + DCHECK_GE(border.x(), 0); + DCHECK_GE(border.y(), 0); + + // |aperture| is in image space. It cannot exceed the bounds of the bitmap. + DCHECK(!aperture.size().IsEmpty()); + DCHECK(gfx::Rect(image_bounds.width(), image_bounds.height()) + .Contains(aperture)); + + // Avoid the degenerate cases where the aperture touches the edge of the + // image. + DCHECK_LT(aperture.width(), image_bounds.width() - 1); + DCHECK_LT(aperture.height(), image_bounds.height() - 1); + DCHECK_GT(aperture.x(), 0); + DCHECK_GT(aperture.y(), 0); + + if (image_bounds_ == image_bounds && image_aperture_ == aperture && + border_ == border && fill_center_ == fill_center) + return; + image_bounds_ = image_bounds; image_aperture_ = aperture; + border_ = border; + fill_center_ = fill_center; + + NoteLayerPropertyChanged(); } bool NinePatchLayerImpl::WillDraw(DrawMode draw_mode, ResourceProvider* resource_provider) { - if (!resource_id_ || draw_mode == DRAW_MODE_RESOURCELESS_SOFTWARE) + if (!ui_resource_id_ || draw_mode == DRAW_MODE_RESOURCELESS_SOFTWARE) return false; return LayerImpl::WillDraw(draw_mode, resource_provider); } void NinePatchLayerImpl::AppendQuads(QuadSink* quad_sink, AppendQuadsData* append_quads_data) { - DCHECK(resource_id_); - SharedQuadState* shared_quad_state = quad_sink->UseSharedQuadState(CreateSharedQuadState()); AppendDebugBorderQuad(quad_sink, shared_quad_state, append_quads_data); + if (!ui_resource_id_) + return; + + ResourceProvider::ResourceId resource = + layer_tree_impl()->ResourceIdForUIResource(ui_resource_id_); + + if (!resource) + return; + static const bool flipped = false; static const bool premultiplied_alpha = true; DCHECK(!bounds().IsEmpty()); - // NinePatch border widths in bitmap pixel space - int left_width = image_aperture_.x(); - int top_height = image_aperture_.y(); - int right_width = image_bounds_.width() - image_aperture_.right(); - int bottom_height = image_bounds_.height() - image_aperture_.bottom(); + // NinePatch border widths in layer space. + int layer_left_width = border_.x(); + int layer_top_height = border_.y(); + int layer_right_width = border_.width() - layer_left_width; + int layer_bottom_height = border_.height() - layer_top_height; - // If layer can't fit the corners, clip to show the outer edges of the - // image. - int corner_total_width = left_width + right_width; - int middle_width = bounds().width() - corner_total_width; - if (middle_width < 0) { - float left_width_proportion = - static_cast<float>(left_width) / corner_total_width; - int left_width_crop = middle_width * left_width_proportion; - left_width += left_width_crop; - right_width = bounds().width() - left_width; - middle_width = 0; - } - int corner_total_height = top_height + bottom_height; - int middle_height = bounds().height() - corner_total_height; - if (middle_height < 0) { - float top_height_proportion = - static_cast<float>(top_height) / corner_total_height; - int top_height_crop = middle_height * top_height_proportion; - top_height += top_height_crop; - bottom_height = bounds().height() - top_height; - middle_height = 0; - } + int layer_middle_width = bounds().width() - border_.width(); + int layer_middle_height = bounds().height() - border_.height(); // Patch positions in layer space - gfx::Rect top_left(0, 0, left_width, top_height); - gfx::Rect top_right( - bounds().width() - right_width, 0, right_width, top_height); - gfx::Rect bottom_left( - 0, bounds().height() - bottom_height, left_width, bottom_height); - gfx::Rect bottom_right( - top_right.x(), bottom_left.y(), right_width, bottom_height); - gfx::Rect top(top_left.right(), 0, middle_width, top_height); - gfx::Rect left(0, top_left.bottom(), left_width, middle_height); - gfx::Rect right(top_right.x(), - top_right.bottom(), - right_width, - left.height()); - gfx::Rect bottom(top.x(), bottom_left.y(), top.width(), bottom_height); - - float img_width = image_bounds_.width(); - float img_height = image_bounds_.height(); - + gfx::Rect layer_top_left(0, 0, layer_left_width, layer_top_height); + gfx::Rect layer_top_right(bounds().width() - layer_right_width, + 0, + layer_right_width, + layer_top_height); + gfx::Rect layer_bottom_left(0, + bounds().height() - layer_bottom_height, + layer_left_width, + layer_bottom_height); + gfx::Rect layer_bottom_right(layer_top_right.x(), + layer_bottom_left.y(), + layer_right_width, + layer_bottom_height); + gfx::Rect layer_top( + layer_top_left.right(), 0, layer_middle_width, layer_top_height); + gfx::Rect layer_left( + 0, layer_top_left.bottom(), layer_left_width, layer_middle_height); + gfx::Rect layer_right(layer_top_right.x(), + layer_top_right.bottom(), + layer_right_width, + layer_left.height()); + gfx::Rect layer_bottom(layer_top.x(), + layer_bottom_left.y(), + layer_top.width(), + layer_bottom_height); + gfx::Rect layer_center(layer_left_width, + layer_top_height, + layer_middle_width, + layer_middle_height); + + // Note the following values are in image (bitmap) space. + float image_width = image_bounds_.width(); + float image_height = image_bounds_.height(); + + int image_aperture_left_width = image_aperture_.x(); + int image_aperture_top_height = image_aperture_.y(); + int image_aperture_right_width = image_width - image_aperture_.right(); + int image_aperture_bottom_height = image_height - image_aperture_.bottom(); // Patch positions in bitmap UV space (from zero to one) gfx::RectF uv_top_left = NormalizedRect(0, 0, - left_width, - top_height, - img_width, - img_height); - gfx::RectF uv_top_right = NormalizedRect(img_width - right_width, - 0, - right_width, - top_height, - img_width, - img_height); - gfx::RectF uv_bottom_left = NormalizedRect(0, - img_height - bottom_height, - left_width, - bottom_height, - img_width, - img_height); - gfx::RectF uv_bottom_right = NormalizedRect(img_width - right_width, - img_height - bottom_height, - right_width, - bottom_height, - img_width, - img_height); - gfx::RectF uv_top(uv_top_left.right(), - 0, - (img_width - left_width - right_width) / img_width, - (top_height) / img_height); + image_aperture_left_width, + image_aperture_top_height, + image_width, + image_height); + gfx::RectF uv_top_right = + NormalizedRect(image_width - image_aperture_right_width, + 0, + image_aperture_right_width, + image_aperture_top_height, + image_width, + image_height); + gfx::RectF uv_bottom_left = + NormalizedRect(0, + image_height - image_aperture_bottom_height, + image_aperture_left_width, + image_aperture_bottom_height, + image_width, + image_height); + gfx::RectF uv_bottom_right = + NormalizedRect(image_width - image_aperture_right_width, + image_height - image_aperture_bottom_height, + image_aperture_right_width, + image_aperture_bottom_height, + image_width, + image_height); + gfx::RectF uv_top( + uv_top_left.right(), + 0, + (image_width - image_aperture_left_width - image_aperture_right_width) / + image_width, + (image_aperture_top_height) / image_height); gfx::RectF uv_left(0, - uv_top_left.bottom(), - left_width / img_width, - (img_height - top_height - bottom_height) / img_height); + uv_top_left.bottom(), + image_aperture_left_width / image_width, + (image_height - image_aperture_top_height - + image_aperture_bottom_height) / + image_height); gfx::RectF uv_right(uv_top_right.x(), - uv_top_right.bottom(), - right_width / img_width, - uv_left.height()); + uv_top_right.bottom(), + image_aperture_right_width / image_width, + uv_left.height()); gfx::RectF uv_bottom(uv_top.x(), - uv_bottom_left.y(), - uv_top.width(), - bottom_height / img_height); + uv_bottom_left.y(), + uv_top.width(), + image_aperture_bottom_height / image_height); + gfx::RectF uv_center(uv_top_left.right(), + uv_top_left.bottom(), + uv_top.width(), + uv_left.height()); // Nothing is opaque here. // TODO(danakj): Should we look at the SkBitmaps to determine opaqueness? @@ -173,9 +241,9 @@ void NinePatchLayerImpl::AppendQuads(QuadSink* quad_sink, quad = TextureDrawQuad::Create(); quad->SetNew(shared_quad_state, - top_left, + layer_top_left, opaque_rect, - resource_id_, + resource, premultiplied_alpha, uv_top_left.origin(), uv_top_left.bottom_right(), @@ -186,9 +254,9 @@ void NinePatchLayerImpl::AppendQuads(QuadSink* quad_sink, quad = TextureDrawQuad::Create(); quad->SetNew(shared_quad_state, - top_right, + layer_top_right, opaque_rect, - resource_id_, + resource, premultiplied_alpha, uv_top_right.origin(), uv_top_right.bottom_right(), @@ -199,9 +267,9 @@ void NinePatchLayerImpl::AppendQuads(QuadSink* quad_sink, quad = TextureDrawQuad::Create(); quad->SetNew(shared_quad_state, - bottom_left, + layer_bottom_left, opaque_rect, - resource_id_, + resource, premultiplied_alpha, uv_bottom_left.origin(), uv_bottom_left.bottom_right(), @@ -212,9 +280,9 @@ void NinePatchLayerImpl::AppendQuads(QuadSink* quad_sink, quad = TextureDrawQuad::Create(); quad->SetNew(shared_quad_state, - bottom_right, + layer_bottom_right, opaque_rect, - resource_id_, + resource, premultiplied_alpha, uv_bottom_right.origin(), uv_bottom_right.bottom_right(), @@ -225,9 +293,9 @@ void NinePatchLayerImpl::AppendQuads(QuadSink* quad_sink, quad = TextureDrawQuad::Create(); quad->SetNew(shared_quad_state, - top, + layer_top, opaque_rect, - resource_id_, + resource, premultiplied_alpha, uv_top.origin(), uv_top.bottom_right(), @@ -238,9 +306,9 @@ void NinePatchLayerImpl::AppendQuads(QuadSink* quad_sink, quad = TextureDrawQuad::Create(); quad->SetNew(shared_quad_state, - left, + layer_left, opaque_rect, - resource_id_, + resource, premultiplied_alpha, uv_left.origin(), uv_left.bottom_right(), @@ -251,9 +319,9 @@ void NinePatchLayerImpl::AppendQuads(QuadSink* quad_sink, quad = TextureDrawQuad::Create(); quad->SetNew(shared_quad_state, - right, + layer_right, opaque_rect, - resource_id_, + resource, premultiplied_alpha, uv_right.origin(), uv_right.bottom_right(), @@ -264,9 +332,9 @@ void NinePatchLayerImpl::AppendQuads(QuadSink* quad_sink, quad = TextureDrawQuad::Create(); quad->SetNew(shared_quad_state, - bottom, + layer_bottom, opaque_rect, - resource_id_, + resource, premultiplied_alpha, uv_bottom.origin(), uv_bottom.bottom_right(), @@ -274,10 +342,21 @@ void NinePatchLayerImpl::AppendQuads(QuadSink* quad_sink, vertex_opacity, flipped); quad_sink->Append(quad.PassAs<DrawQuad>(), append_quads_data); -} -void NinePatchLayerImpl::DidLoseOutputSurface() { - resource_id_ = 0; + if (fill_center_) { + quad = TextureDrawQuad::Create(); + quad->SetNew(shared_quad_state, + layer_center, + opaque_rect, + resource, + premultiplied_alpha, + uv_center.origin(), + uv_center.bottom_right(), + SK_ColorTRANSPARENT, + vertex_opacity, + flipped); + quad_sink->Append(quad.PassAs<DrawQuad>(), append_quads_data); + } } const char* NinePatchLayerImpl::LayerTypeAsString() const { @@ -294,10 +373,12 @@ base::DictionaryValue* NinePatchLayerImpl::LayerTreeAsJson() const { list->AppendInteger(image_aperture_.size().height()); result->Set("ImageAperture", list); - list = new base::ListValue; - list->AppendInteger(image_bounds_.width()); - list->AppendInteger(image_bounds_.height()); - result->Set("ImageBounds", list); + result->Set("ImageBounds", MathUtil::AsValue(image_bounds_).release()); + result->Set("Border", MathUtil::AsValue(border_).release()); + + base::FundamentalValue* fill_center = + base::Value::CreateBooleanValue(fill_center_); + result->Set("FillCenter", fill_center); return result; } diff --git a/chromium/cc/layers/nine_patch_layer_impl.h b/chromium/cc/layers/nine_patch_layer_impl.h index bc442d43f33..ba414aed391 100644 --- a/chromium/cc/layers/nine_patch_layer_impl.h +++ b/chromium/cc/layers/nine_patch_layer_impl.h @@ -10,6 +10,7 @@ #include "cc/base/cc_export.h" #include "cc/layers/layer_impl.h" #include "cc/resources/resource_provider.h" +#include "cc/resources/ui_resource_client.h" #include "ui/gfx/rect.h" #include "ui/gfx/size.h" @@ -27,8 +28,37 @@ class CC_EXPORT NinePatchLayerImpl : public LayerImpl { } virtual ~NinePatchLayerImpl(); - void SetResourceId(unsigned id) { resource_id_ = id; } - void SetLayout(gfx::Size image_bounds, gfx::Rect aperture); + + void SetUIResourceId(UIResourceId uid); + + // The bitmap stretches out the bounds of the layer. The following picture + // illustrates the parameters associated with the dimensions. + // + // Layer space layout Bitmap space layout + // + // ------------------------ ~~~~~~~~~~ W ~~~~~~~~~~ + // | : | : : | + // | C | : Y | + // | : | : : | + // | ------------ | :~~X~~------------ | + // | | | | : | : | + // | | | | : | : | + // |~~A~~| |~~B~~| H | Q | + // | | | | : | : | + // | ------------ | : ~~~~~P~~~~~ | + // | : | : | + // | D | : | + // | : | : | + // ------------------------ ------------------------ + // + // |image_bounds| = (W, H) + // |image_aperture| = (X, Y, P, Q) + // |border| = (A, C, A + B, C + D) + // |fill_center| indicates whether to draw the center quad or not. + void SetLayout(gfx::Size image_bounds, + gfx::Rect image_aperture, + gfx::Rect border, + bool fill_center); virtual scoped_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl) OVERRIDE; @@ -39,7 +69,6 @@ class CC_EXPORT NinePatchLayerImpl : public LayerImpl { virtual void AppendQuads(QuadSink* quad_sink, AppendQuadsData* append_quads_data) OVERRIDE; virtual ResourceProvider::ResourceId ContentsResourceId() const OVERRIDE; - virtual void DidLoseOutputSurface() OVERRIDE; virtual base::DictionaryValue* LayerTreeAsJson() const OVERRIDE; @@ -56,7 +85,12 @@ class CC_EXPORT NinePatchLayerImpl : public LayerImpl { // image space. gfx::Rect image_aperture_; - ResourceProvider::ResourceId resource_id_; + // An inset border that the patches will be mapped to. + gfx::Rect border_; + + bool fill_center_; + + UIResourceId ui_resource_id_; DISALLOW_COPY_AND_ASSIGN(NinePatchLayerImpl); }; diff --git a/chromium/cc/layers/nine_patch_layer_impl_unittest.cc b/chromium/cc/layers/nine_patch_layer_impl_unittest.cc index 1985bd4c29c..0fbc645be1b 100644 --- a/chromium/cc/layers/nine_patch_layer_impl_unittest.cc +++ b/chromium/cc/layers/nine_patch_layer_impl_unittest.cc @@ -2,11 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include <stdio.h> - +#include "base/containers/hash_tables.h" #include "cc/layers/append_quads_data.h" #include "cc/layers/nine_patch_layer_impl.h" #include "cc/quads/texture_draw_quad.h" +#include "cc/resources/ui_resource_bitmap.h" +#include "cc/resources/ui_resource_client.h" #include "cc/test/fake_impl_proxy.h" #include "cc/test/fake_layer_tree_host_impl.h" #include "cc/test/geometry_test_utils.h" @@ -22,6 +23,40 @@ namespace cc { namespace { +class FakeUIResourceLayerTreeHostImpl : public FakeLayerTreeHostImpl { + public: + explicit FakeUIResourceLayerTreeHostImpl(Proxy* proxy) + : FakeLayerTreeHostImpl(proxy), fake_next_resource_id_(1) {} + + virtual void CreateUIResource( + UIResourceId uid, + const UIResourceBitmap& bitmap) OVERRIDE { + if (ResourceIdForUIResource(uid)) + DeleteUIResource(uid); + fake_ui_resource_map_[uid] = fake_next_resource_id_; + } + + virtual void DeleteUIResource(UIResourceId uid) OVERRIDE { + ResourceProvider::ResourceId id = ResourceIdForUIResource(uid); + if (id) + fake_ui_resource_map_.erase(uid); + } + + virtual ResourceProvider::ResourceId ResourceIdForUIResource( + UIResourceId uid) const OVERRIDE { + UIResourceMap::const_iterator iter = fake_ui_resource_map_.find(uid); + if (iter != fake_ui_resource_map_.end()) + return iter->second; + return 0; + } + + private: + ResourceProvider::ResourceId fake_next_resource_id_; + typedef base::hash_map<UIResourceId, ResourceProvider::ResourceId> + UIResourceMap; + UIResourceMap fake_ui_resource_map_; +}; + gfx::Rect ToRoundedIntRect(gfx::RectF rect_f) { return gfx::Rect(gfx::ToRoundedInt(rect_f.x()), gfx::ToRoundedInt(rect_f.y()), @@ -29,19 +64,21 @@ gfx::Rect ToRoundedIntRect(gfx::RectF rect_f) { gfx::ToRoundedInt(rect_f.height())); } -TEST(NinePatchLayerImplTest, VerifyDrawQuads) { - // Input is a 100x100 bitmap with a 40x50 aperture at x=20, y=30. - // The bounds of the layer are set to 400x400, so the draw quads - // generated should leave the border width (40) intact. +void NinePatchLayerLayoutTest(gfx::Size bitmap_size, + gfx::Rect aperture_rect, + gfx::Size layer_size, + gfx::Rect border, + bool fill_center, + size_t expected_quad_size) { MockQuadCuller quad_culler; - gfx::Size bitmap_size(100, 100); - gfx::Size layer_size(400, 400); gfx::Rect visible_content_rect(layer_size); - gfx::Rect aperture_rect(20, 30, 40, 50); - gfx::Rect scaled_aperture_non_uniform(20, 30, 340, 350); + gfx::Rect expected_remaining(border.x(), + border.y(), + layer_size.width() - border.width(), + layer_size.height() - border.height()); FakeImplProxy proxy; - FakeLayerTreeHostImpl host_impl(&proxy); + FakeUIResourceLayerTreeHostImpl host_impl(&proxy); scoped_ptr<NinePatchLayerImpl> layer = NinePatchLayerImpl::Create(host_impl.active_tree(), 1); layer->draw_properties().visible_content_rect = visible_content_rect; @@ -49,21 +86,26 @@ TEST(NinePatchLayerImplTest, VerifyDrawQuads) { layer->SetContentBounds(layer_size); layer->CreateRenderSurface(); layer->draw_properties().render_target = layer.get(); - layer->SetLayout(bitmap_size, aperture_rect); - layer->SetResourceId(1); - // This scale should not affect the generated quad geometry, but only - // the shared draw transform. - gfx::Transform transform; - transform.Scale(10, 10); - layer->draw_properties().target_space_transform = transform; + UIResourceId uid = 1; + SkBitmap skbitmap; + skbitmap.setConfig( + SkBitmap::kARGB_8888_Config, bitmap_size.width(), bitmap_size.height()); + skbitmap.allocPixels(); + skbitmap.setImmutable(); + UIResourceBitmap bitmap(skbitmap); + + host_impl.CreateUIResource(uid, bitmap); + layer->SetUIResourceId(uid); + layer->SetLayout(bitmap_size, aperture_rect, border, fill_center); AppendQuadsData data; layer->AppendQuads(&quad_culler, &data); // Verify quad rects const QuadList& quads = quad_culler.quad_list(); - EXPECT_EQ(8u, quads.size()); + EXPECT_EQ(expected_quad_size, quads.size()); + Region remaining(visible_content_rect); for (size_t i = 0; i < quads.size(); ++i) { DrawQuad* quad = quads[i]; @@ -71,12 +113,16 @@ TEST(NinePatchLayerImplTest, VerifyDrawQuads) { EXPECT_TRUE(visible_content_rect.Contains(quad_rect)) << i; EXPECT_TRUE(remaining.Contains(quad_rect)) << i; - EXPECT_EQ(transform, quad->quadTransform()); remaining.Subtract(Region(quad_rect)); } - EXPECT_RECT_EQ(scaled_aperture_non_uniform, remaining.bounds()); - Region scaled_aperture_region(scaled_aperture_non_uniform); - EXPECT_EQ(scaled_aperture_region, remaining); + + // Check if the left-over quad is the same size as the mapped aperture quad in + // layer space. + if (!fill_center) { + EXPECT_RECT_EQ(expected_remaining, gfx::ToEnclosedRect(remaining.bounds())); + } else { + EXPECT_TRUE(remaining.bounds().IsEmpty()); + } // Verify UV rects gfx::Rect bitmap_rect(bitmap_size); @@ -89,66 +135,59 @@ TEST(NinePatchLayerImplTest, VerifyDrawQuads) { tex_rect.Scale(bitmap_size.width(), bitmap_size.height()); tex_remaining.Subtract(Region(ToRoundedIntRect(tex_rect))); } - EXPECT_RECT_EQ(aperture_rect, tex_remaining.bounds()); - Region aperture_region(aperture_rect); - EXPECT_EQ(aperture_region, tex_remaining); -} - -TEST(NinePatchLayerImplTest, VerifyDrawQuadsForSqueezedLayer) { - // Test with a layer much smaller than the bitmap. - MockQuadCuller quad_culler; - gfx::Size bitmap_size(101, 101); - gfx::Size layer_size(51, 51); - gfx::Rect visible_content_rect(layer_size); - gfx::Rect aperture_rect(20, 30, 40, 45); // rightWidth: 40, botHeight: 25 - - FakeImplProxy proxy; - FakeLayerTreeHostImpl host_impl(&proxy); - scoped_ptr<NinePatchLayerImpl> layer = - NinePatchLayerImpl::Create(host_impl.active_tree(), 1); - layer->draw_properties().visible_content_rect = visible_content_rect; - layer->SetBounds(layer_size); - layer->SetContentBounds(layer_size); - layer->CreateRenderSurface(); - layer->draw_properties().render_target = layer.get(); - layer->SetLayout(bitmap_size, aperture_rect); - layer->SetResourceId(1); - AppendQuadsData data; - layer->AppendQuads(&quad_culler, &data); - - // Verify corner rects fill the layer and don't overlap - const QuadList& quads = quad_culler.quad_list(); - EXPECT_EQ(4u, quads.size()); - Region filled; - for (size_t i = 0; i < quads.size(); ++i) { - DrawQuad* quad = quads[i]; - gfx::Rect quad_rect = quad->rect; - - EXPECT_FALSE(filled.Intersects(quad_rect)); - filled.Union(quad_rect); + if (!fill_center) { + EXPECT_RECT_EQ(aperture_rect, tex_remaining.bounds()); + Region aperture_region(aperture_rect); + EXPECT_EQ(aperture_region, tex_remaining); + } else { + EXPECT_TRUE(remaining.bounds().IsEmpty()); } - Region expected_full(visible_content_rect); - EXPECT_EQ(expected_full, filled); +} - // Verify UV rects cover the corners of the bitmap and the crop is weighted - // proportionately to the relative corner sizes (for uneven apertures). - gfx::Rect bitmap_rect(bitmap_size); - Region tex_remaining(bitmap_rect); - for (size_t i = 0; i < quads.size(); ++i) { - DrawQuad* quad = quads[i]; - const TextureDrawQuad* tex_quad = TextureDrawQuad::MaterialCast(quad); - gfx::RectF tex_rect = - gfx::BoundingRect(tex_quad->uv_top_left, tex_quad->uv_bottom_right); - tex_rect.Scale(bitmap_size.width(), bitmap_size.height()); - tex_remaining.Subtract(Region(ToRoundedIntRect(tex_rect))); - } - Region expected_remaining_region = Region(gfx::Rect(bitmap_size)); - expected_remaining_region.Subtract(gfx::Rect(0, 0, 17, 28)); - expected_remaining_region.Subtract(gfx::Rect(67, 0, 34, 28)); - expected_remaining_region.Subtract(gfx::Rect(0, 78, 17, 23)); - expected_remaining_region.Subtract(gfx::Rect(67, 78, 34, 23)); - EXPECT_EQ(expected_remaining_region, tex_remaining); +TEST(NinePatchLayerImplTest, VerifyDrawQuads) { + // Input is a 100x100 bitmap with a 40x50 aperture at x=20, y=30. + // The bounds of the layer are set to 400x400. + gfx::Size bitmap_size(100, 100); + gfx::Size layer_size(400, 500); + gfx::Rect aperture_rect(20, 30, 40, 50); + gfx::Rect border(40, 40, 80, 80); + bool fill_center = false; + size_t expected_quad_size = 8; + NinePatchLayerLayoutTest(bitmap_size, + aperture_rect, + layer_size, + border, + fill_center, + expected_quad_size); + + // The bounds of the layer are set to less than the bitmap size. + bitmap_size = gfx::Size(100, 100); + layer_size = gfx::Size(40, 50); + aperture_rect = gfx::Rect(20, 30, 40, 50); + border = gfx::Rect(10, 10, 25, 15); + fill_center = true; + expected_quad_size = 9; + NinePatchLayerLayoutTest(bitmap_size, + aperture_rect, + layer_size, + border, + fill_center, + expected_quad_size); + + // Layer and image sizes are equal. + bitmap_size = gfx::Size(100, 100); + layer_size = gfx::Size(100, 100); + aperture_rect = gfx::Rect(20, 30, 40, 50); + border = gfx::Rect(20, 30, 40, 50); + fill_center = true; + expected_quad_size = 9; + NinePatchLayerLayoutTest(bitmap_size, + aperture_rect, + layer_size, + border, + fill_center, + expected_quad_size); } } // namespace diff --git a/chromium/cc/layers/nine_patch_layer_unittest.cc b/chromium/cc/layers/nine_patch_layer_unittest.cc index 5268d02472f..101146c0077 100644 --- a/chromium/cc/layers/nine_patch_layer_unittest.cc +++ b/chromium/cc/layers/nine_patch_layer_unittest.cc @@ -8,9 +8,11 @@ #include "cc/resources/prioritized_resource_manager.h" #include "cc/resources/resource_provider.h" #include "cc/resources/resource_update_queue.h" +#include "cc/resources/scoped_ui_resource.h" #include "cc/scheduler/texture_uploader.h" #include "cc/test/fake_layer_tree_host_client.h" #include "cc/test/fake_output_surface.h" +#include "cc/test/fake_output_surface_client.h" #include "cc/test/geometry_test_utils.h" #include "cc/trees/layer_tree_host.h" #include "cc/trees/occlusion_tracker.h" @@ -54,7 +56,7 @@ class NinePatchLayerTest : public testing::Test { FakeLayerTreeHostClient fake_client_; }; -TEST_F(NinePatchLayerTest, TriggerFullUploadOnceWhenChangingBitmap) { +TEST_F(NinePatchLayerTest, SetBitmap) { scoped_refptr<NinePatchLayer> test_layer = NinePatchLayer::Create(); ASSERT_TRUE(test_layer.get()); test_layer->SetIsDrawable(true); @@ -66,80 +68,60 @@ TEST_F(NinePatchLayerTest, TriggerFullUploadOnceWhenChangingBitmap) { layer_tree_host_->InitializeOutputSurfaceIfNeeded(); - PriorityCalculator calculator; ResourceUpdateQueue queue; OcclusionTracker occlusion_tracker(gfx::Rect(), false); - - // No bitmap set should not trigger any uploads. test_layer->SavePaintProperties(); - test_layer->SetTexturePriorities(calculator); test_layer->Update(&queue, &occlusion_tracker); - EXPECT_EQ(0u, queue.FullUploadSize()); - EXPECT_EQ(0u, queue.PartialUploadSize()); - // Setting a bitmap set should trigger a single full upload. + EXPECT_FALSE(test_layer->DrawsContent()); + SkBitmap bitmap; bitmap.setConfig(SkBitmap::kARGB_8888_Config, 10, 10); bitmap.allocPixels(); - test_layer->SetBitmap(bitmap, gfx::Rect(5, 5, 1, 1)); - test_layer->SavePaintProperties(); - test_layer->SetTexturePriorities(calculator); + bitmap.setImmutable(); + + gfx::Rect aperture(5, 5, 1, 1); + bool fill_center = false; + test_layer->SetBitmap(bitmap, aperture); + test_layer->SetFillCenter(fill_center); test_layer->Update(&queue, &occlusion_tracker); - EXPECT_EQ(1u, queue.FullUploadSize()); - EXPECT_EQ(0u, queue.PartialUploadSize()); - ResourceUpdate params = queue.TakeFirstFullUpload(); - EXPECT_TRUE(params.texture != NULL); - - // Upload the texture. - layer_tree_host_->contents_texture_manager()->SetMaxMemoryLimitBytes( - 1024 * 1024); - layer_tree_host_->contents_texture_manager()->PrioritizeTextures(); - - scoped_ptr<OutputSurface> output_surface; - scoped_ptr<ResourceProvider> resource_provider; - { - DebugScopedSetImplThread impl_thread(Proxy()); - DebugScopedSetMainThreadBlocked main_thread_blocked(Proxy()); - output_surface = CreateFakeOutputSurface(); - resource_provider = ResourceProvider::Create(output_surface.get(), 0); - params.texture->AcquireBackingTexture(resource_provider.get()); - ASSERT_TRUE(params.texture->have_backing_texture()); - } - // Nothing changed, so no repeated upload. + EXPECT_TRUE(test_layer->DrawsContent()); +} + +TEST_F(NinePatchLayerTest, SetUIResourceId) { + scoped_refptr<NinePatchLayer> test_layer = NinePatchLayer::Create(); + ASSERT_TRUE(test_layer.get()); + test_layer->SetIsDrawable(true); + test_layer->SetBounds(gfx::Size(100, 100)); + + layer_tree_host_->SetRootLayer(test_layer); + Mock::VerifyAndClearExpectations(layer_tree_host_.get()); + EXPECT_EQ(test_layer->layer_tree_host(), layer_tree_host_.get()); + + layer_tree_host_->InitializeOutputSurfaceIfNeeded(); + + ResourceUpdateQueue queue; + OcclusionTracker occlusion_tracker(gfx::Rect(), false); test_layer->SavePaintProperties(); - test_layer->SetTexturePriorities(calculator); test_layer->Update(&queue, &occlusion_tracker); - EXPECT_EQ(0u, queue.FullUploadSize()); - EXPECT_EQ(0u, queue.PartialUploadSize()); - { - DebugScopedSetImplThread impl_thread(Proxy()); - DebugScopedSetMainThreadBlocked main_thread_blocked(Proxy()); - layer_tree_host_->contents_texture_manager()->ClearAllMemory( - resource_provider.get()); - } - // Reupload after eviction - test_layer->SavePaintProperties(); - test_layer->SetTexturePriorities(calculator); + EXPECT_FALSE(test_layer->DrawsContent()); + + SkBitmap bitmap; + bitmap.setConfig(SkBitmap::kARGB_8888_Config, 10, 10); + bitmap.allocPixels(); + bitmap.setImmutable(); + + scoped_ptr<ScopedUIResource> resource = ScopedUIResource::Create( + layer_tree_host_.get(), UIResourceBitmap(bitmap)); + gfx::Rect aperture(5, 5, 1, 1); + bool fill_center = true; + test_layer->SetUIResourceId(resource->id(), aperture); + test_layer->SetFillCenter(fill_center); test_layer->Update(&queue, &occlusion_tracker); - EXPECT_EQ(1u, queue.FullUploadSize()); - EXPECT_EQ(0u, queue.PartialUploadSize()); - // PrioritizedResourceManager clearing - layer_tree_host_->contents_texture_manager()->UnregisterTexture( - params.texture); - EXPECT_EQ(NULL, params.texture->resource_manager()); - test_layer->SavePaintProperties(); - test_layer->SetTexturePriorities(calculator); - ResourceUpdateQueue queue2; - test_layer->Update(&queue2, &occlusion_tracker); - EXPECT_EQ(1u, queue2.FullUploadSize()); - EXPECT_EQ(0u, queue2.PartialUploadSize()); - params = queue2.TakeFirstFullUpload(); - EXPECT_TRUE(params.texture != NULL); - EXPECT_EQ(params.texture->resource_manager(), - layer_tree_host_->contents_texture_manager()); + EXPECT_TRUE(test_layer->DrawsContent()); } } // namespace diff --git a/chromium/cc/layers/painted_scrollbar_layer.cc b/chromium/cc/layers/painted_scrollbar_layer.cc new file mode 100644 index 00000000000..4272fe688cc --- /dev/null +++ b/chromium/cc/layers/painted_scrollbar_layer.cc @@ -0,0 +1,244 @@ +// 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/layers/painted_scrollbar_layer.h" + +#include "base/auto_reset.h" +#include "base/basictypes.h" +#include "base/debug/trace_event.h" +#include "cc/layers/painted_scrollbar_layer_impl.h" +#include "cc/resources/ui_resource_bitmap.h" +#include "cc/trees/layer_tree_host.h" +#include "cc/trees/layer_tree_impl.h" +#include "skia/ext/platform_canvas.h" +#include "skia/ext/refptr.h" +#include "third_party/skia/include/core/SkBitmap.h" +#include "third_party/skia/include/core/SkCanvas.h" +#include "third_party/skia/include/core/SkSize.h" +#include "ui/gfx/skia_util.h" + +namespace cc { + +scoped_ptr<LayerImpl> PaintedScrollbarLayer::CreateLayerImpl( + LayerTreeImpl* tree_impl) { + return PaintedScrollbarLayerImpl::Create( + tree_impl, id(), scrollbar_->Orientation()).PassAs<LayerImpl>(); +} + +scoped_refptr<PaintedScrollbarLayer> PaintedScrollbarLayer::Create( + scoped_ptr<Scrollbar> scrollbar, + int scroll_layer_id) { + return make_scoped_refptr( + new PaintedScrollbarLayer(scrollbar.Pass(), scroll_layer_id)); +} + +PaintedScrollbarLayer::PaintedScrollbarLayer( + scoped_ptr<Scrollbar> scrollbar, + int scroll_layer_id) + : scrollbar_(scrollbar.Pass()), + scroll_layer_id_(scroll_layer_id), + thumb_thickness_(scrollbar_->ThumbThickness()), + thumb_length_(scrollbar_->ThumbLength()) { + if (!scrollbar_->IsOverlay()) + SetShouldScrollOnMainThread(true); +} + +PaintedScrollbarLayer::~PaintedScrollbarLayer() {} + +int PaintedScrollbarLayer::ScrollLayerId() const { + return scroll_layer_id_; +} + +void PaintedScrollbarLayer::SetScrollLayerId(int id) { + if (id == scroll_layer_id_) + return; + + scroll_layer_id_ = id; + SetNeedsFullTreeSync(); +} + +bool PaintedScrollbarLayer::OpacityCanAnimateOnImplThread() const { + return scrollbar_->IsOverlay(); +} + +ScrollbarOrientation PaintedScrollbarLayer::orientation() const { + return scrollbar_->Orientation(); +} + +int PaintedScrollbarLayer::MaxTextureSize() { + DCHECK(layer_tree_host()); + return layer_tree_host()->GetRendererCapabilities().max_texture_size; +} + +float PaintedScrollbarLayer::ClampScaleToMaxTextureSize(float scale) { + // If the scaled content_bounds() is bigger than the max texture size of the + // device, we need to clamp it by rescaling, since content_bounds() is used + // below to set the texture size. + gfx::Size scaled_bounds = ComputeContentBoundsForScale(scale, scale); + if (scaled_bounds.width() > MaxTextureSize() || + scaled_bounds.height() > MaxTextureSize()) { + if (scaled_bounds.width() > scaled_bounds.height()) + return (MaxTextureSize() - 1) / static_cast<float>(bounds().width()); + else + return (MaxTextureSize() - 1) / static_cast<float>(bounds().height()); + } + return scale; +} + +void PaintedScrollbarLayer::CalculateContentsScale( + float ideal_contents_scale, + float device_scale_factor, + float page_scale_factor, + bool animating_transform_to_screen, + float* contents_scale_x, + float* contents_scale_y, + gfx::Size* content_bounds) { + ContentsScalingLayer::CalculateContentsScale( + ClampScaleToMaxTextureSize(ideal_contents_scale), + device_scale_factor, + page_scale_factor, + animating_transform_to_screen, + contents_scale_x, + contents_scale_y, + content_bounds); +} + +void PaintedScrollbarLayer::PushPropertiesTo(LayerImpl* layer) { + ContentsScalingLayer::PushPropertiesTo(layer); + + PaintedScrollbarLayerImpl* scrollbar_layer = + static_cast<PaintedScrollbarLayerImpl*>(layer); + + scrollbar_layer->SetThumbThickness(thumb_thickness_); + scrollbar_layer->SetThumbLength(thumb_length_); + if (orientation() == HORIZONTAL) { + scrollbar_layer->SetTrackStart( + track_rect_.x() - location_.x()); + scrollbar_layer->SetTrackLength(track_rect_.width()); + } else { + scrollbar_layer->SetTrackStart( + track_rect_.y() - location_.y()); + scrollbar_layer->SetTrackLength(track_rect_.height()); + } + + if (track_resource_.get()) + scrollbar_layer->set_track_ui_resource_id(track_resource_->id()); + if (thumb_resource_.get()) + scrollbar_layer->set_thumb_ui_resource_id(thumb_resource_->id()); + + scrollbar_layer->set_is_overlay_scrollbar(scrollbar_->IsOverlay()); + + // PaintedScrollbarLayer must push properties every frame. crbug.com/259095 + needs_push_properties_ = true; +} + +ScrollbarLayerInterface* PaintedScrollbarLayer::ToScrollbarLayer() { + return this; +} + +void PaintedScrollbarLayer::SetLayerTreeHost(LayerTreeHost* host) { + // When the LTH is set to null or has changed, then this layer should remove + // all of its associated resources. + if (!host || host != layer_tree_host()) { + track_resource_.reset(); + thumb_resource_.reset(); + } + + ContentsScalingLayer::SetLayerTreeHost(host); +} + +gfx::Rect PaintedScrollbarLayer::ScrollbarLayerRectToContentRect( + gfx::Rect layer_rect) const { + // Don't intersect with the bounds as in LayerRectToContentRect() because + // layer_rect here might be in coordinates of the containing layer. + gfx::Rect expanded_rect = gfx::ScaleToEnclosingRect( + layer_rect, contents_scale_y(), contents_scale_y()); + // We should never return a rect bigger than the content_bounds(). + gfx::Size clamped_size = expanded_rect.size(); + clamped_size.SetToMin(content_bounds()); + expanded_rect.set_size(clamped_size); + return expanded_rect; +} + +gfx::Rect PaintedScrollbarLayer::OriginThumbRect() const { + gfx::Size thumb_size; + if (orientation() == HORIZONTAL) { + thumb_size = + gfx::Size(scrollbar_->ThumbLength(), scrollbar_->ThumbThickness()); + } else { + thumb_size = + gfx::Size(scrollbar_->ThumbThickness(), scrollbar_->ThumbLength()); + } + return ScrollbarLayerRectToContentRect(gfx::Rect(thumb_size)); +} + +void PaintedScrollbarLayer::UpdateThumbAndTrackGeometry() { + track_rect_ = scrollbar_->TrackRect(); + location_ = scrollbar_->Location(); + if (scrollbar_->HasThumb()) { + thumb_thickness_ = scrollbar_->ThumbThickness(); + thumb_length_ = scrollbar_->ThumbLength(); + } +} + +bool PaintedScrollbarLayer::Update(ResourceUpdateQueue* queue, + const OcclusionTracker* occlusion) { + UpdateThumbAndTrackGeometry(); + + gfx::Rect scaled_track_rect = ScrollbarLayerRectToContentRect( + gfx::Rect(location_, bounds())); + + if (track_rect_.IsEmpty() || scaled_track_rect.IsEmpty()) + return false; + + { + base::AutoReset<bool> ignore_set_needs_commit(&ignore_set_needs_commit_, + true); + ContentsScalingLayer::Update(queue, occlusion); + } + + track_resource_ = ScopedUIResource::Create( + layer_tree_host(), RasterizeScrollbarPart(scaled_track_rect, TRACK)); + gfx::Rect thumb_rect = OriginThumbRect(); + + if (scrollbar_->HasThumb() && !thumb_rect.IsEmpty()) { + thumb_resource_ = ScopedUIResource::Create( + layer_tree_host(), RasterizeScrollbarPart(thumb_rect, THUMB)); + } + + return true; +} + +UIResourceBitmap PaintedScrollbarLayer::RasterizeScrollbarPart( + gfx::Rect rect, + ScrollbarPart part) { + DCHECK(!rect.size().IsEmpty()); + + SkBitmap skbitmap; + skbitmap.setConfig(SkBitmap::kARGB_8888_Config, rect.width(), rect.height()); + skbitmap.allocPixels(); + + SkCanvas skcanvas(skbitmap); + skcanvas.translate(SkFloatToScalar(-rect.x()), SkFloatToScalar(-rect.y())); + skcanvas.scale(SkFloatToScalar(contents_scale_x()), + SkFloatToScalar(contents_scale_y())); + + gfx::Rect layer_rect = gfx::ScaleToEnclosingRect( + rect, 1.f / contents_scale_x(), 1.f / contents_scale_y()); + SkRect layer_skrect = RectToSkRect(layer_rect); + SkPaint paint; + paint.setAntiAlias(false); + paint.setXfermodeMode(SkXfermode::kClear_Mode); + skcanvas.drawRect(layer_skrect, paint); + skcanvas.clipRect(layer_skrect); + + scrollbar_->PaintPart(&skcanvas, part, layer_rect); + // Make sure that the pixels are no longer mutable to unavoid unnecessary + // allocation and copying. + skbitmap.setImmutable(); + + return UIResourceBitmap(skbitmap); +} + +} // namespace cc diff --git a/chromium/cc/layers/scrollbar_layer.h b/chromium/cc/layers/painted_scrollbar_layer.h index 6d0e58f7a4e..e84377d1e5a 100644 --- a/chromium/cc/layers/scrollbar_layer.h +++ b/chromium/cc/layers/painted_scrollbar_layer.h @@ -1,40 +1,41 @@ -// Copyright 2012 The Chromium Authors. All rights reserved. +// 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. -#ifndef CC_LAYERS_SCROLLBAR_LAYER_H_ -#define CC_LAYERS_SCROLLBAR_LAYER_H_ +#ifndef CC_LAYERS_PAINTED_SCROLLBAR_LAYER_H_ +#define CC_LAYERS_PAINTED_SCROLLBAR_LAYER_H_ #include "cc/base/cc_export.h" #include "cc/input/scrollbar.h" #include "cc/layers/contents_scaling_layer.h" +#include "cc/layers/scrollbar_layer_interface.h" #include "cc/layers/scrollbar_theme_painter.h" #include "cc/resources/layer_updater.h" +#include "cc/resources/scoped_ui_resource.h" namespace cc { -class CachingBitmapContentLayerUpdater; -class ResourceUpdateQueue; class ScrollbarThemeComposite; -class CC_EXPORT ScrollbarLayer : public ContentsScalingLayer { +class CC_EXPORT PaintedScrollbarLayer : public ScrollbarLayerInterface, + public ContentsScalingLayer { public: virtual scoped_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl) OVERRIDE; - static scoped_refptr<ScrollbarLayer> Create( + static scoped_refptr<PaintedScrollbarLayer> Create( scoped_ptr<Scrollbar> scrollbar, int scroll_layer_id); - int scroll_layer_id() const { return scroll_layer_id_; } - void SetScrollLayerId(int id); - virtual bool OpacityCanAnimateOnImplThread() const OVERRIDE; + virtual ScrollbarLayerInterface* ToScrollbarLayer() OVERRIDE; + + // ScrollbarLayerInterface + virtual int ScrollLayerId() const OVERRIDE; + virtual void SetScrollLayerId(int id) OVERRIDE; - ScrollbarOrientation Orientation() const; + virtual ScrollbarOrientation orientation() const OVERRIDE; // Layer interface - virtual void SetTexturePriorities(const PriorityCalculator& priority_calc) - OVERRIDE; virtual bool Update(ResourceUpdateQueue* queue, const OcclusionTracker* occlusion) OVERRIDE; virtual void SetLayerTreeHost(LayerTreeHost* host) OVERRIDE; @@ -47,49 +48,45 @@ class CC_EXPORT ScrollbarLayer : public ContentsScalingLayer { float* contents_scale_y, gfx::Size* content_bounds) OVERRIDE; - virtual ScrollbarLayer* ToScrollbarLayer() OVERRIDE; - protected: - ScrollbarLayer(scoped_ptr<Scrollbar> scrollbar, - int scroll_layer_id); - virtual ~ScrollbarLayer(); + PaintedScrollbarLayer(scoped_ptr<Scrollbar> scrollbar, int scroll_layer_id); + virtual ~PaintedScrollbarLayer(); + + // For unit tests + UIResourceId track_resource_id() { + return track_resource_.get() ? track_resource_->id() : 0; + } + UIResourceId thumb_resource_id() { + return thumb_resource_.get() ? thumb_resource_->id() : 0; + } + void UpdateThumbAndTrackGeometry(); private: - bool UpdatePart(CachingBitmapContentLayerUpdater* painter, - LayerUpdater::Resource* resource, - gfx::Rect rect, - ResourceUpdateQueue* queue); - void CreateUpdaterIfNeeded(); gfx::Rect ScrollbarLayerRectToContentRect(gfx::Rect layer_rect) const; gfx::Rect OriginThumbRect() const; - bool is_dirty() const { return !dirty_rect_.IsEmpty(); } - int MaxTextureSize(); float ClampScaleToMaxTextureSize(float scale); + UIResourceBitmap RasterizeScrollbarPart(gfx::Rect rect, + ScrollbarPart part); + scoped_ptr<Scrollbar> scrollbar_; + int scroll_layer_id_; + // Snapshot of properties taken in UpdateThumbAndTrackGeometry and used in + // PushPropertiesTo. int thumb_thickness_; int thumb_length_; - gfx::Rect track_rect_; gfx::Point location_; - int scroll_layer_id_; - - unsigned texture_format_; - - gfx::RectF dirty_rect_; - - scoped_refptr<CachingBitmapContentLayerUpdater> track_updater_; - scoped_refptr<CachingBitmapContentLayerUpdater> thumb_updater_; + gfx::Rect track_rect_; - // All the parts of the scrollbar except the thumb - scoped_ptr<LayerUpdater::Resource> track_; - scoped_ptr<LayerUpdater::Resource> thumb_; + scoped_ptr<ScopedUIResource> track_resource_; + scoped_ptr<ScopedUIResource> thumb_resource_; - DISALLOW_COPY_AND_ASSIGN(ScrollbarLayer); + DISALLOW_COPY_AND_ASSIGN(PaintedScrollbarLayer); }; } // namespace cc -#endif // CC_LAYERS_SCROLLBAR_LAYER_H_ +#endif // CC_LAYERS_PAINTED_SCROLLBAR_LAYER_H_ diff --git a/chromium/cc/layers/painted_scrollbar_layer_impl.cc b/chromium/cc/layers/painted_scrollbar_layer_impl.cc new file mode 100644 index 00000000000..e38a1a640be --- /dev/null +++ b/chromium/cc/layers/painted_scrollbar_layer_impl.cc @@ -0,0 +1,181 @@ +// 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/layers/painted_scrollbar_layer_impl.h" + +#include <algorithm> + +#include "cc/animation/scrollbar_animation_controller.h" +#include "cc/layers/layer.h" +#include "cc/layers/quad_sink.h" +#include "cc/quads/solid_color_draw_quad.h" +#include "cc/quads/texture_draw_quad.h" +#include "cc/trees/layer_tree_impl.h" +#include "cc/trees/layer_tree_settings.h" +#include "ui/gfx/rect_conversions.h" + +namespace cc { + +scoped_ptr<PaintedScrollbarLayerImpl> PaintedScrollbarLayerImpl::Create( + LayerTreeImpl* tree_impl, + int id, + ScrollbarOrientation orientation) { + return make_scoped_ptr( + new PaintedScrollbarLayerImpl(tree_impl, id, orientation)); +} + +PaintedScrollbarLayerImpl::PaintedScrollbarLayerImpl( + LayerTreeImpl* tree_impl, + int id, + ScrollbarOrientation orientation) + : ScrollbarLayerImplBase(tree_impl, id, orientation, false), + track_ui_resource_id_(0), + thumb_ui_resource_id_(0), + thumb_thickness_(0), + thumb_length_(0), + track_start_(0), + track_length_(0), + vertical_adjust_(0.f), + scroll_layer_id_(Layer::INVALID_ID) {} + +PaintedScrollbarLayerImpl::~PaintedScrollbarLayerImpl() {} + +scoped_ptr<LayerImpl> PaintedScrollbarLayerImpl::CreateLayerImpl( + LayerTreeImpl* tree_impl) { + return PaintedScrollbarLayerImpl::Create(tree_impl, id(), orientation()) + .PassAs<LayerImpl>(); +} + +void PaintedScrollbarLayerImpl::PushPropertiesTo(LayerImpl* layer) { + ScrollbarLayerImplBase::PushPropertiesTo(layer); + + PaintedScrollbarLayerImpl* scrollbar_layer = + static_cast<PaintedScrollbarLayerImpl*>(layer); + + scrollbar_layer->SetThumbThickness(thumb_thickness_); + scrollbar_layer->SetThumbLength(thumb_length_); + scrollbar_layer->SetTrackStart(track_start_); + scrollbar_layer->SetTrackLength(track_length_); + + scrollbar_layer->set_track_ui_resource_id(track_ui_resource_id_); + scrollbar_layer->set_thumb_ui_resource_id(thumb_ui_resource_id_); +} + +bool PaintedScrollbarLayerImpl::WillDraw(DrawMode draw_mode, + ResourceProvider* resource_provider) { + DCHECK(draw_mode != DRAW_MODE_RESOURCELESS_SOFTWARE); + return LayerImpl::WillDraw(draw_mode, resource_provider); +} + +void PaintedScrollbarLayerImpl::AppendQuads( + QuadSink* quad_sink, + AppendQuadsData* append_quads_data) { + bool premultipled_alpha = true; + bool flipped = false; + gfx::PointF uv_top_left(0.f, 0.f); + gfx::PointF uv_bottom_right(1.f, 1.f); + gfx::Rect bounds_rect(bounds()); + gfx::Rect content_bounds_rect(content_bounds()); + + SharedQuadState* shared_quad_state = + quad_sink->UseSharedQuadState(CreateSharedQuadState()); + AppendDebugBorderQuad(quad_sink, shared_quad_state, append_quads_data); + + gfx::Rect thumb_quad_rect = ComputeThumbQuadRect(); + + ResourceProvider::ResourceId thumb_resource_id = + layer_tree_impl()->ResourceIdForUIResource(thumb_ui_resource_id_); + ResourceProvider::ResourceId track_resource_id = + layer_tree_impl()->ResourceIdForUIResource(track_ui_resource_id_); + + if (thumb_resource_id && !thumb_quad_rect.IsEmpty()) { + gfx::Rect opaque_rect; + const float opacity[] = {1.0f, 1.0f, 1.0f, 1.0f}; + scoped_ptr<TextureDrawQuad> quad = TextureDrawQuad::Create(); + quad->SetNew(shared_quad_state, + thumb_quad_rect, + opaque_rect, + thumb_resource_id, + premultipled_alpha, + uv_top_left, + uv_bottom_right, + SK_ColorTRANSPARENT, + opacity, + flipped); + quad_sink->Append(quad.PassAs<DrawQuad>(), append_quads_data); + } + + gfx::Rect track_quad_rect = content_bounds_rect; + if (track_resource_id && !track_quad_rect.IsEmpty()) { + gfx::Rect opaque_rect(contents_opaque() ? track_quad_rect : gfx::Rect()); + const float opacity[] = {1.0f, 1.0f, 1.0f, 1.0f}; + scoped_ptr<TextureDrawQuad> quad = TextureDrawQuad::Create(); + quad->SetNew(shared_quad_state, + track_quad_rect, + opaque_rect, + track_resource_id, + premultipled_alpha, + uv_top_left, + uv_bottom_right, + SK_ColorTRANSPARENT, + opacity, + flipped); + quad_sink->Append(quad.PassAs<DrawQuad>(), append_quads_data); + } +} + +void PaintedScrollbarLayerImpl::DidLoseOutputSurface() { + track_ui_resource_id_ = 0; + thumb_ui_resource_id_ = 0; +} + +void PaintedScrollbarLayerImpl::SetThumbThickness(int thumb_thickness) { + if (thumb_thickness_ == thumb_thickness) + return; + thumb_thickness_ = thumb_thickness; + NoteLayerPropertyChanged(); +} + +int PaintedScrollbarLayerImpl::ThumbThickness() const { + return thumb_thickness_; +} + +void PaintedScrollbarLayerImpl::SetThumbLength(int thumb_length) { + if (thumb_length_ == thumb_length) + return; + thumb_length_ = thumb_length; + NoteLayerPropertyChanged(); +} + +int PaintedScrollbarLayerImpl::ThumbLength() const { + return thumb_length_; +} + +void PaintedScrollbarLayerImpl::SetTrackStart(int track_start) { + if (track_start_ == track_start) + return; + track_start_ = track_start; + NoteLayerPropertyChanged(); +} + +int PaintedScrollbarLayerImpl::TrackStart() const { + return track_start_; +} + +void PaintedScrollbarLayerImpl::SetTrackLength(int track_length) { + if (track_length_ == track_length) + return; + track_length_ = track_length; + NoteLayerPropertyChanged(); +} + +float PaintedScrollbarLayerImpl::TrackLength() const { + return track_length_ + (orientation() == VERTICAL ? vertical_adjust() : 0); +} + +const char* PaintedScrollbarLayerImpl::LayerTypeAsString() const { + return "cc::PaintedScrollbarLayerImpl"; +} + +} // namespace cc diff --git a/chromium/cc/layers/painted_scrollbar_layer_impl.h b/chromium/cc/layers/painted_scrollbar_layer_impl.h new file mode 100644 index 00000000000..c9d1f3dc0fc --- /dev/null +++ b/chromium/cc/layers/painted_scrollbar_layer_impl.h @@ -0,0 +1,82 @@ +// 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. + +#ifndef CC_LAYERS_PAINTED_SCROLLBAR_LAYER_IMPL_H_ +#define CC_LAYERS_PAINTED_SCROLLBAR_LAYER_IMPL_H_ + +#include "cc/base/cc_export.h" +#include "cc/input/scrollbar.h" +#include "cc/layers/scrollbar_layer_impl_base.h" +#include "cc/resources/ui_resource_client.h" + +namespace cc { + +class LayerTreeImpl; +class ScrollView; + +class CC_EXPORT PaintedScrollbarLayerImpl : public ScrollbarLayerImplBase { + public: + static scoped_ptr<PaintedScrollbarLayerImpl> Create( + LayerTreeImpl* tree_impl, + int id, + ScrollbarOrientation orientation); + virtual ~PaintedScrollbarLayerImpl(); + + // LayerImpl implementation. + virtual scoped_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl) + OVERRIDE; + virtual void PushPropertiesTo(LayerImpl* layer) OVERRIDE; + + virtual bool WillDraw(DrawMode draw_mode, + ResourceProvider* resource_provider) OVERRIDE; + virtual void AppendQuads(QuadSink* quad_sink, + AppendQuadsData* append_quads_data) OVERRIDE; + + virtual void DidLoseOutputSurface() OVERRIDE; + + void SetThumbThickness(int thumb_thickness); + void SetThumbLength(int thumb_length); + void SetTrackStart(int track_start); + void SetTrackLength(int track_length); + + void set_track_ui_resource_id(UIResourceId uid) { + track_ui_resource_id_ = uid; + } + void set_thumb_ui_resource_id(UIResourceId uid) { + thumb_ui_resource_id_ = uid; + } + + protected: + PaintedScrollbarLayerImpl(LayerTreeImpl* tree_impl, + int id, + ScrollbarOrientation orientation); + + // ScrollbarLayerImplBase implementation. + virtual int ThumbThickness() const OVERRIDE; + virtual int ThumbLength() const OVERRIDE; + virtual float TrackLength() const OVERRIDE; + virtual int TrackStart() const OVERRIDE; + + private: + virtual const char* LayerTypeAsString() const OVERRIDE; + + UIResourceId track_ui_resource_id_; + UIResourceId thumb_ui_resource_id_; + + int thumb_thickness_; + int thumb_length_; + int track_start_; + int track_length_; + + // Difference between the clip layer's height and the visible viewport + // height (which may differ in the presence of top-controls hiding). + float vertical_adjust_; + + int scroll_layer_id_; + + DISALLOW_COPY_AND_ASSIGN(PaintedScrollbarLayerImpl); +}; + +} // namespace cc +#endif // CC_LAYERS_PAINTED_SCROLLBAR_LAYER_IMPL_H_ diff --git a/chromium/cc/layers/picture_layer.cc b/chromium/cc/layers/picture_layer.cc index 889e2fbd3f6..4226f1ba713 100644 --- a/chromium/cc/layers/picture_layer.cc +++ b/chromium/cc/layers/picture_layer.cc @@ -6,6 +6,7 @@ #include "cc/debug/benchmark_instrumentation.h" #include "cc/debug/devtools_instrumentation.h" +#include "cc/layers/content_layer_client.h" #include "cc/layers/picture_layer_impl.h" #include "cc/trees/layer_tree_impl.h" #include "ui/gfx/rect_conversions.h" @@ -36,25 +37,26 @@ scoped_ptr<LayerImpl> PictureLayer::CreateLayerImpl(LayerTreeImpl* tree_impl) { void PictureLayer::PushPropertiesTo(LayerImpl* base_layer) { Layer::PushPropertiesTo(base_layer); - PictureLayerImpl* layer_impl = static_cast<PictureLayerImpl*>(base_layer); - // This should be first so others can use it. - layer_impl->UpdateTwinLayer(); + + if (layer_impl->bounds().IsEmpty()) { + // Update may not get called for an empty layer, so resize here instead. + // Using layer_impl because either bounds() or paint_properties().bounds + // may disagree and either one could have been pushed to layer_impl. + pile_->Resize(layer_impl->bounds()); + pile_->UpdateRecordedRegion(); + } + + if (DrawsContent()) { + DCHECK(paint_properties().bounds == pile_->size()); + } layer_impl->SetIsMask(is_mask_); - layer_impl->CreateTilingSetIfNeeded(); // Unlike other properties, invalidation must always be set on layer_impl. // See PictureLayerImpl::PushPropertiesTo for more details. layer_impl->invalidation_.Clear(); layer_impl->invalidation_.Swap(&pile_invalidation_); layer_impl->pile_ = PicturePileImpl::CreateFromOther(pile_.get()); - layer_impl->SyncFromActiveLayer(); - - // PictureLayer must push properties every frame. - // TODO(danakj): If we can avoid requiring to do CreateTilingSetIfNeeded() and - // SyncFromActiveLayer() on every commit then this could go away, maybe - // conditionally. crbug.com/259402 - needs_push_properties_ = true; } void PictureLayer::SetLayerTreeHost(LayerTreeHost* host) { @@ -114,11 +116,14 @@ bool PictureLayer::Update(ResourceUpdateQueue* queue, pile_invalidation_, visible_layer_rect, rendering_stats_instrumentation()); - if (!updated) { + if (updated) { + SetNeedsPushProperties(); + } else { // If this invalidation did not affect the pile, then it can be cleared as // an optimization. pile_invalidation_.Clear(); } + return updated; } @@ -130,4 +135,22 @@ bool PictureLayer::SupportsLCDText() const { return true; } +skia::RefPtr<SkPicture> PictureLayer::GetPicture() const { + // We could either flatten the PicturePile into a single SkPicture, + // or paint a fresh one depending on what we intend to do with the + // picture. For now we just paint a fresh one to get consistent results. + if (!DrawsContent()) + return skia::RefPtr<SkPicture>(); + + int width = bounds().width(); + int height = bounds().height(); + gfx::RectF opaque; + + skia::RefPtr<SkPicture> picture = skia::AdoptRef(new SkPicture); + SkCanvas* canvas = picture->beginRecording(width, height); + client_->PaintContents(canvas, gfx::Rect(width, height), &opaque); + picture->endRecording(); + return picture; +} + } // namespace cc diff --git a/chromium/cc/layers/picture_layer.h b/chromium/cc/layers/picture_layer.h index 7a9a427d427..d7943307ddc 100644 --- a/chromium/cc/layers/picture_layer.h +++ b/chromium/cc/layers/picture_layer.h @@ -34,6 +34,7 @@ class CC_EXPORT PictureLayer : public Layer { const OcclusionTracker* occlusion) OVERRIDE; virtual void SetIsMask(bool is_mask) OVERRIDE; virtual bool SupportsLCDText() const OVERRIDE; + virtual skia::RefPtr<SkPicture> GetPicture() const OVERRIDE; protected: explicit PictureLayer(ContentLayerClient* client); diff --git a/chromium/cc/layers/picture_layer_impl.cc b/chromium/cc/layers/picture_layer_impl.cc index 2f1878bc3a6..95a4a076520 100644 --- a/chromium/cc/layers/picture_layer_impl.cc +++ b/chromium/cc/layers/picture_layer_impl.cc @@ -45,11 +45,11 @@ PictureLayerImpl::PictureLayerImpl(LayerTreeImpl* tree_impl, int id) raster_contents_scale_(0.f), low_res_raster_contents_scale_(0.f), raster_source_scale_was_animating_(false), - is_using_lcd_text_(tree_impl->settings().can_use_lcd_text) { -} + is_using_lcd_text_(tree_impl->settings().can_use_lcd_text), + needs_post_commit_initialization_(true), + should_update_tile_priorities_(false) {} -PictureLayerImpl::~PictureLayerImpl() { -} +PictureLayerImpl::~PictureLayerImpl() {} const char* PictureLayerImpl::LayerTypeAsString() const { return "cc::PictureLayerImpl"; @@ -60,13 +60,11 @@ scoped_ptr<LayerImpl> PictureLayerImpl::CreateLayerImpl( return PictureLayerImpl::Create(tree_impl, id()).PassAs<LayerImpl>(); } -void PictureLayerImpl::CreateTilingSetIfNeeded() { - DCHECK(layer_tree_impl()->IsPendingTree()); - if (!tilings_) - tilings_.reset(new PictureLayerTilingSet(this, bounds())); -} - void PictureLayerImpl::PushPropertiesTo(LayerImpl* base_layer) { + // It's possible this layer was never drawn or updated (e.g. because it was + // a descendant of an opacity 0 layer). + DoPostCommitInitializationIfNeeded(); + LayerImpl::PushPropertiesTo(base_layer); PictureLayerImpl* layer_impl = static_cast<PictureLayerImpl*>(base_layer); @@ -78,8 +76,9 @@ void PictureLayerImpl::PushPropertiesTo(LayerImpl* base_layer) { layer_impl->SetIsMask(is_mask_); layer_impl->pile_ = pile_; - pile_ = NULL; + // Tilings would be expensive to push, so we swap. This optimization requires + // an extra invalidation in SyncFromActiveLayer. layer_impl->tilings_.swap(tilings_); layer_impl->tilings_->SetClient(layer_impl); if (tilings_) @@ -92,19 +91,19 @@ void PictureLayerImpl::PushPropertiesTo(LayerImpl* base_layer) { layer_impl->low_res_raster_contents_scale_ = low_res_raster_contents_scale_; layer_impl->UpdateLCDTextStatus(is_using_lcd_text_); + layer_impl->needs_post_commit_initialization_ = false; - // As an optimization, don't make a copy of this potentially complex region, - // and swap it directly from the pending to the active layer. In general, any - // property pushed to a LayerImpl continues to live on that LayerImpl. - // However, invalidation is the difference between two main thread frames, so - // it no longer makes sense once the pending tree gets recycled. It will - // always get pushed during PictureLayer::PushPropertiesTo. + // The invalidation on this soon-to-be-recycled layer must be cleared to + // mirror clearing the invalidation in PictureLayer's version of this function + // in case push properties is skipped. layer_impl->invalidation_.Swap(&invalidation_); invalidation_.Clear(); + needs_post_commit_initialization_ = true; } void PictureLayerImpl::AppendQuads(QuadSink* quad_sink, AppendQuadsData* append_quads_data) { + DCHECK(!needs_post_commit_initialization_); gfx::Rect rect(visible_content_rect()); gfx::Rect content_rect(content_bounds()); @@ -137,7 +136,7 @@ void PictureLayerImpl::AppendQuads(QuadSink* quad_sink, opaque_rect, texture_rect, texture_size, - false, + RGBA_8888, quad_content_rect, contents_scale, draw_direct_to_backbuffer, @@ -149,11 +148,6 @@ void PictureLayerImpl::AppendQuads(QuadSink* quad_sink, AppendDebugBorderQuad(quad_sink, shared_quad_state, append_quads_data); - bool clipped = false; - gfx::QuadF target_quad = MathUtil::MapQuad( - draw_transform(), - gfx::QuadF(rect), - &clipped); if (ShowDebugBorders()) { for (PictureLayerTilingSet::CoverageIterator iter( tilings_.get(), contents_scale_x(), rect, ideal_contents_scale_); @@ -228,6 +222,7 @@ void PictureLayerImpl::AppendQuads(QuadSink* quad_sink, const ManagedTileState::TileVersion& tile_version = iter->GetTileVersionForDrawing(); + scoped_ptr<DrawQuad> draw_quad; switch (tile_version.mode()) { case ManagedTileState::TileVersion::RESOURCE_MODE: { gfx::RectF texture_rect = iter.texture_rect(); @@ -245,7 +240,7 @@ void PictureLayerImpl::AppendQuads(QuadSink* quad_sink, texture_rect, iter.texture_size(), tile_version.contents_swizzled()); - quad_sink->Append(quad.PassAs<DrawQuad>(), append_quads_data); + draw_quad = quad.PassAs<DrawQuad>(); break; } case ManagedTileState::TileVersion::PICTURE_PILE_MODE: { @@ -253,22 +248,22 @@ void PictureLayerImpl::AppendQuads(QuadSink* quad_sink, gfx::Rect opaque_rect = iter->opaque_rect(); opaque_rect.Intersect(content_rect); + ResourceProvider* resource_provider = + layer_tree_impl()->resource_provider(); + ResourceFormat format = + resource_provider->memory_efficient_texture_format(); scoped_ptr<PictureDrawQuad> quad = PictureDrawQuad::Create(); quad->SetNew(shared_quad_state, geometry_rect, opaque_rect, texture_rect, iter.texture_size(), - // TODO(reveman): This assumes the renderer will use - // GL_RGBA as format of temporary resource. The need - // to swizzle should instead be determined by the - // renderer. - !PlatformColor::SameComponentOrder(GL_RGBA), + format, iter->content_rect(), iter->contents_scale(), draw_direct_to_backbuffer, pile_); - quad_sink->Append(quad.PassAs<DrawQuad>(), append_quads_data); + draw_quad = quad.PassAs<DrawQuad>(); break; } case ManagedTileState::TileVersion::SOLID_COLOR_MODE: { @@ -277,14 +272,15 @@ void PictureLayerImpl::AppendQuads(QuadSink* quad_sink, geometry_rect, tile_version.get_solid_color(), false); - quad_sink->Append(quad.PassAs<DrawQuad>(), append_quads_data); + draw_quad = quad.PassAs<DrawQuad>(); break; } - default: - NOTREACHED(); } - if (!seen_tilings.size() || seen_tilings.back() != iter.CurrentTiling()) + DCHECK(draw_quad); + quad_sink->Append(draw_quad.Pass(), append_quads_data); + + if (seen_tilings.empty() || seen_tilings.back() != iter.CurrentTiling()) seen_tilings.push_back(iter.CurrentTiling()); } @@ -296,6 +292,15 @@ void PictureLayerImpl::AppendQuads(QuadSink* quad_sink, } void PictureLayerImpl::UpdateTilePriorities() { + DCHECK(!needs_post_commit_initialization_); + CHECK(should_update_tile_priorities_); + + if (!layer_tree_impl()->device_viewport_valid_for_tile_management()) { + for (size_t i = 0; i < tilings_->num_tilings(); ++i) + DCHECK(tilings_->tiling_at(i)->has_ever_been_updated()); + return; + } + if (!tilings_->num_tilings()) return; @@ -314,19 +319,17 @@ void PictureLayerImpl::UpdateTilePriorities() { if (!tiling_needs_update) return; - // At this point, tile priorities are going to be modified. - layer_tree_impl()->WillModifyTilePriorities(); - UpdateLCDTextStatus(can_use_lcd_text()); gfx::Transform current_screen_space_transform = screen_space_transform(); + gfx::Size viewport_size = layer_tree_impl()->DrawViewportSize(); gfx::Rect viewport_in_content_space; gfx::Transform screen_to_layer(gfx::Transform::kSkipInitialization); if (screen_space_transform().GetInverse(&screen_to_layer)) { - gfx::Rect device_viewport(layer_tree_impl()->device_viewport_size()); - viewport_in_content_space = gfx::ToEnclosingRect( - MathUtil::ProjectClippedRect(screen_to_layer, device_viewport)); + viewport_in_content_space = + gfx::ToEnclosingRect(MathUtil::ProjectClippedRect( + screen_to_layer, gfx::Rect(viewport_size))); } WhichTree tree = @@ -335,7 +338,7 @@ void PictureLayerImpl::UpdateTilePriorities() { layer_tree_impl()->settings().max_tiles_for_interest_area; tilings_->UpdateTilePriorities( tree, - layer_tree_impl()->device_viewport_size(), + viewport_size, viewport_in_content_space, visible_content_rect(), last_bounds_, @@ -353,12 +356,15 @@ void PictureLayerImpl::UpdateTilePriorities() { last_screen_space_transform_ = current_screen_space_transform; last_bounds_ = bounds(); last_content_scale_ = contents_scale_x(); + + // Tile priorities were modified. + layer_tree_impl()->DidModifyTilePriorities(); } void PictureLayerImpl::DidBecomeActive() { LayerImpl::DidBecomeActive(); tilings_->DidBecomeActive(); - layer_tree_impl()->WillModifyTilePriorities(); + layer_tree_impl()->DidModifyTilePriorities(); } void PictureLayerImpl::DidBeginTracing() { @@ -380,6 +386,12 @@ void PictureLayerImpl::CalculateContentsScale( float* contents_scale_x, float* contents_scale_y, gfx::Size* content_bounds) { + DoPostCommitInitializationIfNeeded(); + + // This function sets valid raster scales and manages tilings, so tile + // priorities can now be updated. + should_update_tile_priorities_ = true; + if (!CanHaveTilings()) { ideal_page_scale_ = page_scale_factor; ideal_device_scale_ = device_scale_factor; @@ -525,14 +537,10 @@ gfx::Size PictureLayerImpl::CalculateTileSize( return default_tile_size; } -void PictureLayerImpl::SyncFromActiveLayer() { - DCHECK(layer_tree_impl()->IsPendingTree()); - - if (twin_layer_) - SyncFromActiveLayer(twin_layer_); -} - void PictureLayerImpl::SyncFromActiveLayer(const PictureLayerImpl* other) { + DCHECK(!other->needs_post_commit_initialization_); + DCHECK(other->tilings_); + UpdateLCDTextStatus(other->is_using_lcd_text_); if (!DrawsContent()) { @@ -581,6 +589,8 @@ void PictureLayerImpl::SyncFromActiveLayer(const PictureLayerImpl* other) { } else { tilings_->RemoveAllTilings(); } + + SanityCheckTilingState(); } void PictureLayerImpl::SyncTiling( @@ -593,19 +603,11 @@ void PictureLayerImpl::SyncTiling( // get updated prior to drawing or activation. If this tree does not // need update draw properties, then its transforms are up to date and // we can create tiles for this tiling immediately. - if (!layer_tree_impl()->needs_update_draw_properties()) + if (!layer_tree_impl()->needs_update_draw_properties() && + should_update_tile_priorities_) UpdateTilePriorities(); } -void PictureLayerImpl::UpdateTwinLayer() { - DCHECK(layer_tree_impl()->IsPendingTree()); - - twin_layer_ = static_cast<PictureLayerImpl*>( - layer_tree_impl()->FindActiveTreeLayerById(id())); - if (twin_layer_) - twin_layer_->twin_layer_ = this; -} - void PictureLayerImpl::SetIsMask(bool is_mask) { if (is_mask_ == is_mask) return; @@ -617,27 +619,24 @@ void PictureLayerImpl::SetIsMask(bool is_mask) { ResourceProvider::ResourceId PictureLayerImpl::ContentsResourceId() const { gfx::Rect content_rect(content_bounds()); float scale = contents_scale_x(); - for (PictureLayerTilingSet::CoverageIterator - iter(tilings_.get(), scale, content_rect, ideal_contents_scale_); - iter; - ++iter) { - // Mask resource not ready yet. - if (!*iter) - return 0; + PictureLayerTilingSet::CoverageIterator iter( + tilings_.get(), scale, content_rect, ideal_contents_scale_); - const ManagedTileState::TileVersion& tile_version = - iter->GetTileVersionForDrawing(); - if (!tile_version.IsReadyToDraw() || - tile_version.mode() != ManagedTileState::TileVersion::RESOURCE_MODE) - return 0; + // Mask resource not ready yet. + if (!iter || !*iter) + return 0; - // Masks only supported if they fit on exactly one tile. - if (iter.geometry_rect() != content_rect) - return 0; + // Masks only supported if they fit on exactly one tile. + if (iter.geometry_rect() != content_rect) + return 0; - return tile_version.get_resource_id(); - } - return 0; + const ManagedTileState::TileVersion& tile_version = + iter->GetTileVersionForDrawing(); + if (!tile_version.IsReadyToDraw() || + tile_version.mode() != ManagedTileState::TileVersion::RESOURCE_MODE) + return 0; + + return tile_version.get_resource_id(); } void PictureLayerImpl::MarkVisibleResourcesAsRequired() const { @@ -695,7 +694,7 @@ void PictureLayerImpl::MarkVisibleResourcesAsRequired() const { continue; missing_region.Subtract(iter.geometry_rect()); - iter->mark_required_for_activation(); + iter->MarkRequiredForActivation(); } } @@ -720,8 +719,30 @@ void PictureLayerImpl::MarkVisibleResourcesAsRequired() const { if (!missing_region.Intersects(iter.geometry_rect())) continue; - iter->mark_required_for_activation(); + iter->MarkRequiredForActivation(); + } +} + +void PictureLayerImpl::DoPostCommitInitialization() { + DCHECK(needs_post_commit_initialization_); + DCHECK(layer_tree_impl()->IsPendingTree()); + + if (!tilings_) + tilings_.reset(new PictureLayerTilingSet(this, bounds())); + + DCHECK(!twin_layer_); + twin_layer_ = static_cast<PictureLayerImpl*>( + layer_tree_impl()->FindActiveTreeLayerById(id())); + if (twin_layer_) { + DCHECK(!twin_layer_->twin_layer_); + twin_layer_->twin_layer_ = this; + // If the twin has never been pushed to, do not sync from it. + // This can happen if this function is called during activation. + if (!twin_layer_->needs_post_commit_initialization_) + SyncFromActiveLayer(twin_layer_); } + + needs_post_commit_initialization_ = false; } PictureLayerTiling* PictureLayerImpl::AddTiling(float contents_scale) { @@ -747,6 +768,7 @@ void PictureLayerImpl::RemoveTiling(float contents_scale) { break; } } + SanityCheckTilingState(); } namespace { @@ -775,6 +797,7 @@ void PictureLayerImpl::ManageTilings(bool animating_transform_to_screen) { DCHECK(ideal_device_scale_); DCHECK(ideal_source_scale_); DCHECK(CanHaveTilings()); + DCHECK(!needs_post_commit_initialization_); bool change_target_tiling = raster_page_scale_ == 0.f || @@ -790,6 +813,9 @@ void PictureLayerImpl::ManageTilings(bool animating_transform_to_screen) { if (!change_target_tiling) return; + if (!layer_tree_impl()->device_viewport_valid_for_tile_management()) + return; + raster_page_scale_ = ideal_page_scale_; raster_device_scale_ = ideal_device_scale_; raster_source_scale_ = ideal_source_scale_; @@ -829,12 +855,13 @@ void PictureLayerImpl::ManageTilings(bool animating_transform_to_screen) { low_res != high_res) low_res = AddTiling(low_res_raster_contents_scale_); - if (high_res) - high_res->set_resolution(HIGH_RESOLUTION); + high_res->set_resolution(HIGH_RESOLUTION); if (low_res && low_res != high_res) low_res->set_resolution(LOW_RESOLUTION); else if (!low_res && previous_low_res) previous_low_res->set_resolution(LOW_RESOLUTION); + + SanityCheckTilingState(); } bool PictureLayerImpl::ShouldAdjustRasterScale( @@ -947,6 +974,8 @@ void PictureLayerImpl::CleanUpTilingsOnActiveLayer( twin->RemoveTiling(to_remove[i]->contents_scale()); tilings_->Remove(to_remove[i]); } + + SanityCheckTilingState(); } float PictureLayerImpl::MinimumContentsScale() const { @@ -981,6 +1010,10 @@ void PictureLayerImpl::ResetRasterScale() { raster_source_scale_ = 0.f; raster_contents_scale_ = 0.f; low_res_raster_contents_scale_ = 0.f; + + // When raster scales aren't valid, don't update tile priorities until + // this layer has been updated via UpdateDrawProperties. + should_update_tile_priorities_ = false; } bool PictureLayerImpl::CanHaveTilings() const { @@ -1002,6 +1035,22 @@ bool PictureLayerImpl::CanHaveTilingWithScale(float contents_scale) const { return true; } +void PictureLayerImpl::SanityCheckTilingState() const { + if (!DCHECK_IS_ON()) + return; + + if (!CanHaveTilings()) { + DCHECK_EQ(0u, tilings_->num_tilings()); + return; + } + if (tilings_->num_tilings() == 0) + return; + + // MarkVisibleResourcesAsRequired depends on having exactly 1 high res + // tiling to mark its tiles as being required for activation. + DCHECK_EQ(1, tilings_->NumHighResTilings()); +} + void PictureLayerImpl::GetDebugBorderProperties( SkColor* color, float* width) const { @@ -1012,6 +1061,7 @@ void PictureLayerImpl::GetDebugBorderProperties( void PictureLayerImpl::AsValueInto(base::DictionaryValue* state) const { LayerImpl::AsValueInto(state); state->SetDouble("ideal_contents_scale", ideal_contents_scale_); + state->SetDouble("geometry_contents_scale", contents_scale_x()); state->Set("tilings", tilings_->AsValue().release()); state->Set("pictures", pile_->AsValue().release()); state->Set("invalidation", invalidation_.AsValue().release()); @@ -1019,7 +1069,7 @@ void PictureLayerImpl::AsValueInto(base::DictionaryValue* state) const { scoped_ptr<base::ListValue> coverage_tiles(new base::ListValue); for (PictureLayerTilingSet::CoverageIterator iter(tilings_.get(), contents_scale_x(), - gfx::Rect(bounds()), + gfx::Rect(content_bounds()), ideal_contents_scale_); iter; ++iter) { diff --git a/chromium/cc/layers/picture_layer_impl.h b/chromium/cc/layers/picture_layer_impl.h index 25cc5d0b7e7..fd5c0677a98 100644 --- a/chromium/cc/layers/picture_layer_impl.h +++ b/chromium/cc/layers/picture_layer_impl.h @@ -62,11 +62,7 @@ class CC_EXPORT PictureLayerImpl const PictureLayerTiling* tiling) OVERRIDE; // PushPropertiesTo active tree => pending tree. - void SyncFromActiveLayer(); void SyncTiling(const PictureLayerTiling* tiling); - void UpdateTwinLayer(); - - void CreateTilingSetIfNeeded(); // Mask-related functions void SetIsMask(bool is_mask); @@ -92,9 +88,15 @@ class CC_EXPORT PictureLayerImpl void UpdateLCDTextStatus(bool new_status); void ResetRasterScale(); void MarkVisibleResourcesAsRequired() const; + void DoPostCommitInitializationIfNeeded() { + if (needs_post_commit_initialization_) + DoPostCommitInitialization(); + } + void DoPostCommitInitialization(); bool CanHaveTilings() const; bool CanHaveTilingWithScale(float contents_scale) const; + void SanityCheckTilingState() const; virtual void GetDebugBorderProperties( SkColor* color, float* width) const OVERRIDE; @@ -124,6 +126,10 @@ class CC_EXPORT PictureLayerImpl bool raster_source_scale_was_animating_; bool is_using_lcd_text_; + bool needs_post_commit_initialization_; + // A sanity state check to make sure UpdateTilePriorities only gets called + // after a CalculateContentsScale/ManageTilings. + bool should_update_tile_priorities_; friend class PictureLayer; DISALLOW_COPY_AND_ASSIGN(PictureLayerImpl); diff --git a/chromium/cc/layers/picture_layer_impl_unittest.cc b/chromium/cc/layers/picture_layer_impl_unittest.cc index 047a529924a..e1cd39ada41 100644 --- a/chromium/cc/layers/picture_layer_impl_unittest.cc +++ b/chromium/cc/layers/picture_layer_impl_unittest.cc @@ -6,6 +6,7 @@ #include <utility> +#include "cc/debug/test_web_graphics_context_3d.h" #include "cc/layers/append_quads_data.h" #include "cc/layers/picture_layer.h" #include "cc/test/fake_content_layer_client.h" @@ -19,7 +20,7 @@ #include "cc/test/mock_quad_culler.h" #include "cc/trees/layer_tree_impl.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/skia/include/core/SkDevice.h" +#include "third_party/skia/include/core/SkBitmapDevice.h" #include "ui/gfx/rect_conversions.h" namespace cc { @@ -27,7 +28,7 @@ namespace { class MockCanvas : public SkCanvas { public: - explicit MockCanvas(SkDevice* device) : SkCanvas(device) {} + explicit MockCanvas(SkBaseDevice* device) : SkCanvas(device) {} virtual void drawRect(const SkRect& rect, const SkPaint& paint) OVERRIDE { // Capture calls before SkCanvas quickReject() kicks in. @@ -40,14 +41,22 @@ class MockCanvas : public SkCanvas { class PictureLayerImplTest : public testing::Test { public: PictureLayerImplTest() - : host_impl_(ImplSidePaintingSettings(), &proxy_), - id_(7) { - host_impl_.InitializeRenderer(CreateFakeOutputSurface()); - } + : host_impl_(ImplSidePaintingSettings(), &proxy_), id_(7) {} + + explicit PictureLayerImplTest(const LayerTreeSettings& settings) + : host_impl_(settings, &proxy_), id_(7) {} virtual ~PictureLayerImplTest() { } + virtual void SetUp() OVERRIDE { + InitializeRenderer(); + } + + virtual void InitializeRenderer() { + host_impl_.InitializeRenderer(CreateFakeOutputSurface()); + } + void SetupDefaultTrees(gfx::Size layer_bounds) { gfx::Size tile_size(100, 100); @@ -69,7 +78,6 @@ class PictureLayerImplTest : public testing::Test { host_impl_.active_tree()->LayerById(id_)); SetupPendingTree(pending_pile); - pending_layer_->UpdateTwinLayer(); } void AddDefaultTilingsWithInvalidation(const Region& invalidation) { @@ -79,7 +87,6 @@ class PictureLayerImplTest : public testing::Test { for (size_t i = 0; i < active_layer_->tilings()->num_tilings(); ++i) active_layer_->tilings()->tiling_at(i)->CreateAllTilesForTesting(); pending_layer_->set_invalidation(invalidation); - pending_layer_->SyncFromActiveLayer(); for (size_t i = 0; i < pending_layer_->tilings()->num_tilings(); ++i) pending_layer_->tilings()->tiling_at(i)->CreateAllTilesForTesting(); } @@ -98,6 +105,7 @@ class PictureLayerImplTest : public testing::Test { pending_layer_ = static_cast<FakePictureLayerImpl*>( host_impl_.pending_tree()->LayerById(id_)); + pending_layer_->DoPostCommitInitializationIfNeeded(); } static void VerifyAllTilesExistAndHavePile( @@ -180,7 +188,7 @@ class PictureLayerImplTest : public testing::Test { SkBitmap store; store.setConfig(SkBitmap::kNo_Config, 1000, 1000); - SkDevice device(store); + SkBitmapDevice device(store); std::vector<SkRect>::const_iterator rect_iter = rects.begin(); for (tile_iter = tiles.begin(); tile_iter < tiles.end(); tile_iter++) { @@ -241,6 +249,62 @@ TEST_F(PictureLayerImplTest, CloneNoInvalidation) { VerifyAllTilesExistAndHavePile(tilings->tiling_at(i), active_pile.get()); } +TEST_F(PictureLayerImplTest, SuppressUpdateTilePriorities) { + base::TimeTicks time_ticks; + host_impl_.SetCurrentFrameTimeTicks(time_ticks); + + gfx::Size tile_size(100, 100); + gfx::Size layer_bounds(400, 400); + + scoped_refptr<FakePicturePileImpl> pending_pile = + FakePicturePileImpl::CreateFilledPile(tile_size, layer_bounds); + scoped_refptr<FakePicturePileImpl> active_pile = + FakePicturePileImpl::CreateFilledPile(tile_size, layer_bounds); + + SetupTrees(pending_pile, active_pile); + + Region invalidation; + AddDefaultTilingsWithInvalidation(invalidation); + float dummy_contents_scale_x; + float dummy_contents_scale_y; + gfx::Size dummy_content_bounds; + active_layer_->CalculateContentsScale(1.f, + 1.f, + 1.f, + false, + &dummy_contents_scale_x, + &dummy_contents_scale_y, + &dummy_content_bounds); + + EXPECT_TRUE(host_impl_.manage_tiles_needed()); + active_layer_->UpdateTilePriorities(); + host_impl_.ManageTiles(); + EXPECT_FALSE(host_impl_.manage_tiles_needed()); + + time_ticks += base::TimeDelta::FromMilliseconds(200); + host_impl_.SetCurrentFrameTimeTicks(time_ticks); + + // Setting this boolean should cause an early out in UpdateTilePriorities. + bool valid_for_tile_management = false; + host_impl_.SetExternalDrawConstraints(gfx::Transform(), + gfx::Rect(layer_bounds), + gfx::Rect(layer_bounds), + valid_for_tile_management); + active_layer_->UpdateTilePriorities(); + EXPECT_FALSE(host_impl_.manage_tiles_needed()); + + time_ticks += base::TimeDelta::FromMilliseconds(200); + host_impl_.SetCurrentFrameTimeTicks(time_ticks); + + valid_for_tile_management = true; + host_impl_.SetExternalDrawConstraints(gfx::Transform(), + gfx::Rect(layer_bounds), + gfx::Rect(layer_bounds), + valid_for_tile_management); + active_layer_->UpdateTilePriorities(); + EXPECT_TRUE(host_impl_.manage_tiles_needed()); +} + TEST_F(PictureLayerImplTest, ClonePartialInvalidation) { gfx::Size tile_size(100, 100); gfx::Size layer_bounds(400, 400); @@ -822,7 +886,7 @@ TEST_F(PictureLayerImplTest, ClampTilesToToMaxTileSize) { TestWebGraphicsContext3D::Create(); context->set_max_texture_size(140); host_impl_.InitializeRenderer(FakeOutputSurface::Create3d( - context.PassAs<WebKit::WebGraphicsContext3D>()).PassAs<OutputSurface>()); + context.Pass()).PassAs<OutputSurface>()); pending_layer_->CalculateContentsScale( 1.f, 1.f, 1.f, false, &result_scale_x, &result_scale_y, &result_bounds); @@ -873,7 +937,7 @@ TEST_F(PictureLayerImplTest, ClampSingleTileToToMaxTileSize) { TestWebGraphicsContext3D::Create(); context->set_max_texture_size(140); host_impl_.InitializeRenderer(FakeOutputSurface::Create3d( - context.PassAs<WebKit::WebGraphicsContext3D>()).PassAs<OutputSurface>()); + context.Pass()).PassAs<OutputSurface>()); pending_layer_->CalculateContentsScale( 1.f, 1.f, 1.f, false, &result_scale_x, &result_scale_y, &result_bounds); @@ -1007,5 +1071,98 @@ TEST_F(PictureLayerImplTest, MarkRequiredOffscreenTiles) { EXPECT_GT(num_offscreen, 0); } +TEST_F(PictureLayerImplTest, ActivateUninitializedLayer) { + gfx::Size tile_size(100, 100); + gfx::Size layer_bounds(400, 400); + scoped_refptr<FakePicturePileImpl> pending_pile = + FakePicturePileImpl::CreateFilledPile(tile_size, layer_bounds); + + host_impl_.CreatePendingTree(); + LayerTreeImpl* pending_tree = host_impl_.pending_tree(); + + scoped_ptr<FakePictureLayerImpl> pending_layer = + FakePictureLayerImpl::CreateWithPile(pending_tree, id_, pending_pile); + pending_layer->SetDrawsContent(true); + pending_tree->SetRootLayer(pending_layer.PassAs<LayerImpl>()); + + pending_layer_ = static_cast<FakePictureLayerImpl*>( + host_impl_.pending_tree()->LayerById(id_)); + + // Set some state on the pending layer, make sure it is not clobbered + // by a sync from the active layer. This could happen because if the + // pending layer has not been post-commit initialized it will attempt + // to sync from the active layer. + bool default_lcd_text_setting = pending_layer_->is_using_lcd_text(); + pending_layer_->force_set_lcd_text(!default_lcd_text_setting); + EXPECT_TRUE(pending_layer_->needs_post_commit_initialization()); + + host_impl_.ActivatePendingTree(); + + active_layer_ = static_cast<FakePictureLayerImpl*>( + host_impl_.active_tree()->LayerById(id_)); + + EXPECT_EQ(0u, active_layer_->num_tilings()); + EXPECT_EQ(!default_lcd_text_setting, active_layer_->is_using_lcd_text()); + EXPECT_FALSE(active_layer_->needs_post_commit_initialization()); +} + +// Solid color scrollbar setting is required for deferred initialization. +class ImplSidePaintingSolidColorScrollbarSettings + : public ImplSidePaintingSettings { + public: + ImplSidePaintingSolidColorScrollbarSettings() { + solid_color_scrollbars = true; + } +}; + +class DeferredInitPictureLayerImplTest : public PictureLayerImplTest { + public: + DeferredInitPictureLayerImplTest() + : PictureLayerImplTest(ImplSidePaintingSolidColorScrollbarSettings()) {} + + virtual void InitializeRenderer() OVERRIDE { + host_impl_.InitializeRenderer(FakeOutputSurface::CreateDeferredGL( + scoped_ptr<SoftwareOutputDevice>(new SoftwareOutputDevice)) + .PassAs<OutputSurface>()); + } + + virtual void SetUp() OVERRIDE { + PictureLayerImplTest::SetUp(); + + // Create some default active and pending trees. + gfx::Size tile_size(100, 100); + gfx::Size layer_bounds(400, 400); + + scoped_refptr<FakePicturePileImpl> pending_pile = + FakePicturePileImpl::CreateFilledPile(tile_size, layer_bounds); + scoped_refptr<FakePicturePileImpl> active_pile = + FakePicturePileImpl::CreateFilledPile(tile_size, layer_bounds); + + SetupTrees(pending_pile, active_pile); + } +}; + +// This test is really a LayerTreeHostImpl test, in that it makes sure +// that trees need update draw properties after deferred initialization. +// However, this is also a regression test for PictureLayerImpl in that +// not having this update will cause a crash. +TEST_F(DeferredInitPictureLayerImplTest, + PreventUpdateTilePrioritiesDuringLostContext) { + host_impl_.pending_tree()->UpdateDrawProperties(); + host_impl_.active_tree()->UpdateDrawProperties(); + EXPECT_FALSE(host_impl_.pending_tree()->needs_update_draw_properties()); + EXPECT_FALSE(host_impl_.active_tree()->needs_update_draw_properties()); + + FakeOutputSurface* fake_output_surface = + static_cast<FakeOutputSurface*>(host_impl_.output_surface()); + ASSERT_TRUE(fake_output_surface->InitializeAndSetContext3d( + TestContextProvider::Create(), NULL)); + + // These will crash PictureLayerImpl if this is not true. + ASSERT_TRUE(host_impl_.pending_tree()->needs_update_draw_properties()); + ASSERT_TRUE(host_impl_.active_tree()->needs_update_draw_properties()); + host_impl_.active_tree()->UpdateDrawProperties(); +} + } // namespace } // namespace cc diff --git a/chromium/cc/layers/picture_layer_unittest.cc b/chromium/cc/layers/picture_layer_unittest.cc new file mode 100644 index 00000000000..c3dd244c2ef --- /dev/null +++ b/chromium/cc/layers/picture_layer_unittest.cc @@ -0,0 +1,69 @@ +// 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/layers/picture_layer.h" + +#include "cc/layers/content_layer_client.h" +#include "cc/layers/picture_layer_impl.h" +#include "cc/resources/resource_update_queue.h" +#include "cc/test/fake_layer_tree_host.h" +#include "cc/test/fake_picture_layer_impl.h" +#include "cc/test/fake_proxy.h" +#include "cc/test/impl_side_painting_settings.h" +#include "cc/trees/occlusion_tracker.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace cc { +namespace { + +class MockContentLayerClient : public ContentLayerClient { + public: + virtual void PaintContents(SkCanvas* canvas, + gfx::Rect clip, + gfx::RectF* opaque) OVERRIDE {} + virtual void DidChangeLayerCanUseLCDText() OVERRIDE {} +}; + +TEST(PictureLayerTest, NoTilesIfEmptyBounds) { + MockContentLayerClient client; + scoped_refptr<PictureLayer> layer = PictureLayer::Create(&client); + layer->SetBounds(gfx::Size(10, 10)); + + scoped_ptr<FakeLayerTreeHost> host = FakeLayerTreeHost::Create(); + host->SetRootLayer(layer); + layer->SetIsDrawable(true); + layer->SavePaintProperties(); + + OcclusionTracker occlusion(gfx::Rect(0, 0, 1000, 1000), false); + scoped_ptr<ResourceUpdateQueue> queue(new ResourceUpdateQueue); + layer->Update(queue.get(), &occlusion); + + layer->SetBounds(gfx::Size(0, 0)); + layer->SavePaintProperties(); + // Intentionally skipping Update since it would normally be skipped on + // a layer with empty bounds. + + FakeProxy proxy; +#ifndef NDEBUG + proxy.SetCurrentThreadIsImplThread(true); +#endif + { + FakeLayerTreeHostImpl host_impl(ImplSidePaintingSettings(), &proxy); + host_impl.CreatePendingTree(); + scoped_ptr<FakePictureLayerImpl> layer_impl = + FakePictureLayerImpl::Create(host_impl.pending_tree(), 1); + + layer->PushPropertiesTo(layer_impl.get()); + EXPECT_FALSE(layer_impl->CanHaveTilings()); + EXPECT_TRUE(layer_impl->bounds() == gfx::Size(0, 0)); + EXPECT_TRUE(layer_impl->pile()->size() == gfx::Size(0, 0)); + EXPECT_TRUE(layer_impl->pile()->recorded_region().IsEmpty()); + } +#ifndef NDEBUG + proxy.SetCurrentThreadIsImplThread(false); +#endif +} + +} // namespace +} // namespace cc diff --git a/chromium/cc/layers/scrollbar_layer.cc b/chromium/cc/layers/scrollbar_layer.cc deleted file mode 100644 index 95eb6384732..00000000000 --- a/chromium/cc/layers/scrollbar_layer.cc +++ /dev/null @@ -1,353 +0,0 @@ - -// Copyright 2012 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/layers/scrollbar_layer.h" - -#include "base/auto_reset.h" -#include "base/basictypes.h" -#include "base/debug/trace_event.h" -#include "cc/layers/scrollbar_layer_impl.h" -#include "cc/resources/caching_bitmap_content_layer_updater.h" -#include "cc/resources/layer_painter.h" -#include "cc/resources/prioritized_resource.h" -#include "cc/resources/resource_update_queue.h" -#include "cc/trees/layer_tree_host.h" -#include "ui/gfx/rect_conversions.h" - -namespace cc { - -scoped_ptr<LayerImpl> ScrollbarLayer::CreateLayerImpl( - LayerTreeImpl* tree_impl) { - return ScrollbarLayerImpl::Create( - tree_impl, id(), scrollbar_->Orientation()).PassAs<LayerImpl>(); -} - -scoped_refptr<ScrollbarLayer> ScrollbarLayer::Create( - scoped_ptr<Scrollbar> scrollbar, - int scroll_layer_id) { - return make_scoped_refptr(new ScrollbarLayer(scrollbar.Pass(), - scroll_layer_id)); -} - -ScrollbarLayer::ScrollbarLayer( - scoped_ptr<Scrollbar> scrollbar, - int scroll_layer_id) - : scrollbar_(scrollbar.Pass()), - scroll_layer_id_(scroll_layer_id), - texture_format_(GL_INVALID_ENUM) { - if (!scrollbar_->IsOverlay()) - SetShouldScrollOnMainThread(true); -} - -ScrollbarLayer::~ScrollbarLayer() {} - -void ScrollbarLayer::SetScrollLayerId(int id) { - if (id == scroll_layer_id_) - return; - - scroll_layer_id_ = id; - SetNeedsFullTreeSync(); -} - -bool ScrollbarLayer::OpacityCanAnimateOnImplThread() const { - return scrollbar_->IsOverlay(); -} - -ScrollbarOrientation ScrollbarLayer::Orientation() const { - return scrollbar_->Orientation(); -} - -int ScrollbarLayer::MaxTextureSize() { - DCHECK(layer_tree_host()); - return layer_tree_host()->GetRendererCapabilities().max_texture_size; -} - -float ScrollbarLayer::ClampScaleToMaxTextureSize(float scale) { - if (layer_tree_host()->settings().solid_color_scrollbars) - return scale; - - // If the scaled content_bounds() is bigger than the max texture size of the - // device, we need to clamp it by rescaling, since content_bounds() is used - // below to set the texture size. - gfx::Size scaled_bounds = ComputeContentBoundsForScale(scale, scale); - if (scaled_bounds.width() > MaxTextureSize() || - scaled_bounds.height() > MaxTextureSize()) { - if (scaled_bounds.width() > scaled_bounds.height()) - return (MaxTextureSize() - 1) / static_cast<float>(bounds().width()); - else - return (MaxTextureSize() - 1) / static_cast<float>(bounds().height()); - } - return scale; -} - -void ScrollbarLayer::CalculateContentsScale(float ideal_contents_scale, - float device_scale_factor, - float page_scale_factor, - bool animating_transform_to_screen, - float* contents_scale_x, - float* contents_scale_y, - gfx::Size* content_bounds) { - ContentsScalingLayer::CalculateContentsScale( - ClampScaleToMaxTextureSize(ideal_contents_scale), - device_scale_factor, - page_scale_factor, - animating_transform_to_screen, - contents_scale_x, - contents_scale_y, - content_bounds); -} - -void ScrollbarLayer::PushPropertiesTo(LayerImpl* layer) { - ContentsScalingLayer::PushPropertiesTo(layer); - - ScrollbarLayerImpl* scrollbar_layer = static_cast<ScrollbarLayerImpl*>(layer); - - if (layer_tree_host() && - layer_tree_host()->settings().solid_color_scrollbars) { - int thickness_override = - layer_tree_host()->settings().solid_color_scrollbar_thickness_dip; - if (thickness_override != -1) { - scrollbar_layer->SetThumbThickness(thickness_override); - } else { - if (Orientation() == HORIZONTAL) - scrollbar_layer->SetThumbThickness(bounds().height()); - else - scrollbar_layer->SetThumbThickness(bounds().width()); - } - } else { - scrollbar_layer->SetThumbThickness(thumb_thickness_); - } - scrollbar_layer->SetThumbLength(thumb_length_); - if (Orientation() == HORIZONTAL) { - scrollbar_layer->SetTrackStart(track_rect_.x() - location_.x()); - scrollbar_layer->SetTrackLength(track_rect_.width()); - } else { - scrollbar_layer->SetTrackStart(track_rect_.y() - location_.y()); - scrollbar_layer->SetTrackLength(track_rect_.height()); - } - - if (track_ && track_->texture()->have_backing_texture()) - scrollbar_layer->set_track_resource_id(track_->texture()->resource_id()); - else - scrollbar_layer->set_track_resource_id(0); - - if (thumb_ && thumb_->texture()->have_backing_texture()) - scrollbar_layer->set_thumb_resource_id(thumb_->texture()->resource_id()); - else - scrollbar_layer->set_thumb_resource_id(0); - - scrollbar_layer->set_is_overlay_scrollbar(scrollbar_->IsOverlay()); - - // ScrollbarLayer must push properties every frame. crbug.com/259095 - needs_push_properties_ = true; -} - -ScrollbarLayer* ScrollbarLayer::ToScrollbarLayer() { - return this; -} - -void ScrollbarLayer::SetLayerTreeHost(LayerTreeHost* host) { - if (!host || host != layer_tree_host()) { - track_updater_ = NULL; - track_.reset(); - thumb_updater_ = NULL; - thumb_.reset(); - } - - ContentsScalingLayer::SetLayerTreeHost(host); -} - -class ScrollbarPartPainter : public LayerPainter { - public: - ScrollbarPartPainter(Scrollbar* scrollbar, ScrollbarPart part) - : scrollbar_(scrollbar), - part_(part) {} - virtual ~ScrollbarPartPainter() {} - - // LayerPainter implementation - virtual void Paint(SkCanvas* canvas, - gfx::Rect content_rect, - gfx::RectF* opaque) OVERRIDE { - scrollbar_->PaintPart(canvas, part_, content_rect); - } - - private: - Scrollbar* scrollbar_; - ScrollbarPart part_; -}; - -void ScrollbarLayer::CreateUpdaterIfNeeded() { - if (layer_tree_host()->settings().solid_color_scrollbars) - return; - - texture_format_ = - layer_tree_host()->GetRendererCapabilities().best_texture_format; - - if (!track_updater_.get()) { - track_updater_ = CachingBitmapContentLayerUpdater::Create( - scoped_ptr<LayerPainter>( - new ScrollbarPartPainter(scrollbar_.get(), TRACK)) - .Pass(), - rendering_stats_instrumentation(), - id()); - } - if (!track_) { - track_ = track_updater_->CreateResource( - layer_tree_host()->contents_texture_manager()); - } - - if (!thumb_updater_.get()) { - thumb_updater_ = CachingBitmapContentLayerUpdater::Create( - scoped_ptr<LayerPainter>( - new ScrollbarPartPainter(scrollbar_.get(), THUMB)) - .Pass(), - rendering_stats_instrumentation(), - id()); - } - if (!thumb_ && scrollbar_->HasThumb()) { - thumb_ = thumb_updater_->CreateResource( - layer_tree_host()->contents_texture_manager()); - } -} - -bool ScrollbarLayer::UpdatePart(CachingBitmapContentLayerUpdater* painter, - LayerUpdater::Resource* resource, - gfx::Rect rect, - ResourceUpdateQueue* queue) { - if (layer_tree_host()->settings().solid_color_scrollbars) - return false; - - // Skip painting and uploading if there are no invalidations and - // we already have valid texture data. - if (resource->texture()->have_backing_texture() && - resource->texture()->size() == rect.size() && - !is_dirty()) - return false; - - // We should always have enough memory for UI. - DCHECK(resource->texture()->can_acquire_backing_texture()); - if (!resource->texture()->can_acquire_backing_texture()) - return false; - - // Paint and upload the entire part. - gfx::Rect painted_opaque_rect; - painter->PrepareToUpdate(rect, - rect.size(), - contents_scale_x(), - contents_scale_y(), - &painted_opaque_rect); - if (!painter->pixels_did_change() && - resource->texture()->have_backing_texture()) { - TRACE_EVENT_INSTANT0("cc", - "ScrollbarLayer::UpdatePart no texture upload needed", - TRACE_EVENT_SCOPE_THREAD); - return false; - } - - bool partial_updates_allowed = - layer_tree_host()->settings().max_partial_texture_updates > 0; - if (!partial_updates_allowed) - resource->texture()->ReturnBackingTexture(); - - gfx::Vector2d dest_offset(0, 0); - resource->Update(queue, rect, dest_offset, partial_updates_allowed); - return true; -} - -gfx::Rect ScrollbarLayer::ScrollbarLayerRectToContentRect( - gfx::Rect layer_rect) const { - // Don't intersect with the bounds as in LayerRectToContentRect() because - // layer_rect here might be in coordinates of the containing layer. - gfx::Rect expanded_rect = gfx::ScaleToEnclosingRect( - layer_rect, contents_scale_y(), contents_scale_y()); - // We should never return a rect bigger than the content_bounds(). - gfx::Size clamped_size = expanded_rect.size(); - clamped_size.SetToMin(content_bounds()); - expanded_rect.set_size(clamped_size); - return expanded_rect; -} - -void ScrollbarLayer::SetTexturePriorities( - const PriorityCalculator& priority_calc) { - if (layer_tree_host()->settings().solid_color_scrollbars) - return; - - if (content_bounds().IsEmpty()) - return; - DCHECK_LE(content_bounds().width(), MaxTextureSize()); - DCHECK_LE(content_bounds().height(), MaxTextureSize()); - - CreateUpdaterIfNeeded(); - - bool draws_to_root = !render_target()->parent(); - if (track_) { - track_->texture()->SetDimensions(content_bounds(), texture_format_); - track_->texture()->set_request_priority( - PriorityCalculator::UIPriority(draws_to_root)); - } - if (thumb_) { - gfx::Size thumb_size = OriginThumbRect().size(); - thumb_->texture()->SetDimensions(thumb_size, texture_format_); - thumb_->texture()->set_request_priority( - PriorityCalculator::UIPriority(draws_to_root)); - } -} - -bool ScrollbarLayer::Update(ResourceUpdateQueue* queue, - const OcclusionTracker* occlusion) { - track_rect_ = scrollbar_->TrackRect(); - location_ = scrollbar_->Location(); - - if (layer_tree_host()->settings().solid_color_scrollbars) - return false; - - bool updated = false; - - { - base::AutoReset<bool> ignore_set_needs_commit(&ignore_set_needs_commit_, - true); - updated = ContentsScalingLayer::Update(queue, occlusion); - } - - dirty_rect_.Union(update_rect_); - if (content_bounds().IsEmpty()) - return false; - if (visible_content_rect().IsEmpty()) - return false; - - CreateUpdaterIfNeeded(); - - gfx::Rect content_rect = ScrollbarLayerRectToContentRect( - gfx::Rect(scrollbar_->Location(), bounds())); - updated |= UpdatePart(track_updater_.get(), track_.get(), content_rect, - queue); - - if (scrollbar_->HasThumb()) { - thumb_thickness_ = scrollbar_->ThumbThickness(); - thumb_length_ = scrollbar_->ThumbLength(); - gfx::Rect origin_thumb_rect = OriginThumbRect(); - if (!origin_thumb_rect.IsEmpty()) { - updated |= UpdatePart(thumb_updater_.get(), thumb_.get(), - origin_thumb_rect, queue); - } - } - - dirty_rect_ = gfx::RectF(); - return updated; -} - -gfx::Rect ScrollbarLayer::OriginThumbRect() const { - gfx::Size thumb_size; - if (Orientation() == HORIZONTAL) { - thumb_size = gfx::Size(scrollbar_->ThumbLength(), - scrollbar_->ThumbThickness()); - } else { - thumb_size = gfx::Size(scrollbar_->ThumbThickness(), - scrollbar_->ThumbLength()); - } - return ScrollbarLayerRectToContentRect(gfx::Rect(thumb_size)); -} - -} // namespace cc diff --git a/chromium/cc/layers/scrollbar_layer_impl.cc b/chromium/cc/layers/scrollbar_layer_impl.cc deleted file mode 100644 index 1de185a77dd..00000000000 --- a/chromium/cc/layers/scrollbar_layer_impl.cc +++ /dev/null @@ -1,328 +0,0 @@ -// Copyright 2012 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/layers/scrollbar_layer_impl.h" - -#include <algorithm> - -#include "cc/animation/scrollbar_animation_controller.h" -#include "cc/layers/layer.h" -#include "cc/layers/quad_sink.h" -#include "cc/quads/solid_color_draw_quad.h" -#include "cc/quads/texture_draw_quad.h" -#include "cc/trees/layer_tree_impl.h" -#include "cc/trees/layer_tree_settings.h" -#include "ui/gfx/rect_conversions.h" - -namespace cc { - -scoped_ptr<ScrollbarLayerImpl> ScrollbarLayerImpl::Create( - LayerTreeImpl* tree_impl, - int id, - ScrollbarOrientation orientation) { - return make_scoped_ptr(new ScrollbarLayerImpl(tree_impl, - id, - orientation)); -} - -ScrollbarLayerImpl::ScrollbarLayerImpl( - LayerTreeImpl* tree_impl, - int id, - ScrollbarOrientation orientation) - : LayerImpl(tree_impl, id), - track_resource_id_(0), - thumb_resource_id_(0), - current_pos_(0.f), - maximum_(0), - thumb_thickness_(0), - thumb_length_(0), - track_start_(0), - track_length_(0), - orientation_(orientation), - vertical_adjust_(0.f), - visible_to_total_length_ratio_(1.f), - scroll_layer_id_(Layer::INVALID_ID), - is_overlay_scrollbar_(false) {} - -ScrollbarLayerImpl::~ScrollbarLayerImpl() {} - -ScrollbarLayerImpl* ScrollbarLayerImpl::ToScrollbarLayer() { - return this; -} - -scoped_ptr<LayerImpl> ScrollbarLayerImpl::CreateLayerImpl( - LayerTreeImpl* tree_impl) { - return ScrollbarLayerImpl::Create(tree_impl, - id(), - orientation_).PassAs<LayerImpl>(); -} - -void ScrollbarLayerImpl::PushPropertiesTo(LayerImpl* layer) { - LayerImpl::PushPropertiesTo(layer); - - ScrollbarLayerImpl* scrollbar_layer = static_cast<ScrollbarLayerImpl*>(layer); - - scrollbar_layer->SetThumbThickness(thumb_thickness_); - scrollbar_layer->SetThumbLength(thumb_length_); - scrollbar_layer->SetTrackStart(track_start_); - scrollbar_layer->SetTrackLength(track_length_); - scrollbar_layer->set_is_overlay_scrollbar(is_overlay_scrollbar_); - - scrollbar_layer->set_track_resource_id(track_resource_id_); - scrollbar_layer->set_thumb_resource_id(thumb_resource_id_); -} - -bool ScrollbarLayerImpl::WillDraw(DrawMode draw_mode, - ResourceProvider* resource_provider) { - if (draw_mode == DRAW_MODE_RESOURCELESS_SOFTWARE && - !layer_tree_impl()->settings().solid_color_scrollbars) - return false; - return LayerImpl::WillDraw(draw_mode, resource_provider); -} - -void ScrollbarLayerImpl::AppendQuads(QuadSink* quad_sink, - AppendQuadsData* append_quads_data) { - bool premultipled_alpha = true; - bool flipped = false; - gfx::PointF uv_top_left(0.f, 0.f); - gfx::PointF uv_bottom_right(1.f, 1.f); - gfx::Rect bounds_rect(bounds()); - gfx::Rect content_bounds_rect(content_bounds()); - - SharedQuadState* shared_quad_state = - quad_sink->UseSharedQuadState(CreateSharedQuadState()); - AppendDebugBorderQuad(quad_sink, shared_quad_state, append_quads_data); - - gfx::Rect thumb_quad_rect = ComputeThumbQuadRect(); - - if (layer_tree_impl()->settings().solid_color_scrollbars) { - scoped_ptr<SolidColorDrawQuad> quad = SolidColorDrawQuad::Create(); - quad->SetNew(shared_quad_state, - thumb_quad_rect, - layer_tree_impl()->settings().solid_color_scrollbar_color, - false); - quad_sink->Append(quad.PassAs<DrawQuad>(), append_quads_data); - return; - } - - if (thumb_resource_id_ && !thumb_quad_rect.IsEmpty()) { - gfx::Rect opaque_rect; - const float opacity[] = {1.0f, 1.0f, 1.0f, 1.0f}; - scoped_ptr<TextureDrawQuad> quad = TextureDrawQuad::Create(); - quad->SetNew(shared_quad_state, - thumb_quad_rect, - opaque_rect, - thumb_resource_id_, - premultipled_alpha, - uv_top_left, - uv_bottom_right, - SK_ColorTRANSPARENT, - opacity, - flipped); - quad_sink->Append(quad.PassAs<DrawQuad>(), append_quads_data); - } - - if (!track_resource_id_) - return; - - // Order matters here: since the back track texture is being drawn to the - // entire contents rect, we must append it after the thumb and fore track - // quads. The back track texture contains (and displays) the buttons. - if (!content_bounds_rect.IsEmpty()) { - gfx::Rect quad_rect(content_bounds_rect); - gfx::Rect opaque_rect(contents_opaque() ? quad_rect : gfx::Rect()); - const float opacity[] = {1.0f, 1.0f, 1.0f, 1.0f}; - scoped_ptr<TextureDrawQuad> quad = TextureDrawQuad::Create(); - quad->SetNew(shared_quad_state, - quad_rect, - opaque_rect, - track_resource_id_, - premultipled_alpha, - uv_top_left, - uv_bottom_right, - SK_ColorTRANSPARENT, - opacity, - flipped); - quad_sink->Append(quad.PassAs<DrawQuad>(), append_quads_data); - } -} - -ScrollbarOrientation ScrollbarLayerImpl::Orientation() const { - return orientation_; -} - -float ScrollbarLayerImpl::CurrentPos() const { - return current_pos_; -} - -int ScrollbarLayerImpl::Maximum() const { - return maximum_; -} - -gfx::Rect ScrollbarLayerImpl::ScrollbarLayerRectToContentRect( - gfx::RectF layer_rect) const { - // Don't intersect with the bounds as in layerRectToContentRect() because - // layer_rect here might be in coordinates of the containing layer. - gfx::RectF content_rect = gfx::ScaleRect(layer_rect, - contents_scale_x(), - contents_scale_y()); - return gfx::ToEnclosingRect(content_rect); -} - -void ScrollbarLayerImpl::SetThumbThickness(int thumb_thickness) { - if (thumb_thickness_ == thumb_thickness) - return; - thumb_thickness_ = thumb_thickness; - NoteLayerPropertyChanged(); -} - -void ScrollbarLayerImpl::SetThumbLength(int thumb_length) { - if (thumb_length_ == thumb_length) - return; - thumb_length_ = thumb_length; - NoteLayerPropertyChanged(); -} -void ScrollbarLayerImpl::SetTrackStart(int track_start) { - if (track_start_ == track_start) - return; - track_start_ = track_start; - NoteLayerPropertyChanged(); -} - -void ScrollbarLayerImpl::SetTrackLength(int track_length) { - if (track_length_ == track_length) - return; - track_length_ = track_length; - NoteLayerPropertyChanged(); -} - -void ScrollbarLayerImpl::SetVerticalAdjust(float vertical_adjust) { - if (vertical_adjust_ == vertical_adjust) - return; - vertical_adjust_ = vertical_adjust; - NoteLayerPropertyChanged(); -} - -void ScrollbarLayerImpl::SetVisibleToTotalLengthRatio(float ratio) { - if (visible_to_total_length_ratio_ == ratio) - return; - visible_to_total_length_ratio_ = ratio; - NoteLayerPropertyChanged(); -} - -void ScrollbarLayerImpl::SetCurrentPos(float current_pos) { - if (current_pos_ == current_pos) - return; - current_pos_ = current_pos; - NoteLayerPropertyChanged(); -} - -void ScrollbarLayerImpl::SetMaximum(int maximum) { - if (maximum_ == maximum) - return; - maximum_ = maximum; - NoteLayerPropertyChanged(); -} - -gfx::Rect ScrollbarLayerImpl::ComputeThumbQuadRect() const { - // Thumb extent is the length of the thumb in the scrolling direction, thumb - // thickness is in the perpendicular direction. Here's an example of a - // horizontal scrollbar - inputs are above the scrollbar, computed values - // below: - // - // |<------------------- track_length_ ------------------->| - // - // |--| <-- start_offset - // - // +--+----------------------------+------------------+-------+--+ - // |<|| |##################| ||>| - // +--+----------------------------+------------------+-------+--+ - // - // |<- thumb_length ->| - // - // |<------- thumb_offset -------->| - // - // For painted, scrollbars, the length is fixed. For solid color scrollbars we - // have to compute it. The ratio of the thumb's length to the track's length - // is the same as that of the visible viewport to the total viewport, unless - // that would make the thumb's length less than its thickness. - // - // vertical_adjust_ is used when the layer geometry from the main thread is - // not in sync with what the user sees. For instance on Android scrolling the - // top bar controls out of view reveals more of the page content. We want the - // root layer scrollbars to reflect what the user sees even if we haven't - // received new layer geometry from the main thread. If the user has scrolled - // down by 50px and the initial viewport size was 950px the geometry would - // look something like this: - // - // vertical_adjust_ = 50, scroll position 0, visible ratios 99% - // Layer geometry: Desired thumb positions: - // +--------------------+-+ +----------------------+ <-- 0px - // | |v| | #| - // | |e| | #| - // | |r| | #| - // | |t| | #| - // | |i| | #| - // | |c| | #| - // | |a| | #| - // | |l| | #| - // | | | | #| - // | |l| | #| - // | |a| | #| - // | |y| | #| - // | |e| | #| - // | |r| | #| - // +--------------------+-+ | #| - // | horizontal layer | | | #| - // +--------------------+-+ | #| <-- 950px - // | | | #| - // | | |##################### | - // +----------------------+ +----------------------+ <-- 1000px - // - // The layer geometry is set up for a 950px tall viewport, but the user can - // actually see down to 1000px. Thus we have to move the quad for the - // horizontal scrollbar down by the vertical_adjust_ factor and lay the - // vertical thumb out on a track lengthed by the vertical_adjust_ factor. This - // means the quads may extend outside the layer's bounds. - - int thumb_length = thumb_length_; - float track_length = track_length_; - if (orientation_ == VERTICAL) - track_length += vertical_adjust_; - - if (layer_tree_impl()->settings().solid_color_scrollbars) { - thumb_length = std::max( - static_cast<int>(visible_to_total_length_ratio_ * track_length), - thumb_thickness_); - } - - // With the length known, we can compute the thumb's position. - float clamped_current_pos = - std::min(std::max(current_pos_, 0.f), static_cast<float>(maximum_)); - float ratio = clamped_current_pos / maximum_; - float max_offset = track_length - thumb_length; - int thumb_offset = static_cast<int>(ratio * max_offset) + track_start_; - - gfx::RectF thumb_rect; - if (orientation_ == HORIZONTAL) { - thumb_rect = gfx::RectF(thumb_offset, vertical_adjust_, - thumb_length, thumb_thickness_); - } else { - thumb_rect = gfx::RectF(0.f, thumb_offset, - thumb_thickness_, thumb_length); - } - - return ScrollbarLayerRectToContentRect(thumb_rect); -} - -void ScrollbarLayerImpl::DidLoseOutputSurface() { - track_resource_id_ = 0; - thumb_resource_id_ = 0; -} - -const char* ScrollbarLayerImpl::LayerTypeAsString() const { - return "cc::ScrollbarLayerImpl"; -} - -} // namespace cc diff --git a/chromium/cc/layers/scrollbar_layer_impl.h b/chromium/cc/layers/scrollbar_layer_impl.h deleted file mode 100644 index 6347c413dfc..00000000000 --- a/chromium/cc/layers/scrollbar_layer_impl.h +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CC_LAYERS_SCROLLBAR_LAYER_IMPL_H_ -#define CC_LAYERS_SCROLLBAR_LAYER_IMPL_H_ - -#include "cc/base/cc_export.h" -#include "cc/input/scrollbar.h" -#include "cc/layers/layer_impl.h" - -namespace cc { - -class LayerTreeImpl; -class ScrollView; - -class CC_EXPORT ScrollbarLayerImpl : public LayerImpl { - public: - static scoped_ptr<ScrollbarLayerImpl> Create( - LayerTreeImpl* tree_impl, - int id, - ScrollbarOrientation orientation); - virtual ~ScrollbarLayerImpl(); - - // LayerImpl implementation. - virtual ScrollbarLayerImpl* ToScrollbarLayer() OVERRIDE; - virtual scoped_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl) - OVERRIDE; - virtual void PushPropertiesTo(LayerImpl* layer) OVERRIDE; - - virtual bool WillDraw(DrawMode draw_mode, - ResourceProvider* resource_provider) OVERRIDE; - virtual void AppendQuads(QuadSink* quad_sink, - AppendQuadsData* append_quads_data) OVERRIDE; - - virtual void DidLoseOutputSurface() OVERRIDE; - - int scroll_layer_id() const { return scroll_layer_id_; } - void set_scroll_layer_id(int id) { scroll_layer_id_ = id; } - - ScrollbarOrientation Orientation() const; - float CurrentPos() const; - int Maximum() const; - - void SetThumbThickness(int thumb_thickness); - int thumb_thickness() const { return thumb_thickness_; } - void SetThumbLength(int thumb_length); - void SetTrackStart(int track_start); - void SetTrackLength(int track_length); - void SetVerticalAdjust(float vertical_adjust); - void set_track_resource_id(ResourceProvider::ResourceId id) { - track_resource_id_ = id; - } - void set_thumb_resource_id(ResourceProvider::ResourceId id) { - thumb_resource_id_ = id; - } - void SetVisibleToTotalLengthRatio(float ratio); - void set_is_overlay_scrollbar(bool is_overlay_scrollbar) { - is_overlay_scrollbar_ = is_overlay_scrollbar; - } - bool is_overlay_scrollbar() const { return is_overlay_scrollbar_; } - - void SetCurrentPos(float current_pos); - void SetMaximum(int maximum); - - gfx::Rect ComputeThumbQuadRect() const; - - protected: - ScrollbarLayerImpl(LayerTreeImpl* tree_impl, - int id, - ScrollbarOrientation orientation); - - private: - virtual const char* LayerTypeAsString() const OVERRIDE; - - gfx::Rect ScrollbarLayerRectToContentRect(gfx::RectF layer_rect) const; - - ResourceProvider::ResourceId track_resource_id_; - ResourceProvider::ResourceId thumb_resource_id_; - - float current_pos_; - int maximum_; - int thumb_thickness_; - int thumb_length_; - int track_start_; - int track_length_; - ScrollbarOrientation orientation_; - - // Difference between the clip layer's height and the visible viewport - // height (which may differ in the presence of top-controls hiding). - float vertical_adjust_; - - float visible_to_total_length_ratio_; - - int scroll_layer_id_; - - bool is_overlay_scrollbar_; - - DISALLOW_COPY_AND_ASSIGN(ScrollbarLayerImpl); -}; - -} // namespace cc -#endif // CC_LAYERS_SCROLLBAR_LAYER_IMPL_H_ diff --git a/chromium/cc/layers/scrollbar_layer_impl_base.cc b/chromium/cc/layers/scrollbar_layer_impl_base.cc new file mode 100644 index 00000000000..c1e7126ff52 --- /dev/null +++ b/chromium/cc/layers/scrollbar_layer_impl_base.cc @@ -0,0 +1,170 @@ +// 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/layers/scrollbar_layer_impl_base.h" + +#include <algorithm> +#include "cc/layers/layer.h" +#include "ui/gfx/rect_conversions.h" + +namespace cc { + +ScrollbarLayerImplBase::ScrollbarLayerImplBase( + LayerTreeImpl* tree_impl, + int id, + ScrollbarOrientation orientation, + bool is_left_side_vertical_scrollbar) + : LayerImpl(tree_impl, id), + scroll_layer_id_(Layer::INVALID_ID), + is_overlay_scrollbar_(false), + thumb_thickness_scale_factor_(1.f), + current_pos_(0.f), + maximum_(0), + orientation_(orientation), + is_left_side_vertical_scrollbar_(is_left_side_vertical_scrollbar), + vertical_adjust_(0.f), + visible_to_total_length_ratio_(1.f) {} + +void ScrollbarLayerImplBase::PushPropertiesTo(LayerImpl* layer) { + LayerImpl::PushPropertiesTo(layer); +} + +ScrollbarLayerImplBase* ScrollbarLayerImplBase::ToScrollbarLayer() { + return this; +} + +gfx::Rect ScrollbarLayerImplBase::ScrollbarLayerRectToContentRect( + gfx::RectF layer_rect) const { + // Don't intersect with the bounds as in LayerRectToContentRect() because + // layer_rect here might be in coordinates of the containing layer. + gfx::RectF content_rect = gfx::ScaleRect(layer_rect, + contents_scale_x(), + contents_scale_y()); + return gfx::ToEnclosingRect(content_rect); +} + +void ScrollbarLayerImplBase::SetCurrentPos(float current_pos) { + if (current_pos_ == current_pos) + return; + current_pos_ = current_pos; + NoteLayerPropertyChanged(); +} + +void ScrollbarLayerImplBase::SetMaximum(int maximum) { + if (maximum_ == maximum) + return; + maximum_ = maximum; + NoteLayerPropertyChanged(); +} + +void ScrollbarLayerImplBase::SetVerticalAdjust(float vertical_adjust) { + if (vertical_adjust_ == vertical_adjust) + return; + vertical_adjust_ = vertical_adjust; + NoteLayerPropertyChanged(); +} + +void ScrollbarLayerImplBase::SetVisibleToTotalLengthRatio(float ratio) { + if (visible_to_total_length_ratio_ == ratio) + return; + visible_to_total_length_ratio_ = ratio; + NoteLayerPropertyChanged(); +} + +gfx::Rect ScrollbarLayerImplBase::ComputeThumbQuadRect() const { + // Thumb extent is the length of the thumb in the scrolling direction, thumb + // thickness is in the perpendicular direction. Here's an example of a + // horizontal scrollbar - inputs are above the scrollbar, computed values + // below: + // + // |<------------------- track_length_ ------------------->| + // + // |--| <-- start_offset + // + // +--+----------------------------+------------------+-------+--+ + // |<|| |##################| ||>| + // +--+----------------------------+------------------+-------+--+ + // + // |<- thumb_length ->| + // + // |<------- thumb_offset -------->| + // + // For painted, scrollbars, the length is fixed. For solid color scrollbars we + // have to compute it. The ratio of the thumb's length to the track's length + // is the same as that of the visible viewport to the total viewport, unless + // that would make the thumb's length less than its thickness. + // + // vertical_adjust_ is used when the layer geometry from the main thread is + // not in sync with what the user sees. For instance on Android scrolling the + // top bar controls out of view reveals more of the page content. We want the + // root layer scrollbars to reflect what the user sees even if we haven't + // received new layer geometry from the main thread. If the user has scrolled + // down by 50px and the initial viewport size was 950px the geometry would + // look something like this: + // + // vertical_adjust_ = 50, scroll position 0, visible ratios 99% + // Layer geometry: Desired thumb positions: + // +--------------------+-+ +----------------------+ <-- 0px + // | |v| | #| + // | |e| | #| + // | |r| | #| + // | |t| | #| + // | |i| | #| + // | |c| | #| + // | |a| | #| + // | |l| | #| + // | | | | #| + // | |l| | #| + // | |a| | #| + // | |y| | #| + // | |e| | #| + // | |r| | #| + // +--------------------+-+ | #| + // | horizontal layer | | | #| + // +--------------------+-+ | #| <-- 950px + // | | | #| + // | | |##################### | + // +----------------------+ +----------------------+ <-- 1000px + // + // The layer geometry is set up for a 950px tall viewport, but the user can + // actually see down to 1000px. Thus we have to move the quad for the + // horizontal scrollbar down by the vertical_adjust_ factor and lay the + // vertical thumb out on a track lengthed by the vertical_adjust_ factor. This + // means the quads may extend outside the layer's bounds. + + // With the length known, we can compute the thumb's position. + float track_length = TrackLength(); + int thumb_length = ThumbLength(); + int thumb_thickness = ThumbThickness(); + + // With the length known, we can compute the thumb's position. + float clamped_current_pos = + std::min(std::max(current_pos_, 0.f), static_cast<float>(maximum_)); + float ratio = clamped_current_pos / maximum_; + float max_offset = track_length - thumb_length; + int thumb_offset = static_cast<int>(ratio * max_offset) + TrackStart(); + + float thumb_thickness_adjustment = + thumb_thickness * (1.f - thumb_thickness_scale_factor_); + + gfx::RectF thumb_rect; + if (orientation_ == HORIZONTAL) { + thumb_rect = gfx::RectF(thumb_offset, + vertical_adjust_ + thumb_thickness_adjustment, + thumb_length, + thumb_thickness - thumb_thickness_adjustment); + } else { + thumb_rect = gfx::RectF( + is_left_side_vertical_scrollbar_ + ? bounds().width() - thumb_thickness + : thumb_thickness_adjustment, + thumb_offset, + thumb_thickness - thumb_thickness_adjustment, + thumb_length); + } + + return ScrollbarLayerRectToContentRect(thumb_rect); +} + +} // namespace cc diff --git a/chromium/cc/layers/scrollbar_layer_impl_base.h b/chromium/cc/layers/scrollbar_layer_impl_base.h new file mode 100644 index 00000000000..55ade6ab04f --- /dev/null +++ b/chromium/cc/layers/scrollbar_layer_impl_base.h @@ -0,0 +1,91 @@ +// 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. + +#ifndef CC_LAYERS_SCROLLBAR_LAYER_IMPL_BASE_H_ +#define CC_LAYERS_SCROLLBAR_LAYER_IMPL_BASE_H_ + +#include "cc/base/cc_export.h" +#include "cc/input/scrollbar.h" +#include "cc/layers/layer_impl.h" + +namespace cc { + +class LayerTreeImpl; + +class CC_EXPORT ScrollbarLayerImplBase : public LayerImpl { + public: + int ScrollLayerId() const { return scroll_layer_id_; } + void set_scroll_layer_id(int id) { scroll_layer_id_ = id; } + + float current_pos() const { return current_pos_; } + void SetCurrentPos(float current_pos); + int maximum() const { return maximum_; } + void SetMaximum(int maximum); + + void SetVerticalAdjust(float vertical_adjust); + + bool is_overlay_scrollbar() const { return is_overlay_scrollbar_; } + void set_is_overlay_scrollbar(bool is_overlay) { + is_overlay_scrollbar_ = is_overlay; + } + + ScrollbarOrientation orientation() const { return orientation_; } + bool is_left_side_vertical_scrollbar() { + return is_left_side_vertical_scrollbar_; + } + + virtual void PushPropertiesTo(LayerImpl* layer) OVERRIDE; + virtual ScrollbarLayerImplBase* ToScrollbarLayer() OVERRIDE; + + void SetVisibleToTotalLengthRatio(float ratio); + virtual gfx::Rect ComputeThumbQuadRect() const; + + float thumb_thickness_scale_factor() { + return thumb_thickness_scale_factor_; + } + void set_thumb_thickness_scale_factor(float thumb_thickness_scale_factor) { + thumb_thickness_scale_factor_ = thumb_thickness_scale_factor; + } + + protected: + ScrollbarLayerImplBase(LayerTreeImpl* tree_impl, + int id, + ScrollbarOrientation orientation, + bool is_left_side_vertical_scrollbar); + virtual ~ScrollbarLayerImplBase() {} + + gfx::Rect ScrollbarLayerRectToContentRect(gfx::RectF layer_rect) const; + + float visible_to_total_length_ratio() const { + return visible_to_total_length_ratio_; + } + float vertical_adjust() const { return vertical_adjust_; } + + virtual int ThumbThickness() const = 0; + virtual int ThumbLength() const = 0; + virtual float TrackLength() const = 0; + virtual int TrackStart() const = 0; + + private: + int scroll_layer_id_; + bool is_overlay_scrollbar_; + + float thumb_thickness_scale_factor_; + float current_pos_; + int maximum_; + ScrollbarOrientation orientation_; + bool is_left_side_vertical_scrollbar_; + + // Difference between the clip layer's height and the visible viewport + // height (which may differ in the presence of top-controls hiding). + float vertical_adjust_; + + float visible_to_total_length_ratio_; + + DISALLOW_COPY_AND_ASSIGN(ScrollbarLayerImplBase); +}; + +} // namespace cc + +#endif // CC_LAYERS_SCROLLBAR_LAYER_IMPL_BASE_H_ diff --git a/chromium/cc/layers/scrollbar_layer_interface.h b/chromium/cc/layers/scrollbar_layer_interface.h new file mode 100644 index 00000000000..bbb0da6ae2b --- /dev/null +++ b/chromium/cc/layers/scrollbar_layer_interface.h @@ -0,0 +1,30 @@ +// 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. + +#ifndef CC_LAYERS_SCROLLBAR_LAYER_INTERFACE_H_ +#define CC_LAYERS_SCROLLBAR_LAYER_INTERFACE_H_ + +#include "cc/base/cc_export.h" +#include "cc/input/scrollbar.h" + +namespace cc { + +class CC_EXPORT ScrollbarLayerInterface { + public: + virtual int ScrollLayerId() const = 0; + virtual void SetScrollLayerId(int id) = 0; + + virtual ScrollbarOrientation orientation() const = 0; + + protected: + ScrollbarLayerInterface() {} + virtual ~ScrollbarLayerInterface() {} + + private: + DISALLOW_COPY_AND_ASSIGN(ScrollbarLayerInterface); +}; + +} // namespace cc + +#endif // CC_LAYERS_SCROLLBAR_LAYER_INTERFACE_H_ diff --git a/chromium/cc/layers/scrollbar_layer_unittest.cc b/chromium/cc/layers/scrollbar_layer_unittest.cc index b7981041f09..2e1b4b6f483 100644 --- a/chromium/cc/layers/scrollbar_layer_unittest.cc +++ b/chromium/cc/layers/scrollbar_layer_unittest.cc @@ -2,24 +2,27 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "cc/layers/scrollbar_layer.h" - +#include "base/containers/hash_tables.h" #include "cc/animation/scrollbar_animation_controller.h" +#include "cc/debug/test_web_graphics_context_3d.h" #include "cc/layers/append_quads_data.h" -#include "cc/layers/scrollbar_layer_impl.h" +#include "cc/layers/painted_scrollbar_layer.h" +#include "cc/layers/painted_scrollbar_layer_impl.h" +#include "cc/layers/scrollbar_layer_interface.h" +#include "cc/layers/solid_color_scrollbar_layer.h" +#include "cc/layers/solid_color_scrollbar_layer_impl.h" #include "cc/quads/solid_color_draw_quad.h" -#include "cc/resources/prioritized_resource_manager.h" -#include "cc/resources/priority_calculator.h" #include "cc/resources/resource_update_queue.h" #include "cc/test/fake_impl_proxy.h" #include "cc/test/fake_layer_tree_host.h" #include "cc/test/fake_layer_tree_host_client.h" #include "cc/test/fake_layer_tree_host_impl.h" +#include "cc/test/fake_painted_scrollbar_layer.h" #include "cc/test/fake_scrollbar.h" #include "cc/test/geometry_test_utils.h" #include "cc/test/layer_tree_test.h" #include "cc/test/mock_quad_culler.h" -#include "cc/test/test_web_graphics_context_3d.h" +#include "cc/trees/layer_tree_host.h" #include "cc/trees/layer_tree_impl.h" #include "cc/trees/single_thread_proxy.h" #include "cc/trees/tree_synchronizer.h" @@ -32,12 +35,20 @@ namespace { LayerImpl* LayerImplForScrollAreaAndScrollbar( FakeLayerTreeHost* host, scoped_ptr<Scrollbar> scrollbar, - bool reverse_order) { + bool reverse_order, + bool use_solid_color_scrollbar, + int thumb_thickness) { scoped_refptr<Layer> layer_tree_root = Layer::Create(); scoped_refptr<Layer> child1 = Layer::Create(); - scoped_refptr<Layer> child2 = - ScrollbarLayer::Create(scrollbar.Pass(), - child1->id()); + scoped_refptr<Layer> child2; + if (use_solid_color_scrollbar) { + const bool kIsLeftSideVerticalScrollbar = false; + child2 = SolidColorScrollbarLayer::Create( + scrollbar->Orientation(), thumb_thickness, + kIsLeftSideVerticalScrollbar, child1->id()); + } else { + child2 = PaintedScrollbarLayer::Create(scrollbar.Pass(), child1->id()); + } layer_tree_root->AddChild(child1); layer_tree_root->InsertChild(child2, reverse_order ? 0 : 1); host->SetRootLayer(layer_tree_root); @@ -47,12 +58,13 @@ LayerImpl* LayerImplForScrollAreaAndScrollbar( TEST(ScrollbarLayerTest, ResolveScrollLayerPointer) { scoped_ptr<FakeLayerTreeHost> host = FakeLayerTreeHost::Create(); scoped_ptr<Scrollbar> scrollbar(new FakeScrollbar); - LayerImpl* layer_impl_tree_root = - LayerImplForScrollAreaAndScrollbar(host.get(), scrollbar.Pass(), false); + LayerImpl* layer_impl_tree_root = LayerImplForScrollAreaAndScrollbar( + host.get(), scrollbar.Pass(), false, false, 0); LayerImpl* cc_child1 = layer_impl_tree_root->children()[0]; - ScrollbarLayerImpl* cc_child2 = static_cast<ScrollbarLayerImpl*>( - layer_impl_tree_root->children()[1]); + PaintedScrollbarLayerImpl* cc_child2 = + static_cast<PaintedScrollbarLayerImpl*>( + layer_impl_tree_root->children()[1]); EXPECT_EQ(cc_child1->horizontal_scrollbar_layer(), cc_child2); } @@ -60,11 +72,12 @@ TEST(ScrollbarLayerTest, ResolveScrollLayerPointer) { TEST(ScrollbarLayerTest, ResolveScrollLayerPointer_ReverseOrder) { scoped_ptr<FakeLayerTreeHost> host = FakeLayerTreeHost::Create(); scoped_ptr<Scrollbar> scrollbar(new FakeScrollbar); - LayerImpl* layer_impl_tree_root = - LayerImplForScrollAreaAndScrollbar(host.get(), scrollbar.Pass(), true); + LayerImpl* layer_impl_tree_root = LayerImplForScrollAreaAndScrollbar( + host.get(), scrollbar.Pass(), true, false, 0); - ScrollbarLayerImpl* cc_child1 = static_cast<ScrollbarLayerImpl*>( - layer_impl_tree_root->children()[0]); + PaintedScrollbarLayerImpl* cc_child1 = + static_cast<PaintedScrollbarLayerImpl*>( + layer_impl_tree_root->children()[0]); LayerImpl* cc_child2 = layer_impl_tree_root->children()[1]; EXPECT_EQ(cc_child2->horizontal_scrollbar_layer(), cc_child1); @@ -75,10 +88,11 @@ TEST(ScrollbarLayerTest, ShouldScrollNonOverlayOnMainThread) { // Create and attach a non-overlay scrollbar. scoped_ptr<Scrollbar> scrollbar(new FakeScrollbar); - LayerImpl* layer_impl_tree_root = - LayerImplForScrollAreaAndScrollbar(host.get(), scrollbar.Pass(), false); - ScrollbarLayerImpl* scrollbar_layer_impl = - static_cast<ScrollbarLayerImpl*>(layer_impl_tree_root->children()[1]); + LayerImpl* layer_impl_tree_root = LayerImplForScrollAreaAndScrollbar( + host.get(), scrollbar.Pass(), false, false, 0); + PaintedScrollbarLayerImpl* scrollbar_layer_impl = + static_cast<PaintedScrollbarLayerImpl*>( + layer_impl_tree_root->children()[1]); // When the scrollbar is not an overlay scrollbar, the scroll should be // responded to on the main thread as the compositor does not yet implement @@ -90,10 +104,10 @@ TEST(ScrollbarLayerTest, ShouldScrollNonOverlayOnMainThread) { // Create and attach an overlay scrollbar. scrollbar.reset(new FakeScrollbar(false, false, true)); - layer_impl_tree_root = - LayerImplForScrollAreaAndScrollbar(host.get(), scrollbar.Pass(), false); - scrollbar_layer_impl = - static_cast<ScrollbarLayerImpl*>(layer_impl_tree_root->children()[1]); + layer_impl_tree_root = LayerImplForScrollAreaAndScrollbar( + host.get(), scrollbar.Pass(), false, false, 0); + scrollbar_layer_impl = static_cast<PaintedScrollbarLayerImpl*>( + layer_impl_tree_root->children()[1]); // The user shouldn't be able to drag an overlay scrollbar and the scroll // may be handled in the compositor. @@ -102,15 +116,14 @@ TEST(ScrollbarLayerTest, ShouldScrollNonOverlayOnMainThread) { InputHandler::Gesture)); } -TEST(ScrollbarLayerTest, ScrollOffsetSynchronization) { +TEST(PaintedScrollbarLayerTest, ScrollOffsetSynchronization) { scoped_ptr<FakeLayerTreeHost> host = FakeLayerTreeHost::Create(); scoped_ptr<Scrollbar> scrollbar(new FakeScrollbar); scoped_refptr<Layer> layer_tree_root = Layer::Create(); scoped_refptr<Layer> content_layer = Layer::Create(); scoped_refptr<Layer> scrollbar_layer = - ScrollbarLayer::Create(scrollbar.Pass(), - layer_tree_root->id()); + PaintedScrollbarLayer::Create(scrollbar.Pass(), layer_tree_root->id()); layer_tree_root->SetScrollable(true); layer_tree_root->SetScrollOffset(gfx::Vector2d(10, 20)); @@ -127,11 +140,12 @@ TEST(ScrollbarLayerTest, ScrollOffsetSynchronization) { LayerImpl* layer_impl_tree_root = host->CommitAndCreateLayerImplTree(); - ScrollbarLayerImpl* cc_scrollbar_layer = - static_cast<ScrollbarLayerImpl*>(layer_impl_tree_root->children()[1]); + ScrollbarLayerImplBase* cc_scrollbar_layer = + static_cast<PaintedScrollbarLayerImpl*>( + layer_impl_tree_root->children()[1]); - EXPECT_EQ(10.f, cc_scrollbar_layer->CurrentPos()); - EXPECT_EQ(30, cc_scrollbar_layer->Maximum()); + EXPECT_EQ(10.f, cc_scrollbar_layer->current_pos()); + EXPECT_EQ(30, cc_scrollbar_layer->maximum()); layer_tree_root->SetScrollOffset(gfx::Vector2d(100, 200)); layer_tree_root->SetMaxScrollOffset(gfx::Vector2d(300, 500)); @@ -146,31 +160,124 @@ TEST(ScrollbarLayerTest, ScrollOffsetSynchronization) { EXPECT_EQ(scrollbar_controller, layer_impl_tree_root->scrollbar_animation_controller()); - EXPECT_EQ(100.f, cc_scrollbar_layer->CurrentPos()); - EXPECT_EQ(300, cc_scrollbar_layer->Maximum()); + EXPECT_EQ(100.f, cc_scrollbar_layer->current_pos()); + EXPECT_EQ(300, cc_scrollbar_layer->maximum()); layer_impl_tree_root->ScrollBy(gfx::Vector2d(12, 34)); - EXPECT_EQ(112.f, cc_scrollbar_layer->CurrentPos()); - EXPECT_EQ(300, cc_scrollbar_layer->Maximum()); + EXPECT_EQ(112.f, cc_scrollbar_layer->current_pos()); + EXPECT_EQ(300, cc_scrollbar_layer->maximum()); +} + +TEST(ScrollbarLayerTest, ThumbRect) { + scoped_ptr<FakeLayerTreeHost> host = FakeLayerTreeHost::Create(); + scoped_refptr<Layer> root_layer = Layer::Create(); + scoped_refptr<Layer> content_layer = Layer::Create(); + scoped_refptr<FakePaintedScrollbarLayer> scrollbar_layer = + FakePaintedScrollbarLayer::Create(false, true, root_layer->id()); + + root_layer->SetScrollable(true); + root_layer->SetMaxScrollOffset(gfx::Vector2d(80, 0)); + root_layer->SetBounds(gfx::Size(100, 50)); + content_layer->SetBounds(gfx::Size(100, 50)); + + host->SetRootLayer(root_layer); + root_layer->AddChild(content_layer); + root_layer->AddChild(scrollbar_layer); + + root_layer->SetScrollOffset(gfx::Vector2d(0, 0)); + scrollbar_layer->SetBounds(gfx::Size(70, 10)); + scrollbar_layer->fake_scrollbar()->set_location(gfx::Point(20, 10)); + scrollbar_layer->fake_scrollbar()->set_track_rect(gfx::Rect(30, 10, 50, 10)); + scrollbar_layer->fake_scrollbar()->set_thumb_thickness(10); + scrollbar_layer->fake_scrollbar()->set_thumb_length(4); + scrollbar_layer->UpdateThumbAndTrackGeometry(); + LayerImpl* root_layer_impl = NULL; + PaintedScrollbarLayerImpl* scrollbar_layer_impl = NULL; + + // Thumb is at the edge of the scrollbar (should be inset to + // the start of the track within the scrollbar layer's + // position). + scrollbar_layer->UpdateThumbAndTrackGeometry(); + root_layer_impl = host->CommitAndCreateLayerImplTree(); + scrollbar_layer_impl = static_cast<PaintedScrollbarLayerImpl*>( + root_layer_impl->children()[1]); + EXPECT_EQ(gfx::Rect(10, 0, 4, 10).ToString(), + scrollbar_layer_impl->ComputeThumbQuadRect().ToString()); + + // Under-scroll (thumb position should clamp and be unchanged). + root_layer->SetScrollOffset(gfx::Vector2d(-5, 0)); + + scrollbar_layer->UpdateThumbAndTrackGeometry(); + root_layer_impl = host->CommitAndCreateLayerImplTree(); + scrollbar_layer_impl = static_cast<PaintedScrollbarLayerImpl*>( + root_layer_impl->children()[1]); + EXPECT_EQ(gfx::Rect(10, 0, 4, 10).ToString(), + scrollbar_layer_impl->ComputeThumbQuadRect().ToString()); + + // Over-scroll (thumb position should clamp on the far side). + root_layer->SetScrollOffset(gfx::Vector2d(85, 0)); + + scrollbar_layer->UpdateThumbAndTrackGeometry(); + root_layer_impl = host->CommitAndCreateLayerImplTree(); + scrollbar_layer_impl = static_cast<PaintedScrollbarLayerImpl*>( + root_layer_impl->children()[1]); + EXPECT_EQ(gfx::Rect(56, 0, 4, 10).ToString(), + scrollbar_layer_impl->ComputeThumbQuadRect().ToString()); + + // Change thumb thickness and length. + scrollbar_layer->fake_scrollbar()->set_thumb_thickness(4); + scrollbar_layer->fake_scrollbar()->set_thumb_length(6); + + scrollbar_layer->UpdateThumbAndTrackGeometry(); + root_layer_impl = host->CommitAndCreateLayerImplTree(); + scrollbar_layer_impl = static_cast<PaintedScrollbarLayerImpl*>( + root_layer_impl->children()[1]); + EXPECT_EQ(gfx::Rect(54, 0, 6, 4).ToString(), + scrollbar_layer_impl->ComputeThumbQuadRect().ToString()); + + // Shrink the scrollbar layer to cover only the track. + scrollbar_layer->SetBounds(gfx::Size(50, 10)); + scrollbar_layer->fake_scrollbar()->set_location(gfx::Point(30, 10)); + scrollbar_layer->fake_scrollbar()->set_track_rect(gfx::Rect(30, 10, 50, 10)); + + scrollbar_layer->UpdateThumbAndTrackGeometry(); + root_layer_impl = host->CommitAndCreateLayerImplTree(); + scrollbar_layer_impl = static_cast<PaintedScrollbarLayerImpl*>( + root_layer_impl->children()[1]); + EXPECT_EQ(gfx::Rect(44, 0, 6, 4).ToString(), + scrollbar_layer_impl->ComputeThumbQuadRect().ToString()); + + // Shrink the track in the non-scrolling dimension so that it only covers the + // middle third of the scrollbar layer (this does not affect the thumb + // position). + scrollbar_layer->fake_scrollbar()->set_track_rect(gfx::Rect(30, 12, 50, 6)); + + scrollbar_layer->UpdateThumbAndTrackGeometry(); + root_layer_impl = host->CommitAndCreateLayerImplTree(); + scrollbar_layer_impl = static_cast<PaintedScrollbarLayerImpl*>( + root_layer_impl->children()[1]); + EXPECT_EQ(gfx::Rect(44, 0, 6, 4).ToString(), + scrollbar_layer_impl->ComputeThumbQuadRect().ToString()); } TEST(ScrollbarLayerTest, SolidColorDrawQuads) { + const int kThumbThickness = 3; + const int kTrackLength = 100; + LayerTreeSettings layer_tree_settings; - layer_tree_settings.solid_color_scrollbars = true; - layer_tree_settings.solid_color_scrollbar_thickness_dip = 3; scoped_ptr<FakeLayerTreeHost> host = FakeLayerTreeHost::Create(layer_tree_settings); scoped_ptr<Scrollbar> scrollbar(new FakeScrollbar(false, true, true)); - LayerImpl* layer_impl_tree_root = - LayerImplForScrollAreaAndScrollbar(host.get(), scrollbar.Pass(), false); - ScrollbarLayerImpl* scrollbar_layer_impl = - static_cast<ScrollbarLayerImpl*>(layer_impl_tree_root->children()[1]); - scrollbar_layer_impl->SetThumbThickness(3); + LayerImpl* layer_impl_tree_root = LayerImplForScrollAreaAndScrollbar( + host.get(), scrollbar.Pass(), false, true, kThumbThickness); + ScrollbarLayerImplBase* scrollbar_layer_impl = + static_cast<SolidColorScrollbarLayerImpl*>( + layer_impl_tree_root->children()[1]); + scrollbar_layer_impl->SetBounds(gfx::Size(kTrackLength, kThumbThickness)); scrollbar_layer_impl->SetCurrentPos(10.f); scrollbar_layer_impl->SetMaximum(100); - scrollbar_layer_impl->SetTrackLength(100); scrollbar_layer_impl->SetVisibleToTotalLengthRatio(0.4f); // Thickness should be overridden to 3. @@ -217,20 +324,21 @@ TEST(ScrollbarLayerTest, SolidColorDrawQuads) { } TEST(ScrollbarLayerTest, LayerDrivenSolidColorDrawQuads) { + const int kThumbThickness = 3; + const int kTrackLength = 10; + LayerTreeSettings layer_tree_settings; - layer_tree_settings.solid_color_scrollbars = true; - layer_tree_settings.solid_color_scrollbar_thickness_dip = 3; scoped_ptr<FakeLayerTreeHost> host = FakeLayerTreeHost::Create(layer_tree_settings); scoped_ptr<Scrollbar> scrollbar(new FakeScrollbar(false, true, true)); - LayerImpl* layer_impl_tree_root = - LayerImplForScrollAreaAndScrollbar(host.get(), scrollbar.Pass(), false); - ScrollbarLayerImpl* scrollbar_layer_impl = - static_cast<ScrollbarLayerImpl*>(layer_impl_tree_root->children()[1]); + LayerImpl* layer_impl_tree_root = LayerImplForScrollAreaAndScrollbar( + host.get(), scrollbar.Pass(), false, true, kThumbThickness); + ScrollbarLayerImplBase* scrollbar_layer_impl = + static_cast<PaintedScrollbarLayerImpl*>( + layer_impl_tree_root->children()[1]); - scrollbar_layer_impl->SetThumbThickness(3); - scrollbar_layer_impl->SetTrackLength(10); + scrollbar_layer_impl->SetBounds(gfx::Size(kTrackLength, kThumbThickness)); scrollbar_layer_impl->SetCurrentPos(4.f); scrollbar_layer_impl->SetMaximum(8); @@ -259,40 +367,43 @@ class ScrollbarLayerSolidColorThumbTest : public testing::Test { layer_tree_settings.solid_color_scrollbars = true; host_impl_.reset(new FakeLayerTreeHostImpl(layer_tree_settings, &proxy_)); - horizontal_scrollbar_layer_ = ScrollbarLayerImpl::Create( - host_impl_->active_tree(), 1, HORIZONTAL); - vertical_scrollbar_layer_ = ScrollbarLayerImpl::Create( - host_impl_->active_tree(), 2, VERTICAL); + const int kThumbThickness = 3; + const bool kIsLeftSideVerticalScrollbar = false; + + horizontal_scrollbar_layer_ = SolidColorScrollbarLayerImpl::Create( + host_impl_->active_tree(), 1, HORIZONTAL, kThumbThickness, + kIsLeftSideVerticalScrollbar); + vertical_scrollbar_layer_ = SolidColorScrollbarLayerImpl::Create( + host_impl_->active_tree(), 2, VERTICAL, kThumbThickness, + kIsLeftSideVerticalScrollbar); } protected: FakeImplProxy proxy_; scoped_ptr<FakeLayerTreeHostImpl> host_impl_; - scoped_ptr<ScrollbarLayerImpl> horizontal_scrollbar_layer_; - scoped_ptr<ScrollbarLayerImpl> vertical_scrollbar_layer_; + scoped_ptr<SolidColorScrollbarLayerImpl> horizontal_scrollbar_layer_; + scoped_ptr<SolidColorScrollbarLayerImpl> vertical_scrollbar_layer_; }; TEST_F(ScrollbarLayerSolidColorThumbTest, SolidColorThumbLength) { horizontal_scrollbar_layer_->SetCurrentPos(0); horizontal_scrollbar_layer_->SetMaximum(10); - horizontal_scrollbar_layer_->SetThumbThickness(3); // Simple case - one third of the scrollable area is visible, so the thumb // should be one third as long as the track. horizontal_scrollbar_layer_->SetVisibleToTotalLengthRatio(0.33f); - horizontal_scrollbar_layer_->SetTrackLength(100); + horizontal_scrollbar_layer_->SetBounds(gfx::Size(100, 3)); EXPECT_EQ(33, horizontal_scrollbar_layer_->ComputeThumbQuadRect().width()); // The thumb's length should never be less than its thickness. horizontal_scrollbar_layer_->SetVisibleToTotalLengthRatio(0.01f); - horizontal_scrollbar_layer_->SetTrackLength(100); + horizontal_scrollbar_layer_->SetBounds(gfx::Size(100, 3)); EXPECT_EQ(3, horizontal_scrollbar_layer_->ComputeThumbQuadRect().width()); } TEST_F(ScrollbarLayerSolidColorThumbTest, SolidColorThumbPosition) { - horizontal_scrollbar_layer_->SetTrackLength(100); + horizontal_scrollbar_layer_->SetBounds(gfx::Size(100, 3)); horizontal_scrollbar_layer_->SetVisibleToTotalLengthRatio(0.1f); - horizontal_scrollbar_layer_->SetThumbThickness(3); horizontal_scrollbar_layer_->SetCurrentPos(0); horizontal_scrollbar_layer_->SetMaximum(100); @@ -311,15 +422,15 @@ TEST_F(ScrollbarLayerSolidColorThumbTest, SolidColorThumbPosition) { } TEST_F(ScrollbarLayerSolidColorThumbTest, SolidColorThumbVerticalAdjust) { - ScrollbarLayerImpl* layers[2] = + SolidColorScrollbarLayerImpl* layers[2] = { horizontal_scrollbar_layer_.get(), vertical_scrollbar_layer_.get() }; for (size_t i = 0; i < 2; ++i) { - layers[i]->SetTrackLength(100); layers[i]->SetVisibleToTotalLengthRatio(0.2f); - layers[i]->SetThumbThickness(3); layers[i]->SetCurrentPos(25); layers[i]->SetMaximum(100); } + layers[0]->SetBounds(gfx::Size(100, 3)); + layers[1]->SetBounds(gfx::Size(3, 100)); EXPECT_RECT_EQ(gfx::RectF(20.f, 0.f, 20.f, 3.f), horizontal_scrollbar_layer_->ComputeThumbQuadRect()); @@ -347,7 +458,7 @@ class ScrollbarLayerTestMaxTextureSize : public LayerTreeTest { virtual void BeginTest() OVERRIDE { scoped_ptr<Scrollbar> scrollbar(new FakeScrollbar); - scrollbar_layer_ = ScrollbarLayer::Create(scrollbar.Pass(), 1); + scrollbar_layer_ = PaintedScrollbarLayer::Create(scrollbar.Pass(), 1); scrollbar_layer_->SetLayerTreeHost(layer_tree_host()); scrollbar_layer_->SetBounds(bounds_); layer_tree_host()->root_layer()->AddChild(scrollbar_layer_); @@ -377,7 +488,7 @@ class ScrollbarLayerTestMaxTextureSize : public LayerTreeTest { virtual void AfterTest() OVERRIDE {} private: - scoped_refptr<ScrollbarLayer> scrollbar_layer_; + scoped_refptr<PaintedScrollbarLayer> scrollbar_layer_; scoped_refptr<Layer> scroll_layer_; gfx::Size bounds_; }; @@ -404,9 +515,49 @@ class MockLayerTreeHost : public LayerTreeHost { public: MockLayerTreeHost(LayerTreeHostClient* client, const LayerTreeSettings& settings) - : LayerTreeHost(client, settings) { + : LayerTreeHost(client, settings), + next_id_(1), + total_ui_resource_created_(0), + total_ui_resource_deleted_(0) { Initialize(NULL); } + + virtual UIResourceId CreateUIResource(UIResourceClient* content) OVERRIDE { + total_ui_resource_created_++; + UIResourceId nid = next_id_++; + ui_resource_bitmap_map_.insert( + std::make_pair(nid, content->GetBitmap(nid, false))); + return nid; + } + + // Deletes a UI resource. May safely be called more than once. + virtual void DeleteUIResource(UIResourceId id) OVERRIDE { + UIResourceBitmapMap::iterator iter = ui_resource_bitmap_map_.find(id); + if (iter != ui_resource_bitmap_map_.end()) { + ui_resource_bitmap_map_.erase(iter); + total_ui_resource_deleted_++; + } + } + + size_t UIResourceCount() { return ui_resource_bitmap_map_.size(); } + int TotalUIResourceDeleted() { return total_ui_resource_deleted_; } + int TotalUIResourceCreated() { return total_ui_resource_created_; } + + gfx::Size ui_resource_size(UIResourceId id) { + UIResourceBitmapMap::iterator iter = ui_resource_bitmap_map_.find(id); + if (iter != ui_resource_bitmap_map_.end()) + return iter->second.GetSize(); + return gfx::Size(); + } + + private: + typedef base::hash_map<UIResourceId, UIResourceBitmap> + UIResourceBitmapMap; + UIResourceBitmapMap ui_resource_bitmap_map_; + + int next_id_; + int total_ui_resource_created_; + int total_ui_resource_deleted_; }; @@ -415,21 +566,34 @@ class ScrollbarLayerTestResourceCreation : public testing::Test { ScrollbarLayerTestResourceCreation() : fake_client_(FakeLayerTreeHostClient::DIRECT_3D) {} - void TestResourceUpload(size_t expected_resources) { + void TestResourceUpload(int num_updates, + size_t expected_resources, + int expected_created, + int expected_deleted, + bool use_solid_color_scrollbar) { layer_tree_host_.reset( new MockLayerTreeHost(&fake_client_, layer_tree_settings_)); scoped_ptr<Scrollbar> scrollbar(new FakeScrollbar(false, true, false)); scoped_refptr<Layer> layer_tree_root = Layer::Create(); scoped_refptr<Layer> content_layer = Layer::Create(); - scoped_refptr<Layer> scrollbar_layer = - ScrollbarLayer::Create(scrollbar.Pass(), layer_tree_root->id()); + scoped_refptr<Layer> scrollbar_layer; + if (use_solid_color_scrollbar) { + const int kThumbThickness = 3; + const bool kIsLeftSideVerticalScrollbar = false; + scrollbar_layer = + SolidColorScrollbarLayer::Create(scrollbar->Orientation(), + kThumbThickness, + kIsLeftSideVerticalScrollbar, + layer_tree_root->id()); + } else { + scrollbar_layer = PaintedScrollbarLayer::Create(scrollbar.Pass(), + layer_tree_root->id()); + } layer_tree_root->AddChild(content_layer); layer_tree_root->AddChild(scrollbar_layer); layer_tree_host_->InitializeOutputSurfaceIfNeeded(); - layer_tree_host_->contents_texture_manager()-> - SetMaxMemoryLimitBytes(1024 * 1024); layer_tree_host_->SetRootLayer(layer_tree_root); scrollbar_layer->SetIsDrawable(true); @@ -447,16 +611,17 @@ class ScrollbarLayerTestResourceCreation : public testing::Test { testing::Mock::VerifyAndClearExpectations(layer_tree_host_.get()); EXPECT_EQ(scrollbar_layer->layer_tree_host(), layer_tree_host_.get()); - PriorityCalculator calculator; ResourceUpdateQueue queue; OcclusionTracker occlusion_tracker(gfx::Rect(), false); scrollbar_layer->SavePaintProperties(); - scrollbar_layer->SetTexturePriorities(calculator); - layer_tree_host_->contents_texture_manager()->PrioritizeTextures(); - scrollbar_layer->Update(&queue, &occlusion_tracker); - EXPECT_EQ(0u, queue.FullUploadSize()); - EXPECT_EQ(expected_resources, queue.PartialUploadSize()); + for (int update_counter = 0; update_counter < num_updates; update_counter++) + scrollbar_layer->Update(&queue, &occlusion_tracker); + + // A non-solid-color scrollbar should have requested two textures. + EXPECT_EQ(expected_resources, layer_tree_host_->UIResourceCount()); + EXPECT_EQ(expected_created, layer_tree_host_->TotalUIResourceCreated()); + EXPECT_EQ(expected_deleted, layer_tree_host_->TotalUIResourceDeleted()); testing::Mock::VerifyAndClearExpectations(layer_tree_host_.get()); @@ -470,13 +635,22 @@ class ScrollbarLayerTestResourceCreation : public testing::Test { }; TEST_F(ScrollbarLayerTestResourceCreation, ResourceUpload) { - layer_tree_settings_.solid_color_scrollbars = false; - TestResourceUpload(2); + bool use_solid_color_scrollbars = false; + TestResourceUpload(0, 0, 0, 0, use_solid_color_scrollbars); + int num_updates[3] = {1, 5, 10}; + for (int j = 0; j < 3; j++) { + TestResourceUpload(num_updates[j], + 2, + num_updates[j] * 2, + (num_updates[j] - 1) * 2, + use_solid_color_scrollbars); + } } TEST_F(ScrollbarLayerTestResourceCreation, SolidColorNoResourceUpload) { - layer_tree_settings_.solid_color_scrollbars = true; - TestResourceUpload(0); + bool use_solid_color_scrollbars = true; + TestResourceUpload(0, 0, 0, 0, use_solid_color_scrollbars); + TestResourceUpload(1, 0, 0, 0, use_solid_color_scrollbars); } class ScaledScrollbarLayerTestResourceCreation : public testing::Test { @@ -484,25 +658,20 @@ class ScaledScrollbarLayerTestResourceCreation : public testing::Test { ScaledScrollbarLayerTestResourceCreation() : fake_client_(FakeLayerTreeHostClient::DIRECT_3D) {} - void TestResourceUpload(size_t expected_resources, const float test_scale) { + void TestResourceUpload(const float test_scale) { layer_tree_host_.reset( new MockLayerTreeHost(&fake_client_, layer_tree_settings_)); gfx::Point scrollbar_location(0, 185); - scoped_ptr<FakeScrollbar> scrollbar(new FakeScrollbar(false, true, false)); - scrollbar->set_location(scrollbar_location); - scoped_refptr<Layer> layer_tree_root = Layer::Create(); scoped_refptr<Layer> content_layer = Layer::Create(); - scoped_refptr<Layer> scrollbar_layer = - ScrollbarLayer::Create(scrollbar.PassAs<cc::Scrollbar>(), - layer_tree_root->id()); + scoped_refptr<FakePaintedScrollbarLayer> scrollbar_layer = + FakePaintedScrollbarLayer::Create(false, true, layer_tree_root->id()); + layer_tree_root->AddChild(content_layer); layer_tree_root->AddChild(scrollbar_layer); layer_tree_host_->InitializeOutputSurfaceIfNeeded(); - layer_tree_host_->contents_texture_manager()-> - SetMaxMemoryLimitBytes(1024 * 1024); layer_tree_host_->SetRootLayer(layer_tree_root); scrollbar_layer->SetIsDrawable(true); @@ -529,30 +698,23 @@ class ScaledScrollbarLayerTestResourceCreation : public testing::Test { testing::Mock::VerifyAndClearExpectations(layer_tree_host_.get()); EXPECT_EQ(scrollbar_layer->layer_tree_host(), layer_tree_host_.get()); - PriorityCalculator calculator; ResourceUpdateQueue queue; OcclusionTracker occlusion_tracker(gfx::Rect(), false); - scrollbar_layer->SavePaintProperties(); - scrollbar_layer->SetTexturePriorities(calculator); - layer_tree_host_->contents_texture_manager()->PrioritizeTextures(); scrollbar_layer->Update(&queue, &occlusion_tracker); - EXPECT_EQ(expected_resources, queue.PartialUploadSize()); // Verify that we have not generated any content uploads that are larger // than their destination textures. - while (queue.HasMoreUpdates()) { - ResourceUpdate update = queue.TakeFirstPartialUpload(); - EXPECT_LE(update.texture->size().width(), - scrollbar_layer->content_bounds().width()); - EXPECT_LE(update.texture->size().height(), - scrollbar_layer->content_bounds().height()); - - EXPECT_LE(update.dest_offset.x() + update.content_rect.width(), - update.texture->size().width()); - EXPECT_LE(update.dest_offset.y() + update.content_rect.height(), - update.texture->size().height()); - } + + gfx::Size track_size = layer_tree_host_->ui_resource_size( + scrollbar_layer->track_resource_id()); + gfx::Size thumb_size = layer_tree_host_->ui_resource_size( + scrollbar_layer->thumb_resource_id()); + + EXPECT_LE(track_size.width(), scrollbar_layer->content_bounds().width()); + EXPECT_LE(track_size.height(), scrollbar_layer->content_bounds().height()); + EXPECT_LE(thumb_size.width(), scrollbar_layer->content_bounds().width()); + EXPECT_LE(thumb_size.height(), scrollbar_layer->content_bounds().height()); testing::Mock::VerifyAndClearExpectations(layer_tree_host_.get()); @@ -566,10 +728,11 @@ class ScaledScrollbarLayerTestResourceCreation : public testing::Test { }; TEST_F(ScaledScrollbarLayerTestResourceCreation, ScaledResourceUpload) { - layer_tree_settings_.solid_color_scrollbars = false; // Pick a test scale that moves the scrollbar's (non-zero) position to // a non-pixel-aligned location. - TestResourceUpload(2, 1.41f); + TestResourceUpload(.041f); + TestResourceUpload(1.41f); + TestResourceUpload(4.1f); } } // namespace diff --git a/chromium/cc/layers/solid_color_scrollbar_layer.cc b/chromium/cc/layers/solid_color_scrollbar_layer.cc new file mode 100644 index 00000000000..d9ed922e5d8 --- /dev/null +++ b/chromium/cc/layers/solid_color_scrollbar_layer.cc @@ -0,0 +1,68 @@ +// 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/layers/solid_color_scrollbar_layer.h" + +#include "base/memory/scoped_ptr.h" +#include "cc/layers/layer_impl.h" +#include "cc/layers/solid_color_scrollbar_layer_impl.h" + +namespace cc { + +scoped_ptr<LayerImpl> SolidColorScrollbarLayer::CreateLayerImpl( + LayerTreeImpl* tree_impl) { + return SolidColorScrollbarLayerImpl::Create( + tree_impl, id(), orientation(), thumb_thickness_, + is_left_side_vertical_scrollbar_).PassAs<LayerImpl>(); +} + +scoped_refptr<SolidColorScrollbarLayer> SolidColorScrollbarLayer::Create( + ScrollbarOrientation orientation, + int thumb_thickness, + bool is_left_side_vertical_scrollbar, + int scroll_layer_id) { + return make_scoped_refptr(new SolidColorScrollbarLayer( + orientation, + thumb_thickness, + is_left_side_vertical_scrollbar, + scroll_layer_id)); +} + +SolidColorScrollbarLayer::SolidColorScrollbarLayer( + ScrollbarOrientation orientation, + int thumb_thickness, + bool is_left_side_vertical_scrollbar, + int scroll_layer_id) + : scroll_layer_id_(scroll_layer_id), + orientation_(orientation), + thumb_thickness_(thumb_thickness), + is_left_side_vertical_scrollbar_(is_left_side_vertical_scrollbar) {} + +SolidColorScrollbarLayer::~SolidColorScrollbarLayer() {} + +ScrollbarLayerInterface* SolidColorScrollbarLayer::ToScrollbarLayer() { + return this; +} + +bool SolidColorScrollbarLayer::OpacityCanAnimateOnImplThread() const { + return true; +} + +int SolidColorScrollbarLayer::ScrollLayerId() const { + return scroll_layer_id_; +} + +void SolidColorScrollbarLayer::SetScrollLayerId(int id) { + if (id == scroll_layer_id_) + return; + + scroll_layer_id_ = id; + SetNeedsFullTreeSync(); +} + +ScrollbarOrientation SolidColorScrollbarLayer::orientation() const { + return orientation_; +} + +} // namespace cc diff --git a/chromium/cc/layers/solid_color_scrollbar_layer.h b/chromium/cc/layers/solid_color_scrollbar_layer.h new file mode 100644 index 00000000000..9c0dc1ed473 --- /dev/null +++ b/chromium/cc/layers/solid_color_scrollbar_layer.h @@ -0,0 +1,54 @@ +// 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. + +#ifndef CC_LAYERS_SOLID_COLOR_SCROLLBAR_LAYER_H_ +#define CC_LAYERS_SOLID_COLOR_SCROLLBAR_LAYER_H_ + +#include "cc/base/cc_export.h" +#include "cc/layers/layer.h" +#include "cc/layers/scrollbar_layer_interface.h" + +namespace cc { + +class CC_EXPORT SolidColorScrollbarLayer : public ScrollbarLayerInterface, + public Layer { + public: + virtual scoped_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl) + OVERRIDE; + + static scoped_refptr<SolidColorScrollbarLayer> Create( + ScrollbarOrientation orientation, + int thumb_thickness, + bool is_left_side_vertical_scrollbar, + int scroll_layer_id); + + // Layer overrides. + virtual bool OpacityCanAnimateOnImplThread() const OVERRIDE; + virtual ScrollbarLayerInterface* ToScrollbarLayer() OVERRIDE; + + // ScrollbarLayerInterface + virtual int ScrollLayerId() const OVERRIDE; + virtual void SetScrollLayerId(int id) OVERRIDE; + + virtual ScrollbarOrientation orientation() const OVERRIDE; + + protected: + SolidColorScrollbarLayer(ScrollbarOrientation orientation, + int thumb_thickness, + bool is_left_side_vertical_scrollbar, + int scroll_layer_id); + virtual ~SolidColorScrollbarLayer(); + + private: + int scroll_layer_id_; + ScrollbarOrientation orientation_; + int thumb_thickness_; + bool is_left_side_vertical_scrollbar_; + + DISALLOW_COPY_AND_ASSIGN(SolidColorScrollbarLayer); +}; + +} // namespace cc + +#endif // CC_LAYERS_SOLID_COLOR_SCROLLBAR_LAYER_H_ diff --git a/chromium/cc/layers/solid_color_scrollbar_layer_impl.cc b/chromium/cc/layers/solid_color_scrollbar_layer_impl.cc new file mode 100644 index 00000000000..9f46a8080f5 --- /dev/null +++ b/chromium/cc/layers/solid_color_scrollbar_layer_impl.cc @@ -0,0 +1,88 @@ +// 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/layers/quad_sink.h" +#include "cc/layers/solid_color_scrollbar_layer_impl.h" +#include "cc/quads/solid_color_draw_quad.h" +#include "cc/trees/layer_tree_impl.h" +#include "cc/trees/layer_tree_settings.h" + +namespace cc { + +scoped_ptr<SolidColorScrollbarLayerImpl> SolidColorScrollbarLayerImpl::Create( + LayerTreeImpl* tree_impl, + int id, + ScrollbarOrientation orientation, + int thumb_thickness, + bool is_left_side_vertical_scrollbar) { + return make_scoped_ptr(new SolidColorScrollbarLayerImpl( + tree_impl, id, orientation, thumb_thickness, + is_left_side_vertical_scrollbar)); +} + +SolidColorScrollbarLayerImpl::~SolidColorScrollbarLayerImpl() {} + +scoped_ptr<LayerImpl> SolidColorScrollbarLayerImpl::CreateLayerImpl( + LayerTreeImpl* tree_impl) { + return SolidColorScrollbarLayerImpl::Create( + tree_impl, id(), orientation(), thumb_thickness_, + is_left_side_vertical_scrollbar()).PassAs<LayerImpl>(); +} + +SolidColorScrollbarLayerImpl::SolidColorScrollbarLayerImpl( + LayerTreeImpl* tree_impl, + int id, + ScrollbarOrientation orientation, + int thumb_thickness, + bool is_left_side_vertical_scrollbar) + : ScrollbarLayerImplBase(tree_impl, id, orientation, + is_left_side_vertical_scrollbar), + thumb_thickness_(thumb_thickness), + color_(tree_impl->settings().solid_color_scrollbar_color) {} + +void SolidColorScrollbarLayerImpl::PushPropertiesTo(LayerImpl* layer) { + ScrollbarLayerImplBase::PushPropertiesTo(layer); +} + +int SolidColorScrollbarLayerImpl::ThumbThickness() const { + if (thumb_thickness_ != -1) + return thumb_thickness_; + + if (orientation() == HORIZONTAL) + return bounds().height(); + else + return bounds().width(); +} + +int SolidColorScrollbarLayerImpl::ThumbLength() const { + return std::max( + static_cast<int>(visible_to_total_length_ratio() * TrackLength()), + ThumbThickness()); +} + +float SolidColorScrollbarLayerImpl::TrackLength() const { + if (orientation() == HORIZONTAL) + return bounds().width(); + else + return bounds().height() + vertical_adjust(); +} + +int SolidColorScrollbarLayerImpl::TrackStart() const { + return 0; +} + +void SolidColorScrollbarLayerImpl::AppendQuads(QuadSink* quad_sink, + AppendQuadsData* append_quads_data) { + gfx::Rect thumb_quad_rect = ComputeThumbQuadRect(); + + SharedQuadState* shared_quad_state = + quad_sink->UseSharedQuadState(CreateSharedQuadState()); + AppendDebugBorderQuad(quad_sink, shared_quad_state, append_quads_data); + + scoped_ptr<SolidColorDrawQuad> quad = SolidColorDrawQuad::Create(); + quad->SetNew(shared_quad_state, thumb_quad_rect, color_, false); + quad_sink->Append(quad.PassAs<DrawQuad>(), append_quads_data); +} + +} // namespace cc diff --git a/chromium/cc/layers/solid_color_scrollbar_layer_impl.h b/chromium/cc/layers/solid_color_scrollbar_layer_impl.h new file mode 100644 index 00000000000..56cbacd888c --- /dev/null +++ b/chromium/cc/layers/solid_color_scrollbar_layer_impl.h @@ -0,0 +1,52 @@ +// 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. + +#ifndef CC_LAYERS_SOLID_COLOR_SCROLLBAR_LAYER_IMPL_H_ +#define CC_LAYERS_SOLID_COLOR_SCROLLBAR_LAYER_IMPL_H_ + +#include "cc/base/cc_export.h" +#include "cc/layers/scrollbar_layer_impl_base.h" + +namespace cc { + +class CC_EXPORT SolidColorScrollbarLayerImpl : public ScrollbarLayerImplBase { + public: + static scoped_ptr<SolidColorScrollbarLayerImpl> Create( + LayerTreeImpl* tree_impl, + int id, + ScrollbarOrientation orientation, + int thumb_thickness, + bool is_left_side_vertical_scrollbar); + virtual ~SolidColorScrollbarLayerImpl(); + + // LayerImpl overrides. + virtual scoped_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl) + OVERRIDE; + virtual void PushPropertiesTo(LayerImpl* layer) OVERRIDE; + + virtual void AppendQuads(QuadSink* quad_sink, + AppendQuadsData* append_quads_data) OVERRIDE; + + + protected: + SolidColorScrollbarLayerImpl(LayerTreeImpl* tree_impl, + int id, + ScrollbarOrientation orientation, + int thumb_thickness, + bool is_left_side_vertical_scrollbar); + + // ScrollbarLayerImplBase implementation. + virtual int ThumbThickness() const OVERRIDE; + virtual int ThumbLength() const OVERRIDE; + virtual float TrackLength() const OVERRIDE; + virtual int TrackStart() const OVERRIDE; + + private: + int thumb_thickness_; + SkColor color_; +}; + +} // namespace cc + +#endif // CC_LAYERS_SOLID_COLOR_SCROLLBAR_LAYER_IMPL_H_ diff --git a/chromium/cc/layers/texture_layer.cc b/chromium/cc/layers/texture_layer.cc index 11009fa1b68..a845709448f 100644 --- a/chromium/cc/layers/texture_layer.cc +++ b/chromium/cc/layers/texture_layer.cc @@ -5,10 +5,13 @@ #include "cc/layers/texture_layer.h" #include "base/bind.h" +#include "base/callback_helpers.h" #include "base/location.h" -#include "base/message_loop/message_loop_proxy.h" +#include "base/synchronization/lock.h" #include "cc/layers/texture_layer_client.h" #include "cc/layers/texture_layer_impl.h" +#include "cc/resources/single_release_callback.h" +#include "cc/trees/blocking_task_runner.h" #include "cc/trees/layer_tree_host.h" #include "third_party/khronos/GLES2/gl2.h" #include "third_party/WebKit/public/platform/WebGraphicsContext3D.h" @@ -51,7 +54,7 @@ void TextureLayer::ClearClient() { layer_tree_host()->StopRateLimiter(client_->Context3d()); client_ = NULL; if (uses_mailbox_) - SetTextureMailbox(TextureMailbox()); + SetTextureMailbox(TextureMailbox(), scoped_ptr<SingleReleaseCallback>()); else SetTextureId(0); } @@ -125,23 +128,34 @@ void TextureLayer::SetTextureId(unsigned id) { layer_tree_host()->AcquireLayerTextures(); texture_id_ = id; SetNeedsCommit(); + // The texture id needs to be removed from the active tree before the + // commit is called complete. + SetNextCommitWaitsForActivation(); } -void TextureLayer::SetTextureMailbox(const TextureMailbox& mailbox) { +void TextureLayer::SetTextureMailbox( + const TextureMailbox& mailbox, + scoped_ptr<SingleReleaseCallback> release_callback) { DCHECK(uses_mailbox_); DCHECK(!mailbox.IsValid() || !holder_ref_ || !mailbox.Equals(holder_ref_->holder()->mailbox())); + DCHECK_EQ(mailbox.IsValid(), !!release_callback); + // If we never commited the mailbox, we need to release it here. if (mailbox.IsValid()) - holder_ref_ = MailboxHolder::Create(mailbox); + holder_ref_ = MailboxHolder::Create(mailbox, release_callback.Pass()); else holder_ref_.reset(); needs_set_mailbox_ = true; SetNeedsCommit(); + // The active frame needs to be replaced and the mailbox returned before the + // commit is called complete. + SetNextCommitWaitsForActivation(); } void TextureLayer::WillModifyTexture() { - if (layer_tree_host() && (DrawsContent() || content_committed_)) { + if (!uses_mailbox_ && layer_tree_host() && (DrawsContent() || + content_committed_)) { layer_tree_host()->AcquireLayerTextures(); content_committed_ = false; } @@ -161,16 +175,24 @@ void TextureLayer::SetLayerTreeHost(LayerTreeHost* host) { } if (layer_tree_host()) { - if (texture_id_) + if (texture_id_) { layer_tree_host()->AcquireLayerTextures(); + // The texture id needs to be removed from the active tree before the + // commit is called complete. + SetNextCommitWaitsForActivation(); + } if (rate_limit_context_ && client_) layer_tree_host()->StopRateLimiter(client_->Context3d()); } // If we're removed from the tree, the TextureLayerImpl will be destroyed, and // we will need to set the mailbox again on a new TextureLayerImpl the next // time we push. - if (!host && uses_mailbox_ && holder_ref_) + if (!host && uses_mailbox_ && holder_ref_) { needs_set_mailbox_ = true; + // The active frame needs to be replaced and the mailbox returned before the + // commit is called complete. + SetNextCommitWaitsForActivation(); + } Layer::SetLayerTreeHost(host); } @@ -184,19 +206,25 @@ bool TextureLayer::Update(ResourceUpdateQueue* queue, if (client_) { if (uses_mailbox_) { TextureMailbox mailbox; + scoped_ptr<SingleReleaseCallback> release_callback; if (client_->PrepareTextureMailbox( - &mailbox, layer_tree_host()->UsingSharedMemoryResources())) { - SetTextureMailbox(mailbox); + &mailbox, + &release_callback, + layer_tree_host()->UsingSharedMemoryResources())) { + SetTextureMailbox(mailbox, release_callback.Pass()); updated = true; } } else { - DCHECK(client_->Context3d()); texture_id_ = client_->PrepareTexture(); + DCHECK_EQ(!!texture_id_, !!client_->Context3d()); if (client_->Context3d() && client_->Context3d()->getGraphicsResetStatusARB() != GL_NO_ERROR) texture_id_ = 0; updated = true; SetNeedsPushProperties(); + // The texture id needs to be removed from the active tree before the + // commit is called complete. + SetNextCommitWaitsForActivation(); } } @@ -218,18 +246,18 @@ void TextureLayer::PushPropertiesTo(LayerImpl* layer) { texture_layer->set_blend_background_color(blend_background_color_); if (uses_mailbox_ && needs_set_mailbox_) { TextureMailbox texture_mailbox; + scoped_ptr<SingleReleaseCallback> release_callback; if (holder_ref_) { MailboxHolder* holder = holder_ref_->holder(); - TextureMailbox::ReleaseCallback callback = - holder->GetCallbackForImplThread(); - texture_mailbox = holder->mailbox().CopyWithNewCallback(callback); + texture_mailbox = holder->mailbox(); + release_callback = holder->GetCallbackForImplThread(); } - texture_layer->SetTextureMailbox(texture_mailbox); + texture_layer->SetTextureMailbox(texture_mailbox, release_callback.Pass()); needs_set_mailbox_ = false; } else { texture_layer->set_texture_id(texture_id_); + content_committed_ = DrawsContent(); } - content_committed_ = DrawsContent(); } Region TextureLayer::VisibleContentOpaqueRegion() const { @@ -242,13 +270,6 @@ Region TextureLayer::VisibleContentOpaqueRegion() const { return Region(); } -bool TextureLayer::BlocksPendingCommit() const { - // Double-buffered texture layers need to be blocked until they can be made - // triple-buffered. Single-buffered layers already prevent draws, so - // can block too for simplicity. - return DrawsContent(); -} - bool TextureLayer::CanClipSelf() const { return true; } @@ -263,10 +284,13 @@ TextureLayer::MailboxHolder::MainThreadReference::~MainThreadReference() { holder_->InternalRelease(); } -TextureLayer::MailboxHolder::MailboxHolder(const TextureMailbox& mailbox) - : message_loop_(base::MessageLoopProxy::current()), +TextureLayer::MailboxHolder::MailboxHolder( + const TextureMailbox& mailbox, + scoped_ptr<SingleReleaseCallback> release_callback) + : message_loop_(BlockingTaskRunner::current()), internal_references_(0), mailbox_(mailbox), + release_callback_(release_callback.Pass()), sync_point_(mailbox.sync_point()), is_lost_(false) { } @@ -276,23 +300,27 @@ TextureLayer::MailboxHolder::~MailboxHolder() { } scoped_ptr<TextureLayer::MailboxHolder::MainThreadReference> -TextureLayer::MailboxHolder::Create(const TextureMailbox& mailbox) { +TextureLayer::MailboxHolder::Create( + const TextureMailbox& mailbox, + scoped_ptr<SingleReleaseCallback> release_callback) { return scoped_ptr<MainThreadReference>(new MainThreadReference( - new MailboxHolder(mailbox))); + new MailboxHolder(mailbox, release_callback.Pass()))); } void TextureLayer::MailboxHolder::Return(unsigned sync_point, bool is_lost) { + base::AutoLock lock(arguments_lock_); sync_point_ = sync_point; is_lost_ = is_lost; } -TextureMailbox::ReleaseCallback +scoped_ptr<SingleReleaseCallback> TextureLayer::MailboxHolder::GetCallbackForImplThread() { // We can't call GetCallbackForImplThread if we released the main thread // reference. DCHECK_GT(internal_references_, 0u); InternalAddRef(); - return base::Bind(&MailboxHolder::ReturnAndReleaseOnImplThread, this); + return SingleReleaseCallback::Create( + base::Bind(&MailboxHolder::ReturnAndReleaseOnImplThread, this)); } void TextureLayer::MailboxHolder::InternalAddRef() { @@ -302,23 +330,17 @@ void TextureLayer::MailboxHolder::InternalAddRef() { void TextureLayer::MailboxHolder::InternalRelease() { DCHECK(message_loop_->BelongsToCurrentThread()); if (!--internal_references_) { - mailbox_.RunReleaseCallback(sync_point_, is_lost_); + release_callback_->Run(sync_point_, is_lost_); mailbox_ = TextureMailbox(); + release_callback_.reset(); } } -void TextureLayer::MailboxHolder::ReturnAndReleaseOnMainThread( - unsigned sync_point, bool is_lost) { - DCHECK(message_loop_->BelongsToCurrentThread()); - Return(sync_point, is_lost); - InternalRelease(); -} - void TextureLayer::MailboxHolder::ReturnAndReleaseOnImplThread( unsigned sync_point, bool is_lost) { - message_loop_->PostTask(FROM_HERE, base::Bind( - &MailboxHolder::ReturnAndReleaseOnMainThread, - this, sync_point, is_lost)); + Return(sync_point, is_lost); + message_loop_->PostTask(FROM_HERE, + base::Bind(&MailboxHolder::InternalRelease, this)); } } // namespace cc diff --git a/chromium/cc/layers/texture_layer.h b/chromium/cc/layers/texture_layer.h index 9e63b6825eb..5fb214c2c13 100644 --- a/chromium/cc/layers/texture_layer.h +++ b/chromium/cc/layers/texture_layer.h @@ -8,23 +8,80 @@ #include <string> #include "base/callback.h" +#include "base/synchronization/lock.h" #include "cc/base/cc_export.h" #include "cc/layers/layer.h" #include "cc/resources/texture_mailbox.h" namespace WebKit { class WebGraphicsContext3D; } -namespace base { -class MessageLoopProxy; -} - namespace cc { - +class BlockingTaskRunner; +class SingleReleaseCallback; class TextureLayerClient; // A Layer containing a the rendered output of a plugin instance. class CC_EXPORT TextureLayer : public Layer { public: + class CC_EXPORT MailboxHolder + : public base::RefCountedThreadSafe<MailboxHolder> { + public: + class CC_EXPORT MainThreadReference { + public: + explicit MainThreadReference(MailboxHolder* holder); + ~MainThreadReference(); + MailboxHolder* holder() { return holder_.get(); } + + private: + scoped_refptr<MailboxHolder> holder_; + DISALLOW_COPY_AND_ASSIGN(MainThreadReference); + }; + + const TextureMailbox& mailbox() const { return mailbox_; } + void Return(unsigned sync_point, bool is_lost); + + // Gets a ReleaseCallback that can be called from another thread. Note: the + // caller must ensure the callback is called. + scoped_ptr<SingleReleaseCallback> GetCallbackForImplThread(); + + protected: + friend class TextureLayer; + + // Protected visiblity so only TextureLayer and unit tests can create these. + static scoped_ptr<MainThreadReference> Create( + const TextureMailbox& mailbox, + scoped_ptr<SingleReleaseCallback> release_callback); + virtual ~MailboxHolder(); + + private: + friend class base::RefCountedThreadSafe<MailboxHolder>; + friend class MainThreadReference; + explicit MailboxHolder(const TextureMailbox& mailbox, + scoped_ptr<SingleReleaseCallback> release_callback); + + void InternalAddRef(); + void InternalRelease(); + void ReturnAndReleaseOnImplThread(unsigned sync_point, bool is_lost); + + // This member is thread safe, and is accessed on main and impl threads. + const scoped_refptr<BlockingTaskRunner> message_loop_; + + // These members are only accessed on the main thread, or on the impl thread + // during commit where the main thread is blocked. + unsigned internal_references_; + TextureMailbox mailbox_; + scoped_ptr<SingleReleaseCallback> release_callback_; + + // This lock guards the sync_point_ and is_lost_ fields because they can be + // accessed on both the impl and main thread. We do this to ensure that the + // values of these fields are well-ordered such that the last call to + // ReturnAndReleaseOnImplThread() defines their values. + base::Lock arguments_lock_; + unsigned sync_point_; + bool is_lost_; + DISALLOW_COPY_AND_ASSIGN(MailboxHolder); + }; + // If this texture layer requires special preparation logic for each frame // driven by the compositor, pass in a non-nil client. Pass in a nil client // pointer if texture updates are driven by an external process. @@ -67,11 +124,13 @@ class CC_EXPORT TextureLayer : public Layer { void SetRateLimitContext(bool rate_limit); // Code path for plugins which supply their own texture ID. + // DEPRECATED. DO NOT USE. void SetTextureId(unsigned texture_id); // Code path for plugins which supply their own mailbox. bool uses_mailbox() const { return uses_mailbox_; } - void SetTextureMailbox(const TextureMailbox& mailbox); + void SetTextureMailbox(const TextureMailbox& mailbox, + scoped_ptr<SingleReleaseCallback> release_callback); void WillModifyTexture(); @@ -83,7 +142,6 @@ class CC_EXPORT TextureLayer : public Layer { const OcclusionTracker* occlusion) OVERRIDE; virtual void PushPropertiesTo(LayerImpl* layer) OVERRIDE; virtual Region VisibleContentOpaqueRegion() const OVERRIDE; - virtual bool BlocksPendingCommit() const OVERRIDE; virtual bool CanClipSelf() const OVERRIDE; @@ -92,50 +150,6 @@ class CC_EXPORT TextureLayer : public Layer { virtual ~TextureLayer(); private: - class MailboxHolder : public base::RefCountedThreadSafe<MailboxHolder> { - public: - class MainThreadReference { - public: - explicit MainThreadReference(MailboxHolder* holder); - ~MainThreadReference(); - MailboxHolder* holder() { return holder_.get(); } - - private: - scoped_refptr<MailboxHolder> holder_; - DISALLOW_COPY_AND_ASSIGN(MainThreadReference); - }; - - static scoped_ptr<MainThreadReference> Create( - const TextureMailbox& mailbox); - - const TextureMailbox& mailbox() const { return mailbox_; } - void Return(unsigned sync_point, bool is_lost); - - // Gets a ReleaseCallback that can be called from another thread. Note: the - // caller must ensure the callback is called. - TextureMailbox::ReleaseCallback GetCallbackForImplThread(); - - private: - friend class base::RefCountedThreadSafe<MailboxHolder>; - friend class MainThreadReference; - explicit MailboxHolder(const TextureMailbox& mailbox); - ~MailboxHolder(); - void InternalAddRef(); - void InternalRelease(); - void ReturnAndReleaseOnMainThread(unsigned sync_point, bool is_lost); - void ReturnAndReleaseOnImplThread(unsigned sync_point, bool is_lost); - - // Thread safety notes: except for the thread-safe message_loop_, all fields - // are only used on the main thread, or on the impl thread during commit - // where the main thread is blocked. - const scoped_refptr<base::MessageLoopProxy> message_loop_; - unsigned internal_references_; - TextureMailbox mailbox_; - unsigned sync_point_; - bool is_lost_; - DISALLOW_COPY_AND_ASSIGN(MailboxHolder); - }; - TextureLayerClient* client_; bool uses_mailbox_; diff --git a/chromium/cc/layers/texture_layer_client.h b/chromium/cc/layers/texture_layer_client.h index 73d34b30775..187b12f16c4 100644 --- a/chromium/cc/layers/texture_layer_client.h +++ b/chromium/cc/layers/texture_layer_client.h @@ -5,6 +5,8 @@ #ifndef CC_LAYERS_TEXTURE_LAYER_CLIENT_H_ #define CC_LAYERS_TEXTURE_LAYER_CLIENT_H_ +#include "cc/resources/single_release_callback.h" + namespace WebKit { class WebGraphicsContext3D; } namespace cc { @@ -24,8 +26,10 @@ class TextureLayerClient { // Returns true and provides a mailbox if a new frame is available. // Returns false if no new data is available // and the old mailbox is to be reused. - virtual bool PrepareTextureMailbox(TextureMailbox* mailbox, - bool use_shared_memory) = 0; + virtual bool PrepareTextureMailbox( + TextureMailbox* mailbox, + scoped_ptr<SingleReleaseCallback>* release_callback, + bool use_shared_memory) = 0; protected: virtual ~TextureLayerClient() {} diff --git a/chromium/cc/layers/texture_layer_impl.cc b/chromium/cc/layers/texture_layer_impl.cc index 851bf9a145b..e16fd3c3f18 100644 --- a/chromium/cc/layers/texture_layer_impl.cc +++ b/chromium/cc/layers/texture_layer_impl.cc @@ -4,12 +4,15 @@ #include "cc/layers/texture_layer_impl.h" +#include <vector> + #include "base/strings/stringprintf.h" #include "cc/layers/quad_sink.h" #include "cc/output/renderer.h" #include "cc/quads/texture_draw_quad.h" #include "cc/resources/platform_color.h" #include "cc/resources/scoped_resource.h" +#include "cc/resources/single_release_callback.h" #include "cc/trees/layer_tree_impl.h" namespace cc { @@ -36,10 +39,14 @@ TextureLayerImpl::TextureLayerImpl(LayerTreeImpl* tree_impl, TextureLayerImpl::~TextureLayerImpl() { FreeTextureMailbox(); } -void TextureLayerImpl::SetTextureMailbox(const TextureMailbox& mailbox) { +void TextureLayerImpl::SetTextureMailbox( + const TextureMailbox& mailbox, + scoped_ptr<SingleReleaseCallback> release_callback) { DCHECK(uses_mailbox_); + DCHECK_EQ(mailbox.IsValid(), !!release_callback); FreeTextureMailbox(); texture_mailbox_ = mailbox; + release_callback_ = release_callback.Pass(); own_mailbox_ = true; valid_texture_copy_ = false; } @@ -60,7 +67,8 @@ void TextureLayerImpl::PushPropertiesTo(LayerImpl* layer) { texture_layer->set_vertex_opacity(vertex_opacity_); texture_layer->set_premultiplied_alpha(premultiplied_alpha_); if (uses_mailbox_ && own_mailbox_) { - texture_layer->SetTextureMailbox(texture_mailbox_); + texture_layer->SetTextureMailbox(texture_mailbox_, + release_callback_.Pass()); own_mailbox_ = false; } else { texture_layer->set_texture_id(texture_id_); @@ -80,7 +88,8 @@ bool TextureLayerImpl::WillDraw(DrawMode draw_mode, texture_mailbox_.IsSharedMemory())) { external_texture_resource_ = resource_provider->CreateResourceFromTextureMailbox( - texture_mailbox_); + texture_mailbox_, + release_callback_.Pass()); DCHECK(external_texture_resource_); texture_copy_.reset(); valid_texture_copy_ = false; @@ -102,8 +111,8 @@ bool TextureLayerImpl::WillDraw(DrawMode draw_mode, if (!texture_copy_->id()) { texture_copy_->Allocate(texture_mailbox_.shared_memory_size(), - resource_provider->best_texture_format(), - ResourceProvider::TextureUsageAny); + ResourceProvider::TextureUsageAny, + resource_provider->best_texture_format()); } if (texture_copy_->id()) { @@ -229,7 +238,10 @@ void TextureLayerImpl::FreeTextureMailbox() { return; if (own_mailbox_) { DCHECK(!external_texture_resource_); - texture_mailbox_.RunReleaseCallback(texture_mailbox_.sync_point(), false); + if (release_callback_) + release_callback_->Run(texture_mailbox_.sync_point(), false); + texture_mailbox_ = TextureMailbox(); + release_callback_.reset(); } else if (external_texture_resource_) { DCHECK(!own_mailbox_); ResourceProvider* resource_provider = diff --git a/chromium/cc/layers/texture_layer_impl.h b/chromium/cc/layers/texture_layer_impl.h index f85720688a6..90c26923c8e 100644 --- a/chromium/cc/layers/texture_layer_impl.h +++ b/chromium/cc/layers/texture_layer_impl.h @@ -12,6 +12,7 @@ #include "cc/layers/layer_impl.h" namespace cc { +class SingleReleaseCallback; class ScopedResource; class CC_EXPORT TextureLayerImpl : public LayerImpl { @@ -61,7 +62,8 @@ class CC_EXPORT TextureLayerImpl : public LayerImpl { virtual bool CanClipSelf() const OVERRIDE; - void SetTextureMailbox(const TextureMailbox& mailbox); + void SetTextureMailbox(const TextureMailbox& mailbox, + scoped_ptr<SingleReleaseCallback> release_callback); private: TextureLayerImpl(LayerTreeImpl* tree_impl, int id, bool uses_mailbox); @@ -81,6 +83,7 @@ class CC_EXPORT TextureLayerImpl : public LayerImpl { scoped_ptr<ScopedResource> texture_copy_; TextureMailbox texture_mailbox_; + scoped_ptr<SingleReleaseCallback> release_callback_; bool uses_mailbox_; bool own_mailbox_; bool valid_texture_copy_; diff --git a/chromium/cc/layers/texture_layer_unittest.cc b/chromium/cc/layers/texture_layer_unittest.cc index aa08e9f81ee..239c5b8fa22 100644 --- a/chromium/cc/layers/texture_layer_unittest.cc +++ b/chromium/cc/layers/texture_layer_unittest.cc @@ -4,16 +4,28 @@ #include "cc/layers/texture_layer.h" +#include <algorithm> #include <string> +#include "base/bind.h" #include "base/callback.h" +#include "base/synchronization/waitable_event.h" +#include "base/threading/thread.h" +#include "base/time/time.h" +#include "cc/debug/test_web_graphics_context_3d.h" +#include "cc/layers/solid_color_layer.h" #include "cc/layers/texture_layer_client.h" #include "cc/layers/texture_layer_impl.h" +#include "cc/output/compositor_frame_ack.h" +#include "cc/output/context_provider.h" +#include "cc/resources/returned_resource.h" #include "cc/test/fake_impl_proxy.h" #include "cc/test/fake_layer_tree_host_client.h" #include "cc/test/fake_layer_tree_host_impl.h" +#include "cc/test/fake_output_surface.h" #include "cc/test/layer_test_common.h" #include "cc/test/layer_tree_test.h" +#include "cc/trees/blocking_task_runner.h" #include "cc/trees/layer_tree_host.h" #include "cc/trees/layer_tree_impl.h" #include "cc/trees/single_thread_proxy.h" @@ -187,7 +199,7 @@ TEST_F(TextureLayerTest, SyncImplWhenRemovingFromTree) { TEST_F(TextureLayerTest, CheckPropertyChangeCausesCorrectBehavior) { scoped_refptr<TextureLayer> test_layer = TextureLayer::Create(NULL); - layer_tree_host_->SetRootLayer(test_layer); + EXPECT_SET_NEEDS_COMMIT(1, layer_tree_host_->SetRootLayer(test_layer)); // Test properties that should call SetNeedsCommit. All properties need to // be set to new values in order for SetNeedsCommit to be called. @@ -243,9 +255,12 @@ class FakeTextureLayerClient : public TextureLayerClient { return context_.get(); } - virtual bool PrepareTextureMailbox(TextureMailbox* mailbox, - bool use_shared_memory) OVERRIDE { + virtual bool PrepareTextureMailbox( + TextureMailbox* mailbox, + scoped_ptr<SingleReleaseCallback>* release_callback, + bool use_shared_memory) OVERRIDE { *mailbox = TextureMailbox(); + *release_callback = scoped_ptr<SingleReleaseCallback>(); return true; } @@ -328,25 +343,25 @@ struct CommonMailboxObjects { mailbox_name2_); gpu::Mailbox m1; m1.SetName(reinterpret_cast<const int8*>(mailbox_name1_.data())); - mailbox1_ = TextureMailbox(m1, release_mailbox1_, sync_point1_); + mailbox1_ = TextureMailbox(m1, sync_point1_); gpu::Mailbox m2; m2.SetName(reinterpret_cast<const int8*>(mailbox_name2_.data())); - mailbox2_ = TextureMailbox(m2, release_mailbox2_, sync_point2_); + mailbox2_ = TextureMailbox(m2, sync_point2_); gfx::Size size(128, 128); EXPECT_TRUE(shared_memory_->CreateAndMapAnonymous(4 * size.GetArea())); release_mailbox3_ = base::Bind(&MockMailboxCallback::Release2, base::Unretained(&mock_callback_), shared_memory_.get()); - mailbox3_ = TextureMailbox(shared_memory_.get(), size, release_mailbox3_); + mailbox3_ = TextureMailbox(shared_memory_.get(), size); } std::string mailbox_name1_; std::string mailbox_name2_; MockMailboxCallback mock_callback_; - TextureMailbox::ReleaseCallback release_mailbox1_; - TextureMailbox::ReleaseCallback release_mailbox2_; - TextureMailbox::ReleaseCallback release_mailbox3_; + ReleaseCallback release_mailbox1_; + ReleaseCallback release_mailbox2_; + ReleaseCallback release_mailbox3_; TextureMailbox mailbox1_; TextureMailbox mailbox2_; TextureMailbox mailbox3_; @@ -355,6 +370,14 @@ struct CommonMailboxObjects { scoped_ptr<base::SharedMemory> shared_memory_; }; +class TestMailboxHolder : public TextureLayer::MailboxHolder { + public: + using TextureLayer::MailboxHolder::Create; + + protected: + virtual ~TestMailboxHolder() {} +}; + class TextureLayerWithMailboxTest : public TextureLayerTest { protected: virtual void TearDown() { @@ -380,7 +403,9 @@ TEST_F(TextureLayerWithMailboxTest, ReplaceMailboxOnMainThreadBeforeCommit) { EXPECT_CALL(*layer_tree_host_, AcquireLayerTextures()).Times(0); EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(AtLeast(1)); - test_layer->SetTextureMailbox(test_data_.mailbox1_); + test_layer->SetTextureMailbox( + test_data_.mailbox1_, + SingleReleaseCallback::Create(test_data_.release_mailbox1_)); Mock::VerifyAndClearExpectations(layer_tree_host_.get()); EXPECT_CALL(*layer_tree_host_, AcquireLayerTextures()).Times(0); @@ -390,7 +415,9 @@ TEST_F(TextureLayerWithMailboxTest, ReplaceMailboxOnMainThreadBeforeCommit) { test_data_.sync_point1_, false)) .Times(1); - test_layer->SetTextureMailbox(test_data_.mailbox2_); + test_layer->SetTextureMailbox( + test_data_.mailbox2_, + SingleReleaseCallback::Create(test_data_.release_mailbox2_)); Mock::VerifyAndClearExpectations(layer_tree_host_.get()); Mock::VerifyAndClearExpectations(&test_data_.mock_callback_); @@ -401,11 +428,16 @@ TEST_F(TextureLayerWithMailboxTest, ReplaceMailboxOnMainThreadBeforeCommit) { test_data_.sync_point2_, false)) .Times(1); - test_layer->SetTextureMailbox(TextureMailbox()); + test_layer->SetTextureMailbox(TextureMailbox(), + scoped_ptr<SingleReleaseCallback>()); Mock::VerifyAndClearExpectations(layer_tree_host_.get()); Mock::VerifyAndClearExpectations(&test_data_.mock_callback_); - test_layer->SetTextureMailbox(test_data_.mailbox3_); + EXPECT_CALL(*layer_tree_host_, AcquireLayerTextures()).Times(0); + EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(AtLeast(1)); + test_layer->SetTextureMailbox( + test_data_.mailbox3_, + SingleReleaseCallback::Create(test_data_.release_mailbox3_)); Mock::VerifyAndClearExpectations(layer_tree_host_.get()); Mock::VerifyAndClearExpectations(&test_data_.mock_callback_); @@ -415,13 +447,309 @@ TEST_F(TextureLayerWithMailboxTest, ReplaceMailboxOnMainThreadBeforeCommit) { Release2(test_data_.shared_memory_.get(), 0, false)) .Times(1); - test_layer->SetTextureMailbox(TextureMailbox()); + test_layer->SetTextureMailbox(TextureMailbox(), + scoped_ptr<SingleReleaseCallback>()); Mock::VerifyAndClearExpectations(layer_tree_host_.get()); Mock::VerifyAndClearExpectations(&test_data_.mock_callback_); // Test destructor. EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(AtLeast(1)); - test_layer->SetTextureMailbox(test_data_.mailbox1_); + test_layer->SetTextureMailbox( + test_data_.mailbox1_, + SingleReleaseCallback::Create(test_data_.release_mailbox1_)); +} + +class TextureLayerMailboxHolderTest : public TextureLayerTest { + public: + TextureLayerMailboxHolderTest() + : main_thread_("MAIN") { + main_thread_.Start(); + } + + void Wait(const base::Thread& thread) { + bool manual_reset = false; + bool initially_signaled = false; + base::WaitableEvent event(manual_reset, initially_signaled); + thread.message_loop()->PostTask( + FROM_HERE, + base::Bind(&base::WaitableEvent::Signal, base::Unretained(&event))); + event.Wait(); + } + + void CreateMainRef() { + main_ref_ = TestMailboxHolder::Create( + test_data_.mailbox1_, + SingleReleaseCallback::Create(test_data_.release_mailbox1_)).Pass(); + } + + void ReleaseMainRef() { + main_ref_.reset(); + } + + void CreateImplRef(scoped_ptr<SingleReleaseCallback>* impl_ref) { + *impl_ref = main_ref_->holder()->GetCallbackForImplThread(); + } + + void CapturePostTasksAndWait(base::WaitableEvent* begin_capture, + base::WaitableEvent* wait_for_capture, + base::WaitableEvent* stop_capture) { + begin_capture->Wait(); + BlockingTaskRunner::CapturePostTasks capture; + wait_for_capture->Signal(); + stop_capture->Wait(); + } + + protected: + scoped_ptr<TestMailboxHolder::MainThreadReference> + main_ref_; + base::Thread main_thread_; + CommonMailboxObjects test_data_; +}; + +TEST_F(TextureLayerMailboxHolderTest, TwoCompositors_BothReleaseThenMain) { + scoped_refptr<TextureLayer> test_layer = TextureLayer::CreateForMailbox(NULL); + ASSERT_TRUE(test_layer.get()); + + main_thread_.message_loop()->PostTask( + FROM_HERE, + base::Bind(&TextureLayerMailboxHolderTest::CreateMainRef, + base::Unretained(this))); + + Wait(main_thread_); + + // The texture layer is attached to compositor1, and passes a reference to its + // impl tree. + scoped_ptr<SingleReleaseCallback> compositor1; + main_thread_.message_loop()->PostTask( + FROM_HERE, + base::Bind(&TextureLayerMailboxHolderTest::CreateImplRef, + base::Unretained(this), + &compositor1)); + + // Then the texture layer is removed and attached to compositor2, and passes a + // reference to its impl tree. + scoped_ptr<SingleReleaseCallback> compositor2; + main_thread_.message_loop()->PostTask( + FROM_HERE, + base::Bind(&TextureLayerMailboxHolderTest::CreateImplRef, + base::Unretained(this), + &compositor2)); + + Wait(main_thread_); + Mock::VerifyAndClearExpectations(&test_data_.mock_callback_); + + // The compositors both destroy their impl trees before the main thread layer + // is destroyed. + compositor1->Run(100, false); + compositor2->Run(200, false); + + Wait(main_thread_); + + EXPECT_CALL(test_data_.mock_callback_, Release(_, _, _)).Times(0); + Mock::VerifyAndClearExpectations(&test_data_.mock_callback_); + + // The main thread ref is the last one, so the mailbox is released back to the + // embedder, with the last sync point provided by the impl trees. + EXPECT_CALL(test_data_.mock_callback_, + Release(test_data_.mailbox_name1_, 200, false)).Times(1); + + main_thread_.message_loop()->PostTask( + FROM_HERE, + base::Bind(&TextureLayerMailboxHolderTest::ReleaseMainRef, + base::Unretained(this))); + Wait(main_thread_); + Mock::VerifyAndClearExpectations(&test_data_.mock_callback_); +} + +TEST_F(TextureLayerMailboxHolderTest, TwoCompositors_MainReleaseBetween) { + scoped_refptr<TextureLayer> test_layer = TextureLayer::CreateForMailbox(NULL); + ASSERT_TRUE(test_layer.get()); + + main_thread_.message_loop()->PostTask( + FROM_HERE, + base::Bind(&TextureLayerMailboxHolderTest::CreateMainRef, + base::Unretained(this))); + + Wait(main_thread_); + + // The texture layer is attached to compositor1, and passes a reference to its + // impl tree. + scoped_ptr<SingleReleaseCallback> compositor1; + main_thread_.message_loop()->PostTask( + FROM_HERE, + base::Bind(&TextureLayerMailboxHolderTest::CreateImplRef, + base::Unretained(this), + &compositor1)); + + // Then the texture layer is removed and attached to compositor2, and passes a + // reference to its impl tree. + scoped_ptr<SingleReleaseCallback> compositor2; + main_thread_.message_loop()->PostTask( + FROM_HERE, + base::Bind(&TextureLayerMailboxHolderTest::CreateImplRef, + base::Unretained(this), + &compositor2)); + + Wait(main_thread_); + Mock::VerifyAndClearExpectations(&test_data_.mock_callback_); + + // One compositor destroys their impl tree. + compositor1->Run(100, false); + + // Then the main thread reference is destroyed. + main_thread_.message_loop()->PostTask( + FROM_HERE, + base::Bind(&TextureLayerMailboxHolderTest::ReleaseMainRef, + base::Unretained(this))); + + Wait(main_thread_); + + EXPECT_CALL(test_data_.mock_callback_, Release(_, _, _)).Times(0); + Mock::VerifyAndClearExpectations(&test_data_.mock_callback_); + + // The second impl reference is destroyed last, causing the mailbox to be + // released back to the embedder with the last sync point from the impl tree. + EXPECT_CALL(test_data_.mock_callback_, + Release(test_data_.mailbox_name1_, 200, true)).Times(1); + + compositor2->Run(200, true); + Wait(main_thread_); + Mock::VerifyAndClearExpectations(&test_data_.mock_callback_); +} + +TEST_F(TextureLayerMailboxHolderTest, TwoCompositors_MainReleasedFirst) { + scoped_refptr<TextureLayer> test_layer = TextureLayer::CreateForMailbox(NULL); + ASSERT_TRUE(test_layer.get()); + + main_thread_.message_loop()->PostTask( + FROM_HERE, + base::Bind(&TextureLayerMailboxHolderTest::CreateMainRef, + base::Unretained(this))); + + Wait(main_thread_); + + // The texture layer is attached to compositor1, and passes a reference to its + // impl tree. + scoped_ptr<SingleReleaseCallback> compositor1; + main_thread_.message_loop()->PostTask( + FROM_HERE, + base::Bind(&TextureLayerMailboxHolderTest::CreateImplRef, + base::Unretained(this), + &compositor1)); + + // Then the texture layer is removed and attached to compositor2, and passes a + // reference to its impl tree. + scoped_ptr<SingleReleaseCallback> compositor2; + main_thread_.message_loop()->PostTask( + FROM_HERE, + base::Bind(&TextureLayerMailboxHolderTest::CreateImplRef, + base::Unretained(this), + &compositor2)); + + Wait(main_thread_); + Mock::VerifyAndClearExpectations(&test_data_.mock_callback_); + + // The main thread reference is destroyed first. + main_thread_.message_loop()->PostTask( + FROM_HERE, + base::Bind(&TextureLayerMailboxHolderTest::ReleaseMainRef, + base::Unretained(this))); + + // One compositor destroys their impl tree. + compositor2->Run(200, false); + + Wait(main_thread_); + + EXPECT_CALL(test_data_.mock_callback_, Release(_, _, _)).Times(0); + Mock::VerifyAndClearExpectations(&test_data_.mock_callback_); + + // The second impl reference is destroyed last, causing the mailbox to be + // released back to the embedder with the last sync point from the impl tree. + EXPECT_CALL(test_data_.mock_callback_, + Release(test_data_.mailbox_name1_, 100, true)).Times(1); + + compositor1->Run(100, true); + Wait(main_thread_); + Mock::VerifyAndClearExpectations(&test_data_.mock_callback_); +} + +TEST_F(TextureLayerMailboxHolderTest, TwoCompositors_SecondImplRefShortcut) { + scoped_refptr<TextureLayer> test_layer = TextureLayer::CreateForMailbox(NULL); + ASSERT_TRUE(test_layer.get()); + + main_thread_.message_loop()->PostTask( + FROM_HERE, + base::Bind(&TextureLayerMailboxHolderTest::CreateMainRef, + base::Unretained(this))); + + Wait(main_thread_); + + // The texture layer is attached to compositor1, and passes a reference to its + // impl tree. + scoped_ptr<SingleReleaseCallback> compositor1; + main_thread_.message_loop()->PostTask( + FROM_HERE, + base::Bind(&TextureLayerMailboxHolderTest::CreateImplRef, + base::Unretained(this), + &compositor1)); + + // Then the texture layer is removed and attached to compositor2, and passes a + // reference to its impl tree. + scoped_ptr<SingleReleaseCallback> compositor2; + main_thread_.message_loop()->PostTask( + FROM_HERE, + base::Bind(&TextureLayerMailboxHolderTest::CreateImplRef, + base::Unretained(this), + &compositor2)); + + Wait(main_thread_); + Mock::VerifyAndClearExpectations(&test_data_.mock_callback_); + + // The main thread reference is destroyed first. + main_thread_.message_loop()->PostTask( + FROM_HERE, + base::Bind(&TextureLayerMailboxHolderTest::ReleaseMainRef, + base::Unretained(this))); + + EXPECT_CALL(test_data_.mock_callback_, + Release(test_data_.mailbox_name1_, 200, true)).Times(1); + + bool manual_reset = false; + bool initially_signaled = false; + base::WaitableEvent begin_capture(manual_reset, initially_signaled); + base::WaitableEvent wait_for_capture(manual_reset, initially_signaled); + base::WaitableEvent stop_capture(manual_reset, initially_signaled); + + // Post a task to start capturing tasks on the main thread. This will block + // the main thread until we signal the |stop_capture| event. + main_thread_.message_loop()->PostTask( + FROM_HERE, + base::Bind(&TextureLayerMailboxHolderTest::CapturePostTasksAndWait, + base::Unretained(this), + &begin_capture, + &wait_for_capture, + &stop_capture)); + + // Before the main thread capturing starts, one compositor destroys their + // impl reference. Since capturing did not start, this gets post-tasked to + // the main thread. + compositor1->Run(100, false); + + // Start capturing on the main thread. + begin_capture.Signal(); + wait_for_capture.Wait(); + + // Meanwhile, the second compositor released its impl reference, but this task + // gets shortcutted directly to the main thread. This means the reference is + // released before compositor1, whose reference will be released later when + // the post-task is serviced. But since it was destroyed _on the impl thread_ + // last, its sync point values should be used. + compositor2->Run(200, true); + + stop_capture.Signal(); + Wait(main_thread_); + + Mock::VerifyAndClearExpectations(&test_data_.mock_callback_); } class TextureLayerImplWithMailboxThreadedCallback : public LayerTreeTest { @@ -432,21 +760,24 @@ class TextureLayerImplWithMailboxThreadedCallback : public LayerTreeTest { // Make sure callback is received on main and doesn't block the impl thread. void ReleaseCallback(unsigned sync_point, bool lost_resource) { - EXPECT_EQ(true, proxy()->IsMainThread()); + EXPECT_EQ(true, main_thread_.CalledOnValidThread()); EXPECT_FALSE(lost_resource); ++callback_count_; } void SetMailbox(char mailbox_char) { - TextureMailbox mailbox( - std::string(64, mailbox_char), + EXPECT_EQ(true, main_thread_.CalledOnValidThread()); + TextureMailbox mailbox(std::string(64, mailbox_char)); + scoped_ptr<SingleReleaseCallback> callback = SingleReleaseCallback::Create( base::Bind( &TextureLayerImplWithMailboxThreadedCallback::ReleaseCallback, base::Unretained(this))); - layer_->SetTextureMailbox(mailbox); + layer_->SetTextureMailbox(mailbox, callback.Pass()); } virtual void BeginTest() OVERRIDE { + EXPECT_EQ(true, main_thread_.CalledOnValidThread()); + gfx::Size bounds(100, 100); root_ = Layer::Create(); root_->SetAnchorPoint(gfx::PointF()); @@ -481,61 +812,58 @@ class TextureLayerImplWithMailboxThreadedCallback : public LayerTreeTest { EXPECT_EQ(1, callback_count_); break; case 2: - // Old mailbox was released, task was posted, but won't execute - // until this DidCommit returns. - // TODO(piman): fix this. - EXPECT_EQ(1, callback_count_); - layer_tree_host()->SetNeedsCommit(); - break; - case 3: EXPECT_EQ(2, callback_count_); // Case #3: change mailbox when the layer doesn't draw. The old // mailbox should be released during the next commit. layer_->SetBounds(gfx::Size()); SetMailbox('4'); break; - case 4: - // Old mailbox was released, task was posted, but won't execute - // until this DidCommit returns. - // TODO(piman): fix this. - EXPECT_EQ(2, callback_count_); - layer_tree_host()->SetNeedsCommit(); - break; - case 5: + case 3: EXPECT_EQ(3, callback_count_); // Case #4: release mailbox that was committed but never drawn. The // old mailbox should be released during the next commit. - layer_->SetTextureMailbox(TextureMailbox()); - break; - case 6: - // Old mailbox was released, task was posted, but won't execute - // until this DidCommit returns. - // TODO(piman): fix this. - EXPECT_EQ(3, callback_count_); - layer_tree_host()->SetNeedsCommit(); + layer_->SetTextureMailbox(TextureMailbox(), + scoped_ptr<SingleReleaseCallback>()); break; - case 7: + case 4: + if (layer_tree_host()->settings().impl_side_painting) { + // With impl painting, the texture mailbox will still be on the impl + // thread when the commit finishes, because the layer is not drawble + // when it has no texture mailbox, and thus does not block the commit + // on activation. So, we wait for activation. + // TODO(danakj): fix this. crbug.com/277953 + layer_tree_host()->SetNeedsCommit(); + break; + } else { + ++commit_count_; + } + case 5: EXPECT_EQ(4, callback_count_); // Restore a mailbox for the next step. SetMailbox('5'); break; - case 8: + case 6: // Case #5: remove layer from tree. Callback should *not* be called, the // mailbox is returned to the main thread. EXPECT_EQ(4, callback_count_); layer_->RemoveFromParent(); break; - case 9: - // Mailbox was released to the main thread, task was posted, but won't - // execute until this DidCommit returns. - // TODO(piman): fix this. - EXPECT_EQ(4, callback_count_); - layer_tree_host()->SetNeedsCommit(); - break; - case 10: + case 7: + if (layer_tree_host()->settings().impl_side_painting) { + // With impl painting, the texture mailbox will still be on the impl + // thread when the commit finishes, because the layer is not around to + // block the commit on activation anymore. So, we wait for activation. + // TODO(danakj): fix this. crbug.com/277953 + layer_tree_host()->SetNeedsCommit(); + break; + } else { + ++commit_count_; + } + case 8: EXPECT_EQ(4, callback_count_); // Resetting the mailbox will call the callback now. - layer_->SetTextureMailbox(TextureMailbox()); + layer_->SetTextureMailbox(TextureMailbox(), + scoped_ptr<SingleReleaseCallback>()); EXPECT_EQ(5, callback_count_); EndTest(); break; @@ -548,6 +876,7 @@ class TextureLayerImplWithMailboxThreadedCallback : public LayerTreeTest { virtual void AfterTest() OVERRIDE {} private: + base::ThreadChecker main_thread_; int callback_count_; int commit_count_; scoped_refptr<Layer> root_; @@ -557,6 +886,247 @@ class TextureLayerImplWithMailboxThreadedCallback : public LayerTreeTest { SINGLE_AND_MULTI_THREAD_DIRECT_RENDERER_TEST_F( TextureLayerImplWithMailboxThreadedCallback); + +class TextureLayerNoMailboxIsActivatedDuringCommit : public LayerTreeTest, + public TextureLayerClient { + protected: + TextureLayerNoMailboxIsActivatedDuringCommit() + : wait_thread_("WAIT"), + wait_event_(false, false) { + wait_thread_.Start(); + } + + virtual void BeginTest() OVERRIDE { + activate_count_ = 0; + + gfx::Size bounds(100, 100); + root_ = Layer::Create(); + root_->SetAnchorPoint(gfx::PointF()); + root_->SetBounds(bounds); + + layer_ = TextureLayer::Create(this); + layer_->SetIsDrawable(true); + layer_->SetAnchorPoint(gfx::PointF()); + layer_->SetBounds(bounds); + + root_->AddChild(layer_); + layer_tree_host()->SetRootLayer(root_); + layer_tree_host()->SetViewportSize(bounds); + + PostSetNeedsCommitToMainThread(); + } + + // TextureLayerClient implementation. + virtual unsigned PrepareTexture() OVERRIDE { + return OffscreenContextProviderForMainThread() + ->Context3d()->createTexture(); + } + virtual WebKit::WebGraphicsContext3D* Context3d() OVERRIDE { + return OffscreenContextProviderForMainThread()->Context3d(); + } + virtual bool PrepareTextureMailbox( + TextureMailbox* mailbox, + scoped_ptr<SingleReleaseCallback>* release_callback, + bool use_shared_memory) OVERRIDE { + return false; + } + + 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(&TextureLayerNoMailboxIsActivatedDuringCommit::DidActivate, + base::Unretained(this))); + } + + void DidActivate() { + base::AutoLock lock(activate_lock_); + switch (activate_count_) { + case 1: + // The first texture has been activated. Invalidate the layer so it + // grabs a new texture id from the client. + layer_->SetNeedsDisplay(); + // So this commit number should complete after the second activate. + EXPECT_EQ(1, layer_tree_host()->source_frame_number()); + break; + case 2: + // The second mailbox 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. + layer_->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 texture 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_; + int activate_commit_; + scoped_refptr<Layer> root_; + scoped_refptr<TextureLayer> layer_; +}; + +SINGLE_AND_MULTI_THREAD_DIRECT_RENDERER_TEST_F( + TextureLayerNoMailboxIsActivatedDuringCommit); + +class TextureLayerMailboxIsActivatedDuringCommit : public LayerTreeTest { + protected: + TextureLayerMailboxIsActivatedDuringCommit() + : wait_thread_("WAIT"), + wait_event_(false, false) { + wait_thread_.Start(); + } + + static void ReleaseCallback(unsigned sync_point, bool lost_resource) {} + + void SetMailbox(char mailbox_char) { + TextureMailbox mailbox(std::string(64, mailbox_char)); + scoped_ptr<SingleReleaseCallback> callback = SingleReleaseCallback::Create( + base::Bind( + &TextureLayerMailboxIsActivatedDuringCommit::ReleaseCallback)); + layer_->SetTextureMailbox(mailbox, callback.Pass()); + } + + virtual void BeginTest() OVERRIDE { + activate_count_ = 0; + + gfx::Size bounds(100, 100); + root_ = Layer::Create(); + root_->SetAnchorPoint(gfx::PointF()); + root_->SetBounds(bounds); + + layer_ = TextureLayer::CreateForMailbox(NULL); + layer_->SetIsDrawable(true); + layer_->SetAnchorPoint(gfx::PointF()); + layer_->SetBounds(bounds); + + root_->AddChild(layer_); + layer_tree_host()->SetRootLayer(root_); + layer_tree_host()->SetViewportSize(bounds); + SetMailbox('1'); + + 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(&TextureLayerMailboxIsActivatedDuringCommit::DidActivate, + base::Unretained(this))); + } + + void DidActivate() { + base::AutoLock lock(activate_lock_); + switch (activate_count_) { + case 1: + // The first mailbox has been activated. Set a new mailbox, and + // expect the next commit to finish *after* it is activated. + SetMailbox('2'); + // So this commit number should complete after the second activate. + EXPECT_EQ(1, layer_tree_host()->source_frame_number()); + break; + case 2: + // The second mailbox 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. + layer_->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 mailbox 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_; + scoped_refptr<Layer> root_; + scoped_refptr<TextureLayer> layer_; +}; + +SINGLE_AND_MULTI_THREAD_DIRECT_RENDERER_TEST_F( + TextureLayerMailboxIsActivatedDuringCommit); + class TextureLayerImplWithMailboxTest : public TextureLayerTest { protected: TextureLayerImplWithMailboxTest() @@ -596,14 +1166,17 @@ TEST_F(TextureLayerImplWithMailboxTest, TestWillDraw) { { scoped_ptr<TextureLayerImpl> impl_layer = TextureLayerImpl::Create(host_impl_.active_tree(), 1, true); - impl_layer->SetTextureMailbox(test_data_.mailbox1_); + impl_layer->SetTextureMailbox( + test_data_.mailbox1_, + SingleReleaseCallback::Create(test_data_.release_mailbox1_)); EXPECT_TRUE(WillDraw(impl_layer.get(), DRAW_MODE_HARDWARE)); } { scoped_ptr<TextureLayerImpl> impl_layer = TextureLayerImpl::Create(host_impl_.active_tree(), 1, true); - impl_layer->SetTextureMailbox(TextureMailbox()); + impl_layer->SetTextureMailbox(TextureMailbox(), + scoped_ptr<SingleReleaseCallback>()); EXPECT_FALSE(WillDraw(impl_layer.get(), DRAW_MODE_HARDWARE)); } @@ -611,15 +1184,19 @@ TEST_F(TextureLayerImplWithMailboxTest, TestWillDraw) { // Software resource. scoped_ptr<TextureLayerImpl> impl_layer = TextureLayerImpl::Create(host_impl_.active_tree(), 1, true); - impl_layer->SetTextureMailbox(test_data_.mailbox3_); + impl_layer->SetTextureMailbox( + test_data_.mailbox3_, + SingleReleaseCallback::Create(test_data_.release_mailbox3_)); EXPECT_TRUE(WillDraw(impl_layer.get(), DRAW_MODE_HARDWARE)); } { scoped_ptr<TextureLayerImpl> impl_layer = TextureLayerImpl::Create(host_impl_.active_tree(), 1, false); + ContextProvider* context_provider = + host_impl_.output_surface()->context_provider(); unsigned texture = - host_impl_.output_surface()->context3d()->createTexture(); + context_provider->Context3d()->createTexture(); impl_layer->set_texture_id(texture); EXPECT_TRUE(WillDraw(impl_layer.get(), DRAW_MODE_HARDWARE)); } @@ -635,14 +1212,17 @@ TEST_F(TextureLayerImplWithMailboxTest, TestWillDraw) { { scoped_ptr<TextureLayerImpl> impl_layer = TextureLayerImpl::Create(host_impl_.active_tree(), 1, true); - impl_layer->SetTextureMailbox(test_data_.mailbox1_); + impl_layer->SetTextureMailbox( + test_data_.mailbox1_, + SingleReleaseCallback::Create(test_data_.release_mailbox1_)); EXPECT_FALSE(WillDraw(impl_layer.get(), DRAW_MODE_SOFTWARE)); } { scoped_ptr<TextureLayerImpl> impl_layer = TextureLayerImpl::Create(host_impl_.active_tree(), 1, true); - impl_layer->SetTextureMailbox(TextureMailbox()); + impl_layer->SetTextureMailbox(TextureMailbox(), + scoped_ptr<SingleReleaseCallback>()); EXPECT_FALSE(WillDraw(impl_layer.get(), DRAW_MODE_SOFTWARE)); } @@ -650,15 +1230,19 @@ TEST_F(TextureLayerImplWithMailboxTest, TestWillDraw) { // Software resource. scoped_ptr<TextureLayerImpl> impl_layer = TextureLayerImpl::Create(host_impl_.active_tree(), 1, true); - impl_layer->SetTextureMailbox(test_data_.mailbox3_); + impl_layer->SetTextureMailbox( + test_data_.mailbox3_, + SingleReleaseCallback::Create(test_data_.release_mailbox3_)); EXPECT_TRUE(WillDraw(impl_layer.get(), DRAW_MODE_SOFTWARE)); } { scoped_ptr<TextureLayerImpl> impl_layer = TextureLayerImpl::Create(host_impl_.active_tree(), 1, false); + ContextProvider* context_provider = + host_impl_.output_surface()->context_provider(); unsigned texture = - host_impl_.output_surface()->context3d()->createTexture(); + context_provider->Context3d()->createTexture(); impl_layer->set_texture_id(texture); EXPECT_FALSE(WillDraw(impl_layer.get(), DRAW_MODE_SOFTWARE)); } @@ -674,15 +1258,19 @@ TEST_F(TextureLayerImplWithMailboxTest, TestWillDraw) { { scoped_ptr<TextureLayerImpl> impl_layer = TextureLayerImpl::Create(host_impl_.active_tree(), 1, true); - impl_layer->SetTextureMailbox(test_data_.mailbox1_); + impl_layer->SetTextureMailbox( + test_data_.mailbox1_, + SingleReleaseCallback::Create(test_data_.release_mailbox1_)); EXPECT_FALSE(WillDraw(impl_layer.get(), DRAW_MODE_RESOURCELESS_SOFTWARE)); } { scoped_ptr<TextureLayerImpl> impl_layer = TextureLayerImpl::Create(host_impl_.active_tree(), 1, false); + ContextProvider* context_provider = + host_impl_.output_surface()->context_provider(); unsigned texture = - host_impl_.output_surface()->context3d()->createTexture(); + context_provider->Context3d()->createTexture(); impl_layer->set_texture_id(texture); EXPECT_FALSE(WillDraw(impl_layer.get(), DRAW_MODE_RESOURCELESS_SOFTWARE)); } @@ -698,7 +1286,9 @@ TEST_F(TextureLayerImplWithMailboxTest, TestImplLayerCallbacks) { pending_layer->CreateLayerImpl(host_impl_.active_tree())); ASSERT_TRUE(active_layer); - pending_layer->SetTextureMailbox(test_data_.mailbox1_); + pending_layer->SetTextureMailbox( + test_data_.mailbox1_, + SingleReleaseCallback::Create(test_data_.release_mailbox1_)); // Test multiple commits without an activation. EXPECT_CALL(test_data_.mock_callback_, @@ -706,7 +1296,9 @@ TEST_F(TextureLayerImplWithMailboxTest, TestImplLayerCallbacks) { test_data_.sync_point1_, false)) .Times(1); - pending_layer->SetTextureMailbox(test_data_.mailbox2_); + pending_layer->SetTextureMailbox( + test_data_.mailbox2_, + SingleReleaseCallback::Create(test_data_.release_mailbox2_)); Mock::VerifyAndClearExpectations(&test_data_.mock_callback_); // Test callback after activation. @@ -714,7 +1306,9 @@ TEST_F(TextureLayerImplWithMailboxTest, TestImplLayerCallbacks) { active_layer->DidBecomeActive(); EXPECT_CALL(test_data_.mock_callback_, Release(_, _, _)).Times(0); - pending_layer->SetTextureMailbox(test_data_.mailbox1_); + pending_layer->SetTextureMailbox( + test_data_.mailbox1_, + SingleReleaseCallback::Create(test_data_.release_mailbox1_)); Mock::VerifyAndClearExpectations(&test_data_.mock_callback_); EXPECT_CALL(test_data_.mock_callback_, @@ -728,7 +1322,8 @@ TEST_F(TextureLayerImplWithMailboxTest, TestImplLayerCallbacks) { EXPECT_CALL(test_data_.mock_callback_, Release(test_data_.mailbox_name1_, _, false)) .Times(1); - pending_layer->SetTextureMailbox(TextureMailbox()); + pending_layer->SetTextureMailbox(TextureMailbox(), + scoped_ptr<SingleReleaseCallback>()); pending_layer->PushPropertiesTo(active_layer.get()); active_layer->DidBecomeActive(); Mock::VerifyAndClearExpectations(&test_data_.mock_callback_); @@ -739,7 +1334,9 @@ TEST_F(TextureLayerImplWithMailboxTest, TestImplLayerCallbacks) { test_data_.sync_point1_, false)) .Times(1); - pending_layer->SetTextureMailbox(test_data_.mailbox1_); + pending_layer->SetTextureMailbox( + test_data_.mailbox1_, + SingleReleaseCallback::Create(test_data_.release_mailbox1_)); } TEST_F(TextureLayerImplWithMailboxTest, @@ -751,18 +1348,23 @@ TEST_F(TextureLayerImplWithMailboxTest, EXPECT_CALL(test_data_.mock_callback_, Release(test_data_.mailbox_name1_, _, false)) .Times(1); - impl_layer->SetTextureMailbox(test_data_.mailbox1_); + impl_layer->SetTextureMailbox( + test_data_.mailbox1_, + SingleReleaseCallback::Create(test_data_.release_mailbox1_)); impl_layer->DidBecomeActive(); EXPECT_TRUE(impl_layer->WillDraw( DRAW_MODE_HARDWARE, host_impl_.active_tree()->resource_provider())); impl_layer->DidDraw(host_impl_.active_tree()->resource_provider()); - impl_layer->SetTextureMailbox(TextureMailbox()); + impl_layer->SetTextureMailbox(TextureMailbox(), + scoped_ptr<SingleReleaseCallback>()); } TEST_F(TextureLayerImplWithMailboxTest, TestCallbackOnInUseResource) { ResourceProvider* provider = host_impl_.active_tree()->resource_provider(); ResourceProvider::ResourceId id = - provider->CreateResourceFromTextureMailbox(test_data_.mailbox1_); + provider->CreateResourceFromTextureMailbox( + test_data_.mailbox1_, + SingleReleaseCallback::Create(test_data_.release_mailbox1_)); provider->AllocateForTesting(id); // Transfer some resources to the parent. @@ -777,7 +1379,9 @@ TEST_F(TextureLayerImplWithMailboxTest, TestCallbackOnInUseResource) { EXPECT_CALL(test_data_.mock_callback_, Release(test_data_.mailbox_name1_, _, false)) .Times(1); - provider->ReceiveFromParent(list); + ReturnedResourceArray returned; + TransferableResource::ReturnResources(list, &returned); + provider->ReceiveReturnsFromParent(returned); } // Check that ClearClient correctly clears the state so that the impl side @@ -799,8 +1403,7 @@ class TextureLayerClientTest TestWebGraphicsContext3D::Create()); context_ = context.get(); texture_ = context->createTexture(); - return FakeOutputSurface::Create3d( - context.PassAs<WebKit::WebGraphicsContext3D>()).PassAs<OutputSurface>(); + return FakeOutputSurface::Create3d(context.Pass()).PassAs<OutputSurface>(); } virtual unsigned PrepareTexture() OVERRIDE { @@ -812,7 +1415,9 @@ class TextureLayerClientTest } virtual bool PrepareTextureMailbox( - cc::TextureMailbox* mailbox, bool use_shared_memory) OVERRIDE { + TextureMailbox* mailbox, + scoped_ptr<SingleReleaseCallback>* release_callback, + bool use_shared_memory) OVERRIDE { return false; } @@ -899,6 +1504,343 @@ class TextureLayerClientTest // renderer. SINGLE_AND_MULTI_THREAD_DIRECT_RENDERER_TEST_F(TextureLayerClientTest); + +// Checks that changing a texture in the client for a TextureLayer that's +// invisible correctly works without drawing a deleted texture. See +// crbug.com/266628 +class TextureLayerChangeInvisibleTest + : public LayerTreeTest, + public TextureLayerClient { + public: + TextureLayerChangeInvisibleTest() + : client_context_(TestWebGraphicsContext3D::Create()), + texture_(client_context_->createTexture()), + texture_to_delete_on_next_commit_(0), + prepare_called_(0), + commit_count_(0), + expected_texture_on_draw_(0) {} + + // TextureLayerClient implementation. + virtual unsigned PrepareTexture() OVERRIDE { + ++prepare_called_; + return texture_; + } + + // TextureLayerClient implementation. + virtual WebKit::WebGraphicsContext3D* Context3d() OVERRIDE { + return client_context_.get(); + } + + // TextureLayerClient implementation. + virtual bool PrepareTextureMailbox( + cc::TextureMailbox* mailbox, + scoped_ptr<SingleReleaseCallback>* release_callback, + bool use_shared_memory) OVERRIDE { + return false; + } + + virtual void SetupTree() OVERRIDE { + scoped_refptr<Layer> root = Layer::Create(); + root->SetBounds(gfx::Size(10, 10)); + root->SetAnchorPoint(gfx::PointF()); + root->SetIsDrawable(true); + + solid_layer_ = SolidColorLayer::Create(); + solid_layer_->SetBounds(gfx::Size(10, 10)); + solid_layer_->SetIsDrawable(true); + solid_layer_->SetBackgroundColor(SK_ColorWHITE); + root->AddChild(solid_layer_); + + parent_layer_ = Layer::Create(); + parent_layer_->SetBounds(gfx::Size(10, 10)); + parent_layer_->SetIsDrawable(true); + root->AddChild(parent_layer_); + + texture_layer_ = TextureLayer::Create(this); + texture_layer_->SetBounds(gfx::Size(10, 10)); + texture_layer_->SetAnchorPoint(gfx::PointF()); + texture_layer_->SetIsDrawable(true); + parent_layer_->AddChild(texture_layer_); + + layer_tree_host()->SetRootLayer(root); + LayerTreeTest::SetupTree(); + } + + virtual void BeginTest() OVERRIDE { + PostSetNeedsCommitToMainThread(); + } + + virtual void DidCommitAndDrawFrame() OVERRIDE { + ++commit_count_; + switch (commit_count_) { + case 1: + // We should have updated the layer, committing the texture. + EXPECT_EQ(1, prepare_called_); + // Make layer invisible. + parent_layer_->SetOpacity(0.f); + break; + case 2: { + // Layer shouldn't have been updated. + EXPECT_EQ(1, prepare_called_); + // Change the texture. + texture_to_delete_on_next_commit_ = texture_; + texture_ = client_context_->createTexture(); + texture_layer_->SetNeedsDisplay(); + // Force a change to make sure we draw a frame. + solid_layer_->SetBackgroundColor(SK_ColorGRAY); + break; + } + case 3: + EXPECT_EQ(1, prepare_called_); + client_context_->deleteTexture(texture_to_delete_on_next_commit_); + texture_to_delete_on_next_commit_ = 0; + // Make layer visible again. + parent_layer_->SetOpacity(1.f); + break; + case 4: { + // Layer should have been updated. + EXPECT_EQ(2, prepare_called_); + texture_layer_->ClearClient(); + client_context_->deleteTexture(texture_); + texture_ = 0; + break; + } + case 5: + EndTest(); + break; + default: + NOTREACHED(); + break; + } + } + + virtual void BeginCommitOnThread(LayerTreeHostImpl* host_impl) OVERRIDE { + ASSERT_TRUE(proxy()->IsMainThreadBlocked()); + // This is the only texture that can be drawn this frame. + expected_texture_on_draw_ = texture_; + } + + virtual bool PrepareToDrawOnThread(LayerTreeHostImpl* host_impl, + LayerTreeHostImpl::FrameData* frame_data, + bool result) OVERRIDE { + ContextForImplThread(host_impl)->ResetUsedTextures(); + return true; + } + + virtual void SwapBuffersOnThread(LayerTreeHostImpl* host_impl, + bool result) OVERRIDE { + ASSERT_TRUE(result); + TestWebGraphicsContext3D* context = ContextForImplThread(host_impl); + int used_textures = context->NumUsedTextures(); + switch (host_impl->active_tree()->source_frame_number()) { + case 0: + EXPECT_EQ(1, used_textures); + EXPECT_TRUE(context->UsedTexture(expected_texture_on_draw_)); + break; + case 1: + case 2: + EXPECT_EQ(0, used_textures); + break; + case 3: + EXPECT_EQ(1, used_textures); + EXPECT_TRUE(context->UsedTexture(expected_texture_on_draw_)); + break; + default: + break; + } + } + + virtual void AfterTest() OVERRIDE {} + + private: + TestWebGraphicsContext3D* ContextForImplThread(LayerTreeHostImpl* host_impl) { + return static_cast<TestWebGraphicsContext3D*>( + host_impl->output_surface()->context_provider()->Context3d()); + } + + scoped_refptr<SolidColorLayer> solid_layer_; + scoped_refptr<Layer> parent_layer_; + scoped_refptr<TextureLayer> texture_layer_; + scoped_ptr<TestWebGraphicsContext3D> client_context_; + + // Used on the main thread, and on the impl thread while the main thread is + // blocked. + unsigned texture_; + + // Used on the main thread. + unsigned texture_to_delete_on_next_commit_; + int prepare_called_; + int commit_count_; + + // Used on the compositor thread. + unsigned expected_texture_on_draw_; +}; + +// The TextureLayerChangeInvisibleTest does not use mailboxes, so can't use a +// delegating renderer. +SINGLE_AND_MULTI_THREAD_DIRECT_RENDERER_TEST_F(TextureLayerChangeInvisibleTest); + +// Checks that changing a mailbox in the client for a TextureLayer that's +// invisible correctly works and uses the new mailbox as soon as the layer +// becomes visible (and returns the old one). +class TextureLayerChangeInvisibleMailboxTest + : public LayerTreeTest, + public TextureLayerClient { + public: + TextureLayerChangeInvisibleMailboxTest() + : mailbox_changed_(true), + mailbox_returned_(0), + prepare_called_(0), + commit_count_(0) { + mailbox_ = MakeMailbox('1'); + } + + // TextureLayerClient implementation. + virtual unsigned PrepareTexture() OVERRIDE { + NOTREACHED(); + return 0; + } + + // TextureLayerClient implementation. + virtual WebKit::WebGraphicsContext3D* Context3d() OVERRIDE { + NOTREACHED(); + return NULL; + } + + // TextureLayerClient implementation. + virtual bool PrepareTextureMailbox( + cc::TextureMailbox* mailbox, + scoped_ptr<SingleReleaseCallback>* release_callback, + bool use_shared_memory) OVERRIDE { + ++prepare_called_; + if (!mailbox_changed_) + return false; + *mailbox = mailbox_; + *release_callback = SingleReleaseCallback::Create( + base::Bind(&TextureLayerChangeInvisibleMailboxTest::MailboxReleased, + base::Unretained(this))); + return true; + } + + TextureMailbox MakeMailbox(char name) { + return TextureMailbox(std::string(64, name)); + } + + void MailboxReleased(unsigned sync_point, bool lost_resource) { + ++mailbox_returned_; + } + + virtual void SetupTree() OVERRIDE { + scoped_refptr<Layer> root = Layer::Create(); + root->SetBounds(gfx::Size(10, 10)); + root->SetAnchorPoint(gfx::PointF()); + root->SetIsDrawable(true); + + solid_layer_ = SolidColorLayer::Create(); + solid_layer_->SetBounds(gfx::Size(10, 10)); + solid_layer_->SetIsDrawable(true); + solid_layer_->SetBackgroundColor(SK_ColorWHITE); + root->AddChild(solid_layer_); + + parent_layer_ = Layer::Create(); + parent_layer_->SetBounds(gfx::Size(10, 10)); + parent_layer_->SetIsDrawable(true); + root->AddChild(parent_layer_); + + texture_layer_ = TextureLayer::CreateForMailbox(this); + texture_layer_->SetBounds(gfx::Size(10, 10)); + texture_layer_->SetAnchorPoint(gfx::PointF()); + texture_layer_->SetIsDrawable(true); + parent_layer_->AddChild(texture_layer_); + + layer_tree_host()->SetRootLayer(root); + LayerTreeTest::SetupTree(); + } + + virtual void BeginTest() OVERRIDE { + PostSetNeedsCommitToMainThread(); + } + + virtual void DidCommitAndDrawFrame() OVERRIDE { + ++commit_count_; + switch (commit_count_) { + case 1: + // We should have updated the layer, committing the texture. + EXPECT_EQ(1, prepare_called_); + // Make layer invisible. + parent_layer_->SetOpacity(0.f); + break; + case 2: + // Layer shouldn't have been updated. + EXPECT_EQ(1, prepare_called_); + // Change the texture. + mailbox_ = MakeMailbox('2'); + mailbox_changed_ = true; + texture_layer_->SetNeedsDisplay(); + // Force a change to make sure we draw a frame. + solid_layer_->SetBackgroundColor(SK_ColorGRAY); + break; + case 3: + // Layer shouldn't have been updated. + EXPECT_EQ(1, prepare_called_); + // So the old mailbox isn't returned yet. + EXPECT_EQ(0, mailbox_returned_); + // Make layer visible again. + parent_layer_->SetOpacity(1.f); + break; + case 4: + // Layer should have been updated. + EXPECT_EQ(2, prepare_called_); + // So the old mailbox should have been returned already. + EXPECT_EQ(1, mailbox_returned_); + texture_layer_->ClearClient(); + break; + case 5: + EXPECT_EQ(2, mailbox_returned_); + EndTest(); + break; + default: + NOTREACHED(); + break; + } + } + + virtual void SwapBuffersOnThread(LayerTreeHostImpl* host_impl, + bool result) OVERRIDE { + ASSERT_TRUE(result); + DelegatedFrameData* delegated_frame_data = + output_surface()->last_sent_frame().delegated_frame_data.get(); + if (!delegated_frame_data) + return; + + // Return all resources immediately. + TransferableResourceArray resources_to_return = + output_surface()->resources_held_by_parent(); + + CompositorFrameAck ack; + for (size_t i = 0; i < resources_to_return.size(); ++i) + output_surface()->ReturnResource(resources_to_return[i].id, &ack); + host_impl->ReclaimResources(&ack); + host_impl->OnSwapBuffersComplete(); + } + + virtual void AfterTest() OVERRIDE {} + + private: + scoped_refptr<SolidColorLayer> solid_layer_; + scoped_refptr<Layer> parent_layer_; + scoped_refptr<TextureLayer> texture_layer_; + + // Used on the main thread. + bool mailbox_changed_; + TextureMailbox mailbox_; + int mailbox_returned_; + int prepare_called_; + int commit_count_; +}; + +SINGLE_AND_MULTI_THREAD_TEST_F(TextureLayerChangeInvisibleMailboxTest); + // Test recovering from a lost context. class TextureLayerLostContextTest : public LayerTreeTest, @@ -928,7 +1870,9 @@ class TextureLayerLostContextTest } virtual bool PrepareTextureMailbox( - cc::TextureMailbox* mailbox, bool use_shared_memory) OVERRIDE { + TextureMailbox* mailbox, + scoped_ptr<SingleReleaseCallback>* release_callback, + bool use_shared_memory) OVERRIDE { return false; } @@ -981,19 +1925,20 @@ SINGLE_AND_MULTI_THREAD_DIRECT_RENDERER_TEST_F(TextureLayerLostContextTest); class TextureLayerWithMailboxMainThreadDeleted : public LayerTreeTest { public: void ReleaseCallback(unsigned sync_point, bool lost_resource) { - EXPECT_EQ(true, proxy()->IsMainThread()); + EXPECT_EQ(true, main_thread_.CalledOnValidThread()); EXPECT_FALSE(lost_resource); ++callback_count_; EndTest(); } void SetMailbox(char mailbox_char) { - TextureMailbox mailbox( - std::string(64, mailbox_char), + EXPECT_EQ(true, main_thread_.CalledOnValidThread()); + TextureMailbox mailbox(std::string(64, mailbox_char)); + scoped_ptr<SingleReleaseCallback> callback = SingleReleaseCallback::Create( base::Bind( &TextureLayerWithMailboxMainThreadDeleted::ReleaseCallback, base::Unretained(this))); - layer_->SetTextureMailbox(mailbox); + layer_->SetTextureMailbox(mailbox, callback.Pass()); } virtual void SetupTree() OVERRIDE { @@ -1013,6 +1958,8 @@ class TextureLayerWithMailboxMainThreadDeleted : public LayerTreeTest { } virtual void BeginTest() OVERRIDE { + EXPECT_EQ(true, main_thread_.CalledOnValidThread()); + callback_count_ = 0; // Set the mailbox on the main thread. @@ -1038,6 +1985,7 @@ class TextureLayerWithMailboxMainThreadDeleted : public LayerTreeTest { } private: + base::ThreadChecker main_thread_; int callback_count_; scoped_refptr<Layer> root_; scoped_refptr<TextureLayer> layer_; @@ -1049,19 +1997,20 @@ SINGLE_AND_MULTI_THREAD_DIRECT_RENDERER_TEST_F( class TextureLayerWithMailboxImplThreadDeleted : public LayerTreeTest { public: void ReleaseCallback(unsigned sync_point, bool lost_resource) { - EXPECT_EQ(true, proxy()->IsMainThread()); + EXPECT_EQ(true, main_thread_.CalledOnValidThread()); EXPECT_FALSE(lost_resource); ++callback_count_; EndTest(); } void SetMailbox(char mailbox_char) { - TextureMailbox mailbox( - std::string(64, mailbox_char), + EXPECT_EQ(true, main_thread_.CalledOnValidThread()); + TextureMailbox mailbox(std::string(64, mailbox_char)); + scoped_ptr<SingleReleaseCallback> callback = SingleReleaseCallback::Create( base::Bind( &TextureLayerWithMailboxImplThreadDeleted::ReleaseCallback, base::Unretained(this))); - layer_->SetTextureMailbox(mailbox); + layer_->SetTextureMailbox(mailbox, callback.Pass()); } virtual void SetupTree() OVERRIDE { @@ -1081,6 +2030,8 @@ class TextureLayerWithMailboxImplThreadDeleted : public LayerTreeTest { } virtual void BeginTest() OVERRIDE { + EXPECT_EQ(true, main_thread_.CalledOnValidThread()); + callback_count_ = 0; // Set the mailbox on the main thread. @@ -1109,6 +2060,7 @@ class TextureLayerWithMailboxImplThreadDeleted : public LayerTreeTest { } private: + base::ThreadChecker main_thread_; int callback_count_; scoped_refptr<Layer> root_; scoped_refptr<TextureLayer> layer_; diff --git a/chromium/cc/layers/tiled_layer.cc b/chromium/cc/layers/tiled_layer.cc index bac12f0d422..8bbf2271bc4 100644 --- a/chromium/cc/layers/tiled_layer.cc +++ b/chromium/cc/layers/tiled_layer.cc @@ -86,7 +86,7 @@ class UpdatableTile : public LayerTilingData::Tile { TiledLayer::TiledLayer() : ContentsScalingLayer(), - texture_format_(GL_INVALID_ENUM), + texture_format_(RGBA_8888), skips_draw_(false), failed_update_(false), tiling_option_(AUTO_TILE) { @@ -235,9 +235,7 @@ void TiledLayer::PushPropertiesTo(LayerImpl* layer) { needs_push_properties_ = true; } -bool TiledLayer::BlocksPendingCommit() const { return true; } - -PrioritizedResourceManager* TiledLayer::ResourceManager() const { +PrioritizedResourceManager* TiledLayer::ResourceManager() { if (!layer_tree_host()) return NULL; return layer_tree_host()->contents_texture_manager(); @@ -731,6 +729,10 @@ bool TiledLayer::Update(ResourceUpdateQueue* queue, const OcclusionTracker* occlusion) { DCHECK(!skips_draw_ && !failed_update_); // Did ResetUpdateState get skipped? + // Tiled layer always causes commits to wait for activation, as it does + // not support pending trees. + SetNextCommitWaitsForActivation(); + bool updated = false; { diff --git a/chromium/cc/layers/tiled_layer.h b/chromium/cc/layers/tiled_layer.h index 8ad7df95e38..3870d69956b 100644 --- a/chromium/cc/layers/tiled_layer.h +++ b/chromium/cc/layers/tiled_layer.h @@ -8,6 +8,7 @@ #include "cc/base/cc_export.h" #include "cc/layers/contents_scaling_layer.h" #include "cc/resources/layer_tiling_data.h" +#include "cc/resources/resource_format.h" namespace cc { class LayerUpdater; @@ -26,7 +27,6 @@ class CC_EXPORT TiledLayer : public ContentsScalingLayer { // Layer implementation. virtual void SetIsMask(bool is_mask) OVERRIDE; virtual void PushPropertiesTo(LayerImpl* layer) OVERRIDE; - virtual bool BlocksPendingCommit() const OVERRIDE; virtual bool DrawsContent() const OVERRIDE; virtual void ReduceMemoryUsage() OVERRIDE; virtual void SetNeedsDisplayRect(const gfx::RectF& dirty_rect) OVERRIDE; @@ -46,7 +46,7 @@ class CC_EXPORT TiledLayer : public ContentsScalingLayer { // Exposed to subclasses for testing. void SetTileSize(gfx::Size size); - void SetTextureFormat(unsigned texture_format) { + void SetTextureFormat(ResourceFormat texture_format) { texture_format_ = texture_format; } void SetBorderTexelOption(LayerTilingData::BorderTexelOption option); @@ -68,7 +68,7 @@ class CC_EXPORT TiledLayer : public ContentsScalingLayer { bool SkipsDraw() const { return skips_draw_; } // Virtual for testing - virtual PrioritizedResourceManager* ResourceManager() const; + virtual PrioritizedResourceManager* ResourceManager(); const LayerTilingData* TilerForTesting() const { return tiler_.get(); } const PrioritizedResource* ResourceAtForTesting(int i, int j) const; @@ -121,7 +121,7 @@ class CC_EXPORT TiledLayer : public ContentsScalingLayer { bool IsSmallAnimatedLayer() const; - unsigned texture_format_; + ResourceFormat texture_format_; bool skips_draw_; bool failed_update_; diff --git a/chromium/cc/layers/tiled_layer_unittest.cc b/chromium/cc/layers/tiled_layer_unittest.cc index a2f0ca7a64d..b6da0769f95 100644 --- a/chromium/cc/layers/tiled_layer_unittest.cc +++ b/chromium/cc/layers/tiled_layer_unittest.cc @@ -16,6 +16,7 @@ #include "cc/test/fake_layer_tree_host_client.h" #include "cc/test/fake_layer_tree_host_impl.h" #include "cc/test/fake_output_surface.h" +#include "cc/test/fake_output_surface_client.h" #include "cc/test/fake_proxy.h" #include "cc/test/fake_rendering_stats_instrumentation.h" #include "cc/test/geometry_test_utils.h" @@ -47,7 +48,7 @@ class TiledLayerTest : public testing::Test { public: TiledLayerTest() : proxy_(NULL), - output_surface_(CreateFakeOutputSurface()), + output_surface_(FakeOutputSurface::Create3d()), queue_(make_scoped_ptr(new ResourceUpdateQueue)), fake_layer_impl_tree_host_client_(FakeLayerTreeHostClient::DIRECT_3D), occlusion_(NULL) { @@ -64,9 +65,12 @@ class TiledLayerTest : public testing::Test { layer_tree_host_->InitializeOutputSurfaceIfNeeded(); layer_tree_host_->SetRootLayer(Layer::Create()); + CHECK(output_surface_->BindToClient(&output_surface_client_)); + DebugScopedSetImplThreadAndMainThreadBlocked - impl_thread_and_main_thread_blocked(proxy_); - resource_provider_ = ResourceProvider::Create(output_surface_.get(), 0); + impl_thread_and_main_thread_blocked(proxy_); + resource_provider_ = + ResourceProvider::Create(output_surface_.get(), 0, false); host_impl_ = make_scoped_ptr(new FakeLayerTreeHostImpl(proxy_)); } @@ -183,6 +187,7 @@ class TiledLayerTest : public testing::Test { public: Proxy* proxy_; LayerTreeSettings settings_; + FakeOutputSurfaceClient output_surface_client_; scoped_ptr<OutputSurface> output_surface_; scoped_ptr<ResourceProvider> resource_provider_; scoped_ptr<ResourceUpdateQueue> queue_; @@ -951,8 +956,10 @@ TEST_F(TiledLayerTest, SkipsDrawGetsReset) { layer_tree_host_->SetRootLayer(root_layer); layer_tree_host_->SetViewportSize(gfx::Size(300, 300)); + layer_tree_host_->contents_texture_manager()->SetMaxMemoryLimitBytes( + memory_limit); - layer_tree_host_->UpdateLayers(queue_.get(), memory_limit); + layer_tree_host_->UpdateLayers(queue_.get()); // We'll skip the root layer. EXPECT_TRUE(root_layer->SkipsDraw()); @@ -963,7 +970,7 @@ TEST_F(TiledLayerTest, SkipsDrawGetsReset) { // Remove the child layer. root_layer->RemoveAllChildren(); - layer_tree_host_->UpdateLayers(queue_.get(), memory_limit); + layer_tree_host_->UpdateLayers(queue_.get()); EXPECT_FALSE(root_layer->SkipsDraw()); ResourceManagerClearAllMemory(layer_tree_host_->contents_texture_manager(), @@ -1029,8 +1036,7 @@ TEST_F(TiledLayerPartialUpdateTest, PartialUpdates) { layer_tree_host_->SetViewportSize(gfx::Size(300, 200)); // Full update of all 6 tiles. - layer_tree_host_->UpdateLayers(queue_.get(), - std::numeric_limits<size_t>::max()); + layer_tree_host_->UpdateLayers(queue_.get()); { scoped_ptr<FakeTiledLayerImpl> layer_impl = make_scoped_ptr(new FakeTiledLayerImpl(host_impl_->active_tree(), 1)); @@ -1046,8 +1052,7 @@ TEST_F(TiledLayerPartialUpdateTest, PartialUpdates) { // Full update of 3 tiles and partial update of 3 tiles. layer->InvalidateContentRect(gfx::Rect(0, 0, 300, 150)); - layer_tree_host_->UpdateLayers(queue_.get(), - std::numeric_limits<size_t>::max()); + layer_tree_host_->UpdateLayers(queue_.get()); { scoped_ptr<FakeTiledLayerImpl> layer_impl = make_scoped_ptr(new FakeTiledLayerImpl(host_impl_->active_tree(), 1)); @@ -1066,8 +1071,7 @@ TEST_F(TiledLayerPartialUpdateTest, PartialUpdates) { { scoped_ptr<FakeTiledLayerImpl> layer_impl = make_scoped_ptr(new FakeTiledLayerImpl(host_impl_->active_tree(), 1)); - layer_tree_host_->UpdateLayers(queue_.get(), - std::numeric_limits<size_t>::max()); + layer_tree_host_->UpdateLayers(queue_.get()); EXPECT_EQ(2u, queue_->FullUploadSize()); EXPECT_EQ(4u, queue_->PartialUploadSize()); UpdateTextures(); @@ -1092,8 +1096,7 @@ TEST_F(TiledLayerPartialUpdateTest, PartialUpdates) { { scoped_ptr<FakeTiledLayerImpl> layer_impl = make_scoped_ptr(new FakeTiledLayerImpl(host_impl_->active_tree(), 1)); - layer_tree_host_->UpdateLayers(queue_.get(), - std::numeric_limits<size_t>::max()); + layer_tree_host_->UpdateLayers(queue_.get()); EXPECT_EQ(6u, queue_->FullUploadSize()); EXPECT_EQ(0u, queue_->PartialUploadSize()); UpdateTextures(); @@ -1109,8 +1112,7 @@ TEST_F(TiledLayerPartialUpdateTest, PartialUpdates) { { scoped_ptr<FakeTiledLayerImpl> layer_impl = make_scoped_ptr(new FakeTiledLayerImpl(host_impl_->active_tree(), 1)); - layer_tree_host_->UpdateLayers(queue_.get(), - std::numeric_limits<size_t>::max()); + layer_tree_host_->UpdateLayers(queue_.get()); EXPECT_EQ(0u, queue_->FullUploadSize()); EXPECT_EQ(4u, queue_->PartialUploadSize()); UpdateTextures(); @@ -1708,8 +1710,7 @@ TEST_F(TiledLayerTest, DontAllocateContentsWhenTargetSurfaceCantBeAllocated) { root->InvalidateContentRect(root_rect); child->InvalidateContentRect(child_rect); child2->InvalidateContentRect(child2_rect); - layer_tree_host_->UpdateLayers(queue_.get(), - std::numeric_limits<size_t>::max()); + layer_tree_host_->UpdateLayers(queue_.get()); { UpdateTextures(); EXPECT_EQ(6, root->fake_layer_updater()->update_count()); @@ -1747,8 +1748,11 @@ TEST_F(TiledLayerTest, DontAllocateContentsWhenTargetSurfaceCantBeAllocated) { root->InvalidateContentRect(root_rect); child->InvalidateContentRect(child_rect); child2->InvalidateContentRect(child2_rect); - layer_tree_host_->UpdateLayers(queue_.get(), - (3 * 2 + 3 * 1) * (100 * 100) * 4); + + size_t memory_limit = (3 * 2 + 3 * 1) * (100 * 100) * 4; + layer_tree_host_->contents_texture_manager()->SetMaxMemoryLimitBytes( + memory_limit); + layer_tree_host_->UpdateLayers(queue_.get()); { UpdateTextures(); EXPECT_EQ(6, root->fake_layer_updater()->update_count()); @@ -1786,7 +1790,11 @@ TEST_F(TiledLayerTest, DontAllocateContentsWhenTargetSurfaceCantBeAllocated) { root->InvalidateContentRect(root_rect); child->InvalidateContentRect(child_rect); child2->InvalidateContentRect(child2_rect); - layer_tree_host_->UpdateLayers(queue_.get(), (3 * 1) * (100 * 100) * 4); + + memory_limit = (3 * 1) * (100 * 100) * 4; + layer_tree_host_->contents_texture_manager()->SetMaxMemoryLimitBytes( + memory_limit); + layer_tree_host_->UpdateLayers(queue_.get()); { UpdateTextures(); EXPECT_EQ(0, root->fake_layer_updater()->update_count()); diff --git a/chromium/cc/layers/video_layer_impl.cc b/chromium/cc/layers/video_layer_impl.cc index 4b5671ff90e..28e470e7c3b 100644 --- a/chromium/cc/layers/video_layer_impl.cc +++ b/chromium/cc/layers/video_layer_impl.cc @@ -13,6 +13,7 @@ #include "cc/quads/texture_draw_quad.h" #include "cc/quads/yuv_video_draw_quad.h" #include "cc/resources/resource_provider.h" +#include "cc/resources/single_release_callback.h" #include "cc/trees/layer_tree_impl.h" #include "cc/trees/proxy.h" #include "media/base/video_frame.h" @@ -93,8 +94,11 @@ bool VideoLayerImpl::WillDraw(DrawMode draw_mode, if (!LayerImpl::WillDraw(draw_mode, resource_provider)) return false; - if (!updater_) - updater_.reset(new VideoResourceUpdater(resource_provider)); + if (!updater_) { + updater_.reset( + new VideoResourceUpdater(layer_tree_impl()->context_provider(), + layer_tree_impl()->resource_provider())); + } VideoFrameExternalResources external_resources = updater_->CreateExternalResourcesFromVideoFrame(frame_); @@ -108,10 +112,13 @@ bool VideoLayerImpl::WillDraw(DrawMode draw_mode, return true; } + DCHECK_EQ(external_resources.mailboxes.size(), + external_resources.release_callbacks.size()); for (size_t i = 0; i < external_resources.mailboxes.size(); ++i) { - frame_resources_.push_back( - resource_provider->CreateResourceFromTextureMailbox( - external_resources.mailboxes[i])); + unsigned resource_id = resource_provider->CreateResourceFromTextureMailbox( + external_resources.mailboxes[i], + SingleReleaseCallback::Create(external_resources.release_callbacks[i])); + frame_resources_.push_back(resource_id); } return true; diff --git a/chromium/cc/layers/video_layer_impl.h b/chromium/cc/layers/video_layer_impl.h index b07187115ed..b4df1417bf1 100644 --- a/chromium/cc/layers/video_layer_impl.h +++ b/chromium/cc/layers/video_layer_impl.h @@ -9,6 +9,7 @@ #include "cc/base/cc_export.h" #include "cc/layers/layer_impl.h" +#include "cc/resources/release_callback.h" #include "cc/resources/video_resource_updater.h" namespace media { @@ -59,7 +60,8 @@ class CC_EXPORT VideoLayerImpl : public LayerImpl { // TODO(danakj): Remove these, hide software path inside ResourceProvider and // ExternalResource (aka TextureMailbox) classes. std::vector<unsigned> software_resources_; - TextureMailbox::ReleaseCallback software_release_callback_; + // Called once for each software resource. + ReleaseCallback software_release_callback_; DISALLOW_COPY_AND_ASSIGN(VideoLayerImpl); }; |
