Meshroom / AliceVision photogrammetry on AMD Radeon GPUs, via HIP.
AliceVision's dense reconstruction (DepthMap, the stage Meshroom needs a GPU for) is
written in CUDA. An official SYCL backend was merged upstream in May 2026
(AliceVision#2077, closing the
eight-year #439) but has not appeared
in any release, resolves to off when AdaptiveCpp is absent, and has no published results on
AMD hardware. Cheshire is a HIP port you can download and run today, on Windows and Linux:
the same CUDA kernels compiled through a small compatibility layer, plus a memory bridge
that lets the stage spill from VRAM into system RAM instead of failing, validated
per architecture against a CUDA node's output on RDNA1, RDNA2 and RDNA4 cards.
The cat that grins on any hardware.
Two things here are not off-the-shelf. The port itself is close to mechanical (a compat
header does what hipify would), but it is validated: every architecture is compared
against a CUDA reference with a stated metric, and the porting bugs found on the way are the
kind that produce wrong output rather than errors (HIP dropping surf2Dwrite into 16-bit
float arrays; GPU atomics into mapped host memory silently wrong on Linux unless the memory
is non-coherent). The memory bridge is the novel part: it decides what spills to system RAM
by buffer class, and the policy is set by measurement, not by intuition. With the default
fine-grained host mapping, 186 MB of texture-sampled camera images behind PCIe cost 22x while
6 GB of streamed volumes cost 4.7x; coarse-grained mapping turns the images to 1.0x. That
number is why the design is what it is.
| Meshroom node | upstream | Cheshire | status |
|---|---|---|---|
| DepthMap | CUDA only | HIP port + VRAM-to-RAM memory bridge | validated RDNA1/2/4, Windows + Linux; bit-identical to native across caps |
| FeatureMatching | CPU (kd-tree) on every vendor | exact GPU brute-force 2-NN (HIP; the source also builds as CUDA), plus exact CPU work in AC-RANSAC (docs/15) | validated end to end (v0.2.5): 592 s -> 75 s on 107 photos, same reconstruction. Geometric verification followed in v0.2.15: 566 s -> 351 s, byte-identical match file, and the profile is why it stayed on the CPU |
| FeatureExtraction | CUDA (PopSift) or CPU | HIP PopSift port (docs/14) | done (v0.2.13): 2021 s -> 26 s on 41 views (RX 9070), and better than the CPU path rather than merely faster — 88,463 landmarks against 78,372 and RMSE 1.0377 against 1.03997 px. Validated on RDNA4, RDNA1 and RDNA2 across two runtimes and both systems, landmarks within 0.11 %; found and reported an upstream PopSift bug (popsift#193) |
| DepthMapFilter | CPU | GPU vote pass (HIP / CUDA source) + a shared decoded-map cache | validated (v0.2.6): 123.5 s -> 26.5 s on 107 photos (RX 9070), 317 s -> 26.9 s on 41 views on an i3 + RX 5500 XT, bit-identical; found and replicated an upstream vote-buffer quirk; v0.2.9 cache: 17.8 s -> 12.7 s, byte-identical |
| Meshing | CPU | GPU graph-weight votes + weakly-supported-surfaces pass, GPU min-cut (push-relabel, docs/12), GPU nearest-neighbour search for the visibility passes (docs/13), exact CPU fixes around them (docs/11) | done (v0.2.7 through v0.2.12, v0.2.16): 510 s -> 101 s on 107 photos (RX 9070), 491 s -> 111 s on 41 views on an i3 + RX 5500 XT; the cut labelling identical to Boykov-Kolmogorov's and the nearest neighbours identical to nanoflann's on every query, every CPU change verified identical in-process; what is left is the tetrahedralisation |
| Texturing | CPU | GPU Laplacian pyramid + rasterisation, parallel camera selection (HIP / CUDA source), exact CPU fixes in the UV unwrap (docs/10) | done (v0.2.8, v0.2.16): 165.8 s -> 79.3 s on 107 photos (RX 9070), 271.5 s -> 102.5 s on 41 views on an i3 + RX 5500 XT, textures inside the CPU's own run-to-run band; v0.2.16 took the UV unwrap 14.4 s -> 8.6 s with a byte-identical texturedMesh.obj, and gains most on the slowest hardware (51.9 s -> 16.8 s on a 2011 Bulldozer); what is left is mesh I/O and padding, about 80 % of the node on the engine bay, queued for 0.3.2 (docs/04) |
| PrepareDenseScene | CPU | every core instead of three threads | done (v0.2.9): 36 s -> 30 s on 107 photos, byte-identical; the loop is codec and disk work |
| StructureFromMotion | CPU | exact CPU fix: the resection pass that ends without its bundle adjustment gets one, and the local-BA graph cannot throw | 0.3.3: the invalid map<K, T> key crash of Meshroom #2344, reproduced three times on an 884-photo set, found and fixed (docs/04); the eighth paired node |
| ImageMatching, MeshFiltering | CPU | stay on the CPU (sequential or tiny) |
Everything installs by pairing: one script swaps the node binaries (DepthMap, FeatureExtraction, FeatureMatching, DepthMapFilter, Meshing, Texturing, PrepareDenseScene) in an existing Meshroom 2023.3 install and keeps Meshroom's own beside them, choosing per run by the card present. On a 12-thread desktop the 107-photo engine bay job went from 39 minutes with the CUDA-era layout to 30 as measured at v0.2.13, before the geometric verification, point-cloud fusion and UV unwrap work in v0.2.15 and v0.2.16 took another 230 s off the stages it covers.
Read the upstream column again: DepthMap is the only stage Meshroom puts on a CUDA GPU. Feature
matching, depth map filtering, meshing and texturing are on the CPU for everyone, NVIDIA included.
Every one of the ports above was written as CUDA source and compiled through a compat header for
HIP, so building them as CUDA costs nothing but a configure - and an NVIDIA user gets the five
stages plus GPU SIFT and the memory bridge, none of which are upstream.
DepthMap is the exception and deliberately so: on CUDA it is upstream's own kernels, and a packaged run is byte-identical to a Meshroom CUDA 11.3 reference, checked from a clean extraction of the release tarball rather than from a build tree. Nobody should switch DepthMap backends for its own sake; the reason to take the CUDA package is everything around it.
Same photos, same SfM, same DepthMap parameters as the CUDA reference (Meshroom 2023.3 on a GTX 1080 Ti); only the GPU stage differs. Metrics are per view over pixels valid in both maps. Times are the DepthMap stage only. The CUDA reference ran as Meshroom runs it, in chunks of 12 views (four chunks for 41 views: 105.4 + 111.1 + 106.3 + 56.2 s); the HIP times are one invocation over all views. The apples-to-apples numbers are the two cards run inside real Meshroom jobs on the same node, also four chunks: RX 6750 XT 352 s, RX 5500 XT 594 s (both jobs reconstructed the full 41-photo mesh, within 2 % of the CUDA job's vertex count).
| GPU | OS | 6 views | 41 views | per view (41) | valid masks | median rel. depth error | within 1 % (median view, 6 / 41) |
|---|---|---|---|---|---|---|---|
| GTX 1080 Ti (CUDA reference, 4 Meshroom chunks) | Linux | 31.8 s | 379.0 s | 9.2 s | |||
| GTX 1080 Ti, CUDA 11.6 (Meshroom 2023.3 Windows build), same host as the RX 6750 XT Windows rows | Windows | 40.0 s | 287.1 s | 7.0 s | 37 / 41 identical to the reference | 0.0000 | 100 % / 100 % (worst view 92.9 %) |
| Radeon RX 9070, RDNA4 | Windows | 17.4 s | 124.4 s | 3.0 s | identical | 0.0000 | 98.9 % / 98.7 % |
| Radeon RX 6750 XT, RDNA2 | Linux | 31.0 s | 226.1 s (352 s in 4 Meshroom chunks) | 5.5 s | identical | 0.0000 | 97.5 % / 98.1 % |
| Radeon RX 6750 XT, RDNA2, HIP SDK 6.2 build | Windows | 28.1 s | 190.3 s | 4.6 s | identical | 0.0000 | 98.7 % / 98.7 % |
| Radeon RX 6750 XT, RDNA2, v0.2.3 bundle | Linux | 28.0 s | 205.7 s | 5.0 s | identical | 0.0000 | 97.5 % / 98.1 % |
| Radeon RX 5500 XT, RDNA1 | Linux | 60.7 s | 447.7 s (594 s in 4 Meshroom chunks) | 10.9 s | identical | 0.0000 | 97.5 % / 98.1 % |
| Radeon RX 5500 XT, RDNA1, v0.2.3 bundle | Linux | 52.4 s | 396.9 s | 9.7 s | identical | 0.0000 | 97.5 % / 98.1 % |
| Radeon RX 5500 XT, RDNA1, v0.2.5 bundle | Linux | 396.4 s | 9.7 s | identical | 0.0000 | 97.5 % / 98.1 % |
Per-view cost on every HIP card is flat between 6 and 41 views (the port scales linearly);
the CUDA reference's per-view cost rises from 5.3 s to 9.2 s across the chunked run. Until
2026-09-15 this table listed the CUDA 41-view time as 105.5 s, which was the first chunk
alone; the corrected figure is the sum of all four (data/ref/monstree-full/DepthMap/*/[0-3].log).
Three different comparisons appear in this repo, and they have three different answers.
All metrics are per view over pixels valid in both maps (scripts/compare_depthmaps.py;
tables in docs/validation).
| comparison | validity masks | median rel. depth error | pixels within 1 % | verdict |
|---|---|---|---|---|
| same card, any bridge configuration vs the uncapped run | identical | 0 | 100 % (every pixel equal) | bit-identical |
| RX 5500 XT vs RX 6750 XT (RDNA1 vs RDNA2), same HIP build | identical | 0 | 100 % (every pixel equal) | bit-identical |
| RX 9070 vs RX 6750 XT (RDNA4 vs RDNA2), same HIP build | identical | 0 | 97.0-98.5 % | not bit-identical: p95 error 0.07-0.24 % |
| RX 6750 XT under Windows / HIP 6.2 vs the same card under Linux / HIP 7.2 | identical | 0 | 97.1-98.7 % | not bit-identical: same silicon, different compiler and runtime |
| GTX 1080 Ti, CUDA 11.6 on Windows vs GTX 1080 Ti, CUDA 11.3 on Linux (the reference itself) | 41 / 41 | 0 | 100 % median view, 92.9 % worst | 37 / 41 depth maps bit-identical; the other four differ by up to 7.2 % of pixels. CUDA is not bit-identical to CUDA across builds either |
| this tree built as CUDA 12.9 vs the CUDA 11.3 reference, same GTX 1080 Ti | 41 / 41 | 0 | 100 % (every pixel equal) | byte-identical, 41 / 41 depth maps and 41 / 41 sim maps - and 1.80x faster (211 s vs 379 s). AliceVision 3.1 vs 3.4-dev and CUDA 11.3 vs 12.9 both contribute exactly nothing |
| any HIP card vs the CUDA GTX 1080 Ti | identical | 0 | 97.5-98.9 % | same noise floor as RDNA4 vs RDNA2 |
So the port is deterministic (same inputs, same build, same architecture family, same bits), and the 1-3 % of pixels that differ by more than 1 % between RDNA4 and RDNA2, between two toolchains on the same card, or between any AMD card and CUDA, is the algorithm's noise floor: texture-filter and FMA rounding propagating through SGM's argmin, not a port defect. The same 1-3 % appears between two AMD generations running identical code, between two compilers on identical silicon, and between two CUDA builds on the same NVIDIA card (the reference disagrees with a Windows CUDA 11.6 run of itself on 4 of 41 views, one of them by 7.2 % of pixels), which is what rules out the port as the cause.
Since 2026-09-20 that argument no longer rests on circumstantial evidence. This same tree, same patches, built as CUDA instead of HIP, is byte-identical to the CUDA 11.3 reference on all 41 views. Every patch this project carries is therefore provably neutral, and the 1-3 % is AMD hardware and compiler float behaviour with nothing else mixed in (docs/18).
Measured on the RX 9070 (hip/tests/membridge_probe.hip), the tiers the bridge chooses between:
| memory | kernel streaming read |
|---|---|
VRAM (hipMalloc) |
585-619 GB/s |
mapped system RAM (hipHostMalloc, PCIe 4.0 x16) |
24.6-26.6 GB/s |
hipMallocManaged |
26.6 GB/s (no page migration on Windows) |
| pinned host copies | 28 GB/s each way |
On Windows the driver already lets hipMalloc run past VRAM into system RAM; on Linux it fails
hard. (Mapped host memory is real system memory on both: hip/tests/host_backing.hip reads it at
28 GB/s against 615 GB/s for VRAM and it leaves free VRAM untouched; Task Manager's "dedicated GPU
memory" for such a process is a commitment figure, not residency.) The bridge (hip/compat/include/cheshire/bridge.h) makes both behave the same, and v2
decides what spills by buffer class instead of by arrival order. Measured, one class at a
time behind PCIe, every run bit-identical:
| spilled to system RAM (6 views) | fine-grained host memory | coarse-grained (shipped) |
|---|---|---|
| nothing | 20.6 s | 20.2 s |
| depth/sim maps (879 MB) | 35.9 s (1.7x) | 25.5 s (1.3x) |
| similarity volumes (6.0 GB) | 96.4 s (4.7x) | 76.6 s (3.8x) |
| camera images (186 MB) | 460 s (22x) | 20.4 s (1.0x) |
The host tier is coarse-grained (hipHostMallocNonCoherent): the GPU caches it, atomics work,
and the texture-sampled camera images stop costing anything. Images stay resident regardless
(the planner reserves VRAM for them), volumes and maps spill, and the DepthMap planner sizes
its tile parallelism from the bridge's budget instead of hipMemGetInfo:
| VRAM available | v1 (arrival order, planner unaware) | v2 |
|---|---|---|
| 1.5 GB | 47.7 s, 188 spills | 20.0 s, 0 spills |
| 1 GB | 20.3 s, 0 spills | |
| 500 MB (below one tile) | fails upstream | 47.2 s, runs |
Tile parallelism costs nothing to give up on this GPU, so a card with 1 GB to spare runs the stage at full speed. Design, knobs and every table: docs/02-memory-bridge.md.
FeatureMatching is the second GPU-shaped stage, and upstream has no GPU path for it on any vendor.
hip/port/gpu_matcher/ is an exact brute-force 2-NN on the GPU behind AliceVision's
RegionsMatcher (taken for ANN_L2 / BRUTE_FORCE_L2 whenever a device is present; the same
source builds as HIP or CUDA), byte-identical to the CPU brute force. Engine bay set, 107 phone
photos, RX 9070: the FeatureMatching node from 592 s to 75 s, the matching stage from 543 s to
40.7 s, and the reconstruction unchanged (107 / 107 cameras, residual RMSE 1.694 px vs 1.712,
mesh within 0.3 %). The pairing scripts swap this binary in next to DepthMap. Details and the
Meshroom 2023.3 chunking compatibility in docs/07-gpu-matcher.md.
| card | uncapped | 2 GB cap | 1 GB cap |
|---|---|---|---|
| RX 9070, Windows | 499.6 s (30 tiles) | 558.7 s (1 tile, no spills) | 2871.6 s (volumes behind PCIe) |
| RX 6750 XT, Linux | 744.1 s (20 tiles) | 749.9 s (1 tile, no spills, after two bridge fixes) | 4035 s |
Capped runs are bit-identical to uncapped on Windows (41 / 41, twice); on Linux 40 / 41, the odd
view differing by 110 pixels at 1e-6. The series produced two bridge fixes (a 4 MB floor under which
nothing spills, and an image reserve net of the images already resident: the Linux 2 GB cap went
from 863 s with 6047 spills to 749.9 s with none) and a hard floor: at 1 GB the SGM volumes
themselves live behind the link and the job runs 4-5x slower, which is the price of running where
upstream refuses. Details in docs/02-memory-bridge.md.
- HIP drops
surf2Dwritestores into 16-bit float arrays, on Windows and Linux alike; reads are fine. AliceVision's mip chain is built through a buffer copy instead. - Linux ROCm 7.2 has no
hipMallocMipmappedArrayon RDNA1 (and WSL2 has no textures at all). Mipmaps are emulated in the compat layer as one texture per level, in array or pitched linear memory: bit-identical to native mipmaps on the RX 9070 and on the RX 6750 XT (41 / 41 views), so the Windows build keeps native and the Linux bundle emulates on every architecture. Emulation used to cost 21 % (RX 9070, 6 views: 19.9 s vs 16.5 s) and 22 % (RX 6750 XT, 41 views: 232.3 s vs 190.3 s), which was the whole Linux-vs-Windows gap on that card. The disassembly showed why: the per-level texture handles lived in a device-side table, and the compiler could not prove the table index wave-uniform, so every sample fetched its descriptor with vector loads and tenv_readfirstlanes. The level descriptors are now packed into fixed-stride constant-memory slots addressed by arithmetic, with a run-time uniformity check that takes one scalar descriptor load on the common path (the per-pixel levels ofuseConsistentScalefall back to an out-of-line waterfall). Emulation now costs 4 % on the RX 9070 (17.2 s) and 9 % on the RX 6750 XT (208.1 s), still bit-identical; the v0.2.3 Linux bundle takes the RX 5500 XT from 447.7 s to 396.9 s and the RX 6750 XT from 226.1 s to 205.7 s on the 41-view set, byte-identical output. The bit-identity is expected rather than remarkable: AliceVision samples its mip chain only at integer levels (level = log2(scale / minDownscale)), where trilinear filtering reduces to bilinear on one level. Linear levels are what lets the bridge account for camera images. - GPU atomics into mapped host memory are silently wrong on Linux unless the memory is
allocated non-coherent: on an RX 6750 XT,
atomicMininto default (fine-grained) host memory fails on every element whileatomicAddworks, and both are right onhipHostMallocNonCoherentmemory (hip/tests/host_atomics.hip; Windows passes all cases). Anything that spills a buffer kernels do atomics on, LLM runtimes included, needs to know: standalone write-up in docs/notes/gpu-host-memory-on-rocm.md. - Fine-grained host memory is the wrong tier for texture-sampled data: with the default mapping, 186 MB of camera images behind PCIe cost 22x while 6 GB of streamed volumes cost 4.7x; allocated coarse-grained, the device caches them and the images cost nothing.
- ROCm for Windows installs as pip wheels, no admin installer. CMake refuses to mix
cl.exewith clang for HIP: useclang-clfor both.-fgpu-rdcis broken on Windows, so the device code is compiled as one unity translation unit. - A relocatable bundle needs things the dependency walkers never see: HIP loads the
code-object manager (
libamd_comgr/amd_comgr0702.dll) at runtime on both OSes, and without it the GPU simply "does not exist". On Linux a WSL build box also ships the/dev/dxgflavour oflibhsa-runtime64, which must be swapped for the KFD one. With those in place RDNA1 works natively on ROCm 7.2.
If you just want to use it, USING.md is the guide - install, check it is really on the GPU, what the error codes mean, and the dozen settings worth knowing. The rest of this file is what was built and why.
Binaries are on the v0.3.3 release; the data sets and references are on v0.1.0 and unchanged, the depth maps being bit-identical between the two:
| asset | size | contents |
|---|---|---|
cheshire-alicevision-windows-x64.zip (v0.3.3) |
184 MB | one Windows package for every AMD card. Eleven GPU payloads across both HIP runtimes - gfx1010/1012 (RX 5500-5700), gfx1030/1031/1032/1034 (RX 6000), gfx1033/1035/1036 (RDNA2 APUs), and gfx11-generic + gfx12-generic for RDNA3/RDNA4 and future chips in those families. cheshire-detect.exe asks the card which it needs, so there is nothing to choose; cheshire-run.cmd runs a node straight from it, meshroom-pair.cmd + launcher pair it into Meshroom |
cheshire-alicevision-cuda-windows-x64-cuda12.9.zip (v0.3.3) |
104 MB | the same stages for NVIDIA, Windows. GPU SIFT, matcher, depth map filter, meshing votes and texturing, plus the memory bridge; cudart is bundled, so no CUDA toolkit is needed - only the driver. Pair it with CHESHIRE_BACKEND=cheshire, or the launcher sees an NVIDIA card and hands every node back to Meshroom |
cheshire-alicevision-hip-linux-x64-rocm7.2.tar.gz (v0.3.3) |
124 MB | relocatable Linux bundle with the GPU matcher, depth map filter, meshing votes, texturing and the packed-slot mipmap sampler, depth-map code objects for RDNA1-RDNA4 discrete parts, the RDNA2/RDNA3 APUs (gfx1035/1036/1103/1150/1151/1152/1153) and Vega (gfx900/906, untested) - 19 in all; GPU SIFT covers 15 of those, not the RDNA2 APUs or Vega, which fall back to CPU SIFT; needs only amdgpu + /dev/kfd; validated on the RX 6750 XT (and the RX 5500 XT in earlier releases) |
cheshire-alicevision-cuda-linux-x64-cuda12.9.tar.gz (v0.3.3) |
48 MB | the same stages for NVIDIA, Linux. libcudart bundled and no libcuda (the driver's, not ours); the 41-view depth and similarity maps from a clean extraction of this tarball (82 files) are byte-identical to the Meshroom CUDA 11.3 reference on a GTX 1080 Ti |
monstree-mini6-meshroom-cache.tar.gz (v0.1.0) |
383 MB | 6-view Meshroom 2023.3 cache: CameraInit, SfM, PrepareDenseScene and the CUDA DepthMap reference |
monstree-full-cuda-reference.tar.gz (v0.1.0) |
680 MB | 41-view SfM + CUDA DepthMap reference (GTX 1080 Ti) |
cheshire-hip-depthmap-outputs.tar.gz (v0.1.0) |
966 MB | the HIP depth maps behind the table above (RX 9070 6 + 41 views, RX 5500 XT, RX 6750 XT) |
All four packages in v0.3.3 are rebuilt from one source, and all four passed the full gates on their own hardware (docs/04): StructureFromMotion is paired as an eighth node, with the fix for the incremental SfM crash of Meshroom issue #2344; the texture downscale runs on the GPU, exact against OpenImageIO; and the Windows packages no longer carry SuiteSparse. What changed, in full: docs/releases/0.3.3.md.
There is nothing to pick any more. Until v0.2.17 there were six Windows downloads and choosing
the wrong one did not fail: a package built for one chip enumerates another, runs every kernel and
returns wrong data with no error at all. The bundle carries every payload and
cheshire-detect.exe asks the card which one it needs - the arch name the runtime reports, never a
table of PCI IDs, because a table needs an entry for hardware that does not exist yet. Run
cheshire-run.cmd --which to see what it chose (docs/16).
Three of the eleven targets have been run on a real card - gfx1012, gfx1031 and
gfx12-generic. The rest are listed in gpu/<family>/UNTESTED and say so on every run that
selects them; four are the same architecture as a tested sibling (gfx1010, gfx1030, gfx1032,
gfx1034) and four are hardware nobody here owns (the three APUs and gfx11-generic). The APUs are
the row to watch: they are unified-memory parts, and the memory bridge was designed and measured
against a discrete card behind PCIe. If you run one, please say how it went.
The package carries every DLL it needs, the MSVC and OpenMP runtimes included (until 2026-09-15 it
did not, and a machine without Visual Studio died with exit code 0xC0000135 and no message). The
RDNA1/RDNA2 half is built /arch:AVX and runs on anything from Sandy Bridge and Bulldozer onwards;
the RDNA3/RDNA4 half is AVX2, so a pre-2013 CPU with a new card dies with 0xC000001D (illegal
instruction). The RDNA3/RDNA4 half also needs Adrenalin 26.2.2 or newer, since it carries the
HIP 7.2 runtime and a 2025 driver answers hipErrorNoDevice, shown by the tools as "No
CUDA-Enabled GPU" even though the card is fine. The RDNA1/RDNA2 half uses the runtime the driver
ships and has no such floor - and the probe tries them in that order, so a card the HIP 7 runtime
refuses simply falls through to the one that works.
Reproduce a row: unpack a cache under data/ref/<dataset>/, then scripts\run-depthmap.cmd <dataset>
(Windows, set CHESHIRE_INSTALL to the unzipped folder) or scripts/linux/run-depthmap.sh (Linux).
Both run the exact Meshroom 2023.3 DepthMap command line and finish with scripts/compare_depthmaps.py,
which prints the per-view table and writes the side-by-side panels. Photos are
alicevision/dataset_monstree.
Meshroom runs its DepthMap node as aliceVision_depthMapEstimation from its own aliceVision\bin.
meshroom-pair.cmd (in every Windows zip from v0.3.1 - the v0.3.0 CUDA zip shipped without it, take
it from this repository - and in
cheshire-meshroom-pair-windows.zip for
the older zips) replaces that one binary with a launcher and keeps the CUDA one beside it as
.cuda.exe:
meshroom-pair.cmd C:\Meshroom-2023.3.0 C:\cheshire-alicevision-windows-x64
meshroom-pair.cmd C:\Meshroom-2023.3.0 --unpair
The launcher decides per run: an NVIDIA card present (nvidia-smi answers) runs Meshroom's own
binary, otherwise the build from the Cheshire package, with the package's DLLs and share/ in front
so nothing of Meshroom's older AliceVision leaks in. CHESHIRE_BACKEND=auto|cheshire|meshroom
forces one, and CHESHIRE_DEPTHMAP=cuda|hip is the older spelling of the same switch, still
honoured. The choice is printed as the first line of the node's log. The nodes Cheshire does not
carry - CameraInit, ImageMatching, StructureFromMotion, MeshFiltering, Publish - keep running from
Meshroom.
Pairing a Cheshire CUDA package is the one case where the automatic choice is wrong: the card is
NVIDIA, so auto hands every node back to Meshroom and the package never runs. Set
CHESHIRE_BACKEND=cheshire for that.
Given a bundle it also picks the payload for the card and prints that too
(bundle payload hip6.2/gfx1031), composing the layered PATH per run. A flat single-chip package
from an older release still works exactly as before - the shape is detected, not configured.
v0.2.17 and v0.2.18: the copy of
meshroom-pair.cmdinside those zips cannot pair the bundle. It looks for<package>\bin\aliceVision_depthMapEstimation.exe, which only a flat package has, and stops with "no HIP aliceVision_depthMapEstimation.exe". The launcher beside it has always understood both shapes; only the pairing script had not. v0.3.0 carries the fix; on an older zip, dropscripts/windows/meshroom-pair.cmdfrom this repository in beside the launcher.
From v0.2.13 the pairing also covers FeatureExtraction, so GPU SIFT reaches the graph: leave
forceCpuExtraction unticked (docs/14). With an older package, or one
for a card the HIP runtime cannot see, the node falls back to CPU SIFT without saying so — a
paired node that still logs [cpu] means the runtime cannot see the card, not that pairing
failed. If you are on a pre-v0.2.13 package, keep the option ticked. The same pairing on Linux is
scripts/linux/meshroom-pair.sh.
Every package carries RDNA3 code objects (gfx1100/1101/1102, plus the APUs gfx1103/1150/1151/1152/1153) and none has run on RDNA3 hardware; there is no such chip here. Ten minutes on an RX 7600/7700/7800/7900, or a Ryzen 7040/8040/AI 300 laptop, closes the gap (Windows: the RDNA3+RDNA4 zip; if it exits without a message, the exit code says why: 0xC0000135 means a DLL is missing, 0xC000001D means the CPU lacks AVX2; "No CUDA-Enabled GPU" with the card present means the Adrenalin driver predates 26.2.2):
- Download the RDNA3+RDNA4 Windows zip (or the Linux bundle) and
monstree-mini6-meshroom-cache.tar.gzfrom the release pages above. - Unpack the cache under
data/ref/monstree-mini6/, unzip the package anywhere, thenset CHESHIRE_INSTALL=<unzipped folder>andscripts\run-depthmap.cmd monstree-mini6(Linux:scripts/linux/run-depthmap.sh <bundle> <cache> <out> <cache>/DepthMap/<id>). - It prints the card name,
Task done in (s), and a per-view table against the CUDA reference;compare_stats.mdlands next to the depth maps. Open an issue with those two things and the card model. A[cheshire] bridge summaryline appears withCHESHIRE_BRIDGE_LOG=1if anything spilled.
Expected: mask agreement 1.000, median relative depth error 0.0000, 97-99 % of pixels within 1 % on every view, and a time somewhere between the RX 6750 XT and the RX 9070 rows.
The current plan is docs/roadmap.md, rebuilt on 2026-09-22 from every open item in the docs, the validation log and the code, each checked against the tree. The numbered list below is how the project got here; parts of it are out of date, and docs/roadmap.md lists which.
The aim is a small PC with a big GPU that runs a Meshroom job as fast as a workstation and just as precisely. In order, each timed on the 41-view and engine bay sets and checked against the CPU output - bit-identity where the same computation moved to another device, and equivalence at the reconstruction where it could not (SIFT is a different descriptor implementation, not the same kernels elsewhere):
DepthMapFilter on the GPU.Done in v0.2.6 (docs/08): 123.5 s to 26.5 s on the engine bay job, what remains is EXR reading; a shared decoded-map cache is the follow-up.Meshing's voting pass.Done in v0.2.7 (docs/09): both ray-marching passes on the GPU, 37 s to 18 s on the engine bay job. It was not the majority of Meshing: the profile said the Boykov-Kolmogorov max-flow (144 s) and building its graph (63 s) were, followed by the dense point cloud (74 s), the neighbour tables (47 s) and the tetrahedralisation (41 s). The graph build, the tables and the kd-tree were then fixed on the CPU, exactly (docs/11), in v0.2.10 the max-flow itself moved to the GPU (docs/12): 109 s to 5.3 s, the same labelling, and the two visibility passes followed (docs/13): 56 s to 13 s, nanoflann's own tree walked on the device. The fusion went last, in v0.2.16, and again the name was misleading: "Load depth maps and add points" spent 60 % of its time in a gaussian blur, not loading anything - 108.7 of 180.8 thread-seconds against 65.5 for all three EXR reads. On the device it is 17.69 s to 3.06 s, reproducing OpenImageIO rather than approximating it, which needed the edge policy, the accumulation order and FMA contraction all matched (HIP defines__fmul_rnand__fadd_rnas the plain operators, so the compiler contracts them right back unless told not to). Meshing is 101 s; what is left is the tetrahedralisation.Texturing.Done in v0.2.8 (docs/10): the per-camera Laplacian pyramid and rasterisation on the GPU, the camera selection parallel, images read ahead; 165.8 s to 79.3 s on the engine bay job. The UV unwrap followed in v0.2.16, on the CPU and byte-identical:ChartRect::insertwalked the whole tree for each of 69,565 charts because occupied leaves returnnullptrbut are still visited (6.28 s to 0.66 s, each node now carrying the largest free extent beneath it), andpackChartsbuilt 7,029,609 single-elementstd::vectors where one contiguous array of 12-byte PODs does (3.31 s to 1.90 s). Together 14.41 s to 8.56 s, and 51.9 s to 16.8 s on a 2011 Bulldozer - seven million allocations and an O(n^2) pointer chase punish a slow memory subsystem hardest. What is left is mesh load and save, padding and the Lanczos downscale (OpenImageIO'ssinf-based filter, not reproducible bit for bit on a GPU).FeatureExtraction (SIFT) on the GPU.Done in v0.2.13 (docs/14): PopSift built as HIP, 2021 s to 26 s on the 41-view set. This one is not bit-identical to the CPU and cannot be - it is a different descriptor implementation, not the same kernels on another device - so the test is the reconstruction: 88,463 landmarks against the CPU path's 78,372 at a lower reprojection error, and within 0.11 % of each other across RDNA1, RDNA2 and RDNA4 on two runtimes. Found an upstream bug on the way (popsift#193).Geometric verification on the GPU.Done in v0.2.15, on the CPU, and the profile is the whole story (docs/15). AC-RANSAC was 566 s of FeatureMatching's 644 s, so it looked like the next port. Measuring first put 48 % of it in onestd::sort- of(residual, index)pairs whose indices are read on 0.026 % of sorts - and only 8 % in the per-element virtual dispatch a code read had blamed. Sorting 8-byte keys with a radix on the double's bit pattern, and going straight to the error functor, gives 566 s -> 351 s with a byte-identical match file and identical iteration, sort and improved-model counts. The port is not planned: iterations are not independent (the sampling pool becomes the current inliers once a meaningful model appears), so the only parallelism is across pairs, and 111.7 M sequential iterations against a per-workgroup budget of about a microsecond make 2-3x a lot of device code for less than two functions bought.- The SVD in
Nullspace2, about 137 s of FeatureMatching and the largest single item left anywhere. Eigen'sJacobiSVDis solving a 9x9 for the fundamental matrix's null space when only the last two singular vectors are wanted. There is no free lunch in it: the two changes that would be byte-identical are already upstream, sinceNullspace/Nullspace2ask forComputeFullValone and the minimal solver already hands them a fixed-sizeMat9. Every cheaper route left (a fixed-iteration Jacobi,LDLTon the normal equations, inverse iteration) gives a different vector within rounding, so unlike everything above this one cannot be byte-identical and has to be argued on reconstruction quality instead. That is why it is the first item that would be a minor bump rather than a patch, and it is held until the quality measurement exists. One Windows packageDone in v0.2.17 (docs/16): eleven payloads across both HIP runtimes in 181 MB, against 3,520 MB as one package per target, because only five DLLs carry GPU code - 4.7 MB per chip - and the vcpkg runtime andshare/tree are bit-identical across toolchains. It closed the gfx1010, gfx1034 and APU gaps, and RDNA3/RDNA4 collapsed togfx11-generic+gfx12-generic, which cover future chips in those families; RDNA1/RDNA2 cannot follow, because HIP SDK 6.2 emits generics only under a code-object version clang calls not production-ready. The probe runs each runtime family in its own process: loading a HIP runtime the installed driver does not support can exit 0xC0000005 rather than report no device, and only in a non-interactive session, which is how Meshroom runs a node. Still to do: x86-64 tiers (v1 / AVX / v3) for the Linux bundle, which is SSE2-only today, and the same generic collapse there - 19 hand-listed code objects become four, though Vega must stay explicit since it is wave64 where all of RDNA is wave32.- A CUDA build of the same tree so NVIDIA users get the GPU matcher too. The Linux build is done and validated byte-identical to the reference (docs/18); what remains is PopSIFT and the memory bridge on CUDA, the Windows build (needs VS 2022 Build Tools for CUDA 12.9's MSVC 193x ceiling), packaging, and a Linux bundle built against an older glibc for Ubuntu 22.04 / Debian 12 nodes.
- Hardware still unrun: RDNA3 discrete, the RDNA3.5 APUs, Vega, and a card without the 4-byte dot instruction (RX 5700, original Vega) for the matcher's fallback path.
Not planned: SfM on the GPU (incremental and sequential; Ceres's GPU solvers are CUDA-only and the outer loop dominates anyway), and the tetrahedralisation, which would mean replacing geogram - that is also what stands between this and a byte-reproducible mesh, since geogram renumbers its cells from byte-identical input on every run.
| Path | What |
|---|---|
hip/compat/include/cheshire/ |
the CUDA -> HIP compatibility header, the memory bridge, the mipmap emulation |
hip/port/ |
fused SGM aggregation and the other upstreamable source changes, applied by scripts/apply_hip_patch.py |
hip/tests/ |
standalone HIP probes: textures, surfaces, mipmaps, bandwidth, allocation, launch overhead, the bridge |
scripts/ |
Windows build (build-alicevision.cmd), Linux superbuild and bundle (scripts/linux/), validation runner, comparison tool |
docs/ |
findings, toolchain notes, bridge design, port log, validation, performance, Linux build |
third_party/aliceVision |
upstream AliceVision, pinned as a submodule and left untouched; the HIP backend is a patch set |
third_party/popsift |
upstream PopSift, cloned rather than vendored and patched by scripts/apply_popsift_patch.py; built as HIP by scripts/build-popsift.cmd (scripts/linux/build-popsift.sh) |
- Windows (RDNA3/RDNA4): docs/01-toolchain-windows.md, then
scripts\build-alicevision.cmd gfx1201 install. - Linux (RDNA1-RDNA4): docs/06-linux-build.md, then
scripts/linux/build-deps.shandscripts/linux/build-alicevision.sh bundle. - Validate the numbers:
scripts\run-depthmap.cmd <dataset>orscripts/linux/run-depthmap.shagainst a Meshroom cache;scripts/compare_depthmaps.pyproduces the numbers and panels. - Validate the package, which is a different question and is checked separately, because a package
can be numerically perfect in six stages and broken in the seventh:
scripts/verify_bundle_stages.py <bundle> <cache>(HIP) andscripts\windows\verify-cuda-stages.ps1(CUDA) run every GPU-bearing stage from the package and require each one to print its own port's line, not merely to produce files.scripts/verify_end_to_end.py <meshroom> <package> <photos> <out>pairs the package into Meshroom and runs whole pipelines under several parameter sets, including one with everyCHESHIRE_GPU_*switch off, which has to fall back to the CPU and still finish.
Works end to end on RDNA1, RDNA2 and RDNA4 (Windows and Linux); RDNA3 has its code object in every bundle but no hardware run yet. GCN 4 (RX 400/500) is out: the ROCm 7.2 runtime refuses to initialise on an RX 570 even though the kernel driver accepts it (docs/06). From v0.3.0 the same stages are built for CUDA as well, on both systems.
A whole Meshroom 2023.3 graph runs on the package with all seven Cheshire nodes paired -
PrepareDenseScene, FeatureExtraction, FeatureMatching, DepthMap, DepthMapFilter, Meshing and
Texturing. On Windows that is checked by scripts/verify_end_to_end.py, which requires each node
to print its own port's line rather than merely to produce a mesh, since a pipeline that fell back
to Meshroom's own binaries would produce a perfectly good one. On 2026-09-20 (docs/04):
- the v0.3.0 Windows AMD bundle passed the stage gate 6/6 and five whole pipelines 5/5 on an
RX 9070, 6/6 ports each - including one with every
CHESHIRE_GPU_*switch off, which fell back to the CPU and still produced a textured mesh; - the Windows CUDA package passed the same two gates on a GTX 1050 Ti, 6/6 and 5/5;
- the Linux CUDA package, extracted from the release tarball rather than run from a build tree, produced a depth map byte-identical to the Meshroom CUDA 11.3 reference on a GTX 1080 Ti.
The same gate runs on Linux, and on 2026-09-20 the shipped v0.3.0 Linux HIP bundle passed all five
parameter sets on an RX 6750 XT (house-pc), 6/6 ports each - the first time the Linux AMD artifact
has been checked as a whole pipeline rather than stage by stage. Building that gate immediately
found that the Linux CUDA bundle never pairs GPU SIFT: it ships libpopsift.so.0.10.0 where the
pairing script looked for libpopsift.so, and the gate's own first pass missed it because
Meshroom's own featureExtraction is also a CUDA PopSIFT and prints the identical line. The gate now
requires each node to prove which binary ran before it looks at any port. What comes next is the
roadmap above.
Primary repository: git.hausofdub.com/dspl1236/Cheshire; mirror: github.com/dspl1236/Cheshire. Licensed MPL-2.0.