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

Last change on this file since 15987 was 15987, checked in by simon04, 4 years ago

see #18802 - Selector: merge GeneralSelector/OptimizedGeneralSelector as there is no difference

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