Bug 555

Summary: Embedding NEWT canvas into Applet object doesn't allow to set framerate on OSX
Product: [JogAmp] Jogl Reporter: ac <andres.colubri>
Component: macosxAssignee: Sven Gothel <sgothel>
Status: RESOLVED FIXED    
Severity: normal CC: gouessej
Priority: ---    
Version: 2   
Hardware: pc_all   
OS: macosx   
Type: --- SCM Refs:
218d67fc0222d7709b21c45792d44501351939c4 9d522e77a9ac1f85c57236f00d5432e671f9169c
Workaround: ---
Attachments: Full source of the test application demonstrating the bug
Updated NEWT test application
Updated AWT test application
Patch file for MacOSXCGLContext.java
New patch for MacOSXCGLContext.java that skips waitUntilNSOpenGLLayerIsReady() when v-sync is disabled

Description ac 2012-02-20 16:05:27 CET
Created attachment 328 [details]
Full source of the test application demonstrating the bug

I have an application built on top of the AWT Applet class. I use NEWT for rendering, and the NewtCanvasAWT class for integrating NEWT with AWT.

I use a custom animator to control the frame rendering. But the framerate of my animator is not honored by the application. This happens only on MacOSX. The full source of my test application showing the issue is attached.

The target framerate is 120 in the test application, but it hovers around 60. This on a MacBook Pro with a NVidia 320M card and MacOSX Snow Leopard 10.6.8. I'm using the b496 and b666 autobuilds packages for gluegen and jogl, respectively.

Also, as a minor detail, the application prints this error:

2012-02-20 09:38:38.304 java[8961:903] invalid drawable

just one time right after it starts.
Comment 1 ac 2012-02-23 02:16:55 CET
Created attachment 330 [details]
Updated NEWT test application
Comment 2 ac 2012-02-23 02:19:28 CET
Created attachment 331 [details]
Updated AWT test application
Comment 3 ac 2012-02-23 03:42:15 CET
Just uploaded updated versions of the test app, using both AWT and NEWT. I added the gl.setSwapInterval(0); call to force disabling vsync, but framerate is still lower in OSX. Other platforms work fine.
Comment 4 ac 2012-03-15 20:52:07 CET
I wrote a small patch for the file jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLContext.java, which appears to solve the issue reported here.

The patch adds the variable vsyncTimeout to the class NSOpenGLImpl. The value of this variable is set to the appropriate timeout in ms needed to call waitUntilNSOpenGLLayerIsReady() in swapBuffers(), according to the desired swap interval. In this way, framerates higher than 60 can be used. Basically, if the user specifies a swap interval of zero, i.e.: v-sync disabled, then vsyncTimeout is set to 2, which corresponds to a theoretical maximum refresh rate of 480Mhz.  If the swap interval is equal or larger than 1, then the following formula is used:

vsyncTimeout = interval * 16;

which yields the correct timeout in milliseconds to use with framerates equal or lower than 60.

The downside of this approach is that the timeout interval for framerates higher than 60 is not accurate, since is set to 2ms regardless of the target framerate. I'd like to suggest the addition of a setFramerate method that should do the following:

public boolean setFramerate(int fps) {
    if(fps <= 60) {
        if(fps <= 15) {
          return setSwapInterval(3);
        } else if(fps <= 30) {
          return setSwapInterval(2);
        } else {
          return setSwapInterval(1);
        }
    } else {
        if(0 != nsOpenGLLayer) {
          CGL.setNSOpenGLLayerSwapInterval(nsOpenGLLayer, 0);
        }
        CGL.setSwapInterval(contextHandle, 0);              
        if(fps <= 120) {
          vsyncTimeout = 8; // 8ms -> 120Mhz
        } else if(fps <= 240) {
          vsyncTimeout = 4; // 4ms -> 240Mhz
        } else {
          vsyncTimeout = 2;
        }              
        return true;
    }      
 }

so that it provides finer granularity for framerates above 60.
Comment 5 ac 2012-03-15 20:53:32 CET
Created attachment 340 [details]
Patch file for MacOSXCGLContext.java
Comment 6 Julien Gouesse 2012-03-15 22:32:44 CET
(In reply to comment #4)
> I wrote a small patch for the file
> jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLContext.java, which appears to
> solve the issue reported here.
> 
> The patch adds the variable vsyncTimeout to the class NSOpenGLImpl. The value
> of this variable is set to the appropriate timeout in ms needed to call
> waitUntilNSOpenGLLayerIsReady() in swapBuffers(), according to the desired swap
> interval. In this way, framerates higher than 60 can be used. Basically, if the
> user specifies a swap interval of zero, i.e.: v-sync disabled, then
> vsyncTimeout is set to 2, which corresponds to a theoretical maximum refresh
> rate of 480Mhz.  If the swap interval is equal or larger than 1, then the
> following formula is used:
> 
> vsyncTimeout = interval * 16;
> 
> which yields the correct timeout in milliseconds to use with framerates equal
> or lower than 60.
> 
> The downside of this approach is that the timeout interval for framerates
> higher than 60 is not accurate, since is set to 2ms regardless of the target
> framerate. I'd like to suggest the addition of a setFramerate method that
> should do the following:
> 
> public boolean setFramerate(int fps) {
>     if(fps <= 60) {
>         if(fps <= 15) {
>           return setSwapInterval(3);
>         } else if(fps <= 30) {
>           return setSwapInterval(2);
>         } else {
>           return setSwapInterval(1);
>         }
>     } else {
>         if(0 != nsOpenGLLayer) {
>           CGL.setNSOpenGLLayerSwapInterval(nsOpenGLLayer, 0);
>         }
>         CGL.setSwapInterval(contextHandle, 0);              
>         if(fps <= 120) {
>           vsyncTimeout = 8; // 8ms -> 120Mhz
>         } else if(fps <= 240) {
>           vsyncTimeout = 4; // 4ms -> 240Mhz
>         } else {
>           vsyncTimeout = 2;
>         }              
>         return true;
>     }      
>  }
> 
> so that it provides finer granularity for framerates above 60.


I prefer your second suggestion. Nice job :)
Comment 7 ac 2012-03-18 23:35:25 CET
Actually, I think that, when v-sync is disabled, we should remove the waiting altogether, since we want to render frames as fast as possible... so I just disabled the waitUntilNSOpenGLLayerIsReady() call when the vsyncTimeout is zero (no v-sync). This appears to work fine on Mac, Linux and Windows. I'm attaching the new patch after this comment.
Comment 8 ac 2012-03-18 23:37:09 CET
Created attachment 345 [details]
New patch for MacOSXCGLContext.java that skips waitUntilNSOpenGLLayerIsReady() when v-sync is disabled
Comment 9 Sven Gothel 2012-04-22 05:13:41 CEST
Thank you Andres, I hope it works fine now.
Works with swap interval 0, 1, 2, ..