| | """Convenience module providing common shader entry points |
| | |
| | The point of this module is to allow client code to use |
| | OpenGL Core names to reference shader-related operations |
| | even if the local hardware only supports ARB extension-based |
| | shader rendering. |
| | |
| | There are also two utility methods compileProgram and compileShader |
| | which make it easy to create demos which are shader-using. |
| | """ |
| | import logging |
| | log = logging.getLogger( __name__ ) |
| | from OpenGL import GL |
| | from OpenGL.GL.ARB import ( |
| | shader_objects, fragment_shader, vertex_shader, vertex_program, |
| | geometry_shader4, separate_shader_objects, get_program_binary, |
| | ) |
| | from OpenGL.extensions import alternate |
| | from OpenGL._bytes import bytes,unicode,as_8_bit |
| |
|
| | __all__ = [ |
| | 'glAttachShader', |
| | 'glDeleteShader', |
| | 'glGetProgramInfoLog', |
| | 'glGetShaderInfoLog', |
| | 'glGetProgramiv', |
| | 'glGetShaderiv', |
| | 'compileProgram', |
| | 'compileShader', |
| | 'GL_VALIDATE_STATUS', |
| | 'GL_LINK_STATUS', |
| | |
| | ] |
| |
|
| | def _alt( base, name ): |
| | if hasattr( GL, base ): |
| | root = getattr( GL, base ) |
| | if hasattr(root,'__call__'): |
| | globals()[base] = alternate( |
| | getattr(GL,base), |
| | getattr(module,name) |
| | ) |
| | __all__.append( base ) |
| | else: |
| | globals()[base] = root |
| | __all__.append( base ) |
| | return True |
| | return False |
| | _excludes = ['glGetProgramiv'] |
| | for module in ( |
| | shader_objects,fragment_shader,vertex_shader,vertex_program, |
| | geometry_shader4, |
| | ): |
| | for name in dir(module): |
| | found = None |
| | for suffix in ('ObjectARB','_ARB','ARB'): |
| | if name.endswith( suffix ): |
| | found = False |
| | base = name[:-(len(suffix))] |
| | if base not in _excludes: |
| | if _alt( base, name ): |
| | found = True |
| | break |
| | if found is False: |
| | log.debug( '''Found no alternate for: %s.%s''', |
| | module.__name__,name, |
| | ) |
| |
|
| | glAttachShader = alternate( GL.glAttachShader,shader_objects.glAttachObjectARB ) |
| | glDetachShader = alternate( GL.glDetachShader,shader_objects.glDetachObjectARB ) |
| | glDeleteShader = alternate( GL.glDeleteShader,shader_objects.glDeleteObjectARB ) |
| | glGetAttachedShaders = alternate( GL.glGetAttachedShaders, shader_objects.glGetAttachedObjectsARB ) |
| |
|
| | glGetProgramInfoLog = alternate( GL.glGetProgramInfoLog, shader_objects.glGetInfoLogARB ) |
| | glGetShaderInfoLog = alternate( GL.glGetShaderInfoLog, shader_objects.glGetInfoLogARB ) |
| |
|
| | glGetShaderiv = alternate( GL.glGetShaderiv, shader_objects.glGetObjectParameterivARB ) |
| | glGetProgramiv = alternate( GL.glGetProgramiv, shader_objects.glGetObjectParameterivARB ) |
| |
|
| | GL_VALIDATE_STATUS = GL.GL_VALIDATE_STATUS |
| | GL_COMPILE_STATUS = GL.GL_COMPILE_STATUS |
| | GL_LINK_STATUS = GL.GL_LINK_STATUS |
| | GL_FALSE = GL.GL_FALSE |
| | GL_TRUE = GL.GL_TRUE |
| |
|
| | class ShaderProgram( int ): |
| | """Integer sub-class with context-manager operation""" |
| | def __enter__( self ): |
| | """Start use of the program""" |
| | glUseProgram( self ) |
| | def __exit__( self, typ, val, tb ): |
| | """Stop use of the program""" |
| | glUseProgram( 0 ) |
| | |
| | def check_validate( self ): |
| | """Check that the program validates |
| | |
| | Validation has to occur *after* linking/loading |
| | |
| | raises RuntimeError on failures |
| | """ |
| | glValidateProgram( self ) |
| | validation = glGetProgramiv( self, GL_VALIDATE_STATUS ) |
| | if validation == GL_FALSE: |
| | raise RuntimeError( |
| | """Validation failure (%r): %s"""%( |
| | validation, |
| | glGetProgramInfoLog( self ), |
| | )) |
| | return self |
| |
|
| | def check_linked( self ): |
| | """Check link status for this program |
| | |
| | raises RuntimeError on failures |
| | """ |
| | link_status = glGetProgramiv( self, GL_LINK_STATUS ) |
| | if link_status == GL_FALSE: |
| | raise RuntimeError( |
| | """Link failure (%s): %s"""%( |
| | link_status, |
| | glGetProgramInfoLog( self ), |
| | )) |
| | return self |
| |
|
| | def retrieve( self ): |
| | """Attempt to retrieve binary for this compiled shader |
| | |
| | Note that binaries for a program are *not* generally portable, |
| | they should be used solely for caching compiled programs for |
| | local use; i.e. to reduce compilation overhead. |
| | |
| | returns (format,binaryData) for the shader program |
| | """ |
| | from OpenGL.raw.GL._types import GLint,GLenum |
| | from OpenGL.arrays import GLbyteArray |
| | size = GLint() |
| | glGetProgramiv( self, get_program_binary.GL_PROGRAM_BINARY_LENGTH, size ) |
| | result = GLbyteArray.zeros( (size.value,)) |
| | size2 = GLint() |
| | format = GLenum() |
| | get_program_binary.glGetProgramBinary( self, size.value, size2, format, result ) |
| | return format.value, result |
| | def load( self, format, binary ): |
| | """Attempt to load binary-format for a pre-compiled shader |
| | |
| | See notes in retrieve |
| | """ |
| | get_program_binary.glProgramBinary( self, format, binary, len(binary)) |
| | self.check_validate() |
| | self.check_linked() |
| | return self |
| |
|
| | def compileProgram(*shaders, **named): |
| | """Create a new program, attach shaders and validate |
| | |
| | shaders -- arbitrary number of shaders to attach to the |
| | generated program. |
| | separable (keyword only) -- set the separable flag to allow |
| | for partial installation of shader into the pipeline (see |
| | glUseProgramStages) |
| | retrievable (keyword only) -- set the retrievable flag to |
| | allow retrieval of the program binary representation, (see |
| | glProgramBinary, glGetProgramBinary) |
| | |
| | This convenience function is *not* standard OpenGL, |
| | but it does wind up being fairly useful for demos |
| | and the like. You may wish to copy it to your code |
| | base to guard against PyOpenGL changes. |
| | |
| | Usage: |
| | |
| | shader = compileProgram( |
| | compileShader( source, GL_VERTEX_SHADER ), |
| | compileShader( source2, GL_FRAGMENT_SHADER ), |
| | ) |
| | glUseProgram( shader ) |
| | |
| | Note: |
| | If (and only if) validation of the linked program |
| | *passes* then the passed-in shader objects will be |
| | deleted from the GL. |
| | |
| | returns ShaderProgram() (GLuint) program reference |
| | raises RuntimeError when a link/validation failure occurs |
| | """ |
| | program = glCreateProgram() |
| | if named.get('separable'): |
| | glProgramParameteri( program, separate_shader_objects.GL_PROGRAM_SEPARABLE, GL_TRUE ) |
| | if named.get('retrievable'): |
| | glProgramParameteri( program, get_program_binary.GL_PROGRAM_BINARY_RETRIEVABLE_HINT, GL_TRUE ) |
| | for shader in shaders: |
| | glAttachShader(program, shader) |
| | program = ShaderProgram( program ) |
| | glLinkProgram(program) |
| | program.check_validate() |
| | program.check_linked() |
| | for shader in shaders: |
| | glDeleteShader(shader) |
| | return program |
| | def compileShader( source, shaderType ): |
| | """Compile shader source of given type |
| | |
| | source -- GLSL source-code for the shader |
| | shaderType -- GLenum GL_VERTEX_SHADER, GL_FRAGMENT_SHADER, etc, |
| | |
| | returns GLuint compiled shader reference |
| | raises RuntimeError when a compilation failure occurs |
| | """ |
| | if isinstance( source, (bytes,unicode)): |
| | source = [ source ] |
| | source = [ as_8_bit(s) for s in source ] |
| | shader = glCreateShader(shaderType) |
| | glShaderSource( shader, source ) |
| | glCompileShader( shader ) |
| | result = glGetShaderiv( shader, GL_COMPILE_STATUS ) |
| | if not(result): |
| | |
| | |
| | raise RuntimeError( |
| | """Shader compile failure (%s): %s"""%( |
| | result, |
| | glGetShaderInfoLog( shader ), |
| | ), |
| | source, |
| | shaderType, |
| | ) |
| | return shader |
| |
|