v0.1.0 d9218d6

damage.go RawHistoryFolder

1package vulkan
2
3import (
4	"image"
5	"math"
6
7	"github.com/bnema/neferwl/internal/ports"
8)
9
10// Damage: an output target keeps what it holds (scene Seq, each window's
11// content Seq). When the scene is the same, only the region changed since
12// then is redrawn: every quad is clipped to it on the CPU and the pass
13// keeps the rest of the image. A changed scene, a target never drawn or
14// a history too short redraws everything.
15
16// damageRegion collects the region of a target to redraw this frame and
17// what the target will hold after it: the windows drawn, their content
18// Seq and rect.
19type damageRegion struct {
20	held  *target // nil: redraw everything
21	area  image.Rectangle
22	seen  map[ports.WindowID]bool
23	bound image.Rectangle
24	drawn map[ports.WindowID]heldWindow
25}
26
27// heldWindow is a window a target holds: its content Seq and rect.
28type heldWindow struct {
29	seq  uint64
30	rect image.Rectangle
31}
32
33func newDamage(tg *target, s ports.Scene, bounds image.Rectangle) *damageRegion {
34	d := &damageRegion{seen: map[ports.WindowID]bool{}, bound: bounds, drawn: map[ports.WindowID]heldWindow{}}
35	// Seq 0 is a scene core did not number (setup, tests): redraw all.
36	if tg.valid && s.Seq != 0 && tg.sceneSeq == s.Seq {
37		d.held = tg
38	}
39	return d
40}
41
42// all reports whether the whole target is redrawn.
43func (d *damageRegion) all() bool { return d.held == nil }
44
45func (d *damageRegion) add(r image.Rectangle) {
46	if d.held != nil {
47		d.area = d.area.Union(r.Intersect(d.bound))
48	}
49}
50
51// window records a window drawn in rect (physical): if its content changed
52// and no finer damage comes (content), the whole rect is redrawn.
53func (d *damageRegion) window(id ports.WindowID, c ports.SurfaceContent, rect image.Rectangle) {
54	if d.seen[id] {
55		return
56	}
57	d.seen[id] = true
58	d.drawn[id] = heldWindow{seq: c.Seq, rect: rect}
59	if d.held == nil {
60		return
61	}
62	hw, ok := d.held.windows[id]
63	held := hw.seq
64	if ok && held == c.Seq {
65		return
66	}
67	if !ok || c.Empty() || len(c.Children) > 0 {
68		d.add(rect)
69		return
70	}
71	rects, known := c.DamageSince(held)
72	if !known {
73		d.add(rect)
74		return
75	}
76	// Mapped by content, called next: nothing more here.
77	_ = rects
78}
79
80// content maps a root surface's buffer damage onto full (where its w×h
81// buffer is drawn), clipped to dst. Scaled buffers filter linearly: one
82// more pixel around each rect.
83func (d *damageRegion) content(id ports.WindowID, c *ports.SurfaceContent, full, dst image.Rectangle) {
84	if d.held == nil {
85		return
86	}
87	hw, ok := d.held.windows[id]
88	held := hw.seq
89	if !ok || held == c.Seq || len(c.Children) > 0 {
90		return
91	}
92	rects, known := c.DamageSince(held)
93	if !known || c.Width <= 0 || c.Height <= 0 {
94		return
95	}
96	// Linear filtering spreads a texel over half a buffer pixel around
97	// it: scale/2 target pixels, plus one for rounding.
98	pad := 0
99	if full.Dx() != c.Width || full.Dy() != c.Height {
100		s := max(float64(full.Dx())/float64(c.Width), float64(full.Dy())/float64(c.Height))
101		pad = int(math.Ceil(s/2)) + 1
102	}
103	for _, r := range rects {
104		x0 := full.Min.X + r.X*full.Dx()/c.Width - pad
105		y0 := full.Min.Y + r.Y*full.Dy()/c.Height - pad
106		x1 := full.Min.X + ((r.X+r.W)*full.Dx()+c.Width-1)/c.Width + pad
107		y1 := full.Min.Y + ((r.Y+r.H)*full.Dy()+c.Height-1)/c.Height + pad
108		d.add(image.Rect(x0, y0, x1, y1).Intersect(dst))
109	}
110}
111
112// finish adds the rects of windows the target holds that this frame does
113// not draw (e.g. a window that moved to an overlay plane and back).
114func (d *damageRegion) finish() {
115	if d.held == nil {
116		return
117	}
118	for id, hw := range d.held.windows {
119		if _, ok := d.drawn[id]; !ok {
120			d.add(hw.rect)
121		}
122	}
123}
124
125// clip limits the draws to the region; draws outside it are dropped.
126func (d *damageRegion) clip(ds []draw) []draw {
127	if d.held == nil {
128		return ds
129	}
130	out := ds[:0]
131	for _, dr := range ds {
132		r := image.Rect(int(dr.pc.rect[0]), int(dr.pc.rect[1]), int(dr.pc.rect[2]), int(dr.pc.rect[3])).Intersect(d.area)
133		if r.Empty() {
134			continue
135		}
136		dr.pc.rect = [4]int32{int32(r.Min.X), int32(r.Min.Y), int32(r.Max.X), int32(r.Max.Y)}
137		out = append(out, dr)
138	}
139	return out
140}
141
142// hold records what the target holds after this frame: only the windows
143// it drew.
144func (tg *target) hold(s ports.Scene, d *damageRegion) {
145	tg.valid, tg.sceneSeq, tg.windows = true, s.Seq, d.drawn
146}

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