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 }
|