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 > 'Weird 3D problem', need 3D geek help

#140800 - simonjhall - Wed Sep 19, 2007 10:39 pm

Sorry if this all seems a bit vague - I'm having a bit of trouble understanding what's going on!

I've always noticed that when I used fogging, changes in the fogging settings don't seem to appear until one frame after the settings have been made. So geometry will be rendered unfogged in the frame it was enabled in, and then will be fogged in the next frame. I assumed "ok, maybe that's how fogging works, or there's something else wrong in my code" and ignored it.
I've recently rejigged lots of my rendering code to remove redundant state transitions (since I was losing over 1ms/frame with redundant projection matrix changes).
My render loop USED TO look like this:
Code:
glReset();
<set up random stuff>
set projection matrix to perspective mode
<more setup>
....
for each gui element
   set projection matrix to orthographic
   render game hud, eg weapon info, crosshair gui elements etc
   set projection matrix to perspective
....
render weapon
render monsters
render world
...
swap buffers (GFX_FLUSH = 2)

My render loop now looks like this:
Code:
glReset();
<set up random stuff>
set projection matrix to orthographic mode
<more setup>
....
for each gui element
   render game hud, eg weapon info, crosshair gui elements etc
....
set projection matrix to perspective mode
...
render weapon
render monsters
render world
...
swap buffers (GFX_FLUSH = 2)


Performance has gone up (slightly - every little helps), and everything looks exactly the same as before.
I've noticed something wrong on the loading screens, after stepping into a teleporter at the end of a level (3D graphics have been on the screen for at least one frame by this point). During loading screens *no geometry is submitted*, however the whole setup/swap buffers loop still occurs, like so.
The render loop looks effectively like this:
Code:
glReset();
<set up random stuff>
set projection matrix to orthographic mode
<more setup>
swap buffers (GFX_FLUSH = 2)
After this swap buffers call what I see for a single frame is the previous rendered scene, but drawn (completely correctly) in orthographic mode, despite not having submitted any geometry.

So I assume that there's some crap going on with my rendering code and cut it down to the very bare minimum - just drawing the crosshair, like so.
My render loop now looks like this:
Code:
glReset();
<set up random stuff>
set projection matrix to orthographic mode
<more setup>
draw crosshair
swap buffers (GFX_FLUSH = 2)
...and I get the crosshair on the screen. Now again, in loading screens no geometry is submitted (not even the crosshair) yet I still get the crosshair on the screen after the swap buffers.

Again, this looks just like the whole system is one frame behind and I'm not too worried by this. But if I change it slightly to this, (note the perspective matrix)
Code:
glReset();
<set up random stuff>
set projection matrix to orthographic mode
<more setup>
draw crosshair
set projection matrix to projection mode
swap buffers (GFX_FLUSH = 2)
I again get a valid crosshair, since the crosshair was submitted whilst the projection matrix was in perspective mode. But during the loading screen, the code is effectively this:
Code:
glReset();
<set up random stuff>
set projection matrix to orthographic mode
<more setup>
<no submission of geometry>
set projection matrix to projection mode
swap buffers (GFX_FLUSH = 2)
What I see is the crosshair, but drawn with a perspective matrix.

This looks to me like geometry isn't rendered until the next frame, yet changes to the projection matrix (and fogging settings...and other things too?) don't occur until the next frame. I have no idea of what's going on here - could anyone point out what I'm doing wrong? I'm using the r20 drop from many number of months ago, there's no libnds CVS goodness in here since I'd rather not update unless I really have to.

Btw I realise I could just turn off the 3D layer during loading screens, but I'd rather know what's going on :-D
_________________
Big thanks to everyone who donated for Quake2

#140801 - mml - Wed Sep 19, 2007 11:10 pm

It's not just that the crosshair is left behind in the old front buffer is it? (So after two swapbufferses you're back to your old data, and since you're not writing any new geometry this frame the old data doesn't get overwritten...) Though I don't think that would explain the perspective change. Weird.

#140816 - DekuTree64 - Thu Sep 20, 2007 12:08 am

Hmm, I don't know offhand what it could be, but just for clarity here's an example of how the 3D works:

- Turn on 3D hardware
- Wait for VBlank
- Send down some polygons
- Send down swap buffers command
- Wait for VBlank

Now you're in VBlank, and the 3D hardware is "cocked and ready" to display some polygons during the next vdraw cycle.

Now if you add in menu switching logic, you could get a 3D frame left over:

--- Frame 1 ---
- Do game logic
- Draw game models
- Send swap buffers command
- Wait for VBlank
--- Frame 2 ---
- Do game logic
- Draw game models
- Send swap buffers command
- Wait for VBlank
--- Frame 3 ---
- Do game logic, which happens to be "switch to inventory menu", so the 2D hardware is set up to display the inventory menu.
- Send swap buffers command
- Wait for VBlank
--- Frame 4 ---
- Update the menu
- Send swap buffers command
- Wait for VBlank

So, in that case, you would get the 3D left over, since the menu init happened during the VBlank after drawing the game models.
One good way to deal with that is to buffer all 2D registers, and when you send a swap buffers command, set a flag to tell your VBlank interrupt handler to swap in the 2D registers as well.


But then the projection matrix changing makes absolutely no sense. The matrix hass nothing to do with anything except for immediately when sending the vertex down. If you do this:

--- Frame 1 ---
- Draw models
- Swap buffers
- Wait for VBlank
--- Frame 2 ---
- Fiddle with projection matrix
- Swap buffers
- Wait for VBlank
--- Frame 3 ---
...

So during frame 2, there should be models on the screen. But during frame 3, the screen should be blank, because no vertices were sent down during frame 2. If there ARE things visible, then something weird is going on. And if those things look different than they did during frame 2, then the only rational explanation is that they got rendered again by accident during frame 2.
_________________
___________
The best optimization is to do nothing at all.
Therefore a fully optimized program doesn't exist.
-Deku

#140820 - M3d10n - Thu Sep 20, 2007 12:30 am

Maybe you're missing a v-blank? Remember that the DS "swap buffer" command is completely different than on OpenGL's or Direct3D's: you're not swapping screen buffers, you're swapping geometry buffers.

The DS GPU uses deferred rendering, with two geometry buffers. All your geometry commands are cached to the "back" geometry buffer and rendering happens only at a specific time, reading from the "front" buffer. The glFlush() commands tells the GPU that it is allowed to swap buffers, but that also only happens at a specific time during v-blank.

If your game loop takes longer than a v-blank to complete, the geometry on the front buffer from the last v-blank will be rendered again to the screen. This might be what you're seeing.

Also, are you waiting for v-blanks at the end of your loop?