GP32 Playing With The Framebuffer


pea

developer
Joined
Oct 3, 2004
Messages
1,089
Age
45
Location
New Zealand
Website
www.projectitis.com
Is it wise to play around with the screenbuffer?

For example, once I load a gif into memory (16bbp) can I simply set the framebuffer pointer to the start of the data, rather than copying the data to the screenbuffer area?

Code:
gif = loadGif('path/to/gif');
framebuffer = &gif->data;

Or something like this:

Code:
u16 *myBuffer;
myBuffer = (u16*)malloc( sizeof(u16)*LCD_WIDTH*LCD_HEIGHT );

Cheers,
pea
 
'framebuffer' is just a variable. You can set it to whatever you want. You need to write to the address the hardware registers are setup for. I believe this is #define'd in Mirko's SDK as FRAMEBUFFER, so framebuffer = FRAMEBUFFER should do the trick.

If you really want to do what I'm thinking your thinking :) Then have a look at the 'rLCDADDR1/2/3' registers (think that's what they are called). I think there's probably a frame buffer set routine in Mirko's sdk too.

Setting it directly to a GIF memory will not work - GIFs are compressed to start with, and even if you decompress them, the gp32's screen is rotated 90 degrees. No reason it shouldn't work once decompressed and rotated though.

Always remember: LCD Width = 240, LCD Height = 320.
 
Yeah, my gif is loaded and uncompressed, and there is an option (I don't use it) to rotate it 90deg clockwise.

Using Mirko SDK and geepee32 emulator, drawing a fullscreen 16bpp image is sooo slow that you can literally see the image drawing down the screen. My estimate is less than 2fps. I REALLY need to update the MrMirko graphics drawing routines...
 
Don't forget:

GeePee32 only runs at around 40mhz or so.

Mirko's graphic routines carry out a whole load of maths for every plotted pixel, which is why it seems slow. If you know what your drawing, you can optimize it greatly.
 
Squidge posted on Dec 1 2004 at 08:03 AM said:
'framebuffer' is just a variable. You can set it to whatever you want.

I'm probably wrong, but I'm sure there have been a fair few discussions relating to reasons why you shouldn't set it to just anywhere. Mainly because certain areas of memory have certain settings with regards to the MMU that can make them much slower (or faster) to read/write.
 
Last edited by a moderator:
RobertJ posted on Dec 1 2004 at 11:27 AM said:
Mainly because certain areas of memory have certain settings with regards to the MMU that can make them much slower (or faster) to read/write.

This is true, but there are 2 more reasons:
First, there are alignment problems also (which is not that bad), and the fact that your gif will have to be 240 pixels (tall / wide, it depends if you rotate it or not) if you want it to be displayed correctly.

So IMO it's not a good idea. Try to use a dma channel if your gif has the good size, but displaying a 16bpp picture should not be that long once you unpacked it.
 
Last edited by a moderator:
RobertJ posted on Dec 1 2004 at 12:27 PM said:
Squidge posted on Dec 1 2004 at 08:03 AM said:
'framebuffer' is just a variable. You can set it to whatever you want.

I'm probably wrong, but I'm sure there have been a fair few discussions relating to reasons why you shouldn't set it to just anywhere. Mainly because certain areas of memory have certain settings with regards to the MMU that can make them much slower (or faster) to read/write.

Maybe I need to make myself more clear. I meant that you can set "framebuffer" to anything as it's just a variable and doesn't affect any hardware. If you move the frame buffer elsewhere, you need to setup the MMU to disable caching for that particular area of memory I believe. I just use the two 'OS' supplied frame buffers.

Also, I'm sure Spiv said that DMA on the GP32 is fairly bad and can actually be slower than doing it in software, unless of course you want to do something else whilst the data is being copied. For this reason, most people only bother with DMA for sound, or to throw data around whilst in an interrupt (so you can exit the interrupt quicker and leave the copy process going in the background).
 
Last edited by a moderator:
pea posted on Dec 1 2004 at 09:49 AM said:
Using Mirko SDK and geepee32 emulator, drawing a fullscreen 16bpp image is sooo slow that you can literally see the image drawing down the screen. My estimate is less than 2fps.

Mmmmm, when you say "you can literally see the image drawing down the screen", are you meaning it? If it's the case, it means that you are drawing on the primary screen buffer and you are not using a backbuffer. And that is really very slow.
If I'm not wrong, what you are asking for is something similar to GpSurfaceFlip(), as this function activates a predeclared screen buffer without transfering any data in memory.
 
Last edited by a moderator:
Squidge posted on Dec 1 2004 at 01:24 PM said:
RobertJ posted on Dec 1 2004 at 12:27 PM said:
Squidge posted on Dec 1 2004 at 08:03 AM said:
'framebuffer' is just a variable. You can set it to whatever you want.

I'm probably wrong, but I'm sure there have been a fair few discussions relating to reasons why you shouldn't set it to just anywhere. Mainly because certain areas of memory have certain settings with regards to the MMU that can make them much slower (or faster) to read/write.

Maybe I need to make myself more clear. I meant that you can set "framebuffer" to anything as it's just a variable and doesn't affect any hardware. If you move the frame buffer elsewhere, you need to setup the MMU to disable caching for that particular area of memory I believe. I just use the two 'OS' supplied frame buffers.

Also, I'm sure Spiv said that DMA on the GP32 is fairly bad and can actually be slower than doing it in software, unless of course you want to do something else whilst the data is being copied. For this reason, most people only bother with DMA for sound, or to throw data around whilst in an interrupt (so you can exit the interrupt quicker and leave the copy process going in the background).

The framebuffer is a memory region somethere in the 8MB gp32 memory.
You dont need to set the MMU cache (write back) off, for this region of memory, only
if you suffer from the gp32 soundbug. Then you "should" set off all caching on all havely used memory regions.

If you are unpacking your gif data, to a memory region, and want to see it later, you dont need to move the data, only set the Framebuffer to the picture data, and you see it.
void gp_setFramebuffer ( void *addr, int vsync );
will do this for you.

One little hint, dont use :
void gp_drawPixel16 ( int x, int y, u16 color, u16 *framebuffer ) ;
to display a (240x320) picture. Its not slow, but it got a overhead.

But if your gif viewing only allows 2fps, then you suffer from a lot other
design problems.
Also remember, geepee32 will only emulate a 40Mhz gp32, on a fast PC ( 3000+ )

ciao
 
Last edited by a moderator:
Yes Oankali I am drawing to the active framebuffer and not flipping, just while I am testing. I thought that the only effect this would have is that I might see jagged lines etc while the LCD is eing redrawn, but I did not think it would affect the speed of the draw ??

Mr.Mirko : The gif is loaded and decompressed, and is sitting in memory as a 240x320 array of u16 (16bpp). All I am doing is drawing this to the screen using the Mirko draw sprite routine in a while loop, with no extra processing or waiting loops. I can't see any reason why it should be so slow.

I'll try flipping the framebuffer and see if it is faster.
 
Of course.
BTW: Double buffering didn't have any affect apart from you can't see the physical 'drawing' now. It is still slow as anything.

A point to note: The image is 640x480, at 16bpp
Another point: gpd_system.framebuffer[0] swaps between FRAMEBUFFER1 and FRAMEBUFFER2 with every call to gpd_swapFramebuffer.

Code:
	// Main loop
	status = GPD_SYSTEM_OK;
	while (1) {
  // Do control
  gpd_drawCanvas( gpd_system.background , x,-120, gpd_system.framebuffer[0] );
  
  if (gp_getButton()&BUTTON_LEFT){ x--; gp_drawString( 30, 200, 4, "left", 0xF800, gpd_system.framebuffer[0] ); }
  if (gp_getButton()&BUTTON_RIGHT){ x++; gp_drawString( 200, 200, 5, "right", 0xF800, gpd_system.framebuffer[0] ); }

  gpd_swapFramebuffer( &gpd_system );
  
  // Terminate
  if (status==GPD_SYSTEM_TERMINATE){ 
  	gpd_kill( &gpd_system );
  	gp_Reset();
  }
	}

Here is the gpd_drawCanvas function:

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

void gpd_drawCanvas( tGPD_canvas *canvas, short x, short y, u16 *framebuffer ){
     short xx,yy;
     int   i=0;

     for (yy=0; yy<canvas->height; yy++)
     for (xx=0; xx<canvas->width; xx++) gpd_setPixel(x+xx,y+yy,(u16)canvas->data[i++],framebuffer);
}

And here are some structures in case you are interested:

Code:
typedef struct tGPD_canvas{
	int width;
	int height;
	unsigned short *data;
}tGPD_canvas;

typedef struct tGPD_sys{
	// ### Graphics subsystem
	// Frame buffers
	u16 *framebuffer[3];
	byte framebufferIndex;
	// Desktop background
	tGPD_canvas *background;
}tGPD_sys;

As you can see, the gpd_drawCanvas routine is modified slightly from MrMirkos to handle my structs, but apart from that is identical.

What you can also see is that it goes through every single pixel in the 640x480 image. even though it only draws 1/4 of those pixels, and for every single pixel (no matter if its drawn or not) it does four comparisons to see if it is within the screen area.

As it is it runs at about 1 fps

If I modify the code to set a pixel_offset and a line_offset (basically clipping the image before I draw it to the screen) then I don't need any comparisons per pixel draw, and this speeds it up to about 2 fps!

But still not speedy is it!
 
The problem is that the 640x480 picture will not fit in the framebuffer !!! And if you set the framebuffer to the picture data, then it won't appear correctly.

You need a good clipped 16 bit blitting routine. The Una-i (I think it was called like that) library should have fast ones.
 
Yes, thats true. But in this case I am not worried about playing with the framebuffer, just with the speed of the draw.

I have already tried it with an image clipping routine, and it only speeds it up to 2fps (rather than 1fps)
 
Here is a working example with official SDK. For the example I just used a rotated bitmap included in bitmaps.h
If you are interested in the complete source and the .fxe, send me a private.

Code:
#include <gpdef.h>
#include <gpstdlib.h>
#include <gpgraphic.h>
#include <gpgraphic16.h>

#include "gpmain.h"
#include "bitmaps.h"


// Declare double buffering variables
GPDRAWSURFACE gtSurface[2];
int giSurface;


unsigned short *LoadImage(char *filename, int *width, int *height)
{
  // Allocate memory
  // ....

  // Load & decode image
  // ...

  // Here for the example
  *width = image_width;
  *height = image_height;
  return (unsigned short *) image_bitmap;
}

void ReleaseImage(unsigned short *image)
{
  // Release allocated memory
}

void GpMain (void *arg)
{
  unsigned short *image;
  int x, y, width, height;
  int pressedKeys;

  // Initialize graphicS mode
  GpGraphicModeSet(16, NULL);

  // Create 2 surfaces
  GpLcdSurfaceGet(&gtSurface[0], 0);
  GpLcdSurfaceGet(&gtSurface[1], 1);

  // Activate primary surface
  GpSurfaceSet(&gtSurface[0]);
  giSurface = 1;

  // Load image
  if ((image = LoadImage("image.gif", &width, &height)) == NULL)
  {
    // Error
    GpAppExit();
  }

  x = y = 0;
  while(TRUE)
  {
    // Show image
    GpBitBlt16(NULL, &gtSurface[giSurface], 0, 0, GPC_LCD_WIDTH, GPC_LCD_HEIGHT,
               (unsigned char *) image,
               x, y, width, height);
    
    // Get keys
    pressedKeys = GpKeyGet();

    // Manage keys
    if (pressedKeys & GPC_VK_START)
      break;
    else if (pressedKeys & GPC_VK_LEFT)
    {
      if (x > 0)
        x--;
    }
    else if (pressedKeys & GPC_VK_RIGHT)
    {
      if (x < width - GPC_LCD_WIDTH)
        x++;
    }
    else if (pressedKeys & GPC_VK_UP)
    {
      if (y > 0)
        y--;
    }
    else if (pressedKeys & GPC_VK_DOWN)
    {
      if (y < height - GPC_LCD_HEIGHT)
        y++;
    }

    // Flip surfaces
    GpSurfaceFlip(&gtSurface[giSurface++]);
    giSurface &= 0x01;
  }
  ReleaseImage(image);

  // Reset GP32
  GpAppExit();
}
 
Good gawd, calling a funciton to draw a pixel kills you every time on every OS pretty much .. ie: Just calling a funciton (push/pop stack a couple of times, cache bolloxing, etc) will kill you.. ie: CAlling a function just to do a single pixel is way overkill.

Theres a lot of ways to optimize, but even just replacing the function call with a freamebuffer calculation will hugely improve your performance; to keep improving, do your own pointer arith blitting using pre-rotated images..

ie: In C code, its hard to beat the old .. while(condition) *x++ = *y++; type blit. (Which assumes a pre-rotated image, otherwise you have to do a += in there to bump down to the next line for each pixel; that still performs pretty well considering the extra add/assign every pixel..

Remember, when you're talking pixels, your'e talking 320x240 operations per screen, which is a heck of a lot; if you're doing a function call and some math and dereferences to arrays and such, then you're doing a lot of work per pixel and thus a huge amount per frame..

jeff
 
Back
Top