Jump to content

onPaint caveats


Botre

Recommended Posts

onPaint caveats

 

Description of onPaint: "called when the script should paint it's debug".

We are generously going to assume the onPaint(Graph) method gets called 30 times per seconds.



@Override
public void onPaint(Graphics2D g2d) {
super.onPaint(g2d);
// Insert custom behavior here.
}


 

1. The method's thread of execution should never be ceased or paused

 

          This will lead to inaccurate or lagged rendering, stuttering and an overall unpleasant experience for your end-user.

 



@Deprecated
@Override
public void onPaint(Graphics2D g2d) {
super.onPaint(g2d);
/*
* BAD CODE!
*/
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}


If your IDE notifies you about an unhandled InterruptedException in your onPaint method, you're probably doing something wrong.

 

2. Calculate values at efficient intervals

 

 Always keep in mind that this method gets called 30 times per seconds however:

 

- Accurate calculation of elapsed seconds requires only one or two calculations per second.

 

- Other calculations (experience, profit, etc...) require even less than that.

 

3. ... and cache them where possible

 

Avoid recalculation of values (such as elapsed time) used in other calculations, store and re-use where possible!

 

Demonstration of caveats 2 and 3:

 



@ScriptManifest(author = "Traum", info = "Tutorial", logo = "", name = "Tutorial - Paint", version = 0)
public class TutorialPaint extends Script {

// The String component that will be rendered.
private String paintcomponentTime = "Time:";

// We store the starting system's clock time in milliseconds.
private long startMilliseconds = System.currentTimeMillis();

// We store the elapsed amount of milliseconds (so we can reuse it instead of having to recalculate it when necessary.
private long elapsedMilliseconds = 0;

// We use this single thread executor to automatically recalculate / update the paint-required values (every 500 milliseconds).
private ScheduledExecutorService paintValueExecutor = Executors.newSingleThreadScheduledExecutor();

// The runnable that will be executed by the executor.
private Runnable paintValueRunnable = () -> {
// We calculate the elapsed amount of milliseconds.
elapsedMilliseconds = System.currentTimeMillis() - startMilliseconds;
// We also build the string value of the component that our onPaint method is going to draw, doing this here will save the cost of rapid repeated concatenation.
paintcomponentTime = "Time: " + elapsedMilliseconds;
};

@Override
public void onStart() throws InterruptedException {
super.onStart();
paintValueExecutor.scheduleAtFixedRate(paintValueRunnable, 0, 500, TimeUnit.MILLISECONDS);
}

@Override
public int onLoop() throws InterruptedException {
// ...
return 200;
}


@Override
public void onPaint(Graphics2D g2d) {
super.onPaint(g2d);
// No object or value is being calculated or created here! (except for the mandatory pixel arrays and stuff like that of course).
g2d.drawString(paintcomponentTime, 50, 50);
}

@Override
public void onExit() throws InterruptedException {
super.onExit();
paintValueExecutor.shutdown();
}

}


4. Avoid transparency

 

Avoid using transparency in your colors and images, your CPU and those of your end-users will thank me later.

 Only use transparency cleverly where it improves usability, don't use it just for the looks of it.

 

More to come soon, feel free to suggest your best onPaint() tip(s)!

 

 

 

Edited by Botre
  • Like 1
Link to comment
Share on other sites

Caching the Strings isnt really necessary, since they are added to the String pool.

 

In other words

 

You aren't gaining any more efficiency by doing 

 

 
private String timeString = "Time:";
 
public void print(){
      for(int i = 0; i < 5; i ++){
          System.out.println(timeString);
      }
}

 

compared to

 

 
public void print(){
      for(int i = 0; i < 5; i ++){
          System.out.println("Time");
      }
}

 

However, it would still be better practice in my opinion, since if you wanted to change it, you would only have to change it in one place.

Link to comment
Share on other sites

Caching the Strings isnt really necessary, since they are added to the String pool.

 

Thread A changes String X twice per second.

Thread B reads String X 30 times per second.

 

The reason I'm putting String X in a variable is so it's available to both thread A & thread B, not for caching purposes (since Strings are pretty much automatically cached internally (like you just pointed out :p)).

Edited by Botre
Link to comment
Share on other sites

Sooo scheduling an additional thread is not more expensive than some minor computations? 

 

You're asking me if:

calculation_cost + constant_scheduling_overhead_cost < calculation_cost * 15

Well, that depends entirely on the cost of the calculations per call and how much the scheduling overhead actually is.

 

Once the total cost of all calculations starts exceeding (constant_scheduling_overhead_cost / 14) it becomes worth using an additional thread from a performance POV.

 

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...