Search across packages, versions, files, declarations, and comments. Use multiple terms to narrow results (AND), and add @vertex, @fragment, or @compute to filter by shader stage.
1#define_import_path bevy_solari::world_cache
3/// How responsive the world cache is to changes in lighting (higher is less responsive, lower is more responsive)
4const WORLD_CACHE_MAX_TEMPORAL_SAMPLES: f32 = 20.0;
5/// Maximum amount of frames a cell can live for without being queried
6const WORLD_CACHE_CELL_LIFETIME: u32 = 30u;
7/// Maximum amount of attempts to find a cache entry after a hash collision
8const WORLD_CACHE_MAX_SEARCH_STEPS: u32 = 3u;
10/// The size of a cache cell at the lowest LOD in meters
11const WORLD_CACHE_POSITION_BASE_CELL_SIZE: f32 = 0.25;
12/// How fast the world cache transitions between LODs as a function of distance to the camera
13const WORLD_CACHE_POSITION_LOD_SCALE: f32 = 30.0;
15/// Marker value for an empty cell
16const WORLD_CACHE_EMPTY_CELL: u32 = 0u;
18struct WorldCacheGeometryData {
19 world_position: vec3<f32>,
20 padding_a: u32,
21 world_normal: vec3<f32>,
22 padding_b: u32
25@group(1) @binding(14) var<storage, read_write> world_cache_checksums: array<atomic<u32>, #{WORLD_CACHE_SIZE}>;
26#ifdef WORLD_CACHE_NON_ATOMIC_LIFE_BUFFER
27@group(1) @binding(15) var<storage, read_write> world_cache_life: array<u32, #{WORLD_CACHE_SIZE}>;
28#else
29@group(1) @binding(15) var<storage, read_write> world_cache_life: array<atomic<u32>, #{WORLD_CACHE_SIZE}>;
30#endif
31@group(1) @binding(16) var<storage, read_write> world_cache_radiance: array<vec4<f32>, #{WORLD_CACHE_SIZE}>;
32@group(1) @binding(17) var<storage, read_write> world_cache_geometry_data: array<WorldCacheGeometryData, #{WORLD_CACHE_SIZE}>;
33@group(1) @binding(18) var<storage, read_write> world_cache_active_cells_new_radiance: array<vec3<f32>, #{WORLD_CACHE_SIZE}>;
34@group(1) @binding(19) var<storage, read_write> world_cache_a: array<u32, #{WORLD_CACHE_SIZE}>;
35@group(1) @binding(20) var<storage, read_write> world_cache_b: array<u32, 1024u>;
36@group(1) @binding(21) var<storage, read_write> world_cache_active_cell_indices: array<u32, #{WORLD_CACHE_SIZE}>;
37@group(1) @binding(22) var<storage, read_write> world_cache_active_cells_count: u32;
39#ifndef WORLD_CACHE_NON_ATOMIC_LIFE_BUFFER
40fn query_world_cache(world_position: vec3<f32>, world_normal: vec3<f32>, view_position: vec3<f32>) -> vec3<f32> {
41 let cell_size = get_cell_size(world_position, view_position);
42 let world_position_quantized = bitcast<vec3<u32>>(quantize_position(world_position, cell_size));
43 let world_normal_quantized = bitcast<vec3<u32>>(quantize_normal(world_normal));
44 var key = compute_key(world_position_quantized, world_normal_quantized);
45 let checksum = compute_checksum(world_position_quantized, world_normal_quantized);
47 for (var i = 0u; i < WORLD_CACHE_MAX_SEARCH_STEPS; i++) {
48 let existing_checksum = atomicCompareExchangeWeak(&world_cache_checksums[key], WORLD_CACHE_EMPTY_CELL, checksum).old_value;
49 if existing_checksum == checksum {
50 // Cache entry already exists - get radiance and reset cell lifetime
51 atomicStore(&world_cache_life[key], WORLD_CACHE_CELL_LIFETIME);
52 return world_cache_radiance[key].rgb;
53 } else if existing_checksum == WORLD_CACHE_EMPTY_CELL {
54 // Cell is empty - reset cell lifetime so that it starts getting updated next frame
55 atomicStore(&world_cache_life[key], WORLD_CACHE_CELL_LIFETIME);
56 world_cache_geometry_data[key].world_position = world_position;
57 world_cache_geometry_data[key].world_normal = world_normal;
58 return vec3(0.0);
59 } else {
60 // Collision - jump to another entry
61 key = wrap_key(pcg_hash(key));
62 }
63 }
65 return vec3(0.0);
67#endif
69fn get_cell_size(world_position: vec3<f32>, view_position: vec3<f32>) -> f32 {
70 let camera_distance = distance(view_position, world_position) / WORLD_CACHE_POSITION_LOD_SCALE;
71 let lod = exp2(floor(log2(1.0 + camera_distance)));
72 return WORLD_CACHE_POSITION_BASE_CELL_SIZE * lod;
75fn quantize_position(world_position: vec3<f32>, quantization_factor: f32) -> vec3<f32> {
76 return floor(world_position / quantization_factor + 0.0001);
79fn quantize_normal(world_normal: vec3<f32>) -> vec3<f32> {
80 return floor(world_normal + 0.0001);
83// TODO: Clustering
84fn compute_key(world_position: vec3<u32>, world_normal: vec3<u32>) -> u32 {
85 var key = pcg_hash(world_position.x);
86 key = pcg_hash(key + world_position.y);
87 key = pcg_hash(key + world_position.z);
88 key = pcg_hash(key + world_normal.x);
89 key = pcg_hash(key + world_normal.y);
90 key = pcg_hash(key + world_normal.z);
91 return wrap_key(key);
94fn compute_checksum(world_position: vec3<u32>, world_normal: vec3<u32>) -> u32 {
95 var key = iqint_hash(world_position.x);
96 key = iqint_hash(key + world_position.y);
97 key = iqint_hash(key + world_position.z);
98 key = iqint_hash(key + world_normal.x);
99 key = iqint_hash(key + world_normal.y);
100 key = iqint_hash(key + world_normal.z);
101 return key;
104fn pcg_hash(input: u32) -> u32 {
105 let state = input * 747796405u + 2891336453u;
106 let word = ((state >> ((state >> 28u) + 4u)) ^ state) * 277803737u;
107 return (word >> 22u) ^ word;
110fn iqint_hash(input: u32) -> u32 {
111 let n = (input << 13u) ^ input;
112 return n * (n * n * 15731u + 789221u) + 1376312589u;
115fn wrap_key(key: u32) -> u32 {
116 return key & (#{WORLD_CACHE_SIZE} - 1u);
Contents