From c09c0b899820d7b0c3fdb5a26b4b6d9d894f68f4 Mon Sep 17 00:00:00 2001 From: johndoe123 Date: Wed, 17 Oct 2012 20:49:43 +0000 Subject: NEVERHOOD: Implement an experimental "render queue" to only draw and update what's actually changed; it works surprisingly well so far and only needs a few more changes here and there --- engines/neverhood/gamemodule.cpp | 7 +- engines/neverhood/graphics.cpp | 16 ++-- engines/neverhood/graphics.h | 1 + engines/neverhood/neverhood.cpp | 4 +- engines/neverhood/screen.cpp | 162 ++++++++++++++++++++++++++++++++---- engines/neverhood/screen.h | 40 ++++++++- engines/neverhood/smackerplayer.cpp | 2 +- 7 files changed, 203 insertions(+), 29 deletions(-) (limited to 'engines/neverhood') diff --git a/engines/neverhood/gamemodule.cpp b/engines/neverhood/gamemodule.cpp index 3d19a59d21..a3c88c2bf4 100644 --- a/engines/neverhood/gamemodule.cpp +++ b/engines/neverhood/gamemodule.cpp @@ -358,12 +358,13 @@ void GameModule::startup() { setGlobalVar(V_CREATURE_ANGRY, 1); setGlobalVar(V_RADIO_ENABLED, 1); setGlobalVar(V_TNT_DUMMY_BUILT, 1); + setGlobalVar(V_FLYTRAP_RING_DOOR, 1); // <<gameState().which = 1; - _vm->gameState().sceneNum = 0; - createModule(2500, -1); + _vm->gameState().which = 0; + _vm->gameState().sceneNum = 3; + createModule(1300, -1); #endif #if 0 _vm->gameState().sceneNum = 0; diff --git a/engines/neverhood/graphics.cpp b/engines/neverhood/graphics.cpp index c5b136f039..cd2234dd81 100644 --- a/engines/neverhood/graphics.cpp +++ b/engines/neverhood/graphics.cpp @@ -28,7 +28,7 @@ namespace Neverhood { BaseSurface::BaseSurface(NeverhoodEngine *vm, int priority, int16 width, int16 height) : _vm(vm), _priority(priority), _visible(true), _transparent(true), - _clipRects(NULL), _clipRectsCount(0) { + _clipRects(NULL), _clipRectsCount(0), _version(0) { _drawRect.x = 0; _drawRect.y = 0; @@ -54,11 +54,11 @@ BaseSurface::~BaseSurface() { void BaseSurface::draw() { if (_surface && _visible && _drawRect.width > 0 && _drawRect.height > 0) { if (_clipRects && _clipRectsCount) { - _vm->_screen->drawSurfaceClipRects(_surface, _drawRect, _clipRects, _clipRectsCount, _transparent); + _vm->_screen->drawSurfaceClipRects(_surface, _drawRect, _clipRects, _clipRectsCount, _transparent, _version); } else if (_sysRect.x == 0 && _sysRect.y == 0) { - _vm->_screen->drawSurface2(_surface, _drawRect, _clipRect, _transparent); + _vm->_screen->drawSurface2(_surface, _drawRect, _clipRect, _transparent, _version); } else { - _vm->_screen->drawUnk(_surface, _drawRect, _sysRect, _clipRect, _transparent); + _vm->_screen->drawUnk(_surface, _drawRect, _sysRect, _clipRect, _transparent, _version); } } } @@ -69,6 +69,7 @@ void BaseSurface::addDirtyRect() { void BaseSurface::clear() { _surface->fillRect(Common::Rect(0, 0, _surface->w, _surface->h), 0); + ++_version; } void BaseSurface::drawSpriteResource(SpriteResource &spriteResource) { @@ -76,6 +77,7 @@ void BaseSurface::drawSpriteResource(SpriteResource &spriteResource) { spriteResource.getDimensions().height <= _drawRect.height) { clear(); spriteResource.draw((byte*)_surface->pixels, _surface->pitch, false, false); + ++_version; } } @@ -89,6 +91,7 @@ void BaseSurface::drawSpriteResourceEx(SpriteResource &spriteResource, bool flip if (_surface) { clear(); spriteResource.draw((byte*)_surface->pixels, _surface->pitch, flipX, flipY); + ++_version; } } } @@ -102,6 +105,7 @@ void BaseSurface::drawAnimResource(AnimResource &animResource, uint frameIndex, clear(); if (frameIndex < animResource.getFrameCount()) { animResource.draw(frameIndex, (byte*)_surface->pixels, _surface->pitch, flipX, flipY); + ++_version; } } } @@ -109,6 +113,7 @@ void BaseSurface::drawAnimResource(AnimResource &animResource, uint frameIndex, void BaseSurface::drawMouseCursorResource(MouseCursorResource &mouseCursorResource, int frameNum) { if (frameNum < 3) { mouseCursorResource.draw(frameNum, (byte*)_surface->pixels, _surface->pitch); + ++_version; } } @@ -132,6 +137,7 @@ void BaseSurface::copyFrom(Graphics::Surface *sourceSurface, int16 x, int16 y, N dest += _surface->pitch; } } + ++_version; } // ShadowSurface @@ -143,7 +149,7 @@ ShadowSurface::ShadowSurface(NeverhoodEngine *vm, int priority, int16 width, int void ShadowSurface::draw() { if (_surface && _visible && _drawRect.width > 0 && _drawRect.height > 0) { - _vm->_screen->drawSurface2(_surface, _drawRect, _clipRect, _transparent, _shadowSurface->getSurface()); + _vm->_screen->drawSurface2(_surface, _drawRect, _clipRect, _transparent, _version, _shadowSurface->getSurface()); } } diff --git a/engines/neverhood/graphics.h b/engines/neverhood/graphics.h index 08357ea8ca..f27ee6295e 100644 --- a/engines/neverhood/graphics.h +++ b/engines/neverhood/graphics.h @@ -118,6 +118,7 @@ protected: NRect *_clipRects; uint _clipRectsCount; bool _transparent; + byte _version; }; class ShadowSurface : public BaseSurface { diff --git a/engines/neverhood/neverhood.cpp b/engines/neverhood/neverhood.cpp index 5152def96a..6a06ad66f6 100644 --- a/engines/neverhood/neverhood.cpp +++ b/engines/neverhood/neverhood.cpp @@ -153,12 +153,14 @@ Common::Error NeverhoodEngine::run() { if (_system->getMillis() >= nextFrameTime) { _gameModule->handleUpdate(); _gameModule->draw(); + _screen->update(); nextFrameTime = _screen->getNextFrameTime(); }; _soundMan->update(); _audioResourceMan->update(); - _screen->update(); + //_screen->update(); + _system->updateScreen(); _system->delayMillis(10); debug(0, "---------------------------------------"); diff --git a/engines/neverhood/screen.cpp b/engines/neverhood/screen.cpp index f9e8d49743..a5f7b4dac7 100644 --- a/engines/neverhood/screen.cpp +++ b/engines/neverhood/screen.cpp @@ -33,9 +33,14 @@ Screen::Screen(NeverhoodEngine *vm) _backScreen = new Graphics::Surface(); _backScreen->create(640, 480, Graphics::PixelFormat::createFormatCLUT8()); + _renderQueue = new RenderQueue(); + _prevRenderQueue = new RenderQueue(); + } Screen::~Screen() { + delete _renderQueue; + delete _prevRenderQueue; _backScreen->free(); delete _backScreen; } @@ -43,9 +48,65 @@ Screen::~Screen() { void Screen::update() { _ticks = _vm->_system->getMillis(); updatePalette(); - // TODO: Implement actual code - _vm->_system->copyRectToScreen((const byte*)_backScreen->pixels, _backScreen->pitch, 0, 0, 640, 480); - _vm->_system->updateScreen(); + + if (_fullRefresh) { + // NOTE When playing a fullscreen/doubled Smacker video usually a full screen refresh is needed + _vm->_system->copyRectToScreen((const byte*)_backScreen->pixels, _backScreen->pitch, 0, 0, 640, 480); + _fullRefresh = false; + return; + } + + // NOTE This is more or less experimental code for a smart "render queue". + // It works well so far but needs some optimizing, e.g. reducing overdraw. + // Maybe I'll use my microtiles code from the Toltecs engine. + // Also better move this to a separate method or even class. + + Common::Array updateRects; + + for (RenderQueue::iterator jt = _prevRenderQueue->begin(); jt != _prevRenderQueue->end(); ++jt) + (*jt)._refresh = true; + + for (RenderQueue::iterator it = _renderQueue->begin(); it != _renderQueue->end(); ++it) { + RenderItem &renderItem = (*it); + renderItem._refresh = true; + for (RenderQueue::iterator jt = _prevRenderQueue->begin(); jt != _prevRenderQueue->end(); ++jt) { + RenderItem &prevRenderItem = (*jt); + if (prevRenderItem == renderItem) { + prevRenderItem._refresh = false; + renderItem._refresh = false; + } + } + } + + for (RenderQueue::iterator jt = _prevRenderQueue->begin(); jt != _prevRenderQueue->end(); ++jt) { + RenderItem &prevRenderItem = (*jt); + if (prevRenderItem._refresh) + updateRects.push_back(Common::Rect(prevRenderItem._destX, prevRenderItem._destY, prevRenderItem._destX + prevRenderItem._width, prevRenderItem._destY + prevRenderItem._height)); + } + + for (RenderQueue::iterator it = _renderQueue->begin(); it != _renderQueue->end(); ++it) { + RenderItem &renderItem = (*it); + if (renderItem._refresh) + updateRects.push_back(Common::Rect(renderItem._destX, renderItem._destY, renderItem._destX + renderItem._width, renderItem._destY + renderItem._height)); + } + + /* + for (Common::Array::iterator ri = updateRects.begin(); ri != updateRects.end(); ++ri) + debug("## (%d, %d, %d, %d)", (*ri).left, (*ri).top, (*ri).right, (*ri).bottom); + */ + + for (RenderQueue::iterator it = _renderQueue->begin(); it != _renderQueue->end(); ++it) { + RenderItem &renderItem = (*it); + for (Common::Array::iterator ri = updateRects.begin(); ri != updateRects.end(); ++ri) + blitRenderItem(renderItem, *ri); + } + + SWAP(_renderQueue, _prevRenderQueue); + _renderQueue->clear(); + + for (Common::Array::iterator ri = updateRects.begin(); ri != updateRects.end(); ++ri) + _vm->_system->copyRectToScreen((const byte*)_backScreen->getBasePtr((*ri).left, (*ri).top), _backScreen->pitch, (*ri).left, (*ri).top, (*ri).width(), (*ri).height()); + } uint32 Screen::getNextFrameTime() { @@ -99,7 +160,7 @@ void Screen::clear() { memset(_backScreen->pixels, 0, _backScreen->pitch * _backScreen->h); } -void Screen::drawSurface2(const Graphics::Surface *surface, NDrawRect &drawRect, NRect &clipRect, bool transparent, +void Screen::drawSurface2(const Graphics::Surface *surface, NDrawRect &drawRect, NRect &clipRect, bool transparent, byte version, const Graphics::Surface *shadowSurface) { int16 destX, destY; @@ -133,7 +194,7 @@ void Screen::drawSurface2(const Graphics::Surface *surface, NDrawRect &drawRect, //debug(2, "draw: x = %d; y = %d; (%d, %d, %d, %d)", destX, destY, ddRect.x1, ddRect.y1, ddRect.x2, ddRect.y2); - blit(surface, destX, destY, ddRect, transparent, shadowSurface); + queueBlit(surface, destX, destY, ddRect, transparent, version, shadowSurface); // Useful for debugging //_backScreen->frameRect(Common::Rect(clipRect.x1, clipRect.y1, clipRect.x2, clipRect.y2), 250); @@ -142,7 +203,7 @@ void Screen::drawSurface2(const Graphics::Surface *surface, NDrawRect &drawRect, } -void Screen::drawSurface3(const Graphics::Surface *surface, int16 x, int16 y, NDrawRect &drawRect, NRect &clipRect, bool transparent) { +void Screen::drawSurface3(const Graphics::Surface *surface, int16 x, int16 y, NDrawRect &drawRect, NRect &clipRect, bool transparent, byte version) { int16 destX, destY; NRect ddRect; @@ -173,7 +234,7 @@ void Screen::drawSurface3(const Graphics::Surface *surface, int16 x, int16 y, ND ddRect.y1 = drawRect.y; } - blit(surface, destX, destY, ddRect, transparent); + queueBlit(surface, destX, destY, ddRect, transparent, version); } @@ -220,7 +281,7 @@ void Screen::drawDoubleSurface2(const Graphics::Surface *surface, NDrawRect &dra const byte *source = (const byte*)surface->getBasePtr(0, 0); byte *dest = (byte*)_backScreen->getBasePtr(drawRect.x, drawRect.y); - + for (int16 yc = 0; yc < surface->h; yc++) { byte *row = dest; for (int16 xc = 0; xc < surface->w; xc++) { @@ -231,10 +292,12 @@ void Screen::drawDoubleSurface2(const Graphics::Surface *surface, NDrawRect &dra dest += _backScreen->pitch; dest += _backScreen->pitch; } + + _fullRefresh = true; // See Screen::update } -void Screen::drawUnk(const Graphics::Surface *surface, NDrawRect &drawRect, NDrawRect &sysRect, NRect &clipRect, bool transparent) { +void Screen::drawUnk(const Graphics::Surface *surface, NDrawRect &drawRect, NDrawRect &sysRect, NRect &clipRect, bool transparent, byte version) { int16 x, y; bool xflag, yflag; @@ -271,7 +334,7 @@ void Screen::drawUnk(const Graphics::Surface *surface, NDrawRect &drawRect, NDra newDrawRect.height = drawRect.height; } - drawSurface3(surface, drawRect.x, drawRect.y, newDrawRect, clipRect, transparent); + drawSurface3(surface, drawRect.x, drawRect.y, newDrawRect, clipRect, transparent, version); if (!xflag) { newDrawRect.x = 0; @@ -280,7 +343,7 @@ void Screen::drawUnk(const Graphics::Surface *surface, NDrawRect &drawRect, NDra newDrawRect.height = sysRect.height - y; if (drawRect.height < newDrawRect.height) newDrawRect.height = drawRect.height; - drawSurface3(surface, sysRect.width + drawRect.x - x, drawRect.y, newDrawRect, clipRect, transparent); + drawSurface3(surface, sysRect.width + drawRect.x - x, drawRect.y, newDrawRect, clipRect, transparent, version); } if (!yflag) { @@ -290,7 +353,7 @@ void Screen::drawUnk(const Graphics::Surface *surface, NDrawRect &drawRect, NDra newDrawRect.height = y + drawRect.height - sysRect.height; if (drawRect.width < newDrawRect.width) newDrawRect.width = drawRect.width; - drawSurface3(surface, drawRect.x, sysRect.height + drawRect.y - y, newDrawRect, clipRect, transparent); + drawSurface3(surface, drawRect.x, sysRect.height + drawRect.y - y, newDrawRect, clipRect, transparent, version); } if (!xflag && !yflag) { @@ -298,15 +361,84 @@ void Screen::drawUnk(const Graphics::Surface *surface, NDrawRect &drawRect, NDra newDrawRect.y = 0; newDrawRect.width = x + drawRect.width - sysRect.width; newDrawRect.height = y + drawRect.height - sysRect.height; - drawSurface3(surface, sysRect.width + drawRect.x - x, sysRect.height + drawRect.y - y, newDrawRect, clipRect, transparent); + drawSurface3(surface, sysRect.width + drawRect.x - x, sysRect.height + drawRect.y - y, newDrawRect, clipRect, transparent, version); } } -void Screen::drawSurfaceClipRects(const Graphics::Surface *surface, NDrawRect &drawRect, NRect *clipRects, uint clipRectsCount, bool transparent) { +void Screen::drawSurfaceClipRects(const Graphics::Surface *surface, NDrawRect &drawRect, NRect *clipRects, uint clipRectsCount, bool transparent, byte version) { NDrawRect clipDrawRect(0, 0, drawRect.width, drawRect.height); for (uint i = 0; i < clipRectsCount; i++) - drawSurface3(surface, drawRect.x, drawRect.y, clipDrawRect, clipRects[i], transparent); + drawSurface3(surface, drawRect.x, drawRect.y, clipDrawRect, clipRects[i], transparent, version); +} + +void Screen::queueBlit(const Graphics::Surface *surface, int16 destX, int16 destY, NRect &ddRect, bool transparent, byte version, + const Graphics::Surface *shadowSurface) { + + int width = ddRect.x2 - ddRect.x1; + int height = ddRect.y2 - ddRect.y1; + + if (width <= 0 || height <= 0) + return; + + RenderItem renderItem; + renderItem._surface = surface; + renderItem._shadowSurface = shadowSurface; + renderItem._destX = destX; + renderItem._destY = destY; + renderItem._srcX = ddRect.x1; + renderItem._srcY = ddRect.y1; + renderItem._width = width; + renderItem._height = height; + renderItem._transparent = transparent; + renderItem._version = version; + _renderQueue->push_back(renderItem); + +} + +void Screen::blitRenderItem(const RenderItem &renderItem, const Common::Rect &clipRect) { + + const Graphics::Surface *surface = renderItem._surface; + const Graphics::Surface *shadowSurface = renderItem._shadowSurface; + const int16 x0 = MAX(clipRect.left, renderItem._destX); + const int16 y0 = MAX(clipRect.top, renderItem._destY); + const int16 x1 = MIN(clipRect.right, renderItem._destX + renderItem._width); + const int16 y1 = MIN(clipRect.bottom, renderItem._destY + renderItem._height); + int16 width = x1 - x0; + int16 height = y1 - y0; + + if (width < 0 || height < 0) + return; + + const byte *source = (const byte*)surface->getBasePtr(renderItem._srcX + x0 - renderItem._destX, renderItem._srcY + y0 - renderItem._destY); + byte *dest = (byte*)_backScreen->getBasePtr(x0, y0); + + if (shadowSurface) { + const byte *shadowSource = (const byte*)shadowSurface->getBasePtr(x0, y0); + while (height--) { + for (int xc = 0; xc < width; xc++) + if (source[xc] != 0) + dest[xc] = shadowSource[xc]; + source += surface->pitch; + shadowSource += shadowSurface->pitch; + dest += _backScreen->pitch; + } + } else if (!renderItem._transparent) { + while (height--) { + memcpy(dest, source, width); + source += surface->pitch; + dest += _backScreen->pitch; + } + } else { + while (height--) { + for (int xc = 0; xc < width; xc++) + if (source[xc] != 0) + dest[xc] = source[xc]; + source += surface->pitch; + dest += _backScreen->pitch; + } + } + } } // End of namespace Neverhood diff --git a/engines/neverhood/screen.h b/engines/neverhood/screen.h index e3a2bfbef7..bbf4f5fe73 100644 --- a/engines/neverhood/screen.h +++ b/engines/neverhood/screen.h @@ -23,6 +23,7 @@ #ifndef NEVERHOOD_SCREEN_H #define NEVERHOOD_SCREEN_H +#include "common/array.h" #include "graphics/surface.h" #include "video/smk_decoder.h" #include "neverhood/neverhood.h" @@ -30,6 +31,31 @@ namespace Neverhood { +struct RenderItem { + const Graphics::Surface *_surface; + const Graphics::Surface *_shadowSurface; + int16 _destX, _destY; + int16 _srcX, _srcY, _width, _height; + bool _transparent; + byte _version; + bool _refresh; + bool operator==(const RenderItem &second) const { + return + _surface == second._surface && + _shadowSurface == second._shadowSurface && + _destX == second._destX && + _destY == second._destY && + _srcX == second._srcX && + _srcY == second._srcY && + _width == second._width && + _height == second._height && + _transparent == second._transparent && + _version == second._version; + } +}; + +typedef Common::Array RenderQueue; + class Screen { public: Screen(NeverhoodEngine *vm); @@ -43,16 +69,19 @@ public: void testPalette(byte *paletteData); void updatePalette(); void clear(); - void drawSurface2(const Graphics::Surface *surface, NDrawRect &drawRect, NRect &clipRect, bool transparent, + void drawSurface2(const Graphics::Surface *surface, NDrawRect &drawRect, NRect &clipRect, bool transparent, byte version, const Graphics::Surface *shadowSurface = NULL); - void drawSurface3(const Graphics::Surface *surface, int16 x, int16 y, NDrawRect &drawRect, NRect &clipRect, bool transparent); + void drawSurface3(const Graphics::Surface *surface, int16 x, int16 y, NDrawRect &drawRect, NRect &clipRect, bool transparent, byte version); void drawShadowSurface(const Graphics::Surface *surface, const Graphics::Surface *shadowSurface, int16 x, int16 y, NDrawRect &drawRect, NRect &clipRect); void blit(const Graphics::Surface *surface, int16 destX, int16 destY, NRect &ddRect, bool transparent, const Graphics::Surface *shadowSurface = NULL); void drawDoubleSurface2(const Graphics::Surface *surface, NDrawRect &drawRect); - void drawUnk(const Graphics::Surface *surface, NDrawRect &drawRect, NDrawRect &sysRect, NRect &clipRect, bool transparent); - void drawSurfaceClipRects(const Graphics::Surface *surface, NDrawRect &drawRect, NRect *clipRects, uint clipRectsCount, bool transparent); + void drawUnk(const Graphics::Surface *surface, NDrawRect &drawRect, NDrawRect &sysRect, NRect &clipRect, bool transparent, byte version); + void drawSurfaceClipRects(const Graphics::Surface *surface, NDrawRect &drawRect, NRect *clipRects, uint clipRectsCount, bool transparent, byte version); void setSmackerDecoder(Video::SmackerDecoder *smackerDecoder) { _smackerDecoder = smackerDecoder; } + void queueBlit(const Graphics::Surface *surface, int16 destX, int16 destY, NRect &ddRect, bool transparent, byte version, + const Graphics::Surface *shadowSurface = NULL); + void blitRenderItem(const RenderItem &renderItem, const Common::Rect &clipRect); protected: NeverhoodEngine *_vm; Graphics::Surface *_backScreen; @@ -61,6 +90,9 @@ protected: int32 _frameDelay; byte *_paletteData; bool _paletteChanged; + // + bool _fullRefresh; + RenderQueue *_renderQueue, *_prevRenderQueue; }; } // End of namespace Neverhood diff --git a/engines/neverhood/smackerplayer.cpp b/engines/neverhood/smackerplayer.cpp index b491bc5789..d7a244ba94 100644 --- a/engines/neverhood/smackerplayer.cpp +++ b/engines/neverhood/smackerplayer.cpp @@ -36,7 +36,7 @@ SmackerSurface::SmackerSurface(NeverhoodEngine *vm) void SmackerSurface::draw() { if (_smackerFrame && _visible && _drawRect.width > 0 && _drawRect.height > 0) - _vm->_screen->drawSurface2(_smackerFrame, _drawRect, _clipRect, false); + _vm->_screen->drawSurface2(_smackerFrame, _drawRect, _clipRect, false, ++_version); } void SmackerSurface::setSmackerFrame(const Graphics::Surface *smackerFrame) { -- cgit v1.2.3