| 8747 | }; |
| 8748 | |
| 8749 | static sam3_ddec_output sam3_build_ddec_graph( |
| 8750 | struct ggml_context* ctx, |
| 8751 | const sam3_model& model, |
| 8752 | struct ggml_tensor* enc_feats, // [D, N_kv, B] |
| 8753 | struct ggml_tensor* enc_pos, // [D, N_kv, B] |
| 8754 | struct ggml_tensor* text_feats, // [D, T, B] |
| 8755 | struct ggml_tensor* sine_dim_t, // [1, 64] — pre-computed angle multipliers |
| 8756 | struct ggml_tensor* rpb_coords, // [feat_hw] — normalized grid coords (or nullptr) |
| 8757 | struct ggml_tensor* text_attn_bias = nullptr, // [T, 1, B] additive text padding bias |
| 8758 | struct ggml_tensor* text_valid_mask = nullptr) // [T, 1, B] for scoring (or nullptr) |
| 8759 | { |
| 8760 | const auto& hp = model.hparams; |
| 8761 | const auto& tensors = model.tensors; |
| 8762 | const int D = hp.neck_dim; // 256 |
| 8763 | const int NQ = hp.ddec_num_queries; // 200 |
| 8764 | const int B = (int)enc_feats->ne[2]; // batch (1) |
| 8765 | const int feat_hw = hp.n_img_embd(); // 72 |
| 8766 | |
| 8767 | // ── Initialize queries from query_embed ────────────────────────────── |
| 8768 | auto* content = ggml_reshape_3d(ctx, model.ddec.query_embed, D, NQ, 1); |
| 8769 | auto* pres_tok = ggml_reshape_3d(ctx, model.ddec.presence_token, D, 1, 1); |
| 8770 | auto* queries = ggml_concat(ctx, pres_tok, content, 1); // [D, NQ+1, B=1] |
| 8771 | |
| 8772 | // Reference points: sigmoid → initial anchor boxes |
| 8773 | auto* ref_pts_raw = ggml_cont(ctx, tensors.at("ddec.reference_points.weight")); // [4, NQ] |
| 8774 | auto* ref_boxes = ggml_sigmoid(ctx, ref_pts_raw); // [4, NQ] |
| 8775 | ref_boxes = ggml_reshape_3d(ctx, ref_boxes, 4, NQ, 1); // [4, NQ, 1] |
| 8776 | #ifndef NDEBUG |
| 8777 | auto* ref_boxes_dbg = ggml_cont(ctx, ref_boxes); |
| 8778 | ggml_set_name(ref_boxes_dbg, "ddec_ref_boxes_init"); |
| 8779 | #endif |
| 8780 | |
| 8781 | // ── Run decoder layers ─────────────────────────────────────────────── |
| 8782 | // Per-layer: recompute query_pos from updated ref_boxes (matching Python exactly) |
| 8783 | struct ggml_tensor* last_presence = pres_tok; |
| 8784 | for (int i = 0; i < hp.ddec_layers; ++i) { |
| 8785 | // Recompute query_pos from current ref_boxes via sine embed + ref_point_head MLP |
| 8786 | auto* query_pos = sam3_build_query_pos(ctx, model, ref_boxes, sine_dim_t, i); |
| 8787 | |
| 8788 | // Compute box-relative positional bias for image cross-attention |
| 8789 | struct ggml_tensor* rpb_mask = nullptr; |
| 8790 | if (rpb_coords) { |
| 8791 | rpb_mask = sam3_compute_box_rpb(ctx, model, ref_boxes, rpb_coords, feat_hw, i); |
| 8792 | } |
| 8793 | |
| 8794 | queries = sam3_ddec_layer_forward(ctx, model.ddec.layers[i], |
| 8795 | queries, query_pos, |
| 8796 | enc_feats, enc_pos, |
| 8797 | text_feats, hp.ddec_heads, |
| 8798 | text_attn_bias, |
| 8799 | rpb_mask, |
| 8800 | i); |
| 8801 | |
| 8802 | // Box refinement after each layer (on object queries only, not presence token) |
| 8803 | auto* obj_q = ggml_view_3d(ctx, queries, D, NQ, 1, |
| 8804 | queries->nb[1], queries->nb[2], 1 * queries->nb[1]); |
| 8805 | obj_q = ggml_cont(ctx, obj_q); |
| 8806 | sam3_name_tensorf(obj_q, "ddec_layer%d_out", i); |
no test coverage detected