Bug 1422966 - implemented WindowBackBuffer to encapsulate wl_buffer, r=jhorak
authorMartin Stransky <stransky@redhat.com>
Mon, 04 Dec 2017 22:25:26 +0100
changeset 396547 4a82861a13995730173b1436aac8a48ee0f0c22b
parent 396546 9e720457902784c5584c9c2872772f8d412ea55a
child 396548 68803e96b4bebbcbdfede91ebc208cd75dfb81a7
push id57034
push userstransky@redhat.com
push dateFri, 15 Dec 2017 15:12:47 +0000
treeherderautoland@68803e96b4be [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjhorak
bugs1422966
milestone59.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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