[{"id":15265,"web_url":"https://patchwork.libcamera.org/comment/15265/","msgid":"<YDLcIum+IXJcawwm@pendragon.ideasonboard.com>","date":"2021-02-21T22:18:10","subject":"Re: [libcamera-devel] [PATCH v3 1/4] pipeline: ipa: raspberrypi:\n\tPass exposure/gain values to IPA though controls","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"Hi Naush,\n\nThank you for the patch.\n\nOn Thu, Feb 18, 2021 at 05:01:23PM +0000, Naushir Patuck wrote:\n> When running with sensors that had no embedded data, the pipeline handler\n> would fill a dummy embedded data buffer with gain/exposure values, and\n> pass this buffer to the IPA together with the bayer buffer. The IPA would\n> extract these values for use in the controller algorithms.\n> \n> Rework this logic entirely by having a new RPiCameraData::BayerFrame\n> queue to replace the existing bayer queue. In addition to storing the\n> FrameBuffer pointer, this also stores all the controls tracked by\n> DelayedControls for that frame in a ControlList. This includes include\n> exposure and gain values. On signalling RPi::IPA_EVENT_SIGNAL_ISP_PREPARE\n> IPA event, the pipeline handler now passes this ControlList from the\n> RPiCameraData::BayerFrame queue.\n> \n> The IPA now extracts the gain and exposure values from the ControlList\n> instead of using RPiController::MdParserRPi to parse the embedded data\n> buffer.\n> \n> Signed-off-by: Naushir Patuck <naush@raspberrypi.com>\n> ---\n>  include/libcamera/ipa/raspberrypi.mojom       |   2 +\n>  src/ipa/raspberrypi/raspberrypi.cpp           | 132 +++++++++++-------\n>  .../pipeline/raspberrypi/raspberrypi.cpp      |  58 ++++----\n>  3 files changed, 108 insertions(+), 84 deletions(-)\n> \n> diff --git a/include/libcamera/ipa/raspberrypi.mojom b/include/libcamera/ipa/raspberrypi.mojom\n> index 5a27b1e4fc2d..f733a2cd2a40 100644\n> --- a/include/libcamera/ipa/raspberrypi.mojom\n> +++ b/include/libcamera/ipa/raspberrypi.mojom\n> @@ -29,6 +29,8 @@ struct SensorConfig {\n>  struct ISPConfig {\n>  \tuint32 embeddedBufferId;\n>  \tuint32 bayerBufferId;\n> +\tbool embeddedBufferPresent;\n> +\tControlList controls;\n>  };\n>  \n>  struct ConfigInput {\n> diff --git a/src/ipa/raspberrypi/raspberrypi.cpp b/src/ipa/raspberrypi/raspberrypi.cpp\n> index 1226ea514521..f9ab6417866e 100644\n> --- a/src/ipa/raspberrypi/raspberrypi.cpp\n> +++ b/src/ipa/raspberrypi/raspberrypi.cpp\n> @@ -1,6 +1,6 @@\n>  /* SPDX-License-Identifier: BSD-2-Clause */\n>  /*\n> - * Copyright (C) 2019-2020, Raspberry Pi (Trading) Ltd.\n> + * Copyright (C) 2019-2021, Raspberry Pi (Trading) Ltd.\n>   *\n>   * rpi.cpp - Raspberry Pi Image Processing Algorithms\n>   */\n> @@ -101,9 +101,11 @@ private:\n>  \tbool validateIspControls();\n>  \tvoid queueRequest(const ControlList &controls);\n>  \tvoid returnEmbeddedBuffer(unsigned int bufferId);\n> -\tvoid prepareISP(unsigned int bufferId);\n> +\tvoid prepareISP(const ipa::RPi::ISPConfig &data);\n>  \tvoid reportMetadata();\n>  \tbool parseEmbeddedData(unsigned int bufferId, struct DeviceStatus &deviceStatus);\n> +\tvoid fillDeviceStatus(uint32_t exposureLines, uint32_t gainCode,\n> +\t\t\t      struct DeviceStatus &deviceStatus);\n>  \tvoid processStats(unsigned int bufferId);\n>  \tvoid applyFrameDurations(double minFrameDuration, double maxFrameDuration);\n>  \tvoid applyAGC(const struct AgcStatus *agcStatus, ControlList &ctrls);\n> @@ -447,7 +449,7 @@ void IPARPi::signalIspPrepare(const ipa::RPi::ISPConfig &data)\n>  \t * avoid running the control algos for a few frames in case\n>  \t * they are \"unreliable\".\n>  \t */\n> -\tprepareISP(data.embeddedBufferId);\n> +\tprepareISP(data);\n>  \tframeCount_++;\n>  \n>  \t/* Ready to push the input buffer into the ISP. */\n> @@ -909,67 +911,84 @@ void IPARPi::returnEmbeddedBuffer(unsigned int bufferId)\n>  \tembeddedComplete.emit(bufferId & ipa::RPi::MaskID);\n>  }\n>  \n> -void IPARPi::prepareISP(unsigned int bufferId)\n> +void IPARPi::prepareISP(const ipa::RPi::ISPConfig &data)\n>  {\n>  \tstruct DeviceStatus deviceStatus = {};\n> -\tbool success = parseEmbeddedData(bufferId, deviceStatus);\n> +\tbool success = false;\n\nThe changes below are hard to read ('git show -b' helps after applying).\nFor future patch series, if large changes in indentation can be isolated\nin a patch that performs no or very little functional changes, it can\nhelp review.\n\n>  \n> -\t/* Done with embedded data now, return to pipeline handler asap. */\n> -\treturnEmbeddedBuffer(bufferId);\n> +\tif (data.embeddedBufferPresent) {\n> +\t\t/*\n> +\t\t * Pipeline handler has supplied us with an embedded data buffer,\n> +\t\t * so parse it and extract the exposure and gain.\n> +\t\t */\n> +\t\tsuccess = parseEmbeddedData(data.embeddedBufferId, deviceStatus);\n>  \n> -\tif (success) {\n> -\t\tControlList ctrls(ispCtrls_);\n> +\t\t/* Done with embedded data now, return to pipeline handler asap. */\n> +\t\treturnEmbeddedBuffer(data.embeddedBufferId);\n> +\t}\n>  \n> -\t\trpiMetadata_.Clear();\n> -\t\trpiMetadata_.Set(\"device.status\", deviceStatus);\n> -\t\tcontroller_.Prepare(&rpiMetadata_);\n> +\tif (!success) {\n> +\t\t/*\n> +\t\t * Pipeline handler has not supplied an embedded data buffer,\n> +\t\t * or embedded data buffer parsing has failed for some reason,\n> +\t\t * so pull the exposure and gain values from the control list.\n> +\t\t */\n> +\t\tint32_t exposureLines = data.controls.get(V4L2_CID_EXPOSURE).get<int32_t>();\n> +\t\tint32_t gainCode = data.controls.get(V4L2_CID_ANALOGUE_GAIN).get<int32_t>();\n> +\t\tfillDeviceStatus(exposureLines, gainCode, deviceStatus);\n> +\t}\n>  \n> -\t\t/* Lock the metadata buffer to avoid constant locks/unlocks. */\n> -\t\tstd::unique_lock<RPiController::Metadata> lock(rpiMetadata_);\n> +\tControlList ctrls(ispCtrls_);\n>  \n> -\t\tAwbStatus *awbStatus = rpiMetadata_.GetLocked<AwbStatus>(\"awb.status\");\n> -\t\tif (awbStatus)\n> -\t\t\tapplyAWB(awbStatus, ctrls);\n> +\trpiMetadata_.Clear();\n> +\trpiMetadata_.Set(\"device.status\", deviceStatus);\n> +\tcontroller_.Prepare(&rpiMetadata_);\n>  \n> -\t\tCcmStatus *ccmStatus = rpiMetadata_.GetLocked<CcmStatus>(\"ccm.status\");\n> -\t\tif (ccmStatus)\n> -\t\t\tapplyCCM(ccmStatus, ctrls);\n> +\t/* Lock the metadata buffer to avoid constant locks/unlocks. */\n> +\tstd::unique_lock<RPiController::Metadata> lock(rpiMetadata_);\n>  \n> -\t\tAgcStatus *dgStatus = rpiMetadata_.GetLocked<AgcStatus>(\"agc.status\");\n> -\t\tif (dgStatus)\n> -\t\t\tapplyDG(dgStatus, ctrls);\n> +\tAwbStatus *awbStatus = rpiMetadata_.GetLocked<AwbStatus>(\"awb.status\");\n> +\tif (awbStatus)\n> +\t\tapplyAWB(awbStatus, ctrls);\n>  \n> -\t\tAlscStatus *lsStatus = rpiMetadata_.GetLocked<AlscStatus>(\"alsc.status\");\n> -\t\tif (lsStatus)\n> -\t\t\tapplyLS(lsStatus, ctrls);\n> +\tCcmStatus *ccmStatus = rpiMetadata_.GetLocked<CcmStatus>(\"ccm.status\");\n> +\tif (ccmStatus)\n> +\t\tapplyCCM(ccmStatus, ctrls);\n>  \n> -\t\tContrastStatus *contrastStatus = rpiMetadata_.GetLocked<ContrastStatus>(\"contrast.status\");\n> -\t\tif (contrastStatus)\n> -\t\t\tapplyGamma(contrastStatus, ctrls);\n> +\tAgcStatus *dgStatus = rpiMetadata_.GetLocked<AgcStatus>(\"agc.status\");\n> +\tif (dgStatus)\n> +\t\tapplyDG(dgStatus, ctrls);\n>  \n> -\t\tBlackLevelStatus *blackLevelStatus = rpiMetadata_.GetLocked<BlackLevelStatus>(\"black_level.status\");\n> -\t\tif (blackLevelStatus)\n> -\t\t\tapplyBlackLevel(blackLevelStatus, ctrls);\n> +\tAlscStatus *lsStatus = rpiMetadata_.GetLocked<AlscStatus>(\"alsc.status\");\n> +\tif (lsStatus)\n> +\t\tapplyLS(lsStatus, ctrls);\n>  \n> -\t\tGeqStatus *geqStatus = rpiMetadata_.GetLocked<GeqStatus>(\"geq.status\");\n> -\t\tif (geqStatus)\n> -\t\t\tapplyGEQ(geqStatus, ctrls);\n> +\tContrastStatus *contrastStatus = rpiMetadata_.GetLocked<ContrastStatus>(\"contrast.status\");\n> +\tif (contrastStatus)\n> +\t\tapplyGamma(contrastStatus, ctrls);\n>  \n> -\t\tDenoiseStatus *denoiseStatus = rpiMetadata_.GetLocked<DenoiseStatus>(\"denoise.status\");\n> -\t\tif (denoiseStatus)\n> -\t\t\tapplyDenoise(denoiseStatus, ctrls);\n> +\tBlackLevelStatus *blackLevelStatus = rpiMetadata_.GetLocked<BlackLevelStatus>(\"black_level.status\");\n> +\tif (blackLevelStatus)\n> +\t\tapplyBlackLevel(blackLevelStatus, ctrls);\n>  \n> -\t\tSharpenStatus *sharpenStatus = rpiMetadata_.GetLocked<SharpenStatus>(\"sharpen.status\");\n> -\t\tif (sharpenStatus)\n> -\t\t\tapplySharpen(sharpenStatus, ctrls);\n> +\tGeqStatus *geqStatus = rpiMetadata_.GetLocked<GeqStatus>(\"geq.status\");\n> +\tif (geqStatus)\n> +\t\tapplyGEQ(geqStatus, ctrls);\n>  \n> -\t\tDpcStatus *dpcStatus = rpiMetadata_.GetLocked<DpcStatus>(\"dpc.status\");\n> -\t\tif (dpcStatus)\n> -\t\t\tapplyDPC(dpcStatus, ctrls);\n> +\tDenoiseStatus *denoiseStatus = rpiMetadata_.GetLocked<DenoiseStatus>(\"denoise.status\");\n> +\tif (denoiseStatus)\n> +\t\tapplyDenoise(denoiseStatus, ctrls);\n>  \n> -\t\tif (!ctrls.empty())\n> -\t\t\tsetIspControls.emit(ctrls);\n> -\t}\n> +\tSharpenStatus *sharpenStatus = rpiMetadata_.GetLocked<SharpenStatus>(\"sharpen.status\");\n> +\tif (sharpenStatus)\n> +\t\tapplySharpen(sharpenStatus, ctrls);\n> +\n> +\tDpcStatus *dpcStatus = rpiMetadata_.GetLocked<DpcStatus>(\"dpc.status\");\n> +\tif (dpcStatus)\n> +\t\tapplyDPC(dpcStatus, ctrls);\n> +\n> +\tif (!ctrls.empty())\n> +\t\tsetIspControls.emit(ctrls);\n>  }\n>  \n>  bool IPARPi::parseEmbeddedData(unsigned int bufferId, struct DeviceStatus &deviceStatus)\n> @@ -985,6 +1004,7 @@ bool IPARPi::parseEmbeddedData(unsigned int bufferId, struct DeviceStatus &devic\n>  \tRPiController::MdParser::Status status = helper_->Parser().Parse(mem.data());\n>  \tif (status != RPiController::MdParser::Status::OK) {\n>  \t\tLOG(IPARPI, Error) << \"Embedded Buffer parsing failed, error \" << status;\n> +\t\treturn false;\n>  \t} else {\n>  \t\tuint32_t exposureLines, gainCode;\n>  \t\tif (helper_->Parser().GetExposureLines(exposureLines) != RPiController::MdParser::Status::OK) {\n> @@ -992,21 +1012,29 @@ bool IPARPi::parseEmbeddedData(unsigned int bufferId, struct DeviceStatus &devic\n>  \t\t\treturn false;\n>  \t\t}\n>  \n> -\t\tdeviceStatus.shutter_speed = helper_->Exposure(exposureLines);\n>  \t\tif (helper_->Parser().GetGainCode(gainCode) != RPiController::MdParser::Status::OK) {\n>  \t\t\tLOG(IPARPI, Error) << \"Gain failed\";\n>  \t\t\treturn false;\n>  \t\t}\n>  \n> -\t\tdeviceStatus.analogue_gain = helper_->Gain(gainCode);\n> -\t\tLOG(IPARPI, Debug) << \"Metadata - Exposure : \"\n> -\t\t\t\t   << deviceStatus.shutter_speed << \" Gain : \"\n> -\t\t\t\t   << deviceStatus.analogue_gain;\n> +\t\tfillDeviceStatus(exposureLines, gainCode, deviceStatus);\n>  \t}\n>  \n>  \treturn true;\n>  }\n>  \n> +void IPARPi::fillDeviceStatus(uint32_t exposureLines, uint32_t gainCode,\n> +\t\t\t      struct DeviceStatus &deviceStatus)\n> +{\n> +\tdeviceStatus.shutter_speed = helper_->Exposure(exposureLines);\n> +\tdeviceStatus.analogue_gain = helper_->Gain(gainCode);\n> +\n> +\tLOG(IPARPI, Debug) << \"Metadata - Exposure : \"\n> +\t\t\t   << deviceStatus.shutter_speed\n> +\t\t\t   << \" Gain : \"\n> +\t\t\t   << deviceStatus.analogue_gain;\n> +}\n> +\n>  void IPARPi::processStats(unsigned int bufferId)\n>  {\n>  \tauto it = buffers_.find(bufferId);\n> diff --git a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp\n> index ba74ace183db..b43d86166c63 100644\n> --- a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp\n> +++ b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp\n> @@ -1,6 +1,6 @@\n>  /* SPDX-License-Identifier: LGPL-2.1-or-later */\n>  /*\n> - * Copyright (C) 2019-2020, Raspberry Pi (Trading) Ltd.\n> + * Copyright (C) 2019-2021, Raspberry Pi (Trading) Ltd.\n>   *\n>   * raspberrypi.cpp - Pipeline handler for Raspberry Pi devices\n>   */\n> @@ -197,7 +197,13 @@ public:\n>  \t */\n>  \tenum class State { Stopped, Idle, Busy, IpaComplete };\n>  \tState state_;\n> -\tstd::queue<FrameBuffer *> bayerQueue_;\n> +\n> +\tstruct BayerFrame {\n> +\t\tFrameBuffer *buffer;\n> +\t\tControlList controls;\n> +\t};\n> +\n> +\tstd::queue<BayerFrame> bayerQueue_;\n>  \tstd::queue<FrameBuffer *> embeddedQueue_;\n>  \tstd::deque<Request *> requestQueue_;\n>  \n> @@ -222,7 +228,7 @@ public:\n>  private:\n>  \tvoid checkRequestCompleted();\n>  \tvoid tryRunPipeline();\n> -\tbool findMatchingBuffers(FrameBuffer *&bayerBuffer, FrameBuffer *&embeddedBuffer);\n> +\tbool findMatchingBuffers(BayerFrame &bayerFrame, FrameBuffer *&embeddedBuffer);\n>  \n>  \tunsigned int ispOutputCount_;\n>  };\n> @@ -1383,29 +1389,14 @@ void RPiCameraData::unicamBufferDequeue(FrameBuffer *buffer)\n>  \t\t\t<< \", timestamp: \" << buffer->metadata().timestamp;\n>  \n>  \tif (stream == &unicam_[Unicam::Image]) {\n> -\t\tbayerQueue_.push(buffer);\n> -\t} else {\n> -\t\tembeddedQueue_.push(buffer);\n> -\n> -\t\tControlList ctrl = delayedCtrls_->get(buffer->metadata().sequence);\n> -\n>  \t\t/*\n> -\t\t * Sensor metadata is unavailable, so put the expected ctrl\n> -\t\t * values (accounting for the staggered delays) into the empty\n> -\t\t * metadata buffer.\n> +\t\t * Lookup the sensor controls used for this frame sequence from\n> +\t\t * StaggeredCtrl and queue them along with the frame buffer.\n\ns/StaggeredCtrl/DelayedControls/ ?\n\n>  \t\t */\n> -\t\tif (!sensorMetadata_) {\n> -\t\t\tunsigned int bufferId = unicam_[Unicam::Embedded].getBufferId(buffer);\n> -\t\t\tauto it = mappedEmbeddedBuffers_.find(bufferId);\n> -\t\t\tif (it != mappedEmbeddedBuffers_.end()) {\n> -\t\t\t\tuint32_t *mem = reinterpret_cast<uint32_t *>(it->second.maps()[0].data());\n> -\t\t\t\tmem[0] = ctrl.get(V4L2_CID_EXPOSURE).get<int32_t>();\n> -\t\t\t\tmem[1] = ctrl.get(V4L2_CID_ANALOGUE_GAIN).get<int32_t>();\n> -\t\t\t} else {\n> -\t\t\t\tLOG(RPI, Warning) << \"Failed to find embedded buffer \"\n> -\t\t\t\t\t\t  << bufferId;\n> -\t\t\t}\n> -\t\t}\n> +\t\tControlList ctrl = delayedCtrls_->get(buffer->metadata().sequence);\n> +\t\tbayerQueue_.push({ buffer, std::move(ctrl) });\n> +\t} else {\n> +\t\tembeddedQueue_.push(buffer);\n>  \t}\n>  \n>  \thandleState();\n> @@ -1656,14 +1647,15 @@ void RPiCameraData::applyScalerCrop(const ControlList &controls)\n>  \n>  void RPiCameraData::tryRunPipeline()\n>  {\n> -\tFrameBuffer *bayerBuffer, *embeddedBuffer;\n> +\tFrameBuffer *embeddedBuffer;\n> +\tBayerFrame bayerFrame;\n>  \n>  \t/* If any of our request or buffer queues are empty, we cannot proceed. */\n>  \tif (state_ != State::Idle || requestQueue_.empty() ||\n>  \t    bayerQueue_.empty() || embeddedQueue_.empty())\n>  \t\treturn;\n>  \n> -\tif (!findMatchingBuffers(bayerBuffer, embeddedBuffer))\n> +\tif (!findMatchingBuffers(bayerFrame, embeddedBuffer))\n>  \t\treturn;\n>  \n>  \t/* Take the first request from the queue and action the IPA. */\n> @@ -1682,7 +1674,7 @@ void RPiCameraData::tryRunPipeline()\n>  \t/* Set our state to say the pipeline is active. */\n>  \tstate_ = State::Busy;\n>  \n> -\tunsigned int bayerId = unicam_[Unicam::Image].getBufferId(bayerBuffer);\n> +\tunsigned int bayerId = unicam_[Unicam::Image].getBufferId(bayerFrame.buffer);\n>  \tunsigned int embeddedId = unicam_[Unicam::Embedded].getBufferId(embeddedBuffer);\n>  \n>  \tLOG(RPI, Debug) << \"Signalling signalIspPrepare:\"\n> @@ -1692,24 +1684,26 @@ void RPiCameraData::tryRunPipeline()\n>  \tipa::RPi::ISPConfig ispPrepare;\n>  \tispPrepare.embeddedBufferId = ipa::RPi::MaskEmbeddedData | embeddedId;\n>  \tispPrepare.bayerBufferId = ipa::RPi::MaskBayerData | bayerId;\n> +\tispPrepare.embeddedBufferPresent = true;\n\nShouldn't the be conditioned by embedded data being present ?\n\n> +\tispPrepare.controls = std::move(bayerFrame.controls);\n>  \tipa_->signalIspPrepare(ispPrepare);\n>  }\n>  \n> -bool RPiCameraData::findMatchingBuffers(FrameBuffer *&bayerBuffer, FrameBuffer *&embeddedBuffer)\n> +bool RPiCameraData::findMatchingBuffers(BayerFrame &bayerFrame, FrameBuffer *&embeddedBuffer)\n>  {\n>  \tunsigned int embeddedRequeueCount = 0, bayerRequeueCount = 0;\n>  \n>  \t/* Loop until we find a matching bayer and embedded data buffer. */\n>  \twhile (!bayerQueue_.empty()) {\n>  \t\t/* Start with the front of the bayer queue. */\n> -\t\tbayerBuffer = bayerQueue_.front();\n> +\t\tbayerFrame = bayerQueue_.front();\n\nThis will create a copy of the ControlList. Is there a way the logic in\nthis function could be reworked to allow an std::move() here ?\n\n>  \n>  \t\t/*\n>  \t\t * Find the embedded data buffer with a matching timestamp to pass to\n>  \t\t * the IPA. Any embedded buffers with a timestamp lower than the\n>  \t\t * current bayer buffer will be removed and re-queued to the driver.\n>  \t\t */\n> -\t\tuint64_t ts = bayerBuffer->metadata().timestamp;\n> +\t\tuint64_t ts = bayerFrame.buffer->metadata().timestamp;\n>  \t\tembeddedBuffer = nullptr;\n>  \t\twhile (!embeddedQueue_.empty()) {\n>  \t\t\tFrameBuffer *b = embeddedQueue_.front();\n> @@ -1739,7 +1733,7 @@ bool RPiCameraData::findMatchingBuffers(FrameBuffer *&bayerBuffer, FrameBuffer *\n>  \t\t\t\t * the front of the queue. This buffer is now orphaned, so requeue\n>  \t\t\t\t * it back to the device.\n>  \t\t\t\t */\n> -\t\t\t\tunicam_[Unicam::Image].queueBuffer(bayerQueue_.front());\n> +\t\t\t\tunicam_[Unicam::Image].queueBuffer(bayerQueue_.front().buffer);\n>  \t\t\t\tbayerQueue_.pop();\n>  \t\t\t\tbayerRequeueCount++;\n>  \t\t\t\tLOG(RPI, Warning) << \"Dropping unmatched input frame in stream \"\n> @@ -1757,7 +1751,7 @@ bool RPiCameraData::findMatchingBuffers(FrameBuffer *&bayerBuffer, FrameBuffer *\n>  \n>  \t\t\t\tLOG(RPI, Warning) << \"Flushing bayer stream!\";\n>  \t\t\t\twhile (!bayerQueue_.empty()) {\n> -\t\t\t\t\tunicam_[Unicam::Image].queueBuffer(bayerQueue_.front());\n> +\t\t\t\t\tunicam_[Unicam::Image].queueBuffer(bayerQueue_.front().buffer);\n>  \t\t\t\t\tbayerQueue_.pop();\n>  \t\t\t\t}\n>  \t\t\t\tflushedBuffers = true;","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 960DCBD1F1\n\tfor <parsemail@patchwork.libcamera.org>;\n\tSun, 21 Feb 2021 22:18:39 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 6370A689ED;\n\tSun, 21 Feb 2021 23:18:39 +0100 (CET)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 10D6A60106\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tSun, 21 Feb 2021 23:18:38 +0100 (CET)","from pendragon.ideasonboard.com (62-78-145-57.bb.dnainternet.fi\n\t[62.78.145.57])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 90504E9;\n\tSun, 21 Feb 2021 23:18:37 +0100 (CET)"],"Authentication-Results":"lancelot.ideasonboard.com;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"oLNNN97Q\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1613945917;\n\tbh=ayt097tTOB5izr5JrI4jeE4Ua7xvtpxov2O1Yd4rr7c=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=oLNNN97QPbpaAXkEqEAMZy6BlJa0pNoXDhU3AUcP7ECRmdHx05vAT+WDOdL+EJNnv\n\thO1buKMOjqCNI+OxjuqmpOgBS6TS+RSXymEhsbGVKJJjEmix4VMHb7oIYwvsEu9b26\n\tSFJ6J3uWi8Qwwv5ZDYwr3rlTpW0G0QTqKYVPmugI=","Date":"Mon, 22 Feb 2021 00:18:10 +0200","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"Naushir Patuck <naush@raspberrypi.com>","Message-ID":"<YDLcIum+IXJcawwm@pendragon.ideasonboard.com>","References":"<20210218170126.2060783-1-naush@raspberrypi.com>\n\t<20210218170126.2060783-2-naush@raspberrypi.com>","MIME-Version":"1.0","Content-Disposition":"inline","In-Reply-To":"<20210218170126.2060783-2-naush@raspberrypi.com>","Subject":"Re: [libcamera-devel] [PATCH v3 1/4] pipeline: ipa: raspberrypi:\n\tPass exposure/gain values to IPA though controls","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>","Cc":"libcamera-devel@lists.libcamera.org","Content-Type":"text/plain; charset=\"us-ascii\"","Content-Transfer-Encoding":"7bit","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":15287,"web_url":"https://patchwork.libcamera.org/comment/15287/","msgid":"<CAEmqJPq9f0imtqbsUt64YaF9VTHr-onxCo94nqjwiTsYRonVow@mail.gmail.com>","date":"2021-02-22T13:09:45","subject":"Re: [libcamera-devel] [PATCH v3 1/4] pipeline: ipa: raspberrypi:\n\tPass exposure/gain values to IPA though controls","submitter":{"id":34,"url":"https://patchwork.libcamera.org/api/people/34/","name":"Naushir Patuck","email":"naush@raspberrypi.com"},"content":"Hi Laurent,\n\nThank you for your review feedback.\n\nOn Sun, 21 Feb 2021 at 22:18, Laurent Pinchart <\nlaurent.pinchart@ideasonboard.com> wrote:\n\n> Hi Naush,\n>\n> Thank you for the patch.\n>\n> On Thu, Feb 18, 2021 at 05:01:23PM +0000, Naushir Patuck wrote:\n> > When running with sensors that had no embedded data, the pipeline handler\n> > would fill a dummy embedded data buffer with gain/exposure values, and\n> > pass this buffer to the IPA together with the bayer buffer. The IPA would\n> > extract these values for use in the controller algorithms.\n> >\n> > Rework this logic entirely by having a new RPiCameraData::BayerFrame\n> > queue to replace the existing bayer queue. In addition to storing the\n> > FrameBuffer pointer, this also stores all the controls tracked by\n> > DelayedControls for that frame in a ControlList. This includes include\n> > exposure and gain values. On signalling RPi::IPA_EVENT_SIGNAL_ISP_PREPARE\n> > IPA event, the pipeline handler now passes this ControlList from the\n> > RPiCameraData::BayerFrame queue.\n> >\n> > The IPA now extracts the gain and exposure values from the ControlList\n> > instead of using RPiController::MdParserRPi to parse the embedded data\n> > buffer.\n> >\n> > Signed-off-by: Naushir Patuck <naush@raspberrypi.com>\n> > ---\n> >  include/libcamera/ipa/raspberrypi.mojom       |   2 +\n> >  src/ipa/raspberrypi/raspberrypi.cpp           | 132 +++++++++++-------\n> >  .../pipeline/raspberrypi/raspberrypi.cpp      |  58 ++++----\n> >  3 files changed, 108 insertions(+), 84 deletions(-)\n> >\n> > diff --git a/include/libcamera/ipa/raspberrypi.mojom\n> b/include/libcamera/ipa/raspberrypi.mojom\n> > index 5a27b1e4fc2d..f733a2cd2a40 100644\n> > --- a/include/libcamera/ipa/raspberrypi.mojom\n> > +++ b/include/libcamera/ipa/raspberrypi.mojom\n> > @@ -29,6 +29,8 @@ struct SensorConfig {\n> >  struct ISPConfig {\n> >       uint32 embeddedBufferId;\n> >       uint32 bayerBufferId;\n> > +     bool embeddedBufferPresent;\n> > +     ControlList controls;\n> >  };\n> >\n> >  struct ConfigInput {\n> > diff --git a/src/ipa/raspberrypi/raspberrypi.cpp\n> b/src/ipa/raspberrypi/raspberrypi.cpp\n> > index 1226ea514521..f9ab6417866e 100644\n> > --- a/src/ipa/raspberrypi/raspberrypi.cpp\n> > +++ b/src/ipa/raspberrypi/raspberrypi.cpp\n> > @@ -1,6 +1,6 @@\n> >  /* SPDX-License-Identifier: BSD-2-Clause */\n> >  /*\n> > - * Copyright (C) 2019-2020, Raspberry Pi (Trading) Ltd.\n> > + * Copyright (C) 2019-2021, Raspberry Pi (Trading) Ltd.\n> >   *\n> >   * rpi.cpp - Raspberry Pi Image Processing Algorithms\n> >   */\n> > @@ -101,9 +101,11 @@ private:\n> >       bool validateIspControls();\n> >       void queueRequest(const ControlList &controls);\n> >       void returnEmbeddedBuffer(unsigned int bufferId);\n> > -     void prepareISP(unsigned int bufferId);\n> > +     void prepareISP(const ipa::RPi::ISPConfig &data);\n> >       void reportMetadata();\n> >       bool parseEmbeddedData(unsigned int bufferId, struct DeviceStatus\n> &deviceStatus);\n> > +     void fillDeviceStatus(uint32_t exposureLines, uint32_t gainCode,\n> > +                           struct DeviceStatus &deviceStatus);\n> >       void processStats(unsigned int bufferId);\n> >       void applyFrameDurations(double minFrameDuration, double\n> maxFrameDuration);\n> >       void applyAGC(const struct AgcStatus *agcStatus, ControlList\n> &ctrls);\n> > @@ -447,7 +449,7 @@ void IPARPi::signalIspPrepare(const\n> ipa::RPi::ISPConfig &data)\n> >        * avoid running the control algos for a few frames in case\n> >        * they are \"unreliable\".\n> >        */\n> > -     prepareISP(data.embeddedBufferId);\n> > +     prepareISP(data);\n> >       frameCount_++;\n> >\n> >       /* Ready to push the input buffer into the ISP. */\n> > @@ -909,67 +911,84 @@ void IPARPi::returnEmbeddedBuffer(unsigned int\n> bufferId)\n> >       embeddedComplete.emit(bufferId & ipa::RPi::MaskID);\n> >  }\n> >\n> > -void IPARPi::prepareISP(unsigned int bufferId)\n> > +void IPARPi::prepareISP(const ipa::RPi::ISPConfig &data)\n> >  {\n> >       struct DeviceStatus deviceStatus = {};\n> > -     bool success = parseEmbeddedData(bufferId, deviceStatus);\n> > +     bool success = false;\n>\n> The changes below are hard to read ('git show -b' helps after applying).\n> For future patch series, if large changes in indentation can be isolated\n> in a patch that performs no or very little functional changes, it can\n> help review.\n>\n\nApologies, I will keep that in mind for the future.\n\n\n>\n> >\n> > -     /* Done with embedded data now, return to pipeline handler asap. */\n> > -     returnEmbeddedBuffer(bufferId);\n> > +     if (data.embeddedBufferPresent) {\n> > +             /*\n> > +              * Pipeline handler has supplied us with an embedded data\n> buffer,\n> > +              * so parse it and extract the exposure and gain.\n> > +              */\n> > +             success = parseEmbeddedData(data.embeddedBufferId,\n> deviceStatus);\n> >\n> > -     if (success) {\n> > -             ControlList ctrls(ispCtrls_);\n> > +             /* Done with embedded data now, return to pipeline handler\n> asap. */\n> > +             returnEmbeddedBuffer(data.embeddedBufferId);\n> > +     }\n> >\n> > -             rpiMetadata_.Clear();\n> > -             rpiMetadata_.Set(\"device.status\", deviceStatus);\n> > -             controller_.Prepare(&rpiMetadata_);\n> > +     if (!success) {\n> > +             /*\n> > +              * Pipeline handler has not supplied an embedded data\n> buffer,\n> > +              * or embedded data buffer parsing has failed for some\n> reason,\n> > +              * so pull the exposure and gain values from the control\n> list.\n> > +              */\n> > +             int32_t exposureLines =\n> data.controls.get(V4L2_CID_EXPOSURE).get<int32_t>();\n> > +             int32_t gainCode =\n> data.controls.get(V4L2_CID_ANALOGUE_GAIN).get<int32_t>();\n> > +             fillDeviceStatus(exposureLines, gainCode, deviceStatus);\n> > +     }\n> >\n> > -             /* Lock the metadata buffer to avoid constant\n> locks/unlocks. */\n> > -             std::unique_lock<RPiController::Metadata>\n> lock(rpiMetadata_);\n> > +     ControlList ctrls(ispCtrls_);\n> >\n> > -             AwbStatus *awbStatus =\n> rpiMetadata_.GetLocked<AwbStatus>(\"awb.status\");\n> > -             if (awbStatus)\n> > -                     applyAWB(awbStatus, ctrls);\n> > +     rpiMetadata_.Clear();\n> > +     rpiMetadata_.Set(\"device.status\", deviceStatus);\n> > +     controller_.Prepare(&rpiMetadata_);\n> >\n> > -             CcmStatus *ccmStatus =\n> rpiMetadata_.GetLocked<CcmStatus>(\"ccm.status\");\n> > -             if (ccmStatus)\n> > -                     applyCCM(ccmStatus, ctrls);\n> > +     /* Lock the metadata buffer to avoid constant locks/unlocks. */\n> > +     std::unique_lock<RPiController::Metadata> lock(rpiMetadata_);\n> >\n> > -             AgcStatus *dgStatus =\n> rpiMetadata_.GetLocked<AgcStatus>(\"agc.status\");\n> > -             if (dgStatus)\n> > -                     applyDG(dgStatus, ctrls);\n> > +     AwbStatus *awbStatus =\n> rpiMetadata_.GetLocked<AwbStatus>(\"awb.status\");\n> > +     if (awbStatus)\n> > +             applyAWB(awbStatus, ctrls);\n> >\n> > -             AlscStatus *lsStatus =\n> rpiMetadata_.GetLocked<AlscStatus>(\"alsc.status\");\n> > -             if (lsStatus)\n> > -                     applyLS(lsStatus, ctrls);\n> > +     CcmStatus *ccmStatus =\n> rpiMetadata_.GetLocked<CcmStatus>(\"ccm.status\");\n> > +     if (ccmStatus)\n> > +             applyCCM(ccmStatus, ctrls);\n> >\n> > -             ContrastStatus *contrastStatus =\n> rpiMetadata_.GetLocked<ContrastStatus>(\"contrast.status\");\n> > -             if (contrastStatus)\n> > -                     applyGamma(contrastStatus, ctrls);\n> > +     AgcStatus *dgStatus =\n> rpiMetadata_.GetLocked<AgcStatus>(\"agc.status\");\n> > +     if (dgStatus)\n> > +             applyDG(dgStatus, ctrls);\n> >\n> > -             BlackLevelStatus *blackLevelStatus =\n> rpiMetadata_.GetLocked<BlackLevelStatus>(\"black_level.status\");\n> > -             if (blackLevelStatus)\n> > -                     applyBlackLevel(blackLevelStatus, ctrls);\n> > +     AlscStatus *lsStatus =\n> rpiMetadata_.GetLocked<AlscStatus>(\"alsc.status\");\n> > +     if (lsStatus)\n> > +             applyLS(lsStatus, ctrls);\n> >\n> > -             GeqStatus *geqStatus =\n> rpiMetadata_.GetLocked<GeqStatus>(\"geq.status\");\n> > -             if (geqStatus)\n> > -                     applyGEQ(geqStatus, ctrls);\n> > +     ContrastStatus *contrastStatus =\n> rpiMetadata_.GetLocked<ContrastStatus>(\"contrast.status\");\n> > +     if (contrastStatus)\n> > +             applyGamma(contrastStatus, ctrls);\n> >\n> > -             DenoiseStatus *denoiseStatus =\n> rpiMetadata_.GetLocked<DenoiseStatus>(\"denoise.status\");\n> > -             if (denoiseStatus)\n> > -                     applyDenoise(denoiseStatus, ctrls);\n> > +     BlackLevelStatus *blackLevelStatus =\n> rpiMetadata_.GetLocked<BlackLevelStatus>(\"black_level.status\");\n> > +     if (blackLevelStatus)\n> > +             applyBlackLevel(blackLevelStatus, ctrls);\n> >\n> > -             SharpenStatus *sharpenStatus =\n> rpiMetadata_.GetLocked<SharpenStatus>(\"sharpen.status\");\n> > -             if (sharpenStatus)\n> > -                     applySharpen(sharpenStatus, ctrls);\n> > +     GeqStatus *geqStatus =\n> rpiMetadata_.GetLocked<GeqStatus>(\"geq.status\");\n> > +     if (geqStatus)\n> > +             applyGEQ(geqStatus, ctrls);\n> >\n> > -             DpcStatus *dpcStatus =\n> rpiMetadata_.GetLocked<DpcStatus>(\"dpc.status\");\n> > -             if (dpcStatus)\n> > -                     applyDPC(dpcStatus, ctrls);\n> > +     DenoiseStatus *denoiseStatus =\n> rpiMetadata_.GetLocked<DenoiseStatus>(\"denoise.status\");\n> > +     if (denoiseStatus)\n> > +             applyDenoise(denoiseStatus, ctrls);\n> >\n> > -             if (!ctrls.empty())\n> > -                     setIspControls.emit(ctrls);\n> > -     }\n> > +     SharpenStatus *sharpenStatus =\n> rpiMetadata_.GetLocked<SharpenStatus>(\"sharpen.status\");\n> > +     if (sharpenStatus)\n> > +             applySharpen(sharpenStatus, ctrls);\n> > +\n> > +     DpcStatus *dpcStatus =\n> rpiMetadata_.GetLocked<DpcStatus>(\"dpc.status\");\n> > +     if (dpcStatus)\n> > +             applyDPC(dpcStatus, ctrls);\n> > +\n> > +     if (!ctrls.empty())\n> > +             setIspControls.emit(ctrls);\n> >  }\n> >\n> >  bool IPARPi::parseEmbeddedData(unsigned int bufferId, struct\n> DeviceStatus &deviceStatus)\n> > @@ -985,6 +1004,7 @@ bool IPARPi::parseEmbeddedData(unsigned int\n> bufferId, struct DeviceStatus &devic\n> >       RPiController::MdParser::Status status =\n> helper_->Parser().Parse(mem.data());\n> >       if (status != RPiController::MdParser::Status::OK) {\n> >               LOG(IPARPI, Error) << \"Embedded Buffer parsing failed,\n> error \" << status;\n> > +             return false;\n> >       } else {\n> >               uint32_t exposureLines, gainCode;\n> >               if (helper_->Parser().GetExposureLines(exposureLines) !=\n> RPiController::MdParser::Status::OK) {\n> > @@ -992,21 +1012,29 @@ bool IPARPi::parseEmbeddedData(unsigned int\n> bufferId, struct DeviceStatus &devic\n> >                       return false;\n> >               }\n> >\n> > -             deviceStatus.shutter_speed =\n> helper_->Exposure(exposureLines);\n> >               if (helper_->Parser().GetGainCode(gainCode) !=\n> RPiController::MdParser::Status::OK) {\n> >                       LOG(IPARPI, Error) << \"Gain failed\";\n> >                       return false;\n> >               }\n> >\n> > -             deviceStatus.analogue_gain = helper_->Gain(gainCode);\n> > -             LOG(IPARPI, Debug) << \"Metadata - Exposure : \"\n> > -                                << deviceStatus.shutter_speed << \" Gain\n> : \"\n> > -                                << deviceStatus.analogue_gain;\n> > +             fillDeviceStatus(exposureLines, gainCode, deviceStatus);\n> >       }\n> >\n> >       return true;\n> >  }\n> >\n> > +void IPARPi::fillDeviceStatus(uint32_t exposureLines, uint32_t gainCode,\n> > +                           struct DeviceStatus &deviceStatus)\n> > +{\n> > +     deviceStatus.shutter_speed = helper_->Exposure(exposureLines);\n> > +     deviceStatus.analogue_gain = helper_->Gain(gainCode);\n> > +\n> > +     LOG(IPARPI, Debug) << \"Metadata - Exposure : \"\n> > +                        << deviceStatus.shutter_speed\n> > +                        << \" Gain : \"\n> > +                        << deviceStatus.analogue_gain;\n> > +}\n> > +\n> >  void IPARPi::processStats(unsigned int bufferId)\n> >  {\n> >       auto it = buffers_.find(bufferId);\n> > diff --git a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp\n> b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp\n> > index ba74ace183db..b43d86166c63 100644\n> > --- a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp\n> > +++ b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp\n> > @@ -1,6 +1,6 @@\n> >  /* SPDX-License-Identifier: LGPL-2.1-or-later */\n> >  /*\n> > - * Copyright (C) 2019-2020, Raspberry Pi (Trading) Ltd.\n> > + * Copyright (C) 2019-2021, Raspberry Pi (Trading) Ltd.\n> >   *\n> >   * raspberrypi.cpp - Pipeline handler for Raspberry Pi devices\n> >   */\n> > @@ -197,7 +197,13 @@ public:\n> >        */\n> >       enum class State { Stopped, Idle, Busy, IpaComplete };\n> >       State state_;\n> > -     std::queue<FrameBuffer *> bayerQueue_;\n> > +\n> > +     struct BayerFrame {\n> > +             FrameBuffer *buffer;\n> > +             ControlList controls;\n> > +     };\n> > +\n> > +     std::queue<BayerFrame> bayerQueue_;\n> >       std::queue<FrameBuffer *> embeddedQueue_;\n> >       std::deque<Request *> requestQueue_;\n> >\n> > @@ -222,7 +228,7 @@ public:\n> >  private:\n> >       void checkRequestCompleted();\n> >       void tryRunPipeline();\n> > -     bool findMatchingBuffers(FrameBuffer *&bayerBuffer, FrameBuffer\n> *&embeddedBuffer);\n> > +     bool findMatchingBuffers(BayerFrame &bayerFrame, FrameBuffer\n> *&embeddedBuffer);\n> >\n> >       unsigned int ispOutputCount_;\n> >  };\n> > @@ -1383,29 +1389,14 @@ void\n> RPiCameraData::unicamBufferDequeue(FrameBuffer *buffer)\n> >                       << \", timestamp: \" << buffer->metadata().timestamp;\n> >\n> >       if (stream == &unicam_[Unicam::Image]) {\n> > -             bayerQueue_.push(buffer);\n> > -     } else {\n> > -             embeddedQueue_.push(buffer);\n> > -\n> > -             ControlList ctrl =\n> delayedCtrls_->get(buffer->metadata().sequence);\n> > -\n> >               /*\n> > -              * Sensor metadata is unavailable, so put the expected ctrl\n> > -              * values (accounting for the staggered delays) into the\n> empty\n> > -              * metadata buffer.\n> > +              * Lookup the sensor controls used for this frame sequence\n> from\n> > +              * StaggeredCtrl and queue them along with the frame\n> buffer.\n>\n> s/StaggeredCtrl/DelayedControls/ ?\n>\n> >                */\n> > -             if (!sensorMetadata_) {\n> > -                     unsigned int bufferId =\n> unicam_[Unicam::Embedded].getBufferId(buffer);\n> > -                     auto it = mappedEmbeddedBuffers_.find(bufferId);\n> > -                     if (it != mappedEmbeddedBuffers_.end()) {\n> > -                             uint32_t *mem = reinterpret_cast<uint32_t\n> *>(it->second.maps()[0].data());\n> > -                             mem[0] =\n> ctrl.get(V4L2_CID_EXPOSURE).get<int32_t>();\n> > -                             mem[1] =\n> ctrl.get(V4L2_CID_ANALOGUE_GAIN).get<int32_t>();\n> > -                     } else {\n> > -                             LOG(RPI, Warning) << \"Failed to find\n> embedded buffer \"\n> > -                                               << bufferId;\n> > -                     }\n> > -             }\n> > +             ControlList ctrl =\n> delayedCtrls_->get(buffer->metadata().sequence);\n> > +             bayerQueue_.push({ buffer, std::move(ctrl) });\n> > +     } else {\n> > +             embeddedQueue_.push(buffer);\n> >       }\n> >\n> >       handleState();\n> > @@ -1656,14 +1647,15 @@ void RPiCameraData::applyScalerCrop(const\n> ControlList &controls)\n> >\n> >  void RPiCameraData::tryRunPipeline()\n> >  {\n> > -     FrameBuffer *bayerBuffer, *embeddedBuffer;\n> > +     FrameBuffer *embeddedBuffer;\n> > +     BayerFrame bayerFrame;\n> >\n> >       /* If any of our request or buffer queues are empty, we cannot\n> proceed. */\n> >       if (state_ != State::Idle || requestQueue_.empty() ||\n> >           bayerQueue_.empty() || embeddedQueue_.empty())\n> >               return;\n> >\n> > -     if (!findMatchingBuffers(bayerBuffer, embeddedBuffer))\n> > +     if (!findMatchingBuffers(bayerFrame, embeddedBuffer))\n> >               return;\n> >\n> >       /* Take the first request from the queue and action the IPA. */\n> > @@ -1682,7 +1674,7 @@ void RPiCameraData::tryRunPipeline()\n> >       /* Set our state to say the pipeline is active. */\n> >       state_ = State::Busy;\n> >\n> > -     unsigned int bayerId =\n> unicam_[Unicam::Image].getBufferId(bayerBuffer);\n> > +     unsigned int bayerId =\n> unicam_[Unicam::Image].getBufferId(bayerFrame.buffer);\n> >       unsigned int embeddedId =\n> unicam_[Unicam::Embedded].getBufferId(embeddedBuffer);\n> >\n> >       LOG(RPI, Debug) << \"Signalling signalIspPrepare:\"\n> > @@ -1692,24 +1684,26 @@ void RPiCameraData::tryRunPipeline()\n> >       ipa::RPi::ISPConfig ispPrepare;\n> >       ispPrepare.embeddedBufferId = ipa::RPi::MaskEmbeddedData |\n> embeddedId;\n> >       ispPrepare.bayerBufferId = ipa::RPi::MaskBayerData | bayerId;\n> > +     ispPrepare.embeddedBufferPresent = true;\n>\n> Shouldn't the be conditioned by embedded data being present ?\n>\n\nYes, good catch!  It is conditional on the next patches, but should be here\nas well.\n\n\n>\n> > +     ispPrepare.controls = std::move(bayerFrame.controls);\n> >       ipa_->signalIspPrepare(ispPrepare);\n> >  }\n> >\n> > -bool RPiCameraData::findMatchingBuffers(FrameBuffer *&bayerBuffer,\n> FrameBuffer *&embeddedBuffer)\n> > +bool RPiCameraData::findMatchingBuffers(BayerFrame &bayerFrame,\n> FrameBuffer *&embeddedBuffer)\n> >  {\n> >       unsigned int embeddedRequeueCount = 0, bayerRequeueCount = 0;\n> >\n> >       /* Loop until we find a matching bayer and embedded data buffer. */\n> >       while (!bayerQueue_.empty()) {\n> >               /* Start with the front of the bayer queue. */\n> > -             bayerBuffer = bayerQueue_.front();\n> > +             bayerFrame = bayerQueue_.front();\n>\n> This will create a copy of the ControlList. Is there a way the logic in\n> this function could be reworked to allow an std::move() here ?\n>\n\nGood point.  I think it should be easy enough to allow a std::move() here.\n\n\n>\n> >\n> >               /*\n> >                * Find the embedded data buffer with a matching timestamp\n> to pass to\n> >                * the IPA. Any embedded buffers with a timestamp lower\n> than the\n> >                * current bayer buffer will be removed and re-queued to\n> the driver.\n> >                */\n> > -             uint64_t ts = bayerBuffer->metadata().timestamp;\n> > +             uint64_t ts = bayerFrame.buffer->metadata().timestamp;\n> >               embeddedBuffer = nullptr;\n> >               while (!embeddedQueue_.empty()) {\n> >                       FrameBuffer *b = embeddedQueue_.front();\n> > @@ -1739,7 +1733,7 @@ bool\n> RPiCameraData::findMatchingBuffers(FrameBuffer *&bayerBuffer, FrameBuffer *\n> >                                * the front of the queue. This buffer is\n> now orphaned, so requeue\n> >                                * it back to the device.\n> >                                */\n> > -\n>  unicam_[Unicam::Image].queueBuffer(bayerQueue_.front());\n> > +\n>  unicam_[Unicam::Image].queueBuffer(bayerQueue_.front().buffer);\n> >                               bayerQueue_.pop();\n> >                               bayerRequeueCount++;\n> >                               LOG(RPI, Warning) << \"Dropping unmatched\n> input frame in stream \"\n> > @@ -1757,7 +1751,7 @@ bool\n> RPiCameraData::findMatchingBuffers(FrameBuffer *&bayerBuffer, FrameBuffer *\n> >\n> >                               LOG(RPI, Warning) << \"Flushing bayer\n> stream!\";\n> >                               while (!bayerQueue_.empty()) {\n> > -\n>  unicam_[Unicam::Image].queueBuffer(bayerQueue_.front());\n> > +\n>  unicam_[Unicam::Image].queueBuffer(bayerQueue_.front().buffer);\n> >                                       bayerQueue_.pop();\n> >                               }\n> >                               flushedBuffers = true;\n>\n> --\n> Regards,\n>\n> Laurent Pinchart\n>","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 84641BD1F6\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon, 22 Feb 2021 13:10:06 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 106F168A16;\n\tMon, 22 Feb 2021 14:10:06 +0100 (CET)","from mail-lj1-x234.google.com (mail-lj1-x234.google.com\n\t[IPv6:2a00:1450:4864:20::234])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 2F0D0637DA\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 22 Feb 2021 14:10:04 +0100 (CET)","by mail-lj1-x234.google.com with SMTP id q14so57717676ljp.4\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 22 Feb 2021 05:10:04 -0800 (PST)"],"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=\"HTIStCyD\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=raspberrypi.com; s=google;\n\th=mime-version:references:in-reply-to:from:date:message-id:subject:to\n\t:cc; bh=+uIXuzs5OYP62HpSW+V5yESm/T8pT1XF9B6rHaE+87A=;\n\tb=HTIStCyDTfDqDB/ceArdarEdxWZbrAtMXqT0+Q7Kp1LU5tm0klqmWIexHQhai0nvfd\n\t88a+mw5bNDRQM3Ix7e6EdpldLfRbFTc1rt5NJOnPlJqarCUSxzfDvVUT2EEeDd418Ckn\n\tRvcP88xxBQAp7U6+zgHC8r5112riy1fxmDFZDw/bF9/ECbLCbPXxGn+O/l0kzgYAlK3/\n\tfC+dlBMYLWUExKVE39LGkGXRpyJcBJaf5BmgbAZXrJGBbHJwSdcfP7lwwQ2o8RXFoI1u\n\t8mtMXKElygTJDggmBFFNuDAgqBJH3Ox/FNuRPOJT1ZeBplteCnTA+/kVuRBeApduSvOa\n\tv4mw==","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20161025;\n\th=x-gm-message-state:mime-version:references:in-reply-to:from:date\n\t:message-id:subject:to:cc;\n\tbh=+uIXuzs5OYP62HpSW+V5yESm/T8pT1XF9B6rHaE+87A=;\n\tb=LvhJwQBlg3eVaVxSTb49shSontr7r0yq+DUonZth0zpTtpEbqy+m4LQSXX+w7zb1IL\n\trljeZYS87Htgya0mQJbkMW1n/bFtLAW/X95AEqc0+z4zzbt3R1aNDX9x/3j9UUp1TEsY\n\tPPWXOMvXN+6QAbElLS+RPLqnqeZh92M8/xbLKh8PYXMid9BTyw2Ys4yrx2wlF8LU0u8I\n\th6Y4wnRli0/lj9l6p6dnp2IsB4jfBVwJFKCOGqcIHUaRLHOsR3z7ooXPQ/yhrnLQTAZJ\n\tMAyd/RrXKsObSi/nXgH6mr/+nhOgEOA/c3I8tx6zPt7JC0nLwU53i4aHznnrOM5LqvS5\n\t6cgQ==","X-Gm-Message-State":"AOAM532oba534lp3nPYAVgE/JseixkehBYQAcrOSIaqHorBWIjG2XIsw\n\tmFOa5+KYmdlbhFrzzmRSk7hBILzEuvcHun5FjnXh5A==","X-Google-Smtp-Source":"ABdhPJwOM1ZsCW2BuhHSU+XmrAsFh+kVAV5mYu0JwudGy/js7xjcYfvhOChF4UHxDi9MAAE9G9DbLFsNwTMAhMJdpas=","X-Received":"by 2002:a19:cd1:: with SMTP id\n\t200mr11996973lfm.171.1613999403574; \n\tMon, 22 Feb 2021 05:10:03 -0800 (PST)","MIME-Version":"1.0","References":"<20210218170126.2060783-1-naush@raspberrypi.com>\n\t<20210218170126.2060783-2-naush@raspberrypi.com>\n\t<YDLcIum+IXJcawwm@pendragon.ideasonboard.com>","In-Reply-To":"<YDLcIum+IXJcawwm@pendragon.ideasonboard.com>","From":"Naushir Patuck <naush@raspberrypi.com>","Date":"Mon, 22 Feb 2021 13:09:45 +0000","Message-ID":"<CAEmqJPq9f0imtqbsUt64YaF9VTHr-onxCo94nqjwiTsYRonVow@mail.gmail.com>","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Subject":"Re: [libcamera-devel] [PATCH v3 1/4] pipeline: ipa: raspberrypi:\n\tPass exposure/gain values to IPA though controls","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>","Cc":"libcamera devel <libcamera-devel@lists.libcamera.org>","Content-Type":"multipart/mixed;\n\tboundary=\"===============5769331291087521651==\"","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}}]