From 11510cbc12d61bf585c1fb981ba6bb622a385d7e Mon Sep 17 00:00:00 2001
From: Han Gao <han.gao@huawei.com>
Date: Sun, 26 Jan 2020 18:26:36 +0100
Subject: [PATCH] JVET-Q0806 Geo related adoptions (JVET-Q0059, JVET-Q0077,
 JVET-Q0123, JVET-Q0188, JVET-Q0242_GEO, JVET-Q0309, JVET-Q0365 and
 JVET-Q0370)

---
 cfg/encoder_lowdelay_vtm.cfg                  |   2 +-
 cfg/encoder_randomaccess_vtm.cfg              |   2 +-
 doc/software-manual.tex                       |  12 +-
 source/App/EncoderApp/EncApp.cpp              |  16 +
 source/App/EncoderApp/EncAppCfg.cpp           |  42 +++
 source/App/EncoderApp/EncAppCfg.h             |  16 +
 source/Lib/CommonLib/Buffer.cpp               |  32 ++
 source/Lib/CommonLib/Buffer.h                 |  19 +
 source/Lib/CommonLib/CodingStatistics.h       |  10 +
 source/Lib/CommonLib/CommonDef.h              |  18 +
 source/Lib/CommonLib/ContextModelling.cpp     |   8 +
 source/Lib/CommonLib/InterPrediction.cpp      |  89 +++++
 source/Lib/CommonLib/InterPrediction.h        |  11 +
 source/Lib/CommonLib/InterpolationFilter.cpp  |  64 ++++
 source/Lib/CommonLib/InterpolationFilter.h    |   9 +
 source/Lib/CommonLib/RdCost.cpp               |  78 ++++
 source/Lib/CommonLib/RdCost.h                 |  29 +-
 source/Lib/CommonLib/Rom.cpp                  | 106 ++++++
 source/Lib/CommonLib/Rom.h                    |  13 +
 source/Lib/CommonLib/Slice.cpp                |  16 +
 source/Lib/CommonLib/Slice.h                  |  40 ++
 source/Lib/CommonLib/TypeDef.h                |  10 +
 source/Lib/CommonLib/Unit.cpp                 |  28 ++
 source/Lib/CommonLib/Unit.h                   |  10 +
 source/Lib/CommonLib/UnitTools.cpp            | 166 ++++++++
 source/Lib/CommonLib/UnitTools.h              |   5 +
 .../Lib/CommonLib/dtrace_blockstatistics.cpp  |  14 +
 source/Lib/CommonLib/dtrace_blockstatistics.h |  12 +
 .../CommonLib/x86/InterpolationFilterX86.h    | 228 +++++++++++
 source/Lib/CommonLib/x86/RdCostX86.h          |  95 +++++
 source/Lib/DecoderLib/CABACReader.cpp         |  70 ++++
 source/Lib/DecoderLib/DecCu.cpp               |  31 ++
 source/Lib/DecoderLib/DecCu.h                 |   4 +
 source/Lib/DecoderLib/VLCReader.cpp           |  37 ++
 source/Lib/EncoderLib/CABACWriter.cpp         |  49 +++
 source/Lib/EncoderLib/EncCfg.h                |  36 ++
 source/Lib/EncoderLib/EncCu.cpp               | 354 ++++++++++++++++++
 source/Lib/EncoderLib/EncCu.h                 | 100 +++++
 source/Lib/EncoderLib/EncLib.cpp              |  16 +
 source/Lib/EncoderLib/EncModeCtrl.cpp         |  28 ++
 source/Lib/EncoderLib/EncModeCtrl.h           |   8 +
 source/Lib/EncoderLib/VLCWriter.cpp           |  29 ++
 42 files changed, 1956 insertions(+), 6 deletions(-)

diff --git a/cfg/encoder_lowdelay_vtm.cfg b/cfg/encoder_lowdelay_vtm.cfg
index 2b449a99e..40e344573 100644
--- a/cfg/encoder_lowdelay_vtm.cfg
+++ b/cfg/encoder_lowdelay_vtm.cfg
@@ -116,7 +116,7 @@ ALF                          : 1
 BCW                          : 1
 BcwFast                      : 1
 CIIP                         : 1
-Triangle                     : 1
+Geo                          : 1
 IBC                          : 0      # turned off in CTC
 AllowDisFracMMVD             : 1
 AffineAmvr                   : 0
diff --git a/cfg/encoder_randomaccess_vtm.cfg b/cfg/encoder_randomaccess_vtm.cfg
index 47f57f957..87ea48f86 100644
--- a/cfg/encoder_randomaccess_vtm.cfg
+++ b/cfg/encoder_randomaccess_vtm.cfg
@@ -129,7 +129,7 @@ BCW                          : 1
 BcwFast                      : 1
 BIO                          : 1
 CIIP                         : 1
-Triangle                     : 1
+Geo                          : 1
 IBC                          : 0      # turned off in CTC
 AllowDisFracMMVD             : 1
 AffineAmvr                   : 1
diff --git a/doc/software-manual.tex b/doc/software-manual.tex
index 582dcbe4f..3f6802993 100644
--- a/doc/software-manual.tex
+++ b/doc/software-manual.tex
@@ -1283,10 +1283,10 @@ $}
 Specifies the maximum number of merge candidates to use.
 \\
 
-\Option{MaxNumTriangleCand} &
+\Option{MaxNumGeoCand} &
 %\ShortOption{\None} &
 \Default{5} &
-Specifies the maximum number of triangle merge candidates to use.
+Specifies the maximum number of geometric partitioning mode candidates to use.
 \\
 
 \Option{MaxNumIBCMergeCand} &
@@ -2158,7 +2158,7 @@ Enables signaling the below parameters either in PPS or for each slice according
   collocated_from_l0_flag & s & s & p & s \\
   six_minus_max_num_merge_cand & s & p & p & p \\
   five_minus_max_num_subblock_merge_cand & s & p & p & p \\
-  max_num_merge_cand_minus_max_num_triangle_cand & s & p & p & s \\
+  max_num_merge_cand_minus_max_num_geo_cand & s & p & p & s \\
 \end{tabular}
 \\
 
@@ -2239,6 +2239,12 @@ Value shall be in the range 1..8.
 Enables or disables symmetric MVD mode.
 \\
 
+\Option{Geo} &
+%\ShortOption{\None} &
+\Default{false} &
+Enables or disables geometric partitioning mode.
+\\
+
 \Option{PLT} &
 %\ShortOption{\None} &
 \Default{false} &
diff --git a/source/App/EncoderApp/EncApp.cpp b/source/App/EncoderApp/EncApp.cpp
index 77f765178..9b9219401 100644
--- a/source/App/EncoderApp/EncApp.cpp
+++ b/source/App/EncoderApp/EncApp.cpp
@@ -226,7 +226,11 @@ void EncApp::xInitLibCfg()
   m_cEncLib.setNoIbcConstraintFlag                               ( m_IBCMode ? false : true );
   m_cEncLib.setNoCiipConstraintFlag                           ( !m_ciip );
   m_cEncLib.setNoFPelMmvdConstraintFlag                          ( !(m_MMVD && m_allowDisFracMMVD) );
+#if !JVET_Q0806
   m_cEncLib.setNoTriangleConstraintFlag                          ( !m_Triangle );
+#else
+  m_cEncLib.setNoGeoConstraintFlag                               ( !m_Geo );
+#endif
   m_cEncLib.setNoLadfConstraintFlag                              ( !m_LadfEnabed );
   m_cEncLib.setNoTransformSkipConstraintFlag                     ( !m_useTransformSkip );
   m_cEncLib.setNoBDPCMConstraintFlag                             ( m_useBDPCM == 0 );
@@ -416,7 +420,11 @@ void EncApp::xInitLibCfg()
   }
 #endif
   m_cEncLib.setUseCiip                                        ( m_ciip );
+#if !JVET_Q0806
   m_cEncLib.setUseTriangle                                       ( m_Triangle );
+#else
+  m_cEncLib.setUseGeo                                            ( m_Geo );
+#endif
   m_cEncLib.setUseHashME                                         ( m_HashME );
 
   m_cEncLib.setAllowDisFracMMVD                                  ( m_allowDisFracMMVD );
@@ -510,7 +518,11 @@ void EncApp::xInitLibCfg()
 
   m_cEncLib.setMaxNumMergeCand                                   ( m_maxNumMergeCand );
   m_cEncLib.setMaxNumAffineMergeCand                             ( m_maxNumAffineMergeCand );
+#if !JVET_Q0806
   m_cEncLib.setMaxNumTriangleCand                                ( m_maxNumTriangleCand );
+#else
+  m_cEncLib.setMaxNumGeoCand                                     ( m_maxNumGeoCand );
+#endif
   m_cEncLib.setMaxNumIBCMergeCand                                ( m_maxNumIBCMergeCand );
 
   //====== Weighted Prediction ========
@@ -694,7 +706,11 @@ void EncApp::xInitLibCfg()
   m_cEncLib.setPPSMvdL1ZeroIdc                                   ( m_PPSMvdL1ZeroIdc );
   m_cEncLib.setPPSCollocatedFromL0Idc                            ( m_PPSCollocatedFromL0Idc );
   m_cEncLib.setPPSSixMinusMaxNumMergeCandPlus1                   ( m_PPSSixMinusMaxNumMergeCandPlus1 );
+#if !JVET_Q0806
   m_cEncLib.setPPSMaxNumMergeCandMinusMaxNumTriangleCandPlus1    ( m_PPSMaxNumMergeCandMinusMaxNumTriangleCandPlus1 );
+#else
+  m_cEncLib.setPPSMaxNumMergeCandMinusMaxNumGeoCandPlus1    ( m_PPSMaxNumMergeCandMinusMaxNumGeoCandPlus1 );
+#endif
   m_cEncLib.setUseScalingListId                                  ( m_useScalingListId  );
   m_cEncLib.setScalingListFileName                               ( m_scalingListFileName );
   m_cEncLib.setDisableScalingMatrixForLfnstBlks                  ( m_disableScalingMatrixForLfnstBlks);
diff --git a/source/App/EncoderApp/EncAppCfg.cpp b/source/App/EncoderApp/EncAppCfg.cpp
index cdad7cff4..2feec71c6 100644
--- a/source/App/EncoderApp/EncAppCfg.cpp
+++ b/source/App/EncoderApp/EncAppCfg.cpp
@@ -101,7 +101,11 @@ EncAppCfg::EncAppCfg()
 , m_noIbcConstraintFlag(false)
 , m_bNoCiipConstraintFlag(false)
 , m_noFPelMmvdConstraintFlag(false)
+#if !JVET_Q0806
 , m_bNoTriangleConstraintFlag(false)
+#else
+, m_bNoGeoConstraintFlag(false)
+#endif
 , m_bNoLadfConstraintFlag(false)
 , m_noTransformSkipConstraintFlag(false)
 , m_noBDPCMConstraintFlag(false)
@@ -919,7 +923,11 @@ bool EncAppCfg::parseCfg( int argc, char* argv[] )
   ("LadfIntervalLowerBound",                          cfg_LadfIntervalLowerBound,  cfg_LadfIntervalLowerBound, "LADF lower bound for 2nd lowest interval")
 #endif
   ("CIIP",                                            m_ciip,                                           false, "Enable CIIP mode")
+#if !JVET_Q0806
   ("Triangle",                                        m_Triangle,                                       false, "Enable triangular shape motion vector prediction (0:off, 1:on)")
+#else
+  ("Geo",                                             m_Geo,                                            false, "Enable geometric partitioning mode (0:off, 1:on)")
+#endif
   ("HashME",                                          m_HashME,                                         false, "Enable hash motion estimation (0:off, 1:on)")
 
   ("AllowDisFracMMVD",                                m_allowDisFracMMVD,                               false, "Disable fractional MVD in MMVD mode adaptively")
@@ -1138,7 +1146,11 @@ bool EncAppCfg::parseCfg( int argc, char* argv[] )
   ("SignHideFlag,-SBH",                               m_signDataHidingEnabledFlag,                                    false,  "Enable sign hiding" )
   ("MaxNumMergeCand",                                 m_maxNumMergeCand,                                   5u, "Maximum number of merge candidates")
   ("MaxNumAffineMergeCand",                           m_maxNumAffineMergeCand,                             5u, "Maximum number of affine merge candidates")
+#if !JVET_Q0806
   ("MaxNumTriangleCand",                              m_maxNumTriangleCand,                                5u, "Maximum number of triangle candidates")
+#else
+  ("MaxNumGeoCand",                                   m_maxNumGeoCand,                                     5u, "Maximum number of geometric partitioning mode candidates")
+#endif
   ("MaxNumIBCMergeCand",                              m_maxNumIBCMergeCand,                                6u, "Maximum number of IBC merge candidates")
     /* Misc. */
   ("SEIDecodedPictureHash,-dph",                      tmpDecodedPictureHashSEIMappedType,                   0, "Control generation of decode picture hash SEI messages\n"
@@ -2508,9 +2520,15 @@ bool EncAppCfg::xCheckParameter()
   xConfirmPara( m_log2MaxTbSize < 5,  "Log2MaxTbSize must be 5 or greater." );
   xConfirmPara( m_maxNumMergeCand < 1,  "MaxNumMergeCand must be 1 or greater.");
   xConfirmPara( m_maxNumMergeCand > MRG_MAX_NUM_CANDS, "MaxNumMergeCand must be no more than MRG_MAX_NUM_CANDS." );
+#if !JVET_Q0806
   xConfirmPara( m_maxNumTriangleCand > TRIANGLE_MAX_NUM_UNI_CANDS, "MaxNumTriangleCand must be no more than TRIANGLE_MAX_NUM_UNI_CANDS." );
   xConfirmPara( m_maxNumTriangleCand > m_maxNumMergeCand, "MaxNumTriangleCand must be no more than MaxNumMergeCand." );
   xConfirmPara( 0 < m_maxNumTriangleCand && m_maxNumTriangleCand < 2, "MaxNumTriangleCand must be no less than 2 unless MaxNumTriangleCand is 0." );
+#else
+  xConfirmPara( m_maxNumGeoCand > GEO_MAX_NUM_UNI_CANDS, "MaxNumGeoCand must be no more than GEO_MAX_NUM_UNI_CANDS." );
+  xConfirmPara( m_maxNumGeoCand > m_maxNumMergeCand, "MaxNumGeoCand must be no more than MaxNumMergeCand." );
+  xConfirmPara( 0 < m_maxNumGeoCand && m_maxNumGeoCand < 2, "MaxNumGeoCand must be no less than 2 unless MaxNumGeoCand is 0." );
+#endif
   xConfirmPara( m_maxNumIBCMergeCand < 1, "MaxNumIBCMergeCand must be 1 or greater." );
   xConfirmPara( m_maxNumIBCMergeCand > IBC_MRG_MAX_NUM_CANDS, "MaxNumIBCMergeCand must be no more than IBC_MRG_MAX_NUM_CANDS." );
   xConfirmPara( m_maxNumAffineMergeCand < 1, "MaxNumAffineMergeCand must be 1 or greater." );
@@ -3252,7 +3270,11 @@ bool EncAppCfg::xCheckParameter()
     m_PPSMvdL1ZeroIdc = 0;
     m_PPSCollocatedFromL0Idc = 0;
     m_PPSSixMinusMaxNumMergeCandPlus1 = 0;
+#if !JVET_Q0806
     m_PPSMaxNumMergeCandMinusMaxNumTriangleCandPlus1 = 0;
+#else
+    m_PPSMaxNumMergeCandMinusMaxNumGeoCandPlus1 = 0;
+#endif
     break;
   case 1: // RA setting
     m_constantSliceHeaderParamsEnabledFlag = 1;
@@ -3262,7 +3284,11 @@ bool EncAppCfg::xCheckParameter()
     m_PPSMvdL1ZeroIdc = 0;
     m_PPSCollocatedFromL0Idc = 0;
     m_PPSSixMinusMaxNumMergeCandPlus1 = 6 - m_maxNumMergeCand + 1;
+#if !JVET_Q0806
     m_PPSMaxNumMergeCandMinusMaxNumTriangleCandPlus1 = m_maxNumMergeCand - m_maxNumTriangleCand + 1;
+#else
+    m_PPSMaxNumMergeCandMinusMaxNumGeoCandPlus1 = m_maxNumMergeCand - m_maxNumGeoCand + 1;
+#endif
     break;
   case 2: // LDB setting
     m_constantSliceHeaderParamsEnabledFlag = 1;
@@ -3272,7 +3298,11 @@ bool EncAppCfg::xCheckParameter()
     m_PPSMvdL1ZeroIdc = 2;
     m_PPSCollocatedFromL0Idc = 1;
     m_PPSSixMinusMaxNumMergeCandPlus1 = 6 - m_maxNumMergeCand + 1;
+#if !JVET_Q0806
     m_PPSMaxNumMergeCandMinusMaxNumTriangleCandPlus1 = m_maxNumMergeCand - m_maxNumTriangleCand + 1;
+#else
+    m_PPSMaxNumMergeCandMinusMaxNumGeoCandPlus1 = m_maxNumMergeCand - m_maxNumGeoCand + 1;
+#endif
     break;
   case 3: // LDP setting
     m_constantSliceHeaderParamsEnabledFlag = 1;
@@ -3282,7 +3312,11 @@ bool EncAppCfg::xCheckParameter()
     m_PPSMvdL1ZeroIdc = 0;
     m_PPSCollocatedFromL0Idc = 0;
     m_PPSSixMinusMaxNumMergeCandPlus1 = 6 - m_maxNumMergeCand + 1;
+#if !JVET_Q0806
     m_PPSMaxNumMergeCandMinusMaxNumTriangleCandPlus1 = 0;
+#else
+    m_PPSMaxNumMergeCandMinusMaxNumGeoCandPlus1 = 0;
+#endif
     break;
   default:
     THROW("Invalid value for PPSorSliceMode");
@@ -3572,7 +3606,11 @@ void EncAppCfg::xPrintParameter()
 
   msg( DETAILS, "Max Num Merge Candidates               : %d\n", m_maxNumMergeCand );
   msg( DETAILS, "Max Num Affine Merge Candidates        : %d\n", m_maxNumAffineMergeCand );
+#if !JVET_Q0806
   msg( DETAILS, "Max Num Triangle Merge Candidates      : %d\n", m_maxNumTriangleCand );
+#else
+  msg( DETAILS, "Max Num Geo Merge Candidates           : %d\n", m_maxNumGeoCand );
+#endif
   msg( DETAILS, "Max Num IBC Merge Candidates           : %d\n", m_maxNumIBCMergeCand );
   msg( DETAILS, "\n");
 
@@ -3643,7 +3681,11 @@ void EncAppCfg::xPrintParameter()
     msg( VERBOSE, "LADF:%d ", m_LadfEnabed );
 #endif
     msg(VERBOSE, "CIIP:%d ", m_ciip);
+#if !JVET_Q0806
     msg( VERBOSE, "Triangle:%d ", m_Triangle );
+#else
+    msg( VERBOSE, "Geo:%d ", m_Geo );
+#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 4ce37e402..0ccf3f246 100644
--- a/source/App/EncoderApp/EncAppCfg.h
+++ b/source/App/EncoderApp/EncAppCfg.h
@@ -151,7 +151,11 @@ protected:
   bool      m_noIbcConstraintFlag;
   bool      m_bNoCiipConstraintFlag;
   bool      m_noFPelMmvdConstraintFlag;
+#if !JVET_Q0806
   bool      m_bNoTriangleConstraintFlag;
+#else
+  bool      m_bNoGeoConstraintFlag;
+#endif
   bool      m_bNoLadfConstraintFlag;
   bool      m_noTransformSkipConstraintFlag;
   bool      m_noBDPCMConstraintFlag;
@@ -305,7 +309,11 @@ protected:
 #endif
 
   bool      m_ciip;
+#if !JVET_Q0806
   bool      m_Triangle;
+#else
+  bool      m_Geo;
+#endif
   bool      m_HashME;
   bool      m_allowDisFracMMVD;
   bool      m_AffineAmvr;
@@ -583,7 +591,11 @@ protected:
 
   uint32_t      m_maxNumMergeCand;                                ///< Max number of merge candidates
   uint32_t      m_maxNumAffineMergeCand;                          ///< Max number of affine merge candidates
+#if !JVET_Q0806
   uint32_t      m_maxNumTriangleCand;
+#else
+  uint32_t      m_maxNumGeoCand;
+#endif
   uint32_t      m_maxNumIBCMergeCand;                             ///< Max number of IBC merge candidates
 
   bool      m_sliceLevelRpl;                                      ///< code reference picture lists in slice headers rather than picture header
@@ -599,7 +611,11 @@ protected:
   int       m_PPSMvdL1ZeroIdc;
   int       m_PPSCollocatedFromL0Idc;
   uint32_t  m_PPSSixMinusMaxNumMergeCandPlus1;
+#if !JVET_Q0806
   uint32_t  m_PPSMaxNumMergeCandMinusMaxNumTriangleCandPlus1;
+#else
+  uint32_t  m_PPSMaxNumMergeCandMinusMaxNumGeoCandPlus1;
+#endif
   bool      m_depQuantEnabledFlag;
   bool      m_signDataHidingEnabledFlag;
   bool      m_RCEnableRateControl;                ///< enable rate control or not
diff --git a/source/Lib/CommonLib/Buffer.cpp b/source/Lib/CommonLib/Buffer.cpp
index e1f967f14..c3d141071 100644
--- a/source/Lib/CommonLib/Buffer.cpp
+++ b/source/Lib/CommonLib/Buffer.cpp
@@ -553,6 +553,38 @@ void AreaBuf<Pel>::copyClip( const AreaBuf<const Pel> &src, const ClpRng& clpRng
   }
 }
 
+#if JVET_Q0806
+template<>
+void AreaBuf<Pel>::roundToOutputBitdepth( const AreaBuf<const Pel> &src, const ClpRng& clpRng )
+{
+  const Pel* srcp = src.buf;
+        Pel* dest =     buf;
+  const unsigned srcStride  = src.stride;
+  const unsigned destStride = stride;
+
+  const int32_t clipbd            = clpRng.bd;
+  const int32_t shiftDefault      = std::max<int>(2, (IF_INTERNAL_PREC - clipbd));
+  const int32_t offsetDefault     = (1<<(shiftDefault-1)) + IF_INTERNAL_OFFS;
+   
+  if( width == 1 )
+  {
+    THROW( "Blocks of width = 1 not supported" );
+  }
+  else
+  {
+#define RND_OP( ADDR ) dest[ADDR] = ClipPel( rightShift( srcp[ADDR] + offsetDefault, shiftDefault), clpRng )
+#define RND_INC        \
+    srcp += srcStride;  \
+    dest += destStride; \
+
+    SIZE_AWARE_PER_EL_OP( RND_OP, RND_INC );
+
+#undef RND_OP
+#undef RND_INC
+  }
+}
+#endif
+
 
 template<>
 void AreaBuf<Pel>::reconstruct( const AreaBuf<const Pel> &pred, const AreaBuf<const Pel> &resi, const ClpRng& clpRng )
diff --git a/source/Lib/CommonLib/Buffer.h b/source/Lib/CommonLib/Buffer.h
index 9b461389d..3455dcea5 100644
--- a/source/Lib/CommonLib/Buffer.h
+++ b/source/Lib/CommonLib/Buffer.h
@@ -110,6 +110,9 @@ struct AreaBuf : public Size
   void memset               ( const int val );
 
   void copyFrom             ( const AreaBuf<const T> &other );
+#if JVET_Q0806
+  void roundToOutputBitdepth(const AreaBuf<const T> &src, const ClpRng& clpRng);
+#endif
 
   void reconstruct          ( const AreaBuf<const T> &pred, const AreaBuf<const T> &resi, const ClpRng& clpRng);
   void copyClip             ( const AreaBuf<const T> &src, const ClpRng& clpRng);
@@ -764,6 +767,9 @@ struct UnitBuf
 
   void fill                 ( const T &val );
   void copyFrom             ( const UnitBuf<const T> &other, const bool lumaOnly = false, const bool chromaOnly = false );
+#if JVET_Q0806
+  void roundToOutputBitdepth(const UnitBuf<const T> &src, const ClpRngs& clpRngs);
+#endif
   void reconstruct          ( const UnitBuf<const T> &pred, const UnitBuf<const T> &resi, const ClpRngs& clpRngs );
   void copyClip             ( const UnitBuf<const T> &src, const ClpRngs& clpRngs, const bool lumaOnly = false, const bool chromaOnly = false );
   void subtract             ( const UnitBuf<const T> &other );
@@ -839,6 +845,19 @@ void UnitBuf<T>::copyClip(const UnitBuf<const T> &src, const ClpRngs &clpRngs, c
 }
 
 
+#if JVET_Q0806
+template<typename T>
+void UnitBuf<T>::roundToOutputBitdepth(const UnitBuf<const T> &src, const ClpRngs& clpRngs)
+{
+  CHECK(chromaFormat != src.chromaFormat, "Incompatible formats");
+
+  for (unsigned i = 0; i < bufs.size(); i++)
+  {
+    bufs[i].roundToOutputBitdepth(src.bufs[i], clpRngs.comp[i]);
+  }
+}
+#endif
+
 template<typename T>
 void UnitBuf<T>::reconstruct(const UnitBuf<const T> &pred, const UnitBuf<const T> &resi, const ClpRngs& clpRngs)
 {
diff --git a/source/Lib/CommonLib/CodingStatistics.h b/source/Lib/CommonLib/CodingStatistics.h
index 375ed6ec2..08de32ce3 100644
--- a/source/Lib/CommonLib/CodingStatistics.h
+++ b/source/Lib/CommonLib/CodingStatistics.h
@@ -112,8 +112,13 @@ enum CodingStatisticsType
   STATS__CABAC_BITS__BCW_IDX,
   STATS__CABAC_BITS__SBT_MODE,
   STATS__CABAC_BITS__MH_INTRA_FLAG,
+#if !JVET_Q0806
   STATS__CABAC_BITS__TRIANGLE_FLAG,
   STATS__CABAC_BITS__TRIANGLE_INDEX,
+#else
+  STATS__CABAC_BITS__GEO_FLAG,
+  STATS__CABAC_BITS__GEO_INDEX,
+#endif
   STATS__CABAC_BITS__MULTI_REF_LINE,
   STATS__CABAC_BITS__SYMMVD_FLAG,
   STATS__CABAC_BITS__BDPCM_MODE,
@@ -204,8 +209,13 @@ static inline const char* getName(CodingStatisticsType name)
     "CABAC_BITS__BCW_IDX",
     "CABAC_BITS__SBT_MODE",
     "CABAC_BITS__MH_INTRA_FLAG",
+#if !JVET_Q0806
     "CABAC_BITS__TRIANGLE_FLAG",
     "CABAC_BITS__TRIANGLE_INDEX",
+#else
+    "CABAC_BITS__GEO_FLAG",
+    "CABAC_BITS__GEO_INDEX",
+#endif
     "CABAC_BITS__MULTI_REF_LINE",
     "CABAC_BITS__SYMMVD_FLAG",
     "CABAC_BITS__BDPCM_MODE",
diff --git a/source/Lib/CommonLib/CommonDef.h b/source/Lib/CommonLib/CommonDef.h
index f24085c10..d186c637f 100644
--- a/source/Lib/CommonLib/CommonDef.h
+++ b/source/Lib/CommonLib/CommonDef.h
@@ -421,11 +421,29 @@ static const int MAX_LADF_INTERVALS       =                         5; /// max n
 static const int NTAPS_BILINEAR           =                         2; ///< Number of taps for bilinear filter
 
 static const int ATMVP_SUB_BLOCK_SIZE =                             3; ///< sub-block size for ATMVP
+#if !JVET_Q0806
 static const int TRIANGLE_MAX_NUM_UNI_CANDS =                       6;
 static const int TRIANGLE_MAX_NUM_CANDS_MEM =                       7;
 static const int TRIANGLE_MAX_NUM_CANDS = TRIANGLE_MAX_NUM_UNI_CANDS * (TRIANGLE_MAX_NUM_UNI_CANDS - 1) * 2;
 static const int TRIANGLE_MAX_NUM_SATD_CANDS =                      3;
 static const int TRIANGLE_MIN_SIZE =                            8 * 8;
+#else
+static const int GEO_MAX_NUM_UNI_CANDS =                            6;
+static const int GEO_MAX_NUM_CANDS = GEO_MAX_NUM_UNI_CANDS * (GEO_MAX_NUM_UNI_CANDS - 1);
+static const int GEO_MIN_CU_LOG2 =                                  3;
+static const int GEO_MAX_CU_LOG2 =                                  6;
+static const int GEO_MIN_CU_SIZE =               1 << GEO_MIN_CU_LOG2;
+static const int GEO_MAX_CU_SIZE =               1 << GEO_MAX_CU_LOG2;
+static const int GEO_NUM_CU_SIZE = ( GEO_MAX_CU_LOG2 - GEO_MIN_CU_LOG2 ) + 1;
+static const int GEO_NUM_PARTITION_MODE =                          64;
+static const int GEO_NUM_ANGLES =                                  32;
+static const int GEO_NUM_DISTANCES =                                4;
+static const int GEO_NUM_PRESTORED_MASK =                           6;
+static const int GEO_WEIGHT_MASK_SIZE = 3 * (GEO_MAX_CU_SIZE >> 3) * 2 + GEO_MAX_CU_SIZE;
+static const int GEO_MV_MASK_SIZE =         GEO_WEIGHT_MASK_SIZE >> 2;
+static const int GEO_MAX_TRY_WEIGHTED_SAD = 60;
+static const int GEO_MAX_TRY_WEIGHTED_SATD = 8;
+#endif
 
 static const int SBT_MAX_SIZE =                                    64; ///< maximum CU size for using SBT
 static const int SBT_NUM_SL =                                      10; ///< maximum number of historical PU decision saved for a CU
diff --git a/source/Lib/CommonLib/ContextModelling.cpp b/source/Lib/CommonLib/ContextModelling.cpp
index a8cd73368..6913c4d54 100644
--- a/source/Lib/CommonLib/ContextModelling.cpp
+++ b/source/Lib/CommonLib/ContextModelling.cpp
@@ -320,11 +320,19 @@ unsigned DeriveCtx::CtxIBCFlag(const CodingUnit& cu)
 void MergeCtx::setMergeInfo( PredictionUnit& pu, int candIdx )
 {
   CHECK( candIdx >= numValidMergeCand, "Merge candidate does not exist" );
+#if !JVET_Q0806
   pu.regularMergeFlag        = !(pu.ciipFlag || pu.cu->triangle);
+#else
+  pu.regularMergeFlag        = !(pu.ciipFlag || pu.cu->geoFlag);
+#endif
   pu.mergeFlag               = true;
   pu.mmvdMergeFlag = false;
   pu.interDir                = interDirNeighbours[candIdx];
+#if !JVET_Q0806
   pu.cu->imv = (!pu.cu->triangle && useAltHpelIf[candIdx]) ? IMV_HPEL : 0;
+#else
+  pu.cu->imv = (!pu.cu->geoFlag && useAltHpelIf[candIdx]) ? IMV_HPEL : 0;
+#endif
   pu.mergeIdx                = candIdx;
   pu.mergeType               = mrgTypeNeighbours[candIdx];
   pu.mv     [REF_PIC_LIST_0] = mvFieldNeighbours[(candIdx << 1) + 0].mv;
diff --git a/source/Lib/CommonLib/InterPrediction.cpp b/source/Lib/CommonLib/InterPrediction.cpp
index d749d78e9..ab4a35f8d 100644
--- a/source/Lib/CommonLib/InterPrediction.cpp
+++ b/source/Lib/CommonLib/InterPrediction.cpp
@@ -126,7 +126,12 @@ void InterPrediction::destroy()
     }
   }
 
+#if !JVET_Q0806
   m_triangleBuf.destroy();
+#else
+  m_geoPartBuf[0].destroy();
+  m_geoPartBuf[1].destroy();
+#endif
   m_colorTransResiBuf[0].destroy();
   m_colorTransResiBuf[1].destroy();
   m_colorTransResiBuf[2].destroy();
@@ -192,7 +197,12 @@ void InterPrediction::init( RdCost* pcRdCost, ChromaFormat chromaFormatIDC, cons
       }
     }
 
+#if !JVET_Q0806
     m_triangleBuf.create(UnitArea(chromaFormatIDC, Area(0, 0, MAX_CU_SIZE, MAX_CU_SIZE)));
+#else
+    m_geoPartBuf[0].create(UnitArea(chromaFormatIDC, Area(0, 0, MAX_CU_SIZE, MAX_CU_SIZE)));
+    m_geoPartBuf[1].create(UnitArea(chromaFormatIDC, Area(0, 0, MAX_CU_SIZE, MAX_CU_SIZE)));
+#endif
     m_colorTransResiBuf[0].create(UnitArea(chromaFormatIDC, Area(0, 0, MAX_CU_SIZE, MAX_CU_SIZE)));
     m_colorTransResiBuf[1].create(UnitArea(chromaFormatIDC, Area(0, 0, MAX_CU_SIZE, MAX_CU_SIZE)));
     m_colorTransResiBuf[2].create(UnitArea(chromaFormatIDC, Area(0, 0, MAX_CU_SIZE, MAX_CU_SIZE)));
@@ -629,7 +639,11 @@ void InterPrediction::xPredInterBi(PredictionUnit &pu, PelUnitBuf &pcYuvPred, co
       }
       else
       {
+#if !JVET_Q0806
         xPredInterUni( pu, eRefPicList, pcMbBuf, pu.cu->triangle
+#else
+        xPredInterUni(pu, eRefPicList, pcMbBuf, pu.cu->geoFlag
+#endif
           , bioApplied
           , luma, chroma
         );
@@ -644,13 +658,21 @@ void InterPrediction::xPredInterBi(PredictionUnit &pu, PelUnitBuf &pcYuvPred, co
                            CPelUnitBuf(pu.chromaFormat, PelBuf(m_acYuvPred[1][0], pcYuvPred.Y()), PelBuf(m_acYuvPred[1][1], pcYuvPred.Cb()), PelBuf(m_acYuvPred[1][2], pcYuvPred.Cr())) );
   const bool lumaOnly   = luma && !chroma;
   const bool chromaOnly = !luma && chroma;
+#if !JVET_Q0806  
   if( !pu.cu->triangle && (!dmvrApplied) && (!bioApplied) && pps.getWPBiPred() && slice.getSliceType() == B_SLICE && pu.cu->BcwIdx==BCW_DEFAULT)
+#else
+  if( !pu.cu->geoFlag && (!dmvrApplied) && (!bioApplied) && pps.getWPBiPred() && slice.getSliceType() == B_SLICE && pu.cu->BcwIdx == BCW_DEFAULT)
+#endif
   {
     xWeightedPredictionBi( pu, srcPred0, srcPred1, pcYuvPred, m_maxCompIDToPred, lumaOnly, chromaOnly );
     if (yuvPredTmp)
       yuvPredTmp->copyFrom(pcYuvPred);
   }
+#if !JVET_Q0806
   else if( !pu.cu->triangle && pps.getUseWP() && slice.getSliceType() == P_SLICE )
+#else
+  else if( !pu.cu->geoFlag && pps.getUseWP() && slice.getSliceType() == P_SLICE )
+#endif
   {
     xWeightedPredictionUni( pu, srcPred0, REF_PIC_LIST_0, pcYuvPred, -1, m_maxCompIDToPred, lumaOnly, chromaOnly );
     if (yuvPredTmp)
@@ -1453,22 +1475,38 @@ void InterPrediction::xWeightedAverage(const PredictionUnit& pu, const CPelUnitB
   }
   else if( iRefIdx0 >= 0 && iRefIdx1 < 0 )
   {
+#if !JVET_Q0806
     if( pu.cu->triangle )
     {
       pcYuvDst.copyFrom( pcYuvSrc0 );
     }
     else
+#else
+    if( pu.cu->geoFlag )
+    {
+      pcYuvDst.copyFrom( pcYuvSrc0 );
+    }
+    else
+#endif
       pcYuvDst.copyClip( pcYuvSrc0, clpRngs, lumaOnly, chromaOnly );
     if (yuvDstTmp)
       yuvDstTmp->copyFrom( pcYuvDst, lumaOnly, chromaOnly );
   }
   else if( iRefIdx0 < 0 && iRefIdx1 >= 0 )
   {
+#if !JVET_Q0806
     if( pu.cu->triangle )
     {
       pcYuvDst.copyFrom( pcYuvSrc1 );
     }
     else
+#else
+    if( pu.cu->geoFlag )
+    {
+      pcYuvDst.copyFrom( pcYuvSrc1 );
+    }
+    else
+#endif
       pcYuvDst.copyClip( pcYuvSrc1, clpRngs, lumaOnly, chromaOnly );
     if (yuvDstTmp)
       yuvDstTmp->copyFrom(pcYuvDst, lumaOnly, chromaOnly);
@@ -1655,6 +1693,7 @@ int InterPrediction::rightShiftMSB(int numer, int denom)
   return numer >> floorLog2(denom);
 }
 
+#if !JVET_Q0806
 void InterPrediction::motionCompensation4Triangle( CodingUnit &cu, MergeCtx &triangleMrgCtx, const bool splitDir, const uint8_t candIdx0, const uint8_t candIdx1 )
 {
   for( auto &pu : CU::traversePUs( cu ) )
@@ -1706,7 +1745,57 @@ void InterPrediction::weightedTriangleBlk( PredictionUnit &pu, const bool splitD
     m_if.weightedTriangleBlk( pu, pu.chromaSize().width, pu.chromaSize().height, COMPONENT_Cr, splitDir, predDst, predSrc0, predSrc1 );
   }
 }
+#else
+void InterPrediction::motionCompensationGeo( CodingUnit &cu, MergeCtx &geoMrgCtx )
+{
+  const uint8_t splitDir = cu.firstPU->geoSplitDir;
+  const uint8_t candIdx0 = cu.firstPU->geoMergeIdx0;
+  const uint8_t candIdx1 = cu.firstPU->geoMergeIdx1;
+  for( auto &pu : CU::traversePUs( cu ) )
+  {
+    const UnitArea localUnitArea( cu.cs->area.chromaFormat, Area( 0, 0, pu.lwidth(), pu.lheight() ) );
+    PelUnitBuf tmpGeoBuf0 = m_geoPartBuf[0].getBuf( localUnitArea );
+    PelUnitBuf tmpGeoBuf1 = m_geoPartBuf[1].getBuf( localUnitArea );
+    PelUnitBuf predBuf    = cu.cs->getPredBuf( pu );
+
+    geoMrgCtx.setMergeInfo( pu, candIdx0 );
+    PU::spanMotionInfo( pu );
+    motionCompensation(pu, tmpGeoBuf0, REF_PIC_LIST_X, true, isChromaEnabled(pu.chromaFormat));
+    if( g_mctsDecCheckEnabled && !MCTSHelper::checkMvBufferForMCTSConstraint( pu, true ) )
+    {
+      printf( "DECODER_GEO_PU: pu motion vector across tile boundaries (%d,%d,%d,%d)\n", pu.lx(), pu.ly(), pu.lwidth(), pu.lheight() );
+    }
+    
+    geoMrgCtx.setMergeInfo( pu, candIdx1 );
+    PU::spanMotionInfo( pu );
+    motionCompensation(pu, tmpGeoBuf1, REF_PIC_LIST_X, true, isChromaEnabled(pu.chromaFormat));
+    if( g_mctsDecCheckEnabled && !MCTSHelper::checkMvBufferForMCTSConstraint( pu, true ) )
+    {
+      printf( "DECODER_GEO_PU: pu motion vector across tile boundaries (%d,%d,%d,%d)\n", pu.lx(), pu.ly(), pu.lwidth(), pu.lheight() );
+    }
+    weightedGeoBlk(pu, splitDir, isChromaEnabled(pu.chromaFormat)? MAX_NUM_CHANNEL_TYPE : CHANNEL_TYPE_LUMA, predBuf, tmpGeoBuf0, tmpGeoBuf1);
+  }
+}
 
+void InterPrediction::weightedGeoBlk( PredictionUnit &pu, const uint8_t splitDir, int32_t channel, PelUnitBuf& predDst, PelUnitBuf& predSrc0, PelUnitBuf& predSrc1)
+{
+  if( channel == CHANNEL_TYPE_LUMA )
+  {
+    m_if.weightedGeoBlk( pu, pu.lumaSize().width, pu.lumaSize().height, COMPONENT_Y, splitDir, predDst, predSrc0, predSrc1 );
+  }
+  else if( channel == CHANNEL_TYPE_CHROMA )
+  {
+    m_if.weightedGeoBlk( pu, pu.chromaSize().width, pu.chromaSize().height, COMPONENT_Cb, splitDir, predDst, predSrc0, predSrc1 );
+    m_if.weightedGeoBlk( pu, pu.chromaSize().width, pu.chromaSize().height, COMPONENT_Cr, splitDir, predDst, predSrc0, predSrc1 );
+  }
+  else
+  {
+    m_if.weightedGeoBlk( pu, pu.lumaSize().width,   pu.lumaSize().height,   COMPONENT_Y,  splitDir, predDst, predSrc0, predSrc1 );
+    m_if.weightedGeoBlk( pu, pu.chromaSize().width, pu.chromaSize().height, COMPONENT_Cb, splitDir, predDst, predSrc0, predSrc1 );
+    m_if.weightedGeoBlk( pu, pu.chromaSize().width, pu.chromaSize().height, COMPONENT_Cr, splitDir, predDst, predSrc0, predSrc1 );
+  }
+}
+#endif
 
 void InterPrediction::xPrefetch(PredictionUnit& pu, PelUnitBuf &pcPad, RefPicList refId, bool forLuma)
 {
diff --git a/source/Lib/CommonLib/InterPrediction.h b/source/Lib/CommonLib/InterPrediction.h
index 1bc16ff8d..a7455d461 100644
--- a/source/Lib/CommonLib/InterPrediction.h
+++ b/source/Lib/CommonLib/InterPrediction.h
@@ -81,7 +81,11 @@ protected:
   RdCost*              m_pcRdCost;
 
   int                  m_iRefListIdx;
+#if !JVET_Q0806
   PelStorage           m_triangleBuf;
+#else
+  PelStorage           m_geoPartBuf[2];
+#endif
   Mv*                  m_storedMv;
  /*buffers for bilinear Filter data for DMVR refinement*/
   Pel*                 m_cYuvPredTempDMVRL0;
@@ -138,7 +142,9 @@ protected:
   void xCalcBlkGradient         (int sx, int sy, int    *arraysGx2, int     *arraysGxGy, int     *arraysGxdI, int     *arraysGy2, int     *arraysGydI, int     &sGx2, int     &sGy2, int     &sGxGy, int     &sGxdI, int     &sGydI, int width, int height, int unitSize);
   void xWeightedAverage         ( const PredictionUnit& pu, const CPelUnitBuf& pcYuvSrc0, const CPelUnitBuf& pcYuvSrc1, PelUnitBuf& pcYuvDst, const BitDepths& clipBitDepths, const ClpRngs& clpRngs, const bool& bioApplied, const bool lumaOnly = false, const bool chromaOnly = false, PelUnitBuf* yuvDstTmp = NULL );
   void xPredAffineBlk           ( const ComponentID& compID, const PredictionUnit& pu, const Picture* refPic, const Mv* _mv, PelUnitBuf& dstPic, const bool& bi, const ClpRng& clpRng, const bool genChromaMv = false, const std::pair<int, int> scalingRatio = SCALE_1X );
+#if !JVET_Q0806
   void xWeightedTriangleBlk     ( const PredictionUnit &pu, const uint32_t width, const uint32_t height, const ComponentID compIdx, const bool splitDir, PelUnitBuf& predDst, PelUnitBuf& predSrc0, PelUnitBuf& predSrc1 );
+#endif
 
   static bool xCheckIdenticalMotion( const PredictionUnit& pu );
 
@@ -171,8 +177,13 @@ public:
     , const bool luma = true, const bool chroma = true
   );
 
+#if !JVET_Q0806
   void    motionCompensation4Triangle( CodingUnit &cu, MergeCtx &triangleMrgCtx, const bool splitDir, const uint8_t candIdx0, const uint8_t candIdx1 );
   void    weightedTriangleBlk        ( PredictionUnit &pu, const bool splitDir, int32_t channel, PelUnitBuf& predDst, PelUnitBuf& predSrc0, PelUnitBuf& predSrc1 );
+#else
+  void    motionCompensationGeo(CodingUnit &cu, MergeCtx &GeoMrgCtx);
+  void    weightedGeoBlk(PredictionUnit &pu, const uint8_t splitDir, int32_t channel, PelUnitBuf& predDst, PelUnitBuf& predSrc0, PelUnitBuf& predSrc1);
+#endif
   void xPrefetch(PredictionUnit& pu, PelUnitBuf &pcPad, RefPicList refId, bool forLuma);
   void xPad(PredictionUnit& pu, PelUnitBuf &pcPad, RefPicList refId);
   void xFinalPaddedMCForDMVR(PredictionUnit& pu, PelUnitBuf &pcYuvSrc0, PelUnitBuf &pcYuvSrc1, PelUnitBuf &pcPad0, PelUnitBuf &pcPad1, const bool bioApplied
diff --git a/source/Lib/CommonLib/InterpolationFilter.cpp b/source/Lib/CommonLib/InterpolationFilter.cpp
index 77f46edca..5fb66c384 100644
--- a/source/Lib/CommonLib/InterpolationFilter.cpp
+++ b/source/Lib/CommonLib/InterpolationFilter.cpp
@@ -371,7 +371,11 @@ InterpolationFilter::InterpolationFilter()
   m_filterCopy[1][0]   = filterCopy<true, false>;
   m_filterCopy[1][1]   = filterCopy<true, true>;
 
+#if !JVET_Q0806
   m_weightedTriangleBlk = xWeightedTriangleBlk;
+#else
+  m_weightedGeoBlk = xWeightedGeoBlk;
+#endif
 }
 
 
@@ -888,6 +892,7 @@ void InterpolationFilter::filterVer(const ComponentID compID, Pel const *src, in
   }
 }
 
+#if !JVET_Q0806
 void InterpolationFilter::xWeightedTriangleBlk( const PredictionUnit &pu, const uint32_t width, const uint32_t height, const ComponentID compIdx, const bool splitDir, PelUnitBuf& predDst, PelUnitBuf& predSrc0, PelUnitBuf& predSrc1 )
 {
   Pel*    dst        = predDst .get(compIdx).buf;
@@ -983,6 +988,65 @@ void InterpolationFilter::weightedTriangleBlk(const PredictionUnit &pu, const ui
 {
   m_weightedTriangleBlk(pu, width, height, compIdx, splitDir, predDst, predSrc0, predSrc1);
 }
+#else
+void InterpolationFilter::weightedGeoBlk(const PredictionUnit &pu, const uint32_t width, const uint32_t height, const ComponentID compIdx, const uint8_t splitDir, PelUnitBuf& predDst, PelUnitBuf& predSrc0, PelUnitBuf& predSrc1)
+{
+  m_weightedGeoBlk(pu, width, height, compIdx, splitDir, predDst, predSrc0, predSrc1);
+}
+
+void InterpolationFilter::xWeightedGeoBlk(const PredictionUnit &pu, const uint32_t width, const uint32_t height, const ComponentID compIdx, const uint8_t splitDir, PelUnitBuf& predDst, PelUnitBuf& predSrc0, PelUnitBuf& predSrc1)
+{
+  Pel*    dst = predDst.get(compIdx).buf;
+  Pel*    src0 = predSrc0.get(compIdx).buf;
+  Pel*    src1 = predSrc1.get(compIdx).buf;
+  int32_t strideDst = predDst.get(compIdx).stride - width;
+  int32_t strideSrc0 = predSrc0.get(compIdx).stride - width;
+  int32_t strideSrc1 = predSrc1.get(compIdx).stride - width;
+
+  const char    log2WeightBase = 3;
+  const ClpRng  clipRng = pu.cu->slice->clpRngs().comp[compIdx];
+  const int32_t clipbd = clipRng.bd;
+  const int32_t shiftWeighted = std::max<int>(2, (IF_INTERNAL_PREC - clipbd)) + log2WeightBase;
+  const int32_t offsetWeighted = (1 << (shiftWeighted - 1)) + (IF_INTERNAL_OFFS << log2WeightBase);
+  const uint32_t scaleX = getComponentScaleX(compIdx, pu.chromaFormat);
+  const uint32_t scaleY = getComponentScaleY(compIdx, pu.chromaFormat);
+
+  int16_t angle = g_GeoParams[splitDir][0];
+  int16_t wIdx = floorLog2(pu.lwidth()) - GEO_MIN_CU_LOG2;
+  int16_t hIdx = floorLog2(pu.lheight()) - GEO_MIN_CU_LOG2;
+  int16_t stepX = 1 << scaleX;
+  int16_t stepY = 0;
+  int16_t* weight = nullptr;
+  if (g_angle2mirror[angle] == 2)
+  {
+    stepY = -(int)((GEO_WEIGHT_MASK_SIZE << scaleY) + pu.lwidth());
+    weight = &g_globalGeoWeights[g_angle2mask[angle]][(GEO_WEIGHT_MASK_SIZE - 1 - g_weightOffset[splitDir][hIdx][wIdx][1]) * GEO_WEIGHT_MASK_SIZE + g_weightOffset[splitDir][hIdx][wIdx][0]];
+  }
+  else if (g_angle2mirror[angle] == 1)
+  {
+    stepX = -1 << scaleX;
+    stepY = (GEO_WEIGHT_MASK_SIZE << scaleY) + pu.lwidth();
+    weight = &g_globalGeoWeights[g_angle2mask[angle]][g_weightOffset[splitDir][hIdx][wIdx][1] * GEO_WEIGHT_MASK_SIZE + (GEO_WEIGHT_MASK_SIZE - 1 - g_weightOffset[splitDir][hIdx][wIdx][0])];
+  }
+  else
+  {
+    stepY = (GEO_WEIGHT_MASK_SIZE << scaleY) - pu.lwidth();
+    weight = &g_globalGeoWeights[g_angle2mask[angle]][g_weightOffset[splitDir][hIdx][wIdx][1] * GEO_WEIGHT_MASK_SIZE + g_weightOffset[splitDir][hIdx][wIdx][0]];
+  }
+  for( int y = 0; y < height; y++ )
+  {
+    for( int x = 0; x < width; x++ )
+    {
+      *dst++  = ClipPel(rightShift((*weight*(*src0++) + ((8 - *weight) * (*src1++)) + offsetWeighted), shiftWeighted), clipRng);
+      weight += stepX;
+    }
+    dst    += strideDst;
+    src0   += strideSrc0;
+    src1   += strideSrc1;
+    weight += stepY;
+  }
+}
+#endif
 
 /**
  * \brief turn on SIMD fuc
diff --git a/source/Lib/CommonLib/InterpolationFilter.h b/source/Lib/CommonLib/InterpolationFilter.h
index db2a08463..c166395af 100644
--- a/source/Lib/CommonLib/InterpolationFilter.h
+++ b/source/Lib/CommonLib/InterpolationFilter.h
@@ -83,8 +83,13 @@ public:
   template<int N>
   void filterVer(const ClpRng& clpRng, Pel const* src, int srcStride, Pel *dst, int dstStride, int width, int height, bool isFirst, bool isLast, TFilterCoeff const *coeff, bool biMCForDMVR);
 
+#if !JVET_Q0806
   static void xWeightedTriangleBlk(const PredictionUnit &pu, const uint32_t width, const uint32_t height, const ComponentID compIdx, const bool splitDir, PelUnitBuf& predDst, PelUnitBuf& predSrc0, PelUnitBuf& predSrc1);
   void weightedTriangleBlk(const PredictionUnit &pu, const uint32_t width, const uint32_t height, const ComponentID compIdx, const bool splitDir, PelUnitBuf& predDst, PelUnitBuf& predSrc0, PelUnitBuf& predSrc1);
+#else
+  static void xWeightedGeoBlk(const PredictionUnit &pu, const uint32_t width, const uint32_t height, const ComponentID compIdx, const uint8_t splitDir, PelUnitBuf& predDst, PelUnitBuf& predSrc0, PelUnitBuf& predSrc1);
+  void weightedGeoBlk(const PredictionUnit &pu, const uint32_t width, const uint32_t height, const ComponentID compIdx, const uint8_t splitDir, PelUnitBuf& predDst, PelUnitBuf& predSrc0, PelUnitBuf& predSrc1);
+#endif
 protected:
 #if JVET_J0090_MEMORY_BANDWITH_MEASURE
   static CacheModel* m_cacheModel;
@@ -95,7 +100,11 @@ public:
   void( *m_filterHor[3][2][2] )( const ClpRng& clpRng, Pel const *src, int srcStride, Pel *dst, int dstStride, int width, int height, TFilterCoeff const *coeff, bool biMCForDMVR);
   void( *m_filterVer[3][2][2] )( const ClpRng& clpRng, Pel const *src, int srcStride, Pel *dst, int dstStride, int width, int height, TFilterCoeff const *coeff, bool biMCForDMVR);
   void( *m_filterCopy[2][2] )  ( const ClpRng& clpRng, Pel const *src, int srcStride, Pel *dst, int dstStride, int width, int height, bool biMCForDMVR);
+#if !JVET_Q0806
   void( *m_weightedTriangleBlk )(const PredictionUnit &pu, const uint32_t width, const uint32_t height, const ComponentID compIdx, const bool splitDir, PelUnitBuf& predDst, PelUnitBuf& predSrc0, PelUnitBuf& predSrc1);
+#else
+  void( *m_weightedGeoBlk )(const PredictionUnit &pu, const uint32_t width, const uint32_t height, const ComponentID compIdx, const uint8_t splitDir, PelUnitBuf& predDst, PelUnitBuf& predSrc0, PelUnitBuf& predSrc1);
+#endif
 
   void initInterpolationFilter( bool enable );
 #ifdef TARGET_SIMD_X86
diff --git a/source/Lib/CommonLib/RdCost.cpp b/source/Lib/CommonLib/RdCost.cpp
index 7f48f5d5e..e13492270 100644
--- a/source/Lib/CommonLib/RdCost.cpp
+++ b/source/Lib/CommonLib/RdCost.cpp
@@ -201,6 +201,10 @@ void RdCost::init()
 
   m_afpDistortFunc[DF_SAD_INTERMEDIATE_BITDEPTH] = RdCost::xGetSAD;
 
+#if JVET_Q0806
+  m_afpDistortFunc[DF_SAD_WITH_MASK] = RdCost::xGetSADwMask;
+#endif
+
 #if ENABLE_SIMD_OPT_DIST
 #ifdef TARGET_SIMD_X86
   initRdCostX86();
@@ -314,6 +318,15 @@ void RdCost::setDistParam( DistParam &rcDP, const CPelBuf &org, const Pel* piRef
       rcDP.subShift = 1;
     }
   }
+#if JVET_Q0806
+  else if( subShiftMode == 3 )
+  {
+    if (rcDP.org.height > 8 )
+    {
+      rcDP.subShift = 1;
+    }
+  }
+#endif
 }
 
 void RdCost::setDistParam( DistParam &rcDP, const CPelBuf &org, const CPelBuf &cur, int bitDepth, ComponentID compID, bool useHadamard )
@@ -3448,4 +3461,69 @@ Distortion RdCost::xGetMRHADs( const DistParam &rcDtParam )
 
   return m_afpDistortFunc[DF_HAD]( modDistParam );
 }
+
+#if JVET_Q0806
+void RdCost::setDistParam( DistParam &rcDP, const CPelBuf &org, const Pel* piRefY, int iRefStride, const Pel* mask, int iMaskStride, int stepX, int iMaskStride2, int bitDepth, ComponentID compID)
+{
+  rcDP.bitDepth     = bitDepth;
+  rcDP.compID       = compID;
+
+  // set Original & Curr Pointer / Stride
+  rcDP.org          = org;
+  rcDP.cur.buf      = piRefY;
+  rcDP.cur.stride   = iRefStride;
+
+  // set Mask
+  rcDP.mask         = mask;
+  rcDP.maskStride   = iMaskStride;
+  rcDP.stepX = stepX;
+  rcDP.maskStride2 = iMaskStride2;
+
+  // set Block Width / Height
+  rcDP.cur.width    = org.width;
+  rcDP.cur.height   = org.height;
+  rcDP.maximumDistortionForEarlyExit = std::numeric_limits<Distortion>::max();
+
+  // set Cost function for motion estimation with Mask
+  rcDP.distFunc = m_afpDistortFunc[ DF_SAD_WITH_MASK ];
+}
+
+Distortion RdCost::xGetSADwMask( const DistParam& rcDtParam )
+{
+  if ( rcDtParam.applyWeight )
+  {
+    return RdCostWeightPrediction::xGetSADw( rcDtParam );
+  }
+
+  const Pel* org           = rcDtParam.org.buf;
+  const Pel* cur           = rcDtParam.cur.buf;
+  const Pel* mask          = rcDtParam.mask;
+  const int  cols           = rcDtParam.org.width;
+  int        rows           = rcDtParam.org.height;
+  const int  subShift       = rcDtParam.subShift;
+  const int  subStep        = ( 1 << subShift);
+  const int  strideCur      = rcDtParam.cur.stride * subStep;
+  const int  strideOrg      = rcDtParam.org.stride * subStep;
+  const int  strideMask     = rcDtParam.maskStride * subStep;
+  const int  stepX = rcDtParam.stepX;
+  const int  strideMask2 = rcDtParam.maskStride2;
+  const uint32_t distortionShift = DISTORTION_PRECISION_ADJUSTMENT(rcDtParam.bitDepth);
+
+  Distortion sum = 0;
+  for (; rows != 0; rows -= subStep)
+  {
+    for (int n = 0; n < cols; n++)
+    {
+      sum += abs(org[n] - cur[n]) * *mask;
+      mask += stepX;
+    }
+    org += strideOrg;
+    cur += strideCur;
+    mask += strideMask;
+    mask += strideMask2;
+  }
+  sum <<= subShift;
+  return (sum >> distortionShift );
+}
+#endif
 //! \}
diff --git a/source/Lib/CommonLib/RdCost.h b/source/Lib/CommonLib/RdCost.h
index a7aef6fa7..7d10e131a 100644
--- a/source/Lib/CommonLib/RdCost.h
+++ b/source/Lib/CommonLib/RdCost.h
@@ -71,6 +71,12 @@ public:
   CPelBuf               cur;
 #if WCG_EXT
   CPelBuf               orgLuma;
+#endif
+#if JVET_Q0806
+  const Pel*            mask;
+  int                   maskStride;
+  int                   stepX;
+  int                   maskStride2;
 #endif
   int                   step;
   FpDistFunc            distFunc;
@@ -90,7 +96,14 @@ public:
   int                   cShiftX;
   int                   cShiftY;
   DistParam() :
-  org(), cur(), step( 1 ), bitDepth( 0 ), useMR( false ), applyWeight( false ), isBiPred( false ), wpCur( nullptr ), compID( MAX_NUM_COMPONENT ), maximumDistortionForEarlyExit( std::numeric_limits<Distortion>::max() ), subShift( 0 )
+  org(), cur(), 
+#if JVET_Q0806
+  mask( nullptr ),
+  maskStride( 0 ),
+  stepX(0),
+  maskStride2(0),
+#endif
+  step( 1 ), bitDepth( 0 ), useMR( false ), applyWeight( false ), isBiPred( false ), wpCur( nullptr ), compID( MAX_NUM_COMPONENT ), maximumDistortionForEarlyExit( std::numeric_limits<Distortion>::max() ), subShift( 0 )
   , cShiftX(-1), cShiftY(-1)
   { }
 };
@@ -167,6 +180,9 @@ public:
   void           setDistParam( DistParam &rcDP, const CPelBuf &org, const Pel* piRefY , int iRefStride, int bitDepth, ComponentID compID, int subShiftMode = 0, int step = 1, bool useHadamard = false );
   void           setDistParam( DistParam &rcDP, const CPelBuf &org, const CPelBuf &cur, int bitDepth, ComponentID compID, bool useHadamard = false );
   void           setDistParam( DistParam &rcDP, const Pel* pOrg, const Pel* piRefY, int iOrgStride, int iRefStride, int bitDepth, ComponentID compID, int width, int height, int subShiftMode = 0, int step = 1, bool useHadamard = false, bool bioApplied = false );
+#if JVET_Q0806
+  void           setDistParam( DistParam &rcDP, const CPelBuf &org, const Pel* piRefY, int iRefStride, const Pel* mask, int iMaskStride, int stepX, int iMaskStride2, int bitDepth,  ComponentID compID);
+#endif
 
   double         getMotionLambda          ( )  { return m_dLambdaMotionSAD; }
   void           selectMotionLambda       ( )  { m_motionLambda = getMotionLambda( ); }
@@ -351,6 +367,9 @@ private:
   static Distortion xGetSAD48         ( const DistParam& pcDtParam );
 
   static Distortion xGetSAD_full      ( const DistParam& pcDtParam );
+#if JVET_Q0806
+  static Distortion xGetSADwMask      ( const DistParam& pcDtParam );
+#endif
 
   static Distortion xGetMRSAD         ( const DistParam& pcDtParam );
   static Distortion xGetMRSAD4        ( const DistParam& pcDtParam );
@@ -389,6 +408,11 @@ private:
 
   template<X86_VEXT vext>
   static Distortion xGetHADs_SIMD   ( const DistParam& pcDtParam );
+
+#if JVET_Q0806
+  template< X86_VEXT vext >
+  static Distortion xGetSADwMask_SIMD( const DistParam& pcDtParam );
+#endif
 #endif
 
 public:
@@ -399,6 +423,9 @@ public:
   Distortion   getDistPart( const CPelBuf &org, const CPelBuf &cur, int bitDepth, const ComponentID compID, DFunc eDFunc );
 #endif
 
+#if JVET_Q0806
+  Distortion   getDistPart(const CPelBuf &org, const CPelBuf &cur, const Pel* mask, int bitDepth, const ComponentID compID, DFunc eDFunc);
+#endif
 };// END CLASS DEFINITION RdCost
 
 //! \}
diff --git a/source/Lib/CommonLib/Rom.cpp b/source/Lib/CommonLib/Rom.cpp
index 5bc2f227a..5f8019a30 100644
--- a/source/Lib/CommonLib/Rom.cpp
+++ b/source/Lib/CommonLib/Rom.cpp
@@ -403,6 +403,7 @@ void initROM()
     }
   }
 
+#if !JVET_Q0806
   for( int idxH = MAX_CU_DEPTH - MIN_CU_LOG2; idxH >= 0; --idxH )
   {
     for( int idxW = MAX_CU_DEPTH - MIN_CU_LOG2; idxW >= 0; --idxW )
@@ -446,6 +447,9 @@ void initROM()
       }
     }
   }
+#else
+  initGeoTemplate();
+#endif
 
   ::memset(g_isReusedUniMVsFilled, 0, sizeof(g_isReusedUniMVsFilled));
 }
@@ -473,6 +477,7 @@ void destroyROM()
   delete gp_sizeIdxInfo;
   gp_sizeIdxInfo = nullptr;
 
+#if !JVET_Q0806
   for (int idxH = 0; idxH < MAX_CU_DEPTH - MIN_CU_LOG2 + 2; ++idxH)
   {
     for (int idxW = 0; idxW < MAX_CU_DEPTH - MIN_CU_LOG2 + 2; ++idxW)
@@ -483,6 +488,21 @@ void destroyROM()
       g_triangleWeights[1][idxH][idxW] = nullptr;
     }
   }
+#else
+  for( int modeIdx = 0; modeIdx < GEO_NUM_PARTITION_MODE; modeIdx++ )
+  {
+    delete[] g_GeoParams[modeIdx];
+    g_GeoParams[modeIdx] = nullptr;
+  }
+  delete[] g_GeoParams;
+  for( int i = 0; i < GEO_NUM_PRESTORED_MASK; i++ )
+  {
+    delete[] g_globalGeoWeights   [i];
+    delete[] g_globalGeoEncSADmask[i];
+    g_globalGeoWeights   [i] = nullptr;
+    g_globalGeoEncSADmask[i] = nullptr;
+  }
+#endif
 }
 
 // ====================================================================================================================
@@ -708,12 +728,98 @@ const uint32_t g_scalingListSize [SCALING_LIST_SIZE_NUM] = { 1, 4, 16, 64, 256,
 const uint32_t g_scalingListSizeX[SCALING_LIST_SIZE_NUM] = { 1, 2,  4,  8,  16,   32,   64,   128 };
 
 
+#if !JVET_Q0806
 uint8_t g_triangleMvStorage[TRIANGLE_DIR_NUM][MAX_CU_DEPTH - MIN_CU_LOG2 + 1][MAX_CU_DEPTH - MIN_CU_LOG2 + 1][MAX_CU_SIZE >> MIN_CU_LOG2][MAX_CU_SIZE >> MIN_CU_LOG2];
 int16_t *g_triangleWeights[TRIANGLE_DIR_NUM][MAX_CU_DEPTH - MIN_CU_LOG2 + 2][MAX_CU_DEPTH - MIN_CU_LOG2 + 2];
+#endif
+
 Mv   g_reusedUniMVs[32][32][8][8][2][33];
 bool g_isReusedUniMVsFilled[32][32][8][8];
 
 const uint8_t g_paletteQuant[52] = { 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 7, 7, 8, 9, 9, 10, 11, 12, 13, 14, 15, 16, 17, 19, 21, 22, 24, 23, 25, 26, 28, 29, 31, 32, 34, 36, 37, 39, 41, 42, 45 };
 uint8_t g_paletteRunTopLut [5] = { 0, 1, 1, 2, 2 };
 uint8_t g_paletteRunLeftLut[5] = { 0, 1, 2, 3, 4 };
+
+#if JVET_Q0806
+void initGeoTemplate()
+{
+  g_GeoParams = new int16_t*[GEO_NUM_PARTITION_MODE];
+  int modeIdx = 0;
+  for( int angleIdx = 0; angleIdx < GEO_NUM_ANGLES; angleIdx++ )
+  {
+    for( int distanceIdx = 0; distanceIdx < GEO_NUM_DISTANCES; distanceIdx++ )
+    {
+      if( (distanceIdx == 0 && angleIdx >= 16)
+        || ((distanceIdx == 2 || distanceIdx == 0) && (g_angle2mask[angleIdx] == 0 || g_angle2mask[angleIdx] == 5))
+        || g_angle2mask[angleIdx] == -1 )
+        continue;
+      g_GeoParams[modeIdx]    = new int16_t[2];
+      g_GeoParams[modeIdx][0] = (int16_t)angleIdx;
+      g_GeoParams[modeIdx][1] = (int16_t)distanceIdx;
+      modeIdx++;
+    }
+  }
+  for (int angleIdx = 0; angleIdx < (GEO_NUM_ANGLES >> 2) + 1; angleIdx++)
+  {
+    if (g_angle2mask[angleIdx] == -1)
+      continue;
+    g_globalGeoWeights[g_angle2mask[angleIdx]] = new int16_t[GEO_WEIGHT_MASK_SIZE * GEO_WEIGHT_MASK_SIZE];
+    g_globalGeoEncSADmask[g_angle2mask[angleIdx]] = new int16_t[GEO_WEIGHT_MASK_SIZE * GEO_WEIGHT_MASK_SIZE];
+  
+    int distanceX = angleIdx;
+    int distanceY = (distanceX + (GEO_NUM_ANGLES >> 2)) % GEO_NUM_ANGLES;
+    int16_t rho = (g_Dis[distanceX] << (GEO_MAX_CU_LOG2+1)) + (g_Dis[distanceY] << (GEO_MAX_CU_LOG2 + 1));
+    static const int16_t maskOffset = (2*GEO_MAX_CU_SIZE - GEO_WEIGHT_MASK_SIZE) >> 1;
+    int index = 0;
+    for( int y = 0; y < GEO_WEIGHT_MASK_SIZE; y++ )
+    {
+      int16_t lookUpY = (((y + maskOffset) << 1) + 1) * g_Dis[distanceY];
+      for( int x = 0; x < GEO_WEIGHT_MASK_SIZE; x++, index++ )
+      {
+        int16_t sx_i = ((x + maskOffset) << 1) + 1;
+        int16_t weightIdx = sx_i * g_Dis[distanceX] + lookUpY - rho;
+        int weightLinearIdx = 32 + weightIdx;
+        g_globalGeoWeights[g_angle2mask[angleIdx]][index] = Clip3(0, 8, (weightLinearIdx + 4) >> 3);
+        g_globalGeoEncSADmask[g_angle2mask[angleIdx]][index] = weightIdx > 0 ? 1 : 0;
+      }
+    }
+  }
+
+  for( int hIdx = 0; hIdx < GEO_NUM_CU_SIZE; hIdx++ )
+  {
+    int16_t height = 1 << ( hIdx + GEO_MIN_CU_LOG2);
+    for( int wIdx = 0; wIdx < GEO_NUM_CU_SIZE; wIdx++ )
+    {
+      int16_t width = 1 << (wIdx + GEO_MIN_CU_LOG2);
+      for( int splitDir = 0; splitDir < GEO_NUM_PARTITION_MODE; splitDir++ )
+      {
+        int16_t angle         = g_GeoParams[splitDir][0];
+        int16_t distance      = g_GeoParams[splitDir][1];
+        int16_t offsetX       = (GEO_WEIGHT_MASK_SIZE - width) >> 1;
+        int16_t offsetY       = (GEO_WEIGHT_MASK_SIZE - height) >> 1;
+        if( distance > 0 )
+        {
+          if( angle % 16 == 8 || (angle % 16 != 0 && height >= width) )
+          {
+            offsetY += angle < 16 ? ((distance * (int32_t)height) >> 3) : -((distance * (int32_t)height) >> 3);
+          }
+          else
+          {
+            offsetX += angle < 16 ? ((distance * (int32_t)width) >> 3) : -((distance * (int32_t)width) >> 3);
+          }
+        }
+        g_weightOffset[splitDir][hIdx][wIdx][0] = offsetX;
+        g_weightOffset[splitDir][hIdx][wIdx][1] = offsetY;
+      }
+    }
+  }
+}
+int16_t** g_GeoParams;
+int16_t*  g_globalGeoWeights   [GEO_NUM_PRESTORED_MASK];
+int16_t*  g_globalGeoEncSADmask[GEO_NUM_PRESTORED_MASK];
+int16_t   g_weightOffset       [GEO_NUM_PARTITION_MODE][GEO_NUM_CU_SIZE][GEO_NUM_CU_SIZE][2];
+int8_t    g_angle2mask[GEO_NUM_ANGLES] = { 0, -1, 1, 2, 3, 4, -1, -1, 5, -1, -1, 4, 3, 2, 1, -1, 0, -1, 1, 2, 3, 4, -1, -1, 5, -1, -1, 4, 3, 2, 1, -1 };
+int8_t    g_Dis[GEO_NUM_ANGLES] = { 8, 8, 8, 8, 4, 4, 2, 1, 0, -1, -2, -4, -4, -8, -8, -8, -8, -8, -8, -8, -4, -4, -2, -1, 0, 1, 2, 4, 4, 8, 8, 8 };
+int8_t    g_angle2mirror[GEO_NUM_ANGLES] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 2 };
+#endif
 //! \}
diff --git a/source/Lib/CommonLib/Rom.h b/source/Lib/CommonLib/Rom.h
index 929a75547..010891cb1 100644
--- a/source/Lib/CommonLib/Rom.h
+++ b/source/Lib/CommonLib/Rom.h
@@ -205,9 +205,12 @@ constexpr uint8_t g_tbMax[257] = { 0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3,
 
 //! \}
 
+#if !JVET_Q0806
 extern       uint8_t g_triangleMvStorage[TRIANGLE_DIR_NUM][MAX_CU_DEPTH - MIN_CU_LOG2 + 1][MAX_CU_DEPTH - MIN_CU_LOG2 + 1][MAX_CU_SIZE >> MIN_CU_LOG2][MAX_CU_SIZE >> MIN_CU_LOG2];
 // 7-tap/3-tap, direction, 2/4/8/16/32/64/128
 extern int16_t *g_triangleWeights[TRIANGLE_DIR_NUM][MAX_CU_DEPTH - MIN_CU_LOG2 + 2][MAX_CU_DEPTH - MIN_CU_LOG2 + 2];
+#endif
+
 extern bool g_mctsDecCheckEnabled;
 
 class  Mv;
@@ -220,5 +223,15 @@ extern uint8_t g_paletteRunLeftLut[5];
 
 const int g_IBCBufferSize = 256 * 128;
 
+#if JVET_Q0806
+void initGeoTemplate();
+extern int16_t** g_GeoParams;
+extern int16_t*  g_globalGeoWeights   [GEO_NUM_PRESTORED_MASK];
+extern int16_t*  g_globalGeoEncSADmask[GEO_NUM_PRESTORED_MASK];
+extern int16_t   g_weightOffset       [GEO_NUM_PARTITION_MODE][GEO_NUM_CU_SIZE][GEO_NUM_CU_SIZE][2];
+extern int8_t    g_angle2mask         [GEO_NUM_ANGLES];
+extern int8_t    g_Dis[GEO_NUM_ANGLES];
+extern int8_t    g_angle2mirror[GEO_NUM_ANGLES];
+#endif
 #endif  //__TCOMROM__
 
diff --git a/source/Lib/CommonLib/Slice.cpp b/source/Lib/CommonLib/Slice.cpp
index e923fdf52..cda889eed 100644
--- a/source/Lib/CommonLib/Slice.cpp
+++ b/source/Lib/CommonLib/Slice.cpp
@@ -1575,7 +1575,11 @@ PicHeader::PicHeader()
 , m_disBdofFlag                                   ( 0 )
 , m_disDmvrFlag                                   ( 0 )
 , m_disProfFlag                                   ( 0 )
+#if !JVET_Q0806
 , m_maxNumTriangleCand                            ( 0 )
+#else
+, m_maxNumGeoCand                                 ( 0 )
+#endif
 , m_maxNumIBCMergeCand                            ( IBC_MRG_MAX_NUM_CANDS )
 , m_jointCbCrSignFlag                             ( 0 )
 , m_saoEnabledPresentFlag                         ( 0 )
@@ -1665,7 +1669,11 @@ void PicHeader::initPicHeader()
   m_disBdofFlag                                   = 0;
   m_disDmvrFlag                                   = 0;
   m_disProfFlag                                   = 0;
+#if !JVET_Q0806
   m_maxNumTriangleCand                            = 0;
+#else
+  m_maxNumGeoCand                                 = 0;
+#endif
   m_maxNumIBCMergeCand                            = IBC_MRG_MAX_NUM_CANDS;
   m_jointCbCrSignFlag                             = 0;
   m_saoEnabledPresentFlag                         = 0;
@@ -1805,7 +1813,11 @@ SPS::SPS()
 , m_AffineType                ( false )
 , m_PROF                      ( false )
 , m_ciip                   ( false )
+#if !JVET_Q0806
 , m_Triangle                  ( false )
+#else
+, m_Geo                       ( false )
+#endif
 #if LUMA_ADAPTIVE_DEBLOCKING_FILTER_QP_OFFSET
 , m_LadfEnabled               ( false )
 , m_LadfNumIntervals          ( 0 )
@@ -2020,7 +2032,11 @@ PPS::PPS()
 , m_PPSMvdL1ZeroIdc                  (0)
 , m_PPSCollocatedFromL0Idc           (0)
 , m_PPSSixMinusMaxNumMergeCandPlus1  (0)
+#if !JVET_Q0806
 , m_PPSMaxNumMergeCandMinusMaxNumTriangleCandPlus1 (0)
+#else
+, m_PPSMaxNumMergeCandMinusMaxNumGeoCandPlus1 (0)
+#endif
 , m_cabacInitPresentFlag             (false)
 , m_pictureHeaderExtensionPresentFlag(0)
 , m_sliceHeaderExtensionPresentFlag  (false)
diff --git a/source/Lib/CommonLib/Slice.h b/source/Lib/CommonLib/Slice.h
index b95485113..b46f2e01a 100644
--- a/source/Lib/CommonLib/Slice.h
+++ b/source/Lib/CommonLib/Slice.h
@@ -256,7 +256,11 @@ class ConstraintInfo
   bool              m_noIbcConstraintFlag;
   bool              m_noCiipConstraintFlag;
   bool              m_noFPelMmvdConstraintFlag;
+#if !JVET_Q0806
   bool              m_noTriangleConstraintFlag;
+#else
+  bool              m_noGeoConstraintFlag;
+#endif
   bool              m_noLadfConstraintFlag;
   bool              m_noTransformSkipConstraintFlag;
   bool              m_noBDPCMConstraintFlag;
@@ -300,7 +304,11 @@ public:
     , m_noIbcConstraintFlag      (false)
     , m_noCiipConstraintFlag  (false)
     , m_noFPelMmvdConstraintFlag (false)
+#if !JVET_Q0806
     , m_noTriangleConstraintFlag (false)
+#else
+    , m_noGeoConstraintFlag      (false)
+#endif
     , m_noLadfConstraintFlag     (false)
     , m_noTransformSkipConstraintFlag(false)
     , m_noBDPCMConstraintFlag    (false)
@@ -383,8 +391,13 @@ public:
   void          setNoCiipConstraintFlag(bool bVal) { m_noCiipConstraintFlag = bVal; }
   bool          getNoFPelMmvdConstraintFlag() const { return m_noFPelMmvdConstraintFlag; }
   void          setNoFPelMmvdConstraintFlag(bool bVal) { m_noFPelMmvdConstraintFlag = bVal; }
+#if !JVET_Q0806
   bool          getNoTriangleConstraintFlag() const { return m_noTriangleConstraintFlag; }
   void          setNoTriangleConstraintFlag(bool bVal) { m_noTriangleConstraintFlag = bVal; }
+#else
+  bool          getNoGeoConstraintFlag() const { return m_noGeoConstraintFlag; }
+  void          setNoGeoConstraintFlag(bool bVal) { m_noGeoConstraintFlag = bVal; }
+#endif
   bool          getNoLadfConstraintFlag() const { return m_noLadfConstraintFlag; }
   void          setNoLadfConstraintFlag(bool bVal) { m_noLadfConstraintFlag = bVal; }
   bool          getNoTransformSkipConstraintFlag() const { return m_noTransformSkipConstraintFlag; }
@@ -1150,7 +1163,11 @@ private:
   bool              m_PROF;
   bool              m_bcw;                        //
   bool              m_ciip;
+#if !JVET_Q0806
   bool              m_Triangle;
+#else
+  bool              m_Geo;
+#endif
 #if LUMA_ADAPTIVE_DEBLOCKING_FILTER_QP_OFFSET
   bool              m_LadfEnabled;
   int               m_LadfNumIntervals;
@@ -1445,8 +1462,13 @@ public:
 
   void      setUseCiip         ( bool b )                                        { m_ciip = b; }
   bool      getUseCiip         ()                                      const     { return m_ciip; }
+#if !JVET_Q0806
   void      setUseTriangle        ( bool b )                                        { m_Triangle = b; }
   bool      getUseTriangle        ()                                      const     { return m_Triangle; }
+#else
+  void      setUseGeo             ( bool b )                                        { m_Geo = b; }
+  bool      getUseGeo             ()                                      const     { return m_Geo; }
+#endif
   void      setUseMRL             ( bool b )                                        { m_MRL = b; }
   bool      getUseMRL             ()                                      const     { return m_MRL; }
   void      setUseMIP             ( bool b )                                        { m_MIP = b; }
@@ -1577,7 +1599,11 @@ private:
   int               m_PPSMvdL1ZeroIdc;
   int               m_PPSCollocatedFromL0Idc;
   uint32_t          m_PPSSixMinusMaxNumMergeCandPlus1;
+#if !JVET_Q0806
   uint32_t          m_PPSMaxNumMergeCandMinusMaxNumTriangleCandPlus1;
+#else
+  uint32_t          m_PPSMaxNumMergeCandMinusMaxNumGeoCandPlus1;
+#endif
 
   bool             m_cabacInitPresentFlag;
 
@@ -1785,8 +1811,13 @@ public:
   void                    setPPSCollocatedFromL0Idc(int u)                                { m_PPSCollocatedFromL0Idc = u;                 }
   uint32_t                getPPSSixMinusMaxNumMergeCandPlus1() const                      { return m_PPSSixMinusMaxNumMergeCandPlus1;     }
   void                    setPPSSixMinusMaxNumMergeCandPlus1(uint32_t u)                  { m_PPSSixMinusMaxNumMergeCandPlus1 = u;        }
+#if !JVET_Q0806
   uint32_t                getPPSMaxNumMergeCandMinusMaxNumTriangleCandPlus1() const       { return m_PPSMaxNumMergeCandMinusMaxNumTriangleCandPlus1; }
   void                    setPPSMaxNumMergeCandMinusMaxNumTriangleCandPlus1(uint32_t u)   { m_PPSMaxNumMergeCandMinusMaxNumTriangleCandPlus1 = u; }
+#else
+  uint32_t                getPPSMaxNumMergeCandMinusMaxNumGeoCandPlus1() const            { return m_PPSMaxNumMergeCandMinusMaxNumGeoCandPlus1; }
+  void                    setPPSMaxNumMergeCandMinusMaxNumGeoCandPlus1(uint32_t u)        { m_PPSMaxNumMergeCandMinusMaxNumGeoCandPlus1 = u; }
+#endif
 
   void                   setCabacInitPresentFlag( bool flag )                             { m_cabacInitPresentFlag = flag;                }
   bool                   getCabacInitPresentFlag() const                                  { return m_cabacInitPresentFlag;                }
@@ -1923,7 +1954,11 @@ private:
   bool                        m_disBdofFlag;                                            //!< picture level BDOF disable flag
   bool                        m_disDmvrFlag;                                            //!< picture level DMVR disable flag
   bool                        m_disProfFlag;                                            //!< picture level PROF disable flag
+#if !JVET_Q0806
   uint32_t                    m_maxNumTriangleCand;                                     //!< max number of triangle merge candidates
+#else
+  uint32_t                    m_maxNumGeoCand;                                          //!< max number of geometric merge candidates
+#endif
   uint32_t                    m_maxNumIBCMergeCand;                                     //!< max number of IBC merge candidates
   bool                        m_jointCbCrSignFlag;                                      //!< joint Cb/Cr residual sign flag  
   bool                        m_saoEnabledPresentFlag;                                  //!< sao enabled flags present in the picture header
@@ -2036,8 +2071,13 @@ public:
   bool                        getDisDmvrFlag() const                                    { return m_disDmvrFlag;                                                                        }
   void                        setDisProfFlag( bool val )                                { m_disProfFlag = val;                                                                         }
   bool                        getDisProfFlag() const                                    { return m_disProfFlag;                                                                        }
+#if !JVET_Q0806
   void                        setMaxNumTriangleCand(uint32_t b)                         { m_maxNumTriangleCand = b;                                                                    }
   uint32_t                    getMaxNumTriangleCand() const                             { return m_maxNumTriangleCand;                                                                 }
+#else
+  void                        setMaxNumGeoCand(uint32_t b)                              { m_maxNumGeoCand = b; }
+  uint32_t                    getMaxNumGeoCand() const                                  { return m_maxNumGeoCand; }
+#endif
   void                        setMaxNumIBCMergeCand( uint32_t b )                       { m_maxNumIBCMergeCand = b;                                                                    }
   uint32_t                    getMaxNumIBCMergeCand() const                             { return m_maxNumIBCMergeCand;                                                                 } 
   void                        setJointCbCrSignFlag( bool b )                            { m_jointCbCrSignFlag = b;                                                                     }
diff --git a/source/Lib/CommonLib/TypeDef.h b/source/Lib/CommonLib/TypeDef.h
index fbcf3e131..a643ffc40 100644
--- a/source/Lib/CommonLib/TypeDef.h
+++ b/source/Lib/CommonLib/TypeDef.h
@@ -78,6 +78,9 @@
 #define JVET_Q0483_CLIP_TMVP                              1 // JVET-Q0483: Clip TMVP when no scaling is applied
 
 #define JVET_Q0516_MTS_SIGNALLING_DC_ONLY_COND            1 // JVET-Q0516/Q0685: disable MTS when there is only DC coefficient 
+
+#define JVET_Q0806                                        1 // Geo related adoptions (JVET-Q0059, JVET-Q0077, JVET-Q0123, JVET-Q0188, JVET-Q0242_GEO, JVET-Q0309, JVET-Q0365 and JVET-Q0370)
+
 #define JVET_Q0055_MTS_SIGNALLING                         1 // JVET-Q0055: Check for transform coefficients outside the 16x16 area
 #define JVET_Q0480_RASTER_RECT_SLICES                     1 // JVET-Q0480: Eliminate redundant slice height syntax when in raster rectangular slice mode (tile_idx_delta_present_flag == 0)
 
@@ -582,7 +585,12 @@ enum DFunc
 
   DF_SAD_INTERMEDIATE_BITDEPTH = 63,
 
+#if JVET_Q0806
+  DF_SAD_WITH_MASK   = 64,
+  DF_TOTAL_FUNCTIONS = 65
+#else
   DF_TOTAL_FUNCTIONS = 64
+#endif
 };
 
 /// motion vector predictor direction used in AMVP
@@ -860,12 +868,14 @@ enum MergeType
   NUM_MRG_TYPE                   // 5
 };
 
+#if !JVET_Q0806
 enum TriangleSplit
 {
   TRIANGLE_DIR_135 = 0,
   TRIANGLE_DIR_45,
   TRIANGLE_DIR_NUM
 };
+#endif
 
 //////////////////////////////////////////////////////////////////////////
 // Encoder modes to try out
diff --git a/source/Lib/CommonLib/Unit.cpp b/source/Lib/CommonLib/Unit.cpp
index 041b241b8..8a6d7c8d6 100644
--- a/source/Lib/CommonLib/Unit.cpp
+++ b/source/Lib/CommonLib/Unit.cpp
@@ -266,7 +266,11 @@ CodingUnit& CodingUnit::operator=( const CodingUnit& other )
   affine            = other.affine;
   affineType        = other.affineType;
   colorTransform = other.colorTransform;
+#if !JVET_Q0806
   triangle          = other.triangle;
+#else
+  geoFlag           = other.geoFlag;
+#endif
   bdpcmMode         = other.bdpcmMode;
   bdpcmModeChroma   = other.bdpcmModeChroma;
   qp                = other.qp;
@@ -326,7 +330,11 @@ void CodingUnit::initData()
   affine            = false;
   affineType        = 0;
   colorTransform = false;
+#if !JVET_Q0806
   triangle          = false;
+#else
+  geoFlag           = false;
+#endif
   bdpcmMode         = 0;
   bdpcmModeChroma   = 0;
   qp                = 0;
@@ -458,10 +466,12 @@ const uint8_t CodingUnit::checkAllowedSbt() const
   {
     return 0;
   }
+#if !JVET_Q0806
   if( triangle )
   {
     return 0;
   }
+#endif
 
   uint8_t sbtAllowed = 0;
   int cuWidth  = lwidth();
@@ -528,9 +538,15 @@ void PredictionUnit::initData()
   mergeFlag   = false;
   regularMergeFlag = false;
   mergeIdx    = MAX_UCHAR;
+#if !JVET_Q0806
   triangleSplitDir  = MAX_UCHAR;
   triangleMergeIdx0 = MAX_UCHAR;
   triangleMergeIdx1 = MAX_UCHAR;
+#else
+  geoSplitDir  = MAX_UCHAR;
+  geoMergeIdx0 = MAX_UCHAR;
+  geoMergeIdx1 = MAX_UCHAR;
+#endif
   mmvdMergeFlag = false;
   mmvdMergeIdx = MAX_UINT;
   interDir    = MAX_UCHAR;
@@ -579,9 +595,15 @@ PredictionUnit& PredictionUnit::operator=(const InterPredictionData& predData)
   mergeFlag   = predData.mergeFlag;
   regularMergeFlag = predData.regularMergeFlag;
   mergeIdx    = predData.mergeIdx;
+#if !JVET_Q0806
   triangleSplitDir  = predData.triangleSplitDir  ;
   triangleMergeIdx0 = predData.triangleMergeIdx0 ;
   triangleMergeIdx1 = predData.triangleMergeIdx1 ;
+#else
+  geoSplitDir  = predData.geoSplitDir;
+  geoMergeIdx0 = predData.geoMergeIdx0;
+  geoMergeIdx1 = predData.geoMergeIdx1;
+#endif
   mmvdMergeFlag = predData.mmvdMergeFlag;
   mmvdMergeIdx = predData.mmvdMergeIdx;
   interDir    = predData.interDir;
@@ -625,9 +647,15 @@ PredictionUnit& PredictionUnit::operator=( const PredictionUnit& other )
   mergeFlag   = other.mergeFlag;
   regularMergeFlag = other.regularMergeFlag;
   mergeIdx    = other.mergeIdx;
+#if !JVET_Q0806
   triangleSplitDir  = other.triangleSplitDir  ;
   triangleMergeIdx0 = other.triangleMergeIdx0 ;
   triangleMergeIdx1 = other.triangleMergeIdx1 ;
+#else
+  geoSplitDir  = other.geoSplitDir;
+  geoMergeIdx0 = other.geoMergeIdx0;
+  geoMergeIdx1 = other.geoMergeIdx1;
+#endif
   mmvdMergeFlag = other.mmvdMergeFlag;
   mmvdMergeIdx = other.mmvdMergeIdx;
   interDir    = other.interDir;
diff --git a/source/Lib/CommonLib/Unit.h b/source/Lib/CommonLib/Unit.h
index 91130a722..b8c2a30c7 100644
--- a/source/Lib/CommonLib/Unit.h
+++ b/source/Lib/CommonLib/Unit.h
@@ -309,7 +309,11 @@ struct CodingUnit : public UnitArea
   bool           affine;
   int            affineType;
   bool           colorTransform;
+#if !JVET_Q0806
   bool           triangle;
+#else
+  bool           geoFlag;
+#endif
   int            bdpcmMode;
   int            bdpcmModeChroma;
   uint8_t          imv;
@@ -383,9 +387,15 @@ struct InterPredictionData
   bool      mergeFlag;
   bool      regularMergeFlag;
   uint8_t     mergeIdx;
+#if !JVET_Q0806
   uint8_t     triangleSplitDir;
   uint8_t     triangleMergeIdx0;
   uint8_t     triangleMergeIdx1;
+#else
+  uint8_t     geoSplitDir;
+  uint8_t     geoMergeIdx0;
+  uint8_t     geoMergeIdx1;
+#endif
   bool           mmvdMergeFlag;
   uint32_t       mmvdMergeIdx;
   uint8_t     interDir;
diff --git a/source/Lib/CommonLib/UnitTools.cpp b/source/Lib/CommonLib/UnitTools.cpp
index 95e59ed1e..13dfb3c8d 100644
--- a/source/Lib/CommonLib/UnitTools.cpp
+++ b/source/Lib/CommonLib/UnitTools.cpp
@@ -244,7 +244,11 @@ void CU::saveMotionInHMVP( const CodingUnit& cu, const bool isToBeDone )
 {
   const PredictionUnit& pu = *cu.firstPU;
 
+#if !JVET_Q0806
   if (!cu.triangle && !cu.affine && !isToBeDone )
+#else
+  if (!cu.geoFlag && !cu.affine && !isToBeDone)
+#endif
   {
     MotionInfo mi = pu.getMotionInfo();
 
@@ -3138,6 +3142,7 @@ void PU::restrictBiPredMergeCandsOne(PredictionUnit &pu)
   }
 }
 
+#if !JVET_Q0806
 void PU::getTriangleMergeCandidates( const PredictionUnit &pu, MergeCtx& triangleMrgCtx )
 {
   MergeCtx tmpMergeCtx;
@@ -3294,6 +3299,167 @@ int32_t PU::mappingRefPic( const PredictionUnit &pu, int32_t refPicPoc, bool tar
   }
   return -1;
 }
+#else
+void PU::getGeoMergeCandidates( const PredictionUnit &pu, MergeCtx& geoMrgCtx )
+{
+  MergeCtx tmpMergeCtx;
+
+  const Slice &slice = *pu.cs->slice;
+  const uint32_t maxNumMergeCand = slice.getPicHeader()->getMaxNumMergeCand();
+
+  geoMrgCtx.numValidMergeCand = 0;
+
+  for (int32_t i = 0; i < GEO_MAX_NUM_UNI_CANDS; i++)
+  {
+    geoMrgCtx.BcwIdx[i] = BCW_DEFAULT;
+    geoMrgCtx.interDirNeighbours[i] = 0;
+    geoMrgCtx.mrgTypeNeighbours[i] = MRG_TYPE_DEFAULT_N;
+    geoMrgCtx.mvFieldNeighbours[(i << 1)].refIdx = NOT_VALID;
+    geoMrgCtx.mvFieldNeighbours[(i << 1) + 1].refIdx = NOT_VALID;
+    geoMrgCtx.mvFieldNeighbours[(i << 1)].mv = Mv();
+    geoMrgCtx.mvFieldNeighbours[(i << 1) + 1].mv = Mv();
+    geoMrgCtx.useAltHpelIf[i] = false;
+  }
+
+  PU::getInterMergeCandidates(pu, tmpMergeCtx, 0);
+
+  for (int32_t i = 0; i < maxNumMergeCand; i++)
+  {
+    int parity = i & 1;
+    if( tmpMergeCtx.interDirNeighbours[i] & (0x01 + parity) )
+    {
+      geoMrgCtx.interDirNeighbours[geoMrgCtx.numValidMergeCand] = 1 + parity;
+      geoMrgCtx.mrgTypeNeighbours[geoMrgCtx.numValidMergeCand] = MRG_TYPE_DEFAULT_N;
+      geoMrgCtx.mvFieldNeighbours[(geoMrgCtx.numValidMergeCand << 1) + !parity].mv = Mv(0, 0);
+      geoMrgCtx.mvFieldNeighbours[(geoMrgCtx.numValidMergeCand << 1) + parity].mv = tmpMergeCtx.mvFieldNeighbours[(i << 1) + parity].mv;
+      geoMrgCtx.mvFieldNeighbours[(geoMrgCtx.numValidMergeCand << 1) + !parity].refIdx = -1;
+      geoMrgCtx.mvFieldNeighbours[(geoMrgCtx.numValidMergeCand << 1) + parity].refIdx = tmpMergeCtx.mvFieldNeighbours[(i << 1) + parity].refIdx;
+      geoMrgCtx.numValidMergeCand++;
+      if (geoMrgCtx.numValidMergeCand == GEO_MAX_NUM_UNI_CANDS)
+      {
+        return;
+      }
+      continue;
+    }
+
+    if (tmpMergeCtx.interDirNeighbours[i] & (0x02 - parity))
+    {
+      geoMrgCtx.interDirNeighbours[geoMrgCtx.numValidMergeCand] = 2 - parity;
+      geoMrgCtx.mrgTypeNeighbours[geoMrgCtx.numValidMergeCand] = MRG_TYPE_DEFAULT_N;
+      geoMrgCtx.mvFieldNeighbours[(geoMrgCtx.numValidMergeCand << 1) + !parity].mv = tmpMergeCtx.mvFieldNeighbours[(i << 1) + !parity].mv;
+      geoMrgCtx.mvFieldNeighbours[(geoMrgCtx.numValidMergeCand << 1) + parity].mv = Mv(0, 0);
+      geoMrgCtx.mvFieldNeighbours[(geoMrgCtx.numValidMergeCand << 1) + !parity].refIdx = tmpMergeCtx.mvFieldNeighbours[(i << 1) + !parity].refIdx;
+      geoMrgCtx.mvFieldNeighbours[(geoMrgCtx.numValidMergeCand << 1) + parity].refIdx = -1;
+      geoMrgCtx.numValidMergeCand++;
+      if (geoMrgCtx.numValidMergeCand == GEO_MAX_NUM_UNI_CANDS)
+      {
+        return;
+      }
+    }
+  }
+}
+
+void PU::spanGeoMotionInfo( PredictionUnit &pu, MergeCtx &geoMrgCtx, const uint8_t splitDir, const uint8_t candIdx0, const uint8_t candIdx1)
+{
+  pu.geoSplitDir  = splitDir;
+  pu.geoMergeIdx0 = candIdx0;
+  pu.geoMergeIdx1 = candIdx1;
+  MotionBuf mb = pu.getMotionBuf();
+
+  MotionInfo biMv;
+  biMv.isInter  = true;
+  biMv.sliceIdx = pu.cs->slice->getIndependentSliceIdx();
+
+  if( geoMrgCtx.interDirNeighbours[candIdx0] == 1 && geoMrgCtx.interDirNeighbours[candIdx1] == 2 )
+  {
+    biMv.interDir  = 3;
+    biMv.mv[0]     = geoMrgCtx.mvFieldNeighbours[ candIdx0 << 1     ].mv;
+    biMv.mv[1]     = geoMrgCtx.mvFieldNeighbours[(candIdx1 << 1) + 1].mv;
+    biMv.refIdx[0] = geoMrgCtx.mvFieldNeighbours[ candIdx0 << 1     ].refIdx;
+    biMv.refIdx[1] = geoMrgCtx.mvFieldNeighbours[(candIdx1 << 1) + 1].refIdx;
+  }
+  else if( geoMrgCtx.interDirNeighbours[candIdx0] == 2 && geoMrgCtx.interDirNeighbours[candIdx1] == 1 )
+  {
+    biMv.interDir  = 3;
+    biMv.mv[0]     = geoMrgCtx.mvFieldNeighbours[ candIdx1 << 1     ].mv;
+    biMv.mv[1]     = geoMrgCtx.mvFieldNeighbours[(candIdx0 << 1) + 1].mv;
+    biMv.refIdx[0] = geoMrgCtx.mvFieldNeighbours[ candIdx1 << 1     ].refIdx;
+    biMv.refIdx[1] = geoMrgCtx.mvFieldNeighbours[(candIdx0 << 1) + 1].refIdx;
+  }
+  else if( geoMrgCtx.interDirNeighbours[candIdx0] == 1 && geoMrgCtx.interDirNeighbours[candIdx1] == 1 )
+  {
+    biMv.interDir = 1;
+    biMv.mv[0] = geoMrgCtx.mvFieldNeighbours[candIdx1 << 1].mv;
+    biMv.mv[1] = Mv(0, 0);
+    biMv.refIdx[0] = geoMrgCtx.mvFieldNeighbours[candIdx1 << 1].refIdx;
+    biMv.refIdx[1] = -1;
+  }
+  else if( geoMrgCtx.interDirNeighbours[candIdx0] == 2 && geoMrgCtx.interDirNeighbours[candIdx1] == 2 )
+  {
+    biMv.interDir = 2;
+    biMv.mv[0] = Mv(0, 0);
+    biMv.mv[1] = geoMrgCtx.mvFieldNeighbours[(candIdx1 << 1) + 1].mv;
+    biMv.refIdx[0] = -1;
+    biMv.refIdx[1] = geoMrgCtx.mvFieldNeighbours[(candIdx1 << 1) + 1].refIdx;
+  }
+
+  int16_t angle = g_GeoParams[splitDir][0];
+  int tpmMask = 0;
+  int lookUpY = 0, motionIdx = 0;
+  bool isFlip = angle >= 13 && angle <= 27;
+	int distanceIdx = g_GeoParams[splitDir][1];
+	int distanceX = angle;
+	int distanceY = (distanceX + (GEO_NUM_ANGLES >> 2)) % GEO_NUM_ANGLES;
+	int offsetX = ( - (int)pu.lwidth()) >> 1;
+	int offsetY = ( - (int)pu.lheight()) >> 1;
+	if (distanceIdx > 0)
+	{
+		if (angle % 16 == 8 || (angle % 16 != 0 && pu.lheight() >= pu.lwidth()))
+      offsetY += angle < 16 ? ((distanceIdx * pu.lheight()) >> 3) : -(int)((distanceIdx * pu.lheight()) >> 3);
+		else
+      offsetX += angle < 16 ? ((distanceIdx * pu.lwidth()) >> 3) : -(int)((distanceIdx * pu.lwidth()) >> 3);
+	}
+  for (int y = 0; y < mb.height; y++)
+  {
+		lookUpY = (((4*y + offsetY) << 1) + 5) * g_Dis[distanceY];
+	  for (int x = 0; x < mb.width; x++)
+	  {
+			motionIdx = (((4*x + offsetX) << 1) + 5) * g_Dis[distanceX] + lookUpY;
+			tpmMask = abs(motionIdx) < 32 ? 2 : ( motionIdx<=0 ? (1 - isFlip):isFlip);
+		  if (tpmMask == 2)
+		  {
+			  mb.at(x, y).isInter = true;
+			  mb.at(x, y).interDir = biMv.interDir;
+			  mb.at(x, y).refIdx[0] = biMv.refIdx[0];
+			  mb.at(x, y).refIdx[1] = biMv.refIdx[1];
+			  mb.at(x, y).mv[0] = biMv.mv[0];
+			  mb.at(x, y).mv[1] = biMv.mv[1];
+			  mb.at(x, y).sliceIdx = biMv.sliceIdx;
+		  }
+		  else if (tpmMask == 0)
+		  {
+			  mb.at(x, y).isInter = true;
+			  mb.at(x, y).interDir = geoMrgCtx.interDirNeighbours[candIdx0];
+			  mb.at(x, y).refIdx[0] = geoMrgCtx.mvFieldNeighbours[candIdx0 << 1].refIdx;
+			  mb.at(x, y).refIdx[1] = geoMrgCtx.mvFieldNeighbours[(candIdx0 << 1) + 1].refIdx;
+			  mb.at(x, y).mv[0] = geoMrgCtx.mvFieldNeighbours[candIdx0 << 1].mv;
+			  mb.at(x, y).mv[1] = geoMrgCtx.mvFieldNeighbours[(candIdx0 << 1) + 1].mv;
+			  mb.at(x, y).sliceIdx = biMv.sliceIdx;
+		  }
+		  else
+		  {
+			  mb.at(x, y).isInter = true;
+			  mb.at(x, y).interDir = geoMrgCtx.interDirNeighbours[candIdx1];
+			  mb.at(x, y).refIdx[0] = geoMrgCtx.mvFieldNeighbours[candIdx1 << 1].refIdx;
+			  mb.at(x, y).refIdx[1] = geoMrgCtx.mvFieldNeighbours[(candIdx1 << 1) + 1].refIdx;
+			  mb.at(x, y).mv[0] = geoMrgCtx.mvFieldNeighbours[candIdx1 << 1].mv;
+			  mb.at(x, y).mv[1] = geoMrgCtx.mvFieldNeighbours[(candIdx1 << 1) + 1].mv;
+			  mb.at(x, y).sliceIdx = biMv.sliceIdx;
+		  }
+	  }
+  }
+}
+#endif
 
 bool CU::hasSubCUNonZeroMVd( const CodingUnit& cu )
 {
diff --git a/source/Lib/CommonLib/UnitTools.h b/source/Lib/CommonLib/UnitTools.h
index 8cead3c5c..e13475ef6 100644
--- a/source/Lib/CommonLib/UnitTools.h
+++ b/source/Lib/CommonLib/UnitTools.h
@@ -168,9 +168,14 @@ namespace PU
   bool isLMCMode                      (                          unsigned mode);
   bool isLMCModeEnabled               (const PredictionUnit &pu, unsigned mode);
   bool isChromaIntraModeCrossCheckMode(const PredictionUnit &pu);
+#if !JVET_Q0806
   void getTriangleMergeCandidates     (const PredictionUnit &pu, MergeCtx &triangleMrgCtx);
   void spanTriangleMotionInfo         (      PredictionUnit &pu, MergeCtx &triangleMrgCtx, const bool splitDir, const uint8_t candIdx0, const uint8_t candIdx1);
   int32_t mappingRefPic               (const PredictionUnit &pu, int32_t refPicPoc, bool targetRefPicList);
+#else
+  void getGeoMergeCandidates          (const PredictionUnit &pu, MergeCtx &GeoMrgCtx);
+  void spanGeoMotionInfo              (      PredictionUnit &pu, MergeCtx &GeoMrgCtx, const uint8_t splitDir, const uint8_t candIdx0, const uint8_t candIdx1);
+#endif
   bool isAddNeighborMv  (const Mv& currMv, Mv* neighborMvs, int numNeighborMv);
   void getIbcMVPsEncOnly(PredictionUnit &pu, Mv* mvPred, int& nbPred);
   bool getDerivedBV(PredictionUnit &pu, const Mv& currentMv, Mv& derivedMv);
diff --git a/source/Lib/CommonLib/dtrace_blockstatistics.cpp b/source/Lib/CommonLib/dtrace_blockstatistics.cpp
index 8005a537f..4338f027a 100644
--- a/source/Lib/CommonLib/dtrace_blockstatistics.cpp
+++ b/source/Lib/CommonLib/dtrace_blockstatistics.cpp
@@ -271,6 +271,7 @@ void CDTrace::dtrace_polygon_vector(int k, int poc, const std::vector<Position>
   dtrace<false>(k, "BlockStat: POC %d @[%s] %s={%4d,%4d}\n", poc, polygonDescription.c_str(), stat_type.c_str(), val_x, val_y);
 }
 
+#if !JVET_Q0806
 void retrieveTriangularMvInfo(const PredictionUnit& pu, MotionInfo& mi0, MotionInfo& mi1)
 {
   int triangleDir = pu.triangleSplitDir;
@@ -358,6 +359,7 @@ void retrieveTrianglePolygon(const PredictionUnit& pu, std::vector<Position>& tr
     CHECK(triangleDir != TRIANGLE_DIR_45 && triangleDir != TRIANGLE_DIR_135, "Unknown triangle type");
   }
 }
+#endif
 
 void writeBlockStatisticsHeader(const SPS *sps)
 {
@@ -493,7 +495,11 @@ void writeAllData(const CodingStructure& cs, const UnitArea& ctuArea)
               DTRACE_BLOCK_SCALAR(g_trace_ctx, D_BLOCK_STATISTICS_ALL, pu, GetBlockStatisticName(BlockStatistic::MVPIdxL1), pu.mvpIdx[REF_PIC_LIST_1]);
               DTRACE_BLOCK_SCALAR(g_trace_ctx, D_BLOCK_STATISTICS_ALL, pu, GetBlockStatisticName(BlockStatistic::RefIdxL1), pu.refIdx[REF_PIC_LIST_1]);
             }
+#if !JVET_Q0806
             if (!pu.cu->affine && !pu.cu->triangle)
+#else
+            if (!pu.cu->affine && !pu.cu->geoFlag)
+#endif
             {
               if (pu.interDir != 2 /* PRED_L1 */)
               {
@@ -518,6 +524,7 @@ void writeAllData(const CodingStructure& cs, const UnitArea& ctuArea)
                 DTRACE_BLOCK_VECTOR(g_trace_ctx, D_BLOCK_STATISTICS_ALL, pu, GetBlockStatisticName(BlockStatistic::MVL1), mv.hor, mv.ver);
               }
             }
+#if !JVET_Q0806
             else if (pu.cu->triangle)
             {
               MotionInfo mi[2];
@@ -539,6 +546,7 @@ void writeAllData(const CodingStructure& cs, const UnitArea& ctuArea)
                 }
               }
             }
