﻿id	summary	reporter	owner	description	type	status	priority	milestone	component	version	resolution	keywords	cc
24842	[patch] Reuse the previous frame when panning at constant zoom	mistenman	team	"Marked as `priority: major` due to being a massive improvement to the responsiveness of the panning action. 

Right-button dragging currently re-renders the whole map on every mouse move. On dense, low-zoom views one frame takes 200–300 ms to render even on my powerful gaming computer, and because the render happens on the UI thread, panning stutters and the editor freezes while a frame is drawn.

The map view already keeps the unchanged layers nonChangedLayersBuffer and reuses it while the view stays the same (lastViewID). The idea is to also reuse it for a panned view, by shifting the old pixels by the pan distance and only redrawing the strip that was exposed. Copying pixels is much cheaper than re-rasterizing the whole map.

The buffer is kept as a complete composite of all visible layers, so a panned view can always be built by shifting it. The reuse has to be skipped, falling back to the current full redraw, in situations such as a zoom, resize,
layer changes, paint preference changes, or a pan larger than the viewport.

One limitation is that while panning, layers invalidated mid-drag (for example freshly loaded imagery tiles) are only redrawn in the exposed strip, so their changes show up once the pan stops. When the view is still, each tile arrival still causes a full redraw of the layer, as before.

The main change is in `MapView.drawMapContent`. The imagery attribution is fixed to the viewport and would smear if it stayed in the reused composite, so it needs to be painted on top per frame instead.

Related: #11487 ""added an optional asynchronous tiled renderer"". This proposal targets the default synchronous renderer (used when tiled rendering is disabled and at zoom levels above `mappaint.fast_render.zlevel`), reducing per-frame work by reusing the previous frame instead of re-rasterizing the whole map. The two approaches are complementary.

=== Summary of attached patch (implementation example against r19616) ===

The map view already keeps a composite of the unchanged layers and reuses it while the view stays the same. This patch extends that to panned views. During a drag at constant zoom, it copies the previous frame shifted by the pan distance and redraws only the strip that was exposed (which I will refer to as pan-reuse). Copying pixels is far cheaper than re-rasterizing the whole map, so pan frames drop from roughly 200-300 ms to 1-20 ms (on my setup), depending on the complexity of the frame.

Changes by file:

* MapView.java: pan-reuse in `drawMapContent`. The buffer is copied back after every frame, so it always holds a complete composite of all visible layers and a pan frame can be built by shifting it and repainting the exposed strips. Reuse requires the layer set to be unchanged, the scale unchanged, the last frame to have covered the whole viewport, and the movement to be between 0.5 px and the viewport size. Layers invalidated during the pan (e.g. imagery tiles arriving) are painted strip-only, and the rest shows up when the pan stops. Also adds `isPanning()` and a per-frame imagery attribution pass. With the config preference `mappaint.debug.pan-reuse=true/false`, panning actions print debug information about which pan-reuse logic triggers into stdout.
* AbstractTileSourceLayer.java: the imagery attribution is fixed to the viewport, so it would smear if left in the reused composite. It is now painted on top of the frame each paint via a new `paintAttribution(Graphics2D, MapView)`.
* SelectAction.java, OsmDataLayer.java: hovering no longer invalidates the data layer. Previously every crossing of a feature caused a full re-render (~200-300 ms) and broke the reuse state. The hover highlight is now drawn by SelectAction as a temporary overlay layer, `OsmDataLayer.primitiveHovered` no longer resets the render cache or invalidates, and `giveUserFeedback` is suppressed while panning.
* MapMover.java: `endMovement` no longer invalidates all layers. A pan changes nothing but the view, so the last composite stays valid; the old invalidation caused a full re-render right after every drag and at the start of the next one.

Reuse falls back to the normal full repaint whenever the view did more than translate, i.e. zoom, resize, layer changes, or a pan larger than the viewport. When the view is still, invalidated layers repaint in full as before.

Testing: set `mappaint.debug.pan-reuse=true` (optionally `mappaint.render.benchmark=true`) and drag at constant zoom on a dense view. each drag frame should log `PANREUSE reuse` and the benchmark's phase 2's render time should drop to a fraction of a full frame render.

I will attach a video demonstrating panning in r19555 vs r19616 with the patch applied."	enhancement	new	major	26.09	Core mappaint			render rendering performance optimization panning graphics	
