I pull live images from a camera in a tight loop and use them to update a live WPF image on the display. In memory profiling I saw that my simple approach was hammering the large-object pool hard.
while (IsLive)
{
var size = camera.GetParamInt(PRM.IMAGE_PAYLOAD_SIZE); // Determine size needed
var data = new byte[size]; // Getbuffer
camera.GetImage(data); // Get image.
ImageReceivedEvent?.Invoke(this, data); // Tell clients of new image
}
So I used ArrayPool.Shared
minimize the GC hit like this:
while (IsLive)
{
var size = camera.GetParamInt(PRM.IMAGE_PAYLOAD_SIZE); // Determine size needed
var data = ArrayPool<byte>.Shared.Rent(size); // Rent buffer
camera.GetImage(data); // Get image.
ImageReceivedEvent?.Invoke(this, data); // Tell clients of new image
ArrayPool<byte>.Shared.Return(data); // Done with buffer
}
But this approach requires that clients can only use the image data for the immediate duration of the ImageReceived
event. Because I need to return the buffer at the end of my loop.
Now my app has new requirements to do one or several post-processing steps which read these images. One step might search the image data for some recognizable feature. Another might check the lens focus. Another might try to extract a QR code.
These steps do not happen every frame but often enough. They must be done asynchronously and sometimes simultaneously when there is more than one step.
So I can no longer just call ArrayPool.Return
in the loop because I don’t know when clients are going to be done with my big buffer of image data. I don’t even know the order in which they’ll use it. Yet I want to avoid forcing clients to make copies of these big images as that will just hit the large-object heap even more.
What I want, basically is to be able to Rent
the buffer, hand it off to clients and be sure that Return
be called when the last client is done with it. It’s almost as if I need the C# equivalent of C++’s std::shared_ptr
. But that is not an approach that seems do-able (or desirable) in a garbage-collected language.
Is there an approach I can take to achieve this, or am I just stuck with operator new[] and large-object-heap thrashing?