Skip to content

Commit

Permalink
BLADERUNNER: Framelimiter simplification
Browse files Browse the repository at this point in the history
  • Loading branch information
peterkohaut committed Sep 4, 2019
1 parent cb88db6 commit 89384b9
Show file tree
Hide file tree
Showing 18 changed files with 115 additions and 324 deletions.
26 changes: 8 additions & 18 deletions engines/bladerunner/bladerunner.cpp
Expand Up @@ -169,6 +169,7 @@ BladeRunnerEngine::BladeRunnerEngine(OSystem *syst, const ADGameDescription *des
_obstacles = nullptr;
_sceneScript = nullptr;
_time = nullptr;
_framelimiter = nullptr;
_gameInfo = nullptr;
_waypoints = nullptr;
_gameVars = nullptr;
Expand Down Expand Up @@ -217,7 +218,6 @@ BladeRunnerEngine::BladeRunnerEngine(OSystem *syst, const ADGameDescription *des
_actors[i] = nullptr;
}
_debugger = nullptr;
_mainLoopFrameLimiter = nullptr;

walkingReset();

Expand Down Expand Up @@ -313,7 +313,6 @@ void BladeRunnerEngine::pauseEngineIntern(bool pause) {
}

Common::Error BladeRunnerEngine::run() {

Common::Array<Common::String> missingFiles;
if (!checkFiles(missingFiles)) {
Common::String missingFileStr = "";
Expand All @@ -340,7 +339,6 @@ Common::Error BladeRunnerEngine::run() {
return Common::Error(Common::kUnknownError, _("Failed to initialize resources"));
}


// improvement: Use a do-while() loop to handle the normal end-game state
// so that the game won't exit abruptly after end credits
do {
Expand Down Expand Up @@ -486,6 +484,8 @@ bool BladeRunnerEngine::startup(bool hasSavegames) {

_time = new Time(this);

_framelimiter = new Framelimiter(this);

// Try to load the SUBTITLES.MIX first, before Startup.MIX
// allows overriding any identically named resources (such as the original font files and as a bonus also the TRE files for the UI and dialogue menu)
_subtitles = new Subtitles(this);
Expand Down Expand Up @@ -529,8 +529,6 @@ 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);
Expand Down Expand Up @@ -898,6 +896,9 @@ void BladeRunnerEngine::shutdown() {
_subtitles = nullptr;
}

delete _framelimiter;
_framelimiter = nullptr;

delete _time;
_time = nullptr;

Expand Down Expand Up @@ -932,11 +933,6 @@ void BladeRunnerEngine::shutdown() {

delete _screenEffects;
_screenEffects = nullptr;

if (_mainLoopFrameLimiter) {
delete _mainLoopFrameLimiter;
_mainLoopFrameLimiter = nullptr;
}
}

bool BladeRunnerEngine::loadSplash() {
Expand Down Expand Up @@ -965,7 +961,6 @@ bool BladeRunnerEngine::isMouseButtonDown() const {

void BladeRunnerEngine::gameLoop() {
_gameIsRunning = true;
_mainLoopFrameLimiter->init();
do {
if (_playerDead) {
playerDied();
Expand All @@ -980,7 +975,6 @@ void BladeRunnerEngine::gameTick() {
handleEvents();

if (!_gameIsRunning || !_windowIsActive) {
_mainLoopFrameLimiter->init();
return;
}

Expand All @@ -989,7 +983,6 @@ void BladeRunnerEngine::gameTick() {
Common::Error runtimeError = Common::Error(Common::kUnknownError, _("A required game resource was not found"));
GUI::MessageDialog dialog(runtimeError.getDesc());
dialog.runModal();
_mainLoopFrameLimiter->init();
return;
}
}
Expand Down Expand Up @@ -1124,12 +1117,8 @@ void BladeRunnerEngine::gameTick() {
// Without this condition the game may flash back to the game screen
// between and ending outtake and the end credits.
if (!_gameOver) {
if (_mainLoopFrameLimiter->shouldExecuteScreenUpdate()) {
blitToScreen(_surfaceFront);
_mainLoopFrameLimiter->postScreenUpdate();
}
blitToScreen(_surfaceFront);
}

}

void BladeRunnerEngine::actorsUpdate() {
Expand Down Expand Up @@ -2253,6 +2242,7 @@ void BladeRunnerEngine::ISez(const Common::String &str) {
}

void BladeRunnerEngine::blitToScreen(const Graphics::Surface &src) const {
_framelimiter->wait();
_system->copyRectToScreen(src.getPixels(), src.pitch, 0, 0, src.w, src.h);
_system->updateScreen();
}
Expand Down
3 changes: 1 addition & 2 deletions engines/bladerunner/bladerunner.h
Expand Up @@ -166,6 +166,7 @@ class BladeRunnerEngine : public Engine {
SuspectsDatabase *_suspectsDatabase;
Time *_time;
View *_view;
Framelimiter *_framelimiter;
VK *_vk;
Waypoints *_waypoints;
int *_gameVars;
Expand Down Expand Up @@ -197,8 +198,6 @@ class BladeRunnerEngine : public Engine {
Common::CosineTable *_cosTable1024;
Common::SineTable *_sinTable1024;

Framelimiter *_mainLoopFrameLimiter;

bool _isWalkingInterruptible;
bool _interruptWalking;
bool _playerActorIdle;
Expand Down
112 changes: 24 additions & 88 deletions engines/bladerunner/framelimiter.cpp
Expand Up @@ -24,112 +24,48 @@

#include "bladerunner/bladerunner.h"
#include "bladerunner/time.h"

#include "common/debug.h"
#include "common/system.h"

namespace BladeRunner {

Framelimiter::Framelimiter(BladeRunnerEngine *vm, FramelimiterFpsRate framerateMode, bool useDelayMs) {
Framelimiter::Framelimiter(BladeRunnerEngine *vm, uint fps) {
_vm = vm;

// // FUTURE: The frame limiter is disabled when vsync is enabled.
// _enabled = !_system->getFeatureState(OSystem::kFeatureVSync);
_enabled = true;
_useDelayMs = useDelayMs;
reset();

_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:
if (fps > 0) {
_enabled = true;
_speedLimitMs = 1000 / fps;
} else {
_enabled = false;
break;
}

if (_enabled) {
_speedLimitMs = 1000 / CLIP<uint32>(framerate, 1, 120);
}

reset();
}

Framelimiter::~Framelimiter() { }

void Framelimiter::init(bool forceScreenUpdate) {
reset();
_timeOfLastPass = _vm->_time->currentSystem();
_forceScreenUpdate = forceScreenUpdate;
}

uint32 Framelimiter::getLastFrameDuration() const {
return _lastFrameDurationMs;
_timeFrameStart = _vm->_time->currentSystem();
}

uint32 Framelimiter::getTimeOfCurrentPass() const {
return _timeOfCurrentPass;
}

uint32 Framelimiter::getTimeOfLastPass() const {
return _timeOfLastPass;
}
void Framelimiter::wait() {
// TODO: when vsync will be supported, use it

bool Framelimiter::shouldExecuteScreenUpdate() {
bool shouldUpdateScreen = true;
_timeOfCurrentPass = _vm->_time->currentSystem();
if (_enabled) {
if (_useDelayMs) {
// _timeOfCurrentPass is used to calculate the duration that the current frame is on screen so far
uint32 frameDuration = _timeOfCurrentPass - _startFrameTime;
if (frameDuration < _speedLimitMs) {
_vm->_system->delayMillis(_speedLimitMs - frameDuration);
// cheaper than calling _vm->_time->currentSystem() again
_timeOfCurrentPass += (_speedLimitMs - frameDuration);
}
}

shouldUpdateScreen = ((_timeOfCurrentPass - _timeOfLastPass) >= _speedLimitMs) || _forceScreenUpdate || _useDelayMs;

if (shouldUpdateScreen) {
if (_forceScreenUpdate) {
_forceScreenUpdate = false;
}
_lastFrameDurationMs = _timeOfCurrentPass - _startFrameTime;
_startFrameTime = _timeOfCurrentPass;
}
if (!_enabled) {
return;
}
return shouldUpdateScreen;
}

void Framelimiter::postScreenUpdate() {
_timeOfLastPass = _timeOfCurrentPass;
// if (_enabled) {
// // for debug purposes, this calculates the time between deciding to draw the frame, and the time after drawing the update to the screen
// uint32 endFrameTime = _vm->_time->currentSystem();
// uint32 frameDuration = endFrameTime - _startFrameTime;
// }
uint32 timeNow = _vm->_time->currentSystem();
uint32 frameDuration = timeNow - _timeFrameStart;
if (frameDuration < _speedLimitMs) {
uint32 wait = _speedLimitMs - frameDuration;
_vm->_system->delayMillis(wait);
timeNow += wait;
}
// debug("frametime %i ms", timeNow - _timeFrameStart);
// using _vm->_time->currentSystem() here is slower and causes some shutters
_timeFrameStart = timeNow;
}

void Framelimiter::reset() {
_forceScreenUpdate = false;
_timeOfLastPass = 0u;
_timeOfCurrentPass = 0u;
_startFrameTime = 0u;
_lastFrameDurationMs = _speedLimitMs;
_timeFrameStart = 0u;
}

} // End of namespace BladeRunner
41 changes: 4 additions & 37 deletions engines/bladerunner/framelimiter.h
Expand Up @@ -27,56 +27,23 @@

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 = true;

private:
BladeRunnerEngine *_vm;

bool _forceScreenUpdate;
bool _enabled;
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
uint32 _timeFrameStart;

public:
Framelimiter(BladeRunnerEngine *vm, FramelimiterFpsRate framerateMode, bool useDelayMs);
~Framelimiter();

// void pause(bool pause);

void init(bool forceScreenUpdate = true);
uint32 getLastFrameDuration() const;
uint32 getTimeOfCurrentPass() const;
uint32 getTimeOfLastPass() const;
Framelimiter(BladeRunnerEngine *vm, uint fps = 60);

bool shouldExecuteScreenUpdate();
void postScreenUpdate();
void wait();

private:
void reset();
Expand Down
15 changes: 0 additions & 15 deletions engines/bladerunner/outtake.cpp
Expand Up @@ -24,7 +24,6 @@

#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"
Expand All @@ -38,16 +37,10 @@ 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) {
Expand Down Expand Up @@ -77,17 +70,10 @@ void OuttakePlayer::play(const Common::String &name, bool noLocalization, int co
_vm->_vqaIsPlaying = true;
_vm->_vqaStopIsRequested = false;

_framelimiter->init();

while (!_vm->_vqaStopIsRequested && !_vm->shouldQuit()) {
_vm->handleEvents();

if (!_vm->_windowIsActive) {
_framelimiter->init();
continue;
}

if (!_framelimiter->shouldExecuteScreenUpdate()) {
continue;
}

Expand All @@ -102,7 +88,6 @@ void OuttakePlayer::play(const Common::String &name, bool noLocalization, int co
_vm->_subtitles->tickOuttakes(_vm->_surfaceFront);
_vm->blitToScreen(_vm->_surfaceFront);
}
_framelimiter->postScreenUpdate();
}

_vm->_vqaIsPlaying = false;
Expand Down
2 changes: 0 additions & 2 deletions engines/bladerunner/outtake.h
Expand Up @@ -30,11 +30,9 @@
namespace BladeRunner {

class BladeRunnerEngine;
class Framelimiter;

class OuttakePlayer {
BladeRunnerEngine *_vm;
Framelimiter *_framelimiter;

Graphics::Surface _surfaceVideo;

Expand Down

0 comments on commit 89384b9

Please sign in to comment.