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 #ifndef _MOZILLA_WIDGET_GTK_WINDOW_SURFACE_WAYLAND_H
8 #define _MOZILLA_WIDGET_GTK_WINDOW_SURFACE_WAYLAND_H
9 
10 #include <prthread.h>
11 #include "gfxImageSurface.h"
12 #include "mozilla/gfx/2D.h"
13 #include "mozilla/gfx/Types.h"
14 #include "nsWaylandDisplay.h"
15 #include "nsWindow.h"
16 #include "WaylandDMABufSurface.h"
17 #include "WindowSurface.h"
18 
19 #define BACK_BUFFER_NUM 3
20 
21 namespace mozilla {
22 namespace widget {
23 
24 class WindowSurfaceWayland;
25 
26 // Allocates and owns shared memory for Wayland drawing surface
27 class WaylandShmPool {
28  public:
29   WaylandShmPool(nsWaylandDisplay* aDisplay, int aSize);
30   ~WaylandShmPool();
31 
32   bool Resize(int aSize);
GetShmPool()33   wl_shm_pool* GetShmPool() { return mShmPool; };
GetImageData()34   void* GetImageData() { return mImageData; };
35   void SetImageDataFromPool(class WaylandShmPool* aSourcePool,
36                             int aImageDataSize);
37 
38  private:
39   wl_shm_pool* mShmPool;
40   int mShmPoolFd;
41   int mAllocatedSize;
42   void* mImageData;
43 };
44 
45 // Holds actual graphics data for wl_surface
46 class WindowBackBuffer {
47  public:
IsDMABufBuffer()48   virtual bool IsDMABufBuffer() { return false; };
49 
50   virtual already_AddRefed<gfx::DrawTarget> Lock() = 0;
51   virtual void Unlock() = 0;
52   virtual bool IsLocked() = 0;
53 
54   void Attach(wl_surface* aSurface);
55   virtual void Detach(wl_buffer* aBuffer) = 0;
56   virtual bool IsAttached() = 0;
57 
58   virtual void Clear() = 0;
59   virtual bool Resize(int aWidth, int aHeight) = 0;
60 
61   virtual int GetWidth() = 0;
62   virtual int GetHeight() = 0;
63   virtual wl_buffer* GetWlBuffer() = 0;
64   virtual void SetAttached() = 0;
65 
66   virtual bool SetImageDataFromBuffer(
67       class WindowBackBuffer* aSourceBuffer) = 0;
68 
IsMatchingSize(int aWidth,int aHeight)69   bool IsMatchingSize(int aWidth, int aHeight) {
70     return aWidth == GetWidth() && aHeight == GetHeight();
71   }
IsMatchingSize(class WindowBackBuffer * aBuffer)72   bool IsMatchingSize(class WindowBackBuffer* aBuffer) {
73     return aBuffer->IsMatchingSize(GetWidth(), GetHeight());
74   }
75 
GetSurfaceFormat()76   static gfx::SurfaceFormat GetSurfaceFormat() { return mFormat; }
77 
78   nsWaylandDisplay* GetWaylandDisplay();
79 
WindowBackBuffer(WindowSurfaceWayland * aWindowSurfaceWayland)80   WindowBackBuffer(WindowSurfaceWayland* aWindowSurfaceWayland)
81       : mWindowSurfaceWayland(aWindowSurfaceWayland){};
82   virtual ~WindowBackBuffer() = default;
83 
84  protected:
85   WindowSurfaceWayland* mWindowSurfaceWayland;
86 
87  private:
88   static gfx::SurfaceFormat mFormat;
89 };
90 
91 class WindowBackBufferShm : public WindowBackBuffer {
92  public:
93   WindowBackBufferShm(WindowSurfaceWayland* aWindowSurfaceWayland, int aWidth,
94                       int aHeight);
95   ~WindowBackBufferShm();
96 
97   already_AddRefed<gfx::DrawTarget> Lock();
IsLocked()98   bool IsLocked() { return mIsLocked; };
Unlock()99   void Unlock() { mIsLocked = false; };
100 
101   void Detach(wl_buffer* aBuffer);
IsAttached()102   bool IsAttached() { return mAttached; }
SetAttached()103   void SetAttached() { mAttached = true; };
104 
105   void Clear();
106   bool Resize(int aWidth, int aHeight);
107   bool SetImageDataFromBuffer(class WindowBackBuffer* aSourceBuffer);
108 
GetWidth()109   int GetWidth() { return mWidth; };
GetHeight()110   int GetHeight() { return mHeight; };
111 
GetWlBuffer()112   wl_buffer* GetWlBuffer() { return mWLBuffer; };
113 
114  private:
115   void Create(int aWidth, int aHeight);
116   void ReleaseShmSurface();
117 
118   // WaylandShmPool provides actual shared memory we draw into
119   WaylandShmPool mShmPool;
120 
121   // wl_buffer is a wayland object that encapsulates the shared memory
122   // and passes it to wayland compositor by wl_surface object.
123   wl_buffer* mWLBuffer;
124   int mWidth;
125   int mHeight;
126   bool mAttached;
127   bool mIsLocked;
128 };
129 
130 class WindowBackBufferDMABuf : public WindowBackBuffer {
131  public:
132   WindowBackBufferDMABuf(WindowSurfaceWayland* aWindowSurfaceWayland,
133                          int aWidth, int aHeight);
134   ~WindowBackBufferDMABuf();
135 
IsDMABufBuffer()136   bool IsDMABufBuffer() { return true; };
137 
138   bool IsAttached();
139   void SetAttached();
140 
141   int GetWidth();
142   int GetHeight();
143   wl_buffer* GetWlBuffer();
144 
145   bool SetImageDataFromBuffer(class WindowBackBuffer* aSourceBuffer);
146 
147   already_AddRefed<gfx::DrawTarget> Lock();
148   bool IsLocked();
149   void Unlock();
150 
151   void Clear();
152   void Detach(wl_buffer* aBuffer);
153   bool Resize(int aWidth, int aHeight);
154 
155  private:
156   RefPtr<WaylandDMABufSurfaceRGBA> mDMAbufSurface;
157 };
158 
159 class WindowImageSurface {
160  public:
161   static void Draw(gfx::SourceSurface* aSurface, gfx::DrawTarget* aDest,
162                    const LayoutDeviceIntRegion& aRegion);
163 
164   void Draw(gfx::DrawTarget* aDest,
165             LayoutDeviceIntRegion& aWaylandBufferDamage);
166 
167   WindowImageSurface(gfxImageSurface* aImageSurface,
168                      const LayoutDeviceIntRegion& aUpdateRegion);
169 
170   bool OverlapsSurface(class WindowImageSurface& aBottomSurface);
171 
GetUpdateRegion()172   const LayoutDeviceIntRegion* GetUpdateRegion() { return &mUpdateRegion; };
173 
174  private:
175   RefPtr<gfx::SourceSurface> mSurface;
176   RefPtr<gfxImageSurface> mImageSurface;
177   const LayoutDeviceIntRegion mUpdateRegion;
178 };
179 
180 // WindowSurfaceWayland is an abstraction for wl_surface
181 // and related management
182 class WindowSurfaceWayland : public WindowSurface {
183  public:
184   explicit WindowSurfaceWayland(nsWindow* aWindow);
185   ~WindowSurfaceWayland();
186 
187   // Lock() / Commit() are called by gecko when Firefox
188   // wants to display something. Lock() returns a DrawTarget
189   // where gecko paints. When gecko is done it calls Commit()
190   // and we try to send the DrawTarget (backed by wl_buffer)
191   // to wayland compositor.
192   //
193   // If we fail (wayland compositor is busy,
194   // wl_surface is not created yet) we queue the painting
195   // and we send it to wayland compositor in FrameCallbackHandler()/
196   // DelayedCommitHandler/CommitWaylandBuffer().
197   already_AddRefed<gfx::DrawTarget> Lock(
198       const LayoutDeviceIntRegion& aRegion) override;
199   void Commit(const LayoutDeviceIntRegion& aInvalidRegion) final;
200 
201   // It's called from wayland compositor when there's the right
202   // time to send wl_buffer to display. It's no-op if there's no
203   // queued commits.
204   void FrameCallbackHandler();
205 
206   // When a new window is created we may not have a valid wl_surface
207   // for drawing (Gtk haven't created it yet). All commits are queued
208   // and DelayedCommitHandler() is called by timer when wl_surface is ready
209   // for drawing.
210   void DelayedCommitHandler();
211 
212   // Try to commit all queued drawings to Wayland compositor. This is usually
213   // called from other routines but can be used to explicitly flush
214   // all drawings as we do when wl_buffer is released
215   // (see WindowBackBufferShm::Detach() for instance).
216   void CommitWaylandBuffer();
217 
GetWaylandDisplay()218   nsWaylandDisplay* GetWaylandDisplay() { return mWaylandDisplay; };
219 
220   // Image cache mode can be set by widget.wayland_cache_mode
221   typedef enum {
222     // Cache and clip all drawings, default. It's slowest
223     // but also without any rendered artifacts.
224     CACHE_ALL = 0,
225     // Cache drawing only when back buffer is missing. May produce
226     // some rendering artifacts and flickering when partial screen update
227     // is rendered.
228     CACHE_MISSING = 1,
229     // Don't cache anything, draw only when back buffer is available.
230     // Suitable for fullscreen content only like fullscreen video playback and
231     // may work well with dmabuf backend.
232     CACHE_NONE = 2
233   } RenderingCacheMode;
234 
235  private:
236   WindowBackBuffer* GetWaylandBufferWithSwitch();
237   WindowBackBuffer* GetWaylandBufferRecent();
238   WindowBackBuffer* SetNewWaylandBuffer(bool aAllowDMABufBackend);
239   WindowBackBuffer* CreateWaylandBuffer(int aWidth, int aHeight,
240                                         bool aUseDMABufBackend);
241   WindowBackBuffer* CreateWaylandBufferInternal(int aWidth, int aHeight,
242                                                 bool aUseDMABufBackend);
243   WindowBackBuffer* WaylandBufferFindAvailable(int aWidth, int aHeight,
244                                                bool aUseDMABufBackend);
245 
246   already_AddRefed<gfx::DrawTarget> LockWaylandBuffer();
247   void UnlockWaylandBuffer();
248 
249   already_AddRefed<gfx::DrawTarget> LockImageSurface(
250       const gfx::IntSize& aLockSize);
251 
252   void CacheImageSurface(const LayoutDeviceIntRegion& aRegion);
253   bool CommitImageCacheToWaylandBuffer();
254 
255   void DrawDelayedImageCommits(gfx::DrawTarget* aDrawTarget,
256                                LayoutDeviceIntRegion& aWaylandBufferDamage);
257 
258   // TODO: Do we need to hold a reference to nsWindow object?
259   nsWindow* mWindow;
260   // Buffer screen rects helps us understand if we operate on
261   // the same window size as we're called on WindowSurfaceWayland::Lock().
262   // mLockedScreenRect is window size when our wayland buffer was allocated.
263   LayoutDeviceIntRect mLockedScreenRect;
264 
265   // mWLBufferRect is an intersection of mozcontainer widgetsize and
266   // mLockedScreenRect size. It can be different than mLockedScreenRect
267   // during resize when mBounds are updated immediately but actual
268   // GtkWidget size is updated asynchronously (see Bug 1489463).
269   LayoutDeviceIntRect mWLBufferRect;
270   nsWaylandDisplay* mWaylandDisplay;
271 
272   // Actual buffer (backed by wl_buffer) where all drawings go into.
273   // Drawn areas are stored at mWaylandBufferDamage and if there's
274   // any uncommited drawings which needs to be send to wayland compositor
275   // the mBufferPendingCommit is set.
276   WindowBackBuffer* mWaylandBuffer;
277   WindowBackBuffer* mShmBackupBuffer[BACK_BUFFER_NUM];
278   WindowBackBuffer* mDMABackupBuffer[BACK_BUFFER_NUM];
279 
280   // When mWaylandFullscreenDamage we invalidate whole surface,
281   // otherwise partial screen updates (mWaylandBufferDamage) are used.
282   bool mWaylandFullscreenDamage;
283   LayoutDeviceIntRegion mWaylandBufferDamage;
284 
285   // After every commit to wayland compositor a frame callback is requested.
286   // Any next commit to wayland compositor will happen when frame callback
287   // comes from wayland compositor back as it's the best time to do the commit.
288   wl_callback* mFrameCallback;
289   wl_surface* mLastCommittedSurface;
290 
291   // Registered reference to pending DelayedCommitHandler() call.
292   WindowSurfaceWayland** mDelayedCommitHandle;
293 
294   // Cached drawings. If we can't get WaylandBuffer (wl_buffer) at
295   // WindowSurfaceWayland::Lock() we direct gecko rendering to
296   // mImageSurface.
297   // If we can't get WaylandBuffer at WindowSurfaceWayland::Commit()
298   // time, mImageSurface is moved to mDelayedImageCommits which
299   // holds all cached drawings.
300   // mDelayedImageCommits can be drawn by FrameCallbackHandler(),
301   // DelayedCommitHandler() or when WaylandBuffer is detached.
302   RefPtr<gfxImageSurface> mImageSurface;
303   AutoTArray<WindowImageSurface, 30> mDelayedImageCommits;
304 
305   int64_t mLastCommitTime;
306 
307   // Indicates that we don't have any cached drawings at mDelayedImageCommits
308   // and WindowSurfaceWayland::Lock() returned WaylandBuffer to gecko
309   // to draw into.
310   bool mDrawToWaylandBufferDirectly;
311 
312   // Set when our cached drawings (mDelayedImageCommits) contains
313   // full screen damage. That means we can safely switch WaylandBuffer
314   // at LockWaylandBuffer().
315   bool mCanSwitchWaylandBuffer;
316 
317   // Set when actual WaylandBuffer contains drawings which are not send to
318   // wayland compositor yet.
319   bool mBufferPendingCommit;
320 
321   // We can't send WaylandBuffer (wl_buffer) to compositor when gecko
322   // is rendering into it (i.e. between WindowSurfaceWayland::Lock() /
323   // WindowSurfaceWayland::Commit()).
324   // Thus we use mBufferCommitAllowed to disable commit by callbacks
325   // (FrameCallbackHandler(), DelayedCommitHandler())
326   bool mBufferCommitAllowed;
327 
328   // We need to clear WaylandBuffer when entire transparent window is repainted.
329   // This typically apply to popup windows.
330   bool mBufferNeedsClear;
331 
332   // Cache all drawings except fullscreen updates.
333   // Avoid any rendering artifacts for significant performance penality.
334   bool mSmoothRendering;
335 
336   bool mIsMainThread;
337 
338   static bool UseDMABufBackend();
339   static bool mUseDMABufInitialized;
340   static bool mUseDMABuf;
341 };
342 
343 }  // namespace widget
344 }  // namespace mozilla
345 
346 #endif  // _MOZILLA_WIDGET_GTK_WINDOW_SURFACE_WAYLAND_H
347