Main Loop questions

manu3d

Active Member
Contributor
Architecture
A couple of questions, presumably Immortius:

I am puzzled by the structure of main loop. I'm looking at the blocks of code after the handling of hibernation, that is what goes on when Terasology runs normally (line 497).

First the networkSystem updates, then the currentState updates. I guess it is in these two steps that most game-related things happen, either driven by a remote server or by the local client/server while playing standalone.

Then, shortly thereafter, the line GameThread.processWaitingProcesses(); is framed by two loops iterating over subsystems and activating their preUpdate() and postUpdate() methods, hinting to that line being "the" update subsystems care about.

What I don't understand is this:

1) why networkSystem and currentState updates do not occur -between- the preUpdate()/postUpdate() loops?
2) what is the processWaitingProcesses() line for when I cannot find usage anywhere in the project of the methods GameThread.synch()/asynch(), which I seem to understand submit processes into the list of Runnable to be processed?
 

Cervator

Org Co-Founder & Project Lead
Contributor
Design
Logistics
SpecOps
Been a little while now - did you get any clues via IRC or that question-PR on GitHub, manu3d ? Or still waiting for more? :)

If you're using "Find Usages" in something like IntelliJ that sometimes doesn't work when we're moving execution around through the use of annotations
 

manu3d

Active Member
Contributor
Architecture
No, no luck on this one yet. And I did use the CTRL+SHIFT+F search which seem to search everywhere in the project. Haven't tried via IRC. Will try via PR/Github eventually but I'm waiting for the outcome of my first PR, so I guess I shouldn't issue another one already?
 

Immortius

Lead Software Architect
Contributor
Architecture
GUI
1) why networkSystem and currentState updates do not occur -between- the preUpdate()/postUpdate() loops?
Network system's update is about sending/receiving entities and events, and is best done outside of the main logic update cycle so we have the most up to date information from the previous cycle, and don't have entities created and destroyed in the middle of the updating. It need before main logic and input processing. So it needs to be before currentState and preUpdate (which is where input processing occurs).

GameThread.processWaitingProcesses() can be moved about, so could be before or after the main updates (or both). Should be adjacent to the main updates I feel.

My understanding is that the preUpdate/postUpdate are used to do some basic ordering of when the subsystems are updated. Originally these subsystems were explicitly updated in a given order, so when it was made more generic preUpdate/postUpdate was introduced. It is a bit confusing though. Notably we're updating input after logic is run, which is potentially a bug - so we should in fact have the main update in between preUpdate and postUpdate.

2) what is the processWaitingProcesses() line for when I cannot find usage anywhere in the project of the methods GameThread.synch()/asynch(), which I seem to understand submit processes into the list of Runnable to be processed?
This is functionality that allows processes off the game thread to synch up with the main thread for actions that need to run there (working with the entity system or various assets). It isn't used in the engine, but exists to support the needs of modules.
 

manu3d

Active Member
Contributor
Architecture
Ok, thanks for the clarifications immortius. So, to summarize, it should be:
  • network update
  • subsystems pre-update
  • ?pre-update process queue processing?
  • game state update
  • ?post-update process queue processing?
  • subsystems post-update
Correct?
 

Immortius

Lead Software Architect
Contributor
Architecture
GUI
I think so, yes. Probably don't need two process queue processing, so would go with the pre-update one.
 

manu3d

Active Member
Contributor
Architecture
As I was looking again into this issue with a fresher mind, I noticed some problems. Please have a look at the code shown below.

From TerasologyEngine.java:
Code:
            Iterator<Float> updateCycles = time.tick();
 
            long totalDelta = 0;
            while (updateCycles.hasNext()) {
                float delta = updateCycles.next();  // gameTime gets updated here!
                totalDelta += time.getDeltaInMs();
                PerformanceMonitor.startActivity("Main Update");
                currentState.update(delta);  // gameState gets updated here!
                PerformanceMonitor.endActivity();
            }
            // gameTime and gameState are updated more than once per main loop.
 
            float delta = totalDelta / 1000f;
 
            // subsystems are pre/post updated only once per main loop, as are waiting processes.
            for (EngineSubsystem subsystem : getSubsystems()) {
                PerformanceMonitor.startActivity(subsystem.getClass().getSimpleName());
                subsystem.preUpdate(currentState, delta);
                PerformanceMonitor.endActivity();
            }
 
            GameThread.processWaitingProcesses();
 
            for (EngineSubsystem subsystem : getSubsystems()) {
                PerformanceMonitor.startActivity(subsystem.getClass().getSimpleName());
                subsystem.postUpdate(currentState, delta);
                PerformanceMonitor.endActivity();
            }
