/* -*- 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
quality 100%
¤ Dauer der Verarbeitung: 0.13 Sekunden
(vorverarbeitet)
¤
*© Formatika GbR, Deutschland