3939import java .io .InputStream ;
4040import java .net .URL ;
4141import java .nio .ByteBuffer ;
42+ import java .util .ArrayList ;
4243import java .util .HashMap ;
4344import java .util .Map ;
4445
5657import com .jogamp .opengl .GLEventListener ;
5758import com .jogamp .opengl .GLException ;
5859import com .jogamp .opengl .GLProfile ;
60+ import com .jogamp .opengl .GLDrawableFactory ;
5961import com .jogamp .nativewindow .MutableGraphicsConfiguration ;
6062import com .jogamp .nativewindow .WindowClosingProtocol ;
6163import com .jogamp .newt .Display ;
@@ -107,6 +109,11 @@ public class PSurfaceJOGL implements PSurface {
107109
108110 protected NewtCanvasAWT canvas ;
109111
112+ protected static GLAutoDrawable sharedDrawable ;
113+ protected static ArrayList <FPSAnimator > animators = new ArrayList <>();
114+ private final static Object sharedSyncMutex = new Object ();
115+ private Object syncMutex ;
116+
110117 protected int windowScaleFactor ;
111118
112119 protected float [] currentPixelScale = { 0 , 0 };
@@ -254,6 +261,29 @@ protected void initGL() {
254261 caps .setBackgroundOpaque (true );
255262 caps .setOnscreen (true );
256263 pgl .setCaps (caps );
264+
265+ if (sharedDrawable == null ) {
266+ // Create a shared drawable to enable context sharing across multiple GL windows
267+ // https://jogamp.org/deployment/jogamp-next/javadoc/jogl/javadoc/com/jogamp/opengl/GLSharedContextSetter.html
268+ sharedDrawable = GLDrawableFactory .getFactory (profile ).createDummyAutoDrawable (null , true , caps , null );
269+ sharedDrawable .display ();
270+ }
271+ }
272+
273+
274+ // To properly deal with synchronization when context sharing across multiple drawables (windows), we need a
275+ // synchronization mutex object to ensure that rendering of each frame completes in their respective animator thread:
276+ // https://jogamp.org/deployment/jogamp-next/javadoc/jogl/javadoc/com/jogamp/opengl/GLSharedContextSetter.html#synchronization
277+ private Object getSyncMutex (GLAutoDrawable drawable ) {
278+ pgl .getGL (drawable );
279+ if (pgl .needSharedObjectSync ()) {
280+ syncMutex = sharedSyncMutex ;
281+ } else {
282+ if (syncMutex == null ) {
283+ syncMutex = new Object ();
284+ }
285+ }
286+ return syncMutex ;
257287 }
258288
259289
@@ -327,6 +357,8 @@ protected void initWindow() {
327357 window .setTopLevelSize ((int ) displayRect .getWidth (), (int ) displayRect .getHeight ());
328358 }
329359 }
360+
361+ window .setSharedAutoDrawable (sharedDrawable );
330362 }
331363
332364
@@ -354,6 +386,7 @@ protected void initAnimator() {
354386 }
355387
356388 animator = new FPSAnimator (window , 60 );
389+ animators .add (animator );
357390
358391 drawException = null ;
359392 animator .setUncaughtExceptionHandler ((animator , drawable , cause ) -> {
@@ -637,6 +670,10 @@ public boolean stopThread() {
637670 drawExceptionHandler = null ;
638671 }
639672 if (animator != null ) {
673+ // Stops all other animators to avoid exceptions when closing a window in a multiple window configuration
674+ for (FPSAnimator ani : animators ) {
675+ if (ani != animator ) ani .stop ();
676+ }
640677 return animator .stop ();
641678 } else {
642679 return false ;
@@ -753,9 +790,13 @@ public void requestFocus() {
753790
754791
755792 public class DrawListener implements GLEventListener {
793+ private boolean isInit = false ;
794+
756795 public void display (GLAutoDrawable drawable ) {
796+ if (!isInit ) return ;
797+
757798 if (display .getEDTUtil ().isCurrentThreadEDT ()) {
758- // For an unknown reason, the first two frames of the animator run on
799+ // For some unknown reason, a few frames of the animator run on
759800 // the EDT. For those, we just skip this draw call to avoid badness.
760801 return ;
761802 }
@@ -770,17 +811,19 @@ public void display(GLAutoDrawable drawable) {
770811 }
771812
772813 if (!sketch .finished ) {
773- pgl .getGL (drawable );
774- int prevFrameCount = sketch .frameCount ;
775- sketch .handleDraw ();
776- if (prevFrameCount == sketch .frameCount || sketch .finished ) {
777- // This hack allows the FBO layer to be swapped normally even if
778- // the sketch is no looping or finished because it does not call draw(),
779- // otherwise background artifacts may occur (depending on the hardware/drivers).
780- pgl .beginRender ();
781- pgl .endRender (sketch .sketchWindowColor ());
814+ synchronized (getSyncMutex (drawable )) {
815+ pgl .getGL (drawable );
816+ int prevFrameCount = sketch .frameCount ;
817+ sketch .handleDraw ();
818+ if (prevFrameCount == sketch .frameCount || sketch .finished ) {
819+ // This hack allows the FBO layer to be swapped normally even if
820+ // the sketch is no looping or finished because it does not call draw(),
821+ // otherwise background artifacts may occur (depending on the hardware/drivers).
822+ pgl .beginRender ();
823+ pgl .endRender (sketch .sketchWindowColor ());
824+ }
825+ PGraphicsOpenGL .completeFinishedPixelTransfers ();
782826 }
783- PGraphicsOpenGL .completeFinishedPixelTransfers ();
784827 }
785828
786829 if (sketch .exitCalled ()) {
@@ -796,24 +839,37 @@ public void dispose(GLAutoDrawable drawable) {
796839 }
797840
798841 public void init (GLAutoDrawable drawable ) {
799- pgl . getGL ( drawable );
800- pgl . init ( drawable ) ;
801- sketch . start ();
842+ if ( display . getEDTUtil (). isCurrentThreadEDT ()) {
843+ return ;
844+ }
802845
803- int c = graphics .backgroundColor ;
804- pgl .clearColor (((c >> 16 ) & 0xff ) / 255f ,
805- ((c >> 8 ) & 0xff ) / 255f ,
806- (c & 0xff ) / 255f ,
807- ((c >> 24 ) & 0xff ) / 255f );
808- pgl .clear (PGL .COLOR_BUFFER_BIT );
846+ synchronized (getSyncMutex (drawable )) {
847+ pgl .init (drawable );
848+ sketch .start ();
849+
850+ int c = graphics .backgroundColor ;
851+ pgl .clearColor (((c >> 16 ) & 0xff ) / 255f ,
852+ ((c >> 8 ) & 0xff ) / 255f ,
853+ (c & 0xff ) / 255f ,
854+ ((c >> 24 ) & 0xff ) / 255f );
855+ pgl .clear (PGL .COLOR_BUFFER_BIT );
856+ isInit = true ;
857+ }
809858 }
810859
811860 public void reshape (GLAutoDrawable drawable , int x , int y , int w , int h ) {
812- pgl .resetFBOLayer ();
813- pgl .getGL (drawable );
814- float scale = PApplet .platform == PConstants .MACOS ?
815- getCurrentPixelScale () : getPixelScale ();
816- setSize ((int ) (w / scale ), (int ) (h / scale ));
861+ if (!isInit ) return ;
862+
863+ if (display .getEDTUtil ().isCurrentThreadEDT ()) {
864+ return ;
865+ }
866+
867+ synchronized (getSyncMutex (drawable )) {
868+ pgl .resetFBOLayer ();
869+ float scale = PApplet .platform == PConstants .MACOS ?
870+ getCurrentPixelScale () : getPixelScale ();
871+ setSize ((int ) (w / scale ), (int ) (h / scale ));
872+ }
817873 }
818874 }
819875
0 commit comments