You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by de...@apache.org on 2021/01/22 23:43:05 UTC
[sis] 03/04: Enable parallel execution of isoline computation in
JavaFX viewer.
This is an automated email from the ASF dual-hosted git repository.
desruisseaux pushed a commit to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git
commit cfd13790296f4501a6c5e1f27ef17112cd965e24
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Sat Jan 23 00:34:47 2021 +0100
Enable parallel execution of isoline computation in JavaFX viewer.
---
.../apache/sis/gui/coverage/CoverageCanvas.java | 15 ++++---
.../apache/sis/gui/coverage/IsolineRenderer.java | 51 ++++++++++++++++++----
.../org/apache/sis/gui/coverage/RenderingData.java | 11 +++--
3 files changed, 59 insertions(+), 18 deletions(-)
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/CoverageCanvas.java b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/CoverageCanvas.java
index c2ae8f6..949c2ae 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/CoverageCanvas.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/CoverageCanvas.java
@@ -20,6 +20,7 @@ import java.util.Map;
import java.util.EnumMap;
import java.util.List;
import java.util.Locale;
+import java.util.concurrent.Future;
import java.util.function.Function;
import java.io.IOException;
import java.awt.Graphics2D;
@@ -67,6 +68,7 @@ import org.apache.sis.gui.map.MapCanvas;
import org.apache.sis.gui.map.MapCanvasAWT;
import org.apache.sis.gui.map.StatusBar;
import org.apache.sis.portrayal.RenderException;
+import org.apache.sis.internal.processing.image.Isolines;
import org.apache.sis.internal.gui.BackgroundThreads;
import org.apache.sis.internal.gui.GUIUtilities;
import org.apache.sis.internal.gui.LogHandler;
@@ -657,7 +659,7 @@ public class CoverageCanvas extends MapCanvasAWT {
*/
@Override
@SuppressWarnings("PointlessBitwiseExpression")
- protected void render() throws TransformException {
+ protected void render() throws Exception {
final Long id = LogHandler.loadingStart(originator);
try {
/*
@@ -697,12 +699,15 @@ public class CoverageCanvas extends MapCanvasAWT {
trace("render(): resampling result:%n\t%s", resampledImage);
}
}
- prefetchedImage = data.prefetch(resampledImage, resampledToDisplay, displayBounds);
/*
- * Create isolines if requested.
+ * Launch isolines creation if requested. We do this operation before `prefetch(…)`
+ * because it will be executed in background threads while we process the coverage.
+ * We can not invoke it sooner because it needs some `resampleAndConvert(…)` results.
*/
- if (isolines != null) {
- data.complete(isolines);
+ final Future<Isolines[]> newIsolines = data.generate(isolines);
+ prefetchedImage = data.prefetch(resampledImage, resampledToDisplay, displayBounds);
+ if (newIsolines != null) {
+ IsolineRenderer.complete(isolines, newIsolines);
}
} finally {
LogHandler.loadingStop(id);
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/IsolineRenderer.java b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/IsolineRenderer.java
index 12116c6..ebb3e83 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/IsolineRenderer.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/IsolineRenderer.java
@@ -22,6 +22,8 @@ import java.util.List;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Arrays;
+import java.util.concurrent.Future;
+import java.util.concurrent.ExecutionException;
import java.awt.Shape;
import java.awt.Color;
import java.awt.Graphics2D;
@@ -265,11 +267,11 @@ final class IsolineRenderer {
}
/**
- * Prepares a list of isolines to draw for all bands, initially populated with shapes that are already available.
+ * Prepares a list of isolines to draw for each bands, initially populated with shapes that are already available.
* This method shall be invoked in JavaFX thread for having consistent information. The snapshots returned by this
* method will be completed and used in a background thread.
*
- * @return snapshots of information about isolines in all bands, or {@code null} if none.
+ * @return snapshots of information about isolines in each bands, or {@code null} if none.
*/
final Snapshot[] prepare() {
assert Platform.isFxApplicationThread();
@@ -287,17 +289,18 @@ final class IsolineRenderer {
/**
* Continues isoline preparation by computing the missing Java2D shapes.
* This method shall be invoked in a background thread. After this call,
+ * {@link #complete(Snapshot[], Future)} needs to be invoked before
* isolines can be painted with {@link Snapshot#paint(Graphics2D, Rectangle2D)}.
*
* @param snapshots value of {@link #prepare()}. Shall not be {@code null}.
* @param data the source of data. Used only if there is new isolines to compute.
* @param gridToCRS transform from pixel coordinates to geometry coordinates, or {@code null} if none.
* Integer source coordinates are located at pixel centers.
- * @return the {@code snapshots} array, potentially with less elements.
+ * @return result of isolines generation, or {@code null} if there is no isoline to compute.
* @throws TransformException if an interpolated point can not be transformed using the given transform.
*/
@SuppressWarnings("UseOfSystemOutOrSystemErr") // Used only for debugging.
- static Snapshot[] complete(final Snapshot[] snapshots, final RenderedImage data, final MathTransform gridToCRS)
+ static Future<Isolines[]> generate(final Snapshot[] snapshots, final RenderedImage data, final MathTransform gridToCRS)
throws TransformException
{
assert !Platform.isFxApplicationThread();
@@ -331,12 +334,31 @@ final class IsolineRenderer {
System.out.printf("\tFor band %d: %s%n", i, Arrays.toString(levels[i]));
}
}
- final Isolines[] isolines = Isolines.generate(data, levels, gridToCRS);
- for (int i=0; i<numViews; i++) {
- snapshots[i].complete(isolines[i]);
- }
+ return Isolines.parallelGenerate(data, levels, gridToCRS);
+ }
+ return null;
+ }
+
+ /**
+ * Waits for completion of isolines generation if not already finished, then stores the result.
+ * The {@code isolines} argument is the {@link #generate(Snapshot[], RenderedImage, MathTransform)}
+ * return value. Caller shall verify that {@code snapshots} is non-null.
+ *
+ * @param snapshots where to store the result of isoline computations.
+ * @param newIsolines the result of isolines generation, or {@code null} if none.
+ */
+ static void complete(final Snapshot[] snapshots, final Future<Isolines[]> newIsolines)
+ throws ExecutionException, InterruptedException
+ {
+ final Isolines[] isolines = newIsolines.get();
+ final int n = Math.min(snapshots.length, isolines.length);
+ int i;
+ for (i=0; i<n; i++) {
+ snapshots[i].complete(isolines[i]);
+ }
+ while (i < snapshots.length) { // Clear remaining snapshots if any.
+ snapshots[i++].clear();
}
- return ArraysExt.resize(snapshots, numViews);
}
/**
@@ -403,6 +425,17 @@ final class IsolineRenderer {
}
/**
+ * Removes all isolines.
+ */
+ private void clear() {
+ isolines.clear();
+ missingLevels.clear();
+ newIsolines = null;
+ Arrays.fill(shapes, null);
+ count = 0;
+ }
+
+ /**
* Adds an isoline level. This method shall be invoked in JavaFX thread.
*
* @param value the level value.
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/RenderingData.java b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/RenderingData.java
index 7dfb293..6e08f03 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/RenderingData.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/RenderingData.java
@@ -19,6 +19,7 @@ package org.apache.sis.gui.coverage;
import java.util.Map;
import java.util.HashMap;
import java.util.List;
+import java.util.concurrent.Future;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.awt.Graphics2D;
@@ -48,6 +49,7 @@ import org.apache.sis.image.ImageProcessor;
import org.apache.sis.internal.coverage.j2d.ColorModelType;
import org.apache.sis.internal.coverage.j2d.ImageUtilities;
import org.apache.sis.internal.referencing.WraparoundApplicator;
+import org.apache.sis.internal.processing.image.Isolines;
import org.apache.sis.internal.system.Modules;
import org.apache.sis.io.TableAppender;
import org.apache.sis.math.Statistics;
@@ -560,14 +562,15 @@ final class RenderingData implements Cloneable {
* This method shall be invoked in a background thread after image rendering has been completed (because this
* method uses some image computation results).
*
- * @param isolines value of {@link IsolineRenderer#prepare()}. Shall not be {@code null}.
- * @return the {@code isolines} array, potentially with less elements.
+ * @param isolines value of {@link IsolineRenderer#prepare()}, or {@code null} if none.
+ * @return result of isolines generation, or {@code null} if there is no isoline to compute.
* @throws TransformException if an interpolated point can not be transformed using the given transform.
*/
- final IsolineRenderer.Snapshot[] complete(final IsolineRenderer.Snapshot[] isolines) throws TransformException {
+ final Future<Isolines[]> generate(final IsolineRenderer.Snapshot[] isolines) throws TransformException {
+ if (isolines == null) return null;
final MathTransform centerToObjective = PixelTranslation.translate(
cornerToObjective, PixelInCell.CELL_CORNER, PixelInCell.CELL_CENTER);
- return IsolineRenderer.complete(isolines, data, centerToObjective);
+ return IsolineRenderer.generate(isolines, data, centerToObjective);
}
/**