LCOV - code coverage report
Current view: top level - gcore - gdalcachedpixelaccessor.h (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 104 116 89.7 %
Date: 2024-11-21 22:18:42 Functions: 119 119 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL
       4             :  * Purpose:  Fast access to individual pixels in a GDALRasterBand
       5             :  * Author:   Even Rouault <even dot rouault at spatialys.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2022, Planet Labs
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #ifndef GDAL_CACHED_PIXEL_ACCESSOR_INCLUDED
      14             : #define GDAL_CACHED_PIXEL_ACCESSOR_INCLUDED
      15             : 
      16             : #include "gdal_priv.h"
      17             : #include "cpl_error.h"
      18             : 
      19             : #include <algorithm>
      20             : #include <array>
      21             : #include <vector>
      22             : 
      23             : /************************************************************************/
      24             : /*                      GDALCachedPixelAccessor                         */
      25             : /************************************************************************/
      26             : 
      27             : /** Class to have reasonably fast random pixel access to a raster band, when
      28             :  * accessing multiple pixels that are close to each other.
      29             :  *
      30             :  * This gives faster access than using GDALRasterBand::RasterIO() with
      31             :  * a 1x1 window.
      32             :  *
      33             :  * @since GDAL 3.5
      34             :  */
      35             : template <class Type, int TILE_SIZE, int CACHED_TILE_COUNT = 4>
      36             : class GDALCachedPixelAccessor
      37             : {
      38             :     GDALRasterBand *m_poBand = nullptr;
      39             : 
      40             :     struct CachedTile
      41             :     {
      42             :         std::vector<Type> m_data{};
      43             :         int m_nTileX = -1;
      44             :         int m_nTileY = -1;
      45             :         bool m_bModified = false;
      46             :     };
      47             : 
      48             :     int m_nCachedTileCount = 0;
      49             :     std::array<CachedTile, CACHED_TILE_COUNT> m_aCachedTiles{};
      50             : 
      51             :     bool LoadTile(int nTileX, int nTileY);
      52             :     bool FlushTile(int iSlot);
      53             : 
      54             :     Type GetSlowPath(int nTileX, int nTileY, int nXInTile, int nYInTile,
      55             :                      bool *pbSuccess);
      56             :     bool SetSlowPath(int nTileX, int nTileY, int nXInTile, int nYInTile,
      57             :                      Type val);
      58             : 
      59             :     GDALCachedPixelAccessor(const GDALCachedPixelAccessor &) = delete;
      60             :     GDALCachedPixelAccessor &
      61             :     operator=(const GDALCachedPixelAccessor &) = delete;
      62             : 
      63             :   public:
      64             :     explicit GDALCachedPixelAccessor(GDALRasterBand *poBand);
      65             :     ~GDALCachedPixelAccessor();
      66             : 
      67             :     /** Assign the raster band if not known at construction time. */
      68          10 :     void SetBand(GDALRasterBand *poBand)
      69             :     {
      70          10 :         m_poBand = poBand;
      71          10 :     }
      72             : 
      73             :     Type Get(int nX, int nY, bool *pbSuccess = nullptr);
      74             :     bool Set(int nX, int nY, Type val);
      75             : 
      76             :     bool FlushCache();
      77             :     void ResetModifiedFlag();
      78             : };
      79             : 
      80             : /************************************************************************/
      81             : /*                      GDALCachedPixelAccessor()                       */
      82             : /************************************************************************/
      83             : 
      84             : /** Constructor.
      85             :  *
      86             :  * The template accepts the following parameters:
      87             :  * - Type: should be one of GByte, GUInt16, GInt16, GUInt32, GInt32, GUInt64,
      88             :  * GInt64, float or double
      89             :  * - TILE_SIZE: the tile size for the cache of GDALCachedPixelAccessor.
      90             :  *              Use a power of two for faster computation.
      91             :  *              It doesn't need to be the same of the underlying raster
      92             :  * - CACHED_TILE_COUNT: number of tiles to cache. Should be >= 1. Defaults to 4.
      93             :  *
      94             :  * @param poBand Raster band.
      95             :  */
      96             : template <class Type, int TILE_SIZE, int CACHED_TILE_COUNT>
      97          21 : GDALCachedPixelAccessor<Type, TILE_SIZE, CACHED_TILE_COUNT>::
      98             :     GDALCachedPixelAccessor(GDALRasterBand *poBand)
      99          21 :     : m_poBand(poBand)
     100             : {
     101          21 : }
     102             : 
     103             : /************************************************************************/
     104             : /*                     ~GDALCachedPixelAccessor()                       */
     105             : /************************************************************************/
     106             : 
     107             : /** Destructor.
     108             :  *
     109             :  * Will call FlushCache()
     110             :  */
     111             : template <class Type, int TILE_SIZE, int CACHED_TILE_COUNT>
     112          21 : GDALCachedPixelAccessor<Type, TILE_SIZE,
     113             :                         CACHED_TILE_COUNT>::~GDALCachedPixelAccessor()
     114             : {
     115          21 :     FlushCache();
     116          21 : }
     117             : 
     118             : /************************************************************************/
     119             : /*                            Get()                                     */
     120             : /************************************************************************/
     121             : 
     122             : /** Get the value of a pixel.
     123             :  *
     124             :  * No bound checking of nX, nY is done.
     125             :  *
     126             :  * @param nX X coordinate (between 0 and GetXSize()-1)
     127             :  * @param nY Y coordinate (between 0 and GetYSize()-1)
     128             :  * @param[out] pbSuccess Optional pointer to a success flag
     129             :  * @return the pixel value
     130             :  */
     131             : template <class Type, int TILE_SIZE, int CACHED_TILE_COUNT>
     132    16193283 : inline Type GDALCachedPixelAccessor<Type, TILE_SIZE, CACHED_TILE_COUNT>::Get(
     133             :     int nX, int nY, bool *pbSuccess)
     134             : {
     135    16193283 :     const int nTileX = nX / TILE_SIZE;
     136    16193283 :     const int nTileY = nY / TILE_SIZE;
     137    16193283 :     const int nXInTile = nX % TILE_SIZE;
     138    16193283 :     const int nYInTile = nY % TILE_SIZE;
     139    32365227 :     if (m_aCachedTiles[0].m_nTileX == nTileX &&
     140    16171944 :         m_aCachedTiles[0].m_nTileY == nTileY)
     141             :     {
     142    16157244 :         if (pbSuccess)
     143           0 :             *pbSuccess = true;
     144    16157244 :         return m_aCachedTiles[0].m_data[nYInTile * TILE_SIZE + nXInTile];
     145             :     }
     146       35986 :     return GetSlowPath(nTileX, nTileY, nXInTile, nYInTile, pbSuccess);
     147             : }
     148             : 
     149             : /************************************************************************/
     150             : /*                       GetSlowPath()                                  */
     151             : /************************************************************************/
     152             : 
     153             : template <class Type, int TILE_SIZE, int CACHED_TILE_COUNT>
     154       35986 : Type GDALCachedPixelAccessor<Type, TILE_SIZE, CACHED_TILE_COUNT>::GetSlowPath(
     155             :     int nTileX, int nTileY, int nXInTile, int nYInTile, bool *pbSuccess)
     156             : {
     157       49788 :     for (int i = 1; i < m_nCachedTileCount; ++i)
     158             :     {
     159       49547 :         const auto &cachedTile = m_aCachedTiles[i];
     160       49547 :         if (cachedTile.m_nTileX == nTileX && cachedTile.m_nTileY == nTileY)
     161             :         {
     162       35745 :             const auto ret = cachedTile.m_data[nYInTile * TILE_SIZE + nXInTile];
     163       35745 :             CachedTile tmp = std::move(m_aCachedTiles[i]);
     164       84518 :             for (int j = i; j >= 1; --j)
     165       48773 :                 m_aCachedTiles[j] = std::move(m_aCachedTiles[j - 1]);
     166       35745 :             m_aCachedTiles[0] = std::move(tmp);
     167       35745 :             if (pbSuccess)
     168           0 :                 *pbSuccess = true;
     169       35745 :             return ret;
     170             :         }
     171             :     }
     172         241 :     if (!LoadTile(nTileX, nTileY))
     173             :     {
     174           0 :         if (pbSuccess)
     175           0 :             *pbSuccess = false;
     176           0 :         return 0;
     177             :     }
     178         241 :     if (pbSuccess)
     179           0 :         *pbSuccess = true;
     180         241 :     return m_aCachedTiles[0].m_data[nYInTile * TILE_SIZE + nXInTile];
     181             : }
     182             : 
     183             : /************************************************************************/
     184             : /*                            Set()                                     */
     185             : /************************************************************************/
     186             : 
     187             : /** Set the value of a pixel.
     188             :  *
     189             :  * The actual modification of the underlying raster is deferred until the tile
     190             :  * is implicit flushed while loading a new tile, or an explicit call to
     191             :  * FlushCache().
     192             :  *
     193             :  * The destructor of GDALCachedPixelAccessor will take care of calling
     194             :  * FlushCache(), if the user hasn't done it explicitly.
     195             :  *
     196             :  * No bound checking of nX, nY is done.
     197             :  *
     198             :  * @param nX X coordinate (between 0 and GetXSize()-1)
     199             :  * @param nY Y coordinate (between 0 and GetYSize()-1)
     200             :  * @param val pixel value
     201             :  * @return true if success
     202             :  */
     203             : template <class Type, int TILE_SIZE, int CACHED_TILE_COUNT>
     204             : inline bool
     205     1475403 : GDALCachedPixelAccessor<Type, TILE_SIZE, CACHED_TILE_COUNT>::Set(int nX, int nY,
     206             :                                                                  Type val)
     207             : {
     208     1475403 :     const int nTileX = nX / TILE_SIZE;
     209     1475403 :     const int nTileY = nY / TILE_SIZE;
     210     1475403 :     const int nXInTile = nX % TILE_SIZE;
     211     1475403 :     const int nYInTile = nY % TILE_SIZE;
     212     2948607 :     if (m_aCachedTiles[0].m_nTileX == nTileX &&
     213     1473194 :         m_aCachedTiles[0].m_nTileY == nTileY)
     214             :     {
     215     1473194 :         m_aCachedTiles[0].m_data[nYInTile * TILE_SIZE + nXInTile] = val;
     216     1473194 :         m_aCachedTiles[0].m_bModified = true;
     217     1473194 :         return true;
     218             :     }
     219        2209 :     return SetSlowPath(nTileX, nTileY, nXInTile, nYInTile, val);
     220             : }
     221             : 
     222             : /************************************************************************/
     223             : /*                         SetSlowPath()                                */
     224             : /************************************************************************/
     225             : 
     226             : template <class Type, int TILE_SIZE, int CACHED_TILE_COUNT>
     227        2209 : bool GDALCachedPixelAccessor<Type, TILE_SIZE, CACHED_TILE_COUNT>::SetSlowPath(
     228             :     int nTileX, int nTileY, int nXInTile, int nYInTile, Type val)
     229             : {
     230        4792 :     for (int i = 1; i < m_nCachedTileCount; ++i)
     231             :     {
     232        4579 :         auto &cachedTile = m_aCachedTiles[i];
     233        4579 :         if (cachedTile.m_nTileX == nTileX && cachedTile.m_nTileY == nTileY)
     234             :         {
     235        1996 :             cachedTile.m_data[nYInTile * TILE_SIZE + nXInTile] = val;
     236        1996 :             cachedTile.m_bModified = true;
     237        1996 :             if (i > 0)
     238             :             {
     239        3992 :                 CachedTile tmp = std::move(m_aCachedTiles[i]);
     240        6068 :                 for (int j = i; j >= 1; --j)
     241        4072 :                     m_aCachedTiles[j] = std::move(m_aCachedTiles[j - 1]);
     242        1996 :                 m_aCachedTiles[0] = std::move(tmp);
     243             :             }
     244        1996 :             return true;
     245             :         }
     246             :     }
     247         213 :     if (!LoadTile(nTileX, nTileY))
     248             :     {
     249           0 :         return false;
     250             :     }
     251         213 :     m_aCachedTiles[0].m_data[nYInTile * TILE_SIZE + nXInTile] = val;
     252         213 :     m_aCachedTiles[0].m_bModified = true;
     253         213 :     return true;
     254             : }
     255             : 
     256             : /************************************************************************/
     257             : /*                            FlushCache()                              */
     258             : /************************************************************************/
     259             : 
     260             : /** Flush content of modified tiles and drop caches
     261             :  *
     262             :  * @return true if success
     263             :  */
     264             : template <class Type, int TILE_SIZE, int CACHED_TILE_COUNT>
     265          36 : bool GDALCachedPixelAccessor<Type, TILE_SIZE, CACHED_TILE_COUNT>::FlushCache()
     266             : {
     267          36 :     bool bRet = true;
     268         202 :     for (int i = 0; i < m_nCachedTileCount; ++i)
     269             :     {
     270         166 :         if (!FlushTile(i))
     271           0 :             bRet = false;
     272         166 :         m_aCachedTiles[i].m_nTileX = -1;
     273         166 :         m_aCachedTiles[i].m_nTileY = -1;
     274             :     }
     275          36 :     return bRet;
     276             : }
     277             : 
     278             : /************************************************************************/
     279             : /*                      ResetModifiedFlag()                             */
     280             : /************************************************************************/
     281             : 
     282             : /** Reset the modified flag for cached tiles.
     283             :  */
     284             : template <class Type, int TILE_SIZE, int CACHED_TILE_COUNT>
     285          10 : void GDALCachedPixelAccessor<Type, TILE_SIZE,
     286             :                              CACHED_TILE_COUNT>::ResetModifiedFlag()
     287             : {
     288          68 :     for (int i = 0; i < m_nCachedTileCount; ++i)
     289             :     {
     290          58 :         m_aCachedTiles[i].m_bModified = false;
     291             :     }
     292          10 : }
     293             : 
     294             : /************************************************************************/
     295             : /*                 GDALCachedPixelAccessorGetDataType                   */
     296             : /************************************************************************/
     297             : 
     298             : /*! @cond Doxygen_Suppress */
     299             : template <class T> struct GDALCachedPixelAccessorGetDataType
     300             : {
     301             : };
     302             : 
     303             : template <> struct GDALCachedPixelAccessorGetDataType<GByte>
     304             : {
     305             :     static constexpr GDALDataType DataType = GDT_Byte;
     306             : };
     307             : 
     308             : template <> struct GDALCachedPixelAccessorGetDataType<GInt8>
     309             : {
     310             :     static constexpr GDALDataType DataType = GDT_Int8;
     311             : };
     312             : 
     313             : template <> struct GDALCachedPixelAccessorGetDataType<GUInt16>
     314             : {
     315             :     static constexpr GDALDataType DataType = GDT_UInt16;
     316             : };
     317             : 
     318             : template <> struct GDALCachedPixelAccessorGetDataType<GInt16>
     319             : {
     320             :     static constexpr GDALDataType DataType = GDT_Int16;
     321             : };
     322             : 
     323             : template <> struct GDALCachedPixelAccessorGetDataType<GUInt32>
     324             : {
     325             :     static constexpr GDALDataType DataType = GDT_UInt32;
     326             : };
     327             : 
     328             : template <> struct GDALCachedPixelAccessorGetDataType<GInt32>
     329             : {
     330             :     static constexpr GDALDataType DataType = GDT_Int32;
     331             : };
     332             : #if SIZEOF_UNSIGNED_LONG == 8
     333             : // std::uint64_t on Linux 64-bit resolves as unsigned long
     334             : template <> struct GDALCachedPixelAccessorGetDataType<unsigned long>
     335             : {
     336             :     static constexpr GDALDataType DataType = GDT_UInt64;
     337             : };
     338             : 
     339             : template <> struct GDALCachedPixelAccessorGetDataType<long>
     340             : {
     341             :     static constexpr GDALDataType DataType = GDT_Int64;
     342             : };
     343             : #endif
     344             : template <> struct GDALCachedPixelAccessorGetDataType<GUInt64>
     345             : {
     346             :     static constexpr GDALDataType DataType = GDT_UInt64;
     347             : };
     348             : 
     349             : template <> struct GDALCachedPixelAccessorGetDataType<GInt64>
     350             : {
     351             :     static constexpr GDALDataType DataType = GDT_Int64;
     352             : };
     353             : 
     354             : template <> struct GDALCachedPixelAccessorGetDataType<float>
     355             : {
     356             :     static constexpr GDALDataType DataType = GDT_Float32;
     357             : };
     358             : 
     359             : template <> struct GDALCachedPixelAccessorGetDataType<double>
     360             : {
     361             :     static constexpr GDALDataType DataType = GDT_Float64;
     362             : };
     363             : 
     364             : /*! @endcond */
     365             : 
     366             : /************************************************************************/
     367             : /*                          LoadTile()                                  */
     368             : /************************************************************************/
     369             : 
     370             : template <class Type, int TILE_SIZE, int CACHED_TILE_COUNT>
     371         454 : bool GDALCachedPixelAccessor<Type, TILE_SIZE, CACHED_TILE_COUNT>::LoadTile(
     372             :     int nTileX, int nTileY)
     373             : {
     374         454 :     if (m_nCachedTileCount == CACHED_TILE_COUNT)
     375             :     {
     376         352 :         if (!FlushTile(CACHED_TILE_COUNT - 1))
     377           0 :             return false;
     378         704 :         CachedTile tmp = std::move(m_aCachedTiles[CACHED_TILE_COUNT - 1]);
     379        1408 :         for (int i = CACHED_TILE_COUNT - 1; i >= 1; --i)
     380        1056 :             m_aCachedTiles[i] = std::move(m_aCachedTiles[i - 1]);
     381         352 :         m_aCachedTiles[0] = std::move(tmp);
     382             :     }
     383             :     else
     384             :     {
     385         102 :         if (m_nCachedTileCount > 0)
     386          81 :             std::swap(m_aCachedTiles[0], m_aCachedTiles[m_nCachedTileCount]);
     387         102 :         m_aCachedTiles[0].m_data.resize(TILE_SIZE * TILE_SIZE);
     388         102 :         m_nCachedTileCount++;
     389             :     }
     390             : 
     391             : #if 0
     392             :     CPLDebug("GDAL", "Load tile(%d, %d) of band %d of dataset %s",
     393             :               nTileX, nTileY, m_poBand->GetBand(),
     394             :               m_poBand->GetDataset() ? m_poBand->GetDataset()->GetDescription() : "(unknown)");
     395             : #endif
     396         454 :     CPLAssert(!m_aCachedTiles[0].m_bModified);
     397         454 :     const int nXOff = nTileX * TILE_SIZE;
     398         454 :     const int nYOff = nTileY * TILE_SIZE;
     399         454 :     const int nReqXSize = std::min(m_poBand->GetXSize() - nXOff, TILE_SIZE);
     400         454 :     const int nReqYSize = std::min(m_poBand->GetYSize() - nYOff, TILE_SIZE);
     401         908 :     if (m_poBand->RasterIO(
     402             :             GF_Read, nXOff, nYOff, nReqXSize, nReqYSize,
     403         454 :             m_aCachedTiles[0].m_data.data(), nReqXSize, nReqYSize,
     404             :             GDALCachedPixelAccessorGetDataType<Type>::DataType, sizeof(Type),
     405         454 :             TILE_SIZE * sizeof(Type), nullptr) != CE_None)
     406             :     {
     407           0 :         m_aCachedTiles[0].m_nTileX = -1;
     408           0 :         m_aCachedTiles[0].m_nTileY = -1;
     409           0 :         return false;
     410             :     }
     411         454 :     m_aCachedTiles[0].m_nTileX = nTileX;
     412         454 :     m_aCachedTiles[0].m_nTileY = nTileY;
     413         454 :     return true;
     414             : }
     415             : 
     416             : /************************************************************************/
     417             : /*                          FlushTile()                                 */
     418             : /************************************************************************/
     419             : 
     420             : template <class Type, int TILE_SIZE, int CACHED_TILE_COUNT>
     421         518 : bool GDALCachedPixelAccessor<Type, TILE_SIZE, CACHED_TILE_COUNT>::FlushTile(
     422             :     int iSlot)
     423             : {
     424         518 :     if (!m_aCachedTiles[iSlot].m_bModified)
     425         300 :         return true;
     426             : 
     427         218 :     m_aCachedTiles[iSlot].m_bModified = false;
     428         218 :     const int nXOff = m_aCachedTiles[iSlot].m_nTileX * TILE_SIZE;
     429         218 :     const int nYOff = m_aCachedTiles[iSlot].m_nTileY * TILE_SIZE;
     430         218 :     const int nReqXSize = std::min(m_poBand->GetXSize() - nXOff, TILE_SIZE);
     431         218 :     const int nReqYSize = std::min(m_poBand->GetYSize() - nYOff, TILE_SIZE);
     432         218 :     return m_poBand->RasterIO(
     433             :                GF_Write, nXOff, nYOff, nReqXSize, nReqYSize,
     434         218 :                m_aCachedTiles[iSlot].m_data.data(), nReqXSize, nReqYSize,
     435             :                GDALCachedPixelAccessorGetDataType<Type>::DataType, sizeof(Type),
     436         218 :                TILE_SIZE * sizeof(Type), nullptr) == CE_None;
     437             : }
     438             : 
     439             : #endif  // GDAL_PIXEL_ACCESSOR_INCLUDED

Generated by: LCOV version 1.14