changeset 11140:4e1e2f56c7af

8189926: [Mac] Pulse timer should pause when idle Reviewed-by: kcr, mbilla
author arapte
date Mon, 24 Dec 2018 13:23:52 +0530
parents ab4db0272524
children 215e3963017f
files modules/javafx.graphics/src/main/java/com/sun/glass/ui/Timer.java modules/javafx.graphics/src/main/java/com/sun/glass/ui/gtk/GtkTimer.java modules/javafx.graphics/src/main/java/com/sun/glass/ui/ios/IosTimer.java modules/javafx.graphics/src/main/java/com/sun/glass/ui/mac/MacTimer.java modules/javafx.graphics/src/main/java/com/sun/glass/ui/monocle/MonocleTimer.java modules/javafx.graphics/src/main/java/com/sun/glass/ui/win/WinTimer.java modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/PaintCollector.java modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/QuantumToolkit.java modules/javafx.graphics/src/main/native-glass/mac/GlassTimer.h modules/javafx.graphics/src/main/native-glass/mac/GlassTimer.m
diffstat 10 files changed, 214 insertions(+), 32 deletions(-) [+]
line wrap: on
line diff
--- a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/Timer.java	Fri Dec 21 19:17:41 2018 +0530
+++ b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/Timer.java	Mon Dec 24 13:23:52 2018 +0530
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -48,6 +48,8 @@
     protected abstract long _start(Runnable runnable);
     protected abstract long _start(Runnable runnable, int period);
     protected abstract void _stop(long timer);
+    protected abstract void _pause(long timer);
+    protected abstract void _resume(long timer);
 
     /**
      * Constructs a new timer.
@@ -133,6 +135,27 @@
     }
 
     /**
+     * Pauses the timer. See JDK-8189926.
+     * Timer is paused only from the timer thread.
+     */
+    public synchronized void pause() {
+        if (ptr != 0L) {
+            _pause(ptr);
+        }
+    }
+
+    /**
+     * Resumes the timer. See JDK-8189926
+     * Timer can get resumed from different threads.
+     */
+    public synchronized void resume() {
+        if (ptr != 0L) {
+            _resume(ptr);
+        }
+    }
+
+
+    /**
      * Returns true if the timer is currently running
      * (convenience API: might not need it)
      */
--- a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/gtk/GtkTimer.java	Fri Dec 21 19:17:41 2018 +0530
+++ b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/gtk/GtkTimer.java	Mon Dec 24 13:23:52 2018 +0530
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -42,4 +42,7 @@
     @Override
     protected native void _stop(long timer);
 
+    @Override protected void _pause(long timer) {}
+    @Override protected void _resume(long timer) {}
+
 }
--- a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/ios/IosTimer.java	Fri Dec 21 19:17:41 2018 +0530
+++ b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/ios/IosTimer.java	Mon Dec 24 13:23:52 2018 +0530
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -40,6 +40,8 @@
     }
 
     @Override native protected long _start(Runnable runnable);
+    @Override protected void _pause(long timer) {}
+    @Override protected void _resume(long timer) {}
 
     native protected void _stopVsyncTimer(long timer);
 
--- a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/mac/MacTimer.java	Fri Dec 21 19:17:41 2018 +0530
+++ b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/mac/MacTimer.java	Mon Dec 24 13:23:52 2018 +0530
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -60,6 +60,8 @@
     @Override native protected long _start(Runnable runnable);
     @Override native protected long _start(Runnable runnable, int period);
     @Override native protected void _stop(long timer);
+    @Override native protected void _pause(long timer);
+    @Override native protected void _resume(long timer);
     native private static void _initIDs();
 }
 
--- a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/monocle/MonocleTimer.java	Fri Dec 21 19:17:41 2018 +0530
+++ b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/monocle/MonocleTimer.java	Mon Dec 24 13:23:52 2018 +0530
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -73,5 +73,8 @@
             task = null;
         }
     }
+
+    @Override protected void _pause(long timer) {}
+    @Override protected void _resume(long timer) {}
 }
 
--- a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/win/WinTimer.java	Fri Dec 21 19:17:41 2018 +0530
+++ b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/win/WinTimer.java	Mon Dec 24 13:23:52 2018 +0530
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -61,5 +61,7 @@
     }
     @Override native protected long _start(Runnable runnable, int period);
     @Override native protected void _stop(long timer);
+    @Override protected void _pause(long timer) {}
+    @Override protected void _resume(long timer) {}
 }
 
--- a/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/PaintCollector.java	Fri Dec 21 19:17:41 2018 +0530
+++ b/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/PaintCollector.java	Mon Dec 24 13:23:52 2018 +0530
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -178,6 +178,13 @@
         return hasDirty;
     }
 
+    private final void setDirty(boolean value) {
+        hasDirty = value;
+        if (hasDirty) {
+            QuantumToolkit.getToolkit().requestNextPulse();
+        }
+    }
+
     /**
      * Adds a dirty scene to the PaintCollector for subsequent processing.
      * This method simply makes the PaintCollector aware of this new
@@ -209,7 +216,7 @@
             dirtyScenes.add(scene);
             // Now that we know we have added a scene to dirtyScenes,
             // we should ensure hasDirty is true.
-            hasDirty = true;
+            setDirty(true);
         }
     }
 
@@ -241,7 +248,7 @@
         // Remove the scene
         dirtyScenes.remove(scene);
         // Update hasDirty
-        hasDirty = !dirtyScenes.isEmpty();
+        setDirty(!dirtyScenes.isEmpty());
     }
 
     /**
@@ -361,7 +368,7 @@
         Collections.sort(dirtyScenes, DIRTY_SCENE_SORTER);
 
         // Reset the fields
-        hasDirty = false;
+        setDirty(false);
         needsHint = false;
 
         // If pulse logging is enabled, then we must call renderStart
--- a/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/QuantumToolkit.java	Fri Dec 21 19:17:41 2018 +0530
+++ b/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/QuantumToolkit.java	Mon Dec 24 13:23:52 2018 +0530
@@ -191,9 +191,27 @@
                 return result;
             });
 
+    private class PulseTask {
+        private volatile boolean isRunning;
+        PulseTask(boolean state) {
+            isRunning = state;
+        }
+
+        synchronized void set(boolean state) {
+            isRunning = state;
+            if (isRunning) {
+                resumeTimer();
+            }
+        }
+
+        boolean get() {
+            return isRunning;
+        }
+    }
+
     private AtomicBoolean           toolkitRunning = new AtomicBoolean(false);
-    private AtomicBoolean           animationRunning = new AtomicBoolean(false);
-    private AtomicBoolean           nextPulseRequested = new AtomicBoolean(false);
+    private PulseTask               animationRunning = new PulseTask(false);
+    private PulseTask               nextPulseRequested = new PulseTask(false);
     private AtomicBoolean           pulseRunning = new AtomicBoolean(false);
     private int                     inPulse = 0;
     private CountDownLatch          launchLatch = new CountDownLatch(1);
@@ -201,6 +219,9 @@
     final int                       PULSE_INTERVAL = (int)(TimeUnit.SECONDS.toMillis(1L) / getRefreshRate());
     final int                       FULLSPEED_INTERVAL = 1;     // ms
     boolean                         nativeSystemVsync = false;
+    private long                    firstPauseRequestTime = 0;
+    private boolean                 pauseRequested = false;
+    private static final long       PAUSE_THRESHOLD_DURATION = 250;
     private float                   _maxPixelScale;
     private Runnable                pulseRunnable, userRunnable, timerRunnable;
     private Timer                   pulseTimer = null;
@@ -455,7 +476,7 @@
 
     void postPulse() {
         if (toolkitRunning.get() &&
-            (animationRunning.get() || nextPulseRequested.get() || collector.hasDirty()) &&
+            (animationRunning.get() || nextPulseRequested.get()) &&
             !setPulseRunning()) {
 
             Application.invokeLater(pulseRunnable);
@@ -463,17 +484,39 @@
             if (debug) {
                 System.err.println("QT.postPulse@(" + System.nanoTime() + "): " + pulseString());
             }
+        } else if (!animationRunning.get() && !nextPulseRequested.get() && !pulseRunning.get()) {
+            pauseTimer();
         } else if (debug) {
-            System.err.println("QT.postPulse#(" + System.nanoTime() + ") DROP: " + pulseString());
+            System.err.println("QT.postPulse#(" + System.nanoTime() + "): DROP : " + pulseString());
         }
     }
 
+    private synchronized void pauseTimer() {
+        if (!pauseRequested) {
+            pauseRequested = true;
+            firstPauseRequestTime = System.currentTimeMillis();
+        }
+
+        if (System.currentTimeMillis() - firstPauseRequestTime >= PAUSE_THRESHOLD_DURATION) {
+            pulseTimer.pause();
+            if (debug) {
+                System.err.println("QT.pauseTimer#(" + System.nanoTime() + "): Pausing Timer : " + pulseString());
+            }
+        } else if (debug) {
+            System.err.println("QT.pauseTimer#(" + System.nanoTime() + "): Pause Timer : DROP : " + pulseString());
+        }
+    }
+
+    private synchronized void resumeTimer() {
+        pauseRequested = false;
+        pulseTimer.resume();
+    }
+
     private String pulseString() {
         return ((toolkitRunning.get() ? "T" : "t") +
                 (animationRunning.get() ? "A" : "a") +
                 (pulseRunning.get() ? "P" : "p") +
-                (nextPulseRequested.get() ? "N" : "n") +
-                (collector.hasDirty() ? "D" : "d"));
+                (nextPulseRequested.get() ? "N" : "n"));
     }
 
     private boolean setPulseRunning() {
--- a/modules/javafx.graphics/src/main/native-glass/mac/GlassTimer.h	Fri Dec 21 19:17:41 2018 +0530
+++ b/modules/javafx.graphics/src/main/native-glass/mac/GlassTimer.h	Mon Dec 24 13:23:52 2018 +0530
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -35,6 +35,7 @@
 
     pthread_t   _thread;
     BOOL        _running;
+    BOOL        _paused;
 
     int64_t     _period;
     JNIEnv*     _env;
--- a/modules/javafx.graphics/src/main/native-glass/mac/GlassTimer.m	Fri Dec 21 19:17:41 2018 +0530
+++ b/modules/javafx.graphics/src/main/native-glass/mac/GlassTimer.m	Mon Dec 24 13:23:52 2018 +0530
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -58,6 +58,7 @@
             while (timer->_running == YES)
             {
                 int64_t now = getTimeMicroseconds();
+                if (timer->_paused == NO)
                 {
                     if (timer->_runnable != NULL)
                     {
@@ -108,6 +109,8 @@
     return NULL;
 }
 
+JavaVMAttachArgs attachArgs = {JNI_VERSION_1_2, "PulseTimer-CVDisplayLink thread", NULL};
+
 CVReturn CVOutputCallback(CVDisplayLinkRef displayLink,
                           const CVTimeStamp *inNow, const CVTimeStamp *inOutputTime,
                           CVOptionFlags flagsIn, CVOptionFlags *flagsOut,
@@ -119,27 +122,33 @@
     {
         GlassTimer *timer = (GlassTimer*)displayLinkContext;
 
-        // Attach the thread every time.  Cocoa changes the thread when a new
-        // display resolution is selected so we need to make sure that we are
-        // able to call into Java.  Attaching the same thread multiple times
-        // does nothing.
-        jint error = (*MAIN_JVM)->AttachCurrentThreadAsDaemon(MAIN_JVM, (void **)&timer->_env, NULL);
-        if (error == 0)
+        // CVDisplayLinkStart() starts a new CVDisplayLink thread. This thread
+        // should be attached to JVM. Cocoa changes the thread when a new display
+        // resolution is selected so we need to make sure that we are able to
+        // call into Java. Attaching the same thread multiple times does nothing.
+        // As the thread is attached as a daemon, it will not stop Java from exiting.
+
+        jint ret = (*MAIN_JVM)->AttachCurrentThreadAsDaemon(MAIN_JVM, (void **)&timer->_env, (void*)&attachArgs);
+        if (ret == 0)
         {
             if (timer->_runnable != NULL)
             {
                 (*timer->_env)->CallVoidMethod(timer->_env, timer->_runnable, jRunnableRun);
             }
 
-              // Do not detach the thread - continuously attaching and detaching a thread
-              // kills some debuggers making it impossible to step through Prism.  Since
-              // the thread is attached as a daemon, it will not stop Java from exiting.
-//            error = (*MAIN_JVM)->DetachCurrentThread(MAIN_JVM);
-//            if (error != JNI_OK) {
-//                NSLog(@"ERROR: Glass could not detach CVDisplayLink _thread to VM, result:%d\n", (int)error);
-//            }
+            if (timer->_paused == YES)
+            {
+                // _1pause() calls CVDisplayLinkStop() on current CVDisplayLink thread, which
+                // in turn stops the thread. Hence the thread should be detached.
+
+                ret = (*MAIN_JVM)->DetachCurrentThread(MAIN_JVM);
+                if (ret != JNI_OK)
+                {
+                    NSLog(@"ERROR: CVOutputCallback(): Glass could not detach CVDisplayLink _thread to VM, result:%d\n", (int)ret);
+                }
+            }
         } else {
-            NSLog(@"ERROR: Glass could not attach CVDisplayLink _thread to VM, result:%d\n", (int)error);
+            NSLog(@"ERROR: Glass could not attach CVDisplayLink _thread to VM, result:%d\n", (int)ret);
         }
     }
 
@@ -155,6 +164,7 @@
     self = [super init];
     if (self != nil)
     {
+        self->_paused = NO;
         CVReturn err = CVDisplayLinkSetOutputCallback(GlassDisplayLink, &CVOutputCallback, self);
         if (err != kCVReturnSuccess)
         {
@@ -175,6 +185,7 @@
     {
         self->_thread = NULL;
         self->_running = NO;
+        self->_paused = NO;
         self->_runnable = NULL;
         self->_period = (1000 * period); // ms --> us
 
@@ -290,6 +301,91 @@
 
 /*
  * Class:     com_sun_glass_ui_mac_MacTimer
+ * Method:    _pause
+ * Signature: (J)V
+ */
+JNIEXPORT void JNICALL Java_com_sun_glass_ui_mac_MacTimer__1pause
+(JNIEnv *env, jclass cls, jlong jTimerPtr)
+{
+    LOG("Java_com_sun_glass_ui_mac_MacTimer__1pause");
+
+    GLASS_ASSERT_MAIN_JAVA_THREAD(env);
+    GLASS_POOL_ENTER;
+    {
+        GlassTimer *timer = (GlassTimer*)jlong_to_ptr(jTimerPtr);
+        if (timer->_period == 0)
+        {
+            // Stop the CVDisplayLink thread. There is no API to pause the CVDisplayLink
+            // thread. CVDisplayLinkStop() stops the CVDisplayLink thread.
+            if (GlassDisplayLink != NULL && CVDisplayLinkIsRunning(GlassDisplayLink))
+            {
+                CVReturn ret = CVDisplayLinkStop(GlassDisplayLink);
+                if (ret == kCVReturnSuccess)
+                {
+                    timer->_paused = YES;
+                }
+                else
+                {
+                    NSLog(@"ERROR: MacTimer__1pause(): CVDisplayLinkStop() error: %d\n", (int)ret);
+                }
+            }
+            else
+            {
+                NSLog(@"ERROR: MacTimer__1pause(): GlassDisplayLink is NULL or GlassDisplayLink is not running.");
+            }
+        }
+        else
+        {
+            timer->_paused = YES;
+        }
+    }
+    GLASS_POOL_EXIT;
+    GLASS_CHECK_EXCEPTION(env);
+}
+
+
+/*
+ * Class:     com_sun_glass_ui_mac_MacTimer
+ * Method:    _resume
+ * Signature: (J)V
+ */
+JNIEXPORT void JNICALL Java_com_sun_glass_ui_mac_MacTimer__1resume
+(JNIEnv *env, jclass cls, jlong jTimerPtr)
+{
+    LOG("Java_com_sun_glass_ui_mac_MacTimer__1resume");
+
+    GLASS_ASSERT_MAIN_JAVA_THREAD(env);
+    GLASS_POOL_ENTER;
+    {
+        GlassTimer *timer = (GlassTimer*)jlong_to_ptr(jTimerPtr);
+        if (timer->_period == 0)
+        {
+            // Start the CVDisplayLink thread. There is no API to resume the CVDisplayLink
+            // thread. CVDisplayLinkStart() starts a new CVDisplayLink thread.
+            if (GlassDisplayLink != NULL && !CVDisplayLinkIsRunning(GlassDisplayLink))
+            {
+                CVReturn ret = CVDisplayLinkStart(GlassDisplayLink);
+                if (ret == kCVReturnSuccess)
+                {
+                    timer->_paused = NO;
+                }
+                else
+                {
+                    NSLog(@"ERROR: MacTimer__1resume(): CVDisplayLinkStart error: %d\n", (int)ret);
+                }
+            }
+        }
+        else
+        {
+            timer->_paused = NO;
+        }
+    }
+    GLASS_POOL_EXIT;
+    GLASS_CHECK_EXCEPTION(env);
+}
+
+/*
+ * Class:     com_sun_glass_ui_mac_MacTimer
  * Method:    _getMinPeriod
  * Signature: ()I
  */