Index: src/org/openstreetmap/josm/data/osm/visitor/paint/RenderBenchmarkCollector.java
===================================================================
--- src/org/openstreetmap/josm/data/osm/visitor/paint/RenderBenchmarkCollector.java	(revision 15429)
+++ src/org/openstreetmap/josm/data/osm/visitor/paint/RenderBenchmarkCollector.java	(working copy)
@@ -18,6 +18,27 @@
  * @since 10697
  */
 public class RenderBenchmarkCollector {
+
+    protected String name = "";
+
+    /**
+     * Returns this collector name.
+     * @return this collector name
+     * @since xxx
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * Sets this collector name.
+     * @param name this collector name
+     * @since xxx
+     */
+    public void setName(String name) {
+        this.name = name;
+    }
+
     /**
      * Notified when the renderer method starts preparing the data
      * @param circum The current circum of the view.
@@ -115,7 +136,7 @@
      * A special version of the benchmark class that logs the output to stderr.
      * @author Michael Zangl
      */
-    public static class LoggingBenchmark extends RenderBenchmarkCollector.CapturingBenchmark {
+    public static class LoggingBenchmark extends CapturingBenchmark {
         private final PrintStream outStream = System.err;
         private double circum;
 
@@ -123,7 +144,7 @@
         public void renderStart(double circum) {
             this.circum = circum;
             super.renderStart(circum);
-            outStream.print("BENCHMARK: rendering ");
+            outStream.print("BENCHMARK " + name + ": rendering ");
         }
 
         @Override
Index: src/org/openstreetmap/josm/data/osm/visitor/paint/StyledMapRenderer.java
===================================================================
--- src/org/openstreetmap/josm/data/osm/visitor/paint/StyledMapRenderer.java	(revision 15429)
+++ src/org/openstreetmap/josm/data/osm/visitor/paint/StyledMapRenderer.java	(working copy)
@@ -1177,8 +1177,6 @@
                                 (p, gv) -> p.append(gv.getOutline(0, 0), false),
                                 (p1, p2) -> p1.append(p2, false)),
                         osm.isDisabled(), text);
-            } else {
-                Logging.trace("Couldn't find a correct label placement for {0} / {1}", osm, name);
             }
         });
         g.setFont(defaultFont);
