Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: native queue list #308

Merged
merged 1 commit into from
Dec 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions Runtime/Triangulator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1266,6 +1266,56 @@ public static void DynamicInsertPoint(this UnsafeTriangulator<fp2> @this, Output
#endif
}

/// <summary>
/// Custom queue implementation which is a wrapper for <see cref="NativeList{T}"/>.
/// This implementation is memory <b>extensive</b>.
/// </summary>
internal struct NativeQueueList<T> : IDisposable where T : unmanaged
{
public readonly bool IsCreated => impl.IsCreated;
public readonly int Count => math.max(impl.Length - indexRef.Value, 0);

private NativeList<T> impl;
private NativeReference<int> indexRef;

public NativeQueueList(int capacity, Allocator allocator)
{
impl = new(capacity, allocator);
indexRef = new(0, allocator);
}

public NativeQueueList(Allocator allocator) : this(1, allocator) { }

public ReadOnlySpan<T> AsReadOnlySpan() => impl.AsReadOnly().AsReadOnlySpan()[indexRef.Value..];
public Span<T> AsSpan() => impl.AsArray().AsSpan()[indexRef.Value..];

public void Clear()
{
impl.Clear();
indexRef.Value = 0;
}

public void Dispose()
{
impl.Dispose();
indexRef.Dispose();
}

public void Enqueue(T item) => impl.Add(item);
public T Dequeue() => impl[indexRef.Value++];
public readonly bool IsEmpty() => Count == 0;
public bool TryDequeue(out T item)
{
var isEmpty = IsEmpty();
if (isEmpty)
{
Clear();
}
item = isEmpty ? default : Dequeue();
return !isEmpty;
}
}

[BurstCompile]
internal struct TriangulationJob<T, T2, TBig, TTransform, TUtils> : IJob
where T : unmanaged, IComparable<T>
Expand Down
134 changes: 134 additions & 0 deletions Tests/InternalUtilsTests.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using andywiecko.BurstTriangulator.LowLevel.Unsafe;
using NUnit.Framework;
using System;
using System.Linq;
using Unity.Collections;
using Unity.Mathematics;

namespace andywiecko.BurstTriangulator.Editor.Tests
Expand Down Expand Up @@ -194,4 +196,136 @@ public void Area2Test(double2 a, double2 b, double2 c, double expected)
[Test, TestCaseSource(nameof(pointInsideTriangleTestData))]
public bool PointInsideTriangleTest(double2 p, double2 a, double2 b, double2 c) => default(UtilsDouble).PointInsideTriangle(p, a, b, c);
}

public class NativeListQueueTests
{
[Test]
public void IsCreatedTest()
{
using var queue = new NativeQueueList<int>(Allocator.Persistent);
Assert.That(queue.IsCreated, Is.True);
}

[Test]
public void CountTest()
{
using var queue = new NativeQueueList<int>(Allocator.Persistent);

var count0 = queue.Count;
queue.Enqueue(default);
queue.Enqueue(default);
queue.Enqueue(default);
var count3 = queue.Count;
queue.Dequeue();
queue.Dequeue();
var count1 = queue.Count;

Assert.That(count0, Is.EqualTo(0));
Assert.That(count3, Is.EqualTo(3));
Assert.That(count1, Is.EqualTo(1));
}

[Test]
public void AsReadOnlySpanTest()
{
using var queue = new NativeQueueList<int>(Allocator.Persistent);
queue.Enqueue(0);
queue.Enqueue(1);
queue.Enqueue(2);
queue.Enqueue(3);
queue.Dequeue();

var span = queue.AsReadOnlySpan();
Assert.That(span.ToArray(), Is.EqualTo(new[] { 1, 2, 3 }));
}

[Test]
public void AsSpanTest()
{
using var queue = new NativeQueueList<int>(Allocator.Persistent);
queue.Enqueue(0);
queue.Enqueue(1);
queue.Enqueue(2);
queue.Enqueue(3);
queue.Dequeue();

var span = queue.AsSpan();
Assert.That(span.ToArray(), Is.EqualTo(new[] { 1, 2, 3 }));
}

[Test]
public void ClearTest()
{
using var queue = new NativeQueueList<int>(Allocator.Persistent);
queue.Enqueue(0);
queue.Enqueue(1);
queue.Enqueue(2);
queue.Enqueue(3);

queue.Clear();

Assert.That(queue.Count, Is.EqualTo(0));
Assert.That(queue.AsReadOnlySpan().ToArray(), Is.Empty);
}

[Test]
public void DisposeTest()
{
var queue = new NativeQueueList<int>(Allocator.Persistent);
queue.Dispose();
Assert.That(queue.IsCreated, Is.False);
}

[Test]
public void EnqueueTest()
{
using var queue = new NativeQueueList<int>(Allocator.Persistent);
queue.Enqueue(42);
Assert.That(queue.AsReadOnlySpan()[0], Is.EqualTo(42));
}

[Test]
public void DequeueTest()
{
using var queue = new NativeQueueList<int>(Allocator.Persistent);
queue.Enqueue(1);
queue.Enqueue(2);
var el = queue.Dequeue();
Assert.That(el, Is.EqualTo(1));
}

[Test]
public void IsEmptyTest()
{
using var queue = new NativeQueueList<int>(Allocator.Persistent);
queue.Enqueue(1);
queue.Dequeue();
Assert.That(queue.IsEmpty(), Is.True);
}

[Test]
public void TryDequeueTest()
{
using var queue = new NativeQueueList<int>(Allocator.Persistent);
queue.Enqueue(0);
queue.Enqueue(1);
queue.Enqueue(2);
queue.Enqueue(3);

using var tmp = new NativeList<int>(Allocator.Persistent);
while (queue.TryDequeue(out var el))
{
tmp.Add(el);
}

Assert.That(tmp.AsReadOnly(), Is.EqualTo(new[] { 0, 1, 2, 3 }));
}

[Test]
public void ThrowDequeueTest()
{
using var queue = new NativeQueueList<int>(Allocator.Persistent);
Assert.Throws<IndexOutOfRangeException>(() => queue.Dequeue());
}
}
}
Loading