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