Files
MapLibreParticleLab/src/mln/renderer/render_segment_json.hpp

231 lines
12 KiB
C++

#pragma once
#include <mln/renderer/render_segment.hpp>
#include <rapidjson/document.h>
#include <rapidjson/stringbuffer.h>
#include <rapidjson/writer.h>
#include <optional>
#include <set>
#include <string>
#include <string_view>
#include <utility>
namespace mln {
inline std::optional<RenderSegmentPlan> parseRenderSegmentPlanJSON(const std::string& json) {
rapidjson::Document document;
if (document.Parse<rapidjson::kParseValidateEncodingFlag>(json.data(), json.size()).HasParseError() ||
!document.IsObject() ||
!document.HasMember("schemaVersion") || !document["schemaVersion"].IsUint() ||
document["schemaVersion"].GetUint() != 1 || !document.HasMember("segments") ||
!document["segments"].IsArray() || document.MemberCount() != 2) {
return std::nullopt;
}
std::set<std::string_view> rootKeys;
for (auto member = document.MemberBegin(); member != document.MemberEnd(); ++member) {
const std::string_view name(member->name.GetString(), member->name.GetStringLength());
if (!rootKeys.insert(name).second || (name != "schemaVersion" && name != "segments")) {
return std::nullopt;
}
}
RenderSegmentPlan plan;
for (const auto& value : document["segments"].GetArray()) {
if (!value.IsObject() || !value.HasMember("strategy") || !value["strategy"].IsString() ||
value.MemberCount() > 2) {
return std::nullopt;
}
std::set<std::string_view> segmentKeys;
for (auto member = value.MemberBegin(); member != value.MemberEnd(); ++member) {
const std::string_view name(member->name.GetString(), member->name.GetStringLength());
if (!segmentKeys.insert(name).second || (name != "strategy" && name != "startLayerId")) {
return std::nullopt;
}
}
RenderSegmentDefinition definition;
const auto& strategyValue = value["strategy"];
const std::string strategy(strategyValue.GetString(), strategyValue.GetStringLength());
if (strategy.find('\0') != std::string::npos) return std::nullopt;
if (strategy == "RETAINED") {
definition.strategy = RenderSegmentStrategy::Retained;
} else if (strategy == "CONTINUOUS") {
definition.strategy = RenderSegmentStrategy::Continuous;
} else {
return std::nullopt;
}
if (value.HasMember("startLayerId")) {
const auto& startLayerId = value["startLayerId"];
if (!startLayerId.IsString()) return std::nullopt;
std::string layerId(startLayerId.GetString(), startLayerId.GetStringLength());
if (layerId.find('\0') != std::string::npos) return std::nullopt;
definition.startLayerId = std::move(layerId);
}
plan.segments.push_back(std::move(definition));
}
return plan;
}
// Android and iOS bridges use the same fail-closed conversion so malformed
// JSON always replaces (and therefore clears) a previously accepted plan.
inline RenderSegmentPlan renderSegmentPlanFromJSONOrInvalid(const std::string& json) {
if (auto plan = parseRenderSegmentPlanJSON(json)) return std::move(*plan);
RenderSegmentPlan invalid;
invalid.schemaVersion = 0;
return invalid;
}
template <typename Writer>
inline void writeOptionalString(Writer& writer, const char* key, const std::optional<std::string>& value) {
writer.Key(key);
if (value) writer.String(value->c_str()); else writer.Null();
}
template <typename Writer>
inline void writeOptionalUint32(Writer& writer, const char* key, const std::optional<uint32_t>& value) {
writer.Key(key);
if (value) writer.Uint(*value); else writer.Null();
}
template <typename Writer>
inline void writeStringArray(Writer& writer, const char* key, const std::vector<std::string>& values) {
writer.Key(key);
writer.StartArray();
for (const auto& value : values) writer.String(value.c_str());
writer.EndArray();
}
template <typename Writer>
inline void writeStyleLayerDiagnostics(Writer& writer, const StyleLayerDiagnostics& layer) {
writer.StartObject();
writer.Key("layerIndex"); writer.Int(layer.layerIndex);
writer.Key("layerId"); writer.String(layer.layerId.c_str());
writer.Key("layerType"); writer.String(layer.layerType.c_str());
writer.Key("visible"); writer.Bool(layer.visible);
writeOptionalString(writer, "sourceId", layer.sourceId);
writeOptionalString(writer, "sourceLayer", layer.sourceLayer);
writer.EndObject();
}
inline std::string renderSegmentDiagnosticsToJSON(const RenderSegmentDiagnostics& snapshot) {
rapidjson::StringBuffer buffer;
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
writer.StartObject();
writer.Key("schemaVersion"); writer.Uint(snapshot.schemaVersion);
writer.Key("enabled"); writer.Bool(snapshot.enabled);
writer.Key("configured"); writer.Bool(snapshot.configured);
writer.Key("valid"); writer.Bool(snapshot.valid);
writer.Key("configSource"); writer.String(snapshot.configSource.c_str());
writer.Key("configRevision"); writer.Uint64(snapshot.configRevision);
writer.Key("topologyRevision"); writer.Uint64(snapshot.topologyRevision);
writer.Key("styleLayerCount"); writer.Uint(snapshot.styleLayerCount);
writer.Key("segmentCount"); writer.Uint(snapshot.segmentCount);
writer.Key("retainedSegmentCount"); writer.Uint(snapshot.retainedSegmentCount);
writer.Key("continuousSegmentCount"); writer.Uint(snapshot.continuousSegmentCount);
writer.Key("coverageComplete"); writer.Bool(snapshot.coverageComplete);
writer.Key("fallback"); writer.Bool(snapshot.fallback);
writer.Key("lastReason"); writer.String(snapshot.lastReason.c_str());
writer.Key("fastPathEligible"); writer.Bool(snapshot.fastPathEligible);
writer.Key("fastPathActive"); writer.Bool(snapshot.fastPathActive);
writer.Key("fastPathFallbackReason"); writer.String(snapshot.fastPathFallbackReason.c_str());
writer.Key("fastPathHit"); writer.Uint64(snapshot.fastPathHit);
writer.Key("fastPathMiss"); writer.Uint64(snapshot.fastPathMiss);
writer.Key("fullCpuFrame"); writer.Uint64(snapshot.fullCpuFrame);
writer.Key("placementChanged"); writer.Bool(snapshot.placementChanged);
writer.Key("symbolFadeChange"); writer.Double(snapshot.symbolFadeChange);
writer.Key("placementTransitionActive"); writer.Bool(snapshot.placementTransitionActive);
writer.Key("crossSourceCollisions"); writer.Bool(snapshot.crossSourceCollisions);
writer.Key("mainBufferExecutionMode"); writer.String(snapshot.mainBufferExecutionMode.c_str());
writer.Key("globalLiveFallbackReason"); writer.String(snapshot.globalLiveFallbackReason.c_str());
writer.Key("styleLoaded"); writer.Bool(snapshot.styleLoaded);
writer.Key("imageManagerLoaded"); writer.Bool(snapshot.imageManagerLoaded);
writer.Key("renderOrchestratorLoaded"); writer.Bool(snapshot.renderOrchestratorLoaded);
writer.Key("spriteLoaded"); writer.Bool(snapshot.spriteLoaded);
writer.Key("hasFadingTiles"); writer.Bool(snapshot.hasFadingTiles);
writer.Key("contentRevision"); writer.Uint64(snapshot.contentRevision);
writer.Key("sources"); writer.StartArray();
for (const auto& source : snapshot.sources) {
writer.StartObject();
writer.Key("sourceId"); writer.String(source.sourceId.c_str());
writer.Key("sourceType"); writer.String(source.sourceType.c_str());
writer.Key("enabled"); writer.Bool(source.enabled);
writer.Key("isLoaded"); writer.Bool(source.isLoaded);
writer.Key("tileBacked"); writer.Bool(source.tileBacked);
writeOptionalUint32(writer, "tileCount", source.tileCount);
writeOptionalUint32(writer, "incompleteTileCount", source.incompleteTileCount);
writeOptionalUint32(writer, "loadedTileCount", source.loadedTileCount);
writer.Key("hasFadingTiles"); writer.Bool(source.hasFadingTiles);
writer.Key("lastSourceMutationSequence"); writer.Uint64(source.lastSourceMutationSequence);
writer.Key("lastTileChangedSequence"); writer.Uint64(source.lastTileChangedSequence);
writer.Key("lastTileActionSequence"); writer.Uint64(source.lastTileActionSequence);
writer.Key("lastTileAction"); writer.String(source.lastTileAction.c_str());
writer.Key("lastMutationReason"); writer.String(source.lastMutationReason.c_str());
writer.EndObject();
}
writer.EndArray();
writer.Key("lastMutation"); writer.StartObject();
writer.Key("sequence"); writer.Uint64(snapshot.lastMutation.sequence);
writer.Key("contentRevision"); writer.Uint64(snapshot.lastMutation.contentRevision);
writer.Key("eventType"); writer.String(snapshot.lastMutation.eventType.c_str());
writer.Key("reason"); writer.String(snapshot.lastMutation.reason.c_str());
writeOptionalString(writer, "sourceId", snapshot.lastMutation.sourceId);
writer.Key("affectedSegmentIndices"); writer.StartArray();
for (const auto index : snapshot.lastMutation.affectedSegmentIndices) writer.Uint(index);
writer.EndArray();
writer.Key("dependencyScope"); writer.String(snapshot.lastMutation.dependencyScope.c_str());
writer.EndObject();
writer.Key("styleLayers"); writer.StartArray();
for (const auto& layer : snapshot.styleLayers) writeStyleLayerDiagnostics(writer, layer);
writer.EndArray();
writer.Key("segments"); writer.StartArray();
for (const auto& segment : snapshot.segments) {
writer.StartObject();
writer.Key("segmentIndex"); writer.Uint(segment.segmentIndex);
writer.Key("strategy"); writer.String(renderSegmentStrategyText(segment.strategy));
writeOptionalString(writer, "configuredStartLayerId", segment.configuredStartLayerId);
writer.Key("beginIndex"); writer.Int(segment.beginIndex);
writer.Key("endExclusive"); writer.Int(segment.endExclusive);
writeOptionalString(writer, "resolvedStartLayerId", segment.resolvedStartLayerId);
writeOptionalString(writer, "resolvedEndLayerId", segment.resolvedEndLayerId);
writer.Key("layerCount"); writer.Uint(static_cast<uint32_t>(segment.layers.size()));
writer.Key("layers"); writer.StartArray();
for (const auto& layer : segment.layers) writeStyleLayerDiagnostics(writer, layer);
writer.EndArray();
writeStringArray(writer, "dependentSourceIds", segment.dependentSourceIds);
writeStringArray(writer, "unknownDependencies", segment.unknownDependencies);
writer.Key("placementCapable"); writer.Bool(segment.placementCapable);
writer.Key("placementGatePassed"); writer.Bool(segment.placementGatePassed);
writer.Key("actualExecution"); writer.String(segment.actualExecution.c_str());
writer.Key("cache");
if (!segment.cache) {
writer.Null();
} else {
const auto& cache = *segment.cache;
writer.StartObject();
writer.Key("cacheState"); writer.String(cache.cacheState.c_str());
writer.Key("contentRevision"); writer.Uint64(cache.contentRevision);
writer.Key("build"); writer.Uint64(cache.build);
writer.Key("reuse"); writer.Uint64(cache.reuse);
writer.Key("invalidation"); writer.Uint64(cache.invalidation);
writer.Key("fallbackCount"); writer.Uint64(cache.fallbackCount);
writer.Key("finalDecision"); writer.String(cache.finalDecision.c_str());
writer.Key("lastFallbackReason"); writer.String(cache.lastFallbackReason.c_str());
writer.Key("sourceReady"); writer.Bool(cache.sourceReady);
writer.Key("sourceStable"); writer.Bool(cache.sourceStable);
writer.Key("supported"); writer.Bool(cache.supported);
writer.Key("sourceRevision"); writer.Uint64(cache.sourceRevision);
writer.Key("lastInvalidationReason"); writer.String(cache.lastInvalidationReason.c_str());
writer.Key("textureCreate"); writer.Uint64(cache.textureCreate);
writer.Key("compositeCreate"); writer.Uint64(cache.compositeCreate);
writer.Key("resourceRelease"); writer.Uint64(cache.resourceRelease);
writer.EndObject();
}
writer.EndObject();
}
writer.EndArray();
writer.EndObject();
return buffer.GetString();
}
} // namespace mln