Title: [112182] trunk/Source
Revision
112182
Author
[email protected]
Date
2012-03-26 17:16:30 -0700 (Mon, 26 Mar 2012)

Log Message

[chromium] Simplify and fix CCLayerSorter
https://bugs.webkit.org/show_bug.cgi?id=82114

Source/WebCore: 

A significant cleanup, simplification and improvement of the CCLayerSorter code.
Simplified the LayerShape structure to use WebCore's FloatQuad for all overlap tests.
By treating every layer as two triangles, the old overlap code did a lot of redundant work
including testing two of the vertices of the layer and its diagonal twice. The new overlap
tests check:
1. The four corners of each of the two layers against the other layer.
2. The four edges of each layer against the edges of the other layer.
Unlike the old code, the new code has no early-outs in the overlap tests as those where causing
us to miss legitimate overlaps.
A new technique for breaking dependency cycles introduced by intersecting layers is implemented.
Instead of arbitrarily breaking cycles by choosing the node in the graph with the smallest number of
incoming edges (i.e. layers that need to be drawn before it) we chose the one with the smallest sum
of graph edge weights (defined as the max depth difference between two layers). Layers that intersect
have their respective graph edge weight set to zero, making them more likely to be picked for breaking
the cycles (it's not a perfect solution but it seems to perform much better than the previous one).

In addition to being overly complex and doing reduntant work, the old code was missing a
perspective divide when computing the coordinates of the layer's corners in the projected plane
which was the source of a lot of mis-sorted results.

In all, these improvements, fix a lot of outstanding issues with layer sorting, on pages such as:
http://www.keithclark.co.uk/labs/3dcss/demo/
http://2012.beercamp.com
https://developer.mozilla.org/fr/demos/detail/the-box/launch

Tests: CCLayerSorter unit tests.

Reviewed by Kenneth Russell.

* platform/graphics/chromium/cc/CCLayerSorter.cpp:
(WebCore):
(WebCore::perpProduct):
(WebCore::edgeEdgeTest):
(WebCore::CCLayerSorter::checkOverlap):
(WebCore::CCLayerSorter::LayerShape::LayerShape):
(WebCore::CCLayerSorter::LayerShape::layerZFromProjectedPoint):
(WebCore::CCLayerSorter::createGraphNodes):
(WebCore::CCLayerSorter::createGraphEdges):
(WebCore::CCLayerSorter::sort):
* platform/graphics/chromium/cc/CCLayerSorter.h:
(WebCore::CCLayerSorter::CCLayerSorter):
(CCLayerSorter):
(LayerShape):
(WebCore::CCLayerSorter::GraphNode::GraphNode):
(GraphNode):
(WebCore::CCLayerSorter::GraphEdge::GraphEdge):
(GraphEdge):

Source/WebKit/chromium: 

Adjustments to the CCLayerSorter unit tests to account for API changes in the
CCLayerSorter class.

Reviewed by Kenneth Russell.

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

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (112181 => 112182)


--- trunk/Source/WebCore/ChangeLog	2012-03-27 00:12:49 UTC (rev 112181)
+++ trunk/Source/WebCore/ChangeLog	2012-03-27 00:16:30 UTC (rev 112182)
@@ -1,3 +1,56 @@
+2012-03-26  Vangelis Kokkevis  <[email protected]>
+
+        [chromium] Simplify and fix CCLayerSorter
+        https://bugs.webkit.org/show_bug.cgi?id=82114
+
+        A significant cleanup, simplification and improvement of the CCLayerSorter code.
+        Simplified the LayerShape structure to use WebCore's FloatQuad for all overlap tests.
+        By treating every layer as two triangles, the old overlap code did a lot of redundant work
+        including testing two of the vertices of the layer and its diagonal twice. The new overlap
+        tests check:
+        1. The four corners of each of the two layers against the other layer.
+        2. The four edges of each layer against the edges of the other layer.
+        Unlike the old code, the new code has no early-outs in the overlap tests as those where causing
+        us to miss legitimate overlaps.
+        A new technique for breaking dependency cycles introduced by intersecting layers is implemented.
+        Instead of arbitrarily breaking cycles by choosing the node in the graph with the smallest number of
+        incoming edges (i.e. layers that need to be drawn before it) we chose the one with the smallest sum
+        of graph edge weights (defined as the max depth difference between two layers). Layers that intersect
+        have their respective graph edge weight set to zero, making them more likely to be picked for breaking
+        the cycles (it's not a perfect solution but it seems to perform much better than the previous one).
+
+        In addition to being overly complex and doing reduntant work, the old code was missing a
+        perspective divide when computing the coordinates of the layer's corners in the projected plane
+        which was the source of a lot of mis-sorted results.
+
+        In all, these improvements, fix a lot of outstanding issues with layer sorting, on pages such as:
+        http://www.keithclark.co.uk/labs/3dcss/demo/
+        http://2012.beercamp.com
+        https://developer.mozilla.org/fr/demos/detail/the-box/launch
+
+        Tests: CCLayerSorter unit tests.
+
+        Reviewed by Kenneth Russell.
+
+        * platform/graphics/chromium/cc/CCLayerSorter.cpp:
+        (WebCore):
+        (WebCore::perpProduct):
+        (WebCore::edgeEdgeTest):
+        (WebCore::CCLayerSorter::checkOverlap):
+        (WebCore::CCLayerSorter::LayerShape::LayerShape):
+        (WebCore::CCLayerSorter::LayerShape::layerZFromProjectedPoint):
+        (WebCore::CCLayerSorter::createGraphNodes):
+        (WebCore::CCLayerSorter::createGraphEdges):
+        (WebCore::CCLayerSorter::sort):
+        * platform/graphics/chromium/cc/CCLayerSorter.h:
+        (WebCore::CCLayerSorter::CCLayerSorter):
+        (CCLayerSorter):
+        (LayerShape):
+        (WebCore::CCLayerSorter::GraphNode::GraphNode):
+        (GraphNode):
+        (WebCore::CCLayerSorter::GraphEdge::GraphEdge):
+        (GraphEdge):
+
 2012-03-26  Fady Samuel  <[email protected]>
 
         [Chromium] Using WebViewPlugins with --force-compositing-mode causes an ASSERT to fail

Modified: trunk/Source/WebCore/platform/graphics/chromium/cc/CCLayerSorter.cpp (112181 => 112182)


--- trunk/Source/WebCore/platform/graphics/chromium/cc/CCLayerSorter.cpp	2012-03-27 00:12:49 UTC (rev 112181)
+++ trunk/Source/WebCore/platform/graphics/chromium/cc/CCLayerSorter.cpp	2012-03-27 00:16:30 UTC (rev 112182)
@@ -46,56 +46,11 @@
 
 namespace WebCore {
 
-bool CCLayerSorter::pointInTriangle(const FloatPoint& point,
-                                    const FloatPoint& a,
-                                    const FloatPoint& b,
-                                    const FloatPoint& c)
-{
-    // Algorithm from http://www.blackpawn.com/texts/pointinpoly/default.html
-    float x0 = c.x() - a.x();
-    float y0 = c.y() - a.y();
-    float x1 = b.x() - a.x();
-    float y1 = b.y() - a.y();
-    float x2 = point.x() - a.x();
-    float y2 = point.y() - a.y();
-
-    float dot00 = x0 * x0 + y0 * y0;
-    float dot01 = x0 * x1 + y0 * y1;
-    float dot02 = x0 * x2 + y0 * y2;
-    float dot11 = x1 * x1 + y1 * y1;
-    float dot12 = x1 * x2 + y1 * y2;
-    float denominator = dot00 * dot11 - dot01 * dot01;
-    if (!denominator)
-        // Triangle is zero-area. Treat query point as not being inside.
-        return false;
-    // Compute
-    float inverseDenominator = 1.0f / denominator;
-    float u = (dot11 * dot02 - dot01 * dot12) * inverseDenominator;
-    float v = (dot00 * dot12 - dot01 * dot02) * inverseDenominator;
-
-    return (u > 0.0f) && (v > 0.0f) && (u + v < 1.0f);
-}
-
 inline static float perpProduct(const FloatSize& u, const FloatSize& v)
 {
     return u.width() * v.height() - u.height() * v.width();
 }
 
-inline static float innerProduct(const FloatSize& u, const FloatSize& v)
-{
-    return u.width() * v.width() + u.height() * v.height();
-}
-
-
-inline static bool pointInColinearEdge(const FloatPoint& p, const FloatPoint& a, const FloatPoint& b)
-{
-    // if ab is not vertical.
-    if (a.x() != b.x())
-        return (p.x() >= min(a.x(), b.x()) && p.x() <= max(a.x(), b.x()));
-
-    return (p.y() >= min(a.y(), b.y()) && p.y() <= max(a.y(), b.y()));
-}
-
 // Tests if two edges defined by their endpoints (a,b) and (c,d) intersect. Returns true and the
 // point of intersection if they do and false otherwise.
 static bool edgeEdgeTest(const FloatPoint& a, const FloatPoint& b, const FloatPoint& c, const FloatPoint& d, FloatPoint& r)
@@ -106,31 +61,12 @@
 
     float denom = perpProduct(u, v);
 
-    // If denom == 0 then the edges are parallel.
-    if (!denom) {
-        // If the edges are not colinear then there's no intersection.
-        if (perpProduct(u, w) || perpProduct(v, w))
-            return false;
-
-        if (pointInColinearEdge(a, c, d)) {
-            r = a;
-            return true;
-        }
-        if (pointInColinearEdge(b, c, d)) {
-            r = b;
-            return true;
-        }
-        if (pointInColinearEdge(c, a, b)) {
-            r = c;
-            return true;
-        }
-        if (pointInColinearEdge(d, a, b)) {
-            r = d;
-            return true;
-        }
-
+    // If denom == 0 then the edges are parallel. While they could be overlapping
+    // we don't bother to check here as the we'll find their intersections from the
+    // corner to quad tests.
+    if (!denom)
         return false;
-    }
+
     float s = perpProduct(v, w) / denom;
     if (s < 0 || s > 1)
         return false;
@@ -144,105 +80,114 @@
     return true;
 }
 
-float CCLayerSorter::calculateZDiff(const LayerShape& layerA, const LayerShape& layerB, float earlyExitThreshold)
+// Checks whether layer "a" draws on top of layer "b". The weight value returned is an indication of
+// the maximum z-depth difference between the layers or zero if the layers are found to be intesecting
+// (some features are in front and some are behind).
+CCLayerSorter::ABCompareResult CCLayerSorter::checkOverlap(LayerShape* a, LayerShape* b, float zThreshold, float& weight)
 {
-    LayerIntersector intersector(layerA, layerB, earlyExitThreshold);
-    intersector.go();
-    return intersector.zDiff;
-}
+    weight = 0;
 
-CCLayerSorter::LayerIntersector::LayerIntersector(const LayerShape& layerA, const LayerShape& layerB, float earlyExitThreshold)
-    : layerA(layerA)
-    , layerB(layerB)
-    , zDiff(0)
-    , earlyExitThreshold(earlyExitThreshold)
-{
-}
+    // Early out if the projected bounds don't overlap.
+    if (!a->projectedBounds.intersects(b->projectedBounds))
+        return None;
 
-void CCLayerSorter::LayerIntersector::go()
-{
-    (triangleTriangleTest(layerA.c1, layerA.c2, layerA.c3, layerB.c1, layerB.c2, layerB.c3)
-     || triangleTriangleTest(layerA.c3, layerA.c4, layerA.c1, layerB.c1, layerB.c2, layerB.c3)
-     || triangleTriangleTest(layerA.c1, layerA.c2, layerA.c3, layerB.c3, layerB.c4, layerB.c1)
-     || triangleTriangleTest(layerA.c3, layerA.c4, layerA.c1, layerB.c3, layerB.c4, layerB.c1));
-}
+    FloatPoint aPoints[4] = {a->projectedQuad.p1(), a->projectedQuad.p2(), a->projectedQuad.p3(), a->projectedQuad.p4() };
+    FloatPoint bPoints[4] = {b->projectedQuad.p1(), b->projectedQuad.p2(), b->projectedQuad.p3(), b->projectedQuad.p4() };
 
-// Checks if segment pq intersects any of the sides of triangle abc.
-bool CCLayerSorter::LayerIntersector::edgeTriangleTest(const FloatPoint& p, const FloatPoint& q, const FloatPoint& a, const FloatPoint& b, const FloatPoint& c)
-{
+    // Make a list of points that inside both layer quad projections.
+    Vector<FloatPoint> overlapPoints;
+
+    // Check all four corners of one layer against the other layer's quad.
+    for (int i = 0; i < 4; ++i) {
+        if (a->projectedQuad.containsPoint(bPoints[i]))
+            overlapPoints.append(bPoints[i]);
+        if (b->projectedQuad.containsPoint(aPoints[i]))
+            overlapPoints.append(aPoints[i]);
+    }
+
+    // Check all the edges of one layer for intersection with the other layer's edges.
     FloatPoint r;
-    if ((edgeEdgeTest(p, q, a, b, r) && checkZDiff(r))
-        || (edgeEdgeTest(p, q, a, c, r) && checkZDiff(r))
-        || (edgeEdgeTest(p, q, b, c, r) && checkZDiff(r)))
-        return true;
+    for (int ea = 0; ea < 4; ++ea)
+        for (int eb = 0; eb < 4; ++eb)
+            if (edgeEdgeTest(aPoints[ea], aPoints[(ea + 1) % 4],
+                             bPoints[eb], bPoints[(eb + 1) % 4],
+                             r))
+                overlapPoints.append(r);
 
-    return false;
-}
+    if (!overlapPoints.size())
+        return None;
 
-// Checks if two co-planar triangles intersect.
-bool CCLayerSorter::LayerIntersector::triangleTriangleTest(const FloatPoint& a1, const FloatPoint& b1, const FloatPoint& c1, const FloatPoint& a2, const FloatPoint& b2, const FloatPoint& c2)
-{
-    // Check all edges of first triangle with edges of the second one.
-    if (edgeTriangleTest(a1, b1, a2, b2, c2)
-        || edgeTriangleTest(a1, c1, a2, b2, c2)
-        || edgeTriangleTest(b1, c1, a2, b2, c2))
-        return true;
+    // Check the corresponding layer depth value for all overlap points to determine
+    // which layer is in front.
+    float maxPositive = 0;
+    float maxNegative = 0;
+    for (unsigned o = 0; o < overlapPoints.size(); o++) {
+        float za = a->layerZFromProjectedPoint(overlapPoints[o]);
+        float zb = b->layerZFromProjectedPoint(overlapPoints[o]);
 
-    // Check all points of the first triangle for inclusion in the second triangle.
-    if ((pointInTriangle(a1, a2, b2, c2) && checkZDiff(a1))
-        || (pointInTriangle(b1, a2, b2, c2) && checkZDiff(b1))
-        || (pointInTriangle(c1, a2, b2, c2) && checkZDiff(c1)))
-        return true;
+        float diff = za - zb;
+        if (diff > maxPositive)
+            maxPositive = diff;
+        if (diff < maxNegative)
+            maxNegative = diff;
+    }
 
-    // Check all points of the second triangle for inclusion in the first triangle.
-    if ((pointInTriangle(a2, a1, b1, c1) && checkZDiff(a2))
-        || (pointInTriangle(b2, a1, b1, c1) && checkZDiff(b2))
-        || (pointInTriangle(c2, a1, b1, c1) && checkZDiff(c2)))
-        return true;
+    float maxDiff = (fabsf(maxPositive) > fabsf(maxNegative) ? maxPositive : maxNegative);
 
-    return false;
+    // If the results are inconsistent (and the z difference substantial to rule out
+    // numerical errors) then the layers are intersecting. We will still return an
+    // order based on the maximum depth difference but with an edge weight of zero
+    // these layers will get priority if a graph cycle is present and needs to be broken.
+    if (maxPositive > zThreshold && maxNegative < -zThreshold)
+        weight = 0;
+    else
+        weight = fabsf(maxDiff);
+
+    // Maintain relative order if the layers have the same depth at all intersection points.
+    if (maxDiff <= 0)
+        return ABeforeB;
+
+    return BBeforeA;
 }
 
-// Computes the z difference between point p projected onto the two layers to determine
-// which layer is in front. If the new z difference is higher than the currently
-// recorded one the this becomes the point of choice for doing the comparison between the 
-// layers. If the value of difference is higher than our early exit threshold then
-// it means that the z difference is conclusive enough that we don't have to check
-// other intersection points.
-bool CCLayerSorter::LayerIntersector::checkZDiff(const FloatPoint& p)
+CCLayerSorter::LayerShape::LayerShape(float width, float height, const TransformationMatrix& drawTransform)
 {
-    float za = layerZFromProjectedPoint(layerA, p);
-    float zb = layerZFromProjectedPoint(layerB, p);
+    FloatQuad layerQuad(FloatPoint(-width * 0.5, height * 0.5),
+                        FloatPoint(width * 0.5, height * 0.5),
+                        FloatPoint(width * 0.5, -height * 0.5),
+                        FloatPoint(-width * 0.5, -height * 0.5));
 
-    float diff = za - zb;
-    float absDiff = fabsf(diff);
-    if ((absDiff) > fabsf(zDiff)) {
-        intersectionPoint = p;
-        zDiff = diff;
-    }
+    // Compute the projection of the layer quad onto the z = 0 plane.
+    projectedQuad = drawTransform.mapQuad(layerQuad);
+    projectedBounds = projectedQuad.boundingBox();
 
-    if (absDiff > earlyExitThreshold)
-        return true;
+    // Compute the normal of the layer's plane.
+    FloatPoint3D c1 = drawTransform.mapPoint(FloatPoint3D(0, 0, 0));
+    FloatPoint3D c2 = drawTransform.mapPoint(FloatPoint3D(0, 1, 0));
+    FloatPoint3D c3 = drawTransform.mapPoint(FloatPoint3D(1, 0, 0));
+    FloatPoint3D c12 = c2 - c1;
+    FloatPoint3D c13 = c3 - c1;
+    layerNormal = c13.cross(c12);
 
-    return false;
+    transformOrigin = c1;
 }
 
-
-// Returns the Z coordinate of a point on the given layer that projects
+// Returns the Z coordinate of a point on the layer that projects
 // to point p which lies on the z = 0 plane. It does it by computing the
 // intersection of a line starting from p along the Z axis and the plane
 // of the layer.
-float CCLayerSorter::LayerIntersector::layerZFromProjectedPoint(const LayerShape& layer, const FloatPoint& p)
+float CCLayerSorter::LayerShape::layerZFromProjectedPoint(const FloatPoint& p) const
 {
     const FloatPoint3D zAxis(0, 0, 1);
-    FloatPoint3D w = FloatPoint3D(p.x(), p.y(), 0) - layer.origin;
+    FloatPoint3D w = FloatPoint3D(p) - transformOrigin;
 
-    float d = layer.normal.dot(zAxis);
-    float n = -layer.normal.dot(w);
+    float d = layerNormal.dot(zAxis);
+    float n = -layerNormal.dot(w);
 
-    // Check if layer is parallel to the z = 0 axis
+    // Check if layer is parallel to the z = 0 axis which will make it
+    // invisible and hence returning zero is fine.
     if (!d)
-        return layer.origin.z();
+        return 0;
 
     // The intersection point would be given by:
     // p + (n / d) * u  but since we are only interested in the 
@@ -250,43 +195,6 @@
     return n / d;
 }
 
-CCLayerSorter::CCLayerSorter()
-    : m_zRange(0)
-{
-}
-
-CCLayerSorter::ABCompareResult CCLayerSorter::checkOverlap(GraphNode* a, GraphNode* b)
-{
-    if (!a->shape.boundingBox.intersects(b->shape.boundingBox))
-        return None;
-
-    // These thresholds are defined relative to the total Z range. If further Z-fighting
-    // bugs come up due to precision errors, it may be worth considering doing an ulp
-    // error measurement instead.
-    float exitThreshold = m_zRange * 0.01;
-    float nonZeroThreshold = max(exitThreshold, 0.001f);
-
-    float zDiff = calculateZDiff(a->shape, b->shape, exitThreshold);
-
-    if (zDiff > nonZeroThreshold)
-        return BBeforeA;
-    if (zDiff < -nonZeroThreshold)
-        return ABeforeB;
-
-    return None;
-}
-
-CCLayerSorter::LayerShape::LayerShape(const FloatPoint3D& p1, const FloatPoint3D& p2, const FloatPoint3D& p3, const FloatPoint3D& p4)
-    : normal((p2 - p1).cross(p3 - p1))
-    , c1(FloatPoint(p1.x(), p1.y()))
-    , c2(FloatPoint(p2.x(), p2.y()))
-    , c3(FloatPoint(p3.x(), p3.y()))
-    , c4(FloatPoint(p4.x(), p4.y()))
-    , origin(p1)
-{
-    boundingBox.fitToPoints(c1, c2, c3, c4);
-}
-
 void CCLayerSorter::createGraphNodes(LayerList::iterator first, LayerList::iterator last)
 {
 #if !defined( NDEBUG )
@@ -309,25 +217,21 @@
         float layerWidth, layerHeight;
         if (renderSurface) {
             drawTransform = renderSurface->drawTransform();
-            layerWidth = 0.5 * renderSurface->contentRect().width();
-            layerHeight = 0.5 * renderSurface->contentRect().height();
+            layerWidth = renderSurface->contentRect().width();
+            layerHeight = renderSurface->contentRect().height();
         } else {
             drawTransform = node.layer->drawTransform();
-            layerWidth = 0.5 * node.layer->bounds().width();
-            layerHeight = 0.5 * node.layer->bounds().height();
+            layerWidth = node.layer->bounds().width();
+            layerHeight = node.layer->bounds().height();
         }
 
-        FloatPoint3D c1 = drawTransform.mapPoint(FloatPoint3D(-layerWidth, layerHeight, 0));
-        FloatPoint3D c2 = drawTransform.mapPoint(FloatPoint3D(layerWidth, layerHeight, 0));
-        FloatPoint3D c3 = drawTransform.mapPoint(FloatPoint3D(layerWidth, -layerHeight, 0));
-        FloatPoint3D c4 = drawTransform.mapPoint(FloatPoint3D(-layerWidth, -layerHeight, 0));
-        node.shape = LayerShape(c1, c2, c3, c4);
-    
-        maxZ = max(c4.z(), max(c3.z(), max(c2.z(), max(maxZ, c1.z()))));
-        minZ = min(c4.z(), min(c3.z(), min(c2.z(), min(minZ, c1.z()))));
+        node.shape = LayerShape(layerWidth, layerHeight, drawTransform);
+
+        maxZ = max(maxZ, node.shape.transformOrigin.z());
+        minZ = min(minZ, node.shape.transformOrigin.z());
     }
-    if (last - first)
-        m_zRange = fabsf(maxZ - minZ);
+
+    m_zRange = fabsf(maxZ - minZ);
 }
 
 void CCLayerSorter::createGraphEdges()
@@ -335,6 +239,11 @@
 #if !defined( NDEBUG )
     LOG(CCLayerSorter, "Edges:\n");
 #endif
+    // Fraction of the total zRange below which z differences
+    // are not considered reliable.
+    const float zThresholdFactor = 0.01;
+    float zThreshold = m_zRange * zThresholdFactor;
+
     for (unsigned na = 0; na < m_nodes.size(); na++) {
         GraphNode& nodeA = m_nodes[na];
         if (!nodeA.layer->drawsContent() && !nodeA.layer->renderSurface())
@@ -343,7 +252,8 @@
             GraphNode& nodeB = m_nodes[nb];
             if (!nodeB.layer->drawsContent() && !nodeB.layer->renderSurface())
                 continue;
-            ABCompareResult overlapResult = checkOverlap(&nodeA, &nodeB);
+            float weight = 0;
+            ABCompareResult overlapResult = checkOverlap(&nodeA.shape, &nodeB.shape, zThreshold, weight);
             GraphNode* startNode = 0;
             GraphNode* endNode = 0;
             if (overlapResult == ABeforeB) {
@@ -358,7 +268,7 @@
 #if !defined( NDEBUG )
                 LOG(CCLayerSorter, "%d -> %d\n", startNode->layer->debugID(), endNode->layer->debugID());
 #endif
-                m_edges.append(GraphEdge(startNode, endNode));
+                m_edges.append(GraphEdge(startNode, endNode, weight));
             }
         }
     }
