raspberrypi / linux

Kernel source tree for Raspberry Pi-provided kernel builds. Issues unrelated to the linux kernel should be posted on the community forum at https://forums.raspberrypi.com/
Other
11.22k stars 5.02k forks source link

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

Closed dividuum closed 2 months ago

dividuum commented 2 months ago

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 commented 2 months ago

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 commented 2 months ago

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 commented 2 months ago

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 commented 2 months ago

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 commented 2 months ago

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 commented 2 months ago

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 commented 2 months ago

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 commented 2 months ago

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 commented 2 months ago

6364 for the fix.

dividuum commented 2 months ago

Can confirm that it now works as expected. Thanks!