diff --git a/.gitattributes b/.gitattributes
index b0f57055dd7623f95d8803b44f7d102eb7ef1dc1..54fda3c6473c15566049239068d7d725d58b5d56 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -493,3 +493,9 @@ models/post_filter/float/overfitted_models_float/nnr_F_SlideShow_37.sadl filter=
 models/post_filter/float/overfitted_models_float/nnr_D_BQSquare_32.sadl filter=lfs diff=lfs merge=lfs -text
 models/post_filter/int16/overfitted_models_int16/nnr_B_BasketBallDrive_27.sadl filter=lfs diff=lfs merge=lfs -text
 models/post_filter/int16/overfitted_models_int16/nnr_C_BasketballDrill_32.sadl filter=lfs diff=lfs merge=lfs -text
+models/super_resolution/Nnsr_ChromaCNNSR_Inter_Intra_float.sadl filter=lfs diff=lfs merge=lfs -text
+models/super_resolution/Nnsr_ChromaCNNSR_Inter_Intra_int16.sadl filter=lfs diff=lfs merge=lfs -text
+models/super_resolution/Nnsr_LumaCNNSR_Inter_float.sadl filter=lfs diff=lfs merge=lfs -text
+models/super_resolution/Nnsr_LumaCNNSR_Inter_int16.sadl filter=lfs diff=lfs merge=lfs -text
+models/super_resolution/Nnsr_LumaCNNSR_Intra_float.sadl filter=lfs diff=lfs merge=lfs -text
+models/super_resolution/Nnsr_LumaCNNSR_Intra_int16.sadl filter=lfs diff=lfs merge=lfs -text
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 175e5ad33c6761b46e9c9878b9c66416197210d6..b62498d973ca6cefea704eb5dbd9789953c5791f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -127,8 +127,7 @@ if( CMAKE_CXX_COMPILER_ID STREQUAL "GNU" )
   endif()
 endif()
 
-set(PATH_TO_DIRECTORY_ROOT_SADL "../../../sadl")
-
+include_directories( "./sadl" )
 
 # modify .lldbinit for lldb custom data formatters
 if( XCODE )
diff --git a/README.md b/README.md
index 5c85ddcd2ba2f064cc39d6d5b90f753cd3482f9a..381fcaabb6133939fa123cf73ea24797c282ed34 100644
--- a/README.md
+++ b/README.md
@@ -419,6 +419,10 @@ please add the following argument when running the VTM-11-NNVC encoder/decoder e
 where `path_to_directory_models_intra` is the path to the directory "models/intra" relatively to the directory from which the
 VTM-11-NNVC encoder/decoder executable is run.
 
+NN-based super resolution
+------------------------------------------------------------------------
+To activate NN-based super resolution, use --NnsrOption=1. And to successfully inference the codec, the decoder should output decoded YUVs.
+For example, the decoder commands should contain "-o /dev/null". 
 
 Content-adaptive post-filter
 ------------------------------------------------------------------------
diff --git a/models/super_resolution/Nnsr_ChromaCNNSR_Inter_Intra_float.sadl b/models/super_resolution/Nnsr_ChromaCNNSR_Inter_Intra_float.sadl
new file mode 100644
index 0000000000000000000000000000000000000000..3c2069a5b2c673a9b25523a412bcbe0d2847d61d
--- /dev/null
+++ b/models/super_resolution/Nnsr_ChromaCNNSR_Inter_Intra_float.sadl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:6faf2337d085a578612fdb231c37683b10c58746b686f4902bb77a02d450240d
+size 6007029
diff --git a/models/super_resolution/Nnsr_ChromaCNNSR_Inter_Intra_int16.sadl b/models/super_resolution/Nnsr_ChromaCNNSR_Inter_Intra_int16.sadl
new file mode 100644
index 0000000000000000000000000000000000000000..d558ce38f42726ebc1f7d5a9aac6da6ba1b62129
--- /dev/null
+++ b/models/super_resolution/Nnsr_ChromaCNNSR_Inter_Intra_int16.sadl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:87674dbee5326dd0bf0e57245f8cf3266f312c8ea52170439cc881beb26939a2
+size 3013371
diff --git a/models/super_resolution/Nnsr_LumaCNNSR_Inter_float.sadl b/models/super_resolution/Nnsr_LumaCNNSR_Inter_float.sadl
new file mode 100644
index 0000000000000000000000000000000000000000..98d9820d9b9ffae0704823843941c8e19f80593b
--- /dev/null
+++ b/models/super_resolution/Nnsr_LumaCNNSR_Inter_float.sadl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e691791b18e4947489c253c517df2ca09909e01dadbf2334aaf8e05ae6c53e2a
+size 5995184
diff --git a/models/super_resolution/Nnsr_LumaCNNSR_Inter_int16.sadl b/models/super_resolution/Nnsr_LumaCNNSR_Inter_int16.sadl
new file mode 100644
index 0000000000000000000000000000000000000000..2739257ac72b8c60a18c4c46bd6050403bf20f8a
--- /dev/null
+++ b/models/super_resolution/Nnsr_LumaCNNSR_Inter_int16.sadl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2031718d04a4352e4a75d6f06a6343b1d960888bbc15670c96016599dd0a34df
+size 3007422
diff --git a/models/super_resolution/Nnsr_LumaCNNSR_Intra_float.sadl b/models/super_resolution/Nnsr_LumaCNNSR_Intra_float.sadl
new file mode 100644
index 0000000000000000000000000000000000000000..e5934cf75fe807ac48f89d47982ea3bf2b18bb07
--- /dev/null
+++ b/models/super_resolution/Nnsr_LumaCNNSR_Intra_float.sadl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:bbb66bf78a6fe2f1ed528dbc266c039a55ec50a20ef43243b53d9f593aff94ad
+size 5994879
diff --git a/models/super_resolution/Nnsr_LumaCNNSR_Intra_int16.sadl b/models/super_resolution/Nnsr_LumaCNNSR_Intra_int16.sadl
new file mode 100644
index 0000000000000000000000000000000000000000..7b26d05ce3a469f68d922236bddd0fbe169c2df1
--- /dev/null
+++ b/models/super_resolution/Nnsr_LumaCNNSR_Intra_int16.sadl
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1730fd3ed2142109032c404be1de4399028502ee61517d4ac83470035250680b
+size 3007245
diff --git a/source/App/DecoderApp/DecApp.cpp b/source/App/DecoderApp/DecApp.cpp
index 0fffc9f072073ab289c8a425602b14d1d229cb61..c44cc1b837babc028e01b2f8ae2fc1379a4a56e5 100644
--- a/source/App/DecoderApp/DecApp.cpp
+++ b/source/App/DecoderApp/DecApp.cpp
@@ -913,7 +913,18 @@ void DecApp::xWriteOutput( PicList* pcListPic, uint32_t tId )
           ChromaFormat chromaFormatIDC = sps->getChromaFormatIdc();
           if( m_upscaledOutput )
           {
-            m_cVideoIOYuvReconFile[pcPic->layerId].writeUpscaledPicture( *sps, *pcPic->cs->pps, pcPic->getRecoBuf(), m_outputColourSpaceConvert, m_packedYUVMode, m_upscaledOutput, NUM_CHROMA_FORMAT, m_bClipOutputVideoToRec709Range );
+            m_cVideoIOYuvReconFile[pcPic->layerId].writeUpscaledPicture( *sps, *pcPic->cs->pps, pcPic->getRecoBuf(), m_outputColourSpaceConvert, m_packedYUVMode
+#if JVET_AC0196_NNSR
+              , pcPic->getPredBufCustom()
+              , pcPic->cs->slice->getSliceQp()
+              , pcPic->cs->pps->getPicInitQPMinus26() + 26
+              , pcPic->cs->slice->getSliceType()
+#endif
+              , m_upscaledOutput, NUM_CHROMA_FORMAT, m_bClipOutputVideoToRec709Range
+            );
+#if JVET_AC0196_NNSR && NNVC_USE_PRED
+            pcPic->m_bufs[PIC_PREDICTION_CUSTOM].destroy();
+#endif
           }
           else
           {
@@ -926,6 +937,9 @@ void DecApp::xWriteOutput( PicList* pcListPic, uint32_t tId )
                                                          conf.getWindowBottomOffset() * SPS::getWinUnitY( chromaFormatIDC ),
                                                          NUM_CHROMA_FORMAT, m_bClipOutputVideoToRec709Range );
           }
+#if JVET_AC0196_NNSR
+          pcPic->destroyTempBuffers();
+#endif
         }
 #if  NNVC_DUMP_DATA
         if (!m_dumpBasename.empty())
@@ -1013,7 +1027,14 @@ void DecApp::xWriteOutput( PicList* pcListPic, uint32_t tId )
           ChromaFormat chromaFormatIDC = sps->getChromaFormatIdc();
           if( m_upscaledOutput )
           {
-            m_cVideoIOYuvReconPostFile[pcPic->layerId].writeUpscaledPicture( *sps, *pcPic->cs->pps, pcPic->getNnPostFilteredBuf(), m_outputColourSpaceConvert, m_packedYUVMode, m_upscaledOutput, NUM_CHROMA_FORMAT, m_bClipOutputVideoToRec709Range );
+            m_cVideoIOYuvReconPostFile[pcPic->layerId].writeUpscaledPicture( *sps, *pcPic->cs->pps, pcPic->getNnPostFilteredBuf(), m_outputColourSpaceConvert, m_packedYUVMode,
+#if JVET_AC0196_NNSR
+              pcPic->getPredBufCustom(),
+              pcPic->cs->slice->getSliceQp(),
+              pcPic->cs->pps->getPicInitQPMinus26() + 26,
+              pcPic->cs->slice->getSliceType(),
+#endif
+              m_upscaledOutput, NUM_CHROMA_FORMAT, m_bClipOutputVideoToRec709Range );
           }
           else
           {
@@ -1178,7 +1199,15 @@ void DecApp::xFlushOutput( PicList* pcListPic, const int layerId )
             ChromaFormat chromaFormatIDC = sps->getChromaFormatIdc();
             if( m_upscaledOutput )
             {
-              m_cVideoIOYuvReconFile[pcPic->layerId].writeUpscaledPicture( *sps, *pcPic->cs->pps, pcPic->getRecoBuf(), m_outputColourSpaceConvert, m_packedYUVMode, m_upscaledOutput, NUM_CHROMA_FORMAT, m_bClipOutputVideoToRec709Range );
+              m_cVideoIOYuvReconFile[pcPic->layerId].writeUpscaledPicture( *sps, *pcPic->cs->pps, pcPic->getRecoBuf(), m_outputColourSpaceConvert, m_packedYUVMode 
+#if JVET_AC0196_NNSR
+              , pcPic->getPredBufCustom()
+              , pcPic->cs->slice->getSliceQp()
+              , pcPic->cs->pps->getPicInitQPMinus26() + 26
+              , pcPic->cs->slice->getSliceType()
+#endif
+              , m_upscaledOutput, NUM_CHROMA_FORMAT, m_bClipOutputVideoToRec709Range 
+              );
             }
             else
             {
@@ -1190,7 +1219,10 @@ void DecApp::xFlushOutput( PicList* pcListPic, const int layerId )
                                                            conf.getWindowTopOffset() * SPS::getWinUnitY( chromaFormatIDC ),
                                                            conf.getWindowBottomOffset() * SPS::getWinUnitY( chromaFormatIDC ),
                                                            NUM_CHROMA_FORMAT, m_bClipOutputVideoToRec709Range );
-              }
+            }
+#if JVET_AC0196_NNSR
+            pcPic->destroyTempBuffers();
+#endif
           }
           // write to file
 #if NNVC_DUMP_DATA
@@ -1278,7 +1310,14 @@ void DecApp::xFlushOutput( PicList* pcListPic, const int layerId )
             ChromaFormat chromaFormatIDC = sps->getChromaFormatIdc();
             if( m_upscaledOutput )
             {
-              m_cVideoIOYuvReconPostFile[pcPic->layerId].writeUpscaledPicture( *sps, *pcPic->cs->pps, pcPic->getNnPostFilteredBuf(), m_outputColourSpaceConvert, m_packedYUVMode, m_upscaledOutput, NUM_CHROMA_FORMAT, m_bClipOutputVideoToRec709Range );
+              m_cVideoIOYuvReconPostFile[pcPic->layerId].writeUpscaledPicture( *sps, *pcPic->cs->pps, pcPic->getNnPostFilteredBuf(), m_outputColourSpaceConvert, m_packedYUVMode, 
+#if JVET_AC0196_NNSR
+              pcPic->getPredBufCustom(),
+              pcPic->cs->slice->getSliceQp(),
+              pcPic->cs->pps->getPicInitQPMinus26() + 26,
+              pcPic->cs->slice->getSliceType(),
+#endif
+              m_upscaledOutput, NUM_CHROMA_FORMAT, m_bClipOutputVideoToRec709Range );
             }
             else
             {
diff --git a/source/App/DecoderApp/DecAppCfg.cpp b/source/App/DecoderApp/DecAppCfg.cpp
index 992409f7c46554bb7eda58f1937f8cf94128e4df..c4343d695bc64a7f9fcb599bd4b67c571284e33b 100644
--- a/source/App/DecoderApp/DecAppCfg.cpp
+++ b/source/App/DecoderApp/DecAppCfg.cpp
@@ -146,7 +146,11 @@ bool DecAppCfg::parseCfg( int argc, char* argv[] )
 #endif
   ("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" )
+#if JVET_AC0196_NNSR
+  ( "UpscaledOutput",          m_upscaledOutput,                          2,       "Upscaled output for RPR" )
+#else
   ( "UpscaledOutput",          m_upscaledOutput,                          0,       "Upscaled output for RPR" )
+#endif
 #if JVET_AB0149_INTRA_PRED
   ("DescriptionPairHeightWidthPathToGraphOutput", m_descriptionPairHeightWidthPathToGraphOutput, string("4,4,graph_output_4_4_int16.sadl;4,8,graph_output_4_8_int16.sadl;4,16,graph_output_4_16_int16.sadl;4,32,graph_output_4_32_int16.sadl;8,8,graph_output_8_8_int16.sadl;8,16,graph_output_8_16_int16.sadl;16,16,graph_output_16_16_int16.sadl;"), "Description of each pair of block height and width being a map key and the path to the output graph of the prediction neural network being its string value.")
   ("PrefixAbsolutePathsToGraphsOutput", m_prefixAbsolutePathsToGraphsOutput, string("models/intra"), "Prefix of the absolute path to the output graph of each prediction neural network.")
diff --git a/source/App/EncoderApp/EncApp.cpp b/source/App/EncoderApp/EncApp.cpp
index c8b2845cb1f9fcce7e84ad85174472bd624a543f..b5c80aabc2f2a4d56157828e778e94c0a572bc53 100644
--- a/source/App/EncoderApp/EncApp.cpp
+++ b/source/App/EncoderApp/EncApp.cpp
@@ -260,6 +260,11 @@ void EncApp::xInitLibCfg()
   m_cEncLib.setRdoCnnlfInterLumaModelNameNNFilter1               (m_rdoCnnlfInterLumaModelNameNNFilter1);
   m_cEncLib.setRdoCnnlfIntraLumaModelNameNNFilter1               (m_rdoCnnlfIntraLumaModelNameNNFilter1);
 #endif
+
+#if JVET_AC0196_NNSR
+  m_cEncLib.setUseNnsr                                           (m_nnsrOption);
+#endif
+
   m_cEncLib.setProfile                                           ( m_profile);
   m_cEncLib.setLevel                                             ( m_levelTier, m_level);
   m_cEncLib.setFrameOnlyConstraintFlag                           ( m_frameOnlyConstraintFlag);
@@ -1273,11 +1278,22 @@ void EncApp::createLib( const int layerIdx )
 #endif
   const int sourceHeight = m_isField ? m_iSourceHeightOrg : m_iSourceHeight;
   UnitArea unitArea( m_chromaFormatIDC, Area( 0, 0, m_iSourceWidth, sourceHeight ) );
-
+  
+#if ADAPTIVE_RPR
+  UnitArea unitAreaRPR10(m_chromaFormatIDC, Area(0, 0, int(m_iSourceWidth), int(sourceHeight)));
+  UnitArea unitAreaRPR20(m_chromaFormatIDC, Area(0, 0, int(m_iSourceWidth / 2.0), int(sourceHeight / 2.0)));
+#endif
+  
   m_orgPic = new PelStorage;
   m_trueOrgPic = new PelStorage;
   m_orgPic->create( unitArea );
   m_trueOrgPic->create( unitArea );
+#if ADAPTIVE_RPR
+  m_rprPic[0] = new PelStorage;
+  m_rprPic[0]->create(unitAreaRPR10);
+  m_rprPic[1] = new PelStorage;
+  m_rprPic[1]->create(unitAreaRPR20);
+#endif
   if(m_gopBasedTemporalFilterEnabled)
   {
     m_filteredOrgPic = new PelStorage;
@@ -1345,6 +1361,13 @@ void EncApp::destroyLib()
   m_trueOrgPic->destroy();
   delete m_trueOrgPic;
   delete m_orgPic;
+#if ADAPTIVE_RPR
+  for (int i = 0; i < 2; i++)
+  {
+    m_rprPic[i]->destroy();
+    delete m_rprPic[i];
+  }
+#endif
   if(m_gopBasedTemporalFilterEnabled)
   {
     m_filteredOrgPic->destroy();
@@ -1417,7 +1440,11 @@ bool EncApp::encodePrep( bool& eos )
   }
   else
   {
-    keepDoing = m_cEncLib.encodePrep( eos, m_flush ? 0 : m_orgPic, m_flush ? 0 : m_trueOrgPic, m_flush ? 0 : m_filteredOrgPic, snrCSC, m_recBufList, m_numEncoded );
+    keepDoing = m_cEncLib.encodePrep( eos, m_flush ? 0 : m_orgPic, m_flush ? 0 : m_trueOrgPic, m_flush ? 0 : m_filteredOrgPic, snrCSC, m_recBufList, m_numEncoded
+#if ADAPTIVE_RPR
+      , m_rprPic
+#endif
+    );
   }
 
   return keepDoing;
@@ -1480,10 +1507,17 @@ void EncApp::xWriteOutput( int iNumEncoded, std::list<PelUnitBuf*>& recBufList )
   const InputColourSpaceConversion ipCSC = (!m_outputInternalColourSpace) ? m_inputColourSpaceConvert : IPCOLOURSPACE_UNCHANGED;
   std::list<PelUnitBuf*>::iterator iterPicYuvRec = recBufList.end();
   int i;
-
+#if JVET_AC0196_NNSR
+  PicList tempPicList = *m_cEncLib.getListPic();
+  Slice::sortPicList(tempPicList); 
+  std::list<Picture *>::iterator iterPicYuvPred = tempPicList.end();
+#endif
   for ( i = 0; i < iNumEncoded; i++ )
   {
     --iterPicYuvRec;
+#if JVET_AC0196_NNSR
+    --iterPicYuvPred;
+#endif
   }
 
   if (m_isField)
@@ -1508,20 +1542,37 @@ void EncApp::xWriteOutput( int iNumEncoded, std::list<PelUnitBuf*>& recBufList )
     for ( i = 0; i < iNumEncoded; i++ )
     {
       const PelUnitBuf* pcPicYuvRec = *(iterPicYuvRec++);
+#if JVET_AC0196_NNSR
+      Picture* tempPic = *(iterPicYuvPred++);
+#endif
       if (!m_reconFileName.empty())
       {
         if( m_cEncLib.isResChangeInClvsEnabled() && m_cEncLib.getUpscaledOutput() )
         {
           const SPS& sps = *m_cEncLib.getSPS( 0 );
+#if ADAPTIVE_RPR
+          const PPS& pps = *m_cEncLib.getPPS((sps.getMaxPicWidthInLumaSamples() != pcPicYuvRec->get(COMPONENT_Y).width || sps.getMaxPicHeightInLumaSamples() != pcPicYuvRec->get(COMPONENT_Y).height) ? m_cEncLib.m_iGOPRprPpsId : 0);
+#else
           const PPS& pps = *m_cEncLib.getPPS( ( sps.getMaxPicWidthInLumaSamples() != pcPicYuvRec->get( COMPONENT_Y ).width || sps.getMaxPicHeightInLumaSamples() != pcPicYuvRec->get( COMPONENT_Y ).height ) ? ENC_PPS_ID_RPR : 0 );
-
-          m_cVideoIOYuvReconFile.writeUpscaledPicture( sps, pps, *pcPicYuvRec, ipCSC, m_packedYUVMode, m_cEncLib.getUpscaledOutput(), NUM_CHROMA_FORMAT, m_bClipOutputVideoToRec709Range );
+#endif
+          m_cVideoIOYuvReconFile.writeUpscaledPicture( sps, pps, *pcPicYuvRec, ipCSC, m_packedYUVMode
+#if JVET_AC0196_NNSR
+              , tempPic->getPredBufCustom()
+              , tempPic->cs->slice->getSliceQp()
+              , tempPic->cs->pps->getPicInitQPMinus26() + 26
+              , tempPic->cs->slice->getSliceType()
+#endif  
+              , m_cEncLib.getUpscaledOutput(), NUM_CHROMA_FORMAT, m_bClipOutputVideoToRec709Range
+          );
         }
         else
         {
           m_cVideoIOYuvReconFile.write( pcPicYuvRec->get( COMPONENT_Y ).width, pcPicYuvRec->get( COMPONENT_Y ).height, *pcPicYuvRec, ipCSC, m_packedYUVMode,
             m_confWinLeft, m_confWinRight, m_confWinTop, m_confWinBottom, NUM_CHROMA_FORMAT, m_bClipOutputVideoToRec709Range );
         }
+#if JVET_AC0196_NNSR
+        tempPic->destroyTempBuffers();
+#endif
       }
     }
   }
diff --git a/source/App/EncoderApp/EncApp.h b/source/App/EncoderApp/EncApp.h
index b3b2f3e8c43782d848e81ffafddaf3c80c463cdc..332c7aa33393fa7f66ca388e47f18d81995b2c86 100644
--- a/source/App/EncoderApp/EncApp.h
+++ b/source/App/EncoderApp/EncApp.h
@@ -98,6 +98,9 @@ private:
   PelStorage*            m_trueOrgPic;
   PelStorage*            m_orgPic;
   PelStorage*            m_filteredOrgPic;
+#if ADAPTIVE_RPR
+  PelStorage*            m_rprPic[2];
+#endif
 #if EXTENSION_360_VIDEO
   TExt360AppEncTop*      m_ext360;
 #endif
diff --git a/source/App/EncoderApp/EncAppCfg.cpp b/source/App/EncoderApp/EncAppCfg.cpp
index 470ee7dd6f3c93b4dd08475c11905feddbb62fcc..3f5d257c121a6017149d44d17a9f3ad721b9552f 100644
--- a/source/App/EncoderApp/EncAppCfg.cpp
+++ b/source/App/EncoderApp/EncAppCfg.cpp
@@ -1463,11 +1463,20 @@ bool EncAppCfg::parseCfg( int argc, char* argv[] )
   ( "RdoCnnlfIntraLumaModelNNFilter1",                m_rdoCnnlfIntraLumaModelNameNNFilter1,     string("models/RdNnlfSet1_LumaCNNFilter_IntraSlice_int16.sadl"), "NnlfSet1 intra luma model name for RDO")
 #endif
   ( "RPR",                                            m_rprEnabledFlag,                          true, "Reference Sample Resolution" )
+#if JVET_AC0196_NNSR
+  ( "NnsrOption",                                     m_nnsrOption,                              false, "NN-based super resolution option")
+  ( "ScalingRatioHor",                                m_scalingRatioHor,                          2.0, "Scaling ratio in hor direction" )
+  ( "ScalingRatioVer",                                m_scalingRatioVer,                          2.0, "Scaling ratio in ver direction" )
+  ( "FractionNumFrames",                              m_fractionOfFrames,                         1.0, "Encode a fraction of the specified in FramesToBeEncoded frames" )
+  ( "SwitchPocPeriod",                                m_switchPocPeriod,                            0, "Switch POC period for RPR" )
+  ( "UpscaledOutput",                                 m_upscaledOutput,                             2, "Output upscaled (2), decoded but in full resolution buffer (1) or decoded cropped (0, default) picture for RPR" )
+#else
   ( "ScalingRatioHor",                                m_scalingRatioHor,                          1.0, "Scaling ratio in hor direction" )
   ( "ScalingRatioVer",                                m_scalingRatioVer,                          1.0, "Scaling ratio in ver direction" )
   ( "FractionNumFrames",                              m_fractionOfFrames,                         1.0, "Encode a fraction of the specified in FramesToBeEncoded frames" )
   ( "SwitchPocPeriod",                                m_switchPocPeriod,                            0, "Switch POC period for RPR" )
   ( "UpscaledOutput",                                 m_upscaledOutput,                             0, "Output upscaled (2), decoded but in full resolution buffer (1) or decoded cropped (0, default) picture for RPR" )
+#endif
   ( "MaxLayers",                                      m_maxLayers,                                  1, "Max number of layers" )
 #if JVET_S0163_ON_TARGETOLS_SUBLAYERS
   ( "EnableOperatingPointInformation",                m_OPIEnabled,                             false, "Enables writing of Operating Point Information (OPI)" )
@@ -1661,6 +1670,12 @@ bool EncAppCfg::parseCfg( int argc, char* argv[] )
   po::ErrorReporter err;
   const list<const char*>& argv_unhandled = po::scanArgv(opts, argc, (const char**) argv, err);
 
+#if JVET_AC0196_NNSR
+  m_nnsrOption = m_nnsrOption ? true : false;
+  m_scalingRatioHor = m_nnsrOption ? 2.0 : 1.0;
+  m_scalingRatioVer = m_nnsrOption ? 2.0 : 1.0;
+  m_upscaledOutput = (m_nnsrOption && (m_upscaledOutput != 0)) ? 2 : 0;
+#endif
   m_resChangeInClvsEnabled = m_scalingRatioHor != 1.0 || m_scalingRatioVer != 1.0;
   m_resChangeInClvsEnabled = m_resChangeInClvsEnabled && m_rprEnabledFlag;
   if( m_fractionOfFrames != 1.0 )
@@ -4207,6 +4222,9 @@ void EncAppCfg::xPrintParameter()
 #if JVET_AC0328_NNLF_RDO
   msg( VERBOSE, "EncNnlfOpt:%d ", m_encNnlfOpt ? 1 : 0);
 #endif
+#if JVET_AC0196_NNSR
+  msg( VERBOSE, "NnsrOption:%d ", m_nnsrOption ? 1 : 0);
+#endif
 #if JVET_AC0055_NN_POST_FILTERING
   msg(VERBOSE, "Nnpf:%d", m_nnpf ? 1 : 0);
 #endif
diff --git a/source/App/EncoderApp/EncAppCfg.h b/source/App/EncoderApp/EncAppCfg.h
index 94db3117a3fed1ed1519466351f4c9852552f554..aef6a5a51f9af03361dcb49f5436c8b76ca1c68d 100644
--- a/source/App/EncoderApp/EncAppCfg.h
+++ b/source/App/EncoderApp/EncAppCfg.h
@@ -781,6 +781,9 @@ protected:
   bool        m_encNnlfOpt;
 #endif
 
+#if JVET_AC0196_NNSR
+  bool        m_nnsrOption;
+#endif
 #if JVET_AC0055_NN_POST_FILTERING
   bool m_nnpf;
 #endif
diff --git a/source/Lib/CommonLib/NNSuperResolution.cpp b/source/Lib/CommonLib/NNSuperResolution.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..1e202e231c5bfb11af2c028cb76b687befff8a48
--- /dev/null
+++ b/source/Lib/CommonLib/NNSuperResolution.cpp
@@ -0,0 +1,296 @@
+/* The copyright in this software is being made available under the BSD
+ * License, included below. This software may be subject to other third party
+ * and contributor rights, including patent rights, and no such rights are
+ * granted under this license.
+ *
+ * Copyright (c) 2010-2018, ITU/ISO/IEC
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  * Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *  * Neither the name of the ITU/ISO/IEC nor the names of its contributors may
+ *    be used to endorse or promote products derived from this software without
+ *    specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** \file     NNSuperResolution.cpp
+    \brief    nn super resolution class
+*/
+
+#include "NNSuperResolution.h"
+
+#if JVET_AC0196_NNSR
+#include "CodingStructure.h"
+#include "Picture.h"
+
+NNSuperResolution::NNSuperResolution()
+{
+  m_initFlag = false;
+}
+
+void NNSuperResolution::initCNNModel()
+{
+  if (m_initFlag)
+  {
+    return;
+  }
+  static const std::string modelDir    = "./models/super_resolution/";
+#if NN_FIXED_POINT_IMPLEMENTATION
+  static const std::string lumaIModel  = modelDir + "Nnsr_LumaCNNSR_Intra_int16.sadl";
+  static const std::string lumaBModel  = modelDir + "Nnsr_LumaCNNSR_Inter_int16.sadl";
+  static const std::string chromaModel = modelDir + "Nnsr_ChromaCNNSR_Inter_Intra_int16.sadl";
+#else
+  static const std::string lumaIModel  = modelDir + "Nnsr_LumaCNNSR_Intra_float.sadl";
+  static const std::string lumaBModel  = modelDir + "Nnsr_LumaCNNSR_Inter_float.sadl";
+  static const std::string chromaModel = modelDir + "Nnsr_ChromaCNNSR_Inter_Intra_float.sadl";
+#endif
+
+  ifstream lumaIfile(lumaIModel, ios::binary);
+  if ((!m_SRYModel[0].load(lumaIfile))) 
+  {
+    cerr << "[ERROR] Unable to read model: " << lumaIModel << endl;
+    exit(-1);
+  }
+  ifstream lumaBfile(lumaBModel, ios::binary);
+  if ((!m_SRYModel[1].load(lumaBfile))) 
+  {
+    cerr << "[ERROR] Unable to read model: " << lumaBModel << endl;
+    exit(-1);
+  }
+  ifstream chromaIBfile(chromaModel, ios::binary);
+  if ((!m_SRUVModel.load(chromaIBfile)))
+  {
+    cerr << "[ERROR] Unable to read model: " << chromaModel << endl;
+    exit(-1);
+  }
+
+  m_initFlag = true;
+}
+
+void NNSuperResolution::CNNSRProcess(const CPelUnitBuf &recBuf, const PelUnitBuf &predBuf, const PelUnitBuf &rprBuf,
+                                      PelUnitBuf &cnnBuf, const int actualHeight, const int actualWidth,
+                                      const int sliceQP, const int baseQP, const SliceType sliceType)
+{
+  // setting
+  int    patchSize    = 128;
+  int    padSize      = 8;
+  double maxValue     = 1023;
+#if NN_FIXED_POINT_IMPLEMENTATION
+  const int org_quantizers_in   = 10;
+  const int org_quantizers_out  = 10;
+  const int sadl_quantizers_in  = 11;
+  const int sadl_quantizers_out = 11;
+  const int in_left_shift       = sadl_quantizers_in - org_quantizers_in;
+  const int out_left_shift      = sadl_quantizers_out - org_quantizers_out;
+#endif
+
+  int    picHeight      = recBuf.get(COMPONENT_Y).height;
+  int    picWidth       = recBuf.get(COMPONENT_Y).width;
+  int picWidthInPatchs  = ceil((double) picWidth / patchSize);
+  int picHeightInPatchs = ceil((double) picHeight / patchSize);
+
+  initCNNModel();
+
+  vector<sadl::Tensor<TypeSadl>> m_InputY;
+  vector<sadl::Tensor<TypeSadl>> m_InputUV;
+  sliceType == I_SLICE ? m_InputY = m_SRYModel[0].getInputsTemplate() : m_InputY = m_SRYModel[1].getInputsTemplate();
+  m_InputUV = m_SRUVModel.getInputsTemplate();
+
+  for (int y = 0; y < picHeightInPatchs; y++)
+  {
+    for (int x = 0; x < picWidthInPatchs; x++)
+    {
+      // obtain input
+      int pix_y = y * patchSize;
+      int pix_x = x * patchSize;
+
+      int pix_y_end = (y + 1) * patchSize > picHeight ? picHeight - 1 : (y + 1) * patchSize - 1;
+      int pix_x_end = (x + 1) * patchSize > picWidth ? picWidth - 1 : (x + 1) * patchSize - 1;
+
+      int st_h = pix_y - padSize < 0 ? 0 : pix_y - padSize;
+      int ed_h = pix_y_end + padSize + 1 > picHeight ? picHeight - 1 : pix_y_end + padSize;
+      int st_w = pix_x - padSize < 0 ? 0 : pix_x - padSize;
+      int ed_w = pix_x_end + padSize + 1 > picWidth ? picWidth - 1 : pix_x_end + padSize;
+
+      int actualPatchSizeH = ed_h - st_h + 1;
+      int actualPatchSizeW = ed_w - st_w + 1;
+
+      int InputIdx = 0;
+      for (auto &m: m_InputY)
+      {
+        sadl::Dimensions dims(std::initializer_list<int>({ 1, actualPatchSizeH, actualPatchSizeW, 1 }));
+        m.resize(dims);
+        InputIdx++;
+      }
+      InputIdx = 0;
+      for (auto &m: m_InputUV)
+      {
+        if (m.dims()[3] == 1 && InputIdx == 0)
+        {
+          sadl::Dimensions dims(std::initializer_list<int>({ 1, actualPatchSizeH, actualPatchSizeW, 1 }));
+          m.resize(dims);
+        }
+        else if (m.dims()[3] == 2 && InputIdx == 1)
+        {
+          sadl::Dimensions dims(std::initializer_list<int>({ 1, actualPatchSizeH / 2, actualPatchSizeW / 2, 2 }));
+          m.resize(dims);
+        }
+        else
+        {
+          sadl::Dimensions dims(std::initializer_list<int>({ 1, actualPatchSizeH / 2, actualPatchSizeW / 2, 1 }));
+          m.resize(dims);
+        }
+        InputIdx++;
+      }
+
+      if (!m_SRYModel[sliceType == I_SLICE ? 0 : 1].init(m_InputY))
+      {
+        cerr << "[ERROR] issue during initialization for luma" << endl;
+        exit(-1);
+      }
+      if (!m_SRUVModel.init(m_InputUV))
+      {
+        cerr << "[ERROR] issue during initialization for chroma" << endl;
+        exit(-1);
+      }
+
+      for (int yy = 0; yy < actualPatchSizeH; yy++)
+      {
+        for (int xx = 0; xx < actualPatchSizeW; xx++)
+        {
+          int id_x[2], id_y[2];
+          id_x[0] = st_w + xx;
+          id_y[0] = st_h + yy;
+          id_x[1] = (st_w >> 1) + (xx >> 1);
+          id_y[1] = (st_h >> 1) + (yy >> 1);
+
+          int xxx[2], yyy[2];
+          xxx[0] = xx;
+          yyy[0] = yy;
+          xxx[1] = (xx >> 1);
+          yyy[1] = (yy >> 1);
+#if NN_FIXED_POINT_IMPLEMENTATION
+          m_InputY[0][yyy[0] * actualPatchSizeW + xxx[0]] = recBuf.get(COMPONENT_Y).at(id_x[0], id_y[0]) << in_left_shift;
+          m_InputY[1][yyy[0] * actualPatchSizeW + xxx[0]] = predBuf.get(COMPONENT_Y).at(id_x[0], id_y[0]) << in_left_shift;
+          m_InputY[2][yyy[0] * actualPatchSizeW + xxx[0]] = sliceQP << in_left_shift;
+          if (sliceType == B_SLICE)
+          {
+            m_InputY[3][yyy[0] * actualPatchSizeW + xxx[0]] = baseQP << in_left_shift;
+          }
+          m_InputUV[0][yyy[0] * actualPatchSizeW + xxx[0]] = recBuf.get(COMPONENT_Y).at(id_x[0], id_y[0]) << in_left_shift;
+          m_InputUV[1][(yyy[1] * actualPatchSizeW / 2 + xxx[1]) * 2] = recBuf.get(COMPONENT_Cb).at(id_x[1], id_y[1]) << in_left_shift;
+          m_InputUV[1][(yyy[1] * actualPatchSizeW / 2 + xxx[1]) * 2 + 1] = recBuf.get(COMPONENT_Cr).at(id_x[1], id_y[1]) << in_left_shift;
+          m_InputUV[2][yyy[1] * actualPatchSizeW / 2 + xxx[1]] = sliceQP << in_left_shift;
+          m_InputUV[3][yyy[1] * actualPatchSizeW / 2 + xxx[1]] = baseQP << in_left_shift;
+          m_InputUV[4][yyy[1] * actualPatchSizeW / 2 + xxx[1]] = (sliceType == I_SLICE ? 0 : 1023) << in_left_shift;
+#else
+          m_InputY[0][yyy[0] * actualPatchSizeW + xxx[0]] = recBuf.get(COMPONENT_Y).at(id_x[0], id_y[0]) / maxValue;
+          m_InputY[1][yyy[0] * actualPatchSizeW + xxx[0]] = predBuf.get(COMPONENT_Y).at(id_x[0], id_y[0]) / maxValue;
+          m_InputY[2][yyy[0] * actualPatchSizeW + xxx[0]] = sliceQP / maxValue;
+          if (sliceType == B_SLICE)
+          {
+            m_InputY[3][yyy[0] * actualPatchSizeW + xxx[0]] = baseQP / maxValue;
+          }
+
+          m_InputUV[0][yyy[0] * actualPatchSizeW + xxx[0]] = recBuf.get(COMPONENT_Y).at(id_x[0], id_y[0]) / maxValue;
+          m_InputUV[1][(yyy[1] * actualPatchSizeW / 2 + xxx[1]) * 2] = recBuf.get(COMPONENT_Cb).at(id_x[1], id_y[1]) / maxValue;
+          m_InputUV[1][(yyy[1] * actualPatchSizeW / 2 + xxx[1]) * 2 + 1] = recBuf.get(COMPONENT_Cr).at(id_x[1], id_y[1]) / maxValue;
+          m_InputUV[2][yyy[1] * actualPatchSizeW / 2 + xxx[1]] = sliceQP / maxValue;
+          m_InputUV[3][yyy[1] * actualPatchSizeW / 2 + xxx[1]] = baseQP / maxValue;
+          m_InputUV[4][yyy[1] * actualPatchSizeW / 2 + xxx[1]] = (sliceType == I_SLICE ? 0 : maxValue) / maxValue;
+#endif
+        }
+      }
+
+      if (sliceType == I_SLICE) 
+      {
+        if (!m_SRYModel[0].apply(m_InputY))
+        {
+          cerr << "[ERROR] issue during inference for luma" << endl;
+          exit(-1);
+        }
+      }
+      else
+      {
+        if (!m_SRYModel[1].apply(m_InputY))
+        {
+          cerr << "[ERROR] issue during inference for luma" << endl;
+          exit(-1);
+        }
+      }
+      if (!m_SRUVModel.apply(m_InputUV))
+      {
+        cerr << "[ERROR] issue during inference for chroma" << endl;
+        exit(-1);
+      }
+
+      int centerH = pix_y_end - pix_y + 1;
+      int centerW = pix_x_end - pix_x + 1;
+      // extract luma 
+      for (int kk = 0; kk < 4; kk++)
+      {
+        for (int yy = 0; yy < centerH; yy++)
+        {
+          for (int xx = 0; xx < centerW; xx++)
+          {
+            int id_x = ((pix_x + xx) << 1) + kk % 2;
+            int id_y = ((pix_y + yy) << 1) + kk / 2;
+            int pos_x = pix_x - st_w + xx;
+            int pos_y = pix_y - st_h + yy;
+            
+
+#if NN_FIXED_POINT_IMPLEMENTATION
+            cnnBuf.get(COMPONENT_Y).at(id_x, id_y) = Pel(Clip3<int>(0, maxValue, int(m_SRYModel[sliceType == I_SLICE ? 0 : 1].result(0)(0, pos_y, pos_x, kk) + (rprBuf.get(COMPONENT_Y).at(id_x, id_y) << in_left_shift)) >> out_left_shift));
+#else
+            cnnBuf.get(COMPONENT_Y).at(id_x, id_y) = Pel(Clip3<int>(0, maxValue, int((m_SRYModel[sliceType == I_SLICE ? 0 : 1].result(0)(0, (id_y - st_h) >> 1, (id_x - st_w) >> 1, kk) + (rprBuf.get(COMPONENT_Y).at(id_x, id_y) / maxValue)) * maxValue + 0.5)));
+#endif
+          }
+        }
+      }
+      // extract chroma
+      int centerH_chroma = centerH >> 1;
+      int centerW_chroma = centerW >> 1;
+      for (int kk = 0; kk < 4; kk++)
+      {
+        for (int yy = 0; yy < centerH_chroma; yy++)
+        {
+          for (int xx = 0; xx < centerW_chroma; xx++)
+          {
+            int id_x = pix_x + (xx << 1) + kk % 2;
+            int id_y = pix_y + (yy << 1) + kk / 2;
+            int pos_x = ((pix_x - st_w) >> 1) + xx;
+            int pos_y = ((pix_y - st_h) >> 1) + yy;
+
+#if NN_FIXED_POINT_IMPLEMENTATION
+            cnnBuf.get(COMPONENT_Cb).at(id_x, id_y) = Pel(Clip3<int>(0, maxValue, int(m_SRUVModel.result(0)(0, pos_y, pos_x, kk) + (rprBuf.get(COMPONENT_Cb).at(id_x, id_y) << in_left_shift)) >> out_left_shift));
+            cnnBuf.get(COMPONENT_Cr).at(id_x, id_y) = Pel(Clip3<int>(0, maxValue, int(m_SRUVModel.result(0)(0, pos_y, pos_x, 4+kk) + (rprBuf.get(COMPONENT_Cr).at(id_x, id_y) << in_left_shift)) >> out_left_shift)); 
+#else
+            cnnBuf.get(COMPONENT_Cb).at(id_x, id_y) = Pel(Clip3<int>(0, maxValue, int((m_SRUVModel.result(0)(0, pos_y >> 1, pos_x >> 1, kk) + (rprBuf.get(COMPONENT_Cb).at(id_x, id_y) / maxValue)) * maxValue + 0.5)));
+            cnnBuf.get(COMPONENT_Cr).at(id_x, id_y) = Pel(Clip3<int>(0, maxValue, int((m_SRUVModel.result(0)(0, pos_y >> 1, pos_x >> 1, 4+kk) + (rprBuf.get(COMPONENT_Cr).at(id_x, id_y) / maxValue)) * maxValue + 0.5)));      
+#endif
+          }
+        }
+      }
+    }
+  }
+}
+
+#endif
diff --git a/source/Lib/CommonLib/NNSuperResolution.h b/source/Lib/CommonLib/NNSuperResolution.h
new file mode 100644
index 0000000000000000000000000000000000000000..73d1ab26d214b420cc48b936ba97e2d12b638072
--- /dev/null
+++ b/source/Lib/CommonLib/NNSuperResolution.h
@@ -0,0 +1,67 @@
+/* The copyright in this software is being made available under the BSD
+ * License, included below. This software may be subject to other third party
+ * and contributor rights, including patent rights, and no such rights are
+ * granted under this license.
+ *
+ * Copyright (c) 2010-2018, ITU/ISO/IEC
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  * Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *  * Neither the name of the ITU/ISO/IEC nor the names of its contributors may
+ *    be used to endorse or promote products derived from this software without
+ *    specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** \file     NNSuperResolution.h
+    \brief    nn super resolution class (header)
+*/
+
+
+#ifndef __NNSuperResolution__
+#define __NNSuperResolution__
+
+#include "CommonDef.h"
+
+#if JVET_AC0196_NNSR
+#include "Picture.h"
+#include <sadl/model.h>
+#include <fstream>
+using namespace std;
+
+class NNSuperResolution
+{
+public:
+  NNSuperResolution();
+  virtual ~NNSuperResolution() {}
+
+  bool m_initFlag;
+  
+  sadl::Model<TypeSadl> m_SRYModel[2];
+  sadl::Model<TypeSadl> m_SRUVModel;
+
+  void initCNNModel();
+  void CNNSRProcess(const CPelUnitBuf &recBuf, const PelUnitBuf &predBuf, const PelUnitBuf &rprBuf, PelUnitBuf &cnnBuf, const int actualHeight, const int actualWidth, const int baseQP, const int sliceQP, const SliceType sliceType);
+
+};
+#endif
+
+#endif
diff --git a/source/Lib/CommonLib/Picture.cpp b/source/Lib/CommonLib/Picture.cpp
index 7b830db77415692fc16ca5cc418e1157782c1179..0b2f589b5d84cccc8fe0b0bef4da389e00d4a57a 100644
--- a/source/Lib/CommonLib/Picture.cpp
+++ b/source/Lib/CommonLib/Picture.cpp
@@ -336,6 +336,12 @@ void Picture::destroyTempBuffers()
         M_BUFS( jId, t ).destroy();
       }
 #endif
