@@ -385,7 +385,7 @@ def __init__(
385385 if not self ._is_active :
386386 if self ._exception :
387387 raise RuntimeError ("Unable to initialize an OpenGL 3+ context." ) from self ._exception
388- raise OpenGL .error .Error ("Invalid OpenGL context." )
388+ raise OpenGL .error .Error ("Invalid OpenGL context (unknown exception) ." )
389389 else :
390390 if self .auto_start :
391391 self .start ()
@@ -1116,38 +1116,76 @@ def start(self, auto_refresh=True):
11161116 confs [1 ],
11171117 ]
11181118 while confs :
1119- # Keep the window invisible for now. It will be displayed only if everything is working fine.
1120- # This approach avoids "flickering" when creating and closing an invalid context. Besides, it avoids
1121- # "frozen" graphical window during compilation that would be interpreted as as bug by the end-user.
11221119 conf = confs .pop (0 )
1120+
1121+ # Close any existing context and window
11231122 try :
1124- super ().__init__ (
1125- config = conf ,
1126- visible = False ,
1127- resizable = True ,
1128- width = self ._viewport_size [0 ],
1129- height = self ._viewport_size [1 ],
1130- )
1123+ OpenGL .contextdata .cleanupContext ()
1124+ self .set_visible (False )
1125+ except Exception :
1126+ pass
1127+ try :
1128+ super ().close ()
1129+ except Exception :
1130+ pass
1131+
1132+ try :
1133+ # Keep the window invisible for now. It will be displayed only if everything is working fine.
1134+ # This approach avoids "flickering" when creating and closing an invalid context. Besides, it avoids
1135+ # "frozen" graphical window during compilation that would be interpreted as as bug by the end-user.
1136+ try :
1137+ super ().__init__ (
1138+ config = conf ,
1139+ visible = False ,
1140+ resizable = True ,
1141+ width = self ._viewport_size [0 ],
1142+ height = self ._viewport_size [1 ],
1143+ )
1144+ except xlib_exceptions as e :
1145+ # Trying again without UTF8 support as a fallback.
1146+ # See: https://github.com/pyglet/pyglet/issues/1024
1147+ if pyglet .window .xlib ._have_utf8 :
1148+ pyglet .window .xlib ._have_utf8 = False
1149+ confs .insert (0 , conf )
1150+ raise
1151+
1152+ # Refresh first without window, to make sure all the necessary kernels are compiled
1153+ self .refresh ()
1154+
1155+ # At this point, we are all set to display the graphical window if requested
1156+ if not pyglet .options ["headless" ]:
1157+ self .set_visible (True )
1158+
1159+ # Run the entire rendering pipeline once, to make sure that everything is fine
1160+ self .refresh ()
1161+
11311162 break
1132- except xlib_exceptions as e :
1133- # Trying again without UTF8 support as a fallback.
1134- # See: https://github.com/pyglet/pyglet/issues/1024
1135- if not pyglet .window .xlib ._have_utf8 :
1136- if self ._run_in_thread :
1137- self .on_close ()
1138- self ._exception = e
1139- return
1140- else :
1141- raise RuntimeError ("Unable to initialize an OpenGL 3+ context." ) from e
1142- pyglet .window .xlib ._have_utf8 = False
1143- confs .insert (0 , conf )
1144- except (pyglet .window .NoSuchConfigException , pyglet .gl .ContextException ) as e :
1163+ except (
1164+ pyglet .window .NoSuchConfigException ,
1165+ pyglet .gl .ContextException ,
1166+ pyglet .gl .GLException ,
1167+ OpenGL .error .Error ,
1168+ AttributeError ,
1169+ ArgumentError ,
1170+ RuntimeError ,
1171+ ) as e :
11451172 if not confs :
1173+ # It is essential to set the exception before closing the viewer, otherwise the main thread preempt
1174+ # execution of this thread and wrongly report unknown exception.
11461175 if self ._run_in_thread :
1147- self .on_close ()
11481176 self ._exception = e
1177+
1178+ # Now the viewer can be safely cause to avoid leaving any global OpenGL context or window dangling
1179+ try :
1180+ self .on_close ()
1181+ except Exception :
1182+ pass
1183+
1184+ if self ._run_in_thread :
1185+ # Reporting the exception for the main thread to raise it
11491186 return
11501187 else :
1188+ # Raise the exception right away
11511189 raise RuntimeError ("Unable to initialize an OpenGL 3+ context." ) from e
11521190
11531191 if self ._run_in_thread :
@@ -1157,27 +1195,7 @@ def start(self, auto_refresh=True):
11571195 pyglet .clock .schedule (Viewer ._time_event , self )
11581196
11591197 # Update window title
1160- self .switch_to ()
11611198 self .set_caption (self .viewer_flags ["window_title" ])
1162-
1163- # Run the entire rendering pipeline once, to make sure that everything is fine
1164- try :
1165- self .refresh ()
1166- except (OpenGL .error .Error , RuntimeError ) as e :
1167- # Invalid OpenGL context and crossing threading boundaries. Closing before anything else
1168- self .on_close ()
1169-
1170- if self ._run_in_thread :
1171- # Reporting the exception for the main thread to raise it
1172- self ._exception = e
1173- return
1174- else :
1175- # Raise the exception right away
1176- raise
1177-
1178- # At this point, we are all set to display the graphical window if requested, finally!
1179- if not pyglet .options ["headless" ]:
1180- self .set_visible (True )
11811199 self .activate ()
11821200
11831201 # The viewer can be considered as fully initialized at this point
@@ -1220,6 +1238,7 @@ def refresh(self):
12201238 self ._event_loop_step_offscreen ()
12211239 self ._offscreen_event .clear ()
12221240
1241+ self .switch_to ()
12231242 pyglet .clock .tick ()
12241243
12251244 if gs .platform != "Windows" :
@@ -1229,7 +1248,6 @@ def refresh(self):
12291248 # this is a workaround on Windows. not sure if it's correct
12301249 time .sleep (0.001 )
12311250
1232- self .switch_to ()
12331251 self .dispatch_pending_events ()
12341252 if self ._is_active :
12351253 self .dispatch_events ()
0 commit comments