Strategies for Reducing VRAM Usage?

slinga

Established Member
My game requires six sprites for the main character. I currently have them in TGA format. Each sprite is 32x64, ~8.3kb each, or 50kb for all six sprites. According to jo_sprite_usage_percent(), I am using 6%. I want 12 player support so 12 x 6% = 72% in just the main characters. I would prefer not to do palette swaps.

What is the best strategy for reducing the VRAM usage? In two of the sprites I have vertical symmetry so I can implement that. Is there a compressed format I can use with jo engine? I saw a reference for jo_sprite_add_8bits_image(), but I'm not sure what file format they are referring to or how to call that API. Doing the math it appears my sprites are currently using 4-bits per pixel.

Thanks in advance.
 
At 4bpp, each sprite should take 1K (32*64/2).

One simple way to reduce VRAM usage is to only keep the current animation frame in VRAM, but you still need to fit all the frames in main memory. Another technique is to subdivide the original sprite into multiple parts. That works best if there's a lot of empty space in the frame (eg. a fighting game character performing a high kick), or there's smaller repeated parts. That makes animation a bit more complex, and adds the processing overhead of the extra drawing commands, though in the first example it can actually be cheaper to not process a lot of blank pixels.
 
As antime said, the easy solution is to use 4 bpp or 8 bpp, it will reduce your texture size by 1/4 and 1/2 respectively.
If you are making a 2d game, use the VDP2 to dsiplay as much as you can, with only the moving objects being handled with the VDP1.
Tiling on the VDP2 is a must in that case.
Again, 4 bpp is your friend.

And palette swaps are amazing. If you are super careful in building your sprites/palettes, you could reuse the exact same sprite to show different actions just by palette swapping. But that's complicated, so I'd start with using 4 bpp. If that color depth is too low, you can mix it with tiling (like have 8x8 sprites make up your main caracter.)
As for easy ways to do it, I guess you could try with BGCON.
 
Thank you for the advice. I agree the simplest path forward is to reduce my bpp from 32 to 4/8. However jo_tga_load() only supports 24 and 32 bpp. What API were you calling to render 4 and 8 bpp TGAs? jo_sprite_add_8bits_image()?
 
Thank you for the advice. I agree the simplest path forward is to reduce my bpp from 32 to 4/8. However jo_tga_load() only supports 24 and 32 bpp. What API were you calling to render 4 and 8 bpp TGAs? jo_sprite_add_8bits_image()?
Jo Engine never supported 4 bpp afaik, you will have to work around it. I made a tread like 2 years ago in the Jo Engine forum with a small image converter I wrote, you could use that even if it's a bit dated now and could be improved
 
Thank you Xl2, that worked beautifully. I used your program here: 4 bpp sprite converter app. I shrunk the sprites from ~8k each to ~1k. Transparencies worked as well.

The only issue is my sprite is now upside down. I tried searching through sprites.c for all uses of SPR_ATTRIBUTE but it's always used with sprNoflip. Any advice? sprHflip, sprVflip, and sprHVflip are not defined by Jo Engine.
 
I think I spoke too soon. When I have two overlapping sprites, the transparencies cause weirdness. Basically I see through one sprite straight to the background when I expect to see the other sprite underneath.
 
I think I spoke too soon. When I have two overlapping sprites, the transparencies cause weirdness. Basically I see through one sprite straight to the background when I expect to see the other sprite underneath.
Your background is with the VDP2?
 
Is the SPD bit set in your drawing commands? When it's set, pixels with a value of zero (palette index or RGB value) are not skipped, but are drawn into the frame buffer.
 
In fact can you show us a screenshot and in Yabause's debugger your sprite command?
 
I'm attaching two screenshots:

1) 32bpp
- calls jo_tga_loader() and jo_set_background_sprite for the background
- jo_sprite_add_tga() and jo_sprite_draw3D2() for all of the sprites
- this seemed to work well, except I was using a ton of VRAM
- sprite transparencies work as I expect
- the z parameter to jo_sprite_draw3D2() lets me order the sprites how I want

