Backed out changeset b71c8c052463 (bug 1943846) for causing mass failures. CLOSED...
[gecko.git] / widget / gtk / DMABufSurface.cpp
blob81e10945a92eb6046170b6ae5d51354551aaa373
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"
9 #include "DMABufFormats.h"
11 #ifdef MOZ_WAYLAND
12 # include "nsWaylandDisplay.h"
13 #endif
15 #include <fcntl.h>
16 #include <getopt.h>
17 #include <signal.h>
18 #include <stdbool.h>
19 #include <stdint.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <unistd.h>
24 #include <sys/time.h>
25 #include <dlfcn.h>
26 #include <sys/mman.h>
27 #ifdef HAVE_EVENTFD
28 # include <sys/eventfd.h>
29 #endif
30 #include <poll.h>
31 #ifdef HAVE_SYSIOCCOM_H
32 # include <sys/ioccom.h>
33 #endif
34 #include <sys/ioctl.h>
36 #include "mozilla/widget/gbm.h"
37 #include "mozilla/widget/va_drmcommon.h"
38 #include "YCbCrUtils.h"
39 #include "mozilla/gfx/2D.h"
40 #include "mozilla/gfx/FileHandleWrapper.h"
41 #include "GLContextTypes.h" // for GLContext, etc
42 #include "GLContextEGL.h"
43 #include "GLContextProvider.h"
44 #include "ScopedGLHelpers.h"
45 #include "GLBlitHelper.h"
46 #include "GLReadTexImageHelper.h"
47 #include "nsGtkUtils.h"
49 #include "mozilla/layers/LayersSurfaces.h"
50 #include "mozilla/ScopeExit.h"
53 TODO:
54 - DRM device selection:
55 https://lists.freedesktop.org/archives/wayland-devel/2018-November/039660.html
56 - Use uint64_t mBufferModifiers / mGbmBufferObject for RGBA
57 - Remove file descriptors open/close?
60 /* C++ / C typecast macros for special EGL handle values */
61 #if defined(__cplusplus)
62 # define EGL_CAST(type, value) (static_cast<type>(value))
63 #else
64 # define EGL_CAST(type, value) ((type)(value))
65 #endif
67 using namespace mozilla;
68 using namespace mozilla::widget;
69 using namespace mozilla::gl;
70 using namespace mozilla::layers;
71 using namespace mozilla::gfx;
73 #ifdef MOZ_LOGGING
74 # include "mozilla/Logging.h"
75 # include "nsTArray.h"
76 # include "Units.h"
77 static LazyLogModule gDmabufRefLog("DmabufRef");
78 # define LOGDMABUFREF(args) \
79 MOZ_LOG(gDmabufRefLog, mozilla::LogLevel::Debug, args)
80 #else
81 # define LOGDMABUFREF(args)
82 #endif /* MOZ_LOGGING */
84 #define BUFFER_FLAGS 0
86 MOZ_RUNINIT static RefPtr<GLContext> sSnapshotContext;
87 static StaticMutex sSnapshotContextMutex MOZ_UNANNOTATED;
88 static Atomic<int> gNewSurfaceUID(1);
90 RefPtr<GLContext> ClaimSnapshotGLContext() {
91 if (!sSnapshotContext) {
92 nsCString discardFailureId;
93 sSnapshotContext = GLContextProvider::CreateHeadless({}, &discardFailureId);
94 if (!sSnapshotContext) {
95 LOGDMABUF(
96 ("ClaimSnapshotGLContext: Failed to create snapshot GLContext."));
97 return nullptr;
99 sSnapshotContext->mOwningThreadId = Nothing(); // No singular owner.
101 if (!sSnapshotContext->MakeCurrent()) {
102 LOGDMABUF(("ClaimSnapshotGLContext: Failed to make GLContext current."));
103 return nullptr;
105 return sSnapshotContext;
108 void ReturnSnapshotGLContext(RefPtr<GLContext> aGLContext) {
109 // direct eglMakeCurrent() call breaks current context caching so make sure
110 // it's not used.
111 MOZ_ASSERT(!aGLContext->mUseTLSIsCurrent);
112 if (!aGLContext->IsCurrent()) {
113 LOGDMABUF(("ReturnSnapshotGLContext() failed, is not current!"));
114 return;
116 const auto& gle = gl::GLContextEGL::Cast(aGLContext);
117 const auto& egl = gle->mEgl;
118 egl->fMakeCurrent(EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
121 bool DMABufSurface::IsGlobalRefSet() const {
122 if (!mGlobalRefCountFd) {
123 return false;
125 struct pollfd pfd;
126 pfd.fd = mGlobalRefCountFd;
127 pfd.events = POLLIN;
128 return poll(&pfd, 1, 0) == 1;
131 void DMABufSurface::GlobalRefRelease() {
132 #ifdef HAVE_EVENTFD
133 if (!mGlobalRefCountFd) {
134 return;
136 LOGDMABUFREF(("DMABufSurface::GlobalRefRelease UID %d", mUID));
137 uint64_t counter;
138 if (read(mGlobalRefCountFd, &counter, sizeof(counter)) != sizeof(counter)) {
139 if (errno == EAGAIN) {
140 LOGDMABUFREF(
141 (" GlobalRefRelease failed: already zero reference! UID %d", mUID));
143 // EAGAIN means the refcount is already zero. It happens when we release
144 // last reference to the surface.
145 if (errno != EAGAIN) {
146 NS_WARNING(nsPrintfCString("Failed to unref dmabuf global ref count: %s",
147 strerror(errno))
148 .get());
151 #endif
154 void DMABufSurface::GlobalRefAdd() {
155 #ifdef HAVE_EVENTFD
156 LOGDMABUFREF(("DMABufSurface::GlobalRefAdd UID %d", mUID));
157 MOZ_DIAGNOSTIC_ASSERT(mGlobalRefCountFd);
158 uint64_t counter = 1;
159 if (write(mGlobalRefCountFd, &counter, sizeof(counter)) != sizeof(counter)) {
160 NS_WARNING(nsPrintfCString("Failed to ref dmabuf global ref count: %s",
161 strerror(errno))
162 .get());
164 #endif
167 void DMABufSurface::GlobalRefCountCreate() {
168 #ifdef HAVE_EVENTFD
169 LOGDMABUFREF(("DMABufSurface::GlobalRefCountCreate UID %d", mUID));
170 MOZ_DIAGNOSTIC_ASSERT(!mGlobalRefCountFd);
171 // Create global ref count initialized to 0,
172 // i.e. is not referenced after create.
173 mGlobalRefCountFd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK | EFD_SEMAPHORE);
174 if (mGlobalRefCountFd < 0) {
175 NS_WARNING(nsPrintfCString("Failed to create dmabuf global ref count: %s",
176 strerror(errno))
177 .get());
178 mGlobalRefCountFd = 0;
179 return;
181 #endif
184 void DMABufSurface::GlobalRefCountImport(int aFd) {
185 #ifdef HAVE_EVENTFD
186 mGlobalRefCountFd = aFd;
187 if (mGlobalRefCountFd) {
188 LOGDMABUFREF(("DMABufSurface::GlobalRefCountImport UID %d", mUID));
189 GlobalRefAdd();
191 #endif
194 int DMABufSurface::GlobalRefCountExport() {
195 #ifdef MOZ_LOGGING
196 if (mGlobalRefCountFd) {
197 LOGDMABUFREF(("DMABufSurface::GlobalRefCountExport UID %d", mUID));
199 #endif
200 return mGlobalRefCountFd;
203 void DMABufSurface::GlobalRefCountDelete() {
204 if (mGlobalRefCountFd) {
205 LOGDMABUFREF(("DMABufSurface::GlobalRefCountDelete UID %d", mUID));
206 close(mGlobalRefCountFd);
207 mGlobalRefCountFd = 0;
211 void DMABufSurface::ReleaseDMABuf() {
212 LOGDMABUF(("DMABufSurface::ReleaseDMABuf() UID %d", mUID));
213 for (int i = 0; i < mBufferPlaneCount; i++) {
214 Unmap(i);
217 MutexAutoLock lockFD(mSurfaceLock);
218 CloseFileDescriptors(lockFD, /* aForceClose */ true);
220 for (int i = 0; i < mBufferPlaneCount; i++) {
221 if (mGbmBufferObject[i]) {
222 GbmLib::Destroy(mGbmBufferObject[i]);
223 mGbmBufferObject[i] = nullptr;
226 mBufferPlaneCount = 0;
229 DMABufSurface::DMABufSurface(SurfaceType aSurfaceType)
230 : mSurfaceType(aSurfaceType),
231 mBufferPlaneCount(0),
232 mDrmFormats(),
233 mStrides(),
234 mOffsets(),
235 mGbmBufferObject(),
236 mMappedRegion(),
237 mMappedRegionStride(),
238 mSync(nullptr),
239 mGlobalRefCountFd(0),
240 mUID(gNewSurfaceUID++),
241 mSurfaceLock("DMABufSurface") {
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_YUV:
263 surf = new DMABufSurfaceYUV();
264 break;
265 default:
266 return nullptr;
269 if (!surf->Create(desc)) {
270 return nullptr;
272 return surf.forget();
275 void DMABufSurface::FenceDelete() {
276 if (mSyncFd) {
277 mSyncFd = nullptr;
280 if (!mGL) {
281 return;
283 const auto& gle = gl::GLContextEGL::Cast(mGL);
284 const auto& egl = gle->mEgl;
286 if (mSync) {
287 egl->fDestroySync(mSync);
288 mSync = nullptr;
292 void DMABufSurface::FenceSet() {
293 if (!mGL || !mGL->MakeCurrent()) {
294 MOZ_DIAGNOSTIC_ASSERT(mGL,
295 "DMABufSurface::FenceSet(): missing GL context!");
296 return;
298 const auto& gle = gl::GLContextEGL::Cast(mGL);
299 const auto& egl = gle->mEgl;
301 if (egl->IsExtensionSupported(EGLExtension::KHR_fence_sync) &&
302 egl->IsExtensionSupported(EGLExtension::ANDROID_native_fence_sync)) {
303 FenceDelete();
305 mSync = egl->fCreateSync(LOCAL_EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr);
306 if (mSync) {
307 auto rawFd = egl->fDupNativeFenceFDANDROID(mSync);
308 mSyncFd = new gfx::FileHandleWrapper(UniqueFileHandle(rawFd));
309 mGL->fFlush();
310 return;
314 // ANDROID_native_fence_sync may not be supported so call glFinish()
315 // as a slow path.
316 mGL->fFinish();
319 void DMABufSurface::FenceWait() {
320 if (!mGL || !mSyncFd) {
321 MOZ_DIAGNOSTIC_ASSERT(mGL,
322 "DMABufSurface::FenceWait() missing GL context!");
323 return;
326 const auto& gle = gl::GLContextEGL::Cast(mGL);
327 const auto& egl = gle->mEgl;
328 auto syncFd = mSyncFd->ClonePlatformHandle();
329 // No need to try mSyncFd twice.
330 mSyncFd = nullptr;
332 const EGLint attribs[] = {LOCAL_EGL_SYNC_NATIVE_FENCE_FD_ANDROID,
333 syncFd.get(), LOCAL_EGL_NONE};
334 EGLSync sync = egl->fCreateSync(LOCAL_EGL_SYNC_NATIVE_FENCE_ANDROID, attribs);
335 if (!sync) {
336 MOZ_ASSERT(false, "DMABufSurface::FenceWait(): Failed to create GLFence!");
337 return;
340 // syncFd is owned by GLFence so clear local reference to avoid double.
341 Unused << syncFd.release();
343 egl->fClientWaitSync(sync, 0, LOCAL_EGL_FOREVER);
344 egl->fDestroySync(sync);
347 void DMABufSurface::MaybeSemaphoreWait(GLuint aGlTexture) {
348 MOZ_ASSERT(aGlTexture);
350 if (!mSemaphoreFd) {
351 return;
354 if (!mGL) {
355 MOZ_DIAGNOSTIC_ASSERT(mGL,
356 "DMABufSurface::SemaphoreWait() missing GL context!");
357 return;
360 if (!mGL->IsExtensionSupported(gl::GLContext::EXT_semaphore) ||
361 !mGL->IsExtensionSupported(gl::GLContext::EXT_semaphore_fd)) {
362 MOZ_ASSERT_UNREACHABLE("unexpected to be called");
363 gfxCriticalNoteOnce << "EXT_semaphore_fd is not suppored";
364 return;
367 auto fd = mSemaphoreFd->ClonePlatformHandle();
368 // No need to try mSemaphoreFd twice.
369 mSemaphoreFd = nullptr;
371 GLuint semaphoreHandle = 0;
372 mGL->fGenSemaphoresEXT(1, &semaphoreHandle);
373 mGL->fImportSemaphoreFdEXT(semaphoreHandle,
374 LOCAL_GL_HANDLE_TYPE_OPAQUE_FD_EXT, fd.release());
375 auto error = mGL->fGetError();
376 if (error != LOCAL_GL_NO_ERROR) {
377 gfxCriticalNoteOnce << "glImportSemaphoreFdEXT failed: " << error;
378 return;
381 GLenum srcLayout = LOCAL_GL_LAYOUT_COLOR_ATTACHMENT_EXT;
382 mGL->fWaitSemaphoreEXT(semaphoreHandle, 0, nullptr, 1, &aGlTexture,
383 &srcLayout);
384 error = mGL->fGetError();
385 if (error != LOCAL_GL_NO_ERROR) {
386 gfxCriticalNoteOnce << "glWaitSemaphoreEXT failed: " << error;
387 return;
391 bool DMABufSurface::OpenFileDescriptors(const MutexAutoLock& aProofOfLock) {
392 for (int i = 0; i < mBufferPlaneCount; i++) {
393 if (!OpenFileDescriptorForPlane(aProofOfLock, i)) {
394 return false;
397 return true;
400 // We can safely close DMABuf file descriptors only when we have a valid
401 // GbmBufferObject. When we don't have a valid GbmBufferObject and a DMABuf
402 // file descriptor is closed, whole surface is released.
403 void DMABufSurface::CloseFileDescriptors(const MutexAutoLock& aProofOfLock,
404 bool aForceClose) {
405 for (int i = 0; i < DMABUF_BUFFER_PLANES; i++) {
406 CloseFileDescriptorForPlane(aProofOfLock, i, aForceClose);
410 nsresult DMABufSurface::ReadIntoBuffer(uint8_t* aData, int32_t aStride,
411 const gfx::IntSize& aSize,
412 gfx::SurfaceFormat aFormat) {
413 LOGDMABUF(("DMABufSurface::ReadIntoBuffer UID %d", mUID));
415 // We're empty, nothing to copy
416 if (!GetTextureCount()) {
417 return NS_ERROR_FAILURE;
420 MOZ_ASSERT(aSize.width == GetWidth());
421 MOZ_ASSERT(aSize.height == GetHeight());
423 StaticMutexAutoLock lock(sSnapshotContextMutex);
424 RefPtr<GLContext> context = ClaimSnapshotGLContext();
425 auto releaseTextures = mozilla::MakeScopeExit([&] {
426 ReleaseTextures();
427 ReturnSnapshotGLContext(context);
430 for (int i = 0; i < GetTextureCount(); i++) {
431 if (!GetTexture(i) && !CreateTexture(context, i)) {
432 LOGDMABUF(("ReadIntoBuffer: Failed to create DMABuf textures."));
433 return NS_ERROR_FAILURE;
437 ScopedTexture scopedTex(context);
438 ScopedBindTexture boundTex(context, scopedTex.Texture());
440 context->fTexImage2D(LOCAL_GL_TEXTURE_2D, 0, LOCAL_GL_RGBA, aSize.width,
441 aSize.height, 0, LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE,
442 nullptr);
444 ScopedFramebufferForTexture autoFBForTex(context, scopedTex.Texture());
445 if (!autoFBForTex.IsComplete()) {
446 LOGDMABUF(("ReadIntoBuffer: ScopedFramebufferForTexture failed."));
447 return NS_ERROR_FAILURE;
450 const gl::OriginPos destOrigin = gl::OriginPos::BottomLeft;
452 const ScopedBindFramebuffer bindFB(context, autoFBForTex.FB());
453 if (!context->BlitHelper()->Blit(this, aSize, destOrigin)) {
454 LOGDMABUF(("ReadIntoBuffer: Blit failed."));
455 return NS_ERROR_FAILURE;
459 ScopedBindFramebuffer bind(context, autoFBForTex.FB());
460 ReadPixelsIntoBuffer(context, aData, aStride, aSize, aFormat);
461 return NS_OK;
464 already_AddRefed<gfx::DataSourceSurface> DMABufSurface::GetAsSourceSurface() {
465 LOGDMABUF(("DMABufSurface::GetAsSourceSurface UID %d", mUID));
467 gfx::IntSize size(GetWidth(), GetHeight());
468 const auto format = gfx::SurfaceFormat::B8G8R8A8;
469 RefPtr<gfx::DataSourceSurface> source =
470 gfx::Factory::CreateDataSourceSurface(size, format);
471 if (NS_WARN_IF(!source)) {
472 LOGDMABUF(("GetAsSourceSurface: CreateDataSourceSurface failed."));
473 return nullptr;
476 gfx::DataSourceSurface::ScopedMap map(source,
477 gfx::DataSourceSurface::READ_WRITE);
478 if (NS_WARN_IF(!map.IsMapped())) {
479 LOGDMABUF(("GetAsSourceSurface: Mapping surface failed."));
480 return nullptr;
483 if (NS_WARN_IF(NS_FAILED(
484 ReadIntoBuffer(map.GetData(), map.GetStride(), size, format)))) {
485 LOGDMABUF(("GetAsSourceSurface: Reading into buffer failed."));
486 return nullptr;
489 return source.forget();
492 DMABufSurfaceRGBA::DMABufSurfaceRGBA()
493 : DMABufSurface(SURFACE_RGBA),
494 mWidth(0),
495 mHeight(0),
496 mEGLImage(LOCAL_EGL_NO_IMAGE),
497 mTexture(0),
498 mGbmBufferFlags(0) {}
500 DMABufSurfaceRGBA::~DMABufSurfaceRGBA() { ReleaseSurface(); }
502 bool DMABufSurfaceRGBA::OpenFileDescriptorForPlane(
503 const MutexAutoLock& aProofOfLock, int aPlane) {
504 if (mDmabufFds[aPlane]) {
505 return true;
507 gbm_bo* bo = mGbmBufferObject[0];
508 if (NS_WARN_IF(!bo)) {
509 LOGDMABUF(
510 ("DMABufSurfaceRGBA::OpenFileDescriptorForPlane: Missing "
511 "mGbmBufferObject object!"));
512 return false;
515 if (mBufferPlaneCount == 1) {
516 MOZ_ASSERT(aPlane == 0, "DMABuf: wrong surface plane!");
517 auto rawFd = GbmLib::GetFd(bo);
518 if (rawFd >= 0) {
519 mDmabufFds[0] = new gfx::FileHandleWrapper(UniqueFileHandle(rawFd));
520 } else {
521 gfxCriticalNoteOnce << "GbmLib::GetFd() failed";
522 LOGDMABUF(
523 ("DMABufSurfaceRGBA::OpenFileDescriptorForPlane: GbmLib::GetFd() "
524 "failed"));
526 } else {
527 auto rawFd = GetDMABufDevice()->GetDmabufFD(
528 GbmLib::GetHandleForPlane(bo, aPlane).u32);
529 if (rawFd >= 0) {
530 mDmabufFds[aPlane] = new gfx::FileHandleWrapper(UniqueFileHandle(rawFd));
531 } else {
532 gfxCriticalNoteOnce << "DMABufDevice::GetDmabufFD() failed";
533 LOGDMABUF(
534 ("DMABufSurfaceRGBA::OpenFileDescriptorForPlane: "
535 "DMABufDevice::GetDmabufFD() failed"));
539 if (!mDmabufFds[aPlane]) {
540 CloseFileDescriptors(aProofOfLock);
541 return false;
544 return true;
547 void DMABufSurfaceRGBA::CloseFileDescriptorForPlane(
548 const MutexAutoLock& aProofOfLock, int aPlane, bool aForceClose = false) {
549 if ((aForceClose || mGbmBufferObject[0]) && mDmabufFds[aPlane]) {
550 mDmabufFds[aPlane] = nullptr;
554 bool DMABufSurfaceRGBA::Create(int aWidth, int aHeight,
555 int aDMABufSurfaceFlags) {
556 mFOURCCFormat = aDMABufSurfaceFlags & DMABUF_ALPHA ? GBM_FORMAT_ARGB8888
557 : GBM_FORMAT_XRGB8888;
558 RefPtr<DRMFormat> format = GetDMABufDevice()->GetDRMFormat(mFOURCCFormat);
559 if (!format) {
560 return false;
562 return Create(aWidth, aHeight, format, aDMABufSurfaceFlags);
565 bool DMABufSurfaceRGBA::Create(int aWidth, int aHeight,
566 RefPtr<DRMFormat> aFormat,
567 int aDMABufSurfaceFlags) {
568 MOZ_ASSERT(mGbmBufferObject[0] == nullptr, "Already created?");
570 if (!GetDMABufDevice()->GetGbmDevice()) {
571 LOGDMABUF(("DMABufSurfaceRGBA::Create(): Missing GbmDevice!"));
572 return false;
575 mWidth = aWidth;
576 mHeight = aHeight;
577 mFOURCCFormat = aFormat->GetFormat();
579 LOGDMABUF(
580 ("DMABufSurfaceRGBA::Create() UID %d size %d x %d format 0x%x "
581 "modifiers %d\n",
582 mUID, mWidth, mHeight, mFOURCCFormat, aFormat->UseModifiers()));
584 if (aDMABufSurfaceFlags & DMABUF_TEXTURE) {
585 mGbmBufferFlags = GBM_BO_USE_RENDERING;
586 } else if (aDMABufSurfaceFlags & DMABUF_SCANOUT) {
587 mGbmBufferFlags = GBM_BO_USE_RENDERING | GBM_BO_USE_SCANOUT;
589 bool useModifiers =
590 aFormat->UseModifiers() && (aDMABufSurfaceFlags & DMABUF_USE_MODIFIERS);
591 if (useModifiers) {
592 LOGDMABUF((" Creating with modifiers\n"));
593 uint32_t modifiersNum = 0;
594 const uint64_t* modifiers = aFormat->GetModifiers(modifiersNum);
595 mGbmBufferObject[0] = GbmLib::CreateWithModifiers2(
596 GetDMABufDevice()->GetGbmDevice(), mWidth, mHeight, mFOURCCFormat,
597 modifiers, modifiersNum, mGbmBufferFlags);
598 if (mGbmBufferObject[0]) {
599 mBufferModifiers[0] = GbmLib::GetModifier(mGbmBufferObject[0]);
603 if (!mGbmBufferObject[0]) {
604 LOGDMABUF((" Creating without modifiers\n"));
605 mGbmBufferFlags = GBM_BO_USE_RENDERING | GBM_BO_USE_LINEAR;
606 mGbmBufferObject[0] =
607 GbmLib::Create(GetDMABufDevice()->GetGbmDevice(), mWidth, mHeight,
608 mFOURCCFormat, mGbmBufferFlags);
609 mBufferModifiers[0] = DRM_FORMAT_MOD_INVALID;
612 if (!mGbmBufferObject[0]) {
613 LOGDMABUF((" Failed to create GbmBufferObject\n"));
614 return false;
617 if (mBufferModifiers[0] != DRM_FORMAT_MOD_INVALID) {
618 mBufferPlaneCount = GbmLib::GetPlaneCount(mGbmBufferObject[0]);
619 LOGDMABUF((" Planes count %d", mBufferPlaneCount));
620 if (mBufferPlaneCount > DMABUF_BUFFER_PLANES) {
621 LOGDMABUF(
622 (" There's too many dmabuf planes! (%d)", mBufferPlaneCount));
623 mBufferPlaneCount = DMABUF_BUFFER_PLANES;
624 return false;
627 for (int i = 0; i < mBufferPlaneCount; i++) {
628 mStrides[i] = GbmLib::GetStrideForPlane(mGbmBufferObject[0], i);
629 mOffsets[i] = GbmLib::GetOffset(mGbmBufferObject[0], i);
631 } else {
632 mBufferPlaneCount = 1;
633 mStrides[0] = GbmLib::GetStride(mGbmBufferObject[0]);
636 LOGDMABUF((" Success\n"));
637 return true;
640 bool DMABufSurfaceRGBA::Create(mozilla::gl::GLContext* aGLContext,
641 const EGLImageKHR aEGLImage, int aWidth,
642 int aHeight) {
643 LOGDMABUF(("DMABufSurfaceRGBA::Create() from EGLImage UID = %d\n", mUID));
644 if (!aGLContext) {
645 return false;
647 const auto& gle = gl::GLContextEGL::Cast(aGLContext);
648 const auto& egl = gle->mEgl;
650 mGL = aGLContext;
651 mWidth = aWidth;
652 mHeight = aHeight;
653 mEGLImage = aEGLImage;
654 if (!egl->fExportDMABUFImageQuery(mEGLImage, mDrmFormats, &mBufferPlaneCount,
655 mBufferModifiers)) {
656 LOGDMABUF((" ExportDMABUFImageQueryMESA failed, quit\n"));
657 return false;
659 if (mBufferPlaneCount > DMABUF_BUFFER_PLANES) {
660 LOGDMABUF((" wrong plane count %d, quit\n", mBufferPlaneCount));
661 mBufferPlaneCount = DMABUF_BUFFER_PLANES;
662 return false;
664 int fds[DMABUF_BUFFER_PLANES] = {-1};
665 if (!egl->fExportDMABUFImage(mEGLImage, fds, mStrides, mOffsets)) {
666 LOGDMABUF((" ExportDMABUFImageMESA failed, quit\n"));
667 return false;
670 for (int i = 0; i < mBufferPlaneCount; i++) {
671 if (fds[i] > 0) {
672 mDmabufFds[i] = new gfx::FileHandleWrapper(UniqueFileHandle(fds[i]));
676 // A broken driver can return dmabuf without valid file descriptors
677 // which leads to fails later so quit now.
678 for (int i = 0; i < mBufferPlaneCount; i++) {
679 if (!mDmabufFds[i]) {
680 LOGDMABUF(
681 (" ExportDMABUFImageMESA failed, mDmabufFds[%d] is invalid, quit",
682 i));
683 return false;
687 LOGDMABUF((" imported size %d x %d format %x planes %d modifiers %" PRIx64,
688 mWidth, mHeight, mFOURCCFormat, mBufferPlaneCount,
689 mBufferModifiers[0]));
690 return true;
693 bool DMABufSurfaceRGBA::Create(
694 RefPtr<mozilla::gfx::FileHandleWrapper>&& aFd,
695 const mozilla::webgpu::ffi::WGPUDMABufInfo& aDMABufInfo, int aWidth,
696 int aHeight) {
697 LOGDMABUF(("DMABufSurfaceRGBA::Create() UID %d size %d x %d\n", mUID, mWidth,
698 mHeight));
700 mWidth = aWidth;
701 mHeight = aHeight;
702 mBufferModifiers[0] = aDMABufInfo.modifier;
704 // TODO: Read Vulkan modifiers from DMABufFormats?
705 mFOURCCFormat = GBM_FORMAT_ARGB8888;
706 RefPtr<DRMFormat> format = GetDMABufDevice()->GetDRMFormat(mFOURCCFormat);
707 if (!format) {
708 return false;
710 mBufferPlaneCount = aDMABufInfo.plane_count;
712 RefPtr<gfx::FileHandleWrapper> fd = std::move(aFd);
714 for (uint32_t i = 0; i < aDMABufInfo.plane_count; i++) {
715 mDmabufFds[i] = fd;
716 mStrides[i] = aDMABufInfo.strides[i];
717 mOffsets[i] = aDMABufInfo.offsets[i];
720 LOGDMABUF((" imported size %d x %d format %x planes %d modifiers %" PRIx64,
721 mWidth, mHeight, mFOURCCFormat, mBufferPlaneCount,
722 mBufferModifiers[0]));
723 return true;
726 bool DMABufSurfaceRGBA::ImportSurfaceDescriptor(
727 const SurfaceDescriptor& aDesc) {
728 const SurfaceDescriptorDMABuf& desc = aDesc.get_SurfaceDescriptorDMABuf();
730 mFOURCCFormat = desc.fourccFormat();
731 mWidth = desc.width()[0];
732 mHeight = desc.height()[0];
733 mBufferPlaneCount = desc.fds().Length();
734 mGbmBufferFlags = desc.flags();
735 MOZ_RELEASE_ASSERT(mBufferPlaneCount <= DMABUF_BUFFER_PLANES);
736 mUID = desc.uid();
738 LOGDMABUF(
739 ("DMABufSurfaceRGBA::ImportSurfaceDescriptor() UID %d size %d x %d\n",
740 mUID, mWidth, mHeight));
742 for (int i = 0; i < mBufferPlaneCount; i++) {
743 mDmabufFds[i] = desc.fds()[i];
744 mStrides[i] = desc.strides()[i];
745 mOffsets[i] = desc.offsets()[i];
746 mDrmFormats[i] = desc.format()[i];
747 mBufferModifiers[i] = desc.modifier()[i];
750 if (desc.fence().Length() > 0) {
751 mSyncFd = desc.fence()[0];
754 if (desc.semaphoreFd()) {
755 mSemaphoreFd = desc.semaphoreFd();
758 if (desc.refCount().Length() > 0) {
759 GlobalRefCountImport(desc.refCount()[0].ClonePlatformHandle().release());
762 LOGDMABUF((" imported size %d x %d format %x planes %d", mWidth, mHeight,
763 mFOURCCFormat, mBufferPlaneCount));
764 return true;
767 bool DMABufSurfaceRGBA::Create(const SurfaceDescriptor& aDesc) {
768 return ImportSurfaceDescriptor(aDesc);
771 bool DMABufSurfaceRGBA::Serialize(
772 mozilla::layers::SurfaceDescriptor& aOutDescriptor) {
773 AutoTArray<uint32_t, DMABUF_BUFFER_PLANES> width;
774 AutoTArray<uint32_t, DMABUF_BUFFER_PLANES> height;
775 AutoTArray<uint32_t, DMABUF_BUFFER_PLANES> format;
776 AutoTArray<NotNull<RefPtr<gfx::FileHandleWrapper>>, DMABUF_BUFFER_PLANES> fds;
777 AutoTArray<uint32_t, DMABUF_BUFFER_PLANES> strides;
778 AutoTArray<uint32_t, DMABUF_BUFFER_PLANES> offsets;
779 AutoTArray<uintptr_t, DMABUF_BUFFER_PLANES> images;
780 AutoTArray<uint64_t, DMABUF_BUFFER_PLANES> modifiers;
781 AutoTArray<NotNull<RefPtr<gfx::FileHandleWrapper>>, 1> fenceFDs;
782 AutoTArray<ipc::FileDescriptor, 1> refCountFDs;
784 LOGDMABUF(("DMABufSurfaceRGBA::Serialize() UID %d\n", mUID));
786 MutexAutoLock lockFD(mSurfaceLock);
787 if (!OpenFileDescriptors(lockFD)) {
788 return false;
791 width.AppendElement(mWidth);
792 height.AppendElement(mHeight);
793 for (int i = 0; i < mBufferPlaneCount; i++) {
794 fds.AppendElement(WrapNotNull(mDmabufFds[i]));
795 strides.AppendElement(mStrides[i]);
796 offsets.AppendElement(mOffsets[i]);
797 format.AppendElement(mDrmFormats[i]);
798 modifiers.AppendElement(mBufferModifiers[i]);
801 CloseFileDescriptors(lockFD);
803 if (mSync && mSyncFd) {
804 fenceFDs.AppendElement(WrapNotNull(mSyncFd));
807 if (mGlobalRefCountFd) {
808 refCountFDs.AppendElement(ipc::FileDescriptor(GlobalRefCountExport()));
811 aOutDescriptor = SurfaceDescriptorDMABuf(
812 mSurfaceType, mFOURCCFormat, modifiers, mGbmBufferFlags, fds, width,
813 height, width, height, format, strides, offsets, GetYUVColorSpace(),
814 mColorRange, mozilla::gfx::ColorSpace2::UNKNOWN,
815 mozilla::gfx::TransferFunction::Default, fenceFDs, mUID, refCountFDs,
816 /* semaphoreFd */ nullptr);
817 return true;
820 bool DMABufSurfaceRGBA::CreateTexture(GLContext* aGLContext, int aPlane) {
821 LOGDMABUF(("DMABufSurfaceRGBA::CreateTexture() UID %d\n", mUID));
822 MOZ_ASSERT(!mEGLImage && !mTexture, "EGLImage is already created!");
824 nsTArray<EGLint> attribs;
825 attribs.AppendElement(LOCAL_EGL_WIDTH);
826 attribs.AppendElement(mWidth);
827 attribs.AppendElement(LOCAL_EGL_HEIGHT);
828 attribs.AppendElement(mHeight);
829 attribs.AppendElement(LOCAL_EGL_LINUX_DRM_FOURCC_EXT);
830 attribs.AppendElement(mFOURCCFormat);
831 #define ADD_PLANE_ATTRIBS(plane_idx) \
833 attribs.AppendElement(LOCAL_EGL_DMA_BUF_PLANE##plane_idx##_FD_EXT); \
834 attribs.AppendElement(mDmabufFds[plane_idx]->GetHandle()); \
835 attribs.AppendElement(LOCAL_EGL_DMA_BUF_PLANE##plane_idx##_OFFSET_EXT); \
836 attribs.AppendElement((int)mOffsets[plane_idx]); \
837 attribs.AppendElement(LOCAL_EGL_DMA_BUF_PLANE##plane_idx##_PITCH_EXT); \
838 attribs.AppendElement((int)mStrides[plane_idx]); \
839 if (mBufferModifiers[0] != DRM_FORMAT_MOD_INVALID) { \
840 attribs.AppendElement( \
841 LOCAL_EGL_DMA_BUF_PLANE##plane_idx##_MODIFIER_LO_EXT); \
842 attribs.AppendElement(mBufferModifiers[0] & 0xFFFFFFFF); \
843 attribs.AppendElement( \
844 LOCAL_EGL_DMA_BUF_PLANE##plane_idx##_MODIFIER_HI_EXT); \
845 attribs.AppendElement(mBufferModifiers[0] >> 32); \
849 MutexAutoLock lockFD(mSurfaceLock);
850 if (!OpenFileDescriptors(lockFD)) {
851 return false;
853 ADD_PLANE_ATTRIBS(0);
854 if (mBufferPlaneCount > 1) ADD_PLANE_ATTRIBS(1);
855 if (mBufferPlaneCount > 2) ADD_PLANE_ATTRIBS(2);
856 if (mBufferPlaneCount > 3) ADD_PLANE_ATTRIBS(3);
857 #undef ADD_PLANE_ATTRIBS
858 attribs.AppendElement(LOCAL_EGL_NONE);
860 if (!aGLContext) return false;
862 if (!aGLContext->MakeCurrent()) {
863 LOGDMABUF(
864 ("DMABufSurfaceRGBA::CreateTexture(): failed to make GL context "
865 "current"));
866 return false;
869 if (!aGLContext->IsExtensionSupported(gl::GLContext::OES_EGL_image)) {
870 LOGDMABUF(("DMABufSurfaceRGBA::CreateTexture(): no OES_EGL_image."));
871 return false;
874 const auto& gle = gl::GLContextEGL::Cast(aGLContext);
875 const auto& egl = gle->mEgl;
876 mEGLImage =
877 egl->fCreateImage(LOCAL_EGL_NO_CONTEXT, LOCAL_EGL_LINUX_DMA_BUF_EXT,
878 nullptr, attribs.Elements());
880 CloseFileDescriptors(lockFD);
882 if (mEGLImage == LOCAL_EGL_NO_IMAGE) {
883 LOGDMABUF(("EGLImageKHR creation failed"));
884 return false;
887 aGLContext->fGenTextures(1, &mTexture);
888 const ScopedBindTexture savedTex(aGLContext, mTexture);
889 aGLContext->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_S,
890 LOCAL_GL_CLAMP_TO_EDGE);
891 aGLContext->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_T,
892 LOCAL_GL_CLAMP_TO_EDGE);
893 aGLContext->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MAG_FILTER,
894 LOCAL_GL_LINEAR);
895 aGLContext->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MIN_FILTER,
896 LOCAL_GL_LINEAR);
897 aGLContext->fEGLImageTargetTexture2D(LOCAL_GL_TEXTURE_2D, mEGLImage);
898 mGL = aGLContext;
900 return true;
903 void DMABufSurfaceRGBA::ReleaseTextures() {
904 LOGDMABUF(("DMABufSurfaceRGBA::ReleaseTextures() UID %d\n", mUID));
905 FenceDelete();
907 if (!mTexture && !mEGLImage) {
908 return;
911 if (!mGL) {
912 #ifdef NIGHTLY_BUILD
913 MOZ_DIAGNOSTIC_ASSERT(mGL, "Missing GL context!");
914 #else
915 NS_WARNING(
916 "DMABufSurfaceRGBA::ReleaseTextures(): Missing GL context! We're "
917 "leaking textures!");
918 return;
919 #endif
922 const auto& gle = gl::GLContextEGL::Cast(mGL);
923 const auto& egl = gle->mEgl;
925 if (mTexture && mGL->MakeCurrent()) {
926 mGL->fDeleteTextures(1, &mTexture);
927 mTexture = 0;
930 if (mEGLImage != LOCAL_EGL_NO_IMAGE) {
931 egl->fDestroyImage(mEGLImage);
932 mEGLImage = LOCAL_EGL_NO_IMAGE;
934 mGL = nullptr;
937 void DMABufSurfaceRGBA::ReleaseSurface() {
938 MOZ_ASSERT(!IsMapped(), "We can't release mapped buffer!");
940 ReleaseTextures();
941 ReleaseDMABuf();
944 #ifdef MOZ_WAYLAND
945 wl_buffer* DMABufSurfaceRGBA::CreateWlBuffer() {
946 nsWaylandDisplay* waylandDisplay = widget::WaylandDisplayGet();
947 auto* dmabuf = waylandDisplay->GetDmabuf();
948 if (!dmabuf) {
949 gfxCriticalNoteOnce
950 << "DMABufSurfaceRGBA::CreateWlBuffer(): Missing DMABuf support!";
951 return nullptr;
954 MutexAutoLock lockFD(mSurfaceLock);
955 LOGDMABUF(
956 ("DMABufSurfaceRGBA::CreateWlBuffer() UID %d format %s size [%d x %d]",
957 mUID, GetSurfaceTypeName(), GetWidth(), GetHeight()));
959 if (!OpenFileDescriptors(lockFD)) {
960 LOGDMABUF((" failed to open dmabuf fd"));
961 return nullptr;
964 struct zwp_linux_buffer_params_v1* params =
965 zwp_linux_dmabuf_v1_create_params(dmabuf);
967 LOGDMABUF((" layer [0] modifier %" PRIx64, mBufferModifiers[0]));
968 for (int i = 0; i < mBufferPlaneCount; i++) {
969 zwp_linux_buffer_params_v1_add(
970 params, mDmabufFds[i]->GetHandle(), i, mOffsets[i], mStrides[i],
971 mBufferModifiers[0] >> 32, mBufferModifiers[0] & 0xffffffff);
974 LOGDMABUF(
975 (" zwp_linux_buffer_params_v1_create_immed() [%d x %d], fourcc [%x]",
976 GetWidth(), GetHeight(), GetFOURCCFormat()));
977 wl_buffer* buffer = zwp_linux_buffer_params_v1_create_immed(
978 params, GetWidth(), GetHeight(), GetFOURCCFormat(), 0);
979 if (!buffer) {
980 LOGDMABUF(
981 (" zwp_linux_buffer_params_v1_create_immed(): failed to create "
982 "wl_buffer!"));
983 } else {
984 LOGDMABUF((" created wl_buffer [%p]", buffer));
986 zwp_linux_buffer_params_v1_destroy(params);
988 CloseFileDescriptors(lockFD);
989 return buffer;
991 #endif
993 // We should synchronize DMA Buffer object access from CPU to avoid potential
994 // cache incoherency and data loss.
995 // See
996 // https://01.org/linuxgraphics/gfx-docs/drm/driver-api/dma-buf.html#cpu-access-to-dma-buffer-objects
997 struct dma_buf_sync {
998 uint64_t flags;
1000 #define DMA_BUF_SYNC_READ (1 << 0)
1001 #define DMA_BUF_SYNC_WRITE (2 << 0)
1002 #define DMA_BUF_SYNC_START (0 << 2)
1003 #define DMA_BUF_SYNC_END (1 << 2)
1004 #define DMA_BUF_BASE 'b'
1005 #define DMA_BUF_IOCTL_SYNC _IOW(DMA_BUF_BASE, 0, struct dma_buf_sync)
1007 static void SyncDmaBuf(int aFd, uint64_t aFlags) {
1008 struct dma_buf_sync sync = {0};
1010 sync.flags = aFlags | DMA_BUF_SYNC_READ | DMA_BUF_SYNC_WRITE;
1011 while (true) {
1012 int ret;
1013 ret = ioctl(aFd, DMA_BUF_IOCTL_SYNC, &sync);
1014 if (ret == -1 && errno == EINTR) {
1015 continue;
1016 } else if (ret == -1) {
1017 LOGDMABUF(
1018 ("Failed to synchronize DMA buffer: %s FD %d", strerror(errno), aFd));
1019 break;
1020 } else {
1021 break;
1026 void* DMABufSurface::MapInternal(uint32_t aX, uint32_t aY, uint32_t aWidth,
1027 uint32_t aHeight, uint32_t* aStride,
1028 int aGbmFlags, int aPlane) {
1029 NS_ASSERTION(!IsMapped(aPlane), "Already mapped!");
1030 if (!mGbmBufferObject[aPlane]) {
1031 NS_WARNING("We can't map DMABufSurfaceRGBA without mGbmBufferObject");
1032 return nullptr;
1035 LOGDMABUF(
1036 ("DMABufSurfaceRGBA::MapInternal() UID %d plane %d size %d x %d -> %d x "
1037 "%d\n",
1038 mUID, aPlane, aX, aY, aWidth, aHeight));
1040 mMappedRegionStride[aPlane] = 0;
1041 mMappedRegionData[aPlane] = nullptr;
1042 mMappedRegion[aPlane] =
1043 GbmLib::Map(mGbmBufferObject[aPlane], aX, aY, aWidth, aHeight, aGbmFlags,
1044 &mMappedRegionStride[aPlane], &mMappedRegionData[aPlane]);
1045 if (!mMappedRegion[aPlane]) {
1046 LOGDMABUF((" Surface mapping failed: %s", strerror(errno)));
1047 return nullptr;
1049 if (aStride) {
1050 *aStride = mMappedRegionStride[aPlane];
1053 MutexAutoLock lockFD(mSurfaceLock);
1054 if (OpenFileDescriptorForPlane(lockFD, aPlane)) {
1055 SyncDmaBuf(mDmabufFds[aPlane]->GetHandle(), DMA_BUF_SYNC_START);
1056 CloseFileDescriptorForPlane(lockFD, aPlane);
1059 return mMappedRegion[aPlane];
1062 void* DMABufSurfaceRGBA::MapReadOnly(uint32_t aX, uint32_t aY, uint32_t aWidth,
1063 uint32_t aHeight, uint32_t* aStride) {
1064 return MapInternal(aX, aY, aWidth, aHeight, aStride, GBM_BO_TRANSFER_READ);
1067 void* DMABufSurfaceRGBA::MapReadOnly(uint32_t* aStride) {
1068 return MapInternal(0, 0, mWidth, mHeight, aStride, GBM_BO_TRANSFER_READ);
1071 void* DMABufSurfaceRGBA::Map(uint32_t aX, uint32_t aY, uint32_t aWidth,
1072 uint32_t aHeight, uint32_t* aStride) {
1073 return MapInternal(aX, aY, aWidth, aHeight, aStride,
1074 GBM_BO_TRANSFER_READ_WRITE);
1077 void* DMABufSurfaceRGBA::Map(uint32_t* aStride) {
1078 return MapInternal(0, 0, mWidth, mHeight, aStride,
1079 GBM_BO_TRANSFER_READ_WRITE);
1082 void DMABufSurface::Unmap(int aPlane) {
1083 if (mMappedRegion[aPlane]) {
1084 LOGDMABUF(("DMABufSurface::Unmap() UID %d plane %d\n", mUID, aPlane));
1085 MutexAutoLock lockFD(mSurfaceLock);
1086 if (OpenFileDescriptorForPlane(lockFD, aPlane)) {
1087 SyncDmaBuf(mDmabufFds[aPlane]->GetHandle(), DMA_BUF_SYNC_END);
1088 CloseFileDescriptorForPlane(lockFD, aPlane);
1090 GbmLib::Unmap(mGbmBufferObject[aPlane], mMappedRegionData[aPlane]);
1091 mMappedRegion[aPlane] = nullptr;
1092 mMappedRegionData[aPlane] = nullptr;
1093 mMappedRegionStride[aPlane] = 0;
1097 nsresult DMABufSurface::BuildSurfaceDescriptorBuffer(
1098 SurfaceDescriptorBuffer& aSdBuffer, Image::BuildSdbFlags aFlags,
1099 const std::function<MemoryOrShmem(uint32_t)>& aAllocate) {
1100 return NS_ERROR_NOT_IMPLEMENTED;
1103 #ifdef DEBUG
1104 void DMABufSurfaceRGBA::DumpToFile(const char* pFile) {
1105 uint32_t stride;
1107 if (!MapReadOnly(&stride)) {
1108 return;
1110 cairo_surface_t* surface = nullptr;
1112 auto unmap = MakeScopeExit([&] {
1113 if (surface) {
1114 cairo_surface_destroy(surface);
1116 Unmap();
1119 surface = cairo_image_surface_create_for_data(
1120 (unsigned char*)mMappedRegion[0], CAIRO_FORMAT_ARGB32, mWidth, mHeight,
1121 stride);
1122 if (cairo_surface_status(surface) == CAIRO_STATUS_SUCCESS) {
1123 cairo_surface_write_to_png(surface, pFile);
1126 #endif
1128 #if 0
1129 // Copy from source surface by GL
1130 # include "GLBlitHelper.h"
1132 bool DMABufSurfaceRGBA::CopyFrom(class DMABufSurface* aSourceSurface,
1133 GLContext* aGLContext) {
1134 MOZ_ASSERT(aSourceSurface->GetTexture());
1135 MOZ_ASSERT(GetTexture());
1137 gfx::IntSize size(GetWidth(), GetHeight());
1138 aGLContext->BlitHelper()->BlitTextureToTexture(aSourceSurface->GetTexture(),
1139 GetTexture(), size, size);
1140 return true;
1142 #endif
1144 // TODO - Clear the surface by EGL
1145 void DMABufSurfaceRGBA::Clear() {
1146 uint32_t destStride;
1147 void* destData = Map(&destStride);
1148 memset(destData, 0, GetHeight() * destStride);
1149 Unmap();
1152 bool DMABufSurfaceRGBA::HasAlpha() {
1153 return mFOURCCFormat == GBM_FORMAT_ARGB8888;
1156 gfx::SurfaceFormat DMABufSurfaceRGBA::GetFormat() {
1157 return HasAlpha() ? gfx::SurfaceFormat::B8G8R8A8
1158 : gfx::SurfaceFormat::B8G8R8X8;
1161 // GL uses swapped R and B components so report accordingly.
1162 gfx::SurfaceFormat DMABufSurfaceRGBA::GetFormatGL() {
1163 return HasAlpha() ? gfx::SurfaceFormat::R8G8B8A8
1164 : gfx::SurfaceFormat::R8G8B8X8;
1167 already_AddRefed<DMABufSurfaceRGBA> DMABufSurfaceRGBA::CreateDMABufSurface(
1168 int aWidth, int aHeight, int aDMABufSurfaceFlags) {
1169 RefPtr<DMABufSurfaceRGBA> surf = new DMABufSurfaceRGBA();
1170 if (!surf->Create(aWidth, aHeight, aDMABufSurfaceFlags)) {
1171 return nullptr;
1173 return surf.forget();
1176 already_AddRefed<DMABufSurfaceRGBA> DMABufSurfaceRGBA::CreateDMABufSurface(
1177 int aWidth, int aHeight, RefPtr<DRMFormat> aFormat,
1178 int aDMABufSurfaceFlags) {
1179 RefPtr<DMABufSurfaceRGBA> surf = new DMABufSurfaceRGBA();
1180 if (!surf->Create(aWidth, aHeight, aFormat, aDMABufSurfaceFlags)) {
1181 return nullptr;
1183 return surf.forget();
1186 already_AddRefed<DMABufSurface> DMABufSurfaceRGBA::CreateDMABufSurface(
1187 mozilla::gl::GLContext* aGLContext, const EGLImageKHR aEGLImage, int aWidth,
1188 int aHeight) {
1189 RefPtr<DMABufSurfaceRGBA> surf = new DMABufSurfaceRGBA();
1190 if (!surf->Create(aGLContext, aEGLImage, aWidth, aHeight)) {
1191 return nullptr;
1193 return surf.forget();
1196 already_AddRefed<DMABufSurface> DMABufSurfaceRGBA::CreateDMABufSurface(
1197 RefPtr<mozilla::gfx::FileHandleWrapper>&& aFd,
1198 const mozilla::webgpu::ffi::WGPUDMABufInfo& aDMABufInfo, int aWidth,
1199 int aHeight) {
1200 RefPtr<DMABufSurfaceRGBA> surf = new DMABufSurfaceRGBA();
1201 if (!surf->Create(std::move(aFd), aDMABufInfo, aWidth, aHeight)) {
1202 return nullptr;
1204 return surf.forget();
1207 already_AddRefed<DMABufSurfaceYUV> DMABufSurfaceYUV::CreateYUVSurface(
1208 const VADRMPRIMESurfaceDescriptor& aDesc, int aWidth, int aHeight) {
1209 RefPtr<DMABufSurfaceYUV> surf = new DMABufSurfaceYUV();
1210 LOGDMABUF(("DMABufSurfaceYUV::CreateYUVSurface() UID %d from desc\n",
1211 surf->GetUID()));
1212 if (!surf->UpdateYUVData(aDesc, aWidth, aHeight, /* aCopy */ false)) {
1213 return nullptr;
1215 return surf.forget();
1218 already_AddRefed<DMABufSurfaceYUV> DMABufSurfaceYUV::CopyYUVSurface(
1219 const VADRMPRIMESurfaceDescriptor& aDesc, int aWidth, int aHeight) {
1220 RefPtr<DMABufSurfaceYUV> surf = new DMABufSurfaceYUV();
1221 LOGDMABUF(("DMABufSurfaceYUV::CreateYUVSurfaceCopy() UID %d from desc\n",
1222 surf->GetUID()));
1223 if (!surf->UpdateYUVData(aDesc, aWidth, aHeight, /* aCopy */ true)) {
1224 return nullptr;
1226 return surf.forget();
1229 already_AddRefed<DMABufSurfaceYUV> DMABufSurfaceYUV::CreateYUVSurface(
1230 int aWidth, int aHeight, void** aPixelData, int* aLineSizes) {
1231 RefPtr<DMABufSurfaceYUV> surf = new DMABufSurfaceYUV();
1232 LOGDMABUF(("DMABufSurfaceYUV::CreateYUVSurface() UID %d %d x %d\n",
1233 surf->GetUID(), aWidth, aHeight));
1234 if (!surf->Create(aWidth, aHeight, aPixelData, aLineSizes)) {
1235 return nullptr;
1237 return surf.forget();
1240 DMABufSurfaceYUV::DMABufSurfaceYUV()
1241 : DMABufSurface(SURFACE_YUV),
1242 mWidth(),
1243 mHeight(),
1244 mWidthAligned(),
1245 mHeightAligned(),
1246 mTexture() {
1247 for (int i = 0; i < DMABUF_BUFFER_PLANES; i++) {
1248 mEGLImage[i] = LOCAL_EGL_NO_IMAGE;
1252 DMABufSurfaceYUV::~DMABufSurfaceYUV() { ReleaseSurface(); }
1254 bool DMABufSurfaceYUV::OpenFileDescriptorForPlane(
1255 const MutexAutoLock& aProofOfLock, int aPlane) {
1256 // The fd is already opened, no need to reopen.
1257 // This can happen when we import dmabuf surface from VA-API decoder,
1258 // mGbmBufferObject is null and we don't close
1259 // file descriptors for surface as they are our only reference to it.
1260 if (mDmabufFds[aPlane]) {
1261 return true;
1264 if (mGbmBufferObject[aPlane] == nullptr) {
1265 LOGDMABUF(
1266 ("DMABufSurfaceYUV::OpenFileDescriptorForPlane: Missing "
1267 "mGbmBufferObject object!"));
1268 return false;
1271 auto rawFd = GbmLib::GetFd(mGbmBufferObject[aPlane]);
1272 if (rawFd < 0) {
1273 CloseFileDescriptors(aProofOfLock);
1274 return false;
1276 mDmabufFds[aPlane] = new gfx::FileHandleWrapper(UniqueFileHandle(rawFd));
1278 return true;
1281 void DMABufSurfaceYUV::CloseFileDescriptorForPlane(
1282 const MutexAutoLock& aProofOfLock, int aPlane, bool aForceClose = false) {
1283 if ((aForceClose || mGbmBufferObject[aPlane]) && mDmabufFds[aPlane]) {
1284 mDmabufFds[aPlane] = nullptr;
1288 bool DMABufSurfaceYUV::ImportPRIMESurfaceDescriptor(
1289 const VADRMPRIMESurfaceDescriptor& aDesc, int aWidth, int aHeight) {
1290 LOGDMABUF(
1291 ("DMABufSurfaceYUV::ImportPRIMESurfaceDescriptor() UID %d FOURCC %x",
1292 mUID, aDesc.fourcc));
1293 // Already exists?
1294 MOZ_DIAGNOSTIC_ASSERT(!mDmabufFds[0]);
1296 if (aDesc.num_layers > DMABUF_BUFFER_PLANES ||
1297 aDesc.num_objects > DMABUF_BUFFER_PLANES) {
1298 LOGDMABUF((" Can't import, wrong layers/objects number (%d, %d)",
1299 aDesc.num_layers, aDesc.num_objects));
1300 return false;
1302 mSurfaceType = SURFACE_YUV;
1303 mFOURCCFormat = aDesc.fourcc;
1304 mBufferPlaneCount = aDesc.num_layers;
1306 for (unsigned int i = 0; i < aDesc.num_layers; i++) {
1307 // All supported formats have 4:2:0 chroma sub-sampling.
1308 unsigned int subsample = i == 0 ? 0 : 1;
1310 unsigned int object = aDesc.layers[i].object_index[0];
1311 mBufferModifiers[i] = aDesc.objects[object].drm_format_modifier;
1312 mDrmFormats[i] = aDesc.layers[i].drm_format;
1313 mOffsets[i] = aDesc.layers[i].offset[0];
1314 mStrides[i] = aDesc.layers[i].pitch[0];
1315 mWidthAligned[i] = aDesc.width >> subsample;
1316 mHeightAligned[i] = aDesc.height >> subsample;
1317 mWidth[i] = aWidth >> subsample;
1318 mHeight[i] = aHeight >> subsample;
1319 LOGDMABUF((" plane %d size %d x %d format %x", i, mWidth[i], mHeight[i],
1320 mDrmFormats[i]));
1322 return true;
1325 bool DMABufSurfaceYUV::MoveYUVDataImpl(const VADRMPRIMESurfaceDescriptor& aDesc,
1326 int aWidth, int aHeight) {
1327 if (!ImportPRIMESurfaceDescriptor(aDesc, aWidth, aHeight)) {
1328 return false;
1330 for (unsigned int i = 0; i < aDesc.num_layers; i++) {
1331 unsigned int object = aDesc.layers[i].object_index[0];
1332 // Keep VADRMPRIMESurfaceDescriptor untouched and dup() dmabuf
1333 // file descriptors.
1334 auto rawFd = dup(aDesc.objects[object].fd);
1335 mDmabufFds[i] = new gfx::FileHandleWrapper(UniqueFileHandle(rawFd));
1337 return true;
1340 void DMABufSurfaceYUV::ReleaseVADRMPRIMESurfaceDescriptor(
1341 VADRMPRIMESurfaceDescriptor& aDesc) {
1342 for (unsigned int i = 0; i < aDesc.num_layers; i++) {
1343 unsigned int object = aDesc.layers[i].object_index[0];
1344 if (aDesc.objects[object].fd != -1) {
1345 close(aDesc.objects[object].fd);
1346 aDesc.objects[object].fd = -1;
1351 bool DMABufSurfaceYUV::CreateYUVPlane(int aPlane) {
1352 LOGDMABUF(("DMABufSurfaceYUV::CreateYUVPlane() UID %d size %d x %d", mUID,
1353 mWidth[aPlane], mHeight[aPlane]));
1355 if (!GetDMABufDevice()->GetGbmDevice()) {
1356 LOGDMABUF((" Missing GbmDevice!"));
1357 return false;
1360 MOZ_DIAGNOSTIC_ASSERT(mGbmBufferObject[aPlane] == nullptr);
1361 bool useModifiers = (mBufferModifiers[aPlane] != DRM_FORMAT_MOD_INVALID);
1362 if (useModifiers) {
1363 LOGDMABUF((" Creating with modifiers"));
1364 mGbmBufferObject[aPlane] = GbmLib::CreateWithModifiers(
1365 GetDMABufDevice()->GetGbmDevice(), mWidth[aPlane], mHeight[aPlane],
1366 mDrmFormats[aPlane], mBufferModifiers + aPlane, 1);
1368 if (!mGbmBufferObject[aPlane]) {
1369 LOGDMABUF((" Creating without modifiers"));
1370 mGbmBufferObject[aPlane] = GbmLib::Create(
1371 GetDMABufDevice()->GetGbmDevice(), mWidth[aPlane], mHeight[aPlane],
1372 mDrmFormats[aPlane], GBM_BO_USE_RENDERING);
1373 mBufferModifiers[aPlane] = DRM_FORMAT_MOD_INVALID;
1375 if (!mGbmBufferObject[aPlane]) {
1376 LOGDMABUF((" Failed to create GbmBufferObject: %s", strerror(errno)));
1377 return false;
1380 mStrides[aPlane] = GbmLib::GetStride(mGbmBufferObject[aPlane]);
1381 mOffsets[aPlane] = GbmLib::GetOffset(mGbmBufferObject[aPlane], 0);
1382 mWidthAligned[aPlane] = mWidth[aPlane];
1383 mHeightAligned[aPlane] = mHeight[aPlane];
1384 return true;
1387 bool DMABufSurfaceYUV::CopyYUVDataImpl(const VADRMPRIMESurfaceDescriptor& aDesc,
1388 int aWidth, int aHeight) {
1389 RefPtr<DMABufSurfaceYUV> tmpSurf = CreateYUVSurface(aDesc, aWidth, aHeight);
1390 if (!tmpSurf) {
1391 return false;
1394 if (!ImportPRIMESurfaceDescriptor(aDesc, aWidth, aHeight)) {
1395 return false;
1398 StaticMutexAutoLock lock(sSnapshotContextMutex);
1399 RefPtr<GLContext> context = ClaimSnapshotGLContext();
1400 auto releaseTextures = MakeScopeExit([&] {
1401 tmpSurf->ReleaseTextures();
1402 ReleaseTextures();
1403 ReturnSnapshotGLContext(context);
1406 for (int i = 0; i < mBufferPlaneCount; i++) {
1407 if (!tmpSurf->CreateTexture(context, i)) {
1408 return false;
1410 if (!CreateYUVPlane(i) || !CreateTexture(context, i)) {
1411 return false;
1413 gfx::IntSize size(GetWidth(i), GetHeight(i));
1414 context->BlitHelper()->BlitTextureToTexture(
1415 tmpSurf->GetTexture(i), GetTexture(i), size, size, LOCAL_GL_TEXTURE_2D,
1416 LOCAL_GL_TEXTURE_2D);
1418 return true;
1421 bool DMABufSurfaceYUV::UpdateYUVData(const VADRMPRIMESurfaceDescriptor& aDesc,
1422 int aWidth, int aHeight, bool aCopy) {
1423 LOGDMABUF(("DMABufSurfaceYUV::UpdateYUVData() UID %d copy %d", mUID, aCopy));
1424 return aCopy ? CopyYUVDataImpl(aDesc, aWidth, aHeight)
1425 : MoveYUVDataImpl(aDesc, aWidth, aHeight);
1428 bool DMABufSurfaceYUV::CreateLinearYUVPlane(int aPlane, int aWidth, int aHeight,
1429 int aDrmFormat) {
1430 LOGDMABUF(("DMABufSurfaceYUV::CreateLinearYUVPlane() UID %d size %d x %d",
1431 mUID, aWidth, aHeight));
1433 if (!GetDMABufDevice()->GetGbmDevice()) {
1434 LOGDMABUF((" Missing GbmDevice!"));
1435 return false;
1438 mWidth[aPlane] = aWidth;
1439 mHeight[aPlane] = aHeight;
1440 mDrmFormats[aPlane] = aDrmFormat;
1442 mGbmBufferObject[aPlane] =
1443 GbmLib::Create(GetDMABufDevice()->GetGbmDevice(), aWidth, aHeight,
1444 aDrmFormat, GBM_BO_USE_LINEAR);
1445 if (!mGbmBufferObject[aPlane]) {
1446 LOGDMABUF((" Failed to create GbmBufferObject: %s", strerror(errno)));
1447 return false;
1450 mStrides[aPlane] = GbmLib::GetStride(mGbmBufferObject[aPlane]);
1451 mDmabufFds[aPlane] = nullptr;
1453 return true;
1456 void DMABufSurfaceYUV::UpdateYUVPlane(int aPlane, void* aPixelData,
1457 int aLineSize) {
1458 LOGDMABUF(
1459 ("DMABufSurfaceYUV::UpdateYUVPlane() UID %d plane %d", mUID, aPlane));
1460 if (aLineSize == mWidth[aPlane] &&
1461 (int)mMappedRegionStride[aPlane] == mWidth[aPlane]) {
1462 memcpy(mMappedRegion[aPlane], aPixelData, aLineSize * mHeight[aPlane]);
1463 } else {
1464 char* src = (char*)aPixelData;
1465 char* dest = (char*)mMappedRegion[aPlane];
1466 for (int i = 0; i < mHeight[aPlane]; i++) {
1467 memcpy(dest, src, mWidth[aPlane]);
1468 src += aLineSize;
1469 dest += mMappedRegionStride[aPlane];
1474 bool DMABufSurfaceYUV::UpdateYUVData(void** aPixelData, int* aLineSizes) {
1475 LOGDMABUF(("DMABufSurfaceYUV::UpdateYUVData() UID %d", mUID));
1477 if (mSurfaceType != SURFACE_YUV || mBufferPlaneCount != 3 ||
1478 mFOURCCFormat != VA_FOURCC_YV12) {
1479 LOGDMABUF((" UpdateYUVData can upload YUV420 surface type only!"));
1480 return false;
1483 auto unmapBuffers = MakeScopeExit([&] {
1484 Unmap(0);
1485 Unmap(1);
1486 Unmap(2);
1489 // Map planes
1490 for (int i = 0; i < mBufferPlaneCount; i++) {
1491 MapInternal(0, 0, mWidth[i], mHeight[i], nullptr, GBM_BO_TRANSFER_WRITE, i);
1492 if (!mMappedRegion[i]) {
1493 LOGDMABUF((" DMABufSurfaceYUV plane can't be mapped!"));
1494 return false;
1496 if ((int)mMappedRegionStride[i] < mWidth[i]) {
1497 LOGDMABUF((" DMABufSurfaceYUV plane size stride does not match!"));
1498 return false;
1502 // Copy planes
1503 for (int i = 0; i < mBufferPlaneCount; i++) {
1504 UpdateYUVPlane(i, aPixelData[i], aLineSizes[i]);
1507 return true;
1510 bool DMABufSurfaceYUV::Create(int aWidth, int aHeight, void** aPixelData,
1511 int* aLineSizes) {
1512 LOGDMABUF(("DMABufSurfaceYUV::Create() UID %d size %d x %d", mUID, aWidth,
1513 aHeight));
1515 mSurfaceType = SURFACE_YUV;
1516 mFOURCCFormat = VA_FOURCC_YV12;
1517 mBufferPlaneCount = 3;
1519 if (!CreateLinearYUVPlane(0, aWidth, aHeight, GBM_FORMAT_R8)) {
1520 return false;
1522 if (!CreateLinearYUVPlane(1, aWidth >> 1, aHeight >> 1, GBM_FORMAT_R8)) {
1523 return false;
1525 if (!CreateLinearYUVPlane(2, aWidth >> 1, aHeight >> 1, GBM_FORMAT_R8)) {
1526 return false;
1528 if (!aPixelData || !aLineSizes) {
1529 return true;
1531 return UpdateYUVData(aPixelData, aLineSizes);
1534 bool DMABufSurfaceYUV::Create(const SurfaceDescriptor& aDesc) {
1535 return ImportSurfaceDescriptor(aDesc);
1538 bool DMABufSurfaceYUV::ImportSurfaceDescriptor(
1539 const SurfaceDescriptorDMABuf& aDesc) {
1540 mBufferPlaneCount = aDesc.fds().Length();
1541 mSurfaceType = SURFACE_YUV;
1542 mFOURCCFormat = aDesc.fourccFormat();
1543 mColorSpace = aDesc.yUVColorSpace();
1544 mColorRange = aDesc.colorRange();
1545 mColorPrimaries = aDesc.colorPrimaries();
1546 mTransferFunction = aDesc.transferFunction();
1547 mUID = aDesc.uid();
1549 LOGDMABUF(("DMABufSurfaceYUV::ImportSurfaceDescriptor() UID %d", mUID));
1551 MOZ_RELEASE_ASSERT(mBufferPlaneCount <= DMABUF_BUFFER_PLANES);
1552 for (int i = 0; i < mBufferPlaneCount; i++) {
1553 mDmabufFds[i] = aDesc.fds()[i];
1554 mWidth[i] = aDesc.width()[i];
1555 mHeight[i] = aDesc.height()[i];
1556 mWidthAligned[i] = aDesc.widthAligned()[i];
1557 mHeightAligned[i] = aDesc.heightAligned()[i];
1558 mDrmFormats[i] = aDesc.format()[i];
1559 mStrides[i] = aDesc.strides()[i];
1560 mOffsets[i] = aDesc.offsets()[i];
1561 mBufferModifiers[i] = aDesc.modifier()[i];
1562 LOGDMABUF((" plane %d fd %d size %d x %d format %x", i,
1563 mDmabufFds[i]->GetHandle(), mWidth[i], mHeight[i],
1564 mDrmFormats[i]));
1567 if (aDesc.fence().Length() > 0) {
1568 mSyncFd = aDesc.fence()[0];
1571 if (aDesc.refCount().Length() > 0) {
1572 GlobalRefCountImport(aDesc.refCount()[0].ClonePlatformHandle().release());
1575 return true;
1578 bool DMABufSurfaceYUV::Serialize(
1579 mozilla::layers::SurfaceDescriptor& aOutDescriptor) {
1580 AutoTArray<uint32_t, DMABUF_BUFFER_PLANES> width;
1581 AutoTArray<uint32_t, DMABUF_BUFFER_PLANES> height;
1582 AutoTArray<uint32_t, DMABUF_BUFFER_PLANES> widthBytes;
1583 AutoTArray<uint32_t, DMABUF_BUFFER_PLANES> heightBytes;
1584 AutoTArray<uint32_t, DMABUF_BUFFER_PLANES> format;
1585 AutoTArray<NotNull<RefPtr<gfx::FileHandleWrapper>>, DMABUF_BUFFER_PLANES> fds;
1586 AutoTArray<uint32_t, DMABUF_BUFFER_PLANES> strides;
1587 AutoTArray<uint32_t, DMABUF_BUFFER_PLANES> offsets;
1588 AutoTArray<uint64_t, DMABUF_BUFFER_PLANES> modifiers;
1589 AutoTArray<NotNull<RefPtr<gfx::FileHandleWrapper>>, 1> fenceFDs;
1590 AutoTArray<ipc::FileDescriptor, 1> refCountFDs;
1592 LOGDMABUF(("DMABufSurfaceYUV::Serialize() UID %d", mUID));
1594 MutexAutoLock lockFD(mSurfaceLock);
1595 if (!OpenFileDescriptors(lockFD)) {
1596 return false;
1599 for (int i = 0; i < mBufferPlaneCount; i++) {
1600 width.AppendElement(mWidth[i]);
1601 height.AppendElement(mHeight[i]);
1602 widthBytes.AppendElement(mWidthAligned[i]);
1603 heightBytes.AppendElement(mHeightAligned[i]);
1604 format.AppendElement(mDrmFormats[i]);
1605 fds.AppendElement(WrapNotNull(mDmabufFds[i]));
1606 strides.AppendElement(mStrides[i]);
1607 offsets.AppendElement(mOffsets[i]);
1608 modifiers.AppendElement(mBufferModifiers[i]);
1611 CloseFileDescriptors(lockFD);
1613 if (mSync && mSyncFd) {
1614 fenceFDs.AppendElement(WrapNotNull(mSyncFd));
1617 if (mGlobalRefCountFd) {
1618 refCountFDs.AppendElement(ipc::FileDescriptor(GlobalRefCountExport()));
1621 aOutDescriptor = SurfaceDescriptorDMABuf(
1622 mSurfaceType, mFOURCCFormat, modifiers, 0, fds, width, height, widthBytes,
1623 heightBytes, format, strides, offsets, GetYUVColorSpace(), mColorRange,
1624 mColorPrimaries, mTransferFunction, fenceFDs, mUID, refCountFDs,
1625 /* semaphoreFd */ nullptr);
1626 return true;
1629 bool DMABufSurfaceYUV::CreateEGLImage(GLContext* aGLContext, int aPlane) {
1630 LOGDMABUF(
1631 ("DMABufSurfaceYUV::CreateEGLImage() UID %d plane %d", mUID, aPlane));
1632 MOZ_ASSERT(mEGLImage[aPlane] == LOCAL_EGL_NO_IMAGE,
1633 "EGLImage is already created!");
1634 MOZ_ASSERT(aGLContext, "Missing GLContext!");
1636 const auto& gle = gl::GLContextEGL::Cast(aGLContext);
1637 const auto& egl = gle->mEgl;
1639 MutexAutoLock lockFD(mSurfaceLock);
1640 if (!OpenFileDescriptorForPlane(lockFD, aPlane)) {
1641 LOGDMABUF((" failed to open dmabuf file descriptors"));
1642 return false;
1645 nsTArray<EGLint> attribs;
1646 attribs.AppendElement(LOCAL_EGL_WIDTH);
1647 attribs.AppendElement(mWidthAligned[aPlane]);
1648 attribs.AppendElement(LOCAL_EGL_HEIGHT);
1649 attribs.AppendElement(mHeightAligned[aPlane]);
1650 attribs.AppendElement(LOCAL_EGL_LINUX_DRM_FOURCC_EXT);
1651 attribs.AppendElement(mDrmFormats[aPlane]);
1652 #define ADD_PLANE_ATTRIBS_NV12(plane_idx) \
1653 attribs.AppendElement(LOCAL_EGL_DMA_BUF_PLANE##plane_idx##_FD_EXT); \
1654 attribs.AppendElement(mDmabufFds[aPlane]->GetHandle()); \
1655 attribs.AppendElement(LOCAL_EGL_DMA_BUF_PLANE##plane_idx##_OFFSET_EXT); \
1656 attribs.AppendElement((int)mOffsets[aPlane]); \
1657 attribs.AppendElement(LOCAL_EGL_DMA_BUF_PLANE##plane_idx##_PITCH_EXT); \
1658 attribs.AppendElement((int)mStrides[aPlane]); \
1659 if (mBufferModifiers[aPlane] != DRM_FORMAT_MOD_INVALID) { \
1660 attribs.AppendElement( \
1661 LOCAL_EGL_DMA_BUF_PLANE##plane_idx##_MODIFIER_LO_EXT); \
1662 attribs.AppendElement(mBufferModifiers[aPlane] & 0xFFFFFFFF); \
1663 attribs.AppendElement( \
1664 LOCAL_EGL_DMA_BUF_PLANE##plane_idx##_MODIFIER_HI_EXT); \
1665 attribs.AppendElement(mBufferModifiers[aPlane] >> 32); \
1667 ADD_PLANE_ATTRIBS_NV12(0);
1668 #undef ADD_PLANE_ATTRIBS_NV12
1669 attribs.AppendElement(LOCAL_EGL_NONE);
1671 mEGLImage[aPlane] =
1672 egl->fCreateImage(LOCAL_EGL_NO_CONTEXT, LOCAL_EGL_LINUX_DMA_BUF_EXT,
1673 nullptr, attribs.Elements());
1675 CloseFileDescriptorForPlane(lockFD, aPlane);
1677 if (mEGLImage[aPlane] == LOCAL_EGL_NO_IMAGE) {
1678 LOGDMABUF((" EGLImageKHR creation failed"));
1679 return false;
1682 LOGDMABUF((" Success."));
1683 return true;
1686 void DMABufSurfaceYUV::ReleaseEGLImages(GLContext* aGLContext) {
1687 LOGDMABUF(("DMABufSurfaceYUV::ReleaseEGLImages() UID %d", mUID));
1688 MOZ_ASSERT(aGLContext, "Missing GLContext!");
1690 const auto& gle = gl::GLContextEGL::Cast(aGLContext);
1691 const auto& egl = gle->mEgl;
1693 for (int i = 0; i < mBufferPlaneCount; i++) {
1694 if (mEGLImage[i] != LOCAL_EGL_NO_IMAGE) {
1695 egl->fDestroyImage(mEGLImage[i]);
1696 mEGLImage[i] = LOCAL_EGL_NO_IMAGE;
1701 bool DMABufSurfaceYUV::CreateTexture(GLContext* aGLContext, int aPlane) {
1702 LOGDMABUF(
1703 ("DMABufSurfaceYUV::CreateTexture() UID %d plane %d", mUID, aPlane));
1704 MOZ_ASSERT(!mTexture[aPlane], "Texture is already created!");
1705 MOZ_ASSERT(aGLContext, "Missing GLContext!");
1707 if (!aGLContext->MakeCurrent()) {
1708 LOGDMABUF((" Failed to make GL context current."));
1709 return false;
1711 if (!CreateEGLImage(aGLContext, aPlane)) {
1712 return false;
1714 aGLContext->fGenTextures(1, &mTexture[aPlane]);
1715 const ScopedBindTexture savedTex(aGLContext, mTexture[aPlane]);
1716 aGLContext->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_S,
1717 LOCAL_GL_CLAMP_TO_EDGE);
1718 aGLContext->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_T,
1719 LOCAL_GL_CLAMP_TO_EDGE);
1720 aGLContext->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MAG_FILTER,
1721 LOCAL_GL_LINEAR);
1722 aGLContext->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MIN_FILTER,
1723 LOCAL_GL_LINEAR);
1724 aGLContext->fEGLImageTargetTexture2D(LOCAL_GL_TEXTURE_2D, mEGLImage[aPlane]);
1725 mGL = aGLContext;
1726 return true;
1729 void DMABufSurfaceYUV::ReleaseTextures() {
1730 LOGDMABUF(("DMABufSurfaceYUV::ReleaseTextures() UID %d", mUID));
1732 FenceDelete();
1734 bool textureActive = false;
1735 for (int i = 0; i < mBufferPlaneCount; i++) {
1736 if (mTexture[i] || mEGLImage[i]) {
1737 textureActive = true;
1738 break;
1742 if (!textureActive) {
1743 return;
1746 if (!mGL) {
1747 #ifdef NIGHTLY_BUILD
1748 MOZ_DIAGNOSTIC_ASSERT(mGL, "Missing GL context!");
1749 #else
1750 NS_WARNING(
1751 "DMABufSurfaceYUV::ReleaseTextures(): Missing GL context! We're "
1752 "leaking textures!");
1753 return;
1754 #endif
1757 if (!mGL->MakeCurrent()) {
1758 NS_WARNING(
1759 "DMABufSurfaceYUV::ReleaseTextures(): MakeCurrent failed. We're "
1760 "leaking textures!");
1761 return;
1764 mGL->fDeleteTextures(DMABUF_BUFFER_PLANES, mTexture);
1765 for (int i = 0; i < DMABUF_BUFFER_PLANES; i++) {
1766 mTexture[i] = 0;
1768 ReleaseEGLImages(mGL);
1769 mGL = nullptr;
1772 bool DMABufSurfaceYUV::VerifyTextureCreation() {
1773 LOGDMABUF(("DMABufSurfaceYUV::VerifyTextureCreation() UID %d", mUID));
1775 StaticMutexAutoLock lock(sSnapshotContextMutex);
1776 RefPtr<GLContext> context = ClaimSnapshotGLContext();
1777 auto release = MakeScopeExit([&] {
1778 ReleaseEGLImages(context);
1779 ReturnSnapshotGLContext(context);
1782 for (int i = 0; i < mBufferPlaneCount; i++) {
1783 if (!CreateEGLImage(context, i)) {
1784 LOGDMABUF((" failed to create EGL image!"));
1785 return false;
1789 LOGDMABUF((" success"));
1790 return true;
1793 gfx::SurfaceFormat DMABufSurfaceYUV::GetFormat() {
1794 switch (mFOURCCFormat) {
1795 case VA_FOURCC_P010:
1796 // ReportVA_FOURCC_P010 as NV12 as Gecko threats P010 as a variant of P016
1797 // with zeroed bits, see gfx::SurfaceFormat for details.
1798 // NV12 / P010 uses the same plane composition but NV12 is 8-bit format
1799 // and P010 10-bit one.
1800 // It doesn't matter much as long as we create textures with correct
1801 // drm format.
1802 case VA_FOURCC_NV12:
1803 return gfx::SurfaceFormat::NV12;
1804 case VA_FOURCC_YV12:
1805 return gfx::SurfaceFormat::YUV420;
1806 default:
1807 gfxCriticalNoteOnce << "DMABufSurfaceYUV::GetFormat() unknow format: "
1808 << mFOURCCFormat;
1809 return gfx::SurfaceFormat::UNKNOWN;
1813 // GL uses swapped R and B components so report accordingly.
1814 // YUV formats are not affected so report what we have directly.
1815 gfx::SurfaceFormat DMABufSurfaceYUV::GetFormatGL() { return GetFormat(); }
1817 int DMABufSurfaceYUV::GetTextureCount() { return mBufferPlaneCount; }
1819 void DMABufSurfaceYUV::ReleaseSurface() {
1820 LOGDMABUF(("DMABufSurfaceYUV::ReleaseSurface() UID %d", mUID));
1821 ReleaseTextures();
1822 ReleaseDMABuf();
1825 nsresult DMABufSurfaceYUV::BuildSurfaceDescriptorBuffer(
1826 SurfaceDescriptorBuffer& aSdBuffer, Image::BuildSdbFlags aFlags,
1827 const std::function<MemoryOrShmem(uint32_t)>& aAllocate) {
1828 LOGDMABUF(("DMABufSurfaceYUV::BuildSurfaceDescriptorBuffer UID %d", mUID));
1830 gfx::IntSize size(GetWidth(), GetHeight());
1831 const auto format = gfx::SurfaceFormat::B8G8R8A8;
1833 uint8_t* buffer = nullptr;
1834 int32_t stride = 0;
1835 nsresult rv = Image::AllocateSurfaceDescriptorBufferRgb(
1836 size, format, buffer, aSdBuffer, stride, aAllocate);
1837 if (NS_WARN_IF(NS_FAILED(rv))) {
1838 LOGDMABUF(("BuildSurfaceDescriptorBuffer allocate descriptor failed"));
1839 return rv;
1842 return ReadIntoBuffer(buffer, stride, size, format);
1845 #ifdef MOZ_WAYLAND
1846 wl_buffer* DMABufSurfaceYUV::CreateWlBuffer() {
1847 nsWaylandDisplay* waylandDisplay = widget::WaylandDisplayGet();
1848 auto* dmabuf = waylandDisplay->GetDmabuf();
1849 if (!dmabuf) {
1850 gfxCriticalNoteOnce
1851 << "DMABufSurfaceYUV::CreateWlBuffer(): Missing DMABuf support!";
1852 return nullptr;
1855 MutexAutoLock lockFD(mSurfaceLock);
1856 LOGDMABUF(
1857 ("DMABufSurfaceYUV::CreateWlBuffer() UID %d format %s size [%d x %d]",
1858 mUID, GetSurfaceTypeName(), GetWidth(), GetHeight()));
1860 if (!OpenFileDescriptors(lockFD)) {
1861 LOGDMABUF((" failed to open dmabuf fd"));
1862 return nullptr;
1865 struct zwp_linux_buffer_params_v1* params =
1866 zwp_linux_dmabuf_v1_create_params(dmabuf);
1867 for (int i = 0; i < GetTextureCount(); i++) {
1868 LOGDMABUF((" layer [%d] modifier %" PRIx64, i, mBufferModifiers[i]));
1869 zwp_linux_buffer_params_v1_add(
1870 params, mDmabufFds[i]->GetHandle(), i, mOffsets[i], mStrides[i],
1871 mBufferModifiers[i] >> 32, mBufferModifiers[i] & 0xffffffff);
1874 LOGDMABUF(
1875 (" zwp_linux_buffer_params_v1_create_immed() [%d x %d], fourcc [%x]",
1876 GetWidth(), GetHeight(), GetFOURCCFormat()));
1877 wl_buffer* buffer = zwp_linux_buffer_params_v1_create_immed(
1878 params, GetWidth(), GetHeight(), GetFOURCCFormat(), 0);
1879 if (!buffer) {
1880 LOGDMABUF(
1881 (" zwp_linux_buffer_params_v1_create_immed(): failed to create "
1882 "wl_buffer!"));
1883 } else {
1884 LOGDMABUF((" created wl_buffer [%p]", buffer));
1887 CloseFileDescriptors(lockFD);
1888 return buffer;
1890 #endif
1892 #if 0
1893 void DMABufSurfaceYUV::ClearPlane(int aPlane) {
1894 if (!MapInternal(0, 0, mWidth[aPlane], mHeight[aPlane], nullptr,
1895 GBM_BO_TRANSFER_WRITE, aPlane)) {
1896 return;
1898 if ((int)mMappedRegionStride[aPlane] < mWidth[aPlane]) {
1899 return;
1901 memset((char*)mMappedRegion[aPlane], 0,
1902 mMappedRegionStride[aPlane] * mHeight[aPlane]);
1903 Unmap(aPlane);
1906 # include "gfxUtils.h"
1908 void DMABufSurfaceYUV::DumpToFile(const char* aFile) {
1909 RefPtr<gfx::DataSourceSurface> surf = GetAsSourceSurface();
1910 gfxUtils::WriteAsPNG(surf, aFile);
1912 #endif