From patchwork Mon May 21 22:35:11 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mark Thompson X-Patchwork-Id: 9045 Delivered-To: ffmpegpatchwork@gmail.com Received: by 2002:a02:155:0:0:0:0:0 with SMTP id c82-v6csp806119jad; Mon, 21 May 2018 15:35:49 -0700 (PDT) X-Google-Smtp-Source: AB8JxZoRDebJxlVKmXbaQ1/8Vg514+62bjIp04QRtrgUNyEFDVHNCZUrJ5sskR9uj/J21uyWWb16 X-Received: by 2002:adf:a452:: with SMTP id e18-v6mr14342353wra.121.1526942149386; Mon, 21 May 2018 15:35:49 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1526942149; cv=none; d=google.com; s=arc-20160816; b=dwvC1zs9le1yIRHue+NZwnCKo6g6LhgAegvznT7P0ltTZayE7qntZK+fFbhkURvP3X U9WB2xUD2XJ3nCj6YSKR9pk965yFFFSmb3q9snJXB3b+1umnGt6QIyuIi1aC0OMi0nPh fjVrKhuEW4upVN97QZ28ZwysHYR9Lwp54F9hLtJDPLqu0jopAnRbviy8vZ5yRZRkBWGO KGZIYSZBGWFyq3xBsMn0JXjTKymO4gKzKB01GUKjy8vaQbY078zkL18fIKSwbUn5HUhS K/ch/tmTniPeCQV7IfIaO1s4WjGUy96skfI5D2q/RT7lBt/jLTQzkCEVdkdO1NMUYVVj V46Q== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:mime-version:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:references:in-reply-to:message-id:date :to:from:dkim-signature:delivered-to:arc-authentication-results; bh=7OsiEy7u4knHWFy5oaOljKaIhhcMt5UvFsgp2Q7/Eic=; b=XK9l89MDRdinQcbZBXINtDF7gDKMg4HPDdbJb6hOPjbuhRXpU85PTiVJrplN2sL/WU Nm4F/BtM6PtUOl8lWUgSmhOLAGaAhL7DkXQO2TWfuTw+DCmDFZ2k8z9KThxaXF09ql7p eZkG3TEA9kjbVfV5DpdYmh/FleG8E19yAYt6h+EAeTOrquQedfUBQgloqICSpSAy8dv/ 6UGoE24uARXFl3MOxShGx2c07A8iNwqnTg8X1v+sYeEi1kUbibeTIsu+ubUmU3cFXqNt 31aKZZAG6D0wVxEq4vHNWUKqMY9FRK2ZeCvB64p9eudRJ+wqyqOjZeUserghGwc/lNpr o9LQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@jkqxz-net.20150623.gappssmtp.com header.s=20150623 header.b=fJq1Ulai; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id 7-v6si12578496wrg.438.2018.05.21.15.35.49; Mon, 21 May 2018 15:35:49 -0700 (PDT) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@jkqxz-net.20150623.gappssmtp.com header.s=20150623 header.b=fJq1Ulai; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id E87E6689CE0; Tue, 22 May 2018 01:34:45 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-wr0-f169.google.com (mail-wr0-f169.google.com [209.85.128.169]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 924B16805E6 for ; Tue, 22 May 2018 01:34:38 +0300 (EEST) Received: by mail-wr0-f169.google.com with SMTP id v60-v6so17481236wrc.7 for ; Mon, 21 May 2018 15:35:19 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=jkqxz-net.20150623.gappssmtp.com; s=20150623; h=from:to:subject:date:message-id:in-reply-to:references; bh=CeeFXUTiJDg6h50bj7KbglUb45la4PTpMUNKunMa1+g=; b=fJq1UlaiMlLJ5iveC1YArQn3M2NG17memgtJb7zGZP+on/X7VqaTrAWLdNE7JsqjmB n8CvGYINGo4rWneCAGAsJysO53cI2qbYjgrg4+YoB6e/1sLauLFDCjtx8rYglBlQC6PW Mw421g645CGaFwF5PX/OIXxno1nphnj3B0EeCY5BXyKJQGZcZGt4F6O8qs1dTnz6cnqb DmE+Vpit35GYwbfXEGgLe0VriYf9RhhBDzCRxM5fQv9B7gqXLmemVJWgJIpVvGfxL46B fZaFKzpI83yPmQnQZTJxY4PrV+udooUMix2bunvQd5rQEFo/H7GBCsVlhkPlkYbvuXeK LRuw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references; bh=CeeFXUTiJDg6h50bj7KbglUb45la4PTpMUNKunMa1+g=; b=eUCRA1oVKdKfePwro88cKW/zf7MWCeFBWPghpy/KltKHlFgVbaFdc7da8dYoEwUgKR 7gJjjOtArR5d04L1sNSqQYszZrMi6c0nCGXe6l/Wmfvnv23H/1gy7zvSsYRpPucGjCOX jfvlI16xAwroV9u3j4nt9buiXD/TALAu6qLhySywELE3YodIBRw5HBRul7pf5IkdYN8V 94jSReHx27k/4AKpOukXSmvPlXiFeURiGV8DI3YHQPWeSacq/rQgb4LQURaJrMc+GG/M u9CRU4KoqcCqp8/N3IbTQ7dPn6uDTLSbPDx4bT82rE3h3FTu1LAyc+bdAbB0A5hLGkcV 3NVw== X-Gm-Message-State: ALKqPweuSSJdSMhnOhZWGwL87T7xydjt/8ld7OrDYM8Jeqib78RKo6Oy 4/49jj3cvDjpMVfjy6kHqxj0GT6p X-Received: by 2002:adf:b685:: with SMTP id j5-v6mr17699071wre.10.1526942118556; Mon, 21 May 2018 15:35:18 -0700 (PDT) Received: from rywe.jkqxz.net (cpc91242-cmbg18-2-0-cust650.5-4.cable.virginm.net. [82.8.130.139]) by smtp.gmail.com with ESMTPSA id e7-v6sm15995209wrn.88.2018.05.21.15.35.17 for (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Mon, 21 May 2018 15:35:17 -0700 (PDT) From: Mark Thompson To: ffmpeg-devel@ffmpeg.org Date: Mon, 21 May 2018 23:35:11 +0100 Message-Id: <20180521223511.20395-4-sw@jkqxz.net> X-Mailer: git-send-email 2.16.3 In-Reply-To: <20180521223511.20395-1-sw@jkqxz.net> References: <20180521223511.20395-1-sw@jkqxz.net> Subject: [FFmpeg-devel] [PATCH 4/4] hwcontext: Add test for frames context creation and transfer X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches MIME-Version: 1.0 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" Creates a frames context of every available type and attempts to upload/download frame data to each, checking that the output is the same as the input in each case. --- This test passes with VDPAU and OpenCL, but may fail for others. For VAAPI the main issue is that there isn't really a consistent way to determine what formats are supported as surfaces, as images and for upload/download. With the i965 driver: * YUV 4:2:2 planar works as surfaces and images but can't be downloaded. * Greyscale images can't be created. * Uploading RGB and then downloading it again gives different results. It looks like it might have gone via 4:2:0 somewhere? * With older hardware (Haswell), P010 appears to be supported but always fails. With the Mesa driver: * Uploading UYVY and then downloading it doesn't match halfway through the image. Halfway suggests another consequence of the separated fields madness, I didn't look very carefully. * It aborts when asked to make a YUYV (YUY2) surface. This should be easily fixable in the driver. DXVA2 can fail because format support is not checked at init time (see patch 1/4 here for OpenCL). Should that be changed? D3D11 and libmfx require fixed-size pools to create, which I don't attempt to deal with here. Any thoughts invited. - Mark libavutil/Makefile | 1 + libavutil/tests/hwframes.c | 404 +++++++++++++++++++++++++++++++++++++++++++++ tests/fate/hw.mak | 5 + 3 files changed, 410 insertions(+) create mode 100644 libavutil/tests/hwframes.c diff --git a/libavutil/Makefile b/libavutil/Makefile index d0632f16a6..59b8d52e04 100644 --- a/libavutil/Makefile +++ b/libavutil/Makefile @@ -207,6 +207,7 @@ TESTPROGS = adler32 \ hash \ hmac \ hwdevice \ + hwframes \ integer \ imgutils \ lfg \ diff --git a/libavutil/tests/hwframes.c b/libavutil/tests/hwframes.c new file mode 100644 index 0000000000..32e3cdbdf9 --- /dev/null +++ b/libavutil/tests/hwframes.c @@ -0,0 +1,404 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "libavutil/hwcontext.h" +#include "libavutil/imgutils.h" +#include "libavutil/pixdesc.h" + +static int test_frames_transfer(AVBufferRef *frames_ref) +{ + AVHWFramesContext *frames = (AVHWFramesContext*)frames_ref->data; + const AVPixFmtDescriptor *desc; + AVFrame *src, *hw, *dst; + int planes, line_width[4], plane_height[4]; + int c, p, x, y, err; + uint8_t val; + + desc = av_pix_fmt_desc_get(frames->sw_format); + if (!desc) + return -1; + + err = av_image_fill_linesizes(line_width, + frames->sw_format, frames->width); + if (err < 0) { + fprintf(stderr, "Failed to determine line widths for format %s.\n", + desc->name); + return -1; + } + planes = 0; + for (c = 0; c < desc->nb_components; c++) + planes = FFMAX(planes, desc->comp[c].plane + 1); + for (p = 0; p < planes; p++) { + plane_height[p] = AV_CEIL_RSHIFT(frames->height, + desc->log2_chroma_h); + } + + src = av_frame_alloc(); + hw = av_frame_alloc(); + dst = av_frame_alloc(); + if (!src || !hw || !dst) { + fprintf(stderr, "Failed to allocate frames.\n"); + err = -1; + goto fail; + } + + src->format = dst->format = frames->sw_format; + src->width = dst->width = frames->width; + src->height = dst->height = frames->height; + + err = av_frame_get_buffer(src, 0); + if (err < 0) { + fprintf(stderr, "Failed to allocate source frame data.\n"); + err = -1; + goto fail; + } + err = av_hwframe_get_buffer(frames_ref, hw, 0); + if (err < 0) { + fprintf(stderr, "Failed to allocate hardware frame.\n"); + err = -1; + goto fail; + } + err = av_frame_get_buffer(dst, 0); + if (err < 0) { + fprintf(stderr, "Failed to allocate destination frame data.\n"); + err = -1; + goto fail; + } + + val = 0; + for (p = 0; p < planes; p++) { + for (y = 0; y < plane_height[p]; y++) { + for (x = 0; x < line_width[p]; x++) { + src->data[p][y * src->linesize[p] + x] = val; + val += 127; + } + } + } + + err = av_hwframe_transfer_data(hw, src, 0); + if (err < 0) { + fprintf(stderr, "Failed to transfer data to hardware frame: %d.\n", + err); + err = -1; + goto fail; + } + + err = av_hwframe_transfer_data(dst, hw, 0); + if (err < 0) { + fprintf(stderr, "Failed to transfer data to hardware frame: %d.\n", + err); + err = -1; + goto fail; + } + + for (p = 0; p < planes; p++) { + for (y = 0; y < plane_height[p]; y++) { + for (x = 0; x < line_width[p]; x++) { + if (src->data[p][y * src->linesize[p] + x] != + dst->data[p][y * dst->linesize[p] + x]) { + fprintf(stderr, "Downloaded frame does not match " + "uploaded frame: differs at plane %d line %d " + "byte %d: wrote %d read %d.\n", p, y, x, + src->data[p][y * src->linesize[p] + x], + dst->data[p][y * dst->linesize[p] + x]); + err = -1; + goto fail; + } + } + } + } + + fprintf(stderr, "Transfer successful for %s %dx%d.\n", + desc->name, frames->width, frames->height); + +fail: + av_frame_free(&src); + av_frame_free(&hw); + av_frame_free(&dst); + return err; +} + +static void test_free_function(AVHWFramesContext *frames) +{ + int *free_check = frames->user_opaque; + + *free_check = 1; +} + +static int test_format(AVBufferRef *device_ref, enum AVPixelFormat hw_format, + enum AVPixelFormat sw_format, int expect_failure) +{ + AVBufferRef *frames_ref; + AVHWFramesContext *frames; + const AVPixFmtDescriptor *hw_desc, *sw_desc; + int err, free_check; + + hw_desc = av_pix_fmt_desc_get(hw_format); + sw_desc = av_pix_fmt_desc_get(sw_format); + if (!hw_desc || !sw_desc) { + fprintf(stderr, "Frame format %d / %d is invalid.\n", + hw_format, sw_format); + return -1; + } + + frames_ref = av_hwframe_ctx_alloc(device_ref); + if (!frames_ref) { + fprintf(stderr, "Failed to allocate frames context.\n"); + return -1; + } + + frames = (AVHWFramesContext*)frames_ref->data; + + frames->format = hw_format; + frames->sw_format = sw_format; + + frames->width = 640; + frames->height = 480; + + free_check = 0; + frames->free = &test_free_function; + frames->user_opaque = &free_check; + + err = av_hwframe_ctx_init(frames_ref); + if (err < 0) { + av_buffer_unref(&frames_ref); + if (!expect_failure) { + fprintf(stderr, "Failed to create frames with format %s / %s.\n", + hw_desc->name, sw_desc->name); + return -1; + } else { + return 0; + } + } + if (expect_failure) { + fprintf(stderr, "Successfully created frames with format %s / %s, " + "but expected to fail.\n", hw_desc->name, sw_desc->name); + // Continue anyway. + } + + fprintf(stderr, "Frames created with format %s / %s.\n", + hw_desc->name, sw_desc->name); + + err = test_frames_transfer(frames_ref); + + if (free_check != 0) { + fprintf(stderr, "Free check value incorrect before freeing: %d.\n", + free_check); + err = -1; + } + av_buffer_unref(&frames_ref); + if (free_check != 1) { + fprintf(stderr, "Free check value incorrect after freeing: %d.\n", + free_check); + err = -1; + } + + return err; +} + +static enum AVPixelFormat test_format_list[] = { + // YUV 4:2:0 formats; + AV_PIX_FMT_YUV420P, + AV_PIX_FMT_NV12, + AV_PIX_FMT_YUV420P10, + AV_PIX_FMT_P010, + AV_PIX_FMT_YUV420P16, + AV_PIX_FMT_P016, + // YUV 4:2:2 formats. + AV_PIX_FMT_YUYV422, + AV_PIX_FMT_UYVY422, + AV_PIX_FMT_YUV422P, + AV_PIX_FMT_YUV422P10, + AV_PIX_FMT_YUV422P16, + // YUV 4:4:4 formats. + AV_PIX_FMT_YUV444P, + AV_PIX_FMT_YUV444P10, + AV_PIX_FMT_YUV444P16, + // Packed RGB / RGBA. + AV_PIX_FMT_RGBA, + AV_PIX_FMT_BGRA, + AV_PIX_FMT_RGB0, + AV_PIX_FMT_BGR0, + // Planar RGB, + AV_PIX_FMT_GBRP, + AV_PIX_FMT_GBRP10, + AV_PIX_FMT_GBRP16, + // A valid format which won't be supported in hardware. + AV_PIX_FMT_PAL8, +}; + +static int test_device(enum AVHWDeviceType type, const char *name, + const char *device, AVDictionary *opts, int flags) +{ + AVBufferRef *ref; + AVHWFramesConstraints *constraints = NULL; + enum AVPixelFormat hw_format, sw_format; + int i, j, k, failures, err; + + err = av_hwdevice_ctx_create(&ref, type, device, opts, flags); + if (err < 0) { + fprintf(stderr, "Failed to create %s device: %d.\n", name, err); + return 1; + } + + constraints = av_hwdevice_get_hwframe_constraints(ref, NULL); + if (!constraints || !constraints->valid_hw_formats) { + fprintf(stderr, "Device does not implement constraints.\n"); + err = 1; + goto done; + } + + failures = 0; + for (i = 0; constraints->valid_hw_formats[i] != AV_PIX_FMT_NONE; i++) { + hw_format = constraints->valid_hw_formats[i]; + + if (constraints->valid_sw_formats) { + for (j = 0; constraints->valid_sw_formats[j] != + AV_PIX_FMT_NONE; j++) { + sw_format = constraints->valid_sw_formats[j]; + + err = test_format(ref, hw_format, sw_format, 0); + if (err < 0) + ++failures; + } + } + for (j = 0; j < FF_ARRAY_ELEMS(test_format_list); j++) { + sw_format = test_format_list[j]; + for (k = 0; constraints->valid_sw_formats[k] != + AV_PIX_FMT_NONE; k++) { + if (constraints->valid_sw_formats[k] == sw_format) + sw_format = AV_PIX_FMT_NONE; + } + if (sw_format == AV_PIX_FMT_NONE) + continue; + + err = test_format(ref, hw_format, sw_format, 1); + if (err < 0) + ++failures; + } + } + + err = failures ? -1 : 0; +done: + av_hwframe_constraints_free(&constraints); + av_buffer_unref(&ref); + return err; +} + +static const struct { + enum AVHWDeviceType type; + const char *possible_devices[5]; +} test_devices[] = { + { AV_HWDEVICE_TYPE_CUDA, + { "0", "1", "2" } }, + { AV_HWDEVICE_TYPE_DRM, + { "/dev/dri/card0", "/dev/dri/card1", + "/dev/dri/renderD128", "/dev/dri/renderD129" } }, + { AV_HWDEVICE_TYPE_DXVA2, + { "0", "1", "2" } }, + { AV_HWDEVICE_TYPE_D3D11VA, + { "0", "1", "2" } }, + { AV_HWDEVICE_TYPE_OPENCL, + { "0.0", "0.1", "1.0", "1.1" } }, + { AV_HWDEVICE_TYPE_VAAPI, + { "/dev/dri/renderD128", "/dev/dri/renderD129", ":0" } }, +}; + +static int test_device_type(enum AVHWDeviceType type) +{ + enum AVHWDeviceType check; + const char *name; + int i, j, found, err; + + name = av_hwdevice_get_type_name(type); + if (!name) { + fprintf(stderr, "No name available for device type %d.\n", type); + return -1; + } + + check = av_hwdevice_find_type_by_name(name); + if (check != type) { + fprintf(stderr, "Type %d maps to name %s maps to type %d.\n", + type, name, check); + return -1; + } + + found = 0; + + err = test_device(type, name, NULL, NULL, 0); + if (err < 0) { + fprintf(stderr, "Test failed for %s with default options.\n", name); + return -1; + } + if (err == 0) { + fprintf(stderr, "Test passed for %s with default options.\n", name); + ++found; + } + + for (i = 0; i < FF_ARRAY_ELEMS(test_devices); i++) { + if (test_devices[i].type != type) + continue; + + for (j = 0; test_devices[i].possible_devices[j]; j++) { + err = test_device(type, name, + test_devices[i].possible_devices[j], + NULL, 0); + if (err < 0) { + fprintf(stderr, "Test failed for %s with device %s.\n", + name, test_devices[i].possible_devices[j]); + return -1; + } + if (err == 0) { + fprintf(stderr, "Test passed for %s with device %s.\n", + name, test_devices[i].possible_devices[j]); + ++found; + } + } + } + + return !found; +} + +int main(void) +{ + enum AVHWDeviceType type = AV_HWDEVICE_TYPE_NONE; + int pass, fail, skip, err; + + pass = fail = skip = 0; + while (1) { + type = av_hwdevice_iterate_types(type); + if (type == AV_HWDEVICE_TYPE_NONE) + break; + + err = test_device_type(type); + if (err == 0) + ++pass; + else if (err < 0) + ++fail; + else + ++skip; + } + + fprintf(stderr, "Attempted to test %d device types: " + "%d passed, %d failed, %d skipped.\n", + pass + fail + skip, pass, fail, skip); + + return fail > 0; +} diff --git a/tests/fate/hw.mak b/tests/fate/hw.mak index d606cdeab6..a46b289b67 100644 --- a/tests/fate/hw.mak +++ b/tests/fate/hw.mak @@ -3,4 +3,9 @@ fate-hwdevice: libavutil/tests/hwdevice$(EXESUF) fate-hwdevice: CMD = run libavutil/tests/hwdevice fate-hwdevice: CMP = null +FATE_HWCONTEXT += fate-hwframes +fate-hwframes: libavutil/tests/hwframes$(EXESUF) +fate-hwframes: CMD = run libavutil/tests/hwframes +fate-hwframes: CMP = null + FATE_HW-$(CONFIG_AVUTIL) += $(FATE_HWCONTEXT)