Files
Jungfraujoch/image_analysis/beam_stop/ShadowAccumulatorGPU.h
T
leonarski_f a395f358ef
Build Packages / Create release (push) Successful in 17s
Build Packages / build:viewer:macos-arm64:nocuda (push) Successful in 3m22s
Build Packages / build:rugnux:macos-arm64:nocuda (push) Successful in 2m37s
Build Packages / build:rugnux:linux-aarch64:cuda (push) Successful in 9m33s
Build Packages / build:rugnux:linux-x86_64:cuda (push) Successful in 10m39s
Build Packages / build:viewer:linux-x86_64:nocuda (push) Successful in 11m4s
Build Packages / build:viewer:linux-x86_64:cuda (push) Successful in 13m19s
Build Packages / build:jfjoch:rocky8:nocuda (push) Successful in 17m37s
Build Packages / build:jfjoch:rocky9:nocuda (push) Successful in 18m49s
Build Packages / build:viewer:windows-x86_64:nocuda (push) Successful in 19m10s
Build Packages / build:viewer:windows-x86_64:cuda (push) Successful in 24m26s
Build Packages / HDF5 consumer tests (DIALS, XDS) (push) Successful in 25m31s
Build Packages / build:jfjoch:ubuntu2404:nocuda (push) Successful in 18m54s
Build Packages / build:jfjoch:ubuntu2204:nocuda (push) Successful in 20m45s
Build Packages / Generate python client (push) Successful in 37s
Build Packages / build:jfjoch:rocky8:cuda-sls9 (push) Successful in 20m20s
Build Packages / Build documentation (push) Successful in 1m32s
Build Packages / build:rugnux:windows-x86_64:cuda (push) Successful in 14m37s
Build Packages / build:jfjoch:rocky9:cuda-sls9 (push) Successful in 21m6s
Build Packages / build:jfjoch:rocky8:cuda (push) Successful in 19m49s
Build Packages / build:jfjoch:rocky9:cuda (push) Successful in 20m29s
Build Packages / build:jfjoch:ubuntu2204:cuda (push) Successful in 17m2s
Build Packages / build:jfjoch:ubuntu2404:cuda (push) Successful in 14m27s
Build Packages / Unit tests (push) Successful in 1h18m12s
1.0.0-rc.174 (#84)
* Rugnux: Performance improvements on GPU and CPU (more of the pre-scan and of scaling on the GPU, faster CPU spot finding and crystal refinement), with unchanged results.
* Rugnux: More robust processing - patches of persistently hot pixels are masked, an inconsistent merge triggers a retry at the measured beam centre, and builds targeting different CPU levels give the same results.
* Rugnux: Improved scaling and merging - reflections with an overloaded pixel are dropped, as in XDS, sparse rotation sweeps are scaled more reliably, and French-Wilson amplitudes use an anisotropic Wilson prior.
* Rugnux: Improved space-group determination - glide planes in groups without a centre of symmetry, screw axes from short or weak axial rows kept when a higher group is adopted, and more reliable decisions on twinned and pseudo-symmetric crystals.
* Rugnux: Improved small-molecule processing - spots that grow wider than the integration disk and split spots are integrated over their measured footprint, sparse lattices are integrated on every frame, and the `.hkl` file holds unmerged scaled reflections (SHELX HKLF 4).
* Rugnux: Reads Rigaku d*TREK SMV images (Saturn CCD), including detector 2theta and encoded pixel overflows; home-source (rotating-anode) datasets were added to the validation battery.
* jfjoch_viewer: Fixed processing failing at the end with "Wrong JPEG library version" on Linux; the merge window shows the space group with proper subscripts and a checklist of crystal pathologies.

Reviewed-on: #84
Co-authored-by: Filip Leonarski <filip.leonarski@psi.ch>
2026-10-06 14:03:18 +02:00

73 lines
3.3 KiB
C++

// SPDX-FileCopyrightText: 2026 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
// SPDX-License-Identifier: GPL-3.0-only
#pragma once
#include <cstdint>
#include <memory>
#include <vector>
#include "../../common/CompressedImage.h"
#include "../image_preprocessing/BSLZ4DecoderGPU.h"
#include "../indexing/CUDAMemHelpers.h"
#include "ShadowMaskGPU.h"
// The beam-stop projection accumulated on the device: only the compressed chunk crosses PCIe, and
// both the decode and the per-pixel maximum / sum / count run on the GPU. The projection comes back
// once, when the mask is read.
//
// This accumulates the RAW STORED COUNTS, with the pixel type's sentinel skipped, exactly as the
// host ShadowFinder does - NOT the preprocessed image. The two are not the same thing and the
// shadow is defined on the raw one. Sums and counts are integers, so the device result is identical
// to the host's rather than merely close.
//
// Only bitshuffle+LZ4 images are handled; anything else is left to the host path.
class ShadowAccumulatorGPU {
const size_t npixels;
std::shared_ptr<CudaStream> stream;
std::unique_ptr<BSLZ4DecoderGPU> decoder;
// Frames are decoded into a batch and folded into the projection together. The fold reads and
// writes 20 bytes per pixel whatever the batch holds, so doing it once per frame would spend
// most of the bandwidth on the accumulator rather than on the data. Four is where that stops
// mattering - the fold is then a few percent of the phase - and every frame beyond it is a
// further full frame of device memory, which costs more in cudaMalloc (device-synchronizing)
// than it saves.
static constexpr int BATCH = 4;
CudaDevicePtr<uint8_t> raw; // BATCH decoded frames, still in their stored type
CudaDevicePtr<int64_t> gpu_max;
CudaDevicePtr<int64_t> gpu_sum;
CudaDevicePtr<uint32_t> gpu_count;
size_t frame_bytes = 0; // stride of one frame within `raw`
size_t raw_capacity = 0; // bytes actually allocated for `raw`
int pending = 0; // frames decoded but not yet folded in
CompressedImageMode pending_mode = CompressedImageMode::Uint32;
uint32_t frames = 0;
int blocks = 0;
void EnsureRawCapacity(size_t bytes_per_frame);
void FoldPending();
public:
explicit ShadowAccumulatorGPU(size_t npixels);
// True when this image can be decoded and accumulated on the device.
static bool Supports(const CompressedImage &image);
// Decode and accumulate one frame. Throws if the image is not one this can take (ask Supports
// first) or if the chunk does not decode.
void Add(const CompressedImage &image);
[[nodiscard]] uint32_t GetFrameCount() const { return frames; }
// The beam-stop mask and the mean projection, made where the projection is (ShadowMaskGPU.h), so
// that it does not have to come back at all.
std::vector<uint32_t> Mask(const ShadowMaskSetup &setup, const std::vector<uint32_t> &pixel_mask);
std::vector<float> MeanProjection(const std::vector<uint32_t> &pixel_mask);
// Bring the projection back to the host, folding in whatever the last batch still holds. Cheap
// to call once; it moves 20 bytes per pixel.
void Download(std::vector<int64_t> &max_value, std::vector<int64_t> &sum_value,
std::vector<uint32_t> &valid_count);
};