Incresing control performance trough multithreading, Design Question

Hello fellow monkeys.

I am pretty new to this whole game programming, but i am learning a lot.

I know that the main update() routinge of jMonkey cannot be multithreaded. However since the whole calculations theoretically could be multithreaded i tryed to implement a solution.

The goal of this is to do the heavy calculations in a preUpdate function(inside the Controls), the results are stored in a ArrayList. In the main update() routine all left is to parse trough the jobsForUpdate list and execute the jobs.

The design looks like this:



Executor Application State:

[java]

/*

  • To change this template, choose Tools | Templates
  • and open the template in the editor.

    */

    package mygame.ApplicationStates;



    import com.jme3.app.Application;

    import com.jme3.app.SimpleApplication;

    import com.jme3.app.state.AbstractAppState;

    import com.jme3.app.state.AppStateManager;

    import java.util.ArrayList;

    import java.util.List;

    import java.util.concurrent.Callable;

    import java.util.concurrent.ExecutorService;

    import java.util.concurrent.Executors;

    import java.util.concurrent.Future;

    import java.util.logging.Level;

    import java.util.logging.Logger;



    /**

    *
  • @author zzuegg

    /

    public class Executor extends AbstractAppState {



    SimpleApplication core;

    private static final Logger logger = Logger.getLogger(CoreSystemEnvironment.class.getName());

    ExecutorService executor;

    List<Callable<Object>> jobs;





    @Override

    public void initialize(AppStateManager stateManager, Application app) {

    super.initialize(stateManager, app);

    this.core = (SimpleApplication) app;

    executor = Executors.newFixedThreadPool(2);

    jobs=new ArrayList<Callable<Object>>();



    }



    @Override

    public void update(float tpf) {

    try {

    this.executor.invokeAll(this.jobs);

    } catch (InterruptedException ex) {

    logger.log(Level.SEVERE, null, ex);

    }

    }



    public void addJob(Callable job){

    this.jobs.add(job);

    }



    public Future executeJob(Callable job){

    return(this.executor.submit(job));

    }



    }

    [/java]



    The unitControl looks like this:

    [java]

    /

  • To change this template, choose Tools | Templates
  • and open the template in the editor.

    */

    package mygame.Controls;



    import com.jme3.export.JmeExporter;

    import com.jme3.export.JmeImporter;

    import com.jme3.renderer.RenderManager;

    import com.jme3.renderer.ViewPort;

    import com.jme3.scene.Spatial;

    import com.jme3.scene.control.Control;

    import java.io.IOException;

    import java.util.concurrent.Callable;



    /**

    *
  • @author zzuegg

    */

    public class unitControl implements Control{



    public Callable<Void> preUpdate = new Callable<Void>() {



    public Void call() throws Exception {

    return null;

    }



    };



    public Control cloneForSpatial(Spatial spatial) {

    throw new UnsupportedOperationException(“Not supported yet.”);

    }



    public void setSpatial(Spatial spatial) {

    throw new UnsupportedOperationException(“Not supported yet.”);

    }



    public void setEnabled(boolean enabled) {

    throw new UnsupportedOperationException(“Not supported yet.”);

    }



    public boolean isEnabled() {

    throw new UnsupportedOperationException(“Not supported yet.”);

    }



    public void update(float tpf) {

    throw new UnsupportedOperationException(“Not supported yet.”);

    }



    public void render(RenderManager rm, ViewPort vp) {

    throw new UnsupportedOperationException(“Not supported yet.”);

    }



    public void write(JmeExporter ex) throws IOException {

    throw new UnsupportedOperationException(“Not supported yet.”);

    }



    public void read(JmeImporter im) throws IOException {

    throw new UnsupportedOperationException(“Not supported yet.”);

    }



    }

    [/java]



    Finally i would attach a new Control with following code:

    [java]

    Spatial tmp=null;

    tmp.addControl(new unitControl());

    this.core.getStateManager().getState(Executor.class).addJob(tmp.getControl(unitControl.class).preUpdate);

    [/java]



    As long as i get only values from other objects and not set them, and only submit jobs to the ‘own’ object i should be thread safe.

    However, since the calculations in the preUpdate() function can include calls to bulletAppState (For detecting if an object is in range) of to the camera (to prepare LOD action) i am not sure if such a design is possible.



    What do you think? Good concept or am i totally of roads?

Well what i would propose instead of full parralel, is a fork join system



