gbadev.org forum archive

This is a read-only mirror of the content originally found on forum.gbadev.org (now offline), salvaged from Wayback machine copies. A new forum can be found here.

Coding > Hblank problems

#60814 - blacksun - Tue Nov 15, 2005 2:58 am

Sorry if this has been posted before, but couldn't find any answers by searching. I am attempting to use sprite multiplexing for my game. I already have the interrupts in place and active. And it will actually do a little of what I want. I am probably committing a crime against nature with my code, but what I am attempting is to just draw the current sprites that are on the current VCount line. Here is my code.

Code:

if((REG_IF & INT_HBLANK) == INT_HBLANK)
      {
         //Go through the number of sprites
         for(int i = 0; i < numOfSprites; i++)
         {
            //If the current VCount line is intersecting a sprite.  11 is the size of the sprite
            if(REG_VCOUNT >= spriteArray[i].yPos && REG_VCOUNT <= spriteArray[i].yPos+11)
            {
               //Update the OAM

               //New Y position
               sprites[index].attribute0 &= 0xFF00;
               sprites[index].attribute0 |= spriteArray[i].yPos;

               //New X position
               sprites[index].attribute1 &= 0xFE00;
               sprites[index].attribute1 |= spriteArray[i].xPos;

               //New OAM index
               sprites[index].attribute2 = spriteArray[i].spriteIndex;            

               //increase the range of the OAM by 4.  4 is 512/128(max # of sprites)
               end+= 4;
            }
         }
            //Copy the sprites to be drawn, but only draw a section to save speed
            CopyOAM(0, end);
      }


What it is doing is drawing the first 2 rows, 14 sprites, but no more. It also has flickering sprites on the right side of my grid from the bottom of the grid to the bottom of my last sprite. It should be drawing 7*12 sprites.

thanks

#60815 - blacksun - Tue Nov 15, 2005 3:02 am

Ok forgot to add index++ in there, but then that just makes things worse

#60816 - DekuTree64 - Tue Nov 15, 2005 3:48 am

I don't know what the exact problem is, but I can say there's no way that will run within a single HBlank with more than a few sprites.

Instead, what you could do is set up a sort of double buffering system that copies in sprites several lines before they'll actually be displayed. Alternate between 2 blocks of OAM indices, so while one is showing, the other is being filled.
Maybe use indices 0-31 as block 0, and indices 32-63 as block 1.

Here's an example, because it's hard to explain in words:
Code:
 ________
|  A     |   scanline 0
|B    C  |   scanline 40
|  D     |   scanline 80
|_E_F__G_|   scanline 120

All the letters are sprites. The first thing to do is seperate them all into groups, making sure that each group can fit into one of your OAM blocks. We'll pretend only 2 actors can fit in each block.
So then, we'll say sprite A and B are group 1, C and D are group 2, E and F are group 3, and G is group 4.

Start by setting up the first 2 groups during VBlank. Group 1 goes in OAM block 0 (indices 0-1 for our example), group 2 goes in OAM block 1 (indices 2-3).

As soon as group 1 is fully drawn (hblank at the end of line 79), overwrite block 0 with group 3. There's still a full 40 scanlines before the top of E and F, so no worries about running out of time.

As soon as group 2 is fully drawn (hblank at the end of line 119), overwrite block 1 with group 4. Here it looks like G is on line 120 too, so we'll just have to hope for the best on getting him copied in time :)


I hope that makes a bit of sense. I've wanted to do a system like that for a long time, but haven't had a reason to so far. Maybe sometime I'll do a tetris attack clone, and draw some nice 15x15 blocks so I'll have to use sprites :)
_________________
___________
The best optimization is to do nothing at all.
Therefore a fully optimized program doesn't exist.
-Deku

#60823 - LOst? - Tue Nov 15, 2005 4:19 am

I have spent too many hours trying to make hblank work as I want. It always turned me down. When it comes to GBA programming, hblank is the pure evil devil.
_________________
Exceptions are fun

#60878 - blacksun - Tue Nov 15, 2005 7:52 pm

Wow thanks a lot. That helps out a bunch. And yes I agree, HBlank is the devil incarnate.

I am a little confused on your wording. So if I get this straight, I first test for the VBlank interrupt and set up my first 2 sets of groups. Then after I draw them during the VBlank I will test for the Hblanks? And I forget, I know there is a VCount but is there also a HCount for the HBlank session?

Thanks

#60927 - tepples - Wed Nov 16, 2005 3:06 am

DekuTree64 wrote:
I hope that makes a bit of sense. I've wanted to do a system like that for a long time, but haven't had a reason to so far. Maybe sometime I'll do a tetris attack clone, and draw some nice 15x15 blocks so I'll have to use sprites :)

Tetris Attack for GBA was done with 13x13 pixel blocks (72 sprites), albeit with only one displayed playfield. With 12x12 pixel blocks, only one side would need to use sprites; the other side could use the 2-layer 12x12 hack used in Puyo Pop and Luminesweeper.
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.

#60951 - DekuTree64 - Wed Nov 16, 2005 6:34 am

blacksun wrote:
So if I get this straight, I first test for the VBlank interrupt and set up my first 2 sets of groups. Then after I draw them during the VBlank I will test for the Hblanks?

Yeah. You'll need to find the "bottom" scanline of each group (when that group will be fully drawn, and safe to overwrite). Then in your HBlank handler you can do something like
Code:
if (REG_VCOUNT >= lastGroupBottom)
{
    Copy curGroup to curBlock
    lastGroupBottom = curGroupBottom
    curGroup++
    toggle curBlock
}


Quote:
And I forget, I know there is a VCount but is there also a HCount for the HBlank session?

