Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8161587

FXCanvas does not consistently render the scenegraph when long running event handlers are used.

    Details

    • Subcomponent:
    • CPU:
      x86_64
    • OS:
      linux

      Description

      FULL PRODUCT VERSION :


      ADDITIONAL OS VERSION INFORMATION :
      Linux wienand 3.13.0-24-generic #46-Ubuntu SMP Thu Apr 10 19:11:08 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux

      EXTRA RELEVANT SYSTEM CONFIGURATION :
      SWT without GTK3 (set SWT_GTK3=0).

      A DESCRIPTION OF THE PROBLEM :
      In a stand-alone JavaFX application, the UI is updated more frequently than in an application that is embedded into SWT using FXCanvas. The FXCanvas should ensure that the UI is updated in between the processing of events when the registered event handlers are slow.

      The example application that is attached to this report can be run in embedded mode (default, EMBEDDED=true) and in stand-alone mode (set EMBEDDED=false). When in stand-alone mode, the circle can be dragged and its position is updated regularly. However, when in embedded mode, the circle can be dragged but its position is not updated regularly, but only occasionally.

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      1. Run the attached SlowDragSnippet in embedded mode (default).
      2. Drag the circle with the mouse.
      3. Observe how infrequent the rendering is updated.
      4. Run the attached SlowDragSnippet in stand-alone mode (set EMBEDDED=false).
      5. Drag the circle with the mouse.
      6. Observe how frequent the rendering is updated.

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      When embedding a JavaFX application into SWT using FXCanvas, the rendering should be updated with a similar frequency compared to a stand-alone JavaFX application, even though long running event handlers are used.
      ACTUAL -
      When embedding a JavaFX application into SWT using FXCanvas, the rendering is only updated very rarely if long running event handlers are used.

      REPRODUCIBILITY :
      This bug can be reproduced always.

      ---------- BEGIN SOURCE ----------
      package org.eclipse.gef4.fx.examples.swt;

      import org.eclipse.gef4.fx.swt.canvas.FXCanvasEx;
      import org.eclipse.swt.SWT;
      import org.eclipse.swt.layout.FillLayout;
      import org.eclipse.swt.widgets.Display;
      import org.eclipse.swt.widgets.Shell;

      import javafx.application.Application;
      import javafx.embed.swt.FXCanvas;
      import javafx.event.EventHandler;
      import javafx.scene.Scene;
      import javafx.scene.input.MouseEvent;
      import javafx.scene.layout.Pane;
      import javafx.scene.paint.Color;
      import javafx.scene.shape.Circle;
      import javafx.stage.Stage;

      public class SlowDragSnippet extends Application {

      private static final String TITLE = "Slow Drag Snippet";
      private static final Boolean EMBEDDED = true;

      public static void main(String[] args) {
      if (EMBEDDED) {
      new SlowDragSnippet().embed();
      } else {
      launch();
      }
      }

      public SlowDragSnippet() {
      }

      public Scene createScene() {
      final Pane root = new Pane();
      final Scene scene = new Scene(root, 400, 400, Color.WHITE);

      final Circle circle = new Circle(100, 100, 25, Color.GREENYELLOW);
      root.getChildren().add(circle);

      scene.addEventFilter(MouseEvent.ANY, new EventHandler<MouseEvent>() {
      @Override
      public void handle(MouseEvent event) {
      // slow it down
      try {
      Thread.sleep(120);
      } catch (InterruptedException e) {
      e.printStackTrace();
      }
      // drag the circle
      if (event.getEventType() == MouseEvent.MOUSE_DRAGGED
      && event.getTarget() == circle) {
      circle.setCenterX(event.getSceneX());
      circle.setCenterY(event.getSceneY());
      }
      }
      });

      return scene;
      }

      public void embed() {
      Display display = new Display();
      Shell shell = new Shell(display);
      shell.setLayout(new FillLayout());
      FXCanvas canvas = new FXCanvasEx(shell, SWT.NONE);

      Scene scene = createScene();
      canvas.setScene(scene);

      shell.setSize((int) scene.getWidth(), (int) scene.getHeight());
      shell.setText(TITLE + " (embedded)");
      shell.open();

      while (!shell.isDisposed()) {
      if (!display.readAndDispatch()) {
      display.sleep();
      }
      }
      display.dispose();
      }

      @Override
      public void start(Stage primaryStage) throws Exception {
      primaryStage.setScene(createScene());
      primaryStage.sizeToScene();
      primaryStage.setTitle(TITLE + " (stand-alone)");
      primaryStage.show();
      }

      }
      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      The only workaround that I have found so far is implementing a custom EventDispatcher that spawns a Display-Runnable (Display#asyncExec) to update the SWT Display. Incoming mouse drag events are discarded until the display was updated:

      Scene scene = getCanvas().getScene();
      final EventDispatcher eventDispatcher = scene.getEventDispatcher();
      final boolean[] updated = new boolean[] { true };
      final Event[] discarded = new Event[] { null };
      scene.setEventDispatcher(new EventDispatcher() {
      @Override
      public Event dispatchEvent(Event event, EventDispatchChain tail) {
      if (!updated[0] && event.getEventType() == MouseEvent.MOUSE_DRAGGED) {
      discarded[0] = event;
      return event;
      }
      // schedule runnable to update the UI
      updated[0] = false;
      Display.getDefault().asyncExec(new Runnable() {
      @Override
      public void run() {
      while (Display.getDefault().readAndDispatch()) {
      ;
      }
      updated[0] = true;
      }
      });
      // dispatch the last discarded drag event (if any)
      if (discarded[0] != null && event.getEventType() != MouseEvent.MOUSE_DRAGGED) {
      long millis = System.currentTimeMillis();
      // TODO: What to do with the dispatched event?
      eventDispatcher.dispatchEvent(discarded[0], tail);
      millis = System.currentTimeMillis() - millis;
      // if (millis > 100) { SLOW! }
      }
      discarded[0] = null;
      // dispatch the event
      long millis = System.currentTimeMillis();
      Event returnedEvent = eventDispatcher.dispatchEvent(event, tail);
      millis = System.currentTimeMillis() - millis;
      // if (millis > 100) { SLOW! }
      return returnedEvent;
      }
      });

        Activity

        There are no comments yet on this issue.

          People

          • Assignee:
            azvegint Alexander Zvegintsev
            Reporter:
            webbuggrp Webbug Group
          • Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

            Dates

            • Created:
              Updated: