GP32 Partially Offscreen Sprites


unit3

Member
Joined
Aug 8, 2003
Messages
151
Location
Canada
Website
demoni.ca
Hi all,

LTNS I know, I've been just mental the past half a year, what with multiple moves, multiple jobs, and trying to fit in school too. Things have settled down, so I've gotten back to work on my tile/layer C++ library that sits on top of Mr. Mirko's excellent SDK.

I think I've got it mostly working, with the exception of one bug I'm not sure how to iron out. Does anyone know how to draw a sprite that's partially offscreen? If I've got 16x16 tiles and I'm scrolling them, I want to be able to draw a tile that's at -10, -10, and have its bottom-right 5x5 pixels show on screen. Every time I try to do this with Mirko's SDK, my GP crashes and reboots. ;)

I'd like to know if I'm just missing something obvious, or if there's a known workaround for this... Anyone?
 
Why don't you try to clip coordinates before to blit the tile?
Here is an example of what I use. I don't know if it's a slow algorithm, but it works well for me.

Code:
  // Clip bitmap
  // x, y: coordinates where to blit the bitmap
  // bitmapx, bitmapy, width, height: portion of the bitmap to blit

  //- If the bitmap is completly off screen, exit
  if ((x >= clip_x + clip_w) || (y >= clip_y + clip_h))
    return;
  if ((x + width - 1 < clip_x) || (y + height - 1 < clip_y))
    return;

  //- Clip X and Width
  if (x < clip_x)
  {
    width -= clip_x - x;
    bitmapx += clip_x - x;
    x = clip_x;
  }
  if (x + width > clip_x + clip_w)
    width = clip_x + clip_w - x;

  //- Clip Y and Height
  if (y < clip_y)
  {
    height -= clip_y - y;
    bitmapy += clip_y - y;
    y = clip_y;
  }
  if (y + height> clip_y + clip_h)
    height = clip_y + clip_h - y;

  // From there, x, y, width and height have valid values
 
Mirko's SDK blitting code doesn't check for boundaries (it doesn't in the last non-GPL version at least), you should add checking to the SDK yourself (or better write your own blitting routine).
 
though it does check for the LCD bounderies (320x240) in the setPixel function the blitting ones use...
I use the same method as Oankali and it works very well, should be added to the Mirko SDK in the non_transparency functions...
 
Hi all,

LTNS I know, I've been just mental the past half a year, what with multiple moves, multiple jobs, and trying to fit in school too. Things have settled down, so I've gotten back to work on my tile/layer C++ library that sits on top of Mr. Mirko's excellent SDK.

I think I've got it mostly working, with the exception of one bug I'm not sure how to iron out. Does anyone know how to draw a sprite that's partially offscreen? If I've got 16x16 tiles and I'm scrolling them, I want to be able to draw a tile that's at -10, -10, and have its bottom-right 5x5 pixels show on screen. Every time I try to do this with Mirko's SDK, my GP crashes and reboots. ;)

I'd like to know if I'm just missing something obvious, or if there's a known workaround for this... Anyone?

There is no range checking in my tiled display routine, but you can easely extend it to this. ( have a look to the normal sprite displaying routines )
 
Last edited by a moderator:
It'd be really bad to add range checking to the SDK .. really shouldn't be there, since it just slows everything down enormously. It shoudl be part of the game itself, since only you know when you want to go out of bounds.

It is common to take a few approaches; given we have no blit hardware it rules out the good methods :)

ie: If you're rendering offscreen and then blitting to screen, then its easy.. your offscreen window should be bigger than the real screen and just render the whole partial tiles/sprites, and then during blit you blit over the visibl screen (minus the 8px or 16px border...) -- this way you have no bounds check in the render code, so its fast.

ie: If you're rendering onscreen (and using hardware multiple screen buffers) then you can render 90% of the screen without bounds checking, and for the obrder elements, render with bounds checking; never render the middle 90% with bounds checking.. thats crazy slow.

Theres other techniques of course.. thids is just off the top of my head :)

See BombZ/Zot for performance.. due to being cross platform, that thing renders 100% offscreen and then blits to the real screen in real time .. no slowdowns even with hundreds of little sprites onscreen on top of the very large tile meshes.. and not even heavily optimized yet.

jeff
 
Hi guys,

Firstly, Mr.Mirkos SDK DOES do range checking for every single pixel blit. Pixles are drawn using the setPixel function, which looks like this:

Code:
void setpixel(short x, short y, u16 color, u16 *framebuffer ) {
     if ( !((x<0) || (x>319) || (y<0) || (y>239)) )
       gp_drawPixel16(x,y,color,framebuffer);
}

I have developed routines (for 16bpp only) for blitting using both transparency and non-transparency. My transparency routines use a transparency mask which is generated for you. What you need to realise though is that this uses twice the memory for each transparent sprite (though hopefully the speed gains are enough to warrant this). The routines work fine for partially, or even completely, offscreen sprites.

EDIT:
I uploaded the latest routines (header file commented well), and wrotea brief example of how to use the routines. The latest routines can be found at http://www.pea.co.nz/gp32/gpd_downloads.php

I would appreciate if somebody could use the transparent blitting function(s) and let me know if they are fast or not. I have never used any others, so can't judge.
 
Why don't you try to clip coordinates before to blit the tile?
Here is an example of what I use. I don't know if it's a slow algorithm, but it works well for me.

Yes, I was thinking about doing something similar, but then I can't use Mr.Mirko's nice fast sprite drawing functions, as they require his header, and don't let you input your own width/height.
 
Last edited by a moderator:
Hi guys,

Firstly, Mr.Mirkos SDK DOES do range checking for every single pixel blit. Pixles are drawn using the setPixel function, which looks like this:

Right, but his sprite drawing functions don't do this, correct?

Hmm... I s'pose I could extend his sprite drawing functions to have user-defined clipping (ie, only draw from (5,5) to (10,10) of a 16x16 sprite). Then, it'd only be slightly slower on sprites where clipping is actually used.
 
Last edited by a moderator:
There is no range checking in my tiled display routine, but you can easely extend it to this. ( have a look to the normal sprite displaying routines )

Wait, so is there range checking in the normal sprite drawing routines? If so, I've got to go back through my code and figure out why when I try to scroll some of my sprites it crashes the gamepark. ;)

... looking through the SDK code, it looks like every gp_drawSprite* function does bounds checking. Damn. Then I don't have any idea why my app would be crashing, since I'm only using those functions. Maybe a pointer error on my part, I'll have to go through my code and audit it. :(



Also, just to be clear, when I was talking about a tile system, I'm not using your sprite tile functions in the SDK, I'm making a game map system that uses tiles of different sprites to draw overlapping layers, it's entirely internal to my library. I'm mostly just using your gp_drawSpriteH* functions. :)
 
Last edited by a moderator:
well here is my drawTile function, it is fairly fast and does it's job ... I took code from Pea for this so thank you mate :) There are some spots to optimize this like killing one ADD and one MULL in the memcopy call for speed up...

WARNING: it uses rotated data like the original SDK AND the MirkoSDK header, you can get this with Dalto's imageconverter.

Code:
/*
 * Draws single Tile from a horizontal tile strip (with header)
 * solid
 *
 * *sprite                    bitmap with header
 * x_lenght                   width of the tile
 * tile_num                   desired tile (0-n)
 * put_x, put_y               screen position
 * *framebuffer               desired buffer
 */
void syn_drawTile(u16 *sprite, int x, int y, int x_len, int tile_num, u16 *framebuffer)
{
  int      i;
  int      px, py;
  int      tx, ty;      // temp x/y
  int      vw, vh;      // visible height/width
  int      foff, doff;  // fb offset/data offset
  SHEADER  *sheader = (SHEADER*) sprite;
  int      size_y = sheader->size_y;
  int      size_x = sheader->size_x;
  int      os = x_len * size_y * tile_num;
  u16      color;

  tx = x + x_len; ty = y + size_y;

  // out of screen check
  if ((x>319) || (y>239)) { return; }
	if ((tx<0)  || (ty<0))  { return; }

  // Positional offsets
  px = max(0, x);
  py = max(0, y);

  // Visible height
	if(y<0)
  {
  vh = min(ty, 240);
	}else{
  if(ty>240)
    {
  	vh = size_y - (ty-240);
  }else{
  	vh = size_y;
  }
  }

  // Visible width
	if(x<0)
  {
  vw = min(tx, 320);
	}else{
  if (tx>320)
    {
  	vw = x_len - (tx-320);
  }else{
  	vw = x_len;
  }
  }

  // Data offset
  doff = (size_y * MAX(0, -y)) + MIN(0, -x) + os;
  // Framebuffer offset
  foff = px*240 + py;

  // Draw visible pixels only
	for(i=0; i<vw; i++)
  {
  memcpy(framebuffer+foff, &sprite[doff+6], vh*2);
  doff += size_y;
  foff += 240;
	}
}
 
