diff --git a/image_analysis/scale_merge/RotationScaleMerge.cpp b/image_analysis/scale_merge/RotationScaleMerge.cpp index 87b44d2e0..117f120a9 100644 --- a/image_analysis/scale_merge/RotationScaleMerge.cpp +++ b/image_analysis/scale_merge/RotationScaleMerge.cpp @@ -7592,7 +7592,9 @@ RotationScaleMerge::Result RotationScaleMerge::RunArm(bool scale_partials, bool BuildFullsGroupCSR(n_groups); // --- 4. Scale the fulls (XDS order, Unity model). --- - if (scale_fulls && !scaled_fulls_on_gpu) { + // On the fulls the host combined: the per-frame CSR this loop slices is built by Combine(). Fulls + // combined on the GPU were scaled there - unless there were none, and then there is nothing to scale. + if (scale_fulls && !combined_on_gpu) { // On a working copy of the fulls, as the partials' loop (ScalingRef and ScalingFit in the header): // full_index[q] is the full at position q, in the group CSR's order and then the fulls in no group. const int nf = static_cast(fulls.size()); diff --git a/tests/RotationScaleMergeSegmentsTest.cpp b/tests/RotationScaleMergeSegmentsTest.cpp index f20d05f49..92fa4325e 100644 --- a/tests/RotationScaleMergeSegmentsTest.cpp +++ b/tests/RotationScaleMergeSegmentsTest.cpp @@ -229,3 +229,18 @@ TEST_CASE("RotationScaleMergeSegments_NoSegmentsIsTheEngineAsBefore", "[Rotation else if (get_gpu_description().find("RTX 5080") != std::string::npos) CHECK(hash == 6594155396455850264ULL); } + +// A sweep in which no rocking event is captured whole enough to make a full - as in one exposed 0.2 deg out +// of every 0.6 deg, where each reflection is caught on one frame and only in part. On the GPU the combine +// returned no fulls, the scale-fulls step then fell through to the host loop, which slices a per-frame CSR +// that only the host combine builds, and the process died on SIGSEGV instead of reporting the empty merge. +TEST_CASE("RotationScaleMerge_NoFullsIsAnErrorNotACrash", "[RotationScale]") { + const std::vector sweeps{{60, 1.0, 0.0}}; + auto data = MakeSweeps(sweeps, 17); + auto x = SyntheticExperiment(false); + // More than any rocking event can capture: the combine makes no full. + ScalingSettings s = x.GetScalingSettings(); + s.MinCapturedFraction(1000.0); + x.ImportScalingSettings(s); + CHECK_THROWS_AS(Merge(data, x, {}), JFJochException); +}