diff options
Diffstat (limited to 'widget/gtk/WaylandBuffer.cpp')
-rw-r--r-- | widget/gtk/WaylandBuffer.cpp | 224 |
1 files changed, 224 insertions, 0 deletions
diff --git a/widget/gtk/WaylandBuffer.cpp b/widget/gtk/WaylandBuffer.cpp new file mode 100644 index 0000000000..f3fc409362 --- /dev/null +++ b/widget/gtk/WaylandBuffer.cpp @@ -0,0 +1,224 @@ +/* -*- 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 */ +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 = MakeUnique<base::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); +} + +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); + 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_buffer_add_listener(buffer->GetWlBuffer(), &sBufferListenerWaylandBuffer, + buffer.get()); + + LOGWAYLAND("WaylandBufferSHM Created [%p] WaylandDisplay [%p]\n", + buffer.get(), waylandDisplay); + + 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; + } + + wl_buffer_add_listener(buffer->GetWlBuffer(), &sBufferListenerWaylandBuffer, + buffer.get()); + + return buffer; +} + +WaylandBufferDMABUF::WaylandBufferDMABUF(const LayoutDeviceIntSize& aSize) + : WaylandBuffer(aSize) {} + +} // namespace mozilla::widget |