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/. */
7 #include "CanvasClient.h"
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/gfx/gfxVars.h"
18 #include "mozilla/layers/BufferTexture.h"
19 #include "mozilla/layers/AsyncCanvasRenderer.h"
20 #include "mozilla/layers/CompositableForwarder.h"
21 #include "mozilla/layers/CompositorBridgeChild.h" // for CompositorBridgeChild
22 #include "mozilla/layers/LayersTypes.h"
23 #include "mozilla/layers/OOPCanvasRenderer.h"
24 #include "mozilla/layers/TextureClient.h" // for TextureClient, etc
25 #include "mozilla/layers/TextureClientOGL.h"
26 #include "mozilla/layers/TextureClientRecycleAllocator.h"
27 #include "nsDebug.h" // for printf_stderr, NS_ASSERTION
28 #include "nsICanvasRenderingContextInternal.h"
29 #include "nsXULAppAPI.h" // for XRE_GetProcessType, etc
30 #include "TextureClientSharedSurface.h"
32 using namespace mozilla::gfx
;
33 using namespace mozilla::gl
;
39 already_AddRefed
<CanvasClient
> CanvasClient::CreateCanvasClient(
40 CanvasClientType aType
, CompositableForwarder
* aForwarder
,
41 TextureFlags aFlags
) {
43 case CanvasClientTypeShSurf
:
44 return MakeAndAddRef
<CanvasClientSharedSurface
>(aForwarder
, aFlags
);
45 case CanvasClientAsync
:
46 return MakeAndAddRef
<CanvasClientBridge
>(aForwarder
, aFlags
);
47 case CanvasClientTypeOOP
:
48 return MakeAndAddRef
<CanvasClientOOP
>(aForwarder
, aFlags
);
50 return MakeAndAddRef
<CanvasClient2D
>(aForwarder
, aFlags
);
55 void CanvasClientBridge::UpdateAsync(AsyncCanvasRenderer
* aRenderer
) {
56 if (!GetForwarder() || !mLayer
|| !aRenderer
||
57 !aRenderer
->GetCanvasClient()) {
61 CompositableHandle asyncID
= aRenderer
->GetCanvasClientAsyncHandle();
62 if (!asyncID
|| mAsyncHandle
== asyncID
) {
66 static_cast<ShadowLayerForwarder
*>(GetForwarder())
67 ->AttachAsyncCompositable(asyncID
, mLayer
);
68 mAsyncHandle
= asyncID
;
71 void CanvasClient2D::UpdateFromTexture(TextureClient
* aTexture
,
72 wr::RenderRoot aRenderRoot
) {
75 if (!aTexture
->IsSharedWithCompositor()) {
76 if (!AddTextureClient(aTexture
)) {
81 mBackBuffer
= nullptr;
82 mFrontBuffer
= nullptr;
83 mBufferProviderTexture
= aTexture
;
85 AutoTArray
<CompositableForwarder::TimedTextureClient
, 1> textures
;
86 CompositableForwarder::TimedTextureClient
* t
= textures
.AppendElement();
87 t
->mTextureClient
= aTexture
;
88 t
->mPictureRect
= nsIntRect(nsIntPoint(0, 0), aTexture
->GetSize());
89 t
->mFrameID
= mFrameID
;
91 GetForwarder()->UseTextures(this, textures
, Some(aRenderRoot
));
92 aTexture
->SyncWithObject(GetForwarder()->GetSyncObject());
95 void CanvasClient2D::Update(gfx::IntSize aSize
,
96 ShareableCanvasRenderer
* aCanvasRenderer
,
97 wr::RenderRoot aRenderRoot
) {
98 mBufferProviderTexture
= nullptr;
100 AutoRemoveTexture
autoRemove(this, aRenderRoot
);
102 (mBackBuffer
->IsReadLocked() || mBackBuffer
->GetSize() != aSize
)) {
103 autoRemove
.mTexture
= mBackBuffer
;
104 mBackBuffer
= nullptr;
107 bool bufferCreated
= false;
109 gfxContentType contentType
= aCanvasRenderer
->IsOpaque()
110 ? gfxContentType::COLOR
111 : gfxContentType::COLOR_ALPHA
;
112 gfx::SurfaceFormat surfaceFormat
=
113 gfxPlatform::GetPlatform()->Optimal2DFormatForContent(contentType
);
114 TextureFlags flags
= TextureFlags::DEFAULT
;
115 if (mTextureFlags
& TextureFlags::ORIGIN_BOTTOM_LEFT
) {
116 flags
|= TextureFlags::ORIGIN_BOTTOM_LEFT
;
118 flags
|= TextureFlags::NON_BLOCKING_READ_LOCK
;
120 mBackBuffer
= CreateTextureClientForCanvas(surfaceFormat
, aSize
, flags
,
123 NS_WARNING("Failed to allocate the TextureClient");
126 MOZ_ASSERT(mBackBuffer
->CanExposeDrawTarget());
128 bufferCreated
= true;
131 bool updated
= false;
133 TextureClientAutoLock
autoLock(mBackBuffer
, OpenMode::OPEN_WRITE_ONLY
);
134 if (!autoLock
.Succeeded()) {
135 mBackBuffer
= nullptr;
139 RefPtr
<DrawTarget
> target
= mBackBuffer
->BorrowDrawTarget();
141 if (!aCanvasRenderer
->UpdateTarget(target
)) {
142 NS_WARNING("Failed to copy the canvas into a TextureClient.");
149 if (bufferCreated
&& !AddTextureClient(mBackBuffer
)) {
150 mBackBuffer
= nullptr;
155 AutoTArray
<CompositableForwarder::TimedTextureClient
, 1> textures
;
156 CompositableForwarder::TimedTextureClient
* t
= textures
.AppendElement();
157 t
->mTextureClient
= mBackBuffer
;
158 t
->mPictureRect
= nsIntRect(nsIntPoint(0, 0), mBackBuffer
->GetSize());
159 t
->mFrameID
= mFrameID
;
160 GetForwarder()->UseTextures(this, textures
, Some(aRenderRoot
));
161 mBackBuffer
->SyncWithObject(GetForwarder()->GetSyncObject());
164 mBackBuffer
.swap(mFrontBuffer
);
167 already_AddRefed
<TextureClient
> CanvasClient2D::CreateTextureClientForCanvas(
168 gfx::SurfaceFormat aFormat
, gfx::IntSize aSize
, TextureFlags aFlags
,
169 ShareableCanvasRenderer
* aCanvasRenderer
) {
170 if (aCanvasRenderer
->HasGLContext()) {
171 // We want a cairo backend here as we don't want to be copying into
172 // an accelerated backend and we like LockBits to work. This is currently
173 // the most effective way to make this work.
174 return TextureClient::CreateForRawBufferAccess(GetForwarder(), aFormat
,
175 aSize
, BackendType::CAIRO
,
176 mTextureFlags
| aFlags
);
180 // With WebRender, host side uses data of TextureClient longer.
181 // Then back buffer reuse in CanvasClient2D::Update() does not work. It causes
182 // a lot of TextureClient allocations.
183 // For reducing the allocations, TextureClientRecycler is used.
184 if (GetForwarder() &&
185 GetForwarder()->GetCompositorBackendType() == LayersBackend::LAYERS_WR
) {
186 return GetTextureClientRecycler()->CreateOrRecycle(
187 aFormat
, aSize
, BackendSelector::Canvas
, aFlags
);
189 return CreateTextureClientForDrawing(aFormat
, aSize
, BackendSelector::Canvas
,
192 // XXX - We should use CreateTextureClientForDrawing, but we first need
193 // to use double buffering.
194 gfx::BackendType backend
=
195 gfxPlatform::GetPlatform()->GetPreferredCanvasBackend();
196 return TextureClient::CreateForRawBufferAccess(
197 GetForwarder(), aFormat
, aSize
, backend
, mTextureFlags
| aFlags
);
201 ////////////////////////////////////////////////////////////////////////
203 CanvasClientSharedSurface::CanvasClientSharedSurface(
204 CompositableForwarder
* aLayerForwarder
, TextureFlags aFlags
)
205 : CanvasClient(aLayerForwarder
, aFlags
) {}
207 CanvasClientSharedSurface::~CanvasClientSharedSurface() { ClearSurfaces(); }
209 ////////////////////////////////////////
212 // For formats compatible with R8G8B8A8.
213 static inline void SwapRB_R8G8B8A8(uint8_t* pixel
) {
215 std::swap(pixel
[0], pixel
[2]);
218 class TexClientFactory
{
219 CompositableForwarder
* const mAllocator
;
220 const bool mHasAlpha
;
221 const gfx::IntSize mSize
;
222 const gfx::BackendType mBackendType
;
223 const TextureFlags mBaseTexFlags
;
224 const LayersBackend mLayersBackend
;
227 TexClientFactory(CompositableForwarder
* allocator
, bool hasAlpha
,
228 const gfx::IntSize
& size
, gfx::BackendType backendType
,
229 TextureFlags baseTexFlags
, LayersBackend layersBackend
)
230 : mAllocator(allocator
),
233 mBackendType(backendType
),
234 mBaseTexFlags(baseTexFlags
),
235 mLayersBackend(layersBackend
) {}
238 already_AddRefed
<TextureClient
> Create(gfx::SurfaceFormat format
) {
239 return TextureClient::CreateForRawBufferAccess(mAllocator
, format
, mSize
,
240 mBackendType
, mBaseTexFlags
);
244 already_AddRefed
<TextureClient
> CreateB8G8R8AX8() {
245 gfx::SurfaceFormat format
=
246 mHasAlpha
? gfx::SurfaceFormat::B8G8R8A8
: gfx::SurfaceFormat::B8G8R8X8
;
247 return Create(format
);
250 already_AddRefed
<TextureClient
> CreateR8G8B8AX8() {
251 RefPtr
<TextureClient
> ret
;
253 bool areRGBAFormatsBroken
= mLayersBackend
== LayersBackend::LAYERS_BASIC
;
254 if (!areRGBAFormatsBroken
) {
255 gfx::SurfaceFormat format
= mHasAlpha
? gfx::SurfaceFormat::R8G8B8A8
256 : gfx::SurfaceFormat::R8G8B8X8
;
257 if (gfxVars::UseWebRender() && format
== gfx::SurfaceFormat::R8G8B8X8
) {
258 MOZ_CRASH("R8G8B8X8 is not supported on WebRender");
260 ret
= Create(format
);
264 ret
= CreateB8G8R8AX8();
266 ret
->AddFlags(TextureFlags::RB_SWAPPED
);
274 static already_AddRefed
<TextureClient
> TexClientFromReadback(
275 SharedSurface
* src
, CompositableForwarder
* allocator
,
276 TextureFlags baseFlags
, LayersBackend layersBackend
) {
277 auto backendType
= gfx::BackendType::SKIA
;
278 TexClientFactory
factory(allocator
, src
->mHasAlpha
, src
->mSize
, backendType
,
279 baseFlags
, layersBackend
);
281 RefPtr
<TextureClient
> texClient
;
284 gl::ScopedReadbackFB
autoReadback(src
);
286 // We have a source FB, now we need a format.
287 GLenum destFormat
= LOCAL_GL_BGRA
;
288 GLenum destType
= LOCAL_GL_UNSIGNED_BYTE
;
292 // We actually don't care if they match, since we can handle
293 // any read{Format,Type} we get.
295 GetActualReadFormats(gl
, destFormat
, destType
, &readFormat
, &readType
);
297 MOZ_ASSERT(readFormat
== LOCAL_GL_RGBA
|| readFormat
== LOCAL_GL_BGRA
);
298 MOZ_ASSERT(readType
== LOCAL_GL_UNSIGNED_BYTE
);
300 // With a format and type, we can create texClient.
301 if (readFormat
== LOCAL_GL_BGRA
&& readType
== LOCAL_GL_UNSIGNED_BYTE
) {
303 // In Lendian: [BB, GG, RR, AA]
304 texClient
= factory
.CreateB8G8R8AX8();
306 } else if (readFormat
== LOCAL_GL_RGBA
&&
307 readType
== LOCAL_GL_UNSIGNED_BYTE
) {
309 texClient
= factory
.CreateR8G8B8AX8();
311 MOZ_CRASH("GFX: Bad `read{Format,Type}`.");
315 gfxWarning() << "Couldn't create texClient for readback.";
319 // With a texClient, we can lock for writing.
320 TextureClientAutoLock
autoLock(texClient
, OpenMode::OPEN_WRITE
);
321 DebugOnly
<bool> succeeded
= autoLock
.Succeeded();
322 MOZ_ASSERT(succeeded
, "texture should have locked");
324 MappedTextureData mapped
;
325 texClient
->BorrowMappedData(mapped
);
327 // ReadPixels from the current FB into mapped.data.
328 auto width
= src
->mSize
.width
;
329 auto height
= src
->mSize
.height
;
330 auto stride
= mapped
.stride
;
333 ScopedPackState
scopedPackState(gl
);
334 bool handled
= scopedPackState
.SetForWidthAndStrideRGBA(width
, stride
);
335 MOZ_RELEASE_ASSERT(handled
, "Unhandled stride");
336 gl
->raw_fReadPixels(0, 0, width
, height
, readFormat
, readType
,
340 // RB_SWAPPED doesn't work with D3D11. (bug 1051010)
341 // RB_SWAPPED doesn't work with Basic. (bug ???????)
342 bool layersNeedsManualSwap
= layersBackend
== LayersBackend::LAYERS_BASIC
||
343 layersBackend
== LayersBackend::LAYERS_D3D11
;
344 if (texClient
->HasFlags(TextureFlags::RB_SWAPPED
) &&
345 layersNeedsManualSwap
) {
346 size_t pixels
= width
* height
;
347 uint8_t* itr
= mapped
.data
;
348 for (size_t i
= 0; i
< pixels
; i
++) {
349 SwapRB_R8G8B8A8(itr
);
353 texClient
->RemoveFlags(TextureFlags::RB_SWAPPED
);
357 return texClient
.forget();
360 ////////////////////////////////////////
362 static already_AddRefed
<SharedSurfaceTextureClient
> CloneSurface(
363 gl::SharedSurface
* src
, gl::SurfaceFactory
* factory
) {
364 RefPtr
<SharedSurfaceTextureClient
> dest
= factory
->NewTexClient(src
->mSize
);
369 gl::SharedSurface
* destSurf
= dest
->Surf();
371 destSurf
->ProducerAcquire();
372 bool ret
= SharedSurface::ProdCopy(src
, dest
->Surf(), factory
);
373 destSurf
->ProducerRelease();
379 return dest
.forget();
382 void CanvasClientSharedSurface::Update(gfx::IntSize aSize
,
383 ShareableCanvasRenderer
* aCanvasRenderer
,
384 wr::RenderRoot aRenderRoot
) {
386 renderer
.construct
<ShareableCanvasRenderer
*>(aCanvasRenderer
);
387 UpdateRenderer(aSize
, renderer
);
390 void CanvasClientSharedSurface::UpdateAsync(AsyncCanvasRenderer
* aRenderer
) {
392 renderer
.construct
<AsyncCanvasRenderer
*>(aRenderer
);
393 UpdateRenderer(aRenderer
->GetSize(), renderer
);
396 void CanvasClientSharedSurface::UpdateRenderer(gfx::IntSize aSize
,
397 Renderer
& aRenderer
) {
398 GLContext
* gl
= nullptr;
399 ShareableCanvasRenderer
* canvasRenderer
= nullptr;
400 AsyncCanvasRenderer
* asyncRenderer
= nullptr;
401 if (aRenderer
.constructed
<ShareableCanvasRenderer
*>()) {
402 canvasRenderer
= aRenderer
.ref
<ShareableCanvasRenderer
*>();
403 gl
= canvasRenderer
->mGLContext
;
405 asyncRenderer
= aRenderer
.ref
<AsyncCanvasRenderer
*>();
406 gl
= asyncRenderer
->mGLContext
;
408 if (!gl
->MakeCurrent()) return;
410 RefPtr
<TextureClient
> newFront
;
412 mShSurfClient
= nullptr;
414 mShSurfClient
= gl
->Screen()->Front();
415 if (mShSurfClient
&& mShSurfClient
->GetAllocator() &&
416 mShSurfClient
->GetAllocator() !=
417 GetForwarder()->GetTextureForwarder()) {
419 CloneSurface(mShSurfClient
->Surf(), gl
->Screen()->Factory().get());
423 if (!mShSurfClient
) {
424 gfxCriticalError() << "Invalid canvas front buffer or screen";
428 newFront
= mShSurfClient
;
430 SharedSurface
* surf
= mShSurfClient
->Surf();
432 if (!surf
->IsBufferAvailable()) {
433 // SharedSurface is already forwared to compositor side.
434 // SharedSurface::Commit() could not be called again.
435 // It happens only with SharedSurface_SurfaceTexture.
436 if (!mNewFront
&& !mFront
) {
437 // This could happen when CanvasClientSharedSurface is re-created, but
438 // GLScreenBuffer is not re-created.
440 mNewFront
= newFront
;
442 NS_WARNING("SharedSurface buffer not available, skip update");
447 // Readback if needed.
448 mReadbackClient
= nullptr;
450 auto forwarder
= GetForwarder();
452 bool needsReadback
= (surf
->mType
== SharedSurfaceType::Basic
);
454 TextureFlags flags
= TextureFlags::IMMUTABLE
;
456 CompositableForwarder
* shadowForwarder
= nullptr;
457 if (canvasRenderer
) {
458 flags
|= canvasRenderer
->Flags();
459 shadowForwarder
= canvasRenderer
->GetForwarder();
461 MOZ_ASSERT(asyncRenderer
);
462 flags
|= mTextureFlags
;
463 shadowForwarder
= GetForwarder();
466 auto layersBackend
= shadowForwarder
->GetCompositorBackendType();
468 TexClientFromReadback(surf
, forwarder
, flags
, layersBackend
);
470 newFront
= mReadbackClient
;
472 mReadbackClient
= nullptr;
478 // If surface type is Basic, above codes will readback
479 // the GLContext to mReadbackClient in order to send frame to
480 // compositor. We copy from this TextureClient directly by
481 // calling CopyFromTextureClient().
482 // Therefore, if main-thread want the content of GLContext,
483 // it doesn't have to readback from GLContext again.
485 // Otherwise, if surface type isn't Basic, we will read from
486 // SharedSurface directly from main-thread. We still pass
487 // mReadbackClient which is nullptr here to tell
488 // AsyncCanvasRenderer reset some properties.
489 asyncRenderer
->CopyFromTextureClient(mReadbackClient
);
493 // May happen in a release build in case of memory pressure.
495 << "Failed to allocate a TextureClient for SharedSurface Canvas. Size: "
500 mNewFront
= newFront
;
503 void CanvasClientSharedSurface::Updated(wr::RenderRoot aRenderRoot
) {
508 auto forwarder
= GetForwarder();
513 // Add the new TexClient.
514 if (!AddTextureClient(mFront
)) {
518 AutoTArray
<CompositableForwarder::TimedTextureClient
, 1> textures
;
519 CompositableForwarder::TimedTextureClient
* t
= textures
.AppendElement();
520 t
->mTextureClient
= mFront
;
521 t
->mPictureRect
= nsIntRect(nsIntPoint(0, 0), mFront
->GetSize());
522 t
->mFrameID
= mFrameID
;
523 forwarder
->UseTextures(this, textures
, Some(aRenderRoot
));
526 void CanvasClientSharedSurface::OnDetach() { ClearSurfaces(); }
528 void CanvasClientSharedSurface::ClearSurfaces() {
531 mShSurfClient
= nullptr;
532 mReadbackClient
= nullptr;
535 //----------------------------------------------------------------------------
537 CanvasClientOOP::CanvasClientOOP(CompositableForwarder
* aLayerForwarder
,
539 : CanvasClient(aLayerForwarder
, aFlags
) {}
541 CanvasClientOOP::~CanvasClientOOP() = default;
543 void CanvasClientOOP::SetLayer(ShadowableLayer
* aLayer
,
544 OOPCanvasRenderer
* aRenderer
) {
546 mCanvasContext
= aRenderer
->mContext
;
547 MOZ_ASSERT(mCanvasContext
);
549 aRenderer
->mCanvasClient
= this;
552 void CanvasClientOOP::Update(gfx::IntSize aSize
,
553 ShareableCanvasRenderer
* aRenderer
,
554 wr::RenderRoot aRenderRoot
) {
555 // DLP: TODO: aRenderRoot?
556 if (!GetForwarder() || !mLayer
|| !mCanvasContext
|| !aRenderer
) {
560 // Make sure the host is using the right Compositable.
561 CompositableHandle handle
= GetIPCHandle();
562 if (!handle
|| mHandle
== handle
) {
566 MOZ_ASSERT(GetForwarder() && GetForwarder()->AsLayerForwarder() &&
567 GetForwarder()->AsLayerForwarder()->GetShadowManager());
569 static_cast<ShadowLayerForwarder
*>(GetForwarder())->Attach(this, mLayer
);
570 LayerTransactionChild
* ltc
=
571 GetForwarder()->AsLayerForwarder()->GetShadowManager();
572 bool success
= mCanvasContext
->UpdateCompositableHandle(ltc
, handle
);
580 } // namespace layers
581 } // namespace mozilla