зеркало из https://github.com/mozilla/gecko-dev.git
298 строки
8.5 KiB
C++
298 строки
8.5 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 <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
|
|
gfx::SurfaceFormat WaylandBuffer::mFormat = gfx::SurfaceFormat::B8G8R8A8;
|
|
|
|
#ifdef MOZ_LOGGING
|
|
int WaylandBufferSHM::mDumpSerial =
|
|
PR_GetEnv("MOZ_WAYLAND_DUMP_WL_BUFFERS") ? 1 : 0;
|
|
char* WaylandBufferSHM::mDumpDir = PR_GetEnv("MOZ_WAYLAND_DUMP_DIR");
|
|
#endif
|
|
|
|
static int WaylandAllocateShmMemory(int aSize) {
|
|
int fd = -1;
|
|
|
|
nsAutoCString shmPrefix("/");
|
|
if (const char* snapName = GetSnapInstanceName()) {
|
|
shmPrefix.AppendPrintf("snap.%s.", snapName);
|
|
}
|
|
shmPrefix.Append("wayland.mozilla.ipc");
|
|
|
|
do {
|
|
static int counter = 0;
|
|
nsPrintfCString shmName("%s.%d", shmPrefix.get(), counter++);
|
|
fd = shm_open(shmName.get(), O_CREAT | O_RDWR | O_EXCL, 0600);
|
|
if (fd >= 0) {
|
|
// We don't want to use leaked file
|
|
if (shm_unlink(shmName.get()) != 0) {
|
|
NS_WARNING("shm_unlink failed");
|
|
return -1;
|
|
}
|
|
}
|
|
} while (fd < 0 && errno == EEXIST);
|
|
|
|
if (fd < 0) {
|
|
NS_WARNING(nsPrintfCString("shm_open failed: %s", strerror(errno)).get());
|
|
return -1;
|
|
}
|
|
|
|
int ret = 0;
|
|
#ifdef HAVE_POSIX_FALLOCATE
|
|
do {
|
|
ret = posix_fallocate(fd, 0, aSize);
|
|
} while (ret == EINTR);
|
|
if (ret == 0) {
|
|
return fd;
|
|
}
|
|
if (ret != ENODEV && ret != EINVAL && ret != EOPNOTSUPP) {
|
|
NS_WARNING(
|
|
nsPrintfCString("posix_fallocate() fails to allocate shm memory: %s",
|
|
strerror(ret))
|
|
.get());
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
#endif
|
|
do {
|
|
ret = ftruncate(fd, aSize);
|
|
} while (ret < 0 && errno == EINTR);
|
|
if (ret < 0) {
|
|
NS_WARNING(nsPrintfCString("ftruncate() fails to allocate shm memory: %s",
|
|
strerror(ret))
|
|
.get());
|
|
close(fd);
|
|
fd = -1;
|
|
}
|
|
|
|
return fd;
|
|
}
|
|
|
|
/* static */
|
|
RefPtr<WaylandShmPool> WaylandShmPool::Create(
|
|
const RefPtr<nsWaylandDisplay>& aWaylandDisplay, int aSize) {
|
|
if (!aWaylandDisplay->GetShm()) {
|
|
NS_WARNING("Missing Wayland shm interface!");
|
|
return nullptr;
|
|
}
|
|
|
|
RefPtr<WaylandShmPool> shmPool = new WaylandShmPool(aSize);
|
|
|
|
shmPool->mShmPoolFd = WaylandAllocateShmMemory(aSize);
|
|
if (shmPool->mShmPoolFd < 0) {
|
|
return nullptr;
|
|
}
|
|
|
|
shmPool->mImageData = mmap(nullptr, aSize, PROT_READ | PROT_WRITE, MAP_SHARED,
|
|
shmPool->mShmPoolFd, 0);
|
|
if (shmPool->mImageData == MAP_FAILED) {
|
|
NS_WARNING("Unable to map drawing surface!");
|
|
return nullptr;
|
|
}
|
|
|
|
shmPool->mShmPool =
|
|
wl_shm_create_pool(aWaylandDisplay->GetShm(), shmPool->mShmPoolFd, aSize);
|
|
if (!shmPool->mShmPool) {
|
|
return nullptr;
|
|
}
|
|
|
|
// We set our queue to get mShmPool events at compositor thread.
|
|
wl_proxy_set_queue((struct wl_proxy*)shmPool->mShmPool,
|
|
aWaylandDisplay->GetEventQueue());
|
|
|
|
return shmPool;
|
|
}
|
|
|
|
WaylandShmPool::WaylandShmPool(int aSize)
|
|
: mShmPool(nullptr),
|
|
mShmPoolFd(-1),
|
|
mAllocatedSize(aSize),
|
|
mImageData(nullptr){};
|
|
|
|
WaylandShmPool::~WaylandShmPool() {
|
|
if (mImageData != MAP_FAILED) {
|
|
munmap(mImageData, mAllocatedSize);
|
|
mImageData = MAP_FAILED;
|
|
}
|
|
MozClearPointer(mShmPool, wl_shm_pool_destroy);
|
|
if (mShmPoolFd >= 0) {
|
|
close(mShmPoolFd);
|
|
mShmPoolFd = -1;
|
|
}
|
|
}
|
|
|
|
static const struct wl_buffer_listener sBufferListenerWaylandBuffer = {
|
|
WaylandBuffer::BufferReleaseCallbackHandler};
|
|
|
|
WaylandBuffer::WaylandBuffer(const LayoutDeviceIntSize& aSize) : mSize(aSize) {}
|
|
|
|
void WaylandBuffer::AttachAndCommit(wl_surface* aSurface) {
|
|
LOGWAYLAND(
|
|
"WaylandBuffer::AttachAndCommit [%p] wl_surface %p ID %d wl_buffer "
|
|
"%p ID %d\n",
|
|
(void*)this, (void*)aSurface,
|
|
aSurface ? wl_proxy_get_id((struct wl_proxy*)aSurface) : -1,
|
|
(void*)GetWlBuffer(),
|
|
GetWlBuffer() ? wl_proxy_get_id((struct wl_proxy*)GetWlBuffer()) : -1);
|
|
|
|
wl_buffer* buffer = GetWlBuffer();
|
|
if (buffer) {
|
|
mAttached = true;
|
|
wl_surface_attach(aSurface, buffer, 0, 0);
|
|
wl_surface_commit(aSurface);
|
|
}
|
|
}
|
|
|
|
void WaylandBuffer::BufferReleaseCallbackHandler(wl_buffer* aBuffer) {
|
|
mAttached = false;
|
|
|
|
if (mBufferReleaseFunc) {
|
|
mBufferReleaseFunc(mBufferReleaseData, aBuffer);
|
|
}
|
|
}
|
|
|
|
void WaylandBuffer::BufferReleaseCallbackHandler(void* aData,
|
|
wl_buffer* aBuffer) {
|
|
auto* buffer = reinterpret_cast<WaylandBuffer*>(aData);
|
|
buffer->BufferReleaseCallbackHandler(aBuffer);
|
|
}
|
|
|
|
/* static */
|
|
RefPtr<WaylandBufferSHM> WaylandBufferSHM::Create(
|
|
const LayoutDeviceIntSize& aSize) {
|
|
RefPtr<WaylandBufferSHM> buffer = new WaylandBufferSHM(aSize);
|
|
RefPtr<nsWaylandDisplay> waylandDisplay = WaylandDisplayGet();
|
|
|
|
int size = aSize.width * aSize.height * BUFFER_BPP;
|
|
buffer->mShmPool = WaylandShmPool::Create(waylandDisplay, size);
|
|
if (!buffer->mShmPool) {
|
|
return nullptr;
|
|
}
|
|
|
|
buffer->mWLBuffer = wl_shm_pool_create_buffer(
|
|
buffer->mShmPool->GetShmPool(), 0, aSize.width, aSize.height,
|
|
aSize.width * BUFFER_BPP, WL_SHM_FORMAT_ARGB8888);
|
|
if (!buffer->mWLBuffer) {
|
|
return nullptr;
|
|
}
|
|
|
|
wl_proxy_set_queue((struct wl_proxy*)buffer->GetWlBuffer(),
|
|
waylandDisplay->GetEventQueue());
|
|
wl_buffer_add_listener(buffer->GetWlBuffer(), &sBufferListenerWaylandBuffer,
|
|
buffer.get());
|
|
|
|
LOGWAYLAND("WaylandBufferSHM Created [%p] WaylandDisplay [%p]\n",
|
|
buffer.get(), waylandDisplay.get());
|
|
|
|
return buffer;
|
|
}
|
|
|
|
WaylandBufferSHM::WaylandBufferSHM(const LayoutDeviceIntSize& aSize)
|
|
: WaylandBuffer(aSize) {}
|
|
|
|
WaylandBufferSHM::~WaylandBufferSHM() {
|
|
MozClearPointer(mWLBuffer, wl_buffer_destroy);
|
|
}
|
|
|
|
already_AddRefed<gfx::DrawTarget> WaylandBufferSHM::Lock() {
|
|
return gfxPlatform::CreateDrawTargetForData(
|
|
static_cast<unsigned char*>(mShmPool->GetImageData()),
|
|
mSize.ToUnknownSize(), BUFFER_BPP * mSize.width, GetSurfaceFormat());
|
|
}
|
|
|
|
void WaylandBufferSHM::Clear() {
|
|
memset(mShmPool->GetImageData(), 0, 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 */
|
|
RefPtr<WaylandBufferDMABUF> WaylandBufferDMABUF::Create(
|
|
const LayoutDeviceIntSize& aSize, GLContext* aGL) {
|
|
RefPtr<WaylandBufferDMABUF> buffer = new WaylandBufferDMABUF(aSize);
|
|
|
|
const auto flags =
|
|
static_cast<DMABufSurfaceFlags>(DMABUF_TEXTURE | DMABUF_ALPHA);
|
|
buffer->mDMABufSurface =
|
|
DMABufSurfaceRGBA::CreateDMABufSurface(aSize.width, aSize.height, flags);
|
|
if (!buffer->mDMABufSurface || !buffer->mDMABufSurface->CreateTexture(aGL)) {
|
|
return nullptr;
|
|
}
|
|
|
|
if (!buffer->mDMABufSurface->CreateWlBuffer()) {
|
|
return nullptr;
|
|
}
|
|
|
|
RefPtr<nsWaylandDisplay> waylandDisplay = WaylandDisplayGet();
|
|
wl_proxy_set_queue((struct wl_proxy*)buffer->GetWlBuffer(),
|
|
waylandDisplay->GetEventQueue());
|
|
wl_buffer_add_listener(buffer->GetWlBuffer(), &sBufferListenerWaylandBuffer,
|
|
buffer.get());
|
|
|
|
return buffer;
|
|
}
|
|
|
|
WaylandBufferDMABUF::WaylandBufferDMABUF(const LayoutDeviceIntSize& aSize)
|
|
: WaylandBuffer(aSize) {}
|
|
|
|
} // namespace mozilla::widget
|