Skip to content

ActiveX: pan by cached layer blit (Phase 2) - #62

Merged
pmeems merged 7 commits into
developmentfrom
phase-2-pan-by-blit
Nov 7, 2026
Merged

pmeems merged 7 commits into
developmentfrom
phase-2-pan-by-blit

Conversation

@Julian-MW

Copy link
Copy Markdown

Phase 2 of the flicker fix plan: port the MapWinGIS5 drag-pan behavior (blit the cached layer frame instead of re-rasterizing every layer on each mouse move).

What changes

MapWinGISControl gains a second offscreen buffer (_layerCache, a FrameBuffer) holding the last fully rendered layer frame plus the mwg::Viewport it was rendered with.

  • OnDraw: after painting the background, TryBlitCachedLayers blits the cached frame — shifted by the pixel offset derived from the center difference ((cacheCenter − center) / resolution, Y flipped) — when the size and resolution match. Only the rectangle whose source lies inside the cache is blitted; the rest keeps the background. Cache miss, resize, zoom, or a clamped sub-pixel pan falls through to a real render.
  • DrawLayersAndUpdateCache: renders the scene into the cache (background-filled, so the blit is opaque), records the viewport, and blits it into the composed frame. Allocation/blit failures fall back to drawing straight to the target (printer/metafile paths unchanged in behavior).
  • Drag pan: OnMouseMove still calls _view.Pan + FireViewChange, but no longer triggers a layer re-render — OnDraw serves the frame from the cache.
  • Button up: OnLButtonUp invalidates the cache and fires one redraw, so the final view is rasterized exactly once (this also covers clamped pans, where the blitted frame could sit up to half a pixel off).
  • Cache invalidation: layer add/remove/clear (LoadVectorDatasetImpl, ClearLayers), all programmatic view changes (FitToMap, ZoomIn/Out, Pan, ZoomAt, ZoomToLayer, mouse wheel), resize (OnSize, SetObjectRects), and put_RenderingBackendPreference.

No changes to FrameBuffer, the renderers, or the Core — this is behavior (not code) ported from v5's Map_Drawing.cpp pan path, per the plan.

Deliberately left out

  • Mouse-move repaint coalescing — only needed if profiling shows it.
  • Phase 3 (tessellation) is untouched.

Testing

Not run here (no Windows build in this environment). The change is confined to the OCX control; existing FrameBufferTests/VectorLayerRendererTests are unaffected. Worth a manual pass in the C# host: drag-pan should show zero re-render and zero flicker; zoom/resize/layer add should re-render crisply.

@pmeems pmeems left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated review. Final decision is with the human reviewer.

- EndMousePan centralizes drag termination (button-up, WM_CAPTURECHANGED, WM_CANCELMODE, button released during move)
- Every termination path now invalidates the layer cache and requests one final redraw, so panned-in strips never keep showing the background
- OnDestroy and Close keep the non-redrawing CancelMousePan
- WM_CAPTURECHANGED, WM_CANCELMODE, and the no-button WM_MOUSEMOVE path now invalidate the layer cache and request a final redraw
- The shifted cached frame can no longer stay on screen after a drag ends outside WM_LBUTTONUP

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The presentation-blit failure path incorrectly leaves cache reuse enabled on the destination DC, potentially producing misplaced or blank fallback frames.

4 open issues
What changed in this PR

Implements Phase 2 of the ActiveX pan optimization by reusing a cached layer image during dragging, without changing Core or the renderers.

Changes:

  • Adds a layer-frame cache and viewport-based offset blitting.
  • Invalidates the cache after navigation, layer, size, and backend changes.
  • Centralizes interactive drag termination to request a freshly rendered final view.

Windows builds and tests were not run. A standalone numerical check confirmed the reported floating-point tolerance issue.

File Description
src/​MapWinGIS.ActiveX/​MapWinGISControl.h Declares cache state, rendering helpers, and drag-termination handling.
src/​MapWinGIS.ActiveX/​MapWinGISControl.cpp Implements cached rendering, shifted blits, and cache invalidation.

Review effort: Balanced

- Disable the layer cache when the presentation blit fails and the frame is drawn straight to the target DC, whose bounds origin and device differ from the offscreen buffer
- CancelMousePan invalidates the layer cache so shutdown paths (Close, OnDestroy, destructor) cannot leave a shifted cached frame for a reactivated control
- Accept any cache offset within half a pixel of an integer pixel shift: center floating-point accumulation no longer forces a full layer re-render on ordinary drags, and the mandatory drag-end render still lands the final frame exactly
- Record COLOR_WINDOW with the cache and reject the cached frame after a system-color or high-contrast change instead of repainting the old background over the new one

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Cache resizing can leave an unrendered replacement bitmap marked valid after FillRect fails, allowing a later draw to display invalid content.

No open issues 🎉

2 previously missed issues

In code that hasn't changed since last review

Medium severity Invalidate resized cache until rendering succeeds

src/​MapWinGIS.ActiveX/​MapWinGISControl.cpp:1177

An alternate-sized IViewObject::Draw can replace the cache bitmap without clearing _layerCacheValid. If the subsequent FillRect fails, the direct-render fallback leaves the replacement marked valid, so the next draw at that size can blit an unrendered bitmap. Invalidate the cache before resizing or modifying it, and revalidate only after a successful cache render.

Low severity Test drag cache reuse and invalidation scenarios

src/​MapWinGIS.ActiveX/​MapWinGISControl.cpp:1291

The existing drag test in tests/MapWinGIS.ActiveX.Tests/ActiveXActivationTests.cpp:1911–1946 draws only after button-up, so it does not verify cache reuse during a drag. Add control-level tests that draw between mouse moves and verify shifted pixels, background-only exposed strips, and no layer-renderer calls. Cover final rendering after button-up, capture loss, and cancellation, plus cache invalidation after navigation, resize, and layer changes.

4 resolved since last review

Review effort: Balanced

@pmeems
pmeems merged commit d761488 into development Nov 7, 2026
3 checks passed
@pmeems
pmeems deleted the phase-2-pan-by-blit branch November 7, 2026 00:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants