Search code examples
iosobjective-cgpuimage

Update one filter in GPUImageFilterGroup without redraw all filters


I use GPUImageFilterGroup to apply some filters to a image. All filters stable (all parameters is constant), but last filter is variable (some parameter is changed).

I need redraw image after last filter was changed.

Now I call processImage on source GPUImagePicture, but this invocation redraw all filters and speed is too slow.

How can I redraw only last filter in the group?

I think, I should save copy of a frame buffer before last filter will draw, and when I've change some parameter in the last filter, I should use saved frame buffer to redraw last filter. But I can't find how I can save copy of the frame buffer.


Solution

  • I've solved this, by subclassing GPUImageFilter and GPUImageFilterGroup. In GPUImageFilter I've overloaded method

    - (void)newFrameReadyAtTime:(CMTime)frameTime atIndex:(NSInteger)textureIndex
    <...>
    
    [self renderToTextureWithVertices:imageVertices textureCoordinates:[[self class] textureCoordinatesForRotation:inputRotation]];
    _bufferCallback(self);
    [self informTargetsAboutNewFrameAtTime:frameTime];
    <...>
    

    In GPUImageFilterGroup I've overloaded methods:

    - (void)addFilter:(GPUImageOutput<GPUImageInput> *)newFilter
    {
        NSParameterAssert([newFilter isKindOfClass:    [FAEShiftFilterWithBackOutputBuffer class]]);
        if ([newFilter isKindOfClass:[FAEShiftFilterWithBackOutputBuffer class]])
        {
            __weak typeof(self) selfWeak = self;
            [(FAEShiftFilterWithBackOutputBuffer*)newFilter setOutputBufferCallback:^(FAEShiftFilterWithBackOutputBuffer *sender) {
            __strong typeof(selfWeak) selfStrong = selfWeak;
            if (selfStrong)
            {
                if (!selfStrong.lastFramebuffer)
                {
                    if ([selfStrong isPreLastFilter:sender])
                    {
                        selfStrong.lastFramebuffer = [sender framebufferForOutput];
                        [selfStrong.lastFramebuffer lock];
                    }
                }
            }
        }];
    }
    [super addFilter:newFilter];
    }
    

    This method stores outputFrameBuffer from preLast filter. And method:

    - (void)newFrameReadyAtTime:(CMTime)frameTime atIndex:(NSInteger)textureIndex
    {
        if (self.filterCount > 1)
       {
        if (self.lastFramebuffer)
        {
            GPUImageFilter* lastFilter = (GPUImageFilter*)self.terminalFilter;
            [lastFilter setInputFramebuffer:self.lastFramebuffer atIndex:0];
            [lastFilter newFrameReadyAtTime:frameTime atIndex:textureIndex];
        }
        else
        {
            [super newFrameReadyAtTime:frameTime atIndex:textureIndex];
        }
    }
    else
    {
        [super newFrameReadyAtTime:frameTime atIndex:textureIndex];
    }
    }
    

    Also I reset saved framebuffer in dealloc and forceProcessingAtSize and forceProcessingAtSizeRespectingAspectRatio methods.

    - (void)_clearLastFrameBuffer
    {
     if (_lastFramebuffer)
     {
         [_lastFramebuffer unlock];
         _lastFramebuffer = nil;
     }
    }