Specifically, we were discussing about placing the update of the game state -between- the subsystems pre/post updates and after the processing of waiting processes. However, with the current code structure, game time and game state are updated more frequently, per main loop, than the subsystems. And the subsystem pre/postUpdate methods are provided with an identical, post game-update delta.

So, the question is: should the subsystems get updated at the same frequency as the game state, by getting the two for loops included in the while loop, or should the preUpdate block be moved before the while loop, with subsystems getting pre/post updated at the current frequency, once per main loop? And in either hypothesis, what delta should be provided to preUpdate()? Zero?
 

Immortius

Lead Software Architect
Contributor
Architecture
GUI
Specifically, we were discussing about placing the update of the game state -between- the subsystems pre/post updates and after the processing of waiting processes. However, with the current code structure, game time and game state are updated more frequently, per main loop, than the subsystems. And the subsystem pre/postUpdate methods are provided with an identical, post game-update delta.
This whole preUpdate/postUpdate thing needs to be reviewed. As previously stated, it is a rough system for ordering updates within the subsystems, and isn't supposed to mean before game logic/after game logic as such.

On the updates, yes the game state *can* (but not always) be updated multiple times per frame, but for the same total delta as the subsystems. Updates will be split into multiple runs if the delta is too large, to prevent issues caused by low temporal resolution. Subsystems don't have this issue, because they are for things like audio and rendering.

or should the preUpdate block be moved before the while loop, with subsystems getting pre/post updated at the current frequency, once per main loop? And in either hypothesis, what delta should be provided to preUpdate()? Zero?
First check whether we need pre/postUpdate, or whether subsystems can just have an update() method and correct ordering can be obtained by the order of the subsystems.
 

manu3d

Active Member
Contributor
Architecture
Thanks for the clarification Immortius.

You mention subsystems do not have the problem of low temporal resolution. Wild thought: wouldn't the input subsystem benefit from higher temporal resolution? For higher responsiveness?

First check whether we need pre/postUpdate, or whether subsystems can just have an update() method and correct ordering can be obtained by the order of the subsystems.
With "check" and "correct ordering" do you mean to check the various subsystems if they have dependencies on each other and the game state and therefore come up with a "correct" ordering? Isn't that something that should be dictated by the engine rather than empirically found? I might find the correct order for the PC facade and the subsystems list it provides, but what if a different facade provides entirely new subsystems? In this context I thought pre/postUpdate calls were a pretty smart thing to have once it is explicitly declared (source code/docs) when they are called in the context of the main loop. Perhaps I'm not understanding correctly.
 

Immortius

Lead Software Architect
Contributor
Architecture
GUI
Thanks for the clarification Immortius.

You mention subsystems do not have the problem of low temporal resolution. Wild thought: wouldn't the input subsystem benefit from higher temporal resolution? For higher responsiveness
That's managed within the input system. Besides which, lwjgl's input updates are tied to display updates so you can't actually cycle it any faster.

With "check" and "correct ordering" do you mean to check the various subsystems if they have dependencies on each other and the game state and therefore come up with a "correct" ordering? Isn't that something that should be dictated by the engine rather than empirically found? I might find the correct order for the PC facade and the subsystems list it provides, but what if a different facade provides entirely new subsystems? In this context I thought pre/postUpdate calls were a pretty smart thing to have once it is explicitly declared (source code/docs) when they are called in the context of the main loop. Perhaps I'm not understanding correctly.
I mean - does any system actually do anything in both preUpdate and postUpdate? If not, why can't the facade just install them in a correct order? In what situations does the order matter in the first instance - it shouldn't matter if audio and display are updated before or after each other I wouldn't think. Those sorts of questions.
 

Mike Kienenberger

Active Member
Contributor
Architecture
GUI
When we started, we had a single update() method, which wasn't sufficient.

preUpdate() is used in addition to postUpdate() for LwjglInput.java to deal with mouse grabbing.
I do not know if it might be possible to rewrite things in such a way as to remove it. Someone more knowledgable than I would have to take a look, but I suspect I tried putting it in one update method without success.

Code:
        // TODO: this originally occurred before GameThread.processWaitingProcesses();
        boolean newGrabbed = engine.hasMouseFocus() && !(nuiManager.isReleasingMouse());
        if (newGrabbed != mouseGrabbed) {
            Mouse.setGrabbed(newGrabbed);
            mouseGrabbed = newGrabbed;
        }
 
Top