Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,7 @@ Features:

- Load textures into OpenGL.
- Bind and unbind textures for rendering.
- Update a texture with an equivalent shaped image.
- Delete textures when no longer needed.

Example:
Expand All @@ -270,6 +271,34 @@ texture.bind()
texture.unbind()
```

Another example when speed and memory management is crucial:

```python
from tachypy import Texture
import numpy as np

H, W = [256]*2 # say we have 256 pixels square images

images = np.random.rand(n_insequence, H, W, 3) # that are also RGB.

n_insequence = 200
blank = np.zeros((H, W, 3), dtype=np.uint8)
textures = [Texture(blank.copy()) for _ in range(n_in_sequence)]
```
In the code above, we pre-initialise a set of 200 textures, perhaps an RSVP or other psychophysics sequence.

```python
for im, ct in enumerate(images):
textures[ct].update(im)
```
Now whenever we start a new trial, in the waiting time, we can simply update the texture buffer with the new set of images for that trial, preventing any GPU leakage that could otherwise happen if we don't perform rigid garbage collection on every trial loop. The update method prevents our GPU from overload. This is especially useful when working with high refresh rate monitors (e.g. 480Hz>)

```python
for t in textures:
t.delete()
```
When the experiment is done, simply delete the textures, and voila.

## Shapes

Provides classes for drawing basic shapes and stimuli.
Expand Down
35 changes: 33 additions & 2 deletions src/tachypy/textures.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,40 @@ def unbind(self):
glBindTexture(GL_TEXTURE_2D, 0)
# Reset the modelview matrix to avoid any transformations being carried over
glLoadIdentity()

def update(self, image):
"""
Update pixel data of an existing texture without reallocating GPU object.
image: np.ndarray (H, W, 3), uint8
"""
glBindTexture(GL_TEXTURE_2D, self.texture_id)
glPixelStorei(GL_UNPACK_ALIGNMENT, 1)

# If size matches, do sub-image update (fast, no reallocation)
w, h = image.shape[1], image.shape[0]
glTexSubImage2D(
GL_TEXTURE_2D,
0,
0,
0,
w,
h,
GL_RGB,
GL_UNSIGNED_BYTE,
image
)
glBindTexture(GL_TEXTURE_2D, 0)

def delete(self):
glDeleteTextures([self.texture_id])
"""
Texture deletion from GPU.

:param self: Texture instance
:return: None
"""
if getattr(self, "texture_id", 0):
glDeleteTextures([self.texture_id])
self.texture_id = 0

# ---------- Gestion du rect / position ----------

Expand Down Expand Up @@ -132,4 +163,4 @@ def draw(self, a_rect=None):
glEnd()

# unbind the texture
self.unbind()
self.unbind()