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

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

see #15229 - fix warnings

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