From 3fdc7d65efe18b73ca5cba129e04f28a23b43a84 Mon Sep 17 00:00:00 2001
From: rlliao <ruling.liao@sg.panasonic.com>
Date: Thu, 1 Nov 2018 13:57:07 +0800
Subject: [PATCH] L0124 & L0208 Triangular shape prediction unit with the bug
 fix

---
 cfg/encoder_lowdelay_vtm.cfg              |   1 +
 cfg/encoder_randomaccess_vtm.cfg          |   1 +
 source/App/EncoderApp/EncApp.cpp          |   3 +
 source/App/EncoderApp/EncAppCfg.cpp       |   9 +
 source/App/EncoderApp/EncAppCfg.h         |   3 +
 source/Lib/CommonLib/CodingStatistics.h   |   8 +
 source/Lib/CommonLib/CommonDef.h          |   8 +
 source/Lib/CommonLib/ContextModelling.cpp |  16 +
 source/Lib/CommonLib/ContextModelling.h   |   3 +
 source/Lib/CommonLib/Contexts.cpp         |  16 +
 source/Lib/CommonLib/Contexts.h           |   4 +
 source/Lib/CommonLib/InterPrediction.cpp  | 684 ++++++++++++++++++++++
 source/Lib/CommonLib/InterPrediction.h    |   8 +
 source/Lib/CommonLib/Rom.cpp              |  84 +++
 source/Lib/CommonLib/Rom.h                |  10 +
 source/Lib/CommonLib/Slice.cpp            |   6 +
 source/Lib/CommonLib/Slice.h              |   7 +
 source/Lib/CommonLib/TypeDef.h            |  10 +
 source/Lib/CommonLib/Unit.cpp             |   6 +
 source/Lib/CommonLib/Unit.h               |   3 +
 source/Lib/CommonLib/UnitTools.cpp        | 489 ++++++++++++++++
 source/Lib/CommonLib/UnitTools.h          |   7 +
 source/Lib/DecoderLib/CABACReader.cpp     |  39 ++
 source/Lib/DecoderLib/CABACReader.h       |   3 +
 source/Lib/DecoderLib/DecCu.cpp           |  27 +
 source/Lib/DecoderLib/DecCu.h             |   4 +
 source/Lib/DecoderLib/VLCReader.cpp       |   3 +
 source/Lib/EncoderLib/CABACWriter.cpp     |  37 ++
 source/Lib/EncoderLib/CABACWriter.h       |   3 +
 source/Lib/EncoderLib/EncCfg.h            |   7 +
 source/Lib/EncoderLib/EncCu.cpp           | 260 ++++++++
 source/Lib/EncoderLib/EncCu.h             |  13 +
 source/Lib/EncoderLib/EncLib.cpp          |   3 +
 source/Lib/EncoderLib/EncModeCtrl.cpp     |  19 +-
 source/Lib/EncoderLib/EncModeCtrl.h       |   6 +
 source/Lib/EncoderLib/InterSearch.cpp     |   6 +
 source/Lib/EncoderLib/VLCWriter.cpp       |   3 +
 37 files changed, 1818 insertions(+), 1 deletion(-)

diff --git a/cfg/encoder_lowdelay_vtm.cfg b/cfg/encoder_lowdelay_vtm.cfg
index 2e9644e55..04f5162bb 100644
--- a/cfg/encoder_lowdelay_vtm.cfg
+++ b/cfg/encoder_lowdelay_vtm.cfg
@@ -130,6 +130,7 @@ ALF                          : 1
 GBi                          : 1 
 GBiFast                      : 1 
 MHIntra                      : 1
+Triangle                     : 1
 
 # Fast tools
 PBIntraFast                  : 1
diff --git a/cfg/encoder_randomaccess_vtm.cfg b/cfg/encoder_randomaccess_vtm.cfg
index 0bce50263..7e8715098 100644
--- a/cfg/encoder_randomaccess_vtm.cfg
+++ b/cfg/encoder_randomaccess_vtm.cfg
@@ -145,6 +145,7 @@ GBi                          : 1
 GBiFast                      : 1
 BIO                          : 1 
 MHIntra                      : 1
+Triangle                     : 1
 
 # Fast tools
 PBIntraFast                  : 1
diff --git a/source/App/EncoderApp/EncApp.cpp b/source/App/EncoderApp/EncApp.cpp
index 13cfd5424..c4fb670b0 100644
--- a/source/App/EncoderApp/EncApp.cpp
+++ b/source/App/EncoderApp/EncApp.cpp
@@ -263,6 +263,9 @@ void EncApp::xInitLibCfg()
 #endif  
 #if JVET_L0100_MULTI_HYPOTHESIS_INTRA
   m_cEncLib.setUseMHIntra                                        ( m_MHIntra );
+#endif
+#if JVET_L0124_L0208_TRIANGLE
+  m_cEncLib.setUseTriangle                                       ( m_Triangle );
 #endif
   // ADD_NEW_TOOL : (encoder app) add setting of tool enabling flags and associated parameters here
 
diff --git a/source/App/EncoderApp/EncAppCfg.cpp b/source/App/EncoderApp/EncAppCfg.cpp
index 93ba95291..14ea3f1e3 100644
--- a/source/App/EncoderApp/EncAppCfg.cpp
+++ b/source/App/EncoderApp/EncAppCfg.cpp
@@ -868,6 +868,9 @@ bool EncAppCfg::parseCfg( int argc, char* argv[] )
 #endif
 #if JVET_L0100_MULTI_HYPOTHESIS_INTRA
   ("MHIntra",                                         m_MHIntra,                                        false, "Enable MHIntra mode")
+#endif
+#if JVET_L0124_L0208_TRIANGLE
+  ("Triangle",                                        m_Triangle,                                       false, "Enable triangular shape motion vector prediction (0:off, 1:on)  [default: on]")
 #endif
   // ADD_NEW_TOOL : (encoder app) add parsing parameters here
 
@@ -1966,6 +1969,9 @@ bool EncAppCfg::xCheckParameter()
 #if JVET_L0646_GBI
     xConfirmPara( m_GBi, "GBi is only allowed with NEXT profile" );
     xConfirmPara( m_GBiFast, "GBiFast is only allowed with NEXT profile" );
+#endif
+#if JVET_L0124_L0208_TRIANGLE
+    xConfirmPara( m_Triangle, "Triangle is only allowed with NEXT profile" );
 #endif
     // ADD_NEW_TOOL : (parameter check) add a check for next tools here
   }
@@ -3191,6 +3197,9 @@ void EncAppCfg::xPrintParameter()
 #endif
 #if JVET_L0100_MULTI_HYPOTHESIS_INTRA
     msg(VERBOSE, "MHIntra:%d ", m_MHIntra);
+#endif
+#if JVET_L0124_L0208_TRIANGLE
+    msg( VERBOSE, "Triangle:%d ", m_Triangle );
 #endif
   }
   // ADD_NEW_TOOL (add some output indicating the usage of tools)
diff --git a/source/App/EncoderApp/EncAppCfg.h b/source/App/EncoderApp/EncAppCfg.h
index 27b243101..0bc0a2446 100644
--- a/source/App/EncoderApp/EncAppCfg.h
+++ b/source/App/EncoderApp/EncAppCfg.h
@@ -240,6 +240,9 @@ protected:
 
 #if JVET_L0100_MULTI_HYPOTHESIS_INTRA
   bool      m_MHIntra;
+#endif
+#if JVET_L0124_L0208_TRIANGLE
+  bool      m_Triangle;
 #endif
   // ADD_NEW_TOOL : (encoder app) add tool enabling flags and associated parameters here
 
diff --git a/source/Lib/CommonLib/CodingStatistics.h b/source/Lib/CommonLib/CodingStatistics.h
index 3c82f4abe..a00a49b10 100644
--- a/source/Lib/CommonLib/CodingStatistics.h
+++ b/source/Lib/CommonLib/CodingStatistics.h
@@ -108,6 +108,10 @@ enum CodingStatisticsType
   STATS__TOOL_EMT,
 #if JVET_L0100_MULTI_HYPOTHESIS_INTRA
   STATS__CABAC_BITS__MH_INTRA_FLAG,
+#endif
+#if JVET_L0124_L0208_TRIANGLE
+  STATS__CABAC_BITS__TRIANGLE_FLAG,
+  STATS__CABAC_BITS__TRIANGLE_INDEX,
 #endif
   STATS__TOOL_TOTAL,
   STATS__NUM_STATS
@@ -183,6 +187,10 @@ static inline const char* getName(CodingStatisticsType name)
     "CABAC_BITS__EMT_TU_INDX",
 #if JVET_L0100_MULTI_HYPOTHESIS_INTRA
     "CABAC_BITS__MH_INTRA_FLAG",
+#endif
+#if JVET_L0124_L0208_TRIANGLE
+    "CABAC_BITS__TRIANGLE_FLAG",
+    "CABAC_BITS__TRIANGLE_INDEX",
 #endif
     "CABAC_BITS__OTHER",
     "CABAC_BITS__INVALID",
diff --git a/source/Lib/CommonLib/CommonDef.h b/source/Lib/CommonLib/CommonDef.h
index 59fa2e336..f21d96a90 100644
--- a/source/Lib/CommonLib/CommonDef.h
+++ b/source/Lib/CommonLib/CommonDef.h
@@ -415,6 +415,14 @@ static const int MAX_LADF_INTERVALS       =                         5; /// max n
 static const int NTAPS_BILINEAR           =                         2; ///< Number of taps for bilinear filter
 #endif
 
+#if JVET_L0124_L0208_TRIANGLE
+static const int TRIANGLE_MAX_NUM_UNI_CANDS =                       5;
+static const int TRIANGLE_MAX_NUM_CANDS_MEM =                       7;
+static const int TRIANGLE_MAX_NUM_CANDS =                          40;
+static const int TRIANGLE_MAX_NUM_SATD_CANDS =                      3;
+static const int TRIANGLE_MIN_SIZE =                            8 * 8;
+#endif
+
 // ====================================================================================================================
 // Macro functions
 // ====================================================================================================================
diff --git a/source/Lib/CommonLib/ContextModelling.cpp b/source/Lib/CommonLib/ContextModelling.cpp
index 300e861c3..f2787ae42 100644
--- a/source/Lib/CommonLib/ContextModelling.cpp
+++ b/source/Lib/CommonLib/ContextModelling.cpp
@@ -359,6 +359,22 @@ unsigned DeriveCtx::CtxBTsplit(const CodingStructure& cs, Partitioner& partition
   return ctx;
 }
 
+#if JVET_L0124_L0208_TRIANGLE
+unsigned DeriveCtx::CtxTriangleFlag( const CodingUnit& cu )
+{
+  const CodingStructure *cs = cu.cs;
+  unsigned ctxId = 0;
+
+  const CodingUnit *cuLeft = cs->getCURestricted( cu.lumaPos().offset( -1, 0 ), cu, CH_L );
+  ctxId = ( cuLeft && cuLeft->triangle ) ? 1 : 0;
+
+  const CodingUnit *cuAbove = cs->getCURestricted( cu.lumaPos().offset( 0, -1 ), cu, CH_L );
+  ctxId += ( cuAbove && cuAbove->triangle ) ? 1 : 0;
+
+  return ctxId;
+}
+#endif
+
 
 void MergeCtx::setMergeInfo( PredictionUnit& pu, int candIdx )
 {
diff --git a/source/Lib/CommonLib/ContextModelling.h b/source/Lib/CommonLib/ContextModelling.h
index 2e03bc1b4..806b6cc57 100644
--- a/source/Lib/CommonLib/ContextModelling.h
+++ b/source/Lib/CommonLib/ContextModelling.h
@@ -353,6 +353,9 @@ unsigned CtxInterDir  ( const PredictionUnit& pu );
 unsigned CtxSkipFlag  ( const CodingUnit& cu );
 unsigned CtxIMVFlag   ( const CodingUnit& cu );
 unsigned CtxAffineFlag( const CodingUnit& cu );
+#if JVET_L0124_L0208_TRIANGLE
+unsigned CtxTriangleFlag( const CodingUnit& cu );
+#endif
 }
 
 #endif // __CONTEXTMODELLING__
diff --git a/source/Lib/CommonLib/Contexts.cpp b/source/Lib/CommonLib/Contexts.cpp
index 7fd0d1de7..4130a6462 100644
--- a/source/Lib/CommonLib/Contexts.cpp
+++ b/source/Lib/CommonLib/Contexts.cpp
@@ -823,6 +823,22 @@ const CtxSet ContextSetCfg::MHIntraPredMode = ContextSetCfg::addCtxSet
 });
 #endif
 
+#if JVET_L0124_L0208_TRIANGLE
+const CtxSet ContextSetCfg::TriangleFlag = ContextSetCfg::addCtxSet
+({
+  { 151, 137, 154, },
+  { 151, 137, 154, },
+  { CNU, CNU, CNU, },
+});
+
+const CtxSet ContextSetCfg::TriangleIdx = ContextSetCfg::addCtxSet
+({
+  { 140, },
+  { 140, },
+  { CNU, },
+});
+#endif
+
 const unsigned ContextSetCfg::NumberOfContexts = (unsigned)ContextSetCfg::sm_InitTables[0].size();
 
 
diff --git a/source/Lib/CommonLib/Contexts.h b/source/Lib/CommonLib/Contexts.h
index 31b4bafe1..718a8b234 100644
--- a/source/Lib/CommonLib/Contexts.h
+++ b/source/Lib/CommonLib/Contexts.h
@@ -206,6 +206,10 @@ public:
 #if JVET_L0100_MULTI_HYPOTHESIS_INTRA
   static const CtxSet   MHIntraFlag;
   static const CtxSet   MHIntraPredMode;
+#endif
+#if JVET_L0124_L0208_TRIANGLE
+  static const CtxSet   TriangleFlag;
+  static const CtxSet   TriangleIdx;
 #endif
   static const unsigned NumberOfContexts;
 
diff --git a/source/Lib/CommonLib/InterPrediction.cpp b/source/Lib/CommonLib/InterPrediction.cpp
index 7a7979567..410cf1eee 100644
--- a/source/Lib/CommonLib/InterPrediction.cpp
+++ b/source/Lib/CommonLib/InterPrediction.cpp
@@ -120,6 +120,10 @@ void InterPrediction::destroy()
     }
   }
 
+#if JVET_L0124_L0208_TRIANGLE
+  m_tmpTriangleBuf.destroy();
+#endif
+
 #if JVET_L0265_AFF_MINIMUM4X4
   if (m_storedMv != nullptr)
   {
@@ -175,6 +179,9 @@ void InterPrediction::init( RdCost* pcRdCost, ChromaFormat chromaFormatIDC )
       }
     }
 
+#if JVET_L0124_L0208_TRIANGLE
+    m_tmpTriangleBuf.create(UnitArea(chromaFormatIDC, Area(0, 0, MAX_CU_SIZE, MAX_CU_SIZE)));
+#endif
 
     m_iRefListIdx = -1;
   
@@ -481,7 +488,11 @@ void InterPrediction::xPredInterBi(PredictionUnit& pu, PelUnitBuf &pcYuvPred)
       }
       else
       {
+#if JVET_L0124_L0208_TRIANGLE
+        xPredInterUni ( pu, eRefPicList, pcMbBuf, pu.cu->triangle, false );
+#else
         xPredInterUni ( pu, eRefPicList, pcMbBuf, false );
+#endif
       }
     }
   }
@@ -1069,10 +1080,24 @@ void InterPrediction::xWeightedAverage( const PredictionUnit& pu, const CPelUnit
   }
   else if( iRefIdx0 >= 0 && iRefIdx1 < 0 )
   {
+#if JVET_L0124_L0208_TRIANGLE
+    if( pu.cu->triangle )
+    {
+      pcYuvDst.copyFrom( pcYuvSrc0 );
+    }
+    else
+#endif 
     pcYuvDst.copyClip( pcYuvSrc0, clpRngs );
   }
   else if( iRefIdx0 < 0 && iRefIdx1 >= 0 )
   {
+#if JVET_L0124_L0208_TRIANGLE
+    if( pu.cu->triangle )
+    {
+      pcYuvDst.copyFrom( pcYuvSrc1 );
+    }
+    else
+#endif
     pcYuvDst.copyClip( pcYuvSrc1, clpRngs );
   }
 }
@@ -1152,6 +1177,665 @@ int InterPrediction::rightShiftMSB(int numer, int denom)
 }
 #endif
 
+#if JVET_L0124_L0208_TRIANGLE
+void InterPrediction::motionCompensation4Triangle( CodingUnit &cu, MergeCtx &TriangleMrgCtx, const bool SplitDir, const uint8_t CandIdx0, const uint8_t CandIdx1 )
+{
+  for( auto &pu : CU::traversePUs( cu ) )
+  {
+    const UnitArea localUnitArea( cu.cs->area.chromaFormat, Area( 0, 0, pu.lwidth(), pu.lheight() ) );
+    PelUnitBuf tmpTriangleBuf = m_tmpTriangleBuf.getBuf( localUnitArea );
+    PelUnitBuf predBuf        = cu.cs->getPredBuf( pu );
+     
+    TriangleMrgCtx.setMergeInfo( pu, CandIdx0 );
+    PU::spanMotionInfo( pu );
+    motionCompensation( pu, tmpTriangleBuf );
+   
+    TriangleMrgCtx.setMergeInfo( pu, CandIdx1 );
+    PU::spanMotionInfo( pu );
+    motionCompensation( pu, predBuf );
+
+    TriangleWeighting( pu, PU::isTriangleEhancedWeight(pu, TriangleMrgCtx, CandIdx0, CandIdx1), SplitDir, MAX_NUM_CHANNEL_TYPE, predBuf, tmpTriangleBuf, predBuf );
+  }
+}
+
+void InterPrediction::TriangleWeighting( PredictionUnit &pu, bool ehanced, const bool SplitDir, int32_t channel, PelUnitBuf& PredDst, PelUnitBuf& PredSrc0, PelUnitBuf& PredSrc1 )
+{
+  const uint32_t WidthY    = pu.lumaSize()  .width;
+  const uint32_t HeightY   = pu.lumaSize()  .height;
+  const uint32_t WidthUV   = pu.chromaSize().width;
+  const uint32_t HeightUV  = pu.chromaSize().height;
+
+        Pel*     DstY      = PredDst .get(COMPONENT_Y).buf;
+        Pel*     Src0Y     = PredSrc0.get(COMPONENT_Y).buf;
+        Pel*     Src1Y     = PredSrc1.get(COMPONENT_Y).buf;
+        Pel*     DstU      = PredDst .get(COMPONENT_Cb).buf;
+        Pel*     Src0U     = PredSrc0.get(COMPONENT_Cb).buf;
+        Pel*     Src1U     = PredSrc1.get(COMPONENT_Cb).buf;
+        Pel*     DstV      = PredDst .get(COMPONENT_Cr).buf;
+        Pel*     Src0V     = PredSrc0.get(COMPONENT_Cr).buf;
+        Pel*     Src1V     = PredSrc1.get(COMPONENT_Cr).buf;
+                           
+        int      strideY   = PredDst .get(COMPONENT_Y).stride  - WidthY;
+        int      stride0Y  = PredSrc0.get(COMPONENT_Y).stride  - WidthY;
+        int      stride1Y  = PredSrc1.get(COMPONENT_Y).stride  - WidthY;
+        int      strideU   = PredDst .get(COMPONENT_Cb).stride - WidthUV;
+        int      stride0U  = PredSrc0.get(COMPONENT_Cb).stride - WidthUV;
+        int      stride1U  = PredSrc1.get(COMPONENT_Cb).stride - WidthUV;
+                           
+  const bool     format    = PredDst.chromaFormat == CHROMA_444 ? 0 : 1;
+
+  const int32_t BlendLengthY  = g_TriangleWeightLengthLuma[ehanced];
+  const int32_t BlendLengthUV = g_TriangleWeightLengthChroma[format][ehanced];
+
+  const char log2WeightBase = 3;
+  const ClpRng clipRngY     = pu.cu->slice->clpRngs().comp[0];
+  const ClpRng clipRngU     = pu.cu->slice->clpRngs().comp[1];
+  const ClpRng clipRngV     = pu.cu->slice->clpRngs().comp[2];
+  const int clipbdY         = clipRngY.bd;
+  const int clipbdU         = clipRngU.bd;
+  const int clipbdV         = clipRngV.bd;
+  const int shiftNumY       = std::max<int>(2, (IF_INTERNAL_PREC - clipbdY)) + log2WeightBase;
+  const int offsetY         = (1 << (shiftNumY - 1)) + (IF_INTERNAL_OFFS << log2WeightBase);
+  const int shiftNumU       = std::max<int>(2, (IF_INTERNAL_PREC - clipbdU)) + log2WeightBase;
+  const int offsetU         = (1 << (shiftNumU - 1)) + (IF_INTERNAL_OFFS << log2WeightBase);
+  const int shiftNumV       = std::max<int>(2, (IF_INTERNAL_PREC - clipbdV)) + log2WeightBase;
+  const int offsetV         = (1 << (shiftNumV - 1)) + (IF_INTERNAL_OFFS << log2WeightBase);
+
+  const int shiftDefault    = std::max<int>(2, (IF_INTERNAL_PREC - clipbdY));
+  const int offsetDefault   = (1<<(shiftDefault-1)) + IF_INTERNAL_OFFS;
+
+  const Pel*   TmpPelWeighted;
+
+  if( channel == CHANNEL_TYPE_LUMA || channel == MAX_NUM_CHANNEL_TYPE )
+  {
+    if( SplitDir == TRIANGLE_DIR_135 && WidthY == HeightY )
+    {
+      int32_t BlendStart = 0 - (BlendLengthY >> 1);
+      int32_t BlendEnd   = 0 + (BlendLengthY >> 1);
+      for( int32_t y = 0; y < HeightY; y++ )
+      {
+        int32_t x = 0;
+        for( x = 0; x < BlendStart; x++ )
+        {
+          *DstY++ = ClipPel( rightShift( *Src1Y++ + offsetDefault, shiftDefault ), clipRngY );
+        }
+        Src0Y += x;
+
+        int32_t TmpBlendStart = std::max( (int32_t)0, BlendStart );
+        int32_t TmpBlendEnd   = std::min( BlendEnd, (int32_t)(WidthY - 1) );
+        TmpPelWeighted        = g_TrianglePelWeightedLuma[SplitDir][ehanced];
+        if( BlendStart < 0 )
+        {
+          TmpPelWeighted += abs( BlendStart );
+        }
+        for( x = TmpBlendStart; x <= TmpBlendEnd; x++ )
+        {
+          *DstY++ = ClipPel( rightShift( ((*TmpPelWeighted)*(*Src0Y++) + ((8 - (*TmpPelWeighted)) * (*Src1Y++)) + offsetY), shiftNumY ), clipRngY );
+          TmpPelWeighted++;
+        }
+
+        for( x = BlendEnd + 1; x < WidthY; x++ )
+        {
+          *DstY++ = ClipPel( rightShift( *Src0Y++ + offsetDefault, shiftDefault ), clipRngY );
+          Src1Y++;
+        }
+
+        BlendStart++;
+        BlendEnd++;
+
+        DstY  += strideY;
+        Src0Y += stride0Y;
+        Src1Y += stride1Y;
+      }
+    }
+    else if( SplitDir == TRIANGLE_DIR_135 && WidthY > HeightY )
+    {
+      int32_t RatioWH     = WidthY / HeightY;
+      int32_t BlendStart = 0 - (BlendLengthY >> 1) * RatioWH;
+      int32_t BlendEnd   = BlendStart + BlendLengthY * RatioWH - 1;
+      for( int32_t y = 0; y < HeightY; y++ )
+      {
+        int32_t x = 0;
+        for( x = 0; x < BlendStart; x++ )
+        {
+          *DstY++ = ClipPel( rightShift( *Src1Y++ + offsetDefault, shiftDefault ), clipRngY );
+        }
+        Src0Y += x;
+
+        int32_t TmpBlendStart = std::max((int32_t)0, BlendStart);
+        int32_t TmpBlendEnd   = std::min(BlendEnd, (int32_t)(WidthY - 1) );
+        TmpPelWeighted    = g_TrianglePelWeightedLuma[SplitDir][ehanced];
+        if( BlendStart < 0 )
+        {
+          TmpPelWeighted += abs(BlendStart) / RatioWH;
+        }
+        for( x = TmpBlendStart; x <= TmpBlendEnd; x += RatioWH )
+        {
+          for( int32_t Cnt = 0; Cnt < RatioWH; Cnt++ )
+          {
+            *DstY++ = ClipPel( rightShift( ((*TmpPelWeighted)*(*Src0Y++) + ((8 - (*TmpPelWeighted)) * (*Src1Y++)) + offsetY), shiftNumY ), clipRngY );
+          }
+          TmpPelWeighted++;
+        }
+
+        for( x = BlendEnd + 1; x < WidthY; x++ )
+        {
+          *DstY++ = ClipPel( rightShift( *Src0Y++ + offsetDefault, shiftDefault ), clipRngY );
+          Src1Y++;
+        }
+        
+        BlendStart += RatioWH;
+        BlendEnd   += RatioWH;
+
+        DstY  += strideY;
+        Src0Y += stride0Y;
+        Src1Y += stride1Y;
+      }
+    }
+    else if( SplitDir == TRIANGLE_DIR_135 && WidthY < HeightY )
+    {
+      int32_t RatioHW     = HeightY / WidthY;
+      int32_t BlendStart = 0 - (BlendLengthY >> 1);
+      int32_t BlendEnd   = BlendStart + BlendLengthY - 1;
+      int32_t Cnt        = RatioHW;
+      for( int32_t y = 0; y < HeightY; y++ )
+      {
+        int32_t x = 0;
+        for( x = 0; x < BlendStart; x++ )
+        {
+          *DstY++ = ClipPel( rightShift( *Src1Y++ + offsetDefault, shiftDefault ), clipRngY );
+        }
+        Src0Y += x;
+
+        int32_t TmpBlendStart = std::max((int32_t)0, BlendStart);
+        int32_t TmpBlendEnd   = std::min(BlendEnd, (int32_t)(WidthY - 1));
+        TmpPelWeighted    = g_TrianglePelWeightedLuma[SplitDir][ehanced];
+        if( BlendStart < 0 )
+        {
+          TmpPelWeighted += abs(BlendStart);
+        }
+        for( x = TmpBlendStart; x <= TmpBlendEnd; x++ )
+        {
+          *DstY++ = ClipPel( rightShift( ((*TmpPelWeighted)*(*Src0Y++) + ((8 - (*TmpPelWeighted)) * (*Src1Y++)) + offsetY), shiftNumY ), clipRngY );
+          TmpPelWeighted++;
+        }
+
+        for( x = BlendEnd + 1; x < WidthY; x++ )
+        {
+          *DstY++ = ClipPel( rightShift( *Src0Y++ + offsetDefault, shiftDefault ), clipRngY );
+          Src1Y++;
+        }
+        
+        Cnt--;
+        if( Cnt <= 0 )
+        {
+          BlendStart++;
+          BlendEnd++;
+          Cnt = RatioHW;
+        }
+
+        DstY  += strideY;
+        Src0Y += stride0Y;
+        Src1Y += stride1Y;
+      }
+    }
+    else if( SplitDir == TRIANGLE_DIR_45 && WidthY == HeightY )
+    {
+      int32_t BlendStart = (WidthY - 1) - (BlendLengthY >> 1);
+      int32_t BlendEnd   = (WidthY - 1) + (BlendLengthY >> 1);
+      for( int32_t y = 0; y < HeightY; y++ )
+      {
+        int32_t x = 0;
+        for( x = 0; x < BlendStart; x++ )
+        {
+          *DstY++ = ClipPel( rightShift( *Src0Y++ + offsetDefault, shiftDefault ), clipRngY );
+        }
+        Src1Y += x;
+
+        int32_t TmpBlendStart = std::max((int32_t)0, BlendStart);
+        int32_t TmpBlendEnd   = std::min(BlendEnd, (int32_t)(WidthY - 1));
+        TmpPelWeighted    = g_TrianglePelWeightedLuma[SplitDir][ehanced];
+        if( BlendStart < 0 )
+        {
+          TmpPelWeighted += abs(BlendStart);
+        }
+        for( x = TmpBlendStart; x <= TmpBlendEnd; x++ )
+        {
+          *DstY++ = ClipPel( rightShift( ((*TmpPelWeighted)*(*Src0Y++) + ((8 - (*TmpPelWeighted)) * (*Src1Y++)) + offsetY), shiftNumY ), clipRngY );
+          TmpPelWeighted++;
+        }
+
+        for( x = BlendEnd + 1; x < WidthY; x++ )
+        {
+          *DstY++ = ClipPel( rightShift( *Src1Y++ + offsetDefault, shiftDefault ), clipRngY );
+          Src0Y++;
+        }
+        
+        BlendStart--;
+        BlendEnd--;
+
+        DstY  += strideY;
+        Src0Y += stride0Y;
+        Src1Y += stride1Y;
+      }
+    }
+    else if( SplitDir == TRIANGLE_DIR_45 && WidthY > HeightY )
+    {
+      int32_t RatioWH     = WidthY / HeightY;
+      int32_t BlendStart = WidthY - ((BlendLengthY + 1) >> 1) * RatioWH;
+      int32_t BlendEnd   = BlendStart + BlendLengthY * RatioWH - 1;
+      for( int32_t y = 0; y < HeightY; y++ )
+      {
+        int32_t x = 0;
+        for( x = 0; x < BlendStart; x++ )
+        {
+          *DstY++ = ClipPel( rightShift( *Src0Y++ + offsetDefault, shiftDefault ), clipRngY );
+        }
+        Src1Y += x;
+
+        int32_t TmpBlendStart = std::max((int32_t)0, BlendStart);
+        int32_t TmpBlendEnd   = std::min(BlendEnd, (int32_t)(WidthY - 1));
+        TmpPelWeighted        = g_TrianglePelWeightedLuma[SplitDir][ehanced];
+        if( BlendStart < 0 )
+        {
+          TmpPelWeighted += abs(BlendStart) / RatioWH;
+        }
+        for( x = TmpBlendStart; x <= TmpBlendEnd; x += RatioWH )
+        {
+          for( int32_t Cnt = 0; Cnt < RatioWH; Cnt++ )
+          {
+            *DstY++ = ClipPel( rightShift( ((*TmpPelWeighted)*(*Src0Y++) + ((8 - (*TmpPelWeighted)) * (*Src1Y++)) + offsetY), shiftNumY ), clipRngY );
+          }
+          TmpPelWeighted++;
+        }
+
+        for( x = BlendEnd + 1; x < WidthY; x++ )
+        {
+          *DstY++ = ClipPel( rightShift( *Src1Y++ + offsetDefault, shiftDefault ), clipRngY );
+          Src0Y++;
+        }
+        
+        BlendStart -= RatioWH;
+        BlendEnd   -= RatioWH;
+
+        DstY  += strideY;
+        Src0Y += stride0Y;
+        Src1Y += stride1Y;
+      }
+    }
+    else if( SplitDir == TRIANGLE_DIR_45 && WidthY < HeightY )
+    {
+      int32_t RatioHW     = HeightY / WidthY;
+      int32_t BlendStart = WidthY - ((BlendLengthY + 1) >> 1);
+      int32_t BlendEnd   = BlendStart + BlendLengthY - 1;
+      int32_t Cnt        = RatioHW;
+      for( int32_t y = 0; y < HeightY; y++ )
+      {
+        int32_t x = 0;
+        for( x = 0; x < BlendStart; x++ )
+        {
+          *DstY++ = ClipPel( rightShift( *Src0Y++ + offsetDefault, shiftDefault ), clipRngY );
+        }
+        Src1Y += x;
+
+        int32_t TmpBlendStart = std::max((int32_t)0, BlendStart);
+        int32_t TmpBlendEnd   = std::min(BlendEnd, (int32_t)(WidthY - 1));
+        TmpPelWeighted    = g_TrianglePelWeightedLuma[SplitDir][ehanced];
+        if( BlendStart < 0 )
+        {
+          TmpPelWeighted += abs(BlendStart);
+        }
+        for( x = TmpBlendStart; x <= TmpBlendEnd; x++ )
+        {
+          *DstY++ = ClipPel( rightShift( ((*TmpPelWeighted)*(*Src0Y++) + ((8 - (*TmpPelWeighted)) * (*Src1Y++)) + offsetY), shiftNumY ), clipRngY );
+          TmpPelWeighted++;
+        }
+
+        for( x = BlendEnd + 1; x < WidthY; x++ )
+        {
+          *DstY++ = ClipPel( rightShift( *Src1Y++ + offsetDefault, shiftDefault ), clipRngY );
+          Src0Y++;
+        }
+        
+        Cnt--;
+        if( Cnt <= 0 )
+        {
+          BlendStart--;
+          BlendEnd--;
+          Cnt = RatioHW;
+        }
+
+        DstY  += strideY;
+        Src0Y += stride0Y;
+        Src1Y += stride1Y;
+      }
+    }
+    else
+    {
+      assert(0);
+    }
+  }
+
+  if( channel == CHANNEL_TYPE_CHROMA || channel == MAX_NUM_CHANNEL_TYPE )
+  {
+    if( SplitDir == TRIANGLE_DIR_135 && WidthY == HeightY )
+    {
+      int32_t BlendStart = 0 - (BlendLengthUV >> 1);
+      int32_t BlendEnd   = 0 + (BlendLengthUV >> 1);
+      for( int32_t y = 0; y < HeightUV; y++ )
+      {
+        int x = 0;
+        for( x = 0; x < BlendStart; x++ )
+        {
+          *DstU++ = ClipPel( rightShift( *Src1U++ + offsetDefault, shiftDefault ), clipRngU );
+          *DstV++ = ClipPel( rightShift( *Src1V++ + offsetDefault, shiftDefault ), clipRngV );
+        }
+        Src0U += x;
+        Src0V += x;
+
+        int32_t TmpBlendStart = std::max((int32_t)0, BlendStart);
+        int32_t TmpBlendEnd   = std::min(BlendEnd, (int32_t)(WidthUV - 1));
+        TmpPelWeighted    = g_TrianglePelWeightedChroma[format][SplitDir][ehanced];
+        if( BlendStart < 0 )
+        {
+          TmpPelWeighted += abs(BlendStart);
+        }
+        for( x = TmpBlendStart; x <= TmpBlendEnd; x++ )
+        {
+          *DstU++ = ClipPel( rightShift( ((*TmpPelWeighted)*(*Src0U++) + ((8 - (*TmpPelWeighted)) * (*Src1U++)) + offsetU), shiftNumU ), clipRngU );
+          *DstV++ = ClipPel( rightShift( ((*TmpPelWeighted)*(*Src0V++) + ((8 - (*TmpPelWeighted)) * (*Src1V++)) + offsetV), shiftNumV ), clipRngV );
+          TmpPelWeighted++;
+        }
+
+        for( x = BlendEnd + 1; x < WidthUV; x++ )
+        {
+          *DstU++ = ClipPel( rightShift( *Src0U++ + offsetDefault, shiftDefault ), clipRngU );
+          *DstV++ = ClipPel( rightShift( *Src0V++ + offsetDefault, shiftDefault ), clipRngV );
+          Src1U++;
+          Src1V++;
+        }
+
+        BlendStart++;
+        BlendEnd++;
+
+        DstU  += strideU;
+        Src0U += stride0U;
+        Src1U += stride1U;
+        DstV  += strideU;
+        Src0V += stride0U;
+        Src1V += stride1U;
+      }
+    }
+    else if( SplitDir == TRIANGLE_DIR_135 && WidthY > HeightY )
+    {
+      int32_t RatioWH     = WidthY / HeightY;
+      int32_t BlendStart = 0 - (BlendLengthUV >> 1) * RatioWH;
+      int32_t BlendEnd   = BlendStart + BlendLengthUV * RatioWH - 1;
+      for( int32_t y = 0; y < HeightUV; y++ )
+      {
+        int32_t x = 0;
+        for( x = 0; x < BlendStart; x++ )
+        {
+          *DstU++ = ClipPel( rightShift( *Src1U++ + offsetDefault, shiftDefault ), clipRngU );
+          *DstV++ = ClipPel( rightShift( *Src1V++ + offsetDefault, shiftDefault ), clipRngV );
+        }
+        Src0U += x;
+        Src0V += x;
+
+        int32_t TmpBlendStart = std::max((int32_t)0, BlendStart);
+        int32_t TmpBlendEnd   = std::min(BlendEnd, (int32_t)(WidthUV - 1));
+        TmpPelWeighted    = g_TrianglePelWeightedChroma[format][SplitDir][ehanced];
+        if( BlendStart < 0 )
+        {
+          TmpPelWeighted += abs(BlendStart) / RatioWH;
+        }
+        for( x = TmpBlendStart; x <= TmpBlendEnd; x += RatioWH )
+        {
+          for( int32_t Cnt = 0; Cnt < RatioWH; Cnt++ )
+          {
+            *DstU++ = ClipPel( rightShift( ((*TmpPelWeighted)*(*Src0U++) + ((8 - (*TmpPelWeighted)) * (*Src1U++)) + offsetU), shiftNumU ), clipRngU );
+            *DstV++ = ClipPel( rightShift( ((*TmpPelWeighted)*(*Src0V++) + ((8 - (*TmpPelWeighted)) * (*Src1V++)) + offsetV), shiftNumV ), clipRngV );
+          }
+          TmpPelWeighted++;
+        }
+
+        for( x = BlendEnd + 1; x < WidthUV; x++ )
+        {
+          *DstU++ = ClipPel( rightShift( *Src0U++ + offsetDefault, shiftDefault ), clipRngU );
+          *DstV++ = ClipPel( rightShift( *Src0V++ + offsetDefault, shiftDefault ), clipRngV );
+          Src1U++;
+          Src1V++;
+        }
+
+        BlendStart += RatioWH;
+        BlendEnd   += RatioWH;
+
+        DstU  += strideU;
+        Src0U += stride0U;
+        Src1U += stride1U;
+        DstV  += strideU;
+        Src0V += stride0U;
+        Src1V += stride1U;
+      }
+    }
+    else if( SplitDir == TRIANGLE_DIR_135 && WidthY < HeightY )
+    {
+      int32_t RatioHW     = HeightY / WidthY;
+      int32_t Cnt        = RatioHW;
+      int32_t BlendStart = 0 - (BlendLengthUV >> 1);
+      int32_t BlendEnd   = BlendStart + BlendLengthUV - 1;
+      for( int32_t y = 0; y < HeightUV; y++ )
+      {
+        int32_t x = 0;
+        for( x = 0; x < BlendStart; x++ )
+        {
+          *DstU++ = ClipPel( rightShift( *Src1U++ + offsetDefault, shiftDefault ), clipRngU );
+          *DstV++ = ClipPel( rightShift( *Src1V++ + offsetDefault, shiftDefault ), clipRngV );
+        }
+        Src0U += x;
+        Src0V += x;
+
+        int32_t TmpBlendStart = std::max((int32_t)0, BlendStart);
+        int32_t TmpBlendEnd   = std::min(BlendEnd, (int32_t)(WidthUV - 1));
+        TmpPelWeighted    = g_TrianglePelWeightedChroma[format][SplitDir][ehanced];
+        if( BlendStart < 0 )
+        {
+          TmpPelWeighted += abs(BlendStart);
+        }
+        for( x = TmpBlendStart; x <= TmpBlendEnd; x++ )
+        {
+          *DstU++ = ClipPel( rightShift( ((*TmpPelWeighted)*(*Src0U++) + ((8 - (*TmpPelWeighted)) * (*Src1U++)) + offsetU), shiftNumU ), clipRngU );
+          *DstV++ = ClipPel( rightShift( ((*TmpPelWeighted)*(*Src0V++) + ((8 - (*TmpPelWeighted)) * (*Src1V++)) + offsetV), shiftNumV ), clipRngV );
+          TmpPelWeighted++;
+        }
+
+        for( x = BlendEnd + 1; x < WidthUV; x++ )
+        {
+          *DstU++ = ClipPel( rightShift( *Src0U++ + offsetDefault, shiftDefault ), clipRngU );
+          *DstV++ = ClipPel( rightShift( *Src0V++ + offsetDefault, shiftDefault ), clipRngV );
+          Src1U++;
+          Src1V++;
+        }
+
+        Cnt--;
+        if( Cnt <= 0 )
+        {
+          BlendStart++;
+          BlendEnd++;
+          Cnt = RatioHW;
+        }
+
+        DstU  += strideU;
+        Src0U += stride0U;
+        Src1U += stride1U;
+        DstV  += strideU;
+        Src0V += stride0U;
+        Src1V += stride1U;
+      }
+    }
+    else if( SplitDir == TRIANGLE_DIR_45 && WidthY == HeightY )
+    {
+      int32_t BlendStart = (WidthUV - 1) - (BlendLengthUV >> 1);
+      int32_t BlendEnd   = (WidthUV - 1) + (BlendLengthUV >> 1);
+      for( int32_t y = 0; y < HeightUV; y++ )
+      {
+        int32_t x = 0;
+        for( x = 0; x < BlendStart; x++ )
+        {
+          *DstU++ = ClipPel( rightShift( *Src0U++ + offsetDefault, shiftDefault ), clipRngU );
+          *DstV++ = ClipPel( rightShift( *Src0V++ + offsetDefault, shiftDefault ), clipRngV );
+        }
+        Src1U += x;
+        Src1V += x;
+
+        int32_t TmpBlendStart = std::max((int32_t)0, BlendStart);
+        int32_t TmpBlendEnd   = std::min(BlendEnd, (int32_t)(WidthUV - 1));
+        TmpPelWeighted    = g_TrianglePelWeightedChroma[format][SplitDir][ehanced];
+        if( BlendStart < 0 )
+        {
+          TmpPelWeighted += abs(BlendStart);
+        }
+        for( x = TmpBlendStart; x <= TmpBlendEnd; x++ )
+        {
+          *DstU++ = ClipPel( rightShift(((*TmpPelWeighted)*(*Src0U++) + ((8 - (*TmpPelWeighted)) * (*Src1U++)) + offsetU), shiftNumU ), clipRngU );
+          *DstV++ = ClipPel( rightShift(((*TmpPelWeighted)*(*Src0V++) + ((8 - (*TmpPelWeighted)) * (*Src1V++)) + offsetV), shiftNumV ), clipRngV );
+          TmpPelWeighted++;
+        }
+
+        for( x = BlendEnd + 1; x < WidthUV; x++ )
+        {
+          *DstU++ = ClipPel( rightShift( *Src1U++ + offsetDefault, shiftDefault ), clipRngU );
+          *DstV++ = ClipPel( rightShift( *Src1V++ + offsetDefault, shiftDefault ), clipRngV );
+          Src0U++;
+          Src0V++;
+        }
+        
+        BlendStart--;
+        BlendEnd--;
+
+        DstU  += strideU;
+        Src0U += stride0U;
+        Src1U += stride1U;
+        DstV  += strideU;
+        Src0V += stride0U;
+        Src1V += stride1U;
+      }
+    }
+    else if( SplitDir == TRIANGLE_DIR_45 && WidthY > HeightY )
+    {
+      int32_t RatioWH     = WidthY / HeightY;
+      int32_t BlendStart = WidthUV - ((BlendLengthUV + 1) >> 1) * RatioWH;
+      int32_t BlendEnd   = BlendStart + BlendLengthUV * RatioWH - 1;
+      for( int32_t y = 0; y < HeightUV; y++ )
+      {
+        int32_t x = 0;
+        for( x = 0; x < BlendStart; x++ )
+        {
+          *DstU++ = ClipPel( rightShift( *Src0U++ + offsetDefault, shiftDefault ), clipRngU );
+          *DstV++ = ClipPel( rightShift( *Src0V++ + offsetDefault, shiftDefault ), clipRngV );
+        }
+        Src1U += x;
+        Src1V += x;
+
+        int32_t TmpBlendStart = std::max((int32_t)0, BlendStart);
+        int32_t TmpBlendEnd   = std::min(BlendEnd, (int32_t)(WidthUV - 1));
+        TmpPelWeighted    = g_TrianglePelWeightedChroma[format][SplitDir][ehanced];
+        if( BlendStart < 0 )
+        {
+          TmpPelWeighted += abs(BlendStart) / RatioWH;
+        }
+        for( x = TmpBlendStart; x <= TmpBlendEnd; x += RatioWH )
+        {
+          for( int32_t Cnt = 0; Cnt < RatioWH; Cnt++ )
+          {
+            *DstU++ = ClipPel( rightShift(((*TmpPelWeighted)*(*Src0U++) + ((8 - (*TmpPelWeighted)) * (*Src1U++)) + offsetU), shiftNumU ), clipRngU );
+            *DstV++ = ClipPel( rightShift(((*TmpPelWeighted)*(*Src0V++) + ((8 - (*TmpPelWeighted)) * (*Src1V++)) + offsetV), shiftNumV ), clipRngV );
+          }
+          TmpPelWeighted++;
+        }
+
+        for( x = BlendEnd + 1; x < WidthUV; x++ )
+        {
+          *DstU++ = ClipPel( rightShift( *Src1U++ + offsetDefault, shiftDefault ), clipRngU );
+          *DstV++ = ClipPel( rightShift( *Src1V++ + offsetDefault, shiftDefault ), clipRngV );
+          Src0U++;
+          Src0V++;
+        }
+
+        BlendStart -= RatioWH;
+        BlendEnd   -= RatioWH;
+
+        DstU  += strideU;
+        Src0U += stride0U;
+        Src1U += stride1U;
+        DstV  += strideU;
+        Src0V += stride0U;
+        Src1V += stride1U;
+      }
+    }
+    else if( SplitDir == TRIANGLE_DIR_45 && WidthY < HeightY )
+    {
+      int32_t RatioHW     = HeightY / WidthY;
+      int32_t BlendStart = WidthUV - ((BlendLengthUV + 1) >> 1);
+      int32_t BlendEnd   = BlendStart + BlendLengthUV - 1;
+      int32_t Cnt        = RatioHW;
+      for( int32_t y = 0; y < HeightUV; y++ )
+      {
+        int32_t x = 0;
+        for( x = 0; x < BlendStart; x++ )
+        {
+          *DstU++ = ClipPel( rightShift( *Src0U++ + offsetDefault, shiftDefault ), clipRngU );
+          *DstV++ = ClipPel( rightShift( *Src0V++ + offsetDefault, shiftDefault ), clipRngV );
+        }
+        Src1U += x;
+        Src1V += x;
+
+        int32_t TmpBlendStart = std::max((int32_t)0, BlendStart);
+        int32_t TmpBlendEnd   = std::min(BlendEnd, (int32_t)(WidthUV - 1));
+        TmpPelWeighted    = g_TrianglePelWeightedChroma[format][SplitDir][ehanced];
+        if( BlendStart < 0 )
+        {
+          TmpPelWeighted += abs(BlendStart);
+        }
+        for( x = TmpBlendStart; x <= TmpBlendEnd; x++ )
+        {
+          *DstU++ = ClipPel( rightShift(((*TmpPelWeighted)*(*Src0U++) + ((8 - (*TmpPelWeighted)) * (*Src1U++)) + offsetU), shiftNumU ), clipRngU );
+          *DstV++ = ClipPel( rightShift(((*TmpPelWeighted)*(*Src0V++) + ((8 - (*TmpPelWeighted)) * (*Src1V++)) + offsetV), shiftNumV ), clipRngV );
+          TmpPelWeighted++;
+        }
+
+        for( x = BlendEnd + 1; x < WidthUV; x++ )
+        {
+          *DstU++ = ClipPel( rightShift( *Src1U++ + offsetDefault, shiftDefault ), clipRngU );
+          *DstV++ = ClipPel( rightShift( *Src1V++ + offsetDefault, shiftDefault ), clipRngV );
+          Src0U++;
+          Src0V++;
+        }
+
+        Cnt--;
+        if( Cnt <= 0 )
+        {
+          BlendStart--;
+          BlendEnd--;
+          Cnt = RatioHW;
+        }
+
+        DstU  += strideU;
+        Src0U += stride0U;
+        Src1U += stride1U;
+        DstV  += strideU;
+        Src0V += stride0U;
+        Src1V += stride1U;
+      }
+    }
+    else
+    {
+      assert(0);
+    }
+  }
+}
+#endif
+
 #if JVET_J0090_MEMORY_BANDWITH_MEASURE
 void InterPrediction::cacheAssign( CacheModel *cache )
 {
diff --git a/source/Lib/CommonLib/InterPrediction.h b/source/Lib/CommonLib/InterPrediction.h
index eb7cf48dd..4f13cb9c8 100644
--- a/source/Lib/CommonLib/InterPrediction.h
+++ b/source/Lib/CommonLib/InterPrediction.h
@@ -91,6 +91,9 @@ protected:
   RdCost*              m_pcRdCost;
 
   int                  m_iRefListIdx;
+#if JVET_L0124_L0208_TRIANGLE
+  PelStorage           m_tmpTriangleBuf;
+#endif
 #if JVET_L0265_AFF_MINIMUM4X4
   Mv*                  m_storedMv;
 #endif
@@ -149,6 +152,11 @@ public:
   void    motionCompensation  (CodingUnit &cu,     const RefPicList &eRefPicList = REF_PIC_LIST_X
   );
 
+#if JVET_L0124_L0208_TRIANGLE
+  void    motionCompensation4Triangle( CodingUnit &cu, MergeCtx &TriangleMrgCtx, const bool SplitDir, const uint8_t CandIdx0, const uint8_t CandIdx1 );
+  void    TriangleWeighting          ( PredictionUnit &pu, bool ehanced, const bool SplitDir, int32_t channel, PelUnitBuf& PredDst, PelUnitBuf& PredSrc0, PelUnitBuf& PredSrc1 );
+#endif
+
 #if JVET_J0090_MEMORY_BANDWITH_MEASURE
   void    cacheAssign( CacheModel *cache );
 #endif
diff --git a/source/Lib/CommonLib/Rom.cpp b/source/Lib/CommonLib/Rom.cpp
index e9972bf93..1ff35211c 100644
--- a/source/Lib/CommonLib/Rom.cpp
+++ b/source/Lib/CommonLib/Rom.cpp
@@ -528,6 +528,30 @@ void initROM()
       //--------------------------------------------------------------------------------------------------
     }
   }
