Skip to content

DRM scaling filter "Nearest Neighbor" on YU12 / NV12 framebuffers looks wrong #6362

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
dividuum opened this issue Sep 16, 2024 · 10 comments
Closed

Comments

@dividuum
Copy link

dividuum commented Sep 16, 2024

Describe the bug

I toyed around with the new scaling filter feature recently added. Something weird is going on when using YU12 or NV12 framebuffers. Here's how it looks for a 560x320 video on a 640x480 display:

scaling

Here's the corresponding plane configuration:

plane[126]: plane-6
        crtc=crtc-2
        fb=334
                allocated by = info-beamer
                refcount=2
                format=YU12 little-endian (0x32315559)
                modifier=0x0
                size=560x320
                layers:
                        size[0]=560x320
                        pitch[0]=560
                        offset[0]=0
                        obj[0]:
                                name=0
                                refcount=3
                                start=001012c4
                                size=315392
                                imported=yes
                                dma_addr=0x0000000a7ea80000
                                vaddr=0000000000000000
                        size[1]=280x160
                        pitch[1]=280
                        offset[1]=200704
                        obj[1]:
                                name=0
                                refcount=3
                                start=001012c4
                                size=315392
                                imported=yes
                                dma_addr=0x0000000a7ea80000
                                vaddr=0000000000000000
                        size[2]=280x160
                        pitch[2]=280
                        offset[2]=258048
                        obj[2]:
                                name=0
                                refcount=3
                                start=001012c4
                                size=315392
                                imported=yes
                                dma_addr=0x0000000a7ea80000
                                vaddr=0000000000000000
        crtc-pos=640x365+0+57
        src-pos=560.000000x320.000000+0.000000+0.000000
        rotation=1
        normalized-zpos=0
        color-encoding=ITU-R BT.709 YCbCr
        color-range=YCbCr limited range

This only happens when using "Nearest Neighbor" as scaling filter. "Default" works fine. It also doesn't seem to happen with RGBA framebuffers.

Steps to reproduce the behaviour

Apply "Nearest Neighbor" value to the SCALING_FILTER property for a YU12 (software decoded H264) or NV12 (HW decoded HEVC) plane.

I haven't tried to reproduce this on Bookworm because kernel 6.6.51 doesn't seem to be officially available yet via APT.

Device (s)

Raspberry Pi 5

System

info-beamer-x ~ # uname -a
Linux info-beamer-x 6.6.51-v8+ #1796 SMP PREEMPT Fri Sep 13 14:38:11 BST 2024 aarch64 GNU/Linux
info-beamer-x ~ # vcgencmd bootloader_version
2024/09/10 14:40:30
version 5be4f304b152e06cbcb9c08c26aafc9da755ae47 (release)
timestamp 1725975630
update-time 1726297958
capabilities 0x0000007f
info-beamer-x ~ # vcgencmd version
2024/09/10 14:40:30 
Copyright (c) 2012 Broadcom
version 5be4f304 (release) (embedded)

Logs

No response

Additional context

No response

@6by9
Copy link
Contributor

6by9 commented Sep 16, 2024

Can you post the original image? I can guess at what it should look like, but would be useful to confirm.

It half looks like the chroma planes are getting scaled by twice the factor desired, but it's tricky to say for definite. At a guess I'd put it down to the SCALER_PPF_NOINTERP flag at https://github.com/raspberrypi/linux/blob/rpi-6.6.y/drivers/gpu/drm/vc4/vc4_plane.c#L617
My other note is that you're upscaling (560x320 to 640x480 for luma, and 280x160 to 640x480 for chroma). Does it do the same on downscaling?

Nearest neighbour is less useful for video planes - it's good for the blocky graphics of retro games emulation, but that's all going to be RGB rather than YUV.

@dividuum
Copy link
Author

Can you post the original image? I can guess at what it should look like, but would be useful to confirm.

Sure. Sorry. Here you go:

default

The bar at the bottom in both screenshots is a GL RGBA overlay rendering fine.

My other note is that you're upscaling (560x320 to 640x480 for luma, and 280x160 to 640x480 for chroma). Does it do the same on downscaling?

Not sure. Might take a moment to try it out.

@dividuum
Copy link
Author

Here's the result of downscaling a FullHD video to 640x360:

