feat: admit settled Symbol render results to retained cache
Some checks failed
validate-scripts / validate-scripts (push) Has been cancelled

This commit is contained in:
tei
2026-10-11 16:47:21 +09:00
parent 869930046b
commit 3d989a00a8
26 changed files with 1534 additions and 104 deletions

View File

@@ -10,8 +10,10 @@
#include <mln/renderer/render_source.hpp>
#include <mln/renderer/render_source_observer.hpp>
#include <mln/renderer/tile_render_data.hpp>
#include <mln/renderer/symbol_render_result.hpp>
#include <mln/style/sources/geojson_source_impl.hpp>
#include <mln/style/layers/symbol_layer.hpp>
#include <mln/style/layers/background_layer.hpp>
#include <mln/tile/tile.hpp>
#include <mln/tile/tile_observer.hpp>
@@ -435,3 +437,137 @@ TEST(Step117Safety, RealAsyncCallbacksAndContextStyleCleanupAdvanceContentEpoch)
scheduler.runRenderJobs();
// Actual callback dispatch, but no network delivery, GPU, or Android assertion.
}
namespace {
void step119Certify(SymbolResultCertificate& certificate, SymbolResultBinding binding) {
SymbolRenderResult result;
result.append(255.0f);
EXPECT_FALSE(certificate.observeFull(result, binding, true));
certificate.completeFull(binding, true, false);
EXPECT_TRUE(certificate.observeFull(result, binding, true));
certificate.completeFull(binding, true, true);
EXPECT_TRUE(certificate.observeFull(result, binding, true));
certificate.completeFull(binding, true, false, true);
EXPECT_TRUE(certificate.canFast(binding));
}
} // namespace
TEST(Step119SymbolResults, RealTileGlyphImageStyleContextCallbacksRejectBuiltCertificate) {
TaggedScheduler scheduler{Scheduler::GetBackground(), {}};
RenderOrchestrator orchestrator(false, scheduler, std::nullopt);
std::atomic<int> deferred{0}, jobs{0}, detached{0};
auto source = std::make_unique<TeardownSource>(scheduler, deferred, jobs, detached);
const auto rejects = [&](auto event) {
SymbolResultBinding binding;
binding.content = orchestrator.getRetainedUnderlayContentRevision();
SymbolResultCertificate certificate;
step119Certify(certificate, binding);
event();
binding.content = orchestrator.getRetainedUnderlayContentRevision();
EXPECT_FALSE(certificate.canFast(binding));
};
rejects([&] {
static_cast<RenderSourceObserver&>(orchestrator).onTileChanged(*source, OverscaledTileID{2, 1, {2, 1, 1}});
});
rejects([&] { static_cast<GlyphManagerObserver&>(orchestrator).onGlyphsLoaded({}, {0, 255}); });
rejects([&] { static_cast<ImageManagerObserver&>(orchestrator).onStyleImageMissing("step119-icon", [] {}); });
rejects([&] { orchestrator.markContextLost(); });
rejects([&] { orchestrator.clearData(); });
source->setObserver(nullptr);
source.reset();
scheduler.waitForEmpty();
scheduler.runRenderJobs();
}
TEST(Step119SymbolResults, AppliedCrossSegmentRegistryRebuildChangesExactEvidence) {
TaggedScheduler scheduler{Scheduler::GetBackground(), {}};
RenderOrchestrator orchestrator(false, scheduler, std::nullopt);
auto runtime = std::make_shared<RetainedUnderlayRuntimeState>();
style::BackgroundLayer lower("step119-lower"), continuous("step119-middle"), upper("step119-upper");
const Immutable<std::vector<Immutable<style::Layer::Impl>>> layers =
makeMutable<std::vector<Immutable<style::Layer::Impl>>>(
std::initializer_list<Immutable<style::Layer::Impl>>{lower.baseImpl, continuous.baseImpl, upper.baseImpl});
const Immutable<std::vector<Immutable<style::Source::Impl>>> sources =
makeMutable<std::vector<Immutable<style::Source::Impl>>>();
auto tree = orchestrator.createRenderTree(step117TreeParameters(layers, sources, runtime, TimePoint{}), nullptr);
ASSERT_TRUE(tree);
orchestrator.processChanges();
auto group = std::make_shared<TestLayerGroup>(0);
ASSERT_TRUE(orchestrator.addLayerGroup(group));
ASSERT_TRUE(orchestrator.addLayerGroup(std::make_shared<TestLayerGroup>(2)));
const auto before = orchestrator.retainedSymbolRenderResult(2, 3, 1, 0);
ASSERT_TRUE(before.complete);
SymbolResultCertificate certificate;
SymbolResultBinding binding;
step119Certify(certificate, binding);
UniqueChangeRequestVec changes;
changes.emplace_back(std::make_unique<RemoveLayerGroupRequest>(group));
RenderOrchestratorTeardownTestAccess::enqueue(orchestrator, changes);
const auto pending = orchestrator.retainedSymbolRenderResult(2, 3, 1, 0);
EXPECT_FALSE(pending.complete);
EXPECT_FALSE(certificate.observeFull(pending, binding, true));
orchestrator.processChanges();
EXPECT_EQ(orchestrator.numLayerGroups(), 1u);
const auto after = orchestrator.retainedSymbolRenderResult(2, 3, 1, 0);
EXPECT_TRUE(after.complete);
EXPECT_NE(before.bytes, after.bytes); // actual global count + original ordinal changed
EXPECT_FALSE(certificate.canFast(binding));
}
TEST(Step119SymbolResults, RangeDrawFailureInvalidatesContentAndCompletedEvidence) {
TaggedScheduler scheduler{Scheduler::GetBackground(), {}};
RenderOrchestrator orchestrator(false, scheduler, std::nullopt);
ErrorObserver observer;
orchestrator.setObserver(&observer);
ASSERT_TRUE(orchestrator.addLayerGroup(std::make_shared<TestLayerGroup>(0)));
SymbolResultBinding binding;
binding.content = orchestrator.getRetainedUnderlayContentRevision();
SymbolResultCertificate certificate;
step119Certify(certificate, binding);
const auto errors = orchestrator.getRenderErrorRevision();
bool failed = false;
orchestrator.visitLayerGroupsInRange(0, 1, [](LayerGroupBase&, size_t) {
throw std::runtime_error("step119-upload-or-draw");
}, &failed);
EXPECT_TRUE(failed);
EXPECT_EQ(observer.errors, 1);
EXPECT_GT(orchestrator.getRenderErrorRevision(), errors);
binding.content = orchestrator.getRetainedUnderlayContentRevision();
EXPECT_FALSE(certificate.canReuse(binding));
EXPECT_FALSE(certificate.canFast(binding));
}
TEST(Step119SymbolResults, RealSourceReplacementAndMissingSourceRetainFailClosed) {
TaggedScheduler scheduler{Scheduler::GetBackground(), {}};
RenderOrchestrator orchestrator(false, scheduler, std::nullopt);
auto runtime = std::make_shared<RetainedUnderlayRuntimeState>();
ASSERT_TRUE(runtime->setRenderSegmentPlan({1, {{RenderSegmentStrategy::Retained, std::nullopt}}}));
style::SymbolLayer symbol("step119-symbol", "step119-source");
const Immutable<std::vector<Immutable<style::Layer::Impl>>> layers =
makeMutable<std::vector<Immutable<style::Layer::Impl>>>(
std::initializer_list<Immutable<style::Layer::Impl>>{symbol.baseImpl});
const auto sourceList = []() -> Immutable<std::vector<Immutable<style::Source::Impl>>> {
return makeMutable<std::vector<Immutable<style::Source::Impl>>>(
std::initializer_list<Immutable<style::Source::Impl>>{
makeMutable<style::GeoJSONSource::Impl>("step119-source", style::GeoJSONOptions::defaultOptions())});
};
auto tree = orchestrator.createRenderTree(step117TreeParameters(layers, sourceList(), runtime, TimePoint{}), nullptr);
ASSERT_TRUE(tree);
SymbolResultBinding binding;
binding.content = orchestrator.getRetainedUnderlayContentRevision();
SymbolResultCertificate certificate;
step119Certify(certificate, binding);
tree.reset();
tree = orchestrator.createRenderTree(step117TreeParameters(layers, sourceList(), runtime, TimePoint{} + Seconds(1)), nullptr);
ASSERT_TRUE(tree);
binding.content = orchestrator.getRetainedUnderlayContentRevision();
EXPECT_FALSE(certificate.canFast(binding));
tree.reset();
const Immutable<std::vector<Immutable<style::Source::Impl>>> absent =
makeMutable<std::vector<Immutable<style::Source::Impl>>>();
tree = orchestrator.createRenderTree(step117TreeParameters(layers, absent, runtime, TimePoint{} + Seconds(2)), nullptr);
ASSERT_TRUE(tree);
EXPECT_STREQ(orchestrator.retainedSegmentSourceState(0, 1).reason, "missing-source");
binding.content = orchestrator.getRetainedUnderlayContentRevision();
EXPECT_FALSE(certificate.canFast(binding));
}