Expose opt-in temporal pixel diagnostics
Native and manual checks / native (ubuntu-24.04) (push) Failing after 35s
Native and manual checks / manual (push) Successful in 30s
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 35s
Native and manual checks / manual (push) Successful in 30s
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:
@@ -66,9 +66,14 @@ with tempfile.TemporaryDirectory(prefix="faset-player-diagnostics-") as temporar
|
||||
"temporal_fallback_reason", "temporal_reset_reason",
|
||||
"temporal_history_valid", "temporal_internal_width",
|
||||
"temporal_internal_height", "temporal_jitter",
|
||||
"temporal_counters_valid", "temporal_accepted_pixels",
|
||||
"temporal_rejected_pixels",
|
||||
"gpu_temporal_resolve_ms", "gpu_temporal_composite_ms", "gpu_ui_ms"]:
|
||||
assert key in sample, (key, sample)
|
||||
assert sample["effective_temporal_mode"] == temporal_mode, sample
|
||||
assert sample["temporal_counters_valid"] is False, sample
|
||||
assert sample["temporal_accepted_pixels"] is None and \
|
||||
sample["temporal_rejected_pixels"] is None, sample
|
||||
if temporal_mode != "off":
|
||||
assert sample["temporal_internal_width"] > 0, sample
|
||||
assert sample["temporal_internal_height"] > 0, sample
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
#include "render_temporal_fixtures.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
|
||||
using namespace faset::render;
|
||||
using namespace faset::render::temporal_test;
|
||||
|
||||
namespace {
|
||||
void pixel_decisions_are_diagnostic_only(VisibilityMode visibility) {
|
||||
constexpr std::uint32_t width = 96, height = 72;
|
||||
auto config = headless_config(width, height, visibility);
|
||||
config.temporal_mode = TemporalMode::TAA;
|
||||
Renderer renderer(config);
|
||||
Renderer reference(config);
|
||||
auto frame = lit_scene(width, height);
|
||||
frame.draws.push_back(cube({0, 0, 0}, {.9f, .4f, .2f, 1}, "diagnostic-cube"));
|
||||
|
||||
renderer.render(frame);
|
||||
reference.render(frame);
|
||||
require(!renderer.stats().temporal_counters_valid &&
|
||||
renderer.stats().temporal_accepted_pixels == 0 &&
|
||||
renderer.stats().temporal_rejected_pixels == 0,
|
||||
"Normal TAA must not read back per-pixel history diagnostics");
|
||||
|
||||
renderer.set_temporal_diagnostics(true);
|
||||
renderer.render(frame);
|
||||
reference.render(frame);
|
||||
const auto accepted = renderer.stats().temporal_accepted_pixels;
|
||||
const auto rejected = renderer.stats().temporal_rejected_pixels;
|
||||
require(renderer.stats().temporal_counters_valid && accepted > 0 &&
|
||||
accepted + rejected == width * height,
|
||||
"Requested TAA diagnostics count each scene pixel and reuse eligible history");
|
||||
require(renderer.pixels() == reference.pixels(),
|
||||
"Counting pixels must not alter the TAA image");
|
||||
|
||||
frame.camera_cut = true;
|
||||
renderer.render(frame);
|
||||
reference.render(frame);
|
||||
require(renderer.stats().temporal_counters_valid &&
|
||||
renderer.stats().temporal_accepted_pixels == 0 &&
|
||||
renderer.stats().temporal_rejected_pixels == width * height,
|
||||
"A camera cut rejects every per-pixel history sample");
|
||||
|
||||
frame.camera_cut = false;
|
||||
frame.scene_rect = {7, 9, 80, 50};
|
||||
renderer.render(frame);
|
||||
reference.render(frame);
|
||||
require(renderer.stats().temporal_counters_valid &&
|
||||
renderer.stats().temporal_accepted_pixels == 0 &&
|
||||
renderer.stats().temporal_rejected_pixels == 80 * 50,
|
||||
"The counter covers the scene rectangle, excluding output chrome pixels");
|
||||
|
||||
renderer.set_temporal_diagnostics(false);
|
||||
renderer.render(frame);
|
||||
reference.render(frame);
|
||||
require(!renderer.stats().temporal_counters_valid &&
|
||||
renderer.stats().temporal_accepted_pixels == 0 &&
|
||||
renderer.stats().temporal_rejected_pixels == 0,
|
||||
"Disabled diagnostics expose no stale values and perform no readback");
|
||||
|
||||
renderer.set_temporal_mode(TemporalMode::Off);
|
||||
reference.set_temporal_mode(TemporalMode::Off);
|
||||
renderer.set_temporal_diagnostics(true);
|
||||
renderer.render(frame);
|
||||
reference.render(frame);
|
||||
require(!renderer.stats().temporal_counters_valid,
|
||||
"Off mode does not claim temporal pixel diagnostics");
|
||||
require(renderer.stats().validation_errors == 0,
|
||||
"Diagnostic counter transitions pass Vulkan validation");
|
||||
require(renderer.pixels() == reference.pixels(),
|
||||
"Diagnostic toggles preserve the Off image");
|
||||
}
|
||||
} // namespace
|
||||
|
||||
int main() {
|
||||
try {
|
||||
pixel_decisions_are_diagnostic_only(VisibilityMode::Direct);
|
||||
pixel_decisions_are_diagnostic_only(VisibilityMode::GpuFrustum);
|
||||
std::cout << "Temporal pixel diagnostics are opt-in and exact\n";
|
||||
return 0;
|
||||
} catch (const std::exception& error) {
|
||||
std::cerr << error.what() << '\n';
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -49,9 +49,11 @@ int main() {
|
||||
auto reflection = temporary / "temporalResolveMain.reflection.json";
|
||||
auto metadata = faset::read_json(reflection);
|
||||
require(metadata["layout"]["stage"] == "compute" &&
|
||||
metadata["layout"]["descriptors"].size() == 7 &&
|
||||
metadata["layout"]["descriptors"].size() == 8 &&
|
||||
metadata["layout"]["descriptors"][7]["type"] == "storage_buffer" &&
|
||||
metadata["layout"]["descriptors"][7]["element_stride"] == 4 &&
|
||||
metadata["layout"]["push_constants"][0]["size"] == 80,
|
||||
"Temporal resolve ABI contains seven images and an 80-byte push block");
|
||||
"Temporal resolve ABI contains seven images, counters and an 80-byte push block");
|
||||
metadata["layout"]["descriptors"][5]["binding"] = 8;
|
||||
metadata["layout_fingerprint"] = faset::sha256(metadata["layout"].dump());
|
||||
faset::atomic_write_json(reflection, metadata);
|
||||
@@ -59,6 +61,14 @@ int main() {
|
||||
"A rehashed temporal image binding change must be rejected");
|
||||
faset::atomic_write_json(
|
||||
reflection, faset::read_json(original / "temporalResolveMain.reflection.json"));
|
||||
metadata = faset::read_json(reflection);
|
||||
metadata["layout"]["descriptors"][7]["element_stride"] = 8;
|
||||
metadata["layout_fingerprint"] = faset::sha256(metadata["layout"].dump());
|
||||
faset::atomic_write_json(reflection, metadata);
|
||||
must_reject([&] { (void)faset::render::detail::load_temporal_shader_bundle(temporary); },
|
||||
"A rehashed temporal counter stride change must be rejected");
|
||||
faset::atomic_write_json(
|
||||
reflection, faset::read_json(original / "temporalResolveMain.reflection.json"));
|
||||
|
||||
auto fragment = temporary / "temporalCompositeFragmentMain.spv";
|
||||
const auto bytes = faset::read_text(fragment);
|
||||
|
||||
Reference in New Issue
Block a user