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