From 537aa0c698f02fb1238c04ff9b87f8edf37d7f5e Mon Sep 17 00:00:00 2001
From: Yang Wang <wangyang.cs@bytedance.com>
Date: Fri, 26 Jul 2024 05:51:17 +0000
Subject: [PATCH] JVET-AI0082: GPM with inter prediction and IBC and temporal
 BV (Test 3.3b)

---
 source/App/EncoderApp/EncApp.cpp          |   3 +
 source/App/EncoderApp/EncAppCfg.cpp       |   6 +
 source/App/EncoderApp/EncAppCfg.h         |   3 +
 source/Lib/CommonLib/CommonDef.h          |  12 +
 source/Lib/CommonLib/Contexts.h           |   4 +
 source/Lib/CommonLib/Contexts_ecm13.inl   |  50 ++
 source/Lib/CommonLib/Contexts_ecm14.0.inl |  50 ++
 source/Lib/CommonLib/InterPrediction.cpp  | 159 +++++++
 source/Lib/CommonLib/InterPrediction.h    |  67 +++
 source/Lib/CommonLib/IntraPrediction.cpp  |   4 +
 source/Lib/CommonLib/Slice.h              |   7 +
 source/Lib/CommonLib/TypeDef.h            |   2 +
 source/Lib/CommonLib/Unit.cpp             |   9 +
 source/Lib/CommonLib/Unit.h               |   3 +
 source/Lib/CommonLib/UnitTools.cpp        | 533 ++++++++++++++++++++++
 source/Lib/CommonLib/UnitTools.h          |  14 +-
 source/Lib/DecoderLib/CABACReader.cpp     |  95 ++++
 source/Lib/DecoderLib/CABACReader.h       |   4 +
 source/Lib/DecoderLib/DecCu.cpp           |   6 +
 source/Lib/DecoderLib/DecCu.h             |   3 +
 source/Lib/DecoderLib/VLCReader.cpp       |   4 +
 source/Lib/EncoderLib/CABACWriter.cpp     | 102 +++++
 source/Lib/EncoderLib/CABACWriter.h       |   4 +
 source/Lib/EncoderLib/EncCfg.h            |   7 +
 source/Lib/EncoderLib/EncCu.cpp           | 414 ++++++++++++++++-
 source/Lib/EncoderLib/EncCu.h             |  18 +
 source/Lib/EncoderLib/EncLib.cpp          |   3 +
 source/Lib/EncoderLib/EncSlice.cpp        |  12 +
 source/Lib/EncoderLib/InterSearch.cpp     |  44 ++
 source/Lib/EncoderLib/InterSearch.h       |  57 +++
 source/Lib/EncoderLib/VLCWriter.cpp       |   3 +
 31 files changed, 1700 insertions(+), 2 deletions(-)

diff --git a/source/App/EncoderApp/EncApp.cpp b/source/App/EncoderApp/EncApp.cpp
index db5c26b3b..878ed3a23 100644
--- a/source/App/EncoderApp/EncApp.cpp
+++ b/source/App/EncoderApp/EncApp.cpp
@@ -970,6 +970,9 @@ void EncApp::xInitLibCfg()
   m_cEncLib.setUseCiipTimd                                       (m_ciipTimd);
 #endif
   m_cEncLib.setUseGeo                                            ( m_Geo );
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+  m_cEncLib.setUseGeoInterIbc                                    ( m_Geo ? m_geoInterIbc : false );
+#endif
   m_cEncLib.setUseHashME                                         ( m_HashME );
 
   m_cEncLib.setAllowDisFracMMVD                                  ( m_allowDisFracMMVD );
diff --git a/source/App/EncoderApp/EncAppCfg.cpp b/source/App/EncoderApp/EncAppCfg.cpp
index 05f6b088a..60c27c6eb 100644
--- a/source/App/EncoderApp/EncAppCfg.cpp
+++ b/source/App/EncoderApp/EncAppCfg.cpp
@@ -1186,6 +1186,9 @@ bool EncAppCfg::parseCfg( int argc, char* argv[] )
   ("CIIPTIMD",                                        m_ciipTimd,                                       true, "Enable CIIP-TIMD mode")
 #endif
   ("Geo",                                             m_Geo,                                            false, "Enable geometric partitioning mode (0:off, 1:on)")
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+  ("GeoInterIbc",                                     m_geoInterIbc,                                    true, "GPM with inter and IBC (0:off, 1:on)  [default: off]" )
+#endif
   ("HashME",                                          m_HashME,                                         false, "Enable hash motion estimation (0:off, 1:on)")
 
   ("AllowDisFracMMVD",                                m_allowDisFracMMVD,                               false, "Disable fractional MVD in MMVD mode adaptively")
@@ -5645,6 +5648,9 @@ void EncAppCfg::xPrintParameter()
     msg(VERBOSE, "CIIPAffine:%d ", m_useCiipAffine);
 #endif
     msg( VERBOSE, "Geo:%d ", m_Geo );
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+    msg( VERBOSE, "GeoInterIbc:%d ", m_geoInterIbc );
+#endif
     m_allowDisFracMMVD = m_MMVD ? m_allowDisFracMMVD : false;
     if ( m_MMVD )
       msg(VERBOSE, "AllowDisFracMMVD:%d ", m_allowDisFracMMVD);
diff --git a/source/App/EncoderApp/EncAppCfg.h b/source/App/EncoderApp/EncAppCfg.h
index fb0a2f886..61eac5f61 100644
--- a/source/App/EncoderApp/EncAppCfg.h
+++ b/source/App/EncoderApp/EncAppCfg.h
@@ -534,6 +534,9 @@ protected:
   bool      m_ciipTimd;
 #endif
   bool      m_Geo;
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+  bool      m_geoInterIbc;
+#endif
   bool      m_HashME;
   bool      m_allowDisFracMMVD;
   bool      m_AffineAmvr;
diff --git a/source/Lib/CommonLib/CommonDef.h b/source/Lib/CommonLib/CommonDef.h
index 03f70d2f1..1cfe4b347 100644
--- a/source/Lib/CommonLib/CommonDef.h
+++ b/source/Lib/CommonLib/CommonDef.h
@@ -1415,7 +1415,12 @@ static const int GEO_MAX_NUM_UNI_AFF_CANDS_ARMC                     = 22;
 
 #if JVET_Y0065_GPM_INTRA
 static const int GEO_MAX_NUM_INTRA_CANDS =                          3;
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+static const int GEO_MAX_NUM_IBC_CANDS =                            4;
+static const int GEO_NUM_INTRA_RDO_BUFFER =                         23 + GEO_MAX_NUM_IBC_CANDS;
+#else
 static const int GEO_NUM_INTRA_RDO_BUFFER =                         23;
+#endif
 #if JVET_AG0112_REGRESSION_BASED_GPM_BLENDING
 static const int GEO_BLEND_MAX_NUM_CANDS =                        ((GEO_MAX_NUM_UNI_CANDS + 1) >> 1) * ((GEO_MAX_NUM_UNI_CANDS + 1) >> 1) / 2;
 static const int GEO_NUM_RDO_BUFFER =                               GEO_MAX_NUM_UNI_CANDS + 67 + 1 + 1;
@@ -1426,7 +1431,11 @@ static const int GEO_NUM_RDO_BUFFER =                               GEO_MAX_NUM_
 static const int GEO_NUM_RDO_BUFFER =                               GEO_MAX_NUM_UNI_CANDS + GEO_NUM_INTRA_RDO_BUFFER;
 #endif
 #endif
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+static const int GEO_MAX_NUM_CANDS = (GEO_MAX_NUM_UNI_CANDS+GEO_MAX_NUM_INTRA_CANDS+GEO_MAX_NUM_IBC_CANDS) * ((GEO_MAX_NUM_UNI_CANDS+GEO_MAX_NUM_INTRA_CANDS+GEO_MAX_NUM_IBC_CANDS) - 1);
+#else
 static const int GEO_MAX_NUM_CANDS = (GEO_MAX_NUM_UNI_CANDS+GEO_MAX_NUM_INTRA_CANDS) * ((GEO_MAX_NUM_UNI_CANDS+GEO_MAX_NUM_INTRA_CANDS) - 1);
+#endif
 #else
 static const int GEO_MAX_NUM_CANDS = GEO_MAX_NUM_UNI_CANDS * (GEO_MAX_NUM_UNI_CANDS - 1);
 #endif
@@ -1469,6 +1478,9 @@ static const int GEO_ENC_MMVD_MAX_REFINE_NUM_ADJ = 1 // regular merge(1)
 #if JVET_Y0065_GPM_INTRA
                                                  + 1 // intra(1)
 #endif
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+                                                 + 1 // IBC(1)
+#endif
 ;
 #else
 static const int GEO_MV_MASK_SIZE =         GEO_WEIGHT_MASK_SIZE >> 2;
diff --git a/source/Lib/CommonLib/Contexts.h b/source/Lib/CommonLib/Contexts.h
index ae31d29cc..51201653b 100644
--- a/source/Lib/CommonLib/Contexts.h
+++ b/source/Lib/CommonLib/Contexts.h
@@ -494,6 +494,10 @@ public:
 #endif
 #if JVET_Y0065_GPM_INTRA
   static const CtxSet   GPMIntraFlag;
+#endif
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+  static const CtxSet   GpmInterIbcFlag;
+  static const CtxSet   GpmInterIbcIdx;
 #endif
   static const CtxSet   PredMode;
   static const CtxSet   MultiRefLineIdx;
diff --git a/source/Lib/CommonLib/Contexts_ecm13.inl b/source/Lib/CommonLib/Contexts_ecm13.inl
index 92548a12a..a214a33a4 100644
--- a/source/Lib/CommonLib/Contexts_ecm13.inl
+++ b/source/Lib/CommonLib/Contexts_ecm13.inl
@@ -450,6 +450,56 @@ const CtxSet ContextSetCfg::GPMIntraFlag = ContextSetCfg::addCtxSet({
  { 133 }, 
 });
 
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+const CtxSet ContextSetCfg::GpmInterIbcFlag = ContextSetCfg::addCtxSet
+({
+  { CNU, },
+  { CNU, },
+  { CNU, },
+  { CNU, },
+  { DWS, },
+  { DWS, },
+  { DWS, },
+  { DWS, },
+  { DWE, },
+  { DWE, },
+  { DWE, },
+  { DWE, },
+  { DWO, },
+  { DWO, },
+  { DWO, },
+  { DWO, },
+  { DWO, },
+  { DWO, },
+  { DWO, },
+  { DWO, },
+  });
+
+const CtxSet ContextSetCfg::GpmInterIbcIdx = ContextSetCfg::addCtxSet
+({
+  { CNU, CNU, CNU, },
+  { CNU, CNU, CNU, },
+  { CNU, CNU, CNU, },
+  { CNU, CNU, CNU, },
+  { DWS, DWS, DWS, },
+  { DWS, DWS, DWS, },
+  { DWS, DWS, DWS, },
+  { DWS, DWS, DWS, },
+  { DWE, DWE, DWE, },
+  { DWE, DWE, DWE, },
+  { DWE, DWE, DWE, },
+  { DWE, DWE, DWE, },
+  { DWO, DWO, DWO, },
+  { DWO, DWO, DWO, },
+  { DWO, DWO, DWO, },
+  { DWO, DWO, DWO, },
+  { DWO, DWO, DWO, },
+  { DWO, DWO, DWO, },
+  { DWO, DWO, DWO, },
+  { DWO, DWO, DWO, },
+  });
+#endif
+
 const CtxSet ContextSetCfg::MmvdFlag = ContextSetCfg::addCtxSet({
 // ctx 81 84
  {  25,  34,  35,  35 },
diff --git a/source/Lib/CommonLib/Contexts_ecm14.0.inl b/source/Lib/CommonLib/Contexts_ecm14.0.inl
index c3dbadb5a..2632028e8 100644
--- a/source/Lib/CommonLib/Contexts_ecm14.0.inl
+++ b/source/Lib/CommonLib/Contexts_ecm14.0.inl
@@ -450,6 +450,56 @@ const CtxSet ContextSetCfg::GPMIntraFlag = ContextSetCfg::addCtxSet({
  { 133 },
 });
 
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+const CtxSet ContextSetCfg::GpmInterIbcFlag = ContextSetCfg::addCtxSet
+({
+  { CNU, },
+  { CNU, },
+  { CNU, },
+  { CNU, },
+  { DWS, },
+  { DWS, },
+  { DWS, },
+  { DWS, },
+  { DWE, },
+  { DWE, },
+  { DWE, },
+  { DWE, },
+  { DWO, },
+  { DWO, },
+  { DWO, },
+  { DWO, },
+  { DWO, },
+  { DWO, },
+  { DWO, },
+  { DWO, },
+  });
+
+const CtxSet ContextSetCfg::GpmInterIbcIdx = ContextSetCfg::addCtxSet
+({
+  { CNU, CNU, CNU, },
+  { CNU, CNU, CNU, },
+  { CNU, CNU, CNU, },
+  { CNU, CNU, CNU, },
+  { DWS, DWS, DWS, },
+  { DWS, DWS, DWS, },
+  { DWS, DWS, DWS, },
+  { DWS, DWS, DWS, },
+  { DWE, DWE, DWE, },
+  { DWE, DWE, DWE, },
+  { DWE, DWE, DWE, },
+  { DWE, DWE, DWE, },
+  { DWO, DWO, DWO, },
+  { DWO, DWO, DWO, },
+  { DWO, DWO, DWO, },
+  { DWO, DWO, DWO, },
+  { DWO, DWO, DWO, },
+  { DWO, DWO, DWO, },
+  { DWO, DWO, DWO, },
+  { DWO, DWO, DWO, },
+  });
+#endif
+
 const CtxSet ContextSetCfg::MmvdFlag = ContextSetCfg::addCtxSet({
 // ctx 81 84
  {  25,  34,  35,  35 },
diff --git a/source/Lib/CommonLib/InterPrediction.cpp b/source/Lib/CommonLib/InterPrediction.cpp
index f476f3732..693bcfb2d 100644
--- a/source/Lib/CommonLib/InterPrediction.cpp
+++ b/source/Lib/CommonLib/InterPrediction.cpp
@@ -9055,6 +9055,9 @@ void InterPrediction::deriveGpmSplitMode(PredictionUnit& pu, MergeCtx &geoMrgCtx
 #if JVET_Y0065_GPM_INTRA
                                        , IntraPrediction* pcIntraPred
 #endif
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+                                       , Mv* geoBvList
+#endif
 )
 {
   if ( pu.cu->cs->pcv->isEncoder || !pu.cs->slice->getSPS()->getUseAltGPMSplitModeCode())
@@ -9109,6 +9112,15 @@ void InterPrediction::deriveGpmSplitMode(PredictionUnit& pu, MergeCtx &geoMrgCtx
       fillPartGPMRefTemplate<0>(pu, geoMrgCtx, pu.geoMergeIdx0, geoMmvdIdx0);
       fillPartGPMRefTemplate<1>(pu, geoMrgCtx, pu.geoMergeIdx1, geoMmvdIdx1);
 #endif
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+      if (pu.gpmInterIbcFlag)
+      {
+        std::vector<Pel>* lut = m_pcReshape->getSliceReshaperInfo().getUseSliceReshaper() && m_pcReshape->getCTUFlag() ? &m_pcReshape->getInvLUT() : nullptr;
+        fillPartGpmInterIbcRefTemplate<0>(pu, lut, geoBvList, pu.geoMergeIdx0, geoMmvdIdx0);
+        fillPartGpmInterIbcRefTemplate<1>(pu, lut, geoBvList, pu.geoMergeIdx1, geoMmvdIdx1);
+      }
+      else
+#endif
 #if JVET_Y0065_GPM_INTRA
       if (pu.gpmIntraFlag)
       {
@@ -9134,11 +9146,21 @@ void InterPrediction::deriveGpmSplitMode(PredictionUnit& pu, MergeCtx &geoMrgCtx
   {
 
 #if JVET_AG0164_AFFINE_GPM
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+    isIintra[0] = pu.geoMergeIdx0 >= GEO_MAX_ALL_INTER_UNI_CANDS && pu.geoMergeIdx0 < GEO_MAX_ALL_INTER_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS;
+    isIintra[1] = pu.geoMergeIdx1 >= GEO_MAX_ALL_INTER_UNI_CANDS && pu.geoMergeIdx1 < GEO_MAX_ALL_INTER_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS;
+#else
     isIintra[0] = pu.geoMergeIdx0 >= GEO_MAX_ALL_INTER_UNI_CANDS;
     isIintra[1] = pu.geoMergeIdx1 >= GEO_MAX_ALL_INTER_UNI_CANDS;
+#endif
+#else
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+    isIintra[0] = pu.geoMergeIdx0 >= GEO_MAX_NUM_UNI_CANDS && pu.geoMergeIdx0 < GEO_MAX_NUM_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS;
+    isIintra[1] = pu.geoMergeIdx1 >= GEO_MAX_NUM_UNI_CANDS && pu.geoMergeIdx1 < GEO_MAX_NUM_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS;
 #else
     isIintra[0] = pu.geoMergeIdx0 >= GEO_MAX_NUM_UNI_CANDS;
     isIintra[1] = pu.geoMergeIdx1 >= GEO_MAX_NUM_UNI_CANDS;
+#endif
 #endif
     for (uint8_t partIdx = 0; partIdx < 2; ++partIdx)
     {
@@ -9538,10 +9560,17 @@ void InterPrediction::motionCompensationGeoBlend( CodingUnit& cu, MergeCtx& geoM
 
 #if JVET_AG0164_AFFINE_GPM
     AffineMergeCtx  dummyAffineMergeCtx;
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+    PU::spanGeoMMVDMotionInfo( pu, geoMrgCtx
+      , dummyAffineMergeCtx
+      , geoMrgCtx, geoMrgCtx, 0, geoMergeIdx0, geoMergeIdx1, pu.geoTmFlag0, pu.geoMMVDFlag0, pu.geoMMVDIdx0, pu.geoTmFlag1, pu.geoMMVDFlag1, pu.geoMMVDIdx1, 0, intraMPM, nullptr
+      , pu.gpmDmvrRefinePart0, pu.gpmDmvrRefinePart1, subBdofBuf[geoMergeIdx0], subBdofBuf[geoMergeIdx1] );
+#else
     PU::spanGeoMMVDMotionInfo( pu, geoMrgCtx
       , dummyAffineMergeCtx
       , geoMrgCtx, geoMrgCtx, 0, geoMergeIdx0, geoMergeIdx1, pu.geoTmFlag0, pu.geoMMVDFlag0, pu.geoMMVDIdx0, pu.geoTmFlag1, pu.geoMMVDFlag1, pu.geoMMVDIdx1, 0, intraMPM
       , pu.gpmDmvrRefinePart0, pu.gpmDmvrRefinePart1, subBdofBuf[geoMergeIdx0], subBdofBuf[geoMergeIdx1] );
+#endif
 #else
     PU::spanGeoMMVDMotionInfo( pu, geoMrgCtx, geoMrgCtx, geoMrgCtx, 0, geoMergeIdx0, geoMergeIdx1, pu.geoTmFlag0, pu.geoMMVDFlag0, pu.geoMMVDIdx0, pu.geoTmFlag1, pu.geoMMVDFlag1, pu.geoMMVDIdx1, 0, intraMPM
       , pu.gpmDmvrRefinePart0, pu.gpmDmvrRefinePart1, subBdofBuf[geoMergeIdx0], subBdofBuf[geoMergeIdx1] );
@@ -9578,6 +9607,9 @@ void InterPrediction::motionCompensationGeo( CodingUnit &cu, MergeCtx &geoMrgCtx
 #if JVET_Y0065_GPM_INTRA
                                            , IntraPrediction* pcIntraPred, std::vector<Pel>* reshapeLUT
 #endif
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+                                           , Mv* geoBvList
+#endif
 )
 #else
 #if JVET_W0097_GPM_MMVD_TM && TM_MRG
@@ -9602,6 +9634,33 @@ void InterPrediction::motionCompensationGeo( CodingUnit &cu, MergeCtx &geoMrgCtx
 #endif
 #endif
 {
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+  if (cu.firstPU->gpmInterIbcFlag)
+  {
+    MergeCtx geoBvMrgCtx;
+    PU::getIBCMergeCandidates(*cu.firstPU, geoBvMrgCtx);
+    memset(geoBvMrgCtx.ibcLicFlags, false, sizeof(bool) * geoBvMrgCtx.numValidMergeCand);
+    memset(geoBvMrgCtx.ibcFilterFlags, false, sizeof(bool) * geoBvMrgCtx.numValidMergeCand);
+    memset(geoBvMrgCtx.rribcFlipTypes, 0, sizeof(int) * geoBvMrgCtx.numValidMergeCand);
+#if JVET_AE0174_NONINTER_TM_TOOLS_CONTROL
+    if (cu.cs->sps->getUseAML() && cu.cs->sps->getTMnoninterToolsEnableFlag())
+#else
+    if (cu.cs->sps->getUseAML())
+#endif
+    {
+      PredictionUnit puSaved = *cu.firstPU;
+      puSaved.cu->predMode = MODE_IBC;
+      adjustIBCMergeCandidates(puSaved, geoBvMrgCtx, 0, geoBvMrgCtx.numValidMergeCand);
+      puSaved.cu->predMode = MODE_INTER;
+    }
+    for (uint32_t ibcIdx = 0; ibcIdx < GEO_MAX_NUM_IBC_CANDS; ibcIdx++)
+    {
+      geoBvList[ibcIdx] = geoBvMrgCtx.mvFieldNeighbours[ibcIdx << 1].mv;
+    }
+    setFillCurTplAboveARMC(false);
+    setFillCurTplLeftARMC(false);
+  }
+#endif
 #if JVET_Z0056_GPM_SPLIT_MODE_REORDERING
   deriveGpmSplitMode(*cu.firstPU, geoMrgCtx
 #if JVET_W0097_GPM_MMVD_TM && TM_MRG
@@ -9612,6 +9671,9 @@ void InterPrediction::motionCompensationGeo( CodingUnit &cu, MergeCtx &geoMrgCtx
 #endif
 #if JVET_Y0065_GPM_INTRA
                    , pcIntraPred
+#endif
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+                   , geoBvList
 #endif
   );
 #if JVET_W0097_GPM_MMVD_TM && TM_MRG
@@ -9659,6 +9721,48 @@ void InterPrediction::motionCompensationGeo( CodingUnit &cu, MergeCtx &geoMrgCtx
 #else
     bool isIntra0 = candIdx0 >= GEO_MAX_NUM_UNI_CANDS;
     bool isIntra1 = candIdx1 >= GEO_MAX_NUM_UNI_CANDS;
+#endif
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+#if JVET_AG0164_AFFINE_GPM
+    bool isIbc0 = candIdx0 >= GEO_MAX_ALL_INTER_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS;
+    bool isIbc1 = candIdx1 >= GEO_MAX_ALL_INTER_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS;
+#else
+    bool isIbc0 = candIdx0 >= GEO_MAX_NUM_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS;
+    bool isIbc1 = candIdx1 >= GEO_MAX_NUM_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS;
+#endif
+    if (isIbc0)
+    {
+      Mv mv = pu.mv[0];
+      int dir = pu.interDir;
+#if JVET_AG0164_AFFINE_GPM
+      int ibcIdx = candIdx0 - GEO_MAX_ALL_INTER_UNI_CANDS - GEO_MAX_NUM_INTRA_CANDS;
+#else
+      int ibcIdx = candIdx0 - GEO_MAX_NUM_UNI_CANDS - GEO_MAX_NUM_INTRA_CANDS;
+#endif
+      const bool luma = cu.Y().valid();
+      const bool chroma = isChromaEnabled(cu.chromaFormat) && cu.Cb().valid();
+      pu.cu->predMode = MODE_IBC;
+      pu.mv[0] = geoBvList[ibcIdx];
+      pu.interDir = 1;
+      pu.bv = pu.mv[REF_PIC_LIST_0];
+      pu.bv.changePrecision(MV_PRECISION_INTERNAL, MV_PRECISION_INT); // used for only integer resolution
+      if (luma && (chroma || !isChromaEnabled(cu.chromaFormat)))
+      {
+        motionCompensation(*cu.firstPU, tmpGeoBuf0, REF_PIC_LIST_0, true, true);
+      }
+      else
+      {
+#if JVET_AE0169_BIPREDICTIVE_IBC
+        motionCompensation(*cu.firstPU, tmpGeoBuf0, REF_PIC_LIST_0, luma, chroma);
+#else
+        m_pcInterPred->motionCompensation(*cu.firstPU, tmpGeoBuf0, REF_PIC_LIST_0, luma, chroma);
+#endif
+      }
+      pu.cu->predMode = MODE_INTER;
+      pu.mv[0] = mv;
+      pu.interDir = dir;
+    }
+    else
 #endif
     if (isIntra0)
     {
@@ -9796,6 +9900,41 @@ void InterPrediction::motionCompensationGeo( CodingUnit &cu, MergeCtx &geoMrgCtx
     }
     }
 
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+    if (isIbc1)
+    {
+      Mv mv = pu.mv[0];
+      int dir = pu.interDir;
+#if JVET_AG0164_AFFINE_GPM
+      int ibcIdx = candIdx1 - GEO_MAX_ALL_INTER_UNI_CANDS - GEO_MAX_NUM_INTRA_CANDS;
+#else
+      int ibcIdx = candIdx1 - GEO_MAX_NUM_UNI_CANDS - GEO_MAX_NUM_INTRA_CANDS;
+#endif
+      const bool luma = cu.Y().valid();
+      const bool chroma = isChromaEnabled(cu.chromaFormat) && cu.Cb().valid();
+      pu.cu->predMode = MODE_IBC;
+      pu.mv[0] = geoBvList[ibcIdx];
+      pu.interDir = 1;
+      pu.bv = pu.mv[REF_PIC_LIST_0];
+      pu.bv.changePrecision(MV_PRECISION_INTERNAL, MV_PRECISION_INT); // used for only integer resolution
+      if (luma && (chroma || !isChromaEnabled(cu.chromaFormat)))
+      {
+        motionCompensation(*cu.firstPU, tmpGeoBuf1, REF_PIC_LIST_0, true, true);
+      }
+      else
+      {
+#if JVET_AE0169_BIPREDICTIVE_IBC
+        motionCompensation(*cu.firstPU, tmpGeoBuf1, REF_PIC_LIST_0, luma, chroma);
+#else
+        m_pcInterPred->motionCompensation(*cu.firstPU, tmpGeoBuf1, REF_PIC_LIST_0, luma, chroma);
+#endif
+      }
+      pu.cu->predMode = MODE_INTER;
+      pu.mv[0] = mv;
+      pu.interDir = dir;
+    }
+    else
+#endif
     if (isIntra1)
     {
       PU::getGeoIntraMPMs(pu, pcIntraPred->m_intraMPM+GEO_MAX_NUM_INTRA_CANDS, splitDir, g_geoTmShape[1][g_geoParams[pu.geoSplitDir][0]]);
@@ -11537,7 +11676,11 @@ bool InterPrediction::xAMLIBCGetCurBlkTemplate(PredictionUnit& pu, int nCurBlkWi
 
 #if JVET_AC0112_IBC_LIC
 #if JVET_AE0169_IBC_MBVD_LIST_DERIVATION
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+void InterPrediction::getIBCAMLRefTemplate(PredictionUnit &pu, int nCurBlkWidth, int nCurBlkHeight, bool doIbcLic, bool checkTmlBvValidaion, Pel* pcBufPredRefTop, Pel* pcBufPredRefLeft)
+#else
 void InterPrediction::getIBCAMLRefTemplate(PredictionUnit &pu, int nCurBlkWidth, int nCurBlkHeight, bool doIbcLic, bool checkTmlBvValidaion)
+#endif
 #else
 void InterPrediction::getIBCAMLRefTemplate(PredictionUnit &pu, int nCurBlkWidth, int nCurBlkHeight, bool doIbcLic)
 #endif
@@ -11778,7 +11921,11 @@ void InterPrediction::getIBCAMLRefTemplate(PredictionUnit &pu, int nCurBlkWidth,
     bool isFracMv = pu.cs->sps->getIBCFracFlag() && mvTop.isFracMv();
     if (isFracMv)
     {
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+      PelUnitBuf pcYBuf(pu.chromaFormat, PelBuf(pcBufPredRefTop == nullptr ? m_acYuvRefAMLTemplate[0][0] : pcBufPredRefTop, nCurBlkWidth, AML_MERGE_TEMPLATE_SIZE));
+#else
       PelUnitBuf pcYBuf(pu.chromaFormat, PelBuf(m_acYuvRefAMLTemplate[0][0], nCurBlkWidth, AML_MERGE_TEMPLATE_SIZE));
+#endif
       getPredIBCBlk(pu, COMPONENT_Y, &currPic, mvTop, pcYBuf, filterIdx == 1, true);
 #if JVET_AA0070_RRIBC
       pcYBuf.Y().flip(pu.cu->rribcFlipType);
@@ -11796,7 +11943,11 @@ void InterPrediction::getIBCAMLRefTemplate(PredictionUnit &pu, int nCurBlkWidth,
 #else
     const Pel*    rec = recBuf.bufAt(pu.blocks[COMPONENT_Y].pos().offset(mvTop.hor, mvTop.ver));
 #endif
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+    PelBuf pcYBuf = PelBuf(pcBufPredRefTop == nullptr ? m_acYuvRefAMLTemplate[0][0] : pcBufPredRefTop, nCurBlkWidth, AML_MERGE_TEMPLATE_SIZE);
+#else
     PelBuf pcYBuf = PelBuf(m_acYuvRefAMLTemplate[0][0], nCurBlkWidth, AML_MERGE_TEMPLATE_SIZE);
+#endif
     Pel*   pcY = pcYBuf.bufAt(0, 0);
     if (numTemplate[0] + numTemplate[1] > 0)
     {
@@ -11862,7 +12013,11 @@ void InterPrediction::getIBCAMLRefTemplate(PredictionUnit &pu, int nCurBlkWidth,
     bool isFracMv = pu.cs->sps->getIBCFracFlag() && mvLeft.isFracMv();
     if (isFracMv)
     {
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+      PelUnitBuf pcYBuf(pu.chromaFormat, PelBuf(pcBufPredRefLeft == nullptr ? m_acYuvRefAMLTemplate[1][0] : pcBufPredRefLeft, AML_MERGE_TEMPLATE_SIZE, nCurBlkHeight));
+#else
       PelUnitBuf pcYBuf(pu.chromaFormat, PelBuf(m_acYuvRefAMLTemplate[1][0], AML_MERGE_TEMPLATE_SIZE, nCurBlkHeight));
+#endif
       getPredIBCBlk(pu, COMPONENT_Y, &currPic, mvLeft, pcYBuf, filterIdx == 1, true);
 #if JVET_AA0070_RRIBC
       pcYBuf.Y().flip(pu.cu->rribcFlipType);
@@ -11876,7 +12031,11 @@ void InterPrediction::getIBCAMLRefTemplate(PredictionUnit &pu, int nCurBlkWidth,
     else
     {
 #endif
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+    PelBuf pcYBuf = PelBuf(pcBufPredRefLeft == nullptr ? m_acYuvRefAMLTemplate[1][0] : pcBufPredRefLeft, AML_MERGE_TEMPLATE_SIZE, nCurBlkHeight);
+#else
     PelBuf pcYBuf = PelBuf(m_acYuvRefAMLTemplate[1][0], AML_MERGE_TEMPLATE_SIZE, nCurBlkHeight);
+#endif
     Pel*   pcY = pcYBuf.bufAt(0, 0);
 #if JVET_AD0208_IBC_ADAPT_FOR_CAM_CAPTURED_CONTENTS
     const Pel*    rec = recBuf.bufAt(pu.blocks[COMPONENT_Y].pos().offset( mvLeft.hor >> MV_FRACTIONAL_BITS_INTERNAL, mvLeft.ver >> MV_FRACTIONAL_BITS_INTERNAL));
diff --git a/source/Lib/CommonLib/InterPrediction.h b/source/Lib/CommonLib/InterPrediction.h
index 7229835ea..e3cea919d 100644
--- a/source/Lib/CommonLib/InterPrediction.h
+++ b/source/Lib/CommonLib/InterPrediction.h
@@ -794,6 +794,9 @@ public:
 #endif
 #if JVET_Y0065_GPM_INTRA
                            , IntraPrediction* pcIntraPred
+#endif
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+                           , Mv* geoBvList
 #endif
   );
 #endif
@@ -824,6 +827,9 @@ public:
 #endif
 #if JVET_Y0065_GPM_INTRA
                               , IntraPrediction* pcIntraPred, std::vector<Pel>* reshapeLUT
+#endif
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+                              , Mv* geoBvList
 #endif
   );
 #else
@@ -901,7 +907,11 @@ public:
   bool xAMLIBCGetCurBlkTemplate(PredictionUnit& pu, int nCurBlkWidth, int nCurBlkHeight);
 #if JVET_AC0112_IBC_LIC
 #if JVET_AE0169_IBC_MBVD_LIST_DERIVATION
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+  void getIBCAMLRefTemplate(PredictionUnit &pu, int nCurBlkWidth, int nCurBlkHeight, bool doIbcLic = false, bool checkTmlBvValidaion = true, Pel* pcBufPredRefTop = nullptr, Pel* pcBufPredRefLeft = nullptr);
+#else
   void getIBCAMLRefTemplate(PredictionUnit &pu, int nCurBlkWidth, int nCurBlkHeight, bool doIbcLic = false, bool checkTmlBvValidaion = true);
+#endif
 #else
   void getIBCAMLRefTemplate(PredictionUnit &pu, int nCurBlkWidth, int nCurBlkHeight, bool doIbcLic = false);
 #endif
@@ -1294,6 +1304,63 @@ public:
     pu.cu->affine = false;
 #endif
   }
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+  template <uint8_t partIdx, bool useDefaultPelBuffer = true>
+  void    fillPartGpmInterIbcRefTemplate(PredictionUnit &pu, std::vector<Pel>* lut, Pel* bufTop = nullptr, Pel* bufLeft = nullptr)
+  {
+    if (useDefaultPelBuffer)
+    {
+      bufTop  = partIdx == 0 ? m_acYuvRefAMLTemplatePart0[0] : m_acYuvRefAMLTemplatePart1[0];
+      bufLeft = partIdx == 0 ? m_acYuvRefAMLTemplatePart0[1] : m_acYuvRefAMLTemplatePart1[1];
+    }
+    getIBCAMLRefTemplate(pu, pu.lumaSize().width, pu.lumaSize().height, false, true, bufTop, bufLeft);
+    if (lut != nullptr)
+    {
+      if (m_bAMLTemplateAvailabe[0])
+      {
+        for (int w = 0; w < pu.lwidth(); ++w)
+        {
+          bufTop[w] = (*lut)[bufTop[w]];
+        }
+      }
+
+      if (m_bAMLTemplateAvailabe[1])
+      {
+        for (int h = 0; h < pu.lheight(); ++h)
+        {
+          bufLeft[h] = (*lut)[bufLeft[h]];
+        }
+      }
+    }
+  }
+
+  template <uint8_t partIdx, bool useDefaultPelBuffer = true>
+  void    fillPartGpmInterIbcRefTemplate(PredictionUnit &pu, std::vector<Pel>* lut, Mv* geoBvList, int candIdx, int geoMmvdIdx = -1, Pel* bufTop = nullptr, Pel* bufLeft = nullptr)
+  {
+#if JVET_Y0065_GPM_INTRA
+#if JVET_AG0164_AFFINE_GPM
+    if (candIdx < GEO_MAX_ALL_INTER_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS)
+#else
+    if (candIdx < GEO_MAX_NUM_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS)
+#endif
+#else
+    if (candIdx < GEO_MAX_NUM_UNI_CANDS)
+#endif
+    {
+      return;
+    }
+    pu.cu->predMode = MODE_IBC;
+#if JVET_AG0164_AFFINE_GPM
+    pu.mv[REF_PIC_LIST_0] = geoBvList[candIdx - (GEO_MAX_ALL_INTER_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS)];
+#else
+    pu.mv[REF_PIC_LIST_0] = geoBvList[candIdx - (GEO_MAX_NUM_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS)];
+#endif
+    pu.bv = pu.mv[REF_PIC_LIST_0];
+    pu.bv.changePrecision(MV_PRECISION_INTERNAL, MV_PRECISION_INT);
+    fillPartGpmInterIbcRefTemplate<partIdx, useDefaultPelBuffer>(pu, lut, bufTop, bufLeft);
+    pu.cu->predMode = MODE_INTER;
+  }
+#endif
 #endif
 #if INTER_LIC || JVET_AC0112_IBC_LIC
 #if JVET_AE0078_IBC_LIC_EXTENSION
diff --git a/source/Lib/CommonLib/IntraPrediction.cpp b/source/Lib/CommonLib/IntraPrediction.cpp
index 5edefec98..dcbb11a38 100644
--- a/source/Lib/CommonLib/IntraPrediction.cpp
+++ b/source/Lib/CommonLib/IntraPrediction.cpp
@@ -26897,7 +26897,11 @@ void IntraPrediction::getNeiEipCands(const PredictionUnit& pu, static_vector<Eip
           for (int i = 0; i < 2 && foundNeighMV == false; i++)
           {
             int refIdx = miNeigh.refIdx[i];
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+            if (miNeigh.isInter && refIdx != -1)
+#else
             if (refIdx != -1)
+#endif
             {
               const int currRefPOC = slice.getRefPic(RefPicList(i), refIdx)->getPOC();
               if (currRefPOC == colPOC)
diff --git a/source/Lib/CommonLib/Slice.h b/source/Lib/CommonLib/Slice.h
index 38adb6e21..65adf26ba 100644
--- a/source/Lib/CommonLib/Slice.h
+++ b/source/Lib/CommonLib/Slice.h
@@ -1855,6 +1855,9 @@ private:
   bool              m_ciipAffine;
 #endif
   bool              m_Geo;
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+  bool              m_geoInterIbc;
+#endif
 #if INTER_LIC
   bool              m_licEnabledFlag;
 #if JVET_AG0276_LIC_SLOPE_ADJUST
@@ -2595,6 +2598,10 @@ void                    setCCALFEnabledFlag( bool b )
 #endif
   void      setUseGeo             ( bool b )                                        { m_Geo = b; }
   bool      getUseGeo             ()                                      const     { return m_Geo; }
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+  void      setUseGeoInterIbc     ( bool b )                                        { m_geoInterIbc = b; }
+  bool      getUseGeoInterIbc     ()                                      const     { return m_geoInterIbc; }
+#endif
 #if INTER_LIC
   void      setLicEnabledFlag     ( bool b )                                        { m_licEnabledFlag = b; }
   bool      getLicEnabledFlag     ()                                     const      { return m_licEnabledFlag; }
diff --git a/source/Lib/CommonLib/TypeDef.h b/source/Lib/CommonLib/TypeDef.h
index 5b27cc0ae..d09dc1d1e 100644
--- a/source/Lib/CommonLib/TypeDef.h
+++ b/source/Lib/CommonLib/TypeDef.h
@@ -249,6 +249,7 @@
 #define JVET_AE0174_NONINTER_TM_TOOLS_CONTROL             1 // JVET-AE0174: Add non-inter TM sps flag to control whether template matching is used for non-inter (Intra and IBC) tools
 #define JVET_AE0094_IBC_NONADJACENT_SPATIAL_CANDIDATES    1 // JVET-AE0094: IBC with non-adjacent spatial candidates
 #define JVET_AG0091_ARBVP                                 1 // JVET-AG0091: Auto-relocated block vector prediction
+#define JVET_AI0082_TEMPORAL_BV                           1 // JVET-AI0081: Temporal BV for IBC merge list construction
 
 #if JVET_AC0071_DBV && JVET_V0130_INTRA_TMP
 #define JVET_AF0066_ENABLE_DBV_4_SINGLE_TREE              1 // JVET-AF0066: Enable DBV mode in single tree configuration
@@ -305,6 +306,7 @@
 #define JVET_Z0139_HIST_AFF                               1 // JVET-Z0139: Affine HMVP 
 #define JVET_Z0139_NA_AFF                                 1 // JVET-Z0139: Constructed non-adjacent spatial neighbors for affine mode
 #define JVET_AA0058_GPM_ADAPTIVE_BLENDING                 1 // JVET-AA0058: GPM adaptive blending
+#define JVET_AI0082_GPM_WITH_INTER_IBC                    1 // JVET-AI0082: GPM with inter prediction and IBC
 #define JVET_AA0146_WRAP_AROUND_FIX                       1 // JVET-AA0146: bugfix&cleanup for wrap around motion compensation
 #define JVET_AA0107_RMVF_AFFINE_MERGE_DERIVATION          1 // JVET-AA0107 Regression based affine merge candidate derivation
 #if JVET_AA0107_RMVF_AFFINE_MERGE_DERIVATION
diff --git a/source/Lib/CommonLib/Unit.cpp b/source/Lib/CommonLib/Unit.cpp
index 75cb57008..7ceacd620 100644
--- a/source/Lib/CommonLib/Unit.cpp
+++ b/source/Lib/CommonLib/Unit.cpp
@@ -1090,6 +1090,9 @@ void PredictionUnit::initData()
 #if JVET_Y0065_GPM_INTRA
   gpmIntraFlag = false;
 #endif
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+  gpmInterIbcFlag = false;
+#endif
 #if JVET_W0097_GPM_MMVD_TM
   geoMMVDFlag0 = false;
   geoMMVDIdx0 = MAX_UCHAR;
@@ -1313,6 +1316,9 @@ PredictionUnit& PredictionUnit::operator=(const InterPredictionData& predData)
 #if JVET_Y0065_GPM_INTRA
   gpmIntraFlag = predData.gpmIntraFlag;
 #endif
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+  gpmInterIbcFlag = predData.gpmInterIbcFlag;
+#endif
 #if JVET_W0097_GPM_MMVD_TM
   geoMMVDFlag0 = predData.geoMMVDFlag0;
   geoMMVDIdx0 = predData.geoMMVDIdx0;
@@ -1544,6 +1550,9 @@ PredictionUnit& PredictionUnit::operator=( const PredictionUnit& other )
 #if JVET_Y0065_GPM_INTRA
   gpmIntraFlag = other.gpmIntraFlag;
 #endif
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+  gpmInterIbcFlag = other.gpmInterIbcFlag;
+#endif
 #if JVET_W0097_GPM_MMVD_TM
   geoMMVDFlag0 = other.geoMMVDFlag0;
   geoMMVDIdx0 = other.geoMMVDIdx0;
diff --git a/source/Lib/CommonLib/Unit.h b/source/Lib/CommonLib/Unit.h
index 4400c04be..9cc8b9b20 100644
--- a/source/Lib/CommonLib/Unit.h
+++ b/source/Lib/CommonLib/Unit.h
@@ -643,6 +643,9 @@ struct InterPredictionData
 #if JVET_Y0065_GPM_INTRA
   bool        gpmIntraFlag;
 #endif
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+  bool        gpmInterIbcFlag;
+#endif
 #if JVET_W0097_GPM_MMVD_TM
   bool        geoMMVDFlag0;
   uint8_t     geoMMVDIdx0;
diff --git a/source/Lib/CommonLib/UnitTools.cpp b/source/Lib/CommonLib/UnitTools.cpp
index ccff4189e..e6b8cdcc2 100644
--- a/source/Lib/CommonLib/UnitTools.cpp
+++ b/source/Lib/CommonLib/UnitTools.cpp
@@ -1324,7 +1324,11 @@ void getNeighBv(const PredictionUnit& puOrg, const PredictionUnit* pu, std::vect
 #endif
 )
 {
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+  if (!pu || ((pu->cu->predMode != MODE_IBC) && (!pu->cu->tmpFlag) && (!pu->cu->geoFlag)))
+#else
   if (!pu || ((pu->cu->predMode != MODE_IBC) && (!pu->cu->tmpFlag)))
+#endif
   {
     return;
   }
@@ -1452,6 +1456,46 @@ void getNeighBv(const PredictionUnit& puOrg, const PredictionUnit* pu, std::vect
     }
     return;
   }
+
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+  if (pu && CU::isInter(*pu->cu) && pu->cu->geoFlag && pu->gpmInterIbcFlag)
+  {
+    bool isValid = pu->getMotionInfo(pu->lumaPos().offset(pu->lwidth() >> 1, pu->lheight() >> 1)).isIBCmot;
+    if (isValid)
+    {
+      MotionInfo mi = pu->getMotionInfo(pu->lumaPos().offset(pu->lwidth() >> 1, pu->lheight() >> 1));
+      Mv bv = mi.mv[0];
+      bv.changePrecision(MV_PRECISION_INTERNAL, MV_PRECISION_INT);
+      if (PU::validItmpBv(puOrg, bv.hor, bv.ver))
+      {
+#if JVET_AH0055_INTRA_TMP_ARBVP
+        if (!PU::CheckBvAvailable(pBv, bv))
+#else
+        if (!CheckBvAvailable(pBv, bv))
+#endif
+        {
+          pBv.push_back(bv);
+        }
+      }
+
+#if JVET_AH0200_INTRA_TMP_BV_REORDER
+      bv.changePrecision(MV_PRECISION_INT, MV_PRECISION_INTERNAL);
+      if (PU::validIBCItmpMv(puOrg, bv, SGPM_TEMPLATE_SIZE))
+      {
+#if JVET_AH0055_INTRA_TMP_ARBVP
+        if (!PU::CheckBvAvailable(pSgpmMvs, bv))
+#else
+        if (!CheckBvAvailable(pSgpmMvs, bv))
+#endif
+        {
+          pSgpmMvs.push_back(bv);
+        }
+      }
+#endif
+    }
+    return;
+  }
+#endif
 }
 
 int PU::getItmpMergeCandidate(const PredictionUnit& pu, std::vector<Mv>& pBvs
@@ -6497,7 +6541,16 @@ void PU::getIBCMergeCandidates(const PredictionUnit &pu, MergeCtx& mrgCtx, const
 #else
   const bool isAvailableA1 = puLeft && pu.cu != puLeft->cu && CU::isIBC(*puLeft->cu);
 #endif
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+  bool isAvailableGpmIbcA1 = puLeft && pu.cu != puLeft->cu && CU::isInter(*puLeft->cu) && puLeft->cu->geoFlag;
+  if (isAvailableGpmIbcA1)
+  {
+    isAvailableGpmIbcA1 &= puLeft->getMotionInfo(posLB.offset(-1, 0)).isIBCmot;
+  }
+  if (isGt4x4 && (isAvailableA1 || isAvailableGpmIbcA1))
+#else
   if (isGt4x4 && isAvailableA1)
+#endif
   {
     miLeft = puLeft->getMotionInfo(posLB.offset(-1, 0));
 #if JVET_AD0208_IBC_ADAPT_FOR_CAM_CAPTURED_CONTENTS
@@ -6650,7 +6703,16 @@ void PU::getIBCMergeCandidates(const PredictionUnit &pu, MergeCtx& mrgCtx, const
 #else
   bool isAvailableB1 = puAbove && pu.cu != puAbove->cu && CU::isIBC(*puAbove->cu);
 #endif
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+  bool isAvailableGpmIbcB1 = puAbove && pu.cu != puAbove->cu && CU::isInter(*puAbove->cu) && puAbove->cu->geoFlag;
+  if (isAvailableGpmIbcB1)
+  {
+    isAvailableGpmIbcB1 &= puAbove->getMotionInfo(posRT.offset(0, -1)).isIBCmot;
+  }
+  if (isGt4x4 && (isAvailableB1 || isAvailableGpmIbcB1))
+#else
   if (isGt4x4 && isAvailableB1)
+#endif
   {
     miAbove = puAbove->getMotionInfo(posRT.offset(0, -1));
 #if JVET_AD0208_IBC_ADAPT_FOR_CAM_CAPTURED_CONTENTS
@@ -6819,7 +6881,16 @@ void PU::getIBCMergeCandidates(const PredictionUnit &pu, MergeCtx& mrgCtx, const
 #else
   bool isAvailableB0 = puAboveRight && pu.cu != puAboveRight->cu && CU::isIBC(*puAboveRight->cu);
 #endif
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+  bool isAvailableGpmIbcB0 = puAboveRight && pu.cu != puAboveRight->cu && CU::isInter(*puAboveRight->cu) && puAboveRight->cu->geoFlag;
+  if (isAvailableGpmIbcB0)
+  {
+    isAvailableGpmIbcB0 &= puAboveRight->getMotionInfo(posRT.offset(1, -1)).isIBCmot;
+  }
+  if (isGt4x4 && (isAvailableB0 || isAvailableGpmIbcB0))
+#else
   if (isGt4x4 && isAvailableB0)
+#endif
   {
     miAboveRight = puAboveRight->getMotionInfo(posRT.offset(1, -1));
 #if JVET_AD0208_IBC_ADAPT_FOR_CAM_CAPTURED_CONTENTS
@@ -6979,7 +7050,16 @@ void PU::getIBCMergeCandidates(const PredictionUnit &pu, MergeCtx& mrgCtx, const
 #else
   bool isAvailableA0 = puLeftBottom && pu.cu != puLeftBottom->cu && CU::isIBC(*puLeftBottom->cu);
 #endif
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+  bool isAvailableGpmIbcA0 = puLeftBottom && pu.cu != puLeftBottom->cu && CU::isInter(*puLeftBottom->cu) && puLeftBottom->cu->geoFlag;
+  if (isAvailableGpmIbcA0)
+  {
+    isAvailableGpmIbcA0 &= puLeftBottom->getMotionInfo(posLB.offset(-1, 1)).isIBCmot;
+  }
+  if (isGt4x4 && (isAvailableA0 || isAvailableGpmIbcA0))
+#else
   if (isGt4x4 && isAvailableA0)
+#endif
   {
     miBelowLeft = puLeftBottom->getMotionInfo(posLB.offset(-1, 1));
 #if JVET_AD0208_IBC_ADAPT_FOR_CAM_CAPTURED_CONTENTS
@@ -7149,7 +7229,16 @@ void PU::getIBCMergeCandidates(const PredictionUnit &pu, MergeCtx& mrgCtx, const
 #else
     bool isAvailableB2 = puAboveLeft && pu.cu != puAboveLeft->cu && CU::isIBC(*puAboveLeft->cu);
 #endif
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+    bool isAvailableGpmIbcB2 = puAboveLeft && pu.cu != puAboveLeft->cu && CU::isInter(*puAboveLeft->cu) && puAboveLeft->cu->geoFlag;
+    if (isAvailableGpmIbcB2)
+    {
+      isAvailableGpmIbcB2 &= puAboveLeft->getMotionInfo(posLT.offset(-1, -1)).isIBCmot;
+    }
+    if (isGt4x4 && (isAvailableB2 || isAvailableGpmIbcB2))
+#else
     if (isGt4x4 && isAvailableB2)
+#endif
     {
       miAboveLeft = puAboveLeft->getMotionInfo(posLT.offset(-1, -1));
 #if JVET_AD0208_IBC_ADAPT_FOR_CAM_CAPTURED_CONTENTS
@@ -7381,7 +7470,16 @@ void PU::getIBCMergeCandidates(const PredictionUnit &pu, MergeCtx& mrgCtx, const
         const PredictionUnit *puNonAdjacent = cs.getPURestricted(posLT.offset(offsetX, offsetY), pu, pu.chType);
         bool isAvailableNonAdjacent = puNonAdjacent && pu.cu != puNonAdjacent->cu && (CU::isIBC(*puNonAdjacent->cu) || puNonAdjacent->cu->tmpFlag);
 
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+        bool isAvailableNonAdjacentGpmIbc = puNonAdjacent && pu.cu != puNonAdjacent->cu && CU::isInter(*puNonAdjacent->cu) && puNonAdjacent->cu->geoFlag;
+        if (isAvailableNonAdjacentGpmIbc)
+        {
+          isAvailableNonAdjacentGpmIbc &= puNonAdjacent->getMotionInfo(posLT.offset(offsetX, offsetY)).isIBCmot;
+        }
+        if (isGt4x4 && (isAvailableNonAdjacent || isAvailableNonAdjacentGpmIbc))
+#else
         if (isGt4x4 && isAvailableNonAdjacent)
+#endif
         {
           miNonAdjacent = puNonAdjacent->getMotionInfo(posLT.offset(offsetX, offsetY));
 #if JVET_AD0208_IBC_ADAPT_FOR_CAM_CAPTURED_CONTENTS
@@ -7591,7 +7689,16 @@ void PU::getIBCMergeCandidates(const PredictionUnit &pu, MergeCtx& mrgCtx, const
         const PredictionUnit *puNonAdjacent = cs.getPURestricted(posLT.offset(offsetX, offsetY), pu, pu.chType);
         bool isAvailableNonAdjacent = puNonAdjacent && pu.cu != puNonAdjacent->cu && (CU::isIBC(*puNonAdjacent->cu) || puNonAdjacent->cu->tmpFlag);
 
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+        bool isAvailableNonAdjacentGpmIbc = puNonAdjacent && pu.cu != puNonAdjacent->cu && CU::isInter(*puNonAdjacent->cu) && puNonAdjacent->cu->geoFlag;
+        if (isAvailableNonAdjacentGpmIbc)
+        {
+          isAvailableNonAdjacentGpmIbc &= puNonAdjacent->getMotionInfo(posLT.offset(offsetX, offsetY)).isIBCmot;
+        }
+        if (isGt4x4 && (isAvailableNonAdjacent || isAvailableNonAdjacentGpmIbc))
+#else
         if (isGt4x4 && isAvailableNonAdjacent)
+#endif
         {
           miNonAdjacent = puNonAdjacent->getMotionInfo(posLT.offset(offsetX, offsetY));
 #if JVET_AD0208_IBC_ADAPT_FOR_CAM_CAPTURED_CONTENTS
@@ -7751,6 +7858,57 @@ void PU::getIBCMergeCandidates(const PredictionUnit &pu, MergeCtx& mrgCtx, const
       }
     }
   }
+#endif
+#if JVET_AI0082_TEMPORAL_BV
+  std::vector<MotionInfo> temporalMi;
+  getTemporalBv(pu, temporalMi);
+  int numValidTemporalBv = 0;
+  for(MotionInfo mi: temporalMi)
+  {
+    if(checkIsIBCCandidateValid(pu, mi) && cnt < maxNumMergeCand && numValidTemporalBv < 8)
+    {
+      mrgCtx.initMrgCand(cnt);
+      mrgCtx.interDirNeighbours[cnt] = 1;
+      mrgCtx.mvFieldNeighbours[cnt << 1].setMvField(mi.mv[0], MAX_NUM_REF);
+      mrgCtx.useAltHpelIf[cnt] = pu.mergeFlag ? mi.useAltHpelIf : (pu.cu->imv == IMV_HPEL);
+      mrgCtx.rribcFlipTypes[cnt] = (mi.isIBCmot && !pu.tmMergeFlag && !pu.ibcMbvdMergeFlag) ? mi.rribcFlipType : 0;
+      if( !mrgCtx.xCheckSimilarIBCMotion(cnt, mvdSimilarityThresh))
+      {
+        cnt++;
+        numValidTemporalBv++;
+        // early termination
+        if (cnt == maxNumMergeCand)
+        {
+          mrgCtx.numAMVPMergeCand = cnt;
+          return;
+        }
+      }
+    }
+    if(mi.interDir == 3)
+    {
+      MotionInfo mi1 = mi;
+      mi1.mv[0] = mi1.mv[1];
+      if(checkIsIBCCandidateValid(pu, mi1) && cnt < maxNumMergeCand && numValidTemporalBv < 8)
+      {
+        mrgCtx.initMrgCand(cnt);
+        mrgCtx.interDirNeighbours[cnt] = 1;
+        mrgCtx.mvFieldNeighbours[cnt << 1].setMvField(mi1.mv[0], MAX_NUM_REF);
+        mrgCtx.useAltHpelIf[cnt] = pu.mergeFlag ? mi1.useAltHpelIf : (pu.cu->imv == IMV_HPEL);
+        mrgCtx.rribcFlipTypes[cnt] = (mi1.isIBCmot && !pu.tmMergeFlag && !pu.ibcMbvdMergeFlag) ? mi1.rribcFlipType : 0;
+        if( !mrgCtx.xCheckSimilarIBCMotion(cnt, mvdSimilarityThresh))
+        {
+          cnt++;
+          numValidTemporalBv++;
+          // early termination
+          if (cnt == maxNumMergeCand)
+          {
+            mrgCtx.numAMVPMergeCand = cnt;
+            return;
+          }
+        }
+      }
+    }
+  }
 #endif
   if (cnt != maxNumMergeCand)
   {
@@ -7830,7 +7988,16 @@ void PU::getIBCMergeCandidates(const PredictionUnit &pu, MergeCtx& mrgCtx, const
         const PredictionUnit* puCascaded = cs.getPURestricted(posCand[n].offset(offsetX, offsetY), pu, pu.chType);
         bool                  isAvailableCascaded =
           puCascaded && pu.cu != puCascaded->cu && (CU::isIBC(*puCascaded->cu) || puCascaded->cu->tmpFlag);
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+        bool isAvailableCascadedGpm = puCascaded && pu.cu != puCascaded->cu && CU::isInter(*puCascaded->cu) && puCascaded->cu->geoFlag;
+        if (isAvailableCascadedGpm)
+        {
+          isAvailableCascadedGpm &= puCascaded->getMotionInfo(posCand[n].offset(offsetX, offsetY)).isIBCmot;
+        }
+        if (isGt4x4 && (isAvailableCascaded || isAvailableCascadedGpm))
+#else
         if (isGt4x4 && isAvailableCascaded)
+#endif
         {
           miCascaded = puCascaded->getMotionInfo(posCand[n].offset(offsetX, offsetY));
 #if JVET_AA0070_RRIBC
@@ -25689,6 +25856,13 @@ void PU::spanSCCInfo(PredictionUnit &pu)
       motionInfo[x].isRefRefSCC = false;
       motionInfo[x].isSCC       = CU::isIBC(*pu.cu) || CU::isPLT(*pu.cu) || (pu.cu->bdpcmMode > 0);
 
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+      if (CU::isInter(*pu.cu) && pu.cu->geoFlag &&  motionInfo[x].isIBCmot)
+      {
+        motionInfo[x].isSCC = true;
+        continue;
+      }
+#endif
       if (CU::isInter(*pu.cu) && motionInfo[x].isInter && (motionInfo[x].interDir == 1 || motionInfo[x].interDir == 2))
       {
         cMv     = motionInfo[x].mv[motionInfo[x].interDir - 1];
@@ -26634,9 +26808,17 @@ void PU::spanGeoMotionInfo( PredictionUnit &pu, MergeCtx &geoMrgCtx, const uint8
 #if JVET_AG0164_AFFINE_GPM
   bool isIntra0 = candIdx0 >= GEO_MAX_ALL_INTER_UNI_CANDS;
   bool isIntra1 = candIdx1 >= GEO_MAX_ALL_INTER_UNI_CANDS;
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+  bool isIbc0 = candIdx0 >= GEO_MAX_ALL_INTER_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS;
+  bool isIbc1 = candIdx1 >= GEO_MAX_ALL_INTER_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS;
+#endif
 #else
   bool isIntra0 = candIdx0 >= GEO_MAX_NUM_UNI_CANDS;
   bool isIntra1 = candIdx1 >= GEO_MAX_NUM_UNI_CANDS;
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+  bool isIbc0 = candIdx0 >= GEO_MAX_NUM_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS;
+  bool isIbc1 = candIdx1 >= GEO_MAX_NUM_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS;
+#endif
 #endif
   uint32_t sliceIdx = pu.cs->slice->getIndependentSliceIdx();
 #else
@@ -26836,9 +27018,17 @@ void PU::spanGeoMotionInfo( PredictionUnit &pu, MergeCtx &geoMrgCtx, const uint8
 #if JVET_AG0164_AFFINE_GPM
     geoIpm[0] = isIntra0 ? intraMPM[candIdx0 - GEO_MAX_ALL_INTER_UNI_CANDS] : -1;
     geoIpm[1] = isIntra1 ? intraMPM[candIdx1 - GEO_MAX_ALL_INTER_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS] : -1;
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+    geoIpm[0] = isIntra0 ? (isIbc0 ? PLANAR_IDX : intraMPM[candIdx0 - GEO_MAX_ALL_INTER_UNI_CANDS]) : -1;
+    geoIpm[1] = isIntra1 ? (isIbc1 ? PLANAR_IDX : intraMPM[candIdx1 - GEO_MAX_ALL_INTER_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS]) : -1;
+#endif
 #else
     geoIpm[0] = isIntra0 ? intraMPM[candIdx0 - GEO_MAX_NUM_UNI_CANDS] : -1;
     geoIpm[1] = isIntra1 ? intraMPM[candIdx1 - GEO_MAX_NUM_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS] : -1;
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+    geoIpm[0] = isIntra0 ? (isIbc0 ? PLANAR_IDX : intraMPM[candIdx0 - GEO_MAX_NUM_UNI_CANDS]) : -1;
+    geoIpm[1] = isIntra1 ? (isIbc1 ? PLANAR_IDX : intraMPM[candIdx1 - GEO_MAX_NUM_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS]) : -1;
+#endif
 #endif
     geoIpm[2] = (isIntra0 && isIntra1) ? ((candIdx1 < candIdx0) ? geoIpm[1] : geoIpm[0]) : isIntra0 ? geoIpm[0] : geoIpm[1];
 
@@ -27138,6 +27328,9 @@ void PU::spanGeoMMVDMotionInfo(PredictionUnit& pu, MergeCtx& geoMrgCtx
   , AffineMergeCtx& geoAffMrgCtx
 #endif
   , MergeCtx& geoTmMrgCtx0, MergeCtx& geoTmMrgCtx1, const uint8_t splitDir, const uint8_t mergeIdx0, const uint8_t mergeIdx1, const bool tmFlag0, const bool mmvdFlag0, const uint8_t mmvdIdx0, const bool tmFlag1, const bool mmvdFlag1, const uint8_t mmvdIdx1, const uint8_t bldIdx, const uint8_t *intraMPM,
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+    const Mv* geoBvList,
+#endif
   const bool dmvrPart0, const bool dmvrPart1, Mv* bdofSubPuMvOffsetPart0, Mv* bdofSubPuMvOffsetPart1)
 #else
 void PU::spanGeoMMVDMotionInfo(PredictionUnit &pu, MergeCtx &geoMrgCtx, MergeCtx &geoTmMrgCtx0, MergeCtx &geoTmMrgCtx1, const uint8_t splitDir, const uint8_t mergeIdx0, const uint8_t mergeIdx1, const bool tmFlag0, const bool mmvdFlag0, const uint8_t mmvdIdx0, const bool tmFlag1, const bool mmvdFlag1, const uint8_t mmvdIdx1, const uint8_t bldIdx, const uint8_t *intraMPM)
@@ -27575,9 +27768,36 @@ void PU::spanGeoMMVDMotionInfo( PredictionUnit &pu, MergeCtx &geoMrgCtx, const u
 #if JVET_AG0164_AFFINE_GPM
   bool isIntra0 = mergeIdx0 >= GEO_MAX_ALL_INTER_UNI_CANDS;
   bool isIntra1 = mergeIdx1 >= GEO_MAX_ALL_INTER_UNI_CANDS;
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+  bool isIbc0 = mergeIdx0 >= GEO_MAX_ALL_INTER_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS;
+  bool isIbc1 = mergeIdx1 >= GEO_MAX_ALL_INTER_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS;
+#endif
 #else
   bool isIntra0 = mergeIdx0 >= GEO_MAX_NUM_UNI_CANDS;
   bool isIntra1 = mergeIdx1 >= GEO_MAX_NUM_UNI_CANDS;
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+  bool isIbc0 = mergeIdx0 >= GEO_MAX_NUM_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS;
+  bool isIbc1 = mergeIdx1 >= GEO_MAX_NUM_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS;
+#endif
+#endif
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+  Mv bv = Mv(0, 0);
+  if (isIbc0)
+  {
+#if JVET_AG0164_AFFINE_GPM
+    bv = geoBvList[mergeIdx0 - GEO_MAX_ALL_INTER_UNI_CANDS - GEO_MAX_NUM_INTRA_CANDS];
+#else
+    bv = geoBvList[mergeIdx0 - GEO_MAX_NUM_UNI_CANDS - GEO_MAX_NUM_INTRA_CANDS];
+#endif
+  }
+  else if (isIbc1)
+  {
+#if JVET_AG0164_AFFINE_GPM
+    bv = geoBvList[mergeIdx1 - GEO_MAX_ALL_INTER_UNI_CANDS - GEO_MAX_NUM_INTRA_CANDS];
+#else
+    bv = geoBvList[mergeIdx1 - GEO_MAX_NUM_UNI_CANDS - GEO_MAX_NUM_INTRA_CANDS];
+#endif
+  }
 #endif
   uint32_t sliceIdx = pu.cs->slice->getIndependentSliceIdx();
 #else
@@ -27672,6 +27892,26 @@ void PU::spanGeoMMVDMotionInfo( PredictionUnit &pu, MergeCtx &geoMrgCtx, const u
       tpmMask = motionIdx <= 0 ? (1 - isFlip) : isFlip;
       if (tpmMask == 0 && isIntra0)
       {
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+        if (isIbc0)
+        {
+          mb.at(x, y).isInter = false;
+          mb.at(x, y).isIBCmot = true;
+          mb.at(x, y).useIbcLic = false;
+          mb.at(x, y).useIbcFilter = false;
+          mb.at(x, y).rribcFlipType = 0;
+          mb.at(x, y).interDir = 1;
+          mb.at(x, y).refIdx[0] = MAX_NUM_REF;
+          mb.at(x, y).refIdx[1] = -1;
+          mb.at(x, y).mv[0] = bv;
+          mb.at(x, y).mv[1] = Mv();
+          mb.at(x, y).bv = mb.at(x, y).mv[0];
+          mb.at(x, y).bv.changePrecision(MV_PRECISION_INTERNAL, MV_PRECISION_INT);
+          mb.at(x, y).sliceIdx = sliceIdx;
+        }
+        else
+        {
+#endif
         mb.at(x, y).isInter = false;
         mb.at(x, y).interDir = MAX_UCHAR;
         mb.at(x, y).refIdx[0] = -1;
@@ -27679,6 +27919,9 @@ void PU::spanGeoMMVDMotionInfo( PredictionUnit &pu, MergeCtx &geoMrgCtx, const u
         mb.at(x, y).mv[0] = Mv();
         mb.at(x, y).mv[1] = Mv();
         mb.at(x, y).sliceIdx = sliceIdx;
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+        }
+#endif
       }
 #else
 #if !JVET_AE0046_BI_GPM
@@ -27743,6 +27986,26 @@ void PU::spanGeoMMVDMotionInfo( PredictionUnit &pu, MergeCtx &geoMrgCtx, const u
 #if JVET_Y0065_GPM_INTRA
       else if (tpmMask == 1 && isIntra1)
       {
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+        if (isIbc1)
+        {
+          mb.at(x, y).isInter = false;
+          mb.at(x, y).isIBCmot = true;
+          mb.at(x, y).useIbcLic = false;
+          mb.at(x, y).useIbcFilter = false;
+          mb.at(x, y).rribcFlipType = 0;
+          mb.at(x, y).interDir = 1;
+          mb.at(x, y).refIdx[0] = MAX_NUM_REF;
+          mb.at(x, y).refIdx[1] = -1;
+          mb.at(x, y).mv[0] = bv;
+          mb.at(x, y).mv[1] = Mv();
+          mb.at(x, y).bv = mb.at(x, y).mv[0];
+          mb.at(x, y).bv.changePrecision(MV_PRECISION_INTERNAL, MV_PRECISION_INT);
+          mb.at(x, y).sliceIdx = sliceIdx;
+        }
+        else
+        {
+#endif
         mb.at(x, y).isInter = false;
         mb.at(x, y).interDir = MAX_UCHAR;
         mb.at(x, y).refIdx[0] = -1;
@@ -27750,6 +28013,9 @@ void PU::spanGeoMMVDMotionInfo( PredictionUnit &pu, MergeCtx &geoMrgCtx, const u
         mb.at(x, y).mv[0] = Mv();
         mb.at(x, y).mv[1] = Mv();
         mb.at(x, y).sliceIdx = sliceIdx;
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+        }
+#endif
       }
 #endif
       else
@@ -27822,9 +28088,17 @@ void PU::spanGeoMMVDMotionInfo( PredictionUnit &pu, MergeCtx &geoMrgCtx, const u
 #if JVET_AG0164_AFFINE_GPM
     geoIpm[0] = isIntra0 ? intraMPM[mergeIdx0 - GEO_MAX_ALL_INTER_UNI_CANDS] : -1;
     geoIpm[1] = isIntra1 ? intraMPM[mergeIdx1 - GEO_MAX_ALL_INTER_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS] : -1;
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+    geoIpm[0] = isIntra0 ? (isIbc0 ? PLANAR_IDX : intraMPM[mergeIdx0 - GEO_MAX_ALL_INTER_UNI_CANDS]) : -1;
+    geoIpm[1] = isIntra1 ? (isIbc1 ? PLANAR_IDX : intraMPM[mergeIdx1 - GEO_MAX_ALL_INTER_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS]) : -1;
+#endif
 #else
     geoIpm[0] = isIntra0 ? intraMPM[mergeIdx0 - GEO_MAX_NUM_UNI_CANDS] : -1;
     geoIpm[1] = isIntra1 ? intraMPM[mergeIdx1 - GEO_MAX_NUM_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS] : -1;
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+    geoIpm[0] = isIntra0 ? (isIbc0 ? PLANAR_IDX : intraMPM[mergeIdx0 - GEO_MAX_NUM_UNI_CANDS]) : -1;
+    geoIpm[1] = isIntra1 ? (isIbc1 ? PLANAR_IDX : intraMPM[mergeIdx1 - GEO_MAX_NUM_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS]) : -1;
+#endif
 #endif
     geoIpm[2] = (isIntra0 && isIntra1) ? ((mergeIdx1 < mergeIdx0) ? geoIpm[1] : geoIpm[0]) : isIntra0 ? geoIpm[0] : geoIpm[1];
 
@@ -31093,4 +31367,263 @@ void CU::saveModelsInHEIP(const CodingUnit &cu)
 }
 #endif
 
+#if JVET_AI0082_TEMPORAL_BV
+bool getColocatedBVP(const PredictionUnit &pu, const Position &posIn, MotionInfo& rcMv, const int col)
+{
+  // don't perform MV compression when generally disabled or subPuMvp is used
+  const unsigned scale = 4 * std::max<int>(1, 4 * AMVP_DECIMATION_FACTOR / 4);
+  const unsigned mask  = ~( scale - 1 );
+  const Position pos = Position{ PosType( posIn.x & mask ), PosType( posIn.y & mask ) };
+  const Slice &slice = *pu.cs->slice;
+  const Picture* const pColPic = slice.getRefPic(RefPicList(col == 0 ? 1 - slice.getColFromL0Flag() : 1 - slice.getColFromL0Flag2nd()), col == 0 ? slice.getColRefIdx() : slice.getColRefIdx2nd());
+  const MotionInfo& mi = pColPic->cs->getMotionInfo( pos );
+  if( !pColPic )
+  {
+    return false;
+  }
+  if (mi.isIBCmot)
+  {
+    rcMv = mi;
+    return true;
+  }
+  else
+  {
+    return false;
+  }
+}
+
+struct ShiftCand
+{
+  Mv shiftMv;
+  int colIdx;
+  ShiftCand(): shiftMv(Mv(0, 0)), colIdx(0) {}
+  ShiftCand(Mv mv, int colid): shiftMv(mv), colIdx(colid){}
+  bool operator==(const ShiftCand &other) const
+  {
+    if (shiftMv == other.shiftMv && colIdx == other.colIdx)
+    {
+      return true;
+    }
+    return false;
+  }
+};
+
+void addOneTypeTempCandidates(const PredictionUnit &pu, std::vector<MotionInfo> &temporalMiCandList,
+                              std::vector<ShiftCand> &shiftCands)
+{
+  std::vector<std::pair<MotionInfo, Position>> tempCandList;
+  const CodingStructure &cs        = *pu.cs;
+  const PreCalcValues   &pcv       = *cs.pcv;
+  const SubPic          &curSubPic = cs.slice->getPPS()->getSubPicFromPos(pu.lumaPos());
+  Position posCand[5] = {pu.Y().center(), pu.Y().topLeft(), pu.Y().topRight(), pu.Y().bottomLeft(), pu.Y().bottomRight()};
+  for (auto shiftCand: shiftCands)
+  {
+    for (int refPosIdx = 0; refPosIdx < 5; refPosIdx++)
+    {
+      int colIdx = shiftCand.colIdx;
+      Mv shiftMv = shiftCand.shiftMv;
+      shiftMv.changePrecision(MV_PRECISION_INTERNAL, MV_PRECISION_INT);
+      Position shiftedRefPos = Position(posCand[refPosIdx].x + shiftMv.hor, posCand[refPosIdx].y + shiftMv.ver);
+      bool boundaryCond = false;
+      if (curSubPic.getTreatedAsPicFlag())
+      {
+        boundaryCond = (shiftedRefPos.x <= curSubPic.getSubPicRight()) && (shiftedRefPos.y <= curSubPic.getSubPicBottom());
+      }
+      else
+      {
+        boundaryCond = (shiftedRefPos.x < pcv.lumaWidth) && (shiftedRefPos.y < pcv.lumaHeight);
+      }
+      MotionInfo cColMi;
+      bool       bExistMv = boundaryCond && getColocatedBVP(pu, shiftedRefPos, cColMi, colIdx);
+      if (bExistMv)
+      {
+        // temporalMiCandList.push_back(cColMi);
+        tempCandList.push_back({cColMi, Position(abs(shiftedRefPos.x - posCand[0].x), abs(shiftedRefPos.y - posCand[0].y))});
+      }
+    }
+  }
+  std::stable_sort(tempCandList.begin(), tempCandList.end(), [](const std::pair<MotionInfo, Position> &a, const std::pair<MotionInfo, Position> &b) { return (a.second.x + a.second.y) < (b.second.x + b.second.y); });
+  for(auto cand: tempCandList)
+  {
+    temporalMiCandList.push_back(cand.first);
+  }
+}
+
+void getTemporalBv(const PredictionUnit &pu, std::vector<MotionInfo>& temporalMiCandList)
+{
+  const CodingStructure &cs        = *pu.cs;
+  const Slice           &slice     = *cs.slice;
+  // co-located positions in colocated picture
+  std::vector<ShiftCand> colPos;
+  for (int colIdx = 0; colIdx < (slice.isInterB() ? 2 : 1) && !slice.isIntra(); colIdx++)
+  {
+    const Picture* const pColPic = slice.getRefPic(RefPicList(colIdx == 0 ? 1 - slice.getColFromL0Flag() : 1 - slice.getColFromL0Flag2nd()), colIdx == 0 ? slice.getColRefIdx() : slice.getColRefIdx2nd());
+    if( !pColPic || pColPic->isRefScaled( slice.getPPS() ))
+    {
+      continue;
+    }
+    colPos.push_back(ShiftCand(Mv(0, 0), colIdx));
+  }
+
+  // shifted positions in colocated pictures
+  std::vector<ShiftCand> shiftedColPos;
+  const Position topLeft    = pu.Y().topLeft();
+  const Position posCand[5] = { topLeft.offset(-1, pu.Y().height - 1), topLeft.offset(pu.Y().width - 1, -1),
+                                topLeft.offset(-1, -1), topLeft.offset(pu.Y().width, -1),
+                                topLeft.offset(-1, pu.Y().height) };
+  const unsigned plevel     = cs.sps->getLog2ParallelMergeLevelMinus2() + 2;
+  for (int colIdx = 0; colIdx < (slice.isInterB() ? 2 : 1) && !slice.isIntra(); colIdx++)
+  {
+    const Picture* const pColPic = slice.getRefPic(RefPicList(colIdx == 0 ? 1 - slice.getColFromL0Flag() : 1 - slice.getColFromL0Flag2nd()), colIdx == 0 ? slice.getColRefIdx() : slice.getColRefIdx2nd());
+    if( !pColPic || pColPic->isRefScaled( slice.getPPS() ))
+    {
+      continue;
+    }
+    MotionInfo miNeigh;
+    const int colPOC     = pColPic->getPOC();
+    for (int posIdx = 0; posIdx < 5; posIdx++)
+    {
+      const PredictionUnit *puRef = cs.getPURestricted(posCand[posIdx], pu, pu.chType);
+      bool isAvailableNeigh = puRef &&
+                              PU::isDiffMER(pu.lumaPos(), posCand[posIdx], plevel) &&
+                              puRef->getMotionInfo( posCand[posIdx] ).isInter &&
+                              pu.cu != puRef->cu && CU::isInter(*puRef->cu);
+      if (isAvailableNeigh)
+      {
+        miNeigh = puRef->getMotionInfo(posCand[posIdx]);
+        for (int i = 0; i < 2; i++)
+        {
+          int refIdx = miNeigh.refIdx[i];
+          if (refIdx != -1)
+          {
+            const int currRefPOC = slice.getRefPic(RefPicList(i), refIdx)->getPOC();
+            if (currRefPOC == colPOC)
+            {
+              shiftedColPos.push_back(ShiftCand(miNeigh.mv[i], colIdx));
+            }
+            else
+            {
+              int distPOC0 = colPOC - cs.picture->getPOC();
+              int distPOC1 = currRefPOC - cs.picture->getPOC();
+              if(SIGN(distPOC0) == SIGN(distPOC1))
+              {
+                Mv scaledMv(miNeigh.mv[i].getHor() * distPOC0 / distPOC1, miNeigh.mv[i].getVer() * distPOC0 / distPOC1);
+                shiftedColPos.push_back(ShiftCand(scaledMv, colIdx));
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+
+  std::vector<ShiftCand> oppositeShiftedColPos;
+  int colPic0Poc = -1;
+  int colPic1Poc = -1;
+  for (int colIdx = 0; colIdx < (slice.isInterB() ? 2 : 1) && !slice.isIntra(); colIdx++)
+  {
+    const Picture* const pColPic = slice.getRefPic(RefPicList(colIdx == 0 ? 1 - slice.getColFromL0Flag() : 1 - slice.getColFromL0Flag2nd()), colIdx == 0 ? slice.getColRefIdx() : slice.getColRefIdx2nd());
+    if( !pColPic || pColPic->isRefScaled( slice.getPPS() ) )
+    {
+      continue;
+    }
+    if(colIdx)
+    {
+      colPic1Poc = pColPic->getPOC();
+    }
+    else
+    {
+      colPic0Poc = pColPic->getPOC();
+    }
+  }
+  if(colPic0Poc != -1 && colPic1Poc != -1)
+  {
+    int curPoc = cs.picture->getPOC();
+    int distPoc0 = colPic0Poc - curPoc;
+    int distPoc1 = colPic1Poc - curPoc;
+    // CHECK(abs(distPoc0) != abs(distPoc1), "abs(distPoc0) != abs(distPoc1)");
+    for(auto shiftCand: shiftedColPos)
+    {
+      Mv shiftMv = shiftCand.shiftMv;
+      int colIdx = shiftCand.colIdx;
+      int mvHor = (colIdx == 0) ? (shiftMv.hor * distPoc1 / distPoc0) : (shiftMv.hor * distPoc0 / distPoc1);
+      int mvVer = (colIdx == 0) ? (shiftMv.ver * distPoc1 / distPoc0) : (shiftMv.ver * distPoc0 / distPoc1);
+      oppositeShiftedColPos.push_back({Mv(mvHor, mvVer), colIdx == 0 ? 1 : 0});
+    }
+  }
+
+  // scaled positions in colocated pictures
+  std::vector<ShiftCand> scaledColPos;
+  for (int colIdx = 0; colIdx < (slice.isInterB() ? 2 : 1) && !slice.isIntra(); colIdx++)
+  {
+    const Picture* const pColPic = slice.getRefPic(RefPicList(colIdx == 0 ? 1 - slice.getColFromL0Flag() : 1 - slice.getColFromL0Flag2nd()), colIdx == 0 ? slice.getColRefIdx() : slice.getColRefIdx2nd());
+    if( !pColPic || pColPic->isRefScaled( slice.getPPS() ))
+    {
+      continue;
+    }
+    MotionInfo miNeigh;
+    const int colPOC     = pColPic->getPOC();
+    for (int posIdx = 0; posIdx < 5; posIdx++)
+    {
+      const PredictionUnit *puRef = cs.getPURestricted(posCand[posIdx], pu, pu.chType);
+      bool isAvailableNeigh = puRef &&
+                              PU::isDiffMER(pu.lumaPos(), posCand[posIdx], plevel) &&
+                              puRef->getMotionInfo( posCand[posIdx] ).isInter &&
+                              pu.cu != puRef->cu && CU::isInter(*puRef->cu);
+      if (isAvailableNeigh)
+      {
+        miNeigh = puRef->getMotionInfo(posCand[posIdx]);
+        for (int i = 0; i < 2; i++)
+        {
+          int refIdx = miNeigh.refIdx[i];
+          if (refIdx != -1)
+          {
+            const int currRefPOC = slice.getRefPic(RefPicList(i), refIdx)->getPOC();
+            if (currRefPOC != colPOC)
+            {
+              int distPOC0 = colPOC - cs.picture->getPOC();
+              int distPOC1 = currRefPOC - cs.picture->getPOC();
+              if(SIGN(distPOC0) == SIGN(distPOC1))
+              {
+                Mv scaledMv(miNeigh.mv[i].getHor() * distPOC0 / distPOC1, miNeigh.mv[i].getVer() * distPOC0 / distPOC1);
+                scaledColPos.push_back(ShiftCand(scaledMv, colIdx));
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+  std::vector<ShiftCand> temporalPos;
+  auto addPos = [&](const ShiftCand pos)->void
+  {
+    bool isIncluded = false;
+    for(auto posInList: temporalPos)
+    {
+      isIncluded |= (pos == posInList);
+    }
+    if (!isIncluded)
+    {
+      temporalPos.push_back(pos);
+    }
+  };
+  for(auto pos: colPos)
+  {
+    addPos(pos);
+  }
+  for(auto pos: shiftedColPos)
+  {
+    addPos(pos);
+  }
+  for(auto pos: oppositeShiftedColPos)
+  {
+    addPos(pos);
+  }
+  for(auto pos: scaledColPos)
+  {
+    addPos(pos);
+  }
+  addOneTypeTempCandidates(pu, temporalMiCandList, temporalPos);
+}
+#endif
 
diff --git a/source/Lib/CommonLib/UnitTools.h b/source/Lib/CommonLib/UnitTools.h
index 1ec488897..98e26459c 100644
--- a/source/Lib/CommonLib/UnitTools.h
+++ b/source/Lib/CommonLib/UnitTools.h
@@ -446,7 +446,7 @@ namespace PU
   inline void       getRribcBvpCand(PredictionUnit &pu, AMVPInfo *amvpInfo);
   inline void       clusterBvpCand(const int cbWidth, const int cbHeight, AMVPInfo *pInfo);
 #endif
-  void fillMvpCand                    (      PredictionUnit &pu, const RefPicList &eRefPicList, const int &refIdx, AMVPInfo &amvpInfo 
+  void fillMvpCand                    (      PredictionUnit &pu, const RefPicList &eRefPicList, const int &refIdx, AMVPInfo &amvpInfo
 #if TM_AMVP || JVET_AC0060_IBC_BVP_CLUSTER_RRIBC_BVD_SIGN_DERIV
                                      , InterPrediction* interPred = nullptr
 #endif
@@ -809,6 +809,9 @@ namespace PU
      , AffineMergeCtx& geoAffMrgCtx
 #endif
     , MergeCtx& geoTmMrgCtx0, MergeCtx& geoTmMrgCtx1, const uint8_t splitDir, const uint8_t mergeIdx0, const uint8_t mergeIdx1, const bool tmFlag0, const bool mmvdFlag0, const uint8_t mmvdIdx0, const bool tmFlag1, const bool mmvdFlag1, const uint8_t mmvdIdx1, const uint8_t bldIdx,const uint8_t *intraMPM,
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+      const Mv* geoBvList,
+#endif
     const bool dmvrPart0 = false, const bool dmvrPart1 = false, Mv* bdofSubPuMvOffsetPart0 = nullptr, Mv* bdofSubPuMvOffsetPart1 = nullptr);
 #else
   void spanGeoMMVDMotionInfo(PredictionUnit &pu, MergeCtx &geoMrgCtx, MergeCtx &geoTmMrgCtx0, MergeCtx &geoTmMrgCtx1, const uint8_t splitDir, const uint8_t mergeIdx0, const uint8_t mergeIdx1, const bool tmFlag0, const bool mmvdFlag0, const uint8_t mmvdIdx0, const bool tmFlag1, const bool mmvdFlag1, const uint8_t mmvdIdx1, const uint8_t bldIdx, const uint8_t *intraMPM);
@@ -1367,7 +1370,11 @@ void sortIntraCandList(double uiCost, int mergeCand, static_vector<double, N>& c
 {
   size_t shift = 0;
   size_t currSize = candCostList.size();
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+  CHECK(currSize >= GEO_MAX_NUM_INTRA_CANDS + GEO_MAX_NUM_IBC_CANDS, "list overflow!");
+#else
   CHECK(currSize >= GEO_MAX_NUM_INTRA_CANDS, "list overflow!");
+#endif
 
   while (shift < currSize && uiCost < candCostList[currSize - 1 - shift])
   {
@@ -1407,6 +1414,11 @@ bool     getAllowedEipMerge(const CodingUnit &cu, const ComponentID compId);
 bool     getAllowedEip(const CodingUnit &cu, const ComponentID compId);
 int getAllowedCurEip(const CodingUnit& cu, const ComponentID compId, static_vector<EIPInfo, NUM_DERIVED_EIP>& eipInfoList);
 #endif
+
+#if JVET_AI0082_TEMPORAL_BV
+bool getColocatedBVP(const PredictionUnit &pu, const Position &posIn, MotionInfo &rcMv, const int col);
+void getTemporalBv(const PredictionUnit &pu, std::vector<MotionInfo>& temporalMiCandList);
+#endif
 #endif
 #if JVET_AG0061_INTER_LFNST_NSPT
 int buildHistogram(const Pel *pReco, int iStride, uint32_t uiHeight, uint32_t uiWidth, int *piHistogram, int direction, int bw, int bh);
diff --git a/source/Lib/DecoderLib/CABACReader.cpp b/source/Lib/DecoderLib/CABACReader.cpp
index 82dc26815..4c146425d 100644
--- a/source/Lib/DecoderLib/CABACReader.cpp
+++ b/source/Lib/DecoderLib/CABACReader.cpp
@@ -5561,6 +5561,9 @@ void CABACReader::merge_idx( PredictionUnit& pu )
         pu.geoMergeIdx0 = (uint8_t)pu.mergeIdx;
         pu.geoMergeIdx1 = (uint8_t)pu.mergeIdx;
         pu.gpmIntraFlag = false;
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+        pu.gpmInterIbcFlag = false;
+#endif
         return;
       }
 #endif
@@ -5578,6 +5581,11 @@ void CABACReader::merge_idx( PredictionUnit& pu )
       bool isIntra0 = false;
       bool isIntra1 = false;
       bool bUseOnlyOneVector = pu.cs->slice->isInterP() || pu.cs->sps->getMaxNumGeoCand() == 1;
+#endif
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+      bool isIbc0 = false;
+      bool isIbc1 = false;
+      bool isGpmInterIbcEnabled = pu.cs->sps->getUseGeoInterIbc();
 #endif
       pu.geoMMVDFlag0 = m_BinDecoder.decodeBin(Ctx::GeoMmvdFlag());
       if (pu.geoMMVDFlag0)
@@ -5599,6 +5607,12 @@ void CABACReader::merge_idx( PredictionUnit& pu )
       else
       {
         isIntra0 = m_BinDecoder.decodeBin( Ctx::GPMIntraFlag() ) ? true : false;
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+        if (isIntra0 && isGpmInterIbcEnabled)
+        {
+          isIbc0 = m_BinDecoder.decodeBin( Ctx::GpmInterIbcFlag() ) ? true : false;
+        }
+#endif
       }
 
       if (!bUseOnlyOneVector || isIntra0)
@@ -5624,6 +5638,12 @@ void CABACReader::merge_idx( PredictionUnit& pu )
       else if (!isIntra0)
       {
         isIntra1 = m_BinDecoder.decodeBin( Ctx::GPMIntraFlag() ) ? true : false;
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+        if (isIntra1 && isGpmInterIbcEnabled)
+        {
+          isIbc1 = m_BinDecoder.decodeBin( Ctx::GpmInterIbcFlag() ) ? true : false;
+        }
+#endif
       }
       }
       else
@@ -5631,6 +5651,9 @@ void CABACReader::merge_idx( PredictionUnit& pu )
         isIntra1 = true;
       }
       pu.gpmIntraFlag = (isIntra0 || isIntra1);
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+      pu.gpmInterIbcFlag = (isIbc0 || isIbc1);
+#endif
 #endif
 
 #if TM_MRG
@@ -5664,7 +5687,11 @@ void CABACReader::merge_idx( PredictionUnit& pu )
 #if JVET_Y0065_GPM_INTRA
           if (isIntra0 || isIntra1)
           {
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+            geo_merge_idx1(pu, isIntra0, isIntra1, isIbc0, isIbc1);
+#else
             geo_merge_idx1(pu, isIntra0, isIntra1);
+#endif
           }
           else
 #endif
@@ -5693,7 +5720,11 @@ void CABACReader::merge_idx( PredictionUnit& pu )
         else
         {
 #if JVET_Y0065_GPM_INTRA
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+          geo_merge_idx1(pu, isIntra0, isIntra1, isIbc0, isIbc1);
+#else
           geo_merge_idx1(pu, isIntra0, isIntra1);
+#endif
 #else
           geo_merge_idx1(pu);
 #endif
@@ -5702,7 +5733,11 @@ void CABACReader::merge_idx( PredictionUnit& pu )
       else
       {
 #if JVET_Y0065_GPM_INTRA
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+        geo_merge_idx1(pu, isIntra0, isIntra1, isIbc0, isIbc1);
+#else
         geo_merge_idx1(pu, isIntra0, isIntra1);
+#endif
 #else
         geo_merge_idx1(pu);
 #endif
@@ -6041,7 +6076,11 @@ void CABACReader::geo_merge_idx(PredictionUnit& pu)
 }
 
 #if JVET_Y0065_GPM_INTRA
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+void CABACReader::geo_merge_idx1(PredictionUnit& pu, bool isIntra0, bool isIntra1, bool isIbc0, bool isIbc1)
+#else
 void CABACReader::geo_merge_idx1(PredictionUnit& pu, bool isIntra0, bool isIntra1)
+#endif
 #else
 void CABACReader::geo_merge_idx1(PredictionUnit& pu)
 #endif
@@ -6072,10 +6111,38 @@ void CABACReader::geo_merge_idx1(PredictionUnit& pu)
 #if JVET_Y0065_GPM_INTRA
   if (isIntra0)
   {
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+    if (isIbc0)
+    {
+      unsigned int uiUnaryIdx = 0;
+      int numCandminus1 = GEO_MAX_NUM_IBC_CANDS - 1;
+      for (; uiUnaryIdx < numCandminus1; ++uiUnaryIdx)
+      {
+        if (!m_BinDecoder.decodeBin(Ctx::GpmInterIbcIdx((uiUnaryIdx > LAST_MERGE_IDX_CABAC - 1 ? LAST_MERGE_IDX_CABAC - 1 : uiUnaryIdx))))
+        {
+          break;
+        }
+      }
+#if JVET_AG0164_AFFINE_GPM
+      mergeCand0 = GEO_MAX_ALL_INTER_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS + uiUnaryIdx;
+#else
+      mergeCand0 = GEO_MAX_NUM_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS + uiUnaryIdx;
+#endif
+    }
+    else
+    {
+#if JVET_AG0164_AFFINE_GPM
+      mergeCand0 = GEO_MAX_ALL_INTER_UNI_CANDS + unary_max_eqprob(GEO_MAX_NUM_INTRA_CANDS-1);
+#else
+      mergeCand0 = GEO_MAX_NUM_UNI_CANDS + unary_max_eqprob(GEO_MAX_NUM_INTRA_CANDS - 1);
+#endif
+    }
+#else
 #if JVET_AG0164_AFFINE_GPM
     mergeCand0 = GEO_MAX_ALL_INTER_UNI_CANDS + unary_max_eqprob(GEO_MAX_NUM_INTRA_CANDS-1);
 #else
     mergeCand0 = GEO_MAX_NUM_UNI_CANDS + unary_max_eqprob(GEO_MAX_NUM_INTRA_CANDS-1);
+#endif
 #endif
   }
   else if (numCandminus2 >= 0)
@@ -6107,10 +6174,38 @@ void CABACReader::geo_merge_idx1(PredictionUnit& pu)
 
   if (isIntra1)
   {
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+    if (isIbc1)
+    {
+      unsigned int uiUnaryIdx = 0;
+      int numCandminus1 = GEO_MAX_NUM_IBC_CANDS - 1;
+      for (; uiUnaryIdx < numCandminus1; ++uiUnaryIdx)
+      {
+        if (!m_BinDecoder.decodeBin(Ctx::GpmInterIbcIdx((uiUnaryIdx > LAST_MERGE_IDX_CABAC - 1 ? LAST_MERGE_IDX_CABAC - 1 : uiUnaryIdx))))
+        {
+          break;
+        }
+      }
+#if JVET_AG0164_AFFINE_GPM
+      mergeCand1 = GEO_MAX_ALL_INTER_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS + uiUnaryIdx;
+#else
+      mergeCand1 = GEO_MAX_NUM_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS + uiUnaryIdx;
+#endif
+    }
+    else
+    {
+#if JVET_AG0164_AFFINE_GPM
+      mergeCand1 = GEO_MAX_ALL_INTER_UNI_CANDS + unary_max_eqprob(GEO_MAX_NUM_INTRA_CANDS - 1);
+#else
+      mergeCand1 = GEO_MAX_NUM_UNI_CANDS + unary_max_eqprob(GEO_MAX_NUM_INTRA_CANDS - 1);
+#endif
+    }
+#else
 #if JVET_AG0164_AFFINE_GPM
     mergeCand1 = GEO_MAX_ALL_INTER_UNI_CANDS + unary_max_eqprob(GEO_MAX_NUM_INTRA_CANDS - 1);
 #else
     mergeCand1 = GEO_MAX_NUM_UNI_CANDS + unary_max_eqprob(GEO_MAX_NUM_INTRA_CANDS-1);
+#endif
 #endif
   }
   else if (numCandminus2 >= 0)
diff --git a/source/Lib/DecoderLib/CABACReader.h b/source/Lib/DecoderLib/CABACReader.h
index 1465c72a7..bb0de1181 100644
--- a/source/Lib/DecoderLib/CABACReader.h
+++ b/source/Lib/DecoderLib/CABACReader.h
@@ -212,7 +212,11 @@ public:
   void        geo_mmvd_idx(PredictionUnit&          pu, RefPicList eRefPicList);
   void        geo_merge_idx(PredictionUnit&          pu);
 #if JVET_Y0065_GPM_INTRA
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+  void        geo_merge_idx1          ( PredictionUnit&          pu, bool isIntra0, bool isIntra1, bool isIbc0, bool isIbc1);
+#else
   void        geo_merge_idx1          ( PredictionUnit&          pu, bool isIntra0, bool isIntra1);
+#endif
 #else
   void        geo_merge_idx1          ( PredictionUnit&          pu);
 #endif
diff --git a/source/Lib/DecoderLib/DecCu.cpp b/source/Lib/DecoderLib/DecCu.cpp
index 90fb33183..ad4d824cd 100644
--- a/source/Lib/DecoderLib/DecCu.cpp
+++ b/source/Lib/DecoderLib/DecCu.cpp
@@ -1889,6 +1889,9 @@ void DecCu::xReconInter(CodingUnit &cu)
 #endif
 #if JVET_Y0065_GPM_INTRA
                                         , m_pcIntraPred, (cu.cs->slice->getLmcsEnabledFlag() && m_pcReshape->getCTUFlag()) ? &m_pcReshape->getFwdLUT() : nullptr
+#endif
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+                                        , m_geoBvList
 #endif
     );
 #if JVET_W0097_GPM_MMVD_TM && TM_MRG
@@ -1922,6 +1925,9 @@ void DecCu::xReconInter(CodingUnit &cu)
                               , cu.firstPU->geoBldIdx
 #endif
                               , m_pcIntraPred->m_intraMPM
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+                              , m_geoBvList
+#endif
 #if JVET_AE0046_BI_GPM
                                , cu.firstPU->gpmDmvrRefinePart0
                                , cu.firstPU->gpmDmvrRefinePart1
diff --git a/source/Lib/DecoderLib/DecCu.h b/source/Lib/DecoderLib/DecCu.h
index 4e1793d13..c74e55597 100644
--- a/source/Lib/DecoderLib/DecCu.h
+++ b/source/Lib/DecoderLib/DecCu.h
@@ -131,6 +131,9 @@ private:
 #if JVET_AG0164_AFFINE_GPM
   AffineMergeCtx    m_geoAffMrgCtx;
 #endif
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+  Mv                m_geoBvList[GEO_MAX_NUM_IBC_CANDS];
+#endif
 #if JVET_W0097_GPM_MMVD_TM && TM_MRG
 #if JVET_Z0056_GPM_SPLIT_MODE_REORDERING
   MergeCtx          m_geoTmMrgCtx[GEO_NUM_TM_MV_CAND];
diff --git a/source/Lib/DecoderLib/VLCReader.cpp b/source/Lib/DecoderLib/VLCReader.cpp
index d644b269c..a1cd16a6b 100644
--- a/source/Lib/DecoderLib/VLCReader.cpp
+++ b/source/Lib/DecoderLib/VLCReader.cpp
@@ -2638,6 +2638,10 @@ void HLSyntaxReader::parseSPS(SPS* pcSPS)
       {
         READ_FLAG( uiCode,    "sps_gpm_tm_enabled_flag" );                      pcSPS->setUseGPMTMMode( uiCode != 0 );
       }
+#endif
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+      READ_FLAG(uiCode, "sps_gpm_inter_ibc_enabled_flag");
+      pcSPS->setUseGeoInterIbc(uiCode != 0);
 #endif
     }
 #if JVET_AA0132_CONFIGURABLE_TM_TOOLS && JVET_W0097_GPM_MMVD_TM && TM_MRG
diff --git a/source/Lib/EncoderLib/CABACWriter.cpp b/source/Lib/EncoderLib/CABACWriter.cpp
index 084b4c091..0b38f85a3 100644
--- a/source/Lib/EncoderLib/CABACWriter.cpp
+++ b/source/Lib/EncoderLib/CABACWriter.cpp
@@ -5200,6 +5200,16 @@ void CABACWriter::merge_idx( const PredictionUnit& pu )
       bool isIntra1 = (pu.geoMergeIdx1 >= GEO_MAX_NUM_UNI_CANDS);
 #endif
       bool bUseOnlyOneVector = pu.cs->slice->isInterP() || pu.cs->sps->getMaxNumGeoCand() == 1;
+#endif
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+#if JVET_AG0164_AFFINE_GPM
+      bool isIbc0 = (pu.geoMergeIdx0 >= GEO_MAX_ALL_INTER_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS);
+      bool isIbc1 = (pu.geoMergeIdx1 >= GEO_MAX_ALL_INTER_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS);
+#else
+      bool isIbc0 = (pu.geoMergeIdx0 >= GEO_MAX_NUM_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS);
+      bool isIbc1 = (pu.geoMergeIdx1 >= GEO_MAX_NUM_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS);
+#endif
+      bool isGpmInterIbcEnabled = pu.cs->sps->getUseGeoInterIbc();
 #endif
       m_BinEncoder.encodeBin(pu.geoMMVDFlag0, Ctx::GeoMmvdFlag());
       if (pu.geoMMVDFlag0)
@@ -5222,6 +5232,12 @@ void CABACWriter::merge_idx( const PredictionUnit& pu )
       else
       {
         m_BinEncoder.encodeBin( isIntra0 ? 1 : 0, Ctx::GPMIntraFlag() );
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+        if (isIntra0 && isGpmInterIbcEnabled)
+        {
+          m_BinEncoder.encodeBin( isIbc0 ? 1 : 0, Ctx::GpmInterIbcFlag() );
+        }
+#endif
       }
 
       if (!bUseOnlyOneVector || isIntra0)
@@ -5248,6 +5264,12 @@ void CABACWriter::merge_idx( const PredictionUnit& pu )
       else if (!isIntra0)
       {
         m_BinEncoder.encodeBin( isIntra1 ? 1 : 0, Ctx::GPMIntraFlag() );
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+        if (isIntra1 && isGpmInterIbcEnabled)
+        {
+          m_BinEncoder.encodeBin( isIbc1 ? 1 : 0, Ctx::GpmInterIbcFlag() );
+        }
+#endif
       }
       }
       else
@@ -5256,6 +5278,9 @@ void CABACWriter::merge_idx( const PredictionUnit& pu )
       }
 
       CHECK( pu.gpmIntraFlag != (isIntra0 || isIntra1), "gpmIntraFlag shall be equal to (isIntra0 || isIntra1)" );
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+      CHECK( pu.gpmInterIbcFlag != (isIbc0 || isIbc1), "gpmInterIbcFlag shall be equal to (isIbc0 || isIbc1)" );
+#endif
 #endif
 
 #if TM_MRG
@@ -5846,6 +5871,15 @@ void CABACWriter::geo_merge_idx1(const PredictionUnit& pu)
   bool isIntra1 = (candIdx1 >= GEO_MAX_NUM_UNI_CANDS);
 #endif
 #endif
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+#if JVET_AG0164_AFFINE_GPM
+  bool isIbc0 = (candIdx0 >= GEO_MAX_ALL_INTER_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS);
+  bool isIbc1 = (candIdx1 >= GEO_MAX_ALL_INTER_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS);
+#else
+  bool isIbc0 = (candIdx0 >= GEO_MAX_NUM_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS);
+  bool isIbc1 = (candIdx1 >= GEO_MAX_NUM_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS);
+#endif
+#endif
 
 #if JVET_Z0056_GPM_SPLIT_MODE_REORDERING
   geoModeIdx(pu);
@@ -5862,10 +5896,40 @@ void CABACWriter::geo_merge_idx1(const PredictionUnit& pu)
 #if JVET_Y0065_GPM_INTRA
   if (isIntra0)
   {
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+    if (isIbc0)
+    {
+#if JVET_AG0164_AFFINE_GPM
+      int mergeIdx = candIdx0 - GEO_MAX_ALL_INTER_UNI_CANDS - GEO_MAX_NUM_INTRA_CANDS;
+#else
+      int mergeIdx = candIdx0 - GEO_MAX_NUM_UNI_CANDS - GEO_MAX_NUM_INTRA_CANDS;
+#endif
+      int numCandminus1 = GEO_MAX_NUM_IBC_CANDS - 1;
+      unsigned int uiUnaryIdx = 0;
+      for (; uiUnaryIdx < numCandminus1; ++uiUnaryIdx)
+      {
+        unsigned int uiSymbol = mergeIdx == uiUnaryIdx ? 0 : 1;
+        m_BinEncoder.encodeBin(uiSymbol, Ctx::GpmInterIbcIdx((uiUnaryIdx > LAST_MERGE_IDX_CABAC - 1 ? LAST_MERGE_IDX_CABAC - 1 : uiUnaryIdx)));
+        if (uiSymbol == 0)
+        {
+          break;
+        }
+      }
+    }
+    else
+    {
+#if JVET_AG0164_AFFINE_GPM
+      unary_max_eqprob(candIdx0 - GEO_MAX_ALL_INTER_UNI_CANDS, GEO_MAX_NUM_INTRA_CANDS-1);
+#else
+      unary_max_eqprob(candIdx0 - GEO_MAX_NUM_UNI_CANDS, GEO_MAX_NUM_INTRA_CANDS - 1);
+#endif
+    }
+#else
 #if JVET_AG0164_AFFINE_GPM
     unary_max_eqprob(candIdx0 - GEO_MAX_ALL_INTER_UNI_CANDS, GEO_MAX_NUM_INTRA_CANDS-1);
 #else
     unary_max_eqprob(candIdx0 - GEO_MAX_NUM_UNI_CANDS, GEO_MAX_NUM_INTRA_CANDS-1);
+#endif
 #endif
   }
   else if (numCandminus2 >= 0)
@@ -5903,10 +5967,40 @@ void CABACWriter::geo_merge_idx1(const PredictionUnit& pu)
 
   if (isIntra1)
   {
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+    if (isIbc1)
+    {
+#if JVET_AG0164_AFFINE_GPM
+      int mergeIdx = candIdx1 - GEO_MAX_ALL_INTER_UNI_CANDS - GEO_MAX_NUM_INTRA_CANDS;
+#else
+      int mergeIdx = candIdx1 - GEO_MAX_NUM_UNI_CANDS - GEO_MAX_NUM_INTRA_CANDS;
+#endif
+      int numCandminus1 = GEO_MAX_NUM_IBC_CANDS - 1;
+      unsigned int uiUnaryIdx = 0;
+      for (; uiUnaryIdx < numCandminus1; ++uiUnaryIdx)
+      {
+        unsigned int uiSymbol = mergeIdx == uiUnaryIdx ? 0 : 1;
+        m_BinEncoder.encodeBin(uiSymbol, Ctx::GpmInterIbcIdx((uiUnaryIdx > LAST_MERGE_IDX_CABAC - 1 ? LAST_MERGE_IDX_CABAC - 1 : uiUnaryIdx)));
+        if (uiSymbol == 0)
+        {
+          break;
+        }
+      }
+    }
+    else
+    {
+#if JVET_AG0164_AFFINE_GPM
+      unary_max_eqprob(candIdx1 - GEO_MAX_ALL_INTER_UNI_CANDS, GEO_MAX_NUM_INTRA_CANDS - 1);
+#else
+      unary_max_eqprob(candIdx1 - GEO_MAX_NUM_UNI_CANDS, GEO_MAX_NUM_INTRA_CANDS - 1);
+#endif
+    }
+#else
 #if JVET_AG0164_AFFINE_GPM
     unary_max_eqprob(candIdx1 - GEO_MAX_ALL_INTER_UNI_CANDS, GEO_MAX_NUM_INTRA_CANDS - 1);
 #else
     unary_max_eqprob(candIdx1 - GEO_MAX_NUM_UNI_CANDS, GEO_MAX_NUM_INTRA_CANDS-1);
+#endif
 #endif
   }
   else if (numCandminus2 >= 0)
@@ -6022,11 +6116,19 @@ uint64_t CABACWriter::geo_intraFlag_est( const TempCtx& ctxStart, const int flag
   return getEstFracBits();
 }
 
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+uint64_t CABACWriter::geo_intraIdx_est( const int intraIdx, const bool isGeoIbc)
+#else
 uint64_t CABACWriter::geo_intraIdx_est( const int intraIdx)
+#endif
 {
   resetBits();
 
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+  unary_max_eqprob(intraIdx, GEO_MAX_NUM_INTRA_CANDS + (isGeoIbc ? GEO_MAX_NUM_IBC_CANDS : 0) - 1);
+#else
   unary_max_eqprob(intraIdx, GEO_MAX_NUM_INTRA_CANDS-1);
+#endif
 
   return getEstFracBits();
 }
diff --git a/source/Lib/EncoderLib/CABACWriter.h b/source/Lib/EncoderLib/CABACWriter.h
index 598949f2a..edaa3ae66 100644
--- a/source/Lib/EncoderLib/CABACWriter.h
+++ b/source/Lib/EncoderLib/CABACWriter.h
@@ -243,7 +243,11 @@ public:
   );
 #if JVET_Y0065_GPM_INTRA
   uint64_t    geo_intraFlag_est         ( const TempCtx& ctxStart, const int flag);
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+  uint64_t    geo_intraIdx_est          ( const int intraIdx, const bool isGeoIbc);
+#else
   uint64_t    geo_intraIdx_est          ( const int intraIdx);
+#endif
 #endif
   uint64_t    geo_mmvdFlag_est(const TempCtx& ctxStart, const int flag);
   uint64_t    geo_mmvdIdx_est(const TempCtx& ctxStart, const int mmvdIdx, const bool extMMVD);
diff --git a/source/Lib/EncoderLib/EncCfg.h b/source/Lib/EncoderLib/EncCfg.h
index 1a3010a99..21dccb06d 100644
--- a/source/Lib/EncoderLib/EncCfg.h
+++ b/source/Lib/EncoderLib/EncCfg.h
@@ -541,6 +541,9 @@ protected:
   bool      m_ciipTimd;
 #endif
   bool      m_Geo;
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+  bool      m_geoInterIbc;
+#endif
   bool      m_allowDisFracMMVD;
   bool      m_AffineAmvr;
   bool      m_HashME;
@@ -1817,6 +1820,10 @@ public:
 #endif
   void      setUseGeo                       ( bool b )       { m_Geo = b; }
   bool      getUseGeo                       ()         const { return m_Geo; }
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+  void      setUseGeoInterIbc               ( bool b )       { m_geoInterIbc = b; }
+  bool      getUseGeoInterIbc               ()         const { return m_geoInterIbc; }
+#endif
   void      setAllowDisFracMMVD             ( bool b )       { m_allowDisFracMMVD = b;    }
   bool      getAllowDisFracMMVD             ()         const { return m_allowDisFracMMVD; }
   void      setUseHashME                    ( bool b )       { m_HashME = b; }
diff --git a/source/Lib/EncoderLib/EncCu.cpp b/source/Lib/EncoderLib/EncCu.cpp
index bca42b840..274ebaa26 100644
--- a/source/Lib/EncoderLib/EncCu.cpp
+++ b/source/Lib/EncoderLib/EncCu.cpp
@@ -75,7 +75,11 @@ EncCu::EncCu() : m_GeoModeTest
 #if NON_ADJACENT_MRG_CAND
   int numGeoModeTest = 0;
 #if JVET_Y0065_GPM_INTRA
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+  for (int i = 1; i < GEO_MAX_NUM_UNI_CANDS+GEO_MAX_NUM_INTRA_CANDS+GEO_MAX_NUM_IBC_CANDS; i++)
+#else
   for (int i = 1; i < GEO_MAX_NUM_UNI_CANDS+GEO_MAX_NUM_INTRA_CANDS; i++)
+#endif
 #else
   for (int i = 1; i < GEO_MAX_NUM_UNI_CANDS; i++)
 #endif
@@ -8858,6 +8862,10 @@ void EncCu::xCheckRDCostMergeGeoComb2Nx2N(CodingStructure *&tempCS, CodingStruct
   refinePossible.fill(false);
 #endif
 
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+  bool testGeoInterIbc = sps.getUseGeoInterIbc();
+#endif
+
   // 1. bit estimation
   const double sqrtLambdaFracBits = m_pcRdCost->getMotionLambda() * FRAC_BITS_SCALE;
   uint8_t maxNumMergeCandidates = tempCS->sps->getMaxNumGeoCand();
@@ -8942,7 +8950,11 @@ void EncCu::xCheckRDCostMergeGeoComb2Nx2N(CodingStructure *&tempCS, CodingStruct
 #endif
 #if JVET_Y0065_GPM_INTRA
   bool bUseOnlyOneVector = (tempCS->slice->isInterP() || tempCS->sps->getMaxNumGeoCand() == 1);
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+  double geoIntraFlag0Cost[2], geoIntraFlag1Cost[2][2], geoIntraIdxCost[GEO_MAX_NUM_INTRA_CANDS + GEO_MAX_NUM_IBC_CANDS];
+#else
   double geoIntraFlag0Cost[2], geoIntraFlag1Cost[2][2], geoIntraIdxCost[GEO_MAX_NUM_INTRA_CANDS];
+#endif
   for (int idx = 0; idx < 2; idx++)
   {
     uint64_t fracBits = m_CABACEstimator->geo_intraFlag_est(ctxStart, idx);
@@ -8950,9 +8962,17 @@ void EncCu::xCheckRDCostMergeGeoComb2Nx2N(CodingStructure *&tempCS, CodingStruct
     geoIntraFlag1Cost[0][idx] = !bUseOnlyOneVector ? geoIntraFlag0Cost[idx] : 0;
     geoIntraFlag1Cost[1][idx] = 0;
   }
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+  for (int idx = 0; idx < GEO_MAX_NUM_INTRA_CANDS + (testGeoInterIbc ? GEO_MAX_NUM_IBC_CANDS : 0); idx++)
+#else
   for (int idx = 0; idx < GEO_MAX_NUM_INTRA_CANDS; idx++)
+#endif
   {
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+    uint64_t fracBits = m_CABACEstimator->geo_intraIdx_est(idx, testGeoInterIbc);
+#else
     uint64_t fracBits = m_CABACEstimator->geo_intraIdx_est(idx);
+#endif
     geoIntraIdxCost[idx] = (double)fracBits * sqrtLambdaFracBits;
   }
 #endif
@@ -9134,6 +9154,15 @@ void EncCu::xCheckRDCostMergeGeoComb2Nx2N(CodingStructure *&tempCS, CodingStruct
   double bestNormalMrgCost = MAX_DOUBLE;
 
 #if JVET_Y0065_GPM_INTRA
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+    Distortion sadIntraWholeBlk[GEO_MAX_ALL_INTER_UNI_CANDS + GEO_MAX_NUM_IBC_CANDS];
+    uint8_t isGeoChromaAvail[GEO_MAX_ALL_INTER_UNI_CANDS + GEO_MAX_NUM_IBC_CANDS];
+    uint8_t isGeoMMVDChromaAvail[GEO_MAX_ALL_INTER_UNI_CANDS + GEO_MAX_NUM_IBC_CANDS][GPM_EXT_MMVD_MAX_REFINE_NUM];
+    uint8_t isGeoIntraChromaAvail[GEO_NUM_INTRA_RDO_BUFFER];
+    memset(isGeoChromaAvail, 0, sizeof(uint8_t) * (GEO_MAX_ALL_INTER_UNI_CANDS + GEO_MAX_NUM_IBC_CANDS));
+    memset(isGeoMMVDChromaAvail, 0, sizeof(uint8_t) * (GEO_MAX_ALL_INTER_UNI_CANDS + GEO_MAX_NUM_IBC_CANDS)* GPM_EXT_MMVD_MAX_REFINE_NUM);
+    memset(isGeoIntraChromaAvail, 0, sizeof(uint8_t) * GEO_NUM_INTRA_RDO_BUFFER);
+#else
   Distortion sadIntraWholeBlk[GEO_MAX_ALL_INTER_UNI_CANDS];
   uint8_t isGeoChromaAvail[GEO_MAX_ALL_INTER_UNI_CANDS];
   uint8_t isGeoMMVDChromaAvail[GEO_MAX_ALL_INTER_UNI_CANDS][GPM_EXT_MMVD_MAX_REFINE_NUM];
@@ -9141,6 +9170,7 @@ void EncCu::xCheckRDCostMergeGeoComb2Nx2N(CodingStructure *&tempCS, CodingStruct
   memset(isGeoChromaAvail, 0, sizeof(uint8_t) * GEO_MAX_ALL_INTER_UNI_CANDS);
   memset(isGeoMMVDChromaAvail, 0, sizeof(uint8_t) * GEO_MAX_ALL_INTER_UNI_CANDS* GPM_EXT_MMVD_MAX_REFINE_NUM);
   memset(isGeoIntraChromaAvail, 0, sizeof(uint8_t) * GEO_NUM_INTRA_RDO_BUFFER);
+#endif
 #else
   bool isGeoChromaAvail[GEO_MAX_ALL_INTER_UNI_CANDS];
   bool isGeoMMVDChromaAvail[GEO_MAX_ALL_INTER_UNI_CANDS][GPM_EXT_MMVD_MAX_REFINE_NUM];
@@ -9431,10 +9461,18 @@ void EncCu::xCheckRDCostMergeGeoComb2Nx2N(CodingStructure *&tempCS, CodingStruct
       for (int intraIdx = 0; intraIdx < GEO_MAX_NUM_INTRA_CANDS; intraIdx++)
       {
         uint8_t intraPred = geoIntraMPMList[splitDir][partIdx][intraIdx];
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+        if (intraRDOBufIdx[intraPred] >= GEO_NUM_INTRA_RDO_BUFFER - GEO_MAX_NUM_IBC_CANDS)
+#else
         if (intraRDOBufIdx[intraPred] >= GEO_NUM_INTRA_RDO_BUFFER)
+#endif
         {
           uint8_t intraCand = intraRDOBufCnt++;
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+          CHECK(intraCand >= GEO_NUM_INTRA_RDO_BUFFER - GEO_MAX_NUM_IBC_CANDS, "Geo Intra buffer overflow");
+#else
           CHECK(intraCand >= GEO_NUM_INTRA_RDO_BUFFER, "Geo Intra buffer overflow");
+#endif
           intraRDOBufIdx[intraPred] = intraCand;
           pu.intraDir[0] = intraPred;
 
@@ -9528,6 +9566,97 @@ void EncCu::xCheckRDCostMergeGeoComb2Nx2N(CodingStructure *&tempCS, CodingStruct
   pu.geoBldIdx = 0;
 #endif
 
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+  auto isFracBv = [&tempCS](Mv mv)
+  {
+    return tempCS->sps->getIBCFracFlag()
+        && ((mv.hor & ((1 << MV_FRACTIONAL_BITS_INTERNAL) - 1)) != 0 || (mv.ver & ((1 << MV_FRACTIONAL_BITS_INTERNAL) - 1)) != 0);
+  };
+  Mv geoBvList[GEO_MAX_NUM_IBC_CANDS];
+  bool skipIbcCand[GEO_MAX_NUM_IBC_CANDS] = {false};
+  if (testGeoInterIbc)
+  {
+    MergeCtx geoBvMrgCtx;
+    PU::getIBCMergeCandidates(pu, geoBvMrgCtx);
+    memset(geoBvMrgCtx.ibcLicFlags, false, sizeof(bool) * geoBvMrgCtx.numValidMergeCand);
+    memset(geoBvMrgCtx.ibcFilterFlags, false, sizeof(bool) * geoBvMrgCtx.numValidMergeCand);
+    memset(geoBvMrgCtx.rribcFlipTypes, 0, sizeof(int) * geoBvMrgCtx.numValidMergeCand);
+#if JVET_AE0174_NONINTER_TM_TOOLS_CONTROL
+    if (pu.cs->sps->getUseAML() && pu.cs->sps->getTMnoninterToolsEnableFlag())
+#else
+    if (pu.cs->sps->getUseAML())
+#endif
+    {
+      PredictionUnit puSaved = pu;
+      puSaved.cu->predMode = MODE_IBC;
+      m_pcInterSearch->adjustIBCMergeCandidates(puSaved, geoBvMrgCtx, 0, geoBvMrgCtx.numValidMergeCand);
+      m_pcInterSearch->setFillCurTplAboveARMC(false);
+      m_pcInterSearch->setFillCurTplLeftARMC(false);
+      puSaved.cu->predMode = MODE_INTER;
+    }
+    for (int ibcIdx = 0; ibcIdx < GEO_MAX_NUM_IBC_CANDS; ibcIdx++)
+    {
+      geoBvList[ibcIdx] = geoBvMrgCtx.mvFieldNeighbours[ibcIdx << 1].mv;
+      Mv bv = geoBvList[ibcIdx];
+      if (bv == Mv(0, 0))
+      {
+        skipIbcCand[ibcIdx] = true;
+        continue;
+      }
+#if JVET_AD0208_IBC_ADAPT_FOR_CAM_CAPTURED_CONTENTS
+      bool foundFracBV = isFracBv(bv);
+      int ibcRdoBuf = GEO_NUM_INTRA_RDO_BUFFER - GEO_MAX_NUM_IBC_CANDS + ibcIdx;
+#if JVET_AG0164_AFFINE_GPM
+      geoIntraBuffer[ibcRdoBuf] = m_acMergeBuffer[GEO_MAX_ALL_INTER_UNI_CANDS + ibcRdoBuf].getBuf(localUnitArea);
+#else
+      geoIntraBuffer[ibcRdoBuf] = m_acMergeBuffer[GEO_MAX_NUM_UNI_CANDS + ibcRdoBuf].getBuf(localUnitArea);
+#endif
+      if (foundFracBV)
+      {
+        pu.cu->predMode = MODE_IBC;
+        pu.mv[0] = bv;
+        pu.bv = pu.mv[REF_PIC_LIST_0];
+        pu.bv.changePrecision(MV_PRECISION_INTERNAL, MV_PRECISION_INT);
+        m_pcInterSearch->getPredIBCBlk(pu, COMPONENT_Y, pu.cu->slice->getPic(), pu.mv[0], geoIntraBuffer[ibcRdoBuf], false, false);
+        pu.cu->predMode = MODE_INTER;
+      }
+      else
+#endif
+      {
+        pu.bv = bv;
+        pu.bv.changePrecision(MV_PRECISION_INTERNAL, MV_PRECISION_INT);
+        int xPred = pu.bv.getHor();
+        int yPred = pu.bv.getVer();
+        Picture* refPic = pu.cu->slice->getPic();
+        const CPelBuf refBuf = refPic->getRecoBuf(pu.blocks[COMPONENT_Y]);
+        const Pel* piRefSrch = refBuf.buf;
+        int refStride = refBuf.stride;
+        Pel* piPred = geoIntraBuffer[ibcRdoBuf].Y().buf;
+        int predStride = geoIntraBuffer[ibcRdoBuf].Y().stride;
+        int height = geoIntraBuffer[ibcRdoBuf].Y().height;
+        int width = geoIntraBuffer[ibcRdoBuf].Y().width;
+        for (int h = 0; h < height; h++)
+        {
+          memcpy(piPred, piRefSrch + h * refStride + refStride * yPred + xPred, width * sizeof(Pel));
+          piPred += predStride;
+        }
+      }
+      if (pu.cs->slice->getLmcsEnabledFlag() && m_pcReshape->getCTUFlag())
+      {
+        geoIntraTempBuf[ibcRdoBuf] = m_acGeoMMVDTmpBuffer[0][ibcRdoBuf].getBuf(localUnitArea);
+        geoIntraTempBuf[ibcRdoBuf].Y().rspSignal(geoIntraBuffer[ibcRdoBuf].Y(), m_pcReshape->getInvLUT());
+      }
+      else
+      {
+        geoIntraTempBuf[ibcRdoBuf] = geoIntraBuffer[ibcRdoBuf];
+      }
+      distParamWholeBlk.cur.buf = geoIntraTempBuf[ibcRdoBuf].Y().buf;
+      distParamWholeBlk.cur.stride = geoIntraTempBuf[ibcRdoBuf].Y().stride;
+      sadIntraWholeBlk[ibcRdoBuf] = distParamWholeBlk.distFunc(distParamWholeBlk);
+    }
+  }
+#endif
+
   int wIdx = floorLog2(cu.lwidth()) - GEO_MIN_CU_LOG2;
   int hIdx = floorLog2(cu.lheight()) - GEO_MIN_CU_LOG2;
   Distortion sadSmall = 0, sadLarge = 0;
@@ -9542,10 +9671,17 @@ void EncCu::xCheckRDCostMergeGeoComb2Nx2N(CodingStructure *&tempCS, CodingStruct
   static_vector<double, 5> sadCostList0[GEO_NUM_PARTITION_MODE];
   static_vector<double, 5> sadCostList1[GEO_NUM_PARTITION_MODE];
 #if JVET_Y0065_GPM_INTRA
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+  static_vector<int, GEO_MAX_NUM_INTRA_CANDS + GEO_MAX_NUM_IBC_CANDS> intraCandList0[GEO_NUM_PARTITION_MODE];
+  static_vector<int, GEO_MAX_NUM_INTRA_CANDS + GEO_MAX_NUM_IBC_CANDS> intraCandList1[GEO_NUM_PARTITION_MODE];
+  static_vector<double, GEO_MAX_NUM_INTRA_CANDS + GEO_MAX_NUM_IBC_CANDS> intraSadCostList0[GEO_NUM_PARTITION_MODE];
+  static_vector<double, GEO_MAX_NUM_INTRA_CANDS + GEO_MAX_NUM_IBC_CANDS> intraSadCostList1[GEO_NUM_PARTITION_MODE];
+#else
   static_vector<int, GEO_MAX_NUM_INTRA_CANDS> intraCandList0[GEO_NUM_PARTITION_MODE];
   static_vector<int, GEO_MAX_NUM_INTRA_CANDS> intraCandList1[GEO_NUM_PARTITION_MODE];
   static_vector<double, GEO_MAX_NUM_INTRA_CANDS> intraSadCostList0[GEO_NUM_PARTITION_MODE];
   static_vector<double, GEO_MAX_NUM_INTRA_CANDS> intraSadCostList1[GEO_NUM_PARTITION_MODE];
+#endif
 #endif
 
   for (int splitDir = 0; splitDir < GEO_NUM_PARTITION_MODE; splitDir++)
@@ -9574,12 +9710,26 @@ void EncCu::xCheckRDCostMergeGeoComb2Nx2N(CodingStructure *&tempCS, CodingStruct
     }
 
 #if JVET_AG0164_AFFINE_GPM
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+    for (uint8_t mergeCand = 0; mergeCand < GEO_MAX_ALL_INTER_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS + GEO_MAX_NUM_IBC_CANDS; mergeCand++)
+#else
     for (uint8_t mergeCand = 0; mergeCand < GEO_MAX_ALL_INTER_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS; mergeCand++)
+#endif
     {
       if ((mergeCand < maxNumMergeCandidates && mrgDuplicated[mergeCand]) || (mergeCand >= maxNumMergeCandidates && mergeCand < GEO_MAX_ALL_INTER_UNI_CANDS))
       {
         continue;
       }
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+      if (!testGeoInterIbc && mergeCand >= GEO_MAX_ALL_INTER_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS)
+      {
+        continue;
+      }
+      if (mergeCand >= GEO_MAX_ALL_INTER_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS && skipIbcCand[mergeCand - GEO_MAX_ALL_INTER_UNI_CANDS - GEO_MAX_NUM_INTRA_CANDS])
+      {
+        continue;
+      }
+#endif
       double tempCost;
 
       int isAffine = (mergeCand < numRegularGpmMergeCand || mergeCand >= GEO_MAX_ALL_INTER_UNI_CANDS) ? 0 : 1;
@@ -9588,7 +9738,11 @@ void EncCu::xCheckRDCostMergeGeoComb2Nx2N(CodingStructure *&tempCS, CodingStruct
       { 
 #else
 #if JVET_Y0065_GPM_INTRA
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+    for (uint8_t mergeCand = 0; mergeCand < GEO_MAX_NUM_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS + GEO_MAX_NUM_IBC_CANDS; mergeCand++)
+#else
     for (uint8_t mergeCand = 0; mergeCand < GEO_MAX_NUM_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS; mergeCand++)
+#endif
 #else
     for (uint8_t mergeCand = 0; mergeCand < maxNumMergeCandidates; mergeCand++)
 #endif
@@ -9601,6 +9755,16 @@ void EncCu::xCheckRDCostMergeGeoComb2Nx2N(CodingStructure *&tempCS, CodingStruct
       {
         continue;
       }
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+      if (!testGeoIbc && mergeCand >= GEO_MAX_NUM_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS)
+      {
+        continue;
+      }
+      if (mergeCand >= GEO_MAX_NUM_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS && skipIbcCand[mergeCand - GEO_MAX_NUM_UNI_CANDS - GEO_MAX_NUM_INTRA_CANDS])
+      {
+        continue;
+      }
+#endif
 #if JVET_Y0065_GPM_INTRA
       double tempCost;
       if (mergeCand < GEO_MAX_NUM_UNI_CANDS)
@@ -9655,7 +9819,17 @@ void EncCu::xCheckRDCostMergeGeoComb2Nx2N(CodingStructure *&tempCS, CodingStruct
 #else
         int intraIdx = mergeCand - GEO_MAX_NUM_UNI_CANDS;
 #endif
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+#if JVET_AG0164_AFFINE_GPM
+        bool isIbc = (mergeCand >= GEO_MAX_ALL_INTER_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS);
+        int rdobuffer = isIbc ? (GEO_NUM_INTRA_RDO_BUFFER - GEO_MAX_NUM_IBC_CANDS) + mergeCand - GEO_MAX_ALL_INTER_UNI_CANDS - GEO_MAX_NUM_INTRA_CANDS : intraRDOBufIdx[geoIntraMPMList[splitDir][0][intraIdx]];
+#else
+        bool isIbc = (mergeCand >= GEO_MAX_NUM_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS);
+        int rdobuffer = isIbc ? (GEO_NUM_INTRA_RDO_BUFFER - GEO_MAX_NUM_IBC_CANDS) + mergeCand - GEO_MAX_NUM_UNI_CANDS - GEO_MAX_NUM_INTRA_CANDS : intraRDOBufIdx[geoIntraMPMList[splitDir][0][intraIdx]];
+#endif
+#else
         int rdobuffer = intraRDOBufIdx[geoIntraMPMList[splitDir][0][intraIdx]];
+#endif
         m_pcRdCost->setDistParam(distParam, tempCS->getOrgBuf().Y(), geoIntraTempBuf[rdobuffer].Y().buf, geoIntraTempBuf[rdobuffer].Y().stride, sadMask, maskStride, stepX, maskStride2, sps.getBitDepth(CHANNEL_TYPE_LUMA), COMPONENT_Y);
         sadLarge = distParam.distFunc(distParam);
         tempCost = (double)sadLarge + geoIntraIdxCost[intraIdx] + geoIntraFlag0Cost[1] + geoMMVDFlagCost[0];
@@ -9665,7 +9839,11 @@ void EncCu::xCheckRDCostMergeGeoComb2Nx2N(CodingStructure *&tempCS, CodingStruct
         m_geoMMVDCostList.insert(splitDir, 0, mergeCand, 0, tempCost);
         sortIntraCandList(tempCost, mergeCand, intraSadCostList0[splitDir], intraCandList0[splitDir]);
 
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+        if (!isIbc && geoIntraMPMList[splitDir][0][intraIdx] != geoIntraMPMList[splitDir][1][intraIdx])
+#else
         if (geoIntraMPMList[splitDir][0][intraIdx] != geoIntraMPMList[splitDir][1][intraIdx])
+#endif
         {
           rdobuffer = intraRDOBufIdx[geoIntraMPMList[splitDir][1][intraIdx]];
           m_pcRdCost->setDistParam(distParam, tempCS->getOrgBuf().Y(), geoIntraTempBuf[rdobuffer].Y().buf, geoIntraTempBuf[rdobuffer].Y().stride, sadMask, maskStride, stepX, maskStride2, sps.getBitDepth(CHANNEL_TYPE_LUMA), COMPONENT_Y);
@@ -9753,6 +9931,9 @@ void EncCu::xCheckRDCostMergeGeoComb2Nx2N(CodingStructure *&tempCS, CodingStruct
 #endif
 #if JVET_Y0065_GPM_INTRA
                                                       , m_pcIntraSearch
+#endif
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+                                                      , geoBvList
 #endif
           );
           geoSyntaxMode = m_pcInterSearch->convertGeoSplitModeToSyntax(splitDir, mergeCand0, mergeCand1);
@@ -9848,14 +10029,34 @@ void EncCu::xCheckRDCostMergeGeoComb2Nx2N(CodingStructure *&tempCS, CodingStruct
       {
         CHECK(!affGPMValid, "AffGPMFlag should be false");
       }
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+      int isIbc0 = (mergeCand0 >= GEO_MAX_ALL_INTER_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS) ? 1 : 0;
+      int isIbc1 = (mergeCand1 >= GEO_MAX_ALL_INTER_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS) ? 1 : 0;
+#endif
 #else
       int isIntra0 = (mergeCand0 >= GEO_MAX_NUM_UNI_CANDS) ? 1 : 0;
       int isIntra1 = (mergeCand1 >= GEO_MAX_NUM_UNI_CANDS) ? 1 : 0;
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+      int isIbc0 = (mergeCand0 >= GEO_MAX_NUM_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS) ? 1 : 0;
+      int isIbc1 = (mergeCand1 >= GEO_MAX_NUM_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS) ? 1 : 0;
+#endif
 #endif
       int candidateSAD = candidateIdx * GEO_BLENDING_NUM + bldIdx;
       if (isIntra0 || isIntra1)
       {
         PelUnitBuf predSrc0, predSrc1;
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+        if (isIbc0)
+        {
+#if JVET_AG0164_AFFINE_GPM
+          int rdobuffer = (GEO_NUM_INTRA_RDO_BUFFER - GEO_MAX_NUM_IBC_CANDS) + mergeCand0 - GEO_MAX_ALL_INTER_UNI_CANDS - GEO_MAX_NUM_INTRA_CANDS;
+#else
+          int rdobuffer = (GEO_NUM_INTRA_RDO_BUFFER - GEO_MAX_NUM_IBC_CANDS) + mergeCand0 - GEO_MAX_NUM_UNI_CANDS - GEO_MAX_NUM_INTRA_CANDS;
+#endif
+          predSrc0 = geoIntraBuffer[rdobuffer];
+        }
+        else
+#endif
         if (isIntra0)
         {
 #if JVET_AG0164_AFFINE_GPM
@@ -9870,6 +10071,18 @@ void EncCu::xCheckRDCostMergeGeoComb2Nx2N(CodingStructure *&tempCS, CodingStruct
         {
           predSrc0 = geoTempBuf[mergeCand0];
         }
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+        if (isIbc1)
+        {
+#if JVET_AG0164_AFFINE_GPM
+          int rdobuffer = (GEO_NUM_INTRA_RDO_BUFFER - GEO_MAX_NUM_IBC_CANDS) + mergeCand1 - GEO_MAX_ALL_INTER_UNI_CANDS - GEO_MAX_NUM_INTRA_CANDS;
+#else
+          int rdobuffer = (GEO_NUM_INTRA_RDO_BUFFER - GEO_MAX_NUM_IBC_CANDS) + mergeCand1 - GEO_MAX_NUM_UNI_CANDS - GEO_MAX_NUM_INTRA_CANDS;
+#endif
+          predSrc1 = geoIntraBuffer[rdobuffer];
+        }
+        else
+#endif
         if (isIntra1)
         {
 #if JVET_AG0164_AFFINE_GPM
@@ -9933,7 +10146,7 @@ void EncCu::xCheckRDCostMergeGeoComb2Nx2N(CodingStructure *&tempCS, CodingStruct
       int intraIdx1 = mergeCand1 - GEO_MAX_ALL_INTER_UNI_CANDS;
       if (!isAffine0)
       {
-        updateCost+=  geoIntraFlag0Cost[isIntra0];        
+        updateCost+=  geoIntraFlag0Cost[isIntra0];
       }
       int mergeIdx0 = mergeCand0 - isAffine0 * numRegularGpmMergeCand;
       int mergeIdx1 = mergeCand1 - isAffine1 * numRegularGpmMergeCand;
@@ -10168,10 +10381,43 @@ void EncCu::xCheckRDCostMergeGeoComb2Nx2N(CodingStructure *&tempCS, CodingStruct
     int isIntra1 = (mergeCand1 >= GEO_MAX_NUM_UNI_CANDS) ? 1 : 0;
 #endif
 
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+#if JVET_AG0164_AFFINE_GPM
+    int isIbc0 = (mergeCand0 >= GEO_MAX_ALL_INTER_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS) ? 1 : 0;
+    int isIbc1 = (mergeCand1 >= GEO_MAX_ALL_INTER_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS) ? 1 : 0;
+#else
+    int isIbc0 = (mergeCand0 >= GEO_MAX_NUM_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS) ? 1 : 0;
+    int isIbc1 = (mergeCand1 >= GEO_MAX_NUM_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS) ? 1 : 0;
+#endif
+#endif
     if (!isChromaEnabled(pu.chromaFormat) && !isIntra0 && !isIntra1)
     {
       continue;
     }
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+    if (isIbc0)
+    {
+#if JVET_AG0164_AFFINE_GPM
+      int ibcIdx = mergeCand0 - GEO_MAX_ALL_INTER_UNI_CANDS - GEO_MAX_NUM_INTRA_CANDS;
+#else
+      int ibcIdx = mergeCand0 - GEO_MAX_NUM_UNI_CANDS - GEO_MAX_NUM_INTRA_CANDS;
+#endif
+      int rdoBuffer = (GEO_NUM_INTRA_RDO_BUFFER - GEO_MAX_NUM_IBC_CANDS) + ibcIdx;
+      Mv bv = geoBvList[ibcIdx];
+      if (isChromaEnabled(pu.chromaFormat) && !isGeoIntraChromaAvail[rdoBuffer])
+      {
+        pu.cu->predMode = MODE_IBC;
+        pu.mv[0] = bv;
+        pu.bv = pu.mv[REF_PIC_LIST_0];
+        pu.bv.changePrecision(MV_PRECISION_INTERNAL, MV_PRECISION_INT); // used for only integer resolution
+        m_pcInterSearch->motionCompensation(pu, geoIntraBuffer[rdoBuffer], REF_PIC_LIST_0, false, true);
+        pu.cu->predMode = MODE_INTER;
+        isGeoIntraChromaAvail[rdoBuffer] = 2;
+      }
+      predSrc0 = geoIntraBuffer[rdoBuffer];
+    }
+    else
+#endif
     if (isIntra0)
     {
 #if JVET_AG0164_AFFINE_GPM
@@ -10253,6 +10499,30 @@ void EncCu::xCheckRDCostMergeGeoComb2Nx2N(CodingStructure *&tempCS, CodingStruct
       predSrc0 = geoTempBuf[mergeCand0];
     }
 
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+    if (isIbc1)
+    {
+#if JVET_AG0164_AFFINE_GPM
+      int ibcIdx = mergeCand1 - GEO_MAX_ALL_INTER_UNI_CANDS - GEO_MAX_NUM_INTRA_CANDS;
+#else
+      int ibcIdx = mergeCand1 - GEO_MAX_NUM_UNI_CANDS - GEO_MAX_NUM_INTRA_CANDS;
+#endif
+      int rdoBuffer = (GEO_NUM_INTRA_RDO_BUFFER - GEO_MAX_NUM_IBC_CANDS) + ibcIdx;
+      Mv bv = geoBvList[ibcIdx];
+      if (isChromaEnabled(pu.chromaFormat) && !isGeoIntraChromaAvail[rdoBuffer])
+      {
+        pu.cu->predMode = MODE_IBC;
+        pu.mv[0] = bv;
+        pu.bv = pu.mv[REF_PIC_LIST_0];
+        pu.bv.changePrecision(MV_PRECISION_INTERNAL, MV_PRECISION_INT); // used for only integer resolution
+        m_pcInterSearch->motionCompensation(pu, geoIntraBuffer[rdoBuffer], REF_PIC_LIST_0, false, true);
+        pu.cu->predMode = MODE_INTER;
+        isGeoIntraChromaAvail[rdoBuffer] = 2;
+      }
+      predSrc1 = geoIntraBuffer[rdoBuffer];
+    }
+    else
+#endif
     if (isIntra1)
     {
 #if JVET_AG0164_AFFINE_GPM
@@ -10621,6 +10891,9 @@ void EncCu::xCheckRDCostMergeGeoComb2Nx2N(CodingStructure *&tempCS, CodingStruct
 #if JVET_Y0065_GPM_INTRA
 #if JVET_AG0164_AFFINE_GPM
       pu.gpmIntraFlag = pu.geoMergeIdx0 >= GEO_MAX_ALL_INTER_UNI_CANDS || pu.geoMergeIdx1 >= GEO_MAX_ALL_INTER_UNI_CANDS;
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+      pu.gpmInterIbcFlag = pu.geoMergeIdx0 >= GEO_MAX_ALL_INTER_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS || pu.geoMergeIdx1 >= GEO_MAX_ALL_INTER_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS;
+#endif
       if (pu.geoMergeIdx0 >= GEO_MAX_ALL_INTER_UNI_CANDS)
       {
         memcpy(m_pcIntraSearch->m_intraMPM, geoIntraMPMList[pu.geoSplitDir][0], sizeof(uint8_t)*GEO_MAX_NUM_INTRA_CANDS);
@@ -10631,6 +10904,9 @@ void EncCu::xCheckRDCostMergeGeoComb2Nx2N(CodingStructure *&tempCS, CodingStruct
       }
 #else
       pu.gpmIntraFlag = pu.geoMergeIdx0 >= GEO_MAX_NUM_UNI_CANDS || pu.geoMergeIdx1 >= GEO_MAX_NUM_UNI_CANDS;
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+      pu.gpmInterIbcFlag = pu.geoMergeIdx0 >= GEO_MAX_NUM_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS || pu.geoMergeIdx1 >= GEO_MAX_NUM_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS;
+#endif
       if (pu.geoMergeIdx0 >= GEO_MAX_NUM_UNI_CANDS)
       {
         memcpy(m_pcIntraSearch->m_intraMPM, geoIntraMPMList[pu.geoSplitDir][0], sizeof(uint8_t)*GEO_MAX_NUM_INTRA_CANDS);
@@ -10731,6 +11007,9 @@ void EncCu::xCheckRDCostMergeGeoComb2Nx2N(CodingStructure *&tempCS, CodingStruct
         , affMergeCtx
 #endif
         , *mergeTmCtx0, *mergeTmCtx1, pu.geoSplitDir, pu.geoMergeIdx0, pu.geoMergeIdx1, pu.geoTmFlag0, pu.geoMMVDFlag0, pu.geoMMVDIdx0, pu.geoTmFlag1, pu.geoMMVDFlag1, pu.geoMMVDIdx1, pu.geoBldIdx, m_pcIntraSearch->m_intraMPM,
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+        geoBvList,
+#endif
         pu.gpmDmvrRefinePart0, pu.gpmDmvrRefinePart1, pu.gpmDmvrRefinePart0 ? m_mvBufEncBDOF4GPM[GEO_TM_OFF][pu.geoMergeIdx0] : nullptr, pu.gpmDmvrRefinePart1 ? m_mvBufEncBDOF4GPM[GEO_TM_OFF][pu.geoMergeIdx1] : nullptr);
 #else
       PU::spanGeoMMVDMotionInfo(pu, mergeCtx[GEO_TM_OFF], *mergeTmCtx0, *mergeTmCtx1, pu.geoSplitDir, pu.geoMergeIdx0, pu.geoMergeIdx1, pu.geoTmFlag0, pu.geoMMVDFlag0, pu.geoMMVDIdx0, pu.geoTmFlag1, pu.geoMMVDFlag1, pu.geoMMVDIdx1, pu.geoBldIdx, m_pcIntraSearch->m_intraMPM);
@@ -11165,6 +11444,9 @@ void EncCu::xCheckRDCostMergeGeoComb2Nx2N(CodingStructure *&tempCS, CodingStruct
 #endif
 #if JVET_Y0065_GPM_INTRA
                                                         , m_pcIntraSearch
+#endif
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+                                                        , geoBvList
 #endif
                                                         , mmvdCand0 - 1, mmvdCand1 - 1);
             geoSyntaxMode = m_pcInterSearch->convertGeoSplitModeToSyntax(splitDir, mergeCand0, mergeCand1, mmvdCand0 - 1, mmvdCand1 - 1);
@@ -11526,9 +11808,17 @@ void EncCu::xCheckRDCostMergeGeoComb2Nx2N(CodingStructure *&tempCS, CodingStruct
 #if JVET_AG0164_AFFINE_GPM
         int isIntra0 = (mergeCand0 >= GEO_MAX_ALL_INTER_UNI_CANDS) ? 1 : 0;
         int isIntra1 = (mergeCand1 >= GEO_MAX_ALL_INTER_UNI_CANDS) ? 1 : 0;
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+        int isIbc0 = (mergeCand0 >= GEO_MAX_ALL_INTER_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS) ? 1 : 0;
+        int isIbc1 = (mergeCand1 >= GEO_MAX_ALL_INTER_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS) ? 1 : 0;
+#endif
 #else
         int isIntra0 = (mergeCand0 >= GEO_MAX_NUM_UNI_CANDS) ? 1 : 0;
         int isIntra1 = (mergeCand1 >= GEO_MAX_NUM_UNI_CANDS) ? 1 : 0;
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+        int isIbc0 = (mergeCand0 >= GEO_MAX_NUM_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS) ? 1 : 0;
+        int isIbc1 = (mergeCand1 >= GEO_MAX_NUM_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS) ? 1 : 0;
+#endif
 #endif
         int candidateSAD = candidateIdx * GEO_BLENDING_NUM + bldIdx;
 #endif
@@ -11544,6 +11834,18 @@ void EncCu::xCheckRDCostMergeGeoComb2Nx2N(CodingStructure *&tempCS, CodingStruct
 #endif
         if (isIntra0 || isIntra1)
         {
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+          if (isIbc0)
+          {
+#if JVET_AG0164_AFFINE_GPM
+            int rdoBuffer = (GEO_NUM_INTRA_RDO_BUFFER - GEO_MAX_NUM_IBC_CANDS) + mergeCand0 - GEO_MAX_ALL_INTER_UNI_CANDS - GEO_MAX_NUM_INTRA_CANDS;
+#else
+            int rdoBuffer = (GEO_NUM_INTRA_RDO_BUFFER - GEO_MAX_NUM_IBC_CANDS) + mergeCand0 - GEO_MAX_NUM_UNI_CANDS - GEO_MAX_NUM_INTRA_CANDS;
+#endif
+            predSrc0 = geoIntraBuffer[rdoBuffer];
+          }
+          else
+#endif
           if (isIntra0)
           {
             int rdoBuffer = intraRDOBufIdx[geoIntraMPMList[splitDir][0][intraIdx0]];
@@ -11561,6 +11863,18 @@ void EncCu::xCheckRDCostMergeGeoComb2Nx2N(CodingStructure *&tempCS, CodingStruct
             predSrc0 = geoMMVDTempBuf[mergeCand0][mmvdCand0];
           }
 
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+          if (isIbc1)
+          {
+#if JVET_AG0164_AFFINE_GPM
+            int rdoBuffer = (GEO_NUM_INTRA_RDO_BUFFER - GEO_MAX_NUM_IBC_CANDS) + mergeCand1 - GEO_MAX_ALL_INTER_UNI_CANDS - GEO_MAX_NUM_INTRA_CANDS;
+#else
+            int rdoBuffer = (GEO_NUM_INTRA_RDO_BUFFER - GEO_MAX_NUM_IBC_CANDS) + mergeCand1 - GEO_MAX_NUM_UNI_CANDS - GEO_MAX_NUM_INTRA_CANDS;
+#endif
+            predSrc1 = geoIntraBuffer[rdoBuffer];
+          }
+          else
+#endif
           if (isIntra1)
           {
             int rdoBuffer = intraRDOBufIdx[geoIntraMPMList[splitDir][1][intraIdx1]];
@@ -11996,11 +12310,43 @@ void EncCu::xCheckRDCostMergeGeoComb2Nx2N(CodingStructure *&tempCS, CodingStruct
       int isIntra1 = (mergeCand1 >= GEO_MAX_NUM_UNI_CANDS) ? 1 : 0;
 #endif
 
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+#if JVET_AG0164_AFFINE_GPM
+      int isIbc0 = (mergeCand0 >= GEO_MAX_ALL_INTER_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS) ? 1 : 0;
+      int isIbc1 = (mergeCand1 >= GEO_MAX_ALL_INTER_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS) ? 1 : 0;
+#else
+      int isIbc0 = (mergeCand0 >= GEO_MAX_NUM_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS) ? 1 : 0;
+      int isIbc1 = (mergeCand1 >= GEO_MAX_NUM_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS) ? 1 : 0;
+#endif
+#endif
       if (!isChromaEnabled(pu.chromaFormat) && !isIntra0 && !isIntra1)
       {
         continue;
       }
       int mrgIntraCand0 = MAX_INT, mrgIntraCand1 = MAX_INT;
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+      if (isIbc0)
+      {
+#if JVET_AG0164_AFFINE_GPM
+        int ibcIdx = mergeCand0 - GEO_MAX_ALL_INTER_UNI_CANDS - GEO_MAX_NUM_INTRA_CANDS;
+#else
+        int ibcIdx = mergeCand0 - GEO_MAX_NUM_UNI_CANDS - GEO_MAX_NUM_INTRA_CANDS;
+#endif
+        int rdoBuffer = (GEO_NUM_INTRA_RDO_BUFFER - GEO_MAX_NUM_IBC_CANDS) + ibcIdx;
+        Mv bv = geoBvList[ibcIdx];
+        if (isChromaEnabled(pu.chromaFormat) && !isGeoIntraChromaAvail[rdoBuffer])
+        {
+          pu.cu->predMode = MODE_IBC;
+          pu.mv[0] = bv;
+          pu.bv = pu.mv[REF_PIC_LIST_0];
+          pu.bv.changePrecision(MV_PRECISION_INTERNAL, MV_PRECISION_INT); // used for only integer resolution
+          m_pcInterSearch->motionCompensation(pu, geoIntraBuffer[rdoBuffer], REF_PIC_LIST_0, false, true);
+          pu.cu->predMode = MODE_INTER;
+          isGeoIntraChromaAvail[rdoBuffer] = 2;
+        }
+      }
+      else
+#endif
       if (isIntra0)
       {
 #if JVET_AG0164_AFFINE_GPM
@@ -12133,6 +12479,29 @@ void EncCu::xCheckRDCostMergeGeoComb2Nx2N(CodingStructure *&tempCS, CodingStruct
         }
       }
 
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+      if (isIbc1)
+      {
+#if JVET_AG0164_AFFINE_GPM
+        int ibcIdx = mergeCand1 - GEO_MAX_ALL_INTER_UNI_CANDS - GEO_MAX_NUM_INTRA_CANDS;
+#else
+        int ibcIdx = mergeCand1 - GEO_MAX_NUM_UNI_CANDS - GEO_MAX_NUM_INTRA_CANDS;
+#endif
+        int rdoBuffer = (GEO_NUM_INTRA_RDO_BUFFER - GEO_MAX_NUM_IBC_CANDS) + ibcIdx;
+        Mv bv = geoBvList[ibcIdx];
+        if (isChromaEnabled(pu.chromaFormat) && !isGeoIntraChromaAvail[rdoBuffer])
+        {
+          pu.cu->predMode = MODE_IBC;
+          pu.mv[0] = bv;
+          pu.bv = pu.mv[REF_PIC_LIST_0];
+          pu.bv.changePrecision(MV_PRECISION_INTERNAL, MV_PRECISION_INT); // used for only integer resolution
+          m_pcInterSearch->motionCompensation(pu, geoIntraBuffer[rdoBuffer], REF_PIC_LIST_0, false, true);
+          pu.cu->predMode = MODE_INTER;
+          isGeoIntraChromaAvail[rdoBuffer] = 2;
+        }
+      }
+      else
+#endif
       if (isIntra1)
       {
 #if JVET_AG0164_AFFINE_GPM
@@ -12276,6 +12645,18 @@ void EncCu::xCheckRDCostMergeGeoComb2Nx2N(CodingStructure *&tempCS, CodingStruct
       PelUnitBuf predSrcTemp0, predSrcTemp1;
       uint8_t* chromaAvailPtr0 = nullptr;
       uint8_t* chromaAvailPtr1 = nullptr;
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+      if (isIbc0)
+      {
+#if JVET_AG0164_AFFINE_GPM
+        int rdoBuffer = (GEO_NUM_INTRA_RDO_BUFFER - GEO_MAX_NUM_IBC_CANDS) + mergeCand0 - GEO_MAX_ALL_INTER_UNI_CANDS - GEO_MAX_NUM_INTRA_CANDS;
+#else
+        int rdoBuffer = (GEO_NUM_INTRA_RDO_BUFFER - GEO_MAX_NUM_IBC_CANDS) + mergeCand0 - GEO_MAX_NUM_UNI_CANDS - GEO_MAX_NUM_INTRA_CANDS;
+#endif
+        predSrcTemp0 = geoIntraBuffer[rdoBuffer];
+      }
+      else
+#endif
       if (isIntra0)
       {
         predSrcTemp0 = geoIntraBuffer[mrgIntraCand0];
@@ -12352,6 +12733,18 @@ void EncCu::xCheckRDCostMergeGeoComb2Nx2N(CodingStructure *&tempCS, CodingStruct
 #endif
       }
 
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+      if (isIbc1)
+      {
+#if JVET_AG0164_AFFINE_GPM
+        int rdoBuffer = (GEO_NUM_INTRA_RDO_BUFFER - GEO_MAX_NUM_IBC_CANDS) + mergeCand1 - GEO_MAX_ALL_INTER_UNI_CANDS - GEO_MAX_NUM_INTRA_CANDS;
+#else
+        int rdoBuffer = (GEO_NUM_INTRA_RDO_BUFFER - GEO_MAX_NUM_IBC_CANDS) + mergeCand1 - GEO_MAX_NUM_UNI_CANDS - GEO_MAX_NUM_INTRA_CANDS;
+#endif
+        predSrcTemp1 = geoIntraBuffer[rdoBuffer];
+      }
+      else
+#endif
       if (isIntra1)
       {
         predSrcTemp1 = geoIntraBuffer[mrgIntraCand1];
@@ -12858,6 +13251,9 @@ void EncCu::xCheckRDCostMergeGeoComb2Nx2N(CodingStructure *&tempCS, CodingStruct
 #if JVET_Y0065_GPM_INTRA
 #if JVET_AG0164_AFFINE_GPM
         pu.gpmIntraFlag = pu.geoMergeIdx0 >= GEO_MAX_ALL_INTER_UNI_CANDS || pu.geoMergeIdx1 >= GEO_MAX_ALL_INTER_UNI_CANDS;
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+        pu.gpmInterIbcFlag = pu.geoMergeIdx0 >= GEO_MAX_ALL_INTER_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS || pu.geoMergeIdx1 >= GEO_MAX_ALL_INTER_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS;
+#endif
         if (pu.geoMergeIdx0 >= GEO_MAX_ALL_INTER_UNI_CANDS)
         {
           memcpy(m_pcIntraSearch->m_intraMPM, geoIntraMPMList[pu.geoSplitDir][0], sizeof(uint8_t) * GEO_MAX_NUM_INTRA_CANDS);
@@ -12868,6 +13264,9 @@ void EncCu::xCheckRDCostMergeGeoComb2Nx2N(CodingStructure *&tempCS, CodingStruct
         }
 #else
         pu.gpmIntraFlag = pu.geoMergeIdx0 >= GEO_MAX_NUM_UNI_CANDS || pu.geoMergeIdx1 >= GEO_MAX_NUM_UNI_CANDS;
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+        pu.gpmInterIbcFlag = pu.geoMergeIdx0 >= GEO_MAX_NUM_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS || pu.geoMergeIdx1 >= GEO_MAX_NUM_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS;
+#endif
         if (pu.geoMergeIdx0 >= GEO_MAX_NUM_UNI_CANDS)
         {
           memcpy(m_pcIntraSearch->m_intraMPM, geoIntraMPMList[pu.geoSplitDir][0], sizeof(uint8_t)*GEO_MAX_NUM_INTRA_CANDS);
@@ -13090,6 +13489,9 @@ void EncCu::xCheckRDCostMergeGeoComb2Nx2N(CodingStructure *&tempCS, CodingStruct
           , affMergeCtx
 #endif
           , *mrgTmCtx0, *mrgTmCtx1, pu.geoSplitDir, pu.geoMergeIdx0, pu.geoMergeIdx1, pu.geoTmFlag0, pu.geoMMVDFlag0, pu.geoMMVDIdx0, pu.geoTmFlag1, pu.geoMMVDFlag1, pu.geoMMVDIdx1, pu.geoBldIdx, m_pcIntraSearch->m_intraMPM,
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+          geoBvList,
+#endif
           pu.gpmDmvrRefinePart0, pu.gpmDmvrRefinePart1, bdofSubPuMvOffsetPart0, bdofSubPuMvOffsetPart1);
 #else
         PU::spanGeoMMVDMotionInfo(pu, mergeCtx[GEO_TM_OFF], *mrgTmCtx0, *mrgTmCtx1, pu.geoSplitDir, pu.geoMergeIdx0, pu.geoMergeIdx1, pu.geoTmFlag0, pu.geoMMVDFlag0, pu.geoMMVDIdx0, pu.geoTmFlag1, pu.geoMMVDFlag1, pu.geoMMVDIdx1, pu.geoBldIdx, m_pcIntraSearch->m_intraMPM);
@@ -13306,11 +13708,21 @@ void EncCu::xCheckRDCostMergeGeoComb2Nx2N(CodingStructure *&tempCS, CodingStruct
 
       uint8_t intraMPM[2] = { PLANAR_IDX, PLANAR_IDX };
 #if JVET_AG0164_AFFINE_GPM
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+      PU::spanGeoMMVDMotionInfo(pu, mergeCtx[GEO_TM_OFF], affMergeCtx, mergeCtx[GEO_TM_OFF], mergeCtx[GEO_TM_OFF], 0, geoMergeIdx0, geoMergeIdx1, pu.geoTmFlag0, pu.geoMMVDFlag0, pu.geoMMVDIdx0, pu.geoTmFlag1, pu.geoMMVDFlag1, pu.geoMMVDIdx1, 0, intraMPM, geoBvList,
+        pu.gpmDmvrRefinePart0, pu.gpmDmvrRefinePart1, pu.gpmDmvrRefinePart0 ? m_mvBufEncBDOF4GPM[GEO_TM_OFF][geoMergeIdx0] : nullptr, pu.gpmDmvrRefinePart1 ? m_mvBufEncBDOF4GPM[GEO_TM_OFF][geoMergeIdx1] : nullptr);
+#else
       PU::spanGeoMMVDMotionInfo(pu, mergeCtx[GEO_TM_OFF], affMergeCtx, mergeCtx[GEO_TM_OFF], mergeCtx[GEO_TM_OFF], 0, geoMergeIdx0, geoMergeIdx1, pu.geoTmFlag0, pu.geoMMVDFlag0, pu.geoMMVDIdx0, pu.geoTmFlag1, pu.geoMMVDFlag1, pu.geoMMVDIdx1, 0, intraMPM,
         pu.gpmDmvrRefinePart0, pu.gpmDmvrRefinePart1, pu.gpmDmvrRefinePart0 ? m_mvBufEncBDOF4GPM[GEO_TM_OFF][geoMergeIdx0] : nullptr, pu.gpmDmvrRefinePart1 ? m_mvBufEncBDOF4GPM[GEO_TM_OFF][geoMergeIdx1] : nullptr);
+#endif
+#else
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+      PU::spanGeoMMVDMotionInfo(pu, mergeCtx[GEO_TM_OFF], mergeCtx[GEO_TM_OFF], mergeCtx[GEO_TM_OFF], 0, geoMergeIdx0, geoMergeIdx1, pu.geoTmFlag0, pu.geoMMVDFlag0, pu.geoMMVDIdx0, pu.geoTmFlag1, pu.geoMMVDFlag1, pu.geoMMVDIdx1, 0, intraMPM, geoBvList,
+        pu.gpmDmvrRefinePart0, pu.gpmDmvrRefinePart1, pu.gpmDmvrRefinePart0 ? m_mvBufEncBDOF4GPM[GEO_TM_OFF][geoMergeIdx0] : nullptr, pu.gpmDmvrRefinePart1 ? m_mvBufEncBDOF4GPM[GEO_TM_OFF][geoMergeIdx1] : nullptr);
 #else
       PU::spanGeoMMVDMotionInfo(pu, mergeCtx[GEO_TM_OFF], mergeCtx[GEO_TM_OFF], mergeCtx[GEO_TM_OFF], 0, geoMergeIdx0, geoMergeIdx1, pu.geoTmFlag0, pu.geoMMVDFlag0, pu.geoMMVDIdx0, pu.geoTmFlag1, pu.geoMMVDFlag1, pu.geoMMVDIdx1, 0, intraMPM,
         pu.gpmDmvrRefinePart0, pu.gpmDmvrRefinePart1, pu.gpmDmvrRefinePart0 ? m_mvBufEncBDOF4GPM[GEO_TM_OFF][geoMergeIdx0] : nullptr, pu.gpmDmvrRefinePart1 ? m_mvBufEncBDOF4GPM[GEO_TM_OFF][geoMergeIdx1] : nullptr);
+#endif
 #endif
 
       tempCS->getPredBuf().copyFrom(blendBuffer);
diff --git a/source/Lib/EncoderLib/EncCu.h b/source/Lib/EncoderLib/EncCu.h
index 2d69e526c..f2d3f81d5 100644
--- a/source/Lib/EncoderLib/EncCu.h
+++ b/source/Lib/EncoderLib/EncCu.h
@@ -190,15 +190,25 @@ public:
       for (int splitDir = 0; splitDir < GEO_NUM_PARTITION_MODE; splitDir++)
       {
 #if JVET_AG0164_AFFINE_GPM
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+        singleDistList[partIdx][splitDir] = new SingleGeoMMVDMergeEntry*[GEO_MAX_ALL_INTER_UNI_CANDS +GEO_MAX_NUM_INTRA_CANDS+GEO_MAX_NUM_IBC_CANDS];
+        for (int candIdx = 0; candIdx < GEO_MAX_ALL_INTER_UNI_CANDS +GEO_MAX_NUM_INTRA_CANDS+GEO_MAX_NUM_IBC_CANDS; candIdx++)
+#else
         singleDistList[partIdx][splitDir] = new SingleGeoMMVDMergeEntry*[GEO_MAX_ALL_INTER_UNI_CANDS +GEO_MAX_NUM_INTRA_CANDS];
         for (int candIdx = 0; candIdx < GEO_MAX_ALL_INTER_UNI_CANDS +GEO_MAX_NUM_INTRA_CANDS; candIdx++)
+#endif
         {
           singleDistList[partIdx][splitDir][candIdx] = new SingleGeoMMVDMergeEntry[GPM_EXT_MMVD_MAX_REFINE_NUM + 2];
         }
 #else
 #if JVET_Y0065_GPM_INTRA
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+        singleDistList[partIdx][splitDir] = new SingleGeoMMVDMergeEntry*[GEO_MAX_NUM_UNI_CANDS+GEO_MAX_NUM_INTRA_CANDS+GEO_MAX_NUM_IBC_CANDS];
+        for (int candIdx = 0; candIdx < GEO_MAX_NUM_UNI_CANDS+GEO_MAX_NUM_INTRA_CANDS+GEO_MAX_NUM_IBC_CANDS; candIdx++)
+#else
         singleDistList[partIdx][splitDir] = new SingleGeoMMVDMergeEntry*[GEO_MAX_NUM_UNI_CANDS+GEO_MAX_NUM_INTRA_CANDS];
         for (int candIdx = 0; candIdx < GEO_MAX_NUM_UNI_CANDS+GEO_MAX_NUM_INTRA_CANDS; candIdx++)
+#endif
 #else
         singleDistList[partIdx][splitDir] = new SingleGeoMMVDMergeEntry*[MRG_MAX_NUM_CANDS];
         for (int candIdx = 0; candIdx < MRG_MAX_NUM_CANDS; candIdx++)
@@ -221,13 +231,21 @@ public:
       for (int splitDir = 0; splitDir < GEO_NUM_PARTITION_MODE; splitDir++)
       {
 #if JVET_AG0164_AFFINE_GPM
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+        for (int candIdx = 0; candIdx < GEO_MAX_ALL_INTER_UNI_CANDS +GEO_MAX_NUM_INTRA_CANDS+GEO_MAX_NUM_IBC_CANDS; candIdx++)
+#else
         for (int candIdx = 0; candIdx < GEO_MAX_ALL_INTER_UNI_CANDS +GEO_MAX_NUM_INTRA_CANDS; candIdx++)
+#endif
         {
           delete[] singleDistList[partIdx][splitDir][candIdx];
         }
 #else
 #if JVET_Y0065_GPM_INTRA
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+        for (int candIdx = 0; candIdx < GEO_MAX_NUM_UNI_CANDS+GEO_MAX_NUM_INTRA_CANDS+GEO_MAX_NUM_IBC_CANDS; candIdx++)
+#else
         for (int candIdx = 0; candIdx < GEO_MAX_NUM_UNI_CANDS+GEO_MAX_NUM_INTRA_CANDS; candIdx++)
+#endif
 #else
         for (int candIdx = 0; candIdx < MRG_MAX_NUM_CANDS; candIdx++)
 #endif
diff --git a/source/Lib/EncoderLib/EncLib.cpp b/source/Lib/EncoderLib/EncLib.cpp
index bd04d79e6..edc1fa640 100644
--- a/source/Lib/EncoderLib/EncLib.cpp
+++ b/source/Lib/EncoderLib/EncLib.cpp
@@ -2038,6 +2038,9 @@ void EncLib::xInitSPS( SPS& sps )
   sps.setUseGeo                ( m_Geo );
 #if JVET_AG0112_REGRESSION_BASED_GPM_BLENDING
   sps.setUseGeoBlend           ( true );
+#endif
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+  sps.setUseGeoInterIbc        ( m_Geo ? m_geoInterIbc : false );
 #endif
   sps.setUseMMVD               ( m_MMVD );
   sps.setFpelMmvdEnabledFlag   (( m_MMVD ) ? m_allowDisFracMMVD : false);
diff --git a/source/Lib/EncoderLib/EncSlice.cpp b/source/Lib/EncoderLib/EncSlice.cpp
index dd89a2531..04372afdb 100644
--- a/source/Lib/EncoderLib/EncSlice.cpp
+++ b/source/Lib/EncoderLib/EncSlice.cpp
@@ -2111,6 +2111,18 @@ void EncSlice::encodeCtus( Picture* pcPic, const bool bCompressEntireSlice, cons
     }
   }
 #endif
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+  if (m_pcCuEncoder->getEncCfg()->getUseGeoInterIbc())
+  {
+    SPS* spsTmp = const_cast<SPS*>(cs.sps);
+    hashBlkHitPerc = (hashBlkHitPerc == -1) ? m_pcCuEncoder->getIbcHashMap().calHashBlkMatchPerc(cs.area.Y()) : hashBlkHitPerc;
+    bool isSCC = hashBlkHitPerc >= 20;
+    if (cs.slice->getPOC() == 0 || cs.slice->getSliceType() == I_SLICE)
+    {
+      spsTmp->setUseGeoInterIbc(isSCC);
+    }
+  }
+#endif
 #if JVET_AD0188_CCP_MERGE
   if ((pCfg->getSwitchPOC() != pcPic->poc || -1 == pCfg->getDebugCTU()))
   {
diff --git a/source/Lib/EncoderLib/InterSearch.cpp b/source/Lib/EncoderLib/InterSearch.cpp
index 904938f7a..715df8738 100644
--- a/source/Lib/EncoderLib/InterSearch.cpp
+++ b/source/Lib/EncoderLib/InterSearch.cpp
@@ -8279,12 +8279,20 @@ void InterSearch::setGeoSplitModeToSyntaxTable(PredictionUnit& pu, MergeCtx& mer
 #endif
 #if JVET_Y0065_GPM_INTRA
                                              , IntraPrediction* pcIntraPred
+#endif
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+                                             , Mv* geoBvList
 #endif
                                              , int mmvdCand0, int mmvdCand1)
 {
 #if JVET_Y0065_GPM_INTRA
   bool isIntra[2];
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+  bool isIbc[2];
+  xRemapMrgIndexAndMmvdIdx(mergeCand0, mergeCand1, mmvdCand0, mmvdCand1, isIntra[0], isIntra[1], isIbc[0], isIbc[1]);
+#else
   xRemapMrgIndexAndMmvdIdx(mergeCand0, mergeCand1, mmvdCand0, mmvdCand1, isIntra[0], isIntra[1]);
+#endif
 #endif
   const int idx0 = mmvdCand0 + 1;
   const int idx1 = mmvdCand1 + 1;
@@ -8299,6 +8307,9 @@ void InterSearch::setGeoSplitModeToSyntaxTable(PredictionUnit& pu, MergeCtx& mer
 #endif
 #if JVET_Y0065_GPM_INTRA
                       , pcIntraPred
+#endif
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+                      , geoBvList
 #endif
                       , m_gpmPartTplCost[idx0][mergeCand0]
                       , m_gpmPartTplCost[idx1][mergeCand1]
@@ -8310,6 +8321,9 @@ void InterSearch::setGeoSplitModeToSyntaxTable(PredictionUnit& pu, MergeCtx& mer
 #else
                       + (isIntra[0] ? GEO_MAX_NUM_UNI_CANDS : 0)
 #endif
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+                      + (isIbc[0] ? GEO_MAX_NUM_INTRA_CANDS : 0)
+#endif
 #endif
                       , mergeCtx1
                       , mergeCand1
@@ -8319,6 +8333,9 @@ void InterSearch::setGeoSplitModeToSyntaxTable(PredictionUnit& pu, MergeCtx& mer
 #else
                       + (isIntra[1] ? GEO_MAX_NUM_UNI_CANDS : 0)
 #endif
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+                      + (isIbc[1] ? GEO_MAX_NUM_INTRA_CANDS : 0)
+#endif
 #endif
                       , numValidInList
                       , modeList
@@ -8363,7 +8380,12 @@ int InterSearch::convertGeoSplitModeToSyntax(int splitDir, int mergeCand0, int m
 {
 #if JVET_Y0065_GPM_INTRA
   bool isIntra[2];
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+  bool isIbc[2];
+  xRemapMrgIndexAndMmvdIdx(mergeCand0, mergeCand1, mmvdCand0, mmvdCand1, isIntra[0], isIntra[1], isIbc[0], isIbc[1]);
+#else
   xRemapMrgIndexAndMmvdIdx(mergeCand0, mergeCand1, mmvdCand0, mmvdCand1, isIntra[0], isIntra[1]);
+#endif
 #endif
   return m_gpmacsSplitModeTmSel[mmvdCand0 + 1][mmvdCand1 + 1][mergeCand0][mergeCand1][splitDir];
 }
@@ -8374,6 +8396,9 @@ bool InterSearch::selectGeoSplitModes(PredictionUnit &pu,
 #endif
 #if JVET_Y0065_GPM_INTRA
                                       IntraPrediction* pcIntraPred,
+#endif
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+                                      Mv* geoBvList,
 #endif
                                       uint32_t (&gpmTplCostPart0)[2][GEO_NUM_PARTITION_MODE],
                                       uint32_t (&gpmTplCostPart1)[2][GEO_NUM_PARTITION_MODE],
@@ -8405,6 +8430,9 @@ bool InterSearch::selectGeoSplitModes(PredictionUnit &pu,
   Pel* pRefLeftPart0   = m_acYuvRefAMLTemplatePart0[1];
   Pel* pRefTopPart1    = m_acYuvRefAMLTemplatePart1[0];
   Pel* pRefLeftPart1   = m_acYuvRefAMLTemplatePart1[1];
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+  std::vector<Pel>* lut = m_pcReshape->getSliceReshaperInfo().getUseSliceReshaper() && m_pcReshape->getCTUFlag() ? &m_pcReshape->getInvLUT() : nullptr;
+#endif
 
   // First partition
   if (fillRefTplPart0)
@@ -8414,6 +8442,9 @@ bool InterSearch::selectGeoSplitModes(PredictionUnit &pu,
 #else
     fillPartGPMRefTemplate<0, false>(pu, mergeCtx0, mergeCand0, mmvdCand0, pRefTopPart0, pRefLeftPart0);
 #endif
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+    fillPartGpmInterIbcRefTemplate<0, false>(pu, lut, geoBvList, mergeCand0, mmvdCand0, pRefTopPart0, pRefLeftPart0);
+#endif
 #if JVET_Y0065_GPM_INTRA
     xCollectIntraGeoPartCost<0>(pu, pcIntraPred, mergeCand0, gpmTplCostPart0[0]);
 #endif
@@ -8427,6 +8458,9 @@ bool InterSearch::selectGeoSplitModes(PredictionUnit &pu,
 #else
     fillPartGPMRefTemplate<1, false>(pu, mergeCtx1, mergeCand1, mmvdCand1, pRefTopPart1, pRefLeftPart1);
 #endif
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+    fillPartGpmInterIbcRefTemplate<1, false>(pu, lut, geoBvList, mergeCand1, mmvdCand1, pRefTopPart1, pRefLeftPart1);
+#endif
 #if JVET_Y0065_GPM_INTRA
     xCollectIntraGeoPartCost<1>(pu, pcIntraPred, mergeCand1, gpmTplCostPart1[1]);
 #endif
@@ -8773,6 +8807,16 @@ void InterSearch::xCollectIntraGeoPartCost(PredictionUnit &pu, IntraPrediction*
   {
     return;
   }
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+#if JVET_AG0164_AFFINE_GPM
+  if (mergeCand >= GEO_MAX_ALL_INTER_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS)
+#else
+  if (mergeCand >= GEO_MAX_NUM_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS)
+#endif
+  {
+    return;
+  }
+#endif
 
   std::vector<Pel>* LUT = m_pcReshape->getSliceReshaperInfo().getUseSliceReshaper() && m_pcReshape->getCTUFlag() ? &m_pcReshape->getInvLUT() : nullptr;
   pcIntraPred->fillIntraGPMRefTemplateAll(pu, m_bAMLTemplateAvailabe[0], m_bAMLTemplateAvailabe[1], true, false, false, LUT, (partIdx == 0 ? mergeCand : 0), (partIdx == 1 ? mergeCand : 0));
diff --git a/source/Lib/EncoderLib/InterSearch.h b/source/Lib/EncoderLib/InterSearch.h
index eb3e73afa..dcda0aac2 100644
--- a/source/Lib/EncoderLib/InterSearch.h
+++ b/source/Lib/EncoderLib/InterSearch.h
@@ -1359,6 +1359,9 @@ public:
 #endif
 #if JVET_Y0065_GPM_INTRA
                                   , IntraPrediction* pcIntraPred
+#endif
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+                                  , Mv* geoBvList
 #endif
                                   , int mmvdCand0 = -1, int mmvdCand1 = -1); // mmvdCandX = -1: regular, 0~GPM_EXT_MMVD_MAX_REFINE_NUM-1: MMVD, >=GPM_EXT_MMVD_MAX_REFINE_NUM: TM
 #if JVET_W0097_GPM_MMVD_TM && TM_MRG
@@ -1375,21 +1378,51 @@ public:
 
 protected:
 #if JVET_Y0065_GPM_INTRA
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+  inline void xRemapMrgIndexAndMmvdIdx(int& mergeCand0, int& mergeCand1, int& mmvdCand0, int& mmvdCand1, bool &isIntra0, bool &isIntra1, bool &isIbc0, bool &isIbc1)
+#else
   inline void xRemapMrgIndexAndMmvdIdx(int& mergeCand0, int& mergeCand1, int& mmvdCand0, int& mmvdCand1, bool &isIntra0, bool &isIntra1)
+#endif
   {
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+#if JVET_AG0164_AFFINE_GPM
+    bool isIbc = (mergeCand0 >= GEO_MAX_ALL_INTER_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS) || (mergeCand1 >= GEO_MAX_ALL_INTER_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS);
+#else
+    bool isIbc = (mergeCand0 >= GEO_MAX_NUM_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS) || (mergeCand1 >= GEO_MAX_NUM_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS);
+#endif
+#endif
 #if JVET_W0097_GPM_MMVD_TM
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+    const int intraMmvdBufIdx = (GPM_EXT_MMVD_MAX_REFINE_NUM + 1) + 1 + (isIbc ? 1 : 0);
+#else
     static const int intraMmvdBufIdx = (GPM_EXT_MMVD_MAX_REFINE_NUM + 1) + 1;
+#endif
+#else
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+    const int intraMmvdBufIdx = 1 + (isIbc ? 1 : 0);
 #else
     static const int intraMmvdBufIdx = 1;
+#endif
 #endif
 
     isIntra0 = false;
     isIntra1 = false;
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+    isIbc0 = false;
+    isIbc1 = false;
+#endif
 #if JVET_AG0164_AFFINE_GPM
     if (mergeCand0 >= GEO_MAX_ALL_INTER_UNI_CANDS)
     {
       isIntra0    = true;
       mergeCand0 -= GEO_MAX_ALL_INTER_UNI_CANDS;
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+      if (isIntra0 && mergeCand0 >= GEO_MAX_NUM_INTRA_CANDS)
+      {
+        isIbc0 = true;
+        mergeCand0 -= GEO_MAX_NUM_INTRA_CANDS;
+      }
+#endif
       mmvdCand0   = intraMmvdBufIdx - 1;
     }
 
@@ -1397,6 +1430,13 @@ protected:
     {
       isIntra1    = true;
       mergeCand1 -= GEO_MAX_ALL_INTER_UNI_CANDS;
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+      if (isIntra1 && mergeCand1 >= GEO_MAX_NUM_INTRA_CANDS)
+      {
+        isIbc1 = true;
+        mergeCand1 -= GEO_MAX_NUM_INTRA_CANDS;
+      }
+#endif
       mmvdCand1   = intraMmvdBufIdx - 1;
     }
 #else
@@ -1404,6 +1444,13 @@ protected:
     {
       isIntra0    = true;
       mergeCand0 -= GEO_MAX_NUM_UNI_CANDS;
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+      if (isIntra0 && mergeCand0 >= GEO_MAX_NUM_INTRA_CANDS)
+      {
+        isIbc0 = true;
+        mergeCand0 -= GEO_MAX_NUM_INTRA_CANDS;
+      }
+#endif
       mmvdCand0   = intraMmvdBufIdx - 1;
     }
 
@@ -1411,6 +1458,13 @@ protected:
     {
       isIntra1    = true;
       mergeCand1 -= GEO_MAX_NUM_UNI_CANDS;
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+      if (isIntra1 && mergeCand1 >= GEO_MAX_NUM_INTRA_CANDS)
+      {
+        isIbc1 = true;
+        mergeCand1 -= GEO_MAX_NUM_INTRA_CANDS;
+      }
+#endif
       mmvdCand1   = intraMmvdBufIdx - 1;
     }
 #endif
@@ -1437,6 +1491,9 @@ protected:
 #endif
 #if JVET_Y0065_GPM_INTRA
                             IntraPrediction* pcIntraPred,
+#endif
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+                            Mv* geoBvList,
 #endif
                             uint32_t (&gpmTplCostPart0)[2][GEO_NUM_PARTITION_MODE],
                             uint32_t (&gpmTplCostPart1)[2][GEO_NUM_PARTITION_MODE],
diff --git a/source/Lib/EncoderLib/VLCWriter.cpp b/source/Lib/EncoderLib/VLCWriter.cpp
index ded408a49..17f6d8dc9 100644
--- a/source/Lib/EncoderLib/VLCWriter.cpp
+++ b/source/Lib/EncoderLib/VLCWriter.cpp
@@ -1674,6 +1674,9 @@ void HLSWriter::codeSPS( const SPS* pcSPS )
       {
         WRITE_FLAG(pcSPS->getUseGPMTMMode() ? 1 : 0, "sps_gpm_tm_enabled_flag");
       }
+#endif
+#if JVET_AI0082_GPM_WITH_INTER_IBC
+      WRITE_FLAG(pcSPS->getUseGeoInterIbc() ? 1 : 0, "sps_gpm_inter_ibc_enabled_flag");
 #endif
     }
 #if JVET_AA0132_CONFIGURABLE_TM_TOOLS && JVET_W0097_GPM_MMVD_TM && TM_MRG
-- 
GitLab