Pyrogenesis  trunk
Grid.h
Go to the documentation of this file.
1 /* Copyright (C) 2020 Wildfire Games.
2  * This file is part of 0 A.D.
3  *
4  * 0 A.D. is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * 0 A.D. is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #ifndef INCLUDED_GRID
19 #define INCLUDED_GRID
20 
22 
23 #include <cstring>
24 
25 #ifdef NDEBUG
26 #define GRID_BOUNDS_DEBUG 0
27 #else
28 #define GRID_BOUNDS_DEBUG 1
29 #endif
30 
31 /**
32  * Basic 2D array, intended for storing tile data, plus support for lazy updates
33  * by ICmpObstructionManager.
34  * @c T must be a POD type that can be initialised with 0s.
35  */
36 template<typename T>
37 class Grid
38 {
39  friend struct SerializeHelper<Grid<T>>;
40 protected:
41  // Tag-dispatching internal utilities for convenience.
42  struct default_type{};
43  struct is_pod { operator default_type() { return default_type{}; }};
44  struct is_container { operator default_type() { return default_type{}; }};
45 
46  // helper to detect value_type
47  template <typename U, typename = int> struct has_value_type : std::false_type { };
48  template <typename U> struct has_value_type <U, decltype(std::declval<typename U::value_type>(), 0)> : std::true_type { };
49 
50  template <typename U, typename A, typename B> using if_ = typename std::conditional<U::value, A, B>::type;
51 
52  template<typename U>
53  using dispatch = if_< std::is_pod<U>, is_pod,
54  if_<has_value_type<U>, is_container,
55  default_type>>;
56 
57 public:
58  Grid() : m_W(0), m_H(0), m_Data(NULL)
59  {
60  }
61 
62  Grid(u16 w, u16 h) : m_W(w), m_H(h), m_Data(NULL)
63  {
64  resize(w, h);
65  }
66 
67  Grid(const Grid& g) : m_W(0), m_H(0), m_Data(NULL)
68  {
69  *this = g;
70  }
71 
72  using value_type = T;
73 public:
74 
75  // Ensure that o and this are the same size before calling.
76  void copy_data(T* o, default_type) { std::copy(o, o + m_H*m_W, &m_Data[0]); }
77  void copy_data(T* o, is_pod) { memcpy(m_Data, o, m_W*m_H*sizeof(T)); }
78 
79  Grid& operator=(const Grid& g)
80  {
81  if (this == &g)
82  return *this;
83 
84  if (m_W == g.m_W && m_H == g.m_H)
85  {
87  return *this;
88  }
89 
90  m_W = g.m_W;
91  m_H = g.m_H;
93  if (g.m_Data)
94  {
95  m_Data = new T[m_W * m_H];
97  }
98  return *this;
99  }
100 
101  void swap(Grid& g)
102  {
103  std::swap(m_Data, g.m_Data);
104  std::swap(m_H, g.m_H);
105  std::swap(m_W, g.m_W);
106  }
107 
109  {
111  }
112 
113  // Ensure that o and this are the same size before calling.
114  bool compare_data(T* o, default_type) const { return std::equal(&m_Data[0], &m_Data[m_W*m_H], o); }
115  bool compare_data(T* o, is_pod) const { return memcmp(m_Data, o, m_W*m_H*sizeof(T)) == 0; }
116 
117  bool operator==(const Grid& g) const
118  {
119  if (!compare_sizes(&g))
120  return false;
121 
122  return compare_data(g.m_Data, dispatch<T>{});
123  }
124  bool operator!=(const Grid& g) const { return !(*this==g); }
125 
126  bool blank() const
127  {
128  return m_W == 0 && m_H == 0;
129  }
130 
131  u16 width() const { return m_W; };
132  u16 height() const { return m_H; };
133 
134 
135  bool _any_set_in_square(int, int, int, int, default_type) const
136  {
137  static_assert(!std::is_same<T, T>::value, "Not implemented.");
138  return false; // Fix warnings.
139  }
140  bool _any_set_in_square(int i0, int j0, int i1, int j1, is_pod) const
141  {
142 #if GRID_BOUNDS_DEBUG
143  ENSURE(i0 >= 0 && j0 >= 0 && i1 <= m_W && j1 <= m_H);
144 #endif
145  for (int j = j0; j < j1; ++j)
146  {
147  int sum = 0;
148  for (int i = i0; i < i1; ++i)
149  sum += m_Data[j*m_W + i];
150  if (sum > 0)
151  return true;
152  }
153  return false;
154  }
155 
156  bool any_set_in_square(int i0, int j0, int i1, int j1) const
157  {
158  return _any_set_in_square(i0, j0, i1, j1, dispatch<T>{});
159  }
160 
161  void reset_data(default_type) { std::fill(&m_Data[0], &m_Data[m_H*m_W], T{}); }
162  void reset_data(is_pod) { memset(m_Data, 0, m_W*m_H*sizeof(T)); }
163 
164  // Reset the data to its default-constructed value (usually 0), not changing size.
165  void reset()
166  {
167  if (m_Data)
169  }
170 
171  // Clear the grid setting the size to 0 and freeing any data.
172  void clear()
173  {
174  resize(0, 0);
175  }
176 
177  void resize(u16 w, u16 h)
178  {
180  m_W = w;
181  m_H = h;
182 
183  if (!m_W && !m_H)
184  return;
185 
186  m_Data = new T[m_W * m_H];
187  ENSURE(m_Data);
188  reset();
189  }
190 
191  // Add two grids of the same size
192  void add(const Grid& g)
193  {
194 #if GRID_BOUNDS_DEBUG
195  ENSURE(g.m_W == m_W && g.m_H == m_H);
196 #endif
197  for (int i=0; i < m_H*m_W; ++i)
198  m_Data[i] += g.m_Data[i];
199  }
200 
201  void bitwise_or(const Grid& g)
202  {
203  if (this == &g)
204  return;
205 
206 #if GRID_BOUNDS_DEBUG
207  ENSURE(g.m_W == m_W && g.m_H == m_H);
208 #endif
209  for (int i = 0; i < m_H*m_W; ++i)
210  m_Data[i] |= g.m_Data[i];
211  }
212 
213  void set(int i, int j, const T& value)
214  {
215 #if GRID_BOUNDS_DEBUG
216  ENSURE(0 <= i && i < m_W && 0 <= j && j < m_H);
217 #endif
218  m_Data[j*m_W + i] = value;
219  }
220 
221  T& operator[](std::pair<u16, u16> coords) { return get(coords.first, coords.second); }
222  T& get(std::pair<u16, u16> coords) { return get(coords.first, coords.second); }
223 
224  T& operator[](std::pair<u16, u16> coords) const { return get(coords.first, coords.second); }
225  T& get(std::pair<u16, u16> coords) const { return get(coords.first, coords.second); }
226 
227  T& get(int i, int j)
228  {
229 #if GRID_BOUNDS_DEBUG
230  ENSURE(0 <= i && i < m_W && 0 <= j && j < m_H);
231 #endif
232  return m_Data[j*m_W + i];
233  }
234 
235  T& get(int i, int j) const
236  {
237 #if GRID_BOUNDS_DEBUG
238  ENSURE(0 <= i && i < m_W && 0 <= j && j < m_H);
239 #endif
240  return m_Data[j*m_W + i];
241  }
242 
243  template<typename U>
244  bool compare_sizes(const Grid<U>* g) const
245  {
246  return g && m_W == g->m_W && m_H == g->m_H;
247  }
248 
251 };
252 
253 
254 /**
255  * Serialize a grid, applying a simple RLE compression that is assumed efficient.
256  */
257 template<typename T>
259 {
260  void operator()(ISerializer& serialize, const char* name, Grid<T>& value)
261  {
262  size_t len = value.m_H * value.m_W;
263  serialize.NumberU16_Unbounded("width", value.m_W);
264  serialize.NumberU16_Unbounded("height", value.m_H);
265  if (len == 0)
266  return;
267  u32 count = 1;
268  T prevVal = value.m_Data[0];
269  for (size_t i = 1; i < len; ++i)
270  {
271  if (prevVal == value.m_Data[i])
272  {
273  count++;
274  continue;
275  }
276  serialize.NumberU32_Unbounded("#", count);
277  Serializer(serialize, name, prevVal);
278  count = 1;
279  prevVal = value.m_Data[i];
280  }
281  serialize.NumberU32_Unbounded("#", count);
282  Serializer(serialize, name, prevVal);
283  }
284 
285  void operator()(IDeserializer& deserialize, const char* name, Grid<T>& value)
286  {
287  u16 w, h;
288  deserialize.NumberU16_Unbounded("width", w);
289  deserialize.NumberU16_Unbounded("height", h);
290  u32 len = h * w;
291  value.resize(w, h);
292  for (size_t i = 0; i < len;)
293  {
294  u32 count;
295  deserialize.NumberU32_Unbounded("#", count);
296  T el;
297  Serializer(deserialize, name, el);
298  std::fill(&value.m_Data[i], &value.m_Data[i+count], el);
299  i += count;
300  }
301  }
302 };
303 
304 
305 /**
306  * Similar to Grid, except optimised for sparse usage (the grid is subdivided into
307  * buckets whose contents are only initialised on demand, to save on memset cost).
308  */
309 template<typename T>
311 {
313 
314  enum { BucketBits = 4, BucketSize = 1 << BucketBits };
315 
316  T* GetBucket(int i, int j)
317  {
318  size_t b = (j >> BucketBits) * m_BW + (i >> BucketBits);
319  if (!m_Data[b])
320  {
321  m_Data[b] = new T[BucketSize*BucketSize]();
322  }
323  return m_Data[b];
324  }
325 
326 public:
327  SparseGrid(u16 w, u16 h) : m_W(w), m_H(h), m_DirtyID(0)
328  {
329  ENSURE(m_W && m_H);
330 
331  m_BW = (u16)((m_W + BucketSize-1) >> BucketBits);
332  m_BH = (u16)((m_H + BucketSize-1) >> BucketBits);
333 
334  m_Data = new T*[m_BW*m_BH]();
335  }
336 
338  {
339  reset();
341  }
342 
343  void reset()
344  {
345  for (size_t i = 0; i < (size_t)(m_BW*m_BH); ++i)
347 
348  // Reset m_Data by value-constructing in place with placement new.
349  m_Data = new (m_Data) T*[m_BW*m_BH]();
350  }
351 
352  void set(int i, int j, const T& value)
353  {
354 #if GRID_BOUNDS_DEBUG
355  ENSURE(0 <= i && i < m_W && 0 <= j && j < m_H);
356 #endif
357  GetBucket(i, j)[(j % BucketSize)*BucketSize + (i % BucketSize)] = value;
358  }
359 
360  T& get(int i, int j)
361  {
362 #if GRID_BOUNDS_DEBUG
363  ENSURE(0 <= i && i < m_W && 0 <= j && j < m_H);
364 #endif
365  return GetBucket(i, j)[(j % BucketSize)*BucketSize + (i % BucketSize)];
366  }
367 
369  u16 m_BW, m_BH;
371 
372  size_t m_DirtyID; // if this is < the id maintained by ICmpObstructionManager then it needs to be updated
373 };
374 
375 /**
376  * Structure holding grid dirtiness informations, for clever updates.
377  */
379 {
380  bool dirty;
383 
384  /**
385  * Update the information with additionnal needed updates, then erase the source of additions.
386  * This can usually be optimized through a careful memory management.
387  */
389  {
390  ENSURE(dirtinessGrid.compare_sizes(&b.dirtinessGrid));
391 
392  bool wasDirty = dirty;
393 
394  dirty |= b.dirty;
395  globallyDirty |= b.globallyDirty;
396 
397  // If the current grid is useless, swap it
398  if (!wasDirty)
399  dirtinessGrid.swap(b.dirtinessGrid);
400  // If the new grid isn't used, don't bother updating it
401  else if (dirty && !globallyDirty)
402  dirtinessGrid.bitwise_or(b.dirtinessGrid);
403 
404  b.Clean();
405  }
406 
407  /**
408  * Mark everything as clean
409  */
410  void Clean()
411  {
412  dirty = false;
413  globallyDirty = false;
414  dirtinessGrid.reset();
415  }
416 };
417 
418 #endif // INCLUDED_GRID
void swap(Grid &g)
Definition: Grid.h:101
#define NONCOPYABLE(className)
Indicates that a class is noncopyable (usually due to const or reference members, or because the clas...
Definition: code_annotation.h:227
void clear()
Definition: Grid.h:172
Definition: Grid.h:44
bool operator==(const Grid &g) const
Definition: Grid.h:117
Grid & operator=(const Grid &g)
Definition: Grid.h:79
Helper templates definitions for serializing/deserializing common objects.
T & operator[](std::pair< u16, u16 > coords)
Definition: Grid.h:221
Similar to Grid, except optimised for sparse usage (the grid is subdivided into buckets whose content...
Definition: Grid.h:310
bool dirty
Definition: Grid.h:380
Grid()
Definition: Grid.h:58
bool blank() const
Definition: Grid.h:126
T & operator[](std::pair< u16, u16 > coords) const
Definition: Grid.h:224
uint16_t u16
Definition: types.h:38
void operator()(ISerializer &serialize, const char *name, Grid< T > &value)
Definition: Grid.h:260
bool operator!=(const Grid &g) const
Definition: Grid.h:124
Serialization interface; see serialization overview.
Definition: ISerializer.h:120
u16 height() const
Definition: Grid.h:132
u16 m_H
Definition: Grid.h:249
void Serializer(S &serialize, const char *name, Args &&... args)
Definition: SerializeTemplates.h:51
Grid(u16 w, u16 h)
Definition: Grid.h:62
virtual void NumberU32_Unbounded(const char *name, uint32_t &out)
Definition: IDeserializer.cpp:124
bool compare_sizes(const Grid< U > *g) const
Definition: Grid.h:244
Definition: ShaderDefines.cpp:30
Structure holding grid dirtiness informations, for clever updates.
Definition: Grid.h:378
Basic 2D array, intended for storing tile data, plus support for lazy updates by ICmpObstructionManag...
Definition: TerritoryBoundary.h:27
typename std::conditional< U::value, A, B >::type if_
Definition: Grid.h:50
void reset_data(default_type)
Definition: Grid.h:161
bool globallyDirty
Definition: Grid.h:381
bool _any_set_in_square(int i0, int j0, int i1, int j1, is_pod) const
Definition: Grid.h:140
void reset_data(is_pod)
Definition: Grid.h:162
void reset()
Definition: Grid.h:343
u16 width() const
Definition: Grid.h:131
#define ENSURE(expr)
ensure the expression <expr> evaluates to non-zero.
Definition: debug.h:290
uint32_t u32
Definition: types.h:39
SparseGrid(u16 w, u16 h)
Definition: Grid.h:327
void bitwise_or(const Grid &g)
Definition: Grid.h:201
bool _any_set_in_square(int, int, int, int, default_type) const
Definition: Grid.h:135
void MergeAndClear(GridUpdateInformation &b)
Update the information with additionnal needed updates, then erase the source of additions.
Definition: Grid.h:388
void reset()
Definition: Grid.h:165
Definition: Grid.h:43
Definition: Grid.h:42
void resize(u16 w, u16 h)
Definition: Grid.h:177
~Grid()
Definition: Grid.h:108
~SparseGrid()
Definition: Grid.h:337
#define T(string_literal)
Definition: secure_crt.cpp:77
void add(const Grid &g)
Definition: Grid.h:192
void NumberU32_Unbounded(const char *name, uint32_t value)
Serialize a number.
Definition: ISerializer.h:171
void copy_data(T *o, default_type)
Definition: Grid.h:76
Definition: SerializeTemplates.h:41
u16 m_W
Definition: Grid.h:368
Grid< u8 > dirtinessGrid
Definition: Grid.h:382
bool compare_data(T *o, default_type) const
Definition: Grid.h:114
if_< std::is_pod< U >, is_pod, if_< has_value_type< U >, is_container, default_type > > dispatch
Definition: Grid.h:55
bool any_set_in_square(int i0, int j0, int i1, int j1) const
Definition: Grid.h:156
T * m_Data
Definition: Grid.h:250
T ** m_Data
Definition: Grid.h:370
bool compare_data(T *o, is_pod) const
Definition: Grid.h:115
void Clean()
Mark everything as clean.
Definition: Grid.h:410
#define SAFE_ARRAY_DELETE(p)
delete memory ensuing from new[] and set the pointer to zero (thus making double-frees safe / a no-op...
Definition: code_generation.h:121
u16 m_W
Definition: Grid.h:249
void operator()(IDeserializer &deserialize, const char *name, Grid< T > &value)
Definition: Grid.h:285
u16 value_type
Definition: Grid.h:72
void copy_data(T *o, is_pod)
Definition: Grid.h:77
void NumberU16_Unbounded(const char *name, uint16_t value)
Serialize a number.
Definition: ISerializer.h:161
size_t m_DirtyID
Definition: Grid.h:372
#define swap(a, i, j)
Grid(const Grid &g)
Definition: Grid.h:67
u16 m_BW
Definition: Grid.h:369
virtual void NumberU16_Unbounded(const char *name, uint16_t &out)
Definition: IDeserializer.cpp:110
Definition: Grid.h:47
T * GetBucket(int i, int j)
Definition: Grid.h:316
Deserialization interface; see serialization overview.
Definition: IDeserializer.h:34