• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

NeonGE / geEngineSDK / ac7673f3-08dc-4326-87b7-3c14ad7938bf

05 Mar 2026 04:29PM UTC coverage: 57.84% (-0.02%) from 57.862%
ac7673f3-08dc-4326-87b7-3c14ad7938bf

push

circleci

NeonGE
Refactor input mapping, improve exception safety & tasks

- Replaced SFML-to-ImGui key mapping switch with static map for clarity.
- Updated input event lambdas to avoid capturing `this`.
- Marked `swap` methods as `_NOEXCEPT` in Matrix4, Path, Vector2.
- Added assertions and removed redundant checks in frame allocator.
- Refactored platform termination logic for clarity and portability.
- Modernized task scheduler waits using condition variable predicates.
- Updated debug.css font stack for better cross-platform support.

6 of 11 new or added lines in 4 files covered. (54.55%)

8 existing lines in 3 files now uncovered.

5603 of 9687 relevant lines covered (57.84%)

9107.71 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/sdk/geUtilities/src/geFrameAlloc.cpp
1
/*****************************************************************************/
2
/**
3
 * @file    geFrameAlloc.cpp
4
 * @author  Samuel Prince (samuel.prince.quezada@gmail.com)
5
 * @date    2016/03/06
6
 * @brief   Frame allocator
7
 *
8
 * Performs very fast allocations but can only free all of its memory at once.
9
 * Perfect for allocations that last just a single frame.
10
 *
11
 * @bug     No known bugs.
12
 */
13
/*****************************************************************************/
14

15
/*****************************************************************************/
16
/**
17
 * Includes
18
 */
19
/*****************************************************************************/
20
#include "gePrerequisitesUtilities.h"
21
#include "geFrameAlloc.h"
22
#include "geException.h"
23

24
namespace geEngineSDK {
25
  byte*
26
  FrameAlloc::MemBlock::alloc(SIZE_T amount) {
×
27
    byte* freePtr = &m_data[m_freePtr];
×
28
    m_freePtr += amount;
×
29
    return freePtr;
×
30
  }
31

32
  void
33
  FrameAlloc::MemBlock::clear() {
×
34
    m_freePtr = 0;
×
35
  }
×
36

37
#if USING(GE_DEBUG_MODE)
38
  FrameAlloc::FrameAlloc(SIZE_T blockSize)
39
    : m_blockSize(blockSize),
40
      m_freeBlock(nullptr),
41
      m_nextBlockIdx(0),
42
      m_totalAllocBytes(0),
43
      m_lastFrame(nullptr)
44
  {}
45
#else
46
  FrameAlloc::FrameAlloc(SIZE_T blockSize)
×
47
    : m_blockSize(blockSize),
×
48
      m_freeBlock(nullptr),
×
49
      m_nextBlockIdx(0),
×
50
      m_totalAllocBytes(0),
×
51
      m_lastFrame(nullptr)
×
52
  {}
×
53
#endif
54

55
  FrameAlloc::~FrameAlloc() {
×
56
    for (auto& block : m_blocks) {
×
57
      deallocBlock(block);
×
58
    }
59
  }
×
60

61
  byte*
62
  FrameAlloc::alloc(SIZE_T amount) {
×
63
    GE_ASSERT(m_freeBlock);
64
    GE_DEBUG_ONLY(amount += sizeof(SIZE_T));
65

66
    SIZE_T freeMem = 0;
×
NEW
67
    freeMem = m_freeBlock->m_size - m_freeBlock->m_freePtr;
×
68

69
    if (amount > freeMem) {
×
70
      allocBlock(amount);
×
71
    }
72

73
    byte* data = m_freeBlock->alloc(amount);
×
74

75
#if USING(GE_DEBUG_MODE)
76
    m_totalAllocBytes += amount;
77

78
    SIZE_T* storedSize = reinterpret_cast<SIZE_T*>(data);
79
    *storedSize = amount;
80

81
    return data + sizeof(SIZE_T);
82
#else
83
    return data;
×
84
#endif
85
  }
86

87
  byte*
88
  FrameAlloc::allocAligned(SIZE_T amount, SIZE_T alignment) {
×
89
    GE_ASSERT(m_freeBlock);
90
    GE_DEBUG_ONLY(amount += sizeof(SIZE_T));
91

92
    SIZE_T freeMem = 0;
×
93
    SIZE_T freePtr = 0;
×
NEW
94
    freeMem = m_freeBlock->m_size - m_freeBlock->m_freePtr;
×
95
#if USING(GE_DEBUG_MODE)
96
    freePtr = m_freeBlock->m_freePtr + sizeof(SIZE_T);
97
#else
NEW
98
    freePtr = m_freeBlock->m_freePtr;
×
99
#endif
100

UNCOV
101
    SIZE_T alignOffset = (alignment - (freePtr & (alignment - 1))) & (alignment - 1);
×
102
    if ((amount + alignOffset) > freeMem) {
×
103
      //New blocks are allocated on a 16 byte boundary, ensure enough space is
104
      //allocated taking into account the requested alignment
105

106
#if USING(GE_DEBUG_MODE)
107
      alignOffset = (alignment - (sizeof(SIZE_T) & (alignment - 1))) & (alignment - 1);
108
#else
109
      if (16 < alignment) {
×
110
        alignOffset = alignment - 16;
×
111
      }
112
      else {
113
        alignOffset = 0;
×
114
      }
115
#endif
116
      allocBlock(amount + alignOffset);
×
117
    }
118

119
    amount += alignOffset;
×
120
    byte* data = m_freeBlock->alloc(amount);
×
121

122
#if USING(GE_DEBUG_MODE)
123
    m_totalAllocBytes += amount;
124

125
    auto storedSize = reinterpret_cast<SIZE_T*>(data + alignOffset);
126
    *storedSize = amount;
127

128
    return data + sizeof(SIZE_T) + alignOffset;
129
#else
130
    return data + alignOffset;
×
131
#endif
132
  }
133

134
  void
135
  FrameAlloc::free(byte* data) {
×
136
    //Dealloc is only used for debug and can be removed if needed.
137
    //All the actual deallocation happens in clear()
138
#if USING(GE_DEBUG_MODE)
139
    if (data) {
140
      data -= sizeof(SIZE_T);
141
      auto storedSize = reinterpret_cast<SIZE_T*>(data);
142
      m_totalAllocBytes -= *storedSize;
143
    }
144
#else
145
    GE_UNREFERENCED_PARAMETER(data);
146
#endif
147
  }
×
148

149
  void
150
  FrameAlloc::markFrame() {
×
151
    auto framePtr = reinterpret_cast<void**>(alloc(sizeof(void*)));
×
152
    *framePtr = m_lastFrame;
×
153
    m_lastFrame = framePtr;
×
154
  }
×
155

156
  void
157
  FrameAlloc::clear() {
×
158
    if (nullptr != m_lastFrame) {
×
159
      GE_ASSERT(m_blocks.size() > 0 && 0 < m_nextBlockIdx);
160

161
      free(reinterpret_cast<byte*>(m_lastFrame));
×
162

163
      auto framePtr = reinterpret_cast<byte*>(m_lastFrame);
×
164
      m_lastFrame = *reinterpret_cast<void**>(m_lastFrame);
×
165

166
      GE_DEBUG_ONLY(framePtr -= sizeof(SIZE_T));
167

168
      uint32 startBlockIdx = m_nextBlockIdx - 1;
×
169
      uint32 numFreedBlocks = 0;
×
170
      for (int32 i = int32(startBlockIdx); i >= 0; --i) {
×
171
        uint32 index = static_cast<uint32>(i);
×
172
        MemBlock* curBlock = m_blocks[index];
×
173
        byte* blockEnd = curBlock->m_data + curBlock->m_size;
×
174
        if (framePtr >= curBlock->m_data && framePtr < blockEnd) {
×
175
          byte* dataEnd = curBlock->m_data + curBlock->m_freePtr;
×
176
          auto sizeInBlock = static_cast<SIZE_T>(dataEnd - framePtr);
×
177
          GE_ASSERT(sizeInBlock <= curBlock->m_freePtr);
178

179
          curBlock->m_freePtr -= sizeInBlock;
×
180
          if (0 == curBlock->m_freePtr) {
×
181
            ++numFreedBlocks;
×
182

183
            //Reset block counter if we're gonna reallocate this one
184
            if (1 < numFreedBlocks) {
×
185
              m_nextBlockIdx = static_cast<uint32>(i);
×
186
            }
187
          }
188

189
          break;
×
190
        }
191
        else {
192
          curBlock->m_freePtr = 0;
×
193
          m_nextBlockIdx = static_cast<uint32>(i);
×
194
          ++numFreedBlocks;
×
195
        }
196
      }
197

198
      if (1 < numFreedBlocks) {
×
199
        SIZE_T totalBytes = 0;
×
200
        for (uint32 i = 0; i < numFreedBlocks; ++i) {
×
201
          MemBlock* curBlock = m_blocks[m_nextBlockIdx];
×
202
          totalBytes += curBlock->m_size;
×
203

204
          deallocBlock(curBlock);
×
205
          m_blocks.erase(m_blocks.begin() + m_nextBlockIdx);
×
206
        }
207

208
        uint32 oldNextBlockIdx = m_nextBlockIdx;
×
209
        allocBlock(totalBytes);
×
210

211
        /** Point to the first non-full block, or if none available then point
212
         the the block we just allocated */
213
        if (0 < oldNextBlockIdx) {
×
214
          m_freeBlock = m_blocks[oldNextBlockIdx - 1];
×
215
        }
216
      }
217
      else {
218
        m_freeBlock = m_blocks[m_nextBlockIdx - 1];
×
219
      }
220
    }
221
    else {
222
#if USING(GE_DEBUG_MODE)
223
      if (m_totalAllocBytes.load() > 0) {
224
        GE_EXCEPT(InvalidStateException,
225
                  "Not all frame allocated bytes were properly released.");
226
      }
227
#endif
228
      if (m_blocks.size() > 1) {
×
229
        //Merge all blocks into one
230
        SIZE_T totalBytes = 0;
×
231
        for (auto& block : m_blocks) {
×
232
          totalBytes += block->m_size;
×
233
          deallocBlock(block);
×
234
        }
235

236
        m_blocks.clear();
×
237
        m_nextBlockIdx = 0;
×
238

239
        allocBlock(totalBytes);
×
240
      }
241
      else if (!m_blocks.empty()) {
×
242
        m_blocks[0]->m_freePtr = 0;
×
243
      }
244
    }
245
  }
×
246

247
  FrameAlloc::MemBlock*
248
  FrameAlloc::allocBlock(SIZE_T wantedSize) {
×
249
    SIZE_T blockSize = m_blockSize;
×
250
    if (wantedSize > blockSize) {
×
251
      blockSize = wantedSize;
×
252
    }
253

254
    MemBlock* newBlock = nullptr;
×
255
    while (m_nextBlockIdx < m_blocks.size()) {
×
256
      MemBlock* curBlock = m_blocks[m_nextBlockIdx];
×
257
      if (blockSize <= curBlock->m_size) {
×
258
        newBlock = curBlock;
×
259
        ++m_nextBlockIdx;
×
260
        break;
×
261
      }
262
      else {
263
        //Found an empty block that doesn't fit our data, delete it
264
        deallocBlock(curBlock);
×
265
        m_blocks.erase(m_blocks.begin() + m_nextBlockIdx);
×
266
      }
267
    }
268

269
    if (nullptr == newBlock) {
×
270
      SIZE_T alignOffset = 16 - (sizeof(MemBlock) & (16 - 1));
×
271

272
      auto data = reinterpret_cast<byte*>(ge_alloc_aligned16(blockSize +
×
273
                                                              sizeof(MemBlock) +
×
274
                                                              alignOffset));
275
      newBlock = new (data) MemBlock(blockSize);
×
276
      data += sizeof(MemBlock) + alignOffset;
×
277
      newBlock->m_data = data;
×
278

279
      m_blocks.push_back(newBlock);
×
280
      ++m_nextBlockIdx;
×
281
    }
282

283
    //If previous block had some empty space it is lost until next "clear"
284
    m_freeBlock = newBlock;
×
285

286
    return newBlock;
×
287
  }
288

289
  void
290
  FrameAlloc::deallocBlock(MemBlock* block) {
×
291
    block->~MemBlock();
×
292
    ge_free_aligned16(block);
×
293
  }
×
294

295
  void
296
  FrameAlloc::setOwnerThread(ThreadId /*thread*/) {}
×
297

298
  GE_THREADLOCAL FrameAlloc* _globalFrameAlloc = nullptr;
299

300
  FrameAlloc&
301
  g_frameAlloc() {
×
302
    if (nullptr == _globalFrameAlloc) {
×
303
      //Note: This will leak memory but since it should exist throughout the
304
      //entirety of runtime it should only leak on shutdown when the OS will
305
      //free it anyway.
306
      _globalFrameAlloc = new FrameAlloc();
×
307
    }
308

309
    return *_globalFrameAlloc;
×
310
  }
311

312
  byte*
313
  ge_frame_alloc(SIZE_T numBytes) {
×
314
    return g_frameAlloc().alloc(numBytes);
×
315
  }
316

317
  byte*
318
  ge_frame_alloc_aligned(SIZE_T count, SIZE_T align) {
×
319
    return g_frameAlloc().allocAligned(count, align);
×
320
  }
321

322
  void
323
  ge_frame_free(void* data) {
×
324
    g_frameAlloc().free(reinterpret_cast<byte*>(data));
×
325
  }
×
326

327
  void
328
  ge_frame_free_aligned(void* data) {
×
329
    g_frameAlloc().free(reinterpret_cast<byte*>(data));
×
330
  }
×
331

332
  void
333
  ge_frame_mark() {
×
334
    g_frameAlloc().markFrame();
×
335
  }
×
336

337
  void
338
  ge_frame_clear() {
×
339
    g_frameAlloc().clear();
×
340
  }
×
341
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc