mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-10-30 15:07:49 -04:00
perf(screenshot): cache overlay background (#3061)
* perf(screenshot): cache overlay background * screenshot: restore exact dirty rect, draw HUD at cache rebuild --------- Co-authored-by: bbedward <[email protected]>
This commit is contained in:
1 parent
ba360213dd
commit
ed8fbf47b0
3 files changed
+163
-87
No files matched your search
@@ -25,10 +25,24 @@ type SelectionState struct {
|
||||
}
|
||||
|
||||
type RenderSlot struct {
|
||||
shm *ShmBuffer
|
||||
pool *client.ShmPool
|
||||
wlBuf *client.Buffer
|
||||
busy bool
|
||||
shm *ShmBuffer
|
||||
pool *client.ShmPool
|
||||
wlBuf *client.Buffer
|
||||
busy bool
|
||||
backgroundInitialized bool
|
||||
backgroundSource *ShmBuffer
|
||||
backgroundDragging bool
|
||||
backgroundCursor bool
|
||||
backgroundPhase selectorPhase
|
||||
dirty *dirtyRect
|
||||
}
|
||||
|
||||
func (s *RenderSlot) cacheValid(src *ShmBuffer, dragging, cursor bool, phase selectorPhase) bool {
|
||||
return s.backgroundInitialized &&
|
||||
s.backgroundSource == src &&
|
||||
s.backgroundDragging == dragging &&
|
||||
s.backgroundCursor == cursor &&
|
||||
s.backgroundPhase == phase
|
||||
}
|
||||
|
||||
type OutputSurface struct {
|
||||
@@ -786,9 +800,24 @@ func (r *RegionSelector) redrawSurface(os *OutputSurface) {
|
||||
switch r.phase {
|
||||
case phaseScroll:
|
||||
r.drawScrollOverlay(os, slot.shm)
|
||||
slot.backgroundInitialized = false
|
||||
slot.backgroundSource = nil
|
||||
slot.dirty = nil
|
||||
default:
|
||||
slot.shm.CopyFrom(srcBuf)
|
||||
r.drawOverlay(os, slot.shm)
|
||||
if !slot.cacheValid(srcBuf, r.selection.dragging, r.showCapturedCursor, r.phase) {
|
||||
slot.shm.CopyFrom(srcBuf)
|
||||
r.dimBackground(slot.shm)
|
||||
r.drawHUD(slot.shm.Data(), slot.shm.Stride, slot.shm.Width, slot.shm.Height, os.screenFormat)
|
||||
slot.backgroundInitialized = true
|
||||
slot.backgroundSource = srcBuf
|
||||
slot.backgroundDragging = r.selection.dragging
|
||||
slot.backgroundCursor = r.showCapturedCursor
|
||||
slot.backgroundPhase = r.phase
|
||||
slot.dirty = nil
|
||||
} else if slot.dirty != nil {
|
||||
r.restoreSourceRect(os, slot.shm, *slot.dirty)
|
||||
}
|
||||
slot.dirty = r.drawOverlay(os, slot.shm)
|
||||
}
|
||||
|
||||
if os.viewport != nil {
|
||||
|
||||
@@ -183,48 +183,12 @@ func (r *RegionSelector) selectionDeviceRect() (*OutputSurface, int, int, int, i
|
||||
}
|
||||
|
||||
os := r.selection.surface
|
||||
srcBuf := r.getSourceBuffer(os)
|
||||
if srcBuf == nil {
|
||||
bounds, ok := r.selectionRenderBounds(os)
|
||||
if !ok {
|
||||
return nil, 0, 0, 0, 0
|
||||
}
|
||||
|
||||
x1, y1 := r.selection.anchorX, r.selection.anchorY
|
||||
x2, y2 := r.selection.currentX, r.selection.currentY
|
||||
|
||||
if x1 > x2 {
|
||||
x1, x2 = x2, x1
|
||||
}
|
||||
if y1 > y2 {
|
||||
y1, y2 = y2, y1
|
||||
}
|
||||
|
||||
scaleX, scaleY := 1.0, 1.0
|
||||
if os.logicalW > 0 {
|
||||
scaleX = float64(srcBuf.Width) / float64(os.logicalW)
|
||||
scaleY = float64(srcBuf.Height) / float64(os.logicalH)
|
||||
}
|
||||
|
||||
bx1 := clamp(int(x1*scaleX), 0, srcBuf.Width)
|
||||
by1 := clamp(int(y1*scaleY), 0, srcBuf.Height)
|
||||
bx2 := clamp(int(x2*scaleX), 0, srcBuf.Width)
|
||||
by2 := clamp(int(y2*scaleY), 0, srcBuf.Height)
|
||||
|
||||
w, h := bx2-bx1+1, by2-by1+1
|
||||
if r.shiftHeld && w != h {
|
||||
if w < h {
|
||||
h = w
|
||||
} else {
|
||||
w = h
|
||||
}
|
||||
}
|
||||
if w < 1 {
|
||||
w = 1
|
||||
}
|
||||
if h < 1 {
|
||||
h = 1
|
||||
}
|
||||
|
||||
return os, bx1, by1, w, h
|
||||
return os, bounds.x, bounds.y, bounds.w, bounds.h
|
||||
}
|
||||
|
||||
func (r *RegionSelector) finishSelection() {
|
||||
|
||||
@@ -51,16 +51,42 @@ var DefaultOverlayStyle = OverlayStyle{
|
||||
AccentR: 100, AccentG: 180, AccentB: 255,
|
||||
}
|
||||
|
||||
func (r *RegionSelector) drawOverlay(os *OutputSurface, renderBuf *ShmBuffer) {
|
||||
data := renderBuf.Data()
|
||||
stride := renderBuf.Stride
|
||||
w, h := renderBuf.Width, renderBuf.Height
|
||||
format := os.screenFormat
|
||||
type selectionRenderBounds struct {
|
||||
x int
|
||||
y int
|
||||
w int
|
||||
h int
|
||||
}
|
||||
|
||||
// dim, forcing alpha: the X-format source's padding byte is undefined
|
||||
for y := 0; y < h; y++ {
|
||||
type dirtyRect struct {
|
||||
x1 int
|
||||
y1 int
|
||||
x2 int
|
||||
y2 int
|
||||
}
|
||||
|
||||
func (d dirtyRect) union(o dirtyRect) dirtyRect {
|
||||
return dirtyRect{
|
||||
x1: min(d.x1, o.x1),
|
||||
y1: min(d.y1, o.y1),
|
||||
x2: max(d.x2, o.x2),
|
||||
y2: max(d.y2, o.y2),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *RegionSelector) dimBackground(renderBuf *ShmBuffer) {
|
||||
data := renderBuf.Data()
|
||||
r.dimRegion(data, renderBuf.Stride, renderBuf.Width, renderBuf.Height, 0, 0, renderBuf.Width, renderBuf.Height)
|
||||
}
|
||||
|
||||
func (r *RegionSelector) dimRegion(data []byte, stride, bufW, bufH, x1, y1, x2, y2 int) {
|
||||
x1 = clamp(x1, 0, bufW)
|
||||
y1 = clamp(y1, 0, bufH)
|
||||
x2 = clamp(x2, 0, bufW)
|
||||
y2 = clamp(y2, 0, bufH)
|
||||
for y := y1; y < y2; y++ {
|
||||
off := y * stride
|
||||
for x := 0; x < w; x++ {
|
||||
for x := x1; x < x2; x++ {
|
||||
i := off + x*4
|
||||
if i+3 >= len(data) {
|
||||
continue
|
||||
@@ -71,38 +97,93 @@ func (r *RegionSelector) drawOverlay(os *OutputSurface, renderBuf *ShmBuffer) {
|
||||
data[i+3] = 255
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
r.drawHUD(data, stride, w, h, format)
|
||||
|
||||
func (r *RegionSelector) selectionRenderBounds(os *OutputSurface) (selectionRenderBounds, bool) {
|
||||
if !r.selection.hasSelection || r.selection.surface != os {
|
||||
return selectionRenderBounds{}, false
|
||||
}
|
||||
|
||||
srcBuf := r.getSourceBuffer(os)
|
||||
if srcBuf == nil {
|
||||
return selectionRenderBounds{}, false
|
||||
}
|
||||
|
||||
scaleX, scaleY := 1.0, 1.0
|
||||
if os.logicalW > 0 && os.logicalH > 0 {
|
||||
scaleX = float64(srcBuf.Width) / float64(os.logicalW)
|
||||
scaleY = float64(srcBuf.Height) / float64(os.logicalH)
|
||||
}
|
||||
x1 := int(r.selection.anchorX * scaleX)
|
||||
y1 := int(r.selection.anchorY * scaleY)
|
||||
x2 := int(r.selection.currentX * scaleX)
|
||||
y2 := int(r.selection.currentY * scaleY)
|
||||
if x1 > x2 {
|
||||
x1, x2 = x2, x1
|
||||
}
|
||||
if y1 > y2 {
|
||||
y1, y2 = y2, y1
|
||||
}
|
||||
|
||||
x1 = clamp(x1, 0, srcBuf.Width-1)
|
||||
y1 = clamp(y1, 0, srcBuf.Height-1)
|
||||
x2 = clamp(x2, 0, srcBuf.Width-1)
|
||||
y2 = clamp(y2, 0, srcBuf.Height-1)
|
||||
w, h := x2-x1+1, y2-y1+1
|
||||
if r.shiftHeld && w != h {
|
||||
if w < h {
|
||||
h = w
|
||||
} else {
|
||||
w = h
|
||||
}
|
||||
}
|
||||
return selectionRenderBounds{x: x1, y: y1, w: w, h: h}, true
|
||||
}
|
||||
|
||||
func (r *RegionSelector) restoreSourceRect(os *OutputSurface, renderBuf *ShmBuffer, d dirtyRect) {
|
||||
srcBuf := r.getSourceBuffer(os)
|
||||
if srcBuf == nil {
|
||||
return
|
||||
}
|
||||
|
||||
scaleX := float64(w) / float64(os.logicalW)
|
||||
scaleY := float64(h) / float64(os.logicalH)
|
||||
|
||||
bx1 := int(r.selection.anchorX * scaleX)
|
||||
by1 := int(r.selection.anchorY * scaleY)
|
||||
bx2 := int(r.selection.currentX * scaleX)
|
||||
by2 := int(r.selection.currentY * scaleY)
|
||||
|
||||
if bx1 > bx2 {
|
||||
bx1, bx2 = bx2, bx1
|
||||
}
|
||||
if by1 > by2 {
|
||||
by1, by2 = by2, by1
|
||||
maxW := min(srcBuf.Width, renderBuf.Width)
|
||||
maxH := min(srcBuf.Height, renderBuf.Height)
|
||||
x1 := clamp(d.x1, 0, maxW)
|
||||
y1 := clamp(d.y1, 0, maxH)
|
||||
x2 := clamp(d.x2, 0, maxW)
|
||||
y2 := clamp(d.y2, 0, maxH)
|
||||
if x2 <= x1 || y2 <= y1 {
|
||||
return
|
||||
}
|
||||
|
||||
bx1 = clamp(bx1, 0, w-1)
|
||||
by1 = clamp(by1, 0, h-1)
|
||||
bx2 = clamp(bx2, 0, w-1)
|
||||
by2 = clamp(by2, 0, h-1)
|
||||
width := (x2 - x1) * 4
|
||||
for y := y1; y < y2; y++ {
|
||||
srcStart := y*srcBuf.Stride + x1*4
|
||||
dstStart := y*renderBuf.Stride + x1*4
|
||||
if srcStart+width > len(srcBuf.Data()) || dstStart+width > len(renderBuf.Data()) {
|
||||
continue
|
||||
}
|
||||
copy(renderBuf.Data()[dstStart:dstStart+width], srcBuf.Data()[srcStart:srcStart+width])
|
||||
}
|
||||
r.dimRegion(renderBuf.Data(), renderBuf.Stride, renderBuf.Width, renderBuf.Height, x1, y1, x2, y2)
|
||||
}
|
||||
|
||||
func (r *RegionSelector) drawOverlay(os *OutputSurface, renderBuf *ShmBuffer) *dirtyRect {
|
||||
bounds, ok := r.selectionRenderBounds(os)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
data := renderBuf.Data()
|
||||
stride := renderBuf.Stride
|
||||
w, h := renderBuf.Width, renderBuf.Height
|
||||
format := os.screenFormat
|
||||
|
||||
srcBuf := r.getSourceBuffer(os)
|
||||
srcData := srcBuf.Data()
|
||||
for y := by1; y <= by2; y++ {
|
||||
for y := bounds.y; y < bounds.y+bounds.h; y++ {
|
||||
rowOff := y * stride
|
||||
for x := bx1; x <= bx2; x++ {
|
||||
for x := bounds.x; x < bounds.x+bounds.w; x++ {
|
||||
si := y*srcBuf.Stride + x*4
|
||||
di := rowOff + x*4
|
||||
if si+3 >= len(srcData) || di+3 >= len(data) {
|
||||
@@ -115,16 +196,16 @@ func (r *RegionSelector) drawOverlay(os *OutputSurface, renderBuf *ShmBuffer) {
|
||||
}
|
||||
}
|
||||
|
||||
selW, selH := bx2-bx1+1, by2-by1+1
|
||||
if r.shiftHeld && selW != selH {
|
||||
if selW < selH {
|
||||
selH = selW
|
||||
} else {
|
||||
selW = selH
|
||||
}
|
||||
}
|
||||
r.drawBorder(data, stride, w, h, bx1, by1, selW, selH, format)
|
||||
r.drawDimensions(data, stride, w, h, bx1, by1, selW, selH, format)
|
||||
r.drawBorder(data, stride, w, h, bounds.x, bounds.y, bounds.w, bounds.h, format)
|
||||
labelRect := r.drawDimensions(data, stride, w, h, bounds.x, bounds.y, bounds.w, bounds.h, format)
|
||||
|
||||
dirty := dirtyRect{
|
||||
x1: bounds.x - borderThickness,
|
||||
y1: bounds.y - borderThickness,
|
||||
x2: bounds.x + bounds.w + borderThickness,
|
||||
y2: bounds.y + bounds.h + borderThickness,
|
||||
}.union(labelRect)
|
||||
return &dirty
|
||||
}
|
||||
|
||||
func (r *RegionSelector) drawScrollOverlay(os *OutputSurface, renderBuf *ShmBuffer) {
|
||||
@@ -257,9 +338,10 @@ func (r *RegionSelector) drawHUD(data []byte, stride, bufW, bufH int, format uin
|
||||
}
|
||||
}
|
||||
|
||||
const borderThickness = 2
|
||||
|
||||
func (r *RegionSelector) drawBorder(data []byte, stride, bufW, bufH, x, y, w, h int, format uint32) {
|
||||
const thickness = 2
|
||||
for i := 0; i < thickness; i++ {
|
||||
for i := 0; i < borderThickness; i++ {
|
||||
r.drawHLine(data, stride, bufW, bufH, x-i, y-i, w+2*i, format)
|
||||
r.drawHLine(data, stride, bufW, bufH, x-i, y+h+i-1, w+2*i, format)
|
||||
r.drawVLine(data, stride, bufW, bufH, x-i, y-i, h+2*i, format)
|
||||
@@ -302,7 +384,7 @@ func (r *RegionSelector) drawVLine(data []byte, stride, bufW, bufH, x, y, length
|
||||
}
|
||||
}
|
||||
|
||||
func (r *RegionSelector) drawDimensions(data []byte, stride, bufW, bufH, x, y, w, h int, format uint32) {
|
||||
func (r *RegionSelector) drawDimensions(data []byte, stride, bufW, bufH, x, y, w, h int, format uint32) dirtyRect {
|
||||
text := fmt.Sprintf("%dx%d", w, h)
|
||||
|
||||
const charW, charH = 8, 12
|
||||
@@ -319,6 +401,7 @@ func (r *RegionSelector) drawDimensions(data []byte, stride, bufW, bufH, x, y, w
|
||||
|
||||
r.fillRect(data, stride, bufW, bufH, tx-4, ty-2, textW+8, textH+4, 0, 0, 0, 200, format)
|
||||
r.drawText(data, stride, bufW, bufH, tx, ty, text, 255, 255, 255, format)
|
||||
return dirtyRect{x1: tx - 4, y1: ty - 2, x2: tx + textW + 4, y2: ty + textH + 2}
|
||||
}
|
||||
|
||||
func (r *RegionSelector) fillRect(data []byte, stride, bufW, bufH, x, y, w, h int, cr, cg, cb, ca uint8, format uint32) {
|
||||
|
||||
Reference in new issue
Block a user