Trigger Idle from present Paint

This commit is contained in:
Zach Pomerantz 2016-05-10 11:20:08 -07:00
parent c3f41cdd89
commit 783be53125
4 changed files with 49 additions and 85 deletions

View file

@ -347,19 +347,14 @@ public:
}; };
#endif #endif
enum CustomEventTypes {
Lambda = QEvent::User + 1,
Paint = Lambda + 1,
};
class LambdaEvent : public QEvent { class LambdaEvent : public QEvent {
std::function<void()> _fun; std::function<void()> _fun;
public: public:
LambdaEvent(const std::function<void()> & fun) : LambdaEvent(const std::function<void()> & fun) :
QEvent(static_cast<QEvent::Type>(Lambda)), _fun(fun) { QEvent(static_cast<QEvent::Type>(Application::Lambda)), _fun(fun) {
} }
LambdaEvent(std::function<void()> && fun) : LambdaEvent(std::function<void()> && fun) :
QEvent(static_cast<QEvent::Type>(Lambda)), _fun(fun) { QEvent(static_cast<QEvent::Type>(Application::Lambda)), _fun(fun) {
} }
void call() const { _fun(); } void call() const { _fun(); }
}; };
@ -1060,18 +1055,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
connect(this, &Application::applicationStateChanged, this, &Application::activeChanged); connect(this, &Application::applicationStateChanged, this, &Application::activeChanged);
qCDebug(interfaceapp, "Startup time: %4.2f seconds.", (double)startupTimer.elapsed() / 1000.0); qCDebug(interfaceapp, "Startup time: %4.2f seconds.", (double)startupTimer.elapsed() / 1000.0);
_idleTimer = new QTimer(this);
connect(_idleTimer, &QTimer::timeout, [=] {
idle(usecTimestampNow());
});
connect(this, &Application::beforeAboutToQuit, [=] {
disconnect(_idleTimer);
});
// Setting the interval to zero forces this to get called whenever there are no messages
// in the queue, which can be pretty damn frequent. Hence the idle function has a bunch
// of logic to abort early if it's being called too often.
_idleTimer->start(0);
} }
@ -1437,23 +1420,15 @@ void Application::initializeUi() {
}); });
} }
void Application::paintGL() { void Application::paintGL() {
updateHeartbeat(); // Some plugins process message events, allowing paintGL to be called reentrantly.
// Some plugins process message events, potentially leading to if (_inPaint || _aboutToQuit) {
// re-entering a paint event. don't allow further processing if this
// happens
if (_inPaint) {
return; return;
} }
_inPaint = true;
Finally clearFlagLambda([this] { _inPaint = false; });
// paintGL uses a queued connection, so we can get messages from the queue even after we've quit _inPaint = true;
// and the plugins have shutdown Finally clearFlag([this] { _inPaint = false; });
if (_aboutToQuit) {
return;
}
_frameCount++; _frameCount++;
_frameCounter.increment(); _frameCounter.increment();
@ -1811,14 +1786,16 @@ bool Application::event(QEvent* event) {
return false; return false;
} }
if ((int)event->type() == (int)Lambda) { if ((int)event->type() == (int)Idle) {
static_cast<LambdaEvent*>(event)->call(); idle();
removePostedEvents(this, Idle); // clear pending idles so we don't clog the pipes
return true; return true;
} } else if ((int)event->type() == (int)Paint) {
if ((int)event->type() == (int)Paint) {
paintGL(); paintGL();
return true; return true;
} else if ((int)event->type() == (int)Lambda) {
static_cast<LambdaEvent*>(event)->call();
return true;
} }
if (!_keyboardFocusedItem.isInvalidID()) { if (!_keyboardFocusedItem.isInvalidID()) {
@ -2595,34 +2572,13 @@ bool Application::acceptSnapshot(const QString& urlString) {
static uint32_t _renderedFrameIndex { INVALID_FRAME }; static uint32_t _renderedFrameIndex { INVALID_FRAME };
void Application::idle(uint64_t now) { void Application::idle() {
// NOTICE NOTICE NOTICE NOTICE // idle is called on a queued connection, so make sure we should be here.
// Do not insert new code between here and the PROFILE_RANGE declaration if (_inPaint || _aboutToQuit) {
// unless you know exactly what you're doing. This idle function can be return;
// called thousands per second or more, so any additional work that's done
// here will have a serious impact on CPU usage. Only add code after all
// the thottling logic, i.e. after PROFILE_RANGE
// NOTICE NOTICE NOTICE NOTICE
updateHeartbeat();
if (_aboutToQuit || _inPaint) {
return; // bail early, nothing to do here.
} }
auto displayPlugin = getActiveDisplayPlugin(); auto displayPlugin = getActiveDisplayPlugin();
// depending on whether we're throttling or not.
// Once rendering is off on another thread we should be able to have Application::idle run at start(0) in
// perpetuity and not expect events to get backed up.
bool isThrottled = displayPlugin->isThrottled();
// Only run simulation code if more than the targetFramePeriod have passed since last time we ran
// This attempts to lock the simulation at 60 updates per second, regardless of framerate
float timeSinceLastUpdateUs = (float)_lastTimeUpdated.nsecsElapsed() / NSECS_PER_USEC;
float secondsSinceLastUpdate = timeSinceLastUpdateUs / USECS_PER_SECOND;
if (isThrottled && (timeSinceLastUpdateUs / USECS_PER_MSEC) < THROTTLED_SIM_FRAME_PERIOD_MS) {
// Throttling both rendering and idle
return; // bail early, we're throttled and not enough time has elapsed
}
#ifdef DEBUG_PAINT_DELAY #ifdef DEBUG_PAINT_DELAY
static uint64_t paintDelaySamples{ 0 }; static uint64_t paintDelaySamples{ 0 };
@ -2638,43 +2594,38 @@ void Application::idle(uint64_t now) {
} }
#endif #endif
auto presentCount = displayPlugin->presentCount(); float msecondsSinceLastUpdate = (float)_lastTimeUpdated.nsecsElapsed() / NSECS_PER_USEC / USECS_PER_MSEC;
if (presentCount < _renderedFrameIndex) {
_renderedFrameIndex = INVALID_FRAME;
}
// Don't saturate the main thread with rendering and simulation, // Throttle if requested
// unless display plugin has increased by at least one frame if (displayPlugin->isThrottled() && (msecondsSinceLastUpdate < THROTTLED_SIM_FRAME_PERIOD_MS)) {
if (_renderedFrameIndex == INVALID_FRAME || presentCount > _renderedFrameIndex) {
// Record what present frame we're on
_renderedFrameIndex = presentCount;
// request a paint, get to it as soon as possible: high priority
postEvent(this, new QEvent(static_cast<QEvent::Type>(Paint)), Qt::HighEventPriority);
} else {
// there's no use in simulating or rendering faster then the present rate.
return; return;
} }
// NOTICE NOTICE NOTICE NOTICE // Sync up the _renderedFrameIndex
// do NOT add new code above this line unless you want it to be executed potentially _renderedFrameIndex = displayPlugin->presentCount();
// thousands of times per second
// NOTICE NOTICE NOTICE NOTICE
PROFILE_RANGE(__FUNCTION__); // Request a paint ASAP
postEvent(this, new QEvent(static_cast<QEvent::Type>(Paint)), Qt::HighEventPriority);
// Update the deadlock watchdog
updateHeartbeat();
auto offscreenUi = DependencyManager::get<OffscreenUi>();
// These tasks need to be done on our first idle, because we don't want the showing of // These tasks need to be done on our first idle, because we don't want the showing of
// overlay subwindows to do a showDesktop() until after the first time through // overlay subwindows to do a showDesktop() until after the first time through
static bool firstIdle = true; static bool firstIdle = true;
if (firstIdle) { if (firstIdle) {
firstIdle = false; firstIdle = false;
auto offscreenUi = DependencyManager::get<OffscreenUi>();
connect(offscreenUi.data(), &OffscreenUi::showDesktop, this, &Application::showDesktop); connect(offscreenUi.data(), &OffscreenUi::showDesktop, this, &Application::showDesktop);
_overlayConductor.setEnabled(Menu::getInstance()->isOptionChecked(MenuOption::Overlays)); _overlayConductor.setEnabled(Menu::getInstance()->isOptionChecked(MenuOption::Overlays));
} }
PROFILE_RANGE(__FUNCTION__);
float secondsSinceLastUpdate = msecondsSinceLastUpdate / MSECS_PER_SECOND;
// If the offscreen Ui has something active that is NOT the root, then assume it has keyboard focus. // If the offscreen Ui has something active that is NOT the root, then assume it has keyboard focus.
auto offscreenUi = DependencyManager::get<OffscreenUi>();
if (_keyboardDeviceHasFocus && offscreenUi && offscreenUi->getWindow()->activeFocusItem() != offscreenUi->getRootItem()) { if (_keyboardDeviceHasFocus && offscreenUi && offscreenUi->getWindow()->activeFocusItem() != offscreenUi->getRootItem()) {
_keyboardMouseDevice->pluginFocusOutEvent(); _keyboardMouseDevice->pluginFocusOutEvent();
_keyboardDeviceHasFocus = false; _keyboardDeviceHasFocus = false;

View file

@ -33,6 +33,7 @@
#include <PhysicalEntitySimulation.h> #include <PhysicalEntitySimulation.h>
#include <PhysicsEngine.h> #include <PhysicsEngine.h>
#include <plugins/Forward.h> #include <plugins/Forward.h>
#include <plugins/DisplayPlugin.h>
#include <ScriptEngine.h> #include <ScriptEngine.h>
#include <ShapeManager.h> #include <ShapeManager.h>
#include <SimpleMovingAverage.h> #include <SimpleMovingAverage.h>
@ -93,6 +94,12 @@ class Application : public QApplication, public AbstractViewStateInterface, publ
friend class PluginContainerProxy; friend class PluginContainerProxy;
public: public:
enum Event {
Idle = DisplayPlugin::Paint,
Paint = Idle + 1,
Lambda = Paint + 1
};
// FIXME? Empty methods, do we still need them? // FIXME? Empty methods, do we still need them?
static void initPlugins(); static void initPlugins();
static void shutdownPlugins(); static void shutdownPlugins();
@ -282,7 +289,6 @@ public slots:
private slots: private slots:
void showDesktop(); void showDesktop();
void clearDomainOctreeDetails(); void clearDomainOctreeDetails();
void idle(uint64_t now);
void aboutToQuit(); void aboutToQuit();
void resettingDomain(); void resettingDomain();
@ -321,6 +327,7 @@ private:
void cleanupBeforeQuit(); void cleanupBeforeQuit();
void idle();
void update(float deltaTime); void update(float deltaTime);
// Various helper functions called during update() // Various helper functions called during update()
@ -498,7 +505,6 @@ private:
int _avatarAttachmentRequest = 0; int _avatarAttachmentRequest = 0;
bool _settingsLoaded { false }; bool _settingsLoaded { false };
QTimer* _idleTimer { nullptr };
bool _fakedMouseEvent { false }; bool _fakedMouseEvent { false };

View file

@ -35,4 +35,7 @@ void DisplayPlugin::incrementPresentCount() {
#endif #endif
++_presentedFrameIndex; ++_presentedFrameIndex;
// Alert the app that it needs to paint a new presentation frame
qApp->postEvent(qApp, new QEvent(static_cast<QEvent::Type>(Paint)), Qt::HighEventPriority);
} }

View file

@ -60,6 +60,10 @@ class DisplayPlugin : public Plugin {
Q_OBJECT Q_OBJECT
using Parent = Plugin; using Parent = Plugin;
public: public:
enum Event {
Paint = QEvent::User + 1
};
bool activate() override; bool activate() override;
void deactivate() override; void deactivate() override;
virtual bool isHmd() const { return false; } virtual bool isHmd() const { return false; }