Removed the Surface class in favor of small struct specially made for the

block surfaces. (A block surface is a 64x64 tile of a parallax layer.)

I've also done a few things to try and optimize the drawing:

* The back buffer is no longer cleared between frames. This may cause
  regressions, but I do believe that the entire picture area is always
  completely re-rendered for each frame.

  As a result of this, the menu code is now responsible for clearing the
  icon areas itself.

* A few unnecessary copy_rect() calls were commented out in favor of one
  big copy_rect() in ServiceWindows().

* Completely opaque block surfaces are copied with memcpy(), one line at a
  time.

Unless we manage to add intelligent screen redrawing, I don't think it will
get that much faster than this, though there is some unnecessary data
copying in DrawSprite() that could be removed.

And the game is still a terrible CPU hog. I believe the animation runs at
approximately 12 fps. If there's still time left, it will pump out further
frames to get smooth scrolling. We ought to put a cap on that, and if it
has already reached the scroll target it should sleep for the rest of the
render cycle.

svn-id: r9886
This commit is contained in:
Torbjörn Andersson 2003-08-28 06:36:15 +00:00
parent 76df5a2733
commit f7ce39763e
7 changed files with 156 additions and 138 deletions

View File

@ -28,7 +28,7 @@
#define SCREENYOFFSET 40
#define MILLISECSPERCYCLE 83
Surface *lpBackBuffer;
byte *lpBackBuffer;
/*
static LPDIRECTDRAW lpDraw; // DirectDraw object
@ -119,7 +119,7 @@ int32 InitialiseDisplay(int16 width, int16 height, int16 colourDepth, int32 wind
screenWide = width;
screenDeep = height;
lpBackBuffer = new Surface(width, height);
lpBackBuffer = (byte *) malloc(screenWide * screenDeep);
return(RD_OK);
}
@ -205,9 +205,15 @@ int32 WaitForVbl(void)
}
int32 EraseBackBuffer( void ) {
debug(9, "EraseBackBuffer");
lpBackBuffer->clear();
return(RD_OK);
// Since the entire screen is redrawn each time, there probably isn't
// any need to actually clear the back buffer.
//
// At the very least, since the menu code now is solely responsible
// for its own parts of the screen, we'd only need to clear the
// picture area.
// memset(lpBackBuffer + MENUDEEP * screnWide, 0, screenWide * (screenDeep - 2 * MENUDEEP));
return RD_OK;
}

View File

@ -78,7 +78,7 @@ extern "C" {
extern uint8 *lpPalette; // palette
extern Surface *lpBackBuffer; // back surface
extern byte *lpBackBuffer; // back surface
// extern Surface *lpPrimarySurface; // DirectDraw front buffer.
extern uint8 *lpDD2; // DirectDraw2 object
extern BOOL bFullScreen; // Defines whether the app is running in full screen mode or not.

View File

@ -1247,39 +1247,6 @@ typedef int BOOL;
#define TRUE 1
#define FALSE 0
// Surface class to help replace LPDIRECTDRAWSURFACE.
//
// This class should be used as little as possible since it introduces an
// extra layer of data copying. It should only be used where we decode
// something once and draw it many times, such as the parallax layers.
//
// Even then it's only necessary if we also need to keep track of the
// surface's dimensions.
//
// Since the building blocks of the parallax layers have constant size,
// expect this class to go away.
class Surface {
public:
uint16 _width, _height;
uint16 _pitch;
byte *_pixels;
Surface(uint width, uint height) {
_width = width;
_height = height;
_pixels = (byte *) calloc(_width, _height);
}
void clear();
void blit(Surface *s, ScummVM::Rect *r);
void blit(Surface *s, ScummVM::Rect *r, ScummVM::Rect *clip_rect);
~Surface() {
free(_pixels);
}
};
//
// Structure definitions
// ---------------------
@ -1378,7 +1345,7 @@ typedef struct
// LPDIRECTDRAW lpDraw;
// LPDIRECTDRAW2 lpDD2;
// Surface *lpPrimarySurface;
Surface *lpBackBuffer;
byte *lpBackBuffer;
// LPDIRECTDRAWPALETTE lpPalette;
int16 screenDeep;
int16 screenWide;

View File

@ -122,7 +122,7 @@ static uint8 menuStatus[2] = {
RDMENU_HIDDEN, RDMENU_HIDDEN
};
static uint8 *icons[2][RDMENU_MAXPOCKETS] = {
static byte *icons[2][RDMENU_MAXPOCKETS] = {
{ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
{ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
};
@ -134,14 +134,32 @@ static uint8 pocketStatus[2][RDMENU_MAXPOCKETS] = {
static uint8 iconCount = 0;
void ClearIconArea(int menu, int pocket, ScummVM::Rect *r) {
byte *dst;
int i;
r->top = menu * (RENDERDEEP + MENUDEEP) + (MENUDEEP - RDMENU_ICONDEEP) / 2;
r->bottom = r->top + RDMENU_ICONDEEP;
r->left = RDMENU_ICONSTART + pocket * (RDMENU_ICONWIDE + RDMENU_ICONSPACING);
r->right = r->left + RDMENU_ICONWIDE;
dst = lpBackBuffer + r->top * screenWide + r->left;
for (i = 0; i < RDMENU_ICONDEEP; i++) {
memset(dst, 0, RDMENU_ICONWIDE);
dst += screenWide;
}
}
int32 ProcessMenu(void) {
byte *src, *dst;
uint8 menu;
uint8 i;
uint8 i, j;
uint8 complete;
uint8 frameCount;
int32 curx, xoff;
int32 cury, yoff;
ScummVM::Rect r;
ScummVM::Rect r1, r2;
int32 delta;
static int32 lastTime = 0;
@ -188,6 +206,11 @@ int32 ProcessMenu(void) {
// Propagate the animation from the first icon.
for (i = RDMENU_MAXPOCKETS - 1; i > 0; i--) {
if (icons[menu][i] && pocketStatus[menu][i] != 0 && pocketStatus[menu][i - 1] == 0) {
ClearIconArea(menu, i, &r1);
UploadRect(&r1);
}
pocketStatus[menu][i] = pocketStatus[menu][i - 1];
if (pocketStatus[menu][i] != 0)
complete = 0;
@ -196,10 +219,16 @@ int32 ProcessMenu(void) {
complete = 0;
// ... and animate the first icon
if (pocketStatus[menu][0] != 0)
if (pocketStatus[menu][0] != 0) {
pocketStatus[menu][0]--;
// Check to see if the menu is fully open
if (pocketStatus[menu][0] == 0) {
ClearIconArea(menu, 0, &r1);
UploadRect(&r1);
}
}
// Check to see if the menu is fully closed
if (complete)
menuStatus[menu] = RDMENU_HIDDEN;
}
@ -215,41 +244,49 @@ int32 ProcessMenu(void) {
for (i = 0; i < RDMENU_MAXPOCKETS; i++) {
if (icons[menu][i]) {
// Since we no longer clear the screen
// after each frame we need to clear
// the icon area.
ClearIconArea(menu, i, &r1);
if (pocketStatus[menu][i] == MAXMENUANIMS) {
xoff = (RDMENU_ICONWIDE / 2);
r.left = curx - xoff;
r.right = r.left + RDMENU_ICONWIDE;
r2.left = curx - xoff;
r2.right = r2.left + RDMENU_ICONWIDE;
yoff = (RDMENU_ICONDEEP / 2);
r.top = cury - yoff;
r.bottom = r.top + RDMENU_ICONDEEP;
r2.top = cury - yoff;
r2.bottom = r2.top + RDMENU_ICONDEEP;
} else {
xoff = (RDMENU_ICONWIDE / 2) * pocketStatus[menu][i] / MAXMENUANIMS;
r.left = curx - xoff;
r.right = curx + xoff;
r2.left = curx - xoff;
r2.right = curx + xoff;
yoff = (RDMENU_ICONDEEP / 2) * pocketStatus[menu][i] / MAXMENUANIMS;
r.top = cury - yoff;
r.bottom = cury + yoff;
r2.top = cury - yoff;
r2.bottom = cury + yoff;
}
if (xoff != 0 && yoff != 0) {
byte *dst = lpBackBuffer->_pixels + r.top * lpBackBuffer->_width + r.left;
byte *src = icons[menu][i];
dst = lpBackBuffer + r2.top * screenWide + r2.left;
src = icons[menu][i];
if (pocketStatus[menu][i] != MAXMENUANIMS) {
SquashImage(
dst, lpBackBuffer->_width, r.right - r.left, r.bottom - r.top,
dst, screenWide, r2.right - r2.left, r2.bottom - r2.top,
src, RDMENU_ICONWIDE, RDMENU_ICONWIDE, RDMENU_ICONDEEP, NULL);
} else {
for (int j = 0; j < RDMENU_ICONDEEP; j++) {
for (j = 0; j < RDMENU_ICONDEEP; j++) {
memcpy(dst, src, RDMENU_ICONWIDE);
src += RDMENU_ICONWIDE;
dst += lpBackBuffer->_width;
dst += screenWide;
}
}
UploadRect(&r);
UploadRect(&r1);
}
}
curx += (RDMENU_ICONSPACING + RDMENU_ICONWIDE);
r1.left += (RDMENU_ICONSPACING + RDMENU_ICONWIDE);
r1.right += (RDMENU_ICONSPACING + RDMENU_ICONWIDE);
}
}
}
@ -501,15 +538,31 @@ int32 HideMenu(uint8 menu) {
return RD_OK;
}
int32 CloseMenuImmediately(void)
{
int32 CloseMenuImmediately(void) {
ScummVM::Rect r;
int i;
menuStatus[0] = RDMENU_HIDDEN;
menuStatus[1] = RDMENU_HIDDEN;
for (i = 0; i < RDMENU_MAXPOCKETS; i++) {
if (icons[0][i]) {
ClearIconArea(0, i, &r);
UploadRect(&r);
}
if (icons[1][i]) {
ClearIconArea(1, i, &r);
UploadRect(&r);
}
}
memset(pocketStatus, 0, sizeof(uint8) * 2 * RDMENU_MAXPOCKETS);
return (RD_OK);
return RD_OK;
}
int32 SetMenuIcon(uint8 menu, uint8 pocket, uint8 *icon) {
ScummVM::Rect r;
debug(5, "stub SetMenuIcon( %d, %d )", menu, pocket);
// Check for invalid menu parameter.
@ -525,6 +578,8 @@ int32 SetMenuIcon(uint8 menu, uint8 pocket, uint8 *icon) {
iconCount--;
free(icons[menu][pocket]);
icons[menu][pocket] = NULL;
ClearIconArea(menu, pocket, &r);
UploadRect(&r);
}
// Only put the icon in the pocket if it is not NULL

View File

@ -544,7 +544,12 @@ int32 ServiceWindows(void)
{
g_sword2->parseEvents();
FadeServer();
g_sword2->_system->update_screen();
// FIXME: We re-render the entire picture area of the screen for each
// frame, which is pretty horrible.
g_system->copy_rect(lpBackBuffer + MENUDEEP * screenWide, screenWide, 0, MENUDEEP, screenWide, screenDeep - 2 * MENUDEEP);
g_system->update_screen();
// warning("stub ServiceWindows"); // too noisy
/*
MSG msg;

View File

@ -277,36 +277,28 @@ uint8 xblocks[MAXLAYERS];
uint8 yblocks[MAXLAYERS];
uint8 restoreLayer[MAXLAYERS];
// Each layer is composed by several sub-blocks
// blockSurfaces stores an array of sub-blocks for each of the parallax layers.
Surface **blockSurfaces[MAXLAYERS] = { 0, 0, 0, 0, 0 };
typedef struct {
byte data[BLOCKWIDTH * BLOCKHEIGHT];
bool transparent;
} BlockSurface;
BlockSurface **blockSurfaces[MAXLAYERS] = { 0, 0, 0, 0, 0 };
void Surface::clear() {
memset(_pixels, 0, _width * _height);
g_sword2->_system->copy_rect(_pixels, _width, 0, 0, _width, _height);
void UploadRect(ScummVM::Rect *r) {
g_system->copy_rect(lpBackBuffer + r->top * screenWide + r->left,
screenWide, r->left, r->top, r->right - r->left, r->bottom - r->top);
}
void Surface::blit(Surface *s, ScummVM::Rect *r) {
ScummVM::Rect clip_rect;
clip_rect.left = 0;
clip_rect.top = 0;
clip_rect.right = 640;
clip_rect.bottom = 480;
blit(s, r, &clip_rect);
}
void Surface::blit(Surface *s, ScummVM::Rect *r, ScummVM::Rect *clip_rect) {
void BlitBlockSurface(BlockSurface *s, ScummVM::Rect *r, ScummVM::Rect *clip_rect) {
if (r->top > clip_rect->bottom || r->left > clip_rect->right || r->bottom <= clip_rect->top || r->right <= clip_rect->left)
return;
byte *src = s->_pixels;
byte *src = s->data;
if (r->top < clip_rect->top) {
src -= s->_width * (r->top - clip_rect->top);
src -= BLOCKWIDTH * (r->top - clip_rect->top);
r->top = clip_rect->top;
}
if (r->left < clip_rect->left) {
@ -318,29 +310,27 @@ void Surface::blit(Surface *s, ScummVM::Rect *r, ScummVM::Rect *clip_rect) {
if (r->right > clip_rect->right)
r->right = clip_rect->right;
byte *dst = _pixels + r->top * _width + r->left;
byte *dst = lpBackBuffer + r->top * screenWide + r->left;
int i, j;
// FIXME: We first render the data to the back buffer, and then copy
// it to the backend. Since the same area will probably be copied
// several times, as each new parallax layer is rendered, this may be
// a bit inefficient.
for (i = 0; i < r->bottom - r->top; i++) {
for (j = 0; j < r->right - r->left; j++) {
if (src[j])
dst[j] = src[j];
if (s->transparent) {
for (i = 0; i < r->bottom - r->top; i++) {
for (j = 0; j < r->right - r->left; j++) {
if (src[j])
dst[j] = src[j];
}
src += BLOCKWIDTH;
dst += screenWide;
}
} else {
for (i = 0; i < r->bottom - r->top; i++) {
memcpy(dst, src, r->right - r->left);
src += BLOCKWIDTH;
dst += screenWide;
}
src += s->_width;
dst += _width;
}
UploadRect(r);
}
void UploadRect(ScummVM::Rect *r) {
g_system->copy_rect(lpBackBuffer->_pixels + r->top * lpBackBuffer->_width + r->left,
lpBackBuffer->_width, r->left, r->top, r->right - r->left, r->bottom - r->top);
// UploadRect(r);
}
#define SCALE_MAXWIDTH 512
@ -445,7 +435,7 @@ void SquashImage(byte *dst, uint16 dstPitch, uint16 dstWidth, uint16 dstHeight,
dst[x] = QuickMatch((uint8) (red / count), (uint8) (green / count), (uint8) (blue / count));
}
dst += dstPitch;
backbuf += lpBackBuffer->_width;
backbuf += screenWide;
}
} else {
for (y = 0; y < dstHeight; y++) {
@ -606,7 +596,7 @@ int32 RestoreBackgroundLayer(_parallax *p, int16 l)
if (blockSurfaces[l]) {
for (i = 0; i < xblocks[l] * yblocks[l]; i++)
if (blockSurfaces[l][i])
delete blockSurfaces[l][i];
free(blockSurfaces[l][i]);
free(blockSurfaces[l]);
blockSurfaces[l] = NULL;
@ -920,8 +910,6 @@ int32 RenderParallax(_parallax *p, int16 l) {
int16 i, j;
ScummVM::Rect r;
debug(9, "RenderParallax %d", l);
if (locationWide == screenWide)
x = 0;
else
@ -937,9 +925,9 @@ int32 RenderParallax(_parallax *p, int16 l) {
// Leave enough space for the top and bottom menues
clip_rect.left = 0;
clip_rect.right = 640;
clip_rect.top = 40;
clip_rect.bottom = 440;
clip_rect.right = screenWide;
clip_rect.top = MENUDEEP;
clip_rect.bottom = screenDeep - MENUDEEP;
for (j = 0; j < yblocks[l]; j++) {
for (i = 0; i < xblocks[l]; i++) {
@ -948,7 +936,7 @@ int32 RenderParallax(_parallax *p, int16 l) {
r.right = r.left + BLOCKWIDTH;
r.top = j * BLOCKHEIGHT - y + 40;
r.bottom = r.top + BLOCKHEIGHT;
lpBackBuffer->blit(blockSurfaces[l][i + j * xblocks[l]], &r, &clip_rect);
BlitBlockSurface(blockSurfaces[l][i + j * xblocks[l]], &r, &clip_rect);
}
}
}
@ -956,7 +944,7 @@ int32 RenderParallax(_parallax *p, int16 l) {
parallaxScrollx = scrollx - x;
parallaxScrolly = scrolly - y;
return(RD_OK);
return RD_OK;
}
@ -1078,7 +1066,6 @@ int32 CopyScreenBuffer(void) {
int32 InitialiseBackgroundLayer(_parallax *p) {
uint8 *memchunk;
uint32 *quaddata;
uint8 zeros;
uint16 count;
uint16 i, j, k;
@ -1105,7 +1092,7 @@ int32 InitialiseBackgroundLayer(_parallax *p) {
xblocks[layer] = (p->w + BLOCKWIDTH - 1) >> BLOCKWBITS;
yblocks[layer] = (p->h + BLOCKHEIGHT - 1) >> BLOCKHBITS;
blockSurfaces[layer] = (Surface **) calloc(xblocks[layer] * yblocks[layer], sizeof(Surface *));
blockSurfaces[layer] = (BlockSurface **) calloc(xblocks[layer] * yblocks[layer], sizeof(BlockSurface *));
if (!blockSurfaces[layer])
return RDERR_OUTOFMEMORY;
@ -1160,36 +1147,34 @@ int32 InitialiseBackgroundLayer(_parallax *p) {
for (i = 0; i < xblocks[layer] * yblocks[layer]; i++) {
bool block_has_data = false;
bool block_is_transparent = false;
data = memchunk + (p->w * BLOCKHEIGHT * (i / xblocks[layer])) + BLOCKWIDTH * (i % xblocks[layer]);
quaddata = (uint32 *) data;
for (j = 0; j < BLOCKHEIGHT; j++) {
for (k = 0; k < BLOCKWIDTH / 4; k++) {
if (*quaddata) {
for (k = 0; k < BLOCKWIDTH; k++) {
if (data[j * p->w + k])
block_has_data = true;
goto bailout;
}
quaddata++;
else
block_is_transparent = true;
}
quaddata += ((p->w - BLOCKWIDTH) / 4);
}
bailout:
// Only assign a surface to the block if it contains data.
if (block_has_data) {
blockSurfaces[layer][i] = new Surface(BLOCKWIDTH, BLOCKHEIGHT);
blockSurfaces[layer][i] = (BlockSurface *) malloc(sizeof(BlockSurface));
// Copy the data into the surfaces.
dst = blockSurfaces[layer][i]->_pixels;
dst = blockSurfaces[layer][i]->data;
for (j = 0; j < BLOCKHEIGHT; j++) {
memcpy(dst, data, BLOCKWIDTH);
data += p->w;
dst += BLOCKWIDTH;
}
blockSurfaces[layer][i]->transparent = block_is_transparent;
} else
blockSurfaces[layer][i] = NULL;
}
@ -1209,7 +1194,7 @@ int32 CloseBackgroundLayer(void) {
if (blockSurfaces[j]) {
for (i = 0; i < xblocks[j] * yblocks[j]; i++)
if (blockSurfaces[j][i])
delete blockSurfaces[j][i];
free(blockSurfaces[j][i]);
free(blockSurfaces[j]);
blockSurfaces[j] = NULL;
}

View File

@ -1308,7 +1308,7 @@ int32 DrawSurface(_spriteInfo *s, uint8 *surface) {
sprite = surface;
src = sprite + rs.top * srcPitch + rs.left;
dst = lpBackBuffer->_pixels + lpBackBuffer->_width * rd.top + rd.left;
dst = lpBackBuffer + screenWide * rd.top + rd.left;
if (s->type & RDSPR_TRANS) {
for (y = 0; y < rd.bottom - rd.top; y++) {
@ -1317,19 +1317,19 @@ int32 DrawSurface(_spriteInfo *s, uint8 *surface) {
dst[x] = src[x];
}
src += srcPitch;
dst += lpBackBuffer->_width;
dst += screenWide;
}
} else {
for (y = 0; y < rd.bottom - rd.top; y++)
memcpy(dst, src, lpBackBuffer->_width);
memcpy(dst, src, screenWide);
src += srcPitch;
dst += lpBackBuffer->_width;
dst += screenWide;
}
if (freeSprite)
free(sprite);
UploadRect(&rd);
// UploadRect(&rd);
return 0;
}
@ -1463,7 +1463,7 @@ int32 DrawSprite(_spriteInfo *s) {
if (scale != 256) {
if ((renderCaps & RDBLTFX_ARITHMETICSTRETCH) && !clipped)
backbuf = lpBackBuffer->_pixels + lpBackBuffer->_width * rd.top + rd.left;
backbuf = lpBackBuffer + screenWide * rd.top + rd.left;
if (s->scaledWidth > SCALE_MAXWIDTH || s->scaledHeight > SCALE_MAXHEIGHT) {
@ -1535,7 +1535,7 @@ int32 DrawSprite(_spriteInfo *s) {
// -----------------------------------------------------------------
src = sprite + rs.top * srcPitch + rs.left;
dst = lpBackBuffer->_pixels + lpBackBuffer->_width * rd.top + rd.left;
dst = lpBackBuffer + screenWide * rd.top + rd.left;
if (s->type & RDSPR_BLEND) {
if (renderCaps & RDBLTFX_ALLHARDWARE) {
@ -1545,7 +1545,7 @@ int32 DrawSprite(_spriteInfo *s) {
dst[j] = src[j];
}
src += srcPitch;
dst += lpBackBuffer->_width;
dst += screenWide;
}
} else {
if (s->blend & 0x01) {
@ -1560,7 +1560,7 @@ int32 DrawSprite(_spriteInfo *s) {
}
}
src += srcPitch;
dst += lpBackBuffer->_width;
dst += screenWide;
}
} else if (s->blend & 0x02) {
// FIXME: This case looks bogus to me. The
@ -1589,7 +1589,7 @@ int32 DrawSprite(_spriteInfo *s) {
}
}
src += srcPitch;
dst += lpBackBuffer->_width;
dst += screenWide;
}
} else {
warning("DrawSprite: Invalid blended sprite");
@ -1606,13 +1606,13 @@ int32 DrawSprite(_spriteInfo *s) {
dst[j] = src[j];
}
src += srcPitch;
dst += lpBackBuffer->_width;
dst += screenWide;
}
} else {
for (i = 0; i < rs.bottom - rs.top; i++) {
memcpy(dst, src, rs.right - rs.left);
src += srcPitch;
dst += lpBackBuffer->_width;
dst += screenWide;
}
}
}
@ -1620,7 +1620,7 @@ int32 DrawSprite(_spriteInfo *s) {
if (freeSprite)
free(sprite);
UploadRect(&rd);
// UploadRect(&rd);
/*