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 "ImageClient.h"
8 
9 #include <stdint.h>  // for uint32_t
10 
11 #include "ClientLayerManager.h"  // for ClientLayer
12 #include "ImageContainer.h"      // for Image, PlanarYCbCrImage, etc
13 #include "ImageTypes.h"          // for ImageFormat::PLANAR_YCBCR, etc
14 #include "GLImages.h"            // for SurfaceTextureImage::Data, etc
15 #include "gfx2DGlue.h"           // for ImageFormatToSurfaceFormat
16 #include "gfxPlatform.h"         // for gfxPlatform
17 #include "mozilla/Assertions.h"  // for MOZ_ASSERT, etc
18 #include "mozilla/RefPtr.h"      // for RefPtr, already_AddRefed
19 #include "mozilla/gfx/2D.h"
20 #include "mozilla/gfx/BaseSize.h"               // for BaseSize
21 #include "mozilla/gfx/Point.h"                  // for IntSize
22 #include "mozilla/gfx/Types.h"                  // for SurfaceFormat, etc
23 #include "mozilla/layers/CompositableClient.h"  // for CompositableClient
24 #include "mozilla/layers/CompositableForwarder.h"
25 #include "mozilla/layers/CompositorTypes.h"  // for CompositableType, etc
26 #include "mozilla/layers/ISurfaceAllocator.h"
27 #include "mozilla/layers/LayersSurfaces.h"    // for SurfaceDescriptor, etc
28 #include "mozilla/layers/ShadowLayers.h"      // for ShadowLayerForwarder
29 #include "mozilla/layers/TextureClient.h"     // for TextureClient, etc
30 #include "mozilla/layers/TextureClientOGL.h"  // for SurfaceTextureClient
31 #include "mozilla/mozalloc.h"                 // for operator delete, etc
32 #include "nsCOMPtr.h"                         // for already_AddRefed
33 #include "nsDebug.h"                          // for NS_WARNING, NS_ASSERTION
34 #include "nsISupportsImpl.h"                  // for Image::Release, etc
35 #include "nsRect.h"                           // for mozilla::gfx::IntRect
36 
37 namespace mozilla {
38 namespace layers {
39 
40 using namespace mozilla::gfx;
41 
42 /* static */
CreateImageClient(CompositableType aCompositableHostType,CompositableForwarder * aForwarder,TextureFlags aFlags)43 already_AddRefed<ImageClient> ImageClient::CreateImageClient(
44     CompositableType aCompositableHostType, CompositableForwarder* aForwarder,
45     TextureFlags aFlags) {
46   RefPtr<ImageClient> result = nullptr;
47   switch (aCompositableHostType) {
48     case CompositableType::IMAGE:
49       result =
50           new ImageClientSingle(aForwarder, aFlags, CompositableType::IMAGE);
51       break;
52     case CompositableType::IMAGE_BRIDGE:
53       result = new ImageClientBridge(aForwarder, aFlags);
54       break;
55     case CompositableType::UNKNOWN:
56       result = nullptr;
57       break;
58     default:
59       MOZ_CRASH("GFX: unhandled program type image");
60   }
61 
62   NS_ASSERTION(result, "Failed to create ImageClient");
63 
64   return result.forget();
65 }
66 
RemoveTexture(TextureClient * aTexture)67 void ImageClient::RemoveTexture(TextureClient* aTexture) {
68   GetForwarder()->RemoveTextureFromCompositable(this, aTexture);
69 }
70 
ImageClientSingle(CompositableForwarder * aFwd,TextureFlags aFlags,CompositableType aType)71 ImageClientSingle::ImageClientSingle(CompositableForwarder* aFwd,
72                                      TextureFlags aFlags,
73                                      CompositableType aType)
74     : ImageClient(aFwd, aFlags, aType) {}
75 
GetTextureInfo() const76 TextureInfo ImageClientSingle::GetTextureInfo() const {
77   return TextureInfo(CompositableType::IMAGE);
78 }
79 
FlushAllImages()80 void ImageClientSingle::FlushAllImages() {
81   for (auto& b : mBuffers) {
82     // It should be safe to just assume a default render root here, even if
83     // the texture actually presents in a content render root, as the only
84     // risk would be if the content render root has not / is not going to
85     // generate a frame before the texture gets cleared.
86     RemoveTexture(b.mTextureClient);
87   }
88   mBuffers.Clear();
89 }
90 
91 /* static */
CreateTextureClientForImage(Image * aImage,KnowsCompositor * aKnowsCompositor)92 already_AddRefed<TextureClient> ImageClient::CreateTextureClientForImage(
93     Image* aImage, KnowsCompositor* aKnowsCompositor) {
94   RefPtr<TextureClient> texture;
95   if (aImage->GetFormat() == ImageFormat::PLANAR_YCBCR) {
96     PlanarYCbCrImage* ycbcr = static_cast<PlanarYCbCrImage*>(aImage);
97     const PlanarYCbCrData* data = ycbcr->GetData();
98     if (!data) {
99       return nullptr;
100     }
101     texture = TextureClient::CreateForYCbCr(
102         aKnowsCompositor, data->GetPictureRect(), data->mYSize, data->mYStride,
103         data->mCbCrSize, data->mCbCrStride, data->mStereoMode,
104         data->mColorDepth, data->mYUVColorSpace, data->mColorRange,
105         TextureFlags::DEFAULT);
106     if (!texture) {
107       return nullptr;
108     }
109 
110     TextureClientAutoLock autoLock(texture, OpenMode::OPEN_WRITE_ONLY);
111     if (!autoLock.Succeeded()) {
112       return nullptr;
113     }
114 
115     bool status = UpdateYCbCrTextureClient(texture, *data);
116     MOZ_ASSERT(status);
117     if (!status) {
118       return nullptr;
119     }
120 #ifdef MOZ_WIDGET_ANDROID
121   } else if (aImage->GetFormat() == ImageFormat::SURFACE_TEXTURE) {
122     gfx::IntSize size = aImage->GetSize();
123     SurfaceTextureImage* typedImage = aImage->AsSurfaceTextureImage();
124     texture = AndroidSurfaceTextureData::CreateTextureClient(
125         typedImage->GetHandle(), size, typedImage->GetContinuous(),
126         typedImage->GetOriginPos(), typedImage->GetHasAlpha(),
127         aKnowsCompositor->GetTextureForwarder(), TextureFlags::DEFAULT);
128 #endif
129   } else {
130     RefPtr<gfx::SourceSurface> surface = aImage->GetAsSourceSurface();
131     MOZ_ASSERT(surface);
132     texture = TextureClient::CreateForDrawing(
133         aKnowsCompositor, surface->GetFormat(), aImage->GetSize(),
134         BackendSelector::Content, TextureFlags::DEFAULT);
135     if (!texture) {
136       return nullptr;
137     }
138 
139     MOZ_ASSERT(texture->CanExposeDrawTarget());
140 
141     if (!texture->Lock(OpenMode::OPEN_WRITE_ONLY)) {
142       return nullptr;
143     }
144 
145     {
146       // We must not keep a reference to the DrawTarget after it has been
147       // unlocked.
148       DrawTarget* dt = texture->BorrowDrawTarget();
149       if (!dt) {
150         gfxWarning()
151             << "ImageClientSingle::UpdateImage failed in BorrowDrawTarget";
152         return nullptr;
153       }
154       MOZ_ASSERT(surface.get());
155       dt->CopySurface(surface, IntRect(IntPoint(), surface->GetSize()),
156                       IntPoint());
157     }
158 
159     texture->Unlock();
160   }
161   return texture.forget();
162 }
163 
UpdateImage(ImageContainer * aContainer,uint32_t aContentFlags)164 bool ImageClientSingle::UpdateImage(ImageContainer* aContainer,
165                                     uint32_t aContentFlags) {
166   AutoTArray<ImageContainer::OwningImage, 4> images;
167   uint32_t generationCounter;
168   aContainer->GetCurrentImages(&images, &generationCounter);
169 
170   if (mLastUpdateGenerationCounter == generationCounter) {
171     return true;
172   }
173   mLastUpdateGenerationCounter = generationCounter;
174 
175   // Don't try to update to invalid images.
176   images.RemoveElementsBy(
177       [](const auto& image) { return !image.mImage->IsValid(); });
178   if (images.IsEmpty()) {
179     // This can happen if a ClearAllImages raced with SetCurrentImages from
180     // another thread and ClearImagesFromImageBridge ran after the
181     // SetCurrentImages call but before UpdateImageClientNow.
182     // This can also happen if all images in the list are invalid.
183     // We return true because the caller would attempt to recreate the
184     // ImageClient otherwise, and that isn't going to help.
185     for (auto& b : mBuffers) {
186       RemoveTexture(b.mTextureClient);
187     }
188     mBuffers.Clear();
189     return true;
190   }
191 
192   nsTArray<Buffer> newBuffers;
193   AutoTArray<CompositableForwarder::TimedTextureClient, 4> textures;
194 
195   for (auto& img : images) {
196     Image* image = img.mImage;
197 
198     RefPtr<TextureClient> texture = image->GetTextureClient(GetForwarder());
199     const bool hasTextureClient = !!texture;
200 
201     for (int32_t i = mBuffers.Length() - 1; i >= 0; --i) {
202       if (mBuffers[i].mImageSerial == image->GetSerial()) {
203         if (hasTextureClient) {
204           MOZ_ASSERT(image->GetTextureClient(GetForwarder()) ==
205                      mBuffers[i].mTextureClient);
206         } else {
207           texture = mBuffers[i].mTextureClient;
208         }
209         // Remove this element from mBuffers so mBuffers only contains
210         // images that aren't present in 'images'
211         mBuffers.RemoveElementAt(i);
212       }
213     }
214 
215     if (!texture) {
216       // Slow path, we should not be hitting it very often and if we do it means
217       // we are using an Image class that is not backed by textureClient and we
218       // should fix it.
219       texture = CreateTextureClientForImage(image, GetForwarder());
220     }
221 
222     if (!texture) {
223       return false;
224     }
225 
226     // We check if the texture's allocator is still open, since in between media
227     // decoding a frame and adding it to the compositable, we could have
228     // restarted the GPU process.
229     if (!texture->GetAllocator()->IPCOpen()) {
230       continue;
231     }
232     if (!AddTextureClient(texture)) {
233       return false;
234     }
235 
236     CompositableForwarder::TimedTextureClient* t = textures.AppendElement();
237     t->mTextureClient = texture;
238     t->mTimeStamp = img.mTimeStamp;
239     t->mPictureRect = image->GetPictureRect();
240     t->mFrameID = img.mFrameID;
241     t->mProducerID = img.mProducerID;
242 
243     Buffer* newBuf = newBuffers.AppendElement();
244     newBuf->mImageSerial = image->GetSerial();
245     newBuf->mTextureClient = texture;
246 
247     texture->SyncWithObject(GetForwarder()->GetSyncObject());
248   }
249 
250   GetForwarder()->UseTextures(this, textures);
251 
252   for (auto& b : mBuffers) {
253     RemoveTexture(b.mTextureClient);
254   }
255   mBuffers = std::move(newBuffers);
256 
257   return true;
258 }
259 
GetForwardedTexture()260 RefPtr<TextureClient> ImageClientSingle::GetForwardedTexture() {
261   if (mBuffers.Length() == 0) {
262     return nullptr;
263   }
264   return mBuffers[0].mTextureClient;
265 }
266 
AddTextureClient(TextureClient * aTexture)267 bool ImageClientSingle::AddTextureClient(TextureClient* aTexture) {
268   MOZ_ASSERT((mTextureFlags & aTexture->GetFlags()) == mTextureFlags);
269   return CompositableClient::AddTextureClient(aTexture);
270 }
271 
OnDetach()272 void ImageClientSingle::OnDetach() { mBuffers.Clear(); }
273 
ImageClient(CompositableForwarder * aFwd,TextureFlags aFlags,CompositableType aType)274 ImageClient::ImageClient(CompositableForwarder* aFwd, TextureFlags aFlags,
275                          CompositableType aType)
276     : CompositableClient(aFwd, aFlags),
277       mLayer(nullptr),
278       mType(aType),
279       mLastUpdateGenerationCounter(0) {}
280 
ImageClientBridge(CompositableForwarder * aFwd,TextureFlags aFlags)281 ImageClientBridge::ImageClientBridge(CompositableForwarder* aFwd,
282                                      TextureFlags aFlags)
283     : ImageClient(aFwd, aFlags, CompositableType::IMAGE_BRIDGE) {}
284 
UpdateImage(ImageContainer * aContainer,uint32_t aContentFlags)285 bool ImageClientBridge::UpdateImage(ImageContainer* aContainer,
286                                     uint32_t aContentFlags) {
287   if (!GetForwarder() || !mLayer) {
288     return false;
289   }
290   if (mAsyncContainerHandle == aContainer->GetAsyncContainerHandle()) {
291     return true;
292   }
293 
294   mAsyncContainerHandle = aContainer->GetAsyncContainerHandle();
295   if (!mAsyncContainerHandle) {
296     // If we couldn't contact a working ImageBridgeParent, just return.
297     return true;
298   }
299 
300   static_cast<ShadowLayerForwarder*>(GetForwarder())
301       ->AttachAsyncCompositable(mAsyncContainerHandle, mLayer);
302   return true;
303 }
304 
305 }  // namespace layers
306 }  // namespace mozilla
307