mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-25 18:55:01 +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) {
|
||||
|
||||
scriptEngine->setEmitScriptUpdatesFunction([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>();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -828,24 +828,44 @@ void ScriptEngine::run() {
|
|||
|
||||
_lastUpdate = usecTimestampNow();
|
||||
|
||||
std::chrono::microseconds totalUpdates;
|
||||
|
||||
// TODO: Integrate this with signals/slots instead of reimplementing throttling for ScriptEngine
|
||||
while (!_isFinished) {
|
||||
auto beforeSleep = clock::now();
|
||||
|
||||
// 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);
|
||||
clock::time_point sleepTime(startTime + thisFrame++ * FRAME_DURATION);
|
||||
std::this_thread::sleep_until(sleepTime);
|
||||
clock::time_point sleepUntil(startTime + thisFrame++ * FRAME_DURATION);
|
||||
|
||||
// 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
|
||||
{
|
||||
auto now = clock::now();
|
||||
uint64_t seconds = std::chrono::duration_cast<std::chrono::seconds>(now - startTime).count();
|
||||
auto actuallySleptUntil = clock::now();
|
||||
uint64_t seconds = std::chrono::duration_cast<std::chrono::seconds>(actuallySleptUntil - 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>(actuallySleptUntil - beforeSleep).count() <<
|
||||
"Avg Updates (us):" << avgerageUpdate.count() <<
|
||||
"FPS:" << fps;
|
||||
}
|
||||
}
|
||||
|
@ -874,10 +894,14 @@ void ScriptEngine::run() {
|
|||
qint64 now = usecTimestampNow();
|
||||
|
||||
// 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;
|
||||
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;
|
||||
|
|
|
@ -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 setEmitScriptUpdatesFunction(std::function<bool()> func) { _emitScriptUpdates = 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()> _emitScriptUpdates{ [](){ return true; } };
|
||||
|
||||
};
|
||||
|
||||
#endif // hifi_ScriptEngine_h
|
||||
|
|
Loading…
Reference in a new issue