+#if JVET_AC0196_NNSR && NNVC_USE_PRED
+      if (t == PIC_PREDICTION_CUSTOM )
+      {
+        M_BUFS( jId, t ).destroy();
+      }
+#endif
 #if ENABLE_SPLIT_PARALLELISM
       if (t == PIC_RECONSTRUCTION && jId > 0)
       {
@@ -1059,6 +1065,17 @@ void Picture::rescalePicture( const std::pair<int, int> scalingRatio,
   }
 }
 
+#if JVET_AC0196_NNSR
+void Picture::rescalePictureCNN(const CPelUnitBuf& recBuf, const PelUnitBuf& predBuf, const PelUnitBuf& rprBuf, PelUnitBuf& cnnBuf, int sliceQP, int baseQP, SliceType sliceType)
+{
+  int actualHeight = rprBuf.get(COMPONENT_Y).height;
+  int actualWidth  = rprBuf.get(COMPONENT_Y).width;
+
+  static NNSuperResolution m_CNNSR;
+  m_CNNSR.CNNSRProcess(recBuf, predBuf, rprBuf, cnnBuf, actualHeight, actualWidth, sliceQP, baseQP, sliceType);
+}
+#endif
+
 void Picture::saveSubPicBorder(int POC, int subPicX0, int subPicY0, int subPicWidth, int subPicHeight)
 {
 
diff --git a/source/Lib/CommonLib/Picture.h b/source/Lib/CommonLib/Picture.h
index a84dbf4ca13717ef83f52fc658391e63ca4e71da..f43fc756779ebc3db2eb44eb7a53019056406956 100644
--- a/source/Lib/CommonLib/Picture.h
+++ b/source/Lib/CommonLib/Picture.h
@@ -50,6 +50,10 @@
 #include "MCTS.h"
 #include <deque>
 
+#if JVET_AC0196_NNSR
+#include "NNSuperResolution.h"
+#endif
+
 #if ENABLE_SPLIT_PARALLELISM
 
 #define CURR_THREAD_ID -1
@@ -240,6 +244,10 @@ const   CPelBuf     getBsMapBuf(const CompArea &blk) const;
                                 const ChromaFormat chromaFormatIDC, const BitDepths& bitDepths, const bool useLumaFilter, const bool downsampling,
                                 const bool horCollocatedChromaFlag, const bool verCollocatedChromaFlag );
 
+#if JVET_AC0196_NNSR
+  static void rescalePictureCNN(const CPelUnitBuf& recBuf, const PelUnitBuf& predBuf, const PelUnitBuf& rprBuf, PelUnitBuf& cnnBuf, int sliceQP, int baseQP, SliceType sliceType);
+#endif
+
 private:
   Window        m_conformanceWindow;
   Window        m_scalingWindow;
diff --git a/source/Lib/CommonLib/TypeDef.h b/source/Lib/CommonLib/TypeDef.h
index afc27ebe906d1641f1be9b793c8d554a193ef34b..0bf33d4e6753a3b9fbe5be7dc9c3e957220b55d2 100644
--- a/source/Lib/CommonLib/TypeDef.h
+++ b/source/Lib/CommonLib/TypeDef.h
@@ -122,6 +122,11 @@ using TypeSadl = float;
 #define JVET_AC0177_FLIP_INPUT                            1 // JVET-AC0177: flip input and output of NN filter model
 #endif
 
+#define JVET_AC0196_NNSR                                  1 // JVET-AC0916: EE1-2.2: GOP Level Adaptive Resampling with CNN-based Super Resolution
+#if JVET_AC0196_NNSR
+#define ADAPTIVE_RPR                                      1 // JVET-AC0196: GOP Level Adaptive Resampling
+#endif
+
 #define JVET_AC0055_NN_POST_FILTERING                     1 // JVET-AC0055: EE1-1.11: Content-adaptive post-filter
 
 //########### place macros to be removed in next cycle below this line ###############
diff --git a/source/Lib/DecoderLib/DecLib.cpp b/source/Lib/DecoderLib/DecLib.cpp
index 875c2867fc211097c66f8c6c550d7cd19eac3e5c..2805f31ce3987a035957700ad095a7da3b580e6c 100644
--- a/source/Lib/DecoderLib/DecLib.cpp
+++ b/source/Lib/DecoderLib/DecLib.cpp
@@ -940,8 +940,9 @@ void DecLib::finishPicture(int &poc, PicList *&rpcListPic, MsgLevel msgl, bool a
   m_bFirstSliceInPicture  = true; // TODO: immer true? hier ist irgendwas faul
   m_maxDecSubPicIdx = 0;
   m_maxDecSliceAddrInSubPic = -1;
-
+#if !JVET_AC0196_NNSR
   m_pcPic->destroyTempBuffers();
+#endif
   m_pcPic->cs->destroyCoeffs();
   m_pcPic->cs->releaseIntermediateData();
   m_pcPic->cs->picHeader->initPicHeader();
@@ -2189,7 +2190,9 @@ bool DecLib::xDecodeSlice(InputNALUnit &nalu, int &iSkipFrame, int iPOCLastDispl
       && naluTemporalId.m_nalUnitType != NAL_UNIT_EOB)
 
     {
+#if !ADAPTIVE_RPR
       CHECK( naluTemporalId.m_temporalId < nalu.m_temporalId, "TemporalId shall be greater than or equal to the TemporalId of the layer access unit containing the NAL unit" );
+#endif
     }
   }
 
diff --git a/source/Lib/EncoderLib/EncCfg.h b/source/Lib/EncoderLib/EncCfg.h
index aeac072d708a301d68f01766d4348249efa8eb61..12b010aab9167afa95cfe9db54e639fe78467e15 100644
--- a/source/Lib/EncoderLib/EncCfg.h
+++ b/source/Lib/EncoderLib/EncCfg.h
@@ -175,7 +175,9 @@ protected:
   std::string m_rdoCnnlfInterLumaModelNameNNFilter1;          ///< inter luma nnlf set1 model
   std::string m_rdoCnnlfIntraLumaModelNameNNFilter1;          ///< intra luma nnlf set1 model
 #endif
-
+#if JVET_AC0196_NNSR
+  bool m_nnsrOption;
+#endif
   int       m_iFrameRate;
   int       m_FrameSkip;
   uint32_t      m_temporalSubsampleRatio;
@@ -909,6 +911,11 @@ public:
   void             setUseEncNnlfOpt(bool b)                       { m_encNnlfOpt = b;                      };
 #endif
 
+#if JVET_AC0196_NNSR
+  bool             getUseNnsr()                                   { return m_nnsrOption;                   };
+  void             setUseNnsr(bool b)                             { m_nnsrOption = b;                      };
+#endif
+
   void setProfile(Profile::Name profile) { m_profile = profile; }
   void setLevel(Level::Tier tier, Level::Name level) { m_levelTier = tier; m_level = level; }
   bool      getFrameOnlyConstraintFlag() const { return m_frameOnlyConstraintFlag; }
diff --git a/source/Lib/EncoderLib/EncGOP.cpp b/source/Lib/EncoderLib/EncGOP.cpp
index a7cb0bbf04f423cb4c02aa18ad74bb774347406d..bcc6e8eadda5701c0a795ec973969c34a2a5bac4 100644
--- a/source/Lib/EncoderLib/EncGOP.cpp
+++ b/source/Lib/EncoderLib/EncGOP.cpp
@@ -381,7 +381,12 @@ int EncGOP::xWritePPS( AccessUnit &accessUnit, const PPS *pps, const int layerId
   OutputNALUnit nalu(NAL_UNIT_PPS);
   m_HLSWriter->setBitstream( &nalu.m_Bitstream );
   nalu.m_nuhLayerId = layerId;
+#if ADAPTIVE_RPR
+  nalu.m_temporalId = accessUnit.temporalId;
+#endif
+#if !ADAPTIVE_RPR
   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" );
+#endif
   m_HLSWriter->codePPS( pps );
   accessUnit.push_back(new NALUnitEBSP(nalu));
   return (int)(accessUnit.back()->m_nalUnitData.str().size()) * 8;
@@ -394,7 +399,9 @@ int EncGOP::xWriteAPS( AccessUnit &accessUnit, APS *aps, const int layerId, cons
   nalu.m_nuhLayerId = layerId;
   nalu.m_temporalId = aps->getTemporalId();
   aps->setLayerId( layerId );
+#if !ADAPTIVE_RPR
   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" );
+#endif
   m_HLSWriter->codeAPS(aps);
   accessUnit.push_back(new NALUnitEBSP(nalu));
   return (int)(accessUnit.back()->m_nalUnitData.str().size()) * 8;
@@ -3895,8 +3902,9 @@ void EncGOP::compressGOP( int iPOCLast, int iNumPicRcvd, PicList& rcListPic,
     {
       iGOPid=effFieldIRAPMap.restoreGOPid(iGOPid);
     }
-
+#if !JVET_AC0196_NNSR
     pcPic->destroyTempBuffers();
+#endif
     pcPic->cs->destroyCoeffs();
     pcPic->cs->releaseIntermediateData();
   } // iGOPid-loop
@@ -4505,7 +4513,23 @@ void EncGOP::xCalculateAddPSNR(Picture* pcPic, PelUnitBuf cPicD, const AccessUni
     CU::getRprScaling( &sps, pps, pcPic, xScale, yScale );
     std::pair<int, int> scalingRatio = std::pair<int, int>( xScale, yScale );
 
+#if JVET_AC0196_NNSR
+    PelUnitBuf predBuf = pcPic->getPredBufCustom();
+    int        sliceQP = pcPic->cs->slice->getSliceQp();
+    int        baseQP  = pcPic->cs->pps->getPicInitQPMinus26() + 26;
+    SliceType  sliceType = pcPic->cs->slice->getSliceType();
+
+    PelStorage upscaledPic;
+    upscaledPic.create( pic.chromaFormat, Area( Position(), upscaledOrg ) );
+    Picture::rescalePicture( scalingRatio, picC, pcPic->getScalingWindow(), upscaledPic, pps->getScalingWindow(), format, sps.getBitDepths(), false, false, sps.getHorCollocatedChromaFlag(), sps.getVerCollocatedChromaFlag() );
+    
+    if (picC.get(COMPONENT_Y).width == upscaledPic.get(COMPONENT_Y).width && picC.get(COMPONENT_Y).height == upscaledPic.get(COMPONENT_Y).height)
+      upscaledRec.copyFrom(upscaledPic);
+    else
+      Picture::rescalePictureCNN(picC, predBuf, upscaledPic, upscaledRec, sliceQP, baseQP, sliceType);
+#else
     Picture::rescalePicture( scalingRatio, picC, pcPic->getScalingWindow(), upscaledRec, pps->getScalingWindow(), format, sps.getBitDepths(), false, false, sps.getHorCollocatedChromaFlag(), sps.getVerCollocatedChromaFlag() );
+#endif
   }
 
   for (int comp = 0; comp < ::getNumberValidComponents(formatD); comp++)
@@ -4559,7 +4583,31 @@ void EncGOP::xCalculateAddPSNR(Picture* pcPic, PelUnitBuf cPicD, const AccessUni
     }
 #endif
 
+#if JVET_AC0196_NNSR
+    if (m_pcEncLib->getUseNnsr() && m_pcEncLib->isResChangeInClvsEnabled())
+    {
+      const CPelBuf& upscaledOrg = (sps.getUseLmcs() || m_pcCfg->getGopBasedTemporalFilterEnabled()) ? pcPic->M_BUFS( 0, PIC_TRUE_ORIGINAL_INPUT).get( compID ) : pcPic->M_BUFS( 0, PIC_ORIGINAL_INPUT).get( compID );
+
+      const uint32_t upscaledWidth = upscaledOrg.width - ( m_pcEncLib->getPad( 0 ) >> ::getComponentScaleX( compID, format ) );
+      const uint32_t upscaledHeight = upscaledOrg.height - ( m_pcEncLib->getPad( 1 ) >> ( !!bPicIsField + ::getComponentScaleY( compID, format ) ) );
 
+      // create new buffers with correct dimensions
+      const CPelBuf upscaledRecPB( upscaledRec.get( compID ).bufAt( 0, 0 ), upscaledRec.get( compID ).stride, upscaledWidth, upscaledHeight );
+      const CPelBuf upscaledOrgPB( upscaledOrg.bufAt( 0, 0 ), upscaledOrg.stride, upscaledWidth, upscaledHeight );
+
+#if ENABLE_QPA
+      const uint64_t upscaledSSD = xFindDistortionPlane( upscaledRecPB, upscaledOrgPB, useWPSNR ? bitDepth : 0, ::getComponentScaleX( compID, format ) );
+#else
+      const uint64_t scaledSSD = xFindDistortionPlane( upsacledRecPB, upsacledOrgPB, 0 );
+#endif
+
+      upscaledPSNR[comp] = upscaledSSD ? 10.0 * log10( (double)maxval * maxval * upscaledWidth * upscaledHeight / (double)upscaledSSD ) : 999.99;
+    }
+    else
+    {
+      upscaledPSNR[comp] = dPSNR[comp];
+    }
+#else
     if (m_pcEncLib->isResChangeInClvsEnabled())
     {
       const CPelBuf& upscaledOrg = (sps.getUseLmcs() || m_pcCfg->getGopBasedTemporalFilterEnabled()) ? pcPic->M_BUFS( 0, PIC_TRUE_ORIGINAL_INPUT).get( compID ) : pcPic->M_BUFS( 0, PIC_ORIGINAL_INPUT).get( compID );
@@ -4579,6 +4627,7 @@ void EncGOP::xCalculateAddPSNR(Picture* pcPic, PelUnitBuf cPicD, const AccessUni
 
       upscaledPSNR[comp] = upscaledSSD ? 10.0 * log10( (double)maxval * maxval * upscaledWidth * upscaledHeight / (double)upscaledSSD ) : 999.99;
     }
+#endif
   }
 
 #if EXTENSION_360_VIDEO
@@ -4630,7 +4679,13 @@ void EncGOP::xCalculateAddPSNR(Picture* pcPic, PelUnitBuf cPicD, const AccessUni
   m_vRVM_RP.push_back( uibits );
 
   //===== add PSNR =====
-  m_gcAnalyzeAll.addResult(dPSNR, (double)uibits, MSEyuvframe, upscaledPSNR, msssim, isEncodeLtRef);
+  m_gcAnalyzeAll.addResult(
+#if JVET_AC0196_NNSR
+    upscaledPSNR,
+#else
+    dPSNR,
+#endif
+    (double)uibits, MSEyuvframe, upscaledPSNR, msssim, isEncodeLtRef);
 #if EXTENSION_360_VIDEO
   m_ext360.addResult(m_gcAnalyzeAll);
 #endif
@@ -4642,7 +4697,13 @@ void EncGOP::xCalculateAddPSNR(Picture* pcPic, PelUnitBuf cPicD, const AccessUni
 #endif
   if (pcSlice->isIntra())
   {
-    m_gcAnalyzeI.addResult(dPSNR, (double)uibits, MSEyuvframe, upscaledPSNR, msssim, isEncodeLtRef);
+    m_gcAnalyzeI.addResult(
+#if JVET_AC0196_NNSR
+      upscaledPSNR,
+#else
+      dPSNR,
+#endif
+      (double)uibits, MSEyuvframe, upscaledPSNR, msssim, isEncodeLtRef);
     *PSNR_Y = dPSNR[COMPONENT_Y];
 #if EXTENSION_360_VIDEO
     m_ext360.addResult(m_gcAnalyzeI);
@@ -4656,7 +4717,13 @@ void EncGOP::xCalculateAddPSNR(Picture* pcPic, PelUnitBuf cPicD, const AccessUni
   }
   if (pcSlice->isInterP())
   {
-    m_gcAnalyzeP.addResult(dPSNR, (double)uibits, MSEyuvframe, upscaledPSNR, msssim, isEncodeLtRef);
+    m_gcAnalyzeP.addResult(
+#if JVET_AC0196_NNSR
+      upscaledPSNR,
+#else
+      dPSNR, 
+#endif
+      (double)uibits, MSEyuvframe, upscaledPSNR, msssim, isEncodeLtRef);
     *PSNR_Y = dPSNR[COMPONENT_Y];
 #if EXTENSION_360_VIDEO
     m_ext360.addResult(m_gcAnalyzeP);
@@ -4670,7 +4737,13 @@ void EncGOP::xCalculateAddPSNR(Picture* pcPic, PelUnitBuf cPicD, const AccessUni
   }
   if (pcSlice->isInterB())
   {
-    m_gcAnalyzeB.addResult(dPSNR, (double)uibits, MSEyuvframe, upscaledPSNR, msssim, isEncodeLtRef);
+    m_gcAnalyzeB.addResult(
+#if JVET_AC0196_NNSR
+      upscaledPSNR,
+#else
+      dPSNR,
+#endif
+      (double)uibits, MSEyuvframe, upscaledPSNR, msssim, isEncodeLtRef);
     *PSNR_Y = dPSNR[COMPONENT_Y];
 #if EXTENSION_360_VIDEO
     m_ext360.addResult(m_gcAnalyzeB);
diff --git a/source/Lib/EncoderLib/EncLib.cpp b/source/Lib/EncoderLib/EncLib.cpp
index ed4aba799530eb62deddb8ac9bad12a1660bb5e7..8cca88ca79f083fa85e0e7deb3b0b188d6aa0e90 100644
--- a/source/Lib/EncoderLib/EncLib.cpp
+++ b/source/Lib/EncoderLib/EncLib.cpp
@@ -635,7 +635,11 @@ void EncLib::deletePicBuffer()
   m_cListPic.clear();
 }
 
-bool EncLib::encodePrep( bool flush, PelStorage* pcPicYuvOrg, PelStorage* cPicYuvTrueOrg, PelStorage* pcPicYuvFilteredOrg, const InputColourSpaceConversion snrCSC, std::list<PelUnitBuf*>& rcListPicYuvRecOut, int& iNumEncoded )
+bool EncLib::encodePrep( bool flush, PelStorage* pcPicYuvOrg, PelStorage* cPicYuvTrueOrg, PelStorage* pcPicYuvFilteredOrg, const InputColourSpaceConversion snrCSC, std::list<PelUnitBuf*>& rcListPicYuvRecOut, int& iNumEncoded
+#if ADAPTIVE_RPR
+  , PelStorage** ppcPicYuvRPR
+#endif
+)
 {
   if( m_compositeRefEnabled && m_cGOPEncoder.getPicBg()->getSpliceFull() && m_iPOCLast >= 10 && m_iNumPicRcvd == 0 && m_cGOPEncoder.getEncodedLTRef() == false )
   {
@@ -688,6 +692,132 @@ bool EncLib::encodePrep( bool flush, PelStorage* pcPicYuvOrg, PelStorage* cPicYu
     }
 #endif
 
+#if ADAPTIVE_RPR
+    const int iPOC = m_iPOCLast + (m_compositeRefEnabled ? 2 : 1);
+    double upscaledPSNR = 0.0, upscaledPSNR1 = 0.0, upscaledPSNR2 = 0.0;
+
+    if (m_resChangeInClvsEnabled && iPOC % getGOPSize() == 0)
+    {
+      // rescale Pelbuffer
+      std::array<std::pair<int, int>, 2> rprScalingRatio = { { { 8192, 8192 },{ 32768, 32768 } } };
+
+      const PPS *pOrgPPS = m_ppsMap.getPS(0);
+      const SPS *pOrgSPS = m_spsMap.getPS(pOrgPPS->getSPSId());
+      const ChromaFormat chFormatIdc = pOrgSPS->getChromaFormatIdc();
+
+      const PPS *pTempPPS = m_ppsMap.getPS(ENC_PPS_ID_RPR);
+      Picture::rescalePicture(rprScalingRatio[1], *pcPicYuvOrg, pOrgPPS->getScalingWindow(), *ppcPicYuvRPR[1], pTempPPS->getScalingWindow(), chFormatIdc, pOrgSPS->getBitDepths(), true, true,
+        pOrgSPS->getHorCollocatedChromaFlag(), pOrgSPS->getVerCollocatedChromaFlag());
+      
+     Picture::rescalePicture(rprScalingRatio[0], *ppcPicYuvRPR[1], pOrgPPS->getScalingWindow(), *ppcPicYuvRPR[0], pTempPPS->getScalingWindow(), chFormatIdc, pOrgSPS->getBitDepths(), true, true,
+       pOrgSPS->getHorCollocatedChromaFlag(), pOrgSPS->getVerCollocatedChromaFlag());
+
+      // Calculate PSNR
+      const  Pel*  pSrc0 = pcPicYuvOrg->get(COMPONENT_Y).bufAt(0, 0);
+      const  Pel*  pSrc1 = ppcPicYuvRPR[0]->get(COMPONENT_Y).bufAt(0, 0);
+
+      const Pel *pSrc0_U = pcPicYuvOrg->get(COMPONENT_Cb).bufAt(0, 0);
+      const Pel *pSrc1_U = ppcPicYuvRPR[0]->get(COMPONENT_Cb).bufAt(0, 0);
+
+      const Pel *pSrc0_V = pcPicYuvOrg->get(COMPONENT_Cr).bufAt(0, 0);
+      const Pel *pSrc1_V = ppcPicYuvRPR[0]->get(COMPONENT_Cr).bufAt(0, 0);
+
+      uint64_t uiTotalDiff = 0, uiTotalDiff1 = 0, uiTotalDiff2 = 0;
+      for (int y = 0; y < pcPicYuvOrg->get(COMPONENT_Y).height; y++)
+      {
+        for (int x = 0; x < pcPicYuvOrg->get(COMPONENT_Y).width; x++)
+        {
+          Intermediate_Int iTemp = pSrc0[x] - pSrc1[x];
+          uiTotalDiff += uint64_t(iTemp * iTemp);
+        }
+        pSrc0 += pcPicYuvOrg->get(COMPONENT_Y).stride;
+        pSrc1 += ppcPicYuvRPR[0]->get(COMPONENT_Y).stride;
+      }
+
+      for (int y = 0; y < pcPicYuvOrg->get(COMPONENT_Cb).height; y++)
+      {
+        for (int x = 0; x < pcPicYuvOrg->get(COMPONENT_Cb).width; x++)
+        {
+          Intermediate_Int iTemp = pSrc0_U[x] - pSrc1_U[x];
+          uiTotalDiff1 += uint64_t(iTemp * iTemp);
+
+          iTemp = pSrc0_V[x] - pSrc1_V[x];
+          uiTotalDiff2 += uint64_t(iTemp * iTemp);
+        }
+        pSrc0_U += pcPicYuvOrg->get(COMPONENT_Cb).stride;
+        pSrc1_U += ppcPicYuvRPR[0]->get(COMPONENT_Cb).stride;
+        pSrc0_V += pcPicYuvOrg->get(COMPONENT_Cr).stride;
+        pSrc1_V += ppcPicYuvRPR[0]->get(COMPONENT_Cr).stride;
+      }
+
+      const uint32_t maxval = 255 << (pOrgSPS->getBitDepth(CHANNEL_TYPE_LUMA) - 8);
+      upscaledPSNR  = uiTotalDiff ? 10.0 * log10((double) maxval * maxval * pOrgPPS->getPicWidthInLumaSamples() * pOrgPPS->getPicHeightInLumaSamples() / (double) uiTotalDiff) : 999.99;
+      upscaledPSNR1 = uiTotalDiff1 ? 10.0 * log10((double) maxval * maxval * (pOrgPPS->getPicWidthInLumaSamples() / 2) * (pOrgPPS->getPicHeightInLumaSamples() / 2) / (double) uiTotalDiff1) : 999.99;
+      upscaledPSNR2 = uiTotalDiff2 ? 10.0 * log10((double) maxval * maxval * (pOrgPPS->getPicWidthInLumaSamples() / 2) * (pOrgPPS->getPicHeightInLumaSamples() / 2) / (double) uiTotalDiff2) : 999.99;
+    }
+
+    if (iPOC % getGOPSize() == 0)
+    {
+      double basePSNR = 45 - (m_gopBasedTemporalFilterEnabled ? 0 : 1);
+      if (m_gopBasedTemporalFilterEnabled) //RA
+      {
+        if (upscaledPSNR1 > 40.0 && upscaledPSNR2 > 40.0) 
+        {
+          if (upscaledPSNR > 44.0) 
+          {
+            ppsID = ENC_PPS_ID_RPR;
+          }
+          else
+          {
+            if ((basePSNR - (m_iQP - 32) * 0.5) < upscaledPSNR)
+            {
+              ppsID = ENC_PPS_ID_RPR;
+            }
+            else
+            {
+              ppsID = 0;
+            }
+          }
+        }
+        else
+        {
+          if ((basePSNR - (m_iQP - 32) * 0.5) < upscaledPSNR)
+          {
+            ppsID = ENC_PPS_ID_RPR;
+          }
+          else
+          {
+            ppsID = 0;
+          }
+        }
+      }
+      else //AI
+      {
+        if (upscaledPSNR1 > 40.0 && upscaledPSNR2 > 40.0)
+        {
+          ppsID = ENC_PPS_ID_RPR;
+        }
+        else
+        {
+          if ((basePSNR - (m_iQP - 32) * 0.5) < upscaledPSNR)
+          {
+            ppsID = ENC_PPS_ID_RPR;
+          }
+          else
+          {
+            ppsID = 0;
+          }
+        }
+      }
+      m_iGOPRprPpsId = ppsID;
+    }
+    else
+    {
+      ppsID = m_iGOPRprPpsId;
+    }
+#elif JVET_AC0196_NNSR
+    ppsID = ENC_PPS_ID_RPR;
+#else
     if( m_resChangeInClvsEnabled && m_intraPeriod == -1 )
     {
       const int poc = m_iPOCLast + ( m_compositeRefEnabled ? 2 : 1 );
@@ -701,7 +831,7 @@ bool EncLib::encodePrep( bool flush, PelStorage* pcPicYuvOrg, PelStorage* cPicYu
         ppsID = 0;
       }
     }
-
+#endif
     if( m_vps->getMaxLayers() > 1 )
     {
       ppsID = m_vps->getGeneralLayerIdx( m_layerId );
@@ -2406,6 +2536,14 @@ int EncCfg::getQPForPicture(const uint32_t gopIndex, const Slice *pSlice) const
     }
 #endif
 
+#if ADAPTIVE_RPR
+    if (pSlice->getPPS()->getPPSId() == ENC_PPS_ID_RPR)
+    {
+      // adjust QP for rate matching. 
+      qp -= 0;
+    }
+#endif
+    
     if(sliceType==I_SLICE)
     {
       qp += getIntraQPOffset();
diff --git a/source/Lib/EncoderLib/EncLib.h b/source/Lib/EncoderLib/EncLib.h
index 52fb45d76bde60150d0453e0e43706589b639ac9..5622d8e7b68f22a8273061dfb869156183e4e0e0 100644
--- a/source/Lib/EncoderLib/EncLib.h
+++ b/source/Lib/EncoderLib/EncLib.h
@@ -181,7 +181,10 @@ public:
   APS**                     getApss() { return m_apss; }
   Ctx                       m_entropyCodingSyncContextState;      ///< leave in addition to vector for compatibility
   PLTBuf                    m_palettePredictorSyncState;
-
+#if ADAPTIVE_RPR
+  int                       m_iGOPRprPpsId;
+#endif
+  
 protected:
   void  xGetNewPicBuffer  ( std::list<PelUnitBuf*>& rcListPicYuvRecOut, Picture*& rpcPic, int ppsId ); ///< get picture buffer which will be processed. If ppsId<0, then the ppsMap will be queried for the first match.
 #if JVET_S0163_ON_TARGETOLS_SUBLAYERS
@@ -299,7 +302,11 @@ public:
                PelStorage* pcPicYuvFilteredOrg,
                const InputColourSpaceConversion snrCSC, // used for SNR calculations. Picture in original colour space.
                std::list<PelUnitBuf*>& rcListPicYuvRecOut,
-               int& iNumEncoded );
+               int& iNumEncoded
+#if ADAPTIVE_RPR
+               , PelStorage** ppcPicYuvRPR
+#endif
+  );
 
   bool encode( const InputColourSpaceConversion snrCSC, // used for SNR calculations. Picture in original colour space.
                std::list<PelUnitBuf*>& rcListPicYuvRecOut,
diff --git a/source/Lib/Utilities/VideoIOYuv.cpp b/source/Lib/Utilities/VideoIOYuv.cpp
index cc26a7d61570a1832d9aba6d10bf0cad095abcec..e0e0519210f69c30323143bd62a3ee1dc5238755 100644
--- a/source/Lib/Utilities/VideoIOYuv.cpp
+++ b/source/Lib/Utilities/VideoIOYuv.cpp
@@ -1299,7 +1299,12 @@ void VideoIOYuv::ColourSpaceConvert(const CPelUnitBuf &src, PelUnitBuf &dest, co
   }
 }
 
-bool VideoIOYuv::writeUpscaledPicture( const SPS& sps, const PPS& pps, const CPelUnitBuf& pic, const InputColourSpaceConversion ipCSC, const bool bPackedYUVOutputMode, int outputChoice, ChromaFormat format, const bool bClipToRec709 )
+bool VideoIOYuv::writeUpscaledPicture( const SPS& sps, const PPS& pps, const CPelUnitBuf& pic, const InputColourSpaceConversion ipCSC, const bool bPackedYUVOutputMode, 
+#if JVET_AC0196_NNSR
+  const PelUnitBuf& predBuf, int sliceQP, int baseQP, SliceType sliceType, 
+#endif
+  int outputChoice, ChromaFormat format, const bool bClipToRec709
+)
 {
   ChromaFormat chromaFormatIDC = sps.getChromaFormatIdc();
   bool ret = false;
@@ -1327,12 +1332,28 @@ bool VideoIOYuv::writeUpscaledPicture( const SPS& sps, const PPS& pps, const CPe
       const Window& beforeScalingWindow = pps.getScalingWindow();
       int refPicWidth = pps.getPicWidthInLumaSamples()   - SPS::getWinUnitX( sps.getChromaFormatIdc() ) * ( beforeScalingWindow.getWindowLeftOffset() + beforeScalingWindow.getWindowRightOffset() );
       int refPicHeight = pps.getPicHeightInLumaSamples() - SPS::getWinUnitY( sps.getChromaFormatIdc() ) * ( beforeScalingWindow.getWindowTopOffset()  + beforeScalingWindow.getWindowBottomOffset() );
+#if ADAPTIVE_RPR
+      if (refPicWidth == curPicWidth && refPicHeight == curPicHeight)
+      {
+        refPicWidth  = curPicWidth >> 1;
+        refPicHeight = curPicHeight >> 1;
+      }
+#endif
 
       int xScale = ( ( refPicWidth << SCALE_RATIO_BITS ) + ( curPicWidth >> 1 ) ) / curPicWidth;
       int yScale = ( ( refPicHeight << SCALE_RATIO_BITS ) + ( curPicHeight >> 1 ) ) / curPicHeight;
 
+#if JVET_AC0196_NNSR
+      PelStorage upscaledRPR;
+      upscaledRPR.create(chromaFormatIDC, Area( Position(), Size( sps.getMaxPicWidthInLumaSamples(), sps.getMaxPicHeightInLumaSamples() ) ));
+      Picture::rescalePicture(std::pair<int, int>(xScale, yScale), pic, pps.getScalingWindow(), upscaledRPR,
+                              afterScaleWindowFullResolution, chromaFormatIDC, sps.getBitDepths(), false, false,
+                              sps.getHorCollocatedChromaFlag(), sps.getVerCollocatedChromaFlag());
+      Picture::rescalePictureCNN(pic, predBuf, upscaledRPR, upscaledPic, sliceQP, baseQP, sliceType);
+#else
       Picture::rescalePicture( std::pair<int, int>( xScale, yScale ), pic, pps.getScalingWindow(), upscaledPic, afterScaleWindowFullResolution, chromaFormatIDC, sps.getBitDepths(), false, false, sps.getHorCollocatedChromaFlag(), sps.getVerCollocatedChromaFlag() );
-
+#endif
+      
       ret = write( sps.getMaxPicWidthInLumaSamples(), sps.getMaxPicHeightInLumaSamples(), upscaledPic,
         ipCSC,
         bPackedYUVOutputMode,
diff --git a/source/Lib/Utilities/VideoIOYuv.h b/source/Lib/Utilities/VideoIOYuv.h
index 4140383623811b9ab61bf6b6d2752bd618f0a788..932e1aca972889e6788276eb5b2a377d62a13adc 100644
--- a/source/Lib/Utilities/VideoIOYuv.h
+++ b/source/Lib/Utilities/VideoIOYuv.h
@@ -105,8 +105,12 @@ public:
   int   getBitdepthShift( int ch )          { return m_bitdepthShift[ch]; }
   int   getFileBitdepth( int ch )           { return m_fileBitdepth[ch];  }
 
-  bool  writeUpscaledPicture( const SPS& sps, const PPS& pps, const CPelUnitBuf& pic,
-    const InputColourSpaceConversion ipCSC, const bool bPackedYUVOutputMode, int outputChoice = 0, ChromaFormat format = NUM_CHROMA_FORMAT, const bool bClipToRec709 = false ); ///< write one upsaled YUV frame
+  bool  writeUpscaledPicture( const SPS& sps, const PPS& pps, const CPelUnitBuf& pic, const InputColourSpaceConversion ipCSC, const bool bPackedYUVOutputMode, 
+#if JVET_AC0196_NNSR
+    const PelUnitBuf& predBuf, int sliceQP, int baseQP, SliceType sliceType, 
+#endif
+    int outputChoice = 0, ChromaFormat format = NUM_CHROMA_FORMAT, const bool bClipToRec709 = false
+  ); ///< write one upsaled YUV frame
 
 };