Sam Johnson
Sam Johnson

Reputation: 634

JavaFX 8 performance issues on Windows 10

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

Answers (1)

Sam Johnson
Sam Johnson

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

Related Questions