Build the full RepViT backbone graph. Input: [W, H, 3, 1] image Returns 4 feature maps as stage_outs[0..3], each [C_stage, W_stage, H_stage, 1]
| 4794 | // Input: [W, H, 3, 1] image |
| 4795 | // Returns 4 feature maps as stage_outs[0..3], each [C_stage, W_stage, H_stage, 1] |
| 4796 | static void edgetam_build_repvit_graph(struct ggml_context* ctx, |
| 4797 | struct ggml_tensor* input, |
| 4798 | const sam3_model& model, |
| 4799 | struct ggml_tensor* stage_outs[4]) { |
| 4800 | const auto& repvit = model.repvit; |
| 4801 | const auto& hp = model.hparams; |
| 4802 | |
| 4803 | // Stem: conv1 (3→24, k=3, s=2, p=1) + GELU, conv2 (24→48, k=3, s=2, p=1), NO GELU |
| 4804 | // Use ggml_conv_2d_direct to avoid im2col + cont overhead (single Metal kernel) |
| 4805 | auto* x = ggml_conv_2d(ctx, repvit.stem_conv1_w, input, 2, 2, 1, 1, 1, 1); |
| 4806 | x = edgetam_conv2d_bias(ctx, x, repvit.stem_conv1_b); |
| 4807 | x = ggml_gelu(ctx, x); |
| 4808 | x = ggml_conv_2d(ctx, repvit.stem_conv2_w, x, 2, 2, 1, 1, 1, 1); |
| 4809 | x = edgetam_conv2d_bias(ctx, x, repvit.stem_conv2_b); |
| 4810 | // No GELU after stem conv2 |
| 4811 | |
| 4812 | // 4 stages |
| 4813 | for (int s = 0; s < hp.repvit_num_stages; ++s) { |
| 4814 | auto& stage = repvit.stages[s]; |
| 4815 | |
| 4816 | // Downsample at start of stages 1, 2, 3 |
| 4817 | if (stage.has_downsample) { |
| 4818 | x = edgetam_repvit_downsample_forward(ctx, x, stage.downsample); |
| 4819 | } |
| 4820 | |
| 4821 | // Process all blocks in this stage |
| 4822 | for (int b = 0; b < (int)stage.blocks.size(); ++b) { |
| 4823 | x = edgetam_repvit_block_forward(ctx, x, stage.blocks[b]); |
| 4824 | } |
| 4825 | |
| 4826 | // Store stage output. |
| 4827 | // Keep as [W, H, C, 1] — the EdgeTAM FPN handles this layout directly |
| 4828 | stage_outs[s] = x; |
| 4829 | } |
| 4830 | } |
| 4831 | |
| 4832 | // Full EdgeTAM image encoding: preprocess → RepViT → FPN → state |
| 4833 | static bool edgetam_encode_image(sam3_state& state, |
no test coverage detected