From 2144b2b73630eae9003563eda0573c7af63bcb0c Mon Sep 17 00:00:00 2001 From: Ian Charest Date: Tue, 20 Jan 2026 15:50:48 -0500 Subject: [PATCH 1/5] Add update method for texture pixel data and safer delete Added an update method to modify existing texture pixel data without reallocating the GPU object. --- src/tachypy/textures.py | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/src/tachypy/textures.py b/src/tachypy/textures.py index 273df21..25ba46f 100644 --- a/src/tachypy/textures.py +++ b/src/tachypy/textures.py @@ -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 ---------- @@ -132,4 +163,4 @@ def draw(self, a_rect=None): glEnd() # unbind the texture - self.unbind() \ No newline at end of file + self.unbind() From 7f26737b60f0051e0fc4354422816b2ce71fa7fc Mon Sep 17 00:00:00 2001 From: Ian Charest Date: Tue, 20 Jan 2026 15:59:54 -0500 Subject: [PATCH 2/5] added some doc on textures.update() method --- README.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/README.md b/README.md index 2f27f4f..9492a1a 100644 --- a/README.md +++ b/README.md @@ -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: @@ -270,6 +271,32 @@ texture.bind() texture.unbind() ``` +Another example when speed and memory management is crucial: + +```python +from tachypy import Texture +import numpy as np + +images = np.random.rand(n_insequence, 128, 128, 3) + +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[c].update(im) +``` +Now whenever we start a new trial, in the waiting time, we can simply update the texture buffer, preventing any GPU leakage that could otherwise happen if we don't perform rigid garbage collection on every loop. The update method prevents our GPU from overload. + +```python +for t in texs_pos + texs_neg + texs_bis: + t.delete() +``` +When the experiment is done, simply delete the textures, and voila. + ## Shapes Provides classes for drawing basic shapes and stimuli. From ac160ad85267379f7f8c04b10e229e720137a66b Mon Sep 17 00:00:00 2001 From: Ian Charest Date: Tue, 20 Jan 2026 16:01:50 -0500 Subject: [PATCH 3/5] little beautify of the texture update example. --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 9492a1a..2fd7c12 100644 --- a/README.md +++ b/README.md @@ -277,7 +277,9 @@ Another example when speed and memory management is crucial: from tachypy import Texture import numpy as np -images = np.random.rand(n_insequence, 128, 128, 3) +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) From eae57f1f84851897cbb72c9643f02e6394abbe07 Mon Sep 17 00:00:00 2001 From: Ian Charest Date: Tue, 20 Jan 2026 16:03:51 -0500 Subject: [PATCH 4/5] Fix texture update logic and improve GPU management explanationa Clarified texture buffer update process and GPU management. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2fd7c12..bc1559d 100644 --- a/README.md +++ b/README.md @@ -289,9 +289,9 @@ In the code above, we pre-initialise a set of 200 textures, perhaps an RSVP or o ```python for im, ct in enumerate(images): - textures[c].update(im) + textures[ct].update(im) ``` -Now whenever we start a new trial, in the waiting time, we can simply update the texture buffer, preventing any GPU leakage that could otherwise happen if we don't perform rigid garbage collection on every loop. The update method prevents our GPU from overload. +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 texs_pos + texs_neg + texs_bis: From e857b6d973c95bafa6cad10277ab03a3a157ba73 Mon Sep 17 00:00:00 2001 From: Ian Charest Date: Tue, 20 Jan 2026 16:04:21 -0500 Subject: [PATCH 5/5] Update texture deletion example in README Simplified texture deletion process in README. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bc1559d..a15e6ec 100644 --- a/README.md +++ b/README.md @@ -294,7 +294,7 @@ for im, ct in enumerate(images): 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 texs_pos + texs_neg + texs_bis: +for t in textures: t.delete() ``` When the experiment is done, simply delete the textures, and voila.