diff --git a/source/App/DecoderApp/DecApp.cpp b/source/App/DecoderApp/DecApp.cpp
index d3bb0c7352c3282ca86f8e8980a1873862b29734..1ce77c7d870a540fe1d91e915747468b9b0d6319 100644
--- a/source/App/DecoderApp/DecApp.cpp
+++ b/source/App/DecoderApp/DecApp.cpp
@@ -604,6 +604,9 @@ void DecApp::xCreateDecLib()
 #endif
   m_cDecLib.m_targetSubPicIdx = this->m_targetSubPicIdx;
   m_cDecLib.initScalingList();
+#if GDR_LEAK_TEST
+  m_cDecLib.m_POC_RandomAccess = this->m_POC_RandomAccess;
+#endif // GDR_LEAK_TEST
 }
 
 void DecApp::xDestroyDecLib()
diff --git a/source/App/DecoderApp/DecAppCfg.cpp b/source/App/DecoderApp/DecAppCfg.cpp
index 036d7f54b9b370a2a2a27ec0db9f6f845e0c90b8..5e9b41709032dde2a26dea04d1efbc434632fb94 100644
--- a/source/App/DecoderApp/DecAppCfg.cpp
+++ b/source/App/DecoderApp/DecAppCfg.cpp
@@ -121,6 +121,9 @@ bool DecAppCfg::parseCfg( int argc, char* argv[] )
   ("MCTSCheck",                m_mctsCheck,                           false,       "If enabled, the decoder checks for violations of mc_exact_sample_value_match_flag in Temporal MCTS ")
   ("targetSubPicIdx",          m_targetSubPicIdx,                     0,           "Specify which subpicture shall be written to output, using subpic index, 0: disabled, subpicIdx=m_targetSubPicIdx-1 \n" )
   ( "UpscaledOutput",          m_upscaledOutput,                          0,       "Upscaled output for RPR" )
+#if GDR_LEAK_TEST
+  ("RandomAccessPos",          m_POC_RandomAccess,                    0,           "POC of Random access picture\n" )
+#endif // GDR_LEAK_TEST
   ;
 
   po::setDefaults(opts);
diff --git a/source/App/DecoderApp/DecAppCfg.h b/source/App/DecoderApp/DecAppCfg.h
index 36b4699d73eb6fb9b1ef57e94757b89e94c6c59a..0b245acf6ced3925de676f53d7c3752f3ae8f684 100644
--- a/source/App/DecoderApp/DecAppCfg.h
+++ b/source/App/DecoderApp/DecAppCfg.h
@@ -88,6 +88,9 @@ protected:
 
   int          m_upscaledOutput;                     ////< Output upscaled (2), decoded but in full resolution buffer (1) or decoded cropped (0, default) picture for RPR.
   int           m_targetSubPicIdx;                    ///< Specify which subpicture shall be write to output, using subpicture index
+#if GDR_LEAK_TEST
+  int           m_POC_RandomAccess;                   ///<
+#endif // GDR_LEAK_TEST
 public:
   DecAppCfg();
   virtual ~DecAppCfg();
diff --git a/source/App/EncoderApp/EncApp.cpp b/source/App/EncoderApp/EncApp.cpp
index bd0cf1bb8ae1bc4336b107cac9e29ddc6601faab..72e15cc4909afc176c3da23445a0e4b6dc1480a4 100644
--- a/source/App/EncoderApp/EncApp.cpp
+++ b/source/App/EncoderApp/EncApp.cpp
@@ -521,6 +521,14 @@ void EncApp::xInitLibCfg()
 
   //====== Coding Structure ========
   m_cEncLib.setIntraPeriod                                       ( m_iIntraPeriod );
+#if GDR_ENABLED  
+  m_cEncLib.setGdrPeriod                                         ( m_iGdrPeriod );
+  m_cEncLib.setGdrPocStart                                       ( m_iGdrPocStart );
+  m_cEncLib.setGdrFrequency                                      ( m_iGdrFrequency);
+  m_cEncLib.setStartWithGdr                                      ( m_bStartWithGdr );
+  m_cEncLib.setNoHashForGdr                                      ( m_bNoHashForGdr );
+  m_cEncLib.setGdrPicOutput                                      ( m_bGdrPicOutput );
+#endif
   m_cEncLib.setDecodingRefreshType                               ( m_iDecodingRefreshType );
   m_cEncLib.setGOPSize                                           ( m_iGOPSize );
   m_cEncLib.setDrapPeriod                                        ( m_drapPeriod );
diff --git a/source/App/EncoderApp/EncAppCfg.cpp b/source/App/EncoderApp/EncAppCfg.cpp
index c34c73bfb40352515af3e1922c4a42a043031059..00d3cfdf28dd1a8fd945a665cf3b54f05997bf72 100644
--- a/source/App/EncoderApp/EncAppCfg.cpp
+++ b/source/App/EncoderApp/EncAppCfg.cpp
@@ -1006,6 +1006,14 @@ bool EncAppCfg::parseCfg( int argc, char* argv[] )
 
   // Coding structure paramters
   ("IntraPeriod,-ip",                                 m_iIntraPeriod,                                      -1, "Intra period in frames, (-1: only first frame)")
+#if GDR_ENABLED
+  ("GdrPocStart",                                     m_iGdrPocStart,                                      -1, "GDR poc start")
+  ("GdrPeriod",                                       m_iGdrPeriod,                                        -1, "GDR period")
+  ("GdrFrequency",                                    m_iGdrFrequency,                                     -1, "GDR freqency")
+  ("StartWithGDR",                                    m_bStartWithGdr,                                  false, "Start bitstream with GDR")
+  ("NoHashforGDR",                                    m_bNoHashForGdr,                                   true, "No Hash for GDR")
+  ("GdrPicOutput",                                    m_bGdrPicOutput,                                  false, "Picture Output for GDR")
+#endif
   ("DecodingRefreshType,-dr",                         m_iDecodingRefreshType,                               0, "Intra refresh type (0:none 1:CRA 2:IDR 3:RecPointSEI)")
   ("GOPSize,g",                                       m_iGOPSize,                                           1, "GOP size of temporal structure")
   ("DRAPPeriod",                                      m_drapPeriod,                                         0, "DRAP period in frames (0: disable Dependent RAP indication SEI messages)")
@@ -1597,6 +1605,37 @@ bool EncAppCfg::parseCfg( int argc, char* argv[] )
     }
   }
 
+#if GDR_ENABLED
+  {     
+    m_iDecodingRefreshType = 3;
+    
+    if (m_iGdrFrequency < 0)
+      m_iGdrFrequency = 2;
+
+    if (m_iGdrPocStart < 0) 
+    {
+      if (m_iIntraPeriod > 0)
+        m_iGdrPocStart = m_iIntraPeriod;
+      else
+        m_iGdrPocStart = m_iFrameRate * m_iGdrFrequency;
+    }
+
+    if (m_iGdrPeriod < 0) 
+    {
+      m_iGdrPeriod = m_iFrameRate;
+    }    
+
+    if (m_iIntraPeriod == -1) 
+    {
+      m_iFrameRate = (m_iFrameRate == 0) ? 30 : m_iFrameRate;
+      if (m_iGdrPocStart % m_iFrameRate != 0)
+        m_iIntraPeriod = -1;
+      else
+        m_iIntraPeriod = m_iFrameRate * m_iGdrFrequency;
+    }
+  }
+#endif
+
   g_verbosity = MsgLevel( m_verbosity );
 
 
@@ -2204,7 +2243,12 @@ bool EncAppCfg::parseCfg( int argc, char* argv[] )
   }
 #endif
 
+#if GDR_ENABLED
+  m_virtualBoundariesEnabledFlag = 1;
+  m_virtualBoundariesPresentFlag = 0;
+#else
   m_virtualBoundariesEnabledFlag = 0;
+#endif
   if( m_numVerVirtualBoundaries > 0 || m_numHorVirtualBoundaries > 0 )
     m_virtualBoundariesEnabledFlag = 1;
 
diff --git a/source/App/EncoderApp/EncAppCfg.h b/source/App/EncoderApp/EncAppCfg.h
index 3b97a4ee1dfa0968c76f86fc299370ea5179814f..13c0349817314508f3486ab4bfbe80bb9736850a 100644
--- a/source/App/EncoderApp/EncAppCfg.h
+++ b/source/App/EncoderApp/EncAppCfg.h
@@ -218,6 +218,14 @@ protected:
   bool          m_noSubpicInfoConstraintFlag;
   // coding structure
   int       m_iIntraPeriod;                                   ///< period of I-slice (random access period)
+#if GDR_ENABLED 
+  int       m_iGdrPocStart;
+  int       m_iGdrPeriod;
+  int       m_iGdrFrequency;
+  bool      m_bStartWithGdr;
+  bool      m_bNoHashForGdr;
+  bool      m_bGdrPicOutput;
+#endif
   int       m_iDecodingRefreshType;                           ///< random access type
   int       m_iGOPSize;                                       ///< GOP size of hierarchical structure
   int       m_drapPeriod;                                     ///< period of dependent RAP pictures
diff --git a/source/Lib/CommonLib/CodingStructure.cpp b/source/Lib/CommonLib/CodingStructure.cpp
index dbfdc3ee38d7963235e5f292f9f6d5a87a2cd1ae..4fcae23e1e113c5260a7b9163abc9467eaf43c8b 100644
--- a/source/Lib/CommonLib/CodingStructure.cpp
+++ b/source/Lib/CommonLib/CodingStructure.cpp
@@ -93,6 +93,10 @@ CodingStructure::CodingStructure(CUCache& cuCache, PUCache& puCache, TUCache& tu
   }
 
   m_motionBuf     = nullptr;
+#if GDR_ENABLED
+  picHeader = nullptr;
+#endif
+
   features.resize( NUM_ENC_FEATURES );
   treeType = TREE_D;
   modeType = MODE_TYPE_ALL;
@@ -131,7 +135,14 @@ void CodingStructure::destroy()
   delete[] m_motionBuf;
   m_motionBuf = nullptr;
 
-
+#if GDR_ENABLED
+  if (picHeader) 
+  {
+    delete picHeader;
+    picHeader = nullptr;
+  }
+#endif
+  
   m_tuCache.cache( tus );
   m_puCache.cache( pus );
   m_cuCache.cache( cus );
@@ -144,6 +155,567 @@ void CodingStructure::releaseIntermediateData()
   clearCUs();
 }
 
+#if GDR_ENABLED
+bool CodingStructure::containRefresh(int begX, int endX) const
+{ 
+  if (begX == endX)
+    return false;
+
+  const Area csArea      = area.Y();
+  const Area refreshArea = Area(begX, area.ly(), endX - begX, std::min(slice->getPPS()->getPicHeightInLumaSamples(), area.lheight()));
+
+  if (csArea.contains(refreshArea))
+    return true;
+  
+  return false;
+}
+
+bool CodingStructure::overlapRefresh(int begX, int endX) const
+{
+  if (begX == endX)
+    return false;
+
+  const Area csArea = area.Y();
+  const Area refreshArea = Area(begX, area.ly(), endX - begX, area.lheight());
+
+  if (csArea.overlap(refreshArea))
+    return true;
+
+  return false;
+}
+
+bool CodingStructure::overlapRefresh() const
+{
+  const int  csX     = area.lx();
+  const int  csWidth = area.lwidth();
+
+  bool ret = overlapRefresh(csX, csX + csWidth);
+  
+  return ret;
+}
+
+bool CodingStructure::withinRefresh(int begX, int endX) const
+{  
+  if (begX == endX)
+    return false;
+
+  const Area csArea = area.Y();
+  const Area refreshArea = Area(begX, area.ly(), endX - begX, area.lheight());
+
+  if (refreshArea.contains(csArea))
+    return true;
+
+  return false;
+}
+
+bool CodingStructure::refreshCrossTTV(int begX, int endX) const
+{
+  const int  csX = area.lx();
+  const int  csY = area.ly();
+  const int  csWidth  = area.lwidth();
+  const int  csHeight = area.lheight();
+
+  const Area refreshArea = Area(begX, csY, endX - begX, csHeight);
+
+  const Area csArea0 = Area(csX,                                   csY, csWidth >> 2, csHeight);
+  const Area csArea1 = Area(csX + (csWidth >> 2),                  csY, csWidth >> 1, csHeight);
+  const Area csArea2 = Area(csX + (csWidth >> 2) + (csWidth >> 1), csY, csWidth >> 2, csHeight);
+
+  bool overlap0 = csArea0.overlap(refreshArea);
+  bool overlap1 = csArea1.overlap(refreshArea);
+  bool overlap2 = csArea2.overlap(refreshArea);
+
+  int sum = (overlap0 ? 1 : 0) + (overlap1 ? 1 : 0) + (overlap2 ? 1 : 0);
+
+  if (0 < sum)
+    return true;
+
+  return false;
+}
+
+bool CodingStructure::refreshCrossBTV(int begX, int endX) const
+{
+  const int  csX = area.lx();
+  const int  csY = area.ly();
+  const int  csWidth = area.lwidth();
+  const int  csHeight = area.lheight();
+
+  const Area refreshArea = Area(begX, csY, endX - begX, csHeight);
+
+  const Area csArea0 = Area(csX,                  csY, (csWidth >> 1), csHeight);
+  const Area csArea1 = Area(csX + (csWidth >> 1), csY, (csWidth >> 1), csHeight);  
+
+  bool overlap0 = csArea0.overlap(refreshArea);
+  bool overlap1 = csArea1.overlap(refreshArea);  
+
+  int sum = (overlap0 ? 1 : 0) + (overlap1 ? 1 : 0);
+
+  if (0 < sum)
+    return true;
+
+  return false;
+}
+
+bool CodingStructure::overlapDirty() const
+{
+  const Position topLeft  = area.Y().topLeft();
+  const Position topRight = area.Y().topRight();
+
+  bool insideLeft  = isClean(topLeft, CHANNEL_TYPE_LUMA);
+  bool insideRight = isClean(topRight, CHANNEL_TYPE_LUMA);
+
+  if (insideLeft != insideRight)
+    return true;
+
+  return false;
+}
+
+bool CodingStructure::dirtyCrossTTV() const
+{ 
+  const int  csX = area.lx();
+  const int  csY = area.ly();
+  const int  csWidth = area.lwidth();
+  const int  csHeight = area.lheight();
+
+  const Area csArea0 = Area(csX, csY, csWidth >> 2, csHeight);
+  const Area csArea1 = Area(csX + (csWidth >> 2), csY, csWidth >> 1, csHeight);
+  const Area csArea2 = Area(csX + (csWidth >> 2) + (csWidth >> 1), csY, csWidth >> 2, csHeight);
+
+  bool clean0 = isClean(csArea0, CHANNEL_TYPE_LUMA);
+  bool clean1 = isClean(csArea1, CHANNEL_TYPE_LUMA);
+  bool clean2 = isClean(csArea2, CHANNEL_TYPE_LUMA);
+
+  bool allclean = clean0 && clean1 && clean2;
+  
+  if (allclean) 
+    return false;
+
+  return true;
+}
+
+bool CodingStructure::dirtyCrossBTV() const
+{
+  const int  csX = area.lx();
+  const int  csY = area.ly();
+  const int  csWidth = area.lwidth();
+  const int  csHeight = area.lheight();
+
+  const Area csArea0 = Area(csX,                  csY, (csWidth >> 1), csHeight);
+  const Area csArea1 = Area(csX + (csWidth >> 1), csY, (csWidth >> 1), csHeight);
+  
+  bool clean0 = isClean(csArea0, CHANNEL_TYPE_LUMA);
+  bool clean1 = isClean(csArea1, CHANNEL_TYPE_LUMA);  
+
+  bool allclean = clean0 && clean1;
+
+  if (allclean)
+    return false;
+
+  return true;
+}
+#endif
+
+
+
+#if GDR_ENABLED
+bool CodingStructure::isClean(const Position &IntPos, Mv FracMv) const
+{
+  /*
+    1. non gdr picture --> false;
+    2. gdr picture
+         pos in clean area -> true
+         pos in dirty area -> false
+  */
+  const Picture* const curPic = slice->getPic();
+
+  if (!curPic)
+    return false;
+
+  PicHeader     *curPh = curPic->cs->picHeader;
+  bool isCurGdrPicture = curPh->getInGdrPeriod();
+
+  if (isCurGdrPicture)
+  {
+    const int lumaPixelAway = 4;
+    const int chromaPixelAway = 5;
+
+    const int iMvShift = MV_FRACTIONAL_BITS_INTERNAL;
+    const int iMvLumaFrac = (1 << iMvShift);
+    const int iMvChromaFrac = (iMvLumaFrac << 1);
+
+    const bool isIntLumaMv = (FracMv.getHor() % iMvLumaFrac) == 0;
+    const bool isIntChromaMv = (FracMv.getHor() % iMvChromaFrac) == 0;
+
+    const int scaledEndX = curPh->getVirtualBoundariesPosX(0) << iMvShift;
+
+
+    const Position OrigFracPos = Position(IntPos.x << iMvShift, IntPos.y << iMvShift);
+    const int lastLumaPos = ((OrigFracPos.x / iMvLumaFrac)   * iMvLumaFrac) + FracMv.getHor() + (isIntLumaMv ? 0 : (lumaPixelAway << iMvShift));
+    const int lastChromaPos = ((OrigFracPos.x / iMvChromaFrac) * iMvChromaFrac) + FracMv.getHor() + (isIntChromaMv ? 0 : (chromaPixelAway << iMvShift));
+
+    const int lastPelPos = std::max(lastLumaPos, lastChromaPos);
+
+    if (lastPelPos < scaledEndX)
+      return true;
+    else
+      return false;
+  }
+  
+  return true;
+}
+
+bool CodingStructure::isClean(const Position &IntPos, Mv FracMv, const Picture* const refPic) const
+{
+  /*
+    1. non gdr picture --> false;
+    2. gdr picture
+         pos in clean area -> true
+         pos in dirty area -> false
+  */  
+  if (!refPic) return false;
+  if (!refPic->cs) return false;
+
+  PicHeader *refPh = refPic->cs->picHeader;
+  if (!refPh) return false;
+
+  bool isRefGdrPicture = refPh->getInGdrPeriod();
+
+  if (isRefGdrPicture)
+  {
+    const int lumaPixelAway = 4;
+    const int chromaPixelAway = 5;
+
+    const int iMvShift = MV_FRACTIONAL_BITS_INTERNAL;
+    const int iMvLumaFrac = (1 << iMvShift);
+    const int iMvChromaFrac = (iMvLumaFrac << 1);
+
+    const bool isIntLumaMv = (FracMv.getHor() % iMvLumaFrac) == 0;
+    const bool isIntChromaMv = (FracMv.getHor() % iMvChromaFrac) == 0;
+
+    const int  scaledEndX = refPh->getVirtualBoundariesPosX(0) << iMvShift;
+
+    const Position OrigFracPos = Position((IntPos.x) << iMvShift, IntPos.y << iMvShift);
+    const int lastLumaPos = ((OrigFracPos.x / iMvLumaFrac)   * iMvLumaFrac) + FracMv.getHor() + (isIntLumaMv ? 0 : (lumaPixelAway << iMvShift));
+    const int lastChromaPos = ((OrigFracPos.x / iMvChromaFrac) * iMvChromaFrac) + FracMv.getHor() + (isIntChromaMv ? 0 : (chromaPixelAway << iMvShift));
+
+    const int lastPelPos = std::max(lastLumaPos, lastChromaPos);
+
+    if (lastPelPos < scaledEndX)
+      return true;
+    else
+      return false;
+  }
+  else 
+  {
+    // refPic is normal picture
+    bool isCurGdrPicture = (slice->getPicHeader()->getNumVerVirtualBoundaries() > 0);
+
+    if (isCurGdrPicture)
+      return false;
+    else
+      return true;
+  }  
+}
+
+
+bool CodingStructure::isClean(const Position &IntPos, Mv FracMv, RefPicList e, int refIdx, int isProf) const
+{
+  /*
+    1. non gdr picture --> false;
+    2. gdr picture
+         pos in clean area -> true
+         pos in dirty area -> false
+  */  
+  if (refIdx < 0) return false;
+
+  const Picture* const refPic = slice->getRefPic(e, refIdx);
+  const bool isExceedNumRef = (refIdx < slice->getNumRefIdx(e)) ? false : true;
+  if (!refPic || isExceedNumRef) return false;
+  if (!refPic->cs) return false;
+
+  PicHeader *refPh = refPic->cs->picHeader;
+
+  if (!refPh) return false;
+    
+  bool isRefGdrPicture = refPh->getInGdrPeriod();
+
+  if (isRefGdrPicture)
+  {    
+    const int lumaPixelAway   = 4 + (isProf << 0);
+    const int chromaPixelAway = 4 + (isProf << 1);
+
+    const int iMvShift      = MV_FRACTIONAL_BITS_INTERNAL;
+    const int iMvLumaFrac   = (1 << iMvShift);
+    const int iMvChromaFrac = (iMvLumaFrac << 1);
+
+    const bool isIntLumaMv      = (FracMv.getHor() % iMvLumaFrac  ) == 0;
+    const bool isIntChromaMv    = isProf ? false : (FracMv.getHor() % iMvChromaFrac) == 0;
+
+    const int  scaledEndX      = refPh->getVirtualBoundariesPosX(0) << iMvShift;
+
+
+    const Position OrigFracPos  = Position((IntPos.x) << iMvShift, IntPos.y << iMvShift);
+    const int lastLumaPos     = ((OrigFracPos.x / iMvLumaFrac)   * iMvLumaFrac)   + FracMv.getHor() + (isIntLumaMv   ? 0 : (lumaPixelAway   << iMvShift));
+    const int lastChromaPos   = ((OrigFracPos.x / iMvChromaFrac) * iMvChromaFrac) + FracMv.getHor() + (isIntChromaMv ? 0 : (chromaPixelAway << iMvShift)) ;
+
+    const int lastPelPos    = std::max(lastLumaPos, lastChromaPos);
+    
+    if (lastPelPos < scaledEndX)
+      return true;
+    else
+      return false;
+  }
+  else 
+  {
+    // refPic is normal picture
+    bool isCurGdrPicture = (slice->getPicHeader()->getNumVerVirtualBoundaries() > 0);
+
+    if (isCurGdrPicture)
+      return false;
+    else
+      return true;
+  }
+}
+
+bool CodingStructure::isClean(const Position &IntPos, Mv FracMv, RefPicList e, int refIdx, bool ibc) const
+{
+  /*
+    1. non gdr picture --> false;
+    2. gdr picture
+         pos in clean area -> true
+         pos in dirty area -> false
+  */
+  if (refIdx < 0) return false;
+
+  Picture*   refPic;
+  PicHeader *refPh;
+
+  if (refIdx == MAX_NUM_REF)
+    refPic = slice->getPic();
+  else
+    refPic = slice->getRefPic(e, refIdx);
+
+  if (!refPic) return false;
+
+  if (refIdx == MAX_NUM_REF) 
+  {
+    refPh = picHeader;
+  }
+  else
+  {
+    if (refPic->cs) return false;
+
+    refPh = refPic->cs->picHeader;
+  }
+
+  if (!refPh) return false;
+
+  bool isRefGdrPicture = refPh->getInGdrPeriod();
+
+  if (isRefGdrPicture)
+  {
+    const int lumaPixelAway = 4;
+    const int chromaPixelAway = 5;
+
+    const int iMvShift = MV_FRACTIONAL_BITS_INTERNAL;
+    const int iMvLumaFrac = (1 << iMvShift);
+    const int iMvChromaFrac = (iMvLumaFrac << 1);
+
+    const bool isIntLumaMv = (FracMv.getHor() % iMvLumaFrac) == 0;
+    const bool isIntChromaMv = (FracMv.getHor() % iMvChromaFrac) == 0;
+
+    const int  scaledEndX = refPh->getVirtualBoundariesPosX(0) << iMvShift;
+
+    const Position OrigFracPos = Position((IntPos.x) << iMvShift, IntPos.y << iMvShift);
+    const int lastLumaPos = ((OrigFracPos.x / iMvLumaFrac)   * iMvLumaFrac) + FracMv.getHor() + (isIntLumaMv ? 0 : (lumaPixelAway << iMvShift));
+    const int lastChromaPos = ((OrigFracPos.x / iMvChromaFrac) * iMvChromaFrac) + FracMv.getHor() + (isIntChromaMv ? 0 : (chromaPixelAway << iMvShift));
+
+    const int lastPelPos = std::max(lastLumaPos, lastChromaPos);
+
+    if (lastPelPos < scaledEndX)
+      return true;
+    else
+      return false;
+  }
+  else 
+  {
+    // refPic is normal picture
+    bool isCurGdrPicture = (slice->getPicHeader()->getNumVerVirtualBoundaries() > 0);
+
+    if (isCurGdrPicture)
+      return false;
+    else
+      return true;
+  }
+}
+
+
+bool CodingStructure::isClean(const Position &IntPos, RefPicList e, int refIdx) const
+{
+  /*
+    1. non gdr picture --> false;
+    2. gdr picture
+         pos in clean area -> true
+         pos in dirty area -> false
+  */
+  const Picture* const refPic = slice->getRefPic(e, refIdx);
+
+  if (!refPic || refIdx < 0)
+    return false;
+
+  PicHeader     *refPh = refPic->cs->picHeader;
+  bool isRefGdrPicture = refPh->getInGdrPeriod();
+
+  if (isRefGdrPicture)
+  {                
+    if (IntPos.x < refPh->getVirtualBoundariesPosX(0)) 
+    {
+      return true;
+    }
+    else
+      return false;
+  }
+  else 
+  {
+    // refPic is normal picture
+    bool isCurGdrPicture = (slice->getPicHeader()->getNumVerVirtualBoundaries() > 0);
+
+    if (isCurGdrPicture)
+      return false;
+    else
+      return true;
+  }
+}
+
+bool CodingStructure::isClean(const Position &IntPos, const Picture* const refPic) const
+{    
+  if (!refPic)
+    return false;
+
+  PicHeader     *refPh = refPic->cs->picHeader;
+  bool isRefGdrPicture = refPh->getInGdrPeriod();
+
+  if (isRefGdrPicture)
+  {
+    if (IntPos.x < refPh->getVirtualBoundariesPosX(0)) 
+    {
+      return true;
+    }
+    else
+      return false;
+  }
+  else 
+  {
+    // refPic is normal picture
+    bool isCurGdrPicture = (slice->getPicHeader()->getNumVerVirtualBoundaries() > 0);
+
+    if (isCurGdrPicture)
+      return false;
+    else
+      return true;
+  }
+}
+
+bool CodingStructure::isClean(const int Intx, const int Inty, const ChannelType effChType) const
+{
+  /*
+    1. non gdr picture --> false;
+    2. gdr picture
+         pos in clean area -> true
+         pos in dirty area -> false
+  */
+  PicHeader     *curPh = picHeader;
+  bool isCurGdrPicture = curPh->getInGdrPeriod();
+  if (isCurGdrPicture)
+  {
+    int virboundary_endx = curPh->getVirtualBoundariesPosX(0);
+
+    virboundary_endx = virboundary_endx >> effChType;
+    if (Intx < virboundary_endx)
+      return true;
+    else
+      return false;
+  }
+
+  return true;
+}
+
+bool CodingStructure::isClean(const Position &IntPos, const ChannelType effChType) const
+{
+  bool ret = isClean(IntPos.x, IntPos.y, effChType);
+
+  return ret;
+}
+
+bool CodingStructure::isClean(const Area &area, const ChannelType effChType) const
+{  
+  Position pTopLeft  = area.topLeft();
+  Position pTopRight = area.topRight();
+  Position pBotLeft  = area.bottomLeft();
+  Position pBotRight = area.bottomRight();
+
+  bool bTopLeft  = isClean(pTopLeft,  effChType);
+  bool bTopRight = isClean(pTopRight, effChType);
+  bool bBotLeft  = isClean(pBotLeft,  effChType);
+  bool bBotRight = isClean(pBotRight, effChType);
+
+  return bTopLeft && bTopRight && bBotLeft && bBotRight;
+}
+
+bool CodingStructure::isClean(const ChannelType effChType) const
+{ 
+  bool ret = isClean(area.Y(), effChType);
+
+  return ret;
+}
+
+bool CodingStructure::isSubPuClean(PredictionUnit &pu, const Mv *mv) const
+{
+  MotionBuf mb = pu.getMotionBuf();  
+  
+  if (pu.cu->affine) 
+  {
+    Position puPos = pu.Y().pos();
+    Size subPuSize = Size(4, 4);
+    
+    int isProf = 1;    
+
+    for (int y = 0; y < mb.height; y++) 
+    {
+      for (int x = 0; x < mb.width; x++) 
+      {
+
+        MotionInfo mi = mb.at(x, y);        
+        Position subPuPos  = Position{puPos.x + (x << 2), puPos.y + (y << 2)};
+        Area     subPuArea = Area(subPuPos, subPuSize);
+        Position subPuTR   = subPuArea.topRight();
+
+        // check if SubPu with L0 is Out of boundary
+        if (mi.refIdx[0] >= 0) 
+        {
+          if (!isClean(subPuTR, mi.mv[0], REF_PIC_LIST_0, mi.refIdx[0], isProf)) 
+          {
+            return false;
+          }
+        }
+
+        // check if SubPu wiht L1 is Out of boundary
+        if (mi.refIdx[1] >= 0) 
+        {
+          if (!isClean(subPuTR, mi.mv[1], REF_PIC_LIST_1, mi.refIdx[1], isProf)) 
+          {
+            return false;
+          }
+        }
+      }
+    }
+  }
+
+  return true;
+}
+#endif
+
+
 bool CodingStructure::isDecomp( const Position &pos, const ChannelType effChType )
 {
   if( area.blocks[effChType].contains( pos ) )
@@ -868,6 +1440,10 @@ void CodingStructure::create(const ChromaFormat &_chromaFormat, const Area& _are
     return;
   }
 
+#if GDR_ENABLED
+  picHeader = new PicHeader();
+#endif
+
   m_reco.create( area );
   m_pred.create( area );
   m_resi.create( area );
@@ -883,6 +1459,10 @@ void CodingStructure::create(const UnitArea& _unit, const bool isTopLayer, const
     return;
   }
 
+#if GDR_ENABLED
+  picHeader = new PicHeader();
+#endif
+
   m_reco.create( area );
   m_pred.create( area );
   m_resi.create( area );
@@ -1157,7 +1737,17 @@ void CodingStructure::initSubStructure( CodingStructure& subStruct, const Channe
   subStruct.sps       = sps;
   subStruct.vps       = vps;
   subStruct.pps       = pps;
+#if GDR_ENABLED
+  if (!subStruct.picHeader) 
+  {
+    subStruct.picHeader = new PicHeader;
+    subStruct.picHeader->initPicHeader();
+  }
+  *subStruct.picHeader = *picHeader;
+#else
   subStruct.picHeader = picHeader;
+#endif
+
   memcpy(subStruct.alfApss, alfApss, sizeof(alfApss));
 
   subStruct.lmcsAps = lmcsAps;
diff --git a/source/Lib/CommonLib/CodingStructure.h b/source/Lib/CommonLib/CodingStructure.h
index 918fc1922ebb39376073e754b9423dcafed13c05..e5a9cfcfbd0eaf24fbd1833989b6fcdc42f1fdeb 100644
--- a/source/Lib/CommonLib/CodingStructure.h
+++ b/source/Lib/CommonLib/CodingStructure.h
@@ -104,6 +104,39 @@ public:
   void destroy();
   void releaseIntermediateData();
 
+#if GDR_ENABLED
+  bool containRefresh(int begX, int endX) const;
+  bool overlapRefresh() const;
+  bool overlapRefresh(int begX, int endX) const;
+  bool withinRefresh(int begX, int endX) const;
+
+  bool refreshCrossTTV(int begX, int endX) const;
+  bool refreshCrossBTV(int begX, int endX) const;
+  bool refreshCrossQT(int begX, int endX) const;  
+
+  bool refreshFitTTV(int begX, int endX) const;
+  bool refreshFitBTV(int begX, int endX) const;
+  bool refreshFitQT(int begX, int endX) const;
+
+  bool overlapDirty() const;
+  bool dirtyCrossTTV() const;  
+  bool dirtyCrossBTV() const;
+#endif
+
+#if GDR_ENABLED
+  bool isClean(const ChannelType effChType) const;
+  bool isClean(const Position &IntPos, RefPicList e, int refIdx) const;
+  bool isClean(const Position &IntPos, const Picture* const ref_pic) const;
+  bool isClean(const Position &IntPos, Mv FracMv) const;  
+  bool isClean(const Position &IntPos, Mv FracMv, const Picture* const refPic) const;
+  bool isClean(const Position &IntPos, Mv FracMv, RefPicList e, int refIdx, int isProf=0) const;
+  bool isClean(const Position &IntPos, Mv FracMv, RefPicList e, int refIdx, bool ibc) const;
+  bool isClean(const Position &IntPos, const ChannelType effChType) const;  
+  bool isClean(const int x, const int y, const ChannelType effChType) const;  
+  bool isClean(const Area &area, const ChannelType effChType) const;
+  
+  bool isSubPuClean(PredictionUnit &pu, const Mv *mv) const;
+#endif
   void rebindPicBufs();
 
   void createCoeffs(const bool isPLTused);
diff --git a/source/Lib/CommonLib/Common.h b/source/Lib/CommonLib/Common.h
index 174bede28a6b3b96d387e98d9b1c1da4e797db1c..52db0a72a93a5fd255c54235984b43b25853cc08 100644
--- a/source/Lib/CommonLib/Common.h
+++ b/source/Lib/CommonLib/Common.h
@@ -97,6 +97,31 @@ struct Area : public Position, public Size
   bool contains(const Position &_pos)       const { return (_pos.x >= x) && (_pos.x < (x + width)) && (_pos.y >= y) && (_pos.y < (y + height)); }
   bool contains(const Area &_area)          const { return contains(_area.pos()) && contains(_area.bottomRight()); }
 
+#if GDR_ENABLED  
+  bool overlap(const Area &_area) const 
+  { 
+    Area thisArea = Area(pos(), size());
+
+    if (contains(_area))
+      return false;
+
+    if (_area.contains(thisArea))
+      return false;
+
+    bool btopLeft  = contains(_area.topLeft());
+    bool btopRight = contains(_area.topRight());
+    bool bbotLeft  = contains(_area.bottomLeft());
+    bool bbotRight = contains(_area.bottomRight());
+
+    int sum = (btopLeft ? 1 : 0) + (btopRight ? 1 : 0) + (bbotLeft ? 1 : 0) + (bbotRight ? 1 : 0);
+
+    if (0 < sum && sum < 4)
+      return true;
+
+    return false;
+  }
+#endif
+
   bool operator!=(const Area &other)        const { return (Size::operator!=(other)) || (Position::operator!=(other)); }
   bool operator==(const Area &other)        const { return (Size::operator==(other)) && (Position::operator==(other)); }
 };
diff --git a/source/Lib/CommonLib/ContextModelling.cpp b/source/Lib/CommonLib/ContextModelling.cpp
index 7b056b1283e83652608621bd91c356640baaada1..4077e9b6ea85e5215a3d6624ea7b0ff69ea4ff93 100644
--- a/source/Lib/CommonLib/ContextModelling.cpp
+++ b/source/Lib/CommonLib/ContextModelling.cpp
@@ -365,6 +365,25 @@ void MergeCtx::setMergeInfo( PredictionUnit& pu, int candIdx )
   pu.mvpIdx [REF_PIC_LIST_1] = NOT_VALID;
   pu.mvpNum [REF_PIC_LIST_0] = NOT_VALID;
   pu.mvpNum [REF_PIC_LIST_1] = NOT_VALID;
+#if GDR_ENABLED
+  CodingStructure &cs = *pu.cs;
+  const bool isEncodeClean = cs.pcv->isEncoder && ((cs.picHeader->getInGdrPeriod() && cs.isClean(pu.Y().topRight(), CHANNEL_TYPE_LUMA)) || (cs.picHeader->getNumVerVirtualBoundaries() == 0));
+
+  if (isEncodeClean) 
+  {    
+    Mv mv0 = pu.mv[REF_PIC_LIST_0];
+    Mv mv1 = pu.mv[REF_PIC_LIST_1];
+
+    int refIdx0 = pu.refIdx[REF_PIC_LIST_0];
+    int refIdx1 = pu.refIdx[REF_PIC_LIST_1];
+
+    pu.mvSolid[REF_PIC_LIST_0] = mvSolid[(candIdx << 1) + 0];
+    pu.mvSolid[REF_PIC_LIST_1] = mvSolid[(candIdx << 1) + 1];
+    pu.mvValid[REF_PIC_LIST_0] = cs.isClean(pu.Y().topRight(), mv0, REF_PIC_LIST_0, refIdx0);
+    pu.mvValid[REF_PIC_LIST_1] = cs.isClean(pu.Y().topRight(), mv1, REF_PIC_LIST_1, refIdx1);      
+  }
+#endif
+
   if (CU::isIBC(*pu.cu))
   {
     pu.bv = pu.mv[REF_PIC_LIST_0];
@@ -389,6 +408,11 @@ void MergeCtx::setMmvdMergeCandiInfo(PredictionUnit& pu, int candIdx)
   int fPosPosition = 0;
   Mv tempMv[2];
 
+#if GDR_ENABLED
+  const CodingStructure &cs = *pu.cs;  
+  const bool isEncodeClean  = cs.pcv->isEncoder && ((cs.picHeader->getInGdrPeriod() && cs.isClean(pu.Y().topRight(), CHANNEL_TYPE_LUMA)) || (cs.picHeader->getNumVerVirtualBoundaries() == 0));
+#endif
+
   tempIdx = candIdx;
   fPosGroup = tempIdx / (MMVD_BASE_MV_NUM * MMVD_MAX_REFINE_NUM);
   tempIdx = tempIdx - fPosGroup * (MMVD_BASE_MV_NUM * MMVD_MAX_REFINE_NUM);
@@ -478,6 +502,25 @@ void MergeCtx::setMmvdMergeCandiInfo(PredictionUnit& pu, int candIdx)
     pu.refIdx[REF_PIC_LIST_0] = refList0;
     pu.mv[REF_PIC_LIST_1] = mmvdBaseMv[fPosBaseIdx][1].mv + tempMv[1];
     pu.refIdx[REF_PIC_LIST_1] = refList1;
+#if GDR_ENABLED
+    if (isEncodeClean) 
+    {
+      Mv mv0 = pu.mv[REF_PIC_LIST_0];
+      Mv mv1 = pu.mv[REF_PIC_LIST_1];
+
+      int refIdx0 = pu.refIdx[REF_PIC_LIST_0];
+      int refIdx1 = pu.refIdx[REF_PIC_LIST_1];
+
+      mmvdValid[fPosBaseIdx][0] = cs.isClean(pu.Y().topRight(), mv0, REF_PIC_LIST_0, refIdx0);
+      mmvdValid[fPosBaseIdx][1] = cs.isClean(pu.Y().topRight(), mv1, REF_PIC_LIST_1, refIdx1);
+
+      pu.mvSolid[REF_PIC_LIST_0] = mmvdSolid[fPosBaseIdx][0];
+      pu.mvSolid[REF_PIC_LIST_1] = mmvdSolid[fPosBaseIdx][1];
+
+      pu.mvValid[REF_PIC_LIST_0] = mmvdValid[fPosBaseIdx][0];
+      pu.mvValid[REF_PIC_LIST_1] = mmvdValid[fPosBaseIdx][1];
+    }
+#endif
   }
   else if (refList0 != -1)
   {
@@ -502,6 +545,26 @@ void MergeCtx::setMmvdMergeCandiInfo(PredictionUnit& pu, int candIdx)
     pu.refIdx[REF_PIC_LIST_0] = refList0;
     pu.mv[REF_PIC_LIST_1] = Mv(0, 0);
     pu.refIdx[REF_PIC_LIST_1] = -1;
+
+#if GDR_ENABLED
+    if (isEncodeClean) 
+    {
+      Mv mv0 = pu.mv[REF_PIC_LIST_0];
+      //Mv mv1 = pu.mv[REF_PIC_LIST_1];
+      
+      int refIdx0 = pu.refIdx[REF_PIC_LIST_0];
+      //int refIdx1 = pu.refIdx[REF_PIC_LIST_1];
+
+      pu.mvSolid[REF_PIC_LIST_0] = mmvdSolid[fPosBaseIdx][0];
+      pu.mvSolid[REF_PIC_LIST_1] = true;
+
+      mmvdValid[fPosBaseIdx][0] = cs.isClean(pu.Y().topRight(), mv0, REF_PIC_LIST_0, refIdx0);
+      mmvdValid[fPosBaseIdx][1] = true;
+
+      pu.mvValid[REF_PIC_LIST_0] = mmvdValid[fPosBaseIdx][0];
+      pu.mvValid[REF_PIC_LIST_1] = true;
+    }
+#endif
   }
   else if (refList1 != -1)
   {
@@ -526,6 +589,25 @@ void MergeCtx::setMmvdMergeCandiInfo(PredictionUnit& pu, int candIdx)
     pu.refIdx[REF_PIC_LIST_0] = -1;
     pu.mv[REF_PIC_LIST_1] = mmvdBaseMv[fPosBaseIdx][1].mv + tempMv[1];
     pu.refIdx[REF_PIC_LIST_1] = refList1;
+#if GDR_ENABLED
+    if (isEncodeClean) 
+    {
+      // Mv mv0 = pu.mv[REF_PIC_LIST_0];
+      Mv mv1 = pu.mv[REF_PIC_LIST_1];
+
+      // int refIdx0 = pu.refIdx[REF_PIC_LIST_0];
+      int refIdx1 = pu.refIdx[REF_PIC_LIST_1];
+
+      mmvdValid[fPosBaseIdx][0] = true;
+      mmvdValid[fPosBaseIdx][1] = cs.isClean(pu.Y().topRight(), mv1, REF_PIC_LIST_1, refIdx1);
+
+      pu.mvSolid[REF_PIC_LIST_0] = true;
+      pu.mvSolid[REF_PIC_LIST_1] = mmvdSolid[fPosBaseIdx][1];
+
+      pu.mvValid[REF_PIC_LIST_0] = true;
+      pu.mvValid[REF_PIC_LIST_1] = mmvdValid[fPosBaseIdx][1];
+    }
+#endif
   }
 
   pu.mmvdMergeFlag = true;
diff --git a/source/Lib/CommonLib/ContextModelling.h b/source/Lib/CommonLib/ContextModelling.h
index 5ada3236eff299f6b265b0b3a1be9e3c11af69ee..9d863b0167285ef76daca88a15b572f8d3520f55 100644
--- a/source/Lib/CommonLib/ContextModelling.h
+++ b/source/Lib/CommonLib/ContextModelling.h
@@ -461,6 +461,13 @@ public:
   ~MergeCtx() {}
 public:
   MvField       mvFieldNeighbours [ MRG_MAX_NUM_CANDS << 1 ]; // double length for mv of both lists
+#if GDR_ENABLED 
+  // note : check if source of mv and mv itself is valid
+  bool          mvSolid           [MRG_MAX_NUM_CANDS << 1];  
+  bool          mvValid           [MRG_MAX_NUM_CANDS << 1];
+  Position      mvPos             [MRG_MAX_NUM_CANDS << 1];
+  MvpType       mvType            [MRG_MAX_NUM_CANDS << 1];
+#endif
   uint8_t       BcwIdx            [ MRG_MAX_NUM_CANDS      ];
   unsigned char interDirNeighbours[ MRG_MAX_NUM_CANDS      ];
   MergeType     mrgTypeNeighbours [ MRG_MAX_NUM_CANDS      ];
@@ -470,6 +477,10 @@ public:
   MotionBuf     subPuMvpMiBuf;
   MotionBuf     subPuMvpExtMiBuf;
   MvField mmvdBaseMv[MMVD_BASE_MV_NUM][2];
+#if GDR_ENABLED   
+  bool          mmvdSolid[MMVD_BASE_MV_NUM][2];
+  bool          mmvdValid[MMVD_BASE_MV_NUM][2];
+#endif
   void setMmvdMergeCandiInfo(PredictionUnit& pu, int candIdx);
   bool          mmvdUseAltHpelIf  [ MMVD_BASE_MV_NUM ];
   bool          useAltHpelIf      [ MRG_MAX_NUM_CANDS ];
@@ -483,6 +494,10 @@ public:
   ~AffineMergeCtx() {}
 public:
   MvField       mvFieldNeighbours[AFFINE_MRG_MAX_NUM_CANDS << 1][3]; // double length for mv of both lists
+#if GDR_ENABLED
+  bool          mvSolid[AFFINE_MRG_MAX_NUM_CANDS << 1][3];   
+  bool          mvValid[AFFINE_MRG_MAX_NUM_CANDS << 1][3];
+#endif
   unsigned char interDirNeighbours[AFFINE_MRG_MAX_NUM_CANDS];
   EAffineModel  affineType[AFFINE_MRG_MAX_NUM_CANDS];
   uint8_t       BcwIdx[AFFINE_MRG_MAX_NUM_CANDS];
diff --git a/source/Lib/CommonLib/InterPrediction.cpp b/source/Lib/CommonLib/InterPrediction.cpp
index a6d47243d2293b8d7c4d0d5a361be72aeaa675b3..932bf8bbb6d8c7c0646660c724385c52cb559c1a 100644
--- a/source/Lib/CommonLib/InterPrediction.cpp
+++ b/source/Lib/CommonLib/InterPrediction.cpp
@@ -878,7 +878,11 @@ bool InterPrediction::isSubblockVectorSpreadOverLimit( int a, int b, int c, int
   return false;
 }
 
+#if GDR_ENABLED
+bool InterPrediction::xPredAffineBlk(const ComponentID &compID, const PredictionUnit &pu, const Picture *refPic, const Mv *_mv, PelUnitBuf &dstPic, const bool &bi, const ClpRng &clpRng, bool genChromaMv, const std::pair<int, int> scalingRatio)
+#else
 void InterPrediction::xPredAffineBlk(const ComponentID &compID, const PredictionUnit &pu, const Picture *refPic, const Mv *_mv, PelUnitBuf &dstPic, const bool &bi, const ClpRng &clpRng, bool genChromaMv, const std::pair<int, int> scalingRatio)
+#endif
 {
 
   JVET_J0090_SET_REF_PICTURE( refPic, compID );
@@ -889,6 +893,13 @@ void InterPrediction::xPredAffineBlk(const ComponentID &compID, const Prediction
   Mv mvLT =_mv[0];
   Mv mvRT =_mv[1];
   Mv mvLB =_mv[2];
+#if GDR_ENABLED  
+  bool allOk = true;
+  const CodingStructure &cs = *pu.cs;
+  const bool isEncodeClean = cs.pcv->isEncoder && ((cs.picHeader->getInGdrPeriod() && cs.isClean(pu.Y().topRight(), CHANNEL_TYPE_LUMA)) || (cs.picHeader->getNumVerVirtualBoundaries() == 0));
+  const int pux = pu.lx();
+  const int puy = pu.ly();
+#endif
 
   // get affine sub-block width and height
   const int width  = pu.Y().width;
@@ -1113,6 +1124,16 @@ void InterPrediction::xPredAffineBlk(const ComponentID &compID, const Prediction
             iMvScaleTmpVer = tmpMv.getVer();
           }
         }
+#if GDR_ENABLED
+        if (isEncodeClean) 
+        {
+          Position subPuPos = Position(pux + w + blockWidth, puy + h + blockHeight);
+          Mv subPuMv = Mv(iMvScaleTmpHor, iMvScaleTmpVer);
+          bool puClean = cs.isClean(subPuPos, subPuMv, refPic);
+
+          allOk = allOk && puClean;
+        }
+#endif
       }
       else
       {
@@ -1133,6 +1154,17 @@ void InterPrediction::xPredAffineBlk(const ComponentID &compID, const Prediction
         }
         iMvScaleTmpHor = curMv.hor;
         iMvScaleTmpVer = curMv.ver;
+
+#if GDR_ENABLED
+        if (isEncodeClean) 
+        {
+          Position subPuPos = Position(pux + (w + blockWidth) * 2, puy + (h + blockHeight) * 2);
+          Mv subPuMv = Mv(iMvScaleTmpHor, iMvScaleTmpVer);
+          bool puClean = cs.isClean(subPuPos, subPuMv, refPic);
+
+          allOk = allOk && puClean;
+        }
+#endif
       }
 
       if( xPredInterBlkRPR( scalingRatio, *pu.cs->pps, CompArea( compID, chFmt, pu.blocks[compID].offset( w, h ), Size( blockWidth, blockHeight ) ), refPic, Mv( iMvScaleTmpHor, iMvScaleTmpVer ), dstBuf.buf + w + h * dstBuf.stride, dstBuf.stride, bi, wrapRef, clpRng, 2 ) )
@@ -1237,6 +1269,9 @@ void InterPrediction::xPredAffineBlk(const ComponentID &compID, const Prediction
       }
     }
   }
+#if GDR_ENABLED
+  return allOk;
+#endif
 }
 
 void InterPrediction::applyBiOptFlow(const PredictionUnit &pu, const CPelUnitBuf &yuvSrc0, const CPelUnitBuf &yuvSrc1, const int &refIdx0, const int &refIdx1, PelUnitBuf &yuvDst, const BitDepths &clipBitDepths)
diff --git a/source/Lib/CommonLib/InterPrediction.h b/source/Lib/CommonLib/InterPrediction.h
index 0ff9d1169773ecd7c2508b0a002948503b8b0755..0c9a69509b2e2d53fd977022d1f869233da7d91d 100644
--- a/source/Lib/CommonLib/InterPrediction.h
+++ b/source/Lib/CommonLib/InterPrediction.h
@@ -137,7 +137,11 @@ protected:
   void xCalcBIOPar              (const Pel* srcY0Temp, const Pel* srcY1Temp, const Pel* gradX0, const Pel* gradX1, const Pel* gradY0, const Pel* gradY1, int* dotProductTemp1, int* dotProductTemp2, int* dotProductTemp3, int* dotProductTemp5, int* dotProductTemp6, const int src0Stride, const int src1Stride, const int gradStride, const int widthG, const int heightG, int bitDepth);
   void xCalcBlkGradient         (int sx, int sy, int    *arraysGx2, int     *arraysGxGy, int     *arraysGxdI, int     *arraysGy2, int     *arraysGydI, int     &sGx2, int     &sGy2, int     &sGxGy, int     &sGxdI, int     &sGydI, int width, int height, int unitSize);
   void xWeightedAverage         ( const PredictionUnit& pu, const CPelUnitBuf& pcYuvSrc0, const CPelUnitBuf& pcYuvSrc1, PelUnitBuf& pcYuvDst, const BitDepths& clipBitDepths, const ClpRngs& clpRngs, const bool& bioApplied, const bool lumaOnly = false, const bool chromaOnly = false, PelUnitBuf* yuvDstTmp = NULL );
+#if GDR_ENABLED
+  bool xPredAffineBlk           ( const ComponentID& compID, const PredictionUnit& pu, const Picture* refPic, const Mv* _mv, PelUnitBuf& dstPic, const bool& bi, const ClpRng& clpRng, const bool genChromaMv = false, const std::pair<int, int> scalingRatio = SCALE_1X );
+#else
   void xPredAffineBlk           ( const ComponentID& compID, const PredictionUnit& pu, const Picture* refPic, const Mv* _mv, PelUnitBuf& dstPic, const bool& bi, const ClpRng& clpRng, const bool genChromaMv = false, const std::pair<int, int> scalingRatio = SCALE_1X );
+#endif
 
   static bool xCheckIdenticalMotion( const PredictionUnit& pu );
 
diff --git a/source/Lib/CommonLib/MotionInfo.h b/source/Lib/CommonLib/MotionInfo.h
index 1e67d2ec559a0be2c00b4a3777d3150eea50bd97..1aad0ce534214909cf6eaafade2ecbff6f07d7c3 100644
--- a/source/Lib/CommonLib/MotionInfo.h
+++ b/source/Lib/CommonLib/MotionInfo.h
@@ -48,12 +48,43 @@
 // ====================================================================================================================
 // Type definition
 // ====================================================================================================================
+#if GDR_ENABLED
+enum MvpType
+{
+  MVP_LEFT,
+  MVP_ABOVE,
+  MVP_ABOVE_RIGHT,
+  MVP_BELOW_LEFT,
+  MVP_ABOVE_LEFT,
+
+  MVP_BELOW_RIGHT,
+  MVP_COMPOSITE,
+
+  MVP_TMVP_C0,
+  MVP_TMVP_C1,
+  MVP_HMVP,
+  MVP_ZERO,
+
+  AFFINE_INHERIT,
+  AFFINE_INHERIT_LB_RB,
+
+  NUM_MVPTYPES
+};
+#endif
 
 /// parameters for AMVP
 struct AMVPInfo
 {
   Mv       mvCand[ AMVP_MAX_NUM_CANDS_MEM ];  ///< array of motion vector predictor candidates
   unsigned numCand;                       ///< number of motion vector predictor candidates
+#if GDR_ENABLED
+  bool     allCandSolidInAbove;
+  bool     mvSolid[AMVP_MAX_NUM_CANDS_MEM];
+  bool     mvValid[AMVP_MAX_NUM_CANDS_MEM];
+
+  Position mvPos[AMVP_MAX_NUM_CANDS_MEM];
+  MvpType  mvType[AMVP_MAX_NUM_CANDS_MEM];
+#endif
 };
 
 struct AffineAMVPInfo
@@ -62,6 +93,25 @@ struct AffineAMVPInfo
   Mv       mvCandRT[ AMVP_MAX_NUM_CANDS_MEM ];  ///< array of affine motion vector predictor candidates for right-top corner
   Mv       mvCandLB[ AMVP_MAX_NUM_CANDS_MEM ];  ///< array of affine motion vector predictor candidates for left-bottom corner
   unsigned numCand;                       ///< number of motion vector predictor candidates
+#if GDR_ENABLED
+  bool     allCandSolidInAbove;  
+
+  bool     mvSolidLT[AMVP_MAX_NUM_CANDS_MEM];
+  bool     mvSolidRT[AMVP_MAX_NUM_CANDS_MEM];
+  bool     mvSolidLB[AMVP_MAX_NUM_CANDS_MEM];
+
+  bool     mvValidLT[AMVP_MAX_NUM_CANDS_MEM];
+  bool     mvValidRT[AMVP_MAX_NUM_CANDS_MEM];
+  bool     mvValidLB[AMVP_MAX_NUM_CANDS_MEM];
+
+  MvpType  mvTypeLT[AMVP_MAX_NUM_CANDS_MEM];
+  MvpType  mvTypeRT[AMVP_MAX_NUM_CANDS_MEM];
+  MvpType  mvTypeLB[AMVP_MAX_NUM_CANDS_MEM];
+  
+  Position mvPosLT[AMVP_MAX_NUM_CANDS_MEM];
+  Position mvPosRT[AMVP_MAX_NUM_CANDS_MEM];
+  Position mvPosLB[AMVP_MAX_NUM_CANDS_MEM];
+#endif
 };
 
 // ====================================================================================================================
@@ -109,6 +159,11 @@ struct MotionInfo
   int16_t   refIdx [ NUM_REF_PIC_LIST_01 ];
   uint8_t         BcwIdx;
   Mv      bv;
+#if GDR_ENABLED
+  bool      soClean;  // source Position is clean/dirty
+  Position  soPos;    // source Position of Mv
+#endif
+
   MotionInfo() : isInter(false), isIBCmot(false), interDir(0), useAltHpelIf(false), sliceIdx(0), refIdx{ NOT_VALID, NOT_VALID }, BcwIdx(0) { }
   // ensure that MotionInfo(0) produces '\x000....' bit pattern - needed to work with AreaBuf - don't use this constructor for anything else
   MotionInfo(int i) : isInter(i != 0), isIBCmot(false), interDir(0), useAltHpelIf(false), sliceIdx(0), refIdx{ 0,         0 }, BcwIdx(0) { CHECKD(i != 0, "The argument for this constructor has to be '0'"); }
@@ -149,12 +204,20 @@ class BcwMotionParam
   bool       m_readOnly[2][33];       // 2 RefLists, 33 RefFrams
   Mv         m_mv[2][33];
   Distortion m_dist[2][33];
+  
+#if GDR_ENABLED
+  bool       m_mvSolid[2][33];
+#endif
 
   bool       m_readOnlyAffine[2][2][33];
   Mv         m_mvAffine[2][2][33][3];
   Distortion m_distAffine[2][2][33];
   int        m_mvpIdx[2][2][33];
 
+#if GDR_ENABLED
+  bool       m_mvAffineSolid[2][2][33][3];
+#endif
+
 public:
 
   void reset()
@@ -176,6 +239,14 @@ public:
     memset(m_readOnlyAffine, false, 2 * 2 * 33 * sizeof(bool));
     memset(m_distAffine, -1, 2 * 2 * 33 * sizeof(Distortion));
     memset( m_mvpIdx, 0, 2 * 2 * 33 * sizeof( int ) );
+
+#if GDR_ENABLED
+    memset(m_mvSolid, true, 2 * 2 * 33 * sizeof(bool));
+#endif
+
+#if GDR_ENABLED
+    memset(m_mvAffineSolid, true, 2 * 2 * 33 * sizeof(bool));
+#endif
   }
 
   void setReadMode(bool b, uint32_t uiRefList, uint32_t uiRefIdx) { m_readOnly[uiRefList][uiRefIdx] = b; }
@@ -192,12 +263,30 @@ public:
     m_dist[uiRefList][uiRefIdx] = uiDist;
   }
 
+#if GDR_ENABLED
+  void copyFrom(Mv& rcMv, bool& rcMvSolid, Distortion uiDist, uint32_t uiRefList, uint32_t uiRefIdx)
+  {
+    m_mv[uiRefList][uiRefIdx] = rcMv;
+    m_dist[uiRefList][uiRefIdx] = uiDist;
+    m_mvSolid[uiRefList][uiRefIdx] = rcMvSolid;
+  }
+#endif
+
   void copyTo(Mv& rcMv, Distortion& ruiDist, uint32_t uiRefList, uint32_t uiRefIdx)
   {
     rcMv = m_mv[uiRefList][uiRefIdx];
     ruiDist = m_dist[uiRefList][uiRefIdx];
   }
 
+#if GDR_ENABLED
+  void copyTo(Mv& rcMv, bool& rcMvSolid, Distortion& ruiDist, uint32_t uiRefList, uint32_t uiRefIdx)
+  {
+    rcMv      = m_mv[uiRefList][uiRefIdx];
+    ruiDist   = m_dist[uiRefList][uiRefIdx];
+    rcMvSolid = m_mvSolid[uiRefList][uiRefIdx];
+  }
+#endif
+
   Mv& getAffineMv(uint32_t uiRefList, uint32_t uiRefIdx, uint32_t uiAffineMvIdx, int bP4) { return m_mvAffine[bP4][uiRefList][uiRefIdx][uiAffineMvIdx]; }
 
   void copyAffineMvFrom(Mv(&racAffineMvs)[3], Distortion uiDist, uint32_t uiRefList, uint32_t uiRefIdx, int bP4
@@ -217,6 +306,26 @@ public:
     ruiDist = m_distAffine[bP4][uiRefList][uiRefIdx];
     mvpIdx  = m_mvpIdx[bP4][uiRefList][uiRefIdx];
   }
+
+#if GDR_ENABLED
+  void copyAffineMvFrom(Mv(&racAffineMvs)[3], bool(&racAffineMvsSolid)[3], Distortion uiDist, uint32_t uiRefList, uint32_t uiRefIdx, int bP4, const int mvpIdx)
+  {
+    memcpy(m_mvAffine[bP4][uiRefList][uiRefIdx],      racAffineMvs,      3 * sizeof(Mv));
+    memcpy(m_mvAffineSolid[bP4][uiRefList][uiRefIdx], racAffineMvsSolid, 3 * sizeof(bool));
+    m_distAffine[bP4][uiRefList][uiRefIdx] = uiDist;
+    m_mvpIdx[bP4][uiRefList][uiRefIdx]     = mvpIdx;
+  }
+#endif
+
+#if GDR_ENABLED
+  void copyAffineMvTo(Mv acAffineMvs[3], bool acAffineMvsSolid[3], Distortion& ruiDist, uint32_t uiRefList, uint32_t uiRefIdx, int bP4, int& mvpIdx)
+  {
+    memcpy(acAffineMvs,      m_mvAffine[bP4][uiRefList][uiRefIdx],      3 * sizeof(Mv));
+    memcpy(acAffineMvsSolid, m_mvAffineSolid[bP4][uiRefList][uiRefIdx], 3 * sizeof(bool));
+    ruiDist = m_distAffine[bP4][uiRefList][uiRefIdx];
+    mvpIdx  = m_mvpIdx[bP4][uiRefList][uiRefIdx];
+  }
+#endif
 };
 struct LutMotionCand
 {
diff --git a/source/Lib/CommonLib/Slice.cpp b/source/Lib/CommonLib/Slice.cpp
index 00980d4c580cb12c034081fe026c514e42f0d778..d0b7a02887331341a7a45fd381cf953010a666d6 100644
--- a/source/Lib/CommonLib/Slice.cpp
+++ b/source/Lib/CommonLib/Slice.cpp
@@ -2741,6 +2741,10 @@ void PicHeader::initPicHeader()
   m_RPL1.setLtrpInSliceHeaderFlag(0);
 
   m_alfApsIdsLuma.resize(0);
+#if GDR_ENABLED
+  m_InGdrPeriod      = false;
+  m_LastGdrPeriodPoc = -1;
+#endif
 }
 
 const WPScalingParam *PicHeader::getWpScaling(const RefPicList refPicList, const int refIdx) const
diff --git a/source/Lib/CommonLib/Slice.h b/source/Lib/CommonLib/Slice.h
index bcd0333c122bb480aff149ebd4b29abfd155245c..4f48b87e39294fab4a092d244ce5213aa224481f 100644
--- a/source/Lib/CommonLib/Slice.h
+++ b/source/Lib/CommonLib/Slice.h
@@ -2332,6 +2332,10 @@ private:
   bool                        m_nonReferencePictureFlag;                                //!< non-reference picture flag
   bool                        m_gdrOrIrapPicFlag;                                       //!< gdr or irap picture flag
   bool                        m_gdrPicFlag;                                             //!< gradual decoding refresh picture flag
+#if GDR_ENABLED
+  bool                        m_InGdrPeriod;
+  int                         m_LastGdrPeriodPoc;
+#endif
   uint32_t                    m_recoveryPocCnt;                                         //!< recovery POC count
   bool                        m_noOutputBeforeRecoveryFlag;                             //!< NoOutputBeforeRecoveryFlag
   bool                        m_handleCraAsCvsStartFlag;                                //!< HandleCraAsCvsStartFlag
@@ -2418,6 +2422,10 @@ public:
   bool                        getGdrOrIrapPicFlag() const                               { return m_gdrOrIrapPicFlag;                                                                   }
   void                        setGdrPicFlag( bool b )                                   { m_gdrPicFlag = b;                                                                            }
   bool                        getGdrPicFlag() const                                     { return m_gdrPicFlag;                                                                         }
+#if GDR_ENABLED
+  void                        setInGdrPeriod(bool b)                                    { m_InGdrPeriod = b;                                                                            }
+  bool                        getInGdrPeriod() const                                    { return m_InGdrPeriod;                                                                         }  
+#endif
   void                        setRecoveryPocCnt( uint32_t u )                           { m_recoveryPocCnt = u;                                                                        }
   uint32_t                    getRecoveryPocCnt() const                                 { return m_recoveryPocCnt;                                                                     }
   void                        setSPSId( uint32_t u )                                    { m_spsId = u;                                                                                 }
@@ -2657,7 +2665,11 @@ private:
   const SPS*                 m_pcSPS;
   const PPS*                 m_pcPPS;
   Picture*                   m_pcPic;
+#if GDR_ENABLED
+  PicHeader*                 m_pcPicHeader;    //!< pointer to picture header structure
+#else
   const PicHeader*           m_pcPicHeader;    //!< pointer to picture header structure
+#endif
   bool                       m_colFromL0Flag;  // collocated picture from List0 flag
 
 
@@ -2711,8 +2723,13 @@ public:
   virtual                     ~Slice();
   void                        initSlice();
   void                        inheritFromPicHeader( PicHeader *picHeader, const PPS *pps, const SPS *sps );
+#if GDR_ENABLED
+  void                        setPicHeader(PicHeader* pcPicHeader) { m_pcPicHeader = pcPicHeader; }
+  PicHeader*                  getPicHeader() const { return m_pcPicHeader; }
+#else
   void                        setPicHeader( const PicHeader* pcPicHeader )           { m_pcPicHeader = pcPicHeader;                                  }
   const PicHeader*            getPicHeader() const                                   { return m_pcPicHeader;                                         }
+#endif
   int                         getRefIdx4MVPair( RefPicList eCurRefPicList, int nCurRefIdx );
 
 
@@ -2844,6 +2861,9 @@ public:
   bool                        isIntra() const                                        { return m_eSliceType == I_SLICE;                               }
   bool                        isInterB() const                                       { return m_eSliceType == B_SLICE;                               }
   bool                        isInterP() const                                       { return m_eSliceType == P_SLICE;                               }
+#if GDR_ENABLED
+  bool                        isInterGDR() const { return (m_eSliceType == B_SLICE && m_eNalUnitType == NAL_UNIT_CODED_SLICE_GDR); }  
+#endif
 
   bool                        getEnableDRAPSEI () const                              { return m_enableDRAPSEI;                                       }
   void                        setEnableDRAPSEI ( bool b )                            { m_enableDRAPSEI = b;                                          }
diff --git a/source/Lib/CommonLib/TypeDef.h b/source/Lib/CommonLib/TypeDef.h
index 67fceef085bdd69715b3b3e7c8b8cbd232847eaf..96fc0572c1472788f0a169edc98e78143acfc019 100644
--- a/source/Lib/CommonLib/TypeDef.h
+++ b/source/Lib/CommonLib/TypeDef.h
@@ -53,6 +53,13 @@
 
 #define PRINT_WPSNR 1
 
+#define GDR_ENABLED   1
+
+#if GDR_ENABLED
+#define GDR_LEAK_TEST  1
+#define GDR_ENC_TRACE  0
+#define GDR_DEC_TRACE  0
+#endif
 
 // clang-format off
 
diff --git a/source/Lib/CommonLib/Unit.cpp b/source/Lib/CommonLib/Unit.cpp
index 8b48488152eddfb15b2fa7591e5b19efaecff767..8f9787c6cf69d206445dc46e8a91b63100163fa9 100644
--- a/source/Lib/CommonLib/Unit.cpp
+++ b/source/Lib/CommonLib/Unit.cpp
@@ -557,6 +557,10 @@ void PredictionUnit::initData()
     for ( uint32_t j = 0; j < 3; j++ )
     {
       mvAffi[i][j].setZero();
+#if GDR_ENABLED
+      mvAffiSolid[i][j] = true;
+      mvAffiValid[i][j] = true;
+#endif
     }
   }
   ciipFlag = false;
diff --git a/source/Lib/CommonLib/Unit.h b/source/Lib/CommonLib/Unit.h
index 430f6e8dcfc71021c917a001774bedf3b81180fc..b6b0c44e4c537477c49b7cd00f68f368626f91ee 100644
--- a/source/Lib/CommonLib/Unit.h
+++ b/source/Lib/CommonLib/Unit.h
@@ -390,12 +390,25 @@ struct InterPredictionData
   uint8_t     mvpNum  [NUM_REF_PIC_LIST_01];
   Mv        mvd     [NUM_REF_PIC_LIST_01];
   Mv        mv      [NUM_REF_PIC_LIST_01];
+#if GDR_ENABLED 
+  bool      mvSolid[NUM_REF_PIC_LIST_01];
+  bool      mvValid[NUM_REF_PIC_LIST_01];
+  bool      mvpSolid[NUM_REF_PIC_LIST_01];
+  MvpType   mvpType[NUM_REF_PIC_LIST_01];
+  Position  mvpPos[NUM_REF_PIC_LIST_01];
+#endif
   int16_t     refIdx  [NUM_REF_PIC_LIST_01];
   MergeType mergeType;
   bool      mvRefine;
   Mv        mvdL0SubPu[MAX_NUM_SUBCU_DMVR];
   Mv        mvdAffi [NUM_REF_PIC_LIST_01][3];
   Mv        mvAffi[NUM_REF_PIC_LIST_01][3];
+#if GDR_ENABLED
+  bool      mvAffiSolid[NUM_REF_PIC_LIST_01][3];
+  bool      mvAffiValid[NUM_REF_PIC_LIST_01][3];
+  MvpType   mvAffiType[NUM_REF_PIC_LIST_01][3];
+  Position  mvAffiPos[NUM_REF_PIC_LIST_01][3];
+#endif
   bool      ciipFlag;
 
   Mv        bv;                             // block vector for IBC
diff --git a/source/Lib/CommonLib/UnitTools.cpp b/source/Lib/CommonLib/UnitTools.cpp
index 5674ef66ada17132faa195b83dc29a83fd7bbfa5..44c4c97717a9046c681434e6f1861cfb9922e031 100644
--- a/source/Lib/CommonLib/UnitTools.cpp
+++ b/source/Lib/CommonLib/UnitTools.cpp
@@ -387,6 +387,10 @@ void CU::saveMotionInHMVP( const CodingUnit& cu, const bool isToBeDone )
   {
     MotionInfo mi = pu.getMotionInfo();
 
+#if GDR_ENABLED 
+    mi.soPos = pu.lumaPos();
+    mi.soClean = pu.cs->isClean(mi.soPos, CHANNEL_TYPE_LUMA);
+#endif
     mi.BcwIdx = (mi.interDir == 3) ? cu.BcwIdx : BCW_DEFAULT;
 
     const unsigned log2ParallelMergeLevel = (pu.cs->sps->getLog2ParallelMergeLevelMinus2() + 2);
@@ -855,7 +859,14 @@ int PU::getWideAngle( const TransformUnit &tu, const uint32_t dirMode, const Com
 bool PU::addMergeHMVPCand(const CodingStructure &cs, MergeCtx &mrgCtx, const int &mrgCandIdx,
                           const uint32_t maxNumMergeCandMin1, int &cnt, const bool isAvailableA1,
                           const MotionInfo miLeft, const bool isAvailableB1, const MotionInfo miAbove,
+#if GDR_ENABLED
+                          const bool ibcFlag, const bool isGt4x4
+  , const PredictionUnit &pu
+  , bool &allCandSolidInAbove
+)
+#else
                           const bool ibcFlag, const bool isGt4x4)
+#endif
 {
   const Slice& slice = *cs.slice;
   MotionInfo miNeighbor;
@@ -863,9 +874,26 @@ bool PU::addMergeHMVPCand(const CodingStructure &cs, MergeCtx &mrgCtx, const int
   auto &lut = ibcFlag ? cs.motionLut.lutIbc : cs.motionLut.lut;
   int num_avai_candInLUT = (int)lut.size();
 
+#if GDR_ENABLED  
+  const bool isEncodeClean = cs.pcv->isEncoder && ((cs.picHeader->getInGdrPeriod() && cs.isClean(pu.Y().topRight(), CHANNEL_TYPE_LUMA)) || (cs.picHeader->getNumVerVirtualBoundaries() == 0));
+  
+  bool  vbOnCtuBoundary = true;  
+  if (isEncodeClean) 
+  {
+    vbOnCtuBoundary = (pu.cs->picHeader->getNumVerVirtualBoundaries() == 0) || (pu.cs->picHeader->getVirtualBoundariesPosX(0) % pu.cs->sps->getMaxCUWidth() == 0);
+    allCandSolidInAbove = allCandSolidInAbove && vbOnCtuBoundary;
+  }
+#endif
   for (int mrgIdx = 1; mrgIdx <= num_avai_candInLUT; mrgIdx++)
   {
     miNeighbor = lut[num_avai_candInLUT - mrgIdx];
+#if GDR_ENABLED    
+    Position soPos = Position(0, 0);
+    if (isEncodeClean) 
+    {
+      soPos = miNeighbor.soPos;
+    }
+#endif
 
     if ( mrgIdx > 2 || ((mrgIdx > 1 || !isGt4x4) && ibcFlag)
       || ((!isAvailableA1 || (miLeft != miNeighbor)) && (!isAvailableB1 || (miAbove != miNeighbor))) )
@@ -875,9 +903,28 @@ bool PU::addMergeHMVPCand(const CodingStructure &cs, MergeCtx &mrgCtx, const int
       mrgCtx.BcwIdx            [cnt] = (miNeighbor.interDir == 3) ? miNeighbor.BcwIdx : BCW_DEFAULT;
 
       mrgCtx.mvFieldNeighbours[cnt << 1].setMvField(miNeighbor.mv[0], miNeighbor.refIdx[0]);
+#if GDR_ENABLED      
+      if (isEncodeClean) 
+      {
+        // note : cannot gaurantee the order/value in the lut if any of the lut is in dirty area
+        mrgCtx.mvPos[(cnt << 1) + 0]   = soPos;
+        mrgCtx.mvSolid[(cnt << 1) + 0] = allCandSolidInAbove && vbOnCtuBoundary;
+        mrgCtx.mvValid[(cnt << 1) + 0] = cs.isClean(pu.Y().bottomRight(), miNeighbor.mv[0], REF_PIC_LIST_0, miNeighbor.refIdx[0]);
+        allCandSolidInAbove = allCandSolidInAbove && vbOnCtuBoundary;
+      }
+#endif
       if (slice.isInterB())
       {
         mrgCtx.mvFieldNeighbours[(cnt << 1) + 1].setMvField(miNeighbor.mv[1], miNeighbor.refIdx[1]);
+#if GDR_ENABLED
+        if (isEncodeClean) 
+        {
+          mrgCtx.mvPos[(cnt << 1) + 1]   = soPos;
+          mrgCtx.mvSolid[(cnt << 1) + 1] = allCandSolidInAbove && vbOnCtuBoundary;
+          mrgCtx.mvValid[(cnt << 1) + 1] = cs.isClean(pu.Y().bottomRight(), miNeighbor.mv[1], REF_PIC_LIST_1, miNeighbor.refIdx[1]);
+          allCandSolidInAbove = allCandSolidInAbove && vbOnCtuBoundary; 
+        }
+#endif
       }
 
       if (mrgCandIdx == cnt)
@@ -905,6 +952,11 @@ void PU::getIBCMergeCandidates(const PredictionUnit &pu, MergeCtx& mrgCtx, const
 {
   const CodingStructure &cs = *pu.cs;
   const uint32_t maxNumMergeCand = pu.cs->sps->getMaxNumIBCMergeCand();
+#if GDR_ENABLED  
+  const bool isEncodeClean = cs.pcv->isEncoder && ((cs.picHeader->getInGdrPeriod() && cs.isClean(pu.Y().topRight(), CHANNEL_TYPE_LUMA)) || (cs.picHeader->getNumVerVirtualBoundaries() == 0));
+  bool  allCandSolidInAbove = true;
+#endif
+
   for (uint32_t ui = 0; ui < maxNumMergeCand; ++ui)
   {
     mrgCtx.BcwIdx[ui] = BCW_DEFAULT;
@@ -912,6 +964,15 @@ void PU::getIBCMergeCandidates(const PredictionUnit &pu, MergeCtx& mrgCtx, const
     mrgCtx.mrgTypeNeighbours[ui] = MRG_TYPE_IBC;
     mrgCtx.mvFieldNeighbours[ui * 2].refIdx = NOT_VALID;
     mrgCtx.mvFieldNeighbours[ui * 2 + 1].refIdx = NOT_VALID;
+#if GDR_ENABLED    
+    if (isEncodeClean) 
+    {
+      mrgCtx.mvSolid[(ui << 1) + 0] = true;
+      mrgCtx.mvSolid[(ui << 1) + 1] = true;
+      mrgCtx.mvValid[(ui << 1) + 0] = true;
+      mrgCtx.mvValid[(ui << 1) + 1] = true;
+    }
+#endif
     mrgCtx.useAltHpelIf[ui] = false;
   }
 
@@ -937,6 +998,12 @@ void PU::getIBCMergeCandidates(const PredictionUnit &pu, MergeCtx& mrgCtx, const
     mrgCtx.interDirNeighbours[cnt] = miLeft.interDir;
     // get Mv from Left
     mrgCtx.mvFieldNeighbours[cnt << 1].setMvField(miLeft.mv[0], miLeft.refIdx[0]);
+#if GDR_ENABLED
+    if (isEncodeClean) 
+    {
+      mrgCtx.mvSolid[(cnt << 1) + 0] = cs.isClean(posLB.offset(-1, 0), pu.chType);
+    }
+#endif
     if (mrgCandIdx == cnt)
     {
       return;
@@ -963,6 +1030,12 @@ void PU::getIBCMergeCandidates(const PredictionUnit &pu, MergeCtx& mrgCtx, const
       mrgCtx.interDirNeighbours[cnt] = miAbove.interDir;
       // get Mv from Above
       mrgCtx.mvFieldNeighbours[cnt << 1].setMvField(miAbove.mv[0], miAbove.refIdx[0]);
+#if GDR_ENABLED
+      if (isEncodeClean) 
+      {
+        mrgCtx.mvSolid[(cnt << 1) + 0] = cs.isClean(posRT.offset(0, -1), pu.chType);
+      }
+#endif
       if (mrgCandIdx == cnt)
       {
         return;
@@ -980,8 +1053,19 @@ void PU::getIBCMergeCandidates(const PredictionUnit &pu, MergeCtx& mrgCtx, const
 
   if (cnt != maxNumMergeCand)
   {
+#if GDR_ENABLED
+    bool allCandSolidInAbove = true;
+    bool bFound = addMergeHMVPCand(cs, mrgCtx, mrgCandIdx, maxNumMergeCand, cnt
+      , isAvailableA1, miLeft, isAvailableB1, miAbove
+      , true
+      , isGt4x4
+      , pu
+      , allCandSolidInAbove
+    );
+#else
     bool bFound = addMergeHMVPCand(cs, mrgCtx, mrgCandIdx, maxNumMergeCand, cnt, isAvailableA1, miLeft, isAvailableB1,
                                    miAbove, true, isGt4x4);
+#endif
 
     if (bFound)
     {
@@ -993,6 +1077,14 @@ void PU::getIBCMergeCandidates(const PredictionUnit &pu, MergeCtx& mrgCtx, const
     {
       mrgCtx.mvFieldNeighbours[cnt * 2].setMvField(Mv(0, 0), MAX_NUM_REF);
       mrgCtx.interDirNeighbours[cnt] = 1;
+#if GDR_ENABLED
+      // GDR: zero mv(0,0)
+      if (isEncodeClean) 
+      {
+        mrgCtx.mvSolid[cnt << 1] = true && allCandSolidInAbove;
+        allCandSolidInAbove       = true && allCandSolidInAbove;
+      }
+#endif
       if (mrgCandIdx == cnt)
       {
         return;
@@ -1012,6 +1104,10 @@ void PU::getInterMergeCandidates( const PredictionUnit &pu, MergeCtx& mrgCtx,
   const Slice &slice         = *pu.cs->slice;
   const uint32_t maxNumMergeCand = pu.cs->sps->getMaxNumMergeCand();
   CHECK (maxNumMergeCand > MRG_MAX_NUM_CANDS, "selected maximum number of merge candidate exceeds global limit");
+#if GDR_ENABLED  
+  const bool isEncodeClean = cs.pcv->isEncoder && ((cs.picHeader->getInGdrPeriod() && cs.isClean(pu.Y().topRight(), CHANNEL_TYPE_LUMA)) || (cs.picHeader->getNumVerVirtualBoundaries() == 0));
+  bool  allCandSolidInAbove = true;
+#endif
   for (uint32_t ui = 0; ui < maxNumMergeCand; ++ui)
   {
     mrgCtx.BcwIdx[ui] = BCW_DEFAULT;
@@ -1019,6 +1115,17 @@ void PU::getInterMergeCandidates( const PredictionUnit &pu, MergeCtx& mrgCtx,
     mrgCtx.mrgTypeNeighbours [ui] = MRG_TYPE_DEFAULT_N;
     mrgCtx.mvFieldNeighbours[(ui << 1)    ].refIdx = NOT_VALID;
     mrgCtx.mvFieldNeighbours[(ui << 1) + 1].refIdx = NOT_VALID;
+#if GDR_ENABLED    
+    if (isEncodeClean) 
+    {
+      mrgCtx.mvSolid[(ui << 1) + 0] = true;
+      mrgCtx.mvSolid[(ui << 1) + 1] = true;
+      mrgCtx.mvValid[(ui << 1) + 0] = true;
+      mrgCtx.mvValid[(ui << 1) + 1] = true;
+      mrgCtx.mvPos[(ui << 1) + 0] = Position(0, 0);
+      mrgCtx.mvPos[(ui << 1) + 1] = Position(0, 0);
+    }
+#endif
     mrgCtx.useAltHpelIf[ui] = false;
   }
 
@@ -1048,9 +1155,27 @@ void PU::getInterMergeCandidates( const PredictionUnit &pu, MergeCtx& mrgCtx,
     mrgCtx.BcwIdx[cnt] = (mrgCtx.interDirNeighbours[cnt] == 3) ? puAbove->cu->BcwIdx : BCW_DEFAULT;
     mrgCtx.mvFieldNeighbours[cnt << 1].setMvField(miAbove.mv[0], miAbove.refIdx[0]);
 
+#if GDR_ENABLED
+    if (isEncodeClean) 
+    {
+      Position pos = puAbove->lumaPos();
+      mrgCtx.mvPos[(cnt << 1) + 0] = pos;
+      mrgCtx.mvSolid[(cnt << 1) + 0] = cs.isClean(pos, pu.chType);
+      mrgCtx.mvValid[(cnt << 1) + 0] = cs.isClean(pu.Y().bottomRight(), miAbove.mv[0], REF_PIC_LIST_0, miAbove.refIdx[0]);
+    }
+#endif
     if (slice.isInterB())
     {
       mrgCtx.mvFieldNeighbours[(cnt << 1) + 1].setMvField(miAbove.mv[1], miAbove.refIdx[1]);
+#if GDR_ENABLED
+      if (isEncodeClean) 
+      {
+        Position pos = puAbove->lumaPos();
+        mrgCtx.mvPos[(cnt << 1) + 1] = pos;
+        mrgCtx.mvSolid[(cnt << 1) + 1] = cs.isClean(pos, pu.chType);
+        mrgCtx.mvValid[(cnt << 1) + 1] = cs.isClean(pu.Y().bottomRight(), miAbove.mv[1], REF_PIC_LIST_1, miAbove.refIdx[1]);
+      }
+#endif
     }
     if (mrgCandIdx == cnt)
     {
@@ -1083,10 +1208,28 @@ void PU::getInterMergeCandidates( const PredictionUnit &pu, MergeCtx& mrgCtx,
       mrgCtx.BcwIdx[cnt] = (mrgCtx.interDirNeighbours[cnt] == 3) ? puLeft->cu->BcwIdx : BCW_DEFAULT;
       // get Mv from Left
       mrgCtx.mvFieldNeighbours[cnt << 1].setMvField(miLeft.mv[0], miLeft.refIdx[0]);
+#if GDR_ENABLED
+      if (isEncodeClean) 
+      {
+        Position pos = puLeft->lumaPos();
+        mrgCtx.mvPos[(cnt << 1) + 0] = pos;
+        mrgCtx.mvSolid[(cnt << 1) + 0] = cs.isClean(pos, pu.chType);
+        mrgCtx.mvValid[(cnt << 1) + 0] = cs.isClean(pu.Y().bottomRight(), miLeft.mv[0], REF_PIC_LIST_0, miLeft.refIdx[0]);
+      }
+#endif
 
       if (slice.isInterB())
       {
         mrgCtx.mvFieldNeighbours[(cnt << 1) + 1].setMvField(miLeft.mv[1], miLeft.refIdx[1]);
+#if GDR_ENABLED
+        if (isEncodeClean) 
+        {
+          Position pos = puLeft->lumaPos();
+          mrgCtx.mvPos[(cnt << 1) + 1] = pos;
+          mrgCtx.mvSolid[(cnt << 1) + 1] = cs.isClean(pos, pu.chType);
+          mrgCtx.mvValid[(cnt << 1) + 1] = cs.isClean(pu.Y().bottomRight(), miLeft.mv[1], REF_PIC_LIST_1, miLeft.refIdx[1]);
+        }
+#endif
       }
       if (mrgCandIdx == cnt)
       {
@@ -1121,10 +1264,28 @@ void PU::getInterMergeCandidates( const PredictionUnit &pu, MergeCtx& mrgCtx,
       // get Mv from Above-right
       mrgCtx.BcwIdx[cnt] = (mrgCtx.interDirNeighbours[cnt] == 3) ? puAboveRight->cu->BcwIdx : BCW_DEFAULT;
       mrgCtx.mvFieldNeighbours[cnt << 1].setMvField( miAboveRight.mv[0], miAboveRight.refIdx[0] );
+#if GDR_ENABLED
+      if (isEncodeClean) 
+      {
+        Position pos = puAboveRight->lumaPos();
+        mrgCtx.mvPos[(cnt << 1) + 0] = pos;
+        mrgCtx.mvSolid[(cnt << 1) + 0] = cs.isClean(pos, pu.chType);
+        mrgCtx.mvValid[(cnt << 1) + 0] = cs.isClean(pu.Y().bottomRight(), miAboveRight.mv[0], REF_PIC_LIST_0, miAboveRight.refIdx[0]);
+      }
+#endif
 
       if( slice.isInterB() )
       {
         mrgCtx.mvFieldNeighbours[( cnt << 1 ) + 1].setMvField( miAboveRight.mv[1], miAboveRight.refIdx[1] );
+#if GDR_ENABLED
+        if (isEncodeClean) 
+        {
+          Position pos = puAboveRight->lumaPos();
+          mrgCtx.mvPos[(cnt << 1) + 1] = pos;
+          mrgCtx.mvSolid[(cnt << 1) + 1] = cs.isClean(pos, pu.chType);
+          mrgCtx.mvValid[(cnt << 1) + 1] = cs.isClean(pu.Y().bottomRight(), miAboveRight.mv[1], REF_PIC_LIST_1, miAboveRight.refIdx[1]);
+        }
+#endif
       }
 
       if (mrgCandIdx == cnt)
@@ -1158,10 +1319,28 @@ void PU::getInterMergeCandidates( const PredictionUnit &pu, MergeCtx& mrgCtx,
       mrgCtx.BcwIdx[cnt] = (mrgCtx.interDirNeighbours[cnt] == 3) ? puLeftBottom->cu->BcwIdx : BCW_DEFAULT;
       // get Mv from Bottom-Left
       mrgCtx.mvFieldNeighbours[cnt << 1].setMvField( miBelowLeft.mv[0], miBelowLeft.refIdx[0] );
+#if GDR_ENABLED
+      if (isEncodeClean) 
+      {
+        Position pos = puLeftBottom->lumaPos();
+        mrgCtx.mvPos[(cnt << 1) + 0] = pos;
+        mrgCtx.mvSolid[(cnt << 1) + 0] = cs.isClean(pos, pu.chType);
+        mrgCtx.mvValid[(cnt << 1) + 0] = cs.isClean(pu.Y().bottomRight(), miBelowLeft.mv[0], REF_PIC_LIST_0, miBelowLeft.refIdx[0]);
+      }
+#endif
 
       if( slice.isInterB() )
       {
         mrgCtx.mvFieldNeighbours[( cnt << 1 ) + 1].setMvField( miBelowLeft.mv[1], miBelowLeft.refIdx[1] );
+#if GDR_ENABLED
+        if (isEncodeClean) 
+        {
+          Position pos = puLeftBottom->lumaPos();
+          mrgCtx.mvPos[(cnt << 1) + 1] = pos;
+          mrgCtx.mvSolid[(cnt << 1) + 1] = cs.isClean(pos, pu.chType);
+          mrgCtx.mvValid[(cnt << 1) + 1] = cs.isClean(pu.Y().bottomRight(), miBelowLeft.mv[1], REF_PIC_LIST_1, miBelowLeft.refIdx[1]);
+        }
+#endif
       }
 
       if (mrgCandIdx == cnt)
@@ -1198,10 +1377,28 @@ void PU::getInterMergeCandidates( const PredictionUnit &pu, MergeCtx& mrgCtx,
         mrgCtx.BcwIdx[cnt] = (mrgCtx.interDirNeighbours[cnt] == 3) ? puAboveLeft->cu->BcwIdx : BCW_DEFAULT;
         // get Mv from Above-Left
         mrgCtx.mvFieldNeighbours[cnt << 1].setMvField( miAboveLeft.mv[0], miAboveLeft.refIdx[0] );
+#if GDR_ENABLED
+        if (isEncodeClean) 
+        {
+          Position pos = puAboveLeft->lumaPos();
+          mrgCtx.mvPos[(cnt << 1) + 0] = pos;
+          mrgCtx.mvSolid[(cnt << 1) + 0] = cs.isClean(pos, pu.chType);
+          mrgCtx.mvValid[(cnt << 1) + 0] = cs.isClean(pu.Y().bottomRight(), miAboveLeft.mv[0], REF_PIC_LIST_0, miAboveLeft.refIdx[0]);
+        }
+#endif
 
         if( slice.isInterB() )
         {
           mrgCtx.mvFieldNeighbours[( cnt << 1 ) + 1].setMvField( miAboveLeft.mv[1], miAboveLeft.refIdx[1] );
+#if GDR_ENABLED
+          if (isEncodeClean) 
+          {
+            Position pos = puAboveLeft->lumaPos();
+            mrgCtx.mvPos[(cnt << 1) + 1] = pos;
+            mrgCtx.mvSolid[(cnt << 1) + 1] = cs.isClean(pos, pu.chType);
+            mrgCtx.mvValid[(cnt << 1) + 1] = cs.isClean(pu.Y().bottomRight(), miAboveLeft.mv[1], REF_PIC_LIST_1, miAboveLeft.refIdx[1]);
+          }
+#endif
         }
 
         if (mrgCandIdx == cnt)
@@ -1225,6 +1422,12 @@ void PU::getInterMergeCandidates( const PredictionUnit &pu, MergeCtx& mrgCtx,
     // offset the pos to be sure to "point" to the same position the uiAbsPartIdx would've pointed to
     Position posRB = pu.Y().bottomRight().offset( -3, -3 );
     const PreCalcValues& pcv = *cs.pcv;
+#if GDR_ENABLED
+    bool posC0inCurPic_solid = true;
+    bool posC1inCurPic_solid = true;
+    bool posC0inRefPic_solid = true;
+    bool posC1inRefPic_solid = true;
+#endif
 
     Position posC0;
     Position posC1 = pu.Y().center();
@@ -1256,6 +1459,24 @@ void PU::getInterMergeCandidates( const PredictionUnit &pu, MergeCtx& mrgCtx,
     {
       dir     |= 1;
       mrgCtx.mvFieldNeighbours[2 * uiArrayAddr].setMvField(cColMv, iRefIdx);
+#if GDR_ENABLED
+      if (isEncodeClean) 
+      {
+        Mv ccMv;
+
+        posC0inCurPic_solid = cs.isClean(posC0, CHANNEL_TYPE_LUMA);
+        posC1inCurPic_solid = cs.isClean(posC1, CHANNEL_TYPE_LUMA);
+        posC0inRefPic_solid = cs.isClean(posC0, REF_PIC_LIST_0, iRefIdx);
+        posC1inRefPic_solid = cs.isClean(posC1, REF_PIC_LIST_0, iRefIdx);
+
+        bool isMVP0exist = C0Avail && getColocatedMVP(pu, REF_PIC_LIST_0, posC0, ccMv, iRefIdx, false);
+
+        Position pos = isMVP0exist ? posC0 : posC1;
+        mrgCtx.mvPos[2 * uiArrayAddr] = pos;
+        mrgCtx.mvSolid[2 * uiArrayAddr] = isMVP0exist ? (posC0inCurPic_solid && posC0inRefPic_solid) : (posC1inCurPic_solid && posC1inRefPic_solid);
+        mrgCtx.mvValid[2 * uiArrayAddr] = cs.isClean(pu.Y().bottomRight(), ccMv, REF_PIC_LIST_0, iRefIdx);
+      }
+#endif
     }
 
     if (slice.isInterB())
@@ -1266,6 +1487,25 @@ void PU::getInterMergeCandidates( const PredictionUnit &pu, MergeCtx& mrgCtx,
       {
         dir     |= 2;
         mrgCtx.mvFieldNeighbours[2 * uiArrayAddr + 1].setMvField(cColMv, iRefIdx);
+#if GDR_ENABLED
+        if (isEncodeClean) 
+        {
+          Mv ccMv;
+
+          posC0inCurPic_solid = cs.isClean(posC0, CHANNEL_TYPE_LUMA);
+          posC1inCurPic_solid = cs.isClean(posC1, CHANNEL_TYPE_LUMA);
+          posC0inRefPic_solid = cs.isClean(posC0, REF_PIC_LIST_1, iRefIdx);
+          posC1inRefPic_solid = cs.isClean(posC1, REF_PIC_LIST_1, iRefIdx);
+
+          bool isMVP0exist = C0Avail && getColocatedMVP(pu, REF_PIC_LIST_1, posC0, ccMv, iRefIdx, false);
+
+          Position pos = isMVP0exist ? posC0 : posC1;
+          mrgCtx.mvPos[2 * uiArrayAddr + 1] = pos;
+          mrgCtx.mvSolid[2 * uiArrayAddr + 1] = isMVP0exist ? (posC0inCurPic_solid && posC0inRefPic_solid) : (posC1inCurPic_solid && posC1inRefPic_solid);
+          mrgCtx.mvValid[2 * uiArrayAddr + 1] = cs.isClean(pu.Y().bottomRight(), ccMv, REF_PIC_LIST_1, iRefIdx);
+
+        }
+#endif
       }
     }
 
@@ -1297,8 +1537,16 @@ void PU::getInterMergeCandidates( const PredictionUnit &pu, MergeCtx& mrgCtx,
   if (cnt != maxNumMergeCandMin1)
   {
     bool isGt4x4 = true;
+#if GDR_ENABLED
+    allCandSolidInAbove = true;
+#endif
+#if GDR_ENABLED
+    bool bFound  = addMergeHMVPCand(cs, mrgCtx, mrgCandIdx, maxNumMergeCandMin1, cnt, isAvailableA1, miLeft,
+                                   isAvailableB1, miAbove, CU::isIBC(*pu.cu), isGt4x4, pu, allCandSolidInAbove);
+#else
     bool bFound  = addMergeHMVPCand(cs, mrgCtx, mrgCandIdx, maxNumMergeCandMin1, cnt, isAvailableA1, miLeft,
                                    isAvailableB1, miAbove, CU::isIBC(*pu.cu), isGt4x4);
+#endif
 
     if (bFound)
     {
@@ -1321,6 +1569,12 @@ void PU::getInterMergeCandidates( const PredictionUnit &pu, MergeCtx& mrgCtx,
         const short refIdxI = mrgCtx.mvFieldNeighbours[0 * 2 + refListId].refIdx;
         const short refIdxJ = mrgCtx.mvFieldNeighbours[1 * 2 + refListId].refIdx;
 
+#if GDR_ENABLED
+        // GDR: Pairwise average candidate       
+        bool mvI_solid = mrgCtx.mvSolid[0 * 2 + refListId];
+        bool mvJ_solid = mrgCtx.mvSolid[1 * 2 + refListId];
+        bool mv_solid = true;
+#endif
         // both MVs are invalid, skip
         if( (refIdxI == NOT_VALID) && (refIdxJ == NOT_VALID) )
         {
@@ -1340,17 +1594,53 @@ void PU::getInterMergeCandidates( const PredictionUnit &pu, MergeCtx& mrgCtx,
           roundAffineMv(avgMv.hor, avgMv.ver, 1);
 
           mrgCtx.mvFieldNeighbours[cnt * 2 + refListId].setMvField( avgMv, refIdxI );
+#if GDR_ENABLED
+          // GDR: Pairwise single I,J candidate
+          if (isEncodeClean) 
+          {
+            mv_solid = mvI_solid && mvJ_solid && allCandSolidInAbove;
+
+            mrgCtx.mvPos[cnt * 2 + refListId] = Position(0, 0);
+            mrgCtx.mvSolid[cnt * 2 + refListId] = mv_solid && allCandSolidInAbove;
+            mrgCtx.mvValid[cnt * 2 + refListId] = cs.isClean(pu.Y().bottomRight(), avgMv, (RefPicList)refListId, refIdxI);
+            allCandSolidInAbove = mv_solid && allCandSolidInAbove;
+          }
+#endif
         }
         // only one MV is valid, take the only one MV
         else if( refIdxI != NOT_VALID )
         {
           Mv singleMv = mrgCtx.mvFieldNeighbours[0 * 2 + refListId].mv;
           mrgCtx.mvFieldNeighbours[cnt * 2 + refListId].setMvField( singleMv, refIdxI );
+#if GDR_ENABLED
+          // GDR: Pairwise single I,J candidate
+          if (isEncodeClean) 
+          {
+            mv_solid = mvI_solid && allCandSolidInAbove;
+
+            mrgCtx.mvPos[cnt * 2 + refListId] = Position(0, 0);
+            mrgCtx.mvSolid[cnt * 2 + refListId] = mv_solid && allCandSolidInAbove;
+            mrgCtx.mvValid[cnt * 2 + refListId] = cs.isClean(pu.Y().bottomRight(), singleMv, (RefPicList)refListId, refIdxI);
+            allCandSolidInAbove = mv_solid && allCandSolidInAbove;
+          }
+#endif
         }
         else if( refIdxJ != NOT_VALID )
         {
           Mv singleMv = mrgCtx.mvFieldNeighbours[1 * 2 + refListId].mv;
           mrgCtx.mvFieldNeighbours[cnt * 2 + refListId].setMvField( singleMv, refIdxJ );
+#if GDR_ENABLED
+          // GDR: Pairwise single I,J candidate
+          if (isEncodeClean) 
+          {
+            mv_solid = mvJ_solid && allCandSolidInAbove;
+
+            mrgCtx.mvPos[cnt * 2 + refListId] = Position(0, 0);
+            mrgCtx.mvSolid[cnt * 2 + refListId] = mv_solid && allCandSolidInAbove;
+            mrgCtx.mvValid[cnt * 2 + refListId] = cs.isClean(pu.Y().bottomRight(), singleMv, (RefPicList)refListId, refIdxJ);
+            allCandSolidInAbove = mv_solid && allCandSolidInAbove;
+          }
+#endif
         }
       }
 
@@ -1381,10 +1671,31 @@ void PU::getInterMergeCandidates( const PredictionUnit &pu, MergeCtx& mrgCtx,
     mrgCtx.mvFieldNeighbours  [uiArrayAddr << 1].setMvField(Mv(0, 0), r);
     mrgCtx.useAltHpelIf[uiArrayAddr] = false;
 
+#if GDR_ENABLED
+    // GDR: zero mv(0,0)
+    if (isEncodeClean) 
+    {
+      mrgCtx.mvPos[uiArrayAddr << 1] = Position(0, 0);
+      mrgCtx.mvSolid[uiArrayAddr << 1] = true && allCandSolidInAbove;
+      mrgCtx.mvValid[uiArrayAddr << 1] = cs.isClean(pu.Y().bottomRight(), Mv(0, 0), REF_PIC_LIST_0, r);
+      allCandSolidInAbove = true && allCandSolidInAbove;
+    }
+#endif
     if (slice.isInterB())
     {
       mrgCtx.interDirNeighbours [ uiArrayAddr          ] = 3;
       mrgCtx.mvFieldNeighbours  [(uiArrayAddr << 1) + 1].setMvField(Mv(0, 0), r);
+#if GDR_ENABLED
+      // GDR: zero mv(0,0)
+      if (isEncodeClean) 
+      {
+
+        mrgCtx.mvPos[(uiArrayAddr << 1) + 1] = Position(0, 0);
+        mrgCtx.mvSolid[(uiArrayAddr << 1) + 1] = true && allCandSolidInAbove;
+        mrgCtx.mvValid[(uiArrayAddr << 1) + 1] = cs.isClean(pu.Y().bottomRight(), Mv(0, 0), (RefPicList)REF_PIC_LIST_1, r);
+        allCandSolidInAbove = true && allCandSolidInAbove;
+      }
+#endif
     }
 
     if ( mrgCtx.interDirNeighbours[uiArrayAddr] == 1 && pu.cs->slice->getRefPic(REF_PIC_LIST_0, mrgCtx.mvFieldNeighbours[uiArrayAddr << 1].refIdx)->getPOC() == pu.cs->slice->getPOC())
@@ -1502,6 +1813,20 @@ void PU::getInterMMVDMergeCandidates(const PredictionUnit &pu, MergeCtx& mrgCtx,
   int currBaseNum = 0;
   const uint16_t maxNumMergeCand = mrgCtx.numValidMergeCand;
 
+#if GDR_ENABLED
+  const CodingStructure &cs = *pu.cs;
+  const bool isEncodeClean = cs.pcv->isEncoder && ((cs.picHeader->getInGdrPeriod() && cs.isClean(pu.Y().topRight(), CHANNEL_TYPE_LUMA)) || (cs.picHeader->getNumVerVirtualBoundaries() == 0));
+#endif
+
+#if GDR_ENABLED
+  for (int k = 0; k < MMVD_BASE_MV_NUM; k++) 
+  {
+    mrgCtx.mmvdSolid[k][0] = true;
+    mrgCtx.mmvdSolid[k][1] = true;
+    mrgCtx.mmvdValid[k][0] = true;
+    mrgCtx.mmvdValid[k][1] = true;
+  }
+#endif
   for (k = 0; k < maxNumMergeCand; k++)
   {
     if (mrgCtx.mrgTypeNeighbours[k] == MRG_TYPE_DEFAULT_N)
@@ -1513,16 +1838,37 @@ void PU::getInterMMVDMergeCandidates(const PredictionUnit &pu, MergeCtx& mrgCtx,
       {
         mrgCtx.mmvdBaseMv[currBaseNum][0] = mrgCtx.mvFieldNeighbours[(k << 1)];
         mrgCtx.mmvdBaseMv[currBaseNum][1] = mrgCtx.mvFieldNeighbours[(k << 1) + 1];
+#if GDR_ENABLED
+        if (isEncodeClean) 
+        {
+          mrgCtx.mmvdSolid[currBaseNum][0] = mrgCtx.mvSolid[(k << 1) + 0];
+          mrgCtx.mmvdSolid[currBaseNum][1] = mrgCtx.mvSolid[(k << 1) + 1];
+        }
+#endif
       }
       else if (refIdxList0 >= 0)
       {
         mrgCtx.mmvdBaseMv[currBaseNum][0] = mrgCtx.mvFieldNeighbours[(k << 1)];
         mrgCtx.mmvdBaseMv[currBaseNum][1] = MvField(Mv(0, 0), -1);
+#if GDR_ENABLED
+        if (isEncodeClean) 
+        {
+          mrgCtx.mmvdSolid[currBaseNum][0] = mrgCtx.mvSolid[(k << 1) + 0];
+          mrgCtx.mmvdSolid[currBaseNum][1] = true;
+        }
+#endif
       }
       else if (refIdxList1 >= 0)
       {
         mrgCtx.mmvdBaseMv[currBaseNum][0] = MvField(Mv(0, 0), -1);
         mrgCtx.mmvdBaseMv[currBaseNum][1] = mrgCtx.mvFieldNeighbours[(k << 1) + 1];
+#if GDR_ENABLED
+        if (isEncodeClean) 
+        {
+          mrgCtx.mmvdSolid[currBaseNum][0] = true;
+          mrgCtx.mmvdSolid[currBaseNum][1] = mrgCtx.mvSolid[(k << 1) + 1];
+        }
+#endif
       }
       mrgCtx.mmvdUseAltHpelIf[currBaseNum] = mrgCtx.useAltHpelIf[k];
 
@@ -1864,6 +2210,10 @@ void PU::fillMvpCand(PredictionUnit &pu, const RefPicList &eRefPicList, const in
   Position posRT = pu.Y().topRight();
   Position posLB = pu.Y().bottomLeft();
 
+#if GDR_ENABLED
+  const bool isEncodeClean = cs.pcv->isEncoder && ((cs.picHeader->getInGdrPeriod() && cs.isClean(pu.Y().topRight(), CHANNEL_TYPE_LUMA)) || (cs.picHeader->getNumVerVirtualBoundaries() == 0));
+  bool &allCandSolidInAbove = amvpInfo.allCandSolidInAbove;
+#endif
   {
     bool bAdded = addMVPCandUnscaled( pu, eRefPicList, refIdx, posLB, MD_BELOW_LEFT, *pInfo );
 
@@ -1934,6 +2284,24 @@ void PU::fillMvpCand(PredictionUnit &pu, const RefPicList &eRefPicList, const in
     }
     if ( ( C0Avail && getColocatedMVP( pu, eRefPicList, posC0, cColMv, refIdx_Col, false ) ) || getColocatedMVP( pu, eRefPicList, posC1, cColMv, refIdx_Col, false ) )
     {
+#if GDR_ENABLED
+      if (isEncodeClean) 
+      {
+        Mv   ccMv;
+        bool posC0inCurPic_solid = cs.isClean(posC0, CHANNEL_TYPE_LUMA);
+        bool posC1inCurPic_solid = cs.isClean(posC1, CHANNEL_TYPE_LUMA);
+        bool posC0inRefPic_solid = cs.isClean(posC0, REF_PIC_LIST_0, refIdx_Col);
+        bool posC1inRefPic_solid = cs.isClean(posC1, REF_PIC_LIST_0, refIdx_Col);
+
+        bool isMVP0exist = C0Avail && getColocatedMVP(pu, eRefPicList, posC0, ccMv, refIdx_Col, false);
+
+        Position pos = isMVP0exist ? posC0 : posC1;
+        pInfo->mvPos[pInfo->numCand]   = pos;
+        pInfo->mvType[pInfo->numCand]  = isMVP0exist ? MVP_TMVP_C0 : MVP_TMVP_C1;
+        pInfo->mvSolid[pInfo->numCand] = allCandSolidInAbove && (isMVP0exist ? (posC0inCurPic_solid && posC0inRefPic_solid) : (posC1inCurPic_solid && posC1inRefPic_solid));
+        allCandSolidInAbove = allCandSolidInAbove && pInfo->mvSolid[pInfo->numCand];
+      }
+#endif
       cColMv.roundTransPrecInternal2Amvr(pu.cu->imv);
       pInfo->mvCand[pInfo->numCand++] = cColMv;
     }
@@ -1952,6 +2320,13 @@ void PU::fillMvpCand(PredictionUnit &pu, const RefPicList &eRefPicList, const in
 
   while (pInfo->numCand < AMVP_MAX_NUM_CANDS)
   {
+#if GDR_ENABLED
+    if (isEncodeClean) 
+    {
+      pInfo->mvType[pInfo->numCand] = MVP_ZERO;
+      allCandSolidInAbove = pInfo->mvSolid[pInfo->numCand] = true && allCandSolidInAbove;
+    }
+#endif
     pInfo->mvCand[pInfo->numCand] = Mv( 0, 0 );
     pInfo->numCand++;
   }
@@ -1968,6 +2343,9 @@ bool PU::addAffineMVPCandUnscaled( const PredictionUnit &pu, const RefPicList &r
   const PredictionUnit *neibPU = NULL;
   Position neibPos;
 
+#if GDR_ENABLED  
+  const bool isEncodeClean = cs.pcv->isEncoder && ((cs.picHeader->getInGdrPeriod() && cs.isClean(pu.Y().topRight(), CHANNEL_TYPE_LUMA)) || (cs.picHeader->getNumVerVirtualBoundaries() == 0));
+#endif
   switch ( dir )
   {
   case MD_LEFT:
@@ -1997,6 +2375,11 @@ bool PU::addAffineMVPCandUnscaled( const PredictionUnit &pu, const RefPicList &r
   }
 
   Mv outputAffineMv[3];
+#if GDR_ENABLED
+  bool     outputAffineMvSolid[3];
+  MvpType  outputAffineMvType[3];
+  Position outputAffineMvPos[3];
+#endif
   const MotionInfo& neibMi = neibPU->getMotionInfo( neibPos );
 
   const int        currRefPOC = cs.slice->getRefPic( refPicList, refIdx )->getPOC();
@@ -2012,15 +2395,54 @@ bool PU::addAffineMVPCandUnscaled( const PredictionUnit &pu, const RefPicList &r
       continue;
     }
 
+#if GDR_ENABLED
+    // note : get MV from neihgbor of neibPu (LB, RB) and save to outputAffineMv
+    if (isEncodeClean) 
+    {
+      xInheritedAffineMv(pu, neibPU, eRefPicListIndex, outputAffineMv, outputAffineMvSolid, outputAffineMvType, outputAffineMvPos);
+    }
+    else 
+    {
+      xInheritedAffineMv(pu, neibPU, eRefPicListIndex, outputAffineMv);
+    }
+#else
     xInheritedAffineMv( pu, neibPU, eRefPicListIndex, outputAffineMv );
+#endif
     outputAffineMv[0].roundAffinePrecInternal2Amvr(pu.cu->imv);
     outputAffineMv[1].roundAffinePrecInternal2Amvr(pu.cu->imv);
     affiAMVPInfo.mvCandLT[affiAMVPInfo.numCand] = outputAffineMv[0];
     affiAMVPInfo.mvCandRT[affiAMVPInfo.numCand] = outputAffineMv[1];
+#if GDR_ENABLED
+    bool neighClean = true;
+
+    if (isEncodeClean) 
+    {
+      neighClean = cs.isClean(neibPU->Y().pos(), CHANNEL_TYPE_LUMA);      
+
+      affiAMVPInfo.mvSolidLT[affiAMVPInfo.numCand] = neighClean && outputAffineMvSolid[0];
+      affiAMVPInfo.mvSolidRT[affiAMVPInfo.numCand] = neighClean && outputAffineMvSolid[1];
+      
+      affiAMVPInfo.mvTypeLT[affiAMVPInfo.numCand]  = outputAffineMvType[0];
+      affiAMVPInfo.mvTypeRT[affiAMVPInfo.numCand]  = outputAffineMvType[1];
+
+      affiAMVPInfo.mvPosLT[affiAMVPInfo.numCand]   = outputAffineMvPos[0];;
+      affiAMVPInfo.mvPosRT[affiAMVPInfo.numCand]   = outputAffineMvPos[1];      
+    }
+#endif
     if ( pu.cu->affineType == AFFINEMODEL_6PARAM )
     {
       outputAffineMv[2].roundAffinePrecInternal2Amvr(pu.cu->imv);
       affiAMVPInfo.mvCandLB[affiAMVPInfo.numCand] = outputAffineMv[2];
+#if GDR_ENABLED
+      if (isEncodeClean) 
+      {
+        neighClean = cs.isClean(neibPU->Y().pos(), CHANNEL_TYPE_LUMA);
+
+        affiAMVPInfo.mvSolidLB[affiAMVPInfo.numCand] = neighClean && outputAffineMvSolid[2];
+        affiAMVPInfo.mvTypeLB[affiAMVPInfo.numCand]  = outputAffineMvType[2];
+        affiAMVPInfo.mvPosLB[affiAMVPInfo.numCand]   = outputAffineMvPos[2];        
+      }
+#endif
     }
     affiAMVPInfo.numCand++;
     return true;
@@ -2028,6 +2450,134 @@ bool PU::addAffineMVPCandUnscaled( const PredictionUnit &pu, const RefPicList &r
 
   return false;
 }
+#if GDR_ENABLED
+void PU::xInheritedAffineMv(const PredictionUnit &pu, const PredictionUnit* puNeighbour, RefPicList eRefPicList, Mv rcMv[3], bool rcMvSolid[3], MvpType rcMvType[3], Position rcMvPos[3])
+{
+#if GDR_ENABLED  
+  const CodingStructure &cs = *pu.cs;
+  const bool isEncodeClean = cs.pcv->isEncoder && ((cs.picHeader->getInGdrPeriod() && cs.isClean(pu.Y().topRight(), CHANNEL_TYPE_LUMA)) || (cs.picHeader->getNumVerVirtualBoundaries() == 0));
+#endif
+
+  int posNeiX = puNeighbour->Y().pos().x;
+  int posNeiY = puNeighbour->Y().pos().y;
+  int posCurX = pu.Y().pos().x;
+  int posCurY = pu.Y().pos().y;
+
+  int neiW = puNeighbour->Y().width;
+  int curW = pu.Y().width;
+  int neiH = puNeighbour->Y().height;
+  int curH = pu.Y().height;
+
+  Mv mvLT, mvRT, mvLB;
+
+  mvLT = puNeighbour->mvAffi[eRefPicList][0];
+  mvRT = puNeighbour->mvAffi[eRefPicList][1];
+  mvLB = puNeighbour->mvAffi[eRefPicList][2];
+
+
+#if GDR_ENABLED
+  bool neighClean = true;
+
+  if (isEncodeClean) 
+  {
+    neighClean = cs.isClean(puNeighbour->Y().pos(), CHANNEL_TYPE_LUMA);
+
+    rcMvSolid[0] = neighClean;
+    rcMvSolid[1] = neighClean;
+    rcMvSolid[2] = neighClean;
+
+    rcMvType[0] = AFFINE_INHERIT;
+    rcMvType[1] = AFFINE_INHERIT;
+    rcMvType[2] = AFFINE_INHERIT;
+
+    rcMvPos[0] = puNeighbour->Y().pos();
+    rcMvPos[1] = puNeighbour->Y().pos();
+    rcMvPos[2] = puNeighbour->Y().pos();
+  }
+#endif
+
+
+  bool isTopCtuBoundary = false;
+  if ((posNeiY + neiH) % pu.cs->sps->getCTUSize() == 0 && (posNeiY + neiH) == posCurY)
+  {
+    // use bottom-left and bottom-right sub-block MVs for inheritance
+    const Position posRB = puNeighbour->Y().bottomRight();
+    const Position posLB = puNeighbour->Y().bottomLeft();
+
+    mvLT = puNeighbour->getMotionInfo(posLB).mv[eRefPicList];
+    mvRT = puNeighbour->getMotionInfo(posRB).mv[eRefPicList];
+
+#if GDR_ENABLED
+    if (isEncodeClean) 
+    {
+      neighClean = cs.isClean(puNeighbour->Y().pos(), CHANNEL_TYPE_LUMA);
+
+      rcMvSolid[0] = cs.isClean(posLB, CHANNEL_TYPE_LUMA);
+      rcMvSolid[1] = cs.isClean(posRB, CHANNEL_TYPE_LUMA);
+      rcMvSolid[2] = neighClean;
+
+      rcMvType[0]  = AFFINE_INHERIT_LB_RB;
+      rcMvType[1]  = AFFINE_INHERIT_LB_RB;
+      rcMvType[2]  = AFFINE_INHERIT_LB_RB;
+
+      rcMvPos[0]   = posLB;
+      rcMvPos[1]   = posRB;
+      rcMvPos[2]   = puNeighbour->Y().pos();
+    }
+#endif
+
+    posNeiY += neiH;
+    isTopCtuBoundary = true;
+  }
+
+  int shift = MAX_CU_DEPTH;
+  int iDMvHorX, iDMvHorY, iDMvVerX, iDMvVerY;
+
+  iDMvHorX = (mvRT - mvLT).getHor() << (shift - floorLog2(neiW));
+  iDMvHorY = (mvRT - mvLT).getVer() << (shift - floorLog2(neiW));
+  if (puNeighbour->cu->affineType == AFFINEMODEL_6PARAM && !isTopCtuBoundary)
+  {
+    iDMvVerX = (mvLB - mvLT).getHor() << (shift - floorLog2(neiH));
+    iDMvVerY = (mvLB - mvLT).getVer() << (shift - floorLog2(neiH));
+  }
+  else
+  {
+    iDMvVerX = -iDMvHorY;
+    iDMvVerY = iDMvHorX;
+  }
+
+  int iMvScaleHor = mvLT.getHor() << shift;
+  int iMvScaleVer = mvLT.getVer() << shift;
+  int horTmp, verTmp;
+
+  // v0
+  horTmp = iMvScaleHor + iDMvHorX * (posCurX - posNeiX) + iDMvVerX * (posCurY - posNeiY);
+  verTmp = iMvScaleVer + iDMvHorY * (posCurX - posNeiX) + iDMvVerY * (posCurY - posNeiY);
+  roundAffineMv(horTmp, verTmp, shift);
+  rcMv[0].hor = horTmp;
+  rcMv[0].ver = verTmp;
+  rcMv[0].clipToStorageBitDepth();
+
+  // v1
+  horTmp = iMvScaleHor + iDMvHorX * (posCurX + curW - posNeiX) + iDMvVerX * (posCurY - posNeiY);
+  verTmp = iMvScaleVer + iDMvHorY * (posCurX + curW - posNeiX) + iDMvVerY * (posCurY - posNeiY);
+  roundAffineMv(horTmp, verTmp, shift);
+  rcMv[1].hor = horTmp;
+  rcMv[1].ver = verTmp;
+  rcMv[1].clipToStorageBitDepth();
+
+  // v2
+  if (pu.cu->affineType == AFFINEMODEL_6PARAM)
+  {
+    horTmp = iMvScaleHor + iDMvHorX * (posCurX - posNeiX) + iDMvVerX * (posCurY + curH - posNeiY);
+    verTmp = iMvScaleVer + iDMvHorY * (posCurX - posNeiX) + iDMvVerY * (posCurY + curH - posNeiY);
+    roundAffineMv(horTmp, verTmp, shift);
+    rcMv[2].hor = horTmp;
+    rcMv[2].ver = verTmp;
+    rcMv[2].clipToStorageBitDepth();
+  }
+}
+#endif
 
 void PU::xInheritedAffineMv( const PredictionUnit &pu, const PredictionUnit* puNeighbour, RefPicList eRefPicList, Mv rcMv[3] )
 {
@@ -2116,8 +2666,31 @@ void PU::fillAffineMvpCand(PredictionUnit &pu, const RefPicList &eRefPicList, co
     return;
   }
 
+#if GDR_ENABLED
+  const CodingStructure &cs = *pu.cs;
+  const bool isEncodeClean = cs.pcv->isEncoder && ((cs.picHeader->getInGdrPeriod() && cs.isClean(pu.Y().topRight(), CHANNEL_TYPE_LUMA)) || (cs.picHeader->getNumVerVirtualBoundaries() == 0));
+  bool &allCandSolidInAbove = affiAMVPInfo.allCandSolidInAbove;
+
+  if (isEncodeClean) 
+  {
+    allCandSolidInAbove = true;
+    
+    affiAMVPInfo.allCandSolidInAbove = true;
+    for (int i = 0; i < AMVP_MAX_NUM_CANDS_MEM; i++) 
+    {
+      affiAMVPInfo.mvSolidLT[i] = true;
+      affiAMVPInfo.mvSolidRT[i] = true;
+      affiAMVPInfo.mvSolidLB[i] = true;
+    }  
+  }
+#endif
   // insert inherited affine candidates
   Mv outputAffineMv[3];
+#if GDR_ENABLED
+  bool     outputAffineMvSolid[3];
+  MvpType  outputAffineMvType[3];
+  Position outputAffineMvPos[3];
+#endif
   Position posLT = pu.Y().topLeft();
   Position posRT = pu.Y().topRight();
   Position posLB = pu.Y().bottomLeft();
@@ -2155,6 +2728,18 @@ void PU::fillAffineMvpCand(PredictionUnit &pu, const RefPicList &eRefPicList, co
   AMVPInfo amvpInfo0;
   amvpInfo0.numCand = 0;
 
+#if GDR_ENABLED  
+  if (isEncodeClean) 
+  {
+    amvpInfo0.allCandSolidInAbove = true;
+    for (int i = 0; i < AMVP_MAX_NUM_CANDS_MEM; i++) 
+    {
+      amvpInfo0.mvSolid[i] = true;
+      amvpInfo0.mvValid[i] = true;
+    }
+  }
+#endif
+
   // A->C: Above Left, Above, Left
   addMVPCandUnscaled( pu, eRefPicList, refIdx, posLT, MD_ABOVE_LEFT, amvpInfo0 );
   if ( amvpInfo0.numCand < 1 )
@@ -2171,6 +2756,18 @@ void PU::fillAffineMvpCand(PredictionUnit &pu, const RefPicList &eRefPicList, co
   AMVPInfo amvpInfo1;
   amvpInfo1.numCand = 0;
 
+#if GDR_ENABLED  
+  if (isEncodeClean) 
+  {
+    amvpInfo1.allCandSolidInAbove = true;
+    for (int i = 0; i < AMVP_MAX_NUM_CANDS_MEM; i++) 
+    {
+      amvpInfo1.mvSolid[i] = true;
+      amvpInfo1.mvValid[i] = true;
+    }
+  }
+#endif
+
   // D->E: Above, Above Right
   addMVPCandUnscaled( pu, eRefPicList, refIdx, posRT, MD_ABOVE, amvpInfo1 );
   if ( amvpInfo1.numCand < 1 )
@@ -2183,6 +2780,18 @@ void PU::fillAffineMvpCand(PredictionUnit &pu, const RefPicList &eRefPicList, co
   AMVPInfo amvpInfo2;
   amvpInfo2.numCand = 0;
 
+#if GDR_ENABLED  
+  if (isEncodeClean) 
+  {
+    amvpInfo2.allCandSolidInAbove = true;
+    for (int i = 0; i < AMVP_MAX_NUM_CANDS_MEM; i++) 
+    {
+      amvpInfo2.mvSolid[i] = true;
+      amvpInfo2.mvValid[i] = true;
+    }
+  }
+#endif
+
   // F->G: Left, Below Left
   addMVPCandUnscaled( pu, eRefPicList, refIdx, posLB, MD_LEFT, amvpInfo2 );
   if ( amvpInfo2.numCand < 1 )
@@ -2195,6 +2804,24 @@ void PU::fillAffineMvpCand(PredictionUnit &pu, const RefPicList &eRefPicList, co
   outputAffineMv[1] = amvpInfo1.mvCand[0];
   outputAffineMv[2] = amvpInfo2.mvCand[0];
 
+#if GDR_ENABLED
+  if (isEncodeClean) 
+  {
+    outputAffineMvSolid[0] = amvpInfo0.mvSolid[0] && allCandSolidInAbove;
+    outputAffineMvSolid[1] = amvpInfo1.mvSolid[0] && allCandSolidInAbove;
+    outputAffineMvSolid[2] = amvpInfo2.mvSolid[0] && allCandSolidInAbove;
+
+    outputAffineMvPos[0] = amvpInfo0.mvPos[0];
+    outputAffineMvPos[1] = amvpInfo1.mvPos[0];
+    outputAffineMvPos[2] = amvpInfo2.mvPos[0];
+
+    outputAffineMvType[0] = amvpInfo0.mvType[0];
+    outputAffineMvType[1] = amvpInfo1.mvType[0];
+    outputAffineMvType[2] = amvpInfo2.mvType[0];   
+
+    allCandSolidInAbove = allCandSolidInAbove && outputAffineMvSolid[0] && outputAffineMvSolid[1] && outputAffineMvSolid[2];
+  }
+#endif
   outputAffineMv[0].roundAffinePrecInternal2Amvr(pu.cu->imv);
   outputAffineMv[1].roundAffinePrecInternal2Amvr(pu.cu->imv);
   outputAffineMv[2].roundAffinePrecInternal2Amvr(pu.cu->imv);
@@ -2204,6 +2831,24 @@ void PU::fillAffineMvpCand(PredictionUnit &pu, const RefPicList &eRefPicList, co
     affiAMVPInfo.mvCandLT[affiAMVPInfo.numCand] = outputAffineMv[0];
     affiAMVPInfo.mvCandRT[affiAMVPInfo.numCand] = outputAffineMv[1];
     affiAMVPInfo.mvCandLB[affiAMVPInfo.numCand] = outputAffineMv[2];
+#if GDR_ENABLED
+    if (isEncodeClean) 
+    {
+      affiAMVPInfo.mvSolidLT[affiAMVPInfo.numCand] = outputAffineMvSolid[0] && allCandSolidInAbove;
+      affiAMVPInfo.mvSolidRT[affiAMVPInfo.numCand] = outputAffineMvSolid[1] && allCandSolidInAbove;
+      affiAMVPInfo.mvSolidLB[affiAMVPInfo.numCand] = outputAffineMvSolid[2] && allCandSolidInAbove;
+
+      affiAMVPInfo.mvPosLT[affiAMVPInfo.numCand] = outputAffineMvPos[0];
+      affiAMVPInfo.mvPosRT[affiAMVPInfo.numCand] = outputAffineMvPos[1];
+      affiAMVPInfo.mvPosLB[affiAMVPInfo.numCand] = outputAffineMvPos[2];
+
+      affiAMVPInfo.mvTypeLT[affiAMVPInfo.numCand] = outputAffineMvType[0];
+      affiAMVPInfo.mvTypeRT[affiAMVPInfo.numCand] = outputAffineMvType[1];
+      affiAMVPInfo.mvTypeLB[affiAMVPInfo.numCand] = outputAffineMvType[2];
+
+      allCandSolidInAbove = allCandSolidInAbove && outputAffineMvSolid[0] && outputAffineMvSolid[1] && outputAffineMvSolid[2];
+    }
+#endif    
     affiAMVPInfo.numCand++;
   }
 
@@ -2217,6 +2862,24 @@ void PU::fillAffineMvpCand(PredictionUnit &pu, const RefPicList &eRefPicList, co
         affiAMVPInfo.mvCandLT[affiAMVPInfo.numCand] = outputAffineMv[i];
         affiAMVPInfo.mvCandRT[affiAMVPInfo.numCand] = outputAffineMv[i];
         affiAMVPInfo.mvCandLB[affiAMVPInfo.numCand] = outputAffineMv[i];
+#if GDR_ENABLED
+        if (isEncodeClean) 
+        {
+          affiAMVPInfo.mvSolidLT[affiAMVPInfo.numCand] = outputAffineMvSolid[i] && allCandSolidInAbove;
+          affiAMVPInfo.mvSolidRT[affiAMVPInfo.numCand] = outputAffineMvSolid[i] && allCandSolidInAbove;
+          affiAMVPInfo.mvSolidLB[affiAMVPInfo.numCand] = outputAffineMvSolid[i] && allCandSolidInAbove;          
+
+          affiAMVPInfo.mvPosLT[affiAMVPInfo.numCand] = outputAffineMvPos[i];
+          affiAMVPInfo.mvPosRT[affiAMVPInfo.numCand] = outputAffineMvPos[i];
+          affiAMVPInfo.mvPosLB[affiAMVPInfo.numCand] = outputAffineMvPos[i];
+
+          affiAMVPInfo.mvTypeLT[affiAMVPInfo.numCand] = outputAffineMvType[i];
+          affiAMVPInfo.mvTypeRT[affiAMVPInfo.numCand] = outputAffineMvType[i];
+          affiAMVPInfo.mvTypeLB[affiAMVPInfo.numCand] = outputAffineMvType[i];
+
+          allCandSolidInAbove = allCandSolidInAbove && outputAffineMvSolid[i];
+        }
+#endif
         affiAMVPInfo.numCand++;
       }
     }
@@ -2256,6 +2919,52 @@ void PU::fillAffineMvpCand(PredictionUnit &pu, const RefPicList &eRefPicList, co
         affiAMVPInfo.mvCandLT[affiAMVPInfo.numCand] = cColMv;
         affiAMVPInfo.mvCandRT[affiAMVPInfo.numCand] = cColMv;
         affiAMVPInfo.mvCandLB[affiAMVPInfo.numCand] = cColMv;
+#if GDR_ENABLED
+        if (isEncodeClean) 
+        {
+          Mv ccMv;
+
+          bool posC0inCurPic_solid = cs.isClean(posC0, CHANNEL_TYPE_LUMA);
+          bool posC1inCurPic_solid = cs.isClean(posC1, CHANNEL_TYPE_LUMA);
+          bool posC0inRefPic_solid = cs.isClean(posC0, eRefPicList, refIdxCol);
+          bool posC1inRefPic_solid = cs.isClean(posC1, eRefPicList, refIdxCol);
+
+          bool isMVP0exist = C0Avail && getColocatedMVP(pu, eRefPicList, posC0, ccMv, refIdxCol, false);
+
+          if (isMVP0exist) 
+          {
+            affiAMVPInfo.mvSolidLT[affiAMVPInfo.numCand] = posC0inCurPic_solid && posC0inRefPic_solid && allCandSolidInAbove;
+            affiAMVPInfo.mvSolidRT[affiAMVPInfo.numCand] = posC0inCurPic_solid && posC0inRefPic_solid && allCandSolidInAbove;
+            affiAMVPInfo.mvSolidLB[affiAMVPInfo.numCand] = posC0inCurPic_solid && posC0inRefPic_solid && allCandSolidInAbove;            
+
+            affiAMVPInfo.mvPosLT[affiAMVPInfo.numCand] = posC0;
+            affiAMVPInfo.mvPosRT[affiAMVPInfo.numCand] = posC0;
+            affiAMVPInfo.mvPosLB[affiAMVPInfo.numCand] = posC0;
+
+            affiAMVPInfo.mvTypeLT[affiAMVPInfo.numCand] = MVP_TMVP_C0;
+            affiAMVPInfo.mvTypeRT[affiAMVPInfo.numCand] = MVP_TMVP_C0;
+            affiAMVPInfo.mvTypeLB[affiAMVPInfo.numCand] = MVP_TMVP_C0;
+
+            allCandSolidInAbove = allCandSolidInAbove && affiAMVPInfo.mvSolidLT[affiAMVPInfo.numCand] && affiAMVPInfo.mvSolidRT[affiAMVPInfo.numCand] && affiAMVPInfo.mvSolidLB[affiAMVPInfo.numCand];
+          }
+          else 
+          {
+            affiAMVPInfo.mvSolidLT[affiAMVPInfo.numCand] = posC1inCurPic_solid && posC1inRefPic_solid && allCandSolidInAbove;
+            affiAMVPInfo.mvSolidRT[affiAMVPInfo.numCand] = posC1inCurPic_solid && posC1inRefPic_solid && allCandSolidInAbove;
+            affiAMVPInfo.mvSolidLB[affiAMVPInfo.numCand] = posC1inCurPic_solid && posC1inRefPic_solid && allCandSolidInAbove;            
+
+            affiAMVPInfo.mvPosLT[affiAMVPInfo.numCand] = posC1;
+            affiAMVPInfo.mvPosRT[affiAMVPInfo.numCand] = posC1;
+            affiAMVPInfo.mvPosLB[affiAMVPInfo.numCand] = posC1;
+
+            affiAMVPInfo.mvTypeLT[affiAMVPInfo.numCand] = MVP_TMVP_C1;
+            affiAMVPInfo.mvTypeRT[affiAMVPInfo.numCand] = MVP_TMVP_C1;
+            affiAMVPInfo.mvTypeLB[affiAMVPInfo.numCand] = MVP_TMVP_C1;
+
+            allCandSolidInAbove = allCandSolidInAbove && affiAMVPInfo.mvSolidLT[affiAMVPInfo.numCand] && affiAMVPInfo.mvSolidRT[affiAMVPInfo.numCand] && affiAMVPInfo.mvSolidLB[affiAMVPInfo.numCand];
+          }
+        }
+#endif
         affiAMVPInfo.numCand++;
       }
     }
@@ -2268,6 +2977,24 @@ void PU::fillAffineMvpCand(PredictionUnit &pu, const RefPicList &eRefPicList, co
         affiAMVPInfo.mvCandLT[affiAMVPInfo.numCand].setZero();
         affiAMVPInfo.mvCandRT[affiAMVPInfo.numCand].setZero();
         affiAMVPInfo.mvCandLB[affiAMVPInfo.numCand].setZero();
+#if GDR_ENABLED
+        if (isEncodeClean) 
+        {
+          affiAMVPInfo.mvSolidLT[affiAMVPInfo.numCand] = true && allCandSolidInAbove;
+          affiAMVPInfo.mvSolidRT[affiAMVPInfo.numCand] = true && allCandSolidInAbove;
+          affiAMVPInfo.mvSolidLB[affiAMVPInfo.numCand] = true && allCandSolidInAbove;          
+
+          affiAMVPInfo.mvPosLT[affiAMVPInfo.numCand] = Position(0, 0);
+          affiAMVPInfo.mvPosRT[affiAMVPInfo.numCand] = Position(0, 0);
+          affiAMVPInfo.mvPosLB[affiAMVPInfo.numCand] = Position(0, 0);
+
+          affiAMVPInfo.mvTypeLT[affiAMVPInfo.numCand] = MVP_ZERO;
+          affiAMVPInfo.mvTypeRT[affiAMVPInfo.numCand] = MVP_ZERO;
+          affiAMVPInfo.mvTypeLB[affiAMVPInfo.numCand] = MVP_ZERO;
+
+          allCandSolidInAbove = allCandSolidInAbove && affiAMVPInfo.mvSolidLT[affiAMVPInfo.numCand] && affiAMVPInfo.mvSolidRT[affiAMVPInfo.numCand] && affiAMVPInfo.mvSolidLB[affiAMVPInfo.numCand];
+        }
+#endif
         affiAMVPInfo.numCand++;
       }
     }
@@ -2287,6 +3014,11 @@ bool PU::addMVPCandUnscaled( const PredictionUnit &pu, const RefPicList &eRefPic
   const PredictionUnit *neibPU = NULL;
         Position neibPos;
 
+#if GDR_ENABLED
+  const bool isEncodeClean = cs.pcv->isEncoder && ((cs.picHeader->getInGdrPeriod() && cs.isClean(pu.Y().topRight(), CHANNEL_TYPE_LUMA)) || (cs.picHeader->getNumVerVirtualBoundaries() == 0));
+  bool &allCandSolidInAbove = info.allCandSolidInAbove;
+#endif  
+
   switch (eDir)
   {
   case MD_LEFT:
@@ -2327,6 +3059,18 @@ bool PU::addMVPCandUnscaled( const PredictionUnit &pu, const RefPicList &eRefPic
 
     if( neibRefIdx >= 0 && currRefPOC == cs.slice->getRefPOC( eRefPicListIndex, neibRefIdx ) )
     {
+#if GDR_ENABLED
+      if (isEncodeClean) 
+      {
+        bool isSolid = cs.isClean(neibPos, CHANNEL_TYPE_LUMA);
+
+        info.mvSolid[info.numCand] = isSolid && allCandSolidInAbove;        
+        info.mvType[info.numCand] = (MvpType)eDir;
+        info.mvPos[info.numCand] = neibPos;
+        
+        allCandSolidInAbove = isSolid && allCandSolidInAbove;
+      }
+#endif
       info.mvCand[info.numCand++] = neibMi.mv[eRefPicListIndex];
       return true;
     }
@@ -2346,6 +3090,22 @@ void PU::addAMVPHMVPCand(const PredictionUnit &pu, const RefPicList eRefPicList,
   int num_allowedCand = std::min(MAX_NUM_HMVP_AVMPCANDS, num_avai_candInLUT);
   const RefPicList eRefPicList2nd = (eRefPicList == REF_PIC_LIST_0) ? REF_PIC_LIST_1 : REF_PIC_LIST_0;
 
+#if GDR_ENABLED
+  CodingStructure &cs = *pu.cs;
+  const bool isEncodeClean = cs.pcv->isEncoder && ((cs.picHeader->getInGdrPeriod() && cs.isClean(pu.Y().topRight(), CHANNEL_TYPE_LUMA)) || (cs.picHeader->getNumVerVirtualBoundaries() == 0));  
+  bool &allCandSolidInAbove = info.allCandSolidInAbove;
+#endif
+
+
+#if GDR_ENABLED
+  bool vbOnCtuBoundary = true;
+  if (isEncodeClean) 
+  {
+    vbOnCtuBoundary = (pu.cs->picHeader->getNumVerVirtualBoundaries() == 0) || (pu.cs->picHeader->getVirtualBoundariesPosX(0) % pu.cs->sps->getMaxCUWidth() == 0);
+    allCandSolidInAbove = allCandSolidInAbove && vbOnCtuBoundary;
+  }
+#endif
+
   for (int mrgIdx = 1; mrgIdx <= num_allowedCand; mrgIdx++)
   {
     if (info.numCand >= AMVP_MAX_NUM_CANDS)
@@ -2364,6 +3124,15 @@ void PU::addAMVPHMVPCand(const PredictionUnit &pu, const RefPicList eRefPicList,
         Mv pmv = neibMi.mv[eRefPicListIndex];
         pmv.roundTransPrecInternal2Amvr(pu.cu->imv);
 
+#if GDR_ENABLED
+        if (isEncodeClean) 
+        {
+          info.mvPos[info.numCand]   = neibMi.soPos;
+          info.mvType[info.numCand]  = MVP_HMVP;
+          info.mvSolid[info.numCand] = allCandSolidInAbove && vbOnCtuBoundary; //  cs.isClean(neibMi.soPos, CHANNEL_TYPE_LUMA);
+          allCandSolidInAbove = allCandSolidInAbove && vbOnCtuBoundary;
+        }
+#endif
         info.mvCand[info.numCand++] = pmv;
         if (info.numCand >= AMVP_MAX_NUM_CANDS)
         {
@@ -2590,6 +3359,9 @@ void PU::getAffineMergeCand( const PredictionUnit &pu, AffineMergeCtx& affMrgCtx
   const Slice &slice = *pu.cs->slice;
   const uint32_t maxNumAffineMergeCand = slice.getPicHeader()->getMaxNumAffineMergeCand();
   const unsigned plevel = pu.cs->sps->getLog2ParallelMergeLevelMinus2() + 2;
+#if GDR_ENABLED
+  const bool isEncodeClean = cs.pcv->isEncoder && ((cs.picHeader->getInGdrPeriod() && cs.isClean(pu.Y().topRight(), CHANNEL_TYPE_LUMA)) || (cs.picHeader->getNumVerVirtualBoundaries() == 0));
+#endif
 
   for ( int i = 0; i < maxNumAffineMergeCand; i++ )
   {
@@ -2597,12 +3369,33 @@ void PU::getAffineMergeCand( const PredictionUnit &pu, AffineMergeCtx& affMrgCtx
     {
       affMrgCtx.mvFieldNeighbours[(i << 1) + 0][mvNum].setMvField( Mv(), -1 );
       affMrgCtx.mvFieldNeighbours[(i << 1) + 1][mvNum].setMvField( Mv(), -1 );
+#if GDR_ENABLED
+      if (isEncodeClean) 
+      {
+        affMrgCtx.mvSolid[(i << 1) + 0][mvNum] = true;
+        affMrgCtx.mvSolid[(i << 1) + 1][mvNum] = true;
+        affMrgCtx.mvValid[(i << 1) + 0][mvNum] = true;
+        affMrgCtx.mvValid[(i << 1) + 1][mvNum] = true;
+      }
+#endif
     }
     affMrgCtx.interDirNeighbours[i] = 0;
     affMrgCtx.affineType[i] = AFFINEMODEL_4PARAM;
     affMrgCtx.mergeType[i] = MRG_TYPE_DEFAULT_N;
     affMrgCtx.BcwIdx[i] = BCW_DEFAULT;
   }
+#if GDR_ENABLED
+  if (isEncodeClean) 
+  {
+    MergeCtx &mrgCtx = *affMrgCtx.mrgCtx;
+    int numMergeCand = MRG_MAX_NUM_CANDS << 1;
+    for (int i = 0; i < numMergeCand; i++) 
+    {
+      mrgCtx.mvSolid[i] = true;
+      mrgCtx.mvValid[i] = true;
+    }
+  }
+#endif
 
   affMrgCtx.numValidMergeCand = 0;
   affMrgCtx.maxNumMergeCand = maxNumAffineMergeCand;
@@ -2639,6 +3432,14 @@ void PU::getAffineMergeCand( const PredictionUnit &pu, AffineMergeCtx& affMrgCtx
       {
         mrgCtx.mvFieldNeighbours[(pos << 1) + 1].setMvField( miLeft.mv[1], miLeft.refIdx[1] );
       }
+#if GDR_ENABLED
+      // check if the (puLeft) is in clean area
+      if (isEncodeClean) 
+      {
+        mrgCtx.mvSolid[(pos << 1) + 0] = cs.isClean(puLeft->Y().bottomRight(), CHANNEL_TYPE_LUMA);
+        mrgCtx.mvSolid[(pos << 1) + 1] = cs.isClean(puLeft->Y().bottomRight(), CHANNEL_TYPE_LUMA);
+      }
+#endif
       pos++;
     }
 
@@ -2651,6 +3452,13 @@ void PU::getAffineMergeCand( const PredictionUnit &pu, AffineMergeCtx& affMrgCtx
       {
         affMrgCtx.mvFieldNeighbours[(affMrgCtx.numValidMergeCand << 1) + 0][mvNum].setMvField( mrgCtx.mvFieldNeighbours[(pos << 1) + 0].mv, mrgCtx.mvFieldNeighbours[(pos << 1) + 0].refIdx );
         affMrgCtx.mvFieldNeighbours[(affMrgCtx.numValidMergeCand << 1) + 1][mvNum].setMvField( mrgCtx.mvFieldNeighbours[(pos << 1) + 1].mv, mrgCtx.mvFieldNeighbours[(pos << 1) + 1].refIdx );
+#if GDR_ENABLED
+        if (isEncodeClean) 
+        {
+          affMrgCtx.mvSolid[(affMrgCtx.numValidMergeCand << 1) + 0][mvNum] = mrgCtx.mvSolid[(pos << 1) + 0];
+          affMrgCtx.mvSolid[(affMrgCtx.numValidMergeCand << 1) + 1][mvNum] = mrgCtx.mvSolid[(pos << 1) + 1];
+        }
+#endif
       }
       affMrgCtx.interDirNeighbours[affMrgCtx.numValidMergeCand] = mrgCtx.interDirNeighbours[pos];
 
@@ -2681,17 +3489,44 @@ void PU::getAffineMergeCand( const PredictionUnit &pu, AffineMergeCtx& affMrgCtx
     {
       // derive Mv from Neigh affine PU
       Mv cMv[2][3];
+#if GDR_ENABLED
+      bool    cMvSolid[2][3] = { {true, true, true}, {true, true, true} };
+      MvpType cMvType[2][3];
+      Position cMvPos[2][3];
+#endif
       const PredictionUnit* puNeigh = npu[idx];
       pu.cu->affineType = puNeigh->cu->affineType;
       if ( puNeigh->interDir != 2 )
       {
-        xInheritedAffineMv( pu, puNeigh, REF_PIC_LIST_0, cMv[0] );
+#if GDR_ENABLED
+        if (isEncodeClean) 
+        {
+          xInheritedAffineMv(pu, puNeigh, REF_PIC_LIST_0, cMv[0], cMvSolid[0], cMvType[0], cMvPos[0]);
+        }
+        else 
+        {
+          xInheritedAffineMv(pu, puNeigh, REF_PIC_LIST_0, cMv[0]);
+        }
+#else
+        xInheritedAffineMv(pu, puNeigh, REF_PIC_LIST_0, cMv[0]);
+#endif
       }
       if ( slice.isInterB() )
       {
         if ( puNeigh->interDir != 1 )
         {
-          xInheritedAffineMv( pu, puNeigh, REF_PIC_LIST_1, cMv[1] );
+#if GDR_ENABLED
+          if (isEncodeClean) 
+          {
+            xInheritedAffineMv(pu, puNeigh, REF_PIC_LIST_1, cMv[1], cMvSolid[1], cMvType[1], cMvPos[1]);
+          }
+          else 
+          {
+            xInheritedAffineMv(pu, puNeigh, REF_PIC_LIST_1, cMv[1]);
+          }
+#else
+          xInheritedAffineMv(pu, puNeigh, REF_PIC_LIST_1, cMv[1]);
+#endif
         }
       }
 
@@ -2699,6 +3534,13 @@ void PU::getAffineMergeCand( const PredictionUnit &pu, AffineMergeCtx& affMrgCtx
       {
         affMrgCtx.mvFieldNeighbours[(affMrgCtx.numValidMergeCand << 1) + 0][mvNum].setMvField( cMv[0][mvNum], puNeigh->refIdx[0] );
         affMrgCtx.mvFieldNeighbours[(affMrgCtx.numValidMergeCand << 1) + 1][mvNum].setMvField( cMv[1][mvNum], puNeigh->refIdx[1] );
+#if GDR_ENABLED
+        if (isEncodeClean) 
+        {
+          affMrgCtx.mvSolid[(affMrgCtx.numValidMergeCand << 1) + 0][mvNum] = cMvSolid[0][mvNum];
+          affMrgCtx.mvSolid[(affMrgCtx.numValidMergeCand << 1) + 1][mvNum] = cMvSolid[0][mvNum];
+        }
+#endif
       }
       affMrgCtx.interDirNeighbours[affMrgCtx.numValidMergeCand] = puNeigh->interDir;
       affMrgCtx.affineType[affMrgCtx.numValidMergeCand] = (EAffineModel)(puNeigh->cu->affineType);
@@ -2726,6 +3568,9 @@ void PU::getAffineMergeCand( const PredictionUnit &pu, AffineMergeCtx& affMrgCtx
       int8_t neighBcw[2] = { BCW_DEFAULT, BCW_DEFAULT };
       // control point: LT B2->B3->A2
       const Position posLT[3] = { pu.Y().topLeft().offset( -1, -1 ), pu.Y().topLeft().offset( 0, -1 ), pu.Y().topLeft().offset( -1, 0 ) };
+#if GDR_ENABLED
+      bool miSolid[4] = { false, false, false, false };
+#endif
       for ( int i = 0; i < 3; i++ )
       {
         const Position pos = posLT[i];
@@ -2736,6 +3581,12 @@ void PU::getAffineMergeCand( const PredictionUnit &pu, AffineMergeCtx& affMrgCtx
           isAvailable[0] = true;
           mi[0] = puNeigh->getMotionInfo( pos );
           neighBcw[0] = puNeigh->cu->BcwIdx;
+#if GDR_ENABLED
+          if (isEncodeClean) 
+          {
+            miSolid[0] = cs.isClean(puNeigh->Y().topRight(), CHANNEL_TYPE_LUMA);
+          }
+#endif
           break;
         }
       }
@@ -2752,6 +3603,12 @@ void PU::getAffineMergeCand( const PredictionUnit &pu, AffineMergeCtx& affMrgCtx
           isAvailable[1] = true;
           mi[1] = puNeigh->getMotionInfo( pos );
           neighBcw[1] = puNeigh->cu->BcwIdx;
+#if GDR_ENABLED
+          if (isEncodeClean) 
+          {
+            miSolid[1] = cs.isClean(puNeigh->Y().topRight(), CHANNEL_TYPE_LUMA);
+          }
+#endif
           break;
         }
       }
@@ -2767,6 +3624,12 @@ void PU::getAffineMergeCand( const PredictionUnit &pu, AffineMergeCtx& affMrgCtx
         {
           isAvailable[2] = true;
           mi[2] = puNeigh->getMotionInfo( pos );
+#if GDR_ENABLED
+          if (isEncodeClean) 
+          {
+            miSolid[2] = cs.isClean(puNeigh->Y().topRight(), CHANNEL_TYPE_LUMA);
+          }
+#endif
           break;
         }
       }
@@ -2808,6 +3671,15 @@ void PU::getAffineMergeCand( const PredictionUnit &pu, AffineMergeCtx& affMrgCtx
           mi[3].refIdx[0] = refIdx;
           mi[3].interDir = 1;
           isAvailable[3] = true;
+#if GDR_ENABLED
+          if (isEncodeClean) 
+          {
+            bool posL0inCurPic_solid = cs.isClean(posC0, CHANNEL_TYPE_LUMA);
+            bool posL0inRefPic_solid = cs.isClean(posC0, REF_PIC_LIST_0, refIdx);
+
+            miSolid[3] = posL0inCurPic_solid && posL0inRefPic_solid;
+          }
+#endif
         }
 
         if ( slice.isInterB() )
@@ -2819,6 +3691,15 @@ void PU::getAffineMergeCand( const PredictionUnit &pu, AffineMergeCtx& affMrgCtx
             mi[3].refIdx[1] = refIdx;
             mi[3].interDir |= 2;
             isAvailable[3] = true;
+#if GDR_ENABLED
+            if (isEncodeClean) 
+            {
+              bool posL1inCurPic_solid = cs.isClean(posC0, CHANNEL_TYPE_LUMA);
+              bool posL1inRefPic_solid = cs.isClean(posC0, REF_PIC_LIST_1, refIdx);
+              
+              miSolid[3] = (mi[3].interDir & 1) ? (miSolid[3] && posL1inCurPic_solid && posL1inRefPic_solid) : (posL1inCurPic_solid && posL1inRefPic_solid);
+            }
+#endif
           }
         }
       }
@@ -2835,12 +3716,36 @@ void PU::getAffineMergeCand( const PredictionUnit &pu, AffineMergeCtx& affMrgCtx
         { 0, 2 },             // 5:  LT, LB
       };
 
+#if GDR_ENABLED     
+      bool model_solid[6] = 
+      {
+        miSolid[0] && miSolid[1] && miSolid[2],
+        miSolid[0] && miSolid[1] && miSolid[3],
+        miSolid[0] && miSolid[2] && miSolid[3],
+        miSolid[1] && miSolid[2] && miSolid[3],
+        miSolid[0] && miSolid[1],
+        miSolid[0] && miSolid[2]
+      };
+#endif
       int verNum[6] = { 3, 3, 3, 3, 2, 2 };
       int startIdx = pu.cs->sps->getUseAffineType() ? 0 : 4;
       for ( int idx = startIdx; idx < modelNum; idx++ )
       {
         int modelIdx = order[idx];
+#if GDR_ENABLED       
+        int affinNumValidCand = affMrgCtx.numValidMergeCand;        
+#endif
         getAffineControlPointCand(pu, mi, isAvailable, model[modelIdx], ((modelIdx == 3) ? neighBcw[1] : neighBcw[0]), modelIdx, verNum[modelIdx], affMrgCtx);
+#if GDR_ENABLED
+        if (isEncodeClean) 
+        {
+          for (int i = 0; i < 3; i++)  
+          {
+            affMrgCtx.mvSolid[(affinNumValidCand << 1) + 0][i] = model_solid[modelIdx];
+            affMrgCtx.mvSolid[(affinNumValidCand << 1) + 1][i] = model_solid[modelIdx];
+          }
+        }
+#endif
         if ( affMrgCtx.numValidMergeCand != 0 && affMrgCtx.numValidMergeCand - 1 == mrgCandIdx )
         {
           return;
@@ -2863,6 +3768,12 @@ void PU::getAffineMergeCand( const PredictionUnit &pu, AffineMergeCtx& affMrgCtx
     for ( int mvNum = 0; mvNum < 3; mvNum++ )
     {
       affMrgCtx.mvFieldNeighbours[(cnt << 1) + 0][mvNum].setMvField( Mv( 0, 0 ), 0 );
+#if GDR_ENABLED
+      if (isEncodeClean) 
+      {
+        affMrgCtx.mvSolid[(cnt << 1) + 0][mvNum] = true;
+      }
+#endif
     }
     affMrgCtx.interDirNeighbours[cnt] = 1;
 
@@ -2871,6 +3782,12 @@ void PU::getAffineMergeCand( const PredictionUnit &pu, AffineMergeCtx& affMrgCtx
       for ( int mvNum = 0; mvNum < 3; mvNum++ )
       {
         affMrgCtx.mvFieldNeighbours[(cnt << 1) + 1][mvNum].setMvField( Mv( 0, 0 ), 0 );
+#if GDR_ENABLED
+        if (isEncodeClean) 
+        {
+          affMrgCtx.mvSolid[(cnt << 1) + 1][mvNum] = true;
+        }
+#endif
       }
       affMrgCtx.interDirNeighbours[cnt] = 3;
     }
@@ -3007,15 +3924,32 @@ bool PU::getInterMergeSubPuMvpCand(const PredictionUnit &pu, MergeCtx& mrgCtx, b
   const Picture *pColPic = slice.getRefPic(RefPicList(slice.isInterB() ? 1 - slice.getColFromL0Flag() : 0), slice.getColRefIdx());
   Mv cTMv;
 
+#if GDR_ENABLED
+  const CodingStructure& cs = *pu.cs;
+  const bool isEncodeClean = cs.pcv->isEncoder && ((cs.picHeader->getInGdrPeriod() && cs.isClean(pu.Y().topRight(), CHANNEL_TYPE_LUMA)) || (cs.picHeader->getNumVerVirtualBoundaries() == 0));
+  bool isSubPuSolid[2] = { true, true };
+#endif
   if ( count )
   {
     if ( (mrgCtx.interDirNeighbours[0] & (1 << REF_PIC_LIST_0)) && slice.getRefPic( REF_PIC_LIST_0, mrgCtx.mvFieldNeighbours[REF_PIC_LIST_0].refIdx ) == pColPic )
     {
       cTMv = mrgCtx.mvFieldNeighbours[REF_PIC_LIST_0].mv;
+#if GDR_ENABLED
+      if (isEncodeClean) 
+      {
+        isSubPuSolid[REF_PIC_LIST_0] = mrgCtx.mvSolid[REF_PIC_LIST_0];
+      }
+#endif  
     }
     else if ( slice.isInterB() && (mrgCtx.interDirNeighbours[0] & (1 << REF_PIC_LIST_1)) && slice.getRefPic( REF_PIC_LIST_1, mrgCtx.mvFieldNeighbours[REF_PIC_LIST_1].refIdx ) == pColPic )
     {
       cTMv = mrgCtx.mvFieldNeighbours[REF_PIC_LIST_1].mv;
+#if GDR_ENABLED
+      if (isEncodeClean) 
+      {
+        isSubPuSolid[REF_PIC_LIST_1] = mrgCtx.mvSolid[REF_PIC_LIST_1];
+      }
+#endif
     }
   }
 
@@ -3071,6 +4005,12 @@ bool PU::getInterMergeSubPuMvpCand(const PredictionUnit &pu, MergeCtx& mrgCtx, b
         // set as default, for further motion vector field spanning
         mrgCtx.mvFieldNeighbours[(count << 1) + currRefListId].setMvField(cColMv, 0);
         mrgCtx.interDirNeighbours[count] |= (1 << currRefListId);
+#if GDR_ENABLED
+        if (isEncodeClean) 
+        {
+          mrgCtx.mvSolid[(count << 1) + currRefListId] = cs.isClean(centerPos, currRefPicList, refIdx);
+        }
+#endif       
         LICFlag = tempLICFlag;
         mrgCtx.BcwIdx[count] = BCW_DEFAULT;
         found = true;
@@ -3124,6 +4064,12 @@ bool PU::getInterMergeSubPuMvpCand(const PredictionUnit &pu, MergeCtx& mrgCtx, b
               mi.refIdx[currRefListId] = 0;
               mi.mv[currRefListId]     = cColMv;
               found                    = true;
+#if GDR_ENABLED              
+              if (isEncodeClean) 
+              {
+                isSubPuSolid[currRefPicList] = isSubPuSolid[currRefPicList] && cs.isClean(colPos, currRefPicList, refIdx);
+              }
+#endif
             }
           }
         }
@@ -3149,6 +4095,16 @@ bool PU::getInterMergeSubPuMvpCand(const PredictionUnit &pu, MergeCtx& mrgCtx, b
       }
     }
   }
+
+#if GDR_ENABLED              
+  if (isEncodeClean) 
+  {
+    // the final if it is solid
+    mrgCtx.mvSolid[(count << 1) + 0] = mrgCtx.mvSolid[(count << 1) + 0] && isSubPuSolid[0];
+    mrgCtx.mvSolid[(count << 1) + 1] = mrgCtx.mvSolid[(count << 1) + 1] && isSubPuSolid[1];
+  }
+#endif
+
   return true;
 }
 
@@ -3326,6 +4282,10 @@ void PU::getGeoMergeCandidates( const PredictionUnit &pu, MergeCtx& geoMrgCtx )
   const uint32_t maxNumMergeCand = pu.cs->sps->getMaxNumMergeCand();
   geoMrgCtx.numValidMergeCand = 0;
 
+#if GDR_ENABLED  
+  CodingStructure &cs = *pu.cs;
+  const bool isEncodeClean = cs.pcv->isEncoder && ((cs.picHeader->getInGdrPeriod() && cs.isClean(pu.Y().topRight(), CHANNEL_TYPE_LUMA)) || (cs.picHeader->getNumVerVirtualBoundaries() == 0));
+#endif
   for (int32_t i = 0; i < GEO_MAX_NUM_UNI_CANDS; i++)
   {
     geoMrgCtx.BcwIdx[i] = BCW_DEFAULT;
@@ -3335,6 +4295,13 @@ void PU::getGeoMergeCandidates( const PredictionUnit &pu, MergeCtx& geoMrgCtx )
     geoMrgCtx.mvFieldNeighbours[(i << 1) + 1].refIdx = NOT_VALID;
     geoMrgCtx.mvFieldNeighbours[(i << 1)].mv = Mv();
     geoMrgCtx.mvFieldNeighbours[(i << 1) + 1].mv = Mv();
+#if GDR_ENABLED
+    if (isEncodeClean) 
+    {
+      geoMrgCtx.mvSolid[(i << 1) + 0] = true;
+      geoMrgCtx.mvSolid[(i << 1) + 1] = true;
+    }
+#endif
     geoMrgCtx.useAltHpelIf[i] = false;
   }
 
@@ -3351,6 +4318,18 @@ void PU::getGeoMergeCandidates( const PredictionUnit &pu, MergeCtx& geoMrgCtx )
       geoMrgCtx.mvFieldNeighbours[(geoMrgCtx.numValidMergeCand << 1) + parity].mv = tmpMergeCtx.mvFieldNeighbours[(i << 1) + parity].mv;
       geoMrgCtx.mvFieldNeighbours[(geoMrgCtx.numValidMergeCand << 1) + !parity].refIdx = -1;
       geoMrgCtx.mvFieldNeighbours[(geoMrgCtx.numValidMergeCand << 1) + parity].refIdx = tmpMergeCtx.mvFieldNeighbours[(i << 1) + parity].refIdx;
+#if GDR_ENABLED
+      if (isEncodeClean) 
+      {
+        Mv  mv = tmpMergeCtx.mvFieldNeighbours[(i << 1) + parity].mv;
+        int refIdx = tmpMergeCtx.mvFieldNeighbours[(i << 1) + parity].refIdx;
+        RefPicList refPicList = parity ? REF_PIC_LIST_1 : REF_PIC_LIST_0;
+        geoMrgCtx.mvSolid[(geoMrgCtx.numValidMergeCand << 1) + !parity] = true;
+        geoMrgCtx.mvSolid[(geoMrgCtx.numValidMergeCand << 1) + parity] = tmpMergeCtx.mvSolid[(i << 1) + parity];
+        geoMrgCtx.mvValid[(geoMrgCtx.numValidMergeCand << 1) + !parity] = true;
+        geoMrgCtx.mvValid[(geoMrgCtx.numValidMergeCand << 1) + parity] = cs.isClean(pu.Y().bottomRight(), mv, refPicList, refIdx);
+      }
+#endif
       geoMrgCtx.numValidMergeCand++;
       if (geoMrgCtx.numValidMergeCand == GEO_MAX_NUM_UNI_CANDS)
       {
@@ -3367,6 +4346,18 @@ void PU::getGeoMergeCandidates( const PredictionUnit &pu, MergeCtx& geoMrgCtx )
       geoMrgCtx.mvFieldNeighbours[(geoMrgCtx.numValidMergeCand << 1) + parity].mv = Mv(0, 0);
       geoMrgCtx.mvFieldNeighbours[(geoMrgCtx.numValidMergeCand << 1) + !parity].refIdx = tmpMergeCtx.mvFieldNeighbours[(i << 1) + !parity].refIdx;
       geoMrgCtx.mvFieldNeighbours[(geoMrgCtx.numValidMergeCand << 1) + parity].refIdx = -1;
+#if GDR_ENABLED
+      if (isEncodeClean) 
+      {
+        Mv  mv = tmpMergeCtx.mvFieldNeighbours[(i << 1) + !parity].mv;
+        int refIdx = tmpMergeCtx.mvFieldNeighbours[(i << 1) + !parity].refIdx;
+        RefPicList refPicList = (!parity) ? REF_PIC_LIST_1 : REF_PIC_LIST_0;
+        geoMrgCtx.mvSolid[(geoMrgCtx.numValidMergeCand << 1) + !parity] = tmpMergeCtx.mvSolid[(i << 1) + !parity];
+        geoMrgCtx.mvSolid[(geoMrgCtx.numValidMergeCand << 1) + parity] = true;
+        geoMrgCtx.mvValid[(geoMrgCtx.numValidMergeCand << 1) + !parity] = cs.isClean(pu.Y().bottomRight(), mv, refPicList, refIdx);;
+        geoMrgCtx.mvValid[(geoMrgCtx.numValidMergeCand << 1) + parity] = true;
+      }
+#endif
       geoMrgCtx.numValidMergeCand++;
       if (geoMrgCtx.numValidMergeCand == GEO_MAX_NUM_UNI_CANDS)
       {
diff --git a/source/Lib/CommonLib/UnitTools.h b/source/Lib/CommonLib/UnitTools.h
index 3ec4a6566ee42386fee58de5ff86a2b8f7eb0548..766205bd6ce317439509d2f0b1f1d705ddba70d8 100644
--- a/source/Lib/CommonLib/UnitTools.h
+++ b/source/Lib/CommonLib/UnitTools.h
@@ -148,11 +148,18 @@ namespace PU
   void fillIBCMvpCand                 (PredictionUnit &pu, AMVPInfo &amvpInfo);
   void fillAffineMvpCand              (      PredictionUnit &pu, const RefPicList &eRefPicList, const int &refIdx, AffineAMVPInfo &affiAMVPInfo);
   bool addMVPCandUnscaled             (const PredictionUnit &pu, const RefPicList &eRefPicList, const int &iRefIdx, const Position &pos, const MvpDir &eDir, AMVPInfo &amvpInfo);
+#if GDR_ENABLED
+  void xInheritedAffineMv(const PredictionUnit &pu, const PredictionUnit* puNeighbour, RefPicList eRefPicList, Mv rcMv[3], bool rcMvSolid[3], MvpType rcMvType[3], Position rcMvPos[3]);  
+#endif
   void xInheritedAffineMv             ( const PredictionUnit &pu, const PredictionUnit* puNeighbour, RefPicList eRefPicList, Mv rcMv[3] );
   bool addMergeHMVPCand               (const CodingStructure &cs, MergeCtx& mrgCtx, const int& mrgCandIdx, const uint32_t maxNumMergeCandMin1, int &cnt
     , const bool isAvailableA1, const MotionInfo miLeft, const bool isAvailableB1, const MotionInfo miAbove
     , const bool ibcFlag
     , const bool isGt4x4
+#if GDR_ENABLED
+    , const PredictionUnit &pu
+    , bool &addMergeHMVPCand
+#endif
   );
   void addAMVPHMVPCand                (const PredictionUnit &pu, const RefPicList eRefPicList, const int currRefPOC, AMVPInfo &info);
   bool addAffineMVPCandUnscaled       ( const PredictionUnit &pu, const RefPicList &refPicList, const int &refIdx, const Position &pos, const MvpDir &dir, AffineAMVPInfo &affiAmvpInfo );
diff --git a/source/Lib/CommonLib/dtrace_blockstatistics.cpp b/source/Lib/CommonLib/dtrace_blockstatistics.cpp
index d92ae1e17a27bdf137349fbaa21b0d8258de8f8e..15b349e0885ede3c34ee660aea9d6f7d304273ec 100644
--- a/source/Lib/CommonLib/dtrace_blockstatistics.cpp
+++ b/source/Lib/CommonLib/dtrace_blockstatistics.cpp
@@ -902,13 +902,21 @@ void writeAllData(const CodingStructure& cs, const UnitArea& ctuArea)
                 {
                   const uint32_t uiChFinalMode  = PU::getFinalIntraMode( pu, ChannelType( chType ) );
                   DTRACE_BLOCK_SCALAR_CHROMA(g_trace_ctx, D_BLOCK_STATISTICS_ALL, pu, GetBlockStatisticName(BlockStatistic::Chroma_IntraMode), uiChFinalMode);
+#if !GDR_ENABLED
                     assert(0);
+#endif
                 }
               }
             }
           }
         }
         break;
+#if GDR_ENABLED
+      case MODE_IBC:
+      case MODE_PLT:
+        // note: not implemented yet
+        break;
+#endif
       default:
         THROW( "Invalid prediction mode" );
         break;
diff --git a/source/Lib/DecoderLib/DecLib.cpp b/source/Lib/DecoderLib/DecLib.cpp
index 67132dbe0a5db5dba92e2acdf0a7451ccbce527c..930cd9e81168a4018fddce788aa6c7bdd16601a3 100644
--- a/source/Lib/DecoderLib/DecLib.cpp
+++ b/source/Lib/DecoderLib/DecLib.cpp
@@ -837,7 +837,11 @@ void DecLib::finishPicture(int &poc, PicList *&rpcListPic, MsgLevel msgl, bool a
   m_pcPic->destroyTempBuffers();
   m_pcPic->cs->destroyCoeffs();
   m_pcPic->cs->releaseIntermediateData();
+#if GDR_ENABLED
+  m_picHeader.initPicHeader();
+#else
   m_pcPic->cs->picHeader->initPicHeader();
+#endif
   m_puCounter++;
 }
 
@@ -1583,7 +1587,14 @@ void DecLib::xActivateParameterSets( const InputNALUnit nalu )
     //  Get a new picture buffer. This will also set up m_pcPic, and therefore give us a SPS and PPS pointer that we can use.
     m_pcPic = xGetNewPicBuffer( *sps, *pps, m_apcSlicePilot->getTLayer(), layerId );
 
+#if GDR_ENABLED
+    PicHeader *picHeader = new PicHeader;
+    *picHeader = m_picHeader;
+    m_apcSlicePilot->setPicHeader(picHeader);
+    m_pcPic->finalInit(vps, *sps, *pps, picHeader, apss, lmcsAPS, scalinglistAPS);
+#else
     m_pcPic->finalInit( vps, *sps, *pps, &m_picHeader, apss, lmcsAPS, scalinglistAPS );
+#endif
     m_pcPic->createTempBuffers( m_pcPic->cs->pps->pcv->maxCUWidth );
     m_pcPic->cs->createCoeffs((bool)m_pcPic->cs->sps->getPLTMode());
 
@@ -2769,6 +2780,26 @@ bool DecLib::xDecodeSlice(InputNALUnit &nalu, int &iSkipFrame, int iPOCLastDispl
     m_cReshaper.setRecReshaped(false);
   }
 
+#if GDR_LEAK_TEST  
+  if (m_POC_RandomAccess == pcSlice->getPOC())
+  {
+    for (int e = 0; e < 2; e++)
+    {
+      for (int ridx = 0; ridx < 4; ridx++)
+      {
+        Picture *pic = pcSlice->getRefPic((RefPicList)e, ridx);
+        if (pic)
+        {
+          CodingStructure& cs = *pic->cs;
+          cs.getRecoBuf().Y().fill(0 * 4); // for 8-bit sequence
+          cs.getRecoBuf().Cb().fill(0 * 4);
+          cs.getRecoBuf().Cr().fill(0 * 4);
+          cs.getMotionBuf().memset(0);    // clear MV storage
+        }
+      }
+    }
+  }
+#endif // GDR_LEAK_TEST
   //  Decode a picture
   m_cSliceDecoder.decompressSlice( pcSlice, &( nalu.getBitstream() ), ( m_pcPic->poc == getDebugPOC() ? getDebugCTU() : -1 ) );
 
diff --git a/source/Lib/DecoderLib/DecLib.h b/source/Lib/DecoderLib/DecLib.h
index 9b53f1d62bfc7f0b4f5ab48d2bda12ec3ede0d42..78deaba84d51f859789c354b6f2c16fb796ae6ec 100644
--- a/source/Lib/DecoderLib/DecLib.h
+++ b/source/Lib/DecoderLib/DecLib.h
@@ -204,6 +204,11 @@ public:
 
   DCI*                    m_dci;
   ParameterSetMap<APS>*   m_apsMapEnc;
+#if GDR_LEAK_TEST
+public:
+  int                     m_POC_RandomAccess;
+#endif // GDR_LEAK_TEST
+
 public:
   DecLib();
   virtual ~DecLib();
diff --git a/source/Lib/DecoderLib/VLCReader.cpp b/source/Lib/DecoderLib/VLCReader.cpp
index aa9688a2fb149fc877f7b4cd92146f2da76f4dc2..8b330e07ffcc3544af04cd5836ad4e00306d903a 100644
--- a/source/Lib/DecoderLib/VLCReader.cpp
+++ b/source/Lib/DecoderLib/VLCReader.cpp
@@ -280,6 +280,9 @@ void FDReader::parseFillerData(InputBitstream* bs, uint32_t &fdSize)
 
 HLSyntaxReader::HLSyntaxReader()
 {
+#if GDR_ENABLED
+  m_last_gdr_poc = -1;
+#endif
 }
 
 HLSyntaxReader::~HLSyntaxReader()
@@ -2801,6 +2804,11 @@ void HLSyntaxReader::parsePictureHeader( PicHeader* picHeader, ParameterSetManag
         READ_UVLC(uiCode, "ph_virtual_boundary_pos_x_minus1[i]");        picHeader->setVirtualBoundariesPosX((uiCode + 1) << 3, i);
         CHECK(uiCode > (((pps->getPicWidthInLumaSamples() + 7) >> 3) - 2), "The value of ph_virtual_boundary_pos_x_minus1[ i ] shall be in the range of 0 to Ceil( pps_pic_width_in_luma_samples / 8 ) - 2, inclusive.");
       }
+#if GDR_DEC_TRACE
+      printf("\n");
+      printf("-num_ver_boundary :%d\n", picHeader->getNumVerVirtualBoundaries());
+      printf("-vir_boundary_pos :%d\n", picHeader->getVirtualBoundariesPosX(0));
+#endif
       READ_UVLC(uiCode, "ph_num_hor_virtual_boundaries");        picHeader->setNumHorVirtualBoundaries( uiCode );
       if (pps->getPicHeightInLumaSamples() <= 8)
       {
@@ -4327,6 +4335,37 @@ void HLSyntaxReader::parseSliceHeader (Slice* pcSlice, PicHeader* picHeader, Par
       pcSlice->addSubstreamSize(entryPointOffset [ idx ] );
     }
   }
+#if GDR_ENABLED
+  int cur_poc = pcSlice->getPOC();
+
+  if (picHeader->getGdrPicFlag())
+  {
+    setLastGdrPoc(cur_poc);
+    setLastGdrRecoveryPocCnt(pcSlice->getPicHeader()->getRecoveryPocCnt());
+  }
+
+  int recovery_poc_cnt = getLastGdrRecoveryPocCnt();
+
+  if (getLastGdrPoc() > 0 && (getLastGdrPoc() <= cur_poc) && (cur_poc < (getLastGdrPoc() + recovery_poc_cnt))) 
+  {
+    picHeader->setInGdrPeriod(true);
+  }  
+  else 
+  {
+    picHeader->setInGdrPeriod(false);
+  }
+#endif
+
+#if GDR_DEC_TRACE
+  printf("-gdr_pic_flag:%d\n", picHeader->getGdrPicFlag() ? 1 : 0);
+  printf("-recovery_poc_cnt:%d\n", picHeader->getRecoveryPocCnt());
+#if GDR_ENABLED
+  printf("-inGdrPeriod:%d\n", picHeader->getInGdrPeriod());
+#endif
+
+  printf("-lmcs_enable : %d\n", picHeader->getLmcsEnabledFlag() ? 1 : 0);
+  printf("-lmcs_chroma : %d\n", picHeader->getLmcsChromaResidualScaleFlag() ? 1 : 0);
+#endif
   return;
 }
 
diff --git a/source/Lib/DecoderLib/VLCReader.h b/source/Lib/DecoderLib/VLCReader.h
index f2299e1a5bce3166fa09a437c21c78627cc233fe..dcbd89ab568dcc57af3b9696a4b999e2198dafc6 100644
--- a/source/Lib/DecoderLib/VLCReader.h
+++ b/source/Lib/DecoderLib/VLCReader.h
@@ -146,6 +146,11 @@ public:
 
 class HLSyntaxReader : public VLCReader
 {
+#if GDR_ENABLED
+  int m_last_gdr_poc;
+  int m_last_gdr_recovery_poc_cnt;
+#endif
+
 public:
   HLSyntaxReader();
   virtual ~HLSyntaxReader();
@@ -155,6 +160,12 @@ protected:
   void  parseRefPicList(SPS* pcSPS, ReferencePictureList* rpl, int rplIdx);
 
 public:
+#if GDR_ENABLED
+  void setLastGdrPoc(int poc) { m_last_gdr_poc = poc;  }
+  int  getLastGdrPoc()        { return m_last_gdr_poc; }
+  void setLastGdrRecoveryPocCnt(int recovery_poc_cnt) { m_last_gdr_recovery_poc_cnt = recovery_poc_cnt; }
+  int  getLastGdrRecoveryPocCnt()                     { return m_last_gdr_recovery_poc_cnt; }
+#endif
   void  setBitstream        ( InputBitstream* p )   { m_pcBitstream = p; }
   void  parseOPI            ( OPI* opi );
   void  parseVPS            ( VPS* pcVPS );
diff --git a/source/Lib/EncoderLib/EncCfg.h b/source/Lib/EncoderLib/EncCfg.h
index 8981657ec8b5051e96aff0489e7492158fa6baf9..d45a816a8e7a4e51224bef754c05a14ea6da6d7c 100644
--- a/source/Lib/EncoderLib/EncCfg.h
+++ b/source/Lib/EncoderLib/EncCfg.h
@@ -302,6 +302,14 @@ protected:
   bool                  m_subPicIdMappingInSpsFlag;
   unsigned              m_subPicIdLen;
   std::vector<uint16_t> m_subPicId;
+#if GDR_ENABLED
+  unsigned  m_GdrPeriod;
+  unsigned  m_GdrPocStart;
+  int       m_GdrFrequency;
+  bool      m_bStartWithGdr;
+  bool      m_bNoHashForGdr;
+  bool      m_bGdrPicOutput;
+#endif
   bool      m_useSplitConsOverride;
   unsigned  m_uiMinQT[3]; //0: I slice; 1: P/B slice, 2: I slice chroma
   unsigned  m_uiMaxBT[3]; //0: I slice; 1: P/B slice, 2: I slice chroma
@@ -996,6 +1004,21 @@ public:
   void      setMinQTSizes                   ( unsigned* minQT)   { m_uiMinQT[0] = minQT[0]; m_uiMinQT[1] = minQT[1]; m_uiMinQT[2] = minQT[2]; }
   void      setMaxBTSizes                   ( unsigned* maxBT)   { m_uiMaxBT[0] = maxBT[0]; m_uiMaxBT[1] = maxBT[1]; m_uiMaxBT[2] = maxBT[2]; }
   void      setMaxTTSizes                   ( unsigned* maxTT)   { m_uiMaxTT[0] = maxTT[0]; m_uiMaxTT[1] = maxTT[1]; m_uiMaxTT[2] = maxTT[2]; }
+#if GDR_ENABLED
+  void      setGdrPeriod(unsigned  u)   { m_GdrPeriod   = u; }
+  void      setGdrPocStart(unsigned  u) { m_GdrPocStart = u; }
+  void      setGdrFrequency(int i)      { m_GdrFrequency = i; }
+  void      setStartWithGdr(bool b)     { m_bStartWithGdr = b; }
+  void      setNoHashForGdr(bool b)     { m_bNoHashForGdr = b; }  
+  void      setGdrPicOutput(bool b)     { m_bGdrPicOutput = b; }
+
+  unsigned  getGdrPeriod()              { return m_GdrPeriod;   }
+  unsigned  getGdrPocStart()            { return m_GdrPocStart; }
+  int       getGdrFrequency()           { return m_GdrFrequency; }
+  bool      getStartWithGdr()           { return m_bStartWithGdr; }
+  bool      getNoHashForGdr()           { return m_bNoHashForGdr; }
+  bool      getGdrPicOutput()           { return m_bGdrPicOutput; }
+#endif
   void      setMaxMTTHierarchyDepth         ( unsigned uiMaxMTTHierarchyDepth, unsigned uiMaxMTTHierarchyDepthI, unsigned uiMaxMTTHierarchyDepthIChroma )
                                                              { m_uiMaxMTTHierarchyDepth = uiMaxMTTHierarchyDepth; m_uiMaxMTTHierarchyDepthI = uiMaxMTTHierarchyDepthI; m_uiMaxMTTHierarchyDepthIChroma = uiMaxMTTHierarchyDepthIChroma; }
   unsigned  getMaxMTTHierarchyDepth         ()         const { return m_uiMaxMTTHierarchyDepth; }
diff --git a/source/Lib/EncoderLib/EncCu.cpp b/source/Lib/EncoderLib/EncCu.cpp
index bbfa1e7fcb71649384139b8ff237f8545a36c0e3..4b482a64cd97aed4118d71ba2c766d45511bff9c 100644
--- a/source/Lib/EncoderLib/EncCu.cpp
+++ b/source/Lib/EncoderLib/EncCu.cpp
@@ -555,6 +555,86 @@ void EncCu::xCompressCU( CodingStructure*& tempCS, CodingStructure*& bestCS, Par
   const UnitArea currCsArea = clipArea( CS::getArea( *bestCS, bestCS->area, partitioner.chType ), *tempCS->picture );
 
   m_modeCtrl->initCULevel( partitioner, *tempCS );
+#if GDR_ENABLED 
+  bool isCurGdrPicture = slice.getPicHeader()->getInGdrPeriod();
+
+  // 1.0 applicable to inter picture only  
+  if (isCurGdrPicture)
+  {
+    int gdrPocStart = m_pcEncCfg->getGdrPocStart();
+    int gdrPeriod = m_pcEncCfg->getGdrPeriod();
+
+    int picWidth = slice.getPPS()->getPicWidthInLumaSamples();
+    int m1, m2, n1;
+
+    int curPoc = slice.getPOC();
+    int gdrPoc = (curPoc - gdrPocStart) % gdrPeriod;
+
+    int begGdrX = 0;
+    int endGdrX = 0;
+
+    double dd = (picWidth / (double)gdrPeriod);
+    int mm = (int)((picWidth / (double)gdrPeriod) + 0.49999);
+    m1 = ((mm + 7) >> 3) << 3;
+    m2 = ((mm + 0) >> 3) << 3;
+
+    if (dd > mm && m1 == m2)
+      m1 = m1 + 8;
+
+    n1 = (picWidth - m2 * gdrPeriod) / 8;
+
+    if (gdrPoc < n1)
+    {
+      begGdrX = m1 * gdrPoc;
+      endGdrX = begGdrX + m1;
+    }
+    else
+    {
+      begGdrX = m1 * n1 + m2 * (gdrPoc - n1);
+      endGdrX = begGdrX + m2;
+      if (picWidth <= endGdrX)
+      {
+        endGdrX = picWidth;
+        endGdrX = picWidth;
+      }
+    }
+
+    bool isInRefreshArea = tempCS->withinRefresh(begGdrX, endGdrX);
+
+    if (isInRefreshArea)
+    {
+      m_modeCtrl->forceIntraMode();
+    }
+    else if (tempCS->containRefresh(begGdrX, endGdrX) || tempCS->overlapRefresh(begGdrX, endGdrX))
+    {
+      // 1.3.1 enable only vertical splits (QT, BT_V, TT_V)                  
+      m_modeCtrl->forceVerSplitOnly();
+
+      // 1.3.2 remove TT_V if it does not satisfy the condition
+      if (tempCS->refreshCrossTTV(begGdrX, endGdrX))
+      {
+        m_modeCtrl->forceRemoveTTV();
+      }
+    }
+
+    if (tempCS->area.lwidth() != tempCS->area.lheight())
+    {
+      m_modeCtrl->forceRemoveQT();
+    }
+
+    if (!m_modeCtrl->anyPredModeLeft())
+    {
+      m_modeCtrl->forceRemoveDontSplit();
+    }
+
+    if (isInRefreshArea && !m_modeCtrl->anyIntraIBCMode() && (tempCS->area.lwidth() == 4 || tempCS->area.lheight() == 4))
+    {
+      m_modeCtrl->finishCULevel(partitioner);
+      return;
+    }
+  }
+#endif
+
   if( partitioner.currQtDepth == 0 && partitioner.currMtDepth == 0 && !tempCS->slice->isIntra() && ( sps.getUseSBT() || sps.getUseInterMTS() ) )
   {
     auto slsSbt = dynamic_cast<SaveLoadEncInfoSbt*>( m_modeCtrl );
@@ -829,7 +909,11 @@ void EncCu::xCompressCU( CodingStructure*& tempCS, CodingStructure*& bestCS, Par
           break;
         }
       }
+#if GDR_ENABLED
+      if (bestCS->cus.size() > 0 && splitmode != bestCS->cus[0]->splitSeries)
+#else
       if (splitmode != bestCS->cus[0]->splitSeries)
+#endif
       {
         splitmode = bestCS->cus[0]->splitSeries;
         const CodingUnit&     cu = *bestCS->cus.front();
@@ -1064,7 +1148,12 @@ void EncCu::xCheckModeSplit(CodingStructure *&tempCS, CodingStructure *&bestCS,
   tempCS->getPredBuf().fill(0);
   AffineMVInfo tmpMVInfo;
   bool isAffMVInfoSaved;
+#if GDR_ENABLED
+  AffineMVInfoSolid tmpMVInfoSolid;
+  m_pcInterSearch->savePrevAffMVInfo(0, tmpMVInfo, tmpMVInfoSolid, isAffMVInfoSaved);
+#else
   m_pcInterSearch->savePrevAffMVInfo(0, tmpMVInfo, isAffMVInfoSaved);
+#endif
   BlkUniMvInfo tmpUniMvInfo;
   bool         isUniMvInfoSaved = false;
   if (!tempCS->slice->isIntra())
@@ -1343,10 +1432,18 @@ void EncCu::xCheckModeSplit(CodingStructure *&tempCS, CodingStructure *&bestCS,
   // RD check for sub partitioned coding structure.
   xCheckBestMode( tempCS, bestCS, partitioner, encTestMode );
 
+#if GDR_ENABLED
+  if (isAffMVInfoSaved)
+  {
+    m_pcInterSearch->addAffMVInfo(tmpMVInfo, tmpMVInfoSolid);
+  }
+#else
   if (isAffMVInfoSaved)
   {
     m_pcInterSearch->addAffMVInfo(tmpMVInfo);
   }
+#endif
+    
   if (!tempCS->slice->isIntra() && isUniMvInfoSaved)
   {
     m_pcInterSearch->addUniMvInfo(tmpUniMvInfo);
@@ -2023,6 +2120,10 @@ void EncCu::xCheckRDCostMerge2Nx2N( CodingStructure *&tempCS, CodingStructure *&
   MergeCtx mergeCtx;
   const SPS &sps = *tempCS->sps;
 
+#if GDR_ENABLED  
+  bool isEncodeClean = false;
+  CodingStructure *cs;
+#endif
   if (sps.getSbTMVPEnabledFlag())
   {
     Size bufSize = g_miScaling.scale( tempCS->area.lumaSize() );
@@ -2046,6 +2147,10 @@ void EncCu::xCheckRDCostMerge2Nx2N( CodingStructure *&tempCS, CodingStructure *&
     PU::getInterMergeCandidates(pu, mergeCtx, 0);
     PU::getInterMMVDMergeCandidates(pu, mergeCtx);
     pu.regularMergeFlag = true;
+#if GDR_ENABLED  
+    cs = pu.cs;
+    isEncodeClean = cs->pcv->isEncoder && ((cs->picHeader->getInGdrPeriod() && cs->isClean(pu.Y().topRight(), CHANNEL_TYPE_LUMA)) || (cs->picHeader->getNumVerVirtualBoundaries() == 0));
+#endif
   }
   bool candHasNoResidual[MRG_MAX_NUM_CANDS + MMVD_ADD_NUM];
   for (uint32_t ui = 0; ui < MRG_MAX_NUM_CANDS + MMVD_ADD_NUM; ui++)
@@ -2210,6 +2315,42 @@ void EncCu::xCheckRDCostMerge2Nx2N( CodingStructure *&tempCS, CodingStructure *&
         uint64_t fracBits = m_pcInterSearch->xCalcPuMeBits(pu);
         double cost = (double)uiSad + (double)fracBits * sqrtLambdaForFirstPassIntra;
         insertPos = -1;
+
+#if GDR_ENABLED
+        // Non-RD cost for regular merge
+        if (isEncodeClean)
+        {
+          bool isSolid = true;
+          bool isValid = true;
+
+          if (mergeCtx.mvFieldNeighbours[(uiMergeCand << 1) + 0].refIdx >= 0)
+          {
+            Mv mv = mergeCtx.mvFieldNeighbours[(uiMergeCand << 1) + 0].mv;
+            int ridx = mergeCtx.mvFieldNeighbours[(uiMergeCand << 1) + 0].refIdx;
+
+            mergeCtx.mvValid[(uiMergeCand << 1) + 0] = cs->isClean(pu.Y().bottomRight(), mv, REF_PIC_LIST_0, ridx);
+
+            isSolid = isSolid && mergeCtx.mvSolid[(uiMergeCand << 1) + 0];
+            isValid = isValid && mergeCtx.mvValid[(uiMergeCand << 1) + 0];
+          }
+
+          if (mergeCtx.mvFieldNeighbours[(uiMergeCand << 1) + 1].refIdx >= 0) \
+          {
+            Mv mv = mergeCtx.mvFieldNeighbours[(uiMergeCand << 1) + 1].mv;
+            int ridx = mergeCtx.mvFieldNeighbours[(uiMergeCand << 1) + 1].refIdx;
+
+            mergeCtx.mvValid[(uiMergeCand << 1) + 1] = cs->isClean(pu.Y().bottomRight(), mv, REF_PIC_LIST_1, ridx);
+
+            isSolid = isSolid && mergeCtx.mvSolid[(uiMergeCand << 1) + 1];
+            isValid = isValid && mergeCtx.mvValid[(uiMergeCand << 1) + 1];
+          }
+
+          if (!isValid || !isSolid)
+          {
+            cost = MAX_DOUBLE;
+          }
+        }
+#endif
         updateCandList(ModeInfo(uiMergeCand, true, false, false), cost, RdModeList, candCostList, uiNumMrgSATDCand, &insertPos);
         if (insertPos != -1)
         {
@@ -2226,7 +2367,9 @@ void EncCu::xCheckRDCostMerge2Nx2N( CodingStructure *&tempCS, CodingStructure *&
             swap(singleMergeTempBuffer, acMergeTempBuffer[insertPos]);
           }
         }
+#if !GDR_ENABLED
         CHECK(std::min(uiMergeCand + 1, uiNumMrgSATDCand) != RdModeList.size(), "");
+#endif
       }
 
       if (isIntrainterEnabled)
@@ -2280,6 +2423,42 @@ void EncCu::xCheckRDCostMerge2Nx2N( CodingStructure *&tempCS, CodingStructure *&
           pu.regularMergeFlag = false;
           uint64_t fracBits = m_pcInterSearch->xCalcPuMeBits(pu);
           double cost = (double)sadValue + (double)fracBits * sqrtLambdaForFirstPassIntra;
+#if GDR_ENABLED
+          // Non-RD cost for CIIP merge
+          if (isEncodeClean)
+          {
+            bool isSolid = true;
+            bool isValid = true;
+
+            if (mergeCtx.mvFieldNeighbours[(mergeCand << 1) + 0].refIdx >= 0)
+            {
+              Mv mv = mergeCtx.mvFieldNeighbours[(mergeCand << 1) + 0].mv;
+              int ridx = mergeCtx.mvFieldNeighbours[(mergeCand << 1) + 0].refIdx;
+
+              mergeCtx.mvValid[(mergeCand << 1) + 0] = cs->isClean(pu.Y().bottomRight(), mv, REF_PIC_LIST_0, ridx);
+
+              isSolid = isSolid && mergeCtx.mvSolid[(mergeCand << 1) + 0];
+              isValid = isValid && mergeCtx.mvValid[(mergeCand << 1) + 0];
+            }
+
+            if (mergeCtx.mvFieldNeighbours[(mergeCand << 1) + 1].refIdx >= 0)
+            {
+              Mv mv = mergeCtx.mvFieldNeighbours[(mergeCand << 1) + 1].mv;
+              int ridx = mergeCtx.mvFieldNeighbours[(mergeCand << 1) + 1].refIdx;
+
+              mergeCtx.mvValid[(mergeCand << 1) + 1] = cs->isClean(pu.Y().bottomRight(), mv, REF_PIC_LIST_1, ridx);
+
+              isSolid = isSolid && mergeCtx.mvSolid[(mergeCand << 1) + 1];
+              isValid = isValid && mergeCtx.mvValid[(mergeCand << 1) + 1];
+            }
+
+            if (!isValid || !isSolid)
+            {
+              cost = MAX_DOUBLE;
+            }
+          }
+#endif
+
           insertPos = -1;
           updateCandList(ModeInfo(mergeCand, false, false, true), cost, RdModeList, candCostList, uiNumMrgSATDCand, &insertPos);
           if (insertPos != -1)
@@ -2306,6 +2485,16 @@ void EncCu::xCheckRDCostMerge2Nx2N( CodingStructure *&tempCS, CodingStructure *&
           {
             continue;
           }
+#if GDR_ENABLED
+          if (isEncodeClean)
+          {
+            pu.mvSolid[REF_PIC_LIST_0] = true;
+            pu.mvSolid[REF_PIC_LIST_1] = true;
+
+            pu.mvValid[REF_PIC_LIST_0] = true;
+            pu.mvValid[REF_PIC_LIST_1] = true;
+          }
+#endif
           mergeCtx.setMmvdMergeCandiInfo(pu, mmvdMergeCand);
 
           PU::spanMotionInfo(pu, mergeCtx);
@@ -2323,6 +2512,35 @@ void EncCu::xCheckRDCostMerge2Nx2N( CodingStructure *&tempCS, CodingStructure *&
           uint64_t fracBits = m_pcInterSearch->xCalcPuMeBits(pu);
           double cost = (double)uiSad + (double)fracBits * sqrtLambdaForFirstPassIntra;
           insertPos = -1;
+
+#if GDR_ENABLED
+          if (isEncodeClean)
+          {
+            bool isSolid = true;
+            bool isValid = true;
+
+            if (pu.refIdx[0] >= 0)
+            {
+              isSolid = isSolid && pu.mvSolid[0];
+              isValid = isValid && pu.mvValid[0];
+            }
+
+            if (pu.refIdx[1] >= 0)
+            {
+              isSolid = isSolid && pu.mvSolid[1];
+              isValid = isValid && pu.mvValid[1];
+            }
+
+            if (!isSolid || !isValid)
+            {
+              cost = MAX_DOUBLE;
+            }
+            else
+            {
+              cost = cost;
+            }
+          }
+#endif
           updateCandList(ModeInfo(mmvdMergeCand, false, true, false), cost, RdModeList, candCostList, uiNumMrgSATDCand, &insertPos);
           if (insertPos != -1)
           {
@@ -2540,7 +2758,36 @@ void EncCu::xCheckRDCostMerge2Nx2N( CodingStructure *&tempCS, CodingStructure *&
         isTestSkipMerge[uiMergeCand] = true;
       }
 
+#if GDR_ENABLED
+      if (isEncodeClean)
+      {
+        bool isSolid = true;
+        bool isValid = true;
+
+        if (pu.refIdx[0] >= 0)
+        {
+          isSolid = isSolid && pu.mvSolid[0];
+          isValid = isValid && pu.mvValid[0];
+        }
+
+        if (pu.refIdx[1] >= 0)
+        {
+          isSolid = isSolid && pu.mvSolid[1];
+          isValid = isValid && pu.mvValid[1];
+        }
+
+        if (isSolid && isValid)
+        {
+          xEncodeInterResidual(tempCS, bestCS, partitioner, encTestMode, uiNoResidualPass, uiNoResidualPass == 0 ? &candHasNoResidual[uiMrgHADIdx] : NULL);
+        }
+      }
+      else
+      {
+        xEncodeInterResidual(tempCS, bestCS, partitioner, encTestMode, uiNoResidualPass, uiNoResidualPass == 0 ? &candHasNoResidual[uiMrgHADIdx] : NULL);
+      }
+#else
       xEncodeInterResidual( tempCS, bestCS, partitioner, encTestMode, uiNoResidualPass, uiNoResidualPass == 0 ? &candHasNoResidual[uiMrgHADIdx] : NULL );
+#endif
 
       if( m_pcEncCfg->getUseFastDecisionForMerge() && !bestIsSkip && !pu.ciipFlag)
       {
@@ -2620,6 +2867,11 @@ void EncCu::xCheckRDCostMergeGeo2Nx2N(CodingStructure *&tempCS, CodingStructure
   cu.bdpcmMode = 0;
 
   PredictionUnit &pu = tempCS->addPU(cu, pm.chType);
+#if GDR_ENABLED  
+  CodingStructure &cs = *pu.cs;
+  const bool isEncodeClean = cs.pcv->isEncoder && ((cs.picHeader->getInGdrPeriod() && cs.isClean(pu.Y().topRight(), CHANNEL_TYPE_LUMA)) || (cs.picHeader->getNumVerVirtualBoundaries() == 0));
+#endif
+  
   pu.mergeFlag = true;
   pu.regularMergeFlag = false;
   PU::getGeoMergeCandidates(pu, mergeCtx);
@@ -2642,6 +2894,21 @@ void EncCu::xCheckRDCostMergeGeo2Nx2N(CodingStructure *&tempCS, CodingStructure
   int        pocMrg[GEO_MAX_NUM_UNI_CANDS];
   Mv         MrgMv[GEO_MAX_NUM_UNI_CANDS];
   bool       isSkipThisCand[GEO_MAX_NUM_UNI_CANDS];
+#if GDR_ENABLED 
+  bool *MrgSolid = nullptr;
+  bool *MrgValid = nullptr;
+
+  if (isEncodeClean) 
+  {
+    MrgSolid = new bool[maxNumMergeCandidates];
+    MrgValid = new bool[maxNumMergeCandidates];
+    for (int i = 0; i < maxNumMergeCandidates; i++) 
+    {
+      MrgSolid[i] = true;
+      MrgValid[i] = true;
+    }
+  }
+#endif
   for (int i = 0; i < maxNumMergeCandidates; i++)
   {
     isSkipThisCand[i] = false;
@@ -2655,6 +2922,13 @@ void EncCu::xCheckRDCostMergeGeo2Nx2N(CodingStructure *&tempCS, CodingStructure
     int MrgrefIdx = mergeCtx.mvFieldNeighbours[(mergeCand << 1) + MrgList].refIdx;
     pocMrg[mergeCand] = tempCS->slice->getRefPic(MrgeRefPicList, MrgrefIdx)->getPOC();
     MrgMv[mergeCand] = mergeCtx.mvFieldNeighbours[(mergeCand << 1) + MrgList].mv;
+#if GDR_ENABLED 
+    if (isEncodeClean)
+    {
+      MrgSolid[mergeCand] = mergeCtx.mvSolid[(mergeCand << 1) + MrgList];
+      MrgValid[mergeCand] = mergeCtx.mvValid[(mergeCand << 1) + MrgList];
+    }
+#endif
     if (mergeCand)
     {
       for (int i = 0; i < mergeCand; i++)
@@ -2679,7 +2953,20 @@ void EncCu::xCheckRDCostMergeGeo2Nx2N(CodingStructure *&tempCS, CodingStructure
     distParamWholeBlk.cur.buf = geoTempBuf[mergeCand].Y().buf;
     distParamWholeBlk.cur.stride = geoTempBuf[mergeCand].Y().stride;
     sadWholeBlk[mergeCand] = distParamWholeBlk.distFunc(distParamWholeBlk);
+#if GDR_ENABLED     
+    bool allOk = (sadWholeBlk[mergeCand] < bestWholeBlkSad);
+    if (isEncodeClean)
+    {
+      bool isSolid = mergeCtx.mvSolid[(mergeCand << 1) + MrgList];
+      bool isValid = mergeCtx.mvValid[(mergeCand << 1) + MrgList];
+      allOk = allOk && isSolid && isValid;
+    }
+#endif    
+#if GDR_ENABLED 
+    if (allOk)
+#else
     if (sadWholeBlk[mergeCand] < bestWholeBlkSad)
+#endif
     {
       bestWholeBlkSad = sadWholeBlk[mergeCand];
       int bitsCand = mergeCand + 1;
@@ -2727,12 +3014,44 @@ void EncCu::xCheckRDCostMergeGeo2Nx2N(CodingStructure *&tempCS, CodingStructure
     for (uint8_t mergeCand = 0; mergeCand < maxNumMergeCandidates; mergeCand++)
     {
       int bitsCand = mergeCand + 1;
+#if GDR_ENABLED 
+      if (isEncodeClean)
+      {
+        double cost0, cost1;
 
+        m_pcRdCost->setDistParam(distParam, tempCS->getOrgBuf().Y(), geoTempBuf[mergeCand].Y().buf, geoTempBuf[mergeCand].Y().stride, SADmask, maskStride, stepX, maskStride2, sps.getBitDepth(CHANNEL_TYPE_LUMA), COMPONENT_Y);
+        sadLarge = distParam.distFunc(distParam);
+        sadSmall = sadWholeBlk[mergeCand] - sadLarge;
+
+        if (MrgSolid[mergeCand] && MrgValid[mergeCand])
+        {
+          cost0 = (double)sadLarge + (double)bitsCand * sqrtLambdaForFirstPass;
+          cost1 = (double)sadSmall + (double)bitsCand * sqrtLambdaForFirstPass;
+        }
+        else
+        {
+          cost0 = MAX_DOUBLE;
+          cost1 = MAX_DOUBLE;
+        }
+
+        m_GeoCostList.insert(splitDir, 0, mergeCand, cost0);
+        m_GeoCostList.insert(splitDir, 1, mergeCand, cost1);
+      }
+      else
+      {
+        m_pcRdCost->setDistParam(distParam, tempCS->getOrgBuf().Y(), geoTempBuf[mergeCand].Y().buf, geoTempBuf[mergeCand].Y().stride, SADmask, maskStride, stepX, maskStride2, sps.getBitDepth(CHANNEL_TYPE_LUMA), COMPONENT_Y);
+        sadLarge = distParam.distFunc(distParam);
+        m_GeoCostList.insert(splitDir, 0, mergeCand, (double)sadLarge + (double)bitsCand * sqrtLambdaForFirstPass);
+        sadSmall = sadWholeBlk[mergeCand] - sadLarge;
+        m_GeoCostList.insert(splitDir, 1, mergeCand, (double)sadSmall + (double)bitsCand * sqrtLambdaForFirstPass);
+      }
+#else
       m_pcRdCost->setDistParam(distParam, tempCS->getOrgBuf().Y(), geoTempBuf[mergeCand].Y().buf, geoTempBuf[mergeCand].Y().stride, SADmask, maskStride, stepX, maskStride2, sps.getBitDepth(CHANNEL_TYPE_LUMA), COMPONENT_Y);
       sadLarge = distParam.distFunc(distParam);
       m_GeoCostList.insert(splitDir, 0, mergeCand, (double)sadLarge + (double)bitsCand * sqrtLambdaForFirstPass);
       sadSmall = sadWholeBlk[mergeCand] - sadLarge;
       m_GeoCostList.insert(splitDir, 1, mergeCand, (double)sadSmall + (double)bitsCand * sqrtLambdaForFirstPass);
+#endif
     }
   }
 
@@ -2748,6 +3067,22 @@ void EncCu::xCheckRDCostMergeGeo2Nx2N(CodingStructure *&tempCS, CodingStructure
         continue;
       }
       tempCost = tempCost + (double)bitsCandTB * sqrtLambdaForFirstPass;
+#if GDR_ENABLED 
+      if (isEncodeClean)
+      {
+        int idx0 = mergeCand0;
+        int idx1 = mergeCand1;
+        bool isSolid0 = mergeCtx.mvSolid[(idx0 << 1) + 0] && mergeCtx.mvSolid[(idx0 << 1) + 1];
+        bool isSolid1 = mergeCtx.mvSolid[(idx1 << 1) + 0] && mergeCtx.mvSolid[(idx1 << 1) + 1];
+        bool isValid0 = mergeCtx.mvValid[(idx0 << 1) + 0] && mergeCtx.mvValid[(idx0 << 1) + 1];
+        bool isValid1 = mergeCtx.mvValid[(idx1 << 1) + 0] && mergeCtx.mvValid[(idx1 << 1) + 1];
+
+        if (!isSolid0 || !isSolid1 || !isValid0 || !isValid1)
+        {
+          tempCost = MAX_DOUBLE;
+        }
+      }
+#endif
       comboList.list.push_back(GeoMergeCombo(splitDir, mergeCand0, mergeCand1, tempCost));
     }
   }
@@ -2786,6 +3121,22 @@ void EncCu::xCheckRDCostMergeGeo2Nx2N(CodingStructure *&tempCS, CodingStructure
     mvBits += mergeCand0;
     mvBits += mergeCand1;
     double updateCost = (double)sad + (double)(bitsCandTB + mvBits) * sqrtLambdaForFirstPass;
+#if GDR_ENABLED 
+    if (isEncodeClean)
+    {
+      int idx0 = mergeCand0;
+      int idx1 = mergeCand1;
+      bool isSolid0 = mergeCtx.mvSolid[(idx0 << 1) + 0] && mergeCtx.mvSolid[(idx0 << 1) + 1];
+      bool isSolid1 = mergeCtx.mvSolid[(idx1 << 1) + 0] && mergeCtx.mvSolid[(idx1 << 1) + 1];
+      bool isValid0 = mergeCtx.mvValid[(idx0 << 1) + 0] && mergeCtx.mvValid[(idx0 << 1) + 1];
+      bool isValid1 = mergeCtx.mvValid[(idx1 << 1) + 0] && mergeCtx.mvValid[(idx1 << 1) + 1];
+
+      if (!isSolid0 || !isSolid1 || !isValid0 || !isValid1)
+      {
+        updateCost = MAX_DOUBLE;
+      }
+    }
+#endif    
     comboList.list[candidateIdx].cost = updateCost;
     updateCandList(candidateIdx, updateCost, geoRdModeList, geocandCostList, geoNumMrgSATDCand);
   }
@@ -2850,7 +3201,28 @@ void EncCu::xCheckRDCostMergeGeo2Nx2N(CodingStructure *&tempCS, CodingStructure
       PU::spanGeoMotionInfo(pu, mergeCtx, pu.geoSplitDir, pu.geoMergeIdx0, pu.geoMergeIdx1);
       tempCS->getPredBuf().copyFrom(geoCombinations[candidateIdx]);
 
+#if GDR_ENABLED 
+      if (isEncodeClean)
+      {
+        int idx0 = pu.geoMergeIdx0;
+        int idx1 = pu.geoMergeIdx1;
+        bool isSolid0 = mergeCtx.mvSolid[(idx0 << 1) + 0] && mergeCtx.mvSolid[(idx0 << 1) + 1];
+        bool isSolid1 = mergeCtx.mvSolid[(idx1 << 1) + 0] && mergeCtx.mvSolid[(idx1 << 1) + 1];
+        bool isValid0 = mergeCtx.mvValid[(idx0 << 1) + 0] && mergeCtx.mvValid[(idx0 << 1) + 1];
+        bool isValid1 = mergeCtx.mvValid[(idx1 << 1) + 0] && mergeCtx.mvValid[(idx1 << 1) + 1];
+
+        if (isSolid0 && isSolid1 && isValid0 && isValid1)
+        {
+          xEncodeInterResidual(tempCS, bestCS, pm, encTestMode, noResidualPass, (noResidualPass == 0 ? &geocandHasNoResidual[candidateIdx] : NULL));
+        }
+      }
+      else
+      {
+        xEncodeInterResidual(tempCS, bestCS, pm, encTestMode, noResidualPass, (noResidualPass == 0 ? &geocandHasNoResidual[candidateIdx] : NULL));
+      }
+#else
       xEncodeInterResidual(tempCS, bestCS, pm, encTestMode, noResidualPass, (noResidualPass == 0 ? &geocandHasNoResidual[candidateIdx] : NULL));
+#endif
 
       if (m_pcEncCfg->getUseFastDecisionForMerge() && !bestIsSkip)
       {
@@ -2863,6 +3235,14 @@ void EncCu::xCheckRDCostMergeGeo2Nx2N(CodingStructure *&tempCS, CodingStructure
   {
     xCalDebCost(*bestCS, pm);
   }
+  
+#if GDR_ENABLED 
+  if (isEncodeClean) 
+  {
+    delete[] MrgSolid;
+    delete[] MrgValid;
+  }
+#endif
 }
 
 void EncCu::xCheckRDCostAffineMerge2Nx2N( CodingStructure *&tempCS, CodingStructure *&bestCS, Partitioner &partitioner, const EncTestMode& encTestMode )
@@ -2876,6 +3256,10 @@ void EncCu::xCheckRDCostAffineMerge2Nx2N( CodingStructure *&tempCS, CodingStruct
   {
     return;
   }
+#if GDR_ENABLED
+  CodingStructure *cs;
+  bool isEncodeClean = false;
+#endif  
   m_bestModeUpdated = tempCS->useDbCost = bestCS->useDbCost = false;
   const Slice &slice = *tempCS->slice;
 
@@ -2913,6 +3297,10 @@ void EncCu::xCheckRDCostAffineMerge2Nx2N( CodingStructure *&tempCS, CodingStruct
     pu.cu = &cu;
     pu.cs = tempCS;
     pu.regularMergeFlag = false;
+#if GDR_ENABLED
+    cs = pu.cs;
+    isEncodeClean = cs->pcv->isEncoder && ((cs->picHeader->getInGdrPeriod() && cs->isClean(pu.Y().topRight(), CHANNEL_TYPE_LUMA)) || (cs->picHeader->getNumVerVirtualBoundaries() == 0));
+#endif
     PU::getAffineMergeCand( pu, affineMergeCtx );
 
     if ( affineMergeCtx.numValidMergeCand <= 0 )
@@ -3003,6 +3391,19 @@ void EncCu::xCheckRDCostAffineMerge2Nx2N( CodingStructure *&tempCS, CodingStruct
           PU::spanMotionInfo( pu );
         }
 
+#if GDR_ENABLED
+        if (isEncodeClean)
+        {
+          Mv zero = Mv(0, 0);
+          bool isValid = cs->isSubPuClean(pu, &zero);
+          affineMergeCtx.mvValid[(uiMergeCand << 1) + 0][0] = isValid;
+          affineMergeCtx.mvValid[(uiMergeCand << 1) + 0][1] = isValid;
+          affineMergeCtx.mvValid[(uiMergeCand << 1) + 0][2] = isValid;
+          affineMergeCtx.mvValid[(uiMergeCand << 1) + 1][0] = isValid;
+          affineMergeCtx.mvValid[(uiMergeCand << 1) + 1][1] = isValid;
+          affineMergeCtx.mvValid[(uiMergeCand << 1) + 1][2] = isValid;
+        }
+#endif
         distParam.cur = acMergeBuffer[uiMergeCand].Y();
 
         m_pcInterSearch->motionCompensation( pu, acMergeBuffer[uiMergeCand], REF_PIC_LIST_X, true, false );
@@ -3014,6 +3415,23 @@ void EncCu::xCheckRDCostAffineMerge2Nx2N( CodingStructure *&tempCS, CodingStruct
           uiBitsCand--;
         }
         double cost = (double)uiSad + (double)uiBitsCand * sqrtLambdaForFirstPass;
+#if GDR_ENABLED
+        if (isEncodeClean)
+        {
+          bool isSolid0 = affineMergeCtx.mvSolid[(uiMergeCand << 1) + 0][0] && affineMergeCtx.mvSolid[(uiMergeCand << 1) + 0][1] && affineMergeCtx.mvSolid[(uiMergeCand << 1) + 0][2];
+          bool isSolid1 = affineMergeCtx.mvSolid[(uiMergeCand << 1) + 1][0] && affineMergeCtx.mvSolid[(uiMergeCand << 1) + 1][1] && affineMergeCtx.mvSolid[(uiMergeCand << 1) + 1][2];
+          bool isValid0 = affineMergeCtx.mvValid[(uiMergeCand << 1) + 0][0] && affineMergeCtx.mvValid[(uiMergeCand << 1) + 0][1] && affineMergeCtx.mvValid[(uiMergeCand << 1) + 0][2];
+          bool isValid1 = affineMergeCtx.mvValid[(uiMergeCand << 1) + 1][0] && affineMergeCtx.mvValid[(uiMergeCand << 1) + 1][1] && affineMergeCtx.mvValid[(uiMergeCand << 1) + 1][2];
+
+          bool isSolid = isSolid0 && isSolid1;
+          bool isValid = isValid0 && isValid1;
+
+          if (!isSolid || !isValid)
+          {
+            cost = MAX_DOUBLE;
+          }
+        }
+#endif        
         updateCandList( uiMergeCand, cost, RdModeList, candCostList
           , uiNumMrgSATDCand );
 
@@ -3106,11 +3524,59 @@ void EncCu::xCheckRDCostAffineMerge2Nx2N( CodingStructure *&tempCS, CodingStruct
         m_pcInterSearch->motionCompensation( pu );
       }
 
+#if GDR_ENABLED
+      bool isSolid = true;
+      bool isValid = true;
+
+      if (isEncodeClean)
+      {
+        if (bestIsSkip)
+        {
+          Mv zero = Mv(0, 0);
+          bool isValid = cs->isSubPuClean(pu, &zero);
+          affineMergeCtx.mvValid[(uiMergeCand << 1) + 0][0] = isValid;
+          affineMergeCtx.mvValid[(uiMergeCand << 1) + 0][1] = isValid;
+          affineMergeCtx.mvValid[(uiMergeCand << 1) + 0][2] = isValid;
+          affineMergeCtx.mvValid[(uiMergeCand << 1) + 1][0] = isValid;
+          affineMergeCtx.mvValid[(uiMergeCand << 1) + 1][1] = isValid;
+          affineMergeCtx.mvValid[(uiMergeCand << 1) + 1][2] = isValid;
+        }
+
+        bool isSolid0 = affineMergeCtx.mvSolid[(uiMergeCand << 1) + 0][0] && affineMergeCtx.mvSolid[(uiMergeCand << 1) + 0][1] && affineMergeCtx.mvSolid[(uiMergeCand << 1) + 0][2];
+        bool isSolid1 = affineMergeCtx.mvSolid[(uiMergeCand << 1) + 1][0] && affineMergeCtx.mvSolid[(uiMergeCand << 1) + 1][1] && affineMergeCtx.mvSolid[(uiMergeCand << 1) + 1][2];
+        bool isValid0 = affineMergeCtx.mvValid[(uiMergeCand << 1) + 0][0] && affineMergeCtx.mvValid[(uiMergeCand << 1) + 0][1] && affineMergeCtx.mvValid[(uiMergeCand << 1) + 0][2];
+        bool isValid1 = affineMergeCtx.mvValid[(uiMergeCand << 1) + 1][0] && affineMergeCtx.mvValid[(uiMergeCand << 1) + 1][1] && affineMergeCtx.mvValid[(uiMergeCand << 1) + 1][2];
+
+        isSolid = isSolid0 && isSolid1;
+        isValid = isValid0 && isValid1;
+
+        if (isSolid && isValid)
+        {
+          xEncodeInterResidual(tempCS, bestCS, partitioner, encTestMode, uiNoResidualPass, (uiNoResidualPass == 0 ? &candHasNoResidual[uiMergeCand] : NULL));
+        }
+      }
+      else
+      {
+        xEncodeInterResidual(tempCS, bestCS, partitioner, encTestMode, uiNoResidualPass, (uiNoResidualPass == 0 ? &candHasNoResidual[uiMergeCand] : NULL));
+      }
+#else
       xEncodeInterResidual( tempCS, bestCS, partitioner, encTestMode, uiNoResidualPass, ( uiNoResidualPass == 0 ? &candHasNoResidual[uiMergeCand] : NULL ) );
+#endif
 
       if ( m_pcEncCfg->getUseFastDecisionForMerge() && !bestIsSkip )
       {
-        bestIsSkip = bestCS->getCU( partitioner.chType )->rootCbf == 0;
+#if GDR_ENABLED                
+        if (bestCS->getCU(partitioner.chType))
+        {
+          bestIsSkip = bestCS->getCU(partitioner.chType)->rootCbf == 0;
+        }
+        else
+        {
+          bestIsSkip = false;
+        }
+#else
+        bestIsSkip = bestCS->getCU(partitioner.chType)->rootCbf == 0;
+#endif
       }
       tempCS->initStructData( encTestMode.qp );
     }// end loop uiMrgHADIdx
@@ -3171,6 +3637,9 @@ void EncCu::xCheckRDCostIBCModeMerge2Nx2N(CodingStructure *&tempCS, CodingStruct
     mergeCtx.subPuMvpMiBuf = MotionBuf(m_SubPuMiBuf, bufSize);
   }
 
+#if GDR_ENABLED
+  bool bGDRclean = true;
+#endif
   {
     // first get merge candidates
     CodingUnit cu(tempCS->area);
@@ -3186,7 +3655,15 @@ void EncCu::xCheckRDCostIBCModeMerge2Nx2N(CodingStructure *&tempCS, CodingStruct
     pu.regularMergeFlag = false;
     cu.geoFlag = false;
     PU::getIBCMergeCandidates(pu, mergeCtx);
+#if GDR_ENABLED
+    bGDRclean = tempCS->isClean(pu.Y().topRight(), CHANNEL_TYPE_LUMA) || tempCS->picHeader->getNumVerVirtualBoundaries() == 0;    
+#endif
   }
+#if GDR_ENABLED
+  const bool isEncodeClean = tempCS->pcv->isEncoder && tempCS->picHeader->getInGdrPeriod() && bGDRclean;
+  bool *MrgSolid = nullptr;
+  bool *MrgValid = nullptr;
+#endif
 
   int candHasNoResidual[MRG_MAX_NUM_CANDS];
   for (unsigned int ui = 0; ui < mergeCtx.numValidMergeCand; ui++)
@@ -3194,6 +3671,19 @@ void EncCu::xCheckRDCostIBCModeMerge2Nx2N(CodingStructure *&tempCS, CodingStruct
     candHasNoResidual[ui] = 0;
   }
 
+#if GDR_ENABLED 
+  if (isEncodeClean)
+  {
+    MrgSolid = new bool[MRG_MAX_NUM_CANDS];
+    MrgValid = new bool[MRG_MAX_NUM_CANDS];
+    for (int i = 0; i < MRG_MAX_NUM_CANDS; i++)
+    {
+      MrgSolid[i] = false;
+      MrgValid[i] = false;
+    }
+  }
+#endif
+
   bool                                        bestIsSkip = false;
   unsigned                                    numMrgSATDCand = mergeCtx.numValidMergeCand;
   static_vector<unsigned, MRG_MAX_NUM_CANDS>  RdModeList(MRG_MAX_NUM_CANDS);
@@ -3275,7 +3765,41 @@ void EncCu::xCheckRDCostIBCModeMerge2Nx2N(CodingStructure *&tempCS, CodingStruct
         bitsCand--;
       }
       double cost = (double) sad + (double) bitsCand * sqrtLambdaForFirstPass;
+#if GDR_ENABLED
+      if (isEncodeClean)
+      {
+        bool isSolid = true;
+        bool isValid = true;
 
+        if (mergeCtx.mvFieldNeighbours[(mergeCand << 1) + 0].refIdx >= 0)
+        {
+          Mv mv = mergeCtx.mvFieldNeighbours[(mergeCand << 1) + 0].mv;
+          int ridx = mergeCtx.mvFieldNeighbours[(mergeCand << 1) + 0].refIdx;
+
+          mergeCtx.mvValid[(mergeCand << 1) + 0] = tempCS->isClean(pu.Y().bottomRight(), mv, REF_PIC_LIST_0, ridx, true);
+
+          isSolid = isSolid && mergeCtx.mvSolid[(mergeCand << 1) + 0];
+          isValid = isValid && mergeCtx.mvValid[(mergeCand << 1) + 0];
+        }
+
+        if (mergeCtx.mvFieldNeighbours[(mergeCand << 1) + 1].refIdx >= 0)
+        {
+          Mv mv = mergeCtx.mvFieldNeighbours[(mergeCand << 1) + 1].mv;
+          int ridx = mergeCtx.mvFieldNeighbours[(mergeCand << 1) + 1].refIdx;
+
+          mergeCtx.mvValid[(mergeCand << 1) + 1] = tempCS->isClean(pu.Y().bottomRight(), mv, REF_PIC_LIST_1, ridx, true);
+
+          isSolid = isSolid && mergeCtx.mvSolid[(mergeCand << 1) + 1];
+          isValid = isValid && mergeCtx.mvValid[(mergeCand << 1) + 1];
+        }
+
+        if (!isValid || !isSolid)
+        {
+          cost = MAX_DOUBLE;
+          numValidBv--;
+        }
+      }
+#endif
       updateCandList(mergeCand, cost, RdModeList, candCostList, numMrgSATDCand);
     }
 
@@ -3342,12 +3866,62 @@ void EncCu::xCheckRDCostIBCModeMerge2Nx2N(CodingStructure *&tempCS, CodingStruct
 
             assert(mergeCtx.mrgTypeNeighbours[mergeCand] == MRG_TYPE_IBC); //  should be IBC candidate at this round
             const bool chroma = !pu.cu->isSepTree();
+#if GDR_ENABLED
+            // redo validation again for Skip
+            {
+              CodingStructure &cs = *pu.cs;
 
+              if (isEncodeClean)
+              {
+                if (mergeCtx.mvFieldNeighbours[(mergeCand << 1) + 0].refIdx >= 0)
+                {
+                  Mv mv = mergeCtx.mvFieldNeighbours[(mergeCand << 1) + 0].mv;
+                  int ridx = mergeCtx.mvFieldNeighbours[(mergeCand << 1) + 0].refIdx;
+
+                  mergeCtx.mvValid[(mergeCand << 1) + 0] = cs.isClean(pu.Y().bottomRight(), mv, REF_PIC_LIST_0, ridx, true);
+                }
+
+                if (mergeCtx.mvFieldNeighbours[(mergeCand << 1) + 1].refIdx >= 0)
+                {
+                  Mv mv = mergeCtx.mvFieldNeighbours[(mergeCand << 1) + 1].mv;
+                  int ridx = mergeCtx.mvFieldNeighbours[(mergeCand << 1) + 1].refIdx;
+                  mergeCtx.mvValid[(mergeCand << 1) + 1] = cs.isClean(pu.Y().bottomRight(), mv, REF_PIC_LIST_1, ridx, true);
+                }
+              }
+            }
+#endif
             //  MC
             m_pcInterSearch->motionCompensation(pu,REF_PIC_LIST_0, true, chroma);
             m_CABACEstimator->getCtx() = m_CurrCtx->start;
 
+#if GDR_ENABLED
+            if (isEncodeClean)
+            {
+              bool mvSolid = true;
+              bool mvValid = true;
+              if (mergeCtx.mvFieldNeighbours[(mergeCand << 1) + 0].refIdx >= 0)
+              {
+                mvSolid = mvSolid && mergeCtx.mvSolid[(mergeCand << 1) + 0];
+                mvValid = mvValid && mergeCtx.mvValid[(mergeCand << 1) + 0];
+              }
+              if (mergeCtx.mvFieldNeighbours[(mergeCand << 1) + 1].refIdx >= 0)
+              {
+                mvSolid = mvSolid && mergeCtx.mvSolid[(mergeCand << 1) + 1];
+                mvValid = mvValid && mergeCtx.mvValid[(mergeCand << 1) + 1];
+              }
+
+              if (mvSolid && mvValid)
+              {
+                m_pcInterSearch->encodeResAndCalcRdInterCU(*tempCS, partitioner, (numResidualPass != 0), true, chroma);
+              }
+            }
+            else
+            {
+              m_pcInterSearch->encodeResAndCalcRdInterCU(*tempCS, partitioner, (numResidualPass != 0), true, chroma);
+            }
+#else
             m_pcInterSearch->encodeResAndCalcRdInterCU(*tempCS, partitioner, (numResidualPass != 0), true, chroma);
+#endif
             if (tempCS->slice->getSPS()->getUseColorTrans())
             {
               bestCS->tmpColorSpaceCost = tempCS->tmpColorSpaceCost;
@@ -3392,6 +3966,13 @@ void EncCu::xCheckRDCostIBCModeMerge2Nx2N(CodingStructure *&tempCS, CodingStruct
   {
     xCalDebCost( *bestCS, partitioner );
   }
+#if GDR_ENABLED 
+  if (isEncodeClean) 
+  {
+    delete[] MrgSolid;
+    delete[] MrgValid;
+  }
+#endif
 }
 
 void EncCu::xCheckRDCostIBCMode(CodingStructure *&tempCS, CodingStructure *&bestCS, Partitioner &partitioner, const EncTestMode& encTestMode)
@@ -3544,6 +4125,9 @@ void EncCu::xCheckRDCostInter( CodingStructure *&tempCS, CodingStructure *&bestC
     uint8_t bcwIdx  = cu.BcwIdx;
     bool    testBcw = (bcwIdx != BCW_DEFAULT);
 
+#if GDR_ENABLED    
+  const bool isEncodeClean = tempCS->pcv->isEncoder && ((tempCS->picHeader->getInGdrPeriod() && tempCS->isClean(cu.Y().topRight(), CHANNEL_TYPE_LUMA)) || (tempCS->picHeader->getNumVerVirtualBoundaries() == 0));
+#endif
     m_pcInterSearch->predInterSearch(cu, partitioner);
 
     bcwIdx = CU::getValidBcwIdx(cu);
@@ -3563,13 +4147,98 @@ void EncCu::xCheckRDCostInter( CodingStructure *&tempCS, CodingStructure *&bestC
       }
     }
 
+#if GDR_ENABLED  
+    // 2.0 xCheckRDCostInter: check residual (compare with bestCS)       
+    if (isEncodeClean)
+    {
+      bool isClean = true;
+
+      if (cu.affine && cu.firstPU)
+      {
+        bool L0ok = true, L1ok = true, L3ok = true;
+
+        L0ok = L0ok && cu.firstPU->mvAffiSolid[0][0] && cu.firstPU->mvAffiSolid[0][1] && cu.firstPU->mvAffiSolid[0][2];
+        L0ok = L0ok && cu.firstPU->mvAffiValid[0][0] && cu.firstPU->mvAffiValid[0][1] && cu.firstPU->mvAffiValid[0][2];
+
+        L1ok = L1ok && cu.firstPU->mvAffiSolid[1][0] && cu.firstPU->mvAffiSolid[1][1] && cu.firstPU->mvAffiSolid[1][2];
+        L1ok = L1ok && cu.firstPU->mvAffiValid[1][0] && cu.firstPU->mvAffiValid[1][1] && cu.firstPU->mvAffiValid[1][2];
+
+        L3ok = L0ok && L1ok;
+
+        if (cu.firstPU->interDir == 1 && !L0ok)
+        {
+          isClean = false;
+        }
+        if (cu.firstPU->interDir == 2 && !L1ok)
+        {
+          isClean = false;
+        }
+        if (cu.firstPU->interDir == 3 && !L3ok)
+        {
+          isClean = false;
+        }
+      }
+      else if (cu.firstPU)
+      {
+        bool L0ok = true;
+        bool L1ok = true;
+        bool L3ok = true;
+
+        L0ok = L0ok && cu.firstPU->mvSolid[0];
+        L0ok = L0ok && cu.firstPU->mvValid[0];
+
+        L1ok = L1ok && cu.firstPU->mvSolid[1];
+        L1ok = L1ok && cu.firstPU->mvValid[1];
+
+        L3ok = L0ok && L1ok;
+
+        if (cu.firstPU->interDir == 1 && !L0ok)
+        {
+          isClean = false;
+        }
+        if (cu.firstPU->interDir == 2 && !L1ok)
+        {
+          isClean = false;
+        }
+        if (cu.firstPU->interDir == 3 && !L3ok)
+        {
+          isClean = false;
+        }
+      }
+      else
+      {
+        isClean = false;
+      }
+
+      if (isClean)
+      {
+        xEncodeInterResidual(tempCS, bestCS, partitioner, encTestMode, 0
+          , 0
+          , &equBcwCost
+        );
+      }
+    }
+    else
+    {
+      xEncodeInterResidual(tempCS, bestCS, partitioner, encTestMode, 0
+        , 0
+        , &equBcwCost
+      );
+    }
+#else
     xEncodeInterResidual(tempCS, bestCS, partitioner, encTestMode, 0, 0, &equBcwCost);
+#endif    
 
+#if GDR_ENABLED    
+  if (g_BcwSearchOrder[bcwLoopIdx] == BCW_DEFAULT && bestCS->cus.size() > 0)
+    m_pcInterSearch->setAffineModeSelected((bestCS->cus.front()->affine && !(bestCS->cus.front()->firstPU->mergeFlag)));
+#else
     if (g_BcwSearchOrder[bcwLoopIdx] == BCW_DEFAULT)
     {
       m_pcInterSearch->setAffineModeSelected(
         (bestCS->cus.front()->affine && !(bestCS->cus.front()->firstPU->mergeFlag)));
     }
+#endif
 
     tempCS->initStructData(encTestMode.qp);
 
@@ -3674,6 +4343,9 @@ bool EncCu::xCheckRDCostInterIMV(CodingStructure *&tempCS, CodingStructure *&bes
 
   CU::addPUs( cu );
 
+#if GDR_ENABLED    
+    const bool isEncodeClean = tempCS->pcv->isEncoder && ((tempCS->picHeader->getInGdrPeriod() && tempCS->isClean(cu.Y().topRight(), CHANNEL_TYPE_LUMA)) || (tempCS->picHeader->getNumVerVirtualBoundaries() == 0));
+#endif
   if (testAltHpelFilter)
   {
     cu.imv = IMV_HPEL;
@@ -3745,7 +4417,79 @@ bool EncCu::xCheckRDCostInterIMV(CodingStructure *&tempCS, CodingStructure *&bes
     }
   }
 
+#if GDR_ENABLED  
+  // 2.0 xCheckRDCostInter: check residual (compare with bestCS)        
+  if (isEncodeClean)
+  {
+    bool isClean = true;
+
+    if (cu.affine && cu.firstPU)
+    {
+      bool L0ok = true, L1ok = true, L3ok = true;
+
+      L0ok = L0ok && cu.firstPU->mvAffiSolid[0][0] && cu.firstPU->mvAffiSolid[0][1] && cu.firstPU->mvAffiSolid[0][2];
+      L0ok = L0ok && cu.firstPU->mvAffiValid[0][0] && cu.firstPU->mvAffiValid[0][1] && cu.firstPU->mvAffiValid[0][2];
+
+      L1ok = L1ok && cu.firstPU->mvAffiSolid[1][0] && cu.firstPU->mvAffiSolid[1][1] && cu.firstPU->mvAffiSolid[1][2];
+      L1ok = L1ok && cu.firstPU->mvAffiValid[1][0] && cu.firstPU->mvAffiValid[1][1] && cu.firstPU->mvAffiValid[1][2];
+
+      L3ok = L0ok && L1ok;
+
+      if (cu.firstPU->interDir == 1 && !L0ok)
+      {
+        isClean = false;
+      }
+      if (cu.firstPU->interDir == 2 && !L1ok)
+      {
+        isClean = false;
+      }
+      if (cu.firstPU->interDir == 3 && !L3ok)
+      {
+        isClean = false;
+      }
+    }
+    else if (cu.firstPU)
+    {
+      bool L0ok = cu.firstPU->mvSolid[0] && cu.firstPU->mvValid[0];
+      bool L1ok = cu.firstPU->mvSolid[1] && cu.firstPU->mvValid[1];
+      bool L3ok = L0ok && L1ok;
+
+      if (cu.firstPU->interDir == 1 && !L0ok)
+      {
+        isClean = false;
+      }
+      if (cu.firstPU->interDir == 2 && !L1ok)
+      {
+        isClean = false;
+      }
+      if (cu.firstPU->interDir == 3 && !L3ok)
+      {
+        isClean = false;
+      }
+    }
+    else
+    {
+      isClean = false;
+    }
+
+    if (isClean)
+    {
+      xEncodeInterResidual(tempCS, bestCS, partitioner, encTestMode, 0
+        , 0
+        , &equBcwCost
+      );
+    }
+  }
+  else
+  {
+    xEncodeInterResidual(tempCS, bestCS, partitioner, encTestMode, 0
+      , 0
+      , &equBcwCost
+    );
+  }
+#else
   xEncodeInterResidual(tempCS, bestCS, partitioner, encTestModeBase, 0, 0, &equBcwCost);
+#endif
 
   if( cu.imv == IMV_FPEL && tempCS->cost < bestIntPelCost )
   {
diff --git a/source/Lib/EncoderLib/EncCu.h b/source/Lib/EncoderLib/EncCu.h
index b953c9b2f26e3621e77d57d478cc7091c1e51ac5..7682705aa0f2d56099e45642fd150d4aecc87a9e 100644
--- a/source/Lib/EncoderLib/EncCu.h
+++ b/source/Lib/EncoderLib/EncCu.h
@@ -88,7 +88,28 @@ struct SmallerThanComboCost
 {
   inline bool operator() (const GeoMergeCombo& first, const GeoMergeCombo& second)
   {
+#if GDR_ENABLED 
+    bool ret = true;
+    
+    ret = (first.cost < second.cost);
+    
+    if (first.cost == second.cost)
+    {
+      ret = first.splitDir < second.splitDir;
+      if (first.splitDir == second.splitDir)
+      {
+        ret = first.mergeIdx0 < second.mergeIdx0;
+        if (first.mergeIdx0 == second.mergeIdx0)
+        {
+          ret = first.mergeIdx1 < second.mergeIdx1;
+        }
+      }
+    }
+
+    return ret;
+#else
       return (first.cost < second.cost);
+#endif
   }
 };
 class GeoComboCostList
diff --git a/source/Lib/EncoderLib/EncGOP.cpp b/source/Lib/EncoderLib/EncGOP.cpp
index 69368d1531182bf2ea39bd147565ef9fdf6dfb14..11d190b39ed4f683f9af2a5a55b8da5954c5fa19 100644
--- a/source/Lib/EncoderLib/EncGOP.cpp
+++ b/source/Lib/EncoderLib/EncGOP.cpp
@@ -312,6 +312,9 @@ void EncGOP::init ( EncLib* pcEncLib )
     m_pcTransferFct       = hdrtoolslib::TransferFunction::create(hdrtoolslib::TF_PQ, true, (float) maxSampleValue, 0, 0.0, 1.0, enableTFunctionLUT);
   }
 #endif
+#if GDR_ENABLED
+  m_LastGdrPeriodPoc = -1;
+#endif
 }
 
 int EncGOP::xWriteOPI (AccessUnit &accessUnit, const OPI *opi)
@@ -374,6 +377,12 @@ int EncGOP::xWriteAPS( AccessUnit &accessUnit, APS *aps, const int layerId, cons
   nalu.m_temporalId = aps->getTemporalId();
   aps->setLayerId( layerId );
   CHECK( nalu.m_temporalId < accessUnit.temporalId, "TemporalId shall be greater than or equal to the TemporalId of the layer access unit containing the NAL unit" );
+  
+#if GDR_ENC_TRACE 
+  if (aps)
+    printf("-aps ty:%d id:%d\n", aps->getAPSType(), aps->getAPSId());
+#endif
+
   m_HLSWriter->codeAPS(aps);
   accessUnit.push_back(new NALUnitEBSP(nalu));
   return (int)(accessUnit.back()->m_nalUnitData.str().size()) * 8;
@@ -1919,6 +1928,9 @@ void EncGOP::xPicInitLMCS(Picture *pic, PicHeader *picHeader, Slice *slice)
       else if (m_pcCfg->getReshapeSignalType() == RESHAPE_SIGNAL_SDR || m_pcCfg->getReshapeSignalType() == RESHAPE_SIGNAL_HLG)
       {
         int modIP = pic->getPOC() - pic->getPOC() / m_pcCfg->getReshapeCW().rspFpsToIp * m_pcCfg->getReshapeCW().rspFpsToIp;
+#if GDR_ENABLED        
+        if (slice->isInterGDR()) modIP = 0;
+#endif
         if (m_pcReshaper->getReshapeFlag() && m_pcCfg->getReshapeCW().updateCtrl == 2 && modIP == 0)
         {
           m_pcReshaper->getSliceReshaperInfo().setSliceReshapeModelPresentFlag(true);
@@ -1936,6 +1948,14 @@ void EncGOP::xPicInitLMCS(Picture *pic, PicHeader *picHeader, Slice *slice)
     picHeader->setLmcsEnabledFlag(m_pcReshaper->getSliceReshaperInfo().getUseSliceReshaper());
     slice->setLmcsEnabledFlag(m_pcReshaper->getSliceReshaperInfo().getUseSliceReshaper());
     picHeader->setLmcsChromaResidualScaleFlag(m_pcReshaper->getSliceReshaperInfo().getSliceReshapeChromaAdj() == 1);
+
+#if GDR_ENABLED   
+    if (picHeader->getInGdrPeriod()) 
+    {
+      picHeader->setLmcsChromaResidualScaleFlag(false);
+    }   
+#endif
+
     if (m_pcReshaper->getSliceReshaperInfo().getSliceReshapeModelPresentFlag())
     {
       int apsId = std::min<int>( 3, m_pcEncLib->getVPS() == nullptr ? 0 : m_pcEncLib->getVPS()->getGeneralLayerIdx( m_pcEncLib->getLayerId() ) );
@@ -2140,6 +2160,18 @@ void EncGOP::compressGOP( int iPOCLast, int iNumPicRcvd, PicList& rcListPic,
       pcSlice->setSliceType(I_SLICE);
     }
     pcSlice->setTLayer(m_pcCfg->getGOPEntry(iGOPid).m_temporalId);
+#if GDR_ENABLED
+    if (pocCurr >= m_pcCfg->getGdrPocStart() && ((pocCurr - m_pcCfg->getGdrPocStart()) % (m_pcCfg->getGdrPeriod() * m_pcCfg->getGdrFrequency()) == 0)) 
+    {
+      pcSlice->setSliceType(B_SLICE);
+    }
+
+    // note : first picture is GDR(I_SLICE)    
+    if (pocCurr == 0) 
+    {    
+      pcSlice->setSliceType(I_SLICE);
+    }
+#endif
 
     // Set the nal unit type
     pcSlice->setNalUnitType(getNalUnitType(pocCurr, m_iLastIDR, isField));
@@ -2913,6 +2945,12 @@ void EncGOP::compressGOP( int iPOCLast, int iNumPicRcvd, PicList& rcListPic,
       if (cs.sps->getUseLmcs() && m_pcReshaper->getSliceReshaperInfo().getUseSliceReshaper())
       {
         picHeader->setLmcsEnabledFlag(true);
+#if GDR_ENABLED
+        if (picHeader->getInGdrPeriod()) 
+        {
+          picHeader->setLmcsChromaResidualScaleFlag(false);
+        }       
+#endif
         int apsId = std::min<int>(3, m_pcEncLib->getVPS() == nullptr ? 0 : m_pcEncLib->getVPS()->getGeneralLayerIdx(m_pcEncLib->getLayerId()));
         picHeader->setLmcsAPSId(apsId);
 
@@ -3210,7 +3248,11 @@ void EncGOP::compressGOP( int iPOCLast, int iNumPicRcvd, PicList& rcListPic,
       /////////////////////////////////////////////////////////////////////////////////////////////////// File writing
 
       // write various parameter sets
+#if GDR_ENABLED // Note : insert SPS/PPS at every GDR picture
+      bool writePS = m_bSeqFirst || (m_pcCfg->getReWriteParamSets() && (pcSlice->isIRAP())) || pcSlice->isInterGDR();
+#else
       bool writePS = m_bSeqFirst || (m_pcCfg->getReWriteParamSets() && (pcSlice->isIRAP()));
+#endif
       if (writePS)
       {
         m_pcEncLib->setParamSetChanged(pcSlice->getSPS()->getSPSId(), pcSlice->getPPS()->getPPSId());
@@ -3246,12 +3288,25 @@ void EncGOP::compressGOP( int iPOCLast, int iNumPicRcvd, PicList& rcListPic,
         ParameterSetMap<APS> *apsMap = m_pcEncLib->getApsMap();
         APS* aps = apsMap->getPS((apsId << NUM_APS_TYPE_LEN) + LMCS_APS);
         bool writeAPS = aps && apsMap->getChangedFlag((apsId << NUM_APS_TYPE_LEN) + LMCS_APS);
+#if GDR_ENABLED // note : insert APS at every GDR picture
+        if (aps && apsId >= 0) 
+        {                    
+          writeAPS |= pcSlice->isInterGDR();          
+        }
+#endif
         if (writeAPS)
         {
           aps->chromaPresentFlag = pcSlice->getSPS()->getChromaFormatIdc() != CHROMA_400;
           actualTotalBits += xWriteAPS( accessUnit, aps, m_pcEncLib->getLayerId(), true );
           apsMap->clearChangedFlag((apsId << NUM_APS_TYPE_LEN) + LMCS_APS);
+#if GDR_ENABLED
+          if (!pcSlice->isInterGDR()) 
+          {
+            CHECK(aps != picHeader->getLmcsAPS(), "Wrong LMCS APS pointer in compressGOP");
+          }
+#else
           CHECK(aps != picHeader->getLmcsAPS(), "Wrong LMCS APS pointer in compressGOP");
+#endif
         }
       }
 
@@ -3262,12 +3317,25 @@ void EncGOP::compressGOP( int iPOCLast, int iNumPicRcvd, PicList& rcListPic,
         ParameterSetMap<APS> *apsMap = m_pcEncLib->getApsMap();
         APS* aps = apsMap->getPS( ( apsId << NUM_APS_TYPE_LEN ) + SCALING_LIST_APS );
         bool writeAPS = aps && apsMap->getChangedFlag( ( apsId << NUM_APS_TYPE_LEN ) + SCALING_LIST_APS );
+#if GDR_ENABLED // note : insert APS at every GDR picture
+        if (aps && apsId >= 0) 
+        {          
+          writeAPS |= pcSlice->isInterGDR();
+        }
+#endif
         if( writeAPS )
         {
           aps->chromaPresentFlag = pcSlice->getSPS()->getChromaFormatIdc() != CHROMA_400;
           actualTotalBits += xWriteAPS( accessUnit, aps, m_pcEncLib->getLayerId(), true );
           apsMap->clearChangedFlag( ( apsId << NUM_APS_TYPE_LEN ) + SCALING_LIST_APS );
+#if GDR_ENABLED
+          if (!pcSlice->isInterGDR()) 
+          {
+            CHECK(aps != picHeader->getScalingListAPS(), "Wrong SCALING LIST APS pointer in compressGOP");
+          }
+#else
           CHECK( aps != picHeader->getScalingListAPS(), "Wrong SCALING LIST APS pointer in compressGOP" );
+#endif
         }
       }
 
@@ -3296,13 +3364,25 @@ void EncGOP::compressGOP( int iPOCLast, int iNumPicRcvd, PicList& rcListPic,
             writeAPS = true;
             aps = apsMap->getPS((pcSlice->getCcAlfCrApsId() << NUM_APS_TYPE_LEN) + ALF_APS);
           }
-
+#if GDR_ENABLED // note : insert APS at every GDR picture
+          if (aps && apsId >= 0) 
+          {            
+            writeAPS |= (pcSlice->isInterGDR());
+          } 
+#endif
           if (writeAPS )
           {
             aps->chromaPresentFlag = pcSlice->getSPS()->getChromaFormatIdc() != CHROMA_400;
             actualTotalBits += xWriteAPS( accessUnit, aps, m_pcEncLib->getLayerId(), true );
             apsMap->clearChangedFlag((apsId << NUM_APS_TYPE_LEN) + ALF_APS);
+#if GDR_ENABLED
+            if (!pcSlice->isInterGDR()) 
+            {
+              CHECK(aps != pcSlice->getAlfAPSs()[apsId] && apsId != pcSlice->getCcAlfCbApsId() && apsId != pcSlice->getCcAlfCrApsId(), "Wrong APS pointer in compressGOP");
+            }
+#else
             CHECK(aps != pcSlice->getAlfAPSs()[apsId] && apsId != pcSlice->getCcAlfCbApsId() && apsId != pcSlice->getCcAlfCrApsId(), "Wrong APS pointer in compressGOP");
+#endif
           }
         }
       }
@@ -3527,7 +3607,13 @@ void EncGOP::compressGOP( int iPOCLast, int iNumPicRcvd, PicList& rcListPic,
       auto encTime = std::chrono::duration_cast<std::chrono::seconds>( elapsed ).count();
 
       std::string digestStr;
+#if GDR_ENABLED
+      // note : generate hash sei only for non-gdr pictures
+      bool genHash = !(m_pcCfg->getNoHashForGdr() && pcSlice->getPicHeader()->getInGdrPeriod());
+      if (m_pcCfg->getDecodedPictureHashSEIType() != HASHTYPE_NONE && genHash)
+#else
       if (m_pcCfg->getDecodedPictureHashSEIType()!=HASHTYPE_NONE)
+#endif
       {
         SEIDecodedPictureHash *decodedPictureHashSei = new SEIDecodedPictureHash();
         PelUnitBuf recoBuf = pcPic->cs->getRecoBuf();
@@ -3568,7 +3654,14 @@ void EncGOP::compressGOP( int iPOCLast, int iNumPicRcvd, PicList& rcListPic,
 
       xWriteTrailingSEIMessages(trailingSeiMessages, accessUnit, pcSlice->getTLayer());
 
+#if GDR_ENABLED
+      if (!(m_pcCfg->getNoHashForGdr() && pcSlice->getPicHeader()->getInGdrPeriod())) 
+      {
+          printHash(m_pcCfg->getDecodedPictureHashSEIType(), digestStr);
+      }
+#else
       printHash(m_pcCfg->getDecodedPictureHashSEIType(), digestStr);
+#endif
 
       if ( m_pcCfg->getUseRateCtrl() )
       {
@@ -4981,7 +5074,18 @@ NalUnitType EncGOP::getNalUnitType(int pocCurr, int lastIDR, bool isField)
 {
   if (pocCurr == 0)
   {
+#if GDR_ENABLED    
+    if (m_pcCfg->getStartWithGdr()) 
+    {
+      return NAL_UNIT_CODED_SLICE_GDR;
+    }
+    else 
+    {
+      return NAL_UNIT_CODED_SLICE_IDR_N_LP;
+    }
+#else
     return NAL_UNIT_CODED_SLICE_IDR_N_LP;
+#endif
   }
 
   if (m_pcCfg->getEfficientFieldIRAPEnabled() && isField && pocCurr == (m_pcCfg->getUseCompositeRef() ? 2: 1))
@@ -4990,6 +5094,18 @@ NalUnitType EncGOP::getNalUnitType(int pocCurr, int lastIDR, bool isField)
     return NAL_UNIT_CODED_SLICE_TRAIL;
   }
 
+#if GDR_ENABLED    
+  if (m_pcCfg->getDecodingRefreshType() == 3 && (pocCurr >= m_pcCfg->getGdrPocStart()))
+  {    
+    int m = pocCurr - m_pcCfg->getGdrPocStart();
+    int n = m_pcCfg->getGdrPeriod() * m_pcCfg->getGdrFrequency();
+    if (m % n == 0) 
+    {
+      return NAL_UNIT_CODED_SLICE_GDR;
+    }
+  }
+#endif
+
   if (m_pcCfg->getDecodingRefreshType() != 3 && (pocCurr - isField) % (m_pcCfg->getIntraPeriod() * (m_pcCfg->getUseCompositeRef() ? 2 : 1)) == 0)
   {
     if (m_pcCfg->getDecodingRefreshType() == 1)
@@ -5029,7 +5145,14 @@ NalUnitType EncGOP::getNalUnitType(int pocCurr, int lastIDR, bool isField)
       return NAL_UNIT_CODED_SLICE_RADL;
     }
   }
+#if GDR_ENABLED    
+  if (pocCurr >= m_pcCfg->getGdrPocStart() && ((pocCurr - m_pcCfg->getGdrPocStart()) % (m_pcCfg->getGdrPeriod() * m_pcCfg->getGdrFrequency()) == 0))
+    return NAL_UNIT_CODED_SLICE_TRAIL;
+  else
+    return NAL_UNIT_CODED_SLICE_TRAIL;
+#else
   return NAL_UNIT_CODED_SLICE_TRAIL;
+#endif
 }
 
 void EncGOP::xUpdateRasInit(Slice* slice)
diff --git a/source/Lib/EncoderLib/EncGOP.h b/source/Lib/EncoderLib/EncGOP.h
index 36972e95e8fb380ca898082c4d5893bbfc328bfe..2c0599c3e6b9cb553d55b34fbd334e25ce3fddbe 100644
--- a/source/Lib/EncoderLib/EncGOP.h
+++ b/source/Lib/EncoderLib/EncGOP.h
@@ -183,6 +183,9 @@ private:
   bool                    m_bInitAMaxBT;
 
   AUWriterIf*             m_AUWriterIf;
+#if GDR_ENABLED
+  int                     m_LastGdrPeriodPoc;  
+#endif
 
 #if JVET_O0756_CALCULATE_HDRMETRICS
 
@@ -234,6 +237,11 @@ public:
   void      setLastLTRefPoc(int iLastLTRefPoc) { m_lastLTRefPoc = iLastLTRefPoc; }
   int       getLastLTRefPoc() const { return m_lastLTRefPoc; }
 
+#if GDR_ENABLED
+  void      setLastGdrPeriodPoc(int p)  { m_LastGdrPeriodPoc = p; }
+  int       getLastGdrPeriodPoc() const { return m_LastGdrPeriodPoc; }
+#endif
+
   void  printOutSummary( uint32_t uiNumAllPicCoded, bool isField, const bool printMSEBasedSNR, const bool printSequenceMSE, 
     const bool printMSSSIM, const bool printHexPsnr, const bool printRprPSNR, const BitDepths &bitDepths );
 #if W0038_DB_OPT
diff --git a/source/Lib/EncoderLib/EncLib.cpp b/source/Lib/EncoderLib/EncLib.cpp
index 7355d8e3d63c830f7a0746dd51d9fb51e8e613e1..4c9c13ca2ccf455500d084f9fdbeaca204c6b017 100644
--- a/source/Lib/EncoderLib/EncLib.cpp
+++ b/source/Lib/EncoderLib/EncLib.cpp
@@ -351,7 +351,13 @@ void EncLib::init( bool isFieldCoding, AUWriterIf* auWriterIf )
     Picture *picBg = new Picture;
     picBg->create( sps0.getChromaFormatIdc(), Size( pps0.getPicWidthInLumaSamples(), pps0.getPicHeightInLumaSamples() ), sps0.getMaxCUWidth(), sps0.getMaxCUWidth() + 16, false, m_layerId, m_gopBasedTemporalFilterEnabled );
     picBg->getRecoBuf().fill(0);
+#if GDR_ENABLED
+    PicHeader *picHeader = new PicHeader();
+    xInitPicHeader(*picHeader, sps0, pps0);
+    picBg->finalInit( m_vps, sps0, pps0, picHeader, m_apss, m_lmcsAPS, m_scalinglistAPS );
+#else
     picBg->finalInit( m_vps, sps0, pps0, &m_picHeader, m_apss, m_lmcsAPS, m_scalinglistAPS );
+#endif
     picBg->allocateNewSlice();
     picBg->createSpliceIdx(pps0.pcv->sizeInCtus);
     m_cGOPEncoder.setPicBg(picBg);
@@ -463,7 +469,13 @@ bool EncLib::encodePrep( bool flush, PelStorage* pcPicYuvOrg, PelStorage* cPicYu
     const SPS *sps = m_spsMap.getPS( pps->getSPSId() );
 
     picCurr->M_BUFS( 0, PIC_ORIGINAL ).copyFrom( m_cGOPEncoder.getPicBg()->getRecoBuf() );
+#if GDR_ENABLED
+    PicHeader *picHeader = new PicHeader();
+    xInitPicHeader(*picHeader, *sps, *pps);
+    picCurr->finalInit( m_vps, *sps, *pps, picHeader, m_apss, m_lmcsAPS, m_scalinglistAPS );
+#else
     picCurr->finalInit( m_vps, *sps, *pps, &m_picHeader, m_apss, m_lmcsAPS, m_scalinglistAPS );
+#endif
     picCurr->poc = m_iPOCLast - 1;
     m_iPOCLast -= 2;
     if( getUseAdaptiveQP() )
@@ -581,8 +593,13 @@ bool EncLib::encodePrep( bool flush, PelStorage* pcPicYuvOrg, PelStorage* cPicYu
         pcPicCurr->M_BUFS( 0, PIC_FILTERED_ORIGINAL ).swap( *pcPicYuvFilteredOrg );
       }
     }
-
+#if GDR_ENABLED
+    PicHeader *picHeader = new PicHeader();
+    xInitPicHeader(*picHeader, *pSPS, *pPPS);
+    pcPicCurr->finalInit( m_vps, *pSPS, *pPPS, picHeader, m_apss, m_lmcsAPS, m_scalinglistAPS );
+#else
     pcPicCurr->finalInit( m_vps, *pSPS, *pPPS, &m_picHeader, m_apss, m_lmcsAPS, m_scalinglistAPS );
+#endif
 
     pcPicCurr->poc = m_iPOCLast;
 
@@ -727,7 +744,13 @@ bool EncLib::encodePrep( bool flush, PelStorage* pcPicYuvOrg, PelStorage* pcPicY
       const PPS *pPPS = ( ppsID < 0 ) ? m_ppsMap.getFirstPS() : m_ppsMap.getPS( ppsID );
       const SPS *pSPS = m_spsMap.getPS( pPPS->getSPSId() );
 
+#if GDR_ENABLED
+      PicHeader *picHeader = new PicHeader();
+      xInitPicHeader(*picHeader, *pSPS, *pPPS);
+      pcField->finalInit( m_vps, *pSPS, *pPPS, picHeader, m_apss, m_lmcsAPS, m_scalinglistAPS );
+#else
       pcField->finalInit( m_vps, *pSPS, *pPPS, &m_picHeader, m_apss, m_lmcsAPS, m_scalinglistAPS );
+#endif
 
       pcField->poc = m_iPOCLast;
       pcField->reconstructed = false;
@@ -1105,6 +1128,12 @@ void EncLib::xInitSPS( SPS& sps )
   /* XXX: may be a good idea to refactor the above into a function
    * that chooses the actual compatibility based upon options */
   sps.setVPSId( m_vps->getVPSId() );
+#if GDR_ENABLED
+  sps.setGDREnabledFlag(true);
+#else
+  sps.setGDREnabledFlag(false);
+#endif
+
   sps.setMaxPicWidthInLumaSamples( m_iSourceWidth );
   sps.setMaxPicHeightInLumaSamples( m_iSourceHeight );
   if (m_resChangeInClvsEnabled)
@@ -1625,7 +1654,14 @@ void EncLib::xInitPPS(PPS &pps, const SPS &sps)
 
   pps.setUseWP( m_useWeightedPred );
   pps.setWPBiPred( m_useWeightedBiPred );
-  pps.setOutputFlagPresentFlag( false );
+#if GDR_ENABLED
+  if (getGdrPicOutput())
+    pps.setOutputFlagPresentFlag( true );
+  else
+    pps.setOutputFlagPresentFlag(false);
+#else
+  pps.setOutputFlagPresentFlag(false);
+#endif
 
   if ( getDeblockingFilterMetric() )
   {
diff --git a/source/Lib/EncoderLib/EncModeCtrl.cpp b/source/Lib/EncoderLib/EncModeCtrl.cpp
index bd2f9d807577b23ea4dd64a140864a6a0ae59bd7..58ae037bd17f2e480e276b72ebc10bf2ad8e5c54 100644
--- a/source/Lib/EncoderLib/EncModeCtrl.cpp
+++ b/source/Lib/EncoderLib/EncModeCtrl.cpp
@@ -1266,10 +1266,12 @@ bool EncModeCtrlMTnoRQT::tryMode( const EncTestMode& encTestmode, const CodingSt
   ComprCUCtx& cuECtx = m_ComprCUCtxList.back();
 
   // Fast checks, partitioning depended
+#if !GDR_ENABLED 
   if (cuECtx.isHashPerfectMatch && encTestmode.type != ETM_MERGE_SKIP && encTestmode.type != ETM_INTER_ME && encTestmode.type != ETM_AFFINE && encTestmode.type != ETM_MERGE_GEO)
   {
     return false;
   }
+#endif
 
   // if early skip detected, skip all modes checking but the splits
   if( cuECtx.earlySkip && m_pcEncCfg->getUseEarlySkipDetection() && !isModeSplit( encTestmode ) && !( isModeInter( encTestmode ) ) )
diff --git a/source/Lib/EncoderLib/EncModeCtrl.h b/source/Lib/EncoderLib/EncModeCtrl.h
index b590cb9323890ec06f3a8c525afc9f49df9d42bf..bb3eb1551ac6d097d1a7bf23ad9a28a48fc6784d 100644
--- a/source/Lib/EncoderLib/EncModeCtrl.h
+++ b/source/Lib/EncoderLib/EncModeCtrl.h
@@ -357,6 +357,393 @@ public:
   void   setPltEnc                    ( bool b )                { m_doPlt = b; }
   bool   getPltEnc()                                      const { return m_doPlt; }
 
+#if GDR_ENABLED
+void forceIntraMode()
+{ 
+  // remove all inter or split to force make intra      
+  int n = (int)m_ComprCUCtxList.back().testModes.size();   
+  for (int j = 0; j < n; j++) 
+  {
+    const EncTestMode etm = m_ComprCUCtxList.back().testModes[j];
+
+    if (isModeInter(etm.type)) 
+    {
+      m_ComprCUCtxList.back().testModes.erase(m_ComprCUCtxList.back().testModes.begin() + j);
+      j--;
+      n--;          
+    }
+  }  
+}
+
+void forceIntraNoSplit()
+{
+  // remove all inter or split to force make intra        
+  int n = (int)m_ComprCUCtxList.back().testModes.size();
+
+  for (int j = 0; j < n; j++) 
+  {
+    const EncTestMode etm = m_ComprCUCtxList.back().testModes[j];
+
+    if (isModeInter(etm.type) || isModeSplit(etm.type)) 
+    {
+      m_ComprCUCtxList.back().testModes.erase(m_ComprCUCtxList.back().testModes.begin() + j);
+      j--;
+      n--;
+    }
+  }  
+}
+
+// Note: ForceInterMode
+void forceInterMode()
+{    
+  int n = (int)m_ComprCUCtxList.back().testModes.size();
+  for (int j = 0; j < n; j++) 
+  {
+    const EncTestMode etm = m_ComprCUCtxList.back().testModes[j];
+    if (etm.type == ETM_INTRA) 
+    {
+      m_ComprCUCtxList.back().testModes.erase(m_ComprCUCtxList.back().testModes.begin() + j);
+      j--;
+      n--;        
+    }
+  }  
+}
+
+void removeHashInter()
+{
+  int n = (int)m_ComprCUCtxList.back().testModes.size();
+  for (int j = 0; j < n; j++) 
+  {
+    const EncTestMode etm = m_ComprCUCtxList.back().testModes[j];
+    if (etm.type == ETM_HASH_INTER) 
+    {
+      m_ComprCUCtxList.back().testModes.erase(m_ComprCUCtxList.back().testModes.begin() + j);
+      j--;
+      n--;
+    }
+  }
+}
+
+void removeMergeSkip()
+{
+  int n = (int)m_ComprCUCtxList.back().testModes.size();
+  for (int j = 0; j < n; j++) 
+  {
+    const EncTestMode etm = m_ComprCUCtxList.back().testModes[j];
+    if (etm.type == ETM_MERGE_SKIP) 
+    {
+      m_ComprCUCtxList.back().testModes.erase(m_ComprCUCtxList.back().testModes.begin() + j);
+      j--;
+      n--;
+    }
+  }
+}
+
+void removeInterME()
+{
+  int n = (int)m_ComprCUCtxList.back().testModes.size();
+  for (int j = 0; j < n; j++) 
+  {
+    const EncTestMode etm = m_ComprCUCtxList.back().testModes[j];
+    if (etm.type == ETM_INTER_ME) 
+    {
+      m_ComprCUCtxList.back().testModes.erase(m_ComprCUCtxList.back().testModes.begin() + j);
+      j--;
+      n--;
+    }
+  }
+}
+
+void removeAffine()
+{
+  int n = (int)m_ComprCUCtxList.back().testModes.size();
+  for (int j = 0; j < n; j++) 
+  {
+    const EncTestMode etm = m_ComprCUCtxList.back().testModes[j];
+    if (etm.type == ETM_AFFINE) 
+    {
+      m_ComprCUCtxList.back().testModes.erase(m_ComprCUCtxList.back().testModes.begin() + j);
+      j--;
+      n--;
+    }
+  }
+}
+
+void removeMergeGeo()
+{
+  int n = (int)m_ComprCUCtxList.back().testModes.size();
+  for (int j = 0; j < n; j++) 
+  {
+    const EncTestMode etm = m_ComprCUCtxList.back().testModes[j];
+    if (etm.type == ETM_MERGE_GEO) 
+    {
+      m_ComprCUCtxList.back().testModes.erase(m_ComprCUCtxList.back().testModes.begin() + j);
+      j--;
+      n--;
+    }
+  }
+}
+
+void removeIntra()
+{
+  int n = (int)m_ComprCUCtxList.back().testModes.size();
+  for (int j = 0; j < n; j++) 
+  {
+    const EncTestMode etm = m_ComprCUCtxList.back().testModes[j];
+    if (etm.type == ETM_INTRA) 
+    {
+      m_ComprCUCtxList.back().testModes.erase(m_ComprCUCtxList.back().testModes.begin() + j);
+      j--;
+      n--;
+    }
+  }
+}
+
+void removeBadMode()
+{  
+  int n = (int)m_ComprCUCtxList.back().testModes.size();
+  for (int j = 0; j < n; j++) 
+  {
+    const EncTestMode etm = m_ComprCUCtxList.back().testModes[j];
+
+    if (etm.type == ETM_INTER_ME && ((etm.opts & ETO_IMV) >> ETO_IMV_SHIFT) > 2) 
+    {  
+      m_ComprCUCtxList.back().testModes.erase(m_ComprCUCtxList.back().testModes.begin() + j);
+      j--;
+      n--;
+      break;
+    }
+  }  
+}
+
+bool anyPredModeLeft()
+{ 
+  int n = (int)m_ComprCUCtxList.back().testModes.size();
+  for (int j = 0; j < n; j++) 
+  {
+    const EncTestMode etm = m_ComprCUCtxList.back().testModes[j];
+
+    if (etm.type == ETM_HASH_INTER ||
+        etm.type == ETM_MERGE_SKIP || 
+        etm.type == ETM_INTER_ME   || 
+        etm.type == ETM_AFFINE     || 
+        etm.type == ETM_MERGE_GEO  || 
+        etm.type == ETM_INTRA      ||
+        etm.type == ETM_PALETTE    || 
+        etm.type == ETM_IBC        ||
+        etm.type == ETM_IBC_MERGE) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
+bool anyIntraIBCMode()
+{
+  int n = (int)m_ComprCUCtxList.back().testModes.size();
+  for (int j = 0; j < n; j++) 
+  {
+    const EncTestMode etm = m_ComprCUCtxList.back().testModes[j];
+
+    if (etm.type == ETM_INTRA || etm.type == ETM_IBC) 
+    {
+      return true;
+    }
+  }
+
+  return false;
+}
+
+void forceRemoveDontSplit()
+{
+  int n = (int)m_ComprCUCtxList.back().testModes.size();
+  for (int j = 0; j < n; j++) 
+  {
+    const EncTestMode etm = m_ComprCUCtxList.back().testModes[j];
+
+    if (etm.type == ETM_POST_DONT_SPLIT) 
+    {
+      m_ComprCUCtxList.back().testModes.erase(m_ComprCUCtxList.back().testModes.begin() + j);
+      j--;
+      n--;
+    }
+  }
+}
+
+void forceVerSplitOnly()
+{
+  int n = (int)m_ComprCUCtxList.back().testModes.size();
+  for (int j = 0; j < n; j++) 
+  {
+    const EncTestMode etm = m_ComprCUCtxList.back().testModes[j];       
+ 
+    if (etm.type != ETM_SPLIT_QT && etm.type != ETM_SPLIT_BT_V && etm.type != ETM_SPLIT_TT_V) 
+    {
+      m_ComprCUCtxList.back().testModes.erase(m_ComprCUCtxList.back().testModes.begin() + j);
+      j--;
+      n--;
+    }
+  }   
+}
+
+void forceRemoveTTV()
+{
+  int n = (int)m_ComprCUCtxList.back().testModes.size();
+  for (int j = 0; j < n; j++) 
+  {
+    const EncTestMode etm = m_ComprCUCtxList.back().testModes[j];
+    
+    if (etm.type == ETM_SPLIT_TT_V) 
+    {
+      m_ComprCUCtxList.back().testModes.erase(m_ComprCUCtxList.back().testModes.begin() + j);
+      j--;
+      n--;
+    }
+  }
+}
+
+void forceRemoveBTV()
+{  
+  int n = (int)m_ComprCUCtxList.back().testModes.size();
+  for (int j = 0; j < n; j++) 
+  {
+    const EncTestMode etm = m_ComprCUCtxList.back().testModes[j];
+
+    if (etm.type == ETM_SPLIT_BT_V) 
+    {
+      m_ComprCUCtxList.back().testModes.erase(m_ComprCUCtxList.back().testModes.begin() + j);
+      j--;
+      n--;
+    }
+  }
+}
+
+void forceRemoveQT()
+{
+  int n = (int)m_ComprCUCtxList.back().testModes.size();
+  for (int j = 0; j < n; j++) 
+  {
+    const EncTestMode etm = m_ComprCUCtxList.back().testModes[j];
+ 
+    if (etm.type == ETM_SPLIT_QT) 
+    {
+      m_ComprCUCtxList.back().testModes.erase(m_ComprCUCtxList.back().testModes.begin() + j);
+      j--;
+      n--;
+    }
+  }  
+}
+
+void forceRemoveHT()
+{
+  int n = (int)m_ComprCUCtxList.back().testModes.size();
+  for (int j = 0; j < n; j++) 
+  {
+    const EncTestMode etm = m_ComprCUCtxList.back().testModes[j];
+
+    if (etm.type == ETM_SPLIT_BT_H || etm.type == ETM_SPLIT_TT_H) 
+    {
+      m_ComprCUCtxList.back().testModes.erase(m_ComprCUCtxList.back().testModes.begin() + j);
+      j--;
+      n--;
+    }
+  }
+}
+
+void forceRemoveQTHT()
+{
+  int n = (int)m_ComprCUCtxList.back().testModes.size();
+  for (int j = 0; j < n; j++) 
+  {
+    const EncTestMode etm = m_ComprCUCtxList.back().testModes[j];
+    
+    if (etm.type == ETM_SPLIT_QT || etm.type == ETM_SPLIT_BT_H || etm.type == ETM_SPLIT_TT_H) 
+    {
+      m_ComprCUCtxList.back().testModes.erase(m_ComprCUCtxList.back().testModes.begin() + j);
+      j--;
+      n--;
+    }
+  }
+}
+
+void forceRemoveAllSplit()
+{
+  int n = (int)m_ComprCUCtxList.back().testModes.size();
+  for (int j = 0; j < n; j++) 
+  {
+    const EncTestMode etm = m_ComprCUCtxList.back().testModes[j];
+
+    if (etm.type == ETM_SPLIT_QT || etm.type == ETM_SPLIT_BT_H || etm.type == ETM_SPLIT_BT_V || etm.type == ETM_SPLIT_TT_H || etm.type == ETM_SPLIT_TT_V) 
+    {
+      m_ComprCUCtxList.back().testModes.erase(m_ComprCUCtxList.back().testModes.begin() + j);
+      j--;
+      n--;
+    }
+  }
+}
+
+void forceQTonlyMode()
+{
+  // remove all split except QT  
+  int n = (int)m_ComprCUCtxList.back().testModes.size();
+  for (int j = 0; j < n; j++) 
+  {
+    const EncTestMode etm = m_ComprCUCtxList.back().testModes[j];
+
+    if (etm.type != ETM_SPLIT_QT) 
+    {
+      m_ComprCUCtxList.back().testModes.erase(m_ComprCUCtxList.back().testModes.begin() + j);
+      j--;
+      n--;        
+    }
+  }    
+}
+
+const char* printType(EncTestModeType type)
+{
+  char *ret;
+
+  switch (type) {
+  case  0: ret = strdup("Hash"); break;
+  case  1: ret = strdup("Mkip"); break;
+  case  2: ret = strdup("Inter"); break;
+  case  3: ret = strdup("Affi"); break;
+  case  4: ret = strdup("Tria"); break;
+  case  5: ret = strdup("Intra"); break;
+  case  6: ret = strdup("Palet"); break;
+
+  case  7: ret = strdup("QT"); break;
+  case  8: ret = strdup("BTH"); break;
+  case  9: ret = strdup("BTV"); break;
+  case 10: ret = strdup("TTH"); break;
+  case 11: ret = strdup("TTV"); break;
+  case 12: ret = strdup("|"); break;
+  case 13: ret = strdup("CACHE"); break;
+  case 14: ret = strdup("IMV"); break;
+  case 15: ret = strdup("IBC"); break;
+  case 16: ret = strdup("IBCM"); break;
+  default:
+    ret = strdup("INVALID");
+  }
+
+  return ret;
+}
+
+void printMode()
+{
+  // remove all inter or split to force make intra          
+  int n = (int)m_ComprCUCtxList.back().testModes.size();
+  printf("-:[");
+  for (int j = 0; j < n; j++) 
+  {
+    const EncTestMode etm = m_ComprCUCtxList.back().testModes[j];      
+    printf(" %s", printType(etm.type));      
+  }
+  printf("]\n");   
+}
+#endif
+
 protected:
   void xExtractFeatures ( const EncTestMode encTestmode, CodingStructure& cs );
   void xGetMinMaxQP     ( int& iMinQP, int& iMaxQP, const CodingStructure& cs, const Partitioner &pm, const int baseQP, const SPS& sps, const PPS& pps, const PartSplit splitMode );
diff --git a/source/Lib/EncoderLib/EncReshape.cpp b/source/Lib/EncoderLib/EncReshape.cpp
index ac70e08d426aa256fb1e2d857aec405d75aef16c..b48822358bf7d1636cb5c69eb4724a5706a9769f 100644
--- a/source/Lib/EncoderLib/EncReshape.cpp
+++ b/source/Lib/EncoderLib/EncReshape.cpp
@@ -444,6 +444,9 @@ void EncReshape::preAnalyzerLMCS(Picture *pcPic, const uint32_t signalType, cons
   m_sliceReshapeInfo.sliceReshaperModelPresentFlag = true;
   m_sliceReshapeInfo.sliceReshaperEnableFlag = true;
   int modIP = pcPic->getPOC() - pcPic->getPOC() / reshapeCW.rspFpsToIp * reshapeCW.rspFpsToIp;
+#if GDR_ENABLED
+  if (pcPic->cs->slice->isInterGDR()) modIP = 0;
+#endif
   if (sliceType == I_SLICE || (reshapeCW.updateCtrl == 2 && modIP == 0))
   {
     if (m_sliceReshapeInfo.sliceReshaperModelPresentFlag == true)
diff --git a/source/Lib/EncoderLib/EncSlice.cpp b/source/Lib/EncoderLib/EncSlice.cpp
index bbf3fe06a0901a652bfc1a1a16b7694fdb5c6f8c..31a81ff8e2083288fe027dbcd42771ecdc6392dc 100644
--- a/source/Lib/EncoderLib/EncSlice.cpp
+++ b/source/Lib/EncoderLib/EncSlice.cpp
@@ -438,6 +438,12 @@ void EncSlice::initEncSlice(Picture* pcPic, const int pocLast, const int pocCurr
       else
       {
         eSliceType = (pocLast == 0 || (pocCurr - (isField ? 1 : 0)) % (m_pcCfg->getIntraPeriod() * multipleFactor) == 0 || m_pcGOPEncoder->getGOPSize() == 0) && (!useIlRef) ? I_SLICE : eSliceType;
+#if GDR_ENABLED
+        if (pocCurr >= m_pcCfg->getGdrPocStart() && ((pocCurr - m_pcCfg->getGdrPocStart()) % (m_pcCfg->getGdrPeriod() * m_pcCfg->getGdrFrequency()) == 0)) 
+        {
+          eSliceType = B_SLICE;
+        }
+#endif
       }
     }
   }
@@ -603,6 +609,12 @@ void EncSlice::initEncSlice(Picture* pcPic, const int pocLast, const int pocCurr
         else
         {
           eSliceType = (pocLast == 0 || (pocCurr - (isField ? 1 : 0)) % (m_pcCfg->getIntraPeriod() * multipleFactor) == 0 || m_pcGOPEncoder->getGOPSize() == 0) && (!useIlRef) ? I_SLICE : eSliceType;
+#if GDR_ENABLED
+          if (pocCurr >= m_pcCfg->getGdrPocStart() && ((pocCurr - m_pcCfg->getGdrPocStart()) % (m_pcCfg->getGdrPeriod() * m_pcCfg->getGdrFrequency()) == 0)) 
+          {
+            eSliceType = B_SLICE;
+          }
+#endif
         }
       }
     }
@@ -705,6 +717,130 @@ void EncSlice::initEncSlice(Picture* pcPic, const int pocLast, const int pocCurr
     m_pcCuEncoder->getIbcHashMap().destroy();
     m_pcCuEncoder->getIbcHashMap().init( pcPic->cs->pps->getPicWidthInLumaSamples(), pcPic->cs->pps->getPicHeightInLumaSamples() );
   }
+#if GDR_ENABLED
+  int gdrPocStart   = m_pcCuEncoder->getEncCfg()->getGdrPocStart();
+  int gdrPeriod     = m_pcCuEncoder->getEncCfg()->getGdrPeriod();
+  int gdr_frequency = m_pcCuEncoder->getEncCfg()->getGdrFrequency();
+
+  int picWidth    = rpcSlice->getPPS()->getPicWidthInLumaSamples();  
+  
+  int curPoc      = rpcSlice->getPOC();
+  int gdrPoc      = (curPoc - gdrPocStart) % gdrPeriod;  
+
+  pcPic->cs->picHeader->setGdrPicFlag(false);
+  pcPic->cs->picHeader->setRecoveryPocCnt(0);
+  pcPic->cs->picHeader->setInGdrPeriod(false);
+  
+  pcPic->cs->picHeader->setVirtualBoundariesPresentFlag(false);    
+
+  int  offset          = (curPoc < gdrPocStart) ? 0 : (((curPoc - gdrPocStart) / gdrPeriod) * gdrPeriod);
+  int  actualGdrStart  = gdrPocStart + offset;
+  int  actualGdrPeriod = min(gdrPeriod, (int)(pcPic->getPicWidthInLumaSamples() / 8));
+  int  actualGdrEndPoc = actualGdrStart + actualGdrPeriod - 1;
+
+  bool isActualGdrPeriod    = (curPoc >= gdrPocStart) && ((((curPoc - gdrPocStart) / gdrPeriod) % gdr_frequency) == 0);
+  bool isGdrPeriodOver      = (isActualGdrPeriod) && (curPoc > actualGdrEndPoc);
+  bool isGdrPic             = (actualGdrStart == curPoc);
+
+#if GDR_ENC_TRACE
+  printf("\n");
+  printf("-poc:%d actualGdrStart:%d actualGdrPeriod:%d actualGdrEndPoc:%d\n", rpcSlice->getPOC(), actualGdrStart, actualGdrPeriod, actualGdrEndPoc);
+#endif
+
+  // for first picture of the sequence (GDR picture with recovery_poc_cnt = 0)
+  if (curPoc == 0 && m_pcCfg->getStartWithGdr())
+  {
+    pcPic->cs->picHeader->setGdrOrIrapPicFlag(true);
+    pcPic->cs->picHeader->setGdrPicFlag(true);
+
+    pcPic->cs->picHeader->setInGdrPeriod(true);
+    pcPic->cs->picHeader->setVirtualBoundariesPresentFlag(true);
+
+    if (m_pcCfg->getGdrPicOutput())
+      pcPic->cs->picHeader->setPicOutputFlag(true);
+    else
+      pcPic->cs->picHeader->setPicOutputFlag(false);
+    
+    pcPic->cs->picHeader->setRecoveryPocCnt(0);
+      
+    pcPic->cs->picHeader->setNumHorVirtualBoundaries(0);
+    pcPic->cs->picHeader->setNumVerVirtualBoundaries(1);
+    pcPic->cs->picHeader->setVirtualBoundariesPosX(picWidth, 0);
+  }
+  // for none gdr period pictures
+  else if ((curPoc < gdrPocStart) || !isActualGdrPeriod || isGdrPeriodOver)
+  {
+    pcPic->cs->picHeader->setInGdrPeriod(false);
+    pcPic->cs->picHeader->setVirtualBoundariesPresentFlag(false);
+
+    pcPic->cs->picHeader->setNumHorVirtualBoundaries(0);
+    pcPic->cs->picHeader->setNumVerVirtualBoundaries(0);
+
+#if GDR_ENC_TRACE    
+    printf("-poc:%d no virtual boundary\n", rpcSlice->getPOC());
+#endif
+  }
+  // for gdr period pictures
+  else 
+  {             
+    pcPic->cs->picHeader->setInGdrPeriod(true);
+    pcPic->cs->picHeader->setVirtualBoundariesPresentFlag(true);
+
+    if (m_pcCfg->getGdrPicOutput())
+      pcPic->cs->picHeader->setPicOutputFlag(true);
+    else
+      pcPic->cs->picHeader->setPicOutputFlag(false);
+
+    if (isGdrPic)
+    {
+      pcPic->cs->picHeader->setGdrOrIrapPicFlag(true);
+      pcPic->cs->picHeader->setGdrPicFlag(true);
+      
+      pcPic->cs->picHeader->setRecoveryPocCnt(actualGdrPeriod);
+      m_pcGOPEncoder->setLastGdrPeriodPoc(actualGdrStart + actualGdrPeriod - 1);
+    }
+
+    pcPic->cs->picHeader->setNumHorVirtualBoundaries(0);
+    pcPic->cs->picHeader->setNumVerVirtualBoundaries(1);
+
+    int begGdrX;
+    int endGdrX;
+    int m1, m2, n1;
+
+    double dd = (picWidth / (double)gdrPeriod);
+    int mm = (int)((picWidth / (double)gdrPeriod) + 0.49999);
+    m1 = ((mm + 7) >> 3) << 3;
+    m2 = ((mm + 0) >> 3) << 3;
+
+    if (dd > mm && m1 == m2)
+      m1 = m1 + 8;
+     
+    n1 = (picWidth - m2 * gdrPeriod) / 8;    
+
+    if (gdrPoc < n1)
+    {
+      begGdrX = m1 * gdrPoc;
+      endGdrX = begGdrX + m1;
+    }
+    else
+    {
+      begGdrX = m1 * n1 + m2 * (gdrPoc - n1);
+      endGdrX   = begGdrX + m2;
+	    if (picWidth <= begGdrX)
+	    {
+          begGdrX = picWidth;
+          endGdrX = picWidth;
+	    }
+    }
+         
+    pcPic->cs->picHeader->setVirtualBoundariesPosX(endGdrX, 0);
+
+#if GDR_ENC_TRACE
+    printf("\n");
+    printf("-poc:%d beg:%d end:%d\n", rpcSlice->getPOC(), begGdrX, endGdrX);
+#endif    
+  } 
+#endif
 }
 
 double EncSlice::initializeLambda(const Slice* slice, const int GOPid, const int refQP, const double dQP)
diff --git a/source/Lib/EncoderLib/InterSearch.cpp b/source/Lib/EncoderLib/InterSearch.cpp
index 07e7e237533d7d966f483aff64709538c082d166..c08b3d300291a97d38f6545111345729ee56d674 100644
--- a/source/Lib/EncoderLib/InterSearch.cpp
+++ b/source/Lib/EncoderLib/InterSearch.cpp
@@ -110,6 +110,9 @@ InterSearch::InterSearch()
 
   setWpScalingDistParam( -1, REF_PIC_LIST_X, nullptr );
   m_affMVList = nullptr;
+#if GDR_ENABLED
+  m_affMVListSolid = nullptr;
+#endif
   m_affMVListSize = 0;
   m_affMVListIdx = 0;
   m_uniMvList = nullptr;
@@ -158,6 +161,14 @@ void InterSearch::destroy()
     delete[] m_affMVList;
     m_affMVList = nullptr;
   }
+#if GDR_ENABLED
+  if (m_affMVListSolid)
+  {
+    delete[] m_affMVListSolid;
+    m_affMVListSolid = nullptr;
+  }
+#endif
+
   m_affMVListIdx = 0;
   m_affMVListSize = 0;
   if (m_uniMvList)
@@ -258,6 +269,12 @@ void InterSearch::init( EncCfg*        pcEncCfg,
   if (!m_affMVList)
   {
     m_affMVList = new AffineMVInfo[m_affMVListMaxSize];
+#if GDR_ENABLED
+    if (!m_affMVListSolid)
+    {
+      m_affMVListSolid = new AffineMVInfoSolid[m_affMVListMaxSize];
+    }
+#endif
   }
   m_affMVListIdx = 0;
   m_affMVListSize = 0;
@@ -279,8 +296,15 @@ void InterSearch::resetSavedAffineMotion()
     {
       m_affineMotion.acMvAffine4Para[i][j] = Mv( 0, 0 );
       m_affineMotion.acMvAffine6Para[i][j] = Mv( 0, 0 );
+#if GDR_ENABLED
+      m_affineMotion.acMvAffine4ParaSolid[i][j] = true;
+      m_affineMotion.acMvAffine6ParaSolid[i][j] = true;
+#endif
     }
     m_affineMotion.acMvAffine6Para[i][2] = Mv( 0, 0 );
+#if GDR_ENABLED
+    m_affineMotion.acMvAffine6ParaSolid[i][2] = true;
+#endif
 
     m_affineMotion.affine4ParaRefIdx[i] = -1;
     m_affineMotion.affine6ParaRefIdx[i] = -1;
@@ -293,7 +317,11 @@ void InterSearch::resetSavedAffineMotion()
   m_affineMotion.affine6ParaAvail = false;
 }
 
+#if GDR_ENABLED
+void InterSearch::storeAffineMotion(Mv acAffineMv[2][3], bool acAffineMvSolid[2][3], int16_t affineRefIdx[2], EAffineModel affineType, int bcwIdx)
+#else
 void InterSearch::storeAffineMotion( Mv acAffineMv[2][3], int16_t affineRefIdx[2], EAffineModel affineType, int bcwIdx )
+#endif
 {
   if ( ( bcwIdx == BCW_DEFAULT || !m_affineMotion.affine6ParaAvail ) && affineType == AFFINEMODEL_6PARAM )
   {
@@ -302,6 +330,9 @@ void InterSearch::storeAffineMotion( Mv acAffineMv[2][3], int16_t affineRefIdx[2
       for ( int j = 0; j < 3; j++ )
       {
         m_affineMotion.acMvAffine6Para[i][j] = acAffineMv[i][j];
+#if GDR_ENABLED
+        m_affineMotion.acMvAffine6ParaSolid[i][j] = acAffineMvSolid[i][j];
+#endif
       }
       m_affineMotion.affine6ParaRefIdx[i] = affineRefIdx[i];
     }
@@ -315,6 +346,9 @@ void InterSearch::storeAffineMotion( Mv acAffineMv[2][3], int16_t affineRefIdx[2
       for ( int j = 0; j < 2; j++ )
       {
         m_affineMotion.acMvAffine4Para[i][j] = acAffineMv[i][j];
+#if GDR_ENABLED
+        m_affineMotion.acMvAffine4ParaSolid[i][j] = acAffineMvSolid[i][j];
+#endif
       }
       m_affineMotion.affine4ParaRefIdx[i] = affineRefIdx[i];
     }
@@ -698,21 +732,46 @@ inline void InterSearch::xTZ8PointDiamondSearch( IntTZSearchStruct& rcStruct,
     } // iDist <= 8
   } // iDist == 1
 }
+#if GDR_ENABLED
+Distortion InterSearch::xPatternRefinement(
+  const PredictionUnit& pu,
+  RefPicList eRefPicList,
+  int iRefIdx,
+  const CPelBuf* pcPatternKey,
+  Mv baseRefMv,
+  int iFrac, Mv& rcMvFrac,
+  bool bAllowUseOfHadamard,
+  bool& rbCleanCandExist)
+#else
 
 Distortion InterSearch::xPatternRefinement( const CPelBuf* pcPatternKey,
                                             Mv baseRefMv,
                                             int iFrac, Mv& rcMvFrac,
                                             bool bAllowUseOfHadamard )
+#endif 
 {
   Distortion  uiDist;
   Distortion  uiDistBest  = std::numeric_limits<Distortion>::max();
   uint32_t        uiDirecBest = 0;
 
+#if GDR_ENABLED
+  const CodingStructure &cs = *pu.cs;
+  const bool isEncodeClean = cs.pcv->isEncoder && ((cs.picHeader->getInGdrPeriod() && cs.isClean(pu.Y().topRight(), CHANNEL_TYPE_LUMA)) || (cs.picHeader->getNumVerVirtualBoundaries() == 0));
+  bool uiDistOk = false;
+  bool uiDistBestOk = false;
+  bool allOk = true;
+#endif
   Pel*  piRefPos;
   int iRefStride = pcPatternKey->width + 1;
   m_pcRdCost->setDistParam( m_cDistParam, *pcPatternKey, m_filteredBlock[0][0][0], iRefStride, m_lumaClpRng.bd, COMPONENT_Y, 0, 1, m_pcEncCfg->getUseHADME() && bAllowUseOfHadamard );
 
   const Mv* pcMvRefine = (iFrac == 2 ? s_acMvRefineH : s_acMvRefineQ);
+#if GDR_ENABLED
+  if (isEncodeClean)
+  {
+    rbCleanCandExist = false;
+  }
+#endif
   for (uint32_t i = 0; i < 9; i++)
   {
     if (m_skipFracME && i > 0)
@@ -742,12 +801,51 @@ Distortion InterSearch::xPatternRefinement( const CPelBuf* pcPatternKey,
     uiDist = m_cDistParam.distFunc( m_cDistParam );
     uiDist += m_pcRdCost->getCostOfVectorWithPredictor( cMvTest.getHor(), cMvTest.getVer(), 0 );
 
+#if GDR_ENABLED
+    allOk = (uiDist < uiDistBest);
+
+    if (isEncodeClean)
+    {
+      Mv motion = cMvTest;
+      MvPrecision curPrec = (iFrac == 2 ? MV_PRECISION_HALF : MV_PRECISION_QUARTER);
+      motion.changePrecision(curPrec, MV_PRECISION_INTERNAL);
+      uiDistOk = cs.isClean(pu.Y().bottomRight(), motion, eRefPicList, iRefIdx);
+
+      if (uiDistOk)
+      {
+        allOk = (uiDistBestOk) ? (uiDist < uiDistBest) : true;
+      }
+      else
+      {
+        allOk = false;
+      }
+    }
+#endif
+
+#if GDR_ENABLED
+    if (allOk)
+#else
     if ( uiDist < uiDistBest )
+#endif
     {
       uiDistBest  = uiDist;
       uiDirecBest = i;
       m_cDistParam.maximumDistortionForEarlyExit = uiDist;
+#if GDR_ENABLED
+      if (isEncodeClean)
+      {
+        uiDistBestOk = uiDistOk;
+        rbCleanCandExist = true;
+      }
+#endif
     }
+#if GDR_ENABLED
+    if (isEncodeClean)
+    {
+      if (!rbCleanCandExist)
+        uiDistBest = 65535;
+    }
+#endif
   }
 
   rcMvFrac = pcMvRefine[uiDirecBest];
@@ -846,6 +944,20 @@ int InterSearch::xIBCSearchMVChromaRefine(PredictionUnit& pu,
       continue;
     }
 
+#if GDR_ENABLED
+    CodingStructure &cs = *pu.cs;
+    const bool isEncodeClean = cs.pcv->isEncoder && ((cs.picHeader->getInGdrPeriod() && cs.isClean(pu.Y().topRight(), CHANNEL_TYPE_LUMA)) || (cs.picHeader->getNumVerVirtualBoundaries() == 0));
+
+    if (isEncodeClean)
+    {
+      Position curBR(cuPelX + roiWidth + cMVCand[cand].getHor() - 1, cuPelY + roiHeight + cMVCand[cand].getVer() - 1);    // is this correct???
+      if (!cs.isClean(curBR, CHANNEL_TYPE_LUMA))
+      {
+        continue;
+      }
+    }
+#endif
+
     tempSad = sadBestCand[cand];
 
     pu.mv[0] = cMVCand[cand];
@@ -957,6 +1069,10 @@ void InterSearch::xIntraPatternSearch(PredictionUnit& pu, IntTZSearchStruct&  cS
   Distortion  sadBestCand[CHROMA_REFINEMENT_CANDIDATES];
   Mv      cMVCand[CHROMA_REFINEMENT_CANDIDATES];
 
+#if GDR_ENABLED
+  CodingStructure &cs = *pu.cs;
+  const bool isEncodeClean = cs.pcv->isEncoder && ((cs.picHeader->getInGdrPeriod() && cs.isClean(pu.Y().topRight(), CHANNEL_TYPE_LUMA)) || (cs.picHeader->getNumVerVirtualBoundaries() == 0));
+#endif
 
   for (int cand = 0; cand < CHROMA_REFINEMENT_CANDIDATES; cand++)
   {
@@ -995,7 +1111,13 @@ void InterSearch::xIntraPatternSearch(PredictionUnit& pu, IntTZSearchStruct&  cS
         && !((xPred < srLeft) || (xPred > srRight)))
       {
         bool validCand = searchBv(pu, cuPelX, cuPelY, roiWidth, roiHeight, picWidth, picHeight, xPred, yPred, lcuWidth);
-
+#if GDR_ENABLED
+        if (isEncodeClean)
+        {
+          Position BvBR(cuPelX + roiWidth + xPred - 1, cuPelY + roiHeight + yPred - 1);
+          validCand = validCand && cs.isClean(BvBR, CHANNEL_TYPE_LUMA);
+        }
+#endif
         if (validCand)
         {
           sad = m_pcRdCost->getBvCostMultiplePreds(xPred, yPred, pu.cs->sps->getAMVREnabledFlag());
@@ -1019,6 +1141,16 @@ void InterSearch::xIntraPatternSearch(PredictionUnit& pu, IntTZSearchStruct&  cS
       {
         continue;
       }
+#if GDR_ENABLED
+      if (isEncodeClean)
+      {
+        Position BvBR(cuPelX + roiWidth - 1, cuPelY + roiHeight + y - 1);
+        if (!cs.isClean(BvBR, CHANNEL_TYPE_LUMA))
+        {
+          continue;
+        }
+      }
+#endif
 
       sad = m_pcRdCost->getBvCostMultiplePreds(0, y, pu.cs->sps->getAMVREnabledFlag());
       m_cDistParam.cur.buf = piRefSrch + cStruct.iRefStride * y;
@@ -1044,6 +1176,16 @@ void InterSearch::xIntraPatternSearch(PredictionUnit& pu, IntTZSearchStruct&  cS
       {
         continue;
       }
+#if GDR_ENABLED
+      if (isEncodeClean)
+      {
+        Position BvBR(cuPelX + roiWidth + x - 1, cuPelY + roiHeight - 1);
+        if (!cs.isClean(BvBR, CHANNEL_TYPE_LUMA))
+        {
+          continue;
+        }
+      }
+#endif
 
       sad = m_pcRdCost->getBvCostMultiplePreds(x, 0, pu.cs->sps->getAMVREnabledFlag());
       m_cDistParam.cur.buf = piRefSrch + x;
@@ -1100,6 +1242,17 @@ void InterSearch::xIntraPatternSearch(PredictionUnit& pu, IntTZSearchStruct&  cS
             continue;
           }
 
+#if GDR_ENABLED
+          if (isEncodeClean)
+          {
+            Position BvBR(cuPelX + roiWidth + x - 1, cuPelY + roiHeight + y - 1);
+            if (!cs.isClean(BvBR, CHANNEL_TYPE_LUMA))
+            {
+              continue;
+            }
+          }
+#endif
+
           sad = m_pcRdCost->getBvCostMultiplePreds(x, y, pu.cs->sps->getAMVREnabledFlag());
           m_cDistParam.cur.buf = piRefSrch + cStruct.iRefStride * y + x;
           sad += m_cDistParam.distFunc(m_cDistParam);
@@ -1144,6 +1297,17 @@ void InterSearch::xIntraPatternSearch(PredictionUnit& pu, IntTZSearchStruct&  cS
             continue;
           }
 
+#if GDR_ENABLED
+          if (isEncodeClean)
+          {
+            Position BvBR(cuPelX + roiWidth + x - 1, cuPelY + roiHeight + y - 1);
+            if (!cs.isClean(BvBR, CHANNEL_TYPE_LUMA))
+            {
+              continue;
+            }
+          }
+#endif
+
           sad = m_pcRdCost->getBvCostMultiplePreds(x, y, pu.cs->sps->getAMVREnabledFlag());
           m_cDistParam.cur.buf = piRefSrch + cStruct.iRefStride * y + x;
           sad += m_cDistParam.distFunc(m_cDistParam);
@@ -1202,6 +1366,16 @@ void InterSearch::xIntraPatternSearch(PredictionUnit& pu, IntTZSearchStruct&  cS
           {
             continue;
           }
+#if GDR_ENABLED
+          if (isEncodeClean)
+          {
+            Position BvBR(cuPelX + roiWidth + x - 1, cuPelY + roiHeight + y - 1);
+            if (!cs.isClean(BvBR, CHANNEL_TYPE_LUMA))
+            {
+              continue;
+            }
+          }
+#endif
 
           sad = m_pcRdCost->getBvCostMultiplePreds(x, y, pu.cs->sps->getAMVREnabledFlag());
           m_cDistParam.cur.buf = piRefSrch + cStruct.iRefStride * y + x;
@@ -1277,6 +1451,11 @@ void InterSearch::xIBCEstimation(PredictionUnit& pu, PelUnitBuf& origBuf,
   CPelBuf* pcPatternKey = &tmpPattern;
   PelBuf tmpOrgLuma;
 
+#if GDR_ENABLED
+  CodingStructure &cs = *pu.cs;
+  const bool isEncodeClean = cs.pcv->isEncoder && ((cs.picHeader->getInGdrPeriod() && cs.isClean(pu.Y().topRight(), CHANNEL_TYPE_LUMA)) || (cs.picHeader->getNumVerVirtualBoundaries() == 0));
+#endif
+
   if ((pu.cs->slice->getLmcsEnabledFlag() && m_pcReshape->getCTUFlag()))
   {
     const CompArea &area = pu.blocks[COMPONENT_Y];
@@ -1319,7 +1498,17 @@ void InterSearch::xIBCEstimation(PredictionUnit& pu, PelUnitBuf& origBuf,
 
       int xBv = bv.hor;
       int yBv = bv.ver;
+#if GDR_ENABLED
+      bool validCand = true;
+      if (isEncodeClean)
+      {
+        Position BvBR(cuPelX + iRoiWidth + xBv - 1, cuPelY + iRoiHeight + yBv - 1);
+        validCand = validCand && cs.isClean(BvBR, CHANNEL_TYPE_LUMA);
+      }
+      if (validCand && searchBv(pu, cuPelX, cuPelY, iRoiWidth, iRoiHeight, iPicWidth, iPicHeight, xBv, yBv, lcuWidth))
+#else
       if (searchBv(pu, cuPelX, cuPelY, iRoiWidth, iRoiHeight, iPicWidth, iPicHeight, xBv, yBv, lcuWidth))
+#endif
       {
         buffered = true;
         Distortion sad = m_pcRdCost->getBvCostMultiplePreds(xBv, yBv, pu.cs->sps->getAMVREnabledFlag());
@@ -1354,7 +1543,17 @@ void InterSearch::xIBCEstimation(PredictionUnit& pu, PelUnitBuf& origBuf,
         int xPred = cMvPredEncOnly[cand].getHor();
         int yPred = cMvPredEncOnly[cand].getVer();
 
+#if GDR_ENABLED
+        bool validCand = true;
+        if (isEncodeClean)
+        {
+          Position BvBR(cuPelX + iRoiWidth + xPred - 1, cuPelY + iRoiHeight + yPred - 1);
+          validCand = cs.isClean(BvBR, CHANNEL_TYPE_LUMA);
+        }
+        if (validCand && searchBv(pu, cuPelX, cuPelY, iRoiWidth, iRoiHeight, iPicWidth, iPicHeight, xPred, yPred, lcuWidth))
+#else
         if (searchBv(pu, cuPelX, cuPelY, iRoiWidth, iRoiHeight, iPicWidth, iPicHeight, xPred, yPred, lcuWidth))
+#endif
         {
           Distortion sad = m_pcRdCost->getBvCostMultiplePreds(xPred, yPred, pu.cs->sps->getAMVREnabledFlag());
           m_cDistParam.cur.buf = cStruct.piRefY + cStruct.iRefStride * yPred + xPred;
@@ -1443,6 +1642,20 @@ bool InterSearch::predIBCSearch(CodingUnit& cu, Partitioner& partitioner, const
     m_maxCompIDToPred = MAX_NUM_COMPONENT;
 
     CHECK(pu.cu != &cu, "PU is contained in another CU");
+#if GDR_ENABLED
+    CodingStructure &cs = *pu.cs;
+    const bool isEncodeClean = cs.pcv->isEncoder && ((cs.picHeader->getInGdrPeriod() && cs.isClean(pu.Y().topRight(), CHANNEL_TYPE_LUMA)) || (cs.picHeader->getNumVerVirtualBoundaries() == 0));
+#endif
+
+#if GDR_ENABLED  
+    if (isEncodeClean)
+    {
+      pu.mvSolid[0] = false;
+      pu.mvSolid[1] = false;
+      pu.mvValid[0] = false;
+      pu.mvValid[1] = false;
+    }
+#endif
     //////////////////////////////////////////////////////////
     /// ibc search
     pu.cu->imv = 2;
@@ -1580,6 +1793,10 @@ void InterSearch::xxIBCHashSearch(PredictionUnit& pu, Mv* mvPred, int numMvPred,
   mv.setZero();
   m_pcRdCost->setCostScale(0);
 
+#if GDR_ENABLED
+  CodingStructure &cs = *pu.cs;
+  const bool isEncodeClean = cs.pcv->isEncoder && ((cs.picHeader->getInGdrPeriod() && cs.isClean(pu.Y().topRight(), CHANNEL_TYPE_LUMA)) || (cs.picHeader->getNumVerVirtualBoundaries() == 0));
+#endif
   std::vector<Position> candPos;
   if (ibcHashMap.ibcHashMatch(pu.Y(), candPos, *pu.cs, m_pcEncCfg->getIBCHashSearchMaxCand(), m_pcEncCfg->getIBCHashSearchRange4SmallBlk()))
   {
@@ -1606,6 +1823,13 @@ void InterSearch::xxIBCHashSearch(PredictionUnit& pu, Mv* mvPred, int numMvPred,
         {
           continue;
         }
+#if GDR_ENABLED
+        Position BvBR(cuPelX + roiWidth + candMv.getHor() - 1, cuPelY + roiHeight + candMv.getVer() - 1);
+        if (isEncodeClean && !cs.isClean(BvBR, CHANNEL_TYPE_LUMA))
+        {
+          continue;
+        }
+#endif
 
         for (int n = 0; n < numMvPred; n++)
         {
@@ -1809,6 +2033,11 @@ bool InterSearch::xRectHashInterEstimation(PredictionUnit& pu, RefPicList& bestR
   unsigned int* hashValue1s = new unsigned int[baseNum];
   unsigned int* hashValue2s = new unsigned int[baseNum];
 
+#if GDR_ENABLED
+  CodingStructure &cs = *pu.cs;
+  const bool isEncodeClean = cs.pcv->isEncoder && ((cs.picHeader->getInGdrPeriod() && cs.isClean(pu.Y().topRight(), CHANNEL_TYPE_LUMA)) || (cs.picHeader->getNumVerVirtualBoundaries() == 0));
+#endif
+
   for (int k = 0; k < baseNum; k++)
   {
     if (isHorizontal)
@@ -1890,6 +2119,26 @@ bool InterSearch::xRectHashInterEstimation(PredictionUnit& pu, RefPicList& bestR
         AMVPInfo currAMVPInfoPel;
         AMVPInfo currAMVPInfo4Pel;
         AMVPInfo currAMVPInfoQPel;
+
+#if GDR_ENABLED
+        if (isEncodeClean)
+        {
+          currAMVPInfoPel.allCandSolidInAbove = true;
+          currAMVPInfo4Pel.allCandSolidInAbove = true;
+          currAMVPInfoQPel.allCandSolidInAbove = true;
+
+          for (int i = 0; i < AMVP_MAX_NUM_CANDS_MEM; i++)
+          {
+            currAMVPInfoPel.mvSolid[i] = true;
+            currAMVPInfoPel.mvValid[i] = true;
+            currAMVPInfo4Pel.mvSolid[i] = true;
+            currAMVPInfo4Pel.mvValid[i] = true;
+            currAMVPInfoQPel.mvSolid[i] = true;
+            currAMVPInfoQPel.mvValid[i] = true;
+          }
+        }
+#endif
+
         pu.cu->imv = 2;
         PU::fillMvpCand(pu, eRefPicList, refIdx, currAMVPInfo4Pel);
         pu.cu->imv = 1;
@@ -1921,6 +2170,25 @@ bool InterSearch::xRectHashInterEstimation(PredictionUnit& pu, RefPicList& bestR
           m_hashMVStoreds[eRefPicList][refIdx][countMV++] = cMv;
           cMv.changePrecision(MV_PRECISION_INT, MV_PRECISION_QUARTER);
 
+#if GDR_ENABLED
+          bool allOk = true;
+          bool anyCandOk = false;
+          bool Valid = true;
+          if (isEncodeClean)
+          {
+            Mv cMv16 = cMv;
+            cMv16.changePrecision(MV_PRECISION_QUARTER, MV_PRECISION_INTERNAL);
+            const Position bottomRight = pu.Y().bottomRight();
+            Valid = cs.isClean(bottomRight, cMv16, eRefPicList, refIdx);
+          }
+#endif
+
+#if GDR_ENABLED
+          if (!Valid)
+          {
+            continue;
+          }
+#endif
           for (int mvpIdxTemp = 0; mvpIdxTemp < 2; mvpIdxTemp++)
           {
             Mv cMvPredPel = currAMVPInfoQPel.mvCand[mvpIdxTemp];
@@ -1928,7 +2196,21 @@ bool InterSearch::xRectHashInterEstimation(PredictionUnit& pu, RefPicList& bestR
 
             unsigned int tempMVPbits = m_pcRdCost->getBitsOfVectorWithPredictor(cMv.getHor(), cMv.getVer(), 0);
 
+#if GDR_ENABLED     
+            allOk = (tempMVPbits < curMVPbits);
+            if (isEncodeClean)
+            {
+              bool isSolid = currAMVPInfoQPel.mvSolid[mvpIdxTemp];
+              allOk = allOk && isSolid;
+              if (allOk) anyCandOk = true;
+            }
+#endif    
+
+#if GDR_ENABLED 
+            if (allOk)
+#else
             if (tempMVPbits < curMVPbits)
+#endif
             {
               curMVPbits = tempMVPbits;
               curMVPIdx = mvpIdxTemp;
@@ -1941,7 +2223,21 @@ bool InterSearch::xRectHashInterEstimation(PredictionUnit& pu, RefPicList& bestR
               Mv mvPred1Pel = currAMVPInfoPel.mvCand[mvpIdxTemp];
               m_pcRdCost->setPredictor(mvPred1Pel);
               bitsMVP1Pel = m_pcRdCost->getBitsOfVectorWithPredictor(cMv.getHor(), cMv.getVer(), 2);
+#if GDR_ENABLED     
+              allOk = (bitsMVP1Pel < curMVPbits);
+              if (isEncodeClean)
+              {
+                bool isSolid = currAMVPInfoPel.mvSolid[mvpIdxTemp];
+                allOk = allOk && isSolid;
+                if (allOk) anyCandOk = true;
+              }
+#endif   
+
+#if GDR_ENABLED 
+              if (allOk)
+#else
               if (bitsMVP1Pel < curMVPbits)
+#endif
               {
                 curMVPbits = bitsMVP1Pel;
                 curMVPIdx = mvpIdxTemp;
@@ -1954,7 +2250,21 @@ bool InterSearch::xRectHashInterEstimation(PredictionUnit& pu, RefPicList& bestR
                 Mv mvPred4Pel = currAMVPInfo4Pel.mvCand[mvpIdxTemp];
                 m_pcRdCost->setPredictor(mvPred4Pel);
                 bitsMVP4Pel = m_pcRdCost->getBitsOfVectorWithPredictor(cMv.getHor(), cMv.getVer(), 4);
+#if GDR_ENABLED     
+                allOk = (bitsMVP1Pel < curMVPbits);
+                if (isEncodeClean)
+                {
+                  bool isSolid = currAMVPInfo4Pel.mvSolid[mvpIdxTemp];
+                  allOk = allOk && isSolid;
+                  if (allOk) anyCandOk = true;
+                }
+#endif   
+
+#if GDR_ENABLED 
+                if (allOk)
+#else
                 if (bitsMVP4Pel < curMVPbits)
+#endif
                 {
                   curMVPbits = bitsMVP4Pel;
                   curMVPIdx = mvpIdxTemp;
@@ -1963,6 +2273,14 @@ bool InterSearch::xRectHashInterEstimation(PredictionUnit& pu, RefPicList& bestR
               }
             }
           }
+
+#if GDR_ENABLED 
+          if (isEncodeClean && !anyCandOk)
+          {
+            continue;
+          }
+#endif
+
           curMVPbits += bitsOnRefIdx;
 
           m_cDistParam.cur.buf = refBufStart + (*it).y*refStride + (*it).x;
@@ -2033,6 +2351,10 @@ bool InterSearch::xHashInterEstimation(PredictionUnit& pu, RefPicList& bestRefPi
     return false;
   }
 
+#if GDR_ENABLED
+  CodingStructure &cs = *pu.cs;
+  const bool isEncodeClean = cs.pcv->isEncoder && ((cs.picHeader->getInGdrPeriod() && cs.isClean(pu.Y().topRight(), CHANNEL_TYPE_LUMA)) || (cs.picHeader->getNumVerVirtualBoundaries() == 0));
+#endif
   BlockHash currBlockHash;
   currBlockHash.x = xPos;
   currBlockHash.y = yPos;
@@ -2087,11 +2409,45 @@ bool InterSearch::xHashInterEstimation(PredictionUnit& pu, RefPicList& bestRefPi
         }
         AMVPInfo currAMVPInfoPel;
         AMVPInfo currAMVPInfo4Pel;
+#if GDR_ENABLED
+        if (isEncodeClean)
+        {
+          currAMVPInfo4Pel.allCandSolidInAbove = true;
+          for (int i = 0; i < AMVP_MAX_NUM_CANDS_MEM; i++)
+          {
+            currAMVPInfo4Pel.mvSolid[i] = true;
+            currAMVPInfo4Pel.mvValid[i] = true;
+          }
+        }
+#endif
         pu.cu->imv = 2;
         PU::fillMvpCand(pu, eRefPicList, refIdx, currAMVPInfo4Pel);
+
+#if GDR_ENABLED
+        if (isEncodeClean)
+        {
+          currAMVPInfoPel.allCandSolidInAbove = true;
+          for (int i = 0; i < AMVP_MAX_NUM_CANDS_MEM; i++)
+          {
+            currAMVPInfoPel.mvSolid[i] = true;
+            currAMVPInfoPel.mvValid[i] = true;
+          }
+        }
+#endif
         pu.cu->imv = 1;
         PU::fillMvpCand(pu, eRefPicList, refIdx, currAMVPInfoPel);
         AMVPInfo currAMVPInfoQPel;
+#if GDR_ENABLED
+        if (isEncodeClean)
+        {
+          currAMVPInfoQPel.allCandSolidInAbove = true;
+          for (int i = 0; i < AMVP_MAX_NUM_CANDS_MEM; i++)
+          {
+            currAMVPInfoQPel.mvSolid[i] = true;
+            currAMVPInfoQPel.mvValid[i] = true;
+          }
+        }
+#endif
         pu.cu->imv = 0;
         PU::fillMvpCand(pu, eRefPicList, refIdx, currAMVPInfoQPel);
         CHECK(currAMVPInfoPel.numCand <= 1, "Wrong")
@@ -2121,6 +2477,27 @@ bool InterSearch::xHashInterEstimation(PredictionUnit& pu, RefPicList& bestRefPi
           m_hashMVStoreds[eRefPicList][refIdx][countMV++] = cMv;
           cMv.changePrecision(MV_PRECISION_INT, MV_PRECISION_QUARTER);
 
+#if GDR_ENABLED
+          bool Valid = true;
+          bool allOk = true;
+          bool anyCandOk = false;
+
+          if (isEncodeClean)
+          {
+            Mv cMv16 = cMv;
+            cMv16.changePrecision(MV_PRECISION_QUARTER, MV_PRECISION_INTERNAL);
+            const Position bottomRight = pu.Y().bottomRight();
+            Valid = cs.isClean(bottomRight, cMv16, eRefPicList, refIdx);
+          }
+#endif
+
+#if GDR_ENABLED
+          if (!Valid)
+          {
+            continue;
+          }
+#endif
+
           for (int mvpIdxTemp = 0; mvpIdxTemp < 2; mvpIdxTemp++)
           {
             Mv cMvPredPel = currAMVPInfoQPel.mvCand[mvpIdxTemp];
@@ -2128,7 +2505,21 @@ bool InterSearch::xHashInterEstimation(PredictionUnit& pu, RefPicList& bestRefPi
 
             unsigned int tempMVPbits = m_pcRdCost->getBitsOfVectorWithPredictor(cMv.getHor(), cMv.getVer(), 0);
 
+#if GDR_ENABLED     
+            allOk = (tempMVPbits < curMVPbits);
+            if (isEncodeClean)
+            {
+              bool isSolid = currAMVPInfoQPel.mvSolid[mvpIdxTemp];
+              allOk = allOk && isSolid;
+              if (allOk) anyCandOk = true;
+            }
+#endif    
+
+#if GDR_ENABLED 
+            if (allOk)
+#else
             if (tempMVPbits < curMVPbits)
+#endif            
             {
               curMVPbits = tempMVPbits;
               curMVPIdx = mvpIdxTemp;
@@ -2141,7 +2532,21 @@ bool InterSearch::xHashInterEstimation(PredictionUnit& pu, RefPicList& bestRefPi
               Mv mvPred1Pel = currAMVPInfoPel.mvCand[mvpIdxTemp];
               m_pcRdCost->setPredictor(mvPred1Pel);
               bitsMVP1Pel = m_pcRdCost->getBitsOfVectorWithPredictor(cMv.getHor(), cMv.getVer(), 2);
+#if GDR_ENABLED     
+              allOk = (bitsMVP1Pel < curMVPbits);
+              if (isEncodeClean)
+              {
+                bool isSolid = currAMVPInfoPel.mvSolid[mvpIdxTemp];
+                allOk = allOk && isSolid;
+                if (allOk) anyCandOk = true;
+              }
+#endif    
+
+#if GDR_ENABLED 
+              if (allOk)
+#else
               if (bitsMVP1Pel < curMVPbits)
+#endif
               {
                 curMVPbits = bitsMVP1Pel;
                 curMVPIdx = mvpIdxTemp;
@@ -2154,7 +2559,22 @@ bool InterSearch::xHashInterEstimation(PredictionUnit& pu, RefPicList& bestRefPi
                 Mv mvPred4Pel = currAMVPInfo4Pel.mvCand[mvpIdxTemp];
                 m_pcRdCost->setPredictor(mvPred4Pel);
                 bitsMVP4Pel = m_pcRdCost->getBitsOfVectorWithPredictor(cMv.getHor(), cMv.getVer(), 4);
+
+#if GDR_ENABLED     
+                allOk = (bitsMVP1Pel < curMVPbits);
+                if (isEncodeClean)
+                {
+                  bool isSolid = currAMVPInfo4Pel.mvSolid[mvpIdxTemp];
+                  allOk = allOk && isSolid;
+                  if (allOk) anyCandOk = true;
+                }
+#endif   
+
+#if GDR_ENABLED 
+                if (allOk)
+#else
                 if (bitsMVP4Pel < curMVPbits)
+#endif
                 {
                   curMVPbits = bitsMVP4Pel;
                   curMVPIdx = mvpIdxTemp;
@@ -2164,6 +2584,13 @@ bool InterSearch::xHashInterEstimation(PredictionUnit& pu, RefPicList& bestRefPi
             }
           }
 
+#if GDR_ENABLED 
+          if (isEncodeClean && !anyCandOk)
+          {
+            continue;
+          }
+#endif
+
           curMVPbits += bitsOnRefIdx;
 
           m_cDistParam.cur.buf = refBufStart + (*it).y*refStride + (*it).x;
@@ -2285,6 +2712,37 @@ void InterSearch::predInterSearch(CodingUnit& cu, Partitioner& partitioner)
   int          aaiMvpIdx[2][33];
   int          aaiMvpNum[2][33];
 
+#if GDR_ENABLED 
+  bool         cMvSolid[2];
+  bool         cMvValid[2];
+  bool         cMvBiSolid[2];
+  bool         cMvBiValid[2];
+
+  bool         cMvPredSolid[2][33];
+  bool         cMvPredBiSolid[2][33];
+
+  bool         cMvTempSolid[2][33];
+  bool         cMvTempValid[2][33];
+
+  bool         cMvHevcTempSolid[2][33];
+  bool         cMvHevcTempValid[2][33];
+
+  bool         allOk;
+  bool         bestBiPDistOk;
+  bool         biPDistTempOk;
+  bool         uiCostTempOk;
+  bool         uiCostTempL0Ok[MAX_NUM_REF];
+
+  bool         uiHevcCostOk;
+  bool         uiAffineCostOk;
+  bool         uiAffine6CostOk;
+  bool         uiCostOk[2];
+  bool         uiCostBiOk;
+  bool         costValidList1Ok;
+
+  bool         bCleanCandExist;
+#endif
+
   AMVPInfo     aacAMVPInfo[2][33];
 
   int          iRefIdx[2]={0,0}; //If un-initialized, may cause SEGV in bi-directional prediction iterative stage.
@@ -2328,6 +2786,60 @@ void InterSearch::predInterSearch(CodingUnit& cu, Partitioner& partitioner)
     checkNonAffine = m_affineMotion.hevcCost[1] < m_affineMotion.hevcCost[0] * 1.06f;
   }
 
+#if GDR_ENABLED  
+  const bool isEncodeClean = cs.pcv->isEncoder && ((cs.picHeader->getInGdrPeriod() && cs.isClean(pu.Y().topRight(), CHANNEL_TYPE_LUMA)) || (cs.picHeader->getNumVerVirtualBoundaries() == 0));
+  const bool init_value = true;
+#endif
+
+  amvp[0].numCand = 0;
+  amvp[1].numCand = 0;
+  memset(aacAMVPInfo, 0, sizeof(aacAMVPInfo));
+
+#if GDR_ENABLED  
+  if (isEncodeClean)
+  {
+    biPDistTempOk = init_value;
+    bestBiPDistOk = init_value;
+    uiCostTempOk = init_value;
+
+    uiHevcCostOk = init_value;
+    uiAffineCostOk = init_value;
+    uiAffine6CostOk = init_value;
+    memset(uiCostOk, init_value, sizeof(uiCostOk));
+    uiCostBiOk = init_value;
+    uiCostTempOk = init_value;
+    costValidList1Ok = init_value;
+
+    memset(cMvSolid, init_value, sizeof(cMvSolid));
+    memset(cMvValid, init_value, sizeof(cMvValid));
+    memset(cMvBiSolid, !init_value, sizeof(cMvBiSolid));
+    memset(cMvBiValid, !init_value, sizeof(cMvBiValid));
+
+    memset(cMvPredSolid, init_value, sizeof(cMvPredSolid));
+    memset(cMvPredBiSolid, init_value, sizeof(cMvPredBiSolid));
+
+    memset(cMvTempSolid, init_value, sizeof(cMvTempSolid));
+    memset(cMvTempValid, init_value, sizeof(cMvTempValid));
+    memset(cMvHevcTempSolid, init_value, sizeof(cMvHevcTempSolid));
+    memset(cMvHevcTempValid, init_value, sizeof(cMvHevcTempValid));
+
+
+    memset(pu.mvSolid, init_value, sizeof(pu.mvSolid));
+    memset(pu.mvValid, init_value, sizeof(pu.mvValid));
+
+    memset(pu.mvAffiSolid, init_value, sizeof(pu.mvAffiSolid));
+    memset(pu.mvAffiValid, init_value, sizeof(pu.mvAffiValid));
+
+    memset(pu.mvpSolid, init_value, sizeof(pu.mvpSolid));
+    memset(pu.mvpType, init_value, sizeof(pu.mvpType));
+
+    pu.mvpPos[0] = Position(0, 0);
+    pu.mvpPos[1] = Position(0, 0);
+
+    bCleanCandExist = false;
+  }
+#endif
+
   {
     if (pu.cu->cs->bestParent != nullptr && pu.cu->cs->bestParent->getCU(CHANNEL_TYPE_LUMA) != nullptr && pu.cu->cs->bestParent->getCU(CHANNEL_TYPE_LUMA)->affine == false)
     {
@@ -2353,6 +2865,15 @@ void InterSearch::predInterSearch(CodingUnit& cu, Partitioner& partitioner)
     Distortion   uiCostBi  =   std::numeric_limits<Distortion>::max();
     Distortion   uiCostTemp;
 
+#if GDR_ENABLED  
+    memset(uiCostTempL0Ok, init_value, sizeof(uiCostTempL0Ok));
+
+    bool mvValidList1Solid = init_value;
+    bool mvValidList1Valid = init_value;
+    uiHevcCostOk = false;
+    uiAffineCostOk = false;
+#endif
+
     uint32_t         uiBits[3];
     uint32_t         uiBitsTemp;
     Distortion   bestBiPDist = std::numeric_limits<Distortion>::max();
@@ -2397,12 +2918,48 @@ void InterSearch::predInterSearch(CodingUnit& cu, Partitioner& partitioner)
 
           aaiMvpIdx[iRefList][iRefIdxTemp] = pu.mvpIdx[eRefPicList];
           aaiMvpNum[iRefList][iRefIdxTemp] = pu.mvpNum[eRefPicList];
+#if GDR_ENABLED             
+          if (isEncodeClean)
+          {
+            biPDistTempOk = true;
+            biPDistTempOk = amvp[eRefPicList].mvSolid[aaiMvpIdx[iRefList][iRefIdxTemp]];
+            cMvPredSolid[iRefList][iRefIdxTemp] = biPDistTempOk;
+            cMvTempSolid[iRefList][iRefIdxTemp] = biPDistTempOk;
+            cMvTempValid[iRefList][iRefIdxTemp] = cs.isClean(pu.Y().bottomRight(), cMvTemp[iRefList][iRefIdxTemp], (RefPicList)iRefList, iRefIdxTemp);
+          }
+#endif
+
+#if GDR_ENABLED    
+          allOk = (cs.picHeader->getMvdL1ZeroFlag() && iRefList == 1 && biPDistTemp < bestBiPDist);
+
+          if (isEncodeClean)
+          {
+            if (biPDistTempOk)
+            {
+              allOk = (bestBiPDistOk) ? (cs.picHeader->getMvdL1ZeroFlag() && iRefList == 1 && biPDistTemp < bestBiPDist) : true;
+            }
+            else
+            {
+              allOk = false;
+            }
+          }
+#endif
 
+#if GDR_ENABLED  
+          if (allOk)
+#else
           if(cs.picHeader->getMvdL1ZeroFlag() && iRefList==1 && biPDistTemp < bestBiPDist)
+#endif
           {
             bestBiPDist = biPDistTemp;
             bestBiPMvpL1 = aaiMvpIdx[iRefList][iRefIdxTemp];
             bestBiPRefIdxL1 = iRefIdxTemp;
+#if GDR_ENABLED    
+            if (isEncodeClean)
+            {
+              bestBiPDistOk = biPDistTempOk;
+            }
+#endif
           }
 
           uiBitsTemp += m_auiMVPIdxCost[aaiMvpIdx[iRefList][iRefIdxTemp]][AMVP_MAX_NUM_CANDS];
@@ -2412,8 +2969,21 @@ void InterSearch::predInterSearch(CodingUnit& cu, Partitioner& partitioner)
             if ( cs.slice->getList1IdxToList0Idx( iRefIdxTemp ) >= 0 )
             {
               cMvTemp[1][iRefIdxTemp] = cMvTemp[0][cs.slice->getList1IdxToList0Idx( iRefIdxTemp )];
+#if GDR_ENABLED            
+              if (isEncodeClean)
+              {
+                cMvTempSolid[1][iRefIdxTemp] = cMvTempSolid[1][cs.slice->getList1IdxToList0Idx(iRefIdxTemp)];
+                cMvTempValid[1][iRefIdxTemp] = cs.isClean(pu.Y().bottomRight(), cMvTemp[1][iRefIdxTemp], (RefPicList)1, cs.slice->getList1IdxToList0Idx(iRefIdxTemp));
+              }
+#endif
               uiCostTemp = uiCostTempL0[cs.slice->getList1IdxToList0Idx( iRefIdxTemp )];
               /*first subtract the bit-rate part of the cost of the other list*/
+#if GDR_ENABLED            
+              if (isEncodeClean)
+              {
+                uiCostTempOk = uiCostTempL0Ok[cs.slice->getList1IdxToList0Idx(iRefIdxTemp)];
+              }
+#endif
               uiCostTemp -= m_pcRdCost->getCost( uiBitsTempL0[cs.slice->getList1IdxToList0Idx( iRefIdxTemp )] );
               /*correct the bit-rate part of the current ref*/
               m_pcRdCost->setPredictor  ( cMvPred[iRefList][iRefIdxTemp] );
@@ -2423,28 +2993,131 @@ void InterSearch::predInterSearch(CodingUnit& cu, Partitioner& partitioner)
             }
             else
             {
+#if GDR_ENABLED              
+              bCleanCandExist = false;
+              xMotionEstimation(pu, origBuf, eRefPicList, cMvPred[iRefList][iRefIdxTemp], iRefIdxTemp, cMvTemp[iRefList][iRefIdxTemp], cMvTempSolid[iRefList][iRefIdxTemp], aaiMvpIdx[iRefList][iRefIdxTemp], uiBitsTemp, uiCostTemp, amvp[eRefPicList], bCleanCandExist);
+#else
               xMotionEstimation( pu, origBuf, eRefPicList, cMvPred[iRefList][iRefIdxTemp], iRefIdxTemp, cMvTemp[iRefList][iRefIdxTemp], aaiMvpIdx[iRefList][iRefIdxTemp], uiBitsTemp, uiCostTemp, amvp[eRefPicList] );
+#endif
+
+#if GDR_ENABLED    
+              if (isEncodeClean)
+              {
+                int mvp_idx = aaiMvpIdx[iRefList][iRefIdxTemp];
+                cMvPredSolid[iRefList][iRefIdxTemp] = amvp[eRefPicList].mvSolid[mvp_idx];
+                cMvTempSolid[iRefList][iRefIdxTemp] = amvp[eRefPicList].mvSolid[mvp_idx];
+                cMvTempValid[iRefList][iRefIdxTemp] = cs.isClean(pu.Y().bottomRight(), cMvTemp[iRefList][iRefIdxTemp], (RefPicList)iRefList, iRefIdxTemp);
+
+                if (cMvTempValid[iRefList][iRefIdxTemp])
+                {
+                  cMvTempValid[iRefList][iRefIdxTemp] = cMvTempSolid[iRefList][iRefIdxTemp];
+                }
+
+                uiCostTempOk = bCleanCandExist;
+                uiCostTempOk = uiCostTempOk && cMvPredSolid[iRefList][iRefIdxTemp];
+                uiCostTempOk = uiCostTempOk && cMvTempSolid[iRefList][iRefIdxTemp];
+                uiCostTempOk = uiCostTempOk && cMvTempValid[iRefList][iRefIdxTemp];
+              }
+#endif
             }
           }
           else
           {
+#if GDR_ENABLED             
+            bCleanCandExist = false;
+            xMotionEstimation(pu, origBuf, eRefPicList, cMvPred[iRefList][iRefIdxTemp], iRefIdxTemp, cMvTemp[iRefList][iRefIdxTemp], cMvTempSolid[iRefList][iRefIdxTemp], aaiMvpIdx[iRefList][iRefIdxTemp], uiBitsTemp, uiCostTemp, amvp[eRefPicList], bCleanCandExist);
+#else
             xMotionEstimation( pu, origBuf, eRefPicList, cMvPred[iRefList][iRefIdxTemp], iRefIdxTemp, cMvTemp[iRefList][iRefIdxTemp], aaiMvpIdx[iRefList][iRefIdxTemp], uiBitsTemp, uiCostTemp, amvp[eRefPicList] );
-          }
-          if( cu.cs->sps->getUseBcw() && cu.BcwIdx == BCW_DEFAULT && cu.cs->slice->isInterB() )
-          {
+#endif           
+
+#if GDR_ENABLED    
+            if (isEncodeClean)
+            {
+              int mvp_idx = aaiMvpIdx[iRefList][iRefIdxTemp];
+              cMvPredSolid[iRefList][iRefIdxTemp] = amvp[eRefPicList].mvSolid[mvp_idx];
+              cMvTempSolid[iRefList][iRefIdxTemp] = amvp[eRefPicList].mvSolid[mvp_idx];
+              cMvTempValid[iRefList][iRefIdxTemp] = cs.isClean(pu.Y().bottomRight(), cMvTemp[iRefList][iRefIdxTemp], (RefPicList)iRefList, iRefIdxTemp);
+              if (cMvTempValid[iRefList][iRefIdxTemp])
+              {
+                cMvTempValid[iRefList][iRefIdxTemp] = cMvTempSolid[iRefList][iRefIdxTemp];
+              }
+
+              uiCostTempOk = bCleanCandExist;
+              uiCostTempOk = uiCostTempOk && cMvPredSolid[iRefList][iRefIdxTemp];
+              uiCostTempOk = uiCostTempOk && cMvTempSolid[iRefList][iRefIdxTemp];
+              uiCostTempOk = uiCostTempOk && cMvTempValid[iRefList][iRefIdxTemp];
+            }
+#endif
+          }
+          if( cu.cs->sps->getUseBcw() && cu.BcwIdx == BCW_DEFAULT && cu.cs->slice->isInterB() )
+          {
             const bool checkIdentical = true;
             m_uniMotions.setReadMode(checkIdentical, (uint32_t)iRefList, (uint32_t)iRefIdxTemp);
+#if GDR_ENABLED
+            m_uniMotions.copyFrom(cMvTemp[iRefList][iRefIdxTemp], cMvTempSolid[iRefList][iRefIdxTemp], uiCostTemp - m_pcRdCost->getCost(uiBitsTemp), (uint32_t)iRefList, (uint32_t)iRefIdxTemp);
+#else
             m_uniMotions.copyFrom(cMvTemp[iRefList][iRefIdxTemp], uiCostTemp - m_pcRdCost->getCost(uiBitsTemp), (uint32_t)iRefList, (uint32_t)iRefIdxTemp);
+#endif
           }
           xCopyAMVPInfo( &amvp[eRefPicList], &aacAMVPInfo[iRefList][iRefIdxTemp]); // must always be done ( also when AMVP_MODE = AM_NONE )
-          xCheckBestMVP( eRefPicList, cMvTemp[iRefList][iRefIdxTemp], cMvPred[iRefList][iRefIdxTemp], aaiMvpIdx[iRefList][iRefIdxTemp], amvp[eRefPicList], uiBitsTemp, uiCostTemp, pu.cu->imv );
+#if GDR_ENABLED
+          xCheckBestMVP(pu, eRefPicList, cMvTemp[iRefList][iRefIdxTemp], cMvPred[iRefList][iRefIdxTemp], aaiMvpIdx[iRefList][iRefIdxTemp], amvp[eRefPicList], uiBitsTemp, uiCostTemp, pu.cu->imv);
+
+          if (isEncodeClean)
+          {
+            int mvp_idx = aaiMvpIdx[iRefList][iRefIdxTemp];
+
+            cMvPredSolid[iRefList][iRefIdxTemp] = amvp[eRefPicList].mvSolid[mvp_idx];
+            cMvTempSolid[iRefList][iRefIdxTemp] = amvp[eRefPicList].mvSolid[mvp_idx];
+            cMvTempValid[iRefList][iRefIdxTemp] = cs.isClean(pu.Y().bottomRight(), cMvTemp[iRefList][iRefIdxTemp], (RefPicList)iRefList, iRefIdxTemp);
+            if (cMvTempValid[iRefList][iRefIdxTemp])
+            {
+              cMvTempValid[iRefList][iRefIdxTemp] = cMvTempSolid[iRefList][iRefIdxTemp];
+            }
 
+            uiCostTempOk = true;
+            uiCostTempOk = uiCostTempOk && cMvPredSolid[iRefList][iRefIdxTemp];
+            uiCostTempOk = uiCostTempOk && cMvTempSolid[iRefList][iRefIdxTemp];
+            uiCostTempOk = uiCostTempOk && cMvTempValid[iRefList][iRefIdxTemp];
+          }
+#else
+          xCheckBestMVP( eRefPicList, cMvTemp[iRefList][iRefIdxTemp], cMvPred[iRefList][iRefIdxTemp], aaiMvpIdx[iRefList][iRefIdxTemp], amvp[eRefPicList], uiBitsTemp, uiCostTemp, pu.cu->imv );
+#endif
           if ( iRefList == 0 )
           {
             uiCostTempL0[iRefIdxTemp] = uiCostTemp;
             uiBitsTempL0[iRefIdxTemp] = uiBitsTemp;
           }
+#if GDR_ENABLED          
+          if (isEncodeClean)
+          {
+            uiCostTempL0Ok[iRefIdxTemp] = uiCostTempOk;
+          }
+#endif
+
+#if GDR_ENABLED    
+          allOk = (uiCostTemp < uiCost[iRefList]);
+          if (isEncodeClean)
+          {
+            if (uiCostTempOk)
+            {
+              allOk = (uiCostOk[iRefList]) ? (uiCostTemp < uiCost[iRefList]) : true;
+            }
+            else
+            {
+              allOk = false;
+            }
+
+            allOk = allOk && bCleanCandExist;
+          }
+#endif
+
+
+#if GDR_ENABLED
+          if (allOk)
+#else
           if ( uiCostTemp < uiCost[iRefList] )
+#endif
           {
             uiCost[iRefList] = uiCostTemp;
             uiBits[iRefList] = uiBitsTemp; // storing for bi-prediction
@@ -2452,21 +3125,65 @@ void InterSearch::predInterSearch(CodingUnit& cu, Partitioner& partitioner)
             // set motion
             cMv    [iRefList] = cMvTemp[iRefList][iRefIdxTemp];
             iRefIdx[iRefList] = iRefIdxTemp;
+
+#if GDR_ENABLED            
+            if (isEncodeClean)
+            {
+              uiCostOk[iRefList] = uiCostTempOk;
+              cMvSolid[iRefList] = cMvTempSolid[iRefList][iRefIdxTemp];
+              cMvValid[iRefList] = cs.isClean(pu.Y().bottomRight(), cMv[iRefList], (RefPicList)iRefList, iRefIdx[iRefList]);
+            }
+#endif
+          }
+
+
+#if GDR_ENABLED    
+          allOk = (iRefList == 1 && uiCostTemp < costValidList1 && cs.slice->getList1IdxToList0Idx(iRefIdxTemp) < 0);
+          if (isEncodeClean)
+          {
+            if (uiCostTempOk)
+            {
+              allOk = (costValidList1Ok) ? (iRefList == 1 && uiCostTemp < costValidList1 && cs.slice->getList1IdxToList0Idx(iRefIdxTemp) < 0) : true;
+            }
+            else
+            {
+              allOk = false;
+            }
           }
+#endif
 
+#if GDR_ENABLED   
+          if (allOk)
+#else
           if ( iRefList == 1 && uiCostTemp < costValidList1 && cs.slice->getList1IdxToList0Idx( iRefIdxTemp ) < 0 )
+#endif
           {
             costValidList1 = uiCostTemp;
             bitsValidList1 = uiBitsTemp;
 
             // set motion
             mvValidList1     = cMvTemp[iRefList][iRefIdxTemp];
+#if GDR_ENABLED            
+            if (isEncodeClean)
+            {
+              costValidList1Ok = uiCostTempOk;
+              mvValidList1Solid = cMvTempSolid[iRefList][iRefIdxTemp];
+              mvValidList1Valid = cs.isClean(pu.Y().bottomRight(), mvValidList1, (RefPicList)iRefList, iRefIdxTemp);
+            }
+#endif
             refIdxValidList1 = iRefIdxTemp;
           }
         }
       }
 
       ::memcpy(cMvHevcTemp, cMvTemp, sizeof(cMvTemp));
+#if GDR_ENABLED
+      if (isEncodeClean)
+      {
+        ::memcpy(cMvHevcTempSolid, cMvTempSolid, sizeof(cMvTempSolid));
+        ::memcpy(cMvHevcTempValid, cMvTempValid, sizeof(cMvTempValid));
+      }
+#endif
       if (cu.imv == 0 && (!cu.slice->getSPS()->getUseBcw() || bcwIdx == BCW_DEFAULT))
       {
         insertUniMvCands(pu.Y(), cMvTemp);
@@ -2488,8 +3205,23 @@ void InterSearch::predInterSearch(CodingUnit& cu, Partitioner& partitioner)
         iRefIdxBi[0] = iRefIdx[0];
         iRefIdxBi[1] = iRefIdx[1];
 
+#if GDR_ENABLED
+        if (isEncodeClean)
+        {
+          cMvBiSolid[0] = cMvSolid[0];
+          cMvBiSolid[1] = cMvSolid[1];
+          cMvBiValid[0] = cMvValid[0];
+          cMvBiValid[1] = cMvValid[1];
+        }
+#endif
         ::memcpy( cMvPredBi,   cMvPred,   sizeof( cMvPred   ) );
         ::memcpy( aaiMvpIdxBi, aaiMvpIdx, sizeof( aaiMvpIdx ) );
+#if GDR_ENABLED
+        if (isEncodeClean)
+        {
+          ::memcpy(cMvPredBiSolid, cMvPredSolid, sizeof(cMvPredSolid));
+        }
+#endif
 
         uint32_t uiMotBits[2];
 
@@ -2500,10 +3232,25 @@ void InterSearch::predInterSearch(CodingUnit& cu, Partitioner& partitioner)
           cMvPredBi  [1][bestBiPRefIdxL1] = amvp[REF_PIC_LIST_1].mvCand[bestBiPMvpL1];
 
           cMvBi    [1] = cMvPredBi[1][bestBiPRefIdxL1];
+#if GDR_ENABLED
+          if (isEncodeClean)
+          {
+            cMvPredBiSolid[1][bestBiPRefIdxL1] = amvp[REF_PIC_LIST_1].mvSolid[bestBiPMvpL1];
+            cMvBiSolid[1] = cMvPredBiSolid[1][bestBiPRefIdxL1];
+            cMvBiValid[1] = cs.isClean(pu.Y().bottomRight(), cMvBi[1], REF_PIC_LIST_1, bestBiPRefIdxL1);
+          }
+#endif
           iRefIdxBi[1] = bestBiPRefIdxL1;
           pu.mv    [REF_PIC_LIST_1] = cMvBi[1];
           pu.refIdx[REF_PIC_LIST_1] = iRefIdxBi[1];
           pu.mvpIdx[REF_PIC_LIST_1] = bestBiPMvpL1;
+#if GDR_ENABLED
+          if (isEncodeClean)
+          {
+            pu.mvSolid[REF_PIC_LIST_1] = cMvBiSolid[1];
+            pu.mvValid[REF_PIC_LIST_1] = cs.isClean(pu.Y().bottomRight(), pu.mv[REF_PIC_LIST_1], REF_PIC_LIST_1, pu.refIdx[REF_PIC_LIST_1]);
+          }
+#endif
 
           if( m_pcEncCfg->getMCTSEncConstraint() )
           {
@@ -2538,6 +3285,13 @@ void InterSearch::predInterSearch(CodingUnit& cu, Partitioner& partitioner)
           uiBits[2] = uiMbBits[2] + uiMotBits[0] + uiMotBits[1];
 
           cMvTemp[1][bestBiPRefIdxL1] = cMvBi[1];
+#if GDR_ENABLED
+          if (isEncodeClean)
+          {
+            cMvTempSolid[1][bestBiPRefIdxL1] = cMvBiSolid[1];
+            cMvTempValid[1][bestBiPRefIdxL1] = cs.isClean(pu.Y().bottomRight(), cMvBi[1], REF_PIC_LIST_1, bestBiPRefIdxL1);
+          }
+#endif
         }
         else
         {
@@ -2566,7 +3320,26 @@ void InterSearch::predInterSearch(CodingUnit& cu, Partitioner& partitioner)
             if (m_pcEncCfg->getFastInterSearchMode() == FASTINTERSEARCH_MODE1
                 || m_pcEncCfg->getFastInterSearchMode() == FASTINTERSEARCH_MODE2)
             {
+#if GDR_ENABLED    
+              allOk = (uiCost[0] <= uiCost[1]);
+              if (isEncodeClean)
+              {
+                if (uiCostOk[0])
+                {
+                  allOk = (uiCostOk[1]) ? (uiCost[0] <= uiCost[1]) : true;
+                }
+                else
+                {
+                  allOk = false;
+                }
+              }
+#endif
+
+#if GDR_ENABLED  
+              if (allOk)
+#else
               if (uiCost[0] <= uiCost[1])
+#endif
               {
                 iRefList = 1;
               }
@@ -2588,7 +3361,13 @@ void InterSearch::predInterSearch(CodingUnit& cu, Partitioner& partitioner)
             {
               pu.mv[1 - iRefList]     = cMv[1 - iRefList];
               pu.refIdx[1 - iRefList] = iRefIdx[1 - iRefList];
-
+#if GDR_ENABLED
+              if (isEncodeClean)
+              {
+                pu.mvSolid[1 - iRefList] = cMvSolid[1 - iRefList];
+                pu.mvValid[1 - iRefList] = cs.isClean(pu.Y().bottomRight(), pu.mv[1 - iRefList], (RefPicList)(1 - iRefList), pu.refIdx[1 - iRefList]);
+              }
+#endif              
               PelUnitBuf predBufTmp = m_tmpPredStorage[1 - iRefList].getBuf(UnitAreaRelative(cu, pu));
               motionCompensation(pu, predBufTmp, RefPicList(1 - iRefList));
             }
@@ -2631,19 +3410,99 @@ void InterSearch::predInterSearch(CodingUnit& cu, Partitioner& partitioner)
               }
               // call ME
               xCopyAMVPInfo(&aacAMVPInfo[iRefList][iRefIdxTemp], &amvp[eRefPicList]);
+#if GDR_ENABLED
+              bCleanCandExist = false;
+              xMotionEstimation(pu, origBuf, eRefPicList, cMvPredBi[iRefList][iRefIdxTemp], iRefIdxTemp, cMvTemp[iRefList][iRefIdxTemp], cMvTempSolid[iRefList][iRefIdxTemp], aaiMvpIdxBi[iRefList][iRefIdxTemp], uiBitsTemp, uiCostTemp, amvp[eRefPicList], bCleanCandExist, true);
+#else
               xMotionEstimation(pu, origBuf, eRefPicList, cMvPredBi[iRefList][iRefIdxTemp], iRefIdxTemp,
                                 cMvTemp[iRefList][iRefIdxTemp], aaiMvpIdxBi[iRefList][iRefIdxTemp], uiBitsTemp,
                                 uiCostTemp, amvp[eRefPicList], true);
+#endif
+#if GDR_ENABLED    
+              if (isEncodeClean)
+              {
+                int mvp_idx = aaiMvpIdxBi[iRefList][iRefIdxTemp];
+                cMvPredBiSolid[iRefList][iRefIdxTemp] = amvp[eRefPicList].mvSolid[mvp_idx];
+                cMvTempSolid[iRefList][iRefIdxTemp] = amvp[eRefPicList].mvSolid[mvp_idx];
+                cMvTempValid[iRefList][iRefIdxTemp] = cs.isClean(pu.Y().bottomRight(), cMvTemp[iRefList][iRefIdxTemp], (RefPicList)iRefList, iRefIdxTemp);
+                if (cMvTempValid[iRefList][iRefIdxTemp])
+                {
+                  cMvTempValid[iRefList][iRefIdxTemp] = cMvTempSolid[iRefList][iRefIdxTemp];
+                }
+
+                uiCostTempOk = bCleanCandExist;
+                uiCostTempOk = uiCostTempOk && cMvPredBiSolid[iRefList][iRefIdxTemp];
+                uiCostTempOk = uiCostTempOk && cMvTempSolid[iRefList][iRefIdxTemp];
+                uiCostTempOk = uiCostTempOk && cMvTempValid[iRefList][iRefIdxTemp];
+              }
+#endif
+
+#if GDR_ENABLED
+              // note : uiCostTemp is the new Best MVP cost, 
+              //        solid info will be at amvp[eRefPicList].mvSolid[aaiMvpIdx[iRefList][iRefIdxTemp]];
+              xCheckBestMVP(pu, eRefPicList, cMvTemp[iRefList][iRefIdxTemp], cMvPredBi[iRefList][iRefIdxTemp], aaiMvpIdxBi[iRefList][iRefIdxTemp], amvp[eRefPicList], uiBitsTemp, uiCostTemp, pu.cu->imv);
+
+              if (isEncodeClean)
+              {
+                int mvp_idx = aaiMvpIdxBi[iRefList][iRefIdxTemp];
+
+                cMvPredBiSolid[iRefList][iRefIdxTemp] = amvp[eRefPicList].mvSolid[mvp_idx];
+                cMvTempSolid[iRefList][iRefIdxTemp] = amvp[eRefPicList].mvSolid[mvp_idx];
+                cMvTempValid[iRefList][iRefIdxTemp] = cs.isClean(pu.Y().bottomRight(), cMvTemp[iRefList][iRefIdxTemp], (RefPicList)iRefList, iRefIdxTemp);
+                if (cMvTempValid[iRefList][iRefIdxTemp])
+                {
+                  cMvTempValid[iRefList][iRefIdxTemp] = cMvTempSolid[iRefList][iRefIdxTemp];
+                }
+
+                uiCostTempOk = true;
+                uiCostTempOk = uiCostTempOk && cMvPredBiSolid[iRefList][iRefIdxTemp];
+                uiCostTempOk = uiCostTempOk && cMvTempSolid[iRefList][iRefIdxTemp];
+                uiCostTempOk = uiCostTempOk && cMvTempValid[iRefList][iRefIdxTemp];
+              }
+#else
+
               xCheckBestMVP(eRefPicList, cMvTemp[iRefList][iRefIdxTemp], cMvPredBi[iRefList][iRefIdxTemp],
                             aaiMvpIdxBi[iRefList][iRefIdxTemp], amvp[eRefPicList], uiBitsTemp, uiCostTemp, pu.cu->imv);
+#endif
+#if GDR_ENABLED    
+              allOk = (uiCostTemp < uiCostBi);
+              if (isEncodeClean)
+              {
+                if (uiCostTempOk)
+                {
+                  allOk = (uiCostBiOk) ? (uiCostTemp < uiCostBi) : true;
+                }
+                else
+                {
+                  allOk = false;
+                }
+              }
+#endif
+#if GDR_ENABLED  
+              if (allOk)
+#else
               if (uiCostTemp < uiCostBi)
+#endif
               {
                 bChanged = true;
 
                 cMvBi[iRefList]     = cMvTemp[iRefList][iRefIdxTemp];
+#if GDR_ENABLED                
+                if (isEncodeClean)
+                {
+                  cMvBiSolid[iRefList] = cMvTempSolid[iRefList][iRefIdxTemp];
+                  cMvBiValid[iRefList] = cs.isClean(pu.Y().bottomRight(), cMvTemp[iRefList][iRefIdxTemp], (RefPicList)iRefList, iRefIdxTemp);
+                }
+#endif
                 iRefIdxBi[iRefList] = iRefIdxTemp;
 
                 uiCostBi            = uiCostTemp;
+#if GDR_ENABLED 
+                if (isEncodeClean)
+                {
+                  uiCostBiOk = uiCostTempOk;
+                }
+#endif
                 uiMotBits[iRefList] = uiBitsTemp - uiMbBits[2] - uiMotBits[1 - iRefList];
                 uiMotBits[iRefList] -= ((cs.slice->getSPS()->getUseBcw() == true) ? getWeightIdxBits(bcwIdx) : 0);
                 uiBits[2] = uiBitsTemp;
@@ -2653,7 +3512,13 @@ void InterSearch::predInterSearch(CodingUnit& cu, Partitioner& partitioner)
                   //  Set motion
                   pu.mv[eRefPicList]     = cMvBi[iRefList];
                   pu.refIdx[eRefPicList] = iRefIdxBi[iRefList];
-
+#if GDR_ENABLED                
+                  if (isEncodeClean)
+                  {
+                    pu.mvSolid[eRefPicList] = cMvBiSolid[iRefList];
+                    pu.mvValid[eRefPicList] = cs.isClean(pu.Y().bottomRight(), pu.mv[eRefPicList], (RefPicList)eRefPicList, pu.refIdx[eRefPicList]);
+                  }
+#endif
                   PelUnitBuf predBufTmp = m_tmpPredStorage[iRefList].getBuf(UnitAreaRelative(cu, pu));
                   motionCompensation(pu, predBufTmp, eRefPicList);
                 }
@@ -2662,16 +3527,83 @@ void InterSearch::predInterSearch(CodingUnit& cu, Partitioner& partitioner)
 
             if (!bChanged)
             {
+#if GDR_ENABLED    
+              allOk = ((uiCostBi <= uiCost[0] && uiCostBi <= uiCost[1]) || enforceBcwPred);
+
+              if (isEncodeClean)
+              {
+                if (uiCostBiOk)
+                {
+                  allOk = (uiCostOk[0] && uiCostOk[1]) ? ((uiCostBi <= uiCost[0] && uiCostBi <= uiCost[1]) || enforceBcwPred) : true;
+                }
+                else
+                {
+                  allOk = false;
+                }
+              }
+#endif
+#if GDR_ENABLED 
+              if (allOk)
+#else
               if ((uiCostBi <= uiCost[0] && uiCostBi <= uiCost[1]) || enforceBcwPred)
+#endif
               {
                 xCopyAMVPInfo(&aacAMVPInfo[0][iRefIdxBi[0]], &amvp[REF_PIC_LIST_0]);
+#if GDR_ENABLED
+                // note : uiCostBi is the new Best MVP cost, 
+                //          solid info will be at amvp[eRefPicList].mvSolid[aaiMvpIdx[iRefList][iRefIdxTemp]];
+                xCheckBestMVP(pu, REF_PIC_LIST_0, cMvBi[0], cMvPredBi[0][iRefIdxBi[0]], aaiMvpIdxBi[0][iRefIdxBi[0]], amvp[REF_PIC_LIST_0], uiBits[2], uiCostBi, pu.cu->imv);
+
+                if (isEncodeClean)
+                {
+                  int mvp_idx = aaiMvpIdxBi[0][iRefIdxBi[0]];
+
+                  cMvPredBiSolid[0][iRefIdxBi[0]] = amvp[0].mvSolid[mvp_idx];
+                  cMvBiSolid[0] = amvp[0].mvSolid[mvp_idx];
+                  cMvBiValid[0] = cs.isClean(pu.Y().bottomRight(), cMvBi[0], (RefPicList)0, iRefIdxBi[0]);
+                  if (cMvBiValid[0])
+                  {
+                    cMvBiValid[0] = cMvBiSolid[0];
+                  }
+
+                  uiCostBiOk = true;
+                  uiCostBiOk = uiCostBiOk && cMvPredBiSolid[0][iRefIdxBi[0]];
+                  uiCostBiOk = uiCostBiOk && cMvBiSolid[0];
+                  uiCostBiOk = uiCostBiOk && cMvBiValid[0];
+                }
+#else
                 xCheckBestMVP(REF_PIC_LIST_0, cMvBi[0], cMvPredBi[0][iRefIdxBi[0]], aaiMvpIdxBi[0][iRefIdxBi[0]],
                               amvp[REF_PIC_LIST_0], uiBits[2], uiCostBi, pu.cu->imv);
+#endif   
                 if (!cs.picHeader->getMvdL1ZeroFlag())
                 {
                   xCopyAMVPInfo(&aacAMVPInfo[1][iRefIdxBi[1]], &amvp[REF_PIC_LIST_1]);
+#if GDR_ENABLED
+                  // note : uiCostBi is the new Best MVP cost, 
+                  //          solid info will be at amvp[eRefPicList].mvSolid[aaiMvpIdx[iRefList][iRefIdxTemp]];
+                  xCheckBestMVP(pu, REF_PIC_LIST_1, cMvBi[1], cMvPredBi[1][iRefIdxBi[1]], aaiMvpIdxBi[1][iRefIdxBi[1]], amvp[REF_PIC_LIST_1], uiBits[2], uiCostBi, pu.cu->imv);
+
+                  if (isEncodeClean)
+                  {
+                    int mvp_idx = aaiMvpIdxBi[1][iRefIdxBi[1]];
+
+                    cMvPredBiSolid[1][iRefIdxBi[1]] = aaiMvpIdxBi[1][iRefIdxBi[1]];
+                    cMvBiSolid[1] = amvp[REF_PIC_LIST_1].mvSolid[mvp_idx];
+                    cMvBiValid[1] = cs.isClean(pu.Y().bottomRight(), cMvBi[1], (RefPicList)1, iRefIdxBi[1]);
+                    if (cMvBiValid[1])
+                    {
+                      cMvBiValid[1] = cMvBiSolid[1];
+                    }
+
+                    uiCostBiOk = true;
+                    uiCostBiOk = uiCostBiOk && cMvPredBiSolid[1][iRefIdxBi[1]];
+                    uiCostBiOk = uiCostBiOk && cMvBiSolid[1];
+                    uiCostBiOk = uiCostBiOk && cMvBiValid[1];
+                  }
+#else
                   xCheckBestMVP(REF_PIC_LIST_1, cMvBi[1], cMvPredBi[1][iRefIdxBi[1]], aaiMvpIdxBi[1][iRefIdxBi[1]],
                                 amvp[REF_PIC_LIST_1], uiBits[2], uiCostBi, pu.cu->imv);
+#endif
                 }
               }
               break;
@@ -2705,6 +3637,21 @@ void InterSearch::predInterSearch(CodingUnit& cu, Partitioner& partitioner)
 
           MvField cCurMvField, cTarMvField;
           Distortion costStart = std::numeric_limits<Distortion>::max();
+
+#if GDR_ENABLED
+          bool cMvPredSymSolid[2] = { init_value, init_value };
+          bool cMvPredSymValid[2] = { init_value, init_value };
+
+          bool cCurMvFieldSolid = init_value;
+          bool cTarMvFieldSolid = init_value;
+          bool cCurMvFieldValid = init_value;
+          bool cTarMvFieldValid = init_value;
+
+          bool costStartOk = false;
+          bool symCostOk = init_value;
+          bool costOk = init_value;
+          bool bestCostOk = init_value;
+#endif
           for ( int i = 0; i < aacAMVPInfo[curRefList][refIdxCur].numCand; i++ )
           {
             for ( int j = 0; j < aacAMVPInfo[tarRefList][refIdxTar].numCand; j++ )
@@ -2712,11 +3659,46 @@ void InterSearch::predInterSearch(CodingUnit& cu, Partitioner& partitioner)
               cCurMvField.setMvField( aacAMVPInfo[curRefList][refIdxCur].mvCand[i], refIdxCur );
               cTarMvField.setMvField( aacAMVPInfo[tarRefList][refIdxTar].mvCand[j], refIdxTar );
               Distortion cost = xGetSymmetricCost( pu, origBuf, eCurRefList, cCurMvField, cTarMvField, bcwIdx );
+
+#if GDR_ENABLED              
+              if (isEncodeClean)
+              {
+                cCurMvFieldSolid = aacAMVPInfo[curRefList][refIdxCur].mvSolid[i];
+                cTarMvFieldSolid = aacAMVPInfo[tarRefList][refIdxTar].mvSolid[i];
+                costOk = cCurMvFieldSolid && cTarMvFieldSolid;
+              }
+#endif
+#if GDR_ENABLED
+              allOk = (cost < costStart);
+              if (isEncodeClean)
+              {
+                if (costOk)
+                {
+                  allOk = (costStartOk) ? (cost < costStart) : true;
+                }
+                else
+                {
+                  allOk = false;
+                }
+              }
+#endif
+#if GDR_ENABLED
+              if (allOk)
+#else
               if ( cost < costStart )
+#endif
               {
                 costStart = cost;
                 cMvPredSym[curRefList] = aacAMVPInfo[curRefList][refIdxCur].mvCand[i];
                 cMvPredSym[tarRefList] = aacAMVPInfo[tarRefList][refIdxTar].mvCand[j];
+#if GDR_ENABLED
+                if (isEncodeClean)
+                {
+                  costStartOk = costOk;
+                  cMvPredSymSolid[curRefList] = aacAMVPInfo[curRefList][refIdxCur].mvSolid[i];
+                  cMvPredSymSolid[tarRefList] = aacAMVPInfo[tarRefList][refIdxTar].mvSolid[j];
+                }
+#endif
                 mvpIdxSym[curRefList] = i;
                 mvpIdxSym[tarRefList] = j;
               }
@@ -2725,6 +3707,13 @@ void InterSearch::predInterSearch(CodingUnit& cu, Partitioner& partitioner)
           cCurMvField.mv = cMvPredSym[curRefList];
           cTarMvField.mv = cMvPredSym[tarRefList];
 
+#if GDR_ENABLED
+          if (isEncodeClean)
+          {
+            cCurMvFieldSolid = cMvPredSymSolid[curRefList];
+            cTarMvFieldSolid = cMvPredSymSolid[tarRefList];
+          }
+#endif
           m_pcRdCost->setCostScale(0);
           Mv pred = cMvPredSym[curRefList];
           pred.changeTransPrecInternal2Amvr(pu.cu->imv);
@@ -2789,11 +3778,63 @@ void InterSearch::predInterSearch(CodingUnit& cu, Partitioner& partitioner)
             }
 
             Distortion bestCost = costStart;
+#if GDR_ENABLED
+            symmvdCheckBestMvp(pu, origBuf, mvStart, (RefPicList)curRefList, aacAMVPInfo, bcwIdx, cMvPredSym, cMvPredSymSolid, mvpIdxSym, costStart);
+#else
             symmvdCheckBestMvp(pu, origBuf, mvStart, (RefPicList)curRefList, aacAMVPInfo, bcwIdx, cMvPredSym, mvpIdxSym, costStart);
+#endif
+
+#if GDR_ENABLED
+            if (isEncodeClean)
+            {
+              int mvp_idx0 = mvpIdxSym[0];
+              int mvp_idx1 = mvpIdxSym[1];
+
+              cMvPredSymSolid[curRefList] = aacAMVPInfo[curRefList][refIdxCur].mvSolid[mvp_idx0];
+              cMvPredSymSolid[tarRefList] = aacAMVPInfo[tarRefList][refIdxTar].mvSolid[mvp_idx1];
+              cMvPredSymValid[curRefList] = cs.isClean(pu.Y().bottomRight(), mvStart, (RefPicList)curRefList, pu.cu->slice->getSymRefIdx(curRefList));
+              cMvPredSymValid[tarRefList] = cs.isClean(pu.Y().bottomRight(), mvStart, (RefPicList)tarRefList, pu.cu->slice->getSymRefIdx(tarRefList));
+
+              costStartOk = true;
+              costStartOk = costStartOk && cMvPredSymSolid[curRefList];
+              costStartOk = costStartOk && cMvPredSymSolid[tarRefList];
+              costStartOk = costStartOk && cMvPredSymValid[curRefList];
+              costStartOk = costStartOk && cMvPredSymValid[tarRefList];
+            }
+#endif
+
+#if GDR_ENABLED
+            bool allOk = (costStart < bestCost);
+            if (isEncodeClean)
+            {
+              if (costStartOk)
+              {
+                allOk = (bestCostOk) ? (costStart < bestCost) : true;
+              }
+              else
+              {
+                allOk = false;
+              }
+            }
+#endif
+
+#if GDR_ENABLED
+            if (allOk)
+#else
             if (costStart < bestCost)
+#endif
             {
               cCurMvField.setMvField(mvStart, refIdxCur);
               cTarMvField.setMvField(mvStart.getSymmvdMv(cMvPredSym[curRefList], cMvPredSym[tarRefList]), refIdxTar);
+#if GDR_ENABLED
+              if (isEncodeClean)
+              {
+                cCurMvFieldSolid = cMvPredSymSolid[curRefList];
+                cTarMvFieldSolid = cMvPredSymSolid[tarRefList];
+                cCurMvFieldValid = cMvPredSymValid[curRefList];
+                cTarMvFieldValid = cMvPredSymValid[tarRefList];
+              }
+#endif
             }
           }
           Mv startPtMv = cCurMvField.mv;
@@ -2802,13 +3843,47 @@ void InterSearch::predInterSearch(CodingUnit& cu, Partitioner& partitioner)
           symCost = costStart - mvpCost;
 
           // ME
+#if GDR_ENABLED      
+          xSymmetricMotionEstimation(pu, origBuf, cMvPredSym[curRefList], cMvPredSym[tarRefList], eCurRefList, cCurMvField, cTarMvField, symCost, bcwIdx, costStartOk);
+#else
           xSymmetricMotionEstimation( pu, origBuf, cMvPredSym[curRefList], cMvPredSym[tarRefList], eCurRefList, cCurMvField, cTarMvField, symCost, bcwIdx );
-
+#endif
           symCost += mvpCost;
 
+#if GDR_ENABLED               
+          if (isEncodeClean)
+          {
+            cCurMvFieldValid = cs.isClean(pu.Y().bottomRight(), cCurMvField.mv, (RefPicList)(eCurRefList), cCurMvField.refIdx);
+            cTarMvFieldValid = cs.isClean(pu.Y().bottomRight(), cTarMvField.mv, (RefPicList)(1 - eCurRefList), cTarMvField.refIdx);
+            symCostOk = (cMvPredSymSolid[curRefList] && cMvPredSymSolid[tarRefList]) && (cCurMvFieldValid && cTarMvFieldValid);
+          }
+#endif         
           if (startPtMv != cCurMvField.mv)
           { // if ME change MV, run a final check for best MVP.
+#if GDR_ENABLED
+            symmvdCheckBestMvp(pu, origBuf, cCurMvField.mv, (RefPicList)curRefList, aacAMVPInfo, bcwIdx, cMvPredSym, cMvPredSymSolid, mvpIdxSym, symCost);
+#else
             symmvdCheckBestMvp(pu, origBuf, cCurMvField.mv, (RefPicList)curRefList, aacAMVPInfo, bcwIdx, cMvPredSym, mvpIdxSym, symCost, true);
+#endif
+
+#if GDR_ENABLED            
+            if (isEncodeClean)
+            {
+              int mvp_idx0 = mvpIdxSym[0];
+              int mvp_idx1 = mvpIdxSym[1];
+
+              cMvPredSymSolid[curRefList] = aacAMVPInfo[curRefList][refIdxCur].mvSolid[mvp_idx0];
+              cMvPredSymSolid[tarRefList] = aacAMVPInfo[tarRefList][refIdxTar].mvSolid[mvp_idx1];
+              cMvPredSymValid[curRefList] = cs.isClean(pu.Y().bottomRight(), cCurMvField.mv, (RefPicList)curRefList, pu.cu->slice->getSymRefIdx(curRefList));
+              cMvPredSymValid[tarRefList] = cs.isClean(pu.Y().bottomRight(), cCurMvField.mv, (RefPicList)tarRefList, pu.cu->slice->getSymRefIdx(tarRefList));
+
+              symCostOk = true;
+              symCostOk = symCostOk && cMvPredSymSolid[curRefList];
+              symCostOk = symCostOk && cMvPredSymSolid[tarRefList];
+              symCostOk = symCostOk && cMvPredSymValid[curRefList];
+              symCostOk = symCostOk && cMvPredSymValid[tarRefList];
+            }
+#endif
           }
 
           bits = uiMbBits[2];
@@ -2825,10 +3900,32 @@ void InterSearch::predInterSearch(CodingUnit& cu, Partitioner& partitioner)
             }
           }
           // save results
+#if GDR_ENABLED
+          bool allOk = (symCost < uiCostBi);
+          if (isEncodeClean)
+          {
+            if (symCostOk)
+            {
+              allOk = (uiCostBiOk) ? (symCost < uiCostBi) : true;
+            }
+            else
+            {
+              allOk = false;
+            }
+          }
+#endif
+
+#if GDR_ENABLED
+          if (allOk)
+#else
           if ( symCost < uiCostBi )
+#endif
           {
             uiCostBi = symCost;
             symMode = 1 + curRefList;
+#if GDR_ENABLED
+            uiCostBiOk = symCostOk;
+#endif
 
             cMvBi[curRefList] = cCurMvField.mv;
             iRefIdxBi[curRefList] = cCurMvField.refIdx;
@@ -2839,6 +3936,16 @@ void InterSearch::predInterSearch(CodingUnit& cu, Partitioner& partitioner)
             iRefIdxBi[tarRefList] = cTarMvField.refIdx;
             aaiMvpIdxBi[tarRefList][cTarMvField.refIdx] = mvpIdxSym[tarRefList];
             cMvPredBi[tarRefList][iRefIdxBi[tarRefList]] = cMvPredSym[tarRefList];
+
+#if GDR_ENABLED
+            if (isEncodeClean)
+            {
+              cMvBiValid[curRefList] = cCurMvFieldValid;
+              cMvBiValid[tarRefList] = cTarMvFieldValid;
+              cMvPredBiSolid[curRefList][iRefIdxBi[curRefList]] = cMvPredSymSolid[curRefList];
+              cMvPredBiSolid[tarRefList][iRefIdxBi[tarRefList]] = cMvPredSymSolid[tarRefList];
+            }
+#endif
           }
         }
       } // if (B_SLICE)
@@ -2857,12 +3964,34 @@ void InterSearch::predInterSearch(CodingUnit& cu, Partitioner& partitioner)
       pu.mvpNum[REF_PIC_LIST_0] = NOT_VALID;
       pu.mvpNum[REF_PIC_LIST_1] = NOT_VALID;
 
+#if GDR_ENABLED
+    if (isEncodeClean)
+    {
+      pu.mvSolid[REF_PIC_LIST_0] = true;
+      pu.mvSolid[REF_PIC_LIST_1] = true;
+      pu.mvValid[REF_PIC_LIST_0] = true;
+      pu.mvValid[REF_PIC_LIST_1] = true;
+    }
+#endif
       // Set Motion Field
 
       cMv[1]     = mvValidList1;
+#if GDR_ENABLED            
+      if (isEncodeClean)
+      {
+        cMvSolid[1] = mvValidList1Solid;
+        cMvValid[1] = mvValidList1Valid;
+      }
+#endif
       iRefIdx[1] = refIdxValidList1;
       uiBits[1]  = bitsValidList1;
       uiCost[1]  = costValidList1;
+#if GDR_ENABLED
+      if (isEncodeClean)
+      {
+        uiCostOk[1] = costValidList1Ok;
+      }
+#endif
       if (cu.cs->pps->getWPBiPred() == true && tryBipred && (bcwIdx != BCW_DEFAULT))
       {
         CHECK(iRefIdxBi[0] < 0, "Invalid picture reference index");
@@ -2872,16 +4001,59 @@ void InterSearch::predInterSearch(CodingUnit& cu, Partitioner& partitioner)
         if (WPScalingParam::isWeighted(wp0) || WPScalingParam::isWeighted(wp1))
         {
           uiCostBi       = MAX_UINT;
+#if GDR_ENABLED
+          if (isEncodeClean)
+          {
+            uiCostBiOk = false;
+          }
+#endif
           enforceBcwPred = false;
         }
       }
       if (enforceBcwPred)
       {
         uiCost[0] = uiCost[1] = MAX_UINT;
+#if GDR_ENABLED
+        uiCostOk[0] = uiCostOk[1] = false;
+#endif
       }
 
       uiLastModeTemp = uiLastMode;
+#if GDR_ENABLED
+      allOk = ((uiCostBi <= uiCost[0] && uiCostBi <= uiCost[1]) || enforceBcwPred);
+
+      if (isEncodeClean)
+      {
+        if (uiCostBiOk)
+        {
+          allOk = (uiCostOk[0] && uiCostOk[1]) ? ((uiCostBi <= uiCost[0] && uiCostBi <= uiCost[1]) || enforceBcwPred) : true;
+        }
+        else
+        {
+          allOk = false;
+        }
+      }
+
+      bool L0ok = (uiCost[0] <= uiCost[1]);
+
+      if (isEncodeClean)
+      {
+        if (uiCostOk[0])
+        {
+          L0ok = (uiCostOk[1]) ? (uiCost[0] <= uiCost[1]) : true;
+        }
+        else
+        {
+          L0ok = false;
+        }
+      }
+#endif
+
+#if GDR_ENABLED
+      if (allOk)
+#else
       if ( uiCostBi <= uiCost[0] && uiCostBi <= uiCost[1])
+#endif        
       {
         uiLastMode = 2;
         pu.mv    [REF_PIC_LIST_0] = cMvBi[0];
@@ -2897,8 +4069,23 @@ void InterSearch::predInterSearch(CodingUnit& cu, Partitioner& partitioner)
         pu.interDir = 3;
 
         pu.cu->smvdMode = symMode;
+#if GDR_ENABLED
+        if (isEncodeClean)
+        {
+          int mvp_idx0 = pu.mvpIdx[REF_PIC_LIST_0];
+          int mvp_idx1 = pu.mvpIdx[REF_PIC_LIST_1];
+          pu.mvSolid[REF_PIC_LIST_0] = cMvBiSolid[REF_PIC_LIST_0] && cMvPredBiSolid[REF_PIC_LIST_0][mvp_idx0];
+          pu.mvSolid[REF_PIC_LIST_1] = cMvBiSolid[REF_PIC_LIST_1] && cMvPredBiSolid[REF_PIC_LIST_1][mvp_idx1];
+          pu.mvValid[REF_PIC_LIST_0] = cs.isClean(pu.Y().bottomRight(), pu.mv[REF_PIC_LIST_0], (RefPicList)REF_PIC_LIST_0, pu.refIdx[REF_PIC_LIST_0]);
+          pu.mvValid[REF_PIC_LIST_1] = cs.isClean(pu.Y().bottomRight(), pu.mv[REF_PIC_LIST_1], (RefPicList)REF_PIC_LIST_1, pu.refIdx[REF_PIC_LIST_1]);
+        }
+#endif         
       }
+#if GDR_ENABLED
+      else if (L0ok)
+#else
       else if ( uiCost[0] <= uiCost[1] )
+#endif
       {
         uiLastMode = 0;
         pu.mv    [REF_PIC_LIST_0] = cMv[0];
@@ -2907,6 +4094,13 @@ void InterSearch::predInterSearch(CodingUnit& cu, Partitioner& partitioner)
         pu.mvpIdx[REF_PIC_LIST_0] = aaiMvpIdx[0][iRefIdx[0]];
         pu.mvpNum[REF_PIC_LIST_0] = aaiMvpNum[0][iRefIdx[0]];
         pu.interDir = 1;
+#if GDR_ENABLED
+        if (isEncodeClean)
+        {
+          pu.mvSolid[REF_PIC_LIST_0] = cMvSolid[REF_PIC_LIST_0] && cMvPredSolid[0][iRefIdx[0]];
+          pu.mvValid[REF_PIC_LIST_0] = cs.isClean(pu.Y().bottomRight(), pu.mv[REF_PIC_LIST_0], (RefPicList)REF_PIC_LIST_0, pu.refIdx[REF_PIC_LIST_0]);
+        }
+#endif
       }
       else
       {
@@ -2917,6 +4111,13 @@ void InterSearch::predInterSearch(CodingUnit& cu, Partitioner& partitioner)
         pu.mvpIdx[REF_PIC_LIST_1] = aaiMvpIdx[1][iRefIdx[1]];
         pu.mvpNum[REF_PIC_LIST_1] = aaiMvpNum[1][iRefIdx[1]];
         pu.interDir = 2;
+#if GDR_ENABLED
+        if (isEncodeClean)
+        {
+          pu.mvSolid[REF_PIC_LIST_1] = cMvSolid[REF_PIC_LIST_1] && cMvPredSolid[1][iRefIdx[1]];
+          pu.mvValid[REF_PIC_LIST_1] = cs.isClean(pu.Y().bottomRight(), pu.mv[REF_PIC_LIST_1], (RefPicList)REF_PIC_LIST_1, pu.refIdx[REF_PIC_LIST_1]);
+        }
+#endif
       }
 
       if( bcwIdx != BCW_DEFAULT )
@@ -2927,13 +4128,26 @@ void InterSearch::predInterSearch(CodingUnit& cu, Partitioner& partitioner)
       uiHevcCost = (uiCostBi <= uiCost[0] && uiCostBi <= uiCost[1])
                      ? uiCostBi
                      : ((uiCost[0] <= uiCost[1]) ? uiCost[0] : uiCost[1]);
-    }
+#if GDR_ENABLED
+      if (isEncodeClean)
+      {
+        uiHevcCostOk = (uiCostBi <= uiCost[0] && uiCostBi <= uiCost[1]) ? uiCostBiOk : ((uiCost[0] <= uiCost[1]) ? uiCostOk[0] : uiCostOk[1]);
+      }
+#endif
+    }
+
     if (cu.Y().width > 8 && cu.Y().height > 8 && cu.slice->getSPS()->getUseAffine()
       && checkAffine
       && (bcwIdx == BCW_DEFAULT || m_affineModeSelected || !m_pcEncCfg->getUseBcwFast())
       )
     {
       m_hevcCost = uiHevcCost;
+#if GDR_ENABLED
+      if (isEncodeClean)
+      {
+        m_hevcCostOk = uiHevcCostOk;
+      }
+#endif
       // save normal hevc result
       uint32_t uiMRGIndex = pu.mergeIdx;
       bool bMergeFlag = pu.mergeFlag;
@@ -2953,28 +4167,101 @@ void InterSearch::predInterSearch(CodingUnit& cu, Partitioner& partitioner)
       cHevcMvField[0].setMvField( pu.mv[REF_PIC_LIST_0], pu.refIdx[REF_PIC_LIST_0] );
       cHevcMvField[1].setMvField( pu.mv[REF_PIC_LIST_1], pu.refIdx[REF_PIC_LIST_1] );
 
+#if GDR_ENABLED
+      bool cHevcMvFieldSolid[2];
+      bool cHevcMvFieldValid[2];
+
+      if (isEncodeClean)
+      {
+        cHevcMvFieldSolid[0] = pu.mvSolid[0];
+        cHevcMvFieldSolid[1] = pu.mvSolid[1];
+        cHevcMvFieldValid[0] = pu.mvValid[0];
+        cHevcMvFieldValid[1] = pu.mvValid[1];
+      }
+#endif
+
       // do affine ME & Merge
       cu.affineType = AFFINEMODEL_4PARAM;
       Mv acMvAffine4Para[2][33][3];
+#if GDR_ENABLED
+      bool acMvAffine4ParaSolid[2][33][3];
+
+      for (int i = 0; i < 2; i++)
+        for (int j = 0; j < 33; j++)
+          for (int k = 0; k < 3; k++)
+            acMvAffine4ParaSolid[i][j][k] = true;
+#endif
       int refIdx4Para[2] = { -1, -1 };
 
+#if GDR_ENABLED      
+      xPredAffineInterSearch(pu, origBuf, puIdx, uiLastModeTemp, uiAffineCost, cMvHevcTemp, cMvHevcTempSolid, acMvAffine4Para, acMvAffine4ParaSolid, refIdx4Para, bcwIdx, enforceBcwPred,
+        ((cu.slice->getSPS()->getUseBcw() == true) ? getWeightIdxBits(bcwIdx) : 0));
+#else
       xPredAffineInterSearch(pu, origBuf, puIdx, uiLastModeTemp, uiAffineCost, cMvHevcTemp, acMvAffine4Para, refIdx4Para, bcwIdx, enforceBcwPred,
         ((cu.slice->getSPS()->getUseBcw() == true) ? getWeightIdxBits(bcwIdx) : 0));
+#endif      
 
       if ( pu.cu->imv == 0 )
       {
+#if GDR_ENABLED
+        storeAffineMotion(pu.mvAffi, pu.mvAffiSolid, pu.refIdx, AFFINEMODEL_4PARAM, bcwIdx);
+#else
         storeAffineMotion( pu.mvAffi, pu.refIdx, AFFINEMODEL_4PARAM, bcwIdx );
+#endif
+      }
+
+#if GDR_ENABLED      
+      if (isEncodeClean)
+      {
+        uiAffineCostOk = true;
+
+        if (pu.interDir & 0x01)
+        {
+          uiAffineCostOk = uiAffineCostOk && pu.mvAffiSolid[0][0] && pu.mvAffiSolid[0][1];
+          uiAffineCostOk = uiAffineCostOk && pu.mvAffiValid[0][0] && pu.mvAffiValid[0][1];
+        }
+
+        if (pu.interDir & 0x02)
+        {
+          uiAffineCostOk = uiAffineCostOk && pu.mvAffiSolid[1][0] && pu.mvAffiSolid[1][1];
+          uiAffineCostOk = uiAffineCostOk && pu.mvAffiValid[1][0] && pu.mvAffiValid[1][1];
+        }
       }
+#endif
 
       if ( cu.slice->getSPS()->getUseAffineType() )
       {
+#if GDR_ENABLED
+        allOk = (uiAffineCost < uiHevcCost * 1.05);
+        if (isEncodeClean)
+        {
+          if (uiAffineCostOk)
+          {
+            allOk = (uiHevcCostOk) ? (uiAffineCost < uiHevcCost * 1.05) : true;
+          }
+          else
+          {
+            allOk = false;
+          }
+        }
+#endif
+
+#if GDR_ENABLED
+        if (allOk)
+#else
         if ( uiAffineCost < uiHevcCost * 1.05 ) ///< condition for 6 parameter affine ME
+#endif
         {
           // save 4 parameter results
           Mv bestMv[2][3], bestMvd[2][3];
           int bestMvpIdx[2], bestMvpNum[2], bestRefIdx[2];
           uint8_t bestInterDir;
 
+#if GDR_ENABLED
+          bool bestMvSolid[2][3];
+          bool bestMvValid[2][3];
+#endif
+
           bestInterDir = pu.interDir;
           bestRefIdx[0] = pu.refIdx[0];
           bestRefIdx[1] = pu.refIdx[1];
@@ -2991,6 +4278,19 @@ void InterSearch::predInterSearch(CodingUnit& cu, Partitioner& partitioner)
             bestMvd[refList][0] = pu.mvdAffi[refList][0];
             bestMvd[refList][1] = pu.mvdAffi[refList][1];
             bestMvd[refList][2] = pu.mvdAffi[refList][2];
+
+#if GDR_ENABLED
+            if (isEncodeClean)
+            {
+              bestMvSolid[refList][0] = pu.mvAffiSolid[refList][0];
+              bestMvSolid[refList][1] = pu.mvAffiSolid[refList][1];
+              bestMvSolid[refList][2] = pu.mvAffiSolid[refList][2];
+
+              bestMvValid[refList][0] = pu.mvAffiValid[refList][0];
+              bestMvValid[refList][1] = pu.mvAffiValid[refList][1];
+              bestMvValid[refList][2] = pu.mvAffiValid[refList][2];
+            }
+#endif
           }
 
           refIdx4Para[0] = bestRefIdx[0];
@@ -2998,16 +4298,63 @@ void InterSearch::predInterSearch(CodingUnit& cu, Partitioner& partitioner)
 
           Distortion uiAffine6Cost = std::numeric_limits<Distortion>::max();
           cu.affineType = AFFINEMODEL_6PARAM;
+#if GDR_ENABLED          
+          xPredAffineInterSearch(pu, origBuf, puIdx, uiLastModeTemp, uiAffine6Cost, cMvHevcTemp, cMvHevcTempSolid, acMvAffine4Para, acMvAffine4ParaSolid, refIdx4Para, bcwIdx, enforceBcwPred,
+            ((cu.slice->getSPS()->getUseBcw() == true) ? getWeightIdxBits(bcwIdx) : 0));
+#else
           xPredAffineInterSearch(pu, origBuf, puIdx, uiLastModeTemp, uiAffine6Cost, cMvHevcTemp, acMvAffine4Para, refIdx4Para, bcwIdx, enforceBcwPred,
             ((cu.slice->getSPS()->getUseBcw() == true) ? getWeightIdxBits(bcwIdx) : 0));
+#endif
 
           if ( pu.cu->imv == 0 )
           {
+#if GDR_ENABLED
+            storeAffineMotion(pu.mvAffi, pu.mvAffiSolid, pu.refIdx, AFFINEMODEL_6PARAM, bcwIdx);
+#else
             storeAffineMotion( pu.mvAffi, pu.refIdx, AFFINEMODEL_6PARAM, bcwIdx );
+#endif
+          }
+
+#if GDR_ENABLED
+          if (isEncodeClean)
+          {
+            uiAffine6CostOk = true;
+
+            if (pu.interDir & 0x01)
+            {
+              uiAffine6CostOk = uiAffine6CostOk && pu.mvAffiSolid[0][0] && pu.mvAffiSolid[0][1] && pu.mvAffiSolid[0][2];
+              uiAffine6CostOk = uiAffine6CostOk && pu.mvAffiValid[0][0] && pu.mvAffiValid[0][1] && pu.mvAffiValid[0][2];
+            }
+
+            if (pu.interDir & 0x02)
+            {
+              uiAffine6CostOk = uiAffine6CostOk && pu.mvAffiSolid[1][0] && pu.mvAffiSolid[1][1] && pu.mvAffiSolid[1][2];
+              uiAffine6CostOk = uiAffine6CostOk && pu.mvAffiValid[1][0] && pu.mvAffiValid[1][1] && pu.mvAffiValid[1][2];
+            }
+          }
+#endif
+
+#if GDR_ENABLED
+          allOk = (uiAffineCost <= uiAffine6Cost);
+          if (isEncodeClean)
+          {
+            if (uiAffineCostOk)
+            {
+              allOk = (uiAffine6CostOk) ? (uiAffineCost < uiHevcCost * 1.05) : true;
+            }
+            else
+            {
+              allOk = false;
+            }
           }
+#endif
 
           // reset to 4 parameter affine inter mode
+#if GDR_ENABLED
+          if (allOk && (uiAffineCost <= uiAffine6Cost))
+#else
           if ( uiAffineCost <= uiAffine6Cost )
+#endif
           {
             cu.affineType = AFFINEMODEL_4PARAM;
             pu.interDir = bestInterDir;
@@ -3026,10 +4373,37 @@ void InterSearch::predInterSearch(CodingUnit& cu, Partitioner& partitioner)
 
             PU::setAllAffineMv( pu, bestMv[0][0], bestMv[0][1], bestMv[0][2], REF_PIC_LIST_0);
             PU::setAllAffineMv( pu, bestMv[1][0], bestMv[1][1], bestMv[1][2], REF_PIC_LIST_1);
+
+#if GDR_ENABLED
+            if (isEncodeClean)
+            {
+              pu.mvAffiSolid[REF_PIC_LIST_0][0] = bestMvSolid[REF_PIC_LIST_0][0];
+              pu.mvAffiSolid[REF_PIC_LIST_0][1] = bestMvSolid[REF_PIC_LIST_0][1];
+              pu.mvAffiSolid[REF_PIC_LIST_0][2] = bestMvSolid[REF_PIC_LIST_0][2];
+
+              pu.mvAffiValid[REF_PIC_LIST_0][0] = bestMvValid[REF_PIC_LIST_0][0];
+              pu.mvAffiValid[REF_PIC_LIST_0][1] = bestMvValid[REF_PIC_LIST_0][1];
+              pu.mvAffiValid[REF_PIC_LIST_0][2] = bestMvValid[REF_PIC_LIST_0][2];
+
+              pu.mvAffiSolid[REF_PIC_LIST_1][0] = bestMvSolid[REF_PIC_LIST_1][0];
+              pu.mvAffiSolid[REF_PIC_LIST_1][1] = bestMvSolid[REF_PIC_LIST_1][1];
+              pu.mvAffiSolid[REF_PIC_LIST_1][2] = bestMvSolid[REF_PIC_LIST_1][2];
+
+              pu.mvAffiValid[REF_PIC_LIST_1][0] = bestMvValid[REF_PIC_LIST_1][0];
+              pu.mvAffiValid[REF_PIC_LIST_1][1] = bestMvValid[REF_PIC_LIST_1][1];
+              pu.mvAffiValid[REF_PIC_LIST_1][2] = bestMvValid[REF_PIC_LIST_1][2];
+            }
+#endif
           }
           else
           {
             uiAffineCost = uiAffine6Cost;
+#if GDR_ENABLED
+            if (isEncodeClean)
+            {
+              uiAffineCostOk = uiAffine6CostOk;
+            }
+#endif
           }
         }
 
@@ -3043,7 +4417,26 @@ void InterSearch::predInterSearch(CodingUnit& cu, Partitioner& partitioner)
           uiAffineCost = std::numeric_limits<Distortion>::max();
         }
       }
+#if GDR_ENABLED
+      allOk = (uiHevcCost <= uiAffineCost);
+      if (isEncodeClean)
+      {
+        if (uiHevcCostOk)
+        {
+          allOk = (uiAffineCostOk) ? (uiHevcCost <= uiAffineCost) : true;
+        }
+        else
+        {
+          allOk = false;
+        }
+      }
+#endif      
+
+#if GDR_ENABLED
+      if (allOk)
+#else
       if ( uiHevcCost <= uiAffineCost )
+#endif
       {
         // set hevc me result
         cu.affine = false;
@@ -3062,6 +4455,15 @@ void InterSearch::predInterSearch(CodingUnit& cu, Partitioner& partitioner)
         pu.mvpNum[REF_PIC_LIST_1] = uiMvpNum[1];
         pu.mvd[REF_PIC_LIST_0] = cMvd[0];
         pu.mvd[REF_PIC_LIST_1] = cMvd[1];
+#if GDR_ENABLED
+        if (isEncodeClean)
+        {
+          pu.mvSolid[REF_PIC_LIST_0] = cHevcMvFieldSolid[0];
+          pu.mvSolid[REF_PIC_LIST_1] = cHevcMvFieldSolid[1];
+          pu.mvValid[REF_PIC_LIST_0] = cHevcMvFieldValid[0];
+          pu.mvValid[REF_PIC_LIST_1] = cHevcMvFieldValid[1];
+        }
+#endif
       }
       else
       {
@@ -3128,6 +4530,25 @@ void InterSearch::xEstimateMvPredAMVP( PredictionUnit& pu, PelUnitBuf& origBuf,
   int        i;
 
   AMVPInfo*  pcAMVPInfo = &rAMVPInfo;
+#if GDR_ENABLED
+  const CodingStructure &cs = *pu.cs;
+  const bool isEncodeClean = cs.pcv->isEncoder && ((cs.picHeader->getInGdrPeriod() && cs.isClean(pu.Y().topRight(), CHANNEL_TYPE_LUMA)) || (cs.picHeader->getNumVerVirtualBoundaries() == 0));
+#endif  
+
+#if GDR_ENABLED
+  if (isEncodeClean)
+  {
+    pcAMVPInfo->allCandSolidInAbove = true;
+    for (int i = 0; i < AMVP_MAX_NUM_CANDS_MEM; i++)
+    {
+      pcAMVPInfo->mvSolid[i] = true;
+      pcAMVPInfo->mvValid[i] = true;
+    }
+  }
+
+  bool uiBestCostOk = false;
+  bool uiTmpCostOk = false;
+#endif
 
   // Fill the MV Candidates
   if (!bFilled)
@@ -3138,6 +4559,12 @@ void InterSearch::xEstimateMvPredAMVP( PredictionUnit& pu, PelUnitBuf& origBuf,
   // initialize Mvp index & Mvp
   iBestIdx = 0;
   cBestMv  = pcAMVPInfo->mvCand[0];
+#if GDR_ENABLED        
+  if (isEncodeClean)
+  {
+    uiBestCostOk = pcAMVPInfo->mvSolid[0];
+  }
+#endif
 
   PelUnitBuf predBuf = m_tmpStorageLCU.getBuf( UnitAreaRelative(*pu.cu, pu) );
 
@@ -3145,12 +4572,46 @@ void InterSearch::xEstimateMvPredAMVP( PredictionUnit& pu, PelUnitBuf& origBuf,
   for( i = 0 ; i < pcAMVPInfo->numCand; i++)
   {
     Distortion uiTmpCost = xGetTemplateCost( pu, origBuf, predBuf, pcAMVPInfo->mvCand[i], i, AMVP_MAX_NUM_CANDS, eRefPicList, iRefIdx );
+
+#if GDR_ENABLED        
+    if (isEncodeClean)
+    {
+      uiTmpCostOk = pcAMVPInfo->mvSolid[i];
+    }
+#endif
+
+#if GDR_ENABLED    
+    bool allOk = (uiBestCost > uiTmpCost);
+
+    if (isEncodeClean)
+    {
+      if (uiBestCostOk)
+      {
+        allOk = (uiTmpCostOk) ? (uiBestCost > uiTmpCost) : true;
+      }
+      else
+      {
+        allOk = false;
+      }
+    }
+#endif
+
+#if GDR_ENABLED    
+    if (allOk)
+#else
     if( uiBestCost > uiTmpCost )
+#endif
     {
       uiBestCost     = uiTmpCost;
       cBestMv        = pcAMVPInfo->mvCand[i];
       iBestIdx       = i;
       (*puiDistBiP)  = uiTmpCost;
+#if GDR_ENABLED
+      if (isEncodeClean)
+      {
+        uiBestCostOk = uiTmpCostOk;
+      }
+#endif
     }
   }
 
@@ -3159,6 +4620,13 @@ void InterSearch::xEstimateMvPredAMVP( PredictionUnit& pu, PelUnitBuf& origBuf,
   pu.mvpIdx[eRefPicList] = iBestIdx;
   pu.mvpNum[eRefPicList] = pcAMVPInfo->numCand;
 
+#if GDR_ENABLED
+  if (isEncodeClean)
+  {
+    pu.mvpSolid[eRefPicList] = pcAMVPInfo->mvSolid[iBestIdx];
+  }
+#endif
+
   return;
 }
 
@@ -3203,11 +4671,28 @@ void InterSearch::xCopyAMVPInfo (AMVPInfo* pSrc, AMVPInfo* pDst)
   for (int i = 0; i < pSrc->numCand; i++)
   {
     pDst->mvCand[i] = pSrc->mvCand[i];
+#if GDR_ENABLED
+    pDst->mvPos[i] = pSrc->mvPos[i];
+    pDst->mvSolid[i] = pSrc->mvSolid[i];
+    pDst->mvValid[i] = pSrc->mvValid[i];
+    pDst->mvType[i] = pSrc->mvType[i];
+#endif
   }
 }
 
+#if GDR_ENABLED
+void InterSearch::xCheckBestMVP(PredictionUnit &pu, RefPicList eRefPicList, Mv cMv, Mv& rcMvPred, int& riMVPIdx, AMVPInfo& amvpInfo, uint32_t& ruiBits, Distortion& ruiCost, const uint8_t imv)
+#else
 void InterSearch::xCheckBestMVP ( RefPicList eRefPicList, Mv cMv, Mv& rcMvPred, int& riMVPIdx, AMVPInfo& amvpInfo, uint32_t& ruiBits, Distortion& ruiCost, const uint8_t imv )
+#endif
 {
+#if GDR_ENABLED  
+  const CodingStructure &cs = *pu.cs;
+  const bool isEncodeClean = cs.pcv->isEncoder && ((cs.picHeader->getInGdrPeriod() && cs.isClean(pu.Y().topRight(), CHANNEL_TYPE_LUMA)) || (cs.picHeader->getNumVerVirtualBoundaries() == 0));
+  bool iBestMvBitsOk = false;
+  bool iMvBitsOk = false;
+#endif
+
   if ( imv > 0 && imv < 3 )
   {
     return;
@@ -3234,6 +4719,12 @@ void InterSearch::xCheckBestMVP ( RefPicList eRefPicList, Mv cMv, Mv& rcMvPred,
   int iOrgMvBits = m_pcRdCost->getBitsOfVectorWithPredictor(mv.getHor(), mv.getVer(), 0);
   iOrgMvBits += m_auiMVPIdxCost[riMVPIdx][AMVP_MAX_NUM_CANDS];
   int iBestMvBits = iOrgMvBits;
+#if GDR_ENABLED  
+  if (isEncodeClean)
+  {
+    iBestMvBitsOk = pcAMVPInfo->mvSolid[riMVPIdx];
+  }
+#endif
 
   for (int iMVPIdx = 0; iMVPIdx < pcAMVPInfo->numCand; iMVPIdx++)
   {
@@ -3248,10 +4739,36 @@ void InterSearch::xCheckBestMVP ( RefPicList eRefPicList, Mv cMv, Mv& rcMvPred,
     int iMvBits = m_pcRdCost->getBitsOfVectorWithPredictor(mv.getHor(), mv.getVer(), 0);
     iMvBits += m_auiMVPIdxCost[iMVPIdx][AMVP_MAX_NUM_CANDS];
 
+#if GDR_ENABLED
+    bool allOk = (iMvBits < iBestMvBits);
+    if (isEncodeClean)
+    {
+      iMvBitsOk = pcAMVPInfo->mvSolid[iMVPIdx];
+      if (iMvBitsOk)
+      {
+        allOk = (iBestMvBitsOk) ? (iMvBits < iBestMvBits) : true;
+      }
+      else
+      {
+        allOk = false;
+      }
+    }
+#endif
+
+#if GDR_ENABLED
+    if (allOk)
+#else
     if (iMvBits < iBestMvBits)
+#endif
     {
       iBestMvBits = iMvBits;
       iBestMVPIdx = iMVPIdx;
+#if GDR_ENABLED
+      if (isEncodeClean)
+      {
+        iBestMvBitsOk = iMvBitsOk;
+      }
+#endif
     }
   }
 
@@ -3300,7 +4817,11 @@ Distortion InterSearch::xGetTemplateCost( const PredictionUnit& pu,
   return uiCost;
 }
 
+#if GDR_ENABLED
+Distortion InterSearch::xGetAffineTemplateCost(PredictionUnit& pu, PelUnitBuf& origBuf, PelUnitBuf& predBuf, Mv acMvCand[3], int iMVPIdx, int iMVPNum, RefPicList eRefPicList, int iRefIdx, bool& rbOk)
+#else
 Distortion InterSearch::xGetAffineTemplateCost( PredictionUnit& pu, PelUnitBuf& origBuf, PelUnitBuf& predBuf, Mv acMvCand[3], int iMVPIdx, int iMVPNum, RefPicList eRefPicList, int iRefIdx )
+#endif
 {
   Distortion uiCost = std::numeric_limits<Distortion>::max();
 
@@ -3311,7 +4832,12 @@ Distortion InterSearch::xGetAffineTemplateCost( PredictionUnit& pu, PelUnitBuf&
   Mv mv[3];
   memcpy(mv, acMvCand, sizeof(mv));
   m_iRefListIdx = eRefPicList;
+
+#if GDR_ENABLED
+  rbOk = xPredAffineBlk(COMPONENT_Y, pu, picRef, mv, predBuf, bi, pu.cu->slice->clpRng(COMPONENT_Y));
+#else
   xPredAffineBlk(COMPONENT_Y, pu, picRef, mv, predBuf, bi, pu.cu->slice->clpRng(COMPONENT_Y));
+#endif
   if( bi )
   {
     xWeightedPredictionUni( pu, predBuf, eRefPicList, predBuf, iRefIdx, m_maxCompIDToPred );
@@ -3327,9 +4853,17 @@ Distortion InterSearch::xGetAffineTemplateCost( PredictionUnit& pu, PelUnitBuf&
   return uiCost;
 }
 
+#if GDR_ENABLED
+void InterSearch::xMotionEstimation(PredictionUnit& pu, PelUnitBuf& origBuf, RefPicList eRefPicList, Mv& rcMvPred, int iRefIdxPred, Mv& rcMv, bool &rcMvSolid, int& riMVPIdx, uint32_t& ruiBits, Distortion& ruiCost, const AMVPInfo& amvpInfo, bool& rbCleanCandExist, bool bBi)
+#else
 void InterSearch::xMotionEstimation(PredictionUnit& pu, PelUnitBuf& origBuf, RefPicList eRefPicList, Mv& rcMvPred, int iRefIdxPred, Mv& rcMv, int& riMVPIdx, uint32_t& ruiBits, Distortion& ruiCost, const AMVPInfo& amvpInfo, bool bBi)
+#endif
 {
+#if GDR_ENABLED
+  if (pu.cu->cs->sps->getUseBcw() && pu.cu->BcwIdx != BCW_DEFAULT && !bBi && xReadBufferedUniMv(pu, eRefPicList, iRefIdxPred, rcMvPred, rcMv, rcMvSolid, ruiBits, ruiCost))
+#else
   if( pu.cu->cs->sps->getUseBcw() && pu.cu->BcwIdx != BCW_DEFAULT && !bBi && xReadBufferedUniMv(pu, eRefPicList, iRefIdxPred, rcMvPred, rcMv, ruiBits, ruiCost) )
+#endif
   {
     return;
   }
@@ -3460,7 +4994,11 @@ void InterSearch::xMotionEstimation(PredictionUnit& pu, PelUnitBuf& origBuf, Ref
 
     if( !bQTBTMV )
     {
+#if GDR_ENABLED
+      xSetSearchRange(pu, bestInitMv, iSrchRng, cStruct.searchRange, cStruct, eRefPicList, iRefIdxPred);
+#else
       xSetSearchRange(pu, bestInitMv, iSrchRng, cStruct.searchRange, cStruct);
+#endif
     }
     xPatternSearch( cStruct, rcMv, ruiCost);
   }
@@ -3506,7 +5044,11 @@ void InterSearch::xMotionEstimation(PredictionUnit& pu, PelUnitBuf& origBuf, Ref
         MCTSHelper::clipMvToArea( rcMv, pu.Y(), curTileAreaSubPelRestricted, *pu.cs->sps, 0 );
       }
     }
+#if GDR_ENABLED
+    xPatternSearchFracDIF(pu, eRefPicList, iRefIdxPred, cStruct, rcMv, cMvHalf, cMvQter, ruiCost, rbCleanCandExist);
+#else
     xPatternSearchFracDIF( pu, eRefPicList, iRefIdxPred, cStruct, rcMv, cMvHalf, cMvQter, ruiCost );
+#endif
     m_pcRdCost->setCostScale( 0 );
     rcMv <<= 2;
     rcMv  += ( cMvHalf <<= 1 );
@@ -3519,7 +5061,11 @@ void InterSearch::xMotionEstimation(PredictionUnit& pu, PelUnitBuf& origBuf, Ref
   else // integer refinement for integer-pel and 4-pel resolution
   {
     rcMv.changePrecision(MV_PRECISION_INT, MV_PRECISION_INTERNAL);
+#if GDR_ENABLED 
+    xPatternSearchIntRefine(pu, cStruct, rcMv, rcMvPred, riMVPIdx, ruiBits, ruiCost, amvpInfo, fWeight, eRefPicList, iRefIdxPred, rbCleanCandExist);
+#else
     xPatternSearchIntRefine( pu, cStruct, rcMv, rcMvPred, riMVPIdx, ruiBits, ruiCost, amvpInfo, fWeight);
+#endif
   }
   DTRACE(g_trace_ctx, D_ME, "   MECost<L%d,%d>: %6d (%d)  MV:%d,%d\n", (int)eRefPicList, (int)bBi, ruiCost, ruiBits, rcMv.getHor() << 2, rcMv.getVer() << 2);
 }
@@ -3531,6 +5077,10 @@ void InterSearch::xSetSearchRange ( const PredictionUnit& pu,
                                     const int iSrchRng,
                                     SearchRange& sr
                                   , IntTZSearchStruct& cStruct
+#if GDR_ENABLED
+                                  , RefPicList eRefPicList
+                                  , int iRefIdx
+#endif
 )
 {
   const int iMvShift = MV_FRACTIONAL_BITS_INTERNAL;
@@ -3539,6 +5089,46 @@ void InterSearch::xSetSearchRange ( const PredictionUnit& pu,
 
   Mv mvTL(cFPMvPred.getHor() - (iSrchRng << iMvShift), cFPMvPred.getVer() - (iSrchRng << iMvShift));
   Mv mvBR(cFPMvPred.getHor() + (iSrchRng << iMvShift), cFPMvPred.getVer() + (iSrchRng << iMvShift));
+#if GDR_ENABLED
+  bool isRefGdrPicture = pu.cs->slice->getRefPic(eRefPicList, iRefIdx)->cs->picHeader->getInGdrPeriod();
+  if (isRefGdrPicture)
+  {
+    mvTL = { cFPMvPred.getHor(), cFPMvPred.getVer() };
+    mvBR = { cFPMvPred.getHor(), cFPMvPred.getVer() };
+
+    const int lumaPixelAway = 4;
+    const int chromaPixelAway = 5;
+
+    const Position LastPos = pu.Y().bottomRight();
+
+    const int iMvShift = MV_FRACTIONAL_BITS_INTERNAL;
+    const int iMvLumaFrac = (1 << iMvShift);
+    const int iMvChromaFrac = (iMvLumaFrac << 1);
+    const int iFracOne = (1 << iMvShift);
+
+    const bool isIntLumaMv = (cFPMvPred.getHor() % iMvLumaFrac) == 0;
+    const bool isIntChromaMv = (cFPMvPred.getHor() % iMvChromaFrac) == 0;
+
+    const int scaled_endx = pu.cs->slice->getRefPic(eRefPicList, iRefIdx)->cs->picHeader->getVirtualBoundariesPosX(0) << iMvShift;
+
+    const Position OrigFracPos = Position(LastPos.x << iMvShift, LastPos.y << iMvShift);
+    const int last_luma_pos = ((OrigFracPos.x / iMvLumaFrac)   * iMvLumaFrac) + cFPMvPred.getHor() + (isIntLumaMv ? 0 : (lumaPixelAway << iMvShift));
+    const int last_chroma_pos = ((OrigFracPos.x / iMvChromaFrac) * iMvChromaFrac) + cFPMvPred.getHor() + (isIntChromaMv ? 0 : (chromaPixelAway << iMvShift));
+
+    const int last_pel_pos = std::max(last_luma_pos, last_chroma_pos);
+
+    const int distance = Clip3(-(iSrchRng << iMvShift), (iSrchRng << iMvShift), scaled_endx - (last_pel_pos + iFracOne));
+
+
+    int srLeft = cFPMvPred.getHor() - (iSrchRng << iMvShift);
+    int srRight = cFPMvPred.getHor() + distance;
+    int srTop = cFPMvPred.getVer() - (iSrchRng << iMvShift);
+    int srBottom = cFPMvPred.getVer() + (iSrchRng << iMvShift);
+
+    mvTL = { srLeft, srTop };
+    mvBR = { srRight, srBottom };
+  }
+#endif
 
   if (m_pcEncCfg->getMCTSEncConstraint())
   {
@@ -3703,6 +5293,10 @@ void InterSearch::xTZSearch( const PredictionUnit& pu,
   const uint32_t uiStarRefinementRounds                  = 2;  // star refinement stop X rounds after best match (must be >=1)
   const bool bNewZeroNeighbourhoodTest               = bExtendedSettings;
 
+#if GDR_ENABLED
+  const CodingStructure &cs = *pu.cs;
+  const bool isEncodeClean = cs.pcv->isEncoder && ((cs.picHeader->getInGdrPeriod() && cs.isClean(pu.Y().topRight(), CHANNEL_TYPE_LUMA)) || (cs.picHeader->getNumVerVirtualBoundaries() == 0));
+#endif    
   int iSearchRange = m_iSearchRange;
   if( m_pcEncCfg->getMCTSEncConstraint() )
   {
@@ -3789,6 +5383,37 @@ void InterSearch::xTZSearch( const PredictionUnit& pu,
 
     Distortion uiSad = m_cDistParam.distFunc(m_cDistParam);
     uiSad += m_pcRdCost->getCostOfVectorWithPredictor(cTmpMv.hor, cTmpMv.ver, cStruct.imvShift);
+#if GDR_ENABLED
+    bool allOk = (uiSad < cStruct.uiBestSad);
+
+    if (isEncodeClean)
+    {
+      Mv motion = cTmpMv;
+      motion.changePrecision(MV_PRECISION_INT, MV_PRECISION_INTERNAL);
+      bool cTmpMvOk = cs.isClean(pu.Y().bottomRight(), motion, eRefPicList, iRefIdxPred);
+
+      Mv bestMv = { cStruct.iBestX, cStruct.iBestY };
+      bestMv.changePrecision(MV_PRECISION_INT, MV_PRECISION_INTERNAL);
+      bool bestMvOk = cs.isClean(pu.Y().bottomRight(), bestMv, eRefPicList, iRefIdxPred);
+
+      if (cTmpMvOk)
+      {
+        allOk = (bestMvOk) ? (uiSad < cStruct.uiBestSad) : true;
+      }
+      else
+      {
+        allOk = false;
+      }
+    }
+
+    if (allOk)
+    {
+      cStruct.uiBestSad = uiSad;
+      cStruct.iBestX = cTmpMv.hor;
+      cStruct.iBestY = cTmpMv.ver;
+      m_cDistParam.maximumDistortionForEarlyExit = uiSad;
+    }
+#else
     if (uiSad < cStruct.uiBestSad)
     {
       cStruct.uiBestSad = uiSad;
@@ -3796,13 +5421,18 @@ void InterSearch::xTZSearch( const PredictionUnit& pu,
       cStruct.iBestY = cTmpMv.ver;
       m_cDistParam.maximumDistortionForEarlyExit = uiSad;
     }
+#endif
   }
 
   {
     // set search range
     Mv currBestMv(cStruct.iBestX, cStruct.iBestY );
     currBestMv <<= MV_FRACTIONAL_BITS_INTERNAL;
+#if GDR_ENABLED
+    xSetSearchRange(pu, currBestMv, m_iSearchRange >> (bFastSettings ? 1 : 0), sr, cStruct, eRefPicList, iRefIdxPred);
+#else
     xSetSearchRange(pu, currBestMv, m_iSearchRange >> (bFastSettings ? 1 : 0), sr, cStruct);
+#endif
   }
   if (m_pcEncCfg->getUseHashME() && (m_currRefPicList == 0 || pu.cu->slice->getList1IdxToList0Idx(m_currRefPicIndex) < 0))
   {
@@ -4108,7 +5738,11 @@ void InterSearch::xTZSearchSelective( const PredictionUnit& pu,
     // set search range
     Mv currBestMv(cStruct.iBestX, cStruct.iBestY );
     currBestMv <<= 2;
+#if GDR_ENABLED
+    xSetSearchRange(pu, currBestMv, m_iSearchRange, sr, cStruct, eRefPicList, iRefIdxPred);
+#else
     xSetSearchRange( pu, currBestMv, m_iSearchRange, sr, cStruct );
+#endif
   }
   if (m_pcEncCfg->getUseHashME() && (m_currRefPicList == 0 || pu.cu->slice->getList1IdxToList0Idx(m_currRefPicIndex) < 0))
   {
@@ -4205,8 +5839,16 @@ void InterSearch::xTZSearchSelective( const PredictionUnit& pu,
   ruiSAD = cStruct.uiBestSad - m_pcRdCost->getCostOfVectorWithPredictor( cStruct.iBestX, cStruct.iBestY, cStruct.imvShift );
 }
 
+#if GDR_ENABLED 
+void InterSearch::xPatternSearchIntRefine(PredictionUnit& pu, IntTZSearchStruct&  cStruct, Mv& rcMv, Mv& rcMvPred, int& riMVPIdx, uint32_t& ruiBits, Distortion& ruiCost, const AMVPInfo& amvpInfo, double fWeight, RefPicList eRefPicList, int iRefIdxPred, bool& rbCleanCandExist)
+#else
 void InterSearch::xPatternSearchIntRefine(PredictionUnit& pu, IntTZSearchStruct&  cStruct, Mv& rcMv, Mv& rcMvPred, int& riMVPIdx, uint32_t& ruiBits, Distortion& ruiCost, const AMVPInfo& amvpInfo, double fWeight)
+#endif
 {
+#if GDR_ENABLED
+  const CodingStructure &cs = *pu.cs;
+  const bool isEncodeClean = cs.pcv->isEncoder && ((cs.picHeader->getInGdrPeriod() && cs.isClean(pu.Y().topRight(), CHANNEL_TYPE_LUMA)) || (cs.picHeader->getNumVerVirtualBoundaries() == 0));
+#endif
 
   CHECK( pu.cu->imv == 0 || pu.cu->imv == IMV_HPEL , "xPatternSearchIntRefine(): Sub-pel MV used.");
   CHECK( amvpInfo.mvCand[riMVPIdx] != rcMvPred, "xPatternSearchIntRefine(): MvPred issue.");
@@ -4238,6 +5880,11 @@ void InterSearch::xPatternSearchIntRefine(PredictionUnit& pu, IntTZSearchStruct&
   cBaseMvd[1].roundTransPrecInternal2Amvr(pu.cu->imv);
 
   // test best integer position and all 8 neighboring positions
+#if GDR_ENABLED  
+  bool allOk = true;
+  bool uiDistOk = false;
+  bool uiBestDistOk = false;
+#endif
   for (int pos = 0; pos < 9; pos ++)
   {
     Mv cTestMv[2];
@@ -4285,12 +5932,42 @@ void InterSearch::xPatternSearchIntRefine(PredictionUnit& pu, IntTZSearchStruct&
       iMvBits += m_pcRdCost->getBitsOfVectorWithPredictor( mv.getHor(), mv.getVer(), 0 );
       uiDist += m_pcRdCost->getCost(iMvBits);
 
+#if GDR_ENABLED
+      allOk = (uiDist < uiBestDist);
+      if (isEncodeClean)
+      {
+        bool isSolid = amvpInfo.mvSolid[iMVPIdx];
+        bool isValid = cs.isClean(pu.Y().bottomRight(), cTestMv[iMVPIdx], eRefPicList, iRefIdxPred);
+
+        uiDistOk = isSolid && isValid;
+        if (uiDistOk)
+        {
+          allOk = (uiBestDistOk) ? (uiDist < uiBestDist) : true;
+        }
+        else
+        {
+          allOk = false;
+        }
+      }
+#endif
+
+#if GDR_ENABLED
+      if (allOk)
+#else
       if (uiDist < uiBestDist)
+#endif
       {
         uiBestDist = uiDist;
         cBestMv = cTestMv[iMVPIdx];
         iBestMVPIdx = iMVPIdx;
         iBestBits = iMvBits;
+#if GDR_ENABLED
+        if (isEncodeClean)
+        {
+          uiBestDistOk = uiDistOk;
+          rbCleanCandExist = true;
+        }
+#endif
       }
     }
   }
@@ -4326,6 +6003,9 @@ void InterSearch::xPatternSearchFracDIF(
   Mv&                   rcMvHalf,
   Mv&                   rcMvQter,
   Distortion&           ruiCost
+#if GDR_ENABLED
+  , bool&                rbCleanCandExist
+#endif
 )
 {
 
@@ -4339,7 +6019,11 @@ void InterSearch::xPatternSearchFracDIF(
     m_pcRdCost->setCostScale(0);
     xExtDIFUpSamplingH(&cPatternRoi, cStruct.useAltHpelIf);
     rcMvQter = rcMvInt;   rcMvQter <<= 2;    // for mv-cost
+#if GDR_ENABLED 
+    ruiCost = xPatternRefinement(pu, eRefPicList, iRefIdx, cStruct.pcPatternKey, baseRefMv, 1, rcMvQter, !pu.cs->slice->getDisableSATDForRD(), rbCleanCandExist);
+#else
     ruiCost = xPatternRefinement(cStruct.pcPatternKey, baseRefMv, 1, rcMvQter, !pu.cs->slice->getDisableSATDForRD());
+#endif
     return;
   }
 
@@ -4358,7 +6042,11 @@ void InterSearch::xPatternSearchFracDIF(
 
   rcMvHalf = rcMvInt;   rcMvHalf <<= 1;    // for mv-cost
   Mv baseRefMv(0, 0);
+#if GDR_ENABLED
+  ruiCost = xPatternRefinement(pu, eRefPicList, iRefIdx, cStruct.pcPatternKey, baseRefMv, 2, rcMvHalf, (!pu.cs->slice->getDisableSATDForRD()), rbCleanCandExist);
+#else
   ruiCost = xPatternRefinement(cStruct.pcPatternKey, baseRefMv, 2, rcMvHalf, (!pu.cs->slice->getDisableSATDForRD()));
+#endif
 
   //  quarter-pel refinement
   if (cStruct.imvShift == IMV_OFF)
@@ -4370,9 +6058,11 @@ void InterSearch::xPatternSearchFracDIF(
 
     rcMvQter = rcMvInt;
     rcMvQter <<= 1;   // for mv-cost
-    rcMvQter += rcMvHalf;
-    rcMvQter <<= 1;
+#if GDR_ENABLED
+    ruiCost = xPatternRefinement(pu, eRefPicList, iRefIdx, cStruct.pcPatternKey, baseRefMv, 1, rcMvQter, (!pu.cs->slice->getDisableSATDForRD()), rbCleanCandExist);
+#else
     ruiCost = xPatternRefinement(cStruct.pcPatternKey, baseRefMv, 1, rcMvQter, (!pu.cs->slice->getDisableSATDForRD()));
+#endif
   }
 }
 
@@ -4428,9 +6118,20 @@ Distortion InterSearch::xGetSymmetricCost( PredictionUnit& pu, PelUnitBuf& origB
   return(cost);
 }
 
+#if GDR_ENABLED
+Distortion InterSearch::xSymmeticRefineMvSearch( PredictionUnit &pu, PelUnitBuf& origBuf, Mv& rcMvCurPred, Mv& rcMvTarPred
+  , RefPicList eRefPicList, MvField& rCurMvField, MvField& rTarMvField, Distortion uiMinCost, int SearchPattern, int nSearchStepShift, uint32_t uiMaxSearchRounds, int bcwIdx, bool& rOk)
+#else
 Distortion InterSearch::xSymmeticRefineMvSearch( PredictionUnit &pu, PelUnitBuf& origBuf, Mv& rcMvCurPred, Mv& rcMvTarPred
   , RefPicList eRefPicList, MvField& rCurMvField, MvField& rTarMvField, Distortion uiMinCost, int SearchPattern, int nSearchStepShift, uint32_t uiMaxSearchRounds, int bcwIdx )
+#endif
 {
+#if GDR_ENABLED  
+  const CodingStructure &cs = *pu.cs;
+  const bool isEncodeClean = cs.pcv->isEncoder && ((cs.picHeader->getInGdrPeriod() && cs.isClean(pu.Y().topRight(), CHANNEL_TYPE_LUMA)) || (cs.picHeader->getNumVerVirtualBoundaries() == 0));
+  bool uiCostOk;
+  bool uiMinCostOk = rOk;
+#endif
   const Mv mvSearchOffsetCross[4] = { Mv( 0 , 1 ) , Mv( 1 , 0 ) , Mv( 0 , -1 ) , Mv( -1 ,  0 ) };
   const Mv mvSearchOffsetSquare[8] = { Mv( -1 , 1 ) , Mv( 0 , 1 ) , Mv( 1 ,  1 ) , Mv( 1 ,  0 ) , Mv( 1 , -1 ) , Mv( 0 , -1 ) , Mv( -1 , -1 ) , Mv( -1 , 0 ) };
   const Mv mvSearchOffsetDiamond[8] = { Mv( 0 , 2 ) , Mv( 1 , 1 ) , Mv( 2 ,  0 ) , Mv( 1 , -1 ) , Mv( 0 , -2 ) , Mv( -1 , -1 ) , Mv( -2 ,  0 ) , Mv( -1 , 1 ) };
@@ -4506,6 +6207,10 @@ Distortion InterSearch::xSymmeticRefineMvSearch( PredictionUnit &pu, PelUnitBuf&
       uint32_t uiMvBits = m_pcRdCost->getBitsOfVectorWithPredictor( mv.getHor(), mv.getVer(), 0 );
       Distortion uiCost = m_pcRdCost->getCost( uiMvBits );
 
+#if GDR_ENABLED  
+      uiCostOk = cs.isClean(pu.Y().bottomRight(), mvCand.mv, eRefPicList, mvCand.refIdx);
+#endif
+
       // get MVD pair and set target MV
       mvPair.refIdx = rTarMvField.refIdx;
       mvPair.mv.set( rcMvTarPred.hor - (mvCand.mv.hor - rcMvCurPred.hor), rcMvTarPred.ver - (mvCand.mv.ver - rcMvCurPred.ver) );
@@ -4515,12 +6220,33 @@ Distortion InterSearch::xSymmeticRefineMvSearch( PredictionUnit &pu, PelUnitBuf&
           continue; // Skip this this pos
       }
       uiCost += xGetSymmetricCost( pu, origBuf, eRefPicList, mvCand, mvPair, bcwIdx );
+
+#if GDR_ENABLED  
+      bool allOk = (uiCost < uiMinCost);
+      if (isEncodeClean)
+      {
+        bool curValid = cs.isClean(pu.Y().bottomRight(), mvCand.mv, (RefPicList)(eRefPicList), mvCand.refIdx);
+        bool tarValid = cs.isClean(pu.Y().bottomRight(), mvPair.mv, (RefPicList)(1 - eRefPicList), mvPair.refIdx);
+        allOk = curValid && tarValid;
+      }
+#endif
+
+#if GDR_ENABLED  
+      if (allOk)
+#else
       if ( uiCost < uiMinCost )
+#endif
       {
         uiMinCost = uiCost;
         rCurMvField = mvCand;
         rTarMvField = mvPair;
         nBestDirect = nDirect;
+#if GDR_ENABLED          
+        if (isEncodeClean)
+        {
+          uiMinCostOk = uiCostOk;
+        }
+#endif        
       }
     }
 
@@ -4537,11 +6263,19 @@ Distortion InterSearch::xSymmeticRefineMvSearch( PredictionUnit &pu, PelUnitBuf&
     nDirectEnd = nBestDirect + nStep;
   }
 
+#if GDR_ENABLED   
+  rOk = uiMinCostOk;
+#endif
+
   return(uiMinCost);
 }
 
 
+#if GDR_ENABLED  
+bool InterSearch::xSymmetricMotionEstimation(PredictionUnit& pu, PelUnitBuf& origBuf, Mv& rcMvCurPred, Mv& rcMvTarPred, RefPicList eRefPicList, MvField& rCurMvField, MvField& rTarMvField, Distortion& ruiCost, int bcwIdx, bool& ruiCostOk)
+#else
 void InterSearch::xSymmetricMotionEstimation( PredictionUnit& pu, PelUnitBuf& origBuf, Mv& rcMvCurPred, Mv& rcMvTarPred, RefPicList eRefPicList, MvField& rCurMvField, MvField& rTarMvField, Distortion& ruiCost, int bcwIdx )
+#endif
 {
   // Refine Search
   int nSearchStepShift = MV_FRACTIONAL_BITS_DIFF;
@@ -4551,8 +6285,17 @@ void InterSearch::xSymmetricMotionEstimation( PredictionUnit& pu, PelUnitBuf& or
   nSearchStepShift += pu.cu->imv == IMV_HPEL ? 1 : (pu.cu->imv << 1);
   nDiamondRound >>= pu.cu->imv;
 
+#if GDR_ENABLED  
+  ruiCost = xSymmeticRefineMvSearch(pu, origBuf, rcMvCurPred, rcMvTarPred, eRefPicList, rCurMvField, rTarMvField, ruiCost, 2, nSearchStepShift, nDiamondRound, bcwIdx, ruiCostOk);
+  ruiCost = xSymmeticRefineMvSearch(pu, origBuf, rcMvCurPred, rcMvTarPred, eRefPicList, rCurMvField, rTarMvField, ruiCost, 0, nSearchStepShift, nCrossRound, bcwIdx, ruiCostOk);
+#else
   ruiCost = xSymmeticRefineMvSearch( pu, origBuf, rcMvCurPred, rcMvTarPred, eRefPicList, rCurMvField, rTarMvField, ruiCost, 2, nSearchStepShift, nDiamondRound, bcwIdx );
   ruiCost = xSymmeticRefineMvSearch( pu, origBuf, rcMvCurPred, rcMvTarPred, eRefPicList, rCurMvField, rTarMvField, ruiCost, 0, nSearchStepShift, nCrossRound, bcwIdx );
+#endif
+
+#if GDR_ENABLED  
+  return ruiCostOk;
+#endif
 }
 
 void InterSearch::xPredAffineInterSearch( PredictionUnit&       pu,
@@ -4561,7 +6304,13 @@ void InterSearch::xPredAffineInterSearch( PredictionUnit&       pu,
                                           uint32_t&                 lastMode,
                                           Distortion&           affineCost,
                                           Mv                    hevcMv[2][33]
+#if GDR_ENABLED
+                                        , bool                  hevcMvSolid[2][33]
+#endif
                                         , Mv                    mvAffine4Para[2][33][3]
+#if GDR_ENABLED
+                                        , bool                  mvAffine4ParaSolid[2][33][3]
+#endif
                                         , int                   refIdx4Para[2]
                                         , uint8_t               bcwIdx
                                         , bool                  enforceBcwPred
@@ -4589,6 +6338,27 @@ void InterSearch::xPredAffineInterSearch( PredictionUnit&       pu,
   int       aaiMvpIdx[2][33];
   int       aaiMvpNum[2][33];
 
+#if GDR_ENABLED
+  bool      aacMvSolid[2][3];
+  bool      aacMvValid[2][3];
+
+  bool      cMvTempSolid[2][33][3];
+  bool      cMvTempValid[2][33][3];
+
+  bool      cMvBiSolid[2][3];
+  bool      cMvBiValid[2][3];
+
+  bool      cMvPredSolid[2][33][3];
+  bool      cMvPredBiSolid[2][33][3];
+
+  bool      mvValidList1Solid[3];
+  bool      mvValidList1Valid[3];
+
+  bool      mvHevcSolid[3];
+
+  const CodingStructure &cs = *pu.cs;
+  const bool isEncodeClean = cs.pcv->isEncoder && ((cs.picHeader->getInGdrPeriod() && cs.isClean(pu.Y().topRight(), CHANNEL_TYPE_LUMA)) || (cs.picHeader->getNumVerVirtualBoundaries() == 0));
+#endif
   AffineAMVPInfo aacAffineAMVPInfo[2][33];
   AffineAMVPInfo affiAMVPInfoTemp[2];
 
@@ -4603,9 +6373,67 @@ void InterSearch::xPredAffineInterSearch( PredictionUnit&       pu,
   int           bestBiPMvpL1 = 0;
   Distortion biPDistTemp = std::numeric_limits<Distortion>::max();
 
+#if GDR_ENABLED
+  bool init_value = true;
+
+  bool          allOk = true;
+  bool          biPDistTempOk = init_value;
+  bool          bestBiPDistOk = init_value;
+
+
+  // if (isEncodeClean) 
+  {
+    memset(mvHevcSolid, init_value, sizeof(mvHevcSolid));
+
+    // note : will have Solid problem if initialize to true
+    memset(aacMvSolid, false, sizeof(aacMvSolid));
+    memset(aacMvValid, false, sizeof(aacMvValid));
+
+    memset(cMvBiSolid, init_value, sizeof(cMvBiSolid));
+    memset(cMvBiValid, init_value, sizeof(cMvBiValid));
+
+    memset(cMvTempSolid, init_value, sizeof(cMvTempSolid));
+    memset(cMvTempValid, init_value, sizeof(cMvTempValid));
+
+    memset(mvValidList1Solid, init_value, sizeof(mvValidList1Solid));
+    memset(mvValidList1Valid, init_value, sizeof(mvValidList1Valid));
+
+    // AffineAMVPInfo aacAffineAMVPInfo[2][33];
+    ::memset(aacAffineAMVPInfo, 0, sizeof(aacAffineAMVPInfo));
+    ::memset(affiAMVPInfoTemp, 0, sizeof(affiAMVPInfoTemp));
+
+    for (int i = 0; i < 2; i++)
+    {
+      for (int j = 0; j < 33; j++)
+      {
+        for (int k = 0; k < AMVP_MAX_NUM_CANDS_MEM; k++)
+        {
+          aacAffineAMVPInfo[i][j].mvSolidLT[k] = init_value;
+          aacAffineAMVPInfo[i][j].mvSolidRT[k] = init_value;
+          aacAffineAMVPInfo[i][j].mvSolidLB[k] = init_value;
+        }
+      }
+
+      for (int k = 0; k < AMVP_MAX_NUM_CANDS_MEM; k++)
+      {
+        affiAMVPInfoTemp[i].mvSolidLT[k] = init_value;
+        affiAMVPInfoTemp[i].mvSolidRT[k] = init_value;
+        affiAMVPInfoTemp[i].mvSolidLB[k] = init_value;
+      }
+    }
+  }
+
+  bool bAnyClean = false;
+#endif
+
   Distortion    uiCost[2] = { std::numeric_limits<Distortion>::max(), std::numeric_limits<Distortion>::max() };
   Distortion    uiCostBi  = std::numeric_limits<Distortion>::max();
   Distortion    uiCostTemp;
+#if GDR_ENABLED      
+  bool uiCostOk[2] = { init_value, init_value };
+  bool uiCostBiOk = init_value;
+  bool uiCostTempOk = init_value;
+#endif
 
   uint32_t          uiBits[3] = { 0 };
   uint32_t          uiBitsTemp;
@@ -4616,12 +6444,24 @@ void InterSearch::xPredAffineInterSearch( PredictionUnit&       pu,
   {
     uiCostTempL0[iNumRef] = std::numeric_limits<Distortion>::max();
   }
+
+#if GDR_ENABLED
+  bool uiCostTempL0Ok[MAX_NUM_REF];
+  for (int iNumRef = 0; iNumRef < MAX_NUM_REF; iNumRef++)
+  {
+    uiCostTempL0Ok[iNumRef] = true;
+  }
+#endif
+
   uint32_t uiBitsTempL0[MAX_NUM_REF];
 
   Mv            mvValidList1[4];
   int           refIdxValidList1 = 0;
   uint32_t          bitsValidList1 = MAX_UINT;
   Distortion costValidList1 = std::numeric_limits<Distortion>::max();
+#if GDR_ENABLED   
+  bool costValidList1Ok = true;
+#endif
   Mv            mvHevc[3];
   const bool affineAmvrEnabled = pu.cu->slice->getSPS()->getAffineAmvrEnabledFlag();
   int tryBipred = 0;
@@ -4663,6 +6503,21 @@ void InterSearch::xPredAffineInterSearch( PredictionUnit&       pu,
       }
       aaiMvpIdx[iRefList][iRefIdxTemp] = pu.mvpIdx[eRefPicList];
       aaiMvpNum[iRefList][iRefIdxTemp] = pu.mvpNum[eRefPicList];;
+
+#if GDR_ENABLED      
+      if (isEncodeClean)
+      {
+        int mvpIdx = aaiMvpIdx[iRefList][iRefIdxTemp];
+        cMvPredSolid[iRefList][iRefIdxTemp][0] = affiAMVPInfoTemp[eRefPicList].mvSolidLT[mvpIdx];
+        cMvPredSolid[iRefList][iRefIdxTemp][1] = affiAMVPInfoTemp[eRefPicList].mvSolidRT[mvpIdx];
+        cMvPredSolid[iRefList][iRefIdxTemp][2] = affiAMVPInfoTemp[eRefPicList].mvSolidLB[mvpIdx];
+
+        biPDistTempOk = true;
+        biPDistTempOk = biPDistTempOk && affiAMVPInfoTemp[eRefPicList].mvSolidLT[mvpIdx] && affiAMVPInfoTemp[eRefPicList].mvSolidRT[mvpIdx];
+        biPDistTempOk = biPDistTempOk && ((mvNum > 2) ? affiAMVPInfoTemp[eRefPicList].mvSolidLB[mvpIdx] : true);
+      }
+#endif
+
       if ( pu.cu->affineType == AFFINEMODEL_6PARAM && refIdx4Para[iRefList] != iRefIdxTemp )
       {
         xCopyAffineAMVPInfo( affiAMVPInfoTemp[eRefPicList], aacAffineAMVPInfo[iRefList][iRefIdxTemp] );
@@ -4674,11 +6529,26 @@ void InterSearch::xPredAffineInterSearch( PredictionUnit&       pu,
       {
         mvHevc[i] = hevcMv[iRefList][iRefIdxTemp];
         mvHevc[i].roundAffinePrecInternal2Amvr(pu.cu->imv);
+
+#if GDR_ENABLED     
+        if (isEncodeClean)
+        {
+          mvHevcSolid[i] = hevcMvSolid[iRefList][iRefIdxTemp];
+        }
+#endif
       }
       PelUnitBuf predBuf = m_tmpStorageLCU.getBuf( UnitAreaRelative(*pu.cu, pu) );
+#if GDR_ENABLED 
+      bool uiCandCostOk = true;
+      Distortion uiCandCost = xGetAffineTemplateCost(pu, origBuf, predBuf, mvHevc, aaiMvpIdx[iRefList][iRefIdxTemp],
+        AMVP_MAX_NUM_CANDS, eRefPicList, iRefIdxTemp, uiCandCostOk);
 
+      uiCandCostOk = uiCandCostOk && mvHevcSolid[0] && mvHevcSolid[1] && ((mvNum > 2) ? mvHevcSolid[2] : true);
+
+#else
       Distortion uiCandCost = xGetAffineTemplateCost(pu, origBuf, predBuf, mvHevc, aaiMvpIdx[iRefList][iRefIdxTemp],
                                                      AMVP_MAX_NUM_CANDS, eRefPicList, iRefIdxTemp);
+#endif
 
       if ( affineAmvrEnabled )
       {
@@ -4693,19 +6563,58 @@ void InterSearch::xPredAffineInterSearch( PredictionUnit&       pu,
       if ( savedParaAvail )
       {
         Mv mvFour[3];
+#if GDR_ENABLED  
+        bool mvFourSolid[3] = { true, true, true };
+#endif
         for ( int i = 0; i < mvNum; i++ )
         {
           mvFour[i] = affine4Para ? m_affineMotion.acMvAffine4Para[iRefList][i] : m_affineMotion.acMvAffine6Para[iRefList][i];
           mvFour[i].roundAffinePrecInternal2Amvr(pu.cu->imv);
+#if GDR_ENABLED  
+          mvFourSolid[i] = affine4Para ? m_affineMotion.acMvAffine4ParaSolid[iRefList][i] : m_affineMotion.acMvAffine6ParaSolid[iRefList][i];
+#endif
         }
 
+#if GDR_ENABLED
+        bool candCostInheritOk = true;
+        Distortion candCostInherit = xGetAffineTemplateCost(pu, origBuf, predBuf, mvFour, aaiMvpIdx[iRefList][iRefIdxTemp], AMVP_MAX_NUM_CANDS, eRefPicList, iRefIdxTemp, candCostInheritOk);
+
+        candCostInheritOk = candCostInheritOk && mvFourSolid[0] && mvFourSolid[1] && ((mvNum > 2) ? mvFourSolid[2] : true);
+#else
         Distortion candCostInherit = xGetAffineTemplateCost( pu, origBuf, predBuf, mvFour, aaiMvpIdx[iRefList][iRefIdxTemp], AMVP_MAX_NUM_CANDS, eRefPicList, iRefIdxTemp );
+#endif
         candCostInherit += m_pcRdCost->getCost( xCalcAffineMVBits( pu, mvFour, cMvPred[iRefList][iRefIdxTemp] ) );
 
+#if GDR_ENABLED    
+        allOk = (candCostInherit < uiCandCost);
+        if (isEncodeClean)
+        {
+          if (candCostInheritOk)
+          {
+            allOk = (uiCandCostOk) ? (candCostInherit < uiCandCost) : true;
+          }
+          else
+          {
+            allOk = false;
+          }
+        }
+#endif
+
+#if GDR_ENABLED    
+        if (allOk)
+#else
         if ( candCostInherit < uiCandCost )
+#endif
         {
           uiCandCost = candCostInherit;
           memcpy( mvHevc, mvFour, 3 * sizeof( Mv ) );
+#if GDR_ENABLED  
+          if (isEncodeClean)
+          {
+            uiCandCostOk = candCostInheritOk;
+            memcpy(mvHevcSolid, mvFourSolid, 3 * sizeof(bool));
+          }
+#endif
         }
       }
 
@@ -4717,6 +6626,10 @@ void InterSearch::xPredAffineInterSearch( PredictionUnit&       pu,
         for (int i = 0; i < m_affMVListSize; i++)
         {
           AffineMVInfo *mvInfo = m_affMVList + ((m_affMVListIdx - i - 1 + m_affMVListMaxSize) % (m_affMVListMaxSize));
+#if GDR_ENABLED    
+          AffineMVInfoSolid *mvInfoSolid = m_affMVListSolid + ((m_affMVListIdx - i - 1 + m_affMVListMaxSize) % (m_affMVListMaxSize));
+#endif
+
           //check;
           int j = 0;
           for (; j < i; j++)
@@ -4737,6 +6650,12 @@ void InterSearch::xPredAffineInterSearch( PredictionUnit&       pu,
           }
 
           Mv mvTmp[3], *nbMv = mvInfo->affMVs[iRefList][iRefIdxTemp];
+#if GDR_ENABLED
+          bool mvTmpSolid[3];
+          bool *nbMvSolid = mvInfoSolid->affMVsSolid[iRefList][iRefIdxTemp];
+          mvTmpSolid[0] = nbMvSolid[0];
+          mvTmpSolid[1] = nbMvSolid[1];
+#endif
           int vx, vy;
           int dMvHorX, dMvHorY, dMvVerX, dMvVerY;
           int mvScaleHor = nbMv[0].getHor() << shift;
@@ -4761,15 +6680,48 @@ void InterSearch::xPredAffineInterSearch( PredictionUnit&       pu,
           clipMv( mvTmp[1], pu.cu->lumaPos(), pu.cu->lumaSize(), *pu.cs->sps, *pu.cs->pps );
           mvTmp[0].roundAffinePrecInternal2Amvr(pu.cu->imv);
           mvTmp[1].roundAffinePrecInternal2Amvr(pu.cu->imv);
+
+#if GDR_ENABLED
+          bool tmpCostOk = true;
+          Distortion tmpCost = xGetAffineTemplateCost(pu, origBuf, predBuf, mvTmp, aaiMvpIdx[iRefList][iRefIdxTemp], AMVP_MAX_NUM_CANDS, eRefPicList, iRefIdxTemp, tmpCostOk);
+          tmpCostOk = tmpCostOk && mvTmpSolid[0] && mvTmpSolid[1];
+#else
           Distortion tmpCost = xGetAffineTemplateCost(pu, origBuf, predBuf, mvTmp, aaiMvpIdx[iRefList][iRefIdxTemp], AMVP_MAX_NUM_CANDS, eRefPicList, iRefIdxTemp);
+#endif
           if ( affineAmvrEnabled )
           {
             tmpCost += m_pcRdCost->getCost( xCalcAffineMVBits( pu, mvTmp, cMvPred[iRefList][iRefIdxTemp] ) );
           }
+#if GDR_ENABLED    
+          allOk = (tmpCost < uiCandCost);
+          if (isEncodeClean)
+          {
+            if (tmpCostOk)
+            {
+              allOk = (uiCandCostOk) ? (tmpCost < uiCandCost) : true;
+            }
+            else
+            {
+              allOk = false;
+            }
+          }
+#endif
+
+#if GDR_ENABLED 
+          if (allOk)
+#else
           if (tmpCost < uiCandCost)
+#endif
           {
             uiCandCost = tmpCost;
             std::memcpy(mvHevc, mvTmp, 3 * sizeof(Mv));
+#if GDR_ENABLED 
+            if (isEncodeClean)
+            {
+              uiCandCostOk = tmpCostOk;
+              std::memset(mvHevcSolid, true, 3 * sizeof(bool));
+            }
+#endif
           }
         }
       }
@@ -4778,6 +6730,12 @@ void InterSearch::xPredAffineInterSearch( PredictionUnit&       pu,
         Mv mvFour[3];
         mvFour[0] = mvAffine4Para[iRefList][iRefIdxTemp][0];
         mvFour[1] = mvAffine4Para[iRefList][iRefIdxTemp][1];
+#if GDR_ENABLED
+        bool mvFourSolid[3];
+        mvFourSolid[0] = mvAffine4ParaSolid[iRefList][iRefIdxTemp][0];
+        mvFourSolid[1] = mvAffine4ParaSolid[iRefList][iRefIdxTemp][1];
+#endif
+
         mvAffine4Para[iRefList][iRefIdxTemp][0].roundAffinePrecInternal2Amvr(pu.cu->imv);
         mvAffine4Para[iRefList][iRefIdxTemp][1].roundAffinePrecInternal2Amvr(pu.cu->imv);
 
@@ -4793,36 +6751,134 @@ void InterSearch::xPredAffineInterSearch( PredictionUnit&       pu,
         mvFour[0].roundAffinePrecInternal2Amvr(pu.cu->imv);
         mvFour[1].roundAffinePrecInternal2Amvr(pu.cu->imv);
         mvFour[2].roundAffinePrecInternal2Amvr(pu.cu->imv);
+
+#if GDR_ENABLED
+        bool uiCandCostInheritOk = true;
+        Distortion uiCandCostInherit = xGetAffineTemplateCost(pu, origBuf, predBuf, mvFour, aaiMvpIdx[iRefList][iRefIdxTemp], AMVP_MAX_NUM_CANDS, eRefPicList, iRefIdxTemp, uiCandCostInheritOk);
+        uiCandCostInheritOk = uiCandCostInheritOk && mvFourSolid[0] && mvFourSolid[1];
+#else
         Distortion uiCandCostInherit = xGetAffineTemplateCost( pu, origBuf, predBuf, mvFour, aaiMvpIdx[iRefList][iRefIdxTemp], AMVP_MAX_NUM_CANDS, eRefPicList, iRefIdxTemp );
+#endif
+
         if ( affineAmvrEnabled )
         {
           uiCandCostInherit += m_pcRdCost->getCost( xCalcAffineMVBits( pu, mvFour, cMvPred[iRefList][iRefIdxTemp] ) );
         }
+#if GDR_ENABLED    
+        allOk = (uiCandCostInherit < uiCandCost);
+
+        if (isEncodeClean)
+        {
+          if (uiCandCostInheritOk)
+          {
+            allOk = (uiCandCostOk) ? (uiCandCostInherit < uiCandCost) : true;
+          }
+          else
+          {
+            allOk = false;
+          }
+        }
+#endif
+
+#if GDR_ENABLED  
+        if (allOk)
+#else
         if ( uiCandCostInherit < uiCandCost )
+#endif
         {
           uiCandCost = uiCandCostInherit;
           for ( int i = 0; i < 3; i++ )
           {
             mvHevc[i] = mvFour[i];
+#if GDR_ENABLED  
+            if (isEncodeClean)
+            {
+              uiCandCostOk = uiCandCostInheritOk;
+              mvHevcSolid[i] = true;
+            }
+#endif
           }
         }
       }
 
+
+#if GDR_ENABLED    
+      allOk = (uiCandCost < biPDistTemp);
+
+      if (isEncodeClean)
+      {
+        if (uiCandCostOk)
+        {
+          allOk = (biPDistTempOk) ? (uiCandCost < biPDistTemp) : true;
+        }
+        else
+        {
+          allOk = false;
+        }
+      }
+#endif
+
+#if GDR_ENABLED  
+      if (allOk)
+#else
       if ( uiCandCost < biPDistTemp )
+#endif
       {
         ::memcpy( cMvTemp[iRefList][iRefIdxTemp], mvHevc, sizeof(Mv)*3 );
+#if GDR_ENABLED     
+        if (isEncodeClean)
+        {
+          cMvTempSolid[iRefList][iRefIdxTemp][0] = mvHevcSolid[0];
+          cMvTempSolid[iRefList][iRefIdxTemp][1] = mvHevcSolid[1];
+          cMvTempSolid[iRefList][iRefIdxTemp][2] = mvHevcSolid[2];
+        }
+#endif        
       }
       else
       {
         ::memcpy( cMvTemp[iRefList][iRefIdxTemp], cMvPred[iRefList][iRefIdxTemp], sizeof(Mv)*3 );
+#if GDR_ENABLED     
+        if (isEncodeClean)
+        {
+          cMvTempSolid[iRefList][iRefIdxTemp][0] = cMvPredSolid[iRefList][iRefIdxTemp][0];
+          cMvTempSolid[iRefList][iRefIdxTemp][1] = cMvPredSolid[iRefList][iRefIdxTemp][1];
+          cMvTempSolid[iRefList][iRefIdxTemp][2] = cMvPredSolid[iRefList][iRefIdxTemp][2];
+        }
+#endif        
       }
 
       // GPB list 1, save the best MvpIdx, RefIdx and Cost
+#if GDR_ENABLED          
+      allOk = (slice.getPicHeader()->getMvdL1ZeroFlag() && iRefList == 1 && (biPDistTemp < bestBiPDist));
+
+      if (isEncodeClean)
+      {
+        if (biPDistTempOk)
+        {
+          allOk = (bestBiPDistOk) ? (slice.getPicHeader()->getMvdL1ZeroFlag() && iRefList == 1 && (biPDistTemp < bestBiPDist)) : true;
+        }
+        else
+        {
+          allOk = false;
+        }
+      }
+#endif
+
+#if GDR_ENABLED  
+      if (allOk)
+#else
       if ( slice.getPicHeader()->getMvdL1ZeroFlag() && iRefList==1 && biPDistTemp < bestBiPDist )
+#endif
       {
         bestBiPDist = biPDistTemp;
         bestBiPMvpL1 = aaiMvpIdx[iRefList][iRefIdxTemp];
         bestBiPRefIdxL1 = iRefIdxTemp;
+#if GDR_ENABLED
+        if (isEncodeClean)
+        {
+          bestBiPDistOk = biPDistTempOk;
+        }
+#endif
       }
 
       // Update bits
@@ -4834,6 +6890,13 @@ void InterSearch::xPredAffineInterSearch( PredictionUnit&       pu,
         {
           int iList1ToList0Idx = slice.getList1IdxToList0Idx( iRefIdxTemp );
           ::memcpy( cMvTemp[1][iRefIdxTemp], cMvTemp[0][iList1ToList0Idx], sizeof(Mv)*3 );
+#if GDR_ENABLED     
+          if (isEncodeClean)
+          {
+            ::memcpy(cMvTempSolid[1][iRefIdxTemp], cMvTempSolid[0][iList1ToList0Idx], sizeof(bool) * 3);
+            uiCostTempOk = uiCostTempL0Ok[iList1ToList0Idx];
+          }
+#endif
           uiCostTemp = uiCostTempL0[iList1ToList0Idx];
 
           uiCostTemp -= m_pcRdCost->getCost( uiBitsTempL0[iList1ToList0Idx] );
@@ -4844,52 +6907,249 @@ void InterSearch::xPredAffineInterSearch( PredictionUnit&       pu,
         }
         else
         {
+#if GDR_ENABLED          
+          xAffineMotionEstimation(pu, origBuf, eRefPicList, cMvPred[iRefList][iRefIdxTemp], iRefIdxTemp, cMvTemp[iRefList][iRefIdxTemp], cMvTempSolid[iRefList][iRefIdxTemp], uiBitsTemp, uiCostTemp
+            , aaiMvpIdx[iRefList][iRefIdxTemp], affiAMVPInfoTemp[eRefPicList], bAnyClean
+#else
           xAffineMotionEstimation( pu, origBuf, eRefPicList, cMvPred[iRefList][iRefIdxTemp], iRefIdxTemp, cMvTemp[iRefList][iRefIdxTemp], uiBitsTemp, uiCostTemp
                                    , aaiMvpIdx[iRefList][iRefIdxTemp], affiAMVPInfoTemp[eRefPicList]
+#endif
           );
+
+#if GDR_ENABLED      
+          if (isEncodeClean)
+          {
+            int mvp_idx = aaiMvpIdx[iRefList][iRefIdxTemp];
+            PelUnitBuf     tmpBuf = m_tmpAffiStorage.getBuf(UnitAreaRelative(*pu.cu, pu));
+            const Picture *refPic = pu.cu->slice->getRefPic((RefPicList)iRefList, iRefIdxTemp);
+
+
+            cMvPredSolid[iRefList][iRefIdxTemp][0] = affiAMVPInfoTemp[eRefPicList].mvSolidLT[mvp_idx];
+            cMvPredSolid[iRefList][iRefIdxTemp][1] = affiAMVPInfoTemp[eRefPicList].mvSolidRT[mvp_idx];
+            cMvPredSolid[iRefList][iRefIdxTemp][2] = affiAMVPInfoTemp[eRefPicList].mvSolidLB[mvp_idx];
+
+            cMvTempSolid[iRefList][iRefIdxTemp][0] = cMvPredSolid[iRefList][iRefIdxTemp][0];
+            cMvTempSolid[iRefList][iRefIdxTemp][1] = cMvPredSolid[iRefList][iRefIdxTemp][1];
+            cMvTempSolid[iRefList][iRefIdxTemp][2] = cMvPredSolid[iRefList][iRefIdxTemp][2];
+
+            bool isSubPuYYClean = xPredAffineBlk(COMPONENT_Y, pu, refPic, cMvTemp[iRefList][iRefIdxTemp], tmpBuf, false, pu.cu->slice->clpRng(COMPONENT_Y));
+            bool isSubPuCbClean = (isSubPuYYClean) ? xPredAffineBlk(COMPONENT_Cb, pu, refPic, cMvTemp[iRefList][iRefIdxTemp], tmpBuf, false, pu.cu->slice->clpRng(COMPONENT_Cb)) : false;
+
+            cMvTempValid[iRefList][iRefIdxTemp][0] = isSubPuYYClean && isSubPuCbClean;
+            cMvTempValid[iRefList][iRefIdxTemp][1] = isSubPuYYClean && isSubPuCbClean;
+            cMvTempValid[iRefList][iRefIdxTemp][2] = isSubPuYYClean && isSubPuCbClean;
+
+            uiCostTempOk = true;
+            uiCostTempOk = uiCostTempOk && cMvPredSolid[iRefList][iRefIdxTemp][0] && cMvPredSolid[iRefList][iRefIdxTemp][1];
+            uiCostTempOk = uiCostTempOk && ((mvNum > 2) ? cMvPredSolid[iRefList][iRefIdxTemp][2] : true);
+            uiCostTempOk = uiCostTempOk && cMvTempSolid[iRefList][iRefIdxTemp][0] && cMvTempSolid[iRefList][iRefIdxTemp][1] && ((mvNum > 2) ? cMvTempSolid[iRefList][iRefIdxTemp][2] : true);
+            uiCostTempOk = uiCostTempOk && cMvTempValid[iRefList][iRefIdxTemp][0] && cMvTempValid[iRefList][iRefIdxTemp][1] && ((mvNum > 2) ? cMvTempValid[iRefList][iRefIdxTemp][2] : true);
+          }
+#endif
         }
       }
       else
       {
+#if GDR_ENABLED  
+        xAffineMotionEstimation(pu, origBuf, eRefPicList, cMvPred[iRefList][iRefIdxTemp], iRefIdxTemp, cMvTemp[iRefList][iRefIdxTemp], cMvTempSolid[iRefList][iRefIdxTemp], uiBitsTemp, uiCostTemp
+          , aaiMvpIdx[iRefList][iRefIdxTemp], affiAMVPInfoTemp[eRefPicList], bAnyClean
+        );
+#else
         xAffineMotionEstimation( pu, origBuf, eRefPicList, cMvPred[iRefList][iRefIdxTemp], iRefIdxTemp, cMvTemp[iRefList][iRefIdxTemp], uiBitsTemp, uiCostTemp
                                  , aaiMvpIdx[iRefList][iRefIdxTemp], affiAMVPInfoTemp[eRefPicList]
         );
+#endif
+
+#if GDR_ENABLED      
+        if (isEncodeClean)
+        {
+          int mvp_idx = aaiMvpIdx[iRefList][iRefIdxTemp];
+          PelUnitBuf     tmpBuf = m_tmpAffiStorage.getBuf(UnitAreaRelative(*pu.cu, pu));
+          const Picture *refPic = pu.cu->slice->getRefPic((RefPicList)iRefList, iRefIdxTemp);
+
+          cMvPredSolid[iRefList][iRefIdxTemp][0] = affiAMVPInfoTemp[eRefPicList].mvSolidLT[mvp_idx];
+          cMvPredSolid[iRefList][iRefIdxTemp][1] = affiAMVPInfoTemp[eRefPicList].mvSolidRT[mvp_idx];
+          cMvPredSolid[iRefList][iRefIdxTemp][2] = affiAMVPInfoTemp[eRefPicList].mvSolidLB[mvp_idx];
+
+          cMvTempSolid[iRefList][iRefIdxTemp][0] = cMvPredSolid[iRefList][iRefIdxTemp][0];
+          cMvTempSolid[iRefList][iRefIdxTemp][1] = cMvPredSolid[iRefList][iRefIdxTemp][1];
+          cMvTempSolid[iRefList][iRefIdxTemp][2] = cMvPredSolid[iRefList][iRefIdxTemp][2];
+
+          bool isSubPuYYClean = xPredAffineBlk(COMPONENT_Y, pu, refPic, cMvTemp[iRefList][iRefIdxTemp], tmpBuf, false, pu.cu->slice->clpRng(COMPONENT_Y));
+          bool isSubPuCbClean = (isSubPuYYClean) ? xPredAffineBlk(COMPONENT_Cb, pu, refPic, cMvTemp[iRefList][iRefIdxTemp], tmpBuf, false, pu.cu->slice->clpRng(COMPONENT_Cb)) : false;
+
+          cMvTempValid[iRefList][iRefIdxTemp][0] = isSubPuYYClean && isSubPuCbClean;
+          cMvTempValid[iRefList][iRefIdxTemp][1] = isSubPuYYClean && isSubPuCbClean;
+          cMvTempValid[iRefList][iRefIdxTemp][2] = isSubPuYYClean && isSubPuCbClean;
+
+          uiCostTempOk = true;
+          uiCostTempOk = uiCostTempOk && cMvPredSolid[iRefList][iRefIdxTemp][0] && cMvPredSolid[iRefList][iRefIdxTemp][1];
+          uiCostTempOk = uiCostTempOk && ((mvNum > 2) ? cMvPredSolid[iRefList][iRefIdxTemp][2] : true);
+          uiCostTempOk = uiCostTempOk && cMvTempSolid[iRefList][iRefIdxTemp][0] && cMvTempSolid[iRefList][iRefIdxTemp][1] && ((mvNum > 2) ? cMvTempSolid[iRefList][iRefIdxTemp][2] : true);
+          uiCostTempOk = uiCostTempOk && cMvTempValid[iRefList][iRefIdxTemp][0] && cMvTempValid[iRefList][iRefIdxTemp][1] && ((mvNum > 2) ? cMvTempValid[iRefList][iRefIdxTemp][2] : true);
+        }
+#endif
       }
       if(pu.cu->cs->sps->getUseBcw() && pu.cu->BcwIdx == BCW_DEFAULT && pu.cu->slice->isInterB())
       {
         m_uniMotions.setReadModeAffine(true, (uint8_t)iRefList, (uint8_t)iRefIdxTemp, pu.cu->affineType);
+#if GDR_ENABLED
+        if (isEncodeClean)
+        {
+          m_uniMotions.copyAffineMvFrom(
+            cMvTemp[iRefList][iRefIdxTemp],
+            cMvTempSolid[iRefList][iRefIdxTemp],
+            uiCostTemp - m_pcRdCost->getCost(uiBitsTemp),
+            (uint8_t)iRefList,
+            (uint8_t)iRefIdxTemp,
+            pu.cu->affineType,
+            aaiMvpIdx[iRefList][iRefIdxTemp]
+          );
+        }
+        else
+        {
+          m_uniMotions.copyAffineMvFrom(cMvTemp[iRefList][iRefIdxTemp], uiCostTemp - m_pcRdCost->getCost(uiBitsTemp), (uint8_t)iRefList, (uint8_t)iRefIdxTemp, pu.cu->affineType
+            , aaiMvpIdx[iRefList][iRefIdxTemp]
+          );
+        }
+#else
         m_uniMotions.copyAffineMvFrom(cMvTemp[iRefList][iRefIdxTemp], uiCostTemp - m_pcRdCost->getCost(uiBitsTemp), (uint8_t)iRefList, (uint8_t)iRefIdxTemp, pu.cu->affineType
                                       , aaiMvpIdx[iRefList][iRefIdxTemp]
         );
+#endif
       }
       // Set best AMVP Index
       xCopyAffineAMVPInfo( affiAMVPInfoTemp[eRefPicList], aacAffineAMVPInfo[iRefList][iRefIdxTemp] );
+#if GDR_ENABLED        
+      if ( pu.cu->imv != 2 || !m_pcEncCfg->getUseAffineAmvrEncOpt() )
+      {
+        xCheckBestAffineMVP( pu, affiAMVPInfoTemp[eRefPicList], eRefPicList, cMvTemp[iRefList][iRefIdxTemp], cMvPred[iRefList][iRefIdxTemp], aaiMvpIdx[iRefList][iRefIdxTemp], uiBitsTemp, uiCostTemp );
+        if (isEncodeClean)
+        {
+          int mvp_idx = aaiMvpIdx[iRefList][iRefIdxTemp];
+
+          cMvPredSolid[iRefList][iRefIdxTemp][0] = affiAMVPInfoTemp[eRefPicList].mvSolidLT[mvp_idx];
+          cMvPredSolid[iRefList][iRefIdxTemp][1] = affiAMVPInfoTemp[eRefPicList].mvSolidRT[mvp_idx];
+          cMvPredSolid[iRefList][iRefIdxTemp][2] = affiAMVPInfoTemp[eRefPicList].mvSolidLB[mvp_idx];
+
+          cMvTempSolid[iRefList][iRefIdxTemp][0] = cMvPredSolid[iRefList][iRefIdxTemp][0];
+          cMvTempSolid[iRefList][iRefIdxTemp][1] = cMvPredSolid[iRefList][iRefIdxTemp][0];
+          cMvTempSolid[iRefList][iRefIdxTemp][2] = cMvPredSolid[iRefList][iRefIdxTemp][0];
+
+          if (cMvTempValid[iRefList][iRefIdxTemp][0] && cMvTempValid[iRefList][iRefIdxTemp][1] && cMvTempValid[iRefList][iRefIdxTemp][2])
+          {
+            cMvTempValid[iRefList][iRefIdxTemp][0] = cMvPredSolid[iRefList][iRefIdxTemp][0];
+            cMvTempValid[iRefList][iRefIdxTemp][1] = cMvPredSolid[iRefList][iRefIdxTemp][0];
+            cMvTempValid[iRefList][iRefIdxTemp][2] = cMvPredSolid[iRefList][iRefIdxTemp][0];
+          }
+
+          uiCostTempOk = true;
+          uiCostTempOk = uiCostTempOk && cMvPredSolid[iRefList][iRefIdxTemp][0] && cMvPredSolid[iRefList][iRefIdxTemp][1];
+          uiCostTempOk = uiCostTempOk && ((mvNum > 2) ? cMvPredSolid[iRefList][iRefIdxTemp][2] : true);
+          uiCostTempOk = uiCostTempOk && cMvTempSolid[iRefList][iRefIdxTemp][0] && cMvTempSolid[iRefList][iRefIdxTemp][1] && ((mvNum > 2) ? cMvTempSolid[iRefList][iRefIdxTemp][2] : true);
+          uiCostTempOk = uiCostTempOk && cMvTempValid[iRefList][iRefIdxTemp][0] && cMvTempValid[iRefList][iRefIdxTemp][1] && ((mvNum > 2) ? cMvTempValid[iRefList][iRefIdxTemp][2] : true);
+        }
+      }
+#else
       if ( pu.cu->imv != 2 || !m_pcEncCfg->getUseAffineAmvrEncOpt() )
-      xCheckBestAffineMVP( pu, affiAMVPInfoTemp[eRefPicList], eRefPicList, cMvTemp[iRefList][iRefIdxTemp], cMvPred[iRefList][iRefIdxTemp], aaiMvpIdx[iRefList][iRefIdxTemp], uiBitsTemp, uiCostTemp );
+      {
+        xCheckBestAffineMVP( pu, affiAMVPInfoTemp[eRefPicList], eRefPicList, cMvTemp[iRefList][iRefIdxTemp], cMvPred[iRefList][iRefIdxTemp], aaiMvpIdx[iRefList][iRefIdxTemp], uiBitsTemp, uiCostTemp );
+      }
+#endif
 
       if ( iRefList == 0 )
       {
         uiCostTempL0[iRefIdxTemp] = uiCostTemp;
         uiBitsTempL0[iRefIdxTemp] = uiBitsTemp;
+#if GDR_ENABLED  
+        if (isEncodeClean)
+        {
+          uiCostTempL0Ok[iRefIdxTemp] = uiCostTempOk;
+        }
+#endif
       }
       DTRACE( g_trace_ctx, D_COMMON, " (%d) uiCostTemp=%d, uiCost[iRefList]=%d\n", DTRACE_GET_COUNTER(g_trace_ctx,D_COMMON), uiCostTemp, uiCost[iRefList] );
+#if GDR_ENABLED  
+      allOk = (uiCostTemp < uiCost[iRefList]);
+
+      if (isEncodeClean)
+      {
+        if (uiCostTempOk)
+        {
+          allOk = (uiCostOk[iRefList]) ? (uiCostTemp < uiCost[iRefList]) : true;
+        }
+        else
+        {
+          allOk = false;
+        }
+      }
+#endif      
+
+#if GDR_ENABLED   
+      if (allOk)
+#else
       if ( uiCostTemp < uiCost[iRefList] )
+#endif
       {
         uiCost[iRefList] = uiCostTemp;
         uiBits[iRefList] = uiBitsTemp; // storing for bi-prediction
 
+#if GDR_ENABLED 
+        if (isEncodeClean)
+        {
+          uiCostOk[iRefList] = uiCostTempOk;
+        }
+#endif      
         // set best motion
         ::memcpy( aacMv[iRefList], cMvTemp[iRefList][iRefIdxTemp], sizeof(Mv) * 3 );
+#if GDR_ENABLED      
+        if (isEncodeClean)
+        {
+          ::memcpy(aacMvSolid[iRefList], cMvTempSolid[iRefList][iRefIdxTemp], sizeof(bool) * 3);
+          ::memcpy(aacMvValid[iRefList], cMvTempValid[iRefList][iRefIdxTemp], sizeof(bool) * 3);
+        }
+#endif
         iRefIdx[iRefList] = iRefIdxTemp;
       }
 
+
+#if GDR_ENABLED  
+      allOk = (iRefList == 1 && uiCostTemp < costValidList1 && slice.getList1IdxToList0Idx(iRefIdxTemp) < 0);
+
+      if (isEncodeClean)
+      {
+        if (uiCostTempOk)
+        {
+          allOk = (costValidList1Ok) ? (iRefList == 1 && uiCostTemp < costValidList1 && slice.getList1IdxToList0Idx(iRefIdxTemp) < 0) : true;
+        }
+        else
+        {
+          allOk = false;
+        }
+      }
+#endif
+
+
+#if GDR_ENABLED   
+      if (allOk)
+#else
       if ( iRefList == 1 && uiCostTemp < costValidList1 && slice.getList1IdxToList0Idx( iRefIdxTemp ) < 0 )
+#endif
       {
         costValidList1 = uiCostTemp;
         bitsValidList1 = uiBitsTemp;
 
         // set motion
         memcpy( mvValidList1, cMvTemp[iRefList][iRefIdxTemp], sizeof(Mv)*3 );
+
+#if GDR_ENABLED      
+        if (isEncodeClean)
+        {
+          costValidList1Ok = uiCostTempOk;
+          ::memcpy(mvValidList1Solid, cMvTempSolid[iRefList][iRefIdxTemp], sizeof(bool) * 3);
+          ::memcpy(mvValidList1Valid, cMvTempSolid[iRefList][iRefIdxTemp], sizeof(bool) * 3);
+        }
+#endif
         refIdxValidList1 = iRefIdxTemp;
       }
     } // End refIdx loop
@@ -4898,9 +7158,15 @@ void InterSearch::xPredAffineInterSearch( PredictionUnit&       pu,
   if ( pu.cu->affineType == AFFINEMODEL_4PARAM )
   {
     ::memcpy( mvAffine4Para, cMvTemp, sizeof( cMvTemp ) );
+#if GDR_ENABLED 
+    ::memcpy(mvAffine4ParaSolid, cMvTempSolid, sizeof(cMvTempSolid));
+#endif
     if ( pu.cu->imv == 0 && ( !pu.cu->cs->sps->getUseBcw() || bcwIdx == BCW_DEFAULT ) )
     {
       AffineMVInfo *affMVInfo = m_affMVList + m_affMVListIdx;
+#if GDR_ENABLED 
+      AffineMVInfoSolid *affMVInfoSolid = m_affMVListSolid + m_affMVListIdx;
+#endif
 
       //check;
       int j = 0;
@@ -4912,10 +7178,19 @@ void InterSearch::xPredAffineInterSearch( PredictionUnit&       pu,
           break;
         }
       }
+#if GDR_ENABLED 
       if (j < m_affMVListSize)
+      {
         affMVInfo = m_affMVList + ((m_affMVListIdx - j - 1 + m_affMVListMaxSize) % (m_affMVListMaxSize));
-
+        affMVInfoSolid = m_affMVListSolid + ((m_affMVListIdx - j - 1 + m_affMVListMaxSize) % (m_affMVListMaxSize));
+      }
       ::memcpy(affMVInfo->affMVs, cMvTemp, sizeof(cMvTemp));
+      ::memcpy(affMVInfoSolid->affMVsSolid, cMvTempSolid, sizeof(cMvTempSolid));
+#else
+      if (j < m_affMVListSize)
+        affMVInfo = m_affMVList + ((m_affMVListIdx - j - 1 + m_affMVListMaxSize) % (m_affMVListMaxSize));
+      ::memcpy(affMVInfo->affMVs, cMvTemp, sizeof(cMvTemp));
+#endif
 
       if (j == m_affMVListSize)
       {
@@ -4943,6 +7218,15 @@ void InterSearch::xPredAffineInterSearch( PredictionUnit&       pu,
     ::memcpy( cMvPredBi,   cMvPred,   sizeof(cMvPred)   );
     ::memcpy( aaiMvpIdxBi, aaiMvpIdx, sizeof(aaiMvpIdx) );
 
+#if GDR_ENABLED
+    if (isEncodeClean)
+    {
+      ::memcpy(cMvBiSolid, aacMvSolid, sizeof(cMvBiSolid));
+      ::memcpy(cMvBiValid, aacMvValid, sizeof(cMvBiValid));
+      ::memcpy(cMvPredBiSolid, cMvPredSolid, sizeof(cMvPredSolid));
+    }
+#endif
+
     uint32_t uiMotBits[2];
     bool doBiPred = true;
 
@@ -4960,6 +7244,33 @@ void InterSearch::xPredAffineInterSearch( PredictionUnit&       pu,
       ::memcpy( cMvBi[1],                      pcMvTemp, sizeof(Mv)*3 );
       ::memcpy( cMvTemp[1][bestBiPRefIdxL1],   pcMvTemp, sizeof(Mv)*3 );
       iRefIdxBi[1] = bestBiPRefIdxL1;
+#if GDR_ENABLED
+      if (isEncodeClean)
+      {
+        PelUnitBuf     tmpBuf = m_tmpAffiStorage.getBuf(UnitAreaRelative(*pu.cu, pu));
+        const Picture *refPic = pu.cu->slice->getRefPic((RefPicList)REF_PIC_LIST_1, iRefIdxBi[1]);
+
+        cMvPredBiSolid[1][bestBiPRefIdxL1][0] = affiAMVPInfoTemp[REF_PIC_LIST_1].mvSolidLT[bestBiPMvpL1];
+        cMvPredBiSolid[1][bestBiPRefIdxL1][1] = affiAMVPInfoTemp[REF_PIC_LIST_1].mvSolidRT[bestBiPMvpL1];
+        cMvPredBiSolid[1][bestBiPRefIdxL1][2] = affiAMVPInfoTemp[REF_PIC_LIST_1].mvSolidLB[bestBiPMvpL1];
+
+        cMvBiSolid[1][0] = affiAMVPInfoTemp[REF_PIC_LIST_1].mvSolidLT[bestBiPMvpL1];
+        cMvBiSolid[1][1] = affiAMVPInfoTemp[REF_PIC_LIST_1].mvSolidRT[bestBiPMvpL1];
+        cMvBiSolid[1][2] = affiAMVPInfoTemp[REF_PIC_LIST_1].mvSolidLB[bestBiPMvpL1];
+
+
+        bool isSubPuYYClean = xPredAffineBlk(COMPONENT_Y, pu, refPic, cMvTemp[1][bestBiPRefIdxL1], tmpBuf, false, pu.cu->slice->clpRng(COMPONENT_Y));
+        bool isSubPuCbClean = (isSubPuYYClean) ? xPredAffineBlk(COMPONENT_Cb, pu, refPic, cMvTemp[1][bestBiPRefIdxL1], tmpBuf, false, pu.cu->slice->clpRng(COMPONENT_Cb)) : false;
+
+        cMvBiValid[1][0] = isSubPuYYClean && isSubPuCbClean;
+        cMvBiValid[1][1] = isSubPuYYClean && isSubPuCbClean;
+        cMvBiValid[1][2] = isSubPuYYClean && isSubPuCbClean;
+
+        cMvTempSolid[1][bestBiPRefIdxL1][0] = affiAMVPInfoTemp[REF_PIC_LIST_1].mvSolidLT[bestBiPMvpL1];
+        cMvTempSolid[1][bestBiPRefIdxL1][1] = affiAMVPInfoTemp[REF_PIC_LIST_1].mvSolidRT[bestBiPMvpL1];
+        cMvTempSolid[1][bestBiPRefIdxL1][2] = affiAMVPInfoTemp[REF_PIC_LIST_1].mvSolidLB[bestBiPMvpL1];
+      }
+#endif
 
       if( m_pcEncCfg->getMCTSEncConstraint() )
       {
@@ -4982,6 +7293,26 @@ void InterSearch::xPredAffineInterSearch( PredictionUnit&       pu,
       PU::setAllAffineMv( pu, cMvBi[1][0], cMvBi[1][1], cMvBi[1][2], REF_PIC_LIST_1);
       pu.refIdx[REF_PIC_LIST_1] = iRefIdxBi[1];
 
+#if GDR_ENABLED
+      if (isEncodeClean)
+      {
+        PelUnitBuf     tmpBuf = m_tmpAffiStorage.getBuf(UnitAreaRelative(*pu.cu, pu));
+        const Picture *refPic = pu.cu->slice->getRefPic((RefPicList)REF_PIC_LIST_1, pu.refIdx[REF_PIC_LIST_1]);
+
+        pu.mvAffiSolid[REF_PIC_LIST_1][0] = cMvBiSolid[REF_PIC_LIST_1][0];
+        pu.mvAffiSolid[REF_PIC_LIST_1][1] = cMvBiSolid[REF_PIC_LIST_1][1];
+        pu.mvAffiSolid[REF_PIC_LIST_1][2] = cMvBiSolid[REF_PIC_LIST_1][2];
+
+
+        bool isSubPuYYClean = xPredAffineBlk(COMPONENT_Y, pu, refPic, cMvBi[1], tmpBuf, false, pu.cu->slice->clpRng(COMPONENT_Y));
+        bool isSubPuCbClean = (isSubPuYYClean) ? xPredAffineBlk(COMPONENT_Cb, pu, refPic, cMvBi[1], tmpBuf, false, pu.cu->slice->clpRng(COMPONENT_Cb)) : false;
+
+        pu.mvAffiValid[REF_PIC_LIST_1][0] = cMvBiValid[REF_PIC_LIST_1][0] = isSubPuYYClean && isSubPuCbClean;
+        pu.mvAffiValid[REF_PIC_LIST_1][1] = cMvBiValid[REF_PIC_LIST_1][1] = isSubPuYYClean && isSubPuCbClean;
+        pu.mvAffiValid[REF_PIC_LIST_1][2] = cMvBiValid[REF_PIC_LIST_1][2] = isSubPuYYClean && isSubPuCbClean;
+      }
+#endif
+
       PelUnitBuf predBufTmp = m_tmpPredStorage[REF_PIC_LIST_1].getBuf( UnitAreaRelative(*pu.cu, pu) );
       motionCompensation( pu, predBufTmp, REF_PIC_LIST_1 );
 
@@ -5023,7 +7354,27 @@ void InterSearch::xPredAffineInterSearch( PredictionUnit&       pu,
       int iRefList = iIter % 2;
       if ( m_pcEncCfg->getFastInterSearchMode()==FASTINTERSEARCH_MODE1 || m_pcEncCfg->getFastInterSearchMode()==FASTINTERSEARCH_MODE2 )
       {
+#if GDR_ENABLED  
+        allOk = (uiCost[0] <= uiCost[1]);
+
+        if (isEncodeClean)
+        {
+          if (uiCostOk[0])
+          {
+            allOk = (uiCostOk[1]) ? (uiCost[0] <= uiCost[1]) : true;
+          }
+          else
+          {
+            allOk = false;
+          }
+        }
+#endif
+
+#if GDR_ENABLED 
+        if (allOk)
+#else
         if( uiCost[0] <= uiCost[1] )
+#endif
         {
           iRefList = 1;
         }
@@ -5046,6 +7397,25 @@ void InterSearch::xPredAffineInterSearch( PredictionUnit&       pu,
       {
         PU::setAllAffineMv( pu, aacMv[1-iRefList][0], aacMv[1-iRefList][1], aacMv[1-iRefList][2], RefPicList(1-iRefList));
         pu.refIdx[1-iRefList] = iRefIdx[1-iRefList];
+#if GDR_ENABLED
+        if (isEncodeClean)
+        {
+          PelUnitBuf     tmpBuf = m_tmpAffiStorage.getBuf(UnitAreaRelative(*pu.cu, pu));
+          const Picture *refPic = pu.cu->slice->getRefPic((RefPicList)(1 - iRefList), pu.refIdx[1 - iRefList]);
+
+          pu.mvAffiSolid[1 - iRefList][0] = aacMvSolid[1 - iRefList][0];
+          pu.mvAffiSolid[1 - iRefList][1] = aacMvSolid[1 - iRefList][1];
+          pu.mvAffiSolid[1 - iRefList][2] = aacMvSolid[1 - iRefList][2];
+
+
+          bool isSubPuYYClean = xPredAffineBlk(COMPONENT_Y, pu, refPic, aacMv[1 - iRefList], tmpBuf, false, pu.cu->slice->clpRng(COMPONENT_Y));
+          bool isSubPuCbClean = (isSubPuYYClean) ? xPredAffineBlk(COMPONENT_Cb, pu, refPic, aacMv[1 - iRefList], tmpBuf, false, pu.cu->slice->clpRng(COMPONENT_Cb)) : false;
+
+          pu.mvAffiValid[1 - iRefList][0] = aacMvValid[1 - iRefList][0] = isSubPuYYClean && isSubPuCbClean;
+          pu.mvAffiValid[1 - iRefList][1] = aacMvValid[1 - iRefList][1] = isSubPuYYClean && isSubPuCbClean;
+          pu.mvAffiValid[1 - iRefList][2] = aacMvValid[1 - iRefList][2] = isSubPuYYClean && isSubPuCbClean;
+        }
+#endif         
 
         PelUnitBuf predBufTmp = m_tmpPredStorage[1 - iRefList].getBuf( UnitAreaRelative(*pu.cu, pu) );
         motionCompensation( pu, predBufTmp, RefPicList(1 - iRefList) );
@@ -5089,24 +7459,129 @@ void InterSearch::xPredAffineInterSearch( PredictionUnit&       pu,
         uiBitsTemp += m_auiMVPIdxCost[aaiMvpIdxBi[iRefList][iRefIdxTemp]][AMVP_MAX_NUM_CANDS];
 
         // call Affine ME
+#if GDR_ENABLED          
+        xAffineMotionEstimation(pu, origBuf, eRefPicList, cMvPredBi[iRefList][iRefIdxTemp], iRefIdxTemp, cMvTemp[iRefList][iRefIdxTemp], cMvTempSolid[iRefList][iRefIdxTemp], uiBitsTemp, uiCostTemp,
+          aaiMvpIdxBi[iRefList][iRefIdxTemp], aacAffineAMVPInfo[iRefList][iRefIdxTemp], bAnyClean,
+          true);
+#else
         xAffineMotionEstimation( pu, origBuf, eRefPicList, cMvPredBi[iRefList][iRefIdxTemp], iRefIdxTemp, cMvTemp[iRefList][iRefIdxTemp], uiBitsTemp, uiCostTemp,
                                  aaiMvpIdxBi[iRefList][iRefIdxTemp], aacAffineAMVPInfo[iRefList][iRefIdxTemp],
           true );
+#endif
+
+#if GDR_ENABLED      
+        if (isEncodeClean)
+        {
+          int mvp_idx = aaiMvpIdx[iRefList][iRefIdxTemp];
+          PelUnitBuf     tmpBuf = m_tmpAffiStorage.getBuf(UnitAreaRelative(*pu.cu, pu));
+          const Picture *refPic = pu.cu->slice->getRefPic((RefPicList)iRefList, iRefIdxTemp);
+
+          cMvPredBiSolid[iRefList][iRefIdxTemp][0] = aacAffineAMVPInfo[iRefList][iRefIdxTemp].mvSolidLT[mvp_idx];
+          cMvPredBiSolid[iRefList][iRefIdxTemp][1] = aacAffineAMVPInfo[iRefList][iRefIdxTemp].mvSolidRT[mvp_idx];
+          cMvPredBiSolid[iRefList][iRefIdxTemp][2] = aacAffineAMVPInfo[iRefList][iRefIdxTemp].mvSolidLB[mvp_idx];
+
+          cMvTempSolid[iRefList][iRefIdxTemp][0] = cMvPredBiSolid[iRefList][iRefIdxTemp][0];
+          cMvTempSolid[iRefList][iRefIdxTemp][1] = cMvPredSolid[iRefList][iRefIdxTemp][1];
+          cMvTempSolid[iRefList][iRefIdxTemp][2] = cMvPredSolid[iRefList][iRefIdxTemp][2];
+
+
+          bool isSubPuYYClean = xPredAffineBlk(COMPONENT_Y, pu, refPic, cMvTemp[iRefList][iRefIdxTemp], tmpBuf, false, pu.cu->slice->clpRng(COMPONENT_Y));
+          bool isSubPuCbClean = (isSubPuYYClean) ? xPredAffineBlk(COMPONENT_Cb, pu, refPic, cMvTemp[iRefList][iRefIdxTemp], tmpBuf, false, pu.cu->slice->clpRng(COMPONENT_Cb)) : false;
+
+          cMvTempValid[iRefList][iRefIdxTemp][0] = isSubPuYYClean && isSubPuCbClean;
+          cMvTempValid[iRefList][iRefIdxTemp][1] = isSubPuYYClean && isSubPuCbClean;
+          cMvTempValid[iRefList][iRefIdxTemp][2] = isSubPuYYClean && isSubPuCbClean;
+
+          uiCostTempOk = true;
+          uiCostTempOk = uiCostTempOk && cMvPredBiSolid[iRefList][iRefIdxTemp][0] && cMvPredBiSolid[iRefList][iRefIdxTemp][1];
+          uiCostTempOk = uiCostTempOk && ((mvNum > 2) ? cMvPredBiSolid[iRefList][iRefIdxTemp][2] : true);
+          uiCostTempOk = uiCostTempOk && cMvTempSolid[iRefList][iRefIdxTemp][0] && cMvTempSolid[iRefList][iRefIdxTemp][1] && ((mvNum > 2) ? cMvTempSolid[iRefList][iRefIdxTemp][2] : true);
+          uiCostTempOk = uiCostTempOk && cMvTempValid[iRefList][iRefIdxTemp][0] && cMvTempValid[iRefList][iRefIdxTemp][1] && ((mvNum > 2) ? cMvTempValid[iRefList][iRefIdxTemp][2] : true);
+        }
+#endif
+
         xCopyAffineAMVPInfo( aacAffineAMVPInfo[iRefList][iRefIdxTemp], affiAMVPInfoTemp[eRefPicList] );
+#if GDR_ENABLED            
+        if ( pu.cu->imv != 2 || !m_pcEncCfg->getUseAffineAmvrEncOpt() )
+        {
+          xCheckBestAffineMVP( pu, affiAMVPInfoTemp[eRefPicList], eRefPicList, cMvTemp[iRefList][iRefIdxTemp], cMvPredBi[iRefList][iRefIdxTemp], aaiMvpIdxBi[iRefList][iRefIdxTemp], uiBitsTemp, uiCostTemp );
+          if (isEncodeClean)
+          {
+            int mvp_idx = aaiMvpIdxBi[iRefList][iRefIdxTemp];
+
+            cMvPredBiSolid[iRefList][iRefIdxTemp][0] = affiAMVPInfoTemp[eRefPicList].mvSolidLT[mvp_idx];
+            cMvPredBiSolid[iRefList][iRefIdxTemp][1] = affiAMVPInfoTemp[eRefPicList].mvSolidRT[mvp_idx];
+            cMvPredBiSolid[iRefList][iRefIdxTemp][2] = affiAMVPInfoTemp[eRefPicList].mvSolidLB[mvp_idx];
+
+            cMvTempSolid[iRefList][iRefIdxTemp][0] = cMvPredBiSolid[iRefList][iRefIdxTemp][0];
+            cMvTempSolid[iRefList][iRefIdxTemp][1] = cMvPredBiSolid[iRefList][iRefIdxTemp][1];
+            cMvTempSolid[iRefList][iRefIdxTemp][2] = cMvPredBiSolid[iRefList][iRefIdxTemp][2];
+
+            if (cMvTempValid[iRefList][iRefIdxTemp][0] && cMvTempValid[iRefList][iRefIdxTemp][1] && cMvTempValid[iRefList][iRefIdxTemp][2])
+            {
+              cMvTempValid[iRefList][iRefIdxTemp][0] = cMvPredBiSolid[iRefList][iRefIdxTemp][0];
+              cMvTempValid[iRefList][iRefIdxTemp][1] = cMvPredBiSolid[iRefList][iRefIdxTemp][1];
+              cMvTempValid[iRefList][iRefIdxTemp][2] = cMvPredBiSolid[iRefList][iRefIdxTemp][2];
+            }
+
+            uiCostTempOk = true;
+            uiCostTempOk = uiCostTempOk && cMvPredBiSolid[iRefList][iRefIdxTemp][0] && cMvPredBiSolid[iRefList][iRefIdxTemp][1];
+            uiCostTempOk = uiCostTempOk && ((mvNum > 2) ? cMvPredBiSolid[iRefList][iRefIdxTemp][2] : true);
+            uiCostTempOk = uiCostTempOk && cMvTempSolid[iRefList][iRefIdxTemp][0] && cMvTempSolid[iRefList][iRefIdxTemp][1] && ((mvNum > 2) ? cMvTempSolid[iRefList][iRefIdxTemp][2] : true);
+            uiCostTempOk = uiCostTempOk && cMvTempValid[iRefList][iRefIdxTemp][0] && cMvTempValid[iRefList][iRefIdxTemp][1] && ((mvNum > 2) ? cMvTempValid[iRefList][iRefIdxTemp][2] : true);
+          }
+        }
+#else
         if ( pu.cu->imv != 2 || !m_pcEncCfg->getUseAffineAmvrEncOpt() )
         {
           xCheckBestAffineMVP(pu, affiAMVPInfoTemp[eRefPicList], eRefPicList, cMvTemp[iRefList][iRefIdxTemp],
                               cMvPredBi[iRefList][iRefIdxTemp], aaiMvpIdxBi[iRefList][iRefIdxTemp], uiBitsTemp,
                               uiCostTemp);
         }
+#endif
+
+#if GDR_ENABLED  
+        allOk = (uiCostTemp < uiCostBi);
 
+        if (isEncodeClean)
+        {
+          if (uiCostTempOk)
+          {
+            allOk = (uiCostBiOk) ? (uiCostTemp < uiCostBi) : true;
+          }
+          else
+          {
+            allOk = false;
+          }
+        }
+#endif
+
+
+
+#if GDR_ENABLED  
+        if (allOk)
+#else
         if ( uiCostTemp < uiCostBi )
+#endif
         {
           bChanged = true;
           ::memcpy( cMvBi[iRefList], cMvTemp[iRefList][iRefIdxTemp], sizeof(Mv)*3 );
+#if GDR_ENABLED
+          if (isEncodeClean)
+          {
+            ::memcpy(cMvBiSolid[iRefList], cMvTempSolid[iRefList][iRefIdxTemp], sizeof(bool) * 3);
+            ::memcpy(cMvBiValid[iRefList], cMvTempValid[iRefList][iRefIdxTemp], sizeof(bool) * 3);
+          }
+#endif
           iRefIdxBi[iRefList] = iRefIdxTemp;
 
           uiCostBi            = uiCostTemp;
+#if GDR_ENABLED
+          if (isEncodeClean)
+          {
+            uiCostBiOk = uiCostTempOk;
+          }
+#endif
           uiMotBits[iRefList] = uiBitsTemp - uiMbBits[2] - uiMotBits[1-iRefList];
           uiMotBits[iRefList] -= ((pu.cu->slice->getSPS()->getUseBcw() == true) ? bcwIdxBits : 0);
           uiBits[2]           = uiBitsTemp;
@@ -5116,23 +7591,119 @@ void InterSearch::xPredAffineInterSearch( PredictionUnit&       pu,
             //  Set motion
             PU::setAllAffineMv( pu, cMvBi[iRefList][0], cMvBi[iRefList][1], cMvBi[iRefList][2], eRefPicList);
             pu.refIdx[eRefPicList] = iRefIdxBi[eRefPicList];
+
+#if GDR_ENABLED
+            if (isEncodeClean)
+            {
+              bool isSubPuYYClean;
+              bool isSubPuCbClean;
+              PelUnitBuf     tmpBuf = m_tmpAffiStorage.getBuf(UnitAreaRelative(*pu.cu, pu));
+              const Picture *refPic = pu.cu->slice->getRefPic((RefPicList)iRefList, pu.refIdx[eRefPicList]);
+
+              pu.mvAffiSolid[eRefPicList][0] = cMvBiSolid[iRefList][0];
+              pu.mvAffiSolid[eRefPicList][1] = cMvBiSolid[iRefList][1];
+              pu.mvAffiSolid[eRefPicList][2] = cMvBiSolid[iRefList][2];
+
+              isSubPuYYClean = xPredAffineBlk(COMPONENT_Y, pu, refPic, cMvBi[iRefList], tmpBuf, false, pu.cu->slice->clpRng(COMPONENT_Y));
+              isSubPuCbClean = (isSubPuYYClean) ? xPredAffineBlk(COMPONENT_Cb, pu, refPic, cMvBi[iRefList], tmpBuf, false, pu.cu->slice->clpRng(COMPONENT_Cb)) : false;
+
+              pu.mvAffiValid[eRefPicList][0] = cMvBiValid[iRefList][0] = isSubPuYYClean && isSubPuCbClean;
+              pu.mvAffiValid[eRefPicList][1] = cMvBiValid[iRefList][1] = isSubPuYYClean && isSubPuCbClean;
+              pu.mvAffiValid[eRefPicList][2] = cMvBiValid[iRefList][2] = isSubPuYYClean && isSubPuCbClean;
+            }
+#endif
+
             PelUnitBuf predBufTmp = m_tmpPredStorage[iRefList].getBuf( UnitAreaRelative(*pu.cu, pu) );
             motionCompensation( pu, predBufTmp, eRefPicList );
           }
         }
       } // for loop-iRefIdxTemp
 
-      if ( !bChanged )
-      {
-        if ((uiCostBi <= uiCost[0] && uiCostBi <= uiCost[1]) || enforceBcwPred)
-        {
-          xCopyAffineAMVPInfo( aacAffineAMVPInfo[0][iRefIdxBi[0]], affiAMVPInfoTemp[REF_PIC_LIST_0] );
-          xCheckBestAffineMVP( pu, affiAMVPInfoTemp[REF_PIC_LIST_0], REF_PIC_LIST_0, cMvBi[0], cMvPredBi[0][iRefIdxBi[0]], aaiMvpIdxBi[0][iRefIdxBi[0]], uiBits[2], uiCostBi );
+      if ( !bChanged )
+      {
+#if GDR_ENABLED    
+        allOk = ((uiCostBi <= uiCost[0] && uiCostBi <= uiCost[1]) || enforceBcwPred);
+
+        if (isEncodeClean)
+        {
+          if (uiCostBiOk)
+          {
+            allOk = (uiCostOk[0] && uiCostOk[1]) ? ((uiCostBi <= uiCost[0] && uiCostBi <= uiCost[1]) || enforceBcwPred) : true;
+          }
+          else
+          {
+            allOk = false;
+          }
+        }
+#endif
+
+#if GDR_ENABLED
+        if (allOk)
+#else
+        if ((uiCostBi <= uiCost[0] && uiCostBi <= uiCost[1]) || enforceBcwPred)
+#endif
+        {
+          xCopyAffineAMVPInfo( aacAffineAMVPInfo[0][iRefIdxBi[0]], affiAMVPInfoTemp[REF_PIC_LIST_0] );
+          xCheckBestAffineMVP( pu, affiAMVPInfoTemp[REF_PIC_LIST_0], REF_PIC_LIST_0, cMvBi[0], cMvPredBi[0][iRefIdxBi[0]], aaiMvpIdxBi[0][iRefIdxBi[0]], uiBits[2], uiCostBi );
+#if GDR_ENABLED            
+          if (isEncodeClean)
+          {
+            int mvp_idx = aaiMvpIdxBi[0][iRefIdxBi[0]];
+
+            cMvPredBiSolid[REF_PIC_LIST_0][iRefIdxBi[0]][0] = affiAMVPInfoTemp[REF_PIC_LIST_0].mvSolidLT[mvp_idx];
+            cMvPredBiSolid[REF_PIC_LIST_0][iRefIdxBi[0]][1] = affiAMVPInfoTemp[REF_PIC_LIST_0].mvSolidRT[mvp_idx];
+            cMvPredBiSolid[REF_PIC_LIST_0][iRefIdxBi[0]][2] = affiAMVPInfoTemp[REF_PIC_LIST_0].mvSolidLB[mvp_idx];
+
+            cMvBiSolid[REF_PIC_LIST_0][0] = cMvPredBiSolid[REF_PIC_LIST_0][iRefIdxBi[0]][0];
+            cMvBiSolid[REF_PIC_LIST_0][1] = cMvPredBiSolid[REF_PIC_LIST_0][iRefIdxBi[0]][1];
+            cMvBiSolid[REF_PIC_LIST_0][2] = cMvPredBiSolid[REF_PIC_LIST_0][iRefIdxBi[0]][2];
+
+            if (cMvBiValid[REF_PIC_LIST_0][0] && cMvBiValid[REF_PIC_LIST_0][1] && cMvBiValid[REF_PIC_LIST_0][2])
+            {
+              cMvBiValid[REF_PIC_LIST_0][0] = cMvPredBiSolid[REF_PIC_LIST_0][iRefIdxBi[0]][0];
+              cMvBiValid[REF_PIC_LIST_0][1] = cMvPredBiSolid[REF_PIC_LIST_0][iRefIdxBi[0]][1];
+              cMvBiValid[REF_PIC_LIST_0][2] = cMvPredBiSolid[REF_PIC_LIST_0][iRefIdxBi[0]][2];
+            }
+
+            uiCostBiOk = true;
+            uiCostBiOk = uiCostBiOk && cMvPredBiSolid[REF_PIC_LIST_0][iRefIdxBi[0]][0] && cMvPredBiSolid[REF_PIC_LIST_0][iRefIdxBi[0]][1];
+            uiCostBiOk = uiCostBiOk && ((mvNum > 2) ? cMvPredBiSolid[REF_PIC_LIST_0][iRefIdxBi[0]][2] : true);
+            uiCostBiOk = uiCostBiOk && cMvBiSolid[0][0] && cMvBiSolid[0][1] && ((mvNum > 2) ? cMvBiSolid[0][2] : true);
+            uiCostBiOk = uiCostBiOk && cMvBiValid[0][0] && cMvBiValid[0][1] && ((mvNum > 2) ? cMvBiValid[0][2] : true);
+          }
+#endif
 
           if ( !slice.getPicHeader()->getMvdL1ZeroFlag() )
           {
             xCopyAffineAMVPInfo( aacAffineAMVPInfo[1][iRefIdxBi[1]], affiAMVPInfoTemp[REF_PIC_LIST_1] );
             xCheckBestAffineMVP( pu, affiAMVPInfoTemp[REF_PIC_LIST_1], REF_PIC_LIST_1, cMvBi[1], cMvPredBi[1][iRefIdxBi[1]], aaiMvpIdxBi[1][iRefIdxBi[1]], uiBits[2], uiCostBi );
+#if GDR_ENABLED            
+            if (isEncodeClean)
+            {
+              int mvp_idx = aaiMvpIdxBi[1][iRefIdxBi[1]];
+
+              cMvPredBiSolid[REF_PIC_LIST_1][iRefIdxBi[1]][0] = affiAMVPInfoTemp[REF_PIC_LIST_1].mvSolidLT[mvp_idx];
+              cMvPredBiSolid[REF_PIC_LIST_1][iRefIdxBi[1]][1] = affiAMVPInfoTemp[REF_PIC_LIST_1].mvSolidRT[mvp_idx];
+              cMvPredBiSolid[REF_PIC_LIST_1][iRefIdxBi[1]][2] = affiAMVPInfoTemp[REF_PIC_LIST_1].mvSolidLB[mvp_idx];
+
+              cMvBiSolid[REF_PIC_LIST_1][0] = cMvPredBiSolid[REF_PIC_LIST_1][iRefIdxBi[1]][0];
+              cMvBiSolid[REF_PIC_LIST_1][1] = cMvPredBiSolid[REF_PIC_LIST_1][iRefIdxBi[1]][1];
+              cMvBiSolid[REF_PIC_LIST_1][2] = cMvPredBiSolid[REF_PIC_LIST_1][iRefIdxBi[1]][2];
+
+              if (cMvBiValid[REF_PIC_LIST_1][0] && cMvBiValid[REF_PIC_LIST_1][1] && cMvBiValid[REF_PIC_LIST_1][2])
+              {
+                cMvBiValid[REF_PIC_LIST_1][0] = cMvPredBiSolid[REF_PIC_LIST_1][iRefIdxBi[1]][0];
+                cMvBiValid[REF_PIC_LIST_1][1] = cMvPredBiSolid[REF_PIC_LIST_1][iRefIdxBi[1]][1];
+                cMvBiValid[REF_PIC_LIST_1][2] = cMvPredBiSolid[REF_PIC_LIST_1][iRefIdxBi[1]][2];
+              }
+
+              uiCostBiOk = true;
+              uiCostBiOk = uiCostBiOk && cMvPredBiSolid[REF_PIC_LIST_1][iRefIdxBi[1]][0] && cMvPredBiSolid[REF_PIC_LIST_1][iRefIdxBi[1]][1];
+              uiCostBiOk = uiCostBiOk && ((mvNum > 2) ? cMvPredBiSolid[REF_PIC_LIST_1][iRefIdxBi[1]][2] : true);
+              uiCostBiOk = uiCostBiOk && cMvBiSolid[1][0] && cMvBiSolid[1][1] && ((mvNum > 2) ? cMvBiSolid[1][2] : true);
+              uiCostBiOk = uiCostBiOk && cMvBiValid[1][0] && cMvBiValid[1][1] && ((mvNum > 2) ? cMvBiValid[1][2] : true);
+            }
+#endif
           }
         }
         break;
@@ -5164,6 +7735,15 @@ void InterSearch::xPredAffineInterSearch( PredictionUnit&       pu,
   iRefIdx[1] = refIdxValidList1;
   uiBits[1]  = bitsValidList1;
   uiCost[1]  = costValidList1;
+
+#if GDR_ENABLED
+  if (isEncodeClean)
+  {
+    memcpy(aacMvSolid[1], mvValidList1Solid, sizeof(bool) * 3);
+    memcpy(aacMvValid[1], mvValidList1Valid, sizeof(bool) * 3);
+    uiCostOk[1] = costValidList1Ok;
+  }
+#endif
   if (pu.cs->pps->getWPBiPred() == true && tryBipred && (bcwIdx != BCW_DEFAULT))
   {
     CHECK(iRefIdxBi[0]<0, "Invalid picture reference index");
@@ -5175,15 +7755,46 @@ void InterSearch::xPredAffineInterSearch( PredictionUnit&       pu,
     {
       uiCostBi = MAX_UINT;
       enforceBcwPred = false;
+#if GDR_ENABLED
+      uiCostBiOk = false;
+#endif
     }
   }
   if( enforceBcwPred )
   {
     uiCost[0] = uiCost[1] = MAX_UINT;
+#if GDR_ENABLED
+    uiCostOk[0] = uiCostOk[1] = false;
+#endif
   }
 
   // Affine ME result set
+#if GDR_ENABLED
+  bool BiOk = (uiCostBi <= uiCost[0] && uiCostBi <= uiCost[1]);
+
+  if (isEncodeClean)
+  {
+    if (uiCostBiOk)
+      BiOk = (uiCostOk[0] && uiCostOk[1]) ? (uiCostBi <= uiCost[0] && uiCostBi <= uiCost[1]) : true;
+    else
+      BiOk = false;
+  }
+
+  bool L0ok = (uiCost[0] <= uiCost[1]);
+  if (isEncodeClean)
+  {
+    if (uiCostOk[0])
+      L0ok = (uiCostOk[1]) ? (uiCost[0] <= uiCost[1]) : true;
+    else
+      L0ok = false;
+  }
+#endif
+
+#if GDR_ENABLED 
+  if (BiOk)
+#else
   if ( uiCostBi <= uiCost[0] && uiCostBi <= uiCost[1] ) // Bi
+#endif
   {
     lastMode = 2;
     affineCost = uiCostBi;
@@ -5193,6 +7804,39 @@ void InterSearch::xPredAffineInterSearch( PredictionUnit&       pu,
     pu.refIdx[REF_PIC_LIST_0] = iRefIdxBi[0];
     pu.refIdx[REF_PIC_LIST_1] = iRefIdxBi[1];
 
+#if GDR_ENABLED
+    if (isEncodeClean)
+    {
+      PelUnitBuf     tmpBuf = m_tmpAffiStorage.getBuf(UnitAreaRelative(*pu.cu, pu));
+      const Picture *refPic0 = pu.cu->slice->getRefPic((RefPicList)REF_PIC_LIST_0, pu.refIdx[REF_PIC_LIST_0]);
+      const Picture *refPic1 = pu.cu->slice->getRefPic((RefPicList)REF_PIC_LIST_1, pu.refIdx[REF_PIC_LIST_1]);
+
+      pu.mvAffiSolid[REF_PIC_LIST_0][0] = cMvBiSolid[REF_PIC_LIST_0][0];
+      pu.mvAffiSolid[REF_PIC_LIST_0][1] = cMvBiSolid[REF_PIC_LIST_0][1];
+      pu.mvAffiSolid[REF_PIC_LIST_0][2] = cMvBiSolid[REF_PIC_LIST_0][2];
+
+      bool isSubPuYYClean0 = xPredAffineBlk(COMPONENT_Y, pu, refPic0, cMvBi[0], tmpBuf, false, pu.cu->slice->clpRng(COMPONENT_Y));
+      bool isSubPuCbClean0 = (isSubPuYYClean0) ? xPredAffineBlk(COMPONENT_Cb, pu, refPic0, cMvBi[0], tmpBuf, false, pu.cu->slice->clpRng(COMPONENT_Cb)) : false;
+
+      pu.mvAffiValid[REF_PIC_LIST_0][0] = cMvBiValid[REF_PIC_LIST_0][0] = isSubPuYYClean0 && isSubPuCbClean0;
+      pu.mvAffiValid[REF_PIC_LIST_0][1] = cMvBiValid[REF_PIC_LIST_0][1] = isSubPuYYClean0 && isSubPuCbClean0;
+      pu.mvAffiValid[REF_PIC_LIST_0][2] = cMvBiValid[REF_PIC_LIST_0][2] = isSubPuYYClean0 && isSubPuCbClean0;
+
+
+      pu.mvAffiSolid[REF_PIC_LIST_1][0] = cMvBiSolid[REF_PIC_LIST_1][0];
+      pu.mvAffiSolid[REF_PIC_LIST_1][1] = cMvBiSolid[REF_PIC_LIST_1][1];
+      pu.mvAffiSolid[REF_PIC_LIST_1][2] = cMvBiSolid[REF_PIC_LIST_1][2];
+
+      bool isSubPuYYClean1 = xPredAffineBlk(COMPONENT_Y, pu, refPic1, cMvBi[1], tmpBuf, false, pu.cu->slice->clpRng(COMPONENT_Y));
+      bool isSubPuCbClean1 = (isSubPuYYClean1) ? xPredAffineBlk(COMPONENT_Cb, pu, refPic1, cMvBi[1], tmpBuf, false, pu.cu->slice->clpRng(COMPONENT_Cb)) : false;
+
+      pu.mvAffiValid[REF_PIC_LIST_1][0] = cMvBiValid[REF_PIC_LIST_1][0] = isSubPuYYClean1 && isSubPuCbClean1;
+      pu.mvAffiValid[REF_PIC_LIST_1][1] = cMvBiValid[REF_PIC_LIST_1][1] = isSubPuYYClean1 && isSubPuCbClean1;
+      pu.mvAffiValid[REF_PIC_LIST_1][2] = cMvBiValid[REF_PIC_LIST_1][2] = isSubPuYYClean1 && isSubPuCbClean1;
+    }
+#endif
+
+
     for ( int verIdx = 0; verIdx < mvNum; verIdx++ )
     {
       pu.mvdAffi[REF_PIC_LIST_0][verIdx] = cMvBi[0][verIdx] - cMvPredBi[0][iRefIdxBi[0]][verIdx];
@@ -5208,8 +7852,26 @@ void InterSearch::xPredAffineInterSearch( PredictionUnit&       pu,
     pu.mvpNum[REF_PIC_LIST_0] = aaiMvpNum[0][iRefIdxBi[0]];
     pu.mvpIdx[REF_PIC_LIST_1] = aaiMvpIdxBi[1][iRefIdxBi[1]];
     pu.mvpNum[REF_PIC_LIST_1] = aaiMvpNum[1][iRefIdxBi[1]];
+
+#if GDR_ENABLED      
+    if (isEncodeClean)
+    {
+      pu.mvpSolid[REF_PIC_LIST_0] = affiAMVPInfoTemp[0].mvSolidLT[pu.mvpIdx[0]] && affiAMVPInfoTemp[0].mvSolidRT[pu.mvpIdx[0]];
+      pu.mvpSolid[REF_PIC_LIST_1] = affiAMVPInfoTemp[1].mvSolidLT[pu.mvpIdx[1]] && affiAMVPInfoTemp[1].mvSolidRT[pu.mvpIdx[1]];
+
+      if (pu.cu->affineType == AFFINEMODEL_6PARAM)
+      {
+        pu.mvpSolid[REF_PIC_LIST_0] = pu.mvpSolid[REF_PIC_LIST_0] && affiAMVPInfoTemp[0].mvSolidLB[pu.mvpIdx[0]];
+        pu.mvpSolid[REF_PIC_LIST_1] = pu.mvpSolid[REF_PIC_LIST_1] && affiAMVPInfoTemp[1].mvSolidLB[pu.mvpIdx[1]];
+      }
+    }
+#endif
   }
+#if GDR_ENABLED 
+  else if (L0ok) // List 0
+#else
   else if ( uiCost[0] <= uiCost[1] ) // List 0
+#endif
   {
     lastMode = 0;
     affineCost = uiCost[0];
@@ -5217,6 +7879,27 @@ void InterSearch::xPredAffineInterSearch( PredictionUnit&       pu,
     PU::setAllAffineMv( pu, aacMv[0][0], aacMv[0][1], aacMv[0][2], REF_PIC_LIST_0);
     pu.refIdx[REF_PIC_LIST_0] = iRefIdx[0];
 
+#if GDR_ENABLED
+    if (isEncodeClean)
+    {
+      bool isSubPuYYClean;
+      bool isSubPuCbClean;
+      PelUnitBuf     tmpBuf = m_tmpAffiStorage.getBuf(UnitAreaRelative(*pu.cu, pu));
+      const Picture *refPic = pu.cu->slice->getRefPic((RefPicList)REF_PIC_LIST_0, pu.refIdx[REF_PIC_LIST_0]);
+
+      pu.mvAffiSolid[0][0] = aacMvSolid[0][0];
+      pu.mvAffiSolid[0][1] = aacMvSolid[0][1];
+      pu.mvAffiSolid[0][2] = aacMvSolid[0][2];
+
+      isSubPuYYClean = xPredAffineBlk(COMPONENT_Y, pu, refPic, aacMv[0], tmpBuf, false, pu.cu->slice->clpRng(COMPONENT_Y));
+      isSubPuCbClean = (isSubPuYYClean) ? xPredAffineBlk(COMPONENT_Cb, pu, refPic, aacMv[0], tmpBuf, false, pu.cu->slice->clpRng(COMPONENT_Cb)) : false;
+
+      pu.mvAffiValid[0][0] = aacMvValid[0][0] = isSubPuYYClean && isSubPuCbClean;
+      pu.mvAffiValid[0][1] = aacMvValid[0][1] = isSubPuYYClean && isSubPuCbClean;
+      pu.mvAffiValid[0][2] = aacMvValid[0][2] = isSubPuYYClean && isSubPuCbClean;
+    }
+#endif
+
     for ( int verIdx = 0; verIdx < mvNum; verIdx++ )
     {
       pu.mvdAffi[REF_PIC_LIST_0][verIdx] = aacMv[0][verIdx] - cMvPred[0][iRefIdx[0]][verIdx];
@@ -5228,6 +7911,17 @@ void InterSearch::xPredAffineInterSearch( PredictionUnit&       pu,
 
     pu.mvpIdx[REF_PIC_LIST_0] = aaiMvpIdx[0][iRefIdx[0]];
     pu.mvpNum[REF_PIC_LIST_0] = aaiMvpNum[0][iRefIdx[0]];
+#if GDR_ENABLED      
+    if (isEncodeClean)
+    {
+      pu.mvpSolid[REF_PIC_LIST_0] = affiAMVPInfoTemp[0].mvSolidLT[pu.mvpIdx[0]] && affiAMVPInfoTemp[0].mvSolidRT[pu.mvpIdx[0]];
+
+      if (pu.cu->affineType == AFFINEMODEL_6PARAM)
+      {
+        pu.mvpSolid[REF_PIC_LIST_0] = pu.mvpSolid[REF_PIC_LIST_0] && affiAMVPInfoTemp[0].mvSolidLB[pu.mvpIdx[0]];
+      }
+    }
+#endif
   }
   else
   {
@@ -5237,6 +7931,27 @@ void InterSearch::xPredAffineInterSearch( PredictionUnit&       pu,
     PU::setAllAffineMv( pu, aacMv[1][0], aacMv[1][1], aacMv[1][2], REF_PIC_LIST_1);
     pu.refIdx[REF_PIC_LIST_1] = iRefIdx[1];
 
+#if GDR_ENABLED
+    if (isEncodeClean)
+    {
+      bool isSubPuYYClean;
+      bool isSubPuCbClean;
+      PelUnitBuf     tmpBuf = m_tmpAffiStorage.getBuf(UnitAreaRelative(*pu.cu, pu));
+      const Picture *refPic = pu.cu->slice->getRefPic((RefPicList)REF_PIC_LIST_1, pu.refIdx[REF_PIC_LIST_1]);
+
+      pu.mvAffiSolid[1][0] = aacMvSolid[1][0];
+      pu.mvAffiSolid[1][1] = aacMvSolid[1][1];
+      pu.mvAffiSolid[1][2] = aacMvSolid[1][2];
+
+      isSubPuYYClean = xPredAffineBlk(COMPONENT_Y, pu, refPic, aacMv[1], tmpBuf, false, pu.cu->slice->clpRng(COMPONENT_Y));
+      isSubPuCbClean = (isSubPuYYClean) ? xPredAffineBlk(COMPONENT_Cb, pu, refPic, aacMv[1], tmpBuf, false, pu.cu->slice->clpRng(COMPONENT_Cb)) : false;
+
+      pu.mvAffiValid[1][0] = aacMvValid[1][0] = isSubPuYYClean && isSubPuCbClean;
+      pu.mvAffiValid[1][1] = aacMvValid[1][1] = isSubPuYYClean && isSubPuCbClean;
+      pu.mvAffiValid[1][2] = aacMvValid[1][2] = isSubPuYYClean && isSubPuCbClean;
+    }
+#endif    
+
     for ( int verIdx = 0; verIdx < mvNum; verIdx++ )
     {
       pu.mvdAffi[REF_PIC_LIST_1][verIdx] = aacMv[1][verIdx] - cMvPred[1][iRefIdx[1]][verIdx];
@@ -5248,6 +7963,17 @@ void InterSearch::xPredAffineInterSearch( PredictionUnit&       pu,
 
     pu.mvpIdx[REF_PIC_LIST_1] = aaiMvpIdx[1][iRefIdx[1]];
     pu.mvpNum[REF_PIC_LIST_1] = aaiMvpNum[1][iRefIdx[1]];
+#if GDR_ENABLED      
+    if (isEncodeClean)
+    {
+      pu.mvpSolid[REF_PIC_LIST_1] = affiAMVPInfoTemp[1].mvSolidLT[pu.mvpIdx[1]] && affiAMVPInfoTemp[1].mvSolidRT[pu.mvpIdx[1]];
+
+      if (pu.cu->affineType == AFFINEMODEL_6PARAM)
+      {
+        pu.mvpSolid[REF_PIC_LIST_1] = pu.mvpSolid[REF_PIC_LIST_1] && affiAMVPInfoTemp[1].mvSolidLB[pu.mvpIdx[1]];
+      }
+    }
+#endif
   }
   if( bcwIdx != BCW_DEFAULT )
   {
@@ -5328,6 +8054,11 @@ void solveEqual(double dEqualCoeff[7][7], int iOrder, double *dAffinePara)
 
 void InterSearch::xCheckBestAffineMVP( PredictionUnit &pu, AffineAMVPInfo &affineAMVPInfo, RefPicList eRefPicList, Mv acMv[3], Mv acMvPred[3], int& riMVPIdx, uint32_t& ruiBits, Distortion& ruiCost )
 {
+#if GDR_ENABLED
+  const CodingStructure &cs = *pu.cs;
+  const bool isEncodeClean = cs.pcv->isEncoder && ((cs.picHeader->getInGdrPeriod() && cs.isClean(pu.Y().topRight(), CHANNEL_TYPE_LUMA)) || (cs.picHeader->getNumVerVirtualBoundaries() == 0));
+#endif
+
   if ( affineAMVPInfo.numCand < 2 )
   {
     return;
@@ -5361,7 +8092,39 @@ void InterSearch::xCheckBestAffineMVP( PredictionUnit &pu, AffineAMVPInfo &affin
     int iMvBits = xCalcAffineMVBits( pu, acMv, tmpPredMv );
     iMvBits += m_auiMVPIdxCost[iMVPIdx][AMVP_MAX_NUM_CANDS];
 
+#if GDR_ENABLED    
+    bool allOk = (iMvBits < iBestMvBits);
+    if (isEncodeClean)
+    {
+      bool curOk = affineAMVPInfo.mvSolidLT[iMVPIdx] && affineAMVPInfo.mvSolidRT[iMVPIdx];
+      if (pu.cu->affineType == AFFINEMODEL_6PARAM)
+      {
+        curOk = curOk && affineAMVPInfo.mvSolidLB[iMVPIdx];
+      }
+
+      bool best_ok = affineAMVPInfo.mvSolidLT[iBestMVPIdx] && affineAMVPInfo.mvSolidRT[iBestMVPIdx];
+      if (pu.cu->affineType == AFFINEMODEL_6PARAM)
+      {
+        curOk = curOk && affineAMVPInfo.mvSolidLB[iBestMVPIdx];
+      }
+
+      if (curOk)
+      {
+        allOk = (best_ok) ? (iMvBits < iBestMvBits) : true;
+      }
+      else
+      {
+        allOk = false;
+      }
+    }
+#endif
+
+
+#if GDR_ENABLED  
+    if (allOk)
+#else
     if (iMvBits < iBestMvBits)
+#endif
     {
       iBestMvBits = iMvBits;
       iBestMVPIdx = iMVPIdx;
@@ -5380,6 +8143,21 @@ void InterSearch::xCheckBestAffineMVP( PredictionUnit &pu, AffineAMVPInfo &affin
   }
 }
 
+#if GDR_ENABLED
+void InterSearch::xAffineMotionEstimation(PredictionUnit& pu,
+  PelUnitBuf&     origBuf,
+  RefPicList      eRefPicList,
+  Mv              acMvPred[3],
+  int             iRefIdxPred,
+  Mv              acMv[3],
+  bool            acMvSolid[3],
+  uint32_t&       ruiBits,
+  Distortion&     ruiCost,
+  int&            mvpIdx,
+  const AffineAMVPInfo& aamvpi,
+  bool&           rbCleanCandExist,
+  bool            bBi)
+#else
 void InterSearch::xAffineMotionEstimation( PredictionUnit& pu,
                                            PelUnitBuf&     origBuf,
                                            RefPicList      eRefPicList,
@@ -5391,13 +8169,25 @@ void InterSearch::xAffineMotionEstimation( PredictionUnit& pu,
                                            int&            mvpIdx,
                                            const AffineAMVPInfo& aamvpi,
                                            bool            bBi)
+#endif
 {
+#if GDR_ENABLED
+  if (pu.cu->cs->sps->getUseBcw() && pu.cu->BcwIdx != BCW_DEFAULT && !bBi && xReadBufferedAffineUniMv(pu, eRefPicList, iRefIdxPred, acMvPred, acMv, acMvSolid, ruiBits, ruiCost
+    , mvpIdx, aamvpi
+  ))
+#else
   if( pu.cu->cs->sps->getUseBcw() && pu.cu->BcwIdx != BCW_DEFAULT && !bBi && xReadBufferedAffineUniMv(pu, eRefPicList, iRefIdxPred, acMvPred, acMv, ruiBits, ruiCost
       , mvpIdx, aamvpi
   ) )
+#endif
   {
     return;
   }
+#if GDR_ENABLED
+  const CodingStructure &cs = *pu.cs;
+  const bool isEncodeClean = cs.pcv->isEncoder && ((cs.picHeader->getInGdrPeriod() && cs.isClean(pu.Y().topRight(), CHANNEL_TYPE_LUMA)) || (cs.picHeader->getNumVerVirtualBoundaries() == 0));
+  bool acMvValid[3];
+#endif
 
   uint32_t dirBits = ruiBits - m_auiMVPIdxCost[mvpIdx][aamvpi.numCand];
   int bestMvpIdx   = mvpIdx;
@@ -5434,6 +8224,11 @@ void InterSearch::xAffineMotionEstimation( PredictionUnit& pu,
   // Set start Mv position, use input mv as started search mv
   Mv acMvTemp[3];
   ::memcpy( acMvTemp, acMv, sizeof(Mv)*3 );
+
+#if GDR_ENABLED
+  bool acMvTempSolid[3];
+  ::memcpy(acMvTempSolid, acMvSolid, sizeof(bool) * 3);
+#endif
   // Set delta mv
   // malloc buffer
   int iParaNum = pu.cu->affineType ? 7 : 5;
@@ -5449,6 +8244,13 @@ void InterSearch::xAffineMotionEstimation( PredictionUnit& pu,
 
   Distortion uiCostBest = std::numeric_limits<Distortion>::max();
   uint32_t uiBitsBest = 0;
+#if GDR_ENABLED
+  bool uiCostBestOk = true;
+  bool uiCostTempOk = true;
+  bool costTempOk = true;
+
+  bool allOk = true;
+#endif
 
   // do motion compensation with origin mv
   if( m_pcEncCfg->getMCTSEncConstraint() )
@@ -5476,7 +8278,11 @@ void InterSearch::xAffineMotionEstimation( PredictionUnit& pu,
   {
     acMvTemp[2].roundAffinePrecInternal2Amvr(pu.cu->imv);
   }
+#if GDR_ENABLED
+  bool YYOk = xPredAffineBlk(COMPONENT_Y, pu, refPic, acMvTemp, predBuf, false, pu.cs->slice->clpRng(COMPONENT_Y));
+#else
   xPredAffineBlk( COMPONENT_Y, pu, refPic, acMvTemp, predBuf, false, pu.cs->slice->clpRng( COMPONENT_Y ) );
+#endif
 
   // get error
   uiCostBest = m_pcRdCost->getDistPart(predBuf.Y(), pBuf->Y(), pu.cs->sps->getBitDepth(CHANNEL_TYPE_LUMA), COMPONENT_Y, distFunc);
@@ -5497,6 +8303,25 @@ void InterSearch::xAffineMotionEstimation( PredictionUnit& pu,
     uiBitsBest += xCalcAffineMVBits( pu, acMvTemp, acMvPred );
     DTRACE( g_trace_ctx, D_COMMON, " (%d) yy uiBitsBest=%d\n", DTRACE_GET_COUNTER(g_trace_ctx,D_COMMON), uiBitsBest );
   }
+
+#if GDR_ENABLED
+  if (isEncodeClean)
+  {
+    acMvSolid[0] = aamvpi.mvSolidLT[mvpIdx];
+    acMvSolid[1] = aamvpi.mvSolidRT[mvpIdx];
+    acMvSolid[2] = aamvpi.mvSolidLB[mvpIdx];
+
+    bool isSubPuYYClean = YYOk;
+    bool isSubPuCbClean = true;
+
+    acMvValid[0] = isSubPuYYClean && isSubPuCbClean;
+    acMvValid[1] = isSubPuYYClean && isSubPuCbClean;
+    acMvValid[2] = isSubPuYYClean && isSubPuCbClean;
+
+    uiCostBestOk = (acMvSolid[0] && acMvSolid[1] && acMvSolid[2]) && (acMvValid[0] && acMvValid[1] && acMvValid[2]);
+  }
+#endif
+
   uiCostBest = (Distortion)( floor( fWeight * (double)uiCostBest ) + (double)m_pcRdCost->getCost( uiBitsBest ) );
 
   DTRACE( g_trace_ctx, D_COMMON, " (%d) uiBitsBest=%d, uiCostBest=%d\n", DTRACE_GET_COUNTER(g_trace_ctx,D_COMMON), uiBitsBest, uiCostBest );
@@ -5660,7 +8485,11 @@ void InterSearch::xAffineMotionEstimation( PredictionUnit& pu,
       }
     }
 
+#if GDR_ENABLED
+    bool YYOk = xPredAffineBlk(COMPONENT_Y, pu, refPic, acMvTemp, predBuf, false, pu.cu->slice->clpRng(COMPONENT_Y));
+#else
     xPredAffineBlk( COMPONENT_Y, pu, refPic, acMvTemp, predBuf, false, pu.cu->slice->clpRng( COMPONENT_Y ) );
+#endif
 
     // get error
     Distortion uiCostTemp = m_pcRdCost->getDistPart(predBuf.Y(), pBuf->Y(), pu.cs->sps->getBitDepth(CHANNEL_TYPE_LUMA), COMPONENT_Y, distFunc);
@@ -5680,12 +8509,53 @@ void InterSearch::xAffineMotionEstimation( PredictionUnit& pu,
     {
       uiBitsTemp += xCalcAffineMVBits( pu, acMvTemp, acMvPred );
     }
+#if GDR_ENABLED
+    if (isEncodeClean)
+    {
+      acMvSolid[0] = aamvpi.mvSolidLT[bestMvpIdx];
+      acMvSolid[1] = aamvpi.mvSolidRT[bestMvpIdx];
+      acMvSolid[2] = aamvpi.mvSolidLB[bestMvpIdx];
+
+      bool isSubPuYYClean = YYOk;
+      bool isSubPuCbClean = true; 
+
+      acMvValid[0] = isSubPuYYClean && isSubPuCbClean;
+      acMvValid[1] = isSubPuYYClean && isSubPuCbClean;
+      acMvValid[2] = isSubPuYYClean && isSubPuCbClean;
+
+      uiCostTempOk = (acMvSolid[0] && acMvSolid[1] && acMvSolid[2]) && (acMvValid[0] && acMvValid[1] && acMvValid[2]);
+    }
+#endif
+
     uiCostTemp = (Distortion)( floor( fWeight * (double)uiCostTemp ) + (double)m_pcRdCost->getCost( uiBitsTemp ) );
 
     // store best cost and mv
+#if GDR_ENABLED    
+    allOk = (uiCostTemp < uiCostBest);
+    if (isEncodeClean)
+    {
+      if (uiCostTempOk)
+      {
+        allOk = (uiCostBestOk) ? (uiCostTemp < uiCostBest) : true;
+      }
+      else
+      {
+        allOk = false;
+      }
+    }
+
+    if (allOk)
+#else
     if ( uiCostTemp < uiCostBest )
+#endif
     {
       uiCostBest = uiCostTemp;
+#if GDR_ENABLED
+      if (isEncodeClean)
+      {
+        uiCostBestOk = uiCostTempOk;
+      }
+#endif
       uiBitsBest = uiBitsTemp;
       memcpy( acMv, acMvTemp, sizeof(Mv) * 3 );
       mvpIdx = bestMvpIdx;
@@ -5694,7 +8564,30 @@ void InterSearch::xAffineMotionEstimation( PredictionUnit& pu,
 
   auto checkCPMVRdCost = [&](Mv ctrlPtMv[3])
   {
+#if GDR_ENABLED
+    bool YYOk = xPredAffineBlk(COMPONENT_Y, pu, refPic, ctrlPtMv, predBuf, false, pu.cu->slice->clpRng(COMPONENT_Y));
+#else
     xPredAffineBlk(COMPONENT_Y, pu, refPic, ctrlPtMv, predBuf, false, pu.cu->slice->clpRng(COMPONENT_Y));
+#endif
+
+#if GDR_ENABLED
+    if (isEncodeClean)
+    {
+      acMvSolid[0] = aamvpi.mvSolidLT[bestMvpIdx];
+      acMvSolid[1] = aamvpi.mvSolidRT[bestMvpIdx];
+      acMvSolid[2] = aamvpi.mvSolidLB[bestMvpIdx];
+
+      bool isSubPuYYClean = YYOk;
+      bool isSubPuCbClean = true; // (isSubPuYYClean) ? xPredAffineBlk(COMPONENT_Cb, pu, refPic, ctrlPtMv, tmpBuf, false, pu.cu->slice->clpRng(COMPONENT_Cb)) : false;
+
+      acMvValid[0] = isSubPuYYClean && isSubPuCbClean;
+      acMvValid[1] = isSubPuYYClean && isSubPuCbClean;
+      acMvValid[2] = isSubPuYYClean && isSubPuCbClean;
+
+      costTempOk = (acMvSolid[0] && acMvSolid[1] && acMvSolid[2]) && (acMvValid[0] && acMvValid[1] && acMvValid[2]);
+    }
+#endif
+
     // get error
     Distortion costTemp = m_pcRdCost->getDistPart(predBuf.Y(), pBuf->Y(), pu.cs->sps->getBitDepth(CHANNEL_TYPE_LUMA), COMPONENT_Y, distFunc);
     // get cost with mv
@@ -5703,9 +8596,33 @@ void InterSearch::xAffineMotionEstimation( PredictionUnit& pu,
     bitsTemp += xCalcAffineMVBits( pu, ctrlPtMv, acMvPred );
     costTemp = (Distortion)(floor(fWeight * (double)costTemp) + (double)m_pcRdCost->getCost(bitsTemp));
     // store best cost and mv
+#if GDR_ENABLED    
+    bool allOk = (costTemp < uiCostBest);
+    if (isEncodeClean)
+    {
+      if (costTempOk)
+      {
+        allOk = (uiCostBestOk) ? (costTemp < uiCostBest) : true;
+      }
+      else
+      {
+        allOk = false;
+      }
+    }
+
+    if (allOk)
+#else
     if (costTemp < uiCostBest)
+#endif
     {
       uiCostBest = costTemp;
+#if GDR_ENABLED
+      if (isEncodeClean)
+      {
+        uiCostBestOk = costTempOk;
+        rbCleanCandExist = true;
+      }
+#endif
       uiBitsBest = bitsTemp;
       ::memcpy(acMv, ctrlPtMv, sizeof(Mv) * 3);
     }
@@ -5786,15 +8703,62 @@ void InterSearch::xAffineMotionEstimation( PredictionUnit& pu,
           {
             acMvTemp[j].set(centerMv[j].getHor() + (testPos[i][0] << mvShift), centerMv[j].getVer() + (testPos[i][1] << mvShift));
             clipMv( acMvTemp[j], pu.cu->lumaPos(), pu.cu->lumaSize(), *pu.cs->sps, *pu.cs->pps );
+#if GDR_ENABLED
+            bool YYOk = xPredAffineBlk(COMPONENT_Y, pu, refPic, acMvTemp, predBuf, false, pu.cu->slice->clpRng(COMPONENT_Y));
+#else
             xPredAffineBlk(COMPONENT_Y, pu, refPic, acMvTemp, predBuf, false, pu.cu->slice->clpRng(COMPONENT_Y));
+#endif
+
+#if GDR_ENABLED
+            if (isEncodeClean)
+            {
+              acMvSolid[0] = aamvpi.mvSolidLT[bestMvpIdx];
+              acMvSolid[1] = aamvpi.mvSolidRT[bestMvpIdx];
+              acMvSolid[2] = aamvpi.mvSolidLB[bestMvpIdx];
+
+              bool isSubPuYYClean = YYOk;
+              bool isSubPuCbClean = true;
+
+              acMvValid[0] = isSubPuYYClean && isSubPuCbClean;
+              acMvValid[1] = isSubPuYYClean && isSubPuCbClean;
+              acMvValid[2] = isSubPuYYClean && isSubPuCbClean;
+
+              costTempOk = (acMvSolid[0] && acMvSolid[1] && acMvSolid[2]) && (acMvValid[0] && acMvValid[1] && acMvValid[2]);
+            }
+#endif
+
             Distortion costTemp = m_pcRdCost->getDistPart(predBuf.Y(), pBuf->Y(), pu.cs->sps->getBitDepth(CHANNEL_TYPE_LUMA), COMPONENT_Y, distFunc);
             uint32_t bitsTemp = ruiBits;
             bitsTemp += xCalcAffineMVBits(pu, acMvTemp, acMvPred);
             costTemp = (Distortion)(floor(fWeight * (double)costTemp) + (double)m_pcRdCost->getCost(bitsTemp));
 
+#if GDR_ENABLED    
+            bool allOk = (costTemp < uiCostBest);
+            if (isEncodeClean)
+            {
+              if (costTempOk)
+              {
+                allOk = (uiCostBestOk) ? (costTemp < uiCostBest) : true;
+              }
+              else
+              {
+                allOk = false;
+              }
+            }
+
+            if (allOk)
+#else
             if (costTemp < uiCostBest)
+#endif
             {
               uiCostBest = costTemp;
+#if GDR_ENABLED
+              if (isEncodeClean)
+              {
+                uiCostBestOk = costTempOk;
+                rbCleanCandExist = true;
+              }
+#endif
               uiBitsBest = bitsTemp;
               ::memcpy(acMv, acMvTemp, sizeof(Mv) * 3);
               modelChange = true;
@@ -5814,6 +8778,12 @@ void InterSearch::xAffineMotionEstimation( PredictionUnit& pu,
   acMvPred[1] = aamvpi.mvCandRT[mvpIdx];
   acMvPred[2] = aamvpi.mvCandLB[mvpIdx];
 
+#if GDR_ENABLED
+  acMvSolid[0] = aamvpi.mvSolidLT[mvpIdx];
+  acMvSolid[1] = aamvpi.mvSolidRT[mvpIdx];
+  acMvSolid[2] = aamvpi.mvSolidLB[mvpIdx];
+#endif
+
   ruiBits = uiBitsBest;
   ruiCost = uiCostBest;
   DTRACE( g_trace_ctx, D_COMMON, " (%d) uiBitsBest=%d, uiCostBest=%d\n", DTRACE_GET_COUNTER(g_trace_ctx,D_COMMON), uiBitsBest, uiCostBest );
@@ -5831,6 +8801,12 @@ void InterSearch::xEstimateAffineAMVP( PredictionUnit&  pu,
   int        iBestIdx = 0;
   Distortion uiBestCost = std::numeric_limits<Distortion>::max();
 
+#if GDR_ENABLED
+  const CodingStructure &cs = *pu.cs;
+  const bool isEncodeClean = cs.pcv->isEncoder && ((cs.picHeader->getInGdrPeriod() && cs.isClean(pu.Y().topRight(), CHANNEL_TYPE_LUMA)) || (cs.picHeader->getNumVerVirtualBoundaries() == 0));
+  bool uiBestCostOk = false;
+#endif
+
   // Fill the MV Candidates
   PU::fillAffineMvpCand( pu, eRefPicList, iRefIdx, affineAMVPInfo );
   CHECK( affineAMVPInfo.numCand == 0, "Assertion failed." );
@@ -5843,9 +8819,36 @@ void InterSearch::xEstimateAffineAMVP( PredictionUnit&  pu,
   {
     Mv mv[3] = { affineAMVPInfo.mvCandLT[i], affineAMVPInfo.mvCandRT[i], affineAMVPInfo.mvCandLB[i] };
 
+#if GDR_ENABLED
+    bool uiTmpCostOk = true;
+    Distortion uiTmpCost = xGetAffineTemplateCost(pu, origBuf, predBuf, mv, i, AMVP_MAX_NUM_CANDS, eRefPicList, iRefIdx, uiTmpCostOk);
+    uiTmpCostOk = uiTmpCostOk && affineAMVPInfo.mvSolidLT[i] && affineAMVPInfo.mvSolidRT[i];
+    uiTmpCostOk = uiTmpCostOk && ((pu.cu->affineType == AFFINEMODEL_6PARAM) ? affineAMVPInfo.mvSolidLB[i] : true);
+#else
     Distortion uiTmpCost = xGetAffineTemplateCost( pu, origBuf, predBuf, mv, i, AMVP_MAX_NUM_CANDS, eRefPicList, iRefIdx );
+#endif
+
+#if GDR_ENABLED    
+    bool allOk = uiBestCost > uiTmpCost;
+
+    if (isEncodeClean)
+    {
+      if (uiTmpCostOk)
+      {
+        allOk = uiBestCostOk ? (uiBestCost > uiTmpCost) : true;
+      }
+      else
+      {
+        allOk = false;
+      }
+    }
+#endif
 
+#if GDR_ENABLED        
+    if (allOk)
+#else
     if ( uiBestCost > uiTmpCost )
+#endif
     {
       uiBestCost = uiTmpCost;
       bestMvLT = affineAMVPInfo.mvCandLT[i];
@@ -5853,6 +8856,12 @@ void InterSearch::xEstimateAffineAMVP( PredictionUnit&  pu,
       bestMvLB = affineAMVPInfo.mvCandLB[i];
       iBestIdx  = i;
       *puiDistBiP = uiTmpCost;
+#if GDR_ENABLED      
+      if (isEncodeClean)
+      {
+        uiBestCostOk = uiTmpCostOk;
+      }
+#endif
     }
   }
 
@@ -5863,6 +8872,10 @@ void InterSearch::xEstimateAffineAMVP( PredictionUnit&  pu,
 
   pu.mvpIdx[eRefPicList] = iBestIdx;
   pu.mvpNum[eRefPicList] = affineAMVPInfo.numCand;
+
+#if GDR_ENABLED     
+  pu.mvpSolid[eRefPicList] = uiBestCostOk;
+#endif
   DTRACE( g_trace_ctx, D_COMMON, "#estAffi=%d \n", affineAMVPInfo.numCand );
 }
 
@@ -5873,6 +8886,24 @@ void InterSearch::xCopyAffineAMVPInfo (AffineAMVPInfo& src, AffineAMVPInfo& dst)
   ::memcpy( dst.mvCandLT, src.mvCandLT, sizeof(Mv)*src.numCand );
   ::memcpy( dst.mvCandRT, src.mvCandRT, sizeof(Mv)*src.numCand );
   ::memcpy( dst.mvCandLB, src.mvCandLB, sizeof(Mv)*src.numCand );
+
+#if GDR_ENABLED
+  ::memcpy(dst.mvSolidLT, src.mvSolidLT, sizeof(bool)*src.numCand);
+  ::memcpy(dst.mvSolidRT, src.mvSolidRT, sizeof(bool)*src.numCand);
+  ::memcpy(dst.mvSolidLB, src.mvSolidLB, sizeof(bool)*src.numCand);
+
+  ::memcpy(dst.mvValidLT, src.mvValidLT, sizeof(bool)*src.numCand);
+  ::memcpy(dst.mvValidRT, src.mvValidRT, sizeof(bool)*src.numCand);
+  ::memcpy(dst.mvValidLB, src.mvValidLB, sizeof(bool)*src.numCand);
+
+  ::memcpy(dst.mvTypeLT, src.mvTypeLT, sizeof(MvpType)*src.numCand);
+  ::memcpy(dst.mvTypeRT, src.mvTypeRT, sizeof(MvpType)*src.numCand);
+  ::memcpy(dst.mvTypeLB, src.mvTypeLB, sizeof(MvpType)*src.numCand);
+
+  ::memcpy(dst.mvPosLT, src.mvPosLT, sizeof(Position)*src.numCand);
+  ::memcpy(dst.mvPosRT, src.mvPosRT, sizeof(Position)*src.numCand);
+  ::memcpy(dst.mvPosLB, src.mvPosLB, sizeof(Position)*src.numCand);
+#endif
 }
 
 
@@ -7827,11 +10858,19 @@ double InterSearch::xGetMEDistortionWeight(uint8_t bcwIdx, RefPicList eRefPicLis
     return 0.5;
   }
 }
+#if GDR_ENABLED
+bool InterSearch::xReadBufferedUniMv(PredictionUnit& pu, RefPicList eRefPicList, int32_t iRefIdx, Mv& pcMvPred, Mv& rcMv, bool& rcMvSolid, uint32_t& ruiBits, Distortion& ruiCost)
+#else
 bool InterSearch::xReadBufferedUniMv(PredictionUnit& pu, RefPicList eRefPicList, int32_t iRefIdx, Mv& pcMvPred, Mv& rcMv, uint32_t& ruiBits, Distortion& ruiCost)
+#endif
 {
   if (m_uniMotions.isReadMode((uint32_t)eRefPicList, (uint32_t)iRefIdx))
   {
+#if GDR_ENABLED
+    m_uniMotions.copyTo(rcMv, rcMvSolid, ruiCost, (uint32_t)eRefPicList, (uint32_t)iRefIdx);
+#else
     m_uniMotions.copyTo(rcMv, ruiCost, (uint32_t)eRefPicList, (uint32_t)iRefIdx);
+#endif
 
     Mv pred = pcMvPred;
     pred.changeTransPrecInternal2Amvr(pu.cu->imv);
@@ -7849,13 +10888,23 @@ bool InterSearch::xReadBufferedUniMv(PredictionUnit& pu, RefPicList eRefPicList,
   return false;
 }
 
+#if GDR_ENABLED
+bool InterSearch::xReadBufferedAffineUniMv(PredictionUnit& pu, RefPicList eRefPicList, int32_t iRefIdx, Mv acMvPred[3], Mv acMv[3], bool acMvSolid[3], uint32_t& ruiBits, Distortion& ruiCost
+  , int& mvpIdx, const AffineAMVPInfo& aamvpi
+)
+#else
 bool InterSearch::xReadBufferedAffineUniMv(PredictionUnit& pu, RefPicList eRefPicList, int32_t iRefIdx, Mv acMvPred[3], Mv acMv[3], uint32_t& ruiBits, Distortion& ruiCost
   , int& mvpIdx, const AffineAMVPInfo& aamvpi
 )
+#endif
 {
   if (m_uniMotions.isReadModeAffine((uint32_t)eRefPicList, (uint32_t)iRefIdx, pu.cu->affineType))
   {
+#if GDR_ENABLED
+    m_uniMotions.copyAffineMvTo(acMv, acMvSolid, ruiCost, (uint32_t)eRefPicList, (uint32_t)iRefIdx, pu.cu->affineType, mvpIdx);
+#else
     m_uniMotions.copyAffineMvTo(acMv, ruiCost, (uint32_t)eRefPicList, (uint32_t)iRefIdx, pu.cu->affineType, mvpIdx);
+#endif
     m_pcRdCost->setCostScale(0);
     acMvPred[0] = aamvpi.mvCandLT[mvpIdx];
     acMvPred[1] = aamvpi.mvCandRT[mvpIdx];
@@ -7921,13 +10970,32 @@ uint32_t InterSearch::xDetermineBestMvp( PredictionUnit& pu, Mv acMvTemp[3], int
 {
   bool mvpUpdated  = false;
   uint32_t minBits = std::numeric_limits<uint32_t>::max();
+#if GDR_ENABLED
+  const CodingStructure &cs = *pu.cs;
+  const bool isEncodeClean = cs.pcv->isEncoder && ((cs.picHeader->getInGdrPeriod() && cs.isClean(pu.Y().topRight(), CHANNEL_TYPE_LUMA)) || (cs.picHeader->getNumVerVirtualBoundaries() == 0));
+#endif
+
   for ( int i = 0; i < aamvpi.numCand; i++ )
   {
     Mv mvPred[3] = { aamvpi.mvCandLT[i], aamvpi.mvCandRT[i], aamvpi.mvCandLB[i] };
     uint32_t candBits = m_auiMVPIdxCost[i][aamvpi.numCand];
     candBits += xCalcAffineMVBits( pu, acMvTemp, mvPred );
 
+#if GDR_ENABLED  
+    bool isSolid = true;
+    if (isEncodeClean)
+    {
+      isSolid = aamvpi.mvSolidLT[i] && aamvpi.mvSolidRT[i];
+      if (pu.cu->affineType == AFFINEMODEL_6PARAM)
+      {
+        isSolid = isSolid && aamvpi.mvSolidLB[i];
+      }
+    }
+
+    if ((candBits < minBits) && isSolid)
+#else
     if ( candBits < minBits )
+#endif
     {
       minBits    = candBits;
       mvpIdx     = i;
@@ -7946,11 +11014,22 @@ void InterSearch::symmvdCheckBestMvp(
   AMVPInfo amvpInfo[2][33],
   int32_t bcwIdx,
   Mv cMvPredSym[2],
+#if GDR_ENABLED
+  bool cMvPredSymSolid[2],
+#endif
   int32_t mvpIdxSym[2],
   Distortion& bestCost,
   bool skip
 )
 {
+#if GDR_ENABLED  
+  CodingStructure &cs = *pu.cs;
+  const bool isEncodeClean = cs.pcv->isEncoder && ((cs.picHeader->getInGdrPeriod() && cs.isClean(pu.Y().topRight(), CHANNEL_TYPE_LUMA)) || (cs.picHeader->getNumVerVirtualBoundaries() == 0));
+  bool bestCostOk = true;
+  bool costOk = true;
+  bool allOk;
+#endif
+
   RefPicList tarRefList = (RefPicList)(1 - curRefList);
   int32_t refIdxCur = pu.cu->slice->getSymRefIdx(curRefList);
   int32_t refIdxTar = pu.cu->slice->getSymRefIdx(tarRefList);
@@ -8028,11 +11107,48 @@ void InterSearch::symmvdCheckBestMvp(
       bits += m_auiMVPIdxCost[i][AMVP_MAX_NUM_CANDS];
       bits += m_auiMVPIdxCost[j][AMVP_MAX_NUM_CANDS];
       cost += m_pcRdCost->getCost(bits);
+#if GDR_ENABLED      
+      if (isEncodeClean)
+      {
+        bool curSolid = amvpCur.mvSolid[i];
+        bool tarSolid = amvpTar.mvSolid[j];
+        costOk = curSolid && tarSolid;
+      }
+#endif
+
+
+#if GDR_ENABLED      
+      allOk = (cost < bestCost);
+      if (isEncodeClean)
+      {
+        if (costOk)
+        {
+          allOk = (bestCostOk) ? (cost < bestCost) : true;
+        }
+        else
+        {
+          allOk = false;
+        }
+      }
+#endif
+
+#if GDR_ENABLED
+      if (allOk)
+#else      
       if (cost < bestCost)
+#endif
       {
         bestCost = cost;
         cMvPredSym[curRefList] = amvpCur.mvCand[i];
         cMvPredSym[tarRefList] = amvpTar.mvCand[j];
+#if GDR_ENABLED
+        if (isEncodeClean)
+        {
+          bestCostOk = costOk;
+          cMvPredSymSolid[curRefList] = amvpCur.mvSolid[i];
+          cMvPredSymSolid[tarRefList] = amvpTar.mvSolid[j];
+        }
+#endif
         mvpIdxSym[curRefList] = i;
         mvpIdxSym[tarRefList] = j;
       }
diff --git a/source/Lib/EncoderLib/InterSearch.h b/source/Lib/EncoderLib/InterSearch.h
index 51800ad05db6281e4e1f5c150f4d7ef23adf9653..1296420265ed62999fd1a21ef9f2df60c1f3136a 100644
--- a/source/Lib/EncoderLib/InterSearch.h
+++ b/source/Lib/EncoderLib/InterSearch.h
@@ -77,6 +77,12 @@ struct AffineMVInfo
   int x, y, w, h;
 };
 
+#if GDR_ENABLED 
+struct AffineMVInfoSolid
+{
+  bool  affMVsSolid[2][33][3];
+};
+#endif
 struct BlkUniMvInfo
 {
   Mv uniMvs[2][33];
@@ -93,6 +99,11 @@ typedef struct
   Distortion affineCost[3];
   bool affine4ParaAvail;
   bool affine6ParaAvail;
+
+#if GDR_ENABLED
+  bool acMvAffine4ParaSolid[2][3];
+  bool acMvAffine6ParaSolid[2][3];
+#endif
 } EncAffineMotion;
 
 /// encoder search class
@@ -118,6 +129,9 @@ private:
   bool            m_affineModeSelected;
   std::unordered_map< Position, std::unordered_map< Size, BlkRecord> > m_ctuRecord;
   AffineMVInfo       *m_affMVList;
+#if GDR_ENABLED  
+  AffineMVInfoSolid  *m_affMVListSolid;
+#endif
   int             m_affMVListIdx;
   int             m_affMVListSize;
   int             m_affMVListMaxSize;
@@ -126,6 +140,9 @@ private:
   int             m_uniMvListSize;
   int             m_uniMvListMaxSize;
   Distortion      m_hevcCost;
+#if GDR_ENABLED  
+  bool            m_hevcCostOk;
+#endif
   EncAffineMotion m_affineMotion;
   PatentBvCand    m_defaultCachedBvs;
 protected:
@@ -208,33 +225,63 @@ public:
   void resetCtuRecord               ()             { m_ctuRecord.clear(); }
   void setAffineModeSelected        ( bool flag) { m_affineModeSelected = flag; }
   void resetAffineMVList() { m_affMVListIdx = 0; m_affMVListSize = 0; }
+#if GDR_ENABLED
+  void savePrevAffMVInfo(int idx, AffineMVInfo &tmpMVInfo, AffineMVInfoSolid &tmpMVInfoSolid, bool& isSaved)
+#else
   void savePrevAffMVInfo(int idx, AffineMVInfo &tmpMVInfo, bool& isSaved)
+#endif
   {
     if (m_affMVListSize > idx)
     {
       tmpMVInfo = m_affMVList[(m_affMVListIdx - 1 - idx + m_affMVListMaxSize) % m_affMVListMaxSize];
+#if GDR_ENABLED
+      tmpMVInfoSolid = m_affMVListSolid[(m_affMVListIdx - 1 - idx + m_affMVListMaxSize) % m_affMVListMaxSize];
+#endif
       isSaved = true;
     }
     else
       isSaved = false;
   }
+#if GDR_ENABLED
+  void addAffMVInfo(AffineMVInfo &tmpMVInfo, AffineMVInfoSolid &tmpMVInfoSolid)
+#else
   void addAffMVInfo(AffineMVInfo &tmpMVInfo)
+#endif
   {
     int j = 0;
     AffineMVInfo *prevInfo = nullptr;
+#if GDR_ENABLED
+    AffineMVInfoSolid *prevInfoSolid = nullptr;
+#endif
     for (; j < m_affMVListSize; j++)
     {
       prevInfo = m_affMVList + ((m_affMVListIdx - j - 1 + m_affMVListMaxSize) % (m_affMVListMaxSize));
+#if GDR_ENABLED
+      prevInfoSolid = m_affMVListSolid + ((m_affMVListIdx - j - 1 + m_affMVListMaxSize) % (m_affMVListMaxSize));
+#endif
       if ((tmpMVInfo.x == prevInfo->x) && (tmpMVInfo.y == prevInfo->y) && (tmpMVInfo.w == prevInfo->w) && (tmpMVInfo.h == prevInfo->h))
       {
         break;
       }
     }
+#if GDR_ENABLED
     if (j < m_affMVListSize)
+    {
       *prevInfo = tmpMVInfo;
+      *prevInfoSolid = tmpMVInfoSolid;
+    }
+#else
+    if (j < m_affMVListSize)
+    {
+      *prevInfo = tmpMVInfo;
+    }
+#endif
     else
     {
       m_affMVList[m_affMVListIdx] = tmpMVInfo;
+#if GDR_ENABLED
+      m_affMVListSolid[m_affMVListIdx] = tmpMVInfoSolid;
+#endif
       m_affMVListIdx = (m_affMVListIdx + 1) % m_affMVListMaxSize;
       m_affMVListSize = std::min(m_affMVListSize + 1, m_affMVListMaxSize);
     }
@@ -312,13 +359,21 @@ public:
     }
   }
   void resetSavedAffineMotion();
+#if GDR_ENABLED
+  void storeAffineMotion(Mv acAffineMv[2][3], bool acAffineMvSolid[2][3], int16_t affineRefIdx[2], EAffineModel affineType, int bcwIdx);
+#else
   void storeAffineMotion( Mv acAffineMv[2][3], int16_t affineRefIdx[2], EAffineModel affineType, int bcwIdx );
+#endif
   bool searchBv(PredictionUnit& pu, int xPos, int yPos, int width, int height, int picWidth, int picHeight, int xBv, int yBv, int ctuSize);
   void setClipMvInSubPic(bool flag) { m_clipMvInSubPic = flag; }
 protected:
 
   /// sub-function for motion vector refinement used in fractional-pel accuracy
+#if GDR_ENABLED
+  Distortion  xPatternRefinement(const PredictionUnit& pu, RefPicList eRefPicList, int iRefIdx, const CPelBuf* pcPatternKey, Mv baseRefMv, int iFrac, Mv& rcMvFrac, bool bAllowUseOfHadamard, bool& rbCleanCandExist);
+#else
   Distortion  xPatternRefinement    ( const CPelBuf* pcPatternKey, Mv baseRefMv, int iFrac, Mv& rcMvFrac, bool bAllowUseOfHadamard );
+#endif
 
    typedef struct
    {
@@ -400,6 +455,20 @@ protected:
                                     Distortion*           puiDistBiP = NULL
                                   );
 
+ #if GDR_ENABLED
+  void xCheckBestMVP(
+    PredictionUnit &pu,
+    RefPicList  eRefPicList,
+    Mv          cMv,
+    Mv&         rcMvPred,
+    int&        riMVPIdx,
+    AMVPInfo&   amvpInfo,
+    uint32_t&   ruiBits,
+    Distortion& ruiCost
+    ,
+    const uint8_t  imv
+  );
+#else
   void xCheckBestMVP              ( RefPicList  eRefPicList,
                                     Mv          cMv,
                                     Mv&         rcMvPred,
@@ -410,6 +479,7 @@ protected:
                                     ,
                                     const uint8_t  imv
                                   );
+#endif
 
   Distortion xGetTemplateCost     ( const PredictionUnit& pu,
                                     PelUnitBuf&           origBuf,
@@ -432,6 +502,22 @@ protected:
   // motion estimation
   // -------------------------------------------------------------------------------------------------------------------
 
+#if GDR_ENABLED
+  void xMotionEstimation          ( PredictionUnit&       pu,
+                                    PelUnitBuf&           origBuf,
+                                    RefPicList            eRefPicList,
+                                    Mv&                   rcMvPred,                                    
+                                    int                   iRefIdxPred,
+                                    Mv&                   rcMv,
+                                    bool&                 rcMvSolid,
+                                    int&                  riMVPIdx,
+                                    uint32_t&             ruiBits,
+                                    Distortion&           ruiCost,
+                                    const AMVPInfo&       amvpInfo,
+                                    bool&                 rbCleanCandExist,
+                                    bool                  bBi = false
+                                  );
+#else
   void xMotionEstimation          ( PredictionUnit&       pu,
                                     PelUnitBuf&           origBuf,
                                     RefPicList            eRefPicList,
@@ -444,7 +530,7 @@ protected:
                                     const AMVPInfo&       amvpInfo,
                                     bool                  bBi = false
                                   );
-
+#endif
   void xTZSearch                  ( const PredictionUnit& pu,
                                     RefPicList            eRefPicList,
                                     int                   iRefIdxPred,
@@ -470,6 +556,10 @@ protected:
                                     const int             iSrchRng,
                                     SearchRange&          sr
                                   , IntTZSearchStruct &  cStruct
+#if GDR_ENABLED
+                                  , RefPicList eRefPicList
+                                  , int iRefIdx
+#endif
                                   );
 
   void xPatternSearchFast         ( const PredictionUnit& pu,
@@ -495,6 +585,11 @@ protected:
                                     Distortion&         ruiCost,
                                     const AMVPInfo&     amvpInfo,
                                     double              fWeight
+#if GDR_ENABLED
+                                    ,RefPicList         eRefPicList
+                                    ,int                iRefIdxPred
+                                    , bool&             rbCleanCandExist
+#endif
                                   );
 
   void xPatternSearchFracDIF      ( const PredictionUnit& pu,
@@ -505,6 +600,9 @@ protected:
                                     Mv&                   rcMvHalf,
                                     Mv&                   rcMvQter,
                                     Distortion&           ruiCost
+#if GDR_ENABLED
+                                    ,bool&                rbCleanCandExist
+#endif
                                   );
 
   void xPredAffineInterSearch     ( PredictionUnit&       pu,
@@ -513,13 +611,35 @@ protected:
                                     uint32_t&                 lastMode,
                                     Distortion&           affineCost,
                                     Mv                    hevcMv[2][33]
+#if GDR_ENABLED
+                                  , bool                  hevcMvSolid[2][33]
+#endif
                                   , Mv                    mvAffine4Para[2][33][3]
+#if GDR_ENABLED
+                                  , bool                  mvAffine4ParaSolid[2][33][3]
+#endif
                                   , int                   refIdx4Para[2]
                                   , uint8_t               bcwIdx = BCW_DEFAULT
                                   , bool                  enforceBcwPred = false
                                   , uint32_t              bcwIdxBits = 0
                                   );
 
+#if GDR_ENABLED
+  void xAffineMotionEstimation    ( PredictionUnit& pu,
+                                    PelUnitBuf&     origBuf,
+                                    RefPicList      eRefPicList,
+                                    Mv              acMvPred[3],                                    
+                                    int             iRefIdxPred,
+                                    Mv              acMv[3],
+                                    bool            acMvSolid[3],
+                                    uint32_t&       ruiBits,
+                                    Distortion&     ruiCost,
+                                    int&            mvpIdx,
+                                    const AffineAMVPInfo& aamvpi,
+                                    bool&           rbCleanCandExist,
+                                    bool            bBi = false
+  );
+#else
   void xAffineMotionEstimation    ( PredictionUnit& pu,
                                     PelUnitBuf&     origBuf,
                                     RefPicList      eRefPicList,
@@ -532,6 +652,7 @@ protected:
                                     const AffineAMVPInfo& aamvpi,
                                     bool            bBi = false
                                   );
+#endif
 
   void xEstimateAffineAMVP        ( PredictionUnit&  pu,
                                     AffineAMVPInfo&  affineAMVPInfo,
@@ -542,23 +663,50 @@ protected:
                                     Distortion*      puiDistBiP
                                   );
 
+#if GDR_ENABLED
+  Distortion xGetAffineTemplateCost(PredictionUnit& pu, PelUnitBuf& origBuf, PelUnitBuf& predBuf, Mv acMvCand[3], int iMVPIdx, int iMVPNum, RefPicList eRefPicList, int iRefIdx, bool& rbOk);
+#else
   Distortion xGetAffineTemplateCost( PredictionUnit& pu, PelUnitBuf& origBuf, PelUnitBuf& predBuf, Mv acMvCand[3], int iMVPIdx, int iMVPNum, RefPicList eRefPicList, int iRefIdx );
+#endif
 
   void xCopyAffineAMVPInfo        ( AffineAMVPInfo& src, AffineAMVPInfo& dst );
   void xCheckBestAffineMVP        ( PredictionUnit &pu, AffineAMVPInfo &affineAMVPInfo, RefPicList eRefPicList, Mv acMv[3], Mv acMvPred[3], int& riMVPIdx, uint32_t& ruiBits, Distortion& ruiCost );
 
   Distortion xGetSymmetricCost( PredictionUnit& pu, PelUnitBuf& origBuf, RefPicList eCurRefPicList, const MvField& cCurMvField, MvField& cTarMvField , int bcwIdx );
 
+#if GDR_ENABLED
+  Distortion xSymmeticRefineMvSearch( 
+    PredictionUnit& pu, PelUnitBuf& origBuf, 
+    Mv& rcMvCurPred, Mv& rcMvTarPred,
+    RefPicList eRefPicList, MvField& rCurMvField, MvField& rTarMvField, 
+    Distortion uiMinCost, int searchPattern, int nSearchStepShift, uint32_t uiMaxSearchRounds , int bcwIdx, bool& rbOk );
+#else
   Distortion xSymmeticRefineMvSearch( PredictionUnit& pu, PelUnitBuf& origBuf, Mv& rcMvCurPred, Mv& rcMvTarPred
     , RefPicList eRefPicList, MvField& rCurMvField, MvField& rTarMvField, Distortion uiMinCost, int searchPattern, int nSearchStepShift, uint32_t uiMaxSearchRounds , int bcwIdx );
+#endif
 
+#if GDR_ENABLED
+  bool xSymmetricMotionEstimation( PredictionUnit& pu, PelUnitBuf& origBuf, Mv& rcMvCurPred, Mv& rcMvTarPred,
+  RefPicList eRefPicList, MvField& rCurMvField, MvField& rTarMvField, Distortion& ruiCost, int bcwIdx, bool& ruiCostOk );
+#else
   void xSymmetricMotionEstimation( PredictionUnit& pu, PelUnitBuf& origBuf, Mv& rcMvCurPred, Mv& rcMvTarPred, RefPicList eRefPicList, MvField& rCurMvField, MvField& rTarMvField, Distortion& ruiCost, int bcwIdx );
+#endif
 
+#if GDR_ENABLED
+  bool xReadBufferedAffineUniMv(PredictionUnit& pu, RefPicList eRefPicList, int32_t iRefIdx, Mv acMvPred[3], Mv acMv[3], bool acMvSolid[3], uint32_t& ruiBits, Distortion& ruiCost
+    , int& mvpIdx, const AffineAMVPInfo& aamvpi
+  );
+#else
   bool xReadBufferedAffineUniMv   ( PredictionUnit& pu, RefPicList eRefPicList, int32_t iRefIdx, Mv acMvPred[3], Mv acMv[3], uint32_t& ruiBits, Distortion& ruiCost
                                     , int& mvpIdx, const AffineAMVPInfo& aamvpi
   );
+#endif
   double xGetMEDistortionWeight   ( uint8_t bcwIdx, RefPicList eRefPicList);
+#if GDR_ENABLED
+  bool xReadBufferedUniMv         (PredictionUnit& pu, RefPicList eRefPicList, int32_t iRefIdx, Mv& pcMvPred, Mv& rcMv, bool& rcMvSolid, uint32_t& ruiBits, Distortion& ruiCost);
+#else
   bool xReadBufferedUniMv         ( PredictionUnit& pu, RefPicList eRefPicList, int32_t iRefIdx, Mv& pcMvPred, Mv& rcMv, uint32_t& ruiBits, Distortion& ruiCost);
+#endif
 
   void xClipMv                    ( Mv& rcMv, const struct Position& pos, const struct Size& size, const class SPS& sps, const class PPS& pps );
 
@@ -574,6 +722,9 @@ public:
     AMVPInfo amvpInfo[2][33],
     int32_t bcwIdx,
     Mv cMvPredSym[2],
+#if GDR_ENABLED
+    bool cMvPredSymSolid[2],
+#endif
     int32_t mvpIdxSym[2],
     Distortion& bestCost,
     bool skip = false
diff --git a/source/Lib/EncoderLib/IntraSearch.cpp b/source/Lib/EncoderLib/IntraSearch.cpp
index 890251ec1f39f83e03efd64026710a4e56b81384..e715da7a99ea87e028443910c8eabcca521d1282 100644
--- a/source/Lib/EncoderLib/IntraSearch.cpp
+++ b/source/Lib/EncoderLib/IntraSearch.cpp
@@ -367,6 +367,191 @@ double IntraSearch::findInterCUCost( CodingUnit &cu )
   return COST_UNKNOWN;
 }
 
+#if GDR_ENABLED
+int IntraSearch::getNumTopRecons(PredictionUnit &pu, int luma_dirMode, bool isChroma)
+{
+  int w = isChroma ? pu.Cb().width  : pu.Y().width;
+  int h = isChroma ? pu.Cb().height : pu.Y().height;
+
+  int numOfTopRecons = w; 
+
+  static const int angTable[32] = { 0,    1,    2,    3,    4,    6,     8,   10,   12,   14,   16,   18,   20,   23,   26,   29,   32,   35,   39,  45,  51,  57,  64,  73,  86, 102, 128, 171, 256, 341, 512, 1024 };
+  static const int invAngTable[32] = {
+    0,   16384, 8192, 5461, 4096, 2731, 2048, 1638, 1365, 1170, 1024, 910, 819, 712, 630, 565,
+    512, 468,   420,  364,  321,  287,  256,  224,  191,  161,  128,  96,  64,  48,  32,  16
+  };   // (512 * 32) / Angle
+
+  const int refIdx             = pu.multiRefIdx;
+  const int predModeIntra      = getModifiedWideAngle(w, h, luma_dirMode);
+  const int isModeVer          = predModeIntra >= DIA_IDX;
+  const int intraPredAngleMode = (isModeVer) ? predModeIntra - VER_IDX : -(predModeIntra - HOR_IDX);
+
+  const int absAngMode         = abs(intraPredAngleMode);
+  const int signAng            = intraPredAngleMode < 0 ? -1 : 1;
+  const int absAng             = (luma_dirMode > DC_IDX && luma_dirMode < NUM_LUMA_MODE) ? angTable[absAngMode] : 0;
+
+  const int invAngle           = invAngTable[absAngMode];
+  const int intraPredAngle     = signAng * absAng;
+
+  const int sideSize = isModeVer ? h : w;
+  const int maxScale = 2;
+
+  const int angularScale = std::min(maxScale, floorLog2(sideSize) - (floorLog2(3 * invAngle - 2) - 8));;  
+
+  bool applyPDPC;
+
+
+  // 1.0 derive PDPC  
+  applyPDPC  = (refIdx == 0) ? true : false;  
+  if (luma_dirMode > DC_IDX && luma_dirMode < NUM_LUMA_MODE) 
+  {
+    if (intraPredAngleMode < 0)
+    {
+      applyPDPC &= false;
+    }
+    else if (intraPredAngleMode > 0)
+    {
+      applyPDPC &= (angularScale >= 0);
+    }        
+  }
+
+  // 2.0 calculate number of recons
+  switch (luma_dirMode) 
+  {
+  case PLANAR_IDX:
+    numOfTopRecons = applyPDPC ? (w + 1) : (w + 1);
+    break;
+
+  case DC_IDX:
+    numOfTopRecons = applyPDPC ? (w) : (w);
+    break;
+
+  case HOR_IDX:
+    numOfTopRecons = applyPDPC ? (w) : (w);
+    break;
+
+  case VER_IDX:
+    numOfTopRecons = applyPDPC ? (w) : (w);
+    break;
+
+  default:
+    // 2..66
+    // note: There should be a way to reduce the number of top recons, in case of non PDPC
+    applyPDPC |= isChroma;
+
+    if (predModeIntra >= DIA_IDX) 
+    {
+      if (intraPredAngle < 0) 
+      {
+        numOfTopRecons = (applyPDPC) ? (w + w) : (w + 1);
+      }
+      else 
+      {
+        numOfTopRecons = (applyPDPC) ? (w + w) : (w + w);
+      }
+    }
+    else 
+    {
+      if (intraPredAngle < 0) 
+      {
+        numOfTopRecons = (applyPDPC) ? (w + w) : (w);
+      }
+      else 
+      {
+        numOfTopRecons = (applyPDPC) ? (w + w) : (w);
+      }
+    }
+    break;
+  }
+
+  return numOfTopRecons;
+}
+
+bool IntraSearch::isValidIntraPredLuma(PredictionUnit &pu, int luma_dirMode)
+{
+  bool isValid  = true;  
+  PicHeader *ph = pu.cs->picHeader;
+
+  if (ph->getInGdrPeriod()) 
+  {
+    int x = pu.Y().x;
+
+    // count num of recons on the top
+    int virX             = ph->getVirtualBoundariesPosX(0);   
+    int numOfTopRecons = getNumTopRecons(pu, luma_dirMode, false);    
+        
+    // check if recon is out of boundary
+    if (x < virX && virX < (x + numOfTopRecons)) 
+    {
+      isValid = false;
+    }    
+  }
+  
+  return isValid;
+}
+
+bool IntraSearch::isValidIntraPredChroma(PredictionUnit &pu, int luma_dirMode, int chroma_dirMode)
+{
+  bool isValid = true;
+  CodingStructure *cs = pu.cs;
+  PicHeader       *ph = cs->picHeader;
+
+  if (ph->getInGdrPeriod()) 
+  {
+    // note: chroma cordinate
+    int cbX = pu.Cb().x;
+    //int cbY = pu.Cb().y;
+    int cbW = pu.Cb().width;
+    int cbH = pu.Cb().height;
+
+    int chromaScaleX = getComponentScaleX(COMPONENT_Cb, cs->area.chromaFormat);
+    int chromaScaleY = getComponentScaleY(COMPONENT_Cb, cs->area.chromaFormat);
+
+    int lumaX = cbX << chromaScaleX;
+    // int lumaY = cbY << chromaScaleY;
+    int lumaW = cbW << chromaScaleX;
+    int lumaH = cbH << chromaScaleY;
+   
+    int numOfTopRecons = lumaW;
+    int virX           = ph->getVirtualBoundariesPosX(0);
+
+    // count num of recons on the top
+    switch (chroma_dirMode) 
+    {
+
+    case LM_CHROMA_IDX :
+      numOfTopRecons = lumaW;
+      break;
+
+    case MDLM_L_IDX :
+      numOfTopRecons = lumaW;
+      break;
+
+    // note: could reduce the actual #of 
+    case MDLM_T_IDX:
+      numOfTopRecons = (lumaW + lumaH);
+      break;
+
+    case DM_CHROMA_IDX :
+      numOfTopRecons = getNumTopRecons(pu, luma_dirMode, true) << chromaScaleX;
+      break;
+
+    default :
+      numOfTopRecons = getNumTopRecons(pu, chroma_dirMode, true) << chromaScaleX;
+      break;
+    }
+
+    // check if recon is out of boundary
+    if (lumaX < virX && virX < (lumaX + numOfTopRecons)) 
+    {
+      isValid = false;
+    }       
+  }
+  
+  return isValid;
+}
+#endif
+
 bool IntraSearch::estIntraPredLumaQT(CodingUnit &cu, Partitioner &partitioner, const double bestCostSoFar, bool mtsCheckRangeFlag, int mtsFirstCheckId, int mtsLastCheckId, bool moreProbMTSIdxFirst, CodingStructure* bestCS)
 {
   CodingStructure       &cs            = *cu.cs;
@@ -452,6 +637,9 @@ bool IntraSearch::estIntraPredLumaQT(CodingUnit &cu, Partitioner &partitioner, c
   static_vector<double, FAST_UDI_MAX_RDMODE_NUM> CandHadList;
 
   auto &pu = *cu.firstPU;
+#if GDR_ENABLED  
+  const bool isEncodeClean = cs.pcv->isEncoder && ((cs.picHeader->getInGdrPeriod() && cs.isClean(pu.Y().topRight(), CHANNEL_TYPE_LUMA)) || (cs.picHeader->getNumVerVirtualBoundaries() == 0));
+#endif
   bool validReturn = false;
   {
     CandHadList.clear();
@@ -586,10 +774,30 @@ bool IntraSearch::estIntraPredLumaQT(CodingUnit &cu, Partitioner &partitioner, c
 
               DTRACE(g_trace_ctx, D_INTRA_COST, "IntraHAD: %u, %llu, %f (%d)\n", minSadHad, fracModeBits, cost, uiMode);
 
+#if GDR_ENABLED
+              if (isEncodeClean) 
+              {
+                if (isValidIntraPredLuma(pu, uiMode)) 
+                {
+                  updateCandList(ModeInfo(false, false, 0, NOT_INTRA_SUBPARTITIONS, uiMode), cost, uiRdModeList,
+                    CandCostList, numModesForFullRD);
+                  updateCandList(ModeInfo(false, false, 0, NOT_INTRA_SUBPARTITIONS, uiMode), double(minSadHad), uiHadModeList,
+                    CandHadList, numHadCand);
+                }
+              }
+              else 
+              {
+                updateCandList(ModeInfo(false, false, 0, NOT_INTRA_SUBPARTITIONS, uiMode), cost, uiRdModeList,
+                  CandCostList, numModesForFullRD);
+                updateCandList(ModeInfo(false, false, 0, NOT_INTRA_SUBPARTITIONS, uiMode), double(minSadHad),
+                  uiHadModeList, CandHadList, numHadCand);
+              }
+#else
               updateCandList(ModeInfo(false, false, 0, NOT_INTRA_SUBPARTITIONS, uiMode), cost, uiRdModeList,
                              CandCostList, numModesForFullRD);
               updateCandList(ModeInfo(false, false, 0, NOT_INTRA_SUBPARTITIONS, uiMode), double(minSadHad),
                              uiHadModeList, CandHadList, numHadCand);
+#endif
             }
             if (!sps.getUseMIP() && LFNSTSaveFlag)
             {
@@ -619,7 +827,17 @@ bool IntraSearch::estIntraPredLumaQT(CodingUnit &cu, Partitioner &partitioner, c
             static_vector<ModeInfo, FAST_UDI_MAX_RDMODE_NUM> parentCandList = uiRdModeList;
 
             // Second round of SATD for extended Angular modes
+#if GDR_ENABLED
+            int nn = numModesForFullRD;
+            if (isEncodeClean) 
+            {
+              nn = std::min((int)numModesForFullRD, (int)parentCandList.size());
+            }
+
+            for (int modeIdx = 0; modeIdx < nn; modeIdx++)
+#else
             for (int modeIdx = 0; modeIdx < numModesForFullRD; modeIdx++)
+#endif
             {
               unsigned parentMode = parentCandList[modeIdx].modeId;
               if (parentMode > (DC_IDX + 1) && parentMode < (NUM_LUMA_MODE - 1))
@@ -652,10 +870,30 @@ bool IntraSearch::estIntraPredLumaQT(CodingUnit &cu, Partitioner &partitioner, c
 
                     double cost = (double) minSadHad + (double) fracModeBits * sqrtLambdaForFirstPass;
 
+#if GDR_ENABLED
+                    if (isEncodeClean) 
+                    {
+                      if (isValidIntraPredLuma(pu, mode)) 
+                      {
+                        updateCandList(ModeInfo(false, false, 0, NOT_INTRA_SUBPARTITIONS, mode), cost, uiRdModeList,
+                          CandCostList, numModesForFullRD);
+                        updateCandList(ModeInfo(false, false, 0, NOT_INTRA_SUBPARTITIONS, mode), double(minSadHad),
+                          uiHadModeList, CandHadList, numHadCand);
+                      }
+                    }
+                    else 
+                    {
+                      updateCandList(ModeInfo(false, false, 0, NOT_INTRA_SUBPARTITIONS, mode), cost, uiRdModeList,
+                        CandCostList, numModesForFullRD);
+                      updateCandList(ModeInfo(false, false, 0, NOT_INTRA_SUBPARTITIONS, mode), double(minSadHad),
+                        uiHadModeList, CandHadList, numHadCand);
+                    }
+#else
                     updateCandList(ModeInfo(false, false, 0, NOT_INTRA_SUBPARTITIONS, mode), cost, uiRdModeList,
                                    CandCostList, numModesForFullRD);
                     updateCandList(ModeInfo(false, false, 0, NOT_INTRA_SUBPARTITIONS, mode), double(minSadHad),
                                    uiHadModeList, CandHadList, numHadCand);
+#endif
 
                     bSatdChecked[mode] = true;
                   }
@@ -703,14 +941,41 @@ bool IntraSearch::estIntraPredLumaQT(CodingUnit &cu, Partitioner &partitioner, c
                   uint64_t fracModeBits = xFracModeBitsIntra(pu, mode, CHANNEL_TYPE_LUMA);
 
                   double cost = (double) minSadHad + (double) fracModeBits * sqrtLambdaForFirstPass;
+#if GDR_ENABLED
+                  if (isEncodeClean) 
+                  {
+                    if (isValidIntraPredLuma(pu, mode)) 
+                    {
+                      updateCandList(ModeInfo(false, false, multiRefIdx, NOT_INTRA_SUBPARTITIONS, mode), cost, uiRdModeList,
+                        CandCostList, numModesForFullRD);
+                      updateCandList(ModeInfo(false, false, multiRefIdx, NOT_INTRA_SUBPARTITIONS, mode), double(minSadHad),
+                        uiHadModeList, CandHadList, numHadCand);
+                    }
+                  }
+                  else 
+                  {
+                    updateCandList(ModeInfo(false, false, multiRefIdx, NOT_INTRA_SUBPARTITIONS, mode), cost, uiRdModeList,
+                      CandCostList, numModesForFullRD);
+                    updateCandList(ModeInfo(false, false, multiRefIdx, NOT_INTRA_SUBPARTITIONS, mode), double(minSadHad),
+                      uiHadModeList, CandHadList, numHadCand);
+                  }
+#else
                   updateCandList(ModeInfo(false, false, multiRefIdx, NOT_INTRA_SUBPARTITIONS, mode), cost, uiRdModeList,
                                  CandCostList, numModesForFullRD);
                   updateCandList(ModeInfo(false, false, multiRefIdx, NOT_INTRA_SUBPARTITIONS, mode), double(minSadHad),
                                  uiHadModeList, CandHadList, numHadCand);
+#endif
                 }
               }
             }
+#if GDR_ENABLED
+            if (!isEncodeClean) 
+            {
+              CHECKD(uiRdModeList.size() != numModesForFullRD, "Error: RD mode list size");
+            }
+#else
             CHECKD(uiRdModeList.size() != numModesForFullRD, "Error: RD mode list size");
+#endif
 
             if (LFNSTSaveFlag && testMip
                 && !allowLfnstWithMip(cu.firstPU->lumaSize()))   // save a different set for the next run
@@ -781,10 +1046,30 @@ bool IntraSearch::estIntraPredLumaQT(CodingUnit &cu, Partitioner &partitioner, c
                 DTRACE(g_trace_ctx, D_INTRA_COST, "IntraMIP: %u, %llu, %f (%d)\n", minSadHad, fracModeBits, cost,
                        uiModeFull);
 
+#if GDR_ENABLED
+                if (isEncodeClean) 
+                {
+                  if (isValidIntraPredLuma(pu, uiMode)) 
+                  {
+                    updateCandList(ModeInfo(true, isTransposed, 0, NOT_INTRA_SUBPARTITIONS, uiMode), cost, uiRdModeList,
+                      CandCostList, numModesForFullRD + 1);
+                    updateCandList(ModeInfo(true, isTransposed, 0, NOT_INTRA_SUBPARTITIONS, uiMode),
+                      0.8 * double(minSadHad), uiHadModeList, CandHadList, numHadCand);
+                  }
+                }
+                else 
+                {
+                  updateCandList(ModeInfo(true, isTransposed, 0, NOT_INTRA_SUBPARTITIONS, uiMode), cost, uiRdModeList,
+                    CandCostList, numModesForFullRD + 1);
+                  updateCandList(ModeInfo(true, isTransposed, 0, NOT_INTRA_SUBPARTITIONS, uiMode),
+                    0.8 * double(minSadHad), uiHadModeList, CandHadList, numHadCand);
+                }
+#else
                 updateCandList(ModeInfo(true, isTransposed, 0, NOT_INTRA_SUBPARTITIONS, uiMode), cost, uiRdModeList,
                                CandCostList, numModesForFullRD + 1);
                 updateCandList(ModeInfo(true, isTransposed, 0, NOT_INTRA_SUBPARTITIONS, uiMode),
                                0.8 * double(minSadHad), uiHadModeList, CandHadList, numHadCand);
+#endif
               }
 
               const double thresholdHadCost = 1.0 + 1.4 / sqrt((double) (pu.lwidth() * pu.lheight()));
@@ -828,16 +1113,38 @@ bool IntraSearch::estIntraPredLumaQT(CodingUnit &cu, Partitioner &partitioner, c
               bool     mostProbableModeIncluded = false;
               ModeInfo mostProbableMode( false, false, 0, NOT_INTRA_SUBPARTITIONS, uiPreds[j] );
 
+#if GDR_ENABLED
+              int nn = numModesForFullRD;
+              if (isEncodeClean) 
+              {
+                nn = std::min((int)numModesForFullRD, (int)uiRdModeList.size());
+              }
+
+              for (int i = 0; i < nn; i++)
+#else
               for (int i = 0; i < numModesForFullRD; i++)
+#endif
               {
                 mostProbableModeIncluded |= (mostProbableMode == uiRdModeList[i]);
               }
+#if GDR_ENABLED
+              if (!isEncodeClean) 
+              {
+                if (!mostProbableModeIncluded)
+                {
+                  numModesForFullRD++;
+                  uiRdModeList.push_back(mostProbableMode);
+                  CandCostList.push_back(0);
+                }
+              }
+#else
               if (!mostProbableModeIncluded)
               {
                 numModesForFullRD++;
                 uiRdModeList.push_back(mostProbableMode);
                 CandCostList.push_back(0);
               }
+#endif
             }
             if (saveDataForISP)
             {
@@ -851,10 +1158,20 @@ bool IntraSearch::estIntraPredLumaQT(CodingUnit &cu, Partitioner &partitioner, c
                 {
                   mostProbableModeIncluded |= (mostProbableMode == m_ispCandListHor[i]);
                 }
+#if GDR_ENABLED
+                if (!isEncodeClean) 
+                {
+                  if (!mostProbableModeIncluded)
+                  {
+                    m_ispCandListHor.push_back(mostProbableMode);
+                  }
+                }
+#else
                 if (!mostProbableModeIncluded)
                 {
                   m_ispCandListHor.push_back(mostProbableMode);
                 }
+#endif
               }
             }
           }
@@ -899,7 +1216,14 @@ bool IntraSearch::estIntraPredLumaQT(CodingUnit &cu, Partitioner &partitioner, c
         }
       }
 
+#if GDR_ENABLED
+      if (!isEncodeClean) 
+      {
+        CHECK(numModesForFullRD != uiRdModeList.size(), "Inconsistent state!");
+      }
+#else
       CHECK(numModesForFullRD != uiRdModeList.size(), "Inconsistent state!");
+#endif
 
       // after this point, don't use numModesForFullRD
 
@@ -965,12 +1289,24 @@ bool IntraSearch::estIntraPredLumaQT(CodingUnit &cu, Partitioner &partitioner, c
     if ( testISP )
     {
       // we reserve positions for ISP in the common full RD list
+#if GDR_ENABLED
+      if (!isEncodeClean) 
+      {
+        const int maxNumRDModesISP = sps.getUseLFNST() ? 16 * NUM_LFNST_NUM_PER_SET : 16;
+        m_curIspLfnstIdx = 0;
+        for (int i = 0; i < maxNumRDModesISP; i++)
+        {
+          uiRdModeList.push_back(ModeInfo(false, false, 0, INTRA_SUBPARTITIONS_RESERVED, 0));
+        }
+      }
+#else
       const int maxNumRDModesISP = sps.getUseLFNST() ? 16 * NUM_LFNST_NUM_PER_SET : 16;
       m_curIspLfnstIdx = 0;
       for (int i = 0; i < maxNumRDModesISP; i++)
       {
         uiRdModeList.push_back( ModeInfo( false, false, 0, INTRA_SUBPARTITIONS_RESERVED, 0 ) );
       }
+#endif
     }
 
     //===== check modes (using r-d costs) =====
@@ -1462,7 +1798,11 @@ void IntraSearch::estIntraPredChromaQT( CodingUnit &cu, Partitioner &partitioner
         double    dCost   = m_pcRdCost->calcRdCost( fracBits, uiDist - baseDist );
 
         //----- compare -----
+#if GDR_ENABLED
+        if (dCost < dBestCost && isValidIntraPredChroma(pu, (int)PU::getCoLocatedIntraLumaMode(pu), chromaIntraMode))
+#else
         if( dCost < dBestCost )
+#endif
         {
           if( lumaUsesISP && dCost < bestCostSoFar )
           {
diff --git a/source/Lib/EncoderLib/IntraSearch.h b/source/Lib/EncoderLib/IntraSearch.h
index 8f53122f3792bcb53eacbd25ddbc0e7c1abd4861..a1feb824c894bed1cdbed2790cd3eed5550d109c 100644
--- a/source/Lib/EncoderLib/IntraSearch.h
+++ b/source/Lib/EncoderLib/IntraSearch.h
@@ -440,6 +440,11 @@ public:
   void invalidateBestRdModeFirstColorSpace();
   void setSavedRdModeIdx(int idx) { m_savedRdModeIdx = idx; }
 
+#if GDR_ENABLED
+  int  getNumTopRecons(PredictionUnit &pu, int luma_dirMode, bool isChroma);
+  bool isValidIntraPredLuma(PredictionUnit &pu, int luma_dirMode);
+  bool isValidIntraPredChroma(PredictionUnit &pu, int luma_dirMode, int chroma_dirMode);
+#endif
 protected:
 
   // -------------------------------------------------------------------------------------------------------------------
diff --git a/source/Lib/EncoderLib/VLCWriter.cpp b/source/Lib/EncoderLib/VLCWriter.cpp
index 8e8ded9717e15f6600c4819b67b662bcc4b0b44f..33c5d432665d694509ecb0097090e5ac374e4c1f 100644
--- a/source/Lib/EncoderLib/VLCWriter.cpp
+++ b/source/Lib/EncoderLib/VLCWriter.cpp
@@ -1578,6 +1578,11 @@ WRITE_FLAG(picHeader->getGdrOrIrapPicFlag(), "ph_gdr_or_irap_pic_flag");
   {
     picHeader->setRecoveryPocCnt( -1 );
   }
+#if GDR_ENC_TRACE
+  printf("-gdr_pic_flag:%d\n", picHeader->getGdrPicFlag());  
+  printf("-recovery_poc_cnt:%d\n", picHeader->getRecoveryPocCnt());
+  printf("-InGdrPeriod:%d\n", picHeader->getInGdrPeriod());
+#endif
   // PH extra bits are not written in the reference encoder
   // as these bits are reserved for future extensions
   // for( i = 0; i < NumExtraPhBits; i++ )
@@ -1673,6 +1678,10 @@ WRITE_FLAG(picHeader->getGdrOrIrapPicFlag(), "ph_gdr_or_irap_pic_flag");
     picHeader->setLmcsEnabledFlag(false);
     picHeader->setLmcsChromaResidualScaleFlag(false);
   }
+#if GDR_ENC_TRACE    
+  printf("-pic_lmcs_enabled_flag:%d\n", picHeader->getLmcsEnabledFlag() ? 1 : 0);
+  printf("-pic_chroma_residual_scale_flag:%d\n", picHeader->getLmcsChromaResidualScaleFlag() ? 1 : 0);
+#endif
 
   // quantization scaling lists
   if( sps->getScalingListFlag() )
@@ -1696,6 +1705,37 @@ WRITE_FLAG(picHeader->getGdrOrIrapPicFlag(), "ph_gdr_or_irap_pic_flag");
     WRITE_FLAG( picHeader->getVirtualBoundariesPresentFlag(), "ph_virtual_boundaries_present_flag" );
     if( picHeader->getVirtualBoundariesPresentFlag() )
     {
+      
+#if GDR_ENABLED
+      int n = picHeader->getNumVerVirtualBoundaries();
+      for (unsigned i = 0; i < n; i++) 
+      {
+        if (picHeader->getVirtualBoundariesPosX(i) == pps->getPicWidthInLumaSamples()) 
+        {
+          n = n - 1;
+        }
+      }
+
+      WRITE_UVLC(n, "ph_num_ver_virtual_boundaries");
+
+      if (pps->getPicWidthInLumaSamples() <= 8)
+      {
+        CHECK(picHeader->getNumVerVirtualBoundaries() != 0, "PH: When picture width is less than or equal to 8, the number of vertical virtual boundaries shall be equal to 0");
+      }
+      else
+      {
+        CHECK(picHeader->getNumVerVirtualBoundaries() > 3, "PH: The number of vertical virtual boundaries shall be in the range of 0 to 3");
+      }
+
+      for (unsigned i = 0; i < picHeader->getNumVerVirtualBoundaries(); i++)
+      {
+        if (picHeader->getVirtualBoundariesPosX(i) != pps->getPicWidthInLumaSamples())
+        {
+          WRITE_UVLC((picHeader->getVirtualBoundariesPosX(i) >> 3) - 1, "ph_virtual_boundary_pos_x_minus1[i]");      
+          CHECK(((picHeader->getVirtualBoundariesPosX(i) >> 3) - 1) > (((pps->getPicWidthInLumaSamples() + 7) >> 3) - 2), "The value of ph_virtual_boundary_pos_x_minus1[ i ] shall be in the range of 0 to Ceil( pps_pic_width_in_luma_samples / 8 ) - 2, inclusive.");
+        }
+      }
+#else
       WRITE_UVLC(picHeader->getNumVerVirtualBoundaries(), "ph_num_ver_virtual_boundaries");
       if (pps->getPicWidthInLumaSamples() <= 8)
       {
@@ -1710,6 +1750,7 @@ WRITE_FLAG(picHeader->getGdrOrIrapPicFlag(), "ph_gdr_or_irap_pic_flag");
         WRITE_UVLC((picHeader->getVirtualBoundariesPosX(i) >> 3) - 1, "ph_virtual_boundary_pos_x_minus1[i]");
         CHECK(((picHeader->getVirtualBoundariesPosX(i)>>3) - 1) > (((pps->getPicWidthInLumaSamples() + 7) >> 3) - 2), "The value of ph_virtual_boundary_pos_x_minus1[ i ] shall be in the range of 0 to Ceil( pps_pic_width_in_luma_samples / 8 ) - 2, inclusive.");
       }
+#endif
       WRITE_UVLC(picHeader->getNumHorVirtualBoundaries(), "ph_num_hor_virtual_boundaries");
       if (pps->getPicHeightInLumaSamples() <= 8)
       {