@@ -1635,6 +1633,7 @@
     @Override
     public void render(final OsmData<?, ?, ?, ?> data, boolean renderVirtualNodes, Bounds bounds) {
         RenderBenchmarkCollector benchmark = benchmarkFactory.get();
+        benchmark.setName(data.getName());
         BBox bbox = bounds.toBBox();
         getSettings(renderVirtualNodes);
 
Index: src/org/openstreetmap/josm/gui/draw/CompositeStroke.java
===================================================================
--- src/org/openstreetmap/josm/gui/draw/CompositeStroke.java	(nonexistent)
+++ src/org/openstreetmap/josm/gui/draw/CompositeStroke.java	(working copy)
@@ -0,0 +1,30 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.draw;
+
+import java.awt.Shape;
+import java.awt.Stroke;
+import java.util.Objects;
+
+/**
+ * A composite stroke draws the outline of the stroke using another stroke.
+ * @since xxx
+ */
+public class CompositeStroke implements Stroke {
+    private final Stroke stroke1;
+    private final Stroke stroke2;
+
+    /**
+     * Constructs a new {@code CompositeStroke}.
+     * @param stroke1 stroke that strokes the shape to render
+     * @param stroke2 stroke that strokes the shape resulting of the first stroke
+     */
+    public CompositeStroke(Stroke stroke1, Stroke stroke2) {
+        this.stroke1 = Objects.requireNonNull(stroke1);
+        this.stroke2 = Objects.requireNonNull(stroke2);
+    }
+
+    @Override
+    public Shape createStrokedShape(Shape shape) {
+        return stroke2.createStrokedShape(stroke1.createStrokedShape(shape));
+    }
+}

Property changes on: src\org\openstreetmap\josm\gui\draw\CompositeStroke.java
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
Index: src/org/openstreetmap/josm/gui/layer/ValidatorLayer.java
===================================================================
--- src/org/openstreetmap/josm/gui/layer/ValidatorLayer.java	(revision 15429)
+++ src/org/openstreetmap/josm/gui/layer/ValidatorLayer.java	(working copy)
@@ -15,7 +15,9 @@
 import org.openstreetmap.josm.actions.RenameLayerAction;
 import org.openstreetmap.josm.actions.SaveActionBase;
 import org.openstreetmap.josm.data.Bounds;
+import org.openstreetmap.josm.data.osm.DataSet;
 import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
+import org.openstreetmap.josm.data.osm.visitor.paint.RenderBenchmarkCollector;
 import org.openstreetmap.josm.data.validation.OsmValidator;
 import org.openstreetmap.josm.data.validation.Severity;
 import org.openstreetmap.josm.data.validation.TestError;
@@ -31,6 +33,7 @@
 import org.openstreetmap.josm.gui.layer.LayerManager.LayerRemoveEvent;
 import org.openstreetmap.josm.gui.layer.validation.PaintVisitor;
 import org.openstreetmap.josm.tools.ImageProvider;
+import org.openstreetmap.josm.tools.Logging;
 import org.openstreetmap.josm.tools.MultiMap;
 
 /**
@@ -69,13 +72,21 @@
     @Override
     public void paint(final Graphics2D g, final MapView mv, Bounds bounds) {
         DefaultMutableTreeNode root = MainApplication.getMap().validatorDialog.tree.getRoot();
-        if (root == null || root.getChildCount() == 0)
+        DataSet data = MainApplication.getLayerManager().getActiveDataSet();
+        if (root == null || root.getChildCount() == 0 || data == null)
             return;
 
-        PaintVisitor paintVisitor = new PaintVisitor(g, mv);
+        RenderBenchmarkCollector benchmark = RenderBenchmarkCollector.defaultBenchmarkSupplier().get();
+        benchmark.setName(getName());
+        benchmark.renderStart(mv.getDist100Pixel());
+        benchmark.renderSort();
 
+        PaintVisitor paintVisitor = new PaintVisitor(g, mv, data, bounds);
+
+        benchmark.renderDraw(null);
         DefaultMutableTreeNode severity = (DefaultMutableTreeNode) root.getLastChild();
         while (severity != null) {
+            Logging.debug("Paiting " + severity);
             ValidatorTreePanel.visitTestErrors(severity, paintVisitor::visit);
 
             // Severities in inverse order
@@ -83,6 +94,7 @@
         }
 
         paintVisitor.clearPaintedObjects();
+        benchmark.renderDone();
     }
 
     @Override
Index: src/org/openstreetmap/josm/gui/layer/validation/PaintVisitor.java
===================================================================
--- src/org/openstreetmap/josm/gui/layer/validation/PaintVisitor.java	(revision 15429)
+++ src/org/openstreetmap/josm/gui/layer/validation/PaintVisitor.java	(working copy)
@@ -1,15 +1,23 @@
 // License: GPL. For details, see LICENSE file.
 package org.openstreetmap.josm.gui.layer.validation;
 
+import java.awt.BasicStroke;
 import java.awt.Color;
 import java.awt.Graphics2D;
-import java.awt.Point;
+import java.awt.Shape;
+import java.awt.Stroke;
+import java.awt.geom.Point2D;
+import java.util.Arrays;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
+import java.util.stream.Collectors;
 
-import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.data.Bounds;
+import org.openstreetmap.josm.data.osm.DataSet;
 import org.openstreetmap.josm.data.osm.Node;
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
 import org.openstreetmap.josm.data.osm.Relation;
@@ -19,8 +27,10 @@
 import org.openstreetmap.josm.data.validation.TestError;
 import org.openstreetmap.josm.data.validation.ValidatorVisitor;
 import org.openstreetmap.josm.gui.MapView;
+import org.openstreetmap.josm.gui.draw.CompositeStroke;
 import org.openstreetmap.josm.gui.draw.MapViewPath;
 import org.openstreetmap.josm.gui.draw.SymbolShape;
+import org.openstreetmap.josm.tools.Logging;
 import org.openstreetmap.josm.tools.Utils;
 
 /**
@@ -42,64 +52,113 @@
 
     private final Set<PaintedPoint> paintedPoints = new HashSet<>();
     private final Set<PaintedSegment> paintedSegments = new HashSet<>();
+    private final Set<PaintedWay> paintedWays = new HashSet<>();
+
+    private final Set<Long> nodes;
+    private long drawSegmentTimeSpent;
+    private long drawArcTimeSpent;
+    private long drawLineTimeSpent;
+
+    private static final Map<Color, Color> highlightColors = new HashMap<>();
+
+    private static final Stroke WAY_STROKE = new CompositeStroke(
+            new BasicStroke(10f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND),
+            new BasicStroke(1f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
 
     /**
      * Constructor
      * @param g The graphics
      * @param mv The Mapview
+     * @param data dataset to render
+     * @param bounds bounds to render
      */
-    public PaintVisitor(Graphics2D g, MapView mv) {
+    public PaintVisitor(Graphics2D g, MapView mv, DataSet data, Bounds bounds) {
         this.g = g;
         this.mv = mv;
+        this.nodes = data.searchNodes(bounds.toBBox()).parallelStream().map(Node::getUniqueId).collect(Collectors.toSet());
     }
 
     protected static class PaintedPoint {
-        protected final LatLon p1;
+        protected final double lat1;
+        protected final double lon1;
         protected final Color color;
 
-        public PaintedPoint(LatLon p1, Color color) {
-            this.p1 = p1;
+        public PaintedPoint(double lat1, double lon1, Color color) {
+            this.lat1 = lat1;
+            this.lon1 = lon1;
             this.color = color;
         }
 
         @Override
         public int hashCode() {
-            return Objects.hash(p1, color);
+            return Objects.hash(color, lat1, lon1);
         }
 
         @Override
         public boolean equals(Object obj) {
-            if (this == obj) return true;
-            if (obj == null || getClass() != obj.getClass()) return false;
-            PaintedPoint that = (PaintedPoint) obj;
-            return Objects.equals(p1, that.p1) &&
-                    Objects.equals(color, that.color);
+            if (this == obj)
+                return true;
+            if (obj == null || getClass() != obj.getClass())
+                return false;
+            PaintedPoint other = (PaintedPoint) obj;
+            return Objects.equals(color, other.color)
+                    && Double.doubleToLongBits(lat1) == Double.doubleToLongBits(other.lat1)
+                    && Double.doubleToLongBits(lon1) == Double.doubleToLongBits(other.lon1);
         }
     }
 
     protected static class PaintedSegment extends PaintedPoint {
-        private final LatLon p2;
+        private final double lat2;
+        private final double lon2;
 
-        public PaintedSegment(LatLon p1, LatLon p2, Color color) {
-            super(p1, color);
-            this.p2 = p2;
+        public PaintedSegment(double lat1, double lon1, double lat2, double lon2, Color color) {
+            super(lat1, lon1, color);
+            this.lat2 = lat2;
+            this.lon2 = lon2;
         }
 
         @Override
         public int hashCode() {
-            return Objects.hash(super.hashCode(), p2);
+            return Objects.hash(color, lat1, lon1, lat2, lon2);
         }
 
         @Override
         public boolean equals(Object obj) {
-            if (this == obj) return true;
-            if (obj == null || getClass() != obj.getClass()) return false;
-            if (!super.equals(obj)) return false;
-            PaintedSegment that = (PaintedSegment) obj;
-            return Objects.equals(p2, that.p2);
+            if (this == obj)
+                return true;
+            if (obj == null || getClass() != obj.getClass() || !super.equals(obj))
+                return false;
+            PaintedSegment other = (PaintedSegment) obj;
+            return Double.doubleToLongBits(lat2) == Double.doubleToLongBits(other.lat2)
+                && Double.doubleToLongBits(lon2) == Double.doubleToLongBits(other.lon2);
         }
     }
 
+    protected static class PaintedWay {
+        private final Way w;
+        private final Color color;
+
+        public PaintedWay(Way w, Color color) {
+            this.w = w;
+            this.color = color;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(color, w);
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj)
+                return true;
+            if (obj == null || getClass() != obj.getClass())
+                return false;
+            PaintedWay other = (PaintedWay) obj;
+            return Objects.equals(color, other.color) && Objects.equals(w, other.w);
+        }
+    }
+
     @Override
     public void visit(TestError error) {
         if (error != null && !error.isIgnored()) {
@@ -122,9 +181,8 @@
      * @param color The circle color
      */
     protected void drawNode(Node n, Color color) {
-        PaintedPoint pp = new PaintedPoint(n.getCoor(), color);
-
-        if (!paintedPoints.contains(pp)) {
+        PaintedPoint pp = new PaintedPoint(n.lat(), n.lon(), color);
+        if (paintedPoints.add(pp)) {
             MapViewPath circle = new MapViewPath(mv.getState()).shapeAround(n, SymbolShape.CIRCLE, 10);
 
             if (selected) {
@@ -133,10 +191,22 @@
             }
             g.setColor(color);
             g.draw(circle);
-            paintedPoints.add(pp);
         }
     }
-
+/*
+    protected void drawWay(Way w, Color color) {
+        PaintedWay pw = new PaintedWay(w, color);
+        if (paintedWays.add(pw)) {
+            MapViewPath path = new MapViewPath(mv.getState());
+            if (w.isClosed()) {
+                path.appendClosed(w.getNodes(), false);
+            } else {
+                path.append(w.getNodes(), false);
+            }
+            g.draw(path.computeClippedLine(WAY_STROKE));
+        }
+    }
+*/
     /**
      * Draws a line around the segment
      *
@@ -144,29 +214,40 @@
      * @param p2 The second point of segment
      * @param color The color
      */
-    protected void drawSegment(Point p1, Point p2, Color color) {
+    protected void drawSegment(Point2D p1, Point2D p2, Color color) {
+        long timeStart = System.currentTimeMillis();
 
-        double t = Math.atan2((double) p2.x - p1.x, (double) p2.y - p1.y);
+        double t = Math.atan2(p2.getX() - p1.getX(), p2.getY() - p1.getY());
         double cosT = 5 * Math.cos(t);
         double sinT = 5 * Math.sin(t);
         int deg = (int) Utils.toDegrees(t);
+        int[] x = new int[] {(int) (p1.getX() + cosT), (int) (p2.getX() + cosT),
+                             (int) (p2.getX() - cosT), (int) (p1.getX() - cosT)};
+        int[] y = new int[] {(int) (p1.getY() - sinT), (int) (p2.getY() - sinT),
+                             (int) (p2.getY() + sinT), (int) (p1.getY() + sinT)};
         if (selected) {
             g.setColor(getHighlightColor(color));
-            int[] x = new int[] {(int) (p1.x + cosT), (int) (p2.x + cosT),
-                                 (int) (p2.x - cosT), (int) (p1.x - cosT)};
-            int[] y = new int[] {(int) (p1.y - sinT), (int) (p2.y - sinT),
-                                 (int) (p2.y + sinT), (int) (p1.y + sinT)};
             g.fillPolygon(x, y, 4);
-            g.fillArc(p1.x - 5, p1.y - 5, 10, 10, deg, 180);
-            g.fillArc(p2.x - 5, p2.y - 5, 10, 10, deg, -180);
+            g.fillArc((int) p1.getX() - 5, (int) p1.getY() - 5, 10, 10, deg, 180);
+            g.fillArc((int) p2.getX() - 5, (int) p2.getY() - 5, 10, 10, deg, -180);
         }
         g.setColor(color);
-        g.drawLine((int) (p1.x + cosT), (int) (p1.y - sinT),
-                (int) (p2.x + cosT), (int) (p2.y - sinT));
-        g.drawLine((int) (p1.x - cosT), (int) (p1.y + sinT),
-                (int) (p2.x - cosT), (int) (p2.y + sinT));
-        g.drawArc(p1.x - 5, p1.y - 5, 10, 10, deg, 180);
-        g.drawArc(p2.x - 5, p2.y - 5, 10, 10, deg, -180);
+        long timeLineStart = System.currentTimeMillis();
+        g.drawLine(x[0], y[0], x[1], y[1]);
+        g.drawLine(x[3], y[3], x[2], y[2]);
+        if (Logging.isTraceEnabled()) {
+            drawLineTimeSpent += System.currentTimeMillis() - timeLineStart;
+        }
+        long timeArcStart = System.currentTimeMillis();
+        g.drawArc((int) p1.getX() - 5, (int) p1.getY() - 5, 10, 10, deg, 180);
+        g.drawArc((int) p2.getX() - 5, (int) p2.getY() - 5, 10, 10, deg, -180);
+        if (Logging.isTraceEnabled()) {
+            drawArcTimeSpent += System.currentTimeMillis() - timeArcStart;
+        }
+
+        if (Logging.isTraceEnabled()) {
+            drawSegmentTimeSpent += System.currentTimeMillis() - timeStart;
+        }
     }
 
     /**
@@ -177,12 +258,22 @@
      * @param color The color
      */
     protected void drawSegment(Node n1, Node n2, Color color) {
-        if (n1.isDrawable() && n2.isDrawable() && isSegmentVisible(n1, n2)) {
-            PaintedSegment ps = new PaintedSegment(n1.getCoor(), n2.getCoor(), color);
-            if (!paintedSegments.contains(ps)) {
-                drawSegment(mv.getPoint(n1), mv.getPoint(n2), color);
-                paintedSegments.add(ps);
+        if (n1.isDrawable() && n2.isDrawable() && (isNodeVisible(n1) || isNodeVisible(n2) || isSegmentVisible(n1, n2))
+                && paintedSegments.add(new PaintedSegment(n1.lat(), n1.lon(), n2.lat(), n2.lon(), color))) {
+            Shape shape = new MapViewPath(mv).append(Arrays.asList(n1, n2), false);//.computeClippedLine(WAY_STROKE);
+            Stroke oldStroke = g.getStroke();
+            try {
+                g.setStroke(WAY_STROKE);
+                if (selected) {
+                    g.setColor(getHighlightColor(color));
+                    g.fill(shape);
+                }
+                g.setColor(color);
+                g.draw(shape);
+            } finally {
+                g.setStroke(oldStroke);
             }
+            //drawSegment(mv.getPoint2D(n1), mv.getPoint2D(n2), color);
         }
     }
 
@@ -201,7 +292,12 @@
 
     @Override
     public void visit(Way w) {
-        visit(w.getNodes());
+        /*if (w.isDrawable() && w.getNodes().parallelStream().anyMatch(this::isNodeVisible)) {
+            drawWay(w, color);
+        }*/
+        if (w.isDrawable()) {
+            visit(w.getNodes());
+        }
     }
 
     @Override
@@ -222,8 +318,7 @@
      * @return true if the node is visible
      */
     protected boolean isNodeVisible(Node n) {
-        Point p = mv.getPoint(n);
-        return !((p.x < 0) || (p.y < 0) || (p.x > mv.getWidth()) || (p.y > mv.getHeight()));
+        return nodes.contains(n.getUniqueId());
     }
 
     /**
@@ -234,23 +329,22 @@
      * @return {@code true} if the segment is visible
      */
     protected boolean isSegmentVisible(Node n1, Node n2) {
-        Point p1 = mv.getPoint(n1);
-        Point p2 = mv.getPoint(n2);
-        return (p1.x >= 0 || p2.x >= 0)
-            && (p1.y >= 0 || p2.y >= 0)
-            && (p1.x <= mv.getWidth() || p2.x <= mv.getWidth())
-            && (p1.y <= mv.getHeight() || p2.y <= mv.getHeight());
+        Point2D p1 = mv.getPoint2D(n1);
+        Point2D p2 = mv.getPoint2D(n2);
+        return (p1.getX() >= 0 || p2.getX() >= 0)
+            && (p1.getY() >= 0 || p2.getY() >= 0)
+            && (p1.getX() <= mv.getWidth() || p2.getX() <= mv.getWidth())
+            && (p1.getY() <= mv.getHeight() || p2.getY() <= mv.getHeight());
+        //return isNodeVisible(n1) || isNodeVisible(n2);
     }
 
     @Override
     public void visit(List<Node> nodes) {
         Node lastN = null;
         for (Node n : nodes) {
-            if (lastN == null) {
-                lastN = n;
-                continue;
+            if (lastN != null) {
+                drawSegment(lastN, n, color);
             }
-            drawSegment(lastN, n, color);
             lastN = n;
         }
     }
@@ -261,7 +355,7 @@
      * @return The color.
      */
     private static Color getHighlightColor(Color color) {
-        return new Color(color.getRed(), color.getGreen(), color.getBlue(), (int) (color.getAlpha() * .4));
+        return highlightColors.computeIfAbsent(color, c -> new Color(c.getRed(), c.getGreen(), c.getBlue(), (int) (c.getAlpha() * .4)));
     }
 
     /**
@@ -270,5 +364,11 @@
     public void clearPaintedObjects() {
         paintedPoints.clear();
         paintedSegments.clear();
+        paintedWays.clear();
+        if (Logging.isTraceEnabled()) {
+            Logging.trace("drawArcTimeSpent: {0}", Utils.getDurationString(drawArcTimeSpent));
+            Logging.trace("drawLineTimeSpent: {0}", Utils.getDurationString(drawLineTimeSpent));
+            Logging.trace("drawSegmentTimeSpent: {0}", Utils.getDurationString(drawSegmentTimeSpent));
+        }
     }
 }
