Backed out 2 changesets (bug 1855992) for causing talos failures @ mozilla::net:...
[gecko.git] / widget / gtk / WindowSurfaceWaylandMultiBuffer.cpp
blob96f478b726c232508a1433f4596ec57a0e961ccd
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 "WindowSurfaceWaylandMultiBuffer.h"
9 #include <errno.h>
10 #include <fcntl.h>
11 #include <sys/mman.h>
12 #include <prenv.h>
14 #include "gfx2DGlue.h"
15 #include "gfxPlatform.h"
16 #include "MozContainer.h"
17 #include "GtkCompositorWidget.h"
18 #include "mozilla/gfx/DataSurfaceHelpers.h"
19 #include "mozilla/gfx/Tools.h"
20 #include "mozilla/ScopeExit.h"
21 #include "mozilla/StaticPrefs_widget.h"
22 #include "mozilla/WidgetUtils.h"
24 #undef LOG
25 #ifdef MOZ_LOGGING
26 # include "mozilla/Logging.h"
27 # include "Units.h"
28 extern mozilla::LazyLogModule gWidgetWaylandLog;
29 # define LOGWAYLAND(...) \
30 MOZ_LOG(gWidgetWaylandLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
31 #else
32 # define LOGWAYLAND(...)
33 #endif /* MOZ_LOGGING */
35 namespace mozilla::widget {
38 Wayland multi-thread rendering scheme
40 Every rendering thread (main thread, compositor thread) contains its own
41 nsWaylandDisplay object connected to Wayland compositor (Mutter, Weston, etc.)
43 WindowSurfaceWayland implements WindowSurface class and draws nsWindow by
44 WindowSurface interface (Lock, Commit) to screen through nsWaylandDisplay.
46 ----------------------
47 | Wayland compositor |
48 ----------------------
51 ----------------------
52 | nsWaylandDisplay |
53 ----------------------
54 ^ ^
55 | |
56 | |
57 | --------------------------------- ------------------
58 | | WindowSurfaceWayland |<------>| nsWindow |
59 | | | ------------------
60 | | ----------------------- |
61 | | | WaylandBufferSHM | |
62 | | | | |
63 | | | ------------------- | |
64 | | | | WaylandShmPool | | |
65 | | | ------------------- | |
66 | | ----------------------- |
67 | | |
68 | | ----------------------- |
69 | | | WaylandBufferSHM | |
70 | | | | |
71 | | | ------------------- | |
72 | | | | WaylandShmPool | | |
73 | | | ------------------- | |
74 | | ----------------------- |
75 | ---------------------------------
78 --------------------------------- ------------------
79 | WindowSurfaceWayland |<------>| nsWindow |
80 | | ------------------
81 | ----------------------- |
82 | | WaylandBufferSHM | |
83 | | | |
84 | | ------------------- | |
85 | | | WaylandShmPool | | |
86 | | ------------------- | |
87 | ----------------------- |
88 | |
89 | ----------------------- |
90 | | WaylandBufferSHM | |
91 | | | |
92 | | ------------------- | |
93 | | | WaylandShmPool | | |
94 | | ------------------- | |
95 | ----------------------- |
96 ---------------------------------
99 nsWaylandDisplay
101 Is our connection to Wayland display server,
102 holds our display connection (wl_display) and event queue (wl_event_queue).
104 nsWaylandDisplay is created for every thread which sends data to Wayland
105 compositor. Wayland events for main thread is served by default Gtk+ loop,
106 for other threads (compositor) we must create wl_event_queue and run event loop.
109 WindowSurfaceWayland
111 Is a Wayland implementation of WindowSurface class for WindowSurfaceProvider,
112 we implement Lock() and Commit() interfaces from WindowSurface
113 for actual drawing.
115 One WindowSurfaceWayland draws one nsWindow so those are tied 1:1.
116 At Wayland level it holds one wl_surface object.
118 To perform visualiation of nsWindow, WindowSurfaceWayland contains one
119 wl_surface and two wl_buffer objects (owned by WaylandBufferSHM)
120 as we use double buffering. When nsWindow drawing is finished to wl_buffer,
121 the wl_buffer is attached to wl_surface and it's sent to Wayland compositor.
123 When there's no wl_buffer available for drawing (all wl_buffers are locked in
124 compositor for instance) we store the drawing to WindowImageSurface object
125 and draw later when wl_buffer becomes available or discard the
126 WindowImageSurface cache when whole screen is invalidated.
128 WaylandBufferSHM
130 Is a class which provides a wl_buffer for drawing.
131 Wl_buffer is a main Wayland object with actual graphics data.
132 Wl_buffer basically represent one complete window screen.
133 When double buffering is involved every window (GdkWindow for instance)
134 utilises two wl_buffers which are cycled. One is filed with data by application
135 and one is rendered by compositor.
137 WaylandBufferSHM is implemented by shared memory (shm).
138 It owns wl_buffer object, owns WaylandShmPool
139 (which provides the shared memory) and ties them together.
141 WaylandShmPool
143 WaylandShmPool acts as a manager of shared memory for WaylandBufferSHM.
144 Allocates it, holds reference to it and releases it.
146 We allocate shared memory (shm) by mmap(..., MAP_SHARED,...) as an interface
147 between us and wayland compositor. We draw our graphics data to the shm and
148 handle to wayland compositor by WaylandBufferSHM/WindowSurfaceWayland
149 (wl_buffer/wl_surface).
152 using gfx::DataSourceSurface;
154 #define BACK_BUFFER_NUM 3
156 WindowSurfaceWaylandMB::WindowSurfaceWaylandMB(
157 RefPtr<nsWindow> aWindow, GtkCompositorWidget* aCompositorWidget)
158 : mSurfaceLock("WindowSurfaceWayland lock"),
159 mWindow(std::move(aWindow)),
160 mCompositorWidget(aCompositorWidget),
161 mFrameInProcess(false),
162 mCallbackRequested(false) {}
164 bool WindowSurfaceWaylandMB::MaybeUpdateWindowSize() {
165 // We want to get window size from compositor widget as it matches window
166 // size used by parent RenderCompositorSWGL rendrer.
167 // For main thread rendering mCompositorWidget is not available so get
168 // window size directly from nsWindow.
169 LayoutDeviceIntSize newWindowSize = mCompositorWidget
170 ? mCompositorWidget->GetClientSize()
171 : mWindow->GetClientSize();
172 if (mWindowSize != newWindowSize) {
173 mWindowSize = newWindowSize;
174 return true;
176 return false;
179 already_AddRefed<DrawTarget> WindowSurfaceWaylandMB::Lock(
180 const LayoutDeviceIntRegion& aInvalidRegion) {
181 MutexAutoLock lock(mSurfaceLock);
183 #ifdef MOZ_LOGGING
184 gfx::IntRect lockRect = aInvalidRegion.GetBounds().ToUnknownRect();
185 LOGWAYLAND("WindowSurfaceWaylandMB::Lock [%p] [%d,%d] -> [%d x %d] rects %d",
186 (void*)mWindow.get(), lockRect.x, lockRect.y, lockRect.width,
187 lockRect.height, aInvalidRegion.GetNumRects());
188 #endif
190 if (mWindow->GetWindowType() == WindowType::Invisible) {
191 return nullptr;
193 mFrameInProcess = true;
195 CollectPendingSurfaces(lock);
197 if (MaybeUpdateWindowSize()) {
198 LOGWAYLAND(" new window size [%d x %d]", mWindowSize.width,
199 mWindowSize.height);
200 if (mInProgressBuffer) {
201 ReturnBufferToPool(lock, mInProgressBuffer);
202 mInProgressBuffer = nullptr;
204 if (mFrontBuffer) {
205 ReturnBufferToPool(lock, mFrontBuffer);
206 mFrontBuffer = nullptr;
208 mAvailableBuffers.Clear();
211 if (!mInProgressBuffer) {
212 if (mFrontBuffer && !mFrontBuffer->IsAttached()) {
213 mInProgressBuffer = mFrontBuffer;
214 } else {
215 mInProgressBuffer = ObtainBufferFromPool(lock, mWindowSize);
216 if (!mInProgressBuffer) {
217 return nullptr;
219 if (mFrontBuffer) {
220 HandlePartialUpdate(lock, aInvalidRegion);
221 ReturnBufferToPool(lock, mFrontBuffer);
224 mFrontBuffer = nullptr;
225 mFrontBufferInvalidRegion.SetEmpty();
228 RefPtr<DrawTarget> dt = mInProgressBuffer->Lock();
229 return dt.forget();
232 void WindowSurfaceWaylandMB::HandlePartialUpdate(
233 const MutexAutoLock& aProofOfLock,
234 const LayoutDeviceIntRegion& aInvalidRegion) {
235 LayoutDeviceIntRegion copyRegion;
236 if (mInProgressBuffer->GetBufferAge() == 2) {
237 copyRegion.Sub(mFrontBufferInvalidRegion, aInvalidRegion);
238 } else {
239 LayoutDeviceIntSize frontBufferSize = mFrontBuffer->GetSize();
240 copyRegion = LayoutDeviceIntRegion(LayoutDeviceIntRect(
241 0, 0, frontBufferSize.width, frontBufferSize.height));
242 copyRegion.SubOut(aInvalidRegion);
245 if (!copyRegion.IsEmpty()) {
246 RefPtr<DataSourceSurface> dataSourceSurface =
247 mozilla::gfx::CreateDataSourceSurfaceFromData(
248 mFrontBuffer->GetSize().ToUnknownSize(),
249 mFrontBuffer->GetSurfaceFormat(),
250 (const uint8_t*)mFrontBuffer->GetShmPool()->GetImageData(),
251 mFrontBuffer->GetSize().width *
252 BytesPerPixel(mFrontBuffer->GetSurfaceFormat()));
253 RefPtr<DrawTarget> dt = mInProgressBuffer->Lock();
255 for (auto iter = copyRegion.RectIter(); !iter.Done(); iter.Next()) {
256 LayoutDeviceIntRect r = iter.Get();
257 dt->CopySurface(dataSourceSurface, r.ToUnknownRect(),
258 gfx::IntPoint(r.x, r.y));
263 void WindowSurfaceWaylandMB::Commit(
264 const LayoutDeviceIntRegion& aInvalidRegion) {
265 MutexAutoLock lock(mSurfaceLock);
266 Commit(lock, aInvalidRegion);
269 void WindowSurfaceWaylandMB::Commit(
270 const MutexAutoLock& aProofOfLock,
271 const LayoutDeviceIntRegion& aInvalidRegion) {
272 #ifdef MOZ_LOGGING
273 gfx::IntRect invalidRect = aInvalidRegion.GetBounds().ToUnknownRect();
274 LOGWAYLAND(
275 "WindowSurfaceWaylandMB::Commit [%p] damage rect [%d, %d] -> [%d x %d] "
276 "Window [%d x %d]\n",
277 (void*)mWindow.get(), invalidRect.x, invalidRect.y, invalidRect.width,
278 invalidRect.height, mWindowSize.width, mWindowSize.height);
279 #endif
281 if (!mInProgressBuffer) {
282 // invisible window
283 return;
285 mFrameInProcess = false;
287 MozContainer* container = mWindow->GetMozContainer();
288 MozContainerSurfaceLock MozContainerLock(container);
289 struct wl_surface* waylandSurface = MozContainerLock.GetSurface();
290 if (!waylandSurface) {
291 LOGWAYLAND(
292 "WindowSurfaceWaylandMB::Commit [%p] frame queued: can't lock "
293 "wl_surface\n",
294 (void*)mWindow.get());
295 if (!mCallbackRequested) {
296 RefPtr<WindowSurfaceWaylandMB> self(this);
297 moz_container_wayland_add_initial_draw_callback_locked(
298 container, [self, aInvalidRegion]() -> void {
299 MutexAutoLock lock(self->mSurfaceLock);
300 if (!self->mFrameInProcess) {
301 self->Commit(lock, aInvalidRegion);
303 self->mCallbackRequested = false;
305 mCallbackRequested = true;
307 return;
310 if (moz_container_wayland_is_commiting_to_parent(container)) {
311 // When committing to parent surface we must use wl_surface_damage().
312 // A parent surface is created as v.3 object which does not support
313 // wl_surface_damage_buffer().
314 wl_surface_damage(waylandSurface, 0, 0, INT32_MAX, INT32_MAX);
315 } else {
316 for (auto iter = aInvalidRegion.RectIter(); !iter.Done(); iter.Next()) {
317 LayoutDeviceIntRect r = iter.Get();
318 wl_surface_damage_buffer(waylandSurface, r.x, r.y, r.width, r.height);
322 // aProofOfLock is a kind of substitution of MozContainerSurfaceLock.
323 // MozContainer is locked but MozContainerSurfaceLock doen't convert to
324 // MutexAutoLock& so we use aProofOfLock here.
325 moz_container_wayland_set_scale_factor_locked(
326 aProofOfLock, container, mWindow->GdkCeiledScaleFactor());
328 // It's possible that scale factor changed between Lock() and Commit()
329 // but window size is the same.
330 // Don't attach such buffer as it may have incorrect size,
331 // we'll paint new content soon.
332 if (moz_container_wayland_size_matches_scale_factor_locked(
333 aProofOfLock, container, mWindowSize.width, mWindowSize.height)) {
334 mInProgressBuffer->AttachAndCommit(waylandSurface);
337 mInProgressBuffer->ResetBufferAge();
338 mFrontBuffer = mInProgressBuffer;
339 mFrontBufferInvalidRegion = aInvalidRegion;
340 mInProgressBuffer = nullptr;
342 EnforcePoolSizeLimit(aProofOfLock);
343 IncrementBufferAge(aProofOfLock);
345 if (wl_display_flush(WaylandDisplayGet()->GetDisplay()) == -1) {
346 LOGWAYLAND("WindowSurfaceWaylandMB::Commit [%p] flush failed\n",
347 (void*)mWindow.get());
351 RefPtr<WaylandBufferSHM> WindowSurfaceWaylandMB::ObtainBufferFromPool(
352 const MutexAutoLock& aProofOfLock, const LayoutDeviceIntSize& aSize) {
353 if (!mAvailableBuffers.IsEmpty()) {
354 RefPtr<WaylandBufferSHM> buffer = mAvailableBuffers.PopLastElement();
355 mInUseBuffers.AppendElement(buffer);
356 return buffer;
359 RefPtr<WaylandBufferSHM> buffer = WaylandBufferSHM::Create(aSize);
360 if (buffer) {
361 mInUseBuffers.AppendElement(buffer);
364 return buffer;
367 void WindowSurfaceWaylandMB::ReturnBufferToPool(
368 const MutexAutoLock& aProofOfLock,
369 const RefPtr<WaylandBufferSHM>& aBuffer) {
370 if (aBuffer->IsAttached()) {
371 mPendingBuffers.AppendElement(aBuffer);
372 } else if (aBuffer->IsMatchingSize(mWindowSize)) {
373 mAvailableBuffers.AppendElement(aBuffer);
375 mInUseBuffers.RemoveElement(aBuffer);
378 void WindowSurfaceWaylandMB::EnforcePoolSizeLimit(
379 const MutexAutoLock& aProofOfLock) {
380 // Enforce the pool size limit, removing least-recently-used entries as
381 // necessary.
382 while (mAvailableBuffers.Length() > BACK_BUFFER_NUM) {
383 mAvailableBuffers.RemoveElementAt(0);
386 NS_WARNING_ASSERTION(mPendingBuffers.Length() < BACK_BUFFER_NUM,
387 "Are we leaking pending buffers?");
388 NS_WARNING_ASSERTION(mInUseBuffers.Length() < BACK_BUFFER_NUM,
389 "Are we leaking in-use buffers?");
392 void WindowSurfaceWaylandMB::CollectPendingSurfaces(
393 const MutexAutoLock& aProofOfLock) {
394 mPendingBuffers.RemoveElementsBy([&](auto& buffer) {
395 if (!buffer->IsAttached()) {
396 if (buffer->IsMatchingSize(mWindowSize)) {
397 mAvailableBuffers.AppendElement(std::move(buffer));
399 return true;
401 return false;
405 void WindowSurfaceWaylandMB::IncrementBufferAge(
406 const MutexAutoLock& aProofOfLock) {
407 for (const RefPtr<WaylandBufferSHM>& buffer : mInUseBuffers) {
408 buffer->IncrementBufferAge();
410 for (const RefPtr<WaylandBufferSHM>& buffer : mPendingBuffers) {
411 buffer->IncrementBufferAge();
413 for (const RefPtr<WaylandBufferSHM>& buffer : mAvailableBuffers) {
414 buffer->IncrementBufferAge();
418 } // namespace mozilla::widget