plane[126]: plane-6
        crtc=crtc-2
        fb=332
                allocated by = info-beamer
                refcount=2
                format=YU12 little-endian (0x32315559)
                modifier=0x0
                size=1920x1080
                layers:
                        size[0]=1920x1080
                        pitch[0]=1920
                        offset[0]=0
                        obj[0]:
                                name=0
                                refcount=3
                                start=00100968
                                size=3256320
                                imported=yes
                                dma_addr=0x0000000a7f000000
                                vaddr=0000000000000000
                        size[1]=960x540
                        pitch[1]=960
                        offset[1]=2150400
                        obj[1]:
                                name=0
                                refcount=3
                                start=00100968
                                size=3256320
                                imported=yes
                                dma_addr=0x0000000a7f000000
                                vaddr=0000000000000000
                        size[2]=960x540
                        pitch[2]=960
                        offset[2]=2703360
                        obj[2]:
                                name=0
                                refcount=3
                                start=00100968
                                size=3256320
                                imported=yes
                                dma_addr=0x0000000a7f000000
                                vaddr=0000000000000000
        crtc-pos=640x360+0+60
        src-pos=1920.000000x1080.000000+0.000000+0.000000
        rotation=1
        normalized-zpos=0
        color-encoding=ITU-R BT.709 YCbCr
        color-range=YCbCr full range

"Default":

downscale-default

"Nearest Neighbor":

downscale-nearest

@6by9
Copy link
Contributor

6by9 commented Sep 16, 2024

Hmm, I think something is just totally wrong. Even RGB images are being scaled incorrectly for me on Pi4.

On my system with HDMI-A-1, I get weird outputs using modetest
modetest -M vc4 -w 91:"SCALING_FILTER":1
modetest -M vc4 -P 91@102:720x1280*2@XB24
(replace 91 and 102 with the relevant primary plane and crtc for your display).
My images are coming through at approx half size, with the right and bottom-most pixels repeated.

I'm going to have to check whether the filter actually works in the firmware.

@6by9
Copy link
Contributor

6by9 commented Sep 16, 2024

My early guess of the SCALER_PPF_NOINTERP bit appears to be correct. If enabled, then no scaling appears to happen. YUV 420 shows more issue as the luma and chroma planes have different scaling factors, so the colours are all totally wrong.

Even without that flag, a x8 scaling (modetest -M vc4 -P 91@102:480x480*4@XB24 on a 4k screen) shows definite jagged lines which I'd expect. YU12 shows a colour artifact on one of the edges.

@popcornmix Do you have any further knowledge on this? It was you who pointed out the interpolate flag on the original PR.

I'm tempted to just drop the interpolate flag for now rather than investigate much further.

@popcornmix
Copy link
Collaborator

I could have a look. I did implement a similar feature on the firmware side, which does set SCALER_PPF_NO_INTERPOLATE in the display list.

But that was a while ago (git says over ten years...) so it may take a while to work out what is needed.

@6by9
Copy link
Contributor

6by9 commented Sep 16, 2024

I could have a look. I did implement a similar feature on the firmware side, which does set SCALER_PPF_NO_INTERPOLATE in the display list.

But that was a while ago (git says over ten years...) so it may take a while to work out what is needed.

4k monitor, disabling the KMS driver and using

framebuffer_width=640
framebuffer_height=400
scaling_kernel=8

gives me a very blocky console (as expected). Now to extract the dlist to analyse....

@6by9
Copy link
Contributor

6by9 commented Sep 16, 2024

Doh, operator precendence

	vc4_dlist_write(vc4_state,
			no_interpolate ? SCALER_PPF_NOINTERP : 0 |
			SCALER_PPF_AGC |
			VC4_SET_FIELD(scale, SCALER_PPF_SCALE) |
			/*
			 * The register layout documentation is slightly
			 * different to setup the phase in the BCM2712,
			 * but they seem equivalent.
			 */
			VC4_SET_FIELD(phase, SCALER_PPF_IPHASE));

?: is lower precedence than |, so we get just SCALER_PPF_NOINTERP when in no_interpolate mode.

Time for a pair of parenthesises.

@6by9
Copy link
Contributor

6by9 commented Sep 16, 2024

#6364 for the fix.

@dividuum
Copy link
Author

Can confirm that it now works as expected. Thanks!

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

No branches or pull requests

3 participants