From patchwork Thu Sep 16 13:20:15 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Plowman X-Patchwork-Id: 13863 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id B7982BF01C for ; Thu, 16 Sep 2021 13:20:27 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 86B816918E; Thu, 16 Sep 2021 15:20:27 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=raspberrypi.com header.i=@raspberrypi.com header.b="ctmHWQC2"; dkim-atps=neutral Received: from mail-wr1-x42b.google.com (mail-wr1-x42b.google.com [IPv6:2a00:1450:4864:20::42b]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 1615469183 for ; Thu, 16 Sep 2021 15:20:25 +0200 (CEST) Received: by mail-wr1-x42b.google.com with SMTP id d21so9416446wra.12 for ; Thu, 16 Sep 2021 06:20:25 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=raspberrypi.com; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=ckvChxHSv2XABFiPTYYH31puU2SrDdDENu7zj+2KEZM=; b=ctmHWQC2vKGoY9OWd+GzyHuvEzWYt4QL8v5DGQwkAhsbrSzvSbcfHmcPddcxhAGA6S rIYWgCNPMwgRw05rtNVVNjGwk0MWXwjaYOtwoUjGSCdV/dAqZ9wcM1ISU+/WM1CYCxjh E6rkjwet5IFL6GqO88wI5P7aVPBlX3pO0azaPEq6Kws9/T7yv9tyHpRvsj9lrNsqSnA7 +FJMmQ+HAIDG0ETYdkwezZDWggINo4pGaU2GBt/HkVH5M3j6WwEDuBYLn3IPKDQAS+Oh kjIdw/xzgG0r5nuKNtDxvzEet8NdukeEHEWHt/w6lGB3bjwen+8hIYNXGgapXOIrZvtG VI4A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=ckvChxHSv2XABFiPTYYH31puU2SrDdDENu7zj+2KEZM=; b=rS4gSJiWO1RctyT48YUdxq26r5bAx0tlzVdzKPRAj8sDEQi7oWsTYXIpULIWLmMjbz qPStaeFbqtkapiHG4b5iTYxwxeTm8gvatGdmqbtXQYklBAFLbpsnbRGPNkZpy/K3NHKg abAzoKWDqKBQBDK6ukQGUXeato2+6HPCjwEgTHCQtlko+iJtgHXOj6pN+LceX8gdXm/8 wDFBdSbbX/Zra68iiRuMGHds414FrI7OIvcGoZ4p5+IqLgqw4Xhxn/FV02PrLoVud3Ww xaWIjcACdOVyepTfl2kEGfaDoVuSpuoLv0iclcD086/PlZSFauGpuDDjkPEQrMaMs+n9 h3XQ== X-Gm-Message-State: AOAM5308V2W2FnVI0wziNuMhuQNrfyfYy43jyPwY87gR5L4tt6rLvJPF oPG/SD0M94ArPBx+2e54faj3/QwRTUcJYYcY X-Google-Smtp-Source: ABdhPJxehE3lxJkePc/K0+Mewp7NWGXdPHZtrVYozKa0eFb4Syt0wq23bElpsTDEIW60Hd3J/3XTIA== X-Received: by 2002:a5d:53c8:: with SMTP id a8mr6041273wrw.168.1631798424373; Thu, 16 Sep 2021 06:20:24 -0700 (PDT) Received: from pi4-davidp.pitowers.org ([2a00:1098:3142:14:1ce1:9965:4328:89c4]) by smtp.gmail.com with ESMTPSA id f3sm3233425wmj.28.2021.09.16.06.20.22 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 16 Sep 2021 06:20:22 -0700 (PDT) From: David Plowman 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 Subject: [libcamera-devel] [RFC PATCH 2/2] libcamera: pipeline_handler: raspberrypi: Handle the new SensorMode hint X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" If no SensorMode hint is supplied the code will fill it in using the stream condfigurations so as to give the same result as before. Then the SensorMode hint will be used in place of the largest output size to guide the mode selection. Signed-off-by: David Plowman --- .../pipeline/raspberrypi/raspberrypi.cpp | 105 +++++++++++++----- 1 file changed, 78 insertions(+), 27 deletions(-) diff --git a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp index 0bdfa727..5d1c5e64 100644 --- a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp +++ b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp @@ -48,6 +48,12 @@ LOG_DEFINE_CATEGORY(RPI) namespace { +constexpr int PREFERRED_BIT_DEPTH = 12; + +constexpr double PENALTY_AR = 1500.0; +constexpr double PENALTY_BITDEPTH = 500.0; +constexpr double PENALTY_UNPACKED = 50.0; + bool isRaw(PixelFormat &pixFmt) { /* @@ -61,58 +67,91 @@ bool isRaw(PixelFormat &pixFmt) return info.colourEncoding == PixelFormatInfo::ColourEncodingRAW; } -double scoreFormat(double desired, double actual) +struct Interval { + static Interval XRange(const SizeRange &s) + { + return { static_cast(s.min.width), static_cast(s.max.width) }; + } + static Interval YRange(const SizeRange &s) + { + return { static_cast(s.min.height), static_cast(s.max.height) }; + } + int min, max; + int separation(const Interval &other) const + { + /* + * The separation between two intervals is positive if "this" interval lies + * wholly above "other", zero if they overlap and otherwise negative. + */ + if (min > other.max) + return min - other.max; + else if (other.min > max) + return max - other.min; + return 0; + } + int clamp(int value) const + { + return std::clamp(value, min, max); + } +}; + +double sizeAr(const Size &s) +{ + return static_cast(s.width) / s.height; +} + +double scoreSeparation(int separation) { - double score = desired - actual; + double score = separation; /* Smaller desired dimensions are preferred. */ - if (score < 0.0) + if (score < 0) score = (-score) / 8; /* Penalise non-exact matches. */ - if (actual != desired) + if (separation != 0) score *= 2; return score; } V4L2DeviceFormat findBestMode(V4L2VideoDevice::Formats &formatsMap, - const Size &req) + const SensorMode &hintMode) { double bestScore = std::numeric_limits::max(), score; V4L2DeviceFormat bestMode; -#define PENALTY_AR 1500.0 -#define PENALTY_8BIT 2000.0 -#define PENALTY_10BIT 1000.0 -#define PENALTY_12BIT 0.0 -#define PENALTY_UNPACKED 500.0 + LOG(RPI, Info) << "Hinted mode: " << hintMode.toString(); + + Interval hintX = Interval::XRange(hintMode.sizeRange); + Interval hintY = Interval::YRange(hintMode.sizeRange); + double hintAr = sizeAr(hintMode.sizeRange.max); - /* Calculate the closest/best mode from the user requested size. */ + /* Calculate the closest/best mode from the hinted sensor mode. */ for (const auto &iter : formatsMap) { V4L2PixelFormat v4l2Format = iter.first; const PixelFormatInfo &info = PixelFormatInfo::info(v4l2Format); for (const SizeRange &sz : iter.second) { - double modeWidth = sz.contains(req) ? req.width : sz.max.width; - double modeHeight = sz.contains(req) ? req.height : sz.max.height; - double reqAr = static_cast(req.width) / req.height; - double modeAr = modeWidth / modeHeight; + Interval sensorX = Interval::XRange(sz); + Interval sensorY = Interval::YRange(sz); + double sensorAr = sizeAr(sz.max); + + double widthSeparation = hintX.separation(sensorX); + double heightSeparation = hintY.separation(sensorY); /* Score the dimensions for closeness. */ - score = scoreFormat(req.width, modeWidth); - score += scoreFormat(req.height, modeHeight); - score += PENALTY_AR * scoreFormat(reqAr, modeAr); + score = scoreSeparation(widthSeparation); + score += scoreSeparation(heightSeparation); + score += PENALTY_AR * std::abs(hintAr - sensorAr); /* Add any penalties... this is not an exact science! */ if (!info.packed) score += PENALTY_UNPACKED; - if (info.bitsPerPixel == 12) - score += PENALTY_12BIT; - else if (info.bitsPerPixel == 10) - score += PENALTY_10BIT; - else if (info.bitsPerPixel == 8) - score += PENALTY_8BIT; + int bitDifference = info.bitsPerPixel - hintMode.bitDepth; + score += std::abs(bitDifference) * PENALTY_BITDEPTH; + int modeWidth = sensorX.clamp(hintMode.sizeRange.max.width); + int modeHeight = sensorY.clamp(hintMode.sizeRange.max.height); if (score <= bestScore) { bestScore = score; bestMode.fourcc = v4l2Format; @@ -353,7 +392,8 @@ CameraConfiguration::Status RPiCameraConfiguration::validate() * the user request. */ V4L2VideoDevice::Formats fmts = data_->unicam_[Unicam::Image].dev()->formats(); - V4L2DeviceFormat sensorFormat = findBestMode(fmts, cfg.size); + SensorMode mode(PREFERRED_BIT_DEPTH, SizeRange(cfg.size)); + V4L2DeviceFormat sensorFormat = findBestMode(fmts, mode); int ret = data_->unicam_[Unicam::Image].dev()->tryFormat(&sensorFormat); if (ret) return Invalid; @@ -484,11 +524,13 @@ CameraConfiguration *PipelineHandlerRPi::generateConfiguration(Camera *camera, unsigned int rawCount = 0; unsigned int outCount = 0; for (const StreamRole role : roles) { + SensorMode mode; switch (role) { case StreamRole::Raw: size = data->sensor_->resolution(); fmts = data->unicam_[Unicam::Image].dev()->formats(); - sensorFormat = findBestMode(fmts, size); + mode = SensorMode(PREFERRED_BIT_DEPTH, SizeRange(size)); + sensorFormat = findBestMode(fmts, mode); pixelFormat = sensorFormat.fourcc.toPixelFormat(); ASSERT(pixelFormat.isValid()); bufferCount = 2; @@ -598,9 +640,18 @@ int PipelineHandlerRPi::configure(Camera *camera, CameraConfiguration *config) } } + /* + * If any parts of the hinted sensor mode are unset, fill them in now. + */ + if (config->sensorMode.bitDepth == 0) + config->sensorMode.bitDepth = PREFERRED_BIT_DEPTH; + + if (config->sensorMode.sizeRange.max == Size(0, 0)) + config->sensorMode.sizeRange = SizeRange(rawStream ? sensorSize : maxSize); + /* First calculate the best sensor mode we can use based on the user request. */ V4L2VideoDevice::Formats fmts = data->unicam_[Unicam::Image].dev()->formats(); - V4L2DeviceFormat sensorFormat = findBestMode(fmts, rawStream ? sensorSize : maxSize); + V4L2DeviceFormat sensorFormat = findBestMode(fmts, config->sensorMode); /* * Unicam image output format. The ISP input format gets set at start,