Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/gfx/layers/composite/ImageHost.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 "ImageHost.h"
8
9
#include "LayersLogging.h"              // for AppendToString
10
#include "composite/CompositableHost.h"  // for CompositableHost, etc
11
#include "ipc/IPCMessageUtils.h"        // for null_t
12
#include "mozilla/Move.h"
13
#include "mozilla/layers/Compositor.h"  // for Compositor
14
#include "mozilla/layers/Effects.h"     // for TexturedEffect, Effect, etc
15
#include "mozilla/layers/LayerManagerComposite.h"     // for TexturedEffect, Effect, etc
16
#include "nsAString.h"
17
#include "nsDebug.h"                    // for NS_WARNING, NS_ASSERTION
18
#include "nsPrintfCString.h"            // for nsPrintfCString
19
#include "nsString.h"                   // for nsAutoCString
20
21
namespace mozilla {
22
23
using namespace gfx;
24
25
namespace layers {
26
27
class ISurfaceAllocator;
28
29
ImageHost::ImageHost(const TextureInfo& aTextureInfo)
30
  : CompositableHost(aTextureInfo)
31
  , ImageComposite()
32
  , mLocked(false)
33
0
{}
34
35
ImageHost::~ImageHost()
36
0
{
37
0
}
38
39
void
40
ImageHost::UseTextureHost(const nsTArray<TimedTexture>& aTextures)
41
0
{
42
0
  MOZ_ASSERT(!mLocked);
43
0
44
0
  CompositableHost::UseTextureHost(aTextures);
45
0
  MOZ_ASSERT(aTextures.Length() >= 1);
46
0
47
0
  nsTArray<TimedImage> newImages;
48
0
49
0
  for (uint32_t i = 0; i < aTextures.Length(); ++i) {
50
0
    const TimedTexture& t = aTextures[i];
51
0
    MOZ_ASSERT(t.mTexture);
52
0
    if (i + 1 < aTextures.Length() &&
53
0
        t.mProducerID == mLastProducerID && t.mFrameID < mLastFrameID) {
54
0
      // Ignore frames before a frame that we already composited. We don't
55
0
      // ever want to display these frames. This could be important if
56
0
      // the frame producer adjusts timestamps (e.g. to track the audio clock)
57
0
      // and the new frame times are earlier.
58
0
      continue;
59
0
    }
60
0
    TimedImage& img = *newImages.AppendElement();
61
0
    img.mTextureHost = t.mTexture;
62
0
    img.mTimeStamp = t.mTimeStamp;
63
0
    img.mPictureRect = t.mPictureRect;
64
0
    img.mFrameID = t.mFrameID;
65
0
    img.mProducerID = t.mProducerID;
66
0
    img.mTextureHost->SetCropRect(img.mPictureRect);
67
0
    img.mTextureHost->Updated();
68
0
  }
69
0
70
0
  SetImages(std::move(newImages));
71
0
72
0
  // If we only have one image we can upload it right away, otherwise we'll upload
73
0
  // on-demand during composition after we have picked the proper timestamp.
74
0
  if (ImagesCount() == 1) {
75
0
    SetCurrentTextureHost(GetImage(0)->mTextureHost);
76
0
  }
77
0
78
0
  HostLayerManager* lm = GetLayerManager();
79
0
80
0
  // Video producers generally send replacement images with the same frameID but
81
0
  // slightly different timestamps in order to sync with the audio clock. This
82
0
  // means that any CompositeUntil() call we made in Composite() may no longer
83
0
  // guarantee that we'll composite until the next frame is ready. Fix that here.
84
0
  if (lm && mLastFrameID >= 0) {
85
0
    for (const auto& img : Images()) {
86
0
      bool frameComesAfter =
87
0
        img.mFrameID > mLastFrameID || img.mProducerID != mLastProducerID;
88
0
      if (frameComesAfter && !img.mTimeStamp.IsNull()) {
89
0
        lm->CompositeUntil(img.mTimeStamp +
90
0
                           TimeDuration::FromMilliseconds(BIAS_TIME_MS));
91
0
        break;
92
0
      }
93
0
    }
94
0
  }
95
0
}
96
97
void
98
ImageHost::SetCurrentTextureHost(TextureHost* aTexture)
99
0
{
100
0
  if (aTexture == mCurrentTextureHost.get()) {
101
0
    return;
102
0
  }
103
0
104
0
  bool swapTextureSources = !!mCurrentTextureHost && !!mCurrentTextureSource
105
0
                            && mCurrentTextureHost->HasIntermediateBuffer();
106
0
107
0
  if (swapTextureSources) {
108
0
    auto dataSource = mCurrentTextureSource->AsDataTextureSource();
109
0
    if (dataSource) {
110
0
      // The current textureHost has an internal buffer in the form of the
111
0
      // DataTextureSource. Removing the ownership of the texture source
112
0
      // will enable the next texture host we bind to the texture source to
113
0
      // acquire it instead of creating a new one. This is desirable in
114
0
      // ImageHost because the current texture won't be used again with the
115
0
      // same content. It wouldn't be desirable with ContentHost for instance,
116
0
      // because the latter reuses the texture's valid regions.
117
0
      dataSource->SetOwner(nullptr);
118
0
    }
119
0
120
0
    RefPtr<TextureSource> tmp = mExtraTextureSource;
121
0
    mExtraTextureSource = mCurrentTextureSource.get();
122
0
    mCurrentTextureSource = tmp;
123
0
  } else {
124
0
    mExtraTextureSource = nullptr;
125
0
  }
126
0
127
0
  mCurrentTextureHost = aTexture;
128
0
  mCurrentTextureHost->PrepareTextureSource(mCurrentTextureSource);
129
0
}
130
131
void
132
ImageHost::CleanupResources()
133
0
{
134
0
  mExtraTextureSource = nullptr;
135
0
  mCurrentTextureSource = nullptr;
136
0
  mCurrentTextureHost = nullptr;
137
0
}
138
139
void
140
ImageHost::RemoveTextureHost(TextureHost* aTexture)
141
0
{
142
0
  MOZ_ASSERT(!mLocked);
143
0
144
0
  CompositableHost::RemoveTextureHost(aTexture);
145
0
  RemoveImagesWithTextureHost(aTexture);
146
0
}
147
148
TimeStamp
149
ImageHost::GetCompositionTime() const
150
0
{
151
0
  TimeStamp time;
152
0
  if (HostLayerManager* lm = GetLayerManager()) {
153
0
    time = lm->GetCompositionTime();
154
0
  }
155
0
  return time;
156
0
}
157
158
TextureHost*
159
ImageHost::GetAsTextureHost(IntRect* aPictureRect)
160
0
{
161
0
  const TimedImage* img = ChooseImage();
162
0
  if (!img) {
163
0
    return nullptr;
164
0
  }
165
0
  SetCurrentTextureHost(img->mTextureHost);
166
0
  if (aPictureRect) {
167
0
    *aPictureRect = img->mPictureRect;
168
0
  }
169
0
  return img->mTextureHost;
170
0
}
171
172
void ImageHost::Attach(Layer* aLayer,
173
                       TextureSourceProvider* aProvider,
174
                       AttachFlags aFlags)
175
0
{
176
0
  CompositableHost::Attach(aLayer, aProvider, aFlags);
177
0
  for (const auto& img : Images()) {
178
0
    img.mTextureHost->SetTextureSourceProvider(aProvider);
179
0
    img.mTextureHost->Updated();
180
0
  }
181
0
}
182
183
void
184
ImageHost::Composite(Compositor* aCompositor,
185
                     LayerComposite* aLayer,
186
                     EffectChain& aEffectChain,
187
                     float aOpacity,
188
                     const gfx::Matrix4x4& aTransform,
189
                     const gfx::SamplingFilter aSamplingFilter,
190
                     const gfx::IntRect& aClipRect,
191
                     const nsIntRegion* aVisibleRegion,
192
                     const Maybe<gfx::Polygon>& aGeometry)
193
0
{
194
0
  RenderInfo info;
195
0
  if (!PrepareToRender(aCompositor, &info)) {
196
0
    return;
197
0
  }
198
0
199
0
  const TimedImage* img = info.img;
200
0
201
0
  {
202
0
    AutoLockCompositableHost autoLock(this);
203
0
    if (autoLock.Failed()) {
204
0
      NS_WARNING("failed to lock front buffer");
205
0
      return;
206
0
    }
207
0
208
0
    if (!mCurrentTextureHost->BindTextureSource(mCurrentTextureSource)) {
209
0
      return;
210
0
    }
211
0
212
0
    if (!mCurrentTextureSource) {
213
0
      // BindTextureSource above should have returned false!
214
0
      MOZ_ASSERT(false);
215
0
      return;
216
0
    }
217
0
218
0
    bool isAlphaPremultiplied =
219
0
        !(mCurrentTextureHost->GetFlags() & TextureFlags::NON_PREMULTIPLIED);
220
0
    RefPtr<TexturedEffect> effect =
221
0
        CreateTexturedEffect(mCurrentTextureHost,
222
0
            mCurrentTextureSource.get(), aSamplingFilter, isAlphaPremultiplied);
223
0
    if (!effect) {
224
0
      return;
225
0
    }
226
0
227
0
    if (!aCompositor->SupportsEffect(effect->mType)) {
228
0
      return;
229
0
    }
230
0
231
0
    DiagnosticFlags diagnosticFlags = DiagnosticFlags::IMAGE;
232
0
    if (effect->mType == EffectTypes::NV12) {
233
0
      diagnosticFlags |= DiagnosticFlags::NV12;
234
0
    } else if (effect->mType == EffectTypes::YCBCR) {
235
0
      diagnosticFlags |= DiagnosticFlags::YCBCR;
236
0
    }
237
0
238
0
    aEffectChain.mPrimaryEffect = effect;
239
0
    gfx::Rect pictureRect(0, 0, img->mPictureRect.Width(), img->mPictureRect.Height());
240
0
    BigImageIterator* it = mCurrentTextureSource->AsBigImageIterator();
241
0
    if (it) {
242
0
243
0
      // This iteration does not work if we have multiple texture sources here
244
0
      // (e.g. 3 YCbCr textures). There's nothing preventing the different
245
0
      // planes from having different resolutions or tile sizes. For example, a
246
0
      // YCbCr frame could have Cb and Cr planes that are half the resolution of
247
0
      // the Y plane, in such a way that the Y plane overflows the maximum
248
0
      // texture size and the Cb and Cr planes do not. Then the Y plane would be
249
0
      // split into multiple tiles and the Cb and Cr planes would just be one
250
0
      // tile each.
251
0
      // To handle the general case correctly, we'd have to create a grid of
252
0
      // intersected tiles over all planes, and then draw each grid tile using
253
0
      // the corresponding source tiles from all planes, with appropriate
254
0
      // per-plane per-tile texture coords.
255
0
      // DrawQuad currently assumes that all planes use the same texture coords.
256
0
      MOZ_ASSERT(it->GetTileCount() == 1 || !mCurrentTextureSource->GetNextSibling(),
257
0
                 "Can't handle multi-plane BigImages");
258
0
259
0
      it->BeginBigImageIteration();
260
0
      do {
261
0
        IntRect tileRect = it->GetTileRect();
262
0
        gfx::Rect rect(tileRect.X(), tileRect.Y(), tileRect.Width(), tileRect.Height());
263
0
        rect = rect.Intersect(pictureRect);
264
0
        effect->mTextureCoords = Rect(Float(rect.X() - tileRect.X()) / tileRect.Width(),
265
0
                                      Float(rect.Y() - tileRect.Y()) / tileRect.Height(),
266
0
                                      Float(rect.Width()) / tileRect.Width(),
267
0
                                      Float(rect.Height()) / tileRect.Height());
268
0
        if (img->mTextureHost->GetFlags() & TextureFlags::ORIGIN_BOTTOM_LEFT) {
269
0
          effect->mTextureCoords.SetRectY(effect->mTextureCoords.YMost(),
270
0
                                          -effect->mTextureCoords.Height());
271
0
        }
272
0
        aCompositor->DrawGeometry(rect, aClipRect, aEffectChain,
273
0
                                  aOpacity, aTransform, aGeometry);
274
0
        aCompositor->DrawDiagnostics(diagnosticFlags | DiagnosticFlags::BIGIMAGE,
275
0
                                     rect, aClipRect, aTransform, mFlashCounter);
276
0
      } while (it->NextTile());
277
0
      it->EndBigImageIteration();
278
0
      // layer border
279
0
      aCompositor->DrawDiagnostics(diagnosticFlags, pictureRect,
280
0
                                   aClipRect, aTransform, mFlashCounter);
281
0
    } else {
282
0
      IntSize textureSize = mCurrentTextureSource->GetSize();
283
0
      effect->mTextureCoords = Rect(Float(img->mPictureRect.X()) / textureSize.width,
284
0
                                    Float(img->mPictureRect.Y()) / textureSize.height,
285
0
                                    Float(img->mPictureRect.Width()) / textureSize.width,
286
0
                                    Float(img->mPictureRect.Height()) / textureSize.height);
287
0
288
0
      if (img->mTextureHost->GetFlags() & TextureFlags::ORIGIN_BOTTOM_LEFT) {
289
0
        effect->mTextureCoords.SetRectY(effect->mTextureCoords.YMost(),
290
0
                                        -effect->mTextureCoords.Height());
291
0
      }
292
0
293
0
      aCompositor->DrawGeometry(pictureRect, aClipRect, aEffectChain,
294
0
                                aOpacity, aTransform, aGeometry);
295
0
      aCompositor->DrawDiagnostics(diagnosticFlags,
296
0
                                   pictureRect, aClipRect,
297
0
                                   aTransform, mFlashCounter);
298
0
    }
299
0
  }
300
0
301
0
  FinishRendering(info);
302
0
}
303
304
bool
305
ImageHost::PrepareToRender(TextureSourceProvider* aProvider, RenderInfo* aOutInfo)
306
0
{
307
0
  HostLayerManager* lm = GetLayerManager();
308
0
  if (!lm) {
309
0
    return false;
310
0
  }
311
0
312
0
  int imageIndex = ChooseImageIndex();
313
0
  if (imageIndex < 0) {
314
0
    return false;
315
0
  }
316
0
317
0
  if (uint32_t(imageIndex) + 1 < ImagesCount()) {
318
0
    lm->CompositeUntil(GetImage(imageIndex + 1)->mTimeStamp +
319
0
                       TimeDuration::FromMilliseconds(BIAS_TIME_MS));
320
0
  }
321
0
322
0
  const TimedImage* img = GetImage(imageIndex);
323
0
  img->mTextureHost->SetTextureSourceProvider(aProvider);
324
0
  SetCurrentTextureHost(img->mTextureHost);
325
0
326
0
  aOutInfo->imageIndex = imageIndex;
327
0
  aOutInfo->img = img;
328
0
  aOutInfo->host = mCurrentTextureHost;
329
0
  return true;
330
0
}
331
332
RefPtr<TextureSource>
333
ImageHost::AcquireTextureSource(const RenderInfo& aInfo)
334
0
{
335
0
  MOZ_ASSERT(aInfo.host == mCurrentTextureHost);
336
0
  if (!aInfo.host->AcquireTextureSource(mCurrentTextureSource)) {
337
0
    return nullptr;
338
0
  }
339
0
  return mCurrentTextureSource.get();
340
0
}
341
342
void
343
ImageHost::FinishRendering(const RenderInfo& aInfo)
344
0
{
345
0
  HostLayerManager* lm = GetLayerManager();
346
0
  const TimedImage* img = aInfo.img;
347
0
  int imageIndex = aInfo.imageIndex;
348
0
349
0
  if (mLastFrameID != img->mFrameID || mLastProducerID != img->mProducerID) {
350
0
    if (mAsyncRef) {
351
0
      ImageCompositeNotificationInfo info;
352
0
      info.mImageBridgeProcessId = mAsyncRef.mProcessId;
353
0
      info.mNotification = ImageCompositeNotification(
354
0
        mAsyncRef.mHandle,
355
0
        img->mTimeStamp, lm->GetCompositionTime(),
356
0
        img->mFrameID, img->mProducerID);
357
0
      lm->AppendImageCompositeNotification(info);
358
0
    }
359
0
    mLastFrameID = img->mFrameID;
360
0
    mLastProducerID = img->mProducerID;
361
0
  }
362
0
363
0
  // Update mBias last. This can change which frame ChooseImage(Index) would
364
0
  // return, and we don't want to do that until we've finished compositing
365
0
  // since callers of ChooseImage(Index) assume the same image will be chosen
366
0
  // during a given composition. This must happen after autoLock's
367
0
  // destructor!
368
0
    UpdateBias(imageIndex);
369
0
}
370
371
void
372
ImageHost::SetTextureSourceProvider(TextureSourceProvider* aProvider)
373
0
{
374
0
  if (mTextureSourceProvider != aProvider) {
375
0
    for (const auto& img : Images()) {
376
0
      img.mTextureHost->SetTextureSourceProvider(aProvider);
377
0
    }
378
0
  }
379
0
  CompositableHost::SetTextureSourceProvider(aProvider);
380
0
}
381
382
void
383
ImageHost::PrintInfo(std::stringstream& aStream, const char* aPrefix)
384
0
{
385
0
  aStream << aPrefix;
386
0
  aStream << nsPrintfCString("ImageHost (0x%p)", this).get();
387
0
388
0
  nsAutoCString pfx(aPrefix);
389
0
  pfx += "  ";
390
0
  for (const auto& img : Images()) {
391
0
    aStream << "\n";
392
0
    img.mTextureHost->PrintInfo(aStream, pfx.get());
393
0
    AppendToString(aStream, img.mPictureRect, " [picture-rect=", "]");
394
0
  }
395
0
}
396
397
void
398
ImageHost::Dump(std::stringstream& aStream,
399
                const char* aPrefix,
400
                bool aDumpHtml)
401
0
{
402
0
  for (const auto& img : Images()) {
403
0
    aStream << aPrefix;
404
0
    aStream << (aDumpHtml ? "<ul><li>TextureHost: "
405
0
                             : "TextureHost: ");
406
0
    DumpTextureHost(aStream, img.mTextureHost);
407
0
    aStream << (aDumpHtml ? " </li></ul> " : " ");
408
0
  }
409
0
}
410
411
already_AddRefed<gfx::DataSourceSurface>
412
ImageHost::GetAsSurface()
413
0
{
414
0
  const TimedImage* img = ChooseImage();
415
0
  if (img) {
416
0
    return img->mTextureHost->GetAsSurface();
417
0
  }
418
0
  return nullptr;
419
0
}
420
421
bool
422
ImageHost::Lock()
423
0
{
424
0
  MOZ_ASSERT(!mLocked);
425
0
  const TimedImage* img = ChooseImage();
426
0
  if (!img) {
427
0
    return false;
428
0
  }
429
0
430
0
  SetCurrentTextureHost(img->mTextureHost);
431
0
432
0
  if (!mCurrentTextureHost->Lock()) {
433
0
    return false;
434
0
  }
435
0
  mLocked = true;
436
0
  return true;
437
0
}
438
439
void
440
ImageHost::Unlock()
441
0
{
442
0
  MOZ_ASSERT(mLocked);
443
0
444
0
  if (mCurrentTextureHost) {
445
0
    mCurrentTextureHost->Unlock();
446
0
  }
447
0
  mLocked = false;
448
0
}
449
450
IntSize
451
ImageHost::GetImageSize()
452
0
{
453
0
  const TimedImage* img = ChooseImage();
454
0
  if (img) {
455
0
    return IntSize(img->mPictureRect.Width(), img->mPictureRect.Height());
456
0
  }
457
0
  return IntSize();
458
0
}
459
460
bool
461
ImageHost::IsOpaque()
462
0
{
463
0
  const TimedImage* img = ChooseImage();
464
0
  if (!img) {
465
0
    return false;
466
0
  }
467
0
468
0
  if (img->mPictureRect.Width() == 0 ||
469
0
      img->mPictureRect.Height() == 0 ||
470
0
      !img->mTextureHost) {
471
0
    return false;
472
0
  }
473
0
474
0
  gfx::SurfaceFormat format = img->mTextureHost->GetFormat();
475
0
  if (gfx::IsOpaque(format)) {
476
0
    return true;
477
0
  }
478
0
  return false;
479
0
}
480
481
already_AddRefed<TexturedEffect>
482
ImageHost::GenEffect(const gfx::SamplingFilter aSamplingFilter)
483
0
{
484
0
  const TimedImage* img = ChooseImage();
485
0
  if (!img) {
486
0
    return nullptr;
487
0
  }
488
0
  SetCurrentTextureHost(img->mTextureHost);
489
0
  if (!mCurrentTextureHost->BindTextureSource(mCurrentTextureSource)) {
490
0
    return nullptr;
491
0
  }
492
0
  bool isAlphaPremultiplied = true;
493
0
  if (mCurrentTextureHost->GetFlags() & TextureFlags::NON_PREMULTIPLIED) {
494
0
    isAlphaPremultiplied = false;
495
0
  }
496
0
497
0
  return CreateTexturedEffect(mCurrentTextureHost,
498
0
                              mCurrentTextureSource,
499
0
                              aSamplingFilter,
500
0
                              isAlphaPremultiplied);
501
0
}
502
503
} // namespace layers
504
} // namespace mozilla