Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow specifying fps on HTML platform #36

Closed
wants to merge 2 commits into from

Conversation

kalmard0
Copy link
Contributor

@kalmard0 kalmard0 commented Jul 4, 2016

While letting the browser control frame render times is a nicer solution, setting a lower fps manually results in your CPU not overheating if rendering is slower than 60 fps, which is ultimately pretty useful.

  • some cleanup of dead code

@samskivert
Copy link
Contributor

Apologies for the major delay on this. But all of this is unnecessary. PlayN just emits a "frame" signal 60 times a second (or whatever the browser manages with requestAnimationFrame). The default implementation of Game (i.e. SceneGame) happens to always update and paint the game on that signal but you certainly don't have to do that. You can skip every other frame if you like and render at 30fps.

It's true that your patch allows totally arbitrary frame times to be specified, but no one does that. Games render at 30fps or 60fps and that's pretty much it (or 90 if you're doing VR but I don't think anyone's doing VR with PlayN). So I'd rather keep things simple and just have games skip every other frame if they want a lower FPS.

@samskivert samskivert closed this Sep 5, 2016
@kalmard0
Copy link
Contributor Author

kalmard0 commented Sep 5, 2016

I disagree. What I found was that in Chrome if you skip a frame, it will still clear the framebuffer and thus render a black frame. But maybe I was testing wrong.

@samskivert
Copy link
Contributor

samskivert commented Sep 5, 2016

You must have been testing wrong. SceneGame.paintScene both clears the framebuffer and redraws the scene graph:

  protected void paintScene () {
    viewSurf.saveTx();
    viewSurf.begin();
    viewSurf.clear(cred, cgreen, cblue, calpha);
    try {
      rootLayer.paint(viewSurf);
    } finally {
      viewSurf.end();
      viewSurf.restoreTx();
    }
  }

SceneGame listens to the paint signal emitted by Game. You can halve the paint frequency by overriding paint in your Game/SceneGame subclass:

  private int frame = 0;
  @Override public void paint (Clock clock) {
    if (++frame % 2 == 0) super.paint(clock);
  }

However, I did just test that and discover that the LWJGL3 backend (when you're testing with Java) does flicker if you take this approach (or behave super weirdly if you skip an odd number of frames). That's because it double buffers and if you skip every other frame then you basically never render into one of the two buffers. To reduce the framerate on the Java backend, we actually have to provide a configuration setting so that we can tell LWJGL to wait two frames before swapping buffers and syncing (and you would not do the paint override that I described above).

So if I did want to support 30 fps games, I'd probably provide an API that allowed you to specify the number of frames to wait before swapping buffers (like the API used by the Java backend). This would not allow you to specify an arbitrary FPS, but would allow you to halve, (or third, fourth, fifth, etc. but that would be pretty crazy) the framerate by requesting that paints happen every other frame. This ensures that the paints happen in line with the vsync which is usually necessary to avoid tearing (half of a frame showing up and half of an old frame).

But if you are just halving the frame-rate on the HTML5 backend, then I'd do something like the following:

public class MyGame extends SceneGame {
  private final int frameSkip;
  private int skippedFrames = 0;

  public MyGame (Platform plat, int frameSkip) {
    super(plat);
    this.frameSkip = frameSkip;
  }

  @Override public void paint (Clock clock) {
    if (skippedFrames < frameSkip) {
      skippedFrames++;
    } else {
      super.paint(clock);
      skippedFrames = 0;
    }
  }
}

Then you can pass in a frameSkip of 1 from your HTML backend, and 0 from your Java backend, and you'll run at 30FPS in HTML and 60FPS in Java.

@kalmard0
Copy link
Contributor Author

kalmard0 commented Sep 6, 2016

You are right of course, it works in chrome afterall. One small note for future reference: it is a good idea to change the dt value of the Clock object passed to the paint Signal, to accomodate for skipped frames.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants