Source/WebCore: [chromium] Allow modification of size of partially occluded quads...
authorwjmaclean@chromium.org <wjmaclean@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 27 Jan 2012 02:11:55 +0000 (02:11 +0000)
committerwjmaclean@chromium.org <wjmaclean@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 27 Jan 2012 02:11:55 +0000 (02:11 +0000)
https://bugs.webkit.org/show_bug.cgi?id=76349

Reviewed by James Robinson.

Prior to this patch, draw culling either rejects a DrawQuad because it is completely
occluded, or draws the entire quad (even if it is largely occluded). This patch
attempts to reduce the number of pixels drawn by determining if a partially
occluded DrawQuad can be resized to a smaller quad, based on what portion of the
DrawQuad is actually visible, and performing that resizing where possible.

Added cases to existing unit tests.

* platform/graphics/chromium/LayerRendererChromium.cpp:
(WebCore::LayerRendererChromium::drawTileQuad):
* platform/graphics/chromium/cc/CCDrawQuad.cpp:
(WebCore::CCDrawQuad::CCDrawQuad):
* platform/graphics/chromium/cc/CCDrawQuad.h:
(WebCore::CCDrawQuad::setQuadVisibleRect):
(WebCore::CCDrawQuad::quadVisibleRect):
* platform/graphics/chromium/cc/CCQuadCuller.cpp:
(WebCore::rectSubtractRegion):
(WebCore::CCQuadCuller::cullOccludedQuads):

Source/WebKit/chromium: [chromium] Allow modification of size of partially occluded quads.
https://bugs.webkit.org/show_bug.cgi?id=76349

Reviewed by James Robinson.

* tests/CCQuadCullerTest.cpp:
(WebCore::makeTileQuads)
(WebCore::setQuads):
(WebCore::TEST):

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

Source/WebCore/ChangeLog
Source/WebCore/platform/graphics/chromium/LayerRendererChromium.cpp
Source/WebCore/platform/graphics/chromium/cc/CCDrawQuad.cpp
Source/WebCore/platform/graphics/chromium/cc/CCDrawQuad.h
Source/WebCore/platform/graphics/chromium/cc/CCQuadCuller.cpp
Source/WebKit/chromium/ChangeLog
Source/WebKit/chromium/tests/CCQuadCullerTest.cpp

index a493183..d2dbb4d 100644 (file)
@@ -1,3 +1,29 @@
+2012-01-26  W. James MacLean  <wjmaclean@chromium.org>
+
+        [chromium] Allow modification of size of partially occluded quads during culling to reduce pixel overdraw.
+        https://bugs.webkit.org/show_bug.cgi?id=76349
+
+        Reviewed by James Robinson.
+
+        Prior to this patch, draw culling either rejects a DrawQuad because it is completely
+        occluded, or draws the entire quad (even if it is largely occluded). This patch
+        attempts to reduce the number of pixels drawn by determining if a partially
+        occluded DrawQuad can be resized to a smaller quad, based on what portion of the
+        DrawQuad is actually visible, and performing that resizing where possible.
+
+        Added cases to existing unit tests.
+
+        * platform/graphics/chromium/LayerRendererChromium.cpp:
+        (WebCore::LayerRendererChromium::drawTileQuad):
+        * platform/graphics/chromium/cc/CCDrawQuad.cpp:
+        (WebCore::CCDrawQuad::CCDrawQuad):
+        * platform/graphics/chromium/cc/CCDrawQuad.h:
+        (WebCore::CCDrawQuad::setQuadVisibleRect):
+        (WebCore::CCDrawQuad::quadVisibleRect):
+        * platform/graphics/chromium/cc/CCQuadCuller.cpp:
+        (WebCore::rectSubtractRegion):
+        (WebCore::CCQuadCuller::cullOccludedQuads):
+
 2012-01-26  Anders Carlsson  <andersca@apple.com>
 
         Scrollbars disappear when switching from legacy to overlay scrollbars
index bbdf89d..f367ef9 100644 (file)
@@ -544,7 +544,7 @@ static void findTileProgramUniforms(LayerRendererChromium* layerRenderer, const
 
 void LayerRendererChromium::drawTileQuad(const CCTileDrawQuad* quad)
 {
-    const IntRect& tileRect = quad->quadRect();
+    const IntRect& tileRect = quad->quadVisibleRect();
 
     FloatRect clampRect(tileRect);
     // Clamp texture coordinates to avoid sampling outside the layer
@@ -560,7 +560,8 @@ void LayerRendererChromium::drawTileQuad(const CCTileDrawQuad* quad)
     clampRect.inflateY(-clampY);
     FloatSize clampOffset = clampRect.minXMinYCorner() - FloatRect(tileRect).minXMinYCorner();
 
-    FloatPoint textureOffset = quad->textureOffset() + clampOffset;
+    FloatPoint textureOffset = quad->textureOffset() + clampOffset +
+                               IntPoint(quad->quadVisibleRect().location() - quad->quadRect().location());
 
     // Map clamping rectangle to unit square.
     float vertexTexTranslateX = -clampRect.x() / clampRect.width();
@@ -623,13 +624,14 @@ void LayerRendererChromium::drawTileQuad(const CCTileDrawQuad* quad)
         CCLayerQuad::Edge topEdge(topLeft, topRight);
         CCLayerQuad::Edge rightEdge(topRight, bottomRight);
 
-        if (quad->topEdgeAA())
+        // Only apply anti-aliasing to edges not clipped during culling.
+        if (quad->topEdgeAA() && quad->quadVisibleRect().y() == quad->quadRect().y())
             topEdge = deviceLayerEdges.top();
-        if (quad->leftEdgeAA())
+        if (quad->leftEdgeAA() && quad->quadVisibleRect().x() == quad->quadRect().x())
             leftEdge = deviceLayerEdges.left();
-        if (quad->rightEdgeAA())
+        if (quad->rightEdgeAA() && quad->quadVisibleRect().maxX() == quad->quadRect().maxX())
             rightEdge = deviceLayerEdges.right();
-        if (quad->bottomEdgeAA())
+        if (quad->bottomEdgeAA() && quad->quadVisibleRect().maxY() == quad->quadRect().maxY())
             bottomEdge = deviceLayerEdges.bottom();
 
         float sign = FloatQuad(tileRect).isCounterclockwise() ? -1 : 1;
index 1bbfbb6..e1f0a80 100644 (file)
@@ -42,6 +42,7 @@ CCDrawQuad::CCDrawQuad(const CCSharedQuadState* sharedQuadState, Material materi
     : m_sharedQuadState(sharedQuadState)
     , m_material(material)
     , m_quadRect(quadRect)
+    , m_quadVisibleRect(quadRect)
     , m_quadOpaque(true)
     , m_needsBlending(false)
 {
@@ -49,6 +50,12 @@ CCDrawQuad::CCDrawQuad(const CCSharedQuadState* sharedQuadState, Material materi
     ASSERT(m_material != Invalid);
 }
 
+void CCDrawQuad::setQuadVisibleRect(const IntRect& quadVisibleRect)
+{
+    m_quadVisibleRect = quadVisibleRect;
+    m_quadVisibleRect.intersect(m_quadRect);
+}
+
 const CCDebugBorderDrawQuad* CCDrawQuad::toDebugBorderDrawQuad() const
 {
     ASSERT(m_material == DebugBorder);
index acc7c02..5841f99 100644 (file)
@@ -56,6 +56,11 @@ public:
     bool needsBlending() const { return !m_sharedQuadState->isOpaque() || m_needsBlending || opacity() != 1; }
     bool isLayerAxisAlignedIntRect() const { return m_sharedQuadState->isLayerAxisAlignedIntRect(); }
 
+    // Allows changing the rect that gets drawn to make it smaller. Parameter passed
+    // in will be clipped to quadRect().
+    void setQuadVisibleRect(const IntRect&);
+    const IntRect& quadVisibleRect() const { return m_quadVisibleRect; }
+
     enum Material {
         Invalid,
         DebugBorder,
@@ -84,6 +89,7 @@ protected:
 
     Material m_material;
     IntRect m_quadRect;
+    IntRect m_quadVisibleRect;
 
     // By default, the shared quad state determines whether or not this quad is
     // opaque or needs blending. Derived classes can override with these
index d30ffc8..06770da 100644 (file)
@@ -50,16 +50,29 @@ void swap(OwnPtr<WebCore::CCDrawQuad>& a, OwnPtr<WebCore::CCDrawQuad>& b)
 
 namespace WebCore {
 
-static bool regionContainsRect(const Region& region, const IntRect& rect)
+// Determines what portion of rect, if any, is visible (not occluded by region). If
+// the resulting visible region is not rectangular, we just return the original rect.
+static IntRect rectSubtractRegion(const Region& region, const IntRect& rect)
 {
     Region rectRegion(rect);
     Region intersectRegion(intersect(region, rectRegion));
 
     if (intersectRegion.isEmpty())
-        return false;
+        return rect;
 
+    // Test if intersectRegion = rectRegion, if so return empty rect.
     rectRegion.subtract(intersectRegion);
-    return rectRegion.isEmpty();
+    IntRect boundsRect = rectRegion.bounds();
+    if (boundsRect.isEmpty())
+        return boundsRect;
+
+    // Test if rectRegion is still a rectangle. If it is, it will be identical to its bounds.
+    Region boundsRegion(boundsRect);
+    boundsRegion.subtract(rectRegion);
+    if (boundsRegion.isEmpty())
+        return boundsRect;
+
+    return rect;
 }
 
 static IntRect enclosedIntRect(const FloatRect& rect)
@@ -87,14 +100,21 @@ void CCQuadCuller::cullOccludedQuads(CCQuadList& quadList)
     for (int i = quadList.size() - 1; i >= 0; --i) {
         CCDrawQuad* drawQuad = quadList[i].get();
 
-        IntRect quadRect(drawQuad->quadTransform().mapRect(drawQuad->quadRect()));
+        FloatRect floatTransformedRect = drawQuad->quadTransform().mapRect(FloatRect(drawQuad->quadRect()));
+        // Inflate rect to be tested to stay conservative.
+        IntRect transformedQuadRect(enclosingIntRect(floatTransformedRect));
+
+        IntRect transformedVisibleQuadRect = rectSubtractRegion(opaqueCoverageThusFar, transformedQuadRect);
+        bool keepQuad = !transformedVisibleQuadRect.isEmpty();
 
-        bool keepQuad = !regionContainsRect(opaqueCoverageThusFar, quadRect);
+        // See if we can reduce the number of pixels to draw by reducing the size of the draw
+        // quad - we do this by changing its visible rect.
+        if (keepQuad && transformedVisibleQuadRect != transformedQuadRect && drawQuad->isLayerAxisAlignedIntRect())
+            drawQuad->setQuadVisibleRect(drawQuad->quadTransform().inverse().mapRect(transformedVisibleQuadRect));
 
-        if (keepQuad && drawQuad->drawsOpaque() && drawQuad->isLayerAxisAlignedIntRect()) {
-            IntRect opaqueRect = enclosedIntRect(drawQuad->quadTransform().mapRect(FloatRect(drawQuad->quadRect())));
-            opaqueCoverageThusFar.unite(opaqueRect);
-        }
+        // When adding rect to opaque region, deflate it to stay conservative.
+        if (keepQuad && drawQuad->drawsOpaque() && drawQuad->isLayerAxisAlignedIntRect())
+            opaqueCoverageThusFar.unite(Region(enclosedIntRect(floatTransformedRect)));
 
         if (keepQuad)
             culledList.append(quadList[i].release());
index df69e2c..a91fd75 100644 (file)
@@ -1,3 +1,15 @@
+2012-01-26  W. James MacLean  <wjmaclean@chromium.org>
+
+        [chromium] Allow modification of size of partially occluded quads.
+        https://bugs.webkit.org/show_bug.cgi?id=76349
+
+        Reviewed by James Robinson.
+
+        * tests/CCQuadCullerTest.cpp:
+        (WebCore::makeTileQuads)
+        (WebCore::setQuads):
+        (WebCore::TEST):
+
 2012-01-26  Michal Mocny  <mmocny@google.com>
 
         [chromium] Revert changes which added setResourceUsageCHROMIUM gl extension since feature changed directions
index ed15e6c..97384b0 100644 (file)
@@ -26,6 +26,7 @@
 
 #include "cc/CCQuadCuller.h"
 
+#include "cc/CCTileDrawQuad.h"
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
@@ -36,37 +37,29 @@ namespace {
 class CCQuadCullerTest : public testing::Test {
 };
 
-class TestDrawQuad : public CCDrawQuad {
-public:
-    TestDrawQuad(const CCSharedQuadState* state, Material m, const IntRect& rect)
-    : CCDrawQuad(state, m, rect)
-    {
-    }
-
-    static PassOwnPtr<TestDrawQuad> create(const CCSharedQuadState* state, Material m, const IntRect& rect)
-    {
-        return adoptPtr(new TestDrawQuad(state, m, rect));
-    }
-};
+static PassOwnPtr<CCDrawQuad> MakeTileQuad(CCSharedQuadState* state, const IntRect& rect)
+{
+    return CCTileDrawQuad::create(state, rect, 1, IntPoint(1, 1), IntSize(100, 100), 0, false, false, false, false, false);
+}
 
 void setQuads(CCSharedQuadState* rootState, CCSharedQuadState* childState, CCQuadList& quadList)
 {
     quadList.clear();
 
-    quadList.append(TestDrawQuad::create(rootState, CCDrawQuad::TiledContent, IntRect(IntPoint(), IntSize(100, 100))));
-    quadList.append(TestDrawQuad::create(rootState, CCDrawQuad::TiledContent, IntRect(IntPoint(100, 0), IntSize(100, 100))));
-    quadList.append(TestDrawQuad::create(rootState, CCDrawQuad::TiledContent, IntRect(IntPoint(200, 0), IntSize(100, 100))));
-    quadList.append(TestDrawQuad::create(rootState, CCDrawQuad::TiledContent, IntRect(IntPoint(0, 100), IntSize(100, 100))));
-    quadList.append(TestDrawQuad::create(rootState, CCDrawQuad::TiledContent, IntRect(IntPoint(100, 100), IntSize(100, 100))));
-    quadList.append(TestDrawQuad::create(rootState, CCDrawQuad::TiledContent, IntRect(IntPoint(200, 100), IntSize(100, 100))));
-    quadList.append(TestDrawQuad::create(rootState, CCDrawQuad::TiledContent, IntRect(IntPoint(0, 200), IntSize(100, 100))));
-    quadList.append(TestDrawQuad::create(rootState, CCDrawQuad::TiledContent, IntRect(IntPoint(100, 200), IntSize(100, 100))));
-    quadList.append(TestDrawQuad::create(rootState, CCDrawQuad::TiledContent, IntRect(IntPoint(200, 200), IntSize(100, 100))));
-
-    quadList.append(TestDrawQuad::create(childState, CCDrawQuad::TiledContent, IntRect(IntPoint(), IntSize(100, 100))));
-    quadList.append(TestDrawQuad::create(childState, CCDrawQuad::TiledContent, IntRect(IntPoint(100, 0), IntSize(100, 100))));
-    quadList.append(TestDrawQuad::create(childState, CCDrawQuad::TiledContent, IntRect(IntPoint(0, 100), IntSize(100, 100))));
-    quadList.append(TestDrawQuad::create(childState, CCDrawQuad::TiledContent, IntRect(IntPoint(100, 100), IntSize(100, 100))));
+    quadList.append(MakeTileQuad(rootState, IntRect(IntPoint(), IntSize(100, 100))));
+    quadList.append(MakeTileQuad(rootState, IntRect(IntPoint(100, 0), IntSize(100, 100))));
+    quadList.append(MakeTileQuad(rootState, IntRect(IntPoint(200, 0), IntSize(100, 100))));
+    quadList.append(MakeTileQuad(rootState, IntRect(IntPoint(0, 100), IntSize(100, 100))));
+    quadList.append(MakeTileQuad(rootState, IntRect(IntPoint(100, 100), IntSize(100, 100))));
+    quadList.append(MakeTileQuad(rootState, IntRect(IntPoint(200, 100), IntSize(100, 100))));
+    quadList.append(MakeTileQuad(rootState, IntRect(IntPoint(0, 200), IntSize(100, 100))));
+    quadList.append(MakeTileQuad(rootState, IntRect(IntPoint(100, 200), IntSize(100, 100))));
+    quadList.append(MakeTileQuad(rootState, IntRect(IntPoint(200, 200), IntSize(100, 100))));
+
+    quadList.append(MakeTileQuad(childState, IntRect(IntPoint(), IntSize(100, 100))));
+    quadList.append(MakeTileQuad(childState, IntRect(IntPoint(100, 0), IntSize(100, 100))));
+    quadList.append(MakeTileQuad(childState, IntRect(IntPoint(0, 100), IntSize(100, 100))));
+    quadList.append(MakeTileQuad(childState, IntRect(IntPoint(100, 100), IntSize(100, 100))));
 }
 
 #define DECLARE_AND_INITIALIZE_TEST_QUADS               \
@@ -129,6 +122,67 @@ TEST(CCQuadCullerTest, verifyCullCenterTileOnly)
     EXPECT_EQ(quadList.size(), 13u);
     CCQuadCuller::cullOccludedQuads(quadList);
     EXPECT_EQ(quadList.size(), 12u);
+
+    IntRect quadVisibleRect1 = quadList[1].get()->quadVisibleRect();
+    EXPECT_EQ(quadVisibleRect1.height(), 50);
+
+    IntRect quadVisibleRect3 = quadList[3].get()->quadVisibleRect();
+    EXPECT_EQ(quadVisibleRect3.width(), 50);
+
+    // Next index is 4, not 5, since centre quad culled.
+    IntRect quadVisibleRect4 = quadList[4].get()->quadVisibleRect();
+    EXPECT_EQ(quadVisibleRect4.width(), 50);
+    EXPECT_EQ(quadVisibleRect4.x(), 250);
+
+    IntRect quadVisibleRect6 = quadList[6].get()->quadVisibleRect();
+    EXPECT_EQ(quadVisibleRect6.height(), 50);
+    EXPECT_EQ(quadVisibleRect6.y(), 250);
+}
+
+TEST(CCQuadCullerTest, verifyCullCenterTileNonIntegralSize1)
+{
+    DECLARE_AND_INITIALIZE_TEST_QUADS
+
+    childTransform.translate(100, 100);
+
+    // Create root layer tile with extent (99.1, 99.1) -> (200.9, 200.9) to make
+    // sure it doesn't get culled due to transform rounding.
+    TransformationMatrix rootTransform;
+    rootTransform.translate(99.1, 99.1);
+    rootTransform.scale(1.018);
+
+    OwnPtr<CCSharedQuadState> rootState = CCSharedQuadState::create(rootTransform, TransformationMatrix(), rootRect, IntRect(), 1.0, true);
+    OwnPtr<CCSharedQuadState> childState = CCSharedQuadState::create(childTransform, TransformationMatrix(), childRect, IntRect(), 1.0, true);
+
+    quadList.append(MakeTileQuad(rootState.get(), IntRect(IntPoint(), IntSize(100, 100))));
+    quadList.append(MakeTileQuad(childState.get(), IntRect(IntPoint(), IntSize(100, 100))));
+
+    EXPECT_EQ(quadList.size(), 2u);
+    CCQuadCuller::cullOccludedQuads(quadList);
+    EXPECT_EQ(quadList.size(), 2u);
+}
+
+TEST(CCQuadCullerTest, verifyCullCenterTileNonIntegralSize2)
+{
+    DECLARE_AND_INITIALIZE_TEST_QUADS
+
+    // Make the child quad slightly smaller than, and centred over, the root layer tile.
+    // Verify the child does not cause the quad below to be culled due to rounding.
+    childTransform.translate(100.1, 100.1);
+    childTransform.scale(0.982);
+
+    TransformationMatrix rootTransform;
+    rootTransform.translate(100, 100);
+
+    OwnPtr<CCSharedQuadState> rootState = CCSharedQuadState::create(rootTransform, TransformationMatrix(), rootRect, IntRect(), 1.0, true);
+    OwnPtr<CCSharedQuadState> childState = CCSharedQuadState::create(childTransform, TransformationMatrix(), childRect, IntRect(), 1.0, true);
+
+    quadList.append(MakeTileQuad(rootState.get(), IntRect(IntPoint(), IntSize(100, 100))));
+    quadList.append(MakeTileQuad(childState.get(), IntRect(IntPoint(), IntSize(100, 100))));
+
+    EXPECT_EQ(quadList.size(), 2u);
+    CCQuadCuller::cullOccludedQuads(quadList);
+    EXPECT_EQ(quadList.size(), 2u);
 }
 
 TEST(CCQuadCullerTest, verifyCullChildLinesUpBottomRight)