agg23 / openfpga-litex

A RISC-V software platform, exposing Analogue Pocket capabilities in a simple way
MIT License
36 stars 2 forks source link

There is (seemingly 100% consistent) vertical tearing when VBLANK deadline is met #7

Open mcclure opened 9 months ago

mcclure commented 9 months ago

Theoretically, the idea with openfpga-litex optimization is that you start writing to the framebuffer when video status (peripherals.APF_VIDEO.video.read()) .vblank_triggered().bit() goes high, and you finish before video status .vblank_status().bit() goes low. If you do this, the theory is the framebuffer only changes when the core is not reading it, and therefore no tearing will occur.

In my testing with minibreak and a custom test program, I find that there is 100% consistent tearing at the y=123 line exactly, if the VBLANK deadline is met (if the VBLANK deadline is not met— IE if we are still drawing when VBLANK goes high— then sometimes I do not see tearing).

I first noticed this in Minibreak. I noticed that the ball sometimes seemed to blink when it is around the midpoint of the screen. I assumed this was random, but then I caught this happening:

20231129_231420 20231129_231423

When only one brick is left in Minibreak, it starts moving side to side. On one run, I happened to have a brick on exactly the y=123 line and I saw it was tearing as it moved. This made me realize that the reason the ball blink seemed to be random was that it was only sometimes the ball happened to intersect the y=123 line, and it was only when it hit this precise pixel it blinked.

This evening I went back and revisited a test program I wrote:

Source: https://github.com/mcclure/pocket-riscv-rs/tree/strobe Binaries: strobe.zip

The program is called "strobe" and I do not actually recommend running it. It slowly fades from green to black and back, inverting the screen once per cycle. By pressing Y you can make the screen invert on demand, and by holding X, A or B you can make the screen strobe at different rates. It is unpleasant to look at and I have a headache now, but it shows us some interesting things about the vertical tearing. The program comes in two flavors, a plain one, and one that tries to talk to the serial port and report debugging information about draws. If a frame fails to complete during vblank, it prints an error, if a frame is missed entirely (the counter goes up by more than 1 between frames) it prints an error, it counts the number of "failed frames". Like this

image

If you don't touch the D-Pad, the program tries to rewrite the entire screen on every cycle, and unsurprisingly it does not succeed. Running this way I see a vertical tear near the bottom of the screen:

20231207_234644

and also I see the "drawing finished outside vblank deadline" warning on every frame when running with strobe-speed-debug.bin. This is all entirely expected, I drew while the framebuffer was being read and I saw tearing. This is "correct". (Note, on like one of every 5 or so runs, I see no tearing. That's a little more surprising, but not totally.)

Tonight I added another feature to see what happens when we meet the vblank deadline: Pressing the d-pad causes it instead of drawing the entire screen to draw only to the limit of SCREEN_WIDTH / 2, 4, 8 or 16.

If I run with any of these four divisors, I see tearing on the y=123 line, but [i]not[/i] on the lower tear line:

20231207_234702

I see this failure even on runs when the SCREEN_WIDTH full does [i]not[/i] produce tearing. These are all screenshots, by the way, which means the tearing is visible to the scaler (or whatever Analogue thingy takes the screenshots).

With SCREEN_WIDTH full or SCREEN_WIDTH / 2, I get the "drawing finished outside vblank deadline" warning. with / 4, 8 or 16 I do not.

This was briefly discussed, with only the first bit of the information above, in Discord. Agg theorized that there is a "logical vblank" which is different from the advertised VBLANK, and that the litex display component's FIFOs could be to blame.

My "expected" behavior is that advertised VBLANK should be the same as "logical VBLANK" and that it should be possible to avoid tearing by drawing within advertised VBLANK (agg has already put a good bit of effort into trying to make this the case). However it may be Litex erects impassable barriers to us fixing this.

Things I can't explain:

mcclure commented 9 months ago

agg in chat: "Y=123 is exactly where I would expect it to be. The FIFO is 32768 pixels, and 32768/266 = 123.19"