GPUImage滤镜链解析

2019-07-28  本文已影响0人  mkvege
理解本篇内容需掌握一点OpenGL基础:

1.glProgram的编译,链接,使用。
2.glFramebuffer与glTexture的使用。

简述

GPUImage本质上是一个OpenGL管理框架,其不仅包含大量的滤镜代码,也包含一套使用方便的链式结构,本篇将主要分析滤镜链的原理。

如果将这个链式结构比喻为一个工厂中的流水线,那么这个流水线的结构就会如下所示:
1.总资源管理者

GPUImageContext

2.搭载原料的容器

GPUImageFramebuffer

3.原料提供者

GPUImagePicture,GPUImageMovie...

4.原料加工者

GPUImageFilter

5.成品展示者

GPUImageView

6.原料容器数量的控制者

GPUImageFramebufferCache

下面将依次介绍这些部分在滤镜链中的详细作用

GPUImageContext

作为总资源管理者,以单例方式应用,主要负责:
1.线程管理
2.context
3.framebuffer缓存管理
4.gl信息查询

GPUImageFramebuffer

作为原料的容器,也是滤镜链传递的核心。但之所以称之为容器,是因为每一级所需要的真正材料不是GPUImageFramebuffer,而是里面的glTexture

GPUImageFramebuffer包含两种类型

1.onlyTexture:只生成了glTexture而没有生成glFramebuffer,多用于第一级输入端,例如GPUImagePicture。

2.texture和framebuffer:生成了glTexture,也生成了glFramebuffer,并使用 glFramebufferTexture2D 将二者绑定,用于滤镜的渲染中,如GPUImageFilter。

GPUImageFramebuffer传递的核心原理:
当前滤镜在渲染时,先绑定到自己的glFramebuffer,然后获取上一级滤镜GPUImageFramebuffer中的glTexture作为输入纹理,通过每个滤镜特有的glProgram,对当前滤镜的输出glTexture进行渲染

GPUImagePicture,GPUImageMovie...

作为原料提供者,处于滤镜链的头部,通过继承GPUImageOutput来实现GPUImageFramebuffer的输出。本质上是将数据转化为OpenGL识别的纹理。

GPUImageFilter

作为原料加工者,处于滤镜链的中段,既继承了GPUImageOutput,又实现了GPUImageInput协议。(无论是Output还是Input都是对于GPUImageFramebuffer而言)

这样便形成了以GPUImageFramebuffer为传递者的链式结构。

而且同一个滤镜可以存在于多条滤镜链中。

以下代码展示的是一个简单的滤镜链,通过效果可以看出每一个filter处理纹理图片的过程:

[picture addTarget:filter1];    

[filter1 addTarget:filter2];
[filter1 addTarget:imageView1];

[filter2 addTarget:imageView2];
[filter2 addTarget:filter3];
    
[filter3 addTarget:imageView3];
[picture processImage]; 
🐷
GPUImageView

作为成品展示者,处于滤镜链的最末端,只接受纹理,不再进行输出。
实现GPUImageInput协议,只接受上级滤镜传来的纹理,而用于展示的glFramebuffer不再使用GPUImageFramebuffer,而直接由自己生成绑定于glRenderbuffer的glFramebuffer。

GPUImageFramebufferCache

作为原料容器的管理者,GPUImageFramebufferCache使GPUImageFramebuffer得以复用,目的是提升性能,就像没必要给每道菜都使用新的盘子一样。

缓存步骤:
1.区分缓存类型
保证同一类型的缓存可以复用,具体就是根据不同的GPUImageFramebuffer生成不同的key,主要以纹理配置,纹理尺寸,以及是否只包含texture作为分类标准,细节如下

- (NSString *)hashForSize:(CGSize)size textureOptions:(GPUTextureOptions)textureOptions onlyTexture:(BOOL)onlyTexture;
{
    if (onlyTexture)
    {
        return [NSString stringWithFormat:@"%.1fx%.1f-%d:%d:%d:%d:%d:%d:%d-NOFB", size.width, size.height, textureOptions.minFilter, textureOptions.magFilter, textureOptions.wrapS, textureOptions.wrapT, textureOptions.internalFormat, textureOptions.format, textureOptions.type];
    }
    else
    {
        return [NSString stringWithFormat:@"%.1fx%.1f-%d:%d:%d:%d:%d:%d:%d", size.width, size.height, textureOptions.minFilter, textureOptions.magFilter, textureOptions.wrapS, textureOptions.wrapT, textureOptions.internalFormat, textureOptions.format, textureOptions.type];
    }
}

2.引用计数
GPUImageFramebufferCache使用缓存的方式与普通的缓存不太一样。

普通缓存
1.按key找缓存对象。
2.有则返回匹配对象。
3.没有则创建新的对象且存入缓存,并返回新对象。

GPUImageFramebufferCache
1.按key找缓存对象。
2.如果有,则从缓存中移除当前匹配对象,对象引用计数+1。
3.如果没有,则新建对象,这时并不会把新对象存入缓存,只是对新对象引用计数+1。

这种缓存有点类似于ARC机制,将要渲染时,则对当前GPUImageFramebuffer调用lock方法,使其引用计数+1,渲染完成或滤镜销毁等释放操作后,调用unlock方法,使其引用计数-1,当引用计数为0时,才将对象放入缓存当中。

使用这种缓存的原因

当一个GPUImageFramebuffer引用计数大于0时,不会再将它分配给其他滤镜进行渲染,避免glFramebuffer数据被篡改。

而当一个GPUImageFramebuffer引用计数为0时,才会继续将它分配给其他滤镜。

由此可以得知,当从缓存中请求GPUImageFramebuffer对象时,同类型的GPUImageFramebuffer对象,可能因为引用计数大于0,而不在缓存中。这时会再次生成这个类型的缓存对象。当这几个同类型的对象引用计数都归0时,便都会存入缓存。

所以GPUImageFramebufferCache在请求缓存时,会先使用while循环来清空多余同类缓存,细节如下:

// Something found, pull the old framebuffer and decrement the count
            NSInteger currentTextureID = (numberOfMatchingTextures - 1);
            while ((framebufferFromCache == nil) && (currentTextureID >= 0))
            {
                NSString *textureHash = [NSString stringWithFormat:@"%@-%ld", lookupHash, (long)currentTextureID];
                framebufferFromCache = [framebufferCache objectForKey:textureHash];
                // Test the values in the cache first, to see if they got invalidated behind our back
                if (framebufferFromCache != nil)
                {
                    // Withdraw this from the cache while it's in use
                    [framebufferCache removeObjectForKey:textureHash];
                }
                currentTextureID--;
            }
上一篇下一篇

猜你喜欢

热点阅读