Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .ai/memory/build/cmake.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,15 @@ Generated Visual Studio projects set `WindowsTargetPlatformVersion` before
importing `Directory.Build.props`; its legacy SDK default must not override
that selection, including in CMake's `VCTargetsPath` probe.

The `cmake-parity` job fails (`-FailOnLostFlags`) when a translation unit is
compiled by MSBuild but missing from the CMake target — including test
projects. Adding a source file to a `.vcxproj` therefore requires the same
addition to the corresponding `CMakeLists.txt` source list (verified on
PR #71, where `PolygonTriangulationTests.cpp` was listed in
`MapWinGIS.Core.Tests.vcxproj` but not in its CMake target). The parity
report (`out/parity/parity-Debug-x64.json` on the runner) names the lost
TU; the job log only shows the lost count.

## References

- [CMake presets](../../../CMakePresets.json)
Expand Down
27 changes: 17 additions & 10 deletions docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ responsibilities, not a requirement to split them into more DLLs.
| `MapWinGIS.ocx` | Own COM activation, host-facing control lifecycle, and translation between the external API and core operations. Integrate core rendering with the host surface and selected backend. | GIS rules, core data ownership, rendering-specific types in its public COM contract |
| Rendering coordinator (in the OCX) | Try OpenGL for supported scenes and use GDI when OpenGL is unavailable or the scene is unsupported. Keep backend selection independent of COM activation. | GIS rules, COM interface layout, backend-specific resource types outside the backend |
| GDI backend (in the OCX) | Paint supported polygon scenes, including interior rings, into a device context; provide the Windows CPU fallback. | Core dataset ownership, COM interface layout, GPU availability |
| OpenGL backend (in the OCX) | Render convex single-ring polygons through a reusable WGL context, then copy pixels to the host device context. | Core GIS data ownership and public COM interfaces |
| OpenGL backend (in the OCX) | Fill polygons from their Core triangulated meshes, outline the original rings, and copy pixels to the host device context through a reusable WGL context. | Core GIS data ownership and public COM interfaces |
| Future compute work | Host optional compute acceleration only for workloads justified by profiling. | OpenGL being installed or selected |

