| 9663 | *****************************************************************************/ |
| 9664 | |
| 9665 | sam3_result sam3_segment_pcs(sam3_state& state, |
| 9666 | const sam3_model& model, |
| 9667 | const sam3_pcs_params& params) { |
| 9668 | if (model.hparams.visual_only || model.hparams.is_sam2()) { |
| 9669 | fprintf(stderr, "%s: ERROR: PCS not available on %s model\n", |
| 9670 | __func__, model.hparams.is_sam2() ? "SAM2" : "visual-only"); |
| 9671 | return sam3_result{}; |
| 9672 | } |
| 9673 | |
| 9674 | #if SAM3_LOG_LEVEL >= 1 |
| 9675 | auto t_start = std::chrono::high_resolution_clock::now(); |
| 9676 | #endif |
| 9677 | const auto& hp = model.hparams; |
| 9678 | const int D = hp.neck_dim; // 256 |
| 9679 | const int H = hp.n_img_embd(); // 72 |
| 9680 | const int L = hp.text_ctx_len; // 32 |
| 9681 | const int NQ = hp.ddec_num_queries; // 200 |
| 9682 | const int N_spatial = H * H; // 5184 |
| 9683 | sam3_result result; |
| 9684 | |
| 9685 | // ── Check that image has been encoded ──────────────────────────────── |
| 9686 | if (!state.neck_det[0]) { |
| 9687 | fprintf(stderr, "%s: image not encoded — call sam3_encode_image first\n", __func__); |
| 9688 | return result; |
| 9689 | } |
| 9690 | |
| 9691 | // ── Tokenize text prompt ───────────────────────────────────────────── |
| 9692 | auto token_ids = sam3_tokenize(const_cast<sam3_bpe_tokenizer&>(model.tokenizer), |
| 9693 | params.text_prompt, L); |
| 9694 | if (token_ids.empty()) { |
| 9695 | fprintf(stderr, "%s: failed to tokenize text prompt\n", __func__); |
| 9696 | return result; |
| 9697 | } |
| 9698 | |
| 9699 | SAM3_LOG(2, "%s: text='%s', %zu tokens\n", __func__, |
| 9700 | params.text_prompt.c_str(), token_ids.size()); |
| 9701 | |
| 9702 | // ── Helper: run a sub-graph with its own context and allocator ────── |
| 9703 | // Each stage below follows this exact pattern: |
| 9704 | // 1. Create fresh ggml_context + graph + allocator |
| 9705 | // 2. Create INPUT tensors (ggml_set_input) |
| 9706 | // 3. Build the computation graph |
| 9707 | // 4. Mark outputs (ggml_set_output) |
| 9708 | // 5. Allocate → set input data → compute → read output data |
| 9709 | // 6. Free allocator + context |
| 9710 | // This ensures ZERO buffer sharing between stages. |
| 9711 | |
| 9712 | // ── Pre-compute shared CPU data used by multiple stages ───────────── |
| 9713 | const int n_boxes = (int)(params.pos_exemplars.size() + params.neg_exemplars.size()); |
| 9714 | const int N_geo = n_boxes + 1; // +1 for CLS |
| 9715 | const int T = L + N_geo; // total prompt tokens (text + geometry) |
| 9716 | |
| 9717 | // Read image features and PE from state into CPU buffers (used by multiple stages) |
| 9718 | std::vector<float> img_feats_cpu(D * N_spatial); |
| 9719 | std::vector<float> img_pe_cpu(D * N_spatial); |
| 9720 | ggml_backend_tensor_get(state.neck_det[2], img_feats_cpu.data(), 0, |
| 9721 | D * N_spatial * sizeof(float)); |
| 9722 | ggml_backend_tensor_get(state.neck_det_pe[2], img_pe_cpu.data(), 0, |