1+ #![ no_std]
2+
3+ use spirv_std:: spirv;
4+ use spirv_std:: glam:: { UVec3 , Vec3 , Vec4 , Mat4 , Vec4Swizzles } ;
5+ use spirv_std:: arch:: atomic_i_add;
6+
7+ #[ repr( C ) ]
8+ #[ derive( Copy , Clone ) ]
9+ pub struct InstanceData {
10+ pub pos : [ f32 ; 3 ] ,
11+ pub scale : f32 ,
12+ }
13+
14+ #[ repr( C ) ]
15+ #[ derive( Copy , Clone ) ]
16+ pub struct IndexedIndirectCommand {
17+ pub index_count : u32 ,
18+ pub instance_count : u32 ,
19+ pub first_index : u32 ,
20+ pub vertex_offset : u32 ,
21+ pub first_instance : u32 ,
22+ }
23+
24+ #[ repr( C ) ]
25+ #[ derive( Copy , Clone ) ]
26+ pub struct UBO {
27+ pub projection : Mat4 ,
28+ pub modelview : Mat4 ,
29+ pub camera_pos : Vec4 ,
30+ pub frustum_planes : [ Vec4 ; 6 ] ,
31+ }
32+
33+ #[ repr( C ) ]
34+ #[ derive( Copy , Clone ) ]
35+ pub struct UBOOut {
36+ pub draw_count : i32 ,
37+ pub lod_count : [ i32 ; 6 ] , // MAX_LOD_LEVEL + 1
38+ }
39+
40+ #[ repr( C ) ]
41+ #[ derive( Copy , Clone ) ]
42+ pub struct LOD {
43+ pub first_index : u32 ,
44+ pub index_count : u32 ,
45+ pub distance : f32 ,
46+ pub _pad0 : f32 ,
47+ }
48+
49+ fn frustum_check ( pos : Vec4 , radius : f32 , frustum_planes : & [ Vec4 ; 6 ] ) -> bool {
50+ for i in 0 ..6 {
51+ if pos. dot ( frustum_planes[ i] ) + radius < 0.0 {
52+ return false ;
53+ }
54+ }
55+ true
56+ }
57+
58+ #[ spirv( compute( threads( 16 ) ) ) ]
59+ pub fn main_cs (
60+ #[ spirv( global_invocation_id) ] global_id : UVec3 ,
61+ #[ spirv( storage_buffer, descriptor_set = 0 , binding = 0 ) ] instances : & [ InstanceData ] ,
62+ #[ spirv( storage_buffer, descriptor_set = 0 , binding = 1 ) ] indirect_draws : & mut [ IndexedIndirectCommand ] ,
63+ #[ spirv( uniform, descriptor_set = 0 , binding = 2 ) ] ubo : & UBO ,
64+ #[ spirv( storage_buffer, descriptor_set = 0 , binding = 3 ) ] ubo_out : & mut UBOOut ,
65+ #[ spirv( storage_buffer, descriptor_set = 0 , binding = 4 ) ] lods : & [ LOD ] ,
66+ #[ spirv( spec_constant( id = 0 , default = 5 ) ) ] max_lod_level : u32 ,
67+ ) {
68+ let idx = global_id. x as usize ;
69+
70+ // Bounds check - important!
71+ if idx >= instances. len ( ) || idx >= indirect_draws. len ( ) {
72+ return ;
73+ }
74+
75+ let pos = Vec4 :: new ( instances[ idx] . pos [ 0 ] , instances[ idx] . pos [ 1 ] , instances[ idx] . pos [ 2 ] , 1.0 ) ;
76+
77+ // Check if object is within current viewing frustum
78+ if frustum_check ( pos, 1.0 , & ubo. frustum_planes ) {
79+ indirect_draws[ idx] . instance_count = 1 ;
80+
81+ // Increase number of indirect draw counts
82+ unsafe {
83+ atomic_i_add :: < i32 , { spirv_std:: memory:: Scope :: Device as u32 } , { spirv_std:: memory:: Semantics :: NONE . bits ( ) } > (
84+ & mut ubo_out. draw_count ,
85+ 1
86+ ) ;
87+ }
88+
89+ // Select appropriate LOD level based on distance to camera
90+ let mut lod_level = max_lod_level;
91+ let camera_pos_vec3 = ubo. camera_pos . xyz ( ) ;
92+ let instance_pos = Vec3 :: new ( instances[ idx] . pos [ 0 ] , instances[ idx] . pos [ 1 ] , instances[ idx] . pos [ 2 ] ) ;
93+ let dist = instance_pos. distance ( camera_pos_vec3) ;
94+ for i in 0 ..max_lod_level {
95+ if dist < lods[ i as usize ] . distance {
96+ lod_level = i;
97+ break ;
98+ }
99+ }
100+ indirect_draws[ idx] . first_index = lods[ lod_level as usize ] . first_index ;
101+ indirect_draws[ idx] . index_count = lods[ lod_level as usize ] . index_count ;
102+
103+ // Update stats
104+ unsafe {
105+ atomic_i_add :: < i32 , { spirv_std:: memory:: Scope :: Device as u32 } , { spirv_std:: memory:: Semantics :: NONE . bits ( ) } > (
106+ & mut ubo_out. lod_count [ lod_level as usize ] ,
107+ 1
108+ ) ;
109+ }
110+ } else {
111+ indirect_draws[ idx] . instance_count = 0 ;
112+ }
113+ }
0 commit comments