Double buffering and vram questions

Discuss the development of new homebrew software, tools and libraries.

Moderators: cheriff, TyRaNiD

Post Reply
Heretic
Posts: 33
Joined: Thu Feb 02, 2006 3:22 pm
Location: Pittsburgh, PA, USA
Contact:

Double buffering and vram questions

Post by Heretic »

I'm trying to do some simple graphics (basically moving a 10x10 px square around the screen), and I'm basing my code off of a lot of some print functions that draw directly to vram. The functions from it I call is:

Code: Select all

#define LINESIZE   512        //in short
#define FRAMESIZE  0xAA000      //in byte

unsigned char *vramtop=(unsigned char *)0x04000000;
unsigned long drawframe;

// get vram address for character position
unsigned char *GetVramAddr(unsigned long x,unsigned long y)
{
  return vramtop+(drawframe?FRAMESIZE:0)+x*4+y*LINESIZE*4+0x40000000;
}
I have a few questions about this code...
1) (I'm sure VERY noobish) how are these large numbers being handled by unsigned chars? Isn't the maximum size being exceeded many times over?
2) Is the vram set up with 2 frames for double buffering, and thats why drawframe is used to determine where to draw? or is this something different?
3) What is the +0x40000000 for?

Any other information would help greatly, because while I play with it I piece it together little by little, but my graphics are still flashing. This is what I have:

Code: Select all

void DrawSquare(int x,int y,int w,int h,int color)
{
	unsigned char * vram = GetVramAddr(x,y);
	int i,j;
	for&#40;i=0;i<h;i++&#41;
	&#123;
		for&#40;j=0;j<w;j++&#41;
		&#123;
			*&#40;&#40;unsigned long *&#41;&#40;vram+j*4&#41;&#41; = color;
		&#125;
		vram+=LINESIZE*4;
	&#125;
&#125;

int main&#40;&#41;
&#123;
  pspDebugScreenInit&#40;&#41;;
  SetupCallbacks&#40;&#41;;
  int x=50,y=50;
  SceCtrlData pad;

  while&#40;1&#41;
  &#123;
    sceCtrlReadBufferPositive&#40;&pad, 1&#41;;
    if&#40;pad.Buttons & PSP_CTRL_CROSS&#41;
          break;
    else if&#40;pad.Buttons & PSP_CTRL_LEFT&#41;
    &#123;
     	x--;
	&#125;
    else if&#40;pad.Buttons & PSP_CTRL_RIGHT&#41;
    &#123;
		x++;
	&#125;
    else if&#40;pad.Buttons & PSP_CTRL_UP&#41;
    &#123;
	    y--;
	&#125;
    else if&#40;pad.Buttons & PSP_CTRL_DOWN&#41;
    &#123;
        y++;
	&#125;
  	char buf&#91;16&#93;;
	Fillvram&#40;0x00FF00FF&#41;;
    DrawSquare&#40;x,y,10,10,0x00FFFFFF&#41;;
    printcoords&#40;x,y,buf&#41;; //buf will be&#58; "x&#58;00003 y&#58;00002" if &#40;x,y&#41; = &#40;3,2&#41;
    Print&#40;0,0,0x00FFFFFF,buf&#41;;
	drawframe = !drawframe;
  &#125;

  sceKernelSleepThread&#40;&#41;;
  return 0;  
  
&#125;
Again, I'm sorry if I am a total nub, but I've never dealt with things this low-level before. Any explanations, etc would also be very helpful.
http://omegagames.netfirms.com -- Support struggling programmers by suffering through their crappy games! (Ok, you don't have to...)
User avatar
Raphael
Posts: 646
Joined: Tue Jan 17, 2006 4:54 pm
Location: Germany
Contact:

Post by Raphael »

1) (I'm sure VERY noobish) how are these large numbers being handled by unsigned chars? Isn't the maximum size being exceeded many times over?
That's because it's actually a pointer to an array of chars, and as such is 4bytes in size. This is determined by the * after the type name. That's why it can contain such large numbers (which are nothing else than the memory address), even though the data at the mem location is only chars.
2) Is the vram set up with 2 frames for double buffering, and thats why drawframe is used to determine where to draw? or is this something different?
Well, the drawframe is a hint to allow for double buffering, because it switches to draw to two different locations in vram which are the size of one framebuffer. But vram as such isn't set up for that, unless you specify so (by using sceGuDrawBuffer and sceGuDisplayBuffer for initialization and simply calling a swapbuffer after each frame drawing). How you use the vram is mostly unrestricted, it's just a problem if you do something odd with the vram location where the display is currently pointing to, so you could get weird results on the screen at most. But that's it.
3) What is the +0x40000000 for?
This is to tell the PSP to use the memory as uncached memory, so whenever you write to or read from that location, the cache will be bypassed. This is for speed reasons, because otherwise reads/writes to vram will void the cache for the system ram and slow down execution of the code. (At last, that's how I got to know this address and its function)
but my graphics are still flashing.
This is because you don't swap the framebuffers after drawing, but still swap the location where you are drawing to. So the code is prepared for double buffering, but you're not using it actually.
At you initialization use
sceGuDrawBuffer(GU_PSM_8888, (void*)0, 512);
sceGuDispBuffer(480, 272, (void*)(FRAMESIZE), 512);
to setup for doublebuffering, then at the end of your inner loop, call sceGuSwapBuffers(). If the sceen then won't show correctly, you most likely will have to simply call sceGuSwapBuffers() once or just do a drawframe = !drawframe before your loop.

I hope this helps :)
Heretic
Posts: 33
Joined: Thu Feb 02, 2006 3:22 pm
Location: Pittsburgh, PA, USA
Contact:

Post by Heretic »

Thank you Raphael,
first off, that char* pointer thing... I feel really dumb... not enough sleep I guess.

about those 2 initialization functions, does the pointer location in each begin relative to the vram top (0x04000000)? So that 0 is 0x04000000 and FRAMESIZE is 0x04000000+0xAA000?

Again, thank you for the help
http://omegagames.netfirms.com -- Support struggling programmers by suffering through their crappy games! (Ok, you don't have to...)
User avatar
Raphael
Posts: 646
Joined: Tue Jan 17, 2006 4:54 pm
Location: Germany
Contact:

Post by Raphael »

Heretic wrote:Thank you Raphael,
first off, that char* pointer thing... I feel really dumb... not enough sleep I guess.

about those 2 initialization functions, does the pointer location in each begin relative to the vram top (0x04000000)? So that 0 is 0x04000000 and FRAMESIZE is 0x04000000+0xAA000?

Again, thank you for the help
No problem, it happens to the best coders to get confused with pointers sometimes :)

And yes, the two functions take relative pointers, since they only tell the gu where the display should start and where all drawing should be going to and that is always in vram :)
Heretic
Posts: 33
Joined: Thu Feb 02, 2006 3:22 pm
Location: Pittsburgh, PA, USA
Contact:

Post by Heretic »

Hmm I can't seem to get those functions to work properly... every time I use them my psp displays nothing and freezes. Without them though I can do pixel manips just fine by writing to the pixels starting at 0x04000000. Does anyone have any good examples of how to do pixel manipulation with the use of pspgu functions? I looked through the examples that come with the pspsdk, but they all use complex matrices and vertexes, which is all beyond my current scope... Until I can get that to work, is there any downside to doing it all without initializing the frames? (as everything appears to display correctly)
http://omegagames.netfirms.com -- Support struggling programmers by suffering through their crappy games! (Ok, you don't have to...)
Heretic
Posts: 33
Joined: Thu Feb 02, 2006 3:22 pm
Location: Pittsburgh, PA, USA
Contact:

Post by Heretic »

ok i've tried the pspgu functions, but i dont understand them at all... especially this (found in an example):
static unsigned int __attribute__((aligned(16))) list[262144];

which is passed to sceGuStart(GU_DIRECT,list);
http://omegagames.netfirms.com -- Support struggling programmers by suffering through their crappy games! (Ok, you don't have to...)
User avatar
Raphael
Posts: 646
Joined: Tue Jan 17, 2006 4:54 pm
Location: Germany
Contact:

Post by Raphael »

This is just the declaration of the display list buffer, that is then sent to the GU, so it knows where it can store its commands. This is because whenever you call something like guDrawArray, the vertices do not get drawn immediately, the command to draw it is stored in a list of commands, which is executed in several ways. Either by GU_DIRECT, GU_CALL or GU_SEND, which I won't explain here further. Just note that the most time you will want to use GU_DIRECT, because it tells the GU to start rendering while the list is being filled. When you finished 'drawing' everything, you probably should however also call a sceGuSync to make sure the GU finished getting everything on screen before you send swap buffers and start drawing other stuff.
cadaver
Posts: 21
Joined: Sun Aug 07, 2005 2:31 am

memalign

Post by cadaver »

I have a (maybe stupid) question:

Why are the adresses >>16<< byte aligned ? If the cache-line size is 64 byte, and some dirty cache lines get written, couldn't this overwrite some allocated memory (if it was allocated using an uncached pointer) ??
(or does it mean, never allocate 16 byte aligned memory with an uncached pointer?)
bradskins
Posts: 25
Joined: Tue Dec 20, 2005 5:54 pm

Post by bradskins »

Another simple question,

What is the uncached memory location of the second buffer? Or is there only a cashed memory location?

Then if there is only a cached location for the second buffer, how do you set up the vram so it does not miss the start/stop commands?
...
User avatar
Raphael
Posts: 646
Joined: Tue Jan 17, 2006 4:54 pm
Location: Germany
Contact:

Re: memalign

Post by Raphael »

cadaver wrote:I have a (maybe stupid) question:

Why are the adresses >>16<< byte aligned ? If the cache-line size is 64 byte, and some dirty cache lines get written, couldn't this overwrite some allocated memory (if it was allocated using an uncached pointer) ??
(or does it mean, never allocate 16 byte aligned memory with an uncached pointer?)
The simple reason is, that the PSP GU saves on the lower 4bit of every pointer address and thus can only access 16byte aligned memory locations correctly. Don't ask me of the exact reason though, I just suppose its because the GU only can read 32bit commands, of which the upper 8bit contain the command ID and the lower 24bit are for parameters. So only 24bit at most of one pointer are submitable in one command.

bradskins wrote: Another simple question,

What is the uncached memory location of the second buffer? Or is there only a cashed memory location?

Then if there is only a cached location for the second buffer, how do you set up the vram so it does not miss the start/stop commands?
You can turn ANY pointer to uncached pointer by ORing the address with 0x40000000. So the uncached address of the backbuffer would be
vramtop+FRAMESIZE+0x40000000
bradskins
Posts: 25
Joined: Tue Dec 20, 2005 5:54 pm

Post by bradskins »

could you tell me the exact value of vramtop?

if it is 0x04000000 dont bother.... but if it is something else please tell me!

sorry i am working in mips only, so all these c variables dont mean anything unles i know their exact value
...
Post Reply