Skip to content

Conversation

@SpookyIluha
Copy link
Contributor

Libdragon is rendering internally at a 480p even in interlaced mode, but the TV output is still 480i,
so this special mode makes use of the RDP's interlacing as well for the optimization.
But to actually make proper use of it, it needs a tight phased frame schedule between the RDP and VI to make sure that the frames drawn are latest and complete.
Moreover since the AA/dedithering algorithms use adjasent lines which may still be rendering by the RDP,
the phased schedule needs to account for that and only use fully drawn framebuffers for the VI.

Using this VI/RDP interlacing halvs the required fillrate to draw a frame and ideally doubles the framerate in high-res mode.
It only works if the Y scale is exactly 1.0 (i.e vertical resolution + borders should be = 640 (in NTSC) or 576 (in PAL)) and the number of buffers should be 3.

If you use this interlacing mode, make sure to enable rdpq_enable_interlaced() after rdpq_attach() as well.
Use display_interlace_rdp_field() to know which field you should draw each frame.

@SpookyIluha SpookyIluha force-pushed the unstable_rdp_vi_optimize branch from 846a760 to e6235f4 Compare May 25, 2025 18:02
@rasky
Copy link
Collaborator

rasky commented May 27, 2025

I thought a bit of this PR. I believe I have now understood the algorithm that we need to implement (though I might still be missing something). My suggestion for a simpler implementation would be the following (this is what happens when the special interlaced mode is active)

  • The various buffer masks (drawing_mask, ready_mask) track fields instead of buffers. This means that bit 3 would be "buffer 1, field 1" for instance (bufidx = bit/2; fieldidx = bit & 1).
    • NOTE: this might be complicated to implement while also keeping the current implementation. Maybe the best compromise would be to switch the whole code to this representation, but then in normal progressive mode, increment by 2 instead of 1 in buffer_next().
  • now_showing also becomes a field index rather than a buffer index.
  • now the magic trick: at every interrupt, if the field to be drawn is different from the one in now_showing (if (*VI_V_CURRENT & 1 != now_showing & 1)), then we need to display now_showing - 1 instead. That is, VI_ORIGIN must be set to __safe_buffers[(now_showing - 1) / 2].
  • In the standard interlaced case (not RDP interlaced), the magic trick will work correctly if now_showing also points to an odd number (so that (now_showing-1) is the same buffer, just the other field). To do this, I think it's sufficient to initialize correctly the various indices, and then buffer_next() must increment them by 2.
  • I believe the correct pacing will be guaranteed if in display_try_get we change this line if (((drawing_mask | ready_mask) & (1 << next)) == 0) to check both fields, so something like 3 << (next & ~1). I need to think a little bit better about this part.

Does this make any sense?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants