Source code

Revision control

Other Tools

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