From 53d6a4f831c9e7c7de594cdaed3c8546b41ea2e2 Mon Sep 17 00:00:00 2001 From: sylvaintv Date: Tue, 8 Mar 2011 00:51:21 +0100 Subject: TOON: Decrease CPU usage Added dirty rects Reduced the max FPS from 60 to 30 --- engines/toon/anim.cpp | 6 ++ engines/toon/font.cpp | 6 ++ engines/toon/movie.cpp | 2 + engines/toon/picture.cpp | 38 ++++++++++++ engines/toon/picture.h | 1 + engines/toon/script_func.cpp | 2 +- engines/toon/toon.cpp | 136 ++++++++++++++++++++++++++++++++++++++++--- engines/toon/toon.h | 9 +++ 8 files changed, 190 insertions(+), 10 deletions(-) diff --git a/engines/toon/anim.cpp b/engines/toon/anim.cpp index 2e63d89f37..6d0d75f077 100644 --- a/engines/toon/anim.cpp +++ b/engines/toon/anim.cpp @@ -151,6 +151,8 @@ void Animation::drawFrame(Graphics::Surface &surface, int32 frame, int32 xx, int int32 offsX = 0; int32 offsY = 0; + _vm->addDirtyRect(xx + _x1 + _frames[frame]._x1, yy + _y1 + _frames[frame]._y1, xx + rectX + _x1 + _frames[frame]._x1 , yy + rectY + _y1 + _frames[frame]._y1); + if (xx + _x1 + _frames[frame]._x1 < 0) { offsX = -(xx + _x1 + _frames[frame]._x1); } @@ -212,6 +214,7 @@ void Animation::drawFrameWithMaskAndScale(Graphics::Surface &surface, int32 fram int32 finalWidth = rectX * scale / 1024; int32 finalHeight = rectY * scale / 1024; + // compute final x1,y1,x2,y2 int32 xx1 = xx + _x1 + _frames[frame]._x1 * scale / 1024; int32 yy1 = yy + _y1 + _frames[frame]._y1 * scale / 1024; @@ -221,6 +224,9 @@ void Animation::drawFrameWithMaskAndScale(Graphics::Surface &surface, int32 fram // Strangerke - Commented (not used) // int32 h = _frames[frame]._y2 - _frames[frame]._y1; + _vm->addDirtyRect(xx1,yy1,xx2,yy2); + + int32 destPitch = surface.pitch; int32 destPitchMask = mask->getWidth(); uint8 *c = _frames[frame]._data; diff --git a/engines/toon/font.cpp b/engines/toon/font.cpp index 8455ca7b61..53786f8617 100644 --- a/engines/toon/font.cpp +++ b/engines/toon/font.cpp @@ -81,6 +81,8 @@ void FontRenderer::renderText(int32 x, int32 y, Common::String origText, int32 m x -= xx / 2; } + _vm->addDirtyRect(x,y,x+xx,y+yy); + int32 curX = x; int32 curY = y; int32 height = 0; @@ -270,9 +272,13 @@ void FontRenderer::renderMultiLineText(int32 x, int32 y, Common::String origText int32 curX = x; int32 curY = y; + + for (int32 i = 0; i < numLines; i++) { const byte *line = lines[i]; curX = x - lineSize[i] / 2; + _vm->addDirtyRect(curX + _vm->state()->_currentScrollValue, y, curX + lineSize[i] + _vm->state()->_currentScrollValue, curY + height); + while (*line) { byte curChar = textToFont(*line); if (curChar != 32) _currentFont->drawFontFrame(_vm->getMainSurface(), curChar, curX + _vm->state()->_currentScrollValue, curY, _currentFontColor); diff --git a/engines/toon/movie.cpp b/engines/toon/movie.cpp index bf4b6639fa..68c09fb586 100644 --- a/engines/toon/movie.cpp +++ b/engines/toon/movie.cpp @@ -135,11 +135,13 @@ bool Movie::playVideo(bool isFirstIntroVideo) { Common::Event event; while (_vm->getSystem()->getEventManager()->pollEvent(event)) if ((event.type == Common::EVENT_KEYDOWN && event.kbd.keycode == Common::KEYCODE_ESCAPE)) { + _vm->dirtyAllScreen(); return false; } _vm->getSystem()->delayMillis(10); } + _vm->dirtyAllScreen(); return !_vm->shouldQuit(); } diff --git a/engines/toon/picture.cpp b/engines/toon/picture.cpp index 18e6a8cf7f..fce711717a 100644 --- a/engines/toon/picture.cpp +++ b/engines/toon/picture.cpp @@ -187,6 +187,44 @@ void Picture::drawMask(Graphics::Surface &surface, int32 x, int32 y, int32 dx, i } } +void Picture::drawWithRectList(Graphics::Surface& surface, int32 x, int32 y, int32 dx, int32 dy, Common::Array& rectArray) { + + int32 rx = MIN(_width, surface.w - x); + int32 ry = MIN(_height, surface.h - y); + + if (rx < 0 || ry < 0) + return; + + int32 destPitch = surface.pitch; + int32 srcPitch = _width; + + for (uint32 i = 0; i < rectArray.size(); i++) { + + Common::Rect rect = rectArray[i]; + + int32 fillRx = MIN(rx, rect.right - rect.left); + int32 fillRy = MIN(ry, rect.bottom - rect.top); + + uint8 *c = _data + _width * (dy + rect.top) + (dx + rect.left); + uint8 *curRow = (uint8 *)surface.pixels + (y + rect.top) * destPitch + (x + rect.left); + + for (int32 yy = 0; yy < fillRy; yy++) { + uint8 *curSrc = c; + uint8 *cur = curRow; + for (int32 xx = 0; xx < fillRx; xx++) { + *cur = *curSrc; + curSrc++; + cur++; + } + curRow += destPitch; + c += srcPitch; + } + + } + + +} + void Picture::draw(Graphics::Surface &surface, int32 x, int32 y, int32 dx, int32 dy) { debugC(6, kDebugPicture, "draw(surface, %d, %d, %d, %d)", x, y, dx, dy); diff --git a/engines/toon/picture.h b/engines/toon/picture.h index 1b0fd7f550..6aca408364 100644 --- a/engines/toon/picture.h +++ b/engines/toon/picture.h @@ -44,6 +44,7 @@ public: bool loadPicture(Common::String file, bool totalPalette = false); void setupPalette(); void draw(Graphics::Surface &surface, int32 x, int32 y, int32 dx, int32 dy); + void drawWithRectList(Graphics::Surface& surface, int32 x, int32 y, int32 dx, int32 dy, Common::Array& rectArray); void drawMask(Graphics::Surface &surface, int32 x, int32 y, int32 dx, int32 dy); void drawLineOnMask(int32 x, int32 y, int32 x2, int32 y2, bool walkable); void floodFillNotWalkableOnMask(int32 x, int32 y); diff --git a/engines/toon/script_func.cpp b/engines/toon/script_func.cpp index adf3a1c9cc..84225ec76b 100644 --- a/engines/toon/script_func.cpp +++ b/engines/toon/script_func.cpp @@ -1028,7 +1028,7 @@ int32 ScriptFunc::sys_Cmd_Draw_Scene_Anim_WSA_Frame(EMCState *state) { else if (animId == 20 || animId == 15 || animId == 21 || animId == 16 || animId == 17 || animId == 18) _vm->pauseSceneAnimationScript(_vm->getCurrentUpdatingSceneAnimation(), 1); else if (animId == 9) { - _vm->pauseSceneAnimationScript(_vm->getCurrentUpdatingSceneAnimation(), 6); + _vm->pauseSceneAnimationScript(_vm->getCurrentUpdatingSceneAnimation(), 3); } } diff --git a/engines/toon/toon.cpp b/engines/toon/toon.cpp index cc694cbe74..b4335d7afd 100644 --- a/engines/toon/toon.cpp +++ b/engines/toon/toon.cpp @@ -378,10 +378,21 @@ void ToonEngine::updateTimer(int32 timeIncrement) { } void ToonEngine::render() { - if (_gameState->_inCutaway) - _currentCutaway->draw(*_mainSurface, 0, 0, 0, 0); - else - _currentPicture->draw(*_mainSurface, 0, 0, 0, 0); + + if (_dirtyAll) { + if (_gameState->_inCutaway) + _currentCutaway->draw(*_mainSurface, 0, 0, 0, 0); + else + _currentPicture->draw(*_mainSurface, 0, 0, 0, 0); + _dirtyRects.push_back(Common::Rect(0, 0, 1280, 400)); + } else { + if (_gameState->_inCutaway) + _currentCutaway->drawWithRectList(*_mainSurface, 0, 0, 0, 0, _dirtyRects); + else + _currentPicture->drawWithRectList(*_mainSurface, 0, 0, 0, 0, _dirtyRects); + } + + clearDirtyRects(); //_currentMask->drawMask(*_mainSurface,0,0,0,0); _animationManager->render(); @@ -422,8 +433,8 @@ void ToonEngine::render() { // add a little sleep here int32 newMillis = (int32)_system->getMillis(); int32 sleepMs = 1; // Minimum delay to allow thread scheduling - if ((newMillis - _lastRenderTime) < _tickLength) - sleepMs = _tickLength - (newMillis - _lastRenderTime); + if ((newMillis - _lastRenderTime) < _tickLength * 2) + sleepMs = _tickLength * 2 - (newMillis - _lastRenderTime); assert(sleepMs >= 0); _system->delayMillis(sleepMs); _lastRenderTime = _system->getMillis(); @@ -484,7 +495,51 @@ void ToonEngine::copyToVirtualScreen(bool updateScreen) { _cursorAnimationInstance->setPosition(_mouseX - 40 + state()->_currentScrollValue - _cursorOffsetX, _mouseY - 40 - _cursorOffsetY, 0, false); _cursorAnimationInstance->render(); } - _system->copyRectToScreen((byte *)_mainSurface->pixels + state()->_currentScrollValue, 1280, 0, 0, 640, 400); + + + // Handle dirty rects here + static int32 lastScroll = 0; + + if (_dirtyAll || _gameState->_currentScrollValue != lastScroll) { + // we have to refresh everything in case of scrolling. + _system->copyRectToScreen((byte *)_mainSurface->pixels + state()->_currentScrollValue, 1280, 0, 0, 640, 400); + } else { + + int32 offX = 0; + for (uint i = 0; i < _oldDirtyRects.size(); i++) { + Common::Rect rect = _oldDirtyRects[i]; + rect.translate(-state()->_currentScrollValue,0); + offX = 0; + if(rect.right <= 0) + continue; + if (rect.left < 0) { + offX = -rect.left; + rect.left = 0; + } + rect.clip(640, 400); + if (rect.left >= 0 && rect.top >= 0 && rect.right - rect.left > 0 && rect.bottom - rect.top > 0) { + _system->copyRectToScreen((byte *)_mainSurface->pixels + _oldDirtyRects[i].left + offX + _oldDirtyRects[i].top * 1280, 1280, rect.left , rect.top, rect.right - rect.left, rect.bottom - rect.top); + } + } + + for (uint i = 0; i < _dirtyRects.size(); i++) { + Common::Rect rect = _dirtyRects[i]; + rect.translate(-state()->_currentScrollValue,0); + offX = 0; + if (rect.right <= 0) + continue; + if (rect.left < 0) { + offX = -rect.left; + rect.left = 0; + } + rect.clip(640, 400); + if (rect.left >= 0 && rect.top >= 0 && rect.right - rect.left > 0 && rect.bottom - rect.top > 0) { + _system->copyRectToScreen((byte *)_mainSurface->pixels + _dirtyRects[i].left + offX + _dirtyRects[i].top * 1280, 1280, rect.left , rect.top, rect.right - rect.left, rect.bottom - rect.top); + } + } + } + lastScroll = _gameState->_currentScrollValue; + if (updateScreen) { _system->updateScreen(); _shouldQuit = shouldQuit(); // update game quit flag - this shouldn't be called all the time, as it's a virtual function @@ -589,6 +644,7 @@ bool ToonEngine::showMainmenu(bool &loadedGame) { bool musicPlaying = false; _gameState->_inMenu = true; + dirtyAllScreen(); while (!doExit) { clickingOn = MAINMENUHOTSPOT_NONE; @@ -607,7 +663,15 @@ bool ToonEngine::showMainmenu(bool &loadedGame) { } while (!clickRelease) { - mainmenuPicture->draw(*_mainSurface, 0, 0, 0, 0); + + if(_dirtyAll) { + mainmenuPicture->draw(*_mainSurface, 0, 0, 0, 0); + addDirtyRect(0,0,640,400); + } else { + mainmenuPicture->drawWithRectList(*_mainSurface, 0, 0, 0, 0, _dirtyRects); + } + + clearDirtyRects(); for (int entryNr = 0; entryNr < MAINMENU_ENTRYCOUNT; entryNr++) { if (entries[entryNr].menuMask & menuMask) { @@ -1211,6 +1275,9 @@ void ToonEngine::loadScene(int32 SceneId, bool forGameLoad) { state()->_mouseHidden = false; + clearDirtyRects(); + dirtyAllScreen(); + if (!forGameLoad) { _script->start(&_scriptState[0], 0); @@ -2562,7 +2629,13 @@ void ToonEngine::renderInventory() { if (!_gameState->_inInventory) return; - _inventoryPicture->draw(*_mainSurface, 0, 0, 0, 0); + if (!_dirtyAll) { + _inventoryPicture->drawWithRectList(*_mainSurface, 0, 0, 0, 0, _dirtyRects); + } else { + _inventoryPicture->draw(*_mainSurface, 0, 0, 0, 0); + _dirtyRects.push_back(Common::Rect(0,0,640,400)); + } + clearDirtyRects(); // draw items on screen for (int32 i = 0; i < _gameState->_numInventoryItems; i++) { @@ -2597,6 +2670,7 @@ int32 ToonEngine::showInventory() { fadeOut(5); _inventoryPicture->loadPicture("SACK128.CPS", true); _inventoryPicture->setupPalette(); + dirtyAllScreen(); if (_gameState->_mouseState >= 0) { setCursor(_gameState->_mouseState, true, -18, -14); @@ -2710,6 +2784,7 @@ int32 ToonEngine::showInventory() { setupGeneralPalette(); } flushPalette(); + dirtyAllScreen(); _firstFrame = true; return 0; @@ -2784,6 +2859,7 @@ void ToonEngine::showCutaway(Common::String cutawayPicture) { _currentCutaway->setupPalette(); _oldScrollValue = _gameState->_currentScrollValue; _gameState->_currentScrollValue = 0; + dirtyAllScreen(); flushPalette(); } @@ -2794,6 +2870,7 @@ void ToonEngine::hideCutaway() { _gameState->_currentScrollValue = _oldScrollValue; _currentCutaway = 0; _currentPicture->setupPalette(); + dirtyAllScreen(); flushPalette(); } @@ -4673,6 +4750,47 @@ void ToonEngine::playRoomMusic() { _audioManager->playMusic(_gameState->_locations[_gameState->_currentScene]._name, _gameState->_locations[_gameState->_currentScene]._music); } +void ToonEngine::dirtyAllScreen() +{ + _dirtyRects.clear(); + _dirtyAll = true; +} + +void ToonEngine::addDirtyRect( int32 left, int32 top, int32 right, int32 bottom ) { + left = MAX(left, 0); + right = MIN(right, 1280); + top = MAX(top, 0); + bottom = MIN(bottom, 400); + + Common::Rect rect(left,top,right,bottom); + + if (bottom - top <= 0 || right - left <= 0) + return; + + for (uint32 i = 0; i < _dirtyRects.size(); i++) { + if (_dirtyRects[i].contains(rect)) + return; + if (rect.contains(_dirtyRects[i])) { + _dirtyRects.remove_at(i); + i--; + } + } + + // check also in the old rect (of the old frame) + for (int32 i = _oldDirtyRects.size() - 1 ; i >= 0; i--) { + if (rect.contains(_oldDirtyRects[i])) { + _oldDirtyRects.remove_at(i); + } + } + + _dirtyRects.push_back(rect); +} + +void ToonEngine::clearDirtyRects() { + _oldDirtyRects = _dirtyRects; + _dirtyRects.clear(); + _dirtyAll = false; +} void SceneAnimation::save(ToonEngine *vm, Common::WriteStream *stream) { stream->writeByte(_active); stream->writeSint32BE(_id); diff --git a/engines/toon/toon.h b/engines/toon/toon.h index f3370aed5e..dca6bfe78e 100644 --- a/engines/toon/toon.h +++ b/engines/toon/toon.h @@ -334,6 +334,10 @@ public: (f == kSupportsSavingDuringRuntime); } + void dirtyAllScreen(); + void addDirtyRect(int32 left, int32 top, int32 right, int32 bottom); + void clearDirtyRects(); + protected: OSystem *_system; int32 _tickLength; @@ -371,6 +375,11 @@ protected: bool _updatingSceneScriptRunFlag; Graphics::Surface *_mainSurface; + Common::Array _dirtyRects; + Common::Array _oldDirtyRects; + + bool _dirtyAll; + AnimationInstance *_cursorAnimationInstance; Animation *_cursorAnimation; -- cgit v1.2.3