feat: add Tianditu elevation provider, GCJ-02 conversion, and GPU acceleration - #1238
feat: add Tianditu elevation provider, GCJ-02 conversion, and GPU acceleration#1238ChidcGithub wants to merge 27 commits into
Conversation
- Add GCJ-02/WGS-84 bidirectional coordinate conversion module - Implement Tianditu elevation provider for China - Add Tianditu settings to GUI (toggle + token input) - Update 19 locale files with new keys - Fix broken en.json locale file
…entity ops, manual range)
8e57c96 to
f3cac1c
Compare
- Add optional gpu feature (wgpu 22 + pollster) for separable Gaussian blur - WGSL compute shader with horizontal/vertical passes - Auto-fallback to CPU (rayon) when GPU unavailable - Cross-platform: DX12/Vulkan on Windows, Vulkan on Linux, Metal on macOS - Benchmark workflow: add Windows/macOS/Linux matrix with fail-fast disabled - Trigger benchmark on push to master/main and workflow_dispatch
- GPU toggle in Settings World section (off by default) - Progress indicator shows (using GPU acceleration) when active - Runtime check via ARNIS_GPU env var, requires compile with --features gpu - Locale keys added to all 19 language files (zh-CN: GPU 加速 / 正在使用 GPU 加速)
…) and memory tracking
…at, allow bench_gpu.ps1 in gitignore
apply_osm_water_override built the water context (connected-component analysis over the whole land-cover grid) before checking whether any element could trigger a fill. In terrain-only mode or water-free bboxes this was pure overhead: ~23 s on a 16384x11145 grid (~20% of total). Add a has_water_override_candidates early-out mirroring the existing bridge_repair pattern; the predicate reuses is_water_polygon_way and is_water_relation so behaviour is identical whenever water elements do exist. Add regression tests: empty/non-water elements leave the grid untouched; water polygons and waterway lines still fill through the public entry.
The previous GPU path never actually ran on large grids and produced mismatched output when it did: - Device limits: request the adapter's real buffer limits instead of the wgpu defaults. The default 128 MB max_storage_buffer_binding_size made every grid above ~33M cells panic at bind time and silently fall back to CPU (a 16384x11145 f32 grid is 730 MB). - gpu_blur.wgsl rewritten to match CPU semantics exactly: non-finite samples are skipped and kernel weights renormalised (the built-up smoothing deliberately blurs grids containing NaN water cells), edges renormalise instead of clamp-extending, and the kernel is centred at size/2.0 like the CPU (the old kernel was asymmetric at size/2). - New resident passes between one upload/download: gpu_anomaly.wgsl (5x5 median/MAD repair) and gpu_nan.wgsl (Jacobi NaN dilation), with an atomic change counter preserving the CPU early-break semantics. NaN fill falls back to the CPU loop if the iteration cap is hit. - Parallelise f64<->f32 conversion, grid flatten/reshape, and the built-up blend loop with rayon; warm up the GPU device during data download so adapter init stays out of the timed phases. Large-area benchmark (16x11 km bbox, 183M-cell grid, terrain-only, RTX 4060 Laptop): anomaly repair 14.8s -> 3.0s land-cover repair 23.6s -> 4.9s (two Gaussian blurs) terrain total 51.2s -> 20.5s overall wall ~94 s -> ~59 s CPU/GPU output-equivalence unit tests included; they skip cleanly on GPU-less CI runners.
The fork-specific GPU benchmark matrix rewrote pr-benchmark.yml for local experimentation (push triggers, no PR comment step). That removed the project's automated PR benchmark comments, baseline verdicts, and the retrigger-benchmark command — none of which this PR intends to change. Restore the upstream file so the PR stays focused on the Tianditu/GPU features; GPU numbers are produced locally via bench_gpu.ps1.
|
Benchmark results (CSV) Committed to this branch as scenario,wall_cpu_s,wall_gpu_s,gen_cpu_ms,gen_gpu_ms,repair_cpu_ms,repair_gpu_ms,speedup
large,165.4,63.9,52128,41617,23012,4717,1.25Measured locally on an RTX 4060 Laptop via |
|
A note on benchmark variance The numbers in the table above and in the attached CSV come from individual local runs, and some variance between runs is expected:
The phase-level conclusions are stable across runs, though: the GPU-resident pipeline consistently cuts anomaly repair (~15s → ~3s) and land-cover repair (~24s → ~5s), and the relative speedups hold regardless of absolute wall-time noise. Treat the exact figures as indicative rather than precise. |
|
Due to GitHub Actions permission issues, the benchmark comment could not be published properly. However, the benchmark workflow actually ran successfully, so I have pasted the generated results here. |
|
⏱️ Benchmark run finished in 0m 50s 📈 Compared against baseline: 18s time, 1160 MB memory 🟢 Generation time is unchanged. 📅 Last benchmark: 2026-08-05 04:07:03 UTC |
|
@louis-e 👋 This PR is now ready for your review. It adds a Tianditu elevation provider, supports GCJ-02 conversion, and enables GPU-accelerated processing and has passed all local tests. Could you please take a look when you have a moment? Let me know if you need any additional context or testing steps. Thanks in advance! =) |
|
Update: ⏱️ Benchmark run finished in 0m 38s 📈 Compared against baseline: 18s time, 1160 MB memory 🟢 Generation time is unchanged. 📅 Last benchmark: 2026-08-06 02:38:29 UTC |
|
Hey there, thanks a lot! :) I'll look into it as soon as I have time for it! |
|
@louis-e Can you please review it as soon as possible, because other commits are starting to make breaking changes to this code? |
Summary
This PR adds two independent features:
1. Tianditu (天地图) elevation provider for China
src/coordinate_system/gcj02.rs)src/elevation/providers/tianditu.rs, ~30 m resolution via Terrain-RGB tiles)TIANDITU_TOKENis set and the bbox overlaps China; graceful fallback to Mapterhorn otherwise2. GPU acceleration (opt-in:
--features gpu+ARNIS_GPU=1)GPU-resident elevation post-processing pipeline built on wgpu compute shaders:
gpu_anomaly.wgsl), Jacobi NaN fill (gpu_nan.wgsl), separable Gaussian blur (gpu_blur.wgsl) — a single upload, multiple passes, a single downloadcatch_unwindguards every entry point)Building & enabling the GPU path
The GPU code lives behind the optional
gpuCargo feature (wgpu+pollster) and is off by default:At runtime the GPU path stays opt-in:
Requirements: any GPU with a DX12 (Windows), Vulkan (Windows/Linux), or Metal (macOS) driver — no CUDA or vendor SDK needed. Without the feature flag, without the runtime opt-in, or when no suitable adapter / buffer limit is available, every step transparently runs on the existing CPU (rayon) path and behaviour is unchanged from upstream.
Benchmarks
RTX 4060 Laptop, terrain-only mode, 16×11 km bbox → 16384×11145 grid (183M cells):
This PR also fixes a full-grid connected-component analysis that ran even when no OSM water elements exist (−23 s on large terrain-only runs, benefits CPU and GPU paths alike).
Correctness fixes in the GPU path
size/2.0like the CPU (the previous kernel was asymmetric)Testing
pr-benchmark.ymlworkflow is intentionally left untouched — GPU benchmarking runs locally viabench_gpu.ps1since GitHub-hosted runners have no GPU. The GPU numbers above are from local testingFiles changed
gcj02.rs,tianditu.rs,gpu.rs,gpu_blur.wgsl,gpu_anomaly.wgsl,gpu_nan.wgsl,bench_gpu.ps1Cargo.toml