I found a weird bug with the MMSP2 hardware blitting in SDL. I don't know what causes it, but I can reproduce it.
If you blit a lot of small-sized bitmaps (say, a screen full of 32x32 tiles) at the screen using an x position where (x%16)==1, some of these bitmaps eventually will be moved by a few pixels during the blit. It very often happens with the first bitmap in a row.
Of cource I have an example, maybe it will make things clearer. Note that the glitch only occurs if XOFF%16 is 1 (e.g. 1,17,33,49,65,..).
CODE
#include <SDL.h>
#ifdef GP2X
#include <SDL_gp2x.h>
#include <unistd.h>
#endif
// 1,17,33,49,65,..
const int XOFF = 17;
int main(int argc, char** argv)
{
SDL_Rect rect;
int x,y;
SDL_Surface* buffer;
SDL_Surface* screen;
SDL_Surface* image;
SDL_Surface* image_hw;
SDL_Init(SDL_INIT_VIDEO);
#ifdef GP2X
SDL_GP2X_AllowGfxMemory(NULL,0);
screen = SDL_SetVideoMode(320,240,16,SDL_HWSURFACE);
buffer = SDL_CreateRGBSurface(SDL_HWSURFACE,
screen->w,screen->h,
screen->format->BitsPerPixel,
screen->format->Rmask,
screen->format->Gmask,
screen->format->Bmask,
screen->format->Amask);
#else
screen = SDL_SetVideoMode(320,240,16,0);
buffer = SDL_CreateRGBSurface(SDL_SWSURFACE,
screen->w,screen->h,
screen->format->BitsPerPixel,
screen->format->Rmask,
screen->format->Gmask,
screen->format->Bmask,
screen->format->Amask);
#endif
image = SDL_LoadBMP("test.bmp");
image_hw = SDL_DisplayFormat(image);
SDL_FreeSurface(image);
while (1)
{
SDL_Event event;
SDL_PollEvent(&event);
if (event.type == SDL_QUIT) break;
SDL_FillRect(buffer,NULL,0);
for (y=0;y<240-1;y+=image_hw->h)
{
for (x=0;x
20-1-XOFF;x+=image_hw->w)
{
rect.x = x+XOFF;
rect.y = y;
rect.w = image_hw->w;
rect.h = image_hw->h;
SDL_BlitSurface(image_hw,NULL,buffer,&rect);
}
}
SDL_BlitSurface(buffer,NULL,screen,NULL);
SDL_UpdateRect(screen,0,0,0,0);
SDL_Delay(10);
}
SDL_FreeSurface(image_hw);
SDL_FreeSurface(buffer);
SDL_Quit();
#ifdef GP2X
chdir("/usr/gp2x");
execl("/usr/gp2x/gp2xmenu","/usr/gp2x/gp2xmenu",NULL);
#endif
}
An interesting fact is that if I blit the tiles as a part from a bigger bitmap (e.g. {64,128,32,32} from a 256x256 tilemap) it appears that the tile in reality doesn't move on the screen. It's the source rectange that is aligned wrongly. I think so because in that special case I see a few pixels from a wrong tile everytime a bitmap "moves" through the glitch. So, it seems if I blit e.g. {64,96,32,32} from src to {33,0,32,32} and the glitch occurs the src rect isn't {64,128,32,32}, but {66,128,32,32} or something like that. I can only judge from my observations as I obviously don't see into the hardware (is there even any way to debug the MMSP2?).
As I said the misalignment of the blit (or the srcrect, if that's the issue) only happens every couple frames and only for a few of the bitmaps.
EDIT:
Another thing I found out: It only happens if I blit until the right edge of the screen. For example, with 32x32 bitmaps and an x offset of 17 (normally causing the bug), stopping before the last tile (which gives a 15-pixel black border on the right edge) everything is ok.
I even did some more experiments. If I do manual clipping, leaving the last pixel blank (in the case of a 32x32 tile at x:289 the destination rect would be of width 30 instead of 32 which normally would get clipped to 31), everything is alright, too. (If I clip to 31 in the said example, which is the exact amount of pixels left on the screen---I think SDL does this, too---the bug is back again).
I hope I expressed the problem in an understandable way. If you don't get it, I can do a quick illustration.
I don't know anything about the MMSP2, but it seems that the number 16 is very important to it (maybe it processes the pixels in chunks of 16?). Otherwise I have no explanation for this weird behaviour.
What would be a possible workaround (need to try it) is to split the "border blit" into two blits to circumvent the issue with the 15th pixel on the edge.