Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 50 additions & 42 deletions native/shared/src/renderer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -784,6 +784,7 @@ pub struct Renderer {
pub taa_depth_history_views: [wgpu::TextureView; 2],
pub taa_current_idx: usize,
pub taa_pipeline: LazyRenderPipeline,
taa_pipeline_fast: LazyRenderPipeline,
pub taa_layout: wgpu::BindGroupLayout,
pub taa_uniform_buffer: wgpu::Buffer,
taa_bind_group_cache: [Option<wgpu::BindGroup>; 2],
Expand All @@ -805,6 +806,7 @@ pub struct Renderer {
pub taa_enabled: bool,
/// TAA also upscales below render scale 1; see `set_temporal_upscale`.
temporal_upscale: bool,
taa_fast: bool, // fs_main_fast; see set_taa_fast
/// Perspective/orthographic clip planes for begin_mode_3d. Defaults keep
/// the historical 0.01..1000; a scene viewed from ~15 units away needs a
/// larger near plane, or surfaces a millimetre apart z-fight.
Expand Down Expand Up @@ -4814,49 +4816,53 @@ impl Renderer {
bind_group_layouts: &[Some(&taa_layout)],
immediate_size: 0,
});
let taa_pipeline = LazyRenderPipeline::new(
&device,
&wgpu::RenderPipelineDescriptor {
label: Some("taa_pipeline"),
layout: Some(&taa_pl_layout),
vertex: wgpu::VertexState {
module: &taa_shader,
entry_point: Some("vs_main"),
buffers: &[],
compilation_options: Default::default(),
},
fragment: Some(wgpu::FragmentState {
module: &taa_shader,
entry_point: Some("fs_main"),
targets: &[
Some(wgpu::ColorTargetState {
format: HDR_FORMAT,
blend: None,
write_mask: wgpu::ColorWrites::ALL,
}),
Some(wgpu::ColorTargetState {
format: TAA_DEPTH_HISTORY_FORMAT,
blend: None,
write_mask: wgpu::ColorWrites::RED | wgpu::ColorWrites::GREEN,
}),
],
compilation_options: Default::default(),
}),
primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleList,
strip_index_format: None,
front_face: wgpu::FrontFace::Ccw,
cull_mode: None,
polygon_mode: wgpu::PolygonMode::Fill,
unclipped_depth: false,
conservative: false,
let create_taa_pipeline = |label: &'static str, entry: &'static str| {
LazyRenderPipeline::new(
&device,
&wgpu::RenderPipelineDescriptor {
label: Some(label),
layout: Some(&taa_pl_layout),
vertex: wgpu::VertexState {
module: &taa_shader,
entry_point: Some("vs_main"),
buffers: &[],
compilation_options: Default::default(),
},
fragment: Some(wgpu::FragmentState {
module: &taa_shader,
entry_point: Some(entry),
targets: &[
Some(wgpu::ColorTargetState {
format: HDR_FORMAT,
blend: None,
write_mask: wgpu::ColorWrites::ALL,
}),
Some(wgpu::ColorTargetState {
format: TAA_DEPTH_HISTORY_FORMAT,
blend: None,
write_mask: wgpu::ColorWrites::RED | wgpu::ColorWrites::GREEN,
}),
],
compilation_options: Default::default(),
}),
primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleList,
strip_index_format: None,
front_face: wgpu::FrontFace::Ccw,
cull_mode: None,
polygon_mode: wgpu::PolygonMode::Fill,
unclipped_depth: false,
conservative: false,
},
depth_stencil: None,
multisample: wgpu::MultisampleState::default(),
multiview_mask: None,
cache: None,
},
depth_stencil: None,
multisample: wgpu::MultisampleState::default(),
multiview_mask: None,
cache: None,
},
);
)
};
let taa_pipeline = create_taa_pipeline("taa_pipeline", "fs_main");
let taa_pipeline_fast = create_taa_pipeline("taa_pipeline_fast", "fs_main_fast");
let taa_uniform_buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("taa_uniform_buffer"),
size: std::mem::size_of::<TaaParams>() as u64,
Expand Down Expand Up @@ -8277,6 +8283,7 @@ impl Renderer {
taa_depth_history_views,
taa_current_idx: 0,
taa_pipeline,
taa_pipeline_fast,
taa_layout,
taa_uniform_buffer,
taa_bind_group_cache: [None, None],
Expand All @@ -8289,6 +8296,7 @@ impl Renderer {
taa_frame_index: 0,
taa_enabled: true,
temporal_upscale: !cfg!(target_os = "android"),
taa_fast: cfg!(target_os = "android"),
camera_near: 0.01,
camera_far: 1000.0,
taa_history_valid: false,
Expand Down
2 changes: 2 additions & 0 deletions native/shared/src/renderer/postfx_chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1062,6 +1062,8 @@ impl Renderer {
.as_ref()
.expect("reactive TAA pipeline initialized"),
);
} else if self.taa_fast {
pass.set_pipeline(&self.taa_pipeline_fast);
} else {
pass.set_pipeline(&self.taa_pipeline);
}
Expand Down
13 changes: 13 additions & 0 deletions native/shared/src/renderer/quality_preset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,19 @@ impl Renderer {
}
}

/// The fast TAA: the current pixel as rendered, history clamped to its
/// 5-pixel cross, one bilinear history fetch (`fs_main_fast`), instead of
/// the full reconstruction with its provenance and detail-lock
/// heuristics. The low tier of temporal AA, for TAA resolving at the
/// render resolution (`set_temporal_upscale(false)`). Default on Android
/// (Pixel 4a: 16 -> 4 ms at 0.5 render scale).
pub fn set_taa_fast(&mut self, on: bool) {
if on != self.taa_fast {
self.taa_fast = on;
self.taa_history_valid = false;
}
}

/// The sun shadow's filter: `true` takes 4 hardware-PCF taps (a
/// 3x3-texel tent: the filter mobile renderers default to, a quarter of
/// the cost), `false` the 16-tap Poisson disc. Default 4-tap on Android.
Expand Down
72 changes: 72 additions & 0 deletions native/shared/src/renderer/shaders/post.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1383,6 +1383,78 @@ fn fs_main(in: VsOut) -> TaaOut {
),
);
}

// The fast TAA (set_taa_fast; the mobile default), for TAA resolving at its
// input's resolution: the current pixel as rendered, history clamped to the
// variance of the pixel and its 4-neighbour cross in YCoCg, one bilinear
// history fetch reprojected by the closest-depth velocity (the sky by its
// direction), and the same blend weight. The low-quality tier of desktop
// temporal AA (Unreal's lowest r.TemporalAA.Quality): without the
// reconstruction filters, provenance and lock heuristics above, a fraction of
// their registers and fetches. Writes the same targets; the provenance keeps
// the depth key and no confidence.
@fragment
fn fs_main_fast(in: VsOut) -> TaaOut {
let dims = vec2<i32>(textureDimensions(composed_tex));
let max_coord = dims - vec2<i32>(1);
let coord = clamp(vec2<i32>(in.uv * vec2<f32>(dims)), vec2<i32>(0), max_coord);
let current_sample = textureLoad(composed_tex, coord, 0);

var closest_coord = coord;
var depth = textureLoad(depth_tex, coord, 0);
var m1 = rgb_to_ycocg(current_sample.rgb);
var m2 = m1 * m1;
for (var i = 0; i < 4; i = i + 1) {
let c = clamp(coord + cross_offset(i), vec2<i32>(0), max_coord);
let d = textureLoad(depth_tex, c, 0);
if (d < depth) {
depth = d;
closest_coord = c;
}
let s = rgb_to_ycocg(textureLoad(composed_tex, c, 0).rgb);
m1 = m1 + s;
m2 = m2 + s * s;
}
let mean = m1 / 5.0;
let stddev = sqrt(max(m2 / 5.0 - mean * mean, vec3<f32>(0.0)) * (5.0 / 3.0));

let ndc = vec4<f32>(in.uv.x * 2.0 - 1.0, (1.0 - in.uv.y) * 2.0 - 1.0, depth, 1.0);
let world_h = u.inv_vp * ndc;
let perspective = u.params.w > 0.0;
let current_depth_key = select(
depth,
select(1.0 / max(abs(world_h.w), 0.000001), 10000.0, depth >= 0.9999),
perspective,
);
let vel = textureLoad(velocity_tex, closest_coord, 0).rg;
var prev_uv = vec2<f32>(in.uv.x - vel.x, in.uv.y + vel.y);
if (depth >= 0.9999) {
let prev_clip = u.prev_vp * vec4<f32>(world_h.xyz, 0.0);
prev_uv = in.uv;
if (prev_clip.w > 0.00001) {
let prev_ndc = prev_clip.xyz / prev_clip.w;
prev_uv = vec2<f32>(prev_ndc.x * 0.5 + 0.5, 1.0 - (prev_ndc.y * 0.5 + 0.5));
}
}

var alpha = abs(u.params.x);
var history_sample = current_sample;
if (any(prev_uv < vec2<f32>(0.0)) || any(prev_uv > vec2<f32>(1.0))) {
alpha = 1.0;
} else {
history_sample = textureSampleLevel(history_tex, history_samp, prev_uv, 0.0);
}
let gamma = mix(1.25, 0.25, smoothstep(0.0005, 0.008, length(vel)));
let h = rgb_to_ycocg(history_sample.rgb);
let lo = mean - stddev * vec3<f32>(gamma, gamma * 3.0, gamma * 3.0);
let hi = mean + stddev * vec3<f32>(gamma, gamma * 3.0, gamma * 3.0);
let clamped_history = ycocg_to_rgb(clamp(h, lo, hi));
let blended = max(mix(clamped_history, current_sample.rgb, alpha), vec3<f32>(0.0));
return TaaOut(
vec4<f32>(blended, mix(history_sample.a, current_sample.a, alpha)),
vec2<f32>(current_depth_key, pack_temporal_history(0.0, 0.0, 0.0)),
);
}
",
);

Expand Down