fix #225: stabilize render pacing and frame CPU
Replace scheduler-quantized software sleeps with a reusable Windows high-resolution deadline timer, expose pacing in the frame profiler, and make shutdown wake every persistent mesh worker without losing the shared signal. Preserve retail alpha order while using a stable radix, skip duplicate deferred-alpha SSBO packing, pack light sets, cache static selection descriptors, and retire historical material groups at the whole-frame boundary. The fixed dense-Caul sample improved from roughly 9-12 ms CPU to 5.3-6.2 ms without reducing visual quality. Release build succeeds with zero warnings and all 6,300 tests pass with five intentional skips. Three independent retail, architecture, and adversarial reviews are clean; the post-review connected route remains pending because local ACE is offline. Co-authored-by: OpenAI Codex <codex@openai.com>
This commit is contained in:
parent
47d7086a74
commit
3718e341be
17 changed files with 1103 additions and 225 deletions
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace AcDream.App.Rendering;
|
||||
|
||||
|
|
@ -38,8 +39,7 @@ internal interface IRetailAlphaDrawSource
|
|||
internal readonly record struct RetailAlphaSubmission(
|
||||
IRetailAlphaDrawSource Source,
|
||||
int Token,
|
||||
float ViewerDistance,
|
||||
long Sequence);
|
||||
float ViewerDistance);
|
||||
|
||||
/// <summary>
|
||||
/// Frame-scoped port of retail's shared <c>D3DPolyRender</c> alpha list.
|
||||
|
|
@ -62,7 +62,8 @@ internal sealed class RetailAlphaQueue
|
|||
private readonly List<IRetailAlphaDrawSource> _sources = new(4);
|
||||
private int[] _tokenScratch = new int[256];
|
||||
private int[] _sourceDrawOffsets = new int[4];
|
||||
private long _nextSequence;
|
||||
private RetailAlphaSubmission[] _sortScratch = new RetailAlphaSubmission[256];
|
||||
private readonly int[] _radixOffsets = new int[256];
|
||||
|
||||
public bool IsCollecting { get; private set; }
|
||||
internal int PendingCount => _submissions.Count;
|
||||
|
|
@ -74,7 +75,6 @@ internal sealed class RetailAlphaQueue
|
|||
if (_submissions.Count != 0 || _sources.Count != 0)
|
||||
throw new InvalidOperationException("Retail alpha queue retained payload outside a frame.");
|
||||
|
||||
_nextSequence = 0;
|
||||
IsCollecting = true;
|
||||
}
|
||||
|
||||
|
|
@ -89,8 +89,7 @@ internal sealed class RetailAlphaQueue
|
|||
_submissions.Add(new RetailAlphaSubmission(
|
||||
source,
|
||||
token,
|
||||
NormalizeDistance(viewerDistance),
|
||||
_nextSequence++));
|
||||
NormalizeDistance(viewerDistance)));
|
||||
|
||||
for (int i = 0; i < _sources.Count; i++)
|
||||
if (ReferenceEquals(_sources[i], source))
|
||||
|
|
@ -114,7 +113,7 @@ internal sealed class RetailAlphaQueue
|
|||
if (_submissions.Count == 0)
|
||||
return;
|
||||
|
||||
_submissions.Sort(CompareRetailOrder);
|
||||
SortRetailOrder();
|
||||
EnsureTokenCapacity(_submissions.Count);
|
||||
EnsureSourceCapacity(_sources.Count);
|
||||
Array.Clear(_sourceDrawOffsets, 0, _sources.Count);
|
||||
|
|
@ -178,17 +177,61 @@ internal sealed class RetailAlphaQueue
|
|||
}
|
||||
}
|
||||
|
||||
private static int CompareRetailOrder(RetailAlphaSubmission left, RetailAlphaSubmission right)
|
||||
private void SortRetailOrder()
|
||||
{
|
||||
// CShadowPart::insertion_sort 0x006B5130 produces descending CYpt
|
||||
// (far-to-near). Sequence makes the List.Sort result stable for equal
|
||||
// distances, preserving part/surface append order like retail.
|
||||
int distance = right.ViewerDistance.CompareTo(left.ViewerDistance);
|
||||
return distance != 0 ? distance : left.Sequence.CompareTo(right.Sequence);
|
||||
int count = _submissions.Count;
|
||||
if (count <= 1)
|
||||
return;
|
||||
|
||||
EnsureSortCapacity(count);
|
||||
Span<RetailAlphaSubmission> submissions = CollectionsMarshal.AsSpan(_submissions);
|
||||
Span<RetailAlphaSubmission> scratch = _sortScratch.AsSpan(0, count);
|
||||
|
||||
// Positive IEEE-754 float bits sort in the same order as their numeric
|
||||
// values. Complementing those bits turns an ascending stable radix pass
|
||||
// into retail's descending CYpt order. Four stable byte passes preserve
|
||||
// original submission order for equal distances, matching
|
||||
// CShadowPart::insertion_sort without List.Sort's O(n log n) interface
|
||||
// comparator overhead in particle-heavy views.
|
||||
RadixPass(submissions, scratch, shift: 0);
|
||||
RadixPass(scratch, submissions, shift: 8);
|
||||
RadixPass(submissions, scratch, shift: 16);
|
||||
RadixPass(scratch, submissions, shift: 24);
|
||||
}
|
||||
|
||||
private static float NormalizeDistance(float value)
|
||||
=> float.IsFinite(value) && value >= 0f ? value : 0f;
|
||||
// value > 0 also canonicalizes -0 to +0 so its IEEE sort key joins the
|
||||
// same stable equal-distance run as ordinary zero.
|
||||
=> float.IsFinite(value) && value > 0f ? value : 0f;
|
||||
|
||||
private void RadixPass(
|
||||
ReadOnlySpan<RetailAlphaSubmission> source,
|
||||
Span<RetailAlphaSubmission> destination,
|
||||
int shift)
|
||||
{
|
||||
Array.Clear(_radixOffsets);
|
||||
for (int i = 0; i < source.Length; i++)
|
||||
{
|
||||
uint key = ~BitConverter.SingleToUInt32Bits(source[i].ViewerDistance);
|
||||
_radixOffsets[(int)((key >> shift) & 0xFF)]++;
|
||||
}
|
||||
|
||||
int prefix = 0;
|
||||
for (int bucket = 0; bucket < _radixOffsets.Length; bucket++)
|
||||
{
|
||||
int bucketCount = _radixOffsets[bucket];
|
||||
_radixOffsets[bucket] = prefix;
|
||||
prefix += bucketCount;
|
||||
}
|
||||
|
||||
for (int i = 0; i < source.Length; i++)
|
||||
{
|
||||
RetailAlphaSubmission submission = source[i];
|
||||
uint key = ~BitConverter.SingleToUInt32Bits(submission.ViewerDistance);
|
||||
int bucket = (int)((key >> shift) & 0xFF);
|
||||
destination[_radixOffsets[bucket]++] = submission;
|
||||
}
|
||||
}
|
||||
|
||||
private void EnsureTokenCapacity(int count)
|
||||
{
|
||||
|
|
@ -204,6 +247,13 @@ internal sealed class RetailAlphaQueue
|
|||
Array.Resize(ref _sourceDrawOffsets, count + 4);
|
||||
}
|
||||
|
||||
private void EnsureSortCapacity(int count)
|
||||
{
|
||||
if (_sortScratch.Length >= count)
|
||||
return;
|
||||
Array.Resize(ref _sortScratch, count + 256);
|
||||
}
|
||||
|
||||
private int FindSourceIndex(IRetailAlphaDrawSource source)
|
||||
{
|
||||
for (int i = 0; i < _sources.Count; i++)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue