GPUImage的filter 响应处理链 的理解笔记

GPUImage的filter的textures处理链式结构

  两个最重要的的地方:

  1. 最重要的一个类GPUImageOutput(所有的filter的父类,其他也有继承它的,如GPUImageUIElement,UIKit元素通过CG转gles贴图 等等);
  2. 协议(或者接口)GPUImageInput

  继承GPUImageOutput且遵循GPUImageInput的filter,处理完成后输出又可以作为下一个filter的输入。

 1 @protocol GPUImageInput <NSObject>
 2 - (void)newFrameReadyAtTime:(CMTime)frameTime atIndex:(NSInteger)textureIndex;
 3 - (void)setInputFramebuffer:(GPUImageFramebuffer *)newInputFramebuffer atIndex:(NSInteger)textureIndex;
 4 - (NSInteger)nextAvailableTextureIndex;
 5 - (void)setInputSize:(CGSize)newSize atIndex:(NSInteger)textureIndex;
 6 - (void)setInputRotation:(GPUImageRotationMode)newInputRotation atIndex:(NSInteger)textureIndex;
 7 - (CGSize)maximumOutputSize;
 8 - (void)endProcessing;
 9 - (BOOL)shouldIgnoreUpdatesToThisTarget;
10 - (BOOL)enabled;
11 - (BOOL)wantsMonochromeInput;
12 - (void)setCurrentlyReceivingMonochromeInput:(BOOL)newValue;
13 @end

p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #000000 }
span.s1 { }

GPUImageFramebuffer

  framebuffer的封装类,根据onlyGenerateTexture 判断 只生成纹理 或 framebuffer;摘自 - (void)generateFramebuffer;

    1. 只生成纹理的情况典型:GPUImageUIElement,GPUImageVideoCamera等等
    2. 生成framebuffer,判断是否支持快速上传纹理数据(其实是判断CVOpenGLESTextureCacheCreate是否可用)

        如果支持快速上传纹理,CVPixelBufferCreate生成renderTarget,CVOpenGLESTextureCacheCreateTextureFromImage根据renderTarget(sourceImage)生成renderTexture,最后调用glFramebufferTexture2D将framebufferrenderTexture绑定在一块,framebuffer输出到texture(注:framebuffer也可以绑定到renderBuffer,也常称为colorbuffer,renderbuffer直接显示在CALayer上了;绑定在texture上通常作为中间值);

        如果不支持;先generate texture,再绑定,glTexImage2D上传数据到GPU,最后调用glFramebufferTexture2D将framebuffertexture绑定在一块

  GPUImageFramebuffer中的- (CGImageRef)newCGImageFromFramebufferContents;,用于从framebuffer中取出图像数据生成CGImageRef;

 1     CGDataProviderRef dataProvider = NULL;
 2         if ([GPUImageContext supportsFastTextureUpload])
 3         {
 4 #if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
 5             NSUInteger paddedWidthOfImage = CVPixelBufferGetBytesPerRow(renderTarget) / 4.0;   //字节对齐后的图片占用宽度可能要大
 6             NSUInteger paddedBytesForImage = paddedWidthOfImage * (int)_size.height * 4;
 7
 8             glFinish();    //强制提交前面调用的gl指令到GPU硬件,阻塞调用
 9             CFRetain(renderTarget); //防止出现野指针,在回调中释放 I need to retain the pixel buffer here and release in the data source callback to prevent its bytes from being prematurely deallocated during a photo write operation
10             [self lockForReading];
11             rawImagePixels = (GLubyte *)CVPixelBufferGetBaseAddress(renderTarget);
12             dataProvider = CGDataProviderCreateWithData((__bridge_retained void*)self, rawImagePixels, paddedBytesForImage, dataProviderUnlockCallback);      //全局的framebuffercache强引用当前自身,防止framebuffer在切换时出现问题
13             [[GPUImageContext sharedFramebufferCache] addFramebufferToActiveImageCaptureList:self]; // In case the framebuffer is swapped out on the filter, need to have a strong reference to it somewhere for it to hang on while the image is in existence
14 #else
15 #endif
16         }
17         else
18         {
19             [self activateFramebuffer];
20             rawImagePixels = (GLubyte *)malloc(totalBytesForImage);
21             glReadPixels(0, 0, (int)_size.width, (int)_size.height, GL_RGBA, GL_UNSIGNED_BYTE, rawImagePixels);  //阻塞调用,直接从framebuffer中读取image 原始数据
22             dataProvider = CGDataProviderCreateWithData(NULL, rawImagePixels, totalBytesForImage, dataProviderReleaseCallback);
23             [self unlock]; // Don‘t need to keep this around anymore
24         }

最后CGImageCreate生成图片返回。

GPUImageOutput

  类的说明其实已经很明了,视频采集,拍照等都是以它为基类,同意套路:源(视频,静态图)上传图片帧给OpenGL ES作为textures,这些textures作为下一个filter的输入,形成处理texture的链式结构。

/** GPUImage‘s base source object

 Images or frames of video are uploaded from source objects, which are subclasses of GPUImageOutput. These include:

 - GPUImageVideoCamera (for live video from an iOS camera)
 - GPUImageStillCamera (for taking photos with the camera)
 - GPUImagePicture (for still images)
 - GPUImageMovie (for movies)

 Source objects upload still image frames to OpenGL ES as textures, then hand those textures off to the next objects in the processing chain.
 */

  类中工具函数runSynchronouslyOnContextQueue等,通过dispatch_get_specific防止死锁,注意不要用dispatch_get_current_queue;

  通过对三个典型类的作用解读,分别为 (GPUImagePicture)source->(GPUImageFilter)filter->(GPUImageView)output,形成处理链式结构,当然还有其他的pipeline。

  • GPUImagePicture

  GPUImagePicture只继承GPUImageOutput,专门用作读取输入数据,上传GPU,交个链条下一步GPUImageFilter处理

      • 初始化initWithCGImage:读取CGImageRef数据,判断是否需要CG的辅助来处理图片数据,如需要CG,固定套路(包含解压图片语义)CGBitmapContextCreate-->CGContextDrawImage;不需要的情况,CGImageGetDataProvider-->CGDataProviderCopyData-->CFDataGetBytePtr获取原始数据, 最后通过调用glTexImage2D上传imageData到当前outputFramebuffer 的GPU texture
      • 处理图片processImageWithCompletionHandler:根据addTarget加入的所有target(即链条结构中间的filter),逐个setInputFramebuffer设置当前outputFramebuffer中processed texture为下一步filter的输入;
      • newFrameReadyAtTime 通知各个加入的target处理数据。
  • GPUImageFilter

  GPUImageFilter(实际开发中通常用到它的子类)继承GPUImageOutput,同时遵循GPUImageInput 协议,类的说明如下

/** GPUImage‘s base filter class

 Filters and other subsequent elements in the chain conform to the GPUImageInput protocol,  which lets them take in the supplied or processed texture from the previous link in the chain and do something with it.  Objects one step further down the chain are considered targets,  and processing can be branched by adding multiple targets to a single output or filter.
 */

GPUImageFilter中 setInputFramebuffer (GPUImageInput协议方法)简单地赋值 ;

p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #000000 }
p.p2 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #4f8187 }
span.s1 { }
span.s2 { color: #ba2da2 }
span.s3 { color: #4f8187 }
span.s4 { color: #703daa }
span.s5 { color: #000000 }
span.s6 { color: #31595d }

- (void)setInputFramebuffer:(GPUImageFramebuffer *)newInputFramebuffer atIndex:(NSInteger)textureIndex;

{

firstInputFramebuffer = newInputFramebuffer;

[firstInputFramebuffer lock];

}

然后调用newFrameReadyAtTime;

p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #000000 }
p.p2 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #000000; min-height: 13.0px }
p.p3 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #31595d }
span.s1 { }
span.s2 { color: #ba2da2 }
span.s3 { color: #703daa }
span.s4 { color: #272ad8 }
span.s5 { color: #000000 }
span.s6 { color: #3e1e81 }
span.s7 { color: #4f8187 }

- (void)newFrameReadyAtTime:(CMTime)frameTime atIndex:(NSInteger)textureIndex;
{
    static const GLfloat imageVertices[] = {
        -1.0f, -1.0f,
        1.0f, -1.0f,
        -1.0f,  1.0f,
        1.0f,  1.0f,
    };    //顶点数据,两个三角形组成texture区域    
    [self renderToTextureWithVertices:imageVertices textureCoordinates:[[self class] textureCoordinatesForRotation:inputRotation]];

    [self informTargetsAboutNewFrameAtTime:frameTime];
}

激活该filter中的 filterProgram(已经attach过 顶点shader 和 片元shader),然后绑定输入的texture并渲染。

- (void)renderToTextureWithVertices:(const GLfloat *)vertices textureCoordinates:(const GLfloat *)textureCoordinates;
{
    if (self.preventRendering)
    {
        [firstInputFramebuffer unlock];
        return;
    }

    [GPUImageContext setActiveShaderProgram:filterProgram];
  /**   * 从GPUImageFrameBufferCache中取出可重用的outputFramebuffer   *   **/    outputFramebuffer = [[GPUImageContext sharedFramebufferCache] fetchFramebufferForSize:[self sizeOfFBO] textureOptions:self.outputTextureOptions onlyTexture:NO];
    [outputFramebuffer activateFramebuffer];
    if (usingNextFrameForImageCapture)
    {
        [outputFramebuffer lock];
    }

    [self setUniformsForProgramAtIndex:0];

    glClearColor(backgroundColorRed, backgroundColorGreen, backgroundColorBlue, backgroundColorAlpha);
    glClear(GL_COLOR_BUFFER_BIT);

    glActiveTexture(GL_TEXTURE2);   //选择GL_TEXTURE2
    glBindTexture(GL_TEXTURE_2D, [firstInputFramebuffer texture]); //绑定当前输入的framebuffer中的texture

    glUniform1i(filterInputTextureUniform, 2);
  //分别设置顶点shader中的顶点数据,和将来用于片元shader中的texture坐标数据
    glVertexAttribPointer(filterPositionAttribute, 2, GL_FLOAT, 0, 0, vertices);
    glVertexAttribPointer(filterTextureCoordinateAttribute, 2, GL_FLOAT, 0, 0, textureCoordinates);

    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

    [firstInputFramebuffer unlock];

    if (usingNextFrameForImageCapture)
    {
        dispatch_semaphore_signal(imageCaptureSemaphore);
    }
}

p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #000000 }
span.s1 { }

informTargetsAboutNewFrameAtTime 中轮询两次当前的target,第一次大致还是调用父类的setInputFramebufferForTarget(父类GPUImageOutput),第二次继续newFrameReadyAtTime,又回到了从source添加target的原点。

GPUImageView

  作为最终的输出target只实现了GPUImageInput的协议,只能接受source或者filter传过来的数据,不再作为输出了;

  其中的setInputFramebuffer 和 newFrameReadyAtTime和filter中处理如出一辙,但是加了一个调用;如下,正如开头提到的framebuffer也可以绑定到renderBuffer,也常称为colorbuffer,renderbuffer直接显示在CAEAGLLayer上了;最终通过设置屏幕大小的缓冲区,直接显示在手机屏幕上。

- (void)presentFramebuffer;
{
    glBindRenderbuffer(GL_RENDERBUFFER, displayRenderbuffer);
    [[GPUImageContext sharedImageProcessingContext] presentBufferForDisplay];
}

p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #000000 }
span.s1 { }
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #703daa }
span.s1 { }

其中的displayRenderBuffer通过createDisplayFramebuffer方法创建,都是些模板代码,没什么可记录的。

p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #000000 }
span.s1 { }

小结

  GPUImage的代码结构可谓是链式处理结构的典范,很值得学习;本文只记录了processing chain(source->filter-->filter...->output)的数据流向,很多细节以后再记录。

参考

GPUImage源码:https://github.com/BradLarson/GPUImage

p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #000000 }
span.s1 { }
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #000000 }
span.s1 { }
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #000000 }
span.s1 { }
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #000000 }
span.s1 { }
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #4f8187 }
span.s1 { }
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #4f8187 }
span.s1 { }
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #3e1e81 }
span.s1 { }
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #000000 }
span.s1 { }
span.s2 { color: #ba2da2 }
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #000000 }
span.s1 { }
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #3e1e81 }
span.s1 { }
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #000000 }
span.s1 { }
span.s2 { color: #703daa }
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #000000 }
span.s1 { }
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #000000 }
span.s1 { }
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #3e1e81 }
span.s1 { }
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #3e1e81 }
span.s1 { }
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #3e1e81 }
span.s1 { }
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #000000 }
span.s1 { }
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #3e1e81 }
span.s1 { }
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #3e1e81 }
span.s1 { }
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #3e1e81 }
span.s1 { }
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #3e1e81 }
span.s1 { }
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #000000 }
span.s1 { }
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #000000 }
span.s1 { }
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #000000 }
span.s1 { }
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #000000 }
span.s1 { }
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #000000 }
span.s1 { }
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #703daa }
span.s1 { }
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #3e1e81 }
span.s1 { }
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #3e1e81 }
span.s1 { }
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #4f8187 }
span.s1 { }
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #3e1e81 }
span.s1 { }
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #31595d }
span.s1 { }
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #000000 }
span.s1 { color: #4f8187 }
span.s2 { }
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #31595d }
span.s1 { }
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #31595d }
span.s1 { }
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #31595d }
span.s1 { }
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #4f8187 }
span.s1 { }

时间: 2024-10-16 00:54:22

GPUImage的filter 响应处理链 的理解笔记的相关文章

Filter技术+职责链模式

Filter是一个过滤器,存在Web客户端与请求的资源之间,这里的资源可以说是jsp或servlet.它的作用就是在请求达到资源之前,先对请求进行预处理,并且也可以对servlet处理后的response进行修改. Filter可以是有很多个,当一个个Filter组合成起来,就形成了一个FilterChain.也就是我们说的过滤链,这个过滤链处理的过程,就是我们前面学的职责链模式的一个体现. 下面是一个修改字符串的小例子: 1.FilterChain内包含各个子filter,利用dofilter

对原型链的理解 语言表达能力不好 直接用代码,哈

分享一下 我对原型和原型链的理解 原型对象: function People(nameValue,ageValue,fondValue)            {                this.name = nameValue;                this.age = ageValue;                this.fond = fondValue;            } People.prototype.rule = function()         

响应式设计:理解设备像素,CSS像素和屏幕分辨率

概述 屏幕分辨率.设备像素和CSS像素这些术语,在非常多语境下,是可互换的,但也因此easy在有差异的地方引起混淆,实际上它们是不同的概念. 屏幕分辨率和设备像素是物理概念,而CSS像素是WEB编程的概念:屏幕分辨率和设备像素的区别在于设备像素显示密度. 当设备屏幕ZOOM=100%的时候,浏览器CSS像素尺寸和设备像素相等,而当像素密度(pixel density)为1的时候,屏幕分辨率和设备像素相等. 响应式设计 在响应式设计中,使用了viewport,device-width,media

对于响应式布局的理解

今天学习了响应式布局颇有些感慨,其中有些重要的方法和属性的用法我在这里跟大家分享一下. 这个是我案例截图的一部分 大家看了以上的代码可知:它是通过@media媒介查询判断来执行的CSS样式,也就是说你写了一套代码,现在可以分别适配在手机.平板.PC上.通过@media的媒体查询来实现的响应式布局. 其中还有个重要的问题就是在手机设备上,我们要禁止用户来缩放屏幕.不禁止的话,可能在显示上会造成错位,以及显示的不是手机网站的样式.所以,我们要通过代码来禁止用户在手机端上缩放屏幕,已达到正常的手机网站

原型链的理解

### 原型链的理解 #### 概念 + javascript每一个对象**包括原型对象**都有一个内置的`proto`属性指向创建他的函数对象的原型对象,即`prototype`属性 #### 作用 + 实现对象的继承 ### 理解 1.函数对象 + 在javascript中,函数就是对象 2.原型对象 + 当定义一个函数对象的时候,会包含一个预定的属性,`prototype`,这就属性称之为原型对象 3.\__proto__ + javascript 在创建对象的时候,都会有一个\_prot

面向对象(2 )构造函数 原型 原型链的理解

面向对象(2) 原型 原型链的理解 1.面向对象的目的就是生成对象object. 2.生成对象的方式 (1)单例模式(字面量定义)var obj={} (2)类的实例 var obj=new Object() (3)工厂模式 (4)构造函数 工厂模式和构造函数的区别? 工厂模式,生成的对象必须要返回,构造函数模式不用return,构造函数模式默认return旳是this,在构造函数内的this就是实例对象. 构造函数如果人为return的不是对象,直接忽略,如果人为return的是对象,就取代t

Memcached理解笔记3---Memcached使用总结

为了将N个前端数据同步,通过Memcached完成数据打通,但带来了一些新问题: 使用iBatis整合了Memcached,iBatis针对每台server生成了唯一标识,导致同一份数据sql会产生不同的key,造成重复缓存.——通过重写iBatis部分原码,终止了唯一标识的生成,同一个SQL产生同一个Key,同时对生成key做hash,控制长度,使得数据统一在Memcached. 为了迎合iBatis的架构,通过CacheModel模式,对缓存数据分组管理.最初通过Map实现CacheMode

Memcached理解笔记4---应对高并发攻击

近半个月过得很痛苦,主要是产品上线后,引来无数机器用户恶意攻击,不停的刷新产品各个服务入口,制造垃圾数据,消耗资源.他们的最好成绩,1秒钟可以并发6次,赶在Database入库前,Cache进行Missing Loading前,强占这其中十几毫秒的时间,进行恶意攻击. 相关链接: Memcached笔记——(一)安装&常规错误&监控Memcached笔记——(二)XMemcached&Spring集成 Memcached笔记——(三)Memcached使用总结  Memcached

网站广告模版理解笔记

电子商务网站的,页面上往往或有很多活动的广告需要频繁的替换,还有关于网站的说明的文章,这些不是经常变动的,还有一些是专区的活动页面,上面放的都是活动的商品. 简单的设计思路: 1.建立一个模版表template,可以根据模版生成html 结构:id.模版名称.状态(有效和无效).模版内容.类型(html和vm) 2.建立一个模版实例表templateInstence,用来存放生成的html的信息 结构:id.templateId.html文件名称.html文件位置 3.建立一个模版实例商品表(非