diff options
Diffstat (limited to 'gfx/layers/NativeLayerWayland.cpp')
-rw-r--r-- | gfx/layers/NativeLayerWayland.cpp | 760 |
1 files changed, 760 insertions, 0 deletions
diff --git a/gfx/layers/NativeLayerWayland.cpp b/gfx/layers/NativeLayerWayland.cpp new file mode 100644 index 0000000000..5b9eecba36 --- /dev/null +++ b/gfx/layers/NativeLayerWayland.cpp @@ -0,0 +1,760 @@ +/* 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 "mozilla/layers/NativeLayerWayland.h" + +#include <dlfcn.h> +#include <utility> +#include <algorithm> + +#include "gfxUtils.h" +#include "nsGtkUtils.h" +#include "GLContextProvider.h" +#include "GLBlitHelper.h" +#include "mozilla/gfx/DataSurfaceHelpers.h" +#include "mozilla/gfx/Logging.h" +#include "mozilla/layers/SurfacePoolWayland.h" +#include "mozilla/StaticPrefs_widget.h" +#include "mozilla/webrender/RenderThread.h" + +namespace mozilla::layers { + +using gfx::BackendType; +using gfx::DrawTarget; +using gfx::IntPoint; +using gfx::IntRect; +using gfx::IntRegion; +using gfx::IntSize; +using gfx::Matrix4x4; +using gfx::Point; +using gfx::Rect; +using gfx::SamplingFilter; +using gfx::Size; + +static const struct wl_callback_listener sFrameListenerNativeLayerWayland = { + NativeLayerWayland::FrameCallbackHandler}; + +CallbackMultiplexHelper::CallbackMultiplexHelper(CallbackFunc aCallbackFunc, + void* aCallbackData) + : mCallbackFunc(aCallbackFunc), mCallbackData(aCallbackData) {} + +void CallbackMultiplexHelper::Callback(uint32_t aTime) { + if (!mActive) { + return; + } + mActive = false; + + // This is likely the first of a batch of frame callbacks being processed and + // may trigger the setup of a successive one. In order to avoid complexity, + // defer calling the callback function until we had a chance to process + // all pending frame callbacks. + + AddRef(); + nsCOMPtr<nsIRunnable> runnable = NewRunnableMethod<uint32_t>( + "layers::CallbackMultiplexHelper::RunCallback", this, + &CallbackMultiplexHelper::RunCallback, aTime); + MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThreadQueue( + runnable.forget(), EventQueuePriority::Vsync)); +} + +void CallbackMultiplexHelper::RunCallback(uint32_t aTime) { + mCallbackFunc(mCallbackData, aTime); + Release(); +} + +/* static */ +already_AddRefed<NativeLayerRootWayland> +NativeLayerRootWayland::CreateForMozContainer(MozContainer* aContainer) { + RefPtr<NativeLayerRootWayland> layerRoot = + new NativeLayerRootWayland(aContainer); + return layerRoot.forget(); +} + +NativeLayerRootWayland::NativeLayerRootWayland(MozContainer* aContainer) + : mMutex("NativeLayerRootWayland"), mContainer(aContainer) { + g_object_ref(mContainer); +} + +NativeLayerRootWayland::~NativeLayerRootWayland() { + GdkWindow* gdkWindow = gtk_widget_get_window(GTK_WIDGET(mContainer)); + if (gdkWindow) { + GdkFrameClock* frameClock = gdk_window_get_frame_clock(gdkWindow); + g_signal_handlers_disconnect_by_data(frameClock, this); + } + g_object_unref(mContainer); +} + +already_AddRefed<NativeLayer> NativeLayerRootWayland::CreateLayer( + const IntSize& aSize, bool aIsOpaque, + SurfacePoolHandle* aSurfacePoolHandle) { + RefPtr<NativeLayer> layer = new NativeLayerWayland( + aSize, aIsOpaque, aSurfacePoolHandle->AsSurfacePoolHandleWayland()); + return layer.forget(); +} + +already_AddRefed<NativeLayer> +NativeLayerRootWayland::CreateLayerForExternalTexture(bool aIsOpaque) { + RefPtr<NativeLayer> layer = new NativeLayerWayland(aIsOpaque); + return layer.forget(); +} + +void NativeLayerRootWayland::AppendLayer(NativeLayer* aLayer) { + MOZ_RELEASE_ASSERT(false); + MutexAutoLock lock(mMutex); + + RefPtr<NativeLayerWayland> layerWayland = aLayer->AsNativeLayerWayland(); + MOZ_RELEASE_ASSERT(layerWayland); + + mSublayers.AppendElement(layerWayland); +} + +void NativeLayerRootWayland::RemoveLayer(NativeLayer* aLayer) { + MOZ_RELEASE_ASSERT(false); + MutexAutoLock lock(mMutex); + + RefPtr<NativeLayerWayland> layerWayland = aLayer->AsNativeLayerWayland(); + MOZ_RELEASE_ASSERT(layerWayland); + + mSublayers.RemoveElement(layerWayland); +} + +void NativeLayerRootWayland::SetLayers( + const nsTArray<RefPtr<NativeLayer>>& aLayers) { + MutexAutoLock lock(mMutex); + + nsTArray<RefPtr<NativeLayerWayland>> newSublayers(aLayers.Length()); + for (const RefPtr<NativeLayer>& sublayer : aLayers) { + RefPtr<NativeLayerWayland> layer = sublayer->AsNativeLayerWayland(); + newSublayers.AppendElement(layer); + } + + if (newSublayers != mSublayers) { + for (const RefPtr<NativeLayerWayland>& layer : mSublayers) { + if (!newSublayers.Contains(layer)) { + mOldSublayers.AppendElement(layer); + } + } + mSublayers = std::move(newSublayers); + mNewLayers = true; + } +} + +bool NativeLayerRootWayland::CommitToScreen() { + MutexAutoLock lock(mMutex); + return CommitToScreen(lock); +} + +bool NativeLayerRootWayland::CommitToScreen(const MutexAutoLock& aProofOfLock) { + mFrameInProcess = false; + + MozContainerSurfaceLock lock(mContainer); + struct wl_surface* containerSurface = lock.GetSurface(); + if (!containerSurface) { + if (!mCallbackRequested) { + RefPtr<NativeLayerRootWayland> self(this); + moz_container_wayland_add_initial_draw_callback_locked( + mContainer, [self]() -> void { + MutexAutoLock lock(self->mMutex); + if (!self->mFrameInProcess) { + self->CommitToScreen(lock); + } + self->mCallbackRequested = false; + }); + mCallbackRequested = true; + } + return true; + } + + wl_surface* previousSurface = nullptr; + for (RefPtr<NativeLayerWayland>& layer : mSublayers) { + layer->EnsureParentSurface(containerSurface); + + if (mNewLayers) { + wl_subsurface_place_above(layer->mWlSubsurface, previousSurface + ? previousSurface + : containerSurface); + previousSurface = layer->mWlSurface; + } + + MOZ_RELEASE_ASSERT(layer->mTransform.Is2D()); + auto transform2D = layer->mTransform.As2D(); + + Rect surfaceRectClipped = + Rect(0, 0, (float)layer->mSize.width, (float)layer->mSize.height); + surfaceRectClipped = + surfaceRectClipped.Intersect(Rect(layer->mDisplayRect)); + + transform2D.PostTranslate((float)layer->mPosition.x, + (float)layer->mPosition.y); + surfaceRectClipped = transform2D.TransformBounds(surfaceRectClipped); + + if (layer->mClipRect) { + surfaceRectClipped = + surfaceRectClipped.Intersect(Rect(layer->mClipRect.value())); + } + + if (roundf(surfaceRectClipped.width) > 0 && + roundf(surfaceRectClipped.height) > 0) { + layer->SetBufferTransformFlipped(transform2D._11 < 0.0, + transform2D._22 < 0.0); + + double bufferScale = moz_container_wayland_get_scale(mContainer); + layer->SetSubsurfacePosition(floor(surfaceRectClipped.x / bufferScale), + floor(surfaceRectClipped.y / bufferScale)); + layer->SetViewportDestinationSize( + ceil(surfaceRectClipped.width / bufferScale), + ceil(surfaceRectClipped.height / bufferScale)); + + auto transform2DInversed = transform2D.Inverse(); + Rect bufferClip = transform2DInversed.TransformBounds(surfaceRectClipped); + layer->SetViewportSourceRect(bufferClip); + + layer->Commit(); + } else { + layer->Unmap(); + } + } + + if (mNewLayers) { + for (RefPtr<NativeLayerWayland>& layer : mOldSublayers) { + layer->Unmap(); + } + mOldSublayers.Clear(); + + nsCOMPtr<nsIRunnable> updateLayersRunnable = NewRunnableMethod<>( + "layers::NativeLayerRootWayland::UpdateLayersOnMainThread", this, + &NativeLayerRootWayland::UpdateLayersOnMainThread); + MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThreadQueue( + updateLayersRunnable.forget(), EventQueuePriority::Normal)); + mNewLayers = false; + } + + if (containerSurface != mWlSurface) { + if (!mShmBuffer) { + mShmBuffer = widget::WaylandBufferSHM::Create(LayoutDeviceIntSize(1, 1)); + mShmBuffer->Clear(); + } + mShmBuffer->AttachAndCommit(containerSurface); + mWlSurface = containerSurface; + } else { + wl_surface_commit(containerSurface); + } + + wl_display_flush(widget::WaylandDisplayGet()->GetDisplay()); + return true; +} + +void NativeLayerRootWayland::RequestFrameCallback(CallbackFunc aCallbackFunc, + void* aCallbackData) { + MutexAutoLock lock(mMutex); + + mCallbackMultiplexHelper = + new CallbackMultiplexHelper(aCallbackFunc, aCallbackData); + + for (const RefPtr<NativeLayerWayland>& layer : mSublayersOnMainThread) { + layer->RequestFrameCallback(mCallbackMultiplexHelper); + } + + MozContainerSurfaceLock lockContainer(mContainer); + struct wl_surface* wlSurface = lockContainer.GetSurface(); + if (wlSurface) { + wl_surface_commit(wlSurface); + wl_display_flush(widget::WaylandDisplayGet()->GetDisplay()); + } +} + +static void sAfterFrameClockAfterPaint( + GdkFrameClock* aClock, NativeLayerRootWayland* aNativeLayerRoot) { + aNativeLayerRoot->AfterFrameClockAfterPaint(); +} + +void NativeLayerRootWayland::AfterFrameClockAfterPaint() { + MutexAutoLock lock(mMutex); + MozContainerSurfaceLock lockContainer(mContainer); + struct wl_surface* containerSurface = lockContainer.GetSurface(); + for (const RefPtr<NativeLayerWayland>& layer : mSublayersOnMainThread) { + wl_surface_commit(layer->mWlSurface); + } + if (containerSurface) { + wl_surface_commit(containerSurface); + } +} + +void NativeLayerRootWayland::UpdateLayersOnMainThread() { + AssertIsOnMainThread(); + MutexAutoLock lock(mMutex); + + static auto sGdkWaylandWindowAddCallbackSurface = + (void (*)(GdkWindow*, struct wl_surface*))dlsym( + RTLD_DEFAULT, "gdk_wayland_window_add_frame_callback_surface"); + static auto sGdkWaylandWindowRemoveCallbackSurface = + (void (*)(GdkWindow*, struct wl_surface*))dlsym( + RTLD_DEFAULT, "gdk_wayland_window_remove_frame_callback_surface"); + + MozContainerSurfaceLock lockContainer(mContainer); + struct wl_surface* containerSurface = lockContainer.GetSurface(); + GdkWindow* gdkWindow = gtk_widget_get_window(GTK_WIDGET(mContainer)); + + mSublayersOnMainThread.RemoveElementsBy([&](const auto& layer) { + if (!mSublayers.Contains(layer)) { + if (layer->IsOpaque() && + StaticPrefs::widget_wayland_opaque_region_enabled_AtStartup() && + sGdkWaylandWindowAddCallbackSurface && gdkWindow) { + sGdkWaylandWindowRemoveCallbackSurface(gdkWindow, layer->mWlSurface); + + wl_compositor* compositor = + widget::WaylandDisplayGet()->GetCompositor(); + wl_region* region = wl_compositor_create_region(compositor); + wl_surface_set_opaque_region(layer->mWlSurface, region); + wl_region_destroy(region); + wl_surface_commit(layer->mWlSurface); + } + return true; + } + return false; + }); + + for (const RefPtr<NativeLayerWayland>& layer : mSublayers) { + if (!mSublayersOnMainThread.Contains(layer)) { + if (layer->IsOpaque() && + StaticPrefs::widget_wayland_opaque_region_enabled_AtStartup() && + sGdkWaylandWindowRemoveCallbackSurface && gdkWindow) { + sGdkWaylandWindowAddCallbackSurface(gdkWindow, layer->mWlSurface); + + wl_compositor* compositor = + widget::WaylandDisplayGet()->GetCompositor(); + wl_region* region = wl_compositor_create_region(compositor); + wl_region_add(region, 0, 0, INT32_MAX, INT32_MAX); + wl_surface_set_opaque_region(layer->mWlSurface, region); + wl_region_destroy(region); + wl_surface_commit(layer->mWlSurface); + } + if (mCallbackMultiplexHelper && mCallbackMultiplexHelper->IsActive()) { + layer->RequestFrameCallback(mCallbackMultiplexHelper); + } + mSublayersOnMainThread.AppendElement(layer); + } + } + + if (containerSurface) { + wl_surface_commit(containerSurface); + } + + if (!mGdkAfterPaintId && gdkWindow) { + GdkFrameClock* frameClock = gdk_window_get_frame_clock(gdkWindow); + mGdkAfterPaintId = + g_signal_connect_after(frameClock, "after-paint", + G_CALLBACK(sAfterFrameClockAfterPaint), this); + } +} + +NativeLayerWayland::NativeLayerWayland( + const IntSize& aSize, bool aIsOpaque, + SurfacePoolHandleWayland* aSurfacePoolHandle) + : mMutex("NativeLayerWayland"), + mSurfacePoolHandle(aSurfacePoolHandle), + mSize(aSize), + mIsOpaque(aIsOpaque) { + MOZ_RELEASE_ASSERT(mSurfacePoolHandle, + "Need a non-null surface pool handle."); + + RefPtr<widget::nsWaylandDisplay> waylandDisplay = widget::WaylandDisplayGet(); + wl_compositor* compositor = waylandDisplay->GetCompositor(); + mWlSurface = wl_compositor_create_surface(compositor); + + wl_region* region = wl_compositor_create_region(compositor); + wl_surface_set_input_region(mWlSurface, region); + wl_region_destroy(region); + + wp_viewporter* viewporter = waylandDisplay->GetViewporter(); + mViewport = wp_viewporter_get_viewport(viewporter, mWlSurface); +} + +NativeLayerWayland::NativeLayerWayland(bool aIsOpaque) + : mMutex("NativeLayerWayland"), + mSurfacePoolHandle(nullptr), + mIsOpaque(aIsOpaque) { + MOZ_RELEASE_ASSERT(false); // external image +} + +NativeLayerWayland::~NativeLayerWayland() { + MutexAutoLock lock(mMutex); + + if (mInProgressBuffer) { + mSurfacePoolHandle->ReturnBufferToPool(mInProgressBuffer); + mInProgressBuffer = nullptr; + } + if (mFrontBuffer) { + mSurfacePoolHandle->ReturnBufferToPool(mFrontBuffer); + mFrontBuffer = nullptr; + } + MozClearPointer(mCallback, wl_callback_destroy); + MozClearPointer(mViewport, wp_viewport_destroy); + MozClearPointer(mWlSubsurface, wl_subsurface_destroy); + MozClearPointer(mWlSurface, wl_surface_destroy); +} + +void NativeLayerWayland::AttachExternalImage( + wr::RenderTextureHost* aExternalImage) { + MOZ_RELEASE_ASSERT(false); +} + +void NativeLayerWayland::SetSurfaceIsFlipped(bool aIsFlipped) { + MutexAutoLock lock(mMutex); + + if (aIsFlipped != mSurfaceIsFlipped) { + mSurfaceIsFlipped = aIsFlipped; + } +} + +bool NativeLayerWayland::SurfaceIsFlipped() { + MutexAutoLock lock(mMutex); + + return mSurfaceIsFlipped; +} + +IntSize NativeLayerWayland::GetSize() { + MutexAutoLock lock(mMutex); + return mSize; +} + +void NativeLayerWayland::SetPosition(const IntPoint& aPosition) { + MutexAutoLock lock(mMutex); + + if (aPosition != mPosition) { + mPosition = aPosition; + } +} + +IntPoint NativeLayerWayland::GetPosition() { + MutexAutoLock lock(mMutex); + return mPosition; +} + +void NativeLayerWayland::SetTransform(const Matrix4x4& aTransform) { + MutexAutoLock lock(mMutex); + MOZ_ASSERT(aTransform.IsRectilinear()); + + if (aTransform != mTransform) { + mTransform = aTransform; + } +} + +void NativeLayerWayland::SetSamplingFilter(SamplingFilter aSamplingFilter) { + MutexAutoLock lock(mMutex); + + if (aSamplingFilter != mSamplingFilter) { + mSamplingFilter = aSamplingFilter; + } +} + +Matrix4x4 NativeLayerWayland::GetTransform() { + MutexAutoLock lock(mMutex); + return mTransform; +} + +IntRect NativeLayerWayland::GetRect() { + MutexAutoLock lock(mMutex); + return IntRect(mPosition, mSize); +} + +bool NativeLayerWayland::IsOpaque() { + MutexAutoLock lock(mMutex); + return mIsOpaque; +} + +void NativeLayerWayland::SetClipRect(const Maybe<IntRect>& aClipRect) { + MutexAutoLock lock(mMutex); + + if (aClipRect != mClipRect) { + mClipRect = aClipRect; + } +} + +Maybe<IntRect> NativeLayerWayland::ClipRect() { + MutexAutoLock lock(mMutex); + return mClipRect; +} + +IntRect NativeLayerWayland::CurrentSurfaceDisplayRect() { + MutexAutoLock lock(mMutex); + return mDisplayRect; +} + +RefPtr<DrawTarget> NativeLayerWayland::NextSurfaceAsDrawTarget( + const IntRect& aDisplayRect, const IntRegion& aUpdateRegion, + BackendType aBackendType) { + MutexAutoLock lock(mMutex); + + mDisplayRect = IntRect(aDisplayRect); + mDirtyRegion = IntRegion(aUpdateRegion); + + MOZ_ASSERT(!mInProgressBuffer); + if (mFrontBuffer && !mFrontBuffer->IsAttached()) { + // the Wayland compositor released the buffer early, we can reuse it + mInProgressBuffer = std::move(mFrontBuffer); + } else { + mInProgressBuffer = mSurfacePoolHandle->ObtainBufferFromPool(mSize); + if (mFrontBuffer) { + HandlePartialUpdate(lock); + mSurfacePoolHandle->ReturnBufferToPool(mFrontBuffer); + } + } + mFrontBuffer = nullptr; + + if (!mInProgressBuffer) { + gfxCriticalError() << "Failed to obtain buffer"; + wr::RenderThread::Get()->HandleWebRenderError( + wr::WebRenderError::NEW_SURFACE); + return nullptr; + } + + return mInProgressBuffer->Lock(); +} + +Maybe<GLuint> NativeLayerWayland::NextSurfaceAsFramebuffer( + const IntRect& aDisplayRect, const IntRegion& aUpdateRegion, + bool aNeedsDepth) { + MutexAutoLock lock(mMutex); + + mDisplayRect = IntRect(aDisplayRect); + mDirtyRegion = IntRegion(aUpdateRegion); + + MOZ_ASSERT(!mInProgressBuffer); + if (mFrontBuffer && !mFrontBuffer->IsAttached()) { + // the Wayland compositor released the buffer early, we can reuse it + mInProgressBuffer = std::move(mFrontBuffer); + mFrontBuffer = nullptr; + } else { + mInProgressBuffer = mSurfacePoolHandle->ObtainBufferFromPool(mSize); + } + + if (!mInProgressBuffer) { + gfxCriticalError() << "Failed to obtain buffer"; + wr::RenderThread::Get()->HandleWebRenderError( + wr::WebRenderError::NEW_SURFACE); + return Nothing(); + } + + // get the framebuffer before handling partial damage so we don't accidently + // create one without depth buffer + Maybe<GLuint> fbo = mSurfacePoolHandle->GetFramebufferForBuffer( + mInProgressBuffer, aNeedsDepth); + MOZ_RELEASE_ASSERT(fbo, "GetFramebufferForBuffer failed."); + + if (mFrontBuffer) { + HandlePartialUpdate(lock); + mSurfacePoolHandle->ReturnBufferToPool(mFrontBuffer); + mFrontBuffer = nullptr; + } + + return fbo; +} + +void NativeLayerWayland::HandlePartialUpdate( + const MutexAutoLock& aProofOfLock) { + IntRegion copyRegion = IntRegion(mDisplayRect); + copyRegion.SubOut(mDirtyRegion); + + if (!copyRegion.IsEmpty()) { + if (mSurfacePoolHandle->gl()) { + mSurfacePoolHandle->gl()->MakeCurrent(); + for (auto iter = copyRegion.RectIter(); !iter.Done(); iter.Next()) { + gfx::IntRect r = iter.Get(); + Maybe<GLuint> sourceFB = + mSurfacePoolHandle->GetFramebufferForBuffer(mFrontBuffer, false); + Maybe<GLuint> destFB = mSurfacePoolHandle->GetFramebufferForBuffer( + mInProgressBuffer, false); + MOZ_RELEASE_ASSERT(sourceFB && destFB); + mSurfacePoolHandle->gl()->BlitHelper()->BlitFramebufferToFramebuffer( + sourceFB.value(), destFB.value(), r, r, LOCAL_GL_NEAREST); + } + } else { + RefPtr<gfx::DataSourceSurface> dataSourceSurface = + gfx::CreateDataSourceSurfaceFromData( + mSize, mFrontBuffer->GetSurfaceFormat(), + (const uint8_t*)mFrontBuffer->GetImageData(), + mSize.width * BytesPerPixel(mFrontBuffer->GetSurfaceFormat())); + RefPtr<DrawTarget> dt = mInProgressBuffer->Lock(); + + for (auto iter = copyRegion.RectIter(); !iter.Done(); iter.Next()) { + IntRect r = iter.Get(); + dt->CopySurface(dataSourceSurface, r, IntPoint(r.x, r.y)); + } + } + } +} + +void NativeLayerWayland::NotifySurfaceReady() { + MOZ_ASSERT(!mFrontBuffer); + MOZ_ASSERT(mInProgressBuffer); + mFrontBuffer = mInProgressBuffer; + mInProgressBuffer = nullptr; +} + +void NativeLayerWayland::DiscardBackbuffers() {} + +void NativeLayerWayland::Commit() { + MutexAutoLock lock(mMutex); + + if (mDirtyRegion.IsEmpty() && mHasBufferAttached) { + wl_surface_commit(mWlSurface); + return; + } + + for (auto iter = mDirtyRegion.RectIter(); !iter.Done(); iter.Next()) { + IntRect r = iter.Get(); + wl_surface_damage_buffer(mWlSurface, r.x, r.y, r.width, r.height); + } + + MOZ_ASSERT(mFrontBuffer); + mFrontBuffer->AttachAndCommit(mWlSurface); + mHasBufferAttached = true; + mDirtyRegion.SetEmpty(); +} + +void NativeLayerWayland::Unmap() { + MutexAutoLock lock(mMutex); + + if (!mHasBufferAttached) { + return; + } + + wl_surface_attach(mWlSurface, nullptr, 0, 0); + wl_surface_commit(mWlSurface); + mHasBufferAttached = false; +} + +void NativeLayerWayland::EnsureParentSurface(wl_surface* aParentSurface) { + MutexAutoLock lock(mMutex); + + if (aParentSurface != mParentWlSurface) { + MozClearPointer(mWlSubsurface, wl_subsurface_destroy); + mSubsurfacePosition = IntPoint(0, 0); + + if (aParentSurface) { + wl_subcompositor* subcompositor = + widget::WaylandDisplayGet()->GetSubcompositor(); + mWlSubsurface = wl_subcompositor_get_subsurface(subcompositor, mWlSurface, + aParentSurface); + } + mParentWlSurface = aParentSurface; + } +} + +void NativeLayerWayland::SetBufferTransformFlipped(bool aFlippedX, + bool aFlippedY) { + MutexAutoLock lock(mMutex); + + if (aFlippedX == mBufferTransformFlippedX && + aFlippedY == mBufferTransformFlippedY) { + return; + } + + mBufferTransformFlippedX = aFlippedX; + mBufferTransformFlippedY = aFlippedY; + + if (mBufferTransformFlippedY) { + if (mBufferTransformFlippedX) { + wl_surface_set_buffer_transform(mWlSurface, WL_OUTPUT_TRANSFORM_180); + } else { + wl_surface_set_buffer_transform(mWlSurface, + WL_OUTPUT_TRANSFORM_FLIPPED_180); + } + } else { + if (mBufferTransformFlippedX) { + wl_surface_set_buffer_transform(mWlSurface, WL_OUTPUT_TRANSFORM_FLIPPED); + } else { + wl_surface_set_buffer_transform(mWlSurface, WL_OUTPUT_TRANSFORM_NORMAL); + } + } +} + +void NativeLayerWayland::SetSubsurfacePosition(int aX, int aY) { + MutexAutoLock lock(mMutex); + + if ((aX == mSubsurfacePosition.x && aY == mSubsurfacePosition.y) || + !mWlSubsurface) { + return; + } + + mSubsurfacePosition.x = aX; + mSubsurfacePosition.y = aY; + wl_subsurface_set_position(mWlSubsurface, mSubsurfacePosition.x, + mSubsurfacePosition.y); +} + +void NativeLayerWayland::SetViewportSourceRect(const Rect aSourceRect) { + MutexAutoLock lock(mMutex); + + Rect bufferRect = Rect(0, 0, mSize.width, mSize.height); + Rect sourceRect = aSourceRect.Intersect(bufferRect); + + if (mViewportSourceRect == sourceRect) { + return; + } + + mViewportSourceRect = sourceRect; + wp_viewport_set_source(mViewport, wl_fixed_from_double(mViewportSourceRect.x), + wl_fixed_from_double(mViewportSourceRect.y), + wl_fixed_from_double(mViewportSourceRect.width), + wl_fixed_from_double(mViewportSourceRect.height)); +} + +void NativeLayerWayland::SetViewportDestinationSize(int aWidth, int aHeight) { + MutexAutoLock lock(mMutex); + + if (aWidth == mViewportDestinationSize.width && + aHeight == mViewportDestinationSize.height) { + return; + } + + mViewportDestinationSize.width = aWidth; + mViewportDestinationSize.height = aHeight; + wp_viewport_set_destination(mViewport, mViewportDestinationSize.width, + mViewportDestinationSize.height); +} + +void NativeLayerWayland::RequestFrameCallback( + const RefPtr<CallbackMultiplexHelper>& aMultiplexHelper) { + MutexAutoLock lock(mMutex); + MOZ_ASSERT(aMultiplexHelper->IsActive()); + + // Avoid piling up old helpers if this surface does not receive callbacks + // for a longer time + mCallbackMultiplexHelpers.RemoveElementsBy( + [&](const auto& object) { return !object->IsActive(); }); + + mCallbackMultiplexHelpers.AppendElement(aMultiplexHelper); + if (!mCallback) { + mCallback = wl_surface_frame(mWlSurface); + wl_callback_add_listener(mCallback, &sFrameListenerNativeLayerWayland, + this); + wl_surface_commit(mWlSurface); + } +} + +void NativeLayerWayland::FrameCallbackHandler(wl_callback* aCallback, + uint32_t aTime) { + MutexAutoLock lock(mMutex); + + MOZ_RELEASE_ASSERT(aCallback == mCallback); + MozClearPointer(mCallback, wl_callback_destroy); + + for (const RefPtr<CallbackMultiplexHelper>& callbackMultiplexHelper : + mCallbackMultiplexHelpers) { + callbackMultiplexHelper->Callback(aTime); + } + mCallbackMultiplexHelpers.Clear(); +} + +/* static */ +void NativeLayerWayland::FrameCallbackHandler(void* aData, + wl_callback* aCallback, + uint32_t aTime) { + auto surface = reinterpret_cast<NativeLayerWayland*>(aData); + surface->FrameCallbackHandler(aCallback, aTime); +} + +} // namespace mozilla::layers |