Path: blob/main/crates/bevy_solari/src/realtime/world_cache_query.wgsl
6596 views
#define_import_path bevy_solari::world_cache /// How responsive the world cache is to changes in lighting (higher is less responsive, lower is more responsive) const WORLD_CACHE_MAX_TEMPORAL_SAMPLES: f32 = 10.0; /// Maximum amount of frames a cell can live for without being queried const WORLD_CACHE_CELL_LIFETIME: u32 = 30u; /// Maximum amount of attempts to find a cache entry after a hash collision const WORLD_CACHE_MAX_SEARCH_STEPS: u32 = 3u; /// The size of a cache cell at the lowest LOD in meters const WORLD_CACHE_POSITION_BASE_CELL_SIZE: f32 = 0.25; /// How fast the world cache transitions between LODs as a function of distance to the camera const WORLD_CACHE_POSITION_LOD_SCALE: f32 = 30.0; /// Marker value for an empty cell const WORLD_CACHE_EMPTY_CELL: u32 = 0u; struct WorldCacheGeometryData { world_position: vec3<f32>, padding_a: u32, world_normal: vec3<f32>, padding_b: u32 } @group(1) @binding(14) var<storage, read_write> world_cache_checksums: array<atomic<u32>, #{WORLD_CACHE_SIZE}>; #ifdef WORLD_CACHE_NON_ATOMIC_LIFE_BUFFER @group(1) @binding(15) var<storage, read_write> world_cache_life: array<u32, #{WORLD_CACHE_SIZE}>; #else @group(1) @binding(15) var<storage, read_write> world_cache_life: array<atomic<u32>, #{WORLD_CACHE_SIZE}>; #endif @group(1) @binding(16) var<storage, read_write> world_cache_radiance: array<vec4<f32>, #{WORLD_CACHE_SIZE}>; @group(1) @binding(17) var<storage, read_write> world_cache_geometry_data: array<WorldCacheGeometryData, #{WORLD_CACHE_SIZE}>; @group(1) @binding(18) var<storage, read_write> world_cache_active_cells_new_radiance: array<vec3<f32>, #{WORLD_CACHE_SIZE}>; @group(1) @binding(19) var<storage, read_write> world_cache_a: array<u32, #{WORLD_CACHE_SIZE}>; @group(1) @binding(20) var<storage, read_write> world_cache_b: array<u32, 1024u>; @group(1) @binding(21) var<storage, read_write> world_cache_active_cell_indices: array<u32, #{WORLD_CACHE_SIZE}>; @group(1) @binding(22) var<storage, read_write> world_cache_active_cells_count: u32; #ifndef WORLD_CACHE_NON_ATOMIC_LIFE_BUFFER fn query_world_cache(world_position: vec3<f32>, world_normal: vec3<f32>, view_position: vec3<f32>) -> vec3<f32> { let cell_size = get_cell_size(world_position, view_position); let world_position_quantized = bitcast<vec3<u32>>(quantize_position(world_position, cell_size)); let world_normal_quantized = bitcast<vec3<u32>>(quantize_normal(world_normal)); var key = compute_key(world_position_quantized, world_normal_quantized); let checksum = compute_checksum(world_position_quantized, world_normal_quantized); for (var i = 0u; i < WORLD_CACHE_MAX_SEARCH_STEPS; i++) { let existing_checksum = atomicCompareExchangeWeak(&world_cache_checksums[key], WORLD_CACHE_EMPTY_CELL, checksum).old_value; if existing_checksum == checksum { // Cache entry already exists - get radiance and reset cell lifetime atomicStore(&world_cache_life[key], WORLD_CACHE_CELL_LIFETIME); return world_cache_radiance[key].rgb; } else if existing_checksum == WORLD_CACHE_EMPTY_CELL { // Cell is empty - reset cell lifetime so that it starts getting updated next frame atomicStore(&world_cache_life[key], WORLD_CACHE_CELL_LIFETIME); world_cache_geometry_data[key].world_position = world_position; world_cache_geometry_data[key].world_normal = world_normal; return vec3(0.0); } else { // Collision - jump to another entry key = wrap_key(pcg_hash(key)); } } return vec3(0.0); } #endif fn get_cell_size(world_position: vec3<f32>, view_position: vec3<f32>) -> f32 { let camera_distance = distance(view_position, world_position) / WORLD_CACHE_POSITION_LOD_SCALE; let lod = exp2(floor(log2(1.0 + camera_distance))); return WORLD_CACHE_POSITION_BASE_CELL_SIZE * lod; } fn quantize_position(world_position: vec3<f32>, quantization_factor: f32) -> vec3<f32> { return floor(world_position / quantization_factor + 0.0001); } fn quantize_normal(world_normal: vec3<f32>) -> vec3<f32> { return floor(world_normal + 0.0001); } // TODO: Clustering fn compute_key(world_position: vec3<u32>, world_normal: vec3<u32>) -> u32 { var key = pcg_hash(world_position.x); key = pcg_hash(key + world_position.y); key = pcg_hash(key + world_position.z); key = pcg_hash(key + world_normal.x); key = pcg_hash(key + world_normal.y); key = pcg_hash(key + world_normal.z); return wrap_key(key); } fn compute_checksum(world_position: vec3<u32>, world_normal: vec3<u32>) -> u32 { var key = iqint_hash(world_position.x); key = iqint_hash(key + world_position.y); key = iqint_hash(key + world_position.z); key = iqint_hash(key + world_normal.x); key = iqint_hash(key + world_normal.y); key = iqint_hash(key + world_normal.z); return key; } fn pcg_hash(input: u32) -> u32 { let state = input * 747796405u + 2891336453u; let word = ((state >> ((state >> 28u) + 4u)) ^ state) * 277803737u; return (word >> 22u) ^ word; } fn iqint_hash(input: u32) -> u32 { let n = (input << 13u) ^ input; return n * (n * n * 15731u + 789221u) + 1376312589u; } fn wrap_key(key: u32) -> u32 { return key & (#{WORLD_CACHE_SIZE} - 1u); }