Add --tune=ssimulacra2 / AOM_TUNE_SSIMULACRA2

It is restricted to all intra mode.

Bug: aomedia:375221136
Change-Id: I1098c88a967cbcf7d5015f5699f575bbf003aa0f
diff --git a/aom/aomcx.h b/aom/aomcx.h
index 1f2f34f..e8595b73ca 100644
--- a/aom/aomcx.h
+++ b/aom/aomcx.h
@@ -1665,6 +1665,11 @@
  *
  * Changes the encoder to tune for certain types of input material.
  *
+ * \note
+ * AOM_TUNE_SSIMULACRA2 is restricted to all intra mode (AOM_USAGE_ALL_INTRA).
+ * Setting the tuning option to AOM_TUNE_SSIMULACRA2 causes the following
+ * options to be set (expressed as command-line options):
+ *   * --enable-qm=1
  */
 typedef enum {
   AOM_TUNE_PSNR = 0,
@@ -1676,6 +1681,11 @@
   AOM_TUNE_VMAF_NEG_MAX_GAIN = 7,
   AOM_TUNE_BUTTERAUGLI = 8,
   AOM_TUNE_VMAF_SALIENCY_MAP = 9,
+/*!\brief Allows detection of the presence of AOM_TUNE_SSIMULACRA2 at compile
+ * time.
+ */
+#define AOM_HAVE_TUNE_SSIMULACRA2 1
+  AOM_TUNE_SSIMULACRA2 = 10,
 } aom_tune_metric;
 
 /*!\brief Distortion metric to use for RD optimization.
diff --git a/av1/arg_defs.c b/av1/arg_defs.c
index 25d1af7..56bee8a 100644
--- a/av1/arg_defs.c
+++ b/av1/arg_defs.c
@@ -48,6 +48,7 @@
   { "vmaf_neg", AOM_TUNE_VMAF_NEG_MAX_GAIN },
   { "butteraugli", AOM_TUNE_BUTTERAUGLI },
   { "vmaf_saliency_map", AOM_TUNE_VMAF_SALIENCY_MAP },
+  { "ssimulacra2", AOM_TUNE_SSIMULACRA2 },
   { NULL, 0 }
 };
 
diff --git a/av1/av1_cx_iface.c b/av1/av1_cx_iface.c
index cd19c48..1500047 100644
--- a/av1/av1_cx_iface.c
+++ b/av1/av1_cx_iface.c
@@ -849,7 +849,7 @@
   }
 #endif
 
-  RANGE_CHECK(extra_cfg, tuning, AOM_TUNE_PSNR, AOM_TUNE_VMAF_SALIENCY_MAP);
+  RANGE_CHECK(extra_cfg, tuning, AOM_TUNE_PSNR, AOM_TUNE_SSIMULACRA2);
 
   RANGE_CHECK(extra_cfg, dist_metric, AOM_DIST_METRIC_PSNR,
               AOM_DIST_METRIC_QM_PSNR);
@@ -1784,10 +1784,22 @@
   return update_extra_cfg(ctx, &extra_cfg);
 }
 
+static aom_codec_err_t handle_tuning(aom_codec_alg_priv_t *ctx,
+                                     struct av1_extracfg *extra_cfg) {
+  if (extra_cfg->tuning == AOM_TUNE_SSIMULACRA2) {
+    if (ctx->cfg.g_usage != AOM_USAGE_ALL_INTRA) return AOM_CODEC_INCAPABLE;
+    extra_cfg->enable_qm = 1;
+    // TODO: bug 375221136 - set other options in extra_cfg.
+  }
+  return AOM_CODEC_OK;
+}
+
 static aom_codec_err_t ctrl_set_tuning(aom_codec_alg_priv_t *ctx,
                                        va_list args) {
   struct av1_extracfg extra_cfg = ctx->extra_cfg;
   extra_cfg.tuning = CAST(AOME_SET_TUNING, args);
+  aom_codec_err_t err = handle_tuning(ctx, &extra_cfg);
+  if (err != AOM_CODEC_OK) return err;
   return update_extra_cfg(ctx, &extra_cfg);
 }
 
@@ -4099,6 +4111,7 @@
   } else if (arg_match_helper(&arg, &g_av1_codec_arg_defs.tune_metric, argv,
                               err_string)) {
     extra_cfg.tuning = arg_parse_enum_helper(&arg, err_string);
+    err = handle_tuning(ctx, &extra_cfg);
   }
 #if CONFIG_TUNE_VMAF
   else if (arg_match_helper(&arg, &g_av1_codec_arg_defs.vmaf_model_path, argv,
diff --git a/av1/encoder/encodeframe_utils.c b/av1/encoder/encodeframe_utils.c
index 864caa6..d66cb93 100644
--- a/av1/encoder/encodeframe_utils.c
+++ b/av1/encoder/encodeframe_utils.c
@@ -45,7 +45,8 @@
   // BLOCK_4X4 (minimum possible block size), geom_mean_of_scale can go up
   // to 4.8323^1024 and exceed DBL_MAX, resulting in data overflow.
   assert(bsize_base >= BLOCK_8X8);
-  assert(cpi->oxcf.tune_cfg.tuning == AOM_TUNE_SSIM);
+  assert(cpi->oxcf.tune_cfg.tuning == AOM_TUNE_SSIM ||
+         cpi->oxcf.tune_cfg.tuning == AOM_TUNE_SSIMULACRA2);
 
   for (row = mi_row / num_mi_w;
        row < num_rows && row < mi_row / num_mi_w + num_brows; ++row) {
diff --git a/av1/encoder/encoder.c b/av1/encoder/encoder.c
index 4d28bd2..5bd48bc 100644
--- a/av1/encoder/encoder.c
+++ b/av1/encoder/encoder.c
@@ -3785,7 +3785,8 @@
     }
   }
 
-  if (oxcf->tune_cfg.tuning == AOM_TUNE_SSIM) {
+  if (oxcf->tune_cfg.tuning == AOM_TUNE_SSIM ||
+      oxcf->tune_cfg.tuning == AOM_TUNE_SSIMULACRA2) {
     av1_set_mb_ssim_rdmult_scaling(cpi);
   }
 #if CONFIG_SALIENCY_MAP
diff --git a/av1/encoder/partition_search.c b/av1/encoder/partition_search.c
index b6f51d7..27fd1eb 100644
--- a/av1/encoder/partition_search.c
+++ b/av1/encoder/partition_search.c
@@ -615,7 +615,8 @@
   }
 #endif  // !CONFIG_REALTIME_ONLY
 
-  if (cpi->oxcf.tune_cfg.tuning == AOM_TUNE_SSIM) {
+  if (cpi->oxcf.tune_cfg.tuning == AOM_TUNE_SSIM ||
+      cpi->oxcf.tune_cfg.tuning == AOM_TUNE_SSIMULACRA2) {
     av1_set_ssim_rdmult(cpi, &x->errorperbit, bsize, mi_row, mi_col,
                         &x->rdmult);
   }
diff --git a/test/encode_api_test.cc b/test/encode_api_test.cc
index 47ddcc1..24a936d 100644
--- a/test/encode_api_test.cc
+++ b/test/encode_api_test.cc
@@ -109,6 +109,21 @@
   EXPECT_EQ(AOM_CODEC_OK, aom_codec_destroy(&enc));
 }
 
+TEST(EncodeAPI, TuneSsimulacra2NotAllIntra) {
+  aom_codec_iface_t *iface = aom_codec_av1_cx();
+  aom_codec_enc_cfg_t cfg;
+  ASSERT_EQ(aom_codec_enc_config_default(iface, &cfg, AOM_USAGE_REALTIME),
+            AOM_CODEC_OK);
+
+  aom_codec_ctx_t enc;
+  ASSERT_EQ(aom_codec_enc_init(&enc, iface, &cfg, 0), AOM_CODEC_OK);
+
+  ASSERT_EQ(aom_codec_control(&enc, AOME_SET_TUNING, AOM_TUNE_SSIMULACRA2),
+            AOM_CODEC_INCAPABLE);
+
+  ASSERT_EQ(aom_codec_destroy(&enc), AOM_CODEC_OK);
+}
+
 TEST(EncodeAPI, InitializeWithPreset) {
   aom_codec_iface_t *iface = aom_codec_av1_cx();
   aom_codec_ctx_t enc;
@@ -982,6 +997,40 @@
   ASSERT_EQ(aom_codec_destroy(&enc), AOM_CODEC_OK);
 }
 
+TEST(EncodeAPI, AllIntraAndTuneSsimulacra2) {
+  aom_codec_iface_t *iface = aom_codec_av1_cx();
+  aom_codec_enc_cfg_t cfg;
+  ASSERT_EQ(aom_codec_enc_config_default(iface, &cfg, AOM_USAGE_ALL_INTRA),
+            AOM_CODEC_OK);
+
+  aom_codec_ctx_t enc;
+  ASSERT_EQ(aom_codec_enc_init(&enc, iface, &cfg, 0), AOM_CODEC_OK);
+
+  ASSERT_EQ(aom_codec_control(&enc, AOME_SET_TUNING, AOM_TUNE_SSIMULACRA2),
+            AOM_CODEC_OK);
+
+  aom_image_t *image = CreateGrayImage(AOM_IMG_FMT_I420, cfg.g_w, cfg.g_h);
+  ASSERT_NE(image, nullptr);
+
+  ASSERT_EQ(aom_codec_encode(&enc, image, 0, 1, 0), AOM_CODEC_OK);
+  const aom_codec_cx_pkt_t *pkt;
+  aom_codec_iter_t iter = nullptr;
+  pkt = aom_codec_get_cx_data(&enc, &iter);
+  ASSERT_NE(pkt, nullptr);
+  ASSERT_EQ(pkt->kind, AOM_CODEC_CX_FRAME_PKT);
+  pkt = aom_codec_get_cx_data(&enc, &iter);
+  ASSERT_EQ(pkt, nullptr);
+
+  // Flush the encoder.
+  ASSERT_EQ(aom_codec_encode(&enc, nullptr, 0, 0, 0), AOM_CODEC_OK);
+  iter = nullptr;
+  pkt = aom_codec_get_cx_data(&enc, &iter);
+  ASSERT_EQ(pkt, nullptr);
+
+  aom_img_free(image);
+  ASSERT_EQ(aom_codec_destroy(&enc), AOM_CODEC_OK);
+}
+
 // A test that reproduces bug aomedia:3534.
 TEST(EncodeAPI, AllIntraAndNoRefLast) {
   aom_codec_iface_t *iface = aom_codec_av1_cx();