Garbled graphics

SGDK only sub forum

Moderator: Stef

Post Reply
tryphon
Very interested
Posts: 316
Joined: Sat Aug 17, 2013 9:38 pm
Location: France

Garbled graphics

Post by tryphon »

Hi,

developping my Shinobi port, I have sometimes garbled frames. I must do something wrong.

Here's some example (taken from Gens, but it happens too with Fusion, I didn't try yet with real hardware) :
Image

The next frame, for comparison :
Image

Here is how the main loop works :

1) read joypad entry
2) disable ints
3) check collisions
4) update characters state
5) compute scroll value
6) compute screen position of all sprites
7) update all sprites
8) enable ints
9) wait for vblank.

The update all sprites things works as follows :
* each sprite attributes is updated using VDP_setSprite (so using SGDK sprite cache)
* if a new frame must be displayed, the ROM locations of patterns data is stored in a table.

Then, during vint, the following is done :
* if the pattern data table has entries, they are DMA-copied into VRAM (I've tried disabling the DMA with same results)
* then VDP_updateSprite() is called to update the SAT using SGDK sprite cache

Since I disable/enable ints in main loop while setting patterns data table and sprite cache, and the VRAM transfers is done all at once during Vint, I'd have expected maybe frame drops (if steps 3 to 6 takes more than 1/60th second) but not garbled graphics.

Maybe the cause is that the DMA-copy from ROM to VRAM takes too much time, but there are around 40 (max 96, but it doesn't actually happen) patterns to update in the frame. From the pictures though, it's quite clear the SAT is written before the patterns were shipped to VRAM.

Or my logic is wrong.
Stef
Very interested
Posts: 3131
Joined: Thu Nov 30, 2006 9:46 pm
Location: France - Sevres
Contact:

Re: Garbled graphics

Post by Stef »

I think that happen because the SPR_update() call is interrupted by vertical interruption callback, try to protect this call from interruption by calling SYS_disableInts() before and SYS_enableInts() right after.
I'm doing major modifications to the sprite engine, next version of SGDK will fix that issue and the sprite engine should be faster :)

Edit: oh sorry i read too quickly, i was pretty sure you were using the included sprite engine in SGDK as i sometime experience the same issue with it :p

Indeed you have something wrong with your timing, sprite table is updated before the pattern are there (or the opposite).
You should log (using Gens KMod message for instance) the scanline where your V-Int job starts and ends, i think you have things going over VBlank area...

Haha, you already know how excited i'm about this Shinobi port :p I will definitely encourage you in your efforts ;)
tryphon
Very interested
Posts: 316
Joined: Sat Aug 17, 2013 9:38 pm
Location: France

Re: Garbled graphics

Post by tryphon »

Stef wrote:Indeed you have something wrong with your timing, sprite table is updated before the pattern are there (or the opposite).
You should log (using Gens KMod message for instance) the scanline where your V-Int job starts and ends, i think you have things going over VBlank area...
I tried but it's hard to log the precise frame it happens (it's quite rare). For the screenshots posted above, I used gens version by r57shell, which has a very practical slow mode. I didn't find the equivalent in gens-kmod. Have I missed something ?

I checked the vint_callback routine (I used SGDK 1.00, which is quite old now) :

Code: Select all

// V-Int Callback
void _vint_callback()
{
    intTrace |= IN_VINT;

    vtimer++;

    // tile cache processing
    if (VIntProcess & PROCESS_TILECACHE_TASK)
    {
        if (!TC_doVBlankProcess()) VIntProcess &= ~PROCESS_TILECACHE_TASK;
    }
    // tile cache processing
    if (VIntProcess & PROCESS_SPRITEENGINE_TASK)
    {
        if (!SPR_doVBlankProcess()) VIntProcess &= ~PROCESS_SPRITEENGINE_TASK;
    }
    // bitmap processing
    if (VIntProcess & PROCESS_BITMAP_TASK)
    {
        if (!BMP_doVBlankProcess()) VIntProcess &= ~PROCESS_BITMAP_TASK;
    }
    // palette fading processing
    if (VIntProcess & PROCESS_PALETTE_FADING)
    {
        if (!VDP_doStepFading(FALSE)) VIntProcess &= ~PROCESS_PALETTE_FADING;
    }

    // ...

    // then call user's callback
    if (VIntCB) VIntCB();

    // joy state refresh (better to do it after user's callback as it can eat some time)
    JOY_update();

    intTrace &= ~IN_VINT;
}
My own callback routine is called at the end, would it be possible that the previous code updates the SAT ? I see stuff like SPR_doVBlankProcess() and likes, executed according to the state of VIntProcess, but I never set it.
Haha, you already know how excited i'm about this Shinobi port :p I will definitely encourage you in your efforts ;)
Thank you. I progressed slowly recently because I was on another project (the english translations of Phantasy Star Generations 1 & 2 on Playstation 2, which are now almost finished), then I spent time to code a sprite ressource editor. But the project is alive and grows smoothly. You can throw shurikens and slice your ennemies (okay, there are only 2 of them), and the feelings are quite similar to the arcade game :)
Stef
Very interested
Posts: 3131
Joined: Thu Nov 30, 2006 9:46 pm
Location: France - Sevres
Contact:

Re: Garbled graphics

Post by Stef »

tryphon wrote: I tried but it's hard to log the precise frame it happens (it's quite rare). For the screenshots posted above, I used gens version by r57shell, which has a very practical slow mode. I didn't find the equivalent in gens-kmod. Have I missed something ?
You have frame per frame execution on Gens KMod, not exactly the same thing but very useful as well :)
I checked the vint_callback routine (I used SGDK 1.00, which is quite old now) :

Code: Select all

// V-Int Callback
void _vint_callback()
{
    intTrace |= IN_VINT;

    vtimer++;

    ...

    // then call user's callback
    if (VIntCB) VIntCB();

    // joy state refresh (better to do it after user's callback as it can eat some time)
    JOY_update();

    intTrace &= ~IN_VINT;
}
My own callback routine is called at the end, would it be possible that the previous code updates the SAT ? I see stuff like SPR_doVBlankProcess() and likes, executed according to the state of VIntProcess, but I never set it.
If you don't use the sprite engine (i mean by that any of the SPR_xxx methods) then no, SPR_doVBlankProcess() should not be called and so can't screw up your sprite data. that should be something else... And since you are almost protecting all your processes from interrupt i don't see how it can mess up things... the only possible solution is that your tiles DMA transfer take too much time (continue during next frame) and your sprite table is updated only for next next frame.
Again, try to just log V counter in your VInt code, that may help in understand what happen. Normally you should enter in your V-Int at scanline ~225/226 (given the overhead of the SGDK V-Int handler), you can also log the V counter when your DMA operation are done.
You can use the KDebug_xxx log message so opening the message windows in Gens KMod will show you the log, very convenient for debugging :)
Thank you. I progressed slowly recently because I was on another project (the english translations of Phantasy Star Generations 1 & 2 on Playstation 2, which are now almost finished), then I spent time to code a sprite ressource editor. But the project is alive and grows smoothly. You can throw shurikens and slice your ennemies (okay, there are only 2 of them), and the feelings are quite similar to the arcade game :)
That's really good news, i'm really looking forward your progresses =)
I really wish we had a good Shinobi arcade port back in time and maybe you will finally do it :) Keep up the great work !
tryphon
Very interested
Posts: 316
Joined: Sat Aug 17, 2013 9:38 pm
Location: France

Re: Garbled graphics

Post by tryphon »

Ok, that's what I did (frame per frame execution was what I needed, thanks :) )

Here are the results :

Code: Select all

Message : VINT_CALLBACK
Message : 000000E2 <---------------------------------------------------------- VCOUNTER at the beginning of DMAs
Message : 0001AED6
Spy:@ (140,230) 68k (@ 0x9966) -> VRAM of 32 words from 01AED6 to 4000 (VRAM access)
Message : 0001AF16
Spy:@ (106,238) 68k (@ 0x9966) -> VRAM of 192 words from 01AF16 to 4040 (VRAM access)
Message : 0001B096
Spy:@ (244,248) 68k (@ 0x9966) -> VRAM of 32 words from 01B096 to 41C0 (VRAM access)
Message : 0001B0D6
Spy:@ (160,255) 68k (@ 0x9966) -> VRAM of 32 words from 01B0D6 to 4200 (VRAM access)
Message : 0001B116
Spy:@ (77,7) 68k (@ 0x9966) -> VRAM of 16 words from 01B116 to 4240 (VRAM access)
Message : update_sprites
Message : 00000013 <---------------------------------------------------------- VCOUNTER at the SAT update
Message : end of callback
Message : 0000001A<---------------------------------------------------------- VCOUNTER at the end of VINT code
So it seems to confirm that the DMA takes too long.

I could put the DMAs in the main loop again, but it'll be slower (but maybe I have more time in the main loop). I guess it's worth a try. If you have ideas, I'm willing to learn :)
Stef
Very interested
Posts: 3131
Joined: Thu Nov 30, 2006 9:46 pm
Location: France - Sevres
Contact:

Re: Garbled graphics

Post by Stef »

I guess here what is slow is the multiple calls to the DMA transfer method, next SGDK version will add a new DMA queue system to offer much faster multiple DMA operations to maximize the VBlank bandwidth, you can get it from github repository if you want to give a try :
https://github.com/Stephane-D/SGDK/blob ... /src/dma.c
https://github.com/Stephane-D/SGDK/blob ... /inc/dma.h

The idea is to queue DMA operations then call DMA_flush() in the VInt callback ;)

Good to see the Gens KMod Spy already give you useful informations about the DMA operations you're doing (HV counter position and DMA length operation). Still the SAT update happen at the beginning of the frame so that should not really matter here, it would if the SAT is transfered after line where sprite are visible.
tryphon
Very interested
Posts: 316
Joined: Sat Aug 17, 2013 9:38 pm
Location: France

Re: Garbled graphics

Post by tryphon »

I was stupid : data was organized in ROM so that they were contiguous, so many successive DMA calls could be grouped. What I did, it fixes the thing...

... for the moment. There are only 3 sprites on screen, I'd like to go to at least 5, ideal would be 8 (original game can go much higher, but slows down).

Now I'll code something to generate easily the state machine code. And sleep before :mrgreen:

Thanks for your help :)
Stef
Very interested
Posts: 3131
Joined: Thu Nov 30, 2006 9:46 pm
Location: France - Sevres
Contact:

Re: Garbled graphics

Post by Stef »

You're welcome :D
tryphon
Very interested
Posts: 316
Joined: Sat Aug 17, 2013 9:38 pm
Location: France

Re: Garbled graphics

Post by tryphon »

You won't ever be thanked enough :)

That said, I have one question :
Stef wrote:the only possible solution is that your tiles DMA transfer take too much time (continue during next frame) and your sprite table is updated only for next next frame.
It turned out to be the problem, but so, DMA transfer don't halt the CPU (in one sense, it's great). Is there a way to tell the CPU to do nothing until DMA has ended ?
Stef
Very interested
Posts: 3131
Joined: Thu Nov 30, 2006 9:46 pm
Location: France - Sevres
Contact:

Re: Garbled graphics

Post by Stef »

It turned out to be the problem, but so, DMA transfer don't halt the CPU (in one sense, it's great). Is there a way to tell the CPU to do nothing until DMA has ended ?
Of course they does, what make you think the opposite O_o ?
tryphon
Very interested
Posts: 316
Joined: Sat Aug 17, 2013 9:38 pm
Location: France

Re: Garbled graphics

Post by tryphon »

Stef wrote:what make you think the opposite O_o ?
My misunderstanding of your statement :)

What you said is : DMA for frame 2 occurs but is too long, so SAT still contains frame 1 data, displaying some part of frame 2 with these, so it appears broken.

What I understood was : DMA for frame 2 occurs but CPU isn't blocked. SAT data for frame 2 is loaded while frame 2 patterns are not completely loaded, so it appears broken.

But you're right : with my version, a broken frame 2 would appear, whereas screenshots shows it's in fact a broken frame 1.

So my question becomes : is there a way to freeze the display (i.e. not updating it) while DMA isn't over ?
Stef
Very interested
Posts: 3131
Joined: Thu Nov 30, 2006 9:46 pm
Location: France - Sevres
Contact:

Re: Garbled graphics

Post by Stef »

No way of doing that, normally the solution is to keep DMA and VDP updates inside VBlank area or use sort of double buffering but that would be complicated and waste VRAM :-/
Post Reply