Nope, the closest thing is bit1 of DISPSTAT to check if you're in HBlank or not.
Sometimes that can be useful though. If you really need every last cycle of HBlank (like to copy a whole 256 color palette), you can use a VCount interrupt to get into your function at the start of the line and get everything set up, then sit and wait until DISPSTAT bit1 gets set, and then start your copy or whatever immediately.

tepples wrote:
Tetris Attack for GBA was done with 13x13 pixel blocks (72 sprites), albeit with only one displayed playfield. With 12x12 pixel blocks, only one side would need to use sprites; the other side could use the 2-layer 12x12 hack used in Puyo Pop and Luminesweeper.

Yeah, I like vs. mode a lot more than endless. Doing split sprites/BGs would be more practical, but then I'd have to think of something else to use multiplexing on :)
_________________
___________
The best optimization is to do nothing at all.
Therefore a fully optimized program doesn't exist.
-Deku

#60977 - wintermute - Wed Nov 16, 2005 12:27 pm

DekuTree64 wrote:

Quote:
And I forget, I know there is a VCount but is there also a HCount for the HBlank session?

Nope, the closest thing is bit1 of DISPSTAT to check if you're in HBlank or not.
Sometimes that can be useful though. If you really need every last cycle of HBlank (like to copy a whole 256 color palette), you can use a VCount interrupt to get into your function at the start of the line and get everything set up, then sit and wait until DISPSTAT bit1 gets set, and then start your copy or whatever immediately.


That's what Hblank DMA is for. You can use a vcount irq to get to the start of the line then just initiate a DMA transfer with hblank timing - http://nocash.emubase.de/gbatek.htm#dmatransfers . You need to set bit 5 of REG_DISPCNT for this to work with OAM too.

#61489 - Joat - Sun Nov 20, 2005 9:29 pm

Another word of warning for sprite multiplexing: you have to change sprites at least a scanline before they are to be rendered, i.e. your copy has to end at least a full scanline before the sprites copied over are to be displayed.
_________________
Joat
http://www.bottledlight.com

#67730 - blacksun - Fri Jan 20, 2006 1:32 am

Ok I think I am getting closer. Now my H-Blank section looks like this.

Code:

      if((REG_IF & INT_HBLANK) == INT_HBLANK)
      {

         if(REG_VCOUNT == 26)
            CopyOAM(4, 8*4);
         if(REG_VCOUNT == 37)
         {         
            CopyOAM(32, 28+32);
               for(int i = 1; i < 8; i++)
            {
               sprites[i].attribute0 &= 0xFF00;
               sprites[i].attribute0 |= spriteArray[14+i].yPos;

               sprites[i].attribute1 &= 0xFE00;
               sprites[i].attribute1 |= spriteArray[14+i].xPos;

               sprites[i].attribute2 = spriteArray[14+i].spriteIndex;
            }
         }

         if(REG_VCOUNT == 48)
            CopyOAM(4, 8*4);


I have it so CopyOAM transfers the sprite attributes into the OAM memory.

What happens is it quickly shows the drawing for the first 7 blocks, then the next line, then the first line disappears and the third line is drawn. It seems like the h-blank interrupt isn't being triggered continuously because it looks like it only performs it once.

#67770 - LOst? - Fri Jan 20, 2006 5:11 am

blacksun wrote:
Ok I think I am getting closer. Now my H-Blank section looks like this.

Code:

      if((REG_IF & INT_HBLANK) == INT_HBLANK)
      {

         if(REG_VCOUNT == 26)
            CopyOAM(4, 8*4);
         if(REG_VCOUNT == 37)
         {         
            CopyOAM(32, 28+32);
               for(int i = 1; i < 8; i++)
            {
               sprites[i].attribute0 &= 0xFF00;
               sprites[i].attribute0 |= spriteArray[14+i].yPos;

               sprites[i].attribute1 &= 0xFE00;
               sprites[i].attribute1 |= spriteArray[14+i].xPos;

               sprites[i].attribute2 = spriteArray[14+i].spriteIndex;
            }
         }

         if(REG_VCOUNT == 48)
            CopyOAM(4, 8*4);


I have it so CopyOAM transfers the sprite attributes into the OAM memory.

What happens is it quickly shows the drawing for the first 7 blocks, then the next line, then the first line disappears and the third line is drawn. It seems like the h-blank interrupt isn't being triggered continuously because it looks like it only performs it once.

It is easy to understand why. Hblank is too short for you to copy sprites like this. It will never finish during one hblank, and it will continue during the drawing time, and possibly the next hblank too.

Here are my advices: Cut as much code as possible in the hblank routine. Don't use that for loop! Do that stuff in vblank, and then DMA it to OAM.

I say DMA everything during hblank that has already been set up in vblank.
You hblank routine should be empty, except for some if conditions. Then you will get results.
_________________
Exceptions are fun

#67861 - blacksun - Fri Jan 20, 2006 11:09 pm

Ok, i guess I need a couple things cleared up. I thought that V-Blank is triggered after VCount has reached 160. Would I be able to use VCount as my scan lines then?

Also, is VBlank period longer or shorter than HBlank? I thought that since HBlank is longer you can do more computations during the interrupt.

And finally, since VBlank preceeds an HBlank, what would you suggest to setting up 2 block in the OAM, drawing the first, drawing the second, then loading something else in the first, then drawing the first again and so on.

#67878 - keldon - Sat Jan 21, 2006 1:23 am

VBlank is much longer than HBlank. Check the documentation for exact figures.

VBlank is vertical blank and happens once a frame - about 60 times per second. HBlank is horizontal blank - this happens 160 times per frame.

Yes VCount is the current scanline. But no, VBlank does not precede HBlank.