diff options
author | Ben Murdoch <benm@google.com> | 2013-07-31 11:42:55 +0100 |
---|---|---|
committer | Ben Murdoch <benm@google.com> | 2013-07-31 11:42:55 +0100 |
commit | fb250657ef40d7500f20882d5c9909c1013367d3 (patch) | |
tree | e7885d8f55b77d8ec4430fa4ec78add75c7270d8 /cc | |
parent | 8b098b9dd9d1242c3db3032622b35d8469d47ac4 (diff) | |
download | chromium_org-fb250657ef40d7500f20882d5c9909c1013367d3.tar.gz |
Merge from Chromium at DEPS revision r214456
This commit was generated by merge_to_master.py.
Change-Id: If3f38c7966c2034e96c669968f72ea1e57f41964
Diffstat (limited to 'cc')
-rw-r--r-- | cc/layers/layer_impl.cc | 3 | ||||
-rw-r--r-- | cc/output/context_provider.h | 9 | ||||
-rw-r--r-- | cc/output/gl_renderer.cc | 36 | ||||
-rw-r--r-- | cc/output/gl_renderer.h | 8 | ||||
-rw-r--r-- | cc/output/gl_renderer_unittest.cc | 32 | ||||
-rw-r--r-- | cc/output/output_surface.cc | 4 | ||||
-rw-r--r-- | cc/output/output_surface.h | 3 | ||||
-rw-r--r-- | cc/output/output_surface_client.h | 1 | ||||
-rw-r--r-- | cc/output/renderer.h | 1 | ||||
-rw-r--r-- | cc/output/renderer_pixeltest.cc | 129 | ||||
-rw-r--r-- | cc/output/software_renderer.cc | 1 | ||||
-rw-r--r-- | cc/output/software_renderer_unittest.cc | 1 | ||||
-rw-r--r-- | cc/test/fake_context_provider.cc | 42 | ||||
-rw-r--r-- | cc/test/fake_context_provider.h | 19 | ||||
-rw-r--r-- | cc/test/fake_output_surface.h | 7 | ||||
-rw-r--r-- | cc/test/fake_output_surface_client.h | 1 | ||||
-rw-r--r-- | cc/test/pixel_test.cc | 13 | ||||
-rw-r--r-- | cc/test/pixel_test.h | 1 | ||||
-rw-r--r-- | cc/trees/layer_tree_host_impl.cc | 43 | ||||
-rw-r--r-- | cc/trees/layer_tree_host_impl.h | 5 | ||||
-rw-r--r-- | cc/trees/layer_tree_host_impl_unittest.cc | 328 | ||||
-rw-r--r-- | cc/trees/layer_tree_impl.cc | 10 |
22 files changed, 552 insertions, 145 deletions
diff --git a/cc/layers/layer_impl.cc b/cc/layers/layer_impl.cc index 008c68969b..f99c36786d 100644 --- a/cc/layers/layer_impl.cc +++ b/cc/layers/layer_impl.cc @@ -108,10 +108,11 @@ void LayerImpl::PassCopyRequests(ScopedPtrVector<CopyOutputRequest>* requests) { if (requests->empty()) return; + bool was_empty = copy_requests_.empty(); copy_requests_.insert_and_take(copy_requests_.end(), *requests); requests->clear(); - if (layer_tree_impl()->IsActiveTree()) + if (was_empty && layer_tree_impl()->IsActiveTree()) layer_tree_impl()->AddLayerWithCopyOutputRequest(this); NoteLayerPropertyChangedForSubtree(); } diff --git a/cc/output/context_provider.h b/cc/output/context_provider.h index 3b489c7104..13b7df5948 100644 --- a/cc/output/context_provider.h +++ b/cc/output/context_provider.h @@ -5,6 +5,7 @@ #ifndef CC_OUTPUT_CONTEXT_PROVIDER_H_ #define CC_OUTPUT_CONTEXT_PROVIDER_H_ +#include "base/callback.h" #include "base/memory/ref_counted.h" class GrContext; @@ -32,6 +33,14 @@ class ContextProvider : public base::RefCountedThreadSafe<ContextProvider> { // the context inside the provider is no longer valid. virtual bool DestroyedOnMainThread() = 0; + // Sets a callback to be called when the context is lost. This should be + // called from the same thread that the context is bound to. To avoid races, + // it should be called before BindToCurrentThread(), or VerifyContexts() + // should be called after setting the callback. + typedef base::Closure LostContextCallback; + virtual void SetLostContextCallback( + const LostContextCallback& lost_context_callback) = 0; + protected: friend class base::RefCountedThreadSafe<ContextProvider>; virtual ~ContextProvider() {} diff --git a/cc/output/gl_renderer.cc b/cc/output/gl_renderer.cc index 60a0b92431..a6d61f6cfd 100644 --- a/cc/output/gl_renderer.cc +++ b/cc/output/gl_renderer.cc @@ -157,6 +157,8 @@ GLRenderer::GLRenderer(RendererClient* client, is_using_bind_uniform_(false), visible_(true), is_scissor_enabled_(false), + stencil_shadow_(false), + blend_shadow_(false), highp_threshold_min_(highp_threshold_min), highp_threshold_cache_(0), offscreen_context_labelled_(false), @@ -295,6 +297,14 @@ void GLRenderer::ViewportChanged() { } void GLRenderer::ClearFramebuffer(DrawingFrame* frame) { + // It's unsafe to clear when we have a stencil test because glClear ignores + // stencil. + if (client_->ExternalStencilTestEnabled() && + frame->current_render_pass == frame->root_render_pass) { + DCHECK(!frame->current_render_pass->has_transparent_background); + return; + } + // On DEBUG builds, opaque render passes are cleared to blue to easily see // regions that were not drawn on the screen. if (frame->current_render_pass->has_transparent_background) @@ -310,8 +320,10 @@ void GLRenderer::ClearFramebuffer(DrawingFrame* frame) { GLbitfield clear_bits = GL_COLOR_BUFFER_BIT; // Only the Skia GPU backend uses the stencil buffer. No need to clear it // otherwise. - if (CanUseSkiaGPUBackend()) + if (always_clear || CanUseSkiaGPUBackend()) { + GLC(context_, context_->clearStencil(0)); clear_bits |= GL_STENCIL_BUFFER_BIT; + } context_->clear(clear_bits); } } @@ -1973,6 +1985,17 @@ void GLRenderer::SetShaderOpacity(float opacity, int alpha_location) { GLC(context_, context_->uniform1f(alpha_location, opacity)); } +void GLRenderer::SetStencilEnabled(bool enabled) { + if (enabled == stencil_shadow_) + return; + + if (enabled) + GLC(context_, context_->enable(GL_STENCIL_TEST)); + else + GLC(context_, context_->disable(GL_STENCIL_TEST)); + stencil_shadow_ = enabled; +} + void GLRenderer::SetBlendEnabled(bool enabled) { if (enabled == blend_shadow_) return; @@ -2470,6 +2493,13 @@ bool GLRenderer::UseScopedTexture(DrawingFrame* frame, void GLRenderer::BindFramebufferToOutputSurface(DrawingFrame* frame) { current_framebuffer_lock_.reset(); output_surface_->BindFramebuffer(); + + if (client_->ExternalStencilTestEnabled()) { + SetStencilEnabled(true); + GLC(context_, context_->stencilFunc(GL_EQUAL, 1, 1)); + } else { + SetStencilEnabled(false); + } } bool GLRenderer::BindFramebufferToTexture(DrawingFrame* frame, @@ -2479,6 +2509,7 @@ bool GLRenderer::BindFramebufferToTexture(DrawingFrame* frame, current_framebuffer_lock_.reset(); + SetStencilEnabled(false); GLC(context_, context_->bindFramebuffer(GL_FRAMEBUFFER, offscreen_framebuffer_id_)); current_framebuffer_lock_ = @@ -3079,10 +3110,11 @@ void GLRenderer::ReinitializeGLState() { // Bind the common vertex attributes used for drawing all the layers. shared_geometry_->PrepareForDraw(); - GLC(context_, context_->disable(GL_STENCIL_TEST)); GLC(context_, context_->disable(GL_DEPTH_TEST)); GLC(context_, context_->disable(GL_CULL_FACE)); GLC(context_, context_->colorMask(true, true, true, true)); + GLC(context_, context_->disable(GL_STENCIL_TEST)); + stencil_shadow_ = false; GLC(context_, context_->enable(GL_BLEND)); blend_shadow_ = true; GLC(context_, context_->blendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA)); diff --git a/cc/output/gl_renderer.h b/cc/output/gl_renderer.h index 3e06537239..b1a3643b2b 100644 --- a/cc/output/gl_renderer.h +++ b/cc/output/gl_renderer.h @@ -101,6 +101,11 @@ class CC_EXPORT GLRenderer : public DirectRenderer { gfx::Rect device_rect); void ReleaseRenderPassTextures(); + void SetStencilEnabled(bool enabled); + bool stencil_enabled() const { return stencil_shadow_; } + void SetBlendEnabled(bool enabled); + bool blend_enabled() const { return blend_shadow_; } + virtual void BindFramebufferToOutputSurface(DrawingFrame* frame) OVERRIDE; virtual bool BindFramebufferToTexture(DrawingFrame* frame, const ScopedResource* resource, @@ -161,8 +166,6 @@ class CC_EXPORT GLRenderer : public DirectRenderer { const gfx::Transform& draw_transform, const gfx::RectF& quad_rect, int matrix_location); - void SetBlendEnabled(bool enabled); - bool blend_enabled() const { return blend_shadow_; } void SetUseProgram(unsigned program); void CopyTextureToFramebuffer(const DrawingFrame* frame, @@ -438,6 +441,7 @@ class CC_EXPORT GLRenderer : public DirectRenderer { bool is_using_bind_uniform_; bool visible_; bool is_scissor_enabled_; + bool stencil_shadow_; bool blend_shadow_; unsigned program_shadow_; TexturedQuadDrawCache draw_cache_; diff --git a/cc/output/gl_renderer_unittest.cc b/cc/output/gl_renderer_unittest.cc index dd4e5f33c7..e4213d6584 100644 --- a/cc/output/gl_renderer_unittest.cc +++ b/cc/output/gl_renderer_unittest.cc @@ -144,7 +144,8 @@ class FakeRendererClient : public RendererClient { set_full_root_layer_damage_count_(0), root_layer_(LayerImpl::Create(host_impl_.active_tree(), 1)), viewport_size_(gfx::Size(1, 1)), - scale_factor_(1.f) { + scale_factor_(1.f), + external_stencil_test_enabled_(false) { root_layer_->CreateRenderSurface(); RenderPass::Id render_pass_id = root_layer_->render_surface()->RenderPassId(); @@ -177,6 +178,13 @@ class FakeRendererClient : public RendererClient { virtual bool AllowPartialSwap() const OVERRIDE { return true; } + virtual bool ExternalStencilTestEnabled() const OVERRIDE { + return external_stencil_test_enabled_; + } + + void EnableExternalStencilTest() { + external_stencil_test_enabled_ = true; + } // Methods added for test. int set_full_root_layer_damage_count() const { @@ -201,6 +209,7 @@ class FakeRendererClient : public RendererClient { RenderPassList render_passes_in_draw_order_; gfx::Size viewport_size_; float scale_factor_; + bool external_stencil_test_enabled_; }; class FakeRendererGL : public GLRenderer { @@ -218,6 +227,7 @@ class FakeRendererGL : public GLRenderer { using GLRenderer::DoDrawQuad; using GLRenderer::BeginDrawingFrame; using GLRenderer::FinishDrawingQuadList; + using GLRenderer::stencil_enabled; }; class GLRendererTest : public testing::Test { @@ -465,6 +475,16 @@ TEST_F(GLRendererTest, FramebufferDiscardedAfterReadbackWhenNotVisible) { EXPECT_EQ(2, mock_client_.set_full_root_layer_damage_count()); } +TEST_F(GLRendererTest, ExternalStencil) { + EXPECT_FALSE(renderer_.stencil_enabled()); + + mock_client_.EnableExternalStencilTest(); + mock_client_.root_render_pass()->has_transparent_background = false; + + renderer_.DrawFrame(mock_client_.render_passes_in_draw_order()); + EXPECT_TRUE(renderer_.stencil_enabled()); +} + class ForbidSynchronousCallContext : public TestWebGraphicsContext3D { public: ForbidSynchronousCallContext() {} @@ -948,14 +968,20 @@ TEST(GLRendererTest2, ShouldClearRootRenderPass) { AddRenderPassQuad(root_pass, child_pass); +#ifdef NDEBUG + GLint clear_bits = GL_COLOR_BUFFER_BIT; +#else + GLint clear_bits = GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT; +#endif + // First render pass is not the root one, clearing should happen. - EXPECT_CALL(*mock_context, clear(GL_COLOR_BUFFER_BIT)).Times(AtLeast(1)); + EXPECT_CALL(*mock_context, clear(clear_bits)).Times(AtLeast(1)); Expectation first_render_pass = EXPECT_CALL(*mock_context, drawElements(_, _, _, _)).Times(1); // The second render pass is the root one, clearing should be prevented. - EXPECT_CALL(*mock_context, clear(GL_COLOR_BUFFER_BIT)).Times(0) + EXPECT_CALL(*mock_context, clear(clear_bits)).Times(0) .After(first_render_pass); EXPECT_CALL(*mock_context, drawElements(_, _, _, _)).Times(AnyNumber()) diff --git a/cc/output/output_surface.cc b/cc/output/output_surface.cc index 201f67cb63..ce2e477013 100644 --- a/cc/output/output_surface.cc +++ b/cc/output/output_surface.cc @@ -273,6 +273,10 @@ void OutputSurface::DidLoseOutputSurface() { client_->DidLoseOutputSurface(); } +void OutputSurface::SetExternalStencilTest(bool enabled) { + client_->SetExternalStencilTest(enabled); +} + void OutputSurface::SetExternalDrawConstraints(const gfx::Transform& transform, gfx::Rect viewport) { client_->SetExternalDrawConstraints(transform, viewport); diff --git a/cc/output/output_surface.h b/cc/output/output_surface.h index 4489c10774..86d4dc81dd 100644 --- a/cc/output/output_surface.h +++ b/cc/output/output_surface.h @@ -60,10 +60,12 @@ class CC_EXPORT OutputSurface : public FrameRateControllerClient { : delegated_rendering(false), max_frames_pending(0), deferred_gl_initialization(false), + draw_and_swap_full_viewport_every_frame(false), adjust_deadline_for_parent(true) {} bool delegated_rendering; int max_frames_pending; bool deferred_gl_initialization; + bool draw_and_swap_full_viewport_every_frame; // This doesn't handle the <webview> case, but once BeginFrame is // supported natively, we shouldn't need adjust_deadline_for_parent. bool adjust_deadline_for_parent; @@ -167,6 +169,7 @@ class CC_EXPORT OutputSurface : public FrameRateControllerClient { void DidSwapBuffers(); void OnSwapBuffersComplete(const CompositorFrameAck* ack); void DidLoseOutputSurface(); + void SetExternalStencilTest(bool enabled); void SetExternalDrawConstraints(const gfx::Transform& transform, gfx::Rect viewport); diff --git a/cc/output/output_surface_client.h b/cc/output/output_surface_client.h index e6f296b3df..b4e2820a01 100644 --- a/cc/output/output_surface_client.h +++ b/cc/output/output_surface_client.h @@ -34,6 +34,7 @@ class CC_EXPORT OutputSurfaceClient { virtual void BeginFrame(const BeginFrameArgs& args) = 0; virtual void OnSwapBuffersComplete(const CompositorFrameAck* ack) = 0; virtual void DidLoseOutputSurface() = 0; + virtual void SetExternalStencilTest(bool enabled) = 0; virtual void SetExternalDrawConstraints(const gfx::Transform& transform, gfx::Rect viewport) = 0; virtual void SetMemoryPolicy(const ManagedMemoryPolicy& policy, diff --git a/cc/output/renderer.h b/cc/output/renderer.h index 3a628ad1a5..a9a800b6a0 100644 --- a/cc/output/renderer.h +++ b/cc/output/renderer.h @@ -30,6 +30,7 @@ class CC_EXPORT RendererClient { virtual bool ShouldClearRootRenderPass() const = 0; virtual CompositorFrameMetadata MakeCompositorFrameMetadata() const = 0; virtual bool AllowPartialSwap() const = 0; + virtual bool ExternalStencilTestEnabled() const = 0; protected: virtual ~RendererClient() {} diff --git a/cc/output/renderer_pixeltest.cc b/cc/output/renderer_pixeltest.cc index ed1df7ad19..9508aa2916 100644 --- a/cc/output/renderer_pixeltest.cc +++ b/cc/output/renderer_pixeltest.cc @@ -1000,6 +1000,135 @@ TEST_F(GLRendererPixelTestWithBackgroundFilter, InvertFilter) { ExactPixelComparator(true))); } +class ExternalStencilPixelTest : public GLRendererPixelTest { + protected: + void ClearBackgroundToGreen() { + WebKit::WebGraphicsContext3D* context3d = output_surface_->context3d(); + output_surface_->EnsureBackbuffer(); + output_surface_->Reshape(device_viewport_size_, 1); + context3d->clearColor(0.f, 1.f, 0.f, 1.f); + context3d->clear(GL_COLOR_BUFFER_BIT); + } + + void PopulateStencilBuffer() { + // Set two quadrants of the stencil buffer to 1. + WebKit::WebGraphicsContext3D* context3d = output_surface_->context3d(); + ASSERT_TRUE(context3d->getContextAttributes().stencil); + output_surface_->EnsureBackbuffer(); + output_surface_->Reshape(device_viewport_size_, 1); + context3d->clearStencil(0); + context3d->clear(GL_STENCIL_BUFFER_BIT); + context3d->enable(GL_SCISSOR_TEST); + context3d->clearStencil(1); + context3d->scissor(0, + 0, + device_viewport_size_.width() / 2, + device_viewport_size_.height() / 2); + context3d->clear(GL_STENCIL_BUFFER_BIT); + context3d->scissor(device_viewport_size_.width() / 2, + device_viewport_size_.height() / 2, + device_viewport_size_.width(), + device_viewport_size_.height()); + context3d->clear(GL_STENCIL_BUFFER_BIT); + } +}; + +TEST_F(ExternalStencilPixelTest, StencilTestEnabled) { + ClearBackgroundToGreen(); + PopulateStencilBuffer(); + this->EnableExternalStencilTest(); + + // Draw a blue quad that covers the entire device viewport. It should be + // clipped to the bottom left and top right corners by the external stencil. + gfx::Rect rect(this->device_viewport_size_); + RenderPass::Id id(1, 1); + scoped_ptr<RenderPass> pass = CreateTestRootRenderPass(id, rect); + scoped_ptr<SharedQuadState> blue_shared_state = + CreateTestSharedQuadState(gfx::Transform(), rect); + scoped_ptr<SolidColorDrawQuad> blue = SolidColorDrawQuad::Create(); + blue->SetNew(blue_shared_state.get(), rect, SK_ColorBLUE, false); + pass->quad_list.push_back(blue.PassAs<DrawQuad>()); + pass->has_transparent_background = false; + RenderPassList pass_list; + pass_list.push_back(pass.Pass()); + + EXPECT_TRUE(this->RunPixelTest( + &pass_list, + base::FilePath(FILE_PATH_LITERAL("four_blue_green_checkers.png")), + ExactPixelComparator(true))); +} + +TEST_F(ExternalStencilPixelTest, StencilTestDisabled) { + PopulateStencilBuffer(); + + // Draw a green quad that covers the entire device viewport. The stencil + // buffer should be ignored. + gfx::Rect rect(this->device_viewport_size_); + RenderPass::Id id(1, 1); + scoped_ptr<RenderPass> pass = CreateTestRootRenderPass(id, rect); + scoped_ptr<SharedQuadState> green_shared_state = + CreateTestSharedQuadState(gfx::Transform(), rect); + scoped_ptr<SolidColorDrawQuad> green = SolidColorDrawQuad::Create(); + green->SetNew(green_shared_state.get(), rect, SK_ColorGREEN, false); + pass->quad_list.push_back(green.PassAs<DrawQuad>()); + RenderPassList pass_list; + pass_list.push_back(pass.Pass()); + + EXPECT_TRUE(this->RunPixelTest( + &pass_list, + base::FilePath(FILE_PATH_LITERAL("green.png")), + ExactPixelComparator(true))); +} + +TEST_F(ExternalStencilPixelTest, RenderSurfacesIgnoreStencil) { + // The stencil test should apply only to the final render pass. + ClearBackgroundToGreen(); + PopulateStencilBuffer(); + this->EnableExternalStencilTest(); + + gfx::Rect viewport_rect(this->device_viewport_size_); + + RenderPass::Id root_pass_id(1, 1); + scoped_ptr<RenderPass> root_pass = + CreateTestRootRenderPass(root_pass_id, viewport_rect); + root_pass->has_transparent_background = false; + + RenderPass::Id child_pass_id(2, 2); + gfx::Rect pass_rect(this->device_viewport_size_); + gfx::Transform transform_to_root; + scoped_ptr<RenderPass> child_pass = + CreateTestRenderPass(child_pass_id, pass_rect, transform_to_root); + + gfx::Transform content_to_target_transform; + scoped_ptr<SharedQuadState> shared_state = + CreateTestSharedQuadState(content_to_target_transform, viewport_rect); + + scoped_ptr<SolidColorDrawQuad> blue = SolidColorDrawQuad::Create(); + blue->SetNew(shared_state.get(), + gfx::Rect(0, + 0, + this->device_viewport_size_.width(), + this->device_viewport_size_.height()), + SK_ColorBLUE, + false); + child_pass->quad_list.push_back(blue.PassAs<DrawQuad>()); + + scoped_ptr<SharedQuadState> pass_shared_state = + CreateTestSharedQuadState(gfx::Transform(), pass_rect); + root_pass->quad_list.push_back( + CreateTestRenderPassDrawQuad(pass_shared_state.get(), + pass_rect, + child_pass_id)); + RenderPassList pass_list; + pass_list.push_back(child_pass.Pass()); + pass_list.push_back(root_pass.Pass()); + + EXPECT_TRUE(this->RunPixelTest( + &pass_list, + base::FilePath(FILE_PATH_LITERAL("four_blue_green_checkers.png")), + ExactPixelComparator(true))); +} + // Software renderer does not support anti-aliased edges. TEST_F(GLRendererPixelTest, AntiAliasing) { gfx::Rect rect(this->device_viewport_size_); diff --git a/cc/output/software_renderer.cc b/cc/output/software_renderer.cc index 7eac62f64a..7735e3c8c1 100644 --- a/cc/output/software_renderer.cc +++ b/cc/output/software_renderer.cc @@ -134,6 +134,7 @@ void SoftwareRenderer::EnsureScissorTestDisabled() { void SoftwareRenderer::Finish() {} void SoftwareRenderer::BindFramebufferToOutputSurface(DrawingFrame* frame) { + DCHECK(!client_->ExternalStencilTestEnabled()); current_framebuffer_lock_.reset(); current_canvas_ = root_canvas_; } diff --git a/cc/output/software_renderer_unittest.cc b/cc/output/software_renderer_unittest.cc index deaf2f992b..238d654ac1 100644 --- a/cc/output/software_renderer_unittest.cc +++ b/cc/output/software_renderer_unittest.cc @@ -72,6 +72,7 @@ class SoftwareRendererTest : public testing::Test, public RendererClient { virtual bool AllowPartialSwap() const OVERRIDE { return true; } + virtual bool ExternalStencilTestEnabled() const OVERRIDE { return false; } protected: scoped_ptr<FakeOutputSurface> output_surface_; diff --git a/cc/test/fake_context_provider.cc b/cc/test/fake_context_provider.cc index e12f783174..d49fa8978e 100644 --- a/cc/test/fake_context_provider.cc +++ b/cc/test/fake_context_provider.cc @@ -9,29 +9,38 @@ namespace cc { FakeContextProvider::FakeContextProvider() - : destroyed_(false) { + : bound_(false), + destroyed_(false) { + DCHECK(main_thread_checker_.CalledOnValidThread()); + context_thread_checker_.DetachFromThread(); } -FakeContextProvider::FakeContextProvider( - const CreateCallback& create_callback) - : create_callback_(create_callback), - bound_(false), - destroyed_(false) { +FakeContextProvider::~FakeContextProvider() { + DCHECK(main_thread_checker_.CalledOnValidThread() || + context_thread_checker_.CalledOnValidThread()); } -FakeContextProvider::~FakeContextProvider() {} +bool FakeContextProvider::InitializeOnMainThread( + const CreateCallback& create_callback) { + DCHECK(main_thread_checker_.CalledOnValidThread()); -bool FakeContextProvider::InitializeOnMainThread() { DCHECK(!context3d_); - - if (create_callback_.is_null()) + if (create_callback.is_null()) context3d_ = TestWebGraphicsContext3D::Create().Pass(); else - context3d_ = create_callback_.Run(); + context3d_ = create_callback.Run(); return context3d_; } bool FakeContextProvider::BindToCurrentThread() { + DCHECK(context3d_); + + // This is called on the thread the context will be used. + DCHECK(context_thread_checker_.CalledOnValidThread()); + + if (bound_) + return true; + bound_ = true; if (!context3d_->makeContextCurrent()) { base::AutoLock lock(destroyed_lock_); @@ -44,12 +53,14 @@ bool FakeContextProvider::BindToCurrentThread() { WebKit::WebGraphicsContext3D* FakeContextProvider::Context3d() { DCHECK(context3d_); DCHECK(bound_); + DCHECK(context_thread_checker_.CalledOnValidThread()); return context3d_.get(); } class GrContext* FakeContextProvider::GrContext() { DCHECK(context3d_); DCHECK(bound_); + DCHECK(context_thread_checker_.CalledOnValidThread()); // TODO(danakj): Make a fake GrContext. return NULL; @@ -58,6 +69,7 @@ class GrContext* FakeContextProvider::GrContext() { void FakeContextProvider::VerifyContexts() { DCHECK(context3d_); DCHECK(bound_); + DCHECK(context_thread_checker_.CalledOnValidThread()); if (context3d_->isContextLost()) { base::AutoLock lock(destroyed_lock_); @@ -66,8 +78,16 @@ void FakeContextProvider::VerifyContexts() { } bool FakeContextProvider::DestroyedOnMainThread() { + DCHECK(main_thread_checker_.CalledOnValidThread()); + base::AutoLock lock(destroyed_lock_); return destroyed_; } +void FakeContextProvider::SetLostContextCallback( + const LostContextCallback& cb) { + DCHECK(context_thread_checker_.CalledOnValidThread()); + NOTIMPLEMENTED(); +} + } // namespace cc diff --git a/cc/test/fake_context_provider.h b/cc/test/fake_context_provider.h index 9ab1abd3d3..4a18e4a8b0 100644 --- a/cc/test/fake_context_provider.h +++ b/cc/test/fake_context_provider.h @@ -8,6 +8,7 @@ #include "base/callback.h" #include "base/memory/scoped_ptr.h" #include "base/synchronization/lock.h" +#include "base/threading/thread_checker.h" #include "cc/output/context_provider.h" namespace cc { @@ -19,17 +20,13 @@ class FakeContextProvider : public cc::ContextProvider { CreateCallback; static scoped_refptr<FakeContextProvider> Create() { - scoped_refptr<FakeContextProvider> provider = new FakeContextProvider(); - if (!provider->InitializeOnMainThread()) - return NULL; - return provider; + return Create(CreateCallback()); } static scoped_refptr<FakeContextProvider> Create( const CreateCallback& create_callback) { - scoped_refptr<FakeContextProvider> provider = - new FakeContextProvider(create_callback); - if (!provider->InitializeOnMainThread()) + scoped_refptr<FakeContextProvider> provider = new FakeContextProvider; + if (!provider->InitializeOnMainThread(create_callback)) return NULL; return provider; } @@ -39,18 +36,20 @@ class FakeContextProvider : public cc::ContextProvider { virtual class GrContext* GrContext() OVERRIDE; virtual void VerifyContexts() OVERRIDE; virtual bool DestroyedOnMainThread() OVERRIDE; + virtual void SetLostContextCallback(const LostContextCallback& cb) OVERRIDE; protected: FakeContextProvider(); - explicit FakeContextProvider(const CreateCallback& create_callback); virtual ~FakeContextProvider(); - bool InitializeOnMainThread(); + bool InitializeOnMainThread(const CreateCallback& create_callback); - CreateCallback create_callback_; scoped_ptr<WebKit::WebGraphicsContext3D> context3d_; bool bound_; + base::ThreadChecker main_thread_checker_; + base::ThreadChecker context_thread_checker_; + base::Lock destroyed_lock_; bool destroyed_; }; diff --git a/cc/test/fake_output_surface.h b/cc/test/fake_output_surface.h index 18759ecb28..dfd4db5344 100644 --- a/cc/test/fake_output_surface.h +++ b/cc/test/fake_output_surface.h @@ -56,6 +56,7 @@ class FakeOutputSurface : public OutputSurface { new FakeOutputSurface(software_device.Pass(), true)); } + // TODO(boliu): Use a general factory that takes Capabilities arg. static scoped_ptr<FakeOutputSurface> CreateDeferredGL( scoped_ptr<SoftwareOutputDevice> software_device) { scoped_ptr<FakeOutputSurface> result( @@ -64,6 +65,12 @@ class FakeOutputSurface : public OutputSurface { return result.Pass(); } + static scoped_ptr<FakeOutputSurface> CreateAlwaysDrawAndSwap3d() { + scoped_ptr<FakeOutputSurface> result(Create3d()); + result->capabilities_.draw_and_swap_full_viewport_every_frame = true; + return result.Pass(); + } + CompositorFrame& last_sent_frame() { return last_sent_frame_; } size_t num_sent_frames() { return num_sent_frames_; } diff --git a/cc/test/fake_output_surface_client.h b/cc/test/fake_output_surface_client.h index 830dcada9f..c8169d2c86 100644 --- a/cc/test/fake_output_surface_client.h +++ b/cc/test/fake_output_surface_client.h @@ -29,6 +29,7 @@ class FakeOutputSurfaceClient : public OutputSurfaceClient { virtual void DidLoseOutputSurface() OVERRIDE; virtual void SetExternalDrawConstraints(const gfx::Transform& transform, gfx::Rect viewport) OVERRIDE {} + virtual void SetExternalStencilTest(bool enable) OVERRIDE {} virtual void SetMemoryPolicy( const ManagedMemoryPolicy& policy, bool discard_backbuffer_when_not_visible) OVERRIDE; diff --git a/cc/test/pixel_test.cc b/cc/test/pixel_test.cc index 23a1a4fed8..5ec7a5c141 100644 --- a/cc/test/pixel_test.cc +++ b/cc/test/pixel_test.cc @@ -28,7 +28,7 @@ class PixelTest::PixelTestRendererClient : public RendererClient, public OutputSurfaceClient { public: explicit PixelTestRendererClient(gfx::Rect device_viewport) - : device_viewport_(device_viewport) {} + : device_viewport_(device_viewport), stencil_enabled_(false) {} // RendererClient implementation. virtual gfx::Rect DeviceViewport() const OVERRIDE { @@ -50,6 +50,9 @@ class PixelTest::PixelTestRendererClient virtual bool AllowPartialSwap() const OVERRIDE { return true; } + virtual bool ExternalStencilTestEnabled() const OVERRIDE { + return stencil_enabled_; + } // OutputSurfaceClient implementation. virtual bool DeferredInitialize( @@ -65,12 +68,16 @@ class PixelTest::PixelTestRendererClient gfx::Rect viewport) OVERRIDE { device_viewport_ = viewport; } + virtual void SetExternalStencilTest(bool enable) OVERRIDE { + stencil_enabled_ = enable; + } virtual void SetMemoryPolicy( const ManagedMemoryPolicy& policy, bool discard) OVERRIDE {} virtual void SetTreeActivationCallback(const base::Closure&) OVERRIDE {} private: gfx::Rect device_viewport_; + bool stencil_enabled_; LayerTreeSettings settings_; }; @@ -175,6 +182,10 @@ void PixelTest::ForceExpandedViewport(gfx::Size surface_expansion, } } +void PixelTest::EnableExternalStencilTest() { + fake_client_->SetExternalStencilTest(true); +} + void PixelTest::SetUpSoftwareRenderer() { CHECK(fake_client_); diff --git a/cc/test/pixel_test.h b/cc/test/pixel_test.h index 3c607ae2cc..1dd3e1a334 100644 --- a/cc/test/pixel_test.h +++ b/cc/test/pixel_test.h @@ -47,6 +47,7 @@ class PixelTest : public testing::Test { void ForceExpandedViewport(gfx::Size surface_expansion, gfx::Vector2d viewport_offset); + void EnableExternalStencilTest(); private: void ReadbackResult(base::Closure quit_run_loop, diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc index f97eea0f51..b263f2f417 100644 --- a/cc/trees/layer_tree_host_impl.cc +++ b/cc/trees/layer_tree_host_impl.cc @@ -194,6 +194,7 @@ LayerTreeHostImpl::LayerTreeHostImpl( zero_budget_(false), device_scale_factor_(1.f), overdraw_bottom_height_(0.f), + external_stencil_test_enabled_(false), animation_registrar_(AnimationRegistrar::Create()), rendering_stats_instrumentation_(rendering_stats_instrumentation), need_to_update_visible_tiles_before_draw_(false) { @@ -257,17 +258,32 @@ void LayerTreeHostImpl::CommitComplete() { client_->SendManagedMemoryStats(); } -bool LayerTreeHostImpl::CanDraw() { +bool LayerTreeHostImpl::CanDraw() const { // Note: If you are changing this function or any other function that might // affect the result of CanDraw, make sure to call // client_->OnCanDrawStateChanged in the proper places and update the // NotifyIfCanDrawChanged test. + if (!renderer_) { + TRACE_EVENT_INSTANT0("cc", "LayerTreeHostImpl::CanDraw no renderer", + TRACE_EVENT_SCOPE_THREAD); + return false; + } + + // Must have an OutputSurface if |renderer_| is not NULL. + DCHECK(output_surface_); + + // TODO(boliu): Make draws without root_layer work and move this below + // draw_and_swap_full_viewport_every_frame check. Tracked in crbug.com/264967. if (!active_tree_->root_layer()) { TRACE_EVENT_INSTANT0("cc", "LayerTreeHostImpl::CanDraw no root layer", TRACE_EVENT_SCOPE_THREAD); return false; } + + if (output_surface_->capabilities().draw_and_swap_full_viewport_every_frame) + return true; + if (device_viewport_size_.IsEmpty()) { TRACE_EVENT_INSTANT0("cc", "LayerTreeHostImpl::CanDraw empty viewport", TRACE_EVENT_SCOPE_THREAD); @@ -279,11 +295,6 @@ bool LayerTreeHostImpl::CanDraw() { TRACE_EVENT_SCOPE_THREAD); return false; } - if (!renderer_) { - TRACE_EVENT_INSTANT0("cc", "LayerTreeHostImpl::CanDraw no renderer", - TRACE_EVENT_SCOPE_THREAD); - return false; - } if (active_tree_->ContentsTexturesPurged()) { TRACE_EVENT_INSTANT0( "cc", "LayerTreeHostImpl::CanDraw contents textures purged", @@ -568,6 +579,8 @@ bool LayerTreeHostImpl::CalculateRenderPasses(FrameData* frame) { // A copy request should cause damage, so we should not have any copy // requests in this case. DCHECK_EQ(0u, active_tree_->LayersWithCopyOutputRequest().size()); + DCHECK(!output_surface_->capabilities() + .draw_and_swap_full_viewport_every_frame); return true; } @@ -730,7 +743,8 @@ bool LayerTreeHostImpl::CalculateRenderPasses(FrameData* frame) { occlusion_tracker.LeaveLayer(it); } - if (have_copy_request) + if (have_copy_request || + output_surface_->capabilities().draw_and_swap_full_viewport_every_frame) draw_frame = true; rendering_stats_instrumentation_->AddLayersDrawn(layers_drawn); @@ -972,8 +986,11 @@ bool LayerTreeHostImpl::PrepareToDraw(FrameData* frame, AddDamageNextUpdate(device_viewport_damage_rect); } - if (!CalculateRenderPasses(frame)) + if (!CalculateRenderPasses(frame)) { + DCHECK(!output_surface_->capabilities() + .draw_and_swap_full_viewport_every_frame); return false; + } // If we return true, then we expect DrawLayers() to be called before this // function is called again. @@ -1117,6 +1134,10 @@ void LayerTreeHostImpl::SetExternalDrawConstraints( external_viewport_ = viewport; } +void LayerTreeHostImpl::SetExternalStencilTest(bool enabled) { + external_stencil_test_enabled_ = enabled; +} + void LayerTreeHostImpl::SetNeedsRedrawRect(gfx::Rect damage_rect) { client_->SetNeedsRedrawRectOnImplThread(damage_rect); } @@ -1171,6 +1192,10 @@ bool LayerTreeHostImpl::AllowPartialSwap() const { return !debug_state_.ShowHudRects(); } +bool LayerTreeHostImpl::ExternalStencilTestEnabled() const { + return external_stencil_test_enabled_; +} + static void LayerTreeHostImplDidBeginTracingCallback(LayerImpl* layer) { layer->DidBeginTracing(); } @@ -1182,6 +1207,8 @@ void LayerTreeHostImpl::DrawLayers(FrameData* frame, if (frame->has_no_damage) { TRACE_EVENT0("cc", "EarlyOut_NoDamage"); + DCHECK(!output_surface_->capabilities() + .draw_and_swap_full_viewport_every_frame); return; } diff --git a/cc/trees/layer_tree_host_impl.h b/cc/trees/layer_tree_host_impl.h index 9a0f72fcc1..f4663ebcc5 100644 --- a/cc/trees/layer_tree_host_impl.h +++ b/cc/trees/layer_tree_host_impl.h @@ -189,6 +189,7 @@ class CC_EXPORT LayerTreeHostImpl virtual bool ShouldClearRootRenderPass() const OVERRIDE; virtual CompositorFrameMetadata MakeCompositorFrameMetadata() const OVERRIDE; virtual bool AllowPartialSwap() const OVERRIDE; + virtual bool ExternalStencilTestEnabled() const OVERRIDE; // TileManagerClient implementation. virtual void NotifyReadyToActivate() OVERRIDE; @@ -201,6 +202,7 @@ class CC_EXPORT LayerTreeHostImpl virtual void BeginFrame(const BeginFrameArgs& args) OVERRIDE; virtual void SetExternalDrawConstraints(const gfx::Transform& transform, gfx::Rect viewport) OVERRIDE; + virtual void SetExternalStencilTest(bool enabled) OVERRIDE; virtual void DidLoseOutputSurface() OVERRIDE; virtual void OnSwapBuffersComplete(const CompositorFrameAck* ack) OVERRIDE; virtual void SetMemoryPolicy( @@ -213,7 +215,7 @@ class CC_EXPORT LayerTreeHostImpl void OnCanDrawStateChangedForTree(); // Implementation - bool CanDraw(); + bool CanDraw() const; OutputSurface* output_surface() const { return output_surface_.get(); } std::string LayerTreeAsJson() const; @@ -524,6 +526,7 @@ class CC_EXPORT LayerTreeHostImpl // used for scrollable size. gfx::Transform external_transform_; gfx::Rect external_viewport_; + bool external_stencil_test_enabled_; gfx::Rect viewport_damage_rect_; diff --git a/cc/trees/layer_tree_host_impl_unittest.cc b/cc/trees/layer_tree_host_impl_unittest.cc index 1fb6c380ed..f26fe7bd89 100644 --- a/cc/trees/layer_tree_host_impl_unittest.cc +++ b/cc/trees/layer_tree_host_impl_unittest.cc @@ -265,6 +265,72 @@ class LayerTreeHostImplTest : public testing::Test, void pinch_zoom_pan_viewport_and_scroll_boundary_test( float device_scale_factor); + void CheckNotifyCalledIfCanDrawChanged(bool always_draw) { + // Note: It is not possible to disable the renderer once it has been set, + // so we do not need to test that disabling the renderer notifies us + // that can_draw changed. + EXPECT_FALSE(host_impl_->CanDraw()); + on_can_draw_state_changed_called_ = false; + + // Set up the root layer, which allows us to draw. + SetupScrollAndContentsLayers(gfx::Size(100, 100)); + EXPECT_TRUE(host_impl_->CanDraw()); + EXPECT_TRUE(on_can_draw_state_changed_called_); + on_can_draw_state_changed_called_ = false; + + // Toggle the root layer to make sure it toggles can_draw + host_impl_->active_tree()->SetRootLayer(scoped_ptr<LayerImpl>()); + EXPECT_FALSE(host_impl_->CanDraw()); + EXPECT_TRUE(on_can_draw_state_changed_called_); + on_can_draw_state_changed_called_ = false; + + SetupScrollAndContentsLayers(gfx::Size(100, 100)); + EXPECT_TRUE(host_impl_->CanDraw()); + EXPECT_TRUE(on_can_draw_state_changed_called_); + on_can_draw_state_changed_called_ = false; + + // Toggle the device viewport size to make sure it toggles can_draw. + host_impl_->SetViewportSize(gfx::Size()); + if (always_draw) { + EXPECT_TRUE(host_impl_->CanDraw()); + } else { + EXPECT_FALSE(host_impl_->CanDraw()); + } + EXPECT_TRUE(on_can_draw_state_changed_called_); + on_can_draw_state_changed_called_ = false; + + host_impl_->SetViewportSize(gfx::Size(100, 100)); + EXPECT_TRUE(host_impl_->CanDraw()); + EXPECT_TRUE(on_can_draw_state_changed_called_); + on_can_draw_state_changed_called_ = false; + + // Toggle contents textures purged without causing any evictions, + // and make sure that it does not change can_draw. + set_reduce_memory_result(false); + host_impl_->SetMemoryPolicy(ManagedMemoryPolicy( + host_impl_->memory_allocation_limit_bytes() - 1), true); + EXPECT_TRUE(host_impl_->CanDraw()); + EXPECT_FALSE(on_can_draw_state_changed_called_); + on_can_draw_state_changed_called_ = false; + + // Toggle contents textures purged to make sure it toggles can_draw. + set_reduce_memory_result(true); + host_impl_->SetMemoryPolicy(ManagedMemoryPolicy( + host_impl_->memory_allocation_limit_bytes() - 1), true); + if (always_draw) { + EXPECT_TRUE(host_impl_->CanDraw()); + } else { + EXPECT_FALSE(host_impl_->CanDraw()); + } + EXPECT_TRUE(on_can_draw_state_changed_called_); + on_can_draw_state_changed_called_ = false; + + host_impl_->active_tree()->ResetContentsTexturesPurged(); + EXPECT_TRUE(host_impl_->CanDraw()); + EXPECT_TRUE(on_can_draw_state_changed_called_); + on_can_draw_state_changed_called_ = false; + } + protected: virtual scoped_ptr<OutputSurface> CreateOutputSurface() { return CreateFakeOutputSurface(); @@ -294,69 +360,30 @@ class LayerTreeHostImplTest : public testing::Test, int current_priority_cutoff_value_; }; +TEST_F(LayerTreeHostImplTest, NotifyIfCanDrawChanged) { + bool always_draw = false; + CheckNotifyCalledIfCanDrawChanged(always_draw); +} + +TEST_F(LayerTreeHostImplTest, CanDrawIncompleteFrames) { + LayerTreeSettings settings; + settings.impl_side_painting = true; + host_impl_ = LayerTreeHostImpl::Create( + settings, this, &proxy_, &stats_instrumentation_); + host_impl_->InitializeRenderer( + FakeOutputSurface::CreateAlwaysDrawAndSwap3d().PassAs<OutputSurface>()); + host_impl_->SetViewportSize(gfx::Size(10, 10)); + + bool always_draw = true; + CheckNotifyCalledIfCanDrawChanged(always_draw); +} + class TestWebGraphicsContext3DMakeCurrentFails : public TestWebGraphicsContext3D { public: virtual bool makeContextCurrent() OVERRIDE { return false; } }; -TEST_F(LayerTreeHostImplTest, NotifyIfCanDrawChanged) { - // Note: It is not possible to disable the renderer once it has been set, - // so we do not need to test that disabling the renderer notifies us - // that can_draw changed. - EXPECT_FALSE(host_impl_->CanDraw()); - on_can_draw_state_changed_called_ = false; - - SetupScrollAndContentsLayers(gfx::Size(100, 100)); - EXPECT_TRUE(host_impl_->CanDraw()); - EXPECT_TRUE(on_can_draw_state_changed_called_); - on_can_draw_state_changed_called_ = false; - - // Toggle the root layer to make sure it toggles can_draw - host_impl_->active_tree()->SetRootLayer(scoped_ptr<LayerImpl>()); - EXPECT_FALSE(host_impl_->CanDraw()); - EXPECT_TRUE(on_can_draw_state_changed_called_); - on_can_draw_state_changed_called_ = false; - - SetupScrollAndContentsLayers(gfx::Size(100, 100)); - EXPECT_TRUE(host_impl_->CanDraw()); - EXPECT_TRUE(on_can_draw_state_changed_called_); - on_can_draw_state_changed_called_ = false; - - // Toggle the device viewport size to make sure it toggles can_draw. - host_impl_->SetViewportSize(gfx::Size()); - EXPECT_FALSE(host_impl_->CanDraw()); - EXPECT_TRUE(on_can_draw_state_changed_called_); - on_can_draw_state_changed_called_ = false; - - host_impl_->SetViewportSize(gfx::Size(100, 100)); - EXPECT_TRUE(host_impl_->CanDraw()); - EXPECT_TRUE(on_can_draw_state_changed_called_); - on_can_draw_state_changed_called_ = false; - - // Toggle contents textures purged without causing any evictions, - // and make sure that it does not change can_draw. - set_reduce_memory_result(false); - host_impl_->SetMemoryPolicy(ManagedMemoryPolicy( - host_impl_->memory_allocation_limit_bytes() - 1), true); - EXPECT_TRUE(host_impl_->CanDraw()); - EXPECT_FALSE(on_can_draw_state_changed_called_); - on_can_draw_state_changed_called_ = false; - - // Toggle contents textures purged to make sure it toggles can_draw. - set_reduce_memory_result(true); - host_impl_->SetMemoryPolicy(ManagedMemoryPolicy( - host_impl_->memory_allocation_limit_bytes() - 1), true); - EXPECT_FALSE(host_impl_->CanDraw()); - EXPECT_TRUE(on_can_draw_state_changed_called_); - on_can_draw_state_changed_called_ = false; - - host_impl_->active_tree()->ResetContentsTexturesPurged(); - EXPECT_TRUE(host_impl_->CanDraw()); - EXPECT_TRUE(on_can_draw_state_changed_called_); - on_can_draw_state_changed_called_ = false; -} - TEST_F(LayerTreeHostImplTest, ScrollDeltaNoLayers) { ASSERT_FALSE(host_impl_->active_tree()->root_layer()); @@ -2771,32 +2798,47 @@ TEST_F(LayerTreeHostImplTest, BlendingOffWhenDrawingOpaqueLayers) { host_impl_->DidDrawAllLayers(frame); } -TEST_F(LayerTreeHostImplTest, ViewportCovered) { - host_impl_->InitializeRenderer(CreateOutputSurface()); - host_impl_->active_tree()->set_background_color(SK_ColorGRAY); - - gfx::Size viewport_size(1000, 1000); - host_impl_->SetViewportSize(viewport_size); - - host_impl_->active_tree()->SetRootLayer( - LayerImpl::Create(host_impl_->active_tree(), 1)); - host_impl_->active_tree()->root_layer()->AddChild( - BlendStateCheckLayer::Create(host_impl_->active_tree(), - 2, - host_impl_->resource_provider())); - BlendStateCheckLayer* child = static_cast<BlendStateCheckLayer*>( - host_impl_->active_tree()->root_layer()->children()[0]); - child->SetExpectation(false, false); - child->SetContentsOpaque(true); - - // No gutter rects - { - gfx::Rect layer_rect(0, 0, 1000, 1000); - child->SetPosition(layer_rect.origin()); - child->SetBounds(layer_rect.size()); - child->SetContentBounds(layer_rect.size()); - child->SetQuadRect(gfx::Rect(layer_rect.size())); - child->SetQuadVisibleRect(gfx::Rect(layer_rect.size())); +class LayerTreeHostImplViewportCoveredTest : public LayerTreeHostImplTest { + public: + void CreateLayerTreeHostImpl(bool always_draw) { + LayerTreeSettings settings; + settings.minimum_occlusion_tracking_size = gfx::Size(); + settings.impl_side_painting = true; + host_impl_ = LayerTreeHostImpl::Create( + settings, this, &proxy_, &stats_instrumentation_); + scoped_ptr<OutputSurface> output_surface; + if (always_draw) { + output_surface = FakeOutputSurface::CreateAlwaysDrawAndSwap3d() + .PassAs<OutputSurface>(); + } else { + output_surface = CreateFakeOutputSurface(); + } + host_impl_->InitializeRenderer(output_surface.Pass()); + viewport_size_ = gfx::Size(1000, 1000); + } + + void SetupActiveTreeLayers() { + host_impl_->active_tree()->set_background_color(SK_ColorGRAY); + host_impl_->active_tree()->SetRootLayer( + LayerImpl::Create(host_impl_->active_tree(), 1)); + host_impl_->active_tree()->root_layer()->AddChild( + BlendStateCheckLayer::Create(host_impl_->active_tree(), + 2, + host_impl_->resource_provider())); + child_ = static_cast<BlendStateCheckLayer*>( + host_impl_->active_tree()->root_layer()->children()[0]); + child_->SetExpectation(false, false); + child_->SetContentsOpaque(true); + } + + // Expect no gutter rects. + void TestLayerCoversFullViewport() { + gfx::Rect layer_rect(viewport_size_); + child_->SetPosition(layer_rect.origin()); + child_->SetBounds(layer_rect.size()); + child_->SetContentBounds(layer_rect.size()); + child_->SetQuadRect(gfx::Rect(layer_rect.size())); + child_->SetQuadVisibleRect(gfx::Rect(layer_rect.size())); LayerTreeHostImpl::FrameData frame; EXPECT_TRUE(host_impl_->PrepareToDraw(&frame, gfx::Rect())); @@ -2810,18 +2852,18 @@ TEST_F(LayerTreeHostImplTest, ViewportCovered) { EXPECT_EQ(1u, frame.render_passes[0]->quad_list.size()); LayerTestCommon::VerifyQuadsExactlyCoverRect( - frame.render_passes[0]->quad_list, gfx::Rect(viewport_size)); + frame.render_passes[0]->quad_list, gfx::Rect(viewport_size_)); host_impl_->DidDrawAllLayers(frame); } - // Empty visible content area (fullscreen gutter rect) - { + // Expect fullscreen gutter rect. + void TestEmptyLayer() { gfx::Rect layer_rect(0, 0, 0, 0); - child->SetPosition(layer_rect.origin()); - child->SetBounds(layer_rect.size()); - child->SetContentBounds(layer_rect.size()); - child->SetQuadRect(gfx::Rect(layer_rect.size())); - child->SetQuadVisibleRect(gfx::Rect(layer_rect.size())); + child_->SetPosition(layer_rect.origin()); + child_->SetBounds(layer_rect.size()); + child_->SetContentBounds(layer_rect.size()); + child_->SetQuadRect(gfx::Rect(layer_rect.size())); + child_->SetQuadVisibleRect(gfx::Rect(layer_rect.size())); LayerTreeHostImpl::FrameData frame; EXPECT_TRUE(host_impl_->PrepareToDraw(&frame, gfx::Rect())); @@ -2835,18 +2877,18 @@ TEST_F(LayerTreeHostImplTest, ViewportCovered) { EXPECT_EQ(1u, frame.render_passes[0]->quad_list.size()); LayerTestCommon::VerifyQuadsExactlyCoverRect( - frame.render_passes[0]->quad_list, gfx::Rect(viewport_size)); + frame.render_passes[0]->quad_list, gfx::Rect(viewport_size_)); host_impl_->DidDrawAllLayers(frame); } - // Content area in middle of clip rect (four surrounding gutter rects) - { + // Expect four surrounding gutter rects. + void TestLayerInMiddleOfViewport() { gfx::Rect layer_rect(500, 500, 200, 200); - child->SetPosition(layer_rect.origin()); - child->SetBounds(layer_rect.size()); - child->SetContentBounds(layer_rect.size()); - child->SetQuadRect(gfx::Rect(layer_rect.size())); - child->SetQuadVisibleRect(gfx::Rect(layer_rect.size())); + child_->SetPosition(layer_rect.origin()); + child_->SetBounds(layer_rect.size()); + child_->SetContentBounds(layer_rect.size()); + child_->SetQuadRect(gfx::Rect(layer_rect.size())); + child_->SetQuadVisibleRect(gfx::Rect(layer_rect.size())); LayerTreeHostImpl::FrameData frame; EXPECT_TRUE(host_impl_->PrepareToDraw(&frame, gfx::Rect())); @@ -2860,11 +2902,96 @@ TEST_F(LayerTreeHostImplTest, ViewportCovered) { EXPECT_EQ(5u, frame.render_passes[0]->quad_list.size()); LayerTestCommon::VerifyQuadsExactlyCoverRect( - frame.render_passes[0]->quad_list, gfx::Rect(viewport_size)); + frame.render_passes[0]->quad_list, gfx::Rect(viewport_size_)); + host_impl_->DidDrawAllLayers(frame); + } + + // Expect no gutter rects. + void TestLayerIsLargerThanViewport() { + gfx::Rect layer_rect(viewport_size_.width() + 10, + viewport_size_.height() + 10); + child_->SetPosition(layer_rect.origin()); + child_->SetBounds(layer_rect.size()); + child_->SetContentBounds(layer_rect.size()); + child_->SetQuadRect(gfx::Rect(layer_rect.size())); + child_->SetQuadVisibleRect(gfx::Rect(layer_rect.size())); + + LayerTreeHostImpl::FrameData frame; + EXPECT_TRUE(host_impl_->PrepareToDraw(&frame, gfx::Rect())); + ASSERT_EQ(1u, frame.render_passes.size()); + + size_t num_gutter_quads = 0; + for (size_t i = 0; i < frame.render_passes[0]->quad_list.size(); ++i) + num_gutter_quads += (frame.render_passes[0]->quad_list[i]->material == + DrawQuad::SOLID_COLOR) ? 1 : 0; + EXPECT_EQ(0u, num_gutter_quads); + EXPECT_EQ(1u, frame.render_passes[0]->quad_list.size()); + host_impl_->DidDrawAllLayers(frame); } + + virtual void DidActivatePendingTree() OVERRIDE { + did_activate_pending_tree_ = true; + } + + protected: + gfx::Size viewport_size_; + BlendStateCheckLayer* child_; + bool did_activate_pending_tree_; +}; + +TEST_F(LayerTreeHostImplViewportCoveredTest, ViewportCovered) { + bool always_draw = false; + CreateLayerTreeHostImpl(always_draw); + + host_impl_->SetViewportSize(viewport_size_); + SetupActiveTreeLayers(); + TestLayerCoversFullViewport(); + TestEmptyLayer(); + TestLayerInMiddleOfViewport(); + TestLayerIsLargerThanViewport(); } +TEST_F(LayerTreeHostImplViewportCoveredTest, ActiveTreeGrowViewportInvalid) { + bool always_draw = true; + CreateLayerTreeHostImpl(always_draw); + + // Pending tree to force active_tree size invalid. Not used otherwise. + host_impl_->CreatePendingTree(); + host_impl_->SetViewportSize(viewport_size_); + EXPECT_TRUE(host_impl_->active_tree()->ViewportSizeInvalid()); + + SetupActiveTreeLayers(); + TestEmptyLayer(); + TestLayerInMiddleOfViewport(); + TestLayerIsLargerThanViewport(); +} + +TEST_F(LayerTreeHostImplViewportCoveredTest, ActiveTreeShrinkViewportInvalid) { + bool always_draw = true; + CreateLayerTreeHostImpl(always_draw); + + // Set larger viewport and activate it to active tree. + host_impl_->CreatePendingTree(); + gfx::Size larger_viewport(viewport_size_.width() + 100, + viewport_size_.height() + 100); + host_impl_->SetViewportSize(larger_viewport); + EXPECT_TRUE(host_impl_->active_tree()->ViewportSizeInvalid()); + did_activate_pending_tree_ = false; + host_impl_->ActivatePendingTreeIfNeeded(); + EXPECT_TRUE(did_activate_pending_tree_); + EXPECT_FALSE(host_impl_->active_tree()->ViewportSizeInvalid()); + + // Shrink pending tree viewport without activating. + host_impl_->CreatePendingTree(); + host_impl_->SetViewportSize(viewport_size_); + EXPECT_TRUE(host_impl_->active_tree()->ViewportSizeInvalid()); + + SetupActiveTreeLayers(); + TestEmptyLayer(); + TestLayerInMiddleOfViewport(); + TestLayerIsLargerThanViewport(); +} class ReshapeTrackerContext: public TestWebGraphicsContext3D { public: @@ -4793,6 +4920,7 @@ class TestRenderer : public GLRenderer, public RendererClient { virtual bool AllowPartialSwap() const OVERRIDE { return true; } + virtual bool ExternalStencilTestEnabled() const OVERRIDE { return false; } protected: TestRenderer(ResourceProvider* resource_provider, diff --git a/cc/trees/layer_tree_impl.cc b/cc/trees/layer_tree_impl.cc index c160d97bb9..8d1d04861e 100644 --- a/cc/trees/layer_tree_impl.cc +++ b/cc/trees/layer_tree_impl.cc @@ -584,10 +584,9 @@ void LayerTreeImpl::AddLayerWithCopyOutputRequest(LayerImpl* layer) { // they are aborted if not serviced during draw. DCHECK(IsActiveTree()); - if (std::find(layers_with_copy_output_request_.begin(), - layers_with_copy_output_request_.end(), - layer) != layers_with_copy_output_request_.end()) - return; + DCHECK(std::find(layers_with_copy_output_request_.begin(), + layers_with_copy_output_request_.end(), + layer) == layers_with_copy_output_request_.end()); layers_with_copy_output_request_.push_back(layer); } @@ -601,8 +600,7 @@ void LayerTreeImpl::RemoveLayerWithCopyOutputRequest(LayerImpl* layer) { layers_with_copy_output_request_.end(), layer); DCHECK(it != layers_with_copy_output_request_.end()); - if (it != layers_with_copy_output_request_.end()) - layers_with_copy_output_request_.erase(it); + layers_with_copy_output_request_.erase(it); } const std::vector<LayerImpl*> LayerTreeImpl::LayersWithCopyOutputRequest() |