Skip to content

Commit

Permalink
zzre: Allocation-less colliders (#371)
Browse files Browse the repository at this point in the history
Reworks the colliders and its users, reducing the runtime by 60% and removes any per-frame allocations (and only retains small amortized allocations).
  • Loading branch information
Helco authored Oct 20, 2024
1 parent b2008fe commit 4a36bd8
Show file tree
Hide file tree
Showing 49 changed files with 2,056 additions and 781 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,5 @@ nuget-feed
**/BuildOutput.sarif
sarif-output
**/.DS_Store

BenchmarkDotNet*
2 changes: 1 addition & 1 deletion zzio.sln
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "0Common", "0Common", "{FB32
NoCodeQuality.props = NoCodeQuality.props
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "zzre.sourcegen", "zzre.sourcegen\zzre.sourcegen.csproj", "{BE8DA2D7-E8FC-4767-894F-CD760F90D5CF}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "zzre.sourcegen", "zzre.sourcegen\zzre.sourcegen.csproj", "{BE8DA2D7-E8FC-4767-894F-CD760F90D5CF}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down
21 changes: 21 additions & 0 deletions zzio/EnumerableExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -111,4 +111,25 @@ public static Range Sub(this Range full, Range sub, int maxValue = int.MaxValue)
int newOffset = fullOffset + subOffset;
return newOffset..(newOffset + subLength);
}

public static IEnumerable<TOutput> PrefixSums<TInput, TOutput>(
this IEnumerable<TInput> set, TOutput first, Func<TOutput, TInput, TOutput> next)
{
foreach (var input in set)
{
yield return first;
first = next(first, input);
}
}

public static IEnumerable<TOutput> PrefixSumsInclusive<TInput, TOutput>(
this IEnumerable<TInput> set, TOutput first, Func<TOutput, TInput, TOutput> next)
{
foreach (var input in set)
{
yield return first;
first = next(first, input);
}
yield return first;
}
}
2 changes: 2 additions & 0 deletions zzio/rwbs/RWCollision.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Runtime.CompilerServices;
using zzio;

namespace zzio.rwbs;
Expand All @@ -14,6 +15,7 @@ public enum CollisionSectorType
}
public static class CollisionSectorTypeExtension
{
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static int ToIndex(this CollisionSectorType t) => ((int)t) / 4;
}

Expand Down
210 changes: 210 additions & 0 deletions zzre.core.tests/TestListOverSpan.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
using System;
using System.Buffers;
using NUnit.Framework;
using NUnit.Framework.Constraints;

namespace zzre.tests;

public class TestListOverSpan
{

[Test]
public void Ctor_Default()
{
ListOverSpan<int> list = default;
}

[Test]
public void Ctor_Array([Values(0, 1, 42)] int arrayLength)
{
var array = new int[arrayLength];
ListOverSpan<int> list = new(array);
}

[Test]
public void Ctor_Count()
{
var array = new int[4];
ListOverSpan<int> list = new(array, 0);
Assert.That(list.Count, Is.EqualTo(0));
list = new(array.AsSpan(), 2);
Assert.That(list.Count, Is.EqualTo(2));
list = new(array.AsSpan(), 4);
Assert.That(list.Count, Is.EqualTo(4));

Assert.That(() => new ListOverSpan<int>(array, -1),
Throws.InstanceOf<ArgumentOutOfRangeException>());
Assert.That(() => new ListOverSpan<int>(array, 5),
Throws.InstanceOf<ArgumentOutOfRangeException>());
}

[Test]
public void Add_Copy()
{
ListOverSpan<int> list = new(new int[16]);
Assert.That(list.Count, Is.Zero);
list.Add(42);
Assert.That(list.Count, Is.EqualTo(1));
Assert.That(list[0], Is.EqualTo(42));
}

[Test]
public void Add_Ref()
{
ListOverSpan<int> list = new(new int[16]);
Assert.That(list.Count, Is.Zero);
list.Add() = 42;
Assert.That(list.Count, Is.EqualTo(1));
Assert.That(list[0], Is.EqualTo(42));
}

[Test]
public void Add_Order()
{
ListOverSpan<int> list = new(new int[16]);
list.Add() = 1337;
list.Add() = 42;
Assert.That(list[1], Is.EqualTo(42));
Assert.That(list[0], Is.EqualTo(1337));
}

[Test]
public void Add_CountIncreases()
{
ListOverSpan<int> list = new(new int[2]);
Assert.That(list.Count, Is.EqualTo(0));
list.Add();
list.Add();
Assert.That(list.Count, Is.EqualTo(2));
}

[Test]
public void Add_OverCapacity()
{
Assert.That(() =>
{
ListOverSpan<int> list = new(new int[2]);
list.Add();
list.Add();
list.Add();
}, Throws.InvalidOperationException);
}

[Test]
public void Clear_Empty()
{
ListOverSpan<int> list = new(new int[16]);
Assert.That(list.Count, Is.Zero);
list.Clear();
Assert.That(list.Count, Is.Zero);
}

[Test]
public void Clear()
{
ListOverSpan<int> list = new(new int[16]);
list.Add(42);
Assert.That(list.Count, Is.EqualTo(1));
list.Clear();
Assert.That(list.Count, Is.Zero);
list.Add(1337);
Assert.That(list.Count, Is.EqualTo(1));
Assert.That(list[0], Is.EqualTo(1337));
}

[Test]
public void Index_OutOfBounds()
{
Assert.That(() => new ListOverSpan<int>(new int[16])[0], Throws.InstanceOf<ArgumentOutOfRangeException>());
Assert.That(() => new ListOverSpan<int>(new int[16])[-1], Throws.InstanceOf<ArgumentOutOfRangeException>());
Assert.That(() =>
{
var list = new ListOverSpan<int>(new int[16]);
list.Add();
_ = list[0];
}, Throws.Nothing);
Assert.That(() =>
{
var list = new ListOverSpan<int>(new int[16]);
list.Add();
_ = list[1];
}, Throws.InstanceOf<ArgumentOutOfRangeException>());
Assert.That(() =>
{
var list = new ListOverSpan<int>(new int[16]);
list.Add();
list.Add();
_ = list[1];
}, Throws.Nothing);
Assert.That(() =>
{
var list = new ListOverSpan<int>(new int[16]);
list.Add();
list.Add();
_ = list[2];
}, Throws.InstanceOf<ArgumentOutOfRangeException>());
Assert.That(() =>
{
var list = new ListOverSpan<int>(new int[16]);
list.Add();
list.Add();
_ = list[-1];
}, Throws.InstanceOf<ArgumentOutOfRangeException>());
}

[Test]
public void Enumerator_Empty()
{
ListOverSpan<int> list = new(new int[16]);
var e = list.GetEnumerator();
Assert.That(e.MoveNext(), Is.False);
Assert.That(e.MoveNext(), Is.False);
}

[Test]
public void Enumerator_Once()
{
ListOverSpan<int> list = new(new int[16]);
list.Add() = 42;
var e = list.GetEnumerator();
Assert.That(e.MoveNext(), Is.True);
Assert.That(e.Current, Is.EqualTo(42));
Assert.That(e.MoveNext(), Is.False);
Assert.That(e.MoveNext(), Is.False);
}

[Test]
public void Enumerator_Twice()
{
ListOverSpan<int> list = new(new int[16]);
list.Add() = 42;
list.Add() = 1337;
var e = list.GetEnumerator();
Assert.That(e.MoveNext(), Is.True);
Assert.That(e.Current, Is.EqualTo(42));
Assert.That(e.MoveNext(), Is.True);
Assert.That(e.Current, Is.EqualTo(1337));
Assert.That(e.MoveNext(), Is.False);
}

[Test]
public void Enumerator_Interleaved()
{
ListOverSpan<int> list = new(new int[16]);
list.Add() = 42;
list.Add() = 1337;
var e0 = list.GetEnumerator();
var e1 = list.GetEnumerator();
Assert.That(e1.MoveNext(), Is.True);
var e2 = list.GetEnumerator();
Assert.That(e2.MoveNext(), Is.True);
Assert.That(e2.MoveNext(), Is.True);
var e3 = list.GetEnumerator();
Assert.That(e3.MoveNext(), Is.True);
Assert.That(e3.MoveNext(), Is.True);
Assert.That(e3.MoveNext(), Is.False);

Assert.That(e2.Current, Is.EqualTo(1337));
Assert.That(e1.Current, Is.EqualTo(42));
}
}
Loading

0 comments on commit 4a36bd8

Please sign in to comment.