I'm working on a simple JavaFX 2D game and I encounter performance issues.
I am rendering my game into a canvas which is updated each frame.
But updating the canvas at 60 FPS strongly increases my CPU usage, and of course my GPU usage.
It doesn't come from big calculations because I tried with very basics manipulations in the canvas, as shown below, and the problem was still there.
Based on research I have made, I use a TimeLine to implement my game loop. It seems to be the best way to update the canvas at the frame rate I want :
private void initGameLoop()
{
final Duration oneFrameAmt = Duration.millis(1000/60);
final KeyFrame oneFrame = new KeyFrame(oneFrameAmt, actionEvent ->
{
gameView.getRenderer().render();
});
gameLoop = new Timeline();
gameLoop.setCycleCount(Animation.INDEFINITE);
gameLoop.getKeyFrames().add(oneFrame);
}
In this configuration I'm running my game loop at 60 FPS, and the changes are running on the FX application thread.
My render function is very simple :
public void render()
{
gc.clearRect(0, 0, 50, 50);
gc.setFill(Color.BLACK);
gc.fillRect(0, 0, 50, 50);
}
gc is the graphicsContext2D of my canvas.
My canvas is scaling on my application's window size, but I'm simply redrawing a 50x50 black rectangle each frames, at 60 FPS my CPU usage increases by 8-10% on default window size, but on full screen my CPU usage increases by 25% to 30% which is insane.
The hardware acceleration is running, besides my CPU usage, my GPU usage is around 30%.
I can't just stop rendering the canvas when nothing happens in the game, because I need to render my animations.
And basically redraw only partials areas of the canvas doesn't change anything because my basic test (shown above) only redraw a 50x50 pixels rectangle on the upper left corner of the canvas, and it still uses 25%+ of the CPU.
All the examples of game loop or animations made with JavaFX that I have found on the web uses the same techniques, but every one I have tried so far have a high CPU usage.
My rendering is very smooth and fluid but the CPU usage is way to high for what it makes. I can't seem to find why doing so little takes so much on my CPU and GPU and can't find a solution on how to improve my game perfomances. From what I have found on my research, canvas seems to be the best way to render a game in JavaFX.
My questions are : Is there any way to improve performance using a canvas ? Should I be using the scene graph with multiple elements ? Or is there any solution that I have not think of yet ?
Thanks for your help !
Conceptionally JavaFX rendering always has a CPU and a GPU portion. In the CPU portion the geometry is prepared to be rendered and the GPU portion finally does the rendering. Canvas rendering is the slowest option because the API forces you to always create new objects in each frame when you want to change something which means that you always suffer from the slow CPU portion of the rendering. On the other hand scene graph rendering can be faster if the changes you apply to your scene are only translation transforms (which can be extended to rotations and scaling with the appropriate rendering hints) because then you suffer from the slow CPU portion only once but not in every frame. If you constantly have to change the geometry too, then you are lost with both approches and the only option is to go for 3D rendering (which is what I do) via a triangle mesh. The nice thing is that in JavaFX 3D rendering can be easily combined with 2D rendering to get the best of both worlds.
Related
I'm creating a game with processing java. I optimized the game as much as I could making sure the image textures are very small, drawing only certain portions of the map, etc., and the game runs consistently at 60 FPS. However, when I want to draw an image across the entire screen, for an example, as a tinted overlay (as seen from the image below)
the FPS significantly decreases, going from 60 FPS to around 40 FPS. The same happens if I use a fullscreen graphic, like a rect(0, 0, width, height) the FPS will still decrease when the graphic is quite large, spanning the width of the entire screen. Literally something as simple as the code below causes lag.
PImage fullscreenImg;
void setup() {
size(displayWidth, displayHeight);
fullscreenImg = loadImage("img.png");
}
void draw() {
image(fullScreenImg, 0, 0, width, width);
}
Here's a video of the lag happening when a full width image is displayed (the FPS goes from ~30 to ~20): https://www.youtube.com/watch?v=bjKFIgb2fII
I've tried to solve this problem by using the get() function, or reducing the resolution of the image (which just causes the image to be more pixely), and none of it works; the FPS still stays at around 40. Is there any way to make an image that has a very wide width, in my case, covering the entire screen, without decreasing the FPS? Am I doing something wrong?
Thanks for any help!
Make sure you are not loading the image during the draw() method. All of your images should load in your setup() function, and should be stored in a variable (memory), before the main loop executes. Otherwise the image will have to be pulled from the disk every time the game loops, which takes much longer than pulling from memory.
Hopefully this helps, I would recommend posting code samples rather than screenshots of the game (although the game looks very nice), otherwise it is a bit hard to diagnose the issue.
I'm using libgdx to make simple tile based game and everything seemed to be fine, until I added a rectangle, which follows mouse position. I figure out, that whenever I jump, rectangle (and other blocks too) expands probably by 1 px, until I let the spacebar. When I hit the spacebar again, it gets to normal size. I tried printing out rectangle width and height, but they didn't change, so problem is with rendering.
Everything allright
On this picture you can see game before jump.
Wider textures
Here is game after jump. You can also clearly see it on players head.
A little more detail. I don't use block2d. Tiles sizes are 8x8 scaled to 20x20. Using texturepacker without padding (problem occurs with padding anyway). I don't know which code to post, because I have no idea where the problem could be, so here is just simple block class. Any help would be much appreciated, thanks.
public class Block extends Sprite {
private int[] id = { 0, 0 };
public Rectangle rect;
private int textureSize = 8;
public Block(PlayScreen play,String texture, int x, int y, int[] id) {
super(play.getAtlas().findRegion("terrain"));
this.id = id;
rect = new Rectangle(x, y, ID.tileSize, ID.tileSize);
setRegion(id[0] * textureSize, id[1] * textureSize + 32, textureSize, textureSize);
setBounds(rect.x, rect.y, rect.width, rect.height);
}
public void render(SpriteBatch batch) {
draw(batch);
}
Welcome to libGDX!
TL;DR- there isn't enough of your code there to tell what the exact problem is, but my guess is that somewhere in your code you are confusing pixel-space with game-space.
A Matter of Perspective
When you first create a libGDX game that is 2D, it's really tempting to think that you are just painting pixels onto the screen. After all, your screen is measured in pixels, your window is measured in pixels, and your texture is measured in pixels.
However, if you start looking closer at the API, you'll find weird little things such as your camera and sprite positions and sizes being measured as floating point values instead of integers (Why floats? You can't have a fraction of a pixel!).
The reason the dimensions of your game object are different than how big they are drawn. It's really easy to understand this in a 3D world- when I am close to something, it is drawn really big on the screen. When I am far away, it is drawn really small. The actual size of the object doesn't change based on my distance from it, but the perceived size did. This tells us that we can't safely measure things in our game just based on how they're drawn- we have to measure based on their true size.
As a side note, while you may be using an Orthographic camera (i.e. one without perspective) and drawing 2D sprites, libGDX is really drawing a flat 3D object (a plane) behind the scenes.
Game Units
So how do we measure the "true size" of something? The answer is that we can measure it using whatever type of unit we want! We can say something is 3.5 meters long, or 42 bananas- whatever you want! For the sake of this conversation, I'm going to call these units "Game Units" (GU).
For your game, you might consider making each block one GU high and one GU wide (essentially measuring your game world in blocks). Your character can move in fractions of a block, but you measure speed in terms of "blocks per second." I can almost guarantee it will make your game logic a lot simpler.
But our textures are in pixels!
As you probably already know, your game uses three things to render: A viewport (the patch of the screen where your game can be painted), A Camera (think of it like a real camera- you change the position and size of the lens to change how much of your world is 'in view'), and your game objects (the things you may or may not want to draw, depending on whether they're visible to the camera).
Now let's look at how they're measured:
Viewport: This is a chunk of your screen (set to be the size of your game window), and as such is measured in pixels.
Camera: The Camera is interesting, because its size and position are measured in Game Units, not pixels. Since the viewport uses the Camera to know what to paint on the screen, it does contain the mapping of GU to pixel.
Game Object: This is measured in Game Units. It may have a texture measured in pixels, but that different than the "true size" of the game object.
Now libGDX defaults all of these sizes such that 1 GU == 1 Pixel, which misleads a lot of folks into thinking that everything is measured by pixels. Once you realize that this isn't really the case, there are some really cool implications.
Really Cool Implications
The first implication is that even if my screen size changes, my camera size can stay the same. For example, if I have a small 800x600 pixel screen, I can set my camera size to 40x30. This maintains a nice aspect ratio, and allows me to draw 40x30 blocks on the screen.
If the screen size changes (say to 1440x900), my game will still show 40x30 blocks on the screen. They may look a little stretched if the aspect ratio changes, but libGDX has special viewports that will counteract this for you. This makes it much easier to support your game on other monitors, other devices, or even just handling screen resizes.
The second cool implication is that you stop caring about texture sizes to a large degree. If you start telling libGDX "Hey, go draw this 32x32px sprite on this 1x1 GU object" instead of "Hey, go draw this 32x32px sprite" (notice the difference?) it means that changing texture sizes doesn't change how big the things on your screen are drawn, it changes how detailed they are. If you want to change how big they are drawn, you can change your camera size to 'zoom in.'
The third cool implication is that this makes your game logic a lot cleaner. For example you start thinking of speeds in "Game Units per second", not "Pixels per second". This means that changes in drawing size won't affect how fast things are in the game, and will save you a ton of bug-hunting further down the road. You also avoid a lot of the weird "My jump behaves differently when I resize the screen" bugs.
Summary
I hope this is helpful and makes sense. It's difficult to get your mind around it at first, but it will make your life a lot easier and your game a lot better in the long run. If you'd like a better example with pictures, I recommend that you read this article by one of the libGDX developers.
I am making a 2d path-geometry based game in java. If I have a bunch of large shapes (Path2D's) that I am rendering every frame, is java taking the time to process the whole thing, or is it only processing the parts actually in the window?
For example say I have an rectangle that is 1000 by 1000 with the top left corner at 10, 10. My window is only 100 by 100. Is java processing the whole thing, or only the part smaller than 100, 100?
Thanks in advance!
Rendering is clipped to the visible portion only.
Obviously.
The clip area of the Graphics2D (see getClip()) is set automatically to the visible portion, but at some moment "Java" will still "take time" to determine whether the generic shapes are inside this clip area.
So it might be a valid optimization technique if you don't draw the shapes that are outside the visible area. Or you could draw the static shapes to an image, and then render this image.
After receiving some information, I decided to test it myself.
I made a complex shape using Path2D and rendered with the whole shape inside the window. Then, I rendered the same thing 90% outside the window, with only a small part of the shape showing.
The one that was outside the window showed much higher performance (260 FPS) than the one entirely in the window (50 FPS).
This suggests that java only processes the part of the shape actually within the window boundaries.
Currently I have JFrame that is always set to fullscreen, JPanel with a resolution that can be changed and a BufferedImage which is drawn onto the JPanel and is a fixed resolution of my game. I feel like this is very inefficient since there can be like three different resolutions, the fullscreen used for the JFrame being a screen resolution, a custom resolution for JPanel set by the user and the game's resolution could be different to both where I would be resizing twice before drawing.
How do modern games like Battlefield allow you to change resolution, they're always fullscreen and they're not just resizing in the window when they resize since I have a dual screen with one a TV and it actually shows me changed resolution due to changing the resolution of the game eventhough my TV can take the max of 1920 x 1080 but it's still fullscreen? All I need is really drawing to pixels, I don't even need adding buttons from swing or anything like that, I'll do that myself. My game is should always be fullscreen. I've been making my game and I'm really comfortable with using JFrame and JPanel but I really want to switch to something else if it's more efficient. What do you think I could do?
you could try this:
Toolkit.getDefaultToolkit().getScreenResolution();
or
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice gd = ge.getDefaultScreenDevice();
gd.setFullScreenWindow(app);
Also take a look at this post:
In JAVA, changing resolution with setDisplayMode for fullscreen application
Rendering to a BufferedImage of fixed size is somewhat defeating the purpose, unless you explicitly want scaling (e.g. because you can't render the full res fast enough).
All you need to do is resize your back buffer when the frame size changes (and render into it according to its current size, of course).
You can detect the changes easily by using either a WindowListener attached to the JFrame, or a ComponentListener attached to the contained panel. For flexibility, I personally would prefer the ComponentListener on the panel, because that enables you to use the same panel and listener for either windowed or full screen mode. Be cautious though that the listener will be called on the Swing event dispatch thread, so depending on the threading architecture of your game you may need some thread synchronization to communicate the change safely.
The way games like battlefield do it is with 3D world coordinates that are independent of the screen resolution using OpenGL or DirectX. Developers feed world coordinates into a rendering engine that then converts to screen coordinates. This is done with whatever the current screen resolution is set to. You will notice though that the HUD size will change depending on your selection of resolution. This is because the HUD is usually a 2D construct that is tied directly to screen coordinates. If you want to do some 3D stuff I would recommend LWJGL. Your only option with Swing is to look into using AffineTransform to scale your display. This however is inefficient compared to 3D and could be slower than you would desire.
i've got a question about drawing animations in Java (SWT).
I try to draw an animation of some process.
When i use canvas.redraw() program firstly erases everything that has been drawn and then draws again.
My program draws about 1000 of rectangles per time step (this big quantity is necessary) so animation doesn't looks smooth - it blinks all the time.
Is there a way to make it look smoother, for example to paint new objects over old ones, without erasing them (that would look better anyway)?
The solution for flickering when doing custom painting is to use double buffering. The SWT Canvas object has built-in double-buffering, use it by adding the flag to the styles in the constructor:
Canvas myCanvas = new Canvas (parentComposite, SWT.DOUBLE_BUFFERED);