#Override
public void run() {
super.run();
final float timePerFrame = 1000 / Options.MAX_FPS;
float timeDiff;
while(!isInterrupted() && isRunning) {
timeDiff = System.currentTimeMillis();
mController.refresh();
timeDiff = timePerFrame - (timeDiff - System.currentTimeMillis());
try {
Thread.sleep(Math.max(Math.round(timeDiff), 0));
} catch(InterruptedException e) {
// do nothing
e.printStackTrace();
}
}
Log.e("GameThread", "Thread dead.");
}
This is what my thread is basically doing.
Options.MAX_FPS = 30;
To limit the frames per second, to have some sort of sleeping time between each refresh. The refresh method looks like this:
public synchronized void refresh() {
int mRotation = Math.round((360 + ((-1)*mAccelerometer[0] * 4.5f)) % 360);
mObject.setRotation(mRotation);
// get canvas from surfaceview
if(mSurfaceHolder != null) {
// do all calculations.
if(Looper.myLooper() == Looper.getMainLooper()) {
log("on main thread!");
} else {
log("Not on main thread");
}
Canvas mCanvas = mSurfaceHolder.lockCanvas();
if (mCanvas != null) {
// shift x offset
mScreenWidth = mCanvas.getWidth();
mScreenHeight = mCanvas.getHeight();
// draw black background, clear everything.
mCanvas.drawRect(new Rect(0,0,mScreenWidth, mScreenHeight), mBlackPaint);
// shift & draw all viewobjects if still visible
for(ViewObject view: mViewObjectList.toArray(new ViewObject[mViewObjectList.size()])) {
view.shiftX(-1 * Options.Gameplay.Environment.MOVING_SPEED);
if(view.getX() + view.getWidth() < 0) {
mViewObjectList.remove(view);
}
if(view.getCurrentBitmap() != null)
mCanvas.drawBitmap(view.getCurrentBitmap(), new Rect(0,0,view.getCurrentBitmap().getWidth(), view.getCurrentBitmap().getHeight()), new Rect(view.getX(), view.getY(), view.getX()+ view.getWidth(), view.getY()+view.getHeight()), new Paint());
view.nextFrame();
}
// draw object
final Bitmap sBitmap = mObject.getCurrentBitmap();
mObject.nextFrame();
if(sBitmap != null) {
mCanvas.drawBitmap(sBitmap, new Rect(0, 0, sBitmap.getWidth(), sBitmap.getHeight()), new Rect(mObject.getX(), mObject.getY(), sBitmap.getWidth(), sBitmap.getHeight()), new Paint());
} else log("bitmap = null!");
}
mSurfaceHolder.unlockCanvasAndPost(mCanvas);
}
}
The thread keeps running for some time, background elements are being drawn (even though my rotation does not work quite yet, but that's another story..), the background "seems" to move (like a side-scroller), but at some random point in time, without any ADB LogCat messages (not limited to the App but the whole LogCat output) - the thread simply stops. No more drawing. Nothing. I am not calling interrupt or setting isRunning to false, as the "Thread dead." message is not written into LogCat as well..
I do not know what's happening.
Thanks for your help.
You have:
timeDiff = System.currentTimeMillis();
// refresh, which takes some time I guess
timeDiff = timePerFrame - (timeDiff - System.currentTimeMillis());
so System.currentTimeMillis() will be later and therefore bigger than timeDiff. The term in brackets will be negative - so you will be adding onto the timePerFrame and timeDiff will grow not reduce.
Related
I'm writing an android app to measure display lag on tvs using the mirror function on the video out. After many revisions, my code got too complex for its own good, so I scraped it and did a rewrite. My issue is that it is not behaving as expected. The square is not blinking, and the time is 0.0 and the rating is excellent. i have tested changing the ui via the thread by making the square turn different colors, that worked fine. Can someone tell me what the issue is and how to fix it? The way the app works is that you hook the device to a tv and it mirrors the display. then it changes the color of a square in the app and dose a time stamp, then it wait till the camera detects a change then dose another time stamp. using both time stamps you can figure out the delay of the tv. I have it in a loop because the camera only captures at 15ish fps, so I need to run the test multiple times to get an accurate result. The issue is that it always shows up as 0.0ms, that is an impossible number because the lag on most consumer tvs is 9ms. I get the RGB values from each camera frame.
class lagTestThread extends Thread {
#Override
public void run () {
long lagStartTime;
long lagEndTime;
long tempResult;
final double rating;
int x;
long testResult = 0;
int cnt;
for (cnt = 0; cnt >= 100; cnt++){
runOnUiThread(new Runnable() {
#Override
public void run() {
lagSquare.setBackgroundColor(Color.rgb(000, 000, 000));
}
});
while (redVal >= 10.0 && blueVal >= 10.0 && greenVal >= 10.0) {
x = 0;
}
redVal = 0;
blueVal = 0;
greenVal = 0;
lagStartTime = System.nanoTime(); //start lagTimer start
runOnUiThread(new Runnable() {
#Override
public void run() {
lagSquare.setBackgroundColor(Color.rgb(255, 255, 255));
}
});
while (redVal <= 100.0 && blueVal <= 100.0 && greenVal <= 100.0) {
x = 0;
}
lagEndTime = System.nanoTime(); //start lagTimer end
runOnUiThread(new Runnable() {
#Override
public void run() {
lagSquare.setBackgroundColor(Color.rgb(000, 000, 000));
}
});
tempResult = (lagEndTime - lagStartTime);
if (tempResult <= testResult && tempResult != 0) {
testResult = tempResult;
}
}
rating = ((double) testResult) / 1000000.0;
final String finalResultString = String.valueOf(rating);
runOnUiThread(new Runnable() {
#Override
public void run() {
lagTime.setText(finalResultString);
if (rating <= 17.0) {
lagRating.setText("Excellent");
} else if (rating <= 34.0) {
lagRating.setText("Great");
} else if (rating <= 51.0) {
lagRating.setText("Average");
} else {
lagRating.setText("Bad");
}
}
});
}
}
I call it like this
public void startTest(View view) {
lagTestThread lagTest = new lagTestThread();
lagTest.start();
}
redVal, blueVal, greenVal declaration
#Override
public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame) {
double[] rgb = inputFrame.rgba().get(100, 100);
redVal = rgb[0];
blueVal = rgb[2];
greenVal = rgb[1];
Log.i("", "red:" + rgb[0] + " green:" + rgb[1] + " blue:" + rgb[2]);
return rgbMat;
}
The runOnUiThread() causes the Runnable to be posted to the UI thread, at which point the function returns immediately. The Runnable executes at some later time.
Your code is posting events to the UI thread and checking the system time, which means you're calculating how long it takes to post events to the UI thread, not how long it takes them to run. Also, because all the events are queued up behind one another, it's likely they will all execute in the same frame, so you will only see the result of the last setBackgroundColor() call.
If you really want to divorce your display and timing code from the UI thread, you should consider doing this with a SurfaceView, which can be updated independently of the UI thread. (The down side of SurfaceView is that it's a lot more complicated to work with than a custom View.)
I'm working on creating a 2D Java game with a lighting engine with OpenGL using LWJGL, but I've hit a wall when trying to link up keyboard inputs.
The render loop works fine, but as soon as I tried to implement a JFrame/canvas and the getParent/KeyListener combo The application crashes immediately after starting up.
I have to shut the application down in netbeans - the window doesn't respond to right clicking application's entry in the start toolbar.
public static void main(String[] args) {
Main main = new Main();
main.run();
}
public void run() {
initialize();
//animLoop();
}
private void initialize() {
try {
Frame theFrame = new Frame("Inlight");
Canvas theCanvas = new Canvas();
theCanvas.setMinimumSize(new Dimension(windowWidth, windowHeight));
theFrame.setSize(windowWidth, windowHeight);
theCanvas.requestFocusInWindow();
theFrame.add(theCanvas);
theFrame.setVisible(true);
//before doing the following, I need to create the canvas within which openGL does it's rendering
//create it before applying keylistener
Display.setDisplayMode(new DisplayMode(windowWidth, windowHeight));
Display.setParent(theCanvas);
Display.getParent().addKeyListener(new InlightKeyListener());
Display.create(new PixelFormat(0, 16, 1));
} catch (Exception e) {
e.printStackTrace();
}
shaderProgram = glCreateProgram();
fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
StringBuilder fragmentShaderSource = new StringBuilder();
try {
String line;
BufferedReader reader = new BufferedReader(new FileReader("src/shader.frag"));
//points to the shader doc
while ((line = reader.readLine()) != null) {
fragmentShaderSource.append(line).append("\n");
}
} catch (IOException e) {}
glShaderSource(fragmentShader, fragmentShaderSource);
glCompileShader(fragmentShader);
if (glGetShaderi(fragmentShader, GL_COMPILE_STATUS) == GL_FALSE) {
System.err.println("Fragment shader not compiled!");
}
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
glValidateProgram(shaderProgram);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, windowWidth, windowHeight, 0, 1, -1);
glMatrixMode(GL_MODELVIEW);
glEnable(GL_STENCIL_TEST);
glClearColor(0, 0, 0, 0);
System.out.println("Done initialize");
}
public synchronized void animLoop() {
//This method will loop the render
long startTime = System.currentTimeMillis();
//sets the starting time to the current time
long curTime = startTime;
//The current time measurement, so at thestart the curTime = starting time
while (curTime - startTime < 1800) {
long timePassed = System.currentTimeMillis() - curTime;
//Makes the timePassed variable equal to the System's current time - the last measured current time.
curTime += timePassed;
//updates the measurement of the current time to the actual current time. (I imagine some small amount to time is lost while it is updated. this is negligible.)
organiseTitle();
//sets up the ojects to display the title screen for 1800 milliseconds
render();
//draws the new screen scene to the display
clearObj();
//cleans up the items built for that last render
}
while (!Display.isCloseRequested()) {
long timePassed = System.currentTimeMillis() - curTime;
//Makes the timePassed variable equal to the System's current time - the last measured current time.
curTime += timePassed;
//updates the measurement of the current time to the actual current time. (I imagine some small amount to time is lost while it is updated. this is negligible.)
Organiselevel1(20, 200);
render();
//draws the new screen scene to the display
clearObj();
//cleans up the items built for that last render
}
glDeleteShader(fragmentShader);
glDeleteProgram(shaderProgram);
Display.destroy();
//closes the window
}
//There's more code after this point of course, but it's all already been tested and works.
Oh god I only just realized the call to my main loop was commented out.
/facepalm
I've met such a problem. I'm getting IndexOutOfBoundsException when I'm calling .revalidate on container.
I have a Runnable class, which increases or decreases margin of component. I'm using it to make kind of drawer animation. Everything, except this exception, works perfectly. Ans the exception is thrown randomly. I can run "animation" method several times before I get this exception logged to the console.
And after that exception app is working fine. Only container is shown incorrect.
Here's error log:
Exception in thread "Thread-15" java.lang.IndexOutOfBoundsException: Index: 5, Size: 1
at java.util.ArrayList.rangeCheck(ArrayList.java:635)
at java.util.ArrayList.get(ArrayList.java:411)
at com.codename1.ui.Container.getComponentAt(Container.java:1083)
at com.codename1.ui.Container.doLayout(Container.java:1054)
at com.codename1.ui.Container.layoutContainer(Container.java:1043)
at com.codename1.ui.Container.doLayout(Container.java:1056)
at com.codename1.ui.Container.layoutContainer(Container.java:1043)
at com.codename1.ui.Container.doLayout(Container.java:1056)
at com.codename1.ui.Container.layoutContainer(Container.java:1043)
at com.codename1.ui.Container.doLayout(Container.java:1056)
at com.codename1.ui.Container.layoutContainer(Container.java:1043)
at com.codename1.ui.Container.revalidate(Container.java:835)
at util.classes.AnimationRunnable.run(AnimationRunnable.java:75)
at java.lang.Thread.run(Thread.java:745)
Why can it be? What can be the problem?
Thank you!
UPD:
Here's my runnable class:
Removed needless code:
public class AnimationRunnable implements Runnable {
private float marginFrom = 0;
private float marginTo = 0;
private Container bottomContainer;
private int screenHeight;
public AnimationRunnable(Container bottomContainer, int screenHeight, float marginFrom, float marginTo) {
this.marginFrom = marginFrom;
this.marginTo = marginTo;
this.bottomContainer = bottomContainer;
this.screenHeight = screenHeight;
}
#Override
public void run() {
boolean finished = false;
boolean directionUp = marginFrom > marginTo; // true = up; false = down
Container parent = bottomContainer.getParent();
while (!finished) {
// if distance between current margin and destination margin is < 10, finish
if (Math.abs(marginFrom - marginTo) < 10) {
finished = true;
} else {
// if container size is increasing
if (directionUp) {
// if current margin is less than destination margin
if (marginFrom <= marginTo) {
marginFrom = marginTo;
finished = true;
} else {
// else - modify current margin to be closer to destination margin
marginFrom -= Math.ceil((marginFrom - marginTo) / 6);
}
} else {
// if current margin is less than destination margin
if (marginFrom >= marginTo) {
marginFrom = marginTo;
finished = true;
} else {
// else - modify current margin to be closer to destination margin
marginFrom += Math.ceil((marginTo - marginFrom) / 6);
}
}
}
// Setting style to container (in case if container's current style is changed)
Style defaultStyle = bottomContainer.getStyle();
bottomContainer.getStyle().setMargin(Component.TOP, Math.round(marginFrom));
// Setting default style to all container states
// HERE EXCEPTION IS THROWN
// Revalidating either parent, or container itself
if (parent != null) {
parent.revalidate();
parent.repaint();
} else {
bottomContainer.revalidate();
bottomContainer.repaint();
}
try {
// Wait 25ms to make animation smooth
Thread.sleep(25);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// revalidate once more after loop is over
if (parent != null) {
parent.revalidate();
parent.repaint();
} else {
bottomContainer.revalidate();
bottomContainer.repaint();
}
return;
}
}
UPDATE
CallSerially doesn't show the animation, it just waits, and updates the UI, when animation runnable. But calling Display.geInstance().invokeAndBlock() with this animation runnable works as wanted without throwing exceptions.
You are changing UI not on the EDT, which can cause issues and conflicts between the thread you are running and the EDT(Event Dispatch thread).
Okay, so why would my frames per second drop at a random time while playing the game? and what can I do to fix it. Below im going to show the code that I have with the engine that was built.
public Engine() {
Log.d("Engine","Engine constructor");
p_view = null;
p_canvas = null;
p_thread = null;
p_running = false;
p_paused = false;
p_resume = false;
p_paintDraw = null;
p_paintFont = null;
p_numPoints = 0;
p_typeface = null;
p_preferredFrameRate = 40;
p_sleepTime = 1000 / p_preferredFrameRate;
p_pauseCount = 0;
p_group = new LinkedList<Sprite>();
}
/**
* Runnable.run thread method (MAIN LOOP)
*/
#Override
public void run() {
Log.d("Engine","Engine.run start");
ListIterator<Sprite> iter=null, iterA=null, iterB=null;
Timer frameTimer = new Timer();
int frameCount=0;
int frameRate=0;
long startTime=0;
long timeDiff=0;
while (p_running) {
// Process frame only if not paused
if (p_paused) continue;
// Calculate frame rate
frameCount++;
startTime = frameTimer.getElapsed();
if (frameTimer.stopwatch(1000)) {
frameRate = frameCount;
frameCount = 0;
//reset touch input count
p_numPoints = 0;
}
// Call abstract update method in sub-class
update();
/**
* Test for collisions in the sprite group.
* Note that this takes place outside of rendering.
*/
iterA = p_group.listIterator();
while (iterA.hasNext()) {
Sprite sprA = (Sprite)iterA.next();
if (!sprA.getAlive()) continue;
if (!sprA.getCollidable()) continue;
/*
* Improvement to prevent double collision testing
*/
if (sprA.getCollided())
continue; //skip to next iterator
//iterate the list again
iterB = p_group.listIterator();
while (iterB.hasNext()) {
Sprite sprB = (Sprite)iterB.next();
if (!sprB.getAlive()) continue;
if (!sprB.getCollidable()) continue;
/*
* Improvement to prevent double collision testing
*/
if (sprB.getCollided())
continue; //skip to next iterator
//do not collide with itself
if (sprA == sprB) continue;
/*
* Ignore sprites with the same ID? This is an important
* consideration. Decide if your game requires it or not.
*/
if (sprA.getIdentifier() == sprB.getIdentifier())
continue;
if (collisionCheck(sprA, sprB)) {
sprA.setCollided(true);
sprA.setOffender(sprB);
sprB.setCollided(true);
sprB.setOffender(sprA);
break; //exit while
}
}
}
// begin drawing
if (beginDrawing()) {
// Call abstract draw method in sub-class
draw();
/**
* Draw the group entities with transforms
*/
iter = p_group.listIterator();
while (iter.hasNext()) {
Sprite spr = (Sprite)iter.next();
if (spr.getAlive()) {
spr.animate();
spr.draw();
}
}
/**
* Print some engine debug info.
*/
int x = p_canvas.getWidth()-150;
p_canvas.drawText("ENGINE", x, 20, p_paintFont);
p_canvas.drawText(toString(frameRate) + " FPS", x, 40,
p_paintFont);
p_canvas.drawText("Pauses: " + toString(p_pauseCount),
x, 60, p_paintFont);
// done drawing
endDrawing();
}
/*
* Do some cleanup: collision notification, removing
* 'dead' sprites from the list.
*/
iter = p_group.listIterator();
Sprite spr = null;
while (iter.hasNext()) {
spr = (Sprite)iter.next();
//remove from list if flagged
if (!spr.getAlive()) {
iter.remove();
continue;
}
//is collision enabled for this sprite?
if (spr.getCollidable()) {
//has this sprite collided with anything?
if (spr.getCollided()) {
//is the target a valid object?
if (spr.getOffender() != null) {
/*
* External func call: notify game of collision
* (with validated offender)
*/
collision(spr);
//reset offender
spr.setOffender(null);
}
//reset collided state
spr.setCollided(false);
}
}
}
// Calculate frame update time and sleep if necessary
timeDiff = frameTimer.getElapsed() - startTime;
long updatePeriod = p_sleepTime - timeDiff;
if (updatePeriod > 0) {
try {
Thread.sleep( updatePeriod ); // i notice this is called when frames are low
}
catch(InterruptedException e) {}
}
}//while
Log.d("Engine","Engine.run end");
System.exit(RESULT_OK);
}
public void setFrameRate(int rate) {
p_preferredFrameRate = rate;
p_sleepTime = 1000 / p_preferredFrameRate;
}
So this is not the entire engine, but it is everything that deals with the frames Why is it randomly dropping? What I am noticing that thread.sleep is being called when the frames drop below 15. I am using a nexus 5 to test this running on android 4.4.2, and My question here is How do I stop the frames from dropping that low and having it sleep especially after calling a recreate(); method when you lose the game?
Just take out the Thread.Sleep statement altogether. Or just pass "0" into the Sleep call (if you are trying to yield cycles to other threads and processes). On each cycle of the run() loop, calculate how much time actually elapsed and update your simulation based on this value. In other words, don't try to force a hard-coded frame rate.
The device has an upperbound on frame rate (60fps on my device). So you'll likely be sleeping a little during each loop anyway.
Asycntask's onprogressupdate is always being called, :
#Override
protected void onProgressUpdate(Integer... values) {
pbSubStatus.incrementProgressBy(values[1]);
rawNum = values[0];
...
}
And on outer class:
private double getCurrentTime() {
return currentTime;
}
private void doSomethingOnThread() {
LinearLayout layout = (LinearLayout) findViewById(R.id.main);
sMeter = new Meter(context);
layout.addView(sMeter);
final Handler mHandler = new Handler();
new Thread() {
#Override
public void run() {
while (running) {
previousTime = getCurrentTime();
previousNum = getRawNum();
try {
Thread.sleep(1500);
} catch (InterruptedException e) {
e.printStackTrace();
}
currentTime=getCurrentTime();
currentNum = getRawNum();
mHandler.post(mUpdateResults);
}
}
}.start();
}
final Runnable mUpdateResults = new Runnable() {
public void run() {
updateResultsInUi();
}
};
private void updateResultsInUi() {
if(previousNum>currentNum){
return;//TODO
}else{
int temp=currentNum-previousNum;//Problem here, both num are same.
double timeTemp=currentTime-previousTime;
someField= (float)( temp/timeTemp);
}
sMeter.setTarget(someField);//this should be last line to be executed in this snippt
}
So the problem is previousNum and currentNum are always returning the same number. I want the thread to wait 1.5 sec to get a new (getRawNum()) from the onProgressupdate. But I don't understand they they are returning the same number. Thanks for any advice.
I don't think this section is doing what you want:
while (running) {
previousTime = getCurrentTime();
previousNum = getRawNum();
try {
Thread.sleep(1500);
} catch (InterruptedException e) {
e.printStackTrace();
}
currentTime=getCurrentTime();
currentNum = getRawNum();
mHandler.post(mUpdateResults);
}
For instance, let's say this starts executing at time=0. At the end of the first iteration you will have:
previousTime = 0
currentTime = 1500
...however, the loop will immediately start its second iteration, and then you will have:
previousTime = 1500
currentTime = 1500
...then it will sleep for another 1500 milliseconds and set currentTime to 3000, and then immediately start the next iteration, setting previousTime to 3000 as well.
So the problem is that for the vast majority of the time, your code is leaving currentTime and previousTime set to the same value. The only time they have different values is for the extremely small span of time in between when one iteration ends and the next iteration begins.
A quick way to work around this problem is as follows:
currentTime = getCurrentTime();
currentNum = getRawNum();
while (running) {
try {
Thread.sleep(1500);
} catch (InterruptedException e) {
e.printStackTrace();
}
previousTime = curentTime;
previousNum = currentNum;
currentTime=getCurrentTime();
currentNum = getRawNum();
mHandler.post(mUpdateResults);
}
A better approach might be to refactor your code to use something like a circular buffer to keep track of the last n samples and sample timestamps. Then you can simply query for a new sample at regular intervals, and when updating the UI you just compare the most recent thing in the buffer against the one that came immediately before it.
And as for fields in your UI not updating, make sure that the code you have that tries to update them is running on the main thread.