ramapcsx2 / gbs-control

GNU General Public License v3.0
771 stars 110 forks source link

Properly darken chroma along with luma when enabling scanlines #508

Closed nyanpasu64 closed 8 months ago

nyanpasu64 commented 8 months ago

Fixes an issue where scanlines were only applied to luma and not chroma, causing blue (and somewhat red) areas of the image failing to apply scanlines.

Note to self ``` crt monitor/gbs-c scanlines, deinterlacing.md # 2023-10-30 chroma fix ```

Background

Scanlines in 240p content are implemented by turning on the deinterlacer (but not RAM accesses?) to weave the current field with a black "previous field", then setting the deinterlacer into a mode where you manually control (using MADPT_Y_MI_OFFSET and MADPT_UV_MI_OFFSET) how much to interpolate between scanlines vs. using the previous black field.

Previously, only luma deinterlacing was enabled, and chroma was wholly interpolated between scanlines. This caused increased color saturation (and brightness if some RGB channels become negative and get clipped to zero) in scanline gaps.

Enabling chroma scanlines

This PR enables chroma deinterlacing (which was largely implemented but commented out). The commented-out code set MADPT_UV_MI_OFFSET (chroma blending/brightness) to a fixed value of 64, which caused chroma to remain partly blended between scanlines even when scanlines were fully set to black, by clicking "scanline intensity" until MADPT_Y_MI_OFFSET (luma blending/brightness) is set to 0.

Tuning chroma scanlines

I want the RGB values in scanline gaps to be linearly interpolated between black and the average of the adjacent scanlines. To do this, we must interpolate Y and UV by the same amount between black and the average of the adjacent scanlines. This can be achieved by setting MADPT_UV_MI_OFFSET = MADPT_Y_MI_OFFSET (which is itself written with GBS::MADPT_Y_MI_OFFSET::write(uopt->scanlineStrength)).

To verify I took pictures of my VGA CRT in 480p (which can resolve output scanlines) using a macro camera, to check that scanlines appeared correctly:

DSC00947

DSC00942 brightened

DSC00948 compressed DSC00955 brightened

Questions

Fixes #458.

nyanpasu64 commented 8 months ago

Try to make it look good, test it on some other displays and other source consoles, and it'll be fine? A lag test setup will be required to tell if the changes add any more latency.

