// 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. // 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. // The draw order between two layers is determined by projecting the two triangles making // up each layer quad to the Z = 0 plane, finding points of intersection between the triangles // and backprojecting those points to the plane of the layer to determine the corresponding Z // coordinate. The layer with the lower Z coordinate (farther from the eye) needs to be rendered // first. If the layer projections don't intersect then the layers can drawn in any order and no // edges are created between them in the graph. void CCLayerSorter::sort(LayerList::iterator first, LayerList::iterator last) { #if !defined( NDEBUG ) LOG(CCLayerSorter, "Sorting start ----\n"); #endif createGraphNodes(first, last); createGraphEdges(); Vector<GraphNode*> sortedList; Vector<GraphNode*> noIncomingEdgeNodeList; // Find all the nodes that don't have incoming edges. for (NodeList::iterator la = m_nodes.begin(); la < m_nodes.end(); la++) { if (!la->incoming.size()) noIncomingEdgeNodeList.append(la); } #if !defined( NDEBUG ) LOG(CCLayerSorter, "Sorted list: "); #endif while (m_activeEdges.size() || noIncomingEdgeNodeList.size()) { while (noIncomingEdgeNodeList.size()) { // Pop the last entry from the list. GraphNode* fromNode = noIncomingEdgeNodeList[noIncomingEdgeNodeList.size() - 1]; noIncomingEdgeNodeList.removeLast(); // Add it to the final list. sortedList.append(fromNode); #if !defined( NDEBUG ) LOG(CCLayerSorter, "%d, ", fromNode->layer->debugID()); #endif // Remove all its outgoing edges from the graph. for (unsigned i = 0; i < fromNode->outgoing.size(); i++) { GraphEdge* outgoingEdge = fromNode->outgoing[i]; m_activeEdges.remove(outgoingEdge); removeEdgeFromList(outgoingEdge, outgoingEdge->to->incoming); if (!outgoingEdge->to->incoming.size()) noIncomingEdgeNodeList.append(outgoingEdge->to); } fromNode->outgoing.clear(); } if (!m_activeEdges.size()) break; // 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; 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(); nextNode = &m_nodes[i]; } } ASSERT(nextNode); // Remove all its incoming edges. for (unsigned e = 0; e < nextNode->incoming.size(); e++) { GraphEdge* incomingEdge = nextNode->incoming[e]; m_activeEdges.remove(incomingEdge); removeEdgeFromList(incomingEdge, incomingEdge->from->outgoing); } nextNode->incoming.clear(); noIncomingEdgeNodeList.append(nextNode); #if !defined( NDEBUG ) LOG(CCLayerSorter, "Breaking cycle by cleaning up %d edges from %d\n", minIncomingEdgeCount, nextNode->layer->debugID()); #endif } // Note: The original elements of the list are in no danger of having their ref count go to zero // here as they are all nodes of the layer hierarchy and are kept alive by their parent nodes. int count = 0; for (LayerList::iterator it = first; it < last; it++) *it = sortedList[count++]->layer; #if !defined( NDEBUG ) LOG(CCLayerSorter, "Sorting end ----\n"); #endif m_nodes.clear(); m_edges.clear(); m_activeEdges.clear(); }
// 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 (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 // preserve the ordering of those layers as they were in the original list. // // The draw order between two layers is determined by projecting the two triangles making // up each layer quad to the Z = 0 plane, finding points of intersection between the triangles // and backprojecting those points to the plane of the layer to determine the corresponding Z // coordinate. The layer with the lower Z coordinate (farther from the eye) needs to be rendered // first. // // If the layer projections don't intersect, then no edges (dependencies) are created // between them in the graph. HOWEVER, in this case we still need to preserve the ordering // of the original list of layers, since that list should already have proper z-index // ordering of layers. // void CCLayerSorter::sort(LayerList::iterator first, LayerList::iterator last) { #if !defined( NDEBUG ) LOG(CCLayerSorter, "Sorting start ----\n"); #endif createGraphNodes(first, last); createGraphEdges(); Vector<GraphNode*> sortedList; Deque<GraphNode*> noIncomingEdgeNodeList; // Find all the nodes that don't have incoming edges. for (NodeList::iterator la = m_nodes.begin(); la < m_nodes.end(); la++) { if (!la->incoming.size()) noIncomingEdgeNodeList.append(la); } #if !defined( NDEBUG ) LOG(CCLayerSorter, "Sorted list: "); #endif while (m_activeEdges.size() || noIncomingEdgeNodeList.size()) { while (noIncomingEdgeNodeList.size()) { // It is necessary to preserve the existing ordering of layers, when there are // no explicit dependencies (because this existing ordering has correct // z-index/layout ordering). To preserve this ordering, we process Nodes in // the same order that they were added to the list. GraphNode* fromNode = noIncomingEdgeNodeList.takeFirst(); // Add it to the final list. sortedList.append(fromNode); #if !defined( NDEBUG ) LOG(CCLayerSorter, "%d, ", fromNode->layer->debugID()); #endif // Remove all its outgoing edges from the graph. for (unsigned i = 0; i < fromNode->outgoing.size(); i++) { GraphEdge* outgoingEdge = fromNode->outgoing[i]; m_activeEdges.remove(outgoingEdge); removeEdgeFromList(outgoingEdge, outgoingEdge->to->incoming); outgoingEdge->to->incomingEdgeWeight -= outgoingEdge->weight; if (!outgoingEdge->to->incoming.size()) noIncomingEdgeNodeList.append(outgoingEdge->to); } fromNode->outgoing.clear(); } if (!m_activeEdges.size()) break; // 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 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].incomingEdgeWeight < minIncomingEdgeWeight) { minIncomingEdgeWeight = m_nodes[i].incomingEdgeWeight; nextNode = &m_nodes[i]; } } ASSERT(nextNode); // Remove all its incoming edges. for (unsigned e = 0; e < nextNode->incoming.size(); e++) { GraphEdge* incomingEdge = nextNode->incoming[e]; m_activeEdges.remove(incomingEdge); removeEdgeFromList(incomingEdge, incomingEdge->from->outgoing); } nextNode->incoming.clear(); nextNode->incomingEdgeWeight = 0; noIncomingEdgeNodeList.append(nextNode); #if !defined( NDEBUG ) LOG(CCLayerSorter, "Breaking cycle by cleaning up incoming edges from %d (weight = %f)\n", nextNode->layer->debugID(), minIncomingEdgeWeight); #endif } // Note: The original elements of the list are in no danger of having their ref count go to zero // here as they are all nodes of the layer hierarchy and are kept alive by their parent nodes. int count = 0; for (LayerList::iterator it = first; it < last; it++) *it = sortedList[count++]->layer; #if !defined( NDEBUG ) LOG(CCLayerSorter, "Sorting end ----\n"); #endif m_nodes.clear(); m_edges.clear(); m_activeEdges.clear(); }