Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/gfx/layers/client/ContentClient.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 "mozilla/layers/ContentClient.h"
8
#include "BasicLayers.h"                // for BasicLayerManager
9
#include "gfxContext.h"                 // for gfxContext, etc
10
#include "gfxPlatform.h"                // for gfxPlatform
11
#include "gfxEnv.h"                     // for gfxEnv
12
#include "gfxPrefs.h"                   // for gfxPrefs
13
#include "gfxPoint.h"                   // for IntSize, gfxPoint
14
#include "gfxUtils.h"                   // for gfxUtils
15
#include "ipc/ShadowLayers.h"           // for ShadowLayerForwarder
16
#include "mozilla/ArrayUtils.h"         // for ArrayLength
17
#include "mozilla/gfx/2D.h"             // for DrawTarget, Factory
18
#include "mozilla/gfx/BasePoint.h"      // for BasePoint
19
#include "mozilla/gfx/BaseSize.h"       // for BaseSize
20
#include "mozilla/gfx/Rect.h"           // for Rect
21
#include "mozilla/gfx/Types.h"
22
#include "mozilla/layers/CompositorBridgeChild.h" // for CompositorBridgeChild
23
#include "mozilla/layers/LayerManagerComposite.h"
24
#include "mozilla/layers/LayersMessages.h"  // for ThebesBufferData
25
#include "mozilla/layers/LayersTypes.h"
26
#include "mozilla/layers/PaintThread.h"
27
#include "nsDebug.h"                    // for NS_ASSERTION, NS_WARNING, etc
28
#include "nsISupportsImpl.h"            // for gfxContext::Release, etc
29
#include "nsIWidget.h"                  // for nsIWidget
30
#include "nsLayoutUtils.h"
31
#ifdef XP_WIN
32
#include "gfxWindowsPlatform.h"
33
#endif
34
#ifdef MOZ_WIDGET_GTK
35
#include "gfxPlatformGtk.h"
36
#endif
37
#include "ReadbackLayer.h"
38
39
#include <utility>
40
#include <vector>
41
42
using namespace std;
43
44
namespace mozilla {
45
46
using namespace gfx;
47
48
namespace layers {
49
50
static TextureFlags TextureFlagsForContentClientFlags(uint32_t aBufferFlags)
51
0
{
52
0
  TextureFlags result = TextureFlags::NO_FLAGS;
53
0
54
0
  if (aBufferFlags & ContentClient::BUFFER_COMPONENT_ALPHA) {
55
0
    result |= TextureFlags::COMPONENT_ALPHA;
56
0
  }
57
0
58
0
  return result;
59
0
}
60
61
static IntRect
62
ComputeBufferRect(const IntRect& aRequestedRect)
63
0
{
64
0
  IntRect rect(aRequestedRect);
65
0
  // Set a minimum width to guarantee a minimum size of buffers we
66
0
  // allocate (and work around problems on some platforms with smaller
67
0
  // dimensions). 64 used to be the magic number needed to work around
68
0
  // a rendering glitch on b2g (see bug 788411). Now that we don't support
69
0
  // this device anymore we should be fine with 8 pixels as the minimum.
70
0
  rect.SetWidth(std::max(aRequestedRect.Width(), 8));
71
0
  return rect;
72
0
}
73
74
/* static */ already_AddRefed<ContentClient>
75
ContentClient::CreateContentClient(CompositableForwarder* aForwarder)
76
0
{
77
0
  LayersBackend backend = aForwarder->GetCompositorBackendType();
78
0
  if (backend != LayersBackend::LAYERS_OPENGL &&
79
0
      backend != LayersBackend::LAYERS_D3D11 &&
80
0
      backend != LayersBackend::LAYERS_WR &&
81
0
      backend != LayersBackend::LAYERS_BASIC) {
82
0
    return nullptr;
83
0
  }
84
0
85
0
  bool useDoubleBuffering = false;
86
0
87
#ifdef XP_WIN
88
  if (backend == LayersBackend::LAYERS_D3D11) {
89
    useDoubleBuffering = gfxWindowsPlatform::GetPlatform()->IsDirect2DBackend();
90
  } else
91
#endif
92
#ifdef MOZ_WIDGET_GTK
93
0
  // We can't use double buffering when using image content with
94
0
  // Xrender support on Linux, as ContentHostDoubleBuffered is not
95
0
  // suited for direct uploads to the server.
96
0
  if (!gfxPlatformGtk::GetPlatform()->UseImageOffscreenSurfaces() ||
97
0
      !gfxVars::UseXRender())
98
0
#endif
99
0
  {
100
0
    useDoubleBuffering = backend == LayersBackend::LAYERS_BASIC;
101
0
  }
102
0
103
0
  if (useDoubleBuffering || gfxEnv::ForceDoubleBuffering()) {
104
0
    return MakeAndAddRef<ContentClientDoubleBuffered>(aForwarder);
105
0
  }
106
0
  return MakeAndAddRef<ContentClientSingleBuffered>(aForwarder);
107
0
}
108
109
void
110
ContentClient::Clear()
111
0
{
112
0
  mBuffer = nullptr;
113
0
}
114
115
ContentClient::PaintState
116
ContentClient::BeginPaint(PaintedLayer* aLayer,
117
                          uint32_t aFlags)
118
0
{
119
0
  BufferDecision dest = CalculateBufferForPaint(aLayer, aFlags);
120
0
121
0
  PaintState result;
122
0
  result.mAsyncPaint = (aFlags & PAINT_ASYNC);
123
0
  result.mContentType = dest.mBufferContentType;
124
0
125
0
  if (!dest.mCanKeepBufferContents) {
126
0
    // We're effectively clearing the valid region, so we need to draw
127
0
    // the entire needed region now.
128
0
    MOZ_ASSERT(!dest.mCanReuseBuffer);
129
0
    MOZ_ASSERT(dest.mValidRegion.IsEmpty());
130
0
131
0
    result.mRegionToInvalidate = aLayer->GetValidRegion();
132
0
133
#if defined(MOZ_DUMP_PAINTING)
134
    if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
135
      if (result.mContentType != mBuffer->GetContentType()) {
136
        printf_stderr("Invalidating entire rotated buffer (layer %p): content type changed\n", aLayer);
137
      } else if ((dest.mBufferMode == SurfaceMode::SURFACE_COMPONENT_ALPHA) != mBuffer->HaveBufferOnWhite()) {
138
        printf_stderr("Invalidating entire rotated buffer (layer %p): component alpha changed\n", aLayer);
139
      }
140
    }
141
#endif
142
    Clear();
143
0
  }
144
0
145
0
  result.mRegionToDraw.Sub(dest.mNeededRegion,
146
0
                           dest.mValidRegion);
147
0
148
0
  if (result.mRegionToDraw.IsEmpty())
149
0
    return result;
150
0
151
0
  // We need to disable rotation if we're going to be resampled when
152
0
  // drawing, because we might sample across the rotation boundary.
153
0
  // Also disable buffer rotation when using webrender.
154
0
  bool canHaveRotation = gfxPlatform::BufferRotationEnabled() &&
155
0
                         !(aFlags & (PAINT_WILL_RESAMPLE | PAINT_NO_ROTATION)) &&
156
0
                         !(aLayer->Manager()->AsWebRenderLayerManager());
157
0
  bool canDrawRotated = aFlags & PAINT_CAN_DRAW_ROTATED;
158
0
  OpenMode readMode = result.mAsyncPaint ? OpenMode::OPEN_READ_ASYNC
159
0
                                         : OpenMode::OPEN_READ;
160
0
  OpenMode writeMode = result.mAsyncPaint ? OpenMode::OPEN_READ_WRITE_ASYNC
161
0
                                          : OpenMode::OPEN_READ_WRITE;
162
0
163
0
  IntRect drawBounds = result.mRegionToDraw.GetBounds();
164
0
165
0
  if (result.mAsyncPaint) {
166
0
    result.mAsyncTask.reset(new PaintTask());
167
0
  }
168
0
169
0
  // Try to acquire the back buffer, copy over contents if we are using a new buffer,
170
0
  // and rotate or unrotate the buffer as necessary
171
0
  if (mBuffer && dest.mCanReuseBuffer) {
172
0
    if (mBuffer->Lock(writeMode)) {
173
0
      auto newParameters = mBuffer->AdjustedParameters(dest.mBufferRect);
174
0
175
0
      bool needsUnrotate = (!canHaveRotation && newParameters.IsRotated()) ||
176
0
                           (!canDrawRotated && newParameters.RectWrapsBuffer(drawBounds));
177
0
      bool canUnrotate = !result.mAsyncPaint || mBuffer->BufferRotation() == IntPoint(0,0);
178
0
179
0
      // Only begin a frame and copy over the previous frame if we don't need
180
0
      // to unrotate, or we can try to unrotate it. This is to ensure that we
181
0
      // don't have a paint task that depends on another paint task.
182
0
      if (!needsUnrotate || canUnrotate) {
183
0
        // If we're async painting then begin to capture draw commands
184
0
        if (result.mAsyncPaint) {
185
0
          mBuffer->BeginCapture();
186
0
        }
187
0
188
0
        // Do not modify result.mRegionToDraw or result.mContentType after this call.
189
0
        FinalizeFrame(result);
190
0
      }
191
0
192
0
      // Try to rotate the buffer or unrotate it if we cannot be rotated
193
0
      if (needsUnrotate) {
194
0
        if (canUnrotate && mBuffer->UnrotateBufferTo(newParameters)) {
195
0
          newParameters.SetUnrotated();
196
0
          mBuffer->SetParameters(newParameters);
197
0
        } else {
198
0
          MOZ_ASSERT(GetFrontBuffer());
199
0
          mBuffer->Unlock();
200
0
          dest.mBufferRect = ComputeBufferRect(dest.mNeededRegion.GetBounds());
201
0
          dest.mCanReuseBuffer = false;
202
0
        }
203
0
      } else {
204
0
        mBuffer->SetParameters(newParameters);
205
0
      }
206
0
    } else {
207
0
      result.mRegionToDraw = dest.mNeededRegion;
208
0
      dest.mCanReuseBuffer = false;
209
0
      Clear();
210
0
    }
211
0
  }
212
0
213
0
  MOZ_ASSERT(dest.mBufferRect.Contains(result.mRegionToDraw.GetBounds()));
214
0
215
0
  NS_ASSERTION(!(aFlags & PAINT_WILL_RESAMPLE) || dest.mBufferRect == dest.mNeededRegion.GetBounds(),
216
0
               "If we're resampling, we need to validate the entire buffer");
217
0
218
0
  // We never had a buffer, the buffer wasn't big enough, the content changed
219
0
  // types, or we failed to unrotate the buffer when requested. In any case,
220
0
  // we need to allocate a new one and prepare it for drawing.
221
0
  if (!dest.mCanReuseBuffer) {
222
0
    uint32_t bufferFlags = 0;
223
0
    if (dest.mBufferMode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
224
0
      bufferFlags |= BUFFER_COMPONENT_ALPHA;
225
0
    }
226
0
227
0
    RefPtr<RotatedBuffer> newBuffer = CreateBuffer(result.mContentType,
228
0
                                                   dest.mBufferRect,
229
0
                                                   bufferFlags);
230
0
231
0
    if (!newBuffer) {
232
0
      if (Factory::ReasonableSurfaceSize(IntSize(dest.mBufferRect.Width(), dest.mBufferRect.Height()))) {
233
0
        gfxCriticalNote << "Failed buffer for "
234
0
                        << dest.mBufferRect.X() << ", "
235
0
                        << dest.mBufferRect.Y() << ", "
236
0
                        << dest.mBufferRect.Width() << ", "
237
0
                        << dest.mBufferRect.Height();
238
0
      }
239
0
      result.mAsyncTask = nullptr;
240
0
      Clear();
241
0
      return result;
242
0
    }
243
0
244
0
    if (!newBuffer->Lock(writeMode)) {
245
0
      gfxCriticalNote << "Failed to lock new back buffer.";
246
0
      result.mAsyncTask = nullptr;
247
0
      Clear();
248
0
      return result;
249
0
    }
250
0
251
0
    if (result.mAsyncPaint) {
252
0
      newBuffer->BeginCapture();
253
0
    }
254
0
255
0
    // If we have an existing front buffer, copy it into the new back buffer
256
0
    RefPtr<RotatedBuffer> frontBuffer = GetFrontBuffer();
257
0
258
0
    if (frontBuffer && frontBuffer->Lock(readMode)) {
259
0
      nsIntRegion updateRegion = newBuffer->BufferRect();
260
0
      updateRegion.Sub(updateRegion, result.mRegionToDraw);
261
0
262
0
      if (!updateRegion.IsEmpty()) {
263
0
        newBuffer->UpdateDestinationFrom(*frontBuffer, updateRegion.GetBounds());
264
0
      }
265
0
266
0
      frontBuffer->Unlock();
267
0
    } else {
268
0
      result.mRegionToDraw = dest.mNeededRegion;
269
0
    }
270
0
271
0
    Clear();
272
0
    mBuffer = newBuffer;
273
0
  }
274
0
275
0
  NS_ASSERTION(canHaveRotation || mBuffer->BufferRotation() == IntPoint(0,0),
276
0
               "Rotation disabled, but we have nonzero rotation?");
277
0
278
0
  if (result.mAsyncPaint) {
279
0
    result.mAsyncTask->mTarget = mBuffer->GetBufferTarget();
280
0
    result.mAsyncTask->mClients.AppendElement(mBuffer->GetClient());
281
0
    if (mBuffer->GetClientOnWhite()) {
282
0
      result.mAsyncTask->mClients.AppendElement(mBuffer->GetClientOnWhite());
283
0
    }
284
0
  }
285
0
286
0
  nsIntRegion invalidate;
287
0
  invalidate.Sub(aLayer->GetValidRegion(), dest.mBufferRect);
288
0
  result.mRegionToInvalidate.Or(result.mRegionToInvalidate, invalidate);
289
0
290
0
  result.mClip = DrawRegionClip::DRAW;
291
0
  result.mMode = dest.mBufferMode;
292
0
293
0
  return result;
294
0
}
295
296
void
297
ContentClient::EndPaint(PaintState& aPaintState, nsTArray<ReadbackProcessor::Update>* aReadbackUpdates)
298
0
{
299
0
  if (aPaintState.mAsyncTask) {
300
0
    aPaintState.mAsyncTask->mCapture = mBuffer->EndCapture();
301
0
  }
302
0
}
303
304
nsIntRegion
305
ExpandDrawRegion(ContentClient::PaintState& aPaintState,
306
                 RotatedBuffer::DrawIterator* aIter,
307
                 BackendType aBackendType)
308
0
{
309
0
  nsIntRegion* drawPtr = &aPaintState.mRegionToDraw;
310
0
  if (aIter) {
311
0
    // The iterators draw region currently only contains the bounds of the region,
312
0
    // this makes it the precise region.
313
0
    aIter->mDrawRegion.And(aIter->mDrawRegion, aPaintState.mRegionToDraw);
314
0
    drawPtr = &aIter->mDrawRegion;
315
0
  }
316
0
  if (aBackendType == BackendType::DIRECT2D ||
317
0
      aBackendType == BackendType::DIRECT2D1_1) {
318
0
    // Simplify the draw region to avoid hitting expensive drawing paths
319
0
    // for complex regions.
320
0
    drawPtr->SimplifyOutwardByArea(100 * 100);
321
0
  }
322
0
  return *drawPtr;
323
0
}
324
325
DrawTarget*
326
ContentClient::BorrowDrawTargetForPainting(ContentClient::PaintState& aPaintState,
327
                                           RotatedBuffer::DrawIterator* aIter /* = nullptr */)
328
0
{
329
0
  if (aPaintState.mMode == SurfaceMode::SURFACE_NONE || !mBuffer) {
330
0
    return nullptr;
331
0
  }
332
0
333
0
  DrawTarget* result = mBuffer->BorrowDrawTargetForQuadrantUpdate(
334
0
                                  aPaintState.mRegionToDraw.GetBounds(),
335
0
                                  aIter);
336
0
  if (!result || !result->IsValid()) {
337
0
    if (result) {
338
0
      mBuffer->ReturnDrawTarget(result);
339
0
    }
340
0
    return nullptr;
341
0
  }
342
0
343
0
  nsIntRegion regionToDraw =
344
0
    ExpandDrawRegion(aPaintState, aIter, result->GetBackendType());
345
0
346
0
  if (aPaintState.mMode == SurfaceMode::SURFACE_COMPONENT_ALPHA ||
347
0
      aPaintState.mContentType == gfxContentType::COLOR_ALPHA) {
348
0
    // HaveBuffer() => we have an existing buffer that we must clear
349
0
    for (auto iter = regionToDraw.RectIter(); !iter.Done(); iter.Next()) {
350
0
      const IntRect& rect = iter.Get();
351
0
      result->ClearRect(Rect(rect.X(), rect.Y(), rect.Width(), rect.Height()));
352
0
    }
353
0
  }
354
0
355
0
  return result;
356
0
}
357
358
void
359
ContentClient::ReturnDrawTarget(gfx::DrawTarget*& aReturned)
360
0
{
361
0
  mBuffer->ReturnDrawTarget(aReturned);
362
0
}
363
364
ContentClient::BufferDecision
365
ContentClient::CalculateBufferForPaint(PaintedLayer* aLayer,
366
                                       uint32_t aFlags)
367
0
{
368
0
  gfxContentType layerContentType =
369
0
    aLayer->CanUseOpaqueSurface() ? gfxContentType::COLOR :
370
0
                                    gfxContentType::COLOR_ALPHA;
371
0
372
0
  SurfaceMode mode;
373
0
  gfxContentType contentType;
374
0
  IntRect destBufferRect;
375
0
  nsIntRegion neededRegion;
376
0
  nsIntRegion validRegion = aLayer->GetValidRegion();
377
0
378
0
  bool canReuseBuffer = !!mBuffer;
379
0
  bool canKeepBufferContents = true;
380
0
381
0
  while (true) {
382
0
    mode = aLayer->GetSurfaceMode();
383
0
    neededRegion = aLayer->GetVisibleRegion().ToUnknownRegion();
384
0
    canReuseBuffer = canReuseBuffer && ValidBufferSize(mBufferSizePolicy,
385
0
                                                       mBuffer->BufferRect().Size(),
386
0
                                                       neededRegion.GetBounds().Size());
387
0
    contentType = layerContentType;
388
0
389
0
    if (canReuseBuffer) {
390
0
      if (mBuffer->BufferRect().Contains(neededRegion.GetBounds())) {
391
0
        // We don't need to adjust mBufferRect.
392
0
        destBufferRect = mBuffer->BufferRect();
393
0
      } else if (neededRegion.GetBounds().Size() <= mBuffer->BufferRect().Size()) {
394
0
        // The buffer's big enough but doesn't contain everything that's
395
0
        // going to be visible. We'll move it.
396
0
        destBufferRect = IntRect(neededRegion.GetBounds().TopLeft(), mBuffer->BufferRect().Size());
397
0
      } else {
398
0
        destBufferRect = neededRegion.GetBounds();
399
0
      }
400
0
    } else {
401
0
      destBufferRect = ComputeBufferRect(neededRegion.GetBounds());
402
0
    }
403
0
404
0
    if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
405
#if defined(MOZ_GFX_OPTIMIZE_MOBILE)
406
      mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA;
407
#else
408
0
      if (!aLayer->GetParent() ||
409
0
          !aLayer->GetParent()->SupportsComponentAlphaChildren() ||
410
0
          !aLayer->AsShadowableLayer() ||
411
0
          !aLayer->AsShadowableLayer()->HasShadow()) {
412
0
        mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA;
413
0
      } else {
414
0
        contentType = gfxContentType::COLOR;
415
0
      }
416
0
#endif
417
0
    }
418
0
419
0
    if ((aFlags & PAINT_WILL_RESAMPLE) &&
420
0
        (!neededRegion.GetBounds().IsEqualInterior(destBufferRect) ||
421
0
         neededRegion.GetNumRects() > 1))
422
0
    {
423
0
      // The area we add to neededRegion might not be painted opaquely.
424
0
      if (mode == SurfaceMode::SURFACE_OPAQUE) {
425
0
        contentType = gfxContentType::COLOR_ALPHA;
426
0
        mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA;
427
0
      }
428
0
429
0
      // We need to validate the entire buffer, to make sure that only valid
430
0
      // pixels are sampled.
431
0
      neededRegion = destBufferRect;
432
0
    }
433
0
434
0
    // If we have an existing buffer, but the content type has changed or we
435
0
    // have transitioned into/out of component alpha, then we need to recreate it.
436
0
    bool needsComponentAlpha = (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA);
437
0
    bool backBufferChangedSurface = mBuffer &&
438
0
                                    (contentType != mBuffer->GetContentType() ||
439
0
                                     needsComponentAlpha != mBuffer->HaveBufferOnWhite());
440
0
    if (canKeepBufferContents && backBufferChangedSurface) {
441
0
      // Restart the decision process; we won't re-enter since we guard on
442
0
      // being able to keep the buffer contents.
443
0
      canReuseBuffer = false;
444
0
      canKeepBufferContents = false;
445
0
      validRegion.SetEmpty();
446
0
      continue;
447
0
    }
448
0
    break;
449
0
  }
450
0
451
0
  NS_ASSERTION(destBufferRect.Contains(neededRegion.GetBounds()),
452
0
               "Destination rect doesn't contain what we need to paint");
453
0
454
0
  BufferDecision dest;
455
0
  dest.mNeededRegion = std::move(neededRegion);
456
0
  dest.mValidRegion = std::move(validRegion);
457
0
  dest.mBufferRect = destBufferRect;
458
0
  dest.mBufferMode = mode;
459
0
  dest.mBufferContentType = contentType;
460
0
  dest.mCanReuseBuffer = canReuseBuffer;
461
0
  dest.mCanKeepBufferContents = canKeepBufferContents;
462
0
  return dest;
463
0
}
464
465
bool
466
ContentClient::ValidBufferSize(BufferSizePolicy aPolicy,
467
                               const gfx::IntSize& aBufferSize,
468
                               const gfx::IntSize& aVisibleBoundsSize)
469
0
{
470
0
  return (aVisibleBoundsSize == aBufferSize ||
471
0
          (SizedToVisibleBounds != aPolicy &&
472
0
           aVisibleBoundsSize < aBufferSize));
473
0
}
474
475
void
476
ContentClient::PrintInfo(std::stringstream& aStream, const char* aPrefix)
477
0
{
478
0
  aStream << aPrefix;
479
0
  aStream << nsPrintfCString("ContentClient (0x%p)", this).get();
480
0
}
481
482
// We pass a null pointer for the ContentClient Forwarder argument, which means
483
// this client will not have a ContentHost on the other side.
484
ContentClientBasic::ContentClientBasic(gfx::BackendType aBackend)
485
  : ContentClient(nullptr, ContainsVisibleBounds)
486
  , mBackend(aBackend)
487
0
{}
488
489
void
490
ContentClientBasic::DrawTo(PaintedLayer* aLayer,
491
                           gfx::DrawTarget* aTarget,
492
                           float aOpacity,
493
                           gfx::CompositionOp aOp,
494
                           gfx::SourceSurface* aMask,
495
                           const gfx::Matrix* aMaskTransform)
496
0
{
497
0
  if (!mBuffer) {
498
0
    return;
499
0
  }
500
0
501
0
  mBuffer->DrawTo(aLayer, aTarget, aOpacity, aOp,
502
0
                  aMask, aMaskTransform);
503
0
}
504
505
RefPtr<RotatedBuffer>
506
ContentClientBasic::CreateBuffer(gfxContentType aType,
507
                                 const IntRect& aRect,
508
                                 uint32_t aFlags)
509
0
{
510
0
  MOZ_ASSERT(!(aFlags & BUFFER_COMPONENT_ALPHA));
511
0
  if (aFlags & BUFFER_COMPONENT_ALPHA) {
512
0
    gfxDevCrash(LogReason::AlphaWithBasicClient) << "Asking basic content client for component alpha";
513
0
  }
514
0
515
0
  IntSize size(aRect.Width(), aRect.Height());
516
0
  RefPtr<gfx::DrawTarget> drawTarget;
517
0
518
#ifdef XP_WIN
519
  if (mBackend == BackendType::CAIRO && 
520
      (aType == gfxContentType::COLOR || aType == gfxContentType::COLOR_ALPHA)) {
521
    RefPtr<gfxASurface> surf =
522
      new gfxWindowsSurface(size, aType == gfxContentType::COLOR ? gfxImageFormat::X8R8G8B8_UINT32 :
523
                                                                   gfxImageFormat::A8R8G8B8_UINT32);
524
    drawTarget = gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(surf, size);
525
  }
526
#endif
527
528
0
  if (!drawTarget) {
529
0
    drawTarget = gfxPlatform::GetPlatform()->CreateDrawTargetForBackend(
530
0
      mBackend, size,
531
0
      gfxPlatform::GetPlatform()->Optimal2DFormatForContent(aType));
532
0
  }
533
0
534
0
  if (!drawTarget) {
535
0
    return nullptr;
536
0
  }
537
0
538
0
  return new DrawTargetRotatedBuffer(drawTarget, nullptr, aRect, IntPoint(0,0));
539
0
}
540
541
class RemoteBufferReadbackProcessor : public TextureReadbackSink
542
{
543
public:
544
  RemoteBufferReadbackProcessor(nsTArray<ReadbackProcessor::Update>* aReadbackUpdates,
545
                                const IntRect& aBufferRect, const nsIntPoint& aBufferRotation)
546
    : mReadbackUpdates(*aReadbackUpdates)
547
    , mBufferRect(aBufferRect)
548
    , mBufferRotation(aBufferRotation)
549
0
  {
550
0
    for (uint32_t i = 0; i < mReadbackUpdates.Length(); ++i) {
551
0
      mLayerRefs.push_back(mReadbackUpdates[i].mLayer);
552
0
    }
553
0
  }
554
555
  virtual void ProcessReadback(gfx::DataSourceSurface *aSourceSurface) override
556
0
  {
557
0
    SourceRotatedBuffer rotBuffer(aSourceSurface, nullptr, mBufferRect, mBufferRotation);
558
0
559
0
    for (uint32_t i = 0; i < mReadbackUpdates.Length(); ++i) {
560
0
      ReadbackProcessor::Update& update = mReadbackUpdates[i];
561
0
      nsIntPoint offset = update.mLayer->GetBackgroundLayerOffset();
562
0
563
0
      ReadbackSink* sink = update.mLayer->GetSink();
564
0
565
0
      if (!sink) {
566
0
        continue;
567
0
      }
568
0
569
0
      if (!aSourceSurface) {
570
0
        sink->SetUnknown(update.mSequenceCounter);
571
0
        continue;
572
0
      }
573
0
574
0
      RefPtr<DrawTarget> dt =
575
0
        sink->BeginUpdate(update.mUpdateRect + offset, update.mSequenceCounter);
576
0
      if (!dt) {
577
0
        continue;
578
0
      }
579
0
580
0
      dt->SetTransform(Matrix::Translation(offset.x, offset.y));
581
0
582
0
      rotBuffer.DrawBufferWithRotation(dt);
583
0
584
0
      update.mLayer->GetSink()->EndUpdate(update.mUpdateRect + offset);
585
0
    }
586
0
  }
587
588
private:
589
  nsTArray<ReadbackProcessor::Update> mReadbackUpdates;
590
  // This array is used to keep the layers alive until the callback.
591
  vector<RefPtr<Layer>> mLayerRefs;
592
593
  IntRect mBufferRect;
594
  nsIntPoint mBufferRotation;
595
};
596
597
void
598
ContentClientRemoteBuffer::EndPaint(PaintState& aPaintState, nsTArray<ReadbackProcessor::Update>* aReadbackUpdates)
599
0
{
600
0
  MOZ_ASSERT(!mBuffer || !mBuffer->HaveBufferOnWhite() ||
601
0
             !aReadbackUpdates || aReadbackUpdates->Length() == 0);
602
0
603
0
  RemoteRotatedBuffer* remoteBuffer = GetRemoteBuffer();
604
0
605
0
  if (remoteBuffer && remoteBuffer->IsLocked()) {
606
0
    if (aReadbackUpdates && aReadbackUpdates->Length() > 0) {
607
0
      RefPtr<TextureReadbackSink> readbackSink = new RemoteBufferReadbackProcessor(aReadbackUpdates,
608
0
                                                                                   remoteBuffer->BufferRect(),
609
0
                                                                                   remoteBuffer->BufferRotation());
610
0
611
0
      remoteBuffer->GetClient()->SetReadbackSink(readbackSink);
612
0
    }
613
0
614
0
    remoteBuffer->Unlock();
615
0
    remoteBuffer->SyncWithObject(mForwarder->GetSyncObject());
616
0
  }
617
0
618
0
  ContentClient::EndPaint(aPaintState, aReadbackUpdates);
619
0
}
620
621
RefPtr<RotatedBuffer>
622
ContentClientRemoteBuffer::CreateBuffer(gfxContentType aType,
623
                                        const IntRect& aRect,
624
                                        uint32_t aFlags)
625
0
{
626
0
  // If we hit this assertion, then it might be due to an empty transaction
627
0
  // followed by a real transaction. Our buffers should be created (but not
628
0
  // painted in the empty transaction) and then painted (but not created) in the
629
0
  // real transaction. That is kind of fragile, and this assert will catch
630
0
  // circumstances where we screw that up, e.g., by unnecessarily recreating our
631
0
  // buffers.
632
0
  MOZ_ASSERT(!mIsNewBuffer,
633
0
             "Bad! Did we create a buffer twice without painting?");
634
0
635
0
  gfx::SurfaceFormat format = gfxPlatform::GetPlatform()->Optimal2DFormatForContent(aType);
636
0
637
0
  TextureFlags textureFlags = TextureFlagsForContentClientFlags(aFlags);
638
0
  if (aFlags & BUFFER_COMPONENT_ALPHA) {
639
0
    textureFlags |= TextureFlags::COMPONENT_ALPHA;
640
0
  }
641
0
642
0
  RefPtr<RotatedBuffer> buffer = CreateBufferInternal(aRect, format, textureFlags);
643
0
644
0
  if (!buffer) {
645
0
    return nullptr;
646
0
  }
647
0
648
0
  mIsNewBuffer = true;
649
0
  mTextureFlags = textureFlags;
650
0
651
0
  return buffer;
652
0
}
653
654
RefPtr<RotatedBuffer>
655
ContentClientRemoteBuffer::CreateBufferInternal(const gfx::IntRect& aRect,
656
                                                gfx::SurfaceFormat aFormat,
657
                                                TextureFlags aFlags)
658
0
{
659
0
  TextureAllocationFlags textureAllocFlags
660
0
                         = (aFlags & TextureFlags::COMPONENT_ALPHA) ?
661
0
                            TextureAllocationFlags::ALLOC_CLEAR_BUFFER_BLACK :
662
0
                            TextureAllocationFlags::ALLOC_CLEAR_BUFFER;
663
0
664
0
  RefPtr<TextureClient> textureClient = CreateTextureClientForDrawing(
665
0
    aFormat, aRect.Size(), BackendSelector::Content,
666
0
    aFlags | ExtraTextureFlags() | TextureFlags::BLOCKING_READ_LOCK,
667
0
    textureAllocFlags
668
0
  );
669
0
670
0
  if (!textureClient || !AddTextureClient(textureClient)) {
671
0
    return nullptr;
672
0
  }
673
0
674
0
  RefPtr<TextureClient> textureClientOnWhite;
675
0
  if (aFlags & TextureFlags::COMPONENT_ALPHA) {
676
0
    TextureAllocationFlags allocFlags = ALLOC_CLEAR_BUFFER_WHITE;
677
0
    if (mForwarder->SupportsTextureDirectMapping()) {
678
0
      allocFlags = TextureAllocationFlags(allocFlags | ALLOC_ALLOW_DIRECT_MAPPING);
679
0
    }
680
0
    textureClientOnWhite = textureClient->CreateSimilar(
681
0
      mForwarder->GetCompositorBackendType(),
682
0
      aFlags | ExtraTextureFlags(),
683
0
      allocFlags
684
0
    );
685
0
    if (!textureClientOnWhite || !AddTextureClient(textureClientOnWhite)) {
686
0
      return nullptr;
687
0
    }
688
0
    // We don't enable the readlock for the white buffer since we always
689
0
    // use them together and waiting on the lock for the black
690
0
    // should be sufficient.
691
0
  }
692
0
693
0
  return new RemoteRotatedBuffer(textureClient,
694
0
                                 textureClientOnWhite,
695
0
                                 aRect,
696
0
                                 IntPoint(0,0));
697
0
}
698
699
nsIntRegion
700
ContentClientRemoteBuffer::GetUpdatedRegion(const nsIntRegion& aRegionToDraw,
701
                                            const nsIntRegion& aVisibleRegion)
702
0
{
703
0
  nsIntRegion updatedRegion;
704
0
  if (mIsNewBuffer || mBuffer->DidSelfCopy()) {
705
0
    // A buffer reallocation clears both buffers. The front buffer has all the
706
0
    // content by now, but the back buffer is still clear. Here, in effect, we
707
0
    // are saying to copy all of the pixels of the front buffer to the back.
708
0
    // Also when we self-copied in the buffer, the buffer space
709
0
    // changes and some changed buffer content isn't reflected in the
710
0
    // draw or invalidate region (on purpose!).  When this happens, we
711
0
    // need to read back the entire buffer too.
712
0
    updatedRegion = aVisibleRegion.GetBounds();
713
0
    mIsNewBuffer = false;
714
0
  } else {
715
0
    updatedRegion = aRegionToDraw;
716
0
  }
717
0
718
0
  MOZ_ASSERT(mBuffer, "should have a back buffer by now");
719
0
  NS_ASSERTION(mBuffer->BufferRect().Contains(aRegionToDraw.GetBounds()),
720
0
               "Update outside of buffer rect!");
721
0
722
0
  return updatedRegion;
723
0
}
724
725
void
726
ContentClientRemoteBuffer::Updated(const nsIntRegion& aRegionToDraw,
727
                                   const nsIntRegion& aVisibleRegion)
728
0
{
729
0
  nsIntRegion updatedRegion = GetUpdatedRegion(aRegionToDraw,
730
0
                                               aVisibleRegion);
731
0
732
0
  RemoteRotatedBuffer* remoteBuffer = GetRemoteBuffer();
733
0
734
0
  MOZ_ASSERT(remoteBuffer && remoteBuffer->GetClient());
735
0
  if (remoteBuffer->HaveBufferOnWhite()) {
736
0
    mForwarder->UseComponentAlphaTextures(this,
737
0
                                          remoteBuffer->GetClient(),
738
0
                                          remoteBuffer->GetClientOnWhite());
739
0
  } else {
740
0
    AutoTArray<CompositableForwarder::TimedTextureClient,1> textures;
741
0
    CompositableForwarder::TimedTextureClient* t = textures.AppendElement();
742
0
    t->mTextureClient = remoteBuffer->GetClient();
743
0
    IntSize size = remoteBuffer->GetClient()->GetSize();
744
0
    t->mPictureRect = nsIntRect(0, 0, size.width, size.height);
745
0
    GetForwarder()->UseTextures(this, textures);
746
0
  }
747
0
748
0
  // This forces a synchronous transaction, so we can swap buffers now
749
0
  // and know that we'll have sole ownership of the old front buffer
750
0
  // by the time we paint next.
751
0
  mForwarder->UpdateTextureRegion(this,
752
0
                                  ThebesBufferData(remoteBuffer->BufferRect(),
753
0
                                                   remoteBuffer->BufferRotation()),
754
0
                                  updatedRegion);
755
0
  SwapBuffers(updatedRegion);
756
0
}
757
758
void
759
ContentClientRemoteBuffer::Dump(std::stringstream& aStream,
760
                                const char* aPrefix,
761
                                bool aDumpHtml, TextureDumpMode aCompress)
762
0
{
763
0
  RemoteRotatedBuffer* remoteBuffer = GetRemoteBuffer();
764
0
765
0
  // TODO We should combine the OnWhite/OnBlack here an just output a single image.
766
0
  if (!aDumpHtml) {
767
0
    aStream << "\n" << aPrefix << "Surface: ";
768
0
  }
769
0
  CompositableClient::DumpTextureClient(aStream,
770
0
                                        remoteBuffer ? remoteBuffer->GetClient() : nullptr,
771
0
                                        aCompress);
772
0
}
773
774
void
775
ContentClientDoubleBuffered::Dump(std::stringstream& aStream,
776
                                  const char* aPrefix,
777
                                  bool aDumpHtml, TextureDumpMode aCompress)
778
0
{
779
0
  // TODO We should combine the OnWhite/OnBlack here an just output a single image.
780
0
  if (!aDumpHtml) {
781
0
    aStream << "\n" << aPrefix << "Surface: ";
782
0
  }
783
0
  CompositableClient::DumpTextureClient(aStream,
784
0
                                        mFrontBuffer ? mFrontBuffer->GetClient() : nullptr,
785
0
                                        aCompress);
786
0
}
787
788
void
789
ContentClientDoubleBuffered::Clear()
790
0
{
791
0
  ContentClient::Clear();
792
0
  mFrontBuffer = nullptr;
793
0
}
794
795
void
796
ContentClientDoubleBuffered::SwapBuffers(const nsIntRegion& aFrontUpdatedRegion)
797
0
{
798
0
  mFrontUpdatedRegion = aFrontUpdatedRegion;
799
0
800
0
  RefPtr<RemoteRotatedBuffer> frontBuffer = mFrontBuffer;
801
0
  RefPtr<RemoteRotatedBuffer> backBuffer = GetRemoteBuffer();
802
0
803
0
  std::swap(frontBuffer, backBuffer);
804
0
805
0
  mFrontBuffer = frontBuffer;
806
0
  mBuffer = backBuffer;
807
0
808
0
  mFrontAndBackBufferDiffer = true;
809
0
}
810
811
ContentClient::PaintState
812
ContentClientDoubleBuffered::BeginPaint(PaintedLayer* aLayer,
813
                                        uint32_t aFlags)
814
0
{
815
0
  EnsureBackBufferIfFrontBuffer();
816
0
817
0
  mIsNewBuffer = false;
818
0
819
0
  if (!mFrontBuffer || !mBuffer) {
820
0
    mFrontAndBackBufferDiffer = false;
821
0
  }
822
0
823
0
  if (mFrontAndBackBufferDiffer) {
824
0
    if (mFrontBuffer->DidSelfCopy()) {
825
0
      // We can't easily draw our front buffer into us, since we're going to be
826
0
      // copying stuff around anyway it's easiest if we just move our situation
827
0
      // to non-rotated while we're at it. If this situation occurs we'll have
828
0
      // hit a self-copy path in PaintThebes before as well anyway.
829
0
      gfx::IntRect backBufferRect = mBuffer->BufferRect();
830
0
      backBufferRect.MoveTo(mFrontBuffer->BufferRect().TopLeft());
831
0
832
0
      mBuffer->SetBufferRect(backBufferRect);
833
0
      mBuffer->SetBufferRotation(IntPoint(0,0));
834
0
    } else {
835
0
      mBuffer->SetBufferRect(mFrontBuffer->BufferRect());
836
0
      mBuffer->SetBufferRotation(mFrontBuffer->BufferRotation());
837
0
    }
838
0
  }
839
0
840
0
  return ContentClient::BeginPaint(aLayer, aFlags);
841
0
}
842
843
// Sync front/back buffers content
844
// After executing, the new back buffer has the same (interesting) pixels as
845
// the new front buffer, and mValidRegion et al. are correct wrt the new
846
// back buffer (i.e. as they were for the old back buffer)
847
void
848
ContentClientDoubleBuffered::FinalizeFrame(PaintState& aPaintState)
849
0
{
850
0
  if (!mFrontAndBackBufferDiffer) {
851
0
    MOZ_ASSERT(!mFrontBuffer || !mFrontBuffer->DidSelfCopy(),
852
0
               "If the front buffer did a self copy then our front and back buffer must be different.");
853
0
    return;
854
0
  }
855
0
856
0
  MOZ_ASSERT(mFrontBuffer && mBuffer);
857
0
  if (!mFrontBuffer || !mBuffer) {
858
0
    return;
859
0
  }
860
0
861
0
  MOZ_LAYERS_LOG(("BasicShadowableThebes(%p): reading back <x=%d,y=%d,w=%d,h=%d>",
862
0
                  this,
863
0
                  mFrontUpdatedRegion.GetBounds().X(),
864
0
                  mFrontUpdatedRegion.GetBounds().Y(),
865
0
                  mFrontUpdatedRegion.GetBounds().Width(),
866
0
                  mFrontUpdatedRegion.GetBounds().Height()));
867
0
868
0
  mFrontAndBackBufferDiffer = false;
869
0
870
0
  nsIntRegion updateRegion = mFrontUpdatedRegion;
871
0
  if (mFrontBuffer->DidSelfCopy()) {
872
0
    mFrontBuffer->ClearDidSelfCopy();
873
0
    updateRegion = mBuffer->BufferRect();
874
0
  }
875
0
876
0
  // No point in sync'ing what we are going to draw over anyway. And if there is
877
0
  // nothing to sync at all, there is nothing to do and we can go home early.
878
0
  updateRegion.Sub(updateRegion, aPaintState.mRegionToDraw);
879
0
  if (updateRegion.IsEmpty()) {
880
0
    return;
881
0
  }
882
0
883
0
  OpenMode openMode = aPaintState.mAsyncPaint
884
0
                        ? OpenMode::OPEN_READ_ASYNC
885
0
                        : OpenMode::OPEN_READ_ONLY;
886
0
887
0
  if (mFrontBuffer->Lock(openMode)) {
888
0
    mBuffer->UpdateDestinationFrom(*mFrontBuffer, updateRegion.GetBounds());
889
0
890
0
    if (aPaintState.mAsyncPaint) {
891
0
      aPaintState.mAsyncTask->mClients.AppendElement(mFrontBuffer->GetClient());
892
0
      if (mFrontBuffer->GetClientOnWhite()) {
893
0
        aPaintState.mAsyncTask->mClients.AppendElement(mFrontBuffer->GetClientOnWhite());
894
0
      }
895
0
    }
896
0
897
0
    mFrontBuffer->Unlock();
898
0
  }
899
0
}
900
901
void
902
ContentClientDoubleBuffered::EnsureBackBufferIfFrontBuffer()
903
0
{
904
0
  if (!mBuffer && mFrontBuffer) {
905
0
    mBuffer = CreateBufferInternal(mFrontBuffer->BufferRect(),
906
0
                                   mFrontBuffer->GetFormat(),
907
0
                                   mTextureFlags);
908
0
    MOZ_ASSERT(mBuffer);
909
0
  }
910
0
}
911
912
} // namespace layers
913
} // namespace mozilla