diff --git a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/gtk/GtkApplication.java b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/gtk/GtkApplication.java index f2a33641fd..b7bebbaa98 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/gtk/GtkApplication.java +++ b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/gtk/GtkApplication.java @@ -397,11 +397,11 @@ final class GtkApplication extends Application implements @Override protected native int staticTimer_getMaxPeriod(); - @Override protected double staticScreen_getVideoRefreshPeriod() { - return 0.0; // indicate millisecond resolution - } + @Override + protected native double staticScreen_getVideoRefreshPeriod(); - @Override native protected Screen[] staticScreen_getScreens(); + @Override + native protected Screen[] staticScreen_getScreens(); @Override protected FileChooserResult staticCommonDialogs_showFileChooser( diff --git a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/gtk/GtkTimer.java b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/gtk/GtkTimer.java index be63a67333..74103b6a4f 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/gtk/GtkTimer.java +++ b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/gtk/GtkTimer.java @@ -32,9 +32,8 @@ final class GtkTimer extends Timer{ super(runnable); } - @Override protected long _start(Runnable runnable) { - throw new RuntimeException("vsync timer not supported"); - } + @Override + protected native long _start(Runnable runnable); @Override protected native long _start(Runnable runnable, int period); diff --git a/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/GlassScene.java b/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/GlassScene.java index e623060b30..021535334d 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/GlassScene.java +++ b/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/GlassScene.java @@ -65,6 +65,7 @@ abstract class GlassScene implements TKScene { private volatile boolean entireSceneDirty = true; private boolean doPresent = true; + private boolean doVSync = false; private final AtomicBoolean painting = new AtomicBoolean(false); private final boolean depthBuffer; @@ -305,6 +306,14 @@ abstract class GlassScene implements TKScene { return doPresent; } + public final synchronized void setDoVSync(boolean value) { + doVSync = value; + } + + public final synchronized boolean getDoVsync() { + return doVSync; + } + protected Color getClearColor() { WindowStage windowStage = stage instanceof WindowStage ? (WindowStage)stage : null; if (windowStage != null && windowStage.getPlatformWindow() != null && diff --git a/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/PaintCollector.java b/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/PaintCollector.java index e6c538bc5d..991e5e828e 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/PaintCollector.java +++ b/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/PaintCollector.java @@ -434,10 +434,14 @@ final class PaintCollector implements CompletionListener { if (!needsHint) { needsHint = gs.isSynchronous(); } + } + + for (final GlassScene gs : dirtyScenes) { // On platforms with a window manager, we always set doPresent = true, because // we always need to rerender the scene if it's in the dirty list and we do a // swap on a per-window basis gs.setDoPresent(true); + gs.setDoVSync(needsHint && dirtyScenes.getLast() == gs); try { gs.repaint(); } catch (Throwable t) { diff --git a/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/PresentingPainter.java b/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/PresentingPainter.java index c2895de7bc..94e1618343 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/PresentingPainter.java +++ b/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/PresentingPainter.java @@ -104,7 +104,7 @@ final class PresentingPainter extends ViewPainter { /* present for vsync buffer swap */ if (vs.getDoPresent()) { - if (!presentable.present()) { + if (!presentable.present(vs.getDoVsync())) { disposePresentable(); sceneState.getScene().entireSceneNeedsRepaint(); } diff --git a/modules/javafx.graphics/src/main/java/com/sun/prism/Presentable.java b/modules/javafx.graphics/src/main/java/com/sun/prism/Presentable.java index 8fc4c29016..75367a4d74 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/prism/Presentable.java +++ b/modules/javafx.graphics/src/main/java/com/sun/prism/Presentable.java @@ -57,6 +57,10 @@ public interface Presentable extends RenderTarget { */ public boolean present(); + public default boolean present(boolean vsync) { + return present(); + } + public float getPixelScaleFactorX(); public float getPixelScaleFactorY(); } diff --git a/modules/javafx.graphics/src/main/java/com/sun/prism/es2/ES2SwapChain.java b/modules/javafx.graphics/src/main/java/com/sun/prism/es2/ES2SwapChain.java index b7b074d72b..886d90e0ce 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/prism/es2/ES2SwapChain.java +++ b/modules/javafx.graphics/src/main/java/com/sun/prism/es2/ES2SwapChain.java @@ -184,7 +184,17 @@ class ES2SwapChain implements ES2RenderTarget, Presentable, GraphicsResource { @Override public boolean present() { boolean presented = drawable.swapBuffers(context.getGLContext()); - context.makeCurrent(null); + //context.makeCurrent(null); no need to null this here + return presented; + } + + @Override + public boolean present(boolean vsync) { + boolean presented = present(); + + if (vsync) + context.getGLContext().finish(); + return presented; } @@ -308,6 +318,7 @@ class ES2SwapChain implements ES2RenderTarget, Presentable, GraphicsResource { } if (drawable != null) { + context.makeCurrent(null); drawable.dispose(); drawable = null; } diff --git a/modules/javafx.graphics/src/main/native-glass/gtk/GlassApplication.cpp b/modules/javafx.graphics/src/main/native-glass/gtk/GlassApplication.cpp index a9e84cd155..4486adc024 100644 --- a/modules/javafx.graphics/src/main/native-glass/gtk/GlassApplication.cpp +++ b/modules/javafx.graphics/src/main/native-glass/gtk/GlassApplication.cpp @@ -362,6 +362,24 @@ JNIEXPORT jint JNICALL Java_com_sun_glass_ui_gtk_GtkApplication_staticTimer_1get return 10000; // There are no restrictions on period in g_threads } +/* + * Class: com_sun_glass_ui_gtk_GtkApplication + * Method: staticScreen_getVideoRefreshPeriod + * Signature: ()D + */ +JNIEXPORT jdouble JNICALL Java_com_sun_glass_ui_gtk_GtkApplication_staticScreen_1getVideoRefreshPeriod + (JNIEnv * env, jobject obj) +{ + (void)env; + (void)obj; + + GdkMonitor *m = gdk_display_get_primary_monitor(gdk_display_get_default()); + + int rr = gdk_monitor_get_refresh_rate(m); + + return 1.0E6 / rr; +} + /* * Class: com_sun_glass_ui_gtk_GtkApplication * Method: staticView_getMultiClickTime diff --git a/modules/javafx.graphics/src/main/native-glass/gtk/GlassTimer.cpp b/modules/javafx.graphics/src/main/native-glass/gtk/GlassTimer.cpp index ff31b55b0d..1f5d219bb5 100644 --- a/modules/javafx.graphics/src/main/native-glass/gtk/GlassTimer.cpp +++ b/modules/javafx.graphics/src/main/native-glass/gtk/GlassTimer.cpp @@ -28,27 +28,112 @@ #include #include #include - -static gboolean call_runnable_in_timer - (gpointer); +#include extern "C" { -/* - * Class: com_sun_glass_ui_gtk_GtkTimer - * Method: _start - * Signature: (Ljava/lang/Runnable;I)J - */ -JNIEXPORT jlong JNICALL Java_com_sun_glass_ui_gtk_GtkTimer__1start - (JNIEnv * env, jobject obj, jobject runnable, jint period) +typedef struct TimerContext { + jobject runnable; + struct timespec start; + uint64_t frame; + int refresh_rate; + // 0 = gdk_thread_timeout + // 1 = gdk_thread_timeout stop + // 2 = vsync thread + // 3 = vsync thread stop + // 4 = vsync thread failed + volatile int flag; +} TimerContext; + +static void *_GlassTimerTask(void *data) { - (void)obj; + TimerContext* context = (TimerContext*) data; + + JNIEnv *env; + jint err = javaVM->AttachCurrentThreadAsDaemon((void **)&env, NULL); + + printf("Start glass timer thread: rate=%6.3f\n", context->refresh_rate * 1.0E-3); fflush(stdout); + + if (err == 0) + { + int64_t frame = 1; + + while (context->flag == 2) { + struct timespec time; + + clock_gettime(CLOCK_REALTIME, &time); + + int64_t current = ((time.tv_sec - context->start.tv_sec) * 1000000 + time.tv_nsec / 1000) - context->start.tv_nsec / 1000; + int64_t target = ((frame * 1000000000 / context->refresh_rate)); + int delay; + + if (target < current) { + frame += (current - target) * context->refresh_rate / 1000000000 + 1; + delay = 0; + } else { + delay = target - current; + frame++; + } + + usleep(delay); + + if (context->flag == 2) + { + env->CallVoidMethod(context->runnable, jRunnableRun, NULL); + LOG_EXCEPTION(env); + } + + //printf(" %8ld: %ld %ld %9.6f\n", frame, current, target, (target-current) * 1.0E-6); + } + + env->DeleteGlobalRef(context->runnable); + context->runnable = NULL; + free(context); + } else { + ERROR0("GlassTimer: Unable to attach JNIEnv\n"); + + // FIXME: races + context->flag = 4; + env->DeleteGlobalRef(context->runnable); + context->runnable = NULL; + } + + printf("Exit glass timer thread\n"); + + return NULL; +} + +static jlong start_timer_task(JNIEnv *env, jobject runnable, int rate) { + TimerContext* context = (TimerContext*) malloc(sizeof(TimerContext)); - RunnableContext* context = (RunnableContext*) malloc(sizeof(RunnableContext)); if (context != NULL) { - context->runnable = env->NewGlobalRef(runnable); - context->flag = 0; - gdk_threads_add_timeout_full(G_PRIORITY_HIGH_IDLE, period, call_runnable_in_timer, context, NULL); + pthread_t thread; + pthread_attr_t attr; + + clock_gettime(CLOCK_REALTIME, &context->start); + + // detach thread, so its resources can be reclaimed as soon as it's finished + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); + + int err = pthread_create(&thread, &attr, _GlassTimerTask, context); + if (err == 0) + { + pthread_setname_np(thread, "Glass Timer Thread"); + + context->refresh_rate = rate; + context->runnable = env->NewGlobalRef(runnable); + context->flag = 2; + } + else + { + free(context); + context = NULL; + } + + pthread_attr_destroy(&attr); + return PTR_TO_JLONG(context); } else { // we throw RuntimeException on Java side when we can't @@ -59,45 +144,57 @@ JNIEXPORT jlong JNICALL Java_com_sun_glass_ui_gtk_GtkTimer__1start /* * Class: com_sun_glass_ui_gtk_GtkTimer - * Method: _stop - * Signature: (J)V + * Method: _start + * Signature: (Ljava/lang/Runnable;)J */ -JNIEXPORT void JNICALL Java_com_sun_glass_ui_gtk_GtkTimer__1stop - (JNIEnv * env, jobject obj, jlong ptr) +JNIEXPORT jlong JNICALL Java_com_sun_glass_ui_gtk_GtkTimer__1start__Ljava_lang_Runnable_2 + (JNIEnv *env, jobject obj, jobject runnable) { (void)obj; - RunnableContext* context = (RunnableContext*) JLONG_TO_PTR(ptr); - context->flag = 1; - env->DeleteGlobalRef(context->runnable); - context->runnable = NULL; -} + GdkMonitor *m = gdk_display_get_primary_monitor(gdk_display_get_default()); + int rr = gdk_monitor_get_refresh_rate(m); -} // extern "C" + printf("start 'vsync' timer: %d\n", rr); fflush(stdout); + return start_timer_task(env, runnable, rr); +} -static gboolean call_runnable_in_timer - (gpointer data) +/* + * Class: com_sun_glass_ui_gtk_GtkTimer + * Method: _start + * Signature: (Ljava/lang/Runnable;I)J + */ +JNIEXPORT jlong JNICALL Java_com_sun_glass_ui_gtk_GtkTimer__1start__Ljava_lang_Runnable_2I + (JNIEnv * env, jobject obj, jobject runnable, jint period) { - RunnableContext* context = (RunnableContext*) data; - if (context->flag) { - free(context); - return FALSE; - } - else if (context->runnable) { - JNIEnv *env; - int envStatus = javaVM->GetEnv((void **)&env, JNI_VERSION_1_6); - if (envStatus == JNI_EDETACHED) { - javaVM->AttachCurrentThread((void **)&env, NULL); - } + (void)obj; - env->CallVoidMethod(context->runnable, jRunnableRun, NULL); - LOG_EXCEPTION(env); + printf("start gdk timer: %d\n", period); fflush(stdout); - if (envStatus == JNI_EDETACHED) { - javaVM->DetachCurrentThread(); - } + return start_timer_task(env, runnable, 1000000 / period); +} + +/* + * Class: com_sun_glass_ui_gtk_GtkTimer + * Method: _stop + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_com_sun_glass_ui_gtk_GtkTimer__1stop + (JNIEnv * env, jobject obj, jlong ptr) +{ + (void)obj; + + TimerContext* context = (TimerContext*) JLONG_TO_PTR(ptr); + if (context->flag == 0) { + context->flag = 1; + env->DeleteGlobalRef(context->runnable); + context->runnable = NULL; + } else if (context->flag == 2) { + context->flag = 3; + } else if (context->flag == 4) { + free(context); } - return TRUE; } +} // extern "C"