[chromium] Avoid pushing dirty tiles to the impl layer
authorjamesr@google.com <jamesr@google.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 21 Nov 2011 22:22:34 +0000 (22:22 +0000)
committerjamesr@google.com <jamesr@google.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 21 Nov 2011 22:22:34 +0000 (22:22 +0000)
https://bugs.webkit.org/show_bug.cgi?id=72765

Reviewed by Kenneth Russell.

Source/WebCore:

If a tile has invalidations at pushPropertiesTo, then we know the contents of that tile are no longer valid even
if they still have valid backing textures. This avoids pushing that texture to the impl side so it is not
displayed to the user. The texture is still kept around (managed by the TextureManager) so that when we later do
decide to update the contents for that tile we can use partial results if they are still valid.

Covered by new unit test in TiledLayerChromiumTest.cpp

* platform/graphics/chromium/TiledLayerChromium.cpp:
(WebCore::UpdatableTile::isDirty):
(WebCore::TiledLayerChromium::updateTileSizeAndTilingOption):
(WebCore::TiledLayerChromium::setTileSize):
(WebCore::TiledLayerChromium::setLayerTreeHost):
(WebCore::TiledLayerChromium::createTiler):
(WebCore::TiledLayerChromium::updateCompositorResources):
(WebCore::TiledLayerChromium::pushPropertiesTo):
* platform/graphics/chromium/TiledLayerChromium.h:
(WebCore::TiledLayerChromium::setTextureFormat):
* platform/graphics/chromium/cc/CCTiledLayerImpl.cpp:
(WebCore::CCTiledLayerImpl::hasTileAt):
(WebCore::CCTiledLayerImpl::drawTiles):
* platform/graphics/chromium/cc/CCTiledLayerImpl.h:

Source/WebKit/chromium:

Add unit test for TiledLayerChromium's tile pushing behavior. Requires a fair amount of fake classes. If these
fakes are useful in other tests, they should be moved to a shared location.

* WebKit.gypi:
* tests/TiledLayerChromiumTest.cpp: Added.
(::FakeTextureAllocator::createTexture):
(::FakeTextureAllocator::deleteTexture):
(::FakeLayerTextureUpdater::FakeLayerTextureUpdater):
(::FakeLayerTextureUpdater::~FakeLayerTextureUpdater):
(::FakeLayerTextureUpdater::orientation):
(::FakeLayerTextureUpdater::sampledTexelFormat):
(::FakeLayerTextureUpdater::prepareToUpdate):
(::FakeLayerTextureUpdater::updateTextureRect):
(::FakeCCTiledLayerImpl::FakeCCTiledLayerImpl):
(::FakeCCTiledLayerImpl::~FakeCCTiledLayerImpl):
(::FakeCCTiledLayerImpl::hasTileAt):
(::FakeTiledLayerChromium::FakeTiledLayerChromium):
(::FakeTiledLayerChromium::~FakeTiledLayerChromium):
(::FakeTiledLayerChromium::invalidateRect):
(::FakeTiledLayerChromium::prepareToUpdate):
(::FakeTiledLayerChromium::textureManager):
(::FakeTiledLayerChromium::createTextureUpdater):
(::FakeTiledLayerChromium::textureUpdater):
(::TEST):

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

Source/WebCore/ChangeLog
Source/WebCore/platform/graphics/chromium/TiledLayerChromium.cpp
Source/WebCore/platform/graphics/chromium/TiledLayerChromium.h
Source/WebCore/platform/graphics/chromium/cc/CCTiledLayerImpl.cpp
Source/WebCore/platform/graphics/chromium/cc/CCTiledLayerImpl.h
Source/WebKit/chromium/ChangeLog
Source/WebKit/chromium/WebKit.gypi
Source/WebKit/chromium/tests/TiledLayerChromiumTest.cpp [new file with mode: 0644]

