[Scummvm-git-logs] scummvm master -> fd7fd3a18b7310aef8203ef5be2b37a9fc3eaed7

antoniou79 antoniou at cti.gr
Sun Sep 1 15:44:50 CEST 2019


This automated email contains information about 1 new commit which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
fd7fd3a18b BLADERUNNER: Isolate new frame limiter code in new class


Commit: fd7fd3a18b7310aef8203ef5be2b37a9fc3eaed7
    https://github.com/scummvm/scummvm/commit/fd7fd3a18b7310aef8203ef5be2b37a9fc3eaed7
Author: Thanasis Antoniou (a.antoniou79 at gmail.com)
Date: 2019-09-01T16:39:36+03:00

Commit Message:
BLADERUNNER: Isolate new frame limiter code in new class

Changed paths:
  A engines/bladerunner/framelimiter.cpp
  A engines/bladerunner/framelimiter.h
    engines/bladerunner/bladerunner.cpp
    engines/bladerunner/bladerunner.h
    engines/bladerunner/module.mk
    engines/bladerunner/outtake.cpp
    engines/bladerunner/outtake.h
    engines/bladerunner/ui/elevator.cpp
    engines/bladerunner/ui/elevator.h
    engines/bladerunner/ui/end_credits.cpp
    engines/bladerunner/ui/end_credits.h
    engines/bladerunner/ui/esper.cpp
    engines/bladerunner/ui/esper.h
    engines/bladerunner/ui/kia.cpp
    engines/bladerunner/ui/kia.h
    engines/bladerunner/ui/spinner.cpp
    engines/bladerunner/ui/spinner.h
    engines/bladerunner/ui/vk.cpp
    engines/bladerunner/ui/vk.h


diff --git a/engines/bladerunner/bladerunner.cpp b/engines/bladerunner/bladerunner.cpp
index 9262d41..b73afdd 100644
--- a/engines/bladerunner/bladerunner.cpp
+++ b/engines/bladerunner/bladerunner.cpp
@@ -34,6 +34,7 @@
 #include "bladerunner/crimes_database.h"
 #include "bladerunner/debugger.h"
 #include "bladerunner/dialogue_menu.h"
+#include "bladerunner/framelimiter.h"
 #include "bladerunner/font.h"
 #include "bladerunner/game_flags.h"
 #include "bladerunner/game_info.h"
@@ -216,6 +217,8 @@ BladeRunnerEngine::BladeRunnerEngine(OSystem *syst, const ADGameDescription *des
 		_actors[i]           = nullptr;
 	}
 	_debugger                = nullptr;
+	_mainLoopFrameLimiter    = nullptr;
+
 	walkingReset();
 
 	_actorUpdateCounter  = 0;
