Skip to content

Solari radiance cache #20406

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 35 commits into from
Aug 9, 2025
Merged

Conversation

JMS55
Copy link
Contributor

@JMS55 JMS55 commented Aug 4, 2025

Objective

  • Make GI pass cheaper
  • Improve GI quality after disocclusions
  • Have multibounce GI instead of the current single bounce

Solution

  • Add a persistent world cache based on spatial hashing
  • Each hash key is a combination of position, normal, and cell size, where the cell size is based on the distance from the camera
  1. Each entry/cell in the cache has a "life" counter. It starts at 30, and the first pass each frame decrements each entry by 1. When it reaches 0, the entry is deallocated.
  2. Passes 2-4 compact and write a list of the active cells (life != 0) in the cache via parallel prefix sum
  3. Pass 5 samples lighting for each cache cell (direct via light sampling, indirect via BRDF sampling and querying the cache at hit points)
  4. Pass 6 blends the new radiance into the existing cell's radiance. Unlike ReSTIR/RIS we are not storing a single light sample/path. We are instead storing radiance.
  5. The ReSTIR GI pass is basically the same, except at the sample point, instead of sampling direct lighting via another raytrace, it instead queries the world cache. Querying a cache cell resets its life to 30.

Issues

  • Querying an empty cache cell initializes the position/normal used for sampling lighting for the cell to the values used in the query. This means that each cache entry has a fixed sample point to represent the entire volume, which can sometimes lead to problems if the chosen sample point poorly represents the volume.
  • When transitioning LODs, cells start out as empty rather than initializing themselves via querying adjacent LOD levels. Not a huge problem in practice though, at least in the cornell box I was hard pressed to notice this as ReSTIR GI temporal reservoirs hide the issue. Yay for two level caching!
  • I'm not entirely sure that cache entries will ever die in larger worlds. The cache can keep itself alive since cells can query each other (resetting their life to 30) when updating cells. Probably need to think about how to handle larger scenes.
  • Worse stability in larger scenes like bistro. The jacobian leads to temporal bright spots with ReSTIR GI more often than not using the cache, and the cache itself can be very unstable. Might need some form of ReSTIR in the cache, like https://wangningbei.github.io/2023/ReSTIR_files/paper_ReSTIRGI.pdf. (EDIT: Added RIS which helps a ton. ReSTIR might be desirable for better responsiveness though)
  • Lots more tweaking of hashing stuff possible.
  • Like the rest of Solari, I haven't done any testing of dynamic content yet. Gotta get static content working first :)
  • The pipeline setup code for Solari has gotten super messy. Ideas on how to fix it are welcome.

Showcase

image image image

@JMS55 JMS55 added this to the 0.17 milestone Aug 4, 2025
@JMS55 JMS55 added A-Rendering Drawing game state to the screen D-Complex Quite challenging from either a design or technical perspective. Ask for help! labels Aug 4, 2025
@JMS55 JMS55 requested review from SparkyPotato and atlv24 August 4, 2025 02:50
@alice-i-cecile alice-i-cecile added C-Feature A new feature, making something new possible S-Needs-Review Needs reviewer attention (from anyone!) to move forward labels Aug 5, 2025
@alice-i-cecile alice-i-cecile modified the milestones: 0.17, 0.18 Aug 5, 2025
@alice-i-cecile
Copy link
Member

This is not a blocker for 0.17; moving milestones :)

let step = max((d * base_size) / 7.0, base_size);
let quantization_factor = exp2(floor(log2(step)));

return floor(world_position / quantization_factor + 0.0001);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the + 0.0001 meant to stop a div by 0?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nah, it goes after the division. It just kinda bugs out without it, not entirely sure why.

}

fn sample_random_light_ris(world_position: vec3<f32>, world_normal: vec3<f32>, workgroup_id: vec2<u32>, rng: ptr<function, u32>) -> vec3<f32> {
var workgroup_rng = (workgroup_id.x * 5782582u) + workgroup_id.y;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should have some sort of util function for this...

Also, this should be moved to sampling.wgsl so we can share it with restir_di.wgsl

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe? I haven't decided how I want to unify the RIS stuff yet. For instance this one dosen't use BRDF.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think Sparky was talking about the hash thing

@alice-i-cecile alice-i-cecile added S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it and removed S-Needs-Review Needs reviewer attention (from anyone!) to move forward labels Aug 9, 2025
@alice-i-cecile alice-i-cecile added this pull request to the merge queue Aug 9, 2025
Merged via the queue into bevyengine:main with commit 3f14e34 Aug 9, 2025
34 checks passed
@github-project-automation github-project-automation bot moved this to Done in Rendering Aug 9, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-Rendering Drawing game state to the screen C-Feature A new feature, making something new possible D-Complex Quite challenging from either a design or technical perspective. Ask for help! S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

4 participants