acdream/src/AcDream.Core/Physics/TransitionScratchArena.cs
Erik 16d182c2f0 perf(physics): reuse reset-complete transition scratch
Mirror retail's ten-deep LIFO transition lifetime, retain all query scratch with complete reset contracts, and remove Tier-0 enum boxing without changing collision decisions. Fresh and retained engines are bit-identical across the expanded oracle, while measured transition profiles now allocate 0 bytes per resolve.
2026-07-25 14:37:02 +02:00

68 lines
2.1 KiB
C#

using System;
using System.Threading;
namespace AcDream.Core.Physics;
/// <summary>
/// Stack-disciplined reusable transition scratch owned by one
/// <see cref="PhysicsEngine"/>.
///
/// Retail <c>CTransition::makeTransition</c> (0x0050B150) leases one of ten
/// pre-existing records by nesting depth, calls <c>CTransition::init</c>, and
/// releases it through <c>CTransition::cleanupTransition</c> (0x00509DC0).
/// This arena keeps the same ten-deep, LIFO, same-thread contract while
/// lazily constructing records that are actually used.
/// </summary>
internal sealed class TransitionScratchArena
{
internal const int Capacity = 10;
private readonly Transition?[] _records = new Transition?[Capacity];
private int _depth;
private int _ownerThreadId;
internal int ActiveDepth => _depth;
internal Transition Rent()
{
int threadId = Environment.CurrentManagedThreadId;
if (_depth != 0 && _ownerThreadId != threadId)
{
throw new InvalidOperationException(
"Physics transition scratch cannot be shared across threads.");
}
if (_depth >= Capacity)
{
throw new InvalidOperationException(
$"Physics transition nesting exceeds retail's {Capacity}-record scratch arena.");
}
if (_depth == 0)
_ownerThreadId = threadId;
Transition transition = _records[_depth] ??= new Transition();
_depth++;
transition.ResetForReuse();
return transition;
}
internal void Return(Transition transition)
{
ArgumentNullException.ThrowIfNull(transition);
int threadId = Environment.CurrentManagedThreadId;
int index = _depth - 1;
if (index < 0
|| _ownerThreadId != threadId
|| !ReferenceEquals(_records[index], transition))
{
throw new InvalidOperationException(
"Physics transition scratch must be returned in LIFO order on its owning thread.");
}
_depth = index;
if (_depth == 0)
_ownerThreadId = 0;
}
}