test: record STEP-117 PBF R cache admission blockers
Some checks failed
validate-scripts / validate-scripts (push) Has been cancelled
Some checks failed
validate-scripts / validate-scripts (push) Has been cancelled
This commit is contained in:
@@ -3,6 +3,8 @@
|
||||
#include <mln/renderer/continuous_layer_range.hpp>
|
||||
#include <mln/renderer/render_segment_json.hpp>
|
||||
#include <mln/renderer/render_segment_plan.hpp>
|
||||
#include <mln/renderer/update_parameters.hpp>
|
||||
#include <mln/text/placement.hpp>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
@@ -66,6 +68,33 @@ ParticleFastPathGateInput fastPathInput() {
|
||||
.annotationMutation = false,
|
||||
};
|
||||
}
|
||||
|
||||
std::shared_ptr<UpdateParameters> step117PlacementParameters(TimePoint now,
|
||||
style::TransitionOptions transitions = {}) {
|
||||
TransformState state;
|
||||
state.setSize({640, 480});
|
||||
return std::make_shared<UpdateParameters>(UpdateParameters{
|
||||
.styleLoaded = true,
|
||||
.mode = MapMode::Continuous,
|
||||
.pixelRatio = 1.0f,
|
||||
.debugOptions = MapDebugOptions::NoDebug,
|
||||
.timePoint = now,
|
||||
.transformState = state,
|
||||
.glyphURL = {},
|
||||
.fontFaces = {},
|
||||
.spriteLoaded = true,
|
||||
.transitionOptions = transitions,
|
||||
.light = style::Light{}.impl,
|
||||
.images = makeMutable<std::vector<Immutable<style::Image::Impl>>>(),
|
||||
.sources = makeMutable<std::vector<Immutable<style::Source::Impl>>>(),
|
||||
.layers = makeMutable<std::vector<Immutable<style::Layer::Impl>>>(),
|
||||
.annotationManager = {},
|
||||
.fileSource = {},
|
||||
.prefetchZoomDelta = 0,
|
||||
.stillImageRequest = false,
|
||||
.crossSourceCollisions = true,
|
||||
});
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST(RetainedDynamicBoundary, SelectsFirstCandidate) {
|
||||
@@ -1305,6 +1334,137 @@ TEST(Step116Safety, AnnotationMutationAndLayerOrderDisableReloadKeepAllShadowFai
|
||||
EXPECT_EQ(particleFastGateFailureMask(unmodified), 0u); // predicate-only; no rendered Fast claim
|
||||
}
|
||||
|
||||
TEST(Step117Safety, RealPlacementControllerHasNoFirstSymbolBuildWindow) {
|
||||
PlacementController controller;
|
||||
RetainedUnderlayCache cache;
|
||||
unsigned recomputed = 0;
|
||||
unsigned recent = 0;
|
||||
const auto start = TimePoint{} + Seconds(10);
|
||||
for (unsigned frame = 0; frame < 120; ++frame) {
|
||||
const auto now = start + Milliseconds(frame * 30);
|
||||
const bool changed = !controller.placementIsRecent(now, 0.0f);
|
||||
if (changed) {
|
||||
++recomputed;
|
||||
auto placement = Placement::create(step117PlacementParameters(now), controller.getPlacement());
|
||||
// Exercise the real Continuous commit even with no buckets. This is
|
||||
// a controller lifecycle regression, not a Symbol GPU equivalence test.
|
||||
placement->placeLayers({});
|
||||
controller.setPlacement(std::move(placement));
|
||||
EXPECT_FLOAT_EQ(controller.getPlacement()->symbolFadeChange(now), 0.0f);
|
||||
EXPECT_FALSE(controller.hasTransitions(now));
|
||||
} else {
|
||||
++recent;
|
||||
controller.setPlacementStale();
|
||||
EXPECT_TRUE(controller.hasTransitions(now));
|
||||
EXPECT_GT(controller.getPlacement()->symbolFadeChange(now), 0.0f);
|
||||
}
|
||||
const bool supported = renderSegmentPlacementGatePassed(
|
||||
true, changed, controller.getPlacement()->symbolFadeChange(now), controller.hasTransitions(now));
|
||||
EXPECT_FALSE(supported);
|
||||
EXPECT_EQ(cache.evaluate(key(), 1, supported, "static-placement"), RetainedUnderlayDecision::Fallback);
|
||||
EXPECT_FALSE(cache.isValid());
|
||||
}
|
||||
EXPECT_GT(recomputed, 0u);
|
||||
EXPECT_GT(recent, 0u);
|
||||
EXPECT_EQ(cache.getBuildCount(), 0u);
|
||||
EXPECT_EQ(cache.getReuseCount(), 0u);
|
||||
}
|
||||
|
||||
TEST(Step117Safety, RealPlacementDisabledOrZeroDurationIsNotAResultCertificate) {
|
||||
const auto now = TimePoint{} + Seconds(10);
|
||||
for (const bool enabled : {false, true}) {
|
||||
SCOPED_TRACE(enabled);
|
||||
PlacementController controller;
|
||||
auto placement = Placement::create(step117PlacementParameters(
|
||||
now, style::TransitionOptions{Milliseconds(0), std::nullopt, enabled}), controller.getPlacement());
|
||||
placement->placeLayers({});
|
||||
controller.setPlacement(std::move(placement));
|
||||
EXPECT_FLOAT_EQ(controller.getPlacement()->symbolFadeChange(now), 1.0f);
|
||||
EXPECT_FALSE(controller.hasTransitions(now));
|
||||
EXPECT_EQ(controller.placementIsRecent(now, 0.0f), enabled);
|
||||
EXPECT_FALSE(renderSegmentPlacementGatePassed(true, true, 1.0f, controller.hasTransitions(now)));
|
||||
controller.setPlacementStale();
|
||||
EXPECT_EQ(controller.hasTransitions(now + Seconds(5)), enabled);
|
||||
EXPECT_FALSE(renderSegmentPlacementGatePassed(true, false,
|
||||
controller.getPlacement()->symbolFadeChange(now + Seconds(5)), controller.hasTransitions(now + Seconds(5))));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Step117Safety, MissingAndSameNameRestorationCannotReuseAnOldCache) {
|
||||
// Test-only source status; no source is added to the production PBF style.
|
||||
const RenderSegmentDependencies dependencies{{"step117-source"}, {}, false};
|
||||
RenderSegmentRevisionBook book;
|
||||
RetainedUnderlayCache cache;
|
||||
bool exists = false;
|
||||
const auto lookup = [&](const std::string&) -> std::optional<RenderSegmentSourceStatus> {
|
||||
if (exists) return RenderSegmentSourceStatus{true, false};
|
||||
return std::nullopt;
|
||||
};
|
||||
auto missing = assessRenderSegmentSources(dependencies, lookup);
|
||||
EXPECT_FALSE(book.revisionFor(dependencies));
|
||||
EXPECT_EQ(cache.evaluate(key(), 0, missing.stable, missing.reason), RetainedUnderlayDecision::Fallback);
|
||||
EXPECT_EQ(cache.getBuildCount(), 0u);
|
||||
exists = true;
|
||||
book.advanceSource("step117-source");
|
||||
const auto initial = book.revisionFor(dependencies);
|
||||
ASSERT_TRUE(initial);
|
||||
EXPECT_EQ(cache.evaluate(key(), *initial, assessRenderSegmentSources(dependencies, lookup).stable),
|
||||
RetainedUnderlayDecision::Build);
|
||||
cache.markBuildSucceeded(); // State-machine fixture only; no GPU draw is claimed.
|
||||
EXPECT_EQ(cache.evaluate(key(), *initial, true), RetainedUnderlayDecision::Reuse);
|
||||
const auto reuseBefore = cache.getReuseCount();
|
||||
exists = false;
|
||||
book.advanceSource("step117-source");
|
||||
const auto removed = book.revisionFor(dependencies);
|
||||
ASSERT_TRUE(removed);
|
||||
missing = assessRenderSegmentSources(dependencies, lookup);
|
||||
EXPECT_EQ(cache.evaluate(key(), *removed, missing.stable, missing.reason), RetainedUnderlayDecision::Fallback);
|
||||
EXPECT_FALSE(cache.isValid());
|
||||
EXPECT_EQ(cache.getBuildRevision(), 0u);
|
||||
EXPECT_EQ(cache.getReuseCount(), reuseBefore);
|
||||
exists = true;
|
||||
book.advanceSource("step117-source");
|
||||
const auto restored = book.revisionFor(dependencies);
|
||||
ASSERT_TRUE(restored);
|
||||
EXPECT_NE(restored, initial);
|
||||
EXPECT_NE(restored, removed);
|
||||
EXPECT_EQ(cache.evaluate(key(), *restored, true), RetainedUnderlayDecision::Build);
|
||||
EXPECT_EQ(cache.getReuseCount(), reuseBefore);
|
||||
}
|
||||
|
||||
TEST(Step117Safety, CameraStyleAnnotationAndContextRequireRebuildBeforeReuse) {
|
||||
using Mutation = void (*)(RetainedUnderlayRevisionState&);
|
||||
const std::array<Mutation, 5> mutations{{
|
||||
[](auto& state) { state.cameraMatrix[12] += 1.0; },
|
||||
[](auto& state) { state.viewport = {800, 600}; },
|
||||
[](auto& state) { ++state.contentRevision; }, // style/glyph/image/context epoch
|
||||
[](auto& state) { ++state.topologyRevision; }, // annotation/layer group
|
||||
[](auto& state) { ++state.rangeConfigRevision; },
|
||||
}};
|
||||
for (std::size_t i = 0; i < mutations.size(); ++i) {
|
||||
SCOPED_TRACE(i);
|
||||
RetainedUnderlayRevisionTracker tracker;
|
||||
RetainedUnderlayCache cache;
|
||||
auto state = revisionState();
|
||||
const auto cacheKey = [&] { return RetainedUnderlayKey{state.viewport, 0, *state.boundary, state.cameraMatrix}; };
|
||||
const auto oldRevision = tracker.observe(state);
|
||||
ASSERT_EQ(cache.evaluate(cacheKey(), oldRevision, true), RetainedUnderlayDecision::Build);
|
||||
cache.markBuildSucceeded(); // State-machine fixture only.
|
||||
mutations[i](state);
|
||||
const auto newRevision = tracker.observe(state);
|
||||
EXPECT_NE(newRevision, oldRevision);
|
||||
EXPECT_EQ(cache.evaluate(cacheKey(), newRevision, true), RetainedUnderlayDecision::Build);
|
||||
EXPECT_FALSE(cache.isValid());
|
||||
EXPECT_EQ(cache.getReuseCount(), 0u);
|
||||
auto input = fastPathInput();
|
||||
input.cacheValid = cache.isValid();
|
||||
input.revisionMatches = cache.getBuildRevision() == newRevision;
|
||||
EXPECT_FALSE(evaluateParticleFastPath(input).candidate);
|
||||
EXPECT_NE(particleFastGateFailureMask(input) & (1u << 2), 0u);
|
||||
EXPECT_NE(particleFastGateFailureMask(input) & (1u << 3), 0u);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(RenderSegmentLocality, MixedExecutionDrawsEachLiveRangeOnceInStyleOrder) {
|
||||
ResolvedRenderSegmentPlan plan;
|
||||
plan.valid = true;
|
||||
|
||||
Reference in New Issue
Block a user