diff options
author | powei@chromium.org <powei@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-08-01 06:28:57 +0000 |
---|---|---|
committer | powei@chromium.org <powei@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-08-01 06:28:57 +0000 |
commit | c928076e7b0f787091d512d6e24e4cbeb90159bb (patch) | |
tree | d7b38b3e0dc6b28e30418f6228ecba5d116a8577 | |
parent | a8a515cf0845fcfdea7163fe1ed78a8c4c2b2e5b (diff) | |
download | chromium_src-c928076e7b0f787091d512d6e24e4cbeb90159bb.zip chromium_src-c928076e7b0f787091d512d6e24e4cbeb90159bb.tar.gz chromium_src-c928076e7b0f787091d512d6e24e4cbeb90159bb.tar.bz2 |
Proposed UI Resource Manager. See internal doc:
https://docs.google.com/a/google.com/document/d/1Im8fR0bVsBHC2I1f2MuzW-e-h1H1F5GYH4fbdBTsbQ8/edit
BUG=259095,173947
Review URL: https://chromiumcodereview.appspot.com/18191020
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@214975 0039d316-1c4b-4281-b951-d872f2087c98
25 files changed, 1167 insertions, 423 deletions
@@ -312,6 +312,8 @@ 'resources/resource_update_queue.h', 'resources/scoped_resource.cc', 'resources/scoped_resource.h', + 'resources/scoped_ui_resource.cc', + 'resources/scoped_ui_resource.h', 'resources/skpicture_content_layer_updater.cc', 'resources/skpicture_content_layer_updater.h', 'resources/sync_point_helper.cc', @@ -326,6 +328,9 @@ 'resources/tile_priority.h', 'resources/transferable_resource.cc', 'resources/transferable_resource.h', + 'resources/ui_resource_bitmap.cc', + 'resources/ui_resource_bitmap.h', + 'resources/ui_resource_client.h', 'resources/video_resource_updater.cc', 'resources/video_resource_updater.h', 'resources/worker_pool.cc', diff --git a/cc/cc_tests.gyp b/cc/cc_tests.gyp index 181ce09..9278c81 100644 --- a/cc/cc_tests.gyp +++ b/cc/cc_tests.gyp @@ -135,6 +135,8 @@ 'test/fake_output_surface.h', 'test/fake_output_surface_client.cc', 'test/fake_output_surface_client.h', + 'test/fake_scoped_ui_resource.cc', + 'test/fake_scoped_ui_resource.h', 'test/fake_video_frame_provider.cc', 'test/fake_video_frame_provider.h', 'test/geometry_test_utils.cc', diff --git a/cc/layers/scrollbar_layer.cc b/cc/layers/scrollbar_layer.cc index 927ae7b..302e3e0 100644 --- a/cc/layers/scrollbar_layer.cc +++ b/cc/layers/scrollbar_layer.cc @@ -1,4 +1,3 @@ - // 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. @@ -9,11 +8,14 @@ #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/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/rect_conversions.h" namespace cc { @@ -27,16 +29,15 @@ scoped_ptr<LayerImpl> ScrollbarLayer::CreateLayerImpl( scoped_refptr<ScrollbarLayer> ScrollbarLayer::Create( scoped_ptr<Scrollbar> scrollbar, int scroll_layer_id) { - return make_scoped_refptr(new ScrollbarLayer(scrollbar.Pass(), - 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) { + scroll_layer_id_(scroll_layer_id) { if (!scrollbar_->IsOverlay()) SetShouldScrollOnMainThread(true); } @@ -128,15 +129,10 @@ void ScrollbarLayer::PushPropertiesTo(LayerImpl* layer) { scrollbar_layer->set_track_length(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); + 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()); @@ -149,113 +145,16 @@ ScrollbarLayer* ScrollbarLayer::ToScrollbarLayer() { } void ScrollbarLayer::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_updater_ = NULL; - track_.reset(); - thumb_updater_ = NULL; - thumb_.reset(); + track_resource_.reset(); + thumb_resource_.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 @@ -269,30 +168,16 @@ gfx::Rect ScrollbarLayer::ScrollbarLayerRectToContentRect( 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)); +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)); } bool ScrollbarLayer::Update(ResourceUpdateQueue* queue, @@ -308,43 +193,62 @@ bool ScrollbarLayer::Update(ResourceUpdateQueue* queue, 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())); - bool 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; + track_resource_ = + ScopedUIResource::Create(layer_tree_host(), RasterizeTrack()); + if (scrollbar_->HasThumb()) + thumb_resource_ = + ScopedUIResource::Create(layer_tree_host(), RasterizeThumb()); + + return true; +} + +scoped_refptr<UIResourceBitmap> ScrollbarLayer::RasterizeTrack() { + DCHECK(!layer_tree_host()->settings().solid_color_scrollbars); + + gfx::Rect track_rect = ScrollbarLayerRectToContentRect( + gfx::Rect(scrollbar_->Location(), bounds())); + scoped_refptr<UIResourceBitmap> track_bitmap = UIResourceBitmap::Create( + new uint8_t[track_rect.width() * track_rect.height() * 4], + UIResourceBitmap::RGBA8, + track_rect.size()); + + SkBitmap skbitmap; + skbitmap.setConfig( + SkBitmap::kARGB_8888_Config, track_rect.width(), track_rect.height()); + skbitmap.setPixels(track_bitmap->GetPixels()); + + SkCanvas track_canvas(skbitmap); + track_canvas.translate(SkFloatToScalar(-track_rect.x()), + SkFloatToScalar(-track_rect.y())); + scrollbar_->PaintPart(&track_canvas, TRACK, track_rect); + + return track_bitmap; } -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)); +scoped_refptr<UIResourceBitmap> ScrollbarLayer::RasterizeThumb() { + DCHECK(!layer_tree_host()->settings().solid_color_scrollbars); + DCHECK(scrollbar_->HasThumb()); + + gfx::Rect thumb_rect = OriginThumbRect(); + scoped_refptr<UIResourceBitmap> thumb_bitmap = UIResourceBitmap::Create( + new uint8_t[thumb_rect.width() * thumb_rect.height() * 4], + UIResourceBitmap::RGBA8, + thumb_rect.size()); + + SkBitmap skbitmap; + skbitmap.setConfig( + SkBitmap::kARGB_8888_Config, thumb_rect.width(), thumb_rect.height()); + skbitmap.setPixels(thumb_bitmap->GetPixels()); + + SkCanvas thumb_canvas(skbitmap); + scrollbar_->PaintPart(&thumb_canvas, THUMB, thumb_rect); + + return thumb_bitmap; } } // namespace cc diff --git a/cc/layers/scrollbar_layer.h b/cc/layers/scrollbar_layer.h index f500dcc..354d549 100644 --- a/cc/layers/scrollbar_layer.h +++ b/cc/layers/scrollbar_layer.h @@ -10,10 +10,9 @@ #include "cc/layers/contents_scaling_layer.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 { @@ -33,8 +32,6 @@ class CC_EXPORT ScrollbarLayer : public ContentsScalingLayer { ScrollbarOrientation Orientation() const; // 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; @@ -55,36 +52,25 @@ class CC_EXPORT ScrollbarLayer : public ContentsScalingLayer { virtual ~ScrollbarLayer(); 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); + scoped_refptr<UIResourceBitmap> RasterizeTrack(); + scoped_refptr<UIResourceBitmap> RasterizeThumb(); + scoped_ptr<Scrollbar> scrollbar_; int thumb_thickness_; int thumb_length_; gfx::Rect track_rect_; + gfx::Rect thumb_rect_; int scroll_layer_id_; - unsigned texture_format_; - - gfx::RectF dirty_rect_; - - scoped_refptr<CachingBitmapContentLayerUpdater> track_updater_; - scoped_refptr<CachingBitmapContentLayerUpdater> thumb_updater_; - - // 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); }; diff --git a/cc/layers/scrollbar_layer_impl.cc b/cc/layers/scrollbar_layer_impl.cc index 0dcfcde..51dbe07 100644 --- a/cc/layers/scrollbar_layer_impl.cc +++ b/cc/layers/scrollbar_layer_impl.cc @@ -31,8 +31,8 @@ ScrollbarLayerImpl::ScrollbarLayerImpl( int id, ScrollbarOrientation orientation) : LayerImpl(tree_impl, id), - track_resource_id_(0), - thumb_resource_id_(0), + track_ui_resource_id_(0), + thumb_ui_resource_id_(0), current_pos_(0.f), maximum_(0), thumb_thickness_(0), @@ -69,8 +69,8 @@ void ScrollbarLayerImpl::PushPropertiesTo(LayerImpl* layer) { scrollbar_layer->set_track_length(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_); + scrollbar_layer->set_track_ui_resource_id(track_ui_resource_id_); + scrollbar_layer->set_thumb_ui_resource_id(thumb_ui_resource_id_); } bool ScrollbarLayerImpl::WillDraw(DrawMode draw_mode, @@ -106,14 +106,19 @@ void ScrollbarLayerImpl::AppendQuads(QuadSink* quad_sink, return; } - if (thumb_resource_id_ && !thumb_quad_rect.IsEmpty()) { + 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_, + thumb_resource_id, premultipled_alpha, uv_top_left, uv_bottom_right, @@ -123,21 +128,15 @@ void ScrollbarLayerImpl::AppendQuads(QuadSink* quad_sink, 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()); + 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, - quad_rect, + track_quad_rect, opaque_rect, - track_resource_id_, + track_resource_id, premultipled_alpha, uv_top_left, uv_bottom_right, @@ -261,11 +260,6 @@ gfx::Rect ScrollbarLayerImpl::ComputeThumbQuadRect() const { return ScrollbarLayerRectToContentRect(thumb_rect); } -void ScrollbarLayerImpl::DidLoseOutputSurface() { - track_resource_id_ = 0; - thumb_resource_id_ = 0; -} - const char* ScrollbarLayerImpl::LayerTypeAsString() const { return "cc::ScrollbarLayerImpl"; } diff --git a/cc/layers/scrollbar_layer_impl.h b/cc/layers/scrollbar_layer_impl.h index f61b660..ffc758f 100644 --- a/cc/layers/scrollbar_layer_impl.h +++ b/cc/layers/scrollbar_layer_impl.h @@ -8,6 +8,7 @@ #include "cc/base/cc_export.h" #include "cc/input/scrollbar.h" #include "cc/layers/layer_impl.h" +#include "cc/resources/ui_resource_client.h" namespace cc { @@ -33,8 +34,6 @@ class CC_EXPORT ScrollbarLayerImpl : public LayerImpl { 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; } @@ -58,11 +57,11 @@ class CC_EXPORT ScrollbarLayerImpl : public LayerImpl { void set_vertical_adjust(float vertical_adjust) { vertical_adjust_ = vertical_adjust; } - void set_track_resource_id(ResourceProvider::ResourceId id) { - track_resource_id_ = id; + void set_track_ui_resource_id(UIResourceId uid) { + track_ui_resource_id_ = uid; } - void set_thumb_resource_id(ResourceProvider::ResourceId id) { - thumb_resource_id_ = id; + void set_thumb_ui_resource_id(UIResourceId uid) { + thumb_ui_resource_id_ = uid; } void set_visible_to_total_length_ratio(float ratio) { visible_to_total_length_ratio_ = ratio; @@ -87,8 +86,8 @@ class CC_EXPORT ScrollbarLayerImpl : public LayerImpl { gfx::Rect ScrollbarLayerRectToContentRect(gfx::RectF layer_rect) const; - ResourceProvider::ResourceId track_resource_id_; - ResourceProvider::ResourceId thumb_resource_id_; + UIResourceId track_ui_resource_id_; + UIResourceId thumb_ui_resource_id_; float current_pos_; int maximum_; diff --git a/cc/layers/scrollbar_layer_unittest.cc b/cc/layers/scrollbar_layer_unittest.cc index 1d46113..c007bb0 100644 --- a/cc/layers/scrollbar_layer_unittest.cc +++ b/cc/layers/scrollbar_layer_unittest.cc @@ -4,6 +4,7 @@ #include "cc/layers/scrollbar_layer.h" +#include "base/containers/hash_tables.h" #include "cc/animation/scrollbar_animation_controller.h" #include "cc/layers/append_quads_data.h" #include "cc/layers/scrollbar_layer_impl.h" @@ -20,6 +21,7 @@ #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" @@ -403,9 +405,39 @@ 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_id_set_.insert(nid); + return nid; + } + + // Deletes a UI resource. May safely be called more than once. + virtual void DeleteUIResource(UIResourceId id) OVERRIDE { + UIResourceIdSet::iterator iter = ui_resource_id_set_.find(id); + if (iter != ui_resource_id_set_.end()) { + ui_resource_id_set_.erase(iter); + total_ui_resource_deleted_++; + } + } + + size_t UIResourceCount() { return ui_resource_id_set_.size(); } + int TotalUIResourceDeleted() { return total_ui_resource_deleted_; } + int TotalUIResourceCreated() { return total_ui_resource_created_; } + + private: + typedef base::hash_set<UIResourceId> UIResourceIdSet; + UIResourceIdSet ui_resource_id_set_; + int next_id_; + int total_ui_resource_created_; + int total_ui_resource_deleted_; }; @@ -414,7 +446,10 @@ 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) { layer_tree_host_.reset( new MockLayerTreeHost(&fake_client_, layer_tree_settings_)); @@ -446,15 +481,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->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. + // A 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()); @@ -469,104 +506,18 @@ class ScrollbarLayerTestResourceCreation : public testing::Test { TEST_F(ScrollbarLayerTestResourceCreation, ResourceUpload) { layer_tree_settings_.solid_color_scrollbars = false; - TestResourceUpload(2); + TestResourceUpload(0, 0, 0, 0); + 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); + } } TEST_F(ScrollbarLayerTestResourceCreation, SolidColorNoResourceUpload) { layer_tree_settings_.solid_color_scrollbars = true; - TestResourceUpload(0); -} - -class ScaledScrollbarLayerTestResourceCreation : public testing::Test { - public: - ScaledScrollbarLayerTestResourceCreation() - : fake_client_(FakeLayerTreeHostClient::DIRECT_3D) {} - - void TestResourceUpload(size_t expected_resources, 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()); - 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); - scrollbar_layer->SetBounds(gfx::Size(100, 15)); - scrollbar_layer->SetPosition(scrollbar_location); - layer_tree_root->SetBounds(gfx::Size(100, 200)); - content_layer->SetBounds(gfx::Size(100, 200)); - gfx::SizeF scaled_size = - gfx::ScaleSize(scrollbar_layer->bounds(), test_scale, test_scale); - gfx::PointF scaled_location = - gfx::ScalePoint(scrollbar_layer->position(), test_scale, test_scale); - scrollbar_layer->draw_properties().content_bounds = - gfx::Size(scaled_size.width(), scaled_size.height()); - scrollbar_layer->draw_properties().contents_scale_x = test_scale; - scrollbar_layer->draw_properties().contents_scale_y = test_scale; - scrollbar_layer->draw_properties().visible_content_rect = - gfx::Rect(scaled_location.x(), - scaled_location.y(), - scaled_size.width(), - scaled_size.height()); - scrollbar_layer->CreateRenderSurface(); - scrollbar_layer->draw_properties().render_target = scrollbar_layer.get(); - - 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->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()); - } - - testing::Mock::VerifyAndClearExpectations(layer_tree_host_.get()); - - scrollbar_layer->ClearRenderSurface(); - } - - protected: - FakeLayerTreeHostClient fake_client_; - LayerTreeSettings layer_tree_settings_; - scoped_ptr<MockLayerTreeHost> layer_tree_host_; -}; - -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(0, 0, 0, 0); + TestResourceUpload(1, 0, 0, 0); } } // namespace diff --git a/cc/resources/scoped_ui_resource.cc b/cc/resources/scoped_ui_resource.cc new file mode 100644 index 0000000..1622552 --- /dev/null +++ b/cc/resources/scoped_ui_resource.cc @@ -0,0 +1,43 @@ +// 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/resources/scoped_ui_resource.h" + +#include "base/basictypes.h" +#include "base/bind.h" +#include "cc/trees/layer_tree_host.h" + +namespace cc { + +scoped_ptr<ScopedUIResource> ScopedUIResource::Create( + LayerTreeHost* host, + scoped_refptr<UIResourceBitmap> bitmap) { + return make_scoped_ptr(new ScopedUIResource(host, bitmap)); +} + +ScopedUIResource::ScopedUIResource(LayerTreeHost* host, + scoped_refptr<UIResourceBitmap> bitmap) + : bitmap_(bitmap), host_(host) { + DCHECK(host_); + id_ = host_->CreateUIResource(this); +} + +// User must make sure that host is still valid before this object goes out of +// scope. +ScopedUIResource::~ScopedUIResource() { + if (id_) { + DCHECK(host_); + host_->DeleteUIResource(id_); + } +} + +scoped_refptr<UIResourceBitmap> ScopedUIResource::GetBitmap( + UIResourceId uid, + bool resource_lost) { + return bitmap_; +} + +ScopedUIResource::ScopedUIResource() {} + +} // namespace cc diff --git a/cc/resources/scoped_ui_resource.h b/cc/resources/scoped_ui_resource.h new file mode 100644 index 0000000..628d372 --- /dev/null +++ b/cc/resources/scoped_ui_resource.h @@ -0,0 +1,46 @@ +// 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_RESOURCES_SCOPED_UI_RESOURCE_H_ +#define CC_RESOURCES_SCOPED_UI_RESOURCE_H_ + +#include "base/memory/ref_counted.h" +#include "cc/base/cc_export.h" +#include "cc/resources/ui_resource_bitmap.h" +#include "cc/resources/ui_resource_client.h" +#include "ui/gfx/size.h" + +namespace cc { + +class LayerTreeHost; + +class CC_EXPORT ScopedUIResource : public UIResourceClient { + public: + static scoped_ptr<ScopedUIResource> Create( + LayerTreeHost* host, + scoped_refptr<UIResourceBitmap> bitmap); + virtual ~ScopedUIResource(); + + virtual scoped_refptr<UIResourceBitmap> GetBitmap( + UIResourceId uid, + bool resource_lost) OVERRIDE; + UIResourceId id() { return id_; } + + protected: + ScopedUIResource(LayerTreeHost* host, scoped_refptr<UIResourceBitmap> bitmap); + + // An empty default contructor for testing. + ScopedUIResource(); + + scoped_refptr<UIResourceBitmap> bitmap_; + LayerTreeHost* host_; + UIResourceId id_; + + private: + DISALLOW_COPY_AND_ASSIGN(ScopedUIResource); +}; + +} // namespace cc + +#endif // CC_RESOURCES_SCOPED_UI_RESOURCE_H_ diff --git a/cc/resources/ui_resource_bitmap.cc b/cc/resources/ui_resource_bitmap.cc new file mode 100644 index 0000000..8bbfb37 --- /dev/null +++ b/cc/resources/ui_resource_bitmap.cc @@ -0,0 +1,26 @@ +// 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/resources/ui_resource_bitmap.h" + +#include "base/memory/scoped_ptr.h" + +namespace cc { + +scoped_refptr<UIResourceBitmap> +UIResourceBitmap::Create(uint8_t* pixels, + UIResourceFormat format, + gfx::Size size) { + scoped_refptr<UIResourceBitmap> ret = new UIResourceBitmap(); + ret->pixels_ = scoped_ptr<uint8_t[]>(pixels); + ret->format_ = format; + ret->size_ = size; + + return ret; +} + +UIResourceBitmap::UIResourceBitmap() {} +UIResourceBitmap::~UIResourceBitmap() {} + +} // namespace cc diff --git a/cc/resources/ui_resource_bitmap.h b/cc/resources/ui_resource_bitmap.h new file mode 100644 index 0000000..dbf7069 --- /dev/null +++ b/cc/resources/ui_resource_bitmap.h @@ -0,0 +1,50 @@ +// 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_RESOURCES_UI_RESOURCE_BITMAP_H_ +#define CC_RESOURCES_UI_RESOURCE_BITMAP_H_ + +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "cc/base/cc_export.h" +#include "third_party/skia/include/core/SkTypes.h" +#include "ui/gfx/size.h" + +namespace cc { + +// Ref-counted bitmap class (can’t use SkBitmap because of ETC1). Thread-safety +// ensures that both main and impl threads can hold references to the bitmap and +// that asynchronous uploads are allowed. +class CC_EXPORT UIResourceBitmap + : public base::RefCountedThreadSafe<UIResourceBitmap> { + public: + enum UIResourceFormat { + RGBA8 + }; + + // Takes ownership of “pixels”. + static scoped_refptr<UIResourceBitmap> Create(uint8_t* pixels, + UIResourceFormat format, + gfx::Size size); + + gfx::Size GetSize() const { return size_; } + UIResourceFormat GetFormat() const { return format_; } + uint8_t* GetPixels() { return pixels_.get(); } + + private: + friend class base::RefCountedThreadSafe<UIResourceBitmap>; + + UIResourceBitmap(); + ~UIResourceBitmap(); + + scoped_ptr<uint8_t[]> pixels_; + UIResourceFormat format_; + gfx::Size size_; + + DISALLOW_COPY_AND_ASSIGN(UIResourceBitmap); +}; + +} // namespace cc + +#endif // CC_RESOURCES_UI_RESOURCE_BITMAP_H_ diff --git a/cc/resources/ui_resource_client.h b/cc/resources/ui_resource_client.h new file mode 100644 index 0000000..d647936 --- /dev/null +++ b/cc/resources/ui_resource_client.h @@ -0,0 +1,34 @@ +// 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_RESOURCES_UI_RESOURCE_CLIENT_H_ +#define CC_RESOURCES_UI_RESOURCE_CLIENT_H_ + +#include "base/basictypes.h" +#include "base/callback.h" +#include "base/memory/ref_counted.h" +#include "cc/base/cc_export.h" + +namespace cc { + +class UIResourceBitmap; + +typedef int UIResourceId; + +class CC_EXPORT UIResourceClient { + public: + // GetBitmap() will be called once soon after resource creation and then will + // be called afterwards whenever the GL context is lost, on the same thread + // that LayerTreeHost::CreateUIResource was called on. It is only safe to + // delete a UIResourceClient object after DeleteUIResource has been called for + // all IDs associated with it. A valid bitmap always must be returned but it + // doesn't need to be the same size or format as the original. + virtual scoped_refptr<UIResourceBitmap> GetBitmap(UIResourceId uid, + bool resource_lost) = 0; + virtual ~UIResourceClient() {} +}; + +} // namespace cc + +#endif // CC_RESOURCES_UI_RESOURCE_CLIENT_H_ diff --git a/cc/test/fake_scoped_ui_resource.cc b/cc/test/fake_scoped_ui_resource.cc new file mode 100644 index 0000000..02f0fa9 --- /dev/null +++ b/cc/test/fake_scoped_ui_resource.cc @@ -0,0 +1,38 @@ +// 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/test/fake_scoped_ui_resource.h" + +#include "cc/trees/layer_tree_host.h" + +namespace cc { + +scoped_ptr<FakeScopedUIResource> FakeScopedUIResource::Create( + LayerTreeHost* host) { + return make_scoped_ptr(new FakeScopedUIResource(host)); +} + +FakeScopedUIResource::FakeScopedUIResource(LayerTreeHost* host) { + ResetCounters(); + bitmap_ = UIResourceBitmap::Create( + new uint8_t[1], UIResourceBitmap::RGBA8, gfx::Size(1, 1)); + host_ = host; + id_ = host_->CreateUIResource(this); +} + +scoped_refptr<UIResourceBitmap> FakeScopedUIResource::GetBitmap( + UIResourceId uid, + bool resource_lost) { + resource_create_count++; + if (resource_lost) + lost_resource_count++; + return ScopedUIResource::GetBitmap(uid, resource_lost); +} + +void FakeScopedUIResource::ResetCounters() { + resource_create_count = 0; + lost_resource_count = 0; +} + +} // namespace cc diff --git a/cc/test/fake_scoped_ui_resource.h b/cc/test/fake_scoped_ui_resource.h new file mode 100644 index 0000000..84c097f --- /dev/null +++ b/cc/test/fake_scoped_ui_resource.h @@ -0,0 +1,33 @@ +// 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_TEST_FAKE_SCOPED_UI_RESOURCE_H_ +#define CC_TEST_FAKE_SCOPED_UI_RESOURCE_H_ + +#include "base/memory/ref_counted.h" +#include "cc/resources/scoped_ui_resource.h" + +namespace cc { + +class LayerTreeHost; + +class FakeScopedUIResource : public ScopedUIResource { + public: + static scoped_ptr<FakeScopedUIResource> Create(LayerTreeHost* host); + + virtual scoped_refptr<UIResourceBitmap> GetBitmap( + UIResourceId uid, + bool resource_lost) OVERRIDE; + void ResetCounters(); + + int resource_create_count; + int lost_resource_count; + + private: + explicit FakeScopedUIResource(LayerTreeHost* host); +}; + +} // namespace cc + +#endif // CC_TEST_FAKE_SCOPED_UI_RESOURCE_H_ diff --git a/cc/test/fake_scrollbar_layer.cc b/cc/test/fake_scrollbar_layer.cc index 64e08ad..77cf826 100644 --- a/cc/test/fake_scrollbar_layer.cc +++ b/cc/test/fake_scrollbar_layer.cc @@ -18,9 +18,7 @@ FakeScrollbarLayer::FakeScrollbarLayer(bool paint_during_update, new FakeScrollbar(paint_during_update, has_thumb, false)).Pass(), scrolling_layer_id), update_count_(0), - push_properties_count_(0), - last_update_full_upload_size_(0), - last_update_partial_upload_size_(0) { + push_properties_count_(0) { SetAnchorPoint(gfx::PointF(0.f, 0.f)); SetBounds(gfx::Size(1, 1)); SetIsDrawable(true); @@ -30,12 +28,8 @@ FakeScrollbarLayer::~FakeScrollbarLayer() {} bool FakeScrollbarLayer::Update(ResourceUpdateQueue* queue, const OcclusionTracker* occlusion) { - size_t full = queue->FullUploadSize(); - size_t partial = queue->PartialUploadSize(); bool updated = ScrollbarLayer::Update(queue, occlusion); - update_count_++; - last_update_full_upload_size_ = queue->FullUploadSize() - full; - last_update_partial_upload_size_ = queue->PartialUploadSize() - partial; + ++update_count_; return updated; } diff --git a/cc/test/fake_scrollbar_layer.h b/cc/test/fake_scrollbar_layer.h index 43be832..60767f4 100644 --- a/cc/test/fake_scrollbar_layer.h +++ b/cc/test/fake_scrollbar_layer.h @@ -23,12 +23,6 @@ class FakeScrollbarLayer : public ScrollbarLayer { int update_count() const { return update_count_; } void reset_update_count() { update_count_ = 0; } - size_t last_update_full_upload_size() const { - return last_update_full_upload_size_; - } - size_t last_update_partial_upload_size() const { - return last_update_partial_upload_size_; - } virtual bool Update(ResourceUpdateQueue* queue, const OcclusionTracker* occlusion) OVERRIDE; @@ -48,8 +42,6 @@ class FakeScrollbarLayer : public ScrollbarLayer { int update_count_; size_t push_properties_count_; - size_t last_update_full_upload_size_; - size_t last_update_partial_upload_size_; }; } // namespace cc diff --git a/cc/trees/layer_tree_host.cc b/cc/trees/layer_tree_host.cc index 86736e6..9d51042 100644 --- a/cc/trees/layer_tree_host.cc +++ b/cc/trees/layer_tree_host.cc @@ -29,6 +29,7 @@ #include "cc/layers/render_surface.h" #include "cc/layers/scrollbar_layer.h" #include "cc/resources/prioritized_resource_manager.h" +#include "cc/resources/ui_resource_client.h" #include "cc/trees/layer_tree_host_client.h" #include "cc/trees/layer_tree_host_common.h" #include "cc/trees/layer_tree_host_impl.h" @@ -59,6 +60,11 @@ RendererCapabilities::RendererCapabilities() RendererCapabilities::~RendererCapabilities() {} +UIResourceRequest::UIResourceRequest() + : type(UIResourceInvalidRequest), id(0), bitmap(NULL) {} + +UIResourceRequest::~UIResourceRequest() {} + bool LayerTreeHost::AnyLayerTreeHostInstanceExists() { return s_num_layer_tree_instances > 0; } @@ -78,7 +84,8 @@ static int s_next_tree_id = 1; LayerTreeHost::LayerTreeHost(LayerTreeHostClient* client, const LayerTreeSettings& settings) - : animating_(false), + : next_ui_resource_id_(1), + animating_(false), needs_full_tree_sync_(true), needs_filter_context_(false), client_(client), @@ -354,6 +361,15 @@ void LayerTreeHost::FinishCommitOnImplThread(LayerTreeHostImpl* host_impl) { pending_page_scale_animation_.reset(); } + if (!ui_resource_request_queue_.empty()) { + sync_tree->set_ui_resource_request_queue(ui_resource_request_queue_); + ui_resource_request_queue_.clear(); + // Process any ui resource requests in the queue. For impl-side-painting, + // the queue is processed in LayerTreeHostImpl::ActivatePendingTree. + if (!settings_.impl_side_painting) + sync_tree->ProcessUIResourceRequestQueue(); + } + DCHECK(!sync_tree->ViewportSizeInvalid()); if (new_impl_tree_has_no_evicted_resources) { @@ -419,6 +435,8 @@ void LayerTreeHost::DidLoseOutputSurface() { if (output_surface_lost_) return; + DidLoseUIResources(); + num_failed_recreate_attempts_ = 0; output_surface_lost_ = true; SetNeedsCommit(); @@ -1050,7 +1068,7 @@ bool LayerTreeHost::RequestPartialTextureUpdate() { } void LayerTreeHost::SetDeviceScaleFactor(float device_scale_factor) { - if (device_scale_factor == device_scale_factor_) + if (device_scale_factor == device_scale_factor_) return; device_scale_factor_ = device_scale_factor; @@ -1106,4 +1124,57 @@ void LayerTreeHost::AnimateLayers(base::TimeTicks time) { } } +UIResourceId LayerTreeHost::CreateUIResource(UIResourceClient* client) { + DCHECK(client); + + UIResourceRequest request; + bool resource_lost = false; + request.type = UIResourceRequest::UIResourceCreate; + request.id = next_ui_resource_id_++; + + DCHECK(ui_resource_client_map_.find(request.id) == + ui_resource_client_map_.end()); + + request.bitmap = client->GetBitmap(request.id, resource_lost); + ui_resource_request_queue_.push_back(request); + ui_resource_client_map_[request.id] = client; + return request.id; +} + +// Deletes a UI resource. May safely be called more than once. +void LayerTreeHost::DeleteUIResource(UIResourceId uid) { + UIResourceClientMap::iterator iter = ui_resource_client_map_.find(uid); + if (iter == ui_resource_client_map_.end()) + return; + + UIResourceRequest request; + request.type = UIResourceRequest::UIResourceDelete; + request.id = uid; + ui_resource_request_queue_.push_back(request); + ui_resource_client_map_.erase(uid); +} + +void LayerTreeHost::UIResourceLost(UIResourceId uid) { + UIResourceClientMap::iterator iter = ui_resource_client_map_.find(uid); + if (iter == ui_resource_client_map_.end()) + return; + + UIResourceRequest request; + bool resource_lost = true; + request.type = UIResourceRequest::UIResourceCreate; + request.id = uid; + request.bitmap = iter->second->GetBitmap(uid, resource_lost); + DCHECK(request.bitmap.get()); + ui_resource_request_queue_.push_back(request); +} + +void LayerTreeHost::DidLoseUIResources() { + // When output surface is lost, we need to recreate the resource. + for (UIResourceClientMap::iterator iter = ui_resource_client_map_.begin(); + iter != ui_resource_client_map_.end(); + ++iter) { + UIResourceLost(iter->first); + } +} + } // namespace cc diff --git a/cc/trees/layer_tree_host.h b/cc/trees/layer_tree_host.h index b720a8d..619c18e 100644 --- a/cc/trees/layer_tree_host.h +++ b/cc/trees/layer_tree_host.h @@ -6,6 +6,7 @@ #define CC_TREES_LAYER_TREE_HOST_H_ #include <limits> +#include <list> #include <vector> #include "base/basictypes.h" @@ -23,6 +24,8 @@ #include "cc/input/top_controls_state.h" #include "cc/layers/layer_lists.h" #include "cc/output/output_surface.h" +#include "cc/resources/ui_resource_bitmap.h" +#include "cc/resources/ui_resource_client.h" #include "cc/scheduler/rate_limiter.h" #include "cc/trees/layer_tree_host_client.h" #include "cc/trees/layer_tree_host_common.h" @@ -81,6 +84,20 @@ struct CC_EXPORT RendererCapabilities { bool using_shared_memory_resources; }; +struct CC_EXPORT UIResourceRequest { + enum UIResourceRequestType { + UIResourceCreate, + UIResourceDelete, + UIResourceInvalidRequest + }; + + UIResourceRequest(); + ~UIResourceRequest(); + UIResourceRequestType type; + UIResourceId id; + scoped_refptr<UIResourceBitmap> bitmap; +}; + class CC_EXPORT LayerTreeHost : NON_EXPORTED_BASE(public RateLimiterClient) { public: static scoped_ptr<LayerTreeHost> Create( @@ -256,6 +273,16 @@ class CC_EXPORT LayerTreeHost : NON_EXPORTED_BASE(public RateLimiterClient) { bool in_paint_layer_contents() const { return in_paint_layer_contents_; } + // CreateUIResource creates a resource given a bitmap. The bitmap is + // generated via an interface function, which is called when initializing the + // resource and when the resource has been lost (due to lost context). The + // parameter of the interface is a single boolean, which indicates whether the + // resource has been lost or not. CreateUIResource returns an Id of the + // resource, which is always positive. + virtual UIResourceId CreateUIResource(UIResourceClient* client); + // Deletes a UI resource. May safely be called more than once. + virtual void DeleteUIResource(UIResourceId id); + bool UsingSharedMemoryResources(); int id() const { return tree_id_; } @@ -292,6 +319,17 @@ class CC_EXPORT LayerTreeHost : NON_EXPORTED_BASE(public RateLimiterClient) { bool AnimateLayersRecursive(Layer* current, base::TimeTicks time); + void UIResourceLost(UIResourceId id); + + void DidLoseUIResources(); + + typedef base::hash_map<UIResourceId, UIResourceClient*> UIResourceClientMap; + UIResourceClientMap ui_resource_client_map_; + int next_ui_resource_id_; + + typedef std::list<UIResourceRequest> UIResourceRequestQueue; + UIResourceRequestQueue ui_resource_request_queue_; + void CalculateLCDTextMetricsCallback(Layer* layer); bool animating_; diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc index 3da5659..052797a 100644 --- a/cc/trees/layer_tree_host_impl.cc +++ b/cc/trees/layer_tree_host_impl.cc @@ -40,6 +40,7 @@ #include "cc/resources/memory_history.h" #include "cc/resources/picture_layer_tiling.h" #include "cc/resources/prioritized_resource_manager.h" +#include "cc/resources/ui_resource_bitmap.h" #include "cc/scheduler/delay_based_time_source.h" #include "cc/scheduler/texture_uploader.h" #include "cc/trees/damage_tracker.h" @@ -1457,6 +1458,11 @@ void LayerTreeHostImpl::ActivatePendingTree() { active_tree_->root_layer()); DCHECK(!recycle_tree_); + // Process any requests in the UI resource queue. The request queue is given + // in LayerTreeHost::FinishCommitOnImplThread. This must take place before + // the swap. + pending_tree_->ProcessUIResourceRequestQueue(); + pending_tree_->PushPropertiesTo(active_tree_.get()); // Now that we've synced everything from the pending tree to the active @@ -1524,6 +1530,9 @@ void LayerTreeHostImpl::ReleaseTreeResources() { SendReleaseResourcesRecursive(pending_tree_->root_layer()); if (recycle_tree_ && recycle_tree_->root_layer()) SendReleaseResourcesRecursive(recycle_tree_->root_layer()); + + // Remove all existing maps from UIResourceId to ResourceId. + ui_resource_map_.clear(); } void LayerTreeHostImpl::CreateAndSetRenderer( @@ -2449,4 +2458,42 @@ void LayerTreeHostImpl::SetDebugState( SetFullRootLayerDamage(); } +void LayerTreeHostImpl::CreateUIResource( + UIResourceId uid, + scoped_refptr<UIResourceBitmap> bitmap) { + DCHECK_GT(uid, 0); + DCHECK_EQ(bitmap->GetFormat(), UIResourceBitmap::RGBA8); + + // Allow for multiple creation requests with the same UIResourceId. The + // previous resource is simply deleted. + ResourceProvider::ResourceId id = ResourceIdForUIResource(uid); + if (id) + DeleteUIResource(uid); + id = resource_provider_->CreateResource( + bitmap->GetSize(), GL_RGBA, ResourceProvider::TextureUsageAny); + + ui_resource_map_[uid] = id; + resource_provider_->SetPixels(id, + reinterpret_cast<uint8_t*>(bitmap->GetPixels()), + gfx::Rect(bitmap->GetSize()), + gfx::Rect(bitmap->GetSize()), + gfx::Vector2d(0, 0)); +} + +void LayerTreeHostImpl::DeleteUIResource(UIResourceId uid) { + ResourceProvider::ResourceId id = ResourceIdForUIResource(uid); + if (id) { + resource_provider_->DeleteResource(id); + ui_resource_map_.erase(uid); + } +} + +ResourceProvider::ResourceId LayerTreeHostImpl::ResourceIdForUIResource( + UIResourceId uid) const { + UIResourceMap::const_iterator iter = ui_resource_map_.find(uid); + if (iter != ui_resource_map_.end()) + return iter->second; + return 0; +} + } // namespace cc diff --git a/cc/trees/layer_tree_host_impl.h b/cc/trees/layer_tree_host_impl.h index f4663eb..6830d6d 100644 --- a/cc/trees/layer_tree_host_impl.h +++ b/cc/trees/layer_tree_host_impl.h @@ -5,10 +5,12 @@ #ifndef CC_TREES_LAYER_TREE_HOST_IMPL_H_ #define CC_TREES_LAYER_TREE_HOST_IMPL_H_ +#include <list> #include <string> #include <vector> #include "base/basictypes.h" +#include "base/containers/hash_tables.h" #include "base/memory/scoped_ptr.h" #include "base/time/time.h" #include "cc/animation/animation_events.h" @@ -24,7 +26,9 @@ #include "cc/output/output_surface_client.h" #include "cc/output/renderer.h" #include "cc/quads/render_pass.h" +#include "cc/resources/resource_provider.h" #include "cc/resources/tile_manager.h" +#include "skia/ext/refptr.h" #include "third_party/skia/include/core/SkColor.h" #include "ui/gfx/rect.h" @@ -42,9 +46,10 @@ class PaintTimeCounter; class MemoryHistory; class RenderingStatsInstrumentation; class RenderPassDrawQuad; -class ResourceProvider; class TopControlsManager; +class UIResourceBitmap; struct RendererCapabilities; +struct UIResourceRequest; // LayerTreeHost->Proxy callback interface. class LayerTreeHostImplClient { @@ -374,6 +379,13 @@ class CC_EXPORT LayerTreeHostImpl bool page_scale_animation_active() const { return !!page_scale_animation_; } + void CreateUIResource(UIResourceId uid, + scoped_refptr<UIResourceBitmap> bitmap); + // Deletes a UI resource. May safely be called more than once. + void DeleteUIResource(UIResourceId uid); + + ResourceProvider::ResourceId ResourceIdForUIResource(UIResourceId uid) const; + protected: LayerTreeHostImpl( const LayerTreeSettings& settings, @@ -443,6 +455,10 @@ class CC_EXPORT LayerTreeHostImpl void DidInitializeVisibleTile(); + typedef base::hash_map<UIResourceId, ResourceProvider::ResourceId> + UIResourceMap; + UIResourceMap ui_resource_map_; + scoped_ptr<OutputSurface> output_surface_; // |resource_provider_| and |tile_manager_| can be NULL, e.g. when using tile- diff --git a/cc/trees/layer_tree_host_impl_unittest.cc b/cc/trees/layer_tree_host_impl_unittest.cc index e25699e..f833655 100644 --- a/cc/trees/layer_tree_host_impl_unittest.cc +++ b/cc/trees/layer_tree_host_impl_unittest.cc @@ -6242,5 +6242,51 @@ TEST_F(LayerTreeHostImplTest, MemoryPolicy) { EXPECT_EQ(not_visible_cutoff_value, current_priority_cutoff_value_); } +TEST_F(LayerTreeHostImplTest, UIResourceManagement) { + scoped_ptr<TestWebGraphicsContext3D> context = + TestWebGraphicsContext3D::Create(); + TestWebGraphicsContext3D* context3d = context.get(); + scoped_ptr<OutputSurface> output_surface = FakeOutputSurface::Create3d( + context.PassAs<WebKit::WebGraphicsContext3D>()).PassAs<OutputSurface>(); + host_impl_->InitializeRenderer(output_surface.Pass()); + + EXPECT_EQ(0u, context3d->NumTextures()); + + UIResourceId ui_resource_id = 1; + scoped_refptr<UIResourceBitmap> bitmap = UIResourceBitmap::Create( + new uint8_t[1], UIResourceBitmap::RGBA8, gfx::Size(1, 1)); + host_impl_->CreateUIResource(ui_resource_id, bitmap); + EXPECT_EQ(1u, context3d->NumTextures()); + ResourceProvider::ResourceId id1 = + host_impl_->ResourceIdForUIResource(ui_resource_id); + EXPECT_NE(0u, id1); + + // Multiple requests with the same id is allowed. The previous texture is + // deleted. + host_impl_->CreateUIResource(ui_resource_id, bitmap); + EXPECT_EQ(1u, context3d->NumTextures()); + ResourceProvider::ResourceId id2 = + host_impl_->ResourceIdForUIResource(ui_resource_id); + EXPECT_NE(0u, id2); + EXPECT_NE(id1, id2); + + // Deleting invalid UIResourceId is allowed and does not change state. + host_impl_->DeleteUIResource(-1); + EXPECT_EQ(1u, context3d->NumTextures()); + + // Should return zero for invalid UIResourceId. Number of textures should + // not change. + EXPECT_EQ(0u, host_impl_->ResourceIdForUIResource(-1)); + EXPECT_EQ(1u, context3d->NumTextures()); + + host_impl_->DeleteUIResource(ui_resource_id); + EXPECT_EQ(0u, host_impl_->ResourceIdForUIResource(ui_resource_id)); + EXPECT_EQ(0u, context3d->NumTextures()); + + // Should not change state for multiple deletion on one UIResourceId + host_impl_->DeleteUIResource(ui_resource_id); + EXPECT_EQ(0u, context3d->NumTextures()); +} + } // namespace } // namespace cc diff --git a/cc/trees/layer_tree_host_unittest.cc b/cc/trees/layer_tree_host_unittest.cc index 41aa2e5..05a65b2 100644 --- a/cc/trees/layer_tree_host_unittest.cc +++ b/cc/trees/layer_tree_host_unittest.cc @@ -31,6 +31,7 @@ #include "cc/test/fake_picture_layer.h" #include "cc/test/fake_picture_layer_impl.h" #include "cc/test/fake_proxy.h" +#include "cc/test/fake_scoped_ui_resource.h" #include "cc/test/fake_scrollbar_layer.h" #include "cc/test/geometry_test_utils.h" #include "cc/test/layer_tree_test.h" @@ -1107,7 +1108,7 @@ class LayerTreeHostTestDeviceScaleFactorScalesViewportAndLayers MULTI_THREAD_TEST_F(LayerTreeHostTestDeviceScaleFactorScalesViewportAndLayers); // Verify atomicity of commits and reuse of textures. -class LayerTreeHostTestAtomicCommit : public LayerTreeHostTest { +class LayerTreeHostTestDirectRendererAtomicCommit : public LayerTreeHostTest { public: virtual void InitializeSettings(LayerTreeSettings* settings) OVERRIDE { // Make sure partial texture updates are turned off. @@ -1158,19 +1159,18 @@ class LayerTreeHostTestAtomicCommit : public LayerTreeHostTest { PostSetNeedsCommitToMainThread(); break; case 1: - // Number of textures should be doubled as the first textures - // are used by impl thread and cannot by used for update. - ASSERT_EQ(4u, context->NumTextures()); - // Number of textures used for commit should still be - // one for each layer. + // Number of textures should be one for scrollbar layer since it was + // requested and deleted on the impl-thread, and double for the content + // layer since its first texture is used by impl thread and cannot by + // used for update. + ASSERT_EQ(3u, context->NumTextures()); + // Number of textures used for commit should be one for each layer. EXPECT_EQ(2u, context->NumUsedTextures()); // First textures should not have been used. EXPECT_FALSE(context->UsedTexture(context->TextureAt(0))); - EXPECT_FALSE(context->UsedTexture(context->TextureAt(1))); + EXPECT_TRUE(context->UsedTexture(context->TextureAt(1))); // New textures should have been used. EXPECT_TRUE(context->UsedTexture(context->TextureAt(2))); - EXPECT_TRUE(context->UsedTexture(context->TextureAt(3))); - context->ResetUsedTextures(); PostSetNeedsCommitToMainThread(); break; @@ -1205,14 +1205,68 @@ class LayerTreeHostTestAtomicCommit : public LayerTreeHostTest { virtual void AfterTest() OVERRIDE {} - private: + protected: FakeContentLayerClient client_; scoped_refptr<FakeContentLayer> layer_; scoped_refptr<FakeScrollbarLayer> scrollbar_; int drew_frame_; }; -MULTI_THREAD_TEST_F(LayerTreeHostTestAtomicCommit); +MULTI_THREAD_DIRECT_RENDERER_TEST_F( + LayerTreeHostTestDirectRendererAtomicCommit); + +class LayerTreeHostTestDelegatingRendererAtomicCommit + : public LayerTreeHostTestDirectRendererAtomicCommit { + public: + virtual void DidActivateTreeOnThread(LayerTreeHostImpl* impl) OVERRIDE { + ASSERT_EQ(0u, layer_tree_host()->settings().max_partial_texture_updates); + + TestWebGraphicsContext3D* context = static_cast<TestWebGraphicsContext3D*>( + impl->output_surface()->context3d()); + + switch (impl->active_tree()->source_frame_number()) { + case 0: + // Number of textures should be one for each layer + ASSERT_EQ(2u, context->NumTextures()); + // Number of textures used for commit should be one for each layer. + EXPECT_EQ(2u, context->NumUsedTextures()); + // Verify that used texture is correct. + EXPECT_TRUE(context->UsedTexture(context->TextureAt(0))); + EXPECT_TRUE(context->UsedTexture(context->TextureAt(1))); + context->ResetUsedTextures(); + PostSetNeedsCommitToMainThread(); + break; + case 1: + // Number of textures should be doubled as the first context layer + // texture is being used by the impl-thread and cannot be used for + // update. The scrollbar behavior is different direct renderer because + // UI resource deletion with delegating renderer occurs after tree + // activation. + ASSERT_EQ(4u, context->NumTextures()); + // Number of textures used for commit should still be + // one for each layer. + EXPECT_EQ(2u, context->NumUsedTextures()); + // First textures should not have been used. + EXPECT_FALSE(context->UsedTexture(context->TextureAt(0))); + EXPECT_FALSE(context->UsedTexture(context->TextureAt(1))); + // New textures should have been used. + EXPECT_TRUE(context->UsedTexture(context->TextureAt(2))); + EXPECT_TRUE(context->UsedTexture(context->TextureAt(3))); + context->ResetUsedTextures(); + PostSetNeedsCommitToMainThread(); + break; + case 2: + EndTest(); + break; + default: + NOTREACHED(); + break; + } + } +}; + +MULTI_THREAD_DELEGATING_RENDERER_TEST_F( + LayerTreeHostTestDelegatingRendererAtomicCommit); static void SetLayerPropertiesForTesting(Layer* layer, Layer* parent, @@ -1237,8 +1291,6 @@ class LayerTreeHostTestAtomicCommitWithPartialUpdate virtual void InitializeSettings(LayerTreeSettings* settings) OVERRIDE { // Allow one partial texture update. settings->max_partial_texture_updates = 1; - // Linear fade animator prevents scrollbars from drawing immediately. - settings->use_linear_fade_scrollbar_animator = false; // No partial updates when impl side painting is enabled. settings->impl_side_painting = false; } @@ -1251,22 +1303,7 @@ class LayerTreeHostTestAtomicCommitWithPartialUpdate child_->SetPosition(gfx::Point(0, 10)); child_->SetBounds(gfx::Size(3, 10)); - bool paint_scrollbar = true; - bool has_thumb = false; - scrollbar_with_paints_ = - FakeScrollbarLayer::Create(paint_scrollbar, has_thumb, parent_->id()); - scrollbar_with_paints_->SetPosition(gfx::Point(3, 10)); - scrollbar_with_paints_->SetBounds(gfx::Size(3, 10)); - - paint_scrollbar = false; - scrollbar_without_paints_ = - FakeScrollbarLayer::Create(paint_scrollbar, has_thumb, parent_->id()); - scrollbar_without_paints_->SetPosition(gfx::Point(6, 10)); - scrollbar_without_paints_->SetBounds(gfx::Size(3, 10)); - parent_->AddChild(child_); - parent_->AddChild(scrollbar_with_paints_); - parent_->AddChild(scrollbar_without_paints_); layer_tree_host()->SetRootLayer(parent_); LayerTreeHostTest::SetupTree(); @@ -1281,22 +1318,14 @@ class LayerTreeHostTestAtomicCommitWithPartialUpdate case 1: parent_->SetNeedsDisplay(); child_->SetNeedsDisplay(); - scrollbar_with_paints_->SetNeedsDisplay(); - scrollbar_without_paints_->SetNeedsDisplay(); break; case 2: // Damage part of layers. parent_->SetNeedsDisplayRect(gfx::RectF(0.f, 0.f, 5.f, 5.f)); child_->SetNeedsDisplayRect(gfx::RectF(0.f, 0.f, 5.f, 5.f)); - scrollbar_with_paints_->SetNeedsDisplayRect( - gfx::RectF(0.f, 0.f, 5.f, 5.f)); - scrollbar_without_paints_->SetNeedsDisplayRect( - gfx::RectF(0.f, 0.f, 5.f, 5.f)); break; case 3: child_->SetNeedsDisplay(); - scrollbar_with_paints_->SetNeedsDisplay(); - scrollbar_without_paints_->SetNeedsDisplay(); layer_tree_host()->SetViewportSize(gfx::Size(10, 10)); break; case 4: @@ -1320,53 +1349,40 @@ class LayerTreeHostTestAtomicCommitWithPartialUpdate switch (impl->active_tree()->source_frame_number()) { case 0: // Number of textures should be one for each layer. - ASSERT_EQ(4u, context->NumTextures()); + ASSERT_EQ(2u, context->NumTextures()); // Number of textures used for commit should be one for each layer. - EXPECT_EQ(4u, context->NumUsedTextures()); + EXPECT_EQ(2u, context->NumUsedTextures()); // Verify that used textures are correct. EXPECT_TRUE(context->UsedTexture(context->TextureAt(0))); EXPECT_TRUE(context->UsedTexture(context->TextureAt(1))); - EXPECT_TRUE(context->UsedTexture(context->TextureAt(2))); - EXPECT_TRUE(context->UsedTexture(context->TextureAt(3))); - context->ResetUsedTextures(); break; case 1: - // Number of textures should be two for each content layer and one - // for each scrollbar, since they always do a partial update. - ASSERT_EQ(6u, context->NumTextures()); + // Number of textures should be two for each content layer. + ASSERT_EQ(4u, context->NumTextures()); // Number of textures used for commit should be one for each content - // layer, and one for the scrollbar layer that paints. - EXPECT_EQ(3u, context->NumUsedTextures()); + // layer. + EXPECT_EQ(2u, context->NumUsedTextures()); // First content textures should not have been used. EXPECT_FALSE(context->UsedTexture(context->TextureAt(0))); EXPECT_FALSE(context->UsedTexture(context->TextureAt(1))); - // The non-painting scrollbar's texture wasn't updated. - EXPECT_FALSE(context->UsedTexture(context->TextureAt(2))); - // The painting scrollbar's partial update texture was used. - EXPECT_TRUE(context->UsedTexture(context->TextureAt(3))); // New textures should have been used. - EXPECT_TRUE(context->UsedTexture(context->TextureAt(4))); - EXPECT_TRUE(context->UsedTexture(context->TextureAt(5))); + EXPECT_TRUE(context->UsedTexture(context->TextureAt(2))); + EXPECT_TRUE(context->UsedTexture(context->TextureAt(3))); context->ResetUsedTextures(); break; case 2: - // Number of textures should be two for each content layer and one - // for each scrollbar, since they always do a partial update. - ASSERT_EQ(6u, context->NumTextures()); + // Number of textures should be two for each content layer. + ASSERT_EQ(4u, context->NumTextures()); // Number of textures used for commit should be one for each content - // layer, and one for the scrollbar layer that paints. - EXPECT_EQ(3u, context->NumUsedTextures()); + // layer. + EXPECT_EQ(2u, context->NumUsedTextures()); - // The non-painting scrollbar's texture wasn't updated. - EXPECT_FALSE(context->UsedTexture(context->TextureAt(2))); - // The painting scrollbar does a partial update. - EXPECT_TRUE(context->UsedTexture(context->TextureAt(3))); // One content layer does a partial update also. - EXPECT_TRUE(context->UsedTexture(context->TextureAt(4))); - EXPECT_FALSE(context->UsedTexture(context->TextureAt(5))); + EXPECT_TRUE(context->UsedTexture(context->TextureAt(2))); + EXPECT_FALSE(context->UsedTexture(context->TextureAt(3))); context->ResetUsedTextures(); break; @@ -1377,10 +1393,9 @@ class LayerTreeHostTestAtomicCommitWithPartialUpdate context->ResetUsedTextures(); break; case 4: - // Number of textures used for commit should be two. One for the - // content layer, and one for the painting scrollbar. The - // non-painting scrollbar doesn't update its texture. - EXPECT_EQ(2u, context->NumUsedTextures()); + // Number of textures used for commit should be one, for the + // content layer. + EXPECT_EQ(1u, context->NumUsedTextures()); context->ResetUsedTextures(); break; @@ -1401,8 +1416,8 @@ class LayerTreeHostTestAtomicCommitWithPartialUpdate if (impl->active_tree()->source_frame_number() == 3) { EXPECT_EQ(1u, context->NumUsedTextures()); } else { - EXPECT_EQ(4u, context->NumUsedTextures()) << - "For frame " << impl->active_tree()->source_frame_number(); + EXPECT_EQ(2u, context->NumUsedTextures()) << + "For frame " << impl->active_tree()->source_frame_number(); } context->ResetUsedTextures(); @@ -1414,8 +1429,6 @@ class LayerTreeHostTestAtomicCommitWithPartialUpdate FakeContentLayerClient client_; scoped_refptr<FakeContentLayer> parent_; scoped_refptr<FakeContentLayer> child_; - scoped_refptr<FakeScrollbarLayer> scrollbar_with_paints_; - scoped_refptr<FakeScrollbarLayer> scrollbar_without_paints_; }; // Partial updates are not possible with a delegating renderer. @@ -1935,7 +1948,7 @@ class LayerTreeHostWithProxy : public LayerTreeHost { const LayerTreeSettings& settings, scoped_ptr<FakeProxy> proxy) : LayerTreeHost(client, settings) { - proxy->SetLayerTreeHost(this); + proxy->SetLayerTreeHost(this); EXPECT_TRUE(InitializeForTesting(proxy.PassAs<Proxy>())); } }; @@ -2372,7 +2385,7 @@ class MockIOSurfaceWebGraphicsContext3D : public FakeWebGraphicsContext3D { MOCK_METHOD1(activeTexture, void(WebKit::WGC3Denum texture)); MOCK_METHOD2(bindTexture, void(WebKit::WGC3Denum target, - WebKit::WebGLId texture_id)); + WebKit::WebGLId texture_id)); MOCK_METHOD3(texParameteri, void(WebKit::WGC3Denum target, WebKit::WGC3Denum pname, WebKit::WGC3Dint param)); @@ -3196,6 +3209,107 @@ class LayerTreeHostTestDeferredInitialize : public LayerTreeHostTest { MULTI_THREAD_TEST_F(LayerTreeHostTestDeferredInitialize); +// Test for UI Resource management. +class LayerTreeHostTestUIResource : public LayerTreeHostTest { + public: + LayerTreeHostTestUIResource() : num_ui_resources_(0), num_commits_(0) {} + + virtual void BeginTest() OVERRIDE { PostSetNeedsCommitToMainThread(); } + + virtual void DidCommit() OVERRIDE { + int frame = num_commits_; + switch (frame) { + case 1: + CreateResource(); + CreateResource(); + PostSetNeedsCommitToMainThread(); + break; + case 2: + // Usually ScopedUIResource are deleted from the manager in their + // destructor. Here we just want to test that a direct call to + // DeleteUIResource works. + layer_tree_host()->DeleteUIResource(ui_resources_[0]->id()); + PostSetNeedsCommitToMainThread(); + break; + case 3: + // DeleteUIResource can be called with an invalid id. + layer_tree_host()->DeleteUIResource(ui_resources_[0]->id()); + PostSetNeedsCommitToMainThread(); + break; + case 4: + CreateResource(); + CreateResource(); + PostSetNeedsCommitToMainThread(); + break; + case 5: + ClearResources(); + EndTest(); + break; + } + } + + void PerformTest(LayerTreeHostImpl* impl) { + TestWebGraphicsContext3D* context = static_cast<TestWebGraphicsContext3D*>( + impl->output_surface()->context3d()); + + int frame = num_commits_; + switch (frame) { + case 1: + ASSERT_EQ(0u, context->NumTextures()); + break; + case 2: + // Created two textures. + ASSERT_EQ(2u, context->NumTextures()); + break; + case 3: + // One texture left after one deletion. + ASSERT_EQ(1u, context->NumTextures()); + break; + case 4: + // Resource manager state should not change when delete is called on an + // invalid id. + ASSERT_EQ(1u, context->NumTextures()); + break; + case 5: + // Creation after deletion: two more creates should total up to + // three textures. + ASSERT_EQ(3u, context->NumTextures()); + break; + } + } + + virtual void CommitCompleteOnThread(LayerTreeHostImpl* impl) OVERRIDE { + ++num_commits_; + if (!layer_tree_host()->settings().impl_side_painting) + PerformTest(impl); + } + + virtual void DidActivateTreeOnThread(LayerTreeHostImpl* impl) OVERRIDE { + if (layer_tree_host()->settings().impl_side_painting) + PerformTest(impl); + } + + virtual void AfterTest() OVERRIDE {} + + private: + // Must clear all resources before exiting. + void ClearResources() { + for (int i = 0; i < num_ui_resources_; i++) + ui_resources_[i].reset(); + } + + void CreateResource() { + ui_resources_[num_ui_resources_++] = + FakeScopedUIResource::Create(layer_tree_host()); + } + + scoped_ptr<FakeScopedUIResource> ui_resources_[5]; + int num_ui_resources_; + int num_commits_; +}; + +MULTI_THREAD_TEST_F(LayerTreeHostTestUIResource); + class PushPropertiesCountingLayer : public Layer { public: static scoped_refptr<PushPropertiesCountingLayer> Create() { diff --git a/cc/trees/layer_tree_host_unittest_context.cc b/cc/trees/layer_tree_host_unittest_context.cc index 3c4d08e..1787d31 100644 --- a/cc/trees/layer_tree_host_unittest_context.cc +++ b/cc/trees/layer_tree_host_unittest_context.cc @@ -24,6 +24,7 @@ #include "cc/test/fake_delegated_renderer_layer_impl.h" #include "cc/test/fake_layer_tree_host_client.h" #include "cc/test/fake_output_surface.h" +#include "cc/test/fake_scoped_ui_resource.h" #include "cc/test/fake_scrollbar.h" #include "cc/test/fake_scrollbar_layer.h" #include "cc/test/fake_video_frame_provider.h" @@ -1528,21 +1529,17 @@ class ScrollbarLayerLostContext : public LayerTreeHostContextTest { LayerTreeHostContextTest::CommitCompleteOnThread(impl); ++commits_; - size_t upload_count = scrollbar_layer_->last_update_full_upload_size() + - scrollbar_layer_->last_update_partial_upload_size(); switch (commits_) { case 1: // First (regular) update, we should upload 2 resources (thumb, and // backtrack). EXPECT_EQ(1, scrollbar_layer_->update_count()); - EXPECT_EQ(2u, upload_count); LoseContext(); break; case 2: // Second update, after the lost context, we should still upload 2 // resources even if the contents haven't changed. EXPECT_EQ(2, scrollbar_layer_->update_count()); - EXPECT_EQ(2u, upload_count); EndTest(); break; default: @@ -1630,5 +1627,279 @@ class LayerTreeHostTestCannotCreateIfCannotCreateOutputSurface SINGLE_AND_MULTI_THREAD_TEST_F( LayerTreeHostTestCannotCreateIfCannotCreateOutputSurface); +class UIResourceLostTest : public LayerTreeHostContextTest { + public: + UIResourceLostTest() : time_step_(0) {} + virtual void BeginTest() OVERRIDE { PostSetNeedsCommitToMainThread(); } + virtual void AfterTest() OVERRIDE {} + + protected: + int time_step_; + scoped_ptr<FakeScopedUIResource> ui_resource_; +}; + +// Losing context after an UI resource has been created. +class UIResourceLostAfterCommit : public UIResourceLostTest { + public: + virtual void CommitCompleteOnThread(LayerTreeHostImpl* impl) OVERRIDE { + LayerTreeHostContextTest::CommitCompleteOnThread(impl); + switch (time_step_) { + case 0: + ui_resource_ = FakeScopedUIResource::Create(layer_tree_host()); + // Expects a valid UIResourceId. + EXPECT_NE(0, ui_resource_->id()); + PostSetNeedsCommitToMainThread(); + break; + case 1: + // The resource should have been created on LTHI after the commit. + if (!layer_tree_host()->settings().impl_side_painting) + EXPECT_NE(0u, impl->ResourceIdForUIResource(ui_resource_->id())); + PostSetNeedsCommitToMainThread(); + break; + case 2: + LoseContext(); + break; + case 3: + // The resources should have been recreated. The bitmap callback should + // have been called once with the resource_lost flag set to true. + EXPECT_EQ(1, ui_resource_->lost_resource_count); + // Resource Id on the impl-side have been recreated as well. Note + // that the same UIResourceId persists after the context lost. + if (!layer_tree_host()->settings().impl_side_painting) + EXPECT_NE(0u, impl->ResourceIdForUIResource(ui_resource_->id())); + PostSetNeedsCommitToMainThread(); + break; + case 4: + // Release resource before ending test. + ui_resource_.reset(); + EndTest(); + break; + } + } + + virtual void DidActivateTreeOnThread(LayerTreeHostImpl* impl) OVERRIDE { + LayerTreeHostContextTest::DidActivateTreeOnThread(impl); + switch (time_step_) { + case 1: + if (layer_tree_host()->settings().impl_side_painting) + EXPECT_NE(0u, impl->ResourceIdForUIResource(ui_resource_->id())); + break; + case 3: + if (layer_tree_host()->settings().impl_side_painting) + EXPECT_NE(0u, impl->ResourceIdForUIResource(ui_resource_->id())); + break; + } + ++time_step_; + } +}; + +SINGLE_AND_MULTI_THREAD_TEST_F(UIResourceLostAfterCommit); + +// Losing context before UI resource requests can be commited. Three sequences +// of creation/deletion are considered: +// 1. Create one resource -> Context Lost => Expect the resource to have been +// created. +// 2. Delete an exisiting resource (test_id0_) -> create a second resource +// (test_id1_) -> Context Lost => Expect the test_id0_ to be removed and +// test_id1_ to have been created. +// 3. Create one resource -> Delete that same resource -> Context Lost => Expect +// the resource to not exist in the manager. +class UIResourceLostBeforeCommit : public UIResourceLostTest { + public: + UIResourceLostBeforeCommit() + : test_id0_(0), + test_id1_(0) {} + + virtual void CommitCompleteOnThread(LayerTreeHostImpl* impl) OVERRIDE { + LayerTreeHostContextTest::CommitCompleteOnThread(impl); + switch (time_step_) { + case 0: + // Sequence 1: + ui_resource_ = FakeScopedUIResource::Create(layer_tree_host()); + LoseContext(); + // Resource Id on the impl-side should no longer be valid after + // context is lost. + EXPECT_EQ(0u, impl->ResourceIdForUIResource(ui_resource_->id())); + break; + case 1: + // The resources should have been recreated. + EXPECT_EQ(2, ui_resource_->resource_create_count); + // "resource lost" callback was called once for the resource in the + // resource map. + EXPECT_EQ(1, ui_resource_->lost_resource_count); + // Resource Id on the impl-side have been recreated as well. Note + // that the same UIResourceId persists after the context lost. + if (!layer_tree_host()->settings().impl_side_painting) + EXPECT_NE(0u, impl->ResourceIdForUIResource(ui_resource_->id())); + PostSetNeedsCommitToMainThread(); + break; + case 2: + // Sequence 2: + // Currently one resource has been created. + test_id0_ = ui_resource_->id(); + // Delete this resource. + ui_resource_.reset(); + // Create another resource. + ui_resource_ = FakeScopedUIResource::Create(layer_tree_host()); + test_id1_ = ui_resource_->id(); + // Sanity check that two resource creations return different ids. + EXPECT_NE(test_id0_, test_id1_); + // Lose the context before commit. + LoseContext(); + break; + case 3: + if (!layer_tree_host()->settings().impl_side_painting) { + // The previous resource should have been deleted. + EXPECT_EQ(0u, impl->ResourceIdForUIResource(test_id0_)); + // The second resource should have been created. + EXPECT_NE(0u, impl->ResourceIdForUIResource(test_id1_)); + } + + // The second resource called the resource callback once and since the + // context is lost, a "resource lost" callback was also issued. + EXPECT_EQ(2, ui_resource_->resource_create_count); + EXPECT_EQ(1, ui_resource_->lost_resource_count); + // Clear the manager of resources. + ui_resource_.reset(); + PostSetNeedsCommitToMainThread(); + break; + case 4: + // Sequence 3: + ui_resource_ = FakeScopedUIResource::Create(layer_tree_host()); + test_id0_ = ui_resource_->id(); + // Sanity check the UIResourceId should not be 0. + EXPECT_NE(0, test_id0_); + // Usually ScopedUIResource are deleted from the manager in their + // destructor (so usually ui_resource_.reset()). But here we need + // ui_resource_ for the next step, so call DeleteUIResource directly. + layer_tree_host()->DeleteUIResource(test_id0_); + LoseContext(); + break; + case 5: + // Expect the resource callback to have been called once. + EXPECT_EQ(1, ui_resource_->resource_create_count); + // No "resource lost" callbacks. + EXPECT_EQ(0, ui_resource_->lost_resource_count); + if (!layer_tree_host()->settings().impl_side_painting) { + // The UI resource id should not be valid + EXPECT_EQ(0u, impl->ResourceIdForUIResource(test_id0_)); + } + PostSetNeedsCommitToMainThread(); + break; + case 6: + ui_resource_.reset(); + EndTest(); + break; + } + } + + virtual void DidActivateTreeOnThread(LayerTreeHostImpl* impl) OVERRIDE { + LayerTreeHostContextTest::DidActivateTreeOnThread(impl); + switch (time_step_) { + case 1: + if (layer_tree_host()->settings().impl_side_painting) + EXPECT_NE(0u, impl->ResourceIdForUIResource(ui_resource_->id())); + break; + case 3: + if (layer_tree_host()->settings().impl_side_painting) { + EXPECT_EQ(0u, impl->ResourceIdForUIResource(test_id0_)); + EXPECT_NE(0u, impl->ResourceIdForUIResource(test_id1_)); + } + break; + case 5: + if (layer_tree_host()->settings().impl_side_painting) + EXPECT_EQ(0u, impl->ResourceIdForUIResource(test_id0_)); + break; + } + ++time_step_; + } + + private: + UIResourceId test_id0_; + UIResourceId test_id1_; +}; + +SINGLE_AND_MULTI_THREAD_TEST_F(UIResourceLostBeforeCommit); + +// Losing UI resource before the pending trees is activated but after the +// commit. Impl-side-painting only. +class UIResourceLostBeforeActivateTree : public UIResourceLostTest { + virtual void CommitCompleteOnThread(LayerTreeHostImpl* impl) OVERRIDE { + LayerTreeHostContextTest::CommitCompleteOnThread(impl); + switch (time_step_) { + case 0: + ui_resource_ = FakeScopedUIResource::Create(layer_tree_host()); + PostSetNeedsCommitToMainThread(); + break; + case 2: + PostSetNeedsCommitToMainThread(); + break; + case 3: + test_id_ = ui_resource_->id(); + ui_resource_.reset(); + PostSetNeedsCommitToMainThread(); + break; + case 4: + PostSetNeedsCommitToMainThread(); + break; + case 5: + EndTest(); + break; + } + } + + virtual void WillActivateTreeOnThread(LayerTreeHostImpl* impl) OVERRIDE { + switch (time_step_) { + case 0: + break; + case 1: + // The resource creation callback has been called. + EXPECT_EQ(1, ui_resource_->resource_create_count); + // The resource is not yet lost (sanity check). + EXPECT_EQ(0, ui_resource_->lost_resource_count); + // The resource should not have been created yet on the impl-side. + EXPECT_EQ(0u, impl->ResourceIdForUIResource(ui_resource_->id())); + LoseContext(); + break; + case 3: + LoseContext(); + break; + } + } + + virtual void DidActivateTreeOnThread(LayerTreeHostImpl* impl) OVERRIDE { + LayerTreeHostContextTest::DidActivateTreeOnThread(impl); + switch (time_step_) { + case 1: + // The pending requests on the impl-side should have been processed. + EXPECT_NE(0u, impl->ResourceIdForUIResource(ui_resource_->id())); + break; + case 2: + // The "lost resource" callback should have been called once. + EXPECT_EQ(1, ui_resource_->lost_resource_count); + break; + case 4: + // The resource is deleted and should not be in the manager. Use + // test_id_ since ui_resource_ has been deleted. + EXPECT_EQ(0u, impl->ResourceIdForUIResource(test_id_)); + break; + } + ++time_step_; + } + + private: + UIResourceId test_id_; +}; + +TEST_F(UIResourceLostBeforeActivateTree, + RunMultiThread_DirectRenderer_ImplSidePaint) { + RunTest(true, false, true); +} + +TEST_F(UIResourceLostBeforeActivateTree, + RunMultiThread_DelegatingRenderer_ImplSidePaint) { + RunTest(true, true, true); +} + } // namespace } // namespace cc diff --git a/cc/trees/layer_tree_impl.cc b/cc/trees/layer_tree_impl.cc index d779507..be25e65 100644 --- a/cc/trees/layer_tree_impl.cc +++ b/cc/trees/layer_tree_impl.cc @@ -104,6 +104,9 @@ scoped_ptr<LayerImpl> LayerTreeImpl::DetachLayerTree() { } void LayerTreeImpl::PushPropertiesTo(LayerTreeImpl* target_tree) { + // The request queue should have been processed and does not require a push. + DCHECK_EQ(ui_resource_request_queue_.size(), 0u); + target_tree->SetLatencyInfo(latency_info_); latency_info_.Clear(); target_tree->SetPageScaleFactorAndLimits( @@ -282,7 +285,7 @@ void LayerTreeImpl::UpdateDrawProperties() { // For max_texture_size. if (!layer_tree_host_impl_->renderer()) - return; + return; if (!root_layer()) return; @@ -529,7 +532,7 @@ scoped_ptr<base::Value> LayerTreeImpl::AsValue() const { } void LayerTreeImpl::SetRootLayerScrollOffsetDelegate( - LayerScrollOffsetDelegate* root_layer_scroll_offset_delegate) { + LayerScrollOffsetDelegate* root_layer_scroll_offset_delegate) { root_layer_scroll_offset_delegate_ = root_layer_scroll_offset_delegate; if (root_scroll_layer_) { root_scroll_layer_->SetScrollOffsetDelegate( @@ -572,6 +575,35 @@ void LayerTreeImpl::WillModifyTilePriorities() { layer_tree_host_impl_->SetNeedsManageTiles(); } +void LayerTreeImpl::set_ui_resource_request_queue( + const UIResourceRequestQueue& queue) { + ui_resource_request_queue_ = queue; +} + +ResourceProvider::ResourceId LayerTreeImpl::ResourceIdForUIResource( + UIResourceId uid) const { + return layer_tree_host_impl_->ResourceIdForUIResource(uid); +} + +void LayerTreeImpl::ProcessUIResourceRequestQueue() { + while (ui_resource_request_queue_.size() > 0) { + UIResourceRequest req = ui_resource_request_queue_.front(); + ui_resource_request_queue_.pop_front(); + + switch (req.type) { + case UIResourceRequest::UIResourceCreate: + layer_tree_host_impl_->CreateUIResource(req.id, req.bitmap); + break; + case UIResourceRequest::UIResourceDelete: + layer_tree_host_impl_->DeleteUIResource(req.id); + break; + default: + NOTREACHED(); + break; + } + } +} + void LayerTreeImpl::AddLayerWithCopyOutputRequest(LayerImpl* layer) { // Only the active tree needs to know about layers with copy requests, as // they are aborted if not serviced during draw. diff --git a/cc/trees/layer_tree_impl.h b/cc/trees/layer_tree_impl.h index 27aa6fa..7e66da8 100644 --- a/cc/trees/layer_tree_impl.h +++ b/cc/trees/layer_tree_impl.h @@ -5,12 +5,14 @@ #ifndef CC_TREES_LAYER_TREE_IMPL_H_ #define CC_TREES_LAYER_TREE_IMPL_H_ +#include <list> #include <string> #include <vector> #include "base/containers/hash_tables.h" #include "base/values.h" #include "cc/layers/layer_impl.h" +#include "cc/resources/ui_resource_client.h" #include "ui/base/latency_info.h" #if defined(COMPILER_GCC) @@ -40,6 +42,9 @@ class Proxy; class ResourceProvider; class TileManager; struct RendererCapabilities; +struct UIResourceRequest; + +typedef std::list<UIResourceRequest> UIResourceRequestQueue; class CC_EXPORT LayerTreeImpl { public: @@ -150,6 +155,8 @@ class CC_EXPORT LayerTreeImpl { void set_needs_full_tree_sync(bool needs) { needs_full_tree_sync_ = needs; } bool needs_full_tree_sync() const { return needs_full_tree_sync_; } + void set_ui_resource_request_queue(const UIResourceRequestQueue& queue); + const LayerImplList& RenderSurfaceLayerList() const; // These return the size of the root scrollable area and the size of @@ -191,6 +198,9 @@ class CC_EXPORT LayerTreeImpl { void WillModifyTilePriorities(); + ResourceProvider::ResourceId ResourceIdForUIResource(UIResourceId uid) const; + void ProcessUIResourceRequestQueue(); + void AddLayerWithCopyOutputRequest(LayerImpl* layer); void RemoveLayerWithCopyOutputRequest(LayerImpl* layer); const std::vector<LayerImpl*> LayersWithCopyOutputRequest() const; @@ -240,6 +250,8 @@ class CC_EXPORT LayerTreeImpl { ui::LatencyInfo latency_info_; + UIResourceRequestQueue ui_resource_request_queue_; + private: DISALLOW_COPY_AND_ASSIGN(LayerTreeImpl); }; |