def __setitem__(self, idxs, data): """Set the contents of the buffer. Currently, only :py:obj:`Ellipsis` is valid as an index. :param idxs: The indices to set. A new buffer will be created if :py:obj:`Ellipsis` is passed. :param data: The new contents of the buffer. If a :py:class:`numpy.ndarray` is passed, it will be used to initialize the buffer. If a :py:class:`numpy.dtype` is passed, the buffer will not be initialized. :type data: :py:class:`numpy.dtype` or :py:class:`numpy.ndarray` .. warning:: |buffer-bind| """ if idxs is Ellipsis: if isinstance(data, dtype): dt = data data = None else: dt = ( data.dtype if product(data.shape) == 1 else dtype((data.dtype, data.shape)) ) try: binding = next(iter(self.active_bindings)) except StopIteration: raise RuntimeError("Buffer data can only be set if it is bound.") GL.glBufferData(binding, dt.itemsize, data, self.usage) self.dtype = dt else: raise NotImplementedError("TODO: Allow changing replacing buffer dtypes.")
def stride(self): """Return the stride of buffer rows (in machine units).""" try: count, *shape = self.dtype.shape except ValueError: return None return self.dtype.base.itemsize * product(shape)
def flush(self): """Ensures changes are visible to GL for rendering. This has two actions depending on how the buffer was mapped. If it was mapped with :py:obj:`GL.GL_MAP_FLUSH_EXPLICIT_BIT` the buffer is flushed using :py:func:`GL.glFlushMappedBufferRange` Otherwise, a memory barrier is issued. This requires ``ARB_shader_image_load_store``. .. warning:: |buffer-bind| """ # FIXME: FLUSH_EXPLICIT_BIT is for flushing sub-ranges (no auto-flush at unmap?), so flush minimal region instead of whole mapped region try: binding = next(iter(self.gl_buffer.active_bindings)) except StopIteration: raise RuntimeError("Buffer mappings can only be flushed if the buffer is bound.") if self.access & GL.GL_MAP_FLUSH_EXPLICIT_BIT: length = self.dtype.itemsize * product(self.shape) GL.glFlushMappedBufferRange(binding, self.offset, length) else: GL.glMemoryBarrier(GL.GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT)
def indices(self): '''The total number of vertex attribute indices taken up by the attribute.''' datatype = getattr(self.datatype, 'base', self.datatype) element_indices = getattr(datatype, 'columns', 1) array_shape = getattr(self.datatype, 'full_shape', (1,)) return element_indices * product(array_shape)
def __getitem__(self, idx): datatype = self.datatype[idx] array_elements = product(getattr(datatype, 'full_shape', (1,))) base_type = getattr(datatype, 'base', datatype) element_indices = getattr(base_type, 'columns', 1) location = self.location + idx * element_indices * array_elements return VAOAttribute(self.vao, location, datatype, self.normalized, self.divisor)
def __getitem__(self, idx): var = super().__getitem__(idx) array_elements = product(getattr(var.datatype, 'full_shape', (1,))) base_type = getattr(var.datatype, 'base', var.datatype) element_indices = getattr(base_type, 'columns', 1) if self.shader_location is None: location = None else: location = self.shader_location + idx * element_indices * array_elements return VertexAttribute(var.name, var.datatype, location, self.normalized)
def components(self): return product(self.dtype.shape)
def __init__(self, parent, *idxs): self.parent = parent if parent.dtype.subdtype is None: # Parent is a record array # Only allow one field for consistency with numpy. if len(idxs) != 1: raise IndexError("Invalid index into record array.") idx = idxs[0] if idx in range(-len(parent.dtype), len(parent.dtype)): idx = idx % len(parent.dtype) else: try: idx = parent.dtype.names.index(idx) except ValueError: raise IndexError("No such field: {}".format(idx)) offset = sum(parent.dtype[i].itemsize for i in range(idx)) self.dtype = parent.dtype.base[idx] else: parent_base, parent_shape = parent.dtype.subdtype if all(isinstance(idx, slice) or isinstance(idx, int) for idx in idxs): # Indexing shape if len(idxs) > len(parent_shape): raise IndexError("Too many indices.") if not isContiguous(idxs, parent_shape): raise IndexError("Non-contiguous indexing is not permitted.") offset = flatOffset(idxs, parent_shape, base=parent_base.itemsize) if any(idx >= s for idx, s in zip(idxs, parent_shape) if not isinstance(idx, slice)): raise IndexError("Index out of bounds.") idxs = chain(idxs, repeat(slice(None))) shape = (len(range(*idx.indices(s))) for idx, s in zip(idxs, parent_shape) if isinstance(idx, slice)) self.dtype = dtype((parent_base, tuple(shape))) else: # Indexing record fields # Multiple indices must be in a list for consistency with numpy if len(idxs) > 1: raise IndexError("Invalid indexes.") if isinstance(idxs[0], str): field_name = first_field = idxs[0] if field_name not in parent_base.names: raise IndexError("No such field: {}".format(field_name)) if len(parent_base) > 1 and product(parent_shape) > 1: raise IndexError("Non-contiguous indexing is not permitted.") field = parent_base[field_name] self.dtype = dtype((field.base, parent_shape + field.shape)) else: field_names = tuple(filter(lambda x: x in parent_base.names, idxs[0])) try: first_field = field_names[0] except IndexError: raise IndexError("No such fields: {}".format(', '.join(field_names))) if product(parent_shape) > 1: if field_names != parent_base.names: raise IndexError("Non-contiguous indexing is not permitted.") else: if not contains(field_names, parent_base.names): raise IndexError("Non-contiguous indexing is not permitted.") base_dtype = dtype([(name, parent_base[name]) for name in field_names]) self.dtype = dtype((base_dtype, parent_shape)) offset = parent_base.fields[first_field][1] self.offset = offset + getattr(parent, 'offset', 0) self.buffer = getattr(parent, 'buffer', parent)