Expose and package temporal rendering with guarded edge history
Native and manual checks / native (ubuntu-24.04) (push) Failing after 37s
Native and manual checks / manual (push) Successful in 27s
Windows editor and software Vulkan / windows-graphics (push) Canceled after 0s
Native and manual checks / native (windows-2025) (push) Canceled after 0s
Native and manual checks / native (ubuntu-24.04) (push) Failing after 37s
Native and manual checks / manual (push) Successful in 27s
Windows editor and software Vulkan / windows-graphics (push) Canceled after 0s
Native and manual checks / native (windows-2025) (push) Canceled after 0s
This commit is contained in:
@@ -2,8 +2,11 @@
|
||||
#include <faset/render/temporal.hpp>
|
||||
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
using namespace faset::render;
|
||||
using namespace faset::render::temporal_test;
|
||||
@@ -75,6 +78,7 @@ void lower_resolution_scene_and_output_ui() {
|
||||
auto frame = lit_scene(width, height);
|
||||
frame.draws.push_back(cube({0, 0, 0}, {.8f, .6f, .25f, 1}, "thin-scene"));
|
||||
frame.ui_quads.push_back({2, 2, 25, 12, {.8f, .7f, .25f, 1}});
|
||||
frame.scene_rect = {13, 17, 287, 199};
|
||||
upscale.render(frame);
|
||||
require(upscale.stats().effective_temporal_mode == TemporalMode::Upscale &&
|
||||
upscale.stats().temporal_internal_width == 215 &&
|
||||
@@ -83,6 +87,82 @@ void lower_resolution_scene_and_output_ui() {
|
||||
"0.67 upscale rasterizes 215x161 while capture remains 320x240");
|
||||
require(upscale.stats().validation_errors == 0,
|
||||
"Upscale image resize and composite pass Vulkan validation");
|
||||
const auto output_ui = pixel(upscale.pixels(), width, 4, 4);
|
||||
upscale.render(frame);
|
||||
require(upscale.stats().temporal_history_valid,
|
||||
"A compatible upscaled second frame has output-resolution history");
|
||||
upscale.set_temporal_mode(TemporalMode::Upscale, .5f);
|
||||
upscale.render(frame);
|
||||
require(upscale.stats().temporal_internal_width == 160 &&
|
||||
upscale.stats().temporal_internal_height == 120 &&
|
||||
!upscale.stats().temporal_history_valid &&
|
||||
upscale.stats().temporal_reset_reason == TemporalResetReason::ScaleChanged &&
|
||||
pixel(upscale.pixels(), width, 4, 4) == output_ui,
|
||||
"Changing upscale factor resets history without changing sharp output UI");
|
||||
upscale.set_temporal_mode(TemporalMode::TAA);
|
||||
upscale.render(frame);
|
||||
require(upscale.stats().temporal_reset_reason == TemporalResetReason::ScaleChanged &&
|
||||
upscale.stats().temporal_internal_width == width &&
|
||||
upscale.stats().temporal_internal_height == height,
|
||||
"Switching from upscale to 1:1 TAA resets the changed internal scale");
|
||||
upscale.set_temporal_mode(TemporalMode::Off);
|
||||
upscale.render(frame);
|
||||
require(upscale.stats().effective_temporal_mode == TemporalMode::Off &&
|
||||
pixel(upscale.pixels(), width, 4, 4) == output_ui,
|
||||
"Switching Off restores the full-resolution baseline and sharp UI");
|
||||
|
||||
upscale.resize(319, 241);
|
||||
frame = lit_scene(319, 241);
|
||||
frame.scene_rect = {13, 17, 285, 199};
|
||||
upscale.set_temporal_mode(TemporalMode::Upscale, .5f);
|
||||
upscale.render(frame);
|
||||
require(upscale.stats().temporal_internal_width == 160 &&
|
||||
upscale.stats().temporal_internal_height == 121 &&
|
||||
upscale.pixels().size() == std::size_t(319) * 241 * 4 &&
|
||||
upscale.stats().validation_errors == 0,
|
||||
"Odd output and offset scene rectangle preserve ceil-rounded internal extent");
|
||||
}
|
||||
|
||||
double frame_variation(const std::vector<std::vector<std::uint8_t>>& frames,
|
||||
std::uint32_t width, Region region) {
|
||||
require(frames.size() >= 2, "Temporal variation metric needs multiple frames");
|
||||
double sum{};
|
||||
for (std::size_t i = 1; i < frames.size(); ++i)
|
||||
sum += mean_rgb_error(frames[i], frames[i - 1], width,
|
||||
static_cast<std::uint32_t>(frames[i].size() / (width * 4)),
|
||||
region);
|
||||
return sum / double(frames.size() - 1);
|
||||
}
|
||||
|
||||
void static_edge_reduces_jitter_variation() {
|
||||
constexpr std::uint32_t width = 160, height = 120;
|
||||
auto config = headless_config(width, height, VisibilityMode::Direct);
|
||||
config.temporal_mode = TemporalMode::TAA;
|
||||
Renderer accumulated(config), spatial(config);
|
||||
auto frame = lit_scene(width, height);
|
||||
frame.clear_color = {0, 0, 0, 1};
|
||||
frame.draws.push_back(cube({0, 0, 0}, {1, 1, 1, 1}, "static-edge"));
|
||||
std::vector<std::vector<std::uint8_t>> resolved, unaccumulated;
|
||||
for (unsigned phase = 0; phase < 16; ++phase) {
|
||||
accumulated.render(frame);
|
||||
if (phase >= 4)
|
||||
resolved.push_back(accumulated.pixels());
|
||||
frame.camera_cut = true; // Same jitter phase, but no prior color may be read.
|
||||
spatial.render(frame);
|
||||
if (phase >= 4)
|
||||
unaccumulated.push_back(spatial.pixels());
|
||||
frame.camera_cut = false;
|
||||
}
|
||||
const Region edge_area{25, 12, 110, 95};
|
||||
const double raw = frame_variation(unaccumulated, width, edge_area);
|
||||
const double temporal = frame_variation(resolved, width, edge_area);
|
||||
std::cerr << "Static-edge mean frame variation: unaccumulated=" << raw
|
||||
<< " TAA=" << temporal << '\n';
|
||||
require(raw > .05 && temporal < raw * .98,
|
||||
"After warm-up TAA must reduce static edge shimmer across Halton phases");
|
||||
require(accumulated.stats().validation_errors == 0 &&
|
||||
spatial.stats().validation_errors == 0,
|
||||
"Static-edge temporal sequence must not raise Vulkan validation errors");
|
||||
}
|
||||
} // namespace
|
||||
|
||||
@@ -91,4 +171,5 @@ int main() {
|
||||
moving_reveal_and_camera_resets(VisibilityMode::GpuFrustum);
|
||||
moving_reveal_and_camera_resets(VisibilityMode::GpuOcclusion);
|
||||
lower_resolution_scene_and_output_ui();
|
||||
static_edge_reduces_jitter_variation();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user