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
@@ -50,33 +50,76 @@ public sealed class CameraControlSystem(Entity cameraEntity, Entity player, Inpu
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Отскок сущностей со скоростью от границ мира.</summary>
|
||||
/// <summary>
|
||||
/// Отскок сущностей со скоростью от границ мира. На больших количествах работа режется
|
||||
/// на сегменты по всем ядрам (чанк Friflo держит весь архетип — сам по себе он слишком крупный).
|
||||
/// </summary>
|
||||
public sealed class BounceSystem(RectF bounds) : QuerySystem<Transform2D, Velocity>
|
||||
{
|
||||
private const int ParallelThreshold = 8192;
|
||||
private const int SegmentSize = 8192;
|
||||
|
||||
private readonly List<(Chunk<Transform2D> Transforms, Chunk<Velocity> Velocities)> _chunks = [];
|
||||
private readonly List<(int Chunk, int Start, int Length)> _segments = [];
|
||||
|
||||
protected override void OnUpdate()
|
||||
{
|
||||
var delta = Tick.deltaTime;
|
||||
_chunks.Clear();
|
||||
var total = 0;
|
||||
foreach (var (transforms, velocities, _) in Query.Chunks)
|
||||
{
|
||||
var t = transforms.Span;
|
||||
var v = velocities.Span;
|
||||
for (var i = 0; i < t.Length; i++)
|
||||
_chunks.Add((transforms, velocities));
|
||||
total += transforms.Length;
|
||||
}
|
||||
|
||||
if (total < ParallelThreshold)
|
||||
{
|
||||
foreach (var (transforms, velocities) in _chunks)
|
||||
{
|
||||
ref var position = ref t[i].Position;
|
||||
ref var velocity = ref v[i].Value;
|
||||
position += velocity * delta;
|
||||
Move(transforms, velocities, 0, transforms.Length, delta);
|
||||
}
|
||||
|
||||
if (position.X < bounds.Left || position.X > bounds.Right)
|
||||
{
|
||||
velocity.X = -velocity.X;
|
||||
position.X = Math.Clamp(position.X, bounds.Left, bounds.Right);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (position.Y < bounds.Top || position.Y > bounds.Bottom)
|
||||
{
|
||||
velocity.Y = -velocity.Y;
|
||||
position.Y = Math.Clamp(position.Y, bounds.Top, bounds.Bottom);
|
||||
}
|
||||
_segments.Clear();
|
||||
for (var c = 0; c < _chunks.Count; c++)
|
||||
{
|
||||
var length = _chunks[c].Transforms.Length;
|
||||
for (var start = 0; start < length; start += SegmentSize)
|
||||
{
|
||||
_segments.Add((c, start, Math.Min(SegmentSize, length - start)));
|
||||
}
|
||||
}
|
||||
|
||||
Parallel.For(0, _segments.Count, i =>
|
||||
{
|
||||
var (chunk, start, length) = _segments[i];
|
||||
Move(_chunks[chunk].Transforms, _chunks[chunk].Velocities, start, length, delta);
|
||||
});
|
||||
}
|
||||
|
||||
private void Move(Chunk<Transform2D> transforms, Chunk<Velocity> velocities, int start, int length, float delta)
|
||||
{
|
||||
var t = transforms.Span.Slice(start, length);
|
||||
var v = velocities.Span.Slice(start, length);
|
||||
for (var i = 0; i < t.Length; i++)
|
||||
{
|
||||
ref var position = ref t[i].Position;
|
||||
ref var velocity = ref v[i].Value;
|
||||
position += velocity * delta;
|
||||
|
||||
if (position.X < bounds.Left || position.X > bounds.Right)
|
||||
{
|
||||
velocity.X = -velocity.X;
|
||||
position.X = Math.Clamp(position.X, bounds.Left, bounds.Right);
|
||||
}
|
||||
|
||||
if (position.Y < bounds.Top || position.Y > bounds.Bottom)
|
||||
{
|
||||
velocity.Y = -velocity.Y;
|
||||
position.Y = Math.Clamp(position.Y, bounds.Top, bounds.Bottom);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -137,7 +180,10 @@ public sealed class StatsSystem(
|
||||
context.Services.Get<GameWindow>().Title = $"MrGameEng Sample — {sceneName} | {stats}";
|
||||
if (hudLabel is not null)
|
||||
{
|
||||
hudLabel.Text = $"{sceneName}\n{stats}";
|
||||
hudLabel.Text =
|
||||
$"{sceneName}\n{stats}\n" +
|
||||
$"submit {renderer.SubmitMs:F2} | sort {renderer.SortMs:F2} | build {renderer.BuildMs:F2} | " +
|
||||
$"upload {renderer.UploadMs:F2} | draw {renderer.DrawMs:F2} (ms)";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user