[solved] input is delayed by 1 frame (when using setFrameRate(x) ie. x=1) ? jme3 r7361

Are you suggesting that maybe we should sense the input after the wait instead of before?

1 Like

the way I see it, setFrameRate uses some kind of delay (ie. sleep?) which happens after update() and during which if I do press some keys, they are not sensed on the next call to update(), they are sensed instead on the next after next call to update(), that is, the one after the next one

thus I would expect the following code to act the same way, but it doesn’t, in other words: this code does sense the key right on the next update, even though it sleeps right after update() is called

[java]package org.jme3.forum2;

import java.util.prefs.BackingStoreException;

import org.w3c.dom.events.MouseEvent;

import com.jme3.app.SimpleApplication;

import com.jme3.input.KeyInput;

import com.jme3.input.MouseInput;

import com.jme3.input.controls.ActionListener;

import com.jme3.input.controls.KeyTrigger;

import com.jme3.input.controls.MouseButtonTrigger;

import com.jme3.material.Material;

import com.jme3.math.ColorRGBA;

import com.jme3.scene.Geometry;

import com.jme3.scene.shape.Box;

import com.jme3.system.AppSettings;

public class Input1FrameLag extends SimpleApplication {

public static void main(String[] args) throws BackingStoreException {

Input1FrameLag i = new Input1FrameLag();

AppSettings aps = new AppSettings(true);

aps.load(aps.getTitle());

aps.setVSync(false);

i.setSettings(aps);

i.setShowSettings(false);

// aps.setFrameRate(1);

i.start();

}

private Geometry boxGeo;

private int frame = 1;

@Override

public void simpleInitApp() {

ActionListener al = new ActionListener() {

@Override

public void onAction(String name, boolean isPressed, float tpf) {

if ((name == “boom”) && (isPressed)) {

boxGeo.move(0, 0, -10);

boxGeo.getWorldTransform();// causes checkDoTransformUpdate

System.out.println(“Box moved!”);

}

}

};

inputManager.addMapping(“boom”, new KeyTrigger(KeyInput.KEY_SPACE),

new MouseButtonTrigger(MouseInput.BUTTON_RIGHT));

inputManager.addListener(al, “boom”);

Box boxMesh = new Box(2, 2, 2);

boxGeo = new Geometry(“box”, boxMesh);

Material boxMat = new Material(assetManager,

“Common/MatDefs/Misc/Unshaded.j3md”);

boxMat.getAdditionalRenderState().setWireframe(true);

boxMat.setColor(“Color”, ColorRGBA.Green);

boxGeo.setMaterial(boxMat);

rootNode.attachChild(boxGeo);

}

@Override

public void simpleUpdate(float tpf) {

// try {

// System.out.println(“frame:” + frame + " in simpleUpdate");

// Thread.sleep(2000);

// Thread.yield();

// } catch (InterruptedException e) {

// e.printStackTrace();

// }

}

@Override

public void update() {

// try {

System.out.print(“frame:” + frame + " before update begins");

// if (1 == frame) {

// System.out.println(" press SPACE now");

// } else {

System.out.println();

// }

// Thread.sleep(2000);

// Thread.yield();

// } catch (InterruptedException e) {

// e.printStackTrace();

// }

super.update();

try {

System.out.println(“frame:” + frame + " after update is done");

Thread.sleep(2000);

Thread.yield();

// Note: rendering only happens after this delay

} catch (InterruptedException e) {

e.printStackTrace();

}

frame++;

}

}

[/java]

upon further scooping around:

I guess it’s all obvious in here com.jme3.system.lwjgl.LwjglAbstractDisplay.runLoop():

[java]/**

  • execute one iteration of the render loop in the OpenGL thread

    */

    protected void runLoop(){

    if (!created.get())

    throw new IllegalStateException();

    listener.update();//XXX: this is our update()

    if (renderable.get()){

    assert checkGLError();

    // calls swap buffers, etc.

    try {

    if (autoFlush){

    Display.update();

    }else{

    Display.processMessages();

    Thread.sleep(50);

    // add a small wait

    // to reduce CPU usage

    }

    } catch (Throwable ex){

    listener.handleError(“Error while swapping buffers”, ex);

    }

    }

    if (frameRate > 0)

    Display.sync(frameRate);//XXX: this is the setFrameRate sleeper

    if (renderable.get() && autoFlush)

    renderer.onFrame();

    }[/java]

    that Display.update or that processMessages, both eventually call org.lwjgl.opengl.Display.pollDevices() which kinda sense the input (all that you pressed before reaching that call) - at least how I see it

    therefore since the setFrameRate delay happens in the Display.sync() call below that, then it’s only natural that the inputs are not sensed in the next frame (at least not before calling Application.update() ) , so only on the frame after the next frame update() can touch them

    EDIT: I also updated the topic title , added “when setFrameRate(1)”, because it apparently does delay input by 1 frame only in this case(when using setFrameRate with a low value?), although I was wrong for the initial case when I thought it was delayed all the time…

I don’t know, I’m kinda just saying or stating how it is now…

Wasn’t actually thinking of a solution, … let me see now,

the way it is now, we sense the input before the wait but also it’s after the update, which means, if we press a key while the wait happens, the next update won’t sense that, because the input is sensed after the update… erm ok i’m starting to not make sense (i sense :))



I guess I would try to Display.pollDevices() at right before inputManager is processing its own queue ? that is, before the update, which means as you said, after the “wait” (aka after Display.sync)

I’m not exactly sure what Display.update(); does, but assuming it refreshes the display (ie. shows the current state of Spatials on screen, aka renders), then I would maybe pull out the Display.processMessages(); from inside it, and the one in the “if else” part, and put them right before the update() aka before listener.update(); - though I am not exactly sure how would this affect other things (like the display.update)



also don’t know what renderer.onFrame(); is supposed to do (yet), but I’d probably do as I said instead of putting processMessages() right after the Display.sync()

Okay in the latest SVN I moved the input polling to after the wait, can you try again now?

1 Like

Thanks!, tried it and it works as expected (on my other program also) with setFrameRate that is; (jme3 7382)

also, what file stores preferences like resolution selected in the settings dialog, because i realized it’s been storing my setFrameRate of 1 fps somewhere, and it keeps loading it if I app.setShowSettings(false); and don’t setFrameRate myself

trying to use process monitor to find out the file but it’s a lot of noise I’ve to filter thru

EDIT: oh it’s in registry … I could’ve almost miss it

HKEY_CURRENT_USERSoftwareJavaSoftPrefsj/Monkey /Engine 3.0

HKEY_CURRENT_USERSoftwareJavaSoftPrefsj/Monkey /Engine 3.0/Frame/Rate

I wonder when does it get unset…

EDIT2: I mean, once I set that (if I do it before showing the settings dialog) it gets saved and in effect forever, unless I use a different setFrameRate value, but just in case I want to unset it, like for maximum fps, I’d have to hack set it to ie. 20000 maybe ? :slight_smile:

Its not a file, its stored in the registry:

HKCU/Software/JavaSoft/Prefs

1 Like

great :slight_smile:

any idea how to unset it? from my java program I mean ie. delete it

Set some settings on the AppSettings and then call save() on it

1 Like

Summary: I have to use setFrameRate(0); to disable any frame rate limitations, if I previously used setFrameRate and my settings got saved

======

I could use setFrameRate(0) to avoid any limitations but it seems hacky :slight_smile:



I tried this:

[java] public static void main(String[] args) throws BackingStoreException {

Input1FrameLag i = new Input1FrameLag();

AppSettings aps = new AppSettings(true);

aps.load(aps.getTitle());

aps.setVSync(false);

aps.setFrameRate(2);

aps.remove(“FrameRate”);

aps.save(aps.getTitle());

i.setSettings(aps);

i.setShowSettings(false);

i.start();

}[/java]



but it doesn’t remove FrameRate from registry, though it does remove it from the current game session ie. i.start() will behave as if no frame rate was set, good but not really portable this way



I guess the problem is, that {once you called setFrameRate in your program} and {you also allowed the settings dialog to be shown (ie. setShowSettings(true)) OR called AppSettings.save()} the value FrameRate is set in the registry, so it you later want it to not be in effect you’d have to call setFrameRate(0) either every time (if you never show the dialog but you do load the settings manually and you don’t save them) OR call it once and then gets saved as 0 and while it is 0

[java]if (frameRate > 0)

Display.sync(frameRate);[/java]

this won’t get executed, which is fine, I guess, now that I think about it, I mean removing it from registry will cause it to be 0 anyway or less than 0 whatever :slight_smile:

Though if you just do this:

[java]setFrameRate(1);[/java]

and then on the next run you comment it out, it will still be in effect (well unless {you never showed the settings dialog which auto saves it})