Files
kitty-mirror/kitty/borders.py
Kovid Goyal d52f2e7981 Rewrite rendering pipeline
This was needed to fix various corner cases when doing blending of colors
in linear space. The new architecture has the same performance as the
old in the common case of opaque rendering with no UI layers or images.

In the case of only positive z-index images there is a performance
decrease as the OS Window is now rendered to a offscreen texture and
then blitted to screen. However, in the future when we move to Vulkan or
I can figure out how to get Wayland to accept buffers with colors in
linear space, this performance penalty can be removed. The performance
penalty was not significant on my system but this is highly GPU
dependent. Modern GPUs are supposedly optimised for rendering to
offscreen buffers, so we will see. The awrit project might be a good
test case.

Now either we have 1-shot rendering for the case of opaque with only ext
or all the various pieces are rendered in successive draw calls into an
offscreen buffer that is blitted to the output buffer after all drawing
is done.

Fixes #8869
2025-08-11 00:47:02 +05:30

114 lines
3.8 KiB
Python

#!/usr/bin/env python
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
from collections.abc import Iterable
from enum import IntFlag
from functools import partial
from typing import NamedTuple
from .fast_data_types import BORDERS_PROGRAM, get_options, init_borders_program, set_borders_rects
from .shaders import program_for
from .typing_compat import LayoutType
from .utils import color_as_int
from .window_list import WindowGroup, WindowList
class BorderColor(IntFlag):
# These are indices into the array of colors in the border vertex shader
default_bg, active, inactive, window_bg, bell, tab_bar_bg, tab_bar_margin_color, tab_bar_left_edge_color, tab_bar_right_edge_color = range(9)
class Border(NamedTuple):
left: int
top: int
right: int
bottom: int
color: BorderColor
def vertical_edge(rects: list[Border], color: BorderColor, width: int, top: int, bottom: int, left: int) -> None:
if width > 0:
rects.append(Border(left, top, left + width, bottom, color))
def horizontal_edge(rects: list[Border], color: BorderColor, height: int, left: int, right: int, top: int) -> None:
if height > 0:
rects.append(Border(left, top, right, top + height, color))
def add_borders(rects: list[Border], color: BorderColor, wg: WindowGroup) -> None:
geometry = wg.geometry
if geometry is None:
return
pl, pt = wg.effective_padding('left'), wg.effective_padding('top')
pr, pb = wg.effective_padding('right'), wg.effective_padding('bottom')
left = geometry.left - pl
top = geometry.top - pt
lr = geometry.right
right = lr + pr
bt = geometry.bottom
bottom = bt + pb
h = partial(horizontal_edge, rects, color)
v = partial(vertical_edge, rects, color)
width = wg.effective_border()
bt = bottom
lr = right
left -= width
top -= width
right += width
bottom += width
pl = pr = pb = pt = width
h(pt, left, right, top)
h(pb, left, right, bt)
v(pl, top, bottom, left)
v(pr, top, bottom, lr)
def load_borders_program() -> None:
program_for('border').compile(BORDERS_PROGRAM)
init_borders_program()
class Borders:
def __init__(self, os_window_id: int, tab_id: int):
self.os_window_id = os_window_id
self.tab_id = tab_id
def __call__(
self,
all_windows: WindowList,
current_layout: LayoutType,
tab_bar_rects: Iterable[Border],
draw_window_borders: bool = True,
) -> None:
opts = get_options()
draw_active_borders = opts.active_border_color is not None
draw_minimal_borders = opts.draw_minimal_borders and max(opts.window_margin_width) < 1
rects: list[Border] = []
for br in current_layout.blank_rects:
rects.append(Border(*br, BorderColor.default_bg))
rects.extend(tab_bar_rects)
bw = 0
groups = tuple(all_windows.iter_all_layoutable_groups(only_visible=True))
if groups:
bw = groups[0].effective_border()
draw_borders = bw > 0 and draw_window_borders
active_group = all_windows.active_group
for i, wg in enumerate(groups):
window_bg = color_as_int(wg.default_bg)
window_bg = (window_bg << 8) | BorderColor.window_bg
if draw_borders and not draw_minimal_borders:
# Draw the border rectangles
if wg is active_group and draw_active_borders:
color = BorderColor.active
else:
color = BorderColor.bell if wg.needs_attention else BorderColor.inactive
add_borders(rects, color, wg)
if draw_minimal_borders:
for border_line in current_layout.get_minimal_borders(all_windows):
rects.append(Border(*border_line.edges, border_line.color))
set_borders_rects(self.os_window_id, self.tab_id, rects)