From 834204a58a20f81c92747befe2b7e9af54a66b08 Mon Sep 17 00:00:00 2001 From: 4aiman Date: Tue, 29 Sep 2026 13:20:53 +0300 Subject: [PATCH] Maps: canvas renderer for heavy-vector maps (SVG blew Chromium tile memory, tiles vanished) Viewing a big track (10865-pt, 60km) and zooming/panning threw 'cc/tiles/tile_manager tile memory limits exceeded, some content may not draw' dozens of times - Chromium's compositor tile-memory cap. The track is drawn by Leaflet's default SVG renderer, multi-layered (casing/core + per-1%-slope segments), so the giant SVG layer exceeds the raster budget and the map tiles stop drawing until a force-stop resets the compositor. Set preferCanvas:true on the heavy-vector view maps - showSessionDetail, showRoadsMap (many road polylines, also reported laggy), showComparisonView, showRouteDetail. Canvas draws into one viewport-bounded canvas instead of thousands of SVG paths. Left the recording map on SVG (rotate:true; leaflet-rotate barely supports Canvas) and the route editor (line clicks). --- PROJECT_MAP.md | 2 +- .../2026-09-29_startup-slowness.md | 26 +++++++++++++++++++ frontend/js/main.js | 12 ++++++--- 3 files changed, 35 insertions(+), 5 deletions(-) diff --git a/PROJECT_MAP.md b/PROJECT_MAP.md index 8b3b15d..41ef82e 100644 --- a/PROJECT_MAP.md +++ b/PROJECT_MAP.md @@ -142,7 +142,7 @@ ### Экраны - **Сегодня** — баланс ккал, еда (продукты + блюда), GPS тренировки, активность, вес, сон, добавки, прогресс-бар жидкости, нутриенты (приход + запасы) - **Продукты** — подтабы [Продукты | Рецепты], поиск, карточки с КБЖУ, добавление в дневник -- **Трек** — GPS тренировки (запись с HR), карта с компасом (leaflet-rotate). Навигационный «пак» позиции (SVG divIcon): круг цвета трека (на 20% темнее) с белой + 1px чёрной обводкой + треугольник, указывающий ВВЕРХ (карта и так доворачивается активным источником — курс/компас — вверх, поэтому без покадрового поворота: не трясёт). Мини-компас только в режиме компаса. Z-order панелями: тайлы < дороги(`roadsPane` 240) < маршрут(`routePane` 250) < трек(overlay) < пак(marker). course-up по GPS-курсу (карта и панель доворачиваются в ландшафт вместе, `bearing = h`), ландшафтный режим (CSS rotation), графики скорости/высоты/ЧСС, импорт GPX/FTB, dead reckoning, placeholder до GPS, карта дорог (дедуп 2м, цвета по вело/пешие/бег), сохранённые маршруты (оверлей-навигация, кнопка-тоггл «М», предпросмотр с профилем высоты, топо/уклон-обводка, тоггл «∡/⛰», высоты по цепочке GPXZ→open-elevation→open-meteo→OpenTopoData), де-джиттер трека (Савицкий-Голей, кнопка «≈» на детали), тоггл следования карты за позицией (`followMode`, кнопка `⊕` в `rec-map-btn` стеке; guard вокруг `panTo`/`setView` в `onPoint`/`onBatchUpdate`/`onStateChange`; выкл = свободный просмотр карты на ходу, повторный тап центрирует; не запоминается) +- **Трек** — GPS тренировки (запись с HR), карта с компасом (leaflet-rotate). Навигационный «пак» позиции (SVG divIcon): круг цвета трека (на 20% темнее) с белой + 1px чёрной обводкой + треугольник, указывающий ВВЕРХ (карта и так доворачивается активным источником — курс/компас — вверх, поэтому без покадрового поворота: не трясёт). Мини-компас только в режиме компаса. Z-order панелями: тайлы < дороги(`roadsPane` 240) < маршрут(`routePane` 250) < трек(overlay) < пак(marker). course-up по GPS-курсу (карта и панель доворачиваются в ландшафт вместе, `bearing = h`), ландшафтный режим (CSS rotation), графики скорости/высоты/ЧСС, импорт GPX/FTB, dead reckoning, placeholder до GPS, карта дорог (дедуп 2м, цвета по вело/пешие/бег), сохранённые маршруты (оверлей-навигация, кнопка-тоггл «М», предпросмотр с профилем высоты, топо/уклон-обводка, тоггл «∡/⛰», высоты по цепочке GPXZ→open-elevation→open-meteo→OpenTopoData), де-джиттер трека (Савицкий-Голей, кнопка «≈» на детали), тоггл следования карты за позицией (`followMode`, кнопка `⊕` в `rec-map-btn` стеке; guard вокруг `panTo`/`setView` в `onPoint`/`onBatchUpdate`/`onStateChange`; выкл = свободный просмотр карты на ходу, повторный тап центрирует; не запоминается). Карты с тяжёлыми векторами (`showSessionDetail`, `showRoadsMap`, `showComparisonView`, `showRouteDetail`) создаются с `preferCanvas: true` — большой трек/дороги в дефолтном SVG превышают лимит памяти тайлов компоновщика Chromium на зуме (`tile_manager tile memory limits exceeded`) и тайлы гаснут до перезапуска; canvas рисует в один холст. НЕ на карте записи (leaflet-rotate плохо дружит с Canvas) и не в редакторе маршрута (клик по линии) - **Добавки** — 41 нутриент (витамины, минералы, жирные кислоты, женьшень, ресвератрол, CoQ10) - **Настройки** — профиль, образ жизни, нормы ЧСС/давления, BLE пульсометр, велосипеды, GPS конфиг (в т.ч. цвет трека `trackColor`, цвета дорог, GPXZ-ключ), OTA обновления, импорт тайлов, карта дорог diff --git a/docs/session-logs/2026-09-29_startup-slowness.md b/docs/session-logs/2026-09-29_startup-slowness.md index 8b27f7b..4db9eb4 100644 --- a/docs/session-logs/2026-09-29_startup-slowness.md +++ b/docs/session-logs/2026-09-29_startup-slowness.md @@ -47,3 +47,29 @@ делал: область products/dedup имела баги (FK), трогать без нужды рискованно. Вынес пользователю на решение. - Тайминг-обвязку (JS [BOOT], Rust [SEED-TIMING]) убрал; lib.rs/main.js вернулись к HEAD. + +## Пропажа тайлов карты на большом треке (отдельный баг) +Симптом: смотришь трек 60 км (сессия 1790501377655, 10865 точек), зумишь/таскаешь - тайлы карты +пропадают, помогает только force-stop+старт. + +Диагностика по живому logcat (пользователь воспроизвёл: открыл трек -> достроил -> сохранил -> +reopen -> убрал достройку -> зум/таскание; буфер предварительно увеличил до 16МБ и очистил): +- `E chromium: [ERROR:cc/tiles/tile_manager.cc:1046] tile memory limits exceeded, some content + may not draw` - 40+ раз ровно в окно зум/таскания. Это лимит памяти тайлов КОМПОНОВЩИКА + Chromium (не карты, не TileCache). Никаких OOM-килла, крахов рендера, CSP по тайлам. +- Причина: трек рисуется дефолтным SVG-рендерером Leaflet (`preferCanvas` нигде не задан), да + ещё многослойно (casing/core + раскраска по уклону мелкими сегментами). Гигантский SVG-слой на + весь трек при зуме превышает бюджет растровой памяти -> компоновщик перестаёт рисовать часть, + тайлы гаснут. Force-stop сбрасывает компоновщик. Проявляется на любом крупном треке/карте. + +Фикс (frontend/js/main.js): `preferCanvas: true` на картах с тяжёлыми векторами (canvas рисует +в один вьюпортный холст вместо тысяч SVG-путей): +- `showSessionDetail` (деталь трека, исходный баг), `showRoadsMap` (карта дорог - много + полилиний, пользователь отметил подтормаживания), `showComparisonView` (два трека), + `showRouteDetail` (превью маршрута с раскраской). +- НЕ трогал: `renderRecordingInline` (там `rotate:true`, а leaflet-rotate почти не поддерживает + Canvas - 1 ссылка на L.Canvas против 5 на SVG; canvas+rotate рискован, отдельная проверка), + редактор/рисование маршрута (клик/тап по линии, малые полилинии), цель-маркер (без треков). + +Проверка: воспроизвести зум/таскание на треке 60км и на карте дорог, снять logcat, убедиться, +что `tile memory limits exceeded` больше НЕ появляется, тайлы держатся. diff --git a/frontend/js/main.js b/frontend/js/main.js index 8746172..f4f91ec 100644 --- a/frontend/js/main.js +++ b/frontend/js/main.js @@ -3807,7 +3807,7 @@ async function showRouteDetail(container, routeId) { if (mapEl) { void mapEl.offsetHeight; // force layout so Leaflet reads the real size L.Icon.Default.imagePath = "lib/"; - let map = L.map(mapEl, { attributionControl: false, zoomControl: false }); + let map = L.map(mapEl, { preferCanvas: true, attributionControl: false, zoomControl: false }); // canvas: slope-coloured route vectors vs SVG compositor tile-memory limit map.setView([pts[0].lat, pts[0].lon], 13); // give the map a view NOW so vector layers render (full-page timing) new (TileCache.createLayer())({ maxZoom: 19 }).addTo(map); let routeLayer = drawRoutePolyline(map, pts, route.color, mode); @@ -5402,7 +5402,11 @@ async function showSessionDetail(sessionId) { if (mapEl && points.length) { void mapEl.offsetHeight; // force layout so Leaflet reads the real container size L.Icon.Default.imagePath = "lib/"; - let map = L.map(mapEl, { attributionControl: false, zoomControl: false, scrollWheelZoom: false, dragging: false, tap: false, touchZoom: true }); + // preferCanvas: big tracks (10k+ points, multi-layer slope colouring) as SVG blow the + // Chromium compositor's tile-memory limit on zoom/pan → map tiles stop drawing until a + // restart ("tile memory limits exceeded, some content may not draw"). Canvas renders the + // vectors into one viewport-bounded canvas instead of thousands of SVG paths. + let map = L.map(mapEl, { preferCanvas: true, attributionControl: false, zoomControl: false, scrollWheelZoom: false, dragging: false, tap: false, touchZoom: true }); mapEl._leafletMap = map; // save ref for winStack collapse // Ctrl+wheel zoom on desktop mapEl.addEventListener("wheel", (e) => { @@ -6436,7 +6440,7 @@ async function showComparisonView(currentId, compareId, currentSession, currentP if (btns) btns.style.display = "none"; L.Icon.Default.imagePath = "lib/"; - let map = L.map(mapEl, { attributionControl: false, zoomControl: false, touchZoom: true, dragging: true }); + let map = L.map(mapEl, { preferCanvas: true, attributionControl: false, zoomControl: false, touchZoom: true, dragging: true }); // canvas: two big tracks vs SVG compositor tile-memory limit mapEl._leafletMap = map; addZoomDisplay(map); let CachedLayer = TileCache.createLayer(); @@ -8386,7 +8390,7 @@ async function showRoadsMap() { let mapEl = document.querySelector("#roads-map"); L.Icon.Default.imagePath = "lib/"; - let map = L.map(mapEl, { attributionControl: false }).setView([47.42, 40.08], 13); + let map = L.map(mapEl, { preferCanvas: true, attributionControl: false }).setView([47.42, 40.08], 13); // canvas: many road polylines vs SVG compositor tile-memory limit addZoomDisplay(map); let CachedLayer = TileCache.createLayer(); new CachedLayer({ maxZoom: 19 }).addTo(map);