index 7ee778c..d99398c 100644 (file)
@@ -1,3 +1,32 @@
+2011-11-21  James Robinson  <jamesr@chromium.org>
+
+        [chromium] Avoid pushing dirty tiles to the impl layer
+        https://bugs.webkit.org/show_bug.cgi?id=72765
+
+        Reviewed by Kenneth Russell.
+
+        If a tile has invalidations at pushPropertiesTo, then we know the contents of that tile are no longer valid even
+        if they still have valid backing textures. This avoids pushing that texture to the impl side so it is not
+        displayed to the user. The texture is still kept around (managed by the TextureManager) so that when we later do
+        decide to update the contents for that tile we can use partial results if they are still valid.
+
+        Covered by new unit test in TiledLayerChromiumTest.cpp
+
+        * platform/graphics/chromium/TiledLayerChromium.cpp:
+        (WebCore::UpdatableTile::isDirty):
+        (WebCore::TiledLayerChromium::updateTileSizeAndTilingOption):
+        (WebCore::TiledLayerChromium::setTileSize):
+        (WebCore::TiledLayerChromium::setLayerTreeHost):
+        (WebCore::TiledLayerChromium::createTiler):
+        (WebCore::TiledLayerChromium::updateCompositorResources):
+        (WebCore::TiledLayerChromium::pushPropertiesTo):
+        * platform/graphics/chromium/TiledLayerChromium.h:
+        (WebCore::TiledLayerChromium::setTextureFormat):
+        * platform/graphics/chromium/cc/CCTiledLayerImpl.cpp:
+        (WebCore::CCTiledLayerImpl::hasTileAt):
+        (WebCore::CCTiledLayerImpl::drawTiles):
+        * platform/graphics/chromium/cc/CCTiledLayerImpl.h:
+
 2011-11-21  Simon Hausmann  <simon.hausmann@nokia.com>
 
         [Qt] Speed up debug builds.
index 87dec0b..ffad2f5 100644 (file)
@@ -56,7 +56,7 @@ public:
 
     ManagedTexture* texture() { return m_tex.get(); }
 
-    bool dirty() const { return !m_dirtyLayerRect.isEmpty(); }
+    bool isDirty() const { return !m_dirtyLayerRect.isEmpty(); }
     void clearDirty() { m_dirtyLayerRect = IntRect(); }
 
     // Layer-space dirty rectangle that needs to be repainted.
@@ -67,11 +67,11 @@ private:
 
 TiledLayerChromium::TiledLayerChromium(CCLayerDelegate* delegate)
     : LayerChromium(delegate)
-    , m_tilingOption(AutoTile)
     , m_textureFormat(GraphicsContext3D::INVALID_ENUM)
     , m_skipsDraw(false)
     , m_textureOrientation(LayerTextureUpdater::InvalidOrientation)
     , m_sampledTexelFormat(LayerTextureUpdater::SampledTexelFormatInvalid)
+    , m_tilingOption(AutoTile)
 {
 }
 
@@ -95,9 +95,6 @@ void TiledLayerChromium::cleanupResources()
 
 void TiledLayerChromium::updateTileSizeAndTilingOption()
 {
-    if (!m_tiler)
-        return;
-
     const IntSize tileSize(min(defaultTileSize, contentBounds().width()), min(defaultTileSize, contentBounds().height()));
 
     // Tile if both dimensions large, or any one dimension large and the other
@@ -118,7 +115,15 @@ void TiledLayerChromium::updateTileSizeAndTilingOption()
     IntSize requestedSize = isTiled ? tileSize : contentBounds();
     const int maxSize = layerTreeHost()->layerRendererCapabilities().maxTextureSize;
     IntSize clampedSize = requestedSize.shrunkTo(IntSize(maxSize, maxSize));
-    m_tiler->setTileSize(clampedSize);
+    setTileSize(clampedSize);
+}
+
+void TiledLayerChromium::setTileSize(const IntSize& size)
+{
+    if (m_tiler && m_tileSize == size)
+        return;
+    m_tileSize = size;
+    m_tiler.clear(); // Changing the tile size invalidates all tiles, so we just throw away the tiling data.
 }
 
 bool TiledLayerChromium::drawsContent() const
@@ -154,15 +159,17 @@ void TiledLayerChromium::setLayerTreeHost(CCLayerTreeHost* host)
 
     createTextureUpdater(host);
 
-    m_textureFormat = host->layerRendererCapabilities().bestTextureFormat;
+    setTextureFormat(host->layerRendererCapabilities().bestTextureFormat);
     m_textureOrientation = textureUpdater()->orientation();
     m_sampledTexelFormat = textureUpdater()->sampledTexelFormat(m_textureFormat);
-    m_tiler = CCLayerTilingData::create(
-        IntSize(defaultTileSize, defaultTileSize),
-        isNonCompositedContent() ? CCLayerTilingData::NoBorderTexels : CCLayerTilingData::HasBorderTexels);
 }
 
-void TiledLayerChromium::updateCompositorResources(GraphicsContext3D* context, CCTextureUpdater& updater)
+void TiledLayerChromium::createTiler(CCLayerTilingData::BorderTexelOption borderTexelOption)
+{
+    m_tiler = CCLayerTilingData::create(m_tileSize, borderTexelOption);
+}
+
+void TiledLayerChromium::updateCompositorResources(GraphicsContext3D*, CCTextureUpdater& updater)
 {
     // Painting could cause compositing to get turned off, which may cause the tiler to become invalidated mid-update.
     if (m_skipsDraw || m_requestedUpdateRect.isEmpty() || !m_tiler->numTiles())
@@ -175,7 +182,7 @@ void TiledLayerChromium::updateCompositorResources(GraphicsContext3D* context, C
             UpdatableTile* tile = tileAt(i, j);
             if (!tile)
                 tile = createTile(i, j);
-            else if (!tile->dirty())
+            else if (!tile->isDirty())
                 continue;
 
             // Calculate page-space rectangle to copy from.
@@ -208,11 +215,6 @@ void TiledLayerChromium::updateCompositorResources(GraphicsContext3D* context, C
             if (paintOffset.y() + destRect.height() > m_paintRect.height())
                 CRASH();
 
-            tile->texture()->bindTexture(context, updater.allocator());
-            const GC3Dint filter = m_tiler->hasBorderTexels() ? GraphicsContext3D::LINEAR : GraphicsContext3D::NEAREST;
-            GLC(context, context->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MIN_FILTER, filter));
-            GLC(context, context->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MAG_FILTER, filter));
-
             updater.append(tile->texture(), textureUpdater(), sourceRect, destRect);
             tile->clearDirty();
         }
@@ -223,8 +225,10 @@ void TiledLayerChromium::updateCompositorResources(GraphicsContext3D* context, C
 
 void TiledLayerChromium::setTilingOption(TilingOption tilingOption)
 {
+    if (m_tiler && m_tilingOption == tilingOption)
+        return;
     m_tilingOption = tilingOption;
-    updateTileSizeAndTilingOption();
+    m_tiler.clear(); // Changing the tiling option invalidates all tiles, so we just throw away the tiling data.
 }
 
 void TiledLayerChromium::setIsMask(bool isMask)
@@ -253,6 +257,8 @@ void TiledLayerChromium::pushPropertiesTo(CCLayerImpl* layer)
         UpdatableTile* tile = static_cast<UpdatableTile*>(iter->second.get());
         if (!tile->texture()->isValid(m_tiler->tileSize(), m_textureFormat))
             continue;
+        if (tile->isDirty())
+            continue;
 
         tiledLayer->syncTextureId(i, j, tile->texture()->textureId());
     }
@@ -330,6 +336,9 @@ void TiledLayerChromium::protectTileTextures(const IntRect& contentRect)
 
 void TiledLayerChromium::prepareToUpdate(const IntRect& contentRect)
 {
+    if (!m_tiler)
+        createTiler(isNonCompositedContent() ? CCLayerTilingData::NoBorderTexels : CCLayerTilingData::HasBorderTexels);
+
     ASSERT(m_tiler);
 
     m_skipsDraw = false;
index f0b549e..363da61 100644 (file)
@@ -63,6 +63,11 @@ protected:
     virtual void cleanupResources();
     void updateTileSizeAndTilingOption();
 
+    // Exposed to subclasses for testing.
+    void setTileSize(const IntSize&);
+    void createTiler(CCLayerTilingData::BorderTexelOption);
+    void setTextureFormat(GC3Denum textureFormat) { m_textureFormat = textureFormat; }
+
     virtual void createTextureUpdater(const CCLayerTreeHost*) = 0;
     virtual LayerTextureUpdater* textureUpdater() const = 0;
 
@@ -73,6 +78,8 @@ protected:
 
     virtual void protectVisibleTileTextures();
 
+    virtual TextureManager* textureManager() const;
+
 private:
     virtual PassRefPtr<CCLayerImpl> createCCLayerImpl();
 
@@ -84,20 +91,19 @@ private:
     UpdatableTile* tileAt(int, int) const;
     UpdatableTile* createTile(int, int);
 
-    TextureManager* textureManager() const;
-
     // Temporary state held between prepareToUpdate() and updateCompositorResources().
     IntRect m_requestedUpdateRect;
     // State held between prepareToUpdate() and pushPropertiesTo(). This represents the area
     // of the layer that is actually re-painted by WebKit.
     IntRect m_paintRect;
 
-    TilingOption m_tilingOption;
     GC3Denum m_textureFormat;
     bool m_skipsDraw;
     LayerTextureUpdater::Orientation m_textureOrientation;
     LayerTextureUpdater::SampledTexelFormat m_sampledTexelFormat;
 
+    TilingOption m_tilingOption;
+    IntSize m_tileSize;
     OwnPtr<CCLayerTilingData> m_tiler;
 };
 
index 2209c8a..e993b57 100644 (file)
@@ -80,6 +80,11 @@ void CCTiledLayerImpl::dumpLayerProperties(TextStream& ts, int indent) const
     ts << "skipsDraw: " << (!m_tiler || m_skipsDraw) << "\n";
 }
 
