detached cf52ba6

perf(app): skip capture demultiplexing when hints are off

commit
cf52ba6c0b0fe6d65c68fa69fe3df9ee3265a471
author
brice · · 2026-10-19 15:34 UTC
parent
3bb35a7
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
3 files · +15 -8
M internal/app/hintoptin_test.go +4 -0
@@ -21,6 +21,8 @@ func TestHintPipesOffByDefault(t *testing.T) {
2121 require.Nil(t, off.hintScans)
2222 require.Nil(t, off.hintResults)
2323 require.Nil(t, off.hintCaptured)
24+ // No demultiplexing: the outputs reply on the wayland channel itself.
25+ require.Equal(t, off.captured, off.capturedAll)
2426 ch := off.core()
2527 require.Nil(t, ch.HintScans)
2628 require.Nil(t, ch.Hints)
@@ -28,6 +30,7 @@ func TestHintPipesOffByDefault(t *testing.T) {
2830 require.NotNil(t, on.hintScans)
2931 require.NotNil(t, on.hintResults)
3032 require.NotNil(t, on.hintCaptured)
33+ require.NotEqual(t, on.captured, on.capturedAll)
3134 }
3235
3336 func TestHintOffStartsNoHintGoroutine(t *testing.T) {
@@ -43,6 +46,7 @@ func TestHintOffStartsNoHintGoroutine(t *testing.T) {
4346 dump := string(buf[:runtime.Stack(buf, true)])
4447 require.NotContains(t, dump, "adapters/hints.")
4548 require.NotContains(t, dump, "adapters/atspi.")
49+ require.NotContains(t, dump, "routeCaptured")
4650 select {
4751 case <-done:
4852 case <-time.After(3 * time.Second):
@@ -21,6 +21,8 @@ func TestHintPipesOffByDefault(t *testing.T) {
21 require.Nil(t, off.hintScans)21 require.Nil(t, off.hintScans)
22 require.Nil(t, off.hintResults)22 require.Nil(t, off.hintResults)
23 require.Nil(t, off.hintCaptured)23 require.Nil(t, off.hintCaptured)
24+ // No demultiplexing: the outputs reply on the wayland channel itself.
25+ require.Equal(t, off.captured, off.capturedAll)
24 ch := off.core()26 ch := off.core()
25 require.Nil(t, ch.HintScans)27 require.Nil(t, ch.HintScans)
26 require.Nil(t, ch.Hints)28 require.Nil(t, ch.Hints)
@@ -28,6 +30,7 @@ func TestHintPipesOffByDefault(t *testing.T) {
28 require.NotNil(t, on.hintScans)30 require.NotNil(t, on.hintScans)
29 require.NotNil(t, on.hintResults)31 require.NotNil(t, on.hintResults)
30 require.NotNil(t, on.hintCaptured)32 require.NotNil(t, on.hintCaptured)
33+ require.NotEqual(t, on.captured, on.capturedAll)
31 }34 }
32 35
33 func TestHintOffStartsNoHintGoroutine(t *testing.T) {36 func TestHintOffStartsNoHintGoroutine(t *testing.T) {
@@ -43,6 +46,7 @@ func TestHintOffStartsNoHintGoroutine(t *testing.T) {
43 dump := string(buf[:runtime.Stack(buf, true)])46 dump := string(buf[:runtime.Stack(buf, true)])
44 require.NotContains(t, dump, "adapters/hints.")47 require.NotContains(t, dump, "adapters/hints.")
45 require.NotContains(t, dump, "adapters/atspi.")48 require.NotContains(t, dump, "adapters/atspi.")
49+ require.NotContains(t, dump, "routeCaptured")
46 select {50 select {
47 case <-done:51 case <-done:
48 case <-time.After(3 * time.Second):52 case <-time.After(3 * time.Second):
M internal/app/session.go +2 -3
@@ -346,10 +346,9 @@ func (s *session) run(ctx context.Context, inject func(chan<- ports.InputEvent))
346346 log.Warn().Err(err).Msg("capture allowlist not watched, restart to apply changes")
347347 }
348348 }()
349- workers.Add(1)
350- go func() { defer workers.Done(); routeCaptured(ctx, p.capturedAll, p.captured, p.internalCaptured()) }()
351349 if p.hintScans != nil {
352- workers.Add(1)
350+ workers.Add(2)
351+ go func() { defer workers.Done(); routeCaptured(ctx, p.capturedAll, p.captured, p.internalCaptured()) }()
353352 go func() {
354353 defer workers.Done()
355354 // Run only returns when ctx is done: nothing to report.
@@ -346,10 +346,9 @@ func (s *session) run(ctx context.Context, inject func(chan<- ports.InputEvent))
346 log.Warn().Err(err).Msg("capture allowlist not watched, restart to apply changes")346 log.Warn().Err(err).Msg("capture allowlist not watched, restart to apply changes")
347 }347 }
348 }()348 }()
349- workers.Add(1)
350- go func() { defer workers.Done(); routeCaptured(ctx, p.capturedAll, p.captured, p.internalCaptured()) }()
351 if p.hintScans != nil {349 if p.hintScans != nil {
352- workers.Add(1)350+ workers.Add(2)
351+ go func() { defer workers.Done(); routeCaptured(ctx, p.capturedAll, p.captured, p.internalCaptured()) }()
353 go func() {352 go func() {
354 defer workers.Done()353 defer workers.Done()
355 // Run only returns when ctx is done: nothing to report.354 // Run only returns when ctx is done: nothing to report.
M internal/app/wiring.go +9 -5
@@ -53,9 +53,10 @@ type pipes struct {
5353 captures chan ports.CaptureRequest
5454 // Every admitted capture owns one reply slot until routeCaptured takes
5555 // it. Keep this capacity tied to admission so output routing never waits.
56- // The outputs reply on capturedAll; routeCaptured splits the replies by
56+ // With hint mode on, the outputs reply on capturedAll and routeCaptured splits the replies by
5757 // ports.CaptureIDInternal into captured (the wayland consumer) and
58- // hintCaptured (internal consumers).
58+ // hintCaptured (internal consumers). With hint mode off, capturedAll is
59+ // captured itself and there is no routeCaptured.
5960 capturedAll chan ports.CaptureDone
6061 captured chan ports.CaptureDone
6162 // hintCaptured carries internal capture replies to the hints adapter.
@@ -110,9 +111,8 @@ func newPipes(realSession, hint bool) *pipes {
110111
111112 frames: make(chan ports.OutputFrame, 8),
112113
113- captures: make(chan ports.CaptureRequest, ports.MaxCaptureInflight),
114- capturedAll: make(chan ports.CaptureDone, ports.MaxCaptureInflight),
115- captured: make(chan ports.CaptureDone, ports.MaxCaptureInflight),
114+ captures: make(chan ports.CaptureRequest, ports.MaxCaptureInflight),
115+ captured: make(chan ports.CaptureDone, ports.MaxCaptureInflight),
116116
117117 outputFormats: make(chan ports.OutputFormats, 8),
118118 outputHeads: make(chan ports.OutputHeads, 8),
@@ -126,7 +126,11 @@ func newPipes(realSession, hint bool) *pipes {
126126
127127 castCommands: make(chan ports.CastCommand, 64),
128128 }
129+ // Without hint mode every reply is for the wayland consumer: the outputs
130+ // reply on its channel directly and nothing demultiplexes.
131+ p.capturedAll = p.captured
129132 if hint {
133+ p.capturedAll = make(chan ports.CaptureDone, ports.MaxCaptureInflight)
130134 p.hintScans = make(chan ports.HintScanRequest, 1)
131135 p.hintResults = make(chan ports.HintScanResult, 1)
132136 p.hintCaptured = make(chan ports.CaptureDone, 1)
@@ -53,9 +53,10 @@ type pipes struct {
53 captures chan ports.CaptureRequest53 captures chan ports.CaptureRequest
54 // Every admitted capture owns one reply slot until routeCaptured takes54 // Every admitted capture owns one reply slot until routeCaptured takes
55 // it. Keep this capacity tied to admission so output routing never waits.55 // it. Keep this capacity tied to admission so output routing never waits.
56- // The outputs reply on capturedAll; routeCaptured splits the replies by56+ // With hint mode on, the outputs reply on capturedAll and routeCaptured splits the replies by
57 // ports.CaptureIDInternal into captured (the wayland consumer) and57 // ports.CaptureIDInternal into captured (the wayland consumer) and
58- // hintCaptured (internal consumers).58+ // hintCaptured (internal consumers). With hint mode off, capturedAll is
59+ // captured itself and there is no routeCaptured.
59 capturedAll chan ports.CaptureDone60 capturedAll chan ports.CaptureDone
60 captured chan ports.CaptureDone61 captured chan ports.CaptureDone
61 // hintCaptured carries internal capture replies to the hints adapter.62 // hintCaptured carries internal capture replies to the hints adapter.
@@ -110,9 +111,8 @@ func newPipes(realSession, hint bool) *pipes {
110 111
111 frames: make(chan ports.OutputFrame, 8),112 frames: make(chan ports.OutputFrame, 8),
112 113
113- captures: make(chan ports.CaptureRequest, ports.MaxCaptureInflight),114+ captures: make(chan ports.CaptureRequest, ports.MaxCaptureInflight),
114- capturedAll: make(chan ports.CaptureDone, ports.MaxCaptureInflight),115+ captured: make(chan ports.CaptureDone, ports.MaxCaptureInflight),
115- captured: make(chan ports.CaptureDone, ports.MaxCaptureInflight),
116 116
117 outputFormats: make(chan ports.OutputFormats, 8),117 outputFormats: make(chan ports.OutputFormats, 8),
118 outputHeads: make(chan ports.OutputHeads, 8),118 outputHeads: make(chan ports.OutputHeads, 8),
@@ -126,7 +126,11 @@ func newPipes(realSession, hint bool) *pipes {
126 126
127 castCommands: make(chan ports.CastCommand, 64),127 castCommands: make(chan ports.CastCommand, 64),
128 }128 }
129+ // Without hint mode every reply is for the wayland consumer: the outputs
130+ // reply on its channel directly and nothing demultiplexes.
131+ p.capturedAll = p.captured
129 if hint {132 if hint {
133+ p.capturedAll = make(chan ports.CaptureDone, ports.MaxCaptureInflight)
130 p.hintScans = make(chan ports.HintScanRequest, 1)134 p.hintScans = make(chan ports.HintScanRequest, 1)
131 p.hintResults = make(chan ports.HintScanResult, 1)135 p.hintResults = make(chan ports.HintScanResult, 1)
132 p.hintCaptured = make(chan ports.CaptureDone, 1)136 p.hintCaptured = make(chan ports.CaptureDone, 1)

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 23

Tags 10

↑↓ moveenter openesc close