diff --git a/CMakeLists.txt b/CMakeLists.txt index c11962a22..390312c3d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -182,6 +182,7 @@ list(APPEND SRC_FILES ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/render_target.cpp ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/retained_underlay_cache.cpp ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/retained_underlay_cache.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/retained_underlay_runtime_state.hpp ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/change_request.cpp ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/layer_group.cpp ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/tile_layer_group.cpp @@ -307,6 +308,7 @@ list(APPEND INCLUDE_FILES ${PROJECT_SOURCE_DIR}/include/mbgl/renderer/renderer_observer.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/renderer/renderer_state.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/renderer/renderer.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/renderer/retained_underlay.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/shaders/program_parameters.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/shaders/shader_source.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/storage/database_file_source.hpp diff --git a/include/mbgl/map/map.hpp b/include/mbgl/map/map.hpp index 464b36f25..439f953ea 100644 --- a/include/mbgl/map/map.hpp +++ b/include/mbgl/map/map.hpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -55,6 +56,11 @@ public: /// Triggers a repaint. void triggerRepaint(); + /** Enables or disables the map-scoped retained static underlay optimization. */ + void setRetainedUnderlayEnabled(bool); + bool isRetainedUnderlayEnabled() const; + RetainedUnderlayDiagnostics getRetainedUnderlayDiagnostics() const; + style::Style& getStyle(); const style::Style& getStyle() const; diff --git a/include/mbgl/renderer/retained_underlay.hpp b/include/mbgl/renderer/retained_underlay.hpp new file mode 100644 index 000000000..62ca5ac2a --- /dev/null +++ b/include/mbgl/renderer/retained_underlay.hpp @@ -0,0 +1,17 @@ +#pragma once + +#include +#include + +namespace mbgl { + +struct RetainedUnderlayDiagnostics { + bool enabled = true; + uint64_t build = 0; + uint64_t reuse = 0; + uint64_t fallback = 0; + uint64_t invalidation = 0; + std::string lastReason; +}; + +} // namespace mbgl diff --git a/integration/react-native/MapLibreParticleRNTestHost/App.tsx b/integration/react-native/MapLibreParticleRNTestHost/App.tsx index c04521451..a253c8e64 100644 --- a/integration/react-native/MapLibreParticleRNTestHost/App.tsx +++ b/integration/react-native/MapLibreParticleRNTestHost/App.tsx @@ -10,7 +10,12 @@ import { findNodeHandle, } from "react-native"; import { Camera, Map } from "@maplibre/maplibre-react-native"; -import { FieldRuntime, type RuntimeSnapshot } from "@navsea/maplibre-particle-rn"; +import { + FieldRuntime, + RendererRuntime, + type RetainedUnderlayDiagnostics, + type RuntimeSnapshot, +} from "@navsea/maplibre-particle-rn"; import { materializeV4Frame as materializeV2Frame, qbinGoldenDatasets, @@ -60,6 +65,8 @@ function valueSourceFor(id: WeatherDatasetId): "SCALAR" | "VECTOR_MAGNITUDE" { export default function App() { const [reactTag, setReactTag] = useState(null); const [snapshot, setSnapshot] = useState(initialSnapshot); + const [retainedSnapshot, setRetainedSnapshot] = + useState(null); const [mapStatus, setMapStatus] = useState("LOADING"); const [weatherStatus, setWeatherStatus] = useState("WAITING"); const [result, setResult] = useState("Waiting for map/style"); @@ -81,6 +88,9 @@ export default function App() { const refresh = useCallback(async (tag: number) => { setSnapshot(await FieldRuntime.getState(tag)); }, []); + const refreshRetained = useCallback(async (tag: number) => { + setRetainedSnapshot(await RendererRuntime.getRetainedUnderlayDiagnostics(tag)); + }, []); const applyVisualToTag = useCallback(async (tag: number) => { const results = await Promise.all([ FieldRuntime.setParticleCount(tag, Number(visual.count)), @@ -254,12 +264,12 @@ export default function App() { try { await FieldRuntime.attach(tag); setReactTag(tag); - await refresh(tag); + await Promise.all([refresh(tag), refreshRetained(tag)]); setResult("FieldRuntime attached / particle.10"); } catch (error) { setResult("attach error: " + String(error)); } - }, [refresh]); + }, [refresh, refreshRetained]); const onStyleReady = useCallback((event: unknown) => { const tag = findNodeHandle((event as { target?: unknown }).target as never); if (tag != null) void attach(tag); @@ -300,6 +310,12 @@ export default function App() { setParticleVisible(next); } }, [particleVisible, reactTag]); + const toggleRetained = useCallback(async () => { + if (reactTag == null) return; + const next = !(retainedSnapshot?.enabled ?? true); + const result = await RendererRuntime.setRetainedUnderlayEnabled(reactTag, next); + if (result === "ACCEPTED") await refreshRetained(reactTag); + }, [reactTag, refreshRetained, retainedSnapshot?.enabled]); const updateVisual = useCallback( (key: keyof typeof initialVisual, value: string) => { @@ -344,6 +360,22 @@ export default function App() { Source: {activeSource} {result} + + Retained: {retainedSnapshot == null ? "not read" : + `${retainedSnapshot.enabled ? "ON" : "OFF"} build=${retainedSnapshot.build} reuse=${retainedSnapshot.reuse} fallback=${retainedSnapshot.fallback} invalidation=${retainedSnapshot.invalidation} reason=${retainedSnapshot.lastReason}`} + + +