File: GrallocTextureHost.cpp

package info (click to toggle)
icedove 1%3A45.8.0-3~deb8u1
  • links: PTS, VCS
  • area: main
  • in suites: jessie
  • size: 1,488,584 kB
  • ctags: 1,068,813
  • sloc: cpp: 4,801,496; ansic: 1,929,291; python: 379,296; java: 252,018; xml: 173,182; asm: 146,741; sh: 89,229; makefile: 23,462; perl: 16,380; objc: 4,088; yacc: 1,841; lex: 1,222; exp: 499; php: 437; lisp: 228; awk: 152; pascal: 116; sed: 51; ruby: 47; csh: 31; ada: 16
file content (470 lines) | stat: -rw-r--r-- 16,003 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
//  * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "base/process.h"
#include "GLContext.h"
#include "gfx2DGlue.h"
#include <ui/GraphicBuffer.h>
#include "GrallocImages.h"  // for GrallocImage
#include "GLLibraryEGL.h"   // for GLLibraryEGL
#include "mozilla/gfx/2D.h"
#include "mozilla/gfx/DataSurfaceHelpers.h"
#include "mozilla/layers/GrallocTextureHost.h"
#include "mozilla/layers/SharedBufferManagerParent.h"
#include "EGLImageHelpers.h"
#include "GLReadTexImageHelper.h"

namespace mozilla {
namespace layers {

using namespace android;
using namespace mozilla::gl;

static gfx::SurfaceFormat
SurfaceFormatForAndroidPixelFormat(android::PixelFormat aFormat,
                                   TextureFlags aFlags)
{
  bool swapRB = bool(aFlags & TextureFlags::RB_SWAPPED);
  switch (aFormat) {
  case android::PIXEL_FORMAT_BGRA_8888:
    return swapRB ? gfx::SurfaceFormat::R8G8B8A8 : gfx::SurfaceFormat::B8G8R8A8;
  case android::PIXEL_FORMAT_RGBA_8888:
    return swapRB ? gfx::SurfaceFormat::B8G8R8A8 : gfx::SurfaceFormat::R8G8B8A8;
  case android::PIXEL_FORMAT_RGBX_8888:
    return swapRB ? gfx::SurfaceFormat::B8G8R8X8 : gfx::SurfaceFormat::R8G8B8X8;
  case android::PIXEL_FORMAT_RGB_565:
    return gfx::SurfaceFormat::R5G6B5_UINT16;
  case HAL_PIXEL_FORMAT_YCbCr_422_SP:
  case HAL_PIXEL_FORMAT_YCrCb_420_SP:
  case HAL_PIXEL_FORMAT_YCbCr_422_I:
  case GrallocImage::HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED:
  case GrallocImage::HAL_PIXEL_FORMAT_YCbCr_420_SP_VENUS:
  case HAL_PIXEL_FORMAT_YV12:
#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
  case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED:
#endif
    return gfx::SurfaceFormat::R8G8B8A8; // yup, use SurfaceFormat::R8G8B8A8 even though it's a YUV texture. This is an external texture.
  default:
    if (aFormat >= 0x100 && aFormat <= 0x1FF) {
      // Reserved range for HAL specific formats.
      return gfx::SurfaceFormat::R8G8B8A8;
    } else {
      // This is not super-unreachable, there's a bunch of hypothetical pixel
      // formats we don't deal with.
      // We only want to abort in debug builds here, since if we crash here
      // we'll take down the compositor process and thus the phone. This seems
      // like undesirable behaviour. We'd rather have a subtle artifact.
      printf_stderr(" xxxxx unknow android format %i\n", (int)aFormat);
      MOZ_ASSERT(false, "Unknown Android pixel format.");
      return gfx::SurfaceFormat::UNKNOWN;
    }
  }
}

static GLenum
TextureTargetForAndroidPixelFormat(android::PixelFormat aFormat)
{
  switch (aFormat) {
  case HAL_PIXEL_FORMAT_YCbCr_422_SP:
  case HAL_PIXEL_FORMAT_YCrCb_420_SP:
  case HAL_PIXEL_FORMAT_YCbCr_422_I:
  case GrallocImage::HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED:
  case GrallocImage::HAL_PIXEL_FORMAT_YCbCr_420_SP_VENUS:
  case HAL_PIXEL_FORMAT_YV12:
#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
  case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED:
#endif
    return LOCAL_GL_TEXTURE_EXTERNAL;
  case android::PIXEL_FORMAT_BGRA_8888:
  case android::PIXEL_FORMAT_RGBA_8888:
  case android::PIXEL_FORMAT_RGBX_8888:
  case android::PIXEL_FORMAT_RGB_565:
    return LOCAL_GL_TEXTURE_2D;
  default:
    if (aFormat >= 0x100 && aFormat <= 0x1FF) {
      // Reserved range for HAL specific formats.
      return LOCAL_GL_TEXTURE_EXTERNAL;
    } else {
      // This is not super-unreachable, there's a bunch of hypothetical pixel
      // formats we don't deal with.
      // We only want to abort in debug builds here, since if we crash here
      // we'll take down the compositor process and thus the phone. This seems
      // like undesirable behaviour. We'd rather have a subtle artifact.
      MOZ_ASSERT(false, "Unknown Android pixel format.");
      return LOCAL_GL_TEXTURE_EXTERNAL;
    }
  }
}

GrallocTextureHostOGL::GrallocTextureHostOGL(TextureFlags aFlags,
                                             const SurfaceDescriptorGralloc& aDescriptor)
  : TextureHost(aFlags)
  , mGrallocHandle(aDescriptor)
  , mSize(0, 0)
  , mCropSize(0, 0)
  , mFormat(gfx::SurfaceFormat::UNKNOWN)
  , mEGLImage(EGL_NO_IMAGE)
  , mIsOpaque(aDescriptor.isOpaque())
{
  android::GraphicBuffer* graphicBuffer = GetGraphicBufferFromDesc(mGrallocHandle).get();
  MOZ_ASSERT(graphicBuffer);

  if (graphicBuffer) {
    mFormat =
      SurfaceFormatForAndroidPixelFormat(graphicBuffer->getPixelFormat(),
                                         aFlags & TextureFlags::RB_SWAPPED);
    mSize = gfx::IntSize(graphicBuffer->getWidth(), graphicBuffer->getHeight());
    mCropSize = mSize;
  } else {
    printf_stderr("gralloc buffer is nullptr\n");
  }
}

GrallocTextureHostOGL::~GrallocTextureHostOGL()
{
  DestroyEGLImage();
}

void
GrallocTextureHostOGL::SetCompositor(Compositor* aCompositor)
{
  MOZ_ASSERT(aCompositor);
  mCompositor = static_cast<CompositorOGL*>(aCompositor);
  if (mGLTextureSource) {
    mGLTextureSource->SetCompositor(mCompositor);
  }

  if (mCompositor && aCompositor != mCompositor) {
    DestroyEGLImage();
  }
}

bool
GrallocTextureHostOGL::Lock()
{
  return IsValid();
}

void
GrallocTextureHostOGL::Unlock()
{
  // Unlock is done internally by binding the texture to another gralloc buffer
}

bool
GrallocTextureHostOGL::IsValid() const
{
  android::GraphicBuffer* graphicBuffer = GetGraphicBufferFromDesc(mGrallocHandle).get();
  return graphicBuffer != nullptr;
}

gfx::SurfaceFormat
GrallocTextureHostOGL::GetFormat() const
{
  return mFormat;
}

void
GrallocTextureHostOGL::DeallocateSharedData()
{
  if (mGLTextureSource) {
    mGLTextureSource = nullptr;
  }

  DestroyEGLImage();

  if (mGrallocHandle.buffer().type() != MaybeMagicGrallocBufferHandle::Tnull_t) {
    MaybeMagicGrallocBufferHandle handle = mGrallocHandle.buffer();
    base::ProcessId owner;
    if (handle.type() == MaybeMagicGrallocBufferHandle::TGrallocBufferRef) {
      owner = handle.get_GrallocBufferRef().mOwner;
    }
    else {
      owner = handle.get_MagicGrallocBufferHandle().mRef.mOwner;
    }

    SharedBufferManagerParent::DropGrallocBuffer(owner, mGrallocHandle);
  }
}

void
GrallocTextureHostOGL::ForgetSharedData()
{
  if (mGLTextureSource) {
    mGLTextureSource = nullptr;
  }
}

void
GrallocTextureHostOGL::DeallocateDeviceData()
{
  if (mGLTextureSource) {
    mGLTextureSource = nullptr;
  }
  DestroyEGLImage();
}

LayerRenderState
GrallocTextureHostOGL::GetRenderState()
{
  android::GraphicBuffer* graphicBuffer = GetGraphicBufferFromDesc(mGrallocHandle).get();

  if (graphicBuffer) {
    LayerRenderStateFlags flags = LayerRenderStateFlags::LAYER_RENDER_STATE_DEFAULT;
    if (mIsOpaque) {
      flags |= LayerRenderStateFlags::OPAQUE;
    }
    if (mFlags & TextureFlags::ORIGIN_BOTTOM_LEFT) {
      flags |= LayerRenderStateFlags::ORIGIN_BOTTOM_LEFT;
    }
    if (mFlags & TextureFlags::RB_SWAPPED) {
      flags |= LayerRenderStateFlags::FORMAT_RB_SWAP;
    }
    return LayerRenderState(graphicBuffer,
                            mCropSize,
                            flags,
                            this);
  }

  return LayerRenderState();
}

already_AddRefed<gfx::DataSourceSurface>
GrallocTextureHostOGL::GetAsSurface() {
  android::GraphicBuffer* graphicBuffer = GetGraphicBufferFromDesc(mGrallocHandle).get();
  if (!graphicBuffer) {
    return nullptr;
  }
  uint8_t* grallocData;
  int32_t rv = graphicBuffer->lock(GRALLOC_USAGE_SW_READ_OFTEN, reinterpret_cast<void**>(&grallocData));
  if (rv) {
    return nullptr;
  }
  RefPtr<gfx::DataSourceSurface> grallocTempSurf =
    gfx::Factory::CreateWrappingDataSourceSurface(grallocData,
                                                  graphicBuffer->getStride() * android::bytesPerPixel(graphicBuffer->getPixelFormat()),
                                                  GetSize(), GetFormat());
  if (!grallocTempSurf) {
    graphicBuffer->unlock();
    return nullptr;
  }
  RefPtr<gfx::DataSourceSurface> surf = CreateDataSourceSurfaceByCloning(grallocTempSurf);

  graphicBuffer->unlock();

  return surf.forget();
}

void
GrallocTextureHostOGL::UnbindTextureSource()
{
  // Clear the reference to the TextureSource (if any), because we know that
  // another TextureHost is being bound to the TextureSource. This means that
  // we will have to re-do gl->fEGLImageTargetTexture2D next time we go through
  // BindTextureSource (otherwise we would have skipped it).
  // Note that this doesn't "unlock" the gralloc buffer or force it to be
  // detached, Although decreasing the refcount of the TextureSource may lead
  // to the gl handle being destroyed, which would unlock the gralloc buffer.
  // That said, this method is called before another TextureHost attaches to the
  // TextureSource, which has the effect of unlocking the gralloc buffer. So when
  // this is called we know we are going to be unlocked soon.
  mGLTextureSource = nullptr;
}

GLenum GetTextureTarget(gl::GLContext* aGL, android::PixelFormat aFormat) {
  MOZ_ASSERT(aGL);
  if (aGL->Renderer() == gl::GLRenderer::SGX530 ||
      aGL->Renderer() == gl::GLRenderer::SGX540) {
    // SGX has a quirk that only TEXTURE_EXTERNAL works and any other value will
    // result in black pixels when trying to draw from bound textures.
    // Unfortunately, using TEXTURE_EXTERNAL on Adreno has a terrible effect on
    // performance.
    // See Bug 950050.
    return LOCAL_GL_TEXTURE_EXTERNAL;
  } else {
    return TextureTargetForAndroidPixelFormat(aFormat);
  }
}

void
GrallocTextureHostOGL::DestroyEGLImage()
{
  // Only called when we want to get rid of the gralloc buffer, usually
  // around the end of life of the TextureHost.
  if (mEGLImage != EGL_NO_IMAGE && GetGLContext()) {
    EGLImageDestroy(GetGLContext(), mEGLImage);
    mEGLImage = EGL_NO_IMAGE;
  }
}

void
GrallocTextureHostOGL::PrepareTextureSource(CompositableTextureSourceRef& aTextureSource)
{
  // This happens during the layers transaction.
  // All of the gralloc magic goes here. The only thing that happens externally
  // and that is good to keep in mind is that when the TextureSource is deleted,
  // it destroys its gl texture handle which is important for genlock.

  // If this TextureHost's mGLTextureSource member is non-null, it means we are
  // still bound to the TextureSource, in which case we can skip the driver
  // overhead of binding the texture again (fEGLImageTargetTexture2D)
  // As a result, if the TextureHost is used with several CompositableHosts,
  // it will be bound to only one TextureSource, and we'll do the driver work
  // only once, which is great. This means that all of the compositables that
  // use this TextureHost will keep a reference to this TextureSource at least
  // for the duration of this frame.

  // If the compositable already has a TextureSource (the aTextureSource parameter),
  // that is compatible and is not in use by several compositable, we try to
  // attach to it. This has the effect of unlocking the previous TextureHost that
  // we attached to the TextureSource (the previous frame)

  // If the TextureSource used by the compositable is also used by other
  // compositables (see NumCompositableRefs), we have to create a new TextureSource,
  // because otherwise we would be modifying the content of every layer that uses
  // the TextureSource in question, even thoug they don't use this TextureHost.

  android::GraphicBuffer* graphicBuffer = GetGraphicBufferFromDesc(mGrallocHandle).get();

  MOZ_ASSERT(graphicBuffer);
  if (!graphicBuffer) {
    mGLTextureSource = nullptr;
    return;
  }

  if (mGLTextureSource && !mGLTextureSource->IsValid()) {
    mGLTextureSource = nullptr;
  }

  if (mGLTextureSource) {
    // We are already attached to a TextureSource, nothing to do except tell
    // the compositable to use it.
    aTextureSource = mGLTextureSource.get();
    return;
  }

  gl::GLContext* gl = GetGLContext();
  if (!gl || !gl->MakeCurrent()) {
    mGLTextureSource = nullptr;
    return;
  }

  if (mEGLImage == EGL_NO_IMAGE) {
    gfx::IntSize cropSize(0, 0);
    if (mCropSize != mSize) {
      cropSize = mCropSize;
    }
    // Should only happen the first time.
    mEGLImage = EGLImageCreateFromNativeBuffer(gl, graphicBuffer->getNativeBuffer(), cropSize);
  }

  GLenum textureTarget = GetTextureTarget(gl, graphicBuffer->getPixelFormat());

  GLTextureSource* glSource = aTextureSource.get() ?
    aTextureSource->AsSourceOGL()->AsGLTextureSource() : nullptr;

  bool shouldCreateTextureSource = !glSource  || !glSource->IsValid()
                                 || glSource->NumCompositableRefs() > 1
                                 || glSource->GetTextureTarget() != textureTarget;

  if (shouldCreateTextureSource) {
    GLuint textureHandle;
    gl->fGenTextures(1, &textureHandle);
    gl->fBindTexture(textureTarget, textureHandle);
    gl->fTexParameteri(textureTarget, LOCAL_GL_TEXTURE_WRAP_T, LOCAL_GL_CLAMP_TO_EDGE);
    gl->fTexParameteri(textureTarget, LOCAL_GL_TEXTURE_WRAP_S, LOCAL_GL_CLAMP_TO_EDGE);
    gl->fEGLImageTargetTexture2D(textureTarget, mEGLImage);

    mGLTextureSource = new GLTextureSource(mCompositor, textureHandle, textureTarget,
                                           mSize, mFormat);
    aTextureSource = mGLTextureSource.get();
  } else {
    gl->fBindTexture(textureTarget, glSource->GetTextureHandle());

    gl->fEGLImageTargetTexture2D(textureTarget, mEGLImage);
    glSource->SetSize(mSize);
    glSource->SetFormat(mFormat);
    mGLTextureSource = glSource;
  }
}

void
GrallocTextureHostOGL::WaitAcquireFenceHandleSyncComplete()
{
  if (!mAcquireFenceHandle.IsValid()) {
    return;
  }

  RefPtr<FenceHandle::FdObj> fence = mAcquireFenceHandle.GetAndResetFdObj();
  int fenceFd = fence->GetAndResetFd();

  EGLint attribs[] = {
              LOCAL_EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fenceFd,
              LOCAL_EGL_NONE
          };

  EGLSync sync = sEGLLibrary.fCreateSync(EGL_DISPLAY(),
                                         LOCAL_EGL_SYNC_NATIVE_FENCE_ANDROID,
                                         attribs);
  if (!sync) {
    NS_WARNING("failed to create native fence sync");
    return;
  }

  // Wait sync complete with timeout.
  // If a source of the fence becomes invalid because of error,
  // fene complete is not signaled. See Bug 1061435.
  EGLint status = sEGLLibrary.fClientWaitSync(EGL_DISPLAY(),
                                              sync,
                                              0,
                                              400000000 /*400 msec*/);
  if (status != LOCAL_EGL_CONDITION_SATISFIED) {
    NS_ERROR("failed to wait native fence sync");
  }
  MOZ_ALWAYS_TRUE( sEGLLibrary.fDestroySync(EGL_DISPLAY(), sync) );
}

void
GrallocTextureHostOGL::SetCropRect(nsIntRect aCropRect)
{
  MOZ_ASSERT(aCropRect.TopLeft() == IntPoint(0, 0));
  MOZ_ASSERT(!aCropRect.IsEmpty());
  MOZ_ASSERT(aCropRect.width <= mSize.width);
  MOZ_ASSERT(aCropRect.height <= mSize.height);

  gfx::IntSize cropSize(aCropRect.width, aCropRect.height);
  if (mCropSize == cropSize) {
    return;
  }

  mCropSize = cropSize;
  mGLTextureSource = nullptr;
}

bool
GrallocTextureHostOGL::BindTextureSource(CompositableTextureSourceRef& aTextureSource)
{
  // This happens at composition time.

  // If mGLTextureSource is null it means PrepareTextureSource failed.
  if (!mGLTextureSource) {
    return false;
  }

  // If Prepare didn't fail, we expect our TextureSource to be the same as aTextureSource,
  // otherwise it means something has fiddled with the TextureSource between Prepare and
  // now.
  MOZ_ASSERT(mGLTextureSource == aTextureSource);
  aTextureSource = mGLTextureSource.get();

#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
  // Wait until it's ready.
  WaitAcquireFenceHandleSyncComplete();
#endif
  return true;
}

} // namepsace layers
} // namepsace mozilla