Single DETR decoder layer. queries: [D, N_q, B] where N_q = 201 (200 object queries + 1 presence token) query_pos: [D, N_q, B] positional encoding for queries enc_feats: [D, N_kv, B] conditioned image features from fusion encoder enc_pos: [D, N_kv, B] positional encoding for image features text_feats: [D, T, B] text features rpb_mask: [N_kv, N_q, n_heads, B] box-relative positional bias (or nullpt
| 8436 | // rpb_mask: [N_kv, N_q, n_heads, B] box-relative positional bias (or nullptr) |
| 8437 | // Returns: updated queries [D, N_q, B] |
| 8438 | static struct ggml_tensor* sam3_ddec_layer_forward( |
| 8439 | struct ggml_context* ctx, |
| 8440 | const sam3_ddec_layer& ly, |
| 8441 | struct ggml_tensor* queries, |
| 8442 | struct ggml_tensor* query_pos, |
| 8443 | struct ggml_tensor* enc_feats, |
| 8444 | struct ggml_tensor* enc_pos, |
| 8445 | struct ggml_tensor* text_feats, |
| 8446 | int n_heads, |
| 8447 | struct ggml_tensor* text_attn_bias = nullptr, |
| 8448 | struct ggml_tensor* rpb_mask = nullptr, |
| 8449 | int layer_idx = -1) { |
| 8450 | const int64_t D = queries->ne[0]; |
| 8451 | |
| 8452 | // Python decoder layer order (all post-norm): |
| 8453 | // 1. Self-attention → norm2 (post-norm) |
| 8454 | // 2. Text cross-attention (ca_text) → catext_norm (post-norm) |
| 8455 | // 3. Image cross-attention (cross_attn) → norm1 (post-norm) |
| 8456 | // 4. FFN → norm3 (post-norm) |
| 8457 | // |
| 8458 | // Norm weight mapping: |
| 8459 | // ly.norm2_w = ".norm2.weight" = Python norm2 (post-SA) |
| 8460 | // ly.norm3_w = ".norm_ca_text.weight" = Python catext_norm (post-text-CA) |
| 8461 | // ly.norm1_w = ".norm1.weight" = Python norm1 (post-image-CA) |
| 8462 | // ly.norm4_w = ".norm3.weight" = Python norm3 (post-FFN) |
| 8463 | |
| 8464 | // 1. Self-attention among queries (post-norm) |
| 8465 | { |
| 8466 | // Q = K = queries + query_pos, V = queries (no pos) |
| 8467 | auto* q_in = ggml_add(ctx, queries, query_pos); |
| 8468 | |
| 8469 | auto* q_w = ggml_view_2d(ctx, ly.sa_in_proj_w, D, D, ly.sa_in_proj_w->nb[1], 0); |
| 8470 | auto* k_w = ggml_view_2d(ctx, ly.sa_in_proj_w, D, D, ly.sa_in_proj_w->nb[1], D * ly.sa_in_proj_w->nb[1]); |
| 8471 | auto* v_w = ggml_view_2d(ctx, ly.sa_in_proj_w, D, D, ly.sa_in_proj_w->nb[1], 2 * D * ly.sa_in_proj_w->nb[1]); |
| 8472 | auto* q_b = ggml_view_1d(ctx, ly.sa_in_proj_b, D, 0); |
| 8473 | auto* k_b = ggml_view_1d(ctx, ly.sa_in_proj_b, D, D * sizeof(float)); |
| 8474 | auto* v_b = ggml_view_1d(ctx, ly.sa_in_proj_b, D, 2 * D * sizeof(float)); |
| 8475 | |
| 8476 | const int64_t N = queries->ne[1]; |
| 8477 | const int64_t B = queries->ne[2]; |
| 8478 | const int64_t HD = D / n_heads; |
| 8479 | |
| 8480 | auto* Q = ggml_add(ctx, ggml_mul_mat(ctx, q_w, q_in), q_b); |
| 8481 | auto* K = ggml_add(ctx, ggml_mul_mat(ctx, k_w, q_in), k_b); |
| 8482 | auto* V = ggml_add(ctx, ggml_mul_mat(ctx, v_w, queries), v_b); |
| 8483 | |
| 8484 | Q = ggml_reshape_4d(ctx, Q, HD, n_heads, N, B); |
| 8485 | Q = ggml_cont(ctx, ggml_permute(ctx, Q, 0, 2, 1, 3)); |
| 8486 | K = ggml_reshape_4d(ctx, K, HD, n_heads, N, B); |
| 8487 | K = ggml_cont(ctx, ggml_permute(ctx, K, 0, 2, 1, 3)); |
| 8488 | V = ggml_reshape_4d(ctx, V, HD, n_heads, N, B); |
| 8489 | V = ggml_permute(ctx, V, 0, 2, 1, 3); |
| 8490 | |
| 8491 | float scale = 1.0f / sqrtf((float)HD); |
| 8492 | auto* sa_out = ggml_flash_attn_ext(ctx, Q, K, V, nullptr, scale, 0.0f, 0.0f); |
| 8493 | sa_out = ggml_reshape_3d(ctx, sa_out, D, N, B); |
| 8494 | sa_out = ggml_mul_mat(ctx, ly.sa_out_proj_w, sa_out); |
| 8495 | sa_out = ggml_add(ctx, sa_out, ly.sa_out_proj_b); |
no test coverage detected