+
+#if JVET_L0124_L0208_TRIANGLE
+  for( int IdxH = MAX_CU_DEPTH - MIN_CU_LOG2; IdxH >= 0; --IdxH )
+  {
+    for( int IdxW = MAX_CU_DEPTH - MIN_CU_LOG2; IdxW >= 0; --IdxW )
+    {
+      int numW   = 1 << IdxW;
+      int numH   = 1 << IdxH;
+      int ratioW = std::max( 0, IdxW - IdxH );
+      int ratioH = std::max( 0, IdxH - IdxW );
+      int sum    = std::max( (numW >> ratioW), (numH >> ratioH) ) - 1;
+      for( int y = 0; y < numH; y++ )
+      {
+        int IdxY = y >> ratioH;
+        for( int x = 0; x < numW; x++ )
+        {
+          int IdxX = x >> ratioW;
+          g_TriangleMvStorage[TRIANGLE_DIR_135][IdxH][IdxW][y][x] = (IdxX == IdxY) ? 2 : (IdxX > IdxY ? 0 : 1);
+          g_TriangleMvStorage[TRIANGLE_DIR_45][IdxH][IdxW][y][x] = (IdxX + IdxY == sum) ? 2 : (IdxX + IdxY > sum ? 1 : 0);
+        }
+      }
+    }
+  }
+#endif
 }
 
 void destroyROM()
@@ -883,5 +907,65 @@ const uint8_t g_NonMPM[257] = { 0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3,
 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8 };
 
+#if JVET_L0124_L0208_TRIANGLE
+const Pel g_TrianglePelWeightedLuma[TRIANGLE_DIR_NUM][2][7] =
+{ 
+  { // TRIANGLE_DIR_135
+    { 1, 2, 4, 6, 7, 0, 0 },
+    { 1, 2, 3, 4, 5, 6, 7 }
+  },
+  { // TRIANGLE_DIR_45
+    { 7, 6, 4, 2, 1, 0, 0 },
+    { 7, 6, 5, 4, 3, 2, 1 }
+  }
+};
+const Pel g_TrianglePelWeightedChroma[2][TRIANGLE_DIR_NUM][2][7] =
+{
+  { // 444 format
+    { // TRIANGLE_DIR_135
+      { 1, 2, 4, 6, 7, 0, 0 },
+      { 1, 2, 3, 4, 5, 6, 7 }
+    },
+    { // TRIANGLE_DIR_45
+      { 7, 6, 4, 2, 1, 0, 0 },
+      { 7, 6, 5, 4, 3, 2, 1 }
+    }
+  },
+  { // 420 format
+    { // TRIANGLE_DIR_135
+      { 1, 4, 7, 0, 0, 0, 0 },
+      { 2, 4, 6, 0, 0, 0, 0 }
+    },
+    { // TRIANGLE_DIR_45
+      { 7, 4, 1, 0, 0, 0, 0 },
+      { 6, 4, 2, 0, 0, 0, 0 }
+    }
+  }
+};
+
+const uint8_t g_TriangleWeightLengthLuma[2] = { 5, 7 };
+const uint8_t g_TriangleWeightLengthChroma[2][2] = { { 5, 7 }, { 3, 3 } };
+
+      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];
 
+const uint8_t g_TriangleCombination[TRIANGLE_MAX_NUM_CANDS][3] =
+{
+  { 0, 1, 0 }, { 1, 0, 1 }, { 1, 0, 2 }, { 0, 0, 1 }, { 0, 2, 0 }, 
+  { 1, 0, 3 }, { 1, 0, 4 }, { 1, 1, 0 }, { 0, 3, 0 }, { 0, 4, 0 }, 
+  { 0, 0, 2 }, { 0, 1, 2 }, { 1, 1, 2 }, { 0, 0, 4 }, { 0, 0, 3 }, 
+  { 0, 1, 3 }, { 0, 1, 4 }, { 1, 1, 4 }, { 1, 1, 3 }, { 1, 2, 1 }, 
+  { 1, 2, 0 }, { 0, 2, 1 }, { 0, 4, 3 }, { 1, 3, 0 }, { 1, 3, 2 }, 
+  { 1, 3, 4 }, { 1, 4, 0 }, { 1, 3, 1 }, { 1, 2, 3 }, { 1, 4, 1 }, 
+  { 0, 4, 1 }, { 0, 2, 3 }, { 1, 4, 2 }, { 0, 3, 2 }, { 1, 4, 3 }, 
+  { 0, 3, 1 }, { 0, 2, 4 }, { 1, 2, 4 }, { 0, 4, 2 }, { 0, 3, 4 }, 
+};
+
+const uint8_t g_TriangleIdxBins[TRIANGLE_MAX_NUM_CANDS] =
+{
+   2,  2,  4,  4,  4,  4,  6,  6,  6,  6,
+   6,  6,  6,  6,  8,  8,  8,  8,  8,  8,
+   8,  8,  8,  8,  8,  8,  8,  8,  8,  8,
+  10, 10, 10, 10, 10, 10, 10, 10, 10, 10
+};
+#endif
 //! \}
diff --git a/source/Lib/CommonLib/Rom.h b/source/Lib/CommonLib/Rom.h
index ba99b1608..56e2f392e 100644
--- a/source/Lib/CommonLib/Rom.h
+++ b/source/Lib/CommonLib/Rom.h
@@ -270,5 +270,15 @@ constexpr uint8_t g_tbMax[257] = { 0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3,
 
 //! \}
 
+#if JVET_L0124_L0208_TRIANGLE
+extern const Pel     g_TrianglePelWeightedLuma[TRIANGLE_DIR_NUM][2][7];
+extern const Pel     g_TrianglePelWeightedChroma[2][TRIANGLE_DIR_NUM][2][7];
+extern const uint8_t g_TriangleWeightLengthLuma[2];
+extern const uint8_t g_TriangleWeightLengthChroma[2][2];
+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];
+extern const uint8_t g_TriangleCombination[TRIANGLE_MAX_NUM_CANDS][3];
+extern const uint8_t g_TriangleIdxBins[TRIANGLE_MAX_NUM_CANDS];
+#endif
+
 #endif  //__TCOMROM__
 
diff --git a/source/Lib/CommonLib/Slice.cpp b/source/Lib/CommonLib/Slice.cpp
index 366b4ae30..af439fd99 100644
--- a/source/Lib/CommonLib/Slice.cpp
+++ b/source/Lib/CommonLib/Slice.cpp
@@ -1651,6 +1651,9 @@ void Slice::updateMotionLUTs(LutMotionCand* lutMC, CodingUnit & cu)
 {
   PredictionUnit *selectedPU = cu.firstPU;
   if (cu.affine) { return; }
+#if JVET_L0124_L0208_TRIANGLE
+  if (cu.triangle) { return; }
+#endif
 
   MotionInfo newMi = selectedPU->getMotionInfo(); 
   addMotionInfoToLUTs(lutMC, newMi);
@@ -1757,6 +1760,9 @@ SPSNext::SPSNext( SPS& sps )
 #if JVET_L0100_MULTI_HYPOTHESIS_INTRA
   , m_MHIntra                   ( false )
 #endif
+#if JVET_L0124_L0208_TRIANGLE
+  , m_Triangle                  ( false )
+#endif
 #if ENABLE_WPP_PARALLELISM
   , m_NextDQP                   ( false )
 #endif
diff --git a/source/Lib/CommonLib/Slice.h b/source/Lib/CommonLib/Slice.h
index 617855753..d7adc7576 100644
--- a/source/Lib/CommonLib/Slice.h
+++ b/source/Lib/CommonLib/Slice.h
@@ -823,6 +823,9 @@ private:
 #if JVET_L0100_MULTI_HYPOTHESIS_INTRA
   bool              m_MHIntra;
 #endif
+#if JVET_L0124_L0208_TRIANGLE
+  bool              m_Triangle;
+#endif
 #if ENABLE_WPP_PARALLELISM
   bool              m_NextDQP;
 #endif
@@ -972,6 +975,10 @@ public:
 #if JVET_L0100_MULTI_HYPOTHESIS_INTRA
   void      setUseMHIntra         ( bool b )                                        { m_MHIntra = b; }
   bool      getUseMHIntra         ()                                      const     { return m_MHIntra; }
+#endif
+#if JVET_L0124_L0208_TRIANGLE
+  void      setUseTriangle        ( bool b )                                        { m_Triangle = b; }
+  bool      getUseTriangle        ()                                      const     { return m_Triangle; }
 #endif
   // ADD_NEW_TOOL : (sps extension) add access functions for tool enabling flags and associated parameters here
 
diff --git a/source/Lib/CommonLib/TypeDef.h b/source/Lib/CommonLib/TypeDef.h
index 97e4a94be..913436f9b 100644
--- a/source/Lib/CommonLib/TypeDef.h
+++ b/source/Lib/CommonLib/TypeDef.h
@@ -50,6 +50,8 @@
 #include <assert.h>
 #include <cassert>
 
+#define JVET_L0124_L0208_TRIANGLE                         1 // triangular shape prediction unit
+
 #define JVET_L0059_MTS_SIMP                               1 // Simpification on MTS signaling
 #define JVET_L0100_MULTI_HYPOTHESIS_INTRA                 1 // Combine intra mode with an extra merge indexed prediction
 
@@ -942,6 +944,14 @@ enum MergeType
   NUM_MRG_TYPE                   // 5
 };
 
+#if JVET_L0124_L0208_TRIANGLE
+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 710868551..098732295 100644
--- a/source/Lib/CommonLib/Unit.cpp
+++ b/source/Lib/CommonLib/Unit.cpp
@@ -258,6 +258,9 @@ CodingUnit& CodingUnit::operator=( const CodingUnit& other )
 #endif
   affine            = other.affine;
   affineType        = other.affineType;
+#if JVET_L0124_L0208_TRIANGLE
+  triangle          = other.triangle;
+#endif
   transQuantBypass  = other.transQuantBypass;
   ipcm              = other.ipcm;
   qp                = other.qp;
@@ -292,6 +295,9 @@ void CodingUnit::initData()
 #endif
   affine            = false;
   affineType        = 0;
+#if JVET_L0124_L0208_TRIANGLE
+  triangle          = false;
+#endif
   transQuantBypass  = false;
   ipcm              = false;
   qp                = 0;
diff --git a/source/Lib/CommonLib/Unit.h b/source/Lib/CommonLib/Unit.h
index 48496a8e6..976dfdf1f 100644
--- a/source/Lib/CommonLib/Unit.h
+++ b/source/Lib/CommonLib/Unit.h
@@ -300,6 +300,9 @@ struct CodingUnit : public UnitArea
 #endif
   bool           affine;
   int            affineType;
+#if JVET_L0124_L0208_TRIANGLE
+  bool           triangle;
+#endif
   bool           transQuantBypass;
   bool           ipcm;
   uint8_t          imv;
diff --git a/source/Lib/CommonLib/UnitTools.cpp b/source/Lib/CommonLib/UnitTools.cpp
index 5d866f823..f7bb9c977 100644
--- a/source/Lib/CommonLib/UnitTools.cpp
+++ b/source/Lib/CommonLib/UnitTools.cpp
@@ -3973,6 +3973,495 @@ void PU::restrictBiPredMergeCands( const PredictionUnit &pu, MergeCtx& mergeCtx
   }
 }
 
+#if JVET_L0124_L0208_TRIANGLE
+void PU::getTriangleMergeCandidates( const PredictionUnit &pu, MergeCtx& TriangleMrgCtx )
+{
+  const CodingStructure &cs  = *pu.cs;
+  const Slice &slice         = *pu.cs->slice;
+  const int32_t maxNumMergeCand = TRIANGLE_MAX_NUM_UNI_CANDS;
+  TriangleMrgCtx.numValidMergeCand = 0;
+
+  for( int32_t i = 0; i < maxNumMergeCand; i++ )
+  {
+    TriangleMrgCtx.interDirNeighbours[i] = 0;
+    TriangleMrgCtx.mrgTypeNeighbours [i] = MRG_TYPE_DEFAULT_N;
+    TriangleMrgCtx.mvFieldNeighbours[(i << 1)    ].refIdx = NOT_VALID;
+    TriangleMrgCtx.mvFieldNeighbours[(i << 1) + 1].refIdx = NOT_VALID;
+    TriangleMrgCtx.mvFieldNeighbours[(i << 1)    ].mv = Mv();
+    TriangleMrgCtx.mvFieldNeighbours[(i << 1) + 1].mv = Mv();
+  }
+
+  MotionInfo Candidate[TRIANGLE_MAX_NUM_CANDS_MEM];
+  int32_t CandCount = 0;
+
+  const Position posLT = pu.Y().topLeft();
+  const Position posRT = pu.Y().topRight();
+  const Position posLB = pu.Y().bottomLeft();
+
+  MotionInfo miAbove, miLeft, miAboveLeft, miAboveRight, miBelowLeft;
+
+  //left
+  const PredictionUnit* puLeft = cs.getPURestricted( posLB.offset( -1, 0 ), pu, pu.chType );
+  const bool isAvailableA1 = puLeft && isDiffMER( pu, *puLeft ) && pu.cu != puLeft->cu && CU::isInter( *puLeft->cu );
+  if( isAvailableA1 )
+  {
+    miLeft = puLeft->getMotionInfo( posLB.offset(-1, 0) );
+    Candidate[CandCount].isInter   = true;
+    Candidate[CandCount].interDir  = miLeft.interDir;
+    Candidate[CandCount].mv[0]     = miLeft.mv[0];
+    Candidate[CandCount].mv[1]     = miLeft.mv[1];
+    Candidate[CandCount].refIdx[0] = miLeft.refIdx[0];
+    Candidate[CandCount].refIdx[1] = miLeft.refIdx[1];
+    CandCount++;
+  }
+
+  // above
+  const PredictionUnit *puAbove = cs.getPURestricted( posRT.offset( 0, -1 ), pu, pu.chType );
+  bool isAvailableB1 = puAbove && isDiffMER( pu, *puAbove ) && pu.cu != puAbove->cu && CU::isInter( *puAbove->cu );
+  if( isAvailableB1 )
+  {
+    miAbove = puAbove->getMotionInfo( posRT.offset( 0, -1 ) );
+    
+    if( !isAvailableA1 || ( miAbove != miLeft ) )
+    {
+      Candidate[CandCount].isInter   = true;
+      Candidate[CandCount].interDir  = miAbove.interDir;
+      Candidate[CandCount].mv[0]     = miAbove.mv[0];
+      Candidate[CandCount].mv[1]     = miAbove.mv[1];
+      Candidate[CandCount].refIdx[0] = miAbove.refIdx[0];
+      Candidate[CandCount].refIdx[1] = miAbove.refIdx[1];
+      CandCount++;
+    }
+  }
+  
+  // above right
+  const PredictionUnit *puAboveRight = cs.getPURestricted( posRT.offset( 1, -1 ), pu, pu.chType );
+  bool isAvailableB0 = puAboveRight && isDiffMER( pu, *puAboveRight ) && CU::isInter( *puAboveRight->cu );
+
+  if( isAvailableB0 )
+  {
+    miAboveRight = puAboveRight->getMotionInfo( posRT.offset( 1, -1 ) );
+
+    if( ( !isAvailableB1 || ( miAbove != miAboveRight ) ) && ( !isAvailableA1 || ( miLeft != miAboveRight ) ) )
+    {
+      Candidate[CandCount].isInter   = true;
+      Candidate[CandCount].interDir  = miAboveRight.interDir;
+      Candidate[CandCount].mv[0]     = miAboveRight.mv[0];
+      Candidate[CandCount].mv[1]     = miAboveRight.mv[1];
+      Candidate[CandCount].refIdx[0] = miAboveRight.refIdx[0];
+      Candidate[CandCount].refIdx[1] = miAboveRight.refIdx[1];
+      CandCount++;
+    }
+  }  
+
+  //left bottom
+  const PredictionUnit *puLeftBottom = cs.getPURestricted( posLB.offset( -1, 1 ), pu, pu.chType );
+  bool isAvailableA0 = puLeftBottom && isDiffMER( pu, *puLeftBottom ) && CU::isInter( *puLeftBottom->cu );
+  if( isAvailableA0 )
+  {
+    miBelowLeft = puLeftBottom->getMotionInfo( posLB.offset( -1, 1 ) );
+    
+    if( ( !isAvailableA1 || ( miBelowLeft != miLeft ) ) && ( !isAvailableB1 || ( miBelowLeft != miAbove ) ) && ( !isAvailableB0 || ( miBelowLeft != miAboveRight ) ) )
+    {
+      Candidate[CandCount].isInter   = true;
+      Candidate[CandCount].interDir  = miBelowLeft.interDir;
+      Candidate[CandCount].mv[0]     = miBelowLeft.mv[0];
+      Candidate[CandCount].mv[1]     = miBelowLeft.mv[1];
+      Candidate[CandCount].refIdx[0] = miBelowLeft.refIdx[0];
+      Candidate[CandCount].refIdx[1] = miBelowLeft.refIdx[1];
+      CandCount++;
+    }
+  }
+
+  // above left
+  const PredictionUnit *puAboveLeft = cs.getPURestricted( posLT.offset( -1, -1 ), pu, pu.chType );
+  bool isAvailableB2 = puAboveLeft && isDiffMER( pu, *puAboveLeft ) && CU::isInter( *puAboveLeft->cu );
+
+  if( isAvailableB2 )
+  {
+    miAboveLeft = puAboveLeft->getMotionInfo( posLT.offset( -1, -1 ) );
+
+    if( ( !isAvailableA1 || ( miLeft != miAboveLeft ) ) && ( !isAvailableB1 || ( miAbove != miAboveLeft ) ) && ( !isAvailableA0 || ( miBelowLeft != miAboveLeft ) ) && ( !isAvailableB0 || ( miAboveRight != miAboveLeft ) ) )
+    {
+      Candidate[CandCount].isInter   = true;
+      Candidate[CandCount].interDir  = miAboveLeft.interDir;
+      Candidate[CandCount].mv[0]     = miAboveLeft.mv[0];
+      Candidate[CandCount].mv[1]     = miAboveLeft.mv[1];
+      Candidate[CandCount].refIdx[0] = miAboveLeft.refIdx[0];
+      Candidate[CandCount].refIdx[1] = miAboveLeft.refIdx[1];
+      CandCount++;
+    }
+  }
+  
+  if( slice.getEnableTMVPFlag() )
+  {
+    Position posRB = pu.Y().bottomRight().offset(-3, -3);
+
+    const PreCalcValues& pcv = *cs.pcv;
+
+    Position posC0;
+    Position posC1 = pu.Y().center();
+    bool C0Avail = false;
+
+    if (((posRB.x + pcv.minCUWidth) < pcv.lumaWidth) && ((posRB.y + pcv.minCUHeight) < pcv.lumaHeight))
+    {
+      Position posInCtu( posRB.x & pcv.maxCUWidthMask, posRB.y & pcv.maxCUHeightMask );
+
+      if( ( posInCtu.x + 4 < pcv.maxCUWidth ) &&           // is not at the last column of CTU
+          ( posInCtu.y + 4 < pcv.maxCUHeight ) )           // is not at the last row    of CTU
+      {
+        posC0 = posRB.offset( 4, 4 );
+        C0Avail = true;
+      }
+      else if( posInCtu.x + 4 < pcv.maxCUWidth )           // is not at the last column of CTU But is last row of CTU
+      {
+        posC0 = posRB.offset( 4, 4 );
+        // in the reference the CTU address is not set - thus probably resulting in no using this C0 possibility
+      }
+      else if( posInCtu.y + 4 < pcv.maxCUHeight )          // is not at the last row of CTU But is last column of CTU
+      {
+        posC0 = posRB.offset( 4, 4 );
+        C0Avail = true;
+      }
+      else //is the right bottom corner of CTU
+      {
+        posC0 = posRB.offset( 4, 4 );
+        // same as for last column but not last row
+      }
+    }
+
+    // C0
+    Mv        cColMv;
+    int32_t   RefIdx     = 0;
+    bool      ExistMV    = ( C0Avail && getColocatedMVP( pu, REF_PIC_LIST_0, posC0, cColMv, RefIdx ) );
+    MotionInfo MvTemporal;
+    MvTemporal.interDir  = 0;
+    if( ExistMV )
+    {
+      MvTemporal.isInter   = true;
+      MvTemporal.interDir |= 1;
+      MvTemporal.mv[0]     = cColMv;
+      MvTemporal.refIdx[0] = RefIdx;
+    }
+    ExistMV = ( C0Avail && getColocatedMVP( pu, REF_PIC_LIST_1, posC0, cColMv, RefIdx ) );
+    if( ExistMV )
+    {
+      MvTemporal.interDir |= 2;
+      MvTemporal.mv[1]     = cColMv;
+      MvTemporal.refIdx[1] = RefIdx;
+    }
+
+    if( MvTemporal.interDir != 0 )
+    {
+      Candidate[CandCount].isInter   = true;
+      Candidate[CandCount].interDir  = MvTemporal.interDir;
+      Candidate[CandCount].mv[0]     = MvTemporal.mv[0];
+      Candidate[CandCount].mv[1]     = MvTemporal.mv[1];
+      Candidate[CandCount].refIdx[0] = MvTemporal.refIdx[0];
+      Candidate[CandCount].refIdx[1] = MvTemporal.refIdx[1];
+      CandCount++;
+    }
+   
+    // C1
+    MvTemporal.interDir = 0;
+    ExistMV    = getColocatedMVP(pu, REF_PIC_LIST_0, posC1, cColMv, RefIdx );
+    if( ExistMV )
+    {
+      MvTemporal.isInter   = true;
+      MvTemporal.interDir |= 1;
+      MvTemporal.mv[0]     = cColMv;
+      MvTemporal.refIdx[0] = RefIdx;
+    }
+    ExistMV    = getColocatedMVP(pu, REF_PIC_LIST_1, posC1, cColMv, RefIdx );
+    if( ExistMV )
+    {
+      MvTemporal.interDir |= 2;
+      MvTemporal.mv[1]     = cColMv;
+      MvTemporal.refIdx[1] = RefIdx;
+    }
+
+    if( MvTemporal.interDir != 0 )
+    {
+      Candidate[CandCount].isInter   = true;
+      Candidate[CandCount].interDir  = MvTemporal.interDir;
+      Candidate[CandCount].mv[0]     = MvTemporal.mv[0];
+      Candidate[CandCount].mv[1]     = MvTemporal.mv[1];
+      Candidate[CandCount].refIdx[0] = MvTemporal.refIdx[0];
+      Candidate[CandCount].refIdx[1] = MvTemporal.refIdx[1];
+      CandCount++;
+    }
+  } // if( slice.getEnableTMVPFlag() )
+  
+  // uni-pred
+  for( int32_t i = 0; i < CandCount; i++ )
+  { 
+    if( Candidate[i].interDir != 3 )
+    {
+      TriangleMrgCtx.interDirNeighbours[TriangleMrgCtx.numValidMergeCand] = Candidate[i].interDir;
+      TriangleMrgCtx.mrgTypeNeighbours [TriangleMrgCtx.numValidMergeCand] = MRG_TYPE_DEFAULT_N;
+      TriangleMrgCtx.mvFieldNeighbours [(TriangleMrgCtx.numValidMergeCand << 1)    ].mv = Candidate[i].mv[0];
+      TriangleMrgCtx.mvFieldNeighbours [(TriangleMrgCtx.numValidMergeCand << 1) + 1].mv = Candidate[i].mv[1];
+      TriangleMrgCtx.mvFieldNeighbours [(TriangleMrgCtx.numValidMergeCand << 1)    ].refIdx = Candidate[i].refIdx[0];
+      TriangleMrgCtx.mvFieldNeighbours [(TriangleMrgCtx.numValidMergeCand << 1) + 1].refIdx = Candidate[i].refIdx[1];
+      TriangleMrgCtx.numValidMergeCand += isUniqueTriangleCandidates(pu, TriangleMrgCtx);
+      if( TriangleMrgCtx.numValidMergeCand == TRIANGLE_MAX_NUM_UNI_CANDS )
+      {
+        return;
+      }
+    }
+  }
+
+  // L0 of bi-pred
+  for( int32_t i = 0; i < CandCount; i++ )
+  {
+    if( Candidate[i].interDir == 3 )
+    {
+      TriangleMrgCtx.interDirNeighbours[TriangleMrgCtx.numValidMergeCand] = 1;
+      TriangleMrgCtx.mrgTypeNeighbours [TriangleMrgCtx.numValidMergeCand] = MRG_TYPE_DEFAULT_N;
+      TriangleMrgCtx.mvFieldNeighbours [(TriangleMrgCtx.numValidMergeCand << 1)    ].mv = Candidate[i].mv[0];
+      TriangleMrgCtx.mvFieldNeighbours [(TriangleMrgCtx.numValidMergeCand << 1) + 1].mv = Mv(0, 0);
+      TriangleMrgCtx.mvFieldNeighbours [(TriangleMrgCtx.numValidMergeCand << 1)    ].refIdx = Candidate[i].refIdx[0];
+      TriangleMrgCtx.mvFieldNeighbours [(TriangleMrgCtx.numValidMergeCand << 1) + 1].refIdx = -1;
+      TriangleMrgCtx.numValidMergeCand += isUniqueTriangleCandidates(pu, TriangleMrgCtx);
+      if( TriangleMrgCtx.numValidMergeCand == TRIANGLE_MAX_NUM_UNI_CANDS )
+      {
+        return;
+      }
+    }
+  }
+
+  // L1 of bi-pred
+  for( int32_t i = 0; i < CandCount; i++ )
+  {
+    if( Candidate[i].interDir == 3 )
+    {
+      TriangleMrgCtx.interDirNeighbours[TriangleMrgCtx.numValidMergeCand] = 2;
+      TriangleMrgCtx.mrgTypeNeighbours [TriangleMrgCtx.numValidMergeCand] = MRG_TYPE_DEFAULT_N;
+      TriangleMrgCtx.mvFieldNeighbours [(TriangleMrgCtx.numValidMergeCand << 1)    ].mv = Mv(0, 0);
+      TriangleMrgCtx.mvFieldNeighbours [(TriangleMrgCtx.numValidMergeCand << 1) + 1].mv = Candidate[i].mv[1];
+      TriangleMrgCtx.mvFieldNeighbours [(TriangleMrgCtx.numValidMergeCand << 1)    ].refIdx = -1;
+      TriangleMrgCtx.mvFieldNeighbours [(TriangleMrgCtx.numValidMergeCand << 1) + 1].refIdx = Candidate[i].refIdx[1];
+      TriangleMrgCtx.numValidMergeCand += isUniqueTriangleCandidates(pu, TriangleMrgCtx);
+      if( TriangleMrgCtx.numValidMergeCand == TRIANGLE_MAX_NUM_UNI_CANDS )
+      {
+        return;
+      }
+    }
+  }
+
+  // averaging two uni-pred of bi-pred
+  for( int32_t i = 0; i < CandCount; i++ )
+  {
+    if( Candidate[i].interDir == 3 )
+    {
+      int32_t currPOC  = slice.getPOC();
+      int32_t L0RefPOC = slice.getRefPOC(REF_PIC_LIST_0, Candidate[i].refIdx[0]);
+      int32_t L1RefPOC = slice.getRefPOC(REF_PIC_LIST_1, Candidate[i].refIdx[1]);
+      Mv aveMv = Candidate[i].mv[1];
+      aveMv = aveMv.scaleMv( xGetDistScaleFactor( currPOC, L0RefPOC, currPOC, L1RefPOC ) ); // scaling to L0
+      aveMv.setHor( ( aveMv.getHor() + Candidate[i].mv[0].getHor() + 1 ) >> 1 );
+      aveMv.setVer( ( aveMv.getVer() + Candidate[i].mv[0].getVer() + 1 ) >> 1 );
+          
+      TriangleMrgCtx.interDirNeighbours[TriangleMrgCtx.numValidMergeCand] = 1;
+      TriangleMrgCtx.mrgTypeNeighbours [TriangleMrgCtx.numValidMergeCand] = MRG_TYPE_DEFAULT_N;
+      TriangleMrgCtx.mvFieldNeighbours [(TriangleMrgCtx.numValidMergeCand << 1)    ].mv = aveMv;
+      TriangleMrgCtx.mvFieldNeighbours [(TriangleMrgCtx.numValidMergeCand << 1) + 1].mv = Mv(0, 0);
+      TriangleMrgCtx.mvFieldNeighbours [(TriangleMrgCtx.numValidMergeCand << 1)    ].refIdx = Candidate[i].refIdx[0];
+      TriangleMrgCtx.mvFieldNeighbours [(TriangleMrgCtx.numValidMergeCand << 1) + 1].refIdx = -1;
+      TriangleMrgCtx.numValidMergeCand += isUniqueTriangleCandidates(pu, TriangleMrgCtx);
+      if( TriangleMrgCtx.numValidMergeCand == TRIANGLE_MAX_NUM_UNI_CANDS )
+      {
+        return;
+      }
+    }
+  } 
+    
+  // fill with Mv(0, 0)
+  int32_t NumRefIdx = std::min( slice.getNumRefIdx(REF_PIC_LIST_0), slice.getNumRefIdx(REF_PIC_LIST_1) );
+  int32_t Cnt = 0;
+  while( TriangleMrgCtx.numValidMergeCand < TRIANGLE_MAX_NUM_UNI_CANDS )
+  {
+    if( Cnt < NumRefIdx )
+    {
+      TriangleMrgCtx.interDirNeighbours[TriangleMrgCtx.numValidMergeCand] = 1;
+      TriangleMrgCtx.mvFieldNeighbours[TriangleMrgCtx.numValidMergeCand << 1].setMvField(Mv(0, 0), Cnt);
+      TriangleMrgCtx.numValidMergeCand++;
+      
+      if( TriangleMrgCtx.numValidMergeCand == TRIANGLE_MAX_NUM_UNI_CANDS )
+      {
+        return;
+      }
+      
+      TriangleMrgCtx.interDirNeighbours[TriangleMrgCtx.numValidMergeCand] = 2;
+      TriangleMrgCtx.mvFieldNeighbours [(TriangleMrgCtx.numValidMergeCand << 1) + 1 ].setMvField(Mv(0, 0), Cnt);
+      TriangleMrgCtx.numValidMergeCand++;
+      
+      Cnt = (Cnt + 1) % NumRefIdx;
+    }
+  }  
+}
+
+bool PU::isUniqueTriangleCandidates( const PredictionUnit &pu, MergeCtx& TriangleMrgCtx )
+{
+  int newCand = TriangleMrgCtx.numValidMergeCand;
+  for( int32_t i = 0; i < newCand; i++ )
+  {
+    int32_t predFlagCur  = TriangleMrgCtx.interDirNeighbours[i] == 1 ? 0 : 1;
+    int32_t predFlagNew  = TriangleMrgCtx.interDirNeighbours[newCand] == 1 ? 0 : 1;
+    int32_t refPicPocCur = pu.cs->slice->getRefPOC( (RefPicList)predFlagCur, TriangleMrgCtx.mvFieldNeighbours[(i << 1) + predFlagCur].refIdx );
+    int32_t refPicPocNew = pu.cs->slice->getRefPOC( (RefPicList)predFlagNew, TriangleMrgCtx.mvFieldNeighbours[(newCand << 1) + predFlagNew].refIdx);
+    if( refPicPocCur == refPicPocNew && TriangleMrgCtx.mvFieldNeighbours[(i << 1) + predFlagCur].mv == TriangleMrgCtx.mvFieldNeighbours[(newCand << 1) + predFlagNew].mv )
+    {
+      return false;
+    }
+  }
+  return true;  
+}
+
+bool PU::isTriangleEhancedWeight( const PredictionUnit& pu, MergeCtx &TriangleMrgCtx, const uint8_t CandIdx0, const uint8_t CandIdx1 )
+{
+  RefPicList RefPicListCand0 = TriangleMrgCtx.interDirNeighbours[CandIdx0] == 1 ? REF_PIC_LIST_0 : REF_PIC_LIST_1;
+  RefPicList RefPicListCand1 = TriangleMrgCtx.interDirNeighbours[CandIdx1] == 1 ? REF_PIC_LIST_0 : REF_PIC_LIST_1;
+  int32_t RefPicPoc0 = pu.cs->slice->getRefPOC( RefPicListCand0, TriangleMrgCtx.mvFieldNeighbours[ (CandIdx0 << 1) + RefPicListCand0 ].refIdx );
+  int32_t RefPicPoc1 = pu.cs->slice->getRefPOC( RefPicListCand1, TriangleMrgCtx.mvFieldNeighbours[ (CandIdx1 << 1) + RefPicListCand1 ].refIdx );
+  
+  if( RefPicPoc0 != RefPicPoc1 )
+  {
+    // different reference picture
+    return true;
+  }
+  
+  // same reference picture, but mv difference is larger than 16 pel
+  int32_t threshold = 16 << 4;
+  Mv DiffMv = TriangleMrgCtx.mvFieldNeighbours[(CandIdx0 << 1) + RefPicListCand0].mv - TriangleMrgCtx.mvFieldNeighbours[(CandIdx1 << 1) + RefPicListCand1].mv;
+  
+  if( DiffMv.getAbsHor() > threshold || DiffMv.getAbsVer() > threshold  )
+  {
+    return true;
+  }
+
+  return false;
+}
+
+void PU::spanTriangleMotionInfo( PredictionUnit &pu, MergeCtx &TriangleMrgCtx, const uint8_t MergeIdx, const bool SplitDir, const uint8_t CandIdx0, const uint8_t CandIdx1 )
+{
+  pu.mergeIdx  = MergeIdx;
+  MotionBuf mb = pu.getMotionBuf();
+
+  MotionInfo BiMv;
+  BiMv.isInter  = true;
+  
+  if( TriangleMrgCtx.interDirNeighbours[CandIdx0] == 1 && TriangleMrgCtx.interDirNeighbours[CandIdx1] == 2 )
+  {
+    BiMv.interDir  = 3;
+    BiMv.mv[0]     = TriangleMrgCtx.mvFieldNeighbours[ CandIdx0 << 1     ].mv;
+    BiMv.mv[1]     = TriangleMrgCtx.mvFieldNeighbours[(CandIdx1 << 1) + 1].mv;
+    BiMv.refIdx[0] = TriangleMrgCtx.mvFieldNeighbours[ CandIdx0 << 1     ].refIdx;
+    BiMv.refIdx[1] = TriangleMrgCtx.mvFieldNeighbours[(CandIdx1 << 1) + 1].refIdx;
+  }
+  else if( TriangleMrgCtx.interDirNeighbours[CandIdx0] == 2 && TriangleMrgCtx.interDirNeighbours[CandIdx1] == 1 )
+  {
+    BiMv.interDir  = 3;
+    BiMv.mv[0]     = TriangleMrgCtx.mvFieldNeighbours[ CandIdx1 << 1     ].mv;
+    BiMv.mv[1]     = TriangleMrgCtx.mvFieldNeighbours[(CandIdx0 << 1) + 1].mv;
+    BiMv.refIdx[0] = TriangleMrgCtx.mvFieldNeighbours[ CandIdx1 << 1     ].refIdx;
+    BiMv.refIdx[1] = TriangleMrgCtx.mvFieldNeighbours[(CandIdx0 << 1) + 1].refIdx;
+  }
+  else if( TriangleMrgCtx.interDirNeighbours[CandIdx0] == 1 && TriangleMrgCtx.interDirNeighbours[CandIdx1] == 1 )
+  {
+    int32_t refIdx = mappingRefPic( pu, pu.cs->slice->getRefPOC( REF_PIC_LIST_0, TriangleMrgCtx.mvFieldNeighbours[CandIdx1 << 1].refIdx ), REF_PIC_LIST_1 );
+    if( refIdx != -1 )
+    {
+      BiMv.interDir  = 3;
+      BiMv.mv[0]     = TriangleMrgCtx.mvFieldNeighbours[CandIdx0 << 1].mv;
+      BiMv.mv[1]     = TriangleMrgCtx.mvFieldNeighbours[CandIdx1 << 1].mv;
+      BiMv.refIdx[0] = TriangleMrgCtx.mvFieldNeighbours[CandIdx0 << 1].refIdx;
+      BiMv.refIdx[1] = refIdx;
+    }
+    else
+    {
+      refIdx = mappingRefPic( pu, pu.cs->slice->getRefPOC( REF_PIC_LIST_0, TriangleMrgCtx.mvFieldNeighbours[CandIdx0 << 1].refIdx), REF_PIC_LIST_1 );
+      BiMv.interDir  = ( refIdx != -1 ) ? 3 : 1;
+      BiMv.mv[0]     = ( refIdx != -1 ) ? TriangleMrgCtx.mvFieldNeighbours[CandIdx1 << 1].mv : TriangleMrgCtx.mvFieldNeighbours[CandIdx0 << 1].mv;
+      BiMv.mv[1]     = ( refIdx != -1 ) ? TriangleMrgCtx.mvFieldNeighbours[CandIdx0 << 1].mv : Mv(0, 0);
+      BiMv.refIdx[0] = ( refIdx != -1 ) ? TriangleMrgCtx.mvFieldNeighbours[CandIdx1 << 1].refIdx : TriangleMrgCtx.mvFieldNeighbours[CandIdx0 << 1].refIdx;
+      BiMv.refIdx[1] = ( refIdx != -1 ) ? refIdx : -1;
+    }
+  }
+  else if( TriangleMrgCtx.interDirNeighbours[CandIdx0] == 2 && TriangleMrgCtx.interDirNeighbours[CandIdx1] == 2 )
+  {
+    int32_t refIdx = mappingRefPic( pu, pu.cs->slice->getRefPOC( REF_PIC_LIST_1, TriangleMrgCtx.mvFieldNeighbours[(CandIdx1 << 1) + 1].refIdx ), REF_PIC_LIST_0 );
+    if( refIdx != -1 )
+    {
+      BiMv.interDir  = 3;
+      BiMv.mv[0]     = TriangleMrgCtx.mvFieldNeighbours[(CandIdx1 << 1) + 1].mv;
+      BiMv.mv[1]     = TriangleMrgCtx.mvFieldNeighbours[(CandIdx0 << 1) + 1].mv;
+      BiMv.refIdx[0] = refIdx;
+      BiMv.refIdx[1] = TriangleMrgCtx.mvFieldNeighbours[(CandIdx0 << 1) + 1].refIdx;
+    }
+    else
+    {
+      refIdx = mappingRefPic( pu, pu.cs->slice->getRefPOC( REF_PIC_LIST_1, TriangleMrgCtx.mvFieldNeighbours[(CandIdx0 << 1) + 1].refIdx ), REF_PIC_LIST_0 );
+      BiMv.interDir  = ( refIdx != -1 ) ? 3 : 2;
+      BiMv.mv[0]     = ( refIdx != -1 ) ? TriangleMrgCtx.mvFieldNeighbours[(CandIdx0 << 1) + 1].mv : Mv(0, 0);
+      BiMv.mv[1]     = ( refIdx != -1 ) ? TriangleMrgCtx.mvFieldNeighbours[(CandIdx1 << 1) + 1].mv : TriangleMrgCtx.mvFieldNeighbours[(CandIdx0 << 1) + 1].mv;
+      BiMv.refIdx[0] = ( refIdx != -1 ) ? refIdx : -1; 
+      BiMv.refIdx[1] = ( refIdx != -1 ) ? TriangleMrgCtx.mvFieldNeighbours[(CandIdx1 << 1) + 1].refIdx : TriangleMrgCtx.mvFieldNeighbours[(CandIdx0 << 1) + 1].refIdx;
+    }
+  }
+
+  int32_t IdxW  = (int32_t)g_aucLog2[pu.lwidth() ] - MIN_CU_LOG2;
+  int32_t IdxH  = (int32_t)g_aucLog2[pu.lheight()] - MIN_CU_LOG2;
+  for( int32_t y = 0; y < mb.height; y++ )
+  {
+    for( int32_t x = 0; x < mb.width; x++ )
+    {
+      if( g_TriangleMvStorage[SplitDir][IdxH][IdxW][y][x] == 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];
+      }
+      else if( g_TriangleMvStorage[SplitDir][IdxH][IdxW][y][x] == 0 )
+      {
+        mb.at( x, y ).isInter   = true;
+        mb.at( x, y ).interDir  = TriangleMrgCtx.interDirNeighbours[CandIdx0];
+        mb.at( x, y ).refIdx[0] = TriangleMrgCtx.mvFieldNeighbours[ CandIdx0 << 1     ].refIdx;
+        mb.at( x, y ).refIdx[1] = TriangleMrgCtx.mvFieldNeighbours[(CandIdx0 << 1) + 1].refIdx;
+        mb.at( x, y ).mv    [0] = TriangleMrgCtx.mvFieldNeighbours[ CandIdx0 << 1     ].mv;
+        mb.at( x, y ).mv    [1] = TriangleMrgCtx.mvFieldNeighbours[(CandIdx0 << 1) + 1].mv;
+      }
+      else
+      {
+        mb.at( x, y ).isInter   = true;
+        mb.at( x, y ).interDir  = TriangleMrgCtx.interDirNeighbours[CandIdx1];
+        mb.at( x, y ).refIdx[0] = TriangleMrgCtx.mvFieldNeighbours[ CandIdx1 << 1     ].refIdx;
+        mb.at( x, y ).refIdx[1] = TriangleMrgCtx.mvFieldNeighbours[(CandIdx1 << 1) + 1].refIdx;
+        mb.at( x, y ).mv    [0] = TriangleMrgCtx.mvFieldNeighbours[ CandIdx1 << 1     ].mv;
+        mb.at( x, y ).mv    [1] = TriangleMrgCtx.mvFieldNeighbours[(CandIdx1 << 1) + 1].mv;
+      }
+    }
+  }
+}
+
+int32_t PU::mappingRefPic( const PredictionUnit &pu, int32_t refPicPoc, bool targetRefPicList )
+{
+  int32_t numRefIdx = pu.cs->slice->getNumRefIdx( (RefPicList)targetRefPicList );
+
+  for( int32_t i = 0; i < numRefIdx; i++ )
+  {
+    if( pu.cs->slice->getRefPOC( (RefPicList)targetRefPicList, i ) == refPicPoc )
+    {
+      return i;
+    }
+  }
+  return -1;
+}
+#endif
+
 void CU::resetMVDandMV2Int( CodingUnit& cu, InterPrediction *interPred )
 {
   for( auto &pu : CU::traversePUs( cu ) )
diff --git a/source/Lib/CommonLib/UnitTools.h b/source/Lib/CommonLib/UnitTools.h
index 9645cd773..445c86de6 100644
--- a/source/Lib/CommonLib/UnitTools.h
+++ b/source/Lib/CommonLib/UnitTools.h
@@ -173,6 +173,13 @@ namespace PU
   int  getMHIntraMPMs                 (const PredictionUnit &pu, unsigned *mpm, const ChannelType &channelType = CHANNEL_TYPE_LUMA, const bool isChromaMDMS = false, const unsigned startIdx = 0);
   int  getNarrowShape                 (const int width, const int height);
 #endif
+#if JVET_L0124_L0208_TRIANGLE
+  void getTriangleMergeCandidates     (const PredictionUnit &pu, MergeCtx& TriangleMrgCtx);
+  bool isUniqueTriangleCandidates     (const PredictionUnit &pu, MergeCtx& TriangleMrgCtx);
+  bool isTriangleEhancedWeight        (const PredictionUnit &pu, MergeCtx &TriangleMrgCtx, const uint8_t CandIdx0, const uint8_t CandIdx1);
+  void spanTriangleMotionInfo         (      PredictionUnit &pu, MergeCtx &TriangleMrgCtx, const uint8_t MergeIdx, const bool SplitDir, const uint8_t CandIdx0, const uint8_t CandIdx1);
+  int32_t mappingRefPic               (const PredictionUnit &pu, int32_t refPicPoc, bool targetRefPicList);
+#endif
 }
 
 // TU tools
diff --git a/source/Lib/DecoderLib/CABACReader.cpp b/source/Lib/DecoderLib/CABACReader.cpp
index 271eb5713..3c709ba3b 100644
--- a/source/Lib/DecoderLib/CABACReader.cpp
+++ b/source/Lib/DecoderLib/CABACReader.cpp
@@ -1172,6 +1172,9 @@ void CABACReader::prediction_unit( PredictionUnit& pu, MergeCtx& mrgCtx )
       pu.intraDir[1] = DM_CHROMA_IDX;
     }
 #endif
+#if JVET_L0124_L0208_TRIANGLE
+    triangle_mode( *pu.cu );
+#endif
 #if JVET_L0054_MMVD
     if (pu.mmvdMergeFlag)
     {
@@ -1413,6 +1416,25 @@ void CABACReader::merge_idx( PredictionUnit& pu )
 #endif
   int numCandminus1 = int( pu.cs->slice->getMaxNumMergeCand() ) - 1;
   pu.mergeIdx       = 0;
+
+#if JVET_L0124_L0208_TRIANGLE
+  if( pu.cu->triangle )
+  {
+    RExt__DECODER_DEBUG_BIT_STATISTICS_CREATE_SET( STATS__CABAC_BITS__TRIANGLE_INDEX );
+    if( m_BinDecoder.decodeBin( Ctx::TriangleIdx() ) == 0 )
+    {
+      pu.mergeIdx += m_BinDecoder.decodeBinEP();
+    }
+    else
+    {
+      pu.mergeIdx += exp_golomb_eqprob( 2 ) + 2;
+    }
+
+    DTRACE( g_trace_ctx, D_SYNTAX, "merge_idx() triangle_idx=%d\n", pu.mergeIdx );
+    return;
+  }
+#endif
+
   if( numCandminus1 > 0 )
   {
     if( m_BinDecoder.decodeBin( Ctx::MergeIdx() ) )
@@ -1728,6 +1750,23 @@ void CABACReader::MHIntra_luma_pred_modes(CodingUnit &cu)
 }
 #endif
 
+#if JVET_L0124_L0208_TRIANGLE
+void CABACReader::triangle_mode( CodingUnit& cu )
+{
+  RExt__DECODER_DEBUG_BIT_STATISTICS_CREATE_SET( STATS__CABAC_BITS__TRIANGLE_FLAG );
+
+  if( !cu.cs->slice->getSPS()->getSpsNext().getUseTriangle() || !cu.cs->slice->isInterB() || cu.lwidth() * cu.lheight() < TRIANGLE_MIN_SIZE || cu.affine )
+  {
+    return;
+  }
+  
+  unsigned flag_idx = DeriveCtx::CtxTriangleFlag( cu );
+  cu.triangle = m_BinDecoder.decodeBin( Ctx::TriangleFlag(flag_idx) );
+
+
+  DTRACE( g_trace_ctx, D_SYNTAX, "triangle_mode() triangle_mode=%d pos=(%d,%d) size: %dx%d\n", cu.triangle, cu.Y().x, cu.Y().y, cu.lumaSize().width, cu.lumaSize().height );
+}
+#endif
 
 //================================================================================
 //  clause 7.3.8.7
diff --git a/source/Lib/DecoderLib/CABACReader.h b/source/Lib/DecoderLib/CABACReader.h
index 046c3b7ae..0ec81a5fa 100644
--- a/source/Lib/DecoderLib/CABACReader.h
+++ b/source/Lib/DecoderLib/CABACReader.h
@@ -111,6 +111,9 @@ public:
   void        MHIntra_flag              ( PredictionUnit&               pu );
   void        MHIntra_luma_pred_modes   ( CodingUnit&                   cu );
 #endif
+#if JVET_L0124_L0208_TRIANGLE
+  void        triangle_mode             ( CodingUnit&                   cu );
+#endif
 
   // pcm samples (clause 7.3.8.7)
   void        pcm_samples               ( TransformUnit&                tu );
diff --git a/source/Lib/DecoderLib/DecCu.cpp b/source/Lib/DecoderLib/DecCu.cpp
index 7cfd907ef..2ea919792 100644
--- a/source/Lib/DecoderLib/DecCu.cpp
+++ b/source/Lib/DecoderLib/DecCu.cpp
@@ -314,12 +314,28 @@ void DecCu::xFillPCMBuffer(CodingUnit &cu)
 
 void DecCu::xReconInter(CodingUnit &cu)
 {
+#if JVET_L0124_L0208_TRIANGLE
+  if( cu.triangle )
+  {
+    const uint8_t mergeIdx = cu.firstPU->mergeIdx;
+    const bool    SplitDir = g_TriangleCombination[mergeIdx][0];
+    const uint8_t CandIdx0 = g_TriangleCombination[mergeIdx][1];
+    const uint8_t CandIdx1 = g_TriangleCombination[mergeIdx][2];
+    m_pcInterPred->motionCompensation4Triangle( cu, m_TriangleMrgCtx, SplitDir, CandIdx0, CandIdx1 );
+    PU::spanTriangleMotionInfo( *cu.firstPU, m_TriangleMrgCtx, mergeIdx, SplitDir, CandIdx0, CandIdx1 );
+  }
+  else
+  {
+#endif
 #if JVET_L0100_MULTI_HYPOTHESIS_INTRA
   m_pcIntraPred->geneIntrainterPred(cu);
 #endif
 
   // inter prediction
   m_pcInterPred->motionCompensation( cu );
+#if JVET_L0124_L0208_TRIANGLE
+  }
+#endif
 #if JVET_L0266_HMVP
   cu.slice->updateMotionLUTs(cu.slice->getMotionLUTs(), cu);
 #endif
@@ -486,6 +502,14 @@ void DecCu::xDeriveCUMV( CodingUnit &cu )
       {
 #endif
       {
+#if JVET_L0124_L0208_TRIANGLE
+        if( pu.cu->triangle )
+        {
+          PU::getTriangleMergeCandidates( pu, m_TriangleMrgCtx );
+        }
+        else
+        {
+#endif
         if( pu.cu->affine )
         {
 #if JVET_L0632_AFFINE_MERGE
@@ -593,6 +617,9 @@ void DecCu::xDeriveCUMV( CodingUnit &cu )
 
           PU::spanMotionInfo( pu, mrgCtx );
         }
+#if JVET_L0124_L0208_TRIANGLE
+        }
+#endif
       }
 #if JVET_L0054_MMVD
       }
diff --git a/source/Lib/DecoderLib/DecCu.h b/source/Lib/DecoderLib/DecCu.h
index 2a61f26cc..4e98da082 100644
--- a/source/Lib/DecoderLib/DecCu.h
+++ b/source/Lib/DecoderLib/DecCu.h
@@ -92,6 +92,10 @@ private:
 
 
   MotionInfo        m_SubPuMiBuf[(MAX_CU_SIZE * MAX_CU_SIZE) >> (MIN_CU_LOG2 << 1)];
+
+#if JVET_L0124_L0208_TRIANGLE
+  MergeCtx          m_TriangleMrgCtx;
+#endif
 };
 
 //! \}
diff --git a/source/Lib/DecoderLib/VLCReader.cpp b/source/Lib/DecoderLib/VLCReader.cpp
index 9113f8ec2..2858f2b6e 100644
--- a/source/Lib/DecoderLib/VLCReader.cpp
+++ b/source/Lib/DecoderLib/VLCReader.cpp
@@ -818,6 +818,9 @@ void HLSyntaxReader::parseSPSNext( SPSNext& spsNext, const bool usePCM )
 #if JVET_L0100_MULTI_HYPOTHESIS_INTRA
   READ_FLAG( symbol,  "mhintra_flag" );                           spsNext.setUseMHIntra             ( symbol != 0 );
 #endif
+#if JVET_L0124_L0208_TRIANGLE
+  READ_FLAG( symbol,    "triangle_flag" );                          spsNext.setUseTriangle            ( symbol != 0 );
+#endif
 #if ENABLE_WPP_PARALLELISM
   READ_FLAG( symbol,  "next_dqp_enabled_flag" );                  spsNext.setUseNextDQP             ( symbol != 0 );
 #else
diff --git a/source/Lib/EncoderLib/CABACWriter.cpp b/source/Lib/EncoderLib/CABACWriter.cpp
index 5e9177706..fbdbf1f53 100644
--- a/source/Lib/EncoderLib/CABACWriter.cpp
+++ b/source/Lib/EncoderLib/CABACWriter.cpp
@@ -1132,6 +1132,9 @@ void CABACWriter::prediction_unit( const PredictionUnit& pu )
       MHIntra_luma_pred_modes( *pu.cu );
     }
 #endif
+#if JVET_L0124_L0208_TRIANGLE
+    triangle_mode( *pu.cu );
+#endif
 #if JVET_L0054_MMVD
     if (pu.mmvdMergeFlag)
     {
@@ -1354,6 +1357,24 @@ void CABACWriter::merge_idx( const PredictionUnit& pu )
   }
   else
   {
+#endif
+#if JVET_L0124_L0208_TRIANGLE
+    if( pu.cu->triangle )
+    {
+      if( pu.mergeIdx < 2 )
+      {
+        m_BinEncoder.encodeBin( 0, Ctx::TriangleIdx() );
+        m_BinEncoder.encodeBinEP( pu.mergeIdx );
+      }
+      else
+      {
+        m_BinEncoder.encodeBin( 1, Ctx::TriangleIdx() );
+        exp_golomb_eqprob( pu.mergeIdx - 2, 2 );
+      }
+
+      DTRACE( g_trace_ctx, D_SYNTAX, "merge_idx() triangle_idx=%d\n", pu.mergeIdx );
+      return;
+    }
 #endif
   int numCandminus1 = int( pu.cs->slice->getMaxNumMergeCand() ) - 1;
   if( numCandminus1 > 0 )
@@ -1618,6 +1639,22 @@ void CABACWriter::MHIntra_luma_pred_modes(const CodingUnit& cu)
 }
 #endif
 
+#if JVET_L0124_L0208_TRIANGLE
+void CABACWriter::triangle_mode( const CodingUnit& cu )
+{
+  if( !cu.cs->slice->getSPS()->getSpsNext().getUseTriangle() || !cu.cs->slice->isInterB() || cu.lwidth() * cu.lheight() < TRIANGLE_MIN_SIZE || cu.affine )
+  {
+    return;
+  }
+
+  unsigned flag_idx     = DeriveCtx::CtxTriangleFlag( cu );
+
+  m_BinEncoder.encodeBin( cu.triangle, Ctx::TriangleFlag(flag_idx) );
+
+  DTRACE( g_trace_ctx, D_SYNTAX, "triangle_mode() triangle_mode=%d pos=(%d,%d) size: %dx%d\n", cu.triangle, cu.Y().x, cu.Y().y, cu.lumaSize().width, cu.lumaSize().height );
+}
+#endif
+
 //================================================================================
 //  clause 7.3.8.7
 //--------------------------------------------------------------------------------
diff --git a/source/Lib/EncoderLib/CABACWriter.h b/source/Lib/EncoderLib/CABACWriter.h
index 646550b8d..faacf9b6f 100644
--- a/source/Lib/EncoderLib/CABACWriter.h
+++ b/source/Lib/EncoderLib/CABACWriter.h
@@ -125,6 +125,9 @@ public:
   void        MHIntra_flag              ( const PredictionUnit&         pu );
   void        MHIntra_luma_pred_modes   ( const CodingUnit&             cu );
 #endif
+#if JVET_L0124_L0208_TRIANGLE
+  void        triangle_mode             ( const CodingUnit&             cu );
+#endif
 
   // pcm samples (clause 7.3.8.7)
   void        pcm_samples               ( const TransformUnit&          tu );
diff --git a/source/Lib/EncoderLib/EncCfg.h b/source/Lib/EncoderLib/EncCfg.h
index bd8301680..1c9744660 100644
--- a/source/Lib/EncoderLib/EncCfg.h
+++ b/source/Lib/EncoderLib/EncCfg.h
@@ -223,6 +223,9 @@ protected:
 
 #if JVET_L0100_MULTI_HYPOTHESIS_INTRA
   bool      m_MHIntra;
+#endif
+#if JVET_L0124_L0208_TRIANGLE
+  bool      m_Triangle;
 #endif
   // ADD_NEW_TOOL : (encoder lib) add tool enabling flags and associated parameters here
 
@@ -695,6 +698,10 @@ public:
   void      setUseMHIntra                   ( bool b )       { m_MHIntra = b; }
   bool      getUseMHIntra                   ()         const { return m_MHIntra; }
 #endif
+#if JVET_L0124_L0208_TRIANGLE
+  void      setUseTriangle                  ( bool b )       { m_Triangle = b; }
+  bool      getUseTriangle                  ()         const { return m_Triangle; }
+#endif
 
   // ADD_NEW_TOOL : (encoder lib) add access functions here
 
diff --git a/source/Lib/EncoderLib/EncCu.cpp b/source/Lib/EncoderLib/EncCu.cpp
index 1ae622840..2572d4cc6 100644
--- a/source/Lib/EncoderLib/EncCu.cpp
+++ b/source/Lib/EncoderLib/EncCu.cpp
@@ -197,6 +197,12 @@ void EncCu::create( EncCfg* encCfg )
     m_acRealMergeBuffer[ui].create(chromaFormat, Area(0, 0, uiMaxWidth, uiMaxHeight));
   }
 #endif
+#if JVET_L0124_L0208_TRIANGLE
+  for( unsigned ui = 0; ui < TRIANGLE_MAX_NUM_CANDS; ui++ )
+  {
+    m_acTriangleWeightBuffer[ui].create( chromaFormat, Area( 0, 0, uiMaxWidth, uiMaxHeight ) );
+  }
+#endif
 
   m_CtxBuffer.resize( maxDepth );
   m_CurrCtx = 0;
@@ -301,6 +307,12 @@ void EncCu::destroy()
     m_acRealMergeBuffer[ui].destroy();
   }
 #endif
+#if JVET_L0124_L0208_TRIANGLE
+  for( unsigned ui = 0; ui < TRIANGLE_MAX_NUM_CANDS; ui++ )
+  {
+    m_acTriangleWeightBuffer[ui].destroy();
+  }
+#endif
 }
 
 
@@ -739,6 +751,12 @@ void EncCu::xCompressCU( CodingStructure *&tempCS, CodingStructure *&bestCS, Par
       cu->mmvdSkip = cu->skip == false ? false : cu->mmvdSkip;
 #endif
     }
+#if JVET_L0124_L0208_TRIANGLE
+    else if( currTestMode.type == ETM_MERGE_TRIANGLE )
+    {
+      xCheckRDCostMergeTriangle2Nx2N( tempCS, bestCS, partitioner, currTestMode );
+    }
+#endif
     else if( currTestMode.type == ETM_INTRA )
     {
       xCheckRDCostIntra( tempCS, bestCS, partitioner, currTestMode );
@@ -1609,6 +1627,10 @@ void EncCu::xCheckRDCostMerge2Nx2N( CodingStructure *&tempCS, CodingStructure *&
     mergeCtx.subPuMvpMiBuf    = MotionBuf( m_SubPuMiBuf,    bufSize );
   }
 
+#if JVET_L0124_L0208_TRIANGLE
+  setMergeBestSATDCost( MAX_DOUBLE );
+#endif
+
   {
     // first get merge candidates
     CodingUnit cu( tempCS->area );
@@ -1779,6 +1801,9 @@ void EncCu::xCheckRDCostMerge2Nx2N( CodingStructure *&tempCS, CodingStructure *&
       cu.skip             = false;
 #if JVET_L0054_MMVD
       cu.mmvdSkip = false;
+#endif
+#if JVET_L0124_L0208_TRIANGLE
+      cu.triangle         = false;
 #endif
       cu.partSize         = SIZE_2Nx2N;
     //cu.affine
@@ -2073,6 +2098,10 @@ void EncCu::xCheckRDCostMerge2Nx2N( CodingStructure *&tempCS, CodingStructure *&
         }
       }
 
+#if JVET_L0124_L0208_TRIANGLE
+      setMergeBestSATDCost( candCostList[0] );
+#endif
+
 #if JVET_L0100_MULTI_HYPOTHESIS_INTRA
       if (isIntrainterEnabled)
       {
@@ -2170,6 +2199,9 @@ void EncCu::xCheckRDCostMerge2Nx2N( CodingStructure *&tempCS, CodingStructure *&
       cu.skip             = false;
 #if JVET_L0054_MMVD
       cu.mmvdSkip = false;
+#endif
+#if JVET_L0124_L0208_TRIANGLE
+      cu.triangle         = false;
 #endif
       cu.partSize         = SIZE_2Nx2N;
     //cu.affine
@@ -2336,6 +2368,234 @@ void EncCu::xCheckRDCostMerge2Nx2N( CodingStructure *&tempCS, CodingStructure *&
   }
 }
 
+#if JVET_L0124_L0208_TRIANGLE
+void EncCu::xCheckRDCostMergeTriangle2Nx2N( CodingStructure *&tempCS, CodingStructure *&bestCS, Partitioner &partitioner, const EncTestMode& encTestMode )
+{
+  const Slice &slice = *tempCS->slice;
+  const SPS &sps = *tempCS->sps;
+
+  CHECK( slice.getSliceType() != B_SLICE, "Triangle mode is only applied to B-slices" );
+  
+  tempCS->initStructData( encTestMode.qp, encTestMode.lossless );
+  
+  bool TrianglecandHasNoResidual[TRIANGLE_MAX_NUM_CANDS];
+  for( int MergeCand = 0; MergeCand < TRIANGLE_MAX_NUM_CANDS; MergeCand++ )
+  {
+    TrianglecandHasNoResidual[MergeCand] = false;
+  }
+
+  bool                                            bestIsSkip             = m_pcEncCfg->getUseFastDecisionForMerge() ? bestCS->getCU( partitioner.chType )->rootCbf == 0 : false;
+  uint8_t                                         NumTriangleCandidate   = TRIANGLE_MAX_NUM_CANDS;
+  uint8_t                                         TriangleNumMrgSATDCand = TRIANGLE_MAX_NUM_SATD_CANDS;
+  PelUnitBuf                                      acTriangleBuffer[TRIANGLE_MAX_NUM_UNI_CANDS];
+  PelUnitBuf                                      acTriangleWeightBuffer[TRIANGLE_MAX_NUM_CANDS];
+  static_vector<uint8_t, TRIANGLE_MAX_NUM_CANDS> TriangleRdModeList;
+  static_vector<double,  TRIANGLE_MAX_NUM_CANDS> TrianglecandCostList;
+
+  if( auto blkCache = dynamic_cast< CacheBlkInfoCtrl* >( m_modeCtrl ) )
+  {
+    bestIsSkip |= blkCache->isSkip( tempCS->area );
+  }
+
+  DistParam distParam;
+  const bool UseHadamard = !encTestMode.lossless;
+  m_pcRdCost->setDistParam( distParam, tempCS->getOrgBuf().Y(), m_acMergeBuffer[0].Y(), sps.getBitDepth( CHANNEL_TYPE_LUMA ), COMPONENT_Y, UseHadamard );
+
+  const UnitArea localUnitArea( tempCS->area.chromaFormat, Area( 0, 0, tempCS->area.Y().width, tempCS->area.Y().height) );
+
+  const double sqrtLambdaForFirstPass = m_pcRdCost->getMotionLambda(encTestMode.lossless);
+
+  MergeCtx TriangleMrgCtx;
+  {
+    CodingUnit cu( tempCS->area );
+    cu.cs       = tempCS;
+    cu.partSize = SIZE_2Nx2N;
+    cu.predMode = MODE_INTER;
+    cu.slice    = tempCS->slice;
+    cu.triangle = true;
+#if JVET_L0054_MMVD
+    cu.mmvdSkip = false;
+#endif    
+#if JVET_L0646_GBI
+    cu.GBiIdx   = GBI_DEFAULT;
+#endif
+
+    PredictionUnit pu( tempCS->area );
+    pu.cu = &cu;
+    pu.cs = tempCS;
+
+
+    PU::getTriangleMergeCandidates( pu, TriangleMrgCtx );
+    for( uint8_t MergeCand = 0; MergeCand < TRIANGLE_MAX_NUM_UNI_CANDS; MergeCand++ )
+    {
+      acTriangleBuffer[MergeCand] = m_acMergeBuffer[MergeCand].getBuf(localUnitArea);
+      TriangleMrgCtx.setMergeInfo( pu, MergeCand );
+      PU::spanMotionInfo( pu, TriangleMrgCtx );
+      
+      m_pcInterSearch->motionCompensation( pu, acTriangleBuffer[MergeCand] );
+    }
+  }
+
+  bool TempBufSet = bestIsSkip ? false : true;
+  TriangleNumMrgSATDCand = bestIsSkip ? TRIANGLE_MAX_NUM_CANDS : TRIANGLE_MAX_NUM_SATD_CANDS;
+  if( bestIsSkip )
+  {
+    for( uint8_t i = 0; i < TRIANGLE_MAX_NUM_CANDS; i++ )
+    {
+      TriangleRdModeList.push_back(i);
+    }
+  }
+  else
+  {
+    CodingUnit &cu      = tempCS->addCU( tempCS->area, partitioner.chType );
+      
+    partitioner.setCUData( cu );
+    cu.slice            = tempCS->slice;
+    cu.skip             = false;
+    cu.partSize         = SIZE_2Nx2N;
+    cu.predMode         = MODE_INTER;
+    cu.transQuantBypass = encTestMode.lossless;
+    cu.chromaQpAdj      = cu.transQuantBypass ? 0 : m_cuChromaQpOffsetIdxPlus1;
+    cu.qp               = encTestMode.qp;
+    cu.triangle         = true;
+#if JVET_L0054_MMVD
+    cu.mmvdSkip         = false;
+#endif
+#if JVET_L0646_GBI
+    cu.GBiIdx           = GBI_DEFAULT;
+#endif
+
+    PredictionUnit &pu  = tempCS->addPU( cu, partitioner.chType );
+      
+    int32_t Ratio = abs(g_aucLog2[cu.lwidth()] - g_aucLog2[cu.lheight()]);
+    if( Ratio >= 2 )
+    {
+      NumTriangleCandidate = 30;
+    }
+    else
+    {
+      NumTriangleCandidate = TRIANGLE_MAX_NUM_CANDS;
+    }
+
+    for( uint8_t MergeCand = 0; MergeCand < NumTriangleCandidate; MergeCand++ )
+    {
+      bool    SplitDir = g_TriangleCombination[MergeCand][0];
+      uint8_t CandIdx0 = g_TriangleCombination[MergeCand][1];
+      uint8_t CandIdx1 = g_TriangleCombination[MergeCand][2];
+
+      pu.mergeIdx  = MergeCand;
+      pu.mergeFlag = true;
+      acTriangleWeightBuffer[MergeCand] = m_acTriangleWeightBuffer[MergeCand].getBuf( localUnitArea );
+      acTriangleBuffer[CandIdx0] = m_acMergeBuffer[CandIdx0].getBuf( localUnitArea );
+      acTriangleBuffer[CandIdx1] = m_acMergeBuffer[CandIdx1].getBuf( localUnitArea );
+
+      m_pcInterSearch->TriangleWeighting( pu, PU::isTriangleEhancedWeight(pu, TriangleMrgCtx, CandIdx0, CandIdx1), SplitDir, CHANNEL_TYPE_LUMA, acTriangleWeightBuffer[MergeCand], acTriangleBuffer[CandIdx0], acTriangleBuffer[CandIdx1] );
+      
+      distParam.cur = acTriangleWeightBuffer[MergeCand].Y();
+
+      Distortion uiSad = distParam.distFunc( distParam );
+
+      uint32_t uiBitsCand = g_TriangleIdxBins[MergeCand];
+
+      double cost = (double)uiSad + (double)uiBitsCand * sqrtLambdaForFirstPass;
+
+      updateCandList( MergeCand, cost, TriangleRdModeList, TrianglecandCostList, TriangleNumMrgSATDCand );
+    }
+        
+    // limit number of candidates using SATD-costs
+    for( uint8_t i = 0; i < TriangleNumMrgSATDCand; i++ )
+    {
+      if( TrianglecandCostList[i] > MRG_FAST_RATIO * TrianglecandCostList[0] || TrianglecandCostList[i] > getMergeBestSATDCost() )
+      {
+        TriangleNumMrgSATDCand = i;
+        break;
+      }
+    }
+
+    // perform chroma weighting process
+    for( uint8_t i = 0; i < TriangleNumMrgSATDCand; i++ )
+    {
+      uint8_t  MergeCand = TriangleRdModeList[i];
+      bool     SplitDir  = g_TriangleCombination[MergeCand][0];
+      uint8_t  CandIdx0  = g_TriangleCombination[MergeCand][1];
+      uint8_t  CandIdx1  = g_TriangleCombination[MergeCand][2];
+        
+      pu.mergeIdx  = MergeCand;
+      pu.mergeFlag = true;
+                
+      m_pcInterSearch->TriangleWeighting( pu, PU::isTriangleEhancedWeight(pu, TriangleMrgCtx, CandIdx0, CandIdx1), SplitDir, CHANNEL_TYPE_CHROMA, acTriangleWeightBuffer[MergeCand], acTriangleBuffer[CandIdx0], acTriangleBuffer[CandIdx1] );
+    }
+
+    tempCS->initStructData( encTestMode.qp, encTestMode.lossless );
+  }
+
+  {
+    const uint8_t iteration = encTestMode.lossless ? 1 : 2;
+    for( uint8_t NoResidualPass = 0; NoResidualPass < iteration; NoResidualPass++ )
+    {
+      for( uint8_t MrgHADIdx = 0; MrgHADIdx < TriangleNumMrgSATDCand; MrgHADIdx++ )
+      {
+        uint8_t MergeCand = TriangleRdModeList[MrgHADIdx];
+
+        if ( ( (NoResidualPass != 0) && TrianglecandHasNoResidual[MergeCand] )
+          || ( (NoResidualPass == 0) && bestIsSkip ) )
+        {
+          continue;
+        }
+
+        bool    SplitDir = g_TriangleCombination[MergeCand][0];
+        uint8_t CandIdx0 = g_TriangleCombination[MergeCand][1];
+        uint8_t CandIdx1 = g_TriangleCombination[MergeCand][2];
+
+        CodingUnit &cu = tempCS->addCU(tempCS->area, partitioner.chType);
+
+        partitioner.setCUData(cu);
+        cu.slice = tempCS->slice;
+        cu.skip = false;
+        cu.partSize = SIZE_2Nx2N;
+        cu.predMode = MODE_INTER;
+        cu.transQuantBypass = encTestMode.lossless;
+        cu.chromaQpAdj = cu.transQuantBypass ? 0 : m_cuChromaQpOffsetIdxPlus1;
+        cu.qp = encTestMode.qp;
+        cu.triangle = true;
+#if JVET_L0054_MMVD
+        cu.mmvdSkip = false;
+#endif
+#if JVET_L0646_GBI
+        cu.GBiIdx   = GBI_DEFAULT;
+#endif
+        PredictionUnit &pu = tempCS->addPU(cu, partitioner.chType);
+
+        pu.mergeIdx = MergeCand;
+        pu.mergeFlag = true;
+
+        PU::spanTriangleMotionInfo(pu, TriangleMrgCtx, MergeCand, SplitDir, CandIdx0, CandIdx1 );
+
+        if( TempBufSet )
+        {
+          tempCS->getPredBuf().copyFrom( acTriangleWeightBuffer[MergeCand] );
+        }
+        else
+        {
+          acTriangleBuffer[CandIdx0] = m_acMergeBuffer[CandIdx0].getBuf( localUnitArea );
+          acTriangleBuffer[CandIdx1] = m_acMergeBuffer[CandIdx1].getBuf( localUnitArea );
+          PelUnitBuf predBuf         = tempCS->getPredBuf();
+          m_pcInterSearch->TriangleWeighting( pu, PU::isTriangleEhancedWeight(pu, TriangleMrgCtx, CandIdx0, CandIdx1), SplitDir, MAX_NUM_CHANNEL_TYPE, predBuf, acTriangleBuffer[CandIdx0], acTriangleBuffer[CandIdx1] );
+        }
+        
+        xEncodeInterResidual( tempCS, bestCS, partitioner, encTestMode, NoResidualPass, NULL, true, ( (NoResidualPass == 0 ) ? &TrianglecandHasNoResidual[MergeCand] : NULL ) );
+
+        if (m_pcEncCfg->getUseFastDecisionForMerge() && !bestIsSkip)
+        {
+          bestIsSkip = bestCS->getCU(partitioner.chType)->rootCbf == 0;
+        }
+        tempCS->initStructData(encTestMode.qp, encTestMode.lossless);
+      }// end loop MrgHADIdx
+    }   
+  }
+}
+#endif
+
 void EncCu::xCheckRDCostAffineMerge2Nx2N( CodingStructure *&tempCS, CodingStructure *&bestCS, Partitioner &partitioner, const EncTestMode& encTestMode )
 {
   if( m_modeCtrl->getFastDeltaQp() )
diff --git a/source/Lib/EncoderLib/EncCu.h b/source/Lib/EncoderLib/EncCu.h
index 3607a2f23..60e254f22 100644
--- a/source/Lib/EncoderLib/EncCu.h
+++ b/source/Lib/EncoderLib/EncCu.h
@@ -119,6 +119,10 @@ private:
 #endif
 #else
   PelStorage            m_acMergeBuffer[MRG_MAX_NUM_CANDS];
+#endif
+#if JVET_L0124_L0208_TRIANGLE
+  PelStorage            m_acTriangleWeightBuffer[TRIANGLE_MAX_NUM_CANDS]; // to store weighted prediction pixles
+  double                m_mergeBestSATDCost;
 #endif
   MotionInfo            m_SubPuMiBuf      [( MAX_CU_SIZE * MAX_CU_SIZE ) >> ( MIN_CU_LOG2 << 1 )];
   unsigned int          m_subMergeBlkSize[10];
@@ -173,6 +177,11 @@ public:
   void setClearSubMergeStatic(bool b) { m_clearSubMergeStatic = b; }
   bool getClearSubMergeStatic() { return m_clearSubMergeStatic; }
 
+#if JVET_L0124_L0208_TRIANGLE
+  void   setMergeBestSATDCost(double cost) { m_mergeBestSATDCost = cost; }
+  double getMergeBestSATDCost()            { return m_mergeBestSATDCost; }
+#endif
+
   ~EncCu();
 
 protected:
@@ -217,6 +226,10 @@ protected:
 
   void xCheckRDCostMerge2Nx2N ( CodingStructure *&tempCS, CodingStructure *&bestCS, Partitioner &pm, const EncTestMode& encTestMode );
 
+#if JVET_L0124_L0208_TRIANGLE
+  void xCheckRDCostMergeTriangle2Nx2N( CodingStructure *&tempCS, CodingStructure *&bestCS, Partitioner &pm, const EncTestMode& encTestMode );
+#endif
+
   void xEncodeInterResidual   ( CodingStructure *&tempCS, CodingStructure *&bestCS, Partitioner &partitioner, const EncTestMode& encTestMode, int residualPass = 0
     , CodingStructure* imvCS = NULL
     , int emtMode = 1
diff --git a/source/Lib/EncoderLib/EncLib.cpp b/source/Lib/EncoderLib/EncLib.cpp
index c3c395ad5..55ad6f0e4 100644
--- a/source/Lib/EncoderLib/EncLib.cpp
+++ b/source/Lib/EncoderLib/EncLib.cpp
@@ -878,6 +878,9 @@ void EncLib::xInitSPS(SPS &sps)
 #if JVET_L0100_MULTI_HYPOTHESIS_INTRA
   sps.getSpsNext().setUseMHIntra            ( m_MHIntra );
 #endif
+#if JVET_L0124_L0208_TRIANGLE
+  sps.getSpsNext().setUseTriangle           ( m_Triangle );
+#endif
 
   // ADD_NEW_TOOL : (encoder lib) set tool enabling flags and associated parameters here
 
diff --git a/source/Lib/EncoderLib/EncModeCtrl.cpp b/source/Lib/EncoderLib/EncModeCtrl.cpp
index 0aba5bc5b..c4e07119a 100644
--- a/source/Lib/EncoderLib/EncModeCtrl.cpp
+++ b/source/Lib/EncoderLib/EncModeCtrl.cpp
@@ -1077,6 +1077,12 @@ void EncModeCtrlMTnoRQT::initCULevel( Partitioner &partitioner, const CodingStru
       // add inter modes
       if( m_pcEncCfg->getUseEarlySkipDetection() )
       {
+#if JVET_L0124_L0208_TRIANGLE
+        if( cs.sps->getSpsNext().getUseTriangle() && cs.slice->isInterB() )
+        {
+          m_ComprCUCtxList.back().testModes.push_back( { ETM_MERGE_TRIANGLE,  SIZE_2Nx2N, ETO_STANDARD, qp, lossless } );  
+        }
+#endif
         m_ComprCUCtxList.back().testModes.push_back( { ETM_MERGE_SKIP,  SIZE_2Nx2N, ETO_STANDARD, qp, lossless } );
 #if JVET_L0369_SUBBLOCK_MERGE
         if ( cs.sps->getSpsNext().getUseAffine() || cs.sps->getSpsNext().getUseSubPuMvp() )
@@ -1091,7 +1097,12 @@ void EncModeCtrlMTnoRQT::initCULevel( Partitioner &partitioner, const CodingStru
       else
       {
         m_ComprCUCtxList.back().testModes.push_back( { ETM_INTER_ME,    SIZE_2Nx2N, ETO_STANDARD, qp, lossless } );
-
+#if JVET_L0124_L0208_TRIANGLE
+        if( cs.sps->getSpsNext().getUseTriangle() && cs.slice->isInterB() )
+        {
+          m_ComprCUCtxList.back().testModes.push_back( { ETM_MERGE_TRIANGLE,  SIZE_2Nx2N, ETO_STANDARD, qp, lossless } );  
+        }
+#endif
         m_ComprCUCtxList.back().testModes.push_back( { ETM_MERGE_SKIP,  SIZE_2Nx2N, ETO_STANDARD, qp, lossless } );
 #if JVET_L0369_SUBBLOCK_MERGE
         if ( cs.sps->getSpsNext().getUseAffine() || cs.sps->getSpsNext().getUseSubPuMvp() )
@@ -1313,6 +1324,12 @@ bool EncModeCtrlMTnoRQT::tryMode( const EncTestMode& encTestmode, const CodingSt
     {
       return false;
     }
+#if JVET_L0124_L0208_TRIANGLE
+    if( encTestmode.type == ETM_MERGE_TRIANGLE && ( partitioner.currArea().lumaSize().area() < TRIANGLE_MIN_SIZE || relatedCU.isIntra ) )
+    { 
+      return false;
+    }
+#endif
     return true;
   }
   else if( isModeSplit( encTestmode ) )
diff --git a/source/Lib/EncoderLib/EncModeCtrl.h b/source/Lib/EncoderLib/EncModeCtrl.h
index f56fceb06..988ba51c4 100644
--- a/source/Lib/EncoderLib/EncModeCtrl.h
+++ b/source/Lib/EncoderLib/EncModeCtrl.h
@@ -57,6 +57,9 @@ enum EncTestModeType
   ETM_MERGE_SKIP,
   ETM_INTER_ME,
   ETM_AFFINE,
+#if JVET_L0124_L0208_TRIANGLE
+  ETM_MERGE_TRIANGLE,
+#endif
   ETM_INTRA,
   ETM_IPCM,
   ETM_SPLIT_QT,
@@ -134,6 +137,9 @@ 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_L0124_L0208_TRIANGLE
+          || encTestmode.type == ETM_MERGE_TRIANGLE
+#endif
          );
 }
 
diff --git a/source/Lib/EncoderLib/InterSearch.cpp b/source/Lib/EncoderLib/InterSearch.cpp
index 8d6f8cb24..5258a13df 100644
--- a/source/Lib/EncoderLib/InterSearch.cpp
+++ b/source/Lib/EncoderLib/InterSearch.cpp
@@ -4860,6 +4860,9 @@ void InterSearch::encodeResAndCalcRdInterCU(CodingStructure &cs, Partitioner &pa
 #else
     m_CABACEstimator->affine_flag( cu );
 #endif
+#if JVET_L0124_L0208_TRIANGLE
+    m_CABACEstimator->triangle_mode ( cu );
+#endif
 #if JVET_L0054_MMVD
     if (cu.mmvdSkip)
     {
@@ -4995,6 +4998,9 @@ uint64_t InterSearch::xGetSymbolFracBitsInter(CodingStructure &cs, Partitioner &
 #else
     m_CABACEstimator->affine_flag   ( cu );
 #endif
+#if JVET_L0124_L0208_TRIANGLE
+    m_CABACEstimator->triangle_mode ( cu );
+#endif
 #if JVET_L0054_MMVD
     if (cu.mmvdSkip)
     {
diff --git a/source/Lib/EncoderLib/VLCWriter.cpp b/source/Lib/EncoderLib/VLCWriter.cpp
index c8a270f6a..34d447271 100644
--- a/source/Lib/EncoderLib/VLCWriter.cpp
+++ b/source/Lib/EncoderLib/VLCWriter.cpp
@@ -559,6 +559,9 @@ void HLSWriter::codeSPSNext( const SPSNext& spsNext, const bool usePCM )
 #if JVET_L0100_MULTI_HYPOTHESIS_INTRA
   WRITE_FLAG( spsNext.getUseMHIntra() ? 1 : 0,                                                  "mhintra_flag" );
 #endif
+#if JVET_L0124_L0208_TRIANGLE
+  WRITE_FLAG( spsNext.getUseTriangle() ? 1: 0,                                                  "triangle_flag" );
+#endif
 #if ENABLE_WPP_PARALLELISM
   WRITE_FLAG( spsNext.getUseNextDQP(),                                                          "next_dqp_enabled_flag" );
 #else
-- 
GitLab