Hi Thomas, Dmitry,

> Subject: Re: [PATCH v4] drm/virtgpu: Make vblank event dependent on the
> external sync
> 
> On 10/27/25 12:52, Thomas Zimmermann wrote:
> > For each plane, store the buffer object's sync status in the state
> > of the plane's respective CRTC. During the CRTC's atomic flush,
> > ignore the vblank timer if any of the CRTC's plane's buffer object
> > is synchronized to an external source. Instead send the vblank event
> > immediately. Avoids corner cases where a vblank event happens too
> > late for the next frame to be page flipped in time.
> >
> > Exporters of GEM objects sometimes interfere with the vblank timer;
> > resulting in framerate drops. Hence detect this case and handle it
> > as outlined above.
> >
> > The vblank timer is still active and fires in regular intervals
> > according to the guest display refresh. This rate limits clients
> > that only wait for the next vblank to occur, such as fbcon. These
> > clients would otherwise produce a very high number of frames per
> > second.
> >
> > v4:
> > - only handle imported GEM buffer objects (Dmitri, Vivek)
> > - fix test for plane visibility (Vivek)
> > - always enable vblank timer to make waiting clients happy
> > v3:
> > - disable vblank unconditionally
> > - compute status on each commit
> > v2:
> > - enable/disable vblank timer according to buffer setup
> >
> > Signed-off-by: Thomas Zimmermann <[email protected]>
> > ---
> > This patch was previously called "drm/virtgpu: Make vblank event
> > dependent on the host resource". Earlier versions where meant for
> > testing, rather than being merged. See [1] for a discussion of when
> > the fixed problem happens.
> >
> > [1] https://lore.kernel.org/dri-devel/20251016145230.79270-1-
> > [email protected]/
> > ---
> >  drivers/gpu/drm/virtio/virtgpu_display.c | 67 ++++++++++++++++++++++--
> >  drivers/gpu/drm/virtio/virtgpu_drv.h     | 15 ++++++
> >  drivers/gpu/drm/virtio/virtgpu_plane.c   | 28 +++++++++-
> >  3 files changed, 104 insertions(+), 6 deletions(-)
> 
> No problems spotted. I'd only extended comment in the code explicitly
> stating that drm_gem_is_imported() is the workaround, might edit it on
> applying. Will wait for Vivek's feedback. Thanks.
I applied this patch and tested 3 different scenarios:
1) blob=true + imported buffers
root@localhost:weston_upstream/weston# ./build/clients/weston-simple-egl -o &
Using config: r8g8b8a0
has EGL_EXT_buffer_age and EGL_EXT_swap_buffers_with_damage
has EGL_EXT_surface_compression
245 frames in 5 seconds: 49.000000 fps
236 frames in 5 seconds: 47.200001 fps
279 frames in 5 seconds: 55.799999 fps
240 frames in 5 seconds: 48.000000 fps
244 frames in 5 seconds: 48.799999 fps
283 frames in 5 seconds: 56.599998 fps
274 frames in 5 seconds: 54.799999 fps

2) blob=true
root@localhost:/weston_upstream/weston# ./build/clients/weston-simple-egl -o &
Using config: r8g8b8a0
has EGL_EXT_buffer_age and EGL_EXT_swap_buffers_with_damage
has EGL_EXT_surface_compression
167 frames in 5 seconds: 33.400002 fps
208 frames in 5 seconds: 41.599998 fps
203 frames in 5 seconds: 40.599998 fps
252 frames in 5 seconds: 50.400002 fps
180 frames in 5 seconds: 36.000000 fps
219 frames in 5 seconds: 43.799999 fps
176 frames in 5 seconds: 35.200001 fps
220 frames in 5 seconds: 44.000000 fps

3) blob=false
root@localhost:/weston_upstream/weston# ./build/clients/weston-simple-egl -o &
[1] 1137
Using config: r8g8b8a0
has EGL_EXT_buffer_age and EGL_EXT_swap_buffers_with_damage
has EGL_EXT_surface_compression
285 frames in 5 seconds: 57.000000 fps
291 frames in 5 seconds: 58.200001 fps
290 frames in 5 seconds: 58.000000 fps
297 frames in 5 seconds: 59.400002 fps
291 frames in 5 seconds: 58.200001 fps
292 frames in 5 seconds: 58.400002 fps

