Stable radix sprite sort and render hot-path optimizations
SpriteBatcher now sorts with a stable LSD radix sort: equal-key sprites keep submission order across frames (no flicker) and passes over digits identical in all keys are skipped, making the common single-layer case nearly free. Hot paths avoid per-sprite trig and square roots: SinCos is skipped for unrotated sprites and the culling radius comes from the region's precomputed diagonal. Stress scene (100k entities, ~61k on screen, Release): 103 -> 124 FPS. Sample now runs with VSync off to show real frame rates. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
2b7d4c4fef
commit
a3e6d3bb0a
@@ -39,6 +39,66 @@ public class SpriteBatcherTests
|
||||
Assert.Equal(99, order[0]); // последний сабмит имеет наименьший ключ
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Sort_IsStable_EqualKeysKeepSubmissionOrder()
|
||||
{
|
||||
var batcher = new SpriteBatcher();
|
||||
var key = SpriteSortKey.Make(1, 5f, 7);
|
||||
|
||||
for (byte i = 0; i < 50; i++)
|
||||
{
|
||||
batcher.Submit(Instance(i), key); // одинаковый ключ у всех
|
||||
}
|
||||
|
||||
var order = batcher.Sort();
|
||||
|
||||
for (var i = 0; i < 50; i++)
|
||||
{
|
||||
Assert.Equal(i, order[i]);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Sort_IsStable_WithinMixedKeys()
|
||||
{
|
||||
var batcher = new SpriteBatcher();
|
||||
var keyA = SpriteSortKey.Make(0, 0f, 1);
|
||||
var keyB = SpriteSortKey.Make(2, 3f, 9);
|
||||
|
||||
// Чередуем два ключа: внутри каждой группы порядок сабмита должен сохраниться.
|
||||
for (byte i = 0; i < 20; i++)
|
||||
{
|
||||
batcher.Submit(Instance(i), i % 2 == 0 ? keyA : keyB);
|
||||
}
|
||||
|
||||
var order = batcher.Sort();
|
||||
|
||||
var expectedA = new[] { 0, 2, 4, 6, 8, 10, 12, 14, 16, 18 };
|
||||
var expectedB = new[] { 1, 3, 5, 7, 9, 11, 13, 15, 17, 19 };
|
||||
Assert.Equal(expectedA, order[..10].ToArray());
|
||||
Assert.Equal(expectedB, order[10..].ToArray());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Sort_LargeRandomSet_FullyOrdered()
|
||||
{
|
||||
var batcher = new SpriteBatcher(initialCapacity: 16);
|
||||
var random = new Random(123);
|
||||
var keys = new ulong[5000];
|
||||
for (var i = 0; i < keys.Length; i++)
|
||||
{
|
||||
keys[i] = (ulong)random.NextInt64();
|
||||
batcher.Submit(Instance(0), keys[i]);
|
||||
}
|
||||
|
||||
var order = batcher.Sort();
|
||||
|
||||
for (var i = 1; i < order.Length; i++)
|
||||
{
|
||||
Assert.True(keys[order[i - 1]] <= keys[order[i]]);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Clear_ResetsCount_KeepsWorking()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user