use metal::MTLSize;
use crate::buffer::MlxBuffer;
use crate::encoder::CommandEncoder;
use crate::error::{MlxError, Result};
use crate::kernel_registry::KernelRegistry;
pub static GPU_SAMPLE_SHADER_SOURCE: &str =
include_str!("../shaders/gpu_sample_argmax_candidates.metal");
pub fn register(registry: &mut KernelRegistry) {
registry.register_source("gpu_sample_argmax_candidates", GPU_SAMPLE_SHADER_SOURCE);
}
#[allow(clippy::too_many_arguments)]
pub fn dispatch_gpu_sample_argmax_candidates(
encoder: &mut CommandEncoder,
registry: &mut KernelRegistry,
device: &metal::DeviceRef,
logits: &MlxBuffer,
out_top1_idx: &MlxBuffer,
out_top1_val: &MlxBuffer,
out_cand_count: &MlxBuffer,
out_overflow: &MlxBuffer,
out_cand_ids: &MlxBuffer,
params_buf: &MlxBuffer,
n_slots: u32,
vocab: u32,
cap: u32,
) -> Result<()> {
if n_slots == 0 || vocab == 0 || cap == 0 {
return Err(MlxError::InvalidArgument(
"gpu_sample: n_slots, vocab, cap must all be > 0".into(),
));
}
if logits.element_count() < (n_slots * vocab) as usize {
return Err(MlxError::InvalidArgument(format!(
"gpu_sample: logits {} < n_slots*vocab {}",
logits.element_count(),
n_slots * vocab
)));
}
if out_cand_ids.element_count() < (n_slots * cap) as usize {
return Err(MlxError::InvalidArgument(
"gpu_sample: out_cand_ids too small".into(),
));
}
let pipeline = registry.get_pipeline("gpu_sample_argmax_candidates", device)?;
let tg_size: u64 = std::cmp::min(1024, vocab.next_power_of_two() as u64).max(1);
let float_shared = tg_size * 4;
let uint_shared = tg_size * 4;
encoder.encode_threadgroups_with_shared(
pipeline,
&[
(0, logits),
(1, out_top1_idx),
(2, out_top1_val),
(3, out_cand_count),
(4, out_overflow),
(5, out_cand_ids),
(6, params_buf),
],
&[(0, float_shared), (1, uint_shared)],
MTLSize::new(n_slots as u64, 1, 1), MTLSize::new(tg_size, 1, 1),
);
Ok(())
}