[08/10] ipa: rkisp1: goc: Re-work to use GammaAlgorithm class
diff mbox series

Message ID 20260616-ipu3-libipa-rework-v1-8-d4448b54f1d8@ideasonboard.com
State Superseded
Headers show
Series
  • libipa: Re-work IPU3 IPA to use libipa algorithms
Related show

Commit Message

Dan Scally June 16, 2026, 6:41 a.m. UTC
Re-work the RkISP1 Gamma Out Correction algorithm to use the new
GammaAlgorithm class from libipa, which allows us to share an
implementation with the other IPAs.

Signed-off-by: Daniel Scally <dan.scally@ideasonboard.com>
---
 src/ipa/rkisp1/algorithms/goc.cpp | 49 +++++++++++----------------------------
 src/ipa/rkisp1/algorithms/goc.h   |  5 +++-
 src/ipa/rkisp1/ipa_context.h      | 10 +++-----
 3 files changed, 21 insertions(+), 43 deletions(-)

Comments

Kieran Bingham June 16, 2026, 6:43 p.m. UTC | #1
Quoting Daniel Scally (2026-06-16 07:41:42)
> Re-work the RkISP1 Gamma Out Correction algorithm to use the new
> GammaAlgorithm class from libipa, which allows us to share an
> implementation with the other IPAs.
> 
> Signed-off-by: Daniel Scally <dan.scally@ideasonboard.com>
> ---
>  src/ipa/rkisp1/algorithms/goc.cpp | 49 +++++++++++----------------------------
>  src/ipa/rkisp1/algorithms/goc.h   |  5 +++-
>  src/ipa/rkisp1/ipa_context.h      | 10 +++-----
>  3 files changed, 21 insertions(+), 43 deletions(-)
> 
> diff --git a/src/ipa/rkisp1/algorithms/goc.cpp b/src/ipa/rkisp1/algorithms/goc.cpp
> index e8f64bf3d5e028c2013d37b7ef9fe90b737fad5f..1afd7fb9538d85f771c5b743b856a4963ec8665c 100644
> --- a/src/ipa/rkisp1/algorithms/goc.cpp
> +++ b/src/ipa/rkisp1/algorithms/goc.cpp
> @@ -9,7 +9,7 @@
>  #include <cmath>
>  
>  #include <libcamera/base/log.h>
> -#include <libcamera/base/utils.h>
> +#include <libcamera/base/span.h>
>  
>  #include <libcamera/control_ids.h>
>  
> @@ -44,6 +44,7 @@ namespace ipa::rkisp1::algorithms {
>  LOG_DEFINE_CATEGORY(RkISP1Gamma)
>  
>  const float kDefaultGamma = 2.2f;
> +static constexpr unsigned int kNumLutSegments = RKISP1_CIF_ISP_GAMMA_OUT_MAX_SAMPLES_V10 - 1;
>  
>  /**
>   * \copydoc libcamera::ipa::Algorithm::init
> @@ -57,10 +58,12 @@ int GammaOutCorrection::init(IPAContext &context, const ValueNode &tuningData)
>                 return -EINVAL;
>         }
>  
> -       defaultGamma_ = tuningData["gamma"].get<double>(kDefaultGamma);
> -       context.ctrlMap[&controls::Gamma] = ControlInfo(0.1f, 10.0f, defaultGamma_);
> +       std::array<unsigned int, kNumLutSegments> segments = {
> +                64,  64,  64,  64, 128, 128, 128, 128,
> +               256, 256, 256, 512, 512, 512, 512, 512
> +       };
>  
> -       return 0;
> +       return gammaAlgo_.init(context.ctrlMap, tuningData, segments);
>  }
>  
>  /**
> @@ -69,7 +72,7 @@ int GammaOutCorrection::init(IPAContext &context, const ValueNode &tuningData)
>  int GammaOutCorrection::configure(IPAContext &context,
>                                   [[maybe_unused]] const IPACameraSensorInfo &configInfo)
>  {
> -       context.activeState.goc.gamma = defaultGamma_;
> +       gammaAlgo_.configure(context.activeState.gamma);
>         return 0;
>  }
>  
> @@ -80,17 +83,8 @@ void GammaOutCorrection::queueRequest(IPAContext &context, const uint32_t frame,
>                                       IPAFrameContext &frameContext,
>                                       const ControlList &controls)
>  {
> -       if (frame == 0)
> -               frameContext.goc.update = true;
> -
> -       const auto &gamma = controls.get(controls::Gamma);
> -       if (gamma) {
> -               context.activeState.goc.gamma = *gamma;
> -               frameContext.goc.update = true;
> -               LOG(RkISP1Gamma, Debug) << "Set gamma to " << *gamma;
> -       }
> -
> -       frameContext.goc.gamma = context.activeState.goc.gamma;
> +       gammaAlgo_.queueRequest(context.activeState.gamma, frame,
> +                               frameContext.gamma, controls);
>  }
>  
>  /**
> @@ -104,29 +98,14 @@ void GammaOutCorrection::prepare(IPAContext &context,
>         ASSERT(context.hw.numGammaOutSamples ==
>                RKISP1_CIF_ISP_GAMMA_OUT_MAX_SAMPLES_V10);
>  
> -       if (!frameContext.goc.update)
> +       if (!frameContext.gamma.update)
>                 return;
>  
> -       /*
> -        * The logarithmic segments as specified in the reference.
> -        * Plus an additional 0 to make the loop easier
> -        */
> -       static constexpr std::array<unsigned int, RKISP1_CIF_ISP_GAMMA_OUT_MAX_SAMPLES_V10> segments = {
> -               64, 64, 64, 64, 128, 128, 128, 128, 256,
> -               256, 256, 512, 512, 512, 512, 512, 0
> -       };
> -
>         auto config = params->block<BlockType::Goc>();
>         config.setEnabled(true);
>  
> -       __u16 *gamma_y = config->gamma_y;
> -
> -       unsigned x = 0;
> -       for (const auto [i, size] : utils::enumerate(segments)) {
> -               gamma_y[i] = std::pow(x / 4096.0, 1.0 / frameContext.goc.gamma) * 1023.0;
> -               x += size;
> -       }
> -
> +       Span<uint16_t> lut{ config->gamma_y };
> +       gammaAlgo_.prepare(frameContext.gamma, lut);
>         config->mode = RKISP1_CIF_ISP_GOC_MODE_LOGARITHMIC;
>  }
>  
> @@ -139,7 +118,7 @@ void GammaOutCorrection::process([[maybe_unused]] IPAContext &context,
>                                  [[maybe_unused]] const rkisp1_stat_buffer *stats,
>                                  ControlList &metadata)
>  {
> -       metadata.set(controls::Gamma, frameContext.goc.gamma);
> +       gammaAlgo_.process(frameContext.gamma, metadata);
>  }
>  
>  REGISTER_IPA_ALGORITHM(GammaOutCorrection, "GammaOutCorrection")
> diff --git a/src/ipa/rkisp1/algorithms/goc.h b/src/ipa/rkisp1/algorithms/goc.h
> index bd79fe19cc86b8aefa2603e98e9d7130b44105d9..f7059d206c935fa72195f36091cf953e5f2413e1 100644
> --- a/src/ipa/rkisp1/algorithms/goc.h
> +++ b/src/ipa/rkisp1/algorithms/goc.h
> @@ -9,6 +9,9 @@
>  
>  #include "algorithm.h"
>  
> +#include <libipa/fixedpoint.h>
> +#include <libipa/gamma.h>
> +
>  namespace libcamera {
>  
>  namespace ipa::rkisp1::algorithms {
> @@ -35,7 +38,7 @@ public:
>                      ControlList &metadata) override;
>  
>  private:
> -       float defaultGamma_;
> +       GammaAlgorithm<RKISP1_CIF_ISP_GAMMA_OUT_MAX_SAMPLES_V10, UQ<0, 10>> gammaAlgo_;

Q and UQ seemed to hurt so much making them - so I'm /really/ glad to
see how useful they're becoming as a way to convey units and precision
through these types.



>  };
>  
>  } /* namespace ipa::rkisp1::algorithms */
> diff --git a/src/ipa/rkisp1/ipa_context.h b/src/ipa/rkisp1/ipa_context.h
> index 005f4102b4f6d465fd370c20060331a4f3b3a943..03be79085052d5161ec57d97fe9600f4bf7e93c4 100644
> --- a/src/ipa/rkisp1/ipa_context.h
> +++ b/src/ipa/rkisp1/ipa_context.h
> @@ -30,6 +30,7 @@
>  #include "libipa/ccm.h"
>  #include "libipa/fc_queue.h"
>  #include "libipa/fixedpoint.h"
> +#include "libipa/gamma.h"
>  #include "libipa/lsc.h"
>  
>  namespace libcamera {
> @@ -124,9 +125,7 @@ struct IPAActiveState {
>                 uint8_t sharpness;
>         } filter;
>  
> -       struct {
> -               double gamma;
> -       } goc;
> +       ipa::gamma::ActiveState gamma;
>  
>         struct {
>                 double lux;
> @@ -190,10 +189,7 @@ struct IPAFrameContext : public FrameContext {
>                 bool update;
>         } filter;
>  
> -       struct {
> -               double gamma;
> -               bool update;
> -       } goc;
> +       ipa::gamma::FrameContext gamma;

Oh, I'm at the bottom already.

Well that was easy ... 

Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>

>  
>         struct {
>                 uint32_t exposure;
> 
> -- 
> 2.43.0
>
Jacopo Mondi June 17, 2026, 9:53 a.m. UTC | #2
Hi Dan

On Tue, Jun 16, 2026 at 07:41:42AM +0100, Daniel Scally wrote:
> Re-work the RkISP1 Gamma Out Correction algorithm to use the new
> GammaAlgorithm class from libipa, which allows us to share an
> implementation with the other IPAs.
>
> Signed-off-by: Daniel Scally <dan.scally@ideasonboard.com>
> ---
>  src/ipa/rkisp1/algorithms/goc.cpp | 49 +++++++++++----------------------------
>  src/ipa/rkisp1/algorithms/goc.h   |  5 +++-
>  src/ipa/rkisp1/ipa_context.h      | 10 +++-----
>  3 files changed, 21 insertions(+), 43 deletions(-)
>
> diff --git a/src/ipa/rkisp1/algorithms/goc.cpp b/src/ipa/rkisp1/algorithms/goc.cpp
> index e8f64bf3d5e028c2013d37b7ef9fe90b737fad5f..1afd7fb9538d85f771c5b743b856a4963ec8665c 100644
> --- a/src/ipa/rkisp1/algorithms/goc.cpp
> +++ b/src/ipa/rkisp1/algorithms/goc.cpp
> @@ -9,7 +9,7 @@
>  #include <cmath>
>
>  #include <libcamera/base/log.h>
> -#include <libcamera/base/utils.h>
> +#include <libcamera/base/span.h>
>
>  #include <libcamera/control_ids.h>
>
> @@ -44,6 +44,7 @@ namespace ipa::rkisp1::algorithms {

I think the comment

/**
 * \class GammaOutCorrection
 * \brief RkISP1 Gamma out correction
 *
 * This algorithm implements the gamma out curve for the RkISP1. It defaults to
 * a gamma value of 2.2.
 *
 * As gamma is internally represented as a piecewise linear function with only
 * 17 knots, the difference between gamma=2.2 and sRGB gamma is minimal.
 * Therefore sRGB gamma was not implemented as special case.
 *
 * Useful links:
 * - https://www.cambridgeincolour.com/tutorials/gamma-correction.htm
 * - https://en.wikipedia.org/wiki/SRGB
 */

Can now be simplified/dropped

>  LOG_DEFINE_CATEGORY(RkISP1Gamma)
>
>  const float kDefaultGamma = 2.2f;
> +static constexpr unsigned int kNumLutSegments = RKISP1_CIF_ISP_GAMMA_OUT_MAX_SAMPLES_V10 - 1;
>
>  /**
>   * \copydoc libcamera::ipa::Algorithm::init
> @@ -57,10 +58,12 @@ int GammaOutCorrection::init(IPAContext &context, const ValueNode &tuningData)
>  		return -EINVAL;
>  	}
>
> -	defaultGamma_ = tuningData["gamma"].get<double>(kDefaultGamma);
> -	context.ctrlMap[&controls::Gamma] = ControlInfo(0.1f, 10.0f, defaultGamma_);
> +	std::array<unsigned int, kNumLutSegments> segments = {
> +		 64,  64,  64,  64, 128, 128, 128, 128,
> +		256, 256, 256, 512, 512, 512, 512, 512
> +	};
>
> -	return 0;
> +	return gammaAlgo_.init(context.ctrlMap, tuningData, segments);
>  }
>
>  /**
> @@ -69,7 +72,7 @@ int GammaOutCorrection::init(IPAContext &context, const ValueNode &tuningData)
>  int GammaOutCorrection::configure(IPAContext &context,
>  				  [[maybe_unused]] const IPACameraSensorInfo &configInfo)
>  {
> -	context.activeState.goc.gamma = defaultGamma_;
> +	gammaAlgo_.configure(context.activeState.gamma);
>  	return 0;
>  }
>
> @@ -80,17 +83,8 @@ void GammaOutCorrection::queueRequest(IPAContext &context, const uint32_t frame,
>  				      IPAFrameContext &frameContext,
>  				      const ControlList &controls)
>  {
> -	if (frame == 0)
> -		frameContext.goc.update = true;
> -
> -	const auto &gamma = controls.get(controls::Gamma);
> -	if (gamma) {
> -		context.activeState.goc.gamma = *gamma;
> -		frameContext.goc.update = true;
> -		LOG(RkISP1Gamma, Debug) << "Set gamma to " << *gamma;
> -	}
> -
> -	frameContext.goc.gamma = context.activeState.goc.gamma;
> +	gammaAlgo_.queueRequest(context.activeState.gamma, frame,
> +				frameContext.gamma, controls);
>  }
>
>  /**
> @@ -104,29 +98,14 @@ void GammaOutCorrection::prepare(IPAContext &context,
>  	ASSERT(context.hw.numGammaOutSamples ==
>  	       RKISP1_CIF_ISP_GAMMA_OUT_MAX_SAMPLES_V10);
>
> -	if (!frameContext.goc.update)
> +	if (!frameContext.gamma.update)
>  		return;
>
> -	/*
> -	 * The logarithmic segments as specified in the reference.
> -	 * Plus an additional 0 to make the loop easier
> -	 */
> -	static constexpr std::array<unsigned int, RKISP1_CIF_ISP_GAMMA_OUT_MAX_SAMPLES_V10> segments = {
> -		64, 64, 64, 64, 128, 128, 128, 128, 256,
> -		256, 256, 512, 512, 512, 512, 512, 0
> -	};
> -
>  	auto config = params->block<BlockType::Goc>();
>  	config.setEnabled(true);
>
> -	__u16 *gamma_y = config->gamma_y;
> -
> -	unsigned x = 0;
> -	for (const auto [i, size] : utils::enumerate(segments)) {
> -		gamma_y[i] = std::pow(x / 4096.0, 1.0 / frameContext.goc.gamma) * 1023.0;
> -		x += size;
> -	}
> -
> +	Span<uint16_t> lut{ config->gamma_y };
> +	gammaAlgo_.prepare(frameContext.gamma, lut);
>  	config->mode = RKISP1_CIF_ISP_GOC_MODE_LOGARITHMIC;
>  }
>
> @@ -139,7 +118,7 @@ void GammaOutCorrection::process([[maybe_unused]] IPAContext &context,
>  				 [[maybe_unused]] const rkisp1_stat_buffer *stats,
>  				 ControlList &metadata)
>  {
> -	metadata.set(controls::Gamma, frameContext.goc.gamma);
> +	gammaAlgo_.process(frameContext.gamma, metadata);
>  }
>
>  REGISTER_IPA_ALGORITHM(GammaOutCorrection, "GammaOutCorrection")
> diff --git a/src/ipa/rkisp1/algorithms/goc.h b/src/ipa/rkisp1/algorithms/goc.h
> index bd79fe19cc86b8aefa2603e98e9d7130b44105d9..f7059d206c935fa72195f36091cf953e5f2413e1 100644
> --- a/src/ipa/rkisp1/algorithms/goc.h
> +++ b/src/ipa/rkisp1/algorithms/goc.h
> @@ -9,6 +9,9 @@
>
>  #include "algorithm.h"
>
> +#include <libipa/fixedpoint.h>
> +#include <libipa/gamma.h>
> +

Same comment as per the previous patch: please check headers

>  namespace libcamera {
>
>  namespace ipa::rkisp1::algorithms {
> @@ -35,7 +38,7 @@ public:
>  		     ControlList &metadata) override;
>
>  private:
> -	float defaultGamma_;
> +	GammaAlgorithm<RKISP1_CIF_ISP_GAMMA_OUT_MAX_SAMPLES_V10, UQ<0, 10>> gammaAlgo_;
>  };
>
>  } /* namespace ipa::rkisp1::algorithms */
> diff --git a/src/ipa/rkisp1/ipa_context.h b/src/ipa/rkisp1/ipa_context.h
> index 005f4102b4f6d465fd370c20060331a4f3b3a943..03be79085052d5161ec57d97fe9600f4bf7e93c4 100644
> --- a/src/ipa/rkisp1/ipa_context.h
> +++ b/src/ipa/rkisp1/ipa_context.h
> @@ -30,6 +30,7 @@
>  #include "libipa/ccm.h"
>  #include "libipa/fc_queue.h"
>  #include "libipa/fixedpoint.h"
> +#include "libipa/gamma.h"
>  #include "libipa/lsc.h"
>
>  namespace libcamera {
> @@ -124,9 +125,7 @@ struct IPAActiveState {
>  		uint8_t sharpness;
>  	} filter;
>
> -	struct {
> -		double gamma;
> -	} goc;
> +	ipa::gamma::ActiveState gamma;
>
>  	struct {
>  		double lux;
> @@ -190,10 +189,7 @@ struct IPAFrameContext : public FrameContext {
>  		bool update;
>  	} filter;
>
> -	struct {
> -		double gamma;
> -		bool update;
> -	} goc;
> +	ipa::gamma::FrameContext gamma;
>
>  	struct {
>  		uint32_t exposure;
>

All minors
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>

> --
> 2.43.0
>

Patch
diff mbox series

diff --git a/src/ipa/rkisp1/algorithms/goc.cpp b/src/ipa/rkisp1/algorithms/goc.cpp
index e8f64bf3d5e028c2013d37b7ef9fe90b737fad5f..1afd7fb9538d85f771c5b743b856a4963ec8665c 100644
--- a/src/ipa/rkisp1/algorithms/goc.cpp
+++ b/src/ipa/rkisp1/algorithms/goc.cpp
@@ -9,7 +9,7 @@ 
 #include <cmath>
 
 #include <libcamera/base/log.h>
-#include <libcamera/base/utils.h>
+#include <libcamera/base/span.h>
 
 #include <libcamera/control_ids.h>
 
@@ -44,6 +44,7 @@  namespace ipa::rkisp1::algorithms {
 LOG_DEFINE_CATEGORY(RkISP1Gamma)
 
 const float kDefaultGamma = 2.2f;
+static constexpr unsigned int kNumLutSegments = RKISP1_CIF_ISP_GAMMA_OUT_MAX_SAMPLES_V10 - 1;
 
 /**
  * \copydoc libcamera::ipa::Algorithm::init
@@ -57,10 +58,12 @@  int GammaOutCorrection::init(IPAContext &context, const ValueNode &tuningData)
 		return -EINVAL;
 	}
 
-	defaultGamma_ = tuningData["gamma"].get<double>(kDefaultGamma);
-	context.ctrlMap[&controls::Gamma] = ControlInfo(0.1f, 10.0f, defaultGamma_);
+	std::array<unsigned int, kNumLutSegments> segments = {
+		 64,  64,  64,  64, 128, 128, 128, 128,
+		256, 256, 256, 512, 512, 512, 512, 512
+	};
 
-	return 0;
+	return gammaAlgo_.init(context.ctrlMap, tuningData, segments);
 }
 
 /**
@@ -69,7 +72,7 @@  int GammaOutCorrection::init(IPAContext &context, const ValueNode &tuningData)
 int GammaOutCorrection::configure(IPAContext &context,
 				  [[maybe_unused]] const IPACameraSensorInfo &configInfo)
 {
-	context.activeState.goc.gamma = defaultGamma_;
+	gammaAlgo_.configure(context.activeState.gamma);
 	return 0;
 }
 
@@ -80,17 +83,8 @@  void GammaOutCorrection::queueRequest(IPAContext &context, const uint32_t frame,
 				      IPAFrameContext &frameContext,
 				      const ControlList &controls)
 {
-	if (frame == 0)
-		frameContext.goc.update = true;
-
-	const auto &gamma = controls.get(controls::Gamma);
-	if (gamma) {
-		context.activeState.goc.gamma = *gamma;
-		frameContext.goc.update = true;
-		LOG(RkISP1Gamma, Debug) << "Set gamma to " << *gamma;
-	}
-
-	frameContext.goc.gamma = context.activeState.goc.gamma;
+	gammaAlgo_.queueRequest(context.activeState.gamma, frame,
+				frameContext.gamma, controls);
 }
 
 /**
@@ -104,29 +98,14 @@  void GammaOutCorrection::prepare(IPAContext &context,
 	ASSERT(context.hw.numGammaOutSamples ==
 	       RKISP1_CIF_ISP_GAMMA_OUT_MAX_SAMPLES_V10);
 
-	if (!frameContext.goc.update)
+	if (!frameContext.gamma.update)
 		return;
 
-	/*
-	 * The logarithmic segments as specified in the reference.
-	 * Plus an additional 0 to make the loop easier
-	 */
-	static constexpr std::array<unsigned int, RKISP1_CIF_ISP_GAMMA_OUT_MAX_SAMPLES_V10> segments = {
-		64, 64, 64, 64, 128, 128, 128, 128, 256,
-		256, 256, 512, 512, 512, 512, 512, 0
-	};
-
 	auto config = params->block<BlockType::Goc>();
 	config.setEnabled(true);
 
-	__u16 *gamma_y = config->gamma_y;
-
-	unsigned x = 0;
-	for (const auto [i, size] : utils::enumerate(segments)) {
-		gamma_y[i] = std::pow(x / 4096.0, 1.0 / frameContext.goc.gamma) * 1023.0;
-		x += size;
-	}
-
+	Span<uint16_t> lut{ config->gamma_y };
+	gammaAlgo_.prepare(frameContext.gamma, lut);
 	config->mode = RKISP1_CIF_ISP_GOC_MODE_LOGARITHMIC;
 }
 
@@ -139,7 +118,7 @@  void GammaOutCorrection::process([[maybe_unused]] IPAContext &context,
 				 [[maybe_unused]] const rkisp1_stat_buffer *stats,
 				 ControlList &metadata)
 {
-	metadata.set(controls::Gamma, frameContext.goc.gamma);
+	gammaAlgo_.process(frameContext.gamma, metadata);
 }
 
 REGISTER_IPA_ALGORITHM(GammaOutCorrection, "GammaOutCorrection")
diff --git a/src/ipa/rkisp1/algorithms/goc.h b/src/ipa/rkisp1/algorithms/goc.h
index bd79fe19cc86b8aefa2603e98e9d7130b44105d9..f7059d206c935fa72195f36091cf953e5f2413e1 100644
--- a/src/ipa/rkisp1/algorithms/goc.h
+++ b/src/ipa/rkisp1/algorithms/goc.h
@@ -9,6 +9,9 @@ 
 
 #include "algorithm.h"
 
+#include <libipa/fixedpoint.h>
+#include <libipa/gamma.h>
+
 namespace libcamera {
 
 namespace ipa::rkisp1::algorithms {
@@ -35,7 +38,7 @@  public:
 		     ControlList &metadata) override;
 
 private:
-	float defaultGamma_;
+	GammaAlgorithm<RKISP1_CIF_ISP_GAMMA_OUT_MAX_SAMPLES_V10, UQ<0, 10>> gammaAlgo_;
 };
 
 } /* namespace ipa::rkisp1::algorithms */
diff --git a/src/ipa/rkisp1/ipa_context.h b/src/ipa/rkisp1/ipa_context.h
index 005f4102b4f6d465fd370c20060331a4f3b3a943..03be79085052d5161ec57d97fe9600f4bf7e93c4 100644
--- a/src/ipa/rkisp1/ipa_context.h
+++ b/src/ipa/rkisp1/ipa_context.h
@@ -30,6 +30,7 @@ 
 #include "libipa/ccm.h"
 #include "libipa/fc_queue.h"
 #include "libipa/fixedpoint.h"
+#include "libipa/gamma.h"
 #include "libipa/lsc.h"
 
 namespace libcamera {
@@ -124,9 +125,7 @@  struct IPAActiveState {
 		uint8_t sharpness;
 	} filter;
 
-	struct {
-		double gamma;
-	} goc;
+	ipa::gamma::ActiveState gamma;
 
 	struct {
 		double lux;
@@ -190,10 +189,7 @@  struct IPAFrameContext : public FrameContext {
 		bool update;
 	} filter;
 
-	struct {
-		double gamma;
-		bool update;
-	} goc;
+	ipa::gamma::FrameContext gamma;
 
 	struct {
 		uint32_t exposure;