The problem in the first 2 cases above is that as long as blob=true, the vblank 
timer
is not helpful as we would be over-synchronizing. Note that even in the second
case, if blob=true, then even dumb BOs would satisfy bo->guest_blob condition.

So, I added the following changes to Thomas' patch and can now achieve FPS >= 58
in all 3 cases above:
diff --git a/drivers/gpu/drm/virtio/virtgpu_display.c 
b/drivers/gpu/drm/virtio/virtgpu_display.c
index a4fa948023da..10c4a16a3908 100644
--- a/drivers/gpu/drm/virtio/virtgpu_display.c
+++ b/drivers/gpu/drm/virtio/virtgpu_display.c
@@ -159,7 +159,10 @@ static void virtio_gpu_crtc_mode_set_nofb(struct drm_crtc 
*crtc)
 static void virtio_gpu_crtc_atomic_enable(struct drm_crtc *crtc,
                                          struct drm_atomic_state *state)
 {
-       drm_crtc_vblank_on(crtc);
+       struct virtio_gpu_crtc_state *vgcrtc_state = 
to_virtio_gpu_crtc_state(crtc->state);
+
+       if (!virtio_gpu_crtc_state_send_event_on_flush(vgcrtc_state))
+               drm_crtc_vblank_on(crtc);
 }

 static void virtio_gpu_crtc_atomic_disable(struct drm_crtc *crtc,
diff --git a/drivers/gpu/drm/virtio/virtgpu_plane.c 
b/drivers/gpu/drm/virtio/virtgpu_plane.c
index 634e1a930783..8381892e6d4e 100644
--- a/drivers/gpu/drm/virtio/virtgpu_plane.c
+++ b/drivers/gpu/drm/virtio/virtgpu_plane.c
@@ -99,6 +99,8 @@ static const struct drm_plane_funcs virtio_gpu_plane_funcs = {
 static int virtio_gpu_plane_atomic_check(struct drm_plane *plane,
                                         struct drm_atomic_state *state)
 {
+       struct drm_device *dev = plane->dev;
+       struct virtio_gpu_device *vgdev = dev->dev_private;
        struct drm_plane_state *new_plane_state = 
drm_atomic_get_new_plane_state(state,
                                                                                
 plane);
        struct drm_plane_state *old_plane_state = 
drm_atomic_get_old_plane_state(state,
@@ -138,13 +140,14 @@ static int virtio_gpu_plane_atomic_check(struct drm_plane 
*plane,

        for (i = 0; i < new_plane_state->fb->format->num_planes; ++i) {
                struct drm_gem_object *obj = 
drm_gem_fb_get_obj(new_plane_state->fb, i);
+               struct virtio_gpu_object *bo = gem_to_virtio_gpu_obj(obj);

                /*
                 * Exporters of GEM buffer objects sometimes interfere with the
                 * vblank timer. Mark the plane as externally synchronized if we
                 * find an imported GEM buffer object.
                 */
-               if (drm_gem_is_imported(obj)) {
+               if (bo->guest_blob && !vgdev->has_virgl_3d) {
                        vgcrtc_state->plane_synced_to_ext |= 
drm_plane_mask(plane);
                        break; /* only need to find one */

For testing the 3 cases, I ran Gnome Wayland like this:
XDG_SESSION_TYPE=wayland dbus-run-session -- ./bin/gnome-shell --wayland 
--no-x11 &

and here are the relevant options for Qemu cmd line I tested with:
./qemu-system-x86_64 -m 4096m 
 -device virtio-gpu,max_outputs=1,xres=1920,yres=1080,blob=true
-display gtk,gl=on
-object memory-backend-memfd,id=mem1,size=4096M
-machine q35,accel=kvm,memory-backend=mem1 -usb -device usb-tablet -serial stdio

Thanks,
Vivek

> 
> Reviewed-by: Dmitry Osipenko <[email protected]>
> Tested-by: Dmitry Osipenko <[email protected]>
> 
> --
> Best regards,
> Dmitry

Reply via email to