firefox/mozilla-1441743.patch
2018-07-17 08:56:42 +02:00

337 lines
12 KiB
Diff

diff --git a/widget/gtk/WindowSurfaceWayland.h b/widget/gtk/WindowSurfaceWayland.h
--- a/widget/gtk/WindowSurfaceWayland.h
+++ b/widget/gtk/WindowSurfaceWayland.h
@@ -3,16 +3,17 @@
* 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 "mozilla/gfx/Types.h"
namespace mozilla {
namespace widget {
// Our general connection to Wayland display server,
// holds our display connection and runs event loop.
class nsWaylandDisplay : public nsISupports {
NS_DECL_THREADSAFE_ISUPPORTS
@@ -61,17 +62,17 @@ private:
};
// Holds actual graphics data for wl_surface
class WindowBackBuffer {
public:
WindowBackBuffer(nsWaylandDisplay* aDisplay, int aWidth, int aHeight);
~WindowBackBuffer();
- already_AddRefed<gfx::DrawTarget> Lock(const LayoutDeviceIntRegion& aRegion);
+ already_AddRefed<gfx::DrawTarget> Lock();
void Attach(wl_surface* aSurface);
void Detach();
bool IsAttached() { return mAttached; }
bool Resize(int aWidth, int aHeight);
bool SetImageDataFromBackBuffer(class WindowBackBuffer* aSourceBuffer);
@@ -107,27 +108,33 @@ public:
WindowSurfaceWayland(nsWindow *aWindow);
~WindowSurfaceWayland();
already_AddRefed<gfx::DrawTarget> Lock(const LayoutDeviceIntRegion& aRegion) override;
void Commit(const LayoutDeviceIntRegion& aInvalidRegion) final;
void FrameCallbackHandler();
private:
- WindowBackBuffer* GetBufferToDraw(int aWidth, int aHeight);
+ WindowBackBuffer* GetFrontBufferToDraw(int aWidth, int aHeight);
void UpdateScaleFactor();
+ already_AddRefed<gfx::DrawTarget> LockFrontBuffer(int aWidth, int aHeight);
+ already_AddRefed<gfx::DrawTarget> LockImageSurface(const gfx::IntSize& aLockSize);
+ bool CommitImageSurface(const LayoutDeviceIntRegion& aRegion);
+
// TODO: Do we need to hold a reference to nsWindow object?
nsWindow* mWindow;
nsWaylandDisplay* mWaylandDisplay;
WindowBackBuffer* mFrontBuffer;
WindowBackBuffer* mBackBuffer;
+ RefPtr<gfxImageSurface> mImageSurface;
wl_callback* mFrameCallback;
wl_surface* mFrameCallbackSurface;
MessageLoop* mDisplayThreadMessageLoop;
+ bool mDirectWlBufferDraw;
bool mDelayedCommit;
bool mFullScreenDamage;
bool mIsMainThread;
};
} // namespace widget
} // namespace mozilla
diff --git a/widget/gtk/WindowSurfaceWayland.cpp b/widget/gtk/WindowSurfaceWayland.cpp
--- a/widget/gtk/WindowSurfaceWayland.cpp
+++ b/widget/gtk/WindowSurfaceWayland.cpp
@@ -299,16 +299,17 @@ nsWaylandDisplay::Matches(wl_display *aD
}
NS_IMPL_ISUPPORTS(nsWaylandDisplay, nsISupports);
nsWaylandDisplay::nsWaylandDisplay(wl_display *aDisplay)
: mThreadId(PR_GetCurrentThread())
// gfx::SurfaceFormat::B8G8R8A8 is a basic Wayland format
// and is always present.
+ // TODO: Provide also format without alpha (Bug 1470126).
, mFormat(gfx::SurfaceFormat::B8G8R8A8)
, mShm(nullptr)
, mDisplay(aDisplay)
{
if (NS_IsMainThread()) {
// Use default event queue in main thread operated by Gtk+.
mEventQueue = nullptr;
} else {
@@ -530,21 +531,19 @@ WindowBackBuffer::SetImageDataFromBackBu
}
mShmPool.SetImageDataFromPool(&aSourceBuffer->mShmPool,
aSourceBuffer->mWidth * aSourceBuffer->mHeight * BUFFER_BPP);
return true;
}
already_AddRefed<gfx::DrawTarget>
-WindowBackBuffer::Lock(const LayoutDeviceIntRegion& aRegion)
+WindowBackBuffer::Lock()
{
- gfx::IntRect bounds = aRegion.GetBounds().ToUnknownRect();
- gfx::IntSize lockSize(bounds.XMost(), bounds.YMost());
-
+ gfx::IntSize lockSize(mWidth, mHeight);
return gfxPlatform::CreateDrawTargetForData(static_cast<unsigned char*>(mShmPool.GetImageData()),
lockSize,
BUFFER_BPP * mWidth,
mWaylandDisplay->GetSurfaceFormat());
}
static void
frame_callback_handler(void *data, struct wl_callback *callback, uint32_t time)
@@ -560,16 +559,17 @@ static const struct wl_callback_listener
WindowSurfaceWayland::WindowSurfaceWayland(nsWindow *aWindow)
: mWindow(aWindow)
, mWaylandDisplay(WaylandDisplayGet(aWindow->GetWaylandDisplay()))
, mFrontBuffer(nullptr)
, mBackBuffer(nullptr)
, mFrameCallback(nullptr)
, mFrameCallbackSurface(nullptr)
, mDisplayThreadMessageLoop(MessageLoop::current())
+ , mDirectWlBufferDraw(true)
, mDelayedCommit(false)
, mFullScreenDamage(false)
, mIsMainThread(NS_IsMainThread())
{
}
WindowSurfaceWayland::~WindowSurfaceWayland()
{
@@ -598,17 +598,17 @@ WindowSurfaceWayland::UpdateScaleFactor(
{
wl_surface* waylandSurface = mWindow->GetWaylandSurface();
if (waylandSurface) {
wl_surface_set_buffer_scale(waylandSurface, mWindow->GdkScaleFactor());
}
}
WindowBackBuffer*
-WindowSurfaceWayland::GetBufferToDraw(int aWidth, int aHeight)
+WindowSurfaceWayland::GetFrontBufferToDraw(int aWidth, int aHeight)
{
if (!mFrontBuffer) {
mFrontBuffer = new WindowBackBuffer(mWaylandDisplay, aWidth, aHeight);
mBackBuffer = new WindowBackBuffer(mWaylandDisplay, aWidth, aHeight);
return mFrontBuffer;
}
if (!mFrontBuffer->IsAttached()) {
@@ -647,46 +647,149 @@ WindowSurfaceWayland::GetBufferToDraw(in
// the new buffer and leave gecko to render new whole content.
mFrontBuffer->Resize(aWidth, aHeight);
}
return mFrontBuffer;
}
already_AddRefed<gfx::DrawTarget>
+WindowSurfaceWayland::LockFrontBuffer(int aWidth, int aHeight)
+{
+ WindowBackBuffer* buffer = GetFrontBufferToDraw(aWidth, aHeight);
+ if (buffer) {
+ return buffer->Lock();
+ }
+
+ NS_WARNING("WindowSurfaceWayland::LockFrontBuffer(): No buffer available");
+ return nullptr;
+}
+
+already_AddRefed<gfx::DrawTarget>
+WindowSurfaceWayland::LockImageSurface(const gfx::IntSize& aLockSize)
+{
+ if (!mImageSurface || mImageSurface->CairoStatus() ||
+ !(aLockSize <= mImageSurface->GetSize())) {
+ mImageSurface = new gfxImageSurface(aLockSize,
+ SurfaceFormatToImageFormat(mWaylandDisplay->GetSurfaceFormat()));
+ if (mImageSurface->CairoStatus()) {
+ return nullptr;
+ }
+ }
+
+ return gfxPlatform::CreateDrawTargetForData(mImageSurface->Data(),
+ mImageSurface->GetSize(),
+ mImageSurface->Stride(),
+ mWaylandDisplay->GetSurfaceFormat());
+}
+
+/*
+ There are some situations which can happen here:
+
+ A) Lock() is called to whole surface. In that case we don't need
+ to clip/buffer the drawing and we can return wl_buffer directly
+ for drawing.
+ - mFrontBuffer is available - that's an ideal situation.
+ - mFrontBuffer is locked by compositor - flip buffers and draw.
+ - if we can't flip buffers - go B)
+
+ B) Lock() is requested for part(s) of screen. We need to provide temporary
+ surface to draw into and copy result (clipped) to target wl_surface.
+ */
+already_AddRefed<gfx::DrawTarget>
WindowSurfaceWayland::Lock(const LayoutDeviceIntRegion& aRegion)
{
MOZ_ASSERT(mIsMainThread == NS_IsMainThread());
- // We allocate back buffer to widget size but return only
- // portion requested by aRegion.
- LayoutDeviceIntRect rect = mWindow->GetBounds();
- WindowBackBuffer* buffer = GetBufferToDraw(rect.width,
- rect.height);
- if (!buffer) {
- NS_WARNING("No drawing buffer available");
- return nullptr;
+ LayoutDeviceIntRect screenRect = mWindow->GetBounds();
+ gfx::IntRect bounds = aRegion.GetBounds().ToUnknownRect();
+ gfx::IntSize lockSize(bounds.XMost(), bounds.YMost());
+
+ // Are we asked for entire nsWindow to draw?
+ mDirectWlBufferDraw = (aRegion.GetNumRects() == 1 &&
+ bounds.x == 0 && bounds.y == 0 &&
+ lockSize.width == screenRect.width &&
+ lockSize.height == screenRect.height);
+
+ if (mDirectWlBufferDraw) {
+ RefPtr<gfx::DrawTarget> dt = LockFrontBuffer(screenRect.width,
+ screenRect.height);
+ if (dt) {
+ return dt.forget();
+ }
+
+ // We don't have any front buffer available. Try indirect drawing
+ // to mImageSurface which is mirrored to front buffer at commit.
+ mDirectWlBufferDraw = false;
}
- return buffer->Lock(aRegion);
+ return LockImageSurface(lockSize);
+}
+
+bool
+WindowSurfaceWayland::CommitImageSurface(const LayoutDeviceIntRegion& aRegion)
+{
+ MOZ_ASSERT(!mDirectWlBufferDraw);
+
+ LayoutDeviceIntRect screenRect = mWindow->GetBounds();
+ gfx::IntRect bounds = aRegion.GetBounds().ToUnknownRect();
+
+ gfx::Rect rect(bounds);
+ if (rect.IsEmpty()) {
+ return false;
+ }
+
+ RefPtr<gfx::DrawTarget> dt = LockFrontBuffer(screenRect.width,
+ screenRect.height);
+ RefPtr<gfx::SourceSurface> surf =
+ gfx::Factory::CreateSourceSurfaceForCairoSurface(mImageSurface->CairoSurface(),
+ mImageSurface->GetSize(),
+ mImageSurface->Format());
+ if (!dt || !surf) {
+ return false;
+ }
+
+ uint32_t numRects = aRegion.GetNumRects();
+ if (numRects != 1) {
+ AutoTArray<IntRect, 32> rects;
+ rects.SetCapacity(numRects);
+ for (auto iter = aRegion.RectIter(); !iter.Done(); iter.Next()) {
+ rects.AppendElement(iter.Get().ToUnknownRect());
+ }
+ dt->PushDeviceSpaceClipRects(rects.Elements(), rects.Length());
+ }
+
+ dt->DrawSurface(surf, rect, rect);
+
+ if (numRects != 1) {
+ dt->PopClip();
+ }
+
+ return true;
}
void
WindowSurfaceWayland::Commit(const LayoutDeviceIntRegion& aInvalidRegion)
{
MOZ_ASSERT(mIsMainThread == NS_IsMainThread());
wl_surface* waylandSurface = mWindow->GetWaylandSurface();
if (!waylandSurface) {
// Target window is already destroyed - don't bother to render there.
+ NS_WARNING("WindowSurfaceWayland::Commit(): parent wl_surface is already hidden/deleted.");
return;
}
wl_proxy_set_queue((struct wl_proxy *)waylandSurface,
mWaylandDisplay->GetEventQueue());
+ if (!mDirectWlBufferDraw) {
+ // We have new content at mImageSurface - copy data to mFrontBuffer first.
+ CommitImageSurface(aInvalidRegion);
+ }
+
if (mFullScreenDamage) {
LayoutDeviceIntRect rect = mWindow->GetBounds();
wl_surface_damage(waylandSurface, 0, 0, rect.width, rect.height);
mFullScreenDamage = false;
} else {
for (auto iter = aInvalidRegion.RectIter(); !iter.Done(); iter.Next()) {
const mozilla::LayoutDeviceIntRect &r = iter.Get();
wl_surface_damage(waylandSurface, r.x, r.y, r.width, r.height);
@@ -730,17 +833,17 @@ WindowSurfaceWayland::FrameCallbackHandl
mFrameCallback = nullptr;
mFrameCallbackSurface = nullptr;
}
if (mDelayedCommit) {
wl_surface* waylandSurface = mWindow->GetWaylandSurface();
if (!waylandSurface) {
// Target window is already destroyed - don't bother to render there.
- NS_WARNING("No drawing buffer available");
+ NS_WARNING("WindowSurfaceWayland::FrameCallbackHandler(): parent wl_surface is already hidden/deleted.");
return;
}
wl_proxy_set_queue((struct wl_proxy *)waylandSurface,
mWaylandDisplay->GetEventQueue());
// Send pending surface to compositor and register frame callback
// for possible subsequent drawing.
mFrameCallback = wl_surface_frame(waylandSurface);