v0.30.0 eb3068f

render_diagnostics.go RawHistoryFolder

1package cef
2
3import (
4	"time"
5
6	purecef "github.com/bnema/purego-cef/cef"
7	cef2gtk "github.com/bnema/purego-cef2gtk"
8
9	"github.com/bnema/dumber/internal/application/port"
10	"github.com/bnema/dumber/internal/logging"
11)
12
13const renderStallRecoveryCooldown = 10 * time.Second
14
15func (e *Engine) logAllWebViewRenderSnapshots(now time.Time, _ port.WebViewID, reason string, classification renderStallClassification) {
16	if e == nil {
17		return
18	}
19	e.activeWebViews.Range(func(_, value any) bool {
20		wv, ok := value.(*WebView)
21		if !ok || wv == nil || wv.viewBridge == nil || wv.destroyed.Load() {
22			return true
23		}
24		diag := wv.viewBridge.Diagnostics()
25		lastPaintAge := time.Duration(0)
26		if lastPaint := latestDiagnosticEventTime(diag, "accelerated-paint"); !lastPaint.IsZero() {
27			lastPaintAge = now.Sub(lastPaint)
28		}
29		wv.mu.RLock()
30		browser := wv.browser
31		uri := wv.uri
32		title := wv.title
33		isLoading := wv.isLoading
34		pendingURI := wv.pendingURI
35		wv.mu.RUnlock()
36		wv.logRenderDiagnosticSnapshot(
37			now, reason, classification, wv.viewBridge, diag, lastPaintAge,
38			browser, uri, title, isLoading, pendingURI, wv.audioPlaying.Load(),
39		)
40		return true
41	})
42}
43
44func (wv *WebView) logRenderDiagnosticSnapshot(
45	now time.Time,
46	reason string,
47	classification renderStallClassification,
48	bridge *Cef2gtkAdapter,
49	diag cef2gtk.Diagnostics,
50	lastPaintAge time.Duration,
51	browser purecef.Browser,
52	uri, title string,
53	isLoading bool,
54	pendingURI string,
55	audioPlaying bool,
56) {
57	if wv == nil || wv.ctx == nil {
58		return
59	}
60	var browserID int32
61	if browser != nil {
62		browserID = browser.GetIdentifier()
63	}
64	width, height := int32(0), int32(0)
65	scale := float32(0)
66	if bridge != nil {
67		width, height = bridge.Size()
68		scale = bridge.DeviceScaleFactor()
69	}
70	uiHeartbeat, ioHeartbeat := wv.cefHeartbeatSnapshots(now)
71	profile, profileAge, hasProfile := wv.latestCEF2GTKProfileSnapshot(now)
72	logging.FromContext(wv.ctx).Warn().
73		Str("reason", reason).
74		Str("classification", classification.Category).
75		Bool("cef_ui_blocked", classification.CEFUIBlocked).
76		Bool("cef_io_alive", classification.CEFIOAlive).
77		Uint64("webview_id", uint64(wv.id)).
78		Int32("browser_id", browserID).
79		Str("uri", logging.TruncateURL(uri, logging.PermissionLogURLMaxLen)).
80		Str("title", title).
81		Bool("is_loading", isLoading).
82		Str("pending_uri", logging.TruncateURL(pendingURI, logging.PermissionLogURLMaxLen)).
83		Bool("audio_playing", audioPlaying).
84		Uint64("audio_packets", wv.audioPacketCount.Load()).
85		Uint64("audio_writes", wv.audioWriteCount.Load()).
86		Int64("last_accelerated_paint_age_ms", durationMillis(lastPaintAge)).
87		Str("render_backend", diag.Backend).
88		Int("accelerated_paints", diag.AcceleratedPaints).
89		Int("unsupported_paints", diag.UnsupportedPaints).
90		Int("accelerated_paint_errors", diag.AcceleratedPaintErrors).
91		Int("import_failures", diag.ImportFailures).
92		Int("render_failures", diag.RenderFailures).
93		Int("textures_built", diag.TexturesBuilt).
94		Int("texture_build_failures", diag.TextureBuildFailures).
95		Int("fd_dup_failures", diag.FDDupFailures).
96		Int("unsupported_formats", diag.UnsupportedFormats).
97		Int("paintable_swaps", diag.PaintableSwaps).
98		Bool("pending_frame", diag.PendingFrame).
99		Bool("pending_scheduled", diag.PendingScheduled).
100		Int64("pending_age_ms", durationMillis(diag.PendingAge)).
101		Uint("pending_source_id", diag.PendingSourceID).
102		Int("pending_reschedules", diag.PendingReschedules).
103		Int("pending_schedule_failures", diag.PendingScheduleFailures).
104		Int("pending_idle_callbacks", diag.PendingIdleCallbacks).
105		Int32("surface_width", width).
106		Int32("surface_height", height).
107		Float32("surface_scale", scale).
108		Bool("external_begin_frame", externalBeginFrameEnabled()).
109		Bool("profile_snapshot_available", hasProfile).
110		Int64("profile_snapshot_age_ms", durationMillis(profileAge)).
111		Uint64("profile_frames_received", profile.FramesReceived).
112		Uint64("profile_frames_queued", profile.FramesQueued).
113		Uint64("profile_frames_rendered", profile.FramesRendered).
114		Uint64("profile_import_failures", profile.ImportFailures).
115		Uint64("profile_render_failures", profile.RenderFailures).
116		Uint32("profile_gc_delta", profile.GC.NumGCDelta).
117		Int64("cef_ui_heartbeat_ack_age_ms", durationMillis(uiHeartbeat.LastAckAge)).
118		Int64("cef_ui_heartbeat_latency_ms", durationMillis(uiHeartbeat.Latency)).
119		Int32("cef_ui_heartbeat_post_result", uiHeartbeat.PostResult).
120		Bool("cef_ui_heartbeat_in_flight", uiHeartbeat.InFlight).
121		Int64("cef_io_heartbeat_ack_age_ms", durationMillis(ioHeartbeat.LastAckAge)).
122		Int64("cef_io_heartbeat_latency_ms", durationMillis(ioHeartbeat.Latency)).
123		Int32("cef_io_heartbeat_post_result", ioHeartbeat.PostResult).
124		Bool("cef_io_heartbeat_in_flight", ioHeartbeat.InFlight).
125		Msg("cef: render diagnostic snapshot")
126}
127
128func (wv *WebView) maybeRunRenderStallRecovery(now time.Time, _ *Cef2gtkAdapter, paintsBefore int) {
129	if wv == nil || wv.ctx == nil || wv.destroyed.Load() || !renderStallRecoveryEnabled() {
130		return
131	}
132	wv.renderStallMu.Lock()
133	last := wv.renderStallRecoveryLastAt
134	if !last.IsZero() && now.Sub(last) < renderStallRecoveryCooldown {
135		wv.renderStallMu.Unlock()
136		return
137	}
138	wv.renderStallMu.Unlock()
139
140	wv.mu.RLock()
141	host := wv.host
142	browser := wv.browser
143	wv.mu.RUnlock()
144	if host == nil {
145		return
146	}
147	browserID := int32(0)
148	if browser != nil {
149		browserID = browser.GetIdentifier()
150	}
151	logging.FromContext(wv.ctx).Warn().
152		Uint64("webview_id", uint64(wv.id)).
153		Int32("browser_id", browserID).
154		Int("accelerated_paints_before", paintsBefore).
155		Msg("cef: render stall recovery pulse scheduled")
156	task := cefNewTask(cefTaskFunc(func() {
157		if wv == nil || wv.viewBridge == nil || wv.destroyed.Load() || wv.ctx == nil || host == nil {
158			return
159		}
160		host.Invalidate(purecef.PaintElementTypePetView)
161		host.WasHidden(1)
162		host.WasHidden(0)
163		host.NotifyScreenInfoChanged()
164		host.WasResized()
165		host.Invalidate(purecef.PaintElementTypePetView)
166		logging.FromContext(wv.ctx).Warn().
167			Uint64("webview_id", uint64(wv.id)).
168			Int32("browser_id", browserID).
169			Msg("cef: render stall recovery pulse executed")
170	}))
171	if task == nil {
172		return
173	}
174	result := cefPostTask(purecef.ThreadIDTidUi, task)
175	logging.FromContext(wv.ctx).Warn().
176		Uint64("webview_id", uint64(wv.id)).
177		Int32("browser_id", browserID).
178		Int32("post_result", result).
179		Msg("cef: render stall recovery pulse posted")
180	if result != 1 {
181		return
182	}
183	wv.renderStallMu.Lock()
184	wv.renderStallRecoveryLastAt = now
185	wv.renderStallMu.Unlock()
186	cefScheduleAfter(2*time.Second, func() {
187		if wv == nil || wv.viewBridge == nil || wv.destroyed.Load() || wv.ctx == nil {
188			return
189		}
190		after := wv.viewBridge.Diagnostics().AcceleratedPaints
191		logging.FromContext(wv.ctx).Warn().
192			Uint64("webview_id", uint64(wv.id)).
193			Int32("browser_id", browserID).
194			Int("accelerated_paints_before", paintsBefore).
195			Int("accelerated_paints_after", after).
196			Bool("recovered", after > paintsBefore).
197			Msg("cef: render stall recovery pulse result")
198	})
199}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 8

Tags 42

↑↓ moveenter openesc close