Reputation: 634
I have a basic JavaFX program consisting of a window with some tabs and a few buttons, etc. On OSX and Linux, everything works great, but on Windows 10, moving the main window around seems to put tremendous strain on my graphics card, and whenever I switch between my application's window and another window, the whole screen flickers for a second. This only happens with this application, and the system is a high performance gaming rig with G-Sync, 980 GTX, i7-6700k, 16 GB RAM, dual monitors, etc., and does extremely well on a variety of CPU and GPU benchmarks, and really, really, shouldn't be having any problems moving a window around on the screen.
When I watch the Java process in the task manager, there aren't any noticeable changes in RAM usage, and CPU usage is only at ~1.4% when moving the window all over the screen. When the window is being dragged, it seems to only update at around 20 FPS, whereas moving any other window on my system seems to run at 60+ FPS. So in short, there is some kind of weird performance bottleneck related to visually rendering my JavaFX window.
To my knowledge, I am not using any 3D features or anything of that manner, and I'm not doing anything weird JavaFX-wise. My window creation code was based on standard tutorials from Oracle on how to initialize JavaFX windows, etc.
Below is the initialization code for my JavaFX window, and the init method from the controller:
Main.start():
@Override
public void start(Stage primaryStage) throws Exception {
primaryStage.hide();
lockInstance();
Platform.setImplicitExit(false);
hostServices = HostServicesFactory.getInstance(this);
AccountManager.init();
if(OS.isLinux()) {
CURRENT_OS = OS_TYPE.LINUX;
} else if(OS.isMacOS()) {
CURRENT_OS = OS_TYPE.OSX;
} else if(OS.isWindows()) {
CURRENT_OS = OS_TYPE.WINDOWS;
}
System.out.println("detected os: " + CURRENT_OS);
//AccountManager.urlPrefix = "http://localhost:8080/api/";
String res = "ERROR";
try {
res = JWSystem.getAppBundleName();
} catch (Exception e) {}
if(!res.contains("ERROR"))
DEPLOY_MODE = true;
System.out.println("DEPLOY_MODE: " + DEPLOY_MODE);
prefs = Preferences.userNodeForPackage(this.getClass());
FIRST_RUN = prefs.getBoolean("first_run", true);
prefs.putBoolean("first_run", false);
System.out.println("FIRST_RUN: " + FIRST_RUN);
if(FIRST_RUN && DEPLOY_MODE) {
createDesktopIcon();
}
// setup
Thread t = new Thread(new Runnable() {
@Override
public void run() {
if(CURRENT_OS == OS_TYPE.LINUX)
SystemIcons.initUbuntuMimeTypes();
if(FIRST_RUN && DEPLOY_MODE) {
addStartupEntry();
}
}
});
t.start();
// load UI
Parent root = FXMLLoader.load(getClass().getResource("assets/main.fxml"));
primaryStage.resizableProperty().setValue(Boolean.FALSE);
primaryStage.setTitle("DuroCloud");
Scene scene = new Scene(root, 600, 400);
mainScene = scene;
primaryStage.setScene(scene);
mainStage = primaryStage;
scene.getStylesheets().addAll(getClass().getResource("assets/overrides.css").toExternalForm());
primaryStage.show();
primaryStage.setOnCloseRequest(new EventHandler<WindowEvent>() {
public void handle(WindowEvent we) {
Platform.exit();
System.out.println("Stage is closing");
System.exit(0);
}
});
}
Controller.initialize():
public void initialize(URL fxmlFileLocation, ResourceBundle resources) {
faFolderIconImage = new Image(this.getClass().getResourceAsStream("assets/fa-folder_256_0_464646_none.png"));
faFileIconImage = new Image(this.getClass().getResourceAsStream("assets/fa-file_256_0_464646_none.png"));
faTextFileIconImage = new Image(this.getClass().getResourceAsStream("assets/fa-file-text_256_0_464646_none.png"));
faPdfFileIconImage = new Image(this.getClass().getResourceAsStream("assets/fa-file-pdf-o_256_0_464646_none.png"));
faMovieFileIconImage = new Image(this.getClass().getResourceAsStream("assets/fa-file-movie-o_256_0_464646_none.png"));
faImageFileIconImage = new Image(this.getClass().getResourceAsStream("assets/fa-file-image-o_256_0_464646_none.png"));
faExcelFileIconImage = new Image(this.getClass().getResourceAsStream("assets/fa-file-excel-o_256_0_464646_none.png"));
faCodeFileIconImage = new Image(this.getClass().getResourceAsStream("assets/fa-file-code-o_256_0_464646_none.png"));
faAudioFileIconImage = new Image(this.getClass().getResourceAsStream("assets/fa-file-audio-o_256_0_464646_none.png"));
faArchiveFileIconImage = new Image(this.getClass().getResourceAsStream("assets/fa-file-archive-o_256_0_464646_none.png"));
// text files
iconMap.put("txt", faTextFileIconImage);
iconMap.put("doc", faTextFileIconImage);
iconMap.put("docx", faTextFileIconImage);
iconMap.put("rtf", faTextFileIconImage);
iconMap.put("asc", faTextFileIconImage);
iconMap.put("cfg", faTextFileIconImage);
iconMap.put("log", faTextFileIconImage);
iconMap.put("ini", faTextFileIconImage);
iconMap.put("odt", faTextFileIconImage);
// pdf
iconMap.put("pdf", faPdfFileIconImage);
// movie files
String[] movie_formats = new String[] {"webm", "mkv", "flv", "vob", "ogv", "drc", "avi", "mov", "wmv", "yuv",
"rm", "rmvb", "asf", "mp4", "m4p", "m4v", "mpg", "mp2", "mpeg", "mpe", "mpv", "svi",
"3gp", "mxf", "roq", "nsv"};
for(String format : movie_formats) {
iconMap.put(format, faMovieFileIconImage);
}
// image files
String[] image_formats = new String[] {"png", "bmp", "jpg", "jpeg", "tiff", "gif", "raw"};
for(String format : image_formats) {
iconMap.put(format, faImageFileIconImage);
}
// excel
iconMap.put("xls", faExcelFileIconImage);
iconMap.put("ods", faExcelFileIconImage);
iconMap.put("xlsx", faExcelFileIconImage);
// code
iconMap.put("rb", faCodeFileIconImage);
iconMap.put("js", faCodeFileIconImage);
iconMap.put("html", faCodeFileIconImage);
iconMap.put("htm", faCodeFileIconImage);
iconMap.put("css", faCodeFileIconImage);
iconMap.put("xml", faCodeFileIconImage);
iconMap.put("php", faCodeFileIconImage);
iconMap.put("exe", faCodeFileIconImage);
iconMap.put("sh", faCodeFileIconImage);
iconMap.put("py", faCodeFileIconImage);
iconMap.put("bat", faCodeFileIconImage);
iconMap.put("jar", faCodeFileIconImage);
iconMap.put("java", faCodeFileIconImage);
iconMap.put("c", faCodeFileIconImage);
iconMap.put("cpp", faCodeFileIconImage);
iconMap.put("h", faCodeFileIconImage);
iconMap.put("makefile", faCodeFileIconImage);
iconMap.put("make", faCodeFileIconImage);
// audio
iconMap.put("asf", faAudioFileIconImage);
iconMap.put("mp3", faAudioFileIconImage);
iconMap.put("flac", faAudioFileIconImage);
iconMap.put("ogg", faAudioFileIconImage);
iconMap.put("wav", faAudioFileIconImage);
iconMap.put("wma", faAudioFileIconImage);
iconMap.put("webm", faAudioFileIconImage);
// archive
iconMap.put("7z", faArchiveFileIconImage);
iconMap.put("zip", faArchiveFileIconImage);
iconMap.put("rar", faArchiveFileIconImage);
iconMap.put("tar", faArchiveFileIconImage);
iconMap.put("gz", faArchiveFileIconImage);
iconMap.put("bz", faArchiveFileIconImage);
VBox accountAltVbox = new VBox();
HBox accountAltHbox = new HBox();
accountAltHbox.setAlignment(Pos.CENTER);
accountAltVbox.setLayoutX(60.0);
accountAltVbox.setLayoutY(40.0);
accountAltVbox.setSpacing(8);
accountEmailLabel.setText("test");
accountEmailLabel.setFont(new Font(18));
accountAltContent.setPrefWidth(800);
accountAltContent.setPrefHeight(800);
accountAltContent.getChildren().add(accountAltVbox);
accountAltVbox.getChildren().add(new Label("Logged in as:"));
accountAltHbox.getChildren().add(accountEmailLabel);
accountAltVbox.getChildren().add(accountAltHbox);
accountAltVbox.getChildren().add(accountLogoutButton);
UIFonts.setFontAwesomeGlyph(upButton, FontAwesomeIcon.LEVEL_UP);
UIFonts.setFontAwesomeGlyph(refreshButton, FontAwesomeIcon.REFRESH);
UIFonts.setFontAwesomeGlyph(openButton, FontAwesomeIcon.FOLDER_OPEN);
UIFonts.setFontAwesomeGlyph(saveButton, FontAwesomeIcon.DOWNLOAD);
UIFonts.setFontAwesomeGlyph(deleteButton, FontAwesomeIcon.TRASH);
UIFonts.setFontAwesomeGlyph(addButton, FontAwesomeIcon.PLUS);
UIFonts.setFontAwesomeGlyph(backupDirectoriesButton, FontAwesomeIcon.CLOUD);
loginEmailField.setOnKeyPressed(this::handleEmailFieldEnter);
loginPasswordField.setOnKeyPressed(this::handlePasswordFieldEnter);
loginButton.setOnAction(this::handleLoginButtonAction);
forgotPasswordLink.setOnAction(this::visitForgotPassword);
accountLogoutButton.setOnAction(this::handleLogoutButton);
refreshButton.setOnAction(this::handleRefreshButton);
upButton.setOnAction(this::handleUpButton);
addButton.setOnAction(this::handleAddButton);
deleteButton.setOnAction(this::handleDeleteButton);
fileBrowser.setCellFactory(new Callback<ListView<BrowserItem>, ListCell<BrowserItem>>() {
@Override
public ListCell<BrowserItem> call(ListView<BrowserItem> param) {
return new BrowserItemCell();
}
});
fileBrowser.setOnMouseClicked(new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent event) {
if(event.getClickCount() == 2) {
BrowserItem selected = fileBrowser.getSelectionModel().getSelectedItem();
if(selected != null) {
if(selected.isFile) {
downloadSelected();
} else {
currentDirectoryId = selected.file.file_id;
refresh();
}
}
}
}
});
openButton.setDisable(true);
saveButton.setDisable(true);
deleteButton.setDisable(false);
if(!AccountManager.loggedIn()) {
mainTabPane.getSelectionModel().select(accountTab);
optionsTab.setDisable(true);
myFilesTab.setDisable(true);
}
}
Upvotes: 1
Views: 1321
Reputation: 634
So it turns out this was a threading-related issue that had nothing to do with JavaFX, and it disappeared once I re-organized some things in my app. As it turned out, an update method on a list view was getting called whenever the window was dragged, causing a computationally expensive method to run. What's interesting, though is that the performance differences was only noticeable on the windows 10 system, and did cause some significant graphics issues.
TLDR: don't do computationally expensive things in control drawing code, especially on Windows 10.
Upvotes: 1