gbadev.org forum archive

This is a read-only mirror of the content originally found on forum.gbadev.org (now offline), salvaged from Wayback machine copies. A new forum can be found here.

DS development > Doing stuff in vblank without flicker

#124988 - simonjhall - Tue Apr 10, 2007 6:50 pm

Yo,
If I spend ages doing work in the vblank handler, does this stall the update of the actual LCD?
At the moment if I upload textures during the vblank (and change banks from texture mode to LCD mode and back again) there's a noticeable flicker for a fraction of a second. I was under the impression that I'm allowed to do whatever I like, for as long as I whilst in a vblank handler!

Should I do it some other way instead? For instance in the hblank, once vcount goes into the 'dead' bit of the screen which can't be seen?

Ta guys :-)
_________________
Big thanks to everyone who donated for Quake2

#124991 - Miked0801 - Tue Apr 10, 2007 7:37 pm

There is only so many cycles in the VBlank. You do not get "as long as you like" to do what you wish. The flicker no doubt is caused because you are running a fat vblank. Could very well be the graphic units not updating due to LCDC mde outside of the VBlank period.

#125000 - simonjhall - Tue Apr 10, 2007 8:50 pm

So how long do you reckon I get? 1ms? Less?
Maybe it would be better to do it in that hblank dead time after all...
_________________
Big thanks to everyone who donated for Quake2

#125001 - relpats_eht - Tue Apr 10, 2007 8:52 pm

There is even less time in hblank than there is in vblank, much less, in fact. You are better off with some sort of double buffering.
_________________
- relpats_eht

#125002 - simonjhall - Tue Apr 10, 2007 9:07 pm

I mean like checking in the hblank interrupt to see if VCOUNT has gone into the part of the screen that isn't visible. As soon as this happens, do some stuff...
_________________
Big thanks to everyone who donated for Quake2

#125008 - sajiimori - Tue Apr 10, 2007 9:34 pm

When loading a lot of textures, you may need to split up the work across multiple vblanks. It helps to have a VRAM transfer manager that you can register new transfer tasks with. Then it can decide when the transfer should be done.

When more transfers are queued than can be transferred in a single vblank, you have to decide whether to block until the next vblank (thereby dropping a frame globally), or continue the game with some transfers left incomplete.

If you allow the game to continue without doing all transfers (in the interest of smooth gameplay), you'll probably want to hide objects that don't have their textures loaded yet.

#125011 - relpats_eht - Tue Apr 10, 2007 9:40 pm

simonjhall wrote:
I mean like checking in the hblank interrupt to see if VCOUNT has gone into the part of the screen that isn't visible. As soon as this happens, do some stuff...


Yes, I know. I was saying that hblank is a much shorter period of time than vblank, therefore, what you cannot do in a single vblank, you cannot do in a single hblank either. So long as you are writing a manager of some sort to split up loading, which you would be required to do if you chose to use the hblank to load, you may as well continue to use vblank and load more each blank.

Or, you could go the easier route of double buffering.
_________________
- relpats_eht

#125015 - masscat - Tue Apr 10, 2007 10:10 pm

I am not sure when the texture memory has to be assigned for the NDS hardware to correctly use it. It will be either when the vertices are being passed and processed by the geometry unit or when you swap buffers (or maybe both).

To quote gbatek:
Quote:
4000540h - Cmd 50h - SWAP_BUFFERS - Swap Rendering Engine Buffer (W)
SwapBuffers exchanges the two sets of Polygon/Vertex RAM buffers, that is, the newly defined polygons/vertices are passed to the rendering engine (and will be displayed in following frame(s)). The other buffer is emptied, and passed to the Geometry Engine (to be filled with new polygons/vertices by Geometry Commands).

0 Translucent polygon Y-sorting (0=Auto-sort, 1=Manual-sort)
1 Depth Buffering (0=With Z-value, 1=With W-value)
(mode 1 does not function properly with orthogonal projections)
2-31 Not used

SwapBuffers isn't executed until next VBlank (Scanline 192) (the Geometry Engine is halted for that duration). SwapBuffers should not be issued within Begin/End.


If it is during the buffer swap then changing texture memory during VBlank is not good as this would be when the hardware is using them.

#125022 - DekuTree64 - Tue Apr 10, 2007 11:05 pm

Another gbatek quote:
Quote:
4000320h - RDLINES_COUNT - Rendered Line Count Register (R)
Rendering starts in scanline 214, the rendered lines are stored in a buffer that can hold up to 48 scanlines. The actual screen output begins after scanline 262, the lines are then read from the buffer and sent to the display. Simultaneously, the rendering engine keeps writing new lines to the buffer (ideally at the same speed than display output, so the buffer would always contain 48 pre-calculated lines).
0-5 Minimum Number (minus 2) of buffered lines in previous frame (0..46)
6-31 Not used


If rendering becomes slower than the display output, then the number of buffered lines decreases. Smaller values in RDLINES indicate that additional load to the rendering engine may cause buffer underflows in further frames, if so, the program should reduce the number of polygons or lights to avoid display glitches.
Even if RDLINES becomes zero, it doesn't indicate if buffer underflows have occured; instead, underflows are indicated in DISP3DCNT Bit12.

So you have from scanline 192 to 214 to mess with texture VRAM (about 1.4ms). The textures are continually read throughout the rendering of the frame, so if you ever take them away from the 3D engine, they all show up as black for any lines rendered during that time.

I agree with sajiimori on hiding any objects that haven't finished loading yet. Since they'll most likely be finished within one or two VBlanks anyway, it shouldn't hurt gameplay to let them function as if they were visible during that time, even if that means attacking the player.
_________________
___________
The best optimization is to do nothing at all.
Therefore a fully optimized program doesn't exist.
-Deku

#125030 - tepples - Wed Apr 11, 2007 12:08 am

DekuTree64 wrote:
I agree with sajiimori on hiding any objects that haven't finished loading yet. Since they'll most likely be finished within one or two VBlanks anyway, it shouldn't hurt gameplay to let them function as if they were visible during that time, even if that means attacking the player.

If a texture hasn't loaded, wouldn't it be better to just draw the object with Gouraud shading than to make the mesh flicker?

But how fast is a DMA copy to texture memory anyway?
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.

#125034 - sajiimori - Wed Apr 11, 2007 12:15 am

It depends, but I'd lean toward not drawing them. I can't imagine why there would be flicker; if the texture is loaded one tick, it should still be loaded for the next tick.

If a character spawns with a magical-looking special effect around them, delaying their appearance might look better than having them appear untextured for a moment.

If they spawn due to coming within range of distance clipping, maybe delaying their appearance for a couple ticks won't be noticable; you might get a little closer in the meantime, but they'll still be far away.

#125038 - HyperHacker - Wed Apr 11, 2007 12:18 am

I know this doesn't really apply to Quake but you could always try to work these things into your game. Make gourad shading part of a special effect.
_________________
I'm a PSP hacker now, but I still <3 DS.

#125044 - DekuTree64 - Wed Apr 11, 2007 12:34 am

tepples wrote:
But how fast is a DMA copy to texture memory anyway?

I don't remember the exact speed offhand, I think it was something like 16KB you can get down from main RAM.

VRAM is quite a bit faster than main RAM though, so if you have one of the 16KB banks free, you could load the texture from storage into that, and then DMA from there during VBlank.

The 16KB shared memory blocks are even faster, plus then maybe you could have the ARM7 help out :)
I wonder if writing to two seperate VRAM banks at the same time causes a stall...
_________________
___________
The best optimization is to do nothing at all.
Therefore a fully optimized program doesn't exist.
-Deku

#125094 - simonjhall - Wed Apr 11, 2007 9:34 am

Ta guys, thanks for the help. At the moment my texture manager queues up textures to load, and then loads them during the slack time. I was planning on spreading the actual transfers across several frames, but I just wanted a rough idea of when I should start doing the transfers and how long I've got to do them...
So I'm gonna have a go with the VCOUNT > 192 but < 214 thing, and see how that plays out.

I do like the idea of transferring stuff into one of the unused/useless vram banks and then moving them to the main texture memory when it's allowed to. May as well do something with those extra banks! To make matters worse, the textures are not being moved from main memory to vram - they're being pulled from *disk* on demand. This may sound a bit stupid, but it works well on all my flash cards but I bet there's one person out there who's using a flash card that only gives 1k/s... ;-)

For the extended-RAM build I intend to pull textures and sounds from that after their initial load.

The 'what to do when the texture hasn't been loaded' thing is pretty interesting - maybe I should add an option to select what you want to do. At the moment when the texture isn't available I disable texturing on that object, which means it flashes white for one frame. It's quite distracting, esp as the Quake world is quite dark. This is only really an issue for the view weapons though as they're right in front of you and changing weapons takes zero frames to do. It's not a problem for other monsters as the game engine normally renders them before they're actually visible, so you don't see them flashing white. Maybe I should just not render the objects when they don't have textures.

One final annoying thing about this deferred loading thing is sprite animations, eg explosions. By the time the required frame of animation has been loaded, the sprite has moved to the next frame! Don't really know what to do about this...

EDIT: oh and I didn't think the ARM7 could write to VRAM? I know you can write to C and D when they's assigned as work RAM - how about the others?
_________________
Big thanks to everyone who donated for Quake2

#125102 - silent_code - Wed Apr 11, 2007 12:00 pm

make sure you use a priority queue and some fast path for "immediate" priority. that would be like: low = far away alias & world textures, med = close alias and world, high = whatever, immediate = effects & view models.
immediate would stop any other transfers, any others would be worked through from high to low. as stuff gets closer to the player it get a higher priority. i know it's not a very very very good solution, but it's better than just a fifo. it still has some problems though (textures never get loaded under certain circumstances, even though they should have etc.).

i wish you good luck!

#125104 - masscat - Wed Apr 11, 2007 12:31 pm

Since the rendered lines are buffered will you not actually have more time than scan lines 192 to 214?

You could have up to (192 - 48) to 214 if the render buffer is fully populated at scan line 144, i.e. all the lines for the remaining display have been rendered.

This would only work if the rendering engine always starts rendering line 0 at scan line 214 rather than continuing to render from where it got up to (assuming there is no SWAP_BUFFERS write).

EDIT: misread what RDLINE_COUNT was, since it is the minimum for the previous frame you would risk black textures using it for the current frame.

#125106 - simonjhall - Wed Apr 11, 2007 1:55 pm

Yeah, maybe I'll try unlocking it at various different places for different lengths of time to see if it breaks or not... Ah, homebrew development :-)

And I did think of some kind of priority system, but not for prioritising the loading of certain textures. More like trying to decide if/when a texture should be discarded...
_________________
Big thanks to everyone who donated for Quake2

#125136 - sajiimori - Wed Apr 11, 2007 6:07 pm

For sprites, I might suggest having them lag by 1 frame overall.

#125144 - DekuTree64 - Wed Apr 11, 2007 7:18 pm

simonjhall wrote:
This is only really an issue for the view weapons though as they're right in front of you and changing weapons takes zero frames to do.

I like silent_code's idea of a priority queue, but if this is the only thing that's causing trouble, you could just make a special transfer task for the gun, and always handle it first. That way you can just render it and assume the texture will make it down in the next VBlank.

Quote:
One final annoying thing about this deferred loading thing is sprite animations, eg explosions. By the time the required frame of animation has been loaded, the sprite has moved to the next frame! Don't really know what to do about this...

How big are these? Any chance you could load all the frames at once? Using the hardware's compressed texture format could help reduce bandwidth/VRAM usage.
Another option would be to make sure that a frame is displayed for at least one render cycle after it finishes loading, and not request a new frame to load until the previous one has finished. Animation might be a bit choppy, but might not look too bad.

Quote:
EDIT: oh and I didn't think the ARM7 could write to VRAM? I know you can write to C and D when they's assigned as work RAM - how about the others?

Yeah, only C and D. Any method of splitting the load would probably be way over-complicated for how useful it would be. Just ran through my head before I sent that post :)
_________________
___________
The best optimization is to do nothing at all.
Therefore a fully optimized program doesn't exist.
-Deku