Parallel rendering pipeline, ring vertex buffer, phase timings
Five optimizations measured on the 100k-entity stress scene (Release, vsync off): 103 FPS baseline -> 297 FPS. - Sprite submission and vertex building run on all cores above Renderer2DOptions.ParallelThreshold (default 8192). Work is sliced into 4096-entity segments: a Friflo chunk holds a whole archetype, so per-chunk parallelism degenerates to one thread. Segments merge in deterministic order, preserving radix sort stability. - Vertex buffer is ring-written with SetDataOptions.NoOverwrite (GPU buffer 2x frame size); Discard only on wrap-around. - Texture2DRegion precomputes UVs - four float divisions per sprite per frame removed. - Renderer2D exposes per-phase timings (submit/sort/build/upload/draw), shown in the sample HUD - all further optimization is data-driven. - Sample BounceSystem parallelized the same segmented way. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
af319d1276
commit
e06f24a319
@@ -1,3 +1,4 @@
|
||||
using Friflo.Engine.ECS;
|
||||
using Friflo.Engine.ECS.Systems;
|
||||
|
||||
namespace MrGameEng.Graphics;
|
||||
@@ -30,10 +31,20 @@ public sealed class CameraSystem : QuerySystem<Camera>
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Submits every entity that has both <see cref="Sprite"/> and <see cref="Transform2D"/>.</summary>
|
||||
/// <summary>
|
||||
/// Submits every entity that has both <see cref="Sprite"/> and <see cref="Transform2D"/>.
|
||||
/// Above <see cref="Renderer2DOptions.ParallelThreshold"/> entities, work is split into
|
||||
/// fixed-size segments processed on all cores (a Friflo chunk holds a whole archetype, so
|
||||
/// chunks themselves are too coarse); results merge in segment order, preserving sort stability.
|
||||
/// </summary>
|
||||
public sealed class SpriteRenderSystem : QuerySystem<Sprite, Transform2D>
|
||||
{
|
||||
private const int SegmentSize = 4096;
|
||||
|
||||
private readonly Renderer2D _renderer;
|
||||
private readonly List<(Chunk<Sprite> Sprites, Chunk<Transform2D> Transforms)> _chunks = [];
|
||||
private readonly List<(int Chunk, int Start, int Length)> _segments = [];
|
||||
private int[] _segmentLengths = [];
|
||||
|
||||
/// <summary>Creates the system for <paramref name="renderer"/>.</summary>
|
||||
public SpriteRenderSystem(Renderer2D renderer) => _renderer = renderer;
|
||||
@@ -41,15 +52,65 @@ public sealed class SpriteRenderSystem : QuerySystem<Sprite, Transform2D>
|
||||
/// <inheritdoc />
|
||||
protected override void OnUpdate()
|
||||
{
|
||||
_chunks.Clear();
|
||||
var total = 0;
|
||||
foreach (var (sprites, transforms, _) in Query.Chunks)
|
||||
{
|
||||
var s = sprites.Span;
|
||||
var t = transforms.Span;
|
||||
for (var i = 0; i < s.Length; i++)
|
||||
_chunks.Add((sprites, transforms));
|
||||
total += sprites.Length;
|
||||
}
|
||||
|
||||
if (total < _renderer.ParallelThreshold)
|
||||
{
|
||||
foreach (var (sprites, transforms) in _chunks)
|
||||
{
|
||||
_renderer.Submit(in t[i], in s[i]);
|
||||
var s = sprites.Span;
|
||||
var t = transforms.Span;
|
||||
for (var i = 0; i < s.Length; i++)
|
||||
{
|
||||
_renderer.Submit(in t[i], in s[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
_segments.Clear();
|
||||
for (var c = 0; c < _chunks.Count; c++)
|
||||
{
|
||||
var length = _chunks[c].Sprites.Length;
|
||||
for (var start = 0; start < length; start += SegmentSize)
|
||||
{
|
||||
_segments.Add((c, start, Math.Min(SegmentSize, length - start)));
|
||||
}
|
||||
}
|
||||
|
||||
if (_segmentLengths.Length < _segments.Count)
|
||||
{
|
||||
Array.Resize(ref _segmentLengths, _segments.Count);
|
||||
}
|
||||
|
||||
for (var i = 0; i < _segments.Count; i++)
|
||||
{
|
||||
_segmentLengths[i] = _segments[i].Length;
|
||||
}
|
||||
|
||||
_renderer.BeginChunkedSubmit(_segmentLengths.AsSpan(0, _segments.Count));
|
||||
Parallel.For(0, _segments.Count, segmentIndex =>
|
||||
{
|
||||
var (chunk, start, length) = _segments[segmentIndex];
|
||||
var (sprites, transforms) = _chunks[chunk];
|
||||
var writer = _renderer.GetChunkWriter(segmentIndex);
|
||||
var s = sprites.Span.Slice(start, length);
|
||||
var t = transforms.Span.Slice(start, length);
|
||||
for (var i = 0; i < s.Length; i++)
|
||||
{
|
||||
_renderer.SubmitInto(ref writer, in t[i], in s[i]);
|
||||
}
|
||||
|
||||
_renderer.EndChunk(segmentIndex, in writer);
|
||||
});
|
||||
_renderer.CommitChunkedSubmit();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user