Poser le filtre du fond OSM sur la couche et réduire la réserve de tuiles LiDAR

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Antoine Jacquin
2026-09-27 12:15:53 +02:00
parent 453530ec3a
commit 759cb96dcc
2 changed files with 10 additions and 3 deletions

View File

@ -40,6 +40,7 @@
- **Carte autonome (Pi sans worker)** : amont (`LIDAR_SOURCE_URL`/`LIDAR_MAPS_URL`) éteint ⇒ la carte reste servie depuis le disque. `_remote_payload` mémorise aussi l'échec (TTL 60 s, délai 10 s : sinon chaque requête repayait le délai réseau et `/api/map/meta` ne répondait plus), puis l'index local (`_build_index`) prend le relais ; coupe-circuits sur les sources (`_SOURCE_OFFLINE`, 60 s) et les tuiles (`_UPSTREAM`, réarmé à expiration) ; source rapatriée datée à sa version amont (`os.utime`) pour que l'index local garde les mêmes dates ; pas de requête amont pour une tuile sans dalle quand l'inventaire vient de l'amont. En cache seule, une tuile périmée reste servie (`no-store`, `X-Tile-Pending`) en attendant la nouvelle. - **Carte autonome (Pi sans worker)** : amont (`LIDAR_SOURCE_URL`/`LIDAR_MAPS_URL`) éteint ⇒ la carte reste servie depuis le disque. `_remote_payload` mémorise aussi l'échec (TTL 60 s, délai 10 s : sinon chaque requête repayait le délai réseau et `/api/map/meta` ne répondait plus), puis l'index local (`_build_index`) prend le relais ; coupe-circuits sur les sources (`_SOURCE_OFFLINE`, 60 s) et les tuiles (`_UPSTREAM`, réarmé à expiration) ; source rapatriée datée à sa version amont (`os.utime`) pour que l'index local garde les mêmes dates ; pas de requête amont pour une tuile sans dalle quand l'inventaire vient de l'amont. En cache seule, une tuile périmée reste servie (`no-store`, `X-Tile-Pending`) en attendant la nouvelle.
- **Pyramide téléchargée en tâche de fond** : avec `LIDAR_MAPS_URL`, la maintenance (`_bg_process_one`) TÉLÉCHARGE les tuiles des niveaux stockés depuis l'amont (stat `telechargees`), sans attendre l'affichage ; rendu local en repli si l'amont ne répond pas. - **Pyramide téléchargée en tâche de fond** : avec `LIDAR_MAPS_URL`, la maintenance (`_bg_process_one`) TÉLÉCHARGE les tuiles des niveaux stockés depuis l'amont (stat `telechargees`), sans attendre l'affichage ; rendu local en repli si l'amont ne répond pas.
- **Première apparition des dalles (`_apply_seen`, `index_xyz/.sources_seen.json`)** : date effective d'une source = max(version, première entrée dans l'inventaire). Une dalle écrite avant mais inventoriée après une tuile (TTL de l'index amont, anti-rebond de l'inventaire) périme donc la tuile — sinon trou permanent à ce niveau, sur disque, en mémoire et dans le navigateur (stamp `?v=` inchangé). Registre persistant ; absent avec un cache existant (mise à jour) ⇒ tout est périmé une fois. - **Première apparition des dalles (`_apply_seen`, `index_xyz/.sources_seen.json`)** : date effective d'une source = max(version, première entrée dans l'inventaire). Une dalle écrite avant mais inventoriée après une tuile (TTL de l'index amont, anti-rebond de l'inventaire) périme donc la tuile — sinon trou permanent à ce niveau, sur disque, en mémoire et dans le navigateur (stamp `?v=` inchangé). Registre persistant ; absent avec un cache existant (mise à jour) ⇒ tout est périmé une fois.
- **Coût navigateur de la carte (mesuré sous Chrome, Intel Iris Xe)** : le filtre d'assombrissement du fond OSM (`.base-dark`) est posé sur le **conteneur** de la couche, jamais sur chaque tuile — rendu identique (opérations par pixel), processus GPU 92 % → 56 % en déplacement et 99 % → 53 % au zoom molette. Couches LiDAR en `keepBuffer: 1` (tuiles 512 px = 1 Mo décodé chacune). Mesuré sans effet notable : `backdrop-filter` des cartes, `isolation`/`mix-blend-mode` en « normal », `will-change`. Tas JS 2-5 Mo, CPU au repos ~1 %. Décodage AVIF ~12 ms/tuile contre ~5 ms en WebP (compromis assumé : stockage AVIF).
- **Encodage AVIF rapide** : `AVIF_SPEED = 9` (`rendering.py`, `tiles.py`, `_SUBTILE_AVIF_SPEED` dans `index.py`) — dalle 5000 × 5000 px encodée en 0,6 s au lieu de 4 s (+3 % de taille, −0,3 dB) ; l'encodage était l'étape la plus longue du rendu d'une couche. - **Encodage AVIF rapide** : `AVIF_SPEED = 9` (`rendering.py`, `tiles.py`, `_SUBTILE_AVIF_SPEED` dans `index.py`) — dalle 5000 × 5000 px encodée en 0,6 s au lieu de 4 s (+3 % de taille, −0,3 dB) ; l'encodage était l'étape la plus longue du rendu d'une couche.
- **Workers bornés par la VRAM** : chaque processus du pool prend une place fixe à sa création (`_init_worker_slot` dans `pipeline.py`, file multiprocessing) calculée par `gpu_worker_slots` (`gpu.py`) : au plus (VRAM libre − `LIDAR_GPU_RESERVE_MIB` 512) / `LIDAR_GPU_WORKER_MIB` (2048, pic estimé : contexte CUDA + calage conjoint CuPy en float64 + EDT de `_fill_nans`) workers par GPU, au moins un, entrelacés ; l'excédent tourne en CPU (`force_cpu`). L'ancien round-robin par numéro de fichier mettait 6 workers par RTX 5060 de 8 Go avec `LIDAR_WORKERS=auto` (12) : OOM. VRAM illisible (nvidia-smi en échec) : round-robin sans borne. - **Workers bornés par la VRAM** : chaque processus du pool prend une place fixe à sa création (`_init_worker_slot` dans `pipeline.py`, file multiprocessing) calculée par `gpu_worker_slots` (`gpu.py`) : au plus (VRAM libre − `LIDAR_GPU_RESERVE_MIB` 512) / `LIDAR_GPU_WORKER_MIB` (2048, pic estimé : contexte CUDA + calage conjoint CuPy en float64 + EDT de `_fill_nans`) workers par GPU, au moins un, entrelacés ; l'excédent tourne en CPU (`force_cpu`). L'ancien round-robin par numéro de fichier mettait 6 workers par RTX 5060 de 8 Go avec `LIDAR_WORKERS=auto` (12) : OOM. VRAM illisible (nvidia-smi en échec) : round-robin sans borne.
- **Préparation sur GPU** : `_fill_nans` (transformée de distance `cupyx`) et les gradients de `SharedDEM` passent sur GPU quand il est actif (repli CPU). `NUMBA_CACHE_DIR=/tmp/numba-cache` (Dockerfile) évite la recompilation des noyaux numba à chaque worker. - **Préparation sur GPU** : `_fill_nans` (transformée de distance `cupyx`) et les gradients de `SharedDEM` passent sur GPU quand il est actif (repli CPU). `NUMBA_CACHE_DIR=/tmp/numba-cache` (Dockerfile) évite la recompilation des noyaux numba à chaque worker.

View File

@ -172,8 +172,11 @@ html, body { height: 100%; overflow: hidden; background: var(--bg); color: var(-
font: 13px/1.45 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; } font: 13px/1.45 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; }
#map { position: absolute; inset: 0; background: var(--bg); } #map { position: absolute; inset: 0; background: var(--bg); }
.leaflet-container { background: var(--bg); font: inherit; } .leaflet-container { background: var(--bg); font: inherit; }
/* Fond OSM assombri pour laisser ressortir les rendus LiDAR. */ /* Fond OSM assombri pour laisser ressortir les rendus LiDAR. Filtre posé sur
.leaflet-tile-pane .leaflet-layer.base-dark .leaflet-tile { filter: brightness(0.38) contrast(1.1) saturate(0.7); } le conteneur de la couche, pas sur chaque tuile : même rendu (opérations
par pixel), mais une seule passe de filtre au lieu d'une par tuile —
−30 % de CPU (moteur de rendu + GPU) en déplacement, mesuré sous Chrome. */
.leaflet-tile-pane .leaflet-layer.base-dark { filter: brightness(0.38) contrast(1.1) saturate(0.7); }
.card { position: fixed; z-index: 900; background: var(--card); color: var(--text); .card { position: fixed; z-index: 900; background: var(--card); color: var(--text);
border: 1px solid var(--border); border-radius: var(--radius); border: 1px solid var(--border); border-radius: var(--radius);
@ -604,7 +607,10 @@ function buildLayers() {
maxNativeZoom: META.max_native_zoom, maxNativeZoom: META.max_native_zoom,
minZoom: 0, minZoom: 0,
maxZoom: 22, maxZoom: 22,
keepBuffer: 2, // Tuiles de 512 px : une rangée de réserve hors écran vaut les deux
// rangées de 256 px du fond. Deux rangées doublaient les images
// décodées gardées en mémoire (1 Mo chacune) pour peu de gain.
keepBuffer: 1,
updateWhenZooming: false, updateWhenZooming: false,
crossOrigin: false, crossOrigin: false,
errorTileUrl: 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7', errorTileUrl: 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7',