Skip to content

Commit 5115318

Browse files
committed
debug map freeze
1 parent c192c8f commit 5115318

1 file changed

Lines changed: 37 additions & 24 deletions

File tree

‎app/javascript/maplibre/map.js‎

Lines changed: 37 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -165,32 +165,48 @@ export async function initializeMap (divId = 'maplibre-map') {
165165
map.triggerRepaint()
166166
}, false)
167167

168-
// Visibility watchdog — surfaces map activity in the 3s after resume via
169-
// status toast (no console available on mobile PWA), so we can identify what
170-
// triggers the delayed freeze.
168+
// Diagnostic state shared with the input watchdog so freeze toasts can carry
169+
// the post-resume map activity summary. Tracked for 10s after each resume.
170+
let postResumeT0 = 0
171+
let postResumeRenderCount = 0
172+
let postResumeMaxGap = 0
173+
let postResumeLastRender = 0
174+
let postResumeCounts = {}
175+
const trackedEvents = ['load', 'idle', 'dataloading', 'data', 'sourcedataloading', 'sourcedata',
176+
'styledataloading', 'styledata', 'error', 'movestart', 'moveend', 'zoomstart', 'zoomend',
177+
'dragstart', 'dragend']
178+
179+
const diagnosticSummary = () => {
180+
const elapsed = Math.round(performance.now() - postResumeT0)
181+
return `t+${elapsed}ms R:${postResumeRenderCount} gap:${Math.round(postResumeMaxGap)}ms ` +
182+
Object.entries(postResumeCounts).filter(([_, n]) => n > 0)
183+
.map(([k, n]) => `${k.replace('source', 'src').replace('style', 'sty').replace('loading', 'L')}×${n}`).join(' ')
184+
}
185+
186+
// Visibility watchdog — silently tracks map activity for 10s after resume so
187+
// the input/render freeze toasts can include the diagnostic summary inline
188+
// (no separate toast that would be clobbered by the freeze toast).
171189
document.addEventListener('visibilitychange', () => {
172190
if (document.visibilityState !== 'visible') return
173191
status('Visibility: visible', 'info', 'medium', 1000)
174192

175-
const t0 = performance.now()
176-
const counts = {}
177-
const eventNames = ['load', 'idle', 'dataloading', 'data', 'sourcedataloading', 'sourcedata',
178-
'styledataloading', 'styledata', 'error', 'movestart', 'moveend', 'zoomstart', 'zoomend',
179-
'dragstart', 'dragend']
193+
postResumeT0 = performance.now()
194+
postResumeRenderCount = 0
195+
postResumeMaxGap = 0
196+
postResumeLastRender = postResumeT0
197+
postResumeCounts = {}
198+
180199
const handlers = {}
181-
eventNames.forEach(name => {
182-
handlers[name] = () => { counts[name] = (counts[name] || 0) + 1 }
200+
trackedEvents.forEach(name => {
201+
handlers[name] = () => { postResumeCounts[name] = (postResumeCounts[name] || 0) + 1 }
183202
map.on(name, handlers[name])
184203
})
185-
let lastRenderInWindow = t0
186-
let maxRenderGap = 0
187-
let renderCount = 0
188204
handlers.render = () => {
189205
const now = performance.now()
190-
const gap = now - lastRenderInWindow
191-
if (gap > maxRenderGap) maxRenderGap = gap
192-
lastRenderInWindow = now
193-
renderCount++
206+
const gap = now - postResumeLastRender
207+
if (gap > postResumeMaxGap) postResumeMaxGap = gap
208+
postResumeLastRender = now
209+
postResumeRenderCount++
194210
}
195211
map.on('render', handlers.render)
196212

@@ -200,17 +216,14 @@ export async function initializeMap (divId = 'maplibre-map') {
200216
const renderedSinceWatchdog = lastRenderAt > beforeRepaint
201217
const glLost = map.painter?.context?.gl?.isContextLost?.()
202218
if (!renderedSinceWatchdog || glLost) {
203-
status(`Map render frozen (gl_lost=${glLost})`, 'warning', 'medium', 3000)
219+
status(`Map render frozen (gl_lost=${glLost}) | ${diagnosticSummary()}`, 'warning', 'medium', 10000)
204220
}
205221
}, 500)
206222

207223
setTimeout(() => {
208-
eventNames.forEach(name => map.off(name, handlers[name]))
224+
trackedEvents.forEach(name => map.off(name, handlers[name]))
209225
map.off('render', handlers.render)
210-
const summary = `R:${renderCount} gap:${Math.round(maxRenderGap)}ms ` +
211-
Object.entries(counts).filter(([_, n]) => n > 0).map(([k, n]) => `${k.replace('source', 'src').replace('style', 'sty').replace('loading', 'L')}×${n}`).join(' ')
212-
status(summary, 'info', 'medium', 15000)
213-
}, 3000)
226+
}, 10000)
214227
})
215228

216229
// Input watchdog — detects handler-level freezes where render still works but
@@ -238,7 +251,7 @@ export async function initializeMap (divId = 'maplibre-map') {
238251
inputFreezeReported = true
239252
const glLost = map.painter?.context?.gl?.isContextLost?.()
240253
console.warn('Map drag input frozen', { glLost })
241-
status(`Map drag frozen (gl_lost=${glLost})`, 'warning', 'medium', 3000)
254+
status(`Map drag frozen (gl_lost=${glLost}) | ${diagnosticSummary()}`, 'warning', 'medium', 10000)
242255
})
243256
mapCanvas.addEventListener('pointerup', () => { pointerDownAt = null })
244257
mapCanvas.addEventListener('pointercancel', () => { pointerDownAt = null })

0 commit comments

Comments
 (0)