Backed out changeset b71c8c052463 (bug 1943846) for causing mass failures. CLOSED...
[gecko.git] / widget / gtk / WaylandBuffer.cpp
blob2afe5c31a1a9a861b85f8f4093bf557be6787898
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"
11 #include <sys/mman.h>
12 #include <fcntl.h>
13 #include <errno.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
23 #ifdef MOZ_LOGGING
24 # include "mozilla/Logging.h"
25 # include "mozilla/ScopeExit.h"
26 # include "Units.h"
27 extern mozilla::LazyLogModule gWidgetWaylandLog;
28 # define LOGWAYLAND(...) \
29 MOZ_LOG(gWidgetWaylandLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
30 #else
31 # define LOGWAYLAND(...)
32 #endif /* MOZ_LOGGING */
34 using namespace mozilla::gl;
36 namespace mozilla::widget {
38 #define BUFFER_BPP 4
40 #ifdef MOZ_LOGGING
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");
45 #endif
47 /* static */
48 RefPtr<WaylandShmPool> WaylandShmPool::Create(nsWaylandDisplay* aWaylandDisplay,
49 int aSize) {
50 if (!aWaylandDisplay->GetShm()) {
51 NS_WARNING("WaylandShmPool: Missing Wayland shm interface!");
52 return nullptr;
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!");
60 return nullptr;
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!");
68 return nullptr;
71 return shmPool;
74 void* WaylandShmPool::GetImageData() {
75 if (mImageData) {
76 return mImageData;
78 if (!mShm->Map(mSize)) {
79 NS_WARNING("WaylandShmPool: Failed to map Shm!");
80 return nullptr;
82 mImageData = mShm->Memory();
83 return mImageData;
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);
99 LOGWAYLAND(
100 "WaylandBuffer::BorrowBuffer() [%p] WaylandSurface [%p] wl_buffer [%p]",
101 (void*)this, mSurface ? mSurface->GetLoggingWidget() : nullptr,
102 mWLBuffer);
104 MOZ_DIAGNOSTIC_ASSERT(!IsWaitingToBufferDelete(), "We're already deleted!");
105 return mWLBuffer;
108 void WaylandBuffer::DeleteWlBuffer() {
109 if (!mWLBuffer) {
110 return;
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,
118 uint32_t time) {
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());
138 return;
141 DeleteWlBuffer();
143 // We're already detached from WaylandSurface
144 if (!mSurface) {
145 return;
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
161 AddRef();
162 wl_callback_add_listener(mBufferDeleteSyncCallback,
163 &sBufferDeleteSyncListener, this);
166 void WaylandBuffer::BufferDetachedCallbackHandler(wl_buffer* aBuffer,
167 bool aWlBufferDeleted) {
168 LOGWAYLAND(
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
190 // callback.
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.
201 if (!mSurface) {
202 return;
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]",
217 aData, aBuffer);
218 RefPtr<WaylandBuffer> buffer = static_cast<WaylandBuffer*>(aData);
219 buffer->BufferDetachedCallbackHandler(aBuffer, /* aWlBufferDeleted */ false);
222 static const struct wl_buffer_listener sBufferDetachListener = {
223 BufferDetachedCallbackHandler};
225 /* static */
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");
238 return nullptr;
241 LOGWAYLAND(" created [%p] WaylandDisplay [%p]\n", buffer.get(),
242 waylandDisplay);
244 return buffer;
247 bool WaylandBufferSHM::CreateWlBuffer() {
248 if (mWLBuffer) {
249 return true;
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);
256 if (!mWLBuffer) {
257 LOGWAYLAND(" failed to create wl_buffer");
258 return false;
261 if (wl_buffer_add_listener(mWLBuffer, &sBufferDetachListener, this) < 0) {
262 LOGWAYLAND(" failed to attach listener");
263 return false;
266 return true;
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());
278 if (!IsAttached()) {
279 DeleteWlBuffer();
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);
297 #ifdef MOZ_LOGGING
298 void WaylandBufferSHM::DumpToFile(const char* aHint) {
299 if (!mDumpSerial) {
300 return;
303 cairo_surface_t* surface = nullptr;
304 auto unmap = MakeScopeExit([&] {
305 if (surface) {
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) {
313 nsCString filename;
314 if (mDumpDir) {
315 filename.Append(mDumpDir);
316 filename.Append('/');
318 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());
324 #endif
326 /* static */
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");
337 return nullptr;
340 LOGWAYLAND("WaylandBufferDMABUF::CreateRGBA() [%p] UID %d [%d x %d]",
341 (void*)buffer, buffer->mDMABufSurface->GetUID(), aSize.width,
342 aSize.height);
343 return buffer.forget();
346 /* static */
347 already_AddRefed<WaylandBufferDMABUF> WaylandBufferDMABUF::CreateExternal(
348 RefPtr<DMABufSurface> aSurface) {
349 const auto size =
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);
363 if (mWLBuffer) {
364 return true;
367 LOGWAYLAND("WaylandBufferDMABUF::CreateWlBuffer() [%p] UID %d", (void*)this,
368 mDMABufSurface->GetUID());
370 mWLBuffer = mDMABufSurface->CreateWlBuffer();
371 if (!mWLBuffer) {
372 LOGWAYLAND(" failed to create wl_buffer");
373 return false;
376 if (wl_buffer_add_listener(mWLBuffer, &sBufferDetachListener, this) < 0) {
377 LOGWAYLAND(" failed to attach listener!");
378 return false;
381 return true;
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());
394 if (!IsAttached()) {
395 DeleteWlBuffer();
397 MOZ_DIAGNOSTIC_ASSERT(!HasWlBuffer());
400 } // namespace mozilla::widget