美文网首页
GPUImage滤镜链解析

GPUImage滤镜链解析

作者: mkvege | 来源:发表于2019-07-28 17:14 被阅读0次
    理解本篇内容需掌握一点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--;
                }
    

    相关文章

      网友评论

          本文标题:GPUImage滤镜链解析

          本文链接:https://www.haomeiwen.com/subject/tvcllctx.html