[4/4] ipa: rkisp1: Initialize FrameContext.agc.meteringMode
diff mbox series

Message ID 20241016170348.715993-5-jacopo.mondi@ideasonboard.com
State New
Headers show
Series
  • libipa: Initialize FrameContext with ActiveState
Related show

Commit Message

Jacopo Mondi Oct. 16, 2024, 5:03 p.m. UTC
The RkISP1 AGC algorithms assumes the metering mode to be
"MeteringMatrix" and pre-fill an array of weights associated
with it stored in meteringModes_[MeteringMatrix] when intializing
the algorithm in parseMeteringModes().

It laters fetches the weights when computing parameters using the
FrameContext.agc.meteringMode as index of the meteringModes_ array.

When a Request gets queued to the algorithm, the
FrameContext.agc.meteringMode index is populated from the algorithm's
active state, and optionally updated if the application has specified
a different metering mode.

In case of Request underrun however, a non-initialized FrameContext
gets provided to the algorithm, causing an (unhandled) exception when
accessing the meteringModes_ array.

Fix this by intializing the AGC metering mode to a supported value
coming from the ActiveState in IPAFrameContext::init().

Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
---
 src/ipa/rkisp1/algorithms/agc.cpp | 2 ++
 src/ipa/rkisp1/ipa_context.cpp    | 5 +++++
 2 files changed, 7 insertions(+)

Comments

Paul Elder Oct. 21, 2024, 3:25 a.m. UTC | #1
On Wed, Oct 16, 2024 at 07:03:45PM +0200, Jacopo Mondi wrote:
> The RkISP1 AGC algorithms assumes the metering mode to be
> "MeteringMatrix" and pre-fill an array of weights associated
> with it stored in meteringModes_[MeteringMatrix] when intializing
> the algorithm in parseMeteringModes().
> 
> It laters fetches the weights when computing parameters using the
> FrameContext.agc.meteringMode as index of the meteringModes_ array.
> 
> When a Request gets queued to the algorithm, the
> FrameContext.agc.meteringMode index is populated from the algorithm's
> active state, and optionally updated if the application has specified
> a different metering mode.
> 
> In case of Request underrun however, a non-initialized FrameContext
> gets provided to the algorithm, causing an (unhandled) exception when
> accessing the meteringModes_ array.
> 
> Fix this by intializing the AGC metering mode to a supported value
> coming from the ActiveState in IPAFrameContext::init().
> 
> Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
> ---
>  src/ipa/rkisp1/algorithms/agc.cpp | 2 ++
>  src/ipa/rkisp1/ipa_context.cpp    | 5 +++++
>  2 files changed, 7 insertions(+)
> 
> diff --git a/src/ipa/rkisp1/algorithms/agc.cpp b/src/ipa/rkisp1/algorithms/agc.cpp
> index 17d074d9c03e..dd7e9468741e 100644
> --- a/src/ipa/rkisp1/algorithms/agc.cpp
> +++ b/src/ipa/rkisp1/algorithms/agc.cpp
> @@ -175,6 +175,8 @@ int Agc::configure(IPAContext &context, const IPACameraSensorInfo &configInfo)
>  		static_cast<controls::AeConstraintModeEnum>(constraintModes().begin()->first);
>  	context.activeState.agc.exposureMode =
>  		static_cast<controls::AeExposureModeEnum>(exposureModeHelpers().begin()->first);
> +
> +	/* Use the metering matrix mode by default. */

Strictly speaking, this isn't where the metering mode is defaulted to
matrix; it's in parseMeteringModes().

