cc: Minimize the number of times GL scissoring state is changed
We may sometimes want to turn off the scissoring optimization. At this time
scissoring is used as both an optimization and for correctness when a layer is
clipped. This patch separates those concepts and rearranges scissoring logic
in an attempt to minimize how often scissoring state changes; scissoring
changes very likely cause graphics pipeline to flush which is definitely
undesirable.
BUG=160358
Review URL: https://chromiumcodereview.appspot.com/11362220
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@169142 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/cc/delegated_renderer_layer_impl_unittest.cc b/cc/delegated_renderer_layer_impl_unittest.cc
index f39285c7..46a74883 100644
--- a/cc/delegated_renderer_layer_impl_unittest.cc
+++ b/cc/delegated_renderer_layer_impl_unittest.cc
@@ -80,7 +80,7 @@
MockQuadCuller quadSink(pass->quad_list, pass->shared_quad_state_list);
AppendQuadsData data(pass->id);
SharedQuadState* sharedState = quadSink.useSharedQuadState(SharedQuadState::Create());
- sharedState->SetAll(WebTransformationMatrix(), rect, rect, 1);
+ sharedState->SetAll(WebTransformationMatrix(), rect, rect, rect, false, 1);
scoped_ptr<SolidColorDrawQuad> quad = SolidColorDrawQuad::Create();
quad->SetNew(sharedState, rect, color);
SolidColorDrawQuad* quadPtr = quad.get();
@@ -94,7 +94,7 @@
AppendQuadsData data(toPass->id);
gfx::Rect outputRect = contributingPass->output_rect;
SharedQuadState* sharedState = quadSink.useSharedQuadState(SharedQuadState::Create());
- sharedState->SetAll(WebTransformationMatrix(), outputRect, outputRect, 1);
+ sharedState->SetAll(WebTransformationMatrix(), outputRect, outputRect, outputRect, false, 1);
scoped_ptr<RenderPassDrawQuad> quad = RenderPassDrawQuad::Create();
quad->SetNew(sharedState, outputRect, contributingPass->id, false, 0, outputRect, 0, 0, 0, 0);
quadSink.append(quad.PassAs<DrawQuad>(), data);
@@ -408,7 +408,7 @@
MockQuadCuller quadSink(pass->quad_list, pass->shared_quad_state_list);
AppendQuadsData data(pass->id);
SharedQuadState* sharedState = quadSink.useSharedQuadState(SharedQuadState::Create());
- sharedState->SetAll(WebTransformationMatrix(), passRect, passRect, 1);
+ sharedState->SetAll(WebTransformationMatrix(), passRect, passRect, passRect, false, 1);
scoped_ptr<SolidColorDrawQuad> colorQuad;
colorQuad = SolidColorDrawQuad::Create();
diff --git a/cc/direct_renderer.cc b/cc/direct_renderer.cc
index 7e2e31e7..c24ca01 100644
--- a/cc/direct_renderer.cc
+++ b/cc/direct_renderer.cc
@@ -169,29 +169,74 @@
finishDrawingFrame(frame);
}
+gfx::RectF DirectRenderer::computeScissorRectForRenderPass(const DrawingFrame& frame)
+{
+ gfx::RectF renderPassScissor = frame.currentRenderPass->output_rect;
+
+ if (frame.rootDamageRect == frame.rootRenderPass->output_rect)
+ return renderPassScissor;
+
+ WebTransformationMatrix inverseTransform = frame.currentRenderPass->transform_to_root_target.inverse();
+ gfx::RectF damageRectInRenderPassSpace = MathUtil::projectClippedRect(inverseTransform, frame.rootDamageRect);
+ renderPassScissor.Intersect(damageRectInRenderPassSpace);
+
+ return renderPassScissor;
+}
+
+void DirectRenderer::setScissorStateForQuad(const DrawingFrame& frame, const DrawQuad& quad)
+{
+ if (quad.isClipped()) {
+ gfx::RectF quadScissorRect = quad.clipRect();
+ setScissorTestRect(moveScissorToWindowSpace(frame, quadScissorRect));
+ }
+ else
+ ensureScissorTestDisabled();
+}
+
+void DirectRenderer::setScissorStateForQuadWithRenderPassScissor(const DrawingFrame& frame, const DrawQuad& quad, const gfx::RectF& renderPassScissor, bool* shouldSkipQuad)
+{
+ gfx::RectF quadScissorRect = renderPassScissor;
+
+ if (quad.isClipped())
+ quadScissorRect.Intersect(quad.clipRect());
+
+ if (quadScissorRect.IsEmpty()) {
+ *shouldSkipQuad = true;
+ return;
+ }
+
+ *shouldSkipQuad = false;
+ setScissorTestRect(moveScissorToWindowSpace(frame, quadScissorRect));
+}
+
void DirectRenderer::drawRenderPass(DrawingFrame& frame, const RenderPass* renderPass)
{
TRACE_EVENT0("cc", "DirectRenderer::drawRenderPass");
if (!useRenderPass(frame, renderPass))
return;
- frame.scissorRectInRenderPassSpace = frame.currentRenderPass->output_rect;
- if (frame.rootDamageRect != frame.rootRenderPass->output_rect) {
- WebTransformationMatrix inverseTransformToRoot = frame.currentRenderPass->transform_to_root_target.inverse();
- gfx::RectF damageRectInRenderPassSpace = MathUtil::projectClippedRect(inverseTransformToRoot, frame.rootDamageRect);
- frame.scissorRectInRenderPassSpace.Intersect(damageRectInRenderPassSpace);
+ bool usingScissorAsOptimization = capabilities().usingPartialSwap;
+ gfx::RectF renderPassScissor;
+
+ if (usingScissorAsOptimization) {
+ renderPassScissor = computeScissorRectForRenderPass(frame);
+ setScissorTestRect(moveScissorToWindowSpace(frame, renderPassScissor));
}
- setScissorTestRect(moveScissorToWindowSpace(frame, frame.scissorRectInRenderPassSpace));
clearFramebuffer(frame);
const QuadList& quadList = renderPass->quad_list;
for (QuadList::constBackToFrontIterator it = quadList.backToFrontBegin(); it != quadList.backToFrontEnd(); ++it) {
- gfx::RectF quadScissorRect = gfx::IntersectRects(frame.scissorRectInRenderPassSpace, (*it)->clippedRectInTarget());
- if (!quadScissorRect.IsEmpty()) {
- setScissorTestRect(moveScissorToWindowSpace(frame, quadScissorRect));
+ const DrawQuad& quad = *(*it);
+ bool shouldSkipQuad = false;
+
+ if (usingScissorAsOptimization)
+ setScissorStateForQuadWithRenderPassScissor(frame, quad, renderPassScissor, &shouldSkipQuad);
+ else
+ setScissorStateForQuad(frame, quad);
+
+ if (!shouldSkipQuad)
drawQuad(frame, *it);
- }
}
CachedResource* texture = m_renderPassTextures.get(renderPass->id);
diff --git a/cc/direct_renderer.h b/cc/direct_renderer.h
index 716f8582..27c42a6 100644
--- a/cc/direct_renderer.h
+++ b/cc/direct_renderer.h
@@ -42,7 +42,6 @@
WebKit::WebTransformationMatrix projectionMatrix;
WebKit::WebTransformationMatrix windowMatrix;
bool flippedY;
- gfx::RectF scissorRectInRenderPassSpace;
};
protected:
@@ -75,6 +74,9 @@
static void quadRectTransform(WebKit::WebTransformationMatrix* quadRectTransform, const WebKit::WebTransformationMatrix& quadTransform, const gfx::RectF& quadRect);
static void initializeMatrices(DrawingFrame&, const gfx::Rect& drawRect, bool flipY);
static gfx::Rect moveScissorToWindowSpace(const DrawingFrame&, gfx::RectF scissorRect);
+ static gfx::RectF computeScissorRectForRenderPass(const DrawingFrame& frame);
+ void setScissorStateForQuad(const DrawingFrame& frame, const DrawQuad& quad);
+ void setScissorStateForQuadWithRenderPassScissor(const DrawingFrame& frame, const DrawQuad& quad, const gfx::RectF& renderPassScissor, bool* shouldSkipQuad);
bool haveCachedResources(RenderPass::Id) const;
static gfx::Size renderPassTextureSize(const RenderPass*);
@@ -92,6 +94,8 @@
virtual void beginDrawingFrame(DrawingFrame&) = 0;
virtual void finishDrawingFrame(DrawingFrame&) = 0;
virtual bool flippedFramebuffer() const = 0;
+ virtual void ensureScissorTestEnabled() = 0;
+ virtual void ensureScissorTestDisabled() = 0;
ScopedPtrHashMap<RenderPass::Id, CachedResource> m_renderPassTextures;
ResourceProvider* m_resourceProvider;
diff --git a/cc/draw_quad.h b/cc/draw_quad.h
index 7dee19c..492630c 100644
--- a/cc/draw_quad.h
+++ b/cc/draw_quad.h
@@ -38,6 +38,8 @@
const WebKit::WebTransformationMatrix& quadTransform() const { return shared_quad_state->content_to_target_transform; }
gfx::Rect visibleContentRect() const { return shared_quad_state->visible_content_rect; }
gfx::Rect clippedRectInTarget() const { return shared_quad_state->clipped_rect_in_target; }
+ gfx::Rect clipRect() const { return shared_quad_state->clip_rect; }
+ bool isClipped() const { return shared_quad_state->is_clipped; }
float opacity() const { return shared_quad_state->opacity; }
Material material;
diff --git a/cc/draw_quad_unittest.cc b/cc/draw_quad_unittest.cc
index d1d8c99a..f541070 100644
--- a/cc/draw_quad_unittest.cc
+++ b/cc/draw_quad_unittest.cc
@@ -27,16 +27,20 @@
WebTransformationMatrix quadTransform(1, 0.5, 0, 1, 0.5, 0);
gfx::Rect visibleContentRect(10, 12, 14, 16);
gfx::Rect clippedRectInTarget(19, 21, 23, 25);
+ gfx::Rect clipRect = clippedRectInTarget;
+ bool isClipped = true;
float opacity = 0.25;
scoped_ptr<SharedQuadState> state(SharedQuadState::Create());
- state->SetAll(quadTransform, visibleContentRect, clippedRectInTarget, opacity);
+ state->SetAll(quadTransform, visibleContentRect, clippedRectInTarget, clipRect, isClipped, opacity);
scoped_ptr<SharedQuadState> copy(state->Copy());
EXPECT_EQ(quadTransform, copy->content_to_target_transform);
EXPECT_RECT_EQ(visibleContentRect, copy->visible_content_rect);
EXPECT_RECT_EQ(clippedRectInTarget, copy->clipped_rect_in_target);
EXPECT_EQ(opacity, copy->opacity);
+ EXPECT_RECT_EQ(clipRect, copy->clip_rect);
+ EXPECT_EQ(isClipped, copy->is_clipped);
}
scoped_ptr<SharedQuadState> createSharedQuadState()
@@ -44,10 +48,12 @@
WebTransformationMatrix quadTransform(1, 0.5, 0, 1, 0.5, 0);
gfx::Rect visibleContentRect(10, 12, 14, 16);
gfx::Rect clippedRectInTarget(19, 21, 23, 25);
+ gfx::Rect clipRect = clippedRectInTarget;
+ bool isClipped = false;
float opacity = 1;
scoped_ptr<SharedQuadState> state(SharedQuadState::Create());
- state->SetAll(quadTransform, visibleContentRect, clippedRectInTarget, opacity);
+ state->SetAll(quadTransform, visibleContentRect, clippedRectInTarget, clipRect, isClipped, opacity);
return state.Pass();
}
diff --git a/cc/gl_renderer.cc b/cc/gl_renderer.cc
index 850b8c29..e8454cc 100644
--- a/cc/gl_renderer.cc
+++ b/cc/gl_renderer.cc
@@ -79,6 +79,7 @@
, m_discardFramebufferWhenNotVisible(false)
, m_isUsingBindUniform(false)
, m_visible(true)
+ , m_isScissorEnabled(false)
{
DCHECK(m_context);
}
@@ -131,6 +132,10 @@
m_isUsingBindUniform = extensions.count("GL_CHROMIUM_bind_uniform_location");
+ // Make sure scissoring starts as disabled.
+ GLC(m_context, m_context->disable(GL_SCISSOR_TEST));
+ DCHECK(!m_isScissorEnabled);
+
if (!initializeSharedObjects())
return false;
@@ -235,7 +240,6 @@
GLC(m_context, m_context->disable(GL_DEPTH_TEST));
GLC(m_context, m_context->disable(GL_CULL_FACE));
- GLC(m_context, m_context->enable(GL_SCISSOR_TEST));
GLC(m_context, m_context->colorMask(true, true, true, true));
GLC(m_context, m_context->enable(GL_BLEND));
GLC(m_context, m_context->blendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA));
@@ -1041,6 +1045,24 @@
return true;
}
+void GLRenderer::ensureScissorTestEnabled()
+{
+ if (m_isScissorEnabled)
+ return;
+
+ GLC(m_context, m_context->enable(GL_SCISSOR_TEST));
+ m_isScissorEnabled = true;
+}
+
+void GLRenderer::ensureScissorTestDisabled()
+{
+ if (!m_isScissorEnabled)
+ return;
+
+ GLC(m_context, m_context->disable(GL_SCISSOR_TEST));
+ m_isScissorEnabled = false;
+}
+
void GLRenderer::toGLMatrix(float* flattened, const WebTransformationMatrix& m)
{
flattened[0] = m.m11();
@@ -1344,6 +1366,14 @@
void GLRenderer::setScissorTestRect(const gfx::Rect& scissorRect)
{
+ ensureScissorTestEnabled();
+
+ // Don't unnecessarily ask the context to change the scissor, because it
+ // may cause undesired GPU pipeline flushes.
+ if (scissorRect == m_scissorRect)
+ return;
+
+ m_scissorRect = scissorRect;
GLC(m_context, m_context->scissor(scissorRect.x(), scissorRect.y(), scissorRect.width(), scissorRect.height()));
}
diff --git a/cc/gl_renderer.h b/cc/gl_renderer.h
index 184c144..7116413b 100644
--- a/cc/gl_renderer.h
+++ b/cc/gl_renderer.h
@@ -83,6 +83,8 @@
virtual void beginDrawingFrame(DrawingFrame&) OVERRIDE;
virtual void finishDrawingFrame(DrawingFrame&) OVERRIDE;
virtual bool flippedFramebuffer() const OVERRIDE;
+ virtual void ensureScissorTestEnabled() OVERRIDE;
+ virtual void ensureScissorTestDisabled() OVERRIDE;
private:
static void toGLMatrix(float*, const WebKit::WebTransformationMatrix&);
@@ -210,11 +212,13 @@
WebKit::WebGraphicsContext3D* m_context;
gfx::Rect m_swapBufferRect;
+ gfx::Rect m_scissorRect;
bool m_isViewportChanged;
bool m_isFramebufferDiscarded;
bool m_discardFramebufferWhenNotVisible;
bool m_isUsingBindUniform;
bool m_visible;
+ bool m_isScissorEnabled;
scoped_ptr<ResourceProvider::ScopedWriteLockGL> m_currentFramebufferLock;
diff --git a/cc/layer.cc b/cc/layer.cc
index 5ba84517..63f5e946 100644
--- a/cc/layer.cc
+++ b/cc/layer.cc
@@ -61,6 +61,7 @@
, m_renderTarget(0)
, m_drawTransformIsAnimating(false)
, m_screenSpaceTransformIsAnimating(false)
+ , m_isClipped(false)
, m_rasterScale(1.0)
, m_automaticallyComputeRasterScale(false)
, m_boundsContainPageScale(false)
diff --git a/cc/layer.h b/cc/layer.h
index c947b96..1548880 100644
--- a/cc/layer.h
+++ b/cc/layer.h
@@ -231,6 +231,13 @@
// It converts logical, non-page-scaled pixels to physical pixels.
const WebKit::WebTransformationMatrix& screenSpaceTransform() const { return m_screenSpaceTransform; }
void setScreenSpaceTransform(const WebKit::WebTransformationMatrix& matrix) { m_screenSpaceTransform = matrix; }
+
+ bool isClipped() const { return m_isClipped; }
+ void setIsClipped(bool isClipped) { m_isClipped = isClipped; }
+
+ const gfx::Rect& clipRect() const { return m_clipRect; }
+ void setClipRect(const gfx::Rect& clipRect) { m_clipRect = clipRect; }
+
const gfx::Rect& drawableContentRect() const { return m_drawableContentRect; }
void setDrawableContentRect(const gfx::Rect& rect) { m_drawableContentRect = rect; }
@@ -399,6 +406,11 @@
// Uses target surface space.
gfx::Rect m_drawableContentRect;
+ gfx::Rect m_clipRect;
+
+ // True if the layer is clipped by m_clipRect
+ bool m_isClipped;
+
float m_rasterScale;
bool m_automaticallyComputeRasterScale;
bool m_boundsContainPageScale;
diff --git a/cc/layer_impl.cc b/cc/layer_impl.cc
index 0b341fa..937b812 100644
--- a/cc/layer_impl.cc
+++ b/cc/layer_impl.cc
@@ -57,6 +57,7 @@
, m_filter(0)
, m_drawTransformIsAnimating(false)
, m_screenSpaceTransformIsAnimating(false)
+ , m_isClipped(false)
#ifndef NDEBUG
, m_betweenWillDrawAndDidDraw(false)
#endif
@@ -125,7 +126,7 @@
scoped_ptr<SharedQuadState> LayerImpl::createSharedQuadState() const
{
scoped_ptr<SharedQuadState> state = SharedQuadState::Create();
- state->SetAll(m_drawTransform, m_visibleContentRect, m_drawableContentRect, m_drawOpacity);
+ state->SetAll(m_drawTransform, m_visibleContentRect, m_drawableContentRect, m_clipRect, m_isClipped, m_drawOpacity);
return state.Pass();
}
diff --git a/cc/layer_impl.h b/cc/layer_impl.h
index 026cdbb..1dd36bb 100644
--- a/cc/layer_impl.h
+++ b/cc/layer_impl.h
@@ -241,8 +241,15 @@
bool screenSpaceTransformIsAnimating() const { return m_screenSpaceTransformIsAnimating; }
void setScreenSpaceTransformIsAnimating(bool animating) { m_screenSpaceTransformIsAnimating = animating; }
+ bool isClipped() const { return m_isClipped; }
+ void setIsClipped(bool isClipped) { m_isClipped = isClipped; }
+
+ const gfx::Rect& clipRect() const { return m_clipRect; }
+ void setClipRect(const gfx::Rect& clipRect) { m_clipRect = clipRect; }
+
const gfx::Rect& drawableContentRect() const { return m_drawableContentRect; }
void setDrawableContentRect(const gfx::Rect& rect) { m_drawableContentRect = rect; }
+
const gfx::RectF& updateRect() const { return m_updateRect; }
void setUpdateRect(const gfx::RectF& updateRect) { m_updateRect = updateRect; }
@@ -398,9 +405,12 @@
// will render to this surface.
scoped_ptr<RenderSurfaceImpl> m_renderSurface;
- // Hierarchical bounding rect containing the layer and its descendants.
// Uses target surface's space.
gfx::Rect m_drawableContentRect;
+ gfx::Rect m_clipRect;
+
+ // True if the layer is clipped by m_clipRect.
+ bool m_isClipped;
// Rect indicating what was repainted/updated during update.
// Note that plugin layers bypass this and leave it empty.
diff --git a/cc/layer_tree_host_common.cc b/cc/layer_tree_host_common.cc
index c05ff65..ca7d4505 100644
--- a/cc/layer_tree_host_common.cc
+++ b/cc/layer_tree_host_common.cc
@@ -618,6 +618,7 @@
nearestAncestorThatMovesPixels = renderSurface;
// The render surface clipRect is expressed in the space where this surface draws, i.e. the same space as clipRectFromAncestor.
+ renderSurface->setIsClipped(ancestorClipsSubtree);
if (ancestorClipsSubtree)
renderSurface->setClipRect(clipRectFromAncestor);
else
@@ -705,6 +706,19 @@
drawableContentRectOfLayer.Intersect(clipRectForSubtree);
layer->setDrawableContentRect(drawableContentRectOfLayer);
+ // Tell the layer the rect that is clipped by. In theory we could use a
+ // tighter clipRect here (drawableContentRect), but that actually does not
+ // reduce how much would be drawn, and instead it would create unnecessary
+ // changes to scissor state affecting GPU performance.
+ layer->setIsClipped(subtreeShouldBeClipped);
+ if (subtreeShouldBeClipped)
+ layer->setClipRect(clipRectForSubtree);
+ else {
+ // Initialize the clipRect to a safe value that will not clip the
+ // layer, just in case clipping is still accidentally used.
+ layer->setClipRect(rectInTargetSpace);
+ }
+
// Compute the layer's visible content rect (the rect is in content space)
gfx::Rect visibleContentRectOfLayer = calculateVisibleContentRect(layer);
layer->setVisibleContentRect(visibleContentRectOfLayer);
diff --git a/cc/layer_tree_host_common_unittest.cc b/cc/layer_tree_host_common_unittest.cc
index 76ecf57b..456ea52f 100644
--- a/cc/layer_tree_host_common_unittest.cc
+++ b/cc/layer_tree_host_common_unittest.cc
@@ -1442,6 +1442,106 @@
EXPECT_EQ(grandChild->id(), renderSurfaceLayerList[2]->id());
}
+TEST(LayerTreeHostCommonTest, verifyIsClippedIsSetCorrectly)
+{
+ // Layer's isClipped() property is set to true when:
+ // - the layer clips its subtree, e.g. masks to bounds,
+ // - the layer is clipped by an ancestor that contributes to the same
+ // renderTarget,
+ // - a surface is clipped by an ancestor that contributes to the same
+ // renderTarget.
+ //
+ // In particular, for a layer that owns a renderSurface:
+ // - the renderSurfarce inherits any clip from ancestors, and does NOT
+ // pass that clipped status to the layer itself.
+ // - but if the layer itself masks to bounds, it is considered clipped
+ // and propagates the clip to the subtree.
+
+ const WebTransformationMatrix identityMatrix;
+ scoped_refptr<Layer> root = Layer::create();
+ scoped_refptr<Layer> parent = Layer::create();
+ scoped_refptr<Layer> child1 = Layer::create();
+ scoped_refptr<Layer> child2 = Layer::create();
+ scoped_refptr<Layer> grandChild = Layer::create();
+ scoped_refptr<LayerWithForcedDrawsContent> leafNode1 = make_scoped_refptr(new LayerWithForcedDrawsContent());
+ scoped_refptr<LayerWithForcedDrawsContent> leafNode2 = make_scoped_refptr(new LayerWithForcedDrawsContent());
+ root->addChild(parent);
+ parent->addChild(child1);
+ parent->addChild(child2);
+ child1->addChild(grandChild);
+ child2->addChild(leafNode2);
+ grandChild->addChild(leafNode1);
+
+ child2->setForceRenderSurface(true);
+
+ setLayerPropertiesForTesting(root.get(), identityMatrix, identityMatrix, gfx::PointF(0, 0), gfx::PointF(0, 0), gfx::Size(100, 100), false);
+ setLayerPropertiesForTesting(parent.get(), identityMatrix, identityMatrix, gfx::PointF(0, 0), gfx::PointF(0, 0), gfx::Size(100, 100), false);
+ setLayerPropertiesForTesting(child1.get(), identityMatrix, identityMatrix, gfx::PointF(0, 0), gfx::PointF(0, 0), gfx::Size(100, 100), false);
+ setLayerPropertiesForTesting(child2.get(), identityMatrix, identityMatrix, gfx::PointF(0, 0), gfx::PointF(0, 0), gfx::Size(100, 100), false);
+ setLayerPropertiesForTesting(grandChild.get(), identityMatrix, identityMatrix, gfx::PointF(0, 0), gfx::PointF(0, 0), gfx::Size(100, 100), false);
+ setLayerPropertiesForTesting(leafNode1.get(), identityMatrix, identityMatrix, gfx::PointF(0, 0), gfx::PointF(0, 0), gfx::Size(100, 100), false);
+ setLayerPropertiesForTesting(leafNode2.get(), identityMatrix, identityMatrix, gfx::PointF(0, 0), gfx::PointF(0, 0), gfx::Size(100, 100), false);
+
+ // Case 1: nothing is clipped except the root renderSurface.
+ std::vector<scoped_refptr<Layer> > renderSurfaceLayerList;
+ int dummyMaxTextureSize = 512;
+ LayerTreeHostCommon::calculateDrawTransforms(root.get(), parent->bounds(), 1, 1, dummyMaxTextureSize, renderSurfaceLayerList);
+
+ ASSERT_TRUE(root->renderSurface());
+ ASSERT_TRUE(child2->renderSurface());
+
+ EXPECT_FALSE(root->isClipped());
+ EXPECT_TRUE(root->renderSurface()->isClipped());
+ EXPECT_FALSE(parent->isClipped());
+ EXPECT_FALSE(child1->isClipped());
+ EXPECT_FALSE(child2->isClipped());
+ EXPECT_FALSE(child2->renderSurface()->isClipped());
+ EXPECT_FALSE(grandChild->isClipped());
+ EXPECT_FALSE(leafNode1->isClipped());
+ EXPECT_FALSE(leafNode2->isClipped());
+
+ // Case 2: parent masksToBounds, so the parent, child1, and child2's
+ // surface are clipped. But layers that contribute to child2's surface are
+ // not clipped explicitly because child2's surface already accounts for
+ // that clip.
+ renderSurfaceLayerList.clear();
+ parent->setMasksToBounds(true);
+ LayerTreeHostCommon::calculateDrawTransforms(root.get(), parent->bounds(), 1, 1, dummyMaxTextureSize, renderSurfaceLayerList);
+
+ ASSERT_TRUE(root->renderSurface());
+ ASSERT_TRUE(child2->renderSurface());
+
+ EXPECT_FALSE(root->isClipped());
+ EXPECT_TRUE(root->renderSurface()->isClipped());
+ EXPECT_TRUE(parent->isClipped());
+ EXPECT_TRUE(child1->isClipped());
+ EXPECT_FALSE(child2->isClipped());
+ EXPECT_TRUE(child2->renderSurface()->isClipped());
+ EXPECT_TRUE(grandChild->isClipped());
+ EXPECT_TRUE(leafNode1->isClipped());
+ EXPECT_FALSE(leafNode2->isClipped());
+
+ // Case 3: child2 masksToBounds. The layer and subtree are clipped, and
+ // child2's renderSurface is not clipped.
+ renderSurfaceLayerList.clear();
+ parent->setMasksToBounds(false);
+ child2->setMasksToBounds(true);
+ LayerTreeHostCommon::calculateDrawTransforms(root.get(), parent->bounds(), 1, 1, dummyMaxTextureSize, renderSurfaceLayerList);
+
+ ASSERT_TRUE(root->renderSurface());
+ ASSERT_TRUE(child2->renderSurface());
+
+ EXPECT_FALSE(root->isClipped());
+ EXPECT_TRUE(root->renderSurface()->isClipped());
+ EXPECT_FALSE(parent->isClipped());
+ EXPECT_FALSE(child1->isClipped());
+ EXPECT_TRUE(child2->isClipped());
+ EXPECT_FALSE(child2->renderSurface()->isClipped());
+ EXPECT_FALSE(grandChild->isClipped());
+ EXPECT_FALSE(leafNode1->isClipped());
+ EXPECT_TRUE(leafNode2->isClipped());
+}
+
TEST(LayerTreeHostCommonTest, verifyDrawableContentRectForLayers)
{
// Verify that layers get the appropriate drawableContentRect when their parent masksToBounds is true.
diff --git a/cc/layer_tree_host_impl.cc b/cc/layer_tree_host_impl.cc
index 54b50810..057c363 100644
--- a/cc/layer_tree_host_impl.cc
+++ b/cc/layer_tree_host_impl.cc
@@ -453,6 +453,8 @@
sharedQuadState->SetAll(rootLayer->drawTransform(),
rootTargetRect,
rootTargetRect,
+ rootTargetRect,
+ false,
opacity);
AppendQuadsData appendQuadsData;
diff --git a/cc/layer_tree_host_impl_unittest.cc b/cc/layer_tree_host_impl_unittest.cc
index fbddb65..3e35986a 100644
--- a/cc/layer_tree_host_impl_unittest.cc
+++ b/cc/layer_tree_host_impl_unittest.cc
@@ -2218,16 +2218,30 @@
MockContext* mockContext = static_cast<MockContext*>(context->context3D());
MockContextHarness harness(mockContext);
- harness.mustDrawSolidQuad();
- harness.mustSetScissor(0, 0, 10, 10);
-
// Run test case
scoped_ptr<LayerTreeHostImpl> myHostImpl = createLayerTreeHost(false, context.Pass(), FakeLayerWithQuads::create(1));
- LayerTreeHostImpl::FrameData frame;
- EXPECT_TRUE(myHostImpl->prepareToDraw(frame));
- myHostImpl->drawLayers(frame);
- myHostImpl->didDrawAllLayers(frame);
+ // without partial swap, and no clipping, no scissor is set.
+ harness.mustDrawSolidQuad();
+ harness.mustSetNoScissor();
+ {
+ LayerTreeHostImpl::FrameData frame;
+ EXPECT_TRUE(myHostImpl->prepareToDraw(frame));
+ myHostImpl->drawLayers(frame);
+ myHostImpl->didDrawAllLayers(frame);
+ }
+ Mock::VerifyAndClearExpectations(&mockContext);
+
+ // without partial swap, but a layer does clip its subtree, one scissor is set.
+ myHostImpl->rootLayer()->setMasksToBounds(true);
+ harness.mustDrawSolidQuad();
+ harness.mustSetScissor(0, 0, 10, 10);
+ {
+ LayerTreeHostImpl::FrameData frame;
+ EXPECT_TRUE(myHostImpl->prepareToDraw(frame));
+ myHostImpl->drawLayers(frame);
+ myHostImpl->didDrawAllLayers(frame);
+ }
Mock::VerifyAndClearExpectations(&mockContext);
}
@@ -2719,7 +2733,7 @@
scoped_ptr<TestRenderPass> pass = TestRenderPass::Create();
pass->SetNew(RenderPass::Id(1, 1), gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1), WebTransformationMatrix());
scoped_ptr<SharedQuadState> sharedState = SharedQuadState::Create();
- sharedState->SetAll(WebTransformationMatrix(), gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1), 1);
+ sharedState->SetAll(WebTransformationMatrix(), gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1), false, 1);
scoped_ptr<TextureDrawQuad> quad = TextureDrawQuad::Create();
quad->SetNew(sharedState.get(), gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1), resourceId, false, gfx::RectF(0, 0, 1, 1), false);
@@ -4194,7 +4208,7 @@
// One shared state for all quads - we don't need the correct details
testData.sharedQuadState = SharedQuadState::Create();
- testData.sharedQuadState->SetAll(WebTransformationMatrix(), gfx::Rect(), gfx::Rect(), 1.0);
+ testData.sharedQuadState->SetAll(WebTransformationMatrix(), gfx::Rect(), gfx::Rect(), gfx::Rect(), false, 1.0);
const char* currentChar = testScript;
diff --git a/cc/render_pass_unittest.cc b/cc/render_pass_unittest.cc
index 7a0ec5f..f18f0f18 100644
--- a/cc/render_pass_unittest.cc
+++ b/cc/render_pass_unittest.cc
@@ -63,7 +63,7 @@
// Stick a quad in the pass, this should not get copied.
scoped_ptr<SharedQuadState> sharedState = SharedQuadState::Create();
- sharedState->SetAll(WebTransformationMatrix(), gfx::Rect(), gfx::Rect(), 1);
+ sharedState->SetAll(WebTransformationMatrix(), gfx::Rect(), gfx::Rect(), gfx::Rect(), false, 1);
pass->AppendSharedQuadState(sharedState.Pass());
scoped_ptr<CheckerboardDrawQuad> checkerboardQuad = CheckerboardDrawQuad::Create();
diff --git a/cc/render_surface.cc b/cc/render_surface.cc
index 0d0efd7..0f083a38 100644
--- a/cc/render_surface.cc
+++ b/cc/render_surface.cc
@@ -18,6 +18,7 @@
, m_drawOpacityIsAnimating(false)
, m_targetSurfaceTransformsAreAnimating(false)
, m_screenSpaceTransformsAreAnimating(false)
+ , m_isClipped(false)
, m_nearestAncestorThatMovesPixels(0)
{
}
diff --git a/cc/render_surface.h b/cc/render_surface.h
index cc84ea6..230f89d 100644
--- a/cc/render_surface.h
+++ b/cc/render_surface.h
@@ -54,6 +54,9 @@
bool screenSpaceTransformsAreAnimating() const { return m_screenSpaceTransformsAreAnimating; }
void setScreenSpaceTransformsAreAnimating(bool animating) { m_screenSpaceTransformsAreAnimating = animating; }
+ bool isClipped() const { return m_isClipped; }
+ void setIsClipped(bool isClipped) { m_isClipped = isClipped; }
+
const gfx::Rect& clipRect() const { return m_clipRect; }
void setClipRect(const gfx::Rect& clipRect) { m_clipRect = clipRect; }
@@ -84,6 +87,8 @@
bool m_targetSurfaceTransformsAreAnimating;
bool m_screenSpaceTransformsAreAnimating;
+ bool m_isClipped;
+
// Uses the space of the surface's target surface.
gfx::Rect m_clipRect;
diff --git a/cc/render_surface_impl.cc b/cc/render_surface_impl.cc
index 9747a474..271d2e7 100644
--- a/cc/render_surface_impl.cc
+++ b/cc/render_surface_impl.cc
@@ -34,6 +34,7 @@
, m_drawOpacityIsAnimating(false)
, m_targetSurfaceTransformsAreAnimating(false)
, m_screenSpaceTransformsAreAnimating(false)
+ , m_isClipped(false)
, m_nearestAncestorThatMovesPixels(0)
, m_targetRenderSurfaceLayerIndexHistory(0)
, m_currentLayerIndexHistory(0)
@@ -200,7 +201,7 @@
gfx::Rect clippedRectInTarget = computeClippedRectInTarget(m_owningLayer);
const WebTransformationMatrix& drawTransform = forReplica ? m_replicaDrawTransform : m_drawTransform;
SharedQuadState* sharedQuadState = quadSink.useSharedQuadState(SharedQuadState::Create());
- sharedQuadState->SetAll(drawTransform, m_contentRect, clippedRectInTarget, m_drawOpacity);
+ sharedQuadState->SetAll(drawTransform, m_contentRect, clippedRectInTarget, m_clipRect, m_isClipped, m_drawOpacity);
if (m_owningLayer->showDebugBorders()) {
SkColor color = forReplica ? DebugColors::SurfaceReplicaBorderColor() : DebugColors::SurfaceBorderColor();
diff --git a/cc/render_surface_impl.h b/cc/render_surface_impl.h
index ec851ff..340103a 100644
--- a/cc/render_surface_impl.h
+++ b/cc/render_surface_impl.h
@@ -63,6 +63,9 @@
bool screenSpaceTransformsAreAnimating() const { return m_screenSpaceTransformsAreAnimating; }
void setScreenSpaceTransformsAreAnimating(bool animating) { m_screenSpaceTransformsAreAnimating = animating; }
+ void setIsClipped(bool isClipped) { m_isClipped = isClipped; }
+ bool isClipped() const { return m_isClipped; }
+
void setClipRect(const gfx::Rect&);
const gfx::Rect& clipRect() const { return m_clipRect; }
@@ -104,6 +107,8 @@
bool m_targetSurfaceTransformsAreAnimating;
bool m_screenSpaceTransformsAreAnimating;
+ bool m_isClipped;
+
// Uses the space of the surface's target surface.
gfx::Rect m_clipRect;
diff --git a/cc/shared_quad_state.cc b/cc/shared_quad_state.cc
index af47fd7..24e9fdf9 100644
--- a/cc/shared_quad_state.cc
+++ b/cc/shared_quad_state.cc
@@ -8,6 +8,8 @@
SharedQuadState::SharedQuadState() : opacity(0) {}
+SharedQuadState::~SharedQuadState() {}
+
scoped_ptr<SharedQuadState> SharedQuadState::Create() {
return make_scoped_ptr(new SharedQuadState);
}
@@ -20,10 +22,14 @@
const WebKit::WebTransformationMatrix& content_to_target_transform,
const gfx::Rect& visible_content_rect,
const gfx::Rect& clipped_rect_in_target,
+ const gfx::Rect& clip_rect,
+ bool is_clipped,
float opacity) {
this->content_to_target_transform = content_to_target_transform;
this->visible_content_rect = visible_content_rect;
this->clipped_rect_in_target = clipped_rect_in_target;
+ this->clip_rect = clip_rect;
+ this->is_clipped = is_clipped;
this->opacity = opacity;
}
diff --git a/cc/shared_quad_state.h b/cc/shared_quad_state.h
index 350e80bdd..300270b 100644
--- a/cc/shared_quad_state.h
+++ b/cc/shared_quad_state.h
@@ -15,12 +15,15 @@
class CC_EXPORT SharedQuadState {
public:
static scoped_ptr<SharedQuadState> Create();
+ ~SharedQuadState();
scoped_ptr<SharedQuadState> Copy() const;
void SetAll(const WebKit::WebTransformationMatrix& content_to_target_transform,
const gfx::Rect& visible_content_rect,
const gfx::Rect& clipped_rect_in_target,
+ const gfx::Rect& clip_rect,
+ bool is_clipped,
float opacity);
// Transforms from quad's original content space to its target content space.
@@ -28,6 +31,8 @@
// This rect lives in the content space for the quad's originating layer.
gfx::Rect visible_content_rect;
gfx::Rect clipped_rect_in_target;
+ gfx::Rect clip_rect;
+ bool is_clipped;
float opacity;
private:
diff --git a/cc/software_renderer.cc b/cc/software_renderer.cc
index 6ec60b4..58c5425 100644
--- a/cc/software_renderer.cc
+++ b/cc/software_renderer.cc
@@ -110,6 +110,23 @@
return false;
}
+void SoftwareRenderer::ensureScissorTestEnabled()
+{
+ // Nothing to do here. Current implementation of software rendering has no
+ // notion of enabling/disabling the feature.
+}
+
+void SoftwareRenderer::ensureScissorTestDisabled()
+{
+ // There is no explicit notion of enabling/disabling scissoring in software
+ // rendering, but the underlying effect we want is to clear any existing
+ // clipRect on the current SkCanvas. This is done by setting clipRect to
+ // the viewport's dimensions.
+ SkISize canvasSize = m_skCurrentCanvas->getDeviceSize();
+ SkRect canvasRect = SkRect::MakeXYWH(0, 0, canvasSize.width(), canvasSize.height());
+ m_skCurrentCanvas->clipRect(canvasRect, SkRegion::kReplace_Op);
+}
+
void SoftwareRenderer::finish()
{
}
diff --git a/cc/software_renderer.h b/cc/software_renderer.h
index 02ed749..caac4f48 100644
--- a/cc/software_renderer.h
+++ b/cc/software_renderer.h
@@ -52,6 +52,8 @@
virtual void beginDrawingFrame(DrawingFrame&) OVERRIDE;
virtual void finishDrawingFrame(DrawingFrame&) OVERRIDE;
virtual bool flippedFramebuffer() const OVERRIDE;
+ virtual void ensureScissorTestEnabled() OVERRIDE;
+ virtual void ensureScissorTestDisabled() OVERRIDE;
private:
SoftwareRenderer(RendererClient*, ResourceProvider*, WebKit::WebCompositorSoftwareOutputDevice*);
diff --git a/cc/software_renderer_unittest.cc b/cc/software_renderer_unittest.cc
index 743649e8..121ba769 100644
--- a/cc/software_renderer_unittest.cc
+++ b/cc/software_renderer_unittest.cc
@@ -67,7 +67,7 @@
initializeRenderer();
scoped_ptr<SharedQuadState> sharedQuadState = SharedQuadState::Create();
- sharedQuadState->SetAll(WebTransformationMatrix(), outerRect, outerRect, 1.0);
+ sharedQuadState->SetAll(WebTransformationMatrix(), outerRect, outerRect, outerRect, false, 1.0);
RenderPass::Id rootRenderPassId = RenderPass::Id(1, 1);
scoped_ptr<TestRenderPass> rootRenderPass = TestRenderPass::Create();
rootRenderPass->SetNew(rootRenderPassId, outerRect, gfx::Rect(), WebTransformationMatrix());
@@ -128,7 +128,7 @@
gfx::Rect rect = gfx::Rect(gfx::Point(), deviceViewportSize());
scoped_ptr<SharedQuadState> sharedQuadState = SharedQuadState::Create();
- sharedQuadState->SetAll(WebTransformationMatrix(), outerRect, outerRect, 1.0);
+ sharedQuadState->SetAll(WebTransformationMatrix(), outerRect, outerRect, outerRect, false, 1.0);
RenderPass::Id rootRenderPassId = RenderPass::Id(1, 1);
scoped_ptr<TestRenderPass> rootRenderPass = TestRenderPass::Create();
rootRenderPass->SetNew(rootRenderPassId, gfx::Rect(gfx::Point(), deviceViewportSize()), gfx::Rect(), WebTransformationMatrix());
diff --git a/cc/test/render_pass_test_common.cc b/cc/test/render_pass_test_common.cc
index bbb8a34..e3bb308e 100644
--- a/cc/test/render_pass_test_common.cc
+++ b/cc/test/render_pass_test_common.cc
@@ -31,6 +31,8 @@
shared_state->SetAll(WebTransformationMatrix(),
rect,
rect,
+ rect,
+ false,
1);
scoped_ptr<cc::CheckerboardDrawQuad> checkerboard_quad =