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 > Drawing in bitmap mode: writing to screen or to array?

#7939 - Domovoi - Sun Jun 29, 2003 12:46 am

Hi there. I've been trying to get back into learning GBA development after having to give up on earlier attempts for a while, and I got to thinking...

When I'm in bitmap mode (say mode 4), and I want to, for instance, draw a background, and then a line over that background...

Suppose my background image is an array of values pointing to the palette entries. I could plot this array to the screen, and then after that, write the necessary pixels for the line to the screen. This is troublesome, since I'd have to figure out what color to make the second pixel (since it draws two pixels a time). Also, two draw operations...

I figured another way: Create an empty array, which will be considered the buffer. What I could do is simply memcopy the array with the background image into the buffer array, and after that, calculate the pixels for the line, and just alter those values in the buffer array. You wouldn't have to worry about the second pixel (since you're not drawing: just changing an array value), and when it's done, just plot the entire buffer array to the screen. Presto!

I think it would be convenient and fast! Am I on the right track, or are there better ways to do these things?

#7950 - tepples - Sun Jun 29, 2003 6:24 am

You've just described a software back buffer.

Because mode 4 is larger than the size of IWRAM, you'll have to put your software back buffer in slow EWRAM, and you'll have to endure a slow copy.

Most programs that draw real-time vector graphics to mode 4 seem to either draw by byte writes to even numbered pixels, reducing horizontal resolution to 120 pixels (as in Doom and most other textured 3D engines) or draw only horizontal spans of identical pixels (as in Star X and other flat-shaded 3D engines), where the left and right sides of a span can be special-cased.
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.

#7953 - Domovoi - Sun Jun 29, 2003 10:01 am

tepples wrote:
You've just described a software back buffer.

Because mode 4 is larger than the size of IWRAM, you'll have to put your software back buffer in slow EWRAM, and you'll have to endure a slow copy.

Most programs that draw real-time vector graphics to mode 4 seem to either draw by byte writes to even numbered pixels, reducing horizontal resolution to 120 pixels (as in Doom and most other textured 3D engines) or draw only horizontal spans of identical pixels (as in Star X and other flat-shaded 3D engines), where the left and right sides of a span can be special-cased.


Interesting... I didn't know about the different RAM types and sizes. So it's not possible to store -any- full screen picture in IWRAM then? How do you determine in which type of RAM to store certain values?


Anyway, the drawing of only even numbered pixels... Do you mean just write the same pixel twice, next to each other? Or do you mean actually only writing to the middle 120 pictures, reducing horizontal screen size?

And how does this horizontal span work? I don't understand the second technique you described. Could you expand on that?

#7971 - DekuTree64 - Sun Jun 29, 2003 6:22 pm

Domovoi wrote:

Interesting... I didn't know about the different RAM types and sizes. So it's not possible to store -any- full screen picture in IWRAM then?

Since 240*160=38400, and IWRAM is 32768 bytes, then no. Unless you're using one of the tiled modes with 16 color tiles, then that would be 2 px/byte, so 19200 bytes of IWRAM. Of course, then you'd have to write 2 pixels at a time again, defeating the whole purpose.
Quote:
How do you determine in which type of RAM to store certain values?

By default, nothnig is stored in EWRAM, so it's free to do whatever you want with. IWRAM is used for global vars, the stack, and any code to specifically put there. Usually if I have a large array that doesn't need high-speed access (for example, an array of color values to DMA or IRQ in on HBlank to do gradients), I put it in EWRAM. You can use __attribute__ ((section (".ewram"))) to have the compiler do it, or just treat EWRAM like a huge stack, and handle everything yourself.

Quote:
Anyway, the drawing of only even numbered pixels... Do you mean just write the same pixel twice, next to each other? Or do you mean actually only writing to the middle 120 pictures, reducing horizontal screen size?

Double-pixels. It just looks a little blocky horizontally, but it's acceptable for having twice the speed+not dealing with single pixels. But if you're going to write 16 bits at a time, you might as well use 16-bit mode and use the hardware to scale it to twice the horizontal resolution (either scale to 2x width in mode 3 and only draw to half the screen at once, or rotate 90 degrees and scale with mode 5. Which is better depends on if you're drawing horizontal or vertical strips (vertical is good for raycasters))

Quote:
And how does this horizontal span work?

You just do something like
Code:

if(xStart & 1)
{
   buf[xStart>>1] = (buf[xStart>>1] & 0xff) | (px << 8);
   xStart++;
}
if(xEnd & 1)
   buf[xEnd>>1] = (buf[xEnd>>1] & 0xff00) | (px);
xStart >>= 1;
xEnd >>= 1;
while(xStart < xEnd)
   buf[xStart++] = px2;   //where px2 = px | (px << 8)


It would be even faster if you write 4 pixels at a time, but depending on how long your spans generally are, the special casing of the first/last 3 might cut more speed than the improvement gains.
Also, you can do something similar for texture mapping, just loading in 2 pixels and ORing them together instead of using the precomputed px2.

#7978 - Quirky - Sun Jun 29, 2003 8:30 pm

Domovoi wrote:
And how does this horizontal span work? I don't understand the second technique you described. Could you expand on that?


When drawing a triangle, the general technique is to start at a the top x,y position and plot spans (or scanlines as they are sometimes called) to draw the triangle. Something like this, for example:

Code:

   .
   ...
   ......
   ....
   ...
   ..
   .


As you have to access the screen 2 pixels at a time, it's best to check the start and end of the span to see if they fall on an even pixel or an odd one. You deal with these "funny" pixels at either end, then the rest of the line is simply a bunch of 2-pixel writes.

#8000 - Domovoi - Mon Jun 30, 2003 8:58 am

Okay... I'll have to do a lot more reading on hardware specs, I see... I'm completely lost in these memory types. More reading up required.

I figured, by the way, that my software backbuffer is completely redundant, since you already -have- an array to write values to: The VRAM starting at 06000000.

Anyway, in the mean time, I've been writing a little function that actually puts a single pixel to the screen. I doubt it's fast (or even useful) though, but it seems to work. Basically, it takes an x-value, an y-value, and a color (only palette index at the moment). It then calculates which array element (of the VRAM array at 06000000) that pixel is stored in. By figuring out wether it's an odd or even value, it determines which of the two bytes in that array element it is. Then using some clever binary math, it puts the required color in there without touching the other pixel.

It seems to work well. It's probably too slow for real-life stuff, but it was a good excercise anyway.

Weird how it's not possible to store full-size images in fast memory... Seems like a design flaw. I wonder, do you think they only use a 120px horizontal resolution in games like Broken Sword? The backgrounds in that game look fantastic...

If that is the case, this opens up new possibilities for mode 5. I never thought of using that mode because of the smaller screen size.... I figured it would actually reduce the (already tiny) screen size. Do you say that you can use the hardware to rotate it 90 degrees and stretch it to the full screen size? That would mean you'd basically have mode 4 (when only writing even pixels), only in glorious 16 bit colors. Sounds great!

#8003 - Paul Shirley - Mon Jun 30, 2003 11:46 am

removed

Last edited by Paul Shirley on Sun Mar 28, 2004 10:03 pm; edited 1 time in total

#8017 - tepples - Mon Jun 30, 2003 8:11 pm

Domovoi wrote:
Do you say that you can use the hardware to rotate it 90 degrees and stretch it to the full screen size? That would mean you'd basically have mode 4 (when only writing even pixels), only in glorious 16 bit colors.

Modes 3, 4, and 5 accept the same affine scaling parameters for background 2 as Modes 1 and 2.
Code:
struct BGAFFINEREC
{
  u16 pa;  /* map_x increment per pixel */
  u16 pb;  /* map_x increment per scanline */
  u16 pc;  /* map_y increment per pixel */
  u16 pd;  /* map_y increment per scanline */
  u32 x_origin, y_origin;
};

#define BGAFFINE ((volatile struct BGAFFINEREC *)0x04000000)
/* only elements 2 and 3 work */

const struct BGAFFINEREC identity =
{0x100, 0, 0, 0x100, 0, 0};
const struct BGAFFINEREC rotated_mode5 =
{0, 0x100, 0x80, 0, 0, 0};

/* do this after you've set mode 5 */
BGAFFINE[2] = rotated_mode5;

/* do this once you're done */
BGAFFINE[2] = identity;

_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.