Research: Dispatch-Strategy Registry Audit — 2026-05-15¶
Summary¶
Full cross-reference audit of every backend's dispatch-strategy registry against the vmaf_fex_* symbols actually built into libvmaf. Three categories of defect were found and fixed in this PR:
- Duplicate pointer entries in
feature_extractor_list[]for the SYCL and Vulkan backends (no coverage gap, but non-trivial bloat). - Wrong feature-name strings in
vmaf_metal_dispatch_supports()'sg_metal_features[]table — the table was populated with placeholder names that do not match what the Metal extractors actually emit. - Unconditionally-zero
vmaf_hip_dispatch_supports()— the function always returned 0 even though 8 HIP kernels are registered.
Audit Scope¶
Every VmafFeatureExtractor vmaf_fex_* definition in core/src/ was extracted via git grep. The resulting symbol sets were cross-referenced against:
feature_extractor_list[]incore/src/feature/feature_extractor.c(the central dispatch table thatvmaf_get_feature_extractor_by_nameandvmaf_get_feature_extractor_by_feature_namewalk).vmaf_<backend>_dispatch_supports()incore/src/<backend>/dispatch_strategy.c(the per-backend predicate callers use to test whether a named feature can route to that backend before binding GPU pictures).
Symbol-vs-Registry Cross-Reference Table¶
CPU (baseline)¶
18 CPU extractors — all present in feature_extractor_list[]. Result: CLEAN.
CUDA¶
17 symbols defined in core/src/feature/cuda/. All 17 appear exactly once in feature_extractor_list[]. Result: CLEAN.
SYCL¶
17 symbols defined in core/src/feature/sycl/. All 17 appear in feature_extractor_list[], but 6 were duplicated (appeared twice): psnr_sycl, psnr_hvs_sycl, float_ssim_sycl, float_ms_ssim_sycl, float_moment_sycl, ciede_sycl.
Gap class: DUPLICATE — no missing coverage, but bloat. Fixed.
Vulkan¶
17 symbols defined in core/src/feature/vulkan/. All 17 appear in feature_extractor_list[], but the table contained ~62 pointer entries for 17 distinct symbols. Worst cases: psnr_hvs_vulkan and float_ms_ssim_vulkan appeared 11 times each.
Gap class: DUPLICATE — no missing coverage, but severe bloat. Fixed.
HIP¶
8 symbols defined in core/src/feature/hip/:
| Symbol | .name field | In feature_extractor_list[] | In vmaf_hip_dispatch_supports() |
|---|---|---|---|
vmaf_fex_psnr_hip | "psnr_hip" | yes | NO — returned 0 always |
vmaf_fex_float_psnr_hip | "float_psnr_hip" | yes | NO |
vmaf_fex_ciede_hip | "ciede_hip" | yes | NO |
vmaf_fex_float_moment_hip | "float_moment_hip" | yes | NO |
vmaf_fex_float_ansnr_hip | "float_ansnr_hip" | yes | NO |
vmaf_fex_integer_motion_v2_hip | "motion_v2_hip" | yes | NO |
vmaf_fex_float_motion_hip | "float_motion_hip" | yes | NO |
vmaf_fex_float_ssim_hip | "float_ssim_hip" | yes | NO |
Gap class: DISPATCH-SUPPORTS STUB never updated. Fixed.
Metal¶
8 symbols defined in core/src/feature/metal/. All 8 in feature_extractor_list[], but g_metal_features[] in vmaf_metal_dispatch_supports() had wrong feature names:
| Old entry (wrong) | Correct entry | Source |
|---|---|---|
"motion2_v2_score" | "VMAF_integer_feature_motion2_v2_score" | integer_motion_v2_metal.mm |
| (missing) | "VMAF_integer_feature_motion_v2_sad_score" | integer_motion_v2_metal.mm |
"float_motion" | "VMAF_feature_motion_score" | float_motion_metal.mm |
| (missing) | "VMAF_feature_motion2_score" | float_motion_metal.mm |
"motion2_score" | "VMAF_integer_feature_motion_y_score" | integer_motion_metal.mm |
"motion3_score" | "VMAF_integer_feature_motion2_score" | integer_motion_metal.mm |
| (missing) | "float_anpsnr" | float_ansnr_metal.mm |
"float_ms_ssim" (spurious) | (removed) | not provided by float_ssim_metal |
Gap class: WRONG FEATURE NAMES in dispatch-supports table. Fixed.
Root Cause¶
The Metal table was populated during the ADR-0421 scaffolding pass with approximate names. The actual provided_features[] arrays in the .mm files use the canonical VMAF_* prefixed names; the dispatch table was never updated to match.
The HIP vmaf_hip_dispatch_supports() body retained its TODO stub through all 8 consumer PRs because each consumer PR only added a kernel and its feature_extractor_list[] registration — neither touched the dispatch-supports predicate.
The SYCL/Vulkan duplicates appear to be copy-paste accidents during the long sequence of backend-parity PRs; the lookup semantics (first-match) masked them from any functional failure.
Reproducer¶
Run scripts/ci/check-dispatch-registry.sh (added in this PR) to compare the symbol set against the dispatch table for each backend and report any gaps.
Decision matrix¶
No novel routing decision is required: the missing entries are mechanically determined by the extractor .name and provided_features[] fields. The SYCL/Vulkan deduplication is purely cosmetic — behaviour is unchanged because the lookup returns on first match and every symbol was already present at least once.