| 10763 | *****************************************************************************/ |
| 10764 | |
| 10765 | sam3_result sam3_segment_pvs(sam3_state& state, |
| 10766 | const sam3_model& model, |
| 10767 | const sam3_pvs_params& params) { |
| 10768 | #if SAM3_LOG_LEVEL >= 1 |
| 10769 | auto t_start = std::chrono::high_resolution_clock::now(); |
| 10770 | #endif |
| 10771 | const auto& hp = model.hparams; |
| 10772 | const int D = hp.sam_embed_dim; // 256 |
| 10773 | const int H = sam3_eff_feat_size(state, hp); |
| 10774 | const int num_mask_tokens = hp.sam_n_multimask + 1; // 4 |
| 10775 | const int eff_img_size = sam3_eff_img_size(state, hp); |
| 10776 | sam3_result result; |
| 10777 | |
| 10778 | // ── Validate ───────────────────────────────────────────────────────── |
| 10779 | if (!state.neck_trk[0]) { |
| 10780 | fprintf(stderr, "%s: image not encoded — call sam3_encode_image first\n", __func__); |
| 10781 | return result; |
| 10782 | } |
| 10783 | if (params.pos_points.empty() && !params.use_box) { |
| 10784 | fprintf(stderr, "%s: no prompts provided (need at least one point or box)\n", __func__); |
| 10785 | return result; |
| 10786 | } |
| 10787 | |
| 10788 | SAM3_LOG(2, "%s: %zu pos points, %zu neg points, box=%s, multimask=%s\n", |
| 10789 | __func__, params.pos_points.size(), params.neg_points.size(), |
| 10790 | params.use_box ? "yes" : "no", params.multimask ? "yes" : "no"); |
| 10791 | |
| 10792 | // ── Build computation graph ────────────────────────────────────────── |
| 10793 | const size_t buf_size = ggml_tensor_overhead() * 8192 + ggml_graph_overhead() * 2; |
| 10794 | struct ggml_init_params gparams = { |
| 10795 | /*.mem_size =*/buf_size, |
| 10796 | /*.mem_buffer =*/nullptr, |
| 10797 | /*.no_alloc =*/true, |
| 10798 | }; |
| 10799 | struct ggml_context* ctx0 = ggml_init(gparams); |
| 10800 | if (!ctx0) { |
| 10801 | fprintf(stderr, "%s: failed to init compute context\n", __func__); |
| 10802 | return result; |
| 10803 | } |
| 10804 | |
| 10805 | // ── SAM prompt encoder (CPU pre-compute + input tensors) ───────────── |
| 10806 | auto pe_out = sam3_build_sam_pe(ctx0, params, D, H); |
| 10807 | |
| 10808 | // ── Create fresh input tensors for tracker features ────────────────── |
| 10809 | // CRITICAL: Do NOT use state.neck_trk[*] directly in graph operations |
| 10810 | // (like ggml_add). They are tensors from a previous graph, and |
| 10811 | // ggml_build_forward_expand traces all ancestors — which would pull in |
| 10812 | // the ENTIRE ViT + neck recomputation (2500+ nodes, ~37 seconds). |
| 10813 | // Instead: create fresh input tensors and copy data from state on CPU. |
| 10814 | const int H0 = H * 4; // 288 |
| 10815 | const int H1 = H * 2; // 144 |
| 10816 | |
| 10817 | auto* image_feats = ggml_new_tensor_4d(ctx0, GGML_TYPE_F32, D, H, H, 1); |
| 10818 | ggml_set_name(image_feats, "sam_dec_image_feats"); |
| 10819 | ggml_set_input(image_feats); |
| 10820 | |
| 10821 | auto* feat_s0 = ggml_new_tensor_4d(ctx0, GGML_TYPE_F32, D, H0, H0, 1); |
| 10822 | ggml_set_name(feat_s0, "pvs_feat_s0"); |