Implement vsync notification for remote compositors. (bug 1285625 part 3, r=mchang)

This commit is contained in:
David Anderson 2016-07-19 11:56:07 -07:00
parent 0bdcce6956
commit 969d03ec34
30 changed files with 425 additions and 104 deletions

View File

@ -13,11 +13,12 @@ The flow of our rendering engine is as follows:
1. Hardware Vsync event occurs on an OS specific *Hardware Vsync Thread* on a per monitor basis.
2. The *Hardware Vsync Thread* attached to the monitor notifies the **CompositorVsyncDispatchers** and **RefreshTimerVsyncDispatcher**.
3. For every Firefox window on the specific monitor, notify a **CompositorVsyncDispatcher**. The **CompositorVsyncDispatcher** is specific to one window.
4. The **CompositorVsyncDispatcher** notifies the **Compositor** that a vsync has occured.
5. The **RefreshTimerVsyncDispatcher** notifies the Chrome **RefreshTimer** that a vsync has occured.
6. The **RefreshTimerVsyncDispatcher** sends IPC messages to all content processes to tick their respective active **RefreshTimer**.
7. The **Compositor** dispatches input events on the *Compositor Thread*, then composites. Input events are only dispatched on the *Compositor Thread* on b2g.
8. The **RefreshDriver** paints on the *Main Thread*.
4. The **CompositorVsyncDispatcher** notifies a **CompositorWidgetVsyncObserver** when remote compositing, or a **CompositorVsyncScheduler::Observer** when compositing in-process.
5. If remote compositing, a vsync notification is sent from the **CompositorWidgetVsyncObserver** to the **VsyncBridgeChild** on the UI process, which sends an IPDL message to the **VsyncBridgeParent** on the compositor thread of the GPU process, which then dispatches to **CompositorVsyncScheduler::Observer**.
6. The **RefreshTimerVsyncDispatcher** notifies the Chrome **RefreshTimer** that a vsync has occured.
7. The **RefreshTimerVsyncDispatcher** sends IPC messages to all content processes to tick their respective active **RefreshTimer**.
8. The **Compositor** dispatches input events on the *Compositor Thread*, then composites. Input events are only dispatched on the *Compositor Thread* on b2g.
9. The **RefreshDriver** paints on the *Main Thread*.
The implementation is broken into the following sections and will reference this figure. Note that **Objects** are bold fonts while *Threads* are italicized.
@ -52,15 +53,29 @@ On OS X, this is through **CVDisplayLinkRef**.
On Windows, it should be through **DwmGetCompositionTimingInfo**.
#Compositor
When the **CompositorVsyncDispatcher** is notified of the vsync event, the **CompositorVsyncObserver** associated with the **CompositorVsyncDispatcher** begins execution.
Since the **CompositorVsyncDispatcher** executes on the *Hardware Vsync Thread* and the **Compositor** composites on the *CompositorThread*, the **CompositorVsyncObserver** posts a task to the *CompositorThread*.
When the **CompositorVsyncDispatcher** is notified of the vsync event, the **CompositorVsyncScheduler::Observer** associated with the **CompositorVsyncDispatcher** begins execution.
Since the **CompositorVsyncDispatcher** executes on the *Hardware Vsync Thread* and the **Compositor** composites on the *CompositorThread*, the **CompositorVsyncScheduler::Observer** posts a task to the *CompositorThread*.
The **CompositorBridgeParent** then composites.
The model where the **CompositorVsyncDispatcher** notifies components on the *Hardware Vsync Thread*, and the component schedules the task on the appropriate thread is used everywhere.
The **CompositorVsyncObserver** listens to vsync events as needed and stops listening to vsync when composites are no longer scheduled or required.
Every **CompositorBridgeParent** is associated and tied to one **CompositorVsyncObserver**, which is associated with the **CompositorVsyncDispatcher**.
The **CompositorVsyncScheduler::Observer** listens to vsync events as needed and stops listening to vsync when composites are no longer scheduled or required.
Every **CompositorBridgeParent** is associated and tied to one **CompositorVsyncScheduler::Observer**, which is associated with the **CompositorVsyncDispatcher**.
Each **CompositorBridgeParent** is associated with one widget and is created when a new platform window or **nsBaseWidget** is created.
The **CompositorBridgeParent**, **CompositorVsyncDispatcher**, **CompositorVsyncObserver**, and **nsBaseWidget** all have the same lifetimes, which are created and destroyed together.
The **CompositorBridgeParent**, **CompositorVsyncDispatcher**, **CompositorVsyncScheduler::Observer**, and **nsBaseWidget** all have the same lifetimes, which are created and destroyed together.
##Out-of-process Compositors
When compositing out-of-process, this model changes slightly.
In this case there are effectively two observers: a UI process observer (**CompositorWidgetVsyncObserver**), and the **CompositorVsyncScheduler::Observer** in the GPU process.
There are also two dispatchers: the widget dispatcher in the UI process (**CompositorVsyncDispatcher**), and the IPDL-based dispatcher in the GPU process (**CompositorBridgeParent::NotifyVsync**).
The UI process observer and the GPU process dispatcher are linked via an IPDL protocol called PVsyncBridge.
**PVsyncBridge** is a top-level protocol for sending vsync notifications to the compositor thread in the GPU process.
The compositor controls vsync observation through a separate actor, **PCompositorWidget**, which (as a subactor for **CompositorBridgeChild**) links the compositor thread in the GPU process to the main thread in the UI process.
Out-of-process compositors do not go through **CompositorVsyncDispatcher** directly.
Instead, the **CompositorWidgetDelegate** in the UI process creates one, and gives it a **CompositorWidgetVsyncObserver**.
This observer forwards notifications to a Vsync I/O thread, where **VsyncBridgeChild** then forwards the notification again to the compositor thread in the GPU process.
The notification is received by a **VsyncBridgeParent**.
The GPU process uses the layers ID in the notification to find the correct compositor to dispatch the notification to.
###CompositorVsyncDispatcher
The **CompositorVsyncDispatcher** executes on the *Hardware Vsync Thread*.
@ -69,6 +84,10 @@ The **CompositorVsyncDispatcher** is responsible for notifying the **CompositorB
There can be multiple **CompositorVsyncDispatchers** per **Display**, one **CompositorVsyncDispatcher** per window.
The only responsibility of the **CompositorVsyncDispatcher** is to notify components when a vsync event has occured, and to stop listening to vsync when no components require vsync events.
We require one **CompositorVsyncDispatcher** per window so that we can handle multiple **Displays**.
When compositing in-process, the **CompositorVsyncDispatcher** is attached to the CompositorWidget for the
window. When out-of-process, it is attached to the CompositorWidgetDelegate, which forwards
observer notifications over IPDL. In the latter case, its lifetime is tied to a CompositorSession
rather than the nsIWidget.
###Multiple Displays
The **VsyncSource** has an API to switch a **CompositorVsyncDispatcher** from one **Display** to another **Display**.
@ -80,14 +99,14 @@ The **CompositorVsyncDispatcher** then notifies the **VsyncSource** to switch to
Because the notification works through the **nsIWidget**, the actual switching of the **CompositorVsyncDispatcher** to the correct **Display** should occur on the *Main Thread*.
The current implementation of Silk does not handle this case and needs to be built out.
###CompositorVsyncObserver
The **CompositorVsyncObserver** handles the vsync notifications and interactions with the **CompositorVsyncDispatcher**.
When the **Compositor** requires a scheduled composite, it notifies the **CompositorVsyncObserver** that it needs to listen to vsync.
The **CompositorVsyncObserver** then observes / unobserves vsync as needed from the **CompositorVsyncDispatcher** to enable composites.
###CompositorVsyncScheduler::Observer
The **CompositorVsyncScheduler::Observer** handles the vsync notifications and interactions with the **CompositorVsyncDispatcher**.
When the **Compositor** requires a scheduled composite, it notifies the **CompositorVsyncScheduler::Observer** that it needs to listen to vsync.
The **CompositorVsyncScheduler::Observer** then observes / unobserves vsync as needed from the **CompositorVsyncDispatcher** to enable composites.
###GeckoTouchDispatcher
The **GeckoTouchDispatcher** is a singleton that resamples touch events to smooth out jank while tracking a user's finger.
Because input and composite are linked together, the **CompositorVsyncObserver** has a reference to the **GeckoTouchDispatcher** and vice versa.
Because input and composite are linked together, the **CompositorVsyncScheduler::Observer** has a reference to the **GeckoTouchDispatcher** and vice versa.
###Input Events
One large goal of Silk is to align touch events with vsync events.
@ -115,25 +134,25 @@ When the [nsBaseWidget shuts down](http://hg.mozilla.org/mozilla-central/file/0d
During nsBaseWidget::DestroyCompositor, it first destroys the CompositorBridgeChild.
CompositorBridgeChild sends a sync IPC call to CompositorBridgeParent::RecvStop, which calls [CompositorBridgeParent::Destroy](http://hg.mozilla.org/mozilla-central/file/ab0490972e1e/gfx/layers/ipc/CompositorBridgeParent.cpp#l509).
During this time, the *main thread* is blocked on the parent process.
CompositorBridgeParent::RecvStop runs on the *Compositor thread* and cleans up some resources, including setting the **CompositorVsyncObserver** to nullptr.
CompositorBridgeParent::RecvStop runs on the *Compositor thread* and cleans up some resources, including setting the **CompositorVsyncScheduler::Observer** to nullptr.
CompositorBridgeParent::RecvStop also explicitly keeps the CompositorBridgeParent alive and posts another task to run CompositorBridgeParent::DeferredDestroy on the Compositor loop so that all ipdl code can finish executing.
The **CompositorVsyncObserver** also unobserves from vsync and cancels any pending composite tasks.
The **CompositorVsyncScheduler::Observer** also unobserves from vsync and cancels any pending composite tasks.
Once CompositorBridgeParent::RecvStop finishes, the *main thread* in the parent process continues shutting down the nsBaseWidget.
At the same time, the *Compositor thread* is executing tasks until CompositorBridgeParent::DeferredDestroy runs, which flushes the compositor message loop.
Now we have two tasks as both the nsBaseWidget releases a reference to the Compositor on the *main thread* during destruction and the CompositorBridgeParent::DeferredDestroy releases a reference to the CompositorBridgeParent on the *Compositor Thread*.
Finally, the CompositorBridgeParent itself is destroyed on the *main thread* once both references are gone due to explicit [main thread destruction](http://hg.mozilla.org/mozilla-central/file/50b95032152c/gfx/layers/ipc/CompositorBridgeParent.h#l148).
With the **CompositorVsyncObserver**, any accesses to the widget after nsBaseWidget::DestroyCompositor executes are invalid.
Any accesses to the compositor between the time the nsBaseWidget::DestroyCompositor runs and the CompositorVsyncObserver's destructor runs aren't safe yet a hardware vsync event could occur between these times.
With the **CompositorVsyncScheduler::Observer**, any accesses to the widget after nsBaseWidget::DestroyCompositor executes are invalid.
Any accesses to the compositor between the time the nsBaseWidget::DestroyCompositor runs and the CompositorVsyncScheduler::Observer's destructor runs aren't safe yet a hardware vsync event could occur between these times.
Since any tasks posted on the Compositor loop after CompositorBridgeParent::DeferredDestroy is posted are invalid, we make sure that no vsync tasks can be posted once CompositorBridgeParent::RecvStop executes and DeferredDestroy is posted on the Compositor thread.
When the sync call to CompositorBridgeParent::RecvStop executes, we explicitly set the CompositorVsyncObserver to null to prevent vsync notifications from occurring.
If vsync notifications were allowed to occur, since the **CompositorVsyncObserver**'s vsync notification executes on the *hardware vsync thread*, it would post a task to the Compositor loop and may execute after CompositorBridgeParent::DeferredDestroy.
Thus, we explicitly shut down vsync events in the **CompositorVsyncDispatcher** and **CompositorVsyncObserver** during nsBaseWidget::Shutdown to prevent any vsync tasks from executing after CompositorBridgeParent::DeferredDestroy.
When the sync call to CompositorBridgeParent::RecvStop executes, we explicitly set the CompositorVsyncScheduler::Observer to null to prevent vsync notifications from occurring.
If vsync notifications were allowed to occur, since the **CompositorVsyncScheduler::Observer**'s vsync notification executes on the *hardware vsync thread*, it would post a task to the Compositor loop and may execute after CompositorBridgeParent::DeferredDestroy.
Thus, we explicitly shut down vsync events in the **CompositorVsyncDispatcher** and **CompositorVsyncScheduler::Observer** during nsBaseWidget::Shutdown to prevent any vsync tasks from executing after CompositorBridgeParent::DeferredDestroy.
The **CompositorVsyncDispatcher** may be destroyed on either the *main thread* or *Compositor Thread*, since both the nsBaseWidget and **CompositorVsyncObserver** race to destroy on different threads.
The **CompositorVsyncDispatcher** may be destroyed on either the *main thread* or *Compositor Thread*, since both the nsBaseWidget and **CompositorVsyncScheduler::Observer** race to destroy on different threads.
nsBaseWidget is destroyed on the *main thread* and releases a reference to the **CompositorVsyncDispatcher** during destruction.
The **CompositorVsyncObserver** has a race to be destroyed either during CompositorBridgeParent shutdown or from the **GeckoTouchDispatcher** which is destroyed on the main thread with [ClearOnShutdown](http://hg.mozilla.org/mozilla-central/file/21567e9a6e40/xpcom/base/ClearOnShutdown.h#l15).
The **CompositorVsyncScheduler::Observer** has a race to be destroyed either during CompositorBridgeParent shutdown or from the **GeckoTouchDispatcher** which is destroyed on the main thread with [ClearOnShutdown](http://hg.mozilla.org/mozilla-central/file/21567e9a6e40/xpcom/base/ClearOnShutdown.h#l15).
Whichever object, the CompositorBridgeParent or the **GeckoTouchDispatcher** is destroyed last will hold the last reference to the **CompositorVsyncDispatcher**, which destroys the object.
#Refresh Driver
@ -212,14 +231,14 @@ There is still only one **VsyncParent/VsyncChild** pair, just each vsync notific
#Object Lifetime
1. CompositorVsyncDispatcher - Lives as long as the nsBaseWidget associated with the VsyncDispatcher
2. CompositorVsyncObserver - Lives and dies the same time as the CompositorBridgeParent.
2. CompositorVsyncScheduler::Observer - Lives and dies the same time as the CompositorBridgeParent.
3. RefreshTimerVsyncDispatcher - As long as the associated display object, which is the lifetime of Firefox.
4. VsyncSource - Lives as long as the gfxPlatform on the chrome process, which is the lifetime of Firefox.
5. VsyncParent/VsyncChild - Lives as long as the content process
6. RefreshTimer - Lives as long as the process
#Threads
All **VsyncObservers** are notified on the *Hardware Vsync Thread*. It is the responsibility of the **VsyncObservers** to post tasks to their respective correct thread. For example, the **CompositorVsyncObserver** will be notified on the *Hardware Vsync Thread*, and post a task to the *Compositor Thread* to do the actual composition.
All **VsyncObservers** are notified on the *Hardware Vsync Thread*. It is the responsibility of the **VsyncObservers** to post tasks to their respective correct thread. For example, the **CompositorVsyncScheduler::Observer** will be notified on the *Hardware Vsync Thread*, and post a task to the *Compositor Thread* to do the actual composition.
1. Compositor Thread - Nothing changes
2. Main Thread - PVsyncChild receives IPC messages on the main thread. We also enable/disable vsync on the main thread.

View File

@ -0,0 +1,35 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=99: */
/* 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 "CompositorWidgetVsyncObserver.h"
#include "mozilla/gfx/VsyncBridgeChild.h"
namespace mozilla {
namespace widget {
CompositorWidgetVsyncObserver::CompositorWidgetVsyncObserver(
RefPtr<VsyncBridgeChild> aVsyncBridge,
const uint64_t& aRootLayerTreeId)
: mVsyncBridge(aVsyncBridge),
mRootLayerTreeId(aRootLayerTreeId)
{
MOZ_ASSERT(XRE_IsParentProcess());
MOZ_ASSERT(NS_IsMainThread());
}
bool
CompositorWidgetVsyncObserver::NotifyVsync(TimeStamp aTimeStamp)
{
// Vsync notifications should only arrive on the vsync thread.
MOZ_ASSERT(XRE_IsParentProcess());
MOZ_ASSERT(!NS_IsMainThread());
mVsyncBridge->NotifyVsync(aTimeStamp, mRootLayerTreeId);
return true;
}
} // namespace widget
} // namespace mozilla

View File

@ -0,0 +1,37 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=99: */
/* 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_gfx_ipc_CompositorWidgetVsyncObserver_h
#define mozilla_gfx_ipc_CompositorWidgetVsyncObserver_h
#include "mozilla/VsyncDispatcher.h"
namespace mozilla {
namespace gfx {
class VsyncBridgeChild;
} // namespace gfx
namespace widget {
class CompositorWidgetVsyncObserver : public VsyncObserver
{
typedef gfx::VsyncBridgeChild VsyncBridgeChild;
public:
CompositorWidgetVsyncObserver(RefPtr<VsyncBridgeChild> aVsyncBridge,
const uint64_t& aRootLayerTreeId);
bool NotifyVsync(TimeStamp aVsyncTimestamp) override;
private:
RefPtr<VsyncBridgeChild> mVsyncBridge;
uint64_t mRootLayerTreeId;
};
} // namespace widget
} // namespace mozilla
#endif // mozilla_gfx_ipc_CompositorWidgetVsyncObserver_h

View File

@ -13,6 +13,7 @@
#ifdef MOZ_WIDGET_SUPPORTS_OOP_COMPOSITING
# include "mozilla/widget/CompositorWidgetChild.h"
#endif
#include "nsBaseWidget.h"
#include "nsContentUtils.h"
#include "VsyncBridgeChild.h"
#include "VsyncIOThreadHolder.h"
@ -217,7 +218,7 @@ GPUProcessManager::DestroyProcess()
}
RefPtr<CompositorSession>
GPUProcessManager::CreateTopLevelCompositor(nsIWidget* aWidget,
GPUProcessManager::CreateTopLevelCompositor(nsBaseWidget* aWidget,
ClientLayerManager* aLayerManager,
CSSToLayoutDeviceScale aScale,
bool aUseAPZ,
@ -254,7 +255,7 @@ GPUProcessManager::CreateTopLevelCompositor(nsIWidget* aWidget,
}
RefPtr<CompositorSession>
GPUProcessManager::CreateRemoteSession(nsIWidget* aWidget,
GPUProcessManager::CreateRemoteSession(nsBaseWidget* aWidget,
ClientLayerManager* aLayerManager,
const uint64_t& aRootLayerTreeId,
CSSToLayoutDeviceScale aScale,
@ -296,7 +297,11 @@ GPUProcessManager::CreateRemoteSession(nsIWidget* aWidget,
if (!ok)
return nullptr;
CompositorWidgetChild* widget = new CompositorWidgetChild(aWidget);
RefPtr<CompositorVsyncDispatcher> dispatcher = aWidget->GetCompositorVsyncDispatcher();
RefPtr<CompositorWidgetVsyncObserver> observer =
new CompositorWidgetVsyncObserver(mVsyncBridge, aRootLayerTreeId);
CompositorWidgetChild* widget = new CompositorWidgetChild(dispatcher, observer);
if (!child->SendPCompositorWidgetConstructor(widget, initData))
return nullptr;
if (!child->SendInitialize(aRootLayerTreeId))

View File

@ -17,6 +17,8 @@
#include "mozilla/ipc/TaskFactory.h"
#include "mozilla/ipc/Transport.h"
#include "nsIObserverService.h"
#include "nsThreadUtils.h"
class nsBaseWidget;
namespace mozilla {
@ -71,7 +73,7 @@ public:
void EnsureGPUReady();
RefPtr<CompositorSession> CreateTopLevelCompositor(
nsIWidget* aWidget,
nsBaseWidget* aWidget,
ClientLayerManager* aLayerManager,
CSSToLayoutDeviceScale aScale,
bool aUseAPZ,
@ -142,7 +144,7 @@ private:
void ShutdownVsyncIOThread();
RefPtr<CompositorSession> CreateRemoteSession(
nsIWidget* aWidget,
nsBaseWidget* aWidget,
ClientLayerManager* aLayerManager,
const uint64_t& aRootLayerTreeId,
CSSToLayoutDeviceScale aScale,

View File

@ -15,7 +15,7 @@ namespace gfx {
sync protocol PVsyncBridge
{
parent:
async NotifyVsync(TimeStamp vsyncTimeStamp);
async NotifyVsync(TimeStamp vsyncTimeStamp, uint64_t layersId);
};
} // namespace gfx

View File

@ -51,10 +51,60 @@ VsyncBridgeChild::Open(Endpoint<PVsyncBridgeChild>&& aEndpoint)
AddRef();
}
class NotifyVsyncTask : public Runnable
{
public:
NotifyVsyncTask(RefPtr<VsyncBridgeChild> aVsyncBridge,
TimeStamp aTimeStamp,
const uint64_t& aLayersId)
: mVsyncBridge(aVsyncBridge),
mTimeStamp(aTimeStamp),
mLayersId(aLayersId)
{}
NS_IMETHOD Run() override {
mVsyncBridge->NotifyVsyncImpl(mTimeStamp, mLayersId);
return NS_OK;
}
private:
RefPtr<VsyncBridgeChild> mVsyncBridge;
TimeStamp mTimeStamp;
uint64_t mLayersId;
};
bool
VsyncBridgeChild::IsOnVsyncIOThread() const
{
return MessageLoop::current() == mLoop;
}
void
VsyncBridgeChild::NotifyVsync(TimeStamp aTimeStamp, const uint64_t& aLayersId)
{
// This should be on the Vsync thread (not the Vsync I/O thread).
MOZ_ASSERT(!IsOnVsyncIOThread());
RefPtr<NotifyVsyncTask> task = new NotifyVsyncTask(this, aTimeStamp, aLayersId);
mLoop->PostTask(task.forget());
}
void
VsyncBridgeChild::NotifyVsyncImpl(TimeStamp aTimeStamp, const uint64_t& aLayersId)
{
// This should be on the Vsync I/O thread.
MOZ_ASSERT(IsOnVsyncIOThread());
if (!mProcessToken) {
return;
}
SendNotifyVsync(aTimeStamp, aLayersId);
}
void
VsyncBridgeChild::Close()
{
if (MessageLoop::current() != mLoop) {
if (!IsOnVsyncIOThread()) {
mLoop->PostTask(NewRunnableMethod(this, &VsyncBridgeChild::Close));
return;
}

View File

@ -16,6 +16,8 @@ class VsyncIOThreadHolder;
class VsyncBridgeChild final : public PVsyncBridgeChild
{
friend class NotifyVsyncTask;
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VsyncBridgeChild)
@ -29,12 +31,18 @@ public:
void DeallocPVsyncBridgeChild() override;
void ProcessingError(Result aCode, const char* aReason) override;
void NotifyVsync(TimeStamp aTimeStamp, const uint64_t& aLayersId);
private:
VsyncBridgeChild(RefPtr<VsyncIOThreadHolder>, const uint64_t& aProcessToken);
~VsyncBridgeChild();
void Open(Endpoint<PVsyncBridgeChild>&& aEndpoint);
void NotifyVsyncImpl(TimeStamp aTimeStamp, const uint64_t& aLayersId);
bool IsOnVsyncIOThread() const;
private:
RefPtr<VsyncIOThreadHolder> mThread;
MessageLoop* mLoop;

View File

@ -44,8 +44,9 @@ VsyncBridgeParent::Open(Endpoint<PVsyncBridgeParent>&& aEndpoint)
}
bool
VsyncBridgeParent::RecvNotifyVsync(const TimeStamp& vsyncTimeStamp)
VsyncBridgeParent::RecvNotifyVsync(const TimeStamp& aTimeStamp, const uint64_t& aLayersId)
{
CompositorBridgeParent::NotifyVsync(aTimeStamp, aLayersId);
return true;
}

View File

@ -19,7 +19,7 @@ public:
static RefPtr<VsyncBridgeParent> Start(Endpoint<PVsyncBridgeParent>&& aEndpoint);
bool RecvNotifyVsync(const TimeStamp& vsyncTimeStamp) override;
bool RecvNotifyVsync(const TimeStamp& vsyncTimeStamp, const uint64_t& aLayersId) override;
void ActorDestroy(ActorDestroyReason aWhy) override;
void DeallocPVsyncBridgeParent() override;

View File

@ -27,6 +27,10 @@ EXPORTS.mozilla.layers += [
'RemoteCompositorSession.h',
]
EXPORTS.mozilla.widget += [
'CompositorWidgetVsyncObserver.h',
]
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
EXPORTS.mozilla.gfx += [
'SharedDIBSurface.h',
@ -39,6 +43,7 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
UNIFIED_SOURCES += [
'CompositorSession.cpp',
'CompositorWidgetVsyncObserver.cpp',
'D3DMessageUtils.cpp',
'GPUChild.cpp',
'GPUParent.cpp',

View File

@ -269,7 +269,7 @@ CompositorVsyncScheduler::CompositorVsyncScheduler(CompositorBridgeParent* aComp
#endif
#endif
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(NS_IsMainThread() || XRE_GetProcessType() == GeckoProcessType_GPU);
mVsyncObserver = new Observer(this);
#ifdef MOZ_WIDGET_GONK
GeckoTouchDispatcher::GetInstance()->SetCompositorVsyncScheduler(this);
@ -451,8 +451,10 @@ CompositorVsyncScheduler::SetNeedsComposite()
bool
CompositorVsyncScheduler::NotifyVsync(TimeStamp aVsyncTimestamp)
{
// Called from the vsync dispatch thread
MOZ_ASSERT(!CompositorThreadHolder::IsInCompositorThread());
// Called from the vsync dispatch thread. When in the GPU Process, that's
// the same as the compositor thread.
MOZ_ASSERT_IF(XRE_IsParentProcess(), !CompositorThreadHolder::IsInCompositorThread());
MOZ_ASSERT_IF(XRE_GetProcessType() == GeckoProcessType_GPU, CompositorThreadHolder::IsInCompositorThread());
MOZ_ASSERT(!NS_IsMainThread());
PostCompositeTask(aVsyncTimestamp);
return true;
@ -639,7 +641,6 @@ CompositorBridgeParent::InitSameProcess(widget::CompositorWidget* aWidget,
if (aUseAPZ) {
mApzcTreeManager = new APZCTreeManager();
}
mCompositorScheduler = new CompositorVsyncScheduler(this, mWidget);
// IPDL initialization. mSelfRef is cleared in DeferredDestroy.
SetOtherProcessId(base::GetCurrentProcId());
@ -690,6 +691,8 @@ CompositorBridgeParent::Initialize()
}
LayerScope::SetPixelScale(mScale.scale);
mCompositorScheduler = new CompositorVsyncScheduler(this, mWidget);
}
uint64_t
@ -1687,6 +1690,28 @@ CompositorBridgeParent* CompositorBridgeParent::RemoveCompositor(uint64_t id)
return retval;
}
void
CompositorBridgeParent::NotifyVsync(const TimeStamp& aTimeStamp, const uint64_t& aLayersId)
{
MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_GPU);
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
MonitorAutoLock lock(*sIndirectLayerTreesLock);
auto it = sIndirectLayerTrees.find(aLayersId);
if (it == sIndirectLayerTrees.end())
return;
CompositorBridgeParent* cbp = it->second.mParent;
if (!cbp || !cbp->mWidget)
return;
RefPtr<VsyncObserver> obs = cbp->mWidget->GetVsyncObserver();
if (!obs)
return;
obs->NotifyVsync(aTimeStamp);
}
bool
CompositorBridgeParent::RecvNotifyChildCreated(const uint64_t& child)
{

View File

@ -401,6 +401,11 @@ public:
*/
static CompositorBridgeParent* GetCompositorBridgeParent(uint64_t id);
/**
* Notify the compositor for the given layer tree that vsync has occurred.
*/
static void NotifyVsync(const TimeStamp& aTimeStamp, const uint64_t& aLayersId);
/**
* Set aController as the pan/zoom callback for the subtree referred
* to by aLayersId.

View File

@ -125,8 +125,6 @@ TEST_F(VsyncTester, EnableVsync)
// Test that if we have vsync enabled, the display should get vsync notifications
TEST_F(VsyncTester, CompositorGetVsyncNotifications)
{
CompositorVsyncDispatcher::SetThreadAssertionsEnabled(false);
VsyncSource::Display& globalDisplay = mVsyncSource->GetGlobalDisplay();
globalDisplay.DisableVsync();
ASSERT_FALSE(globalDisplay.IsVsyncEnabled());

View File

@ -70,5 +70,15 @@ CompositorWidget::GetGLFrameBufferFormat()
return LOCAL_GL_RGBA;
}
RefPtr<VsyncObserver>
CompositorWidget::GetVsyncObserver() const
{
// This should only used when the widget is in the GPU process, and should be
// implemented by IPDL-enabled CompositorWidgets.
// GPU process does not have a CompositorVsyncDispatcher.
MOZ_ASSERT_UNREACHABLE("Must be implemented by derived class");
return nullptr;
}
} // namespace widget
} // namespace mozilla

View File

@ -223,10 +223,15 @@ public:
virtual already_AddRefed<gfx::SourceSurface> EndBackBufferDrawing();
/**
* Observer or unobserve vsync.
* Observe or unobserve vsync.
*/
virtual void ObserveVsync(VsyncObserver* aObserver) = 0;
/**
* This is only used by out-of-process compositors.
*/
virtual RefPtr<VsyncObserver> GetVsyncObserver() const;
virtual WinCompositorWidget* AsWindows() {
return nullptr;
}

View File

@ -18,6 +18,10 @@ sync protocol PCompositorWidget
parent:
async __delete__();
child:
async ObserveVsync();
async UnobserveVsync();
};
} // namespace widget

View File

@ -9,6 +9,7 @@
#include "gfxPlatform.h"
#include "mozilla/layers/Compositor.h"
#include "mozilla/layers/CompositorBridgeParent.h"
#include "mozilla/layers/CompositorThread.h"
#ifdef MOZ_ENABLE_PROFILER_SPS
#include "GeckoProfiler.h"
@ -16,14 +17,6 @@
#endif
namespace mozilla {
static bool sThreadAssertionsEnabled = true;
void CompositorVsyncDispatcher::SetThreadAssertionsEnabled(bool aEnable)
{
// Should only be used in test environments
MOZ_ASSERT(NS_IsMainThread());
sThreadAssertionsEnabled = aEnable;
}
CompositorVsyncDispatcher::CompositorVsyncDispatcher()
: mCompositorObserverLock("CompositorObserverLock")
@ -53,16 +46,6 @@ CompositorVsyncDispatcher::NotifyVsync(TimeStamp aVsyncTimestamp)
}
}
void
CompositorVsyncDispatcher::AssertOnCompositorThread()
{
if (!sThreadAssertionsEnabled) {
return;
}
Compositor::AssertOnCompositorThread();
}
void
CompositorVsyncDispatcher::ObserveVsync(bool aEnable)
{
@ -82,7 +65,11 @@ CompositorVsyncDispatcher::ObserveVsync(bool aEnable)
void
CompositorVsyncDispatcher::SetCompositorVsyncObserver(VsyncObserver* aVsyncObserver)
{
AssertOnCompositorThread();
// When remote compositing or running gtests, vsync observation is
// initiated on the main thread. Otherwise, it is initiated from the compositor
// thread.
MOZ_ASSERT(NS_IsMainThread() || CompositorThreadHolder::IsInCompositorThread());
{ // scope lock
MutexAutoLock lock(mCompositorObserverLock);
mCompositorVsyncObserver = aVsyncObserver;

View File

@ -31,7 +31,18 @@ protected:
virtual ~VsyncObserver() {}
}; // VsyncObserver
// Used to dispatch vsync events in the parent process to compositors
// Used to dispatch vsync events in the parent process to compositors.
//
// When the compositor is in-process, CompositorWidgets own a
// CompositorVsyncDispatcher, and directly attach the compositor's observer
// to it.
//
// When the compositor is out-of-process, the CompositorWidgetDelegate owns
// the vsync dispatcher instead. The widget receives vsync observer/unobserve
// commands via IPDL, and uses this to attach a CompositorWidgetVsyncObserver.
// This observer forwards vsync notifications (on the vsync thread) to a
// dedicated vsync I/O thread, which then forwards the notification to the
// compositor thread in the compositor process.
class CompositorVsyncDispatcher final
{
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CompositorVsyncDispatcher)
@ -46,13 +57,7 @@ public:
void SetCompositorVsyncObserver(VsyncObserver* aVsyncObserver);
void Shutdown();
// This can be used to enable or disable thread assertions.
// This is useful for gtests because usually things run
// in only one thread in that environment
static void SetThreadAssertionsEnabled(bool aEnable);
private:
void AssertOnCompositorThread();
virtual ~CompositorVsyncDispatcher();
void ObserveVsync(bool aEnable);

View File

@ -5,12 +5,19 @@
#include "CompositorWidgetChild.h"
#include "mozilla/unused.h"
#include "mozilla/widget/CompositorWidgetVsyncObserver.h"
#include "nsBaseWidget.h"
#include "VsyncDispatcher.h"
namespace mozilla {
namespace widget {
CompositorWidgetChild::CompositorWidgetChild(nsIWidget* aWidget)
CompositorWidgetChild::CompositorWidgetChild(RefPtr<CompositorVsyncDispatcher> aVsyncDispatcher,
RefPtr<CompositorWidgetVsyncObserver> aVsyncObserver)
: mVsyncDispatcher(aVsyncDispatcher),
mVsyncObserver(aVsyncObserver)
{
MOZ_ASSERT(XRE_IsParentProcess());
}
CompositorWidgetChild::~CompositorWidgetChild()
@ -52,11 +59,26 @@ CompositorWidgetChild::ResizeTransparentWindow(const gfx::IntSize& aSize)
Unused << SendResizeTransparentWindow(aSize);
}
HDC CompositorWidgetChild::GetTransparentDC() const
HDC
CompositorWidgetChild::GetTransparentDC() const
{
// Not supported in out-of-process mode.
return nullptr;
}
bool
CompositorWidgetChild::RecvObserveVsync()
{
mVsyncDispatcher->SetCompositorVsyncObserver(mVsyncObserver);
return true;
}
bool
CompositorWidgetChild::RecvUnobserveVsync()
{
mVsyncDispatcher->SetCompositorVsyncObserver(nullptr);
return true;
}
} // namespace widget
} // namespace mozilla

View File

@ -8,8 +8,11 @@
#include "WinCompositorWidget.h"
#include "mozilla/widget/PCompositorWidgetChild.h"
#include "mozilla/widget/CompositorWidgetVsyncObserver.h"
namespace mozilla {
class CompositorVsyncDispatcher;
namespace widget {
class CompositorWidgetChild final
@ -17,7 +20,8 @@ class CompositorWidgetChild final
public CompositorWidgetDelegate
{
public:
CompositorWidgetChild(nsIWidget* aWidget);
CompositorWidgetChild(RefPtr<CompositorVsyncDispatcher> aVsyncDispatcher,
RefPtr<CompositorWidgetVsyncObserver> aVsyncObserver);
~CompositorWidgetChild() override;
void EnterPresentLock() override;
@ -27,6 +31,13 @@ public:
void ClearTransparentWindow() override;
void ResizeTransparentWindow(const gfx::IntSize& aSize) override;
HDC GetTransparentDC() const override;
bool RecvObserveVsync() override;
bool RecvUnobserveVsync() override;
private:
RefPtr<CompositorVsyncDispatcher> mVsyncDispatcher;
RefPtr<CompositorWidgetVsyncObserver> mVsyncObserver;
};
} // namespace widget

View File

@ -11,6 +11,7 @@ namespace widget {
CompositorWidgetParent::CompositorWidgetParent(const CompositorWidgetInitData& aInitData)
: WinCompositorWidget(aInitData)
{
MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_GPU);
}
CompositorWidgetParent::~CompositorWidgetParent()
@ -52,6 +53,30 @@ CompositorWidgetParent::RecvResizeTransparentWindow(const IntSize& aSize)
return true;
}
nsIWidget*
CompositorWidgetParent::RealWidget()
{
return nullptr;
}
void
CompositorWidgetParent::ObserveVsync(VsyncObserver* aObserver)
{
if (aObserver) {
SendObserveVsync();
} else {
SendUnobserveVsync();
}
mVsyncObserver = aObserver;
}
RefPtr<VsyncObserver>
CompositorWidgetParent::GetVsyncObserver() const
{
MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_GPU);
return mVsyncObserver;
}
void
CompositorWidgetParent::ActorDestroy(ActorDestroyReason aWhy)
{

View File

@ -26,6 +26,13 @@ public:
bool RecvClearTransparentWindow() override;
bool RecvResizeTransparentWindow(const IntSize& aSize) override;
void ActorDestroy(ActorDestroyReason aWhy) override;
nsIWidget* RealWidget() override;
void ObserveVsync(VsyncObserver* aObserver) override;
RefPtr<VsyncObserver> GetVsyncObserver() const override;
private:
RefPtr<VsyncObserver> mVsyncObserver;
};
} // namespace widget

View File

@ -0,0 +1,40 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 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/. */
#include "InProcessWinCompositorWidget.h"
#include "nsWindow.h"
namespace mozilla {
namespace widget {
/* static */ RefPtr<CompositorWidget>
CompositorWidget::CreateLocal(const CompositorWidgetInitData& aInitData, nsIWidget* aWidget)
{
return new InProcessWinCompositorWidget(aInitData, static_cast<nsWindow*>(aWidget));
}
InProcessWinCompositorWidget::InProcessWinCompositorWidget(const CompositorWidgetInitData& aInitData,
nsWindow* aWindow)
: WinCompositorWidget(aInitData),
mWindow(aWindow)
{
MOZ_ASSERT(mWindow);
}
nsIWidget*
InProcessWinCompositorWidget::RealWidget()
{
return mWindow;
}
void
InProcessWinCompositorWidget::ObserveVsync(VsyncObserver* aObserver)
{
RefPtr<CompositorVsyncDispatcher> cvd = mWindow->GetCompositorVsyncDispatcher();
cvd->SetCompositorVsyncObserver(aObserver);
}
} // namespace widget
} // namespace mozilla

View File

@ -0,0 +1,35 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 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 widget_windows_InProcessCompositorWidgetParent_h
#define widget_windows_InProcessCompositorWidgetParent_h
#include "WinCompositorWidget.h"
class nsWindow;
namespace mozilla {
namespace widget {
// This is the Windows-specific implementation of CompositorWidget. For
// the most part it only requires an HWND, however it maintains extra state
// for transparent windows, as well as for synchronizing WM_SETTEXT messages
// with the compositor.
class InProcessWinCompositorWidget final : public WinCompositorWidget
{
public:
InProcessWinCompositorWidget(const CompositorWidgetInitData& aInitData, nsWindow* aWindow);
void ObserveVsync(VsyncObserver* aObserver) override;
nsIWidget* RealWidget() override;
private:
nsWindow* mWindow;
};
} // namespace widget
} // namespace mozilla
#endif // widget_windows_InProcessCompositorWidgetParent_h

View File

@ -22,6 +22,10 @@ parent:
sync ClearTransparentWindow();
sync ResizeTransparentWindow(IntSize aSize);
async __delete__();
child:
async ObserveVsync();
async UnobserveVsync();
};
} // namespace widget

