From 04e7669e4be8cf01a4cfe4fe0aa75d45e6dfed6e Mon Sep 17 00:00:00 2001 From: Dmitry Sergeev Date: Thu, 27 Aug 2026 13:01:38 +0300 Subject: [PATCH] stage borrowed video frames on render thread --- cmd/mxl-player/main.go | 77 ++++++++++++++++++++++++++++++------------ imgui.ini | 2 +- 2 files changed, 57 insertions(+), 22 deletions(-) diff --git a/cmd/mxl-player/main.go b/cmd/mxl-player/main.go index 8f0fa77..279504c 100644 --- a/cmd/mxl-player/main.go +++ b/cmd/mxl-player/main.go @@ -7,6 +7,7 @@ import ( "io" "log" "mxl-player/internal/imgui" + "mxl-player/internal/playback" "mxl-player/internal/renderer" "mxl-player/internal/sdl" "mxl-player/internal/source" @@ -359,7 +360,7 @@ func main() { } // One control channel: grant (empty params) or reconnect (with params). control := make(chan reconnectParams, 1) - staged := make(chan uint64) + videoBridge := playback.NewVideoBridge() reopen := func(params reconnectParams) error { // Close current sources @@ -529,9 +530,7 @@ func main() { continue } - var payload []byte - var grainIdx uint64 - + var videoFrame playback.VideoFrame if syncSrc != nil { vFrame, aFrame, err := syncSrc.NextSync(ctx, audioBatch, 200*time.Millisecond) if err != nil { @@ -551,8 +550,15 @@ func main() { } continue } - payload = vFrame.Payload - grainIdx = vFrame.Index + videoFrame = playback.VideoFrame{ + Index: vFrame.Index, + Width: vFrame.Width, + Height: vFrame.Height, + Stride: vFrame.Stride, + Size: vFrame.Size, + Invalid: vFrame.Invalid, + Payload: vFrame.Payload, + } if aFrame.Samples != nil && audioStream != 0 { sdl.PutAudioStreamData(audioStream, interleaveAudio(aFrame.Samples)) } @@ -575,16 +581,22 @@ func main() { } continue } - payload = f.Payload - grainIdx = f.Index + videoFrame = playback.VideoFrame{ + Index: f.Index, + Width: f.Width, + Height: f.Height, + Stride: f.Stride, + Size: f.Size, + Invalid: f.Invalid, + Payload: f.Payload, + } } - if r != nil { - vk.CopyToMapped(r.StagingMapped(), payload) - } - select { - case staged <- grainIdx: - case <-ctx.Done(): + if err := videoBridge.ConsumeVideo(ctx, videoFrame); err != nil { + if errors.Is(err, context.Canceled) { + return + } + log.Printf("video output: %v", err) return } } @@ -661,15 +673,38 @@ func main() { } var shownIndex uint64 hasFrame := false - select { - case shownIndex = <-staged: + + frameCtx, frameCancel := context.WithTimeout(ctx, 100*time.Millisecond) + pendingFrame, frameErr := videoBridge.Next(frameCtx) + frameCancel() + + if pendingFrame != nil { + var stageErr error + if r != nil { + stageErr = r.StageFrame( + pendingFrame.Frame.Payload, + pendingFrame.Frame.Width, + pendingFrame.Frame.Height, + pendingFrame.Frame.Stride, + ) + } + + // Release the borrowed payload before reacting to a staging error + pendingFrame.Complete(stageErr) + + if stageErr != nil { + panic(stageErr) + } + + shownIndex = pendingFrame.Frame.Index granted = false hasFrame = true - case <-ctx.Done(): - running = false - continue - case <-time.After(100 * time.Millisecond): - // No frame staged. Reset granted so we re-grant on next iteration. + } else if frameErr != nil && + !errors.Is(frameErr, context.DeadlineExceeded) && + !errors.Is(frameErr, context.Canceled) { + panic(frameErr) + } else { + // No frame arrived before the deadline. granted = false } diff --git a/imgui.ini b/imgui.ini index 54a2a14..a963c30 100644 --- a/imgui.ini +++ b/imgui.ini @@ -14,7 +14,7 @@ Size=200,200 Collapsed=0 [Window][Connection] -Pos=425,351 +Pos=1250,701 Size=523,153 Collapsed=0