@@ -526,6 +529,8 @@ bool BladeRunnerEngine::startup(bool hasSavegames) {
 	_cosTable1024 = new Common::CosineTable(1024); // 10-bits = 1024 points for 2*PI;
 	_sinTable1024 = new Common::SineTable(1024);
 
+	_mainLoopFrameLimiter = new Framelimiter(this, Framelimiter::kDefaultFpsRate, Framelimiter::kDefaultUseDelayMillis);
+
 	_view = new View();
 
 	_sceneObjects = new SceneObjects(this, _view);
@@ -927,6 +932,11 @@ void BladeRunnerEngine::shutdown() {
 
 	delete _screenEffects;
 	_screenEffects = nullptr;
+
+	if (_mainLoopFrameLimiter) {
+		delete _mainLoopFrameLimiter;
+		_mainLoopFrameLimiter = nullptr;
+	}
 }
 
 bool BladeRunnerEngine::loadSplash() {
@@ -955,7 +965,7 @@ bool BladeRunnerEngine::isMouseButtonDown() const {
 
 void BladeRunnerEngine::gameLoop() {
 	_gameIsRunning = true;
-	_timeOfMainGameLoopTickPrevious = _time->currentSystem();
+	_mainLoopFrameLimiter->init();
 	do {
 		if (_playerDead) {
 			playerDied();
@@ -970,7 +980,7 @@ void BladeRunnerEngine::gameTick() {
 	handleEvents();
 
 	if (!_gameIsRunning || !_windowIsActive) {
-		_timeOfMainGameLoopTickPrevious = _time->currentSystem();
+		_mainLoopFrameLimiter->init();
 		return;
 	}
 
@@ -979,7 +989,7 @@ void BladeRunnerEngine::gameTick() {
 			Common::Error runtimeError = Common::Error(Common::kUnknownError, _("A required game resource was not found"));
 			GUI::MessageDialog dialog(runtimeError.getDesc());
 			dialog.runModal();
-			_timeOfMainGameLoopTickPrevious = _time->currentSystem();
+			_mainLoopFrameLimiter->init();
 			return;
 		}
 	}
@@ -1111,15 +1121,13 @@ void BladeRunnerEngine::gameTick() {
 
 	_subtitles->tick(_surfaceFront);
 
-	uint32 mainGameLoopTickNow = _time->currentSystem();
-	if (mainGameLoopTickNow - _timeOfMainGameLoopTickPrevious < kUpdateFrameTimeInMs) {
-		return;
-	}
-	_timeOfMainGameLoopTickPrevious = mainGameLoopTickNow;
 	 // Without this condition the game may flash back to the game screen
 	 // between and ending outtake and the end credits.
 	if (!_gameOver) {
-		blitToScreen(_surfaceFront);
+		if (_mainLoopFrameLimiter->shouldExecuteScreenUpdate()) {
+			blitToScreen(_surfaceFront);
+			_mainLoopFrameLimiter->postScreenUpdate();
+		}
 	}
 
 }
diff --git a/engines/bladerunner/bladerunner.h b/engines/bladerunner/bladerunner.h
index 8aee6b6..ebc8bd0 100644
--- a/engines/bladerunner/bladerunner.h
+++ b/engines/bladerunner/bladerunner.h
@@ -73,6 +73,7 @@ class DialogueMenu;
 class Elevator;
 class EndCredits;
 class ESPER;
+class Framelimiter;
 class Font;
 class GameFlags;
 class GameInfo;
@@ -119,8 +120,6 @@ public:
 	// 2: all time code uses uint32 (since July 17 2019),
 	static const int kBladeRunnerScummVMVersion = 2;
 
-	static const uint32 kUpdateFrameTimeInMs = 16u;
-
 	bool _gameIsRunning;
 	bool _windowIsActive;
 	int  _playerLosesControlCounter;
@@ -198,6 +197,8 @@ public:
 	Common::CosineTable *_cosTable1024;
 	Common::SineTable   *_sinTable1024;
 
+	Framelimiter *_mainLoopFrameLimiter;
+
 	bool _isWalkingInterruptible;
 	bool _interruptWalking;
 	bool _playerActorIdle;
diff --git a/engines/bladerunner/framelimiter.cpp b/engines/bladerunner/framelimiter.cpp
new file mode 100644
index 0000000..08cc9fc
--- /dev/null
+++ b/engines/bladerunner/framelimiter.cpp
@@ -0,0 +1,130 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "bladerunner/framelimiter.h"
+
+#include "bladerunner/bladerunner.h"
+#include "bladerunner/time.h"
+#include "common/system.h"
+
+namespace BladeRunner {
+
+Framelimiter::Framelimiter(BladeRunnerEngine *vm, FramelimiterFpsRate framerateMode, bool useDelayMs) {
+	_vm = vm;
+
+//	// FUTURE: The frame limiter is disabled when vsync is enabled.
+//	_enabled = !_system->getFeatureState(OSystem::kFeatureVSync);
+	_enabled         = true;
+	_useDelayMs      = useDelayMs;
+
+	_speedLimitMs    = 0u;
+	uint32 framerate = 1u; // dummy init
+	switch (framerateMode) {
+	case kFramelimiter15fps:
+		framerate =  15u;
+		break;
+	case kFramelimiter25fps:
+		framerate =  25u;
+		break;
+	case kFramelimiter30fps:
+		framerate =  30u;
+		break;
+	case kFramelimiter60fps:
+		framerate =  60u;
+		break;
+	case kFramelimiter120fps:
+		framerate = 120u;
+		break;
+	case kFramelimiterDisabled:
+		// fall through
+	default:
+		_enabled = false;
+		break;
+	}
+
+	if (_enabled) {
+		_speedLimitMs = 1000 / CLIP<uint32>(framerate, 1, 120);
+	}
+
+	reset();
+}
+
+Framelimiter::~Framelimiter() { }
+
+void Framelimiter::init(bool forceFirstPass) {
+	reset();
+	_timeOfLastPass = _vm->_time->currentSystem();
+	_forceFirstPass = forceFirstPass;
+}
+
+uint32 Framelimiter::getLastFrameDuration() const {
+	return _lastFrameDurationMs;
+}
+
+uint32 Framelimiter::getTimeOfCurrentPass() const {
+	return _timeOfCurrentPass;
+}
+
+uint32 Framelimiter::getTimeOfLastPass() const {
+	return _timeOfLastPass;
+}
+
+bool Framelimiter::shouldExecuteScreenUpdate() {
+	bool shouldUpdateScreen = true;
+	_timeOfCurrentPass = _vm->_time->currentSystem();
+	if (_enabled) {
+		shouldUpdateScreen = ((_timeOfCurrentPass - _timeOfLastPass) >= _speedLimitMs) || _forceFirstPass;
+
+		if (shouldUpdateScreen) {
+			if (_forceFirstPass) {
+				_forceFirstPass = false;
+			}
+			_startFrameTime = _timeOfCurrentPass;
+		}
+	}
+	return shouldUpdateScreen;
+}
+
+void  Framelimiter::postScreenUpdate() {
+	_timeOfLastPass = _timeOfCurrentPass;
+	if (_enabled) {
+
+		if (_useDelayMs) {
+			uint32 endFrameTime = _vm->_time->currentSystem();
+			uint32 frameDuration = endFrameTime - _startFrameTime;
+
+			if (frameDuration < _speedLimitMs) {
+				_vm->_system->delayMillis(_speedLimitMs - frameDuration);
+			}
+		}
+	}
+}
+
+void Framelimiter::reset() {
+	_forceFirstPass      = false;
+	_timeOfLastPass      = 0u;
+	_timeOfCurrentPass   = 0u;
+	_startFrameTime      = 0u;
+	_lastFrameDurationMs = _speedLimitMs;
+}
+
+} // End of namespace BladeRunner
diff --git a/engines/bladerunner/framelimiter.h b/engines/bladerunner/framelimiter.h
new file mode 100644
index 0000000..66ee0f3
--- /dev/null
+++ b/engines/bladerunner/framelimiter.h
@@ -0,0 +1,91 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef BLADERUNNER_FRAMELIMITER_H
+#define BLADERUNNER_FRAMELIMITER_H
+
+#include "bladerunner/bladerunner.h"
+
+namespace BladeRunner {
+
+enum FramelimiterFpsRate {
+	kFramelimiterDisabled       = 0,
+	kFramelimiter15fps          = 1,
+	kFramelimiter25fps          = 2,
+	kFramelimiter30fps          = 3,
+	kFramelimiter60fps          = 4,
+	kFramelimiter120fps         = 5
+};
+
+class BladeRunnerEngine;
+
+class Framelimiter {
+	friend class Debugger;
+
+public:
+	static const FramelimiterFpsRate kDefaultFpsRate = kFramelimiter60fps;
+	static const bool kDefaultUseDelayMillis = false;
+
+private:
+	BladeRunnerEngine *_vm;
+
+	bool   _forceFirstPass;
+	uint32 _speedLimitMs;
+
+	// A pass is when a tick or while loop that contains a potential screen update is repeated
+	// it's essentially when the check is made for a screen update
+	// Not every pass will necessarily result in a screen update (because that's the purpose of the frame limiter)
+	// So the "_startFrameTime" is not always equal to "_timeOfCurrentPass"
+	uint32 _timeOfLastPass;
+	uint32 _timeOfCurrentPass;
+
+	uint32 _startFrameTime;      // is updated and valid, only if the current pass will result in a screen update (see method: shouldExecuteScreenUpdate())
+	uint32 _lastFrameDurationMs; // can be used for average FPS calculation and display purposes when frame limiter is enabled
+
+	bool   _enabled;
+	bool   _useDelayMs;          // true: will use calls to delayMillis(), false: will use non-blocking software timer instead
+
+public:
+	Framelimiter(BladeRunnerEngine *vm, FramelimiterFpsRate framerateMode, bool useDelayMs);
+	~Framelimiter();
+
+//	void startFrame();
+//	void delayBeforeSwap();
+
+//	void pause(bool pause);
+
+	void init(bool forceFirstPass = true);
+	uint32 getLastFrameDuration() const;
+	uint32 getTimeOfCurrentPass() const;
+	uint32 getTimeOfLastPass() const;
+
+	bool shouldExecuteScreenUpdate();
+	void postScreenUpdate();
+
+private:
+	void reset();
+
+};
+
+} // End of namespace BladeRunner
+
+#endif
diff --git a/engines/bladerunner/module.mk b/engines/bladerunner/module.mk
index e52f8e5..6be142d 100644
--- a/engines/bladerunner/module.mk
+++ b/engines/bladerunner/module.mk
@@ -24,6 +24,7 @@ MODULE_OBJS = \
 	decompress_lzo.o \
 	detection.o \
 	dialogue_menu.o \
+	framelimiter.o \
 	fog.o \
 	font.o \
 	game_flags.o \
diff --git a/engines/bladerunner/outtake.cpp b/engines/bladerunner/outtake.cpp
index ba02bda..2f49099 100644
--- a/engines/bladerunner/outtake.cpp
+++ b/engines/bladerunner/outtake.cpp
@@ -24,6 +24,7 @@
 
 #include "bladerunner/bladerunner.h"
 #include "bladerunner/chapters.h"
+#include "bladerunner/framelimiter.h"
 #include "bladerunner/subtitles.h"
 #include "bladerunner/vqa_player.h"
 #include "bladerunner/time.h"
@@ -37,10 +38,16 @@ namespace BladeRunner {
 OuttakePlayer::OuttakePlayer(BladeRunnerEngine *vm) {
 	_vm = vm;
 	_surfaceVideo.create(_vm->_surfaceBack.w, _vm->_surfaceBack.h, _vm->_surfaceBack.format);
+	_framelimiter = new Framelimiter(_vm, Framelimiter::kDefaultFpsRate, Framelimiter::kDefaultUseDelayMillis);
 }
 
 OuttakePlayer::~OuttakePlayer() {
 	_surfaceVideo.free();
+
+	if (_framelimiter) {
+		delete _framelimiter;
+		_framelimiter = nullptr;
+	}
 }
 
 void OuttakePlayer::play(const Common::String &name, bool noLocalization, int container) {
@@ -70,28 +77,20 @@ void OuttakePlayer::play(const Common::String &name, bool noLocalization, int co
 	_vm->_vqaIsPlaying = true;
 	_vm->_vqaStopIsRequested = false;
 
-	uint32 timeNow = 0;
-	bool   firstFrame = true;
-	_timeLast = _vm->_time->currentSystem();
+	_framelimiter->init();
 
 	while (!_vm->_vqaStopIsRequested && !_vm->shouldQuit()) {
 		_vm->handleEvents();
 
 		if (!_vm->_windowIsActive) {
-			_timeLast = _vm->_time->currentSystem();
+			_framelimiter->init();
 			continue;
 		}
 
-		timeNow = _vm->_time->currentSystem();
-		// unsigned difference is intentional
-		if (timeNow - _timeLast < _vm->kUpdateFrameTimeInMs && !firstFrame) {
+		if (!_framelimiter->shouldExecuteScreenUpdate()) {
 			continue;
 		}
 
-		if (firstFrame) {
-			firstFrame = false;
-		}
-
 		int frame = vqaPlayer.update();
 		blit(_surfaceVideo, _vm->_surfaceFront); // This helps to make subtitles disappear properly, if the video is rendered in separate surface and then pushed to the front surface
 		if (frame == -3) { // end of video
@@ -103,7 +102,7 @@ void OuttakePlayer::play(const Common::String &name, bool noLocalization, int co
 			_vm->_subtitles->tickOuttakes(_vm->_surfaceFront);
 			_vm->blitToScreen(_vm->_surfaceFront);
 		}
-		_timeLast = timeNow;
+		_framelimiter->postScreenUpdate();
 	}
 
 	_vm->_vqaIsPlaying = false;
diff --git a/engines/bladerunner/outtake.h b/engines/bladerunner/outtake.h
index fc20273..0305e80 100644
--- a/engines/bladerunner/outtake.h
+++ b/engines/bladerunner/outtake.h
@@ -30,11 +30,13 @@
 namespace BladeRunner {
 
 class BladeRunnerEngine;
+class Framelimiter;
 
 class OuttakePlayer {
 	BladeRunnerEngine *_vm;
+	Framelimiter      *_framelimiter;
+
 	Graphics::Surface  _surfaceVideo;
-	uint32             _timeLast;
 
 public:
 	OuttakePlayer(BladeRunnerEngine *vm);
diff --git a/engines/bladerunner/ui/elevator.cpp b/engines/bladerunner/ui/elevator.cpp
index db3a465..d01acc2 100644
--- a/engines/bladerunner/ui/elevator.cpp
+++ b/engines/bladerunner/ui/elevator.cpp
@@ -25,6 +25,7 @@
 #include "bladerunner/actor.h"
 #include "bladerunner/bladerunner.h"
 #include "bladerunner/audio_player.h"
+#include "bladerunner/framelimiter.h"
 #include "bladerunner/game_info.h"
 #include "bladerunner/mouse.h"
 #include "bladerunner/shape.h"
@@ -44,11 +45,17 @@ Elevator::Elevator(BladeRunnerEngine *vm) {
 	_vm = vm;
 	reset();
 	_imagePicker = new UIImagePicker(vm, 8);
+	_framelimiter = new Framelimiter(_vm, Framelimiter::kDefaultFpsRate, Framelimiter::kDefaultUseDelayMillis);
 }
 
 Elevator::~Elevator() {
 	delete _imagePicker;
 	_imagePicker = nullptr;
+
+	if (_framelimiter) {
+		delete _framelimiter;
+		_framelimiter = nullptr;
+	}
 }
 
 int Elevator::activate(int elevatorId) {
@@ -187,8 +194,7 @@ int Elevator::activate(int elevatorId) {
 void Elevator::open() {
 	resetDescription();
 	_isOpen = true;
-	_timeLast = _vm->_time->currentSystem();
-	_firstTickCall = true;
+	_framelimiter->init();
 }
 
 bool Elevator::isOpen() const {
@@ -207,44 +213,36 @@ int Elevator::handleMouseDown(int x, int y) {
 
 void Elevator::tick() {
 	if (!_vm->_windowIsActive) {
-		_timeLast = _vm->_time->currentSystem();
+		_framelimiter->init();
 		return;
 	}
 
-	uint32 timeNow = _vm->_time->currentSystem();
-	// unsigned difference is intentional
-	if (timeNow - _timeLast < _vm->kUpdateFrameTimeInMs && !_firstTickCall) {
-		return;
-	}
+	if (_framelimiter->shouldExecuteScreenUpdate()) {
+		int frame = _vqaPlayer->update();
+		assert(frame >= -1);
 
-	if (_firstTickCall) {
-		_firstTickCall = false;
-	}
+		// vqaPlayer renders to _surfaceBack
+		blit(_vm->_surfaceBack, _vm->_surfaceFront);
 
-	int frame = _vqaPlayer->update();
-	assert(frame >= -1);
+		Common::Point p = _vm->getMousePos();
 
-	// vqaPlayer renders to _surfaceBack
-	blit(_vm->_surfaceBack, _vm->_surfaceFront);
+		// TODO(madmoose): BLADE.EXE has hasHoveredImage before handleMouseAction?
+		_imagePicker->handleMouseAction(p.x, p.y, false, false, false);
+		if (_imagePicker->hasHoveredImage()) {
+			_vm->_mouse->setCursor(1);
+		} else {
+			_vm->_mouse->setCursor(0);
+		}
 
-	Common::Point p = _vm->getMousePos();
+		_imagePicker->draw(_vm->_surfaceFront);
+		_vm->_mouse->draw(_vm->_surfaceFront, p.x, p.y);
 
-	// TODO(madmoose): BLADE.EXE has hasHoveredImage before handleMouseAction?
-	_imagePicker->handleMouseAction(p.x, p.y, false, false, false);
-	if (_imagePicker->hasHoveredImage()) {
-		_vm->_mouse->setCursor(1);
-	} else {
-		_vm->_mouse->setCursor(0);
-	}
-
-	_imagePicker->draw(_vm->_surfaceFront);
-	_vm->_mouse->draw(_vm->_surfaceFront, p.x, p.y);
+		_vm->_subtitles->tick(_vm->_surfaceFront);
 
-	_vm->_subtitles->tick(_vm->_surfaceFront);
-
-	_vm->blitToScreen(_vm->_surfaceFront);
+		_vm->blitToScreen(_vm->_surfaceFront);
+		_framelimiter->postScreenUpdate();
+	}
 	tickDescription();
-	_timeLast = timeNow;
 }
 
 void Elevator::buttonClick(int buttonId) {
diff --git a/engines/bladerunner/ui/elevator.h b/engines/bladerunner/ui/elevator.h
index 92afb6e..c64152c 100644
--- a/engines/bladerunner/ui/elevator.h
+++ b/engines/bladerunner/ui/elevator.h
@@ -28,12 +28,14 @@
 namespace BladeRunner {
 
 class BladeRunnerEngine;
+class Framelimiter;
 class Shape;
 class VQAPlayer;
 class UIImagePicker;
 
 class Elevator {
 	BladeRunnerEngine     *_vm;
+	Framelimiter          *_framelimiter;
 	bool                   _isOpen;
 	VQAPlayer             *_vqaPlayer;
 	int                    _buttonClicked;
@@ -43,9 +45,6 @@ class Elevator {
 	int                    _sentenceId;
 	uint32                 _timeSpeakDescriptionStart;
 
-	uint32                 _timeLast;
-	bool                   _firstTickCall;
-
 public:
 	Elevator(BladeRunnerEngine *vm);
 	~Elevator();
diff --git a/engines/bladerunner/ui/end_credits.cpp b/engines/bladerunner/ui/end_credits.cpp
index 9ff6e1d..291014c 100644
--- a/engines/bladerunner/ui/end_credits.cpp
+++ b/engines/bladerunner/ui/end_credits.cpp
@@ -29,6 +29,7 @@
 #include "bladerunner/game_constants.h"
 #include "bladerunner/ambient_sounds.h"
 #include "bladerunner/audio_speech.h"
+#include "bladerunner/framelimiter.h"
 #include "bladerunner/font.h"
 #include "bladerunner/game_info.h"
 #include "bladerunner/mouse.h"
@@ -41,9 +42,14 @@ namespace BladeRunner {
 
 EndCredits::EndCredits(BladeRunnerEngine *vm) {
 	_vm = vm;
+	_framelimiter = new Framelimiter(_vm, Framelimiter::kDefaultFpsRate, Framelimiter::kDefaultUseDelayMillis);
 }
 
 EndCredits::~EndCredits() {
+	if (_framelimiter) {
+		delete _framelimiter;
+		_framelimiter = nullptr;
+	}
 }
 
 void EndCredits::show() {
@@ -92,8 +98,7 @@ void EndCredits::show() {
 	_vm->_vqaStopIsRequested = false;
 
 	double position = 0.0;
-	uint32 timeLast = _vm->_time->currentSystem();
-	bool   firstPass = true;
+	_framelimiter->init();
 
 	while (!_vm->_vqaStopIsRequested && !_vm->shouldQuit()) {
 		if (position >= textPositions[textCount - 1]) {
@@ -104,22 +109,15 @@ void EndCredits::show() {
 		_vm->handleEvents();
 
 		if (!_vm->_windowIsActive) {
-			timeLast = _vm->_time->currentSystem();
-
+			_framelimiter->init();
 			continue;
 		}
 
-		uint32 timeNow = _vm->_time->currentSystem();
-		if (timeNow - timeLast < _vm->kUpdateFrameTimeInMs && !firstPass) {
+		if (!_framelimiter->shouldExecuteScreenUpdate()) {
 			continue;
 		}
 
-		if (firstPass) {
-			firstPass = false;
-		}
-
-		position += (double)(timeNow - timeLast) * 0.05f; // unsigned difference is intentional
-		timeLast = timeNow;
+		position += (double)(_framelimiter->getTimeOfCurrentPass() - _framelimiter->getTimeOfLastPass()) * 0.05f; // unsigned difference is intentional
 
 		_vm->_surfaceFront.fillRect(Common::Rect(640, 480), 0);
 
@@ -156,6 +154,7 @@ void EndCredits::show() {
 		_vm->_surfaceFront.fillRect(Common::Rect(0, 452, 640, 480), 0);
 
 		_vm->blitToScreen(_vm->_surfaceFront);
+		_framelimiter->postScreenUpdate();
 	}
 
 	_vm->_vqaIsPlaying = false;
diff --git a/engines/bladerunner/ui/end_credits.h b/engines/bladerunner/ui/end_credits.h
index 39086cd..6c45adf 100644
--- a/engines/bladerunner/ui/end_credits.h
+++ b/engines/bladerunner/ui/end_credits.h
@@ -26,9 +26,11 @@
 namespace BladeRunner {
 
 class BladeRunnerEngine;
+class Framelimiter;
 
 class EndCredits {
 	BladeRunnerEngine *_vm;
+	Framelimiter      *_framelimiter;
 
 public:
 	EndCredits(BladeRunnerEngine *vm);
diff --git a/engines/bladerunner/ui/esper.cpp b/engines/bladerunner/ui/esper.cpp
index 5f49830..f5257bf 100644
--- a/engines/bladerunner/ui/esper.cpp
+++ b/engines/bladerunner/ui/esper.cpp
@@ -28,6 +28,7 @@
 #include "bladerunner/bladerunner.h"
 #include "bladerunner/debugger.h"
 #include "bladerunner/decompress_lcw.h"
+#include "bladerunner/framelimiter.h"
 #include "bladerunner/font.h"
 #include "bladerunner/game_info.h"
 #include "bladerunner/mouse.h"
@@ -57,7 +58,6 @@ ESPER::ESPER(BladeRunnerEngine *vm) {
 	_isDrawingSelection = false;
 
 	_isOpen             = false;
-	_firstTickCall      = false;
 	_shapeButton        = nullptr;
 	_shapeThumbnail     = nullptr;
 	_vqaPlayerMain      = nullptr;
@@ -67,11 +67,17 @@ ESPER::ESPER(BladeRunnerEngine *vm) {
 	reset();
 
 	_buttons = new UIImagePicker(vm, kPhotoCount + 4);
+	_framelimiter = new Framelimiter(_vm, Framelimiter::kDefaultFpsRate, Framelimiter::kDefaultUseDelayMillis);
 }
 
 ESPER::~ESPER() {
 	delete _buttons;
 	reset();
+
+	if (_framelimiter) {
+		delete _framelimiter;
+		_framelimiter = nullptr;
+	}
 }
 
 void ESPER::open(Graphics::Surface *surface) {
@@ -116,8 +122,8 @@ void ESPER::open(Graphics::Surface *surface) {
 	_vqaPlayerMain->setLoop(2, -1, kLoopSetModeJustStart, nullptr, nullptr);
 
 	_isOpen = true;
-	_timeLast = _vm->_time->currentSystem();
-	_firstTickCall = true;
+	_framelimiter->init();
+
 	_flash = false;
 
 	_script = new ESPERScript(_vm);
@@ -210,16 +216,11 @@ void ESPER::handleMouseDown(int x, int y, bool mainButton) {
 
 void ESPER::tick() {
 	if (!_vm->_windowIsActive) {
-		_timeLast = _vm->_time->currentSystem();
+		_framelimiter->init();
 		return;
 	}
 
-	uint32 timeNow = _vm->_time->currentSystem();
-	// unsigned difference is intentional
-	if (timeNow - _timeLast >= _vm->kUpdateFrameTimeInMs || _firstTickCall) {
-		if (_firstTickCall) {
-			_firstTickCall = false;
-		}
+	if (_framelimiter->shouldExecuteScreenUpdate()) {
 		tickSound();
 
 		blit(_vm->_surfaceBack, _vm->_surfaceFront);
@@ -242,8 +243,7 @@ void ESPER::tick() {
 		_vm->_subtitles->tick(_vm->_surfaceFront);
 		_vm->blitToScreen(_vm->_surfaceFront);
 
-		// TODO: implement 60hz lock for smoother experience
-		_timeLast = timeNow;
+		_framelimiter->postScreenUpdate();
 	}
 
 	if (_statePhoto == kEsperPhotoStateVideoShow) {
diff --git a/engines/bladerunner/ui/esper.h b/engines/bladerunner/ui/esper.h
index 6feb2ca..818d51c 100644
--- a/engines/bladerunner/ui/esper.h
+++ b/engines/bladerunner/ui/esper.h
@@ -31,6 +31,7 @@
 namespace BladeRunner {
 
 class BladeRunnerEngine;
+class Framelimiter;
 class Font;
 class Shape;
 class VQAPlayer;
@@ -87,6 +88,7 @@ class ESPER {
 	};
 
 	BladeRunnerEngine     *_vm;
+	Framelimiter          *_framelimiter;
 	ESPERScript           *_script;
 
 	bool _isWaiting;
@@ -184,9 +186,6 @@ class ESPER {
 	int _volume3;
 	int _ambientVolume;
 
-	uint32 _timeLast;
-	bool   _firstTickCall;
-
 public:
 	ESPER(BladeRunnerEngine *vm);
 	~ESPER();
diff --git a/engines/bladerunner/ui/kia.cpp b/engines/bladerunner/ui/kia.cpp
index a40a786..501a1e7 100644
--- a/engines/bladerunner/ui/kia.cpp
+++ b/engines/bladerunner/ui/kia.cpp
@@ -26,6 +26,7 @@
 #include "bladerunner/audio_player.h"
 #include "bladerunner/bladerunner.h"
 #include "bladerunner/combat.h"
+#include "bladerunner/framelimiter.h"
 #include "bladerunner/font.h"
 #include "bladerunner/game_constants.h"
 #include "bladerunner/game_flags.h"
@@ -80,7 +81,6 @@ KIA::KIA(BladeRunnerEngine *vm) {
 	_playerPhotograph = nullptr;
 	_playerSliceModelId = -1;
 	_playerSliceModelAngle = 0.0f;
-	_timeLast = _vm->_time->currentSystem();
 	_playerActorDialogueQueuePosition = 0;
 	_playerActorDialogueQueueSize = 0;
 	_playerActorDialogueState = 0;
@@ -93,7 +93,7 @@ KIA::KIA(BladeRunnerEngine *vm) {
 
 	// original imageCount was 22. We add +1 to have a description box for objects in cut content
 	// We don't have separated cases here, for _vm->_cutContent since that causes assertion fault if
-	// loading a "restoed content" save game in a "original game" version
+	// loading a "restored content" save game in a "original game" version
 	_buttons = new UIImagePicker(_vm, 23);
 
 	_crimesSection     = new KIASectionCrimes(_vm, _vm->_playerActor->_clues);
@@ -110,6 +110,9 @@ KIA::KIA(BladeRunnerEngine *vm) {
 		_playerActorDialogueQueue[i].actorId    = -1;
 		_playerActorDialogueQueue[i].sentenceId = -1;
 	}
+
+	_framelimiter = new Framelimiter(_vm, Framelimiter::kDefaultFpsRate, Framelimiter::kDefaultUseDelayMillis);
+	_framelimiter->init();
 }
 
 KIA::~KIA() {
@@ -133,6 +136,11 @@ KIA::~KIA() {
 	delete _shapes;
 	delete _log;
 	delete _script;
+
+	if (_framelimiter) {
+		delete _framelimiter;
+		_framelimiter = nullptr;
+	}
 }
 
 void KIA::reset() {
@@ -227,18 +235,17 @@ bool KIA::isOpen() const {
 
 void KIA::tick() {
 	if (!isOpen()) {
-		_timeLast = _vm->_time->currentSystem();
 		return;
 	}
 
-	uint32 timeNow = _vm->_time->currentSystem();
-	// unsigned difference is intentional
-	uint32 timeDiff = timeNow - _timeLast;
-
-	if (timeDiff < _vm->kUpdateFrameTimeInMs) {
+	if (!_framelimiter->shouldExecuteScreenUpdate()) {
 		return;
 	}
 
+	uint32 timeNow = _framelimiter->getTimeOfCurrentPass();
+	// unsigned difference is intentional
+	uint32 timeDiff = timeNow - _framelimiter->getTimeOfLastPass();
+
 	if (_playerActorDialogueQueueSize == _playerActorDialogueQueuePosition) {
 		_playerActorDialogueState = 0;
 	} else if (_playerActorDialogueState == 0) {
@@ -389,7 +396,7 @@ void KIA::tick() {
 
 	_vm->blitToScreen(_vm->_surfaceFront);
 
-	_timeLast = timeNow;
+	_framelimiter->postScreenUpdate();
 }
 
 void KIA::resume() {
@@ -684,7 +691,8 @@ void KIA::init() {
 	playerReset();
 	_playerVqaFrame = 0;
 	_playerVqaTimeLast = _vm->_time->currentSystem();
-	_timeLast = _vm->_time->currentSystem();
+
+	_framelimiter->init();
 
 	if (_vm->_gameFlags->query(kFlagKIAPrivacyAddon) && !_vm->_gameFlags->query(kFlagKIAPrivacyAddonIntro)) {
 		_vm->_gameFlags->set(kFlagKIAPrivacyAddonIntro);
diff --git a/engines/bladerunner/ui/kia.h b/engines/bladerunner/ui/kia.h
index 2a87f5d..fdc4d8e 100644
--- a/engines/bladerunner/ui/kia.h
+++ b/engines/bladerunner/ui/kia.h
@@ -34,6 +34,7 @@ struct KeyState;
 namespace BladeRunner {
 
 class BladeRunnerEngine;
+class Framelimiter;
 class KIALog;
 class KIAScript;
 class KIASectionBase;
@@ -75,6 +76,7 @@ class KIA {
 	};
 
 	BladeRunnerEngine *_vm;
+	Framelimiter      *_framelimiter;
 
 	int _transitionId;
 
@@ -87,7 +89,7 @@ class KIA {
 	int                _playerSliceModelId;
 	float              _playerSliceModelAngle;
 	Graphics::Surface  _playerImage;
-	uint32             _timeLast;
+//	uint32             _timeLast;
 
 	ActorDialogueQueueEntry _playerActorDialogueQueue[kPlayerActorDialogueQueueCapacity];
 	int                     _playerActorDialogueQueuePosition;
diff --git a/engines/bladerunner/ui/spinner.cpp b/engines/bladerunner/ui/spinner.cpp
index 2b084c9..85d2c37 100644
--- a/engines/bladerunner/ui/spinner.cpp
+++ b/engines/bladerunner/ui/spinner.cpp
@@ -28,6 +28,7 @@
 #include "bladerunner/ambient_sounds.h"
 #include "bladerunner/game_info.h"
 #include "bladerunner/subtitles.h"
+#include "bladerunner/framelimiter.h"
 #include "bladerunner/game_constants.h"
 #include "bladerunner/mouse.h"
 #include "bladerunner/savefile.h"
@@ -48,6 +49,7 @@ Spinner::Spinner(BladeRunnerEngine *vm) {
 	reset();
 	_imagePicker = new UIImagePicker(vm, kSpinnerDestinations);
 	_vqaPlayer = nullptr;
+	_framelimiter = new Framelimiter(_vm, Framelimiter::kDefaultFpsRate, Framelimiter::kDefaultUseDelayMillis);
 }
 
 Spinner::~Spinner() {
@@ -59,6 +61,11 @@ Spinner::~Spinner() {
 		_vqaPlayer->close();
 		delete _vqaPlayer;
 	}
+
+	if (_framelimiter) {
+		delete _framelimiter;
+		_framelimiter = nullptr;
+	}
 }
 
 void Spinner::setSelectableDestinationFlag(int destination, bool selectable) {
@@ -79,9 +86,8 @@ int Spinner::chooseDestination(int loopId, bool immediately) {
 	}
 
 	if (loopId < 0) {
-		_isOpen = true;
-		_timeLast = _vm->_time->currentSystem();
-		_firstTickCall = true;
+		// call Spinner:open()
+		open();
 	} else {
 		_vm->playerLosesControl();
 		_vm->_scene->loopStartSpecial(kSceneLoopModeSpinner, loopId, immediately);
@@ -231,8 +237,7 @@ void Spinner::mouseUpCallback(int destinationImage, void *self) {
 
 void Spinner::open() {
 	_isOpen = true;
-	_timeLast = _vm->_time->currentSystem();
-	_firstTickCall = true;
+	_framelimiter->init();
 }
 
 bool Spinner::isOpen() const {
@@ -251,45 +256,39 @@ int Spinner::handleMouseDown(int x, int y) {
 
 void Spinner::tick() {
 	if (!_vm->_windowIsActive) {
-		_timeLast = _vm->_time->currentSystem();
+		_framelimiter->init();
 		return;
 	}
 
-	uint32 timeNow = _vm->_time->currentSystem();
-	// unsigned difference is intentional
-	if (timeNow - _timeLast < _vm->kUpdateFrameTimeInMs && !_firstTickCall) {
-		return;
-	}
+	if (_framelimiter->shouldExecuteScreenUpdate()) {
+		int frame = _vqaPlayer->update();
+		assert(frame >= -1);
 
-	if (_firstTickCall) {
-		_firstTickCall = false;
-	}
+		// vqaPlayer renders to _surfaceBack
+		blit(_vm->_surfaceBack, _vm->_surfaceFront);
 
-	int frame = _vqaPlayer->update();
-	assert(frame >= -1);
+		Common::Point p = _vm->getMousePos();
+		_imagePicker->handleMouseAction(p.x, p.y, false, false, false);
+		if (_imagePicker->hasHoveredImage()) {
+			_vm->_mouse->setCursor(1);
+		} else {
+			_vm->_mouse->setCursor(0);
+		}
+		_imagePicker->draw(_vm->_surfaceFront);
+		_vm->_mouse->draw(_vm->_surfaceFront, p.x, p.y);
+		_imagePicker->drawTooltip(_vm->_surfaceFront, p.x, p.y);
 
-	// vqaPlayer renders to _surfaceBack
-	blit(_vm->_surfaceBack, _vm->_surfaceFront);
+		if (_vm->_cutContent) {
+			_vm->_subtitles->tick(_vm->_surfaceFront);
+		}
+		_vm->blitToScreen(_vm->_surfaceFront);
+		_framelimiter->postScreenUpdate();
 
-	Common::Point p = _vm->getMousePos();
-	_imagePicker->handleMouseAction(p.x, p.y, false, false, false);
-	if (_imagePicker->hasHoveredImage()) {
-		_vm->_mouse->setCursor(1);
-	} else {
-		_vm->_mouse->setCursor(0);
 	}
-	_imagePicker->draw(_vm->_surfaceFront);
-	_vm->_mouse->draw(_vm->_surfaceFront, p.x, p.y);
-	_imagePicker->drawTooltip(_vm->_surfaceFront, p.x, p.y);
 
 	if (_vm->_cutContent) {
-		_vm->_subtitles->tick(_vm->_surfaceFront);
-	}
-	_vm->blitToScreen(_vm->_surfaceFront);
-	if (_vm->_cutContent) {
 		tickDescription();
 	}
-	_timeLast = timeNow;
 }
 
 void Spinner::setSelectedDestination(int destination) {
@@ -302,7 +301,6 @@ void Spinner::reset() {
 	}
 
 	_isOpen = false;
-	_firstTickCall = false;
 	_destinations = nullptr;
 	_selectedDestination = -1;
 	_imagePicker = nullptr;
diff --git a/engines/bladerunner/ui/spinner.h b/engines/bladerunner/ui/spinner.h
index 0477595..78c2943 100644
--- a/engines/bladerunner/ui/spinner.h
+++ b/engines/bladerunner/ui/spinner.h
@@ -29,6 +29,7 @@
 namespace BladeRunner {
 
 class BladeRunnerEngine;
+class Framelimiter;
 class SaveFileReadStream;
 class SaveFileWriteStream;
 class Shape;
@@ -44,6 +45,7 @@ class Spinner {
 	};
 
 	BladeRunnerEngine      *_vm;
+	Framelimiter           *_framelimiter;
 	bool                    _isDestinationSelectable[kSpinnerDestinations];
 	bool                    _isOpen;
 	VQAPlayer              *_vqaPlayer;
@@ -56,9 +58,6 @@ class Spinner {
 	int                    _sentenceId;
 	uint32                 _timeSpeakDescriptionStart;
 
-	uint32                 _timeLast;
-	bool                   _firstTickCall;
-
 public:
 	Spinner(BladeRunnerEngine *vm);
 	~Spinner();
diff --git a/engines/bladerunner/ui/vk.cpp b/engines/bladerunner/ui/vk.cpp
index 06056c3..7e24b79 100644
--- a/engines/bladerunner/ui/vk.cpp
+++ b/engines/bladerunner/ui/vk.cpp
@@ -27,6 +27,7 @@
 #include "bladerunner/audio_player.h"
 #include "bladerunner/bladerunner.h"
 #include "bladerunner/combat.h"
+#include "bladerunner/framelimiter.h"
 #include "bladerunner/font.h"
 #include "bladerunner/game_constants.h"
 #include "bladerunner/game_flags.h"
@@ -53,10 +54,16 @@ VK::VK(BladeRunnerEngine *vm) {
 	_vm = vm;
 
 	reset();
+	_framelimiter = new Framelimiter(_vm, Framelimiter::kDefaultFpsRate, Framelimiter::kDefaultUseDelayMillis);
 }
 
 VK::~VK() {
 	reset();
+
+	if (_framelimiter) {
+		delete _framelimiter;
+		_framelimiter = nullptr;
+	}
 }
 
 void VK::open(int actorId, int calibrationRatio) {
@@ -126,8 +133,7 @@ void VK::open(int actorId, int calibrationRatio) {
 	}
 
 	_isOpen = true;
-	_timeLast = _vm->_time->currentSystem();
-	_firstTickCall = true;
+	_framelimiter->init();
 
 	_script = new VKScript(_vm);
 
@@ -193,12 +199,7 @@ void VK::close() {
 
 void VK::tick() {
 
-	uint32 timeNow = _vm->_time->currentSystem();
-	// unsigned difference is intentional
-	if (timeNow - _timeLast >= _vm->kUpdateFrameTimeInMs || _firstTickCall) {
-		if (_firstTickCall) {
-			_firstTickCall = false;
-		}
+	if (_framelimiter->shouldExecuteScreenUpdate()) {
 		int mouseX, mouseY;
 		_vm->_mouse->getXY(&mouseX, &mouseY);
 		if (!_vm->_mouse->isDisabled()) {
@@ -220,7 +221,7 @@ void VK::tick() {
 		_vm->_subtitles->tick(_vm->_surfaceFront);
 
 		_vm->blitToScreen(_vm->_surfaceFront);
-		_timeLast = timeNow;
+		_framelimiter->postScreenUpdate();
 	}
 
 	// unsigned difference is intentional
diff --git a/engines/bladerunner/ui/vk.h b/engines/bladerunner/ui/vk.h
index 8593e6b..a880204 100644
--- a/engines/bladerunner/ui/vk.h
+++ b/engines/bladerunner/ui/vk.h
@@ -31,6 +31,7 @@
 namespace BladeRunner {
 
 class BladeRunnerEngine;
+class Framelimiter;
 class VKScript;
 class Shape;
 class UIImagePicker;
@@ -47,6 +48,7 @@ class VK {
 	};
 
 	BladeRunnerEngine *_vm;
+	Framelimiter      *_framelimiter;
 
 	VKScript *_script;
 
@@ -124,9 +126,6 @@ class VK {
 	uint32 _timeNextEyeLineStepStart;
 	uint32 _timeNextEyeLineStart;
 
-	uint32 _timeLast;
-	bool   _firstTickCall;
-
 public:
 	VK(BladeRunnerEngine *vm);
 	~VK();





More information about the Scummvm-git-logs mailing list