Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/gfx/layers/RotatedBuffer.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 "RotatedBuffer.h"
8
#include <sys/types.h>                  // for int32_t
9
#include <algorithm>                    // for max
10
#include "BasicImplData.h"              // for BasicImplData
11
#include "BasicLayersImpl.h"            // for ToData
12
#include "GeckoProfiler.h"              // for AUTO_PROFILER_LABEL
13
#include "Layers.h"                     // for PaintedLayer, Layer, etc
14
#include "gfxPlatform.h"                // for gfxPlatform
15
#include "gfxPrefs.h"                   // for gfxPrefs
16
#include "gfxUtils.h"                   // for gfxUtils
17
#include "mozilla/ArrayUtils.h"         // for ArrayLength
18
#include "mozilla/gfx/BasePoint.h"      // for BasePoint
19
#include "mozilla/gfx/BaseRect.h"       // for BaseRect
20
#include "mozilla/gfx/BaseSize.h"       // for BaseSize
21
#include "mozilla/gfx/Matrix.h"         // for Matrix
22
#include "mozilla/gfx/Point.h"          // for Point, IntPoint
23
#include "mozilla/gfx/Rect.h"           // for Rect, IntRect
24
#include "mozilla/gfx/Types.h"          // for ExtendMode::ExtendMode::CLAMP, etc
25
#include "mozilla/layers/ShadowLayers.h"  // for ShadowableLayer
26
#include "mozilla/layers/TextureClient.h"  // for TextureClient
27
#include "mozilla/Move.h"               // for Move
28
#include "mozilla/gfx/Point.h"          // for IntSize
29
#include "gfx2DGlue.h"
30
#include "nsLayoutUtils.h"              // for invalidation debugging
31
#include "PaintThread.h"
32
33
namespace mozilla {
34
35
using namespace gfx;
36
37
namespace layers {
38
39
void
40
BorrowDrawTarget::ReturnDrawTarget(gfx::DrawTarget*& aReturned)
41
0
{
42
0
  MOZ_ASSERT(mLoanedDrawTarget);
43
0
  MOZ_ASSERT(aReturned == mLoanedDrawTarget);
44
0
  if (mLoanedDrawTarget) {
45
0
    mLoanedDrawTarget->SetTransform(mLoanedTransform);
46
0
    mLoanedDrawTarget = nullptr;
47
0
  }
48
0
  aReturned = nullptr;
49
0
}
50
51
IntRect
52
RotatedBuffer::GetQuadrantRectangle(XSide aXSide, YSide aYSide) const
53
0
{
54
0
  // quadrantTranslation is the amount we translate the top-left
55
0
  // of the quadrant by to get coordinates relative to the layer
56
0
  IntPoint quadrantTranslation = -mBufferRotation;
57
0
  quadrantTranslation.x += aXSide == LEFT ? mBufferRect.Width() : 0;
58
0
  quadrantTranslation.y += aYSide == TOP ? mBufferRect.Height() : 0;
59
0
  return mBufferRect + quadrantTranslation;
60
0
}
61
62
Rect
63
RotatedBuffer::GetSourceRectangle(XSide aXSide, YSide aYSide) const
64
0
{
65
0
  Rect result;
66
0
  if (aXSide == LEFT) {
67
0
    result.SetBoxX(0, mBufferRotation.x);
68
0
  } else {
69
0
    result.SetBoxX(mBufferRotation.x, mBufferRect.Width());
70
0
  }
71
0
  if (aYSide == TOP) {
72
0
    result.SetBoxY(0, mBufferRotation.y);
73
0
  } else {
74
0
    result.SetBoxY(mBufferRotation.y, mBufferRect.Height());
75
0
  }
76
0
  return result;
77
0
}
78
79
void
80
RotatedBuffer::BeginCapture()
81
0
{
82
0
  RefPtr<gfx::DrawTarget> target = GetBufferTarget();
83
0
84
0
  MOZ_ASSERT(!mCapture);
85
0
  MOZ_ASSERT(target);
86
0
  mCapture = Factory::CreateCaptureDrawTargetForTarget(target, gfxPrefs::LayersOMTPCaptureLimit());
87
0
}
88
89
RefPtr<gfx::DrawTargetCapture>
90
RotatedBuffer::EndCapture()
91
0
{
92
0
  MOZ_ASSERT(mCapture);
93
0
  return std::move(mCapture);
94
0
}
95
96
/**
97
 * @param aXSide LEFT means we draw from the left side of the buffer (which
98
 * is drawn on the right side of mBufferRect). RIGHT means we draw from
99
 * the right side of the buffer (which is drawn on the left side of
100
 * mBufferRect).
101
 * @param aYSide TOP means we draw from the top side of the buffer (which
102
 * is drawn on the bottom side of mBufferRect). BOTTOM means we draw from
103
 * the bottom side of the buffer (which is drawn on the top side of
104
 * mBufferRect).
105
 */
106
void
107
RotatedBuffer::DrawBufferQuadrant(gfx::DrawTarget* aTarget,
108
                                  XSide aXSide, YSide aYSide,
109
                                  float aOpacity,
110
                                  gfx::CompositionOp aOperator,
111
                                  gfx::SourceSurface* aMask,
112
                                  const gfx::Matrix* aMaskTransform) const
113
0
{
114
0
  // The rectangle that we're going to fill. Basically we're going to
115
0
  // render the buffer at mBufferRect + quadrantTranslation to get the
116
0
  // pixels in the right place, but we're only going to paint within
117
0
  // mBufferRect
118
0
  IntRect quadrantRect = GetQuadrantRectangle(aXSide, aYSide);
119
0
  IntRect fillRect;
120
0
  if (!fillRect.IntersectRect(mBufferRect, quadrantRect))
121
0
    return;
122
0
123
0
  gfx::Point quadrantTranslation(quadrantRect.X(), quadrantRect.Y());
124
0
125
0
  RefPtr<SourceSurface> snapshot = GetBufferSource();
126
0
127
0
  if (!snapshot) {
128
0
    gfxCriticalError() << "Invalid snapshot in RotatedBuffer::DrawBufferQuadrant";
129
0
    return;
130
0
  }
131
0
132
0
  // direct2d is much slower when using OP_SOURCE so use OP_OVER and
133
0
  // (maybe) a clear instead. Normally we need to draw in a single operation
134
0
  // (to avoid flickering) but direct2d is ok since it defers rendering.
135
0
  // We should try abstract this logic in a helper when we have other use
136
0
  // cases.
137
0
  if ((aTarget->GetBackendType() == BackendType::DIRECT2D ||
138
0
       aTarget->GetBackendType() == BackendType::DIRECT2D1_1) &&
139
0
      aOperator == CompositionOp::OP_SOURCE) {
140
0
    aOperator = CompositionOp::OP_OVER;
141
0
    if (snapshot->GetFormat() == SurfaceFormat::B8G8R8A8) {
142
0
      aTarget->ClearRect(IntRectToRect(fillRect));
143
0
    }
144
0
  }
145
0
146
0
  // OP_SOURCE is unbounded in Azure, and we really don't want that behaviour here.
147
0
  // We also can't do a ClearRect+FillRect since we need the drawing to happen
148
0
  // as an atomic operation (to prevent flickering).
149
0
  // We also need this clip in the case where we have a mask, since the mask surface
150
0
  // might cover more than fillRect, but we only want to touch the pixels inside
151
0
  // fillRect.
152
0
  aTarget->PushClipRect(IntRectToRect(fillRect));
153
0
154
0
  if (aMask) {
155
0
    Matrix oldTransform = aTarget->GetTransform();
156
0
157
0
    // Transform from user -> buffer space.
158
0
    Matrix transform =
159
0
      Matrix::Translation(quadrantTranslation.x, quadrantTranslation.y);
160
0
161
0
    Matrix inverseMask = *aMaskTransform;
162
0
    inverseMask.Invert();
163
0
164
0
    transform *= oldTransform;
165
0
    transform *= inverseMask;
166
0
167
#ifdef MOZ_GFX_OPTIMIZE_MOBILE
168
    SurfacePattern source(snapshot, ExtendMode::CLAMP, transform, SamplingFilter::POINT);
169
#else
170
    SurfacePattern source(snapshot, ExtendMode::CLAMP, transform);
171
0
#endif
172
0
173
0
    aTarget->SetTransform(*aMaskTransform);
174
0
    aTarget->MaskSurface(source, aMask, Point(0, 0), DrawOptions(aOpacity, aOperator));
175
0
    aTarget->SetTransform(oldTransform);
176
0
  } else {
177
#ifdef MOZ_GFX_OPTIMIZE_MOBILE
178
    DrawSurfaceOptions options(SamplingFilter::POINT);
179
#else
180
    DrawSurfaceOptions options;
181
0
#endif
182
0
    aTarget->DrawSurface(snapshot, IntRectToRect(fillRect),
183
0
                         GetSourceRectangle(aXSide, aYSide),
184
0
                         options,
185
0
                         DrawOptions(aOpacity, aOperator));
186
0
  }
187
0
188
0
  aTarget->PopClip();
189
0
}
190
191
void
192
RotatedBuffer::DrawBufferWithRotation(gfx::DrawTarget *aTarget,
193
                                      float aOpacity,
194
                                      gfx::CompositionOp aOperator,
195
                                      gfx::SourceSurface* aMask,
196
                                      const gfx::Matrix* aMaskTransform) const
197
0
{
198
0
  AUTO_PROFILER_LABEL("RotatedBuffer::DrawBufferWithRotation", GRAPHICS);
199
0
200
0
  // See above, in Azure Repeat should always be a safe, even faster choice
201
0
  // though! Particularly on D2D Repeat should be a lot faster, need to look
202
0
  // into that. TODO[Bas]
203
0
  DrawBufferQuadrant(aTarget, LEFT, TOP, aOpacity, aOperator, aMask, aMaskTransform);
204
0
  DrawBufferQuadrant(aTarget, RIGHT, TOP, aOpacity, aOperator, aMask, aMaskTransform);
205
0
  DrawBufferQuadrant(aTarget, LEFT, BOTTOM, aOpacity, aOperator, aMask, aMaskTransform);
206
0
  DrawBufferQuadrant(aTarget, RIGHT, BOTTOM, aOpacity, aOperator,aMask, aMaskTransform);
207
0
}
208
209
bool IsClippingCheap(gfx::DrawTarget* aTarget, const nsIntRegion& aRegion)
210
0
{
211
0
  // Assume clipping is cheap if the draw target just has an integer
212
0
  // translation, and the visible region is simple.
213
0
  return !aTarget->GetTransform().HasNonIntegerTranslation() &&
214
0
         aRegion.GetNumRects() <= 1;
215
0
}
216
217
void
218
RotatedBuffer::DrawTo(PaintedLayer* aLayer,
219
                      DrawTarget* aTarget,
220
                      float aOpacity,
221
                      CompositionOp aOp,
222
                      SourceSurface* aMask,
223
                      const Matrix* aMaskTransform)
224
0
{
225
0
  bool clipped = false;
226
0
227
0
  // If the entire buffer is valid, we can just draw the whole thing,
228
0
  // no need to clip. But we'll still clip if clipping is cheap ---
229
0
  // that might let us copy a smaller region of the buffer.
230
0
  // Also clip to the visible region if we're told to.
231
0
  if (!aLayer->GetValidRegion().Contains(BufferRect()) ||
232
0
      (ToData(aLayer)->GetClipToVisibleRegion() &&
233
0
       !aLayer->GetVisibleRegion().ToUnknownRegion().Contains(BufferRect())) ||
234
0
      IsClippingCheap(aTarget, aLayer->GetLocalVisibleRegion().ToUnknownRegion())) {
235
0
    // We don't want to draw invalid stuff, so we need to clip. Might as
236
0
    // well clip to the smallest area possible --- the visible region.
237
0
    // Bug 599189 if there is a non-integer-translation transform in aTarget,
238
0
    // we might sample pixels outside GetLocalVisibleRegion(), which is wrong
239
0
    // and may cause gray lines.
240
0
    gfxUtils::ClipToRegion(aTarget, aLayer->GetLocalVisibleRegion().ToUnknownRegion());
241
0
    clipped = true;
242
0
  }
243
0
244
0
  DrawBufferWithRotation(aTarget, aOpacity, aOp, aMask, aMaskTransform);
245
0
  if (clipped) {
246
0
    aTarget->PopClip();
247
0
  }
248
0
}
249
250
void
251
RotatedBuffer::UpdateDestinationFrom(const RotatedBuffer& aSource,
252
                                     const gfx::IntRect& aUpdateRect)
253
0
{
254
0
  DrawIterator iter;
255
0
  while (DrawTarget* destDT =
256
0
    BorrowDrawTargetForQuadrantUpdate(aUpdateRect, &iter)) {
257
0
    bool isClippingCheap = IsClippingCheap(destDT, iter.mDrawRegion);
258
0
    if (isClippingCheap) {
259
0
      gfxUtils::ClipToRegion(destDT, iter.mDrawRegion);
260
0
    }
261
0
262
0
    aSource.DrawBufferWithRotation(destDT, 1.0, CompositionOp::OP_SOURCE);
263
0
    if (isClippingCheap) {
264
0
      destDT->PopClip();
265
0
    }
266
0
    ReturnDrawTarget(destDT);
267
0
  }
268
0
}
269
270
static void
271
WrapRotationAxis(int32_t* aRotationPoint, int32_t aSize)
272
0
{
273
0
  if (*aRotationPoint < 0) {
274
0
    *aRotationPoint += aSize;
275
0
  } else if (*aRotationPoint >= aSize) {
276
0
    *aRotationPoint -= aSize;
277
0
  }
278
0
}
279
280
bool
281
RotatedBuffer::Parameters::IsRotated() const
282
0
{
283
0
  return mBufferRotation != IntPoint(0,0);
284
0
}
285
286
bool
287
RotatedBuffer::Parameters::RectWrapsBuffer(const gfx::IntRect& aRect) const
288
0
{
289
0
  int32_t xBoundary = mBufferRect.XMost() - mBufferRotation.x;
290
0
  int32_t yBoundary = mBufferRect.YMost() - mBufferRotation.y;
291
0
  return (aRect.X() < xBoundary && xBoundary < aRect.XMost()) ||
292
0
         (aRect.Y() < yBoundary && yBoundary < aRect.YMost());
293
0
}
294
295
void
296
RotatedBuffer::Parameters::SetUnrotated()
297
0
{
298
0
  mBufferRotation = IntPoint(0,0);
299
0
  mDidSelfCopy = true;
300
0
}
301
302
RotatedBuffer::Parameters
303
RotatedBuffer::AdjustedParameters(const gfx::IntRect& aDestBufferRect) const
304
0
{
305
0
  IntRect keepArea;
306
0
  if (keepArea.IntersectRect(aDestBufferRect, mBufferRect)) {
307
0
    // Set mBufferRotation so that the pixels currently in mDTBuffer
308
0
    // will still be rendered in the right place when mBufferRect
309
0
    // changes to aDestBufferRect.
310
0
    IntPoint newRotation = mBufferRotation +
311
0
      (aDestBufferRect.TopLeft() - mBufferRect.TopLeft());
312
0
    WrapRotationAxis(&newRotation.x, mBufferRect.Width());
313
0
    WrapRotationAxis(&newRotation.y, mBufferRect.Height());
314
0
    NS_ASSERTION(gfx::IntRect(gfx::IntPoint(0,0), mBufferRect.Size()).Contains(newRotation),
315
0
                 "newRotation out of bounds");
316
0
317
0
    return Parameters{aDestBufferRect, newRotation};
318
0
  }
319
0
320
0
  // No pixels are going to be kept. The whole visible region
321
0
  // will be redrawn, so we don't need to copy anything, so we don't
322
0
  // set destBuffer.
323
0
  return Parameters{aDestBufferRect, IntPoint(0,0)};
324
0
}
325
326
bool
327
RotatedBuffer::UnrotateBufferTo(const Parameters& aParameters)
328
0
{
329
0
  RefPtr<gfx::DrawTarget> drawTarget = GetDrawTarget();
330
0
  MOZ_ASSERT(drawTarget && drawTarget->IsValid());
331
0
332
0
  if (mBufferRotation == IntPoint(0,0)) {
333
0
    IntRect srcRect(IntPoint(0, 0), mBufferRect.Size());
334
0
    IntPoint dest = mBufferRect.TopLeft() - aParameters.mBufferRect.TopLeft();
335
0
336
0
    drawTarget->CopyRect(srcRect, dest);
337
0
    return true;
338
0
  } else {
339
0
    return drawTarget->Unrotate(aParameters.mBufferRotation);
340
0
  }
341
0
}
342
343
void
344
RotatedBuffer::SetParameters(const RotatedBuffer::Parameters& aParameters)
345
0
{
346
0
  mBufferRect = aParameters.mBufferRect;
347
0
  mBufferRotation = aParameters.mBufferRotation;
348
0
  mDidSelfCopy = aParameters.mDidSelfCopy;
349
0
}
350
351
RotatedBuffer::ContentType
352
RotatedBuffer::GetContentType() const
353
0
{
354
0
  return ContentForFormat(GetFormat());
355
0
}
356
357
DrawTarget*
358
RotatedBuffer::BorrowDrawTargetForQuadrantUpdate(const IntRect& aBounds,
359
                                                 DrawIterator* aIter)
360
0
{
361
0
  IntRect bounds = aBounds;
362
0
  if (aIter) {
363
0
    // If an iterator was provided, then BeginPaint must have been run with
364
0
    // PAINT_CAN_DRAW_ROTATED, and the draw region might cover multiple quadrants.
365
0
    // Iterate over each of them, and return an appropriate buffer each time we find
366
0
    // one that intersects the draw region. The iterator mCount value tracks which
367
0
    // quadrants we have considered across multiple calls to this function.
368
0
    aIter->mDrawRegion.SetEmpty();
369
0
    while (aIter->mCount < 4) {
370
0
      IntRect quadrant = GetQuadrantRectangle((aIter->mCount & 1) ? LEFT : RIGHT,
371
0
        (aIter->mCount & 2) ? TOP : BOTTOM);
372
0
      aIter->mDrawRegion.And(aBounds, quadrant);
373
0
      aIter->mCount++;
374
0
      if (!aIter->mDrawRegion.IsEmpty()) {
375
0
        break;
376
0
      }
377
0
    }
378
0
    if (aIter->mDrawRegion.IsEmpty()) {
379
0
      return nullptr;
380
0
    }
381
0
    bounds = aIter->mDrawRegion.GetBounds();
382
0
  }
383
0
384
0
  MOZ_ASSERT(!mLoanedDrawTarget, "draw target has been borrowed and not returned");
385
0
  mLoanedDrawTarget = GetDrawTarget();
386
0
387
0
  // Figure out which quadrant to draw in
388
0
  int32_t xBoundary = mBufferRect.XMost() - mBufferRotation.x;
389
0
  int32_t yBoundary = mBufferRect.YMost() - mBufferRotation.y;
390
0
  XSide sideX = bounds.XMost() <= xBoundary ? RIGHT : LEFT;
391
0
  YSide sideY = bounds.YMost() <= yBoundary ? BOTTOM : TOP;
392
0
  IntRect quadrantRect = GetQuadrantRectangle(sideX, sideY);
393
0
  NS_ASSERTION(quadrantRect.Contains(bounds), "Messed up quadrants");
394
0
395
0
  mLoanedTransform = mLoanedDrawTarget->GetTransform();
396
0
  Matrix transform = Matrix(mLoanedTransform)
397
0
                          .PreTranslate(-quadrantRect.X(),
398
0
                                        -quadrantRect.Y());
399
0
  mLoanedDrawTarget->SetTransform(transform);
400
0
401
0
  return mLoanedDrawTarget;
402
0
}
403
404
gfx::SurfaceFormat
405
RemoteRotatedBuffer::GetFormat() const
406
0
{
407
0
  return mClient->GetFormat();
408
0
}
409
410
bool
411
RemoteRotatedBuffer::IsLocked()
412
0
{
413
0
  return mClient->IsLocked();
414
0
}
415
416
bool
417
RemoteRotatedBuffer::Lock(OpenMode aMode)
418
0
{
419
0
  MOZ_ASSERT(!mTarget);
420
0
  MOZ_ASSERT(!mTargetOnWhite);
421
0
422
0
  bool locked = mClient->Lock(aMode) &&
423
0
                (!mClientOnWhite || mClientOnWhite->Lock(aMode));
424
0
  if (!locked) {
425
0
    Unlock();
426
0
    return false;
427
0
  }
428
0
429
0
  mTarget = mClient->BorrowDrawTarget();
430
0
  if (!mTarget || !mTarget->IsValid()) {
431
0
    gfxCriticalNote << "Invalid draw target " << hexa(mTarget)
432
0
                    << " in RemoteRotatedBuffer::Lock";
433
0
    Unlock();
434
0
    return false;
435
0
  }
436
0
437
0
  if (mClientOnWhite) {
438
0
    mTargetOnWhite = mClientOnWhite->BorrowDrawTarget();
439
0
    if (!mTargetOnWhite || !mTargetOnWhite->IsValid()) {
440
0
      gfxCriticalNote << "Invalid draw target(s) " << hexa(mTarget)
441
0
                      << " and " << hexa(mTargetOnWhite)
442
0
                      << " in RemoteRotatedBuffer::Lock";
443
0
      Unlock();
444
0
      return false;
445
0
    }
446
0
  }
447
0
448
0
  if (mTargetOnWhite) {
449
0
    mTargetDual = Factory::CreateDualDrawTarget(mTarget, mTargetOnWhite);
450
0
451
0
    if (!mTargetDual || !mTargetDual->IsValid()) {
452
0
      gfxCriticalNote << "Invalid dual draw target " << hexa(mTargetDual)
453
0
                      << " in RemoteRotatedBuffer::Lock";
454
0
      Unlock();
455
0
      return false;
456
0
    }
457
0
  } else {
458
0
    mTargetDual = mTarget;
459
0
  }
460
0
461
0
  return true;
462
0
}
463
464
void
465
RemoteRotatedBuffer::Unlock()
466
0
{
467
0
  mTarget = nullptr;
468
0
  mTargetOnWhite = nullptr;
469
0
  mTargetDual = nullptr;
470
0
471
0
  if (mClient->IsLocked()) {
472
0
    mClient->Unlock();
473
0
  }
474
0
  if (mClientOnWhite && mClientOnWhite->IsLocked()) {
475
0
    mClientOnWhite->Unlock();
476
0
  }
477
0
}
478
479
void
480
RemoteRotatedBuffer::SyncWithObject(SyncObjectClient* aSyncObject)
481
0
{
482
0
  mClient->SyncWithObject(aSyncObject);
483
0
  if (mClientOnWhite) {
484
0
    mClientOnWhite->SyncWithObject(aSyncObject);
485
0
  }
486
0
}
487
488
void
489
RemoteRotatedBuffer::Clear()
490
0
{
491
0
  MOZ_ASSERT(!mTarget && !mTargetOnWhite);
492
0
  mClient = nullptr;
493
0
  mClientOnWhite = nullptr;
494
0
}
495
496
gfx::DrawTarget*
497
RemoteRotatedBuffer::GetBufferTarget() const
498
0
{
499
0
  return mTargetDual;
500
0
}
501
502
gfx::SurfaceFormat
503
DrawTargetRotatedBuffer::GetFormat() const
504
0
{
505
0
  return mTarget->GetFormat();
506
0
}
507
508
gfx::DrawTarget*
509
DrawTargetRotatedBuffer::GetBufferTarget() const
510
0
{
511
0
  return mTargetDual;
512
0
}
513
514
gfx::SurfaceFormat
515
SourceRotatedBuffer::GetFormat() const
516
0
{
517
0
  return mSource->GetFormat();
518
0
}
519
520
already_AddRefed<SourceSurface>
521
SourceRotatedBuffer::GetBufferSource() const
522
0
{
523
0
  RefPtr<SourceSurface> sourceDual = mSourceDual;
524
0
  return sourceDual.forget();
525
0
}
526
527
} // namespace layers
528
} // namespace mozilla
529