Show a patch.

GET /api/patches/13863/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 13863,
    "url": "https://patchwork.libcamera.org/api/patches/13863/?format=api",
    "web_url": "https://patchwork.libcamera.org/patch/13863/",
    "project": {
        "id": 1,
        "url": "https://patchwork.libcamera.org/api/projects/1/?format=api",
        "name": "libcamera",
        "link_name": "libcamera",
        "list_id": "libcamera_core",
        "list_email": "libcamera-devel@lists.libcamera.org",
        "web_url": "",
        "scm_url": "",
        "webscm_url": ""
    },
    "msgid": "<20210916132015.1790-3-david.plowman@raspberrypi.com>",
    "date": "2021-09-16T13:20:15",
    "name": "[libcamera-devel,RFC,2/2] libcamera: pipeline_handler: raspberrypi: Handle the new SensorMode hint",
    "commit_ref": null,
    "pull_url": null,
    "state": "not-applicable",
    "archived": false,
    "hash": "faa36e047930e11ef5fd5f8b89093a399965a4d3",
    "submitter": {
        "id": 42,
        "url": "https://patchwork.libcamera.org/api/people/42/?format=api",
        "name": "David Plowman",
        "email": "david.plowman@raspberrypi.com"
    },
    "delegate": null,
    "mbox": "https://patchwork.libcamera.org/patch/13863/mbox/",
    "series": [
        {
            "id": 2538,
            "url": "https://patchwork.libcamera.org/api/series/2538/?format=api",
            "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=2538",
            "date": "2021-09-16T13:20:13",
            "name": "Sensor mode hints",
            "version": 1,
            "mbox": "https://patchwork.libcamera.org/series/2538/mbox/"
        }
    ],
    "comments": "https://patchwork.libcamera.org/api/patches/13863/comments/",
    "check": "pending",
    "checks": "https://patchwork.libcamera.org/api/patches/13863/checks/",
    "tags": {},
    "headers": {
        "Return-Path": "<libcamera-devel-bounces@lists.libcamera.org>",
        "X-Original-To": "parsemail@patchwork.libcamera.org",
        "Delivered-To": "parsemail@patchwork.libcamera.org",
        "Received": [
            "from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id B7982BF01C\n\tfor <parsemail@patchwork.libcamera.org>;\n\tThu, 16 Sep 2021 13:20:27 +0000 (UTC)",
            "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 86B816918E;\n\tThu, 16 Sep 2021 15:20:27 +0200 (CEST)",
            "from mail-wr1-x42b.google.com (mail-wr1-x42b.google.com\n\t[IPv6:2a00:1450:4864:20::42b])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 1615469183\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 16 Sep 2021 15:20:25 +0200 (CEST)",
            "by mail-wr1-x42b.google.com with SMTP id d21so9416446wra.12\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 16 Sep 2021 06:20:25 -0700 (PDT)",
            "from pi4-davidp.pitowers.org\n\t([2a00:1098:3142:14:1ce1:9965:4328:89c4])\n\tby smtp.gmail.com with ESMTPSA id\n\tf3sm3233425wmj.28.2021.09.16.06.20.22\n\t(version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n\tThu, 16 Sep 2021 06:20:22 -0700 (PDT)"
        ],
        "Authentication-Results": "lancelot.ideasonboard.com;\n\tdkim=fail reason=\"signature verification failed\" (2048-bit key;\n\tunprotected) header.d=raspberrypi.com header.i=@raspberrypi.com\n\theader.b=\"ctmHWQC2\"; dkim-atps=neutral",
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=raspberrypi.com; s=google;\n\th=from:to:cc:subject:date:message-id:in-reply-to:references\n\t:mime-version:content-transfer-encoding;\n\tbh=ckvChxHSv2XABFiPTYYH31puU2SrDdDENu7zj+2KEZM=;\n\tb=ctmHWQC2vKGoY9OWd+GzyHuvEzWYt4QL8v5DGQwkAhsbrSzvSbcfHmcPddcxhAGA6S\n\trIYWgCNPMwgRw05rtNVVNjGwk0MWXwjaYOtwoUjGSCdV/dAqZ9wcM1ISU+/WM1CYCxjh\n\tE6rkjwet5IFL6GqO88wI5P7aVPBlX3pO0azaPEq6Kws9/T7yv9tyHpRvsj9lrNsqSnA7\n\t+FJMmQ+HAIDG0ETYdkwezZDWggINo4pGaU2GBt/HkVH5M3j6WwEDuBYLn3IPKDQAS+Oh\n\tkjIdw/xzgG0r5nuKNtDxvzEet8NdukeEHEWHt/w6lGB3bjwen+8hIYNXGgapXOIrZvtG\n\tVI4A==",
        "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20210112;\n\th=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to\n\t:references:mime-version:content-transfer-encoding;\n\tbh=ckvChxHSv2XABFiPTYYH31puU2SrDdDENu7zj+2KEZM=;\n\tb=rS4gSJiWO1RctyT48YUdxq26r5bAx0tlzVdzKPRAj8sDEQi7oWsTYXIpULIWLmMjbz\n\tqPStaeFbqtkapiHG4b5iTYxwxeTm8gvatGdmqbtXQYklBAFLbpsnbRGPNkZpy/K3NHKg\n\tabAzoKWDqKBQBDK6ukQGUXeato2+6HPCjwEgTHCQtlko+iJtgHXOj6pN+LceX8gdXm/8\n\twDFBdSbbX/Zra68iiRuMGHds414FrI7OIvcGoZ4p5+IqLgqw4Xhxn/FV02PrLoVud3Ww\n\txaWIjcACdOVyepTfl2kEGfaDoVuSpuoLv0iclcD086/PlZSFauGpuDDjkPEQrMaMs+n9\n\th3XQ==",
        "X-Gm-Message-State": "AOAM5308V2W2FnVI0wziNuMhuQNrfyfYy43jyPwY87gR5L4tt6rLvJPF\n\toPG/SD0M94ArPBx+2e54faj3/QwRTUcJYYcY",
        "X-Google-Smtp-Source": "ABdhPJxehE3lxJkePc/K0+Mewp7NWGXdPHZtrVYozKa0eFb4Syt0wq23bElpsTDEIW60Hd3J/3XTIA==",
        "X-Received": "by 2002:a5d:53c8:: with SMTP id a8mr6041273wrw.168.1631798424373;\n\tThu, 16 Sep 2021 06:20:24 -0700 (PDT)",
        "From": "David Plowman <david.plowman@raspberrypi.com>",
        "To": "libcamera-devel@lists.libcamera.org",
        "Date": "Thu, 16 Sep 2021 14:20:15 +0100",
        "Message-Id": "<20210916132015.1790-3-david.plowman@raspberrypi.com>",
        "X-Mailer": "git-send-email 2.20.1",
        "In-Reply-To": "<20210916132015.1790-1-david.plowman@raspberrypi.com>",
        "References": "<20210916132015.1790-1-david.plowman@raspberrypi.com>",
        "MIME-Version": "1.0",
        "Content-Transfer-Encoding": "8bit",
        "Subject": "[libcamera-devel] [RFC PATCH 2/2] libcamera: pipeline_handler:\n\traspberrypi: Handle the new SensorMode hint",
        "X-BeenThere": "libcamera-devel@lists.libcamera.org",
        "X-Mailman-Version": "2.1.29",
        "Precedence": "list",
        "List-Id": "<libcamera-devel.lists.libcamera.org>",
        "List-Unsubscribe": "<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>",
        "List-Archive": "<https://lists.libcamera.org/pipermail/libcamera-devel/>",
        "List-Post": "<mailto:libcamera-devel@lists.libcamera.org>",
        "List-Help": "<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>",
        "List-Subscribe": "<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>",
        "Errors-To": "libcamera-devel-bounces@lists.libcamera.org",
        "Sender": "\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"
    },
    "content": "If no SensorMode hint is supplied the code will fill it in using the\nstream condfigurations so as to give the same result as before.\n\nThen the SensorMode hint will be used in place of the largest output\nsize to guide the mode selection.\n\nSigned-off-by: David Plowman <david.plowman@raspberrypi.com>\n---\n .../pipeline/raspberrypi/raspberrypi.cpp      | 105 +++++++++++++-----\n 1 file changed, 78 insertions(+), 27 deletions(-)",
    "diff": "diff --git a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp\nindex 0bdfa727..5d1c5e64 100644\n--- a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp\n+++ b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp\n@@ -48,6 +48,12 @@ LOG_DEFINE_CATEGORY(RPI)\n \n namespace {\n \n+constexpr int PREFERRED_BIT_DEPTH = 12;\n+\n+constexpr double PENALTY_AR = 1500.0;\n+constexpr double PENALTY_BITDEPTH = 500.0;\n+constexpr double PENALTY_UNPACKED = 50.0;\n+\n bool isRaw(PixelFormat &pixFmt)\n {\n \t/*\n@@ -61,58 +67,91 @@ bool isRaw(PixelFormat &pixFmt)\n \treturn info.colourEncoding == PixelFormatInfo::ColourEncodingRAW;\n }\n \n-double scoreFormat(double desired, double actual)\n+struct Interval {\n+\tstatic Interval XRange(const SizeRange &s)\n+\t{\n+\t\treturn { static_cast<int>(s.min.width), static_cast<int>(s.max.width) };\n+\t}\n+\tstatic Interval YRange(const SizeRange &s)\n+\t{\n+\t\treturn { static_cast<int>(s.min.height), static_cast<int>(s.max.height) };\n+\t}\n+\tint min, max;\n+\tint separation(const Interval &other) const\n+\t{\n+\t\t/*\n+\t\t * The separation between two intervals is positive if \"this\" interval lies\n+\t\t * wholly above \"other\", zero if they overlap and otherwise negative.\n+\t\t */\n+\t\tif (min > other.max)\n+\t\t\treturn min - other.max;\n+\t\telse if (other.min > max)\n+\t\t\treturn max - other.min;\n+\t\treturn 0;\n+\t}\n+\tint clamp(int value) const\n+\t{\n+\t\treturn std::clamp(value, min, max);\n+\t}\n+};\n+\n+double sizeAr(const Size &s)\n+{\n+\treturn static_cast<double>(s.width) / s.height;\n+}\n+\n+double scoreSeparation(int separation)\n {\n-\tdouble score = desired - actual;\n+\tdouble score = separation;\n \t/* Smaller desired dimensions are preferred. */\n-\tif (score < 0.0)\n+\tif (score < 0)\n \t\tscore = (-score) / 8;\n \t/* Penalise non-exact matches. */\n-\tif (actual != desired)\n+\tif (separation != 0)\n \t\tscore *= 2;\n \n \treturn score;\n }\n \n V4L2DeviceFormat findBestMode(V4L2VideoDevice::Formats &formatsMap,\n-\t\t\t      const Size &req)\n+\t\t\t      const SensorMode &hintMode)\n {\n \tdouble bestScore = std::numeric_limits<double>::max(), score;\n \tV4L2DeviceFormat bestMode;\n \n-#define PENALTY_AR\t\t1500.0\n-#define PENALTY_8BIT\t\t2000.0\n-#define PENALTY_10BIT\t\t1000.0\n-#define PENALTY_12BIT\t\t   0.0\n-#define PENALTY_UNPACKED\t 500.0\n+\tLOG(RPI, Info) << \"Hinted mode: \" << hintMode.toString();\n+\n+\tInterval hintX = Interval::XRange(hintMode.sizeRange);\n+\tInterval hintY = Interval::YRange(hintMode.sizeRange);\n+\tdouble hintAr = sizeAr(hintMode.sizeRange.max);\n \n-\t/* Calculate the closest/best mode from the user requested size. */\n+\t/* Calculate the closest/best mode from the hinted sensor mode. */\n \tfor (const auto &iter : formatsMap) {\n \t\tV4L2PixelFormat v4l2Format = iter.first;\n \t\tconst PixelFormatInfo &info = PixelFormatInfo::info(v4l2Format);\n \n \t\tfor (const SizeRange &sz : iter.second) {\n-\t\t\tdouble modeWidth = sz.contains(req) ? req.width : sz.max.width;\n-\t\t\tdouble modeHeight = sz.contains(req) ? req.height : sz.max.height;\n-\t\t\tdouble reqAr = static_cast<double>(req.width) / req.height;\n-\t\t\tdouble modeAr = modeWidth / modeHeight;\n+\t\t\tInterval sensorX = Interval::XRange(sz);\n+\t\t\tInterval sensorY = Interval::YRange(sz);\n+\t\t\tdouble sensorAr = sizeAr(sz.max);\n+\n+\t\t\tdouble widthSeparation = hintX.separation(sensorX);\n+\t\t\tdouble heightSeparation = hintY.separation(sensorY);\n \n \t\t\t/* Score the dimensions for closeness. */\n-\t\t\tscore = scoreFormat(req.width, modeWidth);\n-\t\t\tscore += scoreFormat(req.height, modeHeight);\n-\t\t\tscore += PENALTY_AR * scoreFormat(reqAr, modeAr);\n+\t\t\tscore = scoreSeparation(widthSeparation);\n+\t\t\tscore += scoreSeparation(heightSeparation);\n+\t\t\tscore += PENALTY_AR * std::abs(hintAr - sensorAr);\n \n \t\t\t/* Add any penalties... this is not an exact science! */\n \t\t\tif (!info.packed)\n \t\t\t\tscore += PENALTY_UNPACKED;\n \n-\t\t\tif (info.bitsPerPixel == 12)\n-\t\t\t\tscore += PENALTY_12BIT;\n-\t\t\telse if (info.bitsPerPixel == 10)\n-\t\t\t\tscore += PENALTY_10BIT;\n-\t\t\telse if (info.bitsPerPixel == 8)\n-\t\t\t\tscore += PENALTY_8BIT;\n+\t\t\tint bitDifference = info.bitsPerPixel - hintMode.bitDepth;\n+\t\t\tscore += std::abs(bitDifference) * PENALTY_BITDEPTH;\n \n+\t\t\tint modeWidth = sensorX.clamp(hintMode.sizeRange.max.width);\n+\t\t\tint modeHeight = sensorY.clamp(hintMode.sizeRange.max.height);\n \t\t\tif (score <= bestScore) {\n \t\t\t\tbestScore = score;\n \t\t\t\tbestMode.fourcc = v4l2Format;\n@@ -353,7 +392,8 @@ CameraConfiguration::Status RPiCameraConfiguration::validate()\n \t\t\t * the user request.\n \t\t\t */\n \t\t\tV4L2VideoDevice::Formats fmts = data_->unicam_[Unicam::Image].dev()->formats();\n-\t\t\tV4L2DeviceFormat sensorFormat = findBestMode(fmts, cfg.size);\n+\t\t\tSensorMode mode(PREFERRED_BIT_DEPTH, SizeRange(cfg.size));\n+\t\t\tV4L2DeviceFormat sensorFormat = findBestMode(fmts, mode);\n \t\t\tint ret = data_->unicam_[Unicam::Image].dev()->tryFormat(&sensorFormat);\n \t\t\tif (ret)\n \t\t\t\treturn Invalid;\n@@ -484,11 +524,13 @@ CameraConfiguration *PipelineHandlerRPi::generateConfiguration(Camera *camera,\n \tunsigned int rawCount = 0;\n \tunsigned int outCount = 0;\n \tfor (const StreamRole role : roles) {\n+\t\tSensorMode mode;\n \t\tswitch (role) {\n \t\tcase StreamRole::Raw:\n \t\t\tsize = data->sensor_->resolution();\n \t\t\tfmts = data->unicam_[Unicam::Image].dev()->formats();\n-\t\t\tsensorFormat = findBestMode(fmts, size);\n+\t\t\tmode = SensorMode(PREFERRED_BIT_DEPTH, SizeRange(size));\n+\t\t\tsensorFormat = findBestMode(fmts, mode);\n \t\t\tpixelFormat = sensorFormat.fourcc.toPixelFormat();\n \t\t\tASSERT(pixelFormat.isValid());\n \t\t\tbufferCount = 2;\n@@ -598,9 +640,18 @@ int PipelineHandlerRPi::configure(Camera *camera, CameraConfiguration *config)\n \t\t}\n \t}\n \n+\t/*\n+\t * If any parts of the hinted sensor mode are unset, fill them in now.\n+\t */\n+\tif (config->sensorMode.bitDepth == 0)\n+\t\tconfig->sensorMode.bitDepth = PREFERRED_BIT_DEPTH;\n+\n+\tif (config->sensorMode.sizeRange.max == Size(0, 0))\n+\t\tconfig->sensorMode.sizeRange = SizeRange(rawStream ? sensorSize : maxSize);\n+\n \t/* First calculate the best sensor mode we can use based on the user request. */\n \tV4L2VideoDevice::Formats fmts = data->unicam_[Unicam::Image].dev()->formats();\n-\tV4L2DeviceFormat sensorFormat = findBestMode(fmts, rawStream ? sensorSize : maxSize);\n+\tV4L2DeviceFormat sensorFormat = findBestMode(fmts, config->sensorMode);\n \n \t/*\n \t * Unicam image output format. The ISP input format gets set at start,\n",
    "prefixes": [
        "libcamera-devel",
        "RFC",
        "2/2"
    ]
}