Pyrogenesis HEAD
Pyrogenesis, a RTS Engine
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 */
36template<typename T>
37class Grid
38{
39 friend struct SerializeHelper<Grid<T>>;
40protected:
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>
56
57public:
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;
73public:
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 {
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 */
257template<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 */
309template<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
326public:
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
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 {
391
392 bool wasDirty = dirty;
393
394 dirty |= b.dirty;
396
397 // If the current grid is useless, swap it
398 if (!wasDirty)
400 // If the new grid isn't used, don't bother updating it
401 else if (dirty && !globallyDirty)
403
404 b.Clean();
405 }
406
407 /**
408 * Mark everything as clean
409 */
410 void Clean()
411 {
412 dirty = false;
413 globallyDirty = false;
415 }
416};
417
418#endif // INCLUDED_GRID
#define swap(a, i, j)
Helper templates definitions for serializing/deserializing common objects.
void Serializer(S &serialize, const char *name, Args &&... args)
Definition: SerializeTemplates.h:51
Basic 2D array, intended for storing tile data, plus support for lazy updates by ICmpObstructionManag...
Definition: Grid.h:38
void reset_data(is_pod)
Definition: Grid.h:162
if_< std::is_pod< U >, is_pod, if_< has_value_type< U >, is_container, default_type > > dispatch
Definition: Grid.h:55
T & operator[](std::pair< u16, u16 > coords)
Definition: Grid.h:221
~Grid()
Definition: Grid.h:108
T & operator[](std::pair< u16, u16 > coords) const
Definition: Grid.h:224
Grid & operator=(const Grid &g)
Definition: Grid.h:79
u16 m_H
Definition: Grid.h:249
u16 height() const
Definition: Grid.h:132
void bitwise_or(const Grid &g)
Definition: Grid.h:201
void copy_data(T *o, is_pod)
Definition: Grid.h:77
T * m_Data
Definition: Grid.h:250
bool compare_data(T *o, is_pod) const
Definition: Grid.h:115
u16 m_W
Definition: Grid.h:249
bool _any_set_in_square(int i0, int j0, int i1, int j1, is_pod) const
Definition: Grid.h:140
T & get(std::pair< u16, u16 > coords)
Definition: Grid.h:222
void add(const Grid &g)
Definition: Grid.h:192
bool _any_set_in_square(int, int, int, int, default_type) const
Definition: Grid.h:135
void clear()
Definition: Grid.h:172
T & get(int i, int j)
Definition: Grid.h:227
T & get(int i, int j) const
Definition: Grid.h:235
Grid()
Definition: Grid.h:58
bool any_set_in_square(int i0, int j0, int i1, int j1) const
Definition: Grid.h:156
void set(int i, int j, const T &value)
Definition: Grid.h:213
Grid(u16 w, u16 h)
Definition: Grid.h:62
void copy_data(T *o, default_type)
Definition: Grid.h:76
void swap(Grid &g)
Definition: Grid.h:101
void reset()
Definition: Grid.h:165
Grid(const Grid &g)
Definition: Grid.h:67
typename std::conditional< U::value, A, B >::type if_
Definition: Grid.h:50
T & get(std::pair< u16, u16 > coords) const
Definition: Grid.h:225
bool operator==(const Grid &g) const
Definition: Grid.h:117
T value_type
Definition: Grid.h:72
bool compare_sizes(const Grid< U > *g) const
Definition: Grid.h:244
bool operator!=(const Grid &g) const
Definition: Grid.h:124
bool compare_data(T *o, default_type) const
Definition: Grid.h:114
u16 width() const
Definition: Grid.h:131
bool blank() const
Definition: Grid.h:126
void resize(u16 w, u16 h)
Definition: Grid.h:177
void reset_data(default_type)
Definition: Grid.h:161
Deserialization interface; see serialization overview.
Definition: IDeserializer.h:35
virtual void NumberU16_Unbounded(const char *name, uint16_t &out)
Definition: IDeserializer.cpp:110
virtual void NumberU32_Unbounded(const char *name, uint32_t &out)
Definition: IDeserializer.cpp:124
Serialization interface; see serialization overview.
Definition: ISerializer.h:121
void NumberU32_Unbounded(const char *name, uint32_t value)
Serialize a number.
Definition: ISerializer.h:171
void NumberU16_Unbounded(const char *name, uint16_t value)
Serialize a number.
Definition: ISerializer.h:161
Similar to Grid, except optimised for sparse usage (the grid is subdivided into buckets whose content...
Definition: Grid.h:311
T * GetBucket(int i, int j)
Definition: Grid.h:316
T ** m_Data
Definition: Grid.h:370
size_t m_DirtyID
Definition: Grid.h:372
u16 m_BH
Definition: Grid.h:369
~SparseGrid()
Definition: Grid.h:337
u16 m_H
Definition: Grid.h:368
void set(int i, int j, const T &value)
Definition: Grid.h:352
u16 m_BW
Definition: Grid.h:369
u16 m_W
Definition: Grid.h:368
void reset()
Definition: Grid.h:343
@ BucketSize
Definition: Grid.h:314
@ BucketBits
Definition: Grid.h:314
T & get(int i, int j)
Definition: Grid.h:360
SparseGrid(u16 w, u16 h)
Definition: Grid.h:327
NONCOPYABLE(SparseGrid)
#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
#define ENSURE(expr)
ensure the expression <expr> evaluates to non-zero.
Definition: debug.h:277
Definition: ShaderDefines.cpp:31
#define T(string_literal)
Definition: secure_crt.cpp:77
Structure holding grid dirtiness informations, for clever updates.
Definition: Grid.h:379
bool dirty
Definition: Grid.h:380
void Clean()
Mark everything as clean.
Definition: Grid.h:410
Grid< u8 > dirtinessGrid
Definition: Grid.h:382
bool globallyDirty
Definition: Grid.h:381
void MergeAndClear(GridUpdateInformation &b)
Update the information with additionnal needed updates, then erase the source of additions.
Definition: Grid.h:388
Definition: Grid.h:42
Definition: Grid.h:47
Definition: Grid.h:44
Definition: Grid.h:43
void operator()(IDeserializer &deserialize, const char *name, Grid< T > &value)
Definition: Grid.h:285
void operator()(ISerializer &serialize, const char *name, Grid< T > &value)
Definition: Grid.h:260
Definition: SerializeTemplates.h:42
uint16_t u16
Definition: types.h:38
uint32_t u32
Definition: types.h:39