GP32 Vga Buffer To Gp32


Franxis

MAME 4 ALL
Joined
Aug 22, 2004
Messages
788
Age
49
Location
Spain
Website
franxis.zxq.net
Hi everyone,

I'm investigating ways to improve the speed of my MAME port for GP32.

One critical part is the video output function. It has to extract a part of a big VGA style video buffer and output it to a GP32 video buffer (320x240).

I have seen xlatgp32 that is done in ARM ASM but it is not useful for MAME because this function needs the origin VGA video buffer to be in 320x240 resolution. In MAME the origin video buffer has bigger resolution. Also another problem is that i need the posibility of rotating the image.

In example the not rotated version of my function is now something like this:

while (y < gp32_height) {
buffer_mem_line=buffer_mem;
buffer_mem+=buffer_mem_offset;
x=0;
px=pxi;
while (x < gp32_width) {
buffer_scr[px+py] = *buffer_mem_line++;
x++;
px+=240;
}
y++;
py--;
}

Any help would be appreciated.

Regards.
 
copying 2 words (2 pixels in 16bit mode) or 4 bytes (4 pixels/8bits) would be much better, isn't it ?
read all data from src in one "u32" variable and then copy variable to screen buffer.
I think it can be a first optim.
(you have to swap loops y and x)
 
Yeah, you can optimize that a lot. Doing an array lookup with addition every pixel is much slower than doing pointer arithmetic for instance. Since the GP32's ARM has a cache, you don't need to unroll the for-loop (that'd slow it down), but doing something like this..

while ( x < y ) *p++ = *z++;

Thats pretty damned fast even in C. But if you do p [ a + b ] = z [ c + d ] you're getting killed, since you havew to do math and array lookup for each pixel. Always get 'things out of the way before the loops if you can.

As he said above, if the size of the thing is divisible by 4 and on an even boundary of 4, you can use unsigned int instead of unsigned char and get a speedup. Lots of tricks.

I forget for MAME, but some emus use macros to write into the intermediate video buffer, so if this early MAME does as well you could alter the macros to wraw into the buffer in pre-rotated form etc, which is a big speedup.

jeff
 
actually, skeezix, unrolling the loop speeds things up quite a bit. The first pass through the loop will cause a couple of cache misses, but the cache is large enough to hold the code and then some. So after the first pass everything will go smoothly.

You are probably thinking of pentium architecture :) The ARM9 has seperate cache for code and data fetches.
 
Oh and btw, the code you posted is generating a cache miss on every line. A better course of action would be to follow the lineair order of the buffer, and calc for each pixel in the buffer the correct offset in the gp32 framebuffer, instead of doing it the other way around. That way you'll minimize the cache misses because the reads, instead of the writes are consecutive.

If you write the code in assembler, you can read four bytes and store four bytes seperately. Unroll the inner loop for extra speed. Note that writes go into the writeback buffer so they take one cycle each, if properly padded with other non-memory access instructions.

Have fun :)
 
Yup, by unrolling the loop it means it doesn't have to dump the prefetch every time around the loop, so you can gain a little there plus the cycles that would normally be used by the branch instructions themselves :)
 
This is my function now, thank you for your suggestions... It is quite faster now. Any other suggestion ???

Code:
/* Update GP32 Screen */
static __inline void update_screen(void)
{
	unsigned char *buffer_scr = gpDraw[nflip].o_buffer;
	unsigned char *buffer_mem = scrbitmap->line[gp32_yoffset]+gp32_xoffset;
	unsigned int *buffer_mem_line;
	int buffer_mem_offset = (unsigned int)(scrbitmap->line[1] - scrbitmap->line[0]);
	int x,y=0,px,pxi,py;

	if (!gp32_rotate) {
  pxi=gp32_xscreen*240;
  py=239-gp32_yscreen;
  while (y < gp32_height) {
  	buffer_mem_line=(unsigned int *)buffer_mem;
  	buffer_mem+=buffer_mem_offset;
  	x=0;
  	px=pxi;
  	while (x < gp32_width) {

    *(buffer_scr+px+py) = (unsigned char)*buffer_mem_line;	
    px+=240;

    *(buffer_scr+px+py) = (unsigned char)((*buffer_mem_line)>>8);	
    px+=240;

    *(buffer_scr+px+py) = (unsigned char)((*buffer_mem_line)>>16);	
    px+=240;

    *(buffer_scr+px+py) = (unsigned char)((*buffer_mem_line)>>24);	
    px+=240;

    x+=4;
    buffer_mem_line+=1;
  	}
  	y++;
  	py--;
  }
  
	} else {
  pxi=239-gp32_xscreen;
  py=(319-gp32_yscreen)*240;
  while (y < gp32_height) {
  	buffer_mem_line=(unsigned int *)buffer_mem;
  	buffer_mem+=buffer_mem_offset;
  	x=0;
  	px=pxi;
  	while (x < gp32_width) {

    *(buffer_scr+px+py) = (unsigned char)*buffer_mem_line;	
    px--;

    *(buffer_scr+px+py) = (unsigned char)((*buffer_mem_line)>>8);	
    px--;

    *(buffer_scr+px+py) = (unsigned char)((*buffer_mem_line)>>16);	
    px--;

    *(buffer_scr+px+py) = (unsigned char)((*buffer_mem_line)>>24);	
    px--;

    x+=4;
    buffer_mem_line+=1;
  	}
  	y++;
  	py-=240;
  	
  }
	}

}
 
Squidge posted on May 10 2005 at 10:26 PM said:
Yup, by unrolling the loop it means it doesn't have to dump the prefetch every time around the loop, so you can gain a little there plus the cycles that would normally be used by the branch instructions themselves :)

I'm not able to unroll the loops because the screen resolution for each of the games are different.

Also i'm not able to access in a linear way the vga buffer completely because i have to access only a certain window in the buffer.

Regards.
 
Last edited by a moderator:
Franxis posted on May 11 2005 at 02:04 AM said:
Squidge posted on May 10 2005 at 10:26 PM said:
Yup, by unrolling the loop it means it doesn't have to dump the prefetch every time around the loop, so you can gain a little there plus the cycles that would normally be used by the branch instructions themselves :)

I'm not able to unroll the loops because the screen resolution for each of the games are different.

Also i'm not able to access in a linear way the vga buffer completely because i have to access only a certain window in the buffer.

Regards.

Are there so many diffrent game-resolutions? If there are not too much, you can write several functions and use function pointers. ( very good site for that: http://www.newty.de/fpt/index.html)

But I will "recode" your function given above in asm. Maybe we can do a little speed test :) For example using the "move multiple instruction" springs to my mind. I send you an email or post here if I finish a asm-copy route.
Ah, and maybe locking the cachelines containing the copy route could bring some benefit. INOPIA I know that you have tried it with not much gaining in speed, but maybe it will do in this particular case here.

bye,
creature
 
Last edited by a moderator:
Creature XL posted on May 11 2005 at 12:12 PM said:
Are there so many diffrent game-resolutions? If there are not too much, you can write several functions and use function pointers. ( very good site for that: http://www.newty.de/fpt/index.html)

But I will "recode" your function given above in asm. Maybe we can do a little speed test :) For example using the "move multiple instruction" springs to my mind. I send you an email or post here if I finish a asm-copy route.
Ah, and maybe locking the cachelines containing the copy route could bring some benefit. INOPIA I know that you have tried it with not much gaining in speed, but maybe it will do in this particular case here.

bye,
creature

THANK YOUUUUUUUUUUUU ! I WILL WAIT FOR IT !
 
Last edited by a moderator:
Creature XL posted on May 11 2005 at 12:12 PM said:
Franxis posted on May 11 2005 at 02:04 AM said:
Squidge posted on May 10 2005 at 10:26 PM said:
Yup, by unrolling the loop it means it doesn't have to dump the prefetch every time around the loop, so you can gain a little there plus the cycles that would normally be used by the branch instructions themselves :)

I'm not able to unroll the loops because the screen resolution for each of the games are different.

Also i'm not able to access in a linear way the vga buffer completely because i have to access only a certain window in the buffer.

Regards.

Are there so many diffrent game-resolutions? If there are not too much, you can write several functions and use function pointers. ( very good site for that: http://www.newty.de/fpt/index.html)

But I will "recode" your function given above in asm. Maybe we can do a little speed test :) For example using the "move multiple instruction" springs to my mind. I send you an email or post here if I finish a asm-copy route.
Ah, and maybe locking the cachelines containing the copy route could bring some benefit. INOPIA I know that you have tried it with not much gaining in speed, but maybe it will do in this particular case here.

bye,
creature


Yes there are TONS of different game resolutions. They are all over the place, it honestly seems like no two are exactly alike. They may be different by only a few scanlines here and there. I have tested all of my ROMs to make sure they worked and noticed the variation in resolutions. You can tell by how much of the screen is filled by each game. Then there are games that are portrait (Pac-Man) and those that are landscape (missile command) and means the Vblank may or may not match the GP32's as the GP32 scans vertical.
 
Last edited by a moderator:
It doesn't really matter that you have different game resolutions. Just unroll the innerloop n times, where n is the size of the largest scanline. If you need to do a loop of m, with m<n, you can simply skip the first n-m passes. For example:

@ r0 holds the number of times we need to go through the innerloop

rsb r0,r0,320
mul r0,r0,#[size_of_innerloop_in_bytes]
add r15,r15,r0

.rept 320
@ innerloop here
.endr

I'm doing this from the top of my head, and it's been over a year since I coded ARM assembler so I might have made a couple of mistakes, but I hope you get the point.

Also, locking the cachelines doesn't really give you any speedup afaik. And yes, I did test that :)

I did a rotozoomer once, with a 256x256 texture (for the record, that's a texture of 65536 bytes, so it doesn't fit in datacache in one piece). It was clear to see that at certain angles the framerate was great, real nice and smooth, and at other angles it was *really* poor, dropping to about 1-2 fps. Simply because the texture was rotated 90 degrees in relation to the lfb. This situation generated a cache miss on every single texture read. Therefore it's really important you make sure READS ARE AS SEQUENTIAL AS POSSIBLE.

have fun
 
Back
Top