GPUImageContext类,提供OpenGL ES基本环境,我们一般不会用到,所以讲的很简单。
属性
@property(readonly, nonatomic) dispatch_queue_t contextQueue
说明:创建一个context线程
描述:_contextQueue = dispatch_queue_create("com.sunsetlakesoftware.GPUImage.openGLESContextQueue", NULL);
@property(readwrite, retain, nonatomic) GLProgram *currentShaderProgram
说明:当前着色器program
@property(readonly, retain, nonatomic) EAGLContext *context
说明:opengl es绘制环境,管理上下文
方法
+ (void *)contextKey
说明:设置当前context的线程标识符,全局静态变量。
+ (GPUImageContext *)sharedImageProcessingContext
说明:创建一个全局的GPUImageContext对象的单例。
+ (dispatch_queue_t)sharedContextQueue
说明:创建一个context queue单例
+ (GPUImageFramebufferCache *)sharedFramebufferCache
说明:创建一个GPUFramebufferCache单例
- (void)useAsCurrentContext
说明:使用当前context
+ (void)setActiveShaderProgram:(GLProgram *)shaderProgram;
- (void)setContextShaderProgram:(GLProgram *)shaderProgram;
+ (GLint)maximumTextureSizeForThisDevice;
+ (GLint)maximumTextureUnitsForThisDevice;
+ (GLint)maximumVaryingVectorsForThisDevice;
+ (BOOL)deviceSupportsOpenGLESExtension:(NSString *)extension;
+ (BOOL)deviceSupportsRedTextures;
+ (BOOL)deviceSupportsFramebufferReads;
+ (CGSize)sizeThatFitsWithinATextureForSize:(CGSize)inputSize;
- (void)presentBufferForDisplay;
- (GLProgram *)programForVertexShaderString:(NSString *)vertexShaderString fragmentShaderString:(NSString *)fragmentShaderString;
- (void)useSharegroup:(EAGLSharegroup *)sharegroup;
+ (BOOL)supportsFastTextureUpload;
完整代码
#import "GLProgram.h" #import "GPUImageFramebuffer.h" #import "GPUImageFramebufferCache.h" #define GPUImageRotationSwapsWidthAndHeight(rotation) ((rotation) == kGPUImageRotateLeft || (rotation) == kGPUImageRotateRight || (rotation) == kGPUImageRotateRightFlipVertical || (rotation) == kGPUImageRotateRightFlipHorizontal) typedef enum { kGPUImageNoRotation, kGPUImageRotateLeft, kGPUImageRotateRight, kGPUImageFlipVertical, kGPUImageFlipHorizonal, kGPUImageRotateRightFlipVertical, kGPUImageRotateRightFlipHorizontal, kGPUImageRotate180 } GPUImageRotationMode; @interface GPUImageContext : NSObject @property(readonly, nonatomic) dispatch_queue_t contextQueue; @property(readwrite, retain, nonatomic) GLProgram *currentShaderProgram; @property(readonly, retain, nonatomic) EAGLContext *context; @property(readonly) CVOpenGLESTextureCacheRef coreVideoTextureCache; @property(readonly) GPUImageFramebufferCache *framebufferCache; + (void *)contextKey; + (GPUImageContext *)sharedImageProcessingContext; + (dispatch_queue_t)sharedContextQueue; + (GPUImageFramebufferCache *)sharedFramebufferCache; + (void)useImageProcessingContext; - (void)useAsCurrentContext; + (void)setActiveShaderProgram:(GLProgram *)shaderProgram; - (void)setContextShaderProgram:(GLProgram *)shaderProgram; + (GLint)maximumTextureSizeForThisDevice; + (GLint)maximumTextureUnitsForThisDevice; + (GLint)maximumVaryingVectorsForThisDevice; + (BOOL)deviceSupportsOpenGLESExtension:(NSString *)extension; + (BOOL)deviceSupportsRedTextures; + (BOOL)deviceSupportsFramebufferReads; + (CGSize)sizeThatFitsWithinATextureForSize:(CGSize)inputSize; - (void)presentBufferForDisplay; - (GLProgram *)programForVertexShaderString:(NSString *)vertexShaderString fragmentShaderString:(NSString *)fragmentShaderString; - (void)useSharegroup:(EAGLSharegroup *)sharegroup; // Manage fast texture upload + (BOOL)supportsFastTextureUpload; @end @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
#import "GPUImageContext.h" #import <OpenGLES/EAGLDrawable.h> #import <AVFoundation/AVFoundation.h> #define MAXSHADERPROGRAMSALLOWEDINCACHE 40 @interface GPUImageContext() { NSMutableDictionary *shaderProgramCache; NSMutableArray *shaderProgramUsageHistory; EAGLSharegroup *_sharegroup; } @end @implementation GPUImageContext @synthesize context = _context; @synthesize currentShaderProgram = _currentShaderProgram; @synthesize contextQueue = _contextQueue; @synthesize coreVideoTextureCache = _coreVideoTextureCache; @synthesize framebufferCache = _framebufferCache; static void *openGLESContextQueueKey; - (id)init; { if (!(self = [super init])) { return nil; } openGLESContextQueueKey = &openGLESContextQueueKey; _contextQueue = dispatch_queue_create("com.sunsetlakesoftware.GPUImage.openGLESContextQueue", NULL); #if OS_OBJECT_USE_OBJC dispatch_queue_set_specific(_contextQueue, openGLESContextQueueKey, (__bridge void *)self, NULL); #endif shaderProgramCache = [[NSMutableDictionary alloc] init]; shaderProgramUsageHistory = [[NSMutableArray alloc] init]; return self; } + (void *)contextKey { return openGLESContextQueueKey; } // Based on Colin Wheeler's example here: http://cocoasamurai.blogspot.com/2011/04/singletons-your-doing-them-wrong.html + (GPUImageContext *)sharedImageProcessingContext; { static dispatch_once_t pred; static GPUImageContext *sharedImageProcessingContext = nil; dispatch_once(&pred, ^{ sharedImageProcessingContext = [[[self class] alloc] init]; }); return sharedImageProcessingContext; } + (dispatch_queue_t)sharedContextQueue; { return [[self sharedImageProcessingContext] contextQueue]; } + (GPUImageFramebufferCache *)sharedFramebufferCache; { return [[self sharedImageProcessingContext] framebufferCache]; } + (void)useImageProcessingContext; { [[GPUImageContext sharedImageProcessingContext] useAsCurrentContext]; } - (void)useAsCurrentContext; { EAGLContext *imageProcessingContext = [self context]; if ([EAGLContext currentContext] != imageProcessingContext) { [EAGLContext setCurrentContext:imageProcessingContext]; } } + (void)setActiveShaderProgram:(GLProgram *)shaderProgram; { GPUImageContext *sharedContext = [GPUImageContext sharedImageProcessingContext]; [sharedContext setContextShaderProgram:shaderProgram]; } - (void)setContextShaderProgram:(GLProgram *)shaderProgram; { EAGLContext *imageProcessingContext = [self context]; if ([EAGLContext currentContext] != imageProcessingContext) { [EAGLContext setCurrentContext:imageProcessingContext]; } if (self.currentShaderProgram != shaderProgram) { self.currentShaderProgram = shaderProgram; [shaderProgram use]; } } + (GLint)maximumTextureSizeForThisDevice; { static dispatch_once_t pred; static GLint maxTextureSize = 0; dispatch_once(&pred, ^{ [self useImageProcessingContext]; glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize); }); return maxTextureSize; } + (GLint)maximumTextureUnitsForThisDevice; { static dispatch_once_t pred; static GLint maxTextureUnits = 0; dispatch_once(&pred, ^{ [self useImageProcessingContext]; glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &maxTextureUnits); }); return maxTextureUnits; } + (GLint)maximumVaryingVectorsForThisDevice; { static dispatch_once_t pred; static GLint maxVaryingVectors = 0; dispatch_once(&pred, ^{ [self useImageProcessingContext]; glGetIntegerv(GL_MAX_VARYING_VECTORS, &maxVaryingVectors); }); return maxVaryingVectors; } + (BOOL)deviceSupportsOpenGLESExtension:(NSString *)extension; { static dispatch_once_t pred; static NSArray *extensionNames = nil; // Cache extensions for later quick reference, since this won't change for a given device dispatch_once(&pred, ^{ [GPUImageContext useImageProcessingContext]; NSString *extensionsString = [NSString stringWithCString:(const char *)glGetString(GL_EXTENSIONS) encoding:NSASCIIStringEncoding]; extensionNames = [extensionsString componentsSeparatedByString:@" "]; }); return [extensionNames containsObject:extension]; } // http://www.khronos.org/registry/gles/extensions/EXT/EXT_texture_rg.txt + (BOOL)deviceSupportsRedTextures; { static dispatch_once_t pred; static BOOL supportsRedTextures = NO; dispatch_once(&pred, ^{ supportsRedTextures = [GPUImageContext deviceSupportsOpenGLESExtension:@"GL_EXT_texture_rg"]; }); return supportsRedTextures; } + (BOOL)deviceSupportsFramebufferReads; { static dispatch_once_t pred; static BOOL supportsFramebufferReads = NO; dispatch_once(&pred, ^{ supportsFramebufferReads = [GPUImageContext deviceSupportsOpenGLESExtension:@"GL_EXT_shader_framebuffer_fetch"]; }); return supportsFramebufferReads; } + (CGSize)sizeThatFitsWithinATextureForSize:(CGSize)inputSize; { GLint maxTextureSize = [self maximumTextureSizeForThisDevice]; if ( (inputSize.width < maxTextureSize) && (inputSize.height < maxTextureSize) ) { return inputSize; } CGSize adjustedSize; if (inputSize.width > inputSize.height) { adjustedSize.width = (CGFloat)maxTextureSize; adjustedSize.height = ((CGFloat)maxTextureSize / inputSize.width) * inputSize.height; } else { adjustedSize.height = (CGFloat)maxTextureSize; adjustedSize.width = ((CGFloat)maxTextureSize / inputSize.height) * inputSize.width; } return adjustedSize; } - (void)presentBufferForDisplay; { [self.context presentRenderbuffer:GL_RENDERBUFFER]; } - (GLProgram *)programForVertexShaderString:(NSString *)vertexShaderString fragmentShaderString:(NSString *)fragmentShaderString; { NSString *lookupKeyForShaderProgram = [NSString stringWithFormat:@"V: %@ - F: %@", vertexShaderString, fragmentShaderString]; GLProgram *programFromCache = [shaderProgramCache objectForKey:lookupKeyForShaderProgram]; if (programFromCache == nil) { programFromCache = [[GLProgram alloc] initWithVertexShaderString:vertexShaderString fragmentShaderString:fragmentShaderString]; [shaderProgramCache setObject:programFromCache forKey:lookupKeyForShaderProgram]; // [shaderProgramUsageHistory addObject:lookupKeyForShaderProgram]; // if ([shaderProgramUsageHistory count] >= MAXSHADERPROGRAMSALLOWEDINCACHE) // { // for (NSUInteger currentShaderProgramRemovedFromCache = 0; currentShaderProgramRemovedFromCache < 10; currentShaderProgramRemovedFromCache++) // { // NSString *shaderProgramToRemoveFromCache = [shaderProgramUsageHistory objectAtIndex:0]; // [shaderProgramUsageHistory removeObjectAtIndex:0]; // [shaderProgramCache removeObjectForKey:shaderProgramToRemoveFromCache]; // } // } } return programFromCache; } - (void)useSharegroup:(EAGLSharegroup *)sharegroup; { NSAssert(_context == nil, @"Unable to use a share group when the context has already been created. Call this method before you use the context for the first time."); _sharegroup = sharegroup; } - (EAGLContext *)createContext; { EAGLContext *context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2 sharegroup:_sharegroup]; NSAssert(context != nil, @"Unable to create an OpenGL ES 2.0 context. The GPUImage framework requires OpenGL ES 2.0 support to work."); return context; } #pragma mark - #pragma mark Manage fast texture upload + (BOOL)supportsFastTextureUpload; { #if TARGET_IPHONE_SIMULATOR return NO; #else #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wtautological-pointer-compare" return (CVOpenGLESTextureCacheCreate != NULL); #pragma clang diagnostic pop #endif } #pragma mark - #pragma mark Accessors - (EAGLContext *)context; { if (_context == nil) { _context = [self createContext]; [EAGLContext setCurrentContext:_context]; // Set up a few global settings for the image processing pipeline glDisable(GL_DEPTH_TEST); } return _context; } - (CVOpenGLESTextureCacheRef)coreVideoTextureCache; { if (_coreVideoTextureCache == NULL) { #if defined(__IPHONE_6_0) CVReturn err = CVOpenGLESTextureCacheCreate(kCFAllocatorDefault, NULL, [self context], NULL, &_coreVideoTextureCache); #else CVReturn err = CVOpenGLESTextureCacheCreate(kCFAllocatorDefault, NULL, (__bridge void *)[self context], NULL, &_coreVideoTextureCache); #endif if (err) { NSAssert(NO, @"Error at CVOpenGLESTextureCacheCreate %d", err); } } return _coreVideoTextureCache; } - (GPUImageFramebufferCache *)framebufferCache; { if (_framebufferCache == nil) { _framebufferCache = [[GPUImageFramebufferCache alloc] init]; } return _framebufferCache; } @end