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 "DMABufSurface.h"
8 
9 #include <fcntl.h>
10 #include <getopt.h>
11 #include <signal.h>
12 #include <stdbool.h>
13 #include <stdint.h>
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <unistd.h>
18 #include <sys/time.h>
19 #include <dlfcn.h>
20 #include <sys/mman.h>
21 #include <sys/eventfd.h>
22 #include <poll.h>
23 #include <sys/ioctl.h>
24 
25 #include "mozilla/widget/gbm.h"
26 #include "mozilla/widget/va_drmcommon.h"
27 #include "GLContextTypes.h"  // for GLContext, etc
28 #include "GLContextEGL.h"
29 #include "GLContextProvider.h"
30 #include "ScopedGLHelpers.h"
31 
32 #include "mozilla/layers/LayersSurfaces.h"
33 #include "mozilla/ScopeExit.h"
34 
35 /*
36 TODO:
37 DRM device selection:
38 https://lists.freedesktop.org/archives/wayland-devel/2018-November/039660.html
39 */
40 
41 /* C++ / C typecast macros for special EGL handle values */
42 #if defined(__cplusplus)
43 #  define EGL_CAST(type, value) (static_cast<type>(value))
44 #else
45 #  define EGL_CAST(type, value) ((type)(value))
46 #endif
47 
48 using namespace mozilla;
49 using namespace mozilla::widget;
50 using namespace mozilla::gl;
51 using namespace mozilla::layers;
52 
53 #define BUFFER_FLAGS 0
54 
55 #ifndef VA_FOURCC_NV12
56 #  define VA_FOURCC_NV12 0x3231564E
57 #endif
58 
59 #ifndef VA_FOURCC_YV12
60 #  define VA_FOURCC_YV12 0x32315659
61 #endif
62 
63 static Atomic<int> gNewSurfaceUID(1);
64 
IsGlobalRefSet() const65 bool DMABufSurface::IsGlobalRefSet() const {
66   if (!mGlobalRefCountFd) {
67     return false;
68   }
69   struct pollfd pfd;
70   pfd.fd = mGlobalRefCountFd;
71   pfd.events = POLLIN;
72   return poll(&pfd, 1, 0) == 1;
73 }
74 
GlobalRefRelease()75 void DMABufSurface::GlobalRefRelease() {
76   MOZ_ASSERT(mGlobalRefCountFd);
77   uint64_t counter;
78   if (read(mGlobalRefCountFd, &counter, sizeof(counter)) != sizeof(counter)) {
79     // EAGAIN means the refcount is already zero. It happens when we release
80     // last reference to the surface.
81     if (errno != EAGAIN) {
82       NS_WARNING(nsPrintfCString("Failed to unref dmabuf global ref count: %s",
83                                  strerror(errno))
84                      .get());
85     }
86   }
87 }
88 
GlobalRefAdd()89 void DMABufSurface::GlobalRefAdd() {
90   MOZ_ASSERT(mGlobalRefCountFd);
91   uint64_t counter = 1;
92   if (write(mGlobalRefCountFd, &counter, sizeof(counter)) != sizeof(counter)) {
93     NS_WARNING(nsPrintfCString("Failed to ref dmabuf global ref count: %s",
94                                strerror(errno))
95                    .get());
96   }
97 }
98 
GlobalRefCountCreate()99 void DMABufSurface::GlobalRefCountCreate() {
100   MOZ_ASSERT(!mGlobalRefCountFd);
101   mGlobalRefCountFd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK | EFD_SEMAPHORE);
102   if (mGlobalRefCountFd < 0) {
103     NS_WARNING(nsPrintfCString("Failed to create dmabuf global ref count: %s",
104                                strerror(errno))
105                    .get());
106     mGlobalRefCountFd = 0;
107     return;
108   }
109 }
110 
GlobalRefCountImport(int aFd)111 void DMABufSurface::GlobalRefCountImport(int aFd) {
112   MOZ_ASSERT(!mGlobalRefCountFd);
113   mGlobalRefCountFd = aFd;
114   GlobalRefAdd();
115 }
116 
GlobalRefCountDelete()117 void DMABufSurface::GlobalRefCountDelete() {
118   if (mGlobalRefCountFd) {
119     GlobalRefRelease();
120     close(mGlobalRefCountFd);
121     mGlobalRefCountFd = 0;
122   }
123 }
124 
ReleaseDMABuf()125 void DMABufSurface::ReleaseDMABuf() {
126   LOGDMABUF(("DMABufSurface::ReleaseDMABuf() UID %d", mUID));
127   for (int i = 0; i < mBufferPlaneCount; i++) {
128     Unmap(i);
129   }
130 
131   MutexAutoLock lockFD(mSurfaceLock);
132   CloseFileDescriptors(/* aForceClose */ true);
133 
134   for (int i = 0; i < mBufferPlaneCount; i++) {
135     if (mGbmBufferObject[i]) {
136       nsGbmLib::Destroy(mGbmBufferObject[i]);
137       mGbmBufferObject[i] = nullptr;
138     }
139   }
140   mBufferPlaneCount = 0;
141 }
142 
DMABufSurface(SurfaceType aSurfaceType)143 DMABufSurface::DMABufSurface(SurfaceType aSurfaceType)
144     : mSurfaceType(aSurfaceType),
145       mBufferModifier(DRM_FORMAT_MOD_INVALID),
146       mBufferPlaneCount(0),
147       mDrmFormats(),
148       mStrides(),
149       mOffsets(),
150       mGbmBufferObject(),
151       mMappedRegion(),
152       mMappedRegionStride(),
153       mSyncFd(-1),
154       mSync(0),
155       mGlobalRefCountFd(0),
156       mUID(gNewSurfaceUID++),
157       mSurfaceLock("DMABufSurface") {
158   for (auto& slot : mDmabufFds) {
159     slot = -1;
160   }
161 }
162 
~DMABufSurface()163 DMABufSurface::~DMABufSurface() {
164   FenceDelete();
165   GlobalRefCountDelete();
166 }
167 
CreateDMABufSurface(const mozilla::layers::SurfaceDescriptor & aDesc)168 already_AddRefed<DMABufSurface> DMABufSurface::CreateDMABufSurface(
169     const mozilla::layers::SurfaceDescriptor& aDesc) {
170   const SurfaceDescriptorDMABuf& desc = aDesc.get_SurfaceDescriptorDMABuf();
171   RefPtr<DMABufSurface> surf;
172 
173   switch (desc.bufferType()) {
174     case SURFACE_RGBA:
175       surf = new DMABufSurfaceRGBA();
176       break;
177     case SURFACE_NV12:
178     case SURFACE_YUV420:
179       surf = new DMABufSurfaceYUV();
180       break;
181     default:
182       return nullptr;
183   }
184 
185   if (!surf->Create(desc)) {
186     return nullptr;
187   }
188   return surf.forget();
189 }
190 
FenceDelete()191 void DMABufSurface::FenceDelete() {
192   if (mSyncFd > 0) {
193     close(mSyncFd);
194     mSyncFd = -1;
195   }
196 
197   if (!mGL) return;
198   const auto& gle = gl::GLContextEGL::Cast(mGL);
199   const auto& egl = gle->mEgl;
200 
201   if (mSync) {
202     egl->fDestroySync(mSync);
203     mSync = nullptr;
204   }
205 }
206 
FenceSet()207 void DMABufSurface::FenceSet() {
208   if (!mGL || !mGL->MakeCurrent()) {
209     return;
210   }
211   const auto& gle = gl::GLContextEGL::Cast(mGL);
212   const auto& egl = gle->mEgl;
213 
214   if (egl->IsExtensionSupported(EGLExtension::KHR_fence_sync) &&
215       egl->IsExtensionSupported(EGLExtension::ANDROID_native_fence_sync)) {
216     FenceDelete();
217 
218     mSync = egl->fCreateSync(LOCAL_EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr);
219     if (mSync) {
220       mSyncFd = egl->fDupNativeFenceFDANDROID(mSync);
221       mGL->fFlush();
222       return;
223     }
224   }
225 
226   // ANDROID_native_fence_sync may not be supported so call glFinish()
227   // as a slow path.
228   mGL->fFinish();
229 }
230 
FenceWait()231 void DMABufSurface::FenceWait() {
232   if (!mGL) return;
233   const auto& gle = gl::GLContextEGL::Cast(mGL);
234   const auto& egl = gle->mEgl;
235 
236   if (!mSync && mSyncFd > 0) {
237     FenceImportFromFd();
238   }
239 
240   // Wait on the fence, because presumably we're going to want to read this
241   // surface
242   if (mSync) {
243     egl->fClientWaitSync(mSync, 0, LOCAL_EGL_FOREVER);
244   }
245 }
246 
FenceImportFromFd()247 bool DMABufSurface::FenceImportFromFd() {
248   if (!mGL) return false;
249   const auto& gle = gl::GLContextEGL::Cast(mGL);
250   const auto& egl = gle->mEgl;
251 
252   const EGLint attribs[] = {LOCAL_EGL_SYNC_NATIVE_FENCE_FD_ANDROID, mSyncFd,
253                             LOCAL_EGL_NONE};
254   mSync = egl->fCreateSync(LOCAL_EGL_SYNC_NATIVE_FENCE_ANDROID, attribs);
255   close(mSyncFd);
256   mSyncFd = -1;
257 
258   if (!mSync) {
259     MOZ_ASSERT(false, "Failed to create GLFence!");
260     return false;
261   }
262 
263   return true;
264 }
265 
OpenFileDescriptors()266 bool DMABufSurface::OpenFileDescriptors() {
267   for (int i = 0; i < mBufferPlaneCount; i++) {
268     if (!OpenFileDescriptorForPlane(i)) {
269       return false;
270     }
271   }
272   return true;
273 }
274 
275 // We can safely close DMABuf file descriptors only when we have a valid
276 // GbmBufferObject. When we don't have a valid GbmBufferObject and a DMABuf
277 // file descriptor is closed, whole surface is released.
CloseFileDescriptors(bool aForceClose)278 void DMABufSurface::CloseFileDescriptors(bool aForceClose) {
279   for (int i = 0; i < DMABUF_BUFFER_PLANES; i++) {
280     CloseFileDescriptorForPlane(i, aForceClose);
281   }
282 }
283 
DMABufSurfaceRGBA()284 DMABufSurfaceRGBA::DMABufSurfaceRGBA()
285     : DMABufSurface(SURFACE_RGBA),
286       mSurfaceFlags(0),
287       mWidth(0),
288       mHeight(0),
289       mGmbFormat(nullptr),
290       mEGLImage(LOCAL_EGL_NO_IMAGE),
291       mTexture(0),
292       mGbmBufferFlags(0) {}
293 
~DMABufSurfaceRGBA()294 DMABufSurfaceRGBA::~DMABufSurfaceRGBA() { ReleaseSurface(); }
295 
OpenFileDescriptorForPlane(int aPlane)296 bool DMABufSurfaceRGBA::OpenFileDescriptorForPlane(int aPlane) {
297   if (mDmabufFds[aPlane] >= 0) {
298     return true;
299   }
300   if (mBufferPlaneCount == 1) {
301     MOZ_ASSERT(aPlane == 0, "DMABuf: wrong surface plane!");
302     mDmabufFds[0] = nsGbmLib::GetFd(mGbmBufferObject[0]);
303   } else {
304     uint32_t handle =
305         nsGbmLib::GetHandleForPlane(mGbmBufferObject[0], aPlane).u32;
306     int ret = nsGbmLib::DrmPrimeHandleToFD(GetDMABufDevice()->GetGbmDeviceFd(),
307                                            handle, 0, &mDmabufFds[aPlane]);
308     if (ret < 0) {
309       mDmabufFds[aPlane] = -1;
310     }
311   }
312 
313   if (mDmabufFds[aPlane] < 0) {
314     CloseFileDescriptors();
315     return false;
316   }
317 
318   return true;
319 }
320 
CloseFileDescriptorForPlane(int aPlane,bool aForceClose=false)321 void DMABufSurfaceRGBA::CloseFileDescriptorForPlane(int aPlane,
322                                                     bool aForceClose = false) {
323   if ((aForceClose || mGbmBufferObject[0]) && mDmabufFds[aPlane] >= 0) {
324     close(mDmabufFds[aPlane]);
325     mDmabufFds[aPlane] = -1;
326   }
327 }
328 
Create(int aWidth,int aHeight,int aDMABufSurfaceFlags)329 bool DMABufSurfaceRGBA::Create(int aWidth, int aHeight,
330                                int aDMABufSurfaceFlags) {
331   MOZ_ASSERT(mGbmBufferObject[0] == nullptr, "Already created?");
332 
333   mSurfaceFlags = aDMABufSurfaceFlags;
334   mWidth = aWidth;
335   mHeight = aHeight;
336 
337   LOGDMABUF(("DMABufSurfaceRGBA::Create() UID %d size %d x %d\n", mUID, mWidth,
338              mHeight));
339 
340   mGmbFormat = GetDMABufDevice()->GetGbmFormat(mSurfaceFlags & DMABUF_ALPHA);
341   if (!mGmbFormat) {
342     // Requested DRM format is not supported.
343     return false;
344   }
345 
346   bool useModifiers = (aDMABufSurfaceFlags & DMABUF_USE_MODIFIERS) &&
347                       mGmbFormat->mModifiersCount > 0;
348   if (useModifiers) {
349     LOGDMABUF(("    Creating with modifiers\n"));
350     mGbmBufferObject[0] = nsGbmLib::CreateWithModifiers(
351         GetDMABufDevice()->GetGbmDevice(), mWidth, mHeight, mGmbFormat->mFormat,
352         mGmbFormat->mModifiers, mGmbFormat->mModifiersCount);
353     if (mGbmBufferObject[0]) {
354       mBufferModifier = nsGbmLib::GetModifier(mGbmBufferObject[0]);
355     }
356   }
357 
358   if (!mGbmBufferObject[0]) {
359     LOGDMABUF(("    Creating without modifiers\n"));
360     mGbmBufferFlags = GBM_BO_USE_LINEAR;
361     mGbmBufferObject[0] =
362         nsGbmLib::Create(GetDMABufDevice()->GetGbmDevice(), mWidth, mHeight,
363                          mGmbFormat->mFormat, mGbmBufferFlags);
364     mBufferModifier = DRM_FORMAT_MOD_INVALID;
365   }
366 
367   if (!mGbmBufferObject[0]) {
368     LOGDMABUF(("    Failed to create GbmBufferObject\n"));
369     return false;
370   }
371 
372   if (mBufferModifier != DRM_FORMAT_MOD_INVALID) {
373     mBufferPlaneCount = nsGbmLib::GetPlaneCount(mGbmBufferObject[0]);
374     if (mBufferPlaneCount > DMABUF_BUFFER_PLANES) {
375       LOGDMABUF(("    There's too many dmabuf planes!"));
376       ReleaseSurface();
377       return false;
378     }
379 
380     for (int i = 0; i < mBufferPlaneCount; i++) {
381       mStrides[i] = nsGbmLib::GetStrideForPlane(mGbmBufferObject[0], i);
382       mOffsets[i] = nsGbmLib::GetOffset(mGbmBufferObject[0], i);
383     }
384   } else {
385     mBufferPlaneCount = 1;
386     mStrides[0] = nsGbmLib::GetStride(mGbmBufferObject[0]);
387   }
388 
389   LOGDMABUF(("    Success\n"));
390   return true;
391 }
392 
ImportSurfaceDescriptor(const SurfaceDescriptor & aDesc)393 bool DMABufSurfaceRGBA::ImportSurfaceDescriptor(
394     const SurfaceDescriptor& aDesc) {
395   const SurfaceDescriptorDMABuf& desc = aDesc.get_SurfaceDescriptorDMABuf();
396 
397   mWidth = desc.width()[0];
398   mHeight = desc.height()[0];
399   mBufferModifier = desc.modifier();
400   if (mBufferModifier != DRM_FORMAT_MOD_INVALID) {
401     mGmbFormat = GetDMABufDevice()->GetExactGbmFormat(desc.format()[0]);
402   } else {
403     mDrmFormats[0] = desc.format()[0];
404   }
405   mBufferPlaneCount = desc.fds().Length();
406   mGbmBufferFlags = desc.flags();
407   MOZ_RELEASE_ASSERT(mBufferPlaneCount <= DMABUF_BUFFER_PLANES);
408   mUID = desc.uid();
409 
410   LOGDMABUF(
411       ("DMABufSurfaceRGBA::ImportSurfaceDescriptor() UID %d size %d x %d\n",
412        mUID, mWidth, mHeight));
413 
414   for (int i = 0; i < mBufferPlaneCount; i++) {
415     mDmabufFds[i] = desc.fds()[i].ClonePlatformHandle().release();
416     if (mDmabufFds[i] < 0) {
417       LOGDMABUF(
418           ("    failed to get DMABuf file descriptor: %s", strerror(errno)));
419       return false;
420     }
421     mStrides[i] = desc.strides()[i];
422     mOffsets[i] = desc.offsets()[i];
423   }
424 
425   if (desc.fence().Length() > 0) {
426     mSyncFd = desc.fence()[0].ClonePlatformHandle().release();
427     if (mSyncFd < 0) {
428       LOGDMABUF(
429           ("    failed to get GL fence file descriptor: %s", strerror(errno)));
430       return false;
431     }
432   }
433 
434   if (desc.refCount().Length() > 0) {
435     GlobalRefCountImport(desc.refCount()[0].ClonePlatformHandle().release());
436   }
437 
438   return true;
439 }
440 
Create(const SurfaceDescriptor & aDesc)441 bool DMABufSurfaceRGBA::Create(const SurfaceDescriptor& aDesc) {
442   return ImportSurfaceDescriptor(aDesc);
443 }
444 
Serialize(mozilla::layers::SurfaceDescriptor & aOutDescriptor)445 bool DMABufSurfaceRGBA::Serialize(
446     mozilla::layers::SurfaceDescriptor& aOutDescriptor) {
447   AutoTArray<uint32_t, DMABUF_BUFFER_PLANES> width;
448   AutoTArray<uint32_t, DMABUF_BUFFER_PLANES> height;
449   AutoTArray<uint32_t, DMABUF_BUFFER_PLANES> format;
450   AutoTArray<ipc::FileDescriptor, DMABUF_BUFFER_PLANES> fds;
451   AutoTArray<uint32_t, DMABUF_BUFFER_PLANES> strides;
452   AutoTArray<uint32_t, DMABUF_BUFFER_PLANES> offsets;
453   AutoTArray<uintptr_t, DMABUF_BUFFER_PLANES> images;
454   AutoTArray<ipc::FileDescriptor, 1> fenceFDs;
455   AutoTArray<ipc::FileDescriptor, 1> refCountFDs;
456 
457   LOGDMABUF(("DMABufSurfaceRGBA::Serialize() UID %d\n", mUID));
458 
459   MutexAutoLock lockFD(mSurfaceLock);
460   if (!OpenFileDescriptors()) {
461     return false;
462   }
463 
464   width.AppendElement(mWidth);
465   height.AppendElement(mHeight);
466   format.AppendElement(mGmbFormat->mFormat);
467   for (int i = 0; i < mBufferPlaneCount; i++) {
468     fds.AppendElement(ipc::FileDescriptor(mDmabufFds[i]));
469     strides.AppendElement(mStrides[i]);
470     offsets.AppendElement(mOffsets[i]);
471   }
472 
473   CloseFileDescriptors();
474 
475   if (mSync) {
476     fenceFDs.AppendElement(ipc::FileDescriptor(mSyncFd));
477   }
478 
479   if (mGlobalRefCountFd) {
480     refCountFDs.AppendElement(ipc::FileDescriptor(mGlobalRefCountFd));
481   }
482 
483   aOutDescriptor =
484       SurfaceDescriptorDMABuf(mSurfaceType, mBufferModifier, mGbmBufferFlags,
485                               fds, width, height, format, strides, offsets,
486                               GetYUVColorSpace(), fenceFDs, mUID, refCountFDs);
487   return true;
488 }
489 
CreateTexture(GLContext * aGLContext,int aPlane)490 bool DMABufSurfaceRGBA::CreateTexture(GLContext* aGLContext, int aPlane) {
491   MOZ_ASSERT(!mEGLImage && !mTexture, "EGLImage is already created!");
492 
493   nsTArray<EGLint> attribs;
494   attribs.AppendElement(LOCAL_EGL_WIDTH);
495   attribs.AppendElement(mWidth);
496   attribs.AppendElement(LOCAL_EGL_HEIGHT);
497   attribs.AppendElement(mHeight);
498   attribs.AppendElement(LOCAL_EGL_LINUX_DRM_FOURCC_EXT);
499   if (mGmbFormat) {
500     attribs.AppendElement(mGmbFormat->mFormat);
501   } else {
502     attribs.AppendElement(mDrmFormats[0]);
503   }
504 #define ADD_PLANE_ATTRIBS(plane_idx)                                        \
505   {                                                                         \
506     attribs.AppendElement(LOCAL_EGL_DMA_BUF_PLANE##plane_idx##_FD_EXT);     \
507     attribs.AppendElement(mDmabufFds[plane_idx]);                           \
508     attribs.AppendElement(LOCAL_EGL_DMA_BUF_PLANE##plane_idx##_OFFSET_EXT); \
509     attribs.AppendElement((int)mOffsets[plane_idx]);                        \
510     attribs.AppendElement(LOCAL_EGL_DMA_BUF_PLANE##plane_idx##_PITCH_EXT);  \
511     attribs.AppendElement((int)mStrides[plane_idx]);                        \
512     if (mBufferModifier != DRM_FORMAT_MOD_INVALID) {                        \
513       attribs.AppendElement(                                                \
514           LOCAL_EGL_DMA_BUF_PLANE##plane_idx##_MODIFIER_LO_EXT);            \
515       attribs.AppendElement(mBufferModifier & 0xFFFFFFFF);                  \
516       attribs.AppendElement(                                                \
517           LOCAL_EGL_DMA_BUF_PLANE##plane_idx##_MODIFIER_HI_EXT);            \
518       attribs.AppendElement(mBufferModifier >> 32);                         \
519     }                                                                       \
520   }
521 
522   MutexAutoLock lockFD(mSurfaceLock);
523   if (!OpenFileDescriptors()) {
524     return false;
525   }
526   ADD_PLANE_ATTRIBS(0);
527   if (mBufferPlaneCount > 1) ADD_PLANE_ATTRIBS(1);
528   if (mBufferPlaneCount > 2) ADD_PLANE_ATTRIBS(2);
529   if (mBufferPlaneCount > 3) ADD_PLANE_ATTRIBS(3);
530 #undef ADD_PLANE_ATTRIBS
531   attribs.AppendElement(LOCAL_EGL_NONE);
532 
533   if (!aGLContext) return false;
534   const auto& gle = gl::GLContextEGL::Cast(aGLContext);
535   const auto& egl = gle->mEgl;
536   mEGLImage =
537       egl->fCreateImage(LOCAL_EGL_NO_CONTEXT, LOCAL_EGL_LINUX_DMA_BUF_EXT,
538                         nullptr, attribs.Elements());
539   if (mEGLImage == LOCAL_EGL_NO_IMAGE) {
540     LOGDMABUF(("EGLImageKHR creation failed"));
541     return false;
542   }
543 
544   CloseFileDescriptors();
545 
546   aGLContext->MakeCurrent();
547   aGLContext->fGenTextures(1, &mTexture);
548   const ScopedBindTexture savedTex(aGLContext, mTexture);
549   aGLContext->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_S,
550                              LOCAL_GL_CLAMP_TO_EDGE);
551   aGLContext->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_T,
552                              LOCAL_GL_CLAMP_TO_EDGE);
553   aGLContext->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MAG_FILTER,
554                              LOCAL_GL_LINEAR);
555   aGLContext->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MIN_FILTER,
556                              LOCAL_GL_LINEAR);
557   aGLContext->fEGLImageTargetTexture2D(LOCAL_GL_TEXTURE_2D, mEGLImage);
558   mGL = aGLContext;
559 
560   return true;
561 }
562 
ReleaseTextures()563 void DMABufSurfaceRGBA::ReleaseTextures() {
564   FenceDelete();
565 
566   if (!mGL) return;
567   const auto& gle = gl::GLContextEGL::Cast(mGL);
568   const auto& egl = gle->mEgl;
569 
570   if (mTexture && mGL->MakeCurrent()) {
571     mGL->fDeleteTextures(1, &mTexture);
572     mTexture = 0;
573     mGL = nullptr;
574   }
575 
576   if (mEGLImage) {
577     egl->fDestroyImage(mEGLImage);
578     mEGLImage = nullptr;
579   }
580 }
581 
ReleaseSurface()582 void DMABufSurfaceRGBA::ReleaseSurface() {
583   MOZ_ASSERT(!IsMapped(), "We can't release mapped buffer!");
584 
585   ReleaseTextures();
586   ReleaseDMABuf();
587 }
588 
589 // We should synchronize DMA Buffer object access from CPU to avoid potential
590 // cache incoherency and data loss.
591 // See
592 // https://01.org/linuxgraphics/gfx-docs/drm/driver-api/dma-buf.html#cpu-access-to-dma-buffer-objects
593 struct dma_buf_sync {
594   uint64_t flags;
595 };
596 #define DMA_BUF_SYNC_READ (1 << 0)
597 #define DMA_BUF_SYNC_WRITE (2 << 0)
598 #define DMA_BUF_SYNC_START (0 << 2)
599 #define DMA_BUF_SYNC_END (1 << 2)
600 #define DMA_BUF_BASE 'b'
601 #define DMA_BUF_IOCTL_SYNC _IOW(DMA_BUF_BASE, 0, struct dma_buf_sync)
602 
SyncDmaBuf(int aFd,uint64_t aFlags)603 static void SyncDmaBuf(int aFd, uint64_t aFlags) {
604   struct dma_buf_sync sync = {0};
605 
606   sync.flags = aFlags | DMA_BUF_SYNC_READ | DMA_BUF_SYNC_WRITE;
607   while (true) {
608     int ret;
609     ret = ioctl(aFd, DMA_BUF_IOCTL_SYNC, &sync);
610     if (ret == -1 && errno == EINTR) {
611       continue;
612     } else if (ret == -1) {
613       LOGDMABUF(
614           ("Failed to synchronize DMA buffer: %s FD %d", strerror(errno), aFd));
615       break;
616     } else {
617       break;
618     }
619   }
620 }
621 
MapInternal(uint32_t aX,uint32_t aY,uint32_t aWidth,uint32_t aHeight,uint32_t * aStride,int aGbmFlags,int aPlane)622 void* DMABufSurface::MapInternal(uint32_t aX, uint32_t aY, uint32_t aWidth,
623                                  uint32_t aHeight, uint32_t* aStride,
624                                  int aGbmFlags, int aPlane) {
625   NS_ASSERTION(!IsMapped(aPlane), "Already mapped!");
626   if (!mGbmBufferObject[aPlane]) {
627     NS_WARNING("We can't map DMABufSurfaceRGBA without mGbmBufferObject");
628     return nullptr;
629   }
630 
631   LOGDMABUF(
632       ("DMABufSurfaceRGBA::MapInternal() UID %d plane %d size %d x %d -> %d x "
633        "%d\n",
634        mUID, aPlane, aX, aY, aWidth, aHeight));
635 
636   mMappedRegionStride[aPlane] = 0;
637   mMappedRegionData[aPlane] = nullptr;
638   mMappedRegion[aPlane] = nsGbmLib::Map(
639       mGbmBufferObject[aPlane], aX, aY, aWidth, aHeight, aGbmFlags,
640       &mMappedRegionStride[aPlane], &mMappedRegionData[aPlane]);
641   if (!mMappedRegion[aPlane]) {
642     LOGDMABUF(("    Surface mapping failed: %s", strerror(errno)));
643     return nullptr;
644   }
645   if (aStride) {
646     *aStride = mMappedRegionStride[aPlane];
647   }
648 
649   MutexAutoLock lockFD(mSurfaceLock);
650   if (OpenFileDescriptorForPlane(aPlane)) {
651     SyncDmaBuf(mDmabufFds[aPlane], DMA_BUF_SYNC_START);
652     CloseFileDescriptorForPlane(aPlane);
653   }
654 
655   return mMappedRegion[aPlane];
656 }
657 
MapReadOnly(uint32_t aX,uint32_t aY,uint32_t aWidth,uint32_t aHeight,uint32_t * aStride)658 void* DMABufSurfaceRGBA::MapReadOnly(uint32_t aX, uint32_t aY, uint32_t aWidth,
659                                      uint32_t aHeight, uint32_t* aStride) {
660   return MapInternal(aX, aY, aWidth, aHeight, aStride, GBM_BO_TRANSFER_READ);
661 }
662 
MapReadOnly(uint32_t * aStride)663 void* DMABufSurfaceRGBA::MapReadOnly(uint32_t* aStride) {
664   return MapInternal(0, 0, mWidth, mHeight, aStride, GBM_BO_TRANSFER_READ);
665 }
666 
Map(uint32_t aX,uint32_t aY,uint32_t aWidth,uint32_t aHeight,uint32_t * aStride)667 void* DMABufSurfaceRGBA::Map(uint32_t aX, uint32_t aY, uint32_t aWidth,
668                              uint32_t aHeight, uint32_t* aStride) {
669   return MapInternal(aX, aY, aWidth, aHeight, aStride,
670                      GBM_BO_TRANSFER_READ_WRITE);
671 }
672 
Map(uint32_t * aStride)673 void* DMABufSurfaceRGBA::Map(uint32_t* aStride) {
674   return MapInternal(0, 0, mWidth, mHeight, aStride,
675                      GBM_BO_TRANSFER_READ_WRITE);
676 }
677 
Unmap(int aPlane)678 void DMABufSurface::Unmap(int aPlane) {
679   if (mMappedRegion[aPlane]) {
680     LOGDMABUF(("DMABufSurface::Unmap() UID %d plane %d\n", mUID, aPlane));
681     MutexAutoLock lockFD(mSurfaceLock);
682     if (OpenFileDescriptorForPlane(aPlane)) {
683       SyncDmaBuf(mDmabufFds[aPlane], DMA_BUF_SYNC_END);
684       CloseFileDescriptorForPlane(aPlane);
685     }
686     nsGbmLib::Unmap(mGbmBufferObject[aPlane], mMappedRegionData[aPlane]);
687     mMappedRegion[aPlane] = nullptr;
688     mMappedRegionData[aPlane] = nullptr;
689     mMappedRegionStride[aPlane] = 0;
690   }
691 }
692 
693 #ifdef DEBUG
DumpToFile(const char * pFile)694 void DMABufSurfaceRGBA::DumpToFile(const char* pFile) {
695   uint32_t stride;
696 
697   if (!MapReadOnly(&stride)) {
698     return;
699   }
700   cairo_surface_t* surface = nullptr;
701 
702   auto unmap = MakeScopeExit([&] {
703     if (surface) {
704       cairo_surface_destroy(surface);
705     }
706     Unmap();
707   });
708 
709   surface = cairo_image_surface_create_for_data(
710       (unsigned char*)mMappedRegion[0], CAIRO_FORMAT_ARGB32, mWidth, mHeight,
711       stride);
712   if (cairo_surface_status(surface) == CAIRO_STATUS_SUCCESS) {
713     cairo_surface_write_to_png(surface, pFile);
714   }
715 }
716 #endif
717 
718 #if 0
719 // Copy from source surface by GL
720 #  include "GLBlitHelper.h"
721 
722 bool DMABufSurfaceRGBA::CopyFrom(class DMABufSurface* aSourceSurface,
723                                         GLContext* aGLContext) {
724   MOZ_ASSERT(aSourceSurface->GetTexture());
725   MOZ_ASSERT(GetTexture());
726 
727   gfx::IntSize size(GetWidth(), GetHeight());
728   aGLContext->BlitHelper()->BlitTextureToTexture(aSourceSurface->GetTexture(),
729     GetTexture(), size, size);
730   return true;
731 }
732 #endif
733 
734 // TODO - Clear the surface by EGL
Clear()735 void DMABufSurfaceRGBA::Clear() {
736   uint32_t destStride;
737   void* destData = Map(&destStride);
738   memset(destData, 0, GetHeight() * destStride);
739   Unmap();
740 }
741 
HasAlpha()742 bool DMABufSurfaceRGBA::HasAlpha() {
743   return mGmbFormat ? mGmbFormat->mHasAlpha : true;
744 }
745 
GetFormat()746 gfx::SurfaceFormat DMABufSurfaceRGBA::GetFormat() {
747   return HasAlpha() ? gfx::SurfaceFormat::B8G8R8A8
748                     : gfx::SurfaceFormat::B8G8R8X8;
749 }
750 
751 // GL uses swapped R and B components so report accordingly.
GetFormatGL()752 gfx::SurfaceFormat DMABufSurfaceRGBA::GetFormatGL() {
753   return HasAlpha() ? gfx::SurfaceFormat::R8G8B8A8
754                     : gfx::SurfaceFormat::R8G8B8X8;
755 }
756 
CreateDMABufSurface(int aWidth,int aHeight,int aDMABufSurfaceFlags)757 already_AddRefed<DMABufSurfaceRGBA> DMABufSurfaceRGBA::CreateDMABufSurface(
758     int aWidth, int aHeight, int aDMABufSurfaceFlags) {
759   RefPtr<DMABufSurfaceRGBA> surf = new DMABufSurfaceRGBA();
760   if (!surf->Create(aWidth, aHeight, aDMABufSurfaceFlags)) {
761     return nullptr;
762   }
763   return surf.forget();
764 }
765 
CreateYUVSurface(const VADRMPRIMESurfaceDescriptor & aDesc)766 already_AddRefed<DMABufSurfaceYUV> DMABufSurfaceYUV::CreateYUVSurface(
767     const VADRMPRIMESurfaceDescriptor& aDesc) {
768   RefPtr<DMABufSurfaceYUV> surf = new DMABufSurfaceYUV();
769   LOGDMABUF(("DMABufSurfaceYUV::CreateYUVSurface() UID %d from desc\n",
770              surf->GetUID()));
771   if (!surf->UpdateYUVData(aDesc)) {
772     return nullptr;
773   }
774   return surf.forget();
775 }
776 
CreateYUVSurface(int aWidth,int aHeight,void ** aPixelData,int * aLineSizes)777 already_AddRefed<DMABufSurfaceYUV> DMABufSurfaceYUV::CreateYUVSurface(
778     int aWidth, int aHeight, void** aPixelData, int* aLineSizes) {
779   RefPtr<DMABufSurfaceYUV> surf = new DMABufSurfaceYUV();
780   LOGDMABUF(("DMABufSurfaceYUV::CreateYUVSurface() UID %d %d x %d\n",
781              surf->GetUID(), aWidth, aHeight));
782   if (!surf->Create(aWidth, aHeight, aPixelData, aLineSizes)) {
783     return nullptr;
784   }
785   return surf.forget();
786 }
787 
DMABufSurfaceYUV()788 DMABufSurfaceYUV::DMABufSurfaceYUV()
789     : DMABufSurface(SURFACE_NV12), mWidth(), mHeight(), mTexture() {
790   for (int i = 0; i < DMABUF_BUFFER_PLANES; i++) {
791     mEGLImage[i] = LOCAL_EGL_NO_IMAGE;
792   }
793 }
794 
~DMABufSurfaceYUV()795 DMABufSurfaceYUV::~DMABufSurfaceYUV() { ReleaseSurface(); }
796 
OpenFileDescriptorForPlane(int aPlane)797 bool DMABufSurfaceYUV::OpenFileDescriptorForPlane(int aPlane) {
798   // The fd is already opened, no need to reopen.
799   // This can happen when we import dmabuf surface from VA-API decoder,
800   // mGbmBufferObject is null and we don't close
801   // file descriptors for surface as they are our only reference to it.
802   if (mDmabufFds[aPlane] >= 0) {
803     return true;
804   }
805 
806   MOZ_RELEASE_ASSERT(mGbmBufferObject[aPlane] != nullptr,
807                      "DMABufSurfaceYUV::OpenFileDescriptorForPlane: Missing "
808                      "mGbmBufferObject object!");
809 
810   mDmabufFds[aPlane] = nsGbmLib::GetFd(mGbmBufferObject[aPlane]);
811   if (mDmabufFds[aPlane] < 0) {
812     CloseFileDescriptors();
813     return false;
814   }
815   return true;
816 }
817 
CloseFileDescriptorForPlane(int aPlane,bool aForceClose=false)818 void DMABufSurfaceYUV::CloseFileDescriptorForPlane(int aPlane,
819                                                    bool aForceClose = false) {
820   if ((aForceClose || mGbmBufferObject[aPlane]) && mDmabufFds[aPlane] >= 0) {
821     close(mDmabufFds[aPlane]);
822     mDmabufFds[aPlane] = -1;
823   }
824 }
825 
UpdateYUVData(const VADRMPRIMESurfaceDescriptor & aDesc)826 bool DMABufSurfaceYUV::UpdateYUVData(const VADRMPRIMESurfaceDescriptor& aDesc) {
827   if (aDesc.num_layers > DMABUF_BUFFER_PLANES ||
828       aDesc.num_objects > DMABUF_BUFFER_PLANES) {
829     return false;
830   }
831 
832   LOGDMABUF(("DMABufSurfaceYUV::UpdateYUVData() UID %d", mUID));
833   if (mDmabufFds[0] >= 0) {
834     LOGDMABUF(("    Already created!"));
835     return false;
836   }
837   if (aDesc.fourcc == VA_FOURCC_NV12) {
838     mSurfaceType = SURFACE_NV12;
839   } else if (aDesc.fourcc == VA_FOURCC_YV12) {
840     mSurfaceType = SURFACE_YUV420;
841   } else {
842     LOGDMABUF(("UpdateYUVData(): Can't import surface data of 0x%x format",
843                aDesc.fourcc));
844     return false;
845   }
846 
847   mBufferPlaneCount = aDesc.num_layers;
848   mBufferModifier = aDesc.objects[0].drm_format_modifier;
849 
850   for (unsigned int i = 0; i < aDesc.num_layers; i++) {
851     // Intel exports VA-API surfaces in one object,planes have the same FD.
852     // AMD exports surfaces in two objects with different FDs.
853     bool dupFD = (aDesc.layers[i].object_index[0] != i);
854     int fd = aDesc.objects[aDesc.layers[i].object_index[0]].fd;
855     mDmabufFds[i] = dupFD ? dup(fd) : fd;
856 
857     mDrmFormats[i] = aDesc.layers[i].drm_format;
858     mOffsets[i] = aDesc.layers[i].offset[0];
859     mStrides[i] = aDesc.layers[i].pitch[0];
860     mWidth[i] = aDesc.width >> i;
861     mHeight[i] = aDesc.height >> i;
862 
863     LOGDMABUF(("    plane %d size %d x %d format %x", i, mWidth[i], mHeight[i],
864                mDrmFormats[i]));
865   }
866 
867   return true;
868 }
869 
CreateYUVPlane(int aPlane,int aWidth,int aHeight,int aDrmFormat)870 bool DMABufSurfaceYUV::CreateYUVPlane(int aPlane, int aWidth, int aHeight,
871                                       int aDrmFormat) {
872   LOGDMABUF(("DMABufSurfaceYUV::CreateYUVPlane() UID %d size %d x %d", mUID,
873              aWidth, aHeight));
874 
875   mWidth[aPlane] = aWidth;
876   mHeight[aPlane] = aHeight;
877   mDrmFormats[aPlane] = aDrmFormat;
878 
879   mGbmBufferObject[aPlane] =
880       nsGbmLib::Create(GetDMABufDevice()->GetGbmDevice(), aWidth, aHeight,
881                        aDrmFormat, GBM_BO_USE_LINEAR);
882   if (!mGbmBufferObject[aPlane]) {
883     LOGDMABUF(("    Failed to create GbmBufferObject: %s", strerror(errno)));
884     return false;
885   }
886 
887   mStrides[aPlane] = nsGbmLib::GetStride(mGbmBufferObject[aPlane]);
888   mDmabufFds[aPlane] = -1;
889 
890   return true;
891 }
892 
UpdateYUVPlane(int aPlane,void * aPixelData,int aLineSize)893 void DMABufSurfaceYUV::UpdateYUVPlane(int aPlane, void* aPixelData,
894                                       int aLineSize) {
895   LOGDMABUF(
896       ("DMABufSurfaceYUV::UpdateYUVPlane() UID %d plane %d", mUID, aPlane));
897   if (aLineSize == mWidth[aPlane] &&
898       (int)mMappedRegionStride[aPlane] == mWidth[aPlane]) {
899     memcpy(mMappedRegion[aPlane], aPixelData, aLineSize * mHeight[aPlane]);
900   } else {
901     char* src = (char*)aPixelData;
902     char* dest = (char*)mMappedRegion[aPlane];
903     for (int i = 0; i < mHeight[aPlane]; i++) {
904       memcpy(dest, src, mWidth[aPlane]);
905       src += aLineSize;
906       dest += mMappedRegionStride[aPlane];
907     }
908   }
909 }
910 
UpdateYUVData(void ** aPixelData,int * aLineSizes)911 bool DMABufSurfaceYUV::UpdateYUVData(void** aPixelData, int* aLineSizes) {
912   LOGDMABUF(("DMABufSurfaceYUV::UpdateYUVData() UID %d", mUID));
913   if (mSurfaceType != SURFACE_YUV420) {
914     LOGDMABUF(("    UpdateYUVData can upload YUV420 surface type only!"));
915     return false;
916   }
917 
918   if (mBufferPlaneCount != 3) {
919     LOGDMABUF(("    DMABufSurfaceYUV planes does not match!"));
920     return false;
921   }
922 
923   auto unmapBuffers = MakeScopeExit([&] {
924     Unmap(0);
925     Unmap(1);
926     Unmap(2);
927   });
928 
929   // Map planes
930   for (int i = 0; i < mBufferPlaneCount; i++) {
931     MapInternal(0, 0, mWidth[i], mHeight[i], nullptr, GBM_BO_TRANSFER_WRITE, i);
932     if (!mMappedRegion[i]) {
933       LOGDMABUF(("    DMABufSurfaceYUV plane can't be mapped!"));
934       return false;
935     }
936     if ((int)mMappedRegionStride[i] < mWidth[i]) {
937       LOGDMABUF(("    DMABufSurfaceYUV plane size stride does not match!"));
938       return false;
939     }
940   }
941 
942   // Copy planes
943   for (int i = 0; i < mBufferPlaneCount; i++) {
944     UpdateYUVPlane(i, aPixelData[i], aLineSizes[i]);
945   }
946 
947   return true;
948 }
949 
Create(int aWidth,int aHeight,void ** aPixelData,int * aLineSizes)950 bool DMABufSurfaceYUV::Create(int aWidth, int aHeight, void** aPixelData,
951                               int* aLineSizes) {
952   LOGDMABUF(("DMABufSurfaceYUV::Create() UID %d size %d x %d", mUID, aWidth,
953              aHeight));
954 
955   mSurfaceType = SURFACE_YUV420;
956   mBufferPlaneCount = 3;
957 
958   if (!CreateYUVPlane(0, aWidth, aHeight, GBM_FORMAT_R8)) {
959     return false;
960   }
961   if (!CreateYUVPlane(1, aWidth >> 1, aHeight >> 1, GBM_FORMAT_R8)) {
962     return false;
963   }
964   if (!CreateYUVPlane(2, aWidth >> 1, aHeight >> 1, GBM_FORMAT_R8)) {
965     return false;
966   }
967 
968   return aPixelData != nullptr && aLineSizes != nullptr
969              ? UpdateYUVData(aPixelData, aLineSizes)
970              : true;
971 }
972 
Create(const SurfaceDescriptor & aDesc)973 bool DMABufSurfaceYUV::Create(const SurfaceDescriptor& aDesc) {
974   return ImportSurfaceDescriptor(aDesc);
975 }
976 
ImportSurfaceDescriptor(const SurfaceDescriptorDMABuf & aDesc)977 bool DMABufSurfaceYUV::ImportSurfaceDescriptor(
978     const SurfaceDescriptorDMABuf& aDesc) {
979   mBufferPlaneCount = aDesc.fds().Length();
980   mSurfaceType = (mBufferPlaneCount == 2) ? SURFACE_NV12 : SURFACE_YUV420;
981   mBufferModifier = aDesc.modifier();
982   mColorSpace = aDesc.yUVColorSpace();
983   mUID = aDesc.uid();
984 
985   LOGDMABUF(("DMABufSurfaceYUV::ImportSurfaceDescriptor() UID %d", mUID));
986 
987   MOZ_RELEASE_ASSERT(mBufferPlaneCount <= DMABUF_BUFFER_PLANES);
988   for (int i = 0; i < mBufferPlaneCount; i++) {
989     mDmabufFds[i] = aDesc.fds()[i].ClonePlatformHandle().release();
990     if (mDmabufFds[i] < 0) {
991       LOGDMABUF(("    failed to get DMABuf plane file descriptor: %s",
992                  strerror(errno)));
993       return false;
994     }
995     mWidth[i] = aDesc.width()[i];
996     mHeight[i] = aDesc.height()[i];
997     mDrmFormats[i] = aDesc.format()[i];
998     mStrides[i] = aDesc.strides()[i];
999     mOffsets[i] = aDesc.offsets()[i];
1000     LOGDMABUF(("    plane %d fd %d size %d x %d format %x", i, mDmabufFds[i],
1001                mWidth[i], mHeight[i], mDrmFormats[i]));
1002   }
1003 
1004   if (aDesc.fence().Length() > 0) {
1005     mSyncFd = aDesc.fence()[0].ClonePlatformHandle().release();
1006     if (mSyncFd < 0) {
1007       LOGDMABUF(
1008           ("    failed to get GL fence file descriptor: %s", strerror(errno)));
1009       return false;
1010     }
1011   }
1012 
1013   if (aDesc.refCount().Length() > 0) {
1014     GlobalRefCountImport(aDesc.refCount()[0].ClonePlatformHandle().release());
1015   }
1016 
1017   return true;
1018 }
1019 
Serialize(mozilla::layers::SurfaceDescriptor & aOutDescriptor)1020 bool DMABufSurfaceYUV::Serialize(
1021     mozilla::layers::SurfaceDescriptor& aOutDescriptor) {
1022   AutoTArray<uint32_t, DMABUF_BUFFER_PLANES> width;
1023   AutoTArray<uint32_t, DMABUF_BUFFER_PLANES> height;
1024   AutoTArray<uint32_t, DMABUF_BUFFER_PLANES> format;
1025   AutoTArray<ipc::FileDescriptor, DMABUF_BUFFER_PLANES> fds;
1026   AutoTArray<uint32_t, DMABUF_BUFFER_PLANES> strides;
1027   AutoTArray<uint32_t, DMABUF_BUFFER_PLANES> offsets;
1028   AutoTArray<ipc::FileDescriptor, 1> fenceFDs;
1029   AutoTArray<ipc::FileDescriptor, 1> refCountFDs;
1030 
1031   LOGDMABUF(("DMABufSurfaceYUV::Serialize() UID %d", mUID));
1032 
1033   MutexAutoLock lockFD(mSurfaceLock);
1034   if (!OpenFileDescriptors()) {
1035     return false;
1036   }
1037 
1038   for (int i = 0; i < mBufferPlaneCount; i++) {
1039     width.AppendElement(mWidth[i]);
1040     height.AppendElement(mHeight[i]);
1041     format.AppendElement(mDrmFormats[i]);
1042     fds.AppendElement(ipc::FileDescriptor(mDmabufFds[i]));
1043     strides.AppendElement(mStrides[i]);
1044     offsets.AppendElement(mOffsets[i]);
1045   }
1046 
1047   CloseFileDescriptors();
1048 
1049   if (mSync) {
1050     fenceFDs.AppendElement(ipc::FileDescriptor(mSyncFd));
1051   }
1052 
1053   if (mGlobalRefCountFd) {
1054     refCountFDs.AppendElement(ipc::FileDescriptor(mGlobalRefCountFd));
1055   }
1056 
1057   aOutDescriptor = SurfaceDescriptorDMABuf(
1058       mSurfaceType, mBufferModifier, 0, fds, width, height, format, strides,
1059       offsets, GetYUVColorSpace(), fenceFDs, mUID, refCountFDs);
1060   return true;
1061 }
1062 
CreateTexture(GLContext * aGLContext,int aPlane)1063 bool DMABufSurfaceYUV::CreateTexture(GLContext* aGLContext, int aPlane) {
1064   LOGDMABUF(
1065       ("DMABufSurfaceYUV::CreateTexture() UID %d plane %d", mUID, aPlane));
1066   MOZ_ASSERT(!mEGLImage[aPlane] && !mTexture[aPlane],
1067              "EGLImage/Texture is already created!");
1068 
1069   if (!aGLContext) return false;
1070   const auto& gle = gl::GLContextEGL::Cast(aGLContext);
1071   const auto& egl = gle->mEgl;
1072 
1073   MutexAutoLock lockFD(mSurfaceLock);
1074   if (!OpenFileDescriptorForPlane(aPlane)) {
1075     return false;
1076   }
1077 
1078   nsTArray<EGLint> attribs;
1079   attribs.AppendElement(LOCAL_EGL_WIDTH);
1080   attribs.AppendElement(mWidth[aPlane]);
1081   attribs.AppendElement(LOCAL_EGL_HEIGHT);
1082   attribs.AppendElement(mHeight[aPlane]);
1083   attribs.AppendElement(LOCAL_EGL_LINUX_DRM_FOURCC_EXT);
1084   attribs.AppendElement(mDrmFormats[aPlane]);
1085 #define ADD_PLANE_ATTRIBS_NV12(plane_idx)                                 \
1086   attribs.AppendElement(LOCAL_EGL_DMA_BUF_PLANE##plane_idx##_FD_EXT);     \
1087   attribs.AppendElement(mDmabufFds[aPlane]);                              \
1088   attribs.AppendElement(LOCAL_EGL_DMA_BUF_PLANE##plane_idx##_OFFSET_EXT); \
1089   attribs.AppendElement((int)mOffsets[aPlane]);                           \
1090   attribs.AppendElement(LOCAL_EGL_DMA_BUF_PLANE##plane_idx##_PITCH_EXT);  \
1091   attribs.AppendElement((int)mStrides[aPlane]);
1092   ADD_PLANE_ATTRIBS_NV12(0);
1093 #undef ADD_PLANE_ATTRIBS_NV12
1094   attribs.AppendElement(LOCAL_EGL_NONE);
1095 
1096   mEGLImage[aPlane] =
1097       egl->fCreateImage(LOCAL_EGL_NO_CONTEXT, LOCAL_EGL_LINUX_DMA_BUF_EXT,
1098                         nullptr, attribs.Elements());
1099 
1100   CloseFileDescriptorForPlane(aPlane);
1101 
1102   if (mEGLImage[aPlane] == LOCAL_EGL_NO_IMAGE) {
1103     LOGDMABUF(("    EGLImageKHR creation failed"));
1104     return false;
1105   }
1106 
1107   aGLContext->MakeCurrent();
1108   aGLContext->fGenTextures(1, &mTexture[aPlane]);
1109   const ScopedBindTexture savedTex(aGLContext, mTexture[aPlane]);
1110   aGLContext->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_S,
1111                              LOCAL_GL_CLAMP_TO_EDGE);
1112   aGLContext->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_T,
1113                              LOCAL_GL_CLAMP_TO_EDGE);
1114   aGLContext->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MAG_FILTER,
1115                              LOCAL_GL_LINEAR);
1116   aGLContext->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MIN_FILTER,
1117                              LOCAL_GL_LINEAR);
1118   aGLContext->fEGLImageTargetTexture2D(LOCAL_GL_TEXTURE_2D, mEGLImage[aPlane]);
1119   mGL = aGLContext;
1120   return true;
1121 }
1122 
ReleaseTextures()1123 void DMABufSurfaceYUV::ReleaseTextures() {
1124   LOGDMABUF(("DMABufSurfaceYUV::ReleaseTextures() UID %d", mUID));
1125 
1126   FenceDelete();
1127 
1128   bool textureActive = false;
1129   for (int i = 0; i < mBufferPlaneCount; i++) {
1130     if (mTexture[i]) {
1131       textureActive = true;
1132       break;
1133     }
1134   }
1135 
1136   if (!mGL) return;
1137   const auto& gle = gl::GLContextEGL::Cast(mGL);
1138   const auto& egl = gle->mEgl;
1139 
1140   if (textureActive && mGL->MakeCurrent()) {
1141     mGL->fDeleteTextures(DMABUF_BUFFER_PLANES, mTexture);
1142     for (int i = 0; i < DMABUF_BUFFER_PLANES; i++) {
1143       mTexture[i] = 0;
1144     }
1145     mGL = nullptr;
1146   }
1147 
1148   for (int i = 0; i < mBufferPlaneCount; i++) {
1149     if (mEGLImage[i]) {
1150       egl->fDestroyImage(mEGLImage[i]);
1151       mEGLImage[i] = nullptr;
1152     }
1153   }
1154 }
1155 
GetFormat()1156 gfx::SurfaceFormat DMABufSurfaceYUV::GetFormat() {
1157   switch (mSurfaceType) {
1158     case SURFACE_NV12:
1159       return gfx::SurfaceFormat::NV12;
1160     case SURFACE_YUV420:
1161       return gfx::SurfaceFormat::YUV;
1162     default:
1163       NS_WARNING("DMABufSurfaceYUV::GetFormat(): Wrong surface format!");
1164       return gfx::SurfaceFormat::UNKNOWN;
1165   }
1166 }
1167 
1168 // GL uses swapped R and B components so report accordingly.
GetFormatGL()1169 gfx::SurfaceFormat DMABufSurfaceYUV::GetFormatGL() { return GetFormat(); }
1170 
GetTextureCount()1171 uint32_t DMABufSurfaceYUV::GetTextureCount() {
1172   switch (mSurfaceType) {
1173     case SURFACE_NV12:
1174       return 2;
1175     case SURFACE_YUV420:
1176       return 3;
1177     default:
1178       NS_WARNING("DMABufSurfaceYUV::GetTextureCount(): Wrong surface format!");
1179       return 1;
1180   }
1181 }
1182 
ReleaseSurface()1183 void DMABufSurfaceYUV::ReleaseSurface() {
1184   LOGDMABUF(("DMABufSurfaceYUV::ReleaseSurface() UID %d", mUID));
1185   ReleaseTextures();
1186   ReleaseDMABuf();
1187 }
1188