I'm using Swing and JavaFX to render images to the screen, but getting unexpected timings: the aim is simply to render 1,000,000 images at random positions on a component. Why is JavaFX taking so long?
Results: Swing : 2.5 secs. JavaFX 8.5 secs. Code below.
In JavaFX.
public class JFXTest extends Application
{
public static void main(String[] args)
{
launch(args);
}
#Override
public void start(Stage theStage)
{
Group root = new Group();
Scene theScene = new Scene( root );
theStage.setScene( theScene );
Canvas canvas = new Canvas( 1000, 1000);
root.getChildren().add( canvas );
GraphicsContext gc = canvas.getGraphicsContext2D();
new ResourceLoaderJFX();
System.out.println("Running test");
Random ran = new Random();
ClassLoader classLoader = getClass().getClassLoader();
URL url = classLoader.getResource("sky.png");
Image image = new Image(url.toString());
long t1 = System.nanoTime();
for (int j=0; j<1000000; j++ ) {
int x = ran.nextInt(1000);
int y = ran.nextInt(1000);
gc.drawImage(image, x, y);
}
System.out.println("\n");
long t2 = System.nanoTime()-t1;
System.out.println("Took " + (t2/1000000000.0) + " secs");
System.out.println("Done");
theStage.show();
}
}
Prism pipeline init order: d3d sw
Using native-based Pisces rasterizer
Using dirty region optimizations
Not using texture mask for primitives
Not forcing power of 2 sizes for textures
Using hardware CLAMP_TO_ZERO mode
Opting in for HiDPI pixel scaling
Prism pipeline name = com.sun.prism.d3d.D3DPipeline
Loading D3D native library ...
D3DPipelineManager: Created D3D9Ex device
succeeded.
Direct3D initialization succeeded
(X) Got class = class com.sun.prism.d3d.D3DPipeline
Initialized prism pipeline: com.sun.prism.d3d.D3DPipeline
OS Information:
Maximum supported texture size: 8192
Windows version 10.0 build 14393
Maximum texture size clamped to 4096
D3D Driver Information:
Intel(R) Iris(TM) Graphics 540
\\.\DISPLAY2
Driver igdumdim64.dll, version 20.19.15.4463
Pixel Shader version 3.0
Device : ven_8086, dev_1926, subsys_00151414
Max Multisamples supported: 4
vsync: true vpipe: true
Running test
Took 8.230974466 secs
In Swing:
public class SwingTest extends JPanel {
public void init() {
setVisible(true);
}
public void runTest() {
System.out.println("Running test");
BufferedImage bufferedImage=null;
try {
bufferedImage = ImageIO.read(new File("C:\\Users\\resources\\png\\sky.png"));
} catch (IOException e) {
e.printStackTrace();
}
long t1 = System.nanoTime();
Random ran = new Random();
for (int j=0; j<(1000000); j++ ) {
int x = ran.nextInt(1000);
int y = ran.nextInt(1000);
this.getGraphics().drawImage(bufferedImage, x, y, null);
}
long t2 = System.nanoTime()-t1;
System.out.println("Took " + (t2/1000000000.0) + " secs");
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame f = new JFrame();
SwingTest view= new SwingTest();
view.init();
f.add(worldViewPanel);
f.pack();
f.setSize(new Dimension(1000,1000));
f.setVisible(true);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
view.runTest();
}
});
}
}
Connected to the target VM, address: '127.0.0.1:53764', transport: 'socket'
Took 2.586923483 secs
Interestingly, for lower numbers
JAVAFX
Took 0.02173174 secs # 10,000 images, second run took 0.018200605 secs
SWING
Took 0.138639497 secs # 10,000 images, second run took 0.13744251 secs
I think what you are experiencing is the difference between the retained mode of JavaFX and the immediate mode of Swing. Swing is literally taking those images and blitting them to the screen then moving on to the next blitting position. When it needs to draw them again, it starts from scratch. As it happens, this is very fast.
JavaFX is creating a distinct Object every time you invoke drawImage (see GraphicsContext.writeImage() ) and then retaining those Objects in an internal buffer it grabs from Canvas. On top of that it's creating six doubles and putting them into exactly the same buffer (see GraphicsContext.updateTransform() ).
The sell of JavaFX is its retained mode. It will allow you to manipulate its Nodes on screen as if they were in a 2-D (in fact 3-D) coordinate system and it will do this "for free". This is very powerful if you want to position objects in a 2-D scene and move them around, as game programmers are well aware.
The price you pay for this is the scene is much heavier than the corresponding scene in Swing and the memory cost of Images is cumulative in the JavaFX application. In your JavaFX app, you have a Scene to which you're adding a Canvas and it's creating a scene graph. Swing is not doing this.
If you run your program in a profiler, you can see exactly where the time is being spent and if you run your program in a debugger you can see how large the Canvas buffer is becoming.
You are comparing apples with beens here. In Swing the image is actually rendered when you call drawImage. In JavaFX this command to draw an image is just added to a command buffer which will be executed later.
Related
Here is my Desktop Launcher code:
public class DesktopLauncher {
public static void main (String[] arg) {
Lwjgl3ApplicationConfiguration config = new Lwjgl3ApplicationConfiguration();
config.setForegroundFPS(60);
config.setTitle("Game10");
config.setWindowedMode(1240, 760);
config.forceExit = false; // ERROR!!!
new Lwjgl3Application(new GdxGame10(), config);
}
}
In new LWJGL3 config.forceExit not working. I can't find any solution so far. Any help is appreciated.
There is no forceExit in config. So presumably you have a master application that runs a child libGDX component and when you end that child component you find that the entire application shuts down, when you want the master application to continue. I guess you are on desktop because Android would be OK. So you must want to avoid a full System.exit
i.e.
Gdx.app.exit()
shuts down everything.
So when you instantiate a libGDX application you instantiate based on the application type, so for me, I use the same as you
final Lwjgl3Application application = new Lwjgl3Application(Services.GAME_CONTROLLER,config);
and the implementation for exit is
#Override
public void exit () {
running = false;
}
This finished the while loop that drives the application i.e. kills the main thread. If you have -other threads- running in the background they keep going.
If on the other hand you were instantiating LwglAWTCanvas then your shutdown would be this.
#Override
public void exit () {
postRunnable(new Runnable() {
#Override
public void run () {
stop();
System.exit(-1);
}
});
}
which would shut down the entire application. Anyway so forceExit being -false- was to stop a full system exit killing all your threads. The forceExit was to "force" the other threads to finish. It doesn't do that anymore so the fact it is now missing should not matter, your background threads should keep going.
In other words, config.forceExit = false; is now the default behaviour for your application type so you don't need it.
I'm converting my game Hold & Drag from Swift (SpriteKit) to Android using LibGDX, I created my 'SpriteKit' api a little clone of the official on Xcode.
I'm looking for optimizing the render of textures especially because I got FPS drop (45-60 /60 fps)
When I don't draw textures I got 60 (the maximum).
I hope you will help me as efficiently as possible!
#Override
public void draw() {
if (texture == null)
return;
Gdx.gl.glEnable(GL20.GL_BLEND);
Gdx.gl.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA);
spriteBatch.setTransformMatrix(getTransformMatrix());
spriteBatch.begin();
Color color = spriteBatch.getColor();
color.a = getAlpha();
spriteBatch.setColor(color);
spriteBatch.draw(texture, -size.width / 2, -size.height / 2, size.width, size.height);
spriteBatch.end();
Gdx.gl.glDisable(GL20.GL_BLEND);
}
public List<SKNode> getParents() {
List<SKNode> parents = new ArrayList<>();
if (parent != null) {
parents.add(parent);
parents.addAll(parent.getParents());
}
return parents;
}
public Matrix4 getTransformMatrix() {
List<SKNode> nodes = getParents();
Collections.reverse(nodes);
nodes.add(this);
Matrix4 transformMatrix = new Matrix4();
transformMatrix.idt();
for (SKNode node : nodes) {
transformMatrix.translate(node.position.x + node.origin.x, node.position.y + node.origin.y, 0);
transformMatrix.rotate(0, 0, 1, node.zRotation);
transformMatrix.scale(node.xScale, node.yScale, 0);
transformMatrix.translate(-node.origin.x, -node.origin.y, 0);
}
return transformMatrix;
}
It is slow to do things that cause the sprite batch to "flush", which means it has to issue a number of OpenGL commands and transfer vertex data, etc. A flush occurs when you call spriteBatch.end() but also occurs if you:
Draw something with a different texture instance than the last thing drawn
Change the projection or transform matrices
Enable/disable blending
So you want to organize so you are not triggering a flush on every object you draw. What is typically done is to begin the batch, and then each sprite is drawn at its particular location with one of the spriteBatch.draw() methods that includes all the parameters you want. Like this:
batch.setProjectionMatrix(camera.combined);
batch.begin();
for (GameObject obj : myGameObjects)
obj.draw();
batch.end();
//And in your game object / sprite class
public void draw() {
if (texture == null)
return;
spriteBatch.setColor(1f, 1f, 1f, getAlpha());
spriteBatch.draw(texture, -size.width / 2, -size.height / 2, size.width, size.height);
}
Note that the above assumes you are referring to the same sprite batch instance in every object.
Now, to make it behave more like SpriteKit (I'm assuming since I haven't used it), your objects each need a transform. But you don't want to be calling setTransformMatrix or you will trigger a flush. Instead you can use the Affine2 class instead of Matrix4. It functions just as well for holding transform data for a 2D object. Then you can use spriteBatch.draw(textureRegion, width, height, affine2Transform) to draw it without triggering a flush.
To avoid triggering flushes from using different texture instances, you should use a TextureAtlas and texture regions. You can read up on that in the LibGDX documentation on their wiki.
As an aside, when using SpriteBatch, you do not need to make OpenGL calls to enable and disable blending. That is handled internally by SpriteBatch. Call spriteBatch.enableBlending() instead.
I've been writing Java SE 8 desktop application. I use Eclipse IDE, Oracle's JDK, and run it on MS Windows 10 OS.
My app draws diagrams, in short. I draw a diagram on JPanel which becomes part of JTabbedPane. It displays it well on GUI, and it is very responsive. The problem shows up when I pass the diagram on printing service. But instead of printing it on printer, I choose, "Microsoft print to PDF" service. What happens next, is that if the diagram is large, when you scroll it down, you will observe that its quality drops down. That is, grids start disappearing, new lines appear, etc.
The pic out here.
As you can see, eventually vertical grids vanish, diagonal line creeps in, and later gets even worse. And that is unwelcome.
Relevant code in here:
public final class GanttChartDiagram extends TopDiagram{
#Override
public void paintComponent(Graphics graph){
super.paintComponent(graph);
Graphics2D g2D = (Graphics2D)graph;
g2D.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
...........
#Override
public Dimension getPreferredSize(){
return new Dimension(this.diagramWidth + 20, this.diagramHeight + 20);
}
}
}
The getPreferredSize() method identifies the size of diagram, so that the app knows how to adjust the scroll-bars accordingly to fit the diagram in. Otherwise by default it return 0, if not overridden.
That is the class where I draw the diagram.
The super-class out here:
public abstract class TopDiagram extends JPanel implements Printable {
private static final long serialVersionUID = 1469816888488484L;
#Override
public void paintComponent(Graphics graph){
super.paintComponent(graph);
};
/**
* Prints selected diagram
*/
#Override
public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) throws PrinterException {
Graphics2D dimension = (Graphics2D)graphics;
dimension.translate(pageFormat.getImageableX(), pageFormat.getImageableY());
if(pageIndex < PrintingImageBuffer.getImageBuffer().size()){
dimension.drawImage(PrintingImageBuffer.getImageBuffer().get(pageIndex), null, 0, 0);
return PAGE_EXISTS;
}
else{
return NO_SUCH_PAGE;
}
}
}
Now that is where I print the diagram:
public static void printDiagram(){
new Thread(new Runnable(){
public void run(){
TopDiagram diagram = null;
if(id.equalsIgnoreCase("GanttChart")){
diagram = ganttChartDiagram;
}
final PrinterJob printer = PrinterJob.getPrinte
rJob();
printer.setJobName("Printing the "+id+" Diagram");
PageFormat format = printer.pageDialog(page);
int nowWidth = (int)diagram.getPreferredSize().getWidth();
int nowHeight = (int)diagram.getPreferredSize().getHeight();
BufferedImage buffImg = new BufferedImage(nowWidth, nowHeight, BufferedImage.TYPE_INT_ARGB);//default type
Graphics2D d = (Graphics2D)buffImg.getGraphics();
....etc..................
}
}).start();
}
Now the interesting part is this:
int nowWidth = (int)diagram.getPreferredSize().getWidth();
int nowHeight = (int)diagram.getPreferredSize().getHeight();
On the same instance of diagram, on multiple invocations of print (or within the method), it may or may not return different values. So that it was causing me some sort of Raster exception. But I managed to get rid off of that exception by invoking size method only once and reusing that size value throughout the method. So that, the size value stays the same, cause it is read only once.
Bad solution, but it works.
I would like to solve this issue too. Firstly, how come that this invocation diagram.getPreferredSize().getWidth() on the same instance of diagram obj. returns different size value? One more thing, is that I overrode this method as has been presented above. And the diagram object is created only once, no recalculations.
This is where I create the diagram obj. on Swing Worker only once per application's life-cycle.
GanttChartSwingWorker ganttSwingWorker = new GanttChartSwingWorker(GanttChartDiagram::new, tabbedPane, showPerformanceDiagramTab, ganttChartDiagramTabReady);
ganttSwingWorker.execute();
new Thread(() -> {
try{
ganttChartDiagramTabReady.await();
ganttChartDiagram = ganttSwingWorker.getGanttChartDiagram();
}catch(InterruptedException ie){ie.printStackTrace();}
}
).start();
Swing Worker part:
diagram = this.get();
JScrollPane scrollPaneChart = addScrollPane(diagram);
tabbedPane.addTab("Gantt Chart", null, scrollPaneChart, "Displays Gantt Chart Diagram");
Some diagram objects can be time consuming to create, imposes delay, so I use Swing Worker to do that.
So, in summary:
How to make the diagram to appear clean when I print/save it on pdf file in the way that I explained?
How to make the diagram size to calculate consistently as per multiple invocations? What leads to different diagram size values retrieving it from the same diagram object instance on multiple calls?
I just figured out what the problem is.
I observed that when it comes to drawing the diagram, the
paintComponent(Graphics g) method has been invoked repeatedly. It keeps redrawing the diagram over and over again. It is invoked by the system implicitly, yet my implementation had been triggering it.
And that trigger comes in the form of this.setSize(width, height) method on derived JPanel object. So that each time the paintComponent(Graphics g) re-executes, it sets the size on JPanel which yet triggers additional execution of the painComponent method. In the end, it is an infinite loop.
And that infinite execution was the cause of the problem; it was producing distorted diagram image on pdf file.
Solution: execute the setSize method only when it is needed; on initial panel set-up, or resize.
I am very new to JAVA, and am attempting to write a pharmacokinetics calculation program for my students to use.
The program reads a database of plasma concentration vs time values and calculates a number of pharmacokinetic parameters and outputs these into a number of frames, as well as drawing linear and exponential curves which are also output into frames.
I wrote the program initially as a console based, to make sure everything worked as required, before moving to GUI. Now comes the difficult steps of GUI building. I realize my console code will change considerably, I'm looking forward to the learning curve.
I am now attempting to output results via a separate 'GUI' class, and so pass frames between classes before output. I have tried to pass a frame from my 'GUI' class to my other class, without success.
In my attempt to learn GUI, and the class structure of JAVA, I have two classes.
A ProjectChart Class, that contains all the calculation and graphing methods.
A ProjectChartGUI class, which will eventually be used to output all data.
ProjectChartGUI currently defines a number of InternalFrames. I have been trying to pass one such internalframe to a function in ProjectChart, OutputInputData(). This constructs a table of data, read by a separate function, and will output this table into a frame for user validation.
I am trying to pass a JInternalFrame from ProjectChartGUI to OutputInputData().
Initially I passed the JInternalFrame using a .getFrame() function using OutputInputData (JInternalFrame frame).
I also tried to pass a ProjectchartGUI object. OutputInputData (ProjectChartGUI tProjectChartGUI). This also did not work.
In both cases, the program runs, all calculations are performed successfully, however the frame which opens via ProjectChartGUI is empty.
Here is my main:
public static void main(String[] args) throws IOException, InterruptedException, URISyntaxException{
ProjectChartGUI tGUI = new ProjectChartGUI();
/* Create and display the form */
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
ActionPharmacokinetics(tGUI);
tGUI.setVisible(true);
}
});
}
Here is a snippet of the function ActionPharmacokinetics():
public static void ActionPharmacokinetics(ProjectChartGUI tGUI){
DetermineUserInput();
if(CHECK == IVBOLUS_TRUE)
{
// Read database to access main information
Access_IVBOLUS_DatabaseList();
// Read database again to allow user to select desired entry for calculation
Access_IVBOLUS_Database();
// Output CP and T values to a new window for confirmation
OutputInputData(tGUI);
//Diplay Linear chart of Data
Display_IVBOLUS_LinearGraph();
// Display exponential chart of Data
Display_IVBOLUS_ExponentialGraph();
// Calculate pharmacokinetic parameters
Calculate_IVBOLUS_PharmacokineticParameters();
// Output pharmacokinetic paramaters
OutputPharmacokineticParamaters();
}}
Here is outputInputData(): Cp, lnCp and T are declared variables in ProjectChart. Numberofpoints is the numberofdata points read from the database.
static public void OutputInputData(ProjectChartGUI tGUI){
//create table objects
String[] ColumnNames = new String[]{"Data Point", "CP"+Units_CP,"ln CP","Time"+Units_T};
double SetDecimalPoint = 0.0;
Object[][] Data = new Object[NumberofPoints][4];
for (int k=0;k<NumberofPoints;k++)
{
Data[k][0] = k+1;//index
Data[k][1] = Cp[k];//plasma
SetDecimalPoint = lnCp[k];//log plasma
BigDecimal bd = BigDecimal.valueOf(SetDecimalPoint);
bd = bd.setScale(2, BigDecimal.ROUND_FLOOR); // rounds ln cp to 2 decimal places for output
Data[k][2] = bd;
Data[k][3] = T[k];//time
}
//create table
JTable jtable = new JTable(Data, ColumnNames);
//Create frame
//JFrame jFrame = new JFrame("Input Data");
//jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//add table to frame
tGUI.getframe().add(new JScrollPane(jtable));
tGUI.getframe().pack(); //auto size
tGUI.getframe().setVisible(true);
}
.getframe() returns an internal frame from ProjectChartGUI
public javax.swing.JInternalFrame getframe(){
return jInternalFrame1;
}
I used Netbeans GUI builder to define a frame, with an internal frame
private javax.swing.JInternalFrame jInternalFrame1;
All other code in ProjectChartGUI was autogenerated by Netbeans.
Does anyone have any suggestions on where I may be going wrong?
Hi Blender & libGDX Gurus,
I am trying to load an blender animation into libGDX for an android app. So I created an animation using Blenders action editor, I exported this into .fbx format. I then ran the fbx-conv tool to create a .G3DB file. I then proceeded to upload this file into libGDX using the modelLoader.
Everything seems to work fine (I am not receiving any error messages) except that the screen is blank. I can't see any animations or the model.
I have run this code in a Samsung galaxy tablet running kitkat, nexus phone running marshmallow and an emulator, but the same result.
I went thru the tutorials and am using some of the code to upload one of my blender models. I still can't figure this out and I need help figuring this out.
Any help will be greatly appreciated.
Here is the link to the Blender file:
Animated Low-Poly Horse No Lights and no Camera
Here is my code where I am uploading the model in libGDX. I basically am using the code from the tutorials.
#Override
public void create () {
// Create camera sized to screens width/height with Field of View of 75 degrees
camera = new PerspectiveCamera(
75,
Gdx.graphics.getWidth(),
Gdx.graphics.getHeight());
// Move the camera 5 units back along the z-axis and look at the origin
camera.position.set(0f,0f,7f);
camera.lookAt(0f,0f,0f);
// Near and Far (plane) represent the minimum and maximum ranges of the camera in, um, units
camera.near = 0.1f;
camera.far = 300.0f;
camera.update();
// A ModelBatch to batch up geometry for OpenGL
modelBatch = new ModelBatch();
// Model loader needs a binary json reader to decode
UBJsonReader jsonReader = new UBJsonReader();
// Create a model loader passing in our json reader
G3dModelLoader modelLoader = new G3dModelLoader(jsonReader);
// Now load the model by name
// Note, the model (g3db file ) and textures need to be added to the assets folder of the Android proj
model = modelLoader.loadModel(Gdx.files.getFileHandle("AnimatedLowPolyHorseStageFenced_Ver5.g3db", Files.FileType.Internal));
// Now create an instance. Instance holds the positioning data, etc of an instance of your model
modelInstance = new ModelInstance(model);
//fbx-conv is supposed to perform this rotation for you... it doesnt seem to
modelInstance.transform.rotate(1, 0, 0, -90);
//move the model down a bit on the screen ( in a z-up world, down is -z ).
modelInstance.transform.translate(0, 0, -2);
// Finally we want some light, or we wont see our color. The environment gets passed in during
// the rendering process. Create one, then create an Ambient ( non-positioned, non-directional ) light.
environment = new Environment();
environment.set(new ColorAttribute(ColorAttribute.AmbientLight, 0.8f, 0.8f, 0.8f, 1.0f));
environment.add(new DirectionalLight().set(0.8f, 0.8f, 0.8f, -1f, -0.8f, -0.2f));
// You use an AnimationController to um, control animations. Each control is tied to the model instance
controller = new AnimationController(modelInstance);
// Pick the current animation by name
controller.setAnimation("Armature|ArmatureAction",1, new AnimationListener(){
#Override
public void onEnd(AnimationDesc animation) {
// this will be called when the current animation is done.
// queue up another animation called "balloon".
// Passing a negative to loop count loops forever. 1f for speed is normal speed.
//controller.queue("Armature|ArmatureAction",-1,1f,null,0f);
}
#Override
public void onLoop(AnimationDesc animation) {
// TODO Auto-generated method stub
}
});
}
#Override
public void resize(int width, int height) {
super.resize(width, height);
}
#Override
public void render () {
Gdx.gl.glViewport(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
//Gdx.gl.glClearColor(1, 1, 1, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);
// For some flavor, lets spin our camera around the Y axis by 1 degree each time render is called
// You need to call update on the animation controller so it will advance the animation. Pass in frame delta
controller.update(Gdx.graphics.getDeltaTime());
// Like spriteBatch, just with models! pass in the box Instance and the environment
modelBatch.begin(camera);
modelBatch.render(modelInstance, environment);
modelBatch.end();
}
When converting to G3DB with fbxconv you got a warning,
"Mesh contains vertices with zero bone weights".
Try the following steps:
- Add a a new bone to your blend
- Connect it to non-animated (or all) vertices
- Re-export & convert
If you still get the warning, repeat but connect the new bone to all vertices.
I know this is an old question, but i had a similar problem recently and this worked.