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