diff --git a/doc/software-manual.tex b/doc/software-manual.tex
index a5dccae4edb0081da0c52e406471b66e09c4c63f..d5bd680429ca895c158468476c415e047a3cfc29 100644
--- a/doc/software-manual.tex
+++ b/doc/software-manual.tex
@@ -2022,6 +2022,12 @@ Enables optimization of non-linear filters for ALF on Chroma channels.
 Enables or disables symmetric MVD mode.
 \\
 
+\Option{RDPCM} &
+%\ShortOption{\None} &
+\Default{false} &
+Enables or disables RDPCM coding mode.
+\\
+
 \end{OptionTableNoShorthand}
 
 %%
diff --git a/source/App/EncoderApp/EncApp.cpp b/source/App/EncoderApp/EncApp.cpp
index 79bac614584a9ae96bd6c0adb8efdefe2d020c73..c8c4e7ac9603e8e8b403b1c3d2f8492e5af8ca86 100644
--- a/source/App/EncoderApp/EncApp.cpp
+++ b/source/App/EncoderApp/EncApp.cpp
@@ -291,6 +291,9 @@ void EncApp::xInitLibCfg()
 #endif
 #if JVET_N0449_MMVD_SIMP
   m_cEncLib.setMmvdDisNum                                        (m_MmvdDisNum);
+#endif
+#if JVET_N0413_RDPCM
+  m_cEncLib.setRDPCM                                              ( m_RdpcmMode );
 #endif
   m_cEncLib.setIBCMode                                           ( m_IBCMode );
   m_cEncLib.setIBCLocalSearchRangeX                              ( m_IBCLocalSearchRangeX );
diff --git a/source/App/EncoderApp/EncAppCfg.cpp b/source/App/EncoderApp/EncAppCfg.cpp
index e9709a4001bf714b927f17bc54336f407771b41e..e7266122e672a7203b1b897d209c4bf98967ca19 100644
--- a/source/App/EncoderApp/EncAppCfg.cpp
+++ b/source/App/EncoderApp/EncAppCfg.cpp
@@ -885,6 +885,9 @@ bool EncAppCfg::parseCfg( int argc, char* argv[] )
   ("DMVR",                                            m_DMVR,                                           false, "Decoder-side Motion Vector Refinement")
 #if JVET_N0449_MMVD_SIMP
   ("MmvdDisNum",                                      m_MmvdDisNum,                                     8,     "Number of MMVD Distance Entries")
+#endif
+#if JVET_N0413_RDPCM
+  ( "RDPCM",                                         m_RdpcmMode,                                       false, "RDPCM")
 #endif
   ( "IBC",                                            m_IBCMode,                                           0u, "IBCMode (0x1:enabled, 0x0:disabled)  [default: disabled]")
   ( "IBCLocalSearchRangeX",                           m_IBCLocalSearchRangeX,                            128u, "Search range of IBC local search in x direction")
@@ -1983,6 +1986,9 @@ bool EncAppCfg::xCheckParameter()
     xConfirmPara(m_DMVR, "DMVR only allowed with NEXT profile");
 #if JVET_N0449_MMVD_SIMP
     xConfirmPara(m_MmvdDisNum, "Number of distance MMVD entry setting only allowed with NEXT profile");
+#endif
+#if JVET_N0413_RDPCM
+    xConfirmPara(m_RdpcmMode, "RDPCM only allowed with NEXT profile");
 #endif
     // ADD_NEW_TOOL : (parameter check) add a check for next tools here
   }
@@ -3183,6 +3189,9 @@ void EncAppCfg::xPrintParameter()
     msg(VERBOSE, "DMVR:%d ", m_DMVR);
 #if JVET_N0449_MMVD_SIMP
     msg(VERBOSE, "MmvdDisNum:%d ", m_MmvdDisNum);
+#endif
+#if JVET_N0413_RDPCM
+    msg(VERBOSE, "RDPCM:%d ", m_RdpcmMode );
 #endif
   }
     msg(VERBOSE, "IBC:%d ", m_IBCMode);
diff --git a/source/App/EncoderApp/EncAppCfg.h b/source/App/EncoderApp/EncAppCfg.h
index 70ee66a5c29656e445bab876fd236e6beab84dc6..2663a5d6b1852f5f0bc2ccb9d768a717c69f12b7 100644
--- a/source/App/EncoderApp/EncAppCfg.h
+++ b/source/App/EncoderApp/EncAppCfg.h
@@ -275,6 +275,9 @@ protected:
 #endif
 #if JVET_N0449_MMVD_SIMP
   int       m_MmvdDisNum;
+#endif
+#if JVET_N0413_RDPCM
+  bool      m_RdpcmMode;
 #endif
   unsigned  m_IBCMode;
   unsigned  m_IBCLocalSearchRangeX;
diff --git a/source/Lib/CommonLib/CommonDef.h b/source/Lib/CommonLib/CommonDef.h
index dc1127c6346b9527414ec74dfe5227c91d27e13e..12f6bf8610a732e1687793f107be9a5372f98423 100644
--- a/source/Lib/CommonLib/CommonDef.h
+++ b/source/Lib/CommonLib/CommonDef.h
@@ -219,6 +219,9 @@ static const int HOR_IDX =                    (1 * (NUM_DIR - 1) + 2); ///< inde
 static const int DIA_IDX =                    (2 * (NUM_DIR - 1) + 2); ///< index for intra DIAGONAL   mode
 static const int VER_IDX =                    (3 * (NUM_DIR - 1) + 2); ///< index for intra VERTICAL   mode
 static const int VDIA_IDX =                   (4 * (NUM_DIR - 1) + 2); ///< index for intra VDIAGONAL  mode
+#if JVET_N0413_RDPCM
+static const int BDPCM_IDX =                  (5 * (NUM_DIR - 1) + 2); ///< index for intra VDIAGONAL  mode
+#endif
 static const int NOMODE_IDX =                               MAX_UCHAR; ///< indicating uninitialized elements
 
 static const int NUM_CHROMA_MODE = (5 + NUM_LMC_MODE); ///< total number of chroma modes
@@ -346,6 +349,9 @@ static const int DMVR_SUBCU_WIDTH_LOG2 = 4;
 static const int DMVR_SUBCU_HEIGHT_LOG2 = 4;
 static const int MAX_NUM_SUBCU_DMVR = ((MAX_CU_SIZE * MAX_CU_SIZE) >> (DMVR_SUBCU_WIDTH_LOG2 + DMVR_SUBCU_HEIGHT_LOG2));
 static const int DMVR_NUM_ITERATION = 2;
+#if JVET_N0413_RDPCM
+static const int BDPCM_MAX_CU_SIZE = 32;      ///<  maximum CU size for RDPCM mode
+#endif
 
 //QTBT high level parameters
 //for I slice luma CTB configuration para.
diff --git a/source/Lib/CommonLib/ContextModelling.cpp b/source/Lib/CommonLib/ContextModelling.cpp
index bc2f329ab97a10bbe91584ad33e3b147ed4d333d..26351a6d912d6856ee9cb9ddd8273307064f3774 100644
--- a/source/Lib/CommonLib/ContextModelling.cpp
+++ b/source/Lib/CommonLib/ContextModelling.cpp
@@ -42,7 +42,11 @@
 
 
 #if HEVC_USE_SIGN_HIDING
+#if JVET_N0413_RDPCM
+CoeffCodingContext::CoeffCodingContext( const TransformUnit& tu, ComponentID component, bool signHide, bool bdpcm )
+#else
 CoeffCodingContext::CoeffCodingContext(const TransformUnit& tu, ComponentID component, bool signHide)
+#endif
 #else
 CoeffCodingContext::CoeffCodingContext(const TransformUnit& tu, ComponentID component )
 #endif
