optimize streaming

This commit is contained in:
KingKDot
2026-04-08 14:14:09 -05:00
parent 67d81d1b75
commit b711efe6bd
10 changed files with 113 additions and 9 deletions
+11 -1
View File
@@ -130,6 +130,8 @@ func CaptureAndSend(ctx context.Context, env *rt.Env) error {
quality := jpegQuality()
frame, encodeDur, err := buildFrame(img, display, quality)
PutRGBA(img)
img = nil
if err != nil {
return err
}
@@ -142,6 +144,9 @@ func CaptureAndSend(ctx context.Context, env *rt.Env) error {
if ctx.Err() != nil {
return nil
}
if !AcquireFrameSlot() {
return nil // backpressure: server hasn't acked previous frames
}
sendStart := time.Now()
err = wire.WriteMsg(ctx, env.Conn, frame)
sendDur := time.Since(sendStart)
@@ -263,14 +268,19 @@ func captureAllDisplaysAndSend(ctx context.Context, env *rt.Env) error {
cH = ry
}
}
canvas := image.NewRGBA(image.Rect(0, 0, cW, cH))
canvas := GetRGBA(cW, cH)
for _, sp := range scaled {
dst := image.Rect(sp.offX, sp.offY, sp.offX+sp.img.Rect.Dx(), sp.offY+sp.img.Rect.Dy())
draw.Draw(canvas, dst, sp.img, sp.img.Rect.Min, draw.Src)
}
for _, part := range parts {
PutRGBA(part.img)
}
quality := jpegQuality()
frame, _, err := buildFrame(canvas, 0, quality)
PutRGBA(canvas)
canvas = nil
if err != nil {
return err
}
@@ -0,0 +1,49 @@
package capture
import (
"sync/atomic"
"time"
)
const maxInFlightFrames int64 = 2
var (
inFlightFrames atomic.Int64
frameAckSeen atomic.Bool
lastAckNano atomic.Int64
)
func AcquireFrameSlot() bool {
if !frameAckSeen.Load() {
return true
}
for {
cur := inFlightFrames.Load()
if cur >= maxInFlightFrames {
lastAck := lastAckNano.Load()
if lastAck > 0 && time.Since(time.Unix(0, lastAck)) > time.Second {
inFlightFrames.Store(0)
continue
}
return false
}
if inFlightFrames.CompareAndSwap(cur, cur+1) {
return true
}
}
}
func ReleaseFrameSlot() {
frameAckSeen.Store(true)
lastAckNano.Store(time.Now().UnixNano())
if inFlightFrames.Add(-1) < 0 {
inFlightFrames.Store(0)
}
}
func ResetFrameSlots() {
inFlightFrames.Store(0)
frameAckSeen.Store(false)
lastAckNano.Store(0)
}
@@ -0,0 +1,33 @@
package capture
import (
"image"
"sync"
)
var rgbaPool sync.Pool
func GetRGBA(w, h int) *image.RGBA {
need := w * h * 4
if need <= 0 {
return image.NewRGBA(image.Rect(0, 0, w, h))
}
if v := rgbaPool.Get(); v != nil {
buf := v.([]byte)
if cap(buf) >= need {
return &image.RGBA{
Pix: buf[:need],
Stride: w * 4,
Rect: image.Rect(0, 0, w, h),
}
}
}
return image.NewRGBA(image.Rect(0, 0, w, h))
}
func PutRGBA(img *image.RGBA) {
if img == nil || len(img.Pix) == 0 {
return
}
rgbaPool.Put(img.Pix)
}
@@ -320,7 +320,7 @@ func captureDisplayBitBlt(display int) (*image.RGBA, error) {
img = resizeNearest(img, dstW, dstH)
} else {
// Detach from the reusable DIB buffer to avoid concurrent mutation during encode.
stable := image.NewRGBA(img.Rect)
stable := GetRGBA(img.Rect.Dx(), img.Rect.Dy())
copy(stable.Pix, img.Pix)
img = stable
}
@@ -422,7 +422,7 @@ func logCaptureTimings(bitDur, dibDur, convDur time.Duration) {
}
func resizeNearest(src *image.RGBA, w, h int) *image.RGBA {
dst := image.NewRGBA(image.Rect(0, 0, w, h))
dst := GetRGBA(w, h)
srcW := src.Bounds().Dx()
srcH := src.Bounds().Dy()
if srcW <= 0 || srcH <= 0 || w <= 0 || h <= 0 {
@@ -828,7 +828,7 @@ func cloneRGBA(src *image.RGBA) *image.RGBA {
if src == nil {
return nil
}
dst := image.NewRGBA(src.Rect)
dst := GetRGBA(src.Rect.Dx(), src.Rect.Dy())
copy(dst.Pix, src.Pix)
return dst
}
@@ -1096,7 +1096,7 @@ func createD3DDevice(adapter *idxgiAdapter1) (*d3d11Device, *d3d11DeviceContext,
func convertBGRA(src []byte, pitch, width, height int, rotation uint32) *image.RGBA {
switch rotation {
case dxgiModeRotationRotate90:
dst := image.NewRGBA(image.Rect(0, 0, height, width))
dst := GetRGBA(height, width)
for y := 0; y < height; y++ {
sRow := src[y*pitch : y*pitch+width*4]
for x := 0; x < width; x++ {
@@ -1112,7 +1112,7 @@ func convertBGRA(src []byte, pitch, width, height int, rotation uint32) *image.R
}
return dst
case dxgiModeRotationRotate180:
dst := image.NewRGBA(image.Rect(0, 0, width, height))
dst := GetRGBA(width, height)
for y := 0; y < height; y++ {
sRow := src[y*pitch : y*pitch+width*4]
dy := height - 1 - y
@@ -1129,7 +1129,7 @@ func convertBGRA(src []byte, pitch, width, height int, rotation uint32) *image.R
}
return dst
case dxgiModeRotationRotate270:
dst := image.NewRGBA(image.Rect(0, 0, height, width))
dst := GetRGBA(height, width)
for y := 0; y < height; y++ {
sRow := src[y*pitch : y*pitch+width*4]
for x := 0; x < width; x++ {
@@ -1145,7 +1145,7 @@ func convertBGRA(src []byte, pitch, width, height int, rotation uint32) *image.R
}
return dst
default:
dst := image.NewRGBA(image.Rect(0, 0, width, height))
dst := GetRGBA(width, height)
for y := 0; y < height; y++ {
sRow := src[y*pitch : y*pitch+width*4]
dRow := dst.Pix[y*dst.Stride : y*dst.Stride+width*4]
@@ -1166,7 +1166,7 @@ func convertBGRAScaled(src []byte, pitch, srcW, srcH, dstW, dstH int, rotation u
if rotation != dxgiModeRotationIdentity && rotation != 0 {
return nil
}
dst := image.NewRGBA(image.Rect(0, 0, dstW, dstH))
dst := GetRGBA(dstW, dstH)
xOff := make([]int, dstW)
for x := 0; x < dstW; x++ {
xOff[x] = (x * srcW / dstW) * 4