optimize javafx code consuming large amount of memory and CPU - image

the code below is for a fundraiser dinner to purchase a land, the purpose is to show the progress of the square meter of land purchased (around 2976m2). everytime a square meter is purchased, the application adds an image tile which corresponds to an acctual 1m2. eventually the tiles (~2976 of them) fill up like in a grid to complete the land once fully purchased.
The size of each tiles is around 320bytes, there are 2976 tiles in total.
I have also showing below an image example.
The thing that drives me crazy with this code (in javafx) is that it consumes around 90 to 100% of 1 of my processors and the memory usage keeps increasing as the tiles add up until the code buffer run out of memory and the program crashes after a while. this is not desirable during the fundraising dinner.
the full code is available for testing at
you will need to change boolean split to true false, which will split the images for you, (around 3000 images);
https://github.com/rihani/Condel-Park-Fundraiser/tree/master/src/javafxapplication3
The main culprit that uses all the memory and CPU is the AnimationTimer() function shown below and I am wondering if anyone can help me reduce memory and CPU usage in this code.
to briefly explain how the code below is used, the land is divided into 2 panes, when the first one grid_pane1 is filled up the second pane grid_pane2 starts to then fill up.
also a flashing tile is used to show the current progress.
I am using total_donnation ++; to test the code, but would normally use mysql to pull the new value raised during the findraising dinner
AnimationTimer() Code:
translate_timer = new AnimationTimer() {
#Override public void handle(long now) {
if (now > translate_lastTimerCall + 10000_000_000l)
{
old_total_donnation = total_donnation;
try
{
// c = DBConnect.connect();
// SQL = "Select * from donations";
// rs = c.createStatement().executeQuery(SQL);
// while (rs.next())
// {total_donnation = rs.getInt("total_donnation");}
// c.close();
total_donnation ++;
if(total_donnation != old_total_donnation)
{
System.out.format("Total Donation: %s \n", total_donnation);
old_total_donnation = total_donnation;
if (!pane1_full)
{
grid_pane1.getChildren().clear();
grid_pane1.getChildren().removeAll(imageview_tile1,hBox_outter_last);
}
grid_pane2.getChildren().clear();
grid_pane2.getChildren().removeAll(imageview_tile2,hBox_outter_last);
for(i=0; i<=total_donnation; i++)
{
if (pane1_full){ System.out.println("Pane 1 has not been redrawn"); break;}
file1 = new File("pane1_img"+i+".png");
pane1_tiled_image = new Image(file1.toURI().toString(),image_Width,image_Height,false,false);
imageview_tile1 = new ImageView(pane1_tiled_image);
grid_pane1.add(imageview_tile1, current_column_pane1,current_row_pane1);
current_column_pane1 = current_column_pane1+1;
if (current_column_pane1 == max_columns_pane1 )
{
current_row_pane1 = current_row_pane1+1;
current_column_pane1 = 0;
}
if (i == max_donnation_pane1 ){ pane1_full = true; System.out.println("Pane 1 full"); break;}
if (i == total_donnation)
{
if (i != max_donnation_pane1)
{
hBox_outter_last = new HBox();
hBox_outter_last.setStyle(style_outter);
hBox_outter_last.getChildren().add(blink_image);
ft1 = new FadeTransition(Duration.millis(500), hBox_outter_last);
ft1.setFromValue(1.0);
ft1.setToValue(0.3);
ft1.setCycleCount(Animation.INDEFINITE);
ft1.setAutoReverse(true);
ft1.play();
grid_pane1.add(hBox_outter_last, current_column_pane1,current_row_pane1);
}
}
}
if (i < total_donnation)
{
total_donnation_left = total_donnation - max_donnation_pane1;
for(j=0; j<=total_donnation_left; j++)
{
file2 = new File("pane2_img"+j+".png");
pane2_tiled_image = new Image(file2.toURI().toString(),image_Width,image_Height,false,false);
imageview_tile2 = new ImageView(pane2_tiled_image);
grid_pane2.add(imageview_tile2, current_column_pane2,current_row_pane2);
current_column_pane2 = current_column_pane2+1;
if (current_column_pane2 == max_columns_pane2 )
{
current_row_pane2 = current_row_pane2+1;
current_column_pane2 = 0;
}
if (j == max_donnation_pane2 ){ System.out.println("Pane 2 full"); break;}
if (j == total_donnation_left)
{
if (j != max_donnation_pane2)
{
hBox_outter_last = new HBox();
hBox_outter_last.setStyle(style_outter);
hBox_outter_last.getChildren().add(blink_image);
ft = new FadeTransition(Duration.millis(500), hBox_outter_last);
ft.setFromValue(1.0);
ft.setToValue(0.3);
ft.setCycleCount(Animation.INDEFINITE);
ft.setAutoReverse(true);
ft.play();
grid_pane2.add(hBox_outter_last, current_column_pane2,current_row_pane2);
}
}
}
}
current_column_pane1 =0;
current_row_pane1=0;
current_column_pane2=0;
current_row_pane2=0;
}
}
catch (Exception ex) {}
translate_lastTimerCall = now;
}
}
};

First and foremost, you create a lot of indefinite FadeTransitions that are never stopped. These add up over time and cause both memory and CPU leaks. You should stop() the transition before starting a new one. Alternatively, you only need one transition to interpolate the value of a DoubleProperty and then bind node's opacity to this property:
DoubleProperty opacity = new SimpleDoubleProperty();
Transition opacityTransition = new Transition() {
protected void interpolate(double frac) {
opacity.set(frac);
}
};
// elsewhere
hBox_outter_last.opacityProperty().bind(opacity);
You may want to preload all the image tiles beforehand, so that you avoid reading from disk in the loop.
You unnecessarily destroy and recreate large part of the scene in every cycle. You should modify your code to only add the new tiles and not drop them all and recreate them from scratch.
Finally, when you actually query the database, you should do it from a different thread and not the JavaFX application thread, because your UI will be unresponsive for the time of the query (e.g. not animating your fade transitions).

I have a suggestion:
Do not split the image instead using 2 panels. One for displaying the whole image. The second will be a grid pane overlapping the first pane. Therefore, when a square meter is purchased, the background of corresponding grid-cell will become transparent.

Related

UWP: calculate expected size of screenshot using multiple monitor setup

Right, parsing the clipboard, I am trying to detect if a stored bitmap in there might be the result of a screenshot the user took.
Everything is working fine as long as the user only has one monitor. Things become a bit more involved with two or more.
I am using the routing below to grab all the displays in use. Now, since I have no idea how they are configured to hang together, I do not know how to calculate the size of the screenshot (that Windows would produce) from that information.
I explicitly do not want to take a screenshot myself to compare. It's a privacy promise by my app.
Any ideas?
Here is the code for the size extractor, run in the UI thread.
public static async Task<Windows.Graphics.SizeInt32[]> GetMonitorSizesAsync()
{
Windows.Graphics.SizeInt32[] result = null;
var selector = DisplayMonitor.GetDeviceSelector();
var devices = await DeviceInformation.FindAllAsync(selector);
if (devices?.Count > 0)
{
result = new Windows.Graphics.SizeInt32[devices.Count];
int i = 0;
foreach (var device in devices)
{
var monitor = await DisplayMonitor.FromInterfaceIdAsync(device.Id);
result[i++] = monitor.NativeResolutionInRawPixels;
}
}
return result;
}
Base on Raymonds comment, here is my current solution in release code...
IN_UI_THREAD is a convenience property to see if the code executes in the UI thread,
public static Windows.Foundation.Size GetDesktopSize()
{
Int32 desktopWidth = 0;
Int32 desktopHeight = 0;
if (IN_UI_THREAD)
{
var regions = Windows.UI.ViewManagement.ApplicationView.GetForCurrentView()?.WindowingEnvironment?.GetDisplayRegions()?.Where(r => r.IsVisible);
if (regions?.Count() > 0)
{
// Get grab the most left and the most right point.
var MostLeft = regions.Min(r => r.WorkAreaOffset.X);
var MostTop = regions.Min(r => r.WorkAreaOffset.Y);
// The width is the distance between the most left and the most right point
desktopWidth = (int)regions.Max(r => r.WorkAreaOffset.X + r.WorkAreaSize.Width - MostLeft);
// Same for height
desktopHeight = (int)regions.Max(r => r.WorkAreaOffset.Y + r.WorkAreaSize.Height - MostTop);
}
}
return new Windows.Foundation.Size(desktopWidth, desktopHeight);
}

