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.
68 lines
2.1 KiB
C#
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;
|
|
}
|
|
}
|