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