@@ -18,6 +18,7 @@ use crate::cmm::cmm_render_intent::RenderIntent;
1818use crate :: gfx_api:: AcquireSync ;
1919use crate :: gfx_api:: BufferResv ;
2020use crate :: gfx_api:: CopyTexture ;
21+ use crate :: gfx_api:: DirectScanoutError ;
2122use crate :: gfx_api:: DirectScanoutPosition ;
2223use crate :: gfx_api:: GfxRenderPass ;
2324use crate :: gfx_api:: GfxTexture ;
@@ -50,6 +51,7 @@ use arrayvec::ArrayVec;
5051use jay_proc:: jay_hash;
5152use std:: rc:: Rc ;
5253use std:: rc:: Weak ;
54+ use thiserror:: Error ;
5355use uapi:: OwnedFd ;
5456use uapi:: c;
5557
@@ -150,6 +152,30 @@ enum PresentFbWait {
150152 Scanout ,
151153}
152154
155+ #[ derive( Copy , Clone , Debug , PartialEq , Error ) ]
156+ pub enum MetalDirectScanoutError {
157+ #[ error( transparent) ]
158+ GfxApi ( #[ from] DirectScanoutError ) ,
159+ #[ error( "The client buffer has pending lazy work" ) ]
160+ LazyWork ,
161+ #[ error( "Cross-device direct scanout is not supported" ) ]
162+ CrossDevice ,
163+ #[ error( "Shm buffers cannot be scanned out" ) ]
164+ Shm ,
165+ #[ error( "The buffer is already marked as impossible" ) ]
166+ MarkedImpossible ,
167+ #[ error( "The buffer could not imported as a framebuffer" ) ]
168+ ImportFb ,
169+ #[ error( "The buffer format is not supported" ) ]
170+ UnsupportedFormat ,
171+ #[ error( "The modifier is not supported" ) ]
172+ UnsupportedModifier ,
173+ #[ error( "There is no suitable color management programming" ) ]
174+ NoCmProgramming ,
175+ #[ error( "The commit failed" ) ]
176+ Commit ,
177+ }
178+
153179impl MetalConnector {
154180 pub fn schedule_present ( & self ) {
155181 self . present_trigger . trigger ( ) ;
@@ -308,7 +334,7 @@ impl MetalConnector {
308334 & mut changed_planes,
309335 & mut connector_drm_state,
310336 ) ;
311- if res . is_err ( )
337+ if let Err ( error ) = & res
312338 && let Some ( dsk) = direct_scanout_key
313339 {
314340 let fb = self . prepare_present_fb (
@@ -324,7 +350,7 @@ impl MetalConnector {
324350 present_fb = Some ( fb) ;
325351 self . await_present_fb ( present_fb. as_mut ( ) , PresentFbWait :: Scanout )
326352 . await ;
327- res = self . program_connector (
353+ let new_res = self . program_connector (
328354 version,
329355 & crtc,
330356 & plane,
@@ -333,9 +359,14 @@ impl MetalConnector {
333359 & mut changed_planes,
334360 & mut connector_drm_state,
335361 ) ;
336- if res . is_ok ( ) {
362+ if new_res . is_ok ( ) {
337363 self . scanout_impossible_cache . insert ( dsk, ( ) ) ;
364+ let log = self . handle_direct_scanout_error ( MetalDirectScanoutError :: Commit ) ;
365+ if log {
366+ log:: debug!( "Commit error: {}" , ErrorFmt ( error) ) ;
367+ }
338368 }
369+ res = new_res;
339370 }
340371 let reset_damage = || {
341372 for buffer in & * buffers {
@@ -369,6 +400,7 @@ impl MetalConnector {
369400 buffer. damage_queue . clear ( ) ;
370401 } else {
371402 reset_damage ( ) ;
403+ self . last_direct_scanout_error . take ( ) ;
372404 }
373405 buffer. locked . set ( fb. core . locked ) ;
374406 self . cm . apply ( & fb. cm_programming ) ;
@@ -696,7 +728,7 @@ impl MetalConnector {
696728 blend_cd : & Rc < ColorDescription > ,
697729 gamma_lut : Option < & Rc < BackendGammaLut > > ,
698730 cd : & Rc < ColorDescription > ,
699- ) -> Option < ( DirectScanoutData , DirectScanoutDataCore ) > {
731+ ) -> Result < ( DirectScanoutData , DirectScanoutDataCore ) , MetalDirectScanoutError > {
700732 let ( ct, position) = pass. prepare_direct_scanout (
701733 plane. mode_w . get ( ) ,
702734 plane. mode_h . get ( ) ,
@@ -719,7 +751,7 @@ impl MetalConnector {
719751 // if there is lazy work, the tex dmabuf is not up-to-date. going into
720752 // the compositing path ensures that it's up-to-date on the next
721753 // frame.
722- return None ;
754+ return Err ( MetalDirectScanoutError :: LazyWork ) ;
723755 }
724756 fb_resv = None ;
725757 dmabuf = ct. tex . dmabuf ( ) ;
@@ -731,12 +763,12 @@ impl MetalConnector {
731763 // until the FB is no longer being scanned out, but if a notification pops up
732764 // then we must be able to disable direct scanout immediately.
733765 // https://gitlab.freedesktop.org/drm/amd/-/issues/3186
734- return None ;
766+ return Err ( MetalDirectScanoutError :: CrossDevice ) ;
735767 }
736768 } ;
737769 let Some ( dmabuf) = dmabuf else {
738770 // Shm buffers cannot be scanned out.
739- return None ;
771+ return Err ( MetalDirectScanoutError :: Shm ) ;
740772 } ;
741773 let key = DirectScanoutKey {
742774 dma_buf_id : dmabuf. id ,
@@ -751,10 +783,10 @@ impl MetalConnector {
751783 } ;
752784 if let Some ( v) = self . scanout_impossible_cache . get ( & key) {
753785 v. mark_used ( ) ;
754- return None ;
786+ return Err ( MetalDirectScanoutError :: MarkedImpossible ) ;
755787 }
756788 let res = self . prepare_direct_scanout2 ( plane, crtc, gamma_lut, cd, ct, dmabuf) ;
757- if res. is_none ( ) {
789+ if res. is_err ( ) {
758790 self . scanout_impossible_cache . insert ( key, ( ) ) ;
759791 }
760792 res. map ( |( fb, cm_programming) | {
@@ -786,30 +818,33 @@ impl MetalConnector {
786818 cd : & Rc < ColorDescription > ,
787819 ct : & CopyTexture ,
788820 dmabuf : & Rc < DmaBuf > ,
789- ) -> Option < ( Rc < DrmFramebuffer > , MetalCmProgramming ) > {
821+ ) -> Result < ( Rc < DrmFramebuffer > , MetalCmProgramming ) , MetalDirectScanoutError > {
790822 let fb = self . prepare_direct_scanout3 ( plane, dmabuf) ?;
791- let cm_programming = self . cm . find_programming (
792- & self . master ,
793- plane,
794- crtc,
795- & ct. cd ,
796- cd,
797- ct. render_intent ,
798- gamma_lut,
799- self . cursor_enabled . get ( ) ,
800- self . dev . use_plane_color_pipelines . get ( ) ,
801- ) ?;
802- Some ( ( fb, cm_programming) )
823+ let cm_programming = self
824+ . cm
825+ . find_programming (
826+ & self . master ,
827+ plane,
828+ crtc,
829+ & ct. cd ,
830+ cd,
831+ ct. render_intent ,
832+ gamma_lut,
833+ self . cursor_enabled . get ( ) ,
834+ self . dev . use_plane_color_pipelines . get ( ) ,
835+ )
836+ . ok_or ( MetalDirectScanoutError :: NoCmProgramming ) ?;
837+ Ok ( ( fb, cm_programming) )
803838 }
804839
805840 fn prepare_direct_scanout3 (
806841 & self ,
807842 plane : & Rc < MetalPlane > ,
808843 dmabuf : & Rc < DmaBuf > ,
809- ) -> Option < Rc < DrmFramebuffer > > {
844+ ) -> Result < Rc < DrmFramebuffer > , MetalDirectScanoutError > {
810845 let mut cache = self . scanout_buffers . borrow_mut ( ) ;
811846 if let Some ( buffer) = cache. get ( & dmabuf. id ) {
812- return buffer. fb . clone ( ) ;
847+ return buffer. fb . clone ( ) . ok_or ( MetalDirectScanoutError :: ImportFb ) ;
813848 }
814849 let format = ' format: {
815850 if let Some ( f) = plane. formats . get ( & dmabuf. format . drm ) {
@@ -821,10 +856,10 @@ impl MetalConnector {
821856 {
822857 break ' format f;
823858 }
824- return None ;
859+ return Err ( MetalDirectScanoutError :: UnsupportedFormat ) ;
825860 } ;
826861 if !format. modifiers . contains ( & dmabuf. modifier ) {
827- return None ;
862+ return Err ( MetalDirectScanoutError :: UnsupportedModifier ) ;
828863 }
829864 let fb = match self . dev . master . add_fb ( dmabuf, Some ( format. format ) ) {
830865 Ok ( fb) => Some ( Rc :: new ( fb) ) ,
@@ -843,7 +878,7 @@ impl MetalConnector {
843878 fb : fb. clone ( ) ,
844879 } ,
845880 ) ;
846- fb
881+ fb. ok_or ( MetalDirectScanoutError :: ImportFb )
847882 }
848883
849884 fn prepare_present_fb (
@@ -861,8 +896,12 @@ impl MetalConnector {
861896 let try_direct_scanout = try_direct_scanout && self . dev . direct_scanout_enabled ( ) ;
862897 let mut direct_scanout_data = None ;
863898 if try_direct_scanout {
864- direct_scanout_data =
865- self . prepare_direct_scanout ( & latched. pass , plane, crtc, blend_cd, gamma_lut, cd) ;
899+ direct_scanout_data = self
900+ . prepare_direct_scanout ( & latched. pass , plane, crtc, blend_cd, gamma_lut, cd)
901+ . inspect_err ( |e| {
902+ self . handle_direct_scanout_error ( * e) ;
903+ } )
904+ . ok ( ) ;
866905 }
867906 let direct_scanout_active = direct_scanout_data. is_some ( ) ;
868907 if self . direct_scanout_active . replace ( direct_scanout_active) != direct_scanout_active {
@@ -996,4 +1035,13 @@ impl MetalConnector {
9961035 }
9971036 }
9981037 }
1038+
1039+ fn handle_direct_scanout_error ( & self , e : MetalDirectScanoutError ) -> bool {
1040+ let some = Some ( e) ;
1041+ if self . last_direct_scanout_error . replace ( some) == some {
1042+ return false ;
1043+ }
1044+ log:: debug!( "Cannot perform direct scanout: {}" , ErrorFmt ( e) ) ;
1045+ true
1046+ }
9991047}
0 commit comments