rpi-rgb-led-matrix icon indicating copy to clipboard operation
rpi-rgb-led-matrix copied to clipboard

Add RGBMatrix::DeleteFrameCanvas(FrameCanvas*)

Open SolidHal opened this issue 2 years ago • 5 comments

And associated python bindings

Enables the user to direct the RGBMatrix to free FrameCanvas objects to avoid wasting memory

SolidHal avatar Aug 21 '22 16:08 SolidHal

Is there a particular use-case you encountered where you need that ?

Having a DeleteFrameCanvas method encourages programming patterns to use canvases in a way that goes beyond double or triple buffering off-screen canvases are meant for, so I would need some convincing.

hzeller avatar Aug 21 '22 17:08 hzeller

The simplified description of my use case is that I am buffering multiple gifs offscreen in an async manner. The "producer" adds some informational overlays (time, weather, etc) and creates a FrameCanvas for each frame in the gif and throws the set of canvases on a queue. This keeps the "consumer" function fast since it just needs to pull a set of canvases (which correspond to a gif) from the queue and call SwapOnVsync on each canvas in the set. Since each gif is different length, reusing canvases would complicate things further, which means over time the memory consumption goes through the roof. The simple solution is to not reuse canvases, and instead allocate/delete them as needed.

SolidHal avatar Aug 21 '22 18:08 SolidHal

Did you consider the content streamer ? It is meant for this use-case. The led-image-viewer uses that for that very purpose: pre-processing GIFs and other animations.

This format can be stored in memory (what the image viewer does) or even on disk: the video-viewer has a mode to output to a stream on disk so that it can be fast replayed later without the video decoding overhead.

The reason why creating and deleting frame-canvases is problematic is that they are exceeding the cache size of the Pi, so if you have many of them, cycling through them means they come in 'cold' from the (rather slow) DRAM, resulting in inconsistent frame-rates up to visible flicker.

If we only have a few FrameCanvas'es, they can stay in Cache, so the main loop that has to read in a high rate can do so without interruptions. It is cheaper to copy data into an existing FrameCanvas using content streamer than having a FrameCanvas coming in cold from DRAM.

hzeller avatar Aug 21 '22 19:08 hzeller

Thanks for the pointers, I hadn't considered the content-stream. Took at look at the content streamer, and led-image-viewer.

I think I could implement something like that on the python side that would work for my use case. The complication being that I also need to be able to interrupt the current stream of gif frames to display frames from other producers (ex: right now I have it showing album art when music start playing) but I can think of a few ways to do that.

It is cheaper to copy data into an existing FrameCanvas using content streamer than having a FrameCanvas coming in cold from DRAM.

This makes some sense, since we are just copying the frame data, and none of the FrameCanvas objects overhead from DRAM -> cache, but its surprising to me that the objects overhead is so large that it causes framerate issues.

I'll expose Serialize & Deserialize on the python bindings and go from there.

SolidHal avatar Aug 22 '22 18:08 SolidHal

I opened https://github.com/hzeller/rpi-rgb-led-matrix/pull/1457 with the Serialize/Deserialize bindings in my testing, I don't observe any performance gains from using them over creating/deleting canvases directly.

I would guess that because of all of the overhead from python, and the additional async tasks I have running, the canvas objects we really care about don't hang around in the rpis cache long enough anyway :/

SolidHal avatar Aug 22 '22 22:08 SolidHal