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