/*
* 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/. */
#ifndef MOZILLA_WIDGET_GTK_WAYLAND_BUFFER_H
#define MOZILLA_WIDGET_GTK_WAYLAND_BUFFER_H
#include "DMABufSurface.h"
#include "GLContext.h"
#include "MozFramebuffer.h"
#include "mozilla/ipc/SharedMemoryHandle.h"
#include "mozilla/ipc/SharedMemoryMapping.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/gfx/Types.h"
#include "mozilla/Mutex.h"
#include "mozilla/RefPtr.h"
#include "nsTArray.h"
#include "nsWaylandDisplay.h"
#include "WaylandSurface.h"
namespace mozilla::widget {
class WaylandBufferDMABUF;
// Allocates and owns shared memory for Wayland drawing surface
class WaylandShmPool {
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WaylandShmPool);
static RefPtr<WaylandShmPool> Create(nsWaylandDisplay* aWaylandDisplay,
int aSize);
wl_shm_pool* GetShmPool() {
return mShmPool; };
void* GetImageData();
private:
WaylandShmPool() =
default;
~WaylandShmPool();
wl_shm_pool* mShmPool = nullptr;
ipc::MutableSharedMemoryHandle mShmHandle;
ipc::SharedMemoryMapping mShm;
};
class BufferTransaction;
class WaylandBuffer {
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WaylandBuffer);
virtual already_AddRefed<gfx::DrawTarget> Lock() {
return nullptr; };
virtual void* GetImageData() {
return nullptr; }
virtual GLuint GetTexture() {
return 0; }
virtual void DestroyGLResources() {};
virtual gfx::SurfaceFormat GetSurfaceFormat() =
0;
virtual WaylandBufferDMABUF* AsWaylandBufferDMABUF() {
return nullptr; };
LayoutDeviceIntSize GetSize()
const {
return mSize; };
bool IsMatchingSize(
const LayoutDeviceIntSize& aSize)
const {
return aSize == mSize;
}
bool IsAttached(
const WaylandSurfaceLock& aSurfaceLock)
const;
BufferTransaction* GetTransaction(
const WaylandSurfaceLock& aSurfaceLock);
void RemoveTransaction(
const WaylandSurfaceLock& aSurfaceLock,
RefPtr<BufferTransaction> aTransaction);
#ifdef MOZ_LOGGING
virtual void DumpToFile(
const char* aHint) =
0;
#endif
// Create and return wl_buffer for underlying memory buffer if it's missing.
virtual wl_buffer* CreateWlBuffer() =
0;
// Set wl_buffer from external source (WaylandBufferDMABUFHolder).
void SetExternalWLBuffer(wl_buffer* aWLBuffer);
protected:
explicit WaylandBuffer(
const LayoutDeviceIntSize& aSize);
virtual ~WaylandBuffer() =
default;
// if set the wl_buffer is managed by someone else
// (for instance WaylandBufferDMABUFHolder)
// and WaylandBuffer can't destroy it.
wl_buffer* mExternalWlBuffer = nullptr;
AutoTArray<RefPtr<BufferTransaction>,
3> mBufferTransactions;
LayoutDeviceIntSize mSize;
static gfx::SurfaceFormat sFormat;
#ifdef MOZ_LOGGING
static int mDumpSerial;
static char* mDumpDir;
#endif
};
// Holds actual graphics data for wl_surface
class WaylandBufferSHM final :
public WaylandBuffer {
public:
static RefPtr<WaylandBufferSHM> Create(
const LayoutDeviceIntSize& aSize);
void ReleaseWlBuffer();
already_AddRefed<gfx::DrawTarget> Lock() override;
void* GetImageData() override {
return mShmPool->GetImageData(); }
gfx::SurfaceFormat GetSurfaceFormat() override {
return gfx::SurfaceFormat::B8G8R8A8;
}
void Clear();
size_t GetBufferAge()
const {
return mBufferAge; };
RefPtr<WaylandShmPool> GetShmPool()
const {
return mShmPool; }
void IncrementBufferAge() { mBufferAge++; };
void ResetBufferAge() { mBufferAge =
0; };
#ifdef MOZ_LOGGING
void DumpToFile(
const char* aHint) override;
#endif
wl_buffer* CreateWlBuffer() override;
private:
explicit WaylandBufferSHM(
const LayoutDeviceIntSize& aSize);
~WaylandBufferSHM() override;
// WaylandShmPoolMB provides actual shared memory we draw into
RefPtr<WaylandShmPool> mShmPool;
size_t mBufferAge =
0;
};
class WaylandBufferDMABUF final :
public WaylandBuffer {
public:
static already_AddRefed<WaylandBufferDMABUF> CreateRGBA(
const LayoutDeviceIntSize& aSize, gl::GLContext* aGL,
RefPtr<DRMFormat> aFormat);
static already_AddRefed<WaylandBufferDMABUF> CreateExternal(
RefPtr<DMABufSurface> aSurface);
WaylandBufferDMABUF* AsWaylandBufferDMABUF() override {
return this; };
GLuint GetTexture() override {
return mDMABufSurface->GetTexture(); };
void DestroyGLResources() override { mDMABufSurface->ReleaseTextures(); };
gfx::SurfaceFormat GetSurfaceFormat() override {
return mDMABufSurface->GetFormat();
}
DMABufSurface* GetSurface() {
return mDMABufSurface; }
#ifdef MOZ_LOGGING
void DumpToFile(
const char* aHint) override;
#endif
wl_buffer* CreateWlBuffer() override;
private:
explicit WaylandBufferDMABUF(
const LayoutDeviceIntSize& aSize);
~WaylandBufferDMABUF();
RefPtr<DMABufSurface> mDMABufSurface;
};
class WaylandBufferDMABUFHolder final {
public:
bool Matches(DMABufSurface* aSurface)
const;
wl_buffer* GetWLBuffer() {
return mWLBuffer; }
WaylandBufferDMABUFHolder(DMABufSurface* aSurface, wl_buffer* aWLBuffer);
~WaylandBufferDMABUFHolder();
private:
wl_buffer* mWLBuffer = nullptr;
uint32_t mUID =
0;
uint32_t mPID =
0;
};
// BufferTransaction class holds wl_buffer callbacks after
// wl_surface_commit and manages wl_buffer. One WaylandBuffer and WaylandSurface
// can have active transactions over the same underlying memory buffer which
// allows to map/unmap wl_surfaces for instance during layered page scrolling.
// This helps slower Wayland compositors (like KDE) which doesn't release
// wl_buffers quickly and holds them for longer time.
class BufferTransaction {
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(BufferTransaction);
BufferTransaction(WaylandBuffer* aBuffer, wl_buffer* aWLBuffer,
bool aIsExternalBuffer);
wl_buffer* BufferBorrowLocked(
const WaylandSurfaceLock& aSurfaceLock);
void BufferDetachCallback();
void BufferDeleteCallback();
bool IsAttached() {
return mBufferState == BufferState::WaitingForDetach ||
mBufferState == BufferState::WaitingForDelete;
}
bool IsDetached() {
return mBufferState == BufferState::Detached; }
bool IsDeleted() {
return mBufferState == BufferState::Deleted; }
void DeleteTransactionLocked(
const WaylandSurfaceLock& aSurfaceLock);
bool MatchesBuffer(uintptr_t aBuffer) {
return aBuffer ==
reinterpret_cast<uintptr_t>(mBuffer.get());
}
bool CanRecycle(WaylandSurface* aSurface) {
return IsDetached() && (!mSurface || mSurface == aSurface);
}
private:
~BufferTransaction();
void WlBufferDeleteLocked(
const WaylandSurfaceLock& aSurfaceLock);
void DeleteLocked(
const WaylandSurfaceLock& aSurfaceLock);
RefPtr<WaylandSurface> mSurface;
RefPtr<WaylandBuffer> mBuffer;
enum class BufferState {
Detached,
Deleted,
WaitingForDetach,
WaitingForDelete
};
BufferState mBufferState{BufferState::Detached};
wl_buffer* mWLBuffer = nullptr;
bool mIsExternalBuffer =
false;
};
}
// namespace mozilla::widget
#endif // MOZILLA_WIDGET_GTK_WAYLAND_BUFFER_H