Skip to content

Commit

Permalink
Bug 1708416 - Implement n-buffering for the Wayland software backend,…
Browse files Browse the repository at this point in the history
… r=stransky

In order to fully comply with the spec and work on all compositors.
This is somewhat inspired by `SurfacePoolCA`, moving buffers between
buckets - in use or available. The idea is to make it easy to expand
this further to share a common buffer pool between multiple surfaces.

It works similar to EGL buffer handling and reuses the partial damage
logic in WR by implementing buffer age.

Notable changes to the current implementation:
 - always draw directly into shm-buffer memory - no caching.
 - drawing is purely driven by the compositor / the VsyncSource.
   No frame callbacks or threads apart from buffer release
   callbacks.

One of the goals here is to handle different compositor behaviour
efficiently - if a compositor releases buffers early, we want to
reuse a single buffer whenever possible. If a compositor holds buffers
longer, we want to minimize the area we need to redraw to older buffers.
This is archived by always using the last released buffer. The expected
usual buffer age is `1` in the fast release case and `2`-`3` in the
holding case.

This changes buffer handling quite significantly, so propper testing
is due. However, as the overall architecture is somewhat simpler than
before, it may improve stability in the long term.

Note: instead of replacing, this now duplicates parts of the existing
backend. This is in order to keep the existing one stable and gruadually
phasing it out together with the Basic compositor.

Differential Revision: https://phabricator.services.mozilla.com/D114349
  • Loading branch information
rmader committed May 18, 2021
1 parent 654541c commit c2f64d7
Show file tree
Hide file tree
Showing 11 changed files with 532 additions and 14 deletions.
30 changes: 30 additions & 0 deletions gfx/webrender_bindings/RenderCompositorSWGL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@
#include "mozilla/gfx/Logging.h"
#include "mozilla/widget/CompositorWidget.h"

#ifdef MOZ_WAYLAND
# include "mozilla/gfx/gfxVars.h"
# include "mozilla/widget/GtkCompositorWidget.h"
#endif

namespace mozilla {
using namespace gfx;

Expand Down Expand Up @@ -52,6 +57,11 @@ bool RenderCompositorSWGL::BeginFrame() {
ClearMappedBuffer();
mDirtyRegion = LayoutDeviceIntRect(LayoutDeviceIntPoint(), GetBufferSize());
wr_swgl_make_current(mContext);

#ifdef MOZ_WAYLAND
mWidget->AsGTK()->PrepareBufferForFrame();
#endif

return true;
}

Expand Down Expand Up @@ -249,6 +259,26 @@ bool RenderCompositorSWGL::RequestFullRender() {
#endif
}

uint32_t RenderCompositorSWGL::GetMaxPartialPresentRects() {
return gfx::gfxVars::WebRenderMaxPartialPresentRects();
}

bool RenderCompositorSWGL::ShouldDrawPreviousPartialPresentRegions() {
#ifdef MOZ_WAYLAND
return mWidget->AsGTK()->ShouldDrawPreviousPartialPresentRegions();
#else
return false;
#endif
}

size_t RenderCompositorSWGL::GetBufferAge() const {
#ifdef MOZ_WAYLAND
return mWidget->AsGTK()->GetBufferAge();
#else
return 0;
#endif
}

void RenderCompositorSWGL::Pause() {}

bool RenderCompositorSWGL::Resume() { return true; }
Expand Down
3 changes: 3 additions & 0 deletions gfx/webrender_bindings/RenderCompositorSWGL.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ class RenderCompositorSWGL : public RenderCompositor {

bool UsePartialPresent() override { return true; }
bool RequestFullRender() override;
uint32_t GetMaxPartialPresentRects() override;
bool ShouldDrawPreviousPartialPresentRegions() override;
size_t GetBufferAge() const override;

void Pause() override;
bool Resume() override;
Expand Down
45 changes: 39 additions & 6 deletions widget/gtk/GtkCompositorWidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,14 @@
#include "GtkCompositorWidget.h"

#include "mozilla/layers/CompositorThread.h"
#include "mozilla/Preferences.h"
#include "mozilla/widget/InProcessCompositorWidget.h"
#include "mozilla/widget/PlatformWidgetTypes.h"
#include "nsWindow.h"
#include "mozilla/X11Util.h"

#ifdef MOZ_X11
# include "mozilla/X11Util.h"
#endif

#ifdef MOZ_WAYLAND
# include "mozilla/layers/NativeLayerWayland.h"
Expand All @@ -25,16 +29,30 @@ GtkCompositorWidget::GtkCompositorWidget(
mWidget(aWindow),
mClientSize("GtkCompositorWidget::mClientSize") {
#if defined(MOZ_WAYLAND)
if (!aInitData.IsX11Display()) {
if (GdkIsWaylandDisplay() && (GetCompositorOptions().UseSoftwareWebRender() ||
!GetCompositorOptions().UseWebRender())) {
if (!aWindow) {
NS_WARNING("GtkCompositorWidget: We're missing nsWindow!");
}
mProvider.Initialize(aWindow);
mNativeLayerRoot = nullptr;

// By default use multi-buffered backend for SW-WR and single-buffered
// for Basic. Assume that SW-WR and Basic never get mixed within the
// same process.
static bool useMultiBufferBackend =
[](const layers::CompositorOptions& aOptions) {
if (Preferences::HasUserValue(
"widget.wayland.software-backend-multi-buffer")) {
return Preferences::GetBool(
"widget.wayland.software-backend-multi-buffer");
}
return aOptions.UseSoftwareWebRender();
}(GetCompositorOptions());

mProvider.Initialize(aWindow, useMultiBufferBackend);
}
#endif
#if defined(MOZ_X11)
if (aInitData.IsX11Display()) {
if (GdkIsX11Display()) {
mXWindow = (Window)aInitData.XWindow();

// Grab the window's visual and depth
Expand All @@ -47,7 +65,10 @@ GtkCompositorWidget::GtkCompositorWidget(
mDepth = windowAttrs.depth;

// Initialize the window surface provider
mProvider.Initialize(mXWindow, visual, mDepth, aInitData.Shaped());
if (GetCompositorOptions().UseSoftwareWebRender() ||
!GetCompositorOptions().UseWebRender()) {
mProvider.Initialize(mXWindow, visual, mDepth, aInitData.Shaped());
}
}
#endif
auto size = mClientSize.Lock();
Expand All @@ -73,6 +94,18 @@ void GtkCompositorWidget::EndRemoteDrawingInRegion(
mProvider.EndRemoteDrawingInRegion(aDrawTarget, aInvalidRegion);
}

void GtkCompositorWidget::PrepareBufferForFrame() {
return mProvider.PrepareBufferForFrame();
}

size_t GtkCompositorWidget::GetBufferAge() const {
return mProvider.GetBufferAge();
}

bool GtkCompositorWidget::ShouldDrawPreviousPartialPresentRegions() {
return mProvider.ShouldDrawPreviousPartialPresentRegions();
}

nsIWidget* GtkCompositorWidget::RealWidget() { return mWidget; }

void GtkCompositorWidget::NotifyClientSizeChanged(
Expand Down
3 changes: 3 additions & 0 deletions widget/gtk/GtkCompositorWidget.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ class GtkCompositorWidget : public CompositorWidget,
void EndRemoteDrawingInRegion(
gfx::DrawTarget* aDrawTarget,
const LayoutDeviceIntRegion& aInvalidRegion) override;
void PrepareBufferForFrame();
size_t GetBufferAge() const;
bool ShouldDrawPreviousPartialPresentRegions();
uintptr_t GetWidgetKey() override;

LayoutDeviceIntSize GetClientSize() override;
Expand Down
4 changes: 4 additions & 0 deletions widget/gtk/WindowSurface.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ class WindowSurface {
// Whether the window surface represents a fallback method.
virtual bool IsFallback() const { return false; }

virtual void PrepareBufferForFrame(){};
virtual size_t GetBufferAge() const { return 0; };
virtual bool ShouldDrawPreviousPartialPresentRegions() { return false; };

protected:
virtual ~WindowSurface() = default;
};
Expand Down
38 changes: 35 additions & 3 deletions widget/gtk/WindowSurfaceProvider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

#ifdef MOZ_WAYLAND
# include "WindowSurfaceWayland.h"
# include "WindowSurfaceWaylandMultiBuffer.h"
#endif
#ifdef MOZ_X11
# include "mozilla/X11Util.h"
Expand All @@ -30,7 +31,8 @@ WindowSurfaceProvider::WindowSurfaceProvider()
: mWindowSurface(nullptr)
#ifdef MOZ_WAYLAND
,
mWidget(nullptr)
mWidget(nullptr),
mUseMultiBuffer(false)
#endif
#ifdef MOZ_X11
,
Expand All @@ -43,7 +45,11 @@ WindowSurfaceProvider::WindowSurfaceProvider()
}

#ifdef MOZ_WAYLAND
void WindowSurfaceProvider::Initialize(nsWindow* aWidget) { mWidget = aWidget; }
void WindowSurfaceProvider::Initialize(nsWindow* aWidget,
bool aUseMultiBuffer) {
mWidget = aWidget;
mUseMultiBuffer = aUseMultiBuffer;
}
#endif
#ifdef MOZ_X11
void WindowSurfaceProvider::Initialize(Window aWindow, Visual* aVisual,
Expand All @@ -60,7 +66,17 @@ void WindowSurfaceProvider::CleanupResources() { mWindowSurface = nullptr; }
RefPtr<WindowSurface> WindowSurfaceProvider::CreateWindowSurface() {
#ifdef MOZ_WAYLAND
if (GdkIsWaylandDisplay()) {
LOG(("Drawing to nsWindow %p will use wl_surface\n", mWidget));
if (mUseMultiBuffer) {
LOG(
("Drawing to nsWindow %p will use wl_surface. Using multi-buffered "
"backend.\n",
mWidget));
return MakeRefPtr<WindowSurfaceWaylandMB>(mWidget);
}
LOG(
("Drawing to nsWindow %p will use wl_surface. Using single-buffered "
"backend.\n",
mWidget));
return MakeRefPtr<WindowSurfaceWayland>(mWidget);
}
#endif
Expand Down Expand Up @@ -124,5 +140,21 @@ void WindowSurfaceProvider::EndRemoteDrawingInRegion(
if (mWindowSurface) mWindowSurface->Commit(aInvalidRegion);
}

bool WindowSurfaceProvider::ShouldDrawPreviousPartialPresentRegions() {
return mWindowSurface
? mWindowSurface->ShouldDrawPreviousPartialPresentRegions()
: false;
}

size_t WindowSurfaceProvider::GetBufferAge() const {
return mWindowSurface ? mWindowSurface->GetBufferAge() : 0;
}

void WindowSurfaceProvider::PrepareBufferForFrame() {
if (mWindowSurface) {
mWindowSurface->PrepareBufferForFrame();
}
}

} // namespace widget
} // namespace mozilla
6 changes: 5 additions & 1 deletion widget/gtk/WindowSurfaceProvider.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ class WindowSurfaceProvider final {
* while WindowSurfaceProvider is used.
*/
#ifdef MOZ_WAYLAND
void Initialize(nsWindow* aWidget);
void Initialize(nsWindow* aWidget, bool aUseMultiBuffer);
#endif
#ifdef MOZ_X11
void Initialize(Window aWindow, Visual* aVisual, int aDepth, bool aIsShaped);
Expand All @@ -59,13 +59,17 @@ class WindowSurfaceProvider final {
layers::BufferMode* aBufferMode);
void EndRemoteDrawingInRegion(gfx::DrawTarget* aDrawTarget,
const LayoutDeviceIntRegion& aInvalidRegion);
bool ShouldDrawPreviousPartialPresentRegions();
size_t GetBufferAge() const;
void PrepareBufferForFrame();

private:
RefPtr<WindowSurface> CreateWindowSurface();

RefPtr<WindowSurface> mWindowSurface;
#ifdef MOZ_WAYLAND
nsWindow* mWidget;
bool mUseMultiBuffer;
#endif
#ifdef MOZ_X11
bool mIsShaped;
Expand Down
Loading

0 comments on commit c2f64d7

Please sign in to comment.