File: shaders.py

package info (click to toggle)
pyopengl 3.1.6%2Bdfsg-3
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 14,732 kB
  • sloc: python: 106,016; makefile: 8
file content (250 lines) | stat: -rw-r--r-- 8,987 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
"""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',
    'ShaderCompilationError', 
    'ShaderValidationError', 
    'ShaderLinkError',
    # automatically added stuff here...
]

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"""
    validated = False
    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 ShaderValidationError on failures
        """
        glValidateProgram( self )
        validation = glGetProgramiv( self, GL_VALIDATE_STATUS )
        if validation == GL_FALSE:
            raise ShaderValidationError(
                """Validation failure (%r): %s"""%(
                validation,
                glGetProgramInfoLog( self ),
            ))
        self.validated = True
        return self

    def check_linked( self ):
        """Check link status for this program
        
        raises ShaderLinkError on failures
        """
        link_status = glGetProgramiv( self, GL_LINK_STATUS )
        if link_status == GL_FALSE:
            raise ShaderLinkError(
                """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, validate=True ):
        """Attempt to load binary-format for a pre-compiled shader
        
        See notes in retrieve
        """
        get_program_binary.glProgramBinary( self, format, binary, len(binary))
        if validate:
            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)
    validate (keyword only) -- if False, suppress automatic 
        validation against current GL state. In advanced usage 
        the validation can produce spurious errors. Note: this 
        function is *not* really intended for advanced usage,
        if you're finding yourself specifying this flag you 
        likely should be using your own shader management code.

    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 subclasses {
        ShaderCompilationError, ShaderValidationError, ShaderLinkError,
    } 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)
    if named.get('validate', True):
        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):
        # TODO: this will be wrong if the user has
        # disabled traditional unpacking array support.
        raise ShaderCompilationError(
            """Shader compile failure (%s): %s"""%(
                result,
                glGetShaderInfoLog( shader ),
            ),
            source,
            shaderType,
        )
    return shader

class ShaderCompilationError(RuntimeError):
    """Raised when a shader compilation fails"""
class ShaderValidationError(RuntimeError):
    """Raised when a program fails to validate"""
class ShaderLinkError(RuntimeError):
    """Raised when a shader link fails"""