diff options
author | danakj@chromium.org <danakj@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-04-26 03:53:04 +0000 |
---|---|---|
committer | danakj@chromium.org <danakj@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-04-26 03:53:04 +0000 |
commit | 45a695dae15e0e2df2887a2b280d28a5ffe3da43 (patch) | |
tree | 1119521f4e9c19a742483ffe839ef59acb219f87 | |
parent | fe3666132dcce324e06f99d116ce0c659617a6eb (diff) | |
download | chromium_src-45a695dae15e0e2df2887a2b280d28a5ffe3da43.zip chromium_src-45a695dae15e0e2df2887a2b280d28a5ffe3da43.tar.gz chromium_src-45a695dae15e0e2df2887a2b280d28a5ffe3da43.tar.bz2 |
cc: Async readback.
Provide a path to get a readback of a layer's subtree via an
asynchronous mechanism.
This path is used for all the cc pixel tests, to show that it
works. Also by some unit tests:
LayerTreeHostTestAsyncReadback.GLRenderer_RunSingleThread
LayerTreeHostTestAsyncReadback.GLRenderer_RunMultiThread
LayerTreeHostTestAsyncReadback.SoftwareRenderer_RunSingleThread
LayerTreeHostTestAsyncReadback.SoftwareRenderer_RunMultiThread
LayerTreeHostTestAsyncReadbackLayerDestroyed.RunSingleThread
LayerTreeHostTestAsyncReadbackLayerDestroyed.RunMultiThread
BUG=179896
Review URL: https://chromiumcodereview.appspot.com/14060015
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@196592 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | cc/layers/layer.cc | 38 | ||||
-rw-r--r-- | cc/layers/layer.h | 15 | ||||
-rw-r--r-- | cc/layers/layer_impl.cc | 30 | ||||
-rw-r--r-- | cc/layers/layer_impl.h | 11 | ||||
-rw-r--r-- | cc/output/direct_renderer.cc | 13 | ||||
-rw-r--r-- | cc/output/direct_renderer.h | 2 | ||||
-rw-r--r-- | cc/output/gl_renderer.cc | 13 | ||||
-rw-r--r-- | cc/output/gl_renderer.h | 2 | ||||
-rw-r--r-- | cc/output/gl_renderer_pixeltest.cc | 56 | ||||
-rw-r--r-- | cc/output/software_renderer.cc | 9 | ||||
-rw-r--r-- | cc/output/software_renderer.h | 2 | ||||
-rw-r--r-- | cc/quads/render_pass.cc | 6 | ||||
-rw-r--r-- | cc/quads/render_pass.h | 10 | ||||
-rw-r--r-- | cc/quads/render_pass_unittest.cc | 5 | ||||
-rw-r--r-- | cc/resources/resource_provider.cc | 5 | ||||
-rw-r--r-- | cc/test/layer_tree_pixel_test.cc | 24 | ||||
-rw-r--r-- | cc/test/layer_tree_pixel_test.h | 4 | ||||
-rw-r--r-- | cc/test/pixel_test.cc | 38 | ||||
-rw-r--r-- | cc/test/pixel_test.h | 8 | ||||
-rw-r--r-- | cc/trees/layer_tree_host_common.cc | 4 | ||||
-rw-r--r-- | cc/trees/layer_tree_host_impl.cc | 15 | ||||
-rw-r--r-- | cc/trees/layer_tree_host_unittest.cc | 193 |
22 files changed, 431 insertions, 72 deletions
diff --git a/cc/layers/layer.cc b/cc/layers/layer.cc index 43a0335..6584e71 100644 --- a/cc/layers/layer.cc +++ b/cc/layers/layer.cc @@ -10,6 +10,7 @@ #include "cc/animation/animation.h" #include "cc/animation/animation_events.h" #include "cc/animation/layer_animation_controller.h" +#include "cc/base/thread.h" #include "cc/layers/layer_impl.h" #include "cc/trees/layer_tree_host.h" #include "cc/trees/layer_tree_impl.h" @@ -69,6 +70,9 @@ Layer::~Layer() { // way for us to be destroyed while we still have a parent. DCHECK(!parent()); + for (size_t i = 0; i < request_copy_callbacks_.size(); ++i) + request_copy_callbacks_[i].Run(scoped_ptr<SkBitmap>()); + layer_animation_controller_->RemoveValueObserver(this); // Remove the parent reference from all children and dependents. @@ -301,6 +305,14 @@ void Layer::SetChildren(const LayerList& children) { AddChild(children[i]); } +void Layer::RequestCopyAsBitmap(RequestCopyAsBitmapCallback callback) { + DCHECK(IsPropertyChangeAllowed()); + if (callback.is_null()) + return; + request_copy_callbacks_.push_back(callback); + SetNeedsCommit(); +} + void Layer::SetAnchorPoint(gfx::PointF anchor_point) { DCHECK(IsPropertyChangeAllowed()); if (anchor_point_ == anchor_point) @@ -610,6 +622,21 @@ void Layer::SetPositionConstraint(const LayerPositionConstraint& constraint) { SetNeedsCommit(); } +static void RunCopyCallbackOnMainThread( + const Layer::RequestCopyAsBitmapCallback& callback, + scoped_ptr<SkBitmap> bitmap) { + callback.Run(bitmap.Pass()); +} + +static void PostCopyCallbackToMainThread( + Thread* main_thread, + const Layer::RequestCopyAsBitmapCallback& callback, + scoped_ptr<SkBitmap> bitmap) { + main_thread->PostTask(base::Bind(&RunCopyCallbackOnMainThread, + callback, + base::Passed(&bitmap))); +} + void Layer::PushPropertiesTo(LayerImpl* layer) { layer->SetAnchorPoint(anchor_point_); layer->SetAnchorPointZ(anchor_point_z_); @@ -651,6 +678,17 @@ void Layer::PushPropertiesTo(LayerImpl* layer) { layer->SetScrollOffset(scroll_offset_); layer->SetMaxScrollOffset(max_scroll_offset_); + // Wrap the request_copy_callbacks_ in a PostTask to the main thread. + std::vector<RequestCopyAsBitmapCallback> main_thread_request_copy_callbacks; + for (size_t i = 0; i < request_copy_callbacks_.size(); ++i) { + main_thread_request_copy_callbacks.push_back( + base::Bind(&PostCopyCallbackToMainThread, + layer_tree_host()->proxy()->MainThread(), + request_copy_callbacks_[i])); + } + request_copy_callbacks_.clear(); + layer->PassRequestCopyCallbacks(&main_thread_request_copy_callbacks); + // If the main thread commits multiple times before the impl thread actually // draws, then damage tracking will become incorrect if we simply clobber the // update_rect here. The LayerImpl's update_rect needs to accumulate (i.e. diff --git a/cc/layers/layer.h b/cc/layers/layer.h index 8411964..a35d7a5 100644 --- a/cc/layers/layer.h +++ b/cc/layers/layer.h @@ -8,6 +8,7 @@ #include <string> #include <vector> +#include "base/callback.h" #include "base/memory/ref_counted.h" #include "base/observer_list.h" #include "cc/animation/layer_animation_controller.h" @@ -76,6 +77,18 @@ class CC_EXPORT Layer : public base::RefCounted<Layer>, const LayerList& children() const { return children_; } Layer* child_at(size_t index) { return children_[index].get(); } + typedef base::Callback<void(scoped_ptr<SkBitmap>)> + RequestCopyAsBitmapCallback; + + // This requests the layer and its subtree be rendered into an SkBitmap and + // call the given callback when the SkBitmap has been produced. If the copy + // is unable to be produced (the layer is destroyed first), then the callback + // is called with a NULL bitmap. + void RequestCopyAsBitmap(RequestCopyAsBitmapCallback callback); + bool HasRequestCopyCallback() const { + return !request_copy_callbacks_.empty(); + } + void SetAnchorPoint(gfx::PointF anchor_point); gfx::PointF anchor_point() const { return anchor_point_; } @@ -480,6 +493,8 @@ class CC_EXPORT Layer : public base::RefCounted<Layer>, gfx::Transform impl_transform_; + std::vector<RequestCopyAsBitmapCallback> request_copy_callbacks_; + WebKit::WebLayerScrollClient* layer_scroll_client_; DrawProperties<Layer, RenderSurface> draw_properties_; diff --git a/cc/layers/layer_impl.cc b/cc/layers/layer_impl.cc index aec8cd2..f7aecf1 100644 --- a/cc/layers/layer_impl.cc +++ b/cc/layers/layer_impl.cc @@ -69,6 +69,10 @@ LayerImpl::~LayerImpl() { #ifndef NDEBUG DCHECK(!between_will_draw_and_did_draw_); #endif + + for (size_t i = 0; i < request_copy_callbacks_.size(); ++i) + request_copy_callbacks_[i].Run(scoped_ptr<SkBitmap>()); + layer_tree_impl_->UnregisterLayer(this); layer_animation_controller_->RemoveValueObserver(this); } @@ -102,6 +106,30 @@ void LayerImpl::ClearChildList() { layer_tree_impl()->set_needs_update_draw_properties(); } +void LayerImpl::PassRequestCopyCallbacks( + std::vector<RenderPass::RequestCopyAsBitmapCallback>* callbacks) { + if (callbacks->empty()) + return; + + request_copy_callbacks_.insert(request_copy_callbacks_.end(), + callbacks->begin(), + callbacks->end()); + callbacks->clear(); + + NoteLayerPropertyChangedForSubtree(); +} + +void LayerImpl::TakeRequestCopyCallbacks( + std::vector<RenderPass::RequestCopyAsBitmapCallback>* callbacks) { + if (request_copy_callbacks_.empty()) + return; + + callbacks->insert(callbacks->end(), + request_copy_callbacks_.begin(), + request_copy_callbacks_.end()); + request_copy_callbacks_.clear(); +} + void LayerImpl::CreateRenderSurface() { DCHECK(!draw_properties_.render_surface); draw_properties_.render_surface = @@ -351,6 +379,8 @@ void LayerImpl::PushPropertiesTo(LayerImpl* layer) { layer->SetScrollOffset(scroll_offset_); layer->SetMaxScrollOffset(max_scroll_offset_); + layer->PassRequestCopyCallbacks(&request_copy_callbacks_); + // If the main thread commits multiple times before the impl thread actually // draws, then damage tracking will become incorrect if we simply clobber the // update_rect here. The LayerImpl's update_rect needs to accumulate (i.e. diff --git a/cc/layers/layer_impl.h b/cc/layers/layer_impl.h index 19aa727..2333e1d 100644 --- a/cc/layers/layer_impl.h +++ b/cc/layers/layer_impl.h @@ -6,6 +6,7 @@ #define CC_LAYERS_LAYER_IMPL_H_ #include <string> +#include <vector> #include "base/logging.h" #include "base/memory/scoped_ptr.h" @@ -75,6 +76,14 @@ class CC_EXPORT LayerImpl : LayerAnimationValueObserver { // Warning: This does not preserve tree structure invariants. void ClearChildList(); + void PassRequestCopyCallbacks( + std::vector<RenderPass::RequestCopyAsBitmapCallback>* callbacks); + void TakeRequestCopyCallbacks( + std::vector<RenderPass::RequestCopyAsBitmapCallback>* callbacks); + bool HasRequestCopyCallback() const { + return !request_copy_callbacks_.empty(); + } + void SetMaskLayer(scoped_ptr<LayerImpl> mask_layer); LayerImpl* mask_layer() { return mask_layer_.get(); } const LayerImpl* mask_layer() const { return mask_layer_.get(); } @@ -529,6 +538,8 @@ class CC_EXPORT LayerImpl : LayerAnimationValueObserver { ScrollbarLayerImpl* horizontal_scrollbar_layer_; ScrollbarLayerImpl* vertical_scrollbar_layer_; + std::vector<RenderPass::RequestCopyAsBitmapCallback> request_copy_callbacks_; + // Group of properties that need to be computed based on the layer tree // hierarchy before layers can be drawn. DrawProperties<LayerImpl, RenderSurfaceImpl> draw_properties_; diff --git a/cc/output/direct_renderer.cc b/cc/output/direct_renderer.cc index ae8ff9b..7d0083f 100644 --- a/cc/output/direct_renderer.cc +++ b/cc/output/direct_renderer.cc @@ -8,6 +8,7 @@ #include <vector> #include "base/debug/trace_event.h" +#include "base/hash_tables.h" #include "base/metrics/histogram.h" #include "cc/base/math_util.h" #include "cc/quads/draw_quad.h" @@ -192,9 +193,19 @@ void DirectRenderer::DrawFrame(RenderPassList* render_passes_in_draw_order) { root_render_pass->damage_rect : root_render_pass->output_rect; frame.root_damage_rect.Intersect(gfx::Rect(ViewportSize())); + std::vector<base::Closure> copy_callbacks; + BeginDrawingFrame(&frame); - for (size_t i = 0; i < render_passes_in_draw_order->size(); ++i) + for (size_t i = 0; i < render_passes_in_draw_order->size(); ++i) { DrawRenderPass(&frame, render_passes_in_draw_order->at(i)); + + const RenderPass* pass = frame.current_render_pass; + for (size_t i = 0; i < pass->copy_callbacks.size(); ++i) { + scoped_ptr<SkBitmap> bitmap(new SkBitmap); + CopyCurrentRenderPassToBitmap(&frame, bitmap.get()); + pass->copy_callbacks[i].Run(bitmap.Pass()); + } + } FinishDrawingFrame(&frame); render_passes_in_draw_order->clear(); diff --git a/cc/output/direct_renderer.h b/cc/output/direct_renderer.h index bb3095a..4153750 100644 --- a/cc/output/direct_renderer.h +++ b/cc/output/direct_renderer.h @@ -109,6 +109,8 @@ class CC_EXPORT DirectRenderer : public Renderer { virtual bool FlippedFramebuffer() const = 0; virtual void EnsureScissorTestEnabled() = 0; virtual void EnsureScissorTestDisabled() = 0; + virtual void CopyCurrentRenderPassToBitmap(DrawingFrame* frame, + SkBitmap* bitmap) = 0; ScopedPtrHashMap<RenderPass::Id, CachedResource> render_pass_textures_; ResourceProvider* resource_provider_; diff --git a/cc/output/gl_renderer.cc b/cc/output/gl_renderer.cc index 19b215b..4a0c7ba 100644 --- a/cc/output/gl_renderer.cc +++ b/cc/output/gl_renderer.cc @@ -1776,6 +1776,19 @@ void GLRenderer::EnsureScissorTestDisabled() { is_scissor_enabled_ = false; } +void GLRenderer::CopyCurrentRenderPassToBitmap(DrawingFrame* frame, + SkBitmap* bitmap) { + gfx::Size render_pass_size = frame->current_render_pass->output_rect.size(); + bitmap->setConfig(SkBitmap::kARGB_8888_Config, + render_pass_size.width(), + render_pass_size.height()); + if (bitmap->allocPixels()) { + bitmap->lockPixels(); + GetFramebufferPixels(bitmap->getPixels(), gfx::Rect(render_pass_size)); + bitmap->unlockPixels(); + } +} + void GLRenderer::ToGLMatrix(float* gl_matrix, const gfx::Transform& transform) { transform.matrix().asColMajorf(gl_matrix); } diff --git a/cc/output/gl_renderer.h b/cc/output/gl_renderer.h index 091d94e..b05d02b 100644 --- a/cc/output/gl_renderer.h +++ b/cc/output/gl_renderer.h @@ -103,6 +103,8 @@ class CC_EXPORT GLRenderer virtual bool FlippedFramebuffer() const OVERRIDE; virtual void EnsureScissorTestEnabled() OVERRIDE; virtual void EnsureScissorTestDisabled() OVERRIDE; + virtual void CopyCurrentRenderPassToBitmap(DrawingFrame* frame, + SkBitmap* bitmap) OVERRIDE; virtual void FinishDrawingQuadList() OVERRIDE; private: diff --git a/cc/output/gl_renderer_pixeltest.cc b/cc/output/gl_renderer_pixeltest.cc index 8da1b24..965b5cc 100644 --- a/cc/output/gl_renderer_pixeltest.cc +++ b/cc/output/gl_renderer_pixeltest.cc @@ -92,14 +92,13 @@ TEST_F(GLRendererPixelTest, SimpleGreenRect) { RenderPassList pass_list; pass_list.push_back(pass.Pass()); - renderer_->DrawFrame(&pass_list); - - EXPECT_TRUE(PixelsMatchReference( + EXPECT_TRUE(RunPixelTest( + &pass_list, base::FilePath(FILE_PATH_LITERAL("green.png")), ExactPixelComparator(true))); } -TEST_F(GLRendererPixelTest, fastPassColorFilterAlpha) { +TEST_F(GLRendererPixelTest, FastPassColorFilterAlpha) { gfx::Rect viewport_rect(device_viewport_size_); RenderPass::Id root_pass_id(1, 1); @@ -168,7 +167,8 @@ TEST_F(GLRendererPixelTest, fastPassColorFilterAlpha) { skia::RefPtr<SkImageFilter> filter = skia::AdoptRef(SkColorFilterImageFilter::Create(colorFilter.get(), NULL)); - scoped_ptr<RenderPassDrawQuad> render_pass_quad = RenderPassDrawQuad::Create(); + scoped_ptr<RenderPassDrawQuad> render_pass_quad = + RenderPassDrawQuad::Create(); render_pass_quad->SetNew(pass_shared_state.get(), pass_rect, child_pass_id, @@ -186,16 +186,13 @@ TEST_F(GLRendererPixelTest, fastPassColorFilterAlpha) { pass_list.push_back(child_pass.Pass()); pass_list.push_back(root_pass.Pass()); - renderer_->SetEnlargePassTextureAmountForTesting(gfx::Vector2d(50, 75)); - renderer_->DecideRenderPassAllocationsForFrame(pass_list); - renderer_->DrawFrame(&pass_list); - - EXPECT_TRUE(PixelsMatchReference( + EXPECT_TRUE(RunPixelTest( + &pass_list, base::FilePath(FILE_PATH_LITERAL("blue_yellow_alpha.png")), ExactPixelComparator(false))); } -TEST_F(GLRendererPixelTest, fastPassColorFilterAlphaTranslation) { +TEST_F(GLRendererPixelTest, FastPassColorFilterAlphaTranslation) { gfx::Rect viewport_rect(device_viewport_size_); RenderPass::Id root_pass_id(1, 1); @@ -286,11 +283,8 @@ TEST_F(GLRendererPixelTest, fastPassColorFilterAlphaTranslation) { pass_list.push_back(child_pass.Pass()); pass_list.push_back(root_pass.Pass()); - renderer_->SetEnlargePassTextureAmountForTesting(gfx::Vector2d(50, 75)); - renderer_->DecideRenderPassAllocationsForFrame(pass_list); - renderer_->DrawFrame(&pass_list); - - EXPECT_TRUE(PixelsMatchReference( + EXPECT_TRUE(RunPixelTest( + &pass_list, base::FilePath(FILE_PATH_LITERAL("blue_yellow_alpha_translate.png")), ExactPixelComparator(false))); } @@ -342,17 +336,16 @@ TEST_F(GLRendererPixelTest, RenderPassChangesSize) { pass_list.push_back(root_pass.Pass()); renderer_->SetEnlargePassTextureAmountForTesting(gfx::Vector2d(50, 75)); - renderer_->DecideRenderPassAllocationsForFrame(pass_list); - renderer_->DrawFrame(&pass_list); - EXPECT_TRUE(PixelsMatchReference( + EXPECT_TRUE(RunPixelTest( + &pass_list, base::FilePath(FILE_PATH_LITERAL("blue_yellow.png")), ExactPixelComparator(true))); } class GLRendererPixelTestWithBackgroundFilter : public GLRendererPixelTest { protected: - void DrawFrame() { + void SetUpRenderPassList() { gfx::Rect device_viewport_rect(device_viewport_size_); RenderPass::Id root_id(1, 1); @@ -452,14 +445,11 @@ class GLRendererPixelTestWithBackgroundFilter : public GLRendererPixelTest { root_pass->quad_list.push_back(background_quad.PassAs<DrawQuad>()); root_pass->shared_quad_state_list.push_back(shared_state.Pass()); - RenderPassList pass_list; - pass_list.push_back(filter_pass.Pass()); - pass_list.push_back(root_pass.Pass()); - - renderer_->DecideRenderPassAllocationsForFrame(pass_list); - renderer_->DrawFrame(&pass_list); + pass_list_.push_back(filter_pass.Pass()); + pass_list_.push_back(root_pass.Pass()); } + RenderPassList pass_list_; WebKit::WebFilterOperations background_filters_; gfx::Transform filter_pass_to_target_transform_; gfx::Rect filter_pass_content_rect_; @@ -472,8 +462,9 @@ TEST_F(GLRendererPixelTestWithBackgroundFilter, InvertFilter) { filter_pass_content_rect_ = gfx::Rect(device_viewport_size_); filter_pass_content_rect_.Inset(12, 14, 16, 18); - DrawFrame(); - EXPECT_TRUE(PixelsMatchReference( + SetUpRenderPassList(); + EXPECT_TRUE(RunPixelTest( + &pass_list_, base::FilePath(FILE_PATH_LITERAL("background_filter.png")), ExactPixelComparator(true))); } @@ -482,9 +473,7 @@ TEST_F(GLRendererPixelTest, AntiAliasing) { gfx::Rect rect(0, 0, 200, 200); RenderPass::Id id(1, 1); - gfx::Transform transform_to_root; - scoped_ptr<RenderPass> pass = - CreateTestRenderPass(id, rect, transform_to_root); + scoped_ptr<RenderPass> pass = CreateTestRootRenderPass(id, rect); gfx::Transform red_content_to_target_transform; red_content_to_target_transform.Rotate(10); @@ -518,9 +507,8 @@ TEST_F(GLRendererPixelTest, AntiAliasing) { RenderPassList pass_list; pass_list.push_back(pass.Pass()); - renderer_->DrawFrame(&pass_list); - - EXPECT_TRUE(PixelsMatchReference( + EXPECT_TRUE(RunPixelTest( + &pass_list, base::FilePath(FILE_PATH_LITERAL("anti_aliasing.png")), ExactPixelComparator(true))); } diff --git a/cc/output/software_renderer.cc b/cc/output/software_renderer.cc index fec4f3e..69c3b7e 100644 --- a/cc/output/software_renderer.cc +++ b/cc/output/software_renderer.cc @@ -407,6 +407,15 @@ void SoftwareRenderer::DrawUnsupportedQuad(const DrawingFrame* frame, current_paint_); } +void SoftwareRenderer::CopyCurrentRenderPassToBitmap(DrawingFrame* frame, + SkBitmap* bitmap) { + gfx::Size render_pass_size = frame->current_render_pass->output_rect.size(); + bitmap->setConfig(SkBitmap::kARGB_8888_Config, + render_pass_size.width(), + render_pass_size.height()); + current_canvas_->readPixels(bitmap, 0, 0); +} + void SoftwareRenderer::GetFramebufferPixels(void* pixels, gfx::Rect rect) { TRACE_EVENT0("cc", "SoftwareRenderer::GetFramebufferPixels"); SkBitmap subset_bitmap; diff --git a/cc/output/software_renderer.h b/cc/output/software_renderer.h index 159044f..90e6ed2 100644 --- a/cc/output/software_renderer.h +++ b/cc/output/software_renderer.h @@ -58,6 +58,8 @@ class CC_EXPORT SoftwareRenderer : public DirectRenderer { virtual bool FlippedFramebuffer() const OVERRIDE; virtual void EnsureScissorTestEnabled() OVERRIDE; virtual void EnsureScissorTestDisabled() OVERRIDE; + virtual void CopyCurrentRenderPassToBitmap(DrawingFrame* frame, + SkBitmap* bitmap) OVERRIDE; private: SoftwareRenderer( diff --git a/cc/quads/render_pass.cc b/cc/quads/render_pass.cc index ef681df..48990cd 100644 --- a/cc/quads/render_pass.cc +++ b/cc/quads/render_pass.cc @@ -16,11 +16,9 @@ scoped_ptr<RenderPass> RenderPass::Create() { RenderPass::RenderPass() : id(Id(-1, -1)), has_transparent_background(true), - has_occlusion_from_outside_target_surface(false) { -} + has_occlusion_from_outside_target_surface(false) {} -RenderPass::~RenderPass() { -} +RenderPass::~RenderPass() {} scoped_ptr<RenderPass> RenderPass::Copy(Id new_id) const { DCHECK(new_id != id); diff --git a/cc/quads/render_pass.h b/cc/quads/render_pass.h index bfd0d83..1fb011a 100644 --- a/cc/quads/render_pass.h +++ b/cc/quads/render_pass.h @@ -9,6 +9,8 @@ #include <vector> #include "base/basictypes.h" +#include "base/callback.h" +#include "base/hash_tables.h" #include "cc/base/cc_export.h" #include "cc/base/hash_pair.h" #include "cc/base/scoped_ptr_hash_map.h" @@ -97,6 +99,14 @@ class CC_EXPORT RenderPass { // complete, since they are occluded. bool has_occlusion_from_outside_target_surface; + // If non-empty, the renderer should produce a copy of the render pass' + // contents as a bitmap, and give a copy of the bitmap to each callback in + // this list. This property should not be serialized between compositors, as + // it only makes sense in the root compositor. + typedef base::Callback<void(scoped_ptr<SkBitmap>)> + RequestCopyAsBitmapCallback; + std::vector<RequestCopyAsBitmapCallback> copy_callbacks; + QuadList quad_list; SharedQuadStateList shared_quad_state_list; diff --git a/cc/quads/render_pass_unittest.cc b/cc/quads/render_pass_unittest.cc index f2271b5..9ab0123 100644 --- a/cc/quads/render_pass_unittest.cc +++ b/cc/quads/render_pass_unittest.cc @@ -29,6 +29,7 @@ struct RenderPassSize { gfx::RectF damage_rect; bool has_transparent_background; bool has_occlusion_from_outside_target_surface; + std::vector<RenderPass::RequestCopyAsBitmapCallback> copy_callbacks; }; TEST(RenderPassTest, CopyShouldBeIdenticalExceptIdAndQuads) { @@ -47,6 +48,7 @@ TEST(RenderPassTest, CopyShouldBeIdenticalExceptIdAndQuads) { transform_to_root, has_transparent_background, has_occlusion_from_outside_target_surface); + pass->copy_callbacks.push_back(RenderPass::RequestCopyAsBitmapCallback()); // Stick a quad in the pass, this should not get copied. scoped_ptr<SharedQuadState> shared_state = SharedQuadState::Create(); @@ -72,6 +74,9 @@ TEST(RenderPassTest, CopyShouldBeIdenticalExceptIdAndQuads) { copy->has_occlusion_from_outside_target_surface); EXPECT_EQ(0u, copy->quad_list.size()); + // The copy callback should not be copied/duplicated. + EXPECT_EQ(0u, copy->copy_callbacks.size()); + EXPECT_EQ(sizeof(RenderPassSize), sizeof(RenderPass)); } diff --git a/cc/resources/resource_provider.cc b/cc/resources/resource_provider.cc index 7c55a3c..1b72257 100644 --- a/cc/resources/resource_provider.cc +++ b/cc/resources/resource_provider.cc @@ -456,7 +456,10 @@ const ResourceProvider::Resource* ResourceProvider::LockForRead(ResourceId id) { ResourceMap::iterator it = resources_.find(id); CHECK(it != resources_.end()); Resource* resource = &it->second; - DCHECK(!resource->locked_for_write || resource->set_pixels_completion_forced); + DCHECK(!resource->locked_for_write || + resource->set_pixels_completion_forced) << + "locked for write: " << resource->locked_for_write << + " pixels completion forced: " << resource->set_pixels_completion_forced; DCHECK(!resource->exported); // Uninitialized! Call SetPixels or LockForWrite first. DCHECK(resource->allocated); diff --git a/cc/test/layer_tree_pixel_test.cc b/cc/test/layer_tree_pixel_test.cc index 34a0f8d..1dfc551 100644 --- a/cc/test/layer_tree_pixel_test.cc +++ b/cc/test/layer_tree_pixel_test.cc @@ -47,35 +47,25 @@ LayerTreePixelTest::OffscreenContextProviderForCompositorThread() { return provider; } -void LayerTreePixelTest::SwapBuffersOnThread(LayerTreeHostImpl* host_impl, - bool result) { - EXPECT_TRUE(result); - - gfx::Rect device_viewport_rect( - host_impl->active_tree()->device_viewport_size()); - - SkBitmap bitmap; - bitmap.setConfig(SkBitmap::kARGB_8888_Config, - device_viewport_rect.width(), - device_viewport_rect.height()); - bitmap.allocPixels(); - unsigned char* pixels = static_cast<unsigned char*>(bitmap.getPixels()); - host_impl->Readback(pixels, device_viewport_rect); +void LayerTreePixelTest::ReadbackResult(scoped_ptr<SkBitmap> bitmap) { + ASSERT_TRUE(bitmap); base::FilePath test_data_dir; EXPECT_TRUE(PathService::Get(cc::DIR_TEST_DATA, &test_data_dir)); // To rebaseline: - // EXPECT_TRUE(WritePNGFile(bitmap, test_data_dir.Append(ref_file_), true)); + // EXPECT_TRUE(WritePNGFile(*bitmap, test_data_dir.Append(ref_file_), true)); - EXPECT_TRUE(MatchesPNGFile(bitmap, + EXPECT_TRUE(MatchesPNGFile(*bitmap, test_data_dir.Append(ref_file_), *pixel_comparator_)); - EndTest(); } void LayerTreePixelTest::BeginTest() { + layer_tree_host()->root_layer()->RequestCopyAsBitmap( + base::Bind(&LayerTreePixelTest::ReadbackResult, + base::Unretained(this))); PostSetNeedsCommitToMainThread(); } diff --git a/cc/test/layer_tree_pixel_test.h b/cc/test/layer_tree_pixel_test.h index ceddfab..4700ebd 100644 --- a/cc/test/layer_tree_pixel_test.h +++ b/cc/test/layer_tree_pixel_test.h @@ -25,8 +25,8 @@ class LayerTreePixelTest : public LayerTreeTest { OffscreenContextProviderForMainThread() OVERRIDE; virtual scoped_refptr<cc::ContextProvider> OffscreenContextProviderForCompositorThread() OVERRIDE; - virtual void SwapBuffersOnThread(LayerTreeHostImpl* host_impl, - bool result) OVERRIDE; + + void ReadbackResult(scoped_ptr<SkBitmap> bitmap); virtual void BeginTest() OVERRIDE; virtual void SetupTree() OVERRIDE; diff --git a/cc/test/pixel_test.cc b/cc/test/pixel_test.cc index 8563440..5976ae3 100644 --- a/cc/test/pixel_test.cc +++ b/cc/test/pixel_test.cc @@ -77,26 +77,40 @@ void PixelTest::SetUp() { resource_provider_->set_offscreen_context_provider(offscreen_contexts); } -bool PixelTest::PixelsMatchReference(const base::FilePath& ref_file, - const PixelComparator& comparator) { - gfx::Rect device_viewport_rect(device_viewport_size_); +bool PixelTest::RunPixelTest(RenderPassList* pass_list, + const base::FilePath& ref_file, + const PixelComparator& comparator) { + pass_list->back()->copy_callbacks.push_back( + base::Bind(&PixelTest::ReadbackResult, base::Unretained(this))); + + renderer_->DecideRenderPassAllocationsForFrame(*pass_list); + renderer_->DrawFrame(pass_list); - SkBitmap bitmap; - bitmap.setConfig(SkBitmap::kARGB_8888_Config, - device_viewport_rect.width(), - device_viewport_rect.height()); - bitmap.allocPixels(); - unsigned char* pixels = static_cast<unsigned char*>(bitmap.getPixels()); - renderer_->GetFramebufferPixels(pixels, device_viewport_rect); + // TODO(danakj): When the glReadPixels is async, wait for it to finish. + return PixelsMatchReference(ref_file, comparator); +} + +void PixelTest::ReadbackResult(scoped_ptr<SkBitmap> bitmap) { + result_bitmap_ = bitmap.Pass(); +} + +bool PixelTest::PixelsMatchReference(const base::FilePath& ref_file, + const PixelComparator& comparator) { base::FilePath test_data_dir; if (!PathService::Get(cc::DIR_TEST_DATA, &test_data_dir)) return false; + // If this is false, we didn't set up a readback on a render pass. + if (!result_bitmap_) + return false; + // To rebaseline: - // return WritePNGFile(bitmap, test_data_dir.Append(ref_file)); + // return WritePNGFile(*result_bitmap_, test_data_dir.Append(ref_file), true); - return MatchesPNGFile(bitmap, test_data_dir.Append(ref_file), comparator); + return MatchesPNGFile(*result_bitmap_, + test_data_dir.Append(ref_file), + comparator); } } // namespace cc diff --git a/cc/test/pixel_test.h b/cc/test/pixel_test.h index e915961..b3d8338 100644 --- a/cc/test/pixel_test.h +++ b/cc/test/pixel_test.h @@ -3,6 +3,7 @@ // found in the LICENSE file. #include "base/file_util.h" +#include "cc/quads/render_pass.h" #include "cc/test/pixel_comparator.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/gfx/size.h" @@ -22,6 +23,12 @@ class PixelTest : public testing::Test { virtual void SetUp() OVERRIDE; + bool RunPixelTest(RenderPassList* pass_list, + const base::FilePath& ref_file, + const PixelComparator& comparator); + + void ReadbackResult(scoped_ptr<SkBitmap> bitmap); + bool PixelsMatchReference(const base::FilePath& ref_file, const PixelComparator& comparator); @@ -31,6 +38,7 @@ class PixelTest : public testing::Test { class PixelTestRendererClient; scoped_ptr<PixelTestRendererClient> fake_client_; scoped_ptr<GLRenderer> renderer_; + scoped_ptr<SkBitmap> result_bitmap_; }; } // namespace cc diff --git a/cc/trees/layer_tree_host_common.cc b/cc/trees/layer_tree_host_common.cc index 6bed0fe..64e768d 100644 --- a/cc/trees/layer_tree_host_common.cc +++ b/cc/trees/layer_tree_host_common.cc @@ -296,6 +296,10 @@ static bool SubtreeShouldRenderToSeparateSurface( if (layer->force_render_surface()) return true; + // If we'll make a copy of the layer's contents. + if (layer->HasRequestCopyCallback()) + return true; + // If the layer uses a mask. if (layer->mask_layer()) return true; diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc index cb53f79..5a0015e 100644 --- a/cc/trees/layer_tree_host_impl.cc +++ b/cc/trees/layer_tree_host_impl.cc @@ -577,6 +577,9 @@ bool LayerTreeHostImpl::CalculateRenderPasses(FrameData* frame) { // due to an impl-animation, we drop the frame to avoid flashing due to the // texture suddenly appearing in the future. bool draw_frame = true; + // When we have a copy request for a layer, we need to draw no matter + // what, as the layer may disappear after this frame. + bool have_copy_request = false; int layers_drawn = 0; @@ -595,7 +598,12 @@ bool LayerTreeHostImpl::CalculateRenderPasses(FrameData* frame) { AppendQuadsData append_quads_data(target_render_pass->id); - if (it.represents_contributing_render_surface()) { + if (it.represents_target_render_surface()) { + if (it->HasRequestCopyCallback()) { + have_copy_request = true; + it->TakeRequestCopyCallbacks(&target_render_pass->copy_callbacks); + } + } else if (it.represents_contributing_render_surface()) { RenderPass::Id contributing_render_pass_id = it->render_surface()->RenderPassId(); RenderPass* contributing_render_pass = @@ -671,6 +679,9 @@ bool LayerTreeHostImpl::CalculateRenderPasses(FrameData* frame) { occlusion_tracker.LeaveLayer(it); } + if (have_copy_request) + draw_frame = true; + rendering_stats_instrumentation_->AddLayersDrawn(layers_drawn); #ifndef NDEBUG @@ -693,6 +704,8 @@ bool LayerTreeHostImpl::CalculateRenderPasses(FrameData* frame) { if (draw_frame) occlusion_tracker.overdraw_metrics()->RecordMetrics(this); + else + DCHECK(!have_copy_request); RemoveRenderPasses(CullRenderPassesWithNoQuads(), frame); renderer_->DecideRenderPassAllocationsForFrame(frame->render_passes); diff --git a/cc/trees/layer_tree_host_unittest.cc b/cc/trees/layer_tree_host_unittest.cc index b81bd047..08c2228 100644 --- a/cc/trees/layer_tree_host_unittest.cc +++ b/cc/trees/layer_tree_host_unittest.cc @@ -2564,5 +2564,198 @@ class LayerTreeHostTestIOSurfaceDrawing : public LayerTreeHostTest { SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestIOSurfaceDrawing); +class LayerTreeHostTestAsyncReadback : public LayerTreeHostTest { + protected: + virtual void SetupTree() OVERRIDE { + root = FakeContentLayer::Create(&client_); + root->SetBounds(gfx::Size(20, 20)); + + child = FakeContentLayer::Create(&client_); + child->SetBounds(gfx::Size(10, 10)); + root->AddChild(child); + + layer_tree_host()->SetRootLayer(root); + LayerTreeHostTest::SetupTree(); + } + + virtual void BeginTest() OVERRIDE { + PostSetNeedsCommitToMainThread(); + } + + virtual void DidCommitAndDrawFrame() { + int frame = layer_tree_host()->commit_number(); + switch (frame) { + case 1: + child->RequestCopyAsBitmap(base::Bind( + &LayerTreeHostTestAsyncReadback::BitmapCallback, + base::Unretained(this))); + EXPECT_EQ(0u, callbacks_.size()); + break; + case 2: + // Flush the message loops and make sure the callbacks run. + layer_tree_host()->SetNeedsCommit(); + break; + case 3: + ASSERT_EQ(1u, callbacks_.size()); + EXPECT_EQ(gfx::Size(10, 10).ToString(), callbacks_[0].ToString()); + + child->RequestCopyAsBitmap(base::Bind( + &LayerTreeHostTestAsyncReadback::BitmapCallback, + base::Unretained(this))); + root->RequestCopyAsBitmap(base::Bind( + &LayerTreeHostTestAsyncReadback::BitmapCallback, + base::Unretained(this))); + child->RequestCopyAsBitmap(base::Bind( + &LayerTreeHostTestAsyncReadback::BitmapCallback, + base::Unretained(this))); + EXPECT_EQ(1u, callbacks_.size()); + break; + case 4: + // Flush the message loops and make sure the callbacks run. + layer_tree_host()->SetNeedsCommit(); + break; + case 5: + ASSERT_EQ(4u, callbacks_.size()); + // The child was copied to a bitmap and passed back twice. + EXPECT_EQ(gfx::Size(10, 10).ToString(), callbacks_[1].ToString()); + EXPECT_EQ(gfx::Size(10, 10).ToString(), callbacks_[2].ToString()); + // The root was copied to a bitmap and passed back also. + EXPECT_EQ(gfx::Size(20, 20).ToString(), callbacks_[3].ToString()); + EndTest(); + break; + } + } + + void BitmapCallback(scoped_ptr<SkBitmap> bitmap) { + EXPECT_TRUE(layer_tree_host()->proxy()->IsMainThread()); + EXPECT_TRUE(bitmap); + callbacks_.push_back(gfx::Size(bitmap->width(), bitmap->height())); + } + + virtual void AfterTest() {} + + virtual scoped_ptr<OutputSurface> CreateOutputSurface() OVERRIDE { + if (use_gl_renderer_) + return FakeOutputSurface::Create3d().PassAs<OutputSurface>(); + return FakeOutputSurface::CreateSoftware( + make_scoped_ptr(new SoftwareOutputDevice)).PassAs<OutputSurface>(); + } + + bool use_gl_renderer_; + std::vector<gfx::Size> callbacks_; + FakeContentLayerClient client_; + scoped_refptr<FakeContentLayer> root; + scoped_refptr<FakeContentLayer> child; +}; + +TEST_F(LayerTreeHostTestAsyncReadback, GLRenderer_RunSingleThread) { + use_gl_renderer_ = true; + RunTest(false); +} + +TEST_F(LayerTreeHostTestAsyncReadback, GLRenderer_RunMultiThread) { + use_gl_renderer_ = true; + RunTest(true); +} + +TEST_F(LayerTreeHostTestAsyncReadback, SoftwareRenderer_RunSingleThread) { + use_gl_renderer_ = false; + RunTest(false); +} + +TEST_F(LayerTreeHostTestAsyncReadback, SoftwareRenderer_RunMultiThread) { + use_gl_renderer_ = false; + RunTest(true); +} + +class LayerTreeHostTestAsyncReadbackLayerDestroyed : public LayerTreeHostTest { + protected: + virtual void SetupTree() OVERRIDE { + root_ = FakeContentLayer::Create(&client_); + root_->SetBounds(gfx::Size(20, 20)); + + main_destroyed_ = FakeContentLayer::Create(&client_); + main_destroyed_->SetBounds(gfx::Size(15, 15)); + root_->AddChild(main_destroyed_); + + impl_destroyed_ = FakeContentLayer::Create(&client_); + impl_destroyed_->SetBounds(gfx::Size(10, 10)); + root_->AddChild(impl_destroyed_); + + layer_tree_host()->SetRootLayer(root_); + LayerTreeHostTest::SetupTree(); + } + + virtual void BeginTest() OVERRIDE { + callback_count_ = 0; + PostSetNeedsCommitToMainThread(); + } + + virtual void DidCommit() { + int frame = layer_tree_host()->commit_number(); + switch (frame) { + case 1: + main_destroyed_->RequestCopyAsBitmap(base::Bind( + &LayerTreeHostTestAsyncReadbackLayerDestroyed::BitmapCallback, + base::Unretained(this))); + impl_destroyed_->RequestCopyAsBitmap(base::Bind( + &LayerTreeHostTestAsyncReadbackLayerDestroyed::BitmapCallback, + base::Unretained(this))); + EXPECT_EQ(0, callback_count_); + + // Destroy the main thread layer right away. + main_destroyed_->RemoveFromParent(); + main_destroyed_ = NULL; + + // Should callback with a NULL bitmap. + EXPECT_EQ(1, callback_count_); + + // Prevent drawing so we can't make a copy of the impl_destroyed layer. + layer_tree_host()->SetViewportSize(gfx::Size()); + break; + case 2: + // Flush the message loops and make sure the callbacks run. + layer_tree_host()->SetNeedsCommit(); + break; + case 3: + // No drawing means no readback yet. + EXPECT_EQ(1, callback_count_); + + // Destroy the impl thread layer. + impl_destroyed_->RemoveFromParent(); + impl_destroyed_ = NULL; + + // No callback yet because it's on the impl side. + EXPECT_EQ(1, callback_count_); + break; + case 4: + // Flush the message loops and make sure the callbacks run. + layer_tree_host()->SetNeedsCommit(); + break; + case 5: + // We should get another callback with a NULL bitmap. + EXPECT_EQ(2, callback_count_); + EndTest(); + break; + } + } + + void BitmapCallback(scoped_ptr<SkBitmap> bitmap) { + EXPECT_TRUE(layer_tree_host()->proxy()->IsMainThread()); + EXPECT_FALSE(bitmap); + ++callback_count_; + } + + virtual void AfterTest() {} + + int callback_count_; + FakeContentLayerClient client_; + scoped_refptr<FakeContentLayer> root_; + scoped_refptr<FakeContentLayer> main_destroyed_; + scoped_refptr<FakeContentLayer> impl_destroyed_; +}; + +SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestAsyncReadbackLayerDestroyed); + } // namespace } // namespace cc |