JDK-8090755 : Canvas Rendering Degrades Proportionally With Size?
  • Type: Enhancement
  • Component: javafx
  • Sub-Component: graphics
  • Affected Version: 7u55
  • Priority: P4
  • Status: Open
  • Resolution: Unresolved
  • Submitted: 2014-04-19
  • Updated: 2018-09-05
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other
tbdUnresolved
Related Reports
Relates :  
Description
As context, I am working on a Painting Application.
 
The following is simply a StackPane with a Canvas of size 10000 X 10000.
This extreme size is just for emphasis purposes, as I don't know how powerful Your machine is. 
For art, most artists wouldn't go above 3000 X 3000, where latency is already getting pretty ugly for my machine, with this issue.
 
The Canvas has a MouseEventHandler attached, which allows You to freely draw on it.
 
You will notice, that as You decrease the Canvas size in the code, drawing will become more and more smooth.
After some testing, it seems like the whole Canvas is redrawn, for every small modification done in some area on it. 
Even when the bulk of it is off-screen.
 
That would imply that: 
a.) Not just the area the stroke occurred in is redrawn, but the whole Canvas. And... 
b.) Not just the portion of the Canvas visible on the screen is redrawn, but the whole Canvas.
 
Depth of my Knowledge: 
I am not a professional in Computer Graphics Programming, so those above are my humble guesses. 
I am also still a Beginning JavaFX & Java Learner, with about 4 Months Java, and 2 Months JavaFX experience. 
I am not trying to waste anyone's time or to troll anyone. A simple guide in the right direction would also be appreciated, if this is an issue that can be avoided.

Here is a SSCCE:
 
import javafx.application.Application; 
import javafx.event.EventHandler; 
import javafx.event.EventType; 
import javafx.scene.Scene; 
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.input.MouseEvent; 
import javafx.scene.layout.StackPane; 
import javafx.stage.Stage;

public class CanvasPerformanceDecline extends Application 
{         
    @Override     
    public void start(Stage primaryStage)     
    {                 
        //Create Canvas and get GraphicsContext:         
        final Canvas          canvas          = new Canvas( 10000, 10000 );         
        final GraphicsContext graphicsContext = canvas.getGraphicsContext2D();                 

        //Allow DRAWING on Canvas with Mouse:         
        canvas.addEventHandler( MouseEvent.ANY,
                                 
                                             new EventHandler<MouseEvent>()                                 
                                             {                                     
                                                 double pmouseX;                                     
                                                 double pmouseY;    
                                                                     
                                                 @Override                                     
                                                 public void handle(MouseEvent mouseEvent)                                     
                                                 {                                         
                                                     EventType eventType = mouseEvent.getEventType();
                                                                                 
                                                     if (eventType == MouseEvent.MOUSE_PRESSED)                                         
                                                     {                                             
                                                         pmouseX = mouseEvent.getX();                                             
                                                         pmouseY = mouseEvent.getY();                                         
                                                     }                                         
                                                     if (eventType == MouseEvent.MOUSE_DRAGGED)                                         
                                                     {                                             
                                                         double mouseX = mouseEvent.getX();                                             
                                                         double mouseY = mouseEvent.getY();
                                                                                                                                                            
                                                         //Stroke Line:                                             
                                                         graphicsContext.strokeLine( pmouseX, pmouseY,mouseX,mouseY);
                                                                                         
                                                         pmouseX = mouseX;                                             
                                                         pmouseY = mouseY;                                         
                                                     }                                     
                                                 } //END - public void handle(...)
                                 
                                             }); //END - canvas.addEventHandler(...)
   
                      
        //PREPARE SCENE & STAGE:         
        StackPane root    = new StackPane();                   
                         root.getChildren().add(canvas);  
               
        Scene     scene   = new Scene(root, 300, 250);    
             
                      primaryStage.setTitle("CanvasPerformanceDecline");         
                      primaryStage.setScene(scene);         
                      primaryStage.show();
      
    } //END - public void start(...)   

         
    public static void main(String[] args)     
    {         
        launch(args);     
    }
     
} //END - class CanvasPerformanceDecline
 
 
 
Thanks for Your time and attention.
Comments
I've tested this on low-end machine with 3000 x 3000 canvas using 1000 iterations of Scene.snapshot(null) On D3D9 the time is 123 seconds, while with D3D9Ex it's 4.7 seconds, so we have huge performance gain here. With 2000 x 2000 canvas the D3D9Ex time is almost the same, on the D3D9 it's 56 seconds. This is exactly because the readback is performed only on D3D9, but not on D3D9Ex.
23-04-2014

I just noticed that the submitter is using Windows 7 which means that the Canvas is being implemented on top of D3D. There is currently an issue with D3D where we have to copy every pixel back to the main memory on every update because Windows 7 can decide to delete all of the pixels asynchronously without any pre-notification to us. We are currently looking at using D3D9ex to avoid this, but there are performance problems associated with that interface that need to be solved first. The issue to track the D3D9ex solution is RT-36571. The full-canvas readback on every frame is probably the primary issue that the submitter is seeing for this test case, and that readback gets more and more expensive as the Canvas grows in size (another area where dirty region tracking might help if we don't upgrade to D3D9ex).
22-04-2014

We do not currently track the dirty part of a Canvas so if you draw to any part of it we will render the entire area of it on the next pulse. The test case does not run on my machine and would not likely run on any machine since it attempts to create a Canvas that exceeds 2 of our internal allocation limits - we limit all textures to 4k x 4k and Canvas is currently limited to a single texture due to implementation issues - and we also limit an application to 256mb of total texture space allocated and a 10k x 10k x 32-bit texture would require 400mb of texture memory. If I reduce the test case down to 3k x 3k which is what the original poster was planning to use then it runs quite well on my machine even without any dirty region optimizations, though I could detect an ever so small lag if I maximized the window. Dirty region processing could benefit a slower machine, though, and it might eliminate the minimal lag I saw on this example.
22-04-2014

I would expect rendering of Canvas to be proportional to the size. Assigning to Jim who is looking into other Canvas issues, but I'm not sure whether there is anything actionable here.
22-04-2014