19 auto fs_ssil_trace = am.get_asset<
gfx::shader>(
"engine:/data/shaders/ssil/fs_ssil_trace.sc");
20 auto fs_ssil_temporal = am.get_asset<
gfx::shader>(
"engine:/data/shaders/ssil/fs_ssil_temporal_resolve.sc");
21 auto cs_ssil_denoise = am.get_asset<
gfx::shader>(
"engine:/data/shaders/ssil/cs_ssil_spatial_denoise.sc");
22 auto cs_ssil_downsample = am.get_asset<
gfx::shader>(
"engine:/data/shaders/ssil/cs_ssil_downsample.sc");
23 auto fs_ssil_upsample = am.get_asset<
gfx::shader>(
"engine:/data/shaders/ssil/fs_ssil_upsample.sc");
25 trace_program_.program = std::make_unique<gpu_program>(vs_clip_quad, fs_ssil_trace);
26 trace_program_.cache_uniforms();
28 temporal_program_.program = std::make_unique<gpu_program>(vs_clip_quad, fs_ssil_temporal);
29 temporal_program_.cache_uniforms();
31 denoise_program_.program = std::make_unique<gpu_program>(cs_ssil_denoise);
32 denoise_program_.cache_uniforms();
34 downsample_program_.program = std::make_unique<gpu_program>(cs_ssil_downsample);
35 downsample_program_.cache_uniforms();
37 upsample_program_.program = std::make_unique<gpu_program>(vs_clip_quad, fs_ssil_upsample);
38 upsample_program_.cache_uniforms();
43 return trace_program_.is_valid() && temporal_program_.is_valid() && denoise_program_.is_valid();
47 const std::string&
name,
54 auto& tex = rview.tex_get_or_emplace(
name);
58 tex = std::make_shared<gfx::texture>(target_size.width, target_size.height,
false, 1,
59 gfx::texture_format::RGBA16F,
60 BGFX_TEXTURE_RT | BGFX_SAMPLER_U_CLAMP |
61 BGFX_SAMPLER_V_CLAMP | extra_flags);
64 auto& fbo = rview.fbo_get_or_emplace(
name);
68 fbo = std::make_shared<gfx::frame_buffer>();
76 const std::string&
name,
83 auto& tex = rview.tex_get_or_emplace(
name);
87 tex = std::make_shared<gfx::texture>(target_size.width, target_size.height,
false, 1,
88 gfx::texture_format::RGBA16F,
89 BGFX_TEXTURE_RT | BGFX_TEXTURE_BLIT_DST |
90 BGFX_SAMPLER_U_CLAMP | BGFX_SAMPLER_V_CLAMP | extra_flags);
97 const std::string& fbo_name,
98 const std::string& color_tex_name,
99 const std::string& moments_tex_name,
106 auto color_tex = create_or_update_ssil_tex(rview, color_tex_name, reference, res, extra_flags);
107 auto moments_tex = create_or_update_ssil_tex(rview, moments_tex_name, reference, res, extra_flags);
109 auto& fbo = rview.fbo_get_or_emplace(fbo_name);
113 fbo = std::make_shared<gfx::frame_buffer>();
114 fbo->populate({color_tex, moments_tex});
122 if(!params.g_buffer || !trace_program_.is_valid())
128 auto ssil_curr_fb = run_trace(rview, params);
135 auto result_fb = ssil_curr_fb;
137 bool moments_valid =
false;
139 if(params.settings.enable_temporal_accumulation && temporal_program_.is_valid())
146 moments_valid = run_temporal_resolve(rview,
153 moments_tex_for_denoise);
154 result_fb = temporal_result_fb;
158 release_history_resources(rview);
161 if(params.settings.enable_spatial_denoise && denoise_program_.is_valid())
166 auto moments_tex = moments_valid ? moments_tex_for_denoise :
gfx::texture::ptr{};
167 result_fb = run_spatial_denoise(rview, result_fb, params.g_buffer, moments_tex, params.cam, params.settings);
171 release_denoise_resources(rview);
181 if(reduced_res && upsample_program_.is_valid())
183 result_fb = run_upsample(rview, result_fb, params.g_buffer, params.cam, params.settings);
187 rview.fbo_remove(
"SSIL_UPSAMPLED");
188 rview.tex_remove(
"SSIL_UPSAMPLED");
192 return result_fb->get_texture();
199 auto ssil_curr_fb = create_or_update_ssil_fb(rview,
"SSIL_CURR", params.g_buffer, params.settings.resolution);
202 pass.bind(ssil_curr_fb.get());
203 pass.set_view_proj(params.cam->get_view(), params.cam->get_projection());
205 trace_program_.program->begin();
208 gfx::set_texture(trace_program_.s_normal, 1, params.g_buffer->get_texture(1));
210 gfx::set_texture(trace_program_.s_emissive, 3, params.g_buffer->get_texture(2));
211 gfx::set_texture(trace_program_.s_albedo, 4, params.g_buffer->get_texture(0));
213 bool multi_bounce_active = params.settings.enable_multi_bounce && params.prev_ssil && trace_program_.s_prev_ssil;
214 if(multi_bounce_active)
222 const bool env_fallback_active =
static_cast<bool>(params.irradiance_sh);
226 float ssil_params[4] = {
227 float(params.settings.max_steps),
228 float(params.settings.max_rays),
229 params.settings.depth_tolerance,
230 params.settings.brightness};
233 float multi_bounce_val = multi_bounce_active ? params.settings.multi_bounce_intensity : 0.0f;
241 float ssil_params2[4] = {
242 params.settings.max_distance,
245 env_fallback_active ? 1.0f : 0.0f};
248 float ssil_params3[4] = {params.settings.thickness, 0.0f, 0.0f, 0.0f};
257 const auto gbuf_sz = params.g_buffer->get_size();
258 const auto trace_sz = ssil_curr_fb->get_size();
259 const float ssil_resolution[4] = {
static_cast<float>(gbuf_sz.width),
260 static_cast<float>(gbuf_sz.height),
261 static_cast<float>(gbuf_sz.width) /
static_cast<float>(trace_sz.width),
262 static_cast<float>(gbuf_sz.height) /
static_cast<float>(trace_sz.height)};
270 gfx::set_state(topology | BGFX_STATE_DEPTH_TEST_NEVER | BGFX_STATE_WRITE_RGB | BGFX_STATE_WRITE_A);
271 gfx::submit(pass.id, trace_program_.program->native_handle());
274 trace_program_.program->end();
289 const int num_passes = std::clamp(settings.spatial_denoise.passes, 1, 5);
290 const int max_step = std::max(settings.spatial_denoise.max_step, 1);
291 const bool has_moments =
static_cast<bool>(moments);
294 auto moments_tex = has_moments ? moments : ssil_curr->get_texture();
296 const float depth_sigma = settings.spatial_denoise.depth_sigma;
297 const float normal_power = settings.spatial_denoise.normal_power;
298 const float luma_sigma = settings.spatial_denoise.luma_sigma;
306 int full_passes = std::clamp(settings.spatial_denoise.full_res_passes, 0, num_passes);
312 full_passes = std::max(full_passes, 1);
314 const bool mixed = downsample_program_.is_valid() && upsample_program_.is_valid() &&
315 (num_passes - full_passes) > 0;
318 full_passes = num_passes;
322 auto fb_a = create_or_update_ssil_fb(rview,
"SSIL_DENOISED_A", ssil_curr,
trace_resolution::full, BGFX_TEXTURE_COMPUTE_WRITE);
323 auto fb_b = create_or_update_ssil_fb(rview,
"SSIL_DENOISED_B", ssil_curr,
trace_resolution::full, BGFX_TEXTURE_COMPUTE_WRITE);
324 auto sz = fb_a->get_size();
325 uint32_t gx = (sz.width + 7) / 8;
326 uint32_t gy = (sz.height + 7) / 8;
333 auto& tex = rview.tex_get_or_emplace(
name);
337 tex = std::make_shared<gfx::texture>(
size.
width,
size.
height,
false, 1, gfx::texture_format::R16F,
338 BGFX_TEXTURE_COMPUTE_WRITE | BGFX_SAMPLER_U_CLAMP |
339 BGFX_SAMPLER_V_CLAMP);
343 auto var_a = make_variance_tex(
"SSIL_VARIANCE_A", sz);
344 auto var_b = make_variance_tex(
"SSIL_VARIANCE_B", sz);
358 const std::string& label) ->
void
364 pass.set_view_proj(cam->get_view(), cam->get_projection());
366 denoise_program_.program->begin();
369 gfx::set_image(1, out_fb->get_texture()->native_handle(), 0, bgfx::Access::Write);
374 gfx::set_image(6, v_dst->native_handle(), 0, bgfx::Access::Write);
376 float denoise_params[4] = {float(step), depth_sigma, normal_power, luma_sigma};
380 float denoise_params2[4] = {use_moments ? 1.0f : 0.0f, first_pass ? 1.0f : 0.0f, 0.0f,
381 float(kernel_radius)};
384 gfx::dispatch(pass.id, denoise_program_.program->native_handle(), dgx, dgy, 1);
386 denoise_program_.program->end();
393 auto src_tex = ssil_curr->get_texture();
405 for(
int i = 0;
i < full_passes; ++
i)
407 const int step = std::min(1 << i, max_step);
409 run_atrous(src_tex, dst_fb, var_src, var_dst, gx, gy, step, i == 0, has_moments, 2,
410 fmt::format(
"Spatial Denoise/Full Pass {}", i));
412 src_tex = dst_fb->get_texture();
413 dst_fb = (dst_fb == fb_a) ? fb_b : fb_a;
415 var_dst = (var_dst == var_a) ? var_b : var_a;
421 return (dst_fb == fb_a) ? fb_b : fb_a;
429 auto half_a = create_or_update_ssil_fb(rview,
"SSIL_DENOISED_HALF_A", ssil_curr,
trace_resolution::half, BGFX_TEXTURE_COMPUTE_WRITE);
430 auto half_b = create_or_update_ssil_fb(rview,
"SSIL_DENOISED_HALF_B", ssil_curr,
trace_resolution::half, BGFX_TEXTURE_COMPUTE_WRITE);
431 auto half_sz = half_a->get_size();
432 uint32_t hgx = (half_sz.width + 7) / 8;
433 uint32_t hgy = (half_sz.height + 7) / 8;
434 auto var_ha = make_variance_tex(
"SSIL_VARIANCE_HALF_A", half_sz);
435 auto var_hb = make_variance_tex(
"SSIL_VARIANCE_HALF_B", half_sz);
436 const bool has_downsampled_variance = full_passes > 0;
443 ds_pass.set_view_proj(cam->get_view(), cam->get_projection());
445 downsample_program_.program->begin();
447 gfx::set_image(1, half_a->get_texture()->native_handle(), 0, bgfx::Access::Write);
448 gfx::set_texture(downsample_program_.s_normal, 2, g_buffer->get_texture(1));
451 gfx::set_image(5, var_ha->native_handle(), 0, bgfx::Access::Write);
453 float ds_params[4] = {depth_sigma, normal_power, has_downsampled_variance ? 1.0f : 0.0f, 0.0f};
456 gfx::dispatch(ds_pass.id, downsample_program_.program->native_handle(), hgx, hgy, 1);
457 downsample_program_.program->end();
460 const int half_passes = num_passes - full_passes;
461 auto half_src = half_a->get_texture();
466 for(
int j = 0;
j < half_passes; ++
j)
470 const bool first_half_pass = !has_downsampled_variance &&
j == 0;
476 const int trace_step = 1 << (full_passes +
j);
477 const int capped_trace_step = std::min(trace_step, max_step);
478 const int half_step = std::max(1, capped_trace_step / half_divisor);
481 run_atrous(half_src, half_dst, hvar_src, hvar_dst, hgx, hgy, half_step, first_half_pass,
false, 1,
482 fmt::format(
"Spatial Denoise/Half Pass {}", j));
484 half_src = half_dst->get_texture();
485 half_dst = (half_dst == half_a) ? half_b : half_a;
487 hvar_dst = (hvar_dst == var_ha) ? var_hb : var_ha;
489 auto half_result_fb = (half_dst == half_a) ? half_b : half_a;
507 return half_result_fb;
510 auto out_fb = dst_fb;
512 gfx::render_pass up_pass(
"Spatial Denoise/Upsample To Trace Resolution Pass");
513 up_pass.bind(out_fb.get());
514 up_pass.set_view_proj(cam->get_view(), cam->get_projection());
516 upsample_program_.program->begin();
517 gfx::set_texture(upsample_program_.s_ssil_input, 0, half_result_fb->get_texture());
525 const auto out_sz = out_fb->get_size();
526 float upsample_params[4] = {depth_sigma, normal_power,
static_cast<float>(out_sz.width),
527 static_cast<float>(out_sz.height)};
531 gfx::set_state(topology | BGFX_STATE_DEPTH_TEST_NEVER | BGFX_STATE_WRITE_RGB | BGFX_STATE_WRITE_A);
532 gfx::submit(up_pass.id, upsample_program_.program->native_handle());
535 upsample_program_.program->end();
555 const ssil_settings& settings,
568 const bool read_is_a = (
frame & 1u) == 0
u;
570 const char* read_color_name = read_is_a ?
"SSIL_HISTORY_A_COLOR" :
"SSIL_HISTORY_B_COLOR";
571 const char* read_moments_name = read_is_a ?
"SSIL_HISTORY_A_MOMENTS" :
"SSIL_HISTORY_B_MOMENTS";
572 const char* write_fb_name = read_is_a ?
"SSIL_HISTORY_B_FB" :
"SSIL_HISTORY_A_FB";
573 const char* write_color_name = read_is_a ?
"SSIL_HISTORY_B_COLOR" :
"SSIL_HISTORY_A_COLOR";
574 const char* write_moments_name = read_is_a ?
"SSIL_HISTORY_B_MOMENTS" :
"SSIL_HISTORY_A_MOMENTS";
580 auto old_read_color = rview.tex_safe_get(read_color_name);
581 auto old_read_moments = rview.tex_safe_get(read_moments_name);
583 auto read_moments = create_or_update_ssil_tex(rview, read_moments_name, ssil_input,
trace_resolution::full);
584 const bool history_just_allocated =
585 (read_color != old_read_color) || (read_moments != old_read_moments);
587 auto write_fb = create_or_update_ssil_fb_mrt(rview, write_fb_name, write_color_name, write_moments_name,
589 auto write_moments = rview.tex_safe_get(write_moments_name);
595 out_result_fb = write_fb;
596 out_moments_tex = write_moments;
598 const auto gbuf_sz = g_buffer->get_size();
599 const auto temporal_sz = ssil_input->get_size();
600 const float temporal_resolution[4] = {
601 static_cast<float>(gbuf_sz.width),
602 static_cast<float>(gbuf_sz.height),
603 static_cast<float>(gbuf_sz.width) /
static_cast<float>(temporal_sz.width),
604 static_cast<float>(gbuf_sz.height) /
static_cast<float>(temporal_sz.height)};
606 const float temporal_params2[4] = {settings.temporal.normal_dot_threshold, 0.0f, 0.0f, 0.0f};
608 auto bind_common = [&](
float enable_temporal,
611 gfx::set_texture(temporal_program_.s_ssil_curr, 0, ssil_input->get_texture());
615 gfx::set_texture(temporal_program_.s_ssil_moments_history, 4, read_moments);
618 float temporal_params[4] = {enable_temporal,
619 settings.temporal.history_strength,
620 settings.temporal.depth_threshold,
621 float(settings.temporal.max_accum_frames)};
624 gfx::set_uniform(temporal_program_.u_temporal_resolution, temporal_resolution);
626 auto prev_vp = cam->get_prev_view_projection();
635 if(history_just_allocated || !prev_depth)
638 init_pass.bind(write_fb.get());
639 init_pass.set_view_proj(cam->get_view(), cam->get_projection());
641 temporal_program_.program->begin();
642 bind_common(0.0f, g_buffer->get_texture(4));
645 gfx::set_state(topology | BGFX_STATE_DEPTH_TEST_NEVER | BGFX_STATE_WRITE_RGB | BGFX_STATE_WRITE_A);
646 gfx::submit(init_pass.id, temporal_program_.program->native_handle());
649 temporal_program_.program->end();
655 pass.bind(write_fb.get());
656 pass.set_view_proj(cam->get_view(), cam->get_projection());
658 temporal_program_.program->begin();
659 bind_common(settings.enable_temporal_accumulation ? 1.0f : 0.0f, prev_depth);
662 gfx::set_state(topology | BGFX_STATE_DEPTH_TEST_NEVER | BGFX_STATE_WRITE_RGB | BGFX_STATE_WRITE_A);
663 gfx::submit(pass.id, temporal_program_.program->native_handle());
666 temporal_program_.program->end();
684 pass.bind(out_fb.get());
687 pass.set_view_proj(cam->get_view(), cam->get_projection());
689 upsample_program_.program->begin();
691 gfx::set_texture(upsample_program_.s_ssil_input, 0, ssil_input->get_texture());
698 const auto out_sz = out_fb->get_size();
699 float upsample_params[4] = {
700 settings.spatial_denoise.depth_sigma,
701 settings.spatial_denoise.normal_power,
702 static_cast<float>(out_sz.width),
703 static_cast<float>(out_sz.height)};
707 gfx::set_state(topology | BGFX_STATE_DEPTH_TEST_NEVER | BGFX_STATE_WRITE_RGB | BGFX_STATE_WRITE_A);
708 gfx::submit(pass.id, upsample_program_.program->native_handle());
711 upsample_program_.program->end();
747 release_denoise_resources(rview);
748 release_history_resources(rview);
std::shared_ptr< frame_buffer > ptr
void tex_remove(const hpp::string_view &id)
void fbo_remove(const hpp::string_view &id)
Manages assets, including loading, unloading, and storage.
auto get_asset(const std::string &key, load_flags flags=load_flags::standard, load_mode mode=load_mode::immediate) -> asset_handle< T >
Gets an asset by its key.
static auto get() -> default_textures &
auto run(gfx::render_view &rview, const run_params ¶ms) -> gfx::texture::ptr
void release_resources(gfx::render_view &rview)
Releases all GPU resources owned by this pass from the render_view.
auto init(rtti::context &ctx) -> bool
void dispatch(view_id _id, program_handle _handle, uint32_t _numX, uint32_t _numY, uint32_t _numZ)
void submit(view_id _id, program_handle _handle, int32_t _depth, bool _preserveState)
void set_state(uint64_t _state, uint32_t _rgba)
auto clip_fullscreen_triangle(float depth, float width, float height) -> uint64_t
auto needs_recreate(const gfx::frame_buffer::ptr &fbo, const usize32_t &size) -> bool
void discard(uint8_t _flags)
void set_image(uint8_t _stage, texture_handle _handle, uint8_t _mip, access _access, texture_format _format)
void set_uniform(uniform_handle _handle, const void *_value, uint16_t _num)
uint32_t get_render_frame()
auto clip_quad(float depth, float width, float height) -> uint64_t
void set_texture(uint8_t _stage, uniform_handle _sampler, texture_handle _handle, uint32_t _flags)
auto get_divisor(trace_resolution res) -> uint32_t
Returns the integer divisor backing the enum value (never zero).
auto compute_trace_size(const usize32_t &ref, trace_resolution res) -> usize32_t
Computes the trace-target size from a full-resolution reference, clamped to 1x1 min.
#define APP_SCOPE_PERF(name_literal)
Create a scoped performance timer that records to the timeline profiler. Only accepts string literals...
static void push_scope(const char *name)