+#endif
             else
             {
               if (pu.interDir != 2 /* PRED_L1 */)
@@ -864,7 +872,11 @@ void writeAllCodedData(const CodingStructure & cs, const UnitArea & ctuArea)
               DTRACE_BLOCK_SCALAR(g_trace_ctx, D_BLOCK_STATISTICS_CODED, pu, GetBlockStatisticName(BlockStatistic::MVPIdxL1), pu.mvpIdx[REF_PIC_LIST_1]);
               DTRACE_BLOCK_SCALAR(g_trace_ctx, D_BLOCK_STATISTICS_CODED, pu, GetBlockStatisticName(BlockStatistic::RefIdxL1), pu.refIdx[REF_PIC_LIST_1]);
             }
+#if !JVET_Q0806
             if (!pu.cu->affine && !pu.cu->triangle)
+#else
+            if (!pu.cu->affine && !pu.cu->geoFlag)
+#endif
             {
               if (pu.interDir != 2 /* PRED_L1 */)
               {
@@ -889,6 +901,7 @@ void writeAllCodedData(const CodingStructure & cs, const UnitArea & ctuArea)
                 DTRACE_BLOCK_VECTOR(g_trace_ctx, D_BLOCK_STATISTICS_CODED, pu, GetBlockStatisticName(BlockStatistic::MVL1), mv.hor, mv.ver);
               }
             }
