GPUImage的filter 响应处理链 的理解笔记
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 响应处理链 的理解笔记的更多相关文章
- 由浅入深讲解责任链模式,理解Tomcat的Filter过滤器
本文将从简单的场景引入, 逐步优化, 最后给出具体的责任链设计模式实现. 场景引入 首先我们考虑这样一个场景: 论坛上用户要发帖子, 但是用户的想法是丰富多变的, 他们可能正常地发帖, 可能会在网页中 ...
- Filter体现职责链模式
1. 前言 Filter—Filter 技术是servlet2.3 新增加的功能.完成的流程:对用户请求进行预处理,接着将请求交给Servlet进行处理并生成响应,最后Filter再对服务器响应进行后 ...
- 谈谈我对 js原型链的理解
想要学习 “原型链” 必须要认识什么是 “原型” 和 “原型链” 先理解一下普通的继承和原型的区别,下面写一段js代码来帮助理解: var Animal = function(){ // 动物抽象类 ...
- javascript原型与原型链个人理解
想了解原型和原型链,我觉得首先我们得知道javascript里有一个Object 与 Function,它俩都是构造函数,当然函数也是一个对象.我们打印Object 与 Function看一下, co ...
- 蒟蒻的长链剖分学习笔记(例题:HOTEL加强版、重建计划)
长链剖分学习笔记 说到树的链剖,大多数人都会首先想到重链剖分.的确,目前重链剖分在OI中有更加多样化的应用,但它大多时候是替代不了长链剖分的. 重链剖分是把size最大的儿子当成重儿子,顾名思义长链剖 ...
- batch normalization学习理解笔记
batch normalization学习理解笔记 最近在Andrew Ng课程中学到了Batch Normalization相关内容,通过查阅资料和原始paper,基本上弄懂了一些算法的细节部分,现 ...
- Filter的过滤链理解
一.Filter过滤链 web.xml配置了filter过滤器,在容器启动的时候执行了init()方法进行了初始化,然后在容器关闭的时候执行了destroy()方法销毁过滤器,在每次服务器接受请求的时 ...
- Filter技术+职责链模式
Filter是一个过滤器,存在Webclient与请求的资源之间.这里的资源能够说是jsp或servlet.它的作用就是在请求达到资源之前,先对请求进行预处理.而且也能够对servlet处理后的res ...
- 响应式设计:理解设备像素,CSS像素和屏幕分辨率
概述 屏幕分辨率.设备像素和CSS像素这些术语,在非常多语境下,是可互换的,但也因此easy在有差异的地方引起混淆,实际上它们是不同的概念. 屏幕分辨率和设备像素是物理概念,而CSS像素是WEB编程的 ...
随机推荐
- netty——私有协议栈开发案例
netty--私有协议栈开发案例 摘要: 在学习李林峰老师的Netty权威指南中,觉得第十二章<私有协议栈开发>中的案例代码比较有代表性,讲的也不错,但是代码中个人认为有些简单的错误,个人 ...
- Tomcat access log配置
在tomcat的access中打印出请求的情况可以帮助我们分析问题,通常比较关注的有访问IP.线程号.访问url.返回状态码.访问时间.持续时间. 在Spring boot中使用了内嵌的tomcat, ...
- 百度UEditor图片上传或文件上传路径自定义
最近在项目中使用到百度UEditor的图片以及文件上传功能,但在上传的时候路径总是按照预设规则来自动生成,不方便一些特殊文件的维护.于是开始查看文档和源代码,其实操作还是比较简单的,具体如下: 1.百 ...
- java进制转换(无视正负数的差别)
最近看了一下学习资料,感觉进制转换其实还是挺有意思的,尤其是对于负数这一方面. 下面和大家分享一下,这里只写了十进制到二进制的转换,其实都是同样的道理 public class Test1 { pub ...
- 少年,是时候换种更优雅的方式部署你的php代码了
让我们来回忆下上次你是怎么发布你的代码的: 1. 先把线上的代码用ftp备份下来 2. 上传修改了的文件 3. 测试一下功能是否正常 4. 网站500了,赶紧用备份替换回去 5. 替换错了/替换漏了 ...
- Spring+SpringMVC+Mybaties整合之配置文件如何配置及内容解释--可直接拷贝使用--不定时更改之2017/4/27
以下配置可直接使用,只需更改包名. 关于内部标签的解释及用法,都以注解形式在代码内部说明.个人原创,转载需注明出处. 1,web.xml.添加jar包后首先需要配置WEB-INF下的web.xml文件 ...
- C++随机数rand(), srand()
c++产生随机数会用到rand(), srand()函数,下面总结两个函数特性和使用. 1. rand() #include <iostream> #include <cstdlib ...
- Linux学习之竿头直上
今天第二讲昨天我们讲解了10基础命令,今早上我继续为大家讲解10个linux常见命令 今天主要讲解与网络相关的命令和软件安装的命名 NetworkManager 与rpm 在windows中我们查看网 ...
- windows下nodejs安装及配置
1)在 http://nodejs.org/download/下载一个window的安装包 2)运行安装包,设置安装路径,如安装到D:\nodejs,安装后会自动将d:\nodejs添加到系统变量pa ...
- Android 安卓实现页面相互跳转并相互传递参数
一.对于两个页面之间相互传值,跳转的时候我们使用 startActivityForResult(intent,0),而不是startActivity(intent) 这个方法 第一个页面中在触发跳转的 ...