461 lines
19 KiB
C++
461 lines
19 KiB
C++
/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim:expandtab:shiftwidth=2:tabstop=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 __MOZ_WAYLAND_SURFACE_H__
|
|
#define __MOZ_WAYLAND_SURFACE_H__
|
|
|
|
#include "nsWaylandDisplay.h"
|
|
#include "mozilla/Mutex.h"
|
|
#include "mozilla/Atomics.h"
|
|
#include "WaylandSurfaceLock.h"
|
|
#include "mozilla/GRefPtr.h"
|
|
|
|
/* Workaround for bug at wayland-util.h,
|
|
* present in wayland-devel < 1.12
|
|
*/
|
|
struct wl_surface;
|
|
struct wl_subsurface;
|
|
struct wl_egl_window;
|
|
|
|
class MessageLoop;
|
|
|
|
namespace mozilla::widget {
|
|
|
|
class WaylandBuffer;
|
|
|
|
// WaylandSurface is a wrapper for Wayland rendering target
|
|
// which is wl_surface / wl_subsurface.
|
|
class WaylandSurface final {
|
|
friend WaylandSurfaceLock;
|
|
|
|
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WaylandSurface);
|
|
|
|
WaylandSurface(RefPtr<WaylandSurface> aParent, gfx::IntSize aSize);
|
|
|
|
#ifdef MOZ_LOGGING
|
|
nsAutoCString GetDebugTag() const;
|
|
void* GetLoggingWidget() const { return mLoggingWidget; };
|
|
void SetLoggingWidget(void* aWidget) { mLoggingWidget = aWidget; }
|
|
#endif
|
|
|
|
void InitialFrameCallbackHandler(struct wl_callback* aCallback);
|
|
void AddOrFireReadyToDrawCallback(const std::function<void(void)>& aDrawCB);
|
|
void ClearReadyToDrawCallbacks();
|
|
|
|
void FrameCallbackHandler(struct wl_callback* aCallback, uint32_t aTime,
|
|
bool aRoutedFromChildSurface);
|
|
// Run only once at most.
|
|
void AddOneTimeFrameCallbackLocked(
|
|
const WaylandSurfaceLock& aProofOfLock,
|
|
const std::function<void(wl_callback*, uint32_t)>& aFrameCallbackHandler);
|
|
|
|
// Run frame callback repeatedly. Callback is removed on Unmap.
|
|
// If aEmulateFrameCallback is set to true and WaylandSurface is mapped and
|
|
// ready to draw and we don't have buffer attached yet,
|
|
// fire aFrameCallbackHandler without frame callback from
|
|
// compositor in sFrameCheckTimeoutMs.
|
|
void AddPersistentFrameCallbackLocked(
|
|
const WaylandSurfaceLock& aProofOfLock,
|
|
const std::function<void(wl_callback*, uint32_t)>& aFrameCallbackHandler,
|
|
bool aEmulateFrameCallback = false);
|
|
|
|
// Enable/Disable any frame callback emission (includes emulated ones).
|
|
void SetFrameCallbackState(bool aEnabled);
|
|
|
|
// Create and resize EGL window.
|
|
// GetEGLWindow() takes unscaled window size as we derive size from GdkWindow.
|
|
// It's scaled internally by WaylandSurface fractional scale.
|
|
wl_egl_window* GetEGLWindow(nsIntSize aUnscaledSize);
|
|
// SetEGLWindowSize() takes scaled size - it's called from rendering code
|
|
// which uses scaled sizes.
|
|
bool SetEGLWindowSize(nsIntSize aScaledSize);
|
|
bool HasEGLWindow() const { return !!mEGLWindow; }
|
|
|
|
// Read to draw means we got frame callback from parent surface
|
|
// where we attached to.
|
|
bool IsReadyToDraw() const { return mIsReadyToDraw; }
|
|
// Mapped means we have all internals created.
|
|
bool IsMapped() const { return mIsMapped; }
|
|
// Indicate that Wayland surface uses Gdk resources which
|
|
// need to be released on main thread by GdkCleanUpLocked().
|
|
// It may be called after Unmap() to make sure
|
|
// Gtk resources are not allocated again.
|
|
bool IsPendingGdkCleanup() const { return mIsPendingGdkCleanup; }
|
|
|
|
bool IsOpaqueSurfaceHandlerSet() const { return mIsOpaqueSurfaceHandlerSet; }
|
|
|
|
bool HasBufferAttached() const { return mBufferAttached; }
|
|
|
|
// Mapped as direct surface of MozContainer
|
|
bool MapLocked(const WaylandSurfaceLock& aProofOfLock,
|
|
wl_surface* aParentWLSurface,
|
|
gfx::IntPoint aSubsurfacePosition);
|
|
// Mapped as child of WaylandSurface (used by layers)
|
|
bool MapLocked(const WaylandSurfaceLock& aProofOfLock,
|
|
WaylandSurfaceLock* aParentWaylandSurfaceLock,
|
|
gfx::IntPoint aSubsurfacePosition);
|
|
// Unmap surface which hides it
|
|
void UnmapLocked(WaylandSurfaceLock& aSurfaceLock);
|
|
|
|
// Clean up Gdk resources, on main thread only
|
|
void GdkCleanUpLocked(const WaylandSurfaceLock& aProofOfLock);
|
|
|
|
// Allow to register and run unmap callback.
|
|
// Unmap callback needs to be called *before* UnmapLocked() call
|
|
// on main thread.
|
|
void SetUnmapCallbackLocked(const WaylandSurfaceLock& aProofOfLock,
|
|
const std::function<void(void)>& aUnmapCB);
|
|
void ClearUnmapCallbackLocked(const WaylandSurfaceLock& aProofOfLock);
|
|
|
|
void RunUnmapCallback();
|
|
|
|
// Create Viewport to manage surface transformations.
|
|
// aFollowsSizeChanges if set, Viewport destination size
|
|
// is updated according to buffer size.
|
|
bool CreateViewportLocked(const WaylandSurfaceLock& aProofOfLock,
|
|
bool aFollowsSizeChanges);
|
|
|
|
void AddReadyToDrawCallbackLocked(
|
|
const WaylandSurfaceLock& aProofOfLock,
|
|
const std::function<void(void)>& aInitialDrawCB);
|
|
|
|
// Attach WaylandBuffer which shows WaylandBuffer content
|
|
// on screen.
|
|
bool AttachLocked(WaylandSurfaceLock& aSurfaceLock,
|
|
RefPtr<WaylandBuffer> aWaylandBuffer);
|
|
|
|
// If there's any WaylandBuffer recently attached, detach it.
|
|
// It makes the WaylandSurface invisible and it doesn't have any
|
|
// content.
|
|
void RemoveAttachedBufferLocked(WaylandSurfaceLock& aProofOfLock);
|
|
|
|
// Called from Wayland compostor async handler when wl_buffer is
|
|
// detached or deleted.
|
|
void BufferFreeCallbackHandler(uintptr_t aWlBufferID, bool aWlBufferDelete);
|
|
|
|
// CommitLocked() is needed to call after some of *Locked() method
|
|
// to submit the action to Wayland compositor by wl_surface_commit().
|
|
|
|
// It's possible to stack more *Locked() methods
|
|
// together and do commit after the last one to do the changes in atomic way.
|
|
|
|
// Need of commit is tracked by mSurfaceNeedsCommit flag and
|
|
// if it's set, CommitLocked() is called when WaylandSurfaceLock is destroyed
|
|
// and WaylandSurface is unlocked.
|
|
void CommitLocked(const WaylandSurfaceLock& aProofOfLock,
|
|
bool aForceCommit = false, bool aForceDisplayFlush = false);
|
|
|
|
void EnableDMABufFormatsLocked(
|
|
const WaylandSurfaceLock& aProofOfLock,
|
|
const std::function<void(DMABufFormats*)>& aFormatRefreshCB);
|
|
void DisableDMABufFormatsLocked(const WaylandSurfaceLock& aProofOfLock);
|
|
|
|
// Place this WaylandSurface above aLowerSurface
|
|
void PlaceAboveLocked(const WaylandSurfaceLock& aProofOfLock,
|
|
WaylandSurfaceLock& aLowerSurfaceLock);
|
|
void MoveLocked(const WaylandSurfaceLock& aProofOfLock,
|
|
gfx::IntPoint aPosition);
|
|
void SetViewPortSourceRectLocked(const WaylandSurfaceLock& aProofOfLock,
|
|
gfx::Rect aRect);
|
|
void SetViewPortDestLocked(const WaylandSurfaceLock& aProofOfLock,
|
|
gfx::IntSize aDestSize);
|
|
void SetTransformFlippedLocked(const WaylandSurfaceLock& aProofOfLock,
|
|
bool aFlippedX, bool aFlippedY);
|
|
|
|
void SetOpaqueRegion(const gfx::IntRegion& aRegion);
|
|
void SetOpaqueRegionLocked(const WaylandSurfaceLock& aProofOfLock,
|
|
const gfx::IntRegion& aRegion);
|
|
void SetOpaqueLocked(const WaylandSurfaceLock& aProofOfLock);
|
|
void ClearOpaqueRegionLocked(const WaylandSurfaceLock& aProofOfLock);
|
|
|
|
bool DisableUserInputLocked(const WaylandSurfaceLock& aProofOfLock);
|
|
void InvalidateRegionLocked(const WaylandSurfaceLock& aProofOfLock,
|
|
const gfx::IntRegion& aInvalidRegion);
|
|
void InvalidateLocked(const WaylandSurfaceLock& aProofOfLock);
|
|
|
|
bool EnableFractionalScaleLocked(
|
|
const WaylandSurfaceLock& aProofOfLock,
|
|
std::function<void(void)> aFractionalScaleCallback, bool aManageViewport);
|
|
bool EnableCeiledScaleLocked(const WaylandSurfaceLock& aProofOfLock);
|
|
|
|
bool IsFractionalScaleLocked(const WaylandSurfaceLock& aProofOfLock) const {
|
|
return mScaleType == ScaleType::Disabled;
|
|
}
|
|
bool IsCeiledScaleLocked(const WaylandSurfaceLock& aProofOfLock) const {
|
|
return mScaleType == ScaleType::Ceiled;
|
|
}
|
|
bool IsScaleEnabledLocked(const WaylandSurfaceLock& aProofOfLock) const {
|
|
return mScaleType != ScaleType::Disabled;
|
|
}
|
|
|
|
// Returns scale as float point number. If WaylandSurface is not mapped,
|
|
// return fractional scale of parent surface.
|
|
// Returns sNoScale is we can't get it.
|
|
static constexpr const double sNoScale = -1;
|
|
double GetScale();
|
|
|
|
// The same as GetScale() but returns monitor scale if window scale is
|
|
// missing.
|
|
double GetScaleSafe();
|
|
|
|
// Called when screen ceiled scale changed or set initial scale before we map
|
|
// and paint the surface.
|
|
void SetCeiledScaleLocked(const WaylandSurfaceLock& aProofOfLock,
|
|
int aScreenCeiledScale);
|
|
|
|
// Called by wayland compositor when fractional scale is changed.
|
|
static void FractionalScaleHandler(void* data,
|
|
struct wp_fractional_scale_v1* info,
|
|
uint32_t wire_scale);
|
|
|
|
static void AfterPaintHandler(GdkFrameClock* aClock, void* aData);
|
|
|
|
// See https://gitlab.gnome.org/GNOME/gtk/-/merge_requests/3111 why we use it.
|
|
// If child surface covers whole area of parent surface and it's opaque,
|
|
// parent surface will not get any events (frame callbacks) from compositor
|
|
// as it's considered as invisible.
|
|
//
|
|
// Firefox uses the parent wl_surface (owned by GdkWindow) to get input
|
|
// events. Without gdk_wayland_window_add_frame_callback_surface() call,
|
|
// Gdk is not getting any events from compostor and we're frozen.
|
|
//
|
|
// So gdk_wayland_window_add_frame_callback_surface() registers wl_surface
|
|
// owned by WaylandSurface to GtkWindow and requests frame callback
|
|
// for it. Such frame callback is then routed to GdkWindow and it's used
|
|
// to fire events like native GdkWindow ones.
|
|
//
|
|
// To make sure WaylandSurface's wl_surface frame callback is generated,
|
|
// we need to commit the wl_surface regularly as Gdk registers frame callback
|
|
// for it at on_frame_clock_after_paint() event of GdkWindow.
|
|
bool AddOpaqueSurfaceHandlerLocked(const WaylandSurfaceLock& aProofOfLock,
|
|
GdkWindow* aGdkWindow,
|
|
bool aRegisterCommitHandler);
|
|
bool RemoveOpaqueSurfaceHandlerLocked(const WaylandSurfaceLock& aProofOfLock);
|
|
|
|
// Additional callback to call from on_frame_clock_after_paint()
|
|
// and before this wl_surface is commited.
|
|
// It can be used to update subsurfaces from main thread.
|
|
void SetGdkCommitCallbackLocked(
|
|
const WaylandSurfaceLock& aProofOfLock,
|
|
const std::function<void(void)>& aGdkCommitCB);
|
|
void ClearGdkCommitCallbackLocked(const WaylandSurfaceLock& aProofOfLock);
|
|
|
|
RefPtr<DMABufFormats> GetDMABufFormats() const { return mFormats; }
|
|
|
|
GdkWindow* GetGdkWindow() const;
|
|
|
|
static bool IsOpaqueRegionEnabled();
|
|
|
|
void SetParentLocked(const WaylandSurfaceLock& aProofOfLock,
|
|
RefPtr<WaylandSurface> aParent);
|
|
|
|
bool EnableColorManagementLocked(const WaylandSurfaceLock& aProofOfLock);
|
|
|
|
static void ImageDescriptionFailed(
|
|
void* aData, struct wp_image_description_v1* aImageDescription,
|
|
uint32_t aCause, const char* aMsg);
|
|
static void ImageDescriptionReady(
|
|
void* aData, struct wp_image_description_v1* aImageDescription,
|
|
uint32_t aIdentity);
|
|
|
|
private:
|
|
~WaylandSurface();
|
|
|
|
bool MapLocked(const WaylandSurfaceLock& aProofOfLock,
|
|
wl_surface* aParentWLSurface,
|
|
WaylandSurfaceLock* aParentWaylandSurfaceLock,
|
|
gfx::IntPoint aSubsurfacePosition, bool aSubsurfaceDesync,
|
|
bool aUseReadyToDrawCallback = true);
|
|
|
|
void SetSizeLocked(const WaylandSurfaceLock& aProofOfLock,
|
|
gfx::IntSize aSizeScaled, gfx::IntSize aUnscaledSize);
|
|
|
|
wl_surface* Lock(WaylandSurfaceLock* aWaylandSurfaceLock);
|
|
void Unlock(struct wl_surface** aSurface,
|
|
WaylandSurfaceLock* aWaylandSurfaceLock);
|
|
void Commit(WaylandSurfaceLock* aProofOfLock, bool aForceCommit,
|
|
bool aForceDisplayFlush);
|
|
|
|
// Force release/detele all buffers. Some of them may be attached to
|
|
// compostor and may get wl_buffer::release callback so we need to sync
|
|
// delete with wayland compostor.
|
|
void ReleaseAllWaylandBuffersLocked(WaylandSurfaceLock& aSurfaceLock);
|
|
|
|
void RequestFrameCallbackLocked(const WaylandSurfaceLock& aProofOfLock);
|
|
void ClearFrameCallbackLocked(const WaylandSurfaceLock& aProofOfLock);
|
|
bool HasEmulatedFrameCallbackLocked(
|
|
const WaylandSurfaceLock& aProofOfLock) const;
|
|
|
|
void ClearReadyToDrawCallbacksLocked(const WaylandSurfaceLock& aProofOfLock);
|
|
|
|
void ClearScaleLocked(const WaylandSurfaceLock& aProofOfLock);
|
|
|
|
// Weak ref to owning widget (nsWindow or NativeLayerWayland),
|
|
// used for diagnostics/logging only.
|
|
void* mLoggingWidget = nullptr;
|
|
|
|
// WaylandSurface mapped - we have valid wl_surface where we can paint to.
|
|
mozilla::Atomic<bool, mozilla::Relaxed> mIsMapped{false};
|
|
|
|
// Wayland shows only subsurfaces of visible parent surfaces.
|
|
// mIsReadyToDraw means our parent wl_surface has content so
|
|
// this WaylandSurface can be visible on screen and get get frame callback.
|
|
mozilla::Atomic<bool, mozilla::Relaxed> mIsReadyToDraw{false};
|
|
|
|
// We used Gdk functions which needs clean up in main thread.
|
|
mozilla::Atomic<bool, mozilla::Relaxed> mIsPendingGdkCleanup{false};
|
|
|
|
std::function<void(void)> mGdkCommitCallback;
|
|
std::function<void(void)> mUnmapCallback;
|
|
|
|
// Scaled surface size, ceiled or fractional.
|
|
// This reflects real surface size which we paint.
|
|
gfx::IntSize mSizeScaled;
|
|
|
|
// Parent GdkWindow where we paint to, directly or via subsurface.
|
|
RefPtr<GdkWindow> mGdkWindow;
|
|
|
|
// Parent wl_surface owned by mGdkWindow. It's used when we're attached
|
|
// directly to MozContainer.
|
|
wl_surface* mParentSurface = nullptr;
|
|
|
|
// Parent WaylandSurface.
|
|
//
|
|
// Layer rendering (compositor) uses mSurface directly attached to
|
|
// wl_surface owned by mParent.
|
|
//
|
|
// For non-compositing rendering (old) mParent is WaylandSurface
|
|
// owned by parent nsWindow.
|
|
RefPtr<WaylandSurface> mParent;
|
|
|
|
// wl_surface setup/states
|
|
wl_surface* mSurface = nullptr;
|
|
bool mSurfaceNeedsCommit = false;
|
|
wl_subsurface* mSubsurface = nullptr;
|
|
gfx::IntPoint mSubsurfacePosition{-1, -1};
|
|
|
|
// Wayland buffers recently attached to this surface or held by
|
|
// Wayland compositor.
|
|
// There may be more than one buffer attached, for instance if
|
|
// previous buffer is hold by compositor. We need to keep
|
|
// there buffers live until compositor notify us that we
|
|
// can release them.
|
|
AutoTArray<RefPtr<WaylandBuffer>, 3> mAttachedBuffers;
|
|
|
|
// Indicates mSurface has buffer attached so we can attach subsurface
|
|
// to it and expect to get frame callbacks from Wayland compositor.
|
|
// We set it at AttachLocked() or when we get first frame callback
|
|
// (when EGL is used).
|
|
mozilla::Atomic<bool, mozilla::Relaxed> mBufferAttached{false};
|
|
|
|
mozilla::Atomic<wl_egl_window*, mozilla::Relaxed> mEGLWindow{nullptr};
|
|
|
|
bool mViewportFollowsSizeChanges = true;
|
|
wp_viewport* mViewport = nullptr;
|
|
gfx::Rect mViewportSourceRect{-1, -1, -1, -1};
|
|
gfx::IntSize mViewportDestinationSize{-1, -1};
|
|
|
|
// Surface flip state on X/Y asix
|
|
bool mBufferTransformFlippedX = false;
|
|
bool mBufferTransformFlippedY = false;
|
|
|
|
// Frame callback registered to parent surface. When we get it we know
|
|
// parent surface is ready and we can paint.
|
|
wl_callback* mReadyToDrawFrameCallback = nullptr;
|
|
std::vector<std::function<void(void)>> mReadToDrawCallbacks;
|
|
|
|
// Frame callbacks of this surface
|
|
wl_callback* mFrameCallback = nullptr;
|
|
|
|
struct FrameCallback {
|
|
std::function<void(wl_callback*, uint32_t)> mCb;
|
|
bool mEmulated = false;
|
|
};
|
|
|
|
bool mFrameCallbackEnabled = true;
|
|
// Frame callback handlers called every frame
|
|
std::vector<FrameCallback> mPersistentFrameCallbackHandlers;
|
|
// Frame callback handlers called only once
|
|
std::vector<FrameCallback> mOneTimeFrameCallbackHandlers;
|
|
|
|
// WaylandSurface is used from Compositor/Rendering/Main threads.
|
|
mozilla::Mutex mMutex{"WaylandSurface"};
|
|
WaylandSurfaceLock* mSurfaceLock = nullptr;
|
|
|
|
// We may mark part of mSurface as opaque (non-transparent) if it's supported
|
|
// by Gtk which allows compositor to skip painting of covered parts.
|
|
mozilla::Atomic<bool, mozilla::Relaxed> mIsOpaqueSurfaceHandlerSet{false};
|
|
gulong mGdkAfterPaintId = 0;
|
|
static bool sIsOpaqueRegionEnabled;
|
|
static void (*sGdkWaylandWindowAddCallbackSurface)(GdkWindow*,
|
|
struct wl_surface*);
|
|
static void (*sGdkWaylandWindowRemoveCallbackSurface)(GdkWindow*,
|
|
struct wl_surface*);
|
|
guint mEmulatedFrameCallbackTimerID = 0;
|
|
constexpr static int sEmulatedFrameCallbackTimeoutMs = (int)(1000.0 / 60.0);
|
|
|
|
// We use two scale systems in Firefox/Wayland. Ceiled (integer) scale and
|
|
// fractional scale. Ceiled scale is easy to implement but comes with
|
|
// rendering overhead while fractional rendering paints buffers with exact
|
|
// scale.
|
|
//
|
|
// Fractional scale is used as rendering optimization.
|
|
// For instance if 225% scale is used, ceiled scale is 3
|
|
// and fractional 2.20.
|
|
//
|
|
// If we paint content with ceiled scale 3 and desktop uses scale 225%,
|
|
// Wayland compositor downscales buffer to 2.20 on rendering
|
|
// but we paint more pixels than neccessary (so we use name ceiled).
|
|
//
|
|
// Scale is used by wp_viewport. If a surface has a surface-local size
|
|
// of 100 px by 50 px and wishes to submit buffers with a scale of 1.5,
|
|
// then a buffer of 150px by 75 px should be used and the wp_viewport
|
|
// destination rectangle should be 100 px by 50 px.
|
|
// The wl_surface buffer scale should remain set to 1.
|
|
//
|
|
// For scale 2 (200%) we use surface size 200 x 100 px and set
|
|
// viewport size to 100 x 50 px.
|
|
//
|
|
// We're getting fractional scale number with a small delay from
|
|
// wp_fractional_scale_v1 after fist commit to surface.
|
|
// Meanwhile we can use ceiled scale number instead of fractional one or
|
|
// get fractional scale from parent window (if there's any).
|
|
//
|
|
enum class ScaleType {
|
|
Disabled,
|
|
Ceiled,
|
|
Fractional,
|
|
};
|
|
|
|
ScaleType mScaleType = ScaleType::Disabled;
|
|
|
|
// mScreenScale is set from main thread only but read from
|
|
// different threads.
|
|
mozilla::Atomic<double, mozilla::Relaxed> mScreenScale{sNoScale};
|
|
|
|
wp_fractional_scale_v1* mFractionalScaleListener = nullptr;
|
|
|
|
// mFractionalScaleCallback is called from
|
|
// wp_fractional_scale_v1_add_listener when scale is changed.
|
|
std::function<void(void)> mFractionalScaleCallback = []() {};
|
|
|
|
bool mUseDMABufFormats = false;
|
|
// Wayland display notifies us when available DRM formats are are changed.
|
|
// For instance if wl_surface becomes fullscreen we may get DRM formats
|
|
// for direct scanout.
|
|
std::function<void(DMABufFormats*)> mDMABufFormatRefreshCallback;
|
|
RefPtr<DMABufFormats> mFormats;
|
|
|
|
// HDR support
|
|
bool mHDRSet = false;
|
|
wp_color_management_surface_v1* mColorSurface = nullptr;
|
|
wp_image_description_v1* mImageDescription = nullptr;
|
|
};
|
|
|
|
} // namespace mozilla::widget
|
|
|
|
#endif /* __MOZ_WAYLAND_SURFACE_H__ */
|