GP2X Optimization Settings


slygamer

Active Member
Joined
Sep 19, 2005
Messages
795
Location
Brisbane, Australia
Website
Visit site
I'm using Rlyeh's Minimal SDK and I have the blitter working very well (based on paeryn and gfoot's work), but only if I do not specify any optimization level. Even if I use -O1, the app locks up on the first blit. I added a heap of trace statements through the blitter (logging out the file and line number to the serial port), and the extra time those trace statements took allowed the blits to work, but then it died waiting for the vertical blank.

I'm using the serialize trick as mentioned in previous threads to force the write to the blitter status register to be last
Code:
#define WAIT_BLITTER_IDLE   { do {} while (blitter->MESGSTATUS & MESGSTATUS_BUSY); }
#define SERIALIZE           __asm__ __volatile__ ("" : : :"memory")

...

  WAIT_BLITTER_IDLE;

  u16 *dest = &(((u16 *)gp2x_physvram[gp2x_physvram[7]])[screenX + (screenY * GP2X_SCREEN_WIDTH)]);

  blitter->MESGDSTCTRL = (mode & BLITMODE_MASKED ? MESGDSTCTRL_DSTENB : 0) | MESGDSTCTRL_DSTBPP_16 | FRAC16(screenX);
  blitter->MESGDSTADDR = (unsigned long)dest & (~3);
  blitter->MESGDSTSTRIDE = GP2X_SCREEN_WIDTH * GP2X_BYTESPERPIXEL;

  blitter->MESGSRCCTRL = MESGSRCCTRL_SRCBPP_16 | MESGSRCCTRL_SRCENB | MESGSRCCTRL_INVIDEO | FRAC16(rx);
  blitter->MESGSRCADDR = (unsigned long)gp2x_heap_getphys(src) & (~3);
  blitter->MESGSRCSTRIDE = image->width * GP2X_BYTESPERPIXEL;

  blitter->MESGPATCTRL = 0;

  blitter->MESGSIZE = (rh << MESGSIZE_HEIGHT) | (rw);
  blitter->MESGCTRL = MESGCTRL_FFCLR | MESGCTRL_YDIR_POS | MESGCTRL_XDIR_POS | SRCCOPY;

  SERIALIZE;

  // Writing to the status register triggers the blit
  blitter->MESGSTATUS = MESGSTATUS_BUSY;
but it doesn't seem to help.

Any ideas?
 
You don't list the declaration of your register pointers, but I'd guess that you didn't make them volatile. The compiler is then free to assume that memory contents don't change unless it changes them itself, which is bad news for hardware registers. There are also implications with writes - the SERIALIZE thing shouldn't be necessary if your blitter object is marked as volatile.

It's an interesting method, defining a blitter class covering the register memory. I'm not sure whether you need to make the whole object volatile:

Code:
    Blitter volatile *blitter;

or whether you need to specify every field within the object as volatile. Whatever works. :)

That said, my code locks up on some people's GP2Xs. I ought to try a non-optimised version I guess - it's hard when it's not your own GP2X that locks up though! My code locks up waiting for the blitter to be idle, before I've even written any registers. I wonder if it's necessary to write 0 to the register on startup or something to give it a kick.

Edit: One more thing - in cases like this it can be useful to look at the generated assembly. It'll be very different in optimised vs non-optimised builds, but with a passing knowledge of any assembly language, and the ARM quick-reference cards, it's not too hard to follow what's going on and see the problem.

On that note, you could also try single-stepping in gdb. I'm not a big fan of that though!
 
I tried specifying the blitter struct as volatile. I tried specifying every member of the struct as volatile. I even changed it all back to using a volatile array. Same result each time.

I'll try perusing the assembly, though I'm not sure if I'll understand enough to see what it is doing.

I don't know how to drive gdb either. I'm a child (metaphorically) of the IDE debugger generation. :)
 
sorry to off-topic the thread,

i'm seeing from diff threads that you guys 've done a nice progress with the blitter...
would slygamer/gfoot/paeryn/oscaruzzo like to help me with it?

for those interested, drop me a mail/PM...
i'd include your blitter code into my minimal library if you dont mind
that would save me some time meanwhile i finish other parts (like .PSD handling which im currently working with)

see ya, and sorry for the off-topic :)
 
No worries rlyeh. PM sent. I'll be keen to see v0.B that you mentioned a few weeks back.

gfoot, it seems to be quite finicky about when it works and when it doesn't. It would be nice to get this thing robust.
 
I'm a bit behind with my SDL driver, but as slygamer's offered already... May Cthulhu spare you eons of torment that is figuring out the blitter :)

I think the SERIALIZE is still needed as even if the registers are marked volatile you're only writing to them, gcc is still free to re-order the final MESGSTATUS earlier. volatile helps when reading (like waiting for blitter busy to clear) as it tells gcc that it has to re-read the memory each time.
Try putting a SERIALIZE after WAIT_BLITTER_IDLE just incase gcc is trying to move the loop further down.
 
Hello,
in my opinion you already received the correct tip (declaring with "volatile" keyword).
However, you still have two alternative solutions:
1) read the register with a callback function.
For example:

#define WAIT_BLITTER_IDLE { while (read_MESGSTATUS(blitter) & MESGSTATUS_BUSY); }

int read_MESGSTATUS(Blitter *blitter)
{
return blitter->MESGSTATUS;
}

Keep in mind that the GCC compiler will surely expand it as an inline function if you use an optimization level higher than 2, unless you have declared the prototype with the right __attribute__ modifier.

2) try to use volatile keyword directly:
#define WAIT_BLITTER_IDLE { while ( *((volatile *)(&(blitter->MESGSTATUS))) & MESGSTATUS_BUSY); }

Technically, since you don't report other closed loops beside this one, I think the solution will be quite easy...

Sincerely,

LDChen.
 
Thanks for the tips. I'll give these a try tonight.

rlyeh, I'd been looking at the front page of retrodev.info for an announcement. I hadn't looked in the forum. D'oh!
 
Found the problem! :)

It wasn't in the blitter code at all. rlyeh's SDK (v0.A) does not declare gp2x_memregs as volatile, so gp2x_video_wait_vsync() was getting optimized incorrectly and waiting forever. Adding the volatile declaration to minimal.h and minimal.c did the trick.

Excellent! Now I can continue working. :)
 
Now I have the blitter working with FIFO, so my source data can exist in either video memory or system memory. Another option is to blit algorithmically created sprites by using the CPU to create the source data and send it to the FIFO.

Mind you, using the hardware FIFO to blit sprites is not much faster than using a software blitter because you still have to use the CPU to feed the source data to the blitter.

Just thought I'd mention it. Getting anything to work from the docs that we have is a win by itself. :)
 
The FIFO could possibly be useful to blit graphics that are dynamically generated by the program, avoiding a copy to the high memory each time, unless memory copying is faster than using the FIFO.
 
Writing to the FIFO won't be much faster than to uncached memory since hw registers can't be cached, though they are on a separate bus I think. The only real benifit will be from having a simplified loop as the hardware takes care of correctly incrementing the destination address. I suppose the blitter could use burst-mode for writing which would give it a slight speed-up.
I suspect the hardware implementation has two loops, the src loop loading data onto the FIFO and the dst loop taking it off.
You could probably set a DMA channel to load the FIFO too, don't know how much use that'd be though
 
Squidge's docs say that the blitter will often read the FIFO faster than the CPU can write to it. If the CPU does manage to fill the FIFO, the blitter will stall the CPU until some data has been read out of the FIFO.

I now have three blitters: video memory to video memory (fastest), FIFO (slow) and software (slowest but can do alpha blending). My code chooses the correct blitter to use based on where the source data resides and what type of blit the user wants to do.
 
Back
Top