LCOV - code coverage report
Current view: top level - Core/Materials - OOShaderProgram.m (source / functions) Hit Total Coverage
Test: coverxygen.info Lines: 0 1 0.0 %
Date: 2025-05-28 07:50:54 Functions: 0 0 -

          Line data    Source code
       1           0 : /*
       2             : 
       3             : OOShaderProgram.m
       4             : 
       5             : 
       6             : Copyright (C) 2007-2013 Jens Ayton
       7             : 
       8             : Permission is hereby granted, free of charge, to any person obtaining a copy
       9             : of this software and associated documentation files (the "Software"), to deal
      10             : in the Software without restriction, including without limitation the rights
      11             : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
      12             : copies of the Software, and to permit persons to whom the Software is
      13             : furnished to do so, subject to the following conditions:
      14             : 
      15             : The above copyright notice and this permission notice shall be included in all
      16             : copies or substantial portions of the Software.
      17             : 
      18             : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      19             : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      20             : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
      21             : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      22             : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
      23             : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
      24             : SOFTWARE.
      25             : 
      26             : */
      27             : 
      28             : #import "OOOpenGLExtensionManager.h"
      29             : 
      30             : #if OO_SHADERS
      31             : 
      32             : #import "OOShaderProgram.h"
      33             : #import "OOFunctionAttributes.h"
      34             : #import "OOStringParsing.h"
      35             : #import "ResourceManager.h"
      36             : #import "OOOpenGLExtensionManager.h"
      37             : #import "OOMacroOpenGL.h"
      38             : #import "OOCollectionExtractors.h"
      39             : #import "OODebugFlags.h"
      40             : #import "Universe.h"
      41             : #import "MyOpenGLView.h"
      42             : 
      43             : 
      44             : static NSMutableDictionary              *sShaderCache = nil;
      45             : static OOShaderProgram                  *sActiveProgram = nil;
      46             : 
      47             : 
      48             : static BOOL GetShaderSource(NSString *fileName, NSString *shaderType, NSString *prefix, NSString **outResult);
      49             : static NSString *GetGLSLInfoLog(GLhandleARB shaderObject);
      50             : 
      51             : 
      52             : @interface OOShaderProgram (OOPrivate)
      53             : 
      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
      60             :                                                          key:(NSString *)key;
      61             : 
      62             : - (void) bindAttributes:(NSDictionary *)attributeBindings;
      63             : - (void) bindStandardMatrixUniforms;
      64             : 
      65             : @end
      66             : 
      67             : 
      68             : @implementation OOShaderProgram
      69             : 
      70             : + (id) shaderProgramWithVertexShader:(NSString *)vertexShaderSource
      71             :                                           fragmentShader:(NSString *)fragmentShaderSource
      72             :                                         vertexShaderName:(NSString *)vertexShaderName
      73             :                                   fragmentShaderName:(NSString *)fragmentShaderName
      74             :                                                           prefix:(NSString *)prefixString                       // String prepended to program source (both vs and fs)
      75             :                                    attributeBindings:(NSDictionary *)attributeBindings  // Maps vertex attribute names to "locations".
      76             :                                                         cacheKey:(NSString *)cacheKey
      77             : {
      78             :         OOShaderProgram                 *result = nil;
      79             :         
      80             :         if ([prefixString length] == 0)  prefixString = nil;
      81             :         
      82             :         // Use cache to avoid creating duplicate shader programs -- saves on GPU resources and potentially state changes.
      83             :         // FIXME: probably needs to respond to graphics resets.
      84             :         result = [[sShaderCache objectForKey:cacheKey] pointerValue];
      85             :         
      86             :         if (result == nil)
      87             :         {
      88             :                 // No cached program; create one...
      89             :                 result = [[OOShaderProgram alloc] initWithVertexShaderSource:vertexShaderSource
      90             :                                                                                                 fragmentShaderSource:fragmentShaderSource
      91             :                                                                                                                 prefixString:prefixString
      92             :                                                                                                                   vertexName:vertexShaderName
      93             :                                                                                                                 fragmentName:fragmentShaderName
      94             :                                                                                                    attributeBindings:attributeBindings
      95             :                                                                                                                                  key:cacheKey];
      96             :                 [result autorelease];
      97             :                 
      98             :                 if (result != nil && cacheKey != nil)
      99             :                 {
     100             :                         // ...and add it to the cache.
     101             :                         if (sShaderCache == nil)  sShaderCache = [[NSMutableDictionary alloc] init];
     102             :                         [sShaderCache setObject:[NSValue valueWithPointer:result] forKey:cacheKey];     // Use NSValue so dictionary doesn't retain program
     103             :                 }
     104             :         }
     105             :         
     106             :         return result;
     107             : }
     108             : 
     109             : 
     110             : + (id)shaderProgramWithVertexShaderName:(NSString *)vertexShaderName
     111             :                                          fragmentShaderName:(NSString *)fragmentShaderName
     112             :                                                                  prefix:(NSString *)prefixString
     113             :                                           attributeBindings:(NSDictionary *)attributeBindings
     114             : {
     115             :         NSString                                *cacheKey = nil;
     116             :         OOShaderProgram                 *result = nil;
     117             :         NSString                                *vertexSource = nil;
     118             :         NSString                                *fragmentSource = nil;
     119             :         
     120             :         if ([prefixString length] == 0)  prefixString = nil;
     121             :         
     122             :         // Use cache to avoid creating duplicate shader programs -- saves on GPU resources and potentially state changes.
     123             :         // FIXME: probably needs to respond to graphics resets.
     124             :         cacheKey = [NSString stringWithFormat:@"vertex:%@\nfragment:%@\n----\n%@", vertexShaderName, fragmentShaderName, prefixString ?: (NSString *)@""];
     125             :         result = [[sShaderCache objectForKey:cacheKey] pointerValue];
     126             :         
     127             :         if (result == nil)
     128             :         {
     129             :                 // No cached program; create one...
     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
     138             :                                                                                                                                  key:cacheKey];
     139             :                 
     140             :                 if (result != nil)
     141             :                 {
     142             :                         // ...and add it to the cache.
     143             :                         [result autorelease];
     144             :                         if (sShaderCache == nil)  sShaderCache = [[NSMutableDictionary alloc] init];
     145             :                         [sShaderCache setObject:[NSValue valueWithPointer:result] forKey:cacheKey];     // Use NSValue so dictionary doesn't retain program
     146             :                 }
     147             :         }
     148             :         
     149             :         return result;
     150             : }
     151             : 
     152             : 
     153             : - (void)dealloc
     154             : {
     155             :         OO_ENTER_OPENGL();
     156             :         
     157             : #ifndef NDEBUG
     158             :         if (EXPECT_NOT(sActiveProgram == self))
     159             :         {
     160             :                 OOLog(@"shader.dealloc.imbalance", @"%@", @"***** OOShaderProgram deallocated while active, indicating a retain/release imbalance. Expect imminent crash.");
     161             :                 [OOShaderProgram applyNone];
     162             :         }
     163             : #endif
     164             :         
     165             :         if (key != nil)
     166             :         {
     167             :                 [sShaderCache removeObjectForKey:key];
     168             :                 [key release];
     169             :         }
     170             :         
     171             :         if (standardMatrixUniformLocations != nil) {
     172             :                 [standardMatrixUniformLocations release];
     173             :         }
     174             :         
     175             :         OOGL(glDeleteObjectARB(program));
     176             :         
     177             :         [super dealloc];
     178             : }
     179             : 
     180             : 
     181             : - (void)apply
     182             : {
     183             :         OO_ENTER_OPENGL();
     184             :         
     185             :         if (sActiveProgram != self)
     186             :         {
     187             :                 [sActiveProgram release];
     188             :                 sActiveProgram = [self retain];
     189             :                 OOGL(glUseProgramObjectARB(program));
     190             :                 [self bindStandardMatrixUniforms];
     191             :         }
     192             : }
     193             : 
     194             : 
     195             : + (void)applyNone
     196             : {
     197             :         OO_ENTER_OPENGL();
     198             :         
     199             :         if (sActiveProgram != nil)
     200             :         {
     201             :                 [sActiveProgram release];
     202             :                 sActiveProgram = nil;
     203             :                 OOGL(glUseProgramObjectARB(NULL_SHADER));
     204             :         }
     205             : }
     206             : 
     207             : 
     208             : - (GLhandleARB)program
     209             : {
     210             :         return program;
     211             : }
     212             : 
     213             : @end
     214             : 
     215             : 
     216             : static BOOL ValidateShaderObject(GLhandleARB object, NSString *name)
     217             : {
     218             :         GLint           type, subtype = 0, status;
     219             :         GLenum          statusType;
     220             :         NSString        *subtypeString = nil;
     221             :         NSString        *actionString = nil;
     222             :         
     223             :         OO_ENTER_OPENGL();
     224             :         
     225             :         OOGL(glGetObjectParameterivARB(object, GL_OBJECT_TYPE_ARB, &type));
     226             :         BOOL linking = type == GL_PROGRAM_OBJECT_ARB;
     227             :         
     228             :         if (linking)
     229             :         {
     230             :                 subtypeString = @"shader program";
     231             :                 actionString = @"linking";
     232             :                 statusType = GL_OBJECT_LINK_STATUS_ARB;
     233             :         }
     234             :         else
     235             :         {
     236             :                 // FIXME
     237             :                 OOGL(glGetObjectParameterivARB(object, GL_OBJECT_SUBTYPE_ARB, &subtype));
     238             :                 switch (subtype)
     239             :                 {
     240             :                         case GL_VERTEX_SHADER_ARB:
     241             :                                 subtypeString = @"vertex shader";
     242             :                                 break;
     243             :                                 
     244             :                         case GL_FRAGMENT_SHADER_ARB:
     245             :                                 subtypeString = @"fragment shader";
     246             :                                 break;
     247             :                                 
     248             : #if GL_EXT_geometry_shader4
     249             :                         case GL_GEOMETRY_SHADER_EXT:
     250             :                                 subtypeString = @"geometry shader";
     251             :                                 break;
     252             : #endif
     253             :                                 
     254             :                         default:
     255             :                                 subtypeString = [NSString stringWithFormat:@"<unknown shader type 0x%.4X>", subtype];
     256             :                 }
     257             :                 actionString = @"compilation";
     258             :                 statusType = GL_OBJECT_COMPILE_STATUS_ARB;
     259             :         }
     260             :         
     261             :         OOGL(glGetObjectParameterivARB(object, statusType, &status));
     262             :         if (status == GL_FALSE)
     263             :         {
     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));
     266             :                 return NO;
     267             :         }
     268             :         
     269             : #ifndef NDEBUG
     270             :         if (gDebugFlags & DEBUG_SHADER_VALIDATION && 0)
     271             :         {
     272             :                 OOGL(glValidateProgramARB(object));
     273             :                 OOGL(glGetObjectParameterivARB(object, GL_OBJECT_VALIDATE_STATUS_ARB, &status));
     274             :                 if (status == GL_FALSE)
     275             :                 {
     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));
     278             :                         return NO;
     279             :                 }
     280             :         }
     281             : #endif
     282             :         
     283             :         return YES;
     284             : }
     285             : 
     286             : 
     287             : @implementation OOShaderProgram (OOPrivate)
     288             : 
     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
     296             : {
     297             :         BOOL                                    OK = YES;
     298             :         const GLcharARB                 *sourceStrings[3] = { "", "#line 0\n", NULL };
     299             :         GLhandleARB                             vertexShader = NULL_SHADER;
     300             :         GLhandleARB                             fragmentShader = NULL_SHADER;
     301             :         
     302             :         OO_ENTER_OPENGL();
     303             :         
     304             :         self = [super init];
     305             :         if (self == nil)  OK = NO;
     306             :         
     307             :         if (OK && vertexSource == nil && fragmentSource == nil)  OK = NO;       // Must have at least one shader!
     308             :         
     309             :         if (OK && prefixString != nil)
     310             :         {
     311             :                 sourceStrings[0] = [prefixString UTF8String];
     312             :         }
     313             :         
     314             :         if (OK && vertexSource != nil)
     315             :         {
     316             :                 // Compile vertex shader.
     317             :                 OOGL(vertexShader = glCreateShaderObjectARB(GL_VERTEX_SHADER_ARB));
     318             :                 if (vertexShader != NULL_SHADER)
     319             :                 {
     320             :                         sourceStrings[2] = [vertexSource UTF8String];
     321             :                         OOGL(glShaderSourceARB(vertexShader, 3, sourceStrings, NULL));
     322             :                         OOGL(glCompileShaderARB(vertexShader));
     323             :                         
     324             :                         OK = ValidateShaderObject(vertexShader, vertexName);
     325             :                 }
     326             :                 else  OK = NO;
     327             :         }
     328             :         
     329             :         if (OK && fragmentSource != nil)
     330             :         {
     331             :                 // Compile fragment shader.
     332             :                 OOGL(fragmentShader = glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB));
     333             :                 if (fragmentShader != NULL_SHADER)
     334             :                 {
     335             :                         sourceStrings[2] = [fragmentSource UTF8String];
     336             :                         OOGL(glShaderSourceARB(fragmentShader, 3, sourceStrings, NULL));
     337             :                         OOGL(glCompileShaderARB(fragmentShader));
     338             :                         
     339             :                         OK = ValidateShaderObject(fragmentShader, fragmentName);
     340             :                 }
     341             :                 else  OK = NO;
     342             :         }
     343             :         
     344             :         if (OK)
     345             :         {
     346             :                 // Link shader.
     347             :                 OOGL(program = glCreateProgramObjectARB());
     348             :                 if (program != NULL_SHADER)
     349             :                 {
     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));
     354             :                         
     355             :                         OK = ValidateShaderObject(program, [NSString stringWithFormat:@"%@/%@", vertexName, fragmentName]);
     356             :                 }
     357             :                 else  OK = NO;
     358             :         }
     359             :         
     360             :         if (OK)
     361             :         {
     362             :                 key = [inKey copy];
     363             :         }
     364             :         
     365             :         if (vertexShader != NULL_SHADER)  OOGL(glDeleteObjectARB(vertexShader));
     366             :         if (fragmentShader != NULL_SHADER)  OOGL(glDeleteObjectARB(fragmentShader));
     367             :         
     368             :         if (OK)
     369             :         {
     370             :                 OOOpenGLMatrixManager *matrixManager = [[UNIVERSE gameView] getOpenGLMatrixManager];
     371             :                 standardMatrixUniformLocations = [matrixManager standardMatrixUniformLocations: program];
     372             :         }
     373             :         else
     374             :         {
     375             :                 if (self != nil && program != NULL_SHADER)
     376             :                 {
     377             :                         OOGL(glDeleteObjectARB(program));
     378             :                         program = NULL_SHADER;
     379             :                 }
     380             :                 
     381             :                 [self release];
     382             :                 self = nil;
     383             :         }
     384             :         return self;
     385             : }
     386             : 
     387             : 
     388             : - (void) bindAttributes:(NSDictionary *)attributeBindings
     389             : {
     390             :         OO_ENTER_OPENGL();
     391             :         
     392             :         NSString                                *attrKey = nil;
     393             :         NSEnumerator                    *keyEnum = nil;
     394             :         
     395             :         for (keyEnum = [attributeBindings keyEnumerator]; (attrKey = [keyEnum nextObject]); )
     396             :         {
     397             :                 OOGL(glBindAttribLocationARB(program, [attributeBindings oo_unsignedIntForKey:attrKey], [attrKey UTF8String]));
     398             :         }
     399             : }
     400             : 
     401             : - (void) bindStandardMatrixUniforms
     402             : {
     403             :         if (standardMatrixUniformLocations != nil)
     404             :         {
     405             :                 OOOpenGLMatrixManager *matrixManager = [[UNIVERSE gameView] getOpenGLMatrixManager];
     406             :                 NSEnumerator *enumerator = [standardMatrixUniformLocations objectEnumerator];
     407             :                 id obj;
     408             :                 NSArray *pair;
     409             :                 
     410             :                 OO_ENTER_OPENGL();
     411             : 
     412             :                 [matrixManager syncModelView];
     413             :                 while ((obj = [enumerator nextObject]))
     414             :                 {
     415             :                         if ([obj isKindOfClass:[NSArray class]])
     416             :                         {
     417             :                                 pair = (NSArray*)obj;
     418             :                                 if ([[pair oo_stringAtIndex: 2] compare: @"mat3"] == 0)
     419             :                                 {
     420             :                                         OOGL(GLUniformMatrix3([pair oo_intAtIndex: 0], [matrixManager getMatrix: [pair oo_intAtIndex: 1]]));
     421             :                                 }
     422             :                                 else
     423             :                                 {
     424             :                                         GLUniformMatrix([pair oo_intAtIndex: 0], [matrixManager getMatrix: [pair oo_intAtIndex: 1]]);
     425             :                                 }
     426             :                         }
     427             :                 }
     428             :         }
     429             :         return;
     430             : }
     431             : 
     432             : 
     433             : 
     434             : @end
     435             : 
     436             : 
     437             : /*      Attempt to load fragment or vertex shader source from a file.
     438             :         Returns YES if source was loaded or no shader was specified, and NO if an
     439             :         external shader was specified but could not be found.
     440             : */
     441             : static BOOL GetShaderSource(NSString *fileName, NSString *shaderType, NSString *prefix, NSString **outResult)
     442             : {
     443             :         NSString                                *result = nil;
     444             :         NSArray                                 *extensions = nil;
     445             :         NSEnumerator                    *extEnum = nil;
     446             :         NSString                                *extension = nil;
     447             :         NSString                                *nameWithExtension = nil;
     448             :         
     449             :         if (fileName == nil)  return YES;       // It's OK for one or the other of the shaders to be undefined.
     450             :         
     451             :         result = [ResourceManager stringFromFilesNamed:fileName inFolder:@"Shaders"];
     452             :         if (result == nil)
     453             :         {
     454             :                 extensions = [NSArray arrayWithObjects:shaderType, [shaderType substringToIndex:4], nil];       // vertex and vert, or fragment and frag
     455             :                 
     456             :                 // Futureproofing -- in future, we may wish to support automatic selection between supported shader languages.
     457             :                 if (![fileName pathHasExtensionInArray:extensions])
     458             :                 {
     459             :                         for (extEnum = [extensions objectEnumerator]; (extension = [extEnum nextObject]); )
     460             :                         {
     461             :                                 nameWithExtension = [fileName stringByAppendingPathExtension:extension];
     462             :                                 result = [ResourceManager stringFromFilesNamed:nameWithExtension
     463             :                                                                                                           inFolder:@"Shaders"];
     464             :                                 if (result != nil) break;
     465             :                         }
     466             :                 }
     467             :                 if (result == nil)
     468             :                 {
     469             :                         OOLog(kOOLogFileNotFound, @"GLSL ERROR: failed to find fragment program %@.", fileName);
     470             :                         return NO;
     471             :                 }
     472             :         }
     473             :         /*      
     474             :         if (result != nil && prefix != nil)
     475             :         {
     476             :                 result = [prefix stringByAppendingString:result];
     477             :         }
     478             :         */
     479             :         if (outResult != NULL) *outResult = result;
     480             :         return YES;
     481             : }
     482             : 
     483             : 
     484             : static NSString *GetGLSLInfoLog(GLhandleARB shaderObject)
     485             : {
     486             :         GLint                                   length;
     487             :         GLcharARB                               *log = NULL;
     488             :         NSString                                *result = nil;
     489             :         
     490             :         OO_ENTER_OPENGL();
     491             :         
     492             :         if (EXPECT_NOT(shaderObject == NULL_SHADER))  return nil;
     493             :         
     494             :         OOGL(glGetObjectParameterivARB(shaderObject, GL_OBJECT_INFO_LOG_LENGTH_ARB, &length));
     495             :         log = malloc(length);
     496             :         if (log == NULL)
     497             :         {
     498             :                 length = 1024;
     499             :                 log = malloc(length);
     500             :                 if (log == NULL)  return @"<out of memory>";
     501             :         }
     502             :         OOGL(glGetInfoLogARB(shaderObject, length, NULL, log));
     503             :         
     504             :         result = [NSString stringWithUTF8String:log];
     505             :         if (result == nil)  result = [[[NSString alloc] initWithBytes:log length:length - 1 encoding:NSISOLatin1StringEncoding] autorelease];
     506             :         free(log);
     507             :         
     508             :         return result;
     509             : }
     510             : 
     511             : #endif // OO_SHADERS

Generated by: LCOV version 1.14