2208 lines
70 KiB
C++
2208 lines
70 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
/* 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>
|
|
|
|
// DMABufLibWrapper 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>
|
|
|
|
#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"
|
|
|
|
/*
|
|
TODO:
|
|
- DRM device selection:
|
|
https://lists.freedesktop.org/archives/wayland-devel/2018-November/039660.html
|
|
- Use uint64_t mBufferModifiers / mGbmBufferObject for RGBA
|
|
- Remove file descriptors open/close?
|
|
*/
|
|
|
|
/* 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 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);
|
|
}
|
|
}
|
|
|
|
MOZ_RUNINIT static RefPtr<GLContext> sSnapshotContext;
|
|
static StaticMutex sSnapshotContextMutex MOZ_UNANNOTATED;
|
|
static Atomic<int> gNewSurfaceUID(1);
|
|
|
|
// We should release all resources allocated by SnapshotGLContext before
|
|
// ReturnSnapshotGLContext() call. Otherwise DMABufSurface references
|
|
// SnapshotGLContext and may colide with other SnapshotGLContext operations.
|
|
RefPtr<GLContext> 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()) {
|
|
LOGDMABUFS("ClaimSnapshotGLContext: Failed to make GLContext current.");
|
|
return nullptr;
|
|
}
|
|
return sSnapshotContext;
|
|
}
|
|
|
|
void ReturnSnapshotGLContext(RefPtr<GLContext> aGLContext) {
|
|
// direct eglMakeCurrent() call breaks current context caching so make sure
|
|
// it's not used.
|
|
MOZ_ASSERT(!aGLContext->mUseTLSIsCurrent);
|
|
if (!aGLContext->IsCurrent()) {
|
|
LOGDMABUFS("ReturnSnapshotGLContext() failed, is not current!");
|
|
return;
|
|
}
|
|
const auto& gle = gl::GLContextEGL::Cast(aGLContext);
|
|
const auto& egl = gle->mEgl;
|
|
egl->fMakeCurrent(EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
|
}
|
|
|
|
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)) {
|
|
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) {
|
|
gfxCriticalNote << "DMABufSurface::UseDmaBufExportExtension(): "
|
|
"MESA_image_dma_buf import/export extensions!";
|
|
}
|
|
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") {
|
|
}
|
|
|
|
DMABufSurface::~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();
|
|
}
|
|
|
|
void DMABufSurface::FenceDelete() {
|
|
if (mSyncFd) {
|
|
mSyncFd = nullptr;
|
|
}
|
|
|
|
if (!mGL) {
|
|
return;
|
|
}
|
|
const auto& gle = gl::GLContextEGL::Cast(mGL);
|
|
const auto& egl = gle->mEgl;
|
|
|
|
if (mSync) {
|
|
egl->fDestroySync(mSync);
|
|
mSync = nullptr;
|
|
}
|
|
}
|
|
|
|
void DMABufSurface::FenceSet() {
|
|
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)) {
|
|
FenceDelete();
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
// ANDROID_native_fence_sync may not be supported so call glFinish()
|
|
// as a slow path.
|
|
mGL->fFinish();
|
|
}
|
|
|
|
void DMABufSurface::FenceWait() {
|
|
if (!mGL || !mSyncFd) {
|
|
MOZ_DIAGNOSTIC_ASSERT(mGL,
|
|
"DMABufSurface::FenceWait() missing GL context!");
|
|
return;
|
|
}
|
|
|
|
const auto& gle = gl::GLContextEGL::Cast(mGL);
|
|
const auto& egl = gle->mEgl;
|
|
auto syncFd = mSyncFd->ClonePlatformHandle();
|
|
// No need to try mSyncFd twice.
|
|
mSyncFd = nullptr;
|
|
|
|
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) {
|
|
MOZ_ASSERT(false, "DMABufSurface::FenceWait(): Failed to create GLFence!");
|
|
return;
|
|
}
|
|
|
|
// syncFd is owned by GLFence so clear local reference to avoid double.
|
|
Unused << syncFd.release();
|
|
|
|
egl->fClientWaitSync(sync, 0, LOCAL_EGL_FOREVER);
|
|
egl->fDestroySync(sync);
|
|
}
|
|
|
|
void DMABufSurface::MaybeSemaphoreWait(GLuint aGlTexture) {
|
|
MOZ_ASSERT(aGlTexture);
|
|
|
|
if (!mSemaphoreFd) {
|
|
return;
|
|
}
|
|
|
|
if (!mGL) {
|
|
MOZ_DIAGNOSTIC_ASSERT(mGL,
|
|
"DMABufSurface::SemaphoreWait() missing GL context!");
|
|
return;
|
|
}
|
|
|
|
if (!mGL->IsExtensionSupported(gl::GLContext::EXT_semaphore) ||
|
|
!mGL->IsExtensionSupported(gl::GLContext::EXT_semaphore_fd)) {
|
|
MOZ_ASSERT_UNREACHABLE("unexpected to be called");
|
|
gfxCriticalNoteOnce << "EXT_semaphore_fd is not suppored";
|
|
return;
|
|
}
|
|
|
|
auto fd = mSemaphoreFd->ClonePlatformHandle();
|
|
// No need to try mSemaphoreFd twice.
|
|
mSemaphoreFd = nullptr;
|
|
|
|
GLuint semaphoreHandle = 0;
|
|
mGL->fGenSemaphoresEXT(1, &semaphoreHandle);
|
|
mGL->fImportSemaphoreFdEXT(semaphoreHandle,
|
|
LOCAL_GL_HANDLE_TYPE_OPAQUE_FD_EXT, fd.release());
|
|
auto error = mGL->fGetError();
|
|
if (error != LOCAL_GL_NO_ERROR) {
|
|
gfxCriticalNoteOnce << "glImportSemaphoreFdEXT failed: " << error;
|
|
return;
|
|
}
|
|
|
|
GLenum srcLayout = LOCAL_GL_LAYOUT_COLOR_ATTACHMENT_EXT;
|
|
mGL->fWaitSemaphoreEXT(semaphoreHandle, 0, nullptr, 1, &aGlTexture,
|
|
&srcLayout);
|
|
error = mGL->fGetError();
|
|
if (error != LOCAL_GL_NO_ERROR) {
|
|
gfxCriticalNoteOnce << "glWaitSemaphoreEXT failed: " << error;
|
|
return;
|
|
}
|
|
}
|
|
|
|
bool DMABufSurface::OpenFileDescriptors() {
|
|
for (int i = 0; i < mBufferPlaneCount; i++) {
|
|
if (!OpenFileDescriptorForPlane(i)) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void DMABufSurface::CloseFileDescriptors() {
|
|
for (int i = 0; i < DMABUF_BUFFER_PLANES; i++) {
|
|
if (mDmabufFds[i]) {
|
|
mDmabufFds[i] = 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, 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() { ReleaseSurface(); }
|
|
|
|
bool DMABufSurfaceRGBA::OpenFileDescriptorForPlane(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 = 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) {
|
|
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)) {
|
|
return false;
|
|
}
|
|
return CreateExport(context, aWidth, aHeight, aDMABufSurfaceFlags);
|
|
}
|
|
|
|
if (gfx::gfxVars::UseDMABufSurfaceExport()) {
|
|
if (!UseDmaBufExportExtension(aGLContext)) {
|
|
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?");
|
|
|
|
if (!GetDMABufDevice()->GetGbmDevice()) {
|
|
LOGDMABUF("DMABufSurfaceRGBA::Create(): Missing GbmDevice!");
|
|
return false;
|
|
}
|
|
|
|
mWidth = aWidth;
|
|
mHeight = aHeight;
|
|
mFOURCCFormat = aFormat->GetFormat();
|
|
|
|
LOGDMABUF(
|
|
"DMABufSurfaceRGBA::Create() 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(
|
|
GetDMABufDevice()->GetGbmDevice(), 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(GetDMABufDevice()->GetGbmDevice(), 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()) {
|
|
LOGDMABUF(" Failed to open Fd!");
|
|
return false;
|
|
}
|
|
|
|
LOGDMABUF(" Success\n");
|
|
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);
|
|
|
|
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();
|
|
return true;
|
|
}
|
|
|
|
bool DMABufSurfaceRGBA::Create(
|
|
RefPtr<mozilla::gfx::FileHandleWrapper>&& aFd,
|
|
const mozilla::webgpu::ffi::WGPUDMABufInfo& aDMABufInfo, int aWidth,
|
|
int aHeight) {
|
|
LOGDMABUF("DMABufSurfaceRGBA::Create() 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];
|
|
}
|
|
|
|
LOGDMABUF(" imported size %d x %d format %x planes %d modifiers %" PRIx64,
|
|
mWidth, mHeight, mFOURCCFormat, mBufferPlaneCount, mBufferModifier);
|
|
return true;
|
|
}
|
|
|
|
bool DMABufSurfaceRGBA::ImportSurfaceDescriptor(
|
|
const SurfaceDescriptor& aDesc) {
|
|
const SurfaceDescriptorDMABuf& desc = aDesc.get_SurfaceDescriptorDMABuf();
|
|
|
|
mFOURCCFormat = desc.fourccFormat();
|
|
mWidth = desc.width()[0];
|
|
mHeight = desc.height()[0];
|
|
mBufferPlaneCount = desc.fds().Length();
|
|
mGbmBufferFlags = desc.flags();
|
|
mBufferModifier = desc.modifier()[0];
|
|
MOZ_RELEASE_ASSERT(mBufferPlaneCount <= DMABUF_BUFFER_PLANES);
|
|
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();
|
|
}
|
|
|
|
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);
|
|
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, fenceFDs, mUID,
|
|
mCanRecycle ? getpid() : 0, refCountFDs,
|
|
/* semaphoreFd */ nullptr);
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
void DMABufSurfaceRGBA::ReleaseTextures() {
|
|
LOGDMABUF("DMABufSurfaceRGBA::ReleaseTextures() UID %d\n", mUID);
|
|
FenceDelete();
|
|
|
|
if (!mTexture && !mEGLImage) {
|
|
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() {
|
|
MOZ_ASSERT(!IsMapped(), "We can't release mapped buffer!");
|
|
|
|
ReleaseTextures();
|
|
ReleaseDMABuf();
|
|
}
|
|
|
|
#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
|
|
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
|
|
|
|
#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) {
|
|
RefPtr<DMABufSurfaceRGBA> surf = new 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) {
|
|
RefPtr<DMABufSurfaceRGBA> surf = new 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) {
|
|
RefPtr<DMABufSurfaceYUV> surf = new 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) {
|
|
RefPtr<DMABufSurfaceYUV> surf = new 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();
|
|
}
|
|
|
|
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() { ReleaseSurface(); }
|
|
|
|
bool DMABufSurfaceYUV::OpenFileDescriptorForPlane(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];
|
|
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]);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
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));
|
|
}
|
|
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 (aDesc.objects[object].fd != -1) {
|
|
close(aDesc.objects[object].fd);
|
|
aDesc.objects[object].fd = -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool DMABufSurfaceYUV::CreateYUVPlaneGBM(int aPlane, DRMFormat* aFormat) {
|
|
LOGDMABUF(
|
|
"DMABufSurfaceYUV::CreateYUVPlaneGBM() UID %d size %d x %d plane %d",
|
|
mUID, mWidth[aPlane], mHeight[aPlane], aPlane);
|
|
|
|
if (!GetDMABufDevice()->GetGbmDevice()) {
|
|
LOGDMABUF(" Missing GbmDevice!");
|
|
return false;
|
|
}
|
|
|
|
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(
|
|
GetDMABufDevice()->GetGbmDevice(), 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(
|
|
GetDMABufDevice()->GetGbmDevice(), mWidth[aPlane], mHeight[aPlane],
|
|
mDrmFormats[aPlane], mBufferModifiers + aPlane, 1, mGbmBufferFlags);
|
|
}
|
|
if (!mGbmBufferObject[aPlane]) {
|
|
LOGDMABUF(" Creating without modifiers");
|
|
mGbmBufferObject[aPlane] = GbmLib::Create(
|
|
GetDMABufDevice()->GetGbmDevice(), 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(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);
|
|
|
|
mGL = aGLContext;
|
|
auto releaseTextures = MakeScopeExit([&] { ReleaseTextures(); });
|
|
|
|
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";
|
|
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)) {
|
|
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);
|
|
}
|
|
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;
|
|
}
|
|
}
|
|
|
|
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();
|
|
mSurfaceType = SURFACE_YUV;
|
|
mFOURCCFormat = aDesc.fourccFormat();
|
|
mColorSpace = aDesc.yUVColorSpace();
|
|
mColorRange = aDesc.colorRange();
|
|
mColorPrimaries = aDesc.colorPrimaries();
|
|
mTransferFunction = aDesc.transferFunction();
|
|
mGbmBufferFlags = aDesc.flags();
|
|
mUID = aDesc.uid();
|
|
mPID = aDesc.pid();
|
|
|
|
LOGDMABUF("DMABufSurfaceYUV::ImportSurfaceDescriptor() UID %d", mUID);
|
|
|
|
MOZ_RELEASE_ASSERT(mBufferPlaneCount <= DMABUF_BUFFER_PLANES);
|
|
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.refCount().Length() > 0) {
|
|
GlobalRefCountImport(aDesc.refCount()[0].ClonePlatformHandle().release());
|
|
}
|
|
|
|
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,
|
|
fenceFDs, mUID, mCanRecycle ? getpid() : 0, refCountFDs,
|
|
/* semaphoreFd */ nullptr);
|
|
return true;
|
|
}
|
|
|
|
bool DMABufSurfaceYUV::CreateTexture(GLContext* aGLContext, int aPlane) {
|
|
if (mTexture[aPlane]) {
|
|
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(!mGL || mGL == aGLContext);
|
|
|
|
mGL = aGLContext;
|
|
auto releaseTextures = MakeScopeExit([&] { ReleaseTextures(); });
|
|
|
|
if (!aGLContext->MakeCurrent()) {
|
|
LOGDMABUF(" Failed to make GL context current.");
|
|
return false;
|
|
}
|
|
|
|
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);
|
|
|
|
const auto& gle = gl::GLContextEGL::Cast(aGLContext);
|
|
const auto& egl = gle->mEgl;
|
|
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]);
|
|
|
|
releaseTextures.release();
|
|
return true;
|
|
}
|
|
|
|
void DMABufSurfaceYUV::ReleaseTextures() {
|
|
LOGDMABUF("DMABufSurfaceYUV::ReleaseTextures() UID %d", mUID);
|
|
|
|
FenceDelete();
|
|
|
|
bool textureActive = false;
|
|
for (int i = 0; i < mBufferPlaneCount; i++) {
|
|
if (mTexture[i] || mEGLImage[i]) {
|
|
textureActive = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!textureActive) {
|
|
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);
|
|
for (int i = 0; i < DMABUF_BUFFER_PLANES; i++) {
|
|
mTexture[i] = 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();
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
# include "gfxUtils.h"
|
|
|
|
void DMABufSurfaceYUV::DumpToFile(const char* aFile) {
|
|
RefPtr<gfx::DataSourceSurface> surf = GetAsSourceSurface();
|
|
gfxUtils::WriteAsPNG(surf, aFile);
|
|
}
|
|
#endif
|