Bug 1729424 [Wayland] Remove WindowSurfaceWayland backend r=rmader

Differential Revision: https://phabricator.services.mozilla.com/D124747
This commit is contained in:
stransky 2021-09-07 12:48:23 +00:00
Родитель 0216200ef6
Коммит 7fe2f36056
9 изменённых файлов: 1 добавлений и 1084 удалений

Просмотреть файл

@ -11928,15 +11928,6 @@
mirror: always
#endif
# Smooth rendering mode for Wayland basic compositor.
# 0 - direct draw
# 1 - basic caching
# 2 - all caching
- name: widget.wayland-smooth-rendering
type: RelaxedAtomicUint32
value: 1
mirror: always
# Use DMABuf backend for WebGL.
- name: widget.dmabuf-webgl.enabled
type: RelaxedAtomicBool
@ -11961,11 +11952,6 @@
value: 0.0f
mirror: once
- name: widget.wayland.multi-buffer-software-backend.enabled
type: bool
value: true
mirror: once
# Use opaque region for MozContainer wl_surface
- name: widget.wayland.opaque-region.enabled
type: bool

Просмотреть файл

@ -174,8 +174,6 @@ void moz_container_wayland_init(MozContainerWayland* container) {
container->opaque_region_needs_updates = false;
container->opaque_region_subtract_corners = false;
container->opaque_region_used = false;
container->surface_needs_clear = true;
container->container_remapped = false;
container->subsurface_dx = 0;
container->subsurface_dy = 0;
container->before_first_size_alloc = true;
@ -291,10 +289,8 @@ static void moz_container_wayland_unmap_internal(MozContainer* container) {
g_clear_pointer(&wl_container->viewport, wp_viewport_destroy);
g_clear_pointer(&wl_container->frame_callback_handler, wl_callback_destroy);
wl_container->surface_needs_clear = true;
wl_container->ready_to_draw = false;
wl_container->buffer_scale = 1;
wl_container->container_remapped = true;
}
static gboolean moz_container_wayland_map_event(GtkWidget* widget,
@ -619,23 +615,6 @@ gboolean moz_container_wayland_has_egl_window(MozContainer* container) {
return container->wl_container.eglwindow != nullptr;
}
gboolean moz_container_wayland_surface_needs_clear(MozContainer* container) {
int ret = container->wl_container.surface_needs_clear;
container->wl_container.surface_needs_clear = false;
return ret;
}
gboolean moz_container_wayland_get_and_reset_remapped(MozContainer* container) {
int ret = container->wl_container.container_remapped;
container->wl_container.container_remapped = false;
return ret;
}
gboolean moz_container_wayland_is_inactive(MozContainer* container) {
MozContainerWayland* wl_container = &container->wl_container;
return !wl_container->ready_to_draw && !wl_container->frame_callback_handler;
}
void moz_container_wayland_update_opaque_region(MozContainer* container,
bool aSubtractCorners) {
MozContainerWayland* wl_container = &container->wl_container;

Просмотреть файл

@ -42,10 +42,8 @@ struct MozContainerWayland {
gboolean opaque_region_needs_updates;
gboolean opaque_region_subtract_corners;
gboolean opaque_region_used;
gboolean surface_needs_clear;
gboolean ready_to_draw;
gboolean before_first_size_alloc;
gboolean container_remapped;
int buffer_scale;
std::vector<std::function<void(void)>> initial_draw_cbs;
// mozcontainer is used from Compositor and Rendering threads
@ -75,7 +73,6 @@ struct wl_egl_window* moz_container_wayland_get_egl_window(
MozContainer* container, double scale);
gboolean moz_container_wayland_has_egl_window(MozContainer* container);
gboolean moz_container_wayland_surface_needs_clear(MozContainer* container);
void moz_container_wayland_egl_window_set_size(MozContainer* container,
int width, int height);
void moz_container_wayland_set_scale_factor(MozContainer* container);
@ -88,7 +85,5 @@ void moz_container_wayland_update_opaque_region(MozContainer* container,
gboolean moz_container_wayland_can_draw(MozContainer* container);
double moz_container_wayland_get_scale(MozContainer* container);
struct wp_viewport* moz_container_wayland_get_viewport(MozContainer* container);
gboolean moz_container_wayland_get_and_reset_remapped(MozContainer* container);
gboolean moz_container_wayland_is_inactive(MozContainer* container);
#endif /* __MOZ_CONTAINER_WAYLAND_H__ */

Просмотреть файл

@ -13,7 +13,6 @@
#ifdef MOZ_WAYLAND
# include "mozilla/StaticPrefs_widget.h"
# include "WindowSurfaceWayland.h"
# include "WindowSurfaceWaylandMultiBuffer.h"
#endif
#ifdef MOZ_X11
@ -74,19 +73,7 @@ void WindowSurfaceProvider::CleanupResources() { mWindowSurface = nullptr; }
RefPtr<WindowSurface> WindowSurfaceProvider::CreateWindowSurface() {
#ifdef MOZ_WAYLAND
if (GdkIsWaylandDisplay()) {
if (StaticPrefs::
widget_wayland_multi_buffer_software_backend_enabled_AtStartup()) {
LOG(
("Drawing to nsWindow %p will use wl_surface. Using multi-buffered "
"backend.\n",
mWidget.get()));
return MakeRefPtr<WindowSurfaceWaylandMB>(mWidget);
}
LOG(
("Drawing to nsWindow %p will use wl_surface. Using single-buffered "
"backend.\n",
mWidget.get()));
return MakeRefPtr<WindowSurfaceWayland>(mWidget);
return MakeRefPtr<WindowSurfaceWaylandMB>(mWidget);
}
#endif
#ifdef MOZ_X11

Просмотреть файл

@ -1,830 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "WindowSurfaceWayland.h"
#include <errno.h>
#include <fcntl.h>
#include <sys/mman.h>
#include "nsPrintfCString.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/gfx/Tools.h"
#include "gfx2DGlue.h"
#include "gfxPlatform.h"
#include "MozContainer.h"
#include "mozilla/ScopeExit.h"
#include "mozilla/StaticPrefs_widget.h"
#include "mozilla/WidgetUtils.h"
#include "nsTArray.h"
#ifdef MOZ_LOGGING
# include "mozilla/Logging.h"
# include "Units.h"
extern mozilla::LazyLogModule gWidgetWaylandLog;
# define LOGWAYLAND(args) \
MOZ_LOG(gWidgetWaylandLog, mozilla::LogLevel::Debug, args)
#else
# define LOGWAYLAND(args)
#endif /* MOZ_LOGGING */
// Maximal compositing timeout it miliseconds
#define COMPOSITING_TIMEOUT 200
namespace mozilla::widget {
/*
Wayland multi-thread rendering scheme
Every rendering thread (main thread, compositor thread) contains its own
nsWaylandDisplay object connected to Wayland compositor (Mutter, Weston, etc.)
WindowSurfaceWayland implements WindowSurface class and draws nsWindow by
WindowSurface interface (Lock, Commit) to screen through nsWaylandDisplay.
----------------------
| Wayland compositor |
----------------------
^
|
----------------------
| nsWaylandDisplay |
----------------------
^ ^
| |
| |
| --------------------------------- ------------------
| | WindowSurfaceWayland |<------>| nsWindow |
| | | ------------------
| | ----------------------- |
| | | WaylandBufferSHM | |
| | | | |
| | | ------------------- | |
| | | | WaylandShmPool | | |
| | | ------------------- | |
| | ----------------------- |
| | |
| | ----------------------- |
| | | WaylandBufferSHM | |
| | | | |
| | | ------------------- | |
| | | | WaylandShmPool | | |
| | | ------------------- | |
| | ----------------------- |
| ---------------------------------
|
|
--------------------------------- ------------------
| WindowSurfaceWayland |<------>| nsWindow |
| | ------------------
| ----------------------- |
| | WaylandBufferSHM | |
| | | |
| | ------------------- | |
| | | WaylandShmPool | | |
| | ------------------- | |
| ----------------------- |
| |
| ----------------------- |
| | WaylandBufferSHM | |
| | | |
| | ------------------- | |
| | | WaylandShmPool | | |
| | ------------------- | |
| ----------------------- |
---------------------------------
nsWaylandDisplay
Is our connection to Wayland display server,
holds our display connection (wl_display) and event queue (wl_event_queue).
nsWaylandDisplay is created for every thread which sends data to Wayland
compositor. Wayland events for main thread is served by default Gtk+ loop,
for other threads (compositor) we must create wl_event_queue and run event loop.
WindowSurfaceWayland
Is a Wayland implementation of WindowSurface class for WindowSurfaceProvider,
we implement Lock() and Commit() interfaces from WindowSurface
for actual drawing.
One WindowSurfaceWayland draws one nsWindow so those are tied 1:1.
At Wayland level it holds one wl_surface object.
To perform visualiation of nsWindow, WindowSurfaceWayland contains one
wl_surface and two wl_buffer objects (owned by WaylandBufferSHM)
as we use double buffering. When nsWindow drawing is finished to wl_buffer,
the wl_buffer is attached to wl_surface and it's sent to Wayland compositor.
When there's no wl_buffer available for drawing (all wl_buffers are locked in
compositor for instance) we store the drawing to WindowImageSurface object
and draw later when wl_buffer becomes available or discard the
WindowImageSurface cache when whole screen is invalidated.
WaylandBufferSHM
Is a class which provides a wl_buffer for drawing.
Wl_buffer is a main Wayland object with actual graphics data.
Wl_buffer basically represent one complete window screen.
When double buffering is involved every window (GdkWindow for instance)
utilises two wl_buffers which are cycled. One is filed with data by application
and one is rendered by compositor.
WaylandBufferSHM is implemented by shared memory (shm).
It owns wl_buffer object, owns WaylandShmPool
(which provides the shared memory) and ties them together.
WaylandShmPool
WaylandShmPool acts as a manager of shared memory for WaylandBufferSHM.
Allocates it, holds reference to it and releases it.
We allocate shared memory (shm) by mmap(..., MAP_SHARED,...) as an interface
between us and wayland compositor. We draw our graphics data to the shm and
handle to wayland compositor by WaylandBufferSHM/WindowSurfaceWayland
(wl_buffer/wl_surface).
*/
#define EVENT_LOOP_DELAY (1000 / 240)
static const struct wl_callback_listener sFrameListenerWindowSurfaceWayland = {
WindowSurfaceWayland::FrameCallbackHandler};
WindowSurfaceWayland::WindowSurfaceWayland(RefPtr<nsWindow> aWindow)
: mWindow(std::move(aWindow)),
mWaylandDisplay(WaylandDisplayGet()),
mWaylandFullscreenDamage(false),
mFrameCallback(nullptr),
mLastCommittedSurfaceID(-1),
mLastCommitTime(0),
mDrawToWaylandBufferDirectly(true),
mCanSwitchWaylandBuffer(true),
mWLBufferIsDirty(false),
mBufferCommitAllowed(false),
mBufferNeedsClear(false),
mSmoothRendering(StaticPrefs::widget_wayland_smooth_rendering()),
mSurfaceReadyTimerID(),
mSurfaceLock("WindowSurfaceWayland lock") {
LOGWAYLAND(("WindowSurfaceWayland::WindowSurfaceWayland() [%p]\n", this));
// Use slow compositing on KDE only.
const char* currentDesktop = getenv("XDG_CURRENT_DESKTOP");
if (currentDesktop && strstr(currentDesktop, "KDE") != nullptr) {
mSmoothRendering = CACHE_NONE;
}
}
WindowSurfaceWayland::~WindowSurfaceWayland() {
LOGWAYLAND(("WindowSurfaceWayland::~WindowSurfaceWayland() [%p]\n", this));
MutexAutoLock lock(mSurfaceLock);
if (mSurfaceReadyTimerID) {
g_source_remove(mSurfaceReadyTimerID);
mSurfaceReadyTimerID = 0;
}
if (mWLBufferIsDirty) {
NS_WARNING("Deleted WindowSurfaceWayland with a pending commit!");
}
if (mFrameCallback) {
wl_callback_destroy(mFrameCallback);
}
}
WaylandBufferSHM* WindowSurfaceWayland::CreateWaylandBuffer(
const LayoutDeviceIntSize& aSize) {
int availableBuffer;
LOGWAYLAND(("WindowSurfaceWayland::CreateWaylandBuffer %d x %d\n",
aSize.width, aSize.height));
for (availableBuffer = 0; availableBuffer < BACK_BUFFER_NUM;
availableBuffer++) {
if (!mShmBackupBuffer[availableBuffer] ||
(!mShmBackupBuffer[availableBuffer]->IsAttached() &&
!mShmBackupBuffer[availableBuffer]->IsMatchingSize(aSize))) {
break;
}
}
// There isn't any free slot for additional buffer.
if (availableBuffer == BACK_BUFFER_NUM) {
LOGWAYLAND((" no free buffer slot!\n"));
return nullptr;
}
RefPtr<WaylandBufferSHM> buffer = WaylandBufferSHM::Create(aSize);
if (!buffer) {
LOGWAYLAND((" failed to create back buffer!\n"));
return nullptr;
}
buffer->SetBufferReleaseFunc(
&WindowSurfaceWayland::BufferReleaseCallbackHandler);
buffer->SetBufferReleaseData(this);
mShmBackupBuffer[availableBuffer] = buffer;
LOGWAYLAND(
(" created new buffer %p at %d!\n", buffer.get(), availableBuffer));
return buffer.get();
}
WaylandBufferSHM* WindowSurfaceWayland::WaylandBufferFindAvailable(
const LayoutDeviceIntSize& aSize) {
LOGWAYLAND(("WindowSurfaceWayland::WaylandBufferFindAvailable %d x %d\n",
aSize.width, aSize.height));
// Try to find a buffer which matches the size
for (int availableBuffer = 0; availableBuffer < BACK_BUFFER_NUM;
availableBuffer++) {
RefPtr<WaylandBufferSHM> buffer = mShmBackupBuffer[availableBuffer];
if (buffer && !buffer->IsAttached() && buffer->IsMatchingSize(aSize)) {
LOGWAYLAND((" found match %d [%p]\n", availableBuffer, buffer.get()));
return buffer.get();
}
}
LOGWAYLAND((" no buffer available!\n"));
return nullptr;
}
WaylandBufferSHM* WindowSurfaceWayland::SetNewWaylandBuffer() {
LOGWAYLAND(
("WindowSurfaceWayland::NewWaylandBuffer [%p] Requested buffer [%d "
"x %d]\n",
(void*)this, mWLBufferSize.width, mWLBufferSize.height));
mWaylandBuffer = WaylandBufferFindAvailable(mWLBufferSize);
if (mWaylandBuffer) {
return mWaylandBuffer;
}
mWaylandBuffer = CreateWaylandBuffer(mWLBufferSize);
return mWaylandBuffer;
}
// Recent
WaylandBufferSHM* WindowSurfaceWayland::GetWaylandBuffer() {
LOGWAYLAND(
("WindowSurfaceWayland::GetWaylandBuffer [%p] Requested buffer [%d "
"x %d] can switch %d\n",
(void*)this, mWLBufferSize.width, mWLBufferSize.height,
mCanSwitchWaylandBuffer));
#if MOZ_LOGGING
LOGWAYLAND((" Recent WaylandBufferSHM [%p]\n", mWaylandBuffer.get()));
for (int i = 0; i < BACK_BUFFER_NUM; i++) {
if (!mShmBackupBuffer[i]) {
LOGWAYLAND((" WaylandBufferSHM [%d] null\n", i));
} else {
LOGWAYLAND(
(" WaylandBufferSHM [%d][%p] width %d height %d attached %d\n",
i, mShmBackupBuffer[i].get(), mShmBackupBuffer[i]->GetSize().width,
mShmBackupBuffer[i]->GetSize().height,
mShmBackupBuffer[i]->IsAttached()));
}
}
#endif
// There's no buffer created yet, create a new one for partial screen updates.
if (!mWaylandBuffer) {
return SetNewWaylandBuffer();
}
if (mWaylandBuffer->IsAttached()) {
if (mCanSwitchWaylandBuffer) {
return SetNewWaylandBuffer();
}
LOGWAYLAND((" Buffer is attached and we can't switch, return null\n"));
return nullptr;
}
if (mWaylandBuffer->IsMatchingSize(mWLBufferSize)) {
LOGWAYLAND((" Size is ok, use the buffer [%d x %d]\n",
mWLBufferSize.width, mWLBufferSize.height));
return mWaylandBuffer;
}
if (mCanSwitchWaylandBuffer) {
return SetNewWaylandBuffer();
}
LOGWAYLAND(
(" Buffer size does not match, requested %d x %d got %d x%d, return "
"null.\n",
mWaylandBuffer->GetSize().width, mWaylandBuffer->GetSize().height,
mWLBufferSize.width, mWLBufferSize.height));
return nullptr;
}
already_AddRefed<gfx::DrawTarget> WindowSurfaceWayland::LockWaylandBuffer() {
// Allocated wayland buffer must match mozcontainer widget size.
mWLBufferSize = mWindow->GetMozContainerSize();
LOGWAYLAND(
("WindowSurfaceWayland::LockWaylandBuffer [%p] Requesting buffer %d x "
"%d\n",
(void*)this, mWLBufferSize.width, mWLBufferSize.height));
WaylandBufferSHM* buffer = GetWaylandBuffer();
LOGWAYLAND(("WindowSurfaceWayland::LockWaylandBuffer [%p] Got buffer %p\n",
(void*)this, (void*)buffer));
if (!buffer) {
if (mLastCommitTime && (g_get_monotonic_time() / 1000) - mLastCommitTime >
COMPOSITING_TIMEOUT) {
NS_WARNING(
"Slow response from Wayland compositor, visual glitches ahead.");
}
return nullptr;
}
mCanSwitchWaylandBuffer = false;
if (mBufferNeedsClear) {
buffer->Clear();
mBufferNeedsClear = false;
}
return buffer->Lock();
}
already_AddRefed<gfx::DrawTarget> WindowSurfaceWayland::LockImageSurface(
const gfx::IntSize& aLockSize) {
if (!mImageSurface || !(aLockSize <= mImageSurface->GetSize())) {
mImageSurface = gfx::Factory::CreateDataSourceSurface(
aLockSize, WaylandBufferSHM::GetSurfaceFormat());
}
gfx::DataSourceSurface::MappedSurface map = {nullptr, 0};
if (!mImageSurface->Map(gfx::DataSourceSurface::READ_WRITE, &map)) {
return nullptr;
}
return gfxPlatform::CreateDrawTargetForData(
map.mData, mImageSurface->GetSize(), map.mStride,
WaylandBufferSHM::GetSurfaceFormat());
}
static bool IsWindowFullScreenUpdate(
LayoutDeviceIntSize& aScreenSize,
const LayoutDeviceIntRegion& aUpdatedRegion) {
if (aUpdatedRegion.GetNumRects() > 1) return false;
gfx::IntRect rect = aUpdatedRegion.RectIter().Get().ToUnknownRect();
return (rect.x == 0 && rect.y == 0 && aScreenSize.width == rect.width &&
aScreenSize.height == rect.height);
}
static bool IsPopupFullScreenUpdate(
LayoutDeviceIntSize& aScreenSize,
const LayoutDeviceIntRegion& aUpdatedRegion) {
// We know that popups can be drawn from two parts; a panel and an arrow.
// Assume we redraw whole popups when we have two rects and bounding
// box is equal to window borders.
if (aUpdatedRegion.GetNumRects() > 2) return false;
gfx::IntRect lockSize = aUpdatedRegion.GetBounds().ToUnknownRect();
return (lockSize.x == 0 && lockSize.y == 0 &&
aScreenSize.width == lockSize.width &&
aScreenSize.height == lockSize.height);
}
already_AddRefed<gfx::DrawTarget> WindowSurfaceWayland::Lock(
const LayoutDeviceIntRegion& aRegion) {
if (mWindow->WindowType() == eWindowType_invisible) {
return nullptr;
}
// Lock the surface *after* WaitForSyncEnd() call as is can fire
// FlushPendingCommits().
MutexAutoLock lock(mSurfaceLock);
// Disable all commits (from potential frame callback/delayed handlers)
// until next WindowSurfaceWayland::Commit() call.
mBufferCommitAllowed = false;
LayoutDeviceIntSize mozContainerSize = mWindow->GetMozContainerSize();
gfx::IntRect lockSize = aRegion.GetBounds().ToUnknownRect();
bool isTransparentPopup =
mWindow->IsWaylandPopup() &&
(eTransparencyTransparent == mWindow->GetTransparencyMode());
bool windowRedraw = isTransparentPopup
? IsPopupFullScreenUpdate(mozContainerSize, aRegion)
: IsWindowFullScreenUpdate(mozContainerSize, aRegion);
if (windowRedraw) {
// Clear buffer when we (re)draw new transparent popup window,
// otherwise leave it as-is, mBufferNeedsClear can be set from previous
// (already pending) commits which are cached now.
mBufferNeedsClear =
mWindow->WaylandSurfaceNeedsClear() || isTransparentPopup;
// We do full buffer repaint so clear our cached drawings.
mDelayedImageCommits.Clear();
mWaylandBufferDamage.SetEmpty();
mCanSwitchWaylandBuffer = true;
mWLBufferIsDirty = false;
// Store info that we can safely invalidate whole screen.
mWaylandFullscreenDamage = true;
} else {
// We can switch buffer if there isn't any content committed
// to active buffer.
mCanSwitchWaylandBuffer = !mWLBufferIsDirty;
}
LOGWAYLAND(
("WindowSurfaceWayland::Lock [%p] [%d,%d] -> [%d x %d] rects %d "
"MozContainer size [%d x %d]\n",
(void*)this, lockSize.x, lockSize.y, lockSize.width, lockSize.height,
aRegion.GetNumRects(), mozContainerSize.width, mozContainerSize.height));
LOGWAYLAND((" nsWindow = %p\n", mWindow.get()));
LOGWAYLAND((" isPopup = %d\n", mWindow->IsWaylandPopup()));
LOGWAYLAND((" isTransparentPopup = %d\n", isTransparentPopup));
LOGWAYLAND((" IsPopupFullScreenUpdate = %d\n",
IsPopupFullScreenUpdate(mozContainerSize, aRegion)));
LOGWAYLAND((" IsWindowFullScreenUpdate = %d\n",
IsWindowFullScreenUpdate(mozContainerSize, aRegion)));
LOGWAYLAND((" mBufferNeedsClear = %d\n", mBufferNeedsClear));
LOGWAYLAND((" mWLBufferIsDirty = %d\n", mWLBufferIsDirty));
LOGWAYLAND((" mCanSwitchWaylandBuffer = %d\n", mCanSwitchWaylandBuffer));
LOGWAYLAND((" windowRedraw = %d\n", windowRedraw));
if (!(mMozContainerSize == mozContainerSize)) {
LOGWAYLAND((" screen size changed\n"));
if (!windowRedraw) {
LOGWAYLAND((" screen size changed without redraw!\n"));
// Screen (window) size changed and we still have some painting pending
// for the last window size. That can happen when window is resized.
// We won't draw it but wait for new content.
mDelayedImageCommits.Clear();
mWaylandBufferDamage.SetEmpty();
mCanSwitchWaylandBuffer = true;
mWLBufferIsDirty = false;
mBufferNeedsClear = true;
}
mMozContainerSize = mozContainerSize;
}
mDrawToWaylandBufferDirectly = windowRedraw || mSmoothRendering == CACHE_NONE;
if (!mDrawToWaylandBufferDirectly && mSmoothRendering == CACHE_SMALL) {
mDrawToWaylandBufferDirectly =
(lockSize.width * 2 > mozContainerSize.width &&
lockSize.height * 2 > mozContainerSize.height);
}
if (!mDrawToWaylandBufferDirectly) {
// Don't switch wl_buffers when we cache drawings.
mCanSwitchWaylandBuffer = false;
LOGWAYLAND((" Indirect drawing, mCanSwitchWaylandBuffer = %d\n",
mCanSwitchWaylandBuffer));
}
if (mDrawToWaylandBufferDirectly) {
LOGWAYLAND((" Direct drawing\n"));
RefPtr<gfx::DrawTarget> dt = LockWaylandBuffer();
if (dt) {
#if MOZ_LOGGING
mWaylandBuffer->DumpToFile("Lock");
#endif
if (!windowRedraw) {
DrawDelayedImageCommits(dt, mWaylandBufferDamage);
#if MOZ_LOGGING
mWaylandBuffer->DumpToFile("Lock-after-commit");
#endif
}
mWLBufferIsDirty = true;
return dt.forget();
}
}
// We do indirect drawing because there isn't any front buffer available.
// Do indirect drawing to mImageSurface which is commited to wayland
// wl_buffer by DrawDelayedImageCommits() later.
mDrawToWaylandBufferDirectly = false;
LOGWAYLAND((" Indirect drawing.\n"));
return LockImageSurface(gfx::IntSize(lockSize.XMost(), lockSize.YMost()));
}
bool WindowImageSurface::OverlapsSurface(
class WindowImageSurface& aBottomSurface) {
return mUpdateRegion.Contains(aBottomSurface.mUpdateRegion);
}
void WindowImageSurface::DrawToTarget(
gfx::DrawTarget* aDest, LayoutDeviceIntRegion& aWaylandBufferDamage) {
#ifdef MOZ_LOGGING
gfx::IntRect bounds = mUpdateRegion.GetBounds().ToUnknownRect();
LOGWAYLAND(("WindowImageSurface::DrawToTarget\n"));
LOGWAYLAND((" rects num %d\n", mUpdateRegion.GetNumRects()));
LOGWAYLAND((" bounds [ %d, %d] -> [%d x %d]\n", bounds.x, bounds.y,
bounds.width, bounds.height));
#endif
for (auto iter = mUpdateRegion.RectIter(); !iter.Done(); iter.Next()) {
gfx::IntRect r(iter.Get().ToUnknownRect());
LOGWAYLAND(
(" draw rect [%d,%d] -> [%d x %d]\n", r.x, r.y, r.width, r.height));
aDest->CopySurface(mImageSurface, r, gfx::IntPoint(r.x, r.y));
}
aWaylandBufferDamage.OrWith(mUpdateRegion);
}
WindowImageSurface::WindowImageSurface(
gfx::DataSourceSurface* aImageSurface,
const LayoutDeviceIntRegion& aUpdateRegion)
: mImageSurface(aImageSurface), mUpdateRegion(aUpdateRegion) {}
bool WindowSurfaceWayland::DrawDelayedImageCommits(
gfx::DrawTarget* aDrawTarget, LayoutDeviceIntRegion& aWaylandBufferDamage) {
unsigned int imagesNum = mDelayedImageCommits.Length();
LOGWAYLAND(("WindowSurfaceWayland::DrawDelayedImageCommits [%p] len %d\n",
(void*)this, imagesNum));
for (unsigned int i = 0; i < imagesNum; i++) {
mDelayedImageCommits[i].DrawToTarget(aDrawTarget, aWaylandBufferDamage);
}
mDelayedImageCommits.Clear();
return (imagesNum != 0);
}
void WindowSurfaceWayland::CacheImageSurface(
const LayoutDeviceIntRegion& aRegion) {
#ifdef MOZ_LOGGING
gfx::IntRect bounds = aRegion.GetBounds().ToUnknownRect();
LOGWAYLAND(("WindowSurfaceWayland::CacheImageSurface [%p]\n", (void*)this));
LOGWAYLAND((" rects num %d\n", aRegion.GetNumRects()));
LOGWAYLAND((" bounds [ %d, %d] -> [%d x %d]\n", bounds.x, bounds.y,
bounds.width, bounds.height));
#endif
mImageSurface->Unmap();
WindowImageSurface surf = WindowImageSurface(mImageSurface, aRegion);
if (mDelayedImageCommits.Length()) {
auto lastSurf = mDelayedImageCommits.PopLastElement();
if (surf.OverlapsSurface(lastSurf)) {
#ifdef MOZ_LOGGING
{
gfx::IntRect size =
lastSurf.GetUpdateRegion()->GetBounds().ToUnknownRect();
LOGWAYLAND((" removing [ %d, %d] -> [%d x %d]\n", size.x, size.y,
size.width, size.height));
}
#endif
} else {
mDelayedImageCommits.AppendElement(lastSurf);
}
}
mDelayedImageCommits.AppendElement(surf);
// mImageSurface is owned by mDelayedImageCommits
mImageSurface = nullptr;
LOGWAYLAND(
(" There's %d cached images\n", int(mDelayedImageCommits.Length())));
}
bool WindowSurfaceWayland::CommitImageCacheToWaylandBuffer() {
if (!mDelayedImageCommits.Length()) {
return false;
}
MOZ_ASSERT(!mDrawToWaylandBufferDirectly);
RefPtr<gfx::DrawTarget> dt = LockWaylandBuffer();
if (!dt) {
return false;
}
LOGWAYLAND((" Flushing %ld cached WindowImageSurfaces to Wayland buffer\n",
long(mDelayedImageCommits.Length())));
return DrawDelayedImageCommits(dt, mWaylandBufferDamage);
}
void WindowSurfaceWayland::FlushPendingCommits() {
MutexAutoLock lock(mSurfaceLock);
if (FlushPendingCommitsLocked()) {
mWaylandDisplay->QueueSyncBegin();
}
}
// When a new window is created we may not have a valid wl_surface
// for drawing (Gtk haven't created it yet). All commits are queued
// and FlushPendingCommitsLocked() is called by timer when wl_surface is ready
// for drawing.
static int WaylandBufferFlushPendingCommits(void* data) {
WindowSurfaceWayland* aSurface = static_cast<WindowSurfaceWayland*>(data);
aSurface->FlushPendingCommits();
return true;
}
bool WindowSurfaceWayland::FlushPendingCommitsLocked() {
LOGWAYLAND(
("WindowSurfaceWayland::FlushPendingCommitsLocked [%p]\n", (void*)this));
LOGWAYLAND((" mDrawToWaylandBufferDirectly = %d\n",
mDrawToWaylandBufferDirectly));
LOGWAYLAND((" mCanSwitchWaylandBuffer = %d\n", mCanSwitchWaylandBuffer));
LOGWAYLAND((" mFrameCallback = %p\n", mFrameCallback));
LOGWAYLAND((" mLastCommittedSurfaceID = %d\n", mLastCommittedSurfaceID));
LOGWAYLAND((" mWLBufferIsDirty = %d\n", mWLBufferIsDirty));
LOGWAYLAND((" mBufferCommitAllowed = %d\n", mBufferCommitAllowed));
// Reset if we're hidden.
MozContainer* container = mWindow->GetMozContainer();
moz_container_wayland_lock(container);
auto unlockContainer =
MakeScopeExit([&] { moz_container_wayland_unlock(container); });
LOGWAYLAND((" mContainer = %p\n", container));
if (moz_container_wayland_get_and_reset_remapped(container)) {
LOGWAYLAND(
(" moz_container [%p] is remapped, clear callbacks.\n", container));
mLastCommittedSurfaceID = -1;
g_clear_pointer(&mFrameCallback, wl_callback_destroy);
}
if (moz_container_wayland_is_inactive(container)) {
LOGWAYLAND((" Quit - moz_container [%p] is inactive.\n", container));
if (mSurfaceReadyTimerID) {
g_source_remove(mSurfaceReadyTimerID);
mSurfaceReadyTimerID = 0;
}
return false;
}
if (!mBufferCommitAllowed) {
LOGWAYLAND((" Quit - buffer commit is not allowed.\n"));
return false;
}
if (CommitImageCacheToWaylandBuffer()) {
mWLBufferIsDirty = true;
}
// There's nothing to do here
if (!mWLBufferIsDirty) {
LOGWAYLAND((" Quit - no pending commit.\n"));
return false;
}
MOZ_ASSERT(!mWaylandBuffer->IsAttached(),
"We can't draw to attached wayland buffer!");
LOGWAYLAND((" Drawing pending commits.\n"));
wl_surface* waylandSurface =
moz_container_wayland_get_surface_locked(container);
if (!waylandSurface) {
LOGWAYLAND(
(" moz_container_wayland_get_surface_locked() failed, delay "
"commit.\n"));
if (!mSurfaceReadyTimerID) {
mSurfaceReadyTimerID = (int)g_timeout_add(
EVENT_LOOP_DELAY, &WaylandBufferFlushPendingCommits, this);
}
return true;
}
if (mSurfaceReadyTimerID) {
g_source_remove(mSurfaceReadyTimerID);
mSurfaceReadyTimerID = 0;
}
LOGWAYLAND((" We have wl_surface %p ID [%d] to commit in.\n",
waylandSurface,
wl_proxy_get_id((struct wl_proxy*)waylandSurface)));
wl_proxy_set_queue((struct wl_proxy*)waylandSurface,
mWaylandDisplay->GetEventQueue());
// We have an active frame callback request so handle it.
if (mFrameCallback) {
int waylandSurfaceID =
(int)wl_proxy_get_id((struct wl_proxy*)waylandSurface);
if (waylandSurfaceID == mLastCommittedSurfaceID) {
LOGWAYLAND((" [%p] wait for frame callback ID %d.\n", (void*)this,
waylandSurfaceID));
// We have an active frame callback pending from our recent surface.
// It means we should defer the commit to FrameCallbackHandler().
return true;
}
LOGWAYLAND((" Removing wrong frame callback [%p] ID %d.\n",
mFrameCallback,
wl_proxy_get_id((struct wl_proxy*)mFrameCallback)));
// If our stored wl_surface does not match the actual one it means the frame
// callback is no longer active and we should release it.
wl_callback_destroy(mFrameCallback);
mFrameCallback = nullptr;
mLastCommittedSurfaceID = -1;
}
if (mWaylandFullscreenDamage) {
LOGWAYLAND((" wl_surface_damage full screen\n"));
wl_surface_damage_buffer(waylandSurface, 0, 0, INT_MAX, INT_MAX);
} else {
for (auto iter = mWaylandBufferDamage.RectIter(); !iter.Done();
iter.Next()) {
mozilla::LayoutDeviceIntRect r = iter.Get();
LOGWAYLAND((" wl_surface_damage_buffer [%d, %d] -> [%d, %d]\n", r.x,
r.y, r.width, r.height));
wl_surface_damage_buffer(waylandSurface, r.x, r.y, r.width, r.height);
}
}
#if MOZ_LOGGING
mWaylandBuffer->DumpToFile("Commit");
#endif
// Clear all back buffer damage as we're committing
// all requested regions.
mWaylandFullscreenDamage = false;
mWaylandBufferDamage.SetEmpty();
mFrameCallback = wl_surface_frame(waylandSurface);
wl_callback_add_listener(mFrameCallback, &sFrameListenerWindowSurfaceWayland,
this);
mWaylandBuffer->AttachAndCommit(waylandSurface);
wl_display_flush(GetWaylandDisplay()->GetDisplay());
mLastCommittedSurfaceID =
(int)wl_proxy_get_id((struct wl_proxy*)waylandSurface);
mLastCommitTime = g_get_monotonic_time() / 1000;
// There's no pending commit, all changes are sent to compositor.
mWLBufferIsDirty = false;
return true;
}
void WindowSurfaceWayland::Commit(const LayoutDeviceIntRegion& aInvalidRegion) {
#ifdef MOZ_LOGGING
{
gfx::IntRect lockSize = aInvalidRegion.GetBounds().ToUnknownRect();
LOGWAYLAND(
("WindowSurfaceWayland::Commit [%p] damage size [%d, %d] -> [%d x %d] "
"MozContainer [%d x %d]\n",
(void*)this, lockSize.x, lockSize.y, lockSize.width, lockSize.height,
mMozContainerSize.width, mMozContainerSize.height));
LOGWAYLAND((" mDrawToWaylandBufferDirectly = %d\n",
mDrawToWaylandBufferDirectly));
}
#endif
MutexAutoLock lock(mSurfaceLock);
if (mDrawToWaylandBufferDirectly) {
mWaylandBufferDamage.OrWith(aInvalidRegion);
} else {
CacheImageSurface(aInvalidRegion);
}
mBufferCommitAllowed = true;
if (FlushPendingCommitsLocked()) {
mWaylandDisplay->QueueSyncBegin();
}
}
void WindowSurfaceWayland::FrameCallbackHandler() {
MOZ_ASSERT(mFrameCallback != nullptr,
"FrameCallbackHandler() called without valid frame callback!");
MOZ_ASSERT(mLastCommittedSurfaceID != -1,
"FrameCallbackHandler() called without valid wl_surface!");
LOGWAYLAND(("WindowSurfaceWayland::FrameCallbackHandler [%p]\n", this));
MutexAutoLock lock(mSurfaceLock);
wl_callback_destroy(mFrameCallback);
mFrameCallback = nullptr;
if (FlushPendingCommitsLocked()) {
mWaylandDisplay->QueueSyncBegin();
}
}
void WindowSurfaceWayland::FrameCallbackHandler(void* aData,
struct wl_callback* aCallback,
uint32_t aTime) {
auto* surface = reinterpret_cast<WindowSurfaceWayland*>(aData);
surface->FrameCallbackHandler();
}
void WindowSurfaceWayland::BufferReleaseCallbackHandler(wl_buffer* aBuffer) {
FlushPendingCommits();
}
void WindowSurfaceWayland::BufferReleaseCallbackHandler(void* aData,
wl_buffer* aBuffer) {
auto* surface = reinterpret_cast<WindowSurfaceWayland*>(aData);
surface->BufferReleaseCallbackHandler(aBuffer);
}
} // namespace mozilla::widget

Просмотреть файл

@ -1,189 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef _MOZILLA_WIDGET_GTK_WINDOW_SURFACE_WAYLAND_H
#define _MOZILLA_WIDGET_GTK_WINDOW_SURFACE_WAYLAND_H
#include <prthread.h>
#include "gfxImageSurface.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/gfx/Types.h"
#include "mozilla/Mutex.h"
#include "nsWaylandDisplay.h"
#include "nsWindow.h"
#include "WaylandBuffer.h"
#include "WindowSurface.h"
#define BACK_BUFFER_NUM 3
namespace mozilla::widget {
class WindowImageSurface {
public:
void DrawToTarget(gfx::DrawTarget* aDest,
LayoutDeviceIntRegion& aWaylandBufferDamage);
WindowImageSurface(gfx::DataSourceSurface* aImageSurface,
const LayoutDeviceIntRegion& aUpdateRegion);
bool OverlapsSurface(class WindowImageSurface& aBottomSurface);
const LayoutDeviceIntRegion* GetUpdateRegion() { return &mUpdateRegion; };
private:
RefPtr<gfx::DataSourceSurface> mImageSurface;
const LayoutDeviceIntRegion mUpdateRegion;
};
// WindowSurfaceWayland is an abstraction for wl_surface
// and related management
class WindowSurfaceWayland : public WindowSurface {
public:
explicit WindowSurfaceWayland(RefPtr<nsWindow> aWindow);
// Lock() / Commit() are called by gecko when Firefox
// wants to display something. Lock() returns a DrawTarget
// where gecko paints. When gecko is done it calls Commit()
// and we try to send the DrawTarget (backed by wl_buffer)
// to wayland compositor.
//
// If we fail (wayland compositor is busy,
// wl_surface is not created yet) we queue the painting
// and we send it to wayland compositor in FrameCallbackHandler()/
// FlushPendingCommits().
already_AddRefed<gfx::DrawTarget> Lock(
const LayoutDeviceIntRegion& aRegion) override;
void Commit(const LayoutDeviceIntRegion& aInvalidRegion) final;
// Try to commit all queued drawings to Wayland compositor. This is usually
// called from other routines but can be used to explicitly flush
// all drawings as we do when wl_buffer is released
// (see WaylandBufferSHMShm::Detach() for instance).
void FlushPendingCommits();
RefPtr<nsWaylandDisplay> GetWaylandDisplay() { return mWaylandDisplay; };
// It's called from wayland compositor when there's the right
// time to send wl_buffer to display. It's no-op if there's no
// queued commits.
static void FrameCallbackHandler(void* aData, struct wl_callback* aCallback,
uint32_t aTime);
static void BufferReleaseCallbackHandler(void* aData, wl_buffer* aBuffer);
private:
~WindowSurfaceWayland();
WaylandBufferSHM* GetWaylandBuffer();
WaylandBufferSHM* SetNewWaylandBuffer();
WaylandBufferSHM* CreateWaylandBuffer(const LayoutDeviceIntSize& aSize);
WaylandBufferSHM* WaylandBufferFindAvailable(
const LayoutDeviceIntSize& aSize);
already_AddRefed<gfx::DrawTarget> LockWaylandBuffer();
already_AddRefed<gfx::DrawTarget> LockImageSurface(
const gfx::IntSize& aLockSize);
void CacheImageSurface(const LayoutDeviceIntRegion& aRegion);
bool CommitImageCacheToWaylandBuffer();
bool DrawDelayedImageCommits(gfx::DrawTarget* aDrawTarget,
LayoutDeviceIntRegion& aWaylandBufferDamage);
// Return true if we need to sync Wayland events after this call.
bool FlushPendingCommitsLocked();
void FrameCallbackHandler();
void BufferReleaseCallbackHandler(wl_buffer* aBuffer);
RefPtr<nsWindow> mWindow;
// Buffer screen rects helps us understand if we operate on
// the same window size as we're called on WindowSurfaceWayland::Lock().
// mMozContainerSize is MozContainer size when our wayland buffer was
// allocated.
LayoutDeviceIntSize mMozContainerSize;
// mWLBufferRect is size of allocated wl_buffer where we paint to.
// It needs to match MozContainer widget size.
LayoutDeviceIntSize mWLBufferSize;
RefPtr<nsWaylandDisplay> mWaylandDisplay;
// Actual buffer (backed by wl_buffer) where all drawings go into.
// Drawn areas are stored at mWaylandBufferDamage and if there's
// any uncommited drawings which needs to be send to wayland compositor
// the mWLBufferIsDirty is set.
RefPtr<WaylandBufferSHM> mWaylandBuffer;
RefPtr<WaylandBufferSHM> mShmBackupBuffer[BACK_BUFFER_NUM];
// When mWaylandFullscreenDamage we invalidate whole surface,
// otherwise partial screen updates (mWaylandBufferDamage) are used.
bool mWaylandFullscreenDamage;
LayoutDeviceIntRegion mWaylandBufferDamage;
// After every commit to wayland compositor a frame callback is requested.
// Any next commit to wayland compositor will happen when frame callback
// comes from wayland compositor back as it's the best time to do the commit.
wl_callback* mFrameCallback;
int mLastCommittedSurfaceID;
// Cached drawings. If we can't get WaylandBuffer (wl_buffer) at
// WindowSurfaceWayland::Lock() we direct gecko rendering to
// mImageSurface.
// If we can't get WaylandBuffer at WindowSurfaceWayland::Commit()
// time, mImageSurface is moved to mDelayedImageCommits which
// holds all cached drawings.
// mDelayedImageCommits can be drawn by FrameCallbackHandler()
// or when WaylandBuffer is detached.
RefPtr<gfx::DataSourceSurface> mImageSurface;
AutoTArray<WindowImageSurface, 30> mDelayedImageCommits;
int64_t mLastCommitTime;
// Indicates that we don't have any cached drawings at mDelayedImageCommits
// and WindowSurfaceWayland::Lock() returned WaylandBuffer to gecko
// to draw into.
bool mDrawToWaylandBufferDirectly;
// Set when our cached drawings (mDelayedImageCommits) contains
// full screen damage. That means we can safely switch WaylandBuffer
// at LockWaylandBuffer().
bool mCanSwitchWaylandBuffer;
// Set when actual WaylandBuffer contains drawings which are not send to
// wayland compositor yet.
bool mWLBufferIsDirty;
// We can't send WaylandBuffer (wl_buffer) to compositor when gecko
// is rendering into it (i.e. between WindowSurfaceWayland::Lock() /
// WindowSurfaceWayland::Commit()).
// Thus we use mBufferCommitAllowed to disable commit by
// FlushPendingCommits().
bool mBufferCommitAllowed;
// We need to clear WaylandBuffer when entire transparent window is repainted.
// This typically apply to popup windows.
bool mBufferNeedsClear;
typedef enum {
// Don't cache anything, always draw directly to wl_buffer
CACHE_NONE = 0,
// Cache only small paints (smaller than 1/2 of screen).
CACHE_SMALL = 1,
// Cache all painting except fullscreen updates.
CACHE_ALL = 2,
} RenderingCacheMode;
// Cache all drawings except fullscreen updates.
// Avoid any rendering artifacts for significant performance penality.
unsigned int mSmoothRendering;
int mSurfaceReadyTimerID;
mozilla::Mutex mSurfaceLock;
};
} // namespace mozilla::widget
#endif // _MOZILLA_WIDGET_GTK_WINDOW_SURFACE_WAYLAND_H

Просмотреть файл

@ -89,7 +89,6 @@ if CONFIG["MOZ_WAYLAND"]:
"nsClipboardWaylandAsync.cpp",
"nsWaylandDisplay.cpp",
"WaylandBuffer.cpp",
"WindowSurfaceWayland.cpp",
"WindowSurfaceWaylandMultiBuffer.cpp",
]
EXPORTS.mozilla.widget += [

Просмотреть файл

@ -9068,15 +9068,6 @@ void nsWindow::GetCompositorWidgetInitData(
isShaped, GdkIsX11Display(), GetClientSize());
}
#ifdef MOZ_WAYLAND
bool nsWindow::WaylandSurfaceNeedsClear() {
if (mContainer) {
return moz_container_wayland_surface_needs_clear(MOZ_CONTAINER(mContainer));
}
return false;
}
#endif
#ifdef MOZ_X11
/* XApp progress support currently works by setting a property
* on a window with this Atom name. A supporting window manager

Просмотреть файл

@ -390,7 +390,6 @@ class nsWindow final : public nsBaseWidget {
void WaylandDragWorkaround(GdkEventButton* aEvent);
wl_display* GetWaylandDisplay();
bool WaylandSurfaceNeedsClear();
virtual void CreateCompositorVsyncDispatcher() override;
LayoutDeviceIntPoint GetNativePointerLockCenter() {
return mNativePointerLockCenter;