@@ -368,6 +278,7 @@
         m_activeEdges.add(&edge, &edge);
         edge.from->outgoing.append(&edge);
         edge.to->incoming.append(&edge);
+        edge.to->incomingEdgeWeight += edge.weight;
     }
 }
 
@@ -390,7 +301,7 @@
 
 // Sorts the given list of layers such that they can be painted in a back-to-front
 // order. Sorting produces correct results for non-intersecting layers that don't have
-// cyclical order dependencies. Cycles and intersections are broken aribtrarily.
+// cyclical order dependencies. Cycles and intersections are broken (somewhat) aribtrarily.
 // Sorting of layers is done via a topological sort of a directed graph whose nodes are
 // the layers themselves. An edge from node A to node B signifies that layer A needs to
 // be drawn before layer B. If A and B have no dependency between each other, then we
@@ -450,6 +361,7 @@
 
                 m_activeEdges.remove(outgoingEdge);
                 removeEdgeFromList(outgoingEdge, outgoingEdge->to->incoming);
+                outgoingEdge->to->incomingEdgeWeight -= outgoingEdge->weight;
 
                 if (!outgoingEdge->to->incoming.size())
                     noIncomingEdgeNodeList.append(outgoingEdge->to);
@@ -462,12 +374,14 @@
 
         // If there are still active edges but the list of nodes without incoming edges
         // is empty then we have run into a cycle. Break the cycle by finding the node
