337 lines
12 KiB
Diff
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);
|