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.
This commit is contained in:
parent
2effba5127
commit
16d182c2f0
19 changed files with 3037 additions and 1228 deletions
68
src/AcDream.Core/Physics/TransitionScratchArena.cs
Normal file
68
src/AcDream.Core/Physics/TransitionScratchArena.cs
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
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;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue