Backed out changeset 52438b61a819 (bug 1943736) Backed out changeset cd369e4db2ee (bug 1942232) Backed out changeset 2606acece21a (bug 1942232)
401 lines
12 KiB
C++
401 lines
12 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
*
|
|
* 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 "WaylandBuffer.h"
|
|
#include "WaylandSurface.h"
|
|
#include "WaylandSurfaceLock.h"
|
|
|
|
#include <sys/mman.h>
|
|
#include <fcntl.h>
|
|
#include <errno.h>
|
|
|
|
#include "gfx2DGlue.h"
|
|
#include "gfxPlatform.h"
|
|
#include "mozilla/WidgetUtilsGtk.h"
|
|
#include "mozilla/gfx/Tools.h"
|
|
#include "nsGtkUtils.h"
|
|
#include "nsPrintfCString.h"
|
|
#include "prenv.h" // For PR_GetEnv
|
|
|
|
#ifdef MOZ_LOGGING
|
|
# include "mozilla/Logging.h"
|
|
# include "mozilla/ScopeExit.h"
|
|
# include "Units.h"
|
|
extern mozilla::LazyLogModule gWidgetWaylandLog;
|
|
# define LOGWAYLAND(...) \
|
|
MOZ_LOG(gWidgetWaylandLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
|
|
#else
|
|
# define LOGWAYLAND(...)
|
|
#endif /* MOZ_LOGGING */
|
|
|
|
using namespace mozilla::gl;
|
|
|
|
namespace mozilla::widget {
|
|
|
|
#define BUFFER_BPP 4
|
|
|
|
#ifdef MOZ_LOGGING
|
|
MOZ_RUNINIT int WaylandBufferSHM::mDumpSerial =
|
|
PR_GetEnv("MOZ_WAYLAND_DUMP_WL_BUFFERS") ? 1 : 0;
|
|
MOZ_RUNINIT char* WaylandBufferSHM::mDumpDir =
|
|
PR_GetEnv("MOZ_WAYLAND_DUMP_DIR");
|
|
#endif
|
|
|
|
/* static */
|
|
RefPtr<WaylandShmPool> WaylandShmPool::Create(nsWaylandDisplay* aWaylandDisplay,
|
|
int aSize) {
|
|
if (!aWaylandDisplay->GetShm()) {
|
|
NS_WARNING("WaylandShmPool: Missing Wayland shm interface!");
|
|
return nullptr;
|
|
}
|
|
|
|
RefPtr<WaylandShmPool> shmPool = new WaylandShmPool();
|
|
|
|
shmPool->mShm = MakeRefPtr<ipc::SharedMemory>();
|
|
if (!shmPool->mShm->Create(aSize)) {
|
|
NS_WARNING("WaylandShmPool: Unable to allocate shared memory!");
|
|
return nullptr;
|
|
}
|
|
|
|
shmPool->mSize = aSize;
|
|
shmPool->mShmPool = wl_shm_create_pool(
|
|
aWaylandDisplay->GetShm(), shmPool->mShm->CloneHandle().get(), aSize);
|
|
if (!shmPool->mShmPool) {
|
|
NS_WARNING("WaylandShmPool: Unable to allocate shared memory pool!");
|
|
return nullptr;
|
|
}
|
|
|
|
return shmPool;
|
|
}
|
|
|
|
void* WaylandShmPool::GetImageData() {
|
|
if (mImageData) {
|
|
return mImageData;
|
|
}
|
|
if (!mShm->Map(mSize)) {
|
|
NS_WARNING("WaylandShmPool: Failed to map Shm!");
|
|
return nullptr;
|
|
}
|
|
mImageData = mShm->Memory();
|
|
return mImageData;
|
|
}
|
|
|
|
WaylandShmPool::~WaylandShmPool() {
|
|
MozClearPointer(mShmPool, wl_shm_pool_destroy);
|
|
}
|
|
|
|
WaylandBuffer::WaylandBuffer(const LayoutDeviceIntSize& aSize) : mSize(aSize) {}
|
|
|
|
wl_buffer* WaylandBuffer::BorrowBuffer(RefPtr<WaylandSurface> aWaylandSurface) {
|
|
MOZ_RELEASE_ASSERT(!mSurface, "We're already attached!");
|
|
|
|
if (CreateWlBuffer()) {
|
|
mSurface = std::move(aWaylandSurface);
|
|
}
|
|
|
|
LOGWAYLAND(
|
|
"WaylandBuffer::BorrowBuffer() [%p] WaylandSurface [%p] wl_buffer [%p]",
|
|
(void*)this, mSurface ? mSurface->GetLoggingWidget() : nullptr,
|
|
mWLBuffer);
|
|
|
|
MOZ_DIAGNOSTIC_ASSERT(!IsWaitingToBufferDelete(), "We're already deleted!");
|
|
return mWLBuffer;
|
|
}
|
|
|
|
void WaylandBuffer::DeleteWlBuffer() {
|
|
if (!mWLBuffer) {
|
|
return;
|
|
}
|
|
LOGWAYLAND("WaylandBuffer::DeleteWlBuffer() [%p] wl_buffer [%p]\n",
|
|
(void*)this, mWLBuffer);
|
|
MozClearPointer(mWLBuffer, wl_buffer_destroy);
|
|
}
|
|
|
|
static void BufferDeleteSyncFinished(void* aData, struct wl_callback* callback,
|
|
uint32_t time) {
|
|
LOGWAYLAND("BufferDeleteSyncFinished() [%p]", aData);
|
|
RefPtr buffer = already_AddRefed(static_cast<WaylandBuffer*>(aData));
|
|
// wl_buffer should be already deleted on our side.
|
|
buffer->BufferDetachedCallbackHandler(nullptr, /* aWlBufferDeleted */ true);
|
|
}
|
|
|
|
static const struct wl_callback_listener sBufferDeleteSyncListener = {
|
|
.done = BufferDeleteSyncFinished,
|
|
};
|
|
|
|
void WaylandBuffer::ReturnBuffer(RefPtr<WaylandSurface> aWaylandSurface) {
|
|
LOGWAYLAND("WaylandBuffer::ReturnBuffer() [%p] WaylandSurface [%p]",
|
|
(void*)this, mSurface.get());
|
|
|
|
MutexAutoLock lock(mBufferReleaseMutex);
|
|
MOZ_RELEASE_ASSERT(aWaylandSurface == mSurface || !mSurface);
|
|
|
|
if (mBufferDeleteSyncCallback) {
|
|
MOZ_DIAGNOSTIC_ASSERT(!HasWlBuffer());
|
|
return;
|
|
}
|
|
|
|
DeleteWlBuffer();
|
|
|
|
// We're already detached from WaylandSurface
|
|
if (!mSurface) {
|
|
return;
|
|
}
|
|
|
|
// There are various Wayland queues processed for every thread.
|
|
// It's possible that wl_buffer release event is pending in any
|
|
// queue while we already asked for wl_buffer delete.
|
|
// We need to finish wl_buffer removal when all events from this
|
|
// point are processed so we use sync callback.
|
|
//
|
|
// When wl_display_sync comes back to us (from main thread)
|
|
// we know all events are processed and there isn't any
|
|
// wl_buffer operation pending so we can safely release WaylandSurface
|
|
// and WaylandBuffer objects.
|
|
mBufferDeleteSyncCallback = wl_display_sync(WaylandDisplayGetWLDisplay());
|
|
// Addref this to keep it live until sync,
|
|
// we're unref it at sBufferDeleteSyncListener
|
|
AddRef();
|
|
wl_callback_add_listener(mBufferDeleteSyncCallback,
|
|
&sBufferDeleteSyncListener, this);
|
|
}
|
|
|
|
void WaylandBuffer::BufferDetachedCallbackHandler(wl_buffer* aBuffer,
|
|
bool aWlBufferDeleted) {
|
|
LOGWAYLAND(
|
|
"WaylandBuffer::BufferDetachedCallbackHandler() [%p] WaylandSurface [%p] "
|
|
"aBuffer [%p] aWlBufferDeleted %d GetWlBuffer() [%p]",
|
|
(void*)this, mSurface ? mSurface->GetLoggingWidget() : nullptr, aBuffer,
|
|
aWlBufferDeleted, GetWlBuffer());
|
|
|
|
// BufferDetachedCallbackHandler() should be caled by Wayland compostor
|
|
// on main thread only.
|
|
AssertIsOnMainThread();
|
|
|
|
// aWlBufferDeleted means wl_buffer should be nullptr
|
|
MOZ_DIAGNOSTIC_ASSERT(!aBuffer == aWlBufferDeleted);
|
|
|
|
RefPtr<WaylandSurface> surface;
|
|
|
|
// Don't take mBufferReleaseMutex and mSurface locks together,
|
|
// may lead to deadlock.
|
|
{
|
|
MutexAutoLock lock(mBufferReleaseMutex);
|
|
|
|
// We should release correct buffer.
|
|
// If GetWlBuffer() is nullptr (deleted) we should have valid delete
|
|
// callback.
|
|
MOZ_DIAGNOSTIC_ASSERT(aBuffer == GetWlBuffer() ||
|
|
(!GetWlBuffer() && mBufferDeleteSyncCallback));
|
|
|
|
if (aWlBufferDeleted) {
|
|
MOZ_DIAGNOSTIC_ASSERT(mBufferDeleteSyncCallback);
|
|
mBufferDeleteSyncCallback = nullptr;
|
|
}
|
|
|
|
// We might be unreffed by previous BufferDetachedCallbackHandler() callback
|
|
// as it's called for both wl_buffer delete and wl_buffer detach events.
|
|
if (!mSurface) {
|
|
return;
|
|
}
|
|
|
|
// Clear surface reference so WaylandBuffer is marked as not attached now.
|
|
surface = std::move(mSurface);
|
|
}
|
|
|
|
// Notify WaylandSurface we're detached by Wayland compositor
|
|
// so it can clear reference to us.
|
|
WaylandSurfaceLock surfaceLock(surface);
|
|
surface->DetachedByWaylandCompositorLocked(surfaceLock, this);
|
|
}
|
|
|
|
static void BufferDetachedCallbackHandler(void* aData, wl_buffer* aBuffer) {
|
|
LOGWAYLAND("BufferDetachedCallbackHandler() [%p] received wl_buffer [%p]",
|
|
aData, aBuffer);
|
|
RefPtr<WaylandBuffer> buffer = static_cast<WaylandBuffer*>(aData);
|
|
buffer->BufferDetachedCallbackHandler(aBuffer, /* aWlBufferDeleted */ false);
|
|
}
|
|
|
|
static const struct wl_buffer_listener sBufferDetachListener = {
|
|
BufferDetachedCallbackHandler};
|
|
|
|
/* static */
|
|
RefPtr<WaylandBufferSHM> WaylandBufferSHM::Create(
|
|
const LayoutDeviceIntSize& aSize) {
|
|
RefPtr<WaylandBufferSHM> buffer = new WaylandBufferSHM(aSize);
|
|
nsWaylandDisplay* waylandDisplay = WaylandDisplayGet();
|
|
|
|
LOGWAYLAND("WaylandBufferSHM::Create() [%p] [%d x %d]", (void*)buffer,
|
|
aSize.width, aSize.height);
|
|
|
|
int size = aSize.width * aSize.height * BUFFER_BPP;
|
|
buffer->mShmPool = WaylandShmPool::Create(waylandDisplay, size);
|
|
if (!buffer->mShmPool) {
|
|
LOGWAYLAND(" failed to create shmPool");
|
|
return nullptr;
|
|
}
|
|
|
|
LOGWAYLAND(" created [%p] WaylandDisplay [%p]\n", buffer.get(),
|
|
waylandDisplay);
|
|
|
|
return buffer;
|
|
}
|
|
|
|
bool WaylandBufferSHM::CreateWlBuffer() {
|
|
if (mWLBuffer) {
|
|
return true;
|
|
}
|
|
LOGWAYLAND("WaylandBufferSHM::CreateWlBuffer() [%p]", (void*)this);
|
|
|
|
mWLBuffer = wl_shm_pool_create_buffer(mShmPool->GetShmPool(), 0, mSize.width,
|
|
mSize.height, mSize.width * BUFFER_BPP,
|
|
WL_SHM_FORMAT_ARGB8888);
|
|
if (!mWLBuffer) {
|
|
LOGWAYLAND(" failed to create wl_buffer");
|
|
return false;
|
|
}
|
|
|
|
if (wl_buffer_add_listener(mWLBuffer, &sBufferDetachListener, this) < 0) {
|
|
LOGWAYLAND(" failed to attach listener");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
WaylandBufferSHM::WaylandBufferSHM(const LayoutDeviceIntSize& aSize)
|
|
: WaylandBuffer(aSize) {
|
|
LOGWAYLAND("WaylandBufferSHM::WaylandBufferSHM() [%p]\n", (void*)this);
|
|
}
|
|
|
|
WaylandBufferSHM::~WaylandBufferSHM() {
|
|
LOGWAYLAND("WaylandBufferSHM::~WaylandBufferSHM() [%p]\n", (void*)this);
|
|
MOZ_DIAGNOSTIC_ASSERT(!IsWaitingToBufferDelete());
|
|
MOZ_DIAGNOSTIC_ASSERT(!IsAttached());
|
|
if (!IsAttached()) {
|
|
DeleteWlBuffer();
|
|
}
|
|
MOZ_DIAGNOSTIC_ASSERT(!HasWlBuffer());
|
|
}
|
|
|
|
already_AddRefed<gfx::DrawTarget> WaylandBufferSHM::Lock() {
|
|
LOGWAYLAND("WaylandBufferSHM::lock() [%p]\n", (void*)this);
|
|
return gfxPlatform::CreateDrawTargetForData(
|
|
static_cast<unsigned char*>(mShmPool->GetImageData()),
|
|
mSize.ToUnknownSize(), BUFFER_BPP * mSize.width, GetSurfaceFormat());
|
|
}
|
|
|
|
void WaylandBufferSHM::Clear() {
|
|
LOGWAYLAND("WaylandBufferSHM::Clear() [%p]\n", (void*)this);
|
|
memset(mShmPool->GetImageData(), 0xff,
|
|
mSize.height * mSize.width * BUFFER_BPP);
|
|
}
|
|
|
|
#ifdef MOZ_LOGGING
|
|
void WaylandBufferSHM::DumpToFile(const char* aHint) {
|
|
if (!mDumpSerial) {
|
|
return;
|
|
}
|
|
|
|
cairo_surface_t* surface = nullptr;
|
|
auto unmap = MakeScopeExit([&] {
|
|
if (surface) {
|
|
cairo_surface_destroy(surface);
|
|
}
|
|
});
|
|
surface = cairo_image_surface_create_for_data(
|
|
(unsigned char*)mShmPool->GetImageData(), CAIRO_FORMAT_ARGB32,
|
|
mSize.width, mSize.height, BUFFER_BPP * mSize.width);
|
|
if (cairo_surface_status(surface) == CAIRO_STATUS_SUCCESS) {
|
|
nsCString filename;
|
|
if (mDumpDir) {
|
|
filename.Append(mDumpDir);
|
|
filename.Append('/');
|
|
}
|
|
filename.Append(
|
|
nsPrintfCString("firefox-wl-buffer-%.5d-%s.png", mDumpSerial++, aHint));
|
|
cairo_surface_write_to_png(surface, filename.get());
|
|
LOGWAYLAND("Dumped wl_buffer to %s\n", filename.get());
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* static */
|
|
already_AddRefed<WaylandBufferDMABUF> WaylandBufferDMABUF::CreateRGBA(
|
|
const LayoutDeviceIntSize& aSize, GLContext* aGL,
|
|
RefPtr<DRMFormat> aFormat) {
|
|
RefPtr<WaylandBufferDMABUF> buffer = new WaylandBufferDMABUF(aSize);
|
|
|
|
buffer->mDMABufSurface = DMABufSurfaceRGBA::CreateDMABufSurface(
|
|
aSize.width, aSize.height, aFormat,
|
|
DMABUF_SCANOUT | DMABUF_USE_MODIFIERS);
|
|
if (!buffer->mDMABufSurface || !buffer->mDMABufSurface->CreateTexture(aGL)) {
|
|
LOGWAYLAND(" failed to create texture");
|
|
return nullptr;
|
|
}
|
|
|
|
LOGWAYLAND("WaylandBufferDMABUF::CreateRGBA() [%p] UID %d [%d x %d]",
|
|
(void*)buffer, buffer->mDMABufSurface->GetUID(), aSize.width,
|
|
aSize.height);
|
|
return buffer.forget();
|
|
}
|
|
|
|
/* static */
|
|
already_AddRefed<WaylandBufferDMABUF> WaylandBufferDMABUF::CreateExternal(
|
|
RefPtr<DMABufSurface> aSurface) {
|
|
const auto size =
|
|
LayoutDeviceIntSize(aSurface->GetWidth(), aSurface->GetWidth());
|
|
RefPtr<WaylandBufferDMABUF> buffer = new WaylandBufferDMABUF(size);
|
|
|
|
LOGWAYLAND("WaylandBufferDMABUF::CreateExternal() [%p] UID %d [%d x %d]",
|
|
(void*)buffer, aSurface->GetUID(), size.width, size.height);
|
|
|
|
buffer->mDMABufSurface = aSurface;
|
|
return buffer.forget();
|
|
}
|
|
|
|
bool WaylandBufferDMABUF::CreateWlBuffer() {
|
|
MOZ_DIAGNOSTIC_ASSERT(mDMABufSurface);
|
|
|
|
if (mWLBuffer) {
|
|
return true;
|
|
}
|
|
|
|
LOGWAYLAND("WaylandBufferDMABUF::CreateWlBuffer() [%p] UID %d", (void*)this,
|
|
mDMABufSurface->GetUID());
|
|
|
|
mWLBuffer = mDMABufSurface->CreateWlBuffer();
|
|
if (!mWLBuffer) {
|
|
LOGWAYLAND(" failed to create wl_buffer");
|
|
return false;
|
|
}
|
|
|
|
if (wl_buffer_add_listener(mWLBuffer, &sBufferDetachListener, this) < 0) {
|
|
LOGWAYLAND(" failed to attach listener!");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
WaylandBufferDMABUF::WaylandBufferDMABUF(const LayoutDeviceIntSize& aSize)
|
|
: WaylandBuffer(aSize) {
|
|
LOGWAYLAND("WaylandBufferDMABUF::WaylandBufferDMABUF [%p]\n", (void*)this);
|
|
}
|
|
|
|
WaylandBufferDMABUF::~WaylandBufferDMABUF() {
|
|
LOGWAYLAND("WaylandBufferDMABUF::~WaylandBufferDMABUF [%p] UID %d\n",
|
|
(void*)this, mDMABufSurface ? mDMABufSurface->GetUID() : -1);
|
|
MOZ_DIAGNOSTIC_ASSERT(!IsWaitingToBufferDelete());
|
|
MOZ_DIAGNOSTIC_ASSERT(!IsAttached());
|
|
if (!IsAttached()) {
|
|
DeleteWlBuffer();
|
|
}
|
|
MOZ_DIAGNOSTIC_ASSERT(!HasWlBuffer());
|
|
}
|
|
|
|
} // namespace mozilla::widget
|