summaryrefslogtreecommitdiffstats
path: root/gfx/thebes/VsyncSource.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/thebes/VsyncSource.cpp')
-rw-r--r--gfx/thebes/VsyncSource.cpp277
1 files changed, 277 insertions, 0 deletions
diff --git a/gfx/thebes/VsyncSource.cpp b/gfx/thebes/VsyncSource.cpp
new file mode 100644
index 0000000000..ba678254e0
--- /dev/null
+++ b/gfx/thebes/VsyncSource.cpp
@@ -0,0 +1,277 @@
+/* -*- Mode: C++; tab-width: 20; 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 "VsyncSource.h"
+#include "nsThreadUtils.h"
+#include "nsXULAppAPI.h"
+#include "mozilla/VsyncDispatcher.h"
+#include "MainThreadUtils.h"
+
+namespace mozilla {
+namespace gfx {
+
+void VsyncSource::EnableCompositorVsyncDispatcher(
+ CompositorVsyncDispatcher* aCompositorVsyncDispatcher) {
+ MOZ_ASSERT(XRE_IsParentProcess());
+ MOZ_ASSERT(NS_IsMainThread());
+ // Just use the global display until we have enough information to get the
+ // corresponding display for compositor.
+ GetGlobalDisplay().EnableCompositorVsyncDispatcher(
+ aCompositorVsyncDispatcher);
+}
+
+void VsyncSource::DisableCompositorVsyncDispatcher(
+ CompositorVsyncDispatcher* aCompositorVsyncDispatcher) {
+ MOZ_ASSERT(XRE_IsParentProcess());
+ MOZ_ASSERT(NS_IsMainThread());
+ // See also EnableCompositorVsyncDispatcher().
+ GetGlobalDisplay().DisableCompositorVsyncDispatcher(
+ aCompositorVsyncDispatcher);
+}
+
+void VsyncSource::RegisterCompositorVsyncDispatcher(
+ CompositorVsyncDispatcher* aCompositorVsyncDispatcher) {
+ MOZ_ASSERT(XRE_IsParentProcess());
+ MOZ_ASSERT(NS_IsMainThread());
+ GetGlobalDisplay().RegisterCompositorVsyncDispatcher(
+ aCompositorVsyncDispatcher);
+}
+
+void VsyncSource::DeregisterCompositorVsyncDispatcher(
+ CompositorVsyncDispatcher* aCompositorVsyncDispatcher) {
+ MOZ_ASSERT(XRE_IsParentProcess());
+ MOZ_ASSERT(NS_IsMainThread());
+ GetGlobalDisplay().DeregisterCompositorVsyncDispatcher(
+ aCompositorVsyncDispatcher);
+}
+
+void VsyncSource::AddGenericObserver(VsyncObserver* aObserver) {
+ MOZ_ASSERT(XRE_IsParentProcess());
+ MOZ_ASSERT(NS_IsMainThread());
+
+ GetGlobalDisplay().AddGenericObserver(aObserver);
+}
+
+void VsyncSource::RemoveGenericObserver(VsyncObserver* aObserver) {
+ MOZ_ASSERT(XRE_IsParentProcess());
+ MOZ_ASSERT(NS_IsMainThread());
+
+ GetGlobalDisplay().RemoveGenericObserver(aObserver);
+}
+
+void VsyncSource::MoveListenersToNewSource(
+ const RefPtr<VsyncSource>& aNewSource) {
+ GetGlobalDisplay().MoveListenersToNewSource(aNewSource);
+}
+
+RefPtr<RefreshTimerVsyncDispatcher>
+VsyncSource::GetRefreshTimerVsyncDispatcher() {
+ MOZ_ASSERT(XRE_IsParentProcess());
+ // See also AddCompositorVsyncDispatcher().
+ return GetGlobalDisplay().GetRefreshTimerVsyncDispatcher();
+}
+
+VsyncSource::Display::Display()
+ : mDispatcherLock("display dispatcher lock"),
+ mRefreshTimerNeedsVsync(false),
+ mHasGenericObservers(false) {
+ MOZ_ASSERT(NS_IsMainThread());
+ mRefreshTimerVsyncDispatcher = new RefreshTimerVsyncDispatcher(this);
+}
+
+VsyncSource::Display::~Display() {
+ MOZ_ASSERT(NS_IsMainThread());
+ MutexAutoLock lock(mDispatcherLock);
+ mRefreshTimerVsyncDispatcher = nullptr;
+ MOZ_ASSERT(mRegisteredCompositorVsyncDispatchers.Length() == 0);
+ MOZ_ASSERT(mEnabledCompositorVsyncDispatchers.Length() == 0);
+}
+
+void VsyncSource::Display::NotifyVsync(const TimeStamp& aVsyncTimestamp,
+ const TimeStamp& aOutputTimestamp) {
+ // Called on the vsync thread
+ MutexAutoLock lock(mDispatcherLock);
+
+ // mRefreshTimerVsyncDispatcher might be null here if MoveListenersToNewSource
+ // was called concurrently with this function and won the race to acquire
+ // mDispatcherLock. In this case the new VsyncSource that is replacing this
+ // one will handle notifications from now on, so we can abort.
+ if (!mRefreshTimerVsyncDispatcher) {
+ return;
+ }
+
+ // If the task posted to the main thread from the last NotifyVsync call
+ // hasn't been processed yet, then don't send another one. Otherwise we might
+ // end up flooding the main thread.
+ bool dispatchToMainThread =
+ mHasGenericObservers &&
+ (mLastVsyncIdSentToMainThread == mLastMainThreadProcessedVsyncId);
+
+ mVsyncId = mVsyncId.Next();
+ const VsyncEvent event(mVsyncId, aVsyncTimestamp, aOutputTimestamp);
+
+ for (size_t i = 0; i < mEnabledCompositorVsyncDispatchers.Length(); i++) {
+ mEnabledCompositorVsyncDispatchers[i]->NotifyVsync(event);
+ }
+
+ mRefreshTimerVsyncDispatcher->NotifyVsync(event);
+
+ if (dispatchToMainThread) {
+ mLastVsyncIdSentToMainThread = mVsyncId;
+ NS_DispatchToMainThread(NewRunnableMethod<VsyncEvent>(
+ "VsyncSource::Display::NotifyGenericObservers", this,
+ &VsyncSource::Display::NotifyGenericObservers, event));
+ }
+}
+
+void VsyncSource::Display::NotifyGenericObservers(VsyncEvent aEvent) {
+ MOZ_ASSERT(NS_IsMainThread());
+ for (size_t i = 0; i < mGenericObservers.Length(); i++) {
+ mGenericObservers[i]->NotifyVsync(aEvent);
+ }
+
+ { // Scope lock
+ MutexAutoLock lock(mDispatcherLock);
+ mLastMainThreadProcessedVsyncId = aEvent.mId;
+ }
+}
+
+TimeDuration VsyncSource::Display::GetVsyncRate() {
+ // If hardware queries fail / are unsupported, we have to just guess.
+ return TimeDuration::FromMilliseconds(1000.0 / 60.0);
+}
+
+void VsyncSource::Display::RegisterCompositorVsyncDispatcher(
+ CompositorVsyncDispatcher* aCompositorVsyncDispatcher) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aCompositorVsyncDispatcher);
+ { // scope lock
+ MutexAutoLock lock(mDispatcherLock);
+ mRegisteredCompositorVsyncDispatchers.AppendElement(
+ aCompositorVsyncDispatcher);
+ }
+}
+
+void VsyncSource::Display::DeregisterCompositorVsyncDispatcher(
+ CompositorVsyncDispatcher* aCompositorVsyncDispatcher) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aCompositorVsyncDispatcher);
+ { // Scope lock
+ MutexAutoLock lock(mDispatcherLock);
+ mRegisteredCompositorVsyncDispatchers.RemoveElement(
+ aCompositorVsyncDispatcher);
+ }
+}
+
+void VsyncSource::Display::EnableCompositorVsyncDispatcher(
+ CompositorVsyncDispatcher* aCompositorVsyncDispatcher) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aCompositorVsyncDispatcher);
+ { // scope lock
+ MutexAutoLock lock(mDispatcherLock);
+ if (!mEnabledCompositorVsyncDispatchers.Contains(
+ aCompositorVsyncDispatcher)) {
+ mEnabledCompositorVsyncDispatchers.AppendElement(
+ aCompositorVsyncDispatcher);
+ }
+ }
+ UpdateVsyncStatus();
+}
+
+void VsyncSource::Display::DisableCompositorVsyncDispatcher(
+ CompositorVsyncDispatcher* aCompositorVsyncDispatcher) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aCompositorVsyncDispatcher);
+ { // Scope lock
+ MutexAutoLock lock(mDispatcherLock);
+ if (mEnabledCompositorVsyncDispatchers.Contains(
+ aCompositorVsyncDispatcher)) {
+ mEnabledCompositorVsyncDispatchers.RemoveElement(
+ aCompositorVsyncDispatcher);
+ }
+ }
+ UpdateVsyncStatus();
+}
+
+void VsyncSource::Display::AddGenericObserver(VsyncObserver* aObserver) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aObserver);
+ mGenericObservers.AppendElement(aObserver);
+
+ UpdateVsyncStatus();
+}
+
+void VsyncSource::Display::RemoveGenericObserver(VsyncObserver* aObserver) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aObserver);
+ mGenericObservers.RemoveElement(aObserver);
+
+ UpdateVsyncStatus();
+}
+
+void VsyncSource::Display::MoveListenersToNewSource(
+ const RefPtr<VsyncSource>& aNewSource) {
+ MOZ_ASSERT(NS_IsMainThread());
+ VsyncSource::Display& aNewDisplay = aNewSource->GetGlobalDisplay();
+ MutexAutoLock lock(mDispatcherLock);
+ MutexAutoLock newLock(aNewDisplay.mDispatcherLock);
+ aNewDisplay.mRegisteredCompositorVsyncDispatchers.AppendElements(
+ std::move(mRegisteredCompositorVsyncDispatchers));
+ aNewDisplay.mEnabledCompositorVsyncDispatchers.AppendElements(
+ std::move(mEnabledCompositorVsyncDispatchers));
+ aNewDisplay.mGenericObservers.AppendElements(std::move(mGenericObservers));
+
+ for (size_t i = 0;
+ i < aNewDisplay.mRegisteredCompositorVsyncDispatchers.Length(); i++) {
+ aNewDisplay.mRegisteredCompositorVsyncDispatchers[i]->MoveToSource(
+ aNewSource);
+ }
+
+ aNewDisplay.mRefreshTimerVsyncDispatcher = mRefreshTimerVsyncDispatcher;
+ mRefreshTimerVsyncDispatcher->MoveToDisplay(&aNewDisplay);
+ mRefreshTimerVsyncDispatcher = nullptr;
+}
+
+void VsyncSource::Display::NotifyRefreshTimerVsyncStatus(bool aEnable) {
+ MOZ_ASSERT(NS_IsMainThread());
+ mRefreshTimerNeedsVsync = aEnable;
+ UpdateVsyncStatus();
+}
+
+void VsyncSource::Display::UpdateVsyncStatus() {
+ MOZ_ASSERT(NS_IsMainThread());
+ // WARNING: This function SHOULD NOT BE CALLED WHILE HOLDING LOCKS
+ // NotifyVsync grabs a lock to dispatch vsync events
+ // When disabling vsync, we wait for the underlying thread to stop on some
+ // platforms We can deadlock if we wait for the underlying vsync thread to
+ // stop while the vsync thread is in NotifyVsync.
+ bool enableVsync = false;
+ { // scope lock
+ MutexAutoLock lock(mDispatcherLock);
+ enableVsync = !mEnabledCompositorVsyncDispatchers.IsEmpty() ||
+ mRefreshTimerNeedsVsync || !mGenericObservers.IsEmpty();
+ mHasGenericObservers = !mGenericObservers.IsEmpty();
+ }
+
+ if (enableVsync) {
+ EnableVsync();
+ } else {
+ DisableVsync();
+ }
+
+ if (IsVsyncEnabled() != enableVsync) {
+ NS_WARNING("Vsync status did not change.");
+ }
+}
+
+RefPtr<RefreshTimerVsyncDispatcher>
+VsyncSource::Display::GetRefreshTimerVsyncDispatcher() {
+ return mRefreshTimerVsyncDispatcher;
+}
+
+void VsyncSource::Shutdown() { GetGlobalDisplay().Shutdown(); }
+
+} // namespace gfx
+} // namespace mozilla