gecko/gfx/tests/gtest/TestVsync.cpp

208 lines
6.0 KiB
C++

/* vim:set ts=2 sw=2 sts=2 et: */
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "gfxPlatform.h"
#include "gfxPrefs.h"
#include "MainThreadUtils.h"
#include "nsIThread.h"
#include "mozilla/RefPtr.h"
#include "SoftwareVsyncSource.h"
#include "VsyncSource.h"
#include "mozilla/Monitor.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/VsyncDispatcher.h"
using namespace mozilla;
using namespace mozilla::gfx;
using namespace mozilla::layers;
using ::testing::_;
// Timeout for vsync events to occur in milliseconds
// Windows 8.1 has intermittents at 50 ms. Raise limit to 5 vsync intervals.
const int kVsyncTimeoutMS = 80;
class TestVsyncObserver : public VsyncObserver {
public:
TestVsyncObserver()
: mDidGetVsyncNotification(false)
, mVsyncMonitor("VsyncMonitor")
{
}
virtual bool NotifyVsync(TimeStamp aVsyncTimeStamp) override {
MonitorAutoLock lock(mVsyncMonitor);
mDidGetVsyncNotification = true;
mVsyncMonitor.Notify();
return true;
}
void WaitForVsyncNotification()
{
MOZ_ASSERT(NS_IsMainThread());
if (DidGetVsyncNotification()) {
return;
}
{ // scope lock
MonitorAutoLock lock(mVsyncMonitor);
PRIntervalTime timeout = PR_MillisecondsToInterval(kVsyncTimeoutMS);
lock.Wait(timeout);
}
}
bool DidGetVsyncNotification()
{
MonitorAutoLock lock(mVsyncMonitor);
return mDidGetVsyncNotification;
}
void ResetVsyncNotification()
{
MonitorAutoLock lock(mVsyncMonitor);
mDidGetVsyncNotification = false;
}
private:
bool mDidGetVsyncNotification;
private:
Monitor mVsyncMonitor;
};
class VsyncTester : public ::testing::Test {
protected:
explicit VsyncTester()
{
gfxPlatform::GetPlatform();
gfxPrefs::GetSingleton();
mVsyncSource = gfxPlatform::GetPlatform()->GetHardwareVsync();
MOZ_RELEASE_ASSERT(mVsyncSource);
}
virtual ~VsyncTester()
{
mVsyncSource = nullptr;
}
RefPtr<VsyncSource> mVsyncSource;
};
static void
FlushMainThreadLoop()
{
// Some tasks are pushed onto the main thread when adding vsync observers
// This function will ensure all tasks are executed on the main thread
// before returning.
nsCOMPtr<nsIThread> mainThread;
nsresult rv = NS_GetMainThread(getter_AddRefs(mainThread));
ASSERT_TRUE(NS_SUCCEEDED(rv));
rv = NS_OK;
bool processed = true;
while (processed && NS_SUCCEEDED(rv)) {
rv = mainThread->ProcessNextEvent(false, &processed);
}
}
// Tests that we can enable/disable vsync notifications
TEST_F(VsyncTester, EnableVsync)
{
VsyncSource::Display& globalDisplay = mVsyncSource->GetGlobalDisplay();
globalDisplay.DisableVsync();
ASSERT_FALSE(globalDisplay.IsVsyncEnabled());
globalDisplay.EnableVsync();
ASSERT_TRUE(globalDisplay.IsVsyncEnabled());
globalDisplay.DisableVsync();
ASSERT_FALSE(globalDisplay.IsVsyncEnabled());
}
// 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());
RefPtr<CompositorVsyncDispatcher> vsyncDispatcher = new CompositorVsyncDispatcher();
RefPtr<TestVsyncObserver> testVsyncObserver = new TestVsyncObserver();
vsyncDispatcher->SetCompositorVsyncObserver(testVsyncObserver);
FlushMainThreadLoop();
ASSERT_TRUE(globalDisplay.IsVsyncEnabled());
testVsyncObserver->WaitForVsyncNotification();
ASSERT_TRUE(testVsyncObserver->DidGetVsyncNotification());
vsyncDispatcher = nullptr;
testVsyncObserver = nullptr;
}
// Test that if we have vsync enabled, the parent refresh driver should get notifications
TEST_F(VsyncTester, ParentRefreshDriverGetVsyncNotifications)
{
VsyncSource::Display& globalDisplay = mVsyncSource->GetGlobalDisplay();
globalDisplay.DisableVsync();
ASSERT_FALSE(globalDisplay.IsVsyncEnabled());
RefPtr<RefreshTimerVsyncDispatcher> vsyncDispatcher = globalDisplay.GetRefreshTimerVsyncDispatcher();
ASSERT_TRUE(vsyncDispatcher != nullptr);
RefPtr<TestVsyncObserver> testVsyncObserver = new TestVsyncObserver();
vsyncDispatcher->SetParentRefreshTimer(testVsyncObserver);
ASSERT_TRUE(globalDisplay.IsVsyncEnabled());
testVsyncObserver->WaitForVsyncNotification();
ASSERT_TRUE(testVsyncObserver->DidGetVsyncNotification());
vsyncDispatcher->SetParentRefreshTimer(nullptr);
testVsyncObserver->ResetVsyncNotification();
testVsyncObserver->WaitForVsyncNotification();
ASSERT_FALSE(testVsyncObserver->DidGetVsyncNotification());
vsyncDispatcher = nullptr;
testVsyncObserver = nullptr;
}
// Test that child refresh vsync observers get vsync notifications
TEST_F(VsyncTester, ChildRefreshDriverGetVsyncNotifications)
{
VsyncSource::Display& globalDisplay = mVsyncSource->GetGlobalDisplay();
globalDisplay.DisableVsync();
ASSERT_FALSE(globalDisplay.IsVsyncEnabled());
RefPtr<RefreshTimerVsyncDispatcher> vsyncDispatcher = globalDisplay.GetRefreshTimerVsyncDispatcher();
ASSERT_TRUE(vsyncDispatcher != nullptr);
RefPtr<TestVsyncObserver> testVsyncObserver = new TestVsyncObserver();
vsyncDispatcher->AddChildRefreshTimer(testVsyncObserver);
ASSERT_TRUE(globalDisplay.IsVsyncEnabled());
testVsyncObserver->WaitForVsyncNotification();
ASSERT_TRUE(testVsyncObserver->DidGetVsyncNotification());
vsyncDispatcher->RemoveChildRefreshTimer(testVsyncObserver);
testVsyncObserver->ResetVsyncNotification();
testVsyncObserver->WaitForVsyncNotification();
ASSERT_FALSE(testVsyncObserver->DidGetVsyncNotification());
vsyncDispatcher = nullptr;
testVsyncObserver = nullptr;
}
// Test that we can read the vsync rate
TEST_F(VsyncTester, VsyncSourceHasVsyncRate)
{
VsyncSource::Display& globalDisplay = mVsyncSource->GetGlobalDisplay();
TimeDuration vsyncRate = globalDisplay.GetVsyncRate();
ASSERT_NE(vsyncRate, TimeDuration::Forever());
ASSERT_GT(vsyncRate.ToMilliseconds(), 0);
}