source: josm/trunk/test/performance/org/openstreetmap/josm/gui/mappaint/MapRendererPerformanceTest.java@ 12649

Last change on this file since 12649 was 12649, checked in by Don-vip, 7 years ago

see #15182 - code refactoring to avoid dependence on GUI packages from Preferences

  • Property svn:eol-style set to native
File size: 12.9 KB
RevLine 
[9786]1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.mappaint;
3
4import java.awt.Color;
5import java.awt.Graphics2D;
6import java.awt.image.BufferedImage;
7import java.io.File;
8import java.io.IOException;
9import java.io.InputStream;
10import java.util.ArrayList;
11import java.util.Collections;
12import java.util.EnumMap;
[10697]13import java.util.HashMap;
[9786]14import java.util.List;
[10222]15import java.util.Locale;
[9786]16import java.util.Map;
[10697]17import java.util.stream.Collectors;
[9786]18
19import javax.imageio.ImageIO;
20
21import org.junit.AfterClass;
22import org.junit.Assert;
23import org.junit.BeforeClass;
24import org.junit.Rule;
25import org.junit.Test;
26import org.junit.rules.Timeout;
27import org.openstreetmap.josm.JOSMFixture;
28import org.openstreetmap.josm.PerformanceTestUtils;
29import org.openstreetmap.josm.TestUtils;
30import org.openstreetmap.josm.data.Bounds;
31import org.openstreetmap.josm.data.coor.LatLon;
32import org.openstreetmap.josm.data.osm.DataSet;
[10697]33import org.openstreetmap.josm.data.osm.visitor.paint.RenderBenchmarkCollector.CapturingBenchmark;
[9786]34import org.openstreetmap.josm.data.osm.visitor.paint.StyledMapRenderer;
[10697]35import org.openstreetmap.josm.data.osm.visitor.paint.StyledMapRenderer.StyleRecord;
[12649]36import org.openstreetmap.josm.data.preferences.sources.SourceEntry;
[9786]37import org.openstreetmap.josm.data.projection.Projections;
[12630]38import org.openstreetmap.josm.gui.MainApplication;
[9786]39import org.openstreetmap.josm.gui.NavigatableComponent;
40import org.openstreetmap.josm.gui.mappaint.StyleSetting.BooleanStyleSetting;
41import org.openstreetmap.josm.gui.mappaint.mapcss.MapCSSStyleSource;
42import org.openstreetmap.josm.gui.mappaint.mapcss.Selector;
43import org.openstreetmap.josm.gui.mappaint.styleelement.StyleElement;
44import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
45import org.openstreetmap.josm.io.Compression;
46import org.openstreetmap.josm.io.OsmReader;
[12620]47import org.openstreetmap.josm.tools.Logging;
[9786]48
[10222]49import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
50
[9786]51public class MapRendererPerformanceTest {
52
53 private static final boolean DUMP_IMAGE = false; // dump images to file for debugging purpose
54
55 private static final int IMG_WIDTH = 2048;
56 private static final int IMG_HEIGHT = 1536;
57
58 private static Graphics2D g;
59 private static BufferedImage img;
60 private static NavigatableComponent nc;
61 private static DataSet dsCity;
62 private static final Bounds BOUNDS_CITY_ALL = new Bounds(53.4382, 13.1094, 53.6153, 13.4074, false);
63 private static final LatLon LL_CITY = new LatLon(53.5574458, 13.2602781);
64 private static final double SCALE_Z17 = 1.5;
65
66 private static int defaultStyleIdx;
67 private static BooleanStyleSetting hideIconsSetting;
68
69 private static int filterStyleIdx;
70 private static StyleSource filterStyle;
71
72 private enum Feature {
73 ICON, SYMBOL, NODE_TEXT, LINE, LINE_TEXT, AREA;
74 public String label() {
[10222]75 return name().toLowerCase(Locale.ENGLISH);
[9786]76 }
77 }
[9793]78
[9786]79 private static final EnumMap<Feature, BooleanStyleSetting> filters = new EnumMap<>(Feature.class);
80
81 /**
82 * Global timeout applied to all test methods.
83 */
84 @Rule
[10222]85 @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
[9786]86 public Timeout globalTimeout = Timeout.seconds(15*60);
87
88 @BeforeClass
89 public static void load() throws Exception {
90 JOSMFixture.createPerformanceTestFixture().init(true);
91
92 img = new BufferedImage(IMG_WIDTH, IMG_HEIGHT, BufferedImage.TYPE_INT_ARGB);
93 g = (Graphics2D) img.getGraphics();
94 g.setClip(0, 0, IMG_WIDTH, IMG_WIDTH);
95 g.setColor(Color.BLACK);
96 g.fillRect(0, 0, IMG_WIDTH, IMG_WIDTH);
[12630]97 nc = MainApplication.getMap().mapView;
[9786]98 nc.setBounds(0, 0, IMG_WIDTH, IMG_HEIGHT);
99
100 MapPaintStyles.readFromPreferences();
101
102 SourceEntry se = new MapCSSStyleSource(TestUtils.getTestDataRoot() + "styles/filter.mapcss", "filter", "");
103 filterStyle = MapPaintStyles.addStyle(se);
104 List<StyleSource> sources = MapPaintStyles.getStyles().getStyleSources();
105 filterStyleIdx = sources.indexOf(filterStyle);
106 Assert.assertEquals(2, filterStyleIdx);
107
108 Assert.assertEquals(Feature.values().length, filterStyle.settings.size());
109 for (StyleSetting set : filterStyle.settings) {
110 BooleanStyleSetting bset = (BooleanStyleSetting) set;
111 String prefKey = bset.prefKey;
112 boolean found = false;
113 for (Feature f : Feature.values()) {
114 if (prefKey.endsWith(":" + f.label() + "_off")) {
115 filters.put(f, bset);
116 found = true;
117 break;
118 }
119 }
120 Assert.assertTrue(prefKey, found);
121 }
122
123 MapCSSStyleSource defaultStyle = null;
[9793]124 for (int i = 0; i < sources.size(); i++) {
[9786]125 StyleSource s = sources.get(i);
126 if ("resource://styles/standard/elemstyles.mapcss".equals(s.url)) {
127 defaultStyle = (MapCSSStyleSource) s;
128 defaultStyleIdx = i;
129 break;
130 }
131 }
132 Assert.assertNotNull(defaultStyle);
133
134 for (StyleSetting set : defaultStyle.settings) {
135 if (set instanceof BooleanStyleSetting) {
136 BooleanStyleSetting bset = (BooleanStyleSetting) set;
137 if (bset.prefKey.endsWith(":hide_icons")) {
138 hideIconsSetting = bset;
139 }
140 }
141 }
142 Assert.assertNotNull(hideIconsSetting);
143 hideIconsSetting.setValue(false);
144 MapPaintStyles.reloadStyles(defaultStyleIdx);
145
146 try (
147 InputStream fisC = Compression.getUncompressedFileInputStream(new File("data_nodist/neubrandenburg.osm.bz2"));
148 ) {
149 dsCity = OsmReader.parseDataSet(fisC, NullProgressMonitor.INSTANCE);
150 }
151 }
152
153 @AfterClass
154 public static void cleanUp() {
155 setFilterStyleActive(false);
156 if (hideIconsSetting != null) {
157 hideIconsSetting.setValue(true);
158 }
159 MapPaintStyles.reloadStyles(defaultStyleIdx);
160 }
161
162 private static class PerformanceTester {
163 public double scale = 0;
164 public LatLon center = LL_CITY;
165 public Bounds bounds;
166 public int noWarmup = 3;
167 public int noIterations = 7;
168 public boolean dumpImage = DUMP_IMAGE;
169 public boolean clearStyleCache = true;
170 public String label = "";
171 public boolean mpGenerate = false;
172 public boolean mpSort = false;
173 public boolean mpDraw = false;
174 public boolean mpTotal = false;
175
176 private final List<Long> generateTimes = new ArrayList<>();
177 private final List<Long> sortTimes = new ArrayList<>();
178 private final List<Long> drawTimes = new ArrayList<>();
179 private final List<Long> totalTimes = new ArrayList<>();
180
[10222]181 @SuppressFBWarnings(value = "DM_GC")
[9786]182 public void run() throws IOException {
183 boolean checkScale = false;
184 if (scale == 0) {
185 checkScale = true;
186 scale = SCALE_Z17;
187 }
188 nc.zoomTo(Projections.project(center), scale);
189 if (checkScale) {
190 int lvl = Selector.OptimizedGeneralSelector.scale2level(nc.getDist100Pixel());
191 Assert.assertEquals(17, lvl);
192 }
193
194 if (bounds == null) {
195 bounds = nc.getLatLonBounds(g.getClipBounds());
196 }
197
198 StyledMapRenderer renderer = new StyledMapRenderer(g, nc, false);
199
200 int noTotal = noWarmup + noIterations;
201 for (int i = 1; i <= noTotal; i++) {
202 g.setColor(Color.BLACK);
203 g.fillRect(0, 0, IMG_WIDTH, IMG_WIDTH);
204 if (clearStyleCache) {
205 MapPaintStyles.getStyles().clearCached();
206 }
207 System.gc();
208 System.runFinalization();
209 try {
210 Thread.sleep(300);
[9793]211 } catch (InterruptedException ex) {
[12620]212 Logging.warn(ex);
[9793]213 }
[10697]214 BenchmarkData data = new BenchmarkData();
215 renderer.setBenchmarkFactory(() -> data);
[9786]216 renderer.render(dsCity, false, bounds);
217
218 if (i > noWarmup) {
[10697]219 generateTimes.add(data.getGenerateTime());
220 sortTimes.add(data.getSortTime());
221 drawTimes.add(data.getDrawTime());
222 totalTimes.add(data.getGenerateTime() + data.getSortTime() + data.getDrawTime());
[9786]223 }
[9788]224 if (i == 1) {
225 dumpElementCount(data);
226 }
227 dumpTimes(data);
[9786]228 if (dumpImage && i == noTotal) {
229 dumpRenderedImage(label);
230 }
231 }
232
233 if (mpGenerate) {
234 processTimes(generateTimes, "generate");
235 }
236 if (mpSort) {
237 processTimes(sortTimes, "sort");
238 }
239 if (mpDraw) {
240 processTimes(drawTimes, "draw");
241 }
242 if (mpTotal) {
243 processTimes(totalTimes, "total");
244 }
245 }
246
247 private void processTimes(List<Long> times, String sublabel) {
248 Collections.sort(times);
249 // Take median instead of average. This should give a more stable
250 // result and avoids distortions by outliers.
251 long medianTime = times.get(times.size() / 2);
252 PerformanceTestUtils.measurementPlotsPluginOutput(label + " " + sublabel + " (ms)", medianTime);
253 }
254 }
255
256 /**
257 * Test phase 1, the calculation of {@link StyleElement}s.
258 * @throws IOException in case of an I/O error
259 */
260 @Test
261 public void testPerformanceGenerate() throws IOException {
262 setFilterStyleActive(false);
263 PerformanceTester test = new PerformanceTester();
264 test.bounds = BOUNDS_CITY_ALL;
265 test.label = "big";
266 test.dumpImage = false;
267 test.noWarmup = 3;
268 test.noIterations = 10;
269 test.mpGenerate = true;
270 test.clearStyleCache = true;
271 test.run();
272 }
273
274 private static void testDrawFeature(Feature feature) throws IOException {
275 PerformanceTester test = new PerformanceTester();
276 test.noWarmup = 3;
277 test.noIterations = 10;
278 test.mpDraw = true;
279 test.clearStyleCache = false;
280 if (feature != null) {
281 BooleanStyleSetting filterSetting = filters.get(feature);
282 test.label = filterSetting.label;
283 setFilterStyleActive(true);
284 for (Feature f : Feature.values()) {
285 filters.get(f).setValue(true);
286 }
287 filterSetting.setValue(false);
288 } else {
289 test.label = "all";
290 setFilterStyleActive(false);
291 }
292 MapPaintStyles.reloadStyles(filterStyleIdx);
293 test.run();
294 }
295
296 /**
297 * Test phase 2, the actual drawing.
298 * Several runs: Icons, lines, etc. are tested separately (+ one run with
299 * all features activated)
300 * @throws IOException in case of an I/O error
301 */
302 @Test
303 public void testPerformanceDrawFeatures() throws IOException {
304 testDrawFeature(null);
305 for (Feature f : Feature.values()) {
306 testDrawFeature(f);
307 }
308 }
309
310 private static void setFilterStyleActive(boolean active) {
311 if (filterStyle.active != active) {
312 MapPaintStyles.toggleStyleActive(filterStyleIdx);
313 }
314 Assert.assertEquals(active, filterStyle.active);
315 }
316
317 private static void dumpRenderedImage(String id) throws IOException {
318 File outputfile = new File("test-neubrandenburg-"+id+".png");
319 ImageIO.write(img, "png", outputfile);
320 }
321
[10697]322 public static void dumpTimes(BenchmarkData bd) {
323 System.out.print(String.format("gen. %3d, sort %3d, draw %3d%n", bd.getGenerateTime(), bd.getSortTime(), bd.getDrawTime()));
[9788]324 }
325
[10697]326 public static void dumpElementCount(BenchmarkData bd) {
327 System.out.println(bd.recordElementStats().entrySet().stream()
328 .map(e -> e.getKey().getSimpleName().replace("Element", "") + ":" + e.getValue()).collect(Collectors.joining(" ")));
329 }
330
331 public static class BenchmarkData extends CapturingBenchmark {
332
333 private List<StyleRecord> allStyleElems;
334
335 @Override
336 public boolean renderDraw(List<StyleRecord> allStyleElems) {
337 this.allStyleElems = allStyleElems;
338 return super.renderDraw(allStyleElems);
339 }
340
341 private Map<Class<? extends StyleElement>, Integer> recordElementStats() {
342 Map<Class<? extends StyleElement>, Integer> styleElementCount = new HashMap<>();
343 for (StyleRecord r : allStyleElems) {
344 Class<? extends StyleElement> klass = r.getStyle().getClass();
345 Integer count = styleElementCount.get(klass);
346 if (count == null) {
347 count = 0;
348 }
349 styleElementCount.put(klass, count + 1);
[9788]350 }
[10697]351 return styleElementCount;
[9786]352 }
353 }
354}
Note: See TracBrowser for help on using the repository browser.