@@ -15,19 +15,22 @@ use super::protocol::{
1515#[ cfg( target_os = "macos" ) ]
1616use crossbeam_channel:: { unbounded, Sender } ;
1717use krun_display:: {
18- DisplayBackend , DisplayBackendBasicFramebuffer , DisplayBackendInstance , Rect , ResourceFormat ,
18+ DisplayBackend , DisplayBackendBasicFramebuffer , DisplayBackendInstance , DmabufExport , Rect ,
19+ ResourceFormat ,
1920} ;
2021use libc:: c_void;
2122#[ cfg( target_os = "macos" ) ]
2223use rutabaga_gfx:: RUTABAGA_MEM_HANDLE_TYPE_APPLE ;
24+ #[ cfg( target_os = "linux" ) ]
25+ use rutabaga_gfx:: RUTABAGA_MEM_HANDLE_TYPE_DMABUF ;
2326#[ cfg( all( not( feature = "virgl_resource_map2" ) , target_os = "linux" ) ) ]
2427use rutabaga_gfx:: RUTABAGA_MEM_HANDLE_TYPE_OPAQUE_FD ;
2528#[ cfg( all( feature = "virgl_resource_map2" , target_os = "linux" ) ) ]
2629use rutabaga_gfx:: RUTABAGA_MEM_HANDLE_TYPE_SHM ;
2730use rutabaga_gfx:: {
2831 ResourceCreate3D , ResourceCreateBlob , Rutabaga , RutabagaBuilder , RutabagaChannel ,
29- RutabagaFence , RutabagaFenceHandler , RutabagaIovec , Transfer3D , RUTABAGA_CHANNEL_TYPE_WAYLAND ,
30- RUTABAGA_MAP_CACHE_MASK ,
32+ RutabagaFence , RutabagaFenceHandler , RutabagaIntoRawDescriptor , RutabagaIovec , Transfer3D ,
33+ RUTABAGA_CHANNEL_TYPE_WAYLAND , RUTABAGA_MAP_CACHE_MASK ,
3134} ;
3235#[ cfg( target_os = "linux" ) ]
3336use rutabaga_gfx:: {
@@ -116,6 +119,9 @@ struct VirtioGpuResource {
116119 size : u64 , // only for blob resources
117120 shmem_offset : Option < u64 > ,
118121 rutabaga_external_mapping : bool ,
122+ #[ cfg( target_os = "linux" ) ]
123+ // The id under which this resource is imported to the display
124+ display_dmabuf_id : Option < u32 > ,
119125}
120126
121127impl VirtioGpuResource {
@@ -137,12 +143,15 @@ impl VirtioGpuResource {
137143 format,
138144 shmem_offset : None ,
139145 rutabaga_external_mapping : false ,
146+ #[ cfg( target_os = "linux" ) ]
147+ display_dmabuf_id : None ,
140148 }
141149 }
142150}
143151
144152pub struct VirtioGpuScanout {
145153 resource_id : u32 ,
154+ uses_dmabuf : bool ,
146155}
147156
148157pub struct VirtioGpu {
@@ -367,6 +376,14 @@ impl VirtioGpu {
367376 return Err ( ErrUnspec ) ;
368377 }
369378
379+ if self . display_backend . supports_dmabuf ( ) {
380+ if let Some ( display_dmabuf_id) = resource. display_dmabuf_id {
381+ if let Err ( e) = self . display_backend . unref_dmabuf ( display_dmabuf_id) {
382+ warn ! ( "Failed to unref display DMABUF resource, resource_id: {resource_id}:, display_dmabuf_id: {display_dmabuf_id}, error: {e:?}" ) ;
383+ }
384+ }
385+ }
386+
370387 if resource. rutabaga_external_mapping {
371388 self . rutabaga . unmap ( resource_id) ?;
372389 }
@@ -382,26 +399,21 @@ impl VirtioGpu {
382399 width : u32 ,
383400 height : u32 ,
384401 ) -> VirtioGpuResult {
385- let scanout = self
386- . scanouts
387- . get_mut ( scanout_id as usize )
388- . ok_or ( ErrInvalidScanoutId ) ?;
389-
390- // If a resource is already associated with this scanout, make sure to disable
391- // this scanout for that resource
392- if let Some ( resource_id) = scanout. as_ref ( ) . map ( |scanout| scanout. resource_id ) {
393- let resource = self
394- . resources
395- . get_mut ( & resource_id)
396- . ok_or ( ErrInvalidResourceId ) ?;
402+ if scanout_id as usize >= self . scanouts . len ( ) {
403+ return Err ( ErrInvalidScanoutId ) ;
404+ }
397405
398- resource. scanouts . disable ( scanout_id) ;
406+ // If a resource is already associated with this scanout, disable it for that resource
407+ if let Some ( old_scanout) = & self . scanouts [ scanout_id as usize ] {
408+ if let Some ( resource) = self . resources . get_mut ( & old_scanout. resource_id ) {
409+ resource. scanouts . disable ( scanout_id) ;
410+ }
399411 }
400412
401413 // Virtio spec: "The driver can use resource_id = 0 to disable a scanout."
402414 if resource_id == 0 {
403415 debug ! ( "Disabling scanout {scanout_id:?}" ) ;
404- * scanout = None ;
416+ self . scanouts [ scanout_id as usize ] = None ;
405417 self . display_backend . disable_scanout ( scanout_id) ?;
406418 return Ok ( OkNoData ) ;
407419 }
@@ -412,28 +424,163 @@ impl VirtioGpu {
412424 . get_mut ( & resource_id)
413425 . ok_or ( ErrInvalidResourceId ) ?;
414426 resource. scanouts . enable ( scanout_id) ;
427+ let resource_format = resource. format ;
428+
429+ let ( display_width, display_height) = {
430+ let display_info = self
431+ . displays
432+ . get ( scanout_id as usize )
433+ . ok_or ( ErrInvalidScanoutId ) ?;
434+ ( display_info. width , display_info. height )
435+ } ;
436+
437+ // Try dmabuf path first if supported
438+ #[ cfg( target_os = "linux" ) ]
439+ if self . display_backend . supports_dmabuf ( ) {
440+ if let Ok ( dmabuf_scanout) = self . try_configure_dmabuf_scanout (
441+ scanout_id,
442+ resource_id,
443+ display_width,
444+ display_height,
445+ width,
446+ height,
447+ ) {
448+ self . scanouts [ scanout_id as usize ] = Some ( dmabuf_scanout) ;
449+ return Ok ( OkNoData ) ;
450+ }
451+ }
415452
416- let Some ( format) = resource . format else {
417- warn ! ( "Cannot use resource {resource_id} with unknown format for scanout" ) ;
453+ let Some ( format) = resource_format else {
454+ warn ! ( "Cannot use resource {resource_id} with unknown format for basic framebuffer scanout" ) ;
418455 return Err ( ErrUnspec ) ;
419456 } ;
420457
421- let display_info = self
422- . displays
423- . get ( scanout_id as usize )
424- . ok_or ( ErrInvalidScanoutId ) ?;
458+ // Fallback to basic framebuffer
459+ let basic_scanout = self . configure_basic_framebuffer_scanout (
460+ scanout_id,
461+ resource_id,
462+ display_width,
463+ display_height,
464+ width,
465+ height,
466+ format,
467+ ) ?;
468+ self . scanouts [ scanout_id as usize ] = Some ( basic_scanout) ;
469+ Ok ( OkNoData )
470+ }
471+
472+ #[ cfg( target_os = "linux" ) ]
473+ fn try_configure_dmabuf_scanout (
474+ & mut self ,
475+ scanout_id : u32 ,
476+ resource_id : u32 ,
477+ display_width : u32 ,
478+ display_height : u32 ,
479+ _width : u32 ,
480+ _height : u32 ,
481+ ) -> std:: result:: Result < VirtioGpuScanout , ( ) > {
482+ // Check if this resource has already been exported to the display
483+ let resource = self . resources . get_mut ( & resource_id) . ok_or ( ( ) ) ?;
484+ let dmabuf_id = match resource. display_dmabuf_id {
485+ Some ( old_dmabuf_id) => {
486+ trace ! ( "Resource {resource_id} already imported as dmabuf with ID {old_dmabuf_id}" ) ;
487+ old_dmabuf_id
488+ }
489+ None => {
490+ let export = self . rutabaga . export_blob ( resource_id) . map_err ( |e| {
491+ debug ! ( "Failed to export resource {resource_id} as dmabuf: {e}" ) ;
492+ } ) ?;
493+
494+ // Verify that the exported handle is actually a dmabuf
495+ if export. handle_type != RUTABAGA_MEM_HANDLE_TYPE_DMABUF {
496+ debug ! (
497+ "Scanout resource {resource_id} was exported with handle type 0x{:x}, not DMABUF (0x{:x})" ,
498+ export. handle_type, RUTABAGA_MEM_HANDLE_TYPE_DMABUF
499+ ) ;
500+ return Err ( ( ) ) ;
501+ }
425502
503+ let info_3d = self . rutabaga . query ( resource_id) . map_err ( |e| {
504+ debug ! ( "Failed to query resource {resource_id} for dmabuf info: {e}" ) ;
505+ } ) ?;
506+
507+ debug ! ( "Resource {resource_id} dmabuf info: fourcc=0x{:08x}, modifier=0x{:016x}, strides={:?}, offsets={:?}" ,
508+ info_3d. drm_fourcc, info_3d. modifier, info_3d. strides, info_3d. offsets
509+ ) ;
510+
511+ // Currently we only ever have 1 plane, but the display frontend API is generic and
512+ // supports multiple
513+ const NUM_PLANES : u32 = 1 ;
514+ let dmabuf_fd = export. os_handle . into_raw_descriptor ( ) ;
515+ let dmabuf_fds = [ dmabuf_fd, -1 , -1 , -1 ] ;
516+
517+ let dmabuf_export = DmabufExport {
518+ dmabuf_fds,
519+ n_planes : NUM_PLANES ,
520+ width : info_3d. width ,
521+ height : info_3d. height ,
522+ fourcc : info_3d. drm_fourcc ,
523+ strides : info_3d. strides ,
524+ offsets : info_3d. offsets ,
525+ modifier : info_3d. modifier ,
526+ } ;
527+
528+ // Import the dmabuf into the display backend
529+ let dmabuf_id =
530+ self . display_backend
531+ . import_dmabuf ( & dmabuf_export)
532+ . map_err ( |e| {
533+ warn ! ( "Failed to import dmabuf for resource {resource_id}: {e}" ) ;
534+ } ) ?;
535+
536+ debug ! ( "Imported resource {resource_id} as dmabuf with ID {dmabuf_id}" ) ;
537+
538+ // Store the dmabuf ID in the resource
539+ resource. display_dmabuf_id = Some ( dmabuf_id) ;
540+ dmabuf_id
541+ }
542+ } ;
543+
544+ // Configure scanout to use the imported dmabuf (no src_rect for now, use entire dmabuf)
545+ self . display_backend
546+ . configure_scanout_dmabuf ( scanout_id, display_width, display_height, dmabuf_id, None )
547+ . map_err ( |e| {
548+ debug ! ( "Failed to configure dmabuf scanout for resource {resource_id}: {e}" ) ;
549+ } ) ?;
550+
551+ debug ! (
552+ "Successfully configured scanout {scanout_id} with dmabuf for resource {resource_id}"
553+ ) ;
554+ Ok ( VirtioGpuScanout {
555+ resource_id,
556+ uses_dmabuf : true ,
557+ } )
558+ }
559+
560+ #[ allow( clippy:: too_many_arguments) ]
561+ fn configure_basic_framebuffer_scanout (
562+ & mut self ,
563+ scanout_id : u32 ,
564+ resource_id : u32 ,
565+ display_width : u32 ,
566+ display_height : u32 ,
567+ width : u32 ,
568+ height : u32 ,
569+ format : ResourceFormat ,
570+ ) -> std:: result:: Result < VirtioGpuScanout , GpuResponse > {
426571 self . display_backend . configure_scanout (
427572 scanout_id,
428- display_info . width ,
429- display_info . height ,
573+ display_width ,
574+ display_height ,
430575 width,
431576 height,
432577 format,
433578 ) ?;
434579
435- * scanout = Some ( VirtioGpuScanout { resource_id } ) ;
436- Ok ( OkNoData )
580+ Ok ( VirtioGpuScanout {
581+ resource_id,
582+ uses_dmabuf : false ,
583+ } )
437584 }
438585
439586 fn read_2d_resource (
@@ -473,14 +620,35 @@ impl VirtioGpu {
473620 . get ( & resource_id)
474621 . ok_or ( ErrInvalidResourceId ) ?;
475622
623+ #[ cfg( target_os = "linux" ) ]
624+ unsafe {
625+ #[ link( name = "GL" ) ]
626+ extern "C" {
627+ fn glFlush ( ) ;
628+ }
629+
630+ glFlush ( )
631+ } ;
632+
476633 for scanout_id in resource. scanouts . iter_enabled ( ) {
477- let ( frame_id, buffer) = self . display_backend . alloc_frame ( scanout_id) ?;
478- if let Err ( e) = Self :: read_2d_resource ( & mut self . rutabaga , resource, buffer) {
479- log:: error!( "Failed to read resource {resource_id} for scanout {scanout_id}: {e}" ) ;
480- return Err ( ErrUnspec ) ;
634+ if self . scanouts [ scanout_id as usize ]
635+ . as_ref ( )
636+ . unwrap ( )
637+ . uses_dmabuf
638+ {
639+ self . display_backend
640+ . present_dmabuf ( scanout_id, Some ( & rect) ) ?;
641+ } else {
642+ let ( frame_id, buffer) = self . display_backend . alloc_frame ( scanout_id) ?;
643+ if let Err ( e) = Self :: read_2d_resource ( & mut self . rutabaga , resource, buffer) {
644+ log:: error!(
645+ "Failed to read resource {resource_id} for scanout {scanout_id}: {e}"
646+ ) ;
647+ return Err ( ErrUnspec ) ;
648+ }
649+ self . display_backend
650+ . present_frame ( scanout_id, frame_id, Some ( & rect) ) ?
481651 }
482- self . display_backend
483- . present_frame ( scanout_id, frame_id, Some ( & rect) ) ?
484652 }
485653
486654 #[ cfg( windows) ]
0 commit comments