summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordanakj@chromium.org <danakj@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-04-26 03:53:04 +0000
committerdanakj@chromium.org <danakj@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-04-26 03:53:04 +0000
commit45a695dae15e0e2df2887a2b280d28a5ffe3da43 (patch)
tree1119521f4e9c19a742483ffe839ef59acb219f87
parentfe3666132dcce324e06f99d116ce0c659617a6eb (diff)
downloadchromium_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.cc38
-rw-r--r--cc/layers/layer.h15
-rw-r--r--cc/layers/layer_impl.cc30
-rw-r--r--cc/layers/layer_impl.h11
-rw-r--r--cc/output/direct_renderer.cc13
-rw-r--r--cc/output/direct_renderer.h2
-rw-r--r--cc/output/gl_renderer.cc13
-rw-r--r--cc/output/gl_renderer.h2
-rw-r--r--cc/output/gl_renderer_pixeltest.cc56
-rw-r--r--cc/output/software_renderer.cc9
-rw-r--r--cc/output/software_renderer.h2
-rw-r--r--cc/quads/render_pass.cc6
-rw-r--r--cc/quads/render_pass.h10
-rw-r--r--cc/quads/render_pass_unittest.cc5
-rw-r--r--cc/resources/resource_provider.cc5
-rw-r--r--cc/test/layer_tree_pixel_test.cc24
-rw-r--r--cc/test/layer_tree_pixel_test.h4
-rw-r--r--cc/test/pixel_test.cc38
-rw-r--r--cc/test/pixel_test.h8
-rw-r--r--cc/trees/layer_tree_host_common.cc4
-rw-r--r--cc/trees/layer_tree_host_impl.cc15
-rw-r--r--cc/trees/layer_tree_host_unittest.cc193
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