>  	context.activeState.agc.meteringMode =
>  		static_cast<controls::AeMeteringModeEnum>(meteringModes_.begin()->first);
>  
> diff --git a/src/ipa/rkisp1/ipa_context.cpp b/src/ipa/rkisp1/ipa_context.cpp
> index 2dad42b3154f..e88609137345 100644
> --- a/src/ipa/rkisp1/ipa_context.cpp
> +++ b/src/ipa/rkisp1/ipa_context.cpp
> @@ -421,6 +421,11 @@ void IPAFrameContext::init(const uint32_t frameNum,
>  			   const ActiveState &activeState)
>  {
>  	FrameContext::init(frameNum, activeState);
> +
> +	const IPAActiveState *rkisp1ActiveState =
> +		reinterpret_cast<const IPAActiveState *>(&activeState);
> +
> +	agc.meteringMode = rkisp1ActiveState->agc.meteringMode;

But yes this looks like the right solution.

Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>

>  }
>  
>  /**
> -- 
> 2.47.0
>
Stefan Klug Oct. 28, 2024, 4:47 p.m. UTC | #2
Hi Jacopo,

Thank you for the patch. 

On Wed, Oct 16, 2024 at 07:03:45PM +0200, Jacopo Mondi wrote:
> The RkISP1 AGC algorithms assumes the metering mode to be
> "MeteringMatrix" and pre-fill an array of weights associated
> with it stored in meteringModes_[MeteringMatrix] when intializing
> the algorithm in parseMeteringModes().
> 
> It laters fetches the weights when computing parameters using the
> FrameContext.agc.meteringMode as index of the meteringModes_ array.
> 
> When a Request gets queued to the algorithm, the
> FrameContext.agc.meteringMode index is populated from the algorithm's
> active state, and optionally updated if the application has specified
> a different metering mode.
> 
> In case of Request underrun however, a non-initialized FrameContext
> gets provided to the algorithm, causing an (unhandled) exception when
> accessing the meteringModes_ array.
> 
> Fix this by intializing the AGC metering mode to a supported value
> coming from the ActiveState in IPAFrameContext::init().
> 
> Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
> ---
>  src/ipa/rkisp1/algorithms/agc.cpp | 2 ++
>  src/ipa/rkisp1/ipa_context.cpp    | 5 +++++
>  2 files changed, 7 insertions(+)
> 
> diff --git a/src/ipa/rkisp1/algorithms/agc.cpp b/src/ipa/rkisp1/algorithms/agc.cpp
> index 17d074d9c03e..dd7e9468741e 100644
> --- a/src/ipa/rkisp1/algorithms/agc.cpp
> +++ b/src/ipa/rkisp1/algorithms/agc.cpp
> @@ -175,6 +175,8 @@ int Agc::configure(IPAContext &context, const IPACameraSensorInfo &configInfo)
>  		static_cast<controls::AeConstraintModeEnum>(constraintModes().begin()->first);
>  	context.activeState.agc.exposureMode =
>  		static_cast<controls::AeExposureModeEnum>(exposureModeHelpers().begin()->first);
> +
> +	/* Use the metering matrix mode by default. */
>  	context.activeState.agc.meteringMode =
>  		static_cast<controls::AeMeteringModeEnum>(meteringModes_.begin()->first);
>  
> diff --git a/src/ipa/rkisp1/ipa_context.cpp b/src/ipa/rkisp1/ipa_context.cpp
> index 2dad42b3154f..e88609137345 100644
> --- a/src/ipa/rkisp1/ipa_context.cpp
> +++ b/src/ipa/rkisp1/ipa_context.cpp
> @@ -421,6 +421,11 @@ void IPAFrameContext::init(const uint32_t frameNum,
>  			   const ActiveState &activeState)
>  {
>  	FrameContext::init(frameNum, activeState);
> +
> +	const IPAActiveState *rkisp1ActiveState =
> +		reinterpret_cast<const IPAActiveState *>(&activeState);
> +
> +	agc.meteringMode = rkisp1ActiveState->agc.meteringMode;

I don't particularly like that the IPAFrameContext needs that knowledge
which should be internal to the agc algorithm. We could add a
initContext() function to the algorithm base class. But then we need to
loop over all algorithms here, which doesn't feel efficient for a single
case. But on the other hand if we assume that request underruns become
more common, then the other algorithms need to be able to continue with
current data without a prior queueRequest() call. While writing this,
yes I think we should forward that to every algorithm. Otherwise we
might end up with strange effects because the algorithms use the data
from the frame context that was reused without proper initialization.

I hope it is somehow understandable.

Cheers,
Stefan

>  }
>  
>  /**
> -- 
> 2.47.0
>
Jacopo Mondi Oct. 28, 2024, 5:14 p.m. UTC | #3
Hi Stefan

On Mon, Oct 28, 2024 at 05:47:28PM +0100, Stefan Klug wrote:
> Hi Jacopo,
>
> Thank you for the patch.
>
> On Wed, Oct 16, 2024 at 07:03:45PM +0200, Jacopo Mondi wrote:
> > The RkISP1 AGC algorithms assumes the metering mode to be
> > "MeteringMatrix" and pre-fill an array of weights associated
> > with it stored in meteringModes_[MeteringMatrix] when intializing
> > the algorithm in parseMeteringModes().
> >
> > It laters fetches the weights when computing parameters using the
> > FrameContext.agc.meteringMode as index of the meteringModes_ array.
> >
> > When a Request gets queued to the algorithm, the
> > FrameContext.agc.meteringMode index is populated from the algorithm's
> > active state, and optionally updated if the application has specified
> > a different metering mode.
> >
> > In case of Request underrun however, a non-initialized FrameContext
> > gets provided to the algorithm, causing an (unhandled) exception when
> > accessing the meteringModes_ array.
> >
> > Fix this by intializing the AGC metering mode to a supported value
> > coming from the ActiveState in IPAFrameContext::init().
> >
> > Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
> > ---
> >  src/ipa/rkisp1/algorithms/agc.cpp | 2 ++
> >  src/ipa/rkisp1/ipa_context.cpp    | 5 +++++
> >  2 files changed, 7 insertions(+)
> >
> > diff --git a/src/ipa/rkisp1/algorithms/agc.cpp b/src/ipa/rkisp1/algorithms/agc.cpp
> > index 17d074d9c03e..dd7e9468741e 100644
> > --- a/src/ipa/rkisp1/algorithms/agc.cpp
> > +++ b/src/ipa/rkisp1/algorithms/agc.cpp
> > @@ -175,6 +175,8 @@ int Agc::configure(IPAContext &context, const IPACameraSensorInfo &configInfo)
> >  		static_cast<controls::AeConstraintModeEnum>(constraintModes().begin()->first);
> >  	context.activeState.agc.exposureMode =
> >  		static_cast<controls::AeExposureModeEnum>(exposureModeHelpers().begin()->first);
> > +
> > +	/* Use the metering matrix mode by default. */
> >  	context.activeState.agc.meteringMode =
> >  		static_cast<controls::AeMeteringModeEnum>(meteringModes_.begin()->first);
> >
> > diff --git a/src/ipa/rkisp1/ipa_context.cpp b/src/ipa/rkisp1/ipa_context.cpp
> > index 2dad42b3154f..e88609137345 100644
> > --- a/src/ipa/rkisp1/ipa_context.cpp
> > +++ b/src/ipa/rkisp1/ipa_context.cpp
> > @@ -421,6 +421,11 @@ void IPAFrameContext::init(const uint32_t frameNum,
> >  			   const ActiveState &activeState)
> >  {
> >  	FrameContext::init(frameNum, activeState);
> > +
> > +	const IPAActiveState *rkisp1ActiveState =
> > +		reinterpret_cast<const IPAActiveState *>(&activeState);
> > +
> > +	agc.meteringMode = rkisp1ActiveState->agc.meteringMode;
>
> I don't particularly like that the IPAFrameContext needs that knowledge
> which should be internal to the agc algorithm. We could add a

You know, my feeling was the opposite. Algorithms should make less
assumptions about their internals implementation details but rely on a
more globally shared state, kept in the FrameContext and the
ActiveState. This would make easy to "swap" algoroithms.

After all, this already shows the AGC algo depends on a precise
ordering of operations which if not respected results in a run-time
exception.

> initContext() function to the algorithm base class. But then we need to
> loop over all algorithms here, which doesn't feel efficient for a single
> case. But on the other hand if we assume that request underruns become

I considered that but it seemed rather non-efficient.

> more common, then the other algorithms need to be able to continue with
> current data without a prior queueRequest() call. While writing this,

Isn't the "current data" exactly the active state ?

> yes I think we should forward that to every algorithm. Otherwise we
> might end up with strange effects because the algorithms use the data
> from the frame context that was reused without proper initialization.

I might be missing how a FrameContext could be "reused" and "without
proper initialization". I understand a FrameContext might not
initialized, or not correctly re-initalized but if that happens is an
implementation issue, isn't it ?

>
> I hope it is somehow understandable.

Thank you!
  j

>
> Cheers,
> Stefan
>
> >  }
> >
> >  /**
> > --
> > 2.47.0
> >
Stefan Klug Oct. 28, 2024, 6:12 p.m. UTC | #4
Hi Jacopo,

On Mon, Oct 28, 2024 at 06:14:42PM +0100, Jacopo Mondi wrote:
> Hi Stefan
> 
> On Mon, Oct 28, 2024 at 05:47:28PM +0100, Stefan Klug wrote:
> > Hi Jacopo,
> >
> > Thank you for the patch.
> >
> > On Wed, Oct 16, 2024 at 07:03:45PM +0200, Jacopo Mondi wrote:
> > > The RkISP1 AGC algorithms assumes the metering mode to be
> > > "MeteringMatrix" and pre-fill an array of weights associated
> > > with it stored in meteringModes_[MeteringMatrix] when intializing
> > > the algorithm in parseMeteringModes().
> > >
> > > It laters fetches the weights when computing parameters using the
> > > FrameContext.agc.meteringMode as index of the meteringModes_ array.
> > >
> > > When a Request gets queued to the algorithm, the
> > > FrameContext.agc.meteringMode index is populated from the algorithm's
> > > active state, and optionally updated if the application has specified
> > > a different metering mode.
> > >
> > > In case of Request underrun however, a non-initialized FrameContext
> > > gets provided to the algorithm, causing an (unhandled) exception when
> > > accessing the meteringModes_ array.
> > >
> > > Fix this by intializing the AGC metering mode to a supported value
> > > coming from the ActiveState in IPAFrameContext::init().
> > >
> > > Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
> > > ---
> > >  src/ipa/rkisp1/algorithms/agc.cpp | 2 ++
> > >  src/ipa/rkisp1/ipa_context.cpp    | 5 +++++
> > >  2 files changed, 7 insertions(+)
> > >
> > > diff --git a/src/ipa/rkisp1/algorithms/agc.cpp b/src/ipa/rkisp1/algorithms/agc.cpp
> > > index 17d074d9c03e..dd7e9468741e 100644
> > > --- a/src/ipa/rkisp1/algorithms/agc.cpp
> > > +++ b/src/ipa/rkisp1/algorithms/agc.cpp
> > > @@ -175,6 +175,8 @@ int Agc::configure(IPAContext &context, const IPACameraSensorInfo &configInfo)
> > >  		static_cast<controls::AeConstraintModeEnum>(constraintModes().begin()->first);
> > >  	context.activeState.agc.exposureMode =
> > >  		static_cast<controls::AeExposureModeEnum>(exposureModeHelpers().begin()->first);
> > > +
> > > +	/* Use the metering matrix mode by default. */
> > >  	context.activeState.agc.meteringMode =
> > >  		static_cast<controls::AeMeteringModeEnum>(meteringModes_.begin()->first);
> > >
> > > diff --git a/src/ipa/rkisp1/ipa_context.cpp b/src/ipa/rkisp1/ipa_context.cpp
> > > index 2dad42b3154f..e88609137345 100644
> > > --- a/src/ipa/rkisp1/ipa_context.cpp
> > > +++ b/src/ipa/rkisp1/ipa_context.cpp
> > > @@ -421,6 +421,11 @@ void IPAFrameContext::init(const uint32_t frameNum,
> > >  			   const ActiveState &activeState)
> > >  {
> > >  	FrameContext::init(frameNum, activeState);
> > > +
> > > +	const IPAActiveState *rkisp1ActiveState =
> > > +		reinterpret_cast<const IPAActiveState *>(&activeState);
> > > +
> > > +	agc.meteringMode = rkisp1ActiveState->agc.meteringMode;
> >
> > I don't particularly like that the IPAFrameContext needs that knowledge
> > which should be internal to the agc algorithm. We could add a
> 
> You know, my feeling was the opposite. Algorithms should make less
> assumptions about their internals implementation details but rely on a
> more globally shared state, kept in the FrameContext and the
> ActiveState. This would make easy to "swap" algoroithms.
> 
> After all, this already shows the AGC algo depends on a precise
> ordering of operations which if not respected results in a run-time
> exception.
> 
> > initContext() function to the algorithm base class. But then we need to
> > loop over all algorithms here, which doesn't feel efficient for a single
> > case. But on the other hand if we assume that request underruns become
> 
> I considered that but it seemed rather non-efficient.

Agreed, but I'm not sure if we can completely avoid it. Maybe with some
more restructuring...

> 
> > more common, then the other algorithms need to be able to continue with
> > current data without a prior queueRequest() call. While writing this,
> 
> Isn't the "current data" exactly the active state ?

The active state is the current measuered/stats data. But the current manual
(user supplied) data is stored in the frame context.

> 
> > yes I think we should forward that to every algorithm. Otherwise we
> > might end up with strange effects because the algorithms use the data
> > from the frame context that was reused without proper initialization.
> 
> I might be missing how a FrameContext could be "reused" and "without
> proper initialization". I understand a FrameContext might not
> initialized, or not correctly re-initalized but if that happens is an
> implementation issue, isn't it ?

Let's see if I can get this straight. For reference I take the awb algo.
Let's assume the following gets queued:

Frame 1: awb.autoEnable = false, gain.blue = 4
Frame 2: awb.autoEnable = true
... no more requests get queued ...

Let's further assume the FCQueue has a length of 2. Now no more requests
get queued. But process() still get's called because the internal
machinery is now ticked by the sensor producing images. That results in
a cyclic reuse of the two frame comtexts. Now the blue gain gets
constantly toggled between 4 and the automatic value. As the stats
calculation depends on the gains there will be unexpected effects.

I expect there are more cases like this and I'm not sure if we should
handle them inside FrameContext::init()

Maybe I'm missing something obvious...

Cheers,
Stefan

> 
> >
> > I hope it is somehow understandable.
> 
> Thank you!
>   j
> 
> >
> > Cheers,
> > Stefan
> >
> > >  }
> > >
> > >  /**
> > > --
> > > 2.47.0
> > >
Jacopo Mondi Oct. 29, 2024, 7:49 a.m. UTC | #5
Hi Stefan

On Mon, Oct 28, 2024 at 07:12:57PM +0100, Stefan Klug wrote:
> Hi Jacopo,
>
> On Mon, Oct 28, 2024 at 06:14:42PM +0100, Jacopo Mondi wrote:
> > Hi Stefan
> >
> > On Mon, Oct 28, 2024 at 05:47:28PM +0100, Stefan Klug wrote:
> > > Hi Jacopo,
> > >
> > > Thank you for the patch.
> > >
> > > On Wed, Oct 16, 2024 at 07:03:45PM +0200, Jacopo Mondi wrote:
> > > > The RkISP1 AGC algorithms assumes the metering mode to be
> > > > "MeteringMatrix" and pre-fill an array of weights associated
> > > > with it stored in meteringModes_[MeteringMatrix] when intializing
> > > > the algorithm in parseMeteringModes().
> > > >
> > > > It laters fetches the weights when computing parameters using the
> > > > FrameContext.agc.meteringMode as index of the meteringModes_ array.
> > > >
> > > > When a Request gets queued to the algorithm, the
> > > > FrameContext.agc.meteringMode index is populated from the algorithm's
> > > > active state, and optionally updated if the application has specified
> > > > a different metering mode.
> > > >
> > > > In case of Request underrun however, a non-initialized FrameContext
> > > > gets provided to the algorithm, causing an (unhandled) exception when
> > > > accessing the meteringModes_ array.
> > > >
> > > > Fix this by intializing the AGC metering mode to a supported value
> > > > coming from the ActiveState in IPAFrameContext::init().
> > > >
> > > > Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
> > > > ---
> > > >  src/ipa/rkisp1/algorithms/agc.cpp | 2 ++
> > > >  src/ipa/rkisp1/ipa_context.cpp    | 5 +++++
> > > >  2 files changed, 7 insertions(+)
> > > >
> > > > diff --git a/src/ipa/rkisp1/algorithms/agc.cpp b/src/ipa/rkisp1/algorithms/agc.cpp
> > > > index 17d074d9c03e..dd7e9468741e 100644
> > > > --- a/src/ipa/rkisp1/algorithms/agc.cpp
> > > > +++ b/src/ipa/rkisp1/algorithms/agc.cpp
> > > > @@ -175,6 +175,8 @@ int Agc::configure(IPAContext &context, const IPACameraSensorInfo &configInfo)
> > > >  		static_cast<controls::AeConstraintModeEnum>(constraintModes().begin()->first);
> > > >  	context.activeState.agc.exposureMode =
> > > >  		static_cast<controls::AeExposureModeEnum>(exposureModeHelpers().begin()->first);
> > > > +
> > > > +	/* Use the metering matrix mode by default. */
> > > >  	context.activeState.agc.meteringMode =
> > > >  		static_cast<controls::AeMeteringModeEnum>(meteringModes_.begin()->first);
> > > >
> > > > diff --git a/src/ipa/rkisp1/ipa_context.cpp b/src/ipa/rkisp1/ipa_context.cpp
> > > > index 2dad42b3154f..e88609137345 100644
> > > > --- a/src/ipa/rkisp1/ipa_context.cpp
> > > > +++ b/src/ipa/rkisp1/ipa_context.cpp
> > > > @@ -421,6 +421,11 @@ void IPAFrameContext::init(const uint32_t frameNum,
> > > >  			   const ActiveState &activeState)
> > > >  {
> > > >  	FrameContext::init(frameNum, activeState);
> > > > +
> > > > +	const IPAActiveState *rkisp1ActiveState =
> > > > +		reinterpret_cast<const IPAActiveState *>(&activeState);
> > > > +
> > > > +	agc.meteringMode = rkisp1ActiveState->agc.meteringMode;
> > >
> > > I don't particularly like that the IPAFrameContext needs that knowledge
> > > which should be internal to the agc algorithm. We could add a
> >
> > You know, my feeling was the opposite. Algorithms should make less
> > assumptions about their internals implementation details but rely on a
> > more globally shared state, kept in the FrameContext and the
> > ActiveState. This would make easy to "swap" algoroithms.
> >
> > After all, this already shows the AGC algo depends on a precise
> > ordering of operations which if not respected results in a run-time
> > exception.
> >
> > > initContext() function to the algorithm base class. But then we need to
> > > loop over all algorithms here, which doesn't feel efficient for a single
> > > case. But on the other hand if we assume that request underruns become
> >
> > I considered that but it seemed rather non-efficient.
>
> Agreed, but I'm not sure if we can completely avoid it. Maybe with some
> more restructuring...
>
> >
> > > more common, then the other algorithms need to be able to continue with
> > > current data without a prior queueRequest() call. While writing this,
> >
> > Isn't the "current data" exactly the active state ?
>
> The active state is the current measuered/stats data. But the current manual
> (user supplied) data is stored in the frame context.
>

As the name suggests, per-frame settings are indeed stored in the
frame context, but the active state keeps track of the algorithm's
state. In example, the AGC algo for RkISP1 stores in the active state
the auto/manual mode and the associated exposure time and gain.

As I see it, and might be my partial understanding of algos, the
FrameContext can be initialized using the active state values, then:

- when alloc()-ed during a queueRequest values coming from the application
  ControlList will override both the FrameContext and the ActiveState as
  it happens today

- when get() without an alloc (Request underrun) the values it has
  been initialized to will continue to be in use and the algorithms
  can keep operating without a user Request

What I'm not sure about is, in case of alloc(), if we need the
initialization or not, as the newly alloc()-ed FrameContext will be
passed through Algo::queueRequest(), so values copied from the
ActiveState will be overwritten anyway.

> >
> > > yes I think we should forward that to every algorithm. Otherwise we
> > > might end up with strange effects because the algorithms use the data
> > > from the frame context that was reused without proper initialization.
> >
> > I might be missing how a FrameContext could be "reused" and "without
> > proper initialization". I understand a FrameContext might not
> > initialized, or not correctly re-initalized but if that happens is an
> > implementation issue, isn't it ?
>
> Let's see if I can get this straight. For reference I take the awb algo.
> Let's assume the following gets queued:
>
> Frame 1: awb.autoEnable = false, gain.blue = 4
> Frame 2: awb.autoEnable = true
> ... no more requests get queued ...
>
> Let's further assume the FCQueue has a length of 2. Now no more requests
> get queued. But process() still get's called because the internal
> machinery is now ticked by the sensor producing images. That results in
> a cyclic reuse of the two frame comtexts. Now the blue gain gets

That shouldn't happen. What have I missed ?

In my understanding, following your example, we have

FCQ:
        frame#0   frame#1
        +--------+--------+
        |auto = f|auto = t|
        |blue = 4|        |
        +--------+--------+

They both get alloc()-ed and passed through Awb::queueRequest() in
IPA::queueRequest(0) and IPA::queueRequest(1)

The call to Awb::queueRequest(1) will

        void Awb::queueRequest(IPAContext &context,
                               [[maybe_unused]] const uint32_t frame,
                               IPAFrameContext &frameContext,
                               const ControlList &controls)
        {
                auto &awb = context.activeState.awb;

                const auto &awbEnable = controls.get(controls::AwbEnable);
                if (awbEnable && *awbEnable != awb.autoEnabled) {
                        awb.autoEnabled = *awbEnable;

                        LOG(RkISP1Awb, Debug)
                                << (*awbEnable ? "Enabling" : "Disabling") << " AWB";
                }

                ...

         }

So now ActiveContext contains

         awb.autoEnabled = true

Now a new frame gets produced, and no Request has been queued, so we get(2)
without a preceding alloc(2).

We call get(2) and hence we target FCQ[0] which has already been alloc()
to store frame#0 settings

	FrameContext &get(uint32_t frame, const ActiveState &activeState)
	{
		FrameContext &frameContext = contexts_[frame % contexts_.size()];

		if (frame < frameContext.frame)
                        /*
                         * We don't get here, frame = 2,
                         * frameContext.frame = 0
                         */


		if (frame == 0 && !frameContext.initialised)
                        /* We don't get here, frame = 2 */

		if (frame == frameContext.frame)
                        /*
                         * We don't get here, frame = 2,
                         * frameContext.frame = 0
                         */

                /*
                 * So we get here, where we WARN and re-initialize the
                 * frame context
                 */
		LOG(FCQueue, Warning)
			<< "Obtained an uninitialised FrameContext for " << frame;

		frameContext.init(frame, activeState);

		return frameContext;
	}

The frameContext is re-initialized with the current activeState values
so with autoEnabled=true and passed to process().

Now, how do we recover from a Request underrun is what we have to
design, but assuming no futher Request gets queued, I don't see the
algo oscillating between auto and manual.gain.blue = 4.

What am I missing ?

> constantly toggled between 4 and the automatic value. As the stats
> calculation depends on the gains there will be unexpected effects.
>
> I expect there are more cases like this and I'm not sure if we should
> handle them inside FrameContext::init()
>
> Maybe I'm missing something obvious...

Maybe I am.

The above is my interpretation of how thing should work. If we agree
but you see a different behaviour, then we might have a bug somewhere
(which I'm not seeing).

>
> Cheers,
> Stefan

Thanks!

>
> >
> > >
> > > I hope it is somehow understandable.
> >
> > Thank you!
> >   j
> >
> > >
> > > Cheers,
> > > Stefan
> > >
> > > >  }
> > > >
> > > >  /**
> > > > --
> > > > 2.47.0
> > > >
Stefan Klug Oct. 29, 2024, 8:55 a.m. UTC | #6
Hi Jacopo,

On Tue, Oct 29, 2024 at 08:49:26AM +0100, Jacopo Mondi wrote:
> Hi Stefan
> 
> On Mon, Oct 28, 2024 at 07:12:57PM +0100, Stefan Klug wrote:
> > Hi Jacopo,
> >
> > On Mon, Oct 28, 2024 at 06:14:42PM +0100, Jacopo Mondi wrote:
> > > Hi Stefan
> > >
> > > On Mon, Oct 28, 2024 at 05:47:28PM +0100, Stefan Klug wrote:
> > > > Hi Jacopo,
> > > >
> > > > Thank you for the patch.
> > > >
> > > > On Wed, Oct 16, 2024 at 07:03:45PM +0200, Jacopo Mondi wrote:
> > > > > The RkISP1 AGC algorithms assumes the metering mode to be
> > > > > "MeteringMatrix" and pre-fill an array of weights associated
> > > > > with it stored in meteringModes_[MeteringMatrix] when intializing
> > > > > the algorithm in parseMeteringModes().
> > > > >
> > > > > It laters fetches the weights when computing parameters using the
> > > > > FrameContext.agc.meteringMode as index of the meteringModes_ array.
> > > > >
> > > > > When a Request gets queued to the algorithm, the
> > > > > FrameContext.agc.meteringMode index is populated from the algorithm's
> > > > > active state, and optionally updated if the application has specified
> > > > > a different metering mode.
> > > > >
> > > > > In case of Request underrun however, a non-initialized FrameContext
> > > > > gets provided to the algorithm, causing an (unhandled) exception when
> > > > > accessing the meteringModes_ array.
> > > > >
> > > > > Fix this by intializing the AGC metering mode to a supported value
> > > > > coming from the ActiveState in IPAFrameContext::init().
> > > > >
> > > > > Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
> > > > > ---
> > > > >  src/ipa/rkisp1/algorithms/agc.cpp | 2 ++
> > > > >  src/ipa/rkisp1/ipa_context.cpp    | 5 +++++
> > > > >  2 files changed, 7 insertions(+)
> > > > >
> > > > > diff --git a/src/ipa/rkisp1/algorithms/agc.cpp b/src/ipa/rkisp1/algorithms/agc.cpp
> > > > > index 17d074d9c03e..dd7e9468741e 100644
> > > > > --- a/src/ipa/rkisp1/algorithms/agc.cpp
> > > > > +++ b/src/ipa/rkisp1/algorithms/agc.cpp
> > > > > @@ -175,6 +175,8 @@ int Agc::configure(IPAContext &context, const IPACameraSensorInfo &configInfo)
> > > > >  		static_cast<controls::AeConstraintModeEnum>(constraintModes().begin()->first);
> > > > >  	context.activeState.agc.exposureMode =
> > > > >  		static_cast<controls::AeExposureModeEnum>(exposureModeHelpers().begin()->first);
> > > > > +
> > > > > +	/* Use the metering matrix mode by default. */
> > > > >  	context.activeState.agc.meteringMode =
> > > > >  		static_cast<controls::AeMeteringModeEnum>(meteringModes_.begin()->first);
> > > > >
> > > > > diff --git a/src/ipa/rkisp1/ipa_context.cpp b/src/ipa/rkisp1/ipa_context.cpp
> > > > > index 2dad42b3154f..e88609137345 100644
> > > > > --- a/src/ipa/rkisp1/ipa_context.cpp
> > > > > +++ b/src/ipa/rkisp1/ipa_context.cpp
> > > > > @@ -421,6 +421,11 @@ void IPAFrameContext::init(const uint32_t frameNum,
> > > > >  			   const ActiveState &activeState)
> > > > >  {
> > > > >  	FrameContext::init(frameNum, activeState);
> > > > > +
> > > > > +	const IPAActiveState *rkisp1ActiveState =
> > > > > +		reinterpret_cast<const IPAActiveState *>(&activeState);
> > > > > +
> > > > > +	agc.meteringMode = rkisp1ActiveState->agc.meteringMode;
> > > >
> > > > I don't particularly like that the IPAFrameContext needs that knowledge
> > > > which should be internal to the agc algorithm. We could add a
> > >
> > > You know, my feeling was the opposite. Algorithms should make less
> > > assumptions about their internals implementation details but rely on a
> > > more globally shared state, kept in the FrameContext and the
> > > ActiveState. This would make easy to "swap" algoroithms.
> > >
> > > After all, this already shows the AGC algo depends on a precise
> > > ordering of operations which if not respected results in a run-time
> > > exception.
> > >
> > > > initContext() function to the algorithm base class. But then we need to
> > > > loop over all algorithms here, which doesn't feel efficient for a single
> > > > case. But on the other hand if we assume that request underruns become
> > >
> > > I considered that but it seemed rather non-efficient.
> >
> > Agreed, but I'm not sure if we can completely avoid it. Maybe with some
> > more restructuring...
> >
> > >
> > > > more common, then the other algorithms need to be able to continue with
> > > > current data without a prior queueRequest() call. While writing this,
> > >
> > > Isn't the "current data" exactly the active state ?
> >
> > The active state is the current measuered/stats data. But the current manual
> > (user supplied) data is stored in the frame context.
> >
> 
> As the name suggests, per-frame settings are indeed stored in the
> frame context, but the active state keeps track of the algorithm's
> state. In example, the AGC algo for RkISP1 stores in the active state
> the auto/manual mode and the associated exposure time and gain.
> 
> As I see it, and might be my partial understanding of algos, the
> FrameContext can be initialized using the active state values, then:
> 
> - when alloc()-ed during a queueRequest values coming from the application
>   ControlList will override both the FrameContext and the ActiveState as
>   it happens today
> 
> - when get() without an alloc (Request underrun) the values it has
>   been initialized to will continue to be in use and the algorithms
>   can keep operating without a user Request
> 
> What I'm not sure about is, in case of alloc(), if we need the
> initialization or not, as the newly alloc()-ed FrameContext will be
> passed through Algo::queueRequest(), so values copied from the
> ActiveState will be overwritten anyway.

I wrote the following paragraph last. Maybe you should read the rest
first :-)

What about renaming Algorithm::queueRequest to
Algorithm::initFrameContext which gets called once in both code paths
(Either after/in alloc() or after/in get() without prior alloc()) For
the get() without alloc we just pass an empty ControlsList.

> 
> > >
> > > > yes I think we should forward that to every algorithm. Otherwise we
> > > > might end up with strange effects because the algorithms use the data
> > > > from the frame context that was reused without proper initialization.
> > >
> > > I might be missing how a FrameContext could be "reused" and "without
> > > proper initialization". I understand a FrameContext might not
> > > initialized, or not correctly re-initalized but if that happens is an
> > > implementation issue, isn't it ?
> >
> > Let's see if I can get this straight. For reference I take the awb algo.
> > Let's assume the following gets queued:
> >
> > Frame 1: awb.autoEnable = false, gain.blue = 4
> > Frame 2: awb.autoEnable = true
> > ... no more requests get queued ...
> >
> > Let's further assume the FCQueue has a length of 2. Now no more requests
> > get queued. But process() still get's called because the internal
> > machinery is now ticked by the sensor producing images. That results in
> > a cyclic reuse of the two frame comtexts. Now the blue gain gets
> 
> That shouldn't happen. What have I missed ?
> 
> In my understanding, following your example, we have
> 
> FCQ:
>         frame#0   frame#1
>         +--------+--------+
>         |auto = f|auto = t|
>         |blue = 4|        |
>         +--------+--------+
> 
> They both get alloc()-ed and passed through Awb::queueRequest() in
> IPA::queueRequest(0) and IPA::queueRequest(1)
> 
> The call to Awb::queueRequest(1) will
> 
>         void Awb::queueRequest(IPAContext &context,
>                                [[maybe_unused]] const uint32_t frame,
>                                IPAFrameContext &frameContext,
>                                const ControlList &controls)
>         {
>                 auto &awb = context.activeState.awb;
> 
>                 const auto &awbEnable = controls.get(controls::AwbEnable);
>                 if (awbEnable && *awbEnable != awb.autoEnabled) {
>                         awb.autoEnabled = *awbEnable;
> 
>                         LOG(RkISP1Awb, Debug)
>                                 << (*awbEnable ? "Enabling" : "Disabling") << " AWB";
>                 }
> 
>                 ...
> 
>          }
> 
> So now ActiveContext contains
> 
>          awb.autoEnabled = true
> 
> Now a new frame gets produced, and no Request has been queued, so we get(2)
> without a preceding alloc(2).
> 
> We call get(2) and hence we target FCQ[0] which has already been alloc()
> to store frame#0 settings
> 
> 	FrameContext &get(uint32_t frame, const ActiveState &activeState)
> 	{
> 		FrameContext &frameContext = contexts_[frame % contexts_.size()];
> 
> 		if (frame < frameContext.frame)
>                         /*
>                          * We don't get here, frame = 2,
>                          * frameContext.frame = 0
>                          */
> 
> 
> 		if (frame == 0 && !frameContext.initialised)
>                         /* We don't get here, frame = 2 */
> 
> 		if (frame == frameContext.frame)
>                         /*
>                          * We don't get here, frame = 2,
>                          * frameContext.frame = 0
>                          */
> 
>                 /*
>                  * So we get here, where we WARN and re-initialize the
>                  * frame context
>                  */
> 		LOG(FCQueue, Warning)
> 			<< "Obtained an uninitialised FrameContext for " << frame;
> 
> 		frameContext.init(frame, activeState);
> 
> 		return frameContext;
> 	}
> 
> The frameContext is re-initialized with the current activeState values
> so with autoEnabled=true and passed to process().

Here is the catch (or the piece I can't see). Who does the assignment
frameContext.awb.autoEnable = activeState.awb.autoEnable?

It has to be inside the frameContext.init() function. Isn't that the
place where we started off with the discussion? That we could either
hardcode it inside IPAFrameContext::init() if the frame context has
enough information on what to initialize from activeState or forward
that to the corresponding algorithm and implement a
Algorithm::initFrameContext(fc, activeState).

> 
> Now, how do we recover from a Request underrun is what we have to
> design, but assuming no futher Request gets queued, I don't see the
> algo oscillating between auto and manual.gain.blue = 4.
> 
> What am I missing ?

I think we are on the same page. The only difference I see is that I
tend to move things into the agorithms to keep all that logic in one
place and I believe you see the algorithms more as a pluggable formula
which operate on the state that is kept (and known) in frame context and
therefore frame context is able to initialize it.

> 
> > constantly toggled between 4 and the automatic value. As the stats
> > calculation depends on the gains there will be unexpected effects.
> >
> > I expect there are more cases like this and I'm not sure if we should
> > handle them inside FrameContext::init()
> >
> > Maybe I'm missing something obvious...
> 
> Maybe I am.
> 
> The above is my interpretation of how thing should work. If we agree
> but you see a different behaviour, then we might have a bug somewhere
> (which I'm not seeing).

I think we agree in the overall picture. Maybe one approach to solve
this is to further fill the FrameContext::init() to see how much
algorithm specific knowledge we need there. This boils down to copying
all assignments to frameContext from the Algorihm::queueRequest()
functions into the FrameContext::init() or try to rename
Algorithm::queueRequest as proposed above.

Best regards,
Stefan

> 
> >
> > Cheers,
> > Stefan
> 
> Thanks!
> 
> >
> > >
> > > >
> > > > I hope it is somehow understandable.
> > >
> > > Thank you!
> > >   j
> > >
> > > >
> > > > Cheers,
> > > > Stefan
> > > >
> > > > >  }
> > > > >
> > > > >  /**
> > > > > --
> > > > > 2.47.0
> > > > >

Patch
diff mbox series

diff --git a/src/ipa/rkisp1/algorithms/agc.cpp b/src/ipa/rkisp1/algorithms/agc.cpp
index 17d074d9c03e..dd7e9468741e 100644
--- a/src/ipa/rkisp1/algorithms/agc.cpp
+++ b/src/ipa/rkisp1/algorithms/agc.cpp
@@ -175,6 +175,8 @@  int Agc::configure(IPAContext &context, const IPACameraSensorInfo &configInfo)
 		static_cast<controls::AeConstraintModeEnum>(constraintModes().begin()->first);
 	context.activeState.agc.exposureMode =
 		static_cast<controls::AeExposureModeEnum>(exposureModeHelpers().begin()->first);
+
+	/* Use the metering matrix mode by default. */
 	context.activeState.agc.meteringMode =
 		static_cast<controls::AeMeteringModeEnum>(meteringModes_.begin()->first);
 
diff --git a/src/ipa/rkisp1/ipa_context.cpp b/src/ipa/rkisp1/ipa_context.cpp
index 2dad42b3154f..e88609137345 100644
--- a/src/ipa/rkisp1/ipa_context.cpp
+++ b/src/ipa/rkisp1/ipa_context.cpp
@@ -421,6 +421,11 @@  void IPAFrameContext::init(const uint32_t frameNum,
 			   const ActiveState &activeState)
 {
 	FrameContext::init(frameNum, activeState);
+
+	const IPAActiveState *rkisp1ActiveState =
+		reinterpret_cast<const IPAActiveState *>(&activeState);
+
+	agc.meteringMode = rkisp1ActiveState->agc.meteringMode;
 }
 
 /**