Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/gfx/layers/LayerSorter.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
 * License, v. 2.0. If a copy of the MPL was not distributed with this
5
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "LayerSorter.h"
8
#include <math.h>                       // for fabs
9
#include <stdint.h>                     // for uint32_t
10
#include <stdio.h>                      // for fprintf, stderr, FILE
11
#include <stdlib.h>                     // for getenv
12
#include "DirectedGraph.h"              // for DirectedGraph
13
#include "Layers.h"                     // for Layer
14
#include "gfxEnv.h"                     // for gfxEnv
15
#include "gfxLineSegment.h"             // for gfxLineSegment
16
#include "gfxPoint.h"                   // for gfxPoint
17
#include "gfxQuad.h"                    // for gfxQuad
18
#include "gfxRect.h"                    // for gfxRect
19
#include "gfxTypes.h"                   // for gfxFloat
20
#include "gfxUtils.h"                   // for TransformToQuad
21
#include "mozilla/gfx/BasePoint3D.h"    // for BasePoint3D
22
#include "mozilla/Sprintf.h"            // for SprintfLiteral
23
#include "nsRegion.h"                   // for nsIntRegion
24
#include "nsTArray.h"                   // for nsTArray, etc
25
#include "limits.h"
26
#include "mozilla/Assertions.h"
27
28
namespace mozilla {
29
namespace layers {
30
31
using namespace mozilla::gfx;
32
33
enum LayerSortOrder {
34
  Undefined,
35
  ABeforeB,
36
  BBeforeA,
37
};
38
39
/**
40
 * Recover the z component from a 2d transformed point by finding the intersection
41
 * of a line through the point in the z direction and the transformed plane.
42
 *
43
 * We want to solve:
44
 *
45
 * point = normal . (p0 - l0) / normal . l
46
 */
47
static gfxFloat RecoverZDepth(const Matrix4x4& aTransform, const gfxPoint& aPoint)
48
0
{
49
0
    const Point3D l(0, 0, 1);
50
0
    Point3D l0 = Point3D(aPoint.x, aPoint.y, 0);
51
0
    Point3D p0 = aTransform.TransformPoint(Point3D(0, 0, 0));
52
0
    Point3D normal = aTransform.GetNormalVector();
53
0
54
0
    gfxFloat n = normal.DotProduct(p0 - l0); 
55
0
    gfxFloat d = normal.DotProduct(l);
56
0
57
0
    if (!d) {
58
0
        return 0;
59
0
    }
60
0
61
0
    return n/d;
62
0
}
63
64
/**
65
 * Determine if this transform layer should be drawn before another when they 
66
 * are both preserve-3d children.
67
 *
68
 * We want to find the relative z depths of the 2 layers at points where they
69
 * intersect when projected onto the 2d screen plane. Intersections are defined
70
 * as corners that are positioned within the other quad, as well as intersections
71
 * of the lines.
72
 *
73
 * We then choose the intersection point with the greatest difference in Z
74
 * depths and use this point to determine an ordering for the two layers.
75
 * For layers that are intersecting in 3d space, this essentially guesses an 
76
 * order. In a lot of cases we only intersect right at the edge point (3d cubes
77
 * in particular) and this generates the 'correct' looking ordering. For planes
78
 * that truely intersect, then there is no correct ordering and this remains
79
 * unsolved without changing our rendering code.
80
 */
81
0
static LayerSortOrder CompareDepth(Layer* aOne, Layer* aTwo) {
82
0
  gfxRect ourRect = ThebesRect(aOne->GetLocalVisibleRegion().GetBounds().ToUnknownRect());
83
0
  gfxRect otherRect = ThebesRect(aTwo->GetLocalVisibleRegion().GetBounds().ToUnknownRect());
84
0
85
0
  MOZ_ASSERT(aOne->GetParent() && aOne->GetParent()->Extend3DContext() &&
86
0
             aTwo->GetParent() && aTwo->GetParent()->Extend3DContext());
87
0
  // Effective transform of leaves may had been projected to 2D.
88
0
  Matrix4x4 ourTransform =
89
0
    aOne->GetLocalTransform() * aOne->GetParent()->GetEffectiveTransform();
90
0
  Matrix4x4 otherTransform =
91
0
    aTwo->GetLocalTransform() * aTwo->GetParent()->GetEffectiveTransform();
92
0
93
0
  // Transform both rectangles and project into 2d space.
94
0
  gfxQuad ourTransformedRect = gfxUtils::TransformToQuad(ourRect, ourTransform);
95
0
  gfxQuad otherTransformedRect = gfxUtils::TransformToQuad(otherRect, otherTransform);
96
0
97
0
  gfxRect ourBounds = ourTransformedRect.GetBounds();
98
0
  gfxRect otherBounds = otherTransformedRect.GetBounds();
99
0
100
0
  if (!ourBounds.Intersects(otherBounds)) {
101
0
    return Undefined;
102
0
  }
103
0
104
0
  // Make a list of all points that are within the other rect.
105
0
  // Could we just check Contains() on the bounds rects. ie, is it possible
106
0
  // for layers to overlap without intersections (in 2d space) and yet still
107
0
  // have their bounds rects not completely enclose each other?
108
0
  nsTArray<gfxPoint> points;
109
0
  for (uint32_t i = 0; i < 4; i++) {
110
0
    if (ourTransformedRect.Contains(otherTransformedRect.mPoints[i])) {
111
0
      points.AppendElement(otherTransformedRect.mPoints[i]);
112
0
    }
113
0
    if (otherTransformedRect.Contains(ourTransformedRect.mPoints[i])) {
114
0
      points.AppendElement(ourTransformedRect.mPoints[i]);
115
0
    }
116
0
  }
117
0
  
118
0
  // Look for intersections between lines (in 2d space) and use these as
119
0
  // depth testing points.
120
0
  for (uint32_t i = 0; i < 4; i++) {
121
0
    for (uint32_t j = 0; j < 4; j++) {
122
0
      gfxPoint intersection;
123
0
      gfxLineSegment one(ourTransformedRect.mPoints[i],
124
0
                         ourTransformedRect.mPoints[(i + 1) % 4]);
125
0
      gfxLineSegment two(otherTransformedRect.mPoints[j],
126
0
                         otherTransformedRect.mPoints[(j + 1) % 4]);
127
0
      if (one.Intersects(two, intersection)) {
128
0
        points.AppendElement(intersection);
129
0
      }
130
0
    }
131
0
  }
132
0
133
0
  // No intersections, no defined order between these layers.
134
0
  if (points.IsEmpty()) {
135
0
    return Undefined;
136
0
  }
137
0
138
0
  // Find the relative Z depths of each intersection point and check that the layers are in the same order.
139
0
  gfxFloat highest = 0;
140
0
  for (uint32_t i = 0; i < points.Length(); i++) {
141
0
    gfxFloat ourDepth = RecoverZDepth(ourTransform, points.ElementAt(i));
142
0
    gfxFloat otherDepth = RecoverZDepth(otherTransform, points.ElementAt(i));
143
0
144
0
    gfxFloat difference = otherDepth - ourDepth;
145
0
146
0
    if (fabs(difference) > fabs(highest)) {
147
0
      highest = difference;
148
0
    }
149
0
  }
150
0
  // If layers have the same depth keep the original order
151
0
  if (fabs(highest) < 0.1 || highest >= 0) {
152
0
    return ABeforeB;
153
0
  } else {
154
0
    return BBeforeA;
155
0
  }
156
0
}
157
158
#ifdef DEBUG
159
// #define USE_XTERM_COLORING
160
#ifdef USE_XTERM_COLORING
161
// List of color values, which can be added to the xterm foreground offset or
162
// background offset to generate a xterm color code.
163
// NOTE: The colors that we don't explicitly use (by name) are commented out,
164
// to avoid triggering Wunused-const-variable build warnings.
165
static const int XTERM_FOREGROUND_COLOR_OFFSET = 30;
166
static const int XTERM_BACKGROUND_COLOR_OFFSET = 40;
167
static const int BLACK = 0;
168
//static const int RED = 1;
169
static const int GREEN = 2;
170
//static const int YELLOW = 3;
171
//static const int BLUE = 4;
172
//static const int MAGENTA = 5;
173
//static const int CYAN = 6;
174
//static const int WHITE = 7;
175
176
static const int RESET = 0;
177
// static const int BRIGHT = 1;
178
// static const int DIM = 2;
179
// static const int UNDERLINE = 3;
180
// static const int BLINK = 4;
181
// static const int REVERSE = 7;
182
// static const int HIDDEN = 8;
183
184
static void SetTextColor(uint32_t aColor)
185
{
186
  char command[13];
187
188
  /* Command is the control command to the terminal */
189
  SprintfLiteral(command, "%c[%d;%d;%dm", 0x1B, RESET,
190
                 aColor + XTERM_FOREGROUND_COLOR_OFFSET,
191
                 BLACK + XTERM_BACKGROUND_COLOR_OFFSET);
192
  printf("%s", command);
193
}
194
195
static void print_layer_internal(FILE* aFile, Layer* aLayer, uint32_t aColor)
196
{
197
  SetTextColor(aColor);
198
  fprintf(aFile, "%p", aLayer);
199
  SetTextColor(GREEN);
200
}
201
#else
202
203
const char *colors[] = { "Black", "Red", "Green", "Yellow", "Blue", "Magenta", "Cyan", "White" };
204
205
static void print_layer_internal(FILE* aFile, Layer* aLayer, uint32_t aColor)
206
{
207
  fprintf(aFile, "%p(%s)", aLayer, colors[aColor]);
208
}
209
#endif
210
211
static void print_layer(FILE* aFile, Layer* aLayer)
212
{
213
  print_layer_internal(aFile, aLayer, aLayer->GetDebugColorIndex());
214
}
215
216
static void DumpLayerList(nsTArray<Layer*>& aLayers)
217
{
218
  for (uint32_t i = 0; i < aLayers.Length(); i++) {
219
    print_layer(stderr, aLayers.ElementAt(i));
220
    fprintf(stderr, " ");
221
  }
222
  fprintf(stderr, "\n");
223
}
224
225
static void DumpEdgeList(DirectedGraph<Layer*>& aGraph)
226
{
227
  const nsTArray<DirectedGraph<Layer*>::Edge>& edges = aGraph.GetEdgeList();
228
  
229
  for (uint32_t i = 0; i < edges.Length(); i++) {
230
    fprintf(stderr, "From: ");
231
    print_layer(stderr, edges.ElementAt(i).mFrom);
232
    fprintf(stderr, ", To: ");
233
    print_layer(stderr, edges.ElementAt(i).mTo);
234
    fprintf(stderr, "\n");
235
  }
236
}
237
#endif
238
239
// The maximum number of layers that we will attempt to sort. Anything
240
// greater than this will be left unsorted. We should consider enabling
241
// depth buffering for the scene in this case.
242
0
#define MAX_SORTABLE_LAYERS 100
243
244
245
uint32_t gColorIndex = 1;
246
247
void SortLayersBy3DZOrder(nsTArray<Layer*>& aLayers)
248
0
{
249
0
  uint32_t nodeCount = aLayers.Length();
250
0
  if (nodeCount > MAX_SORTABLE_LAYERS) {
251
0
    return;
252
0
  }
253
0
  DirectedGraph<Layer*> graph;
254
0
255
#ifdef DEBUG
256
  if (gfxEnv::DumpLayerSortList()) {
257
    for (uint32_t i = 0; i < nodeCount; i++) {
258
      if (aLayers.ElementAt(i)->GetDebugColorIndex() == 0) {
259
        aLayers.ElementAt(i)->SetDebugColorIndex(gColorIndex++);
260
        if (gColorIndex > 7) {
261
          gColorIndex = 1;
262
        }
263
      }
264
    }
265
    fprintf(stderr, " --- Layers before sorting: --- \n");
266
    DumpLayerList(aLayers);
267
  }
268
#endif
269
270
0
  // Iterate layers and determine edges.
271
0
  for (uint32_t i = 0; i < nodeCount; i++) {
272
0
    for (uint32_t j = i + 1; j < nodeCount; j++) {
273
0
      Layer* a = aLayers.ElementAt(i);
274
0
      Layer* b = aLayers.ElementAt(j);
275
0
      LayerSortOrder order = CompareDepth(a, b);
276
0
      if (order == ABeforeB) {
277
0
        graph.AddEdge(a, b);
278
0
      } else if (order == BBeforeA) {
279
0
        graph.AddEdge(b, a);
280
0
      }
281
0
    }
282
0
  }
283
0
284
#ifdef DEBUG
285
  if (gfxEnv::DumpLayerSortList()) {
286
    fprintf(stderr, " --- Edge List: --- \n");
287
    DumpEdgeList(graph);
288
  }
289
#endif
290
291
0
  // Build a new array using the graph.
292
0
  nsTArray<Layer*> noIncoming;
293
0
  nsTArray<Layer*> sortedList;
294
0
295
0
  // Make a list of all layers with no incoming edges.
296
0
  noIncoming.AppendElements(aLayers);
297
0
  const nsTArray<DirectedGraph<Layer*>::Edge>& edges = graph.GetEdgeList();
298
0
  for (uint32_t i = 0; i < edges.Length(); i++) {
299
0
    noIncoming.RemoveElement(edges.ElementAt(i).mTo);
300
0
  }
301
0
302
0
  // Move each item without incoming edges into the sorted list,
303
0
  // and remove edges from it.
304
0
  do {
305
0
    if (!noIncoming.IsEmpty()) {
306
0
      uint32_t last = noIncoming.Length() - 1;
307
0
308
0
      Layer* layer = noIncoming.ElementAt(last);
309
0
      MOZ_ASSERT(layer); // don't let null layer pointers sneak into sortedList
310
0
311
0
      noIncoming.RemoveElementAt(last);
312
0
      sortedList.AppendElement(layer);
313
0
314
0
      nsTArray<DirectedGraph<Layer*>::Edge> outgoing;
315
0
      graph.GetEdgesFrom(layer, outgoing);
316
0
      for (uint32_t i = 0; i < outgoing.Length(); i++) {
317
0
        DirectedGraph<Layer*>::Edge edge = outgoing.ElementAt(i);
318
0
        graph.RemoveEdge(edge);
319
0
        if (!graph.NumEdgesTo(edge.mTo)) {
320
0
          // If this node also has no edges now, add it to the list
321
0
          noIncoming.AppendElement(edge.mTo);
322
0
        }
323
0
      }
324
0
    }
325
0
326
0
    // If there are no nodes without incoming edges, but there
327
0
    // are still edges, then we have a cycle.
328
0
    if (noIncoming.IsEmpty() && graph.GetEdgeCount()) {
329
0
      // Find the node with the least incoming edges.
330
0
      uint32_t minEdges = UINT_MAX;
331
0
      Layer* minNode = nullptr;
332
0
      for (uint32_t i = 0; i < aLayers.Length(); i++) {
333
0
        uint32_t edgeCount = graph.NumEdgesTo(aLayers.ElementAt(i));
334
0
        if (edgeCount && edgeCount < minEdges) {
335
0
          minEdges = edgeCount;
336
0
          minNode = aLayers.ElementAt(i);
337
0
          if (minEdges == 1) {
338
0
            break;
339
0
          }
340
0
        }
341
0
      }
342
0
343
0
      if (minNode) {
344
0
        // Remove all of them!
345
0
        graph.RemoveEdgesTo(minNode);
346
0
        noIncoming.AppendElement(minNode);
347
0
      }
348
0
    }
349
0
  } while (!noIncoming.IsEmpty());
350
0
  NS_ASSERTION(!graph.GetEdgeCount(), "Cycles detected!");
351
#ifdef DEBUG
352
  if (gfxEnv::DumpLayerSortList()) {
353
    fprintf(stderr, " --- Layers after sorting: --- \n");
354
    DumpLayerList(sortedList);
355
  }
356
#endif
357
358
0
  aLayers.Clear();
359
0
  aLayers.AppendElements(sortedList);
360
0
}
361
362
} // namespace layers
363
} // namespace mozilla