17import bevy_render::occlusion_culling::mesh_preprocess_types::{ 18 IndirectParametersMetadata, MeshInput, PreviousMeshInput, PreprocessWorkItem 20import package::render::mesh_types::{ 21 Mesh, MESH_FLAGS_AABB_BASED_VISIBILITY_RANGE_BIT, MESH_FLAGS_NO_FRUSTUM_CULLING_BIT, 22 MESH_FLAGS_VISIBILITY_RANGE_INDEX_BITS 24import package::render::mesh_view_bindings::view; 25import package::render::occlusion_culling; 26import package::prepass::bindings::previous_view_uniforms; 27import package::render::view_transformations::{ 28 position_world_to_ndc, position_world_to_view, ndc_to_uv, view_z_to_depth_ndc, 29 position_world_to_prev_ndc, position_world_to_prev_view, prev_view_z_to_depth_ndc 31import bevy_render::maths; 32import bevy_render::view::View; 37struct MeshCullingData { 40 aabb_center: vec4<f32>, 43 aabb_half_extents: vec4<f32>, 48struct LatePreprocessWorkItemIndirectParameters { 60 dispatch_x: atomic<u32>, 69 work_item_count: atomic<u32>, 82 late_preprocess_work_item_indirect_offset: u32, 86@group(0) @binding(3) var<storage> current_input: array<MeshInput>; 88@group(0) @binding(4) var<storage> previous_input: array<PreviousMeshInput>; 92@group(0) @binding(5) var<storage> work_items: array<PreprocessWorkItem>; 94@group(0) @binding(6) var<storage, read_write> output: array<Mesh>; 98@group(0) @binding(7) var<storage, read_write> indirect_parameters_metadata: 99 array<IndirectParametersMetadata>; 101@if(FRUSTUM_CULLING) { 105@group(0) @binding(9) var<storage> mesh_culling_data: array<MeshCullingData>; 107@group(0) @binding(10) var<storage> visibility_ranges: array<vec4<f32>>; 110@if(OCCLUSION_CULLING) 111@group(0) @binding(11) var depth_pyramid: texture_2d<f32>; 113@if(OCCLUSION_CULLING && EARLY_PHASE) { 114@group(0) @binding(12) var<storage, read_write> late_preprocess_work_items: 115 array<PreprocessWorkItem>; 117@group(0) @binding(13) var<storage, read_write> late_preprocess_work_item_indirect_parameters: 118 array<LatePreprocessWorkItemIndirectParameters>; 121@if(OCCLUSION_CULLING && LATE_PHASE) 122@group(0) @binding(13) var<storage, read> late_preprocess_work_item_indirect_parameters: 123 array<LatePreprocessWorkItemIndirectParameters>; 125@if(OCCLUSION_CULLING) 126var<immediate> immediates: Immediates; 132fn view_frustum_intersects_obb( 133 world_from_local: mat4x4<f32>, 134 aabb_center: vec4<f32>, 135 aabb_half_extents: vec3<f32>, 138 for (var i = 0; i < 5; i += 1) { 140 let plane_normal = view.frustum[i]; 141 let relative_radius = dot( 144 dot(plane_normal.xyz, world_from_local[0].xyz), 145 dot(plane_normal.xyz, world_from_local[1].xyz), 146 dot(plane_normal.xyz, world_from_local[2].xyz), 153 if (!maths::sphere_intersects_plane_half_space( 154 plane_normal, aabb_center, relative_radius)) { 164fn main(@builtin(global_invocation_id) global_invocation_id: vec3<u32>) { 167 let instance_index = global_invocation_id.x; 170 if (instance_index >= late_preprocess_work_item_indirect_parameters[ 171 immediates.late_preprocess_work_item_indirect_offset].work_item_count) { 175 if (instance_index >= arrayLength(&work_items)) { 180 let input_index = work_items[instance_index].input_index; 182 let indirect_parameters_index = work_items[instance_index].output_or_indirect_parameters_index; 187@if(INDIRECT && !LATE_PHASE) 188 if (instance_index == 0u) || (work_items[instance_index - 1].output_or_indirect_parameters_index != indirect_parameters_index) { 189 indirect_parameters_metadata[indirect_parameters_index].mesh_index = input_index; 193 let mesh_output_index = work_items[instance_index].output_or_indirect_parameters_index; 196 let world_from_local_affine_transpose = current_input[input_index].world_from_local; 197 let world_from_local = maths::affine3_to_square(world_from_local_affine_transpose); 199@if(FRUSTUM_CULLING) { 201 if ((current_input[input_index].flags & MESH_FLAGS_NO_FRUSTUM_CULLING_BIT) == 0u) { 202 let aabb_center = mesh_culling_data[input_index].aabb_center.xyz; 203 let aabb_half_extents = mesh_culling_data[input_index].aabb_half_extents.xyz; 206 let model_center = world_from_local * vec4(aabb_center, 1.0); 207 if (!view_frustum_intersects_obb(world_from_local, model_center, aabb_half_extents)) { 213 let visibility_buffer_array_len = arrayLength(&visibility_ranges); 214 let visibility_buffer_index = 215 current_input[input_index].flags & MESH_FLAGS_VISIBILITY_RANGE_INDEX_BITS; 216 if (visibility_buffer_index < visibility_buffer_array_len) { 217 let lod_range = visibility_ranges[visibility_buffer_index]; 221 var world_pos: vec3<f32>; 222 if ((current_input[input_index].flags & MESH_FLAGS_AABB_BASED_VISIBILITY_RANGE_BIT) != 0u) { 223 let aabb_center = mesh_culling_data[input_index].aabb_center.xyz; 224 world_pos = (world_from_local * vec4(aabb_center, 1.0)).xyz; 226 world_pos = world_from_local[3].xyz; 229 let camera_distance = length(world_pos - view.lod_view_world_position); 231 if (camera_distance < lod_range.x || camera_distance >= lod_range.w) { 242 let timestamp = current_input[input_index].timestamp; 243 let mesh_changed_this_frame = timestamp == view.frame_count; 246 let previous_input_index = current_input[input_index].previous_input_index; 247 var previous_world_from_local_affine_transpose: mat3x4<f32>; 248 if (mesh_changed_this_frame && previous_input_index != 0xffffffffu) { 249 previous_world_from_local_affine_transpose = 250 previous_input[previous_input_index].world_from_local; 252 previous_world_from_local_affine_transpose = world_from_local_affine_transpose; 254 let previous_world_from_local = 255 maths::affine3_to_square(previous_world_from_local_affine_transpose); 263@if(OCCLUSION_CULLING) { 264 let aabb_center = mesh_culling_data[input_index].aabb_center.xyz; 265 let aabb_half_extents = mesh_culling_data[input_index].aabb_half_extents.xyz; 268 let infinity = bitcast<f32>(0x7f800000u); 269 let neg_infinity = bitcast<f32>(0xff800000u); 270 var aabb = vec4(infinity, infinity, neg_infinity, neg_infinity); 271 var max_depth_view = neg_infinity; 275 for (var i = 0u; i < 8u; i += 1u) { 276 let local_pos = aabb_center + select( 279 vec3((i & 1) != 0, (i & 2) != 0, (i & 4) != 0) 280 ) * aabb_half_extents; 285 let prev_world_pos = (previous_world_from_local * vec4(local_pos, 1.0)).xyz; 287 let view_pos = position_world_to_prev_view(prev_world_pos); 289 let ndc_pos = position_world_to_prev_ndc(prev_world_pos); 293 let world_pos = (world_from_local * vec4(local_pos, 1.0)).xyz; 295 let view_pos = position_world_to_view(world_pos); 297 let ndc_pos = position_world_to_ndc(world_pos); 299 let uv_pos = ndc_to_uv(ndc_pos.xy); 302 aabb = vec4(min(aabb.xy, uv_pos), max(aabb.zw, uv_pos)); 303 max_depth_view = max(max_depth_view, view_pos.z); 308 max_depth_view = min(-previous_view_uniforms.clip_from_view[3][2], max_depth_view); 310 max_depth_view = min(-view.clip_from_view[3][2], max_depth_view); 314 let aabb_pixel_size = occlusion_culling::get_aabb_size_in_pixels(aabb, depth_pyramid); 315 let occluder_depth_ndc = 316 occlusion_culling::get_occluder_depth(aabb, aabb_pixel_size, depth_pyramid); 319 let max_depth_ndc = prev_view_z_to_depth_ndc(max_depth_view); 321 let max_depth_ndc = view_z_to_depth_ndc(max_depth_view); 324 if (max_depth_ndc < occluder_depth_ndc) { 330 let output_work_item_index = atomicAdd(&late_preprocess_work_item_indirect_parameters[ 331 immediates.late_preprocess_work_item_indirect_offset].work_item_count, 1u); 332 if (output_work_item_index % 64u == 0u) { 337 atomicAdd(&late_preprocess_work_item_indirect_parameters[ 338 immediates.late_preprocess_work_item_indirect_offset].dispatch_x, 1u); 342 late_preprocess_work_items[output_work_item_index].input_index = input_index; 343 late_preprocess_work_items[output_work_item_index].output_or_indirect_parameters_index = 344 indirect_parameters_index; 352 let local_from_world_transpose = transpose(maths::inverse_affine3(transpose( 353 world_from_local_affine_transpose))); 356 let local_from_world_transpose_a = mat2x4<f32>( 357 vec4<f32>(local_from_world_transpose[0].xyz, local_from_world_transpose[1].x), 358 vec4<f32>(local_from_world_transpose[1].yz, local_from_world_transpose[2].xy)); 359 let local_from_world_transpose_b = local_from_world_transpose[2].z; 365@if(INDIRECT && LATE_PHASE) 366 let batch_output_index = atomicLoad( 367 &indirect_parameters_metadata[indirect_parameters_index].early_instance_count 369 &indirect_parameters_metadata[indirect_parameters_index].late_instance_count, 372@if(INDIRECT && !LATE_PHASE) 373 let batch_output_index = atomicAdd( 374 &indirect_parameters_metadata[indirect_parameters_index].early_instance_count, 379 let mesh_output_index = 380 indirect_parameters_metadata[indirect_parameters_index].base_output_index + 384 output[mesh_output_index].world_from_local = world_from_local_affine_transpose; 385 output[mesh_output_index].previous_world_from_local = 386 previous_world_from_local_affine_transpose; 387 output[mesh_output_index].local_from_world_transpose_a = local_from_world_transpose_a; 388 output[mesh_output_index].local_from_world_transpose_b = local_from_world_transpose_b; 389 output[mesh_output_index].flags = current_input[input_index].flags; 390 output[mesh_output_index].lightmap_uv_rect = current_input[input_index].lightmap_uv_rect; 391 output[mesh_output_index].first_vertex_index = current_input[input_index].first_vertex_index; 392 output[mesh_output_index].current_skin_index = current_input[input_index].current_skin_index; 393 output[mesh_output_index].material_and_lightmap_bind_group_slot = 394 current_input[input_index].material_and_lightmap_bind_group_slot; 395 output[mesh_output_index].tag = current_input[input_index].tag; 396 output[mesh_output_index].morph_descriptor_index = current_input[input_index].morph_descriptor_index; 397 output[mesh_output_index].metadata_index = current_input[input_index].metadata_index;