From 93bd9eb1430c68bca4f851805f69d39466678e4e Mon Sep 17 00:00:00 2001
From: Paul McCarthy <pauld.mccarthy@gmail.com>
Date: Thu, 4 Feb 2016 15:14:01 +0000
Subject: [PATCH] First GLTensor draw was failing, because i was calling
 GLSLShader.loadAtts before setting the vertex attribute divisor. Other
 associated changes to initial refresh - it now occurs after all
 eigenvector/value textures are ready.

---
 fsl/fsleyes/gl/gl21/gltensor_funcs.py | 17 +++++++++++++----
 fsl/fsleyes/gl/glvector.py            | 12 +++++++-----
 fsl/utils/async.py                    |  9 +++++++--
 3 files changed, 27 insertions(+), 11 deletions(-)

diff --git a/fsl/fsleyes/gl/gl21/gltensor_funcs.py b/fsl/fsleyes/gl/gl21/gltensor_funcs.py
index 15554b661..65a99bd31 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 3574a7bc2..6e97d1efb 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 bc551d077..73330044b 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():
-- 
GitLab