Retroarch and 240p on CRT TV
-
@b0xspread said in Retroarch and 240p on CRT TV:
Pi Model or other hardware: P4b
This is the reason why you're having problems - the commands you use are not working correctly on the Pi 4 model. The Pi 4 uses a different set of video/GPU drivers than the previous models, the scaling done via
tvservice
that was possible on previous models (using the legacy video drivers) is not effective anymore on the Pi 4. While the terminal (framebuffer) might seem scaled, the underlying KMS/DRM video plane is aware of the scaling and will still display the image in the original (startup) video mode. -
@mitu thanks for the quick response, that makes sense, at least now I'm unstuck.
I am able to force progressive output again with tvservice while retroarch is already running and the image looks great, but I am hoping to find a proper solution.
Looking at the runcommand.sh source it does seem to check for kms for modeset and prefixes the launch command with the env variables that would presumably set a mode preference:
function get_config() { ... if [[ -n "$DISPLAY" ]] && $XRANDR &>/dev/null; then HAS_MODESET="x11" # copy kms tool output to global variable to avoid multiple invocations elif KMS_BUFFER="$($KMSTOOL -r 2>/dev/null)"; then HAS_MODESET="kms" elif [[ -f "$TVSERVICE" ]]; then HAS_MODESET="tvs" fi } function mode_switch() { ... if [[ "$HAS_MODESET" == "kms" ]]; then # update the target resolution even though the underlying fb hasn't changed MODE_CUR=($(get_${HAS_MODESET}_mode_info "${mode_id[*]}")) # inject the environment variables to do modesetting for SDL2 applications command_prefix="SDL_VIDEO_KMSDRM_CRTCID=${MODE_CUR[0]} SDL_VIDEO_KMSDRM_MODEID=${MODE_CUR[1]}" COMMAND="$(echo "$command_prefix $COMMAND" | sed -e "s/;/; $command_prefix /g")" return 0 ... elif [[ "$HAS_MODESET" == "tvs" ]]; then if [[ "${mode_id[0]}" == "PAL" ]] || [[ "${mode_id[0]}" == "NTSC" ]]; then $TVSERVICE -c "${mode_id[*]}" >/dev/null else $TVSERVICE -e "${mode_id[*]}" >/dev/null fi fi ... }
Here is what I have found: when running in 480i modetest only shows "FIXED"MODE" as the only supported mode
pi@retropie:/opt/retropie/supplementary/mesa-drm $ tvservice -s state 0x40000 [NTSC 4:3], 720x480 @ 60.00Hz, interlaced pi@retropie:/opt/retropie/supplementary/mesa-drm $ ./modetest | more trying to open device 'vc4'...done Encoders: id crtc type possible crtcs possible clones 50 49 TVDAC 0x00000001 0x00000000 Connectors: id encoder status name size (mm) modes encoders 51 50 connected composite-1 0x0 1 50 modes: name refresh (Hz) hdisp hss hse htot vdisp vss vse vtot) FIXED_MODE 60 720 734 798 858 480 487 493 525 13513 flags: phsync, pvsync, interlace; type: preferred, driver
but if I manually switch with tvservice it shows 240p as the preferred (FIXED_MODE) and 480i as user defined:
i@retropie:/opt/retropie/supplementary/mesa-drm $ tvservice -c "NTSC 4:3 P"; fbset -depth 8; fbset -depth 32; Powering on SDTV with explicit settings (mode:16 aspect:1) pi@retropie:/opt/retropie/supplementary/mesa-drm $ tvservice -s state 0x40000 [NTSC 4:3], 720x480 @ 60.00Hz, progressive pi@retropie:/opt/retropie/supplementary/mesa-drm $ ./modetest | more trying to open device 'vc4'...done Encoders: id crtc type possible crtcs possible clones 50 49 TVDAC 0x00000001 0x00000000 Connectors: id encoder status name size (mm) modes encoders 51 50 connected composite-1 0x0 2 50 modes: name refresh (Hz) hdisp hss hse htot vdisp vss vse vtot) FIXED_MODE 60 720 734 798 858 240 243 246 262 13513 flags: phsync, pvsync; type: preferred, driver 720x480 60 720 736 808 896 480 481 484 497 26740 flags: nhsync, pvsync; type: userdef
I was hoping that since 240p is the default after switching with tvservice I could switch to it using the 'FIXED_MODE' ID and use the '-d' option [By using the modetest option -d (drop master after mode set) in the above command, the next DRM-based application that will start will detect the current video mode and use it directly. https://wiki.st.com/stm32mpu/wiki/DRM_KMS_overview#Set_a_particular_video_mode]
Unfortunately that didn't work...
(must terminate emulationstation first)pi@retropie:/opt/retropie/supplementary/mesa-drm $ tvservice -c "NTSC 4:3 P"; fbset -depth 8; fbset -depth 32; Powering on SDTV with explicit settings (mode:16 aspect:1) pi@retropie:/opt/retropie/supplementary/mesa-drm $ sudo \./modetest -s 51@49:FIXED_MODE-60;fbset -depth 8; fbset -depth 32;tvservice -s trying to open device 'vc4'...done setting mode FIXED_MODE-60Hz@XR24 on connectors 51, crtc 49 failed to set gamma: Function not implemented state 0x40000 [NTSC 4:3], 720x480 @ 60.00Hz, interlaced
I am able to switch to 480i
pi@retropie:/opt/retropie/supplementary/mesa-drm $ ./modetest -D vc4 -s 51@49:720x480-60; fbset -depth 8; fbset -depth 32; tvservice -s; trying to open device 'vc4'...done setting mode 720x480-60Hz@XR24 on connectors 51, crtc 49 failed to set gamma: Function not implemented state 0x40000 [NTSC 4:3], 720x480 @ 60.00Hz, interlaced
I was hoping that this might work and the modes are enumerated so I tried mode 0/1/2 with those env variables after running tvservice and having two modes, to no avail.
pi@retropie:/opt/retropie/supplementary/mesa-drm $ cat /var/run/shm/runcommand.log Parameters: Executing: SDL_VIDEO_KMSDRM_CRTCID=49 SDL_VIDEO_KMSDRM_MODEID=2 /opt/retropie/emulators/retroarch/bin/retroarch -L /opt/retropie/libretrocores/lr-snes9x/snes9x_libretro.so --config /opt/retropie/configs/snes/retroarch.cfg "/home/pi/RetroPie/roms/snes/240pSuite.sfc" --appendconfig /dev/shm/retroarch.cfg Map_LoROMMap [CDROM] No sg devices found and sg kernel module is not loaded.
I don't know much about DRM/KMS, any ideas are welcome.
I am also thinking about working around this by writing a tvservice status watcher that looks for desired mode based on result of runcommand parsing of videomodes.cfg
-
Questions:
- My understanding is DRM/KMS get the modes from display EDID, but for composite, I am guessing this is provided by the firmware via vc4-fkms and is whatever sdtv_mode was? Would forcing a custom EDID that has both and supplying it to drm_kms_helper work? Then it would be possible to switch to. What is the proper solution here?
For now I got it to work with a watcher:
pi@retropie:/opt/retropie/configs/all $ ./vmodes_watcher.py Setting desired display mode: 'NTSC 4:3' ... Powering on SDTV with explicit settings (mode:0 aspect:1) Waiting for retroarch to start... Waiting for retroarch to start... Waiting for retroarch to start... Waiting for retroarch to start... Setting desired display mode: 'NTSC 4:3 P' ... Powering on SDTV with explicit settings (mode:16 aspect:1)
#!/usr/bin/python3 import time import sys from datetime import datetime, timedelta from subprocess import Popen, PIPE from watchdog.observers import Observer from watchdog.events import FileSystemEventHandler from watchdog.events import FileModifiedEvent import psutil state_path = '/opt/retropie/configs/all/desired_mode/' state_file = 'value' class MyHandler(FileSystemEventHandler): def __init__(self): self.last_modified = datetime.now() def checkIfProcessRunning(self, processName): for proc in psutil.process_iter(): try: if processName.lower() in proc.name().lower(): return True except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess): pass return False; def on_modified(self, event): if datetime.now() - self.last_modified < timedelta(seconds=1): return if isinstance(event, FileModifiedEvent) and event.src_path == state_path + state_file : for i in range(1, 10): if self.checkIfProcessRunning('retroarch'): mode = open(state_path + state_file).read().strip() print(f"Setting desired display mode: '{mode}' ...") p0 = Popen("tvservice -c '%s'" % (mode) , shell=True, stdout=PIPE, stderr=PIPE) out, err = p0.communicate() if p0.returncode == 0: print(out.decode(sys.getdefaultencoding()).strip()) p0 = Popen("fbset -depth 8; fbset -depth 32;", shell=True, stdout=PIPE, stderr=PIPE) out, err = p0.communicate() else: print(f"Failed: {err.decode(sys.getdefaultencoding()).strip()}" , file=sys.stderr) break else: print('Waiting for retroarch to start...') time.sleep(1); if __name__ == "__main__": event_handler = MyHandler() observer = Observer() observer.schedule(event_handler, path=state_path, recursive=False) observer.start() try: while True: time.sleep(1) except KeyboardInterrupt: observer.stop() observer.join()
-
Firsthand, I'd say the proper solution is to use an
x11
environment andxrandr
to add custom modes. Xorg acts like a 'drm master' and any clients started will automatically pick up the new videomode. AFAIR this is what initially CRTSwitchRes feature in RetroArch implemented, but later on they added support forhdmi_timings
and used the BCM api - on the Pi - to do something similar to whattvservice
/vcgencmd
does.2nd solution would be to add custom video mode support in SDL2. As you discovered, when using KMS/DRM,
runcommand
usesmodetest
to get a list of available video modes and to allow modesetting by setting an environment variable (SDL_VIDEO_KMSDRM_MODEID
). This var is picked by SDL2, which switches (usign the DRM api) to that video mode upon video initialization. I'm thinking it would be possible something similar - to pass a modeline definition to SDL2 and in turn SDL2 would create a custom video mode and switch to it.If you replace SDL2 in the 2nd solution with X11, you get the first solution, which is already working. The SDL2 version used by RetroPie is specifically patched to understand
runcommand
's env var, but it doesn't have an implementation of adding custom video modes. -
I noticed I had to kill emulationstation to run modetest, I am guessing it is the 'drm master' until it gives control over to SDL2? Adding a custom mode with xrandr and running it under X11 would work but the gui would look terrible in 240p as it is essentially a 480p image with only half the pixels being shown.
The second solution sounds much better. Ideally the device should expose both modes, not just the boot mode. Where can I find the code or documentation for this build of SDL2? I can't seem to find any references for this variable (SDL_VIDEO_KMSDRM_MODEID).
With the hack I have in place, I have emulationstation in 480i and then certain emulators in 240p based on runcommand, this has to be the end goal.
-
@b0xspread said in Retroarch and 240p on CRT TV:
I noticed I had to kill emulationstation to run modetest, I am guessing it is the 'drm master' until it gives control over to SDL2?
When running an emulator, via
runcommand
, ES goes into background and it's no longer using the GPU.Adding a custom mode with xrandr and running it under X11 would work but the gui would look terrible in 240p as it is essentially a 480p image with only half the pixels being shown.
Which GUI ?
The second solution sounds much better. Ideally the device should expose both modes, not just the boot mode. Where can I find the code or documentation for this build of SDL2? I can't seem to find any references for this variable (SDL_VIDEO_KMSDRM_MODEID).
As I said, it's the RetroPie modified version - which you can find at https://github.com/RetroPie/SDL-mirror.
-
By 'gui' I meant emulationstation. I have it running at 640x448, but when I set the mode to 240p effectively half the lines are lost as the image is no longer being interlaced and only one field is showing. Might be possible to resize it further, but still the issue of switching on the fly remains.
-
@mitu said in Retroarch and 240p on CRT TV:
@b0xspread said in Retroarch and 240p on CRT TV:
I noticed I had to kill emulationstation to run modetest, I am guessing it is the 'drm master' until it gives control over to SDL2?
When running an emulator, via
runcommand
, ES goes into background and it's no longer using the GPU.Adding a custom mode with xrandr and running it under X11 would work but the gui would look terrible in 240p as it is essentially a 480p image with only half the pixels being shown.
Which GUI ?
The second solution sounds much better. Ideally the device should expose both modes, not just the boot mode. Where can I find the code or documentation for this build of SDL2? I can't seem to find any references for this variable (SDL_VIDEO_KMSDRM_MODEID).
As I said, it's the RetroPie modified version - which you can find at https://github.com/RetroPie/SDL-mirror.
I see your merge request.
https://github.com/RetroPie/SDL-mirror/pull/16
I will give SDL_VIDEO_KMSDRM_MODELINE a shot when I have some free time. Thanks for your help!
-
@b0xspread said in Retroarch and 240p on CRT TV:
I will give SDL_VIDEO_KMSDRM_MODELINE a shot when I have some free time. Thanks for your help!
I don't think this will be enough. Runcommand already uses that to switch the resolution - from the list of detected resolutions.
-
-
This sounds really disappointing. Rpi4 was supposed to make things better for CRT users (because of exact pixel clock or something like that) not worse. We've spent literally years tweaking these conigs and runcommands...it'd be a pity if it all went to waste,
-
@youxia said in Retroarch and 240p on CRT TV:
Rpi4 was supposed to make things better for CRT users (because of exact pixel clock or something like that) not worse
First time I hear about this - do you have a source ? If any, video support was augmented for the HDMI output (4K/2 outputs), which makes sense, since that's what most users have. CRT (and 240p) usage is a niche market.
We've spent literally years tweaking these configs and runcommands...it'd be a pity if it all went to waste
The fact that the previous configurations do not work doesn't mean the same level of customization will not be possible. New methods would be developed to configure and improve the CRT output.
-
I installed X11, added a new modeline and added it to the Composite-1 output with xrandr, but it's just cutting off half the vertical resolution.
xrandr --newmode "240p" 13.51 720 734 798 858 240 243 246 262 +HSync +Vsync xrandr --addmode Composite-1 240p xrandr --output Composite-1 --mode 240p
If I set it to 240p in the console using tvservice, prior to launching X, it still comes up interlaced with half the screen cut off.
# xrandr Screen 0: minimum 320 x 200, current 720 x 240, maximum 7680 x 7680 Composite-1 connected primary 720x240+0+0 (normal left inverted right x axis y axis) 0mm x 0mm FIXED_MODE 60.00 + FIXED_MODE (0x42) 13.513MHz +HSync +VSync h: width 720 start 734 end 798 total 858 skew 0 clock 15.75KHz v: height 240 start 243 end 246 total 262 clock 60.11Hz
I was trying to look at the tvsource code to see what it was doing different when the 'P' flag was provided, but it seems to just set mode to 16 and push VC_TV_SDTV_ON to the VCHI message queue.
if (sdtv_progressive) mode |= SDTV_MODE_PROGRESSIVE; LOG_STD( "Powering on SDTV with explicit settings (mode:%d aspect:%d)", mode, aspect ); ret = display_id != -1 ? vc_tv_sdtv_power_on_id(display_id, mode, &options) : vc_tv_sdtv_power_on(mode, &options);
Thoughts?
-
@mitu said in Retroarch and 240p on CRT TV:
First time I hear about this - do you have a source ? If any, video support was augmented for the HDMI output (4K/2 outputs), which makes sense, since that's what most users have. CRT (and 240p) usage is a niche market.
https://github.com/raspberrypi/firmware/issues/734
See the last bunch of comments.
The fact that the previous configurations do not work doesn't mean the same level of customization will not be possible. New methods would be developed to configure and improve the CRT output.
Maybe so, but it may take a very long time, like with Rpi 3. It's been out nearly for a year already. I'm well aware how niche the CRT zone is, but it does not make it any less disappointing (especially given the massive amount of work which went into getting it sorted on Rpi3).
I thought I might make a jump now that new Retropie is out finally, but seeing how things are on this front I'll put this on hold. Thinking I'd have to go through all this config-battling malarkey again gives me shivers. My Rpi3 works fine for the less demanding platforms anyway, for others I have a crtemudriver PC.
@b0xspread if you're on reddit, try to ping u/ErantyInt. He knows a lot about connecting via composite, maybe he has some info on Rpi4 too.
-
@youxia said in Retroarch and 240p on CRT TV:
https://github.com/raspberrypi/firmware/issues/734
See the last bunch of comments.It's an interesting topic, looks useful for VGA/Scart HATs that use the HDMI timings.
Maybe so, but it may take a very long time, like with Rpi 3. It's been out nearly for a year already. I'm well aware how niche the CRT zone is, but it does not make it any less disappointing (especially given the massive amount of work which went into getting it sorted on Rpi3).
This is not an issue that's solvable in RetroPie - it's a kernel/firmware feature that was present in the legacy drivers and it's not present/un-implemented in the new ones. It's been known for quite some time that dynamic mode switching doesn't work like before and I don't think it's on the short term list of things the RPT engineers are working right now.
-
@youxia thanks, I'll give him a shout.
The issue is with the timing and format of the VSync pulse within the CSync signal needed to draw a progressive image.
My understanding is two things are needed:
- Integer number of lines (either 262 or 263) instead of 262.5 used for interlacing
- This will cause the VSync pulse to be sent at the beginning/end of each scanline and not in the middle, thus the scanline will be retraced instead of being shifted due to the ramp of the electron beam sawtooth wave.
It seems to me like changing the mode with xrandr just changes the size of the framebuffer, but the board is still outputting 525 lines with the second field vsync coming in mid scanline, so all my ModeLine efforts seem futile.
tvservice is able to alter the signal timing and modulation at the hardware level. I wish someone who knows how to use a oscilloscope could verify this.
I also wouldn't be this pessimistic about RP4 retropie as a whole. Apart from the CRT issues described , everything else has worked just fine. Albeit this is my first Raspberry pi so I have no point of comparison.
-
For now my conclusion is this:
- Progressive scan with RP4b still works. You just have to either set it in config.txt as the appropriate sdtv_mode (16 NTSC/18 PAL), but then Emulationstation will also be in 240, which looks terrible, or use tvservice to enforce it on a per rom/platform basis after retroarch starts to switch back and forth.
You can use my watcher python script and run it on startup in the background and then make an appropriate runcommand-onstart script or make your own solution.
Script: https://retropie.org.uk/forum/post/220137
pi@retropie:~ $ tail -2 /etc/rc.local su pi -c 'python3 -u /opt/retropie/configs/all/vmodes_watcher.py &> /var/log/vmodes_watcher.log' & exit 0
# taken from https://github.com/Sakitoshi/retropie-crt-tvout/tree/master/to_configs pi@retropie:~ $ cat /opt/retropie/configs/all/runcommand-onstart.sh if [ -f "/opt/retropie/configs/$1/480i.txt" ]; then interlaced=$(tr -d "\r" < "/opt/retropie/configs/$1/480i.txt" | sed -e 's/\[/\\\[/'); fi > /dev/null if [ -f "/opt/retropie/configs/ports/$1/480i.txt" ]; then interlaced=$(tr -d "\r" < "/opt/retropie/configs/ports/$1/480i.txt" | sed -e 's/\[/\\\[/'); fi > /dev/null if [ ! -s "/opt/retropie/configs/$1/480i.txt" ] && [ ! -s "/opt/retropie/configs/ports/$1/480i.txt" ] || [ -z "$interlaced" ]; then interlaced="empty"; fi > /dev/null if [ -f "/opt/retropie/configs/$1/240p.txt" ]; then progresive=$(tr -d "\r" < "/opt/retropie/configs/$1/240p.txt" | sed -e 's/\[/\\\[/'); fi > /dev/null if [ -f "/opt/retropie/configs/ports/$1/240p.txt" ]; then progresive=$(tr -d "\r" < "/opt/retropie/configs/ports/$1/240p.txt" | sed -e 's/\[/\\\[/'); fi > /dev/null if [ ! -s "/opt/retropie/configs/$1/240p.txt" ] && [ ! -s "/opt/retropie/configs/ports/$1/240p.txt" ] || [ -z "$progresive" ]; then progresive="empty"; fi > /dev/null if tvservice -s | grep NTSC && { ! echo "$3" | grep -wi "$interlaced" || echo "$interlaced" | grep empty; } && ! echo "$interlaced" | grep -xi "all" && { echo "$3" | grep -wi "$progresive" || echo "$progresive" | grep empty; }; then echo 'NTSC 4:3 P' > /opt/retropie/configs/all/desired_mode/value; else echo 'NTSC 4:3 ' > /opt/retropie/configs/all/desired_mode/value; fi echo "runcommand-onstart $1 $2 $3" pi@retropie:~ $ cat /opt/retropie/configs/all/runcommand-onend.sh if tvservice -s | grep NTSC; then tvservice -c "NTSC 4:3"; fbset -depth 8; fbset -depth 32; fi > /dev/null
240p is default, override per platform/ROM in 480i.txt files
pi@retropie:~ $ cat /opt/retropie/configs/mame-libretro/480i.txt all
- As stated in this thread: https://github.com/raspberrypi/firmware/issues/683#issuecomment-283179792 what was done in the firmware to make these new modes work is
A. Setting the progressive scan bit in the VEC (Composite TV out) register set:
https://github.com/torvalds/linux/blob/master/drivers/gpu/drm/vc4/vc4_vec.c#L104B. Remove the interlace flag for the mode:
https://github.com/torvalds/linux/blob/master/drivers/gpu/drm/vc4/vc4_vec.c#L256tvservice does essentially the same thing and sets the new modes:
https://github.com/raspberrypi/userland/blob/2448644657e5fbfd82299416d218396ee1115ece/interface/vmcs_host/vc_sdtv.h#L60
https://github.com/raspberrypi/userland/blob/master/host_applications/linux/apps/tvservice/tvservice.c#L703
https://github.com/raspberrypi/userland/blob/master/interface/vmcs_host/vc_vchi_tvservice.c#L1213
https://github.com/raspberrypi/userland/blob/master/interface/vmcs_host/vc_vchi_tvservice.c#L698Since there is a hardware flag that needs to be set, it won't be possible to resolve this with xrandr modesetting alone as was suggested.
Ideally RetroPie in some form could enforce this natively in a similar fashion by taking the SDTV_MODE as a parameter and enforcing it through VCHI like tvservice does, alongside the modsetting being done in the modified SDL library.
-
A simple workaround is to delay the tvservice call in runcommand-onstart.sh , e.g.:
#!/bin/bash echo "starting emulator "$2 >&2 if [ "$2" == 'lr-fceumm' ]; then sleep 5 && tvservice --sdtvon="NTSC 4:3 P" & fi
Contributions to the project are always appreciated, so if you would like to support us with a donation you can do so here.
Hosting provided by Mythic-Beasts. See the Hosting Information page for more information.