modules/graphics/src/main/java/com/sun/javafx/tk/quantum/ViewPainter.java

Print this page


   1 /*
   2  * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any


  32 import com.sun.javafx.geom.DirtyRegionPool;
  33 import com.sun.javafx.geom.RectBounds;
  34 import com.sun.javafx.geom.Rectangle;
  35 import com.sun.javafx.geom.transform.Affine3D;
  36 import com.sun.javafx.geom.transform.BaseTransform;
  37 import com.sun.javafx.geom.transform.GeneralTransform3D;
  38 import com.sun.javafx.sg.prism.NGCamera;
  39 import com.sun.javafx.sg.prism.NGNode;
  40 import com.sun.javafx.sg.prism.NGPerspectiveCamera;
  41 import com.sun.javafx.sg.prism.NodePath;
  42 import com.sun.prism.Graphics;
  43 import com.sun.prism.GraphicsResource;
  44 import com.sun.prism.Image;
  45 import com.sun.prism.Presentable;
  46 import com.sun.prism.RTTexture;
  47 import com.sun.prism.ResourceFactory;
  48 import com.sun.prism.Texture;
  49 import com.sun.prism.impl.PrismSettings;
  50 import com.sun.prism.paint.Color;
  51 import com.sun.prism.paint.Paint;
  52 import static com.sun.javafx.logging.PulseLogger.PULSE_LOGGER;
  53 import static com.sun.javafx.logging.PulseLogger.PULSE_LOGGING_ENABLED;
  54 
  55 /**
  56  * Responsible for "painting" a scene. It invokes as appropriate API on the root NGNode
  57  * of a scene to determine dirty regions, render roots, etc. Also calls the render root
  58  * to render. Also invokes code to print dirty opts and paint overdraw rectangles according
  59  * to debug flags.
  60  */
  61 abstract class ViewPainter implements Runnable {
  62     /**
  63      * An array of initially empty ROOT_PATHS. They are created on demand as
  64      * needed. Each path is associated with a different dirty region. We have
  65      * up to PrismSettings.dirtyRegionCount max dirty regions
  66      */
  67     private static NodePath[] ROOT_PATHS = new NodePath[PrismSettings.dirtyRegionCount];
  68 
  69     /*
  70      * This could be a per-scene lock but there is no guarantee that the
  71      * FX handlers called in GlassViewEventHandler would not modify other scenes.
  72      */


 209                 renderEverything = true;
 210             }
 211             sceneBuffer.contentsUseful();
 212             // Hijack the "g" graphics variable
 213             g = sceneBuffer.createGraphics();
 214             g.scale(pixelScale, pixelScale);
 215         } else if (sceneBuffer != null) {
 216             // We're in a situation where we have previously rendered to the sceneBuffer, but in
 217             // this render pass for whatever reason we're going to draw directly to the back buffer.
 218             // In this case we need to release the sceneBuffer.
 219             sceneBuffer.dispose();
 220             sceneBuffer = null;
 221         }
 222 
 223         // The status will be set only if we're rendering with dirty regions
 224         int status = -1;
 225 
 226         // If we're rendering with dirty regions, then we'll call the root node to accumulate
 227         // the dirty regions and then again to do the pre culling.
 228         if (!renderEverything) {
 229             long start = PULSE_LOGGING_ENABLED ? System.currentTimeMillis() : 0;
 230             clip.setBounds(0, 0, width, height);
 231             dirtyRegionTemp.makeEmpty();
 232             dirtyRegionContainer.reset();
 233             tx.setToIdentity();
 234             projTx.setIdentity();
 235             adjustPerspective(sceneState.getCamera());
 236             status = root.accumulateDirtyRegions(clip, dirtyRegionTemp,
 237                                                      dirtyRegionPool, dirtyRegionContainer,
 238                                                      tx, projTx);
 239             dirtyRegionContainer.roundOut();
 240             if (status == DirtyRegionContainer.DTR_OK) {
 241                 root.doPreCulling(dirtyRegionContainer, tx, projTx);
 242             }
 243             if (PULSE_LOGGING_ENABLED) {
 244                 PULSE_LOGGER.renderMessage(start, System.currentTimeMillis(), "Dirty Opts Computed");
 245             }
 246         }
 247 
 248         // We're going to need to iterate over the dirty region container a lot, so we
 249         // might as well save this reference.
 250         final int dirtyRegionSize = status == DirtyRegionContainer.DTR_OK ? dirtyRegionContainer.size() : 0;
 251 
 252         if (dirtyRegionSize > 0) {
 253             // We set this flag on Graphics so that subsequent code in the render paths of
 254             // NGNode know whether they ought to be paying attention to dirty region
 255             // culling bits.
 256             g.setHasPreCullingBits(true);
 257 
 258             // Find the render roots. There is a different render root for each dirty region
 259             long start = PULSE_LOGGING_ENABLED ? System.currentTimeMillis() : 0;
 260             for (int i = 0; i < dirtyRegionSize; ++i) {
 261                 NodePath path = getRootPath(i);
 262                 path.clear();
 263                 root.getRenderRoot(getRootPath(i), dirtyRegionContainer.getDirtyRegion(i), i, tx, projTx);
 264             }
 265             if (PULSE_LOGGING_ENABLED) {
 266                 PULSE_LOGGER.renderMessage(start, System.currentTimeMillis(), "Render Roots Discovered");
 267             }
 268 
 269             // For debug purposes, write out to the pulse logger the number and size of the dirty
 270             // regions that are being used to render this pulse.
 271             if (PULSE_LOGGING_ENABLED) {
 272                 PULSE_LOGGER.renderMessage(dirtyRegionSize + " different dirty regions to render");
 273                 for (int i=0; i<dirtyRegionSize; i++) {
 274                     PULSE_LOGGER.renderMessage("Dirty Region " + i + ": " + dirtyRegionContainer.getDirtyRegion(i));
 275                     PULSE_LOGGER.renderMessage("Render Root Path " + i + ": " + getRootPath(i));
 276                 }
 277             }
 278 
 279             // If -Dprism.printrendergraph=true then we want to print out the render graph to the
 280             // pulse logger, annotated with all the dirty opts. Invisible nodes are skipped.
 281             if (PULSE_LOGGING_ENABLED && PrismSettings.printRenderGraph) {
 282                 StringBuilder s = new StringBuilder();
 283                 List<NGNode> roots = new ArrayList<>();
 284                 for (int i = 0; i < dirtyRegionSize; i++) {
 285                     final RectBounds dirtyRegion = dirtyRegionContainer.getDirtyRegion(i);
 286                     // TODO it should be impossible to have ever created a dirty region that was empty...
 287                     if (dirtyRegion.getWidth() > 0 && dirtyRegion.getHeight() > 0) {
 288                         NodePath nodePath = getRootPath(i);
 289                         if (!nodePath.isEmpty()) {
 290                             roots.add(nodePath.last());
 291                         }
 292                     }
 293                 }
 294                 root.printDirtyOpts(s, roots);
 295                 PULSE_LOGGER.renderMessage(s.toString());
 296             }
 297 
 298             // Paint each dirty region
 299             for (int i = 0; i < dirtyRegionSize; ++i) {
 300                 final RectBounds dirtyRegion = dirtyRegionContainer.getDirtyRegion(i);
 301                 // TODO it should be impossible to have ever created a dirty region that was empty...
 302                 // Make sure we are not trying to render in some invalid region
 303                 if (dirtyRegion.getWidth() > 0 && dirtyRegion.getHeight() > 0) {
 304                     // Set the clip rectangle using integer bounds since a fractional bounding box will
 305                     // still require a complete repaint on pixel boundaries
 306                     dirtyRect.setBounds(dirtyRegion);
 307                     // TODO I don't understand why this is needed. And if it is, are fractional pixelScale
 308                     // values OK? And if not, shouldn't pixelScale be an int instead?
 309                     if (pixelScale != 1.0f) {
 310                         dirtyRect.x *= pixelScale;
 311                         dirtyRect.y *= pixelScale;
 312                         dirtyRect.width *= pixelScale;
 313                         dirtyRect.height *= pixelScale;
 314                     }
 315                     g.setClipRect(dirtyRect);


 429 
 430         return sceneState.isWindowVisible() && !sceneState.isWindowMinimized();
 431     }
 432     
 433     protected float getPixelScaleFactor() {
 434         return presentable == null ? 0.1f : presentable.getPixelScaleFactor();
 435     }
 436 
 437     private void doPaint(Graphics g, NodePath renderRootPath) {
 438         // Null path indicates that occlusion culling is not used
 439         if (renderRootPath != null) {
 440             if (renderRootPath.isEmpty()) {
 441                 // empty render path indicates that no rendering is needed.
 442                 // There may be occluded dirty Nodes however, so we need to clear them
 443                 root.clearDirtyTree();
 444                 return;
 445             }
 446             // If the path is not empty, the first node must be the root node
 447             assert(renderRootPath.getCurrentNode() == root);
 448         }
 449         long start = PULSE_LOGGING_ENABLED ? System.currentTimeMillis() : 0;
 450         try {
 451             GlassScene scene = sceneState.getScene();
 452             scene.clearEntireSceneDirty();
 453             g.setLights(scene.getLights());
 454             g.setDepthBuffer(scene.getDepthBuffer());
 455             Color clearColor = sceneState.getClearColor();
 456             if (clearColor != null) {
 457                 g.clear(clearColor);
 458             }
 459             Paint curPaint = sceneState.getCurrentPaint();
 460             if (curPaint != null) {
 461                 if (curPaint.getType() != com.sun.prism.paint.Paint.Type.COLOR) {
 462                     g.getRenderTarget().setOpaque(curPaint.isOpaque());
 463                 }
 464                 g.setPaint(curPaint);
 465                 g.fillQuad(0, 0, width, height);
 466             }
 467             g.setCamera(sceneState.getCamera());
 468             g.setRenderRoot(renderRootPath);
 469             root.render(g);
 470         } finally {
 471             if (PULSE_LOGGING_ENABLED) {
 472                 PULSE_LOGGER.renderMessage(start, System.currentTimeMillis(), "Painted");
 473             }
 474         }
 475     }
 476 }
   1 /*
   2  * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any


  32 import com.sun.javafx.geom.DirtyRegionPool;
  33 import com.sun.javafx.geom.RectBounds;
  34 import com.sun.javafx.geom.Rectangle;
  35 import com.sun.javafx.geom.transform.Affine3D;
  36 import com.sun.javafx.geom.transform.BaseTransform;
  37 import com.sun.javafx.geom.transform.GeneralTransform3D;
  38 import com.sun.javafx.sg.prism.NGCamera;
  39 import com.sun.javafx.sg.prism.NGNode;
  40 import com.sun.javafx.sg.prism.NGPerspectiveCamera;
  41 import com.sun.javafx.sg.prism.NodePath;
  42 import com.sun.prism.Graphics;
  43 import com.sun.prism.GraphicsResource;
  44 import com.sun.prism.Image;
  45 import com.sun.prism.Presentable;
  46 import com.sun.prism.RTTexture;
  47 import com.sun.prism.ResourceFactory;
  48 import com.sun.prism.Texture;
  49 import com.sun.prism.impl.PrismSettings;
  50 import com.sun.prism.paint.Color;
  51 import com.sun.prism.paint.Paint;
  52 import com.sun.javafx.logging.PulseLogger;
  53 import static com.sun.javafx.logging.PulseLogger.PULSE_LOGGING_ENABLED;
  54 
  55 /**
  56  * Responsible for "painting" a scene. It invokes as appropriate API on the root NGNode
  57  * of a scene to determine dirty regions, render roots, etc. Also calls the render root
  58  * to render. Also invokes code to print dirty opts and paint overdraw rectangles according
  59  * to debug flags.
  60  */
  61 abstract class ViewPainter implements Runnable {
  62     /**
  63      * An array of initially empty ROOT_PATHS. They are created on demand as
  64      * needed. Each path is associated with a different dirty region. We have
  65      * up to PrismSettings.dirtyRegionCount max dirty regions
  66      */
  67     private static NodePath[] ROOT_PATHS = new NodePath[PrismSettings.dirtyRegionCount];
  68 
  69     /*
  70      * This could be a per-scene lock but there is no guarantee that the
  71      * FX handlers called in GlassViewEventHandler would not modify other scenes.
  72      */


 209                 renderEverything = true;
 210             }
 211             sceneBuffer.contentsUseful();
 212             // Hijack the "g" graphics variable
 213             g = sceneBuffer.createGraphics();
 214             g.scale(pixelScale, pixelScale);
 215         } else if (sceneBuffer != null) {
 216             // We're in a situation where we have previously rendered to the sceneBuffer, but in
 217             // this render pass for whatever reason we're going to draw directly to the back buffer.
 218             // In this case we need to release the sceneBuffer.
 219             sceneBuffer.dispose();
 220             sceneBuffer = null;
 221         }
 222 
 223         // The status will be set only if we're rendering with dirty regions
 224         int status = -1;
 225 
 226         // If we're rendering with dirty regions, then we'll call the root node to accumulate
 227         // the dirty regions and then again to do the pre culling.
 228         if (!renderEverything) {
 229             if (PULSE_LOGGING_ENABLED) PulseLogger.newPhase("Dirty Opts Computed");
 230             clip.setBounds(0, 0, width, height);
 231             dirtyRegionTemp.makeEmpty();
 232             dirtyRegionContainer.reset();
 233             tx.setToIdentity();
 234             projTx.setIdentity();
 235             adjustPerspective(sceneState.getCamera());
 236             status = root.accumulateDirtyRegions(clip, dirtyRegionTemp,
 237                                                      dirtyRegionPool, dirtyRegionContainer,
 238                                                      tx, projTx);
 239             dirtyRegionContainer.roundOut();
 240             if (status == DirtyRegionContainer.DTR_OK) {
 241                 root.doPreCulling(dirtyRegionContainer, tx, projTx);
 242             }



 243         }
 244 
 245         // We're going to need to iterate over the dirty region container a lot, so we
 246         // might as well save this reference.
 247         final int dirtyRegionSize = status == DirtyRegionContainer.DTR_OK ? dirtyRegionContainer.size() : 0;
 248 
 249         if (dirtyRegionSize > 0) {
 250             // We set this flag on Graphics so that subsequent code in the render paths of
 251             // NGNode know whether they ought to be paying attention to dirty region
 252             // culling bits.
 253             g.setHasPreCullingBits(true);
 254 
 255             // Find the render roots. There is a different render root for each dirty region
 256             if (PULSE_LOGGING_ENABLED) PulseLogger.newPhase("Render Roots Discovered");
 257             for (int i = 0; i < dirtyRegionSize; ++i) {
 258                 NodePath path = getRootPath(i);
 259                 path.clear();
 260                 root.getRenderRoot(getRootPath(i), dirtyRegionContainer.getDirtyRegion(i), i, tx, projTx);
 261             }



 262 
 263             // For debug purposes, write out to the pulse logger the number and size of the dirty
 264             // regions that are being used to render this pulse.
 265             if (PULSE_LOGGING_ENABLED) {
 266                 PulseLogger.addMessage(dirtyRegionSize + " different dirty regions to render");
 267                 for (int i=0; i<dirtyRegionSize; i++) {
 268                     PulseLogger.addMessage("Dirty Region " + i + ": " + dirtyRegionContainer.getDirtyRegion(i));
 269                     PulseLogger.addMessage("Render Root Path " + i + ": " + getRootPath(i));
 270                 }
 271             }
 272 
 273             // If -Dprism.printrendergraph=true then we want to print out the render graph to the
 274             // pulse logger, annotated with all the dirty opts. Invisible nodes are skipped.
 275             if (PULSE_LOGGING_ENABLED && PrismSettings.printRenderGraph) {
 276                 StringBuilder s = new StringBuilder();
 277                 List<NGNode> roots = new ArrayList<>();
 278                 for (int i = 0; i < dirtyRegionSize; i++) {
 279                     final RectBounds dirtyRegion = dirtyRegionContainer.getDirtyRegion(i);
 280                     // TODO it should be impossible to have ever created a dirty region that was empty...
 281                     if (dirtyRegion.getWidth() > 0 && dirtyRegion.getHeight() > 0) {
 282                         NodePath nodePath = getRootPath(i);
 283                         if (!nodePath.isEmpty()) {
 284                             roots.add(nodePath.last());
 285                         }
 286                     }
 287                 }
 288                 root.printDirtyOpts(s, roots);
 289                 PulseLogger.addMessage(s.toString());
 290             }
 291 
 292             // Paint each dirty region
 293             for (int i = 0; i < dirtyRegionSize; ++i) {
 294                 final RectBounds dirtyRegion = dirtyRegionContainer.getDirtyRegion(i);
 295                 // TODO it should be impossible to have ever created a dirty region that was empty...
 296                 // Make sure we are not trying to render in some invalid region
 297                 if (dirtyRegion.getWidth() > 0 && dirtyRegion.getHeight() > 0) {
 298                     // Set the clip rectangle using integer bounds since a fractional bounding box will
 299                     // still require a complete repaint on pixel boundaries
 300                     dirtyRect.setBounds(dirtyRegion);
 301                     // TODO I don't understand why this is needed. And if it is, are fractional pixelScale
 302                     // values OK? And if not, shouldn't pixelScale be an int instead?
 303                     if (pixelScale != 1.0f) {
 304                         dirtyRect.x *= pixelScale;
 305                         dirtyRect.y *= pixelScale;
 306                         dirtyRect.width *= pixelScale;
 307                         dirtyRect.height *= pixelScale;
 308                     }
 309                     g.setClipRect(dirtyRect);


 423 
 424         return sceneState.isWindowVisible() && !sceneState.isWindowMinimized();
 425     }
 426     
 427     protected float getPixelScaleFactor() {
 428         return presentable == null ? 0.1f : presentable.getPixelScaleFactor();
 429     }
 430 
 431     private void doPaint(Graphics g, NodePath renderRootPath) {
 432         // Null path indicates that occlusion culling is not used
 433         if (renderRootPath != null) {
 434             if (renderRootPath.isEmpty()) {
 435                 // empty render path indicates that no rendering is needed.
 436                 // There may be occluded dirty Nodes however, so we need to clear them
 437                 root.clearDirtyTree();
 438                 return;
 439             }
 440             // If the path is not empty, the first node must be the root node
 441             assert(renderRootPath.getCurrentNode() == root);
 442         }
 443         if (PULSE_LOGGING_ENABLED) PulseLogger.newPhase("Painting");
 444         try {
 445             GlassScene scene = sceneState.getScene();
 446             scene.clearEntireSceneDirty();
 447             g.setLights(scene.getLights());
 448             g.setDepthBuffer(scene.getDepthBuffer());
 449             Color clearColor = sceneState.getClearColor();
 450             if (clearColor != null) {
 451                 g.clear(clearColor);
 452             }
 453             Paint curPaint = sceneState.getCurrentPaint();
 454             if (curPaint != null) {
 455                 if (curPaint.getType() != com.sun.prism.paint.Paint.Type.COLOR) {
 456                     g.getRenderTarget().setOpaque(curPaint.isOpaque());
 457                 }
 458                 g.setPaint(curPaint);
 459                 g.fillQuad(0, 0, width, height);
 460             }
 461             g.setCamera(sceneState.getCamera());
 462             g.setRenderRoot(renderRootPath);
 463             root.render(g);
 464         } finally {



 465         }
 466     }
 467 }