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

[Feature Request] Split mainLoopIteration() methods #577

Open
AndreasLrx opened this issue Jul 16, 2022 · 7 comments · May be fixed by #580
Open

[Feature Request] Split mainLoopIteration() methods #577

AndreasLrx opened this issue Jul 16, 2022 · 7 comments · May be fixed by #580

Comments

@AndreasLrx
Copy link

Currently mainLoopIteration methods (for the different platform applications) does multiple actions that could be split in 3 other methods:

  • handleEvents: any events including the window close check
  • update: tickEvent(), when available (like SDL)
  • draw: all drawing stuff (and associated drawEvent)

This would allow a better control when making custom game loop.

@mosra
Copy link
Owner

mosra commented Jul 16, 2022

Sure, I'm not opposed to this idea. But first I'd like to know more about your use case, and what exactly is hard / impossible to achieve with the current API.

@AndreasLrx
Copy link
Author

I'm starting a small game engine for personnal use (mostly as a learning project) with Magnum for rendering/physics functionalities.

Looking at the game loop of Magnum applications, you handle the update going faster than expected minimalPeriod (with a delay). By doing something like this:

while (true)
{
  double start = getCurrentTime();
  processInput();
  update();
  render();

  sleep(start + MS_PER_UPDATE- getCurrentTime());
}

However you do not handle the opposite case: update taking more time than expected. This case could be handled like this:

double previous = getCurrentTime();
double lag = 0.0;
while (true)
{
  double current = getCurrentTime();
  double elapsed = current - previous;
  previous = current;
  lag += elapsed;

  processInput();

  if (lag > MS_PER_UPDATE * 10)
    lag = MS_PER_UPDATE * 10;
  while (lag >= MS_PER_UPDATE)
  {
    update();
    lag -= MS_PER_UPDATE;
  }

  render();
}

This is a possible way to manage lagging but isn't the only one. Allowing separated functions call like I suggested above would allow users to handle it the way they want.

(For more informations see https://gameprogrammingpatterns.com/game-loop.html)

@mosra
Copy link
Owner

mosra commented Jul 16, 2022

I see, that makes a lot of sense, thanks!

How about this:

  • I break the mainLoopIteration() into three pieces, mainLoopEventIteration(), mainLoopTickEventIteration() and mainLoopDrawEventIteration()
  • Each of those would contain also the logic like "don't call into drawEvent() if nothing was drawn", "don't call into tickEvent() if it's not implemented", so you don't need to do that yourself.
  • And the mainLoopIteration() would itself call into these three and only handle the delays on top. So if you don't use it, handling the delays would be completely your responsibility.

@AndreasLrx
Copy link
Author

Yes this sounds great ;)

@AndreasLrx AndreasLrx linked a pull request Aug 6, 2022 that will close this issue
@mosra mosra linked a pull request Aug 29, 2022 that will close this issue
AndreasLrx added a commit to AndreasLrx/magnum that referenced this issue Sep 12, 2022
AndreasLrx added a commit to AndreasLrx/magnum that referenced this issue Sep 12, 2022
AndreasLrx added a commit to AndreasLrx/magnum that referenced this issue Sep 12, 2022
AndreasLrx added a commit to AndreasLrx/magnum that referenced this issue Sep 12, 2022
@mosra
Copy link
Owner

mosra commented Sep 18, 2022

Sorry for going back to the drawing board but ... while attempting to merge #580 I realized the new split workflow adds a lot of potential for errors and the set of states that need to be handled is quite hard to get right on the app side. Even documenting the process is rather complex.

So, going back to your example snippets above, why wouldn't something like this work as well, without splititng anything? Assuming you don't call setMinimalLoopPeriod() and rely just on VSync to avoid the sleep(), it's doing exactly the same thing as yours, as far as I can see:

double previous = getCurrentTime();
double lag = 0.0;

YourApplication::tickEvent() {
  if (lag > MS_PER_UPDATE * 10)
    lag = MS_PER_UPDATE * 10;
  while (lag >= MS_PER_UPDATE)
  {
    update();
    lag -= MS_PER_UPDATE;
  }
}

while(true) {
  double current = getCurrentTime();
  double elapsed = current - previous;
  previous = current;
  lag += elapsed;

  // does the following: 
  //   processInput();
  //   tickEvent();
  //   render();
  app.mainLoopIteration();
}

Or am I missing something? The point here is that if you need to perform updates at regular intervals, you do it yourself in the tickEvent().

@AndreasLrx
Copy link
Author

No worries, I don't want to cause any regressions 😉

Your idea totally works, I don't know why I haven't thought of it before. It would have saved time to both of us 😅

@mosra
Copy link
Owner

mosra commented Sep 19, 2022

Yay! I'll salvage the GLFW tickEvent() addition from #580 at least, it's not like your whole work would go to waste ;) Thanks for that!

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 a pull request may close this issue.

2 participants