diff --git a/render/root.go b/render/root.go index 423c58ec51..7f1ccb017f 100644 --- a/render/root.go +++ b/render/root.go @@ -3,6 +3,7 @@ package render import ( "image" "image/color" + "runtime" "sync" "github.com/tidbyt/gg" @@ -38,18 +39,51 @@ type Root struct { Child Widget `starlark:"child,required"` Delay int32 `starlark:"delay"` MaxAge int32 `starlark:"max_age"` + + maxParallelFrames int +} + +type RootPaintOption func(*Root) + +// WithMaxParallelFrames sets the maximum number of frames that will +// be painted in parallel. +// +// By default, only `runtime.NumCPU()` frames are painted in parallel. +// Higher parallelism consumes more memory, and doesn't usually make +// sense since painting is CPU-bouond. +func WithMaxParallelFrames(max int) RootPaintOption { + return func(r *Root) { + r.maxParallelFrames = max + } } // Paint renders the child widget onto the frame. It doesn't do // any resizing or alignment. -func (r Root) Paint(solidBackground bool) []image.Image { +func (r Root) Paint(solidBackground bool, opts ...RootPaintOption) []image.Image { + for _, opt := range opts { + opt(&r) + } + numFrames := r.Child.FrameCount() frames := make([]image.Image, numFrames) + parallelism := r.maxParallelFrames + if parallelism <= 0 { + parallelism = runtime.NumCPU() + } + var wg sync.WaitGroup + sem := make(chan bool, parallelism) for i := 0; i < numFrames; i++ { wg.Add(1) + sem <- true + go func(i int) { + defer func() { + <-sem + wg.Done() + }() + dc := gg.NewContext(DefaultFrameWidth, DefaultFrameHeight) if solidBackground { dc.SetColor(color.Black) @@ -60,7 +94,6 @@ func (r Root) Paint(solidBackground bool) []image.Image { r.Child.Paint(dc, image.Rect(0, 0, DefaultFrameWidth, DefaultFrameHeight), i) dc.Pop() frames[i] = dc.Image() - wg.Done() }(i) }