damn browser cache, he is not using the drawTile() :( so here is the other one

Code:
/*
 * Uses real screen coords
 * and rotated sprite data like the official SDK.
 * You can create this format with dalto's Imageconverter
 */
void syn_drawSprite(u16 *sprite, int x, int y, u16 *framebuffer)
{
  int      i;
  int      px, py;
  int      tx, ty;      // temp x/y
  int      vw, vh;      // visible height/width
  int      foff, doff;  // fb offset/data offset
  SHEADER  *sheader = (SHEADER*) sprite;
  int      size_y = sheader->size_y;
  int      size_x = sheader->size_x;
  u16      color;

  tx = x + size_x; ty = y + size_y;

  // out of screen check
  if ((x>319) || (y>239)) { return; }
	if ((tx<0)  || (ty<0))  { return; }

  // Positional offsets
  px = max(0, x);
  py = max(0, y);

  // Visible height
	if(y<0)
  {
  vh = min(ty, 240);
	}else{
  if(ty>240)
    {
  	vh = size_y - (ty-240);
  }else{
  	vh = size_y;
  }
  }

  // Visible width
	if(x<0)
  {
  vw = min(tx, 320);
	}else{
  if (tx>320)
    {
  	vw = size_x - (tx-320);
  }else{
  	vw = size_x;
  }
  }

  // Data offset
  doff = (size_y * max(0, -x)) + max(0, -y);
  // Framebuffer offset
  foff = px*240 + py;

  // Draw visible pixels only
	for(i=0; i<vw; i++)
  {
  memcpy(framebuffer+foff, &sprite[doff+6], vh*2);
  doff += size_y;
  foff += 240;
	}
}
 
man you confuse me...

LOL, what specifically? Ask, and I'll try to be more clear. :)

In any case, my problems were my own stupid fault, and had nothing to do with Mirko's excellent functions. Yes, his sprite drawing functions correctly check to see if things are offscreent and don't draw if they are (although, it might be more efficient to have them check upfront if the entire sprite is offscreen before doing anything, and if so, just return).

My problem was just a few typos where pointers were involved, checking x instead of y and whatnot. A great arguement for not staying up late and coding on no sleep. :p

In any case, my little lib now handles an arbitrary number of layers with an arbitrary number of tiles that are composed of an arbitrary number of sprites (for animation), and can scroll them all in parallax at different speeds, where the speeds are in pixels/sec (so it'll be the same scroll speed no matter what frame rate you're getting, you can clock it down to 40 MHz and get less FPS or up to 133 and get better FPS, but the sprites will all move the same amount no matter what).

I'm actually quite happy with how it's coming along, I'm done pretty much everything I wanted to implement for layers and tiles, so I'm going to move on to writing some helper functions that load all your tiles and layers out of a zda archive. :)
 
Last edited by a moderator:
That sounds really great unit3!
Any indication of speed? e.g. Whats the slowest clockspeed you can run it at?

For example, 2 layers of paralax, then your background and various sprite characters - what clockspeed can you run this at, and how many fps will it go (approx) ?
 
That sounds really great unit3!
Any indication of speed? e.g. Whats the slowest clockspeed you can run it at?

For example, 2 layers of paralax, then your background and various sprite characters - what clockspeed can you run this at, and how many fps will it go (approx) ?
Well, I was running my test app last night (2 layers, 2nd layer is 50% transparent, each layer has 150 sprites, all are animating), and it was running decently at 60MHz. I haven't done any benchmarking though, so I may write a benchmarking app that lets you add and remove layers and change the layer transparency on the fly with a FPS overlay, so I can get a better idea of the performance.

One thing I have noticed is that, predictably, the introduction of transparency slows things down quite a bit. My system is based around the RTC, so it can't get any faster than 64fps (since that's the granularity of the RTC). I was keeping quite close to that even at low (40 MHz clock speeds) without transparency, but as soon as I reduced the alpha values of my primary layer the FPS dropped visibly at the lower clockspeeds. Most games only use the transparency in certain situations, or for only a couple sprites, so this shouldn't be too big of a drawback.

Suffice to say that Mirko's drawing functions are already quite efficient, so it's pretty fast as is. I imagine that Mirko's drawing could get slightly faster if some custom ASM blitting routines were written, but I'm doubtful that it would be really worth the time and effort. We'll see after some more thorough benchmarking whether or not it looks like I should put in the effort. ;)



If you're interested in using the library, I'll probably be putting it on sourceforge as soon as I decide on a license. I'm leaning to the GPL (of course), but I want to make sure that commercial products can link to and use it. I'm planning on adding a ton more functionality to it (sprite/tile/layer loading from zda, some sort of input management system), but for the time being the tiles/layers system seems to work quite well, once you wrap your head around how I've organized things. Which reminds me, once it's actually available for download, I'll want all the opinions people can give me about how it's currently organized, and how it can be made easier to use and understand while remaining just as powerful. I'd also love to get example code from other people to bundle with it, so that people downloading it can see how to use it to do lots of different things.

Which reminds me, is anyone working on a GUI widget set that sits on top of Mirko's SDK? I'd like to have an easy way to pop up windows with loading information, error messages, status info, etc., but I don't really want to code that all into my library.
 
Last edited by a moderator:
Suffice to say that Mirko's drawing functions are already quite efficient, so it's pretty fast as is. I imagine that Mirko's drawing could get slightly faster if some custom ASM blitting routines were written, but I'm doubtful that it would be really worth the time and effort. We'll see after some more thorough benchmarking whether or not it looks like I should put in the effort. ;)


well my functions for solid sprites I posted above using the memcpy replacement are way faster than the MSDK functions and have still room for optimizations.

do you have already coded something like a c-buffer to prevent overdraw on multiple solid layers?
 
Last edited by a moderator:
As are mine. Mr.Mirko has done a great job with his SDK replacement, but the sprite routines are not optimised.

I am working on a GUI library for my project GPDesktop (see http://www.gp32x.de/board/index.php?showtopic=16533 and http://www.pea.co.nz/GP32/), though when it will be ready is not certain. I work on it as much as I can, but I have a job and wife etc too :) So far it pops up custom windows and can render text to them.

Just to let you know:
My original blitting was rendering full-screen at 1 to 2fps using Mr.Mirkos routines, and my new routines (and Synchro's also) do the same at 42fps!! Please note that I never tested Mr.Mirkos routines on the actual hardware, just geepee32, but my new routines run at 42fps on both hardware and geepee32. My demo app ran at 40fps.

But those are some impressive results you have already!
 
Last edited by a moderator:
[well my functions for solid sprites I posted above using the memcpy replacement are way faster than the MSDK functions and have still room for optimizations.

do you have already coded something like a c-buffer to prevent overdraw on multiple solid layers?
Nope, haven't done any C-buffer stuff, I wanted to wait until benchmarking to see if I needed it. Looks like I do. Of course, it's late and I can't think of a nice way to implement it at the moment, so if you have any good ideas, let's here them! :)

Here's the informal results from the benchmarking app I just whipped together. It uses two layers (well, an arbitrary number, but 2 is enough to give fairly good data on the current lib), 150 animated sprites per layer (so 300 total).

Solid (no alpha):
40MHz ~= 9fps
50MHz ~= 11fps
66MHz ~= 15fps
100MHz ~= 23-24 fps
133MHz ~= 30-31 fps

w/48% alpha on all sprites:
133MHz ~= 23fps

Things to note: alpha blending seems to only incure a few FPS penalty at any CPU speed (it's not like a 50% speed hit), and scrolling the layers doesn't seem to impact the FPS at all, which means that at least that part of my lib is fairly optimized. ;)

So, you're right, there is probably a lot of room for improvement in the blitting that's going on. Now, I'd like to clarify the routines you posted previously: do they use the same in-memory image format as Mirko's SDK? Or does it just use Mirko's header, and another (rotated) format for the sprite data?... basically, what I want to know is if I can use your routine as a drop in replacement for gp_drawSpriteH(). It looks like you're doing bounds checking, so I'm good there at least. ;)

As for blended/alpha sprite drawing, memcopy probably doesn't work, but have you thought about writing an asm optimized version? Or is there not much point? The speed difference between the alpha and non-alpha blitting right now isn't that bad for large numbers of sprites, but I imagine that if I started using your memcopy function but still had to rely on Mirko's alpha function, the difference would be a lot greater.

... which begs another question. If your routine here is a much faster drop-in replacement for gp_drawSpriteH(), shouldn't Mr.Mirko just include it in his SDK as the official gp_drawSpriteH() function so that everyone can take advantage of it? :)
 
Last edited by a moderator:
Back
Top