Metal: a frame with a handed-over radial curve in use is integrated by the CPU engine

With a curve from the whole frame (RadialBackgroundFromRings) the ring's high-side clip follows it
(RadialClipOffset) on the CPU and CUDA; the Metal clip kernel did not, so a Mac run of such a frame
differed from both. It now goes to the CPU engine, which gives that result exactly - the precedent is
the trimmed mean. Frames without a handed-over curve are unchanged.

Not compiled here (no macOS); BraggIntegrationEngineMetal_HandedOverRadialCurveStaysOnCPU checks it on
the Mac CI job.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015CeEGgpKdfkCTJb3YNMJcu
(cherry picked from commit 36be3b8f13dbde2616fe7add3acc3d073ef082af)
This commit is contained in:
leonarski_f committed 2026-10-23 21:21:46 +02:00
1 parent 6940718cf5
commit 3467c21919
2 files changed
+28 -2

No files matched your search

+24
View File
@@ -253,6 +253,30 @@ TEST_CASE("BraggIntegrationEngineMetal_BitIdenticalToCPU", "[portable][Integrati
}
}
// A radial curve handed over from the whole frame (RadialBackgroundFromRings) makes the ring's clip follow
// it, which the clip kernel does not: such a frame is integrated by the CPU engine, so the result is the
// CPU's exactly and no frame goes to the GPU.
TEST_CASE("BraggIntegrationEngineMetal_HandedOverRadialCurveStaysOnCPU", "[portable][Integration][Metal]") {
Case c;
c.radial = true;
const DiffractionExperiment x = MakeExperiment(DetJF(2), c);
const PixelMask mask(x);
BraggIntegrationEngineCPU cpu(x, mask);
BraggIntegrationEngineMetal metal(x, mask);
std::vector<float> rings(400);
for (size_t i = 0; i < rings.size(); ++i)
rings[i] = 1.0f + 0.002f * static_cast<float>(i * i);
cpu.RadialBackgroundFromRings(rings, static_cast<int32_t>(rings.size()), 0.05f, 0.01f);
metal.RadialBackgroundFromRings(rings, static_cast<int32_t>(rings.size()), 0.05f, 0.01f);
ImagePreprocessorBuffer image(x.GetPixelsNum());
const Frame f = RandomFrame(x, 7, {});
std::copy(f.image.begin(), f.image.end(), image.data());
const auto out_cpu = cpu.Run(image, f.predicted, f.predicted.size(), 0);
RequireSame(out_cpu, metal.Run(image, f.predicted, f.predicted.size(), 0));
CHECK(out_cpu.size() > 1000);
CHECK(metal.FramesOnGPU() == 0);
}
// The trimmed-mean background sorts each ring, which only the CPU engine does: such a run keeps the CPU
// engine (MXAnalysisWithoutFPGA asks Available).
TEST_CASE("BraggIntegrationEngineMetal_TrimmedMeanStaysOnCPU", "[portable][Integration][Metal]") {