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/. */
7 #include "DMABufSurface.h"
8 #include "DMABufLibWrapper.h"
11 # include "nsWaylandDisplay.h"
27 # include <sys/eventfd.h>
30 #ifdef HAVE_SYSIOCCOM_H
31 # include <sys/ioccom.h>
33 #include <sys/ioctl.h>
35 #include "mozilla/widget/gbm.h"
36 #include "mozilla/widget/va_drmcommon.h"
37 #include "YCbCrUtils.h"
38 #include "mozilla/gfx/2D.h"
39 #include "GLContextTypes.h" // for GLContext, etc
40 #include "GLContextEGL.h"
41 #include "GLContextProvider.h"
42 #include "ScopedGLHelpers.h"
43 #include "GLBlitHelper.h"
44 #include "GLReadTexImageHelper.h"
45 #include "nsGtkUtils.h"
47 #include "mozilla/layers/LayersSurfaces.h"
48 #include "mozilla/ScopeExit.h"
53 https://lists.freedesktop.org/archives/wayland-devel/2018-November/039660.html
56 /* C++ / C typecast macros for special EGL handle values */
57 #if defined(__cplusplus)
58 # define EGL_CAST(type, value) (static_cast<type>(value))
60 # define EGL_CAST(type, value) ((type)(value))
63 using namespace mozilla
;
64 using namespace mozilla::widget
;
65 using namespace mozilla::gl
;
66 using namespace mozilla::layers
;
67 using namespace mozilla::gfx
;
70 # include "mozilla/Logging.h"
71 # include "nsTArray.h"
73 static LazyLogModule
gDmabufRefLog("DmabufRef");
74 # define LOGDMABUFREF(args) \
75 MOZ_LOG(gDmabufRefLog, mozilla::LogLevel::Debug, args)
77 # define LOGDMABUFREF(args)
78 #endif /* MOZ_LOGGING */
80 #define BUFFER_FLAGS 0
82 static RefPtr
<GLContext
> sSnapshotContext
;
83 static StaticMutex sSnapshotContextMutex MOZ_UNANNOTATED
;
84 static Atomic
<int> gNewSurfaceUID(1);
86 RefPtr
<GLContext
> ClaimSnapshotGLContext() {
87 if (!sSnapshotContext
) {
88 nsCString discardFailureId
;
89 sSnapshotContext
= GLContextProvider::CreateHeadless({}, &discardFailureId
);
90 if (!sSnapshotContext
) {
92 ("ClaimSnapshotGLContext: Failed to create snapshot GLContext."));
95 sSnapshotContext
->mOwningThreadId
= Nothing(); // No singular owner.
97 if (!sSnapshotContext
->MakeCurrent()) {
98 LOGDMABUF(("ClaimSnapshotGLContext: Failed to make GLContext current."));
101 return sSnapshotContext
;
104 void ReturnSnapshotGLContext(RefPtr
<GLContext
> aGLContext
) {
105 // direct eglMakeCurrent() call breaks current context caching so make sure
107 MOZ_ASSERT(!aGLContext
->mUseTLSIsCurrent
);
108 if (!aGLContext
->IsCurrent()) {
109 LOGDMABUF(("ReturnSnapshotGLContext() failed, is not current!"));
112 const auto& gle
= gl::GLContextEGL::Cast(aGLContext
);
113 const auto& egl
= gle
->mEgl
;
114 egl
->fMakeCurrent(EGL_NO_SURFACE
, EGL_NO_SURFACE
, EGL_NO_CONTEXT
);
117 bool DMABufSurface::IsGlobalRefSet() const {
118 if (!mGlobalRefCountFd
) {
122 pfd
.fd
= mGlobalRefCountFd
;
124 return poll(&pfd
, 1, 0) == 1;
127 void DMABufSurface::GlobalRefRelease() {
129 if (!mGlobalRefCountFd
) {
132 LOGDMABUFREF(("DMABufSurface::GlobalRefRelease UID %d", mUID
));
134 if (read(mGlobalRefCountFd
, &counter
, sizeof(counter
)) != sizeof(counter
)) {
135 if (errno
== EAGAIN
) {
137 (" GlobalRefRelease failed: already zero reference! UID %d", mUID
));
139 // EAGAIN means the refcount is already zero. It happens when we release
140 // last reference to the surface.
141 if (errno
!= EAGAIN
) {
142 NS_WARNING(nsPrintfCString("Failed to unref dmabuf global ref count: %s",
150 void DMABufSurface::GlobalRefAdd() {
152 LOGDMABUFREF(("DMABufSurface::GlobalRefAdd UID %d", mUID
));
153 MOZ_DIAGNOSTIC_ASSERT(mGlobalRefCountFd
);
154 uint64_t counter
= 1;
155 if (write(mGlobalRefCountFd
, &counter
, sizeof(counter
)) != sizeof(counter
)) {
156 NS_WARNING(nsPrintfCString("Failed to ref dmabuf global ref count: %s",
163 void DMABufSurface::GlobalRefCountCreate() {
165 LOGDMABUFREF(("DMABufSurface::GlobalRefCountCreate UID %d", mUID
));
166 MOZ_DIAGNOSTIC_ASSERT(!mGlobalRefCountFd
);
167 // Create global ref count initialized to 0,
168 // i.e. is not referenced after create.
169 mGlobalRefCountFd
= eventfd(0, EFD_CLOEXEC
| EFD_NONBLOCK
| EFD_SEMAPHORE
);
170 if (mGlobalRefCountFd
< 0) {
171 NS_WARNING(nsPrintfCString("Failed to create dmabuf global ref count: %s",
174 mGlobalRefCountFd
= 0;
180 void DMABufSurface::GlobalRefCountImport(int aFd
) {
182 mGlobalRefCountFd
= aFd
;
183 if (mGlobalRefCountFd
) {
184 LOGDMABUFREF(("DMABufSurface::GlobalRefCountImport UID %d", mUID
));
190 int DMABufSurface::GlobalRefCountExport() {
192 if (mGlobalRefCountFd
) {
193 LOGDMABUFREF(("DMABufSurface::GlobalRefCountExport UID %d", mUID
));
196 return mGlobalRefCountFd
;
199 void DMABufSurface::GlobalRefCountDelete() {
200 if (mGlobalRefCountFd
) {
201 LOGDMABUFREF(("DMABufSurface::GlobalRefCountDelete UID %d", mUID
));
202 close(mGlobalRefCountFd
);
203 mGlobalRefCountFd
= 0;
207 void DMABufSurface::ReleaseDMABuf() {
208 LOGDMABUF(("DMABufSurface::ReleaseDMABuf() UID %d", mUID
));
209 for (int i
= 0; i
< mBufferPlaneCount
; i
++) {
213 MutexAutoLock
lockFD(mSurfaceLock
);
214 CloseFileDescriptors(lockFD
, /* aForceClose */ true);
216 for (int i
= 0; i
< mBufferPlaneCount
; i
++) {
217 if (mGbmBufferObject
[i
]) {
218 GbmLib::Destroy(mGbmBufferObject
[i
]);
219 mGbmBufferObject
[i
] = nullptr;
222 mBufferPlaneCount
= 0;
225 DMABufSurface::DMABufSurface(SurfaceType aSurfaceType
)
226 : mSurfaceType(aSurfaceType
),
227 mBufferPlaneCount(0),
233 mMappedRegionStride(),
236 mGlobalRefCountFd(0),
237 mUID(gNewSurfaceUID
++),
238 mSurfaceLock("DMABufSurface") {
239 for (auto& slot
: mDmabufFds
) {
242 for (auto& modifier
: mBufferModifiers
) {
243 modifier
= DRM_FORMAT_MOD_INVALID
;
247 DMABufSurface::~DMABufSurface() {
250 GlobalRefCountDelete();
253 already_AddRefed
<DMABufSurface
> DMABufSurface::CreateDMABufSurface(
254 const mozilla::layers::SurfaceDescriptor
& aDesc
) {
255 const SurfaceDescriptorDMABuf
& desc
= aDesc
.get_SurfaceDescriptorDMABuf();
256 RefPtr
<DMABufSurface
> surf
;
258 switch (desc
.bufferType()) {
260 surf
= new DMABufSurfaceRGBA();
264 surf
= new DMABufSurfaceYUV();
270 if (!surf
->Create(desc
)) {
273 return surf
.forget();
276 void DMABufSurface::FenceDelete() {
285 const auto& gle
= gl::GLContextEGL::Cast(mGL
);
286 const auto& egl
= gle
->mEgl
;
289 egl
->fDestroySync(mSync
);
294 void DMABufSurface::FenceSet() {
295 if (!mGL
|| !mGL
->MakeCurrent()) {
296 MOZ_DIAGNOSTIC_ASSERT(mGL
,
297 "DMABufSurface::FenceSet(): missing GL context!");
300 const auto& gle
= gl::GLContextEGL::Cast(mGL
);
301 const auto& egl
= gle
->mEgl
;
303 if (egl
->IsExtensionSupported(EGLExtension::KHR_fence_sync
) &&
304 egl
->IsExtensionSupported(EGLExtension::ANDROID_native_fence_sync
)) {
307 mSync
= egl
->fCreateSync(LOCAL_EGL_SYNC_NATIVE_FENCE_ANDROID
, nullptr);
309 mSyncFd
= egl
->fDupNativeFenceFDANDROID(mSync
);
315 // ANDROID_native_fence_sync may not be supported so call glFinish()
320 void DMABufSurface::FenceWait() {
321 if (!mGL
|| mSyncFd
< 0) {
322 MOZ_DIAGNOSTIC_ASSERT(mGL
,
323 "DMABufSurface::FenceWait() missing GL context!");
327 const auto& gle
= gl::GLContextEGL::Cast(mGL
);
328 const auto& egl
= gle
->mEgl
;
330 const EGLint attribs
[] = {LOCAL_EGL_SYNC_NATIVE_FENCE_FD_ANDROID
, mSyncFd
,
332 EGLSync sync
= egl
->fCreateSync(LOCAL_EGL_SYNC_NATIVE_FENCE_ANDROID
, attribs
);
334 MOZ_ASSERT(false, "DMABufSurface::FenceWait(): Failed to create GLFence!");
335 // We failed to create GLFence so clear mSyncFd to avoid another try.
341 // mSyncFd is owned by GLFence so clear local reference to avoid double close
342 // at DMABufSurface::FenceDelete().
345 egl
->fClientWaitSync(sync
, 0, LOCAL_EGL_FOREVER
);
346 egl
->fDestroySync(sync
);
349 bool DMABufSurface::OpenFileDescriptors(const MutexAutoLock
& aProofOfLock
) {
350 for (int i
= 0; i
< mBufferPlaneCount
; i
++) {
351 if (!OpenFileDescriptorForPlane(aProofOfLock
, i
)) {
358 // We can safely close DMABuf file descriptors only when we have a valid
359 // GbmBufferObject. When we don't have a valid GbmBufferObject and a DMABuf
360 // file descriptor is closed, whole surface is released.
361 void DMABufSurface::CloseFileDescriptors(const MutexAutoLock
& aProofOfLock
,
363 for (int i
= 0; i
< DMABUF_BUFFER_PLANES
; i
++) {
364 CloseFileDescriptorForPlane(aProofOfLock
, i
, aForceClose
);
368 DMABufSurfaceRGBA::DMABufSurfaceRGBA()
369 : DMABufSurface(SURFACE_RGBA
),
374 mEGLImage(LOCAL_EGL_NO_IMAGE
),
376 mGbmBufferFlags(0) {}
378 DMABufSurfaceRGBA::~DMABufSurfaceRGBA() {
385 bool DMABufSurfaceRGBA::OpenFileDescriptorForPlane(
386 const MutexAutoLock
& aProofOfLock
, int aPlane
) {
387 if (mDmabufFds
[aPlane
] >= 0) {
390 gbm_bo
* bo
= mGbmBufferObject
[0];
391 if (NS_WARN_IF(!bo
)) {
393 ("DMABufSurfaceRGBA::OpenFileDescriptorForPlane: Missing "
394 "mGbmBufferObject object!"));
398 if (mBufferPlaneCount
== 1) {
399 MOZ_ASSERT(aPlane
== 0, "DMABuf: wrong surface plane!");
400 mDmabufFds
[0] = GbmLib::GetFd(bo
);
402 mDmabufFds
[aPlane
] = GetDMABufDevice()->GetDmabufFD(
403 GbmLib::GetHandleForPlane(bo
, aPlane
).u32
);
406 if (mDmabufFds
[aPlane
] < 0) {
407 CloseFileDescriptors(aProofOfLock
);
414 void DMABufSurfaceRGBA::CloseFileDescriptorForPlane(
415 const MutexAutoLock
& aProofOfLock
, int aPlane
, bool aForceClose
= false) {
416 if ((aForceClose
|| mGbmBufferObject
[0]) && mDmabufFds
[aPlane
] >= 0) {
417 close(mDmabufFds
[aPlane
]);
418 mDmabufFds
[aPlane
] = -1;
422 bool DMABufSurfaceRGBA::Create(int aWidth
, int aHeight
,
423 int aDMABufSurfaceFlags
) {
424 MOZ_ASSERT(mGbmBufferObject
[0] == nullptr, "Already created?");
426 mSurfaceFlags
= aDMABufSurfaceFlags
;
430 LOGDMABUF(("DMABufSurfaceRGBA::Create() UID %d size %d x %d\n", mUID
, mWidth
,
433 if (!GetDMABufDevice()->GetGbmDevice()) {
434 LOGDMABUF((" Missing GbmDevice!"));
438 mGmbFormat
= GetDMABufDevice()->GetGbmFormat(mSurfaceFlags
& DMABUF_ALPHA
);
440 // Requested DRM format is not supported.
443 mDrmFormats
[0] = mGmbFormat
->mFormat
;
445 bool useModifiers
= (aDMABufSurfaceFlags
& DMABUF_USE_MODIFIERS
) &&
446 !mGmbFormat
->mModifiers
.IsEmpty();
448 LOGDMABUF((" Creating with modifiers\n"));
449 mGbmBufferObject
[0] = GbmLib::CreateWithModifiers(
450 GetDMABufDevice()->GetGbmDevice(), mWidth
, mHeight
, mDrmFormats
[0],
451 mGmbFormat
->mModifiers
.Elements(), mGmbFormat
->mModifiers
.Length());
452 if (mGbmBufferObject
[0]) {
453 mBufferModifiers
[0] = GbmLib::GetModifier(mGbmBufferObject
[0]);
457 if (!mGbmBufferObject
[0]) {
458 LOGDMABUF((" Creating without modifiers\n"));
459 mGbmBufferFlags
= GBM_BO_USE_LINEAR
;
460 mGbmBufferObject
[0] =
461 GbmLib::Create(GetDMABufDevice()->GetGbmDevice(), mWidth
, mHeight
,
462 mDrmFormats
[0], mGbmBufferFlags
);
463 mBufferModifiers
[0] = DRM_FORMAT_MOD_INVALID
;
466 if (!mGbmBufferObject
[0]) {
467 LOGDMABUF((" Failed to create GbmBufferObject\n"));
471 if (mBufferModifiers
[0] != DRM_FORMAT_MOD_INVALID
) {
472 mBufferPlaneCount
= GbmLib::GetPlaneCount(mGbmBufferObject
[0]);
473 if (mBufferPlaneCount
> DMABUF_BUFFER_PLANES
) {
474 LOGDMABUF((" There's too many dmabuf planes!"));
479 for (int i
= 0; i
< mBufferPlaneCount
; i
++) {
480 mStrides
[i
] = GbmLib::GetStrideForPlane(mGbmBufferObject
[0], i
);
481 mOffsets
[i
] = GbmLib::GetOffset(mGbmBufferObject
[0], i
);
484 mBufferPlaneCount
= 1;
485 mStrides
[0] = GbmLib::GetStride(mGbmBufferObject
[0]);
488 LOGDMABUF((" Success\n"));
492 bool DMABufSurfaceRGBA::Create(mozilla::gl::GLContext
* aGLContext
,
493 const EGLImageKHR aEGLImage
, int aWidth
,
495 LOGDMABUF(("DMABufSurfaceRGBA::Create() from EGLImage UID = %d\n", mUID
));
499 const auto& gle
= gl::GLContextEGL::Cast(aGLContext
);
500 const auto& egl
= gle
->mEgl
;
505 mEGLImage
= aEGLImage
;
506 if (!egl
->fExportDMABUFImageQuery(mEGLImage
, mDrmFormats
, &mBufferPlaneCount
,
508 LOGDMABUF((" ExportDMABUFImageQueryMESA failed, quit\n"));
511 if (mBufferPlaneCount
> DMABUF_BUFFER_PLANES
) {
512 LOGDMABUF((" wrong plane count %d, quit\n", mBufferPlaneCount
));
515 if (!egl
->fExportDMABUFImage(mEGLImage
, mDmabufFds
, mStrides
, mOffsets
)) {
516 LOGDMABUF((" ExportDMABUFImageMESA failed, quit\n"));
520 // A broken driver can return dmabuf without valid file descriptors
521 // which leads to fails later so quit now.
522 for (int i
= 0; i
< mBufferPlaneCount
; i
++) {
523 if (mDmabufFds
[i
] < 0) {
525 (" ExportDMABUFImageMESA failed, mDmabufFds[%d] is invalid, quit",
531 LOGDMABUF((" imported size %d x %d format %x planes %d modifiers %" PRIx64
,
532 mWidth
, mHeight
, mDrmFormats
[0], mBufferPlaneCount
,
533 mBufferModifiers
[0]));
537 bool DMABufSurfaceRGBA::ImportSurfaceDescriptor(
538 const SurfaceDescriptor
& aDesc
) {
539 const SurfaceDescriptorDMABuf
& desc
= aDesc
.get_SurfaceDescriptorDMABuf();
541 mWidth
= desc
.width()[0];
542 mHeight
= desc
.height()[0];
543 mBufferModifiers
[0] = desc
.modifier()[0];
544 mDrmFormats
[0] = desc
.format()[0];
545 mBufferPlaneCount
= desc
.fds().Length();
546 mGbmBufferFlags
= desc
.flags();
547 MOZ_RELEASE_ASSERT(mBufferPlaneCount
<= DMABUF_BUFFER_PLANES
);
551 ("DMABufSurfaceRGBA::ImportSurfaceDescriptor() UID %d size %d x %d\n",
552 mUID
, mWidth
, mHeight
));
554 for (int i
= 0; i
< mBufferPlaneCount
; i
++) {
555 mDmabufFds
[i
] = desc
.fds()[i
].ClonePlatformHandle().release();
556 if (mDmabufFds
[i
] < 0) {
558 (" failed to get DMABuf file descriptor: %s", strerror(errno
)));
561 mStrides
[i
] = desc
.strides()[i
];
562 mOffsets
[i
] = desc
.offsets()[i
];
565 if (desc
.fence().Length() > 0) {
566 mSyncFd
= desc
.fence()[0].ClonePlatformHandle().release();
569 (" failed to get GL fence file descriptor: %s", strerror(errno
)));
574 if (desc
.refCount().Length() > 0) {
575 GlobalRefCountImport(desc
.refCount()[0].ClonePlatformHandle().release());
578 LOGDMABUF((" imported size %d x %d format %x planes %d", mWidth
, mHeight
,
579 mDrmFormats
[0], mBufferPlaneCount
));
583 bool DMABufSurfaceRGBA::Create(const SurfaceDescriptor
& aDesc
) {
584 return ImportSurfaceDescriptor(aDesc
);
587 bool DMABufSurfaceRGBA::Serialize(
588 mozilla::layers::SurfaceDescriptor
& aOutDescriptor
) {
589 AutoTArray
<uint32_t, DMABUF_BUFFER_PLANES
> width
;
590 AutoTArray
<uint32_t, DMABUF_BUFFER_PLANES
> height
;
591 AutoTArray
<uint32_t, DMABUF_BUFFER_PLANES
> format
;
592 AutoTArray
<ipc::FileDescriptor
, DMABUF_BUFFER_PLANES
> fds
;
593 AutoTArray
<uint32_t, DMABUF_BUFFER_PLANES
> strides
;
594 AutoTArray
<uint32_t, DMABUF_BUFFER_PLANES
> offsets
;
595 AutoTArray
<uintptr_t, DMABUF_BUFFER_PLANES
> images
;
596 AutoTArray
<uint64_t, DMABUF_BUFFER_PLANES
> modifiers
;
597 AutoTArray
<ipc::FileDescriptor
, 1> fenceFDs
;
598 AutoTArray
<ipc::FileDescriptor
, 1> refCountFDs
;
600 LOGDMABUF(("DMABufSurfaceRGBA::Serialize() UID %d\n", mUID
));
602 MutexAutoLock
lockFD(mSurfaceLock
);
603 if (!OpenFileDescriptors(lockFD
)) {
607 width
.AppendElement(mWidth
);
608 height
.AppendElement(mHeight
);
609 format
.AppendElement(mDrmFormats
[0]);
610 modifiers
.AppendElement(mBufferModifiers
[0]);
611 for (int i
= 0; i
< mBufferPlaneCount
; i
++) {
612 fds
.AppendElement(ipc::FileDescriptor(mDmabufFds
[i
]));
613 strides
.AppendElement(mStrides
[i
]);
614 offsets
.AppendElement(mOffsets
[i
]);
617 CloseFileDescriptors(lockFD
);
620 fenceFDs
.AppendElement(ipc::FileDescriptor(mSyncFd
));
623 if (mGlobalRefCountFd
) {
624 refCountFDs
.AppendElement(ipc::FileDescriptor(GlobalRefCountExport()));
627 aOutDescriptor
= SurfaceDescriptorDMABuf(
628 mSurfaceType
, modifiers
, mGbmBufferFlags
, fds
, width
, height
, width
,
629 height
, format
, strides
, offsets
, GetYUVColorSpace(), mColorRange
,
630 fenceFDs
, mUID
, refCountFDs
);
634 bool DMABufSurfaceRGBA::CreateTexture(GLContext
* aGLContext
, int aPlane
) {
635 LOGDMABUF(("DMABufSurfaceRGBA::CreateTexture() UID %d\n", mUID
));
636 MOZ_ASSERT(!mEGLImage
&& !mTexture
, "EGLImage is already created!");
638 nsTArray
<EGLint
> attribs
;
639 attribs
.AppendElement(LOCAL_EGL_WIDTH
);
640 attribs
.AppendElement(mWidth
);
641 attribs
.AppendElement(LOCAL_EGL_HEIGHT
);
642 attribs
.AppendElement(mHeight
);
643 attribs
.AppendElement(LOCAL_EGL_LINUX_DRM_FOURCC_EXT
);
644 attribs
.AppendElement(mDrmFormats
[0]);
645 #define ADD_PLANE_ATTRIBS(plane_idx) \
647 attribs.AppendElement(LOCAL_EGL_DMA_BUF_PLANE##plane_idx##_FD_EXT); \
648 attribs.AppendElement(mDmabufFds[plane_idx]); \
649 attribs.AppendElement(LOCAL_EGL_DMA_BUF_PLANE##plane_idx##_OFFSET_EXT); \
650 attribs.AppendElement((int)mOffsets[plane_idx]); \
651 attribs.AppendElement(LOCAL_EGL_DMA_BUF_PLANE##plane_idx##_PITCH_EXT); \
652 attribs.AppendElement((int)mStrides[plane_idx]); \
653 if (mBufferModifiers[0] != DRM_FORMAT_MOD_INVALID) { \
654 attribs.AppendElement( \
655 LOCAL_EGL_DMA_BUF_PLANE##plane_idx##_MODIFIER_LO_EXT); \
656 attribs.AppendElement(mBufferModifiers[0] & 0xFFFFFFFF); \
657 attribs.AppendElement( \
658 LOCAL_EGL_DMA_BUF_PLANE##plane_idx##_MODIFIER_HI_EXT); \
659 attribs.AppendElement(mBufferModifiers[0] >> 32); \
663 MutexAutoLock
lockFD(mSurfaceLock
);
664 if (!OpenFileDescriptors(lockFD
)) {
667 ADD_PLANE_ATTRIBS(0);
668 if (mBufferPlaneCount
> 1) ADD_PLANE_ATTRIBS(1);
669 if (mBufferPlaneCount
> 2) ADD_PLANE_ATTRIBS(2);
670 if (mBufferPlaneCount
> 3) ADD_PLANE_ATTRIBS(3);
671 #undef ADD_PLANE_ATTRIBS
672 attribs
.AppendElement(LOCAL_EGL_NONE
);
674 if (!aGLContext
) return false;
675 const auto& gle
= gl::GLContextEGL::Cast(aGLContext
);
676 const auto& egl
= gle
->mEgl
;
678 egl
->fCreateImage(LOCAL_EGL_NO_CONTEXT
, LOCAL_EGL_LINUX_DMA_BUF_EXT
,
679 nullptr, attribs
.Elements());
681 CloseFileDescriptors(lockFD
);
683 if (mEGLImage
== LOCAL_EGL_NO_IMAGE
) {
684 LOGDMABUF(("EGLImageKHR creation failed"));
688 if (!aGLContext
->MakeCurrent()) {
690 ("DMABufSurfaceRGBA::CreateTexture(): failed to make GL context "
694 aGLContext
->fGenTextures(1, &mTexture
);
695 const ScopedBindTexture
savedTex(aGLContext
, mTexture
);
696 aGLContext
->fTexParameteri(LOCAL_GL_TEXTURE_2D
, LOCAL_GL_TEXTURE_WRAP_S
,
697 LOCAL_GL_CLAMP_TO_EDGE
);
698 aGLContext
->fTexParameteri(LOCAL_GL_TEXTURE_2D
, LOCAL_GL_TEXTURE_WRAP_T
,
699 LOCAL_GL_CLAMP_TO_EDGE
);
700 aGLContext
->fTexParameteri(LOCAL_GL_TEXTURE_2D
, LOCAL_GL_TEXTURE_MAG_FILTER
,
702 aGLContext
->fTexParameteri(LOCAL_GL_TEXTURE_2D
, LOCAL_GL_TEXTURE_MIN_FILTER
,
704 aGLContext
->fEGLImageTargetTexture2D(LOCAL_GL_TEXTURE_2D
, mEGLImage
);
710 void DMABufSurfaceRGBA::ReleaseTextures() {
711 LOGDMABUF(("DMABufSurfaceRGBA::ReleaseTextures() UID %d\n", mUID
));
714 if (!mTexture
&& !mEGLImage
) {
720 MOZ_DIAGNOSTIC_ASSERT(mGL
, "Missing GL context!");
723 "DMABufSurfaceRGBA::ReleaseTextures(): Missing GL context! We're "
724 "leaking textures!");
729 const auto& gle
= gl::GLContextEGL::Cast(mGL
);
730 const auto& egl
= gle
->mEgl
;
732 if (mTexture
&& mGL
->MakeCurrent()) {
733 mGL
->fDeleteTextures(1, &mTexture
);
737 if (mEGLImage
!= LOCAL_EGL_NO_IMAGE
) {
738 egl
->fDestroyImage(mEGLImage
);
739 mEGLImage
= LOCAL_EGL_NO_IMAGE
;
744 void DMABufSurfaceRGBA::ReleaseSurface() {
745 MOZ_ASSERT(!IsMapped(), "We can't release mapped buffer!");
752 bool DMABufSurfaceRGBA::CreateWlBuffer() {
753 MutexAutoLock
lockFD(mSurfaceLock
);
754 if (!OpenFileDescriptors(lockFD
)) {
758 nsWaylandDisplay
* waylandDisplay
= widget::WaylandDisplayGet();
759 if (!waylandDisplay
->GetDmabuf()) {
760 CloseFileDescriptors(lockFD
);
764 struct zwp_linux_buffer_params_v1
* params
=
765 zwp_linux_dmabuf_v1_create_params(waylandDisplay
->GetDmabuf());
766 zwp_linux_buffer_params_v1_add(params
, mDmabufFds
[0], 0, mOffsets
[0],
767 mStrides
[0], mBufferModifiers
[0] >> 32,
768 mBufferModifiers
[0] & 0xffffffff);
770 mWlBuffer
= zwp_linux_buffer_params_v1_create_immed(
771 params
, GetWidth(), GetHeight(), mDrmFormats
[0], 0);
773 CloseFileDescriptors(lockFD
);
775 return mWlBuffer
!= nullptr;
778 void DMABufSurfaceRGBA::ReleaseWlBuffer() {
779 MozClearPointer(mWlBuffer
, wl_buffer_destroy
);
783 // We should synchronize DMA Buffer object access from CPU to avoid potential
784 // cache incoherency and data loss.
786 // https://01.org/linuxgraphics/gfx-docs/drm/driver-api/dma-buf.html#cpu-access-to-dma-buffer-objects
787 struct dma_buf_sync
{
790 #define DMA_BUF_SYNC_READ (1 << 0)
791 #define DMA_BUF_SYNC_WRITE (2 << 0)
792 #define DMA_BUF_SYNC_START (0 << 2)
793 #define DMA_BUF_SYNC_END (1 << 2)
794 #define DMA_BUF_BASE 'b'
795 #define DMA_BUF_IOCTL_SYNC _IOW(DMA_BUF_BASE, 0, struct dma_buf_sync)
797 static void SyncDmaBuf(int aFd
, uint64_t aFlags
) {
798 struct dma_buf_sync sync
= {0};
800 sync
.flags
= aFlags
| DMA_BUF_SYNC_READ
| DMA_BUF_SYNC_WRITE
;
803 ret
= ioctl(aFd
, DMA_BUF_IOCTL_SYNC
, &sync
);
804 if (ret
== -1 && errno
== EINTR
) {
806 } else if (ret
== -1) {
808 ("Failed to synchronize DMA buffer: %s FD %d", strerror(errno
), aFd
));
816 void* DMABufSurface::MapInternal(uint32_t aX
, uint32_t aY
, uint32_t aWidth
,
817 uint32_t aHeight
, uint32_t* aStride
,
818 int aGbmFlags
, int aPlane
) {
819 NS_ASSERTION(!IsMapped(aPlane
), "Already mapped!");
820 if (!mGbmBufferObject
[aPlane
]) {
821 NS_WARNING("We can't map DMABufSurfaceRGBA without mGbmBufferObject");
826 ("DMABufSurfaceRGBA::MapInternal() UID %d plane %d size %d x %d -> %d x "
828 mUID
, aPlane
, aX
, aY
, aWidth
, aHeight
));
830 mMappedRegionStride
[aPlane
] = 0;
831 mMappedRegionData
[aPlane
] = nullptr;
832 mMappedRegion
[aPlane
] =
833 GbmLib::Map(mGbmBufferObject
[aPlane
], aX
, aY
, aWidth
, aHeight
, aGbmFlags
,
834 &mMappedRegionStride
[aPlane
], &mMappedRegionData
[aPlane
]);
835 if (!mMappedRegion
[aPlane
]) {
836 LOGDMABUF((" Surface mapping failed: %s", strerror(errno
)));
840 *aStride
= mMappedRegionStride
[aPlane
];
843 MutexAutoLock
lockFD(mSurfaceLock
);
844 if (OpenFileDescriptorForPlane(lockFD
, aPlane
)) {
845 SyncDmaBuf(mDmabufFds
[aPlane
], DMA_BUF_SYNC_START
);
846 CloseFileDescriptorForPlane(lockFD
, aPlane
);
849 return mMappedRegion
[aPlane
];
852 void* DMABufSurfaceRGBA::MapReadOnly(uint32_t aX
, uint32_t aY
, uint32_t aWidth
,
853 uint32_t aHeight
, uint32_t* aStride
) {
854 return MapInternal(aX
, aY
, aWidth
, aHeight
, aStride
, GBM_BO_TRANSFER_READ
);
857 void* DMABufSurfaceRGBA::MapReadOnly(uint32_t* aStride
) {
858 return MapInternal(0, 0, mWidth
, mHeight
, aStride
, GBM_BO_TRANSFER_READ
);
861 void* DMABufSurfaceRGBA::Map(uint32_t aX
, uint32_t aY
, uint32_t aWidth
,
862 uint32_t aHeight
, uint32_t* aStride
) {
863 return MapInternal(aX
, aY
, aWidth
, aHeight
, aStride
,
864 GBM_BO_TRANSFER_READ_WRITE
);
867 void* DMABufSurfaceRGBA::Map(uint32_t* aStride
) {
868 return MapInternal(0, 0, mWidth
, mHeight
, aStride
,
869 GBM_BO_TRANSFER_READ_WRITE
);
872 void DMABufSurface::Unmap(int aPlane
) {
873 if (mMappedRegion
[aPlane
]) {
874 LOGDMABUF(("DMABufSurface::Unmap() UID %d plane %d\n", mUID
, aPlane
));
875 MutexAutoLock
lockFD(mSurfaceLock
);
876 if (OpenFileDescriptorForPlane(lockFD
, aPlane
)) {
877 SyncDmaBuf(mDmabufFds
[aPlane
], DMA_BUF_SYNC_END
);
878 CloseFileDescriptorForPlane(lockFD
, aPlane
);
880 GbmLib::Unmap(mGbmBufferObject
[aPlane
], mMappedRegionData
[aPlane
]);
881 mMappedRegion
[aPlane
] = nullptr;
882 mMappedRegionData
[aPlane
] = nullptr;
883 mMappedRegionStride
[aPlane
] = 0;
888 void DMABufSurfaceRGBA::DumpToFile(const char* pFile
) {
891 if (!MapReadOnly(&stride
)) {
894 cairo_surface_t
* surface
= nullptr;
896 auto unmap
= MakeScopeExit([&] {
898 cairo_surface_destroy(surface
);
903 surface
= cairo_image_surface_create_for_data(
904 (unsigned char*)mMappedRegion
[0], CAIRO_FORMAT_ARGB32
, mWidth
, mHeight
,
906 if (cairo_surface_status(surface
) == CAIRO_STATUS_SUCCESS
) {
907 cairo_surface_write_to_png(surface
, pFile
);
913 // Copy from source surface by GL
914 # include "GLBlitHelper.h"
916 bool DMABufSurfaceRGBA::CopyFrom(class DMABufSurface
* aSourceSurface
,
917 GLContext
* aGLContext
) {
918 MOZ_ASSERT(aSourceSurface
->GetTexture());
919 MOZ_ASSERT(GetTexture());
921 gfx::IntSize
size(GetWidth(), GetHeight());
922 aGLContext
->BlitHelper()->BlitTextureToTexture(aSourceSurface
->GetTexture(),
923 GetTexture(), size
, size
);
928 // TODO - Clear the surface by EGL
929 void DMABufSurfaceRGBA::Clear() {
931 void* destData
= Map(&destStride
);
932 memset(destData
, 0, GetHeight() * destStride
);
936 bool DMABufSurfaceRGBA::HasAlpha() {
937 return !mGmbFormat
|| mGmbFormat
->mHasAlpha
;
940 gfx::SurfaceFormat
DMABufSurfaceRGBA::GetFormat() {
941 return HasAlpha() ? gfx::SurfaceFormat::B8G8R8A8
942 : gfx::SurfaceFormat::B8G8R8X8
;
945 // GL uses swapped R and B components so report accordingly.
946 gfx::SurfaceFormat
DMABufSurfaceRGBA::GetFormatGL() {
947 return HasAlpha() ? gfx::SurfaceFormat::R8G8B8A8
948 : gfx::SurfaceFormat::R8G8B8X8
;
951 already_AddRefed
<DMABufSurfaceRGBA
> DMABufSurfaceRGBA::CreateDMABufSurface(
952 int aWidth
, int aHeight
, int aDMABufSurfaceFlags
) {
953 RefPtr
<DMABufSurfaceRGBA
> surf
= new DMABufSurfaceRGBA();
954 if (!surf
->Create(aWidth
, aHeight
, aDMABufSurfaceFlags
)) {
957 return surf
.forget();
960 already_AddRefed
<DMABufSurface
> DMABufSurfaceRGBA::CreateDMABufSurface(
961 mozilla::gl::GLContext
* aGLContext
, const EGLImageKHR aEGLImage
, int aWidth
,
963 RefPtr
<DMABufSurfaceRGBA
> surf
= new DMABufSurfaceRGBA();
964 if (!surf
->Create(aGLContext
, aEGLImage
, aWidth
, aHeight
)) {
967 return surf
.forget();
970 already_AddRefed
<DMABufSurfaceYUV
> DMABufSurfaceYUV::CreateYUVSurface(
971 const VADRMPRIMESurfaceDescriptor
& aDesc
, int aWidth
, int aHeight
) {
972 RefPtr
<DMABufSurfaceYUV
> surf
= new DMABufSurfaceYUV();
973 LOGDMABUF(("DMABufSurfaceYUV::CreateYUVSurface() UID %d from desc\n",
975 if (!surf
->UpdateYUVData(aDesc
, aWidth
, aHeight
, /* aCopy */ false)) {
978 return surf
.forget();
981 already_AddRefed
<DMABufSurfaceYUV
> DMABufSurfaceYUV::CopyYUVSurface(
982 const VADRMPRIMESurfaceDescriptor
& aDesc
, int aWidth
, int aHeight
) {
983 RefPtr
<DMABufSurfaceYUV
> surf
= new DMABufSurfaceYUV();
984 LOGDMABUF(("DMABufSurfaceYUV::CreateYUVSurfaceCopy() UID %d from desc\n",
986 if (!surf
->UpdateYUVData(aDesc
, aWidth
, aHeight
, /* aCopy */ true)) {
989 return surf
.forget();
992 already_AddRefed
<DMABufSurfaceYUV
> DMABufSurfaceYUV::CreateYUVSurface(
993 int aWidth
, int aHeight
, void** aPixelData
, int* aLineSizes
) {
994 RefPtr
<DMABufSurfaceYUV
> surf
= new DMABufSurfaceYUV();
995 LOGDMABUF(("DMABufSurfaceYUV::CreateYUVSurface() UID %d %d x %d\n",
996 surf
->GetUID(), aWidth
, aHeight
));
997 if (!surf
->Create(aWidth
, aHeight
, aPixelData
, aLineSizes
)) {
1000 return surf
.forget();
1003 DMABufSurfaceYUV::DMABufSurfaceYUV()
1004 : DMABufSurface(SURFACE_NV12
),
1010 for (int i
= 0; i
< DMABUF_BUFFER_PLANES
; i
++) {
1011 mEGLImage
[i
] = LOCAL_EGL_NO_IMAGE
;
1015 DMABufSurfaceYUV::~DMABufSurfaceYUV() { ReleaseSurface(); }
1017 bool DMABufSurfaceYUV::OpenFileDescriptorForPlane(
1018 const MutexAutoLock
& aProofOfLock
, int aPlane
) {
1019 // The fd is already opened, no need to reopen.
1020 // This can happen when we import dmabuf surface from VA-API decoder,
1021 // mGbmBufferObject is null and we don't close
1022 // file descriptors for surface as they are our only reference to it.
1023 if (mDmabufFds
[aPlane
] >= 0) {
1027 if (mGbmBufferObject
[aPlane
] == nullptr) {
1029 ("DMABufSurfaceYUV::OpenFileDescriptorForPlane: Missing "
1030 "mGbmBufferObject object!"));
1034 mDmabufFds
[aPlane
] = GbmLib::GetFd(mGbmBufferObject
[aPlane
]);
1035 if (mDmabufFds
[aPlane
] < 0) {
1036 CloseFileDescriptors(aProofOfLock
);
1042 void DMABufSurfaceYUV::CloseFileDescriptorForPlane(
1043 const MutexAutoLock
& aProofOfLock
, int aPlane
, bool aForceClose
= false) {
1044 if ((aForceClose
|| mGbmBufferObject
[aPlane
]) && mDmabufFds
[aPlane
] >= 0) {
1045 close(mDmabufFds
[aPlane
]);
1046 mDmabufFds
[aPlane
] = -1;
1050 bool DMABufSurfaceYUV::ImportPRIMESurfaceDescriptor(
1051 const VADRMPRIMESurfaceDescriptor
& aDesc
, int aWidth
, int aHeight
) {
1052 LOGDMABUF(("DMABufSurfaceYUV::ImportPRIMESurfaceDescriptor() UID %d", mUID
));
1054 MOZ_DIAGNOSTIC_ASSERT(mDmabufFds
[0] < 0);
1056 if (aDesc
.num_layers
> DMABUF_BUFFER_PLANES
||
1057 aDesc
.num_objects
> DMABUF_BUFFER_PLANES
) {
1058 LOGDMABUF((" Can't import, wrong layers/objects number (%d, %d)",
1059 aDesc
.num_layers
, aDesc
.num_objects
));
1062 if (aDesc
.fourcc
== VA_FOURCC_NV12
) {
1063 mSurfaceType
= SURFACE_NV12
;
1064 } else if (aDesc
.fourcc
== VA_FOURCC_P010
) {
1065 mSurfaceType
= SURFACE_NV12
;
1066 } else if (aDesc
.fourcc
== VA_FOURCC_YV12
) {
1067 mSurfaceType
= SURFACE_YUV420
;
1069 LOGDMABUF((" Can't import surface data of 0x%x format", aDesc
.fourcc
));
1073 mBufferPlaneCount
= aDesc
.num_layers
;
1075 for (unsigned int i
= 0; i
< aDesc
.num_layers
; i
++) {
1076 // All supported formats have 4:2:0 chroma sub-sampling.
1077 unsigned int subsample
= i
== 0 ? 0 : 1;
1079 unsigned int object
= aDesc
.layers
[i
].object_index
[0];
1080 mBufferModifiers
[i
] = aDesc
.objects
[object
].drm_format_modifier
;
1081 mDrmFormats
[i
] = aDesc
.layers
[i
].drm_format
;
1082 mOffsets
[i
] = aDesc
.layers
[i
].offset
[0];
1083 mStrides
[i
] = aDesc
.layers
[i
].pitch
[0];
1084 mWidthAligned
[i
] = aDesc
.width
>> subsample
;
1085 mHeightAligned
[i
] = aDesc
.height
>> subsample
;
1086 mWidth
[i
] = aWidth
>> subsample
;
1087 mHeight
[i
] = aHeight
>> subsample
;
1088 LOGDMABUF((" plane %d size %d x %d format %x", i
, mWidth
[i
], mHeight
[i
],
1094 bool DMABufSurfaceYUV::MoveYUVDataImpl(const VADRMPRIMESurfaceDescriptor
& aDesc
,
1095 int aWidth
, int aHeight
) {
1096 if (!ImportPRIMESurfaceDescriptor(aDesc
, aWidth
, aHeight
)) {
1099 for (unsigned int i
= 0; i
< aDesc
.num_layers
; i
++) {
1100 unsigned int object
= aDesc
.layers
[i
].object_index
[0];
1101 // Keep VADRMPRIMESurfaceDescriptor untouched and dup() dmabuf
1102 // file descriptors.
1103 mDmabufFds
[i
] = dup(aDesc
.objects
[object
].fd
);
1108 void DMABufSurfaceYUV::ReleaseVADRMPRIMESurfaceDescriptor(
1109 VADRMPRIMESurfaceDescriptor
& aDesc
) {
1110 for (unsigned int i
= 0; i
< aDesc
.num_layers
; i
++) {
1111 unsigned int object
= aDesc
.layers
[i
].object_index
[0];
1112 if (aDesc
.objects
[object
].fd
!= -1) {
1113 close(aDesc
.objects
[object
].fd
);
1114 aDesc
.objects
[object
].fd
= -1;
1119 bool DMABufSurfaceYUV::CreateYUVPlane(int aPlane
) {
1120 LOGDMABUF(("DMABufSurfaceYUV::CreateYUVPlane() UID %d size %d x %d", mUID
,
1121 mWidth
[aPlane
], mHeight
[aPlane
]));
1123 if (!GetDMABufDevice()->GetGbmDevice()) {
1124 LOGDMABUF((" Missing GbmDevice!"));
1128 MOZ_DIAGNOSTIC_ASSERT(mGbmBufferObject
[aPlane
] == nullptr);
1129 bool useModifiers
= (mBufferModifiers
[aPlane
] != DRM_FORMAT_MOD_INVALID
);
1131 LOGDMABUF((" Creating with modifiers"));
1132 mGbmBufferObject
[aPlane
] = GbmLib::CreateWithModifiers(
1133 GetDMABufDevice()->GetGbmDevice(), mWidth
[aPlane
], mHeight
[aPlane
],
1134 mDrmFormats
[aPlane
], mBufferModifiers
+ aPlane
, 1);
1136 if (!mGbmBufferObject
[aPlane
]) {
1137 LOGDMABUF((" Creating without modifiers"));
1138 mGbmBufferObject
[aPlane
] = GbmLib::Create(
1139 GetDMABufDevice()->GetGbmDevice(), mWidth
[aPlane
], mHeight
[aPlane
],
1140 mDrmFormats
[aPlane
], GBM_BO_USE_RENDERING
);
1141 mBufferModifiers
[aPlane
] = DRM_FORMAT_MOD_INVALID
;
1143 if (!mGbmBufferObject
[aPlane
]) {
1144 LOGDMABUF((" Failed to create GbmBufferObject: %s", strerror(errno
)));
1148 mStrides
[aPlane
] = GbmLib::GetStride(mGbmBufferObject
[aPlane
]);
1149 mOffsets
[aPlane
] = GbmLib::GetOffset(mGbmBufferObject
[aPlane
], 0);
1150 mWidthAligned
[aPlane
] = mWidth
[aPlane
];
1151 mHeightAligned
[aPlane
] = mHeight
[aPlane
];
1155 bool DMABufSurfaceYUV::CopyYUVDataImpl(const VADRMPRIMESurfaceDescriptor
& aDesc
,
1156 int aWidth
, int aHeight
) {
1157 RefPtr
<DMABufSurfaceYUV
> tmpSurf
= CreateYUVSurface(aDesc
, aWidth
, aHeight
);
1162 if (!ImportPRIMESurfaceDescriptor(aDesc
, aWidth
, aHeight
)) {
1166 StaticMutexAutoLock
lock(sSnapshotContextMutex
);
1167 RefPtr
<GLContext
> context
= ClaimSnapshotGLContext();
1168 auto releaseTextures
= MakeScopeExit([&] {
1169 tmpSurf
->ReleaseTextures();
1171 ReturnSnapshotGLContext(context
);
1174 for (int i
= 0; i
< mBufferPlaneCount
; i
++) {
1175 if (!tmpSurf
->CreateTexture(context
, i
)) {
1178 if (!CreateYUVPlane(i
) || !CreateTexture(context
, i
)) {
1181 gfx::IntSize
size(GetWidth(i
), GetHeight(i
));
1182 context
->BlitHelper()->BlitTextureToTexture(
1183 tmpSurf
->GetTexture(i
), GetTexture(i
), size
, size
, LOCAL_GL_TEXTURE_2D
,
1184 LOCAL_GL_TEXTURE_2D
);
1189 bool DMABufSurfaceYUV::UpdateYUVData(const VADRMPRIMESurfaceDescriptor
& aDesc
,
1190 int aWidth
, int aHeight
, bool aCopy
) {
1191 LOGDMABUF(("DMABufSurfaceYUV::UpdateYUVData() UID %d copy %d", mUID
, aCopy
));
1192 return aCopy
? CopyYUVDataImpl(aDesc
, aWidth
, aHeight
)
1193 : MoveYUVDataImpl(aDesc
, aWidth
, aHeight
);
1196 bool DMABufSurfaceYUV::CreateLinearYUVPlane(int aPlane
, int aWidth
, int aHeight
,
1198 LOGDMABUF(("DMABufSurfaceYUV::CreateLinearYUVPlane() UID %d size %d x %d",
1199 mUID
, aWidth
, aHeight
));
1201 if (!GetDMABufDevice()->GetGbmDevice()) {
1202 LOGDMABUF((" Missing GbmDevice!"));
1206 mWidth
[aPlane
] = aWidth
;
1207 mHeight
[aPlane
] = aHeight
;
1208 mDrmFormats
[aPlane
] = aDrmFormat
;
1210 mGbmBufferObject
[aPlane
] =
1211 GbmLib::Create(GetDMABufDevice()->GetGbmDevice(), aWidth
, aHeight
,
1212 aDrmFormat
, GBM_BO_USE_LINEAR
);
1213 if (!mGbmBufferObject
[aPlane
]) {
1214 LOGDMABUF((" Failed to create GbmBufferObject: %s", strerror(errno
)));
1218 mStrides
[aPlane
] = GbmLib::GetStride(mGbmBufferObject
[aPlane
]);
1219 mDmabufFds
[aPlane
] = -1;
1224 void DMABufSurfaceYUV::UpdateYUVPlane(int aPlane
, void* aPixelData
,
1227 ("DMABufSurfaceYUV::UpdateYUVPlane() UID %d plane %d", mUID
, aPlane
));
1228 if (aLineSize
== mWidth
[aPlane
] &&
1229 (int)mMappedRegionStride
[aPlane
] == mWidth
[aPlane
]) {
1230 memcpy(mMappedRegion
[aPlane
], aPixelData
, aLineSize
* mHeight
[aPlane
]);
1232 char* src
= (char*)aPixelData
;
1233 char* dest
= (char*)mMappedRegion
[aPlane
];
1234 for (int i
= 0; i
< mHeight
[aPlane
]; i
++) {
1235 memcpy(dest
, src
, mWidth
[aPlane
]);
1237 dest
+= mMappedRegionStride
[aPlane
];
1242 bool DMABufSurfaceYUV::UpdateYUVData(void** aPixelData
, int* aLineSizes
) {
1243 LOGDMABUF(("DMABufSurfaceYUV::UpdateYUVData() UID %d", mUID
));
1244 if (mSurfaceType
!= SURFACE_YUV420
) {
1245 LOGDMABUF((" UpdateYUVData can upload YUV420 surface type only!"));
1249 if (mBufferPlaneCount
!= 3) {
1250 LOGDMABUF((" DMABufSurfaceYUV planes does not match!"));
1254 auto unmapBuffers
= MakeScopeExit([&] {
1261 for (int i
= 0; i
< mBufferPlaneCount
; i
++) {
1262 MapInternal(0, 0, mWidth
[i
], mHeight
[i
], nullptr, GBM_BO_TRANSFER_WRITE
, i
);
1263 if (!mMappedRegion
[i
]) {
1264 LOGDMABUF((" DMABufSurfaceYUV plane can't be mapped!"));
1267 if ((int)mMappedRegionStride
[i
] < mWidth
[i
]) {
1268 LOGDMABUF((" DMABufSurfaceYUV plane size stride does not match!"));
1274 for (int i
= 0; i
< mBufferPlaneCount
; i
++) {
1275 UpdateYUVPlane(i
, aPixelData
[i
], aLineSizes
[i
]);
1281 bool DMABufSurfaceYUV::Create(int aWidth
, int aHeight
, void** aPixelData
,
1283 LOGDMABUF(("DMABufSurfaceYUV::Create() UID %d size %d x %d", mUID
, aWidth
,
1286 mSurfaceType
= SURFACE_YUV420
;
1287 mBufferPlaneCount
= 3;
1289 if (!CreateLinearYUVPlane(0, aWidth
, aHeight
, GBM_FORMAT_R8
)) {
1292 if (!CreateLinearYUVPlane(1, aWidth
>> 1, aHeight
>> 1, GBM_FORMAT_R8
)) {
1295 if (!CreateLinearYUVPlane(2, aWidth
>> 1, aHeight
>> 1, GBM_FORMAT_R8
)) {
1298 if (!aPixelData
|| !aLineSizes
) {
1301 return UpdateYUVData(aPixelData
, aLineSizes
);
1304 bool DMABufSurfaceYUV::Create(const SurfaceDescriptor
& aDesc
) {
1305 return ImportSurfaceDescriptor(aDesc
);
1308 bool DMABufSurfaceYUV::ImportSurfaceDescriptor(
1309 const SurfaceDescriptorDMABuf
& aDesc
) {
1310 mBufferPlaneCount
= aDesc
.fds().Length();
1311 mSurfaceType
= (mBufferPlaneCount
== 2) ? SURFACE_NV12
: SURFACE_YUV420
;
1312 mColorSpace
= aDesc
.yUVColorSpace();
1313 mColorRange
= aDesc
.colorRange();
1316 LOGDMABUF(("DMABufSurfaceYUV::ImportSurfaceDescriptor() UID %d", mUID
));
1318 MOZ_RELEASE_ASSERT(mBufferPlaneCount
<= DMABUF_BUFFER_PLANES
);
1319 for (int i
= 0; i
< mBufferPlaneCount
; i
++) {
1320 mDmabufFds
[i
] = aDesc
.fds()[i
].ClonePlatformHandle().release();
1321 if (mDmabufFds
[i
] < 0) {
1322 LOGDMABUF((" failed to get DMABuf plane file descriptor: %s",
1326 mWidth
[i
] = aDesc
.width()[i
];
1327 mHeight
[i
] = aDesc
.height()[i
];
1328 mWidthAligned
[i
] = aDesc
.widthAligned()[i
];
1329 mHeightAligned
[i
] = aDesc
.heightAligned()[i
];
1330 mDrmFormats
[i
] = aDesc
.format()[i
];
1331 mStrides
[i
] = aDesc
.strides()[i
];
1332 mOffsets
[i
] = aDesc
.offsets()[i
];
1333 mBufferModifiers
[i
] = aDesc
.modifier()[i
];
1334 LOGDMABUF((" plane %d fd %d size %d x %d format %x", i
, mDmabufFds
[i
],
1335 mWidth
[i
], mHeight
[i
], mDrmFormats
[i
]));
1338 if (aDesc
.fence().Length() > 0) {
1339 mSyncFd
= aDesc
.fence()[0].ClonePlatformHandle().release();
1342 (" failed to get GL fence file descriptor: %s", strerror(errno
)));
1347 if (aDesc
.refCount().Length() > 0) {
1348 GlobalRefCountImport(aDesc
.refCount()[0].ClonePlatformHandle().release());
1354 bool DMABufSurfaceYUV::Serialize(
1355 mozilla::layers::SurfaceDescriptor
& aOutDescriptor
) {
1356 AutoTArray
<uint32_t, DMABUF_BUFFER_PLANES
> width
;
1357 AutoTArray
<uint32_t, DMABUF_BUFFER_PLANES
> height
;
1358 AutoTArray
<uint32_t, DMABUF_BUFFER_PLANES
> widthBytes
;
1359 AutoTArray
<uint32_t, DMABUF_BUFFER_PLANES
> heightBytes
;
1360 AutoTArray
<uint32_t, DMABUF_BUFFER_PLANES
> format
;
1361 AutoTArray
<ipc::FileDescriptor
, DMABUF_BUFFER_PLANES
> fds
;
1362 AutoTArray
<uint32_t, DMABUF_BUFFER_PLANES
> strides
;
1363 AutoTArray
<uint32_t, DMABUF_BUFFER_PLANES
> offsets
;
1364 AutoTArray
<uint64_t, DMABUF_BUFFER_PLANES
> modifiers
;
1365 AutoTArray
<ipc::FileDescriptor
, 1> fenceFDs
;
1366 AutoTArray
<ipc::FileDescriptor
, 1> refCountFDs
;
1368 LOGDMABUF(("DMABufSurfaceYUV::Serialize() UID %d", mUID
));
1370 MutexAutoLock
lockFD(mSurfaceLock
);
1371 if (!OpenFileDescriptors(lockFD
)) {
1375 for (int i
= 0; i
< mBufferPlaneCount
; i
++) {
1376 width
.AppendElement(mWidth
[i
]);
1377 height
.AppendElement(mHeight
[i
]);
1378 widthBytes
.AppendElement(mWidthAligned
[i
]);
1379 heightBytes
.AppendElement(mHeightAligned
[i
]);
1380 format
.AppendElement(mDrmFormats
[i
]);
1381 fds
.AppendElement(ipc::FileDescriptor(mDmabufFds
[i
]));
1382 strides
.AppendElement(mStrides
[i
]);
1383 offsets
.AppendElement(mOffsets
[i
]);
1384 modifiers
.AppendElement(mBufferModifiers
[i
]);
1387 CloseFileDescriptors(lockFD
);
1390 fenceFDs
.AppendElement(ipc::FileDescriptor(mSyncFd
));
1393 if (mGlobalRefCountFd
) {
1394 refCountFDs
.AppendElement(ipc::FileDescriptor(GlobalRefCountExport()));
1397 aOutDescriptor
= SurfaceDescriptorDMABuf(
1398 mSurfaceType
, modifiers
, 0, fds
, width
, height
, widthBytes
, heightBytes
,
1399 format
, strides
, offsets
, GetYUVColorSpace(), mColorRange
, fenceFDs
, mUID
,
1404 bool DMABufSurfaceYUV::CreateEGLImage(GLContext
* aGLContext
, int aPlane
) {
1406 ("DMABufSurfaceYUV::CreateEGLImage() UID %d plane %d", mUID
, aPlane
));
1407 MOZ_ASSERT(mEGLImage
[aPlane
] == LOCAL_EGL_NO_IMAGE
,
1408 "EGLImage is already created!");
1409 MOZ_ASSERT(aGLContext
, "Missing GLContext!");
1411 const auto& gle
= gl::GLContextEGL::Cast(aGLContext
);
1412 const auto& egl
= gle
->mEgl
;
1414 MutexAutoLock
lockFD(mSurfaceLock
);
1415 if (!OpenFileDescriptorForPlane(lockFD
, aPlane
)) {
1416 LOGDMABUF((" failed to open dmabuf file descriptors"));
1420 nsTArray
<EGLint
> attribs
;
1421 attribs
.AppendElement(LOCAL_EGL_WIDTH
);
1422 attribs
.AppendElement(mWidthAligned
[aPlane
]);
1423 attribs
.AppendElement(LOCAL_EGL_HEIGHT
);
1424 attribs
.AppendElement(mHeightAligned
[aPlane
]);
1425 attribs
.AppendElement(LOCAL_EGL_LINUX_DRM_FOURCC_EXT
);
1426 attribs
.AppendElement(mDrmFormats
[aPlane
]);
1427 #define ADD_PLANE_ATTRIBS_NV12(plane_idx) \
1428 attribs.AppendElement(LOCAL_EGL_DMA_BUF_PLANE##plane_idx##_FD_EXT); \
1429 attribs.AppendElement(mDmabufFds[aPlane]); \
1430 attribs.AppendElement(LOCAL_EGL_DMA_BUF_PLANE##plane_idx##_OFFSET_EXT); \
1431 attribs.AppendElement((int)mOffsets[aPlane]); \
1432 attribs.AppendElement(LOCAL_EGL_DMA_BUF_PLANE##plane_idx##_PITCH_EXT); \
1433 attribs.AppendElement((int)mStrides[aPlane]); \
1434 if (mBufferModifiers[aPlane] != DRM_FORMAT_MOD_INVALID) { \
1435 attribs.AppendElement( \
1436 LOCAL_EGL_DMA_BUF_PLANE##plane_idx##_MODIFIER_LO_EXT); \
1437 attribs.AppendElement(mBufferModifiers[aPlane] & 0xFFFFFFFF); \
1438 attribs.AppendElement( \
1439 LOCAL_EGL_DMA_BUF_PLANE##plane_idx##_MODIFIER_HI_EXT); \
1440 attribs.AppendElement(mBufferModifiers[aPlane] >> 32); \
1442 ADD_PLANE_ATTRIBS_NV12(0);
1443 #undef ADD_PLANE_ATTRIBS_NV12
1444 attribs
.AppendElement(LOCAL_EGL_NONE
);
1447 egl
->fCreateImage(LOCAL_EGL_NO_CONTEXT
, LOCAL_EGL_LINUX_DMA_BUF_EXT
,
1448 nullptr, attribs
.Elements());
1450 CloseFileDescriptorForPlane(lockFD
, aPlane
);
1452 if (mEGLImage
[aPlane
] == LOCAL_EGL_NO_IMAGE
) {
1453 LOGDMABUF((" EGLImageKHR creation failed"));
1457 LOGDMABUF((" Success."));
1461 void DMABufSurfaceYUV::ReleaseEGLImages(GLContext
* aGLContext
) {
1462 LOGDMABUF(("DMABufSurfaceYUV::ReleaseEGLImages() UID %d", mUID
));
1463 MOZ_ASSERT(aGLContext
, "Missing GLContext!");
1465 const auto& gle
= gl::GLContextEGL::Cast(aGLContext
);
1466 const auto& egl
= gle
->mEgl
;
1468 for (int i
= 0; i
< mBufferPlaneCount
; i
++) {
1469 if (mEGLImage
[i
] != LOCAL_EGL_NO_IMAGE
) {
1470 egl
->fDestroyImage(mEGLImage
[i
]);
1471 mEGLImage
[i
] = LOCAL_EGL_NO_IMAGE
;
1476 bool DMABufSurfaceYUV::CreateTexture(GLContext
* aGLContext
, int aPlane
) {
1478 ("DMABufSurfaceYUV::CreateTexture() UID %d plane %d", mUID
, aPlane
));
1479 MOZ_ASSERT(!mTexture
[aPlane
], "Texture is already created!");
1480 MOZ_ASSERT(aGLContext
, "Missing GLContext!");
1482 if (!CreateEGLImage(aGLContext
, aPlane
)) {
1485 if (!aGLContext
->MakeCurrent()) {
1486 LOGDMABUF((" Failed to make GL context current."));
1489 aGLContext
->fGenTextures(1, &mTexture
[aPlane
]);
1490 const ScopedBindTexture
savedTex(aGLContext
, mTexture
[aPlane
]);
1491 aGLContext
->fTexParameteri(LOCAL_GL_TEXTURE_2D
, LOCAL_GL_TEXTURE_WRAP_S
,
1492 LOCAL_GL_CLAMP_TO_EDGE
);
1493 aGLContext
->fTexParameteri(LOCAL_GL_TEXTURE_2D
, LOCAL_GL_TEXTURE_WRAP_T
,
1494 LOCAL_GL_CLAMP_TO_EDGE
);
1495 aGLContext
->fTexParameteri(LOCAL_GL_TEXTURE_2D
, LOCAL_GL_TEXTURE_MAG_FILTER
,
1497 aGLContext
->fTexParameteri(LOCAL_GL_TEXTURE_2D
, LOCAL_GL_TEXTURE_MIN_FILTER
,
1499 aGLContext
->fEGLImageTargetTexture2D(LOCAL_GL_TEXTURE_2D
, mEGLImage
[aPlane
]);
1504 void DMABufSurfaceYUV::ReleaseTextures() {
1505 LOGDMABUF(("DMABufSurfaceYUV::ReleaseTextures() UID %d", mUID
));
1509 bool textureActive
= false;
1510 for (int i
= 0; i
< mBufferPlaneCount
; i
++) {
1511 if (mTexture
[i
] || mEGLImage
[i
]) {
1512 textureActive
= true;
1517 if (!textureActive
) {
1522 #ifdef NIGHTLY_BUILD
1523 MOZ_DIAGNOSTIC_ASSERT(mGL
, "Missing GL context!");
1526 "DMABufSurfaceYUV::ReleaseTextures(): Missing GL context! We're "
1527 "leaking textures!");
1532 if (!mGL
->MakeCurrent()) {
1534 "DMABufSurfaceYUV::ReleaseTextures(): MakeCurrent failed. We're "
1535 "leaking textures!");
1539 mGL
->fDeleteTextures(DMABUF_BUFFER_PLANES
, mTexture
);
1540 for (int i
= 0; i
< DMABUF_BUFFER_PLANES
; i
++) {
1543 ReleaseEGLImages(mGL
);
1547 bool DMABufSurfaceYUV::VerifyTextureCreation() {
1548 LOGDMABUF(("DMABufSurfaceYUV::VerifyTextureCreation() UID %d", mUID
));
1550 StaticMutexAutoLock
lock(sSnapshotContextMutex
);
1551 RefPtr
<GLContext
> context
= ClaimSnapshotGLContext();
1552 auto release
= MakeScopeExit([&] {
1553 ReleaseEGLImages(context
);
1554 ReturnSnapshotGLContext(context
);
1557 for (int i
= 0; i
< mBufferPlaneCount
; i
++) {
1558 if (!CreateEGLImage(context
, i
)) {
1559 LOGDMABUF((" failed to create EGL image!"));
1564 LOGDMABUF((" success"));
1568 gfx::SurfaceFormat
DMABufSurfaceYUV::GetFormat() {
1569 switch (mSurfaceType
) {
1571 return gfx::SurfaceFormat::NV12
;
1572 case SURFACE_YUV420
:
1573 return gfx::SurfaceFormat::YUV
;
1575 NS_WARNING("DMABufSurfaceYUV::GetFormat(): Wrong surface format!");
1576 return gfx::SurfaceFormat::UNKNOWN
;
1580 // GL uses swapped R and B components so report accordingly.
1581 gfx::SurfaceFormat
DMABufSurfaceYUV::GetFormatGL() { return GetFormat(); }
1583 int DMABufSurfaceYUV::GetTextureCount() {
1584 switch (mSurfaceType
) {
1587 case SURFACE_YUV420
:
1590 NS_WARNING("DMABufSurfaceYUV::GetTextureCount(): Wrong surface format!");
1595 void DMABufSurfaceYUV::ReleaseSurface() {
1596 LOGDMABUF(("DMABufSurfaceYUV::ReleaseSurface() UID %d", mUID
));
1601 already_AddRefed
<gfx::DataSourceSurface
>
1602 DMABufSurfaceYUV::GetAsSourceSurface() {
1603 LOGDMABUF(("DMABufSurfaceYUV::GetAsSourceSurface UID %d", mUID
));
1605 StaticMutexAutoLock
lock(sSnapshotContextMutex
);
1606 RefPtr
<GLContext
> context
= ClaimSnapshotGLContext();
1607 auto releaseTextures
= mozilla::MakeScopeExit([&] {
1609 ReturnSnapshotGLContext(context
);
1612 for (int i
= 0; i
< GetTextureCount(); i
++) {
1613 if (!GetTexture(i
) && !CreateTexture(context
, i
)) {
1614 LOGDMABUF(("GetAsSourceSurface: Failed to create DMABuf textures."));
1619 ScopedTexture
scopedTex(context
);
1620 ScopedBindTexture
boundTex(context
, scopedTex
.Texture());
1622 gfx::IntSize
size(GetWidth(), GetHeight());
1623 context
->fTexImage2D(LOCAL_GL_TEXTURE_2D
, 0, LOCAL_GL_RGBA
, size
.width
,
1624 size
.height
, 0, LOCAL_GL_RGBA
, LOCAL_GL_UNSIGNED_BYTE
,
1627 ScopedFramebufferForTexture
autoFBForTex(context
, scopedTex
.Texture());
1628 if (!autoFBForTex
.IsComplete()) {
1629 LOGDMABUF(("GetAsSourceSurface: ScopedFramebufferForTexture failed."));
1633 const gl::OriginPos destOrigin
= gl::OriginPos::BottomLeft
;
1635 const ScopedBindFramebuffer
bindFB(context
, autoFBForTex
.FB());
1636 if (!context
->BlitHelper()->Blit(this, size
, destOrigin
)) {
1637 LOGDMABUF(("GetAsSourceSurface: Blit failed."));
1642 RefPtr
<gfx::DataSourceSurface
> source
=
1643 gfx::Factory::CreateDataSourceSurface(size
, gfx::SurfaceFormat::B8G8R8A8
);
1644 if (NS_WARN_IF(!source
)) {
1645 LOGDMABUF(("GetAsSourceSurface: CreateDataSourceSurface failed."));
1649 ScopedBindFramebuffer
bind(context
, autoFBForTex
.FB());
1650 ReadPixelsIntoDataSurface(context
, source
);
1652 return source
.forget();
1656 void DMABufSurfaceYUV::ClearPlane(int aPlane
) {
1657 if (!MapInternal(0, 0, mWidth
[aPlane
], mHeight
[aPlane
], nullptr,
1658 GBM_BO_TRANSFER_WRITE
, aPlane
)) {
1661 if ((int)mMappedRegionStride
[aPlane
] < mWidth
[aPlane
]) {
1664 memset((char*)mMappedRegion
[aPlane
], 0,
1665 mMappedRegionStride
[aPlane
] * mHeight
[aPlane
]);
1669 # include "gfxUtils.h"
1671 void DMABufSurfaceYUV::DumpToFile(const char* aFile
) {
1672 RefPtr
<gfx::DataSourceSurface
> surf
= GetAsSourceSurface();
1673 gfxUtils::WriteAsPNG(surf
, aFile
);