punish slow scripts and don't send updates while physics is still loading

This commit is contained in:
Brad Hefta-Gaub 2016-07-25 21:46:30 -07:00
parent cb848362ec
commit 83dc9ea6bb
4 changed files with 38 additions and 4 deletions

View file

@ -4678,6 +4678,11 @@ void Application::packetSent(quint64 length) {
}
void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scriptEngine) {
scriptEngine->setPhysicsEnabledFunction([this]() {
return isPhysicsEnabled();
});
// setup the packet senders and jurisdiction listeners of the script engine's scripting interfaces so
// we can use the same ones from the application.
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();

View file

@ -215,6 +215,7 @@ public:
qint64 getCurrentSessionRuntime() const { return _sessionRunTimer.elapsed(); }
bool isAboutToQuit() const { return _aboutToQuit; }
bool isPhysicsEnabled() const { return _physicsEnabled; }
// the isHMDMode is true whenever we use the interface from an HMD and not a standard flat display
// rendering of several elements depend on that

View file

@ -828,24 +828,42 @@ void ScriptEngine::run() {
_lastUpdate = usecTimestampNow();
qint64 totalSleepFor = 0;
std::chrono::microseconds totalUpdates;
auto lastLoopStart = clock::now();
// TODO: Integrate this with signals/slots instead of reimplementing throttling for ScriptEngine
while (!_isFinished) {
auto thisLoopStart = clock::now();
// Throttle to SCRIPT_FPS
const std::chrono::microseconds FRAME_DURATION(USECS_PER_SECOND / SCRIPT_FPS + 1);
const std::chrono::microseconds MINIMUM_SLEEP { FRAME_DURATION / 2 };
auto beforeSleep = clock::now();
clock::time_point sleepTime(startTime + thisFrame++ * FRAME_DURATION);
auto wouldSleep = (sleepTime - clock::now());
auto avgUpdates = totalUpdates / thisFrame;
if (wouldSleep < avgUpdates) {
sleepTime = beforeSleep + avgUpdates;
}
std::this_thread::sleep_until(sleepTime);
#ifdef SCRIPT_DELAY_DEBUG
{
auto now = clock::now();
uint64_t seconds = std::chrono::duration_cast<std::chrono::seconds>(now - startTime).count();
auto sleptTill = clock::now();
uint64_t seconds = std::chrono::duration_cast<std::chrono::seconds>(sleptTill - startTime).count();
if (seconds > 0) { // avoid division by zero and time travel
uint64_t fps = thisFrame / seconds;
// Overreporting artificially reduces the reported rate
if (thisFrame % SCRIPT_FPS == 0) {
qCDebug(scriptengine) <<
"Frame:" << thisFrame <<
"Slept (us):" << std::chrono::duration_cast<std::chrono::microseconds>(now - sleepTime).count() <<
"Slept (us):" << std::chrono::duration_cast<std::chrono::microseconds>(sleptTill - beforeSleep).count() <<
"Avg Updates (us):" << avgUpdates.count() <<
"Last loop time (us):" << std::chrono::duration_cast<std::chrono::microseconds>(thisLoopStart - lastLoopStart).count() <<
"FPS:" << fps;
}
}
@ -874,16 +892,21 @@ void ScriptEngine::run() {
qint64 now = usecTimestampNow();
// we check for 'now' in the past in case people set their clock back
if (_lastUpdate < now) {
if (_isPhysicsEnabledFunc() && _lastUpdate < now) {
float deltaTime = (float) (now - _lastUpdate) / (float) USECS_PER_SECOND;
if (!_isFinished) {
auto preUpdate = clock::now();
emit update(deltaTime);
auto postUpdate = clock::now();
auto elapsed = (postUpdate - preUpdate);
totalUpdates += std::chrono::duration_cast<std::chrono::microseconds>(elapsed);
}
}
_lastUpdate = now;
// Debug and clear exceptions
hadUncaughtExceptions(*this, _fileNameString);
lastLoopStart = thisLoopStart;
}
qCDebug(scriptengine) << "Script Engine stopping:" << getFilename();

View file

@ -168,6 +168,8 @@ public:
// NOTE - this is used by the TypedArray implemetation. we need to review this for thread safety
ArrayBufferClass* getArrayBufferClass() { return _arrayBufferClass; }
void setPhysicsEnabledFunction(std::function<bool()> func) { _isPhysicsEnabledFunc = func; }
public slots:
void callAnimationStateHandler(QScriptValue callback, AnimVariantMap parameters, QStringList names, bool useNames, AnimVariantResultHandler resultHandler);
void updateMemoryCost(const qint64&);
@ -236,6 +238,9 @@ protected:
QUrl currentSandboxURL {}; // The toplevel url string for the entity script that loaded the code being executed, else empty.
void doWithEnvironment(const EntityItemID& entityID, const QUrl& sandboxURL, std::function<void()> operation);
void callWithEnvironment(const EntityItemID& entityID, const QUrl& sandboxURL, QScriptValue function, QScriptValue thisObject, QScriptValueList args);
std::function<bool()> _isPhysicsEnabledFunc{ [](){ return true; } };
};
#endif // hifi_ScriptEngine_h