From fd7fd3a18b7310aef8203ef5be2b37a9fc3eaed7 Mon Sep 17 00:00:00 2001 From: Thanasis Antoniou Date: Sun, 1 Sep 2019 16:39:03 +0300 Subject: BLADERUNNER: Isolate new frame limiter code in new class --- engines/bladerunner/bladerunner.cpp | 26 ++++--- engines/bladerunner/bladerunner.h | 5 +- engines/bladerunner/framelimiter.cpp | 130 +++++++++++++++++++++++++++++++++ engines/bladerunner/framelimiter.h | 91 +++++++++++++++++++++++ engines/bladerunner/module.mk | 1 + engines/bladerunner/outtake.cpp | 23 +++--- engines/bladerunner/outtake.h | 4 +- engines/bladerunner/ui/elevator.cpp | 58 +++++++-------- engines/bladerunner/ui/elevator.h | 5 +- engines/bladerunner/ui/end_credits.cpp | 23 +++--- engines/bladerunner/ui/end_credits.h | 2 + engines/bladerunner/ui/esper.cpp | 24 +++--- engines/bladerunner/ui/esper.h | 5 +- engines/bladerunner/ui/kia.cpp | 28 ++++--- engines/bladerunner/ui/kia.h | 4 +- engines/bladerunner/ui/spinner.cpp | 64 ++++++++-------- engines/bladerunner/ui/spinner.h | 5 +- engines/bladerunner/ui/vk.cpp | 19 ++--- engines/bladerunner/ui/vk.h | 5 +- 19 files changed, 379 insertions(+), 143 deletions(-) create mode 100644 engines/bladerunner/framelimiter.cpp create mode 100644 engines/bladerunner/framelimiter.h (limited to 'engines/bladerunner') diff --git a/engines/bladerunner/bladerunner.cpp b/engines/bladerunner/bladerunner.cpp index 9262d4129f..b73afdd8b8 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 8aee6b6416..ebc8bd06b2 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 0000000000..08cc9fc99b --- /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(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 0000000000..66ee0f3b38 --- /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 e52f8e5bb1..6be142d441 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 ba02bda21e..2f49099455 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 fc202736fa..0305e80554 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 db3a465ee4..d01acc21ed 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 92afb6e2b7..c64152c3fc 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 9ff6e1dbce..291014c5a3 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 39086cd796..6c45adfd60 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 5f49830f44..f5257bf694 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 6feb2cae2d..818d51ca44 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 a40a78670e..501a1e7cf2 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 2a87f5df6a..fdc4d8ef15 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 2b084c9a2c..85d2c373bb 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 0477595ef4..78c2943eb2 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 06056c33dd..7e24b798b6 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 8593e6b1b3..a8802046e3 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(); -- cgit v1.2.3