mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 05:11:16 +00:00
48aa55faf4
This was originally implemented in D111662 to work around a crash that otherwise would get triggered in Gnome/Mutter. The fix for that crash has been available for a couple of month now, thus remove the workaround again. This may help preventing flickering for menus in certain situations. Differential Revision: https://phabricator.services.mozilla.com/D137251
761 lines
24 KiB
C++
761 lines
24 KiB
C++
/* 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 "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;
|
|
|
|
wl_surface* containerSurface = moz_container_wayland_surface_lock(mContainer);
|
|
if (!containerSurface) {
|
|
if (!mCallbackRequested) {
|
|
RefPtr<NativeLayerRootWayland> self(this);
|
|
moz_container_wayland_add_initial_draw_callback(
|
|
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);
|
|
}
|
|
|
|
moz_container_wayland_surface_unlock(mContainer, &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);
|
|
}
|
|
|
|
wl_surface* wlSurface = moz_container_wayland_surface_lock(mContainer);
|
|
if (wlSurface) {
|
|
wl_surface_commit(wlSurface);
|
|
wl_display_flush(widget::WaylandDisplayGet()->GetDisplay());
|
|
moz_container_wayland_surface_unlock(mContainer, &wlSurface);
|
|
}
|
|
}
|
|
|
|
static void sAfterFrameClockAfterPaint(
|
|
GdkFrameClock* aClock, NativeLayerRootWayland* aNativeLayerRoot) {
|
|
aNativeLayerRoot->AfterFrameClockAfterPaint();
|
|
}
|
|
|
|
void NativeLayerRootWayland::AfterFrameClockAfterPaint() {
|
|
MutexAutoLock lock(mMutex);
|
|
wl_surface* containerSurface = moz_container_wayland_surface_lock(mContainer);
|
|
|
|
for (const RefPtr<NativeLayerWayland>& layer : mSublayersOnMainThread) {
|
|
wl_surface_commit(layer->mWlSurface);
|
|
}
|
|
if (containerSurface) {
|
|
wl_surface_commit(containerSurface);
|
|
moz_container_wayland_surface_unlock(mContainer, &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");
|
|
|
|
wl_surface* containerSurface = moz_container_wayland_surface_lock(mContainer);
|
|
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);
|
|
moz_container_wayland_surface_unlock(mContainer, &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;
|
|
}
|
|
g_clear_pointer(&mCallback, wl_callback_destroy);
|
|
g_clear_pointer(&mViewport, wp_viewport_destroy);
|
|
g_clear_pointer(&mWlSubsurface, wl_subsurface_destroy);
|
|
g_clear_pointer(&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) {
|
|
g_clear_pointer(&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);
|
|
g_clear_pointer(&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
|