[关闭]
@qidiandasheng 2020-07-13T14:46:55.000000Z 字数 6562 阅读 1653

GPUImage解析(二):帧缓冲

音视频


简介

OpenGL ES的FrameBuffer是渲染发生的地方,普通的2D图形的渲染默认发生在屏幕上;而三维的图形渲染则除了包括像素点的颜色,还有Depth Buffer,Stencil Buffer等其他空间。因此,FrameBuffer就是一个这些Buffer的一个集合。

默认情况下,FrameBuffer存在于现存中,但是当需要进行多次渲染或者离屏渲染的时候,可以通过创建一个离屏的FrameBuffer进行渲染。当需要渲染3D效果时,除了创建frameBuffer以外,还需要创建相应的Render Buffer, Depth Buffer, Stencil Buffer,并且将它们attach到frameBuffer上;而当只需要渲染2D图形时,可以直接生成一个Texture,并且将FrameBuffer渲染的结果放入Texture中。

生成一个FrameBuffer的代码:

  1. glGenFramebuffers(1, &framebuffer);
  2. glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);

由于存在离屏渲染的情况,实际渲染开始之前,需要先激活目标FrameBuffer,而在渲染完成之后,需要将FrameBuffer绑定为0,回到默认的FrameBuffer,才能够显示在屏幕上:

  1. glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
  2. //Rendering Code
  3. glBindFramebuffer(GL_FRAMEBUFFER, 0);

如果要渲染一个2D图像到一个Texture上,则还需要先生成一个Texture,并且将Texture绑定到FrameBuffer上。当使用FrameBuffer进行流的传递的时候,则可以使用这个Texture:

  1. glActiveTexture(GL_TEXTURE1);
  2. glGenTextures(1, &_texture);
  3. glBindTexture(GL_TEXTURE_2D, _texture);
  4. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  5. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  6. // This is necessary for non-power-of-two textures
  7. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
  8. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
  9. glBindTexture(GL_TEXTURE_2D, _texture);
  10. glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (int)_size.width, (int)_size.height, 0,GL_RGBA, GL_UNSIGNED_BYTE, 0);
  11. glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _texture, 0);

GPUImageFrameBuffer

GPUImageFrameBuffer不只是简单的对OpenGL ES的FrameBuffer的对象化封装,而是一个对渲染对象的对象化封装。它生成的可以是一个带有Texture作为Attachment的FrameBuffer,也可以仅仅生成一个Texture作为渲染对象。它的主要功能有:

  1. - (id)initWithSize:(CGSize)framebufferSize;
  2. - (id)initWithSize:(CGSize)framebufferSize textureOptions:(GPUTextureOptions)fboTextureOptions onlyTexture:(BOOL)onlyGenerateTexture;
  3. - (id)initWithSize:(CGSize)framebufferSize overriddenTexture:(GLuint)inputTexture;
  1. - (void)activateFramebuffer;
  1. - (void)lock;
  2. - (void)unlock;
  3. - (void)clearAllLocks;
  4. - (void)disableReferenceCounting;
  5. - (void)enableReferenceCounting;
  1. - (CGImageRef)newCGImageFromFramebufferContents;
  2. - (GLubyte *)byteBuffer;

生成渲染对象

可以通过onlyGenerateTexture来设置生成的渲染对象,YES表示生成的只是纹理缓冲对象,NO为生成帧缓冲对象。

以下代码为创建帧缓冲:

  1. - (void)generateFramebuffer;
  2. {
  3. runSynchronouslyOnVideoProcessingQueue(^{
  4. [GPUImageContext useImageProcessingContext];
  5. glGenFramebuffers(1, &framebuffer);
  6. glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
  7. // By default, all framebuffers on iOS 5.0+ devices are backed by texture caches, using one shared cache
  8. if ([GPUImageContext supportsFastTextureUpload])
  9. {
  10. #if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
  11. // 从当前上下文获取CoreVideo中的纹理缓存
  12. CVOpenGLESTextureCacheRef coreVideoTextureCache = [[GPUImageContext sharedImageProcessingContext] coreVideoTextureCache];
  13. // Code originally sourced from http://allmybrain.com/2011/12/08/rendering-to-a-texture-with-ios-5-texture-cache-api/
  14. CFDictionaryRef empty; // empty value for attr value.
  15. CFMutableDictionaryRef attrs;
  16. empty = CFDictionaryCreate(kCFAllocatorDefault, NULL, NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); // our empty IOSurface properties dictionary
  17. attrs = CFDictionaryCreateMutable(kCFAllocatorDefault, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
  18. CFDictionarySetValue(attrs, kCVPixelBufferIOSurfacePropertiesKey, empty);
  19. CVReturn err = CVPixelBufferCreate(kCFAllocatorDefault, (int)_size.width, (int)_size.height, kCVPixelFormatType_32BGRA, attrs, &renderTarget);
  20. if (err)
  21. {
  22. NSLog(@"FBO size: %f, %f", _size.width, _size.height);
  23. NSAssert(NO, @"Error at CVPixelBufferCreate %d", err);
  24. }
  25. // 根据纹理缓存获取到CV纹理renderTexture
  26. err = CVOpenGLESTextureCacheCreateTextureFromImage (kCFAllocatorDefault, coreVideoTextureCache, renderTarget,
  27. NULL, // texture attributes
  28. GL_TEXTURE_2D,
  29. _textureOptions.internalFormat, // opengl format
  30. (int)_size.width,
  31. (int)_size.height,
  32. _textureOptions.format, // native iOS format
  33. _textureOptions.type,
  34. 0,
  35. &renderTexture);
  36. if (err)
  37. {
  38. NSAssert(NO, @"Error at CVOpenGLESTextureCacheCreateTextureFromImage %d", err);
  39. }
  40. CFRelease(attrs);
  41. CFRelease(empty);
  42. // 根据CVOpenGLESTextureGetTarget和CVOpenGLESTextureGetName获取纹理的target和标识符
  43. glBindTexture(CVOpenGLESTextureGetTarget(renderTexture), CVOpenGLESTextureGetName(renderTexture));
  44. _texture = CVOpenGLESTextureGetName(renderTexture);
  45. glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, _textureOptions.wrapS);
  46. glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, _textureOptions.wrapT);
  47. /*
  48. 把纹理附加到帧缓存上
  49. target:我们所创建的帧缓冲类型的目标(绘制、读取或两者都有)。
  50. attachment:我们所附加的附件的类型。现在我们附加的是一个颜色附件。需要注意,最后的那个0是暗示我们可以附加1个以上颜色的附件。
  51. textarget:你希望附加的纹理类型。
  52. texture:附加的实际纹理。
  53. level:Mipmap level。我们设置为0。
  54. */
  55. glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, CVOpenGLESTextureGetName(renderTexture), 0);
  56. #endif
  57. }
  58. else
  59. {
  60. [self generateTexture];
  61. glBindTexture(GL_TEXTURE_2D, _texture);
  62. glTexImage2D(GL_TEXTURE_2D, 0, _textureOptions.internalFormat, (int)_size.width, (int)_size.height, 0, _textureOptions.format, _textureOptions.type, 0);
  63. /*
  64. 把纹理附加到帧缓存上
  65. target:我们所创建的帧缓冲类型的目标(绘制、读取或两者都有)。
  66. attachment:我们所附加的附件的类型。现在我们附加的是一个颜色附件。需要注意,最后的那个0是暗示我们可以附加1个以上颜色的附件。
  67. textarget:你希望附加的纹理类型。
  68. texture:附加的实际纹理。
  69. level:Mipmap level。我们设置为0。
  70. */
  71. glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _texture, 0);
  72. }
  73. #ifndef NS_BLOCK_ASSERTIONS
  74. GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
  75. NSAssert(status == GL_FRAMEBUFFER_COMPLETE, @"Incomplete filter FBO: %d", status);
  76. #endif
  77. glBindTexture(GL_TEXTURE_2D, 0);
  78. });
  79. }

GPUImageFrameBuffer的引用计数

由于GPUImageFrameBuffer需要重用,因此,当FrameBuffer的引用为0的时候,这个FrameBuffer就会被放到Cache中,来给其他的需要的地方进行使用。因此作者给GPUImageFrameBuffer引入了引用计数的概念。

引用计数的概念其实和OC自带的MRC有点类似,每当一个地方需要使用这个FrameBuffer的时候,手动调用lock方法,使引用计数+1;当渲染完毕或者这个FrameBuffer使用完毕的时候,手动调用unlock方法,使引用计数-1;当引用计数为0的时候,FrameBuffer会被归还到Cache中。

调用增加FrameBuffer引用计数方法的地方:

调用减少FrameBuffer引用计数方法的地方:

不需要引用计数的情况:

参考

GPUImage源码解读之GPUImageFramebuffer
GPUImage详细解析

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注