2

I am looking for a way to wait for a CIContext render to complete without blocking. For simplicity, here is an example where I render one CVPixelBufferRef to another CVPixelBufferRef using CIImage and CIContext. Obviously my actual pipeline would do more, but this is the simplest case.

CIContext *context = [CIContext new];
// sourceBuffer provided from something like a camera
CIImage *image = [CIImage imageWithCVPixelBuffer:sourceBuffer];

CVPixelBufferRef output;
// pool was allocated elsewhere
CVPixelBufferPoolCreatePixelBuffer(kCFAllocatorDefault, bufferPool, &output)
[context render:image toCVPixelBuffer:output];

The code as it is blocks until the render is complete, because [CIContext render:toCVPixelBuffer:] is blocking.

You can start the render task in a non-blocking way using [CIContext startTaskToRender:toDestination:error:], like so

CIContext *context = [CIContext new];
// sourceBuffer provided from something like a camera
CIImage *image = [CIImage imageWithCVPixelBuffer:sourceBuffer];

CVPixelBufferRef output;
// pool was allocated elsewhere
CVPixelBufferPoolCreatePixelBuffer(kCFAllocatorDefault, bufferPool, &output)

CIRenderDestination *destination = [[CIRenderDestination alloc] initWithPixelBuffer:output];
[context startTaskToRender:image toDestination:destination error:nil];

This is halfway there, the call doesn't block, but I have no idea when the render task is finished. That API returns a CIRenderTask, but that only has a single instance method on it, which is of course waitUntilCompletedAndReturnError, which will block until the task is finished.

To be honest, besides providing a medium for an error callback I don't really understand the point of this API. Unless your pipeline doesn't need to know when the task is finished, you still have to block to get your results.

So, my question: Is it possible to get the results of a CIContext render without blocking?

bclymer
  • 6,679
  • 2
  • 27
  • 36

1 Answers1

2

The first render call should actually not really be a blocking call. As mentioned in this answer,

"Core Image defers the rendering until the client requests the access to the frame buffer, i.e. CVPixelBufferLockBaseAddress".

However, you can also use the CIRenderDestiantion API and make it async yourself like this:

CIRenderTask *task = [context startTaskToRender:image toDestination:destination error:nil];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
    [task waitUntilCompletedAndReturnError: NULL];
    // do async stuff here, e.g. call a callback block
});
Frank Rupprecht
  • 9,191
  • 31
  • 56
  • That's very interesting. When profiling my app, the render line specifically accounts for almost 80% of my total CPU usage. It seems like if it just deferred until the next time I needed to lock the frame buffer it would return instantly, and the CPU tax would be in the next pipeline stage (video encoder). – bclymer Feb 24 '21 at 14:57