perf(daemon): avoid copying cached styles per popup
- commit
- 34b0481e11c95bca3955509a49fdca3c1f016a85
- author
- brice · · 2026-07-18 10:14 UTC
- parent
- f5267fd
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
3 files · +34 -29
M internal/usecase/daemon/floating_render.go +14 -12
| @@ -86,15 +86,16 @@ func calculateContentFloatingGeometry(content domain.Size, cfg domain.FloatingCo | |||
| 86 | 86 | } | |
| 87 | 87 | ||
| 88 | 88 | type floatingComposeInput struct { | |
| 89 | - | baseFrame renderer.Frame | |
| 90 | - | baseDamage []renderer.Damage | |
| 91 | - | floating capturedFloatingRenderState | |
| 92 | - | content domain.Rect | |
| 93 | - | layout capturedTabLayout | |
| 94 | - | theme themeui.Theme | |
| 95 | - | styles themeui.Styles | |
| 96 | - | cache composeCacheInput | |
| 97 | - | full bool | |
| 89 | + | baseFrame renderer.Frame | |
| 90 | + | baseDamage []renderer.Damage | |
| 91 | + | floating capturedFloatingRenderState | |
| 92 | + | content domain.Rect | |
| 93 | + | layout capturedTabLayout | |
| 94 | + | theme themeui.Theme | |
| 95 | + | borderMuted renderer.Style | |
| 96 | + | borderActive renderer.Style | |
| 97 | + | cache composeCacheInput | |
| 98 | + | full bool | |
| 98 | 99 | } | |
| 99 | 100 | ||
| 100 | 101 | func composeCapturedFloatingFrame(input floatingComposeInput) (renderer.Frame, []renderer.Damage) { | |
| @@ -104,7 +105,8 @@ func composeCapturedFloatingFrame(input floatingComposeInput) (renderer.Frame, [ | |||
| 104 | 105 | content := input.content | |
| 105 | 106 | layoutSnap := input.layout | |
| 106 | 107 | theme := input.theme | |
| 107 | - | styles := input.styles | |
| 108 | + | borderMuted := input.borderMuted | |
| 109 | + | borderActive := input.borderActive | |
| 108 | 110 | cache := input.cache | |
| 109 | 111 | full := input.full | |
| 110 | 112 | ||
| @@ -119,9 +121,9 @@ func composeCapturedFloatingFrame(input floatingComposeInput) (renderer.Frame, [ | |||
| 119 | 121 | } | |
| 120 | 122 | popupChanged := !cache.valid || cache.floatingGeneration != floating.generation || cache.floatingGeometry != geometry || cache.floatingFocused != floating.focused | |
| 121 | 123 | titleChanged := popupChanged || cache.floatingTitleGeneration != floating.titleGeneration | |
| 122 | - | border := styles.BorderMuted | |
| 124 | + | border := borderMuted | |
| 123 | 125 | if floating.focused { | |
| 124 | - | border = styles.BorderActive | |
| 126 | + | border = borderActive | |
| 125 | 127 | } | |
| 126 | 128 | drawFloatingBorder(frame, geometry.Bounds, floating.title, border) | |
| 127 | 129 | if full || popupChanged { | |
| @@ -86,15 +86,16 @@ func calculateContentFloatingGeometry(content domain.Size, cfg domain.FloatingCo | |||||
| 86 | } | 86 | } | ||
| 87 | 87 | ||||
| 88 | type floatingComposeInput struct { | 88 | type floatingComposeInput struct { | ||
| 89 | - | baseFrame renderer.Frame | 89 | + | baseFrame renderer.Frame |
| 90 | - | baseDamage []renderer.Damage | 90 | + | baseDamage []renderer.Damage |
| 91 | - | floating capturedFloatingRenderState | 91 | + | floating capturedFloatingRenderState |
| 92 | - | content domain.Rect | 92 | + | content domain.Rect |
| 93 | - | layout capturedTabLayout | 93 | + | layout capturedTabLayout |
| 94 | - | theme themeui.Theme | 94 | + | theme themeui.Theme |
| 95 | - | styles themeui.Styles | 95 | + | borderMuted renderer.Style |
| 96 | - | cache composeCacheInput | 96 | + | borderActive renderer.Style |
| 97 | - | full bool | 97 | + | cache composeCacheInput |
| 98 | + | full bool | |||
| 98 | } | 99 | } | ||
| 99 | 100 | ||||
| 100 | func composeCapturedFloatingFrame(input floatingComposeInput) (renderer.Frame, []renderer.Damage) { | 101 | func composeCapturedFloatingFrame(input floatingComposeInput) (renderer.Frame, []renderer.Damage) { | ||
| @@ -104,7 +105,8 @@ func composeCapturedFloatingFrame(input floatingComposeInput) (renderer.Frame, [ | |||||
| 104 | content := input.content | 105 | content := input.content | ||
| 105 | layoutSnap := input.layout | 106 | layoutSnap := input.layout | ||
| 106 | theme := input.theme | 107 | theme := input.theme | ||
| 107 | - | styles := input.styles | 108 | + | borderMuted := input.borderMuted |
| 109 | + | borderActive := input.borderActive | |||
| 108 | cache := input.cache | 110 | cache := input.cache | ||
| 109 | full := input.full | 111 | full := input.full | ||
| 110 | 112 | ||||
| @@ -119,9 +121,9 @@ func composeCapturedFloatingFrame(input floatingComposeInput) (renderer.Frame, [ | |||||
| 119 | } | 121 | } | ||
| 120 | popupChanged := !cache.valid || cache.floatingGeneration != floating.generation || cache.floatingGeometry != geometry || cache.floatingFocused != floating.focused | 122 | popupChanged := !cache.valid || cache.floatingGeneration != floating.generation || cache.floatingGeometry != geometry || cache.floatingFocused != floating.focused | ||
| 121 | titleChanged := popupChanged || cache.floatingTitleGeneration != floating.titleGeneration | 123 | titleChanged := popupChanged || cache.floatingTitleGeneration != floating.titleGeneration | ||
| 122 | - | border := styles.BorderMuted | 124 | + | border := borderMuted |
| 123 | if floating.focused { | 125 | if floating.focused { | ||
| 124 | - | border = styles.BorderActive | 126 | + | border = borderActive |
| 125 | } | 127 | } | ||
| 126 | drawFloatingBorder(frame, geometry.Bounds, floating.title, border) | 128 | drawFloatingBorder(frame, geometry.Bounds, floating.title, border) | ||
| 127 | if full || popupChanged { | 129 | if full || popupChanged { | ||
M internal/usecase/daemon/floating_render_test.go +10 -8
| @@ -199,9 +199,10 @@ func TestComposeCapturedFloatingFrameUsesSemanticBorderWithoutTintingPaneCells(t | |||
| 199 | 199 | floating: capturedFloatingRenderState{ | |
| 200 | 200 | visible: true, focused: true, pane: capturedPaneRenderState{frame: pane}, geometry: geometry, title: "float", generation: 1, | |
| 201 | 201 | }, | |
| 202 | - | content: content, | |
| 203 | - | styles: themeui.Styles{BorderActive: active, BorderMuted: muted}, | |
| 204 | - | full: true, | |
| 202 | + | content: content, | |
| 203 | + | borderActive: active, | |
| 204 | + | borderMuted: muted, | |
| 205 | + | full: true, | |
| 205 | 206 | }) | |
| 206 | 207 | bounds := geometry.translate(content.X, content.Y) | |
| 207 | 208 | require.Equal(t, active, frame.At(bounds.Bounds.X, bounds.Bounds.Y).Style, "focused floating border must use BorderActive") | |
| @@ -220,11 +221,12 @@ func TestComposeCapturedFloatingFrameUsesMutedBorderWhenUnfocused(t *testing.T) | |||
| 220 | 221 | active, muted := renderer.Style{Foreground: 2}, renderer.Style{Foreground: 4} | |
| 221 | 222 | ||
| 222 | 223 | frame, _ := composeCapturedFloatingFrame(floatingComposeInput{ | |
| 223 | - | baseFrame: base, | |
| 224 | - | floating: capturedFloatingRenderState{visible: true, pane: capturedPaneRenderState{frame: renderer.NewFrame(4, 1)}, geometry: geometry, title: "float", generation: 1}, | |
| 225 | - | content: content, | |
| 226 | - | styles: themeui.Styles{BorderActive: active, BorderMuted: muted}, | |
| 227 | - | full: true, | |
| 224 | + | baseFrame: base, | |
| 225 | + | floating: capturedFloatingRenderState{visible: true, pane: capturedPaneRenderState{frame: renderer.NewFrame(4, 1)}, geometry: geometry, title: "float", generation: 1}, | |
| 226 | + | content: content, | |
| 227 | + | borderActive: active, | |
| 228 | + | borderMuted: muted, | |
| 229 | + | full: true, | |
| 228 | 230 | }) | |
| 229 | 231 | bounds := geometry.translate(content.X, content.Y) | |
| 230 | 232 | require.Equal(t, muted, frame.At(bounds.Bounds.X, bounds.Bounds.Y).Style) | |
| @@ -199,9 +199,10 @@ func TestComposeCapturedFloatingFrameUsesSemanticBorderWithoutTintingPaneCells(t | |||||
| 199 | floating: capturedFloatingRenderState{ | 199 | floating: capturedFloatingRenderState{ | ||
| 200 | visible: true, focused: true, pane: capturedPaneRenderState{frame: pane}, geometry: geometry, title: "float", generation: 1, | 200 | visible: true, focused: true, pane: capturedPaneRenderState{frame: pane}, geometry: geometry, title: "float", generation: 1, | ||
| 201 | }, | 201 | }, | ||
| 202 | - | content: content, | 202 | + | content: content, |
| 203 | - | styles: themeui.Styles{BorderActive: active, BorderMuted: muted}, | 203 | + | borderActive: active, |
| 204 | - | full: true, | 204 | + | borderMuted: muted, |
| 205 | + | full: true, | |||
| 205 | }) | 206 | }) | ||
| 206 | bounds := geometry.translate(content.X, content.Y) | 207 | bounds := geometry.translate(content.X, content.Y) | ||
| 207 | require.Equal(t, active, frame.At(bounds.Bounds.X, bounds.Bounds.Y).Style, "focused floating border must use BorderActive") | 208 | require.Equal(t, active, frame.At(bounds.Bounds.X, bounds.Bounds.Y).Style, "focused floating border must use BorderActive") | ||
| @@ -220,11 +221,12 @@ func TestComposeCapturedFloatingFrameUsesMutedBorderWhenUnfocused(t *testing.T) | |||||
| 220 | active, muted := renderer.Style{Foreground: 2}, renderer.Style{Foreground: 4} | 221 | active, muted := renderer.Style{Foreground: 2}, renderer.Style{Foreground: 4} | ||
| 221 | 222 | ||||
| 222 | frame, _ := composeCapturedFloatingFrame(floatingComposeInput{ | 223 | frame, _ := composeCapturedFloatingFrame(floatingComposeInput{ | ||
| 223 | - | baseFrame: base, | 224 | + | baseFrame: base, |
| 224 | - | floating: capturedFloatingRenderState{visible: true, pane: capturedPaneRenderState{frame: renderer.NewFrame(4, 1)}, geometry: geometry, title: "float", generation: 1}, | 225 | + | floating: capturedFloatingRenderState{visible: true, pane: capturedPaneRenderState{frame: renderer.NewFrame(4, 1)}, geometry: geometry, title: "float", generation: 1}, |
| 225 | - | content: content, | 226 | + | content: content, |
| 226 | - | styles: themeui.Styles{BorderActive: active, BorderMuted: muted}, | 227 | + | borderActive: active, |
| 227 | - | full: true, | 228 | + | borderMuted: muted, |
| 229 | + | full: true, | |||
| 228 | }) | 230 | }) | ||
| 229 | bounds := geometry.translate(content.X, content.Y) | 231 | bounds := geometry.translate(content.X, content.Y) | ||
| 230 | require.Equal(t, muted, frame.At(bounds.Bounds.X, bounds.Bounds.Y).Style) | 232 | require.Equal(t, muted, frame.At(bounds.Bounds.X, bounds.Bounds.Y).Style) | ||
M internal/usecase/daemon/render_pipeline.go +10 -9
| @@ -130,15 +130,16 @@ func composeFrame(state capturedRenderState, in composeCacheInput, scratchIn ... | |||
| 130 | 130 | if state.floating.visible { | |
| 131 | 131 | var floatingDamage []renderer.Damage | |
| 132 | 132 | frame, floatingDamage = composeCapturedFloatingFrame(floatingComposeInput{ | |
| 133 | - | baseFrame: baseFrame, | |
| 134 | - | baseDamage: damage, | |
| 135 | - | floating: state.floating, | |
| 136 | - | content: content, | |
| 137 | - | layout: state.layout, | |
| 138 | - | theme: state.theme, | |
| 139 | - | styles: styles, | |
| 140 | - | cache: in, | |
| 141 | - | full: full || state.overlays.active(), | |
| 133 | + | baseFrame: baseFrame, | |
| 134 | + | baseDamage: damage, | |
| 135 | + | floating: state.floating, | |
| 136 | + | content: content, | |
| 137 | + | layout: state.layout, | |
| 138 | + | theme: state.theme, | |
| 139 | + | borderMuted: styles.BorderMuted, | |
| 140 | + | borderActive: styles.BorderActive, | |
| 141 | + | cache: in, | |
| 142 | + | full: full || state.overlays.active(), | |
| 142 | 143 | }) | |
| 143 | 144 | damage = floatingDamage | |
| 144 | 145 | } | |
| @@ -130,15 +130,16 @@ func composeFrame(state capturedRenderState, in composeCacheInput, scratchIn ... | |||||
| 130 | if state.floating.visible { | 130 | if state.floating.visible { | ||
| 131 | var floatingDamage []renderer.Damage | 131 | var floatingDamage []renderer.Damage | ||
| 132 | frame, floatingDamage = composeCapturedFloatingFrame(floatingComposeInput{ | 132 | frame, floatingDamage = composeCapturedFloatingFrame(floatingComposeInput{ | ||
| 133 | - | baseFrame: baseFrame, | 133 | + | baseFrame: baseFrame, |
| 134 | - | baseDamage: damage, | 134 | + | baseDamage: damage, |
| 135 | - | floating: state.floating, | 135 | + | floating: state.floating, |
| 136 | - | content: content, | 136 | + | content: content, |
| 137 | - | layout: state.layout, | 137 | + | layout: state.layout, |
| 138 | - | theme: state.theme, | 138 | + | theme: state.theme, |
| 139 | - | styles: styles, | 139 | + | borderMuted: styles.BorderMuted, |
| 140 | - | cache: in, | 140 | + | borderActive: styles.BorderActive, |
| 141 | - | full: full || state.overlays.active(), | 141 | + | cache: in, |
| 142 | + | full: full || state.overlays.active(), | |||
| 142 | }) | 143 | }) | ||
| 143 | damage = floatingDamage | 144 | damage = floatingDamage | ||
| 144 | } | 145 | } | ||