Single text encoder block forward pass. Input x: [E, L] where E=text_width=1024, L=seq_len (typically 32). causal_mask: [L, L] F16 additive mask for ggml_flash_attn_ext. Returns: [E, L]
| 3955 | // causal_mask: [L, L] F16 additive mask for ggml_flash_attn_ext. |
| 3956 | // Returns: [E, L] |
| 3957 | static struct ggml_tensor* sam3_text_block_forward(struct ggml_context* ctx, |
| 3958 | struct ggml_tensor* x, |
| 3959 | const sam3_text_block& blk, |
| 3960 | const sam3_hparams& hp, |
| 3961 | struct ggml_tensor* causal_mask, |
| 3962 | int block_idx) { |
| 3963 | const int E = hp.text_width; // 1024 |
| 3964 | const int NH = hp.text_heads; // 16 |
| 3965 | const int HD = E / NH; // 64 |
| 3966 | const int64_t L = x->ne[1]; // sequence length |
| 3967 | |
| 3968 | auto* shortcut = x; |
| 3969 | x = sam3_layer_norm(ctx, x, blk.ln1_w, blk.ln1_b); |
| 3970 | sam3_name_tensorf(x, "text_block_%02d_after_ln1", block_idx); |
| 3971 | |
| 3972 | auto* qkv = ggml_mul_mat(ctx, blk.attn_in_proj_w, x); |
| 3973 | qkv = ggml_add(ctx, qkv, blk.attn_in_proj_b); |
| 3974 | sam3_name_tensorf(qkv, "text_block_%02d_qkv", block_idx); |
| 3975 | |
| 3976 | // [3*E, L] → [E, 3, L] → permute → [E, L, 3] |
| 3977 | qkv = ggml_reshape_3d(ctx, qkv, E, 3, L); |
| 3978 | qkv = ggml_cont(ctx, ggml_permute(ctx, qkv, 0, 2, 1, 3)); |
| 3979 | // qkv: [E, L, 3] |
| 3980 | |
| 3981 | auto* Q = ggml_view_2d(ctx, qkv, E, L, qkv->nb[1], 0); |
| 3982 | auto* K = ggml_view_2d(ctx, qkv, E, L, qkv->nb[1], 1 * qkv->nb[2]); |
| 3983 | auto* V = ggml_view_2d(ctx, qkv, E, L, qkv->nb[1], 2 * qkv->nb[2]); |
| 3984 | |
| 3985 | // [E, L] → [HD, NH, L] → permute → [HD, L, NH, 1] |
| 3986 | Q = ggml_reshape_3d(ctx, Q, HD, NH, L); |
| 3987 | Q = ggml_cont(ctx, ggml_permute(ctx, Q, 0, 2, 1, 3)); |
| 3988 | Q = ggml_reshape_4d(ctx, Q, HD, L, NH, 1); |
| 3989 | |
| 3990 | K = ggml_reshape_3d(ctx, K, HD, NH, L); |
| 3991 | K = ggml_cont(ctx, ggml_permute(ctx, K, 0, 2, 1, 3)); |
| 3992 | K = ggml_reshape_4d(ctx, K, HD, L, NH, 1); |
| 3993 | |
| 3994 | V = ggml_reshape_3d(ctx, V, HD, NH, L); |
| 3995 | V = ggml_permute(ctx, V, 0, 2, 1, 3); // non-contiguous; flash_attn uses strides |
| 3996 | |
| 3997 | float scale = 1.0f / sqrtf((float)HD); |
| 3998 | auto* attn_out = ggml_flash_attn_ext(ctx, Q, K, V, causal_mask, scale, 0.0f, 0.0f); |
| 3999 | x = ggml_reshape_2d(ctx, attn_out, E, L); |
| 4000 | |
| 4001 | x = ggml_mul_mat(ctx, blk.attn_out_proj_w, x); |
| 4002 | x = ggml_add(ctx, x, blk.attn_out_proj_b); |
| 4003 | |
| 4004 | if (blk.ls1) { |
| 4005 | x = ggml_mul(ctx, x, blk.ls1); |
| 4006 | } |
| 4007 | sam3_name_tensorf(x, "text_block_%02d_attn_out", block_idx); |
| 4008 | |
| 4009 | x = ggml_add(ctx, shortcut, x); |
| 4010 | sam3_name_tensorf(x, "text_block_%02d_after_attn_residual", block_idx); |
| 4011 | |
| 4012 | shortcut = x; |
| 4013 | x = sam3_layer_norm(ctx, x, blk.ln2_w, blk.ln2_b); |
| 4014 | sam3_name_tensorf(x, "text_block_%02d_after_ln2", block_idx); |
no test coverage detected