Coverage Report

Created: 2018-09-25 14:53

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