1/****************************************************************************
2**
3** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB).
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the Qt3D module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40/* !\internal
41 \class Qt3DCore::QFrameAllocator
42 \inmodule Qt3DCore
43 \brief Provides a pool of memory chunks to be used to allocate objects on a per frame basis.
44
45 The memory can be recycled by following frames by calling clear which won't deallocate any memory.
46
47 \note Be really careful when allocating polymorphic types. You must be
48 sure to call deallocate with the subclass type to properly release all
49 memory.
50*/
51
52#include "qframeallocator_p.h"
53#include "qframeallocator_p_p.h"
54
55QT_BEGIN_NAMESPACE
56
57namespace Qt3DCore {
58
59QFrameAllocatorPrivate::QFrameAllocatorPrivate()
60 : m_maxObjectSize(0U)
61 , m_alignment(0U)
62{
63}
64
65QFrameAllocator::QFrameAllocator(uint maxObjectSize, uint alignment, uint pageSize)
66 : d_ptr(new QFrameAllocatorPrivate)
67{
68 Q_ASSERT(alignment && pageSize && pageSize < UCHAR_MAX);
69 Q_D(QFrameAllocator);
70 d->m_maxObjectSize = maxObjectSize;
71 d->m_alignment = alignment;
72 d->m_allocatorPool.resize(asize: d->allocatorIndexFromSize(targetSize: maxObjectSize) + 1);
73 for (int i = 0, n = d->m_allocatorPool.size(); i < n; ++i)
74 d->m_allocatorPool[i].init(blockSize: (i + 1) * d->m_alignment, pageSize);
75}
76
77QFrameAllocator::~QFrameAllocator()
78{
79 Q_D(QFrameAllocator);
80 for (int i = 0, n = d->m_allocatorPool.size(); i < n; ++i)
81 d->m_allocatorPool[i].release();
82}
83
84// Clear all memory chunks, allocated memory is not released
85void QFrameAllocator::clear()
86{
87 Q_D(QFrameAllocator);
88 for (int i = 0, n = d->m_allocatorPool.size(); i < n; ++i)
89 d->m_allocatorPool[i].clear();
90}
91
92// Trim excess memory used by chunks
93void QFrameAllocator::trim()
94{
95 Q_D(QFrameAllocator);
96 for (int i = 0, n = d->m_allocatorPool.size(); i < n; ++i)
97 d->m_allocatorPool[i].trim();
98}
99
100uint QFrameAllocator::maxObjectSize() const
101{
102 Q_D(const QFrameAllocator);
103 return d->m_maxObjectSize;
104}
105
106int QFrameAllocator::allocatorPoolSize() const
107{
108 Q_D(const QFrameAllocator);
109 return d->m_allocatorPool.size();
110}
111
112bool QFrameAllocator::isEmpty() const
113{
114 Q_D(const QFrameAllocator);
115 for (const QFixedFrameAllocator &allocator : d->m_allocatorPool) {
116 if (!allocator.isEmpty())
117 return false;
118 }
119 return true;
120}
121
122uint QFrameAllocator::totalChunkCount() const
123{
124 Q_D(const QFrameAllocator);
125 uint chunkCount = 0;
126 for (const QFixedFrameAllocator& allocator : d->m_allocatorPool)
127 chunkCount += allocator.chunkCount();
128 return chunkCount;
129}
130
131QFixedFrameAllocator::QFixedFrameAllocator()
132 : m_blockSize(0)
133 , m_nbrBlock(0)
134 , m_lastAllocatedChunck(nullptr)
135 , m_lastFreedChunck(nullptr)
136{
137}
138
139QFixedFrameAllocator::~QFixedFrameAllocator()
140{
141 release();
142}
143
144void QFixedFrameAllocator::init(uint blockSize, uchar pageSize)
145{
146 m_blockSize = blockSize;
147 m_nbrBlock = pageSize;
148}
149
150void *QFixedFrameAllocator::allocate()
151{
152 Q_ASSERT(m_blockSize);
153 return scan().allocate(blockSize: m_blockSize);
154}
155
156QFrameChunk &QFixedFrameAllocator::scan()
157{
158 Q_ASSERT(m_blockSize);
159 Q_ASSERT(m_nbrBlock);
160
161 if (m_lastAllocatedChunck && m_lastAllocatedChunck->m_blocksAvailable)
162 return *m_lastAllocatedChunck;
163
164 for (int i = 0; i < m_chunks.size(); i++) {
165 if (m_chunks[i].m_blocksAvailable > 0) {
166 m_lastAllocatedChunck = m_chunks.begin() + i;
167 return *m_lastAllocatedChunck;
168 }
169 }
170 m_chunks.resize(asize: m_chunks.size() + 1);
171 QFrameChunk &newChunk = m_chunks.last();
172 newChunk.init(blockSize: m_blockSize, blocks: m_nbrBlock);
173 m_lastAllocatedChunck = &newChunk;
174 m_lastFreedChunck = &newChunk;
175 return newChunk;
176}
177
178void QFixedFrameAllocator::deallocate(void *ptr)
179{
180 Q_ASSERT(m_blockSize && m_nbrBlock);
181 if (!m_chunks.empty() && ptr != nullptr) {
182 if (m_lastFreedChunck != nullptr && m_lastFreedChunck->contains(p: ptr, blockSize: m_blockSize))
183 m_lastFreedChunck->deallocate(p: ptr, blockSize: m_blockSize);
184 else {
185 for (int i = 0; i < m_chunks.size(); i++) {
186 if (m_chunks[i].contains(p: ptr, blockSize: m_blockSize)) {
187 m_chunks[i].deallocate(p: ptr, blockSize: m_blockSize);
188 m_lastFreedChunck = m_chunks.begin() + i;
189 break ;
190 }
191 }
192 }
193 }
194}
195
196void QFixedFrameAllocator::trim()
197{
198 for (int i = m_chunks.size() - 1; i >= 0; i--) {
199 if (m_chunks.at(i).isEmpty()) {
200 m_chunks[i].release();
201 if (m_lastAllocatedChunck == &m_chunks[i])
202 m_lastAllocatedChunck = nullptr;
203 if (m_lastFreedChunck == &m_chunks[i])
204 m_lastFreedChunck = nullptr;
205 m_chunks.removeAt(i);
206 }
207 }
208}
209
210void QFixedFrameAllocator::release()
211{
212 for (int i = m_chunks.size() - 1; i >= 0; i--)
213 m_chunks[i].release();
214 m_chunks.clear();
215 m_lastAllocatedChunck = nullptr;
216 m_lastFreedChunck = nullptr;
217}
218
219// Allows to reuse chunks without having to reinitialize and reallocate them
220void QFixedFrameAllocator::clear()
221{
222 for (int i = m_chunks.size() - 1; i >= 0; i--)
223 m_chunks[i].clear(blockSize: m_blockSize, blocks: m_nbrBlock);
224}
225
226bool QFixedFrameAllocator::isEmpty() const
227{
228 for (const QFrameChunk &chunck : m_chunks) {
229 if (chunck.m_blocksAvailable != chunck.m_maxBlocksAvailable)
230 return false;
231 }
232 return true;
233}
234
235// QFrameChuck is agnostic about blocksize
236// However if it was initialized with a block size of 16
237// You should then pass 16 to allocate and deallocate
238void QFrameChunk::init(uint blockSize, uchar blocks)
239{
240 m_data = new uchar[blockSize * blocks];
241 m_firstAvailableBlock = 0;
242 m_blocksAvailable = blocks;
243 m_maxBlocksAvailable = blocks;
244 uchar *p = m_data;
245 // Init each block with its position stored in its first byte
246 for (uchar i = 0; i < blocks; p += blockSize)
247 *p = ++i;
248#ifdef QFRAMEALLOCATOR_DEBUG
249 VALGRIND_CREATE_MEMPOOL(m_data, 0, true);
250 VALGRIND_MAKE_MEM_NOACCESS(m_data, blockSize * blocks);
251 VALGRIND_MEMPOOL_ALLOC(m_data, m_data, blockSize * blocks);
252#endif
253}
254
255void *QFrameChunk::allocate(uint blockSize)
256{
257 if (m_blocksAvailable == 0)
258 return nullptr;
259 uchar *r = m_data + (m_firstAvailableBlock * blockSize);
260 m_firstAvailableBlock = *r;
261 --m_blocksAvailable;
262 return r;
263}
264
265// Shouldn't be called more than once for the same pointer
266void QFrameChunk::deallocate(void *p, uint blockSize)
267{
268 if (p >= m_data) {
269 uchar *toRelease = static_cast<uchar *>(p);
270 uchar oldFreeBlock = m_firstAvailableBlock;
271 m_firstAvailableBlock = static_cast<uchar>((toRelease - m_data) / blockSize);
272 *toRelease = oldFreeBlock;
273 ++m_blocksAvailable;
274 }
275}
276
277bool QFrameChunk::contains(void *p, uint blockSize)
278{
279 uchar *c = static_cast<uchar *>(p);
280 return (m_data <= c && c < m_data + blockSize * m_maxBlocksAvailable);
281}
282
283// Reset chunck without releasing heap allocated memory
284void QFrameChunk::clear(uint blockSize, uchar blocks)
285{
286 m_firstAvailableBlock = 0;
287 m_blocksAvailable = blocks;
288
289 uchar *p = m_data;
290 // Init each block with its position stored in its first byte
291 for (uchar i = 0; i < blocks; p += blockSize)
292 *p = ++i;
293}
294
295void QFrameChunk::release()
296{
297#ifdef QFRAMEALLOCATOR_DEBUG
298 VALGRIND_MEMPOOL_FREE(m_data, m_data);
299 VALGRIND_DESTROY_MEMPOOL(m_data);
300#endif
301 delete [] m_data;
302}
303
304void* QFrameAllocator::allocateRawMemory(size_t size)
305{
306 Q_D(QFrameAllocator);
307 Q_ASSERT(size <= d->m_maxObjectSize);
308 uint allocatorIndex = d->allocatorIndexFromSize(targetSize: uint(size));
309 return d->allocateAtChunk(allocatorIndex);
310}
311
312void QFrameAllocator::deallocateRawMemory(void* ptr, size_t size)
313{
314 Q_D(QFrameAllocator);
315 Q_ASSERT(size <= d->m_maxObjectSize);
316 uint allocatorIndex = d->allocatorIndexFromSize(targetSize: uint(size));
317 d->deallocateAtChunck(ptr, allocatorIndex);
318}
319
320} // Qt3D
321
322QT_END_NAMESPACE
323

source code of qt3d/src/core/resources/qframeallocator.cpp