@@ -109,6 +113,9 @@ CoeffCodingContext::CoeffCodingContext(const TransformUnit& tu, ComponentID comp
   , m_tsGtxFlagCtxSet           ( Ctx::TsGtxFlag )
 #endif
   , m_sigCoeffGroupFlag         ()
+#if JVET_N0413_RDPCM
+  , m_bdpcm                     (bdpcm)
+#endif
 {
   // LOGTODO
   unsigned log2sizeX = m_log2BlockWidth;
diff --git a/source/Lib/CommonLib/ContextModelling.h b/source/Lib/CommonLib/ContextModelling.h
index f6e4946bfce367a302e5d74c943536d64e1f4079..61b33662de966c3bc89113ba10fbfe277056dd4b 100644
--- a/source/Lib/CommonLib/ContextModelling.h
+++ b/source/Lib/CommonLib/ContextModelling.h
@@ -52,7 +52,11 @@ struct CoeffCodingContext
 {
 public:
 #if HEVC_USE_SIGN_HIDING
+#if JVET_N0413_RDPCM
+  CoeffCodingContext( const TransformUnit& tu, ComponentID component, bool signHide, bool bdpcm = false );
+#else
   CoeffCodingContext( const TransformUnit& tu, ComponentID component, bool signHide);
+#endif
 #else
   CoeffCodingContext( const TransformUnit& tu, ComponentID component );
 #endif
@@ -110,7 +114,9 @@ public:
 #else
   unsigned        sigGroupCtxId   ()                        const { return m_sigGroupCtxId; }
 #endif
-
+#if JVET_N0413_RDPCM
+  bool            bdpcm           ()                        const { return m_bdpcm; }
+#endif
   unsigned sigCtxIdAbs( int scanPos, const TCoeff* coeff, const int state )
   {
     const uint32_t posY      = m_scan[scanPos].y;
@@ -306,6 +312,9 @@ private:
   int                       m_remainingContextBins;
 #endif
   std::bitset<MLS_GRP_NUM>  m_sigCoeffGroupFlag;
+#if JVET_N0413_RDPCM
+  const bool                m_bdpcm;
+#endif
 };
 
 
diff --git a/source/Lib/CommonLib/Contexts.cpp b/source/Lib/CommonLib/Contexts.cpp
index d922e529968d5c0b6aa9ed802f06b8d59f15eefa..4d4ae4d26b80dbbf88643b7a4dab2b187d4adb96 100644
--- a/source/Lib/CommonLib/Contexts.cpp
+++ b/source/Lib/CommonLib/Contexts.cpp
@@ -409,6 +409,16 @@ const CtxSet ContextSetCfg::Mvd = ContextSetCfg::addCtxSet
   { 9, 5, },
 });
 
+#if JVET_N0413_RDPCM
+const CtxSet ContextSetCfg::BDPCMMode = ContextSetCfg::addCtxSet
+({
+  {  CNU, CNU, },
+  {  CNU, CNU, },
+  {  CNU, CNU, },
+  {  DWS, DWS, },
+});
+#endif
+
 const CtxSet ContextSetCfg::QtRootCbf = ContextSetCfg::addCtxSet
 ({
   { 109, },
@@ -419,6 +429,15 @@ const CtxSet ContextSetCfg::QtRootCbf = ContextSetCfg::addCtxSet
 
 const CtxSet ContextSetCfg::QtCbf[] =
 {
+#if JVET_N0413_RDPCM
+  ContextSetCfg::addCtxSet
+  ({
+    {  141, 127, 139, 140, CNU },
+    {  142, 127, 139, 140, CNU },
+    {  CNU, 111, 124, 111, CNU },
+    {    1,   5,   9,   8, DWS },
+  }),
+#else
   ContextSetCfg::addCtxSet
   ({
     { 141, 127, 139, 140, },
@@ -426,6 +445,7 @@ const CtxSet ContextSetCfg::QtCbf[] =
     { CNU, 111, 124, 111, },
     { 1, 5, 9, 8, },
   }),
+#endif
   ContextSetCfg::addCtxSet
   ({
     { 163, 154, CNU, CNU, CNU, },
@@ -870,10 +890,17 @@ const CtxSet ContextSetCfg::TsResidualSign =
 {
   ContextSetCfg::addCtxSet
   ({
+#if JVET_N0413_RDPCM
+    {  CNU, CNU,  },
+    {  CNU, CNU,  },
+    {  CNU, CNU,  },
+    {  DWS, DWS,  },
+#else
     {  CNU,  },
     {  CNU,  },
     {  CNU,  },
     {  DWS,  },
+#endif
    }),
 };
 #endif
diff --git a/source/Lib/CommonLib/Contexts.h b/source/Lib/CommonLib/Contexts.h
index 4cffdab370988809799ef7756ea193cfb201ee8c..d910606f635ea77f47119b08d0ddb82e21006dfd 100644
--- a/source/Lib/CommonLib/Contexts.h
+++ b/source/Lib/CommonLib/Contexts.h
@@ -223,6 +223,9 @@ public:
   static const CtxSet   AffineType;
   static const CtxSet   AffMergeIdx;
   static const CtxSet   Mvd;
+#if JVET_N0413_RDPCM
+  static const CtxSet   BDPCMMode;
+#endif
   static const CtxSet   QtRootCbf;
   static const CtxSet   QtCbf           [3];    // [ channel ]
   static const CtxSet   SigCoeffGroup   [4];    // [ ChannelType ]
diff --git a/source/Lib/CommonLib/IntraPrediction.cpp b/source/Lib/CommonLib/IntraPrediction.cpp
index 7030269b755452f4d17ca7e1f1316648e09b1192..2d9d7613b1b713e751265a27333704f571a0204b 100644
--- a/source/Lib/CommonLib/IntraPrediction.cpp
+++ b/source/Lib/CommonLib/IntraPrediction.cpp
@@ -294,9 +294,11 @@ void IntraPrediction::predIntraAng( const ComponentID compId, PelBuf &piPred, co
   const ChannelType    channelType  = toChannelType( compID );
   const int            iWidth       = piPred.width;
   const int            iHeight      = piPred.height;
-
+#if JVET_N0413_RDPCM
+  const uint32_t       uiDirMode    = isLuma( compId ) && pu.cu->bdpcmMode ? BDPCM_IDX : PU::getFinalIntraMode( pu, channelType );
+#else
   const uint32_t       uiDirMode    = PU::getFinalIntraMode( pu, channelType );
-
+#endif
 
   CHECK( g_aucLog2[iWidth] < 2 && pu.cs->pcv->noChroma2x2, "Size not allowed" );
   CHECK( g_aucLog2[iWidth] > 7, "Size not allowed" );
@@ -315,6 +317,9 @@ void IntraPrediction::predIntraAng( const ComponentID compId, PelBuf &piPred, co
   {
     case(PLANAR_IDX): xPredIntraPlanar(srcBuf, piPred); break;
     case(DC_IDX):     xPredIntraDc(srcBuf, piPred, channelType, false); break;
+#if JVET_N0413_RDPCM
+    case(BDPCM_IDX):  xPredIntraBDPCM(srcBuf, piPred, pu.cu->bdpcmMode, clpRng); break;
+#endif
     default:          xPredIntraAng(srcBuf, piPred, channelType, clpRng); break;
   }
 
@@ -853,6 +858,44 @@ void IntraPrediction::xPredIntraAng( const CPelBuf &pSrc, PelBuf &pDst, const Ch
   }
 }
 
+#if JVET_N0413_RDPCM
+void IntraPrediction::xPredIntraBDPCM(const CPelBuf &pSrc, PelBuf &pDst, const uint32_t dirMode, const ClpRng& clpRng )
+{
+  const int wdt = pDst.width;
+  const int hgt = pDst.height;
+
+  const int strideP = pDst.stride;
+  const int strideS = pSrc.stride;
+
+  CHECK( !( dirMode == 1 || dirMode == 2 ), "Incorrect BDPCM mode parameter." );
+
+  Pel* pred = &pDst.buf[0];
+  if( dirMode == 1 )
+  {
+    Pel  val;
+    for( int y = 0; y < hgt; y++ )
+    {
+      val = pSrc.buf[(y + 1) * strideS];
+      for( int x = 0; x < wdt; x++ )
+      {
+        pred[x] = val;
+      }
+      pred += strideP;
+    }
+  }
+  else
+  {
+    for( int y = 0; y < hgt; y++ )
+    {
+      for( int x = 0; x < wdt; x++ )
+      {
+        pred[x] = pSrc.buf[x + 1];
+      }
+      pred += strideP;
+    }
+  }
+}
+#endif
 
 bool IntraPrediction::useDPCMForFirstPassIntraEstimation(const PredictionUnit &pu, const uint32_t &uiDirMode)
 {
@@ -1349,6 +1392,9 @@ bool IntraPrediction::useFilteredIntraRefSamples( const ComponentID &compID, con
 #else
   if( !isLuma( chType ) && pu.chromaFormat != CHROMA_444 )                                               { return false; }
 #endif
+#if JVET_N0413_RDPCM
+  if(  isLuma( chType ) && pu.cu->bdpcmMode )                                                            { return false; }
+#endif
 
   if( pu.cu->ispMode && isLuma(compID) )                                                                 { return false; }
 
diff --git a/source/Lib/CommonLib/IntraPrediction.h b/source/Lib/CommonLib/IntraPrediction.h
index a2889e3b0978d2aa7aa9fa94c4bb5407f463dd7c..db1a452ad8c8c4f968e8b09645f0a71c7716de8a 100644
--- a/source/Lib/CommonLib/IntraPrediction.h
+++ b/source/Lib/CommonLib/IntraPrediction.h
@@ -121,6 +121,9 @@ protected:
   static bool isIntegerSlope      ( const int absAng ) { return (0 == (absAng & 0x1F)) && absAng <=32; }  //  integer-slope modes 2, DIA_IDX and VDIA_IDX.  "absAng <=32" restricts wide-angle integer modes 
 #endif
 
+#if JVET_N0413_RDPCM
+  void xPredIntraBDPCM            ( const CPelBuf &pSrc, PelBuf &pDst, const uint32_t dirMode, const ClpRng& clpRng );
+#endif
   Pel  xGetPredValDc              ( const CPelBuf &pSrc, const Size &dstSize );
 
   void xFillReferenceSamples      ( const CPelBuf &recoBuf,      Pel* refBufUnfiltered, const CompArea &area, const CodingUnit &cu );
diff --git a/source/Lib/CommonLib/LoopFilter.cpp b/source/Lib/CommonLib/LoopFilter.cpp
index cd0bae703b9f9784254507203dc5154e2c22743f..6941b72138ebed6435b2283f48a6761a4112dc3b 100644
--- a/source/Lib/CommonLib/LoopFilter.cpp
+++ b/source/Lib/CommonLib/LoopFilter.cpp
@@ -749,6 +749,13 @@ unsigned LoopFilter::xGetBoundaryStrengthSingle ( const CodingUnit& cu, const De
   const CodingUnit& cuQ = cu;
   const CodingUnit& cuP = *cu.cs->getCU( posP, cu.chType );
 
+#if JVET_N0413_RDPCM
+  if( ( MODE_INTRA == cuP.predMode && cuP.bdpcmMode ) && ( MODE_INTRA == cuQ.predMode && cuQ.bdpcmMode ) )
+  {
+    return 0;
+  }
+#endif
+
   //-- Set BS for Intra MB : BS = 4 or 3
   if( ( MODE_INTRA == cuP.predMode ) || ( MODE_INTRA == cuQ.predMode ) )
   {
diff --git a/source/Lib/CommonLib/Quant.cpp b/source/Lib/CommonLib/Quant.cpp
index 1b61df49ee3759f6e99437de4596bcd693aaa4a9..907ee11b36e81e2e117540f9445edac50a4ae5f2 100644
--- a/source/Lib/CommonLib/Quant.cpp
+++ b/source/Lib/CommonLib/Quant.cpp
@@ -141,6 +141,82 @@ Quant::~Quant()
 #endif
 }
 
+#if JVET_N0413_RDPCM
+void invResDPCM( const TransformUnit &tu, const ComponentID &compID, CoeffBuf &dstBuf )
+{
+  const CompArea &rect = tu.blocks[compID];
+  const int      wdt = rect.width;
+  const int      hgt = rect.height;
+  const CCoeffBuf coeffs = tu.getCoeffs(compID);
+
+  const TCoeff* coef = &coeffs.buf[0];
+  TCoeff* dst = &dstBuf.buf[0];
+
+  if( tu.cu->bdpcmMode == 1 )
+  {
+    for( int y = 0; y < hgt; y++ )
+    {
+      dst[0] = coef[0];
+      for( int x = 1; x < wdt; x++ )
+      {
+        dst[x] = dst[x - 1] + coef[x];
+      }
+      coef += coeffs.stride;
+      dst += dstBuf.stride;
+    }
+  }
+  else
+  {
+    for( int x = 0; x < wdt; x++ )
+    {
+      dst[x] = coef[x];
+    }
+    for( int y = 0; y < hgt - 1; y++ )
+    {
+      for( int x = 0; x < wdt; x++ )
+      {
+        dst[dstBuf.stride + x] = dst[x] + coef[coeffs.stride + x];
+      }
+      coef += coeffs.stride;
+      dst += dstBuf.stride;
+    }
+  }
+}
+
+void fwdResDPCM( TransformUnit &tu, const ComponentID &compID )
+{
+  const CompArea &rect = tu.blocks[compID];
+  const int      wdt = rect.width;
+  const int      hgt = rect.height;
+  CoeffBuf       coeffs = tu.getCoeffs(compID);
+
+  TCoeff* coef = &coeffs.buf[0];
+
+  if( tu.cu->bdpcmMode == 1 )
+  {
+    for( int y = 0; y < hgt; y++ )
+    {
+      for( int x = wdt - 1; x > 0; x-- )
+      {
+        coef[x] -= coef[x - 1];
+      }
+      coef += coeffs.stride;
+    }
+  }
+  else
+  {
+    coef += coeffs.stride * (hgt - 1);
+    for( int y = 0; y < hgt - 1; y++ )
+    {
+      for ( int x = 0; x < wdt; x++ )
+      {
+        coef[x] -= coef[x - coeffs.stride];
+      }
+      coef -= coeffs.stride;
+    }
+  }
+}
+#endif
 
 #if HEVC_USE_SIGN_HIDING
 // To minimize the distortion only. No rate is considered.
@@ -288,7 +364,9 @@ void Quant::dequant(const TransformUnit &tu,
   const CompArea       &area               = tu.blocks[compID];
   const uint32_t            uiWidth            = area.width;
   const uint32_t            uiHeight           = area.height;
+#if !JVET_N0413_RDPCM
   const TCoeff   *const piQCoef            = tu.getCoeffs(compID).buf;
+#endif
         TCoeff   *const piCoef             = dstCoeff.buf;
   const uint32_t            numSamplesInBlock  = uiWidth * uiHeight;
   const int             maxLog2TrDynamicRange = sps->getMaxLog2TrDynamicRange(toChannelType(compID));
@@ -301,6 +379,19 @@ void Quant::dequant(const TransformUnit &tu,
 #endif
   const int             channelBitDepth    = sps->getBitDepth(toChannelType(compID));
 
+#if JVET_N0413_RDPCM
+  const TCoeff          *coef;
+  if( tu.cu->bdpcmMode && isLuma(compID) )
+  {
+    invResDPCM( tu, compID, dstCoeff );
+    coef = piCoef;
+  }
+  else
+  {
+    coef = tu.getCoeffs(compID).buf;
+  }
+  const TCoeff          *const piQCoef = coef;
+#endif
 #if HEVC_USE_SCALING_LISTS
   CHECK(scalingListType >= SCALING_LIST_NUM, "Invalid scaling list");
 #endif
@@ -853,6 +944,12 @@ void Quant::quant(TransformUnit &tu, const ComponentID &compID, const CCoeffBuf
 
       piQCoef.buf[uiBlockPos] = Clip3<TCoeff>( entropyCodingMinimum, entropyCodingMaximum, quantisedCoefficient );
     } // for n
+#if JVET_N0413_RDPCM
+    if( tu.cu->bdpcmMode && isLuma(compID) )
+    {
+      fwdResDPCM( tu, compID );
+    }
+#endif
 #if HEVC_USE_SIGN_HIDING
     if( cctx.signHiding() && uiWidth>=4 && uiHeight>=4 )
     {
diff --git a/source/Lib/CommonLib/Quant.h b/source/Lib/CommonLib/Quant.h
index 3844bad1aeed32c5d06f5707e88f680bb5cda872..5745269c32d45b7f37cdb1ff2a1379756fd5430d 100644
--- a/source/Lib/CommonLib/Quant.h
+++ b/source/Lib/CommonLib/Quant.h
@@ -58,6 +58,13 @@
 // ====================================================================================================================
 // Class definition
 // ====================================================================================================================
+#if JVET_N0413_RDPCM
+struct TrQuantParams
+{
+  int     rightShift;
+  int     qScale;
+};
+#endif
 
 /// QP struct
 struct QpParam
diff --git a/source/Lib/CommonLib/QuantRDOQ.cpp b/source/Lib/CommonLib/QuantRDOQ.cpp
index 286e285b249e5fe5bf16a47f36d993376b77e155..86fb173e60d5ad329dfe7cc1d3a7561d6af4fd63 100644
--- a/source/Lib/CommonLib/QuantRDOQ.cpp
+++ b/source/Lib/CommonLib/QuantRDOQ.cpp
@@ -579,7 +579,18 @@ void QuantRDOQ::quant(TransformUnit &tu, const ComponentID &compID, const CCoeff
 #if JVET_N0280_RESIDUAL_CODING_TS
       if( isLuma( compID ) && useTransformSkip )
       {
+#if JVET_N0413_RDPCM
+        if( tu.cu->bdpcmMode && isLuma(compID) )
+        {
+          forwardRDPCM( tu, compID, pSrc, uiAbsSum, cQP, ctx );
+        }
+        else
+        {
+          xRateDistOptQuantTS( tu, compID, pSrc, uiAbsSum, cQP, ctx );
+        }
+#else
         xRateDistOptQuantTS( tu, compID, pSrc, uiAbsSum, cQP, ctx );
+#endif
       }
       else
       {
@@ -1487,6 +1498,247 @@ void QuantRDOQ::xRateDistOptQuantTS( TransformUnit &tu, const ComponentID &compI
   }
 }
 
+#if JVET_N0413_RDPCM
+void QuantRDOQ::forwardRDPCM( TransformUnit &tu, const ComponentID &compID, const CCoeffBuf &coeffs, TCoeff &absSum, const QpParam &qp, const Ctx &ctx )
+{
+  const FracBitsAccess& fracBits = ctx.getFracBitsAcess();
+
+  const SPS &sps = *tu.cs->sps;
+  const CompArea &rect = tu.blocks[compID];
+  const uint32_t width = rect.width;
+  const uint32_t height = rect.height;
+  const ChannelType chType = toChannelType(compID);
+  const int channelBitDepth = sps.getBitDepth(chType);
+
+  const bool extendedPrecision = sps.getSpsRangeExtension().getExtendedPrecisionProcessingFlag();
+  const int  maxLog2TrDynamicRange = sps.getMaxLog2TrDynamicRange(chType);
+  const int  dirMode = tu.cu->bdpcmMode;
+
+  int transformShift = getTransformShift(channelBitDepth, rect.size(), maxLog2TrDynamicRange);
+
+  if (extendedPrecision)
+  {
+    transformShift = std::max<int>(0, transformShift);
+  }
+
+  double   blockUncodedCost = 0;
+#if HEVC_USE_SCALING_LISTS
+  const uint32_t log2BlockHeight = g_aucLog2[height];
+#endif
+  const uint32_t maxNumCoeff = rect.area();
+
+  CHECK(compID >= MAX_NUM_TBLOCKS, "Invalid component ID");
+
+#if HEVC_USE_SCALING_LISTS
+  int scalingListType = getScalingListType(tu.cu->predMode, compID);
+  CHECK(scalingListType >= SCALING_LIST_NUM, "Invalid scaling list");
+#endif
+
+  const TCoeff *srcCoeff = coeffs.buf;
+  TCoeff *dstCoeff = tu.getCoeffs(compID).buf;
+
+  double *costCoeff = m_pdCostCoeff;
+  double *costSig = m_pdCostSig;
+  double *costCoeff0 = m_pdCostCoeff0;
+
+  memset(m_pdCostCoeff, 0, sizeof(double) *  maxNumCoeff);
+  memset(m_pdCostSig, 0, sizeof(double) *  maxNumCoeff);
+  memset(m_fullCoeff, 0, sizeof(TCoeff) * maxNumCoeff);
+
+#if JVET_N0246_MODIFIED_QUANTSCALES
+  const bool   needsSqrt2Scale = TU::needsSqrt2Scale(tu, compID);  // should always be false - transform-skipped blocks don't require sqrt(2) compensation.
+  const int    qBits = QUANT_SHIFT + qp.per + transformShift + (needsSqrt2Scale ? -1 : 0);  // Right shift of non-RDOQ quantizer;  level = (coeff*uiQ + offset)>>q_bits
+  const int    quantisationCoefficient = g_quantScales[needsSqrt2Scale ? 1 : 0][qp.rem];
+  const double errorScale = xGetErrScaleCoeff(TU::needsSqrt2Scale(tu, compID), width, height, qp.rem, maxLog2TrDynamicRange, channelBitDepth);
+
+  TrQuantParams trQuantParams;
+  trQuantParams.rightShift = (IQUANT_SHIFT - (transformShift + qp.per));
+  trQuantParams.qScale = g_invQuantScales[needsSqrt2Scale ? 1 : 0][qp.rem];
+#else
+
+  const int qBits = QUANT_SHIFT + qp.per + transformShift;                   // Right shift of non-RDOQ quantizer;  level = (coeff*uiQ + offset)>>q_bits
+
+#if HM_QTBT_AS_IN_JEM_QUANT
+  const int    quantisationCoefficient = (TU::needsSqrt2Scale(tu, compID) ? (g_quantScales[qp.rem] * 181) >> 7 : g_quantScales[qp.rem]);
+  const double errorScale = xGetErrScaleCoeff(TU::needsSqrt2Scale(tu, compID), width, height, qp.rem, maxLog2TrDynamicRange, channelBitDepth);
+#else
+  const double blkErrScale = (TU::needsQP3Offset(tu, compID) ? 2.0 : 1.0);
+  const int    quantisationCoefficient = g_quantScales[qp.rem];
+  const double errorScale = blkErrScale * xGetErrScaleCoeff(width, height, qp.rem, maxLog2TrDynamicRange, channelBitDepth);
+#endif
+#endif
+
+  const TCoeff entropyCodingMaximum = (1 << maxLog2TrDynamicRange) - 1;
+
+#if HEVC_USE_SIGN_HIDING
+  CoeffCodingContext cctx(tu, compID, tu.cs->slice->getSignDataHidingEnabledFlag());
+#else
+  CoeffCodingContext cctx(tu, compID);
+#endif
+  const int sbSizeM1 = (1 << cctx.log2CGSize()) - 1;
+  double    baseCost = 0;
+  uint32_t  goRiceParam = 0;
+
+  double *costSigSubBlock = m_pdCostCoeffGroupSig;
+  memset(costSigSubBlock, 0, (maxNumCoeff >> cctx.log2CGSize()) * sizeof(double));
+
+  const int sbNum = width * height >> cctx.log2CGSize();
+  int scanPos;
+  coeffGroupRDStats rdStats;
+
+  bool anySigCG = false;
+
+  for (int sbId = 0; sbId < sbNum; sbId++)
+  {
+    cctx.initSubblock(sbId);
+
+    memset(&rdStats, 0, sizeof(coeffGroupRDStats));
+
+    for (int scanPosInSB = 0; scanPosInSB <= sbSizeM1; scanPosInSB++)
+    {
+      scanPos = cctx.minSubPos() + scanPosInSB;
+      //===== quantization =====
+      uint32_t blkPos = cctx.blockPos(scanPos);
+
+      const int posX = cctx.posX(scanPos);
+      const int posY = cctx.posY(scanPos);
+      const int posS = (1 == dirMode) ? posX : posY;
+      const int posNb = (1 == dirMode) ? (posX - 1) + posY * coeffs.stride : posX + (posY - 1) * coeffs.stride;
+      TCoeff predCoeff = (0 != posS) ? m_fullCoeff[posNb] : 0;
+
+      // set coeff
+      const int64_t          tmpLevel = int64_t(abs(srcCoeff[blkPos] - predCoeff)) * quantisationCoefficient;
+      const Intermediate_Int levelDouble = (Intermediate_Int)std::min<int64_t>(tmpLevel, std::numeric_limits<Intermediate_Int>::max() - (Intermediate_Int(1) << (qBits - 1)));
+      uint32_t         maxAbsLevel = std::min<uint32_t>(uint32_t(entropyCodingMaximum), uint32_t((levelDouble + (Intermediate_Int(1) << (qBits - 1))) >> qBits));
+
+      const double err = double(levelDouble);
+      costCoeff0[scanPos] = err * err * errorScale;
+      blockUncodedCost += costCoeff0[scanPos];
+      dstCoeff[blkPos] = maxAbsLevel;
+
+      //===== coefficient level estimation =====
+      unsigned    ctxIdSig = cctx.sigCtxIdAbsTS(scanPos, dstCoeff);
+      uint32_t    cLevel;
+      const BinFracBits fracBitsPar = fracBits.getFracBitsArray(cctx.parityCtxIdAbsTS());
+
+      goRiceParam = cctx.templateAbsSumTS(scanPos, dstCoeff);
+      const BinFracBits fracBitsSign = fracBits.getFracBitsArray(Ctx::TsResidualSign(toChannelType(compID)));
+      const uint8_t     sign = srcCoeff[blkPos] - predCoeff < 0 ? 1 : 0;
+
+      DTRACE_COND((maxAbsLevel != 0), g_trace_ctx, D_RDOQ_MORE, " uiCtxSig=%d", ctxIdSig);
+
+      const BinFracBits fracBitsSig = fracBits.getFracBitsArray(ctxIdSig);
+      cLevel = xGetCodedLevelTS(costCoeff[scanPos], costCoeff0[scanPos], costSig[scanPos],
+        levelDouble, maxAbsLevel, &fracBitsSig, fracBitsPar, cctx, fracBits, fracBitsSign, sign, goRiceParam, qBits, errorScale, 0, extendedPrecision, maxLog2TrDynamicRange);
+      dstCoeff[blkPos] = cLevel;
+
+      if (sign)
+      {
+        dstCoeff[blkPos] = -dstCoeff[blkPos];
+      }
+      xDequantSample( m_fullCoeff[blkPos], dstCoeff[blkPos], trQuantParams );
+      m_fullCoeff[blkPos] += predCoeff;
+
+      baseCost += costCoeff[scanPos];
+      rdStats.d64SigCost += costSig[scanPos];
+
+      if (scanPosInSB == 0)
+      {
+        rdStats.d64SigCost_0 = costSig[scanPos];
+      }
+      if (dstCoeff[blkPos])
+      {
+        cctx.setSigGroup();
+        rdStats.d64CodedLevelandDist += costCoeff[scanPos] - costSig[scanPos];
+        rdStats.d64UncodedDist += costCoeff0[scanPos];
+        if (scanPosInSB != 0)
+        {
+          rdStats.iNNZbeforePos0++;
+        }
+      }
+    } //end for (iScanPosinCG)
+
+    if (!cctx.isSigGroup())
+    {
+      const BinFracBits fracBitsSigGroup = fracBits.getFracBitsArray(cctx.sigGroupCtxId(true));
+      baseCost += xGetRateSigCoeffGroup(fracBitsSigGroup, 0) - rdStats.d64SigCost;
+      costSigSubBlock[cctx.subSetId()] = xGetRateSigCoeffGroup(fracBitsSigGroup, 0);
+    }
+    else if (sbId != sbSizeM1 || anySigCG)
+    {
+      if (rdStats.iNNZbeforePos0 == 0)
+      {
+        baseCost -= rdStats.d64SigCost_0;
+        rdStats.d64SigCost -= rdStats.d64SigCost_0;
+      }
+      // rd-cost if SigCoeffGroupFlag = 0, initialization
+      double costZeroSB = baseCost;
+
+      const BinFracBits fracBitsSigGroup = fracBits.getFracBitsArray(cctx.sigGroupCtxId(true));
+
+      baseCost += xGetRateSigCoeffGroup(fracBitsSigGroup, 1);
+      costZeroSB += xGetRateSigCoeffGroup(fracBitsSigGroup, 0);
+      costSigSubBlock[cctx.subSetId()] = xGetRateSigCoeffGroup(fracBitsSigGroup, 1);
+
+      costZeroSB += rdStats.d64UncodedDist;         // distortion for resetting non-zero levels to zero levels
+      costZeroSB -= rdStats.d64CodedLevelandDist;   // distortion and level cost for keeping all non-zero levels
+      costZeroSB -= rdStats.d64SigCost;             // sig cost for all coeffs, including zero levels and non-zerl levels
+
+      if (costZeroSB < baseCost)
+      {
+        cctx.resetSigGroup();
+        baseCost = costZeroSB;
+        costSigSubBlock[cctx.subSetId()] = xGetRateSigCoeffGroup(fracBitsSigGroup, 0);
+
+        for (int scanPosInSB = 0; scanPosInSB < sbSizeM1; scanPosInSB++)
+        {
+          scanPos = cctx.minSubPos() + scanPosInSB;
+          uint32_t blkPos = cctx.blockPos(scanPos);
+
+          const int posX = cctx.posX(scanPos);
+          const int posY = cctx.posY(scanPos);
+          const int posS = (1 == dirMode) ? posX : posY;
+          const int posNb = (1 == dirMode) ? (posX - 1) + posY * coeffs.stride : posX + (posY - 1) * coeffs.stride;
+          m_fullCoeff[scanPos] = (0 != posS) ? m_fullCoeff[posNb] : 0;
+
+          if (dstCoeff[blkPos])
+          {
+            dstCoeff[blkPos] = 0;
+            costCoeff[scanPos] = costCoeff0[scanPos];
+            costSig[scanPos] = 0;
+          }
+        }
+      }
+      else
+      {
+        anySigCG = true;
+      }
+    }
+  }
+
+  //===== estimate last position =====
+  for (int scanPos = 0; scanPos < maxNumCoeff; scanPos++)
+  {
+    int blkPos = cctx.blockPos(scanPos);
+    TCoeff level = dstCoeff[blkPos];
+    absSum += level;
+  }
+}
+
+void QuantRDOQ::xDequantSample(TCoeff& pRes, TCoeff& coeff, const TrQuantParams& trQuantParams)
+{
+  // xDequant
+  if (trQuantParams.rightShift > 0)
+  {
+    const Intermediate_Int qAdd = Intermediate_Int(1) << (trQuantParams.rightShift - 1);
+    pRes = TCoeff((Intermediate_Int(coeff) * trQuantParams.qScale + qAdd) >> trQuantParams.rightShift);
+  }
+  else
+  {
+    pRes = TCoeff((Intermediate_Int(coeff) * trQuantParams.qScale) << -trQuantParams.rightShift);
+  }
+}
+#endif
 inline uint32_t QuantRDOQ::xGetCodedLevelTS(       double&             codedCost,
                                                    double&             codedCost0,
                                                    double&             codedCostSig,
diff --git a/source/Lib/CommonLib/QuantRDOQ.h b/source/Lib/CommonLib/QuantRDOQ.h
index 7077e6ef426a37f3001aad689e4a2ca1fa615105..128f47aa306ed603393c7eba1d5e79500fa766b3 100644
--- a/source/Lib/CommonLib/QuantRDOQ.h
+++ b/source/Lib/CommonLib/QuantRDOQ.h
@@ -67,6 +67,9 @@ public:
 #endif
   // quantization
   void quant                ( TransformUnit &tu, const ComponentID &compID, const CCoeffBuf &pSrc, TCoeff &uiAbsSum, const QpParam &cQP, const Ctx& ctx );
+#if JVET_N0413_RDPCM
+  void forwardRDPCM         ( TransformUnit &tu, const ComponentID &compID, const CCoeffBuf &pSrc, TCoeff &uiAbsSum, const QpParam &cQP, const Ctx &ctx );
+#endif
 
 private:
 #if HEVC_USE_SCALING_LISTS
@@ -78,7 +81,9 @@ private:
 #else
   double  xGetErrScaleCoeff              ( const bool needsSqrt2, SizeType width, SizeType height, int qp, const int maxLog2TrDynamicRange, const int channelBitDepth);
 #endif
-
+#if JVET_N0413_RDPCM
+  void    xDequantSample                 ( TCoeff& pRes, TCoeff& coeff, const TrQuantParams& trQuantParams );
+#endif
   // RDOQ functions
   void xRateDistOptQuant(TransformUnit &tu, const ComponentID &compID, const CCoeffBuf &pSrc, TCoeff &uiAbsSum, const QpParam &cQP, const Ctx &ctx);
 
@@ -168,6 +173,9 @@ private:
   int    m_rateIncDown        [MAX_TB_SIZEY * MAX_TB_SIZEY];
   int    m_sigRateDelta       [MAX_TB_SIZEY * MAX_TB_SIZEY];
   TCoeff m_deltaU             [MAX_TB_SIZEY * MAX_TB_SIZEY];
+#if JVET_N0413_RDPCM
+  TCoeff m_fullCoeff          [MAX_TB_SIZEY * MAX_TB_SIZEY];
+#endif
 #endif
 };// END CLASS DEFINITION QuantRDOQ
 
diff --git a/source/Lib/CommonLib/TrQuant.cpp b/source/Lib/CommonLib/TrQuant.cpp
index e84d183b5fbb3b98a3445416219da4607173ac90..6f6a684818e3786383e5cb25346595252196313f 100644
--- a/source/Lib/CommonLib/TrQuant.cpp
+++ b/source/Lib/CommonLib/TrQuant.cpp
@@ -678,6 +678,13 @@ void TrQuant::transformNxN( TransformUnit &tu, const ComponentID &compID, const
   RDPCMMode rdpcmMode = RDPCM_OFF;
   rdpcmNxN(tu, compID, cQP, uiAbsSum, rdpcmMode);
 
+#if JVET_N0413_RDPCM
+  if( tu.cu->bdpcmMode && isLuma(compID) )
+  {
+    tu.mtsIdx = 1;
+  }
+#endif
+
   if (rdpcmMode == RDPCM_OFF)
   {
     uiAbsSum = 0;
diff --git a/source/Lib/CommonLib/TypeDef.h b/source/Lib/CommonLib/TypeDef.h
index acd80e6141f3d32df6cabfc77540bf368ca2715b..99157289830a335c0d5b9b6ab9fe5152f08c0522 100644
--- a/source/Lib/CommonLib/TypeDef.h
+++ b/source/Lib/CommonLib/TypeDef.h
@@ -50,6 +50,8 @@
 #include <assert.h>
 #include <cassert>
 
+#define JVET_N0413_RDPCM                                  1 // Residual DPCM JVET-N0413/N0214
+
 #define JVET_N0866_UNIF_TRFM_SEL_IMPL_MTS_ISP             1 // JVET-N0866: unified transform derivation for ISP and implicit MTS (combining JVET-N0172, JVET-N0375, JVET-N0419 and JVET-N0420)
 
 #define JVET_N0340_TRI_MERGE_CAND                         1
diff --git a/source/Lib/CommonLib/Unit.cpp b/source/Lib/CommonLib/Unit.cpp
index 739c4518d62332f0d16ba5f07a14ee0b6b1f636d..7a1745d5a12f73d92eedb7606eab598bc73686e8 100644
--- a/source/Lib/CommonLib/Unit.cpp
+++ b/source/Lib/CommonLib/Unit.cpp
@@ -267,6 +267,9 @@ CodingUnit& CodingUnit::operator=( const CodingUnit& other )
   affineType        = other.affineType;
   triangle          = other.triangle;
   transQuantBypass  = other.transQuantBypass;
+#if JVET_N0413_RDPCM
+  bdpcmMode         = other.bdpcmMode;
+#endif
   ipcm              = other.ipcm;
   qp                = other.qp;
   chromaQpAdj       = other.chromaQpAdj;
@@ -300,6 +303,9 @@ void CodingUnit::initData()
   affineType        = 0;
   triangle          = false;
   transQuantBypass  = false;
+#if JVET_N0413_RDPCM
+  bdpcmMode         = 0;
+#endif
   ipcm              = false;
   qp                = 0;
   chromaQpAdj       = 0;
diff --git a/source/Lib/CommonLib/Unit.h b/source/Lib/CommonLib/Unit.h
index 571354693291ee7dbf6c7f214c473b6c0b0c224f..d11399eb9eca2e6b923cfca15012d2c4afe8af1e 100644
--- a/source/Lib/CommonLib/Unit.h
+++ b/source/Lib/CommonLib/Unit.h
@@ -305,6 +305,9 @@ struct CodingUnit : public UnitArea
   int            affineType;
   bool           triangle;
   bool           transQuantBypass;
+#if JVET_N0413_RDPCM
+  int            bdpcmMode;
+#endif
   bool           ipcm;
   uint8_t          imv;
   bool           rootCbf;
diff --git a/source/Lib/CommonLib/UnitTools.cpp b/source/Lib/CommonLib/UnitTools.cpp
index 321523089cb56ea7c0f392f0872df4bdb3965204..32718df43e39ce6198f39f6147c021394d6a4ad0 100644
--- a/source/Lib/CommonLib/UnitTools.cpp
+++ b/source/Lib/CommonLib/UnitTools.cpp
@@ -5462,6 +5462,16 @@ uint8_t CU::deriveGbiIdx( uint8_t gbiLO, uint8_t gbiL1 )
   }
 }
 
+#if JVET_N0413_RDPCM
+bool CU::bdpcmAllowed( const CodingUnit& cu, const ComponentID compID )
+{
+  bool bdpcmAllowed = compID == COMPONENT_Y;
+       bdpcmAllowed &= CU::isIntra( cu );
+       bdpcmAllowed &= ( cu.lwidth() <= 32 && cu.lheight() <= 32 );
+
+  return bdpcmAllowed;
+}
+#endif
 // TU tools
 
 bool TU::isNonTransformedResidualRotated(const TransformUnit &tu, const ComponentID &compID)
@@ -5495,7 +5505,9 @@ bool TU::isTSAllowed(const TransformUnit &tu, const ComponentID compID)
   tsAllowed &= tu.cs->pps->getUseTransformSkip();
   tsAllowed &= !tu.cu->transQuantBypass;
   tsAllowed &= ( !tu.cu->ispMode || !isLuma(compID) );
-
+#if JVET_N0413_RDPCM
+  tsAllowed &= !( tu.cu->bdpcmMode && tu.lwidth() <= BDPCM_MAX_CU_SIZE && tu.lheight() <= BDPCM_MAX_CU_SIZE );
+#endif
   SizeType transformSkipMaxSize = 1 << maxSize;
   tsAllowed &= tu.lwidth() <= transformSkipMaxSize && tu.lheight() <= transformSkipMaxSize;
   tsAllowed &= !tu.cu->sbtInfo;
@@ -5512,6 +5524,9 @@ bool TU::isMTSAllowed(const TransformUnit &tu, const ComponentID compID)
   mtsAllowed &= ( tu.lwidth() <= maxSize && tu.lheight() <= maxSize );
   mtsAllowed &= !tu.cu->ispMode;
   mtsAllowed &= !tu.cu->sbtInfo;
+#if JVET_N0413_RDPCM
+  mtsAllowed &= !( tu.cu->bdpcmMode && tu.lwidth() <= BDPCM_MAX_CU_SIZE && tu.lheight() <= BDPCM_MAX_CU_SIZE );
+#endif
   return mtsAllowed;
 }
 
diff --git a/source/Lib/CommonLib/UnitTools.h b/source/Lib/CommonLib/UnitTools.h
index 89815e85b31aa4f45619e336477942f3f29efdf3..902f8bbbc3ca474b182ef85c52bb0de4d412ccab 100644
--- a/source/Lib/CommonLib/UnitTools.h
+++ b/source/Lib/CommonLib/UnitTools.h
@@ -84,6 +84,10 @@ namespace CU
   uint8_t getValidGbiIdx              (const CodingUnit& cu);
   void  setGbiIdx                     (CodingUnit& cu, uint8_t uh);
   uint8_t deriveGbiIdx                (uint8_t gbiLO, uint8_t gbiL1);
+#if JVET_N0413_RDPCM
+  bool bdpcmAllowed                   (const CodingUnit& cu, const ComponentID compID);
+#endif
+
 
   bool      divideTuInRows            ( const CodingUnit &cu );
   bool      firstTestISPHorSplit      ( const int width, const int height,            const ComponentID compID, const CodingUnit *cuLeft = nullptr, const CodingUnit *cuAbove = nullptr );
diff --git a/source/Lib/DecoderLib/CABACReader.cpp b/source/Lib/DecoderLib/CABACReader.cpp
index 535decc8ea6bca74899a495ba63c548d4d508c0f..37dd601d70d5631abd012012c12bf87fdd18fed9 100644
--- a/source/Lib/DecoderLib/CABACReader.cpp
+++ b/source/Lib/DecoderLib/CABACReader.cpp
@@ -681,6 +681,9 @@ bool CABACReader::coding_unit( CodingUnit &cu, Partitioner &partitioner, CUCtx&
 
   // prediction mode and partitioning data
   pred_mode ( cu );
+#if JVET_N0413_RDPCM
+  bdpcm_mode( cu, ComponentID( partitioner.chType ) );
+#endif
 
   // --> create PUs
 #if !JVET_N0324_REGULAR_MRG_FLAG
@@ -1031,7 +1034,23 @@ void CABACReader::pred_mode( CodingUnit& cu )
     }
   }
 }
+#if JVET_N0413_RDPCM
+void CABACReader::bdpcm_mode( CodingUnit& cu, const ComponentID compID )
+{
+  cu.bdpcmMode = 0;
+
+  if( !CU::bdpcmAllowed( cu, compID ) ) return;
 
+  cu.bdpcmMode = m_BinDecoder.decodeBin( Ctx::BDPCMMode( 0 ) );
+
+  if( cu.bdpcmMode )
+  {
+    cu.bdpcmMode += m_BinDecoder.decodeBin( Ctx::BDPCMMode( 1 ) );
+  }
+
+  DTRACE( g_trace_ctx, D_SYNTAX, "bdpcm_mode() x=%d, y=%d, w=%d, h=%d, bdpcm=%d\n", cu.lumaPos().x, cu.lumaPos().y, cu.lwidth(), cu.lheight(), cu.bdpcmMode );
+}
+#endif
 void CABACReader::pcm_flag( CodingUnit& cu, Partitioner &partitioner )
 {
   const SPS& sps = *cu.cs->sps;
@@ -1162,8 +1181,11 @@ void CABACReader::extend_ref_line(CodingUnit& cu)
 #if !ENABLE_JVET_L0283_MRL
   return;
 #endif
-
+#if JVET_N0413_RDPCM
+  if ( !cu.Y().valid() || cu.predMode != MODE_INTRA || !isLuma(cu.chType) || cu.ipcm || cu.bdpcmMode )
+#else
   if (!cu.Y().valid() || cu.predMode != MODE_INTRA || !isLuma(cu.chType) || cu.ipcm)
+#endif
   {
     cu.firstPU->multiRefIdx = 0;
     return;
@@ -1208,6 +1230,16 @@ void CABACReader::intra_luma_pred_modes( CodingUnit &cu )
     return;
   }
 
+#if JVET_N0413_RDPCM
+  if( cu.bdpcmMode )
+  {
+    PredictionUnit *pu = cu.firstPU;
+    unsigned mpm_pred[NUM_MOST_PROBABLE_MODES];
+    PU::getIntraMPMs(*pu, mpm_pred);
+    cu.firstPU->intraDir[0] = mpm_pred[0];
+    return;
+  }
+#endif
   RExt__DECODER_DEBUG_BIT_STATISTICS_CREATE_SET_SIZE2( STATS__CABAC_BITS__INTRA_DIR_ANG, cu.lumaSize(), CHANNEL_TYPE_LUMA );
 
   // prev_intra_luma_pred_flag
@@ -2411,7 +2443,19 @@ bool CABACReader::cbf_comp( CodingStructure& cs, const CompArea& area, unsigned
 
   RExt__DECODER_DEBUG_BIT_STATISTICS_CREATE_SET_SIZE2(STATS__CABAC_BITS__QT_CBF, area.size(), area.compID);
 
+#if JVET_N0413_RDPCM
+  unsigned  cbf = 0;
+  if( area.compID == COMPONENT_Y && cs.getCU( area.pos(), ChannelType( area.compID ) )->bdpcmMode )
+  {
+    cbf = m_BinDecoder.decodeBin( ctxSet( 4 ) );
+  }
+  else
+  {
+    cbf = m_BinDecoder.decodeBin( ctxSet( ctxId ) );
+  }
+#else
   const unsigned  cbf = m_BinDecoder.decodeBin( ctxSet( ctxId ) );
+#endif
 
   DTRACE( g_trace_ctx, D_SYNTAX, "cbf_comp() etype=%d pos=(%d,%d) ctx=%d cbf=%d\n", area.compID, area.x, area.y, ctxId, cbf );
   return cbf;
@@ -2611,7 +2655,11 @@ void CABACReader::residual_coding( TransformUnit& tu, ComponentID compID )
   explicit_rdpcm_mode( tu, compID );
 
 #if JVET_N0280_RESIDUAL_CODING_TS
+#if JVET_N0413_RDPCM
+  if( isLuma( compID ) && ( tu.mtsIdx == 1 || tu.cu->bdpcmMode ) )
+#else
   if( isLuma( compID ) && tu.mtsIdx == 1 )
+#endif
   {
     residual_codingTS( tu, compID );
     return;
@@ -2669,6 +2717,9 @@ void CABACReader::mts_coding( TransformUnit& tu, ComponentID compID )
   const bool  tsAllowed = TU::isTSAllowed ( tu, compID );
   const bool mtsAllowed = TU::isMTSAllowed( tu, compID );
 
+#if JVET_N0413_RDPCM
+  if( tu.cu->bdpcmMode ) tu.mtsIdx = 1;
+#endif
   if( !mtsAllowed && !tsAllowed ) return;
 
   RExt__DECODER_DEBUG_BIT_STATISTICS_CREATE_SET_SIZE2( STATS__CABAC_BITS__MTS_FLAGS, tu.blocks[compID], compID );
@@ -2713,9 +2764,17 @@ void CABACReader::mts_coding( TransformUnit& tu, ComponentID compID )
 void CABACReader::isp_mode( CodingUnit& cu )
 {
 #if INCLUDE_ISP_CFG_FLAG
+#if JVET_N0413_RDPCM
+  if( !CU::isIntra( cu ) || !isLuma( cu.chType ) || cu.firstPU->multiRefIdx || cu.ipcm || !cu.cs->sps->getUseISP() || cu.bdpcmMode )
+#else
   if( !CU::isIntra( cu ) || !isLuma( cu.chType ) || cu.firstPU->multiRefIdx || cu.ipcm || !cu.cs->sps->getUseISP() )
+#endif
+#else
+#if JVET_N0413_RDPCM
+  if( !CU::isIntra( cu ) || !isLuma( cu.chType ) || cu.firstPU->multiRefIdx || cu.ipcm || cu.bdpcmMode )
 #else
   if( !CU::isIntra( cu ) || !isLuma( cu.chType ) || cu.firstPU->multiRefIdx || cu.ipcm )
+#endif
 #endif
   {
     cu.ispMode = NOT_INTRA_SUBPARTITIONS;
@@ -3038,7 +3097,11 @@ void CABACReader::residual_codingTS( TransformUnit& tu, ComponentID compID )
   DTRACE( g_trace_ctx, D_SYNTAX, "residual_codingTS() etype=%d pos=(%d,%d) size=%dx%d\n", tu.blocks[compID].compID, tu.blocks[compID].x, tu.blocks[compID].y, tu.blocks[compID].width, tu.blocks[compID].height );
 
   // init coeff coding context
+#if JVET_N0413_RDPCM
+  CoeffCodingContext  cctx    ( tu, compID, false, tu.cu->bdpcmMode );
+#else
   CoeffCodingContext  cctx    ( tu, compID, false );
+#endif
   TCoeff*             coeff   = tu.getCoeffs( compID ).buf;
 
   cctx.setNumCtxBins( 2 * tu.lwidth()*tu.lheight() );
@@ -3125,7 +3188,11 @@ void CABACReader::residual_coding_subblockTS( CoeffCodingContext& cctx, TCoeff*
       int sign;
       if( cctx.isContextCoded() )
       {
+#if JVET_N0413_RDPCM
+        sign = m_BinDecoder.decodeBin( Ctx::TsResidualSign(  cctx.bdpcm() ? 1 : 0 ) );
+#else
         sign = m_BinDecoder.decodeBin( Ctx::TsResidualSign( toChannelType( cctx.compID() ) ) );
+#endif
       }
       else
       {
diff --git a/source/Lib/DecoderLib/CABACReader.h b/source/Lib/DecoderLib/CABACReader.h
index c8dd4878140edec5ea0e524119281a6cbf20d44b..4e651c8eaabe1e7942318d389f21b8bfd665bf84 100644
--- a/source/Lib/DecoderLib/CABACReader.h
+++ b/source/Lib/DecoderLib/CABACReader.h
@@ -77,6 +77,9 @@ public:
   void        cu_transquant_bypass_flag ( CodingUnit&                   cu );
   void        cu_skip_flag              ( CodingUnit&                   cu );
   void        pred_mode                 ( CodingUnit&                   cu );
+#if JVET_N0413_RDPCM
+  void        bdpcm_mode                ( CodingUnit&                   cu,     const ComponentID compID );
+#endif
   void        pcm_flag                  ( CodingUnit&                   cu,     Partitioner&    pm );
   void        cu_pred_data              ( CodingUnit&                   cu );
   void        cu_gbi_flag               ( CodingUnit&                   cu );
diff --git a/source/Lib/EncoderLib/CABACWriter.cpp b/source/Lib/EncoderLib/CABACWriter.cpp
index 34171b4bd0a872bf95660850781a2f193c13e662..82416b3603648dba4a95f550804adbc18533a8ff 100644
--- a/source/Lib/EncoderLib/CABACWriter.cpp
+++ b/source/Lib/EncoderLib/CABACWriter.cpp
@@ -595,6 +595,9 @@ void CABACWriter::coding_unit( const CodingUnit& cu, Partitioner& partitioner, C
 
   // prediction mode and partitioning data
   pred_mode ( cu );
+#if JVET_N0413_RDPCM
+  bdpcm_mode( cu, ComponentID( partitioner.chType ) );
+#endif
 
 #if FIX_PCM
   // pcm samples
@@ -807,7 +810,20 @@ void CABACWriter::pred_mode( const CodingUnit& cu )
     m_BinEncoder.encodeBin((CU::isIntra(cu)), Ctx::PredMode(DeriveCtx::CtxPredModeFlag(cu)));
   }
 }
+#if JVET_N0413_RDPCM
+void CABACWriter::bdpcm_mode( const CodingUnit& cu, const ComponentID compID )
+{
+  if( !CU::bdpcmAllowed( cu, compID ) ) return;
+
+  m_BinEncoder.encodeBin( cu.bdpcmMode > 0 ? 1 : 0, Ctx::BDPCMMode( 0 ) );
 
+  if( cu.bdpcmMode )
+  {
+    m_BinEncoder.encodeBin( cu.bdpcmMode > 1 ? 1 : 0, Ctx::BDPCMMode( 1 ) );
+  }
+  DTRACE( g_trace_ctx, D_SYNTAX, "bdpcm_mode() x=%d, y=%d, w=%d, h=%d, bdpcm=%d\n", cu.lumaPos().x, cu.lumaPos().y, cu.lwidth(), cu.lheight(), cu.bdpcmMode );
+}
+#endif
 void CABACWriter::pcm_data( const CodingUnit& cu, Partitioner& partitioner  )
 {
   pcm_flag( cu, partitioner );
@@ -949,7 +965,11 @@ void CABACWriter::extend_ref_line(const PredictionUnit& pu)
 #endif
 
   const CodingUnit& cu = *pu.cu;
+#if JVET_N0413_RDPCM
+  if( !cu.Y().valid() || cu.predMode != MODE_INTRA || !isLuma( cu.chType ) || cu.bdpcmMode )
+#else
   if (!cu.Y().valid() || cu.predMode != MODE_INTRA || !isLuma(cu.chType))
+#endif
   {
     return;
   }
@@ -979,7 +999,11 @@ void CABACWriter::extend_ref_line(const CodingUnit& cu)
   return;
 #endif
 
+#if JVET_N0413_RDPCM
+  if ( !cu.Y().valid() || cu.predMode != MODE_INTRA || !isLuma(cu.chType) || cu.ipcm || cu.bdpcmMode )
+#else
   if (!cu.Y().valid() || cu.predMode != MODE_INTRA || !isLuma(cu.chType) || cu.ipcm)
+#endif
   {
     return;
   }
@@ -1019,6 +1043,17 @@ void CABACWriter::intra_luma_pred_modes( const CodingUnit& cu )
     return;
   }
 
+#if JVET_N0413_RDPCM
+  if( cu.bdpcmMode )
+  {
+    PredictionUnit *pu = cu.firstPU;
+    unsigned mpm_pred[NUM_MOST_PROBABLE_MODES];
+    PU::getIntraMPMs( *pu, mpm_pred );
+    cu.firstPU->intraDir[0] = mpm_pred[0];
+    return;
+  }
+#endif
+
   const int numMPMs   = NUM_MOST_PROBABLE_MODES;
   const int numBlocks = CU::getNumPUs( cu );
   unsigned  mpm_preds   [4][numMPMs];
@@ -1122,6 +1157,9 @@ void CABACWriter::intra_luma_pred_modes( const CodingUnit& cu )
 void CABACWriter::intra_luma_pred_mode( const PredictionUnit& pu )
 {
 
+#if JVET_N0413_RDPCM
+  if( pu.cu->bdpcmMode ) return;
+#endif
   // prev_intra_luma_pred_flag
   const int numMPMs  = NUM_MOST_PROBABLE_MODES;
   unsigned  mpm_pred[numMPMs];
@@ -2270,8 +2308,18 @@ void CABACWriter::cbf_comp( const CodingStructure& cs, bool cbf, const CompArea&
 {
   const unsigned  ctxId   = DeriveCtx::CtxQtCbf( area.compID, depth, prevCbCbf, useISP && isLuma(area.compID) );
   const CtxSet&   ctxSet  = Ctx::QtCbf[ area.compID ];
-
+#if JVET_N0413_RDPCM
+  if( area.compID == COMPONENT_Y && cs.getCU( area.pos(), ChannelType( area.compID ) )->bdpcmMode )
+  {
+    m_BinEncoder.encodeBin( cbf, ctxSet( 4 ) );
+  }
+  else
+  {
   m_BinEncoder.encodeBin( cbf, ctxSet( ctxId ) );
+  }
+#else
+  m_BinEncoder.encodeBin( cbf, ctxSet( ctxId ) );
+#endif
   DTRACE( g_trace_ctx, D_SYNTAX, "cbf_comp() etype=%d pos=(%d,%d) ctx=%d cbf=%d\n", area.compID, area.x, area.y, ctxId, cbf );
 }
 
@@ -2487,7 +2535,11 @@ void CABACWriter::residual_coding( const TransformUnit& tu, ComponentID compID )
   explicit_rdpcm_mode( tu, compID );
 
 #if JVET_N0280_RESIDUAL_CODING_TS
+#if JVET_N0413_RDPCM
+  if( isLuma( compID ) && ( tu.mtsIdx == 1 || tu.cu->bdpcmMode ) )
+#else
   if( isLuma( compID ) && tu.mtsIdx==1 )
+#endif
   {
     residual_codingTS( tu, compID );
     return;
@@ -2605,9 +2657,17 @@ void CABACWriter::mts_coding( const TransformUnit& tu, ComponentID compID )
 void CABACWriter::isp_mode( const CodingUnit& cu )
 {
 #if INCLUDE_ISP_CFG_FLAG
+#if JVET_N0413_RDPCM
+  if( !CU::isIntra( cu ) || !isLuma( cu.chType ) || cu.firstPU->multiRefIdx || cu.ipcm || !cu.cs->sps->getUseISP() || cu.bdpcmMode )
+#else
   if( !CU::isIntra( cu ) || !isLuma( cu.chType ) || cu.firstPU->multiRefIdx || cu.ipcm || !cu.cs->sps->getUseISP() )
+#endif
+#else
+#if JVET_N0413_RDPCM
+  if( !CU::isIntra( cu ) || !isLuma( cu.chType ) || cu.firstPU->multiRefIdx || cu.ipcm || cu.bdpcmMode )
 #else
   if( !CU::isIntra( cu ) || !isLuma( cu.chType ) || cu.firstPU->multiRefIdx || cu.ipcm )
+#endif
 #endif
   {
     CHECK( cu.ispMode != NOT_INTRA_SUBPARTITIONS, "error: cu.intraSubPartitions != 0" );
@@ -2880,7 +2940,11 @@ void CABACWriter::residual_codingTS( const TransformUnit& tu, ComponentID compID
   DTRACE( g_trace_ctx, D_SYNTAX, "residual_codingTS() etype=%d pos=(%d,%d) size=%dx%d\n", tu.blocks[compID].compID, tu.blocks[compID].x, tu.blocks[compID].y, tu.blocks[compID].width, tu.blocks[compID].height );
 
   // init coeff coding context
+#if JVET_N0413_RDPCM
+  CoeffCodingContext  cctx    ( tu, compID, false, tu.cu->bdpcmMode );
+#else
   CoeffCodingContext  cctx    ( tu, compID, false );
+#endif
   const TCoeff*       coeff   = tu.getCoeffs( compID ).buf;
 
   cctx.setNumCtxBins( 2 * tu.lwidth()*tu.lheight() );
@@ -2973,7 +3037,11 @@ void CABACWriter::residual_coding_subblockTS( CoeffCodingContext& cctx, const TC
       int sign = Coeff < 0;
       if( cctx.isContextCoded() )
       {
+#if JVET_N0413_RDPCM
+        m_BinEncoder.encodeBin( sign, Ctx::TsResidualSign( cctx.bdpcm() ? 1 : 0 ) );
+#else
         m_BinEncoder.encodeBin( sign, Ctx::TsResidualSign( toChannelType( cctx.compID() ) ) );
+#endif
       }
       else
       {
diff --git a/source/Lib/EncoderLib/CABACWriter.h b/source/Lib/EncoderLib/CABACWriter.h
index 59b513a48cfb17113a4a36396419fbf8c26f7731..4d511750a90af4ec76418a4d6f8db8293444b332 100644
--- a/source/Lib/EncoderLib/CABACWriter.h
+++ b/source/Lib/EncoderLib/CABACWriter.h
@@ -89,6 +89,9 @@ public:
   void        cu_transquant_bypass_flag ( const CodingUnit&             cu );
   void        cu_skip_flag              ( const CodingUnit&             cu );
   void        pred_mode                 ( const CodingUnit&             cu );
+#if JVET_N0413_RDPCM
+  void        bdpcm_mode                ( const CodingUnit&             cu,       const ComponentID compID );
+#endif
   void        pcm_data                  ( const CodingUnit&             cu,       Partitioner&      pm );
   void        pcm_flag                  ( const CodingUnit&             cu,       Partitioner&      pm );
   void        cu_pred_data              ( const CodingUnit&             cu );
diff --git a/source/Lib/EncoderLib/EncCfg.h b/source/Lib/EncoderLib/EncCfg.h
index 1ddd60bfe7618552f3dd537049463976ce05ac6a..23edf138e4b67d2c230b71af464e7eaa276fffc1 100644
--- a/source/Lib/EncoderLib/EncCfg.h
+++ b/source/Lib/EncoderLib/EncCfg.h
@@ -248,6 +248,9 @@ protected:
 #endif
 #if JVET_N0449_MMVD_SIMP
   int       m_MmvdDisNum;
+#endif
+#if JVET_N0413_RDPCM
+  bool      m_RdpcmMode;
 #endif
   unsigned  m_IBCMode;
   unsigned  m_IBCLocalSearchRangeX;
@@ -787,6 +790,10 @@ public:
 #if JVET_N0449_MMVD_SIMP
   void      setMmvdDisNum                   ( int b )        { m_MmvdDisNum = b; }
   int       getMmvdDisNum                   ()         const { return m_MmvdDisNum; }
+#endif
+#if JVET_N0413_RDPCM
+  void      setRDPCM                     ( bool b )       { m_RdpcmMode = b; }
+  bool      getRDPCM                     ()         const { return m_RdpcmMode; }
 #endif
   void      setIBCMode                      (unsigned n)     { m_IBCMode = n; }
   unsigned  getIBCMode                      ()         const { return m_IBCMode; }
diff --git a/source/Lib/EncoderLib/EncCu.cpp b/source/Lib/EncoderLib/EncCu.cpp
index 0b79eb2f13eb9ca0dee013ac2155c38196d00a11..05619647a1d5bb4a25560d4e94434c9ec2b44842 100644
--- a/source/Lib/EncoderLib/EncCu.cpp
+++ b/source/Lib/EncoderLib/EncCu.cpp
@@ -1426,7 +1426,9 @@ void EncCu::xCheckRDCostIntra( CodingStructure *&tempCS, CodingStructure *&bestC
     m_CABACEstimator->extend_ref_line( cu );
     m_CABACEstimator->isp_mode       ( cu );
     m_CABACEstimator->cu_pred_data   ( cu );
-
+#if JVET_N0413_RDPCM
+    m_CABACEstimator->bdpcm_mode     ( cu, ComponentID(partitioner.chType) );
+#endif
     // Encode Coefficients
     CUCtx cuCtx;
     cuCtx.isDQPCoded = true;
@@ -1476,6 +1478,9 @@ void EncCu::xCheckIntraPCM(CodingStructure *&tempCS, CodingStructure *&bestCS, P
   cu.chromaQpAdj      = cu.transQuantBypass ? 0 : m_cuChromaQpOffsetIdxPlus1;
   cu.qp               = encTestMode.qp;
   cu.ipcm             = true;
+#if JVET_N0413_RDPCM
+  cu.bdpcmMode        = 0;
+#endif
 
   tempCS->addPU( CS::getArea( *tempCS, tempCS->area, partitioner.chType ), partitioner.chType );
 
diff --git a/source/Lib/EncoderLib/IntraSearch.cpp b/source/Lib/EncoderLib/IntraSearch.cpp
index c0a24e92f00d296ac0bf0dce796972f6be21c40a..f2bb6340a03bfe15a5c00efb95f855e2e316ad18 100644
--- a/source/Lib/EncoderLib/IntraSearch.cpp
+++ b/source/Lib/EncoderLib/IntraSearch.cpp
@@ -350,6 +350,9 @@ void IntraSearch::estIntraPredLumaQT( CodingUnit &cu, Partitioner &partitioner,
     m_intraModeTestedNormalIntra.clear();
   }
 
+#if JVET_N0413_RDPCM
+  const bool testBDPCM = m_pcEncCfg->getRDPCM() && CU::bdpcmAllowed(cu, ComponentID(partitioner.chType));
+#endif
   static_vector<uint32_t,   FAST_UDI_MAX_RDMODE_NUM> uiHadModeList;
   static_vector<double, FAST_UDI_MAX_RDMODE_NUM> CandCostList;
   static_vector<double, FAST_UDI_MAX_RDMODE_NUM> CandHadList;
@@ -836,6 +839,10 @@ void IntraSearch::estIntraPredLumaQT( CodingUnit &cu, Partitioner &partitioner,
     //===== check modes (using r-d costs) =====
     uint32_t       uiBestPUMode  = 0;
     int            bestExtendRef = 0;
+#if JVET_N0413_RDPCM
+    int            bestBDPCMMode = 0;
+    double         bestCostNonBDPCM = MAX_DOUBLE;
+#endif
 
     CodingStructure *csTemp = m_pTempCS[gp_sizeIdxInfo->idxFrom( cu.lwidth() )][gp_sizeIdxInfo->idxFrom( cu.lheight() )];
     CodingStructure *csBest = m_pBestCS[gp_sizeIdxInfo->idxFrom( cu.lwidth() )][gp_sizeIdxInfo->idxFrom( cu.lheight() )];
@@ -852,16 +859,39 @@ void IntraSearch::estIntraPredLumaQT( CodingUnit &cu, Partitioner &partitioner,
     uint8_t   bestIspOption               = NOT_INTRA_SUBPARTITIONS;
     TUIntraSubPartitioner subTuPartitioner( partitioner );
     bool      ispHorAllZeroCbfs = false, ispVerAllZeroCbfs = false;
-
+#if JVET_N0413_RDPCM
+    for( int uiMode = -2 * testBDPCM; uiMode < numModesForFullRD; uiMode++ )
+#else
     for (uint32_t uiMode = 0; uiMode < numModesForFullRD; uiMode++)
+#endif
+    {
+#if JVET_N0413_RDPCM
+      int multiRefIdx = 0;
+      uint32_t uiOrgMode;
+
+      if (testBDPCM && uiMode < 0)
     {
+        cu.bdpcmMode = -uiMode;
+        unsigned mpm_pred[NUM_MOST_PROBABLE_MODES];
+        PU::getIntraMPMs(pu, mpm_pred);
+        pu.intraDir[0] = mpm_pred[0];
+        uiOrgMode = mpm_pred[0];
+        cu.ispMode = NOT_INTRA_SUBPARTITIONS;
+      }
+      else
+      {
+        cu.bdpcmMode = 0;
+        uiOrgMode = uiRdModeList[uiMode];
+#else
       // set luma prediction mode
       uint32_t uiOrgMode = uiRdModeList[uiMode];
-
+#endif
       cu.ispMode = extendRefList[uiMode] > MRL_NUM_REF_LINES ? extendRefList[uiMode] - MRL_NUM_REF_LINES : NOT_INTRA_SUBPARTITIONS;
         pu.intraDir[0] = uiOrgMode;
 
+#if !JVET_N0413_RDPCM
         int multiRefIdx = 0;
+#endif
         pu.multiRefIdx = multiRefIdx;
         if( cu.ispMode )
         {
@@ -890,7 +920,9 @@ void IntraSearch::estIntraPredLumaQT( CodingUnit &cu, Partitioner &partitioner,
           CHECK( pu.multiRefIdx && (pu.intraDir[0] == PLANAR_IDX), "ERL" );
 #endif
         }
-
+#if JVET_N0413_RDPCM
+      }
+#endif
 
       // set context models
       m_CABACEstimator->getCtx() = ctxStart;
@@ -933,16 +965,27 @@ void IntraSearch::estIntraPredLumaQT( CodingUnit &cu, Partitioner &partitioner,
         uiBestPUMode  = uiOrgMode;
         bestExtendRef = multiRefIdx;
         bestIspOption = cu.ispMode;
+#if JVET_N0413_RDPCM
+        bestBDPCMMode = cu.bdpcmMode;
+#endif
         if( csBest->cost < bestCurrentCost )
         {
           bestCurrentCost = csBest->cost;
         }
+#if !JVET_N0413_RDPCM
         if( !cu.ispMode )
         {
           bestNormalIntraModeIndex = uiMode;
         }
+#endif
       }
-
+#if JVET_N0413_RDPCM
+      if( !cu.ispMode && !cu.bdpcmMode && csBest->cost < bestCostNonBDPCM )
+      {
+        bestCostNonBDPCM = csBest->cost;
+        bestNormalIntraModeIndex = uiMode;
+      }
+#endif
       csTemp->releaseIntermediateData();
     } // Mode loop
     cu.ispMode = bestIspOption;
@@ -952,6 +995,9 @@ void IntraSearch::estIntraPredLumaQT( CodingUnit &cu, Partitioner &partitioner,
     //=== update PU data ====
     pu.intraDir[0] = uiBestPUMode;
     pu.multiRefIdx = bestExtendRef;
+#if JVET_N0413_RDPCM
+    cu.bdpcmMode   = bestBDPCMMode;
+#endif
   }
 
   //===== reset context models =====
@@ -1334,6 +1380,9 @@ void IntraSearch::xEncIntraHeader( CodingStructure &cs, Partitioner &partitioner
         m_CABACEstimator->cu_skip_flag( cu );
         m_CABACEstimator->pred_mode   ( cu );
       }
+#if JVET_N0413_RDPCM
+      m_CABACEstimator->bdpcm_mode  ( cu, ComponentID(partitioner.chType) );
+#endif
       if( CU::isIntra(cu) )
       {
         m_CABACEstimator->pcm_data( cu, partitioner );