Ignore:
Timestamp:
24 Feb 2012, 20:22:58 (13 years ago)
Author:
poznan-univ
Message:

Poznan Tools

  • Encoding only disoccluded CUs in depended views
  • Depth based motion prediction
  • Texture QP adjustment based on depth data
  • Nonlinear depth representation
File:
1 edited

Legend:

Unmodified
Added
Removed
  • branches/0.3-poznan-univ/source/Lib/TLibEncoder/TEncCu.cpp

    r5 r28  
    6767  m_ppcOrigYuv     = new TComYuv*[m_uhTotalDepth-1];
    6868  m_ppcResPredTmp  = new TComYuv*[m_uhTotalDepth-1];
     69#if POZNAN_AVAIL_MAP
     70  m_ppcAvailYuv    = new TComYuv*[m_uhTotalDepth-1];
     71#endif
     72#if POZNAN_SYNTH_VIEW
     73  m_ppcSynthYuv    = new TComYuv*[m_uhTotalDepth-1];
     74#endif
     75 
    6976
    7077#if HHI_MPI
     
    93100    m_ppcOrigYuv    [i] = new TComYuv; m_ppcOrigYuv    [i]->create(uiWidth, uiHeight);
    94101
     102#if POZNAN_AVAIL_MAP
     103    m_ppcAvailYuv   [i] = new TComYuv; m_ppcAvailYuv    [i]->create(uiWidth, uiHeight);
     104#endif
     105#if POZNAN_SYNTH_VIEW
     106    m_ppcSynthYuv   [i] = new TComYuv; m_ppcSynthYuv    [i]->create(uiWidth, uiHeight);
     107#endif
     108
     109
    95110    m_ppcResPredTmp [i] = new TComYuv; m_ppcResPredTmp [i]->create(uiWidth, uiHeight);
    96111  }
     
    152167      m_ppcOrigYuv[i]->destroy();     delete m_ppcOrigYuv[i];     m_ppcOrigYuv[i] = NULL;
    153168    }
     169#if POZNAN_AVAIL_MAP
     170    if(m_ppcAvailYuv[i])
     171    {
     172      m_ppcAvailYuv[i]->destroy();    delete m_ppcAvailYuv[i];    m_ppcAvailYuv[i]    = NULL;
     173    }
     174#endif
     175#if POZNAN_SYNTH_VIEW
     176    if(m_ppcSynthYuv[i])
     177    {
     178      m_ppcSynthYuv[i]->destroy();    delete m_ppcSynthYuv[i];    m_ppcSynthYuv[i]    = NULL;
     179    }
     180#endif
    154181    if(m_ppcResPredTmp[i])
    155182    {
     
    203230    m_ppcOrigYuv = NULL;
    204231  }
     232#if POZNAN_AVAIL_MAP
     233  if(m_ppcAvailYuv)
     234  {
     235    delete [] m_ppcAvailYuv;
     236    m_ppcAvailYuv = NULL;
     237  }
     238#endif
     239#if POZNAN_SYNTH_VIEW
     240  if(m_ppcSynthYuv)
     241  {
     242    delete [] m_ppcSynthYuv;
     243    m_ppcSynthYuv = NULL;
     244  }
     245#endif
    205246  if(m_ppcResPredTmp)
    206247  {
     
    354395    }
    355396  }
     397
     398#if POZNAN_DBMP & !POZNAN_DBMP_COMPRESS_ME_DATA
     399  //save motion data for every CU point
     400  xSaveDBMPData(m_ppcBestCU[0]);
     401#endif
     402
    356403}
    357404
     
    426473  // get Original YUV data from picture
    427474  m_ppcOrigYuv[uiDepth]->copyFromPicYuv( pcPic->getPicYuvOrg(), rpcBestCU->getAddr(), rpcBestCU->getZorderIdxInCU() );
    428 
     475#if POZNAN_AVAIL_MAP
     476  if (pcPic->getPicYuvAvail())  m_ppcAvailYuv[uiDepth]->copyFromPicYuv( pcPic->getPicYuvAvail(), rpcBestCU->getAddr(), rpcBestCU->getZorderIdxInCU() );
     477#endif 
     478#if POZNAN_SYNTH_VIEW
     479  if (pcPic->getPicYuvSynth())  m_ppcSynthYuv[uiDepth]->copyFromPicYuv( pcPic->getPicYuvSynth(), rpcBestCU->getAddr(), rpcBestCU->getZorderIdxInCU() );
     480#endif
     481
     482#if POZNAN_ENCODE_ONLY_DISOCCLUDED_CU
     483  Bool bWholeCUCanBeSynthesized = false;
     484  Bool bOneSubCUCanNotBeSynthesied = false;
     485  Bool bSubCUCanBeSynthesized[4];
     486  Bool * pbSubCUCanBeSynthesized = bSubCUCanBeSynthesized;
     487  pcPic->checkSynthesisAvailability(rpcBestCU, rpcBestCU->getAddr(), rpcBestCU->getZorderIdxInCU(), uiDepth, pbSubCUCanBeSynthesized); //KUBA SYNTH
     488  Int  iSubCUCanNotBeSynthesized = 0;
     489  Int  iSubCUCanBeSynthesizedCnt = 0;
     490  for(Int i = 0; i < 4; i++)
     491  {
     492    if (!bSubCUCanBeSynthesized[i])
     493    {
     494      iSubCUCanNotBeSynthesized = i;
     495    }
     496    else
     497    {
     498      iSubCUCanBeSynthesizedCnt ++;
     499    }
     500  }
     501  if(iSubCUCanBeSynthesizedCnt == 4)
     502  {
     503    bWholeCUCanBeSynthesized = true;
     504  }
     505  else if(iSubCUCanBeSynthesizedCnt == 3)
     506  {
     507    bOneSubCUCanNotBeSynthesied = true;
     508  }
     509#endif
    429510  // variables for fast encoder decision
    430511  TComDataCU* pcTempCU;
     
    493574  if( ( uiRPelX < rpcBestCU->getSlice()->getSPS()->getWidth() ) && ( uiBPelY < rpcBestCU->getSlice()->getSPS()->getHeight() ) )
    494575  {
     576    // do CU Skip
     577#if POZNAN_ENCODE_ONLY_DISOCCLUDED_CU
     578    if(bWholeCUCanBeSynthesized)
     579    {
     580      rpcBestCU->getTotalCost() = 0;       // Cost of synthesised CU is zero
     581      rpcBestCU->getTotalBits() = 0;       // Cost of synthesised CU is zero
     582      rpcBestCU->getTotalDistortion() = 0; // Distortion of synthesised CU is zero
     583      rpcBestCU->setPredModeSubParts( MODE_SYNTH,      0, uiDepth );
     584      rpcBestCU->setPartSizeSubParts( SIZE_2Nx2N,      0, uiDepth );
     585#if POZNAN_FILL_OCCLUDED_CU_WITH_SYNTHESIS
     586      m_ppcRecoYuvBest[uiDepth]->copyFromPicYuv(pcPic->getPicYuvSynth(), rpcBestCU->getAddr(), rpcBestCU->getZorderIdxInCU()); // First copy synthesis YUV part to CU encoder reconstruction YUV structure
     587#else
     588      m_ppcRecoYuvBest[uiDepth]->copyFromPicYuv(pcPic->getPicYuvAvail(), rpcBestCU->getAddr(), rpcBestCU->getZorderIdxInCU()); // First copy synthesis YUV part to CU encoder reconstruction YUV structure
     589#endif
     590      UInt uiInitTrDepth  = rpcBestCU->getPartitionSize(0) == SIZE_2Nx2N ? 0 : 1;
     591      rpcBestCU->copyToPic(uiDepth, 0, uiInitTrDepth);
     592      xCopyYuv2Pic( rpcBestCU->getPic(), rpcBestCU->getAddr(), rpcBestCU->getZorderIdxInCU(), uiDepth );   // Next copy it to reconstruction YUV buffer in coded picture
     593      assert( rpcBestCU->getPartitionSize ( 0 ) != SIZE_NONE  ); //Not needed any more ??
     594      assert( rpcBestCU->getPredictionMode( 0 ) != MODE_NONE  );
     595      assert( rpcBestCU->getTotalCost     (   ) != MAX_DOUBLE );
     596
     597      #if HHI_VSO//??
     598      if( m_pcRdCost->getUseRenModel() )
     599      {
     600        UInt  uiWidth     = rpcBestCU->getWidth ( 0 );
     601        UInt  uiHeight    = rpcBestCU->getHeight( 0 );
     602        Pel*  piSrc       = m_ppcRecoYuvBest[uiDepth]->getLumaAddr( 0 );
     603        UInt  uiSrcStride = m_ppcRecoYuvBest[uiDepth]->getStride();
     604        m_pcRdCost->setRenModelData( rpcBestCU, 0, piSrc, uiSrcStride, uiWidth, uiHeight );
     605      }
     606      #endif
     607      return;
     608    }
     609    else if (bOneSubCUCanNotBeSynthesied && (uiDepth < g_uiMaxCUDepth - g_uiAddCUDepth )) // If only one from subCU can not be synthesised than force NxN split but donot signal it in the stream
     610    {
     611      //printf("CUSkip - OneCanNotBe %d %d %d\n",rpcBestCU->getCUPelX(),rpcBestCU->getCUPelY(),rpcBestCU->getWidth());
     612      // further split !!!!go to line 866!!!
     613#if HHI_VSO
     614    // reset Model
     615    if( m_pcRdCost->getUseRenModel() )
     616    {
     617      UInt  uiWidth     = m_ppcBestCU[uiDepth]->getWidth ( 0 );
     618      UInt  uiHeight    = m_ppcBestCU[uiDepth]->getHeight( 0 );
     619      Pel*  piSrc       = m_ppcOrigYuv[uiDepth]->getLumaAddr( 0 );
     620      UInt  uiSrcStride = m_ppcOrigYuv[uiDepth]->getStride();
     621      m_pcRdCost->setRenModelData( m_ppcBestCU[uiDepth], 0, piSrc, uiSrcStride, uiWidth, uiHeight );
     622    }
     623#endif
     624
     625      UChar       uhNextDepth         = uiDepth + 1;
     626      TComDataCU* pcSubBestPartCU     = m_ppcBestCU[uhNextDepth];
     627      TComDataCU* pcSubTempPartCU     = m_ppcTempCU[uhNextDepth];
     628     
     629      for ( UInt uiPartUnitIdx = 0; uiPartUnitIdx < 4; uiPartUnitIdx++ ) //UInt uiPartUnitIdx = iSubCUNotSynthesied;
     630      {
     631        pcSubBestPartCU->initSubCU( rpcBestCU, uiPartUnitIdx, uhNextDepth );           // clear sub partition datas or init.
     632        pcSubTempPartCU->initSubCU( rpcBestCU, uiPartUnitIdx, uhNextDepth );           // clear sub partition datas or init.
     633//        pcSubBestPartCU->setLastCodedQP( rpcBestCU->getLastCodedQP() );
     634//        pcSubTempPartCU->setLastCodedQP( rpcBestCU->getLastCodedQP() );
     635       
     636        if( ( pcSubBestPartCU->getCUPelX() < pcSubBestPartCU->getSlice()->getSPS()->getWidth() ) && ( pcSubBestPartCU->getCUPelY() < pcSubBestPartCU->getSlice()->getSPS()->getHeight() ) )
     637        {
     638          if( m_bUseSBACRD )
     639          {
     640            if ( 0 == uiPartUnitIdx) //initialize RD with previous depth buffer
     641            {
     642              m_pppcRDSbacCoder[uhNextDepth][CI_CURR_BEST]->load(m_pppcRDSbacCoder[uiDepth][CI_CURR_BEST]);
     643            }
     644            else
     645            {
     646              m_pppcRDSbacCoder[uhNextDepth][CI_CURR_BEST]->load(m_pppcRDSbacCoder[uhNextDepth][CI_NEXT_BEST]);
     647            }
     648          }
     649         
     650          xCompressCU( pcSubBestPartCU, pcSubTempPartCU, uhNextDepth ); //Compress SubCU's
     651         
     652#if HHI_VSO
     653          if( m_pcRdCost->getUseRenModel() )
     654          {
     655            UInt  uiWidth     = pcSubBestPartCU->getWidth ( 0 );
     656            UInt  uiHeight    = pcSubBestPartCU->getHeight( 0 );
     657            Pel*  piSrc       = m_ppcRecoYuvBest[pcSubBestPartCU->getDepth(0)]->getLumaAddr( 0 );
     658            UInt  uiSrcStride = m_ppcRecoYuvBest[pcSubBestPartCU->getDepth(0)]->getStride();
     659            m_pcRdCost->setRenModelData( pcSubBestPartCU, 0, piSrc, uiSrcStride, uiWidth, uiHeight );
     660          }
     661#endif
     662          rpcTempCU->copyPartFrom( pcSubBestPartCU, uiPartUnitIdx, uhNextDepth );         // Keep best part data to current temporary data.
     663          xCopyYuv2Tmp( pcSubBestPartCU->getTotalNumPart()*uiPartUnitIdx, uhNextDepth );
     664        }
     665      }
     666
     667      if( !bBoundary )
     668      {
     669        m_pcEntropyCoder->resetBits();
     670        m_pcEntropyCoder->encodeSplitFlag( rpcTempCU, 0, uiDepth, true );
     671
     672        rpcTempCU->getTotalBits() += m_pcEntropyCoder->getNumberOfWrittenBits(); // split bits
     673      }
     674
     675#if HHI_INTERVIEW_SKIP_LAMBDA_SCALE
     676      if( bFullyRenderedSec )
     677      {
     678        m_pcRdCost->setLambdaScale( m_pcEncCfg->getInterViewSkipLambdaScale() );
     679      }
     680      else
     681      {
     682        m_pcRdCost->setLambdaScale( 1 );
     683      }
     684#endif
     685#if HHI_VSO
     686      if ( m_pcRdCost->getUseLambdaScaleVSO())
     687      {
     688        rpcTempCU->getTotalCost()  = m_pcRdCost->calcRdCostVSO( rpcTempCU->getTotalBits(), rpcTempCU->getTotalDistortion() );
     689      }
     690      else
     691#endif
     692      {
     693        rpcTempCU->getTotalCost()  = m_pcRdCost->calcRdCost( rpcTempCU->getTotalBits(), rpcTempCU->getTotalDistortion() );
     694      }   
     695
     696      if( m_bUseSBACRD )
     697      {
     698        m_pppcRDSbacCoder[uhNextDepth][CI_NEXT_BEST]->store(m_pppcRDSbacCoder[uiDepth][CI_TEMP_BEST]);
     699      }
     700      //Copy Tmp to Best
     701      xCheckBestMode( rpcBestCU, rpcTempCU, uiDepth );  //Roznica z naszym koder                                          // RD compare current larger prediction
     702
     703#if HHI_VSO
     704      if( m_pcRdCost->getUseRenModel() )
     705      {
     706        UInt  uiWidth     = rpcBestCU->getWidth ( 0 );
     707        UInt  uiHeight    = rpcBestCU->getHeight( 0 );
     708        Pel*  piSrc       = m_ppcRecoYuvBest[uiDepth]->getLumaAddr( 0 );
     709        UInt  uiSrcStride = m_ppcRecoYuvBest[uiDepth]->getStride();
     710        m_pcRdCost->setRenModelData( rpcBestCU, 0, piSrc, uiSrcStride, uiWidth, uiHeight );
     711      }
     712#endif
     713      //Copy result to pic
     714      rpcBestCU->copyToPic(uiDepth);                                                     // Copy Best data to Picture for next partition prediction.
     715     
     716      if( bBoundary )
     717        return;
     718
     719      xCopyYuv2Pic( rpcBestCU->getPic(), rpcBestCU->getAddr(), rpcBestCU->getZorderIdxInCU(), uiDepth );   // Copy Yuv data to picture Yuv
     720     
     721      // Assert if Best prediction mode is NONE
     722      // Selected mode's RD-cost must be not MAX_DOUBLE.
     723      assert( rpcBestCU->getPartitionSize ( 0 ) != SIZE_NONE  ); //Not needed any more?
     724      assert( rpcBestCU->getPredictionMode( 0 ) != MODE_NONE  );
     725      assert( rpcBestCU->getTotalCost     (   ) != MAX_DOUBLE );
     726      return;
     727    }
     728#endif
     729
    495730    // do inter modes
    496731    if( rpcBestCU->getSlice()->getSliceType() != I_SLICE )
     
    8361071  TComPic* pcPic = pcCU->getPic();
    8371072
     1073#if POZNAN_ENCODE_ONLY_DISOCCLUDED_CU
     1074  if( pcCU->isCUSkiped( uiAbsPartIdx ) && uiDepth == pcCU->getDepth( uiAbsPartIdx )) //If CU Skiped no information is coded into stream
     1075    return;
     1076#endif
     1077
    8381078  Bool bBoundary = false;
    8391079  UInt uiLPelX   = pcCU->getCUPelX() + g_auiRasterToPelX[ g_auiZscanToRaster[uiAbsPartIdx] ];
     
    8421082  UInt uiBPelY   = uiTPelY + (g_uiMaxCUHeight>>uiDepth) - 1;
    8431083
     1084#if POZNAN_ENCODE_ONLY_DISOCCLUDED_CU
     1085  Bool bDontSendSplitFlag = false;
     1086  if( ( ( uiDepth < pcCU->getDepth( uiAbsPartIdx ) ) && ( uiDepth < (g_uiMaxCUDepth-g_uiAddCUDepth) ) ) || bBoundary ) //check if CU has 3 synthesied subCU - no split flag is send in that case
     1087  {
     1088    UInt uiQNumParts = ( pcPic->getNumPartInCU() >> (uiDepth<<1) )>>2;
     1089    Int iCUSkipCounter = 0;
     1090    UInt uiAbsPartIdxTmp = uiAbsPartIdx;
     1091    for ( UInt uiPartUnitIdx = 0; uiPartUnitIdx < 4; uiPartUnitIdx++, uiAbsPartIdxTmp+=uiQNumParts )
     1092    {
     1093      if(pcCU->isCUSkiped(uiAbsPartIdxTmp) && (pcCU->getDepth( uiAbsPartIdxTmp ) == uiDepth + 1) )
     1094      {
     1095        iCUSkipCounter++;
     1096      }
     1097    }
     1098    if(iCUSkipCounter == 3)
     1099    {
     1100      bDontSendSplitFlag = true;
     1101    }
     1102  }
     1103#endif
     1104
    8441105  if( ( uiRPelX < pcCU->getSlice()->getSPS()->getWidth() ) && ( uiBPelY < pcCU->getSlice()->getSPS()->getHeight() ) )
    8451106  {
     1107#if POZNAN_ENCODE_ONLY_DISOCCLUDED_CU
     1108    if(!bDontSendSplitFlag)
     1109#endif
    8461110#if HHI_MPI
    8471111    if( pcCU->getTextureModeDepth( uiAbsPartIdx ) == -1 || uiDepth < pcCU->getTextureModeDepth( uiAbsPartIdx ) )
     
    10191283  UChar uhInterDirNeighbours[MRG_MAX_NUM_CANDS];
    10201284  UInt uiNeighbourCandIdx[MRG_MAX_NUM_CANDS]; //MVs with same idx => same cand
     1285#if POZNAN_DBMP_CALC_PRED_DATA
     1286  TComMP* pcMP = rpcTempCU->getSlice()->getMP();
     1287#endif
    10211288
    10221289#if HHI_INTER_VIEW_RESIDUAL_PRED
     
    10751342        rpcTempCU->setNeighbourCandIdxSubParts( uiInner, uiNeighbourCandIdx[uiInner], 0, 0,uhDepth );
    10761343      }
     1344#if POZNAN_DBMP_CALC_PRED_DATA
     1345          if(uiMergeCand==POZNAN_DBMP_MRG_CAND)
     1346          {
     1347                rpcTempCU->getCUMvField2nd( REF_PIC_LIST_0 )->setAllMvField( cMvFieldNeighbours[0 + 2*uiMergeCand].getMv(), cMvFieldNeighbours[0 + 2*uiMergeCand].getRefIdx(), SIZE_2Nx2N, 0, 0, 0 ); // interprets depth relative to rpcTempCU level
     1348                rpcTempCU->getCUMvField2nd( REF_PIC_LIST_1 )->setAllMvField( cMvFieldNeighbours[1 + 2*uiMergeCand].getMv(), cMvFieldNeighbours[1 + 2*uiMergeCand].getRefIdx(), SIZE_2Nx2N, 0, 0, 0 ); // interprets depth relative to rpcTempCU level
     1349          }
     1350          else
     1351#endif
     1352          {
    10771353      rpcTempCU->getCUMvField( REF_PIC_LIST_0 )->setAllMvField( cMvFieldNeighbours[0 + 2*uiMergeCand].getMv(), cMvFieldNeighbours[0 + 2*uiMergeCand].getRefIdx(), SIZE_2Nx2N, 0, 0, 0 ); // interprets depth relative to rpcTempCU level
    10781354      rpcTempCU->getCUMvField( REF_PIC_LIST_1 )->setAllMvField( cMvFieldNeighbours[1 + 2*uiMergeCand].getMv(), cMvFieldNeighbours[1 + 2*uiMergeCand].getRefIdx(), SIZE_2Nx2N, 0, 0, 0 ); // interprets depth relative to rpcTempCU level
     1355          }
    10791356
    10801357#if HHI_INTER_VIEW_RESIDUAL_PRED
     
    10901367      if ( uiNoResidual == 0 ){
    10911368#endif
     1369
     1370#if POZNAN_DBMP
     1371                if(uiMergeCand==POZNAN_DBMP_MRG_CAND){
     1372                        m_pcPredSearch->motionCompensation_DBMP ( rpcTempCU, m_ppcPredYuvTemp[uhDepth] );
     1373#if POZNAN_DBMP_CALC_PRED_DATA         
     1374                  pcMP->setDBMPPredMVField(REF_PIC_LIST_0, rpcTempCU);
     1375                  pcMP->setDBMPPredMVField(REF_PIC_LIST_1, rpcTempCU);
     1376#endif
     1377                }
     1378                else
    10921379        m_pcPredSearch->motionCompensation ( rpcTempCU, m_ppcPredYuvTemp[uhDepth] );
     1380#else
     1381        m_pcPredSearch->motionCompensation ( rpcTempCU, m_ppcPredYuvTemp[uhDepth] );
     1382#endif
    10931383        // save pred adress
    10941384        pcPredYuvTemp = m_ppcPredYuvTemp[uhDepth];
     
    11001390          pcPredYuvTemp = m_ppcPredYuvBest[uhDepth];
    11011391        }
     1392#if POZNAN_DBMP_CALC_PRED_DATA
     1393            if(uiMergeCand==POZNAN_DBMP_MRG_CAND)
     1394            {
     1395              //copy motion data representing CU with DBMP for uiNoResidual==0 case             
     1396              rpcTempCU->getCUMvField( REF_PIC_LIST_0 )->copyFrom(pcMP->getDBMPPredMVField(REF_PIC_LIST_0),rpcTempCU->getTotalNumPart(),0);
     1397              rpcTempCU->getCUMvField( REF_PIC_LIST_1 )->copyFrom(pcMP->getDBMPPredMVField(REF_PIC_LIST_1),rpcTempCU->getTotalNumPart(),0);
     1398            }
     1399#endif
    11021400      }
    11031401#if HHI_VSO
     
    11231421      Bool bQtRootCbf = rpcTempCU->getQtRootCbf(0) == 1;
    11241422#else
     1423#if POZNAN_DBMP
    11251424      // do MC
     1425          if(uiMergeCand==POZNAN_DBMP_MRG_CAND){
     1426                 m_pcPredSearch->motionCompensation_DBMP ( rpcTempCU, m_ppcPredYuvTemp[uhDepth] );
     1427#if POZNAN_DBMP_CALC_PRED_DATA         
     1428                 pcMP->setDBMPPredMVField(REF_PIC_LIST_0, rpcTempCU);
     1429                 pcMP->setDBMPPredMVField(REF_PIC_LIST_1, rpcTempCU);
     1430#endif         
     1431          }
     1432          else
    11261433      m_pcPredSearch->motionCompensation ( rpcTempCU, m_ppcPredYuvTemp[uhDepth] );
     1434
     1435#endif
    11271436
    11281437      // estimate residual and encode everything
     
    14121721    for( UInt ui = 0; ui < rpcTempCU->getTotalNumPart(); ui++ )
    14131722    {
    1414       if( pcTextureCU->isIntra( rpcTempCU->getZorderIdxInCU() + ui ) )
     1723      if( pcTextureCU->isIntra( rpcTempCU->getZorderIdxInCU() + ui )
     1724#if POZNAN_ENCODE_ONLY_DISOCCLUDED_CU
     1725        || pcTextureCU->isCUSkiped( rpcTempCU->getZorderIdxInCU() + ui )
     1726#endif
     1727      )
    14151728      {
    14161729        return;
Note: See TracChangeset for help on using the changeset viewer.