From 70196f8065a1a39b119f15117e100be38ad4cb84 Mon Sep 17 00:00:00 2001 From: claude-noether Date: Tue, 12 May 2026 18:52:33 +0000 Subject: [PATCH] =?UTF-8?q?fresnel-fourier=20iter5b-=CE=B2=20Phase=207=20f?= =?UTF-8?q?ix-forward=20commit=20D:=20destination=5F*=20for=20vaapi-copy?= =?UTF-8?q?=20late-surface=20flow?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Phase 7 empirical: all 5 libva codecs returned all-zero because CreateContext's surfaces_ids[] walk was a no-op for ffmpeg-vaapi-copy which passes surfaces_count=0 to vaCreateContext (per the iter6 comment at context.c:262). Surfaces existed in driver_data's surface_heap but weren't in the param array → destination_* stayed at the zero initialization from CreateSurfaces2 β → BeginPicture's surface_bind_slot saw destination_planes_count=0 → no data assignment → copy_surface_to_image read all-zero. Fix: cache the format-uniform CAPTURE geometry in driver_data (fmt_valid, fmt_planes_count, fmt_buffers_count, fmt_format_height, fmt_sizes[], fmt_bytesperlines[]). Populate at CreateContext after v4l2_get_format(CAPTURE). Walk surface_heap (not just surfaces_ids[]) to fill every existing surface. Add lazy-fill in CreateSurfaces2 for surfaces created AFTER CreateContext. Invalidate cache in DestroyContext. New helper: surface_fill_format_uniform(driver_data, surface_object). Idempotent on destination_planes_count != 0. Signed-off-by: claude-noether --- src/context.c | 78 +++++++++++++++++++++++++++++---------------------- src/request.h | 21 ++++++++++++++ src/surface.c | 49 ++++++++++++++++++++++++++++++++ src/surface.h | 21 ++++++++++++++ 4 files changed, 135 insertions(+), 34 deletions(-) diff --git a/src/context.c b/src/context.c index 0ea781b..09706eb 100644 --- a/src/context.c +++ b/src/context.c @@ -54,7 +54,6 @@ VAStatus RequestCreateContext(VADriverContextP context, VAConfigID config_id, struct request_data *driver_data = context->pDriverData; struct object_config *config_object; struct object_context *context_object = NULL; - struct object_surface *surface_object; struct video_format *video_format; unsigned int destination_sizes[VIDEO_MAX_PLANES]; unsigned int destination_bytesperlines[VIDEO_MAX_PLANES]; @@ -65,7 +64,7 @@ VAStatus RequestCreateContext(VADriverContextP context, VAConfigID config_id, VAContextID id; VAStatus status; unsigned int output_type, capture_type; - unsigned int i, j; + unsigned int j; bool found; int rc; @@ -196,7 +195,7 @@ VAStatus RequestCreateContext(VADriverContextP context, VAConfigID config_id, /* * Compute format-uniform destination_* values. Same for all - * surfaces of this format; written once per surface here, never + * surfaces of this format; written once per surface, never * changed by BeginPicture's slot acquisition. */ if (video_format->v4l2_buffers_count == 1) { @@ -207,39 +206,44 @@ VAStatus RequestCreateContext(VADriverContextP context, VAConfigID config_id, } /* - * Walk surfaces_ids and populate the format-uniform fields on each - * object_surface. CreateSurfaces2 (β) left these zeroed. + * iter5b-β Commit D: cache the format-uniform CAPTURE geometry + * in driver_data. CreateSurfaces2 calls AFTER this CreateContext + * (ffmpeg vaapi-copy late-surface-allocation case) will lazy-fill + * via surface_fill_format_uniform(); the surface_heap walk below + * fills surfaces that pre-existed when CreateContext fired. */ - for (i = 0; i < (unsigned int)surfaces_count; i++) { - surface_object = SURFACE(driver_data, surfaces_ids[i]); - if (surface_object == NULL) { - status = VA_STATUS_ERROR_INVALID_SURFACE; - goto error; - } - surface_object->destination_planes_count = destination_planes_count; - surface_object->destination_buffers_count = - video_format->v4l2_buffers_count; + driver_data->fmt_planes_count = destination_planes_count; + driver_data->fmt_buffers_count = video_format->v4l2_buffers_count; + driver_data->fmt_format_height = format_height; + for (j = 0; j < destination_planes_count; j++) { + driver_data->fmt_sizes[j] = destination_sizes[j]; + driver_data->fmt_bytesperlines[j] = + destination_bytesperlines[j]; + } + driver_data->fmt_valid = true; - if (video_format->v4l2_buffers_count == 1) { - for (j = 0; j < destination_planes_count; j++) { - surface_object->destination_offsets[j] = - j > 0 ? destination_sizes[j - 1] : 0; - surface_object->destination_sizes[j] = - destination_sizes[j]; - surface_object->destination_bytesperlines[j] = - destination_bytesperlines[0]; - } - } else if (video_format->v4l2_buffers_count == destination_planes_count) { - for (j = 0; j < destination_planes_count; j++) { - surface_object->destination_offsets[j] = 0; - surface_object->destination_sizes[j] = - destination_sizes[j]; - surface_object->destination_bytesperlines[j] = - destination_bytesperlines[j]; - } - } else { - status = VA_STATUS_ERROR_ALLOCATION_FAILED; - goto error; + /* + * Walk the surface_heap (not just surfaces_ids[]) to populate + * destination_* on every existing surface. Pre-Commit-D we walked + * surfaces_ids[], which is empty for ffmpeg vaapi-copy consumers + * that call vaCreateContext with surfaces_count=0 — those surfaces + * exist in the heap but aren't in the param array. Walking the + * heap catches both flows. Late-created surfaces (after this + * CreateContext) fill via surface_fill_format_uniform in + * CreateSurfaces2's per-surface init. + */ + { + struct object_surface *surface_iter; + int heap_iter; + + surface_iter = (struct object_surface *) + object_heap_first(&driver_data->surface_heap, + &heap_iter); + while (surface_iter != NULL) { + surface_fill_format_uniform(driver_data, surface_iter); + surface_iter = (struct object_surface *) + object_heap_next(&driver_data->surface_heap, + &heap_iter); } } @@ -500,7 +504,13 @@ VAStatus RequestDestroyContext(VADriverContextP context, VAContextID context_id) * β doesn't have a last_output_{width,height,pixelformat} cache * (those fields are deleted). Each CreateContext is a fresh * S_FMT(OUTPUT) cycle. + * + * Commit D: invalidate the format-uniform cache so a CreateSurfaces2 + * call between DestroyContext and the next CreateContext doesn't + * lazy-fill with stale geometry from the now-torn-down session. + * The next CreateContext re-populates the cache. */ + driver_data->fmt_valid = false; return VA_STATUS_SUCCESS; } diff --git a/src/request.h b/src/request.h index 3ad29e9..37e6d49 100644 --- a/src/request.h +++ b/src/request.h @@ -90,7 +90,28 @@ struct request_data { * which is naturally one-shot per context cycle; no caching is * required. DestroyContext + next CreateContext rebuild from * scratch. + * + * iter5b-β Commit D: cache the format-uniform CAPTURE-side + * geometry from v4l2_get_format so CreateSurfaces2 can populate + * a newly-created surface's destination_* fields without + * re-querying the device. Set by CreateContext after the + * v4l2_get_format(CAPTURE) call; consumed by both: + * 1. CreateContext's surface_heap walk (fills surfaces that + * pre-exist when CreateContext fires); + * 2. CreateSurfaces2's per-surface init (fills surfaces + * created AFTER CreateContext, e.g. ffmpeg vaapi-copy + * pool dynamics where the consumer passes surfaces_count=0 + * to vaCreateContext and creates surfaces lazily). + * + * fmt_valid is true once CreateContext has populated the cache; + * CreateSurfaces2 only lazy-fills when fmt_valid is true. */ + bool fmt_valid; + unsigned int fmt_format_height; + unsigned int fmt_planes_count; + unsigned int fmt_buffers_count; + unsigned int fmt_sizes[VIDEO_MAX_PLANES]; + unsigned int fmt_bytesperlines[VIDEO_MAX_PLANES]; }; VAStatus VA_DRIVER_INIT_FUNC(VADriverContextP context); diff --git a/src/surface.c b/src/surface.c index a7d6a41..44b4325 100644 --- a/src/surface.c +++ b/src/surface.c @@ -115,6 +115,44 @@ void surface_unbind_slot(struct request_data *driver_data, surface_object->current_slot = NULL; } +/* + * iter5b-β Commit D: fill format-uniform destination_* on a surface + * from driver_data's CAPTURE-format cache. Idempotent: no-op if + * destination_planes_count is non-zero already. + */ +void surface_fill_format_uniform(struct request_data *driver_data, + struct object_surface *surface_object) +{ + unsigned int j; + + if (!driver_data->fmt_valid) + return; + if (surface_object->destination_planes_count != 0) + return; + + surface_object->destination_planes_count = driver_data->fmt_planes_count; + surface_object->destination_buffers_count = driver_data->fmt_buffers_count; + + if (driver_data->fmt_buffers_count == 1) { + for (j = 0; j < driver_data->fmt_planes_count; j++) { + surface_object->destination_offsets[j] = + j > 0 ? driver_data->fmt_sizes[j - 1] : 0; + surface_object->destination_sizes[j] = + driver_data->fmt_sizes[j]; + surface_object->destination_bytesperlines[j] = + driver_data->fmt_bytesperlines[0]; + } + } else if (driver_data->fmt_buffers_count == driver_data->fmt_planes_count) { + for (j = 0; j < driver_data->fmt_planes_count; j++) { + surface_object->destination_offsets[j] = 0; + surface_object->destination_sizes[j] = + driver_data->fmt_sizes[j]; + surface_object->destination_bytesperlines[j] = + driver_data->fmt_bytesperlines[j]; + } + } +} + VAStatus RequestCreateSurfaces2(VADriverContextP context, unsigned int format, unsigned int width, unsigned int height, VASurfaceID *surfaces_ids, @@ -173,6 +211,17 @@ VAStatus RequestCreateSurfaces2(VADriverContextP context, unsigned int format, surface_object->request_fd = -1; + /* + * iter5b-β Commit D: if CreateContext has already populated + * the format-uniform cache (driver_data->fmt_valid), fill + * the new surface's destination_* immediately. This covers + * the case where a consumer creates more surfaces AFTER + * CreateContext. The first batch of surfaces (created before + * CreateContext) gets filled by CreateContext's surface_heap + * walk; this lazy-fill handles late arrivals. + */ + surface_fill_format_uniform(driver_data, surface_object); + surfaces_ids[i] = id; } diff --git a/src/surface.h b/src/surface.h index 96f00c0..ada7994 100644 --- a/src/surface.h +++ b/src/surface.h @@ -165,6 +165,27 @@ VAStatus RequestExportSurfaceHandle(VADriverContextP context, VASurfaceID surface_id, uint32_t mem_type, uint32_t flags, void *descriptor); +/* + * iter5b-β Commit D: populate a surface's format-uniform destination_* + * fields (planes_count, buffers_count, offsets, sizes, bytesperlines) + * from driver_data's cached CAPTURE-side geometry. Idempotent: skip + * if already filled (destination_planes_count != 0). Caller must + * ensure driver_data->fmt_valid is true (CreateContext has run). + * + * Called by: + * - context.c::RequestCreateContext after v4l2_get_format(CAPTURE) + * populates the cache; walks the surface_heap and fills every + * existing surface (covers surfaces created before CreateContext, + * including the ffmpeg vaapi-copy case where surfaces_count=0 is + * passed but surfaces exist in the heap from earlier + * CreateSurfaces2 calls). + * - surface.c::RequestCreateSurfaces2 after surface allocation, + * covering the case where CreateContext fired before this + * CreateSurfaces2 call (fmt cache is valid, fill immediately). + */ +void surface_fill_format_uniform(struct request_data *driver_data, + struct object_surface *surface_object); + /* * Iter2 Fix 3: bind / unbind a CAPTURE-pool slot to an object_surface. * Called from picture.c::RequestBeginPicture (acquire+bind) and