@@ -14,6 +14,20 @@ use super::{
1414use std:: sync:: OnceLock ;
1515
1616pub const GPU_DRIVEN_FEATURES : wgpu:: Features = wgpu:: Features :: INDIRECT_FIRST_INSTANCE ;
17+ pub ( crate ) const DRAW_FLAG_DOUBLE_SIDED : u32 = 1 << 0 ;
18+ pub ( crate ) const DRAW_FLAG_VISIBILITY_ELIGIBLE : u32 = 1 << 1 ;
19+
20+ pub ( crate ) const fn draw_flags ( double_sided : bool , visibility_eligible : bool ) -> u32 {
21+ ( if double_sided {
22+ DRAW_FLAG_DOUBLE_SIDED
23+ } else {
24+ 0
25+ } ) | if visibility_eligible {
26+ DRAW_FLAG_VISIBILITY_ELIGIBLE
27+ } else {
28+ 0
29+ }
30+ }
1731/// Below this count, compute/indirect setup costs more than the CPU loop on
1832/// current Metal hardware. Keep small scenes on the lower-overhead oracle.
1933pub const GPU_DRIVEN_MIN_DRAWS : usize = 32 ;
@@ -29,6 +43,7 @@ pub fn request_features_if_supported(supported: wgpu::Features, required: &mut w
2943 if supported. contains ( wgpu:: Features :: MULTI_DRAW_INDIRECT_COUNT ) {
3044 * required |= wgpu:: Features :: MULTI_DRAW_INDIRECT_COUNT ;
3145 }
46+ super :: visibility_buffer:: request_feature_if_supported ( supported, required) ;
3247}
3348
3449#[ derive( Copy , Clone , Debug , Default , Eq , PartialEq ) ]
@@ -71,6 +86,7 @@ struct GeometryArena {
7186 free_indices : Vec < FreeRange > ,
7287 retired : Vec < RetiredGeometry > ,
7388 completion : GpuCompletionTracker ,
89+ generation : u64 ,
7490}
7591
7692impl GeometryArena {
@@ -88,6 +104,7 @@ impl GeometryArena {
88104 free_indices : Vec :: new ( ) ,
89105 retired : Vec :: new ( ) ,
90106 completion : GpuCompletionTracker :: default ( ) ,
107+ generation : 0 ,
91108 }
92109 }
93110
@@ -212,6 +229,7 @@ impl GeometryArena {
212229 self . index = next;
213230 self . index_capacity = new_index_capacity;
214231 }
232+ self . generation = self . generation . wrapping_add ( 1 ) ;
215233 queue. submit ( std:: iter:: once ( encoder. finish ( ) ) ) ;
216234 }
217235}
@@ -347,6 +365,7 @@ pub struct GpuDrivenRenderer {
347365 depth_pipeline : Option < wgpu:: RenderPipeline > ,
348366 main_pipeline : Option < wgpu:: RenderPipeline > ,
349367 main_prepassed_pipeline : Option < wgpu:: RenderPipeline > ,
368+ visibility : super :: visibility_buffer:: VisibilityBufferRuntime ,
350369 pub ( super ) draw_scratch : Vec < GpuDrawRecord > ,
351370 pub stats : SubmissionStats ,
352371}
@@ -464,6 +483,18 @@ impl GpuDrivenRenderer {
464483 ( None , None , None , None )
465484 } ;
466485
486+ let visibility_source = ( super :: visibility_buffer:: requested_mode ( ) . requested ( ) && enabled)
487+ . then ( || make_gpu_scene_shader ( scene_source) ) ;
488+ let visibility = super :: visibility_buffer:: VisibilityBufferRuntime :: new (
489+ device,
490+ enabled,
491+ & draw_layout,
492+ lighting_layout,
493+ global_material_layout,
494+ joint_layout,
495+ visibility_source. as_deref ( ) ,
496+ ) ;
497+
467498 let renderer = Self {
468499 arena : GeometryArena :: new ( device) ,
469500 enabled,
@@ -481,6 +512,7 @@ impl GpuDrivenRenderer {
481512 depth_pipeline,
482513 main_pipeline,
483514 main_prepassed_pipeline,
515+ visibility,
484516 draw_scratch : Vec :: with_capacity ( draw_capacity) ,
485517 stats : SubmissionStats :: default ( ) ,
486518 } ;
@@ -592,6 +624,7 @@ impl GpuDrivenRenderer {
592624 frustum_visible_oracle : u32 ,
593625 frustum_culled_oracle : u32 ,
594626 ) {
627+ self . visibility . begin_frame ( ) ;
595628 self . stats = SubmissionStats {
596629 submitted : self . draw_scratch . len ( ) as u32 ,
597630 compatibility : compatibility_count,
@@ -647,7 +680,8 @@ impl GpuDrivenRenderer {
647680 "\" frustum_visible_oracle\" :{},\" frustum_culled_oracle\" :{}," ,
648681 "\" frustum_culled_ratio\" :{:.6}," ,
649682 "\" classification_source\" :\" retained-scene conservative CPU oracle\" ," ,
650- "\" visibility_buffer_contract\" :{}}}"
683+ "\" visibility_buffer_contract\" :{}," ,
684+ "\" visibility_buffer_runtime\" :{}}}"
651685 ) ,
652686 self . enabled,
653687 self . count_supported,
@@ -658,6 +692,7 @@ impl GpuDrivenRenderer {
658692 self . stats. frustum_culled_oracle,
659693 culled_ratio,
660694 super :: visibility_buffer:: contract_json( ) ,
695+ self . visibility. report_json( ) ,
661696 )
662697 }
663698
@@ -701,6 +736,100 @@ impl GpuDrivenRenderer {
701736 self . bind_and_draw ( pass, lighting, global_materials, joints) ;
702737 }
703738
739+ pub ( crate ) fn visibility_diagnostic_enabled ( & self ) -> bool {
740+ self . visibility . enabled ( )
741+ }
742+
743+ #[ allow( clippy:: too_many_arguments) ]
744+ pub ( crate ) fn record_visibility_diagnostic (
745+ & mut self ,
746+ device : & wgpu:: Device ,
747+ encoder : & mut wgpu:: CommandEncoder ,
748+ profiler : & mut crate :: profiler:: Profiler ,
749+ depth_view : & wgpu:: TextureView ,
750+ lighting : & wgpu:: BindGroup ,
751+ global_materials : & wgpu:: BindGroup ,
752+ joints : & wgpu:: BindGroup ,
753+ extent : ( u32 , u32 ) ,
754+ ) -> super :: visibility_buffer:: ResourceCreations {
755+ if !self . visibility . enabled ( ) {
756+ return super :: visibility_buffer:: ResourceCreations :: default ( ) ;
757+ }
758+ if self . draw_scratch . is_empty ( ) {
759+ self . visibility . set_draw_counts ( 0 , self . stats . compatibility ) ;
760+ return super :: visibility_buffer:: ResourceCreations :: default ( ) ;
761+ }
762+ let eligible = self
763+ . draw_scratch
764+ . iter ( )
765+ . filter ( |draw| {
766+ bitcast_draw_flags ( draw. bounds_min [ 3 ] ) & DRAW_FLAG_VISIBILITY_ELIGIBLE != 0
767+ } )
768+ . count ( ) as u32 ;
769+ let compatibility = self
770+ . stats
771+ . compatibility
772+ . saturating_add ( self . draw_scratch . len ( ) as u32 - eligible) ;
773+ self . visibility . set_draw_counts ( eligible, compatibility) ;
774+ if eligible == 0 {
775+ return super :: visibility_buffer:: ResourceCreations :: default ( ) ;
776+ }
777+ let creations = self . visibility . ensure_resources (
778+ device,
779+ extent,
780+ & self . arena . vertex ,
781+ & self . arena . index ,
782+ & self . draw_buffer ,
783+ self . draw_capacity ,
784+ self . arena . generation ,
785+ ) ;
786+ profiler. begin ( "visibility_raster_pass" ) ;
787+ {
788+ let timestamps = profiler. pass_timestamp_writes ( "visibility_raster_pass" ) ;
789+ self . visibility . record_raster (
790+ encoder,
791+ depth_view,
792+ & self . draw_bind_group ,
793+ lighting,
794+ global_materials,
795+ joints,
796+ & self . arena . vertex ,
797+ & self . arena . index ,
798+ & self . indirect_buffer ,
799+ & self . counter_buffer ,
800+ self . draw_scratch . len ( ) as u32 ,
801+ self . count_supported ,
802+ timestamps,
803+ ) ;
804+ }
805+ profiler. end ( "visibility_raster_pass" ) ;
806+ profiler. begin ( "visibility_reconstruct_pass" ) ;
807+ {
808+ let timestamps = profiler. compute_pass_timestamp_writes ( "visibility_reconstruct_pass" ) ;
809+ self . visibility . record_reconstruct ( encoder, timestamps) ;
810+ }
811+ profiler. end ( "visibility_reconstruct_pass" ) ;
812+ creations
813+ }
814+
815+ pub ( crate ) fn record_visibility_debug_overlay (
816+ & self ,
817+ encoder : & mut wgpu:: CommandEncoder ,
818+ profiler : & mut crate :: profiler:: Profiler ,
819+ hdr_view : & wgpu:: TextureView ,
820+ ) {
821+ if !self . visibility . debug_overlay_enabled ( ) {
822+ return ;
823+ }
824+ profiler. begin ( "visibility_debug_overlay" ) ;
825+ {
826+ let timestamps = profiler. pass_timestamp_writes ( "visibility_debug_overlay" ) ;
827+ self . visibility
828+ . record_debug_overlay ( encoder, hdr_view, timestamps) ;
829+ }
830+ profiler. end ( "visibility_debug_overlay" ) ;
831+ }
832+
704833 fn bind_and_draw < ' a > (
705834 & ' a self ,
706835 pass : & mut wgpu:: RenderPass < ' a > ,
@@ -751,6 +880,10 @@ impl GpuDrivenRenderer {
751880 }
752881}
753882
883+ fn bitcast_draw_flags ( value : f32 ) -> u32 {
884+ value. to_bits ( )
885+ }
886+
754887impl Renderer {
755888 /// Prepare retained scene resources through both the compatibility and
756889 /// GPU-driven paths. Keeping this borrow split inside Renderer lets the
@@ -1102,7 +1235,7 @@ fn create_main_pipeline(
11021235 } )
11031236}
11041237
1105- fn make_gpu_scene_shader ( source : & str ) -> String {
1238+ pub ( super ) fn make_gpu_scene_shader ( source : & str ) -> String {
11061239 const LEGACY_MATERIALS : & str = r#"@group(2) @binding(0) var base_color_tex: texture_2d<f32>;
11071240@group(2) @binding(1) var base_color_samp: sampler;
11081241@group(2) @binding(2) var normal_tex: texture_2d<f32>;
@@ -1332,6 +1465,12 @@ mod tests {
13321465 std:: mem:: size_of:: <wgpu:: util:: DrawIndexedIndirectArgs >( ) ,
13331466 20
13341467 ) ;
1468+ assert_eq ! ( draw_flags( false , false ) , 0 ) ;
1469+ assert_eq ! ( draw_flags( true , false ) , DRAW_FLAG_DOUBLE_SIDED ) ;
1470+ assert_eq ! (
1471+ draw_flags( true , true ) ,
1472+ DRAW_FLAG_DOUBLE_SIDED | DRAW_FLAG_VISIBILITY_ELIGIBLE
1473+ ) ;
13351474 }
13361475
13371476 #[ test]
0 commit comments