1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
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/. */
6 
7 #include "WaylandBuffer.h"
8 
9 #include <sys/mman.h>
10 #include <fcntl.h>
11 #include <errno.h>
12 
13 #include "gfx2DGlue.h"
14 #include "gfxPlatform.h"
15 #include "mozilla/WidgetUtilsGtk.h"
16 #include "mozilla/gfx/Tools.h"
17 #include "nsPrintfCString.h"
18 #include "prenv.h"  // For PR_GetEnv
19 
20 #ifdef MOZ_LOGGING
21 #  include "mozilla/Logging.h"
22 #  include "mozilla/ScopeExit.h"
23 #  include "Units.h"
24 extern mozilla::LazyLogModule gWidgetWaylandLog;
25 #  define LOGWAYLAND(...) \
26     MOZ_LOG(gWidgetWaylandLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
27 #else
28 #  define LOGWAYLAND(...)
29 #endif /* MOZ_LOGGING */
30 
31 using namespace mozilla::gl;
32 
33 namespace mozilla::widget {
34 
35 #define BUFFER_BPP 4
36 gfx::SurfaceFormat WaylandBuffer::mFormat = gfx::SurfaceFormat::B8G8R8A8;
37 
38 #ifdef MOZ_LOGGING
39 int WaylandBufferSHM::mDumpSerial =
40     PR_GetEnv("MOZ_WAYLAND_DUMP_WL_BUFFERS") ? 1 : 0;
41 char* WaylandBufferSHM::mDumpDir = PR_GetEnv("MOZ_WAYLAND_DUMP_DIR");
42 #endif
43 
WaylandAllocateShmMemory(int aSize)44 static int WaylandAllocateShmMemory(int aSize) {
45   int fd = -1;
46 
47   nsAutoCString shmPrefix("/");
48   if (const char* snapName = GetSnapInstanceName()) {
49     shmPrefix.AppendPrintf("snap.%s.", snapName);
50   }
51   shmPrefix.Append("wayland.mozilla.ipc");
52 
53   do {
54     static int counter = 0;
55     nsPrintfCString shmName("%s.%d", shmPrefix.get(), counter++);
56     fd = shm_open(shmName.get(), O_CREAT | O_RDWR | O_EXCL, 0600);
57     if (fd >= 0) {
58       // We don't want to use leaked file
59       if (shm_unlink(shmName.get()) != 0) {
60         NS_WARNING("shm_unlink failed");
61         return -1;
62       }
63     }
64   } while (fd < 0 && errno == EEXIST);
65 
66   if (fd < 0) {
67     NS_WARNING(nsPrintfCString("shm_open failed: %s", strerror(errno)).get());
68     return -1;
69   }
70 
71   int ret = 0;
72 #ifdef HAVE_POSIX_FALLOCATE
73   do {
74     ret = posix_fallocate(fd, 0, aSize);
75   } while (ret == EINTR);
76   if (ret == 0) {
77     return fd;
78   }
79   if (ret != ENODEV && ret != EINVAL && ret != EOPNOTSUPP) {
80     NS_WARNING(
81         nsPrintfCString("posix_fallocate() fails to allocate shm memory: %s",
82                         strerror(ret))
83             .get());
84     close(fd);
85     return -1;
86   }
87 #endif
88   do {
89     ret = ftruncate(fd, aSize);
90   } while (ret < 0 && errno == EINTR);
91   if (ret < 0) {
92     NS_WARNING(nsPrintfCString("ftruncate() fails to allocate shm memory: %s",
93                                strerror(ret))
94                    .get());
95     close(fd);
96     fd = -1;
97   }
98 
99   return fd;
100 }
101 
102 /* static */
Create(const RefPtr<nsWaylandDisplay> & aWaylandDisplay,int aSize)103 RefPtr<WaylandShmPool> WaylandShmPool::Create(
104     const RefPtr<nsWaylandDisplay>& aWaylandDisplay, int aSize) {
105   RefPtr<WaylandShmPool> shmPool = new WaylandShmPool(aSize);
106 
107   shmPool->mShmPoolFd = WaylandAllocateShmMemory(aSize);
108   if (shmPool->mShmPoolFd < 0) {
109     return nullptr;
110   }
111 
112   shmPool->mImageData = mmap(nullptr, aSize, PROT_READ | PROT_WRITE, MAP_SHARED,
113                              shmPool->mShmPoolFd, 0);
114   if (shmPool->mImageData == MAP_FAILED) {
115     NS_WARNING("Unable to map drawing surface!");
116     return nullptr;
117   }
118 
119   shmPool->mShmPool =
120       wl_shm_create_pool(aWaylandDisplay->GetShm(), shmPool->mShmPoolFd, aSize);
121   if (!shmPool->mShmPool) {
122     return nullptr;
123   }
124 
125   // We set our queue to get mShmPool events at compositor thread.
126   wl_proxy_set_queue((struct wl_proxy*)shmPool->mShmPool,
127                      aWaylandDisplay->GetEventQueue());
128 
129   return shmPool;
130 }
131 
WaylandShmPool(int aSize)132 WaylandShmPool::WaylandShmPool(int aSize)
133     : mShmPool(nullptr),
134       mShmPoolFd(-1),
135       mAllocatedSize(aSize),
136       mImageData(nullptr){};
137 
~WaylandShmPool()138 WaylandShmPool::~WaylandShmPool() {
139   if (mImageData != MAP_FAILED) {
140     munmap(mImageData, mAllocatedSize);
141     mImageData = MAP_FAILED;
142   }
143   g_clear_pointer(&mShmPool, wl_shm_pool_destroy);
144   if (mShmPoolFd >= 0) {
145     close(mShmPoolFd);
146     mShmPoolFd = -1;
147   }
148 }
149 
150 static const struct wl_buffer_listener sBufferListenerWaylandBuffer = {
151     WaylandBuffer::BufferReleaseCallbackHandler};
152 
WaylandBuffer(const LayoutDeviceIntSize & aSize)153 WaylandBuffer::WaylandBuffer(const LayoutDeviceIntSize& aSize) : mSize(aSize) {}
154 
AttachAndCommit(wl_surface * aSurface)155 void WaylandBuffer::AttachAndCommit(wl_surface* aSurface) {
156   LOGWAYLAND(
157       "WaylandBuffer::AttachAndCommit [%p] wl_surface %p ID %d wl_buffer "
158       "%p ID %d\n",
159       (void*)this, (void*)aSurface,
160       aSurface ? wl_proxy_get_id((struct wl_proxy*)aSurface) : -1,
161       (void*)GetWlBuffer(),
162       GetWlBuffer() ? wl_proxy_get_id((struct wl_proxy*)GetWlBuffer()) : -1);
163 
164   wl_buffer* buffer = GetWlBuffer();
165   if (buffer) {
166     mAttached = true;
167     wl_surface_attach(aSurface, buffer, 0, 0);
168     wl_surface_commit(aSurface);
169   }
170 }
171 
BufferReleaseCallbackHandler(wl_buffer * aBuffer)172 void WaylandBuffer::BufferReleaseCallbackHandler(wl_buffer* aBuffer) {
173   mAttached = false;
174 
175   if (mBufferReleaseFunc) {
176     mBufferReleaseFunc(mBufferReleaseData, aBuffer);
177   }
178 }
179 
BufferReleaseCallbackHandler(void * aData,wl_buffer * aBuffer)180 void WaylandBuffer::BufferReleaseCallbackHandler(void* aData,
181                                                  wl_buffer* aBuffer) {
182   auto* buffer = reinterpret_cast<WaylandBuffer*>(aData);
183   buffer->BufferReleaseCallbackHandler(aBuffer);
184 }
185 
186 /* static */
Create(const LayoutDeviceIntSize & aSize)187 RefPtr<WaylandBufferSHM> WaylandBufferSHM::Create(
188     const LayoutDeviceIntSize& aSize) {
189   RefPtr<WaylandBufferSHM> buffer = new WaylandBufferSHM(aSize);
190   RefPtr<nsWaylandDisplay> waylandDisplay = WaylandDisplayGet();
191 
192   int size = aSize.width * aSize.height * BUFFER_BPP;
193   buffer->mShmPool = WaylandShmPool::Create(waylandDisplay, size);
194   if (!buffer->mShmPool) {
195     return nullptr;
196   }
197 
198   buffer->mWLBuffer = wl_shm_pool_create_buffer(
199       buffer->mShmPool->GetShmPool(), 0, aSize.width, aSize.height,
200       aSize.width * BUFFER_BPP, WL_SHM_FORMAT_ARGB8888);
201   if (!buffer->mWLBuffer) {
202     return nullptr;
203   }
204 
205   wl_proxy_set_queue((struct wl_proxy*)buffer->GetWlBuffer(),
206                      waylandDisplay->GetEventQueue());
207   wl_buffer_add_listener(buffer->GetWlBuffer(), &sBufferListenerWaylandBuffer,
208                          buffer.get());
209 
210   LOGWAYLAND("WaylandBufferSHM Created [%p] WaylandDisplay [%p]\n",
211              buffer.get(), waylandDisplay.get());
212 
213   return buffer;
214 }
215 
WaylandBufferSHM(const LayoutDeviceIntSize & aSize)216 WaylandBufferSHM::WaylandBufferSHM(const LayoutDeviceIntSize& aSize)
217     : WaylandBuffer(aSize) {}
218 
~WaylandBufferSHM()219 WaylandBufferSHM::~WaylandBufferSHM() {
220   g_clear_pointer(&mWLBuffer, wl_buffer_destroy);
221 }
222 
Lock()223 already_AddRefed<gfx::DrawTarget> WaylandBufferSHM::Lock() {
224   return gfxPlatform::CreateDrawTargetForData(
225       static_cast<unsigned char*>(mShmPool->GetImageData()),
226       mSize.ToUnknownSize(), BUFFER_BPP * mSize.width, GetSurfaceFormat());
227 }
228 
Clear()229 void WaylandBufferSHM::Clear() {
230   memset(mShmPool->GetImageData(), 0, mSize.height * mSize.width * BUFFER_BPP);
231 }
232 
233 #ifdef MOZ_LOGGING
DumpToFile(const char * aHint)234 void WaylandBufferSHM::DumpToFile(const char* aHint) {
235   if (!mDumpSerial) {
236     return;
237   }
238 
239   cairo_surface_t* surface = nullptr;
240   auto unmap = MakeScopeExit([&] {
241     if (surface) {
242       cairo_surface_destroy(surface);
243     }
244   });
245   surface = cairo_image_surface_create_for_data(
246       (unsigned char*)mShmPool->GetImageData(), CAIRO_FORMAT_ARGB32,
247       mSize.width, mSize.height, BUFFER_BPP * mSize.width);
248   if (cairo_surface_status(surface) == CAIRO_STATUS_SUCCESS) {
249     nsCString filename;
250     if (mDumpDir) {
251       filename.Append(mDumpDir);
252       filename.Append('/');
253     }
254     filename.Append(
255         nsPrintfCString("firefox-wl-buffer-%.5d-%s.png", mDumpSerial++, aHint));
256     cairo_surface_write_to_png(surface, filename.get());
257     LOGWAYLAND("Dumped wl_buffer to %s\n", filename.get());
258   }
259 }
260 #endif
261 
262 /* static */
Create(const LayoutDeviceIntSize & aSize,GLContext * aGL)263 RefPtr<WaylandBufferDMABUF> WaylandBufferDMABUF::Create(
264     const LayoutDeviceIntSize& aSize, GLContext* aGL) {
265   RefPtr<WaylandBufferDMABUF> buffer = new WaylandBufferDMABUF(aSize);
266 
267   const auto flags =
268       static_cast<DMABufSurfaceFlags>(DMABUF_TEXTURE | DMABUF_ALPHA);
269   buffer->mDMABufSurface =
270       DMABufSurfaceRGBA::CreateDMABufSurface(aSize.width, aSize.height, flags);
271   if (!buffer->mDMABufSurface || !buffer->mDMABufSurface->CreateTexture(aGL)) {
272     return nullptr;
273   }
274 
275   if (!buffer->mDMABufSurface->CreateWlBuffer()) {
276     return nullptr;
277   }
278 
279   RefPtr<nsWaylandDisplay> waylandDisplay = WaylandDisplayGet();
280   wl_proxy_set_queue((struct wl_proxy*)buffer->GetWlBuffer(),
281                      waylandDisplay->GetEventQueue());
282   wl_buffer_add_listener(buffer->GetWlBuffer(), &sBufferListenerWaylandBuffer,
283                          buffer.get());
284 
285   return buffer;
286 }
287 
WaylandBufferDMABUF(const LayoutDeviceIntSize & aSize)288 WaylandBufferDMABUF::WaylandBufferDMABUF(const LayoutDeviceIntSize& aSize)
289     : WaylandBuffer(aSize) {}
290 
291 }  // namespace mozilla::widget
292