+#if !JVET_Q0806
             else if (pu.cu->triangle)
             {
               MotionInfo mi[2];
@@ -910,6 +923,7 @@ void writeAllCodedData(const CodingStructure & cs, const UnitArea & ctuArea)
                 }
               }
             }
+#endif
             else
             {
               if (pu.interDir != 2 /* PRED_L1 */)
diff --git a/source/Lib/CommonLib/dtrace_blockstatistics.h b/source/Lib/CommonLib/dtrace_blockstatistics.h
index 2df8b9b3a..84c7fe18a 100644
--- a/source/Lib/CommonLib/dtrace_blockstatistics.h
+++ b/source/Lib/CommonLib/dtrace_blockstatistics.h
@@ -118,10 +118,16 @@ enum class BlockStatistic {
   MMVDMergeIdx,
   CiipFlag,
   SMVDFlag,
+#if !JVET_Q0806
   TrianglePartitioning,
   TriangleMVL0, //<< currently only uni-prediction enabled
   TriangleMVL1, //<< currently only uni-prediction enabled
   BCWIndex,
+#else
+  GeoPartitioning,
+  GeoMVL0, //<< currently only uni-prediction enabled
+  GeoMVL1, //<< currently only uni-prediction enabled
+#endif
 // for dual tree
   // general
   Depth_Chroma,
@@ -212,10 +218,16 @@ static const std::map<BlockStatistic, std::tuple<std::string, BlockStatisticType
   { BlockStatistic::MMVDMergeIdx,           std::tuple<std::string, BlockStatisticType, std::string>{"MMVDMergeIdx",                BlockStatisticType::Integer,                "[0, 1]"}},
   { BlockStatistic::CiipFlag,            std::tuple<std::string, BlockStatisticType, std::string>{"CiipFlag",                 BlockStatisticType::Flag,                   ""}},
   { BlockStatistic::SMVDFlag,               std::tuple<std::string, BlockStatisticType, std::string>{"SMVDFlag",                    BlockStatisticType::Flag,                   ""}},
+#if !JVET_Q0806
   { BlockStatistic::TrianglePartitioning,   std::tuple<std::string, BlockStatisticType, std::string>{"TrianglePartitioning",        BlockStatisticType::Line,                   ""}},
   { BlockStatistic::TriangleMVL0,           std::tuple<std::string, BlockStatisticType, std::string>{"TriangleMVL0",                BlockStatisticType::VectorPolygon,          "Scale: 4"}},
   { BlockStatistic::TriangleMVL1,           std::tuple<std::string, BlockStatisticType, std::string>{"TriangleMVL1",                BlockStatisticType::VectorPolygon,          "Scale: 4"}},
   { BlockStatistic::BCWIndex,               std::tuple<std::string, BlockStatisticType, std::string>{"BCWIndex",                    BlockStatisticType::Integer,                "[0, 4]"}},
+#else
+  { BlockStatistic::GeoPartitioning,        std::tuple<std::string, BlockStatisticType, std::string>{"GeoPartitioning",             BlockStatisticType::Line,                   ""} },
+  { BlockStatistic::GeoMVL0,                std::tuple<std::string, BlockStatisticType, std::string>{"GeoMVL0",                     BlockStatisticType::VectorPolygon,          "Scale: 4"} },
+  { BlockStatistic::GeoMVL1,                std::tuple<std::string, BlockStatisticType, std::string>{"GeoMVL1",                     BlockStatisticType::VectorPolygon,          "Scale: 4"} },
+#endif
   // for dual tree
   { BlockStatistic::Depth_Chroma,                  std::tuple<std::string, BlockStatisticType, std::string>{"Depth_Chroma",                       BlockStatisticType::Integer,                "[0, 10]"}}, // todo: actual limits?
   { BlockStatistic::QT_Depth_Chroma,               std::tuple<std::string, BlockStatisticType, std::string>{"QT_Depth_Chroma",                    BlockStatisticType::Integer,                "[0, 10]"}}, // todo: actual limits?
diff --git a/source/Lib/CommonLib/x86/InterpolationFilterX86.h b/source/Lib/CommonLib/x86/InterpolationFilterX86.h
index 2b5bda2df..8ce78e808 100644
--- a/source/Lib/CommonLib/x86/InterpolationFilterX86.h
+++ b/source/Lib/CommonLib/x86/InterpolationFilterX86.h
@@ -1327,6 +1327,7 @@ static void simdFilter( const ClpRng& clpRng, Pel const *src, int srcStride, Pel
   }
 }
 
+#if !JVET_Q0806
 template< X86_VEXT vext >
 void xWeightedTriangleBlk_SSE(const PredictionUnit &pu, const uint32_t width, const uint32_t height, const ComponentID compIdx, const bool splitDir, PelUnitBuf& predDst, PelUnitBuf& predSrc0, PelUnitBuf& predSrc1)
 {
@@ -1449,6 +1450,229 @@ void xWeightedTriangleBlk_SSE(const PredictionUnit &pu, const uint32_t width, co
     }
   }
 }
+#else
+template< X86_VEXT vext >
+void xWeightedGeoBlk_SSE(const PredictionUnit &pu, const uint32_t width, const uint32_t height, const ComponentID compIdx, const uint8_t splitDir, PelUnitBuf& predDst, PelUnitBuf& predSrc0, PelUnitBuf& predSrc1)
+{
+  Pel* dst = predDst.get(compIdx).buf;
+  Pel* src0 = predSrc0.get(compIdx).buf;
+  Pel* src1 = predSrc1.get(compIdx).buf;
+  int32_t strideDst = predDst.get(compIdx).stride;
+  int32_t strideSrc0 = predSrc0.get(compIdx).stride;
+  int32_t strideSrc1 = predSrc1.get(compIdx).stride;
+
+  const char    log2WeightBase = 3;
+  const ClpRng  clpRng         = pu.cu->slice->clpRngs().comp[compIdx];
+  const int32_t shiftWeighted  = std::max<int>(2, (IF_INTERNAL_PREC - clpRng.bd)) + log2WeightBase;
+  const int32_t offsetWeighted = (1 << (shiftWeighted - 1)) + (IF_INTERNAL_OFFS << log2WeightBase);
+
+  int16_t wIdx    = floorLog2(pu.lwidth() ) - GEO_MIN_CU_LOG2;
+  int16_t hIdx    = floorLog2(pu.lheight()) - GEO_MIN_CU_LOG2;
+  int16_t angle   = g_GeoParams[splitDir][0];
+  int16_t stepY = 0;
+  int16_t* weight = nullptr;
+  if (g_angle2mirror[angle] == 2)
+  {
+    stepY = -GEO_WEIGHT_MASK_SIZE;
+    weight = &g_globalGeoWeights[g_angle2mask[angle]][(GEO_WEIGHT_MASK_SIZE - 1 - g_weightOffset[splitDir][hIdx][wIdx][1]) * GEO_WEIGHT_MASK_SIZE + g_weightOffset[splitDir][hIdx][wIdx][0]];
+  }
+  else if (g_angle2mirror[angle] == 1)
+  {
+    stepY = GEO_WEIGHT_MASK_SIZE;
+    weight = &g_globalGeoWeights[g_angle2mask[angle]][g_weightOffset[splitDir][hIdx][wIdx][1] * GEO_WEIGHT_MASK_SIZE + (GEO_WEIGHT_MASK_SIZE - 1 - g_weightOffset[splitDir][hIdx][wIdx][0])];
+  }
+  else
+  {
+    stepY = GEO_WEIGHT_MASK_SIZE;
+    weight = &g_globalGeoWeights[g_angle2mask[angle]][g_weightOffset[splitDir][hIdx][wIdx][1] * GEO_WEIGHT_MASK_SIZE + g_weightOffset[splitDir][hIdx][wIdx][0]];
+  }
+
+  const __m128i mmEight = _mm_set1_epi16(8);
+  const __m128i mmOffset = _mm_set1_epi32(offsetWeighted);
+  const __m128i mmShift = _mm_cvtsi32_si128(shiftWeighted);
+  const __m128i mmMin = _mm_set1_epi16(clpRng.min);
+  const __m128i mmMax = _mm_set1_epi16(clpRng.max);
+
+  if (compIdx != COMPONENT_Y && pu.chromaFormat== CHROMA_420)
+	  stepY <<= 1;
+  if( width == 4 )
+  {
+    // it will occur to chroma only
+    for( int y = 0; y < height; y++ )
+    {
+      __m128i s0 = _mm_loadl_epi64((__m128i *) (src0));
+      __m128i s1 = _mm_loadl_epi64((__m128i *) (src1));
+      __m128i w0;	
+	    if (g_angle2mirror[angle] == 1)
+	    {
+		    w0 = _mm_loadu_si128((__m128i *) (weight - (8 - 1)));
+		    const __m128i shuffle_mask = _mm_set_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
+		    w0 = _mm_shuffle_epi8(w0, shuffle_mask);
+	    }
+	    else
+	    {
+	      w0 = _mm_loadu_si128((__m128i *) (weight));
+	    }
+	    const __m128i mask = _mm_set_epi16(0, 1, 0, 1, 0, 1, 0, 1);
+	    w0 = _mm_mullo_epi16(w0, mask);
+	    w0 = _mm_packs_epi32(w0, _mm_setzero_si128());
+      __m128i w1 = _mm_sub_epi16(mmEight, w0);     	 
+      s0 = _mm_unpacklo_epi16(s0, s1);
+      w0 = _mm_unpacklo_epi16(w0, w1);
+      s0 = _mm_add_epi32(_mm_madd_epi16(s0, w0), mmOffset);
+      s0 = _mm_sra_epi32(s0, mmShift);
+      s0 = _mm_packs_epi32(s0, s0);
+      s0 = _mm_min_epi16(mmMax, _mm_max_epi16(s0, mmMin));
+      _mm_storel_epi64((__m128i *) (dst), s0);
+      dst    += strideDst;
+      src0   += strideSrc0;
+      src1   += strideSrc1;
+      weight += stepY;
+    }
+  }
+#if USE_AVX2
+  else if (width >= 16)
+  {
+	  const __m256i mmEightAVX2 = _mm256_set1_epi16(8);
+	  const __m256i mmOffsetAVX2 = _mm256_set1_epi32(offsetWeighted);
+	  const __m256i mmMinAVX2 = _mm256_set1_epi16(clpRng.min);
+	  const __m256i mmMaxAVX2 = _mm256_set1_epi16(clpRng.max);
+	  for (int y = 0; y < height; y++)
+	  {
+		  for (int x = 0; x < width; x += 16)
+		  {
+			  __m256i s0 = _mm256_lddqu_si256((__m256i *) (src0 + x)); // why not aligned with 128/256 bit boundaries
+			  __m256i s1 = _mm256_lddqu_si256((__m256i *) (src1 + x));
+
+			  __m256i w0 = _mm256_lddqu_si256((__m256i *) (weight + x));
+			  if (compIdx != COMPONENT_Y &&  pu.chromaFormat != CHROMA_444)
+			  {
+				  const __m256i mask = _mm256_set_epi16(0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1);
+				  __m256i w0p0, w0p1;
+				  if (g_angle2mirror[angle] == 1)
+				  {
+					  w0p0 = _mm256_lddqu_si256((__m256i *) (weight - (x << 1) - (16 - 1))); // first sub-sample the required weights.
+					  w0p1 = _mm256_lddqu_si256((__m256i *) (weight - (x << 1) - 16 - (16 - 1)));
+					  const __m256i shuffle_mask = _mm256_set_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14, 1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
+					  w0p0 = _mm256_shuffle_epi8(w0p0, shuffle_mask);
+					  w0p0 = _mm256_permute4x64_epi64(w0p0, _MM_SHUFFLE(1, 0, 3, 2));
+					  w0p1 = _mm256_shuffle_epi8(w0p1, shuffle_mask);
+					  w0p1 = _mm256_permute4x64_epi64(w0p1, _MM_SHUFFLE(1, 0, 3, 2));
+				  }
+				  else
+				  {
+					  w0p0 = _mm256_lddqu_si256((__m256i *) (weight + (x << 1))); // first sub-sample the required weights.
+					  w0p1 = _mm256_lddqu_si256((__m256i *) (weight + (x << 1) + 16));
+				  }
+				  w0p0 = _mm256_mullo_epi16(w0p0, mask);
+				  w0p1 = _mm256_mullo_epi16(w0p1, mask);
+				  w0 = _mm256_packs_epi16(w0p0, w0p1);
+				  w0 = _mm256_permute4x64_epi64(w0, _MM_SHUFFLE(3, 1, 2, 0));
+			  }
+			  else
+			  {
+				  if (g_angle2mirror[angle] == 1)
+				  {
+					  w0 = _mm256_lddqu_si256((__m256i *) (weight - x - (16 - 1)));
+					  const __m256i shuffle_mask = _mm256_set_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14, 1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
+					  w0 = _mm256_shuffle_epi8(w0, shuffle_mask);
+					  w0 = _mm256_permute4x64_epi64(w0, _MM_SHUFFLE(1, 0, 3, 2));
+				  }
+				  else
+				  {
+					  w0 = _mm256_lddqu_si256((__m256i *) (weight + x));
+				  }
+			  }
+			  __m256i w1 = _mm256_sub_epi16(mmEightAVX2, w0);
+
+			  __m256i s0tmp = _mm256_unpacklo_epi16(s0, s1);
+			  __m256i w0tmp = _mm256_unpacklo_epi16(w0, w1);
+			  s0tmp = _mm256_add_epi32(_mm256_madd_epi16(s0tmp, w0tmp), mmOffsetAVX2);
+			  s0tmp = _mm256_sra_epi32(s0tmp, mmShift);
+
+			  s0 = _mm256_unpackhi_epi16(s0, s1);
+			  w0 = _mm256_unpackhi_epi16(w0, w1);
+			  s0 = _mm256_add_epi32(_mm256_madd_epi16(s0, w0), mmOffsetAVX2);
+			  s0 = _mm256_sra_epi32(s0, mmShift);
+
+			  s0 = _mm256_packs_epi32(s0tmp, s0);
+			  s0 = _mm256_min_epi16(mmMaxAVX2, _mm256_max_epi16(s0, mmMinAVX2));
+			  _mm256_storeu_si256((__m256i *) (dst + x), s0);
+		  }
+		  dst += strideDst;
+		  src0 += strideSrc0;
+		  src1 += strideSrc1;
+		  weight += stepY;
+	  }
+  }
+#endif
+  else
+  {
+    for( int y = 0; y < height; y++ )
+    {
+      for( int x = 0; x < width; x += 8 )
+      {
+        __m128i s0 = _mm_lddqu_si128 ((__m128i *) (src0 + x));
+        __m128i s1 = _mm_lddqu_si128 ((__m128i *) (src1 + x));
+		__m128i w0;
+		if (compIdx != COMPONENT_Y && pu.chromaFormat != CHROMA_444)
+		{
+			const __m128i mask = _mm_set_epi16(0, 1, 0, 1, 0, 1, 0, 1);
+			__m128i w0p0,w0p1;
+      if (g_angle2mirror[angle] == 1)
+			{
+				w0p0 = _mm_lddqu_si128 ((__m128i *) (weight - (x << 1) - (8 - 1))); // first sub-sample the required weights.
+				w0p1 = _mm_lddqu_si128 ((__m128i *) (weight - (x << 1) - 8 - (8 - 1)));
+				const __m128i shuffle_mask = _mm_set_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
+				w0p0 = _mm_shuffle_epi8(w0p0, shuffle_mask);
+				w0p1 = _mm_shuffle_epi8(w0p1, shuffle_mask);
+			}
+			else
+			{				
+				w0p0 = _mm_lddqu_si128 ((__m128i *) (weight + (x << 1) ) ); // first sub-sample the required weights.
+				w0p1 = _mm_lddqu_si128 ((__m128i *) (weight + (x << 1) + 8) );
+			}
+			w0p0 = _mm_mullo_epi16(w0p0, mask);
+			w0p1 = _mm_mullo_epi16(w0p1, mask);
+			w0 = _mm_packs_epi32(w0p0, w0p1);
+		}
+		else
+		{
+      if ( g_angle2mirror[angle] == 1)
+			{
+				w0 = _mm_lddqu_si128 ((__m128i *) (weight - x - (8 - 1) ) );
+				const __m128i shuffle_mask = _mm_set_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
+				w0 = _mm_shuffle_epi8(w0, shuffle_mask);
+			}
+			else
+			{
+				w0 = _mm_lddqu_si128((__m128i *) (weight + x));
+			}
+		}
+        __m128i w1 = _mm_sub_epi16(mmEight, w0);
+
+        __m128i s0tmp = _mm_unpacklo_epi16(s0, s1);
+        __m128i w0tmp = _mm_unpacklo_epi16(w0, w1);
+        s0tmp = _mm_add_epi32(_mm_madd_epi16(s0tmp, w0tmp), mmOffset);
+        s0tmp = _mm_sra_epi32(s0tmp, mmShift);
+
+        s0 = _mm_unpackhi_epi16(s0, s1);
+        w0 = _mm_unpackhi_epi16(w0, w1);
+        s0 = _mm_add_epi32(_mm_madd_epi16(s0, w0), mmOffset);
+        s0 = _mm_sra_epi32(s0, mmShift);
+
+        s0 = _mm_packs_epi32(s0tmp, s0);
+        s0 = _mm_min_epi16(mmMax, _mm_max_epi16(s0, mmMin));
+        _mm_storeu_si128((__m128i *) (dst + x), s0);
+      }
+      dst    += strideDst;
+      src0   += strideSrc0;
+      src1   += strideSrc1;
+      weight += stepY;
+    }
+  }
+}
+#endif
 
 template <X86_VEXT vext>
 void InterpolationFilter::_initInterpolationFilterX86()
@@ -1489,7 +1713,11 @@ void InterpolationFilter::_initInterpolationFilterX86()
   m_filterCopy[1][0]   = simdFilterCopy<vext, true, false>;
   m_filterCopy[1][1]   = simdFilterCopy<vext, true, true>;
 
+#if !JVET_Q0806
   m_weightedTriangleBlk = xWeightedTriangleBlk_SSE<vext>;
+#else
+  m_weightedGeoBlk = xWeightedGeoBlk_SSE<vext>;
+#endif
 }
 
 template void InterpolationFilter::_initInterpolationFilterX86<SIMDX86>();
diff --git a/source/Lib/CommonLib/x86/RdCostX86.h b/source/Lib/CommonLib/x86/RdCostX86.h
index b5e3288be..3bdfec3f7 100644
--- a/source/Lib/CommonLib/x86/RdCostX86.h
+++ b/source/Lib/CommonLib/x86/RdCostX86.h
@@ -1973,6 +1973,97 @@ static uint32_t xCalcHAD8x16_AVX2( const Pel* piOrg, const Pel* piCur, const int
   return (sad);
 }
 
+#if JVET_Q0806
+template< X86_VEXT vext >
+Distortion RdCost::xGetSADwMask_SIMD( const DistParam &rcDtParam )
+{
+  if (rcDtParam.org.width < 4  || rcDtParam.bitDepth > 10 || rcDtParam.applyWeight)
+    return RdCost::xGetSADwMask( rcDtParam );
+
+  const short* src1   = (const short*)rcDtParam.org.buf;
+  const short* src2   = (const short*)rcDtParam.cur.buf;
+  const short* weightMask   = (const short*)rcDtParam.mask;
+  int  rows           = rcDtParam.org.height;
+  int  cols           = rcDtParam.org.width;
+  int  subShift       = rcDtParam.subShift;
+  int  subStep        = ( 1 << subShift);
+  const int strideSrc1 = rcDtParam.org.stride * subStep;
+  const int strideSrc2 = rcDtParam.cur.stride * subStep;
+  const int strideMask = rcDtParam.maskStride * subStep;
+
+  Distortion sum = 0;
+  if( vext >= AVX2 && (cols & 15 ) == 0 )
+  {
+#ifdef USE_AVX2
+    // Do for width that multiple of 16
+    __m256i vzero = _mm256_setzero_si256();
+    __m256i vsum32 = vzero;
+    for( int y = 0; y < rows; y+= subStep)
+    {
+      for( int x = 0; x < cols; x+=16 )
+      {
+        __m256i vsrc1 = _mm256_lddqu_si256( ( __m256i* )( &src1[x] ) );
+        __m256i vsrc2 = _mm256_lddqu_si256( ( __m256i* )( &src2[x] ) );
+		    __m256i vmask;
+		    if ( rcDtParam.stepX == -1 )
+		    {
+			    vmask = _mm256_lddqu_si256((__m256i*)((&weightMask[x]) - (x << 1) - (16 - 1)));
+			    const __m256i shuffle_mask = _mm256_set_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14, 1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
+			    vmask = _mm256_shuffle_epi8(vmask, shuffle_mask);
+			    vmask = _mm256_permute4x64_epi64 (vmask, _MM_SHUFFLE(1, 0, 3, 2));
+		    }
+		    else
+		    {
+			    vmask = _mm256_lddqu_si256((__m256i*)(&weightMask[x]));
+		    }
+        vsum32 = _mm256_add_epi32( vsum32, _mm256_madd_epi16( vmask, _mm256_abs_epi16( _mm256_sub_epi16( vsrc1, vsrc2 ) ) ) );
+      }
+      src1 += strideSrc1;
+      src2 += strideSrc2;
+      weightMask += strideMask;
+    }
+    vsum32 = _mm256_hadd_epi32( vsum32, vzero );
+    vsum32 = _mm256_hadd_epi32( vsum32, vzero );
+    sum =  _mm_cvtsi128_si32( _mm256_castsi256_si128( vsum32 ) ) + _mm_cvtsi128_si32( _mm256_castsi256_si128( _mm256_permute2x128_si256( vsum32, vsum32, 0x11 ) ) );
+#endif
+  }
+  else
+  {
+    // Do with step of 8
+    __m128i vzero = _mm_setzero_si128();
+    __m128i vsum32 = vzero;
+    for( int y = 0; y < rows; y+= subStep)
+    {
+      for( int x = 0; x < cols; x+=8 )
+      {
+        __m128i vsrc1 = _mm_loadu_si128( ( const __m128i* )( &src1[x] ) );
+        __m128i vsrc2 = _mm_lddqu_si128( ( const __m128i* )( &src2[x] ) );
+		    __m128i vmask;
+		    if (rcDtParam.stepX == -1)
+		    {
+			    vmask = _mm_lddqu_si128((__m128i*)((&weightMask[x]) - (x << 1) - (8 - 1)));
+			    const __m128i shuffle_mask = _mm_set_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
+			    vmask = _mm_shuffle_epi8(vmask, shuffle_mask);
+		    }
+		    else
+		    {
+			    vmask = _mm_lddqu_si128((const __m128i*)(&weightMask[x]));
+		    }
+        vsum32 = _mm_add_epi32( vsum32, _mm_madd_epi16( vmask, _mm_abs_epi16( _mm_sub_epi16( vsrc1, vsrc2 ) ) ) );
+      }
+      src1 += strideSrc1;
+      src2 += strideSrc2;
+      weightMask += strideMask;
+    }
+    vsum32 = _mm_hadd_epi32( vsum32, vzero );
+    vsum32 = _mm_hadd_epi32( vsum32, vzero );
+    sum =  _mm_cvtsi128_si32( vsum32 );
+  }
+  sum <<= subShift;
+
+  return sum >> DISTORTION_PRECISION_ADJUSTMENT(rcDtParam.bitDepth);
+}
+#endif
 
 template<X86_VEXT vext>
 Distortion RdCost::xGetHADs_SIMD( const DistParam &rcDtParam )
@@ -2150,6 +2241,10 @@ void RdCost::_initRdCostX86()
   m_afpDistortFunc[DF_HAD16N]  = RdCost::xGetHADs_SIMD<vext>;
 
   m_afpDistortFunc[DF_SAD_INTERMEDIATE_BITDEPTH] = RdCost::xGetSAD_IBD_SIMD<vext>;
+
+#if JVET_Q0806
+  m_afpDistortFunc[DF_SAD_WITH_MASK] = xGetSADwMask_SIMD<vext>;
+#endif
 }
 
 template void RdCost::_initRdCostX86<SIMDX86>();
diff --git a/source/Lib/DecoderLib/CABACReader.cpp b/source/Lib/DecoderLib/CABACReader.cpp
index 2b6e06881..12222b372 100644
--- a/source/Lib/DecoderLib/CABACReader.cpp
+++ b/source/Lib/DecoderLib/CABACReader.cpp
@@ -2107,10 +2107,20 @@ void CABACReader::merge_data( PredictionUnit& pu )
     }
 
     RExt__DECODER_DEBUG_BIT_STATISTICS_CREATE_SET( STATS__CABAC_BITS__MERGE_FLAG );
+
+#if JVET_Q0806
+    const bool ciipAvailable = pu.cs->sps->getUseCiip() && !pu.cu->skip && pu.cu->lwidth() < MAX_CU_SIZE && pu.cu->lheight() < MAX_CU_SIZE && pu.cu->lwidth() * pu.cu->lheight() >= 64;
+    const bool geoAvailable = pu.cu->cs->slice->getSPS()->getUseGeo() && pu.cu->cs->slice->isInterB() && pu.cu->cs->picHeader->getMaxNumGeoCand() > 1
+                                                                      && pu.cu->lwidth() >= GEO_MIN_CU_SIZE && pu.cu->lheight() >= GEO_MIN_CU_SIZE
+                                                                      && pu.cu->lwidth() <= GEO_MAX_CU_SIZE && pu.cu->lheight() <= GEO_MAX_CU_SIZE
+                                                                      && pu.cu->lwidth() < 8 * pu.cu->lheight() && pu.cu->lheight() < 8 * pu.cu->lwidth();
+    if (geoAvailable || ciipAvailable)
+#else
     const bool triangleAvailable = pu.cu->cs->slice->getSPS()->getUseTriangle() && pu.cu->cs->slice->isInterB() && pu.cu->cs->picHeader->getMaxNumTriangleCand() > 1;
     const bool ciipAvailable = pu.cs->sps->getUseCiip() && !pu.cu->skip && pu.cu->lwidth() < MAX_CU_SIZE && pu.cu->lheight() < MAX_CU_SIZE;
     if (pu.cu->lwidth() * pu.cu->lheight() >= 64
       && (triangleAvailable || ciipAvailable))
+#endif
     {
       cu.firstPU->regularMergeFlag = m_BinDecoder.decodeBin(Ctx::RegularMergeFlag(cu.skip ? 0 : 1));
     }
@@ -2123,6 +2133,7 @@ void CABACReader::merge_data( PredictionUnit& pu )
       if (cu.cs->slice->getSPS()->getUseMMVD())
       {
         cu.firstPU->mmvdMergeFlag = m_BinDecoder.decodeBin(Ctx::MmvdFlag(0));
+        DTRACE(g_trace_ctx, D_SYNTAX, "mmvd_merge_flag() mmvd_merge=%d pos=(%d,%d) size=%dx%d\n", pu.mmvdMergeFlag ? 1 : 0, pu.lumaPos().x, pu.lumaPos().y, pu.lumaSize().width, pu.lumaSize().height);
       }
       else
       {
@@ -2137,6 +2148,7 @@ void CABACReader::merge_data( PredictionUnit& pu )
     {
       pu.mmvdMergeFlag = false;
       pu.cu->mmvdSkip = false;
+#if !JVET_Q0806
       if (triangleAvailable && ciipAvailable)
       {
         Ciip_flag(pu);
@@ -2158,6 +2170,29 @@ void CABACReader::merge_data( PredictionUnit& pu )
       {
         pu.cu->triangle = true;
       }
+#else
+      if (geoAvailable && ciipAvailable)
+      {
+        Ciip_flag(pu);
+      }
+      else if (ciipAvailable)
+      {
+        pu.ciipFlag = true;
+      }
+      else
+      {
+        pu.ciipFlag = false;
+      }
+      if (pu.ciipFlag)
+      {
+        pu.intraDir[0] = PLANAR_IDX;
+        pu.intraDir[1] = DM_CHROMA_IDX;
+      }
+      else
+      {
+        pu.cu->geoFlag = true;
+      }
+#endif
     }
   }
   if (pu.mmvdMergeFlag || pu.cu->mmvdSkip)
@@ -2200,6 +2235,7 @@ void CABACReader::merge_idx( PredictionUnit& pu )
   int numCandminus1 = int( pu.cs->picHeader->getMaxNumMergeCand() ) - 1;
   pu.mergeIdx       = 0;
 
+#if !JVET_Q0806
   if( pu.cu->triangle )
   {
     RExt__DECODER_DEBUG_BIT_STATISTICS_CREATE_SET( STATS__CABAC_BITS__TRIANGLE_INDEX );
@@ -2237,6 +2273,40 @@ void CABACReader::merge_idx( PredictionUnit& pu )
     pu.triangleMergeIdx1 = candIdx1;
     return;
   }
+#else
+  if( pu.cu->geoFlag )
+  {
+    RExt__DECODER_DEBUG_BIT_STATISTICS_CREATE_SET( STATS__CABAC_BITS__GEO_INDEX );
+    uint32_t splitDir  = 0;
+    xReadTruncBinCode(splitDir, GEO_NUM_PARTITION_MODE);
+    pu.geoSplitDir = splitDir;
+    const int maxNumGeoCand = pu.cs->picHeader->getMaxNumGeoCand();
+    CHECK(maxNumGeoCand < 2, "Incorrect max number of geo candidates");
+    CHECK(pu.cu->lheight() > 64 || pu.cu->lwidth() > 64, "Incorrect block size of geo flag");
+    int numCandminus2 = maxNumGeoCand - 2;
+    pu.mergeIdx = 0;
+    int mergeCand0 = 0;
+    int mergeCand1 = 0;
+    if( m_BinDecoder.decodeBin( Ctx::MergeIdx() ) )
+    {
+      mergeCand0 += unary_max_eqprob(numCandminus2) + 1;;
+    }
+    if (numCandminus2 > 0)
+    {
+      if (m_BinDecoder.decodeBin(Ctx::MergeIdx()))
+      {
+        mergeCand1 += unary_max_eqprob(numCandminus2 - 1) + 1;;
+      }
+    }
+    mergeCand1 += mergeCand1 >= mergeCand0 ? 1 : 0;
+    pu.geoMergeIdx0 = mergeCand0;
+    pu.geoMergeIdx1 = mergeCand1;
+    DTRACE( g_trace_ctx, D_SYNTAX, "merge_idx() geo_split_dir=%d\n", splitDir );
+    DTRACE( g_trace_ctx, D_SYNTAX, "merge_idx() geo_idx0=%d\n", mergeCand0 );
+    DTRACE( g_trace_ctx, D_SYNTAX, "merge_idx() geo_idx1=%d\n", mergeCand1 );
+    return;
+  }
+#endif
 
   if (pu.cu->predMode == MODE_IBC)
   {
diff --git a/source/Lib/DecoderLib/DecCu.cpp b/source/Lib/DecoderLib/DecCu.cpp
index 3b9612819..be16916a2 100644
--- a/source/Lib/DecoderLib/DecCu.cpp
+++ b/source/Lib/DecoderLib/DecCu.cpp
@@ -636,6 +636,7 @@ void DecCu::xFillPCMBuffer(CodingUnit &cu)
 
 void DecCu::xReconInter(CodingUnit &cu)
 {
+#if !JVET_Q0806
   if( cu.triangle )
   {
     const bool    splitDir = cu.firstPU->triangleSplitDir;
@@ -645,13 +646,25 @@ void DecCu::xReconInter(CodingUnit &cu)
     PU::spanTriangleMotionInfo( *cu.firstPU, m_triangleMrgCtx, splitDir, candIdx0, candIdx1 );
   }
   else
+#else
+  if( cu.geoFlag )
+  {
+    m_pcInterPred->motionCompensationGeo( cu, m_geoMrgCtx );
+    PU::spanGeoMotionInfo( *cu.firstPU, m_geoMrgCtx, cu.firstPU->geoSplitDir, cu.firstPU->geoMergeIdx0, cu.firstPU->geoMergeIdx1 );
+  }
+  else
+#endif
   {
   m_pcIntraPred->geneIntrainterPred(cu);
 
   // inter prediction
   CHECK(CU::isIBC(cu) && cu.firstPU->ciipFlag, "IBC and Ciip cannot be used together");
   CHECK(CU::isIBC(cu) && cu.affine, "IBC and Affine cannot be used together");
+#if !JVET_Q0806
   CHECK(CU::isIBC(cu) && cu.triangle, "IBC and triangle cannot be used together");
+#else
+  CHECK(CU::isIBC(cu) && cu.geoFlag, "IBC and geo cannot be used together");
+#endif
   CHECK(CU::isIBC(cu) && cu.firstPU->mmvdMergeFlag, "IBC and MMVD cannot be used together");
   const bool luma = cu.Y().valid();
   const bool chroma = cu.Cb().valid();
@@ -883,11 +896,19 @@ void DecCu::xDeriveCUMV( CodingUnit &cu )
       else
       {
       {
+#if !JVET_Q0806
         if( pu.cu->triangle )
         {
           PU::getTriangleMergeCandidates( pu, m_triangleMrgCtx );
         }
         else
+#else
+        if( pu.cu->geoFlag )
+        {
+          PU::getGeoMergeCandidates( pu, m_geoMrgCtx );
+        }
+        else
+#endif
         {
         if( pu.cu->affine )
         {
@@ -1031,6 +1052,7 @@ void DecCu::xDeriveCUMV( CodingUnit &cu )
         PU::spanMotionInfo( pu, mrgCtx );
       }
     }
+#if !JVET_Q0806
     if( !cu.triangle )
     {
       if( g_mctsDecCheckEnabled && !MCTSHelper::checkMvBufferForMCTSConstraint( pu, true ) )
@@ -1038,6 +1060,15 @@ void DecCu::xDeriveCUMV( CodingUnit &cu )
         printf( "DECODER: pu motion vector across tile boundaries (%d,%d,%d,%d)\n", pu.lx(), pu.ly(), pu.lwidth(), pu.lheight() );
       }
     }
+#else
+    if( !cu.geoFlag )
+    {
+      if( g_mctsDecCheckEnabled && !MCTSHelper::checkMvBufferForMCTSConstraint( pu, true ) )
+      {
+        printf( "DECODER: pu motion vector across tile boundaries (%d,%d,%d,%d)\n", pu.lx(), pu.ly(), pu.lwidth(), pu.lheight() );
+      }
+    }
+#endif
     if (CU::isIBC(cu))
     {
       const int cuPelX = pu.Y().x;
diff --git a/source/Lib/DecoderLib/DecCu.h b/source/Lib/DecoderLib/DecCu.h
index 2cbb597a9..c6932f3e9 100644
--- a/source/Lib/DecoderLib/DecCu.h
+++ b/source/Lib/DecoderLib/DecCu.h
@@ -98,7 +98,11 @@ private:
 
   MotionInfo        m_SubPuMiBuf[(MAX_CU_SIZE * MAX_CU_SIZE) >> (MIN_CU_LOG2 << 1)];
 
+#if !JVET_Q0806
   MergeCtx          m_triangleMrgCtx;
+#else
+  MergeCtx          m_geoMrgCtx;
+#endif
 };
 
 //! \}
diff --git a/source/Lib/DecoderLib/VLCReader.cpp b/source/Lib/DecoderLib/VLCReader.cpp
index fd516bb03..74caa3ae2 100644
--- a/source/Lib/DecoderLib/VLCReader.cpp
+++ b/source/Lib/DecoderLib/VLCReader.cpp
@@ -698,7 +698,11 @@ void HLSyntaxReader::parsePPS( PPS* pcPPS, ParameterSetManager *parameterSetMana
     READ_CODE( 2, uiCode, "pps_mvd_l1_zero_idc");              pcPPS->setPPSMvdL1ZeroIdc(uiCode);
     READ_CODE( 2, uiCode, "pps_collocated_from_l0_idc");       pcPPS->setPPSCollocatedFromL0Idc(uiCode);
     READ_UVLC( uiCode, "pps_six_minus_max_num_merge_cand_plus1"); pcPPS->setPPSSixMinusMaxNumMergeCandPlus1(uiCode);
+#if !JVET_Q0806
     READ_UVLC( uiCode, "pps_max_num_merge_cand_minus_max_num_triangle_cand_plus1");pcPPS->setPPSMaxNumMergeCandMinusMaxNumTriangleCandPlus1(uiCode);
+#else
+    READ_UVLC(uiCode, "pps_max_num_merge_cand_minus_max_num_gpm_cand_plus1"); pcPPS->setPPSMaxNumMergeCandMinusMaxNumGeoCandPlus1(uiCode);
+#endif
   }
   else
   {
@@ -708,7 +712,11 @@ void HLSyntaxReader::parsePPS( PPS* pcPPS, ParameterSetManager *parameterSetMana
     pcPPS->setPPSMvdL1ZeroIdc(0);
     pcPPS->setPPSCollocatedFromL0Idc(0);
     pcPPS->setPPSSixMinusMaxNumMergeCandPlus1(0);
+#if !JVET_Q0806
     pcPPS->setPPSMaxNumMergeCandMinusMaxNumTriangleCandPlus1(0);
+#else
+    pcPPS->setPPSMaxNumMergeCandMinusMaxNumGeoCandPlus1(0);
+#endif
   }
 
 
@@ -1449,7 +1457,11 @@ void HLSyntaxReader::parseSPS(SPS* pcSPS)
     READ_FLAG( uiCode,  "sps_fpel_mmvd_enabled_flag" );             pcSPS->setFpelMmvdEnabledFlag ( uiCode != 0 );
   }
 
+#if !JVET_Q0806
   READ_FLAG( uiCode,    "triangle_flag" );                          pcSPS->setUseTriangle            ( uiCode != 0 );
+#else
+  READ_FLAG( uiCode,    "sps_gpm_enabled_flag" );                               pcSPS->setUseGeo                 ( uiCode != 0 );
+#endif
 
   READ_FLAG(uiCode, "sps_lmcs_enable_flag");                   pcSPS->setUseLmcs(uiCode == 1);
   READ_FLAG( uiCode, "sps_lfnst_enabled_flag" );                    pcSPS->setUseLFNST( uiCode != 0 );
@@ -2139,6 +2151,7 @@ void HLSyntaxReader::parsePictureHeader( PicHeader* picHeader, ParameterSetManag
     picHeader->setDisProfFlag(0);
   }
 
+#if !JVET_Q0806
   // triangle merge candidate list size
   if (sps->getUseTriangle() && picHeader->getMaxNumMergeCand() >= 2)
   {
@@ -2157,6 +2170,26 @@ void HLSyntaxReader::parsePictureHeader( PicHeader* picHeader, ParameterSetManag
   {
     picHeader->setMaxNumTriangleCand(0);
   }
+#else
+  // geometric merge candidate list size
+  if (sps->getUseGeo() && picHeader->getMaxNumMergeCand() >= 2)
+  {
+    if (!pps->getPPSMaxNumMergeCandMinusMaxNumGeoCandPlus1())
+    {
+      READ_UVLC(uiCode, "pic_max_num_merge_cand_minus_max_num_gpm_cand");
+    }
+    else
+    {
+      uiCode = pps->getPPSMaxNumMergeCandMinusMaxNumGeoCandPlus1() - 1;
+    }
+    CHECK(picHeader->getMaxNumMergeCand() < uiCode, "Incorrrect max number of gpm candidates!");
+    picHeader->setMaxNumGeoCand((uint32_t)(picHeader->getMaxNumMergeCand() - uiCode));
+  }
+  else
+  {
+    picHeader->setMaxNumGeoCand(0);
+  }
+#endif
 
   // ibc merge candidate list size
   if (sps->getIBCFlag())
@@ -3146,7 +3179,11 @@ void HLSyntaxReader::parseConstraintInfo(ConstraintInfo *cinfo)
   READ_FLAG(symbol, "no_ibc_constraint_flag");                     cinfo->setNoIbcConstraintFlag(symbol > 0 ? true : false);
   READ_FLAG(symbol, "no_ciip_constraint_flag");                    cinfo->setNoCiipConstraintFlag(symbol > 0 ? true : false);
   READ_FLAG(symbol, "no_fpel_mmvd_constraint_flag");               cinfo->setNoFPelMmvdConstraintFlag(symbol > 0 ? true : false);
+#if !JVET_Q0806
   READ_FLAG(symbol, "no_triangle_constraint_flag");                cinfo->setNoTriangleConstraintFlag(symbol > 0 ? true : false);
+#else
+  READ_FLAG(symbol, "no_gpm_constraint_flag");                     cinfo->setNoGeoConstraintFlag(symbol > 0 ? true : false);
+#endif
   READ_FLAG(symbol, "no_ladf_constraint_flag");                    cinfo->setNoLadfConstraintFlag(symbol > 0 ? true : false);
   READ_FLAG(symbol, "no_transform_skip_constraint_flag");          cinfo->setNoTransformSkipConstraintFlag(symbol > 0 ? true : false);
   READ_FLAG(symbol, "no_bdpcm_constraint_flag");                   cinfo->setNoBDPCMConstraintFlag(symbol > 0 ? true : false);
diff --git a/source/Lib/EncoderLib/CABACWriter.cpp b/source/Lib/EncoderLib/CABACWriter.cpp
index c814c521a..b4233271e 100644
--- a/source/Lib/EncoderLib/CABACWriter.cpp
+++ b/source/Lib/EncoderLib/CABACWriter.cpp
@@ -1864,10 +1864,19 @@ void CABACWriter::merge_data(const PredictionUnit& pu)
     merge_idx(pu);
     return;
   }
+#if JVET_Q0806
+  const bool ciipAvailable = pu.cs->sps->getUseCiip() && !pu.cu->skip && pu.cu->lwidth() < MAX_CU_SIZE && pu.cu->lheight() < MAX_CU_SIZE && pu.cu->lwidth() * pu.cu->lheight() >= 64;
+  const bool geoAvailable = pu.cu->cs->slice->getSPS()->getUseGeo() && pu.cu->cs->slice->isInterB() && pu.cu->cs->picHeader->getMaxNumGeoCand() > 1
+                                                                    && pu.cu->lwidth() >= GEO_MIN_CU_SIZE && pu.cu->lheight() >= GEO_MIN_CU_SIZE
+                                                                    && pu.cu->lwidth() <= GEO_MAX_CU_SIZE && pu.cu->lheight() <= GEO_MAX_CU_SIZE
+                                                                    && pu.cu->lwidth() < 8 * pu.cu->lheight() && pu.cu->lheight() < 8 * pu.cu->lwidth();
+  if (geoAvailable || ciipAvailable)
+#else
   const bool triangleAvailable = pu.cu->cs->slice->getSPS()->getUseTriangle() && pu.cu->cs->slice->isInterB() && pu.cu->cs->picHeader->getMaxNumTriangleCand() > 1;
   const bool ciipAvailable = pu.cs->sps->getUseCiip() && !pu.cu->skip && pu.cu->lwidth() < MAX_CU_SIZE && pu.cu->lheight() < MAX_CU_SIZE;
   if (pu.cu->lwidth() * pu.cu->lheight() >= 64
     && (triangleAvailable || ciipAvailable))
+#endif
   {
     m_BinEncoder.encodeBin(pu.regularMergeFlag, Ctx::RegularMergeFlag(pu.cu->skip ? 0 : 1));
   }
@@ -1889,10 +1898,17 @@ void CABACWriter::merge_data(const PredictionUnit& pu)
   }
   else
   {
+#if !JVET_Q0806
     if (triangleAvailable && ciipAvailable)
     {
       Ciip_flag(pu);
     }
+#else
+    if (geoAvailable && ciipAvailable)
+    {
+      Ciip_flag(pu);
+    }
+#endif
     merge_idx(pu);
   }
 }
@@ -1993,6 +2009,7 @@ void CABACWriter::merge_idx( const PredictionUnit& pu )
   }
   else
   {
+#if !JVET_Q0806
     if( pu.cu->triangle )
     {
       bool    splitDir = pu.triangleSplitDir;
@@ -2036,6 +2053,38 @@ void CABACWriter::merge_idx( const PredictionUnit& pu )
       encodeOneIdx(candIdx1, maxNumTriangleCand - 2);
       return;
     }
+#else
+    if( pu.cu->geoFlag )
+    {
+      uint8_t splitDir = pu.geoSplitDir;
+      uint8_t candIdx0 = pu.geoMergeIdx0;
+      uint8_t candIdx1 = pu.geoMergeIdx1;
+      DTRACE( g_trace_ctx, D_SYNTAX, "merge_idx() geo_split_dir=%d\n", splitDir );
+      DTRACE( g_trace_ctx, D_SYNTAX, "merge_idx() geo_idx0=%d\n", candIdx0 );
+      DTRACE( g_trace_ctx, D_SYNTAX, "merge_idx() geo_idx1=%d\n", candIdx1 );
+      xWriteTruncBinCode(splitDir, GEO_NUM_PARTITION_MODE);
+      candIdx1 -= candIdx1 < candIdx0 ? 0 : 1;
+      const int maxNumGeoCand = pu.cs->picHeader->getMaxNumGeoCand();
+      CHECK(maxNumGeoCand < 2, "Incorrect max number of geo candidates");
+      CHECK(candIdx0 >= maxNumGeoCand, "Incorrect candIdx0");
+      CHECK(candIdx1 >= maxNumGeoCand, "Incorrect candIdx1");
+      int numCandminus2 = maxNumGeoCand - 2;
+      m_BinEncoder.encodeBin( candIdx0 == 0 ? 0 : 1, Ctx::MergeIdx() );
+      if( candIdx0 > 0 )
+      {
+        unary_max_eqprob(candIdx0 - 1, numCandminus2);
+      }
+      if (numCandminus2 > 0)
+      {
+        m_BinEncoder.encodeBin(candIdx1 == 0 ? 0 : 1, Ctx::MergeIdx());
+        if (candIdx1 > 0)
+        {
+          unary_max_eqprob(candIdx1 - 1, numCandminus2 - 1);
+        }
+      }
+      return;
+    }
+#endif
     int numCandminus1;
     if (pu.cu->predMode == MODE_IBC)
       numCandminus1 = int(pu.cs->picHeader->getMaxNumIBCMergeCand()) - 1;
diff --git a/source/Lib/EncoderLib/EncCfg.h b/source/Lib/EncoderLib/EncCfg.h
index f27bb3c10..673920133 100644
--- a/source/Lib/EncoderLib/EncCfg.h
+++ b/source/Lib/EncoderLib/EncCfg.h
@@ -185,7 +185,11 @@ protected:
   bool      m_noIbcConstraintFlag;
   bool      m_bNoCiipConstraintFlag;
   bool      m_noFPelMmvdConstraintFlag;
+#if !JVET_Q0806
   bool      m_bNoTriangleConstraintFlag;
+#else
+  bool      m_bNoGeoConstraintFlag;
+#endif
   bool      m_bNoLadfConstraintFlag;
   bool      m_noTransformSkipConstraintFlag;
   bool      m_noBDPCMConstraintFlag;
@@ -298,7 +302,11 @@ protected:
 #endif
 
   bool      m_ciip;
+#if !JVET_Q0806
   bool      m_Triangle;
+#else
+  bool      m_Geo;
+#endif
   bool      m_allowDisFracMMVD;
   bool      m_AffineAmvr;
   bool      m_HashME;
@@ -594,7 +602,11 @@ protected:
   WeightedPredictionMethod m_weightedPredictionMethod;
   uint32_t      m_maxNumMergeCand;                    ///< Maximum number of merge candidates
   uint32_t      m_maxNumAffineMergeCand;              ///< Maximum number of affine merge candidates
+#if !JVET_Q0806
   uint32_t      m_maxNumTriangleCand;
+#else
+  uint32_t      m_maxNumGeoCand;
+#endif
   uint32_t      m_maxNumIBCMergeCand;                 ///< Max number of IBC merge candidates
   ScalingListMode m_useScalingListId;             ///< Using quantization matrix i.e. 0=off, 1=default, 2=file.
   std::string m_scalingListFileName;              ///< quantization matrix file name
@@ -611,7 +623,11 @@ protected:
   int       m_PPSMvdL1ZeroIdc;
   int       m_PPSCollocatedFromL0Idc;
   uint32_t  m_PPSSixMinusMaxNumMergeCandPlus1;
+#if !JVET_Q0806
   uint32_t  m_PPSMaxNumMergeCandMinusMaxNumTriangleCandPlus1;
+#else
+  uint32_t  m_PPSMaxNumMergeCandMinusMaxNumGeoCandPlus1;
+#endif
   bool      m_DepQuantEnabledFlag;
   bool      m_SignDataHidingEnabledFlag;
   bool      m_RCEnableRateControl;
@@ -760,8 +776,13 @@ public:
   void      setNoCiipConstraintFlag(bool bVal) { m_bNoCiipConstraintFlag = bVal; }
   bool      getNoFPelMmvdConstraintFlag() const { return m_noFPelMmvdConstraintFlag; }
   void      setNoFPelMmvdConstraintFlag(bool bVal) { m_noFPelMmvdConstraintFlag = bVal; }
+#if !JVET_Q0806
   bool      getNoTriangleConstraintFlag() const { return m_bNoTriangleConstraintFlag; }
   void      setNoTriangleConstraintFlag(bool bVal) { m_bNoTriangleConstraintFlag = bVal; }
+#else
+  bool      getNoGeoConstraintFlag() const { return m_bNoGeoConstraintFlag; }
+  void      setNoGeoConstraintFlag(bool bVal) { m_bNoGeoConstraintFlag = bVal; }
+#endif
   bool      getNoLadfConstraintFlag() const { return m_bNoLadfConstraintFlag; }
   void      setNoLadfConstraintFlag(bool bVal) { m_bNoLadfConstraintFlag = bVal; }
   bool      getNoTransformSkipConstraintFlag() const { return m_noTransformSkipConstraintFlag; }
@@ -969,8 +990,13 @@ public:
 
   void      setUseCiip                   ( bool b )       { m_ciip = b; }
   bool      getUseCiip                   ()         const { return m_ciip; }
+#if !JVET_Q0806
   void      setUseTriangle                  ( bool b )       { m_Triangle = b; }
   bool      getUseTriangle                  ()         const { return m_Triangle; }
+#else
+  void      setUseGeo                       ( bool b )       { m_Geo = b; }
+  bool      getUseGeo                       ()         const { return m_Geo; }
+#endif
   void      setAllowDisFracMMVD             ( bool b )       { m_allowDisFracMMVD = b;    }
   bool      getAllowDisFracMMVD             ()         const { return m_allowDisFracMMVD; }
   void      setUseHashME                    ( bool b )       { m_HashME = b; }
@@ -1585,8 +1611,13 @@ public:
   uint32_t         getMaxNumMergeCand                ()                  { return m_maxNumMergeCand;   }
   void         setMaxNumAffineMergeCand          ( uint32_t u )      { m_maxNumAffineMergeCand = u;    }
   uint32_t     getMaxNumAffineMergeCand          ()                  { return m_maxNumAffineMergeCand; }
+#if !JVET_Q0806
   void         setMaxNumTriangleCand             ( uint32_t u )      { m_maxNumTriangleCand = u;    }
   uint32_t     getMaxNumTriangleCand             ()                  { return m_maxNumTriangleCand; }
+#else
+  void         setMaxNumGeoCand                  ( uint32_t u )      { m_maxNumGeoCand = u;    }
+  uint32_t     getMaxNumGeoCand                  ()                  { return m_maxNumGeoCand; }
+#endif
   void         setMaxNumIBCMergeCand             ( uint32_t u )      { m_maxNumIBCMergeCand = u; }
   uint32_t     getMaxNumIBCMergeCand             ()                  { return m_maxNumIBCMergeCand; }
   void         setUseScalingListId    ( ScalingListMode u )          { m_useScalingListId       = u;   }
@@ -1619,8 +1650,13 @@ public:
   int          getPPSCollocatedFromL0Idc ()                          { return m_PPSCollocatedFromL0Idc; }
   void         setPPSSixMinusMaxNumMergeCandPlus1 ( uint32_t u )     { m_PPSSixMinusMaxNumMergeCandPlus1 = u; }
   uint32_t     getPPSSixMinusMaxNumMergeCandPlus1 ()                 { return m_PPSSixMinusMaxNumMergeCandPlus1; }
+#if !JVET_Q0806
   void         setPPSMaxNumMergeCandMinusMaxNumTriangleCandPlus1 ( uint32_t u ) { m_PPSMaxNumMergeCandMinusMaxNumTriangleCandPlus1 = u; }
   uint32_t     getPPSMaxNumMergeCandMinusMaxNumTriangleCandPlus1 ()  { return m_PPSMaxNumMergeCandMinusMaxNumTriangleCandPlus1; }
+#else
+  void         setPPSMaxNumMergeCandMinusMaxNumGeoCandPlus1 ( uint32_t u ) { m_PPSMaxNumMergeCandMinusMaxNumGeoCandPlus1 = u; }
+  uint32_t     getPPSMaxNumMergeCandMinusMaxNumGeoCandPlus1 ()       { return m_PPSMaxNumMergeCandMinusMaxNumGeoCandPlus1; }
+#endif
   WeightedPredictionMethod getWeightedPredictionMethod() const       { return m_weightedPredictionMethod; }
   void         setWeightedPredictionMethod( WeightedPredictionMethod m ) { m_weightedPredictionMethod = m; }
   void         setDepQuantEnabledFlag( bool b )                      { m_DepQuantEnabledFlag = b;    }
diff --git a/source/Lib/EncoderLib/EncCu.cpp b/source/Lib/EncoderLib/EncCu.cpp
index 664375b90..f629e8482 100644
--- a/source/Lib/EncoderLib/EncCu.cpp
+++ b/source/Lib/EncoderLib/EncCu.cpp
@@ -59,6 +59,7 @@
 //! \{
 
 // ====================================================================================================================
+#if !JVET_Q0806
 EncCu::EncCu() : m_triangleModeTest
 {
   TriangleMotionInfo( 0, 1, 0 ), TriangleMotionInfo( 1, 0, 1 ), TriangleMotionInfo( 1, 0, 2 ), TriangleMotionInfo( 0, 0, 1 ), TriangleMotionInfo( 0, 2, 0 ),
@@ -71,6 +72,18 @@ EncCu::EncCu() : m_triangleModeTest
   TriangleMotionInfo( 0, 3, 1 ), TriangleMotionInfo( 0, 2, 4 ), TriangleMotionInfo( 1, 2, 4 ), TriangleMotionInfo( 0, 4, 2 ), TriangleMotionInfo( 0, 3, 4 ),
 }
 {}
+#else
+EncCu::EncCu() : m_GeoModeTest
+{
+  GeoMotionInfo(0, 1), GeoMotionInfo(1, 0),GeoMotionInfo(0, 2), GeoMotionInfo(1, 2), GeoMotionInfo(2, 0),
+  GeoMotionInfo(2, 1), GeoMotionInfo(0, 3),GeoMotionInfo(1, 3), GeoMotionInfo(2, 3), GeoMotionInfo(3, 0),
+  GeoMotionInfo(3, 1), GeoMotionInfo(3, 2),GeoMotionInfo(0, 4), GeoMotionInfo(1, 4), GeoMotionInfo(2, 4),
+  GeoMotionInfo(3, 4), GeoMotionInfo(4, 0),GeoMotionInfo(4, 1), GeoMotionInfo(4, 2), GeoMotionInfo(4, 3),
+  GeoMotionInfo(0, 5), GeoMotionInfo(1, 5),GeoMotionInfo(2, 5), GeoMotionInfo(3, 5), GeoMotionInfo(4, 5),
+  GeoMotionInfo(5, 0), GeoMotionInfo(5, 1),GeoMotionInfo(5, 2), GeoMotionInfo(5, 3), GeoMotionInfo(5, 4)
+}
+{}
+#endif
 
 void EncCu::create( EncCfg* encCfg )
 {
@@ -138,6 +151,7 @@ void EncCu::create( EncCfg* encCfg )
     m_acRealMergeBuffer[ui].create(chromaFormat, Area(0, 0, uiMaxWidth, uiMaxHeight));
     m_acMergeTmpBuffer[ui].create(chromaFormat, Area(0, 0, uiMaxWidth, uiMaxHeight));
   }
+#if !JVET_Q0806
   const unsigned maxNumTriangleCand = encCfg->getMaxNumTriangleCand();
   for (unsigned i = 0; i < maxNumTriangleCand; i++)
   {
@@ -174,6 +188,12 @@ void EncCu::create( EncCfg* encCfg )
   {
     m_acTriangleWeightedBuffer[ui].create( chromaFormat, Area( 0, 0, uiMaxWidth, uiMaxHeight ) );
   }
+#else
+  for( unsigned ui = 0; ui < GEO_MAX_TRY_WEIGHTED_SAD; ui++ )
+  {
+    m_acGeoWeightedBuffer[ui].create( chromaFormat, Area( 0, 0, uiMaxWidth, uiMaxHeight ) );
+  }
+#endif
 
   m_CtxBuffer.resize( maxDepth );
   m_CurrCtx = 0;
@@ -237,10 +257,17 @@ void EncCu::destroy()
     m_acRealMergeBuffer[ui].destroy();
     m_acMergeTmpBuffer[ui].destroy();
   }
+#if !JVET_Q0806
   for( unsigned ui = 0; ui < TRIANGLE_MAX_NUM_CANDS; ui++ )
   {
     m_acTriangleWeightedBuffer[ui].destroy();
   }
+#else
+  for (unsigned ui = 0; ui < GEO_MAX_TRY_WEIGHTED_SAD; ui++)
+  {
+    m_acGeoWeightedBuffer[ui].destroy();
+  }
+#endif
 }
 
 
@@ -270,6 +297,10 @@ void EncCu::init( EncLib* pcEncLib, const SPS& sps PARL_PARAM( const int tId ) )
   m_dataId             = tId;
 #endif
   m_pcLoopFilter       = pcEncLib->getLoopFilter();
+#if JVET_Q0806
+  m_GeoCostList.init(GEO_NUM_PARTITION_MODE, m_pcEncCfg->getMaxNumGeoCand());
+  m_AFFBestSATDCost = MAX_DOUBLE;
+#endif
 
   DecCu::init( m_pcTrQuant, m_pcIntraSearch, m_pcInterSearch );
 
@@ -790,10 +821,17 @@ void EncCu::xCompressCU( CodingStructure*& tempCS, CodingStructure*& bestCS, Par
       if (cu)
       cu->mmvdSkip = cu->skip == false ? false : cu->mmvdSkip;
     }
+#if !JVET_Q0806
     else if( currTestMode.type == ETM_MERGE_TRIANGLE )
     {
       xCheckRDCostMergeTriangle2Nx2N( tempCS, bestCS, partitioner, currTestMode );
     }
+#else
+    else if( currTestMode.type == ETM_MERGE_GEO )
+    {
+      xCheckRDCostMergeGeo2Nx2N( tempCS, bestCS, partitioner, currTestMode );
+    }
+#endif
     else if( currTestMode.type == ETM_INTRA )
     {
       if (slice.getSPS()->getUseColorTrans() && !CS::isDualITree(*tempCS))
@@ -2389,7 +2427,11 @@ void EncCu::xCheckRDCostMerge2Nx2N( CodingStructure *&tempCS, CodingStructure *&
       cu.tileIdx          = tempCS->pps->getTileIdx( tempCS->area.lumaPos() );
       cu.skip             = false;
       cu.mmvdSkip = false;
+#if !JVET_Q0806
       cu.triangle         = false;
+#else
+      cu.geoFlag          = false;
+#endif
     //cu.affine
       cu.predMode         = MODE_INTER;
     //cu.LICFlag
@@ -2648,7 +2690,11 @@ void EncCu::xCheckRDCostMerge2Nx2N( CodingStructure *&tempCS, CodingStructure *&
       cu.tileIdx          = tempCS->pps->getTileIdx( tempCS->area.lumaPos() );
       cu.skip             = false;
       cu.mmvdSkip = false;
+#if !JVET_Q0806
       cu.triangle         = false;
+#else
+      cu.geoFlag          = false;
+#endif
     //cu.affine
       cu.predMode         = MODE_INTER;
     //cu.LICFlag
@@ -2810,6 +2856,7 @@ void EncCu::xCheckRDCostMerge2Nx2N( CodingStructure *&tempCS, CodingStructure *&
   }
 }
 
+#if !JVET_Q0806
 void EncCu::xCheckRDCostMergeTriangle2Nx2N( CodingStructure *&tempCS, CodingStructure *&bestCS, Partitioner &partitioner, const EncTestMode& encTestMode )
 {
   const Slice &slice = *tempCS->slice;
@@ -3031,6 +3078,293 @@ void EncCu::xCheckRDCostMergeTriangle2Nx2N( CodingStructure *&tempCS, CodingStru
     xCalDebCost( *bestCS, partitioner );
   }
 }
+#else
+void EncCu::xCheckRDCostMergeGeo2Nx2N(CodingStructure *&tempCS, CodingStructure *&bestCS, Partitioner &pm, const EncTestMode& encTestMode)
+{
+  const Slice &slice = *tempCS->slice;
+  CHECK(slice.getSliceType() == I_SLICE, "Merge modes not available for I-slices");
+
+  tempCS->initStructData(encTestMode.qp);
+
+  MergeCtx mergeCtx;
+  const SPS &sps = *tempCS->sps;
+
+  if( sps.getSBTMVPEnabledFlag() )
+  {
+    Size bufSize = g_miScaling.scale(tempCS->area.lumaSize());
+    mergeCtx.subPuMvpMiBuf = MotionBuf(m_SubPuMiBuf, bufSize);
+  }
+
+  CodingUnit &cu = tempCS->addCU(tempCS->area, pm.chType);
+  pm.setCUData(cu);
+  cu.predMode  = MODE_INTER;
+  cu.slice     = tempCS->slice;
+  cu.tileIdx   = tempCS->pps->getTileIdx(tempCS->area.lumaPos());
+  cu.qp        = encTestMode.qp;
+  cu.affine    = false;
+  cu.mtsFlag   = false;
+  cu.BcwIdx    = BCW_DEFAULT;
+  cu.geoFlag   = true;
+  cu.imv       = 0;
+  cu.mmvdSkip  = false;
+  cu.skip      = false;
+  cu.mipFlag   = false;
+  cu.bdpcmMode = 0;
+ 
+  PredictionUnit &pu  = tempCS->addPU(cu, pm.chType);
+  pu.mergeFlag        = true;
+  pu.regularMergeFlag = false;
+  PU::getGeoMergeCandidates(pu, mergeCtx);
+
+  GeoComboCostList comboList;
+  int bitsCandTB = (int)std::floor(std::log2((double)GEO_NUM_PARTITION_MODE));
+  PelUnitBuf geoBuffer[MRG_MAX_NUM_CANDS];
+  PelUnitBuf geoTempBuf[MRG_MAX_NUM_CANDS];
+  PelUnitBuf geoCombinations[GEO_MAX_TRY_WEIGHTED_SAD];
+  DistParam distParam;
+
+  const UnitArea localUnitArea(tempCS->area.chromaFormat, Area(0, 0, tempCS->area.Y().width, tempCS->area.Y().height));
+  const double sqrtLambdaForFirstPass = m_pcRdCost->getMotionLambda( );
+  
+  uint8_t maxNumMergeCandidates = cu.cs->picHeader->getMaxNumGeoCand();
+  DistParam distParamWholeBlk;
+  m_pcRdCost->setDistParam(distParamWholeBlk, tempCS->getOrgBuf().Y(), m_acMergeBuffer[0].Y().buf, m_acMergeBuffer[0].Y().stride, sps.getBitDepth(CHANNEL_TYPE_LUMA), COMPONENT_Y);
+  Distortion bestWholeBlkSad = MAX_UINT64;
+  double bestWholeBlkCost = MAX_DOUBLE;
+  Distortion *uiSadWholeBlk;
+  uiSadWholeBlk = new Distortion[maxNumMergeCandidates];
+  int *pocMrg;
+  Mv *MrgMv;
+  bool *isSkipThisCand;
+  pocMrg = new int[maxNumMergeCandidates];
+  MrgMv = new Mv[maxNumMergeCandidates];
+  isSkipThisCand = new bool[maxNumMergeCandidates];
+  for( int i = 0; i < maxNumMergeCandidates; i++ )
+    isSkipThisCand[i] = false;
+  for( uint8_t mergeCand = 0; mergeCand < maxNumMergeCandidates; mergeCand++ )
+  {
+    geoBuffer[mergeCand] = m_acMergeBuffer[mergeCand].getBuf(localUnitArea);
+    mergeCtx.setMergeInfo(pu, mergeCand);
+    int MrgList = mergeCtx.mvFieldNeighbours[(mergeCand << 1) + 0].refIdx == -1 ? 1 : 0;
+    RefPicList MrgeRefPicList = (MrgList ? REF_PIC_LIST_1 : REF_PIC_LIST_0);
+    int MrgrefIdx = mergeCtx.mvFieldNeighbours[(mergeCand << 1) + MrgList].refIdx;
+    pocMrg[mergeCand] = tempCS->slice->getRefPic(MrgeRefPicList, MrgrefIdx)->getPOC();
+    MrgMv[mergeCand] = mergeCtx.mvFieldNeighbours[(mergeCand << 1) + MrgList].mv;
+    if( mergeCand )
+    {
+      for (int i = 0; i < mergeCand ; i++)
+      {
+        if (pocMrg[mergeCand] == pocMrg[i] && MrgMv[mergeCand] == MrgMv[i])
+        {
+          isSkipThisCand[mergeCand] = true;
+          break;
+        }
+      }
+    }
+    PU::spanMotionInfo(pu, mergeCtx);
+    if (m_pcEncCfg->getMCTSEncConstraint() && (!(MCTSHelper::checkMvBufferForMCTSConstraint(pu))))
+    {
+      tempCS->initStructData(encTestMode.qp);
+      return;
+    }
+    m_pcInterSearch->motionCompensation(pu, geoBuffer[mergeCand]);
+    geoTempBuf[mergeCand] = m_acMergeTmpBuffer[mergeCand].getBuf(localUnitArea);
+    geoTempBuf[mergeCand].Y().copyFrom(geoBuffer[mergeCand].Y());
+    geoTempBuf[mergeCand].Y().roundToOutputBitdepth(geoTempBuf[mergeCand].Y(), cu.slice->clpRng(COMPONENT_Y));
+    distParamWholeBlk.cur.buf = geoTempBuf[mergeCand].Y().buf;
+    distParamWholeBlk.cur.stride = geoTempBuf[mergeCand].Y().stride;
+    uiSadWholeBlk[mergeCand] = distParamWholeBlk.distFunc(distParamWholeBlk);
+    if( uiSadWholeBlk[mergeCand] < bestWholeBlkSad )
+    {
+      bestWholeBlkSad = uiSadWholeBlk[mergeCand];
+      int bitsCand = mergeCand + 1;
+      bestWholeBlkCost = (double)bestWholeBlkSad + (double)bitsCand * sqrtLambdaForFirstPass;
+    }
+  }
+  bool isGeo = true;
+  for( uint8_t mergeCand = 1; mergeCand < maxNumMergeCandidates; mergeCand++ )
+  {
+    isGeo &= isSkipThisCand[mergeCand];
+  }
+  if(isGeo)
+  {
+    return;
+  }
+
+  int wIdx = floorLog2(cu.lwidth() ) - GEO_MIN_CU_LOG2;
+  int hIdx = floorLog2(cu.lheight()) - GEO_MIN_CU_LOG2;
+  for( int splitDir = 0; splitDir < GEO_NUM_PARTITION_MODE; splitDir++ )
+  {
+    int maskStride = 0, maskStride2 = 0;
+    int stepX = 1;
+    Pel* SADmask;
+    int16_t angle = g_GeoParams[splitDir][0];
+    if (g_angle2mirror[angle] == 2)
+    {
+      maskStride = - GEO_WEIGHT_MASK_SIZE;
+      maskStride2 = -(int)cu.lwidth();
+      SADmask = &g_globalGeoEncSADmask[g_angle2mask[g_GeoParams[splitDir][0]]][(GEO_WEIGHT_MASK_SIZE - 1 - g_weightOffset[splitDir][hIdx][wIdx][1]) * GEO_WEIGHT_MASK_SIZE + g_weightOffset[splitDir][hIdx][wIdx][0]];
+    }
+    else if (g_angle2mirror[angle] == 1)
+    {
+      stepX = -1;
+      maskStride2 = cu.lwidth();
+      maskStride = GEO_WEIGHT_MASK_SIZE;
+      SADmask = &g_globalGeoEncSADmask[g_angle2mask[g_GeoParams[splitDir][0]]][g_weightOffset[splitDir][hIdx][wIdx][1] * GEO_WEIGHT_MASK_SIZE + (GEO_WEIGHT_MASK_SIZE - 1 - g_weightOffset[splitDir][hIdx][wIdx][0])];
+    }
+    else
+    {
+      maskStride = GEO_WEIGHT_MASK_SIZE;
+      maskStride2 = -(int)cu.lwidth();
+      SADmask = &g_globalGeoEncSADmask[g_angle2mask[g_GeoParams[splitDir][0]]][g_weightOffset[splitDir][hIdx][wIdx][1] * GEO_WEIGHT_MASK_SIZE + g_weightOffset[splitDir][hIdx][wIdx][0]];
+    }
+    Distortion uiSadSmall = 0, uiSadLarge = 0;
+    for( uint8_t mergeCand = 0; mergeCand < maxNumMergeCandidates; mergeCand++ )
+    {
+      int bitsCand = mergeCand + 1;
+
+      m_pcRdCost->setDistParam(distParam, tempCS->getOrgBuf().Y(), geoTempBuf[mergeCand].Y().buf, geoTempBuf[mergeCand].Y().stride, SADmask, maskStride, stepX, maskStride2, sps.getBitDepth(CHANNEL_TYPE_LUMA), COMPONENT_Y);
+      uiSadLarge = distParam.distFunc(distParam);
+      m_GeoCostList.insert(splitDir, 0, mergeCand, (double)uiSadLarge + (double)bitsCand * sqrtLambdaForFirstPass);
+      uiSadSmall = uiSadWholeBlk[mergeCand] - uiSadLarge;
+      m_GeoCostList.insert(splitDir, 1, mergeCand, (double)uiSadSmall + (double)bitsCand * sqrtLambdaForFirstPass);
+    }
+  }
+  delete[] uiSadWholeBlk;
+  delete[] pocMrg;
+  delete[] MrgMv;
+  delete[] isSkipThisCand;
+
+  for( int splitDir = 0; splitDir < GEO_NUM_PARTITION_MODE; splitDir++ )
+  {
+	  for (int GeoMotionIdx = 0; GeoMotionIdx < maxNumMergeCandidates * (maxNumMergeCandidates - 1); GeoMotionIdx++)
+	  {
+		  unsigned int mergeCand0 = m_GeoModeTest[GeoMotionIdx].m_candIdx0;
+		  unsigned int mergeCand1 = m_GeoModeTest[GeoMotionIdx].m_candIdx1;
+	    double tempCost = m_GeoCostList.singleDistList[0][splitDir][mergeCand0].cost + m_GeoCostList.singleDistList[1][splitDir][mergeCand1].cost;
+	    if (tempCost > bestWholeBlkCost)
+		    continue;
+	    int bitsGeoPartNoPred = bitsCandTB;
+	    if(splitDir >= (1 << (bitsCandTB + 1)) - GEO_NUM_PARTITION_MODE)
+		    bitsGeoPartNoPred++;
+	    tempCost = tempCost + (double)bitsGeoPartNoPred * sqrtLambdaForFirstPass;
+	    comboList.list.push_back(GeoMergeCombo(splitDir, mergeCand0, mergeCand1, tempCost));
+	  }
+  }
+  if( comboList.list.empty() )
+    return;
+  comboList.sortByCost();
+  bool geocandHasNoResidual[GEO_MAX_TRY_WEIGHTED_SAD];
+  for( int mergeCand = 0; mergeCand < GEO_MAX_TRY_WEIGHTED_SAD; mergeCand++ )
+  {
+    geocandHasNoResidual[mergeCand] = false;
+  }
+  bool bestIsSkip = false;
+  int geoNumCobo = (int)comboList.list.size();
+  static_vector<uint8_t, GEO_MAX_TRY_WEIGHTED_SAD> geoRdModeList;
+  static_vector<double, GEO_MAX_TRY_WEIGHTED_SAD> geocandCostList;
+
+  DistParam distParamSAD2;
+  const bool useHadamard = !tempCS->slice->getDisableSATDForRD();
+  m_pcRdCost->setDistParam(distParamSAD2, tempCS->getOrgBuf().Y(), m_acMergeBuffer[0].Y(), sps.getBitDepth(CHANNEL_TYPE_LUMA), COMPONENT_Y, useHadamard);  
+  int geoNumMrgSATDCand = min(GEO_MAX_TRY_WEIGHTED_SATD, geoNumCobo);
+
+  for( uint8_t candidateIdx = 0; candidateIdx < min(geoNumCobo, GEO_MAX_TRY_WEIGHTED_SAD); candidateIdx++ )
+  {
+    int splitDir   = comboList.list[candidateIdx].splitDir;
+    int mergeCand0 = comboList.list[candidateIdx].mergeIdx0;
+    int mergeCand1 = comboList.list[candidateIdx].mergeIdx1;
+
+    geoCombinations[candidateIdx] = m_acGeoWeightedBuffer[candidateIdx].getBuf(localUnitArea);
+    m_pcInterSearch->weightedGeoBlk(pu, splitDir, CHANNEL_TYPE_LUMA, geoCombinations[candidateIdx], geoBuffer[mergeCand0], geoBuffer[mergeCand1]);
+    distParamSAD2.cur = geoCombinations[candidateIdx].Y();
+    Distortion uiSad = distParamSAD2.distFunc(distParamSAD2);
+    int bitsGeoPartNoPred = bitsCandTB;
+	if (splitDir >= (1 << (bitsCandTB + 1)) - GEO_NUM_PARTITION_MODE)
+      bitsGeoPartNoPred++;
+    int mvBits = 2;
+    mergeCand1 -= mergeCand1 < mergeCand0 ? 0 : 1;
+    mvBits += mergeCand0;
+    mvBits += mergeCand1;
+    double updateCost = (double)uiSad + (double)(bitsGeoPartNoPred + mvBits) * sqrtLambdaForFirstPass;
+    comboList.list[candidateIdx].cost = updateCost;
+    updateCandList(candidateIdx, updateCost, geoRdModeList, geocandCostList, geoNumMrgSATDCand);
+  }
+  for( uint8_t i = 0; i < geoNumMrgSATDCand; i++ )
+  {
+    if (geocandCostList[i] > MRG_FAST_RATIO * geocandCostList[0] || geocandCostList[i] > getMergeBestSATDCost() || geocandCostList[i] > getAFFBestSATDCost())
+    {
+      geoNumMrgSATDCand = i;
+      break;
+    }
+  }
+  for (uint8_t i = 0; i < geoNumMrgSATDCand && isChromaEnabled(pu.chromaFormat); i++)
+  {
+    uint8_t candidateIdx = geoRdModeList[i];
+    int splitDir   = comboList.list[candidateIdx].splitDir;
+		int mergeCand0 = comboList.list[candidateIdx].mergeIdx0;
+    int mergeCand1 = comboList.list[candidateIdx].mergeIdx1;
+    geoCombinations[candidateIdx] = m_acGeoWeightedBuffer[candidateIdx].getBuf(localUnitArea);
+    m_pcInterSearch->weightedGeoBlk(pu, splitDir, CHANNEL_TYPE_CHROMA, geoCombinations[candidateIdx], geoBuffer[mergeCand0], geoBuffer[mergeCand1]);
+  }
+
+  m_bestModeUpdated = tempCS->useDbCost = bestCS->useDbCost = false;
+  tempCS->initStructData(encTestMode.qp);
+  uint8_t iteration;
+  uint8_t iterationBegin = 0;
+  iteration = 2;
+  for (uint8_t noResidualPass = iterationBegin; noResidualPass < iteration; ++noResidualPass)
+  {
+    for (uint8_t mrgHADIdx = 0; mrgHADIdx < geoNumMrgSATDCand; mrgHADIdx++)
+    {
+      uint8_t candidateIdx = geoRdModeList[mrgHADIdx];
+      if(((noResidualPass != 0) && geocandHasNoResidual[candidateIdx])
+        || ((noResidualPass == 0) && bestIsSkip))
+      {
+        continue;
+      }
+      CodingUnit &cu = tempCS->addCU(tempCS->area, pm.chType);
+      pm.setCUData(cu);
+      cu.predMode  = MODE_INTER;
+      cu.slice     = tempCS->slice;
+      cu.tileIdx   = tempCS->pps->getTileIdx(tempCS->area.lumaPos());
+      cu.qp        = encTestMode.qp;
+      cu.affine    = false;
+      cu.mtsFlag   = false;
+      cu.BcwIdx    = BCW_DEFAULT;
+      cu.geoFlag   = true;
+      cu.imv       = 0;
+      cu.mmvdSkip  = false;
+      cu.skip      = false;
+      cu.mipFlag   = false;
+      cu.bdpcmMode = 0;
+      PredictionUnit &pu = tempCS->addPU(cu, pm.chType);
+      pu.mergeFlag = true;
+      pu.regularMergeFlag = false;
+      pu.geoSplitDir  = comboList.list[candidateIdx].splitDir;
+      pu.geoMergeIdx0 = comboList.list[candidateIdx].mergeIdx0;
+      pu.geoMergeIdx1 = comboList.list[candidateIdx].mergeIdx1;
+      pu.mmvdMergeFlag = false;
+      pu.mmvdMergeIdx = MAX_UINT;
+
+      PU::spanGeoMotionInfo(pu, mergeCtx, pu.geoSplitDir, pu.geoMergeIdx0, pu.geoMergeIdx1);
+      tempCS->getPredBuf().copyFrom(geoCombinations[candidateIdx]);
+
+      xEncodeInterResidual(tempCS, bestCS, pm, encTestMode, noResidualPass, (noResidualPass == 0 ? &geocandHasNoResidual[candidateIdx] : NULL));
+
+      if( m_pcEncCfg->getUseFastDecisionForMerge() && !bestIsSkip )
+      {
+        bestIsSkip = bestCS->getCU(pm.chType)->rootCbf == 0;
+      }
+      tempCS->initStructData(encTestMode.qp);
+    }
+  }
+  if ( m_bestModeUpdated && bestCS->cost != MAX_DOUBLE )
+  {
+    xCalDebCost( *bestCS, pm );
+  }
+}
+#endif
 
 void EncCu::xCheckRDCostAffineMerge2Nx2N( CodingStructure *&tempCS, CodingStructure *&bestCS, Partitioner &partitioner, const EncTestMode& encTestMode )
 {
@@ -3053,6 +3387,10 @@ void EncCu::xCheckRDCostAffineMerge2Nx2N( CodingStructure *&tempCS, CodingStruct
   AffineMergeCtx affineMergeCtx;
   const SPS &sps = *tempCS->sps;
 
+#if JVET_Q0806
+  setAFFBestSATDCost(MAX_DOUBLE);
+#endif
+
   MergeCtx mrgCtx;
   if ( sps.getSBTMVPEnabledFlag() )
   {
@@ -3192,6 +3530,10 @@ void EncCu::xCheckRDCostAffineMerge2Nx2N( CodingStructure *&tempCS, CodingStruct
       }
 
       tempCS->initStructData( encTestMode.qp );
+#if JVET_Q0806
+      setAFFBestSATDCost(candCostList[0]);
+#endif
+
     }
     else
     {
@@ -3344,7 +3686,11 @@ void EncCu::xCheckRDCostIBCModeMerge2Nx2N(CodingStructure *&tempCS, CodingStruct
     cu.mmvdSkip = false;
     pu.mmvdMergeFlag = false;
     pu.regularMergeFlag = false;
+#if !JVET_Q0806
     cu.triangle = false;
+#else
+    cu.geoFlag = false;
+#endif
     PU::getIBCMergeCandidates(pu, mergeCtx);
   }
 
@@ -3378,7 +3724,11 @@ void EncCu::xCheckRDCostIBCModeMerge2Nx2N(CodingStructure *&tempCS, CodingStruct
       cu.chromaQpAdj = m_cuChromaQpOffsetIdxPlus1;
       cu.qp = encTestMode.qp;
       cu.mmvdSkip = false;
+#if !JVET_Q0806
       cu.triangle = false;
+#else
+      cu.geoFlag = false;
+#endif
       DistParam distParam;
       const bool bUseHadamard = !cu.slice->getDisableSATDForRD();
       PredictionUnit &pu = tempCS->addPU(cu, partitioner.chType); //tempCS->addPU(cu);
@@ -3495,7 +3845,11 @@ void EncCu::xCheckRDCostIBCModeMerge2Nx2N(CodingStructure *&tempCS, CodingStruct
             cu.mmvdSkip = false;
             pu.mmvdMergeFlag = false;
             pu.regularMergeFlag = false;
+#if !JVET_Q0806
             cu.triangle = false;
+#else
+            cu.geoFlag = false;
+#endif
             mergeCtx.setMergeInfo(pu, mergeCand);
             PU::spanMotionInfo(pu, mergeCtx);
 
diff --git a/source/Lib/EncoderLib/EncCu.h b/source/Lib/EncoderLib/EncCu.h
index 18b99b00a..1e050c1b0 100644
--- a/source/Lib/EncoderLib/EncCu.h
+++ b/source/Lib/EncoderLib/EncCu.h
@@ -67,6 +67,7 @@ class EncSlice;
 // ====================================================================================================================
 
 /// CU encoder class
+#if !JVET_Q0806
 struct TriangleMotionInfo
 {
   uint8_t   m_splitDir;
@@ -76,6 +77,87 @@ struct TriangleMotionInfo
   TriangleMotionInfo ( uint8_t splitDir, uint8_t candIdx0, uint8_t candIdx1 ): m_splitDir(splitDir), m_candIdx0(candIdx0), m_candIdx1(candIdx1) { }
   TriangleMotionInfo() { m_splitDir = m_candIdx0 = m_candIdx1 = 0; }
 };
+#else
+struct GeoMergeCombo
+{
+  int splitDir;
+  int mergeIdx0;
+  int mergeIdx1;
+  double cost;
+  GeoMergeCombo() : splitDir(), mergeIdx0(-1), mergeIdx1(-1), cost(0.0) {};
+  GeoMergeCombo(int _splitDir, int _mergeIdx0, int _mergeIdx1, double _cost) : splitDir(_splitDir), mergeIdx0(_mergeIdx0), mergeIdx1(_mergeIdx1), cost(_cost) {};
+};
+struct GeoMotionInfo
+{
+	uint8_t   m_candIdx0;
+	uint8_t   m_candIdx1;
+
+	GeoMotionInfo(uint8_t candIdx0, uint8_t candIdx1) : m_candIdx0(candIdx0), m_candIdx1(candIdx1) { }
+	GeoMotionInfo() { m_candIdx0 = m_candIdx1 = 0; }
+};
+struct smaller_than_combo_cost
+{
+  inline bool operator() (const GeoMergeCombo& first, const GeoMergeCombo& second)
+  {
+      return (first.cost < second.cost);
+  }
+};
+class GeoComboCostList
+{
+public:
+  GeoComboCostList() {};
+  ~GeoComboCostList() {};
+  std::vector<GeoMergeCombo> list;
+  void sortByCost() { std::sort(list.begin(), list.end(), smaller_than_combo_cost()); };
+};
+struct SingleGeoMergeEntry
+{
+  int mergeIdx;
+  double cost;
+  SingleGeoMergeEntry() : mergeIdx(0), cost(MAX_DOUBLE) {};
+  SingleGeoMergeEntry(int _mergeIdx, double _cost) : mergeIdx(_mergeIdx), cost(_cost) {};
+};
+class FastGeoCostList
+{
+public:
+  FastGeoCostList() { numGeoTemplatesInitialized = 0; };
+  ~FastGeoCostList() 
+  {
+    for (int partIdx = 0; partIdx < 2; partIdx++)
+    {
+      for (int splitDir = 0; splitDir < GEO_NUM_PARTITION_MODE; splitDir++)
+      {
+        delete[] singleDistList[partIdx][splitDir];
+      }
+      delete[] singleDistList[partIdx];
+      singleDistList[partIdx] = nullptr;
+    }
+  };
+  SingleGeoMergeEntry** singleDistList[2];
+  void init(int numTemplates, int maxNumGeoCand)
+  {
+    if (numGeoTemplatesInitialized == 0 || numGeoTemplatesInitialized < numTemplates)
+    {
+      for (int partIdx = 0; partIdx < 2; partIdx++)
+      {
+        singleDistList[partIdx] = new SingleGeoMergeEntry*[numTemplates];
+        for (int splitDir = 0; splitDir < numTemplates; splitDir++)
+        {
+          singleDistList[partIdx][splitDir] = new SingleGeoMergeEntry[maxNumGeoCand];
+        }
+      }
+      numGeoTemplatesInitialized = numTemplates;
+    }
+  }
+  void insert(int geoIdx, int partIdx, int mergeIdx, double cost)
+  {
+    assert(geoIdx < numGeoTemplatesInitialized);
+	  singleDistList[partIdx][geoIdx][mergeIdx] = SingleGeoMergeEntry(mergeIdx, cost);
+  }
+  int numGeoTemplatesInitialized;
+};
+#endif
+
 class EncCu
   : DecCu
 {
@@ -121,7 +203,13 @@ private:
   PelStorage            m_acMergeBuffer[MMVD_MRG_MAX_RD_BUF_NUM];
   PelStorage            m_acRealMergeBuffer[MRG_MAX_NUM_CANDS];
   PelStorage            m_acMergeTmpBuffer[MRG_MAX_NUM_CANDS];
+#if !JVET_Q0806
   PelStorage            m_acTriangleWeightedBuffer[TRIANGLE_MAX_NUM_CANDS]; // to store weighted prediction pixles
+#else
+  PelStorage            m_acGeoWeightedBuffer[GEO_MAX_TRY_WEIGHTED_SAD]; // to store weighted prediction pixles
+  FastGeoCostList       m_GeoCostList;
+  double                m_AFFBestSATDCost;
+#endif
   double                m_mergeBestSATDCost;
   MotionInfo            m_SubPuMiBuf      [( MAX_CU_SIZE * MAX_CU_SIZE ) >> ( MIN_CU_LOG2 << 1 )];
 
@@ -132,8 +220,12 @@ private:
 #endif
   int                   m_bestBcwIdx[2];
   double                m_bestBcwCost[2];
+#if !JVET_Q0806
   TriangleMotionInfo    m_triangleModeTest[TRIANGLE_MAX_NUM_CANDS];
   uint8_t               m_triangleIdxBins[2][TRIANGLE_MAX_NUM_UNI_CANDS][TRIANGLE_MAX_NUM_UNI_CANDS];
+#else
+  GeoMotionInfo         m_GeoModeTest[GEO_MAX_NUM_CANDS];
+#endif
 #if SHARP_LUMA_DELTA_QP || ENABLE_QPA_SUB_CTU
   void    updateLambda      ( Slice* slice, const int dQP,
  #if WCG_EXT && ER_CHROMA_QP_WCG_PPS
@@ -163,6 +255,10 @@ public:
 
   void   setMergeBestSATDCost(double cost) { m_mergeBestSATDCost = cost; }
   double getMergeBestSATDCost()            { return m_mergeBestSATDCost; }
+#if JVET_Q0806
+  void   setAFFBestSATDCost(double cost)   { m_AFFBestSATDCost = cost; }
+  double getAFFBestSATDCost()              { return m_AFFBestSATDCost; }
+#endif
   IbcHashMap& getIbcHashMap()              { return m_ibcHashMap;        }
   EncCfg*     getEncCfg()            const { return m_pcEncCfg;          }
 
@@ -202,7 +298,11 @@ protected:
 
   void xCheckRDCostMerge2Nx2N ( CodingStructure *&tempCS, CodingStructure *&bestCS, Partitioner &pm, const EncTestMode& encTestMode );
 
+#if !JVET_Q0806
   void xCheckRDCostMergeTriangle2Nx2N( CodingStructure *&tempCS, CodingStructure *&bestCS, Partitioner &pm, const EncTestMode& encTestMode );
+#else
+  void xCheckRDCostMergeGeo2Nx2N(CodingStructure *&tempCS, CodingStructure *&bestCS, Partitioner &pm, const EncTestMode& encTestMode);
+#endif
 
   void xEncodeInterResidual(   CodingStructure *&tempCS
                              , CodingStructure *&bestCS
diff --git a/source/Lib/EncoderLib/EncLib.cpp b/source/Lib/EncoderLib/EncLib.cpp
index 3eb5c0846..ac265e300 100644
--- a/source/Lib/EncoderLib/EncLib.cpp
+++ b/source/Lib/EncoderLib/EncLib.cpp
@@ -990,7 +990,11 @@ void EncLib::xInitSPS( SPS& sps, VPS& vps )
   cinfo->setNoIbcConstraintFlag(m_noIbcConstraintFlag);
   cinfo->setNoCiipConstraintFlag(m_bNoCiipConstraintFlag);
   cinfo->setNoFPelMmvdConstraintFlag(m_noFPelMmvdConstraintFlag);
+#if !JVET_Q0806
   cinfo->setNoTriangleConstraintFlag(m_bNoTriangleConstraintFlag);
+#else
+  cinfo->setNoGeoConstraintFlag(m_bNoGeoConstraintFlag);
+#endif
   cinfo->setNoLadfConstraintFlag(m_bNoLadfConstraintFlag);
   cinfo->setNoTransformSkipConstraintFlag(m_noTransformSkipConstraintFlag);
   cinfo->setNoBDPCMConstraintFlag(m_noBDPCMConstraintFlag);
@@ -1084,7 +1088,11 @@ void EncLib::xInitSPS( SPS& sps, VPS& vps )
 #endif
 
   sps.setUseCiip            ( m_ciip );
+#if !JVET_Q0806
   sps.setUseTriangle           ( m_Triangle );
+#else
+  sps.setUseGeo                ( m_Geo );
+#endif
   sps.setUseMMVD               ( m_MMVD );
   sps.setFpelMmvdEnabledFlag   (( m_MMVD ) ? m_allowDisFracMMVD : false);
   sps.setBdofControlPresentFlag(m_BIO);
@@ -1285,7 +1293,11 @@ void EncLib::xInitPPS(PPS &pps, const SPS &sps)
   pps.setPPSMvdL1ZeroIdc(getPPSMvdL1ZeroIdc());
   pps.setPPSCollocatedFromL0Idc(getPPSCollocatedFromL0Idc());
   pps.setPPSSixMinusMaxNumMergeCandPlus1(getPPSSixMinusMaxNumMergeCandPlus1());
+#if !JVET_Q0806
   pps.setPPSMaxNumMergeCandMinusMaxNumTriangleCandPlus1(getPPSMaxNumMergeCandMinusMaxNumTriangleCandPlus1());
+#else
+  pps.setPPSMaxNumMergeCandMinusMaxNumGeoCandPlus1(getPPSMaxNumMergeCandMinusMaxNumGeoCandPlus1());
+#endif
 
   pps.setNumSubPics(sps.getNumSubPics());
   pps.setSubPicIdSignallingPresentFlag(false);
@@ -1563,7 +1575,11 @@ void EncLib::xInitPicHeader(PicHeader &picHeader, const SPS &sps, const PPS &pps
   // merge list sizes
   picHeader.setMaxNumMergeCand      ( getMaxNumMergeCand()       );
   picHeader.setMaxNumAffineMergeCand( getMaxNumAffineMergeCand() );
+#if !JVET_Q0806
   picHeader.setMaxNumTriangleCand   ( getMaxNumTriangleCand()    );
+#else
+  picHeader.setMaxNumGeoCand        ( getMaxNumGeoCand()         );
+#endif
   picHeader.setMaxNumIBCMergeCand   ( getMaxNumIBCMergeCand()    );
   
   // copy partitioning constraints from SPS
diff --git a/source/Lib/EncoderLib/EncModeCtrl.cpp b/source/Lib/EncoderLib/EncModeCtrl.cpp
index 922835d58..932a628fd 100644
--- a/source/Lib/EncoderLib/EncModeCtrl.cpp
+++ b/source/Lib/EncoderLib/EncModeCtrl.cpp
@@ -1364,10 +1364,17 @@ void EncModeCtrlMTnoRQT::initCULevel( Partitioner &partitioner, const CodingStru
       // add inter modes
       if( m_pcEncCfg->getUseEarlySkipDetection() )
       {
+#if !JVET_Q0806
         if( cs.sps->getUseTriangle() && cs.slice->isInterB() )
         {
           m_ComprCUCtxList.back().testModes.push_back( { ETM_MERGE_TRIANGLE, ETO_STANDARD, qp } );
         }
+#else
+        if( cs.sps->getUseGeo() && cs.slice->isInterB() )
+        {
+          m_ComprCUCtxList.back().testModes.push_back( { ETM_MERGE_GEO, ETO_STANDARD, qp } );
+        }
+#endif
         m_ComprCUCtxList.back().testModes.push_back( { ETM_MERGE_SKIP,  ETO_STANDARD, qp } );
         if ( cs.sps->getUseAffine() || cs.sps->getSBTMVPEnabledFlag() )
         {
@@ -1378,10 +1385,17 @@ void EncModeCtrlMTnoRQT::initCULevel( Partitioner &partitioner, const CodingStru
       else
       {
         m_ComprCUCtxList.back().testModes.push_back( { ETM_INTER_ME,    ETO_STANDARD, qp } );
+#if !JVET_Q0806
         if( cs.sps->getUseTriangle() && cs.slice->isInterB() )
         {
           m_ComprCUCtxList.back().testModes.push_back( { ETM_MERGE_TRIANGLE, ETO_STANDARD, qp } );
         }
+#else
+        if( cs.sps->getUseGeo() && cs.slice->isInterB() )
+        {
+          m_ComprCUCtxList.back().testModes.push_back( { ETM_MERGE_GEO, ETO_STANDARD, qp } );
+        }
+#endif
         m_ComprCUCtxList.back().testModes.push_back( { ETM_MERGE_SKIP,  ETO_STANDARD, qp } );
         if ( cs.sps->getUseAffine() || cs.sps->getSBTMVPEnabledFlag() )
         {
@@ -1419,7 +1433,11 @@ bool EncModeCtrlMTnoRQT::tryMode( const EncTestMode& encTestmode, const CodingSt
   ComprCUCtx& cuECtx = m_ComprCUCtxList.back();
 
   // Fast checks, partitioning depended
+#if !JVET_Q0806
   if (cuECtx.isHashPerfectMatch && encTestmode.type != ETM_MERGE_SKIP && encTestmode.type != ETM_INTER_ME && encTestmode.type != ETM_AFFINE && encTestmode.type != ETM_MERGE_TRIANGLE)
+#else
+  if (cuECtx.isHashPerfectMatch && encTestmode.type != ETM_MERGE_SKIP && encTestmode.type != ETM_INTER_ME && encTestmode.type != ETM_AFFINE && encTestmode.type != ETM_MERGE_GEO)
+#endif
   {
     return false;
   }
@@ -1640,10 +1658,20 @@ bool EncModeCtrlMTnoRQT::tryMode( const EncTestMode& encTestmode, const CodingSt
     {
       return false;
     }
+#if !JVET_Q0806
     if( encTestmode.type == ETM_MERGE_TRIANGLE && ( partitioner.currArea().lumaSize().area() < TRIANGLE_MIN_SIZE || relatedCU.isIntra ) )
     {
       return false;
     }
+#else
+    if( encTestmode.type == ETM_MERGE_GEO && ( partitioner.currArea().lwidth() < GEO_MIN_CU_SIZE || partitioner.currArea().lheight() < GEO_MIN_CU_SIZE  
+                                            || partitioner.currArea().lwidth() > GEO_MAX_CU_SIZE || partitioner.currArea().lheight() > GEO_MAX_CU_SIZE
+		                                        || partitioner.currArea().lwidth() >= 8 * partitioner.currArea().lheight()
+                                            || partitioner.currArea().lheight() >= 8 * partitioner.currArea().lwidth() ) )
+    {
+      return false;
+    }
+#endif
     return true;
   }
   else if( isModeSplit( encTestmode ) )
diff --git a/source/Lib/EncoderLib/EncModeCtrl.h b/source/Lib/EncoderLib/EncModeCtrl.h
index 3ab1b298b..f2b75071d 100644
--- a/source/Lib/EncoderLib/EncModeCtrl.h
+++ b/source/Lib/EncoderLib/EncModeCtrl.h
@@ -59,7 +59,11 @@ enum EncTestModeType
   ETM_MERGE_SKIP,
   ETM_INTER_ME,
   ETM_AFFINE,
+#if !JVET_Q0806
   ETM_MERGE_TRIANGLE,
+#else
+  ETM_MERGE_GEO,
+#endif
   ETM_INTRA,
   ETM_PALETTE,
   ETM_SPLIT_QT,
@@ -138,7 +142,11 @@ inline bool isModeInter( const EncTestMode& encTestmode ) // perhaps remove
   return (   encTestmode.type == ETM_INTER_ME
           || encTestmode.type == ETM_MERGE_SKIP
           || encTestmode.type == ETM_AFFINE
+#if !JVET_Q0806
           || encTestmode.type == ETM_MERGE_TRIANGLE
+#else
+          || encTestmode.type == ETM_MERGE_GEO
+#endif
           || encTestmode.type == ETM_HASH_INTER
          );
 }
diff --git a/source/Lib/EncoderLib/VLCWriter.cpp b/source/Lib/EncoderLib/VLCWriter.cpp
index 4a1fdcc80..25cb753ab 100644
--- a/source/Lib/EncoderLib/VLCWriter.cpp
+++ b/source/Lib/EncoderLib/VLCWriter.cpp
@@ -427,7 +427,11 @@ void HLSWriter::codePPS( const PPS* pcPPS, const SPS* pcSPS )
     WRITE_CODE( pcPPS->getPPSMvdL1ZeroIdc(), 2,                              "pps_mvd_l1_zero_idc");
     WRITE_CODE( pcPPS->getPPSCollocatedFromL0Idc(), 2,                       "pps_collocated_from_l0_idc");
     WRITE_UVLC( pcPPS->getPPSSixMinusMaxNumMergeCandPlus1(),                 "pps_six_minus_max_num_merge_cand_plus1");
+#if !JVET_Q0806
     WRITE_UVLC( pcPPS->getPPSMaxNumMergeCandMinusMaxNumTriangleCandPlus1(),  "pps_max_num_merge_cand_minus_max_num_triangle_cand_plus1");
+#else
+    WRITE_UVLC(pcPPS->getPPSMaxNumMergeCandMinusMaxNumGeoCandPlus1(), "pps_max_num_merge_cand_minus_max_num_gpm_cand_plus1");
+#endif
   }
 
 
@@ -959,7 +963,12 @@ void HLSWriter::codeSPS( const SPS* pcSPS )
   {
     WRITE_FLAG( pcSPS->getFpelMmvdEnabledFlag() ? 1 : 0,                            "sps_fpel_mmvd_enabled_flag" );
   }
+
+#if !JVET_Q0806
   WRITE_FLAG( pcSPS->getUseTriangle() ? 1: 0,                                                  "sps_triangle_enabled_flag" );
+#else
+  WRITE_FLAG( pcSPS->getUseGeo() ? 1: 0,                                                       "sps_gpm_enabled_flag" );
+#endif
 
   WRITE_FLAG(pcSPS->getUseLmcs() ? 1 : 0, "sps_lmcs_enable_flag");
   WRITE_FLAG( pcSPS->getUseLFNST() ? 1 : 0,                                                    "sps_lfnst_enabled_flag" );
@@ -1517,6 +1526,7 @@ void HLSWriter::codePictureHeader( PicHeader* picHeader )
     picHeader->setDisProfFlag(0);
   }
 
+#if !JVET_Q0806
   // triangle merge candidate list size
   if (sps->getUseTriangle() && picHeader->getMaxNumMergeCand() >= 2)
   {
@@ -1530,6 +1540,21 @@ void HLSWriter::codePictureHeader( PicHeader* picHeader )
       picHeader->setMaxNumTriangleCand((uint32_t)(picHeader->getMaxNumMergeCand() - (pps->getPPSMaxNumMergeCandMinusMaxNumTriangleCandPlus1() - 1)));
     }    
   }
+#else
+  // geometric merge candidate list size
+  if (sps->getUseGeo() && picHeader->getMaxNumMergeCand() >= 2)
+  {
+    if (!pps->getPPSMaxNumMergeCandMinusMaxNumGeoCandPlus1())
+    {
+      CHECK(picHeader->getMaxNumMergeCand() < picHeader->getMaxNumGeoCand(), "Incorrrect max number of gpm candidates!");
+      WRITE_UVLC(picHeader->getMaxNumMergeCand() - picHeader->getMaxNumGeoCand(), "pic_max_num_merge_cand_minus_max_num_gpm_cand");
+    }
+    else
+    {
+      picHeader->setMaxNumGeoCand((uint32_t)(picHeader->getMaxNumMergeCand() - (pps->getPPSMaxNumMergeCandMinusMaxNumGeoCandPlus1() - 1)));
+    }
+  }
+#endif
 
   // ibc merge candidate list size
   if (sps->getIBCFlag())
@@ -2103,7 +2128,11 @@ void  HLSWriter::codeConstraintInfo  ( const ConstraintInfo* cinfo )
   WRITE_FLAG(cinfo->getNoIbcConstraintFlag() ? 1 : 0, "no_ibc_constraint_flag");
   WRITE_FLAG(cinfo->getNoCiipConstraintFlag() ? 1 : 0, "no_ciip_constraint_flag");
   WRITE_FLAG(cinfo->getNoFPelMmvdConstraintFlag() ? 1 : 0, "no_fpel_mmvd_constraint_flag");
+#if !JVET_Q0806
   WRITE_FLAG(cinfo->getNoTriangleConstraintFlag() ? 1 : 0, "no_triangle_constraint_flag");
+#else
+  WRITE_FLAG(cinfo->getNoGeoConstraintFlag() ? 1 : 0, "no_gpm_constraint_flag");
+#endif
   WRITE_FLAG(cinfo->getNoLadfConstraintFlag() ? 1 : 0, "no_ladf_constraint_flag");
   WRITE_FLAG(cinfo->getNoTransformSkipConstraintFlag() ? 1 : 0, "no_transform_skip_constraint_flag");
   WRITE_FLAG(cinfo->getNoBDPCMConstraintFlag() ? 1 : 0, "no_bdpcm_constraint_flag");
-- 
GitLab