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 "CanvasClient.h"
8 
9 #include "ClientCanvasLayer.h"  // for ClientCanvasLayer
10 #include "GLContext.h"          // for GLContext
11 #include "GLScreenBuffer.h"     // for GLScreenBuffer
12 #include "ScopedGLHelpers.h"
13 #include "gfx2DGlue.h"    // for ImageFormatToSurfaceFormat
14 #include "gfxPlatform.h"  // for gfxPlatform
15 #include "GLReadTexImageHelper.h"
16 #include "mozilla/gfx/BaseSize.h"  // for BaseSize
17 #include "mozilla/layers/BufferTexture.h"
18 #include "mozilla/layers/AsyncCanvasRenderer.h"
19 #include "mozilla/layers/CompositableForwarder.h"
20 #include "mozilla/layers/CompositorBridgeChild.h"  // for CompositorBridgeChild
21 #include "mozilla/layers/LayersTypes.h"
22 #include "mozilla/layers/TextureClient.h"  // for TextureClient, etc
23 #include "mozilla/layers/TextureClientOGL.h"
24 #include "nsDebug.h"      // for printf_stderr, NS_ASSERTION
25 #include "nsXULAppAPI.h"  // for XRE_GetProcessType, etc
26 #include "TextureClientSharedSurface.h"
27 
28 using namespace mozilla::gfx;
29 using namespace mozilla::gl;
30 
31 namespace mozilla {
32 namespace layers {
33 
CreateCanvasClient(CanvasClientType aType,CompositableForwarder * aForwarder,TextureFlags aFlags)34 /* static */ already_AddRefed<CanvasClient> CanvasClient::CreateCanvasClient(
35     CanvasClientType aType, CompositableForwarder* aForwarder,
36     TextureFlags aFlags) {
37   switch (aType) {
38     case CanvasClientTypeShSurf:
39       return MakeAndAddRef<CanvasClientSharedSurface>(aForwarder, aFlags);
40     case CanvasClientAsync:
41       return MakeAndAddRef<CanvasClientBridge>(aForwarder, aFlags);
42     default:
43       return MakeAndAddRef<CanvasClient2D>(aForwarder, aFlags);
44       break;
45   }
46 }
47 
UpdateAsync(AsyncCanvasRenderer * aRenderer)48 void CanvasClientBridge::UpdateAsync(AsyncCanvasRenderer* aRenderer) {
49   if (!GetForwarder() || !mLayer || !aRenderer ||
50       !aRenderer->GetCanvasClient()) {
51     return;
52   }
53 
54   CompositableHandle asyncID = aRenderer->GetCanvasClientAsyncHandle();
55   if (!asyncID || mAsyncHandle == asyncID) {
56     return;
57   }
58 
59   static_cast<ShadowLayerForwarder*>(GetForwarder())
60       ->AttachAsyncCompositable(asyncID, mLayer);
61   mAsyncHandle = asyncID;
62 }
63 
UpdateFromTexture(TextureClient * aTexture)64 void CanvasClient2D::UpdateFromTexture(TextureClient* aTexture) {
65   MOZ_ASSERT(aTexture);
66 
67   if (!aTexture->IsSharedWithCompositor()) {
68     if (!AddTextureClient(aTexture)) {
69       return;
70     }
71   }
72 
73   mBackBuffer = nullptr;
74   mFrontBuffer = nullptr;
75   mBufferProviderTexture = aTexture;
76 
77   AutoTArray<CompositableForwarder::TimedTextureClient, 1> textures;
78   CompositableForwarder::TimedTextureClient* t = textures.AppendElement();
79   t->mTextureClient = aTexture;
80   t->mPictureRect = nsIntRect(nsIntPoint(0, 0), aTexture->GetSize());
81   t->mFrameID = mFrameID;
82 
83   GetForwarder()->UseTextures(this, textures);
84   aTexture->SyncWithObject(GetForwarder()->GetSyncObject());
85 }
86 
Update(gfx::IntSize aSize,ShareableCanvasRenderer * aCanvasRenderer)87 void CanvasClient2D::Update(gfx::IntSize aSize,
88                             ShareableCanvasRenderer* aCanvasRenderer) {
89   mBufferProviderTexture = nullptr;
90 
91   AutoRemoveTexture autoRemove(this);
92   if (mBackBuffer &&
93       (mBackBuffer->IsReadLocked() || mBackBuffer->GetSize() != aSize)) {
94     autoRemove.mTexture = mBackBuffer;
95     mBackBuffer = nullptr;
96   }
97 
98   bool bufferCreated = false;
99   if (!mBackBuffer) {
100     gfxContentType contentType = aCanvasRenderer->IsOpaque()
101                                      ? gfxContentType::COLOR
102                                      : gfxContentType::COLOR_ALPHA;
103     gfx::SurfaceFormat surfaceFormat =
104         gfxPlatform::GetPlatform()->Optimal2DFormatForContent(contentType);
105     TextureFlags flags = TextureFlags::DEFAULT;
106     if (mTextureFlags & TextureFlags::ORIGIN_BOTTOM_LEFT) {
107       flags |= TextureFlags::ORIGIN_BOTTOM_LEFT;
108     }
109     flags |= TextureFlags::NON_BLOCKING_READ_LOCK;
110 
111     mBackBuffer = CreateTextureClientForCanvas(surfaceFormat, aSize, flags,
112                                                aCanvasRenderer);
113     if (!mBackBuffer) {
114       NS_WARNING("Failed to allocate the TextureClient");
115       return;
116     }
117     MOZ_ASSERT(mBackBuffer->CanExposeDrawTarget());
118 
119     bufferCreated = true;
120   }
121 
122   bool updated = false;
123   {
124     TextureClientAutoLock autoLock(mBackBuffer, OpenMode::OPEN_WRITE_ONLY);
125     if (!autoLock.Succeeded()) {
126       mBackBuffer = nullptr;
127       return;
128     }
129 
130     RefPtr<DrawTarget> target = mBackBuffer->BorrowDrawTarget();
131     if (target) {
132       if (!aCanvasRenderer->UpdateTarget(target)) {
133         NS_WARNING("Failed to copy the canvas into a TextureClient.");
134         return;
135       }
136       updated = true;
137     }
138   }
139 
140   if (bufferCreated && !AddTextureClient(mBackBuffer)) {
141     mBackBuffer = nullptr;
142     return;
143   }
144 
145   if (updated) {
146     AutoTArray<CompositableForwarder::TimedTextureClient, 1> textures;
147     CompositableForwarder::TimedTextureClient* t = textures.AppendElement();
148     t->mTextureClient = mBackBuffer;
149     t->mPictureRect = nsIntRect(nsIntPoint(0, 0), mBackBuffer->GetSize());
150     t->mFrameID = mFrameID;
151     GetForwarder()->UseTextures(this, textures);
152     mBackBuffer->SyncWithObject(GetForwarder()->GetSyncObject());
153   }
154 
155   mBackBuffer.swap(mFrontBuffer);
156 }
157 
CreateTextureClientForCanvas(gfx::SurfaceFormat aFormat,gfx::IntSize aSize,TextureFlags aFlags,ShareableCanvasRenderer * aCanvasRenderer)158 already_AddRefed<TextureClient> CanvasClient2D::CreateTextureClientForCanvas(
159     gfx::SurfaceFormat aFormat, gfx::IntSize aSize, TextureFlags aFlags,
160     ShareableCanvasRenderer* aCanvasRenderer) {
161   if (aCanvasRenderer->HasGLContext()) {
162     // We want a cairo backend here as we don't want to be copying into
163     // an accelerated backend and we like LockBits to work. This is currently
164     // the most effective way to make this work.
165     return TextureClient::CreateForRawBufferAccess(GetForwarder(), aFormat,
166                                                    aSize, BackendType::CAIRO,
167                                                    mTextureFlags | aFlags);
168   }
169 
170 #ifdef XP_WIN
171   return CreateTextureClientForDrawing(aFormat, aSize, BackendSelector::Canvas,
172                                        aFlags);
173 #else
174   // XXX - We should use CreateTextureClientForDrawing, but we first need
175   // to use double buffering.
176   gfx::BackendType backend =
177       gfxPlatform::GetPlatform()->GetPreferredCanvasBackend();
178   return TextureClient::CreateForRawBufferAccess(
179       GetForwarder(), aFormat, aSize, backend, mTextureFlags | aFlags);
180 #endif
181 }
182 
183 ////////////////////////////////////////////////////////////////////////
184 
CanvasClientSharedSurface(CompositableForwarder * aLayerForwarder,TextureFlags aFlags)185 CanvasClientSharedSurface::CanvasClientSharedSurface(
186     CompositableForwarder* aLayerForwarder, TextureFlags aFlags)
187     : CanvasClient(aLayerForwarder, aFlags) {}
188 
~CanvasClientSharedSurface()189 CanvasClientSharedSurface::~CanvasClientSharedSurface() { ClearSurfaces(); }
190 
191 ////////////////////////////////////////
192 // Readback
193 
194 // For formats compatible with R8G8B8A8.
SwapRB_R8G8B8A8(uint8_t * pixel)195 static inline void SwapRB_R8G8B8A8(uint8_t* pixel) {
196   // [RR, GG, BB, AA]
197   Swap(pixel[0], pixel[2]);
198 }
199 
200 class TexClientFactory {
201   CompositableForwarder* const mAllocator;
202   const bool mHasAlpha;
203   const gfx::IntSize mSize;
204   const gfx::BackendType mBackendType;
205   const TextureFlags mBaseTexFlags;
206   const LayersBackend mLayersBackend;
207 
208  public:
TexClientFactory(CompositableForwarder * allocator,bool hasAlpha,const gfx::IntSize & size,gfx::BackendType backendType,TextureFlags baseTexFlags,LayersBackend layersBackend)209   TexClientFactory(CompositableForwarder* allocator, bool hasAlpha,
210                    const gfx::IntSize& size, gfx::BackendType backendType,
211                    TextureFlags baseTexFlags, LayersBackend layersBackend)
212       : mAllocator(allocator),
213         mHasAlpha(hasAlpha),
214         mSize(size),
215         mBackendType(backendType),
216         mBaseTexFlags(baseTexFlags),
217         mLayersBackend(layersBackend) {}
218 
219  protected:
Create(gfx::SurfaceFormat format)220   already_AddRefed<TextureClient> Create(gfx::SurfaceFormat format) {
221     return TextureClient::CreateForRawBufferAccess(mAllocator, format, mSize,
222                                                    mBackendType, mBaseTexFlags);
223   }
224 
225  public:
CreateB8G8R8AX8()226   already_AddRefed<TextureClient> CreateB8G8R8AX8() {
227     gfx::SurfaceFormat format =
228         mHasAlpha ? gfx::SurfaceFormat::B8G8R8A8 : gfx::SurfaceFormat::B8G8R8X8;
229     return Create(format);
230   }
231 
CreateR8G8B8AX8()232   already_AddRefed<TextureClient> CreateR8G8B8AX8() {
233     RefPtr<TextureClient> ret;
234 
235     bool areRGBAFormatsBroken = mLayersBackend == LayersBackend::LAYERS_BASIC;
236     if (!areRGBAFormatsBroken) {
237       gfx::SurfaceFormat format = mHasAlpha ? gfx::SurfaceFormat::R8G8B8A8
238                                             : gfx::SurfaceFormat::R8G8B8X8;
239       ret = Create(format);
240     }
241 
242     if (!ret) {
243       ret = CreateB8G8R8AX8();
244       if (ret) {
245         ret->AddFlags(TextureFlags::RB_SWAPPED);
246       }
247     }
248 
249     return ret.forget();
250   }
251 };
252 
TexClientFromReadback(SharedSurface * src,CompositableForwarder * allocator,TextureFlags baseFlags,LayersBackend layersBackend)253 static already_AddRefed<TextureClient> TexClientFromReadback(
254     SharedSurface* src, CompositableForwarder* allocator,
255     TextureFlags baseFlags, LayersBackend layersBackend) {
256   auto backendType = gfx::BackendType::SKIA;
257   TexClientFactory factory(allocator, src->mHasAlpha, src->mSize, backendType,
258                            baseFlags, layersBackend);
259 
260   RefPtr<TextureClient> texClient;
261 
262   {
263     gl::ScopedReadbackFB autoReadback(src);
264 
265     // We have a source FB, now we need a format.
266     GLenum destFormat = LOCAL_GL_BGRA;
267     GLenum destType = LOCAL_GL_UNSIGNED_BYTE;
268     GLenum readFormat;
269     GLenum readType;
270 
271     // We actually don't care if they match, since we can handle
272     // any read{Format,Type} we get.
273     auto gl = src->mGL;
274     GetActualReadFormats(gl, destFormat, destType, &readFormat, &readType);
275 
276     MOZ_ASSERT(readFormat == LOCAL_GL_RGBA || readFormat == LOCAL_GL_BGRA);
277     MOZ_ASSERT(readType == LOCAL_GL_UNSIGNED_BYTE);
278 
279     // With a format and type, we can create texClient.
280     if (readFormat == LOCAL_GL_BGRA && readType == LOCAL_GL_UNSIGNED_BYTE) {
281       // 0xAARRGGBB
282       // In Lendian: [BB, GG, RR, AA]
283       texClient = factory.CreateB8G8R8AX8();
284 
285     } else if (readFormat == LOCAL_GL_RGBA &&
286                readType == LOCAL_GL_UNSIGNED_BYTE) {
287       // [RR, GG, BB, AA]
288       texClient = factory.CreateR8G8B8AX8();
289     } else {
290       MOZ_CRASH("GFX: Bad `read{Format,Type}`.");
291     }
292 
293     if (!texClient) {
294       gfxWarning() << "Couldn't create texClient for readback.";
295       return nullptr;
296     }
297 
298     // With a texClient, we can lock for writing.
299     TextureClientAutoLock autoLock(texClient, OpenMode::OPEN_WRITE);
300     DebugOnly<bool> succeeded = autoLock.Succeeded();
301     MOZ_ASSERT(succeeded, "texture should have locked");
302 
303     MappedTextureData mapped;
304     texClient->BorrowMappedData(mapped);
305 
306     // ReadPixels from the current FB into mapped.data.
307     auto width = src->mSize.width;
308     auto height = src->mSize.height;
309 
310     {
311       ScopedPackState scopedPackState(gl);
312 
313       MOZ_ASSERT(mapped.stride / 4 == mapped.size.width);
314       gl->raw_fReadPixels(0, 0, width, height, readFormat, readType,
315                           mapped.data);
316     }
317 
318     // RB_SWAPPED doesn't work with D3D11. (bug 1051010)
319     // RB_SWAPPED doesn't work with Basic. (bug ???????)
320     bool layersNeedsManualSwap = layersBackend == LayersBackend::LAYERS_BASIC ||
321                                  layersBackend == LayersBackend::LAYERS_D3D11;
322     if (texClient->HasFlags(TextureFlags::RB_SWAPPED) &&
323         layersNeedsManualSwap) {
324       size_t pixels = width * height;
325       uint8_t* itr = mapped.data;
326       for (size_t i = 0; i < pixels; i++) {
327         SwapRB_R8G8B8A8(itr);
328         itr += 4;
329       }
330 
331       texClient->RemoveFlags(TextureFlags::RB_SWAPPED);
332     }
333   }
334 
335   return texClient.forget();
336 }
337 
338 ////////////////////////////////////////
339 
CloneSurface(gl::SharedSurface * src,gl::SurfaceFactory * factory)340 static already_AddRefed<SharedSurfaceTextureClient> CloneSurface(
341     gl::SharedSurface* src, gl::SurfaceFactory* factory) {
342   RefPtr<SharedSurfaceTextureClient> dest = factory->NewTexClient(src->mSize);
343   if (!dest) {
344     return nullptr;
345   }
346 
347   gl::SharedSurface* destSurf = dest->Surf();
348 
349   destSurf->ProducerAcquire();
350   SharedSurface::ProdCopy(src, dest->Surf(), factory);
351   destSurf->ProducerRelease();
352 
353   return dest.forget();
354 }
355 
Update(gfx::IntSize aSize,ShareableCanvasRenderer * aCanvasRenderer)356 void CanvasClientSharedSurface::Update(
357     gfx::IntSize aSize, ShareableCanvasRenderer* aCanvasRenderer) {
358   Renderer renderer;
359   renderer.construct<ShareableCanvasRenderer*>(aCanvasRenderer);
360   UpdateRenderer(aSize, renderer);
361 }
362 
UpdateAsync(AsyncCanvasRenderer * aRenderer)363 void CanvasClientSharedSurface::UpdateAsync(AsyncCanvasRenderer* aRenderer) {
364   Renderer renderer;
365   renderer.construct<AsyncCanvasRenderer*>(aRenderer);
366   UpdateRenderer(aRenderer->GetSize(), renderer);
367 }
368 
UpdateRenderer(gfx::IntSize aSize,Renderer & aRenderer)369 void CanvasClientSharedSurface::UpdateRenderer(gfx::IntSize aSize,
370                                                Renderer& aRenderer) {
371   GLContext* gl = nullptr;
372   ShareableCanvasRenderer* canvasRenderer = nullptr;
373   AsyncCanvasRenderer* asyncRenderer = nullptr;
374   if (aRenderer.constructed<ShareableCanvasRenderer*>()) {
375     canvasRenderer = aRenderer.ref<ShareableCanvasRenderer*>();
376     gl = canvasRenderer->mGLContext;
377   } else {
378     asyncRenderer = aRenderer.ref<AsyncCanvasRenderer*>();
379     gl = asyncRenderer->mGLContext;
380   }
381   gl->MakeCurrent();
382 
383   RefPtr<TextureClient> newFront;
384 
385   mShSurfClient = nullptr;
386   if (canvasRenderer && canvasRenderer->mGLFrontbuffer) {
387     mShSurfClient = CloneSurface(canvasRenderer->mGLFrontbuffer.get(),
388                                  canvasRenderer->mFactory.get());
389     if (!mShSurfClient) {
390       gfxCriticalError() << "Invalid canvas front buffer";
391       return;
392     }
393   } else if (gl->Screen()) {
394     mShSurfClient = gl->Screen()->Front();
395     if (mShSurfClient && mShSurfClient->GetAllocator() &&
396         mShSurfClient->GetAllocator() !=
397             GetForwarder()->GetTextureForwarder()) {
398       mShSurfClient =
399           CloneSurface(mShSurfClient->Surf(), gl->Screen()->Factory());
400     }
401   }
402 
403   if (!mShSurfClient) {
404     gfxCriticalError() << "Invalid canvas front buffer or screen";
405     return;
406   }
407 
408   newFront = mShSurfClient;
409 
410   SharedSurface* surf = mShSurfClient->Surf();
411 
412   // Readback if needed.
413   mReadbackClient = nullptr;
414 
415   auto forwarder = GetForwarder();
416 
417   bool needsReadback = (surf->mType == SharedSurfaceType::Basic);
418   if (needsReadback) {
419     TextureFlags flags = TextureFlags::IMMUTABLE;
420 
421     CompositableForwarder* shadowForwarder = nullptr;
422     if (canvasRenderer) {
423       flags |= canvasRenderer->Flags();
424       shadowForwarder = canvasRenderer->GetForwarder();
425     } else {
426       MOZ_ASSERT(asyncRenderer);
427       flags |= mTextureFlags;
428       shadowForwarder = GetForwarder();
429     }
430 
431     auto layersBackend = shadowForwarder->GetCompositorBackendType();
432     mReadbackClient =
433         TexClientFromReadback(surf, forwarder, flags, layersBackend);
434 
435     newFront = mReadbackClient;
436   } else {
437     mReadbackClient = nullptr;
438   }
439 
440   surf->Commit();
441 
442   if (asyncRenderer) {
443     // If surface type is Basic, above codes will readback
444     // the GLContext to mReadbackClient in order to send frame to
445     // compositor. We copy from this TextureClient directly by
446     // calling CopyFromTextureClient().
447     // Therefore, if main-thread want the content of GLContext,
448     // it doesn't have to readback from GLContext again.
449     //
450     // Otherwise, if surface type isn't Basic, we will read from
451     // SharedSurface directly from main-thread. We still pass
452     // mReadbackClient which is nullptr here to tell
453     // AsyncCanvasRenderer reset some properties.
454     asyncRenderer->CopyFromTextureClient(mReadbackClient);
455   }
456 
457   if (!newFront) {
458     // May happen in a release build in case of memory pressure.
459     gfxWarning()
460         << "Failed to allocate a TextureClient for SharedSurface Canvas. Size: "
461         << aSize;
462     return;
463   }
464 
465   mNewFront = newFront;
466 }
467 
Updated()468 void CanvasClientSharedSurface::Updated() {
469   if (!mNewFront) {
470     return;
471   }
472 
473   auto forwarder = GetForwarder();
474 
475   mFront = mNewFront;
476   mNewFront = nullptr;
477 
478   // Add the new TexClient.
479   if (!AddTextureClient(mFront)) {
480     return;
481   }
482 
483   AutoTArray<CompositableForwarder::TimedTextureClient, 1> textures;
484   CompositableForwarder::TimedTextureClient* t = textures.AppendElement();
485   t->mTextureClient = mFront;
486   t->mPictureRect = nsIntRect(nsIntPoint(0, 0), mFront->GetSize());
487   t->mFrameID = mFrameID;
488   forwarder->UseTextures(this, textures);
489 }
490 
OnDetach()491 void CanvasClientSharedSurface::OnDetach() { ClearSurfaces(); }
492 
ClearSurfaces()493 void CanvasClientSharedSurface::ClearSurfaces() {
494   if (mFront) {
495     mFront->CancelWaitForRecycle();
496   }
497   mFront = nullptr;
498   mNewFront = nullptr;
499   mShSurfClient = nullptr;
500   mReadbackClient = nullptr;
501 }
502 
503 }  // namespace layers
504 }  // namespace mozilla
505