зеркало из https://github.com/mozilla/gecko-dev.git
860 строки
27 KiB
C++
860 строки
27 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 "WaylandDMABufSurface.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 "mozilla/widget/gbm.h"
|
|
#include "mozilla/widget/va_drmcommon.h"
|
|
#include "GLContextTypes.h" // for GLContext, etc
|
|
#include "GLContextEGL.h"
|
|
#include "GLContextProvider.h"
|
|
|
|
#include "mozilla/layers/LayersSurfaces.h"
|
|
|
|
/*
|
|
TODO:
|
|
DRM device selection:
|
|
https://lists.freedesktop.org/archives/wayland-devel/2018-November/039660.html
|
|
*/
|
|
|
|
/* 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;
|
|
|
|
#ifndef DRM_FORMAT_MOD_INVALID
|
|
# define DRM_FORMAT_MOD_INVALID ((1ULL << 56) - 1)
|
|
#endif
|
|
#define BUFFER_FLAGS 0
|
|
|
|
#ifndef GBM_BO_USE_TEXTURING
|
|
# define GBM_BO_USE_TEXTURING (1 << 5)
|
|
#endif
|
|
|
|
#ifndef VA_FOURCC_NV12
|
|
# define VA_FOURCC_NV12 0x3231564E
|
|
#endif
|
|
|
|
WaylandDMABufSurface::WaylandDMABufSurface(SurfaceType aSurfaceType)
|
|
: mSurfaceType(aSurfaceType),
|
|
mBufferModifier(DRM_FORMAT_MOD_INVALID),
|
|
mBufferPlaneCount(0),
|
|
mDrmFormats(),
|
|
mStrides(),
|
|
mOffsets(),
|
|
mSync(0) {
|
|
for (auto& slot : mDmabufFds) {
|
|
slot = -1;
|
|
}
|
|
}
|
|
|
|
already_AddRefed<WaylandDMABufSurface>
|
|
WaylandDMABufSurface::CreateDMABufSurface(
|
|
const mozilla::layers::SurfaceDescriptor& aDesc) {
|
|
const SurfaceDescriptorDMABuf& desc = aDesc.get_SurfaceDescriptorDMABuf();
|
|
RefPtr<WaylandDMABufSurface> surf;
|
|
|
|
switch (desc.bufferType()) {
|
|
case SURFACE_RGBA:
|
|
surf = new WaylandDMABufSurfaceRGBA();
|
|
break;
|
|
case SURFACE_NV12:
|
|
surf = new WaylandDMABufSurfaceNV12();
|
|
break;
|
|
default:
|
|
return nullptr;
|
|
}
|
|
|
|
if (!surf->Create(desc)) {
|
|
return nullptr;
|
|
}
|
|
return surf.forget();
|
|
}
|
|
|
|
void WaylandDMABufSurface::FenceDelete() {
|
|
auto* egl = gl::GLLibraryEGL::Get();
|
|
|
|
if (mSync) {
|
|
// We can't call this unless we have the ext, but we will always have
|
|
// the ext if we have something to destroy.
|
|
egl->fDestroySync(egl->Display(), mSync);
|
|
mSync = nullptr;
|
|
}
|
|
}
|
|
|
|
void WaylandDMABufSurface::FenceSet() {
|
|
if (!mGL || !mGL->MakeCurrent()) {
|
|
return;
|
|
}
|
|
|
|
auto* egl = gl::GLLibraryEGL::Get();
|
|
if (egl->IsExtensionSupported(GLLibraryEGL::KHR_fence_sync) &&
|
|
egl->IsExtensionSupported(GLLibraryEGL::ANDROID_native_fence_sync)) {
|
|
if (mSync) {
|
|
MOZ_ALWAYS_TRUE(egl->fDestroySync(egl->Display(), mSync));
|
|
mSync = nullptr;
|
|
}
|
|
|
|
mSync = egl->fCreateSync(egl->Display(),
|
|
LOCAL_EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr);
|
|
if (mSync) {
|
|
mGL->fFlush();
|
|
return;
|
|
}
|
|
}
|
|
|
|
// ANDROID_native_fence_sync may not be supported so call glFinish()
|
|
// as a slow path.
|
|
mGL->fFinish();
|
|
}
|
|
|
|
void WaylandDMABufSurface::FenceWait() {
|
|
auto* egl = gl::GLLibraryEGL::Get();
|
|
|
|
// Wait on the fence, because presumably we're going to want to read this
|
|
// surface
|
|
if (mSync) {
|
|
egl->fClientWaitSync(egl->Display(), mSync, 0, LOCAL_EGL_FOREVER);
|
|
}
|
|
}
|
|
|
|
bool WaylandDMABufSurface::FenceCreate(int aFd) {
|
|
MOZ_ASSERT(aFd > 0);
|
|
|
|
auto* egl = gl::GLLibraryEGL::Get();
|
|
const EGLint attribs[] = {LOCAL_EGL_SYNC_NATIVE_FENCE_FD_ANDROID, aFd,
|
|
LOCAL_EGL_NONE};
|
|
mSync = egl->fCreateSync(egl->Display(), LOCAL_EGL_SYNC_NATIVE_FENCE_ANDROID,
|
|
attribs);
|
|
if (!mSync) {
|
|
MOZ_ASSERT(false, "Failed to create GLFence!");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void WaylandDMABufSurfaceRGBA::SetWLBuffer(struct wl_buffer* aWLBuffer) {
|
|
MOZ_ASSERT(mWLBuffer == nullptr, "WLBuffer already assigned!");
|
|
mWLBuffer = aWLBuffer;
|
|
}
|
|
|
|
wl_buffer* WaylandDMABufSurfaceRGBA::GetWLBuffer() { return mWLBuffer; }
|
|
|
|
static void buffer_release(void* data, wl_buffer* buffer) {
|
|
auto surface = reinterpret_cast<WaylandDMABufSurfaceRGBA*>(data);
|
|
surface->WLBufferDetach();
|
|
}
|
|
|
|
static const struct wl_buffer_listener buffer_listener = {buffer_release};
|
|
|
|
static void buffer_created(void* data,
|
|
struct zwp_linux_buffer_params_v1* params,
|
|
struct wl_buffer* new_buffer) {
|
|
auto surface = static_cast<WaylandDMABufSurfaceRGBA*>(data);
|
|
|
|
surface->SetWLBuffer(new_buffer);
|
|
|
|
nsWaylandDisplay* display = WaylandDisplayGet();
|
|
/* When not using explicit synchronization listen to wl_buffer.release
|
|
* for release notifications, otherwise we are going to use
|
|
* zwp_linux_buffer_release_v1. */
|
|
if (!display->IsExplicitSyncEnabled()) {
|
|
wl_buffer_add_listener(new_buffer, &buffer_listener, surface);
|
|
}
|
|
zwp_linux_buffer_params_v1_destroy(params);
|
|
}
|
|
|
|
static void buffer_create_failed(void* data,
|
|
struct zwp_linux_buffer_params_v1* params) {
|
|
zwp_linux_buffer_params_v1_destroy(params);
|
|
}
|
|
|
|
static const struct zwp_linux_buffer_params_v1_listener params_listener = {
|
|
buffer_created, buffer_create_failed};
|
|
|
|
WaylandDMABufSurfaceRGBA::WaylandDMABufSurfaceRGBA()
|
|
: WaylandDMABufSurface(SURFACE_RGBA),
|
|
mSurfaceFlags(0),
|
|
mWidth(0),
|
|
mHeight(0),
|
|
mGmbFormat(nullptr),
|
|
mWLBuffer(nullptr),
|
|
mMappedRegion(nullptr),
|
|
mMappedRegionStride(0),
|
|
mGbmBufferObject(nullptr),
|
|
mGbmBufferFlags(0),
|
|
mEGLImage(LOCAL_EGL_NO_IMAGE),
|
|
mTexture(0),
|
|
mWLBufferAttached(false),
|
|
mFastWLBufferCreation(true) {}
|
|
|
|
WaylandDMABufSurfaceRGBA::~WaylandDMABufSurfaceRGBA() { ReleaseSurface(); }
|
|
|
|
bool WaylandDMABufSurfaceRGBA::Create(int aWidth, int aHeight,
|
|
int aWaylandDMABufSurfaceFlags) {
|
|
MOZ_RELEASE_ASSERT(WaylandDisplayGet());
|
|
MOZ_ASSERT(mGbmBufferObject == nullptr, "Already created?");
|
|
|
|
mSurfaceFlags = aWaylandDMABufSurfaceFlags;
|
|
mWidth = aWidth;
|
|
mHeight = aHeight;
|
|
|
|
nsWaylandDisplay* display = WaylandDisplayGet();
|
|
mGmbFormat = display->GetGbmFormat(mSurfaceFlags & DMABUF_ALPHA);
|
|
if (!mGmbFormat) {
|
|
// Requested DRM format is not supported.
|
|
return false;
|
|
}
|
|
|
|
bool useModifiers = (aWaylandDMABufSurfaceFlags & DMABUF_USE_MODIFIERS) &&
|
|
mGmbFormat->mModifiersCount > 0;
|
|
if (useModifiers) {
|
|
mGbmBufferObject = nsGbmLib::CreateWithModifiers(
|
|
display->GetGbmDevice(), mWidth, mHeight, mGmbFormat->mFormat,
|
|
mGmbFormat->mModifiers, mGmbFormat->mModifiersCount);
|
|
if (mGbmBufferObject) {
|
|
mBufferModifier = nsGbmLib::GetModifier(mGbmBufferObject);
|
|
}
|
|
}
|
|
|
|
// Create without modifiers - use plain/linear format.
|
|
if (!mGbmBufferObject) {
|
|
mGbmBufferFlags = (GBM_BO_USE_SCANOUT | GBM_BO_USE_LINEAR);
|
|
if (mSurfaceFlags & DMABUF_CREATE_WL_BUFFER) {
|
|
mGbmBufferFlags |= GBM_BO_USE_RENDERING;
|
|
} else if (mSurfaceFlags & DMABUF_TEXTURE) {
|
|
mGbmBufferFlags |= GBM_BO_USE_TEXTURING;
|
|
}
|
|
|
|
if (!nsGbmLib::DeviceIsFormatSupported(
|
|
display->GetGbmDevice(), mGmbFormat->mFormat, mGbmBufferFlags)) {
|
|
mGbmBufferFlags &= ~GBM_BO_USE_SCANOUT;
|
|
}
|
|
|
|
mGbmBufferObject =
|
|
nsGbmLib::Create(display->GetGbmDevice(), mWidth, mHeight,
|
|
mGmbFormat->mFormat, mGbmBufferFlags);
|
|
|
|
mBufferModifier = DRM_FORMAT_MOD_INVALID;
|
|
}
|
|
|
|
if (!mGbmBufferObject) {
|
|
return false;
|
|
}
|
|
|
|
if (mBufferModifier != DRM_FORMAT_MOD_INVALID) {
|
|
mBufferPlaneCount = nsGbmLib::GetPlaneCount(mGbmBufferObject);
|
|
if (mBufferPlaneCount > DMABUF_BUFFER_PLANES) {
|
|
NS_WARNING("There's too many dmabuf planes!");
|
|
ReleaseSurface();
|
|
return false;
|
|
}
|
|
|
|
for (int i = 0; i < mBufferPlaneCount; i++) {
|
|
uint32_t handle = nsGbmLib::GetHandleForPlane(mGbmBufferObject, i).u32;
|
|
int ret = nsGbmLib::DrmPrimeHandleToFD(display->GetGbmDeviceFd(), handle,
|
|
0, &mDmabufFds[i]);
|
|
if (ret < 0 || mDmabufFds[i] < 0) {
|
|
ReleaseSurface();
|
|
return false;
|
|
}
|
|
mStrides[i] = nsGbmLib::GetStrideForPlane(mGbmBufferObject, i);
|
|
mOffsets[i] = nsGbmLib::GetOffset(mGbmBufferObject, i);
|
|
}
|
|
} else {
|
|
mBufferPlaneCount = 1;
|
|
mStrides[0] = nsGbmLib::GetStride(mGbmBufferObject);
|
|
mDmabufFds[0] = nsGbmLib::GetFd(mGbmBufferObject);
|
|
if (mDmabufFds[0] < 0) {
|
|
ReleaseSurface();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (mSurfaceFlags & DMABUF_CREATE_WL_BUFFER) {
|
|
return CreateWLBuffer();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void WaylandDMABufSurfaceRGBA::ImportSurfaceDescriptor(
|
|
const SurfaceDescriptor& aDesc) {
|
|
const SurfaceDescriptorDMABuf& desc = aDesc.get_SurfaceDescriptorDMABuf();
|
|
|
|
mWidth = desc.width()[0];
|
|
mHeight = desc.height()[0];
|
|
mBufferModifier = desc.modifier();
|
|
if (mBufferModifier != DRM_FORMAT_MOD_INVALID) {
|
|
mGmbFormat = WaylandDisplayGet()->GetExactGbmFormat(desc.format()[0]);
|
|
} else {
|
|
mDrmFormats[0] = desc.format()[0];
|
|
}
|
|
mBufferPlaneCount = desc.fds().Length();
|
|
mGbmBufferFlags = desc.flags();
|
|
MOZ_RELEASE_ASSERT(mBufferPlaneCount <= DMABUF_BUFFER_PLANES);
|
|
|
|
for (int i = 0; i < mBufferPlaneCount; i++) {
|
|
mDmabufFds[i] = desc.fds()[i].ClonePlatformHandle().release();
|
|
mStrides[i] = desc.strides()[i];
|
|
mOffsets[i] = desc.offsets()[i];
|
|
}
|
|
|
|
if (desc.fence().Length() > 0) {
|
|
int fd = desc.fence()[0].ClonePlatformHandle().release();
|
|
if (!FenceCreate(fd)) {
|
|
close(fd);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool WaylandDMABufSurfaceRGBA::Create(const SurfaceDescriptor& aDesc) {
|
|
ImportSurfaceDescriptor(aDesc);
|
|
return true;
|
|
}
|
|
|
|
bool WaylandDMABufSurfaceRGBA::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> format;
|
|
AutoTArray<ipc::FileDescriptor, 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<ipc::FileDescriptor, 1> fenceFDs;
|
|
|
|
width.AppendElement(mWidth);
|
|
height.AppendElement(mHeight);
|
|
format.AppendElement(mGmbFormat->mFormat);
|
|
for (int i = 0; i < mBufferPlaneCount; i++) {
|
|
fds.AppendElement(ipc::FileDescriptor(mDmabufFds[i]));
|
|
strides.AppendElement(mStrides[i]);
|
|
offsets.AppendElement(mOffsets[i]);
|
|
}
|
|
|
|
if (mSync) {
|
|
auto* egl = gl::GLLibraryEGL::Get();
|
|
fenceFDs.AppendElement(ipc::FileDescriptor(
|
|
egl->fDupNativeFenceFDANDROID(egl->Display(), mSync)));
|
|
}
|
|
|
|
aOutDescriptor = SurfaceDescriptorDMABuf(
|
|
mSurfaceType, mBufferModifier, mGbmBufferFlags, fds, width, height,
|
|
format, strides, offsets, GetYUVColorSpace(), fenceFDs);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool WaylandDMABufSurfaceRGBA::CreateWLBuffer() {
|
|
nsWaylandDisplay* display = WaylandDisplayGet();
|
|
if (!display->GetDmabuf()) {
|
|
return false;
|
|
}
|
|
|
|
struct zwp_linux_buffer_params_v1* params =
|
|
zwp_linux_dmabuf_v1_create_params(display->GetDmabuf());
|
|
for (int i = 0; i < mBufferPlaneCount; i++) {
|
|
zwp_linux_buffer_params_v1_add(params, mDmabufFds[i], i, mOffsets[i],
|
|
mStrides[i], mBufferModifier >> 32,
|
|
mBufferModifier & 0xffffffff);
|
|
}
|
|
zwp_linux_buffer_params_v1_add_listener(params, ¶ms_listener, this);
|
|
|
|
if (mFastWLBufferCreation) {
|
|
mWLBuffer = zwp_linux_buffer_params_v1_create_immed(
|
|
params, mWidth, mHeight, mGmbFormat->mFormat, BUFFER_FLAGS);
|
|
/* When not using explicit synchronization listen to
|
|
* wl_buffer.release for release notifications, otherwise we
|
|
* are going to use zwp_linux_buffer_release_v1. */
|
|
if (!display->IsExplicitSyncEnabled()) {
|
|
wl_buffer_add_listener(mWLBuffer, &buffer_listener, this);
|
|
}
|
|
} else {
|
|
zwp_linux_buffer_params_v1_create(params, mWidth, mHeight,
|
|
mGmbFormat->mFormat, BUFFER_FLAGS);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool WaylandDMABufSurfaceRGBA::CreateTexture(GLContext* aGLContext,
|
|
int aPlane) {
|
|
MOZ_ASSERT(!mEGLImage && !mTexture, "EGLImage is already created!");
|
|
|
|
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);
|
|
if (mGmbFormat) {
|
|
attribs.AppendElement(mGmbFormat->mFormat);
|
|
} else {
|
|
attribs.AppendElement(mDrmFormats[0]);
|
|
}
|
|
#define ADD_PLANE_ATTRIBS(plane_idx) \
|
|
{ \
|
|
attribs.AppendElement(LOCAL_EGL_DMA_BUF_PLANE##plane_idx##_FD_EXT); \
|
|
attribs.AppendElement(mDmabufFds[plane_idx]); \
|
|
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);
|
|
|
|
auto* egl = gl::GLLibraryEGL::Get();
|
|
mEGLImage = egl->fCreateImage(egl->Display(), LOCAL_EGL_NO_CONTEXT,
|
|
LOCAL_EGL_LINUX_DMA_BUF_EXT, nullptr,
|
|
attribs.Elements());
|
|
if (mEGLImage == LOCAL_EGL_NO_IMAGE) {
|
|
NS_WARNING("EGLImageKHR creation failed");
|
|
return false;
|
|
}
|
|
|
|
aGLContext->MakeCurrent();
|
|
aGLContext->fGenTextures(1, &mTexture);
|
|
aGLContext->fBindTexture(LOCAL_GL_TEXTURE_2D, 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);
|
|
mGL = aGLContext;
|
|
return true;
|
|
}
|
|
|
|
void WaylandDMABufSurfaceRGBA::ReleaseTextures() {
|
|
FenceDelete();
|
|
|
|
if (mTexture && mGL->MakeCurrent()) {
|
|
mGL->fDeleteTextures(1, &mTexture);
|
|
mTexture = 0;
|
|
mGL = nullptr;
|
|
}
|
|
|
|
if (mEGLImage) {
|
|
auto* egl = gl::GLLibraryEGL::Get();
|
|
egl->fDestroyImage(egl->Display(), mEGLImage);
|
|
mEGLImage = nullptr;
|
|
}
|
|
}
|
|
|
|
void WaylandDMABufSurfaceRGBA::ReleaseSurface() {
|
|
MOZ_ASSERT(!IsMapped(), "We can't release mapped buffer!");
|
|
|
|
ReleaseTextures();
|
|
|
|
if (mWLBuffer) {
|
|
wl_buffer_destroy(mWLBuffer);
|
|
mWLBuffer = nullptr;
|
|
}
|
|
|
|
for (int i = 0; i < mBufferPlaneCount; i++) {
|
|
if (mDmabufFds[i] >= 0) {
|
|
close(mDmabufFds[i]);
|
|
mDmabufFds[i] = 0;
|
|
}
|
|
}
|
|
|
|
if (mGbmBufferObject) {
|
|
nsGbmLib::Destroy(mGbmBufferObject);
|
|
mGbmBufferObject = nullptr;
|
|
}
|
|
}
|
|
|
|
void* WaylandDMABufSurfaceRGBA::MapInternal(uint32_t aX, uint32_t aY,
|
|
uint32_t aWidth, uint32_t aHeight,
|
|
uint32_t* aStride, int aGbmFlags) {
|
|
NS_ASSERTION(!IsMapped(), "Already mapped!");
|
|
if (!mGbmBufferObject) {
|
|
NS_WARNING(
|
|
"We can't map WaylandDMABufSurfaceRGBA without mGbmBufferObject");
|
|
return nullptr;
|
|
}
|
|
|
|
if (mSurfaceFlags & DMABUF_USE_MODIFIERS) {
|
|
NS_WARNING("We should not map dmabuf surfaces with modifiers!");
|
|
}
|
|
|
|
mMappedRegionStride = 0;
|
|
mMappedRegion =
|
|
nsGbmLib::Map(mGbmBufferObject, aX, aY, aWidth, aHeight, aGbmFlags,
|
|
&mMappedRegionStride, &mMappedRegionData);
|
|
if (aStride) {
|
|
*aStride = mMappedRegionStride;
|
|
}
|
|
return mMappedRegion;
|
|
}
|
|
|
|
void* WaylandDMABufSurfaceRGBA::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* WaylandDMABufSurfaceRGBA::MapReadOnly(uint32_t* aStride) {
|
|
return MapInternal(0, 0, mWidth, mHeight, aStride, GBM_BO_TRANSFER_READ);
|
|
}
|
|
|
|
void* WaylandDMABufSurfaceRGBA::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* WaylandDMABufSurfaceRGBA::Map(uint32_t* aStride) {
|
|
return MapInternal(0, 0, mWidth, mHeight, aStride,
|
|
GBM_BO_TRANSFER_READ_WRITE);
|
|
}
|
|
|
|
void WaylandDMABufSurfaceRGBA::Unmap() {
|
|
if (mMappedRegion) {
|
|
nsGbmLib::Unmap(mGbmBufferObject, mMappedRegionData);
|
|
mMappedRegion = nullptr;
|
|
mMappedRegionData = nullptr;
|
|
mMappedRegionStride = 0;
|
|
}
|
|
}
|
|
|
|
bool WaylandDMABufSurfaceRGBA::Resize(int aWidth, int aHeight) {
|
|
if (aWidth == mWidth && aHeight == mHeight) {
|
|
return true;
|
|
}
|
|
|
|
if (IsMapped()) {
|
|
NS_WARNING("We can't resize mapped surface!");
|
|
return false;
|
|
}
|
|
|
|
ReleaseSurface();
|
|
if (Create(aWidth, aHeight, mSurfaceFlags)) {
|
|
if (mSurfaceFlags & DMABUF_CREATE_WL_BUFFER) {
|
|
return CreateWLBuffer();
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
#if 0
|
|
// Copy from source surface by GL
|
|
# include "GLBlitHelper.h"
|
|
|
|
bool WaylandDMABufSurfaceRGBA::CopyFrom(class WaylandDMABufSurface* 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;
|
|
}
|
|
#endif
|
|
|
|
// TODO - Clear the surface by EGL
|
|
void WaylandDMABufSurfaceRGBA::Clear() {
|
|
uint32_t destStride;
|
|
void* destData = Map(&destStride);
|
|
memset(destData, 0, GetHeight() * destStride);
|
|
Unmap();
|
|
}
|
|
|
|
bool WaylandDMABufSurfaceRGBA::HasAlpha() {
|
|
return mGmbFormat ? mGmbFormat->mHasAlpha : true;
|
|
}
|
|
|
|
gfx::SurfaceFormat WaylandDMABufSurfaceRGBA::GetFormat() {
|
|
return HasAlpha() ? gfx::SurfaceFormat::B8G8R8A8
|
|
: gfx::SurfaceFormat::B8G8R8X8;
|
|
}
|
|
|
|
// GL uses swapped R and B components so report accordingly.
|
|
gfx::SurfaceFormat WaylandDMABufSurfaceRGBA::GetFormatGL() {
|
|
return HasAlpha() ? gfx::SurfaceFormat::R8G8B8A8
|
|
: gfx::SurfaceFormat::R8G8B8X8;
|
|
}
|
|
|
|
already_AddRefed<WaylandDMABufSurfaceRGBA>
|
|
WaylandDMABufSurfaceRGBA::CreateDMABufSurface(int aWidth, int aHeight,
|
|
int aWaylandDMABufSurfaceFlags) {
|
|
RefPtr<WaylandDMABufSurfaceRGBA> surf = new WaylandDMABufSurfaceRGBA();
|
|
if (!surf->Create(aWidth, aHeight, aWaylandDMABufSurfaceFlags)) {
|
|
return nullptr;
|
|
}
|
|
return surf.forget();
|
|
}
|
|
|
|
already_AddRefed<WaylandDMABufSurfaceNV12>
|
|
WaylandDMABufSurfaceNV12::CreateNV12Surface(
|
|
const VADRMPRIMESurfaceDescriptor& aDesc) {
|
|
RefPtr<WaylandDMABufSurfaceNV12> surf = new WaylandDMABufSurfaceNV12();
|
|
if (!surf->Create(aDesc)) {
|
|
return nullptr;
|
|
}
|
|
return surf.forget();
|
|
}
|
|
|
|
WaylandDMABufSurfaceNV12::WaylandDMABufSurfaceNV12()
|
|
: WaylandDMABufSurface(SURFACE_NV12),
|
|
mSurfaceFormat(gfx::SurfaceFormat::NV12),
|
|
mWidth(),
|
|
mHeight(),
|
|
mTexture(),
|
|
mColorSpace(mozilla::gfx::YUVColorSpace::UNKNOWN) {
|
|
for (int i = 0; i < DMABUF_BUFFER_PLANES; i++) {
|
|
mEGLImage[i] = LOCAL_EGL_NO_IMAGE;
|
|
}
|
|
}
|
|
|
|
WaylandDMABufSurfaceNV12::~WaylandDMABufSurfaceNV12() { ReleaseSurface(); }
|
|
|
|
bool WaylandDMABufSurfaceNV12::Create(
|
|
const VADRMPRIMESurfaceDescriptor& aDesc) {
|
|
if (aDesc.fourcc != VA_FOURCC_NV12) {
|
|
return false;
|
|
}
|
|
if (aDesc.num_layers > DMABUF_BUFFER_PLANES ||
|
|
aDesc.num_objects > DMABUF_BUFFER_PLANES) {
|
|
return false;
|
|
}
|
|
|
|
mSurfaceFormat = gfx::SurfaceFormat::NV12;
|
|
mBufferPlaneCount = aDesc.num_layers;
|
|
mBufferModifier = aDesc.objects[0].drm_format_modifier;
|
|
|
|
for (unsigned int i = 0; i < aDesc.num_layers; i++) {
|
|
// Intel exports VA-API surfaces in one object,planes have the same FD.
|
|
// AMD exports surfaces in two objects with different FDs.
|
|
bool dupFD = (aDesc.layers[i].object_index[0] != i);
|
|
int fd = aDesc.objects[aDesc.layers[i].object_index[0]].fd;
|
|
mDmabufFds[i] = dupFD ? dup(fd) : fd;
|
|
|
|
mDrmFormats[i] = aDesc.layers[i].drm_format;
|
|
mOffsets[i] = aDesc.layers[i].offset[0];
|
|
mStrides[i] = aDesc.layers[i].pitch[0];
|
|
mWidth[i] = aDesc.width >> i;
|
|
mHeight[i] = aDesc.height >> i;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool WaylandDMABufSurfaceNV12::Create(const SurfaceDescriptor& aDesc) {
|
|
ImportSurfaceDescriptor(aDesc);
|
|
return true;
|
|
}
|
|
|
|
void WaylandDMABufSurfaceNV12::ImportSurfaceDescriptor(
|
|
const SurfaceDescriptorDMABuf& aDesc) {
|
|
mSurfaceFormat = gfx::SurfaceFormat::NV12;
|
|
mBufferPlaneCount = aDesc.fds().Length();
|
|
mBufferModifier = aDesc.modifier();
|
|
mColorSpace = aDesc.yUVColorSpace();
|
|
|
|
MOZ_RELEASE_ASSERT(mBufferPlaneCount <= DMABUF_BUFFER_PLANES);
|
|
for (int i = 0; i < mBufferPlaneCount; i++) {
|
|
mDmabufFds[i] = aDesc.fds()[i].ClonePlatformHandle().release();
|
|
mWidth[i] = aDesc.width()[i];
|
|
mHeight[i] = aDesc.height()[i];
|
|
mDrmFormats[i] = aDesc.format()[i];
|
|
mStrides[i] = aDesc.strides()[i];
|
|
mOffsets[i] = aDesc.offsets()[i];
|
|
}
|
|
|
|
if (aDesc.fence().Length() > 0) {
|
|
int fd = aDesc.fence()[0].ClonePlatformHandle().release();
|
|
if (!FenceCreate(fd)) {
|
|
close(fd);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool WaylandDMABufSurfaceNV12::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> format;
|
|
AutoTArray<ipc::FileDescriptor, DMABUF_BUFFER_PLANES> fds;
|
|
AutoTArray<uint32_t, DMABUF_BUFFER_PLANES> strides;
|
|
AutoTArray<uint32_t, DMABUF_BUFFER_PLANES> offsets;
|
|
AutoTArray<ipc::FileDescriptor, 1> fenceFDs;
|
|
|
|
for (int i = 0; i < mBufferPlaneCount; i++) {
|
|
width.AppendElement(mWidth[i]);
|
|
height.AppendElement(mHeight[i]);
|
|
format.AppendElement(mDrmFormats[i]);
|
|
fds.AppendElement(ipc::FileDescriptor(mDmabufFds[i]));
|
|
strides.AppendElement(mStrides[i]);
|
|
offsets.AppendElement(mOffsets[i]);
|
|
}
|
|
|
|
if (mSync) {
|
|
auto* egl = gl::GLLibraryEGL::Get();
|
|
fenceFDs.AppendElement(ipc::FileDescriptor(
|
|
egl->fDupNativeFenceFDANDROID(egl->Display(), mSync)));
|
|
}
|
|
|
|
aOutDescriptor = SurfaceDescriptorDMABuf(
|
|
mSurfaceType, mBufferModifier, 0, fds, width, height, format, strides,
|
|
offsets, GetYUVColorSpace(), fenceFDs);
|
|
return true;
|
|
}
|
|
|
|
#if 0
|
|
already_AddRefed<gl::GLContext> MyCreateGLContextEGL() {
|
|
nsCString discardFailureId;
|
|
if (!gl::GLLibraryEGL::EnsureInitialized(true, &discardFailureId)) {
|
|
gfxCriticalNote << "Failed to load EGL library: " << discardFailureId.get();
|
|
return nullptr;
|
|
}
|
|
// Create GLContext with dummy EGLSurface.
|
|
RefPtr<gl::GLContext> gl =
|
|
gl::GLContextProviderEGL::CreateForCompositorWidget(nullptr, true, true);
|
|
if (!gl || !gl->MakeCurrent()) {
|
|
gfxCriticalNote << "Failed GL context creation for WebRender: "
|
|
<< gfx::hexa(gl.get());
|
|
return nullptr;
|
|
}
|
|
return gl.forget();
|
|
}
|
|
#endif
|
|
|
|
bool WaylandDMABufSurfaceNV12::CreateTexture(GLContext* aGLContext,
|
|
int aPlane) {
|
|
MOZ_ASSERT(!mEGLImage[aPlane] && !mTexture[aPlane],
|
|
"EGLImage/Texture is already created!");
|
|
|
|
nsTArray<EGLint> attribs;
|
|
attribs.AppendElement(LOCAL_EGL_WIDTH);
|
|
attribs.AppendElement(mWidth[aPlane]);
|
|
attribs.AppendElement(LOCAL_EGL_HEIGHT);
|
|
attribs.AppendElement(mHeight[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]); \
|
|
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]);
|
|
ADD_PLANE_ATTRIBS_NV12(0);
|
|
#undef ADD_PLANE_ATTRIBS_NV12
|
|
attribs.AppendElement(LOCAL_EGL_NONE);
|
|
|
|
auto* egl = gl::GLLibraryEGL::Get();
|
|
mEGLImage[aPlane] = egl->fCreateImage(egl->Display(), LOCAL_EGL_NO_CONTEXT,
|
|
LOCAL_EGL_LINUX_DMA_BUF_EXT, nullptr,
|
|
attribs.Elements());
|
|
if (mEGLImage[aPlane] == LOCAL_EGL_NO_IMAGE) {
|
|
NS_WARNING("EGLImageKHR creation failed");
|
|
return false;
|
|
}
|
|
|
|
aGLContext->MakeCurrent();
|
|
aGLContext->fGenTextures(1, &mTexture[aPlane]);
|
|
aGLContext->fBindTexture(LOCAL_GL_TEXTURE_2D, 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]);
|
|
mGL = aGLContext;
|
|
return true;
|
|
}
|
|
|
|
void WaylandDMABufSurfaceNV12::ReleaseTextures() {
|
|
FenceDelete();
|
|
|
|
bool textureActive = false;
|
|
for (int i = 0; i < mBufferPlaneCount; i++) {
|
|
if (mTexture[i]) {
|
|
textureActive = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (textureActive && mGL->MakeCurrent()) {
|
|
mGL->fDeleteTextures(DMABUF_BUFFER_PLANES, mTexture);
|
|
for (int i = 0; i < DMABUF_BUFFER_PLANES; i++) {
|
|
mTexture[i] = 0;
|
|
}
|
|
mGL = nullptr;
|
|
}
|
|
|
|
for (int i = 0; i < mBufferPlaneCount; i++) {
|
|
if (mEGLImage[i]) {
|
|
auto* egl = gl::GLLibraryEGL::Get();
|
|
egl->fDestroyImage(egl->Display(), mEGLImage[i]);
|
|
mEGLImage[i] = nullptr;
|
|
}
|
|
}
|
|
}
|
|
|
|
gfx::SurfaceFormat WaylandDMABufSurfaceNV12::GetFormat() {
|
|
return gfx::SurfaceFormat::NV12;
|
|
}
|
|
|
|
// GL uses swapped R and B components so report accordingly.
|
|
gfx::SurfaceFormat WaylandDMABufSurfaceNV12::GetFormatGL() {
|
|
return gfx::SurfaceFormat::NV12;
|
|
}
|
|
|
|
void WaylandDMABufSurfaceNV12::ReleaseSurface() {
|
|
ReleaseTextures();
|
|
|
|
for (int i = 0; i < mBufferPlaneCount; i++) {
|
|
if (mDmabufFds[i] >= 0) {
|
|
close(mDmabufFds[i]);
|
|
mDmabufFds[i] = 0;
|
|
}
|
|
}
|
|
}
|