marCCD frames are widened on the device

The marCCD reader read each frame's 16-bit scanlines into scratch and widened them to int32 on the
worker thread (marccd::ReadInto, ~20 % of the image-loop worker CPU on 4nwv), and the 4-byte image was
uploaded. ReadRawImage now reads the scanlines straight into the image's buffer and hands them over as
a new read-only CompressionAlgorithm::UINT16_WIDENED (mode Int32): two bytes a pixel cross PCIe, from
page-locked memory, and the preprocessing kernel widens them itself.

It is not CompressedImageMode::Uint16, which calls 65535 an error pixel; a marCCD's 65535 is its
saturation marker. So the kernel compares each pixel with int32's error marker and the int32 saturation
cap - the comparisons are made on the pixel's value, in int64, rather than in the storage type - and
decides every pixel exactly as for the int32 image the host would have widened it to. The host widening
(JFJochDecompressPtr) is the CPU path.

Test: ImagePreprocessorGPU_StoredEncodingsMatchCPU, with a frame carrying 65535 at a saturation of
65535, against the CPU preprocessor on the host-widened image.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EizBKhTqrAYago9KJG3dA3
This commit is contained in:
2026-10-12 22:37:47 +02:00
co-authored by Claude Opus 5.5
parent f4252ad39d
commit dfd69fcb91
10 changed files with 94 additions and 30 deletions
+12 -1
View File
@@ -203,7 +203,18 @@ bool JFJochMarCCDReader::HasImage(int64_t image_number) const {
bool JFJochMarCCDReader::ReadRawImage(int64_t image_number, JFJochReaderRawImage &image) {
if (!HasImage(image_number))
return false;
image.image = DecodeInto(image_number, image.image_buffer, image.read_buffer);
// The pixels as the file stores them, two bytes each: the consumer widens them, on the device where
// there is one, so the host neither widens nor uploads a four-byte image.
const size_t npixel = static_cast<size_t>(header0_.nx) * static_cast<size_t>(header0_.ny);
image.image_buffer.resize(npixel * sizeof(uint16_t));
const auto h = marccd::ReadRaw(files_[image_number], reinterpret_cast<uint16_t *>(image.image_buffer.data()),
npixel);
if (h.nx != header0_.nx || h.ny != header0_.ny)
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
"marCCD image size differs from the first image of the sweep");
image.image = CompressedImage(image.image_buffer.data(), image.image_buffer.size(),
static_cast<size_t>(header0_.nx), static_cast<size_t>(header0_.ny),
CompressedImageMode::Int32, CompressionAlgorithm::UINT16_WIDENED);
return true;
}