2) 4bpp
- calls jo_tga_loader() and jo_set_background_sprite for the background
- load_4bits_bin_image() and render_CLUT_sprite() from XL2's 4bpp convertor and sample program
- VRAM usage dramatically lower
- Sonic and the pipe are flipped upside down, Tails is right-side up. I can no longer use jo_sprite_enable_*_flip() to flip the sprites as I was before
- transparencies work until I overlap them

Thanks in advance.
 

Attachments

  • 4bpp.png
    4bpp.png
    26.6 KB · Views: 318
  • 32bpp.png
    32bpp.png
    232.6 KB · Views: 304
I'm attaching two screenshots:

1) 32bpp
- calls jo_tga_loader() and jo_set_background_sprite for the background
- jo_sprite_add_tga() and jo_sprite_draw3D2() for all of the sprites
- this seemed to work well, except I was using a ton of VRAM
- sprite transparencies work as I expect
- the z parameter to jo_sprite_draw3D2() lets me order the sprites how I want

2) 4bpp
- calls jo_tga_loader() and jo_set_background_sprite for the background
- load_4bits_bin_image() and render_CLUT_sprite() from XL2's 4bpp convertor and sample program
- VRAM usage dramatically lower
- Sonic and the pipe are flipped upside down, Tails is right-side up. I can no longer use jo_sprite_enable_*_flip() to flip the sprites as I was before
- transparencies work until I overlap them

Thanks in advance.
It was a while ago that I worked with Jo engine, but I think with my image converter it's expected that you use SGL functions directly.
You could look at the exemples I made to make sure it's ok.
I suspect that you simply forgot to flip the transparent pixel bit, so it draws transparent pixels with 0x0000 values instead of skipping them.
Also look in Yabause sprite debugger to see if the sprite's orientation is ok.
Look at the vdp1 manual for more info.
 
I'm using the code directly from your 4bpp demo:

C:
/**Simple function to draw the sprites with Color Lookup tables**/
void render_CLUT_sprite(unsigned int id, int x, int y, int z)
{
    FIXED pos[XYZS];    pos[X]=toFIXED(x); pos[Y]=toFIXED(y); pos[Z]=toFIXED(z); pos[S]=65536;
    SPR_ATTR spr_attributes = SPR_ATTRIBUTE(id, LUTidx(id), No_Gouraud, CL16Look | ECdis | SPdis | HSSon ,  sprNoflip  );
    slPutSprite(pos , &spr_attributes , 0) ;
}

/**Simple function to draw the sprites with Color Bank from Color RAM**/
void render_CRAM_sprite(unsigned int id, int x, int y, int z)
{
    FIXED pos[XYZS];    pos[X]=toFIXED(x); pos[Y]=toFIXED(y); pos[Z]=toFIXED(z); pos[S]=65536;
    SPR_ATTR spr_attributes = SPR_ATTRIBUTE(id, LUTcramIdx(id), No_Gouraud, CL16Bnk | ECdis | SPdis | HSSon , sprNoflip  );
    slPutSprite(pos, &spr_attributes , 0) ;
}

/***************************
This sets palette
***************************/
void    set_palette(Uint16 * palette, Uint16 TextureId)
{
    //For SGL only use slDMACopy instead : slDMACopy( (void*)palette, (void*)adr, sizeof(palette) );
    jo_dma_copy(palette, (void*)(returnLUTaddr(TextureId)), sizeof(Uint16)*16);

    //Only if you want to use the VDP2 CRAM for your sprite, else skip this
    jo_palette_to_cram(palette, (void*)(returnCRAMaddr(TextureId)), 16);
}

/******************************
Load binary image
CAUTION : Jo Engine might throw an error if the texture's width/4 < 8,
The alternative would be to divide the height instead, but since the height doesn't need to be a multiple of 8 (ex.:3),
dividing by 4 might break everything (Ex.: 3/4 = 0).
Turn off the debug options in your makefile if that's an issue
******************************/
int                load_4bits_bin_image(char * sub_dir, char * name, unsigned short width, unsigned short height)
{
    jo_img *  img;
    jo_texture_definition   *texture;
    void * currentAddr = (void*)LWRAM;  //Makes it easier to track the address

    /**CD functions**/
    jo_file file;
    
    if(sub_dir != JO_NULL)
    {
        jo_fs_cd(sub_dir);
    }
    
    jo_fs_open(&file, name);
    jo_fs_read_next_bytes(&file, (void*)LWRAM, (8+(width*height>>1)+32));  //8 bytes for width, height and pointer, then the width*heigth/2 (since we use 4 bits data) + the size of the palette (always 16*sizeof(Uint16))
    jo_fs_close(&file);  //or use this : GFS_Load(GFS_NameToId(name), 0, (void *)LWRAM, (4+(width*height>>1)+32));
    
    if(sub_dir != JO_NULL)
    {
        jo_fs_cd(JO_PARENT_DIR);
    }

    /**Set the pointers**/
    img = (jo_img*)currentAddr;               currentAddr=(void*)(currentAddr+sizeof(jo_img));
    img->width=img->width>>2;  //Quick and dirty way to get around the fact that Jo Engine doesn't support 4 bits pixel data.
    img->data = (jo_color*)(currentAddr);      currentAddr=(void*)(currentAddr+(sizeof(Uint16)*img->width * img->height));

    /**Adds the img in memory**/
    int id = jo_sprite_add(img);

    /**Here we just fix the issue caused by using the add 8 bits image for 4 bits images**/
    texture=&__jo_sprite_def[id];
    texture->width=img->width<<2;  //Ghetto technique for compatibility with Jo Engine, but trying to replace the sprite will throw an error
    __jo_sprite_pic[id].color_mode=COL_16;
    texture->size = JO_MULT_BY_32(texture->width & 0x1f8) | texture->height;

    /**Here we set the palette**/
    set_palette((unsigned short *)currentAddr, id);

    return id;
}


The Yabause VDP1 Command List output for the Sonic image (renders upside-down):
Scaled Sprite
Zoom Point: Center-center
xa = 36, ya = 36, xb = 9, yb = 19
Texture address = 00010000
Texture width = 32, height = 64
Texture read direction: Normal
High Speed Shrink Enabled
Pre-clipping Enabled
Color mode: 4 BPP(16 color LUT)
Color lookup table: 0007D960
Color Calc. mode: Replace

The Yabause VDP1 Command List output for the Tails image (renders right-side up, but transparencies have issues like Sonic's):
Scaled Sprite
Zoom Point: Center-center
xa = 0, ya = 47, xb = 34, yb = 68
Texture address = 00011800
Texture width = 32, height = 64
Texture read direction: Normal
High Speed Shrink Enabled
Pre-clipping Enabled
Color mode: 4 BPP(16 color LUT)
Color lookup table: 0007DA20
Color Calc. mode: Replace

I will spend some time reading the VDP1 manual.
 
Hmmm, I am surprised that the Tails image is upside down in Yabause's VDP1 debugger.
Maybe it's an older version of my image converter :/
But you clearly here don't have the transparent pixel set, which explains your transparency problem.
Just set it and it will show correctly.

For the converter, cd 8 for 256 colors, cd 4 for 16 colors
 

Attachments

  • Image converter.zip
    337 KB · Views: 235
Hi XL2,

I believe I found the issue for the flipped sprites. When exporting my sprites to TGA some sprites were set to "Top Left" and others were set to "Bottom Left". Exporting all sprites to TGA as "Bottom left" fixed the alignment issue.

But you clearly here don't have the transparent pixel set, which explains your transparency problem.
Just set it and it will show correctly.

I'm stumped on how to do this. I was unable to find an "SPD" flag in SL_DEF.H. I did find and add a "CL_Trans" flag as follows:

C:
/**Simple function to draw the sprites with Color Lookup tables**/
void render_CLUT_sprite(unsigned int id, int x, int y, int z, int flip )
{
    FIXED pos[XYZS];    pos[X]=toFIXED(x); pos[Y]=toFIXED(y); pos[Z]=toFIXED(z); pos[S]=65536;
    SPR_ATTR spr_attributes = SPR_ATTRIBUTE(id, LUTidx(id), No_Gouraud, CL16Look | ECdis | SPdis | HSSon | CL_Trans, flip);
    slPutSprite(pos , &spr_attributes , 0) ;
}

Some sort of half transparency is happening. I guess this is OK, but is there another set of flags I could be using?

Thanks again.
 

Attachments

  • transparency.png
    transparency.png
    26.2 KB · Views: 214
  • transparency2.png
    transparency2.png
    28.3 KB · Views: 227
Last edited:
Hi XL2,

I believe I found the issue for the flipped sprites. When exporting my sprites to TGA some sprites were set to "Top Left" and others were set to "Bottom Left". Exporting all sprites to TGA as "Bottom left" fixed the alignment issue.



I'm stumped on how to do this. I was unable to find an "SPD" flag in SL_DEF.H. I did find and add a "CL_Trans" flag as follows:

C:
/**Simple function to draw the sprites with Color Lookup tables**/
void render_CLUT_sprite(unsigned int id, int x, int y, int z, int flip )
{
    FIXED pos[XYZS];    pos[X]=toFIXED(x); pos[Y]=toFIXED(y); pos[Z]=toFIXED(z); pos[S]=65536;
    SPR_ATTR spr_attributes = SPR_ATTRIBUTE(id, LUTidx(id), No_Gouraud, CL16Look | ECdis | SPdis | HSSon | CL_Trans, flip);
    slPutSprite(pos , &spr_attributes , 0) ;
}

Some sort of half transparency is happening. I guess this is OK, but is there another set of flags I could be using?

Thanks again.
SPdis = transparency disable, should be SPenb (or something like that).
And CL_Trans = half-transparency flag. I don't recommand that you use it.
Also, for the scale, use 65535 instead of 65536, as it causes your sprites to be 1 pixel too big.
 
Yes that was it. Thank you again for your help. My sprites appear to be rendering how I would expect.
4888
 
  • Like
Reactions: XL2
I'm struggling a bit with RAM/VRAM usage. Initially I thought I would be able to put all of my sprites in VRAM at once.

Using Jo Engine, VDP1 appears to only have around ~400k of VRAM instead of 512k. Each pipe image I have is 128x112 or 7.2k. I have 55 pipe images so I'm using a total of ~396 k in just the pipe images. If I had access to 512k VRAM instead of ~400k I believe I could fit all of my sprites into VRAM, simplifying the game.

I then attempted to do what Antime suggested by loading all of my images to main memory, and then stream them in and out of VRAM as needed. Unfortunately jo_malloc() only has around ~316k of RAM before it fails and returns NULL.

I will look into jo_add_memory_zone(unsigned char *ptr, const unsigned int size_in_bytes) as LWRAM appears to be unused in XL2's example code. Just wondering if you guys had any thoughts.
 
I'm struggling a bit with RAM/VRAM usage. Initially I thought I would be able to put all of my sprites in VRAM at once.

Using Jo Engine, VDP1 appears to only have around ~400k of VRAM instead of 512k. Each pipe image I have is 128x112 or 7.2k. I have 55 pipe images so I'm using a total of ~396 k in just the pipe images. If I had access to 512k VRAM instead of ~400k I believe I could fit all of my sprites into VRAM, simplifying the game.

I then attempted to do what Antime suggested by loading all of my images to main memory, and then stream them in and out of VRAM as needed. Unfortunately jo_malloc() only has around ~316k of RAM before it fails and returns NULL.

I will look into jo_add_memory_zone(unsigned char *ptr, const unsigned int size_in_bytes) as LWRAM appears to be unused in XL2's example code. Just wondering if you guys had any thoughts.
I always use lwram when I load stuff from discs, I doubt I did it in another way in these old demos.
You also won't have 512 KB available because you need to leave space for draw commands, gouraud shading tables and lookup tables.
400-450 KB seems about right, depending on how much you allocate for each category.
Offload everything you can to the VDP2 to save memory.
And if your goal is to do the halfpipe from Sonic 2, doing the pipe in 3d should be easy and it should work well enough.
 
Do you know if anything else is using LWRAM? If not I will go ahead and extend my jo_engine heap to use it as well.

Regarding using 3d for the pipe, I like the idea but I believe it will be above my ability to get it working before the end of the contest (~6 weeks).
 
Back
Top