簡體   English   中英

如何在不影響 animation 性能的情況下執行 JavaFX 場景圖更新

[英]How to perform JavaFX scene graph updates without affecting animation performance

我正在尋找有關在 animation 運行時更新 JavaFX 場景圖的建議。 我擁有的是 3D 場景中的自定義細節級別節點,該場景在后台線程中加載並創建 MeshView。 沒關系,它可以在不影響 JavaFX 應用程序線程的情況下工作。 但問題是,當我更新這樣的場景圖中的節點(實際上只是替換組節點中的一個子節點)時(在 JavaFX 應用程序線程上運行此部分):

Group group = (some value that is a live node in the scene graph ...)
group.getChildren().set (0, response.view);   <-- response.view is my MeshView instance

它可以工作,但是像這樣的大量更改在短時間內被推送到場景中,使得 3D 場景的 animation 不寒而栗。 我知道為什么會這樣——Pulse 正在做很多工作來將場景圖更新到顯卡。 但我想要一些關於如何最好地處理這個問題的建議和/或代碼示例。 我能想到的一件事是使用生產者/消費者 model 生成場景圖更改並將其放入隊列中,然后消費者一次只會消耗這么多。 但是有時 animation 會停止並且不再運行,並且可以將場景圖更改盡快推送到場景中。

有沒有在我沒有找到的地方在線處理這個問題的例子? 或者一些有用的標准做法/解決方案? 基本上就像我想確保 animation 的幀速率,並且只以可以在不破壞 animation 速率的情況下處理的速率推送更改,但我不知道如何確定該速率。 我不知道如何測量我的每個場景圖修改實際上在幕后花費的時間長度,或者低於通常 60 Hz 的脈沖速率,以便我可以在需要時減少我的影響。

所以我有一些效果很好的東西。 每當我需要執行場景圖更新並且我不知道 animation 是否正在運行(因為我在另一個班級)時,我將場景圖更新包裝在消費者接受的 Runnable 中:

private Consumer<Runnable> updateConsumer;
...
updateConsumer.accept (() -> group.getChildren().set (0, response.view));

然后在作為消費者最終目的地的 class 中,我將場景圖更新添加到隊列中:

private ConcurrentLinkedQueue<Runnable> sceneGraphChangeQueue;
...
public void addSceneGraphChange (Runnable change) {
  sceneGraphChangeQueue.add (change);
}

當執行 animation 時,我觀察到一個變量,我知道該變量在 animation 的每個步驟中都在發生變化,並將一個偵聽器附加到它,以便它可以跟蹤正在發生的 animation。 就我而言,我正在跟蹤我的相機 position 並且我保留了自上次重置計數器以來相機 position 更新的計數(我將在下面解釋其目的):

private int cameraUpdatesSinceReset;
...
view.cameraPositionProperty().addListener ((obs, oldVal, newVal) -> {
  cameraUpdatesSinceReset++;
  performSceneChanges (SCENE_CHANGES_PER_PULSE);
});
...
private void performSceneChanges (
  int limit
) {

  int count = 0;
  while (!sceneGraphChangeQueue.isEmpty() && count < limit) {
    Runnable change = sceneGraphChangeQueue.remove();
    change.run();
    count++;
   } // while

  if (count != 0) LOGGER.finer ("Performed " + count + " scene graph changes");

} // performSceneChanges

這樣一來,場景圖的變化就會在每個 animation 脈沖期間逐漸發生變化,我發現在 animation 開始顫抖之前,設置 SCENE_CHANGES_PER_PULSE=2 幾乎是我的機器所能處理的全部。 最后,為了確保在不再播放動畫時刷新場景更改隊列,我使用 ScheduledService 定期輪詢隊列並檢查我的 cameraUpdatesSinceReset 計數器:

ScheduledService<Void> service = new ScheduledService<Void>() {
  protected Task<Void> createTask() {
    return new Task<Void>() {
      protected Void call() {
        Platform.runLater (() -> {
          if (cameraUpdatesSinceReset == 0) {
            performSceneChanges (Integer.MAX_VALUE);
          } // if
          else cameraUpdatesSinceReset = 0;
        });
        return (null);
      } // call
    };
  } // createTask
};
service.setPeriod (Duration.millis (333));
service.start();

該服務每秒輪詢 3 次隊列,這足以讓 20 個脈沖通過,如果相機在那段時間內沒有更新,我假設沒有 animation 或用戶交互正在進行。 結果是 animation 是平滑的,並且系統會盡可能多地更新場景圖。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM