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