From facb343be28bdf776365d39f8a024cd6abedf59a Mon Sep 17 00:00:00 2001
From: Adam Wieckowski <adam.wieckowski@hhi.fraunhofer.de>
Date: Tue, 22 Jan 2019 16:10:16 +0100
Subject: [PATCH] JVET-M0421

---
 source/Lib/CommonLib/ContextModelling.cpp | 113 ++++++++++++++++++++++
 source/Lib/CommonLib/ContextModelling.h   |   4 +
 source/Lib/CommonLib/Contexts.cpp         |  42 ++++++++
 source/Lib/CommonLib/Contexts.h           |   6 ++
 source/Lib/CommonLib/TypeDef.h            |   2 +
 source/Lib/CommonLib/UnitPartitioner.cpp  | 107 +++++++++++++++++++-
 source/Lib/CommonLib/UnitPartitioner.h    |   8 +-
 source/Lib/DecoderLib/CABACReader.cpp     |  89 ++++++++++++++++-
 source/Lib/DecoderLib/CABACReader.h       |   4 +
 source/Lib/EncoderLib/CABACWriter.cpp     |  83 +++++++++++++++-
 source/Lib/EncoderLib/CABACWriter.h       |   4 +
 source/Lib/EncoderLib/EncCu.cpp           |  24 +++++
 12 files changed, 482 insertions(+), 4 deletions(-)

diff --git a/source/Lib/CommonLib/ContextModelling.cpp b/source/Lib/CommonLib/ContextModelling.cpp
index 57dd1636b..b9ae70afd 100644
--- a/source/Lib/CommonLib/ContextModelling.cpp
+++ b/source/Lib/CommonLib/ContextModelling.cpp
@@ -150,6 +150,116 @@ void CoeffCodingContext::initSubblock( int SubsetId, bool sigGroupFlag )
 
 
 