-        // with the least number incoming edges and remove them all.
-        unsigned minIncomingEdgeCount = UINT_MAX;
+        // with the smallest overall incoming edge weight and use it. This will favor
+        // nodes that have zero-weight incoming edges i.e. layers that are being
+        // occluded by a layer that intersects them.
+        float minIncomingEdgeWeight = FLT_MAX;
         GraphNode* nextNode = 0;
         for (unsigned i = 0; i < m_nodes.size(); i++) {
-            if (m_nodes[i].incoming.size() && (m_nodes[i].incoming.size() < minIncomingEdgeCount)) {
-                minIncomingEdgeCount = m_nodes[i].incoming.size();
+            if (m_nodes[i].incoming.size() && m_nodes[i].incomingEdgeWeight < minIncomingEdgeWeight) {
+                minIncomingEdgeWeight = m_nodes[i].incomingEdgeWeight;
                 nextNode = &m_nodes[i];
             }
         }
@@ -480,9 +394,10 @@
             removeEdgeFromList(incomingEdge, incomingEdge->from->outgoing);
         }
         nextNode->incoming.clear();
+        nextNode->incomingEdgeWeight = 0;
         noIncomingEdgeNodeList.append(nextNode);
 #if !defined( NDEBUG )
-        LOG(CCLayerSorter, "Breaking cycle by cleaning up %d edges from %d\n", minIncomingEdgeCount, nextNode->layer->debugID());
+        LOG(CCLayerSorter, "Breaking cycle by cleaning up incoming edges from %d (weight = %f)\n", nextNode->layer->debugID(), minIncomingEdgeWeight);
 #endif
     }
 

Modified: trunk/Source/WebCore/platform/graphics/chromium/cc/CCLayerSorter.h (112181 => 112182)


--- trunk/Source/WebCore/platform/graphics/chromium/cc/CCLayerSorter.h	2012-03-27 00:12:49 UTC (rev 112181)
+++ trunk/Source/WebCore/platform/graphics/chromium/cc/CCLayerSorter.h	2012-03-27 00:16:30 UTC (rev 112182)
@@ -26,6 +26,7 @@
 #define CCLayerSorter_h
 
 #include "FloatPoint3D.h"
+#include "FloatQuad.h"
 #include "FloatRect.h"
 #include "cc/CCLayerImpl.h"
 #include <wtf/HashMap.h>
@@ -37,64 +38,54 @@
 class CCLayerSorter {
     WTF_MAKE_NONCOPYABLE(CCLayerSorter);
 public:
-    CCLayerSorter();
+    CCLayerSorter() : m_zRange(0) { }
 
     typedef Vector<CCLayerImpl*> LayerList;
 
     void sort(LayerList::iterator first, LayerList::iterator last);
 
-    // Helper methods, public for unit testing.
-    static bool pointInTriangle(const FloatPoint&, const FloatPoint&, const FloatPoint&, const FloatPoint&);
-
     // Holds various useful properties derived from a layer's 3D outline.
     struct LayerShape {
         LayerShape() { }
-        LayerShape(const FloatPoint3D&, const FloatPoint3D&, const FloatPoint3D&, const FloatPoint3D&);
+        LayerShape(float width, float height, const TransformationMatrix& drawTransform);
 
-        FloatPoint3D normal;
-        FloatPoint c1, c2, c3, c4;
-        FloatPoint3D origin;
-        FloatRect boundingBox;
+        float layerZFromProjectedPoint(const FloatPoint&) const;
+
+        FloatPoint3D layerNormal;
+        FloatPoint3D transformOrigin;
+        FloatQuad projectedQuad;
+        FloatRect projectedBounds;
     };
 
-    static float calculateZDiff(const LayerShape&, const LayerShape&, float earlyExitThreshold);
+    enum ABCompareResult {
+        ABeforeB,
+        BBeforeA,
+        None
+    };
 
+    static ABCompareResult checkOverlap(LayerShape*, LayerShape*, float zThreshold, float& weight);
+
 private:
     struct GraphEdge;
 
     struct GraphNode {
-        explicit GraphNode(CCLayerImpl* cclayer) : layer(cclayer) { }
+        explicit GraphNode(CCLayerImpl* cclayer) : layer(cclayer), incomingEdgeWeight(0) { }
 
         CCLayerImpl* layer;
         LayerShape shape;
         Vector<GraphEdge*> incoming;
         Vector<GraphEdge*> outgoing;
+        float incomingEdgeWeight;
     };
 
     struct GraphEdge {
-        GraphEdge(GraphNode* fromNode, GraphNode* toNode) : from(fromNode), to(toNode) { };
+        GraphEdge(GraphNode* fromNode, GraphNode* toNode, float weight) : from(fromNode), to(toNode), weight(weight) { };
 
         GraphNode* from;
         GraphNode* to;
+        float weight;
     };
 
-    struct LayerIntersector {
-        LayerIntersector(const LayerShape&, const LayerShape&, float);
-
-        void go();
-
-        float layerZFromProjectedPoint(const LayerShape&, const FloatPoint&);
-        bool triangleTriangleTest(const FloatPoint&, const FloatPoint&, const FloatPoint&, const FloatPoint&, const FloatPoint&, const FloatPoint&);
-        bool edgeTriangleTest(const FloatPoint&, const FloatPoint&, const FloatPoint&, const FloatPoint&, const FloatPoint&);
-        bool checkZDiff(const FloatPoint&);
-
-        FloatPoint intersectionPoint;
-        const LayerShape& layerA;
-        const LayerShape& layerB;
-        float zDiff;
-        float earlyExitThreshold;
-    };
-
     typedef Vector<GraphNode> NodeList;
     typedef Vector<GraphEdge> EdgeList;
     NodeList m_nodes;
@@ -104,18 +95,9 @@
     typedef HashMap<GraphEdge*, GraphEdge*> EdgeMap;
     EdgeMap m_activeEdges;
 
-    float m_zDiffEpsilon;
-
     void createGraphNodes(LayerList::iterator first, LayerList::iterator last);
     void createGraphEdges();
     void removeEdgeFromList(GraphEdge*, Vector<GraphEdge*>&);
-
-    enum ABCompareResult {
-        ABeforeB,
-        BBeforeA,
-        None
-    };
-    ABCompareResult checkOverlap(GraphNode*, GraphNode*);
 };
 
 }

Modified: trunk/Source/WebKit/chromium/ChangeLog (112181 => 112182)


--- trunk/Source/WebKit/chromium/ChangeLog	2012-03-27 00:12:49 UTC (rev 112181)
+++ trunk/Source/WebKit/chromium/ChangeLog	2012-03-27 00:16:30 UTC (rev 112182)
@@ -1,3 +1,17 @@
+2012-03-26  Vangelis Kokkevis  <[email protected]>
+
+        [chromium] Simplify and fix CCLayerSorter
+        https://bugs.webkit.org/show_bug.cgi?id=82114
+
+        Adjustments to the CCLayerSorter unit tests to account for API changes in the
+        CCLayerSorter class.
+
+        Reviewed by Kenneth Russell.
+
+        * tests/CCLayerSorterTest.cpp:
+        (WebCore):
+        (WebCore::TEST):
+
 2012-03-26  James Robinson  <[email protected]>
 
         Scrollable plugins not registered properly in ScrollingCoordinator

Modified: trunk/Source/WebKit/chromium/tests/CCLayerSorterTest.cpp (112181 => 112182)


--- trunk/Source/WebKit/chromium/tests/CCLayerSorterTest.cpp	2012-03-27 00:12:49 UTC (rev 112181)
+++ trunk/Source/WebKit/chromium/tests/CCLayerSorterTest.cpp	2012-03-27 00:16:30 UTC (rev 112182)
@@ -34,82 +34,95 @@
 
 namespace {
 
-TEST(CCLayerSorterTest, PointInTriangle)
+// Note: In the following overlap tests, the "camera" is looking down the negative Z axis,
+// meaning that layers with smaller z values (more negative) are further from the camera
+// and therefore must be drawn before layers with higher z values.
+
+TEST(CCLayerSorterTest, BasicOverlap)
 {
-    FloatPoint a(10.0, 10.0);
-    FloatPoint b(30.0, 10.0);
-    FloatPoint c(20.0, 20.0);
+    CCLayerSorter::ABCompareResult overlapResult;
+    const float zThreshold = 0.1f;
+    float weight = 0;
 
-    // Point in the center is in the triangle.
-    EXPECT_TRUE(CCLayerSorter::pointInTriangle(FloatPoint(20.0, 15.0), a, b, c));
+    // Trivial test, with one layer directly obscuring the other.
+    TransformationMatrix neg4Translate;
+    neg4Translate.translate3d(0, 0, -4);
+    CCLayerSorter::LayerShape front(2, 2, neg4Translate);
 
-    // Permuting the corners doesn't change the result.
-    EXPECT_TRUE(CCLayerSorter::pointInTriangle(FloatPoint(20.0, 15.0), a, c, b));
-    EXPECT_TRUE(CCLayerSorter::pointInTriangle(FloatPoint(20.0, 15.0), b, a, c));
-    EXPECT_TRUE(CCLayerSorter::pointInTriangle(FloatPoint(20.0, 15.0), b, c, a));
-    EXPECT_TRUE(CCLayerSorter::pointInTriangle(FloatPoint(20.0, 15.0), c, a, b));
-    EXPECT_TRUE(CCLayerSorter::pointInTriangle(FloatPoint(20.0, 15.0), c, b, a));
+    TransformationMatrix neg5Translate;
+    neg5Translate.translate3d(0, 0, -5);
+    CCLayerSorter::LayerShape back(2, 2, neg5Translate);
 
-    // Points on the edges are not in the triangle.
-    EXPECT_FALSE(CCLayerSorter::pointInTriangle(FloatPoint(20.0, 10.0), a, b, c));
-    EXPECT_FALSE(CCLayerSorter::pointInTriangle(FloatPoint(15.0, 15.0), a, b, c));
-    EXPECT_FALSE(CCLayerSorter::pointInTriangle(FloatPoint(25.0, 15.0), a, b, c));
+    overlapResult = CCLayerSorter::checkOverlap(&front, &back, zThreshold, weight);
+    EXPECT_EQ(CCLayerSorter::BBeforeA, overlapResult);
+    EXPECT_EQ(1, weight);
 
-    // Points just inside the edges are in the triangle.
-    EXPECT_TRUE(CCLayerSorter::pointInTriangle(FloatPoint(20.0, 10.01), a, b, c));
-    EXPECT_TRUE(CCLayerSorter::pointInTriangle(FloatPoint(15.01, 15.0), a, b, c));
-    EXPECT_TRUE(CCLayerSorter::pointInTriangle(FloatPoint(24.99, 15.0), a, b, c));
+    overlapResult = CCLayerSorter::checkOverlap(&back, &front, zThreshold, weight);
+    EXPECT_EQ(CCLayerSorter::ABeforeB, overlapResult);
+    EXPECT_EQ(1, weight);
 
-    // Zero-area triangle doesn't intersect any point.
-    EXPECT_FALSE(CCLayerSorter::pointInTriangle(FloatPoint(15.0, 10.0), a, b, FloatPoint(20.0, 10.0)));
+    // One layer translated off to the right. No overlap should be detected.
+    TransformationMatrix rightTranslate;
+    rightTranslate.translate3d(10, 0, -5);
+    CCLayerSorter::LayerShape backRight(2, 2, rightTranslate);
+    overlapResult = CCLayerSorter::checkOverlap(&front, &backRight, zThreshold, weight);
+    EXPECT_EQ(CCLayerSorter::None, overlapResult);
+
+    // When comparing a layer with itself, z difference is always 0.
+    overlapResult = CCLayerSorter::checkOverlap(&front, &front, zThreshold, weight);
+    EXPECT_EQ(0, weight);
 }
 
-TEST(CCLayerSorterTest, CalculateZDiff)
+TEST(CCLayerSorterTest, RightAngleOverlap)
 {
-    // This should be bigger than the range of z values used.
-    const float threshold = 10.0;
+    CCLayerSorter::ABCompareResult overlapResult;
+    const float zThreshold = 0.1f;
+    float weight = 0;
 
-    // Trivial test, with one layer directly obscuring the other.
+    TransformationMatrix perspectiveMatrix;
+    perspectiveMatrix.applyPerspective(1000);
 
-    CCLayerSorter::LayerShape front(
-        FloatPoint3D(-1.0, 1.0, 5.0),
-        FloatPoint3D(1.0, 1.0, 5.0),
-        FloatPoint3D(1.0, -1.0, 5.0),
-        FloatPoint3D(-1.0, -1.0, 5.0));
+    // Two layers forming a right angle with a perspective viewing transform.
+    TransformationMatrix leftFaceMatrix;
+    leftFaceMatrix.rotate3d(0, 1, 0, -90).translateRight3d(-1, 0, -5);
+    CCLayerSorter::LayerShape leftFace(2, 2, perspectiveMatrix * leftFaceMatrix);
+    TransformationMatrix frontFaceMatrix;
+    frontFaceMatrix.translate3d(0, 0, -4);
+    CCLayerSorter::LayerShape frontFace(2, 2, perspectiveMatrix * frontFaceMatrix);
 
-    CCLayerSorter::LayerShape back(
-        FloatPoint3D(-1.0, 1.0, 4.0),
-        FloatPoint3D(1.0, 1.0, 4.0),
-        FloatPoint3D(1.0, -1.0, 4.0),
-        FloatPoint3D(-1.0, -1.0, 4.0));
+    overlapResult = CCLayerSorter::checkOverlap(&frontFace, &leftFace, zThreshold, weight);
+    EXPECT_EQ(CCLayerSorter::BBeforeA, overlapResult);
+}
 
-    EXPECT_GT(CCLayerSorter::calculateZDiff(front, back, threshold), 0.0);
-    EXPECT_LT(CCLayerSorter::calculateZDiff(back, front, threshold), 0.0);
+TEST(CCLayerSorterTest, IntersectingLayerOverlap)
+{
+    CCLayerSorter::ABCompareResult overlapResult;
+    const float zThreshold = 0.1f;
+    float weight = 0;
 
-    // When comparing a layer with itself, zDiff is always 0.
-    EXPECT_EQ(CCLayerSorter::calculateZDiff(front, front, threshold), 0.0);
-    EXPECT_EQ(CCLayerSorter::calculateZDiff(back, back, threshold), 0.0);
+    TransformationMatrix perspectiveMatrix;
+    perspectiveMatrix.applyPerspective(1000);
 
-    // Same again but with two layers that intersect only at one point (0,0).
-    // This *does* count as obscuring, so we should get the same results.
+    // Intersecting layers. An explicit order will be returned based on relative z
+    // values at the overlapping features but the weight returned should be zero.
+    TransformationMatrix frontFaceMatrix;
+    frontFaceMatrix.translate3d(0, 0, -4);
+    CCLayerSorter::LayerShape frontFace(2, 2, perspectiveMatrix * frontFaceMatrix);
 
-    front = CCLayerSorter::LayerShape(
-        FloatPoint3D(-1.0, 0.0, 5.0),
-        FloatPoint3D(0.0, 0.0, 5.0),
-        FloatPoint3D(0.0, -1.0, 5.0),
-        FloatPoint3D(-1.0, -1.0, 5.0));
+    TransformationMatrix throughMatrix;
+    throughMatrix.rotate3d(0, 1, 0, 45).translateRight3d(0, 0, -4);
+    CCLayerSorter::LayerShape rotatedFace(2, 2, perspectiveMatrix * throughMatrix);
+    overlapResult = CCLayerSorter::checkOverlap(&frontFace, &rotatedFace, zThreshold, weight);
+    EXPECT_NE(CCLayerSorter::None, overlapResult);
+    EXPECT_EQ(0, weight);
+}
 
-    back = CCLayerSorter::LayerShape(
-        FloatPoint3D(0.0, 1.0, 4.0),
-        FloatPoint3D(1.0, 1.0, 4.0),
-        FloatPoint3D(1.0, 0.0, 4.0),
-        FloatPoint3D(0.0, 0.0, 4.0));
+TEST(CCLayerSorterTest, LayersAtAngleOverlap)
+{
+    CCLayerSorter::ABCompareResult overlapResult;
+    const float zThreshold = 0.1f;
+    float weight = 0;
 
-    EXPECT_GT(CCLayerSorter::calculateZDiff(front, back, threshold), 0.0);
-    EXPECT_LT(CCLayerSorter::calculateZDiff(back, front, threshold), 0.0);
-    EXPECT_EQ(CCLayerSorter::calculateZDiff(front, front, threshold), 0.0);
-    EXPECT_EQ(CCLayerSorter::calculateZDiff(back, back, threshold), 0.0);
-
     // Trickier test with layers at an angle.
     //
     //   -x . . . . 0 . . . . +x
@@ -120,42 +133,26 @@
     // +z         /
     //
     // C is in front of A and behind B (not what you'd expect by comparing centers).
-    // A and B don't overlap, so they're incomparable (zDiff = 0).
+    // A and B don't overlap, so they're incomparable.
 
-    const float yHi = 10.0;
-    const float yLo = -10.0;
-    const float zA = 1.0;
-    const float zB = -1.0;
+    TransformationMatrix transformA;
+    transformA.translate3d(-6, 0, 1);
+    CCLayerSorter::LayerShape layerA(8, 20, transformA);
 
-    CCLayerSorter::LayerShape layerA(
-        FloatPoint3D(-10.0, yHi, zA),
-        FloatPoint3D(-2.0, yHi, zA),
-        FloatPoint3D(-2.0, yLo, zA),
-        FloatPoint3D(-10.0, yLo, zA));
+    TransformationMatrix transformB;
+    transformB.translate3d(6, 0, -1);
+    CCLayerSorter::LayerShape layerB(8, 20, transformB);
 
-    CCLayerSorter::LayerShape layerB(
-        FloatPoint3D(2.0, yHi, zB),
-        FloatPoint3D(10.0, yHi, zB),
-        FloatPoint3D(10.0, yLo, zB),
-        FloatPoint3D(2.0, yLo, zB));
+    TransformationMatrix transformC;
+    transformC.rotate3d(0, 1, 0, 40);
+    CCLayerSorter::LayerShape layerC(8, 20, transformC);
 
-    CCLayerSorter::LayerShape layerC(
-        FloatPoint3D(-5.0, yHi, 5.0),
-        FloatPoint3D(5.0, yHi, -5.0),
-        FloatPoint3D(5.0, yLo, -5.0),
-        FloatPoint3D(-5.0, yLo, 5.0));
-
-    EXPECT_EQ(CCLayerSorter::calculateZDiff(layerA, layerA, threshold), 0.0);
-    EXPECT_EQ(CCLayerSorter::calculateZDiff(layerA, layerB, threshold), 0.0);
-    EXPECT_LT(CCLayerSorter::calculateZDiff(layerA, layerC, threshold), 0.0);
-
-    EXPECT_EQ(CCLayerSorter::calculateZDiff(layerB, layerA, threshold), 0.0);
-    EXPECT_EQ(CCLayerSorter::calculateZDiff(layerB, layerB, threshold), 0.0);
-    EXPECT_GT(CCLayerSorter::calculateZDiff(layerB, layerC, threshold), 0.0);
-
-    EXPECT_GT(CCLayerSorter::calculateZDiff(layerC, layerA, threshold), 0.0);
-    EXPECT_LT(CCLayerSorter::calculateZDiff(layerC, layerB, threshold), 0.0);
-    EXPECT_EQ(CCLayerSorter::calculateZDiff(layerC, layerC, threshold), 0.0);
+    overlapResult = CCLayerSorter::checkOverlap(&layerA, &layerC, zThreshold, weight);
+    EXPECT_EQ(CCLayerSorter::ABeforeB, overlapResult);
+    overlapResult = CCLayerSorter::checkOverlap(&layerC, &layerB, zThreshold, weight);
+    EXPECT_EQ(CCLayerSorter::ABeforeB, overlapResult);
+    overlapResult = CCLayerSorter::checkOverlap(&layerA, &layerB, zThreshold, weight);
+    EXPECT_EQ(CCLayerSorter::None, overlapResult);
 }
 
 TEST(CCLayerSorterTest, verifyExistingOrderingPreservedWhenNoZDiff)
_______________________________________________
webkit-changes mailing list
[email protected]
http://lists.webkit.org/mailman/listinfo.cgi/webkit-changes

Reply via email to