mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-29 22:22:54 +02:00
Merge pull request #8319 from ZappoMan/punishSlowScripts
better allocation of CPU time while loading scenes and when slow scripts are running
This commit is contained in:
commit
a13c8385bb
4 changed files with 41 additions and 6 deletions
|
@ -4678,6 +4678,11 @@ void Application::packetSent(quint64 length) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scriptEngine) {
|
void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scriptEngine) {
|
||||||
|
|
||||||
|
scriptEngine->setEmitScriptUpdatesFunction([this]() {
|
||||||
|
return isPhysicsEnabled();
|
||||||
|
});
|
||||||
|
|
||||||
// setup the packet senders and jurisdiction listeners of the script engine's scripting interfaces so
|
// setup the packet senders and jurisdiction listeners of the script engine's scripting interfaces so
|
||||||
// we can use the same ones from the application.
|
// we can use the same ones from the application.
|
||||||
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
|
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
|
||||||
|
|
|
@ -215,6 +215,7 @@ public:
|
||||||
qint64 getCurrentSessionRuntime() const { return _sessionRunTimer.elapsed(); }
|
qint64 getCurrentSessionRuntime() const { return _sessionRunTimer.elapsed(); }
|
||||||
|
|
||||||
bool isAboutToQuit() const { return _aboutToQuit; }
|
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
|
// 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
|
// rendering of several elements depend on that
|
||||||
|
|
|
@ -828,24 +828,44 @@ void ScriptEngine::run() {
|
||||||
|
|
||||||
_lastUpdate = usecTimestampNow();
|
_lastUpdate = usecTimestampNow();
|
||||||
|
|
||||||
|
std::chrono::microseconds totalUpdates;
|
||||||
|
|
||||||
// TODO: Integrate this with signals/slots instead of reimplementing throttling for ScriptEngine
|
// TODO: Integrate this with signals/slots instead of reimplementing throttling for ScriptEngine
|
||||||
while (!_isFinished) {
|
while (!_isFinished) {
|
||||||
|
auto beforeSleep = clock::now();
|
||||||
|
|
||||||
// Throttle to SCRIPT_FPS
|
// Throttle to SCRIPT_FPS
|
||||||
|
// We'd like to try to keep the script at a solid SCRIPT_FPS update rate. And so we will
|
||||||
|
// calculate a sleepUntil to be the time from our start time until the original target
|
||||||
|
// sleepUntil for this frame.
|
||||||
const std::chrono::microseconds FRAME_DURATION(USECS_PER_SECOND / SCRIPT_FPS + 1);
|
const std::chrono::microseconds FRAME_DURATION(USECS_PER_SECOND / SCRIPT_FPS + 1);
|
||||||
clock::time_point sleepTime(startTime + thisFrame++ * FRAME_DURATION);
|
clock::time_point sleepUntil(startTime + thisFrame++ * FRAME_DURATION);
|
||||||
std::this_thread::sleep_until(sleepTime);
|
|
||||||
|
// However, if our sleepUntil is not at least our average update time into the future
|
||||||
|
// it means our script is taking too long in it's updates, and we want to punish the
|
||||||
|
// script a little bit. So we will force the sleepUntil to be at least our averageUpdate
|
||||||
|
// time into the future.
|
||||||
|
auto wouldSleep = (sleepUntil - clock::now());
|
||||||
|
auto avgerageUpdate = totalUpdates / thisFrame;
|
||||||
|
|
||||||
|
if (wouldSleep < avgerageUpdate) {
|
||||||
|
sleepUntil = beforeSleep + avgerageUpdate;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::this_thread::sleep_until(sleepUntil);
|
||||||
|
|
||||||
#ifdef SCRIPT_DELAY_DEBUG
|
#ifdef SCRIPT_DELAY_DEBUG
|
||||||
{
|
{
|
||||||
auto now = clock::now();
|
auto actuallySleptUntil = clock::now();
|
||||||
uint64_t seconds = std::chrono::duration_cast<std::chrono::seconds>(now - startTime).count();
|
uint64_t seconds = std::chrono::duration_cast<std::chrono::seconds>(actuallySleptUntil - startTime).count();
|
||||||
if (seconds > 0) { // avoid division by zero and time travel
|
if (seconds > 0) { // avoid division by zero and time travel
|
||||||
uint64_t fps = thisFrame / seconds;
|
uint64_t fps = thisFrame / seconds;
|
||||||
// Overreporting artificially reduces the reported rate
|
// Overreporting artificially reduces the reported rate
|
||||||
if (thisFrame % SCRIPT_FPS == 0) {
|
if (thisFrame % SCRIPT_FPS == 0) {
|
||||||
qCDebug(scriptengine) <<
|
qCDebug(scriptengine) <<
|
||||||
"Frame:" << thisFrame <<
|
"Frame:" << thisFrame <<
|
||||||
"Slept (us):" << std::chrono::duration_cast<std::chrono::microseconds>(now - sleepTime).count() <<
|
"Slept (us):" << std::chrono::duration_cast<std::chrono::microseconds>(actuallySleptUntil - beforeSleep).count() <<
|
||||||
|
"Avg Updates (us):" << avgerageUpdate.count() <<
|
||||||
"FPS:" << fps;
|
"FPS:" << fps;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -874,10 +894,14 @@ void ScriptEngine::run() {
|
||||||
qint64 now = usecTimestampNow();
|
qint64 now = usecTimestampNow();
|
||||||
|
|
||||||
// we check for 'now' in the past in case people set their clock back
|
// we check for 'now' in the past in case people set their clock back
|
||||||
if (_lastUpdate < now) {
|
if (_emitScriptUpdates() && _lastUpdate < now) {
|
||||||
float deltaTime = (float) (now - _lastUpdate) / (float) USECS_PER_SECOND;
|
float deltaTime = (float) (now - _lastUpdate) / (float) USECS_PER_SECOND;
|
||||||
if (!_isFinished) {
|
if (!_isFinished) {
|
||||||
|
auto preUpdate = clock::now();
|
||||||
emit update(deltaTime);
|
emit update(deltaTime);
|
||||||
|
auto postUpdate = clock::now();
|
||||||
|
auto elapsed = (postUpdate - preUpdate);
|
||||||
|
totalUpdates += std::chrono::duration_cast<std::chrono::microseconds>(elapsed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_lastUpdate = now;
|
_lastUpdate = now;
|
||||||
|
|
|
@ -168,6 +168,8 @@ public:
|
||||||
// NOTE - this is used by the TypedArray implemetation. we need to review this for thread safety
|
// NOTE - this is used by the TypedArray implemetation. we need to review this for thread safety
|
||||||
ArrayBufferClass* getArrayBufferClass() { return _arrayBufferClass; }
|
ArrayBufferClass* getArrayBufferClass() { return _arrayBufferClass; }
|
||||||
|
|
||||||
|
void setEmitScriptUpdatesFunction(std::function<bool()> func) { _emitScriptUpdates = func; }
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void callAnimationStateHandler(QScriptValue callback, AnimVariantMap parameters, QStringList names, bool useNames, AnimVariantResultHandler resultHandler);
|
void callAnimationStateHandler(QScriptValue callback, AnimVariantMap parameters, QStringList names, bool useNames, AnimVariantResultHandler resultHandler);
|
||||||
void updateMemoryCost(const qint64&);
|
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.
|
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 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);
|
void callWithEnvironment(const EntityItemID& entityID, const QUrl& sandboxURL, QScriptValue function, QScriptValue thisObject, QScriptValueList args);
|
||||||
|
|
||||||
|
std::function<bool()> _emitScriptUpdates{ [](){ return true; } };
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_ScriptEngine_h
|
#endif // hifi_ScriptEngine_h
|
||||||
|
|
Loading…
Reference in a new issue