Bug 1422966 - implemented WindowBackBuffer to encapsulate wl_buffer, r?jhorak draft
authorMartin Stransky <stransky@redhat.com>
Mon, 04 Dec 2017 22:25:26 +0100
changeset 711990 7d281617386ca8b6042763ade14cd0adb66a790a
parent 711989 c4e9f9cdca02b5cb957d437c63e07b5e9512ee8a
child 711991 a89f9609fbc245799697969f94d5ef24aba069bb
child 711992 4a0db35e2328da8b7885bd8c9f9cacd78870cadd
child 712044 51c264306d7547143f0e905e62a4d98bc7c58c2a
push id93216
push userstransky@redhat.com
push dateFri, 15 Dec 2017 09:42:38 +0000
reviewersjhorak
bugs1422966
milestone59.0a1
Bug 1422966 - implemented WindowBackBuffer to encapsulate wl_buffer, r?jhorak wl_buffer is a main Wayland object with graphics data. wl_buffer basically represent one complete window screen. When double buffering is involved every window (GdkWindow in our case) utilises two wl_buffers which are cycled. One is filed with data by application and one is rendered by compositor. WindowBackBuffer class manages one wl_buffer. It owns wl_buffer object, owns WaylandShmPool (which provides shared memory) and ties them together. MozReview-Commit-ID: v8Hlezo7oD
widget/gtk/WindowSurfaceWayland.cpp
widget/gtk/WindowSurfaceWayland.h
--- a/widget/gtk/WindowSurfaceWayland.cpp
+++ b/widget/gtk/WindowSurfaceWayland.cpp
@@ -129,16 +129,18 @@ We allocate shared memory (shm) by mmap(
 between us and wayland compositor. We draw our graphics data to the shm and
 handle to wayland compositor by WindowBackBuffer/WindowSurfaceWayland
 (wl_buffer/wl_surface).
 */
 
 namespace mozilla {
 namespace widget {
 
+#define BUFFER_BPP 4
+
 // TODO: How many rendering threads do we actualy handle?
 static nsCOMArray<nsWaylandDisplay> gWaylandDisplays;
 static StaticMutex gWaylandDisplaysMutex;
 
 // Each thread which is using wayland connection (wl_display) has to operate
 // its own wl_event_queue. Main Firefox thread wl_event_queue is handled
 // by Gtk main loop, other threads/wl_event_queue has to be handled by us.
 //
@@ -412,17 +414,129 @@ WaylandShmPool::Resize(int aSize)
                     PROT_READ | PROT_WRITE, MAP_SHARED, mShmPoolFd, 0);
   if (mImageData == MAP_FAILED)
     return false;
 
   mAllocatedSize = aSize;
   return true;
 }
 
+void
+WaylandShmPool::SetImageDataFromPool(class WaylandShmPool* aSourcePool,
+                                     int aImageDataSize)
+{
+  MOZ_ASSERT(mAllocatedSize <= aImageDataSize, "WaylandShmPool overflows!");
+  memcpy(mImageData, aSourcePool->GetImageData(), aImageDataSize);
+}
+
 WaylandShmPool::~WaylandShmPool()
 {
   munmap(mImageData, mAllocatedSize);
   wl_shm_pool_destroy(mShmPool);
   close(mShmPoolFd);
 }
 
+static void
+buffer_release(void *data, wl_buffer *buffer)
+{
+  auto surface = reinterpret_cast<WindowBackBuffer*>(data);
+  surface->Detach();
+}
+
+static const struct wl_buffer_listener buffer_listener = {
+  buffer_release
+};
+
+void WindowBackBuffer::Create(int aWidth, int aHeight)
+{
+  MOZ_ASSERT(!IsAttached(), "We can't resize attached buffers.");
+
+  int newBufferSize = aWidth*aHeight*BUFFER_BPP;
+  mShmPool.Resize(newBufferSize);
+
+  mWaylandBuffer = wl_shm_pool_create_buffer(mShmPool.GetShmPool(), 0,
+                                            aWidth, aHeight, aWidth*BUFFER_BPP,
+                                            WL_SHM_FORMAT_ARGB8888);
+  wl_proxy_set_queue((struct wl_proxy *)mWaylandBuffer,
+                     mWaylandDisplay->GetEventQueue());
+  wl_buffer_add_listener(mWaylandBuffer, &buffer_listener, this);
+
+  mWidth = aWidth;
+  mHeight = aHeight;
+}
+
+void WindowBackBuffer::Release()
+{
+  wl_buffer_destroy(mWaylandBuffer);
+  mWidth = mHeight = 0;
+}
+
+WindowBackBuffer::WindowBackBuffer(nsWaylandDisplay* aWaylandDisplay,
+                                   int aWidth, int aHeight)
+ : mShmPool(aWaylandDisplay, aWidth*aHeight*BUFFER_BPP)
+  ,mWaylandBuffer(nullptr)
+  ,mWidth(aWidth)
+  ,mHeight(aHeight)
+  ,mAttached(false)
+  ,mWaylandDisplay(aWaylandDisplay)
+{
+  Create(aWidth, aHeight);
+}
+
+WindowBackBuffer::~WindowBackBuffer()
+{
+  Release();
+}
+
+bool
+WindowBackBuffer::Resize(int aWidth, int aHeight)
+{
+  if (aWidth == mWidth && aHeight == mHeight)
+    return true;
+
+  Release();
+  Create(aWidth, aHeight);
+
+  return (mWaylandBuffer != nullptr);
+}
+
+void
+WindowBackBuffer::Attach(wl_surface* aSurface)
+{
+  wl_surface_attach(aSurface, mWaylandBuffer, 0, 0);
+  wl_surface_commit(aSurface);
+  wl_display_flush(mWaylandDisplay->GetDisplay());
+  mAttached = true;
+}
+
+void
+WindowBackBuffer::Detach()
+{
+  mAttached = false;
+}
+
+bool
+WindowBackBuffer::SetImageDataFromBackBuffer(
+  class WindowBackBuffer* aSourceBuffer)
+{
+  if (!MatchSize(aSourceBuffer)) {
+    Resize(aSourceBuffer->mWidth, aSourceBuffer->mHeight);
+  }
+
+  mShmPool.SetImageDataFromPool(aSourceBuffer->mShmPool,
+    aSourceBuffer->mWidth * aSourceBuffer->mHeight * BUFFER_BPP);
+  return true;
+}
+
+already_AddRefed<gfx::DrawTarget>
+WindowBackBuffer::Lock(const LayoutDeviceIntRegion& aRegion)
+{
+  gfx::IntRect bounds = aRegion.GetBounds().ToUnknownRect();
+  gfx::IntSize lockSize(bounds.XMost(), bounds.YMost());
+
+  return gfxPlatform::CreateDrawTargetForData(static_cast<unsigned char*>(mShmPool.GetImageData()),
+                                              lockSize,
+                                              BUFFER_BPP * mWidth,
+                                              mWaylandDisplay->GetSurfaceFormat());
+}
+
 }  // namespace widget
 }  // namespace mozilla
--- a/widget/gtk/WindowSurfaceWayland.h
+++ b/widget/gtk/WindowSurfaceWayland.h
@@ -43,22 +43,63 @@ private:
 class WaylandShmPool {
 public:
   WaylandShmPool(nsWaylandDisplay* aDisplay, int aSize);
   ~WaylandShmPool();
 
   bool                Resize(int aSize);
   wl_shm_pool*        GetShmPool()    { return mShmPool;   };
   void*               GetImageData()  { return mImageData; };
+  void                SetImageDataFromPool(class WaylandShmPool* aSourcePool);
 
 private:
   int CreateTemporaryFile(int aSize);
 
   wl_shm_pool*        mShmPool;
   int                 mShmPoolFd;
   int                 mAllocatedSize;
   void*               mImageData;
 };
 
+// Holds actual graphics data for wl_surface
+class WindowBackBuffer {
+public:
+  WindowBackBuffer(nsWaylandDisplay* aDisplay, int aWidth, int aHeight);
+  ~WindowBackBuffer();
+
+  already_AddRefed<gfx::DrawTarget> Lock(const LayoutDeviceIntRegion& aRegion);
+
+  void Attach(wl_surface* aSurface);
+  void Detach();
+  bool IsAttached() { return mAttached; }
+
+  bool Resize(int aWidth, int aHeight);
+  bool SetImageDataFromBackBuffer(class WindowBackBuffer* aSourceBuffer);
+
+  bool MatchSize(int aWidth, int aHeight)
+  {
+    return aWidth == mWidth && aHeight == mHeight;
+  }
+  bool MatchSize(class WindowBackBuffer *aBuffer)
+  {
+    return aBuffer->mWidth == mWidth && aBuffer->mHeight == mHeight;
+  }
+
+private:
+  void Create(int aWidth, int aHeight);
+  void Release();
+
+  // WaylandShmPool provides actual shared memory we draw into
+  WaylandShmPool      mShmPool;
+
+  // wl_buffer is a wayland object that encapsulates the shared memory
+  // and passes it to wayland compositor by wl_surface object.
+  wl_buffer*          mWaylandBuffer;
+  int                 mWidth;
+  int                 mHeight;
+  bool                mAttached;
+  nsWaylandDisplay*   mWaylandDisplay;
+};
+
 }  // namespace widget
 }  // namespace mozilla
 
 #endif // _MOZILLA_WIDGET_GTK_WINDOW_SURFACE_WAYLAND_H