perf(drm): keep a float on the overlay plane over the lines painted under it
- commit
- 8f14070c0e59e602e50d9cc12ab7f53f1987acef
- author
- brice · · 2026-10-08 19:50 UTC
- parent
- b0baaac
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
2 files · +50 -2
M internal/adapters/drm/overlay.go +22 -2
| @@ -73,6 +73,7 @@ func overlayCandidate(s ports.Scene, surfaces map[ports.WindowID]ports.SurfaceCo | |||
| 73 | 73 | scale = 1 | |
| 74 | 74 | } | |
| 75 | 75 | var pick *ports.SceneWindow | |
| 76 | + | var pickAt int | |
| 76 | 77 | var mode colorMode | |
| 77 | 78 | for i := range s.Windows { | |
| 78 | 79 | w := &s.Windows[i] | |
| @@ -95,7 +96,7 @@ func overlayCandidate(s ports.Scene, surfaces map[ports.WindowID]ports.SurfaceCo | |||
| 95 | 96 | // lift; an overview preview is drawn smaller than its buffer. | |
| 96 | 97 | if w.Dim <= 0 && w.Fade <= 0 && w.Zoom <= 0 && w.FocusEffect <= 0 && w.Preview <= 0 && c.DMABuf != nil && !isYUVFormat(c.DMABuf.Format) && c.Opaque && len(c.Children) == 0 && c.Transform == 0 && !cropped(c) && (kept == nil || !kept(w.ID)) { | |
| 97 | 98 | if m, why := planeColor(c.Color, c.DMABuf.Format, c.Opaque, hdrOn, pipeline); why == "" { | |
| 98 | - | pick, mode = w, m | |
| 99 | + | pick, pickAt, mode = w, i, m | |
| 99 | 100 | continue | |
| 100 | 101 | } | |
| 101 | 102 | } | |
| @@ -143,7 +144,7 @@ func overlayCandidate(s ports.Scene, surfaces map[ports.WindowID]ports.SurfaceCo | |||
| 143 | 144 | return ports.SceneWindow{}, ports.SurfaceContent{}, colorBypass, "border" | |
| 144 | 145 | } | |
| 145 | 146 | for _, sep := range s.Separators { | |
| 146 | - | if sep.Rect.Overlaps(pick.Rect) { | |
| 147 | + | if sep.Rect.Overlaps(pick.Rect) && !linesUnder(s, sep, pick, pickAt) { | |
| 147 | 148 | // The overlay shows the buffer alone; the lines would go | |
| 148 | 149 | // under it. | |
| 149 | 150 | return ports.SceneWindow{}, ports.SurfaceContent{}, colorBypass, "border" | |
| @@ -170,6 +171,25 @@ func overlayCandidate(s ports.Scene, surfaces map[ports.WindowID]ports.SurfaceCo | |||
| 170 | 171 | return *pick, c, mode, "" | |
| 171 | 172 | } | |
| 172 | 173 | ||
| 174 | + | // linesUnder reports whether sep is painted before the candidate at index | |
| 175 | + | // pickAt, so stays composed under the plane: the tile lines and the lines | |
| 176 | + | // of windows listed before it, when the candidate is a float drawn above | |
| 177 | + | // the tiles. | |
| 178 | + | func linesUnder(s ports.Scene, sep ports.Separator, pick *ports.SceneWindow, pickAt int) bool { | |
| 179 | + | if !pick.Floating || pick.Below { | |
| 180 | + | return false | |
| 181 | + | } | |
| 182 | + | if sep.Window == 0 { | |
| 183 | + | return true | |
| 184 | + | } | |
| 185 | + | for _, w := range s.Windows[:pickAt] { | |
| 186 | + | if w.ID == sep.Window { | |
| 187 | + | return true | |
| 188 | + | } | |
| 189 | + | } | |
| 190 | + | return false | |
| 191 | + | } | |
| 192 | + | ||
| 173 | 193 | // overlayProps puts ov on the overlay plane, or turns it off. | |
| 174 | 194 | func (o *Output) overlayProps(req *atomicReq, ov overlayWin) { | |
| 175 | 195 | p := o.overlay | |
| @@ -73,6 +73,7 @@ func overlayCandidate(s ports.Scene, surfaces map[ports.WindowID]ports.SurfaceCo | |||||
| 73 | scale = 1 | 73 | scale = 1 | ||
| 74 | } | 74 | } | ||
| 75 | var pick *ports.SceneWindow | 75 | var pick *ports.SceneWindow | ||
| 76 | + | var pickAt int | |||
| 76 | var mode colorMode | 77 | var mode colorMode | ||
| 77 | for i := range s.Windows { | 78 | for i := range s.Windows { | ||
| 78 | w := &s.Windows[i] | 79 | w := &s.Windows[i] | ||
| @@ -95,7 +96,7 @@ func overlayCandidate(s ports.Scene, surfaces map[ports.WindowID]ports.SurfaceCo | |||||
| 95 | // lift; an overview preview is drawn smaller than its buffer. | 96 | // lift; an overview preview is drawn smaller than its buffer. | ||
| 96 | if w.Dim <= 0 && w.Fade <= 0 && w.Zoom <= 0 && w.FocusEffect <= 0 && w.Preview <= 0 && c.DMABuf != nil && !isYUVFormat(c.DMABuf.Format) && c.Opaque && len(c.Children) == 0 && c.Transform == 0 && !cropped(c) && (kept == nil || !kept(w.ID)) { | 97 | if w.Dim <= 0 && w.Fade <= 0 && w.Zoom <= 0 && w.FocusEffect <= 0 && w.Preview <= 0 && c.DMABuf != nil && !isYUVFormat(c.DMABuf.Format) && c.Opaque && len(c.Children) == 0 && c.Transform == 0 && !cropped(c) && (kept == nil || !kept(w.ID)) { | ||
| 97 | if m, why := planeColor(c.Color, c.DMABuf.Format, c.Opaque, hdrOn, pipeline); why == "" { | 98 | if m, why := planeColor(c.Color, c.DMABuf.Format, c.Opaque, hdrOn, pipeline); why == "" { | ||
| 98 | - | pick, mode = w, m | 99 | + | pick, pickAt, mode = w, i, m |
| 99 | continue | 100 | continue | ||
| 100 | } | 101 | } | ||
| 101 | } | 102 | } | ||
| @@ -143,7 +144,7 @@ func overlayCandidate(s ports.Scene, surfaces map[ports.WindowID]ports.SurfaceCo | |||||
| 143 | return ports.SceneWindow{}, ports.SurfaceContent{}, colorBypass, "border" | 144 | return ports.SceneWindow{}, ports.SurfaceContent{}, colorBypass, "border" | ||
| 144 | } | 145 | } | ||
| 145 | for _, sep := range s.Separators { | 146 | for _, sep := range s.Separators { | ||
| 146 | - | if sep.Rect.Overlaps(pick.Rect) { | 147 | + | if sep.Rect.Overlaps(pick.Rect) && !linesUnder(s, sep, pick, pickAt) { |
| 147 | // The overlay shows the buffer alone; the lines would go | 148 | // The overlay shows the buffer alone; the lines would go | ||
| 148 | // under it. | 149 | // under it. | ||
| 149 | return ports.SceneWindow{}, ports.SurfaceContent{}, colorBypass, "border" | 150 | return ports.SceneWindow{}, ports.SurfaceContent{}, colorBypass, "border" | ||
| @@ -170,6 +171,25 @@ func overlayCandidate(s ports.Scene, surfaces map[ports.WindowID]ports.SurfaceCo | |||||
| 170 | return *pick, c, mode, "" | 171 | return *pick, c, mode, "" | ||
| 171 | } | 172 | } | ||
| 172 | 173 | ||||
| 174 | + | // linesUnder reports whether sep is painted before the candidate at index | |||
| 175 | + | // pickAt, so stays composed under the plane: the tile lines and the lines | |||
| 176 | + | // of windows listed before it, when the candidate is a float drawn above | |||
| 177 | + | // the tiles. | |||
| 178 | + | func linesUnder(s ports.Scene, sep ports.Separator, pick *ports.SceneWindow, pickAt int) bool { | |||
| 179 | + | if !pick.Floating || pick.Below { | |||
| 180 | + | return false | |||
| 181 | + | } | |||
| 182 | + | if sep.Window == 0 { | |||
| 183 | + | return true | |||
| 184 | + | } | |||
| 185 | + | for _, w := range s.Windows[:pickAt] { | |||
| 186 | + | if w.ID == sep.Window { | |||
| 187 | + | return true | |||
| 188 | + | } | |||
| 189 | + | } | |||
| 190 | + | return false | |||
| 191 | + | } | |||
| 192 | + | ||||
| 173 | // overlayProps puts ov on the overlay plane, or turns it off. | 193 | // overlayProps puts ov on the overlay plane, or turns it off. | ||
| 174 | func (o *Output) overlayProps(req *atomicReq, ov overlayWin) { | 194 | func (o *Output) overlayProps(req *atomicReq, ov overlayWin) { | ||
| 175 | p := o.overlay | 195 | p := o.overlay | ||
M internal/adapters/drm/overlay_test.go +28 -0
| @@ -271,6 +271,34 @@ func TestOverlayPopupAndBorder(t *testing.T) { | |||
| 271 | 271 | } | |
| 272 | 272 | } | |
| 273 | 273 | ||
| 274 | + | // A float above the tiles covers the tile lines and the lines of windows | |
| 275 | + | // listed before it; its own lines and those of later windows still block | |
| 276 | + | // the overlay. | |
| 277 | + | func TestOverlayFloatOverLines(t *testing.T) { | |
| 278 | + | for _, tc := range []struct { | |
| 279 | + | name string | |
| 280 | + | sep ports.WindowID | |
| 281 | + | below bool | |
| 282 | + | reason string | |
| 283 | + | }{ | |
| 284 | + | {"tile line", 0, false, ""}, | |
| 285 | + | {"earlier window line", 1, false, ""}, | |
| 286 | + | {"own line", 2, false, "border"}, | |
| 287 | + | {"later window line", 3, false, "border"}, | |
| 288 | + | {"tile line over a below float", 0, true, "border"}, | |
| 289 | + | } { | |
| 290 | + | t.Run(tc.name, func(t *testing.T) { | |
| 291 | + | s, c := overlayScene() | |
| 292 | + | s.Windows[1].Floating, s.Windows[1].Below = true, tc.below | |
| 293 | + | s.Windows = append(s.Windows, ports.SceneWindow{ID: 3, Floating: true, Rect: ports.Rect{W: 10, H: 10}}) | |
| 294 | + | s.Separators = []ports.Separator{{Rect: ports.Rect{X: 150, W: 2, H: 100}, Window: tc.sep}} | |
| 295 | + | if _, _, _, reason := overlayCandidate(s, c, false, nil, nil); reason != tc.reason { | |
| 296 | + | t.Fatalf("reason %q, want %q", reason, tc.reason) | |
| 297 | + | } | |
| 298 | + | }) | |
| 299 | + | } | |
| 300 | + | } | |
| 301 | + | ||
| 274 | 302 | // What is drawn elsewhere on screen stays composed on the primary plane: a | |
| 275 | 303 | // bar, a popup, another window's lines or a window that do not touch the | |
| 276 | 304 | // candidate leave it on the overlay. | |
| @@ -271,6 +271,34 @@ func TestOverlayPopupAndBorder(t *testing.T) { | |||||
| 271 | } | 271 | } | ||
| 272 | } | 272 | } | ||
| 273 | 273 | ||||
| 274 | + | // A float above the tiles covers the tile lines and the lines of windows | |||
| 275 | + | // listed before it; its own lines and those of later windows still block | |||
| 276 | + | // the overlay. | |||
| 277 | + | func TestOverlayFloatOverLines(t *testing.T) { | |||
| 278 | + | for _, tc := range []struct { | |||
| 279 | + | name string | |||
| 280 | + | sep ports.WindowID | |||
| 281 | + | below bool | |||
| 282 | + | reason string | |||
| 283 | + | }{ | |||
| 284 | + | {"tile line", 0, false, ""}, | |||
| 285 | + | {"earlier window line", 1, false, ""}, | |||
| 286 | + | {"own line", 2, false, "border"}, | |||
| 287 | + | {"later window line", 3, false, "border"}, | |||
| 288 | + | {"tile line over a below float", 0, true, "border"}, | |||
| 289 | + | } { | |||
| 290 | + | t.Run(tc.name, func(t *testing.T) { | |||
| 291 | + | s, c := overlayScene() | |||
| 292 | + | s.Windows[1].Floating, s.Windows[1].Below = true, tc.below | |||
| 293 | + | s.Windows = append(s.Windows, ports.SceneWindow{ID: 3, Floating: true, Rect: ports.Rect{W: 10, H: 10}}) | |||
| 294 | + | s.Separators = []ports.Separator{{Rect: ports.Rect{X: 150, W: 2, H: 100}, Window: tc.sep}} | |||
| 295 | + | if _, _, _, reason := overlayCandidate(s, c, false, nil, nil); reason != tc.reason { | |||
| 296 | + | t.Fatalf("reason %q, want %q", reason, tc.reason) | |||
| 297 | + | } | |||
| 298 | + | }) | |||
| 299 | + | } | |||
| 300 | + | } | |||
| 301 | + | ||||
| 274 | // What is drawn elsewhere on screen stays composed on the primary plane: a | 302 | // What is drawn elsewhere on screen stays composed on the primary plane: a | ||
| 275 | // bar, a popup, another window's lines or a window that do not touch the | 303 | // bar, a popup, another window's lines or a window that do not touch the | ||
| 276 | // candidate leave it on the overlay. | 304 | // candidate leave it on the overlay. | ||