+bool CCTiledLayerImpl::hasTileAt(int i, int j) const
+{
+    return m_tiler->tileAt(i, j);
+}
+
 DrawableTile* CCTiledLayerImpl::tileAt(int i, int j) const
 {
     return static_cast<DrawableTile*>(m_tiler->tileAt(i, j));
@@ -219,6 +224,7 @@ void CCTiledLayerImpl::drawTiles(LayerRendererChromium* layerRenderer, const Int
     m_tiler->contentRectToTileIndices(contentRect, left, top, right, bottom);
     IntRect layerRect = m_tiler->contentRectToLayerRect(contentRect);
     float sign = FloatQuad(contentRect).isCounterclockwise() ? -1 : 1;
+    const GC3Dint filter = m_tiler->hasBorderTexels() ? GraphicsContext3D::LINEAR : GraphicsContext3D::NEAREST;
     for (int j = top; j <= bottom; ++j) {
         CCLayerQuad::Edge prevEdgeX = contentQuad.left();
 
@@ -343,6 +349,9 @@ void CCTiledLayerImpl::drawTiles(LayerRendererChromium* layerRenderer, const Int
 
             if (tile && tile->textureId()) {
                 context->bindTexture(GraphicsContext3D::TEXTURE_2D, tile->textureId());
+                GLC(context, context->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MIN_FILTER, filter));
+                GLC(context, context->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MAG_FILTER, filter));
+
                 layerRenderer->drawTexturedQuad(globalTransform,
                                                 tileRect.width(), tileRect.height(), opacity, quad,
                                                 program->vertexShader().matrixLocation(),
index 56ecb4d..1003dbc 100644 (file)
@@ -67,8 +67,12 @@ public:
     typedef ProgramBinding<VertexShaderTile, FragmentShaderRGBATexClampAlphaAA> ProgramAA;
     typedef ProgramBinding<VertexShaderTile, FragmentShaderRGBATexClampSwizzleAlphaAA> ProgramSwizzleAA;
 
-private:
+protected:
     explicit CCTiledLayerImpl(int id);
+    // Exposed for testing.
+    bool hasTileAt(int, int) const;
+
+private:
 
     virtual const char* layerTypeAsString() const { return "ContentLayer"; }
 
index 6c5b90d..2bdf180 100644 (file)
@@ -1,3 +1,35 @@
+2011-11-21  James Robinson  <jamesr@chromium.org>
+
+        [chromium] Avoid pushing dirty tiles to the impl layer
+        https://bugs.webkit.org/show_bug.cgi?id=72765
+
+        Reviewed by Kenneth Russell.
+
+        Add unit test for TiledLayerChromium's tile pushing behavior. Requires a fair amount of fake classes. If these
+        fakes are useful in other tests, they should be moved to a shared location.
+
+        * WebKit.gypi:
+        * tests/TiledLayerChromiumTest.cpp: Added.
+        (::FakeTextureAllocator::createTexture):
+        (::FakeTextureAllocator::deleteTexture):
+        (::FakeLayerTextureUpdater::FakeLayerTextureUpdater):
+        (::FakeLayerTextureUpdater::~FakeLayerTextureUpdater):
+        (::FakeLayerTextureUpdater::orientation):
+        (::FakeLayerTextureUpdater::sampledTexelFormat):
+        (::FakeLayerTextureUpdater::prepareToUpdate):
+        (::FakeLayerTextureUpdater::updateTextureRect):
+        (::FakeCCTiledLayerImpl::FakeCCTiledLayerImpl):
+        (::FakeCCTiledLayerImpl::~FakeCCTiledLayerImpl):
+        (::FakeCCTiledLayerImpl::hasTileAt):
+        (::FakeTiledLayerChromium::FakeTiledLayerChromium):
+        (::FakeTiledLayerChromium::~FakeTiledLayerChromium):
+        (::FakeTiledLayerChromium::invalidateRect):
+        (::FakeTiledLayerChromium::prepareToUpdate):
+        (::FakeTiledLayerChromium::textureManager):
+        (::FakeTiledLayerChromium::createTextureUpdater):
+        (::FakeTiledLayerChromium::textureUpdater):
+        (::TEST):
+
 2011-11-21  Dominic Mazzoni  <dmazzoni@google.com>
 
         [Chromium] WebAccessibilityNotification should use AssertMatchingEnums.
index 0f598e7..e0171ac 100644 (file)
@@ -81,6 +81,7 @@
             'tests/PODIntervalTreeTest.cpp',
             'tests/PODRedBlackTreeTest.cpp',
             'tests/RenderTableCellTest.cpp',
+            'tests/TiledLayerChromiumTest.cpp',
             'tests/TilingDataTest.cpp',
             'tests/TreeSynchronizerTest.cpp',
             'tests/TreeTestHelpers.cpp',
diff --git a/Source/WebKit/chromium/tests/TiledLayerChromiumTest.cpp b/Source/WebKit/chromium/tests/TiledLayerChromiumTest.cpp
new file mode 100644 (file)
index 0000000..81e2b93
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include "TiledLayerChromium.h"
+
+#include "LayerTextureUpdater.h"
+#include "TextureManager.h"
+#include "cc/CCSingleThreadProxy.h" // For DebugScopedSetImplThread
+#include "cc/CCTextureUpdater.h"
+#include "cc/CCTiledLayerImpl.h"
+#include <gtest/gtest.h>
+
+using namespace WebCore;
+using namespace WTF;
+
+namespace {
+
+class FakeTextureAllocator : public TextureAllocator {
+public:
+    virtual unsigned createTexture(const IntSize&, GC3Denum) { return 0; }
+    virtual void deleteTexture(unsigned, const IntSize&, GC3Denum) { }
+};
+
+class FakeLayerTextureUpdater : public LayerTextureUpdater {
+public:
+    FakeLayerTextureUpdater() { }
+    virtual ~FakeLayerTextureUpdater() { }
+
+    virtual Orientation orientation() { return BottomUpOrientation; }
+    virtual SampledTexelFormat sampledTexelFormat(GC3Denum) { return SampledTexelFormatRGBA; }
+    virtual void prepareToUpdate(const IntRect&, const IntSize&, int) { }
+    virtual void updateTextureRect(GraphicsContext3D*, TextureAllocator*, ManagedTexture*, const IntRect&, const IntRect&) { }
+};
+
+class FakeCCTiledLayerImpl : public CCTiledLayerImpl {
+public:
+    explicit FakeCCTiledLayerImpl(int id)
+        : CCTiledLayerImpl(id) { }
+    virtual ~FakeCCTiledLayerImpl() { }
+
+    bool hasTileAt(int i, int j)
+    {
+        return CCTiledLayerImpl::hasTileAt(i, j);
+    }
+};
+
+class FakeTiledLayerChromium : public TiledLayerChromium {
+public:
+    explicit FakeTiledLayerChromium(TextureManager* textureManager)
+        : TiledLayerChromium(0)
+        , m_fakeTextureUpdater(adoptRef(new FakeLayerTextureUpdater))
+        , m_textureManager(textureManager)
+    {
+        createTiler(CCLayerTilingData::NoBorderTexels);
+        setTileSize(IntSize(100, 100));
+        setTextureFormat(GraphicsContext3D::RGBA);
+    }
+    virtual ~FakeTiledLayerChromium() { }
+
+    void invalidateRect(const IntRect& rect)
+    {
+        TiledLayerChromium::invalidateRect(rect);
+    }
+
+    void prepareToUpdate(const IntRect& rect)
+    {
+        TiledLayerChromium::prepareToUpdate(rect);
+    }
+
+    virtual TextureManager* textureManager() const { return m_textureManager; }
+
+private:
+    virtual void createTextureUpdater(const CCLayerTreeHost*) { }
+
+    virtual LayerTextureUpdater* textureUpdater() const
+    {
+        return m_fakeTextureUpdater.get();
+    }
+
+    RefPtr<FakeLayerTextureUpdater> m_fakeTextureUpdater;
+    TextureManager* m_textureManager;
+};
+
+TEST(TiledLayerChromiumTest, pushDirtyTiles)
+{
+    OwnPtr<TextureManager> textureManager = TextureManager::create(4*1024*1024, 1024);
+    RefPtr<FakeTiledLayerChromium> layer = adoptRef(new FakeTiledLayerChromium(textureManager.get()));
+    DebugScopedSetImplThread implThread;
+    RefPtr<FakeCCTiledLayerImpl> layerImpl = adoptRef(new FakeCCTiledLayerImpl(0));
+
+    FakeTextureAllocator textureAllocator;
+    CCTextureUpdater updater(&textureAllocator);
+
+    // The tile size is 100x100, so this invalidates and then paints two tiles.
+    layer->invalidateRect(IntRect(0, 0, 100, 200));
+    layer->prepareToUpdate(IntRect(0, 0, 100, 200));
+    layer->updateCompositorResources(0, updater);
+    layer->pushPropertiesTo(layerImpl.get());
+
+    // We should have both tiles on the impl side.
+    EXPECT_TRUE(layerImpl->hasTileAt(0, 0));
+    EXPECT_TRUE(layerImpl->hasTileAt(0, 1));
+
+    textureManager->unprotectAllTextures();
+
+    // Invalidates both tiles...
+    layer->invalidateRect(IntRect(0, 0, 100, 200));
+    // ....but then only update one of them.
+    layer->prepareToUpdate(IntRect(0, 0, 100, 100));
+    layer->updateCompositorResources(0, updater);
+    layer->pushPropertiesTo(layerImpl.get());
+
+    // We should only have the first tile since the other tile was invalidated but not painted.
+    EXPECT_TRUE(layerImpl->hasTileAt(0, 0));
+    EXPECT_FALSE(layerImpl->hasTileAt(0, 1));
+}
+
+} // namespace
+