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