Skip to content

Commit

Permalink
SCI: Detect SCI16 unthrottled inner loops
Browse files Browse the repository at this point in the history
This restores kGetEvent throttling (10ms delay) during inner loops that
don't throttle themselves. For example, the event processing loop in
Dialog:doit doesn't include a kWait(1) call in some games.
In these games, dialogs max out CPU, and if they run animations then
they run too fast. This happens in the CD version of JONES, bug #14020.

I had considered doing something like this earlier, but it wasn't clear
how to best detect these loops. What I didn't realize is that the SCI32
code already does this by counting kGetEvent calls in between kFrameout.
Now that technique is adapted for SCI16. Combined with the existing
"fast cast" detection, this throttles event-polling inner loops while
keeping the kGetEvent and kGameIsRestarting throttling separate so that
they don't overlap and conflict.

See: e09010f
  • Loading branch information
sluicebox committed Jan 10, 2023
1 parent 7f426af commit 6eea9e5
Show file tree
Hide file tree
Showing 5 changed files with 19 additions and 14 deletions.
23 changes: 15 additions & 8 deletions engines/sci/engine/kevent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -265,14 +265,21 @@ reg_t kGetEvent(EngineState *s, int argc, reg_t *argv) {
g_sci->_soundCmd->updateSci0Cues();
}

// Wait a bit if a "fast cast" game is polling kGetEvent while a message is
// being said or displayed. This prevents fast cast mode from maxing out
// CPU by polling from an inner loop when the fast cast global is set.
// Fixes bugs #5091, #5326
if (getSciVersion() <= SCI_VERSION_1_1 &&
g_sci->_gfxAnimate->isFastCastEnabled() &&
!s->variables[VAR_GLOBAL][kGlobalVarFastCast].isNull()) {
g_system->delayMillis(10);
// If we're in a SCI16 unthrottled inner loop then delay a bit.
// This prevents the CPU from maxing out and prevents inner loop animations
// from running too fast. "Fast cast" games poll kGetEvent from an inner
// loop while they display or say a message. This can be detected by testing
// the fast cast global. Other inner loops can be detected by counting the
// kGetEvent calls since the last kGameIsRestarting(0) or kWait call.
// For example, some versions of Dialog:doit poll without calling kWait(1).
// See above for similar SCI32 code that counts calls between kFrameout.
// Fixes bugs #5091, #5326, #14020
if (getSciVersion() <= SCI_VERSION_1_1) {
if (++s->_eventCounter > 2 ||
(g_sci->_gfxAnimate->isFastCastEnabled() &&
!s->variables[VAR_GLOBAL][kGlobalVarFastCast].isNull())) {
g_system->delayMillis(10);
}
}

return s->r_acc;
Expand Down
1 change: 1 addition & 0 deletions engines/sci/engine/kgraphics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,7 @@ reg_t kWait(EngineState *s, int argc, reg_t *argv) {
return NULL_REG;
}

s->_eventCounter = 0;
s->_paletteSetIntensityCounter = 0;
return make_reg(0, delta);
}
Expand Down
1 change: 1 addition & 0 deletions engines/sci/engine/kmisc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ reg_t kGameIsRestarting(EngineState *s, int argc, reg_t *argv) {

s->speedThrottler(neededSleep);

s->_eventCounter = 0;
s->_paletteSetIntensityCounter = 0;
return make_reg(0, previousRestartingFlag);
}
Expand Down
2 changes: 0 additions & 2 deletions engines/sci/engine/state.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,9 +104,7 @@ void EngineState::reset(bool isRestoring) {

gcCountDown = 0;

#ifdef ENABLE_SCI32
_eventCounter = 0;
#endif
_paletteSetIntensityCounter = 0;
_throttleLastTime = 0;
_throttleTrigger = false;
Expand Down
6 changes: 2 additions & 4 deletions engines/sci/engine/state.h
Original file line number Diff line number Diff line change
Expand Up @@ -116,10 +116,8 @@ struct EngineState : public Common::Serializable {
uint16 wait(uint16 ticks);
void sleep(uint16 ticks);

#ifdef ENABLE_SCI32
uint32 _eventCounter; /**< total times kGetEvent was invoked since the last call to kFrameOut */
#endif
uint32 _paletteSetIntensityCounter; /**< total times kPaletteSetIntensity was invoked since the last call to kGameIsRestarting or kWait */
uint32 _eventCounter; /**< total times kGetEvent was invoked since the last call to kGameIsRestarting(0) or kWait or kFrameOut */
uint32 _paletteSetIntensityCounter; /**< total times kPaletteSetIntensity was invoked since the last call to kGameIsRestarting(0) or kWait */
uint32 _throttleLastTime; /**< last time kAnimate was invoked */
bool _throttleTrigger;

Expand Down

0 comments on commit 6eea9e5

Please sign in to comment.