Package glitter :: Package convenience :: Module pipeline
[hide private]
[frames] | no frames]

Source Code for Module glitter.convenience.pipeline

  1  """Pipeline class unifying vertex array, shader and framebuffer. 
  2   
  3  @author: Stephan Wenger 
  4  @date: 2012-03-05 
  5  """ 
  6   
  7  from glitter.framebuffers import Framebuffer 
  8  from glitter.arrays import VertexArray 
  9  from glitter.utils import ItemProxy, InstanceDescriptorMixin, State, StateMixin, add_proxies, PropertyProxy 
 10  from glitter.shaders.uniform import BaseUniform # BaseUniform is an implementation detail of the shaders package 
 11   
12 -class Pipeline(InstanceDescriptorMixin, StateMixin):
13 """Convenience class for rendering pipelines. 14 15 C{Pipeline}s contain a vertex array, a shader, and an optional framebuffer. 16 Property access and method calls are appropriately redirected to these 17 objects. Vertex attributes, shader uniforms, and fragment outputs can be 18 set by their name either in the constructor or by accessing the 19 corresponding properties on the pipeline. 20 21 Vertex attributes can be set to buffer objects or anything that can be 22 converted to an C{ArrayBuffer}. 23 24 Shader uniforms behave as usual. 25 26 Fragment outputs can be set to texture objects. 27 28 @attention: In order to guarantee correct functioning of vertex attribute, 29 uniform and fragment output variables, their names must be unique, must not 30 begin with an underscore, and must not collide with the names of any vertex 31 array or framebuffer methods. 32 33 Usage examples: 34 35 >>> shader = ShaderProgram(...) 36 >>> vertices = [...] 37 >>> colors = [...] 38 >>> elements = [...] 39 >>> texture = Texture2D(...) 40 41 >>> pipeline = Pipeline(shader, in_position=vertices, in_color=colors, elements=elements, out_color=texture) 42 >>> pipeline.clear() 43 >>> pipeline.draw() 44 45 >>> pipeline = Pipeline(shader, use_framebuffer=False) 46 >>> with pipeline(in_position=vertices, in_color=colors, elements=elements): 47 ... pipeline.draw() 48 >>> with pipeline(in_position=vertices, in_color=colors, elements=elements) as p: 49 ... p.draw() 50 >>> pipeline.draw_with(in_position=vertices, in_color=colors, elements=elements) 51 52 >>> pipeline = Pipeline(shader, use_framebuffer=False) 53 >>> vertex_array = VertexArray(...) 54 >>> with pipeline: 55 ... vertex_array.draw() 56 """ 57 58 _frozen = False 59 """Whether setting of unknown attributes should be interpreted literally or as accessing vertex array and framebuffer properties.""" 60
61 - def __init__(self, shader, use_framebuffer=True, **kwargs):
62 """Create a new C{Pipeline}. 63 64 @param shader: The compiled and linked shader program object to use. 65 @type shader: L{ShaderProgram} 66 @param use_framebuffer: If C{True}, render to textures instead of the currently bound framebuffer. 67 @type use_framebuffer: C{bool} 68 @param kwargs: Named arguments are translated to setting of attributes. 69 70 @todo: C{_vao} and C{_fbo} should be created dynamically when (and if) 71 attributes and attachments are accessed, respectively; 72 C{use_framebuffer} should not be necessary then. 73 """ 74 75 self._shader = shader 76 self._context = self._shader._context 77 self._lazy_context_properties = {} 78 self._lazy_context_property_stack = [] 79 80 # add shader uniforms (attributes are handled by the vertex array) 81 for name, proxy in self._shader.__dict__.items(): 82 if isinstance(proxy, BaseUniform): 83 setattr(self, name, PropertyProxy(self._shader, name)) 84 85 # create vertex array 86 self._vao = VertexArray(context=self._context) 87 add_proxies(self, self._vao) 88 89 # create framebuffer 90 if use_framebuffer: 91 self._fbo = Framebuffer(context=self._context) 92 add_proxies(self, self._fbo) 93 else: 94 self._fbo = None 95 96 self._frozen = True 97 98 # translate kwargs to setters on self 99 for key, value in kwargs.items(): 100 setattr(self, key, value)
101
102 - def _has_input(self, name):
103 """Determine whether the shader as an attribute named C{name}.""" 104 return self._shader.has_attribute_location(name)
105
106 - def _add_input(self, name, value=None):
107 """Add a proxy for the attribute named C{name}.""" 108 index = self._shader.get_attribute_location(name) 109 with State(self, _frozen=False): 110 setattr(self, name, ItemProxy(self._vao, index)) 111 setattr(self, name, value)
112
113 - def _has_output(self, name):
114 """Determine whether the shader as a fragment output named C{name}.""" 115 return self._fbo is not None and self._shader.has_frag_data_location(name)
116
117 - def _add_output(self, name, value):
118 """Add a proxy for the fragment output named C{name}.""" 119 index = self._shader.get_frag_data_location(name) 120 with State(self, _frozen=False): 121 setattr(self, name, ItemProxy(self._fbo, index)) 122 setattr(self, name, value)
123
124 - def __setattr__(self, name, value):
125 """Set an attribute. 126 127 When C{name} is a known attribute or starts with an underscore, or when 128 C{self} is not in L{_frozen} state, C{__setattr__} works as usual. 129 130 When C{self} is in L{_frozen} state (default), setting of unknown 131 attributes will check for vertex attributes, fragment outputs and 132 context properties called C{name} and create appropriate proxies or 133 raise an error if no such attribute or output exists. 134 135 Context properties set here will be set on binding and reset on 136 unbinding the pipeline. 137 """ 138 139 if hasattr(self, name) or name.startswith("_") or not self._frozen: 140 super(Pipeline, self).__setattr__(name, value) 141 elif self._has_input(name): 142 self._add_input(name, value) 143 elif self._has_output(name): 144 self._add_output(name, value) 145 elif hasattr(self._context, name): 146 self._lazy_context_properties[name] = value 147 else: 148 raise AttributeError("'%s' object has no attribute '%s'" % (self.__class__.__name__, name))
149
150 - def __delattr__(self, name, value):
151 """Delete an attribute. 152 153 When C{name} is a known attribute or starts with an underscore, or when 154 C{self} is not in L{_frozen} state, C{__delattr__} works as usual. 155 156 Otherwise, if the attribute is a previously set context property, it 157 will be removed from the pipeline. 158 """ 159 160 if hasattr(self, name) or name.startswith("_") or not self._frozen: 161 super(Pipeline, self).__delattr__(name, value) 162 elif name in self._lazy_context_properties: 163 del self._lazy_context_properties[name] 164 else: 165 raise AttributeError("'%s' object has no attribute '%s'" % (self.__class__.__name__, name))
166
167 - def __enter__(self):
168 """Bind framebuffer and shader.""" 169 170 stack_frame = [] 171 for key, value in self._lazy_context_properties.items(): 172 stack_frame.append((key, getattr(self._context, key))) 173 setattr(self._context, key, value) 174 self._lazy_context_property_stack.append(stack_frame) 175 176 if self._fbo is not None: 177 self._fbo.__enter__() 178 self._shader.__enter__() 179 return self
180
181 - def __exit__(self, type, value, traceback):
182 """Unbind framebuffer and shader.""" 183 self._shader.__exit__(type, value, traceback) 184 if self._fbo is not None: 185 self._fbo.__exit__(type, value, traceback) 186 187 for key, value in self._lazy_context_property_stack.pop(): 188 setattr(self._context, key, value)
189
190 - def draw_with(self, *args, **kwargs):
191 """Call L{draw<VertexArray.draw>} on C{self} with attributes from C{kwargs} set. 192 193 Keyword arguments that are known attributes or shader input our output 194 variable names will be set as properties on C{self}. Context properties 195 are set before drawing and reset afterwards. Any other arguments will 196 be passed to L{draw<VertexArray.draw>}. 197 198 @todo: C{kwargs} context properties should override C{_lazy_context_properties}. 199 """ 200 201 def is_valid_property(name): 202 return hasattr(self, name) or self._has_input(name) or self._has_output(name)
203 with self(**{key: kwargs.pop(key) for key, value in kwargs.items() if is_valid_property(key)}): 204 with State(context=self._context, **{key: kwargs.pop(key) for key, value in kwargs.items() if hasattr(self._context, key)}): 205 self.draw(*args, **kwargs)
206 207 __all__ = ["Pipeline"] 208