The OCX is the composition root for control, render-coordinator, and
Expand Down Expand Up @@ -296,8 +296,8 @@ and has no mesh. `ViewTransform` captures the `Viewport` once
per frame so both backends transform every vertex of every layer with the
same arithmetic as `Viewport::MapToScreen`; GDI draws a screen position in
the pixel that contains it (`ToPixel`). One backend draws the whole frame.
The coordinator tries the OpenGL backend when every layer has only simple
convex polygons with one ring. GDI checks each layer before drawing it; a
The coordinator tries the OpenGL backend when every polygon of every layer
has a triangulated fill mesh. GDI checks each layer before drawing it; a
layer with non-finite or out-of-range device coordinates is skipped and
reported (`GetFailedLayers`, `RenderingStatus`) while the other layers are
still drawn.
Expand All @@ -307,10 +307,13 @@ fixed-function calls into a framebuffer object (OpenGL 3.0 or
before copying them to the host device context; this is a working backend
boundary, not an end-to-end accelerated presentation path. Rendering into a
framebuffer object avoids undefined pixels from window pixel-ownership rules.
Interior rings, concave or self-intersecting rings, missing framebuffer-object
support, initialization failures, and drawing failures use the GDI backend.
The GDI backend draws the currently supported polygon scene, including its
interior rings. Both backends draw source coordinates without reprojection,
Interior rings and concave boundaries draw identically in both backends
because both consume the same scene; a polygon without a fill mesh (for
example a self-intersecting ring that GEOS cannot triangulate), missing
framebuffer-object support, initialization failures, and drawing failures use
the GDI backend, which draws the original rings.
The GDI backend draws the original rings of the whole supported polygon scene,
including its interior rings and concave boundaries. Both backends draw source coordinates without reprojection,
and scene preparation currently supports Polygon, MultiPolygon, CurvePolygon,
and MultiSurface features.

Expand Down Expand Up @@ -407,10 +410,14 @@ changes and explicit invalidation rebuild the scene for all visible layers.
There is no tiling, paging, or clipping to the visible extent. GDI skips a
layer whose device coordinates exceed its range. It clips drawing to the
requested target rectangle and the host's existing clip region, then restores
the device context. The opt-in
the device context. Triangulation runs once per polygon when the scene is
prepared; view changes (resize, zoom, pan) and redraws reuse the retained
meshes. The opt-in
rendering baseline (see [TESTING.md](TESTING.md#rendering-performance-baseline))
tracks scene preparation and backend frame costs. OpenGL only supports convex
single-ring polygons and copies each frame through CPU memory. Its reusable CPU
tracks scene preparation, mesh storage, and backend frame costs on both a
convex grid and a complex mix of concave, holed multipart, and many-vertex
polygons. OpenGL fills every polygon from its retained triangle mesh and
copies each frame through CPU memory. Its reusable CPU
staging buffer is limited to 64 MiB per frame; target dimensions are also
limited by the detected framebuffer and viewport capabilities. GDI is the supported
Windows CPU fallback; Core has no independent platform-neutral renderer.
Expand Down
7 changes: 4 additions & 3 deletions docs/PORTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,10 @@ Each control load selects the first dataset layer. Core prepares an ordered
scene for all visible map layers, including Polygon, MultiPolygon,
CurvePolygon, and MultiSurface geometry. It
does not expose the broad v5 map, layer, shape, event, or editing API. It draws
in source coordinates without reprojection. OpenGL only handles convex
single-ring polygons; GDI handles the supported polygon scene as the CPU
fallback.
in source coordinates without reprojection. OpenGL fills the triangulated
meshes Core prepares for convex, concave, holed, and multipart polygons; GDI
draws the original rings as the CPU fallback for scenes and systems OpenGL
cannot handle.

Keep these gaps visible when extending the COM surface. Reuse Core behavior,
add only the automation members required by a concrete use case, and retain a
Expand Down
46 changes: 38 additions & 8 deletions docs/TESTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -185,18 +185,48 @@ For build and manual-use instructions, see the
GeoPackage grid of convex 32-vertex polygons and separately reports Core
dataset open, render-scene preparation, process private-memory growth, the
median GDI frame, the first OpenGL frame (context creation and drawing), and
the median steady OpenGL frame, for a 1280x720 off-screen target. It records
the values as GoogleTest properties and fails only when a stage fails. Use a
Release build for meaningful numbers:
the median steady OpenGL frame, for a 1280x720 off-screen target.
`RenderingBaseline.MeasuresComplexPolygonCosts` generates a second dataset —
concave L-shaped polygons, holed rectangles where every feature is a
two-component multipolygon, and concave 32-vertex star polygons across three
layers — and additionally reports the number of triangulated polygons and the
mesh storage (vertices plus indices). Scene preparation in both tests
includes the one-time constrained Delaunay triangulation of every polygon.
Results are recorded as GoogleTest properties; the tests fail only when a
stage fails. Use a Release build for meaningful numbers:

```powershell
$env:MWG_RUN_RENDERING_BASELINE = '1'
$env:MWG_RENDERING_BASELINE_FEATURES = '100000' # optional; default 50000
bin\Release\x64\MapWinGIS.ActiveX.Tests.exe --gtest_filter=RenderingBaseline.*
```

Measurements on one developer workstation (Release, hardware OpenGL); the
previous implementation measurements are retained for comparison:
Measurements on one developer workstation (Release, hardware OpenGL), with
triangulation included in scene preparation:

| Measurement | Convex grid, 50k polygons | Complex mix, 162.5k meshes |
|---|---:|---:|
| Core open dataset | 1.8 ms | 2.1 ms |
| Core prepare render scene | 2,505.6 ms | 2,758.7 ms |
| Mesh storage (vertices+indices) | — | 32.1 MiB |
| Private memory increase for scene | 87.6 MiB | 86.4 MiB |
| GDI frame (median) | 84.0 ms | 163.9 ms |
| OpenGL first frame | 291.4 ms | 49.3 ms |
| OpenGL frame (median) | 28.4 ms | 38.1 ms |
| Private memory increase total | 203.7 MiB | 94.4 MiB |

The complex mix is 50k concave L-shapes, 50k holed multipart features, and
12.5k star polygons (162,500 triangulated meshes in total); it ran after the
convex grid in the same process, so its total memory does not repeat the
one-time OpenGL driver allocation. Triangulation runs once per polygon during
scene preparation and the meshes are reused on redraw, so the triangulation
cost shows up in scene preparation, not in per-frame times. Before
triangulation existed, the same convex grid prepared its scene in 91.5 ms per
100k features; triangulation now dominates scene preparation at roughly
15 µs per polygon on this workstation. OpenGL frames re-submit all geometry
because there is no unchanged-geometry cache.

The pre-triangulation measurements are retained for comparison:

| Measurement | x64, 10k (previous) | x64, 100k (previous) | x64, 100k (sequential) |
|---|---:|---:|---:|
Expand All @@ -208,11 +238,11 @@ previous implementation measurements are retained for comparison:
| OpenGL frame (median) | 10.0 ms | 82.3 ms | 80.5 ms |
| Private memory, total | 91.7 MiB | 167.4 MiB | 167.2 MiB |

Scene preparation now reads geometries sequentially through OGR and copies
Scene preparation reads geometries sequentially through OGR and copies
their coordinates directly, avoiding indexed lookups, WKT serialization, and
reparsing. The 100k-feature measurement dropped from 15,225 ms to 91.5 ms on
the recorded x64 run. OpenGL frames re-submit all geometry because there is no
unchanged-geometry cache. Total memory includes the OpenGL driver and context.
the recorded x64 run before triangulation was added. Total memory includes
the OpenGL driver and context.

## Conventions

Expand Down
Loading