1 /****************************************************************************
2 **
3 ** Copyright (C) 2008-2012 NVIDIA Corporation.
4 ** Copyright (C) 2019 The Qt Company Ltd.
5 ** Contact: https://www.qt.io/licensing/
6 **
7 ** This file is part of Qt Quick 3D.
8 **
9 ** $QT_BEGIN_LICENSE:GPL$
10 ** Commercial License Usage
11 ** Licensees holding valid commercial Qt licenses may use this file in
12 ** accordance with the commercial license agreement provided with the
13 ** Software or, alternatively, in accordance with the terms contained in
14 ** a written agreement between you and The Qt Company. For licensing terms
15 ** and conditions see https://www.qt.io/terms-conditions. For further
16 ** information use the contact form at https://www.qt.io/contact-us.
17 **
18 ** GNU General Public License Usage
19 ** Alternatively, this file may be used under the terms of the GNU
20 ** General Public License version 3 or (at your option) any later version
21 ** approved by the KDE Free Qt Foundation. The licenses are as published by
22 ** the Free Software Foundation and appearing in the file LICENSE.GPL3
23 ** included in the packaging of this file. Please review the following
24 ** information to ensure the GNU General Public License requirements will
25 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
26 **
27 ** $QT_END_LICENSE$
28 **
29 ****************************************************************************/
30 
31 
32 #ifndef QSSGPERFRAMEALLOCATOR_H
33 #define QSSGPERFRAMEALLOCATOR_H
34 
35 //
36 //  W A R N I N G
37 //  -------------
38 //
39 // This file is not part of the Qt API.  It exists purely as an
40 // implementation detail.  This header file may change from version to
41 // version without notice, or even be removed.
42 //
43 // We mean it.
44 //
45 
46 #include <QtQuick3DRuntimeRender/private/qtquick3druntimerenderglobal_p.h>
47 
48 QT_BEGIN_NAMESPACE
49 
50 class QSSGPerFrameAllocator
51 {
52     struct FastAllocator
53     {
54         struct Slab;
55 
56         enum : size_t {
57             ChunkSize = 8192*2,
58             Alignment = 4,
59             SlabSize = ChunkSize - sizeof(Slab *),
60             MaxAlloc = ChunkSize/2 // don't go all the way up to SlabSize, or we'd almost always get a big hole
61         };
62         struct Slab {
63             Slab() = default;
SlabFastAllocator::Slab64             Slab(Slab *previous)
65             {
66                 previous->next = this;
67             }
68             Slab *next = nullptr;
69             quint8 data[SlabSize];
70         };
71         Q_STATIC_ASSERT(sizeof(Slab) == ChunkSize);
72 
73         Slab *first = nullptr;
74         Slab *current = nullptr;
75         size_t offset = 0;
76 
FastAllocatorFastAllocator77         FastAllocator()
78         {
79             first = current = new Slab;
80         }
81 
~FastAllocatorFastAllocator82         ~FastAllocator()
83         {
84             Slab *s = first;
85             while (s) {
86                 Slab *n = s->next;
87                 delete s;
88                 s = n;
89             }
90         }
allocateFastAllocator91         void *allocate(size_t size)
92         {
93             size = (size + Alignment - 1) & ~(Alignment - 1);
94             Q_ASSERT(size <= SlabSize);
95             Q_ASSERT(!(offset % Alignment));
96 
97             size_t amountLeftInSlab = SlabSize - offset;
98             if (size > amountLeftInSlab) {
99                 if (current->next)
100                     current = current->next;
101                 else
102                     current = new Slab(current);
103                 offset = 0;
104             }
105 
106             quint8 *data = current->data + offset;
107             offset += size;
108             return data;
109         }
110 
111         // only reset, so we can re-use the memory
resetFastAllocator112         void reset() { current = first; offset = 0; }
113     };
114 
115     struct LargeAllocator
116     {
117         struct Slab {
118             Slab *next = nullptr;
119         };
120         Slab *current = nullptr;
121 
LargeAllocatorLargeAllocator122         LargeAllocator() {}
123 
124         // Automatically deallocates everything that hasn't already been deallocated.
~LargeAllocatorLargeAllocator125         ~LargeAllocator() { deallocateAll(); }
126 
deallocateAllLargeAllocator127         void deallocateAll()
128         {
129             while (current) {
130                 Slab *n = current->next;
131                 ::free(current);
132                 current = n;
133             }
134             current = nullptr;
135         }
136 
allocateLargeAllocator137         void *allocate(size_t size)
138         {
139             quint8 *mem = reinterpret_cast<quint8 *>(::malloc(sizeof(Slab) + size));
140             Slab *s = reinterpret_cast<Slab *>(mem);
141             s->next = current;
142             current = s;
143             return mem + sizeof(Slab);
144         }
145     };
146 
147     FastAllocator m_fastAllocator;
148     LargeAllocator m_largeAllocator;
149 
150 public:
QSSGPerFrameAllocator()151     QSSGPerFrameAllocator() {}
152 
allocate(size_t size)153     inline void *allocate(size_t size)
154     {
155         if (size < FastAllocator::MaxAlloc)
156             return m_fastAllocator.allocate(size);
157 
158         return m_largeAllocator.allocate(size);
159     }
160 
reset()161     void reset()
162     {
163         m_fastAllocator.reset();
164         m_largeAllocator.deallocateAll();
165     }
166 };
167 
168 QT_END_NAMESPACE
169 
170 #endif // QSSGPERFRAMEALLOCATOR_H
171