Repository: kivy/kivy

Issue:
shader transitions flicker on start
When using shader transitions with `ScreenManager`, the screen sometimes flickers and shows the background behind the `ScreenManager`.

![shader](https://cloud.githubusercontent.com/assets/1126441/5442317/db191b50-845d-11e4-9926-67eabff6392f.gif)

The issue is easier to reproduce with a lower framerate (i.e. set maxfps = 5 in config.ini). With this test code, the white canvas sometimes flickers in the background at maxfps = 60, and is clearly visible every time with maxfps = 5:

``` python
#import pydevd; pydevd.settrace('localhost', port=51514, suspend=False)

import kivy
kivy.require('1.8.1')

from kivy.app import App
from kivy.lang import Builder
from kivy.clock import Clock

root = Builder.load_string('''
#:import sm kivy.uix.screenmanager
ScreenManager:
    transition: sm.WipeTransition()
    canvas.before:
        Color:
            rgba: 1, 1, 1, 1
        Rectangle:
            pos: self.pos
            size: self.size
    Screen:
        name: 'first'
        BoxLayout:
            canvas.before:
                Color:
                    rgba: 1, .5, .5, 1
                Rectangle:
                    size: self.size
            Label:
                text: 'first'
            Button:
                text: 'button'
    Screen:
        name: 'second'
        BoxLayout:
            canvas.before:
                Color:
                    rgba: .5, .5, 1, 1
                Rectangle:
                    size: self.size
            Label:
                text: 'second'
            BoxLayout:
                orientation: 'vertical'
                Button:
                    text: 'button 1'
                Button:
                    text: 'button 2'
''')

class TestApp(App):
    def build(self):
        Clock.schedule_interval(lambda dt: setattr(root, 'current', 'first' if root.current == 'second' else 'second'), 1)
        return root

if __name__ == '__main__':
    TestApp().run()
```



Discussion:

Resolving patch:
diff --git a/kivy/uix/screenmanager.py b/kivy/uix/screenmanager.py
--- a/kivy/uix/screenmanager.py
+++ b/kivy/uix/screenmanager.py
@@ -192,6 +192,7 @@ def build(self):
 from kivy.compat import iteritems
 from kivy.logger import Logger
 from kivy.event import EventDispatcher
+from kivy.clock import Clock
 from kivy.uix.floatlayout import FloatLayout
 from kivy.properties import (StringProperty, ObjectProperty, AliasProperty,
                              NumericProperty, ListProperty, OptionProperty,
@@ -201,7 +202,7 @@ def build(self):
 from kivy.lang import Builder
 from kivy.graphics import (RenderContext, Rectangle, Fbo,
                            ClearColor, ClearBuffers, BindTexture, PushMatrix,
-                           PopMatrix, Translate)
+                           PopMatrix, Translate, Callback)
 
 
 class ScreenManagerException(Exception):
@@ -459,7 +460,7 @@ class ShaderTransition(TransitionBase):
     None.'''
 
     clearcolor = ListProperty([0, 0, 0, 1])
-    '''Sets the color of Fbo ClearColor. 
+    '''Sets the color of Fbo ClearColor.
 
     .. versionadded:: 1.9.0
 
@@ -486,10 +487,21 @@ def on_complete(self):
         self.render_ctx['t'] = 1.
         super(ShaderTransition, self).on_complete()
 
+    def _remove_out_canvas(self, *args):
+        if (self.screen_out
+                and self.screen_out.canvas in self.manager.canvas.children
+                and self.screen_out not in self.manager.children):
+            self.manager.canvas.remove(self.screen_out.canvas)
+
     def add_screen(self, screen):
         self.screen_in.pos = self.screen_out.pos
         self.screen_in.size = self.screen_out.size
         self.manager.real_remove_widget(self.screen_out)
+        self.manager.canvas.add(self.screen_out.canvas)
+
+        def remove_screen_out(instr):
+            Clock.schedule_once(self._remove_out_canvas, -1)
+            self.render_ctx.remove(instr)
 
         self.fbo_in = self.make_screen_fbo(self.screen_in)
         self.fbo_out = self.make_screen_fbo(self.screen_out)
@@ -506,6 +518,7 @@ def add_screen(self, screen):
             w, h = self.fbo_in.texture.size
             Rectangle(size=(w, h), pos=(x, y),
                       tex_coords=self.fbo_in.texture.tex_coords)
+            Callback(remove_screen_out)
         self.render_ctx['tex_out'] = 1
         self.render_ctx['tex_in'] = 2
         self.manager.canvas.add(self.render_ctx)
@@ -514,8 +527,13 @@ def remove_screen(self, screen):
         self.manager.canvas.remove(self.fbo_in)
         self.manager.canvas.remove(self.fbo_out)
         self.manager.canvas.remove(self.render_ctx)
+        self._remove_out_canvas()
         self.manager.real_add_widget(self.screen_in)
 
+    def stop(self):
+        self._remove_out_canvas()
+        super(ShaderTransition, self).stop()
+
 
 class NoTransition(TransitionBase):
     '''No transition, instantly switches to the next screen with no delay or


Test patch: