Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  DMABufSurface.cpp   Sprache: C

 

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


#include "DMABufSurface.h"
#include "DMABufDevice.h"
#include "DMABufFormats.h"

#ifdef MOZ_WAYLAND
#  include "nsWaylandDisplay.h"
#endif

#include <gbm.h>
#include <fcntl.h>
#include <getopt.h>
#include <signal.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/time.h>
#include <dlfcn.h>
#include <sys/mman.h>
#ifdef HAVE_EVENTFD
#  include <sys/eventfd.h>
#endif
#include <poll.h>
#ifdef HAVE_SYSIOCCOM_H
#  include <sys/ioccom.h>
#endif
#include <sys/ioctl.h>
#ifdef MOZ_LOGGING
#  include "gfxUtils.h"
#endif

// DMABufDevice defines its own version of this which collides with the
// official version in drm_fourcc.h
#ifdef DRM_FORMAT_MOD_INVALID
#  undef DRM_FORMAT_MOD_INVALID
#endif
#include <libdrm/drm_fourcc.h>
#ifndef DRM_FORMAT_MOD_INVALID
#  define DRM_FORMAT_MOD_INVALID ((1ULL << 56) - 1)
#endif

#include "mozilla/widget/va_drmcommon.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/gfx/FileHandleWrapper.h"
#include "GLContextTypes.h"  // for GLContext, etc
#include "GLContextEGL.h"
#include "GLContextProvider.h"
#include "ScopedGLHelpers.h"
#include "GLBlitHelper.h"
#include "GLReadTexImageHelper.h"
#include "nsGtkUtils.h"
#include "ImageContainer.h"
#include "mozilla/layers/LayersSurfaces.h"
#include "mozilla/ScopeExit.h"
#include "mozilla/gfx/gfxVars.h"
#include "nsIMemoryReporter.h"

/* C++ / C typecast macros for special EGL handle values */
#if defined(__cplusplus)
#  define EGL_CAST(type, value) (static_cast<type>(value))
#else
#  define EGL_CAST(type, value) ((type)(value))
#endif

using namespace mozilla;
using namespace mozilla::widget;
using namespace mozilla::gl;
using namespace mozilla::layers;
using namespace mozilla::gfx;

#undef LOGDMABUF
#undef LOGDMABUFREF
#ifdef MOZ_LOGGING
#  include "mozilla/Logging.h"
#  include "nsTArray.h"
#  include "Units.h"

extern mozilla::LazyLogModule gDmabufLog;
#  define LOGDMABUF(str, ...)                     \
    MOZ_LOG(gDmabufLog, mozilla::LogLevel::Debug, \
            ("%s: " str, GetDebugTag().get(), ##__VA_ARGS__))
#  define LOGDMAVERBOSES(str, ...) \
    MOZ_LOG(gDmabufLog, mozilla::LogLevel::Verbose, (str, ##__VA_ARGS__))
#  define LOGDMABUFS(str, ...) \
    MOZ_LOG(gDmabufLog, mozilla::LogLevel::Debug, (str, ##__VA_ARGS__))
static LazyLogModule gDmabufRefLog("DmabufRef");
#  define LOGDMABUFREF(str, ...)                     \
    MOZ_LOG(gDmabufRefLog, mozilla::LogLevel::Debug, \
            ("%s: " str, GetDebugTag().get(), ##__VA_ARGS__))
#else
#  define LOGDMABUF(str, ...)
#  define LOGDMABUFREF(str, ...)
#endif /* MOZ_LOGGING */

#define BUFFER_FLAGS 0

static const std::string FormatEGLError(EGLint err) {
  switch (err) {
    case LOCAL_EGL_NOT_INITIALIZED:
      return "EGL_NOT_INITIALIZED";
    case LOCAL_EGL_BAD_ACCESS:
      return "EGL_BAD_ACCESS";
    case LOCAL_EGL_BAD_ALLOC:
      return "EGL_BAD_ALLOC";
    case LOCAL_EGL_BAD_ATTRIBUTE:
      return "EGL_BAD_ATTRIBUTE";
    case LOCAL_EGL_BAD_CONTEXT:
      return "EGL_BAD_CONTEXT";
    case LOCAL_EGL_BAD_CONFIG:
      return "EGL_BAD_CONFIG";
    case LOCAL_EGL_BAD_CURRENT_SURFACE:
      return "EGL_BAD_CURRENT_SURFACE";
    case LOCAL_EGL_BAD_DISPLAY:
      return "EGL_BAD_DISPLAY";
    case LOCAL_EGL_BAD_SURFACE:
      return "EGL_BAD_SURFACE";
    case LOCAL_EGL_BAD_MATCH:
      return "EGL_BAD_MATCH";
    case LOCAL_EGL_BAD_PARAMETER:
      return "EGL_BAD_PARAMETER";
    case LOCAL_EGL_BAD_NATIVE_PIXMAP:
      return "EGL_BAD_NATIVE_PIXMAP";
    case LOCAL_EGL_BAD_NATIVE_WINDOW:
      return "EGL_BAD_NATIVE_WINDOW";
    case LOCAL_EGL_CONTEXT_LOST:
      return "EGL_CONTEXT_LOST";
    default:
      return "EGL error code: " + std::to_string(err);
  }
}

constinit static RefPtr<GLContext> sSnapshotContext;
static StaticMutex sSnapshotContextMutex MOZ_UNANNOTATED;
MOZ_RUNINIT static Atomic<int> gNewSurfaceUID(getpid());
/* Memory reporter stuff */
static Atomic<int64_t, Relaxed> gDMABufSurfaceMemoryRGBAUsed(0);
static Atomic<size_t, Relaxed> gDMABufSurfaceRGBAUsed(0);
static Atomic<int64_t, Relaxed> gDMABufSurfaceMemoryYUVUsed(0);
static Atomic<size_t, Relaxed> gDMABufSurfaceYUVUsed(0);

void LogMemoryAddRGBA(int aUID, size_t aUsedMem) {
  gDMABufSurfaceRGBAUsed++;
  gDMABufSurfaceMemoryRGBAUsed += aUsedMem;
  LOGDMAVERBOSES("UID %d Memory RGBA [%zu] mem %" PRId64 " KB (+ %zu KB)", aUID,
                 static_cast<size_t>(gDMABufSurfaceRGBAUsed),
                 static_cast<int64_t>(gDMABufSurfaceMemoryRGBAUsed) / 1000,
                 aUsedMem / 1000);
}

void LogMemorySubRGBA(int aUID, size_t aUsedMem) {
  gDMABufSurfaceMemoryRGBAUsed -= aUsedMem;
  gDMABufSurfaceRGBAUsed--;
  LOGDMAVERBOSES("UID %d Memory RGBA [%zu] mem %" PRId64 " KB (- %zu KB)", aUID,
                 static_cast<size_t>(gDMABufSurfaceRGBAUsed),
                 static_cast<int64_t>(gDMABufSurfaceMemoryRGBAUsed) / 1000,
                 aUsedMem / 1000);
}

void LogMemoryAddYUV(int aUID, size_t aUsedMem) {
  gDMABufSurfaceYUVUsed++;
  gDMABufSurfaceMemoryYUVUsed += aUsedMem;
  LOGDMAVERBOSES("UID %d Memory YUV [%zu] mem %" PRId64 " KB (+ %zu KB)", aUID,
                 static_cast<size_t>(gDMABufSurfaceYUVUsed),
                 static_cast<int64_t>(gDMABufSurfaceMemoryYUVUsed) / 1000,
                 aUsedMem / 1000);
}

void LogMemorySubYUV(int aUID, size_t aUsedMem) {
  gDMABufSurfaceMemoryYUVUsed -= aUsedMem;
  gDMABufSurfaceYUVUsed--;
  LOGDMAVERBOSES("UID %d Memory YUV [%zu] mem %" PRId64 " KB (- %zu KB)", aUID,
                 static_cast<size_t>(gDMABufSurfaceYUVUsed),
                 static_cast<int64_t>(gDMABufSurfaceMemoryYUVUsed) / 1000,
                 aUsedMem / 1000);
}

class DMABufSurfaceReporter final : public nsIMemoryReporter {
  ~DMABufSurfaceReporter() = default;

 public:
  NS_DECL_ISUPPORTS

  NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
                            nsISupports* aData, bool aAnonymize) override {
    MOZ_COLLECT_REPORT("dmabuf-rgba", KIND_OTHER, UNITS_BYTES,
                       gDMABufSurfaceMemoryRGBAUsed,
                       "Memory used by GPU/DMABuf for RGBA framebuffers.");
    MOZ_COLLECT_REPORT("dmabuf-rgba-num", KIND_OTHER, UNITS_COUNT,
                       gDMABufSurfaceRGBAUsed,
                       "Number of allocated GPU/DMABuf RGBA framebuffers.");
    MOZ_COLLECT_REPORT("dmabuf-yuv", KIND_OTHER, UNITS_BYTES,
                       gDMABufSurfaceMemoryYUVUsed,
                       "Memory used by GPU/DMABuf for YUV video frames.");
    MOZ_COLLECT_REPORT("dmabuf-yuv-num", KIND_OTHER, UNITS_COUNT,
                       gDMABufSurfaceYUVUsed,
                       "Number of allocated GPU/DMABuf YUV video frames.");

    return NS_OK;
  }
};

NS_IMPL_ISUPPORTS(DMABufSurfaceReporter, nsIMemoryReporter)

size_t DMABufSurfaceRGBA::GetUsedMemoryRGBA() { return mWidth * mHeight * 4; }

void DMABufSurface::InitMemoryReporting() {
  RegisterStrongMemoryReporter(MakeAndAddRef<DMABufSurfaceReporter>());
}

// We should release all resources allocated by SnapshotGLContext before
// ReturnSnapshotGLContext() call. Otherwise DMABufSurface references
// SnapshotGLContext and may colide with other SnapshotGLContext operations.
RefPtr<GLContext> DMABufSurface::ClaimSnapshotGLContext() {
  if (!sSnapshotContext) {
    nsCString discardFailureId;
    sSnapshotContext = GLContextProvider::CreateHeadless({}, &discardFailureId);
    if (!sSnapshotContext) {
      LOGDMABUFS(
          "ClaimSnapshotGLContext: Failed to create snapshot GLContext.");
      return nullptr;
    }
    sSnapshotContext->mOwningThreadId = Nothing();  // No singular owner.
  }
  if (!sSnapshotContext->MakeCurrent()) {
    gfxCriticalNote
        << "ClaimSnapshotGLContext: Failed to make GLContext current";
    return nullptr;
  }
  return sSnapshotContext;
}

void DMABufSurface::ReturnSnapshotGLContext(RefPtr<GLContext> aGLContext) {
  if (!aGLContext || !aGLContext->IsCurrent()) {
    LOGDMABUFS("ReturnSnapshotGLContext() failed, is not current!");
    return;
  }
  // direct eglMakeCurrent() call breaks current context caching so make sure
  // it's not used.
  MOZ_ASSERT(!aGLContext->mUseTLSIsCurrent);
  const auto& gle = gl::GLContextEGL::Cast(aGLContext);
  const auto& egl = gle->mEgl;
  egl->fMakeCurrent(EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
}

void DMABufSurface::ReleaseSnapshotGLContext() {
  {
    StaticMutexAutoLock lock(sSnapshotContextMutex);
    sSnapshotContext = nullptr;
  }
  gl::GLContextProvider::Shutdown();
}

bool DMABufSurface::UseDmaBufGL(GLContext* aGLContext) {
  if (!aGLContext) {
    LOGDMABUFS("DMABufSurface::UseDmaBufGL(): Missing GLContext!");
    return false;
  }

  static bool useDmabufGL = [&]() {
    if (!aGLContext->IsExtensionSupported(gl::GLContext::OES_EGL_image)) {
      gfxCriticalNote << "DMABufSurface::UseDmaBufGL(): no OES_EGL_image.";
      return false;
    }
    return true;
  }();

  return useDmabufGL;
}

bool DMABufSurface::UseDmaBufExportExtension(GLContext* aGLContext) {
  static bool useDmabufExport = [&]() {
    if (!gfx::gfxVars::UseDMABufSurfaceExport()) {
      return false;
    }

    if (!UseDmaBufGL(aGLContext)) {
      gfxCriticalNote
          << "DMABufSurface::UseDmaBufExportExtension(): dmabuf is disabled.";
      return false;
    }

    if (!aGLContext->IsAtLeast(gl::ContextProfile::OpenGLCore, 300) &&
        !aGLContext->IsAtLeast(gl::ContextProfile::OpenGLES, 300)) {
      gfxCriticalNote
          << "DMABufSurface::UseDmaBufExportExtension(): old GL version!";
      return false;
    }

    const auto& gle = gl::GLContextEGL::Cast(aGLContext);
    const auto& egl = gle->mEgl;
    bool extensionsAvailable =
        egl->IsExtensionSupported(EGLExtension::EXT_image_dma_buf_import) &&
        egl->IsExtensionSupported(
            EGLExtension::EXT_image_dma_buf_import_modifiers) &&
        egl->IsExtensionSupported(EGLExtension::MESA_image_dma_buf_export);
    if (!extensionsAvailable) {
      if (!egl->IsExtensionSupported(EGLExtension::EXT_image_dma_buf_import)) {
        gfxCriticalNote << "DMABufSurface::UseDmaBufExportExtension(): "
                           "EXT_image_dma_buf_import is missing!";
      }
      if (!egl->IsExtensionSupported(
              EGLExtension::EXT_image_dma_buf_import_modifiers)) {
        gfxCriticalNote << "DMABufSurface::UseDmaBufExportExtension(): "
                           "EXT_image_dma_buf_import_modifiers is missing!";
      }
      if (!egl->IsExtensionSupported(EGLExtension::MESA_image_dma_buf_export)) {
        gfxCriticalNote << "DMABufSurface::UseDmaBufExportExtension(): "
                           "MESA_image_dma_buf_export is missing!";
      }
      LOGDMAVERBOSES("Vendor: %s", aGLContext->VendorString().get());
      LOGDMAVERBOSES("Renderer: %s", aGLContext->RendererString().get());
      auto& strings = aGLContext->ExtensionStrings();
      for (auto& ext : strings) {
        LOGDMAVERBOSES("%s", ext.get());
      }
    }
    return extensionsAvailable;
  }();

  return aGLContext && useDmabufExport;
}

nsAutoCString DMABufSurface::GetDebugTag() const {
  nsAutoCString tag;
  tag.AppendPrintf("[%p]", this);
  return tag;
}
bool DMABufSurface::IsGlobalRefSet() {
  MutexAutoLock lock(mSurfaceLock);
  if (!mGlobalRefCountFd) {
    return false;
  }
  struct pollfd pfd;
  pfd.fd = mGlobalRefCountFd;
  pfd.events = POLLIN;
  return poll(&pfd, 1, 0) == 1;
}

void DMABufSurface::GlobalRefRelease() {
#ifdef HAVE_EVENTFD
  MutexAutoLock lock(mSurfaceLock);
  if (!mGlobalRefCountFd) {
    return;
  }
  LOGDMABUFREF("DMABufSurface::GlobalRefRelease UID %d", mUID);
  uint64_t counter;
  if (read(mGlobalRefCountFd, &counter, sizeof(counter)) != sizeof(counter)) {
    if (errno == EAGAIN) {
      LOGDMABUFREF("  GlobalRefRelease failed: already zero reference! UID %d",
                   mUID);
    }
    // EAGAIN means the refcount is already zero. It happens when we release
    // last reference to the surface.
    if (errno != EAGAIN) {
      NS_WARNING(nsPrintfCString("Failed to unref dmabuf global ref count: %s",
                                 strerror(errno))
                     .get());
    }
  }
#endif
}

void DMABufSurface::GlobalRefAddLocked(const MutexAutoLock& aProofOfLock) {
#ifdef HAVE_EVENTFD
  LOGDMABUFREF("DMABufSurface::GlobalRefAddLocked UID %d", mUID);
  MOZ_DIAGNOSTIC_ASSERT(mGlobalRefCountFd);
  uint64_t counter = 1;
  if (write(mGlobalRefCountFd, &counter, sizeof(counter)) != sizeof(counter)) {
    NS_WARNING(nsPrintfCString("Failed to ref dmabuf global ref count: %s",
                               strerror(errno))
                   .get());
  }
#endif
}

void DMABufSurface::GlobalRefAdd() {
  LOGDMABUFREF("DMABufSurface::GlobalRefAdd UID %d", mUID);
  MutexAutoLock lock(mSurfaceLock);
  GlobalRefAddLocked(lock);
}

void DMABufSurface::GlobalRefCountCreate() {
#ifdef HAVE_EVENTFD
  LOGDMABUFREF("DMABufSurface::GlobalRefCountCreate UID %d", mUID);
  MutexAutoLock lock(mSurfaceLock);
  MOZ_DIAGNOSTIC_ASSERT(!mGlobalRefCountFd);
  // Create global ref count initialized to 0,
  // i.e. is not referenced after create.
  mGlobalRefCountFd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK | EFD_SEMAPHORE);
  if (mGlobalRefCountFd < 0) {
    NS_WARNING(nsPrintfCString("Failed to create dmabuf global ref count: %s",
                               strerror(errno))
                   .get());
    mGlobalRefCountFd = 0;
    return;
  }
#endif
}

void DMABufSurface::GlobalRefCountImport(int aFd) {
#ifdef HAVE_EVENTFD
  MutexAutoLock lock(mSurfaceLock);
  mGlobalRefCountFd = aFd;
  if (mGlobalRefCountFd) {
    LOGDMABUFREF("DMABufSurface::GlobalRefCountImport UID %d", mUID);
    GlobalRefAddLocked(lock);
  }
#endif
}

int DMABufSurface::GlobalRefCountExport() {
  MutexAutoLock lock(mSurfaceLock);
#ifdef MOZ_LOGGING
  if (mGlobalRefCountFd) {
    LOGDMABUFREF("DMABufSurface::GlobalRefCountExport UID %d", mUID);
  }
#endif
  return mGlobalRefCountFd;
}

void DMABufSurface::GlobalRefCountDelete() {
  MutexAutoLock lock(mSurfaceLock);
  if (mGlobalRefCountFd) {
    LOGDMABUFREF("DMABufSurface::GlobalRefCountDelete UID %d", mUID);
    close(mGlobalRefCountFd);
    mGlobalRefCountFd = 0;
  }
}

void DMABufSurface::ReleaseDMABuf() {
  LOGDMABUF("DMABufSurface::ReleaseDMABuf() UID %d", mUID);
#ifdef MOZ_LOGGING
  for (int i = 0; i < mBufferPlaneCount; i++) {
    Unmap(i);
  }
#endif

  CloseFileDescriptors();

  for (int i = 0; i < mBufferPlaneCount; i++) {
    if (mGbmBufferObject[i]) {
      GbmLib::Destroy(mGbmBufferObject[i]);
      mGbmBufferObject[i] = nullptr;
    }
  }
  mBufferPlaneCount = 0;
}

DMABufSurface::DMABufSurface(SurfaceType aSurfaceType)
    : mSurfaceType(aSurfaceType),
      mBufferPlaneCount(0),
      mStrides(),
      mOffsets(),
      mGbmBufferObject(),
      mGbmBufferFlags(0),
#ifdef MOZ_LOGGING
      mMappedRegion(),
      mMappedRegionStride(),
#endif
      mSync(nullptr),
      mGlobalRefCountFd(0),
      mUID(gNewSurfaceUID++),
      mPID(0),
      mCanRecycle(true),
      mSurfaceLock("DMABufSurface") {
  MOZ_COUNT_CTOR(DMABufSurface);
}

DMABufSurface::~DMABufSurface() {
  MOZ_COUNT_DTOR(DMABufSurface);
  FenceDelete();
  GlobalRefRelease();
  GlobalRefCountDelete();
}

already_AddRefed<DMABufSurface> DMABufSurface::CreateDMABufSurface(
    const mozilla::layers::SurfaceDescriptor& aDesc) {
  const SurfaceDescriptorDMABuf& desc = aDesc.get_SurfaceDescriptorDMABuf();
  RefPtr<DMABufSurface> surf;

  switch (desc.bufferType()) {
    case SURFACE_RGBA:
      surf = new DMABufSurfaceRGBA();
      break;
    case SURFACE_YUV:
      surf = new DMABufSurfaceYUV();
      break;
    default:
      return nullptr;
  }

  if (!surf->Create(desc)) {
    return nullptr;
  }
  return surf.forget();
}

/* static */
void DMABufSurface::FenceDelete(RefPtr<gl::GLContext> aGL, EGLSyncKHR aSync) {
  if (!aSync) {
    return;
  }
  const auto& gle = gl::GLContextEGL::Cast(aGL);
  const auto& egl = gle->mEgl;
  egl->fDestroySync(aSync);
}

void DMABufSurface::FenceDeleteLocked(const MutexAutoLock& aProofOfLock) {
  mSyncFd = nullptr;
  if (!mGL) {
    return;
  }
  EGLSyncKHR sync = mSync;
  mSync = nullptr;
  FenceDelete(mGL, sync);
}

void DMABufSurface::FenceDelete() {
  MutexAutoLock lock(mSurfaceLock);
  FenceDeleteLocked(lock);
}

void DMABufSurface::FenceSet() {
  if (!HoldsTexture()) {
    return;
  }

  MutexAutoLock lock(mSurfaceLock);
  if (!mGL || !mGL->MakeCurrent()) {
    MOZ_DIAGNOSTIC_ASSERT(mGL,
                          "DMABufSurface::FenceSet(): missing GL context!");
    return;
  }
  const auto& gle = gl::GLContextEGL::Cast(mGL);
  const auto& egl = gle->mEgl;

  if (egl->IsExtensionSupported(EGLExtension::KHR_fence_sync) &&
      egl->IsExtensionSupported(EGLExtension::ANDROID_native_fence_sync)) {
    FenceDeleteLocked(lock);

    mSync = egl->fCreateSync(LOCAL_EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr);
    if (mSync) {
      auto rawFd = egl->fDupNativeFenceFDANDROID(mSync);
      mSyncFd = new gfx::FileHandleWrapper(UniqueFileHandle(rawFd));
      mGL->fFlush();
      return;
    }
  }

  mGL->fFinish();
}

/* static */
void DMABufSurface::FenceWait(RefPtr<gl::GLContext> aGL,
                              RefPtr<gfx::FileHandleWrapper> aSyncFd) {
  const auto& gle = gl::GLContextEGL::Cast(aGL);
  const auto& egl = gle->mEgl;
  auto syncFd = aSyncFd->ClonePlatformHandle();

  const EGLint attribs[] = {LOCAL_EGL_SYNC_NATIVE_FENCE_FD_ANDROID,
                            syncFd.get(), LOCAL_EGL_NONE};
  EGLSync sync = egl->fCreateSync(LOCAL_EGL_SYNC_NATIVE_FENCE_ANDROID, attribs);
  if (!sync) {
    gfxCriticalNoteOnce << "CreateSync failed";
    int rawFd = syncFd.get();
    if (rawFd >= 0) {
      // CreateSync failed, so we cannot use eglClientWaitSync; poll(POLLIN)
      // waits for the sync_file fd to become readable when the fence signals.
      constexpr int kSyncPollTimeoutMs = 100;
      struct pollfd pfd = {rawFd, POLLIN, 0};
      int ret = poll(&pfd, 1, kSyncPollTimeoutMs);
      if (!(ret == 1 && (pfd.revents & POLLIN))) {
        gfxCriticalNoteOnce << "poll failed: " << ret;
      }
    }
    return;
  }

  // syncFd is owned by GLFence so clear local reference to avoid double.
  (void)syncFd.release();

  egl->fClientWaitSync(sync, 0, LOCAL_EGL_FOREVER);
  const EGLint waitErr = egl->mLib->fGetError();
  if (waitErr != LOCAL_EGL_SUCCESS) {
    gfxCriticalNoteOnce << "ClientWaitSync failed: " << FormatEGLError(waitErr);
    egl->fDestroySync(sync);
    return;
  }
  egl->fDestroySync(sync);
}

void DMABufSurface::FenceWait() {
  if (!HoldsTexture()) {
    return;
  }

  RefPtr<gl::GLContext> gl;
  RefPtr<gfx::FileHandleWrapper> syncFd;
  {
    MutexAutoLock lock(mSurfaceLock);
    if (!mGL || !mSyncFd) {
      MOZ_DIAGNOSTIC_ASSERT(mGL,
                            "DMABufSurface::FenceWait() missing GL context!");
      return;
    }
    gl = mGL;
    syncFd = mSyncFd.forget();
  }

  FenceWait(std::move(gl), std::move(syncFd));
}

void DMABufSurface::SetSemaphoreFd(int aDuppedRawFd, bool aIsSyncFd) {
  MutexAutoLock lock(mSurfaceLock);
  mSemaphoreFd = new gfx::FileHandleWrapper(UniqueFileHandle(aDuppedRawFd));
  mSemaphoreFdIsSyncFd = aIsSyncFd;
}

void DMABufSurface::MaybeSemaphoreWait(GLuint aGlTexture) {
  MOZ_ASSERT(aGlTexture);

  RefPtr<gl::GLContext> gl;
  RefPtr<gfx::FileHandleWrapper> semFdWrapper;
  bool isSyncFd;
  {
    MutexAutoLock lock(mSurfaceLock);
    if (!mSemaphoreFd) {
      return;
    }
    if (!mGL) {
      MOZ_DIAGNOSTIC_ASSERT(
          mGL, "DMABufSurface::SemaphoreWait() missing GL context!");
      return;
    }
    gl = mGL;
    semFdWrapper = mSemaphoreFd.forget();
    isSyncFd = mSemaphoreFdIsSyncFd;
  }

  auto fd = semFdWrapper->ClonePlatformHandle();

  if (isSyncFd) {
    const auto& gle = gl::GLContextEGL::Cast(gl);
    const auto& egl = gle->mEgl;
    if (egl->IsExtensionSupported(EGLExtension::ANDROID_native_fence_sync)) {
      FenceWait(gl, new gfx::FileHandleWrapper(std::move(fd)));
    }
    return;
  }

  if (!gl->IsExtensionSupported(gl::GLContext::EXT_semaphore) ||
      !gl->IsExtensionSupported(gl::GLContext::EXT_semaphore_fd)) {
    MOZ_ASSERT_UNREACHABLE("unexpected to be called");
    gfxCriticalNoteOnce << "EXT_semaphore_fd is not suppored";
    return;
  }

  GLuint semaphoreHandle = 0;
  gl->fGenSemaphoresEXT(1, &semaphoreHandle);
  gl->fImportSemaphoreFdEXT(semaphoreHandle, LOCAL_GL_HANDLE_TYPE_OPAQUE_FD_EXT,
                            fd.release());
  auto error = gl->fGetError();
  if (error != LOCAL_GL_NO_ERROR) {
    gfxCriticalNoteOnce << "glImportSemaphoreFdEXT failed: " << error;
    return;
  }

  GLenum srcLayout = LOCAL_GL_LAYOUT_COLOR_ATTACHMENT_EXT;
  gl->fWaitSemaphoreEXT(semaphoreHandle, 0, nullptr, 1, &aGlTexture,
                        &srcLayout);
  error = gl->fGetError();
  if (error != LOCAL_GL_NO_ERROR) {
    gfxCriticalNoteOnce << "glWaitSemaphoreEXT failed: " << error;
    gl->fDeleteSemaphoresEXT(1, &semaphoreHandle);
    return;
  }
  gl->fDeleteSemaphoresEXT(1, &semaphoreHandle);
  LOGDMABUF("MaybeSemaphoreWait: done (GL semaphore wait)");
}

bool DMABufSurface::OpenFileDescriptors(
    mozilla::widget::DMABufDeviceLock* aDeviceLock) {
  for (int i = 0; i < mBufferPlaneCount; i++) {
    if (!OpenFileDescriptorForPlane(aDeviceLock, i)) {
      return false;
    }
  }
  return true;
}

void DMABufSurface::CloseFileDescriptors() {
  for (auto& mDmabufFd : mDmabufFds) {
    if (mDmabufFd) {
      mDmabufFd = nullptr;
    }
  }
}

nsresult DMABufSurface::ReadIntoBuffer(mozilla::gl::GLContext* aGLContext,
                                       uint8_t* aData, int32_t aStride,
                                       const gfx::IntSize& aSize,
                                       gfx::SurfaceFormat aFormat) {
  LOGDMABUF("DMABufSurface::ReadIntoBuffer UID %d", mUID);

  // We're empty, nothing to copy
  if (!GetTextureCount()) {
    return NS_ERROR_FAILURE;
  }

  MOZ_ASSERT(aSize.width == GetWidth());
  MOZ_ASSERT(aSize.height == GetHeight());

  for (int i = 0; i < GetTextureCount(); i++) {
    if (!GetTexture(i) && !CreateTexture(aGLContext, i)) {
      LOGDMABUF("ReadIntoBuffer: Failed to create DMABuf textures.");
      return NS_ERROR_FAILURE;
    }
  }

  ScopedTexture scopedTex(aGLContext);
  ScopedBindTexture boundTex(aGLContext, scopedTex.Texture());

  aGLContext->fTexImage2D(LOCAL_GL_TEXTURE_2D, 0, LOCAL_GL_RGBA, aSize.width,
                          aSize.height, 0, LOCAL_GL_RGBA,
                          LOCAL_GL_UNSIGNED_BYTE, nullptr);

  ScopedFramebufferForTexture autoFBForTex(aGLContext, scopedTex.Texture());
  if (!autoFBForTex.IsComplete()) {
    LOGDMABUF("ReadIntoBuffer: ScopedFramebufferForTexture failed.");
    return NS_ERROR_FAILURE;
  }

  const gl::OriginPos destOrigin = gl::OriginPos::BottomLeft;
  {
    const ScopedBindFramebuffer bindFB(aGLContext, autoFBForTex.FB());
    if (!aGLContext->BlitHelper()->Blit(
            this, gfx::IntRect(gfx::IntPoint(0, 0), aSize), destOrigin)) {
      LOGDMABUF("ReadIntoBuffer: Blit failed.");
      return NS_ERROR_FAILURE;
    }
  }

  ScopedBindFramebuffer bind(aGLContext, autoFBForTex.FB());
  ReadPixelsIntoBuffer(aGLContext, aData, aStride, aSize, aFormat);
  return NS_OK;
}

already_AddRefed<gfx::DataSourceSurface> DMABufSurface::GetAsSourceSurface() {
  LOGDMABUF("DMABufSurface::GetAsSourceSurface UID %d", mUID);

  gfx::IntSize size(GetWidth(), GetHeight());
  const auto format = gfx::SurfaceFormat::B8G8R8A8;
  RefPtr<gfx::DataSourceSurface> source =
      gfx::Factory::CreateDataSourceSurface(size, format);
  if (NS_WARN_IF(!source)) {
    LOGDMABUF("GetAsSourceSurface: CreateDataSourceSurface failed.");
    return nullptr;
  }

  gfx::DataSourceSurface::ScopedMap map(source,
                                        gfx::DataSourceSurface::READ_WRITE);
  if (NS_WARN_IF(!map.IsMapped())) {
    LOGDMABUF("GetAsSourceSurface: Mapping surface failed.");
    return nullptr;
  }

  if (mGL) {
    if (NS_WARN_IF(NS_FAILED(ReadIntoBuffer(mGL, map.GetData(), map.GetStride(),
                                            size, format)))) {
      LOGDMABUF("GetAsSourceSurface: Reading into buffer failed.");
      return nullptr;
    }
  } else {
    // We're missing active GL context - take a snapshot one.
    StaticMutexAutoLock lock(sSnapshotContextMutex);
    RefPtr<GLContext> context = ClaimSnapshotGLContext();
    auto releaseTextures = mozilla::MakeScopeExit([&] {
      ReleaseTextures();
      ReturnSnapshotGLContext(context);
    });
    if (NS_WARN_IF(NS_FAILED(ReadIntoBuffer(context, map.GetData(),
                                            map.GetStride(), size, format)))) {
      LOGDMABUF("GetAsSourceSurface: Reading into buffer failed.");
      return nullptr;
    }
  }

  return source.forget();
}

DMABufSurfaceRGBA::DMABufSurfaceRGBA()
    : DMABufSurface(SURFACE_RGBA),
      mWidth(0),
      mHeight(0),
      mEGLImage(LOCAL_EGL_NO_IMAGE),
      mTexture(0),
      mBufferModifier(DRM_FORMAT_MOD_INVALID) {}

DMABufSurfaceRGBA::~DMABufSurfaceRGBA() {
  LOGDMABUF("DMABufSurfaceRGBA::~DMABufSurfaceRGBA() UID %d", mUID);
  ReleaseSurface();
}

bool DMABufSurfaceRGBA::OpenFileDescriptorForPlane(
    DMABufDeviceLock* aDeviceLock, int aPlane) {
  if (mDmabufFds[aPlane]) {
    return true;
  }
  gbm_bo* bo = mGbmBufferObject[0];
  if (NS_WARN_IF(!bo)) {
    LOGDMABUF(
        "DMABufSurfaceRGBA::OpenFileDescriptorForPlane: Missing "
        "mGbmBufferObject object!");
    return false;
  }

  if (mBufferPlaneCount == 1) {
    MOZ_ASSERT(aPlane == 0, "DMABuf: wrong surface plane!");
    auto rawFd = GbmLib::GetFd(bo);
    if (rawFd >= 0) {
      mDmabufFds[0] = new gfx::FileHandleWrapper(UniqueFileHandle(rawFd));
    } else {
      gfxCriticalNoteOnce << "GbmLib::GetFd() failed";
      LOGDMABUF(
          "DMABufSurfaceRGBA::OpenFileDescriptorForPlane: GbmLib::GetFd() "
          "failed");
    }
  } else {
    auto rawFd = aDeviceLock->GetDMABufDevice()->GetDmabufFD(
        GbmLib::GetHandleForPlane(bo, aPlane).u32);
    if (rawFd >= 0) {
      mDmabufFds[aPlane] = new gfx::FileHandleWrapper(UniqueFileHandle(rawFd));
    } else {
      gfxCriticalNoteOnce << "DMABufDevice::GetDmabufFD() failed";
      LOGDMABUF(
          "DMABufSurfaceRGBA::OpenFileDescriptorForPlane: "
          "DMABufDevice::GetDmabufFD() failed");
    }
  }

  if (!mDmabufFds[aPlane]) {
    CloseFileDescriptors();
    return false;
  }

  return true;
}

bool DMABufSurfaceRGBA::Create(mozilla::gl::GLContext* aGLContext, int aWidth,
                               int aHeight, int aDMABufSurfaceFlags,
                               RefPtr<DRMFormat> aFormat) {
  bool useGLSnapshot = gfx::gfxVars::UseDMABufSurfaceExport() && !aGLContext;
  if (useGLSnapshot) {
    LOGDMAVERBOSES("DMABufSurfaceRGBA::Create(): use GL snapshot.");
    StaticMutexAutoLock lock(sSnapshotContextMutex);
    RefPtr<GLContext> context = ClaimSnapshotGLContext();
    auto releaseTextures = MakeScopeExit([&] {
      ReleaseTextures();
      ReturnSnapshotGLContext(context);
    });

    // If gfxVars::UseDMABufSurfaceExport() is set but we fail due to missing
    // system support, don't try GBM.
    if (!UseDmaBufExportExtension(context)) {
      LOGDMAVERBOSES(
          "DMABufSurfaceRGBA::Create(): quit, can't use "
          "UseDmaBufExportExtension!");
      return false;
    }
    return CreateExport(context, aWidth, aHeight, aDMABufSurfaceFlags);
  }

  if (gfx::gfxVars::UseDMABufSurfaceExport()) {
    if (!UseDmaBufExportExtension(aGLContext)) {
      LOGDMAVERBOSES(
          "DMABufSurfaceRGBA::Create(): quit, can't use "
          "UseDmaBufExportExtension!");
      return false;
    }
    return CreateExport(aGLContext, aWidth, aHeight, aDMABufSurfaceFlags);
  }

  if (!aFormat) {
    mFOURCCFormat = aDMABufSurfaceFlags & DMABUF_ALPHA ? GBM_FORMAT_ARGB8888
                                                       : GBM_FORMAT_XRGB8888;
    aFormat = GetGlobalDMABufFormats()->GetDRMFormat(mFOURCCFormat);
    if (!aFormat) {
      LOGDMABUF("DMABufSurfaceRGBA::Create(): Missing drm format 0x%x!",
                mFOURCCFormat);
      return false;
    }
  }
  return CreateGBM(aWidth, aHeight, aDMABufSurfaceFlags, aFormat);
}

bool DMABufSurfaceRGBA::CreateGBM(int aWidth, int aHeight,
                                  int aDMABufSurfaceFlags,
                                  RefPtr<DRMFormat> aFormat) {
  MOZ_ASSERT(mGbmBufferObject[0] == nullptr, "Already created?");

  DMABufDeviceLock device;

  mWidth = aWidth;
  mHeight = aHeight;
  mFOURCCFormat = aFormat->GetFormat();

  LOGDMABUF(
      "DMABufSurfaceRGBA::CreateGBM() UID %d size %d x %d format 0x%x "
      "modifiers %d\n",
      mUID, mWidth, mHeight, mFOURCCFormat, aFormat->UseModifiers());

  if (aDMABufSurfaceFlags & DMABUF_TEXTURE) {
    mGbmBufferFlags = GBM_BO_USE_RENDERING;
  } else if (aDMABufSurfaceFlags & DMABUF_SCANOUT) {
    mGbmBufferFlags = GBM_BO_USE_RENDERING | GBM_BO_USE_SCANOUT;
  }
  bool useModifiers =
      aFormat->UseModifiers() && (aDMABufSurfaceFlags & DMABUF_USE_MODIFIERS);
  if (useModifiers) {
    LOGDMABUF("    Creating with modifiers\n");
    uint32_t modifiersNum = 0;
    const uint64_t* modifiers = aFormat->GetModifiers(modifiersNum);
    mGbmBufferObject[0] =
        GbmLib::CreateWithModifiers2(device, mWidth, mHeight, mFOURCCFormat,
                                     modifiers, modifiersNum, mGbmBufferFlags);
    if (mGbmBufferObject[0]) {
      mBufferModifier = GbmLib::GetModifier(mGbmBufferObject[0]);
    }
  }

  if (!mGbmBufferObject[0]) {
    LOGDMABUF("    Creating without modifiers\n");
    mGbmBufferFlags = GBM_BO_USE_RENDERING | GBM_BO_USE_LINEAR;
    mGbmBufferObject[0] =
        GbmLib::Create(device, mWidth, mHeight, mFOURCCFormat, mGbmBufferFlags);
    mBufferModifier = DRM_FORMAT_MOD_INVALID;
  }

  if (!mGbmBufferObject[0]) {
    LOGDMABUF("    Failed to create GbmBufferObject\n");
    return false;
  }

  if (mBufferModifier != DRM_FORMAT_MOD_INVALID) {
    mBufferPlaneCount = GbmLib::GetPlaneCount(mGbmBufferObject[0]);
    LOGDMABUF("    Planes count %d", mBufferPlaneCount);
    if (mBufferPlaneCount > DMABUF_BUFFER_PLANES) {
      LOGDMABUF("    There's too many dmabuf planes! (%d)", mBufferPlaneCount);
      mBufferPlaneCount = DMABUF_BUFFER_PLANES;
      return false;
    }

    for (int i = 0; i < mBufferPlaneCount; i++) {
      mStrides[i] = GbmLib::GetStrideForPlane(mGbmBufferObject[0], i);
      mOffsets[i] = GbmLib::GetOffset(mGbmBufferObject[0], i);
    }
  } else {
    mBufferPlaneCount = 1;
    mStrides[0] = GbmLib::GetStride(mGbmBufferObject[0]);
  }

  if (!OpenFileDescriptors(&device)) {
    LOGDMABUF("    Failed to open Fd!");
    return false;
  }

  LOGDMABUF("    Success\n");

  LogMemoryAddRGBA(GetUID(), GetUsedMemoryRGBA());
  return true;
}

bool DMABufSurfaceRGBA::CreateExport(mozilla::gl::GLContext* aGLContext,
                                     int aWidth, int aHeight,
                                     int aDMABufSurfaceFlags) {
  LOGDMABUF("DMABufSurfaceRGBA::CreateExport() UID %d size %d x %d flags %d",
            mUID, aWidth, aHeight, aDMABufSurfaceFlags);

  MOZ_ASSERT(aGLContext);
  MOZ_DIAGNOSTIC_ASSERT(!mTexture && !mEGLImage, "Already exported??");
  MOZ_DIAGNOSTIC_ASSERT(!mGL || mGL == aGLContext);
  MOZ_DIAGNOSTIC_ASSERT(aGLContext);

  mGL = aGLContext;
  auto releaseTextures = MakeScopeExit([&] { ReleaseTextures(); });

  if (!mGL->MakeCurrent()) {
    LOGDMABUF(" failed to make GL context current");
    return false;
  }

  mWidth = aWidth;
  mHeight = aHeight;

  mGL->fGenTextures(1, &mTexture);
  const ScopedBindTexture savedTex(mGL, mTexture);

  GLContext::LocalErrorScope errorScope(*mGL);
  mGL->fTexImage2D(LOCAL_GL_TEXTURE_2D, 0, LOCAL_GL_RGBA, mWidth, mHeight, 0,
                   LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE, nullptr);
  const auto err = errorScope.GetError();
  if (err) {
    LOGDMABUF("  TexImage2D failed %x error %s", err,
              GLContext::GLErrorToString(err).c_str());
    return false;
  }

  const auto& gle = gl::GLContextEGL::Cast(mGL);
  const auto& context = gle->mContext;
  const auto& egl = gle->mEgl;
  mEGLImage =
      egl->fCreateImage(context, LOCAL_EGL_GL_TEXTURE_2D,
                        reinterpret_cast<EGLClientBuffer>(mTexture), nullptr);
  if (mEGLImage == LOCAL_EGL_NO_IMAGE) {
    LOGDMABUF("  EGLImageKHR creation failed, EGL error %s",
              FormatEGLError(egl->mLib->fGetError()).c_str());
    return false;
  }

  if (!egl->fExportDMABUFImageQuery(mEGLImage, &mFOURCCFormat,
                                    &mBufferPlaneCount, &mBufferModifier)) {
    LOGDMABUF("  ExportDMABUFImageQueryMESA failed, quit\n");
    return false;
  }
  if (mBufferPlaneCount > DMABUF_BUFFER_PLANES) {
    LOGDMABUF("  wrong plane count %d, quit\n", mBufferPlaneCount);
    mBufferPlaneCount = DMABUF_BUFFER_PLANES;
    return false;
  }
  int fds[DMABUF_BUFFER_PLANES] = {-1};
  if (!egl->fExportDMABUFImage(mEGLImage, fds, mStrides, mOffsets)) {
    LOGDMABUF("  ExportDMABUFImageMESA failed, quit\n");
    return false;
  }

  for (int i = 0; i < mBufferPlaneCount; i++) {
    if (fds[i] > 0) {
      mDmabufFds[i] = new gfx::FileHandleWrapper(UniqueFileHandle(fds[i]));
    }
  }

  // A broken driver can return dmabuf without valid file descriptors
  // which leads to fails later so quit now.
  for (int i = 0; i < mBufferPlaneCount; i++) {
    if (!mDmabufFds[i]) {
      LOGDMABUF(
          "  ExportDMABUFImageMESA failed, mDmabufFds[%d] is invalid, quit", i);
      return false;
    }
  }

  if (GetFormat() == gfx::SurfaceFormat::UNKNOWN) {
    LOGDMABUF("  failed, unsupported drm format %x", mFOURCCFormat);
    return false;
  }

  LOGDMABUF("  created size %d x %d format %x planes %d modifiers %" PRIx64
            " alpha %d",
            mWidth, mHeight, mFOURCCFormat, mBufferPlaneCount, mBufferModifier,
            HasAlpha());

  releaseTextures.release();

  LogMemoryAddRGBA(GetUID(), GetUsedMemoryRGBA());
  return true;
}

bool DMABufSurfaceRGBA::Create(
    RefPtr<mozilla::gfx::FileHandleWrapper>&& aFd,
    const mozilla::webgpu::ffi::WGPUDMABufInfo& aDMABufInfo, int aWidth,
    int aHeight) {
  LOGDMABUF("DMABufSurfaceRGBA::CreateWGPUDMABufInfo() UID %d size %d x %d\n",
            mUID, mWidth, mHeight);

  mWidth = aWidth;
  mHeight = aHeight;
  mBufferModifier = aDMABufInfo.modifier;

  // TODO: Read Vulkan modifiers from DMABufFormats?
  mFOURCCFormat = GBM_FORMAT_ARGB8888;
  mBufferPlaneCount = aDMABufInfo.plane_count;

  RefPtr<gfx::FileHandleWrapper> fd = std::move(aFd);

  for (uint32_t i = 0; i < aDMABufInfo.plane_count; i++) {
    mDmabufFds[i] = fd;
    mStrides[i] = aDMABufInfo.strides[i];
    mOffsets[i] = aDMABufInfo.offsets[i];
  }

  LogMemoryAddRGBA(GetUID(), GetUsedMemoryRGBA());
  return true;
}

bool DMABufSurfaceRGBA::ImportSurfaceDescriptor(
    const SurfaceDescriptor& aDesc) {
  const SurfaceDescriptorDMABuf& desc = aDesc.get_SurfaceDescriptorDMABuf();

  mBufferPlaneCount = desc.fds().Length();

  MOZ_RELEASE_ASSERT(mBufferPlaneCount <= DMABUF_BUFFER_PLANES);
  if (mBufferPlaneCount <= 0 || desc.width().Length() == 0 ||
      desc.height().Length() == 0 || desc.modifier().Length() == 0 ||
      desc.strides().Length() < (uint32_t)mBufferPlaneCount ||
      desc.offsets().Length() < (uint32_t)mBufferPlaneCount) {
    return false;
  }

  mFOURCCFormat = desc.fourccFormat();
  mWidth = desc.width()[0];
  mHeight = desc.height()[0];
  mGbmBufferFlags = desc.flags();
  mBufferModifier = desc.modifier()[0];
  mUID = desc.uid();
  mPID = desc.pid();

  LOGDMABUF(
      "DMABufSurfaceRGBA::ImportSurfaceDescriptor() UID %d size %d x %d\n",
      mUID, mWidth, mHeight);

  for (int i = 0; i < mBufferPlaneCount; i++) {
    mDmabufFds[i] = desc.fds()[i];
    mStrides[i] = desc.strides()[i];
    mOffsets[i] = desc.offsets()[i];
  }

  if (desc.fence().Length() > 0) {
    mSyncFd = desc.fence()[0];
  }

  if (desc.semaphoreFd()) {
    mSemaphoreFd = desc.semaphoreFd();
    mSemaphoreFdIsSyncFd = desc.semaphoreFdIsSyncFd();
  }

  if (desc.refCount().Length() > 0) {
    GlobalRefCountImport(desc.refCount()[0].ClonePlatformHandle().release());
  }

  LOGDMABUF("  imported size %d x %d format %x planes %d", mWidth, mHeight,
            mFOURCCFormat, mBufferPlaneCount);

  LogMemoryAddRGBA(GetUID(), GetUsedMemoryRGBA());
  return true;
}

bool DMABufSurfaceRGBA::Create(const SurfaceDescriptor& aDesc) {
  return ImportSurfaceDescriptor(aDesc);
}

bool DMABufSurfaceRGBA::Serialize(
    mozilla::layers::SurfaceDescriptor& aOutDescriptor) {
  AutoTArray<uint32_t, DMABUF_BUFFER_PLANES> width;
  AutoTArray<uint32_t, DMABUF_BUFFER_PLANES> height;
  AutoTArray<NotNull<RefPtr<gfx::FileHandleWrapper>>, DMABUF_BUFFER_PLANES> fds;
  AutoTArray<uint32_t, DMABUF_BUFFER_PLANES> strides;
  AutoTArray<uint32_t, DMABUF_BUFFER_PLANES> offsets;
  AutoTArray<uintptr_t, DMABUF_BUFFER_PLANES> images;
  AutoTArray<uint64_t, DMABUF_BUFFER_PLANES> modifiers;
  AutoTArray<NotNull<RefPtr<gfx::FileHandleWrapper>>, 1> fenceFDs;
  AutoTArray<ipc::FileDescriptor, 1> refCountFDs;

  LOGDMABUF("DMABufSurfaceRGBA::Serialize() UID %d\n", mUID);

  width.AppendElement(mWidth);
  height.AppendElement(mHeight);
  modifiers.AppendElement(mBufferModifier);
  for (int i = 0; i < mBufferPlaneCount; i++) {
    fds.AppendElement(WrapNotNull(mDmabufFds[i]));
    strides.AppendElement(mStrides[i]);
    offsets.AppendElement(mOffsets[i]);
  }

  if (mSync && mSyncFd) {
    fenceFDs.AppendElement(WrapNotNull(mSyncFd));
  }

  if (mGlobalRefCountFd) {
    refCountFDs.AppendElement(ipc::FileDescriptor(GlobalRefCountExport()));
  }

  // GCC needs it (Bug 1959653).
  AutoTArray<uint32_t, 1> tmp;
  aOutDescriptor = SurfaceDescriptorDMABuf(
      mSurfaceType, mFOURCCFormat, modifiers, mGbmBufferFlags, fds, width,
      height, width, height, tmp, strides, offsets, GetYUVColorSpace(),
      mColorRange, mozilla::gfx::ColorSpace2::UNKNOWN,
      mozilla::gfx::TransferFunction::Default, 0, fenceFDs, mUID,
      mCanRecycle ? getpid() : 0, refCountFDs,
      /* semaphoreFd */ nullptr, /* semaphoreFdIsSyncFd */ false);
  return true;
}

bool DMABufSurfaceRGBA::CreateTexture(GLContext* aGLContext, int aPlane) {
  if (mTexture) {
    MOZ_DIAGNOSTIC_ASSERT(mGL == aGLContext);
    return true;
  }

  LOGDMABUF("DMABufSurfaceRGBA::CreateTexture() UID %d plane %d\n", mUID,
            aPlane);

  if (!UseDmaBufGL(aGLContext)) {
    LOGDMABUF("  UseDmaBufGL() failed");
    return false;
  }

  MOZ_DIAGNOSTIC_ASSERT(aGLContext);
  mGL = aGLContext;
  auto releaseTextures = MakeScopeExit([&] { ReleaseTextures(); });

  nsTArray<EGLint> attribs;
  attribs.AppendElement(LOCAL_EGL_WIDTH);
  attribs.AppendElement(mWidth);
  attribs.AppendElement(LOCAL_EGL_HEIGHT);
  attribs.AppendElement(mHeight);
  attribs.AppendElement(LOCAL_EGL_LINUX_DRM_FOURCC_EXT);
  attribs.AppendElement(mFOURCCFormat);
#define ADD_PLANE_ATTRIBS(plane_idx)                                        \
  {                                                                         \
    attribs.AppendElement(LOCAL_EGL_DMA_BUF_PLANE##plane_idx##_FD_EXT);     \
    attribs.AppendElement(mDmabufFds[plane_idx]->GetHandle());              \
    attribs.AppendElement(LOCAL_EGL_DMA_BUF_PLANE##plane_idx##_OFFSET_EXT); \
    attribs.AppendElement((int)mOffsets[plane_idx]);                        \
    attribs.AppendElement(LOCAL_EGL_DMA_BUF_PLANE##plane_idx##_PITCH_EXT);  \
    attribs.AppendElement((int)mStrides[plane_idx]);                        \
    if (mBufferModifier != DRM_FORMAT_MOD_INVALID) {                        \
      attribs.AppendElement(                                                \
          LOCAL_EGL_DMA_BUF_PLANE##plane_idx##_MODIFIER_LO_EXT);            \
      attribs.AppendElement(mBufferModifier & 0xFFFFFFFF);                  \
      attribs.AppendElement(                                                \
          LOCAL_EGL_DMA_BUF_PLANE##plane_idx##_MODIFIER_HI_EXT);            \
      attribs.AppendElement(mBufferModifier >> 32);                         \
    }                                                                       \
  }

  ADD_PLANE_ATTRIBS(0);
  if (mBufferPlaneCount > 1) ADD_PLANE_ATTRIBS(1);
  if (mBufferPlaneCount > 2) ADD_PLANE_ATTRIBS(2);
  if (mBufferPlaneCount > 3) ADD_PLANE_ATTRIBS(3);
#undef ADD_PLANE_ATTRIBS
  attribs.AppendElement(LOCAL_EGL_NONE);

  if (!aGLContext->MakeCurrent()) {
    LOGDMABUF(
        "DMABufSurfaceRGBA::CreateTexture(): failed to make GL context "
        "current");
    return false;
  }

  const auto& gle = gl::GLContextEGL::Cast(aGLContext);
  const auto& egl = gle->mEgl;

  MOZ_ASSERT(!mEGLImage);
  mEGLImage =
      egl->fCreateImage(LOCAL_EGL_NO_CONTEXT, LOCAL_EGL_LINUX_DMA_BUF_EXT,
                        nullptr, attribs.Elements());

  if (mEGLImage == LOCAL_EGL_NO_IMAGE) {
    LOGDMABUF("  EGLImageKHR creation failed, EGL error %s",
              FormatEGLError(egl->mLib->fGetError()).c_str());
    return false;
  }

  aGLContext->fGenTextures(1, &mTexture);
  const ScopedBindTexture savedTex(aGLContext, mTexture);
  aGLContext->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_S,
                             LOCAL_GL_CLAMP_TO_EDGE);
  aGLContext->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_T,
                             LOCAL_GL_CLAMP_TO_EDGE);
  aGLContext->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MAG_FILTER,
                             LOCAL_GL_LINEAR);
  aGLContext->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MIN_FILTER,
                             LOCAL_GL_LINEAR);
  aGLContext->fEGLImageTargetTexture2D(LOCAL_GL_TEXTURE_2D, mEGLImage);

  releaseTextures.release();
  return true;
}

bool DMABufSurfaceRGBA::HoldsTexture() { return mTexture || mEGLImage; }

void DMABufSurfaceRGBA::ReleaseTextures() {
  LOGDMABUF("DMABufSurfaceRGBA::ReleaseTextures() UID %d\n", mUID);
  FenceDelete();

  if (!HoldsTexture()) {
    return;
  }

  if (!mGL) {
#ifdef NIGHTLY_BUILD
    MOZ_DIAGNOSTIC_ASSERT(mGL, "Missing GL context!");
#else
    NS_WARNING(
        "DMABufSurfaceRGBA::ReleaseTextures(): Missing GL context! We're "
        "leaking textures!");
    return;
#endif
  }

  const auto& gle = gl::GLContextEGL::Cast(mGL);
  const auto& egl = gle->mEgl;

  if (mTexture && mGL->MakeCurrent()) {
    mGL->fDeleteTextures(1, &mTexture);
    mTexture = 0;
  }

  if (mEGLImage != LOCAL_EGL_NO_IMAGE) {
    egl->fDestroyImage(mEGLImage);
    mEGLImage = LOCAL_EGL_NO_IMAGE;
  }
  mGL = nullptr;
}

void DMABufSurfaceRGBA::ReleaseSurface() {
  LOGDMABUF("DMABufSurfaceRGBA::ReleaseSurface() UID %d", mUID);
  MOZ_ASSERT(!IsMapped(), "We can't release mapped buffer!");
  ReleaseTextures();
  ReleaseDMABuf();
  LogMemorySubRGBA(GetUID(), GetUsedMemoryRGBA());
}

#ifdef MOZ_WAYLAND
wl_buffer* DMABufSurfaceRGBA::CreateWlBuffer() {
  nsWaylandDisplay* waylandDisplay = widget::WaylandDisplayGet();
  auto* dmabuf = waylandDisplay->GetDmabuf();
  if (!dmabuf) {
    gfxCriticalNoteOnce
        << "DMABufSurfaceRGBA::CreateWlBuffer(): Missing DMABuf support!";
    return nullptr;
  }

  LOGDMABUF(
      "DMABufSurfaceRGBA::CreateWlBuffer() UID %d format %s size [%d x %d]",
      mUID, GetSurfaceTypeName(), GetWidth(), GetHeight());

  struct zwp_linux_buffer_params_v1* params =
      zwp_linux_dmabuf_v1_create_params(dmabuf);

  LOGDMABUF("  layer [0] modifier %" PRIx64, mBufferModifier);
  for (int i = 0; i < mBufferPlaneCount; i++) {
    zwp_linux_buffer_params_v1_add(
        params, mDmabufFds[i]->GetHandle(), i, mOffsets[i], mStrides[i],
        mBufferModifier >> 32, mBufferModifier & 0xffffffff);
  }

  LOGDMABUF(
      "  zwp_linux_buffer_params_v1_create_immed() [%d x %d], fourcc [%x]",
      GetWidth(), GetHeight(), GetFOURCCFormat());
  wl_buffer* buffer = zwp_linux_buffer_params_v1_create_immed(
      params, GetWidth(), GetHeight(), GetFOURCCFormat(), 0);
  if (!buffer) {
    LOGDMABUF(
        "  zwp_linux_buffer_params_v1_create_immed(): failed to create "
        "wl_buffer!");
  } else {
    LOGDMABUF("  created wl_buffer [%p]", buffer);
  }
  zwp_linux_buffer_params_v1_destroy(params);

  return buffer;
}
#endif

#ifdef MOZ_LOGGING
// We should synchronize DMA Buffer object access from CPU to avoid potential
// cache incoherency and data loss.
// See
// https://01.org/linuxgraphics/gfx-docs/drm/driver-api/dma-buf.html#cpu-access-to-dma-buffer-objects
struct dma_buf_sync {
  uint64_t flags;
};
#  define DMA_BUF_SYNC_READ (1 << 0)
#  define DMA_BUF_SYNC_WRITE (2 << 0)
#  define DMA_BUF_SYNC_START (0 << 2)
#  define DMA_BUF_SYNC_END (1 << 2)
#  define DMA_BUF_BASE 'b'
#  define DMA_BUF_IOCTL_SYNC _IOW(DMA_BUF_BASE, 0, struct dma_buf_sync)

static void SyncDmaBuf(int aFd, uint64_t aFlags) {
  struct dma_buf_sync sync = {0};

  sync.flags = aFlags | DMA_BUF_SYNC_READ | DMA_BUF_SYNC_WRITE;
  while (true) {
    int ret;
    ret = ioctl(aFd, DMA_BUF_IOCTL_SYNC, &sync);
    if (ret == -1 && errno == EINTR) {
      continue;
    } else if (ret == -1) {
      LOGDMABUFS("Failed to synchronize DMA buffer: %s FD %d", strerror(errno),
                 aFd);
      break;
    } else {
      break;
    }
  }
}

void* DMABufSurface::MapInternal(uint32_t aX, uint32_t aY, uint32_t aWidth,
                                 uint32_t aHeight, uint32_t* aStride,
                                 int aGbmFlags, int aPlane) {
  NS_ASSERTION(!IsMapped(aPlane), "Already mapped!");
  if (!mGbmBufferObject[aPlane]) {
    NS_WARNING("We can't map DMABufSurface without mGbmBufferObject");
    return nullptr;
  }

  LOGDMABUF(
      "DMABufSurface::MapInternal() UID %d plane %d size %d x %d -> %d x %d\n",
      mUID, aPlane, aX, aY, aWidth, aHeight);

  mMappedRegionStride[aPlane] = 0;
  mMappedRegionData[aPlane] = nullptr;
  mMappedRegion[aPlane] =
      GbmLib::Map(mGbmBufferObject[aPlane], aX, aY, aWidth, aHeight, aGbmFlags,
                  &mMappedRegionStride[aPlane], &mMappedRegionData[aPlane]);
  if (!mMappedRegion[aPlane]) {
    LOGDMABUF("    Surface mapping failed: %s", strerror(errno));
    return nullptr;
  }
  if (aStride) {
    *aStride = mMappedRegionStride[aPlane];
  }

  SyncDmaBuf(mDmabufFds[aPlane]->GetHandle(), DMA_BUF_SYNC_START);
  return mMappedRegion[aPlane];
}

void* DMABufSurfaceRGBA::MapReadOnly(uint32_t aX, uint32_t aY, uint32_t aWidth,
                                     uint32_t aHeight, uint32_t* aStride) {
  return MapInternal(aX, aY, aWidth, aHeight, aStride, GBM_BO_TRANSFER_READ);
}

void* DMABufSurfaceRGBA::MapReadOnly(uint32_t* aStride) {
  return MapInternal(0, 0, mWidth, mHeight, aStride, GBM_BO_TRANSFER_READ);
}

void* DMABufSurfaceRGBA::Map(uint32_t aX, uint32_t aY, uint32_t aWidth,
                             uint32_t aHeight, uint32_t* aStride) {
  return MapInternal(aX, aY, aWidth, aHeight, aStride,
                     GBM_BO_TRANSFER_READ_WRITE);
}

void* DMABufSurfaceRGBA::Map(uint32_t* aStride) {
  return MapInternal(0, 0, mWidth, mHeight, aStride,
                     GBM_BO_TRANSFER_READ_WRITE);
}

void DMABufSurface::Unmap(int aPlane) {
  if (mMappedRegion[aPlane]) {
    LOGDMABUF("DMABufSurface::Unmap() UID %d plane %d\n", mUID, aPlane);
    SyncDmaBuf(mDmabufFds[aPlane]->GetHandle(), DMA_BUF_SYNC_END);
    GbmLib::Unmap(mGbmBufferObject[aPlane], mMappedRegionData[aPlane]);
    mMappedRegion[aPlane] = nullptr;
    mMappedRegionData[aPlane] = nullptr;
    mMappedRegionStride[aPlane] = 0;
  }
}
#endif  // MOZ_LOGGING

nsresult DMABufSurface::BuildSurfaceDescriptorBuffer(
    SurfaceDescriptorBuffer& aSdBuffer, Image::BuildSdbFlags aFlags,
    const std::function<MemoryOrShmem(uint32_t)>& aAllocate) {
  return NS_ERROR_NOT_IMPLEMENTED;
}

#ifdef MOZ_LOGGING
// Universal OpenGL version but needs GL/textures.
void DMABufSurfaceRGBA::DumpToFile(const char* aFile) {
  RefPtr<gfx::DataSourceSurface> surf = GetAsSourceSurface();
  gfxUtils::WriteAsPNG(surf, aFile);
}

#  if 0
// A direct mapping version without GL.
void DMABufSurfaceRGBA::DumpToFile(const char* pFile) {
  uint32_t stride;

  if (!MapReadOnly(&stride)) {
    return;
  }
  cairo_surface_t* surface = nullptr;

  auto unmap = MakeScopeExit([&] {
    if (surface) {
      cairo_surface_destroy(surface);
    }
    Unmap();
  });

  surface = cairo_image_surface_create_for_data(
      (unsigned char*)mMappedRegion[0], CAIRO_FORMAT_ARGB32, mWidth, mHeight,
      stride);
  if (cairo_surface_status(surface) == CAIRO_STATUS_SUCCESS) {
    cairo_surface_write_to_png(surface, pFile);
  }
}
#  endif
#endif

#if 0
// Copy from source surface by GL
#  include "GLBlitHelper.h"

bool DMABufSurfaceRGBA::CopyFrom(class DMABufSurface* aSourceSurface,
                                 GLContext* aGLContext) {
  MOZ_ASSERT(aSourceSurface->GetTexture());
  MOZ_ASSERT(GetTexture());

  gfx::IntSize size(GetWidth(), GetHeight());
  aGLContext->BlitHelper()->BlitTextureToTexture(aSourceSurface->GetTexture(),
    GetTexture(), size, size);
  return true;
}

void DMABufSurfaceRGBA::Clear() {
  uint32_t destStride;
  void* destData = Map(&destStride);
  memset(destData, 0, GetHeight() * destStride);
  Unmap();
}
#endif

#ifdef MOZ_LOGGING
void DMABufSurfaceRGBA::Clear(unsigned int aValue) {
  uint32_t destStride;
  void* destData = Map(&destStride);

  unsigned int* data = (unsigned int*)destData;
  for (unsigned int i = 0; i < (GetHeight() * destStride) >> 2; i++) {
    *data++ = aValue;
  }

  Unmap();
}
#endif

bool DMABufSurfaceRGBA::HasAlpha() {
  return mFOURCCFormat == GBM_FORMAT_ARGB8888 ||
         mFOURCCFormat == GBM_FORMAT_ABGR8888 ||
         mFOURCCFormat == GBM_FORMAT_RGBA8888 ||
         mFOURCCFormat == GBM_FORMAT_BGRA8888;
}

gfx::SurfaceFormat DMABufSurfaceRGBA::GetFormat() {
  switch (mFOURCCFormat) {
    case GBM_FORMAT_ARGB8888:
      return gfx::SurfaceFormat::B8G8R8A8;
    case GBM_FORMAT_ABGR8888:
      return gfx::SurfaceFormat::R8G8B8A8;
    case GBM_FORMAT_BGRA8888:
      return gfx::SurfaceFormat::A8R8G8B8;
    case GBM_FORMAT_RGBA8888:
      gfxCriticalError() << "DMABufSurfaceRGBA::GetFormat(): Unsupported "
                            "format GBM_FORMAT_RGBA8888";
      return gfx::SurfaceFormat::UNKNOWN;

    case GBM_FORMAT_XRGB8888:
      return gfx::SurfaceFormat::B8G8R8X8;
    case GBM_FORMAT_XBGR8888:
      return gfx::SurfaceFormat::R8G8B8X8;
    case GBM_FORMAT_BGRX8888:
      return gfx::SurfaceFormat::X8R8G8B8;
    case GBM_FORMAT_RGBX8888:
      gfxCriticalError() << "DMABufSurfaceRGBA::GetFormat(): Unsupported "
                            "format GBM_FORMAT_RGBX8888";
      return gfx::SurfaceFormat::UNKNOWN;

    default:
      gfxCriticalError() << "DMABufSurfaceRGBA::GetFormat(): Unknown format"
                         << gfx::hexa(mFOURCCFormat);
      return gfx::SurfaceFormat::UNKNOWN;
  }
}

already_AddRefed<DMABufSurfaceRGBA> DMABufSurfaceRGBA::CreateDMABufSurface(
    mozilla::gl::GLContext* aGLContext, int aWidth, int aHeight,
    int aDMABufSurfaceFlags, RefPtr<mozilla::widget::DRMFormat> aFormat) {
  auto surf = MakeRefPtr<DMABufSurfaceRGBA>();
  if (!surf->Create(aGLContext, aWidth, aHeight, aDMABufSurfaceFlags,
                    aFormat)) {
    return nullptr;
  }
  return surf.forget();
}

already_AddRefed<DMABufSurface> DMABufSurfaceRGBA::CreateDMABufSurface(
    RefPtr<mozilla::gfx::FileHandleWrapper>&& aFd,
    const mozilla::webgpu::ffi::WGPUDMABufInfo& aDMABufInfo, int aWidth,
    int aHeight) {
  auto surf = MakeRefPtr<DMABufSurfaceRGBA>();
  if (!surf->Create(std::move(aFd), aDMABufInfo, aWidth, aHeight)) {
    return nullptr;
  }
  return surf.forget();
}

already_AddRefed<DMABufSurfaceYUV> DMABufSurfaceYUV::CreateYUVSurface(
    const VADRMPRIMESurfaceDescriptor& aDesc, int aWidth, int aHeight) {
  auto surf = MakeRefPtr<DMABufSurfaceYUV>();
  LOGDMABUFS("[%p] DMABufSurfaceYUV::CreateYUVSurface() UID %d from desc\n",
             surf.get(), surf->GetUID());
  if (!surf->UpdateYUVData(aDesc, aWidth, aHeight, /* aCopy */ false)) {
    return nullptr;
  }
  return surf.forget();
}

already_AddRefed<DMABufSurfaceYUV> DMABufSurfaceYUV::CopyYUVSurface(
    const VADRMPRIMESurfaceDescriptor& aDesc, int aWidth, int aHeight) {
  auto surf = MakeRefPtr<DMABufSurfaceYUV>();
  LOGDMABUFS("[%p] DMABufSurfaceYUV::CreateYUVSurfaceCopy() UID %d from desc\n",
             surf.get(), surf->GetUID());
  if (!surf->UpdateYUVData(aDesc, aWidth, aHeight, /* aCopy */ true)) {
    return nullptr;
  }
  return surf.forget();
}

size_t DMABufSurfaceYUV::GetUsedMemoryYUV(int32_t aFOURCCFormat, int aWidth,
                                          int aHeight) {
  switch (aFOURCCFormat) {
    case VA_FOURCC_P010:
    case VA_FOURCC_P016:
      // one plane 16b + two planes 16b (half sized).
      return aWidth * aHeight * 2 + (aWidth >> 1) * (aHeight >> 1) * 4;
    case VA_FOURCC_NV12:
    case VA_FOURCC_YV12:
    case VA_FOURCC_I420:
      // one plane 8b + two planes 8b (half sized).
      return aWidth * aHeight + (aWidth >> 1) * (aHeight >> 1) * 2;
    default:
      gfxCriticalError()
          << "DMABufSurfaceYUV::GetUsedMemoryYUV(): unknown format: "
          << gfx::hexa(aFOURCCFormat);
      return 0;
  }
}

DMABufSurfaceYUV::DMABufSurfaceYUV()
    : DMABufSurface(SURFACE_YUV),
      mWidth(),
      mHeight(),
      mWidthAligned(),
      mHeightAligned(),
      mDrmFormats(),
      mTexture() {
  for (int i = 0; i < DMABUF_BUFFER_PLANES; i++) {
    mEGLImage[i] = LOCAL_EGL_NO_IMAGE;
    mBufferModifiers[i] = DRM_FORMAT_MOD_INVALID;
  }
}

DMABufSurfaceYUV::~DMABufSurfaceYUV() {
  LOGDMABUF("DMABufSurfaceYUV::~DMABufSurfaceYUV() UID %d", mUID);
  ReleaseSurface();
}

bool DMABufSurfaceYUV::OpenFileDescriptorForPlane(DMABufDeviceLock* aDeviceLock,
                                                  int aPlane) {
  // The fd is already opened, no need to reopen.
  // This can happen when we import dmabuf surface from VA-API decoder,
  // mGbmBufferObject is null and we don't close
  // file descriptors for surface as they are our only reference to it.
  if (mDmabufFds[aPlane]) {
    return true;
  }

  if (mGbmBufferObject[aPlane] == nullptr) {
    LOGDMABUF(
        "DMABufSurfaceYUV::OpenFileDescriptorForPlane: Missing "
        "mGbmBufferObject object!");
    return false;
  }

  auto rawFd = GbmLib::GetFd(mGbmBufferObject[aPlane]);
  if (rawFd < 0) {
    CloseFileDescriptors();
    return false;
  }
  mDmabufFds[aPlane] = new gfx::FileHandleWrapper(UniqueFileHandle(rawFd));

  return true;
}

bool DMABufSurfaceYUV::ImportPRIMESurfaceDescriptor(
    const VADRMPRIMESurfaceDescriptor& aDesc, int aWidth, int aHeight) {
  LOGDMABUF("DMABufSurfaceYUV::ImportPRIMESurfaceDescriptor() UID %d FOURCC %x",
            mUID, aDesc.fourcc);
  // Already exists?
  MOZ_DIAGNOSTIC_ASSERT(!mDmabufFds[0]);

  if (aDesc.num_layers > DMABUF_BUFFER_PLANES ||
      aDesc.num_objects > DMABUF_BUFFER_PLANES) {
    LOGDMABUF("  Can't import, wrong layers/objects number (%d, %d)",
              aDesc.num_layers, aDesc.num_objects);
    return false;
  }
  mSurfaceType = SURFACE_YUV;
  mFOURCCFormat = aDesc.fourcc;
  mBufferPlaneCount = aDesc.num_layers;

  for (unsigned int i = 0; i < aDesc.num_layers; i++) {
    // All supported formats have 4:2:0 chroma sub-sampling.
    unsigned int subsample = i == 0 ? 0 : 1;

    unsigned int object = aDesc.layers[i].object_index[0];
    if (object >= aDesc.num_objects) {
      return false;
    }
    mBufferModifiers[i] = aDesc.objects[object].drm_format_modifier;
    mDrmFormats[i] = aDesc.layers[i].drm_format;
    mOffsets[i] = aDesc.layers[i].offset[0];
    mStrides[i] = aDesc.layers[i].pitch[0];
    mWidthAligned[i] = aDesc.width >> subsample;
    mHeightAligned[i] = aDesc.height >> subsample;
    mWidth[i] = aWidth >> subsample;
    mHeight[i] = aHeight >> subsample;
    LOGDMABUF("    plane %d size %d x %d format %x", i, mWidth[i], mHeight[i],
              mDrmFormats[i]);
  }

  LogMemoryAddYUV(GetUID(),
                  GetUsedMemoryYUV(aDesc.fourcc, aDesc.width, aDesc.height));
  return true;
}

void DMABufSurfaceYUV::ReleaseVADRMPRIMESurfaceDescriptor(
    VADRMPRIMESurfaceDescriptor& aDesc) {
  for (unsigned int i = 0; i < aDesc.num_layers; i++) {
    unsigned int object = aDesc.layers[i].object_index[0];
    if (object >= aDesc.num_objects) {
      continue;
    }
    if (aDesc.objects[object].fd != -1) {
      close(aDesc.objects[object].fd);
      aDesc.objects[object].fd = -1;
    }
  }
  LogMemorySubYUV(-1,
                  GetUsedMemoryYUV(aDesc.fourcc, aDesc.width, aDesc.height));
}

bool DMABufSurfaceYUV::MoveYUVDataImpl(const VADRMPRIMESurfaceDescriptor& aDesc,
                                       int aWidth, int aHeight) {
  if (!ImportPRIMESurfaceDescriptor(aDesc, aWidth, aHeight)) {
    return false;
  }
  for (unsigned int i = 0; i < aDesc.num_layers; i++) {
    unsigned int object = aDesc.layers[i].object_index[0];
    // Keep VADRMPRIMESurfaceDescriptor untouched and dup() dmabuf
    // file descriptors.
    auto rawFd = dup(aDesc.objects[object].fd);
    mDmabufFds[i] = new gfx::FileHandleWrapper(UniqueFileHandle(rawFd));
  }
  LogMemoryAddYUV(GetUID(),
                  GetUsedMemoryYUV(mFOURCCFormat, mWidth[0], mHeight[0]));
  return true;
}

bool DMABufSurfaceYUV::CreateYUVPlaneGBM(int aPlane, DRMFormat* aFormat) {
  LOGDMABUF(
      "DMABufSurfaceYUV::CreateYUVPlaneGBM() UID %d size %d x %d plane %d",
      mUID, mWidth[aPlane], mHeight[aPlane], aPlane);

  DMABufDeviceLock device;

  MOZ_DIAGNOSTIC_ASSERT(mGbmBufferObject[aPlane] == nullptr);

  if (aFormat && aFormat->UseModifiers()) {
    LOGDMABUF("    Creating with modifiers from DRMFormat");
    uint32_t modifiersNum = 0;
    const uint64_t* modifiers = aFormat->GetModifiers(modifiersNum);
    mGbmBufferObject[aPlane] = GbmLib::CreateWithModifiers2(
        device, mWidth[aPlane], mHeight[aPlane], mDrmFormats[aPlane], modifiers,
        modifiersNum, mGbmBufferFlags);
    if (mGbmBufferObject[aPlane]) {
      mBufferModifiers[aPlane] = GbmLib::GetModifier(mGbmBufferObject[aPlane]);
    }
  } else if (mBufferModifiers[aPlane] != DRM_FORMAT_MOD_INVALID) {
    LOGDMABUF(
        "    Creating with modifiers from DMABufSurface mBufferModifiers");
    mGbmBufferObject[aPlane] = GbmLib::CreateWithModifiers2(
        device, mWidth[aPlane], mHeight[aPlane], mDrmFormats[aPlane],
        mBufferModifiers + aPlane, 1, mGbmBufferFlags);
  }
  if (!mGbmBufferObject[aPlane]) {
    LOGDMABUF("    Creating without modifiers");
    mGbmBufferObject[aPlane] =
        GbmLib::Create(device, mWidth[aPlane], mHeight[aPlane],
                       mDrmFormats[aPlane], GBM_BO_USE_RENDERING);
    mBufferModifiers[aPlane] = DRM_FORMAT_MOD_INVALID;
  }
  if (!mGbmBufferObject[aPlane]) {
    LOGDMABUF("    Failed to create GbmBufferObject: %s", strerror(errno));
    return false;
  }

  mStrides[aPlane] = GbmLib::GetStride(mGbmBufferObject[aPlane]);
  mOffsets[aPlane] = GbmLib::GetOffset(mGbmBufferObject[aPlane], 0);
  mWidthAligned[aPlane] = mWidth[aPlane];
  mHeightAligned[aPlane] = mHeight[aPlane];

  if (!OpenFileDescriptorForPlane(&device, aPlane)) {
    return false;
  }

  return true;
}

bool DMABufSurfaceYUV::CreateYUVPlaneExport(GLContext* aGLContext, int aPlane) {
  LOGDMABUF(
      "DMABufSurfaceYUV::CreateYUVPlaneExport() UID %d size %d x %d plane %d",
      mUID, mWidth[aPlane], mHeight[aPlane], aPlane);

  MOZ_DIAGNOSTIC_ASSERT(aGLContext);
  mGL = aGLContext;
  auto releaseTextures = MakeScopeExit([&] { ReleaseTextures(); });

  MOZ_DIAGNOSTIC_ASSERT(!mEGLImage[aPlane]);
  MOZ_DIAGNOSTIC_ASSERT(!mTexture[aPlane]);

  mGL->fGenTextures(1, &mTexture[aPlane]);
  const ScopedBindTexture savedTex(mGL, mTexture[aPlane]);

  GLenum internalFormat;
  GLenum unpackFormat;
  GLenum sizeFormat;
  switch (mDrmFormats[aPlane]) {
    case GBM_FORMAT_R8:
      internalFormat = LOCAL_GL_R8;
      unpackFormat = LOCAL_GL_RED;
      sizeFormat = LOCAL_GL_UNSIGNED_BYTE;
      break;
    case GBM_FORMAT_GR88:
      internalFormat = LOCAL_GL_RG8;
      unpackFormat = LOCAL_GL_RG;
      sizeFormat = LOCAL_GL_UNSIGNED_BYTE;
      break;
    case GBM_FORMAT_R16:
      internalFormat = LOCAL_GL_R16;
      unpackFormat = LOCAL_GL_RED;
      sizeFormat = LOCAL_GL_UNSIGNED_SHORT;
      break;
    case GBM_FORMAT_GR1616:
      internalFormat = LOCAL_GL_RG16;
      unpackFormat = LOCAL_GL_RG;
      sizeFormat = LOCAL_GL_UNSIGNED_SHORT;
      break;
    default:
      gfxCriticalError()
          << "DMABufSurfaceYUV::CreateYUVPlaneExport(): Unsupported format:"
          << gfx::hexa(mDrmFormats[aPlane]);
      return false;
  }

  GLContext::LocalErrorScope errorScope(*mGL);
  mGL->fTexImage2D(LOCAL_GL_TEXTURE_2D, 0, internalFormat, mWidth[aPlane],
                   mHeight[aPlane], 0, unpackFormat, sizeFormat, nullptr);
  const auto err = errorScope.GetError();
  if (err) {
    if (err != LOCAL_GL_OUT_OF_MEMORY) {
      LOGDMABUF("  failed %x error %s", err,
                GLContext::GLErrorToString(err).c_str());
    }
    return false;
  }

  const auto buffer = reinterpret_cast<EGLClientBuffer>(mTexture[aPlane]);

  const auto& gle = gl::GLContextEGL::Cast(mGL);
  const auto& context = gle->mContext;
  const auto& egl = gle->mEgl;
  mEGLImage[aPlane] =
      egl->fCreateImage(context, LOCAL_EGL_GL_TEXTURE_2D, buffer, nullptr);
  if (mEGLImage[aPlane] == LOCAL_EGL_NO_IMAGE) {
    LOGDMABUF("  EGLImageKHR creation failed, EGL error %s",
              FormatEGLError(egl->mLib->fGetError()).c_str());
    return false;
  }

  int bufferPlaneCount = 0;
  if (!egl->fExportDMABUFImageQuery(mEGLImage[aPlane], mDrmFormats + aPlane,
                                    &bufferPlaneCount,
                                    mBufferModifiers + aPlane)) {
    LOGDMABUF("  ExportDMABUFImageQueryMESA failed, quit\n");
    return false;
  }
  if (bufferPlaneCount != 1) {
    LOGDMABUF("  wrong plane count %d, quit\n", bufferPlaneCount);
    return false;
  }
  int fds[DMABUF_BUFFER_PLANES] = {-1};
  if (!egl->fExportDMABUFImage(mEGLImage[aPlane], fds, mStrides + aPlane,
                               mOffsets + aPlane)) {
    LOGDMABUF("  ExportDMABUFImageMESA failed, quit\n");
    return false;
  }

  mDmabufFds[aPlane] = new gfx::FileHandleWrapper(UniqueFileHandle(fds[0]));
  if (!mDmabufFds[aPlane]) {
    LOGDMABUF("  ExportDMABUFImageMESA failed, mDmabufFds[%d] is invalid, quit",
              aPlane);
    return false;
  }

  LOGDMABUF("  imported size %d x %d format %x planes %d modifier %" PRIx64,
            mWidth[aPlane], mHeight[aPlane], mFOURCCFormat, mBufferPlaneCount,
            mBufferModifiers[aPlane]);

  releaseTextures.release();
  return true;
}

bool DMABufSurfaceYUV::CreateYUVPlane(GLContext* aGLContext, int aPlane,
                                      DRMFormat* aFormat) {
  if (gfx::gfxVars::UseDMABufSurfaceExport()) {
    if (!UseDmaBufExportExtension(aGLContext)) {
      LOGDMAVERBOSES(
          "DMABufSurfaceYUV::CreateYUVPlane(): quit, can't use "
          "UseDmaBufExportExtension.");
      return false;
    }
    return CreateYUVPlaneExport(aGLContext, aPlane);
  }
  return CreateYUVPlaneGBM(aPlane, aFormat);
}

bool DMABufSurfaceYUV::CopyYUVDataImpl(const VADRMPRIMESurfaceDescriptor& aDesc,
                                       int aWidth, int aHeight) {
  RefPtr<DMABufSurfaceYUV> tmpSurf = CreateYUVSurface(aDesc, aWidth, aHeight);
  if (!tmpSurf) {
    return false;
  }

  if (!ImportPRIMESurfaceDescriptor(aDesc, aWidth, aHeight)) {
    return false;
  }

  StaticMutexAutoLock lock(sSnapshotContextMutex);
  RefPtr<GLContext> context = ClaimSnapshotGLContext();
  auto releaseTextures = MakeScopeExit([&] {
    tmpSurf->ReleaseTextures();
    ReleaseTextures();
    ReturnSnapshotGLContext(context);
  });

  for (int i = 0; i < mBufferPlaneCount; i++) {
    if (!tmpSurf->CreateTexture(context, i)) {
      return false;
    }
    if (!CreateYUVPlane(context, i) || !CreateTexture(context, i)) {
      return false;
    }
    gfx::IntSize size(GetWidth(i), GetHeight(i));
    context->BlitHelper()->BlitTextureToTexture(
        tmpSurf->GetTexture(i), GetTexture(i), size, size, LOCAL_GL_TEXTURE_2D,
        LOCAL_GL_TEXTURE_2D);
  }

  LogMemoryAddYUV(GetUID(),
                  GetUsedMemoryYUV(mFOURCCFormat, mWidth[0], mHeight[0]));
  return true;
}

bool DMABufSurfaceYUV::UpdateYUVData(const VADRMPRIMESurfaceDescriptor& aDesc,
                                     int aWidth, int aHeight, bool aCopy) {
  LOGDMABUF("DMABufSurfaceYUV::UpdateYUVData() UID %d copy %d", mUID, aCopy);
  return aCopy ? CopyYUVDataImpl(aDesc, aWidth, aHeight)
               : MoveYUVDataImpl(aDesc, aWidth, aHeight);
}

bool DMABufSurfaceYUV::UpdateYUVData(
    const mozilla::layers::PlanarYCbCrData& aData,
    gfx::SurfaceFormat aImageFormat) {
  LOGDMABUF("DMABufSurfaceYUV::UpdateYUVData() PlanarYCbCrData.");

  gfx::SurfaceFormat targetFormat = GetHWFormat(aImageFormat);
  if (targetFormat == gfx::SurfaceFormat::UNKNOWN) {
    LOGDMABUF("DMABufSurfaceYUV::UpdateYUVData() wrong format!");
    return false;
  }

  StaticMutexAutoLock lock(sSnapshotContextMutex);
  RefPtr<GLContext> context = ClaimSnapshotGLContext();
  auto releaseTextures = MakeScopeExit([&] {
    ReleaseTextures();
    ReturnSnapshotGLContext(context);
  });

  gfx::IntSize size = aData.YPictureSize();

  mWidthAligned[0] = mWidth[0] = size.width;
  mHeightAligned[0] = mHeight[0] = size.height;
  mWidthAligned[1] = mWidth[1] = (size.width + 1) >> 1;
  mHeightAligned[1] = mHeight[1] = (size.height + 1) >> 1;
  mBufferPlaneCount = 2;

  // We use this YUV plane for direct rendering of YUV video as wl_buffer
  // for ask for scanout modifiers.
  mGbmBufferFlags = GBM_BO_USE_RENDERING | GBM_BO_USE_SCANOUT;

  switch (targetFormat) {
    case gfx::SurfaceFormat::P010:
      mFOURCCFormat = VA_FOURCC_P010;
      mDrmFormats[0] = GBM_FORMAT_R16;
      mDrmFormats[1] = GBM_FORMAT_GR1616;
      break;
    case gfx::SurfaceFormat::NV12:
      mFOURCCFormat = VA_FOURCC_NV12;
      mDrmFormats[0] = GBM_FORMAT_R8;
      mDrmFormats[1] = GBM_FORMAT_GR88;
      break;
    default:
      MOZ_DIAGNOSTIC_CRASH("Unsupported target format!");
      return false;
  }

  auto format = GetGlobalDMABufFormats()->GetDRMFormat(mFOURCCFormat);
  for (int i = 0; i < mBufferPlaneCount; i++) {
    if (!CreateYUVPlane(context, i, format)) {
      return false;
    }
    if (!CreateTexture(context, i)) {
      return false;
    }
  }

  LogMemoryAddYUV(GetUID(),
                  GetUsedMemoryYUV(mFOURCCFormat, mWidth[0], mHeight[0]));
  return context->BlitHelper()->BlitYCbCrImageToDMABuf(aData, this);
}

bool DMABufSurfaceYUV::Create(const SurfaceDescriptor& aDesc) {
  return ImportSurfaceDescriptor(aDesc);
}

bool DMABufSurfaceYUV::ImportSurfaceDescriptor(
    const SurfaceDescriptorDMABuf& aDesc) {
  mBufferPlaneCount = aDesc.fds().Length();
  MOZ_RELEASE_ASSERT(mBufferPlaneCount <= DMABUF_BUFFER_PLANES);
  if (mBufferPlaneCount <= 0 ||
      aDesc.width().Length() < (uint32_t)mBufferPlaneCount ||
      aDesc.height().Length() < (uint32_t)mBufferPlaneCount ||
      aDesc.widthAligned().Length() < (uint32_t)mBufferPlaneCount ||
      aDesc.heightAligned().Length() < (uint32_t)mBufferPlaneCount ||
      aDesc.format().Length() < (uint32_t)mBufferPlaneCount ||
      aDesc.modifier().Length() < (uint32_t)mBufferPlaneCount ||
      aDesc.strides().Length() < (uint32_t)mBufferPlaneCount ||
      aDesc.offsets().Length() < (uint32_t)mBufferPlaneCount) {
    return false;
  }

  mSurfaceType = SURFACE_YUV;
  mFOURCCFormat = aDesc.fourccFormat();
  mColorSpace = aDesc.yUVColorSpace();
  mColorRange = aDesc.colorRange();
  mColorPrimaries = aDesc.colorPrimaries();
  mTransferFunction = aDesc.transferFunction();
  mWPChromaLocation = aDesc.chromaLocation();
  mGbmBufferFlags = aDesc.flags();
  mUID = aDesc.uid();
  mPID = aDesc.pid();

  LOGDMABUF("DMABufSurfaceYUV::ImportSurfaceDescriptor() UID %d", mUID);

  for (int i = 0; i < mBufferPlaneCount; i++) {
    mDmabufFds[i] = aDesc.fds()[i];
    mWidth[i] = aDesc.width()[i];
    mHeight[i] = aDesc.height()[i];
    mWidthAligned[i] = aDesc.widthAligned()[i];
    mHeightAligned[i] = aDesc.heightAligned()[i];
    mDrmFormats[i] = aDesc.format()[i];
    mStrides[i] = aDesc.strides()[i];
    mOffsets[i] = aDesc.offsets()[i];
    mBufferModifiers[i] = aDesc.modifier()[i];
    LOGDMABUF("    plane %d fd %d size %d x %d format %x modifier %" PRIx64, i,
              mDmabufFds[i]->GetHandle(), mWidth[i], mHeight[i], mDrmFormats[i],
              mBufferModifiers[i]);
  }

  if (aDesc.fence().Length() > 0) {
    mSyncFd = aDesc.fence()[0];
  }

  if (aDesc.semaphoreFd()) {
    mSemaphoreFd = aDesc.semaphoreFd();
    mSemaphoreFdIsSyncFd = aDesc.semaphoreFdIsSyncFd();
  }

  if (aDesc.refCount().Length() > 0) {
    GlobalRefCountImport(aDesc.refCount()[0].ClonePlatformHandle().release());
  }

  LogMemoryAddYUV(GetUID(),
                  GetUsedMemoryYUV(mFOURCCFormat, mWidth[0], mHeight[0]));
  return true;
}

bool DMABufSurfaceYUV::Serialize(
    mozilla::layers::SurfaceDescriptor& aOutDescriptor) {
  AutoTArray<uint32_t, DMABUF_BUFFER_PLANES> width;
  AutoTArray<uint32_t, DMABUF_BUFFER_PLANES> height;
  AutoTArray<uint32_t, DMABUF_BUFFER_PLANES> widthBytes;
  AutoTArray<uint32_t, DMABUF_BUFFER_PLANES> heightBytes;
  AutoTArray<uint32_t, DMABUF_BUFFER_PLANES> format;
  AutoTArray<NotNull<RefPtr<gfx::FileHandleWrapper>>, DMABUF_BUFFER_PLANES> fds;
  AutoTArray<uint32_t, DMABUF_BUFFER_PLANES> strides;
  AutoTArray<uint32_t, DMABUF_BUFFER_PLANES> offsets;
  AutoTArray<uint64_t, DMABUF_BUFFER_PLANES> modifiers;
  AutoTArray<NotNull<RefPtr<gfx::FileHandleWrapper>>, 1> fenceFDs;
  AutoTArray<ipc::FileDescriptor, 1> refCountFDs;

  LOGDMABUF("DMABufSurfaceYUV::Serialize() UID %d", mUID);

  for (int i = 0; i < mBufferPlaneCount; i++) {
    width.AppendElement(mWidth[i]);
    height.AppendElement(mHeight[i]);
    widthBytes.AppendElement(mWidthAligned[i]);
    heightBytes.AppendElement(mHeightAligned[i]);
    format.AppendElement(mDrmFormats[i]);
    fds.AppendElement(WrapNotNull(mDmabufFds[i]));
    strides.AppendElement(mStrides[i]);
    offsets.AppendElement(mOffsets[i]);
    modifiers.AppendElement(mBufferModifiers[i]);
  }

  if (mSync && mSyncFd) {
    fenceFDs.AppendElement(WrapNotNull(mSyncFd));
  }

  if (mGlobalRefCountFd) {
    refCountFDs.AppendElement(ipc::FileDescriptor(GlobalRefCountExport()));
  }

  aOutDescriptor = SurfaceDescriptorDMABuf(
      mSurfaceType, mFOURCCFormat, modifiers, mGbmBufferFlags, fds, width,
      height, widthBytes, heightBytes, format, strides, offsets,
      GetYUVColorSpace(), mColorRange, mColorPrimaries, mTransferFunction,
      mWPChromaLocation, fenceFDs, mUID, mCanRecycle ? getpid() : 0,
      refCountFDs, mSemaphoreFd, mSemaphoreFdIsSyncFd);
  return true;
}

bool DMABufSurfaceYUV::CreateTexture(GLContext* aGLContext, int aPlane) {
  if (mTexture[aPlane]) {
    MOZ_DIAGNOSTIC_ASSERT(aGLContext);
    MOZ_DIAGNOSTIC_ASSERT(mGL == aGLContext);
    return true;
  }

  LOGDMABUF("DMABufSurfaceYUV::CreateTexture() UID %d plane %d", mUID, aPlane);

  if (!UseDmaBufGL(aGLContext)) {
    LOGDMABUF("  UseDmaBufGL() failed");
    return false;
  }

  MOZ_DIAGNOSTIC_ASSERT(aGLContext);
  MOZ_DIAGNOSTIC_ASSERT(!mGL || mGL == aGLContext);

  mGL = aGLContext;
  auto releaseTextures = MakeScopeExit([&] { ReleaseTextures(); });

  if (!aGLContext->MakeCurrent()) {
    LOGDMABUF("  Failed to make GL context current.");
    return false;
  }

  const auto& gle = gl::GLContextEGL::Cast(aGLContext);
  const auto& egl = gle->mEgl;

  if ((aPlane == 1) &&
      ((GetFOURCCFormat() == VA_FOURCC_NV12) ||
       (GetFOURCCFormat() == VA_FOURCC_P010) ||
       (GetFOURCCFormat() == VA_FOURCC_P016)) &&
      egl->IsExtensionSupported(
          EGLExtension::EXT_image_dma_buf_import_modifiers)) {
    uint32_t wasRG = (mDrmFormats[aPlane] == DRM_FORMAT_RG88 ||
                      mDrmFormats[aPlane] == DRM_FORMAT_RG1616);
    uint32_t wasGR = (mDrmFormats[aPlane] == DRM_FORMAT_GR88 ||
                      mDrmFormats[aPlane] == DRM_FORMAT_GR1616);

    EGLint modifierCount = 0;
    egl->mLib->fQueryDmaBufModifiersEXT(egl->mDisplay, mDrmFormats[aPlane], 0,
                                        nullptr, nullptr, &modifierCount);
    if (modifierCount <= 0 && (wasGR || wasRG)) {
      uint32_t swappedFormat = 0;
      if (GetFOURCCFormat() == VA_FOURCC_NV12) {
        swappedFormat = wasGR ? DRM_FORMAT_RG88 : DRM_FORMAT_GR88;
      } else if (GetFOURCCFormat() == VA_FOURCC_P010 ||
                 GetFOURCCFormat() == VA_FOURCC_P016) {
        swappedFormat = wasGR ? DRM_FORMAT_RG1616 : DRM_FORMAT_GR1616;
      }
      EGLint swappedModifierCount = 0;
      egl->mLib->fQueryDmaBufModifiersEXT(
          egl->mDisplay, static_cast<EGLint>(swappedFormat), 0, nullptr,
          nullptr, &swappedModifierCount);
      if (swappedModifierCount > 0) {
        mDrmFormats[aPlane] = static_cast<int>(swappedFormat);
        int bits = GetFOURCCFormat() == VA_FOURCC_NV12 ? 8 : 16;
        LOGDMABUF("  EGL DMA-BUF import: swapped plane 1 to %s%d%d",
                  wasGR ? "RG" : "GR", bits, bits);
      }
    }
  }

  nsTArray<EGLint> attribs;
  attribs.AppendElement(LOCAL_EGL_WIDTH);
  attribs.AppendElement(mWidthAligned[aPlane]);
  attribs.AppendElement(LOCAL_EGL_HEIGHT);
  attribs.AppendElement(mHeightAligned[aPlane]);
  attribs.AppendElement(LOCAL_EGL_LINUX_DRM_FOURCC_EXT);
  attribs.AppendElement(mDrmFormats[aPlane]);
#define ADD_PLANE_ATTRIBS_NV12(plane_idx)                                 \
  attribs.AppendElement(LOCAL_EGL_DMA_BUF_PLANE##plane_idx##_FD_EXT);     \
  attribs.AppendElement(mDmabufFds[aPlane]->GetHandle());                 \
  attribs.AppendElement(LOCAL_EGL_DMA_BUF_PLANE##plane_idx##_OFFSET_EXT); \
  attribs.AppendElement((int)mOffsets[aPlane]);                           \
  attribs.AppendElement(LOCAL_EGL_DMA_BUF_PLANE##plane_idx##_PITCH_EXT);  \
  attribs.AppendElement((int)mStrides[aPlane]);                           \
  if (mBufferModifiers[aPlane] != DRM_FORMAT_MOD_INVALID) {               \
    attribs.AppendElement(                                                \
        LOCAL_EGL_DMA_BUF_PLANE##plane_idx##_MODIFIER_LO_EXT);            \
    attribs.AppendElement(mBufferModifiers[aPlane] & 0xFFFFFFFF);         \
    attribs.AppendElement(                                                \
        LOCAL_EGL_DMA_BUF_PLANE##plane_idx##_MODIFIER_HI_EXT);            \
    attribs.AppendElement(mBufferModifiers[aPlane] >> 32);                \
  }
  ADD_PLANE_ATTRIBS_NV12(0);
#undef ADD_PLANE_ATTRIBS_NV12
  attribs.AppendElement(LOCAL_EGL_NONE);

  LOGDMABUF(
      "Plane %d: attributes: "
      "fd=%d, offset=%u, pitch=%u, modifier=0x%llx, format=0x%x, size=%dx%d",
      aPlane, mDmabufFds[aPlane]->GetHandle(), mOffsets[aPlane],
      mStrides[aPlane], (unsigned long long)mBufferModifiers[aPlane],
      mDrmFormats[aPlane], mWidthAligned[aPlane], mHeightAligned[aPlane]);

  mEGLImage[aPlane] =
      egl->fCreateImage(LOCAL_EGL_NO_CONTEXT, LOCAL_EGL_LINUX_DMA_BUF_EXT,
                        nullptr, attribs.Elements());

  if (mEGLImage[aPlane] == LOCAL_EGL_NO_IMAGE) {
    LOGDMABUF("  EGLImageKHR creation failed, EGL error %s",
              FormatEGLError(egl->mLib->fGetError()).c_str());
    return false;
  }

  aGLContext->fGenTextures(1, &mTexture[aPlane]);
  const ScopedBindTexture savedTex(aGLContext, mTexture[aPlane]);
  aGLContext->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_S,
                             LOCAL_GL_CLAMP_TO_EDGE);
  aGLContext->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_T,
                             LOCAL_GL_CLAMP_TO_EDGE);
  aGLContext->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MAG_FILTER,
                             LOCAL_GL_LINEAR);
  aGLContext->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MIN_FILTER,
                             LOCAL_GL_LINEAR);
  aGLContext->fEGLImageTargetTexture2D(LOCAL_GL_TEXTURE_2D, mEGLImage[aPlane]);

  GLenum glErr = aGLContext->fGetError();

  if (glErr == 0) {
    LOGDMABUF("  Plane %d: zero-copy EGLImageTargetTexture2D succeeded",
              aPlane);
    releaseTextures.release();
    return true;
  }

  if (mBufferModifiers[aPlane] == DRM_FORMAT_MOD_LINEAR &&
      GetFOURCCFormat() == VA_FOURCC_NV12 &&
      CreateTextureViaCopyYUV(aGLContext, aPlane)) {
    LOGDMABUF("  Plane %d: successfully created texture via copy", aPlane);
    releaseTextures.release();
    return true;
  }

  if (mBufferModifiers[aPlane] == DRM_FORMAT_MOD_LINEAR &&
      (GetFOURCCFormat() == VA_FOURCC_P010 ||
       GetFOURCCFormat() == VA_FOURCC_P016) &&
      CreateTextureViaCopyP010(aGLContext, aPlane)) {
    LOGDMABUF("  Plane %d: successfully created P010 texture via copy", aPlane);
    releaseTextures.release();
    return true;
  }

  LOGDMABUF(
      "  CreateTexture failed: EGLImageTargetTexture2D glErr=0x%x, copy "
      "fallback not used or failed",
      glErr);
  return false;
}

bool DMABufSurfaceYUV::CreateTextureViaCopyP010(GLContext* aGLContext,
                                                int aPlane) {
  GLuint srcTex = 0;
  aGLContext->fGenTextures(1, &srcTex);
  aGLContext->fBindTexture(LOCAL_GL_TEXTURE_EXTERNAL, srcTex);
  aGLContext->fTexParameteri(LOCAL_GL_TEXTURE_EXTERNAL, LOCAL_GL_TEXTURE_WRAP_S,
                             LOCAL_GL_CLAMP_TO_EDGE);
  aGLContext->fTexParameteri(LOCAL_GL_TEXTURE_EXTERNAL, LOCAL_GL_TEXTURE_WRAP_T,
                             LOCAL_GL_CLAMP_TO_EDGE);
  aGLContext->fTexParameteri(LOCAL_GL_TEXTURE_EXTERNAL,
                             LOCAL_GL_TEXTURE_MAG_FILTER, LOCAL_GL_LINEAR);
  aGLContext->fTexParameteri(LOCAL_GL_TEXTURE_EXTERNAL,
                             LOCAL_GL_TEXTURE_MIN_FILTER, LOCAL_GL_LINEAR);
  aGLContext->fEGLImageTargetTexture2D(LOCAL_GL_TEXTURE_EXTERNAL,
                                       mEGLImage[aPlane]);
  if (aGLContext->fGetError() != 0) {
    aGLContext->fDeleteTextures(1, &srcTex);
    return false;
  }
  GLenum internalFormat = (aPlane == 0) ? LOCAL_GL_R16 : LOCAL_GL_RG16;
  aGLContext->fBindTexture(LOCAL_GL_TEXTURE_2D, mTexture[aPlane]);
  aGLContext->fTexStorage2D(LOCAL_GL_TEXTURE_2D, 1, internalFormat,
                            mWidth[aPlane], mHeight[aPlane]);
  aGLContext->fCopyImageSubData(srcTex, LOCAL_GL_TEXTURE_EXTERNAL, 0, 0, 0, 0,
                                mTexture[aPlane], LOCAL_GL_TEXTURE_2D, 0, 0, 0,
                                0, mWidth[aPlane], mHeight[aPlane], 1);
  aGLContext->fDeleteTextures(1, &srcTex);
  return aGLContext->fGetError() == 0;
}

bool DMABufSurfaceYUV::CreateTextureViaCopyYUV(GLContext* aGLContext,
                                               int aPlane) {
  GLuint srcTex = 0;
  aGLContext->fGenTextures(1, &srcTex);
  aGLContext->fBindTexture(LOCAL_GL_TEXTURE_EXTERNAL, srcTex);
  aGLContext->fTexParameteri(LOCAL_GL_TEXTURE_EXTERNAL, LOCAL_GL_TEXTURE_WRAP_S,
                             LOCAL_GL_CLAMP_TO_EDGE);
  aGLContext->fTexParameteri(LOCAL_GL_TEXTURE_EXTERNAL, LOCAL_GL_TEXTURE_WRAP_T,
                             LOCAL_GL_CLAMP_TO_EDGE);
  aGLContext->fTexParameteri(LOCAL_GL_TEXTURE_EXTERNAL,
                             LOCAL_GL_TEXTURE_MAG_FILTER, LOCAL_GL_LINEAR);
  aGLContext->fTexParameteri(LOCAL_GL_TEXTURE_EXTERNAL,
                             LOCAL_GL_TEXTURE_MIN_FILTER, LOCAL_GL_LINEAR);
  aGLContext->fEGLImageTargetTexture2D(LOCAL_GL_TEXTURE_EXTERNAL,
                                       mEGLImage[aPlane]);
  if (aGLContext->fGetError() != 0) {
    aGLContext->fDeleteTextures(1, &srcTex);
    return false;
  }
  GLenum internalFormat = (aPlane == 0) ? LOCAL_GL_R8 : LOCAL_GL_RG8;
  aGLContext->fBindTexture(LOCAL_GL_TEXTURE_2D, mTexture[aPlane]);
  aGLContext->fTexStorage2D(LOCAL_GL_TEXTURE_2D, 1, internalFormat,
                            mWidth[aPlane], mHeight[aPlane]);
  aGLContext->fCopyImageSubData(srcTex, LOCAL_GL_TEXTURE_EXTERNAL, 0, 0, 0, 0,
                                mTexture[aPlane], LOCAL_GL_TEXTURE_2D, 0, 0, 0,
                                0, mWidth[aPlane], mHeight[aPlane], 1);
  aGLContext->fDeleteTextures(1, &srcTex);
  return aGLContext->fGetError() == 0;
}

bool DMABufSurfaceYUV::HoldsTexture() {
  for (int i = 0; i < mBufferPlaneCount; i++) {
    if (mTexture[i] || mEGLImage[i]) {
      return true;
    }
  }
  return false;
}

void DMABufSurfaceYUV::ReleaseTextures() {
  LOGDMABUF("DMABufSurfaceYUV::ReleaseTextures() UID %d", mUID);

  FenceDelete();

  if (!HoldsTexture()) {
    return;
  }

  if (!mGL) {
#ifdef NIGHTLY_BUILD
    MOZ_DIAGNOSTIC_ASSERT(mGL, "Missing GL context!");
#else
    NS_WARNING(
        "DMABufSurfaceYUV::ReleaseTextures(): Missing GL context! We're "
        "leaking textures!");
    return;
#endif
  }

  if (!mGL->MakeCurrent()) {
    NS_WARNING(
        "DMABufSurfaceYUV::ReleaseTextures(): MakeCurrent failed. We're "
        "leaking textures!");
    return;
  }

  mGL->fDeleteTextures(DMABUF_BUFFER_PLANES, mTexture);
  std::fill(std::begin(mTexture), std::end(mTexture), 0);

  const auto& gle = gl::GLContextEGL::Cast(mGL);
  const auto& egl = gle->mEgl;
  for (int i = 0; i < mBufferPlaneCount; i++) {
    if (mEGLImage[i] != LOCAL_EGL_NO_IMAGE) {
      egl->fDestroyImage(mEGLImage[i]);
      mEGLImage[i] = LOCAL_EGL_NO_IMAGE;
    }
  }

  mGL = nullptr;
}

bool DMABufSurfaceYUV::VerifyTextureCreation() {
  LOGDMABUF("DMABufSurfaceYUV::VerifyTextureCreation() UID %d", mUID);

  StaticMutexAutoLock lock(sSnapshotContextMutex);
  RefPtr<GLContext> context = ClaimSnapshotGLContext();
  auto release = MakeScopeExit([&] {
    ReleaseTextures();
    ReturnSnapshotGLContext(context);
  });

  for (int i = 0; i < mBufferPlaneCount; i++) {
    if (!CreateTexture(context, i)) {
      LOGDMABUF("  failed to create EGL image!");
      return false;
    }
  }

  LOGDMABUF("  success");
  return true;
}

gfx::SurfaceFormat DMABufSurfaceYUV::GetFormat() {
  switch (mFOURCCFormat) {
    case VA_FOURCC_P010:
      return gfx::SurfaceFormat::P010;
    case VA_FOURCC_P016:
      return gfx::SurfaceFormat::P016;
    case VA_FOURCC_NV12:
      return gfx::SurfaceFormat::NV12;
    case VA_FOURCC_YV12:
    case VA_FOURCC_I420:
      return gfx::SurfaceFormat::YUV420;
    default:
      gfxCriticalNoteOnce << "DMABufSurfaceYUV::GetFormat() unknown format: "
                          << mFOURCCFormat;
      return gfx::SurfaceFormat::UNKNOWN;
  }
}

gfx::SurfaceFormat DMABufSurfaceYUV::GetHWFormat(gfx::SurfaceFormat aSWFormat) {
  switch (aSWFormat) {
    case gfx::SurfaceFormat::YUV420P10:
      return gfx::SurfaceFormat::P010;
    case gfx::SurfaceFormat::YUV420:
      return gfx::SurfaceFormat::NV12;
    default:
      return gfx::SurfaceFormat::UNKNOWN;
  }
}

int DMABufSurfaceYUV::GetTextureCount() { return mBufferPlaneCount; }

void DMABufSurfaceYUV::ReleaseSurface() {
  LOGDMABUF("DMABufSurfaceYUV::ReleaseSurface() UID %d", mUID);
  ReleaseTextures();
  ReleaseDMABuf();
  LogMemorySubYUV(GetUID(),
                  GetUsedMemoryYUV(mFOURCCFormat, mWidth[0], mHeight[0]));
}

nsresult DMABufSurfaceYUV::BuildSurfaceDescriptorBuffer(
    SurfaceDescriptorBuffer& aSdBuffer, Image::BuildSdbFlags aFlags,
    const std::function<MemoryOrShmem(uint32_t)>& aAllocate) {
  LOGDMABUF("DMABufSurfaceYUV::BuildSurfaceDescriptorBuffer UID %d", mUID);

  gfx::IntSize size(GetWidth(), GetHeight());
  const auto format = gfx::SurfaceFormat::B8G8R8A8;

  uint8_t* buffer = nullptr;
  int32_t stride = 0;
  nsresult rv = Image::AllocateSurfaceDescriptorBufferRgb(
      size, format, buffer, aSdBuffer, stride, aAllocate);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    LOGDMABUF("BuildSurfaceDescriptorBuffer allocate descriptor failed");
    return rv;
  }

  if (mGL) {
    return ReadIntoBuffer(mGL, buffer, stride, size, format);
  } else {
    // We're missing active GL context - take a snapshot one.
    StaticMutexAutoLock lock(sSnapshotContextMutex);
    RefPtr<GLContext> context = ClaimSnapshotGLContext();
    auto releaseTextures = mozilla::MakeScopeExit([&] {
      ReleaseTextures();
      ReturnSnapshotGLContext(context);
    });
    return ReadIntoBuffer(context, buffer, stride, size, format);
  }
}

#if 0
// Debugging / testing only
void DMABufSurfaceYUV::ClearPlane(int aPlane, int aValue) {
  if (!MapInternal(0, 0, mWidth[aPlane], mHeight[aPlane], nullptr,
                   GBM_BO_TRANSFER_WRITE, aPlane)) {
    return;
  }
  if ((int)mMappedRegionStride[aPlane] < mWidth[aPlane]) {
    return;
  }

  unsigned short* data = (unsigned short*)mMappedRegion[aPlane];
  for (unsigned int i = 0; i < (mMappedRegionStride[aPlane] * mHeight[aPlane]) >> 1; i++) {
    *data++ = aValue;
  }

  Unmap(aPlane);
}

void DMABufSurfaceYUV::CopyPlane(int aPlane, char* aData) {
  if (!MapInternal(0, 0, mWidth[aPlane], mHeight[aPlane], nullptr,
                   GBM_BO_TRANSFER_WRITE, aPlane)) {
    return;
  }
  if ((int)mMappedRegionStride[aPlane] < mWidth[aPlane]) {
    return;
  }

  /*
    memcpy((char*)mMappedRegion[aPlane], aData,
            mMappedRegionStride[aPlane] * mHeight[aPlane]);
  */


  unsigned short* dst = (unsigned short*)mMappedRegion[aPlane];
  unsigned short* src = (unsigned short*)aData;
  for (unsigned int i = 0; i < (mMappedRegionStride[aPlane] * mHeight[aPlane]) >> 1; i++) {
    // YUV -> P010 biteshift
    *dst++ = *src++ << 6;
  }

  Unmap(aPlane);
}
#endif

#ifdef MOZ_WAYLAND
wl_buffer* DMABufSurfaceYUV::CreateWlBuffer() {
  nsWaylandDisplay* waylandDisplay = widget::WaylandDisplayGet();
  auto* dmabuf = waylandDisplay->GetDmabuf();
  if (!dmabuf) {
    gfxCriticalNoteOnce
        << "DMABufSurfaceYUV::CreateWlBuffer(): Missing DMABuf support!";
    return nullptr;
  }

  LOGDMABUF(
      "DMABufSurfaceYUV::CreateWlBuffer() UID %d format %s size [%d x %d]",
      mUID, GetSurfaceTypeName(), GetWidth(), GetHeight());

  struct zwp_linux_buffer_params_v1* params =
      zwp_linux_dmabuf_v1_create_params(dmabuf);
  for (int i = 0; i < GetTextureCount(); i++) {
    LOGDMABUF("  layer [%d] modifier %" PRIx64, i, mBufferModifiers[i]);
    zwp_linux_buffer_params_v1_add(
        params, mDmabufFds[i]->GetHandle(), i, mOffsets[i], mStrides[i],
        mBufferModifiers[i] >> 32, mBufferModifiers[i] & 0xffffffff);
  }

  // The format passed to wayland needs to be a DRM_FORMAT_* enum.  These are
  // largely the same as VA_FOURCC_* values except for I420/YUV420
  uint32_t format = GetFOURCCFormat();
  if (format == VA_FOURCC_I420) {
    format = DRM_FORMAT_YUV420;
  }

  LOGDMABUF(
      "  zwp_linux_buffer_params_v1_create_immed() [%d x %d], fourcc [%x]",
      GetWidth(), GetHeight(), format);
  wl_buffer* buffer = zwp_linux_buffer_params_v1_create_immed(
      params, GetWidth(), GetHeight(), format, 0);
  if (!buffer) {
    LOGDMABUF(
        "  zwp_linux_buffer_params_v1_create_immed(): failed to create "
        "wl_buffer!");
  } else {
    LOGDMABUF("  created wl_buffer [%p]", buffer);
  }

  return buffer;
}
#endif

#if 0
void DMABufSurfaceYUV::ClearPlane(int aPlane) {
  if (!MapInternal(0, 0, mWidth[aPlane], mHeight[aPlane], nullptr,
                   GBM_BO_TRANSFER_WRITE, aPlane)) {
    return;
  }
  if ((int)mMappedRegionStride[aPlane] < mWidth[aPlane]) {
    return;
  }
  memset((char*)mMappedRegion[aPlane], 0,
         mMappedRegionStride[aPlane] * mHeight[aPlane]);
  Unmap(aPlane);
}
#endif

#ifdef MOZ_LOGGING
void DMABufSurfaceYUV::DumpToFile(const char* aFile) {
  RefPtr<gfx::DataSourceSurface> surf = GetAsSourceSurface();
  gfxUtils::WriteAsPNG(surf, aFile);
}
#endif

Messung V0.5 in Prozent
C=98 H=95 G=96

¤ Dauer der Verarbeitung: 0.50 Sekunden  (vorverarbeitet am  2026-09-29) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=1126438
#Domains=1867298