I brought out a Samsung T24C550ND LCD monitor/TV for testing (since I didn't want to play on my vertically mounted VGA-compatible LCD monitor), and plugged the GBS-C into the monitor over VGA. Unfortunately it misinterprets GBS-C's 1280x960 output as 1600x900, so I set the GBS-C to output in 1080p mode (at the cost of horizontal resolution 😦).

Appearance, SNES testing

Super Mario World on a 1:1:1 Super Famicom looked good on the monitor (as good as GBS-C's emulated scanlines will get), and the scanlines on a 1080p display/signal were not noticeably worse than 960p on a CRT. The blue water on the Yoshi's Island overworld map had well-defined dark scanline gaps (as opposed to the vaguely differentiated sea of blue before this PR). I found that on this LCD, setting scanline brightness to 20 (rather than my earlier 30) made the image look better (though that may sacrifice too much brightness on a CRT with an inherently darker gamma curve). I did not feel noticeable input lag, but did not test the lag added by the monitor.

When I booted SNES 240p Test Suite and opened color bars, I noticed that the image background was a noisy dark green, and that with scanlines enabled, the darkened scanlines had a dark purple background instead. This was not noticeable unless I was looking closely at the display, and could be caused by the GBS-C and monitor's AC coupling and DC offsets. I did not notice this issue during actual gameplay.

Oscilloscope RGB testing

When performing oscilloscope captures with the GBS-C set to 480p output, I found that even on Scanline Brightness: 0 where scanline gap lines should be pitch-black, the output voltage within scanline gaps was around 6% of the full-scale voltage (using the blanking voltage as a reference). This occurred in both white, green, and blue images. Disabling "line filter" did not fix this problem.

On colored images, there was a small amount of crosstalk between output channels; a green bright line would induce a small amount of blue signal (while a blue bright line would induce less of a green signal). Darkened output scanlines would induce proportionally less signal to other color channels, or no signal at all if I set scanline brightness to 0.

Oscilloscope captures: Green output scanline leakage, and low crosstalk to blue: ![DS1Z_QuickPrint33](https://github.com/ramapcsx2/gbs-control/assets/913957/aba34771-83d0-4393-bcd1-c62d312c0a4e) Blue output scanline leakage, and near-zero crosstalk to green: ![DS1Z_QuickPrint34](https://github.com/ramapcsx2/gbs-control/assets/913957/c59a86d9-5868-4256-b90a-6a9cab5cdc2e)

In summary, oscilloscope testing reveals that the scanlines aren't perfect, but it's "close enough" that I don't think my code has obvious flaws. Also I don't think I can reasonably improve it further, and don't want to try. I guess you get what you pay for...


While testing various parameters, I noticed that the scanline gaps were a lot brighter than I expected at higher scanline brightness values. I found that scanline brightness 50 (out of a 7-bit number or 127) produced a darkened output scanline RGB level (BY-AY) of 510 mV, compared to a bright scanline RGB level of 710 mV. This means that darkened output scanlines are 0.718 times as bright as regular output scanlines, while the blending factor programmed into the TV5725 is only 50/127 = 0.394. I'm not sure what's going on (what formula is used to interpolate between black/blended lines), but don't want to look deeper ATM.

Signal latency testing

Earlier I had already ran an button-to-photon lag test on Wii -> GBS-C -> CRT, using a slow-motion camera recording of my GC controller and the monitor.

This time I decided to run a signal-level lag test of the GBS-C alone:

DS1Z_QuickPrint32

I found that the GBS-C's output switched cleanly from green to blue output, a quarter frame after the input switched from green to blue. By zooming into the signal capture, I found no visible errors (like using chroma levels from the previous frame) in the blue scanlines after the switch.

This implies that the GBS-C in chroma scanline mode has no latency, beyond the GBS-C's natural frame-buffering (which I've configured to a quarter-frame using frame sync).

Interestingly I found that with clockgen framesync enabled, the output signal actually drifts back and forth by around a quarter input-scanline or 12 microseconds. This is only visible when zooming way into the signal. The phase error appears to reach a positive or negative peak every 1.6 seconds, or lockInterval = 100 * 16.70; // every 100 frames (perhaps with some long-term drift/noise by a few microseconds). In any case quarter-scanline accuracy is more than good enough (assuming the monitor doesn't lose sync, which I haven't seen this LCD do so far), and I recall the error being even smaller when locking onto a Wii.

Summary

I could probably merge the PR now? I'm still hoping ArcheyChen can test the PR, though if he isn't ready to test the fix I may still merge it since it "works on my machine" and I don't want to change it further.

ramapcsx2 commented 8 months ago

Man, I love that crazy test you built there, huge fan :D

The latency is actually my main worry, and it's good that it seems like it will not be an issue. If you read the register descriptions/programmer's guide a bit between the lines, it seems like adding Chroma would require more fully buffered fields (half frames, so 16.66ms / 2). It's possible though that we don't automatically get the added latency, if the registers aren't set up "correctly", and since we don't use this for actual deinterlacing, that might be what's going on.

I'm okay with merging this in any case. Love the scope test xD

ramapcsx2 commented 8 months ago

Little side note: The SNES 240p test suite doesn't have to be switching the shown image in Vblank. There's no reason to assume anything like that, in any kind of test software, unless it was made specifically to avoid this :p

nyanpasu64 commented 8 months ago

Sidenote: in television terminology, for interlaced signals, individual images (eg. in 480i, 240-line for 16.6ms) are called fields, and frames instead last for two vertical periods (eg. 33.3ms) and include both an odd and even field.

In 240p we call 16.7ms images "frames" for convenience (matching 480p), even though 240p isn't a standard TV signal and gets misinterpreted as 480i by some capture cards and TVs (which will weave the previous frame/field with the current, if you're lucky it's motion-adaptive "deinterlacing" and will only weave in 30hz flicker). It sucks how when a game switches from 240p to 480i, the 16.6ms vertical period changes names from a frame to a field, but I didn't make the rules.


In luma deinterlacing, I found that the previous frame buffers are only used to compare the current frame to, and weave old frame data in non-moving sections of the image. I'd speculate (but am not confident) that a similar thing is happening in chroma deinterlacing.