41#import "MyOpenGLView.h"
44static NSMutableDictionary *sShaderCache =
nil;
45static OOShaderProgram *sActiveProgram =
nil;
48static BOOL
GetShaderSource(NSString *fileName, NSString *shaderType, NSString *prefix, NSString **outResult);
49static NSString *GetGLSLInfoLog(GLhandleARB shaderObject);
52@interface OOShaderProgram (OOPrivate)
54- (id)initWithVertexShaderSource:(NSString *)vertexSource
55 fragmentShaderSource:(NSString *)fragmentSource
56 prefixString:(NSString *)prefixString
57 vertexName:(NSString *)vertexName
58 fragmentName:(NSString *)fragmentName
59 attributeBindings:(NSDictionary *)attributeBindings
62- (void) bindAttributes:(NSDictionary *)attributeBindings;
63- (void) bindStandardMatrixUniforms;
68@implementation OOShaderProgram
70+ (id) shaderProgramWithVertexShader:(NSString *)vertexShaderSource
71 fragmentShader:(NSString *)fragmentShaderSource
72 vertexShaderName:(NSString *)vertexShaderName
73 fragmentShaderName:(NSString *)fragmentShaderName
74 prefix:(NSString *)prefixString
75 attributeBindings:(NSDictionary *)attributeBindings
76 cacheKey:(NSString *)cacheKey
78 OOShaderProgram *result =
nil;
80 if ([prefixString length] == 0) prefixString =
nil;
84 result = [[sShaderCache objectForKey:cacheKey] pointerValue];
89 result = [[OOShaderProgram alloc] initWithVertexShaderSource:vertexShaderSource
90 fragmentShaderSource:fragmentShaderSource
91 prefixString:prefixString
92 vertexName:vertexShaderName
93 fragmentName:fragmentShaderName
94 attributeBindings:attributeBindings
98 if (result !=
nil && cacheKey !=
nil)
101 if (sShaderCache ==
nil) sShaderCache = [[NSMutableDictionary alloc] init];
102 [sShaderCache setObject:[NSValue valueWithPointer:result] forKey:cacheKey];
110+ (id)shaderProgramWithVertexShaderName:(NSString *)vertexShaderName
111 fragmentShaderName:(NSString *)fragmentShaderName
112 prefix:(NSString *)prefixString
113 attributeBindings:(NSDictionary *)attributeBindings
115 NSString *cacheKey =
nil;
116 OOShaderProgram *result =
nil;
117 NSString *vertexSource =
nil;
118 NSString *fragmentSource =
nil;
120 if ([prefixString length] == 0) prefixString =
nil;
124 cacheKey = [NSString stringWithFormat:@"vertex:%@\nfragment:%@\n----\n%@", vertexShaderName, fragmentShaderName, prefixString ?: (NSString *)@""];
125 result = [[sShaderCache objectForKey:cacheKey] pointerValue];
130 if (!
GetShaderSource(vertexShaderName,
@"vertex", prefixString, &vertexSource))
return nil;
131 if (!
GetShaderSource(fragmentShaderName,
@"fragment", prefixString, &fragmentSource))
return nil;
132 result = [[OOShaderProgram alloc] initWithVertexShaderSource:vertexSource
133 fragmentShaderSource:fragmentSource
134 prefixString:prefixString
135 vertexName:vertexShaderName
136 fragmentName:fragmentShaderName
137 attributeBindings:attributeBindings
143 [result autorelease];
144 if (sShaderCache ==
nil) sShaderCache = [[NSMutableDictionary alloc] init];
145 [sShaderCache setObject:[NSValue valueWithPointer:result] forKey:cacheKey];
160 OOLog(
@"shader.dealloc.imbalance",
@"%@",
@"***** OOShaderProgram deallocated while active, indicating a retain/release imbalance. Expect imminent crash.");
161 [OOShaderProgram applyNone];
167 [sShaderCache removeObjectForKey:key];
171 if (standardMatrixUniformLocations !=
nil) {
172 [standardMatrixUniformLocations release];
175 OOGL(glDeleteObjectARB(program));
185 if (sActiveProgram !=
self)
187 [sActiveProgram release];
188 sActiveProgram = [
self retain];
189 OOGL(glUseProgramObjectARB(program));
190 [
self bindStandardMatrixUniforms];
199 if (sActiveProgram !=
nil)
201 [sActiveProgram release];
202 sActiveProgram =
nil;
208- (GLhandleARB)program
216static BOOL ValidateShaderObject(GLhandleARB
object, NSString *name)
218 GLint type, subtype = 0, status;
220 NSString *subtypeString =
nil;
221 NSString *actionString =
nil;
225 OOGL(glGetObjectParameterivARB(
object, GL_OBJECT_TYPE_ARB, &type));
226 BOOL linking = type == GL_PROGRAM_OBJECT_ARB;
230 subtypeString =
@"shader program";
231 actionString =
@"linking";
232 statusType = GL_OBJECT_LINK_STATUS_ARB;
237 OOGL(glGetObjectParameterivARB(
object, GL_OBJECT_SUBTYPE_ARB, &subtype));
240 case GL_VERTEX_SHADER_ARB:
241 subtypeString =
@"vertex shader";
244 case GL_FRAGMENT_SHADER_ARB:
245 subtypeString =
@"fragment shader";
248#if GL_EXT_geometry_shader4
249 case GL_GEOMETRY_SHADER_EXT:
250 subtypeString =
@"geometry shader";
255 subtypeString = [NSString stringWithFormat:@"<unknown shader type 0x%.4X>", subtype];
257 actionString =
@"compilation";
258 statusType = GL_OBJECT_COMPILE_STATUS_ARB;
261 OOGL(glGetObjectParameterivARB(
object, statusType, &status));
262 if (status == GL_FALSE)
264 NSString *msgClass = [NSString stringWithFormat:@"shader.%@.failure", linking ? @"link" : @"compile"];
265 OOLogERR(msgClass,
@"GLSL %@ %@ failed for %@:\n>>>>> GLSL log:\n%@\n", subtypeString, actionString, name, GetGLSLInfoLog(
object));
272 OOGL(glValidateProgramARB(
object));
273 OOGL(glGetObjectParameterivARB(
object, GL_OBJECT_VALIDATE_STATUS_ARB, &status));
274 if (status == GL_FALSE)
276 NSString *msgClass = [NSString stringWithFormat:@"shader.%@.validationFailure", linking ? @"link" : @"compile"];
277 OOLogWARN(msgClass,
@"GLSL %@ %@ failed for %@:\n>>>>> GLSL log:\n%@\n", subtypeString,
@"validation", name, GetGLSLInfoLog(
object));
287@implementation OOShaderProgram (OOPrivate)
289- (id)initWithVertexShaderSource:(NSString *)vertexSource
290 fragmentShaderSource:(NSString *)fragmentSource
291 prefixString:(NSString *)prefixString
292 vertexName:(NSString *)vertexName
293 fragmentName:(NSString *)fragmentName
294 attributeBindings:(NSDictionary *)attributeBindings
295 key:(NSString *)inKey
298 const GLcharARB *sourceStrings[3] = {
"",
"#line 0\n", NULL };
305 if (
self ==
nil) OK = NO;
307 if (OK && vertexSource ==
nil && fragmentSource ==
nil) OK = NO;
309 if (OK && prefixString !=
nil)
311 sourceStrings[0] = [prefixString UTF8String];
314 if (OK && vertexSource !=
nil)
317 OOGL(vertexShader = glCreateShaderObjectARB(GL_VERTEX_SHADER_ARB));
320 sourceStrings[2] = [vertexSource UTF8String];
321 OOGL(glShaderSourceARB(vertexShader, 3, sourceStrings, NULL));
322 OOGL(glCompileShaderARB(vertexShader));
324 OK = ValidateShaderObject(vertexShader, vertexName);
329 if (OK && fragmentSource !=
nil)
332 OOGL(fragmentShader = glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB));
335 sourceStrings[2] = [fragmentSource UTF8String];
336 OOGL(glShaderSourceARB(fragmentShader, 3, sourceStrings, NULL));
337 OOGL(glCompileShaderARB(fragmentShader));
339 OK = ValidateShaderObject(fragmentShader, fragmentName);
347 OOGL(program = glCreateProgramObjectARB());
350 if (vertexShader !=
NULL_SHADER)
OOGL(glAttachObjectARB(program, vertexShader));
351 if (fragmentShader !=
NULL_SHADER)
OOGL(glAttachObjectARB(program, fragmentShader));
352 [
self bindAttributes:attributeBindings];
353 OOGL(glLinkProgramARB(program));
355 OK = ValidateShaderObject(program, [NSString stringWithFormat:
@"%@/%@", vertexName, fragmentName]);
365 if (vertexShader !=
NULL_SHADER)
OOGL(glDeleteObjectARB(vertexShader));
366 if (fragmentShader !=
NULL_SHADER)
OOGL(glDeleteObjectARB(fragmentShader));
377 OOGL(glDeleteObjectARB(program));
388- (void) bindAttributes:(NSDictionary *)attributeBindings
392 NSString *attrKey =
nil;
393 NSEnumerator *keyEnum =
nil;
395 for (keyEnum = [attributeBindings keyEnumerator]; (attrKey = [keyEnum nextObject]); )
397 OOGL(glBindAttribLocationARB(program, [attributeBindings oo_unsignedIntForKey:attrKey], [attrKey UTF8String]));
401- (void) bindStandardMatrixUniforms
403 if (standardMatrixUniformLocations !=
nil)
406 NSEnumerator *enumerator = [standardMatrixUniformLocations objectEnumerator];
413 while ((obj = [enumerator nextObject]))
415 if ([obj isKindOfClass:[NSArray
class]])
417 pair = (NSArray*)obj;
418 if ([[pair oo_stringAtIndex: 2] compare:
@"mat3"] == 0)
420 OOGL(
GLUniformMatrix3([pair oo_intAtIndex: 0], [matrixManager getMatrix: [pair oo_intAtIndex: 1]]));
424 GLUniformMatrix([pair oo_intAtIndex: 0], [matrixManager getMatrix: [pair oo_intAtIndex: 1]]);
441static BOOL
GetShaderSource(NSString *fileName, NSString *shaderType, NSString *prefix, NSString **outResult)
443 NSString *result =
nil;
444 NSArray *extensions =
nil;
445 NSEnumerator *extEnum =
nil;
446 NSString *extension =
nil;
447 NSString *nameWithExtension =
nil;
449 if (fileName ==
nil)
return YES;
454 extensions = [NSArray arrayWithObjects:shaderType, [shaderType substringToIndex:4], nil];
457 if (![fileName pathHasExtensionInArray:extensions])
459 for (extEnum = [extensions objectEnumerator]; (extension = [extEnum nextObject]); )
461 nameWithExtension = [fileName stringByAppendingPathExtension:extension];
464 if (result !=
nil)
break;
479 if (outResult != NULL) *outResult = result;
484static NSString *GetGLSLInfoLog(GLhandleARB shaderObject)
487 GLcharARB *log = NULL;
488 NSString *result =
nil;
494 OOGL(glGetObjectParameterivARB(shaderObject, GL_OBJECT_INFO_LOG_LENGTH_ARB, &length));
495 log = malloc(length);
499 log = malloc(length);
500 if (log == NULL)
return @"<out of memory>";
502 OOGL(glGetInfoLogARB(shaderObject, length, NULL, log));
504 result = [NSString stringWithUTF8String:log];
505 if (result ==
nil) result = [[[NSString alloc] initWithBytes:log length:length - 1 encoding:NSISOLatin1StringEncoding] autorelease];
@ DEBUG_SHADER_VALIDATION
#define OOLogWARN(class, format,...)
#define OOLogERR(class, format,...)
#define OOLog(class, format,...)
NSString *const kOOLogFileNotFound
#define OO_ENTER_OPENGL()
void GLUniformMatrix3(int location, OOMatrix M)
static BOOL GetShaderSource(NSString *fileName, NSString *shaderType, NSString *prefix, NSString **outResult)
NSArray * standardMatrixUniformLocations:(GLhandleARB program)
NSString * stringFromFilesNamed:inFolder:(NSString *fileName,[inFolder] NSString *folderName)