From aeeaac983ebc9f4d17f130705d47e1f4fe51b8cd Mon Sep 17 00:00:00 2001 From: Paul McCarthy <pauld.mccarthy@gmail.com> Date: Wed, 20 Jan 2016 17:29:54 +0000 Subject: [PATCH] async.wait related updates to other GL types. --- fsl/fsleyes/gl/gllabel.py | 42 ++++++++------- fsl/fsleyes/gl/gllinevector.py | 9 ++-- fsl/fsleyes/gl/glmask.py | 28 +++++----- fsl/fsleyes/gl/glrgbvector.py | 20 +++---- fsl/fsleyes/gl/gltensor.py | 10 ++-- fsl/fsleyes/gl/glvector.py | 95 ++++++++++++++++++++++------------ fsl/fsleyes/gl/glvolume.py | 34 ++++++------ 7 files changed, 138 insertions(+), 100 deletions(-) diff --git a/fsl/fsleyes/gl/gllabel.py b/fsl/fsleyes/gl/gllabel.py index c224819a8..4af047079 100644 --- a/fsl/fsleyes/gl/gllabel.py +++ b/fsl/fsleyes/gl/gllabel.py @@ -9,12 +9,13 @@ functionality to render an :class:`.Image` overlay as a label/atlas image. """ -import OpenGL.GL as gl +import OpenGL.GL as gl -import fsl.fsleyes.gl as fslgl -import resources as glresources -import globject -import textures +import fsl.fsleyes.gl as fslgl +import fsl.utils.async as async +import resources as glresources +import globject +import textures class GLLabel(globject.GLImageObject): @@ -99,18 +100,17 @@ class GLLabel(globject.GLImageObject): self.notify() def shaderUpdate(*a): - fslgl.gllabel_funcs.updateShaderState(self) - self.notify() + if self.ready(): + fslgl.gllabel_funcs.updateShaderState(self) + self.notify() def shaderCompile(*a): fslgl.gllabel_funcs.compileShaders(self) - fslgl.gllabel_funcs.updateShaderState(self) - self.notify() + shaderUpdate() def lutUpdate(*a): self.refreshLutTexture() - fslgl.gllabel_funcs.updateShaderState(self) - self.notify() + shaderUpdate() def lutChanged(*a): if self.__lut is not None: @@ -124,14 +124,13 @@ class GLLabel(globject.GLImageObject): lutUpdate() def imageRefresh(*a): - self.refreshImageTexture() - fslgl.gllabel_funcs.updateShaderState(self) + async.wait([self.refreshImageTexture()], shaderUpdate) def imageUpdate(*a): self.imageTexture.set(volume=opts.volume, resolution=opts.resolution) - - fslgl.gllabel_funcs.updateShaderState(self) + + async.wait([self.imageTexture.refreshThread()], shaderUpdate) self.__lut = opts.lut @@ -184,13 +183,13 @@ class GLLabel(globject.GLImageObject): opts.removeSyncChangeListener('resolution', name) - def setAxes(self, xax, yax): """Overrides :meth:`.GLImageObject.setAxes`. Updates the shader - program state, + program state. """ globject.GLImageObject.setAxes(self, xax, yax) - fslgl.gllabel_funcs.updateShaderState(self) + if self.ready(): + fslgl.gllabel_funcs.updateShaderState(self) def refreshImageTexture(self): @@ -211,7 +210,7 @@ class GLLabel(globject.GLImageObject): if self.imageTexture is not None: if self.imageTexture.getTextureName() == texName: - return + return None self.imageTexture.deregister(self.name) glresources.delete(self.imageTexture.getTextureName()) @@ -220,10 +219,13 @@ class GLLabel(globject.GLImageObject): texName, textures.ImageTexture, texName, - self.image) + self.image, + notify=False) self.imageTexture.register(self.name, self.__imageTextureChanged) + return self.imageTexture.refreshThread() + def refreshLutTexture(self, *a): """Refreshes the :class:`.LookupTableTexture` which stores the diff --git a/fsl/fsleyes/gl/gllinevector.py b/fsl/fsleyes/gl/gllinevector.py index d27f39161..4507d96c7 100644 --- a/fsl/fsleyes/gl/gllinevector.py +++ b/fsl/fsleyes/gl/gllinevector.py @@ -67,9 +67,12 @@ class GLLineVector(glvector.GLVector): if isinstance(image, tensorimage.TensorImage): vecImage = image.V1() else: vecImage = image - glvector.GLVector.__init__(self, image, display, vectorImage=vecImage) - - fslgl.gllinevector_funcs.init(self) + glvector.GLVector.__init__(self, + image, + display, + vectorImage=vecImage, + init=lambda: fslgl.gllinevector_funcs.init( + self)) def update(*a): self.notify() diff --git a/fsl/fsleyes/gl/glmask.py b/fsl/fsleyes/gl/glmask.py index 9596c1202..b6436d26b 100644 --- a/fsl/fsleyes/gl/glmask.py +++ b/fsl/fsleyes/gl/glmask.py @@ -14,6 +14,7 @@ import numpy as np import fsl.fsleyes.gl as fslgl import fsl.fsleyes.colourmaps as colourmaps +import fsl.utils.async as async import glvolume @@ -49,34 +50,29 @@ class GLMask(glvolume.GLVolume): def update(*a): self.notify() - - def shaderCompile(*a): - fslgl.glvolume_funcs.compileShaders( self) - fslgl.glvolume_funcs.updateShaderState(self) - self.notify() def shaderUpdate(*a): - fslgl.glvolume_funcs.updateShaderState(self) - self.notify() - + if self.ready(): + fslgl.glvolume_funcs.updateShaderState(self) + self.notify() + + def shaderCompile(*a): + fslgl.glvolume_funcs.compileShaders(self) + shaderUpdate() + def colourUpdate(*a): self.refreshColourTextures() - fslgl.glvolume_funcs.updateShaderState(self) - self.notify() + shaderUpdate() def imageRefresh(*a): - self.refreshImageTexture() - fslgl.glvolume_funcs.updateShaderState(self) - self.notify() + async.wait([self.refreshImageTexture()], shaderUpdate) def imageUpdate(*a): volume = opts.volume resolution = opts.resolution self.imageTexture.set(volume=volume, resolution=resolution) - - fslgl.glvolume_funcs.updateShaderState(self) - self.notify() + async.wait([self.refreshThread()], shaderUpdate) display.addListener('alpha', name, colourUpdate, weak=False) display.addListener('brightness', name, colourUpdate, weak=False) diff --git a/fsl/fsleyes/gl/glrgbvector.py b/fsl/fsleyes/gl/glrgbvector.py index 86ef4bbc9..8d8968dce 100644 --- a/fsl/fsleyes/gl/glrgbvector.py +++ b/fsl/fsleyes/gl/glrgbvector.py @@ -13,6 +13,7 @@ import numpy as np import OpenGL.GL as gl import fsl.data.tensorimage as tensorimage +import fsl.utils.async as async import fsl.fsleyes.gl as fslgl import fsl.fsleyes.gl.glvector as glvector @@ -75,9 +76,9 @@ class GLRGBVector(glvector.GLVector): image, display, prefilter=np.abs, - vectorImage=vecImage) - - fslgl.glrgbvector_funcs.init(self) + vectorImage=vecImage, + init=lambda: fslgl.glrgbvector_funcs.init( + self)) self.displayOpts.addListener('interpolation', self.name, @@ -107,7 +108,7 @@ class GLRGBVector(glvector.GLVector): if opts.interpolation == 'none': interp = gl.GL_NEAREST else: interp = gl.GL_LINEAR - glvector.GLVector.refreshImageTexture(self, interp) + return glvector.GLVector.refreshImageTexture(self, interp) def __dataChanged(self, *a): @@ -126,12 +127,13 @@ class GLRGBVector(glvector.GLVector): if opts.interpolation == 'none': interp = gl.GL_NEAREST else: interp = gl.GL_LINEAR - texChange = self.imageTexture.set(interp=interp) - self.updateShaderState() + self.imageTexture.set(interp=interp) + + def onRefresh(): + self.updateShaderState() + self.notify() - # See comments in GLVolume.addDisplayListeners about this - if not texChange: - self.notify() + async.wait([self.imageTexture.refreshThread()], onRefresh) def compileShaders(self): diff --git a/fsl/fsleyes/gl/gltensor.py b/fsl/fsleyes/gl/gltensor.py index f2f9c45b4..513c51f9e 100644 --- a/fsl/fsleyes/gl/gltensor.py +++ b/fsl/fsleyes/gl/gltensor.py @@ -41,8 +41,9 @@ class GLTensor(glvector.GLVector): image, display, prefilter=np.abs, - vectorImage=image.V1()) - fslgl.gltensor_funcs.init(self) + vectorImage=image.V1(), + init=lambda: fslgl.gltensor_funcs.init( + self)) def destroy(self): @@ -65,8 +66,9 @@ class GLTensor(glvector.GLVector): opts = self.displayOpts def shaderUpdate(*a): - self.updateShaderState() - self.notify() + if self.ready(): + self.updateShaderState() + self.notify() opts.addListener('lighting', name, shaderUpdate, weak=False) opts.addListener('tensorResolution', name, shaderUpdate, weak=False) diff --git a/fsl/fsleyes/gl/glvector.py b/fsl/fsleyes/gl/glvector.py index 54b5aa032..73b6dfc54 100644 --- a/fsl/fsleyes/gl/glvector.py +++ b/fsl/fsleyes/gl/glvector.py @@ -14,6 +14,7 @@ import numpy as np import OpenGL.GL as gl import fsl.data.image as fslimage +import fsl.utils.async as async import fsl.fsleyes.colourmaps as fslcm import resources as glresources import textures @@ -92,7 +93,12 @@ class GLVector(globject.GLImageObject): """ - def __init__(self, image, display, prefilter=None, vectorImage=None): + def __init__(self, + image, + display, + prefilter=None, + vectorImage=None, + init=None): """Create a ``GLVector`` object bound to the given image and display. Initialises the OpenGL data required to render the given image. @@ -122,6 +128,11 @@ class GLVector(globject.GLImageObject): ``vectorImage`` parameter can be used to specify an ``Image`` instance which does contain the vector data. + + :arg init: An optional function to be called when all of the + :class:`.ImageTexture` instances associated with + this ``GLVector`` have been initialised. + """ if vectorImage is None: vectorImage = image @@ -140,7 +151,8 @@ class GLVector(globject.GLImageObject): self.yColourTexture = textures.ColourMapTexture('{}_y' .format(name)) self.zColourTexture = textures.ColourMapTexture('{}_z' .format(name)) self.cmapTexture = textures.ColourMapTexture('{}_cm'.format(name)) - + + self.shader = None self.modulateImage = None self.clipImage = None self.colourImage = None @@ -162,12 +174,19 @@ class GLVector(globject.GLImageObject): if opts.clipImage is not None: self.registerAuxImage('clip') self.addListeners() - self.refreshImageTexture() - self.refreshAuxTexture('modulate') - self.refreshAuxTexture('clip') - self.refreshAuxTexture('colour') self.refreshColourTextures() + def texRefresh(): + if init is not None: + init() + self.notify() + + async.wait([self.refreshImageTexture(), + self.refreshAuxTexture('modulate'), + self.refreshAuxTexture('clip'), + self.refreshAuxTexture('colour')], + texRefresh) + def destroy(self): """Must be called when this ``GLVector`` is no longer needed. Deletes @@ -210,7 +229,8 @@ class GLVector(globject.GLImageObject): """Returns ``True`` if this ``GLVector`` is ready to be drawn, ``False`` otherwise. """ - return all((self.imageTexture is not None, + return all((self.shader is not None, + self.imageTexture is not None, self.modulateTexture is not None, self.clipTexture is not None, self.colourTexture is not None, @@ -232,50 +252,47 @@ class GLVector(globject.GLImageObject): def update(*a): self.notify() + + def shaderUpdate(*a): + if self.ready(): + self.updateShaderState() + self.notify() def modUpdate( *a): self.deregisterAuxImage('modulate') - self.registerAuxImage( 'modulate') - self.refreshAuxTexture( 'modulate') - self.updateShaderState() + self.registerAuxImage( 'modulate') + async.wait([self.refreshAuxTexture( 'modulate')], shaderUpdate) def clipUpdate( *a): self.deregisterAuxImage('clip') self.registerAuxImage( 'clip') - self.refreshAuxTexture( 'clip') - self.updateShaderState() + async.wait([self.refreshAuxTexture( 'clip')], shaderUpdate) def colourUpdate( *a): self.deregisterAuxImage('colour') self.registerAuxImage( 'colour') - self.refreshAuxTexture( 'colour') + + def onRefresh(): + self.compileShaders() + self.refreshColourTextures() + shaderUpdate() + + async.wait([self.refreshAuxTexture( 'colour')], onRefresh) - self.compileShaders() - self.refreshColourTextures() - self.updateShaderState() - def cmapUpdate(*a): self.refreshColourTextures() - self.updateShaderState() - self.notify() - - def shaderUpdate(*a): - self.updateShaderState() - self.notify() + shaderUpdate() def shaderCompile(*a): self.compileShaders() - self.updateShaderState() - self.notify() + shaderUpdate() def imageRefresh(*a): - self.refreshImageTexture() - self.updateShaderState() + async.wait([self.refreshImageTexture()], shaderUpdate) def imageUpdate(*a): - self.imageTexture.set(resolution=opts.resolution) - self.updateShaderState() + async.wait([self.imageTexture.refreshThread()], shaderUpdate) display.addListener('alpha', name, cmapUpdate, weak=False) display.addListener('brightness', name, cmapUpdate, weak=False) @@ -373,10 +390,13 @@ class GLVector(globject.GLImageObject): nvals=3, interp=interp, normalise=True, - prefilter=realPrefilter) + prefilter=realPrefilter, + notify=False) self.imageTexture.register(self.name, self.__textureChanged) + return self.imageTexture.refreshThread() + def compileShaders(self): """This method must be provided by subclasses (the @@ -408,8 +428,10 @@ class GLVector(globject.GLImageObject): imageAttr = '{}Image' .format(which) optsAttr = '{}Opts' .format(which) + texAttr = '{}Texture'.format(which) image = getattr(self.displayOpts, imageAttr) + tex = getattr(self, texAttr) if image is None or image == 'none': image = None @@ -426,8 +448,12 @@ class GLVector(globject.GLImageObject): def volumeChange(*a): - self.refreshAuxTexture(which) - self.notify() + def onRefresh(): + self.updateShaderState() + self.notify() + + tex.set(volume=opts.volume) + async.wait([tex.refreshThread()], onRefresh) # We set overwrite=True, because # the modulate/clip/colour images @@ -508,12 +534,15 @@ class GLVector(globject.GLImageObject): textures.ImageTexture, texName, image, - normalise=norm) + normalise=norm, + notify=False) tex.register(self.name, self.__textureChanged) setattr(self, texAttr, tex) + return tex.refreshThread() + def refreshColourTextures(self, colourRes=256): """Called when the component colour maps need to be updated, when one diff --git a/fsl/fsleyes/gl/glvolume.py b/fsl/fsleyes/gl/glvolume.py index 1b93a440c..5e07775dc 100644 --- a/fsl/fsleyes/gl/glvolume.py +++ b/fsl/fsleyes/gl/glvolume.py @@ -264,25 +264,20 @@ class GLVolume(globject.GLImageObject): def update(*a): self.notify() - - def colourUpdate(*a): - self.refreshColourTextures() - if self.ready(): - fslgl.glvolume_funcs.updateShaderState(self) - self.notify() + def shaderUpdate(*a): if self.ready(): fslgl.glvolume_funcs.updateShaderState(self) self.notify() - - def onTextureRefresh(): + + def colourUpdate(*a): + self.refreshColourTextures() if self.ready(): - fslgl.glvolume_funcs.updateShaderState(self) - self.notify() + shaderUpdate() def imageRefresh(*a): - async.wait([self.refreshImageTexture()], onTextureRefresh) + async.wait([self.refreshImageTexture()], shaderUpdate) def imageUpdate(*a): volume = opts.volume @@ -304,12 +299,12 @@ class GLVolume(globject.GLImageObject): notify=False) waitfor.append(self.clipTexture.refreshThread()) - async.wait(waitfor, onTextureRefresh) + async.wait(waitfor, shaderUpdate) def clipUpdate(*a): self.deregisterClipImage() self.registerClipImage() - async.wait([self.refreshClipTexture()], onTextureRefresh) + async.wait([self.refreshClipTexture()], shaderUpdate) display.addListener('alpha', lName, colourUpdate, weak=False) opts .addListener('displayRange', lName, colourUpdate, weak=False) @@ -397,6 +392,11 @@ class GLVolume(globject.GLImageObject): :class:`.Image` data. This is performed through the :mod:`.resources` module, so the image texture can be shared between multiple ``GLVolume`` instances. + + :returns: A reference to the ``Thread`` instance which is + asynchronously updating the :class:`.ImageTexture`, + or ``None`` if the texture is updated - see the + :meth:`.ImageTexture.refreshThread` method. """ opts = self.displayOpts @@ -447,9 +447,13 @@ class GLVolume(globject.GLImageObject): self.clipOpts = clipOpts def updateClipTexture(*a): + + def onRefresh(): + fslgl.glvolume_funcs.updateShaderState(self) + self.notify() + self.clipTexture.set(volume=clipOpts.volume) - async.wait([self.clipTexture.refreshThread()], - fslgl.glvolume_funcs.updateShaderState, self) + async.wait([self.clipTexture.refreshThread()], onRefresh) clipOpts.addListener('volume', self.name, -- GitLab