diff --git a/fsl/fsleyes/gl/gl21/gltensor_funcs.py b/fsl/fsleyes/gl/gl21/gltensor_funcs.py index 15554b661892b741871df346cb4d9d04d5e76c59..65a99bd316664159aba3c772e7ea58bb54dd56bd 100644 --- a/fsl/fsleyes/gl/gl21/gltensor_funcs.py +++ b/fsl/fsleyes/gl/gl21/gltensor_funcs.py @@ -60,6 +60,10 @@ import glvector_funcs def init(self): """Creates textures for the tensor eigenvalue and eigenvector images, and calls :func:`compileShaders` and :func:`updateShaderState`. + + :returns: A list of ``Thread`` instances, one for each of the + :class:`.ImageTexture` instances that are created. See + the ``init`` parameter to :meth:`.GLVector.__init__`. """ image = self.image @@ -74,8 +78,9 @@ def init(self): def vPrefilter(d): return d.transpose((3, 0, 1, 2)) - names = ['v1', 'v2', 'v3', 'l1', 'l2', 'l3'] - imgs = [ v1, v2, v3, l1, l2, l3] + names = ['v1', 'v2', 'v3', 'l1', 'l2', 'l3'] + imgs = [ v1, v2, v3, l1, l2, l3] + texThreads = [] for name, img in zip(names, imgs): texName = '{}_{}_{}'.format(type(self).__name__, name, id(img)) @@ -96,6 +101,8 @@ def init(self): normalise=True, prefilter=prefilter) + texThreads.append(tex.refreshThread()) + setattr(self, '{}Texture'.format(name), tex) self.shader = None @@ -103,6 +110,8 @@ def init(self): compileShaders(self) updateShaderState(self) + return texThreads + def destroy(self): """Deletes the :class:`.GLSLShader`, and all textures. """ @@ -262,10 +271,10 @@ def draw(self, zpos, xform=None): # Set divisor to 1, so we use one set of # voxel coordinates for every sphere drawn - shader.loadAtts() shader.setAtt('voxel', voxels, divisor=1) shader.set( 'voxToDisplayMat', xform) - + shader.loadAtts() + arbdi.glDrawElementsInstancedARB( gl.GL_QUADS, self.nVertices, gl.GL_UNSIGNED_INT, None, nVoxels) diff --git a/fsl/fsleyes/gl/glvector.py b/fsl/fsleyes/gl/glvector.py index 3574a7bc22851c81d2aa8eadc29c8a30943053ae..6e97d1efb9e5b4583ef75eed188087364d178eda 100644 --- a/fsl/fsleyes/gl/glvector.py +++ b/fsl/fsleyes/gl/glvector.py @@ -131,8 +131,11 @@ class GLVector(globject.GLImageObject): :arg init: An optional function to be called when all of the :class:`.ImageTexture` instances associated with - this ``GLVector`` have been initialised. - + this ``GLVector`` have been initialised. If this + function does any initialisation on one or more + separate threads, it should return references to + those threads so that this method is able to + determine when initialisation is complete. """ if vectorImage is None: vectorImage = image @@ -177,9 +180,8 @@ class GLVector(globject.GLImageObject): self.refreshColourTextures() def texRefresh(): - if init is not None: - init() - self.notify() + if init is not None: async.wait(init(), self.notify) + else: self.notify() async.wait([self.refreshImageTexture(), self.refreshAuxTexture('modulate'), diff --git a/fsl/utils/async.py b/fsl/utils/async.py index bc551d077fa6d1b692d71a509ba10b33cf882cef..73330044b4653df87e8142df5a3178b8a367ae05 100644 --- a/fsl/utils/async.py +++ b/fsl/utils/async.py @@ -34,6 +34,7 @@ the task (via :func:`idle`). import Queue import logging import threading +import collections log = logging.getLogger(__name__) @@ -173,13 +174,17 @@ def wait(threads, task, *args, **kwargs): If a ``wx.App`` is not running, this function ``join``s the threads directly instead of creating a new ``Thread`` to do so. - :arg threads: A sequence of ``Thread`` instances to join. Elements in the - sequence may be ``None``. + :arg threads: A ``Thread``, or a sequence of ``Thread`` instances to + join. Elements in the sequence may be ``None``. :arg task: The task to run. All other arguments are passed to the ``task`` function. """ + + if not isinstance(threads, collections.Sequence): + threads = [threads] + haveWX = _haveWX() def joinAll():