[chromium] Store origin/screen space transforms for surface and replica in the surface
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 21 Mar 2012 05:02:57 +0000 (05:02 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 21 Mar 2012 05:02:57 +0000 (05:02 +0000)
https://bugs.webkit.org/show_bug.cgi?id=81296

Patch by Dana Jansens <danakj@chromium.org> on 2012-03-20
Reviewed by Adrienne Walker.

Source/WebCore:

Compute and save a number of transforms for render surfaces. The origin
tranform for the replica into its target surface. The origin transform
for the replica into the screen. And the origin transform for the surface
into the screen.

These transforms will allow us to detect occlusion of render surfaces,
allowing us to cull (or partially-cull) the render surface quad during
drawing.

This also fixes a subtle bug in the transforms used by the damage
tracking code.

Unit test: CCLayerTreeHostCommonTest.verifyTransformsForSingleRenderSurface
           CCLayerTreeHostCommonTest.verifyTransformsForReplica
           CCLayerTreeHostCommonTest.verifyTransformsForRenderSurfaceHierarchy

* platform/graphics/chromium/RenderSurfaceChromium.h:
(WebCore::RenderSurfaceChromium::screenSpaceTransform):
(WebCore::RenderSurfaceChromium::setScreenSpaceTransform):
(RenderSurfaceChromium):
(WebCore::RenderSurfaceChromium::replicaOriginTransform):
(WebCore::RenderSurfaceChromium::setReplicaOriginTransform):
(WebCore::RenderSurfaceChromium::replicaScreenSpaceTransform):
(WebCore::RenderSurfaceChromium::setReplicaScreenSpaceTransform):
* platform/graphics/chromium/cc/CCDamageTracker.cpp:
(WebCore::CCDamageTracker::extendDamageForRenderSurface):
* platform/graphics/chromium/cc/CCLayerTreeHostCommon.cpp:
(WebCore::calculateDrawTransformsAndVisibilityInternal):
* platform/graphics/chromium/cc/CCLayerTreeHostImpl.cpp:
(WebCore::damageInSurfaceSpace):
* platform/graphics/chromium/cc/CCRenderSurface.h:
(WebCore::CCRenderSurface::setDrawTransform):
(WebCore::CCRenderSurface::drawTransform):
(WebCore::CCRenderSurface::setScreenSpaceTransform):
(WebCore::CCRenderSurface::screenSpaceTransform):
(CCRenderSurface):
(WebCore::CCRenderSurface::setReplicaDrawTransform):
(WebCore::CCRenderSurface::replicaDrawTransform):
(WebCore::CCRenderSurface::setReplicaOriginTransform):
(WebCore::CCRenderSurface::replicaOriginTransform):
(WebCore::CCRenderSurface::setReplicaScreenSpaceTransform):
(WebCore::CCRenderSurface::replicaScreenSpaceTransform):

Source/WebKit/chromium:

* tests/CCLayerTreeHostCommonTest.cpp:
(WebCore::TEST):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@111499 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/WebCore/ChangeLog
Source/WebCore/platform/graphics/chromium/RenderSurfaceChromium.h
Source/WebCore/platform/graphics/chromium/cc/CCDamageTracker.cpp
Source/WebCore/platform/graphics/chromium/cc/CCLayerTreeHostCommon.cpp
Source/WebCore/platform/graphics/chromium/cc/CCLayerTreeHostImpl.cpp
Source/WebCore/platform/graphics/chromium/cc/CCRenderSurface.h
Source/WebKit/chromium/ChangeLog
Source/WebKit/chromium/tests/CCLayerTreeHostCommonTest.cpp

index 1dbdb197d0519d697a29ac54628bcf7557852245..254796f9b4c6806ea6b0e9ae24da49440fc17b12 100644 (file)
@@ -1,3 +1,53 @@
+2012-03-20  Dana Jansens  <danakj@chromium.org>
+
+        [chromium] Store origin/screen space transforms for surface and replica in the surface
+        https://bugs.webkit.org/show_bug.cgi?id=81296
+
+        Reviewed by Adrienne Walker.
+
+        Compute and save a number of transforms for render surfaces. The origin
+        tranform for the replica into its target surface. The origin transform
+        for the replica into the screen. And the origin transform for the surface
+        into the screen.
+
+        These transforms will allow us to detect occlusion of render surfaces,
+        allowing us to cull (or partially-cull) the render surface quad during
+        drawing.
+
+        This also fixes a subtle bug in the transforms used by the damage
+        tracking code.
+
+        Unit test: CCLayerTreeHostCommonTest.verifyTransformsForSingleRenderSurface
+                   CCLayerTreeHostCommonTest.verifyTransformsForReplica
+                   CCLayerTreeHostCommonTest.verifyTransformsForRenderSurfaceHierarchy
+
+        * platform/graphics/chromium/RenderSurfaceChromium.h:
+        (WebCore::RenderSurfaceChromium::screenSpaceTransform):
+        (WebCore::RenderSurfaceChromium::setScreenSpaceTransform):
+        (RenderSurfaceChromium):
+        (WebCore::RenderSurfaceChromium::replicaOriginTransform):
+        (WebCore::RenderSurfaceChromium::setReplicaOriginTransform):
+        (WebCore::RenderSurfaceChromium::replicaScreenSpaceTransform):
+        (WebCore::RenderSurfaceChromium::setReplicaScreenSpaceTransform):
+        * platform/graphics/chromium/cc/CCDamageTracker.cpp:
+        (WebCore::CCDamageTracker::extendDamageForRenderSurface):
+        * platform/graphics/chromium/cc/CCLayerTreeHostCommon.cpp:
+        (WebCore::calculateDrawTransformsAndVisibilityInternal):
+        * platform/graphics/chromium/cc/CCLayerTreeHostImpl.cpp:
+        (WebCore::damageInSurfaceSpace):
+        * platform/graphics/chromium/cc/CCRenderSurface.h:
+        (WebCore::CCRenderSurface::setDrawTransform):
+        (WebCore::CCRenderSurface::drawTransform):
+        (WebCore::CCRenderSurface::setScreenSpaceTransform):
+        (WebCore::CCRenderSurface::screenSpaceTransform):
+        (CCRenderSurface):
+        (WebCore::CCRenderSurface::setReplicaDrawTransform):
+        (WebCore::CCRenderSurface::replicaDrawTransform):
+        (WebCore::CCRenderSurface::setReplicaOriginTransform):
+        (WebCore::CCRenderSurface::replicaOriginTransform):
+        (WebCore::CCRenderSurface::setReplicaScreenSpaceTransform):
+        (WebCore::CCRenderSurface::replicaScreenSpaceTransform):
+
 2012-03-20  Yoshifumi Inoue  <yosin@chromium.org>
 
         [Forms] The option element should not be form associated element.
index 03cbbd76523ba1d3661f19cfde7b372f6431d527..59e5d961dc81d70b6689836232cf9a9cd43687c9 100644 (file)
@@ -74,9 +74,18 @@ public:
     const TransformationMatrix& originTransform() const { return m_originTransform; }
     void setOriginTransform(const TransformationMatrix& originTransform) { m_originTransform = originTransform; }
 
+    const TransformationMatrix& screenSpaceTransform() const { return m_screenSpaceTransform; }
+    void setScreenSpaceTransform(const TransformationMatrix& screenSpaceTransform) { m_screenSpaceTransform = screenSpaceTransform; }
+
     const TransformationMatrix& replicaDrawTransform() const { return m_replicaDrawTransform; }
     void setReplicaDrawTransform(const TransformationMatrix& replicaDrawTransform) { m_replicaDrawTransform = replicaDrawTransform; }
 
+    const TransformationMatrix& replicaOriginTransform() const { return m_replicaOriginTransform; }
+    void setReplicaOriginTransform(const TransformationMatrix& replicaOriginTransform) { m_replicaOriginTransform = replicaOriginTransform; }
+
+    const TransformationMatrix& replicaScreenSpaceTransform() const { return m_replicaScreenSpaceTransform; }
+    void setReplicaScreenSpaceTransform(const TransformationMatrix& replicaScreenSpaceTransform) { m_replicaScreenSpaceTransform = replicaScreenSpaceTransform; }
+
     bool targetSurfaceTransformsAreAnimating() const { return m_targetSurfaceTransformsAreAnimating; }
     void setTargetSurfaceTransformsAreAnimating(bool animating) { m_targetSurfaceTransformsAreAnimating = animating; }
     bool screenSpaceTransformsAreAnimating() const { return m_screenSpaceTransformsAreAnimating; }
@@ -109,8 +118,11 @@ private:
     float m_drawOpacity;
     bool m_drawOpacityIsAnimating;
     TransformationMatrix m_drawTransform;
-    TransformationMatrix m_replicaDrawTransform;
     TransformationMatrix m_originTransform;
+    TransformationMatrix m_screenSpaceTransform;
+    TransformationMatrix m_replicaDrawTransform;
+    TransformationMatrix m_replicaOriginTransform;
+    TransformationMatrix m_replicaScreenSpaceTransform;
     bool m_targetSurfaceTransformsAreAnimating;
     bool m_screenSpaceTransformsAreAnimating;
     FilterOperations m_filters;
index 6a062caca7cc5875f2c92599856b6a5ec8e53c79..96c5a5e1825cfff49bf93cc861772eaf8f7e730c 100644 (file)
@@ -286,12 +286,7 @@ void CCDamageTracker::extendDamageForRenderSurface(CCLayerImpl* layer, FloatRect
         targetDamageRect.uniteIfNonZero(damageRectInTargetSpace);
 
         if (layer->replicaLayer()) {
-            // Compute the replica's "originTransform" that maps from the replica's origin space to the target surface origin space.
-            TransformationMatrix replicaOriginTransform = layer->renderSurface()->originTransform();
-            replicaOriginTransform.translate(layer->replicaLayer()->position().x(), layer->replicaLayer()->position().y());
-            replicaOriginTransform.multiply(layer->replicaLayer()->transform());
-            replicaOriginTransform.translate(-layer->replicaLayer()->position().x(), -layer->replicaLayer()->position().y());
-
+            const TransformationMatrix& replicaOriginTransform = renderSurface->replicaOriginTransform();
             targetDamageRect.uniteIfNonZero(replicaOriginTransform.mapRect(damageRectInLocalSpace));
         }
     }
index 9124b076590131ff8c6af4814f216f17d12476c8..bd22154b918a7de628c3c98461e0672d481fbf29 100644 (file)
@@ -264,6 +264,25 @@ static bool calculateDrawTransformsAndVisibilityInternal(LayerType* layer, Layer
     //        P is the projection matrix
     //        S is the scale adjustment (to scale up to the layer size)
     //
+    // When a render surface has a replica layer, that layer's transform is used to draw a second copy of the surface.
+    // Transforms named here are relative to the surface, unless they specify they are relative to the replica layer.
+    //
+    // The render surface origin transform to its target surface origin is:
+    //        M[surfaceOrigin] = M[owningLayer->Draw] * Tr[origin2center].inverse()
+    //
+    // The render surface origin transform to its the root (screen space) origin is:
+    //        M[surface2root] = M[owningLayer->screenspace]
+    //
+    // The replica draw transform is:
+    //        M[replicaDraw] = M[surfaceOrigin] * Tr[replica->position()] * Tr[replica] * Tr[anchor2center]
+    //                       = M[owningLayer->draw] * Tr[origin2center].inverse() * Tr[replica->position()] * Tr[replica] * Tr[anchor2clippedCenter]
+    //
+    // The replica origin transform to its target surface origin is:
+    //        M[replicaOrigin] = M[surfaceOrigin] * Tr[replica->position()] * Tr[replica] * Tr[origin2anchor].inverse()
+    //
+    // The replica origin transform to the root (screen space) origin is:
+    //        M[replica2root] = M[surface2root] * Tr[replica->position()] * Tr[replica] * Tr[origin2anchor].inverse()
+    //
 
     if (subtreeShouldBeSkipped(layer))
         return false;
@@ -333,9 +352,9 @@ static bool calculateDrawTransformsAndVisibilityInternal(LayerType* layer, Layer
         layer->setDrawOpacity(1);
         layer->setDrawOpacityIsAnimating(false);
 
-        TransformationMatrix layerOriginTransform = combinedTransform;
-        layerOriginTransform.translate3d(-0.5 * bounds.width(), -0.5 * bounds.height(), 0);
-        renderSurface->setOriginTransform(layerOriginTransform);
+        TransformationMatrix surfaceOriginTransform = combinedTransform;
+        surfaceOriginTransform.translate3d(-0.5 * bounds.width(), -0.5 * bounds.height(), 0);
+        renderSurface->setOriginTransform(surfaceOriginTransform);
 
         renderSurface->setTargetSurfaceTransformsAreAnimating(layerIsInAnimatingSubtreeForSurface);
         renderSurface->setScreenSpaceTransformsAreAnimating(layerIsInAnimatingSubtreeForScreen);
@@ -344,7 +363,7 @@ static bool calculateDrawTransformsAndVisibilityInternal(LayerType* layer, Layer
         layer->setScreenSpaceTransformIsAnimating(layerIsInAnimatingSubtreeForScreen);
 
         // Update the aggregate hierarchy matrix to include the transform of the newly created RenderSurface.
-        nextHierarchyMatrix.multiply(layerOriginTransform);
+        nextHierarchyMatrix.multiply(surfaceOriginTransform);
 
         // The render surface clipRect contributes to the scissor rect that needs to
         // be applied before drawing the render surface onto its containing
@@ -503,14 +522,29 @@ static bool calculateDrawTransformsAndVisibilityInternal(LayerType* layer, Layer
         drawTransform.translate3d(surfaceCenter.x() + centerOffsetDueToClipping.width(), surfaceCenter.y() + centerOffsetDueToClipping.height(), 0);
         renderSurface->setDrawTransform(drawTransform);
 
-        // Compute the transformation matrix used to draw the replica of the render
-        // surface.
+        // The layer's origin is equal to the surface's origin so the screenSpaceTransform is the same.
+        renderSurface->setScreenSpaceTransform(layer->screenSpaceTransform());
+
         if (layer->replicaLayer()) {
+            // Compute the transformation matrix used to draw the surface's replica to the target surface.
             TransformationMatrix replicaDrawTransform = renderSurface->originTransform();
-            replicaDrawTransform.translate3d(layer->replicaLayer()->position().x(), layer->replicaLayer()->position().y(), 0);
+            replicaDrawTransform.translate(layer->replicaLayer()->position().x(), layer->replicaLayer()->position().y());
             replicaDrawTransform.multiply(layer->replicaLayer()->transform());
-            replicaDrawTransform.translate3d(surfaceCenter.x() - anchorPoint.x() * bounds.width(), surfaceCenter.y() - anchorPoint.y() * bounds.height(), 0);
+            replicaDrawTransform.translate(surfaceCenter.x() - anchorPoint.x() * bounds.width(), surfaceCenter.y() - anchorPoint.y() * bounds.height());
             renderSurface->setReplicaDrawTransform(replicaDrawTransform);
+
+            TransformationMatrix surfaceOriginToReplicaOriginTransform;
+            surfaceOriginToReplicaOriginTransform.translate(layer->replicaLayer()->position().x(), layer->replicaLayer()->position().y());
+            surfaceOriginToReplicaOriginTransform.multiply(layer->replicaLayer()->transform());
+            surfaceOriginToReplicaOriginTransform.translate(-anchorPoint.x() * bounds.width(), -anchorPoint.y() * bounds.height());
+
+            // Compute the replica's "originTransform" that maps from the replica's origin space to the target surface origin space.
+            TransformationMatrix replicaOriginTransform = layer->renderSurface()->originTransform() * surfaceOriginToReplicaOriginTransform;
+            renderSurface->setReplicaOriginTransform(replicaOriginTransform);
+
+            // Compute the replica's "screenSpaceTransform" that maps from the replica's origin space to the screen's origin space.
+            TransformationMatrix replicaScreenSpaceTransform = layer->renderSurface()->screenSpaceTransform() * surfaceOriginToReplicaOriginTransform;
+            renderSurface->setReplicaScreenSpaceTransform(replicaScreenSpaceTransform);
         }
 
         // If a render surface has no layer list, then it and none of its children needed to get drawn.
index 07812e21c1c9ad166b1885d9d89e884e57a84059..ad79a41ceaede01a49b008e70447da12fd035e1e 100644 (file)
@@ -200,27 +200,12 @@ void CCLayerTreeHostImpl::trackDamageForAllSurfaces(CCLayerImpl* rootDrawLayer,
     }
 }
 
-static TransformationMatrix computeScreenSpaceTransformForSurface(CCLayerImpl* renderSurfaceLayer)
-{
-    // The layer's screen space transform can be written as:
-    //   layerScreenSpaceTransform = surfaceScreenSpaceTransform * layerOriginTransform
-    // So, to compute the surface screen space, we can do:
-    //   surfaceScreenSpaceTransform = layerScreenSpaceTransform * inverse(layerOriginTransform)
-
-    TransformationMatrix layerOriginTransform = renderSurfaceLayer->drawTransform();
-    layerOriginTransform.translate(-0.5 * renderSurfaceLayer->bounds().width(), -0.5 * renderSurfaceLayer->bounds().height());
-    TransformationMatrix surfaceScreenSpaceTransform = renderSurfaceLayer->screenSpaceTransform();
-    surfaceScreenSpaceTransform.multiply(layerOriginTransform.inverse());
-
-    return surfaceScreenSpaceTransform;
-}
-
 static FloatRect damageInSurfaceSpace(CCLayerImpl* renderSurfaceLayer, const FloatRect& rootDamageRect)
 {
     FloatRect surfaceDamageRect;
     // For now, we conservatively use the root damage as the damage for
     // all surfaces, except perspective transforms.
-    TransformationMatrix screenSpaceTransform = computeScreenSpaceTransformForSurface(renderSurfaceLayer);
+    const TransformationMatrix& screenSpaceTransform = renderSurfaceLayer->renderSurface()->screenSpaceTransform();
     if (screenSpaceTransform.hasPerspective()) {
         // Perspective projections do not play nice with mapRect of
         // inverse transforms. In this uncommon case, its simpler to
index 62f140a188462b5c72beb7b25ef053387c5363e4..f26bd4e327cd1ebb11eb59da2a357ab88c2ae36e 100644 (file)
@@ -70,24 +70,33 @@ public:
     float drawOpacity() const { return m_drawOpacity; }
     void setDrawOpacity(float opacity) { m_drawOpacity = opacity; }
 
+    void setFilters(const FilterOperations& filters) { m_filters = filters; }
+    const FilterOperations& filters() const { return m_filters; }
+    SkBitmap applyFilters(LayerRendererChromium*);
+
+    void setNearestAncestorThatMovesPixels(CCRenderSurface* surface) { m_nearestAncestorThatMovesPixels = surface; }
+    const CCRenderSurface* nearestAncestorThatMovesPixels() const { return m_nearestAncestorThatMovesPixels; }
+
     bool drawOpacityIsAnimating() const { return m_drawOpacityIsAnimating; }
     void setDrawOpacityIsAnimating(bool drawOpacityIsAnimating) { m_drawOpacityIsAnimating = drawOpacityIsAnimating; }
 
     void setDrawTransform(const TransformationMatrix& drawTransform) { m_drawTransform = drawTransform; }
     const TransformationMatrix& drawTransform() const { return m_drawTransform; }
 
-    void setFilters(const FilterOperations& filters) { m_filters = filters; }
-    const FilterOperations& filters() const { return m_filters; }
-    SkBitmap applyFilters(LayerRendererChromium*);
+    void setOriginTransform(const TransformationMatrix& originTransform) { m_originTransform = originTransform; }
+    const TransformationMatrix& originTransform() const { return m_originTransform; }
 
-    void setNearestAncestorThatMovesPixels(CCRenderSurface* surface) { m_nearestAncestorThatMovesPixels = surface; }
-    const CCRenderSurface* nearestAncestorThatMovesPixels() const { return m_nearestAncestorThatMovesPixels; }
+    void setScreenSpaceTransform(const TransformationMatrix& screenSpaceTransform) { m_screenSpaceTransform = screenSpaceTransform; }
+    const TransformationMatrix& screenSpaceTransform() const { return m_screenSpaceTransform; }
 
     void setReplicaDrawTransform(const TransformationMatrix& replicaDrawTransform) { m_replicaDrawTransform = replicaDrawTransform; }
     const TransformationMatrix& replicaDrawTransform() const { return m_replicaDrawTransform; }
 
-    void setOriginTransform(const TransformationMatrix& originTransform) { m_originTransform = originTransform; }
-    const TransformationMatrix& originTransform() const { return m_originTransform; }
+    void setReplicaOriginTransform(const TransformationMatrix& replicaOriginTransform) { m_replicaOriginTransform = replicaOriginTransform; }
+    const TransformationMatrix& replicaOriginTransform() const { return m_replicaOriginTransform; }
+
+    void setReplicaScreenSpaceTransform(const TransformationMatrix& replicaScreenSpaceTransform) { m_replicaScreenSpaceTransform = replicaScreenSpaceTransform; }
+    const TransformationMatrix& replicaScreenSpaceTransform() const { return m_replicaScreenSpaceTransform; }
 
     bool targetSurfaceTransformsAreAnimating() const { return m_targetSurfaceTransformsAreAnimating; }
     void setTargetSurfaceTransformsAreAnimating(bool animating) { m_targetSurfaceTransformsAreAnimating = animating; }
@@ -141,8 +150,11 @@ private:
     float m_drawOpacity;
     bool m_drawOpacityIsAnimating;
     TransformationMatrix m_drawTransform;
-    TransformationMatrix m_replicaDrawTransform;
     TransformationMatrix m_originTransform;
+    TransformationMatrix m_screenSpaceTransform;
+    TransformationMatrix m_replicaDrawTransform;
+    TransformationMatrix m_replicaOriginTransform;
+    TransformationMatrix m_replicaScreenSpaceTransform;
     bool m_targetSurfaceTransformsAreAnimating;
     bool m_screenSpaceTransformsAreAnimating;
     FilterOperations m_filters;
index 33803dc9c8e33badd3ebd49308e6968c8f926d4f..e7701847b4f48192b48d27e1755020b3652777e2 100644 (file)
@@ -1,3 +1,13 @@
+2012-03-20  Dana Jansens  <danakj@chromium.org>
+
+        [chromium] Store origin/screen space transforms for surface and replica in the surface
+        https://bugs.webkit.org/show_bug.cgi?id=81296
+
+        Reviewed by Adrienne Walker.
+
+        * tests/CCLayerTreeHostCommonTest.cpp:
+        (WebCore::TEST):
+
 2012-03-20  W. James MacLean  <wjmaclean@chromium.org>
 
         [chromium] Convert TouchFlingPlatformGestureCurve to a 2-D Bezier for better control of curve shape.
index 7032be269edf2cd58feafad585d7a633154fc099..9efddf9fb1a78252a08080e37d45f7aabf8e3e37 100644 (file)
@@ -310,6 +310,56 @@ TEST(CCLayerTreeHostCommonTest, verifyTransformsForSingleRenderSurface)
     EXPECT_TRANSFORMATION_MATRIX_EQ(parentCompositeTransform, child->targetRenderSurface()->originTransform());
     EXPECT_TRANSFORMATION_MATRIX_EQ(parentCompositeTransform, child->targetRenderSurface()->drawTransform());
 
+    // The screen space is the same as the target since the child surface draws into the root.
+    EXPECT_TRANSFORMATION_MATRIX_EQ(parentCompositeTransform, child->targetRenderSurface()->screenSpaceTransform());
+}
+
+TEST(CCLayerTreeHostCommonTest, verifyTransformsForReplica)
+{
+    RefPtr<LayerChromium> parent = LayerChromium::create();
+    RefPtr<LayerChromium> child = LayerChromium::create();
+    RefPtr<LayerChromium> childReplica = LayerChromium::create();
+    RefPtr<LayerChromiumWithForcedDrawsContent> grandChild = adoptRef(new LayerChromiumWithForcedDrawsContent());
+    parent->createRenderSurface();
+    parent->addChild(child);
+    child->addChild(grandChild);
+    child->setReplicaLayer(childReplica.get());
+
+    // Child is set up so that a new render surface should be created.
+    child->setOpacity(0.5f);
+
+    TransformationMatrix identityMatrix;
+    TransformationMatrix parentLayerTransform;
+    parentLayerTransform.scale3d(2.0, 2.0, 1.0);
+    TransformationMatrix parentTranslationToAnchor;
+    parentTranslationToAnchor.translate(2.5, 3.0);
+    TransformationMatrix parentSublayerMatrix;
+    parentSublayerMatrix.scale3d(10.0, 10.0, 3.3);
+    TransformationMatrix parentTranslationToCenter;
+    parentTranslationToCenter.translate(5.0, 6.0);
+    TransformationMatrix parentCompositeTransform = parentTranslationToAnchor * parentLayerTransform * parentTranslationToAnchor.inverse()
+            * parentTranslationToCenter * parentSublayerMatrix * parentTranslationToCenter.inverse();
+    TransformationMatrix childTranslationToCenter;
+    childTranslationToCenter.translate(8.0, 9.0);
+    TransformationMatrix replicaLayerTransform;
+    replicaLayerTransform.scale3d(3.0, 3.0, 1.0);
+    TransformationMatrix replicaCompositeTransform = parentCompositeTransform * replicaLayerTransform;
+
+    // Child's render surface should not exist yet.
+    ASSERT_FALSE(child->renderSurface());
+
+    setLayerPropertiesForTesting(parent.get(), parentLayerTransform, parentSublayerMatrix, FloatPoint(0.25f, 0.25f), FloatPoint(2.5f, 3.0f), IntSize(10, 12), false);
+    setLayerPropertiesForTesting(child.get(), identityMatrix, identityMatrix, FloatPoint(0.0f, 0.0f), FloatPoint(0.0f, 0.0f), IntSize(16, 18), false);
+    setLayerPropertiesForTesting(grandChild.get(), identityMatrix, identityMatrix, FloatPoint(0.0f, 0.0f), FloatPoint(-0.5f, -0.5f), IntSize(1, 1), false);
+    setLayerPropertiesForTesting(childReplica.get(), replicaLayerTransform, identityMatrix, FloatPoint(0.0f, 0.0f), FloatPoint(0.0f, 0.0f), IntSize(0, 0), false);
+    executeCalculateDrawTransformsAndVisibility(parent.get());
+
+    // Render surface should have been created now.
+    ASSERT_TRUE(child->renderSurface());
+    ASSERT_EQ(child->renderSurface(), child->targetRenderSurface());
+
+    EXPECT_TRANSFORMATION_MATRIX_EQ(replicaCompositeTransform, child->targetRenderSurface()->replicaOriginTransform());
+    EXPECT_TRANSFORMATION_MATRIX_EQ(replicaCompositeTransform, child->targetRenderSurface()->replicaScreenSpaceTransform());
 }
 
 TEST(CCLayerTreeHostCommonTest, verifyTransformsForRenderSurfaceHierarchy)
@@ -317,6 +367,7 @@ TEST(CCLayerTreeHostCommonTest, verifyTransformsForRenderSurfaceHierarchy)
     // This test creates a more complex tree and verifies it all at once. This covers the following cases:
     //   - layers that are described w.r.t. a render surface: should have draw transforms described w.r.t. that surface
     //   - A render surface described w.r.t. an ancestor render surface: should have a draw transform described w.r.t. that ancestor surface
+    //   - Replicas of a render surface are described w.r.t. the replica's transform around its anchor, along with the surface itself.
     //   - Sanity check on recursion: verify transforms of layers described w.r.t. a render surface that is described w.r.t. an ancestor render surface.
     //   - verifying that each layer has a reference to the correct renderSurface and targetRenderSurface values.
 
@@ -326,6 +377,8 @@ TEST(CCLayerTreeHostCommonTest, verifyTransformsForRenderSurfaceHierarchy)
     RefPtr<LayerChromium> childOfRoot = LayerChromium::create();
     RefPtr<LayerChromium> childOfRS1 = LayerChromium::create();
     RefPtr<LayerChromium> childOfRS2 = LayerChromium::create();
+    RefPtr<LayerChromium> replicaOfRS1 = LayerChromium::create();
+    RefPtr<LayerChromium> replicaOfRS2 = LayerChromium::create();
     RefPtr<LayerChromium> grandChildOfRoot = LayerChromium::create();
     RefPtr<LayerChromiumWithForcedDrawsContent> grandChildOfRS1 = adoptRef(new LayerChromiumWithForcedDrawsContent());
     RefPtr<LayerChromiumWithForcedDrawsContent> grandChildOfRS2 = adoptRef(new LayerChromiumWithForcedDrawsContent());
@@ -338,6 +391,8 @@ TEST(CCLayerTreeHostCommonTest, verifyTransformsForRenderSurfaceHierarchy)
     childOfRoot->addChild(grandChildOfRoot);
     childOfRS1->addChild(grandChildOfRS1);
     childOfRS2->addChild(grandChildOfRS2);
+    renderSurface1->setReplicaLayer(replicaOfRS1.get());
+    renderSurface2->setReplicaLayer(replicaOfRS2.get());
 
     // In combination with descendantDrawsContent, opacity != 1 forces the layer to have a new renderSurface.
     renderSurface1->setOpacity(0.5f);
@@ -346,6 +401,7 @@ TEST(CCLayerTreeHostCommonTest, verifyTransformsForRenderSurfaceHierarchy)
     // All layers in the tree are initialized with an anchor at 2.5 and a size of (10,10).
     // matrix "A" is the composite layer transform used in all layers, centered about the anchor point
     // matrix "B" is the sublayer transform used in all layers, centered about the center position of the layer.
+    // matrix "R" is the composite replica transform used in all replica layers.
     //
     // x component tests that layerTransform and sublayerTransform are done in the right order (translation and scale are noncommutative).
     // y component has a translation by 1.0 for every ancestor, which indicates the "depth" of the layer in the hierarchy.
@@ -357,9 +413,12 @@ TEST(CCLayerTreeHostCommonTest, verifyTransformsForRenderSurfaceHierarchy)
     layerTransform.translate(1.0, 1.0);
     TransformationMatrix sublayerTransform;
     sublayerTransform.scale3d(10.0, 1.0, 1.0);
+    TransformationMatrix replicaLayerTransform;
+    replicaLayerTransform.scale3d(-2.0, 5.0, 1.0);
 
     TransformationMatrix A = translationToAnchor * layerTransform * translationToAnchor.inverse();
     TransformationMatrix B = translationToCenter * sublayerTransform * translationToCenter.inverse();
+    TransformationMatrix R = A * translationToAnchor * replicaLayerTransform * translationToAnchor.inverse();
 
     setLayerPropertiesForTesting(parent.get(), layerTransform, sublayerTransform, FloatPoint(0.25f, 0.0f), FloatPoint(2.5f, 0.0f), IntSize(10, 10), false);
     setLayerPropertiesForTesting(renderSurface1.get(), layerTransform, sublayerTransform, FloatPoint(0.25f, 0.0f), FloatPoint(2.5f, 0.0f), IntSize(10, 10), false);
@@ -370,6 +429,8 @@ TEST(CCLayerTreeHostCommonTest, verifyTransformsForRenderSurfaceHierarchy)
     setLayerPropertiesForTesting(grandChildOfRoot.get(), layerTransform, sublayerTransform, FloatPoint(0.25f, 0.0f), FloatPoint(2.5f, 0.0f), IntSize(10, 10), false);
     setLayerPropertiesForTesting(grandChildOfRS1.get(), layerTransform, sublayerTransform, FloatPoint(0.25f, 0.0f), FloatPoint(2.5f, 0.0f), IntSize(10, 10), false);
     setLayerPropertiesForTesting(grandChildOfRS2.get(), layerTransform, sublayerTransform, FloatPoint(0.25f, 0.0f), FloatPoint(2.5f, 0.0f), IntSize(10, 10), false);
+    setLayerPropertiesForTesting(replicaOfRS1.get(), replicaLayerTransform, sublayerTransform, FloatPoint(), FloatPoint(2.5f, 0.0f), IntSize(), false);
+    setLayerPropertiesForTesting(replicaOfRS2.get(), replicaLayerTransform, sublayerTransform, FloatPoint(), FloatPoint(2.5f, 0.0f), IntSize(), false);
 
     executeCalculateDrawTransformsAndVisibility(parent.get());
 
@@ -435,8 +496,14 @@ TEST(CCLayerTreeHostCommonTest, verifyTransformsForRenderSurfaceHierarchy)
     //
     // Origin transform of render surface 1 is described with respect to root.
     EXPECT_TRANSFORMATION_MATRIX_EQ(A * B * A, renderSurface1->renderSurface()->originTransform());
+    EXPECT_TRANSFORMATION_MATRIX_EQ(A * B * R, renderSurface1->renderSurface()->replicaOriginTransform());
+    EXPECT_TRANSFORMATION_MATRIX_EQ(A * B * A, renderSurface1->renderSurface()->screenSpaceTransform());
+    EXPECT_TRANSFORMATION_MATRIX_EQ(A * B * R, renderSurface1->renderSurface()->replicaScreenSpaceTransform());
     // Origin transform of render surface 2 is described with respect to render surface 2.
     EXPECT_TRANSFORMATION_MATRIX_EQ(B * A, renderSurface2->renderSurface()->originTransform());
+    EXPECT_TRANSFORMATION_MATRIX_EQ(B * R, renderSurface2->renderSurface()->replicaOriginTransform());
+    EXPECT_TRANSFORMATION_MATRIX_EQ(A * B * A * B * A, renderSurface2->renderSurface()->screenSpaceTransform());
+    EXPECT_TRANSFORMATION_MATRIX_EQ(A * B * A * B * R, renderSurface2->renderSurface()->replicaScreenSpaceTransform());
 
     // Sanity check. If these fail there is probably a bug in the test itself.
     // It is expected that we correctly set up transforms so that the y-component of the screen-space transform