lets say for example tick = 50ms


  1. synchronizing point, synchronizse your logic and your rendering data here,(simple solution copy entire scenegraph to logicsystem, and apply changes from last ticks logik to rendering scengraph

    2.1 now process the logic in whatever way you want for one tick

    2.2 also render meanwhile images for the same tick, interpolate movement between frames so the whole game feels smooth



    As far as I know the jbullet binding uses that way, the only problem with this is, that you always see everything done in the logic 50ms delayed. Also the synchronizing between both frames could be a little problematic. If there are some logic that is needeng more thatn 50ms the whole game will slow down, but feel mostly smooth still. For extreme long needing logic, (like high level AI processing) it would be usefull to not wait for them on the enxt synchronzied point, but synchronized them the next tick after they are finished.





    Basic flow is

    Tick 0

    Tick 1

    render tick 0, process logic for tick 1

    copy positions/rotations ect from logic to rendering

    Tick 2

    render tick1, process tick 2

Yeah, basically you should do multithreading like its outlined in the wiki/manual. The update loop itself will later be multithreaded in parts where it makes sense (e.g. animation update etc).

EmpirePhoenix said:
Well what i would propose instead of full parralel, is a fork join system

lets say for example tick = 50ms

1. synchronizing point, synchronizse your logic and your rendering data here,(simple solution copy entire scenegraph to logicsystem, and apply changes from last ticks logik to rendering scengraph
2.1 now process the logic in whatever way you want for one tick
2.2 also render meanwhile images for the same tick, interpolate movement between frames so the whole game feels smooth

As far as I know the jbullet binding uses that way, the only problem with this is, that you always see everything done in the logic 50ms delayed. Also the synchronizing between both frames could be a little problematic. If there are some logic that is needeng more thatn 50ms the whole game will slow down, but feel mostly smooth still. For extreme long needing logic, (like high level AI processing) it would be usefull to not wait for them on the enxt synchronzied point, but synchronized them the next tick after they are finished.


Basic flow is
Tick 0
Tick 1
render tick 0, process logic for tick 1
copy positions/rotations ect from logic to rendering
Tick 2
render tick1, process tick 2


Pretty interesting approach. I need to think over this to get an idea for a possible implementation.

normen said:
Yeah, basically you should do multithreading like its outlined in the wiki/manual. The update loop itself will later be multithreaded in parts where it makes sense (e.g. animation update etc).


I do not like the multithreading way like outlined in the manual, simply beause the 'its done when it's done' prinziple. Depending on the cpu speed, the cost for the calculations there is the possiblilty that the calculation thread need a very long time for finnishing. At least it looks that way. With the above solution even on a slow cpu and heavy calculations the result is here within the same frame. (Of course framerate might drop)

BTW, is there an easy way to know if the CPU or the GPU is the limiting factor? Would be interesting

Thank you both

Whats the point of doing multithreading when you block the update loop anyway? You will have to handle this somehow. The outlined solution shows you how you can still do the logic part on the update loop and then decide what you draw and what not (e.g. stuff popping in, a loading screen or whatever). If you block the update loop your app is “dead” to the user. Also the outlined version does not force you do do stuff over the course of many frames, you can also check after rendering, or just update all controls that can be updated in parallel in parallel (which is what we intend to do as I said).

The point is that i can do the calculations for all my units ‘simultaneously’ instead of sequentlial as in the update loop

See my updates above. You can use the outlined way of multithreading for that.

Think like this (simplified):

->update loop

->call all controls updates simultaneously (make Callables, give them to the excutor and get the futures)

->get the return value of all futures (will return when the Callables are all finished)

->continue rendering



This is what we’ll do and any contributions on that front will probably be turned down, this topic causes heated discussions in the team as it is anyway.

Isn’t the updateControl() function automatically called every frame?

normen said:
See my updates above. You can use the outlined way of multithreading for that.
Think like this (simplified):
->update loop
->call all controls updates simultaneously (make Callables, give them to the excutor and get the futures)
->get the return value of all futures (will return when the Callables are all finished)
->continue rendering

This is what we'll do and any contributions on that front will probably be turned down, this topic causes heated discussions in the team as it is anyway.


The above solution does basically the same, but in a other sequenze.
->Start updateing appstates
->Call callable for each control registered (Dooing calculations)
->Finnish updating appstates
->Start updateControls (Where we can apply our results from the callable to the scengrapf
->render

to clarify maybe, in the paralell preUpdate call i want to make only calculations which are needed every frame (but could nicely get calculated paralell). I perfectly understand that for things which takes more time (Like a progress bar) the outlined solution in the tutorials is necessary



As far i understand, updates to the scenegraphs are not possible from within threads. So my idea was to precalculate the actions needed in paralell and then in the main update loop apply the actions at a very low cost (needed since the main update loop is only single threaded)

…which boils down to all the solutions we already outline, yes. You have the enqueue() to “apply the actions” and you have the Callables and Futures to manage the state of the Threads. Its not like this is our idea of this, its the official java API for threading.

Indeed, i never said that should be something implemented in the core, or was ment as contribution, all i asked is if such approach works together with the update loop of jMonkey.

Maybe for the experiend programmes this is trivial, but for me as a multithreading and game programming newbie this is new land.

Yes, its cool. I intend to write up some “Threading for Dummies” some time so the basics for understanding the issues with threading (and what it actually means) become clearer to beginners. Threading is never really trivial, most programmers go through various phases of “ok I got this – my, this is complicated – omg! – ok, got it… just be careful – mhoa, whyy?” :slight_smile:



Personally, to me the most important thing is keeping the logic straight, which is easiest when theres absolutely no global logic on the other threads, only processing. This way also makes it easier to parallelize the external tasks, as they work on their own little payload solely anyway. Imagine instead of a line a line that splits itself up on the way but then rejoins the strings again some time… If you keep going with this pattern in every little moment you can you effectively get a massive amount of parallel lines but still have only one logic string (along the direction of the line).

1 Like