use crate::real_forward_types::RealForwardError;
use crate::vision::scratch::VisionScratch;
use crate::vision::shape::{VisionShape, VISION_LAYER_NORM_EPS};
use crate::vision::weights::VisionResident;
pub(crate) fn encode_patch_embed(
context: &mut gpu::MetalContext,
pass: &gpu::PassEncoder,
weights: &gpu::ResidentGpuWeights,
resident: &VisionResident,
s: &VisionScratch,
shape: &VisionShape,
) -> Result<(), RealForwardError> {
gpu::encode_vision_matmul(
context,
pass,
(&s.rows, 0),
(weights.buffer(), weights.gpu_offset(resident.patch_w)),
Some((weights.buffer(), weights.gpu_offset(resident.patch_b))),
(&s.x, 0),
s.seq as u32,
shape.patch_dim as u32,
shape.hidden as u32,
)
.map_err(RealForwardError::Gpu)?;
gpu::encode_vision_residual_add(
context,
pass,
(&s.x, 0),
(&s.pos, 0),
(s.seq * shape.hidden) as u32,
)
.map_err(RealForwardError::Gpu)
}
pub(crate) fn pos_embed_rows(
table: &[f32],
entries: &turbospark_vision_io::PosEmbedTable,
shape: &VisionShape,
) -> Result<Vec<half::f16>, RealForwardError> {
let hidden = shape.hidden;
let patches = entries.len();
let mut out = vec![half::f16::ZERO; patches * hidden];
let mut acc = vec![0.0f32; hidden];
for patch in 0..patches {
acc.fill(0.0);
let dst = &mut out[patch * hidden..(patch + 1) * hidden];
for corner in 0..4 {
let row = entries.indices[corner][patch];
let w = entries.weights[corner][patch];
let src = table.get(row * hidden..(row + 1) * hidden).ok_or_else(|| {
RealForwardError::Unsupported(format!(
"position embedding row {row} is past the {}-row table",
shape.pos_rows
))
})?;
for (a, v) in acc.iter_mut().zip(src) {
*a += w * v;
}
}
for (d, a) in dst.iter_mut().zip(&acc) {
*d = half::f16::from_f32(*a);
}
}
Ok(out)
}
pub(crate) fn encode_merger(
context: &mut gpu::MetalContext,
pass: &gpu::PassEncoder,
weights: &gpu::ResidentGpuWeights,
resident: &VisionResident,
s: &VisionScratch,
shape: &VisionShape,
) -> Result<(), RealForwardError> {
let gpu_err = RealForwardError::Gpu;
let merger_in = shape.merger_input();
gpu::encode_vision_layer_norm(
context,
pass,
(&s.x, 0),
(weights.buffer(), weights.gpu_offset(resident.merger_norm_w)),
(weights.buffer(), weights.gpu_offset(resident.merger_norm_b)),
(&s.normed, 0),
s.seq as u32,
shape.hidden as u32,
VISION_LAYER_NORM_EPS,
)
.map_err(gpu_err)?;
gpu::encode_vision_matmul(
context,
pass,
(&s.normed, 0),
(weights.buffer(), weights.gpu_offset(resident.merger_fc1_w)),
Some((weights.buffer(), weights.gpu_offset(resident.merger_fc1_b))),
(&s.m1, 0),
s.merged as u32,
merger_in as u32,
merger_in as u32,
)
.map_err(gpu_err)?;
gpu::encode_vision_gelu(
context,
pass,
(&s.m1, 0),
(s.merged * merger_in) as u32,
gpu::GeluKind::Erf,
)
.map_err(gpu_err)?;
gpu::encode_vision_matmul(
context,
pass,
(&s.m1, 0),
(weights.buffer(), weights.gpu_offset(resident.merger_fc2_w)),
Some((weights.buffer(), weights.gpu_offset(resident.merger_fc2_b))),
(&s.out, 0),
s.merged as u32,
merger_in as u32,
shape.out_hidden as u32,
)
.map_err(gpu_err)
}
pub(crate) fn encode_deepstack_merger(
context: &mut gpu::MetalContext,
pass: &gpu::PassEncoder,
weights: &gpu::ResidentGpuWeights,
merger: &super::weights::DeepstackMergerWeights,
s: &VisionScratch,
shape: &VisionShape,
) -> Result<(), RealForwardError> {
let gpu_err = RealForwardError::Gpu;
let merger_in = shape.merger_input();
gpu::encode_vision_layer_norm(
context,
pass,
(&s.x, 0),
(weights.buffer(), weights.gpu_offset(merger.norm_w)),
(weights.buffer(), weights.gpu_offset(merger.norm_b)),
(&s.normed, 0),
s.merged as u32,
merger_in as u32,
VISION_LAYER_NORM_EPS,
)
.map_err(gpu_err)?;
gpu::encode_vision_matmul(
context,
pass,
(&s.normed, 0),
(weights.buffer(), weights.gpu_offset(merger.fc1_w)),
Some((weights.buffer(), weights.gpu_offset(merger.fc1_b))),
(&s.m1, 0),
s.merged as u32,
merger_in as u32,
merger_in as u32,
)
.map_err(gpu_err)?;
gpu::encode_vision_gelu(
context,
pass,
(&s.m1, 0),
(s.merged * merger_in) as u32,
gpu::GeluKind::Erf,
)
.map_err(gpu_err)?;
gpu::encode_vision_matmul(
context,
pass,
(&s.m1, 0),
(weights.buffer(), weights.gpu_offset(merger.fc2_w)),
Some((weights.buffer(), weights.gpu_offset(merger.fc2_b))),
(&s.out, 0),
s.merged as u32,
merger_in as u32,
shape.out_hidden as u32,
)
.map_err(gpu_err)
}