Bug 1751217 Part 3: Make HDR-capable macOS screens report 30 pixelDepth. r=mstange
[gecko.git] / widget / gtk / WindowSurfaceWaylandMultiBuffer.cpp
blob756201b64a7237cc71ed0fccfc2652d87fd63d3c
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>
13 #include "gfx2DGlue.h"
14 #include "gfxPlatform.h"
15 #include "MozContainer.h"
16 #include "mozilla/gfx/DataSurfaceHelpers.h"
17 #include "mozilla/gfx/Tools.h"
18 #include "mozilla/ScopeExit.h"
19 #include "mozilla/StaticPrefs_widget.h"
20 #include "mozilla/WidgetUtils.h"
22 #undef LOG
23 #ifdef MOZ_LOGGING
24 # include "mozilla/Logging.h"
25 # include "Units.h"
26 extern mozilla::LazyLogModule gWidgetWaylandLog;
27 # define LOGWAYLAND(...) \
28 MOZ_LOG(gWidgetWaylandLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
29 #else
30 # define LOGWAYLAND(...)
31 #endif /* MOZ_LOGGING */
33 namespace mozilla::widget {
36 Wayland multi-thread rendering scheme
38 Every rendering thread (main thread, compositor thread) contains its own
39 nsWaylandDisplay object connected to Wayland compositor (Mutter, Weston, etc.)
41 WindowSurfaceWayland implements WindowSurface class and draws nsWindow by
42 WindowSurface interface (Lock, Commit) to screen through nsWaylandDisplay.
44 ----------------------
45 | Wayland compositor |
46 ----------------------
49 ----------------------
50 | nsWaylandDisplay |
51 ----------------------
52 ^ ^
53 | |
54 | |
55 | --------------------------------- ------------------
56 | | WindowSurfaceWayland |<------>| nsWindow |
57 | | | ------------------
58 | | ----------------------- |
59 | | | WaylandBufferSHM | |
60 | | | | |
61 | | | ------------------- | |
62 | | | | WaylandShmPool | | |
63 | | | ------------------- | |
64 | | ----------------------- |
65 | | |
66 | | ----------------------- |
67 | | | WaylandBufferSHM | |
68 | | | | |
69 | | | ------------------- | |
70 | | | | WaylandShmPool | | |
71 | | | ------------------- | |
72 | | ----------------------- |
73 | ---------------------------------
76 --------------------------------- ------------------
77 | WindowSurfaceWayland |<------>| nsWindow |
78 | | ------------------
79 | ----------------------- |
80 | | WaylandBufferSHM | |
81 | | | |
82 | | ------------------- | |
83 | | | WaylandShmPool | | |
84 | | ------------------- | |
85 | ----------------------- |
86 | |
87 | ----------------------- |
88 | | WaylandBufferSHM | |
89 | | | |
90 | | ------------------- | |
91 | | | WaylandShmPool | | |
92 | | ------------------- | |
93 | ----------------------- |
94 ---------------------------------
97 nsWaylandDisplay
99 Is our connection to Wayland display server,
100 holds our display connection (wl_display) and event queue (wl_event_queue).
102 nsWaylandDisplay is created for every thread which sends data to Wayland
103 compositor. Wayland events for main thread is served by default Gtk+ loop,
104 for other threads (compositor) we must create wl_event_queue and run event loop.
107 WindowSurfaceWayland
109 Is a Wayland implementation of WindowSurface class for WindowSurfaceProvider,
110 we implement Lock() and Commit() interfaces from WindowSurface
111 for actual drawing.
113 One WindowSurfaceWayland draws one nsWindow so those are tied 1:1.
114 At Wayland level it holds one wl_surface object.
116 To perform visualiation of nsWindow, WindowSurfaceWayland contains one
117 wl_surface and two wl_buffer objects (owned by WaylandBufferSHM)
118 as we use double buffering. When nsWindow drawing is finished to wl_buffer,
119 the wl_buffer is attached to wl_surface and it's sent to Wayland compositor.
121 When there's no wl_buffer available for drawing (all wl_buffers are locked in
122 compositor for instance) we store the drawing to WindowImageSurface object
123 and draw later when wl_buffer becomes available or discard the
124 WindowImageSurface cache when whole screen is invalidated.
126 WaylandBufferSHM
128 Is a class which provides a wl_buffer for drawing.
129 Wl_buffer is a main Wayland object with actual graphics data.
130 Wl_buffer basically represent one complete window screen.
131 When double buffering is involved every window (GdkWindow for instance)
132 utilises two wl_buffers which are cycled. One is filed with data by application
133 and one is rendered by compositor.
135 WaylandBufferSHM is implemented by shared memory (shm).
136 It owns wl_buffer object, owns WaylandShmPool
137 (which provides the shared memory) and ties them together.
139 WaylandShmPool
141 WaylandShmPool acts as a manager of shared memory for WaylandBufferSHM.
142 Allocates it, holds reference to it and releases it.
144 We allocate shared memory (shm) by mmap(..., MAP_SHARED,...) as an interface
145 between us and wayland compositor. We draw our graphics data to the shm and
146 handle to wayland compositor by WaylandBufferSHM/WindowSurfaceWayland
147 (wl_buffer/wl_surface).
150 using gfx::DataSourceSurface;
152 #define BACK_BUFFER_NUM 3
154 WindowSurfaceWaylandMB::WindowSurfaceWaylandMB(RefPtr<nsWindow> aWindow)
155 : mSurfaceLock("WindowSurfaceWayland lock"),
156 mWindow(std::move(aWindow)),
157 mFrameInProcess(false),
158 mCallbackRequested(false) {}
160 already_AddRefed<DrawTarget> WindowSurfaceWaylandMB::Lock(
161 const LayoutDeviceIntRegion& aInvalidRegion) {
162 MutexAutoLock lock(mSurfaceLock);
164 #ifdef MOZ_LOGGING
165 gfx::IntRect lockRect = aInvalidRegion.GetBounds().ToUnknownRect();
166 LOGWAYLAND("WindowSurfaceWaylandMB::Lock [%p] [%d,%d] -> [%d x %d] rects %d",
167 (void*)mWindow.get(), lockRect.x, lockRect.y, lockRect.width,
168 lockRect.height, aInvalidRegion.GetNumRects());
169 #endif
171 if (mWindow->WindowType() == eWindowType_invisible) {
172 return nullptr;
174 mFrameInProcess = true;
176 CollectPendingSurfaces(lock);
178 LayoutDeviceIntSize newMozContainerSize = mWindow->GetMozContainerSize();
179 if (mMozContainerSize != newMozContainerSize) {
180 mMozContainerSize = newMozContainerSize;
181 LOGWAYLAND(" new MozContainer size [%d x %d]", mMozContainerSize.width,
182 mMozContainerSize.height);
183 if (mInProgressBuffer) {
184 ReturnBufferToPool(lock, mInProgressBuffer);
185 mInProgressBuffer = nullptr;
187 if (mFrontBuffer) {
188 ReturnBufferToPool(lock, mFrontBuffer);
189 mFrontBuffer = nullptr;
191 mAvailableBuffers.Clear();
194 if (!mInProgressBuffer) {
195 if (mFrontBuffer && !mFrontBuffer->IsAttached()) {
196 mInProgressBuffer = mFrontBuffer;
197 } else {
198 mInProgressBuffer = ObtainBufferFromPool(lock, mMozContainerSize);
199 if (mFrontBuffer) {
200 HandlePartialUpdate(lock, aInvalidRegion);
201 ReturnBufferToPool(lock, mFrontBuffer);
204 mFrontBuffer = nullptr;
205 mFrontBufferInvalidRegion.SetEmpty();
208 RefPtr<DrawTarget> dt = mInProgressBuffer->Lock();
209 return dt.forget();
212 void WindowSurfaceWaylandMB::HandlePartialUpdate(
213 const MutexAutoLock& aProofOfLock,
214 const LayoutDeviceIntRegion& aInvalidRegion) {
215 LayoutDeviceIntRegion copyRegion;
216 if (mInProgressBuffer->GetBufferAge() == 2) {
217 copyRegion.Sub(mFrontBufferInvalidRegion, aInvalidRegion);
218 } else {
219 LayoutDeviceIntSize frontBufferSize = mFrontBuffer->GetSize();
220 copyRegion = LayoutDeviceIntRegion(LayoutDeviceIntRect(
221 0, 0, frontBufferSize.width, frontBufferSize.height));
222 copyRegion.SubOut(aInvalidRegion);
225 if (!copyRegion.IsEmpty()) {
226 RefPtr<DataSourceSurface> dataSourceSurface =
227 mozilla::gfx::CreateDataSourceSurfaceFromData(
228 mFrontBuffer->GetSize().ToUnknownSize(),
229 mFrontBuffer->GetSurfaceFormat(),
230 (const uint8_t*)mFrontBuffer->GetShmPool()->GetImageData(),
231 mFrontBuffer->GetSize().width *
232 BytesPerPixel(mFrontBuffer->GetSurfaceFormat()));
233 RefPtr<DrawTarget> dt = mInProgressBuffer->Lock();
235 for (auto iter = copyRegion.RectIter(); !iter.Done(); iter.Next()) {
236 LayoutDeviceIntRect r = iter.Get();
237 dt->CopySurface(dataSourceSurface, r.ToUnknownRect(),
238 gfx::IntPoint(r.x, r.y));
243 void WindowSurfaceWaylandMB::Commit(
244 const LayoutDeviceIntRegion& aInvalidRegion) {
245 MutexAutoLock lock(mSurfaceLock);
246 Commit(lock, aInvalidRegion);
249 void WindowSurfaceWaylandMB::Commit(
250 const MutexAutoLock& aProofOfLock,
251 const LayoutDeviceIntRegion& aInvalidRegion) {
252 #ifdef MOZ_LOGGING
253 gfx::IntRect invalidRect = aInvalidRegion.GetBounds().ToUnknownRect();
254 LOGWAYLAND(
255 "WindowSurfaceWaylandMB::Commit [%p] damage rect [%d, %d] -> [%d x %d] "
256 "MozContainer [%d x %d]\n",
257 (void*)mWindow.get(), invalidRect.x, invalidRect.y, invalidRect.width,
258 invalidRect.height, mMozContainerSize.width, mMozContainerSize.height);
259 #endif
261 if (!mInProgressBuffer) {
262 // invisible window
263 return;
265 mFrameInProcess = false;
267 MozContainer* container = mWindow->GetMozContainer();
268 wl_surface* waylandSurface = moz_container_wayland_surface_lock(container);
269 if (!waylandSurface) {
270 LOGWAYLAND(
271 "WindowSurfaceWaylandMB::Commit [%p] frame queued: can't lock "
272 "wl_surface\n",
273 (void*)mWindow.get());
274 if (!mCallbackRequested) {
275 RefPtr<WindowSurfaceWaylandMB> self(this);
276 moz_container_wayland_add_initial_draw_callback(
277 container, [self, aInvalidRegion]() -> void {
278 MutexAutoLock lock(self->mSurfaceLock);
279 if (!self->mFrameInProcess) {
280 self->Commit(lock, aInvalidRegion);
282 self->mCallbackRequested = false;
284 mCallbackRequested = true;
286 return;
289 if (moz_container_wayland_is_commiting_to_parent(container)) {
290 // When committing to parent surface we must use wl_surface_damage().
291 // A parent surface is created as v.3 object which does not support
292 // wl_surface_damage_buffer().
293 wl_surface_damage(waylandSurface, 0, 0, INT32_MAX, INT32_MAX);
294 } else {
295 for (auto iter = aInvalidRegion.RectIter(); !iter.Done(); iter.Next()) {
296 LayoutDeviceIntRect r = iter.Get();
297 wl_surface_damage_buffer(waylandSurface, r.x, r.y, r.width, r.height);
301 moz_container_wayland_set_scale_factor_locked(container);
302 mInProgressBuffer->AttachAndCommit(waylandSurface);
303 moz_container_wayland_surface_unlock(container, &waylandSurface);
305 mInProgressBuffer->ResetBufferAge();
306 mFrontBuffer = mInProgressBuffer;
307 mFrontBufferInvalidRegion = aInvalidRegion;
308 mInProgressBuffer = nullptr;
310 EnforcePoolSizeLimit(aProofOfLock);
311 IncrementBufferAge(aProofOfLock);
313 if (wl_display_flush(WaylandDisplayGet()->GetDisplay()) == -1) {
314 LOGWAYLAND("WindowSurfaceWaylandMB::Commit [%p] flush failed\n",
315 (void*)mWindow.get());
319 RefPtr<WaylandBufferSHM> WindowSurfaceWaylandMB::ObtainBufferFromPool(
320 const MutexAutoLock& aProofOfLock, const LayoutDeviceIntSize& aSize) {
321 if (!mAvailableBuffers.IsEmpty()) {
322 RefPtr<WaylandBufferSHM> buffer = mAvailableBuffers.PopLastElement();
323 mInUseBuffers.AppendElement(buffer);
324 return buffer;
327 RefPtr<WaylandBufferSHM> buffer = WaylandBufferSHM::Create(aSize);
328 mInUseBuffers.AppendElement(buffer);
330 return buffer;
333 void WindowSurfaceWaylandMB::ReturnBufferToPool(
334 const MutexAutoLock& aProofOfLock,
335 const RefPtr<WaylandBufferSHM>& aBuffer) {
336 if (aBuffer->IsAttached()) {
337 mPendingBuffers.AppendElement(aBuffer);
338 } else if (aBuffer->IsMatchingSize(mMozContainerSize)) {
339 mAvailableBuffers.AppendElement(aBuffer);
341 mInUseBuffers.RemoveElement(aBuffer);
344 void WindowSurfaceWaylandMB::EnforcePoolSizeLimit(
345 const MutexAutoLock& aProofOfLock) {
346 // Enforce the pool size limit, removing least-recently-used entries as
347 // necessary.
348 while (mAvailableBuffers.Length() > BACK_BUFFER_NUM) {
349 mAvailableBuffers.RemoveElementAt(0);
352 NS_WARNING_ASSERTION(mPendingBuffers.Length() < BACK_BUFFER_NUM,
353 "Are we leaking pending buffers?");
354 NS_WARNING_ASSERTION(mInUseBuffers.Length() < BACK_BUFFER_NUM,
355 "Are we leaking in-use buffers?");
358 void WindowSurfaceWaylandMB::CollectPendingSurfaces(
359 const MutexAutoLock& aProofOfLock) {
360 mPendingBuffers.RemoveElementsBy([&](auto& buffer) {
361 if (!buffer->IsAttached()) {
362 if (buffer->IsMatchingSize(mMozContainerSize)) {
363 mAvailableBuffers.AppendElement(std::move(buffer));
365 return true;
367 return false;
371 void WindowSurfaceWaylandMB::IncrementBufferAge(
372 const MutexAutoLock& aProofOfLock) {
373 for (const RefPtr<WaylandBufferSHM>& buffer : mInUseBuffers) {
374 buffer->IncrementBufferAge();
376 for (const RefPtr<WaylandBufferSHM>& buffer : mPendingBuffers) {
377 buffer->IncrementBufferAge();
379 for (const RefPtr<WaylandBufferSHM>& buffer : mAvailableBuffers) {
380 buffer->IncrementBufferAge();
384 } // namespace mozilla::widget