M9: TailwindCSS + shadcn/ui frontend redesign
M10: Dynamic plugin system with shared library loading
M11: Macro recording, playback, and batch processing
M12: 3D volume visualization, marching cubes, STL/OBJ export, rivol:// protocol
M13: Additional GPU shaders, enhanced auto-threshold, merge channels
M14: Binary image processing (EDT, watershed, skeleton, connected components,
voronoi), segmentation & analysis (particles, colocalization, find maxima),
math/noise/filter/transform ops (60+ total), scientific LUTs (Fire, Ice,
Spectrum, Jet, Phase, HiLo + .lut file I/O), Apple Metal GPU optimization
(metal-only feature, Apple Silicon detection, lower dispatch threshold),
native Metal compute crate (ri-metal with MSL shaders via objc2-metal),
7 new WGSL GPU shaders (bilateral, variance, mean, math_ops, outline,
minmax_filter, affine transform). 25 crates, 162 tests, 75+ IPC commands.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
46 lines
1.6 KiB
Metal
46 lines
1.6 KiB
Metal
#include <metal_stdlib>
|
|
using namespace metal;
|
|
|
|
/// Per-threadgroup histogram using shared memory atomics, then reduced to global.
|
|
/// Input: buffer of f32 pixel values (expected range [0, 1]).
|
|
/// Output: buffer of 256 uint32 bins.
|
|
|
|
struct HistogramParams {
|
|
uint pixel_count;
|
|
uint _pad0;
|
|
uint _pad1;
|
|
uint _pad2;
|
|
};
|
|
|
|
kernel void histogram_compute(
|
|
device const float* input [[buffer(0)]],
|
|
device atomic_uint* output [[buffer(1)]],
|
|
constant HistogramParams& params [[buffer(2)]],
|
|
threadgroup atomic_uint* local_hist [[threadgroup(0)]],
|
|
uint tid [[thread_index_in_threadgroup]],
|
|
uint tg_size [[threads_per_threadgroup]],
|
|
uint gid [[thread_position_in_grid]]
|
|
) {
|
|
// Initialize threadgroup histogram bins to zero
|
|
for (uint i = tid; i < 256; i += tg_size) {
|
|
atomic_store_explicit(&local_hist[i], 0, memory_order_relaxed);
|
|
}
|
|
threadgroup_barrier(mem_flags::mem_threadgroup);
|
|
|
|
// Each thread accumulates its pixel into the local histogram
|
|
if (gid < params.pixel_count) {
|
|
float val = input[gid];
|
|
uint bin = (uint)clamp(val * 255.0f, 0.0f, 255.0f);
|
|
atomic_fetch_add_explicit(&local_hist[bin], 1, memory_order_relaxed);
|
|
}
|
|
threadgroup_barrier(mem_flags::mem_threadgroup);
|
|
|
|
// First threads in group flush local histogram to global
|
|
for (uint i = tid; i < 256; i += tg_size) {
|
|
uint count = atomic_load_explicit(&local_hist[i], memory_order_relaxed);
|
|
if (count > 0) {
|
|
atomic_fetch_add_explicit(&output[i], count, memory_order_relaxed);
|
|
}
|
|
}
|
|
}
|