Scale UI for multiple resolutions/different devices

I have a quite simple unity GUI that has the following scheme :
Where Brekt and so are buttons.
The GUI works just fine on PC and is on screen space : overlay so it is supposed to be adapted automatically to fit every screen.
But on tablet the whole GUI is smaller and reduced in the center of the screen, with huge margins around the elements (can't join a screenshot now)
What is the way to fix that? Is it something in player settings or in project settings?
Automatically scaling the UI requires using combination of anchor,pivot point of RecTransform and the Canvas Scaler component. It is hard to understand it without images or videos. It is very important that you thoroughly understand how to do this and Unity provided full video tutorial for this.You can watch it here.
Also, when using scrollbar, scrollview and other similar UI controls, the ContentSizeFitter component is also used to make sure they fit in that layout.
There is a problem with MovementRange. We must scale this value too.
I did it so:
public int MovementRange = 100;
public AxisOption axesToUse = AxisOption.Both; // The options for the axes that the still will use
public string horizontalAxisName = "Horizontal"; // The name given to the horizontal axis for the cross platform input
public string verticalAxisName = "Vertical"; // The name given to the vertical axis for the cross platform input
private int _MovementRange = 100;
Vector3 m_StartPos;
bool m_UseX; // Toggle for using the x axis
bool m_UseY; // Toggle for using the Y axis
CrossPlatformInputManager.VirtualAxis m_HorizontalVirtualAxis; // Reference to the joystick in the cross platform input
CrossPlatformInputManager.VirtualAxis m_VerticalVirtualAxis; // Reference to the joystick in the cross platform input
void OnEnable()
{
CreateVirtualAxes();
}
void Start()
{
m_StartPos = transform.position;
Canvas c = GetComponentInParent<Canvas>();
_MovementRange = (int)(MovementRange * c.scaleFactor);
Debug.Log("Range:"+ _MovementRange);
}
void UpdateVirtualAxes(Vector3 value)
{
var delta = m_StartPos - value;
delta.y = -delta.y;
delta /= _MovementRange;
if (m_UseX)
{
m_HorizontalVirtualAxis.Update(-delta.x);
}
if (m_UseY)
{
m_VerticalVirtualAxis.Update(delta.y);
}
}
void CreateVirtualAxes()
{
// set axes to use
m_UseX = (axesToUse == AxisOption.Both || axesToUse == AxisOption.OnlyHorizontal);
m_UseY = (axesToUse == AxisOption.Both || axesToUse == AxisOption.OnlyVertical);
// create new axes based on axes to use
if (m_UseX)
{
m_HorizontalVirtualAxis = new CrossPlatformInputManager.VirtualAxis(horizontalAxisName);
CrossPlatformInputManager.RegisterVirtualAxis(m_HorizontalVirtualAxis);
}
if (m_UseY)
{
m_VerticalVirtualAxis = new CrossPlatformInputManager.VirtualAxis(verticalAxisName);
CrossPlatformInputManager.RegisterVirtualAxis(m_VerticalVirtualAxis);
}
}
public void OnDrag(PointerEventData data)
{
Vector3 newPos = Vector3.zero;
if (m_UseX)
{
int delta = (int)(data.position.x - m_StartPos.x);
delta = Mathf.Clamp(delta, -_MovementRange, _MovementRange);
newPos.x = delta;
}
if (m_UseY)
{
int delta = (int)(data.position.y - m_StartPos.y);
delta = Mathf.Clamp(delta, -_MovementRange, _MovementRange);
newPos.y = delta;
}
transform.position = new Vector3(m_StartPos.x + newPos.x, m_StartPos.y + newPos.y, m_StartPos.z + newPos.z);
UpdateVirtualAxes(transform.position);
}

Trying to create a very basic game, but experiencing some bottlenecking issues (I think)!

I'm using Adobe Flash Professional CS6 to create the game. I'll post the code under. Be noticed that there are two symbol I've created using Flash that are not made by code. These symbols are the Crosshair symbol, and the Hitbox symbol. Basically, the objective of the game is to click the Hitbox symbol. My issue is that I am experiencing what seems to be bottlenecking issues. When I click the Hitbox symbol a lot of times with a fast timer the score doesn't register. I am pressuming that this comes from the (maybe) ineffective movement algorithm. But I can't really seem to find room for improvement. Some help would be appreciated.
Be noticed, I had to change the timer from Timer(1) to Timer(30). This made the bottlenecking issue a little bit better, but made the game less fluent.
Aah, and the reason as to why I am using the directionCheckerY and directionCheckerX variables is that I will later in the development add random movement. A random timer will change these to either 0 and 1, creating random movement.
import flash.events.MouseEvent;
import flash.events.TimerEvent;
// Variables
var directionCheckerX:int=0;
var directionCheckerY:int=0;
var pointChecker:int=0;
// Croshair
var crosshair:Crosshair = new Crosshair();
addChild(crosshair);
Mouse.hide();
function moveCrossEvent (evt: MouseEvent) {
crosshair.x = mouseX;
crosshair.y = mouseY;
evt.updateAfterEvent();
}
// Hitbox
var hitbox:Hitbox = new Hitbox();
addChild(hitbox);
hitbox.x=50;
hitbox.y=50;
// Timer
var myTimer:Timer = new Timer(30);
myTimer.addEventListener(TimerEvent.TIMER, timerEvent);
myTimer.start();
function timerEvent(evt:TimerEvent) {
// Border code (Keeps the Hitbox away from out of bounds)
if (hitbox.x <= 0) {
directionCheckerX = 1;
} else if (hitbox.x >= 550) {
directionCheckerX = 0;
}
if (directionCheckerX == 0) {
hitbox.x-=2;
} else {
hitbox.x+=2;
}
if (hitbox.y <= 0) {
directionCheckerY = 1;
} else if (hitbox.y >= 400) {
directionCheckerY = 0;
}
if (directionCheckerY == 0) {
hitbox.y-=2;
} else {
hitbox.y+=2;
}
}
// EventListeners
stage.addEventListener(MouseEvent.MOUSE_MOVE, moveCrossEvent);
hitbox.addEventListener(MouseEvent.CLICK, hitboxEvent);
stage.addEventListener(MouseEvent.CLICK, stageEvent);
function hitboxEvent (evt:MouseEvent) {
pointChecker+=1;
outputTxt.text = String(pointChecker);
evt.stopImmediatePropagation();
//evt.updateAfterEvent();
}
function stageEvent(evt:MouseEvent) {
pointChecker-=1;
outputTxt.text = String(pointChecker);
}
To be clear, I'm not a game developer.
Actually, sometimes there is no big difference between a Timer with 1 millisecond interval and another one with 30 milliseconds interval because it's depending on the SWF file's framerate or the runtime environment ... but here, what about using an Event.ENTER_FRAME event instead of a Timer ? because as Adobe said here about Timers versus ENTER_FRAME events :
Choose either timers or ENTER_FRAME events, depending on whether content is animated.
Timers are preferred over Event.ENTER_FRAME events for non-animated content that executes for a long time.
and in your case the content is animated (even if your game is still basic).
Then you can use a var to set the speed of your hitbox which you can update at any time :
var speed:int = 2;
function timerEvent(evt:TimerEvent): void
{
// ...
if (directionCheckerX == 0) {
hitbox.x -= speed;
} else {
hitbox.x += speed;
}
// ...
}
Hope that can help.

Loading time - the GameScreen starts running before being displayed

I am using libGdx to code a Pong-like game.
In the GameScreen I want to display the remaining lives of each player.
For that, in the create() of the GameScreen I have a table containing several ImageButtons:
for(int i = 0; i < lifePlayer1; i++){
lives[i] = new ImageButton(skin, "Life");
lifeTable.add(lives[i]);
lives[i].setVisible(true);
lives[i].setTouchable(Touchable.disabled);
}
Each ImageButton represent a life. When a player loses a life, the ImageButton corresponding to the lost life is setChecked, and the imageChecked represents the lost life
Before the game start I want to display a message saying "Game Starts !". The message crosses the screen from left to right, and the game starts only when the message has disappeared.
The problem I encounter is that the instantiation of the lifeTable takes time, and the GameScreen starts running before the instantiation is finished. I explain :
When I disable the lifeTable, everything runs smoothly, the "Game Starts !" message crosses the screen from left to right and the game starts.
When I enable the lifeTable, when I press on the "New Game" button in my MainMenueScreen, there is a delay (let's say 0.5 seconds), like a loading time, and when the GameScreen is finally displayed, the "Game Starts !" message has almost finished crossing the screen.
I have a 2 players mode, with 2 lifeTable to load, and it's even worse, when the GameScreen displays, in 2 players mode, the "Game Starts !" message has already finished crossing the screen and the game has already started.
I am using an AssetManager, and every texture used in the game are already loaded before reaching the MainMenuScreen. The AssetManager helped to reduce the GameScreen loading time, but it's still not satisfying.
Is there something to preload the lifeTable, the same way we preload the textures with the AssetManager ?
Or, is there a way to have the render() of the GameScreen starts only when the create() has finished the instantiation of ever objects ?
Thank you for your help.
is probably not the most efficient way to make but which occurred to me while reading your question, and it is simple
Variable Class
private boolean loadForTest = false;
.
#Override
public void show() {
.//Other Code
image = new Image(YourAssetManager.get("YourImage.png", Texture.class));
image.addAction(Actions.sequence(
Actions.moveTo(Gdx.graphics.getWidth(),
(Gdx.graphics.getHeight()/2),
1f)));
image.setPosition((-100f), Gdx.graphics.getHeight()/2);
YourStage.addActor(image);
}
#Override
public void render(float delta) {
.//Other Code
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
stage.act(Gdx.graphics.getDeltaTime());
stage.draw();
if (loadForTest == false){
if (image.getActions().size == 0){
Gdx.app.log("test" ,"now load table");
for (int i = 0; i <lifePlayer1; i ++) {
vidas [i] = new ImageButton (piel, "Life");
lifeTable.add(lives[i]);
lives[i].setVisible(true);
lives[i].setTouchable(Touchable.disabled);
}
loadForTest = true;
}else{
Gdx.app.log("test" ,"Your Other Code Play");
}
}
}
Edit
added another code because my English is not very good and did not know very well what you wanted to do
Variable Class
private boolean loadForTest = false;
.
#Override
public void render(float delta) {
.//Other Code
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
stage.act(Gdx.graphics.getDeltaTime());
stage.draw();
if (loadForTest == false){
for (int i = 0; i <lifePlayer1; i ++) {
vidas [i] = new ImageButton (piel, "Life");
lifeTable.add(lives[i]);
lives[i].setVisible(true);
lives[i].setTouchable(Touchable.disabled);
}
loadForTest = true;
}else{
Gdx.app.log("test" ,"Your Image load, Your Other Code Play");
}
}

Showing images of an Actor changing in time as Animating

I am trying to animate 4 images of an actor but what i am seeing is only one image and no other change.
function init()
{
i=0;
while(i<ActStandX.length) //ActStandX holds the x locations of actor in a image spritesheet
{
for(var currentFrame=0;currentFrame<10;currentFrame++)
{
ctxBg.clearRect(0,0,800,600); //ctxBg is 2d context of canvas
ctxBg.drawImage(img,ActStandX[i],10,actWidth,actHeight,200,300,60,110);
}
i++;
}
requestAnimFrame(init);
}
i have tried this code so far but no success.
Try this:
var i = 0;
function init()
{
ctxBg.clearRect(0,0,800,600);
ctxBg.drawImage(img,ActStandX[i++],10,actWidth,actHeight,200,300,60,110);
if (i == ActStandX.length) i = 0; // loop the animation
requestAnimFrame(init);
}
Note that it will change frames every time it paints, so it may be faster than you actually want. It isn't hard to limit the incrementation of i based on the time passed, I'm sure you can figure it out. :)

Resources