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 "Layers.h"
8
#include <algorithm> // for max, min
9
#include "apz/src/AsyncPanZoomController.h"
10
#include "CompositableHost.h" // for CompositableHost
11
#include "ImageContainer.h" // for ImageContainer, etc
12
#include "ImageLayers.h" // for ImageLayer
13
#include "LayerSorter.h" // for SortLayersBy3DZOrder
14
#include "LayersLogging.h" // for AppendToString
15
#include "LayerUserData.h"
16
#include "ReadbackLayer.h" // for ReadbackLayer
17
#include "UnitTransforms.h" // for ViewAs
18
#include "gfxEnv.h"
19
#include "gfxPlatform.h" // for gfxPlatform
20
#include "gfxUtils.h" // for gfxUtils, etc
21
#include "gfx2DGlue.h"
22
#include "mozilla/DebugOnly.h" // for DebugOnly
23
#include "mozilla/IntegerPrintfMacros.h"
24
#include "mozilla/StaticPrefs_layers.h"
25
#include "mozilla/Telemetry.h" // for Accumulate
26
#include "mozilla/ToString.h"
27
#include "mozilla/gfx/2D.h" // for DrawTarget
28
#include "mozilla/gfx/BaseSize.h" // for BaseSize
29
#include "mozilla/gfx/Matrix.h" // for Matrix4x4
30
#include "mozilla/gfx/Polygon.h" // for Polygon
31
#include "mozilla/layers/AsyncCanvasRenderer.h"
32
#include "mozilla/layers/BSPTree.h" // for BSPTree
33
#include "mozilla/layers/CompositableClient.h" // for CompositableClient
34
#include "mozilla/layers/Compositor.h" // for Compositor
35
#include "mozilla/layers/CompositorTypes.h"
36
#include "mozilla/layers/LayerManagerComposite.h" // for LayerComposite
37
#include "mozilla/layers/LayerMetricsWrapper.h" // for LayerMetricsWrapper
38
#include "mozilla/layers/LayersMessages.h" // for TransformFunction, etc
39
#include "mozilla/layers/LayersTypes.h" // for TextureDumpMode
40
#include "mozilla/layers/PersistentBufferProvider.h"
41
#include "mozilla/layers/ShadowLayers.h" // for ShadowableLayer
42
#include "nsAString.h"
43
#include "nsCSSValue.h" // for nsCSSValue::Array, etc
44
#include "nsDisplayList.h" // for nsDisplayItem
45
#include "nsPrintfCString.h" // for nsPrintfCString
46
#include "protobuf/LayerScopePacket.pb.h"
47
#include "mozilla/Compression.h"
48
#include "TreeTraversal.h" // for ForEachNode
49
50
#include <list>
51
#include <set>
52
53
uint8_t gLayerManagerLayerBuilder;
54
55
namespace mozilla {
56
namespace layers {
57
58
typedef ScrollableLayerGuid::ViewID ViewID;
59
60
using namespace mozilla::gfx;
61
using namespace mozilla::Compression;
62
63
//--------------------------------------------------
64
// LayerManager
65
66
/* static */ mozilla::LogModule* LayerManager::GetLog() {
67
static LazyLogModule sLog("Layers");
68
return sLog;
69
}
70
71
ScrollableLayerGuid::ViewID LayerManager::GetRootScrollableLayerId() {
72
if (!mRoot) {
73
return ScrollableLayerGuid::NULL_SCROLL_ID;
74
}
75
76
LayerMetricsWrapper layerMetricsRoot = LayerMetricsWrapper(mRoot);
77
78
LayerMetricsWrapper rootScrollableLayerMetrics =
79
BreadthFirstSearch<ForwardIterator>(
80
layerMetricsRoot, [](LayerMetricsWrapper aLayerMetrics) {
81
return aLayerMetrics.Metrics().IsScrollable();
82
});
83
84
return rootScrollableLayerMetrics.IsValid()
85
? rootScrollableLayerMetrics.Metrics().GetScrollId()
86
: ScrollableLayerGuid::NULL_SCROLL_ID;
87
}
88
89
LayerMetricsWrapper LayerManager::GetRootContentLayer() {
90
if (!mRoot) {
91
return LayerMetricsWrapper();
92
}
93
94
LayerMetricsWrapper root(mRoot);
95
96
return BreadthFirstSearch<ForwardIterator>(
97
root, [](LayerMetricsWrapper aLayerMetrics) {
98
return aLayerMetrics.Metrics().IsRootContent();
99
});
100
}
101
102
already_AddRefed<DrawTarget> LayerManager::CreateOptimalDrawTarget(
103
const gfx::IntSize& aSize, SurfaceFormat aFormat) {
104
return gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(aSize,
105
aFormat);
106
}
107
108
already_AddRefed<DrawTarget> LayerManager::CreateOptimalMaskDrawTarget(
109
const gfx::IntSize& aSize) {
110
return CreateOptimalDrawTarget(aSize, SurfaceFormat::A8);
111
}
112
113
already_AddRefed<DrawTarget> LayerManager::CreateDrawTarget(
114
const IntSize& aSize, SurfaceFormat aFormat) {
115
return gfxPlatform::GetPlatform()->CreateOffscreenCanvasDrawTarget(aSize,
116
aFormat);
117
}
118
119
already_AddRefed<PersistentBufferProvider>
120
LayerManager::CreatePersistentBufferProvider(
121
const mozilla::gfx::IntSize& aSize, mozilla::gfx::SurfaceFormat aFormat) {
122
RefPtr<PersistentBufferProviderBasic> bufferProvider =
123
PersistentBufferProviderBasic::Create(
124
aSize, aFormat,
125
gfxPlatform::GetPlatform()->GetPreferredCanvasBackend());
126
127
if (!bufferProvider) {
128
bufferProvider = PersistentBufferProviderBasic::Create(
129
aSize, aFormat, gfxPlatform::GetPlatform()->GetFallbackCanvasBackend());
130
}
131
132
return bufferProvider.forget();
133
}
134
135
already_AddRefed<ImageContainer> LayerManager::CreateImageContainer(
136
ImageContainer::Mode flag) {
137
RefPtr<ImageContainer> container = new ImageContainer(flag);
138
return container.forget();
139
}
140
141
bool LayerManager::LayersComponentAlphaEnabled() {
142
// If MOZ_GFX_OPTIMIZE_MOBILE is defined, we force component alpha off
143
// and ignore the preference.
144
#ifdef MOZ_GFX_OPTIMIZE_MOBILE
145
return false;
146
#else
147
return StaticPrefs::
148
layers_componentalpha_enabled_AtStartup_DoNotUseDirectly();
149
#endif
150
}
151
152
bool LayerManager::AreComponentAlphaLayersEnabled() {
153
return LayerManager::LayersComponentAlphaEnabled();
154
}
155
156
/*static*/
157
void LayerManager::LayerUserDataDestroy(void* data) {
158
delete static_cast<LayerUserData*>(data);
159
}
160
161
UniquePtr<LayerUserData> LayerManager::RemoveUserData(void* aKey) {
162
UniquePtr<LayerUserData> d(static_cast<LayerUserData*>(
163
mUserData.Remove(static_cast<gfx::UserDataKey*>(aKey))));
164
return d;
165
}
166
167
void LayerManager::PayloadPresented() {
168
RecordCompositionPayloadsPresented(mPayload);
169
}
170
171
//--------------------------------------------------
172
// Layer
173
174
Layer::Layer(LayerManager* aManager, void* aImplData)
175
: mManager(aManager),
176
mParent(nullptr),
177
mNextSibling(nullptr),
178
mPrevSibling(nullptr),
179
mImplData(aImplData),
180
mUseTileSourceRect(false)
181
#ifdef DEBUG
182
,
183
mDebugColorIndex(0)
184
#endif
185
{
186
}
187
188
Layer::~Layer() = default;
189
190
void Layer::SetCompositorAnimations(
191
const CompositorAnimations& aCompositorAnimations) {
192
MOZ_LAYERS_LOG_IF_SHADOWABLE(
193
this, ("Layer::Mutated(%p) SetCompositorAnimations with id=%" PRIu64,
194
this, mAnimationInfo.GetCompositorAnimationsId()));
195
196
mAnimationInfo.SetCompositorAnimations(aCompositorAnimations);
197
198
Mutated();
199
}
200
201
void Layer::ClearCompositorAnimations() {
202
MOZ_LAYERS_LOG_IF_SHADOWABLE(
203
this, ("Layer::Mutated(%p) ClearCompositorAnimations with id=%" PRIu64,
204
this, mAnimationInfo.GetCompositorAnimationsId()));
205
206
mAnimationInfo.ClearAnimations();
207
208
Mutated();
209
}
210
211
void Layer::StartPendingAnimations(const TimeStamp& aReadyTime) {
212
ForEachNode<ForwardIterator>(this, [&aReadyTime](Layer* layer) {
213
if (layer->mAnimationInfo.StartPendingAnimations(aReadyTime)) {
214
layer->Mutated();
215
}
216
});
217
}
218
219
void Layer::SetAsyncPanZoomController(uint32_t aIndex,
220
AsyncPanZoomController* controller) {
221
MOZ_ASSERT(aIndex < GetScrollMetadataCount());
222
// We should never be setting an APZC on a non-scrollable layer
223
MOZ_ASSERT(!controller || GetFrameMetrics(aIndex).IsScrollable());
224
mApzcs[aIndex] = controller;
225
}
226
227
AsyncPanZoomController* Layer::GetAsyncPanZoomController(
228
uint32_t aIndex) const {
229
MOZ_ASSERT(aIndex < GetScrollMetadataCount());
230
#ifdef DEBUG
231
if (mApzcs[aIndex]) {
232
MOZ_ASSERT(GetFrameMetrics(aIndex).IsScrollable());
233
}
234
#endif
235
return mApzcs[aIndex];
236
}
237
238
void Layer::ScrollMetadataChanged() {
239
mApzcs.SetLength(GetScrollMetadataCount());
240
}
241
242
std::unordered_set<ScrollableLayerGuid::ViewID>
243
Layer::ApplyPendingUpdatesToSubtree() {
244
ForEachNode<ForwardIterator>(this, [](Layer* layer) {
245
layer->ApplyPendingUpdatesForThisTransaction();
246
});
247
// Once we're done recursing through the whole tree, clear the pending
248
// updates from the manager.
249
return Manager()->ClearPendingScrollInfoUpdate();
250
}
251
252
bool Layer::IsOpaqueForVisibility() {
253
return GetEffectiveOpacity() == 1.0f &&
254
GetEffectiveMixBlendMode() == CompositionOp::OP_OVER;
255
}
256
257
bool Layer::CanUseOpaqueSurface() {
258
// If the visible content in the layer is opaque, there is no need
259
// for an alpha channel.
260
if (GetContentFlags() & CONTENT_OPAQUE) return true;
261
// Also, if this layer is the bottommost layer in a container which
262
// doesn't need an alpha channel, we can use an opaque surface for this
263
// layer too. Any transparent areas must be covered by something else
264
// in the container.
265
ContainerLayer* parent = GetParent();
266
return parent && parent->GetFirstChild() == this &&
267
parent->CanUseOpaqueSurface();
268
}
269
270
// NB: eventually these methods will be defined unconditionally, and
271
// can be moved into Layers.h
272
const Maybe<ParentLayerIntRect>& Layer::GetLocalClipRect() {
273
if (HostLayer* shadow = AsHostLayer()) {
274
return shadow->GetShadowClipRect();
275
}
276
return GetClipRect();
277
}
278
279
const LayerIntRegion& Layer::GetLocalVisibleRegion() {
280
if (HostLayer* shadow = AsHostLayer()) {
281
return shadow->GetShadowVisibleRegion();
282
}
283
return GetVisibleRegion();
284
}
285
286
Matrix4x4 Layer::SnapTransformTranslation(const Matrix4x4& aTransform,
287
Matrix* aResidualTransform) {
288
if (aResidualTransform) {
289
*aResidualTransform = Matrix();
290
}
291
292
if (!mManager->IsSnappingEffectiveTransforms()) {
293
return aTransform;
294
}
295
296
Matrix matrix2D;
297
if (aTransform.CanDraw2D(&matrix2D) && !matrix2D.HasNonTranslation() &&
298
matrix2D.HasNonIntegerTranslation()) {
299
auto snappedTranslation = IntPoint::Round(matrix2D.GetTranslation());
300
Matrix snappedMatrix =
301
Matrix::Translation(snappedTranslation.x, snappedTranslation.y);
302
Matrix4x4 result = Matrix4x4::From2D(snappedMatrix);
303
if (aResidualTransform) {
304
// set aResidualTransform so that aResidual * snappedMatrix == matrix2D.
305
// (I.e., appying snappedMatrix after aResidualTransform gives the
306
// ideal transform.)
307
*aResidualTransform =
308
Matrix::Translation(matrix2D._31 - snappedTranslation.x,
309
matrix2D._32 - snappedTranslation.y);
310
}
311
return result;
312
}
313
314
return SnapTransformTranslation3D(aTransform, aResidualTransform);
315
}
316
317
Matrix4x4 Layer::SnapTransformTranslation3D(const Matrix4x4& aTransform,
318
Matrix* aResidualTransform) {
319
if (aTransform.IsSingular() || aTransform.HasPerspectiveComponent() ||
320
aTransform.HasNonTranslation() ||
321
!aTransform.HasNonIntegerTranslation()) {
322
// For a singular transform, there is no reversed matrix, so we
323
// don't snap it.
324
// For a perspective transform, the content is transformed in
325
// non-linear, so we don't snap it too.
326
return aTransform;
327
}
328
329
// Snap for 3D Transforms
330
331
Point3D transformedOrigin = aTransform.TransformPoint(Point3D());
332
333
// Compute the transformed snap by rounding the values of
334
// transformed origin.
335
auto transformedSnapXY =
336
IntPoint::Round(transformedOrigin.x, transformedOrigin.y);
337
Matrix4x4 inverse = aTransform;
338
inverse.Invert();
339
// see Matrix4x4::ProjectPoint()
340
Float transformedSnapZ =
341
inverse._33 == 0 ? 0
342
: (-(transformedSnapXY.x * inverse._13 +
343
transformedSnapXY.y * inverse._23 + inverse._43) /
344
inverse._33);
345
Point3D transformedSnap =
346
Point3D(transformedSnapXY.x, transformedSnapXY.y, transformedSnapZ);
347
if (transformedOrigin == transformedSnap) {
348
return aTransform;
349
}
350
351
// Compute the snap from the transformed snap.
352
Point3D snap = inverse.TransformPoint(transformedSnap);
353
if (snap.z > 0.001 || snap.z < -0.001) {
354
// Allow some level of accumulated computation error.
355
MOZ_ASSERT(inverse._33 == 0.0);
356
return aTransform;
357
}
358
359
// The difference between the origin and snap is the residual transform.
360
if (aResidualTransform) {
361
// The residual transform is to translate the snap to the origin
362
// of the content buffer.
363
*aResidualTransform = Matrix::Translation(-snap.x, -snap.y);
364
}
365
366
// Translate transformed origin to transformed snap since the
367
// residual transform would trnslate the snap to the origin.
368
Point3D transformedShift = transformedSnap - transformedOrigin;
369
Matrix4x4 result = aTransform;
370
result.PostTranslate(transformedShift.x, transformedShift.y,
371
transformedShift.z);
372
373
// For non-2d transform, residual translation could be more than
374
// 0.5 pixels for every axis.
375
376
return result;
377
}
378
379
Matrix4x4 Layer::SnapTransform(const Matrix4x4& aTransform,
380
const gfxRect& aSnapRect,
381
Matrix* aResidualTransform) {
382
if (aResidualTransform) {
383
*aResidualTransform = Matrix();
384
}
385
386
Matrix matrix2D;
387
Matrix4x4 result;
388
if (mManager->IsSnappingEffectiveTransforms() && aTransform.Is2D(&matrix2D) &&
389
gfxSize(1.0, 1.0) <= aSnapRect.Size() &&
390
matrix2D.PreservesAxisAlignedRectangles()) {
391
auto transformedTopLeft =
392
IntPoint::Round(matrix2D.TransformPoint(ToPoint(aSnapRect.TopLeft())));
393
auto transformedTopRight =
394
IntPoint::Round(matrix2D.TransformPoint(ToPoint(aSnapRect.TopRight())));
395
auto transformedBottomRight = IntPoint::Round(
396
matrix2D.TransformPoint(ToPoint(aSnapRect.BottomRight())));
397
398
Matrix snappedMatrix = gfxUtils::TransformRectToRect(
399
aSnapRect, transformedTopLeft, transformedTopRight,
400
transformedBottomRight);
401
402
result = Matrix4x4::From2D(snappedMatrix);
403
if (aResidualTransform && !snappedMatrix.IsSingular()) {
404
// set aResidualTransform so that aResidual * snappedMatrix == matrix2D.
405
// (i.e., appying snappedMatrix after aResidualTransform gives the
406
// ideal transform.
407
Matrix snappedMatrixInverse = snappedMatrix;
408
snappedMatrixInverse.Invert();
409
*aResidualTransform = matrix2D * snappedMatrixInverse;
410
}
411
} else {
412
result = aTransform;
413
}
414
return result;
415
}
416
417
static bool AncestorLayerMayChangeTransform(Layer* aLayer) {
418
for (Layer* l = aLayer; l; l = l->GetParent()) {
419
if (l->GetContentFlags() & Layer::CONTENT_MAY_CHANGE_TRANSFORM) {
420
return true;
421
}
422
423
if (l->GetParent() && l->GetParent()->AsRefLayer()) {
424
return false;
425
}
426
}
427
return false;
428
}
429
430
bool Layer::MayResample() {
431
Matrix transform2d;
432
return !GetEffectiveTransform().Is2D(&transform2d) ||
433
ThebesMatrix(transform2d).HasNonIntegerTranslation() ||
434
AncestorLayerMayChangeTransform(this);
435
}
436
437
RenderTargetIntRect Layer::CalculateScissorRect(
438
const RenderTargetIntRect& aCurrentScissorRect) {
439
ContainerLayer* container = GetParent();
440
ContainerLayer* containerChild = nullptr;
441
NS_ASSERTION(GetParent(), "This can't be called on the root!");
442
443
// Find the layer creating the 3D context.
444
while (container->Extend3DContext() && !container->UseIntermediateSurface()) {
445
containerChild = container;
446
container = container->GetParent();
447
MOZ_ASSERT(container);
448
}
449
450
// Find the nearest layer with a clip, or this layer.
451
// ContainerState::SetupScrollingMetadata() may install a clip on
452
// the layer.
453
Layer* clipLayer = containerChild && containerChild->GetLocalClipRect()
454
? containerChild
455
: this;
456
457
// Establish initial clip rect: it's either the one passed in, or
458
// if the parent has an intermediate surface, it's the extents of that
459
// surface.
460
RenderTargetIntRect currentClip;
461
if (container->UseIntermediateSurface()) {
462
currentClip.SizeTo(container->GetIntermediateSurfaceRect().Size());
463
} else {
464
currentClip = aCurrentScissorRect;
465
}
466
467
if (!clipLayer->GetLocalClipRect()) {
468
return currentClip;
469
}
470
471
if (GetLocalVisibleRegion().IsEmpty()) {
472
// When our visible region is empty, our parent may not have created the
473
// intermediate surface that we would require for correct clipping; however,
474
// this does not matter since we are invisible.
475
// Make sure we still compute a clip rect if we want to draw checkboarding
476
// for this layer, since we want to do this even if the layer is invisible.
477
return RenderTargetIntRect(currentClip.TopLeft(),
478
RenderTargetIntSize(0, 0));
479
}
480
481
const RenderTargetIntRect clipRect = ViewAs<RenderTargetPixel>(
482
*clipLayer->GetLocalClipRect(),
483
PixelCastJustification::RenderTargetIsParentLayerForRoot);
484
if (clipRect.IsEmpty()) {
485
// We might have a non-translation transform in the container so we can't
486
// use the code path below.
487
return RenderTargetIntRect(currentClip.TopLeft(),
488
RenderTargetIntSize(0, 0));
489
}
490
491
RenderTargetIntRect scissor = clipRect;
492
if (!container->UseIntermediateSurface()) {
493
gfx::Matrix matrix;
494
DebugOnly<bool> is2D = container->GetEffectiveTransform().Is2D(&matrix);
495
// See DefaultComputeEffectiveTransforms below
496
NS_ASSERTION(is2D && matrix.PreservesAxisAlignedRectangles(),
497
"Non preserves axis aligned transform with clipped child "
498
"should have forced intermediate surface");
499
gfx::Rect r(scissor.X(), scissor.Y(), scissor.Width(), scissor.Height());
500
gfxRect trScissor = gfx::ThebesRect(matrix.TransformBounds(r));
501
trScissor.Round();
502
IntRect tmp;
503
if (!gfxUtils::GfxRectToIntRect(trScissor, &tmp)) {
504
return RenderTargetIntRect(currentClip.TopLeft(),
505
RenderTargetIntSize(0, 0));
506
}
507
scissor = ViewAs<RenderTargetPixel>(tmp);
508
509
// Find the nearest ancestor with an intermediate surface
510
do {
511
container = container->GetParent();
512
} while (container && !container->UseIntermediateSurface());
513
}
514
515
if (container) {
516
scissor.MoveBy(-container->GetIntermediateSurfaceRect().TopLeft());
517
}
518
return currentClip.Intersect(scissor);
519
}
520
521
Maybe<ParentLayerIntRect> Layer::GetScrolledClipRect() const {
522
const Maybe<LayerClip> clip = mSimpleAttrs.GetScrolledClip();
523
return clip ? Some(clip->GetClipRect()) : Nothing();
524
}
525
526
const ScrollMetadata& Layer::GetScrollMetadata(uint32_t aIndex) const {
527
MOZ_ASSERT(aIndex < GetScrollMetadataCount());
528
return mScrollMetadata[aIndex];
529
}
530
531
const FrameMetrics& Layer::GetFrameMetrics(uint32_t aIndex) const {
532
return GetScrollMetadata(aIndex).GetMetrics();
533
}
534
535
bool Layer::HasScrollableFrameMetrics() const {
536
for (uint32_t i = 0; i < GetScrollMetadataCount(); i++) {
537
if (GetFrameMetrics(i).IsScrollable()) {
538
return true;
539
}
540
}
541
return false;
542
}
543
544
bool Layer::IsScrollableWithoutContent() const {
545
// A scrollable container layer with no children
546
return AsContainerLayer() && HasScrollableFrameMetrics() && !GetFirstChild();
547
}
548
549
Matrix4x4 Layer::GetTransform() const {
550
Matrix4x4 transform = mSimpleAttrs.GetTransform();
551
transform.PostScale(GetPostXScale(), GetPostYScale(), 1.0f);
552
if (const ContainerLayer* c = AsContainerLayer()) {
553
transform.PreScale(c->GetPreXScale(), c->GetPreYScale(), 1.0f);
554
}
555
return transform;
556
}
557
558
const CSSTransformMatrix Layer::GetTransformTyped() const {
559
return ViewAs<CSSTransformMatrix>(GetTransform());
560
}
561
562
Matrix4x4 Layer::GetLocalTransform() {
563
if (HostLayer* shadow = AsHostLayer()) {
564
return shadow->GetShadowTransform();
565
}
566
return GetTransform();
567
}
568
569
const LayerToParentLayerMatrix4x4 Layer::GetLocalTransformTyped() {
570
return ViewAs<LayerToParentLayerMatrix4x4>(GetLocalTransform());
571
}
572
573
bool Layer::IsScrollbarContainer() const {
574
const ScrollbarData& data = GetScrollbarData();
575
return (data.mScrollbarLayerType == ScrollbarLayerType::Container)
576
? data.mDirection.isSome()
577
: false;
578
}
579
580
bool Layer::HasTransformAnimation() const {
581
return mAnimationInfo.HasTransformAnimation();
582
}
583
584
void Layer::ApplyPendingUpdatesForThisTransaction() {
585
if (mPendingTransform && *mPendingTransform != mSimpleAttrs.GetTransform()) {
586
MOZ_LAYERS_LOG_IF_SHADOWABLE(
587
this, ("Layer::Mutated(%p) PendingUpdatesForThisTransaction", this));
588
mSimpleAttrs.SetTransform(*mPendingTransform);
589
MutatedSimple();
590
}
591
mPendingTransform = nullptr;
592
593
if (mAnimationInfo.ApplyPendingUpdatesForThisTransaction()) {
594
MOZ_LAYERS_LOG_IF_SHADOWABLE(
595
this, ("Layer::Mutated(%p) PendingUpdatesForThisTransaction", this));
596
Mutated();
597
}
598
599
for (size_t i = 0; i < mScrollMetadata.Length(); i++) {
600
FrameMetrics& fm = mScrollMetadata[i].GetMetrics();
601
ScrollableLayerGuid::ViewID scrollId = fm.GetScrollId();
602
Maybe<ScrollUpdateInfo> update =
603
Manager()->GetPendingScrollInfoUpdate(scrollId);
604
if (update) {
605
fm.UpdatePendingScrollInfo(update.value());
606
Mutated();
607
}
608
}
609
}
610
611
float Layer::GetLocalOpacity() {
612
float opacity = mSimpleAttrs.GetOpacity();
613
if (HostLayer* shadow = AsHostLayer()) opacity = shadow->GetShadowOpacity();
614
return std::min(std::max(opacity, 0.0f), 1.0f);
615
}
616
617
float Layer::GetEffectiveOpacity() {
618
float opacity = GetLocalOpacity();
619
for (ContainerLayer* c = GetParent(); c && !c->UseIntermediateSurface();
620
c = c->GetParent()) {
621
opacity *= c->GetLocalOpacity();
622
}
623
return opacity;
624
}
625
626
CompositionOp Layer::GetEffectiveMixBlendMode() {
627
if (mSimpleAttrs.GetMixBlendMode() != CompositionOp::OP_OVER)
628
return mSimpleAttrs.GetMixBlendMode();
629
for (ContainerLayer* c = GetParent(); c && !c->UseIntermediateSurface();
630
c = c->GetParent()) {
631
if (c->mSimpleAttrs.GetMixBlendMode() != CompositionOp::OP_OVER)
632
return c->mSimpleAttrs.GetMixBlendMode();
633
}
634
635
return mSimpleAttrs.GetMixBlendMode();
636
}
637
638
Matrix4x4 Layer::ComputeTransformToPreserve3DRoot() {
639
Matrix4x4 transform = GetLocalTransform();
640
for (Layer* layer = GetParent(); layer && layer->Extend3DContext();
641
layer = layer->GetParent()) {
642
transform = transform * layer->GetLocalTransform();
643
}
644
return transform;
645
}
646
647
void Layer::ComputeEffectiveTransformForMaskLayers(
648
const gfx::Matrix4x4& aTransformToSurface) {
649
if (GetMaskLayer()) {
650
ComputeEffectiveTransformForMaskLayer(GetMaskLayer(), aTransformToSurface);
651
}
652
for (size_t i = 0; i < GetAncestorMaskLayerCount(); i++) {
653
Layer* maskLayer = GetAncestorMaskLayerAt(i);
654
ComputeEffectiveTransformForMaskLayer(maskLayer, aTransformToSurface);
655
}
656
}
657
658
/* static */
659
void Layer::ComputeEffectiveTransformForMaskLayer(
660
Layer* aMaskLayer, const gfx::Matrix4x4& aTransformToSurface) {
661
#ifdef DEBUG
662
bool maskIs2D = aMaskLayer->GetTransform().CanDraw2D();
663
NS_ASSERTION(maskIs2D, "How did we end up with a 3D transform here?!");
664
#endif
665
// The mask layer can have an async transform applied to it in some
666
// situations, so be sure to use its GetLocalTransform() rather than
667
// its GetTransform().
668
aMaskLayer->mEffectiveTransform = aMaskLayer->SnapTransformTranslation(
669
aMaskLayer->GetLocalTransform() * aTransformToSurface, nullptr);
670
}
671
672
RenderTargetRect Layer::TransformRectToRenderTarget(const LayerIntRect& aRect) {
673
LayerRect rect(aRect);
674
RenderTargetRect quad = RenderTargetRect::FromUnknownRect(
675
GetEffectiveTransform().TransformBounds(rect.ToUnknownRect()));
676
return quad;
677
}
678
679
bool Layer::GetVisibleRegionRelativeToRootLayer(nsIntRegion& aResult,
680
IntPoint* aLayerOffset) {
681
MOZ_ASSERT(aLayerOffset, "invalid offset pointer");
682
683
if (!GetParent()) {
684
return false;
685
}
686
687
IntPoint offset;
688
aResult = GetLocalVisibleRegion().ToUnknownRegion();
689
for (Layer* layer = this; layer; layer = layer->GetParent()) {
690
gfx::Matrix matrix;
691
if (!layer->GetLocalTransform().Is2D(&matrix) || !matrix.IsTranslation()) {
692
return false;
693
}
694
695
// The offset of |layer| to its parent.
696
auto currentLayerOffset = IntPoint::Round(matrix.GetTranslation());
697
698
// Translate the accumulated visible region of |this| by the offset of
699
// |layer|.
700
aResult.MoveBy(currentLayerOffset.x, currentLayerOffset.y);
701
702
// If the parent layer clips its lower layers, clip the visible region
703
// we're accumulating.
704
if (layer->GetLocalClipRect()) {
705
aResult.AndWith(layer->GetLocalClipRect()->ToUnknownRect());
706
}
707
708
// Now we need to walk across the list of siblings for this parent layer,
709
// checking to see if any of these layer trees obscure |this|. If so,
710
// remove these areas from the visible region as well. This will pick up
711
// chrome overlays like a tab modal prompt.
712
Layer* sibling;
713
for (sibling = layer->GetNextSibling(); sibling;
714
sibling = sibling->GetNextSibling()) {
715
gfx::Matrix siblingMatrix;
716
if (!sibling->GetLocalTransform().Is2D(&siblingMatrix) ||
717
!siblingMatrix.IsTranslation()) {
718
continue;
719
}
720
721
// Retreive the translation from sibling to |layer|. The accumulated
722
// visible region is currently oriented with |layer|.
723
auto siblingOffset = IntPoint::Round(siblingMatrix.GetTranslation());
724
nsIntRegion siblingVisibleRegion(
725
sibling->GetLocalVisibleRegion().ToUnknownRegion());
726
// Translate the siblings region to |layer|'s origin.
727
siblingVisibleRegion.MoveBy(-siblingOffset.x, -siblingOffset.y);
728
// Apply the sibling's clip.
729
// Layer clip rects are not affected by the layer's transform.
730
Maybe<ParentLayerIntRect> clipRect = sibling->GetLocalClipRect();
731
if (clipRect) {
732
siblingVisibleRegion.AndWith(clipRect->ToUnknownRect());
733
}
734
// Subtract the sibling visible region from the visible region of |this|.
735
aResult.SubOut(siblingVisibleRegion);
736
}
737
738
// Keep track of the total offset for aLayerOffset. We use this in plugin
739
// positioning code.
740
offset += currentLayerOffset;
741
}
742
743
*aLayerOffset = IntPoint(offset.x, offset.y);
744
return true;
745
}
746
747
Maybe<ParentLayerIntRect> Layer::GetCombinedClipRect() const {
748
Maybe<ParentLayerIntRect> clip = GetClipRect();
749
750
clip = IntersectMaybeRects(clip, GetScrolledClipRect());
751
752
for (size_t i = 0; i < mScrollMetadata.Length(); i++) {
753
clip = IntersectMaybeRects(clip, mScrollMetadata[i].GetClipRect());
754
}
755
756
return clip;
757
}
758
759
ContainerLayer::ContainerLayer(LayerManager* aManager, void* aImplData)
760
: Layer(aManager, aImplData),
761
mFirstChild(nullptr),
762
mLastChild(nullptr),
763
mPreXScale(1.0f),
764
mPreYScale(1.0f),
765
mInheritedXScale(1.0f),
766
mInheritedYScale(1.0f),
767
mPresShellResolution(1.0f),
768
mUseIntermediateSurface(false),
769
mSupportsComponentAlphaChildren(false),
770
mMayHaveReadbackChild(false),
771
mChildrenChanged(false) {}
772
773
ContainerLayer::~ContainerLayer() = default;
774
775
bool ContainerLayer::InsertAfter(Layer* aChild, Layer* aAfter) {
776
if (aChild->Manager() != Manager()) {
777
NS_ERROR("Child has wrong manager");
778
return false;
779
}
780
if (aChild->GetParent()) {
781
NS_ERROR("aChild already in the tree");
782
return false;
783
}
784
if (aChild->GetNextSibling() || aChild->GetPrevSibling()) {
785
NS_ERROR("aChild already has siblings?");
786
return false;
787
}
788
if (aAfter &&
789
(aAfter->Manager() != Manager() || aAfter->GetParent() != this)) {
790
NS_ERROR("aAfter is not our child");
791
return false;
792
}
793
794
aChild->SetParent(this);
795
if (aAfter == mLastChild) {
796
mLastChild = aChild;
797
}
798
if (!aAfter) {
799
aChild->SetNextSibling(mFirstChild);
800
if (mFirstChild) {
801
mFirstChild->SetPrevSibling(aChild);
802
}
803
mFirstChild = aChild;
804
NS_ADDREF(aChild);
805
DidInsertChild(aChild);
806
return true;
807
}
808
809
Layer* next = aAfter->GetNextSibling();
810
aChild->SetNextSibling(next);
811
aChild->SetPrevSibling(aAfter);
812
if (next) {
813
next->SetPrevSibling(aChild);
814
}
815
aAfter->SetNextSibling(aChild);
816
NS_ADDREF(aChild);
817
DidInsertChild(aChild);
818
return true;
819
}
820
821
void ContainerLayer::RemoveAllChildren() {
822
// Optimizes "while (mFirstChild) ContainerLayer::RemoveChild(mFirstChild);"
823
Layer* current = mFirstChild;
824
825
// This is inlining DidRemoveChild() on each layer; we can skip the calls
826
// to NotifyPaintedLayerRemoved as it gets taken care of when as we call
827
// NotifyRemoved prior to removing any layers.
828
while (current) {
829
Layer* next = current->GetNextSibling();
830
if (current->GetType() == TYPE_READBACK) {
831
static_cast<ReadbackLayer*>(current)->NotifyRemoved();
832
}
833
current = next;
834
}
835
836
current = mFirstChild;
837
mFirstChild = nullptr;
838
while (current) {
839
MOZ_ASSERT(!current->GetPrevSibling());
840
841
Layer* next = current->GetNextSibling();
842
current->SetParent(nullptr);
843
current->SetNextSibling(nullptr);
844
if (next) {
845
next->SetPrevSibling(nullptr);
846
}
847
NS_RELEASE(current);
848
current = next;
849
}
850
}
851
852
// Note that ContainerLayer::RemoveAllChildren is an optimized
853
// version of this code; if you make changes to ContainerLayer::RemoveChild
854
// consider whether the matching changes need to be made to
855
// ContainerLayer::RemoveAllChildren
856
bool ContainerLayer::RemoveChild(Layer* aChild) {
857
if (aChild->Manager() != Manager()) {
858
NS_ERROR("Child has wrong manager");
859
return false;
860
}
861
if (aChild->GetParent() != this) {
862
NS_ERROR("aChild not our child");
863
return false;
864
}
865
866
Layer* prev = aChild->GetPrevSibling();
867
Layer* next = aChild->GetNextSibling();
868
if (prev) {
869
prev->SetNextSibling(next);
870
} else {
871
this->mFirstChild = next;
872
}
873
if (next) {
874
next->SetPrevSibling(prev);
875
} else {
876
this->mLastChild = prev;
877
}
878
879
aChild->SetNextSibling(nullptr);
880
aChild->SetPrevSibling(nullptr);
881
aChild->SetParent(nullptr);
882
883
this->DidRemoveChild(aChild);
884
NS_RELEASE(aChild);
885
return true;
886
}
887
888
bool ContainerLayer::RepositionChild(Layer* aChild, Layer* aAfter) {
889
if (aChild->Manager() != Manager()) {
890
NS_ERROR("Child has wrong manager");
891
return false;
892
}
893
if (aChild->GetParent() != this) {
894
NS_ERROR("aChild not our child");
895
return false;
896
}
897
if (aAfter &&
898
(aAfter->Manager() != Manager() || aAfter->GetParent() != this)) {
899
NS_ERROR("aAfter is not our child");
900
return false;
901
}
902
if (aChild == aAfter) {
903
NS_ERROR("aChild cannot be the same as aAfter");
904
return false;
905
}
906
907
Layer* prev = aChild->GetPrevSibling();
908
Layer* next = aChild->GetNextSibling();
909
if (prev == aAfter) {
910
// aChild is already in the correct position, nothing to do.
911
return true;
912
}
913
if (prev) {
914
prev->SetNextSibling(next);
915
} else {
916
mFirstChild = next;
917
}
918
if (next) {
919
next->SetPrevSibling(prev);
920
} else {
921
mLastChild = prev;
922
}
923
if (!aAfter) {
924
aChild->SetPrevSibling(nullptr);
925
aChild->SetNextSibling(mFirstChild);
926
if (mFirstChild) {
927
mFirstChild->SetPrevSibling(aChild);
928
}
929
mFirstChild = aChild;
930
return true;
931
}
932
933
Layer* afterNext = aAfter->GetNextSibling();
934
if (afterNext) {
935
afterNext->SetPrevSibling(aChild);
936
} else {
937
mLastChild = aChild;
938
}
939
aAfter->SetNextSibling(aChild);
940
aChild->SetPrevSibling(aAfter);
941
aChild->SetNextSibling(afterNext);
942
return true;
943
}
944
945
void ContainerLayer::FillSpecificAttributes(SpecificLayerAttributes& aAttrs) {
946
aAttrs = ContainerLayerAttributes(mPreXScale, mPreYScale, mInheritedXScale,
947
mInheritedYScale, mPresShellResolution);
948
}
949
950
bool ContainerLayer::Creates3DContextWithExtendingChildren() {
951
if (Extend3DContext()) {
952
return false;
953
}
954
for (Layer* child = GetFirstChild(); child; child = child->GetNextSibling()) {
955
if (child->Extend3DContext()) {
956
return true;
957
}
958
}
959
return false;
960
}
961
962
RenderTargetIntRect ContainerLayer::GetIntermediateSurfaceRect() {
963
NS_ASSERTION(mUseIntermediateSurface, "Must have intermediate surface");
964
LayerIntRect bounds = GetLocalVisibleRegion().GetBounds();
965
return RenderTargetIntRect::FromUnknownRect(bounds.ToUnknownRect());
966
}
967
968
bool ContainerLayer::HasMultipleChildren() {
969
uint32_t count = 0;
970
for (Layer* child = GetFirstChild(); child; child = child->GetNextSibling()) {
971
const Maybe<ParentLayerIntRect>& clipRect = child->GetLocalClipRect();
972
if (clipRect && clipRect->IsEmpty()) continue;
973
if (!child->Extend3DContext() && child->GetLocalVisibleRegion().IsEmpty())
974
continue;
975
++count;
976
if (count > 1) return true;
977
}
978
979
return false;
980
}
981
982
/**
983
* Collect all leaf descendants of the current 3D context.
984
*/
985
void ContainerLayer::Collect3DContextLeaves(nsTArray<Layer*>& aToSort) {
986
ForEachNode<ForwardIterator>((Layer*)this, [this, &aToSort](Layer* layer) {
987
ContainerLayer* container = layer->AsContainerLayer();
988
if (layer == this || (container && container->Extend3DContext() &&
989
!container->UseIntermediateSurface())) {
990
return TraversalFlag::Continue;
991
}
992
aToSort.AppendElement(layer);
993
return TraversalFlag::Skip;
994
});
995
}
996
997
static nsTArray<LayerPolygon> SortLayersWithBSPTree(nsTArray<Layer*>& aArray) {
998
std::list<LayerPolygon> inputLayers;
999
1000
// Build a list of polygons to be sorted.
1001
for (Layer* layer : aArray) {
1002
// Ignore invisible layers.
1003
if (!layer->IsVisible()) {
1004
continue;
1005
}
1006
1007
const gfx::IntRect& bounds =
1008
layer->GetLocalVisibleRegion().GetBounds().ToUnknownRect();
1009
1010
const gfx::Matrix4x4& transform = layer->GetEffectiveTransform();
1011
1012
if (transform.IsSingular()) {
1013
// Transform cannot be inverted.
1014
continue;
1015
}
1016
1017
gfx::Polygon polygon = gfx::Polygon::FromRect(gfx::Rect(bounds));
1018
1019
// Transform the polygon to screen space.
1020
polygon.TransformToScreenSpace(transform);
1021
1022
if (polygon.GetPoints().Length() >= 3) {
1023
inputLayers.push_back(LayerPolygon(layer, std::move(polygon)));
1024
}
1025
}
1026
1027
if (inputLayers.empty()) {
1028
return nsTArray<LayerPolygon>();
1029
}
1030
1031
// Build a BSP tree from the list of polygons.
1032
BSPTree tree(inputLayers);
1033
1034
nsTArray<LayerPolygon> orderedLayers(tree.GetDrawOrder());
1035
1036
// Transform the polygons back to layer space.
1037
for (LayerPolygon& layerPolygon : orderedLayers) {
1038
gfx::Matrix4x4 inverse =
1039
layerPolygon.layer->GetEffectiveTransform().Inverse();
1040
1041
MOZ_ASSERT(layerPolygon.geometry);
1042
layerPolygon.geometry->TransformToLayerSpace(inverse);
1043
}
1044
1045
return orderedLayers;
1046
}
1047
1048
static nsTArray<LayerPolygon> StripLayerGeometry(
1049
const nsTArray<LayerPolygon>& aLayers) {
1050
nsTArray<LayerPolygon> layers;
1051
std::set<Layer*> uniqueLayers;
1052
1053
for (const LayerPolygon& layerPolygon : aLayers) {
1054
auto result = uniqueLayers.insert(layerPolygon.layer);
1055
1056
if (result.second) {
1057
// Layer was added to the set.
1058
layers.AppendElement(LayerPolygon(layerPolygon.layer));
1059
}
1060
}
1061
1062
return layers;
1063
}
1064
1065
nsTArray<LayerPolygon> ContainerLayer::SortChildrenBy3DZOrder(
1066
SortMode aSortMode) {
1067
AutoTArray<Layer*, 10> toSort;
1068
nsTArray<LayerPolygon> drawOrder;
1069
1070
for (Layer* layer = GetFirstChild(); layer; layer = layer->GetNextSibling()) {
1071
ContainerLayer* container = layer->AsContainerLayer();
1072
1073
if (container && container->Extend3DContext() &&
1074
!container->UseIntermediateSurface()) {
1075
// Collect 3D layers in toSort array.
1076
container->Collect3DContextLeaves(toSort);
1077
1078
// Sort the 3D layers.
1079
if (toSort.Length() > 0) {
1080
nsTArray<LayerPolygon> sorted = SortLayersWithBSPTree(toSort);
1081
drawOrder.AppendElements(std::move(sorted));
1082
1083
toSort.ClearAndRetainStorage();
1084
}
1085
1086
continue;
1087
}
1088
1089
drawOrder.AppendElement(LayerPolygon(layer));
1090
}
1091
1092
if (aSortMode == SortMode::WITHOUT_GEOMETRY) {
1093
// Compositor does not support arbitrary layers, strip the layer geometry
1094
// and duplicate layers.
1095
return StripLayerGeometry(drawOrder);
1096
}
1097
1098
return drawOrder;
1099
}
1100
1101
bool ContainerLayer::AnyAncestorOrThisIs3DContextLeaf() {
1102
Layer* parent = this;
1103
while (parent != nullptr) {
1104
if (parent->Is3DContextLeaf()) {
1105
return true;
1106
}
1107
1108
parent = parent->GetParent();
1109
}
1110
1111
return false;
1112
}
1113
1114
void ContainerLayer::DefaultComputeEffectiveTransforms(
1115
const Matrix4x4& aTransformToSurface) {
1116
Matrix residual;
1117
Matrix4x4 idealTransform = GetLocalTransform() * aTransformToSurface;
1118
1119
// Keep 3D transforms for leaves to keep z-order sorting correct.
1120
if (!Extend3DContext() && !Is3DContextLeaf()) {
1121
idealTransform.ProjectTo2D();
1122
}
1123
1124
bool useIntermediateSurface;
1125
if (HasMaskLayers() || GetForceIsolatedGroup()) {
1126
useIntermediateSurface = true;
1127
#ifdef MOZ_DUMP_PAINTING
1128
} else if (gfxEnv::DumpPaintIntermediate() && !Extend3DContext()) {
1129
useIntermediateSurface = true;
1130
#endif
1131
} else {
1132
/* Don't use an intermediate surface for opacity when it's within a 3d
1133
* context, since we'd rather keep the 3d effects. This matches the
1134
* WebKit/blink behaviour, but is changing in the latest spec.
1135
*/
1136
float opacity = GetEffectiveOpacity();
1137
CompositionOp blendMode = GetEffectiveMixBlendMode();
1138
if ((HasMultipleChildren() || Creates3DContextWithExtendingChildren()) &&
1139
((opacity != 1.0f && !Extend3DContext()) ||
1140
(blendMode != CompositionOp::OP_OVER))) {
1141
useIntermediateSurface = true;
1142
} else if ((!idealTransform.Is2D() || AnyAncestorOrThisIs3DContextLeaf()) &&
1143
Creates3DContextWithExtendingChildren()) {
1144
useIntermediateSurface = true;
1145
} else if (blendMode != CompositionOp::OP_OVER &&
1146
Manager()->BlendingRequiresIntermediateSurface()) {
1147
useIntermediateSurface = true;
1148
} else {
1149
useIntermediateSurface = false;
1150
gfx::Matrix contTransform;
1151
bool checkClipRect = false;
1152
bool checkMaskLayers = false;
1153
1154
if (!idealTransform.Is2D(&contTransform)) {
1155
// In 3D case, always check if we should use IntermediateSurface.
1156
checkClipRect = true;
1157
checkMaskLayers = true;
1158
} else {
1159
#ifdef MOZ_GFX_OPTIMIZE_MOBILE
1160
if (!contTransform.PreservesAxisAlignedRectangles()) {
1161
#else
1162
if (gfx::ThebesMatrix(contTransform).HasNonIntegerTranslation()) {
1163
#endif
1164
checkClipRect = true;
1165
}
1166
/* In 2D case, only translation and/or positive scaling can be done w/o
1167
* using IntermediateSurface. Otherwise, when rotation or flip happen,
1168
* we should check whether to use IntermediateSurface.
1169
*/
1170
if (contTransform.HasNonAxisAlignedTransform() ||
1171
contTransform.HasNegativeScaling()) {
1172
checkMaskLayers = true;
1173
}
1174
}
1175
1176
if (checkClipRect || checkMaskLayers) {
1177
for (Layer* child = GetFirstChild(); child;
1178
child = child->GetNextSibling()) {
1179
const Maybe<ParentLayerIntRect>& clipRect = child->GetLocalClipRect();
1180
/* We can't (easily) forward our transform to children with a
1181
* non-empty clip rect since it would need to be adjusted for the
1182
* transform. See the calculations performed by CalculateScissorRect
1183
* above. Nor for a child with a mask layer.
1184
*/
1185
if (checkClipRect && (clipRect && !clipRect->IsEmpty() &&
1186
(child->Extend3DContext() ||
1187
!child->GetLocalVisibleRegion().IsEmpty()))) {
1188
useIntermediateSurface = true;
1189
break;
1190
}
1191
if (checkMaskLayers && child->HasMaskLayers()) {
1192
useIntermediateSurface = true;
1193
break;
1194
}
1195
}
1196
}
1197
}
1198
}
1199
1200
NS_ASSERTION(!Extend3DContext() || !useIntermediateSurface,
1201
"Can't have an intermediate surface with preserve-3d!");
1202
1203
if (useIntermediateSurface) {
1204
mEffectiveTransform = SnapTransformTranslation(idealTransform, &residual);
1205
} else {
1206
mEffectiveTransform = idealTransform;
1207
}
1208
1209
// For layers extending 3d context, its ideal transform should be
1210
// applied on children.
1211
if (!Extend3DContext()) {
1212
// Without this projection, non-container children would get a 3D
1213
// transform while 2D is expected.
1214
idealTransform.ProjectTo2D();
1215
}
1216
mUseIntermediateSurface = useIntermediateSurface;
1217
if (useIntermediateSurface) {
1218
ComputeEffectiveTransformsForChildren(Matrix4x4::From2D(residual));
1219
} else {
1220
ComputeEffectiveTransformsForChildren(idealTransform);
1221
}
1222
1223
ComputeEffectiveTransformForMaskLayers(aTransformToSurface);
1224
}
1225
1226
void ContainerLayer::DefaultComputeSupportsComponentAlphaChildren(
1227
bool* aNeedsSurfaceCopy) {
1228
if (!(GetContentFlags() & Layer::CONTENT_COMPONENT_ALPHA_DESCENDANT) ||
1229
!Manager()->AreComponentAlphaLayersEnabled()) {
1230
mSupportsComponentAlphaChildren = false;
1231
if (aNeedsSurfaceCopy) {
1232
*aNeedsSurfaceCopy = false;
1233
}
1234
return;
1235
}
1236
1237
mSupportsComponentAlphaChildren = false;
1238
bool needsSurfaceCopy = false;
1239
CompositionOp blendMode = GetEffectiveMixBlendMode();
1240
if (UseIntermediateSurface()) {
1241
if (GetLocalVisibleRegion().GetNumRects() == 1 &&
1242
(GetContentFlags() & Layer::CONTENT_OPAQUE)) {
1243
mSupportsComponentAlphaChildren = true;
1244
} else {
1245
gfx::Matrix transform;
1246
if (HasOpaqueAncestorLayer(this) &&
1247
GetEffectiveTransform().Is2D(&transform) &&
1248
!gfx::ThebesMatrix(transform).HasNonIntegerTranslation() &&
1249
blendMode == gfx::CompositionOp::OP_OVER) {
1250
mSupportsComponentAlphaChildren = true;
1251
needsSurfaceCopy = true;
1252
}
1253
}
1254
} else if (blendMode == gfx::CompositionOp::OP_OVER) {
1255
mSupportsComponentAlphaChildren =
1256
(GetContentFlags() & Layer::CONTENT_OPAQUE) ||
1257
(GetParent() && GetParent()->SupportsComponentAlphaChildren());
1258
}
1259
1260
if (aNeedsSurfaceCopy) {
1261
*aNeedsSurfaceCopy = mSupportsComponentAlphaChildren && needsSurfaceCopy;
1262
}
1263
}
1264
1265
void ContainerLayer::ComputeEffectiveTransformsForChildren(
1266
const Matrix4x4& aTransformToSurface) {
1267
for (Layer* l = mFirstChild; l; l = l->GetNextSibling()) {
1268
l->ComputeEffectiveTransforms(aTransformToSurface);
1269
}
1270
}
1271
1272
/* static */
1273
bool ContainerLayer::HasOpaqueAncestorLayer(Layer* aLayer) {
1274
for (Layer* l = aLayer->GetParent(); l; l = l->GetParent()) {
1275
if (l->GetContentFlags() & Layer::CONTENT_OPAQUE) return true;
1276
}
1277
return false;
1278
}
1279
1280
// Note that ContainerLayer::RemoveAllChildren contains an optimized
1281
// version of this code; if you make changes to ContainerLayer::DidRemoveChild
1282
// consider whether the matching changes need to be made to
1283
// ContainerLayer::RemoveAllChildren
1284
void ContainerLayer::DidRemoveChild(Layer* aLayer) {
1285
PaintedLayer* tl = aLayer->AsPaintedLayer();
1286
if (tl && tl->UsedForReadback()) {
1287
for (Layer* l = mFirstChild; l; l = l->GetNextSibling()) {
1288
if (l->GetType() == TYPE_READBACK) {
1289
static_cast<ReadbackLayer*>(l)->NotifyPaintedLayerRemoved(tl);
1290
}
1291
}
1292
}
1293
if (aLayer->GetType() == TYPE_READBACK) {
1294
static_cast<ReadbackLayer*>(aLayer)->NotifyRemoved();
1295
}
1296
}
1297
1298
void ContainerLayer::DidInsertChild(Layer* aLayer) {
1299
if (aLayer->GetType() == TYPE_READBACK) {
1300
mMayHaveReadbackChild = true;
1301
}
1302
}
1303
1304
void RefLayer::FillSpecificAttributes(SpecificLayerAttributes& aAttrs) {
1305
aAttrs = RefLayerAttributes(GetReferentId(), mEventRegionsOverride,
1306
mRemoteDocumentSize);
1307
}
1308
1309
/**
1310
* StartFrameTimeRecording, together with StopFrameTimeRecording
1311
* enable recording of frame intervals.
1312
*
1313
* To allow concurrent consumers, a cyclic array is used which serves all
1314
* consumers, practically stateless with regard to consumers.
1315
*
1316
* To save resources, the buffer is allocated on first call to
1317
* StartFrameTimeRecording and recording is paused if no consumer which called
1318
* StartFrameTimeRecording is able to get valid results (because the cyclic
1319
* buffer was overwritten since that call).
1320
*
1321
* To determine availability of the data upon StopFrameTimeRecording:
1322
* - mRecording.mNextIndex increases on each RecordFrame, and never resets.
1323
* - Cyclic buffer position is realized as mNextIndex % bufferSize.
1324
* - StartFrameTimeRecording returns mNextIndex. When StopFrameTimeRecording is
1325
* called, the required start index is passed as an arg, and we're able to
1326
* calculate the required length. If this length is bigger than bufferSize, it
1327
* means data was overwritten. otherwise, we can return the entire sequence.
1328
* - To determine if we need to pause, mLatestStartIndex is updated to
1329
* mNextIndex on each call to StartFrameTimeRecording. If this index gets
1330
* overwritten, it means that all earlier start indices obtained via
1331
* StartFrameTimeRecording were also overwritten, hence, no point in
1332
* recording, so pause.
1333
* - mCurrentRunStartIndex indicates the oldest index of the recording after
1334
* which the recording was not paused. If StopFrameTimeRecording is invoked
1335
* with a start index older than this, it means that some frames were not
1336
* recorded, so data is invalid.
1337
*/
1338
uint32_t FrameRecorder::StartFrameTimeRecording(int32_t aBufferSize) {
1339
if (mRecording.mIsPaused) {
1340
mRecording.mIsPaused = false;
1341
1342
if (!mRecording.mIntervals.Length()) { // Initialize recording buffers
1343
mRecording.mIntervals.SetLength(aBufferSize);
1344
}
1345
1346
// After being paused, recent values got invalid. Update them to now.
1347
mRecording.mLastFrameTime = TimeStamp::Now();
1348
1349
// Any recording which started before this is invalid, since we were paused.
1350
mRecording.mCurrentRunStartIndex = mRecording.mNextIndex;
1351
}
1352
1353
// If we'll overwrite this index, there are no more consumers with aStartIndex
1354
// for which we're able to provide the full recording, so no point in keep
1355
// recording.
1356
mRecording.mLatestStartIndex = mRecording.mNextIndex;
1357
return mRecording.mNextIndex;
1358
}
1359
1360
void FrameRecorder::RecordFrame() {
1361
if (!mRecording.mIsPaused) {
1362
TimeStamp now = TimeStamp::Now();
1363
uint32_t i = mRecording.mNextIndex % mRecording.mIntervals.Length();
1364
mRecording.mIntervals[i] =
1365
static_cast<float>((now - mRecording.mLastFrameTime).ToMilliseconds());
1366
mRecording.mNextIndex++;
1367
mRecording.mLastFrameTime = now;
1368
1369
if (mRecording.mNextIndex >
1370
(mRecording.mLatestStartIndex + mRecording.mIntervals.Length())) {
1371
// We've just overwritten the most recent recording start -> pause.
1372
mRecording.mIsPaused = true;
1373
}
1374
}
1375
}
1376
1377
void FrameRecorder::StopFrameTimeRecording(uint32_t aStartIndex,
1378
nsTArray<float>& aFrameIntervals) {
1379
uint32_t bufferSize = mRecording.mIntervals.Length();
1380
uint32_t length = mRecording.mNextIndex - aStartIndex;
1381
if (mRecording.mIsPaused || length > bufferSize ||
1382
aStartIndex < mRecording.mCurrentRunStartIndex) {
1383
// aStartIndex is too old. Also if aStartIndex was issued before
1384
// mRecordingNextIndex overflowed (uint32_t)
1385
// and stopped after the overflow (would happen once every 828 days of
1386
// constant 60fps).
1387
length = 0;
1388
}
1389
1390
if (!length) {
1391
aFrameIntervals.Clear();
1392
return; // empty recording, return empty arrays.
1393
}
1394
// Set length in advance to avoid possibly repeated reallocations
1395
aFrameIntervals.SetLength(length);
1396
1397
uint32_t cyclicPos = aStartIndex % bufferSize;
1398
for (uint32_t i = 0; i < length; i++, cyclicPos++) {
1399
if (cyclicPos == bufferSize) {
1400
cyclicPos = 0;
1401
}
1402
aFrameIntervals[i] = mRecording.mIntervals[cyclicPos];
1403
}
1404
}
1405
1406
static void PrintInfo(std::stringstream& aStream, HostLayer* aLayerComposite);
1407
1408
#ifdef MOZ_DUMP_PAINTING
1409
template <typename T>
1410
void WriteSnapshotToDumpFile_internal(T* aObj, DataSourceSurface* aSurf) {
1411
nsCString string(aObj->Name());
1412
string.Append('-');
1413
string.AppendInt((uint64_t)aObj);
1414
if (gfxUtils::sDumpPaintFile != stderr) {
1415
fprintf_stderr(gfxUtils::sDumpPaintFile, R"(array["%s"]=")",
1416
string.BeginReading());
1417
}
1418
gfxUtils::DumpAsDataURI(aSurf, gfxUtils::sDumpPaintFile);
1419
if (gfxUtils::sDumpPaintFile != stderr) {
1420
fprintf_stderr(gfxUtils::sDumpPaintFile, R"(";)");
1421
}
1422
}
1423
1424
void WriteSnapshotToDumpFile(Layer* aLayer, DataSourceSurface* aSurf) {
1425
WriteSnapshotToDumpFile_internal(aLayer, aSurf);
1426
}
1427
1428
void WriteSnapshotToDumpFile(LayerManager* aManager, DataSourceSurface* aSurf) {
1429
WriteSnapshotToDumpFile_internal(aManager, aSurf);
1430
}
1431
1432
void WriteSnapshotToDumpFile(Compositor* aCompositor, DrawTarget* aTarget) {
1433
RefPtr<SourceSurface> surf = aTarget->Snapshot();
1434
RefPtr<DataSourceSurface> dSurf = surf->GetDataSurface();
1435
WriteSnapshotToDumpFile_internal(aCompositor, dSurf);
1436
}
1437
#endif
1438
1439
void Layer::Dump(std::stringstream& aStream, const char* aPrefix,
1440
bool aDumpHtml, bool aSorted,
1441
const Maybe<gfx::Polygon>& aGeometry) {
1442
#ifdef MOZ_DUMP_PAINTING
1443
bool dumpCompositorTexture = gfxEnv::DumpCompositorTextures() &&
1444
AsHostLayer() &&
1445
AsHostLayer()->GetCompositableHost();
1446
bool dumpClientTexture = gfxEnv::DumpPaint() && AsShadowableLayer() &&
1447
AsShadowableLayer()->GetCompositableClient();
1448
nsCString layerId(Name());
1449
layerId.Append('-');
1450
layerId.AppendInt((uint64_t)this);
1451
#endif
1452
if (aDumpHtml) {
1453
aStream << nsPrintfCString(R"(<li><a id="%p" )", this).get();
1454
#ifdef MOZ_DUMP_PAINTING
1455
if (dumpCompositorTexture || dumpClientTexture) {
1456
aStream << nsPrintfCString(R"lit(href="javascript:ViewImage('%s')")lit",
1457
layerId.BeginReading())
1458
.get();
1459
}
1460
#endif
1461
aStream << ">";
1462
}
1463
DumpSelf(aStream, aPrefix, aGeometry);
1464
1465
#ifdef MOZ_DUMP_PAINTING
1466
if (dumpCompositorTexture) {
1467
AsHostLayer()->GetCompositableHost()->Dump(aStream, aPrefix, aDumpHtml);
1468
} else if (dumpClientTexture) {
1469
if (aDumpHtml) {
1470
aStream << nsPrintfCString(R"(<script>array["%s"]=")",
1471
layerId.BeginReading())
1472
.get();
1473
}
1474
AsShadowableLayer()->GetCompositableClient()->Dump(
1475
aStream, aPrefix, aDumpHtml, TextureDumpMode::DoNotCompress);
1476
if (aDumpHtml) {
1477
aStream << R"(";</script>)";
1478
}
1479
}
1480
#endif
1481
1482
if (aDumpHtml) {
1483
aStream << "</a>";
1484
#ifdef MOZ_DUMP_PAINTING
1485
if (dumpClientTexture) {
1486
aStream << nsPrintfCString("<br><img id=\"%s\">\n",
1487
layerId.BeginReading())
1488
.get();
1489
}
1490
#endif
1491
}
1492
1493
if (Layer* mask = GetMaskLayer()) {
1494
aStream << nsPrintfCString("%s Mask layer:\n", aPrefix).get();
1495
nsAutoCString pfx(aPrefix);
1496
pfx += " ";
1497
mask->Dump(aStream, pfx.get(), aDumpHtml);
1498
}
1499
1500
for (size_t i = 0; i < GetAncestorMaskLayerCount(); i++) {
1501
aStream << nsPrintfCString("%s Ancestor mask layer %d:\n", aPrefix,
1502
uint32_t(i))
1503
.get();
1504
nsAutoCString pfx(aPrefix);
1505
pfx += " ";
1506
GetAncestorMaskLayerAt(i)->Dump(aStream, pfx.get(), aDumpHtml);
1507
}
1508
1509
#ifdef MOZ_DUMP_PAINTING
1510
for (size_t i = 0; i < mExtraDumpInfo.Length(); i++) {
1511
const nsCString& str = mExtraDumpInfo[i];
1512
aStream << aPrefix << " Info:\n" << str.get();
1513
}
1514
#endif
1515
1516
if (ContainerLayer* container = AsContainerLayer()) {
1517
nsTArray<LayerPolygon> children;
1518
if (aSorted) {
1519
children = container->SortChildrenBy3DZOrder(
1520
ContainerLayer::SortMode::WITH_GEOMETRY);
1521
} else {
1522
for (Layer* l = container->GetFirstChild(); l; l = l->GetNextSibling()) {
1523
children.AppendElement(LayerPolygon(l));
1524
}
1525
}
1526
nsAutoCString pfx(aPrefix);
1527
pfx += " ";
1528
if (aDumpHtml) {
1529
aStream << "<ul>";
1530
}
1531
1532
for (LayerPolygon& child : children) {
1533
child.layer->Dump(aStream, pfx.get(), aDumpHtml, aSorted, child.geometry);
1534
}
1535
1536
if (aDumpHtml) {
1537
aStream << "</ul>";
1538
}
1539
}
1540
1541
if (aDumpHtml) {
1542
aStream << "</li>";
1543
}
1544
}
1545
1546
static void DumpGeometry(std::stringstream& aStream,
1547
const Maybe<gfx::Polygon>& aGeometry) {
1548
aStream << " [geometry=[";
1549
1550
const nsTArray<gfx::Point4D>& points = aGeometry->GetPoints();
1551
for (size_t i = 0; i < points.Length(); ++i) {
1552
const gfx::IntPoint point = TruncatedToInt(points[i].As2DPoint());
1553
const char* sfx = (i != points.Length() - 1) ? "," : "";
1554
AppendToString(aStream, point, "", sfx);
1555
}
1556
1557
aStream << "]]";
1558
}
1559
1560
void Layer::DumpSelf(std::stringstream& aStream, const char* aPrefix,
1561
const Maybe<gfx::Polygon>& aGeometry) {
1562
PrintInfo(aStream, aPrefix);
1563
1564
if (aGeometry) {
1565
DumpGeometry(aStream, aGeometry);
1566
}
1567
1568
aStream << "\n";
1569
}
1570
1571
void Layer::Dump(layerscope::LayersPacket* aPacket, const void* aParent) {
1572
DumpPacket(aPacket, aParent);
1573
1574
if (Layer* kid = GetFirstChild()) {
1575
kid->Dump(aPacket, this);
1576
}
1577
1578
if (Layer* next = GetNextSibling()) {
1579
next->Dump(aPacket, aParent);
1580
}
1581
}
1582
1583
void Layer::SetDisplayListLog(const char* log) {
1584
if (gfxUtils::DumpDisplayList()) {
1585
mDisplayListLog = log;
1586
}
1587
}
1588
1589
void Layer::GetDisplayListLog(nsCString& log) {
1590
log.SetLength(0);
1591
1592
if (gfxUtils::DumpDisplayList()) {
1593
// This function returns a plain text string which consists of two things
1594
// 1. DisplayList log.
1595
// 2. Memory address of this layer.
1596
// We know the target layer of each display item by information in #1.
1597
// Here is an example of a Text display item line log in #1
1598
// Text p=0xa9850c00 f=0x0xaa405b00(.....
1599
// f keeps the address of the target client layer of a display item.
1600
// For LayerScope, display-item-to-client-layer mapping is not enough since
1601
// LayerScope, which lives in the chrome process, knows only composite
1602
// layers. As so, we need display-item-to-client-layer-to-layer-composite
1603
// mapping. That's the reason we insert #2 into the log
1604
log.AppendPrintf("0x%p\n%s", (void*)this, mDisplayListLog.get());
1605
}
1606
}
1607
1608
void Layer::Log(const char* aPrefix) {
1609
if (!IsLogEnabled()) return;
1610
1611
LogSelf(aPrefix);
1612
1613
if (Layer* kid = GetFirstChild()) {
1614
nsAutoCString pfx(aPrefix);
1615
pfx += " ";
1616
kid->Log(pfx.get());
1617
}
1618
1619
if (Layer* next = GetNextSibling()) next->Log(aPrefix);
1620
}
1621
1622
void Layer::LogSelf(const char* aPrefix) {
1623
if (!IsLogEnabled()) return;
1624
1625
std::stringstream ss;
1626
PrintInfo(ss, aPrefix);
1627
MOZ_LAYERS_LOG(("%s", ss.str().c_str()));
1628
1629
if (mMaskLayer) {
1630
nsAutoCString pfx(aPrefix);
1631
pfx += R"( \ MaskLayer )";
1632
mMaskLayer->LogSelf(pfx.get());
1633
}
1634
}
1635
1636
void Layer::PrintInfo(std::stringstream& aStream, const char* aPrefix) {
1637
aStream << aPrefix;
1638
aStream
1639
<< nsPrintfCString("%s%s (0x%p)", mManager->Name(), Name(), this).get();
1640
1641
layers::PrintInfo(aStream, AsHostLayer());
1642
1643
if (mClipRect) {
1644
AppendToString(aStream, *mClipRect, " [clip=", "]");
1645
}
1646
if (mSimpleAttrs.GetScrolledClip()) {
1647
AppendToString(aStream, mSimpleAttrs.GetScrolledClip()->GetClipRect(),
1648
" [scrolled-clip=", "]");
1649
if (const Maybe<size_t>& ix =
1650
mSimpleAttrs.GetScrolledClip()->GetMaskLayerIndex()) {
1651
AppendToString(aStream, ix.value(), " [scrolled-mask=", "]");
1652
}
1653
}
1654
if (1.0 != mSimpleAttrs.GetPostXScale() ||
1655
1.0 != mSimpleAttrs.GetPostYScale()) {
1656
aStream << nsPrintfCString(" [postScale=%g, %g]",
1657
mSimpleAttrs.GetPostXScale(),
1658
mSimpleAttrs.GetPostYScale())
1659
.get();
1660
}
1661
if (!GetBaseTransform().IsIdentity()) {
1662
AppendToString(aStream, GetBaseTransform(), " [transform=", "]");
1663
}
1664
if (!GetEffectiveTransform().IsIdentity()) {
1665
AppendToString(aStream, GetEffectiveTransform(),
1666
" [effective-transform=", "]");
1667
}
1668
if (GetTransformIsPerspective()) {
1669
aStream << " [perspective]";
1670
}
1671
if (!mVisibleRegion.IsEmpty()) {
1672
AppendToString(aStream, mVisibleRegion.ToUnknownRegion(),
1673
" [visible=", "]");
1674
} else {
1675
aStream << " [not visible]";
1676
}
1677
if (!mEventRegions.IsEmpty()) {
1678
AppendToString(aStream, mEventRegions, " ", "");
1679
}
1680
if (1.0 != GetOpacity()) {
1681
aStream << nsPrintfCString(" [opacity=%g]", GetOpacity()).get();
1682
}
1683
if (IsOpaque()) {
1684
aStream << " [opaqueContent]";
1685
}
1686
if (GetContentFlags() & CONTENT_COMPONENT_ALPHA) {
1687
aStream << " [componentAlpha]";
1688
}
1689
if (GetContentFlags() & CONTENT_BACKFACE_HIDDEN) {
1690
aStream << " [backfaceHidden]";
1691
}
1692
if (Extend3DContext()) {
1693
aStream << " [extend3DContext]";
1694
}
1695
if (Combines3DTransformWithAncestors()) {
1696
aStream << " [combines3DTransformWithAncestors]";
1697
}
1698
if (Is3DContextLeaf()) {
1699
aStream << " [is3DContextLeaf]";
1700
}
1701
if (Maybe<FrameMetrics::ViewID> viewId = IsAsyncZoomContainer()) {
1702
aStream << nsPrintfCString(" [asyncZoomContainer scrollId=%" PRIu64 "]",
1703
*viewId)
1704
.get();
1705
}
1706
if (IsScrollbarContainer()) {
1707
aStream << " [scrollbar]";
1708
}
1709
if (GetScrollbarData().IsThumb()) {
1710
if (Maybe<ScrollDirection> thumbDirection = GetScrollbarData().mDirection) {
1711
if (*thumbDirection == ScrollDirection::eVertical) {
1712
aStream << nsPrintfCString(" [vscrollbar=%" PRIu64 "]",
1713
GetScrollbarData().mTargetViewId)
1714
.get();
1715
}
1716
if (*thumbDirection == ScrollDirection::eHorizontal) {
1717
aStream << nsPrintfCString(" [hscrollbar=%" PRIu64 "]",
1718
GetScrollbarData().mTargetViewId)
1719
.get();
1720
}
1721
}
1722
}
1723
if (GetIsFixedPosition()) {
1724
LayerPoint anchor = GetFixedPositionAnchor();
1725
aStream << nsPrintfCString(
1726
" [isFixedPosition scrollId=%" PRIu64
1727
" sides=0x%x anchor=%s]",
1728
GetFixedPositionScrollContainerId(),
1729
static_cast<unsigned int>(GetFixedPositionSides()),
1730
ToString(anchor).c_str())
1731
.get();
1732
}
1733
if (GetIsStickyPosition()) {
1734
aStream << nsPrintfCString(" [isStickyPosition scrollId=%" PRIu64
1735
" outer=(%.3f,%.3f)-(%.3f,%.3f) "
1736
"inner=(%.3f,%.3f)-(%.3f,%.3f)]",
1737
GetStickyScrollContainerId(),
1738
GetStickyScrollRangeOuter().X(),
1739
GetStickyScrollRangeOuter().Y(),
1740
GetStickyScrollRangeOuter().XMost(),
1741
GetStickyScrollRangeOuter().YMost(),
1742
GetStickyScrollRangeInner().X(),
1743
GetStickyScrollRangeInner().Y(),
1744
GetStickyScrollRangeInner().XMost(),
1745
GetStickyScrollRangeInner().YMost())
1746
.get();
1747
}
1748
if (mMaskLayer) {
1749
aStream << nsPrintfCString(" [mMaskLayer=%p]", mMaskLayer.get()).get();
1750
}
1751
for (uint32_t i = 0; i < mScrollMetadata.Length(); i++) {
1752
if (!mScrollMetadata[i].IsDefault()) {
1753
aStream << nsPrintfCString(" [metrics%d=", i).get();
1754
AppendToString(aStream, mScrollMetadata[i], "", "]");
1755
}
1756
}
1757
// FIXME: On the compositor thread, we don't set mAnimationInfo::mAnimations,
1758
// All animations are transformed by AnimationHelper::ExtractAnimations() into
1759
// mAnimationInfo.mPropertyAnimationGroups, instead. So if we want to check
1760
// if layer trees are properly synced up across processes, we should dump
1761
// mAnimationInfo.mPropertyAnimationGroups for the compositor thread.
1762
// (See AnimationInfo.h for more details.)
1763
if (!mAnimationInfo.GetAnimations().IsEmpty()) {
1764
aStream << nsPrintfCString(" [%d animations with id=%" PRIu64 " ]",
1765
(int)mAnimationInfo.GetAnimations().Length(),
1766
mAnimationInfo.GetCompositorAnimationsId())
1767
.get();
1768
}
1769
}
1770
1771
// The static helper function sets the transform matrix into the packet
1772
static void DumpTransform(layerscope::LayersPacket::Layer::Matrix* aLayerMatrix,
1773
const Matrix4x4& aMatrix) {
1774
aLayerMatrix->set_is2d(aMatrix.Is2D());
1775
if (aMatrix.Is2D()) {
1776
Matrix m = aMatrix.As2D();
1777
aLayerMatrix->set_isid(m.IsIdentity());
1778
if (!m.IsIdentity()) {
1779
aLayerMatrix->add_m(m._11);
1780
aLayerMatrix->add_m(m._12);
1781
aLayerMatrix->add_m(m._21);
1782
aLayerMatrix->add_m(m._22);
1783
aLayerMatrix->add_m(m._31);
1784
aLayerMatrix->add_m(m._32);
1785
}
1786
} else {
1787
aLayerMatrix->add_m(aMatrix._11);