-
Notifications
You must be signed in to change notification settings - Fork 3
Performance Benchmarks
This page documents reproducible performance measurements for VOID Engine 2.1.0, including a direct 2D sprite rendering comparison with MonoGame 3.8.5.1.
The purpose of these benchmarks is not to claim that one framework will be faster in every game or on every machine. The goal is to publish the exact workload, configuration, hardware, and results so developers can see how VOID performs under a defined 2D rendering test.
In the tested 25,000-sprite workload, VOID completed frames with a 1.1258 ms median frame time compared with 1.2580 ms for MonoGame.
That is approximately 10.5% lower median frame time for VOID in this benchmark.
Both frameworks completed the measured hot path with 0 bytes of managed allocation.
| Metric | VOID 2.1.0 | MonoGame 3.8.5.1 |
|---|---|---|
| Whole frame mean | 1.1478 ms | 1.2787 ms |
| Whole frame median | 1.1258 ms | 1.2580 ms |
| Whole frame P99 | 1.5331 ms | 1.7525 ms |
| Batch CPU median | 0.9928 ms | 1.0600 ms |
| Draw-command CPU median | 0.3526 ms | 0.4680 ms |
| End / Flush CPU median | 0.6393 ms | 0.5914 ms |
| Measured frames | 8,712 | 7,820 |
| Managed allocation | 0 bytes | 0 bytes |
VOID completed about 11.4% more measured frames during the same 10-second measurement window.
These results apply to this specific benchmark and test system. They should not be interpreted as a universal statement that VOID is faster than MonoGame for every workload.
| Component | Configuration |
|---|---|
| CPU | AMD Ryzen 7 1700X |
| GPU | AMD Radeon RX 6600 8 GB |
| Memory | 32 GB RAM |
| OS | Linux |
| Window backend | X11 |
| Runtime | .NET 10 |
| VOID | 2.1.0 |
| VOID renderer | Built-in OpenGL backend |
| MonoGame | 3.8.5.1 |
| MonoGame backend | DesktopGL |
VOID benchmark build hash:
956C2605CC5B820
The comparison uses a deliberately simple static sprite workload so the test is focused on the rendering and batching paths rather than gameplay logic.
| Setting | Value |
|---|---|
| Sprites | 25,000 |
| Sprite size | 32x32 |
| Texture | One in-memory white texture |
| Positions | Deterministic |
| Blend mode | Alpha |
| Resolution | 1280x720 |
| VSync | Disabled |
| Fixed timestep | Disabled |
| Build | Release |
| Warmup | 5 seconds |
| Measurement | 10 seconds |
| VOID batch capacity | 32,768 |
| MonoGame offscreen target | Enabled |
No debugger was attached during the measurements.
The test uses the same logical sprite count, sprite dimensions, alpha blending, output resolution, warmup time, and measurement duration for both frameworks.
VOID's normal window rendering path renders the game to its internal render target and then presents that texture to the window.
For the primary comparison, MonoGame was therefore run with an offscreen RenderTarget2D and a final presentation pass as well.
This makes the overall rendering structure closer to VOID's normal path instead of comparing VOID's offscreen-present path against a direct-to-backbuffer MonoGame path.
A separate MonoGame direct-backbuffer test was also performed during investigation and showed nearly identical performance to its offscreen test on this machine.
During initial testing, VOID appeared to be roughly three times slower than MonoGame.
That result was wrong.
VOID's default SuperSample value is 4. The first benchmark configuration did not override it, so VOID was internally rendering a 1280x720 viewport at:
5120x2880
while MonoGame was rendering at:
1280x720
That is 16 times as many render-target pixels.
For a fair raw rendering comparison, the VOID benchmark explicitly uses:
.SetViewport(1280, 720)
.SetSuperSample(1)
.SetVsync(false)
.SetFixedTimeStep(false)Once both frameworks were rendering at the same physical resolution, the apparent large VOID performance deficit disappeared.
This is also why benchmark configuration matters. A framework's default quality settings should not be mistaken for equivalent test conditions.
VOID BENCHMARK RESULT
Framework: VOID
Version: 2.1.0.0
Scenario: StaticSprites_BlendAB
Sprites: 25,000
Sprite Size: 32x32
Blend: alpha
Batch Capacity: 32,768
Resolution: 1280x720
Frames: 8,712
Whole Frame:
Mean: 1.1478 ms
Median: 1.1258 ms
P99: 1.5331 ms
Min: 1.0583 ms
Max: 6.8223 ms
Batch CPU:
Mean: 1.0142 ms
Median: 0.9928 ms
P99: 1.3703 ms
Draw Calls CPU:
Mean: 0.3612 ms
Median: 0.3526 ms
P99: 0.5210 ms
End / Flush CPU:
Mean: 0.6526 ms
Median: 0.6393 ms
P99: 0.8396 ms
Draw Calls: 1
Allocated: 0 bytes
MONOGAME BENCHMARK RESULT
Framework: MonoGame
Version: 3.8.5.1
Scenario: StaticSprites_OffscreenPresent_BlendAB
Sprites: 25,000
Offscreen: Enabled
Sprite Size: 32x32
Blend: alpha
Resolution: 1280x720
Frames: 7,820
Whole Frame:
Mean: 1.2787 ms
Median: 1.2580 ms
P99: 1.7525 ms
Min: 1.1573 ms
Max: 4.1940 ms
Batch CPU:
Mean: 1.0846 ms
Median: 1.0600 ms
P99: 1.5242 ms
Draw Calls CPU:
Mean: 0.4817 ms
Median: 0.4680 ms
P99: 0.8302 ms
End / Flush CPU:
Mean: 0.6028 ms
Median: 0.5914 ms
P99: 0.7331 ms
Draw Calls: 6
Allocated: 0 bytes
Whole-frame time is the most useful end-to-end measurement in this comparison.
VOID:
1.1258 ms median
MonoGame:
1.2580 ms median
VOID's median whole-frame time was approximately 10.5% lower in this workload.
The mean results tell a similar story:
VOID: 1.1478 ms
MonoGame: 1.2787 ms
VOID's mean whole-frame time was approximately 10.2% lower.
P99 is useful because it shows how the slower end of normal frame delivery behaves without relying on a single worst-frame outlier.
VOID: 1.5331 ms
MonoGame: 1.7525 ms
VOID's P99 frame time was approximately 12.5% lower in this run.
VOID: 0.9928 ms median
MonoGame: 1.0600 ms median
VOID's measured batch CPU time was approximately 6.3% lower.
VOID: 0.3526 ms median
MonoGame: 0.4680 ms median
VOID's measured draw-command CPU section was approximately 24.7% lower.
MonoGame was faster in the measured flush section:
VOID: 0.6393 ms median
MonoGame: 0.5914 ms median
MonoGame's measured value was about 8.1% lower here.
That means VOID still has an area worth profiling further even though its complete frame was faster in this workload.
Both tests used the same 10-second measurement interval.
VOID completed:
8,712 frames
MonoGame completed:
7,820 frames
The higher frame count is consistent with VOID's lower average whole-frame time. VOID returned to the next frame sooner and therefore completed more frames during the same measurement period.
The final result for both frameworks was:
VOID: 0 bytes
MonoGame: 0 bytes
This measurement uses:
GC.GetAllocatedBytesForCurrentThread()around the measured game-thread workload.
Earlier VOID runs consistently allocated about:
160 bytes per frame
At 8,152 measured frames, one run reported:
1,304,480 bytes
The renderer and sprite batcher were instrumented individually and were found to allocate 0 bytes per frame.
The allocation came from InputActionState.
The input action system was creating a new snapshot every update, and the snapshot constructor cloned two dictionaries even when the logical action state had not changed.
That meant two managed dictionary objects were being created every frame.
The system was changed so that:
- No action snapshot is created when no actions are registered.
- Existing immutable snapshots are reused while the logical action state is unchanged.
- A new snapshot is still created whenever an input transition requires one.
The important snapshot behavior remains intact:
Up -> JustPressed -> Pressed
Pressed -> JustReleased -> Up
Retained InputActionState values remain immutable and independent of future updates.
After the fix, the same allocation diagnostic reported:
After Draw -> OnUpdate 0.00 B/frame
OnUpdate body 0.00 B/frame
OnUpdate -> OnDraw 0.00 B/frame
Batch.Begin 0.00 B/frame
Sprite Draw loop 0.00 B/frame
Batch.End 0.00 B/frame
Allocated: 0 bytes
This was an engine-level optimization, not a benchmark-only workaround.
The benchmark investigation went considerably deeper than simply comparing two frame-time numbers.
Temporary profiling included:
- CPU timing around batch begin, sprite submission, flush, presentation, and buffer swap
- asynchronous OpenGL timer queries
- forced GPU synchronization tests
- sprite-size sweeps
- alpha versus opaque blending
- simplified fragment shaders
- constant-color fragment shaders
- rasterizer discard
- disabled color writes
- direct-backbuffer rendering
- texture-backed framebuffer rendering
- renderbuffer-backed framebuffer rendering
- different vertex-buffer capacities and upload paths
- managed allocation tracing by game-loop phase
Those experiments initially appeared to point toward a GPU raster/output bottleneck inside VOID.
After the supersampling mismatch was discovered, the results made sense: the experiments had been measuring VOID at a much larger physical render resolution than MonoGame.
The investigation was still useful because it verified several things independently, but the early VOID-versus-MonoGame conclusions were not valid until the resolutions matched.
The final output reported:
VOID: 1 draw call
MonoGame: 6 draw calls
This number should be interpreted carefully.
VOID's counter reflects its sprite batch draw submission, while the MonoGame benchmark includes its internal sprite-batch flush behavior and presentation path.
The counters are useful for understanding each implementation, but they should not be treated as a perfectly identical cross-framework metric by themselves.
Whole-frame time is the cleaner comparison.
This test does not prove that VOID will be faster in every game.
Real projects may behave differently depending on:
- texture changes
- sprite sorting
- multiple blend modes
- shaders
- render targets
- text
- primitives
- cameras
- post-processing
- asset streaming
- CPU-heavy gameplay
- driver behavior
- operating system
- GPU architecture
- CPU architecture
This benchmark isolates one common 2D workload: a large number of static alpha-blended sprites using one texture.
More scenarios can be added over time.
For this 25,000-sprite test on the documented Linux/OpenGL system:
- VOID and MonoGame are in the same overall performance class.
- VOID completed the workload with approximately 10.5% lower median whole-frame time.
- VOID completed approximately 11.4% more frames during the same measurement window.
- Both frameworks measured 0 bytes of managed allocation on the benchmark game thread.
- VOID's sprite batching and draw-command CPU measurements were lower in this workload.
- MonoGame's measured End / Flush section was lower.
- VOID's batcher did not show evidence of an allocation or architectural performance problem in this test.
- Further VOID optimization work is most reasonably focused on profiling the flush/upload/submission path rather than rewriting the batching architecture.
VOID:
dotnet Bench.Void/bin/Release/net10.0/Bench.Void.dll \
--sprites 25000 \
--capacity 32768 \
--size 32 \
--blend alphaMonoGame:
dotnet Bench.MonoGame/bin/Release/net10.0/Bench.MonoGame.dll \
--sprites 25000 \
--offscreen true \
--size 32 \
--blend alphaFor VOID, make sure the benchmark configuration contains:
.SetViewport(1280, 720)
.SetSuperSample(1)
.SetVsync(false)
.SetFixedTimeStep(false)A Release build should be used, and the benchmark should be run without a debugger attached.
These results were recorded on September 17, 2026 using VOID 2.1.0 and MonoGame 3.8.5.1.
Future engine versions, framework versions, drivers, or benchmark changes may produce different results.
When benchmark results are updated, the test configuration should be updated with them so the comparison remains reproducible.
Home · Rendering · Input · Custom Renderers · GitHub Repository
Home · Getting Started · Rendering · Custom Renderers · GitHub · Report an Issue
Built with VOID Engine · MIT License
- Coroutines
- State Machine
- Save System
- Pathfinding
- Logging
- Beacon Manager
- Beacon Event System
- Discoverable System
- Instance Helper
- BeaconManager Extensions
- BeaconHandle Extensions
- Enum Extensions
- String Extensions
- Int Extensions
- Float Extensions
- IEnumerable Extensions
- Random Extensions
- Sound Extensions
- Font Extensions