View File

@ -4,26 +4,18 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "WinCompositorWidget.h"
#include "nsWindow.h"
#include "VsyncDispatcher.h"
#include "mozilla/gfx/Point.h"
#include "mozilla/widget/PlatformWidgetTypes.h"
#include "nsWindow.h"
#include "VsyncDispatcher.h"
namespace mozilla {
namespace widget {
using namespace mozilla::gfx;
/* static */ RefPtr<CompositorWidget>
CompositorWidget::CreateLocal(const CompositorWidgetInitData& aInitData, nsIWidget* aWidget)
{
return new WinCompositorWidget(aInitData, static_cast<nsWindow*>(aWidget));
}
WinCompositorWidget::WinCompositorWidget(const CompositorWidgetInitData& aInitData,
nsWindow* aWindow)
: mWindow(aWindow),
mWidgetKey(aInitData.widgetKey()),
WinCompositorWidget::WinCompositorWidget(const CompositorWidgetInitData& aInitData)
: mWidgetKey(aInitData.widgetKey()),
mWnd(reinterpret_cast<HWND>(aInitData.hWnd())),
mTransparencyMode(static_cast<nsTransparencyMode>(aInitData.transparencyMode())),
mMemoryDC(nullptr),
@ -57,13 +49,6 @@ WinCompositorWidget::PostRender(layers::LayerManagerComposite* aManager)
mPresentLock.Leave();
}
nsIWidget*
WinCompositorWidget::RealWidget()
{
MOZ_ASSERT(mWindow);
return mWindow;
}
LayoutDeviceIntSize
WinCompositorWidget::GetClientSize()
{
@ -173,13 +158,6 @@ WinCompositorWidget::EndBackBufferDrawing()
return CompositorWidget::EndBackBufferDrawing();
}
void
WinCompositorWidget::ObserveVsync(VsyncObserver* aObserver)
{
RefPtr<CompositorVsyncDispatcher> cvd = mWindow->GetCompositorVsyncDispatcher();
cvd->SetCompositorVsyncObserver(aObserver);
}
uintptr_t
WinCompositorWidget::GetWidgetKey()
{

View File

@ -46,8 +46,7 @@ class WinCompositorWidget
public CompositorWidgetDelegate
{
public:
WinCompositorWidget(const CompositorWidgetInitData& aInitData,
nsWindow* aWindow = nullptr);
WinCompositorWidget(const CompositorWidgetInitData& aInitData);
bool PreRender(layers::LayerManagerComposite*) override;
void PostRender(layers::LayerManagerComposite*) override;
@ -58,9 +57,7 @@ public:
const LayoutDeviceIntRect& aRect,
const LayoutDeviceIntRect& aClearRect) override;
already_AddRefed<gfx::SourceSurface> EndBackBufferDrawing() override;
void ObserveVsync(VsyncObserver* aObserver) override;
uintptr_t GetWidgetKey() override;
nsIWidget* RealWidget() override;
WinCompositorWidget* AsWindows() override {
return this;
}
@ -95,7 +92,6 @@ private:
void CreateTransparentSurface(const gfx::IntSize& aSize);
private:
nsWindow* mWindow;
uintptr_t mWidgetKey;
HWND mWnd;
gfx::CriticalSection mPresentLock;

View File

@ -16,6 +16,7 @@ EXPORTS.mozilla.widget += [
'AudioSession.h',
'CompositorWidgetChild.h',
'CompositorWidgetParent.h',
'InProcessWinCompositorWidget.h',
'WinCompositorWidget.h',
'WinMessages.h',
'WinModifierKeyState.h',
@ -30,6 +31,7 @@ UNIFIED_SOURCES += [
'IEnumFE.cpp',
'IMMHandler.cpp',
'InkCollector.cpp',
'InProcessWinCompositorWidget.cpp',
'JumpListItem.cpp',
'KeyboardLayout.cpp',
'nsAppShell.cpp',

View File

@ -141,7 +141,7 @@
#include "nsBidiKeyboard.h"
#include "nsThemeConstants.h"
#include "gfxConfig.h"
#include "WinCompositorWidget.h"
#include "InProcessWinCompositorWidget.h"
#include "nsIGfxInfo.h"
#include "nsUXThemeConstants.h"
@ -3732,7 +3732,7 @@ nsWindow::GetLayerManager(PLayerTransactionChild* aShadowManager,
reinterpret_cast<uintptr_t>(mWnd),
reinterpret_cast<uintptr_t>(static_cast<nsIWidget*>(this)),
mTransparencyMode);
mBasicLayersSurface = new WinCompositorWidget(initData, this);
mBasicLayersSurface = new InProcessWinCompositorWidget(initData, this);
mCompositorWidgetDelegate = mBasicLayersSurface;
mLayerManager = CreateBasicLayerManager();
}