1use bevy_app::{App, Plugin};
2use bevy_asset::{embedded_asset, load_embedded_asset, Handle};
3use bevy_camera::{Camera, Camera3d};
4use bevy_core_pipeline::{
5 prepass::{DepthPrepass, NormalPrepass, ViewPrepassTextures},
6 schedule::{Core3d, Core3dSystems},
7};
8use bevy_ecs::{
9 prelude::{Component, Entity},
10 query::{Has, With},
11 reflect::ReflectComponent,
12 resource::Resource,
13 schedule::IntoScheduleConfigs,
14 system::{Commands, Query, Res, ResMut},
15 world::{FromWorld, World},
16};
17use bevy_image::ToExtents;
18use bevy_reflect::{std_traits::ReflectDefault, Reflect};
19use bevy_render::{
20 camera::{ExtractedCamera, TemporalJitter},
21 diagnostic::RecordDiagnostics,
22 extract_component::ExtractComponent,
23 globals::{GlobalsBuffer, GlobalsUniform},
24 render_resource::{
25 binding_types::{
26 sampler, texture_2d, texture_depth_2d, texture_storage_2d, uniform_buffer,
27 },
28 *,
29 },
30 renderer::{RenderAdapter, RenderContext, RenderDevice, RenderQueue, ViewQuery},
31 sync_component::SyncComponentPlugin,
32 sync_world::RenderEntity,
33 texture::{CachedTexture, TextureCache},
34 view::{Msaa, ViewUniform, ViewUniformOffset, ViewUniforms},
35 Extract, ExtractSchedule, GpuResourceAppExt, Render, RenderApp, RenderSystems,
36};
37use bevy_shader::{load_shader_library, Shader, ShaderDefVal};
38use bevy_utils::prelude::default;
39use core::mem;
40use tracing::{error, warn};
41
42pub struct ScreenSpaceAmbientOcclusionPlugin;
44
45impl Plugin for ScreenSpaceAmbientOcclusionPlugin {
46 fn build(&self, app: &mut App) {
47 {
{
let mut embedded =
app.world_mut().resource_mut::<::bevy_asset::io::embedded::EmbeddedAssetRegistry>();
let path =
{
let crate_name = "bevy_pbr::ssao".split(':').next().unwrap();
::bevy_asset::io::embedded::_embedded_asset_path(crate_name,
"src".as_ref(), "src/ssao/mod.rs".as_ref(),
"utils.wesl".as_ref())
};
let watched_path =
::bevy_asset::io::embedded::watched_path("src/ssao/mod.rs",
"utils.wesl");
embedded.insert_asset(watched_path, &path,
b"import bevy_render::maths::{PI, HALF_PI};\n\n/// Approximates single-bounce ambient occlusion to multi-bounce ambient occlusion\n/// https://blog.selfshadow.com/publications/s2016-shading-course/activision/s2016_pbs_activision_occlusion.pdf#page=78\nfn ssao_multibounce(visibility: f32, base_color: vec3<f32>) -> vec3<f32> {\n let a = 2.0404 * base_color - 0.3324;\n let b = -4.7951 * base_color + 0.6417;\n let c = 2.7552 * base_color + 0.6903;\n let x = vec3<f32>(visibility);\n return max(x, ((x * a + b) * x + c) * x);\n}\n");
}
};
let handle:
::bevy_shader::_macro::bevy_asset::prelude::Handle<::bevy_shader::prelude::Shader> =
{
let (path, asset_server) =
{
let path =
{
{
let crate_name =
"bevy_pbr::ssao".split(':').next().unwrap();
::bevy_asset::io::embedded::_embedded_asset_path(crate_name,
"src".as_ref(), "src/ssao/mod.rs".as_ref(),
"utils.wesl".as_ref())
}
};
let path =
::bevy_asset::AssetPath::from_path_buf(path).with_source("embedded");
let asset_server =
::bevy_asset::io::embedded::GetAssetServer::get_asset_server(app);
(path, asset_server)
};
asset_server.load(path)
};
::core::mem::forget(handle);load_shader_library!(app, "utils.wesl");
48
49 {
{
let mut embedded =
app.world_mut().resource_mut::<::bevy_asset::io::embedded::EmbeddedAssetRegistry>();
let path =
{
let crate_name = "bevy_pbr::ssao".split(':').next().unwrap();
::bevy_asset::io::embedded::_embedded_asset_path(crate_name,
"src".as_ref(), "src/ssao/mod.rs".as_ref(),
"preprocess_depth.wesl".as_ref())
};
let watched_path =
::bevy_asset::io::embedded::watched_path("src/ssao/mod.rs",
"preprocess_depth.wesl");
embedded.insert_asset(watched_path, &path,
b"//! Inputs a depth texture and outputs a MIP-chain of depths.\n//!\n//! Because SSAO\'s performance is bound by texture reads, this increases\n//! performance over using the full resolution depth for every sample.\n//!\n//! Reference: https://research.nvidia.com/sites/default/files/pubs/2012-06_Scalable-Ambient-Obscurance/McGuire12SAO.pdf, section 2.2\n\nimport bevy_render::view::View;\n\n@group(0) @binding(0) var input_depth: texture_depth_2d;\n@if(USE_R16FLOAT) {\n@group(0) @binding(1) var preprocessed_depth_mip0: texture_storage_2d<r16float, write>;\n@group(0) @binding(2) var preprocessed_depth_mip1: texture_storage_2d<r16float, write>;\n@group(0) @binding(3) var preprocessed_depth_mip2: texture_storage_2d<r16float, write>;\n@group(0) @binding(4) var preprocessed_depth_mip3: texture_storage_2d<r16float, write>;\n@group(0) @binding(5) var preprocessed_depth_mip4: texture_storage_2d<r16float, write>;\n}\n@if(!USE_R16FLOAT) {\n@group(0) @binding(1) var preprocessed_depth_mip0: texture_storage_2d<r32float, write>;\n@group(0) @binding(2) var preprocessed_depth_mip1: texture_storage_2d<r32float, write>;\n@group(0) @binding(3) var preprocessed_depth_mip2: texture_storage_2d<r32float, write>;\n@group(0) @binding(4) var preprocessed_depth_mip3: texture_storage_2d<r32float, write>;\n@group(0) @binding(5) var preprocessed_depth_mip4: texture_storage_2d<r32float, write>;\n}\n@group(1) @binding(0) var point_clamp_sampler: sampler;\n@group(1) @binding(1) var linear_clamp_sampler: sampler;\n@group(1) @binding(2) var<uniform> view: View;\n\n/// Using 4 depths from the previous MIP, compute a weighted average for the depth of the current MIP\nfn weighted_average(depth0: f32, depth1: f32, depth2: f32, depth3: f32) -> f32 {\n let depth_range_scale_factor = 0.75;\n let effect_radius = depth_range_scale_factor * 0.5 * 1.457;\n let falloff_range = 0.615 * effect_radius;\n let falloff_from = effect_radius * (1.0 - 0.615);\n let falloff_mul = -1.0 / falloff_range;\n let falloff_add = falloff_from / falloff_range + 1.0;\n\n let min_depth = min(min(depth0, depth1), min(depth2, depth3));\n let weight0 = saturate((depth0 - min_depth) * falloff_mul + falloff_add);\n let weight1 = saturate((depth1 - min_depth) * falloff_mul + falloff_add);\n let weight2 = saturate((depth2 - min_depth) * falloff_mul + falloff_add);\n let weight3 = saturate((depth3 - min_depth) * falloff_mul + falloff_add);\n let weight_total = weight0 + weight1 + weight2 + weight3;\n\n return ((weight0 * depth0) + (weight1 * depth1) + (weight2 * depth2) + (weight3 * depth3)) / weight_total;\n}\n\n/// Used to share the depths from the previous MIP level between all invocations in a workgroup\nvar<workgroup> previous_mip_depth: array<array<f32, 8>, 8>;\n\n@compute\n@workgroup_size(8, 8, 1)\nfn preprocess_depth(@builtin(global_invocation_id) global_id: vec3<u32>, @builtin(local_invocation_id) local_id: vec3<u32>) {\n let base_coordinates = vec2<i32>(global_id.xy);\n\n // MIP 0 - Copy 4 texels from the input depth (per invocation, 8x8 invocations per workgroup)\n let pixel_coordinates0 = base_coordinates * 2i;\n let pixel_coordinates1 = pixel_coordinates0 + vec2<i32>(1i, 0i);\n let pixel_coordinates2 = pixel_coordinates0 + vec2<i32>(0i, 1i);\n let pixel_coordinates3 = pixel_coordinates0 + vec2<i32>(1i, 1i);\n let input_depth_size = vec2<f32>(textureDimensions(input_depth));\n let depths_uv = (vec2<f32>(pixel_coordinates0) + view.viewport.xy) / input_depth_size;\n let depths = textureGather(input_depth, point_clamp_sampler, depths_uv, vec2<i32>(1i, 1i));\n textureStore(preprocessed_depth_mip0, pixel_coordinates0, vec4<f32>(depths.w, 0.0, 0.0, 0.0));\n textureStore(preprocessed_depth_mip0, pixel_coordinates1, vec4<f32>(depths.z, 0.0, 0.0, 0.0));\n textureStore(preprocessed_depth_mip0, pixel_coordinates2, vec4<f32>(depths.x, 0.0, 0.0, 0.0));\n textureStore(preprocessed_depth_mip0, pixel_coordinates3, vec4<f32>(depths.y, 0.0, 0.0, 0.0));\n\n // MIP 1 - Weighted average of MIP 0\'s depth values (per invocation, 8x8 invocations per workgroup)\n let depth_mip1 = weighted_average(depths.w, depths.z, depths.x, depths.y);\n textureStore(preprocessed_depth_mip1, base_coordinates, vec4<f32>(depth_mip1, 0.0, 0.0, 0.0));\n previous_mip_depth[local_id.x][local_id.y] = depth_mip1;\n\n workgroupBarrier();\n\n // MIP 2 - Weighted average of MIP 1\'s depth values (per invocation, 4x4 invocations per workgroup)\n if all(local_id.xy % vec2<u32>(2u) == vec2<u32>(0u)) {\n let depth0 = previous_mip_depth[local_id.x + 0u][local_id.y + 0u];\n let depth1 = previous_mip_depth[local_id.x + 1u][local_id.y + 0u];\n let depth2 = previous_mip_depth[local_id.x + 0u][local_id.y + 1u];\n let depth3 = previous_mip_depth[local_id.x + 1u][local_id.y + 1u];\n let depth_mip2 = weighted_average(depth0, depth1, depth2, depth3);\n textureStore(preprocessed_depth_mip2, base_coordinates / 2i, vec4<f32>(depth_mip2, 0.0, 0.0, 0.0));\n previous_mip_depth[local_id.x][local_id.y] = depth_mip2;\n }\n\n workgroupBarrier();\n\n // MIP 3 - Weighted average of MIP 2\'s depth values (per invocation, 2x2 invocations per workgroup)\n if all(local_id.xy % vec2<u32>(4u) == vec2<u32>(0u)) {\n let depth0 = previous_mip_depth[local_id.x + 0u][local_id.y + 0u];\n let depth1 = previous_mip_depth[local_id.x + 2u][local_id.y + 0u];\n let depth2 = previous_mip_depth[local_id.x + 0u][local_id.y + 2u];\n let depth3 = previous_mip_depth[local_id.x + 2u][local_id.y + 2u];\n let depth_mip3 = weighted_average(depth0, depth1, depth2, depth3);\n textureStore(preprocessed_depth_mip3, base_coordinates / 4i, vec4<f32>(depth_mip3, 0.0, 0.0, 0.0));\n previous_mip_depth[local_id.x][local_id.y] = depth_mip3;\n }\n\n workgroupBarrier();\n\n // MIP 4 - Weighted average of MIP 3\'s depth values (per invocation, 1 invocation per workgroup)\n if all(local_id.xy % vec2<u32>(8u) == vec2<u32>(0u)) {\n let depth0 = previous_mip_depth[local_id.x + 0u][local_id.y + 0u];\n let depth1 = previous_mip_depth[local_id.x + 4u][local_id.y + 0u];\n let depth2 = previous_mip_depth[local_id.x + 0u][local_id.y + 4u];\n let depth3 = previous_mip_depth[local_id.x + 4u][local_id.y + 4u];\n let depth_mip4 = weighted_average(depth0, depth1, depth2, depth3);\n textureStore(preprocessed_depth_mip4, base_coordinates / 8i, vec4<f32>(depth_mip4, 0.0, 0.0, 0.0));\n }\n}\n");
}
};embedded_asset!(app, "preprocess_depth.wesl");
50 {
{
let mut embedded =
app.world_mut().resource_mut::<::bevy_asset::io::embedded::EmbeddedAssetRegistry>();
let path =
{
let crate_name = "bevy_pbr::ssao".split(':').next().unwrap();
::bevy_asset::io::embedded::_embedded_asset_path(crate_name,
"src".as_ref(), "src/ssao/mod.rs".as_ref(),
"ssao.wesl".as_ref())
};
let watched_path =
::bevy_asset::io::embedded::watched_path("src/ssao/mod.rs",
"ssao.wesl");
embedded.insert_asset(watched_path, &path,
b"//! Visibility Bitmask Ambient Occlusion (VBAO)\n//! Paper: ttps://ar5iv.labs.arxiv.org/html/2301.11376\n//!\n//! Source code heavily based on XeGTAO v1.30 from Intel\n//! https://github.com/GameTechDev/XeGTAO/blob/0d177ce06bfa642f64d8af4de1197ad1bcb862d4/Source/Rendering/Shaders/XeGTAO.hlsli\n//!\n//! Source code based on the existing XeGTAO implementation and\n//! https://cdrinmatane.github.io/posts/ssaovb-code/\n//!\n//! Source code base on SSRT3 implementation\n//! https://github.com/cdrinmatane/SSRT3\n\nimport bevy_render::maths::fast_acos;\n\nimport bevy_render::{\n view::View,\n globals::Globals,\n maths::{PI, HALF_PI},\n};\n\n@group(0) @binding(0) var preprocessed_depth: texture_2d<f32>;\n@group(0) @binding(1) var normals: texture_2d<f32>;\n@group(0) @binding(2) var hilbert_index_lut: texture_2d<u32>;\n@if(USE_R16FLOAT)\n@group(0) @binding(3) var ambient_occlusion: texture_storage_2d<r16float, write>;\n@else\n@group(0) @binding(3) var ambient_occlusion: texture_storage_2d<r32float, write>;\n@group(0) @binding(4) var depth_differences: texture_storage_2d<r32uint, write>;\n@group(0) @binding(5) var<uniform> globals: Globals;\nstruct SsaoSettings {\n radius: f32,\n thickness: f32,\n}\n@group(0) @binding(6) var<uniform> settings: SsaoSettings;\n@group(1) @binding(0) var point_clamp_sampler: sampler;\n@group(1) @binding(1) var linear_clamp_sampler: sampler;\n@group(1) @binding(2) var<uniform> view: View;\n\nfn load_noise(pixel_coordinates: vec2<i32>) -> vec2<f32> {\n var index = textureLoad(hilbert_index_lut, pixel_coordinates % 64, 0).r;\n\n@if(TEMPORAL_JITTER)\n index += 288u * (globals.frame_count % 64u);\n\n // R2 sequence - http://extremelearning.com.au/unreasonable-effectiveness-of-quasirandom-sequences\n return fract(0.5 + f32(index) * vec2<f32>(0.75487766624669276005, 0.5698402909980532659114));\n}\n\n/// Calculate differences in depth between neighbor pixels (later used by the spatial denoiser pass to preserve object edges)\nfn calculate_neighboring_depth_differences(pixel_coordinates: vec2<i32>) -> f32 {\n // Sample the pixel\'s depth and 4 depths around it\n let uv = vec2<f32>(pixel_coordinates) / view.viewport.zw;\n let depths_upper_left = textureGather(0, preprocessed_depth, point_clamp_sampler, uv);\n let depths_bottom_right = textureGather(0, preprocessed_depth, point_clamp_sampler, uv, vec2<i32>(1i, 1i));\n let depth_center = depths_upper_left.y;\n let depth_left = depths_upper_left.x;\n let depth_top = depths_upper_left.z;\n let depth_bottom = depths_bottom_right.x;\n let depth_right = depths_bottom_right.z;\n\n // Calculate the depth differences (large differences represent object edges)\n var edge_info = vec4<f32>(depth_left, depth_right, depth_top, depth_bottom) - depth_center;\n let slope_left_right = (edge_info.y - edge_info.x) * 0.5;\n let slope_top_bottom = (edge_info.w - edge_info.z) * 0.5;\n let edge_info_slope_adjusted = edge_info + vec4<f32>(slope_left_right, -slope_left_right, slope_top_bottom, -slope_top_bottom);\n edge_info = min(abs(edge_info), abs(edge_info_slope_adjusted));\n let bias = 0.25; // Using the bias and then saturating nudges the values a bit\n let scale = depth_center * 0.011; // Weight the edges by their distance from the camera\n edge_info = saturate((1.0 + bias) - edge_info / scale); // Apply the bias and scale, and invert edge_info so that small values become large, and vice versa\n\n // Pack the edge info into the texture\n let edge_info_packed = vec4<u32>(pack4x8unorm(edge_info), 0u, 0u, 0u);\n textureStore(depth_differences, pixel_coordinates, edge_info_packed);\n\n return depth_center;\n}\n\nfn load_normal_view_space(uv_normals: vec2<f32>) -> vec3<f32> {\n var world_normal = textureSampleLevel(normals, point_clamp_sampler, uv_normals, 0.0).xyz;\n world_normal = (world_normal * 2.0) - 1.0;\n let view_from_world = mat3x3<f32>(\n view.view_from_world[0].xyz,\n view.view_from_world[1].xyz,\n view.view_from_world[2].xyz,\n );\n return view_from_world * world_normal;\n}\n\nfn reconstruct_view_space_position(depth: f32, uv: vec2<f32>) -> vec3<f32> {\n let clip_xy = vec2<f32>(uv.x * 2.0 - 1.0, 1.0 - 2.0 * uv.y);\n let t = view.view_from_clip * vec4<f32>(clip_xy, depth, 1.0);\n let view_xyz = t.xyz / t.w;\n return view_xyz;\n}\n\nfn load_and_reconstruct_view_space_position(uv: vec2<f32>, sample_mip_level: f32) -> vec3<f32> {\n let depth = textureSampleLevel(preprocessed_depth, linear_clamp_sampler, uv, sample_mip_level).r;\n return reconstruct_view_space_position(depth, uv);\n}\n\nfn updateSectors(\n min_horizon: f32,\n max_horizon: f32,\n samples_per_slice: f32,\n bitmask: u32,\n) -> u32 {\n let start_horizon = u32(min_horizon * samples_per_slice);\n let angle_horizon = u32(ceil((max_horizon - min_horizon) * samples_per_slice));\n\n return insertBits(bitmask, 0xFFFFFFFFu, start_horizon, angle_horizon);\n}\n\nfn processSample(\n delta_position: vec3<f32>,\n view_vec: vec3<f32>,\n sampling_direction: f32,\n n: vec2<f32>,\n samples_per_slice: f32,\n bitmask: ptr<function, u32>,\n) {\n let delta_position_back_face = delta_position - view_vec * settings.thickness;\n\n var front_back_horizon = vec2(\n fast_acos(dot(normalize(delta_position), view_vec)),\n fast_acos(dot(normalize(delta_position_back_face), view_vec)),\n );\n\n front_back_horizon = saturate(fma(vec2(sampling_direction), -front_back_horizon * (1.0 / PI), n));\n front_back_horizon = select(front_back_horizon.xy, front_back_horizon.yx, sampling_direction >= 0.0);\n\n *bitmask = updateSectors(front_back_horizon.x, front_back_horizon.y, samples_per_slice, *bitmask);\n}\n\n@compute\n@workgroup_size(8, 8, 1)\nfn ssao(@builtin(global_invocation_id) global_id: vec3<u32>) {\n let slice_count = f32(constants::SLICE_COUNT);\n let samples_per_slice_side = f32(constants::SAMPLES_PER_SLICE_SIDE);\n\n let pixel_coordinates = vec2<i32>(global_id.xy);\n let uv = (vec2<f32>(pixel_coordinates) + 0.5) / view.viewport.zw;\n\n var pixel_depth = calculate_neighboring_depth_differences(pixel_coordinates);\n pixel_depth += 0.00001; // Avoid depth precision issues\n\n let pixel_position = reconstruct_view_space_position(pixel_depth, uv);\n let normals_size = vec2<f32>(textureDimensions(normals));\n let uv_normals = (vec2<f32>(pixel_coordinates) + 0.5 + view.viewport.xy) / normals_size;\n let pixel_normal = load_normal_view_space(uv_normals);\n let view_vec = normalize(-pixel_position);\n\n let noise = load_noise(pixel_coordinates);\n let safe_radius = max(settings.radius, 0.0001);\n let sample_scale = (-0.5 * safe_radius * view.clip_from_view[0][0]) / pixel_position.z;\n\n var visibility = 0.0;\n var occluded_sample_count = 0u;\n for (var slice_t = 0.0; slice_t < slice_count; slice_t += 1.0) {\n let slice = slice_t + noise.x;\n let phi = (PI / slice_count) * slice;\n let omega = vec2<f32>(cos(phi), sin(phi));\n\n let direction = vec3<f32>(omega.xy, 0.0);\n let orthographic_direction = direction - (dot(direction, view_vec) * view_vec);\n let axis = cross(direction, view_vec);\n let projected_normal = pixel_normal - axis * dot(pixel_normal, axis);\n let projected_normal_length = length(projected_normal);\n\n let sign_norm = sign(dot(orthographic_direction, projected_normal));\n let cos_norm = saturate(dot(projected_normal, view_vec) / projected_normal_length);\n let n = vec2((HALF_PI - sign_norm * fast_acos(cos_norm)) * (1.0 / PI));\n\n var bitmask = 0u;\n\n let sample_mul = vec2<f32>(omega.x, -omega.y) * sample_scale;\n for (var sample_t = 0.0; sample_t < samples_per_slice_side; sample_t += 1.0) {\n var sample_noise = (slice_t + sample_t * samples_per_slice_side) * 0.6180339887498948482;\n sample_noise = fract(noise.y + sample_noise);\n\n var s = (sample_t + sample_noise) / samples_per_slice_side;\n s *= s; // https://github.com/GameTechDev/XeGTAO#sample-distribution\n let sample = s * sample_mul;\n\n // * view.viewport.zw gets us from [0, 1] to [0, viewport_size], which is needed for this to get the correct mip levels\n let sample_mip_level = clamp(log2(length(sample * view.viewport.zw)) - 3.3, 0.0, 5.0); // https://github.com/GameTechDev/XeGTAO#memory-bandwidth-bottleneck\n let sample_position_1 = load_and_reconstruct_view_space_position(uv + sample, sample_mip_level);\n let sample_position_2 = load_and_reconstruct_view_space_position(uv - sample, sample_mip_level);\n\n let sample_difference_1 = sample_position_1 - pixel_position;\n let sample_difference_2 = sample_position_2 - pixel_position;\n\n processSample(sample_difference_1, view_vec, -1.0, n, samples_per_slice_side * 2.0, &bitmask);\n processSample(sample_difference_2, view_vec, 1.0, n, samples_per_slice_side * 2.0, &bitmask);\n }\n\n occluded_sample_count += countOneBits(bitmask);\n }\n\n visibility = 1.0 - f32(occluded_sample_count) / (slice_count * 2.0 * samples_per_slice_side);\n\n visibility = clamp(visibility, 0.03, 1.0);\n\n textureStore(ambient_occlusion, pixel_coordinates, vec4<f32>(visibility, 0.0, 0.0, 0.0));\n}\n");
}
};embedded_asset!(app, "ssao.wesl");
51 {
{
let mut embedded =
app.world_mut().resource_mut::<::bevy_asset::io::embedded::EmbeddedAssetRegistry>();
let path =
{
let crate_name = "bevy_pbr::ssao".split(':').next().unwrap();
::bevy_asset::io::embedded::_embedded_asset_path(crate_name,
"src".as_ref(), "src/ssao/mod.rs".as_ref(),
"spatial_denoise.wesl".as_ref())
};
let watched_path =
::bevy_asset::io::embedded::watched_path("src/ssao/mod.rs",
"spatial_denoise.wesl");
embedded.insert_asset(watched_path, &path,
b"//! 3x3 bilaterial filter (edge-preserving blur)\n//! https://people.csail.mit.edu/sparis/bf_course/course_notes.pdf\n//!\n//! Note: Does not use the Gaussian kernel part of a typical bilateral blur\n//! From the paper: \"use the information gathered on a neighborhood of 4 \xc3\x97 4 using a bilateral filter for\n//! reconstruction, using _uniform_ convolution weights\"\n//!\n//! Note: The paper does a 4x4 (not quite centered) filter, offset by +/- 1 pixel every other frame\n//! XeGTAO does a 3x3 filter, on two pixels at a time per compute thread, applied twice\n//! We do a 3x3 filter, on 1 pixel per compute thread, applied once\n\nimport bevy_render::view::View;\n\n@group(0) @binding(0) var ambient_occlusion_noisy: texture_2d<f32>;\n@group(0) @binding(1) var depth_differences: texture_2d<u32>;\n@if(USE_R16FLOAT)\n@group(0) @binding(2) var ambient_occlusion: texture_storage_2d<r16float, write>;\n@else\n@group(0) @binding(2) var ambient_occlusion: texture_storage_2d<r32float, write>;\n@group(1) @binding(0) var point_clamp_sampler: sampler;\n@group(1) @binding(1) var linear_clamp_sampler: sampler;\n@group(1) @binding(2) var<uniform> view: View;\n\n@compute\n@workgroup_size(8, 8, 1)\nfn spatial_denoise(@builtin(global_invocation_id) global_id: vec3<u32>) {\n let pixel_coordinates = vec2<i32>(global_id.xy);\n let uv = vec2<f32>(pixel_coordinates) / view.viewport.zw;\n\n let edges0 = textureGather(0, depth_differences, point_clamp_sampler, uv);\n let edges1 = textureGather(0, depth_differences, point_clamp_sampler, uv, vec2<i32>(2i, 0i));\n let edges2 = textureGather(0, depth_differences, point_clamp_sampler, uv, vec2<i32>(1i, 2i));\n let visibility0 = textureGather(0, ambient_occlusion_noisy, point_clamp_sampler, uv);\n let visibility1 = textureGather(0, ambient_occlusion_noisy, point_clamp_sampler, uv, vec2<i32>(2i, 0i));\n let visibility2 = textureGather(0, ambient_occlusion_noisy, point_clamp_sampler, uv, vec2<i32>(0i, 2i));\n let visibility3 = textureGather(0, ambient_occlusion_noisy, point_clamp_sampler, uv, vec2<i32>(2i, 2i));\n\n let left_edges = unpack4x8unorm(edges0.x);\n let right_edges = unpack4x8unorm(edges1.x);\n let top_edges = unpack4x8unorm(edges0.z);\n let bottom_edges = unpack4x8unorm(edges2.w);\n var center_edges = unpack4x8unorm(edges0.y);\n center_edges *= vec4<f32>(left_edges.y, right_edges.x, top_edges.w, bottom_edges.z);\n\n let center_weight = 1.2;\n let left_weight = center_edges.x;\n let right_weight = center_edges.y;\n let top_weight = center_edges.z;\n let bottom_weight = center_edges.w;\n let top_left_weight = 0.425 * (top_weight * top_edges.x + left_weight * left_edges.z);\n let top_right_weight = 0.425 * (top_weight * top_edges.y + right_weight * right_edges.z);\n let bottom_left_weight = 0.425 * (bottom_weight * bottom_edges.x + left_weight * left_edges.w);\n let bottom_right_weight = 0.425 * (bottom_weight * bottom_edges.y + right_weight * right_edges.w);\n\n let center_visibility = visibility0.y;\n let left_visibility = visibility0.x;\n let right_visibility = visibility1.x;\n let top_visibility = visibility0.z;\n let bottom_visibility = visibility2.z;\n let top_left_visibility = visibility0.w;\n let top_right_visibility = visibility1.w;\n let bottom_left_visibility = visibility2.w;\n let bottom_right_visibility = visibility3.w;\n\n var sum = center_visibility * center_weight;\n sum += left_visibility * left_weight;\n sum += right_visibility * right_weight;\n sum += top_visibility * top_weight;\n sum += bottom_visibility * bottom_weight;\n sum += top_left_visibility * top_left_weight;\n sum += top_right_visibility * top_right_weight;\n sum += bottom_left_visibility * bottom_left_weight;\n sum += bottom_right_visibility * bottom_right_weight;\n\n var sum_weight = center_weight;\n sum_weight += left_weight;\n sum_weight += right_weight;\n sum_weight += top_weight;\n sum_weight += bottom_weight;\n sum_weight += top_left_weight;\n sum_weight += top_right_weight;\n sum_weight += bottom_left_weight;\n sum_weight += bottom_right_weight;\n\n let denoised_visibility = sum / sum_weight;\n\n textureStore(ambient_occlusion, pixel_coordinates, vec4<f32>(denoised_visibility, 0.0, 0.0, 0.0));\n}\n");
}
};embedded_asset!(app, "spatial_denoise.wesl");
52
53 app.add_plugins(SyncComponentPlugin::<ScreenSpaceAmbientOcclusion>::default());
54 }
55
56 fn finish(&self, app: &mut App) {
57 let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
58 return;
59 };
60
61 if render_app
62 .world()
63 .resource::<RenderDevice>()
64 .limits()
65 .max_storage_textures_per_shader_stage
66 < 5
67 {
68 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event src/ssao/mod.rs:68",
"bevy_pbr::ssao", ::tracing::Level::WARN,
::tracing_core::__macro_support::Option::Some("src/ssao/mod.rs"),
::tracing_core::__macro_support::Option::Some(68u32),
::tracing_core::__macro_support::Option::Some("bevy_pbr::ssao"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::WARN <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::WARN <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("ScreenSpaceAmbientOcclusionPlugin not loaded. GPU lacks support: Limits::max_storage_textures_per_shader_stage is less than 5.")
as &dyn ::tracing::field::Value))])
});
} else { ; }
};warn!("ScreenSpaceAmbientOcclusionPlugin not loaded. GPU lacks support: Limits::max_storage_textures_per_shader_stage is less than 5.");
69 return;
70 }
71
72 render_app
73 .init_gpu_resource::<SsaoPipelines>()
74 .init_gpu_resource::<SpecializedComputePipelines<SsaoPipelines>>()
75 .add_systems(ExtractSchedule, extract_ssao_settings)
76 .add_systems(
77 Render,
78 (
79 prepare_ssao_pipelines.in_set(RenderSystems::Prepare),
80 prepare_ssao_textures.in_set(RenderSystems::PrepareResources),
81 prepare_ssao_bind_groups.in_set(RenderSystems::PrepareBindGroups),
82 ),
83 );
84
85 render_app.add_systems(
86 Core3d,
87 ssao.after(Core3dSystems::Prepass)
88 .before(Core3dSystems::MainPass),
89 );
90 }
91}
92
93#[derive(#[doc =
"**Required Components**: [`DepthPrepass`], [`NormalPrepass`]. \n\n A component's Required Components are inserted whenever it is inserted. Note that this will also insert the required components _of_ the required components, recursively, in depth-first order."]
impl bevy_ecs::component::Component for ScreenSpaceAmbientOcclusion where
Self: ::core::marker::Send + ::core::marker::Sync + 'static {
const STORAGE_TYPE: bevy_ecs::component::StorageType =
bevy_ecs::component::StorageType::Table;
type Mutability = bevy_ecs::component::Mutable;
fn register_required_components(_requiree:
bevy_ecs::component::ComponentId,
required_components:
&mut bevy_ecs::component::RequiredComponentsRegistrator) {
required_components.register_required::<DepthPrepass>(<DepthPrepass as
::core::default::Default>::default);
required_components.register_required::<NormalPrepass>(<NormalPrepass
as ::core::default::Default>::default);
}
fn clone_behavior() -> bevy_ecs::component::ComponentCloneBehavior {
use bevy_ecs::component::{
DefaultCloneBehaviorBase, DefaultCloneBehaviorViaClone,
};
(&&&bevy_ecs::component::DefaultCloneBehaviorSpecialization::<Self>::default()).default_clone_behavior()
}
fn relationship_accessor()
->
::core::option::Option<bevy_ecs::relationship::ComponentRelationshipAccessor<Self>> {
::core::option::Option::None
}
}Component, impl bevy_extract::sync_component::SyncComponent<RenderApp> for
ScreenSpaceAmbientOcclusion where Self: ::core::clone::Clone {
type Target =
(Self, ScreenSpaceAmbientOcclusionResources, SsaoPipelineId,
SsaoBindGroups);
}
impl bevy_extract::extract_component::ExtractComponent<RenderApp> for
ScreenSpaceAmbientOcclusion where Self: ::core::clone::Clone {
type QueryData = &'static Self;
type QueryFilter = ();
type Out = Self;
fn extract_component(item:
bevy_ecs::query::QueryItem<'_, '_, Self::QueryData>)
-> ::core::option::Option<Self::Out> {
::core::option::Option::Some(item.clone())
}
}ExtractComponent, const _: () =
{
impl bevy_reflect::GetTypeRegistration for ScreenSpaceAmbientOcclusion
where {
fn get_type_registration() -> bevy_reflect::TypeRegistration {
let mut registration =
bevy_reflect::TypeRegistration::of::<Self>();
registration.insert(<bevy_reflect::ReflectFromPtr as
bevy_reflect::CreateTypeData<Self>>::create_type_data(()));
registration.insert(<bevy_reflect::ReflectFromReflect as
bevy_reflect::CreateTypeData<Self>>::create_type_data(()));
registration.register_type_data_with::<ReflectComponent, Self,
_>(());
registration.register_type_data_with::<ReflectDefault, Self,
_>(());
registration
}
#[inline(never)]
fn register_type_dependencies(registry:
&mut bevy_reflect::TypeRegistry) {
<ScreenSpaceAmbientOcclusionQualityLevel as
bevy_reflect::__macro_exports::RegisterForReflection>::__register(registry);
<f32 as
bevy_reflect::__macro_exports::RegisterForReflection>::__register(registry);
}
}
impl bevy_reflect::Typed for ScreenSpaceAmbientOcclusion where {
#[inline(never)]
fn type_info() -> &'static bevy_reflect::TypeInfo {
static CELL: bevy_reflect::utility::NonGenericTypeInfoCell =
bevy_reflect::utility::NonGenericTypeInfoCell::new();
CELL.get_or_set(#[inline(never)] ||
{
bevy_reflect::TypeInfo::Struct(bevy_reflect::structs::StructInfo::new::<Self>(&[bevy_reflect::NamedField::new::<ScreenSpaceAmbientOcclusionQualityLevel>("quality_level"),
bevy_reflect::NamedField::new::<f32>("radius"),
bevy_reflect::NamedField::new::<f32>("constant_object_thickness")]))
})
}
}
#[allow(deprecated, reason =
"derives on a deprecated type shouldn't be considered a usage")]
impl bevy_reflect::TypePath for ScreenSpaceAmbientOcclusion where {
fn type_path() -> &'static str {
"bevy_pbr::ssao::ScreenSpaceAmbientOcclusion"
}
fn short_type_path() -> &'static str {
"ScreenSpaceAmbientOcclusion"
}
fn type_ident() -> ::core::option::Option<&'static str> {
::core::option::Option::Some("ScreenSpaceAmbientOcclusion")
}
fn crate_name() -> ::core::option::Option<&'static str> {
::core::option::Option::Some("bevy_pbr::ssao".split(':').next().unwrap())
}
fn module_path() -> ::core::option::Option<&'static str> {
::core::option::Option::Some("bevy_pbr::ssao")
}
}
impl bevy_reflect::Reflect for ScreenSpaceAmbientOcclusion where {
#[inline]
fn into_any(self:
bevy_reflect::__macro_exports::alloc_utils::Box<Self>)
->
bevy_reflect::__macro_exports::alloc_utils::Box<dyn ::core::any::Any> {
self
}
#[inline]
fn as_any(&self) -> &dyn ::core::any::Any { self }
#[inline]
fn as_any_mut(&mut self) -> &mut dyn ::core::any::Any { self }
#[inline]
fn into_reflect(self:
bevy_reflect::__macro_exports::alloc_utils::Box<Self>)
->
bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::Reflect> {
self
}
#[inline]
fn as_reflect(&self) -> &dyn bevy_reflect::Reflect { self }
#[inline]
fn as_reflect_mut(&mut self) -> &mut dyn bevy_reflect::Reflect {
self
}
#[inline]
fn set(&mut self,
value:
bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::Reflect>)
->
::core::result::Result<(),
bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::Reflect>> {
*self = <dyn bevy_reflect::Reflect>::take(value)?;
::core::result::Result::Ok(())
}
}
#[allow(non_upper_case_globals)]
const _: () =
{
static __INVENTORY: ::inventory::Node =
::inventory::Node {
value: &{
bevy_reflect::__macro_exports::auto_register::AutomaticReflectRegistrations(<ScreenSpaceAmbientOcclusion
as
bevy_reflect::__macro_exports::auto_register::RegisterForReflection>::__register)
},
next: ::inventory::__private::UnsafeCell::new(::inventory::__private::Option::None),
};
#[link_section = ".text.startup"]
unsafe extern "C" fn __ctor() {
unsafe {
::inventory::ErasedNode::submit(__INVENTORY.value,
&__INVENTORY)
}
}
#[used]
#[link_section = ".init_array"]
static __CTOR: unsafe extern "C" fn() = __ctor;
};
impl bevy_reflect::structs::Struct for ScreenSpaceAmbientOcclusion
where {
fn field(&self, name: &str)
-> ::core::option::Option<&dyn bevy_reflect::PartialReflect> {
match name {
"quality_level" =>
::core::option::Option::Some(&self.quality_level),
"radius" => ::core::option::Option::Some(&self.radius),
"constant_object_thickness" =>
::core::option::Option::Some(&self.constant_object_thickness),
_ => ::core::option::Option::None,
}
}
fn field_mut(&mut self, name: &str)
->
::core::option::Option<&mut dyn bevy_reflect::PartialReflect> {
match name {
"quality_level" =>
::core::option::Option::Some(&mut self.quality_level),
"radius" => ::core::option::Option::Some(&mut self.radius),
"constant_object_thickness" =>
::core::option::Option::Some(&mut self.constant_object_thickness),
_ => ::core::option::Option::None,
}
}
fn field_at(&self, index: usize)
-> ::core::option::Option<&dyn bevy_reflect::PartialReflect> {
match index {
0usize => ::core::option::Option::Some(&self.quality_level),
1usize => ::core::option::Option::Some(&self.radius),
2usize =>
::core::option::Option::Some(&self.constant_object_thickness),
_ => ::core::option::Option::None,
}
}
fn field_at_mut(&mut self, index: usize)
->
::core::option::Option<&mut dyn bevy_reflect::PartialReflect> {
match index {
0usize =>
::core::option::Option::Some(&mut self.quality_level),
1usize => ::core::option::Option::Some(&mut self.radius),
2usize =>
::core::option::Option::Some(&mut self.constant_object_thickness),
_ => ::core::option::Option::None,
}
}
fn name_at(&self, index: usize) -> ::core::option::Option<&str> {
match index {
0usize => ::core::option::Option::Some("quality_level"),
1usize => ::core::option::Option::Some("radius"),
2usize =>
::core::option::Option::Some("constant_object_thickness"),
_ => ::core::option::Option::None,
}
}
fn index_of_name(&self, name: &str)
-> ::core::option::Option<usize> {
match name {
"quality_level" => ::core::option::Option::Some(0usize),
"radius" => ::core::option::Option::Some(1usize),
"constant_object_thickness" =>
::core::option::Option::Some(2usize),
_ => ::core::option::Option::None,
}
}
fn field_len(&self) -> usize { 3usize }
fn iter_fields(&self) -> bevy_reflect::structs::FieldIter {
bevy_reflect::structs::FieldIter::new(self)
}
fn to_dynamic_struct(&self)
->
::core::result::Result<bevy_reflect::structs::DynamicStruct,
bevy_reflect::ReflectCloneError> {
let mut dynamic: bevy_reflect::structs::DynamicStruct =
::core::default::Default::default();
dynamic.set_represented_type(bevy_reflect::PartialReflect::get_represented_type_info(self));
dynamic.insert_boxed("quality_level",
bevy_reflect::PartialReflect::to_dynamic(&self.quality_level)?);
dynamic.insert_boxed("radius",
bevy_reflect::PartialReflect::to_dynamic(&self.radius)?);
dynamic.insert_boxed("constant_object_thickness",
bevy_reflect::PartialReflect::to_dynamic(&self.constant_object_thickness)?);
::core::result::Result::Ok(dynamic)
}
}
impl bevy_reflect::PartialReflect for ScreenSpaceAmbientOcclusion
where {
#[inline]
fn get_represented_type_info(&self)
-> ::core::option::Option<&'static bevy_reflect::TypeInfo> {
::core::option::Option::Some(<Self as
bevy_reflect::Typed>::type_info())
}
#[inline]
fn try_apply(&mut self, value: &dyn bevy_reflect::PartialReflect)
-> ::core::result::Result<(), bevy_reflect::ApplyError> {
if let bevy_reflect::ReflectRef::Struct(struct_value) =
bevy_reflect::PartialReflect::reflect_ref(value) {
for (name, value) in
bevy_reflect::structs::Struct::iter_fields(struct_value) {
if let ::core::option::Option::Some(v) =
bevy_reflect::structs::Struct::field_mut(self, name) {
bevy_reflect::PartialReflect::try_apply(v, value)?;
}
}
} else {
return ::core::result::Result::Err(bevy_reflect::ApplyError::MismatchedKinds {
from_kind: bevy_reflect::PartialReflect::reflect_kind(value),
to_kind: bevy_reflect::ReflectKind::Struct,
});
}
::core::result::Result::Ok(())
}
#[inline]
fn reflect_kind(&self) -> bevy_reflect::ReflectKind {
bevy_reflect::ReflectKind::Struct
}
#[inline]
fn reflect_ref(&self) -> bevy_reflect::ReflectRef {
bevy_reflect::ReflectRef::Struct(self)
}
#[inline]
fn reflect_mut(&mut self) -> bevy_reflect::ReflectMut {
bevy_reflect::ReflectMut::Struct(self)
}
#[inline]
fn reflect_owned(self:
bevy_reflect::__macro_exports::alloc_utils::Box<Self>)
-> bevy_reflect::ReflectOwned {
bevy_reflect::ReflectOwned::Struct(self)
}
#[inline]
fn try_into_reflect(self:
bevy_reflect::__macro_exports::alloc_utils::Box<Self>)
->
::core::result::Result<bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::Reflect>,
bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::PartialReflect>> {
::core::result::Result::Ok(self)
}
#[inline]
fn try_as_reflect(&self)
-> ::core::option::Option<&dyn bevy_reflect::Reflect> {
::core::option::Option::Some(self)
}
#[inline]
fn try_as_reflect_mut(&mut self)
-> ::core::option::Option<&mut dyn bevy_reflect::Reflect> {
::core::option::Option::Some(self)
}
#[inline]
fn into_partial_reflect(self:
bevy_reflect::__macro_exports::alloc_utils::Box<Self>)
->
bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::PartialReflect> {
self
}
#[inline]
fn as_partial_reflect(&self)
-> &dyn bevy_reflect::PartialReflect {
self
}
#[inline]
fn as_partial_reflect_mut(&mut self)
-> &mut dyn bevy_reflect::PartialReflect {
self
}
fn reflect_partial_eq(&self,
value: &dyn bevy_reflect::PartialReflect)
-> ::core::option::Option<bool> {
let value =
<dyn bevy_reflect::PartialReflect>::try_downcast_ref::<Self>(value);
if let ::core::option::Option::Some(value) = value {
::core::option::Option::Some(::core::cmp::PartialEq::eq(self,
value))
} else { ::core::option::Option::Some(false) }
}
fn reflect_partial_cmp(&self,
value: &dyn bevy_reflect::PartialReflect)
-> ::core::option::Option<::core::cmp::Ordering> {
(bevy_reflect::structs::struct_partial_cmp)(self, value)
}
fn debug(&self, f: &mut ::core::fmt::Formatter<'_>)
-> ::core::fmt::Result {
::core::fmt::Debug::fmt(self, f)
}
#[inline]
fn reflect_clone(&self)
->
::core::result::Result<bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::Reflect>,
bevy_reflect::ReflectCloneError> {
::core::result::Result::Ok(bevy_reflect::__macro_exports::alloc_utils::Box::new(::core::clone::Clone::clone(self)))
}
}
impl bevy_reflect::FromReflect for ScreenSpaceAmbientOcclusion where
{
fn from_reflect(reflect: &dyn bevy_reflect::PartialReflect)
-> ::core::option::Option<Self> {
if let bevy_reflect::ReflectRef::Struct(__ref_struct) =
bevy_reflect::PartialReflect::reflect_ref(reflect) {
let mut __this =
<Self as ::core::default::Default>::default();
if let ::core::option::Option::Some(__field) =
(||
<ScreenSpaceAmbientOcclusionQualityLevel as
bevy_reflect::FromReflect>::from_reflect(bevy_reflect::structs::Struct::field(__ref_struct,
"quality_level")?))() {
__this.quality_level = __field;
}
if let ::core::option::Option::Some(__field) =
(||
<f32 as
bevy_reflect::FromReflect>::from_reflect(bevy_reflect::structs::Struct::field(__ref_struct,
"radius")?))() {
__this.radius = __field;
}
if let ::core::option::Option::Some(__field) =
(||
<f32 as
bevy_reflect::FromReflect>::from_reflect(bevy_reflect::structs::Struct::field(__ref_struct,
"constant_object_thickness")?))() {
__this.constant_object_thickness = __field;
}
::core::option::Option::Some(__this)
} else { ::core::option::Option::None }
}
}
};Reflect, #[automatically_derived]impl ::core::marker::StructuralPartialEq for
ScreenSpaceAmbientOcclusion {
}
#[automatically_derived]impl ::core::cmp::PartialEq for
ScreenSpaceAmbientOcclusion {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.radius == other.radius &&
self.constant_object_thickness ==
other.constant_object_thickness &&
self.quality_level == other.quality_level
}
}PartialEq, #[automatically_derived]impl ::core::clone::Clone for
ScreenSpaceAmbientOcclusion {
#[inline]
fn clone(&self) -> Self {
ScreenSpaceAmbientOcclusion {
quality_level: ::core::clone::Clone::clone(&self.quality_level),
radius: ::core::clone::Clone::clone(&self.radius),
constant_object_thickness: ::core::clone::Clone::clone(&self.constant_object_thickness),
}
}
}Clone, #[automatically_derived]impl ::core::fmt::Debug for
ScreenSpaceAmbientOcclusion {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_struct_field3_finish(f,
"ScreenSpaceAmbientOcclusion", "quality_level",
&self.quality_level, "radius", &self.radius,
"constant_object_thickness", &&self.constant_object_thickness)
}
}Debug)]
112#[reflect(Component, Debug, Default, PartialEq, Clone)]
113#[require(DepthPrepass, NormalPrepass)]
114#[extract_component_sync_target((Self, ScreenSpaceAmbientOcclusionResources, SsaoPipelineId, SsaoBindGroups))]
115#[doc(alias = "Ssao")]
116#[extract_app(RenderApp)]
117pub struct ScreenSpaceAmbientOcclusion {
118 pub quality_level: ScreenSpaceAmbientOcclusionQualityLevel,
120 pub radius: f32,
128 pub constant_object_thickness: f32,
133}
134
135const DEFAULT_SSAO_RADIUS: f32 = 0.5 * 1.457;
136
137impl Default for ScreenSpaceAmbientOcclusion {
138 fn default() -> Self {
139 Self {
140 quality_level: ScreenSpaceAmbientOcclusionQualityLevel::default(),
141 radius: DEFAULT_SSAO_RADIUS,
142 constant_object_thickness: 0.25,
143 }
144 }
145}
146
147#[derive(#[automatically_derived]#[doc(hidden)]unsafe impl ::core::clone::TrivialClone
for SsaoSettingsUniform {
}
#[automatically_derived]impl ::core::clone::Clone for SsaoSettingsUniform {
#[inline]
fn clone(&self) -> Self {
let _: ::core::clone::AssertParamIsClone<f32>;
*self
}
}Clone, #[automatically_derived]impl ::core::marker::Copy for SsaoSettingsUniform { }Copy, impl bevy_render::render_resource::encase::private::ShaderType for
SsaoSettingsUniform where
f32: bevy_render::render_resource::encase::private::ShaderType +
bevy_render::render_resource::encase::private::ShaderSize,
f32: bevy_render::render_resource::encase::private::ShaderType {
type ExtraMetadata =
bevy_render::render_resource::encase::private::StructMetadata<2usize>;
const METADATA:
bevy_render::render_resource::encase::private::Metadata<Self::ExtraMetadata>
=
{
let struct_alignment =
bevy_render::render_resource::encase::private::AlignmentValue::max([<f32
as
bevy_render::render_resource::encase::private::ShaderType>::METADATA.alignment(),
<f32 as
bevy_render::render_resource::encase::private::ShaderType>::METADATA.alignment()]);
let extra =
{
let mut paddings = [0; 2usize];
let mut offsets = [0; 2usize];
let mut offset = 0;
offset +=
<f32 as
bevy_render::render_resource::encase::private::ShaderSize>::SHADER_SIZE.get();
offsets[1usize] =
<f32 as
bevy_render::render_resource::encase::private::ShaderType>::METADATA.alignment().round_up(offset);
let padding =
<f32 as
bevy_render::render_resource::encase::private::ShaderType>::METADATA.alignment().padding_needed_for(offset);
offset += padding;
paddings[0usize] = padding;
offset +=
<f32 as
bevy_render::render_resource::encase::private::ShaderSize>::SHADER_SIZE.get();
paddings[1usize] =
struct_alignment.padding_needed_for(offset);
bevy_render::render_resource::encase::private::StructMetadata {
offsets,
paddings,
}
};
let min_size =
{
let mut offset = extra.offsets[2usize - 1];
offset +=
<f32 as
bevy_render::render_resource::encase::private::ShaderType>::METADATA.min_size().get();
bevy_render::render_resource::encase::private::SizeValue::new(struct_alignment.round_up(offset))
};
bevy_render::render_resource::encase::private::Metadata {
alignment: struct_alignment,
has_uniform_min_alignment: true,
min_size,
is_pod: false,
extra,
}
};
const UNIFORM_COMPAT_ASSERT: fn() =
||
bevy_render::render_resource::encase::private::consume_zsts([<f32
as
bevy_render::render_resource::encase::private::ShaderType>::UNIFORM_COMPAT_ASSERT(),
if let ::core::option::Option::Some(min_alignment) =
<f32 as
bevy_render::render_resource::encase::private::ShaderType>::METADATA.uniform_min_alignment()
{
let offset =
<Self as
bevy_render::render_resource::encase::private::ShaderType>::METADATA.offset(0usize);
{
#[allow(clippy :: equatable_if_let)]
if let false = min_alignment.is_aligned(offset) {
{
let mut fmt: ::const_panic::FmtArg =
::const_panic::FmtArg::DEBUG;
match &[::const_panic::StdWrapper(&match &"offset of field '"
{
reff =>
::const_panic::__::PanicFmt::PROOF.infer(reff).coerce(reff),
}.to_panicvals(fmt.set_display().set_alternate(false))).deref_panic_vals(),
::const_panic::StdWrapper(&match &"radius" {
reff =>
::const_panic::__::PanicFmt::PROOF.infer(reff).coerce(reff),
}.to_panicvals(fmt.set_display().set_alternate(false))).deref_panic_vals(),
::const_panic::StdWrapper(&match &"' must be a multiple of "
{
reff =>
::const_panic::__::PanicFmt::PROOF.infer(reff).coerce(reff),
}.to_panicvals(fmt.set_display().set_alternate(false))).deref_panic_vals(),
::const_panic::StdWrapper(&match &min_alignment.get() {
reff =>
::const_panic::__::PanicFmt::PROOF.infer(reff).coerce(reff),
}.to_panicvals(fmt)).deref_panic_vals(),
::const_panic::StdWrapper(&match &" (current offset: " {
reff =>
::const_panic::__::PanicFmt::PROOF.infer(reff).coerce(reff),
}.to_panicvals(fmt.set_display().set_alternate(false))).deref_panic_vals(),
::const_panic::StdWrapper(&match &offset {
reff =>
::const_panic::__::PanicFmt::PROOF.infer(reff).coerce(reff),
}.to_panicvals(fmt)).deref_panic_vals(),
::const_panic::StdWrapper(&match &")" {
reff =>
::const_panic::__::PanicFmt::PROOF.infer(reff).coerce(reff),
}.to_panicvals(fmt.set_display().set_alternate(false))).deref_panic_vals()]
{
args => ::const_panic::concat_panic(args),
}
}
}
}
}, (),
<f32 as
bevy_render::render_resource::encase::private::ShaderType>::UNIFORM_COMPAT_ASSERT(),
if let ::core::option::Option::Some(min_alignment) =
<f32 as
bevy_render::render_resource::encase::private::ShaderType>::METADATA.uniform_min_alignment()
{
let offset =
<Self as
bevy_render::render_resource::encase::private::ShaderType>::METADATA.offset(1usize);
{
#[allow(clippy :: equatable_if_let)]
if let false = min_alignment.is_aligned(offset) {
{
let mut fmt: ::const_panic::FmtArg =
::const_panic::FmtArg::DEBUG;
match &[::const_panic::StdWrapper(&match &"offset of field '"
{
reff =>
::const_panic::__::PanicFmt::PROOF.infer(reff).coerce(reff),
}.to_panicvals(fmt.set_display().set_alternate(false))).deref_panic_vals(),
::const_panic::StdWrapper(&match &"constant_object_thickness"
{
reff =>
::const_panic::__::PanicFmt::PROOF.infer(reff).coerce(reff),
}.to_panicvals(fmt.set_display().set_alternate(false))).deref_panic_vals(),
::const_panic::StdWrapper(&match &"' must be a multiple of "
{
reff =>
::const_panic::__::PanicFmt::PROOF.infer(reff).coerce(reff),
}.to_panicvals(fmt.set_display().set_alternate(false))).deref_panic_vals(),
::const_panic::StdWrapper(&match &min_alignment.get() {
reff =>
::const_panic::__::PanicFmt::PROOF.infer(reff).coerce(reff),
}.to_panicvals(fmt)).deref_panic_vals(),
::const_panic::StdWrapper(&match &" (current offset: " {
reff =>
::const_panic::__::PanicFmt::PROOF.infer(reff).coerce(reff),
}.to_panicvals(fmt.set_display().set_alternate(false))).deref_panic_vals(),
::const_panic::StdWrapper(&match &offset {
reff =>
::const_panic::__::PanicFmt::PROOF.infer(reff).coerce(reff),
}.to_panicvals(fmt)).deref_panic_vals(),
::const_panic::StdWrapper(&match &")" {
reff =>
::const_panic::__::PanicFmt::PROOF.infer(reff).coerce(reff),
}.to_panicvals(fmt.set_display().set_alternate(false))).deref_panic_vals()]
{
args => ::const_panic::concat_panic(args),
}
}
}
}
},
if let ::core::option::Option::Some(min_alignment) =
<f32 as
bevy_render::render_resource::encase::private::ShaderType>::METADATA.uniform_min_alignment()
{
let prev_offset =
<Self as
bevy_render::render_resource::encase::private::ShaderType>::METADATA.offset(1usize
- 1);
let offset =
<Self as
bevy_render::render_resource::encase::private::ShaderType>::METADATA.offset(1usize);
let diff = offset - prev_offset;
let prev_size =
<f32 as
bevy_render::render_resource::encase::private::ShaderSize>::SHADER_SIZE.get();
let prev_size = min_alignment.round_up(prev_size);
{
#[allow(clippy :: equatable_if_let)]
if let false = diff >= prev_size {
{
let mut fmt: ::const_panic::FmtArg =
::const_panic::FmtArg::DEBUG;
match &[::const_panic::StdWrapper(&match &"offset between fields '"
{
reff =>
::const_panic::__::PanicFmt::PROOF.infer(reff).coerce(reff),
}.to_panicvals(fmt.set_display().set_alternate(false))).deref_panic_vals(),
::const_panic::StdWrapper(&match &"radius" {
reff =>
::const_panic::__::PanicFmt::PROOF.infer(reff).coerce(reff),
}.to_panicvals(fmt.set_display().set_alternate(false))).deref_panic_vals(),
::const_panic::StdWrapper(&match &"' and '" {
reff =>
::const_panic::__::PanicFmt::PROOF.infer(reff).coerce(reff),
}.to_panicvals(fmt.set_display().set_alternate(false))).deref_panic_vals(),
::const_panic::StdWrapper(&match &"constant_object_thickness"
{
reff =>
::const_panic::__::PanicFmt::PROOF.infer(reff).coerce(reff),
}.to_panicvals(fmt.set_display().set_alternate(false))).deref_panic_vals(),
::const_panic::StdWrapper(&match &"' must be at least " {
reff =>
::const_panic::__::PanicFmt::PROOF.infer(reff).coerce(reff),
}.to_panicvals(fmt.set_display().set_alternate(false))).deref_panic_vals(),
::const_panic::StdWrapper(&match &min_alignment.get() {
reff =>
::const_panic::__::PanicFmt::PROOF.infer(reff).coerce(reff),
}.to_panicvals(fmt)).deref_panic_vals(),
::const_panic::StdWrapper(&match &" (currently: " {
reff =>
::const_panic::__::PanicFmt::PROOF.infer(reff).coerce(reff),
}.to_panicvals(fmt.set_display().set_alternate(false))).deref_panic_vals(),
::const_panic::StdWrapper(&match &diff {
reff =>
::const_panic::__::PanicFmt::PROOF.infer(reff).coerce(reff),
}.to_panicvals(fmt)).deref_panic_vals(),
::const_panic::StdWrapper(&match &")" {
reff =>
::const_panic::__::PanicFmt::PROOF.infer(reff).coerce(reff),
}.to_panicvals(fmt.set_display().set_alternate(false))).deref_panic_vals()]
{
args => ::const_panic::concat_panic(args),
}
}
}
}
}]);
fn size(&self) -> ::core::num::NonZeroU64 {
let mut offset = Self::METADATA.last_offset();
offset +=
bevy_render::render_resource::encase::private::ShaderType::size(&self.constant_object_thickness).get();
bevy_render::render_resource::encase::private::SizeValue::new(Self::METADATA.alignment().round_up(offset)).0
}
}
impl bevy_render::render_resource::encase::private::WriteInto for
SsaoSettingsUniform where
Self: bevy_render::render_resource::encase::private::ShaderType<ExtraMetadata
= bevy_render::render_resource::encase::private::StructMetadata<2usize>>,
for<'__> f32: bevy_render::render_resource::encase::private::WriteInto,
for<'__> f32: bevy_render::render_resource::encase::private::WriteInto {
#[inline]
fn write_into<B: bevy_render::render_resource::encase::private::BufferMut>(&self,
writer:
&mut bevy_render::render_resource::encase::private::Writer<B>) {
bevy_render::render_resource::encase::private::WriteInto::write_into(&self.radius,
writer);
bevy_render::render_resource::encase::private::Writer::advance(writer,
<Self as
bevy_render::render_resource::encase::private::ShaderType>::METADATA.padding(0usize)
as ::core::primitive::usize);
bevy_render::render_resource::encase::private::WriteInto::write_into(&self.constant_object_thickness,
writer);
bevy_render::render_resource::encase::private::Writer::advance(writer,
<Self as
bevy_render::render_resource::encase::private::ShaderType>::METADATA.padding(1usize)
as ::core::primitive::usize);
}
}
impl bevy_render::render_resource::encase::private::ReadFrom for
SsaoSettingsUniform where
Self: bevy_render::render_resource::encase::private::ShaderType<ExtraMetadata
= bevy_render::render_resource::encase::private::StructMetadata<2usize>>,
for<'__> f32: bevy_render::render_resource::encase::private::ReadFrom,
for<'__> f32: bevy_render::render_resource::encase::private::ReadFrom {
#[inline]
fn read_from<B: bevy_render::render_resource::encase::private::BufferRef>(&mut self,
reader:
&mut bevy_render::render_resource::encase::private::Reader<B>) {
bevy_render::render_resource::encase::private::ReadFrom::read_from(&mut self.radius,
reader);
bevy_render::render_resource::encase::private::Reader::advance(reader,
<Self as
bevy_render::render_resource::encase::private::ShaderType>::METADATA.padding(0usize)
as ::core::primitive::usize);
bevy_render::render_resource::encase::private::ReadFrom::read_from(&mut self.constant_object_thickness,
reader);
bevy_render::render_resource::encase::private::Reader::advance(reader,
<Self as
bevy_render::render_resource::encase::private::ShaderType>::METADATA.padding(1usize)
as ::core::primitive::usize);
}
}
impl bevy_render::render_resource::encase::private::CreateFrom for
SsaoSettingsUniform where
Self: bevy_render::render_resource::encase::private::ShaderType<ExtraMetadata
= bevy_render::render_resource::encase::private::StructMetadata<2usize>>,
for<'__> f32: bevy_render::render_resource::encase::private::CreateFrom,
for<'__> f32: bevy_render::render_resource::encase::private::CreateFrom {
#[inline]
fn create_from<B: bevy_render::render_resource::encase::private::BufferRef>(reader:
&mut bevy_render::render_resource::encase::private::Reader<B>)
-> Self {
let radius =
bevy_render::render_resource::encase::private::CreateFrom::create_from(reader);
bevy_render::render_resource::encase::private::Reader::advance(reader,
<Self as
bevy_render::render_resource::encase::private::ShaderType>::METADATA.padding(0usize)
as ::core::primitive::usize);
let constant_object_thickness =
bevy_render::render_resource::encase::private::CreateFrom::create_from(reader);
bevy_render::render_resource::encase::private::Reader::advance(reader,
<Self as
bevy_render::render_resource::encase::private::ShaderType>::METADATA.padding(1usize)
as ::core::primitive::usize);
{
let mut uninit_struct =
::core::mem::MaybeUninit::<Self>::uninit();
let ptr =
::core::mem::MaybeUninit::as_mut_ptr(&mut uninit_struct);
let field_ptr = unsafe { &raw mut (*ptr).radius };
unsafe { field_ptr.write(radius) };
;
let field_ptr =
unsafe { &raw mut (*ptr).constant_object_thickness };
unsafe { field_ptr.write(constant_object_thickness) };
;
unsafe { ::core::mem::MaybeUninit::assume_init(uninit_struct) }
}
}
}
impl bevy_render::render_resource::encase::private::ShaderSize for
SsaoSettingsUniform where
f32: bevy_render::render_resource::encase::private::ShaderSize,
f32: bevy_render::render_resource::encase::private::ShaderSize {}ShaderType)]
148struct SsaoSettingsUniform {
149 radius: f32,
150 constant_object_thickness: f32,
151}
152
153#[derive(const _: () =
{
impl bevy_reflect::GetTypeRegistration for
ScreenSpaceAmbientOcclusionQualityLevel where {
fn get_type_registration() -> bevy_reflect::TypeRegistration {
let mut registration =
bevy_reflect::TypeRegistration::of::<Self>();
registration.insert(<bevy_reflect::ReflectFromPtr as
bevy_reflect::CreateTypeData<Self>>::create_type_data(()));
registration.insert(<bevy_reflect::ReflectFromReflect as
bevy_reflect::CreateTypeData<Self>>::create_type_data(()));
registration.register_type_data_with::<ReflectDefault, Self,
_>(());
registration
}
#[inline(never)]
fn register_type_dependencies(registry:
&mut bevy_reflect::TypeRegistry) {
<u32 as
bevy_reflect::__macro_exports::RegisterForReflection>::__register(registry);
}
}
impl bevy_reflect::Typed for ScreenSpaceAmbientOcclusionQualityLevel
where {
#[inline(never)]
fn type_info() -> &'static bevy_reflect::TypeInfo {
static CELL: bevy_reflect::utility::NonGenericTypeInfoCell =
bevy_reflect::utility::NonGenericTypeInfoCell::new();
CELL.get_or_set(#[inline(never)] ||
{
bevy_reflect::TypeInfo::Enum(bevy_reflect::enums::EnumInfo::new::<Self>(&[bevy_reflect::enums::VariantInfo::Unit(bevy_reflect::enums::UnitVariantInfo::new("Low")),
bevy_reflect::enums::VariantInfo::Unit(bevy_reflect::enums::UnitVariantInfo::new("Medium")),
bevy_reflect::enums::VariantInfo::Unit(bevy_reflect::enums::UnitVariantInfo::new("High")),
bevy_reflect::enums::VariantInfo::Unit(bevy_reflect::enums::UnitVariantInfo::new("Ultra")),
bevy_reflect::enums::VariantInfo::Struct(bevy_reflect::enums::StructVariantInfo::new("Custom",
&[bevy_reflect::NamedField::new::<u32>("slice_count"),
bevy_reflect::NamedField::new::<u32>("samples_per_slice_side")]))]))
})
}
}
#[allow(deprecated, reason =
"derives on a deprecated type shouldn't be considered a usage")]
impl bevy_reflect::TypePath for
ScreenSpaceAmbientOcclusionQualityLevel where {
fn type_path() -> &'static str {
"bevy_pbr::ssao::ScreenSpaceAmbientOcclusionQualityLevel"
}
fn short_type_path() -> &'static str {
"ScreenSpaceAmbientOcclusionQualityLevel"
}
fn type_ident() -> ::core::option::Option<&'static str> {
::core::option::Option::Some("ScreenSpaceAmbientOcclusionQualityLevel")
}
fn crate_name() -> ::core::option::Option<&'static str> {
::core::option::Option::Some("bevy_pbr::ssao".split(':').next().unwrap())
}
fn module_path() -> ::core::option::Option<&'static str> {
::core::option::Option::Some("bevy_pbr::ssao")
}
}
impl bevy_reflect::Reflect for ScreenSpaceAmbientOcclusionQualityLevel
where {
#[inline]
fn into_any(self:
bevy_reflect::__macro_exports::alloc_utils::Box<Self>)
->
bevy_reflect::__macro_exports::alloc_utils::Box<dyn ::core::any::Any> {
self
}
#[inline]
fn as_any(&self) -> &dyn ::core::any::Any { self }
#[inline]
fn as_any_mut(&mut self) -> &mut dyn ::core::any::Any { self }
#[inline]
fn into_reflect(self:
bevy_reflect::__macro_exports::alloc_utils::Box<Self>)
->
bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::Reflect> {
self
}
#[inline]
fn as_reflect(&self) -> &dyn bevy_reflect::Reflect { self }
#[inline]
fn as_reflect_mut(&mut self) -> &mut dyn bevy_reflect::Reflect {
self
}
#[inline]
fn set(&mut self,
value:
bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::Reflect>)
->
::core::result::Result<(),
bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::Reflect>> {
*self = <dyn bevy_reflect::Reflect>::take(value)?;
::core::result::Result::Ok(())
}
}
#[allow(non_upper_case_globals)]
const _: () =
{
static __INVENTORY: ::inventory::Node =
::inventory::Node {
value: &{
bevy_reflect::__macro_exports::auto_register::AutomaticReflectRegistrations(<ScreenSpaceAmbientOcclusionQualityLevel
as
bevy_reflect::__macro_exports::auto_register::RegisterForReflection>::__register)
},
next: ::inventory::__private::UnsafeCell::new(::inventory::__private::Option::None),
};
#[link_section = ".text.startup"]
unsafe extern "C" fn __ctor() {
unsafe {
::inventory::ErasedNode::submit(__INVENTORY.value,
&__INVENTORY)
}
}
#[used]
#[link_section = ".init_array"]
static __CTOR: unsafe extern "C" fn() = __ctor;
};
impl bevy_reflect::enums::Enum for
ScreenSpaceAmbientOcclusionQualityLevel where {
fn field(&self, __name_param: &str)
-> ::core::option::Option<&dyn bevy_reflect::PartialReflect> {
match self {
ScreenSpaceAmbientOcclusionQualityLevel::Custom {
slice_count: __value, .. } if __name_param == "slice_count"
=> ::core::option::Option::Some(__value),
ScreenSpaceAmbientOcclusionQualityLevel::Custom {
samples_per_slice_side: __value, .. } if
__name_param == "samples_per_slice_side" =>
::core::option::Option::Some(__value),
_ => ::core::option::Option::None,
}
}
fn field_at(&self, __index_param: usize)
-> ::core::option::Option<&dyn bevy_reflect::PartialReflect> {
match self {
ScreenSpaceAmbientOcclusionQualityLevel::Custom {
slice_count: __value, .. } if __index_param == 0usize =>
::core::option::Option::Some(__value),
ScreenSpaceAmbientOcclusionQualityLevel::Custom {
samples_per_slice_side: __value, .. } if
__index_param == 1usize =>
::core::option::Option::Some(__value),
_ => ::core::option::Option::None,
}
}
fn field_mut(&mut self, __name_param: &str)
->
::core::option::Option<&mut dyn bevy_reflect::PartialReflect> {
match self {
ScreenSpaceAmbientOcclusionQualityLevel::Custom {
slice_count: __value, .. } if __name_param == "slice_count"
=> ::core::option::Option::Some(__value),
ScreenSpaceAmbientOcclusionQualityLevel::Custom {
samples_per_slice_side: __value, .. } if
__name_param == "samples_per_slice_side" =>
::core::option::Option::Some(__value),
_ => ::core::option::Option::None,
}
}
fn field_at_mut(&mut self, __index_param: usize)
->
::core::option::Option<&mut dyn bevy_reflect::PartialReflect> {
match self {
ScreenSpaceAmbientOcclusionQualityLevel::Custom {
slice_count: __value, .. } if __index_param == 0usize =>
::core::option::Option::Some(__value),
ScreenSpaceAmbientOcclusionQualityLevel::Custom {
samples_per_slice_side: __value, .. } if
__index_param == 1usize =>
::core::option::Option::Some(__value),
_ => ::core::option::Option::None,
}
}
fn index_of(&self, __name_param: &str)
-> ::core::option::Option<usize> {
match self {
ScreenSpaceAmbientOcclusionQualityLevel::Custom { .. } if
__name_param == "slice_count" =>
::core::option::Option::Some(0usize),
ScreenSpaceAmbientOcclusionQualityLevel::Custom { .. } if
__name_param == "samples_per_slice_side" =>
::core::option::Option::Some(1usize),
_ => ::core::option::Option::None,
}
}
fn name_at(&self, __index_param: usize)
-> ::core::option::Option<&str> {
match self {
ScreenSpaceAmbientOcclusionQualityLevel::Custom { .. } if
__index_param == 0usize =>
::core::option::Option::Some("slice_count"),
ScreenSpaceAmbientOcclusionQualityLevel::Custom { .. } if
__index_param == 1usize =>
::core::option::Option::Some("samples_per_slice_side"),
_ => ::core::option::Option::None,
}
}
fn iter_fields(&self) -> bevy_reflect::enums::VariantFieldIter {
bevy_reflect::enums::VariantFieldIter::new(self)
}
#[inline]
fn field_len(&self) -> usize {
match self {
ScreenSpaceAmbientOcclusionQualityLevel::Low { .. } =>
0usize,
ScreenSpaceAmbientOcclusionQualityLevel::Medium { .. } =>
0usize,
ScreenSpaceAmbientOcclusionQualityLevel::High { .. } =>
0usize,
ScreenSpaceAmbientOcclusionQualityLevel::Ultra { .. } =>
0usize,
ScreenSpaceAmbientOcclusionQualityLevel::Custom { .. } =>
2usize,
_ => 0,
}
}
#[inline]
fn variant_name(&self) -> &str {
match self {
ScreenSpaceAmbientOcclusionQualityLevel::Low { .. } =>
"Low",
ScreenSpaceAmbientOcclusionQualityLevel::Medium { .. } =>
"Medium",
ScreenSpaceAmbientOcclusionQualityLevel::High { .. } =>
"High",
ScreenSpaceAmbientOcclusionQualityLevel::Ultra { .. } =>
"Ultra",
ScreenSpaceAmbientOcclusionQualityLevel::Custom { .. } =>
"Custom",
_ => ::core::panicking::unreachable_default_msg(),
}
}
#[inline]
fn variant_index(&self) -> usize {
match self {
ScreenSpaceAmbientOcclusionQualityLevel::Low { .. } =>
0usize,
ScreenSpaceAmbientOcclusionQualityLevel::Medium { .. } =>
1usize,
ScreenSpaceAmbientOcclusionQualityLevel::High { .. } =>
2usize,
ScreenSpaceAmbientOcclusionQualityLevel::Ultra { .. } =>
3usize,
ScreenSpaceAmbientOcclusionQualityLevel::Custom { .. } =>
4usize,
_ => ::core::panicking::unreachable_default_msg(),
}
}
#[inline]
fn variant_type(&self) -> bevy_reflect::enums::VariantType {
match self {
ScreenSpaceAmbientOcclusionQualityLevel::Low { .. } =>
bevy_reflect::enums::VariantType::Unit,
ScreenSpaceAmbientOcclusionQualityLevel::Medium { .. } =>
bevy_reflect::enums::VariantType::Unit,
ScreenSpaceAmbientOcclusionQualityLevel::High { .. } =>
bevy_reflect::enums::VariantType::Unit,
ScreenSpaceAmbientOcclusionQualityLevel::Ultra { .. } =>
bevy_reflect::enums::VariantType::Unit,
ScreenSpaceAmbientOcclusionQualityLevel::Custom { .. } =>
bevy_reflect::enums::VariantType::Struct,
_ => ::core::panicking::unreachable_default_msg(),
}
}
fn to_dynamic_enum(&self)
->
::core::result::Result<bevy_reflect::enums::DynamicEnum,
bevy_reflect::ReflectCloneError> {
bevy_reflect::enums::DynamicEnum::try_from_ref::<Self>(self)
}
}
impl bevy_reflect::PartialReflect for
ScreenSpaceAmbientOcclusionQualityLevel where {
#[inline]
fn get_represented_type_info(&self)
-> ::core::option::Option<&'static bevy_reflect::TypeInfo> {
::core::option::Option::Some(<Self as
bevy_reflect::Typed>::type_info())
}
#[inline]
fn try_apply(&mut self,
__value_param: &dyn bevy_reflect::PartialReflect)
-> ::core::result::Result<(), bevy_reflect::ApplyError> {
if let bevy_reflect::ReflectRef::Enum(__value_param) =
bevy_reflect::PartialReflect::reflect_ref(__value_param) {
if bevy_reflect::enums::Enum::variant_name(self) ==
bevy_reflect::enums::Enum::variant_name(__value_param) {
match bevy_reflect::enums::Enum::variant_type(__value_param)
{
bevy_reflect::enums::VariantType::Struct => {
for field in
bevy_reflect::enums::Enum::iter_fields(__value_param) {
let name = field.name().unwrap();
if let ::core::option::Option::Some(v) =
bevy_reflect::enums::Enum::field_mut(self, name) {
bevy_reflect::PartialReflect::try_apply(v, field.value())?;
}
}
}
bevy_reflect::enums::VariantType::Tuple => {
for (index, field) in
::core::iter::Iterator::enumerate(bevy_reflect::enums::Enum::iter_fields(__value_param))
{
if let ::core::option::Option::Some(v) =
bevy_reflect::enums::Enum::field_at_mut(self, index) {
bevy_reflect::PartialReflect::try_apply(v, field.value())?;
}
}
}
_ => {}
}
} else {
match bevy_reflect::enums::Enum::variant_name(__value_param)
{
"Low" => {
*self = ScreenSpaceAmbientOcclusionQualityLevel::Low {}
}
"Medium" => {
*self = ScreenSpaceAmbientOcclusionQualityLevel::Medium {}
}
"High" => {
*self = ScreenSpaceAmbientOcclusionQualityLevel::High {}
}
"Ultra" => {
*self = ScreenSpaceAmbientOcclusionQualityLevel::Ultra {}
}
"Custom" => {
*self =
ScreenSpaceAmbientOcclusionQualityLevel::Custom {
slice_count: {
let __slice_count = __value_param.field("slice_count");
let __slice_count =
__slice_count.ok_or(bevy_reflect::ApplyError::MissingEnumField {
variant_name: ::core::convert::Into::into("Custom"),
field_name: ::core::convert::Into::into("slice_count"),
})?;
<u32 as
bevy_reflect::FromReflect>::from_reflect(__slice_count).ok_or(bevy_reflect::ApplyError::MismatchedTypes {
from_type: ::core::convert::Into::into(bevy_reflect::DynamicTypePath::reflect_type_path(__slice_count)),
to_type: ::core::convert::Into::into(<u32 as
bevy_reflect::TypePath>::type_path()),
})?
},
samples_per_slice_side: {
let __samples_per_slice_side =
__value_param.field("samples_per_slice_side");
let __samples_per_slice_side =
__samples_per_slice_side.ok_or(bevy_reflect::ApplyError::MissingEnumField {
variant_name: ::core::convert::Into::into("Custom"),
field_name: ::core::convert::Into::into("samples_per_slice_side"),
})?;
<u32 as
bevy_reflect::FromReflect>::from_reflect(__samples_per_slice_side).ok_or(bevy_reflect::ApplyError::MismatchedTypes {
from_type: ::core::convert::Into::into(bevy_reflect::DynamicTypePath::reflect_type_path(__samples_per_slice_side)),
to_type: ::core::convert::Into::into(<u32 as
bevy_reflect::TypePath>::type_path()),
})?
},
}
}
name => {
return ::core::result::Result::Err(bevy_reflect::ApplyError::UnknownVariant {
enum_name: ::core::convert::Into::into(bevy_reflect::DynamicTypePath::reflect_type_path(self)),
variant_name: ::core::convert::Into::into(name),
});
}
}
}
} else {
return ::core::result::Result::Err(bevy_reflect::ApplyError::MismatchedKinds {
from_kind: bevy_reflect::PartialReflect::reflect_kind(__value_param),
to_kind: bevy_reflect::ReflectKind::Enum,
});
}
::core::result::Result::Ok(())
}
fn reflect_kind(&self) -> bevy_reflect::ReflectKind {
bevy_reflect::ReflectKind::Enum
}
fn reflect_ref(&self) -> bevy_reflect::ReflectRef {
bevy_reflect::ReflectRef::Enum(self)
}
fn reflect_mut(&mut self) -> bevy_reflect::ReflectMut {
bevy_reflect::ReflectMut::Enum(self)
}
fn reflect_owned(self:
bevy_reflect::__macro_exports::alloc_utils::Box<Self>)
-> bevy_reflect::ReflectOwned {
bevy_reflect::ReflectOwned::Enum(self)
}
#[inline]
fn try_into_reflect(self:
bevy_reflect::__macro_exports::alloc_utils::Box<Self>)
->
::core::result::Result<bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::Reflect>,
bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::PartialReflect>> {
::core::result::Result::Ok(self)
}
#[inline]
fn try_as_reflect(&self)
-> ::core::option::Option<&dyn bevy_reflect::Reflect> {
::core::option::Option::Some(self)
}
#[inline]
fn try_as_reflect_mut(&mut self)
-> ::core::option::Option<&mut dyn bevy_reflect::Reflect> {
::core::option::Option::Some(self)
}
#[inline]
fn into_partial_reflect(self:
bevy_reflect::__macro_exports::alloc_utils::Box<Self>)
->
bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::PartialReflect> {
self
}
#[inline]
fn as_partial_reflect(&self)
-> &dyn bevy_reflect::PartialReflect {
self
}
#[inline]
fn as_partial_reflect_mut(&mut self)
-> &mut dyn bevy_reflect::PartialReflect {
self
}
fn reflect_hash(&self) -> ::core::option::Option<u64> {
use ::core::hash::{Hash, Hasher};
let mut hasher = bevy_reflect::utility::reflect_hasher();
Hash::hash(&::core::any::Any::type_id(self), &mut hasher);
Hash::hash(self, &mut hasher);
::core::option::Option::Some(Hasher::finish(&hasher))
}
fn reflect_partial_eq(&self,
value: &dyn bevy_reflect::PartialReflect)
-> ::core::option::Option<bool> {
let value =
<dyn bevy_reflect::PartialReflect>::try_downcast_ref::<Self>(value);
if let ::core::option::Option::Some(value) = value {
::core::option::Option::Some(::core::cmp::PartialEq::eq(self,
value))
} else { ::core::option::Option::Some(false) }
}
fn reflect_partial_cmp(&self,
value: &dyn bevy_reflect::PartialReflect)
-> ::core::option::Option<::core::cmp::Ordering> {
(bevy_reflect::enums::enum_partial_cmp)(self, value)
}
#[inline]
fn reflect_clone(&self)
->
::core::result::Result<bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::Reflect>,
bevy_reflect::ReflectCloneError> {
::core::result::Result::Ok(bevy_reflect::__macro_exports::alloc_utils::Box::new(::core::clone::Clone::clone(self)))
}
}
impl bevy_reflect::FromReflect for
ScreenSpaceAmbientOcclusionQualityLevel where {
fn from_reflect(__param0: &dyn bevy_reflect::PartialReflect)
-> ::core::option::Option<Self> {
if let bevy_reflect::ReflectRef::Enum(__param0) =
bevy_reflect::PartialReflect::reflect_ref(__param0) {
match bevy_reflect::enums::Enum::variant_name(__param0) {
"Low" =>
::core::option::Option::Some(ScreenSpaceAmbientOcclusionQualityLevel::Low {}),
"Medium" =>
::core::option::Option::Some(ScreenSpaceAmbientOcclusionQualityLevel::Medium {}),
"High" =>
::core::option::Option::Some(ScreenSpaceAmbientOcclusionQualityLevel::High {}),
"Ultra" =>
::core::option::Option::Some(ScreenSpaceAmbientOcclusionQualityLevel::Ultra {}),
"Custom" =>
::core::option::Option::Some(ScreenSpaceAmbientOcclusionQualityLevel::Custom {
slice_count: {
let __slice_count = __param0.field("slice_count");
let __slice_count = __slice_count?;
<u32 as
bevy_reflect::FromReflect>::from_reflect(__slice_count)?
},
samples_per_slice_side: {
let __samples_per_slice_side =
__param0.field("samples_per_slice_side");
let __samples_per_slice_side = __samples_per_slice_side?;
<u32 as
bevy_reflect::FromReflect>::from_reflect(__samples_per_slice_side)?
},
}),
name => ::core::option::Option::None,
}
} else { ::core::option::Option::None }
}
}
};Reflect, #[automatically_derived]impl ::core::marker::StructuralPartialEq for
ScreenSpaceAmbientOcclusionQualityLevel {
}
#[automatically_derived]impl ::core::cmp::PartialEq for
ScreenSpaceAmbientOcclusionQualityLevel {
#[inline]
fn eq(&self, other: &Self) -> bool {
::core::intrinsics::discriminant_value(self) ==
::core::intrinsics::discriminant_value(other) &&
match (self, other) {
(Self::Custom {
slice_count: __self_0, samples_per_slice_side: __self_1 },
Self::Custom {
slice_count: __arg1_0, samples_per_slice_side: __arg1_1 })
=> __self_0 == __arg1_0 && __self_1 == __arg1_1,
_ => true,
}
}
}PartialEq, #[automatically_derived]impl ::core::cmp::Eq for
ScreenSpaceAmbientOcclusionQualityLevel {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_fields_are_eq(&self) {
let _: ::core::cmp::AssertParamIsEq<u32>;
}
}Eq, #[automatically_derived]impl ::core::hash::Hash for
ScreenSpaceAmbientOcclusionQualityLevel {
#[inline]
fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) {
::core::hash::Hash::hash(&::core::intrinsics::discriminant_value(self),
state);
match self {
Self::Custom {
slice_count: __self_0, samples_per_slice_side: __self_1 } => {
::core::hash::Hash::hash(__self_0, state);
::core::hash::Hash::hash(__self_1, state)
}
_ => {}
}
}
}Hash, #[automatically_derived]#[doc(hidden)]unsafe impl ::core::clone::TrivialClone
for ScreenSpaceAmbientOcclusionQualityLevel {
}
#[automatically_derived]impl ::core::clone::Clone for
ScreenSpaceAmbientOcclusionQualityLevel {
#[inline]
fn clone(&self) -> Self {
let _: ::core::clone::AssertParamIsClone<u32>;
*self
}
}Clone, #[automatically_derived]impl ::core::marker::Copy for
ScreenSpaceAmbientOcclusionQualityLevel {
}Copy, #[automatically_derived]impl ::core::default::Default for
ScreenSpaceAmbientOcclusionQualityLevel {
#[inline]
fn default() -> Self { Self::High }
}Default, #[automatically_derived]impl ::core::fmt::Debug for
ScreenSpaceAmbientOcclusionQualityLevel {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
match self {
Self::Low => ::core::fmt::Formatter::write_str(f, "Low"),
Self::Medium => ::core::fmt::Formatter::write_str(f, "Medium"),
Self::High => ::core::fmt::Formatter::write_str(f, "High"),
Self::Ultra => ::core::fmt::Formatter::write_str(f, "Ultra"),
Self::Custom {
slice_count: __self_0, samples_per_slice_side: __self_1 } =>
::core::fmt::Formatter::debug_struct_field2_finish(f,
"Custom", "slice_count", __self_0, "samples_per_slice_side",
&__self_1),
}
}
}Debug)]
154#[reflect(PartialEq, Hash, Clone, Default)]
155pub enum ScreenSpaceAmbientOcclusionQualityLevel {
156 Low,
157 Medium,
158 #[default]
159 High,
160 Ultra,
161 Custom {
162 slice_count: u32,
164 samples_per_slice_side: u32,
166 },
167}
168
169impl ScreenSpaceAmbientOcclusionQualityLevel {
170 fn sample_counts(&self) -> (u32, u32) {
171 match self {
172 Self::Low => (1, 2), Self::Medium => (2, 2), Self::High => (3, 3), Self::Ultra => (9, 3), Self::Custom {
177 slice_count: slices,
178 samples_per_slice_side,
179 } => (*slices, *samples_per_slice_side),
180 }
181 }
182}
183
184fn ssao(
185 view: ViewQuery<(
186 &ExtractedCamera,
187 &SsaoPipelineId,
188 &SsaoBindGroups,
189 &ViewUniformOffset,
190 )>,
191 pipelines: Res<SsaoPipelines>,
192 pipeline_cache: Res<PipelineCache>,
193 mut ctx: RenderContext,
194) {
195 let (camera, pipeline_id, bind_groups, view_uniform_offset) = view.into_inner();
196
197 let (
198 Some(camera_size),
199 Some(preprocess_depth_pipeline),
200 Some(spatial_denoise_pipeline),
201 Some(ssao_pipeline),
202 ) = (
203 camera.physical_viewport_size,
204 pipeline_cache.get_compute_pipeline(pipelines.preprocess_depth_pipeline),
205 pipeline_cache.get_compute_pipeline(pipelines.spatial_denoise_pipeline),
206 pipeline_cache.get_compute_pipeline(pipeline_id.0),
207 )
208 else {
209 return;
210 };
211
212 let diagnostics = ctx.diagnostic_recorder();
213 let diagnostics = diagnostics.as_deref();
214 let time_span = diagnostics.time_span(ctx.command_encoder(), "ssao");
215
216 let command_encoder = ctx.command_encoder();
217 command_encoder.push_debug_group("ssao");
218
219 {
220 let mut preprocess_depth_pass =
221 command_encoder.begin_compute_pass(&ComputePassDescriptor {
222 label: Some("ssao_preprocess_depth"),
223 timestamp_writes: None,
224 });
225 preprocess_depth_pass.set_pipeline(preprocess_depth_pipeline);
226 preprocess_depth_pass.set_bind_group(0, &bind_groups.preprocess_depth_bind_group, &[]);
227 preprocess_depth_pass.set_bind_group(
228 1,
229 &bind_groups.common_bind_group,
230 &[view_uniform_offset.offset],
231 );
232 preprocess_depth_pass.dispatch_workgroups(
233 camera_size.x.div_ceil(16),
234 camera_size.y.div_ceil(16),
235 1,
236 );
237 }
238
239 {
240 let mut ssao_pass = command_encoder.begin_compute_pass(&ComputePassDescriptor {
241 label: Some("ssao"),
242 timestamp_writes: None,
243 });
244 ssao_pass.set_pipeline(ssao_pipeline);
245 ssao_pass.set_bind_group(0, &bind_groups.ssao_bind_group, &[]);
246 ssao_pass.set_bind_group(
247 1,
248 &bind_groups.common_bind_group,
249 &[view_uniform_offset.offset],
250 );
251 ssao_pass.dispatch_workgroups(camera_size.x.div_ceil(8), camera_size.y.div_ceil(8), 1);
252 }
253
254 {
255 let mut spatial_denoise_pass = command_encoder.begin_compute_pass(&ComputePassDescriptor {
256 label: Some("ssao_spatial_denoise"),
257 timestamp_writes: None,
258 });
259 spatial_denoise_pass.set_pipeline(spatial_denoise_pipeline);
260 spatial_denoise_pass.set_bind_group(0, &bind_groups.spatial_denoise_bind_group, &[]);
261 spatial_denoise_pass.set_bind_group(
262 1,
263 &bind_groups.common_bind_group,
264 &[view_uniform_offset.offset],
265 );
266 spatial_denoise_pass.dispatch_workgroups(
267 camera_size.x.div_ceil(8),
268 camera_size.y.div_ceil(8),
269 1,
270 );
271 }
272
273 command_encoder.pop_debug_group();
274 time_span.end(ctx.command_encoder());
275}
276
277#[derive(impl bevy_ecs::component::Component for SsaoPipelines where
Self: ::core::marker::Send + ::core::marker::Sync + 'static {
const STORAGE_TYPE: bevy_ecs::component::StorageType =
bevy_ecs::component::StorageType::SparseSet;
type Mutability = bevy_ecs::component::Mutable;
fn register_required_components(_requiree:
bevy_ecs::component::ComponentId,
required_components:
&mut bevy_ecs::component::RequiredComponentsRegistrator) {
let resource_component_id =
if let ::core::option::Option::Some(id) =
required_components.components_registrator().component_id::<SsaoPipelines>()
{
id
} else {
required_components.components_registrator().register_component::<SsaoPipelines>()
};
required_components.register_required::<bevy_ecs::resource::IsResource>(move
||
bevy_ecs::resource::IsResource::new(resource_component_id));
}
fn clone_behavior() -> bevy_ecs::component::ComponentCloneBehavior {
use bevy_ecs::component::{
DefaultCloneBehaviorBase, DefaultCloneBehaviorViaClone,
};
(&&&bevy_ecs::component::DefaultCloneBehaviorSpecialization::<Self>::default()).default_clone_behavior()
}
fn relationship_accessor()
->
::core::option::Option<bevy_ecs::relationship::ComponentRelationshipAccessor<Self>> {
::core::option::Option::None
}
}
impl bevy_ecs::resource::Resource for SsaoPipelines where
Self: ::core::marker::Send + ::core::marker::Sync + 'static {}Resource)]
278struct SsaoPipelines {
279 preprocess_depth_pipeline: CachedComputePipelineId,
280 spatial_denoise_pipeline: CachedComputePipelineId,
281
282 common_bind_group_layout: BindGroupLayoutDescriptor,
283 preprocess_depth_bind_group_layout: BindGroupLayoutDescriptor,
284 ssao_bind_group_layout: BindGroupLayoutDescriptor,
285 spatial_denoise_bind_group_layout: BindGroupLayoutDescriptor,
286
287 hilbert_index_lut: TextureView,
288 point_clamp_sampler: Sampler,
289 linear_clamp_sampler: Sampler,
290
291 shader: Handle<Shader>,
292 depth_format: TextureFormat,
293}
294
295impl FromWorld for SsaoPipelines {
296 fn from_world(world: &mut World) -> Self {
297 let render_device = world.resource::<RenderDevice>();
298 let render_queue = world.resource::<RenderQueue>();
299 let pipeline_cache = world.resource::<PipelineCache>();
300
301 let render_adapter = world.resource::<RenderAdapter>();
303 let depth_format = if render_adapter
304 .get_texture_format_features(TextureFormat::R16Float)
305 .allowed_usages
306 .contains(TextureUsages::STORAGE_BINDING)
307 {
308 TextureFormat::R16Float
309 } else {
310 TextureFormat::R32Float
311 };
312
313 let hilbert_index_lut = render_device
314 .create_texture_with_data(
315 render_queue,
316 &(TextureDescriptor {
317 label: Some("ssao_hilbert_index_lut"),
318 size: Extent3d {
319 width: HILBERT_WIDTH as u32,
320 height: HILBERT_WIDTH as u32,
321 depth_or_array_layers: 1,
322 },
323 mip_level_count: 1,
324 sample_count: 1,
325 dimension: TextureDimension::D2,
326 format: TextureFormat::R16Uint,
327 usage: TextureUsages::TEXTURE_BINDING,
328 view_formats: &[],
329 }),
330 TextureDataOrder::default(),
331 bytemuck::cast_slice(&generate_hilbert_index_lut()),
332 )
333 .create_view(&TextureViewDescriptor::default());
334
335 let point_clamp_sampler = render_device.create_sampler(&SamplerDescriptor {
336 min_filter: FilterMode::Nearest,
337 mag_filter: FilterMode::Nearest,
338 mipmap_filter: MipmapFilterMode::Nearest,
339 address_mode_u: AddressMode::ClampToEdge,
340 address_mode_v: AddressMode::ClampToEdge,
341 ..Default::default()
342 });
343 let linear_clamp_sampler = render_device.create_sampler(&SamplerDescriptor {
344 min_filter: FilterMode::Linear,
345 mag_filter: FilterMode::Linear,
346 mipmap_filter: MipmapFilterMode::Nearest,
347 address_mode_u: AddressMode::ClampToEdge,
348 address_mode_v: AddressMode::ClampToEdge,
349 ..Default::default()
350 });
351
352 let common_bind_group_layout = BindGroupLayoutDescriptor::new(
353 "ssao_common_bind_group_layout",
354 &BindGroupLayoutEntries::sequential(
355 ShaderStages::COMPUTE,
356 (
357 sampler(SamplerBindingType::NonFiltering),
358 sampler(SamplerBindingType::Filtering),
359 uniform_buffer::<ViewUniform>(true),
360 ),
361 ),
362 );
363
364 let preprocess_depth_bind_group_layout = BindGroupLayoutDescriptor::new(
365 "ssao_preprocess_depth_bind_group_layout",
366 &BindGroupLayoutEntries::sequential(
367 ShaderStages::COMPUTE,
368 (
369 texture_depth_2d(),
370 texture_storage_2d(depth_format, StorageTextureAccess::WriteOnly),
371 texture_storage_2d(depth_format, StorageTextureAccess::WriteOnly),
372 texture_storage_2d(depth_format, StorageTextureAccess::WriteOnly),
373 texture_storage_2d(depth_format, StorageTextureAccess::WriteOnly),
374 texture_storage_2d(depth_format, StorageTextureAccess::WriteOnly),
375 ),
376 ),
377 );
378
379 let ssao_bind_group_layout = BindGroupLayoutDescriptor::new(
380 "ssao_ssao_bind_group_layout",
381 &BindGroupLayoutEntries::sequential(
382 ShaderStages::COMPUTE,
383 (
384 texture_2d(TextureSampleType::Float { filterable: true }),
385 texture_2d(TextureSampleType::Float { filterable: false }),
386 texture_2d(TextureSampleType::Uint),
387 texture_storage_2d(depth_format, StorageTextureAccess::WriteOnly),
388 texture_storage_2d(TextureFormat::R32Uint, StorageTextureAccess::WriteOnly),
389 uniform_buffer::<GlobalsUniform>(false),
390 uniform_buffer::<SsaoSettingsUniform>(false),
391 ),
392 ),
393 );
394
395 let spatial_denoise_bind_group_layout = BindGroupLayoutDescriptor::new(
396 "ssao_spatial_denoise_bind_group_layout",
397 &BindGroupLayoutEntries::sequential(
398 ShaderStages::COMPUTE,
399 (
400 texture_2d(TextureSampleType::Float { filterable: false }),
401 texture_2d(TextureSampleType::Uint),
402 texture_storage_2d(depth_format, StorageTextureAccess::WriteOnly),
403 ),
404 ),
405 );
406
407 let mut shader_defs = Vec::new();
408 if depth_format == TextureFormat::R16Float {
409 shader_defs.push("USE_R16FLOAT".into());
410 }
411
412 let preprocess_depth_pipeline =
413 pipeline_cache.queue_compute_pipeline(ComputePipelineDescriptor {
414 label: Some("ssao_preprocess_depth_pipeline".into()),
415 layout: ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[preprocess_depth_bind_group_layout.clone(),
common_bind_group_layout.clone()]))vec![
416 preprocess_depth_bind_group_layout.clone(),
417 common_bind_group_layout.clone(),
418 ],
419 shader: {
let (path, asset_server) =
{
let path =
{
{
let crate_name =
"bevy_pbr::ssao".split(':').next().unwrap();
::bevy_asset::io::embedded::_embedded_asset_path(crate_name,
"src".as_ref(), "src/ssao/mod.rs".as_ref(),
"preprocess_depth.wesl".as_ref())
}
};
let path =
::bevy_asset::AssetPath::from_path_buf(path).with_source("embedded");
let asset_server =
::bevy_asset::io::embedded::GetAssetServer::get_asset_server(world);
(path, asset_server)
};
asset_server.load(path)
}load_embedded_asset!(world, "preprocess_depth.wesl"),
420 shader_defs: shader_defs.clone(),
421 ..default()
422 });
423
424 let spatial_denoise_pipeline =
425 pipeline_cache.queue_compute_pipeline(ComputePipelineDescriptor {
426 label: Some("ssao_spatial_denoise_pipeline".into()),
427 layout: ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[spatial_denoise_bind_group_layout.clone(),
common_bind_group_layout.clone()]))vec![
428 spatial_denoise_bind_group_layout.clone(),
429 common_bind_group_layout.clone(),
430 ],
431 shader: {
let (path, asset_server) =
{
let path =
{
{
let crate_name =
"bevy_pbr::ssao".split(':').next().unwrap();
::bevy_asset::io::embedded::_embedded_asset_path(crate_name,
"src".as_ref(), "src/ssao/mod.rs".as_ref(),
"spatial_denoise.wesl".as_ref())
}
};
let path =
::bevy_asset::AssetPath::from_path_buf(path).with_source("embedded");
let asset_server =
::bevy_asset::io::embedded::GetAssetServer::get_asset_server(world);
(path, asset_server)
};
asset_server.load(path)
}load_embedded_asset!(world, "spatial_denoise.wesl"),
432 shader_defs,
433 ..default()
434 });
435
436 Self {
437 preprocess_depth_pipeline,
438 spatial_denoise_pipeline,
439
440 common_bind_group_layout,
441 preprocess_depth_bind_group_layout,
442 ssao_bind_group_layout,
443 spatial_denoise_bind_group_layout,
444
445 hilbert_index_lut,
446 point_clamp_sampler,
447 linear_clamp_sampler,
448
449 shader: {
let (path, asset_server) =
{
let path =
{
{
let crate_name =
"bevy_pbr::ssao".split(':').next().unwrap();
::bevy_asset::io::embedded::_embedded_asset_path(crate_name,
"src".as_ref(), "src/ssao/mod.rs".as_ref(),
"ssao.wesl".as_ref())
}
};
let path =
::bevy_asset::AssetPath::from_path_buf(path).with_source("embedded");
let asset_server =
::bevy_asset::io::embedded::GetAssetServer::get_asset_server(world);
(path, asset_server)
};
asset_server.load(path)
}load_embedded_asset!(world, "ssao.wesl"),
450 depth_format,
451 }
452 }
453}
454
455#[derive(#[automatically_derived]impl ::core::marker::StructuralPartialEq for
SsaoPipelineKey {
}
#[automatically_derived]impl ::core::cmp::PartialEq for SsaoPipelineKey {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.temporal_jitter == other.temporal_jitter &&
self.quality_level == other.quality_level
}
}PartialEq, #[automatically_derived]impl ::core::cmp::Eq for SsaoPipelineKey {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_fields_are_eq(&self) {
let _:
::core::cmp::AssertParamIsEq<ScreenSpaceAmbientOcclusionQualityLevel>;
let _: ::core::cmp::AssertParamIsEq<bool>;
}
}Eq, #[automatically_derived]impl ::core::hash::Hash for SsaoPipelineKey {
#[inline]
fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) {
::core::hash::Hash::hash(&self.quality_level, state);
::core::hash::Hash::hash(&self.temporal_jitter, state)
}
}Hash, #[automatically_derived]impl ::core::clone::Clone for SsaoPipelineKey {
#[inline]
fn clone(&self) -> Self {
SsaoPipelineKey {
quality_level: ::core::clone::Clone::clone(&self.quality_level),
temporal_jitter: ::core::clone::Clone::clone(&self.temporal_jitter),
}
}
}Clone)]
456struct SsaoPipelineKey {
457 quality_level: ScreenSpaceAmbientOcclusionQualityLevel,
458 temporal_jitter: bool,
459}
460
461impl SpecializedComputePipeline for SsaoPipelines {
462 type Key = SsaoPipelineKey;
463
464 fn specialize(&self, key: Self::Key) -> ComputePipelineDescriptor {
465 let (slice_count, samples_per_slice_side) = key.quality_level.sample_counts();
466
467 let mut shader_defs = ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[ShaderDefVal::Int("SLICE_COUNT".into(), slice_count as i32),
ShaderDefVal::Int("SAMPLES_PER_SLICE_SIDE".into(),
samples_per_slice_side as i32)]))vec![
468 ShaderDefVal::Int("SLICE_COUNT".into(), slice_count as i32),
469 ShaderDefVal::Int(
470 "SAMPLES_PER_SLICE_SIDE".into(),
471 samples_per_slice_side as i32,
472 ),
473 ];
474
475 if key.temporal_jitter {
476 shader_defs.push("TEMPORAL_JITTER".into());
477 }
478
479 if self.depth_format == TextureFormat::R16Float {
480 shader_defs.push("USE_R16FLOAT".into());
481 }
482
483 ComputePipelineDescriptor {
484 label: Some("ssao_ssao_pipeline".into()),
485 layout: ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[self.ssao_bind_group_layout.clone(),
self.common_bind_group_layout.clone()]))vec![
486 self.ssao_bind_group_layout.clone(),
487 self.common_bind_group_layout.clone(),
488 ],
489 shader: self.shader.clone(),
490 shader_defs,
491 ..default()
492 }
493 }
494}
495
496fn extract_ssao_settings(
497 mut commands: Commands,
498 cameras: Extract<
499 Query<
500 (RenderEntity, &Camera, &ScreenSpaceAmbientOcclusion, &Msaa),
501 (With<Camera3d>, With<DepthPrepass>, With<NormalPrepass>),
502 >,
503 >,
504) {
505 for (entity, camera, ssao_settings, msaa) in &cameras {
506 if *msaa != Msaa::Off {
507 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event src/ssao/mod.rs:507",
"bevy_pbr::ssao", ::tracing::Level::ERROR,
::tracing_core::__macro_support::Option::Some("src/ssao/mod.rs"),
::tracing_core::__macro_support::Option::Some(507u32),
::tracing_core::__macro_support::Option::Some("bevy_pbr::ssao"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::ERROR <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::ERROR <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("SSAO is being used which requires Msaa::Off, but Msaa is currently set to Msaa::{0:?}",
*msaa) as &dyn ::tracing::field::Value))])
});
} else { ; }
};error!(
508 "SSAO is being used which requires Msaa::Off, but Msaa is currently set to Msaa::{:?}",
509 *msaa
510 );
511 continue;
512 }
513 let mut entity_commands = commands
514 .get_entity(entity)
515 .expect("SSAO entity wasn't synced.");
516 if camera.is_active {
517 entity_commands.insert(ssao_settings.clone());
518 } else {
519 entity_commands.remove::<ScreenSpaceAmbientOcclusion>();
520 }
521 }
522}
523
524#[derive(impl bevy_ecs::component::Component for ScreenSpaceAmbientOcclusionResources
where Self: ::core::marker::Send + ::core::marker::Sync + 'static {
const STORAGE_TYPE: bevy_ecs::component::StorageType =
bevy_ecs::component::StorageType::Table;
type Mutability = bevy_ecs::component::Mutable;
fn register_required_components(_requiree:
bevy_ecs::component::ComponentId,
required_components:
&mut bevy_ecs::component::RequiredComponentsRegistrator) {}
fn clone_behavior() -> bevy_ecs::component::ComponentCloneBehavior {
use bevy_ecs::component::{
DefaultCloneBehaviorBase, DefaultCloneBehaviorViaClone,
};
(&&&bevy_ecs::component::DefaultCloneBehaviorSpecialization::<Self>::default()).default_clone_behavior()
}
fn relationship_accessor()
->
::core::option::Option<bevy_ecs::relationship::ComponentRelationshipAccessor<Self>> {
::core::option::Option::None
}
}Component)]
525pub struct ScreenSpaceAmbientOcclusionResources {
526 preprocessed_depth_texture: CachedTexture,
527 ssao_noisy_texture: CachedTexture, pub screen_space_ambient_occlusion_texture: CachedTexture, depth_differences_texture: CachedTexture,
530 settings_buffer: Buffer,
531}
532
533fn prepare_ssao_textures(
534 mut commands: Commands,
535 mut texture_cache: ResMut<TextureCache>,
536 render_device: Res<RenderDevice>,
537 pipelines: Res<SsaoPipelines>,
538 views: Query<(Entity, &ExtractedCamera, &ScreenSpaceAmbientOcclusion)>,
539) {
540 for (entity, camera, ssao_settings) in &views {
541 let Some(physical_viewport_size) = camera.physical_viewport_size else {
542 continue;
543 };
544 let size = physical_viewport_size.to_extents();
545
546 let preprocessed_depth_texture = texture_cache.get(
547 &render_device,
548 TextureDescriptor {
549 label: Some("ssao_preprocessed_depth_texture"),
550 size,
551 mip_level_count: 5,
552 sample_count: 1,
553 dimension: TextureDimension::D2,
554 format: pipelines.depth_format,
555 usage: TextureUsages::STORAGE_BINDING | TextureUsages::TEXTURE_BINDING,
556 view_formats: &[],
557 },
558 );
559
560 let ssao_noisy_texture = texture_cache.get(
561 &render_device,
562 TextureDescriptor {
563 label: Some("ssao_noisy_texture"),
564 size,
565 mip_level_count: 1,
566 sample_count: 1,
567 dimension: TextureDimension::D2,
568 format: pipelines.depth_format,
569 usage: TextureUsages::STORAGE_BINDING | TextureUsages::TEXTURE_BINDING,
570 view_formats: &[],
571 },
572 );
573
574 let ssao_texture = texture_cache.get(
575 &render_device,
576 TextureDescriptor {
577 label: Some("ssao_texture"),
578 size,
579 mip_level_count: 1,
580 sample_count: 1,
581 dimension: TextureDimension::D2,
582 format: pipelines.depth_format,
583 usage: TextureUsages::STORAGE_BINDING | TextureUsages::TEXTURE_BINDING,
584 view_formats: &[],
585 },
586 );
587
588 let depth_differences_texture = texture_cache.get(
589 &render_device,
590 TextureDescriptor {
591 label: Some("ssao_depth_differences_texture"),
592 size,
593 mip_level_count: 1,
594 sample_count: 1,
595 dimension: TextureDimension::D2,
596 format: TextureFormat::R32Uint,
597 usage: TextureUsages::STORAGE_BINDING | TextureUsages::TEXTURE_BINDING,
598 view_formats: &[],
599 },
600 );
601
602 let mut settings_uniform_buffer = encase::UniformBuffer::new(Vec::new());
603 settings_uniform_buffer
604 .write(&SsaoSettingsUniform {
605 radius: ssao_settings.radius,
606 constant_object_thickness: ssao_settings.constant_object_thickness,
607 })
608 .unwrap();
609
610 let settings_buffer = render_device.create_buffer_with_data(&BufferInitDescriptor {
611 label: Some("ssao_settings_buffer"),
612 contents: settings_uniform_buffer.as_ref(),
613 usage: BufferUsages::UNIFORM,
614 });
615
616 commands
617 .entity(entity)
618 .insert(ScreenSpaceAmbientOcclusionResources {
619 preprocessed_depth_texture,
620 ssao_noisy_texture,
621 screen_space_ambient_occlusion_texture: ssao_texture,
622 depth_differences_texture,
623 settings_buffer,
624 });
625 }
626}
627
628#[derive(impl bevy_ecs::component::Component for SsaoPipelineId where
Self: ::core::marker::Send + ::core::marker::Sync + 'static {
const STORAGE_TYPE: bevy_ecs::component::StorageType =
bevy_ecs::component::StorageType::Table;
type Mutability = bevy_ecs::component::Mutable;
fn register_required_components(_requiree:
bevy_ecs::component::ComponentId,
required_components:
&mut bevy_ecs::component::RequiredComponentsRegistrator) {}
fn clone_behavior() -> bevy_ecs::component::ComponentCloneBehavior {
use bevy_ecs::component::{
DefaultCloneBehaviorBase, DefaultCloneBehaviorViaClone,
};
(&&&bevy_ecs::component::DefaultCloneBehaviorSpecialization::<Self>::default()).default_clone_behavior()
}
fn relationship_accessor()
->
::core::option::Option<bevy_ecs::relationship::ComponentRelationshipAccessor<Self>> {
::core::option::Option::None
}
}Component)]
630pub struct SsaoPipelineId(pub CachedComputePipelineId);
631
632fn prepare_ssao_pipelines(
633 mut commands: Commands,
634 pipeline_cache: Res<PipelineCache>,
635 mut pipelines: ResMut<SpecializedComputePipelines<SsaoPipelines>>,
636 pipeline: Res<SsaoPipelines>,
637 views: Query<(Entity, &ScreenSpaceAmbientOcclusion, Has<TemporalJitter>)>,
638) {
639 for (entity, ssao_settings, temporal_jitter) in &views {
640 let pipeline_id = pipelines.specialize(
641 &pipeline_cache,
642 &pipeline,
643 SsaoPipelineKey {
644 quality_level: ssao_settings.quality_level,
645 temporal_jitter,
646 },
647 );
648
649 commands.entity(entity).insert(SsaoPipelineId(pipeline_id));
650 }
651}
652
653#[derive(impl bevy_ecs::component::Component for SsaoBindGroups where
Self: ::core::marker::Send + ::core::marker::Sync + 'static {
const STORAGE_TYPE: bevy_ecs::component::StorageType =
bevy_ecs::component::StorageType::Table;
type Mutability = bevy_ecs::component::Mutable;
fn register_required_components(_requiree:
bevy_ecs::component::ComponentId,
required_components:
&mut bevy_ecs::component::RequiredComponentsRegistrator) {}
fn clone_behavior() -> bevy_ecs::component::ComponentCloneBehavior {
use bevy_ecs::component::{
DefaultCloneBehaviorBase, DefaultCloneBehaviorViaClone,
};
(&&&bevy_ecs::component::DefaultCloneBehaviorSpecialization::<Self>::default()).default_clone_behavior()
}
fn relationship_accessor()
->
::core::option::Option<bevy_ecs::relationship::ComponentRelationshipAccessor<Self>> {
::core::option::Option::None
}
}Component)]
658pub struct SsaoBindGroups {
659 pub common_bind_group: BindGroup,
660 pub preprocess_depth_bind_group: BindGroup,
661 pub ssao_bind_group: BindGroup,
662 pub spatial_denoise_bind_group: BindGroup,
663}
664
665fn prepare_ssao_bind_groups(
666 mut commands: Commands,
667 render_device: Res<RenderDevice>,
668 pipelines: Res<SsaoPipelines>,
669 view_uniforms: Res<ViewUniforms>,
670 global_uniforms: Res<GlobalsBuffer>,
671 pipeline_cache: Res<PipelineCache>,
672 views: Query<(
673 Entity,
674 &ScreenSpaceAmbientOcclusionResources,
675 &ViewPrepassTextures,
676 )>,
677) {
678 let (Some(view_uniforms), Some(globals_uniforms)) = (
679 view_uniforms.uniforms.binding(),
680 global_uniforms.buffer.binding(),
681 ) else {
682 return;
683 };
684
685 for (entity, ssao_resources, prepass_textures) in &views {
686 let Some(depth_view) = prepass_textures.depth_only_view() else {
687 continue;
688 };
689 let common_bind_group = render_device.create_bind_group(
690 "ssao_common_bind_group",
691 &pipeline_cache.get_bind_group_layout(&pipelines.common_bind_group_layout),
692 &BindGroupEntries::sequential((
693 &pipelines.point_clamp_sampler,
694 &pipelines.linear_clamp_sampler,
695 view_uniforms.clone(),
696 )),
697 );
698
699 let create_depth_view = |mip_level| {
700 ssao_resources
701 .preprocessed_depth_texture
702 .texture
703 .create_view(&TextureViewDescriptor {
704 label: Some("ssao_preprocessed_depth_texture_mip_view"),
705 base_mip_level: mip_level,
706 format: Some(pipelines.depth_format),
707 dimension: Some(TextureViewDimension::D2),
708 mip_level_count: Some(1),
709 ..default()
710 })
711 };
712
713 let preprocess_depth_bind_group = render_device.create_bind_group(
714 "ssao_preprocess_depth_bind_group",
715 &pipeline_cache.get_bind_group_layout(&pipelines.preprocess_depth_bind_group_layout),
716 &BindGroupEntries::sequential((
717 depth_view,
718 &create_depth_view(0),
719 &create_depth_view(1),
720 &create_depth_view(2),
721 &create_depth_view(3),
722 &create_depth_view(4),
723 )),
724 );
725
726 let ssao_bind_group = render_device.create_bind_group(
727 "ssao_ssao_bind_group",
728 &pipeline_cache.get_bind_group_layout(&pipelines.ssao_bind_group_layout),
729 &BindGroupEntries::sequential((
730 &ssao_resources.preprocessed_depth_texture.default_view,
731 prepass_textures.normal_view().unwrap(),
732 &pipelines.hilbert_index_lut,
733 &ssao_resources.ssao_noisy_texture.default_view,
734 &ssao_resources.depth_differences_texture.default_view,
735 globals_uniforms.clone(),
736 ssao_resources.settings_buffer.as_entire_binding(),
737 )),
738 );
739
740 let spatial_denoise_bind_group = render_device.create_bind_group(
741 "ssao_spatial_denoise_bind_group",
742 &pipeline_cache.get_bind_group_layout(&pipelines.spatial_denoise_bind_group_layout),
743 &BindGroupEntries::sequential((
744 &ssao_resources.ssao_noisy_texture.default_view,
745 &ssao_resources.depth_differences_texture.default_view,
746 &ssao_resources
747 .screen_space_ambient_occlusion_texture
748 .default_view,
749 )),
750 );
751
752 commands.entity(entity).insert(SsaoBindGroups {
753 common_bind_group,
754 preprocess_depth_bind_group,
755 ssao_bind_group,
756 spatial_denoise_bind_group,
757 });
758 }
759}
760
761fn generate_hilbert_index_lut() -> [[u16; 64]; 64] {
762 use core::array::from_fn;
763 from_fn(|x| from_fn(|y| hilbert_index(x as u16, y as u16)))
764}
765
766const HILBERT_WIDTH: u16 = 64;
768fn hilbert_index(mut x: u16, mut y: u16) -> u16 {
769 let mut index = 0;
770
771 let mut level: u16 = HILBERT_WIDTH / 2;
772 while level > 0 {
773 let region_x = (x & level > 0) as u16;
774 let region_y = (y & level > 0) as u16;
775 index += level * level * ((3 * region_x) ^ region_y);
776
777 if region_y == 0 {
778 if region_x == 1 {
779 x = HILBERT_WIDTH - 1 - x;
780 y = HILBERT_WIDTH - 1 - y;
781 }
782
783 mem::swap(&mut x, &mut y);
784 }
785
786 level /= 2;
787 }
788
789 index
790}