1 """Shader program class.
2
3 @todo: wrap C{glGetProgramStage}, C{glGetActiveSubroutineName},
4 C{glGetActiveSubroutineUniform}, C{glGetActiveSubroutineUniformName},
5 C{glGetSubroutineIndex}, C{glGetSubroutineUniformLocation},
6 C{glGetTransformFeedbackVarying}, C{glTransformFeedbackVarying},
7 C{glGetUniformBlockIndex}, C{glGetUniformIndices}, C{glGetActiveUniformBlock},
8 C{glGetActiveUniformBlockName}, C{glUniformBlockBinding}, and
9 C{glUniformSubroutine}.
10
11 @author: Stephan Wenger
12 @date: 2012-02-29
13 """
14
15 from collections import OrderedDict as _odict
16 import re as _re
17
18 import glitter.raw as _gl
19 from glitter.utils import transform_feedback_buffer_modes, int32, ShaderDatatype, ManagedObject, BindableObject, ShaderLinkError, ShaderValidateError, ListProxy, InstanceDescriptorMixin, Proxy
20 from glitter.shaders.shader import Shader, VertexShader, TesselationControlShader, TesselationEvaluationShader, GeometryShader, FragmentShader
21 from glitter.shaders.attribute import Attribute, AttributeStruct, AttributeStructArray
22 from glitter.shaders.uniform import Uniform, UniformStruct, UniformStructArray
25 - def __init__(self, _id, arg, enum=None):
27
28 -class ShaderProgram(ManagedObject, BindableObject, InstanceDescriptorMixin):
29 _generate_id = _gl.glCreateProgram
30 _delete_id = _gl.glDeleteProgram
31 _db = "shader_programs"
32 _binding = "current_program"
33
34 transform_feedback_buffer_modes = transform_feedback_buffer_modes
35 _frozen = False
36
37 - def __init__(self, shaders=[], vertex=[], tess_control=[], tess_evaluation=[], geometry=[], fragment=[], link=None, context=None, **variables):
38 super(ShaderProgram, self).__init__(context=context)
39 self._shaders = []
40 self._variable_proxies = []
41
42 self._delete_status = ProgramProxy(self._id, _gl.GL_DELETE_STATUS)
43 self._link_status = ProgramProxy(self._id, _gl.GL_LINK_STATUS)
44 self._validate_status = ProgramProxy(self._id, _gl.GL_VALIDATE_STATUS)
45 self._info_log_length = ProgramProxy(self._id, _gl.GL_INFO_LOG_LENGTH)
46 self._attached_shaders = ProgramProxy(self._id, _gl.GL_ATTACHED_SHADERS)
47 self._active_attributes = ProgramProxy(self._id, _gl.GL_ACTIVE_ATTRIBUTES)
48 self._active_attribute_max_length = ProgramProxy(self._id, _gl.GL_ACTIVE_ATTRIBUTE_MAX_LENGTH)
49 self._active_uniforms = ProgramProxy(self._id, _gl.GL_ACTIVE_UNIFORMS)
50 self._active_uniform_max_length = ProgramProxy(self._id, _gl.GL_ACTIVE_UNIFORM_MAX_LENGTH)
51 self._program_binary_length = ProgramProxy(self._id, _gl.GL_PROGRAM_BINARY_LENGTH)
52 self._transform_feedback_buffer_mode = ProgramProxy(self._id, _gl.GL_TRANSFORM_FEEDBACK_BUFFER_MODE, self.transform_feedback_buffer_modes)
53 self._transform_feedback_varyings = ProgramProxy(self._id, _gl.GL_TRANSFORM_FEEDBACK_VARYINGS)
54 self._transform_feedback_varying_max_length = ProgramProxy(self._id, _gl.GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH)
55
56 shaders = list(shaders) if hasattr(shaders, "__iter__") else [shaders]
57 if not all(isinstance(x, Shader) for x in shaders):
58 raise TypeError("expected Shader instance")
59 shaders += [x if isinstance(x, VertexShader) else VertexShader(x, context=context)
60 for x in (vertex if hasattr(vertex, "__iter__") else [vertex])]
61 shaders += [x if isinstance(x, TesselationControlShader) else TesselationControlShader(x, context=context)
62 for x in (tess_control if hasattr(tess_control, "__iter__") else [tess_control])]
63 shaders += [x if isinstance(x, TesselationEvaluationShader) else TesselationEvaluationShader(x, context=context)
64 for x in (tess_evaluation if hasattr(tess_evaluation, "__iter__") else [tess_evaluation])]
65 shaders += [x if isinstance(x, GeometryShader) else GeometryShader(x, context=context)
66 for x in (geometry if hasattr(geometry, "__iter__") else [geometry])]
67 shaders += [x if isinstance(x, FragmentShader) else FragmentShader(x, context=context)
68 for x in (fragment if hasattr(fragment, "__iter__") else [fragment])]
69 self.shaders.extend(shaders)
70
71 if link is None:
72 link = bool(shaders)
73 if link:
74 self.link()
75
76 for name, proxy in self._get_active_attributes().items():
77 setattr(self, name, proxy)
78 self._variable_proxies.append(proxy)
79 for name, proxy in self._get_active_uniforms().items():
80 setattr(self, name, proxy)
81 self._variable_proxies.append(proxy)
82
83 self._frozen = True
84
85 for key, value in variables.items():
86 setattr(self, key, value)
87
92
96
100
104
108
109 - def _get_texture_unit(self, name):
110 candidates = [x for x in self._variable_proxies if x.name == name]
111 if candidates:
112 return candidates[0].__get__(self, get_texture_unit_instead_of_object=True)
113 else:
114 return None
115
116 - def _get_active_X(self, getter, location_getter, max_length, index):
117 _size = _gl.GLint()
118 _type = _gl.GLenum()
119 _name = _gl.create_string_buffer(max_length)
120 with self._context:
121 getter(self._id, index, max_length, None, _gl.pointer(_size), _gl.pointer(_type), _name)
122 location = location_getter(self._id, _name)
123 return location, _size.value, ShaderDatatype._from_gl(_type.value), _name.value
124
128
132
136
140
141 - def _group_structs(self, lst, make_variable, make_struct, coerce_array):
142 array_of_structs_re = _re.compile("^([a-zA-Z_][a-zA-Z_0-9]*)\[([0-9]+)\]\.([a-zA-Z_][a-zA-Z_0-9]*)$")
143 struct_re = _re.compile("^([a-zA-Z_][a-zA-Z_0-9]*)\.([a-zA-Z_][a-zA-Z_0-9]*)$")
144 basic_re = _re.compile("^([a-zA-Z_][a-zA-Z_0-9]*)$")
145 names = _odict()
146 for location, size, dtype, name in lst:
147 m = array_of_structs_re.match(name)
148 if m is not None:
149 name, index, field = m.groups()
150 index = int(index)
151 array = names.setdefault(name, coerce_array(name, parent=self))
152 array[index, field] = make_variable(field, location, dtype, size, parent=self)
153 continue
154 m = struct_re.match(name)
155 if m is not None:
156 name, field = m.groups()
157 struct = names.setdefault(name, make_struct(name, parent=self))
158 struct[field] = make_variable(field, location, dtype, size, parent=self)
159 continue
160 m = basic_re.match(name)
161 if m is not None:
162 name, = m.groups()
163 names[name] = make_variable(name, location, dtype, size, parent=self)
164 continue
165 raise NameError("shader variable '%s' could not be parsed" % name)
166 return names
167
168 @property
170 """The list of currently attached shaders.
171
172 @rtype: L{ListProxy}
173 """
174
175 return ListProxy(self._shaders, self._attach, self._detach)
176
191
200
204
206 with self._context:
207 loc = _gl.glGetAttribLocation(self._id, name)
208 if loc == -1:
209 raise NameError("shader has no attribute '%s'" % name)
210 return loc
211
215
217 with self._context:
218 loc = _gl.glGetFragDataLocation(self._id, name)
219 if loc == -1:
220 raise NameError("shader has no frag data '%s'" % name)
221 return loc
222
223 @property
225 """The current shader info log.
226
227 @rtype: C{string}
228 """
229
230 _info_log = _gl.create_string_buffer(self._info_log_length)
231 with self._context:
232 _gl.glGetProgramInfoLog(self._id, self._info_log_length, _gl.POINTER(_gl.GLint)(), _info_log)
233 return _info_log.value
234
235 __all__ = ["ShaderProgram"]
236