From fe02142cd0e9e27eaeba2270ca41f021108d3f8a Mon Sep 17 00:00:00 2001
From: Karsten Suehring <karsten.suehring@hhi.fraunhofer.de>
Date: Sun, 12 Apr 2020 12:07:08 +0200
Subject: [PATCH] Bugfix: initialization of slice map from subpicture layout
 (bug ids #1005, #1006, #1007)

- initialize slice map from subpicture map
- encoder support for inferring slice map from subpicture map
- improve software manual
---
 doc/software-manual.tex             | 101 ++++++++---
 source/App/EncoderApp/EncAppCfg.cpp | 271 ++++++++++++++--------------
 source/App/EncoderApp/EncAppCfg.h   |   1 -
 source/Lib/CommonLib/Slice.cpp      | 174 ++++++++++++------
 source/Lib/CommonLib/Slice.h        |   2 +-
 source/Lib/DecoderLib/VLCReader.cpp |   7 +-
 source/Lib/EncoderLib/EncLib.cpp    |   5 +-
 7 files changed, 334 insertions(+), 227 deletions(-)

diff --git a/doc/software-manual.tex b/doc/software-manual.tex
index 99f1f3b45..4a5521da6 100644
--- a/doc/software-manual.tex
+++ b/doc/software-manual.tex
@@ -1082,24 +1082,6 @@ Specifies the value of one_subpic_per_pic_constraint_flag
 Specifies the value of general_frame_only_constraint_flag
 \\
 
-\Option{SubPicInfoPresentFlag} &
-%\ShortOption{\None} &
-\Default{false} &
-Specifies the value of subpicture subpic_info_present_flag
-\\
-
-\Option{SubPicIdMappingExplicitlySignalledFlag} &
-%\ShortOption{\None} &
-\Default{false} &
-Specifies the value of subpicture subpic_id_mapping_explicitly_signalled_flag
-\\
-
-\Option{SubPicIdMappingInSpsFlag} &
-%\ShortOption{\None} &
-\Default{false} &
-Specifies the value of subpicture subpic_id_mapping_in_sps_flag
-\\
-
 \end{OptionTableNoShorthand}
 
 
@@ -1991,6 +1973,12 @@ Tile row heights in units of CTUs. Last row height in list will be repeated unif
 Use raster-scan or rectangular slices (0: rectangular, 1: raster-scan).
 \\
 
+\Option{SingleSlicePerSubpic} &
+%\ShortOption{\None} &
+\Default{false} &
+Enables slice layout derivation from subpicture layout. Requires more than one subpicture to be enabled. If enabled, all other slice layout parameters will be ignored.
+\\
+
 \Option{RectSlicePositions} &
 %\ShortOption{\None} &
 \Default{\NotSet} &
@@ -2051,16 +2039,83 @@ Note that when a slice contains more than one tile, entry point offsets for tile
 \end{OptionTableNoShorthand}
 
 %%
-%% Slice/Sub-Picture coding parameters
+%% Subpicture coding parameters
 %%
-\begin{OptionTableNoShorthand}{Slice and Sub-Picture coding parameters}{tab:subpicture-coding}
+\begin{OptionTableNoShorthand}{Subpicture coding parameters}{tab:subpicture-coding}
 
-\Option{EnableSubPicPartitioning} &
+\Option{SubPicInfoPresentFlag} &
 %\ShortOption{\None} &
-\Default{1} &
-Enable Sub Picture partitioning (0: single slice per sub-picture, 1: multiple slices per sub-picture can be used).
+\Default{false} &
+Enables conding of subpictures.
 \\
 
+\Option{NumSubPics} &
+%\ShortOption{\None} &
+\Default{0} &
+Number of subpictures. Must be greater that zero, if SubPicInfoPresentFlag is enabled.
+\\
+
+\Option{SubPicCtuTopLeftX} &
+%\ShortOption{\None} &
+\Default{\None} &
+Array of subpicture top left horizontal (x) coordinates. The number of entries must be equal to NumSubPics.
+\\
+
+\Option{SubPicCtuTopLeftY} &
+%\ShortOption{\None} &
+\Default{\None} &
+Array of subpicture top left vertical (y) coordinates. The number of entries must be equal to NumSubPics.
+\\
+
+\Option{SubPicWidth} &
+%\ShortOption{\None} &
+\Default{\None} &
+Array of subpicture widths. The number of entries must be equal to NumSubPics.
+\\
+
+\Option{SubPicHeight} &
+%\ShortOption{\None} &
+\Default{\None} &
+Array of subpicture heights. The number of entries must be equal to NumSubPics.
+\\
+
+\Option{SubPicTreatedAsPicFlag} &
+%\ShortOption{\None} &
+\Default{\None} &
+Setting of subpic_treated_as_pic_flag for each subpicture. If enabled subpicture boundaries will be treated as picture boundaries. The number of entries must be equal to NumSubPics.
+\\
+
+\Option{LoopFilterAcrossSubpicEnabledFlag} &
+%\ShortOption{\None} &
+\Default{\None} &
+Enables loop filtering across subpicture boundaries for each subpicture. The number of entries must be equal to NumSubPics.
+\\
+
+\Option{SubPicIdMappingExplicitlySignalledFlag} &
+%\ShortOption{\None} &
+\Default{false} &
+Enables explicit signalling of a subpicture ID map. If disabled, a default map will be derived.
+\\
+
+\Option{SubPicIdMappingInSpsFlag} &
+%\ShortOption{\None} &
+\Default{false} &
+Specifies wheter to signal the subpicture ID map in SPS or PPS. If SubPicIdMappingInSpsFlag is enabled subpicture IDs are signalled in SPS, otherwise in PPS.
+\\
+
+\Option{SubPicIdLen} &
+%\ShortOption{\None} &
+\Default{0} &
+Length of the subpicture IDs in bits. (1<<SubPicIdLen) must be bigger than the number of subpictures and the highes subpicture ID specifid in SubPicId. 
+\\
+
+\Option{SubPicId} &
+%\ShortOption{\None} &
+\Default{\None} &
+Array of subpicture IDs. The number of entries must be equal to NumSubPics.
+\\
+
+
 \end{OptionTableNoShorthand}
 
 %%
diff --git a/source/App/EncoderApp/EncAppCfg.cpp b/source/App/EncoderApp/EncAppCfg.cpp
index cd1e4e43b..087269d5c 100644
--- a/source/App/EncoderApp/EncAppCfg.cpp
+++ b/source/App/EncoderApp/EncAppCfg.cpp
@@ -909,6 +909,7 @@ bool EncAppCfg::parseCfg( int argc, char* argv[] )
 #endif
   ("SubPicIdLen",                                     m_subPicIdLen,                                       0u, "specifies the number of bits used to represent the syntax element sps_subpic_id[ i ]. ")
   ("SubPicId",                                        cfg_subPicId,                              cfg_subPicId, "specifies that subpicture ID of the i-th subpicture")
+  ("SingleSlicePerSubpic",                            m_singleSlicePerSubPicFlag,                       false, "Enables setting of a single slice per sub-picture (no explicit configuration required)")
   ("EnablePartitionConstraintsOverride",              m_SplitConsOverrideEnabledFlag,                    true, "Enable partition constraints override")
   ("MinQTISlice",                                     m_uiMinQT[0],                                        8u, "MinQTISlice")
   ("MinQTLumaISlice",                                 m_uiMinQT[0],                                        8u, "MinQTLumaISlice")
@@ -1197,7 +1198,6 @@ bool EncAppCfg::parseCfg( int argc, char* argv[] )
   ("RasterSliceSizes",                                cfgRasterSliceSize,                  cfgRasterSliceSize, "Raster-scan slice sizes in units of tiles. Last size in list will be repeated uniformly to cover any remaining tiles in the picture")
   ("DisableLoopFilterAcrossTiles",                    m_disableLFCrossTileBoundaryFlag,                 false, "Loop filtering applied across tile boundaries or not (0: filter across tile boundaries  1: do not filter across tile boundaries)")
   ("DisableLoopFilterAcrossSlices",                   m_disableLFCrossSliceBoundaryFlag,                false, "Loop filtering applied across slice boundaries or not (0: filter across slice boundaries 1: do not filter across slice boundaries)")
-  ("EnableSubPicPartitioning",                        m_subPicPartitionFlag,                             true, "Enable Sub-Picture partitioning (0: single slice per sub-picture, 1: multiple slices per sub-picture can be used)")
   ("FastUDIUseMPMEnabled",                            m_bFastUDIUseMPMEnabled,                           true, "If enabled, adapt intra direction search, accounting for MPM")
   ("FastMEForGenBLowDelayEnabled",                    m_bFastMEForGenBLowDelayEnabled,                   true, "If enabled use a fast ME for generalised B Low Delay slices")
   ("UseBLambdaForNonKeyLowDelayPictures",             m_bUseBLambdaForNonKeyLowDelayPictures,            true, "Enables use of B-Lambda for non-key low-delay pictures")
@@ -1783,14 +1783,6 @@ bool EncAppCfg::parseCfg( int argc, char* argv[] )
   {
     m_subProfile[i] = cfg_SubProfile.values[i];
   }
-  if (m_subPicPartitionFlag)
-  {
-    m_singleSlicePerSubPicFlag = false;
-  }
-  else
-  {
-    m_singleSlicePerSubPicFlag = true;
-  }
   /* rules for input, output and internal bitdepths as per help text */
   if (m_MSBExtendedBitDepth[CHANNEL_TYPE_LUMA  ] == 0)
   {
@@ -3387,155 +3379,162 @@ bool EncAppCfg::xCheckParameter()
     // rectangular slices
     if( !m_rasterSliceFlag )
     {
-      uint32_t sliceIdx;
-      bool     needTileIdxDelta = false;
-
-      // generate slice list for the simplified fixed-rectangular-slice-size config option
-      if( m_rectSliceFixedWidth > 0 && m_rectSliceFixedHeight > 0 )
+      if (m_singleSlicePerSubPicFlag)
       {
-        int tileIdx = 0;
-        m_rectSlicePos.clear();
-        while( tileIdx < pps.getNumTiles() ) 
-        {
-          uint32_t startTileX = tileIdx % pps.getNumTileColumns();
-          uint32_t startTileY = tileIdx / pps.getNumTileColumns();
-          uint32_t startCtuX  = pps.getTileColumnBd( startTileX );
-          uint32_t startCtuY  = pps.getTileRowBd( startTileY );
-          uint32_t stopCtuX   = (startTileX + m_rectSliceFixedWidth)  >= pps.getNumTileColumns() ? pps.getPicWidthInCtu() - 1  : pps.getTileColumnBd( startTileX + m_rectSliceFixedWidth ) - 1;
-          uint32_t stopCtuY   = (startTileY + m_rectSliceFixedHeight) >= pps.getNumTileRows()    ? pps.getPicHeightInCtu() - 1 : pps.getTileRowBd( startTileY + m_rectSliceFixedHeight ) - 1;
-          uint32_t stopTileX  = pps.ctuToTileCol( stopCtuX );
-          uint32_t stopTileY  = pps.ctuToTileRow( stopCtuY );
-          
-          // add rectangular slice to list
-          m_rectSlicePos.push_back( startCtuY * pps.getPicWidthInCtu() + startCtuX );          
-          m_rectSlicePos.push_back( stopCtuY  * pps.getPicWidthInCtu() + stopCtuX  );
-          
-          // get slice size in tiles
-          uint32_t sliceWidth  = stopTileX - startTileX + 1;
-          uint32_t sliceHeight = stopTileY - startTileY + 1;
+        xConfirmPara( m_subPicInfoPresentFlag == 0 || m_numSubPics < 2, "SingleSlicePerSubPic requires more than one subpicture.");
+      }
+      else
+      {
+        uint32_t sliceIdx;
+        bool     needTileIdxDelta = false;
 
-          // move to next tile in raster scan order
-          tileIdx += sliceWidth;
-          if( tileIdx % pps.getNumTileColumns() == 0 )
+        // generate slice list for the simplified fixed-rectangular-slice-size config option
+        if( m_rectSliceFixedWidth > 0 && m_rectSliceFixedHeight > 0 )
+        {
+          int tileIdx = 0;
+          m_rectSlicePos.clear();
+          while( tileIdx < pps.getNumTiles() )
           {
-            tileIdx += (sliceHeight - 1) * pps.getNumTileColumns();
+            uint32_t startTileX = tileIdx % pps.getNumTileColumns();
+            uint32_t startTileY = tileIdx / pps.getNumTileColumns();
+            uint32_t startCtuX  = pps.getTileColumnBd( startTileX );
+            uint32_t startCtuY  = pps.getTileRowBd( startTileY );
+            uint32_t stopCtuX   = (startTileX + m_rectSliceFixedWidth)  >= pps.getNumTileColumns() ? pps.getPicWidthInCtu() - 1  : pps.getTileColumnBd( startTileX + m_rectSliceFixedWidth ) - 1;
+            uint32_t stopCtuY   = (startTileY + m_rectSliceFixedHeight) >= pps.getNumTileRows()    ? pps.getPicHeightInCtu() - 1 : pps.getTileRowBd( startTileY + m_rectSliceFixedHeight ) - 1;
+            uint32_t stopTileX  = pps.ctuToTileCol( stopCtuX );
+            uint32_t stopTileY  = pps.ctuToTileRow( stopCtuY );
+
+            // add rectangular slice to list
+            m_rectSlicePos.push_back( startCtuY * pps.getPicWidthInCtu() + startCtuX );
+            m_rectSlicePos.push_back( stopCtuY  * pps.getPicWidthInCtu() + stopCtuX  );
+
+            // get slice size in tiles
+            uint32_t sliceWidth  = stopTileX - startTileX + 1;
+            uint32_t sliceHeight = stopTileY - startTileY + 1;
+
+            // move to next tile in raster scan order
+            tileIdx += sliceWidth;
+            if( tileIdx % pps.getNumTileColumns() == 0 )
+            {
+              tileIdx += (sliceHeight - 1) * pps.getNumTileColumns();
+            }
           }
         }
-      }
 
-      xConfirmPara( m_rectSlicePos.size() & 1, "Odd number of rectangular slice positions provided. Rectangular slice positions must be specified in pairs of (top-left / bottom-right) raster-scan CTU addresses.");
-      
-      // set default slice size if not provided
-      if( m_rectSlicePos.size() == 0 ) 
-      {
-        m_rectSlicePos.push_back( 0 );
-        m_rectSlicePos.push_back( pps.getPicWidthInCtu() * pps.getPicHeightInCtu() - 1 );
-      }
-      pps.setNumSlicesInPic( (uint32_t)(m_rectSlicePos.size() >> 1) );
-      xConfirmPara(pps.getNumSlicesInPic() > getMaxSlicesByLevel( m_level ), "Number of rectangular slices exceeds maximum number allowed according to specified level");
-      pps.initRectSlices();
+        xConfirmPara( m_rectSlicePos.size() & 1, "Odd number of rectangular slice positions provided. Rectangular slice positions must be specified in pairs of (top-left / bottom-right) raster-scan CTU addresses.");
 
-      // set slice parameters from CTU addresses
-      for( sliceIdx = 0; sliceIdx < pps.getNumSlicesInPic(); sliceIdx++ )
-      {
-        xConfirmPara( m_rectSlicePos[2*sliceIdx]     >= pps.getPicWidthInCtu() * pps.getPicHeightInCtu(), "Rectangular slice position exceeds total number of CTU in picture.");
-        xConfirmPara( m_rectSlicePos[2*sliceIdx + 1] >= pps.getPicWidthInCtu() * pps.getPicHeightInCtu(), "Rectangular slice position exceeds total number of CTU in picture.");
-
-        // map raster scan CTU address to X/Y position
-        uint32_t startCtuX = m_rectSlicePos[2*sliceIdx]     % pps.getPicWidthInCtu();
-        uint32_t startCtuY = m_rectSlicePos[2*sliceIdx]     / pps.getPicWidthInCtu();
-        uint32_t stopCtuX  = m_rectSlicePos[2*sliceIdx + 1] % pps.getPicWidthInCtu();
-        uint32_t stopCtuY  = m_rectSlicePos[2*sliceIdx + 1] / pps.getPicWidthInCtu();
-        
-        // get corresponding tile index
-        uint32_t startTileX = pps.ctuToTileCol( startCtuX );
-        uint32_t startTileY = pps.ctuToTileRow( startCtuY );
-        uint32_t stopTileX  = pps.ctuToTileCol( stopCtuX );
-        uint32_t stopTileY  = pps.ctuToTileRow( stopCtuY );
-        uint32_t tileIdx    = startTileY * pps.getNumTileColumns() + startTileX;
-
-        // get slice size in tiles
-        uint32_t sliceWidth  = stopTileX - startTileX + 1;
-        uint32_t sliceHeight = stopTileY - startTileY + 1;
-        
-        // check for slice / tile alignment
-        xConfirmPara( startCtuX != pps.getTileColumnBd( startTileX ), "Rectangular slice position does not align with a left tile edge.");
-        xConfirmPara( stopCtuX  != (pps.getTileColumnBd( stopTileX + 1 ) - 1), "Rectangular slice position does not align with a right tile edge.");
-        if( sliceWidth > 1 || sliceHeight > 1 )
+        // set default slice size if not provided
+        if( m_rectSlicePos.size() == 0 )
         {
-          xConfirmPara( startCtuY != pps.getTileRowBd( startTileY ), "Rectangular slice position does not align with a top tile edge.");
-          xConfirmPara( stopCtuY  != (pps.getTileRowBd( stopTileY + 1 ) - 1), "Rectangular slice position does not align with a bottom tile edge.");
+          m_rectSlicePos.push_back( 0 );
+          m_rectSlicePos.push_back( pps.getPicWidthInCtu() * pps.getPicHeightInCtu() - 1 );
         }
+        pps.setNumSlicesInPic( (uint32_t)(m_rectSlicePos.size() >> 1) );
+        xConfirmPara(pps.getNumSlicesInPic() > getMaxSlicesByLevel( m_level ), "Number of rectangular slices exceeds maximum number allowed according to specified level");
+        pps.initRectSlices();
 
-        // set slice size and tile index
-        pps.setSliceWidthInTiles( sliceIdx, sliceWidth );
-        pps.setSliceHeightInTiles( sliceIdx, sliceHeight );
-        pps.setSliceTileIdx( sliceIdx, tileIdx );
-        if( sliceIdx > 0 && !needTileIdxDelta )
+        // set slice parameters from CTU addresses
+        for( sliceIdx = 0; sliceIdx < pps.getNumSlicesInPic(); sliceIdx++ )
         {
-          uint32_t lastTileIdx = pps.getSliceTileIdx( sliceIdx-1 );
-          lastTileIdx += pps.getSliceWidthInTiles( sliceIdx-1 );
-          if( lastTileIdx % pps.getNumTileColumns() == 0)
+          xConfirmPara( m_rectSlicePos[2*sliceIdx]     >= pps.getPicWidthInCtu() * pps.getPicHeightInCtu(), "Rectangular slice position exceeds total number of CTU in picture.");
+          xConfirmPara( m_rectSlicePos[2*sliceIdx + 1] >= pps.getPicWidthInCtu() * pps.getPicHeightInCtu(), "Rectangular slice position exceeds total number of CTU in picture.");
+
+          // map raster scan CTU address to X/Y position
+          uint32_t startCtuX = m_rectSlicePos[2*sliceIdx]     % pps.getPicWidthInCtu();
+          uint32_t startCtuY = m_rectSlicePos[2*sliceIdx]     / pps.getPicWidthInCtu();
+          uint32_t stopCtuX  = m_rectSlicePos[2*sliceIdx + 1] % pps.getPicWidthInCtu();
+          uint32_t stopCtuY  = m_rectSlicePos[2*sliceIdx + 1] / pps.getPicWidthInCtu();
+
+          // get corresponding tile index
+          uint32_t startTileX = pps.ctuToTileCol( startCtuX );
+          uint32_t startTileY = pps.ctuToTileRow( startCtuY );
+          uint32_t stopTileX  = pps.ctuToTileCol( stopCtuX );
+          uint32_t stopTileY  = pps.ctuToTileRow( stopCtuY );
+          uint32_t tileIdx    = startTileY * pps.getNumTileColumns() + startTileX;
+
+          // get slice size in tiles
+          uint32_t sliceWidth  = stopTileX - startTileX + 1;
+          uint32_t sliceHeight = stopTileY - startTileY + 1;
+
+          // check for slice / tile alignment
+          xConfirmPara( startCtuX != pps.getTileColumnBd( startTileX ), "Rectangular slice position does not align with a left tile edge.");
+          xConfirmPara( stopCtuX  != (pps.getTileColumnBd( stopTileX + 1 ) - 1), "Rectangular slice position does not align with a right tile edge.");
+          if( sliceWidth > 1 || sliceHeight > 1 )
           {
-            lastTileIdx += (pps.getSliceHeightInTiles( sliceIdx-1 ) - 1) * pps.getNumTileColumns();
+            xConfirmPara( startCtuY != pps.getTileRowBd( startTileY ), "Rectangular slice position does not align with a top tile edge.");
+            xConfirmPara( stopCtuY  != (pps.getTileRowBd( stopTileY + 1 ) - 1), "Rectangular slice position does not align with a bottom tile edge.");
           }
-          if( lastTileIdx != tileIdx )
+
+          // set slice size and tile index
+          pps.setSliceWidthInTiles( sliceIdx, sliceWidth );
+          pps.setSliceHeightInTiles( sliceIdx, sliceHeight );
+          pps.setSliceTileIdx( sliceIdx, tileIdx );
+          if( sliceIdx > 0 && !needTileIdxDelta )
           {
-            needTileIdxDelta = true;
+            uint32_t lastTileIdx = pps.getSliceTileIdx( sliceIdx-1 );
+            lastTileIdx += pps.getSliceWidthInTiles( sliceIdx-1 );
+            if( lastTileIdx % pps.getNumTileColumns() == 0)
+            {
+              lastTileIdx += (pps.getSliceHeightInTiles( sliceIdx-1 ) - 1) * pps.getNumTileColumns();
+            }
+            if( lastTileIdx != tileIdx )
+            {
+              needTileIdxDelta = true;
+            }
           }
-        }
 
-        // special case for multiple slices within a single tile
-        if( sliceWidth == 1 && sliceHeight == 1 )
-        {
-          uint32_t firstSliceIdx = sliceIdx;
-          uint32_t numSlicesInTile = 1;
-          pps.setSliceHeightInCtu( sliceIdx, stopCtuY - startCtuY + 1 );
-          
-          while( sliceIdx < pps.getNumSlicesInPic()-1 ) 
+          // special case for multiple slices within a single tile
+          if( sliceWidth == 1 && sliceHeight == 1 )
           {
-            uint32_t nextTileIdx;
-            startCtuX   = m_rectSlicePos[2*(sliceIdx+1)]     % pps.getPicWidthInCtu();
-            startCtuY   = m_rectSlicePos[2*(sliceIdx+1)]     / pps.getPicWidthInCtu();
-            stopCtuX    = m_rectSlicePos[2*(sliceIdx+1) + 1] % pps.getPicWidthInCtu();
-            stopCtuY    = m_rectSlicePos[2*(sliceIdx+1) + 1] / pps.getPicWidthInCtu();          
-            startTileX  = pps.ctuToTileCol( startCtuX );
-            startTileY  = pps.ctuToTileRow( startCtuY );
-            stopTileX   = pps.ctuToTileCol( stopCtuX );
-            stopTileY   = pps.ctuToTileRow( stopCtuY );
-            nextTileIdx = startTileY * pps.getNumTileColumns() + startTileX;
-            sliceWidth  = stopTileX - startTileX + 1;
-            sliceHeight = stopTileY - startTileY + 1;
-            if(nextTileIdx != tileIdx || sliceWidth != 1 || sliceHeight != 1) 
+            uint32_t firstSliceIdx = sliceIdx;
+            uint32_t numSlicesInTile = 1;
+            pps.setSliceHeightInCtu( sliceIdx, stopCtuY - startCtuY + 1 );
+
+            while( sliceIdx < pps.getNumSlicesInPic()-1 )
             {
-              break;
+              uint32_t nextTileIdx;
+              startCtuX   = m_rectSlicePos[2*(sliceIdx+1)]     % pps.getPicWidthInCtu();
+              startCtuY   = m_rectSlicePos[2*(sliceIdx+1)]     / pps.getPicWidthInCtu();
+              stopCtuX    = m_rectSlicePos[2*(sliceIdx+1) + 1] % pps.getPicWidthInCtu();
+              stopCtuY    = m_rectSlicePos[2*(sliceIdx+1) + 1] / pps.getPicWidthInCtu();
+              startTileX  = pps.ctuToTileCol( startCtuX );
+              startTileY  = pps.ctuToTileRow( startCtuY );
+              stopTileX   = pps.ctuToTileCol( stopCtuX );
+              stopTileY   = pps.ctuToTileRow( stopCtuY );
+              nextTileIdx = startTileY * pps.getNumTileColumns() + startTileX;
+              sliceWidth  = stopTileX - startTileX + 1;
+              sliceHeight = stopTileY - startTileY + 1;
+              if(nextTileIdx != tileIdx || sliceWidth != 1 || sliceHeight != 1)
+              {
+                break;
+              }
+              numSlicesInTile++;
+              sliceIdx++;
+              pps.setSliceWidthInTiles( sliceIdx, 1 );
+              pps.setSliceHeightInTiles( sliceIdx, 1 );
+              pps.setSliceTileIdx( sliceIdx, tileIdx );
+              pps.setSliceHeightInCtu( sliceIdx, stopCtuY - startCtuY + 1 );
             }
-            numSlicesInTile++;
-            sliceIdx++;
-            pps.setSliceWidthInTiles( sliceIdx, 1 );
-            pps.setSliceHeightInTiles( sliceIdx, 1 );
-            pps.setSliceTileIdx( sliceIdx, tileIdx );    
-            pps.setSliceHeightInCtu( sliceIdx, stopCtuY - startCtuY + 1 );
+            pps.setNumSlicesInTile( firstSliceIdx, numSlicesInTile );
           }
-          pps.setNumSlicesInTile( firstSliceIdx, numSlicesInTile );
         }
-      }
-      pps.setTileIdxDeltaPresentFlag( needTileIdxDelta );
-      m_tileIdxDeltaPresentFlag = needTileIdxDelta;
-      
-      // check rectangular slice mapping and full picture CTU coverage
-      pps.initRectSliceMap();
+        pps.setTileIdxDeltaPresentFlag( needTileIdxDelta );
+        m_tileIdxDeltaPresentFlag = needTileIdxDelta;
 
-      // store rectangular slice parameters from temporary PPS structure
-      m_numSlicesInPic = pps.getNumSlicesInPic();
-      m_rectSlices.resize( pps.getNumSlicesInPic() );
-      for( sliceIdx = 0; sliceIdx < pps.getNumSlicesInPic(); sliceIdx++ )
-      {
-        m_rectSlices[sliceIdx].setSliceWidthInTiles( pps.getSliceWidthInTiles(sliceIdx) );
-        m_rectSlices[sliceIdx].setSliceHeightInTiles( pps.getSliceHeightInTiles(sliceIdx) );
-        m_rectSlices[sliceIdx].setNumSlicesInTile( pps.getNumSlicesInTile(sliceIdx) );
-        m_rectSlices[sliceIdx].setSliceHeightInCtu( pps.getSliceHeightInCtu(sliceIdx) );
-        m_rectSlices[sliceIdx].setTileIdx( pps.getSliceTileIdx(sliceIdx) );
+        // check rectangular slice mapping and full picture CTU coverage
+        pps.initRectSliceMap(nullptr);
+
+        // store rectangular slice parameters from temporary PPS structure
+        m_numSlicesInPic = pps.getNumSlicesInPic();
+        m_rectSlices.resize( pps.getNumSlicesInPic() );
+        for( sliceIdx = 0; sliceIdx < pps.getNumSlicesInPic(); sliceIdx++ )
+        {
+          m_rectSlices[sliceIdx].setSliceWidthInTiles( pps.getSliceWidthInTiles(sliceIdx) );
+          m_rectSlices[sliceIdx].setSliceHeightInTiles( pps.getSliceHeightInTiles(sliceIdx) );
+          m_rectSlices[sliceIdx].setNumSlicesInTile( pps.getNumSlicesInTile(sliceIdx) );
+          m_rectSlices[sliceIdx].setSliceHeightInCtu( pps.getSliceHeightInCtu(sliceIdx) );
+          m_rectSlices[sliceIdx].setTileIdx( pps.getSliceTileIdx(sliceIdx) );
+        }
       }
     }
     // raster-scan slices
diff --git a/source/App/EncoderApp/EncAppCfg.h b/source/App/EncoderApp/EncAppCfg.h
index 9897961ac..197ebafd8 100644
--- a/source/App/EncoderApp/EncAppCfg.h
+++ b/source/App/EncoderApp/EncAppCfg.h
@@ -499,7 +499,6 @@ protected:
   std::vector<RectSlice> m_rectSlices;                        ///< derived list of rectangular slice signalling parameters
   uint32_t  m_numTileCols;                                    ///< derived number of tile columns
   uint32_t  m_numTileRows;                                    ///< derived number of tile rows
-  bool      m_subPicPartitionFlag;
   bool      m_singleSlicePerSubPicFlag;
   bool      m_entropyCodingSyncEnabledFlag;
 #if JVET_Q0151_Q0205_ENTRYPOINTS
diff --git a/source/Lib/CommonLib/Slice.cpp b/source/Lib/CommonLib/Slice.cpp
index 75ad5e25a..d213a2821 100644
--- a/source/Lib/CommonLib/Slice.cpp
+++ b/source/Lib/CommonLib/Slice.cpp
@@ -2938,7 +2938,7 @@ PPS::PPS()
 , m_numTileCols                      (1)
 , m_numTileRows                      (1)
 , m_rectSliceFlag                    (1)  
-  , m_singleSlicePerSubPicFlag       (0)
+, m_singleSlicePerSubPicFlag         (0)
 , m_numSlicesInPic                   (1)
 , m_tileIdxDeltaPresentFlag          (0)
 , m_loopFilterAcrossTilesEnabledFlag (1)
@@ -3138,89 +3138,143 @@ void PPS::initRectSlices()
 /**
  - initialize mapping between rectangular slices and CTUs
  */
-void PPS::initRectSliceMap()
+void PPS::initRectSliceMap(const SPS  *sps)
 {
   uint32_t  ctuY;
   uint32_t  tileX, tileY;
-    
-  // allocate new memory for slice list
-  CHECK(m_numSlicesInPic > MAX_SLICES, "Number of slices in picture exceeds valid range");
-  m_sliceMap.resize( m_numSlicesInPic );
-#if JVET_Q0817   
-  if( getSingleSlicePerSubPicFlag() )
-  {
-#else
-  if ((getNumSubPics() > 0) && getSingleSlicePerSubPicFlag())
+
+  if (sps)
   {
-#endif
-    for (uint32_t i = 0; i <= getNumSubPics() - 1; i++)
+    m_ctuToSubPicIdx.resize(getPicWidthInCtu() * getPicHeightInCtu());
+    if (sps->getNumSubPics() > 1)
     {
-      m_sliceMap[i].initSliceMap();
+      for (int i = 0; i <= sps->getNumSubPics() - 1; i++)
+      {
+        for (int y = sps->getSubPicCtuTopLeftY(i); y < sps->getSubPicCtuTopLeftY(i) + sps->getSubPicHeight(i); y++)
+        {
+          for (int x = sps->getSubPicCtuTopLeftX(i); x < sps->getSubPicCtuTopLeftX(i) + sps->getSubPicWidth(i); x++)
+          {
+            m_ctuToSubPicIdx[ x+ y * getPicWidthInCtu()] = i;
+          }
+        }
+      }
     }
-    uint32_t picSizeInCtu = getPicWidthInCtu() * getPicHeightInCtu();
-    uint32_t sliceIdx;
-    for (uint32_t i = 0; i < picSizeInCtu; i++)
+    else
     {
-      sliceIdx = getCtuToSubPicIdx(i);
-      m_sliceMap[sliceIdx].pushToCtuAddrInSlice(i);
+      for (int i = 0; i < getPicWidthInCtu() * getPicHeightInCtu(); i++)
+      {
+        m_ctuToSubPicIdx[i] = 0;
+      }
     }
   }
-  else
-  {
-  // generate CTU maps for all rectangular slices in picture
-  for( uint32_t i = 0; i < m_numSlicesInPic; i++ )
+
+#if JVET_Q0817
+  if( getSingleSlicePerSubPicFlag() )
+#else
+  if ((getNumSubPics() > 0) && getSingleSlicePerSubPicFlag())
+#endif
   {
-    m_sliceMap[ i ].initSliceMap();
+    CHECK (sps==nullptr, "RectSliceMap can only be initialized for slice_per_sub_pic_flag with a valid SPS");
+    m_numSlicesInPic = sps->getNumSubPics();
 
-    // get position of first tile in slice
-    tileX =  m_rectSlices[ i ].getTileIdx() % m_numTileCols;
-    tileY =  m_rectSlices[ i ].getTileIdx() / m_numTileCols;
+    // allocate new memory for slice list
+    CHECK(m_numSlicesInPic > MAX_SLICES, "Number of slices in picture exceeds valid range");
+    m_sliceMap.resize( m_numSlicesInPic );
     
-    // infer slice size for last slice in picture
-    if( i == m_numSlicesInPic-1 ) 
+    for( int i = 0; i < m_numSlicesInPic; i++ )
     {
-      m_rectSlices[ i ].setSliceWidthInTiles ( m_numTileCols - tileX );
-      m_rectSlices[ i ].setSliceHeightInTiles( m_numTileRows - tileY );
-      m_rectSlices[ i ].setNumSlicesInTile( 1 );
+      m_sliceMap[ i ].initSliceMap();
     }
-
-    // set slice index
-    m_sliceMap[ i ].setSliceID(i);
-    
-    // complete tiles within a single slice case
-    if( m_rectSlices[ i ].getSliceWidthInTiles( ) > 1 || m_rectSlices[ i ].getSliceHeightInTiles( ) > 1)
+    for (int row = 0; row < m_numTileRows; row++)
     {
-      for( uint32_t j = 0; j < m_rectSlices[ i ].getSliceHeightInTiles( ); j++ )
+      for (int col = 0; col < m_numTileCols; col++)
       {
-        for( uint32_t k = 0; k < m_rectSlices[ i ].getSliceWidthInTiles( ); k++ )
+        const int tileTopLeftCtu = getTileColumnBd(col) + getTileRowBd(row) * getPicWidthInCtu();
+        const int sliceIdx = getCtuToSubPicIdx(tileTopLeftCtu);
+        const int tileSizeInCtus = (getTileColumnBd(col + 1) - getTileColumnBd(col)) * (getTileRowBd(row + 1) - getTileRowBd(row));
+        const int subPicSizeInCtus = sps->getSubPicHeight(sliceIdx) * sps->getSubPicWidth(sliceIdx);
+        if (subPicSizeInCtus >= tileSizeInCtus)
+        {
+          m_sliceMap[sliceIdx].addCtusToSlice(getTileColumnBd(col), getTileColumnBd(col + 1),
+            getTileRowBd(row), getTileRowBd(row + 1), m_picWidthInCtu);
+        }
+        else
         {
-          m_sliceMap[ i ].addCtusToSlice( getTileColumnBd(tileX + k), getTileColumnBd(tileX + k +1),
-                                          getTileRowBd(tileY + j), getTileRowBd(tileY + j +1), m_picWidthInCtu);
+          int sliceIdxInTile = sliceIdx;
+          do
+          {
+            // subpicture vertical boundary must be a tile boundary, so tile column parameter doesn't change
+            m_sliceMap[sliceIdxInTile].addCtusToSlice(getTileColumnBd(col), getTileColumnBd(col + 1),
+              sps->getSubPicCtuTopLeftY(sliceIdxInTile), sps->getSubPicCtuTopLeftY(sliceIdxInTile) +  sps->getSubPicHeight(sliceIdxInTile), m_picWidthInCtu);
+            sliceIdxInTile++;
+            if (sliceIdxInTile == sps->getNumSubPics())
+            {
+              break;
+            }
+          } while (sps->getSubPicCtuTopLeftY(sliceIdxInTile) < getTileRowBd(row + 1));
         }
       }
-    }
-    // multiple slices within a single tile case
-    else 
+    }    
+  }
+  else
+  {
+    // allocate new memory for slice list
+    CHECK(m_numSlicesInPic > MAX_SLICES, "Number of slices in picture exceeds valid range");
+    m_sliceMap.resize( m_numSlicesInPic );
+    // generate CTU maps for all rectangular slices in picture
+    for( uint32_t i = 0; i < m_numSlicesInPic; i++ )
     {
-      uint32_t  numSlicesInTile = m_rectSlices[ i ].getNumSlicesInTile( );
+      m_sliceMap[ i ].initSliceMap();
 
-      ctuY = getTileRowBd( tileY );
-      for( uint32_t j = 0; j < numSlicesInTile-1; j++ ) 
+      // get position of first tile in slice
+      tileX =  m_rectSlices[ i ].getTileIdx() % m_numTileCols;
+      tileY =  m_rectSlices[ i ].getTileIdx() / m_numTileCols;
+
+      // infer slice size for last slice in picture
+      if( i == m_numSlicesInPic-1 )
       {
-        m_sliceMap[ i ].addCtusToSlice( getTileColumnBd(tileX), getTileColumnBd(tileX+1),
-                                        ctuY, ctuY + m_rectSlices[ i ].getSliceHeightInCtu(), m_picWidthInCtu);
-        ctuY += m_rectSlices[ i ].getSliceHeightInCtu();
-        i++;
-        m_sliceMap[ i ].setSliceID(i);
+        m_rectSlices[ i ].setSliceWidthInTiles ( m_numTileCols - tileX );
+        m_rectSlices[ i ].setSliceHeightInTiles( m_numTileRows - tileY );
+        m_rectSlices[ i ].setNumSlicesInTile( 1 );
       }
 
-      // infer slice height for last slice in tile
-      CHECK( ctuY >= getTileRowBd( tileY + 1 ), "Invalid rectangular slice signalling");
-      m_rectSlices[ i ].setSliceHeightInCtu( getTileRowBd( tileY + 1 ) - ctuY );
-      m_sliceMap[ i ].addCtusToSlice( getTileColumnBd(tileX), getTileColumnBd(tileX+1),
-                                      ctuY, getTileRowBd( tileY + 1 ), m_picWidthInCtu);
-    } 
-  }
+      // set slice index
+      m_sliceMap[ i ].setSliceID(i);
+
+      // complete tiles within a single slice case
+      if( m_rectSlices[ i ].getSliceWidthInTiles( ) > 1 || m_rectSlices[ i ].getSliceHeightInTiles( ) > 1)
+      {
+        for( uint32_t j = 0; j < m_rectSlices[ i ].getSliceHeightInTiles( ); j++ )
+        {
+          for( uint32_t k = 0; k < m_rectSlices[ i ].getSliceWidthInTiles( ); k++ )
+          {
+            m_sliceMap[ i ].addCtusToSlice( getTileColumnBd(tileX + k), getTileColumnBd(tileX + k +1),
+                                            getTileRowBd(tileY + j), getTileRowBd(tileY + j +1), m_picWidthInCtu);
+          }
+        }
+      }
+      // multiple slices within a single tile case
+      else
+      {
+        uint32_t  numSlicesInTile = m_rectSlices[ i ].getNumSlicesInTile( );
+
+        ctuY = getTileRowBd( tileY );
+        for( uint32_t j = 0; j < numSlicesInTile-1; j++ )
+        {
+          m_sliceMap[ i ].addCtusToSlice( getTileColumnBd(tileX), getTileColumnBd(tileX+1),
+                                          ctuY, ctuY + m_rectSlices[ i ].getSliceHeightInCtu(), m_picWidthInCtu);
+          ctuY += m_rectSlices[ i ].getSliceHeightInCtu();
+          i++;
+          m_sliceMap[ i ].setSliceID(i);
+        }
+
+        // infer slice height for last slice in tile
+        CHECK( ctuY >= getTileRowBd( tileY + 1 ), "Invalid rectangular slice signalling");
+        m_rectSlices[ i ].setSliceHeightInCtu( getTileRowBd( tileY + 1 ) - ctuY );
+        m_sliceMap[ i ].addCtusToSlice( getTileColumnBd(tileX), getTileColumnBd(tileX+1),
+                                        ctuY, getTileRowBd( tileY + 1 ), m_picWidthInCtu);
+      }
+    }
   }
   // check for valid rectangular slice map
   checkSliceMap();
diff --git a/source/Lib/CommonLib/Slice.h b/source/Lib/CommonLib/Slice.h
index d5e81aada..334d2c445 100644
--- a/source/Lib/CommonLib/Slice.h
+++ b/source/Lib/CommonLib/Slice.h
@@ -2295,7 +2295,7 @@ public:
   void                   resetTileSliceInfo();
   void                   initTiles();
   void                   initRectSlices();
-  void                   initRectSliceMap();
+  void                   initRectSliceMap(const SPS  *sps);
 #if JVET_O1143_SUBPIC_BOUNDARY
   std::vector<SubPic>    getSubPics()  const                                              {return m_subPics;          };
 #if JVET_Q0044_SLICE_IDX_WITH_SUBPICS
diff --git a/source/Lib/DecoderLib/VLCReader.cpp b/source/Lib/DecoderLib/VLCReader.cpp
index 4db59237e..5e2b5f95e 100644
--- a/source/Lib/DecoderLib/VLCReader.cpp
+++ b/source/Lib/DecoderLib/VLCReader.cpp
@@ -713,9 +713,6 @@ void HLSyntaxReader::parsePPS( PPS* pcPPS )
         }
       }
       pcPPS->setSliceTileIdx(pcPPS->getNumSlicesInPic()-1, tileIdx );
-      
-      // initialize mapping between rectangular slices and CTUs
-      pcPPS->initRectSliceMap();
     }
 
     // loop filtering across slice/tile controls
@@ -3140,6 +3137,7 @@ void HLSyntaxReader::parsePictureHeader( PicHeader* picHeader, ParameterSetManag
 #endif
   
   // initialize tile/slice info for no partitioning case
+
   if( pps->getNoPicPartitionFlag() )
   {
     pps->resetTileSliceInfo();
@@ -3154,7 +3152,7 @@ void HLSyntaxReader::parsePictureHeader( PicHeader* picHeader, ParameterSetManag
     pps->initRectSlices( );
     pps->setTileIdxDeltaPresentFlag( 0 );
     pps->setSliceTileIdx( 0, 0 );
-    pps->initRectSliceMap( );
+    pps->initRectSliceMap(sps);
 #if JVET_O1143_SUBPIC_BOUNDARY
     // when no Pic partition, number of sub picture shall be less than 2
     CHECK(pps->getNumSubPics()>=2, "error, no picture partitions, but have equal to or more than 2 sub pictures");
@@ -3163,6 +3161,7 @@ void HLSyntaxReader::parsePictureHeader( PicHeader* picHeader, ParameterSetManag
   else 
   {
     CHECK(pps->getCtuSize() != sps->getCTUSize(), "PPS CTU size does not match CTU size in SPS");
+    pps->initRectSliceMap(sps);
   }
 
 #if JVET_Q0044_SLICE_IDX_WITH_SUBPICS
diff --git a/source/Lib/EncoderLib/EncLib.cpp b/source/Lib/EncoderLib/EncLib.cpp
index 1c08f782f..28e5ca4a6 100644
--- a/source/Lib/EncoderLib/EncLib.cpp
+++ b/source/Lib/EncoderLib/EncLib.cpp
@@ -1871,10 +1871,11 @@ void EncLib::xInitPPS(PPS &pps, const SPS &sps)
     pps.setRectSliceFlag( m_rectSliceFlag );
     if( m_rectSliceFlag ) 
     {
+      pps.setSingleSlicePerSubPicFlag(m_singleSlicePerSubPicFlag);
       pps.setNumSlicesInPic( m_numSlicesInPic );
       pps.setTileIdxDeltaPresentFlag( m_tileIdxDeltaPresentFlag );
       pps.setRectSlices( m_rectSlices );
-      pps.initRectSliceMap( );
+      pps.initRectSliceMap(&sps);
 #if JVET_O1143_SUBPIC_BOUNDARY
       pps.initSubPic(sps);
 #endif
@@ -1899,7 +1900,7 @@ void EncLib::xInitPPS(PPS &pps, const SPS &sps)
     pps.initRectSlices( );
     pps.setTileIdxDeltaPresentFlag( 0 );
     pps.setSliceTileIdx( 0, 0 );
-    pps.initRectSliceMap( );
+    pps.initRectSliceMap( &sps );
 #if JVET_O1143_SUBPIC_BOUNDARY
     pps.initSubPic(sps);
 #endif
-- 
GitLab