Research-0761 — Metal Backend Audit (2026-05-29)¶
Purpose¶
Full audit of core/src/metal/ (Obj-C++ runtime, picture, IOSurface import, kernel template, dispatch strategy) and core/src/feature/metal/ (.mm host binders + .metal MSL shaders) across eight concern classes:
- Apple-Family-7 gate correctness.
- ARC correctness (no manual retain/release in ARC-compiled TUs).
- MTLFunction lookup safety (nil-check after
newFunctionWithName:). - IOSurface lifecycle (analogous to the pinned-host leak in HIP/PR #94).
- MSL struct-by-value in kernel signatures.
- Scaffold vs real-kernel classification.
- Dispatch table completeness when a new kernel lands.
- Public-header Doxygen status block currency.
Scope¶
Files read:
core/src/metal/{common,picture_metal,picture_import,kernel_template,dispatch_strategy}.{mm,c,h}core/src/metal/{state_priv,import,kernel_template,common,picture_metal,dispatch_strategy}.hcore/src/metal/meson.buildcore/src/feature/metal/*.{mm,metal}(9.mmhost TUs, 8.metalshader files)core/src/metal/AGENTS.md,core/src/feature/metal/AGENTS.mdcore/include/libvmaf/libvmaf_metal.hcore/src/feature/feature_extractor.c(HAVE_METAL block)
Inventory¶
Runtime (core/src/metal/)¶
| File | Status |
|---|---|
common.mm | Real (T8-1b / ADR-0420) |
picture_metal.mm | Real (T8-1b / ADR-0420) |
picture_import.mm | Real (T8-IOS / ADR-0423) |
kernel_template.mm | Real (T8-1b / ADR-0420) |
dispatch_strategy.c | Real (ADR-0421) |
Feature extractors (core/src/feature/metal/)¶
| Extractor | .mm | .metal | Status |
|---|---|---|---|
integer_motion_v2_metal | Yes | Yes | Real (T8-1c) |
float_psnr_metal | Yes | Yes | Real (T8-1d) |
float_moment_metal | Yes | Yes | Real (T8-1e) |
integer_psnr_metal | Yes | Yes | Real (T8-1g) |
float_motion_metal | Yes | Yes | Real (T8-1h) |
integer_motion_metal | Yes | Yes | Real (T8-1i) |
float_ssim_metal | Yes | Yes | Real (T8-1j) |
float_ms_ssim_metal | Yes | Yes | Real (T8-2b / ADR-0490) |
float_ansnr_metal | No | No | Deleted (PR #38 — ansnr feature dropped) |
All 8 active extractors have real .mm + .metal pairs compiled in meson.build. No scaffold stubs remain in tree.
Findings¶
MT-1 — dispatch-table | severity: HIGH¶
float_ms_ssim_metal extractor name missing from dispatch table
core/src/metal/dispatch_strategy.c:g_metal_features[] lists "float_ms_ssim" (the provided feature key) but not "float_ms_ssim_metal" (the extractor name). Every other wired extractor has both entries: for example "float_ssim_metal" and "float_ssim", "float_motion_metal" and "float_motion". The omission means vmaf_metal_dispatch_supports(ctx, "float_ms_ssim_metal") returns 0, which will cause the dispatcher to skip Metal routing for this extractor when the caller queries by extractor name.
Recommend: add "float_ms_ssim_metal" to g_metal_features[] in core/src/metal/dispatch_strategy.c immediately before or after the existing "float_ms_ssim" entry.
MT-2 — IOSurface | severity: MEDIUM¶
vmaf_metal_state_init_external double-retains externally-supplied handles
core/src/metal/picture_import.mm:vmaf_metal_state_init_external (lines ~209–217) applies CFRetain((__bridge CFTypeRef)device) and then (__bridge_retained void *)device, yielding a net +2 retain count on the external device. vmaf_metal_state_free issues only a single (__bridge_transfer ...) = -1. The caller-owned +1 from CFRetain is never released. The same pattern applies to an externally-supplied command_queue when queue_owned_externally == 1.
This is an analog of the HIP pinned-host leak (PR #94): the caller expects to remain the sole owner of its id<MTLDevice>, but libvmaf holds a dangling extra retain that delays deallocation.
Recommend: Remove the CFRetain calls. __bridge_retained alone takes the +1 needed to anchor the handle across the C ABI boundary; the corresponding __bridge_transfer in vmaf_metal_state_free correctly drops that single +1. The external ownership comment in the code is the source of the confusion — the correct interpretation is "we take our own +1; the caller still owns theirs". With CFRetain absent, teardown is balanced.
MT-3 — header-install | severity: MEDIUM¶
libvmaf_metal.h Doxygen status block is stale after T8-2b
core/include/libvmaf/libvmaf_metal.h lines 17–18 read:
* (T8-1c/d, ADR-0421 — `integer_motion_v2.metal` + 7 additional
* feature-extractor MSL shaders) are fully shipped.
That describes the T8-1 batch (8 kernels). float_ms_ssim_metal (T8-2b / ADR-0490) is now wired and registered, making the live count 8 shaders (after float_ansnr was dropped in PR #38). The status block should reflect the current T8-2b milestone and cite ADR-0490.
Recommend: Update the @brief status block to say "T8-1c through T8-2b, ADR-0421 + ADR-0490 — 8 MSL shaders compiled and registered" and update the ADR citation list.
MT-4 — parity | severity: MEDIUM¶
float_ms_ssim.metal lacks the ADR-0214 places=4 parity gate citation
core/src/feature/metal/integer_motion_v2.metal cites "Bit-exactness gate: places=4 cross-backend-diff against scalar (per ADR-0214)" explicitly in its header comment. All other batch-1 kernels carry similar citations. float_ms_ssim.metal (T8-2b) has no such citation and no reference to ADR-0214 in its header comment. The parity obligation still applies per ADR-0214.
Recommend: Add to float_ms_ssim.metal header:
* Parity gate (ADR-0214): `places=4` cross-backend-diff against CPU
* float_ms_ssim. Run with:
* scripts/parity/cross-backend-diff.sh --extractor float_ms_ssim \
* --ref testdata/src01_hrc00_576x324.yuv \
* --dis testdata/src01_hrc01_576x324.yuv \
* --backend metal --places 4
MT-5 — IOSurface | severity: LOW¶
vmaf_metal_picture_import does not validate IOSurface plane count against declared pixel format
ADR-0423 audit requirement states the import function should validate the IOSurface plane count and pixel format against the declared VmafMetalConfiguration. The current implementation in picture_import.mm calls IOSurfaceGetBaseAddressOfPlane and IOSurfaceGetBytesPerRowOfPlane directly, with no preceding call to IOSurfaceGetPlaneCount or IOSurfaceGetPixelFormat. A YUV 4:2:2 or packed IOSurface passed as plane 0/1/2 would silently read out-of-bounds plane data if the surface has fewer than 3 planes.
Recommend: Add a guard at the top of vmaf_metal_picture_import:
This mirrors the Vulkan import guard for VK_FORMAT plane count validation.
MT-6 — arc | severity: LOW (informational)¶
AGENTS.md references float_ansnr_metal as "Done T8-1f" after files were deleted
core/src/feature/metal/AGENTS.md lists float_ansnr.metal and float_ansnr_metal.mm as "Done (T8-1f)" in both the wired-set inventory and the kernel table. PR #38 deleted these files and removed the feature across all backends. The stale table entry will mislead agents auditing wired-set completeness.
Recommend: Remove float_ansnr rows from the AGENTS.md kernel table and remove it from the wired-set inventory sentence.
Summary of audit criteria¶
| Criterion | Result |
|---|---|
| Apple-Family-7 gate on every entry point | PASS — all _init / _init_external / select_device_or_nil paths gate on MTLGPUFamilyApple7 |
| ARC correctness — no manual retain/release | PASS — all TUs use -fobjc-arc; no raw [obj release] / [obj retain] found |
| MTLFunction lookup nil-checks | PASS — float_ms_ssim_metal.mm checks all three function handles in a compound nil test; same pattern in other .mm files |
| MSL struct-by-value in kernel signatures | PASS — all kernels pass params as primitive MSL types (uint4, float4, uint2); no host-defined structs cross the buffer boundary |
| Scaffold vs real classification | PASS — all 8 live extractors have real .mm + .metal pairs; no ENOSYS stubs remain |
| Dispatch table completeness | FAIL — MT-1: float_ms_ssim_metal extractor name absent |
| IOSurface lifecycle correctness | FAIL — MT-2: double-retain in init_external; MT-5: missing plane-count guard |
| Public-header Doxygen status | FAIL — MT-3: stale kernel count after T8-2b |
| Parity gate citation | FAIL — MT-4: float_ms_ssim.metal missing ADR-0214 citation |
| AGENTS.md accuracy | FAIL — MT-6: stale float_ansnr rows |
Cross-backend ULP gate command¶
For the float_ms_ssim parity run required by ADR-0214:
scripts/parity/cross-backend-diff.sh \
--extractor float_ms_ssim \
--ref testdata/src01_hrc00_576x324.yuv \
--dis testdata/src01_hrc01_576x324.yuv \
--backend metal --places 4
No code changes in this PR. All findings are recommendations only.