From fbf6511e3579b388ad56d91246d0cfdce9da6d4d Mon Sep 17 00:00:00 2001 From: Bas Nieuwenhuizen Date: Mon, 2 Nov 2020 02:09:11 +0100 Subject: [PATCH] gallium/va: Add support for PRIME_2 import. That way we can actually import surfaces with modifiers & metadata planes. Tested with patches for ffmpeg to use this with kmsgrab & modifiers. For AMD & multiplanar formats we always have 1 format plane = 1 memory plane, even with modifiers. Intel (non Gallium) does have 1 format plane is 2 memory planes for some modifiers with NV12. Currently with Gallium we don't really have info about layer/plane ordering so this manually orders things so that they align with Intel. This shouldn't impact other drivers as without modifiers with metadata planes this should give equivalent behavior to the old import path. Reviewed-and-Tested-by: Leo Liu Part-of: --- src/gallium/frontends/va/surface.c | 143 ++++++++++++++++++++++++++++- 1 file changed, 141 insertions(+), 2 deletions(-) diff --git a/src/gallium/frontends/va/surface.c b/src/gallium/frontends/va/surface.c index de42e5401ea..8c772d5544a 100644 --- a/src/gallium/frontends/va/surface.c +++ b/src/gallium/frontends/va/surface.c @@ -503,7 +503,8 @@ vlVaQuerySurfaceAttributes(VADriverContextP ctx, VAConfigID config_id, attribs[i].value.type = VAGenericValueTypeInteger; attribs[i].flags = VA_SURFACE_ATTRIB_GETTABLE | VA_SURFACE_ATTRIB_SETTABLE; attribs[i].value.value.i = VA_SURFACE_ATTRIB_MEM_TYPE_VA | - VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME; + VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME | + VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2; i++; attribs[i].type = VASurfaceAttribExternalBufferDescriptor; @@ -655,6 +656,127 @@ fail: return result; } +static VAStatus +surface_from_prime_2(VADriverContextP ctx, vlVaSurface *surface, + VADRMPRIMESurfaceDescriptor *desc, + struct pipe_video_buffer *templat) +{ + vlVaDriver *drv; + struct pipe_screen *pscreen; + struct pipe_resource res_templ; + struct winsys_handle whandle; + struct pipe_resource *resources[VL_NUM_COMPONENTS]; + enum pipe_format resource_formats[VL_NUM_COMPONENTS]; + unsigned num_format_planes, expected_planes, input_planes, plane; + VAStatus result; + + num_format_planes = util_format_get_num_planes(templat->buffer_format); + pscreen = VL_VA_PSCREEN(ctx); + drv = VL_VA_DRIVER(ctx); + + if (!desc || desc->num_layers >= 4 ||desc->num_objects == 0) + return VA_STATUS_ERROR_INVALID_PARAMETER; + + if (surface->templat.width != desc->width || + surface->templat.height != desc->height || + desc->num_layers < 1) + return VA_STATUS_ERROR_INVALID_PARAMETER; + + if (desc->num_layers != num_format_planes) + return VA_STATUS_ERROR_INVALID_PARAMETER; + + input_planes = 0; + for (unsigned i = 0; i < desc->num_layers; ++i) { + if (desc->layers[i].num_planes == 0 || desc->layers[i].num_planes > 4) + return VA_STATUS_ERROR_INVALID_PARAMETER; + + for (unsigned j = 0; j < desc->layers[i].num_planes; ++j) + if (desc->layers[i].object_index[j] >= desc->num_objects) + return VA_STATUS_ERROR_INVALID_PARAMETER; + + input_planes += desc->layers[i].num_planes; + } + + expected_planes = num_format_planes; + if (desc->objects[0].drm_format_modifier != DRM_FORMAT_MOD_INVALID && + pscreen->is_dmabuf_modifier_supported && + pscreen->is_dmabuf_modifier_supported(pscreen, desc->objects[0].drm_format_modifier, + templat->buffer_format, NULL) && + pscreen->get_dmabuf_modifier_planes) + expected_planes = pscreen->get_dmabuf_modifier_planes(pscreen, desc->objects[0].drm_format_modifier, + templat->buffer_format); + + if (input_planes != expected_planes) + return VA_STATUS_ERROR_INVALID_PARAMETER; + + vl_get_video_buffer_formats(pscreen, templat->buffer_format, resource_formats); + + memset(&res_templ, 0, sizeof(res_templ)); + res_templ.target = PIPE_TEXTURE_2D; + res_templ.last_level = 0; + res_templ.depth0 = 1; + res_templ.array_size = 1; + res_templ.width0 = desc->width; + res_templ.height0 = desc->height; + res_templ.bind = PIPE_BIND_SAMPLER_VIEW; + res_templ.usage = PIPE_USAGE_DEFAULT; + res_templ.format = templat->buffer_format; + + memset(&whandle, 0, sizeof(struct winsys_handle)); + whandle.type = WINSYS_HANDLE_TYPE_FD; + whandle.format = templat->buffer_format; + whandle.modifier = desc->objects[0].drm_format_modifier; + + // Create a resource for each plane. + memset(resources, 0, sizeof resources); + + /* This does a backwards walk to set the next pointers. It interleaves so + * that the main planes always come first and then the first compression metadata + * plane of each main plane etc. */ + plane = input_planes - 1; + for (int layer_plane = 3; layer_plane >= 0; --layer_plane) { + for (int layer = desc->num_layers - 1; layer >= 0; --layer) { + if (layer_plane >= desc->layers[layer].num_planes) + continue; + + if (plane < num_format_planes) + res_templ.format = resource_formats[plane]; + + whandle.stride = desc->layers[layer].pitch[layer_plane]; + whandle.offset = desc->layers[layer].offset[layer_plane]; + whandle.handle = desc->objects[desc->layers[layer].object_index[layer_plane]].fd; + whandle.plane = plane; + + resources[plane] = pscreen->resource_from_handle(pscreen, &res_templ, &whandle, + PIPE_HANDLE_USAGE_FRAMEBUFFER_WRITE); + if (!resources[plane]) { + result = VA_STATUS_ERROR_ALLOCATION_FAILED; + goto fail; + } + + /* After the resource gets created the resource now owns the next reference. */ + res_templ.next = NULL; + + if (plane) + pipe_resource_reference(&res_templ.next, resources[plane]); + --plane; + } + } + + surface->buffer = vl_video_buffer_create_ex2(drv->pipe, templat, resources); + if (!surface->buffer) { + result = VA_STATUS_ERROR_ALLOCATION_FAILED; + goto fail; + } + return VA_STATUS_SUCCESS; + +fail: + pipe_resource_reference(&res_templ.next, NULL); + for (int i = 0; i < VL_NUM_COMPONENTS; i++) + pipe_resource_reference(&resources[i], NULL); + return result; +} + VAStatus vlVaHandleSurfaceAllocate(vlVaDriver *drv, vlVaSurface *surface, struct pipe_video_buffer *templat, @@ -704,6 +826,7 @@ vlVaCreateSurfaces2(VADriverContextP ctx, unsigned int format, { vlVaDriver *drv; VASurfaceAttribExternalBuffers *memory_attribute; + VADRMPRIMESurfaceDescriptor *prime_desc; #ifdef HAVE_VA_SURFACE_ATTRIB_DRM_FORMAT_MODIFIERS const VADRMFormatModifierList *modifier_list; #endif @@ -736,6 +859,7 @@ vlVaCreateSurfaces2(VADriverContextP ctx, unsigned int format, /* Default. */ memory_attribute = NULL; + prime_desc = NULL; memory_type = VA_SURFACE_ATTRIB_MEM_TYPE_VA; expected_fourcc = 0; modifiers = NULL; @@ -758,6 +882,7 @@ vlVaCreateSurfaces2(VADriverContextP ctx, unsigned int format, switch (attrib_list[i].value.value.i) { case VA_SURFACE_ATTRIB_MEM_TYPE_VA: case VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME: + case VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2: memory_type = attrib_list[i].value.value.i; break; default: @@ -767,7 +892,10 @@ vlVaCreateSurfaces2(VADriverContextP ctx, unsigned int format, case VASurfaceAttribExternalBufferDescriptor: if (attrib_list[i].value.type != VAGenericValueTypePointer) return VA_STATUS_ERROR_INVALID_PARAMETER; - memory_attribute = (VASurfaceAttribExternalBuffers *)attrib_list[i].value.value.p; + if (memory_type == VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2) + prime_desc = (VADRMPRIMESurfaceDescriptor *)attrib_list[i].value.value.p; + else + memory_attribute = (VASurfaceAttribExternalBuffers *)attrib_list[i].value.value.p; break; #ifdef HAVE_VA_SURFACE_ATTRIB_DRM_FORMAT_MODIFIERS case VASurfaceAttribDRMFormatModifiers: @@ -809,6 +937,12 @@ vlVaCreateSurfaces2(VADriverContextP ctx, unsigned int format, expected_fourcc = memory_attribute->pixel_format; break; + case VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2: + if (!prime_desc) + return VA_STATUS_ERROR_INVALID_PARAMETER; + + expected_fourcc = prime_desc->fourcc; + break; default: assert(0); } @@ -878,6 +1012,11 @@ vlVaCreateSurfaces2(VADriverContextP ctx, unsigned int format, goto free_surf; break; + case VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2: + vaStatus = surface_from_prime_2(ctx, surf, prime_desc, &templat); + if (vaStatus != VA_STATUS_SUCCESS) + goto free_surf; + break; default: assert(0); }