+
+#if JVET_M0421_SPLIT_SIG
+void DeriveCtx::CtxSplit( const CodingStructure& cs, Partitioner& partitioner, unsigned& ctxSpl, unsigned& ctxQt, unsigned& ctxHv, unsigned& ctxHorBt, unsigned& ctxVerBt, bool* _canSplit /*= nullptr */ )
+{
+  const Position pos         = partitioner.currArea().blocks[partitioner.chType];
+  const unsigned curSliceIdx = cs.slice->getIndependentSliceIdx();
+#if HEVC_TILES_WPP
+  const unsigned curTileIdx  = cs.picture->tileMap->getTileIdxMap( partitioner.currArea().lumaPos() );
+#endif
+
+  // get left depth
+#if HEVC_TILES_WPP
+  const CodingUnit* cuLeft = cs.getCURestricted( pos.offset( -1, 0 ), curSliceIdx, curTileIdx, partitioner.chType );
+#else
+  const CodingUnit* cuLeft = cs.getCURestricted( pos.offset( -1, 0 ), curSliceIdx, partitioner.chType );
+#endif
+
+  // get above depth
+#if HEVC_TILES_WPP
+  const CodingUnit* cuAbove = cs.getCURestricted( pos.offset( 0, -1 ), curSliceIdx, curTileIdx, partitioner.chType );
+#else
+  const CodingUnit* cuAbove = cs.getCURestricted( pos.offset( 0, -1 ), curSliceIdx, partitioner.chType );
+#endif
+
+  bool canSplit[6];
+
+  if( _canSplit == nullptr )
+  {
+    partitioner.canSplit( cs, canSplit[0], canSplit[1], canSplit[2], canSplit[3], canSplit[4], canSplit[5] );
+  }
+  else
+  {
+    memcpy( canSplit, _canSplit, 6 * sizeof( bool ) );
+  }
+
+  ///////////////////////
+  // CTX do split (0-8)
+  ///////////////////////
+  const unsigned widthCurr  = partitioner.currArea().blocks[partitioner.chType].width;
+  const unsigned heightCurr = partitioner.currArea().blocks[partitioner.chType].height;
+
+  ctxSpl = 0;
+
+  if( cuLeft )
+  {
+    const unsigned heightLeft = cuLeft->blocks[partitioner.chType].height;
+    ctxSpl += ( heightLeft < heightCurr ? 1 : 0 );
+  }
+  if( cuAbove )
+  {
+    const unsigned widthAbove = cuAbove->blocks[partitioner.chType].width;
+    ctxSpl += ( widthAbove < widthCurr ? 1 : 0 );
+  }
+
+  unsigned numSplit = 0;
+  if( canSplit[1] ) numSplit += 2;
+  if( canSplit[2] ) numSplit += 1;
+  if( canSplit[3] ) numSplit += 1;
+  if( canSplit[4] ) numSplit += 1;
+  if( canSplit[5] ) numSplit += 1;
+
+  if( numSplit > 0 ) numSplit--;
+
+  ctxSpl += 3 * ( numSplit >> 1 );
+
+  //////////////////////////
+  // CTX is qt split (0-5)
+  //////////////////////////
+  ctxQt =  ( cuLeft  && cuLeft->qtDepth  > partitioner.currQtDepth ) ? 1 : 0;
+  ctxQt += ( cuAbove && cuAbove->qtDepth > partitioner.currQtDepth ) ? 1 : 0;
+  ctxQt += partitioner.currQtDepth < 2 ? 0 : 3;
+
+  ////////////////////////////
+  // CTX is ver split (0-4)
+  ////////////////////////////
+  ctxHv = 0;
+
+  const unsigned numHor = ( canSplit[2] ? 1 : 0 ) + ( canSplit[4] ? 1 : 0 );
+  const unsigned numVer = ( canSplit[3] ? 1 : 0 ) + ( canSplit[5] ? 1 : 0 );
+
+  if( numVer == numHor )
+  {
+    const Area& area = partitioner.currArea().blocks[partitioner.chType];
+
+    const unsigned wAbove       = cuAbove ? cuAbove->blocks[partitioner.chType].width  : 1;
+    const unsigned hLeft        = cuLeft  ? cuLeft ->blocks[partitioner.chType].height : 1;
+
+    const unsigned depAbove     = area.width / wAbove;
+    const unsigned depLeft      = area.height / hLeft;
+
+    if( depAbove == depLeft || !cuLeft || !cuAbove ) ctxHv = 0;
+    else if( depAbove < depLeft ) ctxHv = 1;
+    else ctxHv = 2;
+  }
+  else if( numVer < numHor )
+  {
+    ctxHv = 3;
+  }
+  else
+  {
+    ctxHv = 4;
+  }
+
+  //////////////////////////
+  // CTX is h/v bt (0-3)
+  //////////////////////////
+  ctxHorBt = ( partitioner.currBtDepth >= 2 ? 1 : 0 );
+  ctxVerBt = ( partitioner.currBtDepth >= 2 ? 3 : 2 );
+}
+#else
 unsigned DeriveCtx::CtxCUsplit( const CodingStructure& cs, Partitioner& partitioner )
 {
   auto adPartitioner = dynamic_cast<AdaptiveDepthPartitioner*>( &partitioner );
@@ -186,6 +296,7 @@ unsigned DeriveCtx::CtxCUsplit( const CodingStructure& cs, Partitioner& partitio
 
   return ctxId;
 }
+#endif
 
 unsigned DeriveCtx::CtxQtCbf( const ComponentID compID, const unsigned trDepth, const bool prevCbCbf )
 {
@@ -258,6 +369,7 @@ unsigned DeriveCtx::CtxIMVFlag( const CodingUnit& cu )
   return ctxId;
 }
 
+#if !JVET_M0421_SPLIT_SIG
 unsigned DeriveCtx::CtxBTsplit(const CodingStructure& cs, Partitioner& partitioner)
 {
   const Position pos          = partitioner.currArea().blocks[partitioner.chType];
@@ -306,6 +418,7 @@ unsigned DeriveCtx::CtxBTsplit(const CodingStructure& cs, Partitioner& partition
   return ctx;
 }
 
+#endif
 unsigned DeriveCtx::CtxTriangleFlag( const CodingUnit& cu )
 {
   const CodingStructure *cs = cu.cs;
diff --git a/source/Lib/CommonLib/ContextModelling.h b/source/Lib/CommonLib/ContextModelling.h
index 76fba82a0..f0856345a 100644
--- a/source/Lib/CommonLib/ContextModelling.h
+++ b/source/Lib/CommonLib/ContextModelling.h
@@ -294,8 +294,12 @@ public:
 
 namespace DeriveCtx
 {
+#if JVET_M0421_SPLIT_SIG
+void     CtxSplit     ( const CodingStructure& cs, Partitioner& partitioner, unsigned& ctxSpl, unsigned& ctxQt, unsigned& ctxHv, unsigned& ctxHorBt, unsigned& ctxVerBt, bool* canSplit = nullptr );
+#else
 unsigned CtxCUsplit   ( const CodingStructure& cs, Partitioner& partitioner );
 unsigned CtxBTsplit   ( const CodingStructure& cs, Partitioner& partitioner );
+#endif
 unsigned CtxQtCbf     ( const ComponentID compID, const unsigned trDepth, const bool prevCbCbf );
 unsigned CtxInterDir  ( const PredictionUnit& pu );
 unsigned CtxSkipFlag  ( const CodingUnit& cu );
diff --git a/source/Lib/CommonLib/Contexts.cpp b/source/Lib/CommonLib/Contexts.cpp
index 84a6947a1..cb190a390 100644
--- a/source/Lib/CommonLib/Contexts.cpp
+++ b/source/Lib/CommonLib/Contexts.cpp
@@ -349,6 +349,15 @@ std::vector<std::vector<uint8_t>> ContextSetCfg::sm_InitTables( NUMBER_OF_SLICE_
 // clang-format off
 const CtxSet ContextSetCfg::SplitFlag = ContextSetCfg::addCtxSet
 ({
+#if JVET_M0421_SPLIT_SIG
+  // |-------- do split ctx -------------------| 
+  {  93, 124, 141, 123, 125, 141, 139, 126, 157, },
+  { 108, 139, 156, 138, 140, 141, 139, 141, 143, },
+  { 153, 154, 172, 153, 140, 156, 154, 127, 159, },
+#if JVET_M0453_CABAC_ENGINE
+  { DWS, DWS, DWS, DWS, DWS, DWS, DWS, DWS, DWS, },
+#endif
+#else
 #if JVET_M0453_CABAC_ENGINE
   {  107, 110, 127, 106, 123, 140,},
   {  138, 140, 142, 106, 123, 125,},
@@ -359,8 +368,40 @@ const CtxSet ContextSetCfg::SplitFlag = ContextSetCfg::addCtxSet
   { 138, 111, 143, 107, 138, 140, },
   { 138, 141, 158, 151, 124, 126, },
 #endif
+#endif
+});
+
+#if JVET_M0421_SPLIT_SIG
+const CtxSet ContextSetCfg::SplitQtFlag = ContextSetCfg::addCtxSet
+({
+  { 153, 126, 142, 137, 109, 155, },
+  { 153, 126, 157, 122, 138, 140, },
+  { 153, 125, 127, 137, 153, 155, },
+#if JVET_M0453_CABAC_ENGINE
+  { DWS, DWS, DWS, DWS, DWS, DWS, },
+#endif
+});
+
+const CtxSet ContextSetCfg::SplitHvFlag = ContextSetCfg::addCtxSet
+({
+  { 154, 168, 155, 153, 155, },
+  { 154, 168, 170, 153, 170, },
+  { 154, 153, 140, 153, 154, },
+#if JVET_M0453_CABAC_ENGINE
+  { DWS, DWS, DWS, DWS, DWS, },
+#endif
 });
 
+const CtxSet ContextSetCfg::Split12Flag = ContextSetCfg::addCtxSet
+({
+  { 140, 154, 140, 154, },
+  { 155, 169, 140, 154, },
+  { 155, 154, 155, 154, },
+#if JVET_M0453_CABAC_ENGINE
+  { DWS, DWS, DWS, DWS, },
+#endif
+});
+#else
 const CtxSet ContextSetCfg::BTSplitFlag = ContextSetCfg::addCtxSet
 ({
   // |-------- 1st bin, 9 ctx for luma + 3 ctx for chroma------| |--2nd bin--| |3rd bin|
@@ -375,6 +416,7 @@ const CtxSet ContextSetCfg::BTSplitFlag = ContextSetCfg::addCtxSet
   { 139, 141, 157, 139, 155, 142, 153, 125, 141, 154, 154, 154, 154, 154, 154, 140, },
 #endif
 });
+#endif
 
 const CtxSet ContextSetCfg::SkipFlag = ContextSetCfg::addCtxSet
 ({
diff --git a/source/Lib/CommonLib/Contexts.h b/source/Lib/CommonLib/Contexts.h
index dae5f60a0..336bf5453 100644
--- a/source/Lib/CommonLib/Contexts.h
+++ b/source/Lib/CommonLib/Contexts.h
@@ -245,7 +245,13 @@ class ContextSetCfg
 public:
   // context sets: specify offset and size
   static const CtxSet   SplitFlag;
+#if JVET_M0421_SPLIT_SIG
+  static const CtxSet   SplitQtFlag;
+  static const CtxSet   SplitHvFlag;
+  static const CtxSet   Split12Flag;
+#else
   static const CtxSet   BTSplitFlag;
+#endif
   static const CtxSet   SkipFlag;
   static const CtxSet   MergeFlag;
   static const CtxSet   MergeIdx;
diff --git a/source/Lib/CommonLib/TypeDef.h b/source/Lib/CommonLib/TypeDef.h
index 63a0c44fa..02dd93e30 100644
--- a/source/Lib/CommonLib/TypeDef.h
+++ b/source/Lib/CommonLib/TypeDef.h
@@ -50,6 +50,8 @@
 #include <assert.h>
 #include <cassert>
 
+#define JVET_M0421_SPLIT_SIG                              1
+
 #define REMOVE_BIN_DECISION_TREE                          1
 
 // clang-format off
diff --git a/source/Lib/CommonLib/UnitPartitioner.cpp b/source/Lib/CommonLib/UnitPartitioner.cpp
index be921219b..82030ba6f 100644
--- a/source/Lib/CommonLib/UnitPartitioner.cpp
+++ b/source/Lib/CommonLib/UnitPartitioner.cpp
@@ -297,8 +297,89 @@ void QTBTPartitioner::splitCurrArea( const PartSplit split, const CodingStructur
   }
 }
 
+#if JVET_M0421_SPLIT_SIG
+void QTBTPartitioner::canSplit( const CodingStructure &cs, bool& canNo, bool& canQt, bool& canBh, bool& canBv, bool& canTh, bool& canTv )
+{
+  const PartSplit implicitSplit = m_partStack.back().checkdIfImplicit ? m_partStack.back().implicitSplit : getImplicitSplit( cs );
+  
+  const unsigned maxBTD         = cs.pcv->getMaxBtDepth( *cs.slice, chType ) + currImplicitBtDepth;
+  const unsigned maxBtSize      = cs.pcv->getMaxBtSize ( *cs.slice, chType );
+  const unsigned minBtSize      = cs.pcv->getMinBtSize ( *cs.slice, chType );
+  const unsigned maxTtSize      = cs.pcv->getMaxTtSize ( *cs.slice, chType );
+  const unsigned minTtSize      = cs.pcv->getMinTtSize ( *cs.slice, chType );
+  const unsigned minQtSize      = cs.pcv->getMinQtSize ( *cs.slice, chType );
+
+  canNo = canQt = canBh = canTh = canBv = canTv = true;
+  bool canBtt = currMtDepth < maxBTD;
+
+  // the minimal and maximal sizes are given in luma samples
+  const CompArea&  area  = currArea().Y();
+        PartLevel& level = m_partStack.back();
+
+  const PartSplit lastSplit = level.split;
+  const PartSplit parlSplit = lastSplit == CU_TRIH_SPLIT ? CU_HORZ_SPLIT : CU_VERT_SPLIT;
+
+  // don't allow QT-splitting below a BT split
+  if( lastSplit != CTU_LEVEL && lastSplit != CU_QUAD_SPLIT ) canQt = false;
+  if( area.width <= minQtSize )                              canQt = false;
+
+  if( implicitSplit != CU_DONT_SPLIT )
+  {
+    canNo = canTh = canTv = false;
+
+    canBh = implicitSplit == CU_HORZ_SPLIT;
+    canBv = implicitSplit == CU_VERT_SPLIT;
+
+    return;
+  }
+
+  if( ( lastSplit == CU_TRIH_SPLIT || lastSplit == CU_TRIV_SPLIT ) && currPartIdx() == 1 )
+  {
+    canBh = parlSplit != CU_HORZ_SPLIT;
+    canBv = parlSplit != CU_VERT_SPLIT;
+  }
+
+  if( canBtt ) if( ( area.width <= minBtSize && area.height <= minBtSize )
+              && ( ( area.width <= minTtSize && area.height <= minTtSize ) || cs.sps->getSpsNext().getMTTMode() == 0 ) ) canBtt = false;
+  if( canBtt ) if( ( area.width > maxBtSize || area.height > maxBtSize )
+              && ( ( area.width > maxTtSize || area.height > maxTtSize ) || cs.sps->getSpsNext().getMTTMode() == 0 ) ) canBtt = false;
+
+  if( !canBtt )
+  {
+    canBh = canTh = canBv = canTv = false;
+
+    return;
+  }
+
+  // specific check for BT splits
+  if( area.height <= minBtSize || area.height > maxBtSize )                            canBh = false;
+  if( area.width > MAX_TU_SIZE_FOR_PROFILE && area.height <= MAX_TU_SIZE_FOR_PROFILE ) canBh = false;
+
+  if( area.width <= minBtSize || area.width > maxBtSize )                              canBv = false;
+  if( area.width <= MAX_TU_SIZE_FOR_PROFILE && area.height > MAX_TU_SIZE_FOR_PROFILE ) canBv = false;
+
+  if( ( cs.sps->getSpsNext().getMTTMode() & 1 ) != 1 )                                 canTh = false;
+  if( area.height <= 2 * minTtSize || area.height > maxTtSize || area.width > maxTtSize ) 
+                                                                                       canTh = false;
+  if( area.width > MAX_TU_SIZE_FOR_PROFILE || area.height > MAX_TU_SIZE_FOR_PROFILE )  canTh = false;
+
+  if( ( cs.sps->getSpsNext().getMTTMode() & 1 ) != 1 )                                 canTv = false;
+  if( area.width <= 2 * minTtSize || area.width > maxTtSize || area.height > maxTtSize )
+                                                                                       canTv = false;
+  if( area.width > MAX_TU_SIZE_FOR_PROFILE || area.height > MAX_TU_SIZE_FOR_PROFILE )  canTv = false;
+}
+
+#endif
 bool QTBTPartitioner::canSplit( const PartSplit split, const CodingStructure &cs )
 {
+#if JVET_M0421_SPLIT_SIG
+  const CompArea area       = currArea().Y();
+  const unsigned maxTrSize  = cs.sps->getMaxTrSize();
+
+  bool canNo, canQt, canBh, canTh, canBv, canTv;
+
+  canSplit( cs, canNo, canQt, canBh, canBv, canTh, canTv );
+#else
   const PartSplit implicitSplit = getImplicitSplit( cs );
 
   // the minimal and maximal sizes are given in luma samples
@@ -319,6 +400,7 @@ bool QTBTPartitioner::canSplit( const PartSplit split, const CodingStructure &cs
     return false;
   }
 
+#endif
   switch( split )
   {
   case CTU_LEVEL:
@@ -328,6 +410,20 @@ bool QTBTPartitioner::canSplit( const PartSplit split, const CodingStructure &cs
   case TU_MAX_TR_SPLIT:
     return area.width > maxTrSize || area.height > maxTrSize;
     break;
+#if JVET_M0421_SPLIT_SIG
+  case CU_QUAD_SPLIT:
+    return canQt;
+  case CU_DONT_SPLIT:
+    return canNo;
+  case CU_HORZ_SPLIT:
+    return canBh;
+  case CU_VERT_SPLIT:
+    return canBv;
+  case CU_TRIH_SPLIT:
+    return canTh;
+  case CU_TRIV_SPLIT:
+    return canTv;
+#else
   case CU_QUAD_SPLIT:
   {
     // don't allow QT-splitting below a BT split
@@ -369,8 +465,15 @@ bool QTBTPartitioner::canSplit( const PartSplit split, const CodingStructure &cs
   }
     if( implicitSplit == split )                                   return true;
     if( implicitSplit != CU_DONT_SPLIT && implicitSplit != split ) return false;
+#endif
   case CU_MT_SPLIT:
+#if JVET_M0421_SPLIT_SIG
+    return ( canBh || canTh || canBv || canTv );
+#endif
   case CU_BT_SPLIT:
+#if JVET_M0421_SPLIT_SIG
+    return ( canBh || canBv );
+#else
   {
     if( currMtDepth >= maxBTD )                               return false;
     if(      ( area.width <= minBtSize && area.height <= minBtSize )
@@ -382,13 +485,14 @@ bool QTBTPartitioner::canSplit( const PartSplit split, const CodingStructure &cs
       return false;
     }
   }
+#endif
   break;
   default:
     THROW( "Unknown split mode" );
     return false;
     break;
   }
-
+#if !JVET_M0421_SPLIT_SIG
   // specific check for BT splits
   switch( split )
   {
@@ -413,6 +517,7 @@ bool QTBTPartitioner::canSplit( const PartSplit split, const CodingStructure &cs
   default:
     break;
   }
+#endif
 
   return true;
 }
diff --git a/source/Lib/CommonLib/UnitPartitioner.h b/source/Lib/CommonLib/UnitPartitioner.h
index 6b8e354f9..222fe99ed 100644
--- a/source/Lib/CommonLib/UnitPartitioner.h
+++ b/source/Lib/CommonLib/UnitPartitioner.h
@@ -128,6 +128,9 @@ public:
   virtual void copyState                  ( const Partitioner& other );
 
 public:
+#if JVET_M0421_SPLIT_SIG
+  virtual void canSplit                   ( const CodingStructure &cs, bool& canNo, bool& canQt, bool& canBh, bool& canBv, bool& canTh, bool& canTv ) = 0;
+#endif
   virtual bool canSplit                   ( const PartSplit split,                          const CodingStructure &cs ) = 0;
   virtual bool isSplitImplicit            ( const PartSplit split,                          const CodingStructure &cs ) = 0;
   virtual PartSplit getImplicitSplit      (                                                 const CodingStructure &cs ) = 0;
@@ -147,7 +150,10 @@ public:
   void exitCurrSplit              ();
   bool nextPart                   ( const CodingStructure &cs, bool autoPop = false );
   bool hasNextPart                ();
-
+  
+#if JVET_M0421_SPLIT_SIG
+  void canSplit                   ( const CodingStructure &cs, bool& canNo, bool& canQt, bool& canBh, bool& canBv, bool& canTh, bool& canTv );
+#endif
   bool canSplit                   ( const PartSplit split,                          const CodingStructure &cs );
   bool isSplitImplicit            ( const PartSplit split,                          const CodingStructure &cs );
   PartSplit getImplicitSplit      (                                                 const CodingStructure &cs );
diff --git a/source/Lib/DecoderLib/CABACReader.cpp b/source/Lib/DecoderLib/CABACReader.cpp
index aa33ec56f..f1ee33467 100644
--- a/source/Lib/DecoderLib/CABACReader.cpp
+++ b/source/Lib/DecoderLib/CABACReader.cpp
@@ -421,6 +421,14 @@ bool CABACReader::coding_tree( CodingStructure& cs, Partitioner& partitioner, CU
     }
   }
 
+#if JVET_M0421_SPLIT_SIG
+  const PartSplit splitMode = split_cu_mode( cs, partitioner );
+
+  CHECK( !partitioner.canSplit( splitMode, cs ), "Got an invalid split!" );
+
+  if( splitMode != CU_DONT_SPLIT )
+  {
+#else
   const PartSplit implicitSplit = partitioner.getImplicitSplit( cs );
 
   // QT
@@ -440,6 +448,7 @@ bool CABACReader::coding_tree( CodingStructure& cs, Partitioner& partitioner, CU
     // quad-tree split
     if( qtSplit )
     {
+#endif
       if (CS::isDualITree(cs) && pPartitionerChroma != nullptr && (partitioner.currArea().lwidth() >= 64 || partitioner.currArea().lheight() >= 64))
       {
         partitioner.splitCurrArea(CU_QUAD_SPLIT, cs);
@@ -519,7 +528,11 @@ bool CABACReader::coding_tree( CodingStructure& cs, Partitioner& partitioner, CU
       }
       else
       {
+#if JVET_M0421_SPLIT_SIG
+      partitioner.splitCurrArea( splitMode, cs );
+#else
       partitioner.splitCurrArea( CU_QUAD_SPLIT, cs );
+#endif
       do
       {
         if( !lastSegment && cs.area.blocks[partitioner.chType].contains( partitioner.currArea().blocks[partitioner.chType].pos() ) )
@@ -531,9 +544,12 @@ bool CABACReader::coding_tree( CodingStructure& cs, Partitioner& partitioner, CU
       partitioner.exitCurrSplit();
       }
       return lastSegment;
+#if !JVET_M0421_SPLIT_SIG
     }
+#endif
   }
 
+#if !JVET_M0421_SPLIT_SIG
   {
     // MT
     bool mtSplit = partitioner.canSplit( CU_MT_SPLIT, cs );
@@ -560,7 +576,7 @@ bool CABACReader::coding_tree( CodingStructure& cs, Partitioner& partitioner, CU
     }
   }
 
-
+#endif
   CodingUnit& cu = cs.addCU( CS::getArea( cs, currArea, partitioner.chType ), partitioner.chType );
 
   partitioner.setCUData( cu );
@@ -595,6 +611,76 @@ bool CABACReader::coding_tree( CodingStructure& cs, Partitioner& partitioner, CU
   return isLastCtu;
 }
 
+#if JVET_M0421_SPLIT_SIG
+PartSplit CABACReader::split_cu_mode( CodingStructure& cs, Partitioner &partitioner )
+{
+  RExt__DECODER_DEBUG_BIT_STATISTICS_CREATE_SET_SIZE2( STATS__CABAC_BITS__SPLIT_FLAG, partitioner.currArea().blocks[partitioner.chType].size(), partitioner.chType );
+
+  PartSplit mode = CU_DONT_SPLIT;
+  
+  bool canNo, canQt, canBh, canBv, canTh, canTv;
+  partitioner.canSplit( cs, canNo, canQt, canBh, canBv, canTh, canTv );
+
+  bool canSpl[6] = { canNo, canQt, canBh, canBv, canTh, canTv };
+
+  unsigned ctxSplit = 0, ctxQtSplit = 0, ctxBttHV = 0, ctxBttH12 = 0, ctxBttV12;
+  DeriveCtx::CtxSplit( cs, partitioner, ctxSplit, ctxQtSplit, ctxBttHV, ctxBttH12, ctxBttV12, canSpl );
+
+  bool isSplit = canBh || canBv || canTh || canTv || canQt;
+
+  if( canNo && isSplit )
+  {
+    isSplit = m_BinDecoder.decodeBin( Ctx::SplitFlag( ctxSplit ) );
+  }
+
+  DTRACE( g_trace_ctx, D_SYNTAX, "split_cu_mode() ctx=%d split=%d\n", ctxSplit, isSplit );
+
+  if( !isSplit )
+  {
+    return CU_DONT_SPLIT;
+  }
+
+  const bool canBtt = canBh || canBv || canTh || canTv;
+  bool       isQt   = canQt;
+
+  if( isQt && canBtt )
+  {
+    isQt = m_BinDecoder.decodeBin( Ctx::SplitQtFlag( ctxQtSplit ) );
+  }
+
+  DTRACE( g_trace_ctx, D_SYNTAX, "split_cu_mode() ctx=%d qt=%d\n", ctxQtSplit, isQt );
+
+  if( isQt )
+  {
+    return CU_QUAD_SPLIT;
+  }
+
+  const bool canHor = canBh || canTh;
+  bool        isVer = canBv || canTv;
+
+  if( isVer && canHor )
+  {
+    isVer = m_BinDecoder.decodeBin( Ctx::SplitHvFlag( ctxBttHV ) );
+  }
+
+  const bool can14 = isVer ? canTv : canTh;
+  bool        is12 = isVer ? canBv : canBh;
+
+  if( is12 && can14 )
+  {
+    is12 = m_BinDecoder.decodeBin( Ctx::Split12Flag( isVer ? ctxBttV12 : ctxBttH12 ) );
+  }
+
+  if     ( isVer && is12 )  mode = CU_VERT_SPLIT;
+  else if( isVer && !is12 ) mode = CU_TRIV_SPLIT;
+  else if( !isVer && is12 ) mode = CU_HORZ_SPLIT;
+  else                      mode = CU_TRIH_SPLIT;
+
+  DTRACE( g_trace_ctx, D_SYNTAX, "split_cu_mode() ctxHv=%d ctx12=%d mode=%d\n", ctxBttHV, isVer ? ctxBttV12 : ctxBttH12, mode );
+
+  return mode;
+}
+#else
 PartSplit CABACReader::split_cu_mode_mt( CodingStructure& cs, Partitioner &partitioner )
 {
   RExt__DECODER_DEBUG_BIT_STATISTICS_CREATE_SET( STATS__CABAC_BITS__SPLIT_FLAG );
@@ -694,6 +780,7 @@ bool CABACReader::split_cu_flag( CodingStructure& cs, Partitioner &partitioner )
   return split;
 }
 
+#endif
 
 //================================================================================
 //  clause 7.3.8.5
diff --git a/source/Lib/DecoderLib/CABACReader.h b/source/Lib/DecoderLib/CABACReader.h
index 89dcf70e7..1fb95d7fb 100644
--- a/source/Lib/DecoderLib/CABACReader.h
+++ b/source/Lib/DecoderLib/CABACReader.h
@@ -70,8 +70,12 @@ public:
 
   // coding (quad)tree (clause 7.3.8.4)
   bool        coding_tree               ( CodingStructure&              cs,     Partitioner&    pm,       CUCtx& cuCtx, Partitioner* pPartitionerChroma = nullptr, CUCtx* pCuCtxChroma = nullptr);
+#if JVET_M0421_SPLIT_SIG
+  PartSplit   split_cu_mode             ( CodingStructure&              cs,     Partitioner&    pm );
+#else
   bool        split_cu_flag             ( CodingStructure&              cs,     Partitioner&    pm );
   PartSplit   split_cu_mode_mt          ( CodingStructure&              cs,     Partitioner&    pm );
+#endif
 
   // coding unit (clause 7.3.8.5)
   bool        coding_unit               ( CodingUnit&                   cu,     Partitioner&    pm,       CUCtx& cuCtx );
diff --git a/source/Lib/EncoderLib/CABACWriter.cpp b/source/Lib/EncoderLib/CABACWriter.cpp
index 014198831..453fc371f 100644
--- a/source/Lib/EncoderLib/CABACWriter.cpp
+++ b/source/Lib/EncoderLib/CABACWriter.cpp
@@ -396,6 +396,16 @@ void CABACWriter::coding_tree(const CodingStructure& cs, Partitioner& partitione
     }
   }
 
+#if JVET_M0421_SPLIT_SIG
+  const PartSplit splitMode = CU::getSplitAtDepth( cu, partitioner.currDepth );
+
+  split_cu_mode( splitMode, cs, partitioner );
+
+  CHECK( !partitioner.canSplit( splitMode, cs ), "The chosen split mode is invalid!" );
+
+  if( splitMode != CU_DONT_SPLIT )
+  {
+#else
   const PartSplit implicitSplit = partitioner.getImplicitSplit( cs );
 
   // QT
@@ -415,6 +425,7 @@ void CABACWriter::coding_tree(const CodingStructure& cs, Partitioner& partitione
     // quad-tree split
     if( qtSplit )
     {
+#endif
       if (CS::isDualITree(cs) && pPartitionerChroma != nullptr && (partitioner.currArea().lwidth() >= 64 || partitioner.currArea().lheight() >= 64))
       {
         partitioner.splitCurrArea(CU_QUAD_SPLIT, cs);
@@ -459,7 +470,11 @@ void CABACWriter::coding_tree(const CodingStructure& cs, Partitioner& partitione
       }
       else
       {
+#if JVET_M0421_SPLIT_SIG
+      partitioner.splitCurrArea( splitMode, cs );
+#else
       partitioner.splitCurrArea( CU_QUAD_SPLIT, cs );
+#endif
 
       do
       {
@@ -472,9 +487,12 @@ void CABACWriter::coding_tree(const CodingStructure& cs, Partitioner& partitione
       partitioner.exitCurrSplit();
       }
       return;
+#if !JVET_M0421_SPLIT_SIG
     }
+#endif
   }
 
+#if !JVET_M0421_SPLIT_SIG
   {
     bool mtSplit = partitioner.canSplit( CU_MT_SPLIT, cs );
 
@@ -501,6 +519,7 @@ void CABACWriter::coding_tree(const CodingStructure& cs, Partitioner& partitione
     }
   }
 
+#endif
   // Predict QP on start of quantization group
   if( pps.getUseDQP() && !cuCtx.isDQPCoded && CU::isQGStart( cu, partitioner ) )
   {
@@ -515,6 +534,68 @@ void CABACWriter::coding_tree(const CodingStructure& cs, Partitioner& partitione
   DTRACE_BLOCK_REC_COND( ( !isEncoding() ), cs.picture->getRecoBuf( cu ), cu, cu.predMode );
 }
 
+#if JVET_M0421_SPLIT_SIG
+void CABACWriter::split_cu_mode( const PartSplit split, const CodingStructure& cs, Partitioner& partitioner )
+{
+  bool canNo, canQt, canBh, canBv, canTh, canTv;
+  partitioner.canSplit( cs, canNo, canQt, canBh, canBv, canTh, canTv );
+
+  bool canSpl[6] = { canNo, canQt, canBh, canBv, canTh, canTv };
+
+  unsigned ctxSplit = 0, ctxQtSplit = 0, ctxBttHV = 0, ctxBttH12 = 0, ctxBttV12;
+  DeriveCtx::CtxSplit( cs, partitioner, ctxSplit, ctxQtSplit, ctxBttHV, ctxBttH12, ctxBttV12, canSpl );
+  
+  const bool canSplit = canBh || canBv || canTh || canTv || canQt;
+  const bool isNo     = split == CU_DONT_SPLIT;
+
+  if( canNo && canSplit )
+  {
+    m_BinEncoder.encodeBin( !isNo, Ctx::SplitFlag( ctxSplit ) );
+  }
+
+  DTRACE( g_trace_ctx, D_SYNTAX, "split_cu_mode() ctx=%d split=%d\n", ctxSplit, !isNo );
+
+  if( isNo )
+  {
+    return;
+  }
+
+  const bool canBtt = canBh || canBv || canTh || canTv;
+  const bool isQt   = split == CU_QUAD_SPLIT;
+
+  if( canQt && canBtt )
+  {
+    m_BinEncoder.encodeBin( isQt, Ctx::SplitQtFlag( ctxQtSplit ) );
+  }
+
+  DTRACE( g_trace_ctx, D_SYNTAX, "split_cu_mode() ctx=%d qt=%d\n", ctxQtSplit, isQt );
+
+  if( isQt )
+  {
+    return;
+  }
+
+  const bool canHor = canBh || canTh;
+  const bool canVer = canBv || canTv;
+  const bool  isVer = split == CU_VERT_SPLIT || split == CU_TRIV_SPLIT;
+
+  if( canVer && canHor )
+  {
+    m_BinEncoder.encodeBin( isVer, Ctx::SplitHvFlag( ctxBttHV ) );
+  }
+
+  const bool can14 = isVer ? canTv : canTh;
+  const bool can12 = isVer ? canBv : canBh;
+  const bool  is12 = isVer ? ( split == CU_VERT_SPLIT ) : ( split == CU_HORZ_SPLIT );
+
+  if( can12 && can14 )
+  {
+    m_BinEncoder.encodeBin( is12, Ctx::Split12Flag( isVer ? ctxBttV12 : ctxBttH12 ) );
+  }
+
+  DTRACE( g_trace_ctx, D_SYNTAX, "split_cu_mode() ctxHv=%d ctx12=%d mode=%d\n", ctxBttHV, isVer ? ctxBttV12 : ctxBttH12, split );
+}
+#else
 void CABACWriter::split_cu_flag( bool split, const CodingStructure& cs, Partitioner& partitioner )
 {
   unsigned maxQTDepth = ( g_aucLog2[cs.sps->getCTUSize()] - g_aucLog2[cs.sps->getMinQTSize(cs.slice->getSliceType(), partitioner.chType)] );
@@ -600,7 +681,7 @@ void CABACWriter::split_cu_mode_mt(const PartSplit split, const CodingStructure&
 
   DTRACE(g_trace_ctx, D_SYNTAX, "split_cu_mode_mt() ctx=%d split=%d\n", ctxIdBT, split);
 }
-
+#endif
 
 //================================================================================
 //  clause 7.3.8.5
diff --git a/source/Lib/EncoderLib/CABACWriter.h b/source/Lib/EncoderLib/CABACWriter.h
index 46820c7be..a43066e4c 100644
--- a/source/Lib/EncoderLib/CABACWriter.h
+++ b/source/Lib/EncoderLib/CABACWriter.h
@@ -82,8 +82,12 @@ public:
   void        sao_offset_pars           ( const SAOOffset&              ctbPars,  ComponentID       compID,     bool sliceEnabled,  int bitDepth );
   // coding (quad)tree (clause 7.3.8.4)
   void        coding_tree               ( const CodingStructure&        cs,       Partitioner&      pm,         CUCtx& cuCtx, Partitioner* pPartitionerChroma = nullptr, CUCtx* pCuCtxChroma = nullptr);
+#if JVET_M0421_SPLIT_SIG
+  void        split_cu_mode             ( const PartSplit               split,    const CodingStructure& cs,    Partitioner& pm );
+#else
   void        split_cu_flag             ( bool                          split,    const CodingStructure& cs,    Partitioner& pm );
   void        split_cu_mode_mt          ( const PartSplit               split,    const CodingStructure& cs,    Partitioner& pm );
+#endif
 
   // coding unit (clause 7.3.8.5)
   void        coding_unit               ( const CodingUnit&             cu,       Partitioner&      pm,         CUCtx& cuCtx );
diff --git a/source/Lib/EncoderLib/EncCu.cpp b/source/Lib/EncoderLib/EncCu.cpp
index d34cdde6d..0c1987e0c 100644
--- a/source/Lib/EncoderLib/EncCu.cpp
+++ b/source/Lib/EncoderLib/EncCu.cpp
@@ -1029,10 +1029,19 @@ void EncCu::xCheckModeSplit(CodingStructure *&tempCS, CodingStructure *&bestCS,
   m_CABACEstimator->getCtx() = m_CurrCtx->start;
 
   const TempCtx ctxStartSP( m_CtxCache, SubCtx( Ctx::SplitFlag,   m_CABACEstimator->getCtx() ) );
+#if JVET_M0421_SPLIT_SIG
+  const TempCtx ctxStartQt( m_CtxCache, SubCtx( Ctx::SplitQtFlag, m_CABACEstimator->getCtx() ) );
+  const TempCtx ctxStartHv( m_CtxCache, SubCtx( Ctx::SplitHvFlag, m_CABACEstimator->getCtx() ) );
+  const TempCtx ctxStart12( m_CtxCache, SubCtx( Ctx::Split12Flag, m_CABACEstimator->getCtx() ) );
+#else
   const TempCtx ctxStartBT( m_CtxCache, SubCtx( Ctx::BTSplitFlag, m_CABACEstimator->getCtx() ) );
+#endif
 
   m_CABACEstimator->resetBits();
 
+#if JVET_M0421_SPLIT_SIG
+  m_CABACEstimator->split_cu_mode( split, *tempCS, partitioner );
+#else
   if( partitioner.getImplicitSplit( *tempCS ) != CU_QUAD_SPLIT )
   {
     if( partitioner.canSplit( CU_QUAD_SPLIT, *tempCS ) )
@@ -1044,12 +1053,19 @@ void EncCu::xCheckModeSplit(CodingStructure *&tempCS, CodingStructure *&bestCS,
       m_CABACEstimator->split_cu_mode_mt( split, *tempCS, partitioner );
     }
   }
+#endif
 
   const double factor = ( tempCS->currQP[partitioner.chType] > 30 ? 1.1 : 1.075 );
   const double cost   = m_pcRdCost->calcRdCost( uint64_t( m_CABACEstimator->getEstFracBits() + ( ( bestCS->fracBits ) / factor ) ), Distortion( bestCS->dist / factor ) );
 
   m_CABACEstimator->getCtx() = SubCtx( Ctx::SplitFlag,   ctxStartSP );
+#if JVET_M0421_SPLIT_SIG
+  m_CABACEstimator->getCtx() = SubCtx( Ctx::SplitQtFlag, ctxStartQt );
+  m_CABACEstimator->getCtx() = SubCtx( Ctx::SplitHvFlag, ctxStartHv );
+  m_CABACEstimator->getCtx() = SubCtx( Ctx::Split12Flag, ctxStart12 );
+#else
   m_CABACEstimator->getCtx() = SubCtx( Ctx::BTSplitFlag, ctxStartBT );
+#endif
 
   if( cost > bestCS->cost )
   {
@@ -1159,6 +1175,9 @@ void EncCu::xCheckModeSplit(CodingStructure *&tempCS, CodingStructure *&bestCS,
     {
       m_CABACEstimator->resetBits();
 
+#if JVET_M0421_SPLIT_SIG
+      m_CABACEstimator->split_cu_mode( split, *tempCS, partitioner );
+#else
       if( partitioner.canSplit( CU_QUAD_SPLIT, *tempCS ) )
       {
         m_CABACEstimator->split_cu_flag( split == CU_QUAD_SPLIT, *tempCS, partitioner );
@@ -1167,6 +1186,7 @@ void EncCu::xCheckModeSplit(CodingStructure *&tempCS, CodingStructure *&bestCS,
       {
         m_CABACEstimator->split_cu_mode_mt( split, *tempCS, partitioner );
       }
+#endif
 
       tempCS->fracBits += m_CABACEstimator->getEstFracBits(); // split bits
     }
@@ -3542,6 +3562,9 @@ void EncCu::xEncodeDontSplit( CodingStructure &cs, Partitioner &partitioner )
 {
   m_CABACEstimator->resetBits();
 
+#if JVET_M0421_SPLIT_SIG
+  m_CABACEstimator->split_cu_mode( CU_DONT_SPLIT, cs, partitioner );
+#else
   {
     if( partitioner.canSplit( CU_QUAD_SPLIT, cs ) )
     {
@@ -3552,6 +3575,7 @@ void EncCu::xEncodeDontSplit( CodingStructure &cs, Partitioner &partitioner )
       m_CABACEstimator->split_cu_mode_mt( CU_DONT_SPLIT, cs, partitioner );
     }
   }
+#endif
 
   cs.fracBits += m_CABACEstimator->getEstFracBits(); // split bits
   cs.cost      = m_pcRdCost->calcRdCost( cs.fracBits, cs.dist );
-- 
GitLab