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