GPUImage的filter的textures处理链式结构

两个最重要的的地方:

最重要的一个类GPUImageOutput(所有的filter的父类,其他也有继承它的,如GPUImageUIElement,UIKit元素通过CG转gles贴图 等等); 
协议(或者接口)GPUImageInput。 
  继承GPUImageOutput且遵循GPUImageInput的filter,处理完成后输出又可以作为下一个filter的输入。

@protocol GPUImageInput <NSObject>
- (void)newFrameReadyAtTime:(CMTime)frameTime atIndex:(NSInteger)textureIndex;
- (void)setInputFramebuffer:(GPUImageFramebuffer *)newInputFramebuffer atIndex:(NSInteger)textureIndex;
- (NSInteger)nextAvailableTextureIndex;
- (void)setInputSize:(CGSize)newSize atIndex:(NSInteger)textureIndex;
- (void)setInputRotation:(GPUImageRotationMode)newInputRotation atIndex:(NSInteger)textureIndex;
- (CGSize)maximumOutputSize;
- (void)endProcessing;
- (BOOL)shouldIgnoreUpdatesToThisTarget;
- (BOOL)enabled;
- (BOOL)wantsMonochromeInput;
- (void)setCurrentlyReceivingMonochromeInput:(BOOL)newValue;
@end

GPUImageFramebuffer

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

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

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

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

CGDataProviderRef dataProvider = NULL;
if ([GPUImageContext supportsFastTextureUpload])
{
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
NSUInteger paddedWidthOfImage = CVPixelBufferGetBytesPerRow(renderTarget) / 4.0;   //字节对齐后的图片占用宽度可能要大
NSUInteger paddedBytesForImage = paddedWidthOfImage * (int)_size.height * ;
glFinish();    //强制提交前面调用的gl指令到GPU硬件,阻塞调用
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
[self lockForReading];
rawImagePixels = (GLubyte *)CVPixelBufferGetBaseAddress(renderTarget);
dataProvider = CGDataProviderCreateWithData((__bridge_retained void*)self, rawImagePixels, paddedBytesForImage, dataProviderUnlockCallback); //全局的framebuffercache强引用当前自身,防止framebuffer在切换时出现问题
[[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
#else
#endif
}
else
{
[self activateFramebuffer];
rawImagePixels = (GLubyte *)malloc(totalBytesForImage);
glReadPixels(, , (int)_size.width, (int)_size.height, GL_RGBA, GL_UNSIGNED_BYTE, rawImagePixels); //阻塞调用,直接从framebuffer中读取image 原始数据
dataProvider = CGDataProviderCreateWithData(NULL, rawImagePixels, totalBytesForImage, dataProviderReleaseCallback);
[self unlock]; // Don't need to keep this around anymore
}

最后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。

1,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处理数据。

2,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协议方法)简单地赋值 ;

- (void)setInputFramebuffer:(GPUImageFramebuffer *)newInputFramebuffer atIndex:(NSInteger)textureIndex;
{
firstInputFramebuffer = newInputFramebuffer;
[firstInputFramebuffer lock];
}

然后调用newFrameReadyAtTime;

- (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:]; 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, );
  //分别设置顶点shader中的顶点数据,和将来用于片元shader中的texture坐标数据
glVertexAttribPointer(filterPositionAttribute, , GL_FLOAT, , , vertices);
glVertexAttribPointer(filterTextureCoordinateAttribute, , GL_FLOAT, , , textureCoordinates); glDrawArrays(GL_TRIANGLE_STRIP, , ); [firstInputFramebuffer unlock]; if (usingNextFrameForImageCapture)
{
dispatch_semaphore_signal(imageCaptureSemaphore);
}
}

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

3,GPUImageView

  作为最终的输出target只实现了GPUImageInput的协议,只能接受source或者filter传过来的数据,不再作为输出了; 
  其中的setInputFramebuffer 和 newFrameReadyAtTime和filter中处理如出一辙,但是加了一个调用;如下,正如开头提到的framebuffer也可以绑定到renderBuffer,也常称为colorbuffer,renderbuffer直接显示在CAEAGLLayer上了;最终通过设置屏幕大小的缓冲区,直接显示在手机屏幕上。

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

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

小结

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

参考

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

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

  1. 由浅入深讲解责任链模式,理解Tomcat的Filter过滤器

    本文将从简单的场景引入, 逐步优化, 最后给出具体的责任链设计模式实现. 场景引入 首先我们考虑这样一个场景: 论坛上用户要发帖子, 但是用户的想法是丰富多变的, 他们可能正常地发帖, 可能会在网页中 ...

  2. Filter体现职责链模式

    1. 前言 Filter—Filter 技术是servlet2.3 新增加的功能.完成的流程:对用户请求进行预处理,接着将请求交给Servlet进行处理并生成响应,最后Filter再对服务器响应进行后 ...

  3. 谈谈我对 js原型链的理解

    想要学习 “原型链” 必须要认识什么是 “原型” 和 “原型链” 先理解一下普通的继承和原型的区别,下面写一段js代码来帮助理解: var Animal = function(){ // 动物抽象类 ...

  4. javascript原型与原型链个人理解

    想了解原型和原型链,我觉得首先我们得知道javascript里有一个Object 与 Function,它俩都是构造函数,当然函数也是一个对象.我们打印Object 与 Function看一下, co ...

  5. 蒟蒻的长链剖分学习笔记(例题:HOTEL加强版、重建计划)

    长链剖分学习笔记 说到树的链剖,大多数人都会首先想到重链剖分.的确,目前重链剖分在OI中有更加多样化的应用,但它大多时候是替代不了长链剖分的. 重链剖分是把size最大的儿子当成重儿子,顾名思义长链剖 ...

  6. batch normalization学习理解笔记

    batch normalization学习理解笔记 最近在Andrew Ng课程中学到了Batch Normalization相关内容,通过查阅资料和原始paper,基本上弄懂了一些算法的细节部分,现 ...

  7. Filter的过滤链理解

    一.Filter过滤链 web.xml配置了filter过滤器,在容器启动的时候执行了init()方法进行了初始化,然后在容器关闭的时候执行了destroy()方法销毁过滤器,在每次服务器接受请求的时 ...

  8. Filter技术+职责链模式

    Filter是一个过滤器,存在Webclient与请求的资源之间.这里的资源能够说是jsp或servlet.它的作用就是在请求达到资源之前,先对请求进行预处理.而且也能够对servlet处理后的res ...

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

    概述 屏幕分辨率.设备像素和CSS像素这些术语,在非常多语境下,是可互换的,但也因此easy在有差异的地方引起混淆,实际上它们是不同的概念. 屏幕分辨率和设备像素是物理概念,而CSS像素是WEB编程的 ...

随机推荐

  1. less的基本操作

    less的四大特性及示例 1.特性一(变量) less写法 @color:#ffffff: body{background-color:@color:} 生成的css body{background- ...

  2. 【小瑕疵】表单中的button会自动提交?

    在表单中使用button标签的时候会发现,即使什么类型都没有设置,但是在点击的时候会发现,表单会自动提交 比如: 我在一个表单的button中添加一个函数,当点击这个按钮时会增加一行内容: 但是当我实 ...

  3. 简单的后台数据和前台数据交互.net

    最近忙着做POS项目,心血来来潮写了点小项目. 更具要求是随机显示数据并且产生的数据是可以控制的.前台交互显示能够倒叙,切每次只显示一条,页面不能超过20条超过的部分做删除. 我先展示一下前台的代码, ...

  4. JavaScript中的数据结构及实战系列(1):队列

    开题 张三丰教无忌太极剑法: 还记得吗? 全都记得. 现在呢? 已经忘却了一小半. 啊,已经忘了一大半. 不坏不坏,忘得真快,那么现在呢? 已经全都忘了,忘得干干净净. 好了,你上吧. 长时间写前端代 ...

  5. 初学T4模板

    1.T4模板语法 <#@ template language="C#v3.5" hostSpecific="true" debug="true& ...

  6. http接口加密《一》:移动应用中,通过在客户端对访问的url进行加密处理来保护服务器上的数据

    来源:http://meiyitianabc.blog.163.com/blog/static/10502212720131056273619/ 我认为,保护服务器端的数据,有这么几个关键点: 不能对 ...

  7. JQuery 根据ID在页面中定位

    1.锚点跳转简介 锚点其实就是可以让页面定位到某个位置上的点.在高度较高的页面中经常见到.比如百度的百科页面,wiki中的page内容. 我知道实现锚点的跳转有两种形式,一种是a标签+name属性:还 ...

  8. 使用Nodejs进行反向代理

    在实际工程开发中,会有前后端分离的需求. 为了平滑的完成前端请求到后端各个独立服务,需要一个中间件实现请求转发的功能,利用Nginx可以实现,在这里,使用nodejs实现一个反向代理服务器.   实际 ...

  9. javascript中的==与===

    一.主要区别: 1.通俗的来说,==是值的比较,而===不仅仅比较值,还比较引用的是否是同一个对象. 2.用==来比较的时候,如果两个数的操作数的类型不一样,会先转换.而===的操作数则不会进行任何转 ...

  10. 转载: RAID详解[RAID0/RAID1/RAID10/RAID5]

    一.RAID定义 RAID(Redundant Array of Independent Disk 独立冗余磁盘阵列)技术是加州大学伯克利分校1987年提出,最初是为了组合小的廉价磁盘来代替大的昂贵磁 ...