1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "WaylandBuffer.h"
8 #include "WaylandSurface.h"
9 #include "WaylandSurfaceLock.h"
15 #include "gfx2DGlue.h"
16 #include "gfxPlatform.h"
17 #include "mozilla/WidgetUtilsGtk.h"
18 #include "mozilla/gfx/Tools.h"
19 #include "nsGtkUtils.h"
20 #include "nsPrintfCString.h"
21 #include "prenv.h" // For PR_GetEnv
24 # include "mozilla/Logging.h"
25 # include "mozilla/ScopeExit.h"
27 extern mozilla::LazyLogModule gWidgetWaylandLog
;
28 # define LOGWAYLAND(...) \
29 MOZ_LOG(gWidgetWaylandLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
31 # define LOGWAYLAND(...)
32 #endif /* MOZ_LOGGING */
34 using namespace mozilla::gl
;
36 namespace mozilla::widget
{
41 MOZ_RUNINIT
int WaylandBufferSHM::mDumpSerial
=
42 PR_GetEnv("MOZ_WAYLAND_DUMP_WL_BUFFERS") ? 1 : 0;
43 MOZ_RUNINIT
char* WaylandBufferSHM::mDumpDir
=
44 PR_GetEnv("MOZ_WAYLAND_DUMP_DIR");
48 RefPtr
<WaylandShmPool
> WaylandShmPool::Create(nsWaylandDisplay
* aWaylandDisplay
,
50 if (!aWaylandDisplay
->GetShm()) {
51 NS_WARNING("WaylandShmPool: Missing Wayland shm interface!");
55 RefPtr
<WaylandShmPool
> shmPool
= new WaylandShmPool();
57 shmPool
->mShm
= MakeRefPtr
<ipc::SharedMemory
>();
58 if (!shmPool
->mShm
->Create(aSize
)) {
59 NS_WARNING("WaylandShmPool: Unable to allocate shared memory!");
63 shmPool
->mSize
= aSize
;
64 shmPool
->mShmPool
= wl_shm_create_pool(
65 aWaylandDisplay
->GetShm(), shmPool
->mShm
->CloneHandle().get(), aSize
);
66 if (!shmPool
->mShmPool
) {
67 NS_WARNING("WaylandShmPool: Unable to allocate shared memory pool!");
74 void* WaylandShmPool::GetImageData() {
78 if (!mShm
->Map(mSize
)) {
79 NS_WARNING("WaylandShmPool: Failed to map Shm!");
82 mImageData
= mShm
->Memory();
86 WaylandShmPool::~WaylandShmPool() {
87 MozClearPointer(mShmPool
, wl_shm_pool_destroy
);
90 WaylandBuffer::WaylandBuffer(const LayoutDeviceIntSize
& aSize
) : mSize(aSize
) {}
92 wl_buffer
* WaylandBuffer::BorrowBuffer(RefPtr
<WaylandSurface
> aWaylandSurface
) {
93 MOZ_RELEASE_ASSERT(!mSurface
, "We're already attached!");
95 if (CreateWlBuffer()) {
96 mSurface
= std::move(aWaylandSurface
);
100 "WaylandBuffer::BorrowBuffer() [%p] WaylandSurface [%p] wl_buffer [%p]",
101 (void*)this, mSurface
? mSurface
->GetLoggingWidget() : nullptr,
104 MOZ_DIAGNOSTIC_ASSERT(!IsWaitingToBufferDelete(), "We're already deleted!");
108 void WaylandBuffer::DeleteWlBuffer() {
112 LOGWAYLAND("WaylandBuffer::DeleteWlBuffer() [%p] wl_buffer [%p]\n",
113 (void*)this, mWLBuffer
);
114 MozClearPointer(mWLBuffer
, wl_buffer_destroy
);
117 static void BufferDeleteSyncFinished(void* aData
, struct wl_callback
* callback
,
119 LOGWAYLAND("BufferDeleteSyncFinished() [%p]", aData
);
120 RefPtr buffer
= already_AddRefed(static_cast<WaylandBuffer
*>(aData
));
121 // wl_buffer should be already deleted on our side.
122 buffer
->BufferDetachedCallbackHandler(nullptr, /* aWlBufferDeleted */ true);
125 static const struct wl_callback_listener sBufferDeleteSyncListener
= {
126 .done
= BufferDeleteSyncFinished
,
129 void WaylandBuffer::ReturnBuffer(RefPtr
<WaylandSurface
> aWaylandSurface
) {
130 LOGWAYLAND("WaylandBuffer::ReturnBuffer() [%p] WaylandSurface [%p]",
131 (void*)this, mSurface
.get());
133 MutexAutoLock
lock(mBufferReleaseMutex
);
134 MOZ_RELEASE_ASSERT(aWaylandSurface
== mSurface
|| !mSurface
);
136 if (mBufferDeleteSyncCallback
) {
137 MOZ_DIAGNOSTIC_ASSERT(!HasWlBuffer());
143 // We're already detached from WaylandSurface
148 // There are various Wayland queues processed for every thread.
149 // It's possible that wl_buffer release event is pending in any
150 // queue while we already asked for wl_buffer delete.
151 // We need to finish wl_buffer removal when all events from this
152 // point are processed so we use sync callback.
154 // When wl_display_sync comes back to us (from main thread)
155 // we know all events are processed and there isn't any
156 // wl_buffer operation pending so we can safely release WaylandSurface
157 // and WaylandBuffer objects.
158 mBufferDeleteSyncCallback
= wl_display_sync(WaylandDisplayGetWLDisplay());
159 // Addref this to keep it live until sync,
160 // we're unref it at sBufferDeleteSyncListener
162 wl_callback_add_listener(mBufferDeleteSyncCallback
,
163 &sBufferDeleteSyncListener
, this);
166 void WaylandBuffer::BufferDetachedCallbackHandler(wl_buffer
* aBuffer
,
167 bool aWlBufferDeleted
) {
169 "WaylandBuffer::BufferDetachedCallbackHandler() [%p] WaylandSurface [%p] "
170 "aBuffer [%p] aWlBufferDeleted %d GetWlBuffer() [%p]",
171 (void*)this, mSurface
? mSurface
->GetLoggingWidget() : nullptr, aBuffer
,
172 aWlBufferDeleted
, GetWlBuffer());
174 // BufferDetachedCallbackHandler() should be caled by Wayland compostor
175 // on main thread only.
176 AssertIsOnMainThread();
178 // aWlBufferDeleted means wl_buffer should be nullptr
179 MOZ_DIAGNOSTIC_ASSERT(!aBuffer
== aWlBufferDeleted
);
181 RefPtr
<WaylandSurface
> surface
;
183 // Don't take mBufferReleaseMutex and mSurface locks together,
184 // may lead to deadlock.
186 MutexAutoLock
lock(mBufferReleaseMutex
);
188 // We should release correct buffer.
189 // If GetWlBuffer() is nullptr (deleted) we should have valid delete
191 MOZ_DIAGNOSTIC_ASSERT(aBuffer
== GetWlBuffer() ||
192 (!GetWlBuffer() && mBufferDeleteSyncCallback
));
194 if (aWlBufferDeleted
) {
195 MOZ_DIAGNOSTIC_ASSERT(mBufferDeleteSyncCallback
);
196 mBufferDeleteSyncCallback
= nullptr;
199 // We might be unreffed by previous BufferDetachedCallbackHandler() callback
200 // as it's called for both wl_buffer delete and wl_buffer detach events.
205 // Clear surface reference so WaylandBuffer is marked as not attached now.
206 surface
= std::move(mSurface
);
209 // Notify WaylandSurface we're detached by Wayland compositor
210 // so it can clear reference to us.
211 WaylandSurfaceLock
surfaceLock(surface
);
212 surface
->DetachedByWaylandCompositorLocked(surfaceLock
, this);
215 static void BufferDetachedCallbackHandler(void* aData
, wl_buffer
* aBuffer
) {
216 LOGWAYLAND("BufferDetachedCallbackHandler() [%p] received wl_buffer [%p]",
218 RefPtr
<WaylandBuffer
> buffer
= static_cast<WaylandBuffer
*>(aData
);
219 buffer
->BufferDetachedCallbackHandler(aBuffer
, /* aWlBufferDeleted */ false);
222 static const struct wl_buffer_listener sBufferDetachListener
= {
223 BufferDetachedCallbackHandler
};
226 RefPtr
<WaylandBufferSHM
> WaylandBufferSHM::Create(
227 const LayoutDeviceIntSize
& aSize
) {
228 RefPtr
<WaylandBufferSHM
> buffer
= new WaylandBufferSHM(aSize
);
229 nsWaylandDisplay
* waylandDisplay
= WaylandDisplayGet();
231 LOGWAYLAND("WaylandBufferSHM::Create() [%p] [%d x %d]", (void*)buffer
,
232 aSize
.width
, aSize
.height
);
234 int size
= aSize
.width
* aSize
.height
* BUFFER_BPP
;
235 buffer
->mShmPool
= WaylandShmPool::Create(waylandDisplay
, size
);
236 if (!buffer
->mShmPool
) {
237 LOGWAYLAND(" failed to create shmPool");
241 LOGWAYLAND(" created [%p] WaylandDisplay [%p]\n", buffer
.get(),
247 bool WaylandBufferSHM::CreateWlBuffer() {
251 LOGWAYLAND("WaylandBufferSHM::CreateWlBuffer() [%p]", (void*)this);
253 mWLBuffer
= wl_shm_pool_create_buffer(mShmPool
->GetShmPool(), 0, mSize
.width
,
254 mSize
.height
, mSize
.width
* BUFFER_BPP
,
255 WL_SHM_FORMAT_ARGB8888
);
257 LOGWAYLAND(" failed to create wl_buffer");
261 if (wl_buffer_add_listener(mWLBuffer
, &sBufferDetachListener
, this) < 0) {
262 LOGWAYLAND(" failed to attach listener");
269 WaylandBufferSHM::WaylandBufferSHM(const LayoutDeviceIntSize
& aSize
)
270 : WaylandBuffer(aSize
) {
271 LOGWAYLAND("WaylandBufferSHM::WaylandBufferSHM() [%p]\n", (void*)this);
274 WaylandBufferSHM::~WaylandBufferSHM() {
275 LOGWAYLAND("WaylandBufferSHM::~WaylandBufferSHM() [%p]\n", (void*)this);
276 MOZ_DIAGNOSTIC_ASSERT(!IsWaitingToBufferDelete());
277 MOZ_DIAGNOSTIC_ASSERT(!IsAttached());
281 MOZ_DIAGNOSTIC_ASSERT(!HasWlBuffer());
284 already_AddRefed
<gfx::DrawTarget
> WaylandBufferSHM::Lock() {
285 LOGWAYLAND("WaylandBufferSHM::lock() [%p]\n", (void*)this);
286 return gfxPlatform::CreateDrawTargetForData(
287 static_cast<unsigned char*>(mShmPool
->GetImageData()),
288 mSize
.ToUnknownSize(), BUFFER_BPP
* mSize
.width
, GetSurfaceFormat());
291 void WaylandBufferSHM::Clear() {
292 LOGWAYLAND("WaylandBufferSHM::Clear() [%p]\n", (void*)this);
293 memset(mShmPool
->GetImageData(), 0xff,
294 mSize
.height
* mSize
.width
* BUFFER_BPP
);
298 void WaylandBufferSHM::DumpToFile(const char* aHint
) {
303 cairo_surface_t
* surface
= nullptr;
304 auto unmap
= MakeScopeExit([&] {
306 cairo_surface_destroy(surface
);
309 surface
= cairo_image_surface_create_for_data(
310 (unsigned char*)mShmPool
->GetImageData(), CAIRO_FORMAT_ARGB32
,
311 mSize
.width
, mSize
.height
, BUFFER_BPP
* mSize
.width
);
312 if (cairo_surface_status(surface
) == CAIRO_STATUS_SUCCESS
) {
315 filename
.Append(mDumpDir
);
316 filename
.Append('/');
319 nsPrintfCString("firefox-wl-buffer-%.5d-%s.png", mDumpSerial
++, aHint
));
320 cairo_surface_write_to_png(surface
, filename
.get());
321 LOGWAYLAND("Dumped wl_buffer to %s\n", filename
.get());
327 already_AddRefed
<WaylandBufferDMABUF
> WaylandBufferDMABUF::CreateRGBA(
328 const LayoutDeviceIntSize
& aSize
, GLContext
* aGL
,
329 RefPtr
<DRMFormat
> aFormat
) {
330 RefPtr
<WaylandBufferDMABUF
> buffer
= new WaylandBufferDMABUF(aSize
);
332 buffer
->mDMABufSurface
= DMABufSurfaceRGBA::CreateDMABufSurface(
333 aSize
.width
, aSize
.height
, aFormat
,
334 DMABUF_SCANOUT
| DMABUF_USE_MODIFIERS
);
335 if (!buffer
->mDMABufSurface
|| !buffer
->mDMABufSurface
->CreateTexture(aGL
)) {
336 LOGWAYLAND(" failed to create texture");
340 LOGWAYLAND("WaylandBufferDMABUF::CreateRGBA() [%p] UID %d [%d x %d]",
341 (void*)buffer
, buffer
->mDMABufSurface
->GetUID(), aSize
.width
,
343 return buffer
.forget();
347 already_AddRefed
<WaylandBufferDMABUF
> WaylandBufferDMABUF::CreateExternal(
348 RefPtr
<DMABufSurface
> aSurface
) {
350 LayoutDeviceIntSize(aSurface
->GetWidth(), aSurface
->GetWidth());
351 RefPtr
<WaylandBufferDMABUF
> buffer
= new WaylandBufferDMABUF(size
);
353 LOGWAYLAND("WaylandBufferDMABUF::CreateExternal() [%p] UID %d [%d x %d]",
354 (void*)buffer
, aSurface
->GetUID(), size
.width
, size
.height
);
356 buffer
->mDMABufSurface
= aSurface
;
357 return buffer
.forget();
360 bool WaylandBufferDMABUF::CreateWlBuffer() {
361 MOZ_DIAGNOSTIC_ASSERT(mDMABufSurface
);
367 LOGWAYLAND("WaylandBufferDMABUF::CreateWlBuffer() [%p] UID %d", (void*)this,
368 mDMABufSurface
->GetUID());
370 mWLBuffer
= mDMABufSurface
->CreateWlBuffer();
372 LOGWAYLAND(" failed to create wl_buffer");
376 if (wl_buffer_add_listener(mWLBuffer
, &sBufferDetachListener
, this) < 0) {
377 LOGWAYLAND(" failed to attach listener!");
384 WaylandBufferDMABUF::WaylandBufferDMABUF(const LayoutDeviceIntSize
& aSize
)
385 : WaylandBuffer(aSize
) {
386 LOGWAYLAND("WaylandBufferDMABUF::WaylandBufferDMABUF [%p]\n", (void*)this);
389 WaylandBufferDMABUF::~WaylandBufferDMABUF() {
390 LOGWAYLAND("WaylandBufferDMABUF::~WaylandBufferDMABUF [%p] UID %d\n",
391 (void*)this, mDMABufSurface
? mDMABufSurface
->GetUID() : -1);
392 MOZ_DIAGNOSTIC_ASSERT(!IsWaitingToBufferDelete());
393 MOZ_DIAGNOSTIC_ASSERT(!IsAttached());
397 MOZ_DIAGNOSTIC_ASSERT(!HasWlBuffer());
400 } // namespace mozilla::widget