KiCad PCB EDA Suite
Loading...
Searching...
No Matches
cached_container.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright 2013-2017 CERN
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * @author Maciej Suminski <[email protected]>
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version 2
12 * of the License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, you may find one here:
21 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
22 * or you may search the http://www.gnu.org website for the version 2 license,
23 * or you may write to the Free Software Foundation, Inc.,
24 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
25 */
26
37#include <gal/opengl/utils.h>
38
39#include <list>
40#include <algorithm>
41#include <cassert>
42
43#ifdef __WIN32__
44#include <excpt.h>
45#endif
46
47#ifdef KICAD_GAL_PROFILE
48#include <wx/log.h>
49#include <core/profile.h>
50#endif /* KICAD_GAL_PROFILE */
51
52using namespace KIGFX;
53
55 VERTEX_CONTAINER( aSize ),
56 m_item( nullptr ),
57 m_chunkSize( 0 ),
58 m_chunkOffset( 0 ),
59 m_maxIndex( 0 )
60{
61 // In the beginning there is only free space
62 m_freeChunks.insert( std::make_pair( aSize, 0 ) );
63}
64
65
67{
68 assert( aItem != nullptr );
69
70 unsigned int itemSize = aItem->GetSize();
71 m_item = aItem;
72 m_chunkSize = itemSize;
73
74 // Get the previously set offset if the item was stored previously
75 m_chunkOffset = itemSize > 0 ? aItem->GetOffset() : -1;
76}
77
78
80{
81 assert( m_item != nullptr );
82
83 unsigned int itemSize = m_item->GetSize();
84
85 // Finishing the previously edited item
86 if( itemSize < m_chunkSize )
87 {
88 // There is some not used but reserved memory left, so we should return it to the pool
89 int itemOffset = m_item->GetOffset();
90
91 // Add the not used memory back to the pool
92 addFreeChunk( itemOffset + itemSize, m_chunkSize - itemSize );
93
94 // mergeFreeChunks(); // veery slow and buggy
95
96 m_maxIndex = std::max( itemOffset + itemSize, m_maxIndex );
97 }
98
99 if( itemSize > 0 )
100 m_items.insert( m_item );
101
102 m_item = nullptr;
103 m_chunkSize = 0;
104 m_chunkOffset = 0;
105
106#if CACHED_CONTAINER_TEST > 1
107 test();
108#endif
109}
110
111
112VERTEX* CACHED_CONTAINER::Allocate( unsigned int aSize )
113{
114 assert( m_item != nullptr );
115 assert( IsMapped() );
116
117 if( m_failed )
118 return nullptr;
119
120 unsigned int itemSize = m_item->GetSize();
121 unsigned int newSize = itemSize + aSize;
122
123 if( newSize > m_chunkSize )
124 {
125 // There is not enough space in the currently reserved chunk, so we have to resize it
126 if( !reallocate( newSize ) )
127 {
128 m_failed = true;
129 return nullptr;
130 }
131 }
132
133 VERTEX* reserved = &m_vertices[m_chunkOffset + itemSize];
134
135 // Now the item officially possesses the memory chunk
136 m_item->setSize( newSize );
137
138 // The content has to be updated
139 m_dirty = true;
140
141#if CACHED_CONTAINER_TEST > 0
142 test();
143#endif
144#if CACHED_CONTAINER_TEST > 2
147#endif
148
149 return reserved;
150}
151
152
154{
155 assert( aItem != nullptr );
156 assert( m_items.find( aItem ) != m_items.end() || aItem->GetSize() == 0 );
157
158 int size = aItem->GetSize();
159
160 if( size == 0 )
161 return; // Item is not stored here
162
163 int offset = aItem->GetOffset();
164
165 // Insert a free memory chunk entry in the place where item was stored
166 addFreeChunk( offset, size );
167
168 // Indicate that the item is not stored in the container anymore
169 aItem->setSize( 0 );
170
171 m_items.erase( aItem );
172
173#if CACHED_CONTAINER_TEST > 0
174 test();
175#endif
176
177 // This dynamic memory freeing optimize memory usage, but in fact can create
178 // out of memory issues because freeing and reallocation large chunks of memory
179 // can create memory fragmentation and no room to reallocate large chunks
180 // after many free/reallocate cycles during a session using the same complex board
181 // So it can be disable.
182 // Currently: it is disable to avoid "out of memory" issues
183#if 0
184 // Dynamic memory freeing, there is no point in holding
185 // a large amount of memory when there is no use for it
187 {
189 }
190#endif
191}
192
193
195{
197 m_maxIndex = 0;
198 m_failed = false;
199
200 // Set the size of all the stored VERTEX_ITEMs to 0, so it is clear that they are not held
201 // in the container anymore
202 for( ITEMS::iterator it = m_items.begin(); it != m_items.end(); ++it )
203 ( *it )->setSize( 0 );
204
205 m_items.clear();
206
207 // Now there is only free space left
208 m_freeChunks.clear();
209 m_freeChunks.insert( std::make_pair( m_freeSpace, 0 ) );
210}
211
212
213bool CACHED_CONTAINER::reallocate( unsigned int aSize )
214{
215 assert( aSize > 0 );
216 assert( IsMapped() );
217
218 unsigned int itemSize = m_item->GetSize();
219
220 // Find a free space chunk >= aSize
221 FREE_CHUNK_MAP::iterator newChunk = m_freeChunks.lower_bound( aSize );
222
223 // Is there enough space to store vertices?
224 if( newChunk == m_freeChunks.end() )
225 {
226 bool result;
227
228 // Would it be enough to double the current space?
229 if( aSize < m_freeSpace + m_currentSize )
230 {
231 // Yes: exponential growing
232 result = defragmentResize( m_currentSize * 2 );
233 }
234 else
235 {
236 // No: grow to the nearest greater power of 2
237 result = defragmentResize( pow( 2, ceil( log2( m_currentSize * 2 + aSize ) ) ) );
238 }
239
240 if( !result )
241 return false;
242
243 newChunk = m_freeChunks.lower_bound( aSize );
244 assert( newChunk != m_freeChunks.end() );
245 }
246
247 // Parameters of the allocated chunk
248 unsigned int newChunkSize = getChunkSize( *newChunk );
249 unsigned int newChunkOffset = getChunkOffset( *newChunk );
250
251 assert( newChunkSize >= aSize );
252 assert( newChunkOffset < m_currentSize );
253
254 // Check if the item was previously stored in the container
255 if( itemSize > 0 )
256 {
257 // The item was reallocated, so we have to copy all the old data to the new place
258 memcpy( &m_vertices[newChunkOffset], &m_vertices[m_chunkOffset], itemSize * VERTEX_SIZE );
259
260 // Free the space used by the previous chunk
262 }
263
264 // Remove the new allocated chunk from the free space pool
265 m_freeChunks.erase( newChunk );
266 m_freeSpace -= newChunkSize;
267
268 m_chunkSize = newChunkSize;
269 m_chunkOffset = newChunkOffset;
270
272
273 return true;
274}
275
276
278{
279 // Defragmentation
280 ITEMS::iterator it, it_end;
281 int newOffset = 0;
282
283 [&]()
284 {
285#ifdef __WIN32__
286 #ifdef __MINGW32__
287 // currently, because SEH (Structured Exception Handling) is not documented on msys
288 // (for instance __try or __try1 exists without doc) or is not supported, do nothing
289 #else
290 __try
291 #endif
292#endif
293 {
294 for( VERTEX_ITEM* item : m_items )
295 {
296 int itemOffset = item->GetOffset();
297 int itemSize = item->GetSize();
298
299 // Move an item to the new container
300 memcpy( &aTarget[newOffset], &m_vertices[itemOffset], itemSize * VERTEX_SIZE );
301
302 // Update new offset
303 item->setOffset( newOffset );
304
305 // Move to the next free space
306 newOffset += itemSize;
307 }
308
309 // Move the current item and place it at the end
310 if( m_item->GetSize() > 0 )
311 {
312 memcpy( &aTarget[newOffset], &m_vertices[m_item->GetOffset()],
314 m_item->setOffset( newOffset );
315 m_chunkOffset = newOffset;
316 }
317 }
318#ifdef __WIN32__
319 #ifdef __MINGW32__
320 // currently, because SEH (Structured Exception Handling) is not documented on msys
321 // (for instance __except1 exists without doc) or is not supported, do nothing
322 #else
323 __except( GetExceptionCode() == STATUS_ACCESS_VIOLATION ? EXCEPTION_EXECUTE_HANDLER
324 : EXCEPTION_CONTINUE_SEARCH )
325 {
326 throw std::runtime_error(
327 "Access violation in defragment. This is usually an indicator of "
328 "system or GPU memory running low." );
329 };
330 #endif
331#endif
332 }();
333
335}
336
337
339{
340 if( m_freeChunks.size() <= 1 ) // There are no chunks that can be merged
341 return;
342
343#ifdef KICAD_GAL_PROFILE
344 PROF_TIMER totalTime;
345#endif /* KICAD_GAL_PROFILE */
346
347 // Reversed free chunks map - this one stores chunk size with its offset as the key
348 std::list<CHUNK> freeChunks;
349
350 FREE_CHUNK_MAP::const_iterator it, it_end;
351
352 for( it = m_freeChunks.begin(), it_end = m_freeChunks.end(); it != it_end; ++it )
353 {
354 freeChunks.emplace_back( it->second, it->first );
355 }
356
357 m_freeChunks.clear();
358 freeChunks.sort();
359
360 std::list<CHUNK>::const_iterator itf, itf_end;
361 unsigned int offset = freeChunks.front().first;
362 unsigned int size = freeChunks.front().second;
363 freeChunks.pop_front();
364
365 for( itf = freeChunks.begin(), itf_end = freeChunks.end(); itf != itf_end; ++itf )
366 {
367 if( itf->first == offset + size )
368 {
369 // These chunks can be merged, so just increase the current chunk size and go on
370 size += itf->second;
371 }
372 else
373 {
374 // These chunks cannot be merged
375 // So store the previous one
376 m_freeChunks.insert( std::make_pair( size, offset ) );
377
378 // and let's check the next chunk
379 offset = itf->first;
380 size = itf->second;
381 }
382 }
383
384 // Add the last one
385 m_freeChunks.insert( std::make_pair( size, offset ) );
386
387#if CACHED_CONTAINER_TEST > 0
388 test();
389#endif
390}
391
392
393void CACHED_CONTAINER::addFreeChunk( unsigned int aOffset, unsigned int aSize )
394{
395 assert( aOffset + aSize <= m_currentSize );
396 assert( aSize > 0 );
397
398 m_freeChunks.insert( std::make_pair( aSize, aOffset ) );
399 m_freeSpace += aSize;
400}
401
402
404{
405}
406
407
409{
410}
411
412
414{
415#ifdef KICAD_GAL_PROFILE
416 // Free space check
417 unsigned int freeSpace = 0;
418 FREE_CHUNK_MAP::iterator itf;
419
420 for( itf = m_freeChunks.begin(); itf != m_freeChunks.end(); ++itf )
421 freeSpace += getChunkSize( *itf );
422
423 assert( freeSpace == m_freeSpace );
424
425 // Used space check
426 unsigned int used_space = 0;
427 ITEMS::iterator itr;
428
429 for( itr = m_items.begin(); itr != m_items.end(); ++itr )
430 used_space += ( *itr )->GetSize();
431
432 // If we have a chunk assigned, then there must be an item edited
433 assert( m_chunkSize == 0 || m_item );
434
435 // Currently reserved chunk is also counted as used
436 used_space += m_chunkSize;
437
438 assert( ( m_freeSpace + used_space ) == m_currentSize );
439
440 // Overlapping check TODO
441#endif /* KICAD_GAL_PROFILE */
442}
unsigned int m_chunkOffset
Maximal vertex index number stored in the container.
void addFreeChunk(unsigned int aOffset, unsigned int aSize)
Add a chunk marked as a free space.
int getChunkSize(const CHUNK &aChunk) const
Return the size of a chunk.
void mergeFreeChunks()
Look for consecutive free memory chunks and merges them, decreasing fragmentation of memory.
void showFreeChunks()
Debug & test functions.
VERTEX_ITEM * m_item
Properties of currently modified chunk & item.
FREE_CHUNK_MAP m_freeChunks
Stored VERTEX_ITEMs.
void defragment(VERTEX *aTarget)
Transfer all stored data to a new buffer, removing empty spaces between the data chunks in the contai...
virtual void FinishItem() override
Clean up after adding an item.
virtual void Delete(VERTEX_ITEM *aItem) override
Remove all data stored in the container and restores its original state.
unsigned int getChunkOffset(const CHUNK &aChunk) const
Return the offset of a chunk.
virtual VERTEX * Allocate(unsigned int aSize) override
Return allocated space for the requested number of vertices associated with the current item (set wit...
virtual void Clear() override
Remove all data stored in the container and restores its original state.
virtual bool defragmentResize(unsigned int aNewSize)=0
Remove empty spaces between chunks and optionally resizes the container.
virtual void SetItem(VERTEX_ITEM *aItem) override
Clean up after adding an item.
CACHED_CONTAINER(unsigned int aSize=DEFAULT_SIZE)
virtual bool IsMapped() const =0
Return true if vertex buffer is currently mapped.
ITEMS m_items
Currently modified item.
bool reallocate(unsigned int aSize)
Resize the chunk that stores the current item to the given size.
unsigned int m_initialSize
Actual storage memory.
unsigned int m_currentSize
Store the initial size, so it can be resized to this on Clear()
unsigned int m_freeSpace
Current container size, expressed in vertices.
unsigned int usedSpace() const
Return size of the used memory space.
bool m_dirty
Default initial size of a container (expressed in vertices)
void setSize(unsigned int aSize)
Set data size in the container.
Definition: vertex_item.h:94
void setOffset(unsigned int aOffset)
Set data offset in the container.
Definition: vertex_item.h:84
unsigned int GetOffset() const
Return data offset in the container.
Definition: vertex_item.h:68
unsigned int GetSize() const
Return information about number of vertices stored.
Definition: vertex_item.h:58
A small class to help profiling.
Definition: profile.h:49
The Cairo implementation of the graphics abstraction layer.
Definition: color4d.cpp:247
static constexpr size_t VERTEX_SIZE
Definition: vertex_common.h:67
Class to handle an item held in a container.