GPU Backend Context-API Contract¶
Backends that own a device context (HIP and Metal today) expose the same three internal functions. Keeping one shape prevents per-backend API drift and lets the feature-extractor glue stay uniform across backends. See ADR-0486 for the decision record.
Note
This is an internal contract between libvmaf's own translation units. The headers core/src/<backend>/common.h are not installed. The public, installed GPU surface is the state API in libvmaf_cuda.h, libvmaf_sycl.h, libvmaf_hip.h and libvmaf_metal.h; see GPU API.
Which backends follow it¶
| Backend | Context layer | Notes |
|---|---|---|
| HIP | vmaf_hip_context_* in core/src/hip/common.h | follows the three-function shape |
| Metal | vmaf_metal_context_* in core/src/metal/common.h | follows the shape and adds two handle accessors |
| CUDA | vmaf_cuda_state_init / vmaf_cuda_release (public header) | exempt, predates the contract |
| SYCL | vmaf_sycl_state_init (public header) | exempt, uses a state API |
The CUDA and SYCL APIs are public and consumed by ffmpeg-patches/. Do not rename them without a dedicated ADR and a matching update of the patches.
Required functions¶
A backend named <backend> that follows the contract declares these in core/src/<backend>/common.h. The Vulkan backend followed it before its removal (ADR-0726).
/* Allocate and initialise a new context bound to device_index.
*
* Returns 0 on success; *ctx is non-NULL.
* Returns -EINVAL if ctx is NULL or device_index is out of range.
* Returns -ENODEV if no device with that index exists.
* Returns -ENOMEM on allocation failure.
*
* The caller owns *ctx and must release it with vmaf_<backend>_context_destroy.
*/
int vmaf_<backend>_context_new(Vmaf<Backend>Context **ctx, int device_index);
/* Release all resources owned by ctx.
*
* Safe to call with ctx == NULL (no-op).
*/
void vmaf_<backend>_context_destroy(Vmaf<Backend>Context *ctx);
/* Return the number of available devices of this type.
*
* Returns >= 0 number of devices (0 means "none found, but
* device discovery succeeded").
* Returns -ENODEV if device discovery itself failed.
*/
int vmaf_<backend>_device_count(void);
Error-return contract¶
| Condition | Return value |
|---|---|
| Success | 0 |
ctx or required pointer is NULL | -EINVAL |
| Device index out of range | -EINVAL |
| No device found | -ENODEV |
| Allocation failure | -ENOMEM |
| Runtime / driver error | -EIO (fallback; prefer a more specific errno where the underlying runtime maps cleanly) |
context_destroy never returns an error because it is void. A cleanup error that cannot be handled silently is logged through vmaf_log before it is discarded.
Where the implementations deviate¶
The Metal context implements the table (core/src/metal/common.mm returns -EINVAL for a NULL out-pointer and -ENODEV when the device index does not match a supported GPU). The HIP context does not yet:
vmaf_hip_context_newonly rejects a NULL out-pointer and never inspectsdevice_index, so it returns neither-EINVALnor-ENODEVfor a bad index (core/src/hip/common.c).vmaf_hip_device_countreturns0whenhipGetDeviceCountfails, not-ENODEV. The callers branch oncount > 0.
These are code gaps against the contract, not documented behaviour.
Opaque-handle accessors (optional)¶
Where a backend context owns GPU handles that consumer translation units need (for example a queue or a device handle), expose them through typed accessors:
/* Returns the opaque handle; NULL for a NULL ctx.
* Lifetime: tied to ctx — callers must NOT release the returned pointer. */
void *vmaf_<backend>_context_<handle_name>(Vmaf<Backend>Context *ctx);
Metal is the reference: vmaf_metal_context_device_handle and vmaf_metal_context_queue_handle (ADR-0361). The accessor returns an opaque pointer so pure-C consumers never depend on the struct layout.
Checklist for new backends¶
-
vmaf_<backend>_context_newis declared incore/src/<backend>/common.h. -
vmaf_<backend>_context_destroyaccepts NULL without crashing. -
vmaf_<backend>_device_countreturns>= 0on success,-ENODEVon discovery failure. -
vmaf_<backend>_context_newvalidatesdevice_indexand returns-EINVAL/-ENODEVas in the table above. - Error returns use POSIX errno values (not raw runtime codes).
- Opaque-handle accessors follow the
void *+ lifetime-tied pattern. - The public state API (
libvmaf_<backend>.h) is installed throughcore/include/libvmaf/meson.build.