Compute Hiera positional embedding on CPU. Returns [E, H, W] in ggml column-major layout: element (e, x, y) at flat index e + x*E + y*E*W. PE = bicubic_interp(pos_embed_bkg, (H, W)) + tile(pos_embed_window, (H/W0, W/W0))
| 4116 | // Returns [E, H, W] in ggml column-major layout: element (e, x, y) at flat index e + x*E + y*E*W. |
| 4117 | // PE = bicubic_interp(pos_embed_bkg, (H, W)) + tile(pos_embed_window, (H/W0, W/W0)) |
| 4118 | static std::vector<float> sam2_compute_pos_embed(const sam3_model& model, int H, int W) { |
| 4119 | const auto& hp = model.hparams; |
| 4120 | const int E = hp.hiera_embed_dim; |
| 4121 | const int bkg_h = hp.hiera_pos_embed_bkg_h; |
| 4122 | const int bkg_w = hp.hiera_pos_embed_bkg_w; |
| 4123 | const int ws = hp.hiera_window_spec[0]; |
| 4124 | |
| 4125 | // Read background PE from GPU. |
| 4126 | // Registered as [bkg_w, bkg_h, E, 1] in ggml; raw bytes match PyTorch NCHW [1,E,H,W]. |
| 4127 | // ggml flat index for ne=[W,H,E,1]: w + h*W + e*W*H = same as CHW[e,h,w] = e*H*W + h*W + w. |
| 4128 | // So we can use the raw data directly as CHW layout for bicubic interpolation. |
| 4129 | std::vector<float> bkg_chw(E * bkg_h * bkg_w); |
| 4130 | ggml_backend_tensor_get(model.hiera.pos_embed, bkg_chw.data(), 0, |
| 4131 | bkg_chw.size() * sizeof(float)); |
| 4132 | |
| 4133 | // Bicubic interpolate to [E, H, W] |
| 4134 | std::vector<float> bkg_interp(E * H * W); |
| 4135 | sam2_bicubic_interpolate_cpu(bkg_chw.data(), E, bkg_h, bkg_w, |
| 4136 | bkg_interp.data(), H, W); |
| 4137 | |
| 4138 | // Read window PE from GPU: registered [ws, ws, E, 1], raw bytes = CHW [E, ws, ws] |
| 4139 | std::vector<float> win_chw(E * ws * ws); |
| 4140 | ggml_backend_tensor_get(model.hiera.pos_embed_window, win_chw.data(), 0, |
| 4141 | win_chw.size() * sizeof(float)); |
| 4142 | |
| 4143 | // Tile window PE to [E, H, W] |
| 4144 | std::vector<float> win_tiled(E * H * W); |
| 4145 | for (int e = 0; e < E; ++e) |
| 4146 | for (int y = 0; y < H; ++y) |
| 4147 | for (int x = 0; x < W; ++x) |
| 4148 | win_tiled[e * H * W + y * W + x] = win_chw[e * ws * ws + (y % ws) * ws + (x % ws)]; |
| 4149 | |
| 4150 | // Dump intermediates if requested |
| 4151 | const char* dump_dir = getenv("SAM2_DUMP_DIR"); |
| 4152 | if (dump_dir) { |
| 4153 | char path[512]; |
| 4154 | // Dump bkg_interp as CHW |
| 4155 | snprintf(path, sizeof(path), "%s/cpp_pe_bkg_interp.bin", dump_dir); |
| 4156 | FILE* f = fopen(path, "wb"); |
| 4157 | if (f) { fwrite(bkg_interp.data(), sizeof(float), bkg_interp.size(), f); fclose(f); } |
| 4158 | snprintf(path, sizeof(path), "%s/cpp_pe_bkg_interp.shape", dump_dir); |
| 4159 | f = fopen(path, "w"); if (f) { fprintf(f, "%d,%d,%d", E, H, W); fclose(f); } |
| 4160 | // Dump win_tiled as CHW |
| 4161 | snprintf(path, sizeof(path), "%s/cpp_pe_win_tiled.bin", dump_dir); |
| 4162 | f = fopen(path, "wb"); |
| 4163 | if (f) { fwrite(win_tiled.data(), sizeof(float), win_tiled.size(), f); fclose(f); } |
| 4164 | } |
| 4165 | |
| 4166 | // Sum and convert to ggml layout [E, W, H, 1] |
| 4167 | std::vector<float> pe(E * H * W); |
| 4168 | for (int e = 0; e < E; ++e) |
| 4169 | for (int y = 0; y < H; ++y) |
| 4170 | for (int x = 0; x < W; ++x) |
| 4171 | pe[e + x * E + y * E * W] = bkg_interp[e * H * W + y * W + x] |
| 4172 | + win_tiled[e * H * W + y * W + x]; |
| 4173 | |
| 4174 | return pe; |
| 4175 | } |
no test coverage detected