no bug - Bumping Firefox l10n changesets r=release a=l10n-bump DONTBUILD CLOSED TREE
[gecko.git] / widget / gtk / DMABufSurface.cpp
blob04faee4016b9a945b56d0ac95597750c0bc5cab6
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"
10 #ifdef MOZ_WAYLAND
11 # include "nsWaylandDisplay.h"
12 #endif
14 #include <fcntl.h>
15 #include <getopt.h>
16 #include <signal.h>
17 #include <stdbool.h>
18 #include <stdint.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <unistd.h>
23 #include <sys/time.h>
24 #include <dlfcn.h>
25 #include <sys/mman.h>
26 #ifdef HAVE_EVENTFD
27 # include <sys/eventfd.h>
28 #endif
29 #include <poll.h>
30 #ifdef HAVE_SYSIOCCOM_H
31 # include <sys/ioccom.h>
32 #endif
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"
51 TODO:
52 DRM device selection:
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))
59 #else
60 # define EGL_CAST(type, value) ((type)(value))
61 #endif
63 using namespace mozilla;
64 using namespace mozilla::widget;
65 using namespace mozilla::gl;
66 using namespace mozilla::layers;
67 using namespace mozilla::gfx;
69 #ifdef MOZ_LOGGING
70 # include "mozilla/Logging.h"
71 # include "nsTArray.h"
72 # include "Units.h"
73 static LazyLogModule gDmabufRefLog("DmabufRef");
74 # define LOGDMABUFREF(args) \
75 MOZ_LOG(gDmabufRefLog, mozilla::LogLevel::Debug, args)
76 #else
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) {
91 LOGDMABUF(
92 ("ClaimSnapshotGLContext: Failed to create snapshot GLContext."));
93 return nullptr;
95 sSnapshotContext->mOwningThreadId = Nothing(); // No singular owner.
97 if (!sSnapshotContext->MakeCurrent()) {
98 LOGDMABUF(("ClaimSnapshotGLContext: Failed to make GLContext current."));
99 return nullptr;
101 return sSnapshotContext;
104 void ReturnSnapshotGLContext(RefPtr<GLContext> aGLContext) {
105 // direct eglMakeCurrent() call breaks current context caching so make sure
106 // it's not used.
107 MOZ_ASSERT(!aGLContext->mUseTLSIsCurrent);
108 if (!aGLContext->IsCurrent()) {
109 LOGDMABUF(("ReturnSnapshotGLContext() failed, is not current!"));
110 return;
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) {
119 return false;
121 struct pollfd pfd;
122 pfd.fd = mGlobalRefCountFd;
123 pfd.events = POLLIN;
124 return poll(&pfd, 1, 0) == 1;
127 void DMABufSurface::GlobalRefRelease() {
128 #ifdef HAVE_EVENTFD
129 if (!mGlobalRefCountFd) {
130 return;
132 LOGDMABUFREF(("DMABufSurface::GlobalRefRelease UID %d", mUID));
133 uint64_t counter;
134 if (read(mGlobalRefCountFd, &counter, sizeof(counter)) != sizeof(counter)) {
135 if (errno == EAGAIN) {
136 LOGDMABUFREF(
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",
143 strerror(errno))
144 .get());
147 #endif
150 void DMABufSurface::GlobalRefAdd() {
151 #ifdef HAVE_EVENTFD
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",
157 strerror(errno))
158 .get());
160 #endif
163 void DMABufSurface::GlobalRefCountCreate() {
164 #ifdef HAVE_EVENTFD
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",
172 strerror(errno))
173 .get());
174 mGlobalRefCountFd = 0;
175 return;
177 #endif
180 void DMABufSurface::GlobalRefCountImport(int aFd) {
181 #ifdef HAVE_EVENTFD
182 mGlobalRefCountFd = aFd;
183 if (mGlobalRefCountFd) {
184 LOGDMABUFREF(("DMABufSurface::GlobalRefCountImport UID %d", mUID));
185 GlobalRefAdd();
187 #endif
190 int DMABufSurface::GlobalRefCountExport() {
191 #ifdef MOZ_LOGGING
192 if (mGlobalRefCountFd) {
193 LOGDMABUFREF(("DMABufSurface::GlobalRefCountExport UID %d", mUID));
195 #endif
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++) {
210 Unmap(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),
228 mDrmFormats(),
229 mStrides(),
230 mOffsets(),
231 mGbmBufferObject(),
232 mMappedRegion(),
233 mMappedRegionStride(),
234 mSyncFd(-1),
235 mSync(nullptr),
236 mGlobalRefCountFd(0),
237 mUID(gNewSurfaceUID++),
238 mSurfaceLock("DMABufSurface") {
239 for (auto& slot : mDmabufFds) {
240 slot = -1;
242 for (auto& modifier : mBufferModifiers) {
243 modifier = DRM_FORMAT_MOD_INVALID;
247 DMABufSurface::~DMABufSurface() {
248 FenceDelete();
249 GlobalRefRelease();
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()) {
259 case SURFACE_RGBA:
260 surf = new DMABufSurfaceRGBA();
261 break;
262 case SURFACE_NV12:
263 case SURFACE_YUV420:
264 surf = new DMABufSurfaceYUV();
265 break;
266 default:
267 return nullptr;
270 if (!surf->Create(desc)) {
271 return nullptr;
273 return surf.forget();
276 void DMABufSurface::FenceDelete() {
277 if (mSyncFd > 0) {
278 close(mSyncFd);
279 mSyncFd = -1;
282 if (!mGL) {
283 return;
285 const auto& gle = gl::GLContextEGL::Cast(mGL);
286 const auto& egl = gle->mEgl;
288 if (mSync) {
289 egl->fDestroySync(mSync);
290 mSync = nullptr;
294 void DMABufSurface::FenceSet() {
295 if (!mGL || !mGL->MakeCurrent()) {
296 MOZ_DIAGNOSTIC_ASSERT(mGL,
297 "DMABufSurface::FenceSet(): missing GL context!");
298 return;
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)) {
305 FenceDelete();
307 mSync = egl->fCreateSync(LOCAL_EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr);
308 if (mSync) {
309 mSyncFd = egl->fDupNativeFenceFDANDROID(mSync);
310 mGL->fFlush();
311 return;
315 // ANDROID_native_fence_sync may not be supported so call glFinish()
316 // as a slow path.
317 mGL->fFinish();
320 void DMABufSurface::FenceWait() {
321 if (!mGL || mSyncFd < 0) {
322 MOZ_DIAGNOSTIC_ASSERT(mGL,
323 "DMABufSurface::FenceWait() missing GL context!");
324 return;
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,
331 LOCAL_EGL_NONE};
332 EGLSync sync = egl->fCreateSync(LOCAL_EGL_SYNC_NATIVE_FENCE_ANDROID, attribs);
333 if (!sync) {
334 MOZ_ASSERT(false, "DMABufSurface::FenceWait(): Failed to create GLFence!");
335 // We failed to create GLFence so clear mSyncFd to avoid another try.
336 close(mSyncFd);
337 mSyncFd = -1;
338 return;
341 // mSyncFd is owned by GLFence so clear local reference to avoid double close
342 // at DMABufSurface::FenceDelete().
343 mSyncFd = -1;
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)) {
352 return false;
355 return true;
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,
362 bool aForceClose) {
363 for (int i = 0; i < DMABUF_BUFFER_PLANES; i++) {
364 CloseFileDescriptorForPlane(aProofOfLock, i, aForceClose);
368 DMABufSurfaceRGBA::DMABufSurfaceRGBA()
369 : DMABufSurface(SURFACE_RGBA),
370 mSurfaceFlags(0),
371 mWidth(0),
372 mHeight(0),
373 mGmbFormat(nullptr),
374 mEGLImage(LOCAL_EGL_NO_IMAGE),
375 mTexture(0),
376 mGbmBufferFlags(0) {}
378 DMABufSurfaceRGBA::~DMABufSurfaceRGBA() {
379 #ifdef MOZ_WAYLAND
380 ReleaseWlBuffer();
381 #endif
382 ReleaseSurface();
385 bool DMABufSurfaceRGBA::OpenFileDescriptorForPlane(
386 const MutexAutoLock& aProofOfLock, int aPlane) {
387 if (mDmabufFds[aPlane] >= 0) {
388 return true;
390 gbm_bo* bo = mGbmBufferObject[0];
391 if (NS_WARN_IF(!bo)) {
392 LOGDMABUF(
393 ("DMABufSurfaceRGBA::OpenFileDescriptorForPlane: Missing "
394 "mGbmBufferObject object!"));
395 return false;
398 if (mBufferPlaneCount == 1) {
399 MOZ_ASSERT(aPlane == 0, "DMABuf: wrong surface plane!");
400 mDmabufFds[0] = GbmLib::GetFd(bo);
401 } else {
402 mDmabufFds[aPlane] = GetDMABufDevice()->GetDmabufFD(
403 GbmLib::GetHandleForPlane(bo, aPlane).u32);
406 if (mDmabufFds[aPlane] < 0) {
407 CloseFileDescriptors(aProofOfLock);
408 return false;
411 return true;
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;
427 mWidth = aWidth;
428 mHeight = aHeight;
430 LOGDMABUF(("DMABufSurfaceRGBA::Create() UID %d size %d x %d\n", mUID, mWidth,
431 mHeight));
433 if (!GetDMABufDevice()->GetGbmDevice()) {
434 LOGDMABUF((" Missing GbmDevice!"));
435 return false;
438 mGmbFormat = GetDMABufDevice()->GetGbmFormat(mSurfaceFlags & DMABUF_ALPHA);
439 if (!mGmbFormat) {
440 // Requested DRM format is not supported.
441 return false;
443 mDrmFormats[0] = mGmbFormat->mFormat;
445 bool useModifiers = (aDMABufSurfaceFlags & DMABUF_USE_MODIFIERS) &&
446 !mGmbFormat->mModifiers.IsEmpty();
447 if (useModifiers) {
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"));
468 return false;
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!"));
475 ReleaseSurface();
476 return false;
479 for (int i = 0; i < mBufferPlaneCount; i++) {
480 mStrides[i] = GbmLib::GetStrideForPlane(mGbmBufferObject[0], i);
481 mOffsets[i] = GbmLib::GetOffset(mGbmBufferObject[0], i);
483 } else {
484 mBufferPlaneCount = 1;
485 mStrides[0] = GbmLib::GetStride(mGbmBufferObject[0]);
488 LOGDMABUF((" Success\n"));
489 return true;
492 bool DMABufSurfaceRGBA::Create(mozilla::gl::GLContext* aGLContext,
493 const EGLImageKHR aEGLImage, int aWidth,
494 int aHeight) {
495 LOGDMABUF(("DMABufSurfaceRGBA::Create() from EGLImage UID = %d\n", mUID));
496 if (!aGLContext) {
497 return false;
499 const auto& gle = gl::GLContextEGL::Cast(aGLContext);
500 const auto& egl = gle->mEgl;
502 mGL = aGLContext;
503 mWidth = aWidth;
504 mHeight = aHeight;
505 mEGLImage = aEGLImage;
506 if (!egl->fExportDMABUFImageQuery(mEGLImage, mDrmFormats, &mBufferPlaneCount,
507 mBufferModifiers)) {
508 LOGDMABUF((" ExportDMABUFImageQueryMESA failed, quit\n"));
509 return false;
511 if (mBufferPlaneCount > DMABUF_BUFFER_PLANES) {
512 LOGDMABUF((" wrong plane count %d, quit\n", mBufferPlaneCount));
513 return false;
515 if (!egl->fExportDMABUFImage(mEGLImage, mDmabufFds, mStrides, mOffsets)) {
516 LOGDMABUF((" ExportDMABUFImageMESA failed, quit\n"));
517 return false;
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) {
524 LOGDMABUF(
525 (" ExportDMABUFImageMESA failed, mDmabufFds[%d] is invalid, quit",
526 i));
527 return false;
531 LOGDMABUF((" imported size %d x %d format %x planes %d modifiers %" PRIx64,
532 mWidth, mHeight, mDrmFormats[0], mBufferPlaneCount,
533 mBufferModifiers[0]));
534 return true;
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);
548 mUID = desc.uid();
550 LOGDMABUF(
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) {
557 LOGDMABUF(
558 (" failed to get DMABuf file descriptor: %s", strerror(errno)));
559 return false;
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();
567 if (mSyncFd < 0) {
568 LOGDMABUF(
569 (" failed to get GL fence file descriptor: %s", strerror(errno)));
570 return false;
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));
580 return true;
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)) {
604 return false;
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);
619 if (mSync) {
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);
631 return true;
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)) {
665 return false;
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;
677 mEGLImage =
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"));
685 return false;
688 if (!aGLContext->MakeCurrent()) {
689 LOGDMABUF(
690 ("DMABufSurfaceRGBA::CreateTexture(): failed to make GL context "
691 "current"));
692 return false;
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,
701 LOCAL_GL_LINEAR);
702 aGLContext->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MIN_FILTER,
703 LOCAL_GL_LINEAR);
704 aGLContext->fEGLImageTargetTexture2D(LOCAL_GL_TEXTURE_2D, mEGLImage);
705 mGL = aGLContext;
707 return true;
710 void DMABufSurfaceRGBA::ReleaseTextures() {
711 LOGDMABUF(("DMABufSurfaceRGBA::ReleaseTextures() UID %d\n", mUID));
712 FenceDelete();
714 if (!mTexture && !mEGLImage) {
715 return;
718 if (!mGL) {
719 #ifdef NIGHTLY_BUILD
720 MOZ_DIAGNOSTIC_ASSERT(mGL, "Missing GL context!");
721 #else
722 NS_WARNING(
723 "DMABufSurfaceRGBA::ReleaseTextures(): Missing GL context! We're "
724 "leaking textures!");
725 return;
726 #endif
729 const auto& gle = gl::GLContextEGL::Cast(mGL);
730 const auto& egl = gle->mEgl;
732 if (mTexture && mGL->MakeCurrent()) {
733 mGL->fDeleteTextures(1, &mTexture);
734 mTexture = 0;
737 if (mEGLImage != LOCAL_EGL_NO_IMAGE) {
738 egl->fDestroyImage(mEGLImage);
739 mEGLImage = LOCAL_EGL_NO_IMAGE;
741 mGL = nullptr;
744 void DMABufSurfaceRGBA::ReleaseSurface() {
745 MOZ_ASSERT(!IsMapped(), "We can't release mapped buffer!");
747 ReleaseTextures();
748 ReleaseDMABuf();
751 #ifdef MOZ_WAYLAND
752 bool DMABufSurfaceRGBA::CreateWlBuffer() {
753 MutexAutoLock lockFD(mSurfaceLock);
754 if (!OpenFileDescriptors(lockFD)) {
755 return false;
758 nsWaylandDisplay* waylandDisplay = widget::WaylandDisplayGet();
759 if (!waylandDisplay->GetDmabuf()) {
760 CloseFileDescriptors(lockFD);
761 return false;
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);
781 #endif
783 // We should synchronize DMA Buffer object access from CPU to avoid potential
784 // cache incoherency and data loss.
785 // See
786 // https://01.org/linuxgraphics/gfx-docs/drm/driver-api/dma-buf.html#cpu-access-to-dma-buffer-objects
787 struct dma_buf_sync {
788 uint64_t flags;
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;
801 while (true) {
802 int ret;
803 ret = ioctl(aFd, DMA_BUF_IOCTL_SYNC, &sync);
804 if (ret == -1 && errno == EINTR) {
805 continue;
806 } else if (ret == -1) {
807 LOGDMABUF(
808 ("Failed to synchronize DMA buffer: %s FD %d", strerror(errno), aFd));
809 break;
810 } else {
811 break;
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");
822 return nullptr;
825 LOGDMABUF(
826 ("DMABufSurfaceRGBA::MapInternal() UID %d plane %d size %d x %d -> %d x "
827 "%d\n",
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)));
837 return nullptr;
839 if (aStride) {
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;
887 #ifdef DEBUG
888 void DMABufSurfaceRGBA::DumpToFile(const char* pFile) {
889 uint32_t stride;
891 if (!MapReadOnly(&stride)) {
892 return;
894 cairo_surface_t* surface = nullptr;
896 auto unmap = MakeScopeExit([&] {
897 if (surface) {
898 cairo_surface_destroy(surface);
900 Unmap();
903 surface = cairo_image_surface_create_for_data(
904 (unsigned char*)mMappedRegion[0], CAIRO_FORMAT_ARGB32, mWidth, mHeight,
905 stride);
906 if (cairo_surface_status(surface) == CAIRO_STATUS_SUCCESS) {
907 cairo_surface_write_to_png(surface, pFile);
910 #endif
912 #if 0
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);
924 return true;
926 #endif
928 // TODO - Clear the surface by EGL
929 void DMABufSurfaceRGBA::Clear() {
930 uint32_t destStride;
931 void* destData = Map(&destStride);
932 memset(destData, 0, GetHeight() * destStride);
933 Unmap();
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)) {
955 return nullptr;
957 return surf.forget();
960 already_AddRefed<DMABufSurface> DMABufSurfaceRGBA::CreateDMABufSurface(
961 mozilla::gl::GLContext* aGLContext, const EGLImageKHR aEGLImage, int aWidth,
962 int aHeight) {
963 RefPtr<DMABufSurfaceRGBA> surf = new DMABufSurfaceRGBA();
964 if (!surf->Create(aGLContext, aEGLImage, aWidth, aHeight)) {
965 return nullptr;
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",
974 surf->GetUID()));
975 if (!surf->UpdateYUVData(aDesc, aWidth, aHeight, /* aCopy */ false)) {
976 return nullptr;
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",
985 surf->GetUID()));
986 if (!surf->UpdateYUVData(aDesc, aWidth, aHeight, /* aCopy */ true)) {
987 return nullptr;
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)) {
998 return nullptr;
1000 return surf.forget();
1003 DMABufSurfaceYUV::DMABufSurfaceYUV()
1004 : DMABufSurface(SURFACE_NV12),
1005 mWidth(),
1006 mHeight(),
1007 mWidthAligned(),
1008 mHeightAligned(),
1009 mTexture() {
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) {
1024 return true;
1027 if (mGbmBufferObject[aPlane] == nullptr) {
1028 LOGDMABUF(
1029 ("DMABufSurfaceYUV::OpenFileDescriptorForPlane: Missing "
1030 "mGbmBufferObject object!"));
1031 return false;
1034 mDmabufFds[aPlane] = GbmLib::GetFd(mGbmBufferObject[aPlane]);
1035 if (mDmabufFds[aPlane] < 0) {
1036 CloseFileDescriptors(aProofOfLock);
1037 return false;
1039 return true;
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));
1053 // Already exists?
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));
1060 return false;
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;
1068 } else {
1069 LOGDMABUF((" Can't import surface data of 0x%x format", aDesc.fourcc));
1070 return false;
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],
1089 mDrmFormats[i]));
1091 return true;
1094 bool DMABufSurfaceYUV::MoveYUVDataImpl(const VADRMPRIMESurfaceDescriptor& aDesc,
1095 int aWidth, int aHeight) {
1096 if (!ImportPRIMESurfaceDescriptor(aDesc, aWidth, aHeight)) {
1097 return false;
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);
1105 return true;
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!"));
1125 return false;
1128 MOZ_DIAGNOSTIC_ASSERT(mGbmBufferObject[aPlane] == nullptr);
1129 bool useModifiers = (mBufferModifiers[aPlane] != DRM_FORMAT_MOD_INVALID);
1130 if (useModifiers) {
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)));
1145 return false;
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];
1152 return true;
1155 bool DMABufSurfaceYUV::CopyYUVDataImpl(const VADRMPRIMESurfaceDescriptor& aDesc,
1156 int aWidth, int aHeight) {
1157 RefPtr<DMABufSurfaceYUV> tmpSurf = CreateYUVSurface(aDesc, aWidth, aHeight);
1158 if (!tmpSurf) {
1159 return false;
1162 if (!ImportPRIMESurfaceDescriptor(aDesc, aWidth, aHeight)) {
1163 return false;
1166 StaticMutexAutoLock lock(sSnapshotContextMutex);
1167 RefPtr<GLContext> context = ClaimSnapshotGLContext();
1168 auto releaseTextures = MakeScopeExit([&] {
1169 tmpSurf->ReleaseTextures();
1170 ReleaseTextures();
1171 ReturnSnapshotGLContext(context);
1174 for (int i = 0; i < mBufferPlaneCount; i++) {
1175 if (!tmpSurf->CreateTexture(context, i)) {
1176 return false;
1178 if (!CreateYUVPlane(i) || !CreateTexture(context, i)) {
1179 return false;
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);
1186 return true;
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,
1197 int aDrmFormat) {
1198 LOGDMABUF(("DMABufSurfaceYUV::CreateLinearYUVPlane() UID %d size %d x %d",
1199 mUID, aWidth, aHeight));
1201 if (!GetDMABufDevice()->GetGbmDevice()) {
1202 LOGDMABUF((" Missing GbmDevice!"));
1203 return false;
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)));
1215 return false;
1218 mStrides[aPlane] = GbmLib::GetStride(mGbmBufferObject[aPlane]);
1219 mDmabufFds[aPlane] = -1;
1221 return true;
1224 void DMABufSurfaceYUV::UpdateYUVPlane(int aPlane, void* aPixelData,
1225 int aLineSize) {
1226 LOGDMABUF(
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]);
1231 } else {
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]);
1236 src += aLineSize;
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!"));
1246 return false;
1249 if (mBufferPlaneCount != 3) {
1250 LOGDMABUF((" DMABufSurfaceYUV planes does not match!"));
1251 return false;
1254 auto unmapBuffers = MakeScopeExit([&] {
1255 Unmap(0);
1256 Unmap(1);
1257 Unmap(2);
1260 // Map planes
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!"));
1265 return false;
1267 if ((int)mMappedRegionStride[i] < mWidth[i]) {
1268 LOGDMABUF((" DMABufSurfaceYUV plane size stride does not match!"));
1269 return false;
1273 // Copy planes
1274 for (int i = 0; i < mBufferPlaneCount; i++) {
1275 UpdateYUVPlane(i, aPixelData[i], aLineSizes[i]);
1278 return true;
1281 bool DMABufSurfaceYUV::Create(int aWidth, int aHeight, void** aPixelData,
1282 int* aLineSizes) {
1283 LOGDMABUF(("DMABufSurfaceYUV::Create() UID %d size %d x %d", mUID, aWidth,
1284 aHeight));
1286 mSurfaceType = SURFACE_YUV420;
1287 mBufferPlaneCount = 3;
1289 if (!CreateLinearYUVPlane(0, aWidth, aHeight, GBM_FORMAT_R8)) {
1290 return false;
1292 if (!CreateLinearYUVPlane(1, aWidth >> 1, aHeight >> 1, GBM_FORMAT_R8)) {
1293 return false;
1295 if (!CreateLinearYUVPlane(2, aWidth >> 1, aHeight >> 1, GBM_FORMAT_R8)) {
1296 return false;
1298 if (!aPixelData || !aLineSizes) {
1299 return true;
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();
1314 mUID = aDesc.uid();
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",
1323 strerror(errno)));
1324 return false;
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();
1340 if (mSyncFd < 0) {
1341 LOGDMABUF(
1342 (" failed to get GL fence file descriptor: %s", strerror(errno)));
1343 return false;
1347 if (aDesc.refCount().Length() > 0) {
1348 GlobalRefCountImport(aDesc.refCount()[0].ClonePlatformHandle().release());
1351 return true;
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)) {
1372 return false;
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);
1389 if (mSync) {
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,
1400 refCountFDs);
1401 return true;
1404 bool DMABufSurfaceYUV::CreateEGLImage(GLContext* aGLContext, int aPlane) {
1405 LOGDMABUF(
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"));
1417 return false;
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);
1446 mEGLImage[aPlane] =
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"));
1454 return false;
1457 LOGDMABUF((" Success."));
1458 return true;
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) {
1477 LOGDMABUF(
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)) {
1483 return false;
1485 if (!aGLContext->MakeCurrent()) {
1486 LOGDMABUF((" Failed to make GL context current."));
1487 return false;
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,
1496 LOCAL_GL_LINEAR);
1497 aGLContext->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MIN_FILTER,
1498 LOCAL_GL_LINEAR);
1499 aGLContext->fEGLImageTargetTexture2D(LOCAL_GL_TEXTURE_2D, mEGLImage[aPlane]);
1500 mGL = aGLContext;
1501 return true;
1504 void DMABufSurfaceYUV::ReleaseTextures() {
1505 LOGDMABUF(("DMABufSurfaceYUV::ReleaseTextures() UID %d", mUID));
1507 FenceDelete();
1509 bool textureActive = false;
1510 for (int i = 0; i < mBufferPlaneCount; i++) {
1511 if (mTexture[i] || mEGLImage[i]) {
1512 textureActive = true;
1513 break;
1517 if (!textureActive) {
1518 return;
1521 if (!mGL) {
1522 #ifdef NIGHTLY_BUILD
1523 MOZ_DIAGNOSTIC_ASSERT(mGL, "Missing GL context!");
1524 #else
1525 NS_WARNING(
1526 "DMABufSurfaceYUV::ReleaseTextures(): Missing GL context! We're "
1527 "leaking textures!");
1528 return;
1529 #endif
1532 if (!mGL->MakeCurrent()) {
1533 NS_WARNING(
1534 "DMABufSurfaceYUV::ReleaseTextures(): MakeCurrent failed. We're "
1535 "leaking textures!");
1536 return;
1539 mGL->fDeleteTextures(DMABUF_BUFFER_PLANES, mTexture);
1540 for (int i = 0; i < DMABUF_BUFFER_PLANES; i++) {
1541 mTexture[i] = 0;
1543 ReleaseEGLImages(mGL);
1544 mGL = nullptr;
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!"));
1560 return false;
1564 LOGDMABUF((" success"));
1565 return true;
1568 gfx::SurfaceFormat DMABufSurfaceYUV::GetFormat() {
1569 switch (mSurfaceType) {
1570 case SURFACE_NV12:
1571 return gfx::SurfaceFormat::NV12;
1572 case SURFACE_YUV420:
1573 return gfx::SurfaceFormat::YUV;
1574 default:
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) {
1585 case SURFACE_NV12:
1586 return 2;
1587 case SURFACE_YUV420:
1588 return 3;
1589 default:
1590 NS_WARNING("DMABufSurfaceYUV::GetTextureCount(): Wrong surface format!");
1591 return 1;
1595 void DMABufSurfaceYUV::ReleaseSurface() {
1596 LOGDMABUF(("DMABufSurfaceYUV::ReleaseSurface() UID %d", mUID));
1597 ReleaseTextures();
1598 ReleaseDMABuf();
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([&] {
1608 ReleaseTextures();
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."));
1615 return nullptr;
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,
1625 nullptr);
1627 ScopedFramebufferForTexture autoFBForTex(context, scopedTex.Texture());
1628 if (!autoFBForTex.IsComplete()) {
1629 LOGDMABUF(("GetAsSourceSurface: ScopedFramebufferForTexture failed."));
1630 return nullptr;
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."));
1638 return nullptr;
1642 RefPtr<gfx::DataSourceSurface> source =
1643 gfx::Factory::CreateDataSourceSurface(size, gfx::SurfaceFormat::B8G8R8A8);
1644 if (NS_WARN_IF(!source)) {
1645 LOGDMABUF(("GetAsSourceSurface: CreateDataSourceSurface failed."));
1646 return nullptr;
1649 ScopedBindFramebuffer bind(context, autoFBForTex.FB());
1650 ReadPixelsIntoDataSurface(context, source);
1652 return source.forget();
1655 #if 0
1656 void DMABufSurfaceYUV::ClearPlane(int aPlane) {
1657 if (!MapInternal(0, 0, mWidth[aPlane], mHeight[aPlane], nullptr,
1658 GBM_BO_TRANSFER_WRITE, aPlane)) {
1659 return;
1661 if ((int)mMappedRegionStride[aPlane] < mWidth[aPlane]) {
1662 return;
1664 memset((char*)mMappedRegion[aPlane], 0,
1665 mMappedRegionStride[aPlane] * mHeight[aPlane]);
1666 Unmap(aPlane);
1669 # include "gfxUtils.h"
1671 void DMABufSurfaceYUV::DumpToFile(const char* aFile) {
1672 RefPtr<gfx::DataSourceSurface> surf = GetAsSourceSurface();
1673 gfxUtils::